test(fase2): assert BudgetMiddleware short-circuits a real chat call + the debate
This commit is contained in:
parent
1694141bff
commit
7b08b37da3
2 changed files with 49 additions and 3 deletions
|
|
@ -33,9 +33,7 @@ async def _noop() -> None:
|
|||
|
||||
def _resp(total: int | None) -> ChatResponse:
|
||||
usage = UsageDetails(total_token_count=total) if total is not None else None
|
||||
return ChatResponse(
|
||||
messages=[Message(role="assistant", contents=["x"])], usage_details=usage
|
||||
)
|
||||
return ChatResponse(messages=[Message(role="assistant", contents=["x"])], usage_details=usage)
|
||||
|
||||
|
||||
async def test_meter_reads_total_token_count_and_accumulates() -> None:
|
||||
|
|
@ -69,6 +67,24 @@ async def test_strict_usage_none_hard_fails() -> None:
|
|||
await mw.process(_Ctx(_resp(None)), _noop) # type: ignore[arg-type]
|
||||
|
||||
|
||||
async def test_budget_middleware_fires_on_real_agent_chat(make_client_factory) -> None:
|
||||
"""F8 (real-client half): registering BudgetMiddleware on a real Agent (layered chat client
|
||||
that carries ChatMiddlewareLayer) and running an actual chat call short-circuits with
|
||||
BudgetExceeded — the middleware<->client integration the prior suite never exercised (the
|
||||
minimal-base stand-in silently no-ops the middleware)."""
|
||||
from agent_framework import Agent
|
||||
|
||||
client = make_client_factory("ok", tokens=8)("proposer") # 8 tokens/reply > cap 5
|
||||
meter = TokenMeter(Budget(max_tokens=5, max_rounds=10))
|
||||
agent = Agent(
|
||||
client, "propose a measure", name="proposer", middleware=[BudgetMiddleware(meter)]
|
||||
)
|
||||
with pytest.raises(BudgetExceeded) as exc:
|
||||
await agent.run("hi")
|
||||
assert exc.value.kind == "tokens"
|
||||
assert meter.tokens == 8 # charged from the synthetic UsageDetails via the middleware
|
||||
|
||||
|
||||
def test_no_word_count_token_proxy_in_src() -> None:
|
||||
# The meter is fed from real UsageDetails, never a len(text.split()) word-count proxy
|
||||
# (research 03 Rec 3 — the Fase 1 _word_tokens anti-pattern is retired). NOTE: a bare
|
||||
|
|
|
|||
|
|
@ -169,6 +169,36 @@ async def test_g_validated_proposal_derives_from_debate(
|
|||
assert isinstance(result.outcome, ValidatedProposal)
|
||||
|
||||
|
||||
async def test_h_tiny_budget_halts_via_debate_middleware(
|
||||
docs_dir, make_client_factory, fresh_store, monkeypatch
|
||||
) -> None:
|
||||
"""F8 (debate-path half): a tiny budget must short-circuit in the DEBATE middleware, not the
|
||||
later generate loop. Fence generation — if generate_via_llm is reached, the debate did NOT
|
||||
short-circuit, so fail. Because generate_via_llm independently raises BudgetExceeded, a plain
|
||||
pytest.raises would pass even with the debate middleware detached; the fence makes this test
|
||||
load-bearing (detaching the middleware fails it)."""
|
||||
from portfolio_optimiser import run as run_mod
|
||||
|
||||
async def _forbidden(*args, **kwargs):
|
||||
raise AssertionError(
|
||||
"generate_via_llm reached — debate did not short-circuit via middleware"
|
||||
)
|
||||
|
||||
monkeypatch.setattr(run_mod, "generate_via_llm", _forbidden)
|
||||
with pytest.raises(BudgetExceeded):
|
||||
await run_project(
|
||||
"FV42-GSV-E1",
|
||||
"local",
|
||||
docs_dir=docs_dir,
|
||||
verdict_input=_VI,
|
||||
client_factory=make_client_factory(
|
||||
_VALID, tokens=100
|
||||
), # 100 > cap 10, on the FIRST debate call
|
||||
store=fresh_store,
|
||||
max_tokens=10,
|
||||
)
|
||||
|
||||
|
||||
async def test_f_malformed_contract_raises_before_any_chat(docs_dir, make_client_factory) -> None:
|
||||
calls = {"n": 0}
|
||||
base = make_client_factory(_VALID)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue