{"version":3,"file":"infer.d.ts","sourceRoot":"","sources":["../../../src/components/capabilities/infer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,gBAAgB,EAAE,OAAO,EAAE,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEjH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,KAAK,EAEX,oBAAoB,EAGpB,MAAM,cAAc,CAAC;AAKtB,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,aAAa,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC/B,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IACpD,cAAc,CACb,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,WAAW,GAAG,WAAW,GAAG,QAAQ,CAAC,GACtE,OAAO,CAAC,gBAAgB,CAAC,CAAC;CAC7B;AA2FD,wBAAgB,4BAA4B,CAC3C,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,qBAAqB,CAAC,GAChE,eAAe,CAgCjB;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,eAAe,GAAG,oBAAoB,CAwCxF","sourcesContent":["import { Buffer } from \"node:buffer\";\nimport type { Api, AssistantMessage, Context, Model, SimpleStreamOptions, ThinkingLevel } from \"@ch1nyzzz/pi-ai\";\nimport { completeSimple } from \"@ch1nyzzz/pi-ai/compat\";\nimport type { ModelRegistry } from \"@ch1nyzzz/pi-coding-agent\";\nimport { canonicalJson } from \"../../storage.ts\";\nimport type {\n\tEvoCapabilityExecutionResult,\n\tEvoCapabilityService,\n\tEvoCapabilityServiceContext,\n\tEvoPreparedCapabilityRequest,\n} from \"./service.ts\";\n\nconst THINKING_LEVELS = new Set<ThinkingLevel>([\"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"]);\nconst PROMPT_PROTOCOL_TOKEN_ALLOWANCE = 1_024;\n\nexport interface EvoInferRequest {\n\tmodel: string;\n\tsystemPrompt?: string;\n\tprompt: string;\n\tmaxOutputTokens: number;\n\treasoning?: ThinkingLevel;\n}\n\nexport interface EvoRawInferHost {\n\tresolveModel(route: string): Model<Api> | undefined;\n\tcompleteSimple(\n\t\tmodel: Model<Api>,\n\t\tcontext: Context,\n\t\toptions: Pick<SimpleStreamOptions, \"maxTokens\" | \"reasoning\" | \"signal\">,\n\t): Promise<AssistantMessage>;\n}\n\nfunction asRecord(value: unknown, label: string): Record<string, unknown> {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new Error(`${label} must be an object`);\n\t}\n\treturn value as Record<string, unknown>;\n}\n\nfunction rejectUnknownKeys(record: Record<string, unknown>, allowed: readonly string[], label: string): void {\n\tconst allowedKeys = new Set(allowed);\n\tfor (const key of Object.keys(record)) {\n\t\tif (!allowedKeys.has(key)) throw new Error(`${label} has unknown key: ${key}`);\n\t}\n}\n\nfunction parseInferRequest(value: unknown): EvoInferRequest {\n\tconst request = asRecord(value, \"infer request\");\n\trejectUnknownKeys(request, [\"model\", \"systemPrompt\", \"prompt\", \"maxOutputTokens\", \"reasoning\"], \"infer request\");\n\tif (\n\t\ttypeof request.model !== \"string\" ||\n\t\t!request.model.includes(\"/\") ||\n\t\trequest.model.startsWith(\"/\") ||\n\t\trequest.model.endsWith(\"/\") ||\n\t\trequest.model.length > 300 ||\n\t\t/\\s/.test(request.model)\n\t) {\n\t\tthrow new Error(\"infer request.model must be a provider/model route\");\n\t}\n\tif (request.systemPrompt !== undefined && typeof request.systemPrompt !== \"string\") {\n\t\tthrow new Error(\"infer request.systemPrompt must be a string\");\n\t}\n\tif (typeof request.prompt !== \"string\" || !request.prompt) {\n\t\tthrow new Error(\"infer request.prompt must be a non-empty string\");\n\t}\n\tif (!Number.isSafeInteger(request.maxOutputTokens) || (request.maxOutputTokens as number) <= 0) {\n\t\tthrow new Error(\"infer request.maxOutputTokens must be a positive safe integer\");\n\t}\n\tif (request.reasoning !== undefined && !THINKING_LEVELS.has(request.reasoning as ThinkingLevel)) {\n\t\tthrow new Error(\"infer request.reasoning is invalid\");\n\t}\n\treturn {\n\t\tmodel: request.model,\n\t\t...(request.systemPrompt === undefined ? {} : { systemPrompt: request.systemPrompt }),\n\t\tprompt: request.prompt,\n\t\tmaxOutputTokens: request.maxOutputTokens as number,\n\t\t...(request.reasoning === undefined ? {} : { reasoning: request.reasoning as ThinkingLevel }),\n\t};\n}\n\nfunction maximumRates(model: Model<Api>): { input: number; output: number } {\n\tconst rates = [model.cost, ...(model.cost.tiers ?? [])];\n\treturn {\n\t\tinput: Math.max(...rates.map((rate) => Math.max(rate.input * 2, rate.cacheWrite, rate.cacheRead))),\n\t\toutput: Math.max(...rates.map((rate) => rate.output)),\n\t};\n}\n\nfunction reservationFor(model: Model<Api>, request: EvoInferRequest) {\n\tconst promptBytes =\n\t\tBuffer.byteLength(request.systemPrompt ?? \"\", \"utf8\") + Buffer.byteLength(request.prompt, \"utf8\");\n\tconst inputTokens = promptBytes + PROMPT_PROTOCOL_TOKEN_ALLOWANCE;\n\tif (request.maxOutputTokens > model.maxTokens) {\n\t\tthrow new Error(`infer request.maxOutputTokens exceeds model limit ${model.maxTokens}`);\n\t}\n\tif (inputTokens + request.maxOutputTokens > model.contextWindow) {\n\t\tthrow new Error(\"infer request exceeds the model context window under the conservative token bound\");\n\t}\n\tconst rates = maximumRates(model);\n\treturn {\n\t\tinputTokens,\n\t\toutputTokens: request.maxOutputTokens,\n\t\ttotalTokens: inputTokens + request.maxOutputTokens,\n\t\tcostUsd: (inputTokens * rates.input + request.maxOutputTokens * rates.output) / 1_000_000,\n\t};\n}\n\nfunction usageFrom(message: AssistantMessage) {\n\tconst usage = message.usage;\n\tconst values = [usage.input, usage.output, usage.totalTokens, usage.cost.total];\n\tif (values.some((value) => typeof value !== \"number\" || !Number.isFinite(value) || value < 0)) {\n\t\tthrow new Error(\"infer host returned invalid usage\");\n\t}\n\treturn {\n\t\tinputTokens: usage.input + usage.cacheRead + usage.cacheWrite,\n\t\toutputTokens: usage.output,\n\t\ttotalTokens: usage.totalTokens,\n\t\tcostUsd: usage.cost.total,\n\t};\n}\n\nexport function createModelRegistryInferHost(\n\tmodelRegistry: Pick<ModelRegistry, \"find\" | \"getApiKeyAndHeaders\">,\n): EvoRawInferHost {\n\treturn {\n\t\tresolveModel(route) {\n\t\t\tconst slash = route.indexOf(\"/\");\n\t\t\treturn slash <= 0 ? undefined : modelRegistry.find(route.slice(0, slash), route.slice(slash + 1));\n\t\t},\n\t\tasync completeSimple(model, context, options) {\n\t\t\tconst signal = options.signal;\n\t\t\tif (signal?.aborted) {\n\t\t\t\tthrow signal.reason instanceof Error ? signal.reason : new Error(\"Infer request aborted\");\n\t\t\t}\n\t\t\tconst pendingAuth = modelRegistry.getApiKeyAndHeaders(model);\n\t\t\tlet removeAbortListener = (): void => {};\n\t\t\tconst aborted = new Promise<never>((_resolve, reject) => {\n\t\t\t\tif (!signal) return;\n\t\t\t\tconst onAbort = (): void => {\n\t\t\t\t\treject(signal.reason instanceof Error ? signal.reason : new Error(\"Infer request aborted\"));\n\t\t\t\t};\n\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\tremoveAbortListener = (): void => signal.removeEventListener(\"abort\", onAbort);\n\t\t\t\tif (signal.aborted) onAbort();\n\t\t\t});\n\t\t\tconst auth = await Promise.race([pendingAuth, aborted]).finally(removeAbortListener);\n\t\t\tif (!auth.ok) throw new Error(auth.error);\n\t\t\treturn completeSimple(model, context, {\n\t\t\t\t...options,\n\t\t\t\tapiKey: auth.apiKey,\n\t\t\t\theaders: auth.headers,\n\t\t\t\tenv: auth.env,\n\t\t\t});\n\t\t},\n\t};\n}\n\nexport function createInferCapabilityService(host: EvoRawInferHost): EvoCapabilityService {\n\treturn {\n\t\tprepare(payload: unknown): EvoPreparedCapabilityRequest {\n\t\t\tconst request = parseInferRequest(payload);\n\t\t\tconst model = host.resolveModel(request.model);\n\t\t\tif (!model) throw new Error(`infer model is unavailable: ${request.model}`);\n\t\t\treturn {\n\t\t\t\trequest,\n\t\t\t\treservation: reservationFor(model, request),\n\t\t\t\tauthorization: { model: request.model, maxOutputTokens: request.maxOutputTokens },\n\t\t\t};\n\t\t},\n\t\tasync execute(value: unknown, context: EvoCapabilityServiceContext): Promise<EvoCapabilityExecutionResult> {\n\t\t\tconst request = parseInferRequest(value);\n\t\t\tconst model = host.resolveModel(request.model);\n\t\t\tif (!model) throw new Error(`infer model became unavailable: ${request.model}`);\n\t\t\tconst message = await host.completeSimple(\n\t\t\t\tmodel,\n\t\t\t\t{\n\t\t\t\t\t...(request.systemPrompt === undefined ? {} : { systemPrompt: request.systemPrompt }),\n\t\t\t\t\tmessages: [{ role: \"user\", content: request.prompt, timestamp: Date.now() }],\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tmaxTokens: request.maxOutputTokens,\n\t\t\t\t\tsignal: context.signal,\n\t\t\t\t\t...(request.reasoning === undefined ? {} : { reasoning: request.reasoning }),\n\t\t\t\t},\n\t\t\t);\n\t\t\tconst result = JSON.parse(\n\t\t\t\tcanonicalJson({\n\t\t\t\t\tmodel: `${message.provider}/${message.model}`,\n\t\t\t\t\tcontent: message.content,\n\t\t\t\t\tstopReason: message.stopReason,\n\t\t\t\t\t...(message.errorMessage === undefined ? {} : { errorMessage: message.errorMessage }),\n\t\t\t\t\tusage: message.usage,\n\t\t\t\t}),\n\t\t\t) as unknown;\n\t\t\treturn { result, usage: usageFrom(message) };\n\t\t},\n\t};\n}\n"]}