1import {
2 type ProviderInstanceId,
3 textGeneratorForSelection,
4 type UnifiedSettings,
5} from "@t3tools/contracts";
6
7import { mergeEnvironmentSettings } from "../../hooks/useSettings";
8import {
9 getCustomModelOptionsByInstance,
10 getTextGenerationModelOptionsByInstance,
11} from "../../modelSelection";
12import {
13 applyProviderInstanceSettings,
14 deriveProviderInstanceEntries,
15 type ProviderInstanceEntry,
16} from "../../providerInstances";
17import { useEnvironmentProviders, useEnvironments } from "../../state/environments";
18import { useSettingsScope } from "./SettingsScopeContext";
19
20export function useScopedModelDisabledReason(
21 settings: UnifiedSettings,
22 entries: readonly ProviderInstanceEntry[],
23 textGeneration = false,
24) {
25 const { targets } = useSettingsScope();
26 const { environments } = useEnvironments();
27 const providersByEnvironment = useEnvironmentProviders();
28 return (instanceId: ProviderInstanceId, model: string): string | null => {
29 const sourceEntry = entries.find((entry) => entry.instanceId === instanceId);
30 const shownGenerator = textGeneratorForSelection(settings, instanceId);
31 for (const candidate of targets) {
32 const environment = environments.find(
33 (entry) => entry.environmentId === candidate.environmentId,
34 );
35 const unavailable = `This model is unavailable on ${environment?.label ?? "a selected environment"}. Select that environment to choose its model separately.`;
36 const generator = textGeneratorForSelection(candidate.settings, instanceId);
37 if (shownGenerator !== undefined || generator !== undefined) {
38 // Only the shown environment's model list is loaded; elsewhere it must reach the same API.
39 if (
40 !generator?.enabled ||
41 generator.kind !== shownGenerator?.kind ||
42 generator.baseUrl !== shownGenerator.baseUrl
43 ) {
44 return unavailable;
45 }
46 continue;
47 }
48 const providers = providersByEnvironment.get(candidate.environmentId);
49 if (!environment?.serverConfig || !providers) continue;
50 const entry = applyProviderInstanceSettings(
51 deriveProviderInstanceEntries(providers),
52 candidate.settings,
53 candidate.projectId,
54 ).find((option) => option.instanceId === instanceId);
55 const optionsForInstance = textGeneration
56 ? getTextGenerationModelOptionsByInstance
57 : getCustomModelOptionsByInstance;
58 const options = optionsForInstance(
59 mergeEnvironmentSettings(candidate.settings, settings),
60 providers,
61 ).get(instanceId);
62 if (
63 !entry?.enabled ||
64 !entry.isAvailable ||
65 entry.driverKind !== sourceEntry?.driverKind ||
66 !options?.some((option) => option.slug === model && !option.isUnavailable)
67 ) {
68 return unavailable;
69 }
70 }
71 return null;
72 };
73}