Skip to content

Commit dda4c9e

Browse files
committed
fix: fix more structured output
1 parent b89284a commit dda4c9e

2 files changed

Lines changed: 157 additions & 107 deletions

File tree

evaluation_function/evaluation.py

Lines changed: 140 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,16 @@
1010
The answer specifies a complexity upper bound that the student's code must meet.
1111
"""
1212

13+
import json
1314
from typing import Any, Dict, Optional, Tuple, List, Union
1415
from lf_toolkit.evaluation import Result
1516

1617
from .schemas.output_schema import (
18+
EvaluationResult,
1719
ParseResult,
20+
SectionFeedback,
1821
SpaceComplexityResult,
22+
TestCaseFeedback,
1923
TestCaseResult,
2024
TimeComplexityResult,
2125
)
@@ -191,12 +195,9 @@ def evaluation_function(
191195
eval_options=params_model,
192196
)
193197

194-
# ---------------------------------------------------------
195-
# Build result
196-
# ---------------------------------------------------------
197198
result = Result(is_correct=is_correct)
198199
# result.score = score
199-
result.add_feedback("complexity", feedback)
200+
result.add_feedback("complexity", feedback.model_dump_json())
200201

201202
return result
202203

@@ -297,69 +298,69 @@ def _calculate_result(
297298
return is_correct#, score
298299

299300

300-
def _generate_feedback(
301-
time_result: Optional[TimeComplexityResult],
302-
space_result: Optional[SpaceComplexityResult],
303-
test_case_results: List[TestCaseResult],
304-
analysis: AnalysisResult,
305-
is_correct: bool,
306-
eval_options: EvaluationParams,
307-
) -> str:
308-
"""Generate comprehensive feedback for the student using FeedbackGenerator."""
309-
feedback_generator = FeedbackGenerator()
310-
311-
show_detailed = eval_options.show_detailed_feedback
312-
level = FeedbackLevel.DETAILED if show_detailed and not is_correct else FeedbackLevel.STANDARD
313-
314-
detailed_feedback = feedback_generator.generate(analysis, level)
315-
316-
lines = []
317-
318-
if is_correct:
319-
lines.append("✓ Correct! Your algorithm meets the complexity requirements.")
320-
else:
321-
lines.append("✗ Your algorithm does not meet the complexity requirements.")
322-
lines.append("")
323-
if time_result:
324-
lines.append("Time Complexity:")
325-
lines.append(f" • Required: {time_result.expected_answer} or better")
326-
lines.append(f" • Detected: {time_result.detected_complexity}")
327-
328-
if time_result.is_correct:
329-
lines.append(" ✓ Your algorithm meets the time complexity requirement.")
330-
else:
331-
lines.append(" ✗ Your algorithm exceeds the allowed time complexity.")
332-
333-
if space_result:
334-
lines.append("")
335-
lines.append("Space Complexity:")
336-
lines.append(f" • Required: {space_result.expected_answer} or better")
337-
lines.append(f" • Detected: {space_result.detected_complexity}")
338-
339-
if space_result.is_correct:
340-
lines.append(" ✓ Your algorithm meets the space complexity requirement.")
341-
else:
342-
lines.append(" ✗ Your algorithm exceeds the allowed space complexity.")
343-
344-
if test_case_results:
345-
lines.append("")
346-
lines.append("Execution Test Cases:")
347-
for idx, tc in enumerate(test_case_results, 1):
348-
status = "✓ Passed" if tc.passed else "✗ Failed"
349-
lines.append(f" Test Case {idx}: {status}")
350-
if not tc.passed and tc.error_message:
351-
lines.append(f" Error: {tc.error_message}")
352-
353-
if show_detailed and not is_correct:
354-
lines.append("")
355-
lines.append("-" * 50)
356-
357-
for section in detailed_feedback.sections:
358-
lines.append(f"[{section.importance.upper()}] {section.title}")
359-
lines.append(section.content)
360-
lines.append("")
361-
362-
return "\n".join(lines)
301+
# def _generate_feedback(
302+
# time_result: Optional[TimeComplexityResult],
303+
# space_result: Optional[SpaceComplexityResult],
304+
# test_case_results: List[TestCaseResult],
305+
# analysis: AnalysisResult,
306+
# is_correct: bool,
307+
# eval_options: EvaluationParams,
308+
# ) -> str:
309+
# """Generate comprehensive feedback for the student using FeedbackGenerator."""
310+
# feedback_generator = FeedbackGenerator()
311+
312+
# show_detailed = eval_options.show_detailed_feedback
313+
# level = FeedbackLevel.DETAILED if show_detailed and not is_correct else FeedbackLevel.STANDARD
314+
315+
# detailed_feedback = feedback_generator.generate(analysis, level)
316+
317+
# lines = []
318+
319+
# if is_correct:
320+
# lines.append("✓ Correct! Your algorithm meets the complexity requirements.")
321+
# else:
322+
# lines.append("✗ Your algorithm does not meet the complexity requirements.")
323+
# lines.append("")
324+
# if time_result:
325+
# lines.append("Time Complexity:")
326+
# lines.append(f" • Required: {time_result.expected_answer} or better")
327+
# lines.append(f" • Detected: {time_result.detected_complexity}")
328+
329+
# if time_result.is_correct:
330+
# lines.append(" ✓ Your algorithm meets the time complexity requirement.")
331+
# else:
332+
# lines.append(" ✗ Your algorithm exceeds the allowed time complexity.")
333+
334+
# if space_result:
335+
# lines.append("")
336+
# lines.append("Space Complexity:")
337+
# lines.append(f" • Required: {space_result.expected_answer} or better")
338+
# lines.append(f" • Detected: {space_result.detected_complexity}")
339+
340+
# if space_result.is_correct:
341+
# lines.append(" ✓ Your algorithm meets the space complexity requirement.")
342+
# else:
343+
# lines.append(" ✗ Your algorithm exceeds the allowed space complexity.")
344+
345+
# if test_case_results:
346+
# lines.append("")
347+
# lines.append("Execution Test Cases:")
348+
# for idx, tc in enumerate(test_case_results, 1):
349+
# status = "✓ Passed" if tc.passed else "✗ Failed"
350+
# lines.append(f" Test Case {idx}: {status}")
351+
# if not tc.passed and tc.error_message:
352+
# lines.append(f" Error: {tc.error_message}")
353+
354+
# if show_detailed and not is_correct:
355+
# lines.append("")
356+
# lines.append("-" * 50)
357+
358+
# for section in detailed_feedback.sections:
359+
# lines.append(f"[{section.importance.upper()}] {section.title}")
360+
# lines.append(section.content)
361+
# lines.append("")
362+
363+
# return "\n".join(lines)
363364

364365

365366
def _format_parse_error(parse_result: ParseResult) -> str:
@@ -377,4 +378,75 @@ def _format_parse_error(parse_result: ParseResult) -> str:
377378
lines.append(f" • {warning}")
378379

379380
lines.append("\nPlease check your pseudocode syntax and try again.")
380-
return "\n".join(lines)
381+
return "\n".join(lines)
382+
383+
384+
def _generate_feedback(
385+
time_result: Optional[TimeComplexityResult],
386+
space_result: Optional[SpaceComplexityResult],
387+
test_case_results: List[TestCaseResult],
388+
analysis: AnalysisResult,
389+
is_correct: bool,
390+
eval_options: EvaluationParams,
391+
) -> EvaluationResult:
392+
"""Generate structured feedback for the student using FeedbackGenerator."""
393+
394+
feedback_generator = FeedbackGenerator()
395+
show_detailed = eval_options.show_detailed_feedback
396+
level = FeedbackLevel.DETAILED if show_detailed and not is_correct else FeedbackLevel.STANDARD
397+
detailed_feedback = feedback_generator.generate(analysis, level)
398+
399+
overall_message = "✓ Correct! Your algorithm meets the complexity requirements." if is_correct else "✗ Your algorithm does not meet the complexity requirements."
400+
401+
# Time complexity section
402+
time_feedback = None
403+
if time_result:
404+
time_feedback = {
405+
"required": time_result.expected_answer,
406+
"detected": time_result.detected_complexity,
407+
"is_correct": time_result.is_correct,
408+
"message": "✓ Your algorithm meets the time complexity requirement."
409+
if time_result.is_correct
410+
else "✗ Your algorithm exceeds the allowed time complexity."
411+
}
412+
413+
# Space complexity section
414+
space_feedback = None
415+
if space_result:
416+
space_feedback = {
417+
"required": space_result.expected_answer,
418+
"detected": space_result.detected_complexity,
419+
"is_correct": space_result.is_correct,
420+
"message": "✓ Your algorithm meets the space complexity requirement."
421+
if space_result.is_correct
422+
else "✗ Your algorithm exceeds the allowed space complexity."
423+
}
424+
425+
# Test cases section
426+
test_case_feedback = [
427+
TestCaseFeedback(
428+
index=i + 1,
429+
passed=tc.passed,
430+
error_message=tc.error_message if tc.error_message else ""
431+
)
432+
for i, tc in enumerate(test_case_results)
433+
] if test_case_results else []
434+
435+
# Detailed sections
436+
detailed_sections = [
437+
SectionFeedback(
438+
importance=section.importance,
439+
title=section.title,
440+
content=section.content
441+
)
442+
for section in detailed_feedback.sections
443+
] if show_detailed and not is_correct else []
444+
445+
return EvaluationResult(
446+
is_correct=is_correct,
447+
overall_message=overall_message,
448+
time_complexity=time_feedback,
449+
space_complexity=space_feedback,
450+
test_cases=test_case_feedback,
451+
detailed_sections=detailed_sections
452+
)

evaluation_function/schemas/output_schema.py

Lines changed: 17 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -190,48 +190,26 @@ class ComplexityAnalysis(BaseModel):
190190
confidence: float = Field(default=1.0, ge=0.0, le=1.0)
191191
parse_result: Optional[ParseResult] = None
192192

193+
from pydantic import BaseModel, Field
194+
from typing import List, Optional
193195

194-
class EvaluationResult(BaseModel):
195-
is_correct: bool
196-
time_complexity_result: Optional[TimeComplexityResult] = None
197-
space_complexity_result: Optional[SpaceComplexityResult] = None
198-
score: float = Field(default=0.0, ge=0.0, le=1.0)
199-
analysis: Optional[ComplexityAnalysis] = None
200-
ast: Optional[ProgramNode] = None
201-
feedback: str = ""
202-
feedback_items: List[FeedbackItem] = Field(default_factory=list)
203-
test_case_results: List[TestCaseResult] = Field(default_factory=list)
204-
warnings: List[str] = Field(default_factory=list)
205-
errors: List[str] = Field(default_factory=list)
206-
metadata: Dict[str, Any] = Field(default_factory=dict)
207-
208-
def to_lambda_feedback_response(self) -> Dict[str, Any]:
209-
response = {"is_correct": self.is_correct}
210196

211-
if self.feedback:
212-
response["feedback"] = self.feedback
197+
class TestCaseFeedback(BaseModel):
198+
index: int
199+
passed: bool
200+
error_message: str = Field(default="")
213201

214-
if self.time_complexity_result:
215-
response["time_complexity"] = {
216-
"is_correct": self.time_complexity_result.is_correct,
217-
"student_answer": self.time_complexity_result.student_answer,
218-
"expected_answer": self.time_complexity_result.expected_answer,
219-
"feedback": self.time_complexity_result.feedback,
220-
}
221202

222-
if self.space_complexity_result:
223-
response["space_complexity"] = {
224-
"is_correct": self.space_complexity_result.is_correct,
225-
"student_answer": self.space_complexity_result.student_answer,
226-
"expected_answer": self.space_complexity_result.expected_answer,
227-
"feedback": self.space_complexity_result.feedback,
228-
}
203+
class SectionFeedback(BaseModel):
204+
importance: str
205+
title: str
206+
content: str
229207

230-
if self.analysis:
231-
response["analysis"] = {
232-
"detected_time_complexity": self.analysis.time_complexity.expression,
233-
"detected_space_complexity": self.analysis.space_complexity.expression,
234-
"constructs": [c.model_dump() for c in self.analysis.constructs],
235-
}
236208

237-
return response
209+
class EvaluationResult(BaseModel):
210+
is_correct: bool
211+
overall_message: str
212+
time_complexity: dict = Field(default_factory=dict)
213+
space_complexity: dict = Field(default_factory=dict)
214+
test_cases: List[TestCaseFeedback] = Field(default_factory=list)
215+
detailed_sections: List[SectionFeedback] = Field(default_factory=list)

0 commit comments

Comments
 (0)