steerable-agent-runtime 0.6.8__tar.gz → 0.6.10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/PKG-INFO +1 -1
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/pyproject.toml +1 -1
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/loop.py +20 -3
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/model_resolve.py +77 -1
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime.egg-info/PKG-INFO +1 -1
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_antihallucination.py +37 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_gateway_catalog.py +6 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_loop.py +1 -1
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_model_resolve.py +41 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/README.md +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/setup.cfg +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/__init__.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/antihallucination.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/approval.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/approval_policy.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/ask_user.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/branch.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/cache_control.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/calibration.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/compaction.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/config.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/default.harness.json +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/default.harness.yaml +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/errors.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/gateway_catalog.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/handoff.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/harness.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/harness_spec.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/history.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/hooks.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/llm/__init__.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/llm/anthropic_native.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/llm/compat.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/llm/errors.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/llm/google_genai.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/llm/openai_compat.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/llm/openai_responses.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/llm/parts.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/llm/presets.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/llm/system_proxy.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/maintenance.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/mcp.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/mcp_server.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/model_catalog.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/model_info.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/observation_aging.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/orchestration.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/otel.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/plugins.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/pool.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/pricing.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/pseudo.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/recording.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/reminders.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/replay.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/resume.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/retry.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/sandboxed.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/skills.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/spill.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/storage/__init__.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/storage/in_memory.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/storage/sqlalchemy_store.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/storage/sqlite_store.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/storage/write_lease.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/subagent.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/todo.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/tokens.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/tool_schema.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/tool_search.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/tools.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/tracing.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/transport/__init__.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/transport/fastapi_sse.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/transport/stdio_jsonrpc.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/world_state.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime.egg-info/SOURCES.txt +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime.egg-info/dependency_links.txt +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime.egg-info/requires.txt +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime.egg-info/top_level.txt +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_approval.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_approval_policy.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_ask_user.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_branch.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_cache_control.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_cache_instrumentation.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_calibration.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_compaction.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_config.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_content_parts.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_error_taxonomy.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_fragment_bounds.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_golden.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_handoff.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_harness.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_harness_spec.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_history.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_history_persistence.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_hooks.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_in_memory_storage.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_llm_gemini_wire.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_llm_responses_wire.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_llm_stream_e2e.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_llm_wire_helpers.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_long_session.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_loop_cancellation.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_loop_replay.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_loop_sandbox_event.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_maintenance.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_mcp.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_mcp_server.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_model_catalog.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_model_equivalence.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_model_info.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_observation_aging.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_orchestration.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_otel.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_parallel_tools.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_plugins.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_provider_compat.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_provider_presets.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_pseudo.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_recording.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_reminders.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_replay_crosslang.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_resume.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_retry_hooks.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_safety_gate.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_sandboxed.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_skills.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_soft_timeout.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_spill.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_sqlite_storage.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_steer.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_storage_contract.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_stream_strip.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_subagent.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_system_proxy.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_todo.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_todo_gate.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_tokens.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_tool_exposure.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_tool_hygiene.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_tool_router.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_tool_schema.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_tool_timeout.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_trace_recorder.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_transport_jsonrpc.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_transport_sse.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_usage_attribution.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_world_state.py +0 -0
- {steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_write_lease.py +0 -0
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/loop.py
RENAMED
|
@@ -877,6 +877,13 @@ class CoreLoop:
|
|
|
877
877
|
else None
|
|
878
878
|
)
|
|
879
879
|
wrap_up = False
|
|
880
|
+
# Terminal that triggered the current wrap-up when a budget wall
|
|
881
|
+
# (maxRounds) offered narration. The wrap-up summary is an interim
|
|
882
|
+
# report, not task completion: the no-tools terminal below restores
|
|
883
|
+
# this decision so the host's auto-continue sees budget_exhausted and
|
|
884
|
+
# resumes with a fresh budget instead of mistaking the summary for a
|
|
885
|
+
# finished task.
|
|
886
|
+
wrap_up_terminal: CompletionDecision | None = None
|
|
880
887
|
wrap_up_tool_rounds_used = 0
|
|
881
888
|
idle_cut_count = 0
|
|
882
889
|
|
|
@@ -1051,7 +1058,10 @@ class CoreLoop:
|
|
|
1051
1058
|
)
|
|
1052
1059
|
yield LoopEvent(
|
|
1053
1060
|
"budget_exhausted",
|
|
1054
|
-
|
|
1061
|
+
# "budget" not "kind": the sidecar wraps loop events as
|
|
1062
|
+
# {"kind": <event kind>, **data} — a data["kind"] would
|
|
1063
|
+
# clobber the envelope kind hosts dispatch on.
|
|
1064
|
+
{"budget": "rounds", "rounds": self._config.max_rounds},
|
|
1055
1065
|
)
|
|
1056
1066
|
narrate = await self._offer_narration(
|
|
1057
1067
|
"", round_index, had_tool_calls=True, ctx=ctx,
|
|
@@ -1060,6 +1070,7 @@ class CoreLoop:
|
|
|
1060
1070
|
if narrate is not None:
|
|
1061
1071
|
prompt, reason = narrate
|
|
1062
1072
|
completion_redos += 1
|
|
1073
|
+
wrap_up_terminal = decision
|
|
1063
1074
|
mark_wrap_up()
|
|
1064
1075
|
manager.append_fragment(NarrationRequest(prompt))
|
|
1065
1076
|
yield LoopEvent(
|
|
@@ -1362,7 +1373,7 @@ class CoreLoop:
|
|
|
1362
1373
|
if exhausted:
|
|
1363
1374
|
yield LoopEvent(
|
|
1364
1375
|
"budget_exhausted",
|
|
1365
|
-
{"
|
|
1376
|
+
{"budget": "tokens", "used": budget_state.tokens_used},
|
|
1366
1377
|
)
|
|
1367
1378
|
decision = CompletionDecision(
|
|
1368
1379
|
status="budget_exhausted", reason="token budget exceeded"
|
|
@@ -1586,7 +1597,13 @@ class CoreLoop:
|
|
|
1586
1597
|
|
|
1587
1598
|
# ── decide: no tool calls → terminal (unless a hook vetoes) ──
|
|
1588
1599
|
if not tool_calls:
|
|
1589
|
-
if
|
|
1600
|
+
if wrap_up_terminal is not None:
|
|
1601
|
+
# Budget-wall wrap-up: the narration summary is an interim
|
|
1602
|
+
# report, not a finished task. Restore the wall's terminal
|
|
1603
|
+
# (budget_exhausted) so the host auto-continues with a
|
|
1604
|
+
# fresh budget instead of stopping on a half-done task.
|
|
1605
|
+
decision = wrap_up_terminal
|
|
1606
|
+
elif content.strip():
|
|
1590
1607
|
decision = CompletionDecision(
|
|
1591
1608
|
status="completed",
|
|
1592
1609
|
reason="assistant produced final response with no pending tools",
|
|
@@ -19,7 +19,7 @@ silent defaults are exactly how the drift stayed invisible.
|
|
|
19
19
|
from __future__ import annotations
|
|
20
20
|
|
|
21
21
|
from dataclasses import dataclass
|
|
22
|
-
from typing import Literal
|
|
22
|
+
from typing import Any, Literal
|
|
23
23
|
|
|
24
24
|
from .model_catalog import MODEL_ENTRIES, PROVIDER_ENTRIES
|
|
25
25
|
|
|
@@ -173,6 +173,82 @@ def provider_endpoint(provider: str) -> ProviderEndpoint | None:
|
|
|
173
173
|
return ProviderEndpoint(provider=provider, api_base_url=api, env_vars=env)
|
|
174
174
|
|
|
175
175
|
|
|
176
|
+
#: Sidecar factory kinds the host settings picker maps onto. Catalog ids
|
|
177
|
+
#: not in this table (and whose models are not all Anthropic-format) speak
|
|
178
|
+
#: OpenAI-compatible chat completions.
|
|
179
|
+
_WIRE_KIND_BY_PROVIDER: dict[str, str] = {
|
|
180
|
+
"anthropic": "anthropic",
|
|
181
|
+
"google": "google",
|
|
182
|
+
"google-vertex": "google",
|
|
183
|
+
"google-vertex-anthropic": "anthropic",
|
|
184
|
+
"xai": "openai-responses",
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
#: models.dev omits ``api`` for first-party SDKs. Host pickers still need a
|
|
188
|
+
#: fillable URL so the user is not dropped into a blank field for OpenAI /
|
|
189
|
+
#: Anthropic / Gemini / Groq / xAI.
|
|
190
|
+
_SDK_DEFAULT_BASE_URLS: dict[str, str] = {
|
|
191
|
+
"openai": "https://api.openai.com/v1",
|
|
192
|
+
"anthropic": "https://api.anthropic.com",
|
|
193
|
+
"google": "https://generativelanguage.googleapis.com",
|
|
194
|
+
"groq": "https://api.groq.com/openai/v1",
|
|
195
|
+
"xai": "https://api.x.ai/v1",
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _wire_kind(provider_id: str, api: str | None, formats: set[str]) -> str:
|
|
200
|
+
mapped = _WIRE_KIND_BY_PROVIDER.get(provider_id)
|
|
201
|
+
if mapped:
|
|
202
|
+
return mapped
|
|
203
|
+
if api and "/anthropic" in api.lower():
|
|
204
|
+
return "anthropic"
|
|
205
|
+
if formats and formats <= {"anthropic"}:
|
|
206
|
+
return "anthropic"
|
|
207
|
+
return "openai_compat"
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def describe_catalog_providers() -> list[dict[str, Any]]:
|
|
211
|
+
"""Host settings pickers: one row per catalogued serving provider.
|
|
212
|
+
|
|
213
|
+
Same service pattern as ``compat.describe`` / ``presets.describe``: the
|
|
214
|
+
bundled catalog is the source of truth for default URLs and chat-capable
|
|
215
|
+
model ids. ``wireKind`` is the sidecar factory kind. ``apiBaseUrl`` is
|
|
216
|
+
None when neither the catalog nor the first-party SDK default knows an
|
|
217
|
+
endpoint — the host then collects one by hand. Embedding / TTS / video
|
|
218
|
+
ids (``tool_format == "none"``) are omitted so the model list is what a
|
|
219
|
+
chat agent can actually call.
|
|
220
|
+
"""
|
|
221
|
+
models_by_provider: dict[str, list[str]] = {}
|
|
222
|
+
formats_by_provider: dict[str, set[str]] = {}
|
|
223
|
+
for key, row in MODEL_ENTRIES.items():
|
|
224
|
+
provider_id, _, model_id = key.partition("/")
|
|
225
|
+
if not provider_id or not model_id:
|
|
226
|
+
continue
|
|
227
|
+
_context, _modalities, tool_format, _reasoning = row
|
|
228
|
+
if tool_format == "none":
|
|
229
|
+
continue
|
|
230
|
+
models_by_provider.setdefault(provider_id, []).append(model_id)
|
|
231
|
+
formats_by_provider.setdefault(provider_id, set()).add(tool_format)
|
|
232
|
+
|
|
233
|
+
described: list[dict[str, Any]] = []
|
|
234
|
+
for provider_id in sorted(PROVIDER_ENTRIES):
|
|
235
|
+
api, env_vars = PROVIDER_ENTRIES[provider_id]
|
|
236
|
+
models = models_by_provider.get(provider_id, [])
|
|
237
|
+
if not models:
|
|
238
|
+
continue
|
|
239
|
+
formats = formats_by_provider.get(provider_id, set())
|
|
240
|
+
described.append(
|
|
241
|
+
{
|
|
242
|
+
"id": provider_id,
|
|
243
|
+
"apiBaseUrl": api or _SDK_DEFAULT_BASE_URLS.get(provider_id),
|
|
244
|
+
"envVars": list(env_vars),
|
|
245
|
+
"wireKind": _wire_kind(provider_id, api, formats),
|
|
246
|
+
"models": sorted(models),
|
|
247
|
+
}
|
|
248
|
+
)
|
|
249
|
+
return described
|
|
250
|
+
|
|
251
|
+
|
|
176
252
|
def catalog_provider_for_base_url(base_url: str | None) -> str | None:
|
|
177
253
|
"""The catalog provider serving ``base_url``, matched by URL host.
|
|
178
254
|
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_antihallucination.py
RENAMED
|
@@ -17,6 +17,7 @@ from steerable_agent_runtime import (
|
|
|
17
17
|
AntiHallucinationConfig,
|
|
18
18
|
AntiHallucinationHooks,
|
|
19
19
|
CoreLoop,
|
|
20
|
+
LoopConfig,
|
|
20
21
|
LoopEvent,
|
|
21
22
|
RouterToolExecutor,
|
|
22
23
|
ToolRouter,
|
|
@@ -521,6 +522,42 @@ async def test_narration_round_on_empty_terminal() -> None:
|
|
|
521
522
|
)
|
|
522
523
|
|
|
523
524
|
|
|
525
|
+
@pytest.mark.asyncio
|
|
526
|
+
async def test_narration_at_max_rounds_wall_stays_budget_exhausted() -> None:
|
|
527
|
+
"""The maxRounds wall interrupts work; the narration summary it triggers
|
|
528
|
+
is an interim report, not task completion. The terminal must keep
|
|
529
|
+
budget_exhausted so the host's auto-continue resumes with a fresh budget
|
|
530
|
+
(a completed status would silently stop a half-done task)."""
|
|
531
|
+
provider = make_provider(
|
|
532
|
+
[
|
|
533
|
+
{"content": "", "tool_calls": [tc("get_data", call_id="c1")]},
|
|
534
|
+
{"content": "", "tool_calls": [tc("get_data", {"source": "a"}, "c2")]},
|
|
535
|
+
{"content": "", "tool_calls": [tc("get_data", {"source": "b"}, "c3")]},
|
|
536
|
+
# narration wrap-up round (tools withheld): interim summary
|
|
537
|
+
{"content": "阶段性总结:已取数 3 次。"},
|
|
538
|
+
]
|
|
539
|
+
)
|
|
540
|
+
hooks = AntiHallucinationHooks(provider, AntiHallucinationConfig())
|
|
541
|
+
loop = CoreLoop(
|
|
542
|
+
provider,
|
|
543
|
+
RouterToolExecutor(make_router_with_tool()),
|
|
544
|
+
LoopConfig(max_rounds=3),
|
|
545
|
+
hooks=hooks,
|
|
546
|
+
)
|
|
547
|
+
events = await collect(loop.run([LLMMessage.text_of("user", "取数")], tools=TOOLS))
|
|
548
|
+
|
|
549
|
+
final = final_completion(events)
|
|
550
|
+
assert final["status"] == "budget_exhausted"
|
|
551
|
+
assert "maxRounds" in final["reason"]
|
|
552
|
+
# The interim summary still rides the completion and the transcript.
|
|
553
|
+
assert final["textLength"] == len("阶段性总结:已取数 3 次。")
|
|
554
|
+
narrations = [
|
|
555
|
+
e for e in events
|
|
556
|
+
if e.kind == "hook_action" and e.data.get("action") == "narrate"
|
|
557
|
+
]
|
|
558
|
+
assert len(narrations) == 1
|
|
559
|
+
|
|
560
|
+
|
|
524
561
|
@pytest.mark.asyncio
|
|
525
562
|
async def test_narration_second_chance_on_empty_wrap_up() -> None:
|
|
526
563
|
"""Real-data replay finding: the wrap-up round itself can come back empty
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_gateway_catalog.py
RENAMED
|
@@ -179,6 +179,12 @@ async def test_fetch_caches_within_ttl(fake_httpx) -> None:
|
|
|
179
179
|
assert second.entries == first.entries
|
|
180
180
|
|
|
181
181
|
|
|
182
|
+
async def test_fetch_ttl_zero_bypasses_cache(fake_httpx) -> None:
|
|
183
|
+
await fetch_gateway_models("http://gw.test/v1")
|
|
184
|
+
await fetch_gateway_models("http://gw.test/v1", ttl_sec=0)
|
|
185
|
+
assert fake_httpx.calls == 2
|
|
186
|
+
|
|
187
|
+
|
|
182
188
|
async def test_fetch_serves_stale_on_refresh_failure(fake_httpx) -> None:
|
|
183
189
|
await fetch_gateway_models("http://gw.test/v1")
|
|
184
190
|
fake_httpx.payload = ConnectionError("gateway down")
|
|
@@ -410,7 +410,7 @@ async def test_token_budget_exhausted() -> None:
|
|
|
410
410
|
events = await collect(loop.run([LLMMessage.text_of("user", "hi")]))
|
|
411
411
|
decision = final_completion(events)
|
|
412
412
|
assert decision["status"] == "budget_exhausted"
|
|
413
|
-
assert any(e.kind == "budget_exhausted" and e.data["
|
|
413
|
+
assert any(e.kind == "budget_exhausted" and e.data["budget"] == "tokens" for e in events)
|
|
414
414
|
|
|
415
415
|
|
|
416
416
|
@pytest.mark.asyncio
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_model_resolve.py
RENAMED
|
@@ -84,3 +84,44 @@ def test_provider_endpoint_none_for_first_party_default() -> None:
|
|
|
84
84
|
|
|
85
85
|
def test_provider_endpoint_unknown_provider() -> None:
|
|
86
86
|
assert provider_endpoint("no-such-provider") is None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_describe_catalog_providers_covers_featured_vendors() -> None:
|
|
90
|
+
from steerable_agent_runtime.model_resolve import describe_catalog_providers
|
|
91
|
+
|
|
92
|
+
described = describe_catalog_providers()
|
|
93
|
+
by_id = {row["id"]: row for row in described}
|
|
94
|
+
assert "deepseek" in by_id
|
|
95
|
+
assert by_id["deepseek"]["apiBaseUrl"] == "https://api.deepseek.com"
|
|
96
|
+
assert by_id["deepseek"]["wireKind"] == "openai_compat"
|
|
97
|
+
assert "deepseek-v4-flash" in by_id["deepseek"]["models"]
|
|
98
|
+
|
|
99
|
+
assert by_id["anthropic"]["wireKind"] == "anthropic"
|
|
100
|
+
assert by_id["anthropic"]["apiBaseUrl"] == "https://api.anthropic.com"
|
|
101
|
+
assert any(m.startswith("claude-") for m in by_id["anthropic"]["models"])
|
|
102
|
+
|
|
103
|
+
assert by_id["google"]["wireKind"] == "google"
|
|
104
|
+
assert by_id["google"]["apiBaseUrl"] == "https://generativelanguage.googleapis.com"
|
|
105
|
+
assert "gemini-embedding-001" not in by_id["google"]["models"]
|
|
106
|
+
|
|
107
|
+
assert by_id["xai"]["wireKind"] == "openai-responses"
|
|
108
|
+
assert by_id["minimax"]["wireKind"] == "anthropic"
|
|
109
|
+
assert by_id["minimax"]["apiBaseUrl"] and "/anthropic" in by_id["minimax"]["apiBaseUrl"]
|
|
110
|
+
|
|
111
|
+
# First-party openai has no catalog api field; the describe row still
|
|
112
|
+
# fills the SDK default so a host picker can auto-set the URL.
|
|
113
|
+
assert by_id["openai"]["apiBaseUrl"] == "https://api.openai.com/v1"
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_describe_catalog_providers_skips_providers_without_chat_models() -> None:
|
|
117
|
+
from steerable_agent_runtime.model_resolve import describe_catalog_providers
|
|
118
|
+
|
|
119
|
+
described = describe_catalog_providers()
|
|
120
|
+
for row in described:
|
|
121
|
+
assert row["models"], row["id"]
|
|
122
|
+
assert row["wireKind"] in {
|
|
123
|
+
"openai_compat",
|
|
124
|
+
"anthropic",
|
|
125
|
+
"google",
|
|
126
|
+
"openai-responses",
|
|
127
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/mcp.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/otel.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/pool.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/src/steerable_agent_runtime/todo.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_approval_policy.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_cache_control.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_cache_instrumentation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_content_parts.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_error_taxonomy.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_fragment_bounds.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_history_persistence.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_in_memory_storage.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_llm_gemini_wire.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_llm_responses_wire.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_llm_stream_e2e.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_llm_wire_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_loop_cancellation.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_loop_sandbox_event.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_model_catalog.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_model_equivalence.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_observation_aging.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_orchestration.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_parallel_tools.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_provider_compat.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_provider_presets.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_replay_crosslang.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_sqlite_storage.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_storage_contract.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_tool_exposure.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_trace_recorder.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_transport_jsonrpc.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_transport_sse.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.8 → steerable_agent_runtime-0.6.10}/tests/test_usage_attribution.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|