mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-06 06:52:20 +02:00
test(agent): restore exact turn-policy regressions
This commit is contained in:
@@ -3499,11 +3499,12 @@ def setup_chat_routes(
|
||||
# has an exact routed contract. Adding the whole native
|
||||
# workspace inventory here reintroduced overlapping PDF
|
||||
# readers and caused the model to abandon the selected
|
||||
# OCR operation. Exact operations therefore stay exact;
|
||||
# ordinary native turns retain warm and workspace tools.
|
||||
# OCR operation. A task-only turn likewise has an exact
|
||||
# personal manager; core shell/Web tools are not task
|
||||
# fallbacks. Other turns retain warm and workspace tools.
|
||||
extra_tools=(
|
||||
frozenset()
|
||||
if _exact_selected_native_chain or (
|
||||
if _exact_selected_native_chain or _active_turn_capabilities == frozenset({"tasks"}) or (
|
||||
_active_turn_capabilities in (
|
||||
frozenset({"transcription"}), frozenset({"ocr"}),
|
||||
)
|
||||
|
||||
@@ -617,7 +617,7 @@ def test_finish_nudge_does_not_accept_unfinished_correction_promise(monkeypatch)
|
||||
monkeypatch,
|
||||
[
|
||||
'```write_file\n/workspace/output.html\n<body>draft</body>\n```',
|
||||
"The preview revealed a defect. I should complete output.html by adding labels.",
|
||||
"The draft has a defect. I should complete output.html by adding labels.",
|
||||
'```write_file\n/workspace/output.html\n<body>corrected</body>\n```',
|
||||
"Done. Corrected and checked output.html.",
|
||||
],
|
||||
|
||||
@@ -9,6 +9,7 @@ Fix: (1) Read from JSON body as fallback.
|
||||
"""
|
||||
|
||||
import ast
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
@@ -35,6 +36,7 @@ from src.tool_policy import (
|
||||
web_intent_may_enable_for_turn,
|
||||
web_search_enabled_for_turn,
|
||||
)
|
||||
from tests.test_foreground_model_routing import _RouteRequest, _chat_stream_endpoint
|
||||
|
||||
_CHAT_ROUTES = Path(__file__).resolve().parent.parent / "routes" / "chat_routes.py"
|
||||
|
||||
@@ -283,23 +285,94 @@ def test_contextual_browser_followup_recognizes_current_page_inspection():
|
||||
assert not _is_contextual_browser_followup("Show my notes.", session)
|
||||
|
||||
|
||||
def test_clean_browser_filter_preserves_native_pdf_extraction_contract():
|
||||
source = _CHAT_ROUTES.read_text(encoding="utf-8")
|
||||
assert "INTERACTIVE_CORE_TOOLS" in source
|
||||
assert "NATIVE_WORKSPACE_TOOLS" in source
|
||||
assert "scope_preview_contract(" in source
|
||||
async def _clean_route_contract(monkeypatch, message, *, history=(), native=False):
|
||||
from routes import chat_routes
|
||||
from src import tool_security
|
||||
|
||||
with monkeypatch.context() as route_patch:
|
||||
endpoint = _chat_stream_endpoint(
|
||||
route_patch, "agent", {},
|
||||
session_model="odysseus-qwen3.5-tools-pre-heretic",
|
||||
session_history=history,
|
||||
)
|
||||
route_patch.setattr(
|
||||
chat_routes, "coerce_message_and_session",
|
||||
lambda *args, **kwargs: (message, "session-1"),
|
||||
)
|
||||
route_patch.setattr(
|
||||
tool_security, "owner_is_admin_or_single_user", lambda owner: True,
|
||||
)
|
||||
observed = []
|
||||
|
||||
async def capture_agent(*args, **kwargs):
|
||||
observed.append(kwargs["turn_contract"])
|
||||
yield 'data: {"delta":"Contract constructed."}\n\n'
|
||||
yield "data: [DONE]\n\n"
|
||||
|
||||
route_patch.setattr(chat_routes, "stream_agent_loop", capture_agent)
|
||||
request = _RouteRequest("agent")
|
||||
request._form.update({"message": message, "compare_mode": "false"})
|
||||
if native:
|
||||
request._form.update({
|
||||
"cwd": "/tmp/native-workspace",
|
||||
"workspace": "/tmp/native-workspace",
|
||||
"client_runtime_context": json.dumps({
|
||||
"surface": "odysseus-native", "terminal_agent": True,
|
||||
"unattended_mode": True,
|
||||
"input_files": ["/workspace/paper.pdf"],
|
||||
}),
|
||||
})
|
||||
response = await endpoint(request)
|
||||
async for _ in response.body_iterator:
|
||||
pass
|
||||
assert len(observed) == 1
|
||||
return observed[0]
|
||||
|
||||
|
||||
def test_clean_preview_only_offers_browser_for_explicit_or_typed_warm_turns():
|
||||
source = _CHAT_ROUTES.read_text(encoding="utf-8")
|
||||
assert "INTERACTIVE_CORE_TOOLS" in source
|
||||
assert '{"private_browser"} if _local_browser_render_intent else frozenset()' in source
|
||||
@pytest.mark.asyncio
|
||||
async def test_clean_browser_filter_preserves_native_pdf_extraction_contract(monkeypatch):
|
||||
contract = await _clean_route_contract(
|
||||
monkeypatch,
|
||||
"Extract Table 2 from /workspace/paper.pdf using pdf_extract.",
|
||||
native=True,
|
||||
)
|
||||
assert contract.capabilities == {"shell_files"}
|
||||
assert contract.permits("pdf_extract")
|
||||
assert "private_browser" not in contract.offered
|
||||
assert not {"manage_tasks", "search_emails", "send_email"} & contract.offered
|
||||
|
||||
|
||||
def test_explicit_web_fetch_is_not_erased_by_generic_browser_intent():
|
||||
source = _CHAT_ROUTES.read_text(encoding="utf-8")
|
||||
assert "INTERACTIVE_CORE_TOOLS" in source
|
||||
assert "_exact_selected_native_chain" in source
|
||||
@pytest.mark.asyncio
|
||||
async def test_clean_preview_only_offers_browser_for_explicit_or_typed_warm_turns(monkeypatch):
|
||||
message = "Summarize the status."
|
||||
no_history = await _clean_route_contract(monkeypatch, message)
|
||||
failed_history = [{"role": "assistant", "metadata": {"tool_events": [{
|
||||
"tool": "private_browser", "exit_code": 1, "error": True,
|
||||
}]}}]
|
||||
failed = await _clean_route_contract(monkeypatch, message, history=failed_history)
|
||||
successful_history = [{"role": "assistant", "metadata": {"tool_events": [{
|
||||
"tool": "private_browser", "exit_code": 0, "error": False,
|
||||
}]}}]
|
||||
warm = await _clean_route_contract(monkeypatch, message, history=successful_history)
|
||||
explicit = await _clean_route_contract(
|
||||
monkeypatch, "Open https://example.com with the private browser",
|
||||
)
|
||||
assert "private_browser" not in no_history.offered
|
||||
assert "private_browser" not in failed.offered
|
||||
assert warm.permits("private_browser")
|
||||
assert explicit.permits("private_browser")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_explicit_web_fetch_is_not_erased_by_generic_browser_intent(monkeypatch):
|
||||
contract = await _clean_route_contract(
|
||||
monkeypatch,
|
||||
"Use web_fetch to read https://example.com/report in the private browser.",
|
||||
)
|
||||
assert contract.capabilities == {"search_browser"}
|
||||
assert contract.required == {"web_fetch"}
|
||||
assert contract.permits("web_fetch")
|
||||
assert not {"manage_tasks", "search_emails", "send_email"} & contract.offered
|
||||
|
||||
|
||||
def test_web_followup_grammar_covers_article_detail_questions():
|
||||
|
||||
@@ -147,6 +147,7 @@ def _chat_stream_endpoint(
|
||||
capture_context=False,
|
||||
endpoint_url="https://selected.example/v1",
|
||||
session_model="selected-model",
|
||||
session_history=(),
|
||||
):
|
||||
def add_message(message):
|
||||
captured.setdefault("added_messages", []).append(message)
|
||||
@@ -156,7 +157,7 @@ def _chat_stream_endpoint(
|
||||
model=session_model,
|
||||
headers={"Authorization": "Bearer selected"},
|
||||
name="test",
|
||||
history=[],
|
||||
history=list(session_history),
|
||||
add_message=add_message,
|
||||
)
|
||||
session_manager = SimpleNamespace(
|
||||
|
||||
@@ -107,7 +107,7 @@ def test_odysseus_notes_mode_clamps_without_overriding_caller_denials(monkeypatc
|
||||
assert {"manage_notes", "manage_calendar", "manage_tasks"} <= route["disabled_tools"]
|
||||
|
||||
|
||||
def test_odysseus_general_mode_uses_compact_core(monkeypatch):
|
||||
def test_odysseus_router_uses_compact_core(monkeypatch):
|
||||
prompt_calls, _ = _install_route_probe(monkeypatch)
|
||||
|
||||
_run_probe(
|
||||
@@ -119,6 +119,23 @@ def test_odysseus_general_mode_uses_compact_core(monkeypatch):
|
||||
assert route["relevant_tools"] == GENERAL_COMPACT_TOOLS
|
||||
|
||||
|
||||
def test_odysseus_general_no_tool_mode_has_no_executable_surface(monkeypatch):
|
||||
from src.tool_policy import known_tool_names
|
||||
|
||||
# The current merged profile takes the compact-router branch. Exercise
|
||||
# the legacy general mode itself so its execution denial stays covered.
|
||||
monkeypatch.setattr(agent_loop, "_is_qwen38_tool_router", lambda model: False)
|
||||
prompt_calls, stream_calls = _install_route_probe(monkeypatch)
|
||||
|
||||
_run_probe(
|
||||
[{"role": "user", "content": "Explain the CAP theorem with a concrete distributed database example."}],
|
||||
relevant_tools={"bash", "manage_notes", "ask_user"},
|
||||
)
|
||||
|
||||
assert stream_calls[0]["tools"] is None
|
||||
assert known_tool_names() <= prompt_calls[0]["disabled_tools"]
|
||||
|
||||
|
||||
def test_odysseus_calendar_intent_uses_compact_calendar_route(monkeypatch):
|
||||
prompt_calls, _ = _install_route_probe(monkeypatch)
|
||||
|
||||
|
||||
@@ -368,13 +368,13 @@ async def test_exact_odysseus_clean_route_offers_only_requested_compact_family(
|
||||
async for _ in response.body_iterator:
|
||||
pass
|
||||
|
||||
from src.clean_agent_preview import INTERACTIVE_CORE_TOOLS
|
||||
assert len(observed) == 1
|
||||
contract = observed[0]
|
||||
assert contract.selection_mode == "clean_compact_v3_preview"
|
||||
assert contract.capabilities == {"tasks"}
|
||||
assert contract.offered == {"manage_tasks"} | set(INTERACTIVE_CORE_TOOLS)
|
||||
assert contract.offered == {"manage_tasks", "ask_user"}
|
||||
assert contract.required == {"manage_tasks"}
|
||||
assert contract.permits("manage_tasks")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
Reference in New Issue
Block a user