mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-11 12:17:45 +02:00
[Haik]: ckpt, dashboards subapp now done fully. Also made the resolve_sdk_env which is used now both for normal agents and quick llm calls, also modded the agents subapp to properly make use of this helper, also modded the run_browser_agent to inheret this from its parent agent
This commit is contained in:
@@ -3,8 +3,8 @@ from uuid import uuid4
|
||||
from pydantic import BaseModel, Field
|
||||
from typeguard import typechecked
|
||||
|
||||
from backend.apps.agents.utils.comms_utils.classes.FutureBridge import FutureBridge
|
||||
from backend.apps.agents.utils.comms_utils.classes.FrontendBroadcaster import FrontendBroadcaster
|
||||
from backend.apps.agents.COMMS_MANAGER.classes.FutureBridge import FutureBridge
|
||||
from backend.apps.agents.COMMS_MANAGER.classes.FrontendBroadcaster import FrontendBroadcaster
|
||||
from backend.core.events.events import AnyEvent, ApprovalRequestEvent, EventCallback
|
||||
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@ from backend.core.tools.make_builtin_toolkit.make_builtin_toolkit import make_bu
|
||||
from backend.apps.agents.agent_utils.create_sdk_hooks import create_sdk_hooks
|
||||
from backend.apps.agents.agent_utils.build_search_text import build_search_text
|
||||
from backend.apps.agents.COMMS_MANAGER.COMMS_MANAGER import COMMS_MANAGER
|
||||
from backend.apps.settings.settings import load_settings
|
||||
from backend.core.llm.resolve_sdk_env import resolve_sdk_env
|
||||
from backend.ports import NINE_ROUTER_PORT
|
||||
from claude_agent_sdk import ClaudeAgentOptions
|
||||
from claude_agent_sdk.types import HookMatcher, McpServerConfig
|
||||
from backend.core.tools.shared_structs.Toolkit import Toolkit
|
||||
@@ -118,7 +121,15 @@ async def launch(body: LaunchBody) -> dict:
|
||||
|
||||
can_use_tool, pre_tool_hook, post_tool_hook = create_sdk_hooks(agent)
|
||||
|
||||
settings = load_settings()
|
||||
env = resolve_sdk_env(
|
||||
api_key=settings.anthropic_api_key,
|
||||
nine_router_port=NINE_ROUTER_PORT if not settings.anthropic_api_key else None,
|
||||
)
|
||||
|
||||
agent.config = ClaudeAgentOptions(
|
||||
env=env,
|
||||
model=body.model,
|
||||
system_prompt=system_prompt,
|
||||
max_turns=body.max_turns,
|
||||
mcp_servers=mcp_servers if mcp_servers else None,
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import json
|
||||
import os
|
||||
import logging
|
||||
from contextlib import asynccontextmanager
|
||||
from datetime import datetime
|
||||
from uuid import uuid4
|
||||
from pydantic import BaseModel
|
||||
|
||||
from backend.config.Apps import SubApp
|
||||
# from backend.apps.common.json_store import JsonStore
|
||||
from backend.core.db.PydanticStore import PydanticStore
|
||||
from backend.apps.dashboards.Dashboard import (
|
||||
Dashboard,
|
||||
@@ -15,16 +13,9 @@ from backend.apps.dashboards.Dashboard import (
|
||||
)
|
||||
from backend.apps.agents.agents import list_sessions, delete_session
|
||||
from backend.apps.settings.settings import load_settings
|
||||
from backend.core.llm.quick_llm_call import quick_llm_call
|
||||
from backend.apps.dashboards.generate_dashboard_name import generate_dashboard_name
|
||||
from backend.ports import NINE_ROUTER_PORT
|
||||
from typing import Optional
|
||||
|
||||
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# from backend.config.paths import DASHBOARDS_DIR as DATA_DIR, SESSIONS_DIR
|
||||
from backend.config.paths import DB_ROOT
|
||||
|
||||
|
||||
@@ -75,7 +66,6 @@ async def create_dashboard(body: DashboardCreate):
|
||||
# TODO: Maybe parse the output of list_sessions into actual Agent objects?
|
||||
@dashboards.router.post("/{dashboard_id}/generate-name")
|
||||
async def generate_name(dashboard_id: str):
|
||||
|
||||
dashboard = DASHBOARD_STORE.load(dashboard_id)
|
||||
|
||||
if not dashboard.auto_named and dashboard.name != "Untitled Dashboard":
|
||||
@@ -94,33 +84,18 @@ async def generate_name(dashboard_id: str):
|
||||
if not prompts:
|
||||
return {"name": dashboard.name, "auto_named": dashboard.auto_named}
|
||||
|
||||
settings = load_settings()
|
||||
fallback = prompts[0][:40]
|
||||
try:
|
||||
if len(prompts) == 1:
|
||||
system = "Generate a concise 2-5 word workspace name for a project based on this task. Return only the name, nothing else."
|
||||
user_content = prompts[0]
|
||||
else:
|
||||
system = "Generate a concise 2-5 word workspace name that captures the overall theme of these tasks. Return only the name, nothing else."
|
||||
user_content = "\n".join(f"- {p}" for p in prompts)
|
||||
|
||||
settings = load_settings()
|
||||
llm_kwargs = {}
|
||||
if settings.anthropic_api_key:
|
||||
llm_kwargs["api_key"] = settings.anthropic_api_key
|
||||
else:
|
||||
llm_kwargs["nine_router_port"] = NINE_ROUTER_PORT
|
||||
|
||||
generated = await quick_llm_call(
|
||||
system, user_content,
|
||||
model="claude-haiku-4-5-20251001",
|
||||
max_tokens=30,
|
||||
**llm_kwargs,
|
||||
generated = await generate_dashboard_name(
|
||||
prompts=prompts,
|
||||
api_key=settings.anthropic_api_key,
|
||||
nine_router_port=NINE_ROUTER_PORT,
|
||||
)
|
||||
generated = generated.strip('"\'')
|
||||
if generated:
|
||||
fallback = generated
|
||||
except Exception as e:
|
||||
logger.warning(f"Dashboard name generation failed, using fallback: {e}")
|
||||
print(f"[dashboards.generate_name] ERROR: Dashboard name generation failed, using fallback: {e}")
|
||||
|
||||
dashboard.name = fallback
|
||||
dashboard.auto_named = True
|
||||
@@ -166,7 +141,7 @@ async def delete_dashboard(dashboard_id: str):
|
||||
try:
|
||||
await delete_session(session["session_id"])
|
||||
except Exception:
|
||||
logger.warning(f"Failed to delete session {session.get('session_id')} during dashboard deletion")
|
||||
print(f"[dashboards.delete_dashboard] ERROR: Failed to delete session {session.get('session_id')} during dashboard deletion")
|
||||
|
||||
DASHBOARD_STORE.delete(dashboard_id)
|
||||
return {"ok": True}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
from typing import List, Optional
|
||||
from backend.core.llm.quick_llm_call import quick_llm_call
|
||||
from typeguard import typechecked
|
||||
|
||||
SINGLE_AGENT_SYSTEM_PROMPT = "Generate a concise 2-5 word workspace name for a project based on this task. Return only the name with no markdown formatting, nothing else."
|
||||
MULTI_AGENT_SYSTEM_PROMPT = "Generate a concise 2-5 word workspace name that captures the overall theme of these tasks. Return only the name with no markdown formatting, nothing else."
|
||||
|
||||
@typechecked
|
||||
async def generate_dashboard_name(
|
||||
prompts: List[str],
|
||||
api_key: Optional[str] = None,
|
||||
nine_router_port: Optional[int] = None,
|
||||
) -> str:
|
||||
"""Given user prompts from a dashboard's sessions, generate a short name via LLM."""
|
||||
if len(prompts) == 1:
|
||||
system_prompt = SINGLE_AGENT_SYSTEM_PROMPT
|
||||
user_prompt = prompts[0]
|
||||
else:
|
||||
system_prompt = MULTI_AGENT_SYSTEM_PROMPT
|
||||
user_prompt = "\n".join(f"- {p}" for p in prompts)
|
||||
|
||||
result = await quick_llm_call(
|
||||
api_key=api_key,
|
||||
nine_router_port=nine_router_port,
|
||||
system_prompt=system_prompt,
|
||||
user_prompt=user_prompt,
|
||||
model="claude-haiku-4-5-20251001",
|
||||
max_tokens=30,
|
||||
)
|
||||
return result.strip("\"'")
|
||||
@@ -1,20 +1,25 @@
|
||||
from backend.core.llm.get_llm_client import get_llm_client
|
||||
from backend.core.llm.resolve_sdk_env import resolve_sdk_env, SDK_ENV_DICT
|
||||
from typing import Optional
|
||||
from anthropic.types import Message as AnthropicMessage
|
||||
import anthropic
|
||||
|
||||
async def quick_llm_call(
|
||||
system: str,
|
||||
user_content: str,
|
||||
system_prompt: str,
|
||||
user_prompt: str,
|
||||
model: str,
|
||||
max_tokens: int,
|
||||
api_key: Optional[str] = None,
|
||||
nine_router_port: Optional[int] = None,
|
||||
) -> str:
|
||||
client = get_llm_client(api_key=api_key, nine_router_port=nine_router_port)
|
||||
env_dict: SDK_ENV_DICT = resolve_sdk_env(api_key=api_key, nine_router_port=nine_router_port)
|
||||
client = anthropic.AsyncAnthropic(
|
||||
api_key=env_dict["ANTHROPIC_API_KEY"],
|
||||
base_url=env_dict.get("ANTHROPIC_BASE_URL"),
|
||||
)
|
||||
resp: AnthropicMessage = await client.messages.create(
|
||||
model=model,
|
||||
max_tokens=max_tokens,
|
||||
system=system,
|
||||
messages=[{"role": "user", "content": user_content}],
|
||||
system=system_prompt,
|
||||
messages=[{"role": "user", "content": user_prompt}],
|
||||
)
|
||||
return resp.content[0].text.strip()
|
||||
@@ -1,7 +1,6 @@
|
||||
import anthropic
|
||||
import httpx
|
||||
from typeguard import typechecked
|
||||
from typing import Optional
|
||||
from typing import Optional, Literal, Dict
|
||||
from backend.core.generic_utils.assert_exactly_one_optional import assert_exactly_one_optional
|
||||
|
||||
@typechecked
|
||||
@@ -12,28 +11,23 @@ def p_check_9router(nine_router_port: int) -> bool:
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
SDK_ENV_DICT = Dict[Literal["ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL"], str]
|
||||
|
||||
@typechecked
|
||||
def get_llm_client(
|
||||
def resolve_sdk_env(
|
||||
api_key: Optional[str] = None,
|
||||
nine_router_port: Optional[int] = None,
|
||||
) -> anthropic.AsyncAnthropic:
|
||||
"""Build an AsyncAnthropic client.
|
||||
|
||||
Priority: API key → 9Router subscription
|
||||
"""
|
||||
) -> SDK_ENV_DICT:
|
||||
"""Resolve credentials into an env dict for ClaudeAgentOptions."""
|
||||
assert_exactly_one_optional([api_key, nine_router_port])
|
||||
|
||||
if api_key is not None:
|
||||
return anthropic.AsyncAnthropic(api_key=api_key)
|
||||
|
||||
elif nine_router_port is not None:
|
||||
if api_key:
|
||||
return {"ANTHROPIC_API_KEY": api_key}
|
||||
elif nine_router_port:
|
||||
if not p_check_9router(nine_router_port):
|
||||
raise ValueError("9Router is not running. Set an API key or connect a subscription.")
|
||||
return anthropic.AsyncAnthropic(
|
||||
api_key="9router",
|
||||
base_url=f"http://localhost:{nine_router_port}",
|
||||
)
|
||||
|
||||
return {
|
||||
"ANTHROPIC_API_KEY": "9router",
|
||||
"ANTHROPIC_BASE_URL": f"http://localhost:{nine_router_port}",
|
||||
}
|
||||
else:
|
||||
raise ValueError("No AI provider configured. Set an API key or a 9Router port.")
|
||||
raise ValueError("No AI provider configured. Set an API key or a 9router port")
|
||||
+1
@@ -34,6 +34,7 @@ async def run_browser_agent(
|
||||
tool_names.append(tool.to_sdk_args())
|
||||
|
||||
browser_config = ClaudeAgentOptions(
|
||||
env=parent.config.env,
|
||||
model=parent.model,
|
||||
system_prompt=BROWSER_AGENT_SYSTEM_PROMPT,
|
||||
tools=tool_names,
|
||||
|
||||
Reference in New Issue
Block a user