refactor(providers): define typed usage contract

This commit is contained in:
chengyongru
2026-08-25 01:04:25 +08:00
committed by chengyongru
parent 89c94d8744
commit 9895c23cb5
84 changed files with 1643 additions and 726 deletions
+28 -28
View File
@@ -263,9 +263,9 @@ async def test_checkpoint1_injects_after_tool_execution():
return LLMResponse(
content="using tool",
tool_calls=[ToolCallRequest(id="c1", name="read_file", arguments={"path": "x"})],
usage={},
usage=None,
)
return LLMResponse(content="final answer", tool_calls=[], usage={})
return LLMResponse(content="final answer", tool_calls=[], usage=None)
provider.chat_with_retry = chat_with_retry
tools = MagicMock()
@@ -323,8 +323,8 @@ async def test_checkpoint2_injects_after_final_response_with_resuming_stream():
async def chat_stream_with_retry(*, messages, on_content_delta=None, **kwargs):
call_count["n"] += 1
if call_count["n"] == 1:
return LLMResponse(content="first answer", tool_calls=[], usage={})
return LLMResponse(content="second answer", tool_calls=[], usage={})
return LLMResponse(content="first answer", tool_calls=[], usage=None)
return LLMResponse(content="second answer", tool_calls=[], usage=None)
provider.chat_stream_with_retry = chat_stream_with_retry
tools = MagicMock()
@@ -411,8 +411,8 @@ async def test_checkpoint2_preserves_final_response_in_history_before_followup()
call_count["n"] += 1
captured_messages.append([dict(message) for message in messages])
if call_count["n"] == 1:
return LLMResponse(content="first answer", tool_calls=[], usage={})
return LLMResponse(content="second answer", tool_calls=[], usage={})
return LLMResponse(content="first answer", tool_calls=[], usage=None)
return LLMResponse(content="second answer", tool_calls=[], usage=None)
provider.chat_with_retry = chat_with_retry
tools = MagicMock()
@@ -474,8 +474,8 @@ async def test_loop_injected_followup_preserves_image_media(tmp_path):
call_count["n"] += 1
captured_messages.append(list(messages))
if call_count["n"] == 1:
return LLMResponse(content="first answer", tool_calls=[], usage={})
return LLMResponse(content="second answer", tool_calls=[], usage={})
return LLMResponse(content="first answer", tool_calls=[], usage=None)
return LLMResponse(content="second answer", tool_calls=[], usage=None)
provider.chat_with_retry = chat_with_retry
loop = AgentLoop(bus=bus, provider=provider, workspace=tmp_path, model="test-model")
@@ -528,8 +528,8 @@ async def test_pending_injection_resolves_its_own_runtime_context(tmp_path):
provider = MagicMock()
provider.get_default_model.return_value = "test-model"
provider.chat_with_retry = AsyncMock(side_effect=[
LLMResponse(content="first answer", tool_calls=[], usage={}),
LLMResponse(content="second answer", tool_calls=[], usage={}),
LLMResponse(content="first answer", tool_calls=[], usage=None),
LLMResponse(content="second answer", tool_calls=[], usage=None),
])
loop = AgentLoop(
bus=MessageBus(),
@@ -653,8 +653,8 @@ async def test_subagent_pending_injection_is_hidden_history_and_not_merged(tmp_p
async def chat_with_retry(*, messages, **kwargs):
call_count["n"] += 1
if call_count["n"] == 1:
return LLMResponse(content="first answer", tool_calls=[], usage={})
return LLMResponse(content="second answer", tool_calls=[], usage={})
return LLMResponse(content="first answer", tool_calls=[], usage=None)
return LLMResponse(content="second answer", tool_calls=[], usage=None)
provider.chat_with_retry = chat_with_retry
loop = AgentLoop(bus=bus, provider=provider, workspace=tmp_path, model="test-model")
@@ -714,8 +714,8 @@ async def test_runner_merges_multiple_injected_user_messages_without_losing_medi
call_count["n"] += 1
captured_messages.append([dict(message) for message in messages])
if call_count["n"] == 1:
return LLMResponse(content="first answer", tool_calls=[], usage={})
return LLMResponse(content="second answer", tool_calls=[], usage={})
return LLMResponse(content="first answer", tool_calls=[], usage=None)
return LLMResponse(content="second answer", tool_calls=[], usage=None)
provider.chat_with_retry = chat_with_retry
tools = MagicMock()
@@ -841,7 +841,7 @@ async def test_injection_cycles_capped_at_max():
async def chat_with_retry(*, messages, **kwargs):
call_count["n"] += 1
return LLMResponse(content=f"answer-{call_count['n']}", tool_calls=[], usage={})
return LLMResponse(content=f"answer-{call_count['n']}", tool_calls=[], usage=None)
provider.chat_with_retry = chat_with_retry
tools = MagicMock()
@@ -879,7 +879,7 @@ async def test_no_injections_flag_is_false_by_default():
provider = MagicMock()
async def chat_with_retry(**kwargs):
return LLMResponse(content="done", tool_calls=[], usage={})
return LLMResponse(content="done", tool_calls=[], usage=None)
provider.chat_with_retry = chat_with_retry
tools = MagicMock()
@@ -903,7 +903,7 @@ async def test_pending_queue_cleanup_on_dispatch(tmp_path):
loop = _make_loop(tmp_path)
async def chat_with_retry(**kwargs):
return LLMResponse(content="done", tool_calls=[], usage={})
return LLMResponse(content="done", tool_calls=[], usage=None)
loop.provider.chat_with_retry = chat_with_retry
@@ -1329,7 +1329,7 @@ async def test_pending_queue_preserves_overflow_for_next_injection_cycle(tmp_pat
async def chat_with_retry(*, messages, **kwargs):
call_count["n"] += 1
captured_messages.append([dict(message) for message in messages])
return LLMResponse(content=f"answer-{call_count['n']}", tool_calls=[], usage={})
return LLMResponse(content=f"answer-{call_count['n']}", tool_calls=[], usage=None)
provider.chat_with_retry = chat_with_retry
loop = AgentLoop(bus=bus, provider=provider, workspace=tmp_path, model="test-model")
@@ -1502,16 +1502,16 @@ async def test_drain_injections_on_fatal_tool_error():
return LLMResponse(
content="stale prefix ",
finish_reason="length",
usage={},
usage=None,
)
if call_count["n"] == 2:
return LLMResponse(
content="",
tool_calls=[ToolCallRequest(id="c1", name="exec", arguments={"cmd": "bad"})],
usage={},
usage=None,
)
# Third call: respond normally to the injected follow-up.
return LLMResponse(content="reply to follow-up", tool_calls=[], usage={})
return LLMResponse(content="reply to follow-up", tool_calls=[], usage=None)
provider.chat_with_retry = chat_with_retry
tools = MagicMock()
@@ -1563,10 +1563,10 @@ async def test_drain_injections_on_llm_error():
content=None,
tool_calls=[],
finish_reason="error",
usage={},
usage=None,
)
# Second call: respond normally to the injected follow-up
return LLMResponse(content="recovered answer", tool_calls=[], usage={})
return LLMResponse(content="recovered answer", tool_calls=[], usage=None)
provider.chat_with_retry = chat_with_retry
tools = MagicMock()
@@ -1614,9 +1614,9 @@ async def test_drain_injections_on_empty_final_response():
async def chat_with_retry(*, messages, **kwargs):
call_count["n"] += 1
if call_count["n"] <= _MAX_EMPTY_RETRIES + 1:
return LLMResponse(content="", tool_calls=[], usage={})
return LLMResponse(content="", tool_calls=[], usage=None)
# After retries exhausted + injection drain, respond normally
return LLMResponse(content="answer after empty", tool_calls=[], usage={})
return LLMResponse(content="answer after empty", tool_calls=[], usage=None)
provider.chat_with_retry = chat_with_retry
tools = MagicMock()
@@ -1671,7 +1671,7 @@ async def test_drain_injections_on_max_iterations():
return LLMResponse(
content="",
tool_calls=[ToolCallRequest(id=f"c{call_count['n']}", name="read_file", arguments={"path": "x"})],
usage={},
usage=None,
)
provider.chat_with_retry = chat_with_retry
@@ -1723,7 +1723,7 @@ async def test_drain_injections_set_flag_when_followup_arrives_after_last_iterat
return LLMResponse(
content="",
tool_calls=[ToolCallRequest(id=f"c{call_count['n']}", name="read_file", arguments={"path": "x"})],
usage={},
usage=None,
)
provider.chat_with_retry = chat_with_retry
@@ -1786,7 +1786,7 @@ async def test_injection_cycle_cap_on_error_path():
content=None,
tool_calls=[],
finish_reason="error",
usage={},
usage=None,
)
provider.chat_with_retry = chat_with_retry