studio chat: harden autoload singleflight + abort handling
Three follow-up fixes from the PR review pass: - Follower callers (sharing an in-flight cascade) now race their own abortSignal against the leader's promise, so a cancelled stream is no longer blocked on the leader's lifecycle. - Pre-aborted entry into autoLoadSmallestModel bails before the "Loading a model..." toast fires, avoiding a flash for abort-then- send sequences. - Fallback-gate abort path now returns blockedByTrustRemoteCode:false (matching the early-abort blocks at the loop bodies). Avoids surfacing "Enable custom code" when the real outcome was an abort.
This commit is contained in:
parent
faf35ed2c0
commit
8cfd4a5a6c
1 changed files with 40 additions and 2 deletions
|
|
@ -454,7 +454,13 @@ async function autoLoadSmallestModel(abortSignal?: AbortSignal): Promise<{
|
|||
loaded: boolean;
|
||||
blockedByTrustRemoteCode: boolean;
|
||||
}> {
|
||||
if (autoLoadInflight) return autoLoadInflight;
|
||||
// Pre-aborted callers bail before even creating a toast.
|
||||
if (abortSignal?.aborted) {
|
||||
return { loaded: false, blockedByTrustRemoteCode: false };
|
||||
}
|
||||
// Followers share the leader's cascade result but race against their own
|
||||
// abortSignal so a cancelled stream isn't blocked on the leader.
|
||||
if (autoLoadInflight) return awaitWithAbort(autoLoadInflight, abortSignal);
|
||||
const work = runAutoLoadCascade(abortSignal);
|
||||
autoLoadInflight = work;
|
||||
try {
|
||||
|
|
@ -464,10 +470,38 @@ async function autoLoadSmallestModel(abortSignal?: AbortSignal): Promise<{
|
|||
}
|
||||
}
|
||||
|
||||
function awaitWithAbort(
|
||||
work: Promise<{ loaded: boolean; blockedByTrustRemoteCode: boolean }>,
|
||||
signal?: AbortSignal,
|
||||
): Promise<{ loaded: boolean; blockedByTrustRemoteCode: boolean }> {
|
||||
if (!signal) return work;
|
||||
if (signal.aborted) {
|
||||
return Promise.resolve({ loaded: false, blockedByTrustRemoteCode: false });
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const onAbort = () =>
|
||||
resolve({ loaded: false, blockedByTrustRemoteCode: false });
|
||||
signal.addEventListener("abort", onAbort, { once: true });
|
||||
work.then(
|
||||
(value) => {
|
||||
signal.removeEventListener("abort", onAbort);
|
||||
resolve(value);
|
||||
},
|
||||
(err) => {
|
||||
signal.removeEventListener("abort", onAbort);
|
||||
reject(err);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async function runAutoLoadCascade(abortSignal?: AbortSignal): Promise<{
|
||||
loaded: boolean;
|
||||
blockedByTrustRemoteCode: boolean;
|
||||
}> {
|
||||
if (abortSignal?.aborted) {
|
||||
return { loaded: false, blockedByTrustRemoteCode: false };
|
||||
}
|
||||
const store = useChatRuntimeStore.getState();
|
||||
const hfToken = store.hfToken || null;
|
||||
const trustRemoteCode = store.params.trustRemoteCode ?? false;
|
||||
|
|
@ -658,9 +692,13 @@ async function runAutoLoadCascade(abortSignal?: AbortSignal): Promise<{
|
|||
}
|
||||
}
|
||||
|
||||
if (abortSignal?.aborted) {
|
||||
toast.dismiss(toastId);
|
||||
return { loaded: false, blockedByTrustRemoteCode: false };
|
||||
}
|
||||
// Cap also gates the default download so the total /api/inference/load
|
||||
// budget across cached + fallback is MAX_AUTO_LOAD_ATTEMPTS, not +1.
|
||||
if (abortSignal?.aborted || loadAttempts >= MAX_AUTO_LOAD_ATTEMPTS) {
|
||||
if (loadAttempts >= MAX_AUTO_LOAD_ATTEMPTS) {
|
||||
toast.dismiss(toastId);
|
||||
return {
|
||||
loaded: false,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue