mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-11 12:17:45 +02:00
[haik]: backend cleanup: remove unused imports (anthropic, json, time, sys, HTTPException, ws_manager, etc.), drop unused context params from tool hooks and execute methods, delete dead _resolve_model helper and entire thinking.py module, prune multi-provider subagent model fallback block in agent_manager, fix session_store to import SESSIONS_DIR directly from config.paths, re-home compute_tiers/compute_billing_kind imports from registry to pricing in agents.py, add google-workspace-mcp==2.0.1 to requirements-dev
This commit is contained in:
@@ -25,10 +25,7 @@ from backend.apps.tools_lib.tools_lib import (
|
||||
refresh_hubspot_token,
|
||||
save_trusted_sensitive_paths,
|
||||
)
|
||||
from backend.config.paths import SESSIONS_DIR
|
||||
from backend.apps.agents.core.error_classify import (
|
||||
_NON_TRANSIENT_PATTERNS,
|
||||
_TRANSIENT_CAPACITY_PATTERNS,
|
||||
_is_auth_error,
|
||||
_is_free_trial_exhausted,
|
||||
_is_long_context_error,
|
||||
@@ -316,8 +313,6 @@ class AgentManager:
|
||||
_apply_context_window(session, global_settings)
|
||||
self.sessions[session_id] = session
|
||||
|
||||
from backend.apps.service.version import APP_VERSION
|
||||
|
||||
await ws_manager.send_to_session(session_id, "agent:status", {
|
||||
"session_id": session_id,
|
||||
"status": "running",
|
||||
@@ -750,7 +745,7 @@ class AgentManager:
|
||||
})
|
||||
return decision
|
||||
|
||||
async def can_use_tool(tool_name, input_data, context):
|
||||
async def can_use_tool(tool_name, input_data):
|
||||
sensitive_pattern: str | None = None
|
||||
if tool_name != "AskUserQuestion":
|
||||
policy, sensitive_pattern = _maybe_override_policy(
|
||||
@@ -772,7 +767,7 @@ class AgentManager:
|
||||
|
||||
tool_start_times: dict[str, float] = {}
|
||||
|
||||
async def pre_tool_hook(input_data, tool_use_id, context):
|
||||
async def pre_tool_hook(input_data, tool_use_id):
|
||||
tool_name = input_data.get("tool_name", "")
|
||||
hook_event = input_data.get("hook_event_name", "PreToolUse")
|
||||
|
||||
@@ -815,7 +810,7 @@ class AgentManager:
|
||||
tool_start_times[tool_use_id] = time.time()
|
||||
return {}
|
||||
|
||||
async def post_tool_hook(input_data, tool_use_id, context):
|
||||
async def post_tool_hook(input_data, tool_use_id):
|
||||
elapsed_ms = None
|
||||
if tool_use_id and tool_use_id in tool_start_times:
|
||||
elapsed_ms = int((time.time() - tool_start_times.pop(tool_use_id)) * 1000)
|
||||
@@ -1645,29 +1640,11 @@ class AgentManager:
|
||||
}
|
||||
# Pin subagent ids to whichever lane the user has, else CLI's
|
||||
# default Haiku 4.5 hits 9Router with no Claude route and 401s.
|
||||
try:
|
||||
_sub_conns = _conns # reuse list fetched above
|
||||
except NameError:
|
||||
_sub_conns = []
|
||||
_active = {c.get("provider") for c in _sub_conns
|
||||
if isinstance(c, dict) and c.get("isActive")}
|
||||
_sub_model = None
|
||||
_small_model = None
|
||||
if global_settings.anthropic_api_key:
|
||||
_sub_model = "claude-sonnet-4-6"
|
||||
_small_model = "claude-haiku-4-5-20251001"
|
||||
elif "claude" in _active or "anthropic" in _active:
|
||||
_sub_model = "cc/claude-sonnet-4-6"
|
||||
_small_model = "cc/claude-haiku-4-5-20251001"
|
||||
elif "antigravity" in _active:
|
||||
_sub_model = "ag/gemini-3-flash"
|
||||
_small_model = "ag/gemini-3-flash"
|
||||
elif "gemini-cli" in _active:
|
||||
_sub_model = "gc/gemini-2.5-flash"
|
||||
_small_model = "gc/gemini-2.5-flash"
|
||||
elif "codex" in _active:
|
||||
_sub_model = "cx/gpt-5.4-mini"
|
||||
_small_model = "cx/gpt-5.4-mini"
|
||||
if _sub_model:
|
||||
env["CLAUDE_CODE_SUBAGENT_MODEL"] = _sub_model
|
||||
if _small_model:
|
||||
@@ -3281,7 +3258,6 @@ class AgentManager:
|
||||
prompt: str,
|
||||
mode: str | None = None,
|
||||
model: str | None = None,
|
||||
provider: str | None = None,
|
||||
images: list | None = None,
|
||||
context_paths: list | None = None,
|
||||
forced_tools: list[str] | None = None,
|
||||
@@ -4080,14 +4056,6 @@ class AgentManager:
|
||||
session = AgentSession(**data)
|
||||
_apply_context_window(session)
|
||||
|
||||
hours_since_closed = 0
|
||||
if data.get("closed_at"):
|
||||
try:
|
||||
closed = datetime.fromisoformat(data["closed_at"][:19])
|
||||
hours_since_closed = round((datetime.now() - closed).total_seconds() / 3600, 1)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
session.closed_at = None
|
||||
self.sessions[session_id] = session
|
||||
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
from backend.config.Apps import SubApp
|
||||
from backend.apps.agents.agent_manager import agent_manager
|
||||
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
|
||||
from fastapi import HTTPException
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -547,7 +544,9 @@ async def list_models():
|
||||
def _serialize(models: list[dict]) -> list[dict]:
|
||||
# Tiers describe the model; billing_kind describes the wallet. Pricing shown only for paid.
|
||||
from backend.apps.agents.providers.registry import (
|
||||
COST_PER_1M_TOKENS,
|
||||
COST_PER_1M_TOKENS
|
||||
)
|
||||
from backend.apps.agents.providers.pricing import (
|
||||
compute_tiers,
|
||||
compute_billing_kind,
|
||||
)
|
||||
@@ -645,6 +644,8 @@ async def list_models():
|
||||
has_openrouter_key = bool(getattr(settings, "openrouter_api_key", None))
|
||||
from backend.apps.agents.providers.registry import (
|
||||
COST_PER_1M_TOKENS as _CPM,
|
||||
)
|
||||
from backend.apps.agents.providers.pricing import (
|
||||
compute_tiers as _ct_native,
|
||||
compute_billing_kind as _cbk_native,
|
||||
)
|
||||
|
||||
@@ -14,8 +14,6 @@ import time
|
||||
from datetime import datetime
|
||||
from uuid import uuid4
|
||||
|
||||
import anthropic
|
||||
|
||||
from backend.apps.agents.browser import browser_history
|
||||
from backend.apps.agents.browser.browser_history import (
|
||||
_MAX_HISTORY_MESSAGES,
|
||||
@@ -71,9 +69,7 @@ from backend.apps.agents.browser import browser_schema
|
||||
from backend.apps.agents.browser.browser_schema import (
|
||||
_ACTION_TOOLS_REQUIRING_REPORT,
|
||||
ACTION_MAP,
|
||||
BROWSER_TOOLS_SCHEMA,
|
||||
MAX_TURNS,
|
||||
MODEL_MAP,
|
||||
SYSTEM_PROMPT,
|
||||
)
|
||||
from backend.apps.agents.core.models import AgentSession, ApprovalRequest, Message
|
||||
|
||||
@@ -7,8 +7,8 @@ from backend.config.json_store import read_json_or_none, atomic_write_json
|
||||
def _sessions_dir() -> str:
|
||||
# Resolve live so test patches on either the paths module or the
|
||||
# agent_manager facade re-export land on the same directory.
|
||||
from backend.apps.agents import agent_manager
|
||||
return agent_manager.SESSIONS_DIR
|
||||
from backend.config.paths import SESSIONS_DIR
|
||||
return SESSIONS_DIR
|
||||
|
||||
|
||||
def _save_session(session_id: str, doc_data: dict):
|
||||
|
||||
@@ -13,17 +13,7 @@ from typing import Any, TYPE_CHECKING
|
||||
|
||||
from .openrouter import (
|
||||
_OPENROUTER_VALUE_PREFIX,
|
||||
fetch_openrouter_models,
|
||||
get_direct_pricing,
|
||||
get_openrouter_pricing,
|
||||
invalidate_openrouter_cache,
|
||||
)
|
||||
from .pricing import (
|
||||
compute_billing_kind,
|
||||
compute_tiers,
|
||||
_heuristic_tiers,
|
||||
)
|
||||
from .thinking import thinking_params_for
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from backend.apps.settings.models import AppSettings
|
||||
@@ -372,7 +362,7 @@ async def resolve_aux_model(
|
||||
)
|
||||
|
||||
|
||||
def get_context_window(provider: str, model: str, settings: AppSettings | None = None) -> int:
|
||||
def get_context_window(model: str, settings: AppSettings | None = None) -> int:
|
||||
"""Look up context window for any model."""
|
||||
# Check built-in models first
|
||||
for models in BUILTIN_MODELS.values():
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
"""Thinking-level translation. Provider-agnostic off/low/medium/high/auto → per-API params."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def thinking_params_for(api: str, level: str, model_id: str = "") -> dict | None:
|
||||
"""Translate a provider-agnostic thinking level to per-provider API params.
|
||||
|
||||
Args:
|
||||
api: "anthropic" | "codex" | "gemini-cli"
|
||||
level: "off" | "low" | "medium" | "high" | "auto"
|
||||
model_id: optional, used to pick adaptive vs legacy for Claude
|
||||
|
||||
Returns a dict to merge into request params, or None for "use defaults".
|
||||
"""
|
||||
if level == "auto":
|
||||
if api == "anthropic":
|
||||
return {"thinking": {"type": "adaptive"}}
|
||||
return None
|
||||
|
||||
if level == "off":
|
||||
if api == "anthropic":
|
||||
# Fable 5 400s on an explicit thinking:disabled; omit the param to
|
||||
# turn thinking off (off is its default). Other Claude models accept it.
|
||||
if "fable" in model_id:
|
||||
return None
|
||||
return {"thinking": {"type": "disabled"}}
|
||||
if api == "codex":
|
||||
return {"reasoning": {"effort": "none"}}
|
||||
# Gemini: budget=0 actually disables reasoning. Anything else still
|
||||
# emits thoughtSignatures and 400s the next tool turn.
|
||||
if api == "gemini-cli":
|
||||
return {"thinkingConfig": {"thinkingBudget": 0}}
|
||||
return None
|
||||
|
||||
if api == "anthropic":
|
||||
return {"thinking": {"type": "adaptive"}}
|
||||
|
||||
if api == "codex":
|
||||
effort_map = {"low": "low", "medium": "medium", "high": "high"}
|
||||
return {"reasoning": {"effort": effort_map[level]}}
|
||||
|
||||
if api == "gemini-cli":
|
||||
level_map = {"low": "LOW", "medium": "MEDIUM", "high": "HIGH"}
|
||||
return {"thinkingConfig": {"thinkingLevel": level_map[level]}}
|
||||
|
||||
return None
|
||||
@@ -96,7 +96,6 @@ def _rewrite_document_to_openai_file(parsed: dict) -> None:
|
||||
msgs = parsed.get("messages") if isinstance(parsed, dict) else None
|
||||
if not isinstance(msgs, list):
|
||||
return
|
||||
counter = 0
|
||||
for m in msgs:
|
||||
content = m.get("content") if isinstance(m, dict) else None
|
||||
if not isinstance(content, list):
|
||||
|
||||
@@ -19,7 +19,6 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
import uuid
|
||||
from typing import AsyncIterator
|
||||
|
||||
|
||||
@@ -4,11 +4,10 @@ from __future__ import annotations
|
||||
|
||||
import html
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
from backend.apps.agents.tools.base import BaseTool, ToolContext
|
||||
from backend.apps.agents.tools.base import BaseTool
|
||||
from backend.apps.agents.tools.ssrf_guard import SSRFBlocked, safe_fetch
|
||||
|
||||
_HTTP_TIMEOUT = 30
|
||||
@@ -89,7 +88,7 @@ class WebSearchTool(BaseTool):
|
||||
"additionalProperties": False,
|
||||
}
|
||||
|
||||
async def execute(self, input_data: dict, context: ToolContext) -> list[dict]:
|
||||
async def execute(self, input_data: dict) -> list[dict]:
|
||||
query: str = input_data["query"]
|
||||
num_results: int = input_data.get("num_results", 5)
|
||||
|
||||
@@ -210,7 +209,7 @@ class WebFetchTool(BaseTool):
|
||||
"additionalProperties": False,
|
||||
}
|
||||
|
||||
async def execute(self, input_data: dict, context: ToolContext) -> list[dict]:
|
||||
async def execute(self, input_data: dict) -> list[dict]:
|
||||
url: str = input_data["url"]
|
||||
prompt: str | None = input_data.get("prompt")
|
||||
|
||||
|
||||
@@ -11,9 +11,6 @@ from backend.apps.dashboards.models import (
|
||||
DashboardCreate,
|
||||
DashboardUpdate,
|
||||
DashboardLayout,
|
||||
CardPosition,
|
||||
ViewCardPosition,
|
||||
BrowserCardPosition,
|
||||
)
|
||||
from fastapi import HTTPException
|
||||
|
||||
@@ -142,7 +139,6 @@ async def create_dashboard(body: DashboardCreate):
|
||||
@dashboards.router.post("/{dashboard_id}/seed-demo")
|
||||
async def seed_demo(dashboard_id: str):
|
||||
"""Create a pre-populated demo session for onboarding."""
|
||||
dashboard = _load(dashboard_id)
|
||||
|
||||
session_id = uuid4().hex
|
||||
now = datetime.now()
|
||||
|
||||
@@ -3,7 +3,7 @@ from contextlib import asynccontextmanager
|
||||
from fastapi.responses import PlainTextResponse
|
||||
from typeguard import typechecked
|
||||
import debug
|
||||
from fastapi import status, HTTPException
|
||||
from fastapi import status
|
||||
|
||||
@asynccontextmanager
|
||||
async def health_lifespan():
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import json
|
||||
import os
|
||||
import logging
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
@@ -90,7 +90,6 @@ def _find_9router_dir() -> str | None:
|
||||
_is_packaged = os.environ.get("OPENSWARM_PACKAGED") == "1"
|
||||
|
||||
if _is_packaged:
|
||||
import sys
|
||||
_resources = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
_candidate = os.path.join(_resources, "router")
|
||||
if os.path.isdir(_candidate):
|
||||
|
||||
@@ -20,11 +20,6 @@ MODEL_MAP = {
|
||||
"haiku": "claude-haiku-4-5-20251001",
|
||||
}
|
||||
|
||||
|
||||
def _resolve_model(short_name: str) -> str:
|
||||
return MODEL_MAP.get(short_name, short_name)
|
||||
|
||||
|
||||
def _get_anthropic_client(api_model: str | None = None):
|
||||
"""Create an AsyncAnthropic client using the API key from app settings.
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ from backend.apps.settings.settings import load_settings
|
||||
from backend.config.paths import OUTPUTS_DIR as DATA_DIR, OUTPUTS_WORKSPACE_DIR as WORKSPACE_DIR
|
||||
from backend.apps.outputs.html_inject import (
|
||||
MODEL_MAP,
|
||||
_resolve_model,
|
||||
_get_anthropic_client,
|
||||
_validate_against_schema,
|
||||
_build_data_injection,
|
||||
|
||||
@@ -19,3 +19,4 @@ pytest-asyncio==0.25.2
|
||||
# imports/locals to ruff (see linter/checks/vulture.py).
|
||||
vulture==2.16
|
||||
ruff==0.15.17
|
||||
google-workspace-mcp==2.0.1
|
||||
Reference in New Issue
Block a user