dsh-kenari-plugin 0.1.1
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.
- package/LICENSE +21 -0
- package/README.en.md +281 -0
- package/README.md +283 -0
- package/assets/kenari-favicon-128.png +0 -0
- package/assets/kenari-favicon-256.png +0 -0
- package/client/index.js +1998 -0
- package/cordis.patch.yml +137 -0
- package/lib/billing.d.ts +120 -0
- package/lib/billing.d.ts.map +1 -0
- package/lib/billing.js +209 -0
- package/lib/billing.js.map +1 -0
- package/lib/catalog-view.d.ts +78 -0
- package/lib/catalog-view.d.ts.map +1 -0
- package/lib/catalog-view.js +113 -0
- package/lib/catalog-view.js.map +1 -0
- package/lib/catalog.d.ts +239 -0
- package/lib/catalog.d.ts.map +1 -0
- package/lib/catalog.js +445 -0
- package/lib/catalog.js.map +1 -0
- package/lib/default-route.d.ts +35 -0
- package/lib/default-route.d.ts.map +1 -0
- package/lib/default-route.js +111 -0
- package/lib/default-route.js.map +1 -0
- package/lib/errors.d.ts +29 -0
- package/lib/errors.d.ts.map +1 -0
- package/lib/errors.js +74 -0
- package/lib/errors.js.map +1 -0
- package/lib/favicon.d.ts +24 -0
- package/lib/favicon.d.ts.map +1 -0
- package/lib/favicon.js +68 -0
- package/lib/favicon.js.map +1 -0
- package/lib/http.d.ts +104 -0
- package/lib/http.d.ts.map +1 -0
- package/lib/http.js +247 -0
- package/lib/http.js.map +1 -0
- package/lib/index.d.ts +83 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +285 -0
- package/lib/index.js.map +1 -0
- package/lib/llm/adapter.d.ts +78 -0
- package/lib/llm/adapter.d.ts.map +1 -0
- package/lib/llm/adapter.js +429 -0
- package/lib/llm/adapter.js.map +1 -0
- package/lib/llm/candidates.d.ts +57 -0
- package/lib/llm/candidates.d.ts.map +1 -0
- package/lib/llm/candidates.js +86 -0
- package/lib/llm/candidates.js.map +1 -0
- package/lib/llm/recovery.d.ts +48 -0
- package/lib/llm/recovery.d.ts.map +1 -0
- package/lib/llm/recovery.js +118 -0
- package/lib/llm/recovery.js.map +1 -0
- package/lib/llm/retry.d.ts +35 -0
- package/lib/llm/retry.d.ts.map +1 -0
- package/lib/llm/retry.js +53 -0
- package/lib/llm/retry.js.map +1 -0
- package/lib/plans.d.ts +59 -0
- package/lib/plans.d.ts.map +1 -0
- package/lib/plans.js +118 -0
- package/lib/plans.js.map +1 -0
- package/lib/session-title.d.ts +63 -0
- package/lib/session-title.d.ts.map +1 -0
- package/lib/session-title.js +280 -0
- package/lib/session-title.js.map +1 -0
- package/lib/settings.d.ts +35 -0
- package/lib/settings.d.ts.map +1 -0
- package/lib/settings.js +78 -0
- package/lib/settings.js.map +1 -0
- package/lib/tools/account.d.ts +11 -0
- package/lib/tools/account.d.ts.map +1 -0
- package/lib/tools/account.js +132 -0
- package/lib/tools/account.js.map +1 -0
- package/lib/tools/billing.d.ts +10 -0
- package/lib/tools/billing.d.ts.map +1 -0
- package/lib/tools/billing.js +100 -0
- package/lib/tools/billing.js.map +1 -0
- package/lib/tools/count-tokens.d.ts +11 -0
- package/lib/tools/count-tokens.d.ts.map +1 -0
- package/lib/tools/count-tokens.js +84 -0
- package/lib/tools/count-tokens.js.map +1 -0
- package/lib/tools/data.d.ts +12 -0
- package/lib/tools/data.d.ts.map +1 -0
- package/lib/tools/data.js +205 -0
- package/lib/tools/data.js.map +1 -0
- package/lib/tools/docs.d.ts +13 -0
- package/lib/tools/docs.d.ts.map +1 -0
- package/lib/tools/docs.js +209 -0
- package/lib/tools/docs.js.map +1 -0
- package/lib/tools/documents.d.ts +10 -0
- package/lib/tools/documents.d.ts.map +1 -0
- package/lib/tools/documents.js +118 -0
- package/lib/tools/documents.js.map +1 -0
- package/lib/tools/media.d.ts +28 -0
- package/lib/tools/media.d.ts.map +1 -0
- package/lib/tools/media.js +599 -0
- package/lib/tools/media.js.map +1 -0
- package/lib/tools/shared.d.ts +89 -0
- package/lib/tools/shared.d.ts.map +1 -0
- package/lib/tools/shared.js +128 -0
- package/lib/tools/shared.js.map +1 -0
- package/lib/tools/x-search.d.ts +11 -0
- package/lib/tools/x-search.d.ts.map +1 -0
- package/lib/tools/x-search.js +129 -0
- package/lib/tools/x-search.js.map +1 -0
- package/lib/web/fallback.d.ts +39 -0
- package/lib/web/fallback.d.ts.map +1 -0
- package/lib/web/fallback.js +79 -0
- package/lib/web/fallback.js.map +1 -0
- package/lib/web/fetch.d.ts +19 -0
- package/lib/web/fetch.d.ts.map +1 -0
- package/lib/web/fetch.js +41 -0
- package/lib/web/fetch.js.map +1 -0
- package/lib/web/search.d.ts +21 -0
- package/lib/web/search.d.ts.map +1 -0
- package/lib/web/search.js +47 -0
- package/lib/web/search.js.map +1 -0
- package/package.json +84 -0
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kenari 原生 LlmAdapter(第 5 期,**可选**,默认关闭)。
|
|
3
|
+
*
|
|
4
|
+
* 为什么有它:`llm-pi-ai` 预设(第 3 期)已经能把 Kenari 当会话模型用,但它按通用
|
|
5
|
+
* OpenAI 兼容语义处理线上字段,丢掉了几样 Kenari 特有的东西:
|
|
6
|
+
*
|
|
7
|
+
* 1. **真实价格进模型选择器**:目录里的 `pricing` 换算成 IDR/1M 写进 `description`
|
|
8
|
+
* 2. **token 与费用进账本**:pi-ai 的 usage 到不了本插件,`kenari_billing` 的
|
|
9
|
+
* `cached_tokens` 命中率因此一直没有数据;本适配器逐次把 usage 记进同一本账
|
|
10
|
+
* 3. **推理档位按目录原样暴露**:`reasoning_options` 的值就是线上 `reasoning_effort`
|
|
11
|
+
* 的写法(含 `none`),不再需要 off/none 的键位翻译
|
|
12
|
+
* 4. **reasoning 通道**:`reasoning_content` → `reasoning-delta`,与可见文本分开
|
|
13
|
+
*
|
|
14
|
+
* 不做的事:不回放思考块、不注入 `file-parser`(文件块投影成说明文本,读文档请用
|
|
15
|
+
* `kenari_ocr` 工具)。这些在 README 里写明。
|
|
16
|
+
*
|
|
17
|
+
* 与预设路由**不同名**(默认 `kenari-direct`),所以两者可以并存、可以回退。
|
|
18
|
+
* @module dsh-kenari-plugin/llm/adapter
|
|
19
|
+
*/
|
|
20
|
+
import { LlmAdapter, LlmError, attributionHeaders, isContextWindowExceededError } from '@deepseek-ai/dsh-llm';
|
|
21
|
+
import { inputModalitiesOf, isFreeModel, microToIdrPerMillion, toResolvedModelInfo } from '../catalog.js';
|
|
22
|
+
import { GLOBAL_SCOPE } from '../billing.js';
|
|
23
|
+
import { extractKenariErrorMessage } from '../errors.js';
|
|
24
|
+
/** 会话模型的价格摘要(进选择器 description;不确定的量不写)。 */
|
|
25
|
+
function describePricing(model) {
|
|
26
|
+
const bits = [];
|
|
27
|
+
if (isFreeModel(model)) {
|
|
28
|
+
bits.push('免费');
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
const input = model.pricing?.input;
|
|
32
|
+
const output = model.pricing?.output;
|
|
33
|
+
if (typeof input === 'number' && typeof output === 'number') {
|
|
34
|
+
bits.push(`入 ${microToIdrPerMillion(input)} / 出 ${microToIdrPerMillion(output)} IDR per 1M tokens`);
|
|
35
|
+
}
|
|
36
|
+
else if (model.pricing?.varies === true) {
|
|
37
|
+
bits.push('价格浮动');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (typeof model.context_length === 'number')
|
|
41
|
+
bits.push(`ctx ${model.context_length}`);
|
|
42
|
+
if (inputModalitiesOf(model).includes('image'))
|
|
43
|
+
bits.push('视觉');
|
|
44
|
+
if ((model.reasoning_options ?? []).length > 0)
|
|
45
|
+
bits.push(`推理 ${(model.reasoning_options ?? []).join('/')}`);
|
|
46
|
+
return bits.join(' · ');
|
|
47
|
+
}
|
|
48
|
+
/** HTTP 状态 → dsh 的 provider-neutral 失败码(路由靠 code,不靠 message)。 */
|
|
49
|
+
function failureCodeFor(status, detail) {
|
|
50
|
+
if (status === 401 || status === 403)
|
|
51
|
+
return 'AUTH';
|
|
52
|
+
if (status === 402)
|
|
53
|
+
return 'QUOTA';
|
|
54
|
+
if (status === 429)
|
|
55
|
+
return 'RATE_LIMIT';
|
|
56
|
+
if (isContextWindowExceededError(detail))
|
|
57
|
+
return 'CONTEXT_WINDOW_EXCEEDED';
|
|
58
|
+
return status >= 500 ? 'TRANSPORT' : 'INVALID_REQUEST';
|
|
59
|
+
}
|
|
60
|
+
/** 嵌套块 → 纯文本(工具结果、文件引用等只能以文本回传的情形)。 */
|
|
61
|
+
function textOfBlocks(blocks) {
|
|
62
|
+
const parts = [];
|
|
63
|
+
for (const block of blocks) {
|
|
64
|
+
if (block.type === 'text')
|
|
65
|
+
parts.push(block.text);
|
|
66
|
+
else if (block.type === 'reasoning')
|
|
67
|
+
continue;
|
|
68
|
+
else if (block.type === 'tool-result')
|
|
69
|
+
parts.push(textOfBlocks(block.content));
|
|
70
|
+
else if (block.type === 'image')
|
|
71
|
+
parts.push(`[图像 ${block.attachment.width}×${block.attachment.height}]`);
|
|
72
|
+
else if (block.type === 'file')
|
|
73
|
+
parts.push(`[文件 ${block.attachment.name}]`);
|
|
74
|
+
}
|
|
75
|
+
return parts.filter((part) => part.length > 0).join('\n');
|
|
76
|
+
}
|
|
77
|
+
/** 图像引用 → data URI;没有 attachment 存储时返回 undefined(调用方降级为文本)。 */
|
|
78
|
+
async function imageDataUri(imageRef, read) {
|
|
79
|
+
if (read === undefined)
|
|
80
|
+
return undefined;
|
|
81
|
+
try {
|
|
82
|
+
const stored = await read(imageRef);
|
|
83
|
+
return `data:${stored.ref.mediaType};base64,${Buffer.from(stored.data).toString('base64')}`;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** 一条 dsh 消息 → 一条或多条 [OI] 消息。 */
|
|
90
|
+
async function toWireMessages(message, read) {
|
|
91
|
+
// 工具结果:dsh 把它挂在一条 user 角色的消息上,线上要用 role=tool + tool_call_id
|
|
92
|
+
if (message.source.kind === 'tool') {
|
|
93
|
+
return [{ role: 'tool', tool_call_id: String(message.source.callId), content: textOfBlocks(message.content) }];
|
|
94
|
+
}
|
|
95
|
+
if (message.role === 'assistant') {
|
|
96
|
+
const text = textOfBlocks(message.content);
|
|
97
|
+
const calls = message.content
|
|
98
|
+
.filter((block) => block.type === 'tool-call')
|
|
99
|
+
.map((block) => ({ id: String(block.id), type: 'function', function: { name: block.name, arguments: block.arguments } }));
|
|
100
|
+
return [{
|
|
101
|
+
role: 'assistant',
|
|
102
|
+
// content 为 null 表示「只有工具调用」,[OI] 接受这种组合
|
|
103
|
+
content: text.length > 0 ? text : null,
|
|
104
|
+
...(calls.length > 0 ? { tool_calls: calls } : {}),
|
|
105
|
+
}];
|
|
106
|
+
}
|
|
107
|
+
// user / plugin / system:文本 + 图像走 parts,纯文本走字符串(线上两种都收)
|
|
108
|
+
const parts = [];
|
|
109
|
+
const texts = [];
|
|
110
|
+
for (const block of message.content) {
|
|
111
|
+
if (block.type === 'text') {
|
|
112
|
+
texts.push(block.text);
|
|
113
|
+
parts.push({ type: 'text', text: block.text });
|
|
114
|
+
}
|
|
115
|
+
else if (block.type === 'reasoning') {
|
|
116
|
+
// 思考块不属于对话内容,不回放
|
|
117
|
+
}
|
|
118
|
+
else if (block.type === 'image') {
|
|
119
|
+
const uri = await imageDataUri(block.attachment, read);
|
|
120
|
+
if (uri === undefined) {
|
|
121
|
+
texts.push('[图像:本部署没有可读的 attachment 存储,无法回传]');
|
|
122
|
+
parts.push({ type: 'text', text: '[图像:本部署没有可读的 attachment 存储,无法回传]' });
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
parts.push({ type: 'image_url', image_url: { url: uri } });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else if (block.type === 'file') {
|
|
129
|
+
const note = `[文件 ${block.attachment.name}:本适配器不回传文件字节,读文档请用 kenari_ocr 工具]`;
|
|
130
|
+
texts.push(note);
|
|
131
|
+
parts.push({ type: 'text', text: note });
|
|
132
|
+
}
|
|
133
|
+
else if (block.type === 'tool-result') {
|
|
134
|
+
const nested = textOfBlocks(block.content);
|
|
135
|
+
texts.push(nested);
|
|
136
|
+
parts.push({ type: 'text', text: nested });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const hasImage = parts.some((part) => part.type === 'image_url');
|
|
140
|
+
return [{
|
|
141
|
+
role: message.role === 'system' ? 'system' : 'user',
|
|
142
|
+
content: hasImage ? parts : texts.join('\n'),
|
|
143
|
+
}];
|
|
144
|
+
}
|
|
145
|
+
/** dsh ToolSchema → [OI] tools 条目。 */
|
|
146
|
+
function toWireTool(tool) {
|
|
147
|
+
return { type: 'function', function: { name: tool.name, description: tool.description, parameters: tool.parameters } };
|
|
148
|
+
}
|
|
149
|
+
/** [OI] usage → dsh TokenUsage(dsh 要求各项**互斥**:inputTokens 是不含缓存的那部分)。 */
|
|
150
|
+
export function mapUsage(usage) {
|
|
151
|
+
const cached = usage.prompt_tokens_details?.cached_tokens ?? 0;
|
|
152
|
+
const prompt = usage.prompt_tokens ?? 0;
|
|
153
|
+
const mapped = {
|
|
154
|
+
inputTokens: Math.max(0, prompt - cached),
|
|
155
|
+
outputTokens: usage.completion_tokens ?? 0,
|
|
156
|
+
};
|
|
157
|
+
if (cached > 0)
|
|
158
|
+
mapped.cacheReadTokens = cached;
|
|
159
|
+
if (typeof usage.total_tokens === 'number')
|
|
160
|
+
mapped.totalTokens = usage.total_tokens;
|
|
161
|
+
return mapped;
|
|
162
|
+
}
|
|
163
|
+
/** 逐行切 SSE:`data: <payload>`,空行与注释行跳过。 */
|
|
164
|
+
async function* ssePayloads(body) {
|
|
165
|
+
const decoder = new TextDecoder();
|
|
166
|
+
let buffer = '';
|
|
167
|
+
for await (const chunk of body) {
|
|
168
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
169
|
+
let newline = buffer.indexOf('\n');
|
|
170
|
+
while (newline !== -1) {
|
|
171
|
+
const line = buffer.slice(0, newline).trim();
|
|
172
|
+
buffer = buffer.slice(newline + 1);
|
|
173
|
+
if (line.startsWith('data:'))
|
|
174
|
+
yield line.slice(5).trim();
|
|
175
|
+
newline = buffer.indexOf('\n');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const tail = buffer.trim();
|
|
179
|
+
if (tail.startsWith('data:'))
|
|
180
|
+
yield tail.slice(5).trim();
|
|
181
|
+
}
|
|
182
|
+
/** Kenari [OI] 线的原生适配器。 */
|
|
183
|
+
export class KenariLlmAdapter extends LlmAdapter {
|
|
184
|
+
deps;
|
|
185
|
+
readImage;
|
|
186
|
+
constructor(deps,
|
|
187
|
+
/** 目录里读图像字节的入口(`ctx.attachments.readImage`);缺失则图像降级为文本。 */
|
|
188
|
+
readImage) {
|
|
189
|
+
super();
|
|
190
|
+
this.deps = deps;
|
|
191
|
+
this.readImage = readImage;
|
|
192
|
+
}
|
|
193
|
+
providerInfo(provider) {
|
|
194
|
+
return { id: provider, name: 'Kenari(原生适配器)' };
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* 把插件配置拼出的重试策略交给 dsh 的重试机制。基类默认返回 undefined,
|
|
198
|
+
* 也就是走 dsh 默认策略(指数退避 500ms→10s、5 次)。
|
|
199
|
+
* 注意 dsh 只在 `registerAdapter()` 时读取一次并冻结,所以改配置要重启才生效。
|
|
200
|
+
*/
|
|
201
|
+
providerRetryPolicy(_provider) {
|
|
202
|
+
return this.deps.retryPolicy;
|
|
203
|
+
}
|
|
204
|
+
/** 目录即模型列表:价格与上下文随目录刷新,不再静态写死在配置里。 */
|
|
205
|
+
async listModels(provider) {
|
|
206
|
+
const models = await this.deps.catalog.chatModels();
|
|
207
|
+
return models.map((model) => ({
|
|
208
|
+
provider,
|
|
209
|
+
id: model.id,
|
|
210
|
+
name: model.name ?? model.id,
|
|
211
|
+
description: describePricing(model),
|
|
212
|
+
inputModalities: inputModalitiesOf(model),
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
215
|
+
/** 逐模型元数据:上下文窗口、推理档位、输出上限都来自目录。 */
|
|
216
|
+
async resolveModel(provider, model) {
|
|
217
|
+
const resolved = await this.deps.catalog.resolve(model);
|
|
218
|
+
// 目录解析失败不拒绝请求(目录是建议性的),只给出最小元数据
|
|
219
|
+
if (resolved === undefined)
|
|
220
|
+
return { provider, id: model, name: model, inputModalities: ['text'] };
|
|
221
|
+
return toResolvedModelInfo(resolved.model, provider);
|
|
222
|
+
}
|
|
223
|
+
stream(options) {
|
|
224
|
+
return this.run(options);
|
|
225
|
+
}
|
|
226
|
+
async *run(options) {
|
|
227
|
+
const { deps } = this;
|
|
228
|
+
const config = deps.http.config;
|
|
229
|
+
const apiKey = await deps.http.resolveApiKey();
|
|
230
|
+
if (apiKey === undefined || apiKey.length === 0) {
|
|
231
|
+
throw new LlmError(`Kenari 凭据 ${config.apiKeyEnv ?? 'KENARI_API_KEY'} 未配置,无法发起模型请求`, 'MISSING_CREDENTIAL');
|
|
232
|
+
}
|
|
233
|
+
const wireMessages = [];
|
|
234
|
+
if (options.system !== undefined && options.system.length > 0) {
|
|
235
|
+
wireMessages.push({ role: 'system', content: options.system });
|
|
236
|
+
}
|
|
237
|
+
for (const message of options.messages) {
|
|
238
|
+
wireMessages.push(...await toWireMessages(message, this.readImage));
|
|
239
|
+
}
|
|
240
|
+
const base = (config.baseURL ?? 'https://kenari.id').replace(/\/+$/, '');
|
|
241
|
+
const endpoint = `${base.endsWith('/v1') ? base : `${base}/v1`}/chat/completions`;
|
|
242
|
+
const body = {
|
|
243
|
+
model: options.model,
|
|
244
|
+
messages: wireMessages,
|
|
245
|
+
stream: true,
|
|
246
|
+
// 最后一帧带 usage:cached_tokens 计量的唯一来源
|
|
247
|
+
stream_options: { include_usage: true },
|
|
248
|
+
};
|
|
249
|
+
if (options.maxTokens !== undefined)
|
|
250
|
+
body.max_tokens = options.maxTokens;
|
|
251
|
+
if (options.temperature !== undefined)
|
|
252
|
+
body.temperature = options.temperature;
|
|
253
|
+
if (options.stop !== undefined && options.stop.length > 0)
|
|
254
|
+
body.stop = options.stop;
|
|
255
|
+
if (options.reasoningEffort !== undefined)
|
|
256
|
+
body.reasoning_effort = String(options.reasoningEffort);
|
|
257
|
+
if (options.tools !== undefined && options.tools.length > 0)
|
|
258
|
+
body.tools = options.tools.map(toWireTool);
|
|
259
|
+
const response = await fetch(endpoint, {
|
|
260
|
+
method: 'POST',
|
|
261
|
+
// 契约:每个 provider 请求都必须带 attribution 头
|
|
262
|
+
headers: {
|
|
263
|
+
...attributionHeaders(),
|
|
264
|
+
authorization: `Bearer ${apiKey}`,
|
|
265
|
+
'content-type': 'application/json',
|
|
266
|
+
accept: 'text/event-stream',
|
|
267
|
+
},
|
|
268
|
+
body: JSON.stringify(body),
|
|
269
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
270
|
+
});
|
|
271
|
+
if (!response.ok) {
|
|
272
|
+
const raw = await response.text().catch(() => '');
|
|
273
|
+
const detail = extractKenariErrorMessage(raw, response.status);
|
|
274
|
+
deps.logger?.warn(`kenari: ${endpoint} → HTTP ${response.status}`);
|
|
275
|
+
throw new LlmError(`kenari: ${detail}`, failureCodeFor(response.status, detail), { status: response.status });
|
|
276
|
+
}
|
|
277
|
+
if (response.body === null)
|
|
278
|
+
throw new LlmError('kenari: 响应没有 body', 'TRANSPORT');
|
|
279
|
+
let textIndex;
|
|
280
|
+
let reasoningIndex;
|
|
281
|
+
let nextIndex = 0;
|
|
282
|
+
let textBuffer = '';
|
|
283
|
+
let reasoningBuffer = '';
|
|
284
|
+
const toolStates = new Map();
|
|
285
|
+
let usage;
|
|
286
|
+
let finishKind = { kind: 'stop' };
|
|
287
|
+
let emitted = 0;
|
|
288
|
+
const closeText = function* () {
|
|
289
|
+
if (textIndex === undefined)
|
|
290
|
+
return;
|
|
291
|
+
yield { type: 'block-end', index: textIndex, block: { type: 'text', text: textBuffer } };
|
|
292
|
+
textIndex = undefined;
|
|
293
|
+
textBuffer = '';
|
|
294
|
+
};
|
|
295
|
+
const closeReasoning = function* () {
|
|
296
|
+
if (reasoningIndex === undefined)
|
|
297
|
+
return;
|
|
298
|
+
yield { type: 'block-end', index: reasoningIndex, block: { type: 'reasoning', text: reasoningBuffer } };
|
|
299
|
+
reasoningIndex = undefined;
|
|
300
|
+
reasoningBuffer = '';
|
|
301
|
+
};
|
|
302
|
+
for await (const payload of ssePayloads(response.body)) {
|
|
303
|
+
if (payload === '[DONE]')
|
|
304
|
+
break;
|
|
305
|
+
let parsed;
|
|
306
|
+
try {
|
|
307
|
+
parsed = JSON.parse(payload);
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (parsed.usage !== undefined)
|
|
313
|
+
usage = mapUsage(parsed.usage);
|
|
314
|
+
const choice = parsed.choices?.[0];
|
|
315
|
+
const delta = choice?.delta;
|
|
316
|
+
if (delta !== undefined) {
|
|
317
|
+
const reasoning = delta.reasoning_content ?? delta.reasoning;
|
|
318
|
+
if (typeof reasoning === 'string' && reasoning.length > 0) {
|
|
319
|
+
yield* closeText();
|
|
320
|
+
if (reasoningIndex === undefined) {
|
|
321
|
+
reasoningIndex = nextIndex++;
|
|
322
|
+
yield { type: 'block-start', index: reasoningIndex, blockType: 'reasoning' };
|
|
323
|
+
}
|
|
324
|
+
reasoningBuffer += reasoning;
|
|
325
|
+
emitted++;
|
|
326
|
+
yield { type: 'reasoning-delta', index: reasoningIndex, text: reasoning };
|
|
327
|
+
}
|
|
328
|
+
if (typeof delta.content === 'string' && delta.content.length > 0) {
|
|
329
|
+
yield* closeReasoning();
|
|
330
|
+
if (textIndex === undefined) {
|
|
331
|
+
textIndex = nextIndex++;
|
|
332
|
+
yield { type: 'block-start', index: textIndex, blockType: 'text' };
|
|
333
|
+
}
|
|
334
|
+
textBuffer += delta.content;
|
|
335
|
+
emitted++;
|
|
336
|
+
yield { type: 'text-delta', index: textIndex, text: delta.content };
|
|
337
|
+
}
|
|
338
|
+
for (const call of delta.tool_calls ?? []) {
|
|
339
|
+
yield* closeText();
|
|
340
|
+
yield* closeReasoning();
|
|
341
|
+
const wireIndex = call.index ?? 0;
|
|
342
|
+
let state = toolStates.get(wireIndex);
|
|
343
|
+
if (state === undefined) {
|
|
344
|
+
state = { index: nextIndex++, id: call.id ?? `call_kenari_${wireIndex}_${Date.now()}`, name: '', args: '' };
|
|
345
|
+
toolStates.set(wireIndex, state);
|
|
346
|
+
yield { type: 'block-start', index: state.index, blockType: 'tool-call' };
|
|
347
|
+
}
|
|
348
|
+
if (call.id !== undefined)
|
|
349
|
+
state.id = call.id;
|
|
350
|
+
const namePart = call.function?.name;
|
|
351
|
+
if (namePart !== undefined && namePart.length > 0)
|
|
352
|
+
state.name += namePart;
|
|
353
|
+
const argsPart = call.function?.arguments ?? '';
|
|
354
|
+
state.args += argsPart;
|
|
355
|
+
emitted++;
|
|
356
|
+
yield {
|
|
357
|
+
type: 'tool-call-delta',
|
|
358
|
+
index: state.index,
|
|
359
|
+
id: state.id,
|
|
360
|
+
...(namePart !== undefined && namePart.length > 0 ? { name: namePart } : {}),
|
|
361
|
+
argumentsDelta: argsPart,
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (choice?.finish_reason != null) {
|
|
366
|
+
finishKind = choice.finish_reason === 'tool_calls' || choice.finish_reason === 'tool_call'
|
|
367
|
+
? { kind: 'tool-calls' }
|
|
368
|
+
: choice.finish_reason === 'length'
|
|
369
|
+
? { kind: 'max-tokens' }
|
|
370
|
+
: { kind: 'stop' };
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
yield* closeText();
|
|
374
|
+
yield* closeReasoning();
|
|
375
|
+
for (const state of toolStates.values()) {
|
|
376
|
+
yield {
|
|
377
|
+
type: 'block-end',
|
|
378
|
+
index: state.index,
|
|
379
|
+
block: { type: 'tool-call', id: state.id, name: state.name, arguments: state.args },
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
if (usage !== undefined) {
|
|
383
|
+
yield { type: 'usage', usage };
|
|
384
|
+
this.recordUsage(options, usage);
|
|
385
|
+
}
|
|
386
|
+
// 退化响应:一次空完成会让 turn 静默结束,按契约显式归类
|
|
387
|
+
if (emitted === 0) {
|
|
388
|
+
throw new LlmError('kenari: 模型返回了空响应(没有文本、思考或工具调用)', 'EMPTY_RESPONSE');
|
|
389
|
+
}
|
|
390
|
+
yield { type: 'finish', reason: finishKind };
|
|
391
|
+
}
|
|
392
|
+
/** usage 记进共享账本:命中率直接来自 `cached_tokens`,费用按目录单价预估。 */
|
|
393
|
+
recordUsage(options, usage) {
|
|
394
|
+
const scope = options.sessionId === undefined ? GLOBAL_SCOPE : String(options.sessionId);
|
|
395
|
+
this.deps.billing.recordUsage(scope, {
|
|
396
|
+
model: options.model,
|
|
397
|
+
inputTokens: usage.inputTokens,
|
|
398
|
+
outputTokens: usage.outputTokens,
|
|
399
|
+
...(usage.cacheReadTokens === undefined ? {} : { cacheReadTokens: usage.cacheReadTokens }),
|
|
400
|
+
...(usage.cacheWriteTokens === undefined ? {} : { cacheWriteTokens: usage.cacheWriteTokens }),
|
|
401
|
+
...(usage.reasoningTokens === undefined ? {} : { reasoningTokens: usage.reasoningTokens }),
|
|
402
|
+
});
|
|
403
|
+
void this.recordSpendEstimate(options, usage, scope);
|
|
404
|
+
}
|
|
405
|
+
async recordSpendEstimate(options, usage, scope) {
|
|
406
|
+
try {
|
|
407
|
+
const resolved = await this.deps.catalog.resolve(options.model);
|
|
408
|
+
if (resolved === undefined || isFreeModel(resolved.model))
|
|
409
|
+
return;
|
|
410
|
+
const inputPrice = resolved.model.pricing?.input;
|
|
411
|
+
const outputPrice = resolved.model.pricing?.output;
|
|
412
|
+
if (typeof inputPrice !== 'number' || typeof outputPrice !== 'number')
|
|
413
|
+
return;
|
|
414
|
+
// 缓存读有独立单价就按它算,否则退回输入价(宁可高估也不谎报)
|
|
415
|
+
const cacheReadPrice = typeof resolved.model.pricing?.cache_read === 'number'
|
|
416
|
+
? resolved.model.pricing.cache_read
|
|
417
|
+
: inputPrice;
|
|
418
|
+
// token_1m 单价 × 百万 token 数:token 类保留小数,不按整单位上取
|
|
419
|
+
const microIdr = Math.round((inputPrice * usage.inputTokens
|
|
420
|
+
+ cacheReadPrice * (usage.cacheReadTokens ?? 0)
|
|
421
|
+
+ outputPrice * usage.outputTokens) / 1_000_000);
|
|
422
|
+
this.deps.billing.recordSpend(scope, { tool: `chat:${options.model}`, model: options.model, microIdr, estimated: true });
|
|
423
|
+
}
|
|
424
|
+
catch {
|
|
425
|
+
// 目录不可用时不计费,模型调用本身已完成
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../src/llm/adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 「换一个上下文 >= 当前的模型」的候选挑选。
|
|
3
|
+
*
|
|
4
|
+
* 挑最小够用者而不是最大者:一次失败不值得把会话切到又贵又慢的最大上下文模型。
|
|
5
|
+
* 阈值取**当前**模型的窗口,所以一次会话里窗口只会持平或变大,不会来回横跳。
|
|
6
|
+
* @module dsh-kenari-plugin/llm/candidates
|
|
7
|
+
*/
|
|
8
|
+
/** 候选挑选用到的 `ctx.llm` 最小面。注入是为了单测能替身,也避免把整个 LlmRuntime 拖进依赖。 */
|
|
9
|
+
export interface ModelDirectory {
|
|
10
|
+
listProviders(): readonly {
|
|
11
|
+
id: string;
|
|
12
|
+
}[];
|
|
13
|
+
listModels(provider: string): Promise<readonly {
|
|
14
|
+
id: string;
|
|
15
|
+
}[]>;
|
|
16
|
+
resolveModelInfo(provider: string, model: string, signal?: AbortSignal): Promise<{
|
|
17
|
+
context?: {
|
|
18
|
+
contextWindow: number;
|
|
19
|
+
};
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
/** 一个候选模型及其已解析的上下文窗口。 */
|
|
23
|
+
export interface CandidateModel {
|
|
24
|
+
provider: string;
|
|
25
|
+
model: string;
|
|
26
|
+
contextWindow: number;
|
|
27
|
+
}
|
|
28
|
+
/** {@link ModelCandidates} 的依赖。 */
|
|
29
|
+
export interface ModelCandidatesDeps {
|
|
30
|
+
llm: ModelDirectory;
|
|
31
|
+
/** 目录缓存时长;复用设置节的 catalogCacheTtlMs,语义同为「模型元数据可信多久」。 */
|
|
32
|
+
cacheTtlMs: number;
|
|
33
|
+
/** 注入时钟,便于测试 TTL。 */
|
|
34
|
+
now?: () => number;
|
|
35
|
+
}
|
|
36
|
+
/** `pick` 的输入。 */
|
|
37
|
+
export interface PickInput {
|
|
38
|
+
provider: string;
|
|
39
|
+
model: string;
|
|
40
|
+
contextWindow?: number;
|
|
41
|
+
signal?: AbortSignal;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 找一个上下文窗口 >= 阈值的候选:先在同一 provider 内,再跨 provider。
|
|
45
|
+
* 每次失败都去解析「所有 provider × 所有模型」太贵,所以按 provider 缓存目录。
|
|
46
|
+
*/
|
|
47
|
+
export declare class ModelCandidates {
|
|
48
|
+
private readonly deps;
|
|
49
|
+
private readonly cache;
|
|
50
|
+
private readonly now;
|
|
51
|
+
constructor(deps: ModelCandidatesDeps);
|
|
52
|
+
/** 一个 provider 下所有「上下文窗口已知」的模型。未知窗口的模型无法证明 >= 阈值,直接排除。 */
|
|
53
|
+
private entriesOf;
|
|
54
|
+
/** 找最小够用的候选;找不到返回 undefined。 */
|
|
55
|
+
pick(input: PickInput): Promise<CandidateModel | undefined>;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=candidates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"candidates.d.ts","sourceRoot":"","sources":["../../src/llm/candidates.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,+DAA+D;AAC/D,MAAM,WAAW,cAAc;IAC7B,aAAa,IAAI,SAAS;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC1C,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC,CAAA;IAChE,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC;QAAE,OAAO,CAAC,EAAE;YAAE,aAAa,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAA;CAC1H;AAED,yBAAyB;AACzB,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,mCAAmC;AACnC,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,cAAc,CAAA;IACnB,uDAAuD;IACvD,UAAU,EAAE,MAAM,CAAA;IAClB,qBAAqB;IACrB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED,kBAAkB;AAClB,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED;;;GAGG;AACH,qBAAa,eAAe;IAId,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA+D;IACrF,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAc;gBAEL,IAAI,EAAE,mBAAmB;IAItD,0DAA0D;YAC5C,SAAS;IA4BvB,gCAAgC;IAC1B,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;CAalE"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 「换一个上下文 >= 当前的模型」的候选挑选。
|
|
3
|
+
*
|
|
4
|
+
* 挑最小够用者而不是最大者:一次失败不值得把会话切到又贵又慢的最大上下文模型。
|
|
5
|
+
* 阈值取**当前**模型的窗口,所以一次会话里窗口只会持平或变大,不会来回横跳。
|
|
6
|
+
* @module dsh-kenari-plugin/llm/candidates
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* 找一个上下文窗口 >= 阈值的候选:先在同一 provider 内,再跨 provider。
|
|
10
|
+
* 每次失败都去解析「所有 provider × 所有模型」太贵,所以按 provider 缓存目录。
|
|
11
|
+
*/
|
|
12
|
+
export class ModelCandidates {
|
|
13
|
+
deps;
|
|
14
|
+
cache = new Map();
|
|
15
|
+
now;
|
|
16
|
+
constructor(deps) {
|
|
17
|
+
this.deps = deps;
|
|
18
|
+
this.now = deps.now ?? (() => Date.now());
|
|
19
|
+
}
|
|
20
|
+
/** 一个 provider 下所有「上下文窗口已知」的模型。未知窗口的模型无法证明 >= 阈值,直接排除。 */
|
|
21
|
+
async entriesOf(provider, signal) {
|
|
22
|
+
const at = this.now();
|
|
23
|
+
const cached = this.cache.get(provider);
|
|
24
|
+
if (cached !== undefined && at - cached.at < this.deps.cacheTtlMs)
|
|
25
|
+
return cached.entries;
|
|
26
|
+
let entries = [];
|
|
27
|
+
try {
|
|
28
|
+
const models = await this.deps.llm.listModels(provider);
|
|
29
|
+
const resolved = await Promise.all(models.map(async (model) => {
|
|
30
|
+
try {
|
|
31
|
+
const info = await this.deps.llm.resolveModelInfo(provider, model.id, signal);
|
|
32
|
+
const contextWindow = info.context?.contextWindow;
|
|
33
|
+
if (contextWindow === undefined || !Number.isFinite(contextWindow) || contextWindow <= 0)
|
|
34
|
+
return undefined;
|
|
35
|
+
return { provider, model: model.id, contextWindow };
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// 单个模型元数据解析失败不该毁掉整个候选列表
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
}));
|
|
42
|
+
entries = resolved.filter((entry) => entry !== undefined);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// 目录整体不可用时当作「没有候选」,让调用方走到下一阶段
|
|
46
|
+
entries = [];
|
|
47
|
+
}
|
|
48
|
+
this.cache.set(provider, { at, entries });
|
|
49
|
+
return entries;
|
|
50
|
+
}
|
|
51
|
+
/** 找最小够用的候选;找不到返回 undefined。 */
|
|
52
|
+
async pick(input) {
|
|
53
|
+
const threshold = input.contextWindow ?? 0;
|
|
54
|
+
const local = smallestAtLeast(await this.entriesOf(input.provider, input.signal), input.model, threshold);
|
|
55
|
+
if (local !== undefined)
|
|
56
|
+
return local;
|
|
57
|
+
for (const entry of this.deps.llm.listProviders()) {
|
|
58
|
+
if (entry.id === input.provider)
|
|
59
|
+
continue;
|
|
60
|
+
const found = smallestAtLeast(await this.entriesOf(entry.id, input.signal), undefined, threshold);
|
|
61
|
+
if (found !== undefined)
|
|
62
|
+
return found;
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 排除 `exclude`、取 `contextWindow >= threshold` 的最小者。
|
|
69
|
+
* 同窗口时按 model id 字典序取小 —— 确定性很重要,否则同一份配置在不同机器上会换到不同模型。
|
|
70
|
+
*/
|
|
71
|
+
function smallestAtLeast(entries, exclude, threshold) {
|
|
72
|
+
let best;
|
|
73
|
+
for (const entry of entries) {
|
|
74
|
+
if (exclude !== undefined && entry.model === exclude)
|
|
75
|
+
continue;
|
|
76
|
+
if (entry.contextWindow < threshold)
|
|
77
|
+
continue;
|
|
78
|
+
if (best === undefined
|
|
79
|
+
|| entry.contextWindow < best.contextWindow
|
|
80
|
+
|| (entry.contextWindow === best.contextWindow && entry.model < best.model)) {
|
|
81
|
+
best = entry;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return best;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=candidates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"candidates.js","sourceRoot":"","sources":["../../src/llm/candidates.ts"],"names":[],"mappings":"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"}
|