| 1 | import { matchingModelKey } from "./providerImageInput"; |
| 2 | import type { ProviderModelOverrideView } from "./types"; |
| 3 | |
| 4 | export type ModelDraft = { model: string; context: string; output: string; vision: "auto" | "yes" | "no" }; |
| 5 | |
| 6 | /** Validate explicit overrides without turning an inherited value into a guessed limit. */ |
| 7 | export function modelDraftError(draft: ModelDraft, existing: string[], original?: string): "required" | "duplicate" | "syntax" | "context" | "output" | null { |
| 8 | const model = draft.model.trim(); |
| 9 | if (!model) return "required"; |
| 10 | if (/[\s,,]/.test(model)) return "syntax"; |
| 11 | if (existing.some((name) => name !== original && name === model)) return "duplicate"; |
| 12 | const valid = (value: string, allowOmit = false) => !value.trim() || (Number.isSafeInteger(Number(value)) && (Number(value) > 0 || (allowOmit && Number(value) === -1))); |
| 13 | if (!valid(draft.context)) return "context"; |
| 14 | if (!valid(draft.output, true)) return "output"; |
| 15 | return null; |
| 16 | } |
| 17 | |
| 18 | /** Keep unedited reasoning and future view fields when changing one model. */ |
| 19 | export function applyModelDraft(overrides: ProviderModelOverrideView[], draft: ModelDraft, original?: string): ProviderModelOverrideView[] { |
| 20 | const key = matchingModelKey(overrides.map((item) => item.model), original ?? draft.model); |
| 21 | const previous = overrides.find((item) => item.model === key); |
| 22 | const next: ProviderModelOverrideView = { |
| 23 | reasoningProtocol: "", supportedEfforts: [], defaultEffort: "", ...previous, |
| 24 | model: draft.model.trim(), contextWindow: Number(draft.context) || 0, maxOutputTokens: Number(draft.output) || 0, |
| 25 | vision: draft.vision === "auto" ? null : draft.vision === "yes", |
| 26 | }; |
| 27 | return [...overrides.filter((item) => item.model !== key && item.model !== next.model), next]; |
| 28 | } |
| 29 |