Skip to content

Commit 454476c

Browse files
committed
Add model identity event fixtures
1 parent e1a818c commit 454476c

5 files changed

Lines changed: 134 additions & 4 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"type":"result","subtype":"success","is_error":false,"modelUsage":{"claude-haiku-4-5-20251001":{},"claude-sonnet-4-6":{}},"usage":{}}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{"type":"system","subtype":"init","uuid":"00000000-0000-0000-0000-000000000000","session_id":"00000000-0000-0000-0000-000000000000","permissionMode":"acceptEdits","model":"claude-sonnet-4-6"}
2+
{"type":"assistant","uuid":"00000000-0000-0000-0000-000000000000","session_id":"00000000-0000-0000-0000-000000000000","message":{"id":"msg_sanitized","type":"message","role":"assistant","model":"claude-sonnet-4-6","content":[],"stop_reason":null,"stop_sequence":null,"usage":{}}}
3+
{"type":"result","subtype":"success","is_error":false,"modelUsage":{"claude-haiku-4-5-20251001":{},"claude-sonnet-4-6":{}},"usage":{}}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{"type":"thread.started","thread_id":"00000000-0000-0000-0000-000000000000"}
2+
{"type":"turn.started"}
3+
{"type":"item.completed","item":{"id":"item_0","type":"agent_message","text":"Sanitized agent progress update."}}
4+
{"type":"turn.completed","usage":{}}

‎tests/test_model_identity.py‎

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,50 @@
11
import unittest
2+
from pathlib import Path
23

34
from agentlab.model_identity import model_identity_from_events
45

56

