-
Notifications
You must be signed in to change notification settings - Fork 1
Rename fields in CaseRecord for consistency with langfuse evaluators
#38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -163,12 +163,12 @@ def _write_results(output_path: Path, input_records: list[CaseRecord], results_b | |||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| with tmp_path.open("w", encoding="utf-8") as outfile: | ||||||||||||||||||||||||||||||||||||||||||||
| for record in input_records: | ||||||||||||||||||||||||||||||||||||||||||||
| case_id = record.case.case_id | ||||||||||||||||||||||||||||||||||||||||||||
| case_id = record.input.case_id | ||||||||||||||||||||||||||||||||||||||||||||
| if case_id in written: | ||||||||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||||||||
| written.add(case_id) | ||||||||||||||||||||||||||||||||||||||||||||
| out_record = results_by_id.get(case_id, record) | ||||||||||||||||||||||||||||||||||||||||||||
| analyzed += int(out_record.analysis is not None) | ||||||||||||||||||||||||||||||||||||||||||||
| analyzed += int(out_record.output is not None) | ||||||||||||||||||||||||||||||||||||||||||||
| outfile.write(out_record.model_dump_json() + "\n") | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| tmp_path.replace(output_path) | ||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -178,7 +178,7 @@ def _write_results(output_path: Path, input_records: list[CaseRecord], results_b | |||||||||||||||||||||||||||||||||||||||||||
| async def _analyze_case(runner: Runner, record: CaseRecord) -> CaseRecord: | ||||||||||||||||||||||||||||||||||||||||||||
| """Run the agent on one case and attach the validated AnalystOutput.""" | ||||||||||||||||||||||||||||||||||||||||||||
| message = google.genai.types.Content( | ||||||||||||||||||||||||||||||||||||||||||||
| role="user", parts=[google.genai.types.Part(text=record.case.model_dump_json())] | ||||||||||||||||||||||||||||||||||||||||||||
| role="user", parts=[google.genai.types.Part(text=record.input.model_dump_json())] | ||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||
| events_async = runner.run_async(session_id=str(uuid.uuid4()), user_id=getpass.getuser(), new_message=message) | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -188,10 +188,10 @@ async def _analyze_case(runner: Runner, record: CaseRecord) -> CaseRecord: | |||||||||||||||||||||||||||||||||||||||||||
| final_text = "".join(part.text or "" for part in event.content.parts if part.text) | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| if not final_text: | ||||||||||||||||||||||||||||||||||||||||||||
| logger.warning("No analyst output produced for case_id=%s", record.case.case_id) | ||||||||||||||||||||||||||||||||||||||||||||
| logger.warning("No analyst output produced for case_id=%s", record.input.case_id) | ||||||||||||||||||||||||||||||||||||||||||||
| return record | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| record.analysis = AnalystOutput.model_validate(_extract_json(final_text.strip())) | ||||||||||||||||||||||||||||||||||||||||||||
| record.output = AnalystOutput.model_validate(_extract_json(final_text.strip())) | ||||||||||||||||||||||||||||||||||||||||||||
| return record | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -200,7 +200,7 @@ async def _safe_analyze_case(runner: Runner, record: CaseRecord) -> CaseRecord: | |||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||
| return await _analyze_case(runner, record) | ||||||||||||||||||||||||||||||||||||||||||||
| except Exception as exc: | ||||||||||||||||||||||||||||||||||||||||||||
| logger.exception("Case failed (case_id=%s): %s", record.case.case_id, exc) | ||||||||||||||||||||||||||||||||||||||||||||
| logger.exception("Case failed (case_id=%s): %s", record.input.case_id, exc) | ||||||||||||||||||||||||||||||||||||||||||||
| return record | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -234,7 +234,7 @@ async def _analyze_cases_to_jsonl( | |||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| for finished in asyncio.as_completed(tasks): | ||||||||||||||||||||||||||||||||||||||||||||
| record = await finished | ||||||||||||||||||||||||||||||||||||||||||||
| analyzed_by_id[record.case.case_id] = record | ||||||||||||||||||||||||||||||||||||||||||||
| analyzed_by_id[record.input.case_id] = record | ||||||||||||||||||||||||||||||||||||||||||||
| outfile.write(record.model_dump_json() + "\n") | ||||||||||||||||||||||||||||||||||||||||||||
| outfile.flush() | ||||||||||||||||||||||||||||||||||||||||||||
| os.fsync(outfile.fileno()) | ||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -253,8 +253,8 @@ async def _main() -> None: | |||||||||||||||||||||||||||||||||||||||||||
| output_path.parent.mkdir(parents=True, exist_ok=True) | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| input_records = _load_records(input_path) | ||||||||||||||||||||||||||||||||||||||||||||
| existing_results = {record.case.case_id: record for record in _load_records(output_path)} | ||||||||||||||||||||||||||||||||||||||||||||
| to_run = [r for r in input_records if existing_results.get(r.case.case_id, r).analysis is None] | ||||||||||||||||||||||||||||||||||||||||||||
| existing_results = {record.input.case_id: record for record in _load_records(output_path)} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
| existing_results = {record.input.case_id: record for record in _load_records(output_path)} | |
| # Load existing results from the output file, and detect any lines that could not be parsed | |
| existing_records = list(_load_records(output_path)) | |
| existing_results = {record.input.case_id: record for record in existing_records} | |
| # Compare parsed records against total lines to surface potential legacy/invalid rows | |
| if output_path.exists(): | |
| try: | |
| with output_path.open("r", encoding="utf-8") as f: | |
| total_lines = sum(1 for _ in f) | |
| except OSError: | |
| total_lines = None | |
| if total_lines is not None and total_lines > len(existing_records): | |
| logger.warning( | |
| "Detected %d/%d records in %s that could not be parsed. " | |
| "These may be legacy or invalid rows, and resume behavior may be affected.", | |
| total_lines - len(existing_records), | |
| total_lines, | |
| output_path, | |
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renaming these serialized fields will break parsing of any existing JSON/JSONL persisted with the old keys (
case,groundtruth,analysis). Since the agent reads prior results viaCaseRecord.model_validate_json(...)for resume behavior, legacy lines will fail validation and be skipped. Consider adding Pydantic v2 compatibility viavalidation_alias(e.g.,AliasChoices('input', 'case'),AliasChoices('expected_output', 'groundtruth'),AliasChoices('output', 'analysis')) so existing artifacts remain readable while emitting the new field names.