mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-10-30 19:32:39 +08:00
refactor(vscode): reduce validateCustomProvider complexity by extracting helpers
Extract checkModel, checkVariant, checkHeader, checkProviderID, serializeModel, serializeVariant, and resolveEnv from validateCustomProvider to bring it under the ESLint complexity limit of 20. Also remove the now-unnecessary complexity exception for CustomProviderDialog.tsx.
This commit is contained in:
@@ -79,10 +79,6 @@ export default [
|
||||
],
|
||||
rules: { complexity: ["error", 27] },
|
||||
},
|
||||
{
|
||||
files: ["webview-ui/src/components/settings/CustomProviderDialog.tsx"],
|
||||
rules: { complexity: ["error", 26] },
|
||||
},
|
||||
{
|
||||
files: ["src/agent-manager/WorktreeStateManager.ts"],
|
||||
rules: { complexity: ["error", 24] },
|
||||
|
||||
+95
-92
@@ -1,4 +1,4 @@
|
||||
import type { ModelEntry } from "./CustomProviderModelCard"
|
||||
import type { ModelEntry, VariantEntry } from "./CustomProviderModelCard"
|
||||
|
||||
type Translator = (key: string, params?: Record<string, string>) => string
|
||||
|
||||
@@ -54,22 +54,95 @@ type ValidateResult = {
|
||||
const PROVIDER_ID = /^[a-z0-9][a-z0-9-_]*$/
|
||||
const OPENAI_COMPATIBLE = "@ai-sdk/openai-compatible"
|
||||
|
||||
function checkVariant(v: VariantEntry, seen: Set<string>, t: Translator) {
|
||||
const n = v.name.trim()
|
||||
if (!n) return { name: t("provider.custom.error.required") }
|
||||
if (seen.has(n)) return { name: t("provider.custom.error.duplicate") }
|
||||
seen.add(n)
|
||||
return { name: undefined }
|
||||
}
|
||||
|
||||
function checkModel(m: ModelEntry, seenModels: Set<string>, t: Translator) {
|
||||
const id = m.id.trim()
|
||||
let idErr: string | undefined
|
||||
if (!id) idErr = t("provider.custom.error.required")
|
||||
else if (seenModels.has(id)) idErr = t("provider.custom.error.duplicate")
|
||||
else seenModels.add(id)
|
||||
|
||||
const nameErr = !m.name.trim() ? t("provider.custom.error.required") : undefined
|
||||
const seen = new Set<string>()
|
||||
const variants = m.reasoning ? m.variants.map((v) => checkVariant(v, seen, t)) : []
|
||||
return { id: idErr, name: nameErr, variants }
|
||||
}
|
||||
|
||||
function checkHeader(h: HeaderRow, seenKeys: Set<string>, t: Translator) {
|
||||
const key = h.key.trim()
|
||||
const value = h.value.trim()
|
||||
if (!key && !value) return {}
|
||||
|
||||
let keyErr: string | undefined
|
||||
if (!key) keyErr = t("provider.custom.error.required")
|
||||
else if (seenKeys.has(key.toLowerCase())) keyErr = t("provider.custom.error.duplicate")
|
||||
else seenKeys.add(key.toLowerCase())
|
||||
|
||||
const valueErr = !value ? t("provider.custom.error.required") : undefined
|
||||
return { key: keyErr, value: valueErr }
|
||||
}
|
||||
|
||||
function checkProviderID(id: string, editing: boolean, disabled: string[], existing: Set<string>, t: Translator) {
|
||||
const idErr = !id
|
||||
? t("provider.custom.error.providerID.required")
|
||||
: !PROVIDER_ID.test(id)
|
||||
? t("provider.custom.error.providerID.format")
|
||||
: undefined
|
||||
const existsErr =
|
||||
idErr || editing || !existing.has(id) || disabled.includes(id)
|
||||
? undefined
|
||||
: t("provider.custom.error.providerID.exists")
|
||||
return { idErr, existsErr }
|
||||
}
|
||||
|
||||
function serializeVariant(v: VariantEntry): [string, Record<string, unknown>] {
|
||||
const cfg: Record<string, unknown> = {}
|
||||
if (v.enableThinking !== undefined) cfg.enable_thinking = v.enableThinking
|
||||
if (v.thinking !== undefined) cfg.thinking = { type: v.thinking }
|
||||
if (v.reasoningEffort !== undefined) cfg.reasoningEffort = v.reasoningEffort
|
||||
if (v.chatTemplateArgs !== undefined) cfg.chat_template_args = { enable_thinking: v.chatTemplateArgs }
|
||||
return [v.name.trim(), cfg]
|
||||
}
|
||||
|
||||
function serializeModel(m: ModelEntry): [string, Record<string, unknown>] {
|
||||
const ventries = m.reasoning ? m.variants.filter((v) => v.name.trim()).map(serializeVariant) : []
|
||||
const entry: Record<string, unknown> = { name: m.name.trim() }
|
||||
if (m.reasoning) entry.reasoning = true
|
||||
if (ventries.length > 0) entry.variants = Object.fromEntries(ventries)
|
||||
return [m.id.trim(), entry]
|
||||
}
|
||||
|
||||
function resolveEnv(rawEnv: string | undefined, savedEnv: string[] | undefined) {
|
||||
if (rawEnv) return { env: [rawEnv] }
|
||||
if (savedEnv) return { env: savedEnv }
|
||||
return {}
|
||||
}
|
||||
|
||||
export function validateCustomProvider(input: ValidateArgs): ValidateResult {
|
||||
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 rawEnv = apiKey.match(/^\{env:([^}]+)\}$/)?.[1]?.trim()
|
||||
// When editing and apiKey is empty, preserve existing env from the original config
|
||||
const existingEnv = input.editing && !apiKey ? input.existingEnv : undefined
|
||||
const key = apiKey && !env ? apiKey : undefined
|
||||
const savedEnv = input.editing && !apiKey ? input.existingEnv : undefined
|
||||
const key = apiKey && !rawEnv ? 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 { idErr, existsErr } = checkProviderID(
|
||||
providerID,
|
||||
input.editing,
|
||||
input.disabledProviders,
|
||||
input.existingProviderIDs,
|
||||
input.t,
|
||||
)
|
||||
|
||||
const nameError = !name ? input.t("provider.custom.error.name.required") : undefined
|
||||
const urlError = !baseURL
|
||||
@@ -78,102 +151,32 @@ export function validateCustomProvider(input: ValidateArgs): ValidateResult {
|
||||
? input.t("provider.custom.error.baseURL.format")
|
||||
: undefined
|
||||
|
||||
const disabled = input.disabledProviders.includes(providerID)
|
||||
const existsError = idError
|
||||
? undefined
|
||||
: input.editing
|
||||
? undefined
|
||||
: input.existingProviderIDs.has(providerID) && !disabled
|
||||
? input.t("provider.custom.error.providerID.exists")
|
||||
: undefined
|
||||
|
||||
const seenModels = new Set<string>()
|
||||
const modelErrors = input.form.models.map((m) => {
|
||||
const id = m.id.trim()
|
||||
const modelIdError = !id
|
||||
? input.t("provider.custom.error.required")
|
||||
: seenModels.has(id)
|
||||
? input.t("provider.custom.error.duplicate")
|
||||
: (() => {
|
||||
seenModels.add(id)
|
||||
return undefined
|
||||
})()
|
||||
const modelNameError = !m.name.trim() ? input.t("provider.custom.error.required") : undefined
|
||||
const seen = new Set<string>()
|
||||
const verrs = m.reasoning
|
||||
? m.variants.map((v) => {
|
||||
const n = v.name.trim()
|
||||
const vErr = !n
|
||||
? input.t("provider.custom.error.required")
|
||||
: seen.has(n)
|
||||
? input.t("provider.custom.error.duplicate")
|
||||
: (() => {
|
||||
seen.add(n)
|
||||
return undefined
|
||||
})()
|
||||
return { name: vErr }
|
||||
})
|
||||
: []
|
||||
return { id: modelIdError, name: modelNameError, variants: verrs }
|
||||
})
|
||||
const modelErrors = input.form.models.map((m) => checkModel(m, seenModels, input.t))
|
||||
const modelsValid = modelErrors.every((m) => !m.id && !m.name && m.variants.every((v) => !v.name))
|
||||
const models = Object.fromEntries(
|
||||
input.form.models.map((m) => {
|
||||
const ventries = m.reasoning
|
||||
? m.variants
|
||||
.filter((v) => v.name.trim())
|
||||
.map((v) => {
|
||||
const cfg: Record<string, unknown> = {}
|
||||
if (v.enableThinking !== undefined) cfg.enable_thinking = v.enableThinking
|
||||
if (v.thinking !== undefined) cfg.thinking = { type: v.thinking }
|
||||
if (v.reasoningEffort !== undefined) cfg.reasoningEffort = v.reasoningEffort
|
||||
if (v.chatTemplateArgs !== undefined) cfg.chat_template_args = { enable_thinking: v.chatTemplateArgs }
|
||||
return [v.name.trim(), cfg]
|
||||
})
|
||||
: []
|
||||
const entry: Record<string, unknown> = { name: m.name.trim() }
|
||||
if (m.reasoning) entry.reasoning = true
|
||||
if (ventries.length > 0) entry.variants = Object.fromEntries(ventries)
|
||||
return [m.id.trim(), entry]
|
||||
}),
|
||||
)
|
||||
|
||||
const seenHeaders = new Set<string>()
|
||||
const headerErrors = input.form.headers.map((h) => {
|
||||
const hKey = h.key.trim()
|
||||
const value = h.value.trim()
|
||||
|
||||
if (!hKey && !value) return {}
|
||||
const keyError = !hKey
|
||||
? input.t("provider.custom.error.required")
|
||||
: seenHeaders.has(hKey.toLowerCase())
|
||||
? input.t("provider.custom.error.duplicate")
|
||||
: (() => {
|
||||
seenHeaders.add(hKey.toLowerCase())
|
||||
return undefined
|
||||
})()
|
||||
const valueError = !value ? input.t("provider.custom.error.required") : undefined
|
||||
return { key: keyError, value: valueError }
|
||||
})
|
||||
const headerErrors = input.form.headers.map((h) => checkHeader(h, seenHeaders, input.t))
|
||||
const headersValid = headerErrors.every((h) => !h.key && !h.value)
|
||||
const headers = 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 errors: FormErrors = {
|
||||
providerID: idError ?? existsError,
|
||||
providerID: idErr ?? existsErr,
|
||||
name: nameError,
|
||||
baseURL: urlError,
|
||||
models: modelErrors,
|
||||
headers: headerErrors,
|
||||
}
|
||||
|
||||
const ok = !idError && !existsError && !nameError && !urlError && modelsValid && headersValid
|
||||
const ok = !idErr && !existsErr && !nameError && !urlError && modelsValid && headersValid
|
||||
if (!ok) return { errors }
|
||||
|
||||
const headers = 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 options = {
|
||||
baseURL,
|
||||
...(Object.keys(headers).length ? { headers } : {}),
|
||||
@@ -188,9 +191,9 @@ export function validateCustomProvider(input: ValidateArgs): ValidateResult {
|
||||
config: {
|
||||
npm: OPENAI_COMPATIBLE,
|
||||
name,
|
||||
...(env ? { env: [env] } : existingEnv ? { env: existingEnv } : {}),
|
||||
...resolveEnv(rawEnv, savedEnv),
|
||||
options,
|
||||
models,
|
||||
models: Object.fromEntries(input.form.models.map(serializeModel)),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user