mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-06 06:52:20 +02:00
refactor(runtime): one compact-runtime selection rule for route and dispatch
The chat route repeated the compact (clean v3) eligibility decision inline to prepare the turn's context resolution, while the agent loop dispatched on the contract stamp set by a separate, later condition. The two could drift, and already disagreed for a user whose privileges demote the turn to plain chat: the route prepared a compact resolution that no compact runtime used. src/agent_runtime/runtime_selection.py (no imports) now owns the rule: - uses_compact_preview_runtime(): clean route requested, contract policy enabled, agent mode, agent permitted, not an image generation session. - is_compact_preview_contract() and COMPACT_PREVIEW_MODE for the stamp. The route evaluates the rule once, before context preparation, where all of its facts are final (the agent privilege is read through the same _request_privileges helper the later enforcement uses). That one value gates the typed context resolution and is the _clean_v3_preview flag that stamps the contract; inside the agent-contract branch it equals the previous condition, so stamping behavior is unchanged. The agent loop dispatches through is_compact_preview_contract(), and the compact runtime's MODE is the shared constant. A route-level matrix drives the real agent loop and asserts that route preparation and compact dispatch agree for compact, escalated, configured compact/full, regular, TUI, privilege-denied and image-generation turns.
This commit is contained in:
+38
-17
@@ -87,6 +87,7 @@ from src.model_profiles import (
|
||||
)
|
||||
from src.tool_execution import AgentExecutionBridge, bind_execution_bridge
|
||||
from src.agent_runtime.authority import is_internal_tool_request, request_authority_for_http
|
||||
from src.agent_runtime.runtime_selection import uses_compact_preview_runtime
|
||||
from src.turn_contract import (
|
||||
FAMILY_TOOLS, bind_turn_contract, preserve_bound_editor_selected_tools,
|
||||
requested_capabilities, resolve_turn_contract,
|
||||
@@ -145,6 +146,14 @@ def _turn_contract_enabled(*, exact_tool_approval, runtime_surface,
|
||||
)
|
||||
|
||||
|
||||
def _request_privileges(request, user) -> Dict[str, Any]:
|
||||
"""Per-user privileges from the app's auth manager; empty when unmanaged."""
|
||||
auth_manager = getattr(request.app.state, "auth_manager", None)
|
||||
if not user or not auth_manager:
|
||||
return {}
|
||||
return auth_manager.get_privileges(user) or {}
|
||||
|
||||
|
||||
def _native_runtime_requires_local_browser(client_runtime_context):
|
||||
"""Use the private browser to verify declared local HTML artifacts."""
|
||||
context = client_runtime_context if isinstance(client_runtime_context, dict) else {}
|
||||
@@ -2893,20 +2902,32 @@ def setup_chat_routes(
|
||||
allowed_models=_allowed_models_for_request(request),
|
||||
)
|
||||
|
||||
# A compact (clean v3) turn resolves its typed context window once,
|
||||
# here, with the session's provider credentials. History shaping below
|
||||
# and the compact runtime both reuse this exact object, so the turn
|
||||
# neither probes twice nor mixes the legacy untyped lookup into it.
|
||||
# The predicate mirrors ``_clean_v3_preview`` below; the native
|
||||
# workspace term cannot veto a requested clean route.
|
||||
# Decide once whether this turn runs on the compact (clean v3)
|
||||
# runtime. Every input is final here; the native workspace term of
|
||||
# the contract policy cannot veto a requested clean route. This one
|
||||
# value prepares the turn below and stamps its contract later, and
|
||||
# the agent loop dispatches on that stamp.
|
||||
_compact_preview_turn = uses_compact_preview_runtime(
|
||||
clean_route_requested=_clean_v3_route_requested,
|
||||
turn_contract_enabled=_turn_contract_enabled(
|
||||
exact_tool_approval=exact_tool_approval,
|
||||
runtime_surface=str((client_runtime_context or {}).get("surface") or ""),
|
||||
native_workspace_contract=False,
|
||||
clean_v3_route=_clean_v3_route_requested,
|
||||
full_schema_route=(_effective_tool_schema_mode == "full"),
|
||||
),
|
||||
agent_mode=(chat_mode == "agent"),
|
||||
agent_permitted=_request_privileges(
|
||||
request, effective_user(request),
|
||||
).get("can_use_agent", True),
|
||||
image_generation=image_generation_session,
|
||||
)
|
||||
# A compact turn resolves its typed context window once, here, with
|
||||
# the session's provider credentials. History shaping below and the
|
||||
# compact runtime both reuse this exact object, so the turn neither
|
||||
# probes twice nor mixes the legacy untyped lookup into it.
|
||||
_compact_context_resolution = None
|
||||
if _clean_v3_route_requested and _turn_contract_enabled(
|
||||
exact_tool_approval=exact_tool_approval,
|
||||
runtime_surface=str((client_runtime_context or {}).get("surface") or ""),
|
||||
native_workspace_contract=False,
|
||||
clean_v3_route=True,
|
||||
full_schema_route=(_effective_tool_schema_mode == "full"),
|
||||
):
|
||||
if _compact_preview_turn:
|
||||
from src.agent_runtime.context_resolution import resolve_effective_context
|
||||
_compact_context_resolution = await resolve_effective_context(
|
||||
sess.endpoint_url, sess.model, headers=sess.headers,
|
||||
@@ -3143,7 +3164,9 @@ def setup_chat_routes(
|
||||
# pasted revision request. This only offers permitted schemas;
|
||||
# it never requires or performs a document mutation.
|
||||
_turn_capabilities = _turn_capabilities | {"documents"}
|
||||
_clean_v3_preview = bool(_use_turn_contract and _clean_v3_route_requested)
|
||||
# Same decision that prepared the turn; it only stamps the contract
|
||||
# inside the agent-contract branch below.
|
||||
_clean_v3_preview = _compact_preview_turn
|
||||
# requested_capabilities already inherits a typed, recently executed
|
||||
# family for referential follow-ups. Do not additionally union stale
|
||||
# families into an explicit new request: that inflated regular-model
|
||||
@@ -3288,13 +3311,11 @@ def setup_chat_routes(
|
||||
})
|
||||
|
||||
# Enforce per-user privileges
|
||||
_privs = {}
|
||||
# Bearer clients enter the agent loop as the sandboxed ``api`` user,
|
||||
# but their token is owned by the real account. Use that owner here so
|
||||
# a permitted TUI/WebUI client does not inherit api's default denial.
|
||||
_user = effective_user(request)
|
||||
if _user and hasattr(request.app.state, 'auth_manager') and request.app.state.auth_manager:
|
||||
_privs = request.app.state.auth_manager.get_privileges(_user)
|
||||
_privs = _request_privileges(request, _user)
|
||||
if _privs:
|
||||
if not _privs.get("can_use_bash", True):
|
||||
disabled_tools.update(FAMILY_TOOLS["shell_files"])
|
||||
|
||||
+2
-1
@@ -84,6 +84,7 @@ from src.tool_types import ToolBlock
|
||||
from src.turn_contract import selected_tools_for_request, with_turn_contract
|
||||
from src.agent_runtime.journal import propose_action, execute_action
|
||||
from src.agent_runtime.completion import with_completion_gate
|
||||
from src.agent_runtime.runtime_selection import is_compact_preview_contract
|
||||
from src.teacher_escalation import with_teacher_takeover, request_teacher_takeover
|
||||
from src.tool_utils import _truncate, get_mcp_manager
|
||||
from src.agent_tools import (
|
||||
@@ -20461,7 +20462,7 @@ async def stream_agent_loop(
|
||||
),
|
||||
)
|
||||
|
||||
if turn_contract is not None and turn_contract.selection_mode == 'clean_compact_v3_preview':
|
||||
if is_compact_preview_contract(turn_contract):
|
||||
from src.clean_agent_preview import stream_preview
|
||||
async for chunk in stream_preview(
|
||||
endpoint_url=endpoint_url, model=model, messages=messages, headers=headers,
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
"""Whether a turn runs on the compact (clean v3) preview runtime.
|
||||
|
||||
The chat route decides this once, from facts known before context
|
||||
preparation, and uses that one value both to prepare the turn (its typed
|
||||
context resolution) and to stamp the turn contract's selection mode. The
|
||||
agent loop dispatches on that stamp. Keeping both sides here, with no other
|
||||
imports, means preparation and dispatch read one rule and cannot drift.
|
||||
|
||||
Runtime selection is not authority: it grants or denies no operation.
|
||||
"""
|
||||
|
||||
COMPACT_PREVIEW_MODE = "clean_compact_v3_preview"
|
||||
|
||||
|
||||
def uses_compact_preview_runtime(
|
||||
*,
|
||||
clean_route_requested: bool,
|
||||
turn_contract_enabled: bool,
|
||||
agent_mode: bool,
|
||||
agent_permitted: bool,
|
||||
image_generation: bool,
|
||||
) -> bool:
|
||||
"""The single compact-runtime eligibility rule for one turn.
|
||||
|
||||
``turn_contract_enabled`` is the route's contract policy for this turn
|
||||
(exact approvals, TUI surface and full-schema routes opt out).
|
||||
``agent_permitted`` is false when the user's privileges demote the turn
|
||||
to plain chat; image generation sessions run their own execution path.
|
||||
"""
|
||||
return bool(
|
||||
clean_route_requested
|
||||
and turn_contract_enabled
|
||||
and agent_mode
|
||||
and agent_permitted
|
||||
and not image_generation
|
||||
)
|
||||
|
||||
|
||||
def is_compact_preview_contract(turn_contract) -> bool:
|
||||
"""Whether a turn contract was stamped for the compact runtime."""
|
||||
return (
|
||||
turn_contract is not None
|
||||
and getattr(turn_contract, "selection_mode", None) == COMPACT_PREVIEW_MODE
|
||||
)
|
||||
@@ -21,6 +21,7 @@ import httpx
|
||||
import jsonschema
|
||||
|
||||
from src.context_compactor import prune_multimodal_images, trim_for_context
|
||||
from src.agent_runtime.runtime_selection import COMPACT_PREVIEW_MODE
|
||||
from src import agent_runs
|
||||
from src.agent_evidence import command_has_mutation_effect, workspace_artifact_is_usable
|
||||
from src.tool_capabilities import ToolEffect, ToolRunSecurityContext, capabilities_for_action
|
||||
@@ -47,7 +48,7 @@ from src.model_profiles import (
|
||||
)
|
||||
|
||||
ENDPOINT_ID = 'cleanv3'
|
||||
MODE = 'clean_compact_v3_preview'
|
||||
MODE = COMPACT_PREVIEW_MODE
|
||||
|
||||
|
||||
class ProviderStreamError(Exception):
|
||||
|
||||
@@ -399,3 +399,114 @@ async def test_offline_guard_replaces_only_io_edges(context_probe_ledger):
|
||||
assert first.probe_errors == ("models:transport_error",)
|
||||
assert (first.evidence, first.effective) == (ContextEvidence.KNOWN_TABLE, 128000)
|
||||
assert second.cached and not second.provider_io
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Route preparation and agent-loop dispatch share one compact decision
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class _RegularPath(Exception):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
"case, model, mode, privileges, surface, configured, image_generation, expected, regular_loop",
|
||||
[
|
||||
("compact_agent", COMPACT_MODEL, "agent", None, None, "", False, True, False),
|
||||
("compact_chat_escalates", COMPACT_MODEL, "chat", None, None, "", False, True, False),
|
||||
("regular_model", "selected-model", "agent", None, None, "", False, False, True),
|
||||
("configured_compact", "selected-model", "agent", None, None, "compact", False, True, False),
|
||||
("configured_full", COMPACT_MODEL, "agent", None, None, "full", False, False, True),
|
||||
# Plain chat and image generation leave before the agent loop.
|
||||
("agent_privilege_denied", COMPACT_MODEL, "agent", {"can_use_agent": False}, None, "", False, False, False),
|
||||
("agent_privilege_granted", COMPACT_MODEL, "agent", {"can_use_agent": True}, None, "", False, True, False),
|
||||
("tui_surface", COMPACT_MODEL, "agent", None, "odysseus-tui", "", False, False, True),
|
||||
("image_generation", COMPACT_MODEL, "agent", None, None, "", True, False, False),
|
||||
],
|
||||
)
|
||||
async def test_route_preparation_and_compact_dispatch_cannot_diverge(
|
||||
monkeypatch, context_probe_ledger,
|
||||
case, model, mode, privileges, surface, configured, image_generation, expected, regular_loop,
|
||||
):
|
||||
from routes import chat_routes
|
||||
import src.agent_loop as agent_loop
|
||||
|
||||
seen = _spy(monkeypatch)
|
||||
_install_model(monkeypatch)
|
||||
captured = {}
|
||||
endpoint = _chat_stream_endpoint(
|
||||
monkeypatch, mode, captured, capture_context=True, session_model=model,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
chat_routes, "coerce_message_and_session", lambda *args, **kwargs: ("hello", "session-1"),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
chat_routes, "_configured_model_tool_surface", lambda *args, **kwargs: configured,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
chat_routes, "_is_image_generation_session", lambda *args, **kwargs: image_generation,
|
||||
)
|
||||
# Real agent loop: the compact branch reaches the recorded stream_preview;
|
||||
# the regular branch stops at its first step.
|
||||
regular = []
|
||||
|
||||
def stop_regular(*args, **kwargs):
|
||||
regular.append(True)
|
||||
raise _RegularPath()
|
||||
|
||||
monkeypatch.setattr(agent_loop, "_contract_allows_single_action_terminal", stop_regular)
|
||||
monkeypatch.setattr(chat_routes, "stream_agent_loop", agent_loop.stream_agent_loop)
|
||||
|
||||
request = _RouteRequest(mode, privileges=privileges)
|
||||
request._form.update({"message": "hello", "compare_mode": "false"})
|
||||
if surface:
|
||||
request._form["client_runtime_context"] = json.dumps({"surface": surface})
|
||||
response = await endpoint(request)
|
||||
try:
|
||||
async for _ in response.body_iterator:
|
||||
pass
|
||||
except _RegularPath:
|
||||
pass
|
||||
|
||||
prepared = captured["build_context"].get("context_resolution")
|
||||
dispatched_compact = bool(seen["preview_kwargs"])
|
||||
assert (prepared is not None) == dispatched_compact == expected, case
|
||||
if expected:
|
||||
# One resolution, prepared by the route and reused by dispatch.
|
||||
assert len(seen["resolutions"]) == 1 and len(context_probe_ledger) == 1
|
||||
assert seen["preview_kwargs"][0]["context_resolution"] is prepared
|
||||
else:
|
||||
assert seen["resolutions"] == [] and context_probe_ledger == []
|
||||
# The case really reached the dispatch point it claims to exercise.
|
||||
assert bool(regular) == regular_loop, case
|
||||
|
||||
|
||||
def test_compact_selection_rule_and_contract_stamp():
|
||||
from src.agent_runtime.runtime_selection import (
|
||||
COMPACT_PREVIEW_MODE, is_compact_preview_contract, uses_compact_preview_runtime,
|
||||
)
|
||||
from src.clean_agent_preview import MODE
|
||||
from routes.chat_routes import _turn_contract_enabled
|
||||
from types import SimpleNamespace
|
||||
|
||||
facts = dict(
|
||||
clean_route_requested=True, turn_contract_enabled=True,
|
||||
agent_mode=True, agent_permitted=True, image_generation=False,
|
||||
)
|
||||
assert uses_compact_preview_runtime(**facts)
|
||||
for name, value in (
|
||||
("clean_route_requested", False), ("turn_contract_enabled", False),
|
||||
("agent_mode", False), ("agent_permitted", False), ("image_generation", True),
|
||||
):
|
||||
assert not uses_compact_preview_runtime(**{**facts, name: value}), name
|
||||
# An exact tool approval opts the turn out through the contract policy.
|
||||
assert not uses_compact_preview_runtime(**{**facts, "turn_contract_enabled": _turn_contract_enabled(
|
||||
exact_tool_approval=object(), runtime_surface="", native_workspace_contract=False,
|
||||
clean_v3_route=True,
|
||||
)})
|
||||
# The route stamps contracts with the same constant the loop checks.
|
||||
assert MODE == COMPACT_PREVIEW_MODE
|
||||
assert is_compact_preview_contract(SimpleNamespace(selection_mode=MODE))
|
||||
assert not is_compact_preview_contract(SimpleNamespace(selection_mode="routed"))
|
||||
assert not is_compact_preview_contract(None)
|
||||
|
||||
Reference in New Issue
Block a user