mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-07 18:27:45 +02:00
[eric] models: default is opus-5 via one DEFAULT_MODEL constant; per-provider picks upgraded to gpt-5.6 + latest gemini
This commit is contained in:
@@ -31,10 +31,14 @@ DEFAULT_SYSTEM_PROMPT = (
|
||||
)
|
||||
|
||||
|
||||
# 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 = "sonnet"
|
||||
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"
|
||||
|
||||
@@ -136,7 +136,8 @@ async def clear_free_trial(settings_obj) -> None:
|
||||
if getattr(settings_obj, "default_model", None) == "haiku" and (
|
||||
has_own_model(settings_obj) or await p_has_connected_subscription()
|
||||
):
|
||||
settings_obj.default_model = "sonnet"
|
||||
from backend.apps.settings.models import DEFAULT_MODEL
|
||||
settings_obj.default_model = DEFAULT_MODEL
|
||||
settings_obj.free_trial_token = None
|
||||
await save_settings_async(settings_obj)
|
||||
await p_sync_routing(settings_obj)
|
||||
|
||||
@@ -14,6 +14,7 @@ from uuid import uuid4
|
||||
|
||||
from backend.apps.swarm.exportable import DepRef, ExportContext, RemapTable
|
||||
from backend.apps.swarm.models import EntityType, Requirement, RequirementKind
|
||||
from backend.apps.settings.models import DEFAULT_MODEL
|
||||
|
||||
P_BUILTIN_MODES = {"agent", "ask", "plan", "view-builder", "skill-builder"}
|
||||
# Transcript fields ride along so the shared agent keeps its history; ids inside (message ids, branch ids, their parent/fork refs) are self-consistent within the one session file, so they carry verbatim with no remap.
|
||||
@@ -95,7 +96,7 @@ class SessionExportable:
|
||||
"name": payload.get("name") or "Agent",
|
||||
"status": "completed",
|
||||
"provider": payload.get("provider") or "anthropic",
|
||||
"model": payload.get("model") or "sonnet",
|
||||
"model": payload.get("model") or DEFAULT_MODEL,
|
||||
"mode": payload.get("mode") or "agent",
|
||||
"system_prompt": payload.get("system_prompt"),
|
||||
"allowed_tools": payload.get("allowed_tools") or [],
|
||||
|
||||
@@ -14,6 +14,7 @@ from typing import Optional
|
||||
from backend.apps.agents.core.models import AgentConfig
|
||||
from backend.apps.workflows.models import Workflow, WorkflowRun
|
||||
from backend.apps.workflows import storage
|
||||
from backend.apps.settings.models import DEFAULT_MODEL
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -268,7 +269,7 @@ async def execute(
|
||||
resolved_allowed_tools = _resolve_allowed_tools(wf)
|
||||
config = AgentConfig(
|
||||
name=wf.title or "Workflow",
|
||||
model=wf.model or "sonnet",
|
||||
model=wf.model or DEFAULT_MODEL,
|
||||
mode=wf.mode or "agent",
|
||||
provider=wf.provider or "anthropic",
|
||||
system_prompt=_resolve_system_prompt(wf),
|
||||
|
||||
@@ -2,6 +2,7 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_valida
|
||||
from typing import Optional, Literal, Any
|
||||
from datetime import datetime
|
||||
from uuid import uuid4
|
||||
from backend.apps.settings.models import DEFAULT_MODEL
|
||||
|
||||
|
||||
# Each "tier" in the permission chain: notify in app, fall through to text after N minutes if no response, then to call after a further N minutes/hours. Matches images 17 to 19 (Schedule edit). Order in the list = escalation order.
|
||||
@@ -97,7 +98,7 @@ class Workflow(BaseModel):
|
||||
# Tool names observed in the source chat when this workflow was generated. This preserves conversion context without pretending those calls map to generated workflow step ids. Explicit approval decisions still live in remembered_approvals and are the only values reused as permissions.
|
||||
source_tools: list[str] = Field(default_factory=list)
|
||||
dashboard_id: Optional[str] = None
|
||||
model: str = "sonnet"
|
||||
model: str = DEFAULT_MODEL
|
||||
mode: str = "agent"
|
||||
provider: str = "anthropic"
|
||||
created_at: datetime = Field(default_factory=datetime.now)
|
||||
|
||||
@@ -20,6 +20,7 @@ from backend.apps.workflows.models import (
|
||||
GenerateMetadataResponse,
|
||||
)
|
||||
from backend.apps.workflows import storage, scheduler, executor, audit, escalation
|
||||
from backend.apps.settings.models import DEFAULT_MODEL
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -265,7 +266,7 @@ async def create_workflow(body: WorkflowCreate):
|
||||
permissions=body.permissions or [],
|
||||
source_session_id=body.source_session_id,
|
||||
dashboard_id=body.dashboard_id,
|
||||
model=body.model or "sonnet",
|
||||
model=body.model or DEFAULT_MODEL,
|
||||
mode=body.mode or "agent",
|
||||
provider=body.provider or "anthropic",
|
||||
cost_cap_usd_monthly=body.cost_cap_usd_monthly,
|
||||
@@ -313,7 +314,7 @@ async def create_workflow(body: WorkflowCreate):
|
||||
@workflows.router.post("/generate-metadata")
|
||||
async def generate_workflow_metadata(body: GenerateMetadataRequest) -> GenerateMetadataResponse:
|
||||
# Preview-time naming for the convert-to-workflow draft. Generates without persisting so the card can show a real title before the user saves.
|
||||
wf = Workflow(steps=body.steps, model=body.model or "sonnet")
|
||||
wf = Workflow(steps=body.steps, model=body.model or DEFAULT_MODEL)
|
||||
title, description, labels = await _generate_workflow_metadata(wf)
|
||||
return GenerateMetadataResponse(title=title, description=description, step_labels=labels)
|
||||
|
||||
@@ -1017,7 +1018,7 @@ async def edit_agent_session(workflow_id: str):
|
||||
edit_dashboard_id = p_wsm.active_dashboard_id or executor.resolve_workflow_dashboard_id(wf)
|
||||
config = AgentConfig(
|
||||
name=f"Edit Agent: {wf.title}",
|
||||
model=wf.model or "sonnet",
|
||||
model=wf.model or DEFAULT_MODEL,
|
||||
mode=wf.mode or "agent",
|
||||
provider=wf.provider or "anthropic",
|
||||
system_prompt=system_prompt,
|
||||
@@ -1256,7 +1257,7 @@ async def test_run_workflow(workflow_id: str, body: dict):
|
||||
resolved_allowed_tools = executor._resolve_allowed_tools(wf)
|
||||
config = AgentConfig(
|
||||
name=f"{wf.title or 'Workflow'} (test)",
|
||||
model=wf.model or "sonnet",
|
||||
model=wf.model or DEFAULT_MODEL,
|
||||
mode=wf.mode or "agent",
|
||||
provider=wf.provider or "anthropic",
|
||||
system_prompt=executor._resolve_system_prompt(wf),
|
||||
@@ -1395,7 +1396,7 @@ async def schedule_agent_session(workflow_id: str):
|
||||
)
|
||||
config = AgentConfig(
|
||||
name=f"Scheduling: {wf.title}",
|
||||
model=wf.model or "sonnet",
|
||||
model=wf.model or DEFAULT_MODEL,
|
||||
mode=wf.mode or "agent",
|
||||
provider=wf.provider or "anthropic",
|
||||
system_prompt=system_prompt,
|
||||
|
||||
+2
-1
@@ -503,7 +503,8 @@ async def browser_agent_run(request: Request):
|
||||
|
||||
body = await request.json()
|
||||
tasks = body.get("tasks", [])
|
||||
model = body.get("model", "sonnet")
|
||||
from backend.apps.settings.models import DEFAULT_MODEL
|
||||
model = body.get("model", DEFAULT_MODEL)
|
||||
dashboard_id = body.get("dashboard_id", "")
|
||||
pre_selected_browser_ids = body.get("pre_selected_browser_ids", [])
|
||||
parent_session_id = body.get("parent_session_id", "")
|
||||
|
||||
@@ -168,13 +168,13 @@ async def test_clear_keeps_haiku_as_trial_face_unless_a_real_model_is_connected(
|
||||
s2 = AppSettings(connection_mode="free-trial", free_trial_token="ftk",
|
||||
default_model="haiku", anthropic_api_key="sk-ant-real")
|
||||
await clear_free_trial(s2)
|
||||
assert s2.default_model == "sonnet"
|
||||
assert s2.default_model == "opus-5"
|
||||
|
||||
# A connected 9Router subscription (invisible in settings) also frees haiku -> sonnet.
|
||||
monkeypatch.setattr(ft, "p_has_connected_subscription", _true)
|
||||
s3 = AppSettings(connection_mode="free-trial", free_trial_token="ftk", default_model="haiku")
|
||||
await clear_free_trial(s3)
|
||||
assert s3.default_model == "sonnet"
|
||||
assert s3.default_model == "opus-5"
|
||||
|
||||
# A user who deliberately picked haiku OUTSIDE free-trial mode is left alone.
|
||||
s4 = AppSettings(connection_mode="own_key", default_model="haiku")
|
||||
|
||||
@@ -63,7 +63,7 @@ def test_minimal_old_file_fills_missing_with_defaults(settings_file):
|
||||
p_write(settings_file, {"theme": "light"})
|
||||
s = store.load_settings()
|
||||
assert s.theme == "light"
|
||||
assert s.default_model == "sonnet" # filled from default
|
||||
assert s.default_model == "opus-5" # filled from default
|
||||
assert s.auto_reveal_sub_agents is True
|
||||
|
||||
|
||||
|
||||
Generated
+1
@@ -7,6 +7,7 @@
|
||||
"": {
|
||||
"name": "open-swarm",
|
||||
"version": "1.0.0",
|
||||
"license": "AGPL-3.0-only",
|
||||
"dependencies": {
|
||||
"@codemirror/lang-html": "^6.4.11",
|
||||
"@codemirror/lang-json": "^6.0.2",
|
||||
|
||||
@@ -289,9 +289,9 @@ const DEFAULT_MODEL_PRIORITY: string[] = [
|
||||
];
|
||||
|
||||
const DEFAULT_MODEL_PICKS: Record<string, string[]> = {
|
||||
Anthropic: ['sonnet-cc', 'sonnet'],
|
||||
OpenAI: ['gpt-5.4-mini', 'gpt-5.4'],
|
||||
Google: ['gemini-2.5-flash', 'gemini-3-flash', 'gemini-2.5-pro'],
|
||||
Anthropic: ['opus-5-cc', 'opus-5', 'opus-5-api', 'sonnet-5-cc', 'sonnet-5'],
|
||||
OpenAI: ['gpt-5.6', 'gpt-5.6-api', 'gpt-5.5', 'gpt-5.5-api'],
|
||||
Google: ['gemini-3.6-flash-api', 'gemini-3.5-flash-api', 'gemini-3.1-flash-lite'],
|
||||
'OpenSwarm Pro': ['sonnet', 'opus'],
|
||||
OpenSwarm: ['gpt-5-mini', 'claude-haiku-4.5', 'gpt-4.1'],
|
||||
};
|
||||
|
||||
@@ -337,7 +337,7 @@ const AgentChat: React.FC<AgentChatProps> = ({ sessionId: sessionIdProp, onClose
|
||||
// Holds the last non-empty suggestions so the docked banner's exit fade renders them instead of going blank the instant the array is cleared.
|
||||
const mcpSnapshotRef = useRef<Array<{ id: string; title: string; description: string; reason?: string }>>([]);
|
||||
const [mode, setMode] = useState('agent');
|
||||
const [model, setModel] = useState('sonnet');
|
||||
const [model, setModel] = useState('opus-5');
|
||||
// Workflow build chat only: brief "this model now runs the workflow" notice when the user switches models, so the run-model change isn't silent.
|
||||
const [workflowModelNotice, setWorkflowModelNotice] = useState<string | null>(null);
|
||||
const workflowModelNoticeTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useAppSelector } from '@/shared/hooks';
|
||||
import { sortModelsForPicker } from '../model-picker/modelPicker';
|
||||
|
||||
const FALLBACK_MODELS = [
|
||||
{ value: 'opus-5', label: 'Claude Opus 5', context_window: 1_000_000, reasoning: true },
|
||||
{ value: 'sonnet', label: 'Claude Sonnet 4.6', context_window: 1_000_000, reasoning: true },
|
||||
{ value: 'opus', label: 'Claude Opus 4.6', context_window: 1_000_000, reasoning: true },
|
||||
{ value: 'haiku', label: 'Claude Haiku 4.5', context_window: 200_000, reasoning: true },
|
||||
|
||||
@@ -84,14 +84,14 @@ const DashboardToolbar = React.forwardRef<HTMLDivElement, Props>(
|
||||
const defaultThinkingLevel = useAppSelector((s) => s.settings.data.default_thinking_level);
|
||||
const settingsLoaded = useAppSelector((s) => s.settings.loaded);
|
||||
const [mode, setMode] = useState(defaultMode || 'agent');
|
||||
const [model, setModel] = useState(defaultModel || 'sonnet');
|
||||
const [model, setModel] = useState(defaultModel || 'opus-5');
|
||||
const [thinkingLevel, setThinkingLevel] = useState<'off' | 'low' | 'medium' | 'high' | 'auto'>(defaultThinkingLevel || 'auto');
|
||||
// Snap to the persisted Settings defaults as soon as they arrive from the backend. Without the settingsLoaded guard, the effect fires against the Redux initialState ('sonnet') before the real default has loaded, and the settingsApplied flag then locks out the real default for the rest of the session, so new chats spawn under the stale value.
|
||||
const settingsApplied = useRef(false);
|
||||
useEffect(() => {
|
||||
if (settingsLoaded && !settingsApplied.current) {
|
||||
setMode(defaultMode || 'agent');
|
||||
setModel(defaultModel || 'sonnet');
|
||||
setModel(defaultModel || 'opus-5');
|
||||
setThinkingLevel(defaultThinkingLevel || 'auto');
|
||||
settingsApplied.current = true;
|
||||
}
|
||||
@@ -126,7 +126,7 @@ const DashboardToolbar = React.forwardRef<HTMLDivElement, Props>(
|
||||
useEffect(() => {
|
||||
if (settingsLoaded && inputOpen && !prevInputOpen.current) {
|
||||
setMode(defaultMode || 'agent');
|
||||
setModel(defaultModel || 'sonnet');
|
||||
setModel(defaultModel || 'opus-5');
|
||||
setThinkingLevel(defaultThinkingLevel || 'auto');
|
||||
}
|
||||
prevInputOpen.current = inputOpen;
|
||||
|
||||
@@ -44,6 +44,7 @@ let lastOpenTab: string | null = null;
|
||||
|
||||
// Shown only in the brief window before the live model list loads from the backend. Keep the flagship current so the default-model dropdown isn't stale.
|
||||
const DEFAULT_MODEL_FALLBACK = [
|
||||
{ value: 'opus-5', label: 'Claude Opus 5' },
|
||||
{ value: 'opus-4-8', label: 'Claude Opus 4.8' },
|
||||
{ value: 'sonnet', label: 'Claude Sonnet 4.6' },
|
||||
{ value: 'opus', label: 'Claude Opus 4.6' },
|
||||
|
||||
@@ -156,7 +156,7 @@ interface SettingsState {
|
||||
export const DEFAULT_SETTINGS: AppSettings = {
|
||||
default_system_prompt: DEFAULT_SYSTEM_PROMPT,
|
||||
default_folder: null,
|
||||
default_model: 'sonnet',
|
||||
default_model: 'opus-5',
|
||||
default_mode: 'agent',
|
||||
default_max_turns: null,
|
||||
default_thinking_level: 'auto',
|
||||
|
||||
Reference in New Issue
Block a user