-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapi.py
More file actions
120 lines (96 loc) · 5.15 KB
/
Copy pathapi.py
File metadata and controls
120 lines (96 loc) · 5.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
from xml.dom import ValidationErr
from fastapi import FastAPI, HTTPException, Request, status, Depends
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from dotenv import load_dotenv
from models import CodeHint, CodeSnippet
from database import create_db_and_tables, get_session, save_code_snippet_and_hints
from utils import is_this_python
from pydantic import ValidationError
import json
import uvicorn
import requests
import os
from contextlib import asynccontextmanager
from sqlmodel import Session
@asynccontextmanager
async def lifespan(app: FastAPI):
create_db_and_tables()
yield
app = FastAPI(lifespan=lifespan)
load_dotenv() # Load the .env file
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Allows all origins
allow_credentials=True,
allow_methods=["*"], # Allows all methods
allow_headers=["*"], # Allows all headers
)
def get_code_hints_from_openai(code: str, attempt=1):
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
headers = {
"Authorization": f"Bearer {OPENAI_API_KEY}",
"Content-Type": "application/json"
}
# Adjust temperature based on the attempt
temperature = 0 if attempt == 1 else 0.8
data = {
"model": "gpt-4o",
"temperature": temperature,
"max_tokens": 150,
"messages": [
{
"role": "system",
"content": "You are an assistant. Your task is to analyze Python code and provide a JSON response with the following structure: {\"runtime_error_free\": BOOLEAN (True if the code has no errors at runtime), \"runtime_error_line\": INT (None if runtime_error_free is true), \"small_hint\": STR (puts the student on the right path to fixing the runtime error, maximum of 8 words), \"big_hint\": STR (provides all the information needed to fix the runtime error, up to 30 words), \"content_warning\": BOOLEAN, \"logical_error\": BOOLEAN, \"logical_error_hint\": STR (up to 200 characters)}. You should not make up any additional values or provide any information not requested."
},
{
"role": "user",
"content": f"Analyze this Python code:\n{code}\n Identify any runtime errors and logical errors (any errors that mean the code does not fulfill its purpose) and provide a JSON response with the following structure: {{\"runtime_error_free\": BOOLEAN (True if the code has no errors at runtime), \"runtime_error_line\": INT (None if runtime_error_free is true), \"small_hint\": STR (puts the student on the right path to fixing the runtime error, maximum of 8 words), \"big_hint\": STR (provides all the information needed to fix the runtime error, up to 30 words), \"content_warning\": BOOLEAN, \"logical_error\": BOOLEAN, \"logical_error_hint\": STR (up to 200 characters)}}"
}
],
"response_format": {
"type": "json_object"
}
}
response = requests.post('https://api.openai.com/v1/chat/completions', headers=headers, json=data)
return response.json()
def extract_assistant_message(openai_response):
messages = openai_response.get("choices", [])
return next((msg for msg in messages if msg.get("message", {}).get("role") == "assistant"), None)
@app.get("/")
async def root():
return {"message": "Hello World I am the code hint api"}
@app.get("/ui")
async def ui():
return FileResponse(os.path.join(BASE_DIR, "static", "index.html"))
from database import save_code_snippet_and_hints
@app.post("/get_code_hints")
async def get_code_hints(code_request: CodeSnippet, session: Session = Depends(get_session)):
code_snippet = code_request.code
attempt = 1
if not is_this_python(code_snippet):
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="The code provided is not valid Python code")
while attempt <= 3:
openai_response = get_code_hints_from_openai(code_snippet, attempt)
json_reply = extract_assistant_message(openai_response)
if json_reply and json_reply.get("message", {}).get("content"):
try:
hint_data = json.loads(json_reply["message"]["content"])
hint_data["is_python"] = is_this_python(code_snippet)
# Validate the hint_data against the CodeHint model
CodeHint(**hint_data)
save_code_snippet_and_hints(session, code_snippet, hint_data, attempt)
return hint_data
except (ValidationError, json.JSONDecodeError) as e:
print(f"Attempt {attempt}: ValidationError or JSONDecodeError - {str(e)}")
attempt += 1
except ValueError as e:
print(f"Attempt {attempt}: ValueError - {str(e)}")
attempt += 1
else:
attempt += 1
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Unable to get valid code hints after 3 attempts")
# Run the application
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)