1+ import ast
12import json
23import os
34import re
1415Each annotation should include: filename: The name of the student's file. content:
1516A short description of the mistake. line_start and line_end: The line number(s) where the
1617mistake occurs. Ensure the JSON is valid and properly formatted. Here is a sample format
17- of the json array to return: {{ "annotations": [{{"filename": "student_code.py",
18- "content": "Variable 'x' is unused.", "line_start": 5, "line_end": 5}}] }}.
18+ of the json array to return: {{
19+ "annotations": [
20+ {{
21+ "filename": "submission.py",
22+ "content": "The variable 'result' is assigned but never used.",
23+ "line_start": 3,
24+ "line_end": 3,
25+ "column_start": 4,
26+ "column_end": 16
27+ }},
28+ {{
29+ "filename": "submission.py",
30+ "content": "Missing parentheses in the print statement. Use print('Hello') instead.",
31+ "line_start": 5,
32+ "line_end": 5,
33+ "column_start": 0,
34+ "column_end": 20
35+ }},
36+ {{
37+ "filename": "submission.py",
38+ "content": "The function 'calculate_sum' is called but not defined.",
39+ "line_start": 10,
40+ "line_end": 10,
41+ "column_start": 0,
42+ "column_end": 15
43+ }}
44+ ]
45+ }}
46+
1947ONLY return the json object and nothing else. Make sure the line #s don't exceed
2048the number of lines in the file. You can use markdown syntax in the annotation's content,
21- especially when denoting code."""
49+ especially when denoting code.
50+
51+ Use only double quotes in your response
52+ """
2253
2354
2455def add_annotation_columns (annotations : List [Dict [str , Any ]], submission : Any ) -> List [Dict [str , Any ]]:
@@ -92,13 +123,14 @@ def run_llm(
92123 submission_path : str ,
93124 model : str ,
94125 scope : str ,
95- submission_type : Optional [str ],
126+ submission_type : Optional [str ] = None ,
96127 submission_image : Optional [str ] = None ,
97128 question : Optional [str ] = None ,
98129 prompt_text : Optional [str ] = None ,
99130 prompt : Optional [str ] = None ,
100131 json_schema : Optional [str ] = None ,
101132 output : Optional [str ] = None ,
133+ model_options : Optional [str ] = None ,
102134) -> str :
103135 """
104136 Executes the LLM feedback generator script and captures its output.
@@ -114,6 +146,9 @@ def run_llm(
114146 prompt: Name of predefined prompt file to use.
115147 json_schema: Optional JSON schema to format the response.
116148 output: filepath of output file.
149+ model_options: model options to pass to the llm
150+ submission_image: An optional file path to the image to give feedback to.
151+ json_schema: Optional JSON schema to format the response.
117152
118153 Returns:
119154 The output from the LLM feedback generator as a string, or an error message.
@@ -142,6 +177,8 @@ def run_llm(
142177 llm_command += ["--output" , output ]
143178 if submission_image :
144179 llm_command += ["--submission_image" , submission_image ]
180+ if model_options :
181+ llm_command += ["--model_options" , model_options ]
145182 if submission_type :
146183 llm_command += ["--submission_type" , submission_type ]
147184
@@ -155,6 +192,17 @@ def run_llm(
155192 return llm_result .stdout .strip ()
156193
157194
195+ def safe_eval_dict (match : str ):
196+ try :
197+ return json .loads (match ) # Try strict JSON first
198+ except json .JSONDecodeError :
199+ try :
200+ return ast .literal_eval (match ) # Fall back to Python-style dict
201+ except (SyntaxError , ValueError ) as e :
202+ print (f"[SKIPPED] Malformed match: { match [:80 ]} ... Reason: { e } " )
203+ return None
204+
205+
158206def extract_json (response : str ) -> List [Dict [str , Any ]]:
159207 """
160208 Extracts JSON objects embedded in a string.
@@ -166,7 +214,7 @@ def extract_json(response: str) -> List[Dict[str, Any]]:
166214 A list of parsed JSON dictionaries extracted from the input string.
167215 """
168216 matches = re .findall (r"(\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\}))*\}))*\})" , response )
169- return [json . loads ( match ) for match in matches ]
217+ return [parsed for match in matches if ( parsed := safe_eval_dict ( match )) ]
170218
171219
172220MINIMUM_ANNOTATION_WIDTH = 8
0 commit comments