steerable-agent-runtime 0.6.0__tar.gz → 0.6.2__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.0 → steerable_agent_runtime-0.6.2}/PKG-INFO +1 -1
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/pyproject.toml +1 -1
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/llm/compat.py +38 -3
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/llm/openai_compat.py +19 -9
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime.egg-info/PKG-INFO +1 -1
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_llm_wire_helpers.py +28 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_provider_compat.py +39 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/README.md +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/setup.cfg +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/__init__.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/antihallucination.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/approval.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/approval_policy.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/ask_user.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/branch.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/cache_control.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/calibration.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/compaction.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/config.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/default.harness.json +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/default.harness.yaml +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/errors.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/handoff.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/harness.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/harness_spec.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/history.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/hooks.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/llm/__init__.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/llm/anthropic_native.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/llm/errors.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/llm/parts.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/llm/system_proxy.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/loop.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/maintenance.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/mcp.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/mcp_server.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/model_catalog.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/model_info.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/model_resolve.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/observation_aging.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/orchestration.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/otel.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/plugins.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/pricing.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/pseudo.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/recording.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/reminders.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/replay.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/resume.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/retry.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/sandboxed.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/skills.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/spill.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/storage/__init__.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/storage/in_memory.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/storage/sqlalchemy_store.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/storage/sqlite_store.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/storage/write_lease.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/subagent.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/tokens.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/tool_schema.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/tool_search.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/tools.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/tracing.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/transport/__init__.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/transport/fastapi_sse.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/transport/stdio_jsonrpc.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/world_state.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime.egg-info/SOURCES.txt +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime.egg-info/dependency_links.txt +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime.egg-info/requires.txt +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime.egg-info/top_level.txt +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_antihallucination.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_approval.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_approval_policy.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_ask_user.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_branch.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_cache_control.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_cache_instrumentation.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_calibration.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_compaction.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_config.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_content_parts.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_error_taxonomy.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_fragment_bounds.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_golden.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_handoff.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_harness.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_harness_spec.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_history.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_history_persistence.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_hooks.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_in_memory_storage.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_long_session.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_loop.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_loop_cancellation.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_loop_replay.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_loop_sandbox_event.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_maintenance.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_mcp.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_mcp_server.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_model_catalog.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_model_equivalence.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_model_info.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_model_resolve.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_observation_aging.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_orchestration.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_otel.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_parallel_tools.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_plugins.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_pseudo.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_recording.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_reminders.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_replay_crosslang.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_resume.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_retry_hooks.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_safety_gate.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_sandboxed.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_skills.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_soft_timeout.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_spill.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_sqlite_storage.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_steer.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_storage_contract.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_stream_strip.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_subagent.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_system_proxy.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_tokens.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_tool_exposure.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_tool_hygiene.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_tool_router.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_tool_schema.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_tool_timeout.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_trace_recorder.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_transport_jsonrpc.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_transport_sse.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_usage_attribution.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_world_state.py +0 -0
- {steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_write_lease.py +0 -0
|
@@ -57,6 +57,18 @@ _FLAG_WIRE_SPEC: tuple[tuple[str, str, str, str], ...] = (
|
|
|
57
57
|
"string-list",
|
|
58
58
|
"Delta keys read as reasoning text, in preference order",
|
|
59
59
|
),
|
|
60
|
+
(
|
|
61
|
+
"reasoningEchoField",
|
|
62
|
+
"reasoning_echo_field",
|
|
63
|
+
"string",
|
|
64
|
+
"Message key for echoing plaintext reasoning back to the vendor",
|
|
65
|
+
),
|
|
66
|
+
(
|
|
67
|
+
"supportsForcedToolChoice",
|
|
68
|
+
"supports_forced_tool_choice",
|
|
69
|
+
"bool",
|
|
70
|
+
"Accept tool_choice=required; disable to downgrade it to auto",
|
|
71
|
+
),
|
|
60
72
|
(
|
|
61
73
|
"cachedTokensFields",
|
|
62
74
|
"cached_tokens_fields",
|
|
@@ -109,11 +121,23 @@ class OpenAICompatFlags:
|
|
|
109
121
|
#: Whether ``temperature`` may be sent. Reasoning-only model families
|
|
110
122
|
#: (o-series class) reject it.
|
|
111
123
|
supports_temperature: bool = True
|
|
124
|
+
#: Whether ``tool_choice: "required"`` is accepted. Thinking-mode
|
|
125
|
+
#: gateways (DeepSeek live-verified 2026-09-08: "Thinking mode does not
|
|
126
|
+
#: support this tool_choice") 400 the forced value while plain ``auto``
|
|
127
|
+
#: works — flagging off downgrades ``required`` → ``auto`` on the wire.
|
|
128
|
+
supports_forced_tool_choice: bool = True
|
|
112
129
|
|
|
113
130
|
# ── response shape ───────────────────────────────────────────────
|
|
114
131
|
#: Delta keys read as reasoning text, in preference order. DeepSeek uses
|
|
115
132
|
#: ``reasoning_content``; OpenRouter's GLM path uses ``reasoning``.
|
|
116
133
|
reasoning_delta_fields: tuple[str, ...] = ("reasoning_content", "reasoning")
|
|
134
|
+
#: Message key for echoing plaintext reasoning back on the next request.
|
|
135
|
+
#: OpenRouter normalizes to ``reasoning`` (the reference default);
|
|
136
|
+
#: DeepSeek thinking mode *requires* the round-trip under its own
|
|
137
|
+
#: ``reasoning_content`` key — echoing as ``reasoning`` is an HTTP 400
|
|
138
|
+
#: ("The `reasoning_content` in the thinking mode must be passed back to
|
|
139
|
+
#: the API.", live-verified 2026-09-08 with deepseek-v4-flash).
|
|
140
|
+
reasoning_echo_field: str = "reasoning"
|
|
117
141
|
#: Usage locations read for cached prompt tokens, in preference order.
|
|
118
142
|
#: Dotted paths resolve nested objects. OpenAI nests under
|
|
119
143
|
#: ``prompt_tokens_details.cached_tokens``; DeepSeek reports top-level
|
|
@@ -142,6 +166,8 @@ class OpenAICompatFlags:
|
|
|
142
166
|
value = data[wire]
|
|
143
167
|
if field in ("reasoning_delta_fields", "cached_tokens_fields"):
|
|
144
168
|
kwargs[field] = tuple(str(v) for v in value)
|
|
169
|
+
elif field == "reasoning_echo_field":
|
|
170
|
+
kwargs[field] = str(value)
|
|
145
171
|
elif field == "max_tokens_field":
|
|
146
172
|
if value not in _MAX_TOKENS_FIELDS:
|
|
147
173
|
raise ValueError(
|
|
@@ -162,13 +188,22 @@ class OpenAICompatFlags:
|
|
|
162
188
|
#: as data — adding a vendor must not touch request-building or parsing.
|
|
163
189
|
PROVIDER_COMPAT_HOSTS: list[tuple[str, OpenAICompatFlags]] = [
|
|
164
190
|
# DeepSeek: reasoning arrives as ``reasoning_content`` and cache hits as
|
|
165
|
-
# top-level ``prompt_cache_hit_tokens``.
|
|
166
|
-
#
|
|
167
|
-
#
|
|
191
|
+
# top-level ``prompt_cache_hit_tokens``. Thinking mode additionally
|
|
192
|
+
# REQUIRES the reasoning round-trip under ``reasoning_content`` — the
|
|
193
|
+
# reference echo key ``reasoning`` is ignored and the follow-up request
|
|
194
|
+
# 400s (live-verified 2026-09-08, deepseek-v4-flash tool-turn: "The
|
|
195
|
+
# `reasoning_content` in the thinking mode must be passed back to the
|
|
196
|
+
# API."). The tolerant delta-field defaults already cover parsing; the
|
|
197
|
+
# echo key is the divergence pinned here.
|
|
168
198
|
(
|
|
169
199
|
"api.deepseek.com",
|
|
170
200
|
OpenAICompatFlags(
|
|
171
201
|
reasoning_delta_fields=("reasoning_content", "reasoning"),
|
|
202
|
+
reasoning_echo_field="reasoning_content",
|
|
203
|
+
# Thinking mode 400s ``tool_choice="required"`` ("Thinking mode
|
|
204
|
+
# does not support this tool_choice", live-verified 2026-09-08
|
|
205
|
+
# with deepseek-v4-flash); ``auto`` and omission both work.
|
|
206
|
+
supports_forced_tool_choice=False,
|
|
172
207
|
cached_tokens_fields=(
|
|
173
208
|
"prompt_cache_hit_tokens",
|
|
174
209
|
"prompt_tokens_details.cached_tokens",
|
|
@@ -312,7 +312,7 @@ class OpenAICompatProvider:
|
|
|
312
312
|
compat = self.compat or OpenAICompatFlags()
|
|
313
313
|
body: dict[str, Any] = {
|
|
314
314
|
"model": self.model,
|
|
315
|
-
"messages": [_encode_message(m) for m in messages],
|
|
315
|
+
"messages": [_encode_message(m, compat=compat) for m in messages],
|
|
316
316
|
"stream": stream,
|
|
317
317
|
}
|
|
318
318
|
if stream and compat.supports_usage_in_streaming:
|
|
@@ -331,11 +331,16 @@ class OpenAICompatProvider:
|
|
|
331
331
|
if tools_list:
|
|
332
332
|
body["tools"] = tools_list
|
|
333
333
|
body.update(extra)
|
|
334
|
-
#
|
|
335
|
-
#
|
|
336
|
-
#
|
|
337
|
-
|
|
338
|
-
|
|
334
|
+
# ``tool_choice=required`` downgrade paths:
|
|
335
|
+
# - compat flag: vendors whose thinking mode 400s the forced value
|
|
336
|
+
# (DeepSeek: "Thinking mode does not support this tool_choice");
|
|
337
|
+
# - Z.AI (direct or OpenRouter pin) 400s it outright — model-name
|
|
338
|
+
# based, which host flags can't cover. Harbor still logs the hook;
|
|
339
|
+
# the wire must send auto or the trial dies on round 0
|
|
340
|
+
# (failed-prev 33335200327).
|
|
341
|
+
if body.get("tool_choice") == "required" and (
|
|
342
|
+
not compat.supports_forced_tool_choice
|
|
343
|
+
or _z_ai_tool_choice_auto_only(self.model, self.base_url)
|
|
339
344
|
):
|
|
340
345
|
body["tool_choice"] = "auto"
|
|
341
346
|
# W6-8: clamp the env-requested reasoning effort to a level the model
|
|
@@ -476,7 +481,10 @@ def _encode_content(message: LLMMessage) -> str | list[dict[str, Any]]:
|
|
|
476
481
|
return out
|
|
477
482
|
|
|
478
483
|
|
|
479
|
-
def _encode_message(
|
|
484
|
+
def _encode_message(
|
|
485
|
+
message: LLMMessage, *, compat: OpenAICompatFlags | None = None
|
|
486
|
+
) -> dict[str, Any]:
|
|
487
|
+
flags = compat or OpenAICompatFlags()
|
|
480
488
|
out: dict[str, Any] = {"role": message.role, "content": _encode_content(message)}
|
|
481
489
|
if message.name is not None:
|
|
482
490
|
out["name"] = message.name
|
|
@@ -495,11 +503,13 @@ def _encode_message(message: LLMMessage) -> dict[str, Any]:
|
|
|
495
503
|
for tc in message.tool_calls
|
|
496
504
|
]
|
|
497
505
|
# OpenRouter: echo reasoning_details unmodified so GLM continues after
|
|
498
|
-
# tools. Prefer the structured block; plaintext is the fallback.
|
|
506
|
+
# tools. Prefer the structured block; plaintext is the fallback. The
|
|
507
|
+
# plaintext echo key is compat data: DeepSeek thinking mode 400s unless
|
|
508
|
+
# the round-trip uses its own ``reasoning_content`` field.
|
|
499
509
|
if message.reasoning_details:
|
|
500
510
|
out["reasoning_details"] = message.reasoning_details
|
|
501
511
|
elif message.reasoning:
|
|
502
|
-
out[
|
|
512
|
+
out[flags.reasoning_echo_field] = message.reasoning
|
|
503
513
|
return out
|
|
504
514
|
|
|
505
515
|
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_llm_wire_helpers.py
RENAMED
|
@@ -154,6 +154,34 @@ def test_openai_parse_and_encode_reasoning_details() -> None:
|
|
|
154
154
|
assert "reasoning_details" not in plaintext
|
|
155
155
|
|
|
156
156
|
|
|
157
|
+
def test_openai_encode_reasoning_echo_field_from_compat() -> None:
|
|
158
|
+
"""DeepSeek thinking mode 400s unless reasoning round-trips under its own
|
|
159
|
+
``reasoning_content`` key (live-verified 2026-09-08, deepseek-v4-flash);
|
|
160
|
+
the echo field name is compat data, not a hardcoded constant."""
|
|
161
|
+
from steerable_agent_runtime.llm import OpenAICompatFlags, compat_for_base_url
|
|
162
|
+
|
|
163
|
+
msg = LLMMessage.text_of("assistant", "", reasoning="think step")
|
|
164
|
+
|
|
165
|
+
deepseek = compat_for_base_url("https://api.deepseek.com")
|
|
166
|
+
assert deepseek is not None
|
|
167
|
+
encoded = _encode_message(msg, compat=deepseek)
|
|
168
|
+
assert encoded["reasoning_content"] == "think step"
|
|
169
|
+
assert "reasoning" not in encoded
|
|
170
|
+
|
|
171
|
+
# Reference default (and OpenRouter) keep the ``reasoning`` echo key.
|
|
172
|
+
default = _encode_message(msg)
|
|
173
|
+
assert default["reasoning"] == "think step"
|
|
174
|
+
assert "reasoning_content" not in default
|
|
175
|
+
|
|
176
|
+
openrouter = compat_for_base_url("https://openrouter.ai/api/v1")
|
|
177
|
+
assert openrouter is not None
|
|
178
|
+
assert _encode_message(msg, compat=openrouter)["reasoning"] == "think step"
|
|
179
|
+
|
|
180
|
+
# Host-passed camelCase payload builds the same flags.
|
|
181
|
+
from_dict = OpenAICompatFlags.from_dict({"reasoningEchoField": "reasoning_content"})
|
|
182
|
+
assert _encode_message(msg, compat=from_dict)["reasoning_content"] == "think step"
|
|
183
|
+
|
|
184
|
+
|
|
157
185
|
def test_openai_parse_stream_chunk_usage_only() -> None:
|
|
158
186
|
chunk = {
|
|
159
187
|
"choices": [],
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_provider_compat.py
RENAMED
|
@@ -96,6 +96,45 @@ def test_temperature_unsupported_suppresses_field() -> None:
|
|
|
96
96
|
assert "temperature" not in body
|
|
97
97
|
|
|
98
98
|
|
|
99
|
+
def test_forced_tool_choice_downgraded_when_flagged_off() -> None:
|
|
100
|
+
"""DeepSeek thinking mode 400s ``tool_choice="required"`` ("Thinking mode
|
|
101
|
+
does not support this tool_choice", live-verified 2026-09-08 with
|
|
102
|
+
deepseek-v4-flash); the flag downgrades it to ``auto`` on the wire."""
|
|
103
|
+
from steerable_agent_runtime.llm import LLMMessage
|
|
104
|
+
|
|
105
|
+
provider = _provider(compat=OpenAICompatFlags(supports_forced_tool_choice=False))
|
|
106
|
+
body = provider._build_body(
|
|
107
|
+
messages=[LLMMessage.text_of("user", "hi")],
|
|
108
|
+
tools=None,
|
|
109
|
+
temperature=None,
|
|
110
|
+
max_tokens=None,
|
|
111
|
+
stream=True,
|
|
112
|
+
extra={"tool_choice": "required"},
|
|
113
|
+
)
|
|
114
|
+
assert body["tool_choice"] == "auto"
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_forced_tool_choice_kept_by_default() -> None:
|
|
118
|
+
from steerable_agent_runtime.llm import LLMMessage
|
|
119
|
+
|
|
120
|
+
provider = _provider()
|
|
121
|
+
body = provider._build_body(
|
|
122
|
+
messages=[LLMMessage.text_of("user", "hi")],
|
|
123
|
+
tools=None,
|
|
124
|
+
temperature=None,
|
|
125
|
+
max_tokens=None,
|
|
126
|
+
stream=True,
|
|
127
|
+
extra={"tool_choice": "required"},
|
|
128
|
+
)
|
|
129
|
+
assert body["tool_choice"] == "required"
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_registry_deepseek_disables_forced_tool_choice() -> None:
|
|
133
|
+
entry = compat_for_base_url("https://api.deepseek.com")
|
|
134
|
+
assert entry is not None
|
|
135
|
+
assert entry.supports_forced_tool_choice is False
|
|
136
|
+
|
|
137
|
+
|
|
99
138
|
# ---------------------------------------------------------------------------
|
|
100
139
|
# Response-shape flags
|
|
101
140
|
# ---------------------------------------------------------------------------
|
|
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.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/hooks.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.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/loop.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/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
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/otel.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.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/retry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/spill.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
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/src/steerable_agent_runtime/tools.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
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_antihallucination.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_approval_policy.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_cache_instrumentation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_error_taxonomy.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/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.0 → steerable_agent_runtime-0.6.2}/tests/test_history_persistence.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_in_memory_storage.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_loop_cancellation.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_loop_sandbox_event.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_model_equivalence.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_observation_aging.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_parallel_tools.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/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.0 → steerable_agent_runtime-0.6.2}/tests/test_sqlite_storage.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_trace_recorder.py
RENAMED
|
File without changes
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_transport_jsonrpc.py
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime-0.6.0 → steerable_agent_runtime-0.6.2}/tests/test_usage_attribution.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|