| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- const PROVIDER_ID = /^[a-z0-9][a-z0-9-_]*$/
- const OPENAI_COMPATIBLE = "@ai-sdk/openai-compatible"
- type Translator = (key: string, vars?: Record<string, string | number | boolean>) => string
- export type ModelErr = {
- id?: string
- name?: string
- }
- export type HeaderErr = {
- key?: string
- value?: string
- }
- export type ModelRow = {
- row: string
- id: string
- name: string
- err: ModelErr
- }
- export type HeaderRow = {
- row: string
- key: string
- value: string
- err: HeaderErr
- }
- export type FormState = {
- providerID: string
- name: string
- baseURL: string
- apiKey: string
- models: ModelRow[]
- headers: HeaderRow[]
- err: {
- providerID?: string
- name?: string
- baseURL?: string
- }
- }
- type ValidateArgs = {
- form: FormState
- t: Translator
- disabledProviders: string[]
- existingProviderIDs: Set<string>
- }
- export function validateCustomProvider(input: ValidateArgs) {
- const providerID = input.form.providerID.trim()
- const name = input.form.name.trim()
- const baseURL = input.form.baseURL.trim()
- const apiKey = input.form.apiKey.trim()
- const env = apiKey.match(/^\{env:([^}]+)\}$/)?.[1]?.trim()
- const key = apiKey && !env ? apiKey : undefined
- const idError = !providerID
- ? input.t("provider.custom.error.providerID.required")
- : !PROVIDER_ID.test(providerID)
- ? input.t("provider.custom.error.providerID.format")
- : undefined
- const nameError = !name ? input.t("provider.custom.error.name.required") : undefined
- const urlError = !baseURL
- ? input.t("provider.custom.error.baseURL.required")
- : !/^https?:\/\//.test(baseURL)
- ? input.t("provider.custom.error.baseURL.format")
- : undefined
- const disabled = input.disabledProviders.includes(providerID)
- const existsError = idError
- ? undefined
- : input.existingProviderIDs.has(providerID) && !disabled
- ? input.t("provider.custom.error.providerID.exists")
- : undefined
- const seenModels = new Set<string>()
- const models = input.form.models.map((m) => {
- const id = m.id.trim()
- const idError = !id
- ? input.t("provider.custom.error.required")
- : seenModels.has(id)
- ? input.t("provider.custom.error.duplicate")
- : (() => {
- seenModels.add(id)
- return undefined
- })()
- const nameError = !m.name.trim() ? input.t("provider.custom.error.required") : undefined
- return { id: idError, name: nameError }
- })
- const modelsValid = models.every((m) => !m.id && !m.name)
- const modelConfig = Object.fromEntries(input.form.models.map((m) => [m.id.trim(), { name: m.name.trim() }]))
- const seenHeaders = new Set<string>()
- const headers = input.form.headers.map((h) => {
- const key = h.key.trim()
- const value = h.value.trim()
- if (!key && !value) return {}
- const keyError = !key
- ? input.t("provider.custom.error.required")
- : seenHeaders.has(key.toLowerCase())
- ? input.t("provider.custom.error.duplicate")
- : (() => {
- seenHeaders.add(key.toLowerCase())
- return undefined
- })()
- const valueError = !value ? input.t("provider.custom.error.required") : undefined
- return { key: keyError, value: valueError }
- })
- const headersValid = headers.every((h) => !h.key && !h.value)
- const headerConfig = Object.fromEntries(
- input.form.headers
- .map((h) => ({ key: h.key.trim(), value: h.value.trim() }))
- .filter((h) => !!h.key && !!h.value)
- .map((h) => [h.key, h.value]),
- )
- const err = {
- providerID: idError ?? existsError,
- name: nameError,
- baseURL: urlError,
- }
- const ok = !idError && !existsError && !nameError && !urlError && modelsValid && headersValid
- if (!ok) return { err, models, headers }
- return {
- err,
- models,
- headers,
- result: {
- providerID,
- name,
- key,
- config: {
- npm: OPENAI_COMPATIBLE,
- name,
- ...(env ? { env: [env] } : {}),
- options: {
- baseURL,
- ...(Object.keys(headerConfig).length ? { headers: headerConfig } : {}),
- },
- models: modelConfig,
- },
- },
- }
- }
- let row = 0
- const nextRow = () => `row-${row++}`
- export const modelRow = (): ModelRow => ({ row: nextRow(), id: "", name: "", err: {} })
- export const headerRow = (): HeaderRow => ({ row: nextRow(), key: "", value: "", err: {} })
|