7+
FIXTURES = Path(__file__).parent / "fixtures" / "model_identity"
8+
9+
610
class ModelIdentityTest(unittest.TestCase):
11+
def test_captured_event_stream_fixtures_parse_model_identity(self):
12+
cases = [
13+
{
14+
"name": "claude_code_system_init_assistant_message_and_result_usage",
15+
"fixture": (
16+
FIXTURES / "claude" / "system-init-assistant-result.jsonl"
17+
),
18+
"requested_model_name": "requested-sonnet",
19+
"expected_model_name": "claude-sonnet-4-6",
20+
"expected_model_source": "events",
21+
"expected_requested_model_name": "requested-sonnet",
22+
},
23+
{
24+
"name": "codex_thread_turn_completed_usage_without_runtime_model",
25+
"fixture": FIXTURES / "codex" / "thread-turn-usage-only.jsonl",
26+
"requested_model_name": "gpt-requested",
27+
"expected_model_name": "gpt-requested",
28+
"expected_model_source": "explicit",
29+
"expected_requested_model_name": "gpt-requested",
30+
},
31+
]
32+
33+
for case in cases:
34+
with self.subTest(case["name"]):
35+
events = case["fixture"].read_text(encoding="utf-8")
36+
identity = model_identity_from_events(
37+
events,
38+
requested_model_name=case["requested_model_name"],
39+
)
40+
41+
self.assertEqual(identity.model_name, case["expected_model_name"])
42+
self.assertEqual(identity.model_source, case["expected_model_source"])
43+
self.assertEqual(
44+
identity.requested_model_name,
45+
case["expected_requested_model_name"],
46+
)
47+
748
def test_prefers_direct_event_model_over_requested_model(self):
849
identity = model_identity_from_events(
950
'{"type":"turn.completed","model":"actual-model"}\n',
@@ -33,6 +74,44 @@ def test_uses_single_model_usage_entry_as_last_resort(self):
3374
self.assertEqual(identity.model_name, "only-model")
3475
self.assertEqual(identity.model_source, "events")
3576

77+
def test_ambiguous_multimodel_usage_without_primary_model_is_unknown(self):
78+
events = (
79+
FIXTURES / "claude" / "result-multimodel-usage-only.jsonl"
80+
).read_text(encoding="utf-8")
81+
82+
identity = model_identity_from_events(events, requested_model_name=None)
83+
84+
self.assertIsNone(identity.model_name)
85+
self.assertEqual(identity.model_source, "unknown")
86+
self.assertIsNone(identity.requested_model_name)
87+
88+
def test_ambiguous_multimodel_usage_falls_back_to_requested_model(self):
89+
events = (
90+
FIXTURES / "claude" / "result-multimodel-usage-only.jsonl"
91+
).read_text(encoding="utf-8")
92+
93+
identity = model_identity_from_events(
94+
events,
95+
requested_model_name="requested-sonnet",
96+
)
97+
98+
self.assertEqual(identity.model_name, "requested-sonnet")
99+
self.assertEqual(identity.model_source, "explicit")
100+
self.assertEqual(identity.requested_model_name, "requested-sonnet")
101+
102+
def test_malformed_and_irrelevant_lines_are_ignored_safely(self):
103+
identity = model_identity_from_events(
104+
"not json\n"
105+
"[]\n"
106+
'{"type":"turn.completed","usage":{"input_tokens":1}}\n'
107+
'{"type":"turn.completed","model":"actual-model"}\n',
108+
requested_model_name="requested-model",
109+
)
110+
111+
self.assertEqual(identity.model_name, "actual-model")
112+
self.assertEqual(identity.model_source, "events")
113+
self.assertEqual(identity.requested_model_name, "requested-model")
114+
36115
def test_falls_back_to_requested_model_when_events_do_not_identify_model(self):
37116
identity = model_identity_from_events(
38117
'{"type":"turn.completed","usage":{"input_tokens":1}}\n',

‎tests/test_results.py‎

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
from agentlab.summary import summarize_trials
1010

1111

12+
MODEL_IDENTITY_FIXTURES = Path(__file__).parent / "fixtures" / "model_identity"
13+
14+
1215
class ResultsTest(unittest.TestCase):
1316
def test_file_artifacts_feed_trial_listing_summaries_reviews_and_evidence(self):
1417
with tempfile.TemporaryDirectory() as temp:
@@ -175,10 +178,11 @@ def test_load_results_backfills_model_identity_from_claude_events(self):
175178
run_dir = Path(temp) / "run-1"
176179
run_dir.mkdir()
177180
(run_dir / "claude-events.jsonl").write_text(
178-
'{"type":"system","model":"claude-sonnet-4-6"}\n'
179-
'{"type":"result","modelUsage":{'
180-
'"claude-haiku-4-5-20251001":{"costUSD":0.1},'
181-
'"claude-sonnet-4-6":{"costUSD":0.2}}}\n',
181+
(
182+
MODEL_IDENTITY_FIXTURES
183+
/ "claude"
184+
/ "system-init-assistant-result.jsonl"
185+
).read_text(encoding="utf-8"),
182186
encoding="utf-8",
183187
)
184188
result_path = run_dir / "result.json"
@@ -209,6 +213,45 @@ def test_load_results_backfills_model_identity_from_claude_events(self):
209213
)
210214
self.assertEqual(result["agent_harness_config"]["model_source"], "events")
211215

216+
def test_load_results_does_not_infer_model_from_codex_usage_only_fixture(self):
217+
with tempfile.TemporaryDirectory() as temp:
218+
run_dir = Path(temp) / "run-1"
219+
run_dir.mkdir()
220+
(run_dir / "codex-events.jsonl").write_text(
221+
(
222+
MODEL_IDENTITY_FIXTURES
223+
/ "codex"
224+
/ "thread-turn-usage-only.jsonl"
225+
).read_text(encoding="utf-8"),
226+
encoding="utf-8",
227+
)
228+
result_path = run_dir / "result.json"
229+
result_path.write_text(
230+
json.dumps(
231+
{
232+
"trial_kind": "agent_trial",
233+
"run_dir": str(run_dir),
234+
"agent_name": "codex",
235+
"model_name": "gpt-requested",
236+
"agent_harness_config": {
237+
"agent_harness": "codex",
238+
"agent_adapter": "codex_cli",
239+
"model_name": "gpt-requested",
240+
"model_source": "explicit",
241+
},
242+
}
243+
),
244+
encoding="utf-8",
245+
)
246+
247+
result = load_results([result_path])[0]
248+
249+
self.assertEqual(result["model_name"], "gpt-requested")
250+
harness_config = result["agent_harness_config"]
251+
self.assertEqual(harness_config["model_name"], "gpt-requested")
252+
self.assertEqual(harness_config["model_source"], "explicit")
253+
self.assertEqual(harness_config["requested_model_name"], "gpt-requested")
254+
212255
def test_load_results_backfills_codex_event_model_over_requested_model(self):
213256
with tempfile.TemporaryDirectory() as temp:
214257
run_dir = Path(temp) / "run-1"

0 commit comments

Comments
 (0)