From 00e4d77423f53e9aaf3a1a9e71da22b013a93df2 Mon Sep 17 00:00:00 2001 From: ErenAta16 Date: Tue, 7 Jul 2026 16:45:07 +0300 Subject: [PATCH] Address latest Codex findings: stale detection, model swap, cache - Clear detectedAgents (and skip the network call entirely) when the panel leaves a loopback base, instead of leaving a previous loopback detection result marked 'installed' for a command that now targets a LAN/tunnel/ remote host. - Add a separate, network-free correction effect keyed on the live activeGgufVariant: if codex was auto-picked while a GGUF model was loaded and the user then switches to a transformers-backed model while this panel stays mounted, steer away from codex instead of leaving a command that unsloth_cli's _require_gguf_for_codex will now reject. Never touches a manual pick. - Drop coding-agents.ts's module-lifetime cache. Installed-CLI detection is environment state, not a persisted setting, so a stale positive/negative from before the user installed something (or reopened the tab) is worse than one extra cheap local API call per mount; keep only the in-flight de-dupe for concurrent callers. Verified the correction-effect logic (gguf->non-gguf swap with/without a fallback, still-gguf no-op, manual pick never overridden) with a standalone port of the effect. --- .../features/settings/api/coding-agents.ts | 20 +++++------ .../settings/components/usage-examples.tsx | 35 ++++++++++++++----- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/studio/frontend/src/features/settings/api/coding-agents.ts b/studio/frontend/src/features/settings/api/coding-agents.ts index 708cf8fd0c..ae371b2d3a 100644 --- a/studio/frontend/src/features/settings/api/coding-agents.ts +++ b/studio/frontend/src/features/settings/api/coding-agents.ts @@ -16,7 +16,11 @@ type ApiCodingAgentsInfo = { detected: string[]; }; -let cachedInfo: CodingAgentsInfo | null = null; +// Which CLIs are on PATH is environment state, not a persisted setting -- it +// can change any time the user installs something new, so this only +// de-duplicates concurrent in-flight calls (e.g. React strict-mode's double +// mount) rather than caching the result across the module's lifetime. Every +// fresh call (each time a settings panel mounts) re-checks PATH for real. let inFlightInfo: Promise | null = null; function fromApi(info: ApiCodingAgentsInfo): CodingAgentsInfo { @@ -34,16 +38,8 @@ async function fetchCodingAgents(): Promise { } export async function loadCodingAgents(): Promise { - if (cachedInfo) { - return cachedInfo; - } - inFlightInfo ??= fetchCodingAgents() - .then((info) => { - cachedInfo = info; - return info; - }) - .finally(() => { - inFlightInfo = null; - }); + inFlightInfo ??= fetchCodingAgents().finally(() => { + inFlightInfo = null; + }); return inFlightInfo; } diff --git a/studio/frontend/src/features/settings/components/usage-examples.tsx b/studio/frontend/src/features/settings/components/usage-examples.tsx index c02b22a096..11120c45d6 100644 --- a/studio/frontend/src/features/settings/components/usage-examples.tsx +++ b/studio/frontend/src/features/settings/components/usage-examples.tsx @@ -501,19 +501,24 @@ export function UsageExamples({ apiKey }: { apiKey?: string | null }) { }, []); useEffect(() => { + // shutil.which runs on the Studio backend, so "detected" only means + // something for the browser's own machine when the base this panel + // targets is loopback; for a LAN/tunnel/remote base the server's + // installed CLIs describe a different machine. Skip the request + // entirely and clear out any stale result from a previous loopback + // base (e.g. the Cloudflare URL arriving after mount, or the user + // flipping Secure HTTPS/tunnel) instead of leaving old agents marked + // "detected" for a command that now targets somewhere else. + if (!isLoopbackBase) { + setDetectedAgents([]); + return; + } + let cancelled = false; void loadCodingAgents() .then((info) => { if (cancelled) return; setAvailableAgents(info.agents); - - // shutil.which runs on the Studio backend, so "detected" only means - // something for the browser's own machine when the base this panel - // targets is loopback; for a LAN/tunnel/remote base the server's - // installed CLIs describe a different machine, so don't mark - // anything as detected or let it drive the default. - if (!isLoopbackBase) return; - setDetectedAgents(info.detected); // Prefer an agent that's actually installed over the hardcoded // "claude" starting point, but never override a choice the user @@ -538,6 +543,20 @@ export function UsageExamples({ apiKey }: { apiKey?: string | null }) { }; }, [isLoopbackBase]); + // The effect above only re-evaluates codex's GGUF requirement at the + // moment detection resolves; if the user swaps to a non-GGUF model while + // this panel stays mounted, steer the auto-pick away from codex instead of + // leaving a command that unsloth_cli's _require_gguf_for_codex will now + // reject. No network call here -- it only re-derives from state already in + // hand, and it never overrides a choice the user made by hand. + const activeGgufVariant = useChatRuntimeStore((s) => s.activeGgufVariant); + useEffect(() => { + if (agentPickedByUserRef.current) return; + if (agent !== "codex" || activeGgufVariant) return; + const fallback = detectedAgents.find((id) => id !== "codex"); + if (fallback) setAgent(fallback); + }, [agent, activeGgufVariant, detectedAgents]); + useEffect(() => { let cancelled = false; void loadOpenAIAutoSwitchSettings()