@klarkxy/dsh-ai-services 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["fail"],"sources":["../src/errors.ts","../src/storage.ts","../src/rpc.ts","../src/generate.ts","../src/queue.ts","../src/routing.ts","../src/service.ts","../src/index.ts"],"sourcesContent":["export class AiServicesError extends Error {\n constructor(message: string, readonly code: string) {\n super(message)\n this.name = 'AiServicesError'\n }\n}\n\nexport const AI_UNKNOWN_PURPOSE = 'AI_UNKNOWN_PURPOSE'\nexport const AI_UNKNOWN_ROLE = 'AI_UNKNOWN_ROLE'\nexport const AI_ROLE_UNSET = 'AI_ROLE_UNSET'\nexport const AI_INVALID_ROUTE = 'AI_INVALID_ROUTE'\nexport const AI_SESSION_UNAVAILABLE = 'AI_SESSION_UNAVAILABLE'\nexport const AI_SESSION_INVALID = 'AI_SESSION_INVALID'\nexport const AI_POLICY_CONFLICT = 'AI_POLICY_CONFLICT'\nexport const AI_POLICY_INVALID = 'AI_POLICY_INVALID'\nexport const AI_POLICY_SAVE_FAILED = 'AI_POLICY_SAVE_FAILED'\nexport const AI_DUPLICATE_PURPOSE = 'AI_DUPLICATE_PURPOSE'\nexport const AI_INACTIVE = 'AI_INACTIVE'\nexport const AI_INPUT_TOO_LARGE = 'AI_INPUT_TOO_LARGE'\n\nexport function fail(code: string, message: string): never {\n throw new AiServicesError(message, code)\n}\n\nexport function isAbortError(error: unknown): boolean {\n if (!error || typeof error !== 'object') return false\n const name = 'name' in error ? String(error.name) : ''\n const code = 'code' in error ? String((error as { code?: unknown }).code) : ''\n return name === 'AbortError' || name === 'TimeoutError' || code === 'ABORT_ERR' || code === 'ABORTED'\n}\n\nexport async function abortable<T>(work: () => Promise<T>, signal: AbortSignal): Promise<T> {\n signal.throwIfAborted()\n let off = () => {}\n const aborted = new Promise<never>((_, reject) => {\n const onAbort = () => reject(signal.reason instanceof Error\n ? signal.reason\n : Object.assign(new Error('This operation was aborted'), { name: 'AbortError' }))\n signal.addEventListener('abort', onAbort, { once: true })\n off = () => signal.removeEventListener('abort', onAbort)\n })\n try {\n return await Promise.race([Promise.resolve().then(work), aborted])\n } finally { off() }\n}\n\nexport function publicCallError(code: string): string {\n if (code === 'NO_ADAPTER') return '指定的模型供应不可用,未改用其他模型。'\n if (code === 'UNSUPPORTED_REASONING_EFFORT') return '该模型不支持所选推理强度,未改用其他设置。'\n if (code === 'INVALID_CREDENTIAL' || code === 'AUTH' || code === 'MISSING_CREDENTIAL') return '模型凭据不可用。'\n if (code === 'QUOTA') return '模型额度不足。'\n if (code === 'CONTEXT_WINDOW_EXCEEDED') return '输入超出模型上下文。'\n return '模型调用失败。'\n}\n","import { defineDomain, domainTable } from '@deepseek-ai/dsh-storage-domain'\nimport { z } from 'zod'\nimport type { AiPolicy, ModelRole, ModelRoute, ModelTarget, PurposeSpec, UsageReceipt } from './contracts.ts'\nimport { AI_POLICY_INVALID, fail } from './errors.ts'\n\nexport const POLICY_KEY = 'current'\nexport const RECEIPTS_KEY = 'log'\nexport const RECEIPT_LIMIT = 100\n\nexport const DEFAULT_LIMITS = {\n concurrency: 1,\n timeoutMs: 60_000,\n maxInputChars: 32_000,\n maxOutputTokens: 2048,\n maxAttempts: 2,\n} as const\n\nconst roleSchema = z.enum(['normal', 'weak', 'strong', 'fantasy'])\n\nexport const modelRouteSchema = z.object({\n provider: z.string().trim().min(1).max(128),\n model: z.string().trim().min(1).max(256),\n reasoningEffort: z.string().trim().min(1).max(64).optional(),\n}).strict()\n\nexport const modelTargetSchema = z.discriminatedUnion('kind', [\n z.object({ kind: z.literal('role'), role: roleSchema }).strict(),\n z.object({ kind: z.literal('session') }).strict(),\n z.object({ kind: z.literal('model') }).merge(modelRouteSchema).strict(),\n])\n\nexport const limitsSchema = z.object({\n concurrency: z.number().int().min(1).max(8),\n timeoutMs: z.number().int().min(1_000).max(300_000),\n maxInputChars: z.number().int().min(1).max(200_000),\n maxOutputTokens: z.number().int().min(1).max(8_192),\n maxAttempts: z.number().int().min(1).max(5),\n}).strict()\n\nexport const policyDataSchema = z.object({\n roles: z.object({\n normal: modelRouteSchema.optional(),\n weak: modelRouteSchema.optional(),\n strong: modelRouteSchema.optional(),\n fantasy: modelRouteSchema.optional(),\n }).strict(),\n purposes: z.record(z.string().min(1).max(80), modelTargetSchema).refine(value => Object.keys(value).length <= 200),\n limits: limitsSchema,\n}).strict()\n\nexport const policySchema = policyDataSchema.extend({\n revision: z.number().int().nonnegative(),\n}).strict()\n\nexport const storedPolicySchema = policySchema.extend({ imports: z.array(z.string().min(1).max(80)).max(100).optional() }).strict()\n\nexport const updatePolicySchema = z.object({\n expectedRevision: z.number().int().nonnegative(),\n policy: policyDataSchema,\n}).strict()\n\nexport const resolveRpcSchema = z.object({\n purpose: z.string().min(1).max(80),\n sessionId: z.string().min(1).max(128).optional(),\n override: modelTargetSchema.optional(),\n}).strict()\n\nconst usageReceiptSchema = z.object({\n id: z.string().min(1).max(80),\n plugin: z.string().min(1).max(80),\n purpose: z.string().min(1).max(80),\n sessionId: z.string().max(128).optional(),\n sourceVersion: z.string().max(128),\n contractVersion: z.number().int().optional(),\n promptVersion: z.string().max(80).optional(),\n schemaVersion: z.string().max(80).optional(),\n route: z.object({\n provider: z.string(),\n model: z.string(),\n reasoningEffort: z.string().optional(),\n source: z.enum(['override', 'purpose', 'default']),\n target: modelTargetSchema,\n policyRevision: z.number().int().nonnegative(),\n inheritedRole: roleSchema.optional(),\n }).strict().optional(),\n status: z.enum(['success', 'failed', 'cancelled', 'superseded', 'skipped']),\n attempts: z.number().int().nonnegative(),\n inputTokens: z.number().int().nonnegative().optional(),\n outputTokens: z.number().int().nonnegative().optional(),\n cost: z.null(),\n startedAt: z.number(),\n finishedAt: z.number(),\n error: z.string().max(240).optional(),\n}).strict()\n\nexport const storedReceiptsSchema = z.object({\n items: z.array(usageReceiptSchema).max(RECEIPT_LIMIT),\n}).strict()\n\nexport const purposeSpecSchema = z.object({\n id: z.string().regex(/^[A-Za-z][A-Za-z0-9._:-]{0,79}$/),\n label: z.string().trim().min(1).max(80),\n defaultTarget: modelTargetSchema,\n maxOutputTokens: z.number().int().min(1).max(8_192).optional(),\n maxInputChars: z.number().int().min(1).max(200_000).optional(),\n timeoutMs: z.number().int().min(1_000).max(300_000).optional(),\n}).strict()\n\nexport function defaultPolicy(): AiPolicy {\n return {\n revision: 0,\n roles: {},\n purposes: {},\n limits: { ...DEFAULT_LIMITS },\n }\n}\n\nexport function clonePolicy(policy: AiPolicy): AiPolicy {\n return structuredClone(policy)\n}\n\nexport function parsePolicy(value: unknown): AiPolicy {\n const parsed = policySchema.safeParse(value)\n if (!parsed.success) fail(AI_POLICY_INVALID, '模型策略格式无效。')\n return parsed.data\n}\n\nexport function parsePolicyData(value: unknown): Omit<AiPolicy, 'revision'> {\n const parsed = policyDataSchema.safeParse(value)\n if (!parsed.success) fail(AI_POLICY_INVALID, '模型策略格式无效。')\n return parsed.data\n}\n\nexport function sanitizeReceipt(receipt: UsageReceipt): z.output<typeof usageReceiptSchema> {\n return usageReceiptSchema.parse({\n ...receipt,\n cost: null,\n error: receipt.error?.slice(0, 240),\n })\n}\n\nexport function boundedReceipts(items: UsageReceipt[]): z.output<typeof storedReceiptsSchema>['items'] {\n return storedReceiptsSchema.parse({ items: items.map(sanitizeReceipt).slice(-RECEIPT_LIMIT) }).items\n}\n\nexport const aiServicesDomain = defineDomain({\n name: 'dsh_editor_ai_services',\n version: 1,\n tables: {\n policy: domainTable<string, z.output<typeof storedPolicySchema>>(storedPolicySchema),\n receipts: domainTable<string, z.output<typeof storedReceiptsSchema>>(storedReceiptsSchema),\n },\n})\n\nexport function isModelRole(value: unknown): value is ModelRole {\n return value === 'normal' || value === 'weak' || value === 'strong' || value === 'fantasy'\n}\n\nexport function cloneRoute(route: ModelRoute): ModelRoute {\n return route.reasoningEffort\n ? { provider: route.provider, model: route.model, reasoningEffort: route.reasoningEffort }\n : { provider: route.provider, model: route.model }\n}\n\nexport function cloneTarget(target: ModelTarget): ModelTarget {\n return structuredClone(target)\n}\n\nexport function clonePurpose(spec: PurposeSpec): PurposeSpec {\n return structuredClone(spec)\n}\n","import type { AiServices, RpcResult } from './contracts.ts'\nimport { AiServicesError } from './errors.ts'\nimport { resolveRpcSchema, updatePolicySchema } from './storage.ts'\n\nfunction fail(code: string, message: string): RpcResult {\n return { ok: false, error: { code, message } }\n}\n\nfunction mapError(error: unknown): RpcResult {\n if (error instanceof AiServicesError) return fail(error.code, error.message)\n return fail('AI_REQUEST_FAILED', '操作失败,请刷新后重试。')\n}\n\nexport async function handleAiRpc(\n service: AiServices & { storageFailedFlag?: boolean },\n endpoint: string,\n payload: unknown,\n signal: AbortSignal,\n): Promise<RpcResult> {\n try {\n signal.throwIfAborted()\n if (endpoint === 'status') {\n if (payload !== undefined && payload !== null && (typeof payload !== 'object' || Array.isArray(payload) || Object.keys(payload as object).length > 0)) {\n return fail('AI_INVALID_REQUEST', '未知操作。')\n }\n return {\n ok: true,\n value: {\n policy: service.getPolicy(),\n purposes: service.purposes(),\n storageFailed: Boolean(service.storageFailedFlag),\n },\n }\n }\n if (endpoint === 'update') {\n const parsed = updatePolicySchema.safeParse(payload)\n if (!parsed.success) return fail('AI_INVALID_REQUEST', '策略格式无效。')\n return { ok: true, value: await service.updatePolicy(parsed.data.policy, parsed.data.expectedRevision) }\n }\n if (endpoint === 'resolve') {\n const parsed = resolveRpcSchema.safeParse(payload)\n if (!parsed.success) return fail('AI_INVALID_REQUEST', '路由请求无效。')\n return { ok: true, value: await service.resolve(parsed.data.purpose, parsed.data.sessionId, parsed.data.override) }\n }\n if (endpoint === 'usage') {\n if (payload !== undefined && payload !== null && (typeof payload !== 'object' || Array.isArray(payload) || Object.keys(payload as object).length > 0)) {\n return fail('AI_INVALID_REQUEST', '未知操作。')\n }\n return { ok: true, value: service.usage() }\n }\n return fail('AI_INVALID_REQUEST', '未知操作。')\n } catch (error) {\n if (signal.aborted) return fail('AI_CANCELLED', '请求已取消。')\n return mapError(error)\n }\n}\n","import { createInsertCollector } from './insert-output.ts'\nimport {\n BlockAssembler, LlmError, ReasoningEffortId, createUserMessage,\n type GenerateOptions, type LlmRuntime, type StreamChunk,\n} from '@deepseek-ai/dsh-llm'\nimport { producerMessageSource, type ResolvedRoute } from './contracts.ts'\nimport { abortable, isAbortError, publicCallError } from './errors.ts'\n\nexport type LlmGenerate = Pick<LlmRuntime, 'prepareCall'>\n\nexport type GenerateOutcome = {\n text: string\n attempts: number\n inputTokens?: number\n outputTokens?: number\n error?: string\n status: 'success' | 'failed' | 'cancelled'\n}\n\nconst PERMANENT_CODES = new Set([\n 'NO_ADAPTER', 'UNSUPPORTED_REASONING_EFFORT', 'INVALID_PREPARED_CALL',\n 'INVALID_CREDENTIAL', 'AUTH', 'MISSING_CREDENTIAL', 'QUOTA', 'CONTEXT_WINDOW_EXCEEDED',\n])\n\nfunction joinText(assembler: BlockAssembler): string {\n return assembler.blocks()\n .filter((block): block is { type: 'text'; text: string } => block.type === 'text')\n .map(block => block.text)\n .join('')\n}\n\nasync function consume(\n stream: AsyncIterable<StreamChunk>,\n signal: AbortSignal,\n live: () => boolean,\n insert?: { maxChars: number; stop(): void },\n): Promise<{ assembler: BlockAssembler; sawFinish: boolean; boundedText?: string; visibleText?: string }> {\n const assembler = new BlockAssembler()\n let sawFinish = false\n const collector = insert ? createInsertCollector(insert.maxChars) : undefined\n let boundedText: string | undefined\n const iterator = stream[Symbol.asyncIterator]()\n try {\n while (live()) {\n const item = await abortable(() => iterator.next(), signal)\n if (item.done) break\n if (item.value.type === 'finish') sawFinish = true\n assembler.push(item.value)\n if (collector && item.value.type === 'text-delta') {\n collector.append(item.value.text)\n if (collector.full && !sawFinish && !assembler.blocks().some(block => block.type === 'tool-call')) {\n boundedText = collector.text()\n insert!.stop()\n break\n }\n }\n }\n } finally {\n try { const closing = iterator.return?.(); if (closing) void closing.catch(() => {}) } catch { /* ignore a producer that does not honor abort */ }\n }\n return { assembler, sawFinish, boundedText, visibleText: collector?.text() }\n}\n\nfunction outcomeFromAssembler(\n assembler: BlockAssembler,\n signal: AbortSignal,\n sawFinish: boolean,\n): Omit<GenerateOutcome, 'attempts'> {\n if (signal.aborted) return { text: '', status: 'cancelled', error: '调用已取消。' }\n const finish = assembler.finish\n const usage = assembler.usage\n const tokens = usage\n ? { inputTokens: usage.inputTokens, outputTokens: usage.outputTokens }\n : {}\n if (finish.kind === 'aborted') return { text: '', status: 'cancelled', error: '调用已取消。', ...tokens }\n if (finish.kind === 'error') {\n return { text: '', status: 'failed', error: publicCallError(finish.failure.code), ...tokens }\n }\n if (!sawFinish) return { text: '', status: 'failed', error: '模型调用未完成。', ...tokens }\n if (finish.kind === 'max-tokens') return { text: '', status: 'failed', error: '输出被截断。', ...tokens }\n if (finish.kind === 'tool-calls' || assembler.blocks().some(block => block.type === 'tool-call')) {\n return { text: '', status: 'failed', error: '辅助调用不使用工具。', ...tokens }\n }\n if (finish.kind !== 'stop') return { text: '', status: 'failed', error: '模型调用失败。', ...tokens }\n return { text: joinText(assembler), status: 'success', ...tokens }\n}\n\nexport async function generateAuxiliary(input: {\n llm: LlmGenerate\n plugin: string\n route: ResolvedRoute\n system: string\n text: string\n maxTokens: number\n maxAttempts: number\n insert?: { maxChars: number }\n signal: AbortSignal\n sessionId?: string\n live: () => boolean\n}): Promise<GenerateOutcome> {\n const { llm, plugin, route, system, text, maxTokens, maxAttempts, signal, sessionId, live } = input\n let last: GenerateOutcome = { text: '', attempts: 0, status: 'failed', error: '模型调用失败。' }\n const boundAttempts = Math.max(1, Math.min(5, maxAttempts))\n for (let attempt = 1; attempt <= boundAttempts; attempt += 1) {\n if (signal.aborted) return { text: '', attempts: attempt - 1, status: 'cancelled', error: '调用已取消。' }\n if (!live()) return { ...last, attempts: attempt - 1 }\n try {\n const effort = route.reasoningEffort ? ReasoningEffortId(route.reasoningEffort) : undefined\n const prepared = await llm.prepareCall({\n provider: route.provider,\n model: route.model,\n ...(effort ? { reasoningEffort: effort } : {}),\n maxTokens,\n }, signal)\n if (signal.aborted) return { text: '', attempts: attempt - 1, status: 'cancelled', error: '调用已取消。' }\n if (!live()) return { ...last, attempts: attempt - 1 }\n const outputStop = new AbortController()\n const outputSignal = AbortSignal.any([signal, outputStop.signal])\n const options: GenerateOptions = {\n provider: prepared.config.provider,\n model: prepared.config.model,\n ...(prepared.config.reasoningEffort ? { reasoningEffort: prepared.config.reasoningEffort } : {}),\n ...(prepared.config.maxTokens !== undefined ? { maxTokens: Math.min(maxTokens, prepared.config.maxTokens) } : { maxTokens }),\n ...(prepared.config.temperature !== undefined ? { temperature: prepared.config.temperature } : {}),\n ...(prepared.config.stop ? { stop: [...prepared.config.stop] } : {}),\n system,\n messages: [createUserMessage({\n source: producerMessageSource(plugin),\n content: [{ type: 'text', text }],\n })],\n signal: outputSignal,\n ...(sessionId ? { sessionId: sessionId as GenerateOptions['sessionId'] } : {}),\n }\n const { assembler, sawFinish, boundedText, visibleText } = await consume(prepared.stream(options), signal, live,\n input.insert ? { ...input.insert, stop: () => outputStop.abort() } : undefined)\n if (boundedText !== undefined && !signal.aborted && live()) return { text: boundedText, status: 'success', attempts: attempt,\n ...(assembler.usage ? { inputTokens: assembler.usage.inputTokens, outputTokens: assembler.usage.outputTokens } : {}) }\n last = { ...outcomeFromAssembler(assembler, signal, sawFinish), attempts: attempt }\n if (last.status === 'success' && visibleText !== undefined) last.text = visibleText\n if (last.status !== 'failed') return last\n if (assembler.finish.kind !== 'error') return last\n const code = assembler.finish.failure.code\n if (PERMANENT_CODES.has(code)) return last\n } catch (error) {\n if (signal.aborted || isAbortError(error)) {\n return { text: '', attempts: attempt, status: 'cancelled', error: '调用已取消。' }\n }\n if (error instanceof LlmError) {\n last = { text: '', attempts: attempt, status: 'failed', error: publicCallError(error.code) }\n if (PERMANENT_CODES.has(error.code)) return last\n } else {\n last = { text: '', attempts: attempt, status: 'failed', error: '模型调用失败。' }\n }\n }\n }\n return last\n}\n","export type QueuePriority = 'interactive' | 'background'\n\ntype Waiter = {\n priority: QueuePriority\n signal: AbortSignal\n resume: () => void\n reject: (error: unknown) => void\n dispose: () => void\n}\n\nfunction abortError(signal: AbortSignal): Error {\n if (signal.reason instanceof Error) return signal.reason\n return Object.assign(new Error('This operation was aborted'), { name: 'AbortError' })\n}\n\n/** Per-provider limiter: interactive waiters before background; background yields to in-flight agent streams. */\nexport class ProviderQueue {\n private running = 0\n private agent = 0\n private waiters: Waiter[] = []\n\n constructor(private readonly limit: () => number) {}\n\n noteAgent(delta: 1 | -1 | 0): void {\n if (delta !== 0) this.agent = Math.max(0, this.agent + delta)\n this.pump()\n }\n\n wake(): void {\n this.pump()\n }\n\n get agentCount(): number { return this.agent }\n get runningCount(): number { return this.running }\n get waiterCount(): number { return this.waiters.length }\n\n async acquire(priority: QueuePriority, signal: AbortSignal): Promise<() => void> {\n signal.throwIfAborted()\n if (this.tryAcquire(priority)) return () => this.release()\n await new Promise<void>((resolve, reject) => {\n let waiter: Waiter\n const onAbort = () => {\n const index = this.waiters.indexOf(waiter)\n if (index < 0) return\n this.waiters.splice(index, 1)\n waiter.dispose()\n reject(abortError(signal))\n }\n waiter = {\n priority,\n signal,\n resume: resolve,\n reject,\n dispose: () => signal.removeEventListener('abort', onAbort),\n }\n signal.addEventListener('abort', onAbort, { once: true })\n this.enqueue(waiter)\n this.pump()\n })\n return () => this.release()\n }\n\n private enqueue(waiter: Waiter): void {\n if (waiter.priority === 'interactive') {\n const index = this.waiters.findIndex(item => item.priority === 'background')\n this.waiters.splice(index === -1 ? this.waiters.length : index, 0, waiter)\n return\n }\n this.waiters.push(waiter)\n }\n\n private canStart(priority: QueuePriority): boolean {\n if (this.running >= this.limit()) return false\n if (priority === 'background' && this.agent > 0) return false\n return true\n }\n\n private tryAcquire(priority: QueuePriority): boolean {\n if (!this.canStart(priority)) return false\n this.running += 1\n return true\n }\n\n private release(): void {\n this.running = Math.max(0, this.running - 1)\n this.pump()\n }\n\n private pump(): void {\n while (true) {\n const index = this.waiters.findIndex(waiter => this.canStart(waiter.priority))\n if (index < 0) return\n const waiter = this.waiters[index]!\n if (!this.tryAcquire(waiter.priority)) return\n this.waiters.splice(index, 1)\n waiter.dispose()\n waiter.resume()\n }\n }\n}\n\nexport class ProviderQueues {\n private readonly queues = new Map<string, ProviderQueue>()\n constructor(private readonly limit: () => number) {}\n\n forProvider(provider: string): ProviderQueue {\n let queue = this.queues.get(provider)\n if (!queue) {\n queue = new ProviderQueue(this.limit)\n this.queues.set(provider, queue)\n }\n return queue\n }\n\n noteAgent(provider: string, delta: 1 | -1): void {\n this.forProvider(provider).noteAgent(delta)\n }\n\n wake(): void {\n for (const queue of this.queues.values()) queue.wake()\n }\n}\n","import { LlmError, ReasoningEffortId, type LlmCallConfig, type LlmRuntime } from '@deepseek-ai/dsh-llm'\nimport type { ModelRole, ModelRoute, ModelTarget, PurposeSpec, ResolvedRoute } from './contracts.ts'\nimport {\n AI_INVALID_ROUTE, AI_ROLE_UNSET, AI_SESSION_INVALID, AI_SESSION_UNAVAILABLE, AI_UNKNOWN_PURPOSE, AI_UNKNOWN_ROLE,\n AiServicesError, abortable, fail, isAbortError, publicCallError,\n} from './errors.ts'\nimport { cloneRoute, isModelRole } from './storage.ts'\nimport type { AiPolicy } from './contracts.ts'\n\nexport type SessionModelsFn = (sessionId: string, signal?: AbortSignal) => Promise<ModelRoute>\n\nexport type LlmValidate = Pick<LlmRuntime, 'resolveCallConfig'>\n\nexport function streamReasoningEffort(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined\n const id = value.trim()\n if (!id) return undefined\n return id\n}\n\nfunction asCallConfig(route: ModelRoute): LlmCallConfig {\n const effort = streamReasoningEffort(route.reasoningEffort)\n return effort\n ? { provider: route.provider, model: route.model, reasoningEffort: ReasoningEffortId(effort) }\n : { provider: route.provider, model: route.model }\n}\n\nexport async function validateRoute(llm: LlmValidate, route: ModelRoute, signal?: AbortSignal): Promise<void> {\n if (!route.provider.trim() || !route.model.trim()) fail(AI_INVALID_ROUTE, '指定的模型路由不完整,未改用其他模型。')\n try {\n const work = () => llm.resolveCallConfig(asCallConfig(route), signal)\n await (signal ? abortable(work, signal) : work())\n } catch (error) {\n if (signal?.aborted || isAbortError(error)) throw error\n if (error instanceof LlmError) fail(AI_INVALID_ROUTE, publicCallError(error.code))\n fail(AI_INVALID_ROUTE, '指定的模型路由无效,未改用其他模型。')\n }\n}\n\nfunction configuredRole(policy: AiPolicy, role: ModelRole): ModelRoute | undefined {\n const route = policy.roles[role]\n if (!route) return undefined\n if (!route.provider.trim() || !route.model.trim()) return undefined\n return cloneRoute(route)\n}\n\nexport async function resolveTarget(input: {\n llm: LlmValidate\n policy: AiPolicy\n target: ModelTarget\n source: ResolvedRoute['source']\n sessionId?: string\n sessionModels?: SessionModelsFn\n defaultModel?: () => ModelRoute | undefined\n modelCenterAvailable?: boolean\n signal?: AbortSignal\n}): Promise<ResolvedRoute> {\n const { llm, policy, target, source, sessionId, sessionModels, defaultModel, modelCenterAvailable, signal } = input\n const policyRevision = policy.revision\n if (target.kind === 'model') {\n const route = cloneRoute({\n provider: target.provider,\n model: target.model,\n ...(streamReasoningEffort(target.reasoningEffort) ? { reasoningEffort: streamReasoningEffort(target.reasoningEffort) } : {}),\n })\n await validateRoute(llm, route, signal)\n return { ...route, source, target, policyRevision }\n }\n if (target.kind === 'session') {\n if (!sessionId) fail(AI_SESSION_UNAVAILABLE, '会话模型需要当前会话。')\n if (!sessionModels) fail(AI_SESSION_UNAVAILABLE, '无法读取当前会话模型。')\n let selected: ModelRoute\n try {\n selected = await sessionModels(sessionId, signal)\n } catch (error) {\n if (signal?.aborted || isAbortError(error)) throw error\n if (error instanceof AiServicesError) throw error\n if (error instanceof Error && error.message) fail(AI_SESSION_INVALID, error.message)\n fail(AI_SESSION_UNAVAILABLE, '无法读取当前会话模型。')\n }\n const effort = streamReasoningEffort(selected.reasoningEffort)\n const route = effort\n ? { provider: selected.provider, model: selected.model, reasoningEffort: effort }\n : { provider: selected.provider, model: selected.model }\n await validateRoute(llm, route, signal)\n return { ...route, source, target, policyRevision }\n }\n if (!isModelRole(target.role)) fail(AI_UNKNOWN_ROLE, '未知模型角色。')\n let inheritedRole: ModelRole | undefined\n // Without the manager, every tier uses the default chat model. Explicit\n // model/session choices above remain authoritative, including invalid ones.\n let route = modelCenterAvailable === false ? undefined : configuredRole(policy, target.role)\n if (!route) {\n route = configuredRole(policy, 'normal') ?? defaultModel?.()\n if (!route) fail(AI_ROLE_UNSET, '请先设置默认对话模型。')\n route = cloneRoute(route)\n if (target.role !== 'normal') inheritedRole = 'normal'\n }\n await validateRoute(llm, route, signal)\n return inheritedRole\n ? { ...route, source, target, policyRevision, inheritedRole }\n : { ...route, source, target, policyRevision }\n}\n\nexport async function resolvePurposeRoute(input: {\n llm: LlmValidate\n policy: AiPolicy\n purpose: string\n spec?: PurposeSpec\n sessionId?: string\n override?: ModelTarget\n sessionModels?: SessionModelsFn\n defaultModel?: () => ModelRoute | undefined\n modelCenterAvailable?: boolean\n signal?: AbortSignal\n}): Promise<ResolvedRoute> {\n const { llm, policy, purpose, spec, sessionId, override, sessionModels, defaultModel, modelCenterAvailable, signal } = input\n if (override) {\n return resolveTarget({ llm, policy, target: override, source: 'override', sessionId, sessionModels, defaultModel, modelCenterAvailable, signal })\n }\n const mapped = policy.purposes[purpose]\n if (mapped) {\n return resolveTarget({ llm, policy, target: mapped, source: 'purpose', sessionId, sessionModels, defaultModel, modelCenterAvailable, signal })\n }\n if (spec) {\n return resolveTarget({ llm, policy, target: spec.defaultTarget, source: 'default', sessionId, sessionModels, defaultModel, modelCenterAvailable, signal })\n }\n fail(AI_UNKNOWN_PURPOSE, '用途未注册。')\n}\n\ntype ModelSelectionLike = { provider?: unknown; model?: unknown; reasoningEffort?: unknown }\ntype RequestHeaderLike = { config?: ModelSelectionLike; adapterDefaults?: { reasoningEffort?: unknown } }\n\ntype SessionModelsHost = {\n agents?: { get(sessionId: string): { session?: { requestHeader?: () => RequestHeaderLike | undefined } } | undefined }\n sessionProjections?: { stateOf(session: unknown, key: string): { pending?: ModelSelectionLike | null } | undefined }\n agentDefaultModel?: { currentSelection(): ModelSelectionLike }\n}\n\nfunction routeFromSelection(value: ModelSelectionLike | undefined, message: string): ModelRoute {\n if (typeof value?.provider !== 'string' || typeof value.model !== 'string') fail(AI_SESSION_INVALID, message)\n const effort = streamReasoningEffort(value.reasoningEffort)\n return effort\n ? { provider: value.provider, model: value.model, reasoningEffort: effort }\n : { provider: value.provider, model: value.model }\n}\n\n/**\n * Read the live host picker. A durable pending choice wins, then the route\n * recorded by the current request, then the host's default model.\n */\nexport function sessionModelsFromHost(getHost: () => unknown): SessionModelsFn {\n return async (sessionId, signal) => {\n signal?.throwIfAborted()\n const host = getHost() as SessionModelsHost | undefined\n if (!host?.agents || !host.sessionProjections) fail(AI_SESSION_UNAVAILABLE, '无法读取当前会话模型。')\n const agent = host.agents.get(sessionId)\n const session = agent?.session\n if (!session) fail(AI_SESSION_UNAVAILABLE, '当前会话不在 Host 中,无法读取模型。')\n let projection: { pending?: ModelSelectionLike | null } | undefined\n try {\n projection = host.sessionProjections.stateOf(session, 'modelSelection')\n } catch (error) {\n if (signal?.aborted || isAbortError(error)) throw error\n fail(AI_SESSION_UNAVAILABLE, '无法读取当前会话模型。')\n }\n signal?.throwIfAborted()\n if (!projection) fail(AI_SESSION_UNAVAILABLE, '当前会话模型投影不可用。')\n if (projection.pending !== null && projection.pending !== undefined) {\n return routeFromSelection(projection.pending, '当前会话模型选择无效。')\n }\n const header = session.requestHeader?.()\n const recorded = header?.config\n if (recorded !== undefined) {\n const selected = header?.adapterDefaults?.reasoningEffort === true\n ? { provider: recorded.provider, model: recorded.model }\n : recorded\n return routeFromSelection(selected, '当前会话模型记录无效。')\n }\n return routeFromSelection(host.agentDefaultModel?.currentSelection(), '默认对话模型不可用。')\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport type {\n AiFeatureScope, AiPolicy, AiServices, AuxiliaryRequest, AuxiliaryResult, ModelRoute, ModelTarget, PurposeSpec, ResolvedRoute,\n UsageReceipt,\n} from './contracts.ts'\nimport { AiServicesError, AI_DUPLICATE_PURPOSE, AI_INACTIVE, AI_POLICY_CONFLICT, AI_POLICY_INVALID, AI_POLICY_SAVE_FAILED, abortable, fail, isAbortError } from './errors.ts'\nimport { generateAuxiliary, type LlmGenerate } from './generate.ts'\nimport { ProviderQueues } from './queue.ts'\nimport { resolvePurposeRoute, type LlmValidate, type SessionModelsFn } from './routing.ts'\nimport {\n boundedReceipts, clonePolicy, clonePurpose, defaultPolicy, parsePolicyData, purposeSpecSchema, sanitizeReceipt,\n} from './storage.ts'\n\nexport type AiServicesStore = {\n savePolicy(policy: AiPolicy, imports?: string[]): Promise<void>\n saveReceipts(items: UsageReceipt[]): Promise<void>\n}\n\nexport type AiServicesOptions = {\n llm: LlmValidate & LlmGenerate\n initialPolicy?: AiPolicy\n initialImports?: string[]\n initialReceipts?: UsageReceipt[]\n store: AiServicesStore\n sessionModels?: SessionModelsFn\n /** Read lazily so host model changes and manager unloads apply to the next call. */\n defaultModel?: () => ModelRoute | undefined\n modelCenterAvailable?: () => boolean\n now?: () => number\n id?: () => string\n}\n\ntype OwnedPurpose = PurposeSpec & { plugin: string }\n\ntype Lifetime = {\n plugin: string\n controller: AbortController\n active: boolean\n purposes: Map<string, PurposeSpec>\n}\n\nconst PLUGIN_NAME = /^(?:@[A-Za-z0-9._-]+\\/)?[A-Za-z0-9._-]+$/\n\nfunction pluginName(value: string): string {\n const id = value.trim()\n if (!id || id.length > 80 || id.includes('..') || !PLUGIN_NAME.test(id)) fail(AI_POLICY_INVALID, '插件标识无效。')\n return id\n}\n\nfunction minLimit(policyValue: number, purposeValue: number | undefined): number {\n return purposeValue === undefined ? policyValue : Math.min(policyValue, purposeValue)\n}\n\nexport class AiServicesRuntime implements AiServices {\n private policy: AiPolicy\n private imports: string[]\n private receipts: UsageReceipt[]\n private storageFailed = false\n private disposed = false\n private pending = Promise.resolve()\n private readonly purposeRegistry = new Map<string, OwnedPurpose>()\n private readonly lifetimes = new Set<Lifetime>()\n private readonly queues: ProviderQueues\n private readonly now: () => number\n private readonly id: () => string\n\n constructor(private readonly options: AiServicesOptions) {\n this.imports = [...(options.initialImports ?? [])]\n this.policy = clonePolicy(options.initialPolicy ?? defaultPolicy())\n this.receipts = boundedReceipts(options.initialReceipts ?? [])\n this.now = options.now ?? Date.now\n this.id = options.id ?? (() => randomUUID())\n this.queues = new ProviderQueues(() => this.policy.limits.concurrency)\n }\n\n get storageFailedFlag(): boolean { return this.storageFailed }\n\n noteAgent(provider: string, delta: 1 | -1): void {\n this.queues.noteAgent(provider, delta)\n }\n\n getPolicy(): AiPolicy {\n return clonePolicy(this.policy)\n }\n\n purposes(): Array<PurposeSpec & { plugin: string }> {\n return [...this.purposeRegistry.values()].map(item => ({ ...clonePurpose(item), plugin: item.plugin }))\n }\n\n usage(): UsageReceipt[] {\n return this.receipts.map(item => structuredClone(item))\n }\n\n importPurposes(migrationId: string, defaults: Record<string, ModelTarget>, roles: AiPolicy['roles'] = {}): Promise<AiPolicy> {\n const task = this.pending.then(async () => {\n this.assertOpen()\n if (!/^[A-Za-z][A-Za-z0-9._:-]{0,79}$/.test(migrationId)) fail(AI_POLICY_INVALID, '迁移标识无效。')\n if (this.imports.includes(migrationId)) return this.getPolicy()\n if (this.imports.length >= 100) fail(AI_POLICY_INVALID, '迁移记录已满。')\n const { revision: _revision, ...current } = this.policy\n const parsed = parsePolicyData({ ...current, roles: { ...roles, ...current.roles }, purposes: { ...defaults, ...this.policy.purposes } })\n const purposes = { ...parsed.purposes, ...this.policy.purposes }\n const next = { ...this.getPolicy(), roles: parsed.roles, purposes, revision: this.policy.revision + 1 }\n const imports = [...this.imports, migrationId]\n try { await this.options.store.savePolicy(next, imports) }\n catch { this.storageFailed = true; fail(AI_POLICY_SAVE_FAILED, '旧模型配置迁移失败,已保留原设置。') }\n this.policy = next\n this.imports = imports\n this.storageFailed = false\n return this.getPolicy()\n })\n this.pending = task.then(() => {}, () => {})\n return task\n }\n\n updatePolicy(policy: Omit<AiPolicy, 'revision'>, expectedRevision: number): Promise<AiPolicy> {\n const task = this.pending.then(async () => {\n if (this.disposed) fail(AI_INACTIVE, 'AI 服务已停止。')\n if (expectedRevision !== this.policy.revision) fail(AI_POLICY_CONFLICT, '策略已被其他页面修改,请刷新后重试。')\n const parsed = parsePolicyData(policy)\n const next: AiPolicy = { ...clonePolicy({ revision: expectedRevision + 1, ...parsed }), revision: expectedRevision + 1 }\n try {\n await this.options.store.savePolicy(next, this.imports)\n } catch {\n this.storageFailed = true\n fail(AI_POLICY_SAVE_FAILED, '策略保存失败,已保留原设置。')\n }\n this.policy = next\n this.storageFailed = false\n this.queues.wake()\n return clonePolicy(this.policy)\n })\n this.pending = task.then(() => {}, () => {})\n return task\n }\n\n async resolve(purpose: string, sessionId?: string, override?: ModelTarget): Promise<ResolvedRoute> {\n this.assertOpen()\n const spec = this.purposeRegistry.get(purpose)\n return resolvePurposeRoute({\n llm: this.options.llm,\n policy: this.policy,\n purpose,\n spec,\n sessionId,\n override,\n sessionModels: this.options.sessionModels,\n defaultModel: this.options.defaultModel,\n modelCenterAvailable: this.options.modelCenterAvailable?.(),\n })\n }\n\n activate(plugin: string): AiFeatureScope {\n this.assertOpen()\n const name = pluginName(plugin)\n const lifetime: Lifetime = {\n plugin: name,\n controller: new AbortController(),\n active: true,\n purposes: new Map(),\n }\n this.lifetimes.add(lifetime)\n const scope: AiFeatureScope = {\n plugin: name,\n get signal() { return lifetime.controller.signal },\n get active() { return lifetime.active && !lifetime.controller.signal.aborted },\n registerPurpose: spec => this.registerPurpose(lifetime, spec),\n run: request => this.run(lifetime, request),\n dispose: () => this.disposeLifetime(lifetime),\n }\n return scope\n }\n\n async dispose(): Promise<void> {\n this.disposed = true\n for (const lifetime of [...this.lifetimes]) this.disposeLifetime(lifetime)\n await this.pending\n }\n\n private assertOpen(): void {\n if (this.disposed) fail(AI_INACTIVE, 'AI 服务已停止。')\n }\n\n private registerPurpose(lifetime: Lifetime, spec: PurposeSpec): () => void {\n if (!lifetime.active) fail(AI_INACTIVE, '插件范围已停用。')\n let parsed: PurposeSpec\n try { parsed = purposeSpecSchema.parse(spec) }\n catch { fail(AI_POLICY_INVALID, '用途声明无效。') }\n const existing = this.purposeRegistry.get(parsed.id)\n if (existing && existing.plugin !== lifetime.plugin) fail(AI_DUPLICATE_PURPOSE, '用途已被其他插件注册。')\n const owned: OwnedPurpose = { ...clonePurpose(parsed), plugin: lifetime.plugin }\n lifetime.purposes.set(parsed.id, owned)\n this.purposeRegistry.set(parsed.id, owned)\n let removed = false\n return () => {\n if (removed) return\n removed = true\n lifetime.purposes.delete(parsed.id)\n this.refreshPurpose(parsed.id)\n }\n }\n\n private disposeLifetime(lifetime: Lifetime): void {\n if (!lifetime.active && !this.lifetimes.has(lifetime)) return\n lifetime.active = false\n if (!lifetime.controller.signal.aborted) lifetime.controller.abort()\n const ids = [...lifetime.purposes.keys()]\n lifetime.purposes.clear()\n this.lifetimes.delete(lifetime)\n for (const id of ids) this.refreshPurpose(id)\n }\n\n private refreshPurpose(id: string): void {\n for (const other of this.lifetimes) {\n const spec = other.purposes.get(id)\n if (spec && other.active) {\n this.purposeRegistry.set(id, { ...spec, plugin: other.plugin })\n return\n }\n }\n this.purposeRegistry.delete(id)\n }\n\n private live(lifetime: Lifetime, request: AuxiliaryRequest): boolean {\n if (!lifetime.active || lifetime.controller.signal.aborted || request.signal?.aborted) return false\n try {\n if (request.isCurrent && !request.isCurrent()) return false\n } catch {\n return false\n }\n return true\n }\n\n private classify(lifetime: Lifetime, request: AuxiliaryRequest, queued: boolean): UsageReceipt['status'] | undefined {\n const aborted = lifetime.controller.signal.aborted || Boolean(request.signal?.aborted)\n let current = true\n try { current = !request.isCurrent || request.isCurrent() } catch { current = false }\n if (aborted) return 'cancelled'\n if (!lifetime.active || !current) return queued ? 'superseded' : 'skipped'\n return undefined\n }\n\n private async run(lifetime: Lifetime, request: AuxiliaryRequest): Promise<AuxiliaryResult> {\n const startedAt = this.now()\n const base = {\n id: this.id(),\n plugin: lifetime.plugin,\n purpose: request.purpose,\n ...(request.sessionId ? { sessionId: request.sessionId } : {}),\n sourceVersion: request.sourceVersion,\n ...(request.contractVersion !== undefined ? { contractVersion: request.contractVersion } : {}),\n ...(request.promptVersion ? { promptVersion: request.promptVersion } : {}),\n ...(request.schemaVersion ? { schemaVersion: request.schemaVersion } : {}),\n cost: null as null,\n startedAt,\n }\n let queued = false\n let timeout: AbortSignal | undefined\n const finish = async (receipt: UsageReceipt, text = ''): Promise<AuxiliaryResult> => {\n let sealed = sanitizeReceipt({ ...receipt, finishedAt: this.now(), cost: null })\n await this.record(sealed)\n const again = this.classify(lifetime, request, queued)\n if (again && (sealed.status === 'success' || text !== '')) {\n const { error: _error, ...rest } = sealed\n const timedOut = Boolean(timeout?.aborted) && !request.signal?.aborted && !lifetime.controller.signal.aborted\n sealed = sanitizeReceipt({\n ...rest,\n status: again,\n finishedAt: this.now(),\n cost: null,\n ...(again === 'cancelled' ? { error: timedOut ? '调用超时。' : '调用已取消。' } : {}),\n })\n await this.record(sealed)\n return { text: '', receipt: sealed }\n }\n return { text: sealed.status === 'success' ? text : '', receipt: sealed }\n }\n const early = this.classify(lifetime, request, false)\n if (early) {\n return finish({ ...base, status: early, attempts: 0, finishedAt: this.now(), ...(early === 'cancelled' ? { error: '调用已取消。' } : {}) })\n }\n\n const spec = lifetime.purposes.get(request.purpose)\n if (!spec) {\n return finish({ ...base, status: 'failed', attempts: 0, finishedAt: this.now(), error: '用途未注册。' })\n }\n if (request.insert && (!Number.isInteger(request.insert.maxChars) || request.insert.maxChars < 1 || request.insert.maxChars > 12000)) return finish({ ...base, status: 'failed', attempts: 0, finishedAt: this.now(), error: '输出字符上限无效。' })\n const policy = this.policy\n const maxInputChars = minLimit(policy.limits.maxInputChars, spec.maxInputChars)\n if (request.input.length + request.system.length > maxInputChars) {\n return finish({\n ...base, status: 'failed', attempts: 0, finishedAt: this.now(), error: '输入超出上限。',\n } satisfies UsageReceipt)\n }\n\n const timeoutMs = minLimit(policy.limits.timeoutMs, spec.timeoutMs)\n timeout = AbortSignal.timeout(timeoutMs)\n const combined = AbortSignal.any([lifetime.controller.signal, timeout, ...(request.signal ? [request.signal] : [])])\n\n let route: ResolvedRoute\n try {\n route = await abortable(() => resolvePurposeRoute({\n llm: this.options.llm,\n policy,\n purpose: request.purpose,\n spec,\n sessionId: request.sessionId,\n override: request.override,\n sessionModels: this.options.sessionModels,\n defaultModel: this.options.defaultModel,\n modelCenterAvailable: this.options.modelCenterAvailable?.(),\n signal: combined,\n }), combined)\n } catch (error) {\n const classified = this.classify(lifetime, request, false)\n ?? (isAbortError(error) || combined.aborted ? 'cancelled' : undefined)\n if (classified) {\n const timedOut = timeout.aborted && !request.signal?.aborted && !lifetime.controller.signal.aborted\n return finish({\n ...base, status: classified, attempts: 0, finishedAt: this.now(),\n ...(classified === 'cancelled' ? { error: timedOut ? '调用超时。' : '调用已取消。' } : {}),\n })\n }\n const message = error instanceof AiServicesError ? error.message : '模型路由无效,未改用其他模型。'\n return finish({ ...base, status: 'failed', attempts: 0, finishedAt: this.now(), error: message })\n }\n\n const afterResolve = this.classify(lifetime, request, false)\n if (afterResolve) {\n return finish({ ...base, status: afterResolve, attempts: 0, finishedAt: this.now(), route, ...(afterResolve === 'cancelled' ? { error: '调用已取消。' } : {}) })\n }\n\n const maxOutputTokens = minLimit(policy.limits.maxOutputTokens, spec.maxOutputTokens)\n const maxAttempts = policy.limits.maxAttempts\n const priority = request.priority === 'interactive' ? 'interactive' : 'background'\n\n queued = true\n let release = () => {}\n try {\n release = await this.queues.forProvider(route.provider).acquire(priority, combined)\n } catch (error) {\n const classified = this.classify(lifetime, request, true) ?? (isAbortError(error) || combined.aborted ? 'cancelled' : 'failed')\n return finish({\n ...base, status: classified, attempts: 0, finishedAt: this.now(), route,\n error: classified === 'cancelled' ? (timeout.aborted && !request.signal?.aborted && !lifetime.controller.signal.aborted ? '调用超时。' : '调用已取消。') : '排队失败。',\n })\n }\n\n try {\n const afterQueue = this.classify(lifetime, request, true)\n if (afterQueue) {\n return finish({\n ...base, status: afterQueue, attempts: 0, finishedAt: this.now(), route,\n ...(afterQueue === 'cancelled' ? { error: '调用已取消。' } : {}),\n })\n }\n const generated = await generateAuxiliary({\n llm: this.options.llm,\n plugin: lifetime.plugin,\n route,\n system: request.system,\n text: request.input,\n maxTokens: maxOutputTokens,\n maxAttempts,\n insert: request.insert,\n signal: combined,\n sessionId: request.sessionId,\n live: () => this.live(lifetime, request),\n })\n const classified = this.classify(lifetime, request, true)\n if (classified === 'superseded') {\n return finish({\n ...base, status: 'superseded', attempts: generated.attempts, finishedAt: this.now(), route,\n ...(generated.inputTokens !== undefined ? { inputTokens: generated.inputTokens } : {}),\n ...(generated.outputTokens !== undefined ? { outputTokens: generated.outputTokens } : {}),\n })\n }\n const timedOut = timeout.aborted && !request.signal?.aborted && !lifetime.controller.signal.aborted\n if (classified === 'cancelled' || generated.status === 'cancelled') {\n return finish({\n ...base, status: 'cancelled', attempts: generated.attempts, finishedAt: this.now(), route,\n ...(generated.inputTokens !== undefined ? { inputTokens: generated.inputTokens } : {}),\n ...(generated.outputTokens !== undefined ? { outputTokens: generated.outputTokens } : {}),\n error: timedOut ? '调用超时。' : '调用已取消。',\n })\n }\n if (generated.status !== 'success') {\n return finish({\n ...base, status: generated.status, attempts: generated.attempts, finishedAt: this.now(), route,\n ...(generated.inputTokens !== undefined ? { inputTokens: generated.inputTokens } : {}),\n ...(generated.outputTokens !== undefined ? { outputTokens: generated.outputTokens } : {}),\n ...(generated.error ? { error: generated.error } : {}),\n })\n }\n return finish({\n ...base, status: 'success', attempts: generated.attempts, finishedAt: this.now(), route,\n ...(generated.inputTokens !== undefined ? { inputTokens: generated.inputTokens } : {}),\n ...(generated.outputTokens !== undefined ? { outputTokens: generated.outputTokens } : {}),\n }, generated.text)\n } finally {\n release()\n }\n }\n\n private record(receipt: UsageReceipt): Promise<void> {\n const task = this.pending.then(async () => {\n const index = this.receipts.findIndex(row => row.id === receipt.id)\n const list = [...this.receipts]\n if (index >= 0) list[index] = receipt\n else list.push(receipt)\n const next = boundedReceipts(list)\n try {\n await this.options.store.saveReceipts(next)\n this.receipts = next\n } catch {\n this.storageFailed = true\n this.receipts = next\n }\n })\n this.pending = task.then(() => {}, () => {})\n return task\n }\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { isAgentLoopRequest, type GenerateOptions } from '@deepseek-ai/dsh-llm'\nimport { AI_RPC_CHANNEL, type AiServices, type RpcResult } from './contracts.ts'\nimport { registerHostRpc, type HostRpcContext } from './host-rpc.ts'\nimport { handleAiRpc } from './rpc.ts'\nimport { AiServicesRuntime } from './service.ts'\nimport { sessionModelsFromHost } from './routing.ts'\nimport { POLICY_KEY, RECEIPTS_KEY, aiServicesDomain, boundedReceipts } from './storage.ts'\n\nexport { AiServicesRuntime } from './service.ts'\nexport { registerHostRpc, type HostRpcContext } from './host-rpc.ts'\nexport { AI_RPC_CHANNEL, CHAT_EVENTS_SLOT, MODEL_SETTINGS_SLOT } from './contracts.ts'\nexport type * from './contracts.ts'\n\nexport const name = '@klarkxy/dsh-ai-services'\nexport const inject = ['llm', 'storageDomain', 'connection', 'webServer', 'agents', 'sessionProjections', 'agentDefaultModel'] as const\n\ndeclare module '@deepseek-ai/cordis' { interface Context { aiServices: AiServices } }\n\ntype Host = Context & HostRpcContext\ntype StreamListener = (\n options: GenerateOptions,\n next: () => AsyncIterable<unknown>,\n) => AsyncIterable<unknown>\n\nfunction installForegroundPriority(ctx: Context, service: AiServicesRuntime): () => void {\n const on = ctx.on as unknown as (\n name: 'llm/stream',\n listener: StreamListener,\n options?: { global?: boolean; prepend?: boolean },\n ) => () => boolean\n return on.call(ctx, 'llm/stream', (options, next) => {\n if (!isAgentLoopRequest(options)) return next()\n return (async function* () {\n service.noteAgent(options.provider, 1)\n try {\n yield* next()\n } finally {\n service.noteAgent(options.provider, -1)\n }\n })()\n }, { global: true, prepend: true })\n}\n\nexport async function apply(ctx: Context): Promise<void> {\n const host = ctx as Host\n const domain = await ctx.storageDomain.open(aiServicesDomain)\n const policyTable = domain.table('policy')\n const receiptTable = domain.table('receipts')\n const stored = policyTable.get(POLICY_KEY)\n const { imports = [], ...initialPolicy } = stored ?? {}\n const service = new AiServicesRuntime({\n llm: ctx.llm,\n initialPolicy: stored ? initialPolicy as import('./contracts.ts').AiPolicy : undefined,\n initialImports: imports,\n initialReceipts: receiptTable.get(RECEIPTS_KEY)?.items,\n store: {\n savePolicy: (policy, imports) => policyTable.put(POLICY_KEY, { ...policy, imports }),\n saveReceipts: items => receiptTable.put(RECEIPTS_KEY, { items: boundedReceipts(items) }),\n },\n sessionModels: sessionModelsFromHost(() => ctx),\n defaultModel: () => (ctx.get('agentDefaultModel') as { currentSelection(): import('./contracts.ts').ModelRoute } | undefined)?.currentSelection(),\n modelCenterAvailable: () => Boolean(ctx.get('modelCenter')),\n })\n ctx.effect(() => async () => { await service.dispose(); await domain.close() }, 'ai-services.dispose')\n ctx.effect(() => installForegroundPriority(ctx, service), 'ai-services.foreground')\n ctx.provide('aiServices', service)\n ctx.effect(() => registerHostRpc(host, AI_RPC_CHANNEL, (endpoint, payload, signal): Promise<RpcResult> => (\n handleAiRpc(service, endpoint, payload, signal)\n )), 'ai-services.rpc')\n}\n"],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ //#region src/insert-output.d.ts
2
+ type StreamChunkLike = {
3
+ type: string;
4
+ text?: string;
5
+ reason?: {
6
+ kind?: string;
7
+ failure?: {
8
+ message?: string;
9
+ };
10
+ };
11
+ };
12
+ /** Keep only visible continuation text. Drop fences, reasoning, and tool payloads. */
13
+ declare function sanitizeInsert(raw: string): string;
14
+ declare function collectInsertText(stream: AsyncIterable<StreamChunkLike>, options?: {
15
+ maxChars?: number;
16
+ signal?: AbortSignal;
17
+ }): Promise<string>;
18
+ /** Incremental visible text; split think markers never count toward the writing limit. */
19
+ declare function createInsertCollector(maxChars: number): {
20
+ append: (fragment: string) => void;
21
+ readonly full: boolean;
22
+ text: () => string;
23
+ };
24
+ //#endregion
25
+ export { StreamChunkLike, collectInsertText, createInsertCollector, sanitizeInsert };
26
+ //# sourceMappingURL=insert-output.d.ts.map
@@ -0,0 +1,63 @@
1
+ //#region src/insert-output.ts
2
+ const DEFAULT_MAX_CHARS = 240;
3
+ /** Keep only visible continuation text. Drop fences, reasoning, and tool payloads. */
4
+ function sanitizeInsert(raw) {
5
+ let text = raw.replace(/\r\n/g, "\n");
6
+ text = text.replace(/^```[^\n]*\n?/, "").replace(/\n```\s*$/, "");
7
+ text = text.replace(/^(?:插入内容|Insert(?:ion)?|Continuation)\s*[::]\s*/i, "");
8
+ return text;
9
+ }
10
+ async function collectInsertText(stream, options = {}) {
11
+ const maxChars = options.maxChars ?? DEFAULT_MAX_CHARS;
12
+ if (options.signal?.aborted) return "";
13
+ const collector = createInsertCollector(maxChars);
14
+ try {
15
+ for await (const chunk of stream) {
16
+ if (options.signal?.aborted) return "";
17
+ if (chunk.type === "aborted" || chunk.type === "finish" && chunk.reason?.kind === "aborted") return "";
18
+ if (chunk.type === "error" || chunk.type === "finish" && chunk.reason?.kind === "error") throw new Error(chunk.reason?.failure?.message || "模型请求失败,请检查模型设置后重试");
19
+ if (chunk.type !== "text-delta" || typeof chunk.text !== "string") continue;
20
+ collector.append(chunk.text);
21
+ if (collector.full) break;
22
+ }
23
+ } catch (error) {
24
+ if (options.signal?.aborted) return "";
25
+ throw error;
26
+ }
27
+ return options.signal?.aborted ? "" : collector.text();
28
+ }
29
+ /** Incremental visible text; split think markers never count toward the writing limit. */
30
+ function createInsertCollector(maxChars) {
31
+ let out = "";
32
+ let pending = "";
33
+ let thinking = false;
34
+ const append = (fragment) => {
35
+ pending += fragment;
36
+ while (pending) {
37
+ const marker = thinking ? "</think>" : "<think>";
38
+ const index = pending.indexOf(marker);
39
+ if (index >= 0) {
40
+ if (!thinking) out += pending.slice(0, index);
41
+ pending = pending.slice(index + marker.length);
42
+ thinking = !thinking;
43
+ continue;
44
+ }
45
+ let keep = Math.min(marker.length - 1, pending.length);
46
+ while (keep > 0 && !marker.startsWith(pending.slice(-keep))) keep--;
47
+ if (!thinking) out += pending.slice(0, pending.length - keep);
48
+ pending = keep ? pending.slice(-keep) : "";
49
+ break;
50
+ }
51
+ };
52
+ return {
53
+ append,
54
+ get full() {
55
+ return out.length >= maxChars;
56
+ },
57
+ text: () => sanitizeInsert((out + (!thinking ? pending : "")).slice(0, maxChars))
58
+ };
59
+ }
60
+ //#endregion
61
+ export { collectInsertText, createInsertCollector, sanitizeInsert };
62
+
63
+ //# sourceMappingURL=insert-output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insert-output.js","names":[],"sources":["../src/insert-output.ts"],"sourcesContent":["export type StreamChunkLike = {\n type: string\n text?: string\n reason?: { kind?: string; failure?: { message?: string } }\n}\n\nconst DEFAULT_MAX_CHARS = 240\n\n/** Keep only visible continuation text. Drop fences, reasoning, and tool payloads. */\nexport function sanitizeInsert(raw: string): string {\n let text = raw.replace(/\\r\\n/g, '\\n')\n text = text.replace(/^```[^\\n]*\\n?/, '').replace(/\\n```\\s*$/, '')\n text = text.replace(/^(?:插入内容|Insert(?:ion)?|Continuation)\\s*[::]\\s*/i, '')\n return text\n}\n\nexport async function collectInsertText(\n stream: AsyncIterable<StreamChunkLike>,\n options: { maxChars?: number; signal?: AbortSignal } = {},\n): Promise<string> {\n const maxChars = options.maxChars ?? DEFAULT_MAX_CHARS\n if (options.signal?.aborted) return ''\n const collector = createInsertCollector(maxChars)\n try {\n for await (const chunk of stream) {\n if (options.signal?.aborted) return ''\n if (chunk.type === 'aborted' || (chunk.type === 'finish' && chunk.reason?.kind === 'aborted')) return ''\n if (chunk.type === 'error' || (chunk.type === 'finish' && chunk.reason?.kind === 'error')) throw new Error(chunk.reason?.failure?.message || '模型请求失败,请检查模型设置后重试')\n if (chunk.type !== 'text-delta' || typeof chunk.text !== 'string') continue\n collector.append(chunk.text)\n if (collector.full) break\n }\n } catch (error) { if (options.signal?.aborted) return ''; throw error }\n return options.signal?.aborted ? '' : collector.text()\n}\n\n/** Incremental visible text; split think markers never count toward the writing limit. */\nexport function createInsertCollector(maxChars: number) {\n let out = ''\n let pending = ''\n let thinking = false\n const append = (fragment: string) => {\n pending += fragment\n while (pending) {\n const marker = thinking ? '</think>' : '<think>'\n const index = pending.indexOf(marker)\n if (index >= 0) {\n if (!thinking) out += pending.slice(0, index)\n pending = pending.slice(index + marker.length)\n thinking = !thinking\n continue\n }\n // Keep only a possible split marker, never buffer the reasoning body.\n let keep = Math.min(marker.length - 1, pending.length)\n while (keep > 0 && !marker.startsWith(pending.slice(-keep))) keep--\n if (!thinking) out += pending.slice(0, pending.length - keep)\n pending = keep ? pending.slice(-keep) : ''\n break\n }\n }\n return { append, get full() { return out.length >= maxChars }, text: () => sanitizeInsert((out + (!thinking ? pending : '')).slice(0, maxChars)) }\n}\n"],"mappings":";AAMA,MAAM,oBAAoB;;AAG1B,SAAgB,eAAe,KAAqB;CAClD,IAAI,OAAO,IAAI,QAAQ,SAAS,IAAI;CACpC,OAAO,KAAK,QAAQ,iBAAiB,EAAE,CAAC,CAAC,QAAQ,aAAa,EAAE;CAChE,OAAO,KAAK,QAAQ,oDAAoD,EAAE;CAC1E,OAAO;AACT;AAEA,eAAsB,kBACpB,QACA,UAAuD,CAAC,GACvC;CACjB,MAAM,WAAW,QAAQ,YAAY;CACrC,IAAI,QAAQ,QAAQ,SAAS,OAAO;CACpC,MAAM,YAAY,sBAAsB,QAAQ;CAChD,IAAI;EACF,WAAW,MAAM,SAAS,QAAQ;GAChC,IAAI,QAAQ,QAAQ,SAAS,OAAO;GACpC,IAAI,MAAM,SAAS,aAAc,MAAM,SAAS,YAAY,MAAM,QAAQ,SAAS,WAAY,OAAO;GACtG,IAAI,MAAM,SAAS,WAAY,MAAM,SAAS,YAAY,MAAM,QAAQ,SAAS,SAAU,MAAM,IAAI,MAAM,MAAM,QAAQ,SAAS,WAAW,mBAAmB;GAChK,IAAI,MAAM,SAAS,gBAAgB,OAAO,MAAM,SAAS,UAAU;GACnE,UAAU,OAAO,MAAM,IAAI;GAC3B,IAAI,UAAU,MAAM;EACtB;CACF,SAAS,OAAO;EAAE,IAAI,QAAQ,QAAQ,SAAS,OAAO;EAAI,MAAM;CAAM;CACtE,OAAO,QAAQ,QAAQ,UAAU,KAAK,UAAU,KAAK;AACvD;;AAGA,SAAgB,sBAAsB,UAAkB;CACtD,IAAI,MAAM;CACV,IAAI,UAAU;CACd,IAAI,WAAW;CACf,MAAM,UAAU,aAAqB;EACnC,WAAW;EACX,OAAO,SAAS;GACd,MAAM,SAAS,WAAW,aAAa;GACvC,MAAM,QAAQ,QAAQ,QAAQ,MAAM;GACpC,IAAI,SAAS,GAAG;IACd,IAAI,CAAC,UAAU,OAAO,QAAQ,MAAM,GAAG,KAAK;IAC5C,UAAU,QAAQ,MAAM,QAAQ,OAAO,MAAM;IAC7C,WAAW,CAAC;IACZ;GACF;GAEA,IAAI,OAAO,KAAK,IAAI,OAAO,SAAS,GAAG,QAAQ,MAAM;GACrD,OAAO,OAAO,KAAK,CAAC,OAAO,WAAW,QAAQ,MAAM,CAAC,IAAI,CAAC,GAAG;GAC7D,IAAI,CAAC,UAAU,OAAO,QAAQ,MAAM,GAAG,QAAQ,SAAS,IAAI;GAC5D,UAAU,OAAO,QAAQ,MAAM,CAAC,IAAI,IAAI;GACxC;EACF;CACF;CACA,OAAO;EAAE;EAAQ,IAAI,OAAO;GAAE,OAAO,IAAI,UAAU;EAAS;EAAG,YAAY,gBAAgB,OAAO,CAAC,WAAW,UAAU,IAAA,CAAK,MAAM,GAAG,QAAQ,CAAC;CAAE;AACnJ"}
package/package.json ADDED
@@ -0,0 +1,111 @@
1
+ {
2
+ "name": "@klarkxy/dsh-ai-services",
3
+ "version": "0.1.0",
4
+ "description": "Inert shared Cordis service for purpose routing, bounded auxiliary model calls, and usage receipts",
5
+ "license": "SEE LICENSE IN LICENSE",
6
+ "type": "module",
7
+ "engines": {
8
+ "node": ">=22"
9
+ },
10
+ "main": "./lib/index.js",
11
+ "types": "./lib/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./lib/index.d.ts",
15
+ "import": "./lib/index.js",
16
+ "default": "./lib/index.js"
17
+ },
18
+ "./contracts": {
19
+ "types": "./lib/contracts.d.ts",
20
+ "import": "./lib/contracts.js",
21
+ "default": "./lib/contracts.js"
22
+ },
23
+ "./host-rpc": {
24
+ "types": "./lib/host-rpc.d.ts",
25
+ "import": "./lib/host-rpc.js",
26
+ "default": "./lib/host-rpc.js"
27
+ },
28
+ "./package.json": "./package.json",
29
+ "./client-utils": {
30
+ "types": "./lib/client-utils.d.ts",
31
+ "import": "./lib/client-utils.js",
32
+ "default": "./lib/client-utils.js"
33
+ },
34
+ "./insert-output": {
35
+ "types": "./lib/insert-output.d.ts",
36
+ "import": "./lib/insert-output.js",
37
+ "default": "./lib/insert-output.js"
38
+ }
39
+ },
40
+ "files": [
41
+ "lib",
42
+ "cordis.patch.yml",
43
+ "README.md",
44
+ "docs/README.zh-CN.md",
45
+ "LICENSE"
46
+ ],
47
+ "dsh": {
48
+ "bundle": {
49
+ "patch": "./cordis.patch.yml"
50
+ }
51
+ },
52
+ "dshEditor": {
53
+ "role": "core",
54
+ "visibility": "public",
55
+ "entries": [
56
+ {
57
+ "id": "ai-services",
58
+ "title": "AI 服务",
59
+ "description": "插件共用的模型路由与有界调用,不自动发起推理",
60
+ "locked": true,
61
+ "service": "aiServices"
62
+ }
63
+ ]
64
+ },
65
+ "dependencies": {
66
+ "@deepseek-ai/dsh-llm": "0.1.7-alpha.1",
67
+ "@deepseek-ai/dsh-storage-domain": "0.1.7-alpha.1",
68
+ "zod": "^4.1.5"
69
+ },
70
+ "peerDependencies": {
71
+ "@deepseek-ai/cordis": "^4.0.3",
72
+ "react": "^18.2.0"
73
+ },
74
+ "devDependencies": {
75
+ "@deepseek-ai/cordis": "^4.0.3",
76
+ "react": "^18.2.0",
77
+ "@types/react": "18.3.31"
78
+ },
79
+ "keywords": [
80
+ "dsh",
81
+ "dsh-plugin",
82
+ "deepseek-harness",
83
+ "ai-services"
84
+ ],
85
+ "repository": {
86
+ "type": "git",
87
+ "url": "git+https://github.com/klarkxy/dsh-editor.git",
88
+ "directory": "packages/dsh-ai-services"
89
+ },
90
+ "homepage": "https://github.com/klarkxy/dsh-editor/tree/main/packages/dsh-ai-services#readme",
91
+ "bugs": {
92
+ "url": "https://github.com/klarkxy/dsh-editor/issues"
93
+ },
94
+ "publishConfig": {
95
+ "access": "public",
96
+ "registry": "https://registry.npmjs.org/"
97
+ },
98
+ "peerDependenciesMeta": {
99
+ "react": {
100
+ "optional": true
101
+ }
102
+ },
103
+ "dshRelease": {
104
+ "contentHash": "sha256-eeec387db42aaa76ed7ad0edb238fff693322f85dfacdd39a450e63c750cb127"
105
+ },
106
+ "scripts": {
107
+ "build": "tsdown",
108
+ "typecheck": "tsc --noEmit -p tsconfig.json",
109
+ "test": "vitest run --root ../.. packages/dsh-ai-services/src"
110
+ }
111
+ }