| import asyncio |
| import json |
| import os |
| import threading |
| from typing import AsyncGenerator, List |
|
|
| import groq |
| from fastapi import FastAPI |
| from fastapi.middleware.cors import CORSMiddleware |
| from fastapi.responses import StreamingResponse |
| from pydantic import BaseModel |
|
|
| from agent.loop_controller import AgentLoopController |
| from agent.evaluator import Evaluator |
|
|
| GROQ_API_KEY = os.environ.get("GROQ_API_KEY", "") |
| MODEL = "llama-3.3-70b-versatile" |
|
|
| app = FastAPI(title="Self-Improving Coding Agent API") |
|
|
| app.add_middleware( |
| CORSMiddleware, |
| allow_origins=[ |
| "http://localhost:5173", |
| "http://localhost:3000", |
| "https://swarna7414.github.io", |
| ], |
| allow_methods=["*"], |
| allow_headers=["*"], |
| ) |
|
|
| evaluator = Evaluator() |
|
|
|
|
| class AgentRequest(BaseModel): |
| task: str |
| test_cases: str = "" |
| max_iterations: int = 5 |
| timeout: int = 10 |
|
|
|
|
| async def _agent_event_stream(req: AgentRequest) -> AsyncGenerator[str, None]: |
| q: asyncio.Queue = asyncio.Queue() |
| loop = asyncio.get_running_loop() |
|
|
| def _run_agent(): |
| try: |
| controller = AgentLoopController(GROQ_API_KEY, MODEL) |
| for event in controller.run( |
| req.task, req.test_cases, req.max_iterations, req.timeout |
| ): |
| safe = _make_serialisable(event) |
| loop.call_soon_threadsafe(q.put_nowait, safe) |
| except Exception as exc: |
| loop.call_soon_threadsafe( |
| q.put_nowait, |
| {"type": "failed", "message": str(exc), "timestamp": ""}, |
| ) |
| finally: |
| loop.call_soon_threadsafe(q.put_nowait, None) |
|
|
| thread = threading.Thread(target=_run_agent, daemon=True) |
| thread.start() |
|
|
| while True: |
| event = await q.get() |
| if event is None: |
| break |
| yield f"data: {json.dumps(event)}\n\n" |
|
|
| yield "data: {\"type\": \"done\"}\n\n" |
|
|
|
|
| def _make_serialisable(obj): |
| if isinstance(obj, dict): |
| return {k: _make_serialisable(v) for k, v in obj.items()} |
| if isinstance(obj, (list, tuple)): |
| return [_make_serialisable(i) for i in obj] |
| try: |
| json.dumps(obj) |
| return obj |
| except (TypeError, ValueError): |
| return str(obj) |
|
|
|
|
| @app.get("/") |
| def root(): |
| return {"status": "ok", "model": MODEL} |
|
|
|
|
| @app.get("/health") |
| def health(): |
| groq_configured = bool(GROQ_API_KEY) |
| return { |
| "status": "ok" if groq_configured else "degraded", |
| "model": MODEL, |
| "groq_configured": groq_configured, |
| } |
|
|
|
|
| @app.post("/api/run-agent") |
| async def run_agent(req: AgentRequest): |
| return StreamingResponse( |
| _agent_event_stream(req), |
| media_type="text/event-stream", |
| headers={ |
| "Cache-Control": "no-cache", |
| "X-Accel-Buffering": "no", |
| }, |
| ) |
|
|
|
|
| @app.post("/api/evaluate") |
| async def evaluate(session: dict): |
| metrics = evaluator.compute_metrics(session) |
| return metrics |
|
|
|
|
| class ChatMessage(BaseModel): |
| role: str |
| content: str |
|
|
|
|
| class ChatRequest(BaseModel): |
| message: str |
| mode: str = "fix" |
| history: List[ChatMessage] = [] |
|
|
|
|
| class FixCodeRequest(BaseModel): |
| code: str |
| language: str = "auto" |
|
|
|
|
| SYSTEM_PROMPTS = { |
| "fix": ( |
| "You are an expert software engineer and code debugger. " |
| "When the user shares broken or buggy code, carefully analyze it, " |
| "explain what is wrong in plain language, and provide the corrected " |
| "working code with clear explanations. Format code blocks with markdown." |
| ), |
| "learn": ( |
| "You are a patient and expert programming teacher. " |
| "Explain concepts clearly with examples, break down complex topics " |
| "into simple steps, and adapt your explanations so the user truly " |
| "understands. Format code examples with markdown code blocks." |
| ), |
| } |
|
|
| FIX_CODE_SYSTEM = """You are an expert code analyst, debugger, and software engineer. |
| |
| When given code, follow these exact steps: |
| |
| STEP 1 — Error Detection: |
| Carefully scan for ALL of the following: |
| - Syntax errors |
| - Runtime errors and exceptions |
| - Logic bugs and incorrect behaviour |
| - Security vulnerabilities |
| - Bad practices or deprecated usage |
| |
| STEP 2 — Respond based on what you find: |
| |
| If errors ARE found: |
| - Start with a short summary: "Found X issue(s) in your code." |
| - List each issue with: type, location (line number if possible), and a plain-language description |
| - Provide the COMPLETE fixed code in a properly labelled code block |
| - Below the fixed code, briefly explain every change made |
| |
| If NO errors are found: |
| - Start with: "No errors found. Your code looks correct." |
| - Then write: "Would you like me to optimize this to industry standards? I can improve readability, performance, naming conventions, and apply best practices." |
| - Do NOT add unsolicited changes — wait for the user to confirm. |
| |
| Always be specific, practical, and concise. Format all code with markdown code blocks and include the language label.""" |
|
|
|
|
| async def _chat_stream(req: ChatRequest) -> AsyncGenerator[str, None]: |
| client = groq.Groq(api_key=GROQ_API_KEY) |
| system = SYSTEM_PROMPTS.get(req.mode, SYSTEM_PROMPTS["fix"]) |
|
|
| messages = [{"role": "system", "content": system}] |
| for h in req.history: |
| messages.append({"role": h.role, "content": h.content}) |
| messages.append({"role": "user", "content": req.message}) |
|
|
| try: |
| completion = client.chat.completions.create( |
| model=MODEL, |
| messages=messages, |
| stream=True, |
| ) |
| for chunk in completion: |
| delta = chunk.choices[0].delta.content |
| if delta: |
| yield f"data: {json.dumps({'type': 'chunk', 'content': delta})}\n\n" |
| yield f"data: {json.dumps({'type': 'done'})}\n\n" |
| except Exception as exc: |
| yield f"data: {json.dumps({'type': 'error', 'message': str(exc)})}\n\n" |
|
|
|
|
| EXPLAIN_CODE_SYSTEM = """You are an expert programming teacher and code explainer. |
| |
| When given code, provide a thorough, beginner-friendly explanation structured as follows: |
| |
| ## Overview |
| A 2–3 sentence summary of what this code does and its overall purpose. |
| |
| ## Line-by-Line / Section-by-Section Breakdown |
| Walk through the code in logical chunks. For each chunk: |
| - Quote or reference the specific lines |
| - Explain exactly what it does in plain English |
| - Explain WHY it does it that way (the reasoning, not just the mechanics) |
| |
| ## Key Concepts Used |
| List and explain every concept, technique, or pattern used in the code (e.g. recursion, list comprehension, closures, REST, async/await, etc.). Assume the reader may not know these terms — explain each one clearly. |
| |
| ## How It All Flows Together |
| Describe the execution flow from start to finish — what happens first, what calls what, and how data moves through the code. |
| |
| ## Things Worth Noting |
| Point out anything clever, unusual, potentially confusing, or worth remembering about this code. |
| |
| Be thorough but clear. Use simple language. Format code references with inline backticks.""" |
|
|
| async def _fix_code_stream(req: FixCodeRequest) -> AsyncGenerator[str, None]: |
| client = groq.Groq(api_key=GROQ_API_KEY) |
| lang_hint = f" The code appears to be {req.language}." if req.language != "auto" else "" |
| user_message = f"Please analyze and fix this code:{lang_hint}\n\n```\n{req.code}\n```" |
|
|
| messages = [ |
| {"role": "system", "content": FIX_CODE_SYSTEM}, |
| {"role": "user", "content": user_message}, |
| ] |
| try: |
| completion = client.chat.completions.create( |
| model=MODEL, |
| messages=messages, |
| stream=True, |
| ) |
| for chunk in completion: |
| delta = chunk.choices[0].delta.content |
| if delta: |
| yield f"data: {json.dumps({'type': 'chunk', 'content': delta})}\n\n" |
| yield f"data: {json.dumps({'type': 'done'})}\n\n" |
| except Exception as exc: |
| yield f"data: {json.dumps({'type': 'error', 'message': str(exc)})}\n\n" |
|
|
|
|
| async def _explain_code_stream(req: FixCodeRequest) -> AsyncGenerator[str, None]: |
| client = groq.Groq(api_key=GROQ_API_KEY) |
| lang_hint = f" The code is written in {req.language}." if req.language != "auto" else "" |
| user_message = f"Please explain this code in full detail:{lang_hint}\n\n```\n{req.code}\n```" |
|
|
| messages = [ |
| {"role": "system", "content": EXPLAIN_CODE_SYSTEM}, |
| {"role": "user", "content": user_message}, |
| ] |
| try: |
| completion = client.chat.completions.create( |
| model=MODEL, |
| messages=messages, |
| stream=True, |
| ) |
| for chunk in completion: |
| delta = chunk.choices[0].delta.content |
| if delta: |
| yield f"data: {json.dumps({'type': 'chunk', 'content': delta})}\n\n" |
| yield f"data: {json.dumps({'type': 'done'})}\n\n" |
| except Exception as exc: |
| yield f"data: {json.dumps({'type': 'error', 'message': str(exc)})}\n\n" |
|
|
|
|
| @app.post("/api/explain-code") |
| async def explain_code(req: FixCodeRequest): |
| return StreamingResponse( |
| _explain_code_stream(req), |
| media_type="text/event-stream", |
| headers={ |
| "Cache-Control": "no-cache", |
| "X-Accel-Buffering": "no", |
| }, |
| ) |
|
|
|
|
| @app.post("/api/fix-code") |
| async def fix_code(req: FixCodeRequest): |
| return StreamingResponse( |
| _fix_code_stream(req), |
| media_type="text/event-stream", |
| headers={ |
| "Cache-Control": "no-cache", |
| "X-Accel-Buffering": "no", |
| }, |
| ) |
|
|
|
|
| @app.post("/api/chat") |
| async def chat(req: ChatRequest): |
| return StreamingResponse( |
| _chat_stream(req), |
| media_type="text/event-stream", |
| headers={ |
| "Cache-Control": "no-cache", |
| "X-Accel-Buffering": "no", |
| }, |
| ) |
|
|
|
|
| if __name__ == "__main__": |
| import uvicorn |
| uvicorn.run("main:app", host="0.0.0.0", port=7860) |
|
|