mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-26 22:42:23 +02:00
[eric] tree: group foundational agent modules under agents/core
This commit is contained in:
@@ -9,10 +9,10 @@ from datetime import datetime
|
||||
from uuid import uuid4
|
||||
from typing import Optional
|
||||
|
||||
from backend.apps.agents.models import (
|
||||
from backend.apps.agents.core.models import (
|
||||
AgentConfig, AgentSession, Message, MessageBranch, ApprovalRequest, ToolGroupMeta,
|
||||
)
|
||||
from backend.apps.agents.ws_manager import ws_manager
|
||||
from backend.apps.agents.core.ws_manager import ws_manager
|
||||
from backend.apps.settings.settings import load_settings
|
||||
from backend.apps.tools_lib.tools_lib import (
|
||||
_load_all as load_all_tools,
|
||||
@@ -26,7 +26,7 @@ from backend.apps.tools_lib.tools_lib import (
|
||||
save_trusted_sensitive_paths,
|
||||
)
|
||||
from backend.config.paths import SESSIONS_DIR
|
||||
from backend.apps.agents.error_classify import (
|
||||
from backend.apps.agents.core.error_classify import (
|
||||
_NON_TRANSIENT_PATTERNS,
|
||||
_TRANSIENT_CAPACITY_PATTERNS,
|
||||
_is_auth_error,
|
||||
@@ -48,7 +48,7 @@ from backend.apps.agents.tool_catalog import (
|
||||
_get_denied_tool_names,
|
||||
_is_fully_denied,
|
||||
)
|
||||
from backend.apps.agents.aux_llm import _safe_resp_text
|
||||
from backend.apps.agents.core.aux_llm import _safe_resp_text
|
||||
from backend.apps.agents.history_compaction import (
|
||||
_build_history_prefix,
|
||||
_get_branch_messages,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from backend.config.Apps import SubApp
|
||||
from backend.apps.agents.agent_manager import agent_manager
|
||||
from backend.apps.agents.ws_manager import ws_manager
|
||||
from backend.apps.agents.models import AgentConfig, ApprovalResponse
|
||||
from backend.apps.agents.core.ws_manager import ws_manager
|
||||
from backend.apps.agents.core.models import AgentConfig, ApprovalResponse
|
||||
from contextlib import asynccontextmanager
|
||||
from fastapi import WebSocket, WebSocketDisconnect, HTTPException
|
||||
from fastapi.responses import JSONResponse
|
||||
@@ -67,8 +67,8 @@ async def send_message(session_id: str, body: dict):
|
||||
|
||||
# Run MCP-suggestion classifier in parallel with the agent launch; fails open.
|
||||
try:
|
||||
from backend.apps.agents.mcp_preflight import run_preflight
|
||||
from backend.apps.agents.ws_manager import ws_manager as _ws
|
||||
from backend.apps.agents.core.mcp_preflight import run_preflight
|
||||
from backend.apps.agents.core.ws_manager import ws_manager as _ws
|
||||
|
||||
async def _emit_preflight():
|
||||
try:
|
||||
@@ -271,7 +271,7 @@ async def compact_session(session_id: str):
|
||||
raise HTTPException(status_code=404, detail="session not found")
|
||||
fired = agent_manager._maybe_compact(session, force=True)
|
||||
if fired:
|
||||
from backend.apps.agents.ws_manager import ws_manager
|
||||
from backend.apps.agents.core.ws_manager import ws_manager
|
||||
try:
|
||||
await ws_manager.send_to_session(session_id, "agent:context_status", {
|
||||
"session_id": session_id,
|
||||
@@ -296,7 +296,7 @@ async def clear_session(session_id: str):
|
||||
session.compacted_through_msg_id = None
|
||||
session.tokens = {"input": 0, "output": 0}
|
||||
session.needs_fresh_session = True
|
||||
from backend.apps.agents.ws_manager import ws_manager
|
||||
from backend.apps.agents.core.ws_manager import ws_manager
|
||||
try:
|
||||
await ws_manager.send_to_session(session_id, "agent:status", {
|
||||
"session_id": session_id,
|
||||
|
||||
@@ -38,8 +38,8 @@ from backend.apps.agents.browser_schema import (
|
||||
MODEL_MAP,
|
||||
SYSTEM_PROMPT,
|
||||
)
|
||||
from backend.apps.agents.models import AgentSession, ApprovalRequest, Message
|
||||
from backend.apps.agents.ws_manager import ws_manager
|
||||
from backend.apps.agents.core.models import AgentSession, ApprovalRequest, Message
|
||||
from backend.apps.agents.core.ws_manager import ws_manager
|
||||
from backend.apps.tools_lib.tools_lib import load_builtin_permissions
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from datetime import datetime
|
||||
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
from backend.apps.service.client import sync as _sync
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import json
|
||||
import logging
|
||||
from fastapi import WebSocket
|
||||
|
||||
from backend.apps.agents.seq_log import TERMINAL_STATUSES, seq_log
|
||||
from backend.apps.agents.core.seq_log import TERMINAL_STATUSES, seq_log
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
|
||||
|
||||
def _sessions_dir() -> str:
|
||||
|
||||
@@ -338,7 +338,7 @@ async def generate_name(dashboard_id: str):
|
||||
system=system,
|
||||
messages=[{"role": "user", "content": user_content}],
|
||||
)
|
||||
from backend.apps.agents.aux_llm import _safe_resp_text
|
||||
from backend.apps.agents.core.aux_llm import _safe_resp_text
|
||||
generated = _safe_resp_text(resp).strip().strip('"\'')
|
||||
if generated:
|
||||
fallback = generated
|
||||
|
||||
@@ -39,7 +39,7 @@ NINE_ROUTER_V1 = f"{NINE_ROUTER_URL}/v1"
|
||||
#
|
||||
# Note: 0.3.60-0.4.20 ALL emit `max_tokens` (not max_completion_tokens)
|
||||
# when translating Anthropic→OpenAI, which OpenAI's GPT-5 family rejects.
|
||||
# The fix lives in our /api/openai-passthrough proxy; see openai_passthrough.py
|
||||
# The fix lives in our /api/openai-passthrough proxy; see core/openai_passthrough.py
|
||||
# and sync_openai_api_key for how the translation lane is rerouted via an
|
||||
# `openai-compatible` provider-node that honors `baseUrl`.
|
||||
NINE_ROUTER_NPM_VERSION = "0.3.60"
|
||||
|
||||
@@ -610,7 +610,7 @@ async def vibe_code(body: VibeCodeRequest):
|
||||
system=VIBE_CODE_SYSTEM_PROMPT,
|
||||
messages=[{"role": "user", "content": user_message}],
|
||||
)
|
||||
from backend.apps.agents.aux_llm import _safe_resp_text
|
||||
from backend.apps.agents.core.aux_llm import _safe_resp_text
|
||||
raw = _safe_resp_text(resp).strip()
|
||||
if not raw:
|
||||
return {
|
||||
|
||||
+6
-6
@@ -18,7 +18,7 @@ from backend.apps.oauth_state import (
|
||||
from backend.config.Apps import MainApp
|
||||
from backend.apps.health.health import health
|
||||
from backend.apps.agents.agents import agents
|
||||
from backend.apps.agents.ws_manager import ws_manager
|
||||
from backend.apps.agents.core.ws_manager import ws_manager
|
||||
from backend.apps.skills.skills import skills
|
||||
from backend.apps.tools_lib.tools_lib import tools_lib
|
||||
from backend.apps.modes.modes import modes
|
||||
@@ -175,7 +175,7 @@ async def websocket_session(websocket: WebSocket, session_id: str):
|
||||
last_seq = int(payload.get("last_seq") or 0)
|
||||
connection_uuid = payload.get("connection_uuid") or ""
|
||||
ack = await ws_manager.replay_to(session_id, websocket, last_seq)
|
||||
from backend.apps.agents.seq_log import seq_log as _sl
|
||||
from backend.apps.agents.core.seq_log import seq_log as _sl
|
||||
await websocket.send_text(json.dumps({
|
||||
"event": "server:hello",
|
||||
"session_id": session_id,
|
||||
@@ -636,7 +636,7 @@ async def mcp_meta(action: str, request: Request):
|
||||
if session.sdk_session_id:
|
||||
session.needs_fresh_session = True
|
||||
try:
|
||||
from backend.apps.agents.ws_manager import ws_manager as _ws
|
||||
from backend.apps.agents.core.ws_manager import ws_manager as _ws
|
||||
await _ws.send_to_session(parent_session_id, "agent:status", {
|
||||
"session_id": parent_session_id,
|
||||
"status": session.status,
|
||||
@@ -703,7 +703,7 @@ async def session_compact(session_id: str):
|
||||
options and ships the compacted prefix.
|
||||
"""
|
||||
from backend.apps.agents.agent_manager import agent_manager
|
||||
from backend.apps.agents.ws_manager import ws_manager as _ws
|
||||
from backend.apps.agents.core.ws_manager import ws_manager as _ws
|
||||
session = agent_manager.sessions.get(session_id)
|
||||
if not session:
|
||||
return JSONResponse({"error": "session not found"}, status_code=404)
|
||||
@@ -721,8 +721,8 @@ async def session_compact(session_id: str):
|
||||
async def session_clear(session_id: str):
|
||||
"""Wipe the session's UI history AND its SDK convo state (/clear slash cmd, Reset history button)."""
|
||||
from backend.apps.agents.agent_manager import agent_manager
|
||||
from backend.apps.agents.ws_manager import ws_manager as _ws
|
||||
from backend.apps.agents.models import MessageBranch
|
||||
from backend.apps.agents.core.ws_manager import ws_manager as _ws
|
||||
from backend.apps.agents.core.models import MessageBranch
|
||||
session = agent_manager.sessions.get(session_id)
|
||||
if not session:
|
||||
return JSONResponse({"error": "session not found"}, status_code=404)
|
||||
|
||||
@@ -56,14 +56,14 @@ os.makedirs(_SEQ_DIR, exist_ok=True)
|
||||
@pytest.fixture(autouse=True)
|
||||
def _patch_persist_dir():
|
||||
"""Force the seq_log to use our tmp dir so we can assert on disk state."""
|
||||
from backend.apps.agents import seq_log as sl_mod
|
||||
from backend.apps.agents.core import seq_log as sl_mod
|
||||
|
||||
# Rebuild the singleton with our test dir.
|
||||
new_store = sl_mod.SeqLogStore(persist_dir=_SEQ_DIR)
|
||||
monkey = patch.object(sl_mod, "seq_log", new_store)
|
||||
monkey.start()
|
||||
# Also patch the symbol re-exported into ws_manager's import scope.
|
||||
from backend.apps.agents import ws_manager as wm_mod
|
||||
from backend.apps.agents.core import ws_manager as wm_mod
|
||||
wm_monkey = patch.object(wm_mod, "seq_log", new_store)
|
||||
wm_monkey.start()
|
||||
yield new_store
|
||||
@@ -84,7 +84,7 @@ def _build_app(seq_log):
|
||||
so the test thread can drive event emission through the same
|
||||
event loop as the WS handler, avoiding the cross-loop hazards
|
||||
of `asyncio.run()` mid-test."""
|
||||
from backend.apps.agents.ws_manager import ws_manager
|
||||
from backend.apps.agents.core.ws_manager import ws_manager
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@@ -150,7 +150,7 @@ async def _emit_run(session_id: str, n_events: int, terminate: str | None = "com
|
||||
fanning out the broadcast across multiple coroutines. The seq
|
||||
log must still order them strictly.
|
||||
"""
|
||||
from backend.apps.agents.ws_manager import ws_manager
|
||||
from backend.apps.agents.core.ws_manager import ws_manager
|
||||
|
||||
async def emit_chunk(start: int, count: int):
|
||||
for i in range(count):
|
||||
@@ -518,7 +518,7 @@ def test_disconnect_does_not_touch_agent_task(_patch_persist_dir):
|
||||
this test will catch it. We import agent_manager lazily so the
|
||||
`tasks` dict starts empty; we register a sentinel task and confirm
|
||||
disconnect_session doesn't poke it."""
|
||||
from backend.apps.agents.ws_manager import ws_manager
|
||||
from backend.apps.agents.core.ws_manager import ws_manager
|
||||
# Insert a real Future into a parallel registry to mimic
|
||||
# `agent_manager.tasks[session_id]` and confirm ws_manager
|
||||
# never reaches into it. We don't import agent_manager (heavy);
|
||||
|
||||
@@ -34,7 +34,7 @@ os.environ.setdefault("OPENSWARM_DATA_DIR", _TMPROOT)
|
||||
|
||||
def test_message_round_trips_client_id():
|
||||
"""The new field must default to None and survive model_dump."""
|
||||
from backend.apps.agents.models import Message
|
||||
from backend.apps.agents.core.models import Message
|
||||
|
||||
m = Message(role="user", content="hi")
|
||||
assert m.client_message_id is None
|
||||
@@ -52,7 +52,7 @@ def test_message_round_trips_client_id():
|
||||
|
||||
def test_message_legacy_payload_without_client_id():
|
||||
"""Older session JSON files won't have the field, must still load."""
|
||||
from backend.apps.agents.models import Message
|
||||
from backend.apps.agents.core.models import Message
|
||||
|
||||
legacy = {
|
||||
"id": "abc",
|
||||
@@ -68,7 +68,7 @@ def test_message_legacy_payload_without_client_id():
|
||||
def test_client_message_id_collision_resistance():
|
||||
"""Many random client_message_ids must remain distinct values
|
||||
after serialization. Smoke-tests the field preservation in bulk."""
|
||||
from backend.apps.agents.models import Message
|
||||
from backend.apps.agents.core.models import Message
|
||||
|
||||
seen: set[str] = set()
|
||||
for _ in range(500):
|
||||
@@ -267,7 +267,7 @@ def test_notes_stress_many_round_trips():
|
||||
async def test_concurrent_send_message_unique_client_ids():
|
||||
"""100 parallel Message constructions with unique client_message_ids
|
||||
must round-trip independently, no cross-talk on the dataclass."""
|
||||
from backend.apps.agents.models import Message
|
||||
from backend.apps.agents.core.models import Message
|
||||
|
||||
async def make_one(i: int):
|
||||
cmi = f"opt-{i}-{random.randint(0, 1_000_000)}"
|
||||
|
||||
@@ -136,7 +136,7 @@ def last_sync(kind: str) -> dict:
|
||||
|
||||
# Import application modules (after fixtures are wired).
|
||||
from backend.apps.service.client import record
|
||||
from backend.apps.agents.models import AgentConfig, AgentSession, Message, ApprovalRequest
|
||||
from backend.apps.agents.core.models import AgentConfig, AgentSession, Message, ApprovalRequest
|
||||
from backend.apps.agents.agent_manager import AgentManager
|
||||
|
||||
|
||||
|
||||
@@ -220,14 +220,14 @@ async def test_gate_stress_random_activations():
|
||||
|
||||
def test_needs_fresh_session_field_default_false():
|
||||
"""Brand-new sessions must default needs_fresh_session=False."""
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
s = AgentSession(id="x", name="t", model="sonnet", mode="agent")
|
||||
assert s.needs_fresh_session is False
|
||||
|
||||
|
||||
def test_needs_fresh_session_serializes_round_trip():
|
||||
"""Pydantic round-trip must preserve the flag for session.json persistence."""
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
s = AgentSession(id="x", name="t", model="sonnet", mode="agent")
|
||||
s.needs_fresh_session = True
|
||||
s.sdk_session_id = "claude-session-abc-123"
|
||||
@@ -240,7 +240,7 @@ def test_needs_fresh_session_serializes_round_trip():
|
||||
|
||||
def test_legacy_session_json_loads_without_field():
|
||||
"""Old session JSONs predate the field, Pydantic must fill in default."""
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
legacy = {
|
||||
"id": "old", "name": "legacy", "model": "sonnet", "mode": "agent",
|
||||
"status": "completed", "messages": [],
|
||||
@@ -255,7 +255,7 @@ def test_legacy_session_json_loads_without_field():
|
||||
|
||||
def test_mcp_activate_sets_fresh_session_when_history_exists():
|
||||
"""The gate logic at main.py: if sdk_session_id exists, set needs_fresh_session=True."""
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
# Mid-session: sdk already locked in
|
||||
s = AgentSession(id="mid", name="t", model="sonnet", mode="agent")
|
||||
s.sdk_session_id = "claude-session-existing"
|
||||
@@ -267,7 +267,7 @@ def test_mcp_activate_sets_fresh_session_when_history_exists():
|
||||
|
||||
def test_mcp_activate_skips_fresh_session_on_first_turn():
|
||||
"""First-turn activation: no sdk_session_id yet, so needs_fresh_session stays False."""
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
s = AgentSession(id="fresh", name="t", model="sonnet", mode="agent")
|
||||
# No sdk_session_id yet
|
||||
if s.sdk_session_id:
|
||||
@@ -277,7 +277,7 @@ def test_mcp_activate_skips_fresh_session_on_first_turn():
|
||||
|
||||
def test_active_mcps_append_idempotent():
|
||||
"""Activating the same server twice doesn't dupe."""
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
s = AgentSession(id="x", name="t", model="sonnet", mode="agent")
|
||||
s.active_mcps.append("gmail")
|
||||
if "gmail" not in s.active_mcps:
|
||||
@@ -292,7 +292,7 @@ def test_active_mcps_append_idempotent():
|
||||
|
||||
def test_message_no_ghost_fields():
|
||||
"""answer_tokens + thought_signature must NOT be Message attributes anymore."""
|
||||
from backend.apps.agents.models import Message
|
||||
from backend.apps.agents.core.models import Message
|
||||
m = Message(role="thinking", content="x")
|
||||
dumped = m.model_dump(mode="json")
|
||||
assert "answer_tokens" not in dumped
|
||||
@@ -301,7 +301,7 @@ def test_message_no_ghost_fields():
|
||||
|
||||
def test_message_legacy_payload_with_ghost_fields_still_loads():
|
||||
"""Old session JSONs may carry the deleted fields, Pydantic must ignore them."""
|
||||
from backend.apps.agents.models import Message
|
||||
from backend.apps.agents.core.models import Message
|
||||
legacy = {
|
||||
"id": "m1",
|
||||
"role": "thinking",
|
||||
@@ -323,7 +323,7 @@ def test_message_legacy_payload_with_ghost_fields_still_loads():
|
||||
|
||||
def test_message_kept_fields():
|
||||
"""Verify the live fields remain on the model."""
|
||||
from backend.apps.agents.models import Message
|
||||
from backend.apps.agents.core.models import Message
|
||||
m = Message(
|
||||
role="thinking",
|
||||
content="x",
|
||||
@@ -340,7 +340,7 @@ def test_message_kept_fields():
|
||||
|
||||
def test_message_round_trip_50_iterations():
|
||||
"""Stress: 50 randomized message round-trips."""
|
||||
from backend.apps.agents.models import Message
|
||||
from backend.apps.agents.core.models import Message
|
||||
for _ in range(50):
|
||||
roles = ["user", "assistant", "tool_call", "tool_result", "system", "thinking"]
|
||||
m = Message(
|
||||
@@ -650,7 +650,7 @@ def test_mcp_activate_handler_unknown_server():
|
||||
|
||||
def test_active_mcps_persistence_on_session():
|
||||
"""active_mcps survives session.model_dump() round-trip, critical for resume."""
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
s = AgentSession(id="x", name="t", model="sonnet", mode="agent")
|
||||
s.active_mcps = ["gmail", "slack"]
|
||||
dumped = json.dumps(s.model_dump(mode="json"))
|
||||
@@ -719,7 +719,7 @@ def test_chat_mode_not_in_builtins():
|
||||
|
||||
def test_active_mcps_default_factory_creates_new_list():
|
||||
"""Defaults must use Field(default_factory=list), not [], to avoid shared mutation."""
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
s1 = AgentSession(id="a", name="a", model="sonnet", mode="agent")
|
||||
s2 = AgentSession(id="b", name="b", model="sonnet", mode="agent")
|
||||
s1.active_mcps.append("gmail")
|
||||
@@ -758,14 +758,14 @@ async def test_concurrent_gate_calls_isolated():
|
||||
|
||||
|
||||
def test_pending_continuation_default_false():
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
s = AgentSession(id="x", name="t", model="sonnet", mode="agent")
|
||||
assert s.pending_continuation is False
|
||||
assert s.pending_continuation_prompt is None
|
||||
|
||||
|
||||
def test_pending_continuation_serializes():
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
s = AgentSession(id="x", name="t", model="sonnet", mode="agent")
|
||||
s.pending_continuation = True
|
||||
s.pending_continuation_prompt = "[mcp:auto-continue] retry now"
|
||||
@@ -777,7 +777,7 @@ def test_pending_continuation_serializes():
|
||||
|
||||
def test_compact_threshold_default():
|
||||
"""compact_threshold_pct default of 0.65, drift here breaks Phase 2 compaction."""
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
s = AgentSession(id="x", name="t", model="sonnet", mode="agent")
|
||||
assert s.compact_threshold_pct == 0.65
|
||||
assert s.context_soft_cap_pct == 0.90
|
||||
@@ -999,7 +999,7 @@ def test_apply_context_window_overwrites_default_for_opus_4_7():
|
||||
dataclass default for every model. _apply_context_window must pull
|
||||
the real 1M value from the registry for opus-4-7 / sonnet so the
|
||||
soft-cap trim and the % meter both reflect the real model cap."""
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
from backend.apps.agents.agent_manager import _apply_context_window
|
||||
s = AgentSession(id="x", name="t", model="opus-4-7", mode="agent")
|
||||
assert s.context_window == 200_000
|
||||
@@ -1017,7 +1017,7 @@ def test_apply_context_window_silent_on_unknown_model():
|
||||
"""Bad lookup must NEVER raise; sessions with unknown/custom models
|
||||
that aren't in the registry fall back to the 128k registry default
|
||||
without breaking session creation."""
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
from backend.apps.agents.agent_manager import _apply_context_window
|
||||
s = AgentSession(id="x", name="t", model="nonexistent-model-xyz", mode="agent")
|
||||
_apply_context_window(s)
|
||||
@@ -1912,7 +1912,7 @@ def test_apply_context_window_respects_custom_provider_value():
|
||||
"""Custom OpenAI-compatible models supply their own context_window
|
||||
via settings.custom_providers. _apply_context_window must look them
|
||||
up the same way get_context_window does."""
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
from backend.apps.agents.agent_manager import _apply_context_window
|
||||
from backend.apps.settings.models import AppSettings, CustomProvider
|
||||
s = AgentSession(id="x", name="t", provider="custom", model="custom/ollama/qwen2.5:7b", mode="agent")
|
||||
@@ -2725,7 +2725,7 @@ def test_web_fetch_tool_has_name_and_schema():
|
||||
|
||||
|
||||
def test_tool_group_meta_round_trip():
|
||||
from backend.apps.agents.models import ToolGroupMeta, AgentSession
|
||||
from backend.apps.agents.core.models import ToolGroupMeta, AgentSession
|
||||
s = AgentSession(id="x", name="t", model="sonnet", mode="agent")
|
||||
s.tool_group_meta["g1"] = ToolGroupMeta(id="g1", name="Reading files", svg="<svg/>", is_refined=True)
|
||||
d = s.model_dump(mode="json")
|
||||
@@ -2735,7 +2735,7 @@ def test_tool_group_meta_round_trip():
|
||||
|
||||
|
||||
def test_tool_group_meta_default_is_refined_false():
|
||||
from backend.apps.agents.models import ToolGroupMeta
|
||||
from backend.apps.agents.core.models import ToolGroupMeta
|
||||
m = ToolGroupMeta(id="g", name="x")
|
||||
assert m.is_refined is False
|
||||
|
||||
@@ -2746,14 +2746,14 @@ def test_tool_group_meta_default_is_refined_false():
|
||||
|
||||
|
||||
def test_session_has_main_branch_by_default():
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
s = AgentSession(id="x", name="t", model="sonnet", mode="agent")
|
||||
assert "main" in s.branches
|
||||
assert s.active_branch_id == "main"
|
||||
|
||||
|
||||
def test_branch_serialization():
|
||||
from backend.apps.agents.models import AgentSession, MessageBranch
|
||||
from backend.apps.agents.core.models import AgentSession, MessageBranch
|
||||
s = AgentSession(id="x", name="t", model="sonnet", mode="agent")
|
||||
s.branches["alt"] = MessageBranch(id="alt", parent_branch_id="main", fork_point_message_id="msg-1")
|
||||
d = s.model_dump(mode="json")
|
||||
@@ -2777,7 +2777,7 @@ async def test_e2e_session_lifecycle_with_mcp_activation():
|
||||
4. Persist & re-load, state survives
|
||||
"""
|
||||
from backend.apps.agents.agent_manager import AgentManager
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
fake_tools = [_fake_tool("Gmail"), _fake_tool("Slack")]
|
||||
with patch("backend.apps.agents.agent_manager.load_all_tools", return_value=fake_tools), \
|
||||
patch("backend.apps.agents.agent_manager.refresh_google_token", new=AsyncMock(return_value=True)):
|
||||
@@ -2838,14 +2838,14 @@ async def test_e2e_50_random_activation_sequences():
|
||||
def test_session_agent_active_ms_default_zero_for_legacy():
|
||||
"""A session loaded from JSON without `agent_active_ms` deserializes
|
||||
cleanly with default 0 (not None, not missing-key crash)."""
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
s = AgentSession(name="legacy", model="sonnet", mode="agent")
|
||||
assert s.agent_active_ms == 0
|
||||
assert s.time_per_model == {}
|
||||
|
||||
|
||||
def test_session_agent_active_ms_round_trip():
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
s = AgentSession(name="t", model="sonnet", mode="agent",
|
||||
agent_active_ms=12345, time_per_model={"haiku": 1000, "sonnet": 11345})
|
||||
d = s.model_dump(mode="json")
|
||||
@@ -2857,7 +2857,7 @@ def test_session_agent_active_ms_round_trip():
|
||||
def test_session_agent_active_ms_accumulates_via_dict_update():
|
||||
"""Simulates two turns adding to the bucket, the production accumulator
|
||||
pattern in agent_manager._on_result."""
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
s = AgentSession(name="t", model="sonnet", mode="agent")
|
||||
s.agent_active_ms = (s.agent_active_ms or 0) + 1500
|
||||
s.time_per_model[s.model] = int(s.time_per_model.get(s.model, 0)) + 1500
|
||||
@@ -2870,7 +2870,7 @@ def test_session_agent_active_ms_accumulates_via_dict_update():
|
||||
def test_session_time_per_model_records_switch():
|
||||
"""Simulates a model switch mid-session, each model accumulates its
|
||||
own bucket."""
|
||||
from backend.apps.agents.models import AgentSession
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
s = AgentSession(name="t", model="haiku", mode="agent")
|
||||
# Turn 1 on haiku
|
||||
s.time_per_model[s.model] = int(s.time_per_model.get(s.model, 0)) + 1200
|
||||
|
||||
Reference in New Issue
Block a user