Version 1.0
This commit is contained in:
+181
@@ -0,0 +1,181 @@
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
from src.core.loader import ChallengeLoader
|
||||
from src.core.executor import executor
|
||||
from src.core.linter import linter
|
||||
from src.core.prompts import get_socratic_prompt
|
||||
|
||||
app = FastAPI(title="Socratic Tutor API")
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
loader = ChallengeLoader()
|
||||
|
||||
|
||||
class LintRequest(BaseModel):
|
||||
language: str
|
||||
code: str
|
||||
|
||||
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class GuideRequest(BaseModel):
|
||||
challenge_id: str
|
||||
language: str
|
||||
code: str
|
||||
question: Optional[str] = ""
|
||||
|
||||
|
||||
class RunRequest(BaseModel):
|
||||
language: str
|
||||
code: str
|
||||
stdin: Optional[str] = ""
|
||||
|
||||
|
||||
@app.get("/challenges")
|
||||
def list_challenges():
|
||||
"""Returns a list of all challenge details."""
|
||||
challenges = loader.load_all()
|
||||
return {"challenges": [c.to_dict(challenge_id=k) for k, c in sorted(challenges.items())]}
|
||||
|
||||
|
||||
@app.get("/challenges/{challenge_id}")
|
||||
def get_challenge(challenge_id: str):
|
||||
"""Returns details of a single challenge."""
|
||||
challenges = loader.load_all()
|
||||
if challenge_id not in challenges:
|
||||
raise HTTPException(status_code=404, detail=f"Challenge '{challenge_id}' not found.")
|
||||
return challenges[challenge_id].to_dict(challenge_id=challenge_id)
|
||||
|
||||
|
||||
@app.post("/lint")
|
||||
def lint_code(req: LintRequest):
|
||||
"""Runs linting on user code using Core Linter."""
|
||||
res = linter.lint(req.language, req.code)
|
||||
if res.get("stderr") == f"Language '{req.language}' is not supported.":
|
||||
raise HTTPException(status_code=404, detail=res["stderr"])
|
||||
return res
|
||||
|
||||
|
||||
@app.post("/run")
|
||||
def run_code(req: RunRequest):
|
||||
"""Executes user code using Core Executor."""
|
||||
res = executor.run(req.language, req.code, stdin=req.stdin or "")
|
||||
if res.get("stderr") == f"Language '{req.language}' is not supported.":
|
||||
raise HTTPException(status_code=404, detail=res["stderr"])
|
||||
return res
|
||||
|
||||
|
||||
from src.core.mentor import mentor
|
||||
from src.core.generator import generator
|
||||
|
||||
|
||||
class GenerateChallengeRequest(BaseModel):
|
||||
prompt: str
|
||||
difficulty: Optional[str] = "Medium"
|
||||
language: Optional[str] = "Python"
|
||||
subject: Optional[str] = "General"
|
||||
|
||||
|
||||
class SaveChallengeRequest(BaseModel):
|
||||
filename: str
|
||||
markdown: str
|
||||
|
||||
|
||||
@app.post("/guide")
|
||||
async def guide_code(req: GuideRequest):
|
||||
"""Provides mentor guidance based on user's code and question for a challenge."""
|
||||
challenges = loader.load_all()
|
||||
if req.challenge_id not in challenges:
|
||||
raise HTTPException(status_code=404, detail=f"Challenge '{req.challenge_id}' not found.")
|
||||
|
||||
challenge = challenges[req.challenge_id]
|
||||
guidance = await mentor.get_guidance_async(
|
||||
challenge, req.code, user_question=req.question or ""
|
||||
)
|
||||
|
||||
return {
|
||||
"challenge_id": req.challenge_id,
|
||||
"status": guidance.get("status"),
|
||||
"mentor_response": guidance.get("mentor_response"),
|
||||
}
|
||||
|
||||
|
||||
@app.post("/challenges/generate")
|
||||
async def generate_challenge_endpoint(req: GenerateChallengeRequest):
|
||||
"""Generates a new challenge markdown document via LLM."""
|
||||
res = await generator.generate_challenge_async(
|
||||
prompt_text=req.prompt,
|
||||
difficulty=req.difficulty or "Medium",
|
||||
language=req.language or "Python",
|
||||
subject=req.subject or "General",
|
||||
)
|
||||
if res.get("status") == "error":
|
||||
raise HTTPException(status_code=500, detail=res.get("message"))
|
||||
return res
|
||||
|
||||
|
||||
@app.post("/challenges/save")
|
||||
def save_challenge_endpoint(req: SaveChallengeRequest):
|
||||
"""Saves generated markdown challenge content to disk."""
|
||||
res = generator.save_challenge(req.filename, req.markdown)
|
||||
if res.get("status") == "error":
|
||||
raise HTTPException(status_code=500, detail=res.get("message"))
|
||||
return res
|
||||
|
||||
|
||||
from src.core.handbook import handbook_service
|
||||
|
||||
|
||||
class HandbookExampleRequest(BaseModel):
|
||||
language: str
|
||||
topic_id: str
|
||||
topic_title: str
|
||||
|
||||
|
||||
@app.get("/handbook/catalog/{language}")
|
||||
def get_handbook_catalog_endpoint(language: str):
|
||||
"""Returns W3Schools-style syntax handbook catalog (functions & subjects) for specified language."""
|
||||
catalog = handbook_service.get_catalog(language)
|
||||
return {"language": language, "catalog": catalog}
|
||||
|
||||
|
||||
@app.get("/handbook/topics/{language}")
|
||||
def get_handbook_topics_endpoint(language: str):
|
||||
"""Returns syntax handbook catalog for backwards compatibility."""
|
||||
catalog = handbook_service.get_catalog(language)
|
||||
return {"language": language, "topics": catalog.get("functions", []) + catalog.get("subjects", [])}
|
||||
|
||||
|
||||
|
||||
@app.post("/handbook/example")
|
||||
async def get_handbook_example_endpoint(req: HandbookExampleRequest):
|
||||
"""Generates an LLM code example for a handbook reference topic."""
|
||||
res = await handbook_service.generate_example_async(
|
||||
language=req.language,
|
||||
topic_id=req.topic_id,
|
||||
topic_title=req.topic_title,
|
||||
)
|
||||
if res.get("status") == "error":
|
||||
raise HTTPException(status_code=500, detail=res.get("message"))
|
||||
return res
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
from src.core.config import config
|
||||
uvicorn.run("src.api.main:app", host=config.web_host, port=config.web_port, reload=True)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user