Echo DeepSeek thinking reasoning_content on the next turn.

HubAudio U11/U12/U38 400'd after a thinking-only review turn because
the retry dropped reasoning_content. Native client now appends that
field and continues; hierarchical sheet ingest stays in 2.60.10.
This commit is contained in:
2026-09-21 09:11:37 +02:00
parent 348f390cce
commit 0d817c876f
3 changed files with 102 additions and 17 deletions
+42 -7
View File
@@ -182,31 +182,36 @@ def test_forced_tool_choice_disables_thinking():
assert captured["tool_choice"]["function"]["name"] == "save_resolved_specs"
def test_thinking_only_retries_with_thinking_disabled():
def test_thinking_only_echoes_reasoning_content_on_follow_up():
"""Live U11/U12/U38: thinking-only then retry without reasoning_content → 400."""
calls: list[dict] = []
class FakeCompletions:
async def create(self, **kwargs):
calls.append({
"thinking": kwargs["extra_body"]["thinking"]["type"],
"messages": kwargs["messages"],
})
usage = SimpleNamespace(
prompt_tokens=10, completion_tokens=5,
prompt_cache_hit_tokens=0, prompt_tokens_details=None,
)
if kwargs["extra_body"]["thinking"]["type"] == "enabled":
if kwargs["extra_body"]["thinking"]["type"] == "enabled" and len(calls) == 1:
msg = SimpleNamespace(
content=None,
reasoning_content="pondering the schematic",
tool_calls=None,
model_extra=None,
)
return SimpleNamespace(
choices=[SimpleNamespace(message=msg, finish_reason="stop")],
usage=usage,
)
fn = SimpleNamespace(name="get_pintable", arguments='{"ref":"U19"}')
fn = SimpleNamespace(name="get_pintable", arguments='{"ref":"U11"}')
tc = SimpleNamespace(id="c1", function=fn)
msg = SimpleNamespace(content=None, reasoning_content=None, tool_calls=[tc])
msg = SimpleNamespace(
content=None, reasoning_content=None, tool_calls=[tc], model_extra=None,
)
return SimpleNamespace(
choices=[SimpleNamespace(message=msg, finish_reason="tool_calls")],
usage=usage,
@@ -222,14 +227,14 @@ def test_thinking_only_retries_with_thinking_disabled():
session = DeepSeekSession(
client=FakeClient(),
model="deepseek-v4-pro",
model="deepseek-flash",
system="sys",
max_tokens=256,
thinking=True,
reasoning_effort="medium",
)
completion = asyncio.run(session.complete(
messages=[Message("user", [TextBlock("review U19")])],
messages=[Message("user", [TextBlock("review U11")])],
tools=[ToolSchema(
name="get_pintable", description="x",
input_schema={"type": "object"},
@@ -238,10 +243,40 @@ def test_thinking_only_retries_with_thinking_disabled():
))
assert len(calls) == 2
assert calls[0]["thinking"] == "enabled"
assert calls[1]["thinking"] == "disabled"
assert calls[1]["thinking"] == "enabled"
asst = [m for m in calls[1]["messages"] if m.get("role") == "assistant"]
assert asst
assert asst[0]["reasoning_content"] == "pondering the schematic"
assert asst[0]["content"] == ""
assert completion.tool_calls[0].name == "get_pintable"
def test_completion_from_openai_reads_model_extra_reasoning():
fn = SimpleNamespace(name="get_pintable", arguments='{"ref":"U11"}')
tc = SimpleNamespace(id="c1", function=fn)
msg = SimpleNamespace(
content=None,
reasoning_content=None,
tool_calls=[tc],
model_extra={"reasoning_content": "need pin table"},
)
usage = SimpleNamespace(
prompt_tokens=10, completion_tokens=5,
prompt_cache_hit_tokens=0, prompt_tokens_details=None,
)
resp = SimpleNamespace(
choices=[SimpleNamespace(message=msg, finish_reason="tool_calls")],
usage=usage,
)
completion = completion_from_openai(resp)
assert completion.raw_assistant_blocks[0].reasoning_content == "need pin table"
out = messages_to_openai(
[Message("assistant", list(completion.raw_assistant_blocks))],
vision=False,
)
assert out[0]["reasoning_content"] == "need pin table"
def test_tool_schema_and_choice():
schema = ToolSchema(
name="save_pintable",