Skip to content

Commit

Permalink
ui
Browse files Browse the repository at this point in the history
  • Loading branch information
smith-nathanh committed Nov 23, 2024
1 parent 3cf077a commit 800b9b4
Show file tree
Hide file tree
Showing 290 changed files with 519 additions and 45 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ __pycache__/utils.cpython-310.pyc
outputs/
__pycache__/structure.cpython-310.pyc
__pycache__/tools.cpython-310.pyc
temp/
File renamed without changes.
Binary file added __pycache__/__init__.cpython-310.pyc
Binary file not shown.
Binary file added __pycache__/app.cpython-310.pyc
Binary file not shown.
Binary file added __pycache__/main.cpython-310.pyc
Binary file not shown.
115 changes: 115 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import os
import zipfile
import shutil
from pathlib import Path
from flask import Flask, render_template, request, flash, redirect, url_for, send_file
from werkzeug.utils import secure_filename
import markdown
from langchain_core.messages import HumanMessage
from system.graph import build_graph
from system.prompts import DESIGN_PROMPT
from io import BytesIO

UPLOAD_FOLDER = 'uploads'
ALLOWED_EXTENSIONS = {'md'}

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.secret_key = 'development-key-change-in-production'

# Ensure upload directory exists
Path(UPLOAD_FOLDER).mkdir(exist_ok=True)

def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def generate_artifacts(prd_file_path):
"""Generate software artifacts from a PRD file using autoSWE."""
try:
# Read the PRD content
with open(prd_file_path, 'r') as f:
prd_content = f.read()
except Exception as e:
flash(f"Error reading PRD file: {str(e)}")
return None

try:
# Build the graph and initialize state
graph = build_graph()
state = {
'documents': {'PRD': prd_content},
'messages': [HumanMessage(content=DESIGN_PROMPT.format(PRD=prd_content))]
}

# Generate artifacts using the graph
final_state = graph.invoke(state)

# Map the final state to our web display structure
artifacts = {
'prd': final_state['documents'].get('PRD', ''),
'class_diagram': final_state['documents'].get('UML_class', ''),
'sequence_diagram': final_state['documents'].get('UML_sequence', ''),
'architecture': final_state['documents'].get('architecture_design', ''),
'code_files': final_state['documents']['code'],
'unit_tests': final_state['documents']['unit_tests'],
'acceptance_tests': final_state['documents']['acceptance_tests'],
}

return artifacts

except Exception as e:
flash(f"Error generating artifacts: {str(e)}")
return None

@app.route('/', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)

file = request.files['file']
if file.filename == '':
flash('No selected file')
return redirect(request.url)

if file and file.filename and allowed_file(file.filename):
filename = secure_filename(file.filename)
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
file.save(filepath)

# Generate artifacts
artifacts = generate_artifacts(filepath)

if artifacts is None:
return redirect(request.url)

# Convert PRD markdown to HTML
if 'prd' in artifacts:
artifacts['prd'] = markdown.markdown(artifacts['prd'])

return render_template('view_artifacts.html', artifacts=artifacts)

return render_template('index.html')

@app.route('/download_temp')
def download_temp():
temp_dir = Path(app.config['UPLOAD_FOLDER'])

# Create zip file in memory
memory_file = BytesIO()

with zipfile.ZipFile(memory_file, 'w', zipfile.ZIP_DEFLATED) as zf:
for file_path in temp_dir.rglob('*'):
if file_path.is_file():
arcname = file_path.relative_to(temp_dir)
zf.write(file_path, arcname)

memory_file.seek(0)

return send_file(
memory_file,
mimetype='application/zip',
as_attachment=True,
download_name='generated_artifacts.zip'
)
37 changes: 37 additions & 0 deletions js/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Smooth scrolling for navigation links
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
});

// Highlight active section in navigation
window.addEventListener('scroll', function () {
const sections = document.querySelectorAll('section');
const navLinks = document.querySelectorAll('.list-group-item');

let current = '';

sections.forEach(section => {
const sectionTop = section.offsetTop;
if (pageYOffset >= sectionTop - 60) {
current = section.getAttribute('id');
}
});

navLinks.forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href').substring(1) === current) {
link.classList.add('active');
}
});
});
41 changes: 2 additions & 39 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,4 @@
from dotenv import load_dotenv
import os
import logging
from langsmith import utils
from langchain_core.messages import HumanMessage
from graph import build_graph
from prompts import SAMPLE_PRD, DESIGN_PROMPT
import argparse
import json


# set logging level
logging.basicConfig(level=logging.INFO)


def main():
"""
Main entry point to invoke the SWEGraph.
"""
parser = argparse.ArgumentParser(description="Run the SWEGraph with a specified PRD.")
parser.add_argument("--prd_path", type=str, default=None, help="Path to the PRD file")
args = parser.parse_args()

if args.prd_path is None:
prd_content = SAMPLE_PRD
else:
with open(args.prd_path, 'r', encoding="utf-8") as prd_file:
prd_content = prd_file.read()

graph = build_graph()
graph.get_graph().draw_mermaid_png(output_file_path="images/swegraph.png")
state = {'documents': {'PRD': prd_content},
'messages': [HumanMessage(content=DESIGN_PROMPT.format(PRD=prd_content))]}
final_state = graph.invoke(state)

from app import app

