steerable-agent-runtime 0.6.73__tar.gz → 0.6.74__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.73 → steerable_agent_runtime-0.6.74}/PKG-INFO +1 -1
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/pyproject.toml +1 -1
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/native_bridge.py +3 -1
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/pseudo.py +97 -5
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime.egg-info/PKG-INFO +1 -1
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_stream_strip.py +65 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/README.md +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/setup.cfg +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/__init__.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/antihallucination.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/approval.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/approval_policy.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/ask_user.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/branch.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/cache_control.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/calibration.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/compaction.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/compaction_policy.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/config.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/default.harness.json +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/default.harness.yaml +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/delegation_gate.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/errors.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/gateway_catalog.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/handoff.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/harness.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/harness_spec.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/history.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/hooks.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/llm/__init__.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/llm/anthropic_native.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/llm/compat.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/llm/errors.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/llm/google_genai.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/llm/openai_compat.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/llm/openai_responses.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/llm/parts.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/llm/presets.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/llm/system_proxy.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/loop.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/maintenance.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/mcp.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/mcp_server.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/model_catalog.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/model_info.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/model_resolve.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/observation_aging.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/orchestration.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/otel.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/plugins.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/pool.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/pricing.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/recording.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/reminders.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/replay.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/resume.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/retry.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/sandboxed.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/skills.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/spill.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/storage/__init__.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/storage/in_memory.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/storage/sqlalchemy_store.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/storage/sqlite_store.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/storage/write_lease.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/subagent.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/todo.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/tokens.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/tool_presentation.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/tool_schema.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/tool_search.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/tools.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/tracing.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/transport/__init__.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/transport/fastapi_sse.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/transport/stdio_jsonrpc.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/world_state.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime.egg-info/SOURCES.txt +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime.egg-info/dependency_links.txt +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime.egg-info/requires.txt +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime.egg-info/top_level.txt +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_antihallucination.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_approval.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_approval_policy.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_ask_user.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_branch.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_cache_control.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_cache_instrumentation.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_calibration.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_compaction.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_compaction_policy.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_compaction_strategy.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_config.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_content_parts.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_delegation_gate.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_error_taxonomy.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_fragment_bounds.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_gateway_catalog.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_golden.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_handoff.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_harness.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_harness_spec.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_history.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_history_persistence.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_hooks.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_in_memory_storage.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_llm_gemini_wire.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_llm_responses_wire.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_llm_stream_e2e.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_llm_wire_helpers.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_long_session.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_loop.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_loop_cancellation.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_loop_replay.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_loop_sandbox_event.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_loop_terminal.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_maintenance.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_mcp.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_mcp_server.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_model_catalog.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_model_equivalence.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_model_info.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_model_resolve.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_native_default.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_observation_aging.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_orchestration.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_otel.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_parallel_tools.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_plugins.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_provider_compat.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_provider_presets.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_pseudo.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_pyo3_api_surface.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_recording.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_reminders.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_replay_crosslang.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_resume.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_retry_hooks.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_safety_gate.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_sandboxed.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_skills.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_soft_timeout.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_spill.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_sqlite_storage.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_steer.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_storage_contract.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_subagent.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_system_proxy.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_todo.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_todo_gate.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_tokens.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_tool_exposure.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_tool_hygiene.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_tool_presentation.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_tool_router.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_tool_schema.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_tool_timeout.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_trace_recorder.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_transport_jsonrpc.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_transport_sse.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_usage_attribution.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_world_state.py +0 -0
- {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_write_lease.py +0 -0
|
@@ -33,6 +33,7 @@ from steerable_agent_runtime.loop import (
|
|
|
33
33
|
from steerable_agent_runtime.pseudo import (
|
|
34
34
|
PseudoStreamStripper,
|
|
35
35
|
extract_inline_tool_calls,
|
|
36
|
+
reply_text_from_call,
|
|
36
37
|
split_trailing_high_surrogate,
|
|
37
38
|
)
|
|
38
39
|
from steerable_agent_runtime.replay import build_step_decision_event
|
|
@@ -316,7 +317,8 @@ async def _collect_stream(
|
|
|
316
317
|
send("chunk", chunk={"finish_reason": cut_finish_reason})
|
|
317
318
|
elif not tool_seen:
|
|
318
319
|
recovered, _ = extract_inline_tool_calls("".join(raw_content))
|
|
319
|
-
|
|
320
|
+
actionable = [call for call in recovered if reply_text_from_call(call) is None]
|
|
321
|
+
for index, call in enumerate(actionable):
|
|
320
322
|
send(
|
|
321
323
|
"chunk",
|
|
322
324
|
chunk={
|
|
@@ -8,7 +8,8 @@ regress into prose) emit tool *intent* as text instead of a structured
|
|
|
8
8
|
- DeepSeek XML: ``<function=tool><parameter=k>v</parameter></function>``
|
|
9
9
|
- Markdown pseudo: ``[Tool call: NAME]\\n{"k": "v"}``
|
|
10
10
|
- JSON tool object: a fenced or ``<tool_call>`` ``{"name", "arguments"}``
|
|
11
|
-
object (Qwen / Ollama), or a message that is only that object
|
|
11
|
+
object (Qwen / Ollama), or a message that is only that object.
|
|
12
|
+
``{"name": "text", "arguments": {"value": "..."}}`` is a reply, not a tool.
|
|
12
13
|
|
|
13
14
|
If the loop treated that text as a final answer it would end the turn having
|
|
14
15
|
never run the tool — the model "meant" to act but the loop stopped. This module
|
|
@@ -202,6 +203,31 @@ def _coerce_arguments(raw: Any) -> dict[str, Any] | None:
|
|
|
202
203
|
return None
|
|
203
204
|
|
|
204
205
|
|
|
206
|
+
_REPLY_TOOL_NAMES = frozenset({"text"})
|
|
207
|
+
_REPLY_ARG_KEYS = ("value", "content", "text", "message")
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def reply_text_from_call(call: dict[str, Any]) -> str | None:
|
|
211
|
+
"""Return the visible reply when a model wrapped plain text as a tool.
|
|
212
|
+
|
|
213
|
+
Some local models emit ``{"name": "text", "arguments": {"value": "..."}}``
|
|
214
|
+
instead of a message. ``None`` means the call is a real tool.
|
|
215
|
+
|
|
216
|
+
@param call: one ``{"name", "arguments"}`` object from inline recovery.
|
|
217
|
+
"""
|
|
218
|
+
name = call.get("name")
|
|
219
|
+
if not isinstance(name, str) or name.strip().lower() not in _REPLY_TOOL_NAMES:
|
|
220
|
+
return None
|
|
221
|
+
arguments = call.get("arguments") or {}
|
|
222
|
+
if not isinstance(arguments, dict):
|
|
223
|
+
return ""
|
|
224
|
+
for key in _REPLY_ARG_KEYS:
|
|
225
|
+
value = arguments.get(key)
|
|
226
|
+
if isinstance(value, str):
|
|
227
|
+
return value
|
|
228
|
+
return ""
|
|
229
|
+
|
|
230
|
+
|
|
205
231
|
def _calls_from_payload(payload: Any) -> list[dict[str, Any]] | None:
|
|
206
232
|
"""Accept one tool-call object or a list of them. Anything else is text."""
|
|
207
233
|
items = payload if isinstance(payload, list) else [payload]
|
|
@@ -600,8 +626,72 @@ class _JsonFenceFilter:
|
|
|
600
626
|
return tail
|
|
601
627
|
|
|
602
628
|
|
|
629
|
+
class _BareToolJsonFilter:
|
|
630
|
+
"""Hold a message that is only a tool-call JSON object.
|
|
631
|
+
|
|
632
|
+
A real tool object is dropped so recovery can execute it. A ``text``
|
|
633
|
+
reply object is replaced by its value. Any other leading ``{`` or ``[``
|
|
634
|
+
is released once it is clearly not that object.
|
|
635
|
+
"""
|
|
636
|
+
|
|
637
|
+
def __init__(self) -> None:
|
|
638
|
+
self._buf = ""
|
|
639
|
+
self._passthrough = False
|
|
640
|
+
|
|
641
|
+
def feed(self, chunk: str) -> str:
|
|
642
|
+
if self._passthrough:
|
|
643
|
+
return chunk or ""
|
|
644
|
+
self._buf += chunk or ""
|
|
645
|
+
return self._decide(final=False)
|
|
646
|
+
|
|
647
|
+
def flush(self) -> str:
|
|
648
|
+
if self._passthrough:
|
|
649
|
+
return ""
|
|
650
|
+
return self._decide(final=True)
|
|
651
|
+
|
|
652
|
+
def _release(self, text: str) -> str:
|
|
653
|
+
self._passthrough = True
|
|
654
|
+
self._buf = ""
|
|
655
|
+
return text
|
|
656
|
+
|
|
657
|
+
def _decide(self, final: bool) -> str:
|
|
658
|
+
text = self._buf
|
|
659
|
+
stripped = text.lstrip()
|
|
660
|
+
if not stripped:
|
|
661
|
+
if final:
|
|
662
|
+
return self._release(text)
|
|
663
|
+
return ""
|
|
664
|
+
if stripped[0] not in "{[":
|
|
665
|
+
return self._release(text)
|
|
666
|
+
if len(text) > _FENCE_HOLD_CAP and not final:
|
|
667
|
+
return self._release(text)
|
|
668
|
+
try:
|
|
669
|
+
payload, end = json.JSONDecoder().raw_decode(stripped)
|
|
670
|
+
except json.JSONDecodeError as exc:
|
|
671
|
+
# An error at the end, or an open string, can still become JSON.
|
|
672
|
+
# `{not` and `[Tool call:` cannot, so they must stream immediately.
|
|
673
|
+
incomplete = exc.pos >= len(stripped) or "Unterminated" in exc.msg
|
|
674
|
+
if incomplete and not final and len(text) <= _FENCE_HOLD_CAP:
|
|
675
|
+
return ""
|
|
676
|
+
return self._release(text)
|
|
677
|
+
rest = stripped[end:]
|
|
678
|
+
if rest.strip():
|
|
679
|
+
return self._release(text)
|
|
680
|
+
calls = _calls_from_payload(payload)
|
|
681
|
+
if not calls:
|
|
682
|
+
return self._release(text)
|
|
683
|
+
if not final:
|
|
684
|
+
return ""
|
|
685
|
+
self._passthrough = True
|
|
686
|
+
self._buf = ""
|
|
687
|
+
replies = [reply_text_from_call(call) for call in calls]
|
|
688
|
+
if all(reply is not None for reply in replies):
|
|
689
|
+
return "".join(replies)
|
|
690
|
+
return ""
|
|
691
|
+
|
|
692
|
+
|
|
603
693
|
class PseudoStreamStripper:
|
|
604
|
-
"""Display-path filter chaining the fn-echo, JSON
|
|
694
|
+
"""Display-path filter chaining the fn-echo, JSON, and markdown filters.
|
|
605
695
|
|
|
606
696
|
Usage: ``feed`` each raw content delta, emit what comes back; at stream
|
|
607
697
|
end, ``flush`` once and emit the tail. Text held back to detect markers
|
|
@@ -611,11 +701,13 @@ class PseudoStreamStripper:
|
|
|
611
701
|
def __init__(self) -> None:
|
|
612
702
|
self._fn = _FnEchoFilter()
|
|
613
703
|
self._fence = _JsonFenceFilter()
|
|
704
|
+
self._bare = _BareToolJsonFilter()
|
|
614
705
|
self._md = _MdCallFilter()
|
|
615
706
|
|
|
616
707
|
def feed(self, chunk: str) -> str:
|
|
617
|
-
return self._md.feed(self._fence.feed(self._fn.feed(chunk)))
|
|
708
|
+
return self._md.feed(self._bare.feed(self._fence.feed(self._fn.feed(chunk))))
|
|
618
709
|
|
|
619
710
|
def flush(self) -> str:
|
|
620
|
-
|
|
621
|
-
|
|
711
|
+
fenced = self._fence.feed(self._fn.flush()) + self._fence.flush()
|
|
712
|
+
bare = self._bare.feed(fenced) + self._bare.flush()
|
|
713
|
+
return self._md.feed(bare) + self._md.flush()
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_stream_strip.py
RENAMED
|
@@ -99,6 +99,25 @@ def test_stripper_keeps_a_fenced_config() -> None:
|
|
|
99
99
|
assert _strip([text]) == text
|
|
100
100
|
|
|
101
101
|
|
|
102
|
+
def test_stripper_shows_a_text_tool_as_the_reply() -> None:
|
|
103
|
+
# Local models wrap the answer as {"name":"text","arguments":{"value":...}}.
|
|
104
|
+
text = '{"name": "text", "arguments": {"value": "Hello, World!"}}'
|
|
105
|
+
assert _strip(['{"name": "te', 'xt", "arguments": {"value": "Hello, World!"}}']) == (
|
|
106
|
+
"Hello, World!"
|
|
107
|
+
)
|
|
108
|
+
assert _strip([text]) == "Hello, World!"
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_stripper_hides_a_bare_tool_object() -> None:
|
|
112
|
+
text = '{"name": "get_weather", "arguments": {"city": "Berlin"}}'
|
|
113
|
+
assert _strip([text[:12], text[12:]]) == ""
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_stripper_keeps_json_that_is_not_a_tool() -> None:
|
|
117
|
+
text = '{"host": "127.0.0.1"}'
|
|
118
|
+
assert _strip([text]) == text
|
|
119
|
+
|
|
120
|
+
|
|
102
121
|
def test_stripper_holds_partial_opener_then_releases() -> None:
|
|
103
122
|
# "[Tool" at a chunk end is held back; when the next chunk shows it was
|
|
104
123
|
# NOT an opener, the held text is released.
|
|
@@ -241,6 +260,52 @@ async def test_loop_display_stream_strips_json_fence_but_recovers_call() -> None
|
|
|
241
260
|
assert events[-1].data["status"] == "completed"
|
|
242
261
|
|
|
243
262
|
|
|
263
|
+
@pytest.mark.asyncio
|
|
264
|
+
async def test_loop_display_stream_shows_text_tool_as_reply() -> None:
|
|
265
|
+
# The whole message is a fake text tool. Show the value, do not call it.
|
|
266
|
+
payload = '{"name": "text", "arguments": {"value": "Hello, World!"}}'
|
|
267
|
+
provider = make_provider([{"content_pieces": [payload[:20], payload[20:]]}])
|
|
268
|
+
router = ToolRouter()
|
|
269
|
+
executed: list[str] = []
|
|
270
|
+
|
|
271
|
+
async def text(value: str) -> str:
|
|
272
|
+
executed.append(value)
|
|
273
|
+
return value
|
|
274
|
+
|
|
275
|
+
router.register(text)
|
|
276
|
+
loop = CoreLoop(provider, RouterToolExecutor(router))
|
|
277
|
+
events = await collect(loop.run([LLMMessage.text_of("user", "hello")]))
|
|
278
|
+
|
|
279
|
+
displayed = "".join(e.data["delta"] for e in events if e.kind == "content_delta")
|
|
280
|
+
assert displayed == "Hello, World!"
|
|
281
|
+
assert executed == []
|
|
282
|
+
assert events[-1].data["status"] == "completed"
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
@pytest.mark.asyncio
|
|
286
|
+
async def test_loop_display_stream_strips_bare_tool_json_but_recovers_call() -> None:
|
|
287
|
+
payload = '{"name": "get_weather", "arguments": {"city": "Berlin"}}'
|
|
288
|
+
provider = make_provider(
|
|
289
|
+
[{"content_pieces": [payload[:18], payload[18:]]}, {"content": "It is sunny."}]
|
|
290
|
+
)
|
|
291
|
+
router = ToolRouter()
|
|
292
|
+
executed: list[str] = []
|
|
293
|
+
|
|
294
|
+
async def get_weather(city: str) -> str:
|
|
295
|
+
executed.append(city)
|
|
296
|
+
return "sunny"
|
|
297
|
+
|
|
298
|
+
router.register(get_weather)
|
|
299
|
+
loop = CoreLoop(provider, RouterToolExecutor(router))
|
|
300
|
+
events = await collect(loop.run([LLMMessage.text_of("user", "weather?")]))
|
|
301
|
+
|
|
302
|
+
displayed = "".join(e.data["delta"] for e in events if e.kind == "content_delta")
|
|
303
|
+
assert "get_weather" not in displayed
|
|
304
|
+
assert "{" not in displayed.split("It is sunny.")[0]
|
|
305
|
+
assert "It is sunny." in displayed
|
|
306
|
+
assert executed == ["Berlin"]
|
|
307
|
+
|
|
308
|
+
|
|
244
309
|
@pytest.mark.asyncio
|
|
245
310
|
async def test_loop_display_stream_strips_echo_blocks() -> None:
|
|
246
311
|
pieces = ["answer: ", "<function_results>{'fake': 1}</function_results>", " 42"]
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/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
|
|
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
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_antihallucination.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_approval_policy.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_cache_control.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_compaction_policy.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_compaction_strategy.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_content_parts.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_delegation_gate.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_error_taxonomy.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_fragment_bounds.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_gateway_catalog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_harness_spec.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_history_persistence.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_in_memory_storage.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_llm_gemini_wire.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_llm_responses_wire.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_llm_stream_e2e.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_llm_wire_helpers.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_long_session.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_loop_cancellation.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_loop_sandbox_event.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_loop_terminal.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_model_catalog.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_model_equivalence.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_model_resolve.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_native_default.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_observation_aging.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_orchestration.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_parallel_tools.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_provider_compat.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_provider_presets.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_pyo3_api_surface.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_replay_crosslang.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.73 → steerable_agent_runtime-0.6.74}/tests/test_soft_timeout.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_sqlite_storage.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_storage_contract.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_system_proxy.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_tool_exposure.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_tool_hygiene.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_tool_presentation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_tool_timeout.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_trace_recorder.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_transport_jsonrpc.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_transport_sse.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_usage_attribution.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|