Merge upstream v0.4.0 into Gitea fork
This commit is contained in:
@@ -17,10 +17,10 @@ import { useI18n } from "@/components/i18n";
|
||||
* 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 { CUSTOM_PROVIDER, MODEL_SLOTS, type AuthKeyStatus, type ModelCatalogResponse } from "@/src/core";
|
||||
import { classifyKeys, type ProviderKeyInfo } from "@/components/settings/keys-section";
|
||||
import { useModelCatalog } from "@/components/model-picker";
|
||||
import type { ModelCatalogResponse } from "@/src/core";
|
||||
import { useModelCatalog } from "@/components/model-catalog";
|
||||
|
||||
/** Pure: does a MODEL-PROVIDER key exist among the statuses? Reuses
|
||||
* classifyKeys (the keys section's own classification — no drift):
|
||||
@@ -31,7 +31,40 @@ export function hasProviderKey(
|
||||
statuses: readonly AuthKeyStatus[],
|
||||
providers: readonly ProviderKeyInfo[] | undefined,
|
||||
): boolean {
|
||||
return classifyKeys(statuses, providers).providerRows.some((s) => s.present);
|
||||
// A stored custom-endpoint key counts too: classifyKeys drops it from
|
||||
// providerRows (it has its own dedicated row), but a connected custom
|
||||
// endpoint IS a way to run inference, so it clears the zero-keys gate.
|
||||
return (
|
||||
classifyKeys(statuses, providers).providerRows.some((s) => s.present) ||
|
||||
statuses.some((s) => s.id === CUSTOM_PROVIDER.envVar && 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
|
||||
|
||||
Reference in New Issue
Block a user