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:
parent
6ad2fbb77c
commit
00e4d77423
2 changed files with 35 additions and 20 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue