mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-17 18:25:42 +02:00
[eric] boot: anthropic and the sdk+mcp chain leave the import graph, api-ready median 2531 to 1843ms, hooks re-import at call time
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import re
|
||||
from typing import Optional, Tuple
|
||||
|
||||
import anthropic
|
||||
import httpx
|
||||
from typeguard import typechecked
|
||||
|
||||
@@ -207,9 +206,19 @@ def parse_retry_after(exc: BaseException, extra_text: str = "") -> int | None:
|
||||
# anthropic.APIConnectionError stringifies to the bare "Connection error.", so the patterns above
|
||||
# score it NON-transient and one network hiccup throws away a whole run (measured live, twice). A
|
||||
# transport failure is transient by construction, so classify by TYPE, which no rewording breaks.
|
||||
P_TRANSIENT_EXC_TYPES: Tuple[type, ...] = (
|
||||
anthropic.APIConnectionError, anthropic.InternalServerError, # APITimeoutError subclasses the first
|
||||
httpx.TransportError, ConnectionError, TimeoutError) # connect/read/pool timeouts, protocol errors
|
||||
# Built lazily: importing the anthropic SDK at module scope cost 224ms of every backend boot.
|
||||
p_transient_exc_types: Optional[Tuple[type, ...]] = None
|
||||
|
||||
|
||||
def p_get_transient_exc_types() -> Tuple[type, ...]:
|
||||
global p_transient_exc_types
|
||||
if p_transient_exc_types is None:
|
||||
import anthropic
|
||||
|
||||
p_transient_exc_types = (
|
||||
anthropic.APIConnectionError, anthropic.InternalServerError, # APITimeoutError subclasses the first
|
||||
httpx.TransportError, ConnectionError, TimeoutError) # connect/read/pool timeouts, protocol errors
|
||||
return p_transient_exc_types
|
||||
|
||||
|
||||
@typechecked
|
||||
@@ -222,7 +231,7 @@ def is_transient_capacity_error(exc: BaseException, extra_text: str = "") -> boo
|
||||
if is_context_overflow_error(exc, extra_text):
|
||||
return False
|
||||
# Ahead of the empty-string bail on purpose: what the exception IS doesn't depend on whether it bothered to say anything.
|
||||
if isinstance(exc, P_TRANSIENT_EXC_TYPES):
|
||||
if isinstance(exc, p_get_transient_exc_types()):
|
||||
return True
|
||||
if not combined:
|
||||
return False
|
||||
|
||||
@@ -11,7 +11,13 @@ import time
|
||||
from typing import Dict, Optional, Union
|
||||
|
||||
from typeguard import typechecked
|
||||
from claude_agent_sdk import PermissionResultAllow, PermissionResultDeny
|
||||
# Runtime aliases stay `object` so the 350ms claude_agent_sdk+mcp chain stays off the boot graph; the hook body re-imports the real classes at call time, when the SDK is already resident.
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from claude_agent_sdk import PermissionResultAllow, PermissionResultDeny
|
||||
else:
|
||||
PermissionResultAllow = PermissionResultDeny = object
|
||||
|
||||
from backend.apps.agents.core.ws_manager import ws_manager
|
||||
from backend.apps.settings.settings import load_settings
|
||||
@@ -36,6 +42,8 @@ logger = logging.getLogger(__name__)
|
||||
async def can_use_tool(
|
||||
ctx: HookContext, tool_name: str, input_data: object, context: object
|
||||
) -> Union[PermissionResultAllow, PermissionResultDeny]:
|
||||
from claude_agent_sdk import PermissionResultAllow, PermissionResultDeny
|
||||
|
||||
if is_claude_schedule_skill(tool_name, input_data):
|
||||
note_tool_used(ctx.session_id, tool_name, False)
|
||||
return PermissionResultDeny(
|
||||
|
||||
@@ -18,11 +18,13 @@ from backend.apps.agents.manager.streaming.upsert_message import upsert_message
|
||||
from backend.apps.agents.manager.streaming.PartialReply import PartialReply
|
||||
from backend.apps.agents.manager.streaming import thinking as thinking_mod
|
||||
|
||||
try:
|
||||
# The block types drive isinstance DISPATCH, so they must be real at runtime; imported inside the handler because by stream time the SDK is already resident (the turn's presence check imported it), keeping the 350ms sdk+mcp chain off the boot graph.
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from claude_agent_sdk import AssistantMessage
|
||||
from claude_agent_sdk.types import ThinkingBlock, TextBlock, ToolUseBlock
|
||||
except ImportError: # the SDK is optional at runtime (mock mode); keep this module importable
|
||||
AssistantMessage = ThinkingBlock = TextBlock = ToolUseBlock = object # type: ignore
|
||||
else:
|
||||
AssistantMessage = object
|
||||
|
||||
|
||||
@typechecked
|
||||
@@ -35,6 +37,8 @@ async def handle_assistant_message(
|
||||
live_partial: Dict[str, PartialReply],
|
||||
sessions: Dict[str, AgentSession],
|
||||
) -> None:
|
||||
from claude_agent_sdk.types import ThinkingBlock, TextBlock, ToolUseBlock
|
||||
|
||||
content_parts = []
|
||||
new_thinking_parts = []
|
||||
tool_uses = []
|
||||
|
||||
@@ -15,10 +15,13 @@ from backend.apps.agents.core.ws_manager import ws_manager
|
||||
from backend.apps.agents.manager.streaming.state import ThinkingState, TurnState
|
||||
from backend.apps.agents.manager.streaming import thinking as thinking_mod
|
||||
|
||||
try:
|
||||
# Annotation-only here (no isinstance dispatch), so the runtime symbol can stay `object` and the SDK chain stays off the boot import graph.
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from claude_agent_sdk import ResultMessage
|
||||
except ImportError: # the SDK is optional at runtime (mock mode); keep this module importable
|
||||
ResultMessage = object # type: ignore
|
||||
else:
|
||||
ResultMessage = object
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -15,10 +15,14 @@ from backend.apps.agents.core.ws_manager import ws_manager
|
||||
from backend.apps.agents.manager.streaming.state import ThinkingState, TurnState
|
||||
from backend.apps.agents.manager.streaming.PartialReply import PartialReply
|
||||
|
||||
try:
|
||||
# Runtime annotation stays `object` (the old ImportError fallback already admitted that); the real
|
||||
# type lives behind TYPE_CHECKING so importing this module stops paying the 350ms claude_agent_sdk+mcp chain at boot.
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from claude_agent_sdk.types import StreamEvent
|
||||
except ImportError: # the SDK is optional at runtime (mock mode); keep this module importable
|
||||
StreamEvent = object # type: ignore
|
||||
else:
|
||||
StreamEvent = object
|
||||
|
||||
|
||||
@typechecked
|
||||
|
||||
Reference in New Issue
Block a user