Files
Conflict_Checker/tests/agents/test_sheet_extractor_fallback.py
T
woogi 3d7fce7bf9
Docker Release / build-and-push (push) Successful in 57s
Docker Release / release (push) Skipped
Kill extract-wave truncation: 65k ceiling, hard thinking budget, reasoning-token telemetry
Job 98194fa8d215 showed every extract call hitting the 32k cap with only
~20k chars visible despite reasoning effort=low - Gemini 2.5 Pro still
burned ~25k thinking tokens per sheet.

- EXTRACT_MAX_TOKENS default 32768 -> 65536 (model output ceiling)
- new EXTRACT_REASONING_MAX_TOKENS (default 2048): OpenRouter reasoning
  max_tokens / Gemini thinking_budget; takes precedence over effort
- log per-call reasoning token counts (usage.completion_tokens_details)
  and include thinking count in the finish_reason=length marker
2026-08-09 08:17:41 -05:00

77 lines
2.6 KiB
Python

"""SheetExtractorAgent fallback ladder tests (bare-list wrap + compact retry)."""
from unittest.mock import patch
from backend.agents.base import AgentScope, AgentUsage
from backend.agents.extractors import SheetExtractorAgent, _wrap_bare_list
def _scope():
return AgentScope(
scope_id="sheet:4",
payload={"page": {"page_number": 4, "base64": "QUJD"}},
)
def _objects(n=2):
return [
{
"object_id": f"obj-{i}",
"object_type": "equipment",
"category": "mechanical",
"name": f"RTU-{i}",
"source_text": f"RTU-{i}",
"confidence": "high",
}
for i in range(n)
]
def test_wrap_bare_list_builds_sheet_envelope():
wrapped = _wrap_bare_list(_objects(3), page_number=4)
assert wrapped["sheet"] == {}
assert len(wrapped["objects"]) == 3
def test_wrap_bare_list_passes_dicts_and_none_through():
assert _wrap_bare_list({"sheet": {}, "objects": []}, 1) == {"sheet": {}, "objects": []}
assert _wrap_bare_list(None, 1) is None
def test_run_accepts_bare_list_response():
agent = SheetExtractorAgent(usage=AgentUsage())
with patch("backend.agents.extractors.call_json",
return_value=_objects(5)) as mock_call:
result = agent.run(_scope())
assert not result.error
assert len(result.artifacts) == 1
sheet = result.artifacts[0]
assert sheet["page_number"] == 4
assert len(sheet["assertions"]) == 5
# No compact retry needed when the first call yields data.
assert mock_call.call_count == 1
# Reasoning knobs are forwarded (None when config is blank in tests).
assert "reasoning_effort" in mock_call.call_args.kwargs
assert "reasoning_max_tokens" in mock_call.call_args.kwargs
def test_run_compact_retry_after_hard_failure():
agent = SheetExtractorAgent(usage=AgentUsage())
with patch("backend.agents.extractors.call_json",
side_effect=[None, {"sheet": {"sheet_number": "A102"},
"objects": _objects(2)}]) as mock_call:
result = agent.run(_scope())
assert not result.error
assert result.artifacts[0]["sheet_number"] == "A102"
assert mock_call.call_count == 2
# Second call carried the compact suffix.
assert "COMPACT RETRY" in mock_call.call_args_list[1].kwargs["user_text"]
def test_run_fails_only_after_both_attempts_miss():
agent = SheetExtractorAgent(usage=AgentUsage())
with patch("backend.agents.extractors.call_json", return_value=None) as mock_call:
result = agent.run(_scope())
assert result.error == "no structured extraction returned"
assert mock_call.call_count == 2