628 lines
19 KiB
TypeScript
628 lines
19 KiB
TypeScript
import {
|
|
getProviderScopedTeamModelLabel,
|
|
getRuntimeAwareProviderScopedTeamModelLabel,
|
|
getRuntimeAwareTeamModelBadgeLabel,
|
|
getRuntimeAwareTeamModelUiDisabledReason,
|
|
getTeamModelSourceBadgeLabel,
|
|
getTeamProviderLabel,
|
|
getTeamProviderModelOptions,
|
|
getVisibleTeamProviderModels,
|
|
isSupportedAnthropicTeamModel,
|
|
normalizeTeamModelForUi as normalizeCatalogTeamModelForUi,
|
|
sortTeamProviderModels,
|
|
type TeamProviderModelOption,
|
|
} from './teamModelCatalog';
|
|
import { extractProviderScopedBaseModel } from './teamModelContext';
|
|
|
|
import type {
|
|
CliProviderId,
|
|
CliProviderModelAvailability,
|
|
CliProviderModelAvailabilityStatus,
|
|
CliProviderStatus,
|
|
TeamProviderId,
|
|
} from '@shared/types';
|
|
|
|
export {
|
|
GPT_5_1_CODEX_MAX_CHATGPT_UI_DISABLED_REASON,
|
|
GPT_5_1_CODEX_MINI_UI_DISABLED_MODEL,
|
|
GPT_5_1_CODEX_MINI_UI_DISABLED_REASON,
|
|
GPT_5_2_CODEX_UI_DISABLED_MODEL,
|
|
GPT_5_2_CODEX_UI_DISABLED_REASON,
|
|
GPT_5_3_CODEX_SPARK_UI_DISABLED_MODEL,
|
|
GPT_5_3_CODEX_SPARK_UI_DISABLED_REASON,
|
|
TEAM_MODEL_UI_DISABLED_BADGE_LABEL,
|
|
} from './teamModelCatalog';
|
|
|
|
type SupportedProviderId = CliProviderId | TeamProviderId;
|
|
|
|
export const OPENCODE_OPENAI_AUTH_UNAVAILABLE_REASON =
|
|
'OpenCode OpenAI provider authentication failed. Reconnect OpenAI in provider settings, then refresh runtime status.';
|
|
|
|
export type TeamModelRuntimeProviderStatus = Pick<
|
|
CliProviderStatus,
|
|
| 'providerId'
|
|
| 'models'
|
|
| 'modelCatalog'
|
|
| 'modelAvailability'
|
|
| 'modelVerificationState'
|
|
| 'runtimeCapabilities'
|
|
| 'authMethod'
|
|
| 'backend'
|
|
| 'authenticated'
|
|
| 'supported'
|
|
| 'detailMessage'
|
|
| 'availableBackends'
|
|
| 'externalRuntimeDiagnostics'
|
|
> &
|
|
Partial<Pick<CliProviderStatus, 'verificationState' | 'statusMessage'>>;
|
|
|
|
export type TeamRuntimeModelOption = TeamProviderModelOption & {
|
|
availabilityStatus?: CliProviderModelAvailabilityStatus | null;
|
|
availabilityReason?: string | null;
|
|
};
|
|
|
|
export interface TeamProviderModelVerificationCounts {
|
|
checkedCount: number;
|
|
totalCount: number;
|
|
verifying: boolean;
|
|
}
|
|
|
|
export function getOpenCodeOpenAiRouteAuthUnavailableReason(
|
|
providerId: SupportedProviderId | undefined,
|
|
model: string | undefined,
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): string | null {
|
|
if (
|
|
providerId !== 'opencode' ||
|
|
!model?.trim().toLowerCase().startsWith('openai/') ||
|
|
!providerStatus
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
const openAiBackends = (providerStatus.availableBackends ?? []).filter((backend) =>
|
|
[backend.id, backend.label, backend.description].some((value) => /\bopenai\b/i.test(value))
|
|
);
|
|
const backendRequiresAuth = openAiBackends.some(
|
|
(backend) =>
|
|
backend.state === 'authentication-required' ||
|
|
(!backend.available &&
|
|
[backend.statusMessage, backend.detailMessage].some((value) =>
|
|
/auth|token|api key|401|403/i.test(value ?? '')
|
|
))
|
|
);
|
|
if (backendRequiresAuth) {
|
|
return OPENCODE_OPENAI_AUTH_UNAVAILABLE_REASON;
|
|
}
|
|
|
|
const diagnosticText = [
|
|
providerStatus.statusMessage,
|
|
providerStatus.detailMessage,
|
|
...openAiBackends.flatMap((backend) => [backend.statusMessage, backend.detailMessage]),
|
|
...(providerStatus.externalRuntimeDiagnostics ?? [])
|
|
.filter((diagnostic) => /\bopenai\b/i.test(diagnostic.label))
|
|
.flatMap((diagnostic) => [diagnostic.statusMessage, diagnostic.detailMessage]),
|
|
]
|
|
.map((value) => value?.trim() ?? '')
|
|
.filter(Boolean)
|
|
.join('\n');
|
|
|
|
if (
|
|
/\bopenai\b/i.test(diagnosticText) &&
|
|
/token refresh failed|token.*invalid|invalid.*token|not[_\s-]?authenticated|not authenticated|unauthorized|forbidden|\b401\b|\b403\b|invalid api key|api key.*invalid|authentication required/i.test(
|
|
diagnosticText
|
|
)
|
|
) {
|
|
return OPENCODE_OPENAI_AUTH_UNAVAILABLE_REASON;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
export function getTeamModelUiDisabledReason(
|
|
providerId: SupportedProviderId | undefined,
|
|
model: string | undefined,
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): string | null {
|
|
return getRuntimeAwareTeamModelUiDisabledReason(providerId, model, providerStatus);
|
|
}
|
|
|
|
export function isTeamModelUiDisabled(
|
|
providerId: SupportedProviderId | undefined,
|
|
model: string | undefined,
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): boolean {
|
|
return getTeamModelUiDisabledReason(providerId, model, providerStatus) !== null;
|
|
}
|
|
|
|
export function isTeamProviderModelVerificationPending(
|
|
providerId: SupportedProviderId | undefined,
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): boolean {
|
|
if (!providerId || providerId === 'anthropic' || !providerStatus) {
|
|
return false;
|
|
}
|
|
|
|
if (providerStatus.modelVerificationState === 'verifying') {
|
|
return true;
|
|
}
|
|
|
|
const hasRuntimeModelTruth =
|
|
providerStatus.models.length > 0 ||
|
|
(providerStatus.modelCatalog?.models.length ?? 0) > 0 ||
|
|
(providerStatus.modelAvailability?.length ?? 0) > 0;
|
|
if (!hasRuntimeModelTruth) {
|
|
if (
|
|
providerId === 'codex' &&
|
|
providerStatus.backend?.kind === 'codex-native' &&
|
|
providerStatus.supported
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
if (
|
|
providerId === 'opencode' &&
|
|
providerStatus.backend?.kind === 'opencode-cli' &&
|
|
providerStatus.supported
|
|
) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (providerStatus.verificationState !== 'unknown') {
|
|
return false;
|
|
}
|
|
|
|
if (hasRuntimeModelTruth) {
|
|
return false;
|
|
}
|
|
|
|
const statusMessage = providerStatus.statusMessage?.trim().toLowerCase() ?? '';
|
|
return statusMessage.length === 0 || statusMessage === 'checking...';
|
|
}
|
|
|
|
function getFallbackTeamProviderModels(providerId: SupportedProviderId): string[] {
|
|
return getVisibleTeamProviderModels(
|
|
providerId,
|
|
getTeamProviderModelOptions(providerId)
|
|
.map((option) => option.value)
|
|
.filter((value) => value.trim().length > 0)
|
|
);
|
|
}
|
|
|
|
function getFallbackTeamProviderModelOptions(
|
|
providerId: SupportedProviderId,
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): TeamRuntimeModelOption[] {
|
|
return getTeamProviderModelOptions(providerId).map((option) => ({
|
|
...option,
|
|
label:
|
|
option.value === ''
|
|
? option.label
|
|
: (getRuntimeAwareProviderScopedTeamModelLabel(providerId, option.value, providerStatus) ??
|
|
option.value),
|
|
badgeLabel:
|
|
option.value === ''
|
|
? option.badgeLabel
|
|
: (getRuntimeAwareTeamModelBadgeLabel(providerId, option.value, providerStatus) ??
|
|
option.badgeLabel),
|
|
}));
|
|
}
|
|
|
|
function hasAnthropicRuntimeCatalog(
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): boolean {
|
|
return providerStatus?.modelCatalog?.providerId === 'anthropic';
|
|
}
|
|
|
|
function getAnthropicCatalogModel(
|
|
model: string,
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): NonNullable<TeamModelRuntimeProviderStatus['modelCatalog']>['models'][number] | null {
|
|
const catalog = hasAnthropicRuntimeCatalog(providerStatus) ? providerStatus?.modelCatalog : null;
|
|
if (!catalog) {
|
|
return null;
|
|
}
|
|
|
|
return catalog.models.find((item) => item.launchModel === model || item.id === model) ?? null;
|
|
}
|
|
|
|
function getRuntimeCatalogModels(
|
|
providerId: SupportedProviderId,
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): string[] | null {
|
|
if (providerId === 'anthropic') {
|
|
return null;
|
|
}
|
|
|
|
if (providerId !== 'codex' || providerStatus?.modelCatalog?.providerId !== 'codex') {
|
|
return null;
|
|
}
|
|
|
|
const models = providerStatus.modelCatalog.models
|
|
.filter((model) => !model.hidden)
|
|
.map((model) => model.launchModel.trim())
|
|
.filter(Boolean);
|
|
return models.length > 0 ? models : null;
|
|
}
|
|
|
|
function getRuntimeCatalogModelOption(
|
|
providerId: SupportedProviderId,
|
|
model: string,
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): TeamRuntimeModelOption | null {
|
|
if (providerId !== 'codex' || providerStatus?.modelCatalog?.providerId !== 'codex') {
|
|
return null;
|
|
}
|
|
|
|
const catalogModel = providerStatus.modelCatalog.models.find(
|
|
(item) => item.launchModel === model || item.id === model
|
|
);
|
|
if (!catalogModel) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
value: catalogModel.launchModel,
|
|
label:
|
|
getProviderScopedTeamModelLabel(providerId, catalogModel.displayName) ??
|
|
catalogModel.displayName,
|
|
badgeLabel:
|
|
catalogModel.badgeLabel ??
|
|
(getTeamProviderModelOptions(providerId).some((option) => option.value === model)
|
|
? undefined
|
|
: 'New'),
|
|
availabilityStatus: getRuntimeModelAvailability(
|
|
providerId,
|
|
catalogModel.launchModel,
|
|
providerStatus
|
|
),
|
|
availabilityReason: getRuntimeModelAvailabilityReason(catalogModel.launchModel, providerStatus),
|
|
};
|
|
}
|
|
|
|
function getRuntimeSelectorModels(
|
|
providerId: SupportedProviderId,
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): string[] {
|
|
if (!providerStatus) {
|
|
return [];
|
|
}
|
|
|
|
const catalogModels = getRuntimeCatalogModels(providerId, providerStatus);
|
|
if (catalogModels) {
|
|
return getVisibleTeamProviderModels(providerId, catalogModels, providerStatus);
|
|
}
|
|
|
|
return sortTeamProviderModels(providerId, providerStatus.models, providerStatus);
|
|
}
|
|
|
|
function getVisibleRuntimeModels(
|
|
providerId: SupportedProviderId,
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): string[] {
|
|
return getRuntimeSelectorModels(providerId, providerStatus).filter(
|
|
(model) => getTeamModelUiDisabledReason(providerId, model, providerStatus) == null
|
|
);
|
|
}
|
|
|
|
function withSupplementalDisabledRuntimeModels(
|
|
providerId: SupportedProviderId,
|
|
models: readonly string[],
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): string[] {
|
|
if (providerId !== 'codex') {
|
|
return [...models];
|
|
}
|
|
|
|
const modelSet = new Set(models);
|
|
const supplementalDisabledModels = getTeamProviderModelOptions(providerId)
|
|
.map((option) => option.value.trim())
|
|
.filter(
|
|
(model) =>
|
|
model.length > 0 &&
|
|
!modelSet.has(model) &&
|
|
getTeamModelUiDisabledReason(providerId, model, providerStatus) !== null
|
|
);
|
|
|
|
return sortTeamProviderModels(
|
|
providerId,
|
|
[...models, ...supplementalDisabledModels],
|
|
providerStatus
|
|
);
|
|
}
|
|
|
|
function getModelAvailabilityMap(
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): Map<string, CliProviderModelAvailability> {
|
|
return new Map(
|
|
(providerStatus?.modelAvailability ?? []).map((item) => [item.modelId.trim(), item])
|
|
);
|
|
}
|
|
|
|
function getRuntimeModelAvailabilityFromLookup(
|
|
model: string,
|
|
visibleModelSet: ReadonlySet<string>,
|
|
modelAvailabilityById: ReadonlyMap<string, CliProviderModelAvailability>
|
|
): CliProviderModelAvailabilityStatus | null {
|
|
if (!visibleModelSet.has(model)) {
|
|
return null;
|
|
}
|
|
|
|
const runtimeAvailability = modelAvailabilityById.get(model)?.status ?? null;
|
|
if (runtimeAvailability === 'unavailable') {
|
|
return 'unavailable';
|
|
}
|
|
return 'available';
|
|
}
|
|
|
|
function getRuntimeModelAvailability(
|
|
providerId: SupportedProviderId,
|
|
model: string,
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): CliProviderModelAvailabilityStatus | null {
|
|
if (providerId === 'anthropic') {
|
|
if (!providerStatus || !hasAnthropicRuntimeCatalog(providerStatus)) {
|
|
return isSupportedAnthropicTeamModel(model) ? 'available' : null;
|
|
}
|
|
|
|
return getAnthropicCatalogModel(model, providerStatus) ? 'available' : null;
|
|
}
|
|
|
|
if (!providerStatus) {
|
|
return null;
|
|
}
|
|
|
|
const visibleModels = getVisibleRuntimeModels(providerId, providerStatus);
|
|
if (!visibleModels.includes(model)) {
|
|
return null;
|
|
}
|
|
const runtimeAvailability = getModelAvailabilityMap(providerStatus).get(model)?.status ?? null;
|
|
if (runtimeAvailability === 'unavailable') {
|
|
return 'unavailable';
|
|
}
|
|
return 'available';
|
|
}
|
|
|
|
function getRuntimeModelAvailabilityReason(
|
|
model: string,
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): string | null {
|
|
return getModelAvailabilityMap(providerStatus).get(model)?.reason ?? null;
|
|
}
|
|
|
|
export function getTeamProviderModelVerificationCounts(
|
|
providerId: SupportedProviderId,
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): TeamProviderModelVerificationCounts {
|
|
if (providerId === 'anthropic') {
|
|
const visibleAnthropicModels = getFallbackTeamProviderModels(providerId);
|
|
return {
|
|
checkedCount: visibleAnthropicModels.length,
|
|
totalCount: visibleAnthropicModels.length,
|
|
verifying: false,
|
|
};
|
|
}
|
|
|
|
const totalCount = getRuntimeSelectorModels(providerId, providerStatus).length;
|
|
|
|
return {
|
|
checkedCount: totalCount,
|
|
totalCount,
|
|
verifying: false,
|
|
};
|
|
}
|
|
|
|
export function getAvailableTeamProviderModels(
|
|
providerId: SupportedProviderId,
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): string[] {
|
|
if (providerId === 'anthropic') {
|
|
return getFallbackTeamProviderModels(providerId).filter(
|
|
(model) => getRuntimeModelAvailability(providerId, model, providerStatus) === 'available'
|
|
);
|
|
}
|
|
|
|
if (!providerStatus) {
|
|
return [];
|
|
}
|
|
|
|
const visibleModels = getVisibleRuntimeModels(providerId, providerStatus);
|
|
const modelAvailabilityById = getModelAvailabilityMap(providerStatus);
|
|
return visibleModels.filter(
|
|
(model) => modelAvailabilityById.get(model)?.status !== 'unavailable'
|
|
);
|
|
}
|
|
|
|
export function getAvailableTeamProviderModelOptions(
|
|
providerId: SupportedProviderId,
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): TeamRuntimeModelOption[] {
|
|
if (providerId === 'anthropic') {
|
|
return getFallbackTeamProviderModelOptions(providerId, providerStatus).map((option) => ({
|
|
...option,
|
|
availabilityStatus:
|
|
option.value.trim().length > 0
|
|
? getRuntimeModelAvailability(providerId, option.value, providerStatus)
|
|
: undefined,
|
|
availabilityReason:
|
|
option.value.trim().length > 0
|
|
? getRuntimeModelAvailabilityReason(option.value, providerStatus)
|
|
: undefined,
|
|
}));
|
|
}
|
|
|
|
if (!providerStatus) {
|
|
return [{ value: '', label: 'Default', badgeLabel: 'Default' }];
|
|
}
|
|
|
|
if (isTeamProviderModelVerificationPending(providerId, providerStatus)) {
|
|
return getFallbackTeamProviderModelOptions(providerId, providerStatus);
|
|
}
|
|
|
|
const visibleModels = withSupplementalDisabledRuntimeModels(
|
|
providerId,
|
|
getRuntimeSelectorModels(providerId, providerStatus),
|
|
providerStatus
|
|
);
|
|
const runtimeVisibleModelSet = new Set(
|
|
visibleModels.filter(
|
|
(model) => getTeamModelUiDisabledReason(providerId, model, providerStatus) == null
|
|
)
|
|
);
|
|
const modelAvailabilityById = getModelAvailabilityMap(providerStatus);
|
|
const getPrecomputedAvailability = (model: string): CliProviderModelAvailabilityStatus | null =>
|
|
getRuntimeModelAvailabilityFromLookup(model, runtimeVisibleModelSet, modelAvailabilityById);
|
|
const getPrecomputedAvailabilityReason = (model: string): string | null =>
|
|
modelAvailabilityById.get(model)?.reason ?? null;
|
|
|
|
return [
|
|
{ value: '', label: 'Default', badgeLabel: 'Default' },
|
|
...visibleModels.map((model) => {
|
|
const catalogOption = getRuntimeCatalogModelOption(providerId, model, providerStatus);
|
|
if (catalogOption) {
|
|
return {
|
|
...catalogOption,
|
|
availabilityStatus: getPrecomputedAvailability(model),
|
|
availabilityReason: getPrecomputedAvailabilityReason(model),
|
|
};
|
|
}
|
|
return {
|
|
value: model,
|
|
label: getProviderScopedTeamModelLabel(providerId, model) ?? model,
|
|
badgeLabel:
|
|
providerId === 'opencode'
|
|
? (getTeamModelSourceBadgeLabel(providerId, model) ?? undefined)
|
|
: undefined,
|
|
availabilityStatus: getPrecomputedAvailability(model),
|
|
availabilityReason: getPrecomputedAvailabilityReason(model),
|
|
};
|
|
}),
|
|
];
|
|
}
|
|
|
|
export function isTeamModelAvailableForUi(
|
|
providerId: SupportedProviderId | undefined,
|
|
model: string | undefined,
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): boolean {
|
|
const trimmed = model?.trim();
|
|
if (!providerId || !trimmed) {
|
|
return true;
|
|
}
|
|
|
|
if (getTeamModelUiDisabledReason(providerId, trimmed, providerStatus)) {
|
|
return false;
|
|
}
|
|
|
|
if (providerId === 'anthropic') {
|
|
if (!isSupportedAnthropicTeamModel(trimmed)) {
|
|
return false;
|
|
}
|
|
|
|
return getRuntimeModelAvailability(providerId, trimmed, providerStatus) === 'available';
|
|
}
|
|
|
|
if (isTeamProviderModelVerificationPending(providerId, providerStatus)) {
|
|
return true;
|
|
}
|
|
|
|
return getRuntimeModelAvailability(providerId, trimmed, providerStatus) === 'available';
|
|
}
|
|
|
|
export function normalizeExplicitTeamModelForUi(
|
|
providerId: SupportedProviderId | undefined,
|
|
model: string | undefined
|
|
): string {
|
|
const normalized = extractProviderScopedBaseModel(model, providerId) ?? '';
|
|
return normalizeCatalogTeamModelForUi(providerId, normalized).trim();
|
|
}
|
|
|
|
export function normalizeTeamModelForUi(
|
|
providerId: SupportedProviderId | undefined,
|
|
model: string | undefined,
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): string {
|
|
const normalized = normalizeCatalogTeamModelForUi(providerId, model);
|
|
const trimmed = normalized.trim();
|
|
if (!providerId || !trimmed) {
|
|
return normalized;
|
|
}
|
|
|
|
if (getTeamModelUiDisabledReason(providerId, trimmed, providerStatus)) {
|
|
return '';
|
|
}
|
|
|
|
if (providerId === 'anthropic') {
|
|
return isTeamModelAvailableForUi(providerId, trimmed, providerStatus) ? normalized : '';
|
|
}
|
|
|
|
if (!providerStatus) {
|
|
return '';
|
|
}
|
|
|
|
if (isTeamProviderModelVerificationPending(providerId, providerStatus)) {
|
|
return normalized;
|
|
}
|
|
|
|
const visibleModels = getVisibleRuntimeModels(providerId, providerStatus);
|
|
if (!visibleModels.includes(trimmed)) {
|
|
return '';
|
|
}
|
|
|
|
const availability = getRuntimeModelAvailability(providerId, trimmed, providerStatus);
|
|
return availability === 'available' ? normalized : '';
|
|
}
|
|
|
|
export function getTeamModelSelectionError(
|
|
providerId: SupportedProviderId | undefined,
|
|
model: string | undefined,
|
|
providerStatus?: TeamModelRuntimeProviderStatus | null
|
|
): string | null {
|
|
const trimmed = model?.trim();
|
|
if (!providerId || !trimmed) {
|
|
return null;
|
|
}
|
|
|
|
const disabledReason = getTeamModelUiDisabledReason(providerId, trimmed, providerStatus);
|
|
if (disabledReason) {
|
|
return `Model "${trimmed}" is disabled. ${disabledReason}`;
|
|
}
|
|
|
|
const dynamicUnavailableReason = getOpenCodeOpenAiRouteAuthUnavailableReason(
|
|
providerId,
|
|
trimmed,
|
|
providerStatus
|
|
);
|
|
if (dynamicUnavailableReason) {
|
|
return `Model "${trimmed}" is not available for the current ${getTeamProviderLabel(providerId) ?? providerId} runtime. ${dynamicUnavailableReason}`;
|
|
}
|
|
|
|
if (providerId === 'anthropic') {
|
|
return isTeamModelAvailableForUi(providerId, trimmed, providerStatus)
|
|
? null
|
|
: `Model "${trimmed}" is not available for the current ${getTeamProviderLabel(providerId) ?? providerId} runtime. Pick one of the listed models or use Default.`;
|
|
}
|
|
|
|
if (!providerStatus) {
|
|
return null;
|
|
}
|
|
|
|
if (isTeamProviderModelVerificationPending(providerId, providerStatus)) {
|
|
return null;
|
|
}
|
|
|
|
const visibleModels = getVisibleRuntimeModels(providerId, providerStatus);
|
|
if (!visibleModels.includes(trimmed)) {
|
|
return `Model "${trimmed}" is not available for the current ${getTeamProviderLabel(providerId) ?? providerId} runtime. Pick one of the listed models or use Default.`;
|
|
}
|
|
|
|
const availability = getRuntimeModelAvailability(providerId, trimmed, providerStatus);
|
|
if (availability !== 'available') {
|
|
const reason = getRuntimeModelAvailabilityReason(trimmed, providerStatus);
|
|
const reasonSuffix = reason ? ` ${reason}` : '';
|
|
return `Model "${trimmed}" is not available for the current ${getTeamProviderLabel(providerId) ?? providerId} runtime.${reasonSuffix} Pick one of the listed models or use Default.`;
|
|
}
|
|
|
|
return null;
|
|
}
|