| import { createHash } from 'node:crypto'; |
|
|
| import { readApiErrorMessage } from './api-error'; |
| import { DEFAULT_KIMI_CODE_OAUTH_HOST } from './constants'; |
| import { OAuthUnauthorizedError } from './errors'; |
| import { parseKimiCodeCustomHeaders } from './identity'; |
| import { DEFAULT_KIMI_CODE_BASE_URL, kimiCodeBaseUrl } from './managed-usage'; |
| import { MANAGED_KIMI_MODEL_FIELDS, mergeRefreshedModelAlias } from './model-alias-merge'; |
| import { isRecord } from './utils'; |
|
|
| export const KIMI_CODE_PLATFORM_ID = 'kimi-code'; |
| export const KIMI_CODE_PROVIDER_NAME = 'managed:kimi-code'; |
| export const KIMI_CODE_OAUTH_KEY = 'oauth/kimi-code'; |
| const KIMI_CODE_SCOPED_OAUTH_KEY_PREFIX = 'oauth/kimi-code-env-'; |
|
|
| export type ManagedKimiCodeProtocol = 'kimi' | 'anthropic' | 'openai_responses'; |
|
|
| export function parseModelProtocol(value: unknown): ManagedKimiCodeProtocol | undefined { |
| if (value === 'anthropic') return 'anthropic'; |
| if (value === 'response') return 'openai_responses'; |
| return undefined; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| export type SupportsThinkingType = 'only' | 'no' | 'both'; |
|
|
| export interface ManagedKimiCodeModelInfo { |
| readonly id: string; |
| readonly contextLength: number; |
| readonly supportsReasoning: boolean; |
| readonly supportsImageIn: boolean; |
| readonly supportsVideoIn: boolean; |
| readonly supportsToolUse?: boolean; |
| readonly supportsDynamicTools?: boolean; |
| readonly supportsThinkingType?: SupportsThinkingType; |
| readonly supportEfforts?: readonly string[]; |
| readonly defaultEffort?: string; |
| readonly displayName?: string | undefined; |
| readonly protocol?: ManagedKimiCodeProtocol | undefined; |
| } |
|
|
| export interface ManagedKimiCodeProvisionResult { |
| readonly providerName: typeof KIMI_CODE_PROVIDER_NAME; |
| readonly defaultModel: string; |
| readonly defaultThinking: boolean; |
| readonly models: readonly ManagedKimiCodeModelInfo[]; |
| readonly configPath?: string | undefined; |
| } |
|
|
| export interface FetchManagedKimiCodeModelsOptions { |
| readonly accessToken: string; |
| readonly baseUrl?: string | undefined; |
| readonly fetchImpl?: typeof fetch | undefined; |
| readonly headers?: Record<string, string> | undefined; |
| |
| |
| |
| |
| |
| |
| readonly credentialKind?: 'oauth' | 'apiKey' | undefined; |
| } |
|
|
| export interface ManagedKimiCodeApplyResult { |
| readonly defaultModel: string; |
| readonly defaultThinking: boolean; |
| } |
|
|
| export interface ManagedKimiCodeCleanupResult { |
| readonly providerName: typeof KIMI_CODE_PROVIDER_NAME; |
| readonly removedProvider: boolean; |
| readonly removedModels: readonly string[]; |
| readonly defaultModelCleared: boolean; |
| readonly removedServices: readonly string[]; |
| } |
|
|
| export interface ManagedKimiOAuthRef { |
| readonly storage: 'file' | 'keyring'; |
| readonly key: string; |
| readonly oauthHost?: string | undefined; |
| } |
|
|
| export interface ManagedKimiOAuthRefInput { |
| readonly storage?: 'file' | 'keyring' | undefined; |
| readonly key?: string | undefined; |
| readonly oauthHost?: string | undefined; |
| } |
|
|
| export interface ManagedKimiRuntimeAuth { |
| readonly baseUrl?: string | undefined; |
| readonly oauthRef: ManagedKimiOAuthRef; |
| } |
|
|
| export interface ManagedKimiLoginAuth { |
| readonly baseUrl?: string | undefined; |
| readonly oauthHost?: string | undefined; |
| readonly oauthRef?: ManagedKimiOAuthRef | undefined; |
| } |
|
|
| export interface ManagedKimiEnv { |
| readonly KIMI_CODE_BASE_URL?: string | undefined; |
| readonly KIMI_CODE_OAUTH_HOST?: string | undefined; |
| readonly KIMI_OAUTH_HOST?: string | undefined; |
| } |
|
|
| export class ManagedKimiCodeModelsAuthError extends OAuthUnauthorizedError { |
| readonly status: number; |
| readonly baseUrl: string; |
|
|
| constructor(options: { |
| readonly status: number; |
| readonly baseUrl: string; |
| readonly message: string; |
| readonly credentialKind?: 'oauth' | 'apiKey' | undefined; |
| }) { |
| super( |
| `Kimi Code models endpoint ${options.baseUrl} rejected ${ |
| options.credentialKind === 'apiKey' ? 'the API key' : 'OAuth credentials' |
| }: ${options.message}`, |
| ); |
| this.name = 'ManagedKimiCodeModelsAuthError'; |
| this.status = options.status; |
| this.baseUrl = options.baseUrl; |
| } |
| } |
|
|
| export interface ManagedKimiProviderConfig { |
| type: ManagedKimiCodeProtocol; |
| baseUrl?: string | undefined; |
| apiKey?: string | undefined; |
| oauth?: ManagedKimiOAuthRef | undefined; |
| readonly [key: string]: unknown; |
| } |
|
|
| export interface ManagedKimiModelAliasOverrides { |
| maxContextSize?: number | undefined; |
| maxOutputSize?: number | undefined; |
| capabilities?: string[] | undefined; |
| displayName?: string | undefined; |
| reasoningKey?: string | undefined; |
| adaptiveThinking?: boolean | undefined; |
| supportEfforts?: readonly string[] | undefined; |
| defaultEffort?: string | undefined; |
| readonly [key: string]: unknown; |
| } |
|
|
| export interface ManagedKimiModelAlias { |
| provider: string; |
| model: string; |
| maxContextSize: number; |
| capabilities?: string[] | undefined; |
| supportEfforts?: readonly string[] | undefined; |
| defaultEffort?: string | undefined; |
| displayName?: string | undefined; |
| protocol?: ManagedKimiCodeProtocol; |
| betaApi?: boolean; |
| adaptiveThinking?: boolean | undefined; |
| overrides?: ManagedKimiModelAliasOverrides | undefined; |
| readonly [key: string]: unknown; |
| } |
|
|
| export interface ManagedKimiServiceConfig { |
| baseUrl?: string | undefined; |
| apiKey?: string | undefined; |
| oauth?: ManagedKimiOAuthRef | undefined; |
| } |
|
|
| export interface ManagedKimiServicesConfig { |
| moonshotSearch?: ManagedKimiServiceConfig | undefined; |
| moonshotFetch?: ManagedKimiServiceConfig | undefined; |
| readonly [key: string]: unknown; |
| } |
|
|
| export interface ManagedKimiThinkingShape { |
| enabled?: boolean | undefined; |
| effort?: string | undefined; |
| [key: string]: unknown; |
| } |
|
|
| export interface ManagedKimiConfigShape { |
| providers: Record<string, ManagedKimiProviderConfig | Record<string, unknown>>; |
| models?: Record<string, ManagedKimiModelAlias | Record<string, unknown>> | undefined; |
| defaultModel?: string | undefined; |
| thinking?: ManagedKimiThinkingShape | undefined; |
| services?: ManagedKimiServicesConfig | undefined; |
| [key: string]: unknown; |
| } |
|
|
| export interface ManagedKimiConfigAdapter<TConfig> { |
| read(): Promise<TConfig> | TConfig; |
| write(config: TConfig): Promise<void> | void; |
| apply( |
| config: TConfig, |
| input: { |
| readonly models: readonly ManagedKimiCodeModelInfo[]; |
| readonly baseUrl?: string | undefined; |
| readonly oauthKey?: string | undefined; |
| readonly oauthHost?: string | undefined; |
| readonly preserveDefaultModel?: boolean | undefined; |
| }, |
| ): ManagedKimiCodeApplyResult; |
| remove?(config: TConfig): void; |
| readonly configPath?: string | undefined; |
| } |
|
|
| export interface ProvisionManagedKimiCodeConfigOptions<TConfig> { |
| readonly adapter: ManagedKimiConfigAdapter<TConfig>; |
| readonly accessToken: string; |
| readonly baseUrl?: string | undefined; |
| readonly oauthKey?: string | undefined; |
| readonly oauthHost?: string | undefined; |
| readonly preserveDefaultModel?: boolean | undefined; |
| readonly fetchImpl?: typeof fetch | undefined; |
| readonly headers?: Record<string, string> | undefined; |
| } |
|
|
| function managedModelKey(modelId: string): string { |
| return `${KIMI_CODE_PLATFORM_ID}/${modelId}`; |
| } |
|
|
| interface SelectedDefaultModel { |
| readonly modelKey: string; |
| readonly thinking: boolean; |
| } |
|
|
| function capabilitiesForModel(model: ManagedKimiCodeModelInfo): string[] | undefined { |
| const caps = new Set<string>(); |
| |
| |
| switch (model.supportsThinkingType) { |
| case 'only': |
| caps.add('thinking'); |
| caps.add('always_thinking'); |
| break; |
| case 'both': |
| caps.add('thinking'); |
| break; |
| case 'no': |
| break; |
| case undefined: |
| if (model.supportsReasoning) caps.add('thinking'); |
| break; |
| } |
| if (model.supportsImageIn) caps.add('image_in'); |
| if (model.supportsVideoIn) caps.add('video_in'); |
| if (model.supportsToolUse ?? true) caps.add('tool_use'); |
| if (model.supportsDynamicTools === true) caps.add('dynamically_loaded_tools'); |
| return caps.size > 0 ? [...caps] : undefined; |
| } |
|
|
| function defaultBaseUrl(baseUrl: string | undefined): string { |
| return (baseUrl ?? kimiCodeBaseUrl()).replace(/\/+$/, ''); |
| } |
|
|
| function normalizeBaseUrl(baseUrl: string): string { |
| return baseUrl.replace(/\/+$/, ''); |
| } |
|
|
| function normalizeEndpoint(value: string): string { |
| return value.trim().replace(/\/+$/, ''); |
| } |
|
|
| function persistedOAuthHost(options: { |
| readonly key: string; |
| readonly oauthHost?: string | undefined; |
| }): string | undefined { |
| const oauthHost = options.oauthHost; |
| const normalized = normalizeEndpoint(oauthHost ?? DEFAULT_KIMI_CODE_OAUTH_HOST); |
| if ( |
| options.key === KIMI_CODE_OAUTH_KEY && |
| normalized === normalizeEndpoint(DEFAULT_KIMI_CODE_OAUTH_HOST) |
| ) { |
| return undefined; |
| } |
| return normalized; |
| } |
|
|
| function managedOAuthRef(options: { |
| readonly key: string; |
| readonly oauthHost?: string | undefined; |
| readonly storage?: 'file' | 'keyring' | undefined; |
| }): ManagedKimiOAuthRef { |
| const oauthHost = persistedOAuthHost(options); |
| return { |
| storage: options.storage ?? 'file', |
| key: options.key, |
| oauthHost, |
| }; |
| } |
|
|
| function configuredOAuthRef( |
| oauthRef: ManagedKimiOAuthRefInput | undefined, |
| ): ManagedKimiOAuthRef | undefined { |
| if (oauthRef === undefined) return undefined; |
| const key = oauthRef.key; |
| if (key === undefined) return undefined; |
| return managedOAuthRef({ |
| storage: oauthRef.storage, |
| key, |
| oauthHost: oauthRef.oauthHost, |
| }); |
| } |
|
|
| export function kimiCodeEnvBaseUrl(env: ManagedKimiEnv = process.env): string | undefined { |
| return env.KIMI_CODE_BASE_URL; |
| } |
|
|
| export function kimiCodeEnvOAuthHost(env: ManagedKimiEnv = process.env): string | undefined { |
| return env.KIMI_CODE_OAUTH_HOST ?? env.KIMI_OAUTH_HOST; |
| } |
|
|
| |
| const SHARED_DEFAULT_BASE_URLS: readonly string[] = [ |
| normalizeEndpoint(DEFAULT_KIMI_CODE_BASE_URL), |
| ]; |
|
|
| export function resolveKimiCodeOAuthKey(options: { |
| readonly oauthHost?: string | undefined; |
| readonly baseUrl?: string | undefined; |
| }): string { |
| const oauthHost = normalizeEndpoint(options.oauthHost ?? DEFAULT_KIMI_CODE_OAUTH_HOST); |
| const baseUrl = defaultBaseUrl(options.baseUrl); |
| const defaultOauthHost = normalizeEndpoint(DEFAULT_KIMI_CODE_OAUTH_HOST); |
|
|
| if (oauthHost === defaultOauthHost && SHARED_DEFAULT_BASE_URLS.includes(baseUrl)) { |
| return KIMI_CODE_OAUTH_KEY; |
| } |
|
|
| const digest = createHash('sha256') |
| .update(JSON.stringify({ oauthHost, baseUrl })) |
| .digest('hex') |
| .slice(0, 16); |
| return `${KIMI_CODE_SCOPED_OAUTH_KEY_PREFIX}${digest}`; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export function resolveKimiCodeOAuthRef(options: { |
| readonly oauthHost?: string | undefined; |
| readonly baseUrl?: string | undefined; |
| }): ManagedKimiOAuthRef { |
| return managedOAuthRef({ |
| key: resolveKimiCodeOAuthKey(options), |
| oauthHost: options.oauthHost, |
| }); |
| } |
|
|
| export function resolveKimiCodeRuntimeAuth(options: { |
| readonly configuredBaseUrl?: string | undefined; |
| readonly configuredOAuthRef?: ManagedKimiOAuthRefInput | undefined; |
| readonly env?: ManagedKimiEnv | undefined; |
| }): ManagedKimiRuntimeAuth { |
| const env = options.env ?? process.env; |
| const envBaseUrl = kimiCodeEnvBaseUrl(env); |
| const envOAuthHost = kimiCodeEnvOAuthHost(env); |
| const hasEnvOverride = envBaseUrl !== undefined || envOAuthHost !== undefined; |
| const baseUrl = |
| envBaseUrl !== undefined ? normalizeBaseUrl(envBaseUrl) : options.configuredBaseUrl; |
| const expected = resolveKimiCodeOAuthRef({ |
| oauthHost: hasEnvOverride ? envOAuthHost : options.configuredOAuthRef?.oauthHost, |
| baseUrl, |
| }); |
| const configured = configuredOAuthRef(options.configuredOAuthRef); |
| if (configured === undefined) return { baseUrl, oauthRef: expected }; |
| if (hasEnvOverride) return { baseUrl, oauthRef: expected }; |
| if (configured.key !== expected.key) return { baseUrl, oauthRef: expected }; |
| return { baseUrl, oauthRef: configured }; |
| } |
|
|
| export function resolveKimiCodeLoginAuth(options: { |
| readonly configuredBaseUrl?: string | undefined; |
| readonly configuredOAuthRef?: ManagedKimiOAuthRefInput | undefined; |
| readonly requestedBaseUrl?: string | undefined; |
| readonly requestedOAuthHost?: string | undefined; |
| readonly env?: ManagedKimiEnv | undefined; |
| }): ManagedKimiLoginAuth { |
| const env = options.env ?? process.env; |
| const envBaseUrl = kimiCodeEnvBaseUrl(env); |
| const envOAuthHost = kimiCodeEnvOAuthHost(env); |
| const hasOverride = |
| options.requestedBaseUrl !== undefined || |
| options.requestedOAuthHost !== undefined || |
| envBaseUrl !== undefined || |
| envOAuthHost !== undefined; |
| const baseUrl = |
| options.requestedBaseUrl !== undefined |
| ? normalizeBaseUrl(options.requestedBaseUrl) |
| : envBaseUrl !== undefined |
| ? normalizeBaseUrl(envBaseUrl) |
| : options.configuredBaseUrl; |
| const oauthHost = options.requestedOAuthHost ?? envOAuthHost; |
| if (hasOverride) return { baseUrl, oauthHost }; |
|
|
| const configured = configuredOAuthRef(options.configuredOAuthRef); |
| if (configured === undefined) return { baseUrl, oauthHost }; |
| const expectedKey = resolveKimiCodeOAuthKey({ |
| oauthHost: configured.oauthHost, |
| baseUrl, |
| }); |
| return configured.key === expectedKey |
| ? { baseUrl, oauthHost, oauthRef: configured } |
| : { baseUrl, oauthHost }; |
| } |
|
|
| function toModelInfo(item: unknown): ManagedKimiCodeModelInfo | undefined { |
| if (!isRecord(item) || typeof item['id'] !== 'string' || item['id'].length === 0) { |
| return undefined; |
| } |
| const contextLength = Number(item['context_length']); |
| if (!Number.isInteger(contextLength) || contextLength <= 0) { |
| throw new Error(`Kimi Code model "${item['id']}" must include a positive context_length.`); |
| } |
| const displayName = item['display_name']; |
| const normalizedDisplayName = |
| typeof displayName === 'string' && displayName.length > 0 ? displayName : undefined; |
| const supportsToolUse = Object.hasOwn(item, 'supports_tool_use') |
| ? Boolean(item['supports_tool_use']) |
| : true; |
| |
| |
| const thinkEfforts = parseThinkEfforts(item['think_efforts']); |
| return { |
| id: item['id'], |
| contextLength, |
| supportsReasoning: Boolean(item['supports_reasoning']), |
| supportsImageIn: Boolean(item['supports_image_in']), |
| supportsVideoIn: Boolean(item['supports_video_in']), |
| supportsToolUse, |
| supportsDynamicTools: item['supports_dynamic_tools'] === true, |
| supportsThinkingType: parseSupportsThinkingType(item['supports_thinking_type']), |
| supportEfforts: thinkEfforts.supportEfforts, |
| defaultEffort: thinkEfforts.defaultEffort, |
| displayName: normalizedDisplayName, |
| protocol: parseModelProtocol(item['protocol']), |
| }; |
| } |
|
|
| export function parseStringArray(value: unknown): readonly string[] | undefined { |
| if (!Array.isArray(value)) return undefined; |
| const out = value.filter((v): v is string => typeof v === 'string' && v.length > 0); |
| return out.length > 0 ? out : undefined; |
| } |
|
|
| |
| |
| export function parseSupportsThinkingType(value: unknown): SupportsThinkingType | undefined { |
| return value === 'only' || value === 'no' || value === 'both' ? value : undefined; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| export function parseThinkEfforts(value: unknown): { |
| supportEfforts: readonly string[] | undefined; |
| defaultEffort: string | undefined; |
| } { |
| if (value === null || typeof value !== 'object') { |
| return { supportEfforts: undefined, defaultEffort: undefined }; |
| } |
| const record = value as Record<string, unknown>; |
| |
| |
| if (record['support'] !== true) { |
| return { supportEfforts: undefined, defaultEffort: undefined }; |
| } |
| const rawDefault = record['default_effort']; |
| return { |
| supportEfforts: parseStringArray(record['valid_efforts']), |
| defaultEffort: |
| typeof rawDefault === 'string' && rawDefault.length > 0 ? rawDefault : undefined, |
| }; |
| } |
|
|
| export async function fetchManagedKimiCodeModels( |
| options: FetchManagedKimiCodeModelsOptions, |
| ): Promise<ManagedKimiCodeModelInfo[]> { |
| const fetchImpl = options.fetchImpl ?? fetch; |
| const baseUrl = defaultBaseUrl(options.baseUrl); |
| const response = await fetchImpl(`${baseUrl}/models`, { |
| headers: { |
| ...parseKimiCodeCustomHeaders(), |
| ...options.headers, |
| Authorization: `Bearer ${options.accessToken}`, |
| Accept: 'application/json', |
| }, |
| }); |
| if (!response.ok) { |
| const message = await readApiErrorMessage( |
| response, |
| `Failed to list Kimi Code models (HTTP ${response.status}).`, |
| ); |
| if (response.status === 401 || response.status === 402 || response.status === 403) { |
| throw new ManagedKimiCodeModelsAuthError({ |
| status: response.status, |
| baseUrl, |
| message, |
| credentialKind: options.credentialKind, |
| }); |
| } |
| throw new Error(message); |
| } |
| const payload: unknown = await response.json(); |
| if (!isRecord(payload) || !Array.isArray(payload['data'])) { |
| throw new Error(`Unexpected models response for ${baseUrl}.`); |
| } |
| return payload['data'] |
| .map((item) => toModelInfo(item)) |
| .filter((item): item is ManagedKimiCodeModelInfo => item !== undefined); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export function toManagedModelAlias( |
| providerId: string, |
| model: ManagedKimiCodeModelInfo, |
| ): ManagedKimiModelAlias { |
| const capabilities = capabilitiesForModel(model); |
| |
| |
| |
| |
| |
| |
| const supportsAdaptiveThinking = |
| model.protocol === 'anthropic' && |
| (capabilities?.includes('thinking') === true || |
| capabilities?.includes('always_thinking') === true); |
| return { |
| provider: providerId, |
| model: model.id, |
| maxContextSize: model.contextLength, |
| capabilities, |
| ...(model.displayName !== undefined ? { displayName: model.displayName } : {}), |
| ...(model.supportEfforts !== undefined ? { supportEfforts: model.supportEfforts } : {}), |
| ...(model.defaultEffort !== undefined ? { defaultEffort: model.defaultEffort } : {}), |
| protocol: model.protocol, |
| |
| |
| |
| |
| betaApi: model.protocol === 'anthropic' ? true : undefined, |
| adaptiveThinking: supportsAdaptiveThinking ? true : undefined, |
| }; |
| } |
|
|
| export function applyManagedKimiCodeConfig( |
| config: ManagedKimiConfigShape, |
| options: { |
| readonly models: readonly ManagedKimiCodeModelInfo[]; |
| readonly baseUrl?: string | undefined; |
| readonly oauthKey?: string | undefined; |
| readonly oauthHost?: string | undefined; |
| readonly preserveDefaultModel?: boolean | undefined; |
| }, |
| ): ManagedKimiCodeApplyResult { |
| if (options.models.length === 0) { |
| throw new Error('No models available for Kimi Code.'); |
| } |
| for (const model of options.models) { |
| assertPositiveContextLength(model); |
| } |
|
|
| const baseUrl = defaultBaseUrl(options.baseUrl); |
| const oauth = |
| options.oauthKey !== undefined |
| ? managedOAuthRef({ key: options.oauthKey, oauthHost: options.oauthHost }) |
| : resolveKimiCodeOAuthRef({ baseUrl, oauthHost: options.oauthHost }); |
| const existingModels = config.models ?? {}; |
| const selectedDefault = selectDefaultModel(config, options.models, { |
| preserveExisting: options.preserveDefaultModel === true, |
| }); |
|
|
| config.providers[KIMI_CODE_PROVIDER_NAME] = { |
| type: 'kimi', |
| baseUrl, |
| apiKey: '', |
| oauth, |
| }; |
|
|
| |
| |
| |
| |
| |
| const upstreamKeys = new Set(options.models.map((m) => managedModelKey(m.id))); |
| for (const [key, model] of Object.entries(existingModels)) { |
| if ( |
| isRecord(model) && |
| model['provider'] === KIMI_CODE_PROVIDER_NAME && |
| !upstreamKeys.has(key) |
| ) { |
| delete existingModels[key]; |
| } |
| } |
| for (const model of options.models) { |
| const key = managedModelKey(model.id); |
| const existing = isRecord(existingModels[key]) ? existingModels[key] : {}; |
| existingModels[key] = mergeRefreshedModelAlias( |
| existing, |
| toManagedModelAlias(KIMI_CODE_PROVIDER_NAME, model), |
| MANAGED_KIMI_MODEL_FIELDS, |
| ); |
| } |
|
|
| config.models = existingModels; |
| config.defaultModel = selectedDefault.modelKey; |
| config.thinking = { ...config.thinking, enabled: selectedDefault.thinking }; |
| config.services = { |
| moonshotSearch: { |
| baseUrl: `${baseUrl}/search`, |
| apiKey: '', |
| oauth, |
| }, |
| moonshotFetch: { |
| baseUrl: `${baseUrl}/fetch`, |
| apiKey: '', |
| oauth, |
| }, |
| }; |
|
|
| return { |
| defaultModel: selectedDefault.modelKey, |
| defaultThinking: selectedDefault.thinking, |
| }; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export function applyManagedApiKeyProviderModels( |
| config: ManagedKimiConfigShape, |
| providerId: string, |
| models: readonly ManagedKimiCodeModelInfo[], |
| aliasPrefix: string, |
| ): void { |
| for (const model of models) { |
| assertPositiveContextLength(model); |
| } |
|
|
| const existingModels = config.models ?? {}; |
| |
| |
| |
| const upstreamKeys = new Set(models.map((m) => `${aliasPrefix}${m.id}`)); |
| for (const [key, model] of Object.entries(existingModels)) { |
| if (isRecord(model) && model['provider'] === providerId && !upstreamKeys.has(key)) { |
| delete existingModels[key]; |
| } |
| } |
| for (const model of models) { |
| const key = `${aliasPrefix}${model.id}`; |
| const existing = isRecord(existingModels[key]) ? existingModels[key] : {}; |
| existingModels[key] = mergeRefreshedModelAlias( |
| existing, |
| toManagedModelAlias(providerId, model), |
| MANAGED_KIMI_MODEL_FIELDS, |
| ); |
| } |
|
|
| config.models = existingModels; |
| } |
|
|
| export function applyManagedKimiCodeLogoutConfig(config: ManagedKimiConfigShape): void { |
| delete config.providers[KIMI_CODE_PROVIDER_NAME]; |
|
|
| let removedDefaultModel = false; |
| const existingModels = config.models ?? {}; |
| for (const [key, model] of Object.entries(existingModels)) { |
| if (!isRecord(model) || model['provider'] !== KIMI_CODE_PROVIDER_NAME) continue; |
| delete existingModels[key]; |
| if (config.defaultModel === key) removedDefaultModel = true; |
| } |
| config.models = existingModels; |
|
|
| if (removedDefaultModel) { |
| config.defaultModel = undefined; |
| } |
|
|
| if (config['defaultProvider'] === KIMI_CODE_PROVIDER_NAME) { |
| config['defaultProvider'] = undefined; |
| } |
|
|
| if (config.services !== undefined) { |
| delete config.services.moonshotSearch; |
| delete config.services.moonshotFetch; |
| if (Object.keys(config.services).length === 0) { |
| config.services = undefined; |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
| function forcedThinking( |
| model: ManagedKimiCodeModelInfo | undefined, |
| fallback: boolean, |
| ): boolean { |
| if (model?.supportsThinkingType === 'only') return true; |
| if (model?.supportsThinkingType === 'no') return false; |
| return fallback; |
| } |
|
|
| function selectDefaultModel( |
| config: ManagedKimiConfigShape, |
| models: readonly ManagedKimiCodeModelInfo[], |
| options: { readonly preserveExisting: boolean }, |
| ): SelectedDefaultModel { |
| const firstModel = models[0]; |
| if (firstModel === undefined) { |
| throw new Error('No models available for Kimi Code.'); |
| } |
|
|
| const managedModels = new Map(models.map((model) => [managedModelKey(model.id), model])); |
| const existingModels = config.models ?? {}; |
| const currentDefault = |
| typeof config.defaultModel === 'string' && config.defaultModel.length > 0 |
| ? config.defaultModel |
| : undefined; |
|
|
| if ( |
| options.preserveExisting && |
| currentDefault !== undefined && |
| canPreserveDefaultModel(existingModels, currentDefault, managedModels) |
| ) { |
| const preservedModel = managedModels.get(currentDefault); |
| return { |
| modelKey: currentDefault, |
| thinking: forcedThinking( |
| preservedModel, |
| config.thinking?.enabled ?? preservedModel?.supportsReasoning ?? false, |
| ), |
| }; |
| } |
|
|
| return { |
| modelKey: managedModelKey(firstModel.id), |
| thinking: forcedThinking(firstModel, config.thinking?.enabled ?? firstModel.supportsReasoning), |
| }; |
| } |
|
|
| function canPreserveDefaultModel( |
| existingModels: Record<string, ManagedKimiModelAlias | Record<string, unknown>>, |
| defaultModel: string, |
| managedModels: ReadonlyMap<string, ManagedKimiCodeModelInfo>, |
| ): boolean { |
| if (managedModels.has(defaultModel)) return true; |
| const existing = existingModels[defaultModel]; |
| return isRecord(existing) && existing['provider'] !== KIMI_CODE_PROVIDER_NAME; |
| } |
|
|
| export function clearManagedKimiCodeConfig( |
| config: ManagedKimiConfigShape, |
| ): ManagedKimiCodeCleanupResult { |
| const removedProvider = Object.hasOwn(config.providers, KIMI_CODE_PROVIDER_NAME); |
| delete config.providers[KIMI_CODE_PROVIDER_NAME]; |
|
|
| const removedModels: string[] = []; |
| const models = config.models; |
| if (models !== undefined) { |
| for (const [key, model] of Object.entries(models)) { |
| if (!isRecord(model) || model['provider'] !== KIMI_CODE_PROVIDER_NAME) continue; |
| delete models[key]; |
| removedModels.push(key); |
| } |
| } |
|
|
| let defaultModelCleared = false; |
| if (typeof config.defaultModel === 'string' && removedModels.includes(config.defaultModel)) { |
| config.defaultModel = undefined; |
| defaultModelCleared = true; |
| } |
|
|
| const removedServices: string[] = []; |
| if (config.services?.moonshotSearch !== undefined) { |
| delete config.services.moonshotSearch; |
| removedServices.push('moonshotSearch'); |
| } |
| if (config.services?.moonshotFetch !== undefined) { |
| delete config.services.moonshotFetch; |
| removedServices.push('moonshotFetch'); |
| } |
| if (config.services !== undefined && Object.keys(config.services).length === 0) { |
| config.services = undefined; |
| } |
|
|
| return { |
| providerName: KIMI_CODE_PROVIDER_NAME, |
| removedProvider, |
| removedModels, |
| defaultModelCleared, |
| removedServices, |
| }; |
| } |
|
|
| function assertPositiveContextLength(model: ManagedKimiCodeModelInfo): void { |
| if (!Number.isInteger(model.contextLength) || model.contextLength <= 0) { |
| throw new Error(`Kimi Code model "${model.id}" must include a positive context_length.`); |
| } |
| } |
|
|
| export async function provisionManagedKimiCodeConfigAfterLogin( |
| options: ProvisionManagedKimiCodeConfigOptions<ManagedKimiConfigShape>, |
| ): Promise<ManagedKimiCodeProvisionResult> { |
| return provisionManagedKimiCodeConfig(options); |
| } |
|
|
| export async function provisionManagedKimiCodeConfig<TConfig>( |
| options: ProvisionManagedKimiCodeConfigOptions<TConfig>, |
| ): Promise<ManagedKimiCodeProvisionResult> { |
| const models = await fetchManagedKimiCodeModels(options); |
| const config = await options.adapter.read(); |
| const applied = options.adapter.apply(config, { |
| models, |
| baseUrl: options.baseUrl, |
| oauthKey: options.oauthKey, |
| oauthHost: options.oauthHost, |
| preserveDefaultModel: options.preserveDefaultModel, |
| }); |
| await options.adapter.write(config); |
| return { |
| providerName: KIMI_CODE_PROVIDER_NAME, |
| defaultModel: applied.defaultModel, |
| defaultThinking: applied.defaultThinking, |
| models, |
| configPath: options.adapter.configPath, |
| }; |
| } |
|
|