mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-21 04:02:22 +02:00
156 lines
8.7 KiB
Python
156 lines
8.7 KiB
Python
from pydantic import BaseModel, Field
|
|
from typing import Optional, Any, Literal
|
|
|
|
DEFAULT_SYSTEM_PROMPT = (
|
|
"You are a personal AI assistant running inside OpenSwarm.\n\n"
|
|
"## Core Behavior\n"
|
|
"Act, don't ask. When a tool can accomplish the task, call it immediately; "
|
|
"do not describe what you would do, do not ask for confirmation, just execute. "
|
|
"The user expects results, not plans.\n"
|
|
"If ANY available tool is relevant to the user's request, use it. Never respond "
|
|
'with "I can do X for you" or "Would you like me to..."; just do it. '
|
|
"A tool call is always better than a text explanation of what the tool would do.\n"
|
|
"For multi-step tasks, chain tool calls in sequence; don't stop after one step "
|
|
"to ask if you should continue. Complete the entire task, then report the results.\n"
|
|
"Be adaptable. If one approach fails, try a different tool or strategy instead of "
|
|
"giving up or repeating the same action. Always stay focused on what the user "
|
|
"actually wants to accomplish; their intent matters more than the specific method.\n\n"
|
|
"## Tool Priority\n"
|
|
"1. Connected MCP tools; fastest and most reliable. To reach an integration you "
|
|
"don't already see, use MCPSearch then MCPActivate; never ToolSearch for it.\n"
|
|
"2. WebSearch / WebFetch; for general web lookups when no MCP tool fits.\n"
|
|
"3. BrowserAgent; last resort, only for visual interaction with websites, "
|
|
"filling forms, or tasks no other tool can handle.\n\n"
|
|
"## Style\n"
|
|
"Do not narrate routine tool calls; just call the tool.\n"
|
|
"After tool calls complete, present the results directly. Do not recap which "
|
|
"tools you called or why; the user can see tool calls in the UI.\n"
|
|
"Keep responses brief and direct. Use plain language.\n"
|
|
"If you genuinely need clarification on something ambiguous, use the "
|
|
"AskUserQuestion tool. Never ask questions inline in plain text.\n"
|
|
)
|
|
|
|
|
|
# Fresh-install / unset-fallback model. "opus-5" is the plain row: cc/ sub lane for subscription users, API key otherwise.
|
|
DEFAULT_MODEL = "opus-5"
|
|
|
|
|
|
class AppSettings(BaseModel):
|
|
default_system_prompt: Optional[str] = DEFAULT_SYSTEM_PROMPT
|
|
default_folder: Optional[str] = None
|
|
default_model: str = DEFAULT_MODEL
|
|
default_mode: str = "agent"
|
|
default_max_turns: Optional[int] = None
|
|
default_thinking_level: Literal["off", "low", "medium", "high", "auto"] = "auto"
|
|
zoom_sensitivity: float = 50.0
|
|
# What a plain MOUSE wheel does on the canvas. "zoom" is the Google-Maps model we ship; "scroll"
|
|
# suits people who expect a wheel to move the page, and swaps the pair so cmd/ctrl+wheel zooms
|
|
# instead. A trackpad two-finger scroll pans either way, since that gesture is already a pan
|
|
# everywhere else on the machine.
|
|
mouse_wheel_action: Literal["zoom", "scroll"] = "zoom"
|
|
# Root font-size multiplier (0.9/1/1.1/1.2 from Settings > Interface); the whole rem type scale rides it.
|
|
ui_font_scale: float = 1.0
|
|
theme: str = "light"
|
|
# Shared across App Builder workspaces (each runs its own vite port / localStorage origin); null = follow system.
|
|
app_template_theme_override: Optional[Literal["light", "dark"]] = None
|
|
new_agent_shortcut: str = "Meta+l"
|
|
# None = platform default (Cmd/Ctrl+Shift+D); parts format matches new_agent_shortcut.
|
|
dictation_shortcut: Optional[str] = None
|
|
voice_hold_to_talk: bool = True
|
|
# Whisper model id from the desktop catalog (electron/voice/whisperModels.js); None = its default.
|
|
dictation_model: Optional[str] = None
|
|
anthropic_api_key: Optional[str] = None
|
|
browser_homepage: str = "https://www.google.com"
|
|
# Opt-in: let a blocked browser agent borrow the sign-in you already have in your everyday
|
|
# browser instead of stopping to ask you to log in again. Default OFF because reading your real
|
|
# browser's session is your decision to make once, explicitly, not ours to assume.
|
|
browser_import_signins: bool = False
|
|
openai_api_key: Optional[str] = None
|
|
google_api_key: Optional[str] = None
|
|
openrouter_api_key: Optional[str] = None
|
|
custom_providers: list["CustomProvider"] = Field(default_factory=list)
|
|
auto_select_mode_on_new_agent: bool = False
|
|
expand_new_chats_in_dashboard: bool = True
|
|
auto_reveal_sub_agents: bool = True
|
|
dev_mode: bool = False
|
|
allow_experimental_updates: bool = False
|
|
claude_subscription_token: Optional[str] = None
|
|
openai_subscription_token: Optional[str] = None
|
|
gemini_subscription_token: Optional[str] = None
|
|
user_name: Optional[str] = None
|
|
user_email: Optional[str] = None
|
|
user_use_case: Optional[str] = None
|
|
user_referral_source: Optional[str] = None
|
|
# Onboarding v3 lifecycle: None = never seen, "done"/"skipped" once resolved.
|
|
onboarding_v3: Optional[str] = None
|
|
# User-picked accent hex from the onboarding theme pad; None = stock accent.
|
|
accent_color: Optional[str] = None
|
|
# Multi-stop gradient from the theme pad (2-3 hexes); washes the canvas.
|
|
accent_gradient: Optional[list[str]] = None
|
|
personalized_greeting: Optional[str] = None
|
|
# Short one-glance identity hook for the reveal's focal beat (greeting is the longer warm read).
|
|
personalized_headline: Optional[str] = None
|
|
personalized_starters: list["PersonalizedStarter"] = Field(default_factory=list)
|
|
personalized_automations: list["PersonalizedAutomation"] = Field(default_factory=list)
|
|
# The hero's two-level menu: 4 general categories, each holding 4 starters tailored to this user.
|
|
personalized_menu: Optional["PersonalizedMenu"] = None
|
|
# Distilled from the user's provider chat history the first time they open ChatGPT/Claude in-app; re-feeds prep to sharpen suggestions.
|
|
personalized_usage_summary: Optional[str] = None
|
|
# Suppresses preflight suggestion modal entries the user dismissed; keyed by ToolDefinition.name, value ISO timestamp.
|
|
dismissed_mcp_suggestions: dict[str, str] = Field(default_factory=dict)
|
|
analytics_opt_in: bool = True
|
|
installation_id: Optional[str] = None
|
|
# Minted once by the analytics SDK's register() and reused forever; server-owned.
|
|
analytics_token: Optional[str] = None
|
|
# Renderer-reported browser Intl values, stamped on analytics submissions; server-owned.
|
|
timezone: Optional[str] = None
|
|
locale: Optional[str] = None
|
|
first_opened_at: Optional[str] = None
|
|
connection_mode: str = "own_key"
|
|
openswarm_bearer_token: Optional[str] = None
|
|
openswarm_proxy_url: Optional[str] = None
|
|
# Zero-config free trial: server-funded runs for a brand-new user with no key and no subscription. connection_mode flips to "free-trial" while armed; the token + remaining count are server-owned (minted by the cloud, sticky per machine). remaining is cached for the onboarding "runs low" nudge.
|
|
free_trial_token: Optional[str] = None
|
|
free_trial_remaining: Optional[int] = None
|
|
free_trial_runs_limit: Optional[int] = None
|
|
# Epoch seconds when the rolling window refills to a fresh allotment; lets the spent-trial nudge say "fresh runs in ~3h" instead of a vague "for now". Server-owned.
|
|
free_trial_resets_at: Optional[float] = None
|
|
openswarm_subscription_plan: Optional[str] = None
|
|
openswarm_subscription_expires: Optional[str] = None
|
|
openswarm_usage_cached: Optional[dict] = None
|
|
# Server-validated identity from /api/auth/signin-activate; user_email above is the self-reported onboarding value.
|
|
user_id: Optional[str] = None
|
|
signin_method: Optional[Literal["google", "stripe", "email"]] = None
|
|
# Runtime preflight (electron/preflight.js). Default-on; users opt out via this flag, env var OPENSWARM_DISABLE_PREFLIGHT=1, or the cloud-side cohort rollout knocking preflight_rollout_pct down.
|
|
preflight_enabled: bool = True
|
|
# 0-100; the cohort gate compares (hash(installation_id) % 100) < pct. 100 = everyone, 0 = nobody, used as the kill switch if a staged rollout finds a false-positive spike.
|
|
preflight_rollout_pct: int = 100
|
|
|
|
|
|
class CustomProvider(BaseModel):
|
|
name: str
|
|
base_url: str
|
|
api_key: str = ""
|
|
models: list[dict[str, Any]] = Field(default_factory=list)
|
|
|
|
|
|
class PersonalizedStarter(BaseModel):
|
|
title: str
|
|
prompt: str
|
|
# One short clause tying this task to something real we saw about the user; the reveal shows it off.
|
|
reason: str = ""
|
|
|
|
|
|
class PersonalizedAutomation(BaseModel):
|
|
title: str
|
|
prompt: str
|
|
# 'daily' | 'weekday' | 'weekly'; the frontend maps this to a workflow schedule.
|
|
cadence: str = "weekly"
|
|
|
|
|
|
class PersonalizedMenu(BaseModel):
|
|
computer: list[PersonalizedStarter] = Field(default_factory=list)
|
|
research: list[PersonalizedStarter] = Field(default_factory=list)
|
|
web: list[PersonalizedStarter] = Field(default_factory=list)
|
|
build: list[PersonalizedStarter] = Field(default_factory=list)
|