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.
This commit is contained in:
ErenAta16 2026-07-07 16:45:07 +03:00
commit 00e4d77423
2 changed files with 35 additions and 20 deletions

View file

@ -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<CodingAgentsInfo> | null = null;
function fromApi(info: ApiCodingAgentsInfo): CodingAgentsInfo {
@ -34,16 +38,8 @@ async function fetchCodingAgents(): Promise<CodingAgentsInfo> {
}
export async function loadCodingAgents(): Promise<CodingAgentsInfo> {
if (cachedInfo) {
return cachedInfo;
}
inFlightInfo ??= fetchCodingAgents()
.then((info) => {
cachedInfo = info;
return info;
})
.finally(() => {
inFlightInfo = null;
});
inFlightInfo ??= fetchCodingAgents().finally(() => {
inFlightInfo = null;
});
return inFlightInfo;
}

View file

@ -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()