mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-09 16:32:21 +02:00
preserve DeepSeek reasoning across clean tool rounds
This commit is contained in:
@@ -3899,6 +3899,7 @@ def provider_wire_messages(messages):
|
|||||||
item.get('role') == 'assistant'
|
item.get('role') == 'assistant'
|
||||||
and not item.get('content')
|
and not item.get('content')
|
||||||
and not item.get('tool_calls')
|
and not item.get('tool_calls')
|
||||||
|
and not item.get('reasoning_content')
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
cleaned.append(item)
|
cleaned.append(item)
|
||||||
@@ -4483,7 +4484,7 @@ async def stream_preview(*, endpoint_url, model, messages, headers, turn_contrac
|
|||||||
'function': {'name': private_browser_name},
|
'function': {'name': private_browser_name},
|
||||||
}
|
}
|
||||||
force_private_browser_next_round = False
|
force_private_browser_next_round = False
|
||||||
pending, content = {}, ''
|
pending, content, round_reasoning = {}, '', ''
|
||||||
streamed_round_text = False
|
streamed_round_text = False
|
||||||
request = search_tool_choice_request(request)
|
request = search_tool_choice_request(request)
|
||||||
request = provider_compatible_tool_choice_request(request, model)
|
request = provider_compatible_tool_choice_request(request, model)
|
||||||
@@ -4528,6 +4529,14 @@ async def stream_preview(*, endpoint_url, model, messages, headers, turn_contrac
|
|||||||
if not choices:
|
if not choices:
|
||||||
continue
|
continue
|
||||||
delta = choices[0].get('delta') or {}
|
delta = choices[0].get('delta') or {}
|
||||||
|
reasoning = (
|
||||||
|
delta.get('reasoning_content')
|
||||||
|
or delta.get('reasoning')
|
||||||
|
or delta.get('thinking')
|
||||||
|
or ''
|
||||||
|
)
|
||||||
|
if reasoning:
|
||||||
|
round_reasoning += str(reasoning)
|
||||||
text = delta.get('content') or ''
|
text = delta.get('content') or ''
|
||||||
if text:
|
if text:
|
||||||
first_token = first_token or time.monotonic()
|
first_token = first_token or time.monotonic()
|
||||||
@@ -4610,6 +4619,12 @@ async def stream_preview(*, endpoint_url, model, messages, headers, turn_contrac
|
|||||||
yield event({'type': 'model_tool_proposal', 'round': round_number,
|
yield event({'type': 'model_tool_proposal', 'round': round_number,
|
||||||
'function': proposal.get('function', {})})
|
'function': proposal.get('function', {})})
|
||||||
message = {'role': 'assistant', 'content': content or None}
|
message = {'role': 'assistant', 'content': content or None}
|
||||||
|
if round_reasoning and 'deepseek' in str(model or '').casefold():
|
||||||
|
# DeepSeek requires each tool-round reasoning payload to
|
||||||
|
# be echoed verbatim on subsequent requests. Unlike local
|
||||||
|
# Qwen/Nemotron templates, its API owns this structured
|
||||||
|
# field and does not reinterpret it as visible output.
|
||||||
|
message['reasoning_content'] = round_reasoning
|
||||||
if proposed:
|
if proposed:
|
||||||
message['tool_calls'] = protocol_safe_tool_calls(proposed)
|
message['tool_calls'] = protocol_safe_tool_calls(proposed)
|
||||||
history.append(message)
|
history.append(message)
|
||||||
|
|||||||
@@ -531,18 +531,30 @@ def test_repeated_off_contract_artifact_calls_trigger_single_file_body_handoff()
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_provider_wire_keeps_deepseek_reasoning_only_turn_for_continuity():
|
||||||
|
import src.clean_agent_preview as module
|
||||||
|
|
||||||
|
messages = [{
|
||||||
|
'role': 'assistant',
|
||||||
|
'content': None,
|
||||||
|
'reasoning_content': 'private provider reasoning token stream',
|
||||||
|
}]
|
||||||
|
|
||||||
|
assert module.provider_wire_messages(messages) == messages
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_repeated_off_contract_calls_recover_via_required_artifact_body(monkeypatch):
|
async def test_repeated_off_contract_calls_recover_via_required_artifact_body(monkeypatch):
|
||||||
import src.clean_agent_preview as module
|
import src.clean_agent_preview as module
|
||||||
|
|
||||||
responses = iter([
|
responses = iter([
|
||||||
{'choices': [{'delta': {'tool_calls': [{
|
{'choices': [{'delta': {'reasoning_content': 'reasoning-one', 'tool_calls': [{
|
||||||
'index': 0, 'id': 'bad-bash',
|
'index': 0, 'id': 'bad-bash',
|
||||||
'function': {'name': 'bash', 'arguments': json.dumps({
|
'function': {'name': 'bash', 'arguments': json.dumps({
|
||||||
'command': 'echo nope',
|
'command': 'echo nope',
|
||||||
})},
|
})},
|
||||||
}]}}]},
|
}]}}]},
|
||||||
{'choices': [{'delta': {'tool_calls': [{
|
{'choices': [{'delta': {'reasoning_content': 'reasoning-two', 'tool_calls': [{
|
||||||
'index': 0, 'id': 'bad-python',
|
'index': 0, 'id': 'bad-python',
|
||||||
'function': {'name': 'python', 'arguments': json.dumps({
|
'function': {'name': 'python', 'arguments': json.dumps({
|
||||||
'code': 'print("nope")',
|
'code': 'print("nope")',
|
||||||
@@ -566,11 +578,14 @@ async def test_repeated_off_contract_calls_recover_via_required_artifact_body(mo
|
|||||||
yield 'data: ' + json.dumps(self.payload)
|
yield 'data: ' + json.dumps(self.payload)
|
||||||
yield 'data: [DONE]'
|
yield 'data: [DONE]'
|
||||||
|
|
||||||
|
requests = []
|
||||||
|
|
||||||
class Client:
|
class Client:
|
||||||
def __init__(self, **kwargs): pass
|
def __init__(self, **kwargs): pass
|
||||||
async def __aenter__(self): return self
|
async def __aenter__(self): return self
|
||||||
async def __aexit__(self, *args): pass
|
async def __aexit__(self, *args): pass
|
||||||
def stream(self, *args, **kwargs):
|
def stream(self, *args, **kwargs):
|
||||||
|
requests.append(kwargs['json'])
|
||||||
return Response(next(responses))
|
return Response(next(responses))
|
||||||
|
|
||||||
executed = []
|
executed = []
|
||||||
@@ -602,6 +617,11 @@ async def test_repeated_off_contract_calls_recover_via_required_artifact_body(mo
|
|||||||
)]
|
)]
|
||||||
|
|
||||||
assert [block.tool_type for block in executed] == ['write_file']
|
assert [block.tool_type for block in executed] == ['write_file']
|
||||||
|
prior_tool_turn = next(
|
||||||
|
message for message in requests[1]['messages']
|
||||||
|
if message.get('role') == 'assistant' and message.get('tool_calls')
|
||||||
|
)
|
||||||
|
assert prior_tool_turn['reasoning_content'] == 'reasoning-one'
|
||||||
assert executed[0].content == (
|
assert executed[0].content == (
|
||||||
'/workspace/output.html\n<html><body>Recovered</body></html>'
|
'/workspace/output.html\n<html><body>Recovered</body></html>'
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user