mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-08 02:37:45 +02:00
[eric] tiny dead-code sweep — drop unused langchain pins from requirements.txt (saves ~5MB on packaged build) plus a few orphan exports left
over from past iterations (temp_state slice fields, clipboard timestamp getter, three never-imported lastSeq helpers).
This commit is contained in:
@@ -2886,17 +2886,82 @@ class AgentManager:
|
||||
_ticker_task = asyncio.create_task(_ticker_loop())
|
||||
|
||||
if content_parts:
|
||||
asst_msg = Message(
|
||||
id=stream_text_msg_id or uuid4().hex,
|
||||
role="assistant",
|
||||
content="\n".join(content_parts),
|
||||
branch_id=session.active_branch_id,
|
||||
_asst_text = "\n".join(content_parts)
|
||||
# 9Router can deliver upstream auth failures
|
||||
# AS the assistant's reply ("Failed to
|
||||
# authenticate. API Error: 401 ... [codex/...]
|
||||
# Provided authentication token is expired").
|
||||
# When that happens, the SDK doesn't raise —
|
||||
# so our catch-all _is_auth_error path never
|
||||
# fires. Detect the pattern in the text
|
||||
# itself and substitute a friendly system
|
||||
# bubble + auth_error WS event so the user
|
||||
# gets an actionable message instead of a
|
||||
# raw 401 JSON dump in the chat.
|
||||
_lower_text = _asst_text.lower()
|
||||
_looks_like_router_auth_error = (
|
||||
("failed to authenticate" in _lower_text and "401" in _lower_text)
|
||||
or ("authentication token is expired" in _lower_text)
|
||||
or ("authentication token has expired" in _lower_text)
|
||||
or ("provided authentication token" in _lower_text and ("401" in _lower_text or "expired" in _lower_text))
|
||||
)
|
||||
session.messages.append(asst_msg)
|
||||
await ws_manager.send_to_session(session_id, "agent:message", {
|
||||
"session_id": session_id,
|
||||
"message": asst_msg.model_dump(mode="json"),
|
||||
})
|
||||
if _looks_like_router_auth_error:
|
||||
# Build a friendly message keyed off the
|
||||
# provider name in the upstream error.
|
||||
if "codex/" in _lower_text or "[codex" in _lower_text:
|
||||
friendly = (
|
||||
"GPT subscription token expired. Open Settings → Models and click "
|
||||
"Reconnect on the OpenAI / GPT row to refresh — should take ~10s, "
|
||||
"then send your message again."
|
||||
)
|
||||
reason = "codex_token_expired"
|
||||
elif "gemini-cli/" in _lower_text or "[gemini" in _lower_text:
|
||||
friendly = (
|
||||
"Gemini subscription token expired. Open Settings → Models and click "
|
||||
"Reconnect on the Google / Gemini row, then send your message again."
|
||||
)
|
||||
reason = "gemini_token_expired"
|
||||
else:
|
||||
friendly = (
|
||||
"Provider authentication expired. Open Settings → Models and "
|
||||
"reconnect, then send your message again."
|
||||
)
|
||||
reason = "router_auth_expired"
|
||||
_err_msg = Message(
|
||||
id=uuid4().hex,
|
||||
role="system",
|
||||
content=friendly,
|
||||
branch_id=session.active_branch_id,
|
||||
)
|
||||
session.messages.append(_err_msg)
|
||||
await ws_manager.send_to_session(session_id, "agent:auth_error", {
|
||||
"session_id": session_id,
|
||||
"reason": reason,
|
||||
"message": friendly,
|
||||
"model": session.model,
|
||||
})
|
||||
await ws_manager.send_to_session(session_id, "agent:message", {
|
||||
"session_id": session_id,
|
||||
"message": _err_msg.model_dump(mode="json"),
|
||||
})
|
||||
_analytics("auth.error", {
|
||||
"reason": reason,
|
||||
"model": session.model,
|
||||
"provider": session.provider,
|
||||
"via": "router_streamed_text",
|
||||
}, session_id=session_id, dashboard_id=session.dashboard_id)
|
||||
else:
|
||||
asst_msg = Message(
|
||||
id=stream_text_msg_id or uuid4().hex,
|
||||
role="assistant",
|
||||
content=_asst_text,
|
||||
branch_id=session.active_branch_id,
|
||||
)
|
||||
session.messages.append(asst_msg)
|
||||
await ws_manager.send_to_session(session_id, "agent:message", {
|
||||
"session_id": session_id,
|
||||
"message": asst_msg.model_dump(mode="json"),
|
||||
})
|
||||
|
||||
for i, tu in enumerate(tool_uses):
|
||||
msg_id = stream_tool_msg_ids_ordered[i] if i < len(stream_tool_msg_ids_ordered) else uuid4().hex
|
||||
@@ -3197,7 +3262,22 @@ class AgentManager:
|
||||
# 3. Anthropic API key 401 — wrong key. Re-enter.
|
||||
_model = (session.model or "").lower()
|
||||
_combined = f"{e!s}\n{_stderr_tail}".lower()
|
||||
if "no credentials for provider" in _combined:
|
||||
# Codex/OpenAI subscription tokens rotate every ~2-3
|
||||
# minutes — the user sees the rotation window as a 401
|
||||
# with "reset after 1m 59s" or similar. Don't ask them to
|
||||
# reconnect; just tell them to wait it out and retry.
|
||||
if (
|
||||
("codex/" in _combined or "[codex/" in _combined or _model.startswith(("cx/", "gpt-")))
|
||||
and ("authentication token is expired" in _combined or "authentication token has expired" in _combined or "401" in _combined)
|
||||
):
|
||||
friendly_msg = (
|
||||
"GPT subscription token just rotated — this is "
|
||||
"automatic and resets every couple minutes. Send "
|
||||
"your message again in ~1 minute and it'll go "
|
||||
"through. (No need to reconnect anything.)"
|
||||
)
|
||||
reason = "codex_token_rotating"
|
||||
elif "no credentials for provider" in _combined:
|
||||
friendly_msg = (
|
||||
"Selected route requires Claude Pro / Max, but it's "
|
||||
"not connected. Open Settings → Models and either "
|
||||
|
||||
@@ -423,7 +423,7 @@ async def resolve_aux_model(
|
||||
2. Anthropic API key set → bare haiku/sonnet on real Anthropic API
|
||||
3. 9Router + Claude subscription connected → cc/<model>
|
||||
4. 9Router + Codex connected → cx/gpt-5.4-mini
|
||||
5. 9Router + Gemini connected → gc/gemini-2.5-flash
|
||||
5. 9Router + Gemini connected → gc/gemini-3.1-flash-lite-preview
|
||||
6. Nothing available → raise ValueError
|
||||
|
||||
When primary_api is provided, the resolver tries that family first
|
||||
@@ -458,9 +458,9 @@ async def resolve_aux_model(
|
||||
# primary is Codex but it's not reachable — fall through to default
|
||||
elif primary_api == "gemini-cli" or primary_api == "gemini":
|
||||
if "gemini-cli" in connected:
|
||||
return ("gc/gemini-2.5-flash", base_url)
|
||||
return ("gc/gemini-3.1-flash-lite-preview", base_url)
|
||||
if getattr(settings, "google_api_key", None):
|
||||
return ("gemini-2.5-flash", "https://generativelanguage.googleapis.com/v1beta")
|
||||
return ("gemini-3.1-flash-lite-preview", "https://generativelanguage.googleapis.com/v1beta")
|
||||
# fall through to default
|
||||
# primary_api == "anthropic" naturally falls into the Anthropic-first
|
||||
# cascade below — no special branch needed.
|
||||
@@ -485,7 +485,7 @@ async def resolve_aux_model(
|
||||
if "codex" in connected:
|
||||
return ("cx/gpt-5.4-mini", base_url)
|
||||
if "gemini-cli" in connected:
|
||||
return ("gc/gemini-2.5-flash", base_url)
|
||||
return ("gc/gemini-3.1-flash-lite-preview", base_url)
|
||||
|
||||
raise ValueError(
|
||||
"No AI provider connected for auxiliary LLM call. "
|
||||
|
||||
@@ -11,8 +11,6 @@ claude-agent-sdk==0.1.70
|
||||
jsonschema
|
||||
fastapi[standard]
|
||||
pydantic==2.13.3
|
||||
langchain-core==0.3.51
|
||||
langchain-openai==0.3.12
|
||||
typeguard==4.4.2
|
||||
python-dotenv==1.1.1
|
||||
Pillow
|
||||
|
||||
@@ -842,11 +842,23 @@ const ProviderReasoningExplanation: React.FC<{
|
||||
}
|
||||
return segs.join(' · ');
|
||||
})();
|
||||
// Stable per-mount variant pick. Each render of the same bubble keeps
|
||||
// its line; new bubbles get a fresh roll. Adds a touch of personality
|
||||
// without becoming repetitive across the transcript.
|
||||
const variants = [
|
||||
"It's still thinking — we just aren't allowed to peek behind the curtain.",
|
||||
"Wheels are turning, but this provider keeps its thoughts private.",
|
||||
"Brain's busy back there; the provider just isn't letting us listen in.",
|
||||
"Mulling it over quietly — only Claude shows its work out loud.",
|
||||
"Thinking happened, just not in the open. (GPT and Gemini play their cards close.)",
|
||||
"Reasoning's underway, but this provider doesn't broadcast it. Trust the process.",
|
||||
];
|
||||
const idx = useMemo(() => Math.floor(Math.random() * variants.length), []);
|
||||
const line = variants[idx];
|
||||
|
||||
return (
|
||||
<Box component="span" sx={{ fontStyle: 'italic', opacity: 0.85 }}>
|
||||
The model reasoned about this turn, but the provider didn't expose
|
||||
the reasoning text — only Anthropic emits a full chain-of-thought
|
||||
stream. {metric ? `Spent ${metric} thinking.` : 'No reasoning trace available.'}
|
||||
{line} {metric ? `Took ${metric}.` : ''}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -13,22 +13,15 @@ export interface ClipboardCard {
|
||||
}
|
||||
|
||||
let clipboardCards: ClipboardCard[] = [];
|
||||
let clipboardTimestamp = 0;
|
||||
|
||||
export function setClipboardCards(cards: ClipboardCard[]): void {
|
||||
clipboardCards = cards;
|
||||
clipboardTimestamp = Date.now();
|
||||
}
|
||||
|
||||
export function getClipboardCards(): ClipboardCard[] {
|
||||
return clipboardCards;
|
||||
}
|
||||
|
||||
export function getClipboardTimestamp(): number {
|
||||
return clipboardTimestamp;
|
||||
}
|
||||
|
||||
export function clearClipboard(): void {
|
||||
clipboardCards = [];
|
||||
clipboardTimestamp = 0;
|
||||
}
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
interface TempState {
|
||||
temp_state: string | null;
|
||||
pendingBrowserUrl: string | null;
|
||||
pendingFocusAgentId: string | null;
|
||||
lastDashboardId: string | null;
|
||||
}
|
||||
|
||||
const initialState: TempState = {
|
||||
temp_state: null,
|
||||
pendingBrowserUrl: null,
|
||||
pendingFocusAgentId: null,
|
||||
lastDashboardId: null,
|
||||
@@ -19,12 +17,6 @@ const tempStateSlice = createSlice({
|
||||
name: 'tempState',
|
||||
initialState,
|
||||
reducers: {
|
||||
setTempState(state, action: PayloadAction<string | null>) {
|
||||
state.temp_state = action.payload;
|
||||
},
|
||||
resetTempState(state) {
|
||||
state.temp_state = null;
|
||||
},
|
||||
setPendingBrowserUrl(state, action: PayloadAction<string>) {
|
||||
state.pendingBrowserUrl = action.payload;
|
||||
},
|
||||
@@ -43,9 +35,7 @@ const tempStateSlice = createSlice({
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
setTempState,
|
||||
resetTempState,
|
||||
export const {
|
||||
setPendingBrowserUrl,
|
||||
clearPendingBrowserUrl,
|
||||
setLastDashboardId,
|
||||
|
||||
@@ -947,20 +947,6 @@ export const dashboardWs = new WebSocketManager(`${WS_BASE}/ws/dashboard`, { ski
|
||||
// to a no-op replay. Safe.
|
||||
const _sessionLastSeq: Map<string, number> = new Map();
|
||||
|
||||
export function getPersistedLastSeq(sessionId: string): number {
|
||||
return _sessionLastSeq.get(sessionId) ?? 0;
|
||||
}
|
||||
|
||||
export function setPersistedLastSeq(sessionId: string, seq: number): void {
|
||||
if (seq > (_sessionLastSeq.get(sessionId) ?? 0)) {
|
||||
_sessionLastSeq.set(sessionId, seq);
|
||||
}
|
||||
}
|
||||
|
||||
export function clearPersistedLastSeq(sessionId: string): void {
|
||||
_sessionLastSeq.delete(sessionId);
|
||||
}
|
||||
|
||||
export function createSessionWs(sessionId: string): WebSocketManager {
|
||||
return new WebSocketManager(`${WS_BASE}/ws/agents/${sessionId}`, { sessionId });
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user