[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:
haikdc
2026-06-13 11:45:55 -07:00
parent 7fdf37f416
commit d89fb7d870
16 changed files with 17 additions and 123 deletions
+3 -35
View File
@@ -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
+6 -5
View File
@@ -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):
+1 -11
View File
@@ -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():
-47
View File
@@ -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
+3 -4
View File
@@ -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")
-4
View File
@@ -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()
+1 -1
View File
@@ -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
View File
@@ -1,4 +1,3 @@
import json
import os
import logging
from contextlib import asynccontextmanager
-1
View File
@@ -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):
-5
View File
@@ -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.
-1
View File
@@ -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,
+1
View File
@@ -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