-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapi.py
109 lines (89 loc) · 4.86 KB
/
api.py
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
from xml.dom import ValidationErr
from fastapi import FastAPI, HTTPException, Request, status, Depends
from fastapi.middleware.cors import CORSMiddleware
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 sqlmodel import Session
app = FastAPI()
load_dotenv() # Load the .env 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"}
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")
@app.on_event("startup")
def on_startup():
create_db_and_tables()
# Run the application
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)