mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-21 06:12:21 +02:00
Merge remote-tracking branch 'preview-upstream/lab' into feature/runtime-containment
This commit is contained in:
+13
-1
@@ -1027,11 +1027,15 @@ async def build_chat_context(
|
||||
persist_user_message: bool = True,
|
||||
interaction_mode: str | None = None,
|
||||
auto_escalated: bool = False,
|
||||
context_resolution=None,
|
||||
) -> ChatContext:
|
||||
"""Build the full context (preface + messages) for an LLM call.
|
||||
|
||||
This is the shared logic between /chat and /chat_stream — preset extraction,
|
||||
message preprocessing, memory/RAG/web injection, compaction, normalization.
|
||||
|
||||
``context_resolution`` is the turn's already resolved context window. When
|
||||
supplied, history shaping sizes against it instead of probing the endpoint.
|
||||
"""
|
||||
# Preset
|
||||
preset = extract_preset(chat_handler, preset_id)
|
||||
@@ -1225,12 +1229,20 @@ async def build_chat_context(
|
||||
# for every candidate. Running selected-model compaction here would mutate
|
||||
# session history before we know which route can answer and would make a
|
||||
# later larger-context candidate unable to recover discarded history.
|
||||
if context_resolution is not None:
|
||||
prepared_window = {"context_length": context_resolution.shaping_window}
|
||||
else:
|
||||
prepared_window = {}
|
||||
if defer_context_shaping:
|
||||
context_length = get_context_length(sess.endpoint_url, sess.model)
|
||||
context_length = (
|
||||
prepared_window.get("context_length")
|
||||
or get_context_length(sess.endpoint_url, sess.model)
|
||||
)
|
||||
was_compacted = False
|
||||
else:
|
||||
messages, context_length, was_compacted = await maybe_compact(
|
||||
sess, sess.endpoint_url, sess.model, messages, sess.headers, owner=user,
|
||||
**prepared_window,
|
||||
)
|
||||
_before_trim_messages = len(messages)
|
||||
_before_trim_tokens = estimate_tokens(messages)
|
||||
|
||||
+54
-4
@@ -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,19 @@ 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."""
|
||||
try:
|
||||
app = getattr(request, "app", None)
|
||||
except (AttributeError, KeyError):
|
||||
app = None
|
||||
state = getattr(app, "state", None) if app is not None else None
|
||||
auth_manager = getattr(state, "auth_manager", None) if state is not None else 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,6 +2907,38 @@ def setup_chat_routes(
|
||||
allowed_models=_allowed_models_for_request(request),
|
||||
)
|
||||
|
||||
# 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 _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,
|
||||
client_runtime_context=client_runtime_context,
|
||||
)
|
||||
|
||||
# Build shared context (stream path uses enhanced_message for context preface)
|
||||
ctx = await build_chat_context(
|
||||
sess, request, chat_handler, chat_processor,
|
||||
@@ -2923,6 +2969,7 @@ def setup_chat_routes(
|
||||
else None
|
||||
),
|
||||
persist_user_message=not tool_approval_continuation and not is_internal_tool_request(request),
|
||||
context_resolution=_compact_context_resolution,
|
||||
interaction_mode=chat_mode,
|
||||
auto_escalated=auto_escalated,
|
||||
)
|
||||
@@ -3122,7 +3169,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
|
||||
@@ -3267,13 +3316,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"])
|
||||
@@ -4459,6 +4506,9 @@ def setup_chat_routes(
|
||||
client_runtime_context=client_runtime_context,
|
||||
thinking_mode=thinking_mode,
|
||||
reasoning_effort=reasoning_effort,
|
||||
context_resolution=(
|
||||
_compact_context_resolution if _clean_v3_preview else None
|
||||
),
|
||||
):
|
||||
if chunk.startswith("data: ") and not chunk.startswith("data: [DONE]"):
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user