if __name__ == "__main__":
load_dotenv(dotenv_path=".env", override=True)
logging.info('TRACING %s', str(utils.tracing_is_enabled()))
logging.info(os.environ["LANGCHAIN_PROJECT"])
main()
app.run(host="0.0.0.0", port=5000, debug=False, use_reloader=False)
76 changes: 76 additions & 0 deletions static/css/custom.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/* Additional custom styles */
.mermaid {
background-color: var(--bs-dark);
padding: 1rem;
border-radius: 0.375rem;
margin: 1rem 0;
}

pre {
background-color: var(--bs-dark);
border-radius: 0.375rem;
margin: 1rem 0;
}

.list-group-item {
background-color: var(--bs-dark);
border-color: var(--bs-border-color);
}

.list-group-item:hover {
background-color: var(--bs-secondary);
}

section {
padding: 1rem;
background-color: var(--bs-dark);
border-radius: 0.375rem;
margin-bottom: 1rem;
}

.markdown-content {
padding: 1rem;
background-color: var(--bs-dark);
border-radius: 0.375rem;
margin-bottom: 1rem;
}

.markdown-content h1,
.markdown-content h2,
.markdown-content h3,
.markdown-content h4,
.markdown-content h5,
.markdown-content h6 {
margin-top: 1rem;
margin-bottom: 0.5rem;
}

.markdown-content p {
margin-bottom: 1rem;
}

.markdown-content ul,
.markdown-content ol {
margin-bottom: 1rem;
padding-left: 2rem;
}

.markdown-content code {
color: #e4e4e4; /* Light gray text for contrast */
background-color: #2d2d2d; /* Dark gray background */
padding: 0.2em 0.4em;
border-radius: 0.25rem;
font-family: monospace;
}

.intro-section {
padding: 2rem;
background: linear-gradient(to bottom, var(--bs-dark) 0%, var(--bs-dark-rgb) 100%);
border-radius: 1rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

.intro-section .list-unstyled li {
margin: 0.5rem 0;
font-size: 1.1rem;
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
Binary file added system/__pycache__/__init__.cpython-310.pyc
Binary file not shown.
Binary file added system/__pycache__/graph.cpython-310.pyc
Binary file not shown.
Binary file added system/__pycache__/nodes.cpython-310.pyc
Binary file not shown.
Binary file added system/__pycache__/prompts.cpython-310.pyc
Binary file not shown.
Binary file added system/__pycache__/structure.cpython-310.pyc
Binary file not shown.
Binary file added system/__pycache__/tools.cpython-310.pyc
Binary file not shown.
Binary file added system/__pycache__/utils.cpython-310.pyc
Binary file not shown.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Empty file.
Empty file.
File renamed without changes.
File renamed without changes.
8 changes: 4 additions & 4 deletions graph.py → system/graph.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
from typing import Any, List, Dict, Type, Union, Literal, Annotated
from pydantic import BaseModel, Field
from langgraph.graph import StateGraph, START, END
from prompts import *
from system.prompts import *
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage, AnyMessage
import logging
from langchain_openai import ChatOpenAI
import subprocess
from utils import check_and_install_packages, create_repository
from structure import (GraphState,
from system.utils import check_and_install_packages, create_repository
from system.structure import (GraphState,
Design,
ApproveDesign,
EnvironmentSetup,
Implementation,
ApproveImplementation,
AcceptanceTests,
UnitTests)
from tools import (view_document,update_document,add_document,delete_document)
from system.tools import (view_document,update_document,add_document,delete_document)

# set logging level
logging.basicConfig(level=logging.INFO)
Expand Down
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
40 changes: 40 additions & 0 deletions system/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from dotenv import load_dotenv
import os
import logging
from langsmith import utils
from langchain_core.messages import HumanMessage
from graph import build_graph
from prompts import SAMPLE_PRD, DESIGN_PROMPT
import argparse


# set logging level
logging.basicConfig(level=logging.INFO)


def main():
"""
Main entry point to invoke the graph.
"""
parser = argparse.ArgumentParser(description="Run the graph with a specified PRD.")
parser.add_argument("--prd_path", type=str, default=None, help="Path to the PRD file")
args = parser.parse_args()

if args.prd_path is None:
prd_content = SAMPLE_PRD
else:
with open(args.prd_path, 'r', encoding="utf-8") as prd_file:
prd_content = prd_file.read()

graph = build_graph()
graph.get_graph().draw_mermaid_png(output_file_path="images/swegraph.png")
state = {'documents': {'PRD': prd_content},
'messages': [HumanMessage(content=DESIGN_PROMPT.format(PRD=prd_content))]}
final_state = graph.invoke(state)


if __name__ == "__main__":
load_dotenv(dotenv_path=".env", override=True)
logging.info('TRACING %s', str(utils.tracing_is_enabled()))
logging.info(os.environ["LANGCHAIN_PROJECT"])
main()
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion tools.py → system/tools.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Any, List, Dict, Type, Union, Literal, Annotated
from langchain_core.tools import tool
from langgraph.prebuilt import InjectedState, ToolNode, ToolExecutor, tools_condition
from structure import GraphState
from system.structure import GraphState

@tool
def view_document(document_name: str, state: Annotated[dict, InjectedState]) -> str:
Expand Down
1 change: 0 additions & 1 deletion utils.py → system/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import subprocess
import sys
import importlib
import os
import copy
from typing import Dict, List, Union
Expand Down
Loading

0 comments on commit 800b9b4

Please sign in to comment.