Skip to content

Commit 321837a

Browse files
author
Cody McCodePants
committed
Fix CI lint and remove Python 3.11 workflow
1 parent 0732e89 commit 321837a

9 files changed

Lines changed: 60 additions & 41 deletions

File tree

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
- name: Set up Python
1919
uses: actions/setup-python@v5
2020
with:
21-
python-version: "3.11"
21+
python-version: "3.12"
2222

2323
- name: Install lint dependencies
2424
run: |
@@ -39,7 +39,7 @@ jobs:
3939
strategy:
4040
fail-fast: false
4141
matrix:
42-
python-version: ["3.11", "3.12", "3.13"]
42+
python-version: ["3.12", "3.13"]
4343

4444
steps:
4545
- name: Checkout

netopsbench/agents/tracing.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import threading
77
import uuid
88
from datetime import UTC, datetime
9-
from typing import Any
9+
from typing import Any, cast
1010

1111
from netopsbench.agents._trace_utils import jsonable as _jsonable
1212

@@ -110,7 +110,9 @@ def on_llm_end(self, response: Any, *, run_id: Any, parent_run_id: Any = None, *
110110
del kwargs
111111
recorder.record_llm_response(response, run_id=run_id, parent_run_id=parent_run_id)
112112

113-
def on_llm_error(self, error: BaseException, *, run_id: Any, parent_run_id: Any = None, **kwargs: Any) -> None:
113+
def on_llm_error(
114+
self, error: BaseException, *, run_id: Any, parent_run_id: Any = None, **kwargs: Any
115+
) -> None:
114116
del kwargs
115117
recorder.record_error(
116118
stage="llm",
@@ -175,7 +177,9 @@ def record_llm_request(
175177
"provider": provider,
176178
"extra": {
177179
"llm_request": {
178-
"messages": [_message_payload(message, index=index) for index, message in enumerate(messages or [], 1)]
180+
"messages": [
181+
_message_payload(message, index=index) for index, message in enumerate(messages or [], 1)
182+
]
179183
}
180184
},
181185
}
@@ -297,7 +301,9 @@ def record_tool_error(self, *, error: BaseException, run_id: Any, parent_run_id:
297301
step["ended_at"] = _isoformat(datetime.now(UTC))
298302
step["duration_seconds"] = _duration_seconds(step.get("started_at"), step.get("ended_at"))
299303

300-
def record_error(self, *, stage: str, error: BaseException | str, run_id: Any = None, parent_run_id: Any = None) -> None:
304+
def record_error(
305+
self, *, stage: str, error: BaseException | str, run_id: Any = None, parent_run_id: Any = None
306+
) -> None:
301307
if not self.enabled:
302308
return
303309
call_id = str(run_id) if run_id else None
@@ -399,7 +405,11 @@ async def chat(self, messages: list[dict[str, Any]], **kwargs: Any) -> Any:
399405
)
400406
client = AsyncOpenAI(api_key=self.api_key, base_url=self.base_url, **self.client_kwargs)
401407
try:
402-
response = await client.chat.completions.create(model=self.model, messages=messages, **kwargs)
408+
response = await client.chat.completions.create(
409+
model=self.model,
410+
messages=cast(Any, messages),
411+
**kwargs,
412+
)
403413
except Exception as exc:
404414
self.recorder.record_error(stage="llm", error=exc, run_id=run_id)
405415
raise

netopsbench/cli/trace.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ def add_trace_subparser(subparsers: argparse._SubParsersAction) -> None:
1515
trace_parser = subparsers.add_parser("trace", help="Inspect and export agent runtime traces")
1616
trace_sub = trace_parser.add_subparsers(dest="trace_action", required=True)
1717
trace_list = trace_sub.add_parser("list", help="List runs with trace artifacts")
18-
trace_list.add_argument("--limit", type=int, default=20, help="Maximum number of runs to show. Default: %(default)s.")
18+
trace_list.add_argument(
19+
"--limit", type=int, default=20, help="Maximum number of runs to show. Default: %(default)s."
20+
)
1921
trace_export = trace_sub.add_parser("export", help="Export run traces as a Harbor jobs directory")
2022
trace_export.add_argument("run_id", help="Run id, for example run-20260605T124040Z")
2123
trace_export.add_argument("--output", required=True, help="Output Harbor jobs directory")

netopsbench/platform/session/orchestrator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ def callback(episode_result: dict) -> dict:
353353
except Exception as exc:
354354
trace_recorder.record_error(stage="agent", error=exc)
355355
ended_at = self._timestamp()
356-
diagnosis_payload = {
356+
diagnosis_payload: dict[str, Any] = {
357357
"error": str(exc),
358358
"success": False,
359359
"time_taken_seconds": max(0.0, (ended_at - start_time).total_seconds()),

netopsbench/platform/session/tracing.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -680,8 +680,15 @@ def _metrics_payload(
680680
) -> dict[str, Any]:
681681
recorder_metrics = trace_recorder.metrics() if trace_recorder is not None else {}
682682
payload = {
683-
"time_taken_seconds": float(diagnosis_payload.get("time_taken_seconds") or max(0.0, (ended_at - started_at).total_seconds())),
684-
"tool_calls_count": len((trace_recorder.tool_calls() if trace_recorder is not None else []) or diagnosis_payload.get("tool_calls") or metadata.get("tool_calls") or []),
683+
"time_taken_seconds": float(
684+
diagnosis_payload.get("time_taken_seconds") or max(0.0, (ended_at - started_at).total_seconds())
685+
),
686+
"tool_calls_count": len(
687+
(trace_recorder.tool_calls() if trace_recorder is not None else [])
688+
or diagnosis_payload.get("tool_calls")
689+
or metadata.get("tool_calls")
690+
or []
691+
),
685692
}
686693
for key in ("input_tokens", "output_tokens", "total_tokens", "llm_call_count"):
687694
if recorder_metrics.get(key):
@@ -745,7 +752,9 @@ def _steps_from_tool_calls(tool_calls: Any) -> list[dict[str, Any]]:
745752
"name": (item if isinstance(item, dict) else {"tool": str(item)}).get("tool")
746753
or (item if isinstance(item, dict) else {}).get("name")
747754
or "tool",
748-
"args": (item if isinstance(item, dict) else {}).get("args") or (item if isinstance(item, dict) else {}).get("input") or {},
755+
"args": (item if isinstance(item, dict) else {}).get("args")
756+
or (item if isinstance(item, dict) else {}).get("input")
757+
or {},
749758
}
750759
for index, item in enumerate(tool_calls, 1)
751760
]
@@ -756,9 +765,7 @@ def _final_diagnosis_step(diagnosis_payload: dict[str, Any], *, ended_at: dateti
756765
verdict = final.get("verdict") or ("error" if final.get("error") else "unknown")
757766
fault_type = final.get("fault_type")
758767
location = final.get("location") if isinstance(final.get("location"), dict) else {}
759-
location_text = ", ".join(
760-
str(value) for value in (location or {}).values() if value not in (None, "")
761-
)
768+
location_text = ", ".join(str(value) for value in (location or {}).values() if value not in (None, ""))
762769
parts = [f"Final diagnosis: {verdict}"]
763770
if fault_type:
764771
parts.append(f"fault_type={fault_type}")
@@ -810,7 +817,9 @@ def _matching_result_row(result_rows: list[dict[str, Any]], index_row: dict[str,
810817
if row.get("trace_id") == trace_id:
811818
return row
812819
for row in result_rows:
813-
if row.get("scenario_id") == index_row.get("scenario_id") and row.get("episode_id") == index_row.get("episode_id"):
820+
if row.get("scenario_id") == index_row.get("scenario_id") and row.get("episode_id") == index_row.get(
821+
"episode_id"
822+
):
814823
return row
815824
return None
816825

@@ -863,16 +872,16 @@ def _run_times(run_path: Path, index_rows: list[dict[str, Any]]) -> dict[str, st
863872
report_path = run_path / "report.json"
864873
if report_path.exists():
865874
try:
866-
summary = (json.loads(report_path.read_text(encoding="utf-8")).get("summary") or {})
875+
summary = json.loads(report_path.read_text(encoding="utf-8")).get("summary") or {}
867876
if summary.get("started_at") and summary.get("completed_at"):
868877
return {
869878
"started_at": _normalise_iso_z(summary["started_at"]),
870879
"finished_at": _normalise_iso_z(summary["completed_at"]),
871880
}
872881
except Exception:
873882
pass
874-
starts = [row.get("started_at") for row in index_rows if row.get("started_at")]
875-
ends = [row.get("ended_at") for row in index_rows if row.get("ended_at")]
883+
starts = [str(value) for row in index_rows if (value := row.get("started_at"))]
884+
ends = [str(value) for row in index_rows if (value := row.get("ended_at"))]
876885
now = _isoformat(datetime.now(UTC))
877886
return {
878887
"started_at": _normalise_iso_z(min(starts) if starts else now),

tests/test_api_sessions.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -327,9 +327,7 @@ def diagnose(self, context):
327327
context.trace.record_llm_request([{"role": "user", "content": "diagnose"}], run_id="llm-1")
328328
context.trace.record_llm_response(
329329
SimpleNamespace(
330-
generations=[
331-
[SimpleNamespace(message=SimpleNamespace(type="ai", content="checking"))]
332-
]
330+
generations=[[SimpleNamespace(message=SimpleNamespace(type="ai", content="checking"))]]
333331
),
334332
run_id="llm-1",
335333
)

tests/test_commands_cli.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -357,9 +357,7 @@ def fake_launch(folder, *, host, port):
357357
assert "synced traces:" in out
358358
expected = tmp_path / ".netopsbench" / "harbor-jobs"
359359
assert launched == {"folder": expected, "host": "127.0.0.1", "port": "55668"}
360-
assert (
361-
expected / "netopsbench-run-20260605T124040Z" / "scenario-1__case-1" / "agent" / "trajectory.json"
362-
).exists()
360+
assert (expected / "netopsbench-run-20260605T124040Z" / "scenario-1__case-1" / "agent" / "trajectory.json").exists()
363361

364362

365363
def test_cli_trace_list_shows_trace_runs(tmp_path, monkeypatch, capsys):
@@ -406,12 +404,8 @@ def fake_launch(folder, *, host, port):
406404
expected = tmp_path / ".netopsbench" / "harbor-jobs"
407405
assert "synced traces:" in out
408406
assert launched == {"folder": expected, "host": "127.0.0.1", "port": "8080-8089"}
409-
assert (
410-
expected / "netopsbench-run-20260605T124040Z" / "scenario-1__case-1" / "agent" / "trajectory.json"
411-
).exists()
412-
assert (
413-
expected / "netopsbench-run-20260605T123000Z" / "scenario-1__case-1" / "agent" / "trajectory.json"
414-
).exists()
407+
assert (expected / "netopsbench-run-20260605T124040Z" / "scenario-1__case-1" / "agent" / "trajectory.json").exists()
408+
assert (expected / "netopsbench-run-20260605T123000Z" / "scenario-1__case-1" / "agent" / "trajectory.json").exists()
415409

416410

417411
def test_cli_trace_view_without_run_id_syncs_all_trace_runs(tmp_path, monkeypatch, capsys):
@@ -441,12 +435,8 @@ def fake_launch(folder, *, host, port):
441435
expected = tmp_path / ".netopsbench" / "harbor-jobs"
442436
assert "synced traces:" in out
443437
assert launched == {"folder": expected, "host": "127.0.0.1", "port": "8080-8089"}
444-
assert (
445-
expected / "netopsbench-run-20260605T124040Z" / "scenario-1__case-1" / "agent" / "trajectory.json"
446-
).exists()
447-
assert (
448-
expected / "netopsbench-run-20260605T123000Z" / "scenario-1__case-1" / "agent" / "trajectory.json"
449-
).exists()
438+
assert (expected / "netopsbench-run-20260605T124040Z" / "scenario-1__case-1" / "agent" / "trajectory.json").exists()
439+
assert (expected / "netopsbench-run-20260605T123000Z" / "scenario-1__case-1" / "agent" / "trajectory.json").exists()
450440

451441

452442
def test_cli_benchmark_prepare_runs_topology_then_scenario_generation(tmp_path, monkeypatch, capsys):

tests/test_example_agents.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ def test_minimal_deepagent_parses_final_json_block(monkeypatch):
251251
SimpleNamespace(
252252
type="ai",
253253
content=(
254-
'```json\n'
254+
"```json\n"
255255
'{"verdict":"fault_detected","fault_type":"link_down",'
256256
'"location":{"device":"leaf1","interface":"Ethernet8"}}\n'
257257
"```"

tests/test_session_tracing.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ def __init__(self, **kwargs):
5252
def test_disabled_trace_recorder_preserves_api_without_collecting():
5353
recorder = AgentTraceRecorder.disabled()
5454
run_id = recorder.record_llm_request([{"role": "user", "content": "diagnose"}], model="gpt-test")
55-
recorder.record_llm_response(SimpleNamespace(generations=[[SimpleNamespace(message=SimpleNamespace(content="ok"))]]), run_id=run_id)
55+
recorder.record_llm_response(
56+
SimpleNamespace(generations=[[SimpleNamespace(message=SimpleNamespace(content="ok"))]]), run_id=run_id
57+
)
5658
recorder.record_tool_start(name="get_topology", args={"verbose": True}, run_id="tool-1")
5759
recorder.record_tool_end(output={"ok": True}, run_id="tool-1")
5860
recorder.record_error(stage="agent", error=RuntimeError("boom"))
@@ -66,7 +68,9 @@ def test_disabled_trace_recorder_preserves_api_without_collecting():
6668

6769
def test_trace_recorder_preserves_tool_call_llm_response_payload():
6870
recorder = AgentTraceRecorder()
69-
run_id = recorder.record_llm_request([{"role": "user", "content": "inspect topology"}], model="deepseek-v4-pro", provider="deepseek")
71+
run_id = recorder.record_llm_request(
72+
[{"role": "user", "content": "inspect topology"}], model="deepseek-v4-pro", provider="deepseek"
73+
)
7074

7175
recorder.record_llm_response(
7276
SimpleNamespace(
@@ -277,7 +281,13 @@ def test_export_traces_writes_harbor_jobs_directory(tmp_path):
277281
"episodes": [
278282
{
279283
"episode": {"episode_id": "ep1"},
280-
"diagnosis": {"trace": {"trace_id": trace_result.trace_id, "case_id": trace_result.case_id, "atif_path": trace_result.atif_path}},
284+
"diagnosis": {
285+
"trace": {
286+
"trace_id": trace_result.trace_id,
287+
"case_id": trace_result.case_id,
288+
"atif_path": trace_result.atif_path,
289+
}
290+
},
281291
}
282292
]
283293
}

0 commit comments

Comments
 (0)