Restore exclusive provider-first setup recovery

This commit is contained in:
amosblomqvist
2026-10-27 16:12:13 +02:00
parent c1ef6d6822
commit 419dcf1ba2
7 changed files with 222 additions and 97 deletions
+30 -2
View File
@@ -15,9 +15,10 @@
* its own save), the same re-read pattern chat.tsx uses for "models".
*/
import type { AuthKeyStatus } from "@/src/core";
import { useEffect, useState } from "react";
import { MODEL_SLOTS, type AuthKeyStatus } from "@/src/core";
import { classifyKeys, type ProviderKeyInfo } from "@/components/settings/keys-section";
import { useModelCatalog } from "@/components/model-picker";
import { useModelCatalog } from "@/components/model-catalog";
import type { ModelCatalogResponse } from "@/src/core";
/** Pure: does a MODEL-PROVIDER key exist among the statuses? Reuses
@@ -32,6 +33,33 @@ export function hasProviderKey(
return classifyKeys(statuses, providers).providerRows.some((s) => s.present);
}
/** Completeness is saved identity, not current provider availability. */
export function savedSlotsConfigured(slots: ModelCatalogResponse["slots"] | undefined): boolean {
return MODEL_SLOTS.every((slot) => !!slots?.[slot]?.model);
}
/** Keep the last known completeness during connection invalidation, but
* follow ordinary slot repairs as soon as their shared snapshot arrives. */
export function useModelSetup(initialConfigured: boolean) {
const { catalog } = useModelCatalog();
const [lastConfigured, setLastConfigured] = useState(initialConfigured);
const configured = catalog ? savedSlotsConfigured(catalog.slots) : lastConfigured;
useEffect(() => { if (catalog) setLastConfigured(savedSlotsConfigured(catalog.slots)); }, [catalog]);
return { catalog, configured, zeroKeys: catalog ? !catalog.providers.some((p) => p.keyPresent) : false };
}
/** A known unavailable route needs an explicit replacement, never an
* automatic provider/model substitution. Unknown metadata defers to the
* server's admission gate instead of declaring a route disconnected. */
export function selectedModelUnavailable(catalog: ModelCatalogResponse | undefined, selected: string | undefined): boolean {
return !!catalog && !!selected && !catalog.models.some((m) => `${m.provider}:${m.id}` === selected);
}
/** Reuse the ordinary control (also opened by the existing keyboard action). */
export function openModelPicker(): void {
window.dispatchEvent(new CustomEvent("lh:kbd-open-model-picker"));
}
/** The zero-keys truth for the calling page: TRUE only after a
* successful read says no provider key exists (pending and failed
* reads render nothing — the pill must never flash on a gap, and a