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.
Files changed (116) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +281 -0
  3. package/README.md +283 -0
  4. package/assets/kenari-favicon-128.png +0 -0
  5. package/assets/kenari-favicon-256.png +0 -0
  6. package/client/index.js +1998 -0
  7. package/cordis.patch.yml +137 -0
  8. package/lib/billing.d.ts +120 -0
  9. package/lib/billing.d.ts.map +1 -0
  10. package/lib/billing.js +209 -0
  11. package/lib/billing.js.map +1 -0
  12. package/lib/catalog-view.d.ts +78 -0
  13. package/lib/catalog-view.d.ts.map +1 -0
  14. package/lib/catalog-view.js +113 -0
  15. package/lib/catalog-view.js.map +1 -0
  16. package/lib/catalog.d.ts +239 -0
  17. package/lib/catalog.d.ts.map +1 -0
  18. package/lib/catalog.js +445 -0
  19. package/lib/catalog.js.map +1 -0
  20. package/lib/default-route.d.ts +35 -0
  21. package/lib/default-route.d.ts.map +1 -0
  22. package/lib/default-route.js +111 -0
  23. package/lib/default-route.js.map +1 -0
  24. package/lib/errors.d.ts +29 -0
  25. package/lib/errors.d.ts.map +1 -0
  26. package/lib/errors.js +74 -0
  27. package/lib/errors.js.map +1 -0
  28. package/lib/favicon.d.ts +24 -0
  29. package/lib/favicon.d.ts.map +1 -0
  30. package/lib/favicon.js +68 -0
  31. package/lib/favicon.js.map +1 -0
  32. package/lib/http.d.ts +104 -0
  33. package/lib/http.d.ts.map +1 -0
  34. package/lib/http.js +247 -0
  35. package/lib/http.js.map +1 -0
  36. package/lib/index.d.ts +83 -0
  37. package/lib/index.d.ts.map +1 -0
  38. package/lib/index.js +285 -0
  39. package/lib/index.js.map +1 -0
  40. package/lib/llm/adapter.d.ts +78 -0
  41. package/lib/llm/adapter.d.ts.map +1 -0
  42. package/lib/llm/adapter.js +429 -0
  43. package/lib/llm/adapter.js.map +1 -0
  44. package/lib/llm/candidates.d.ts +57 -0
  45. package/lib/llm/candidates.d.ts.map +1 -0
  46. package/lib/llm/candidates.js +86 -0
  47. package/lib/llm/candidates.js.map +1 -0
  48. package/lib/llm/recovery.d.ts +48 -0
  49. package/lib/llm/recovery.d.ts.map +1 -0
  50. package/lib/llm/recovery.js +118 -0
  51. package/lib/llm/recovery.js.map +1 -0
  52. package/lib/llm/retry.d.ts +35 -0
  53. package/lib/llm/retry.d.ts.map +1 -0
  54. package/lib/llm/retry.js +53 -0
  55. package/lib/llm/retry.js.map +1 -0
  56. package/lib/plans.d.ts +59 -0
  57. package/lib/plans.d.ts.map +1 -0
  58. package/lib/plans.js +118 -0
  59. package/lib/plans.js.map +1 -0
  60. package/lib/session-title.d.ts +63 -0
  61. package/lib/session-title.d.ts.map +1 -0
  62. package/lib/session-title.js +280 -0
  63. package/lib/session-title.js.map +1 -0
  64. package/lib/settings.d.ts +35 -0
  65. package/lib/settings.d.ts.map +1 -0
  66. package/lib/settings.js +78 -0
  67. package/lib/settings.js.map +1 -0
  68. package/lib/tools/account.d.ts +11 -0
  69. package/lib/tools/account.d.ts.map +1 -0
  70. package/lib/tools/account.js +132 -0
  71. package/lib/tools/account.js.map +1 -0
  72. package/lib/tools/billing.d.ts +10 -0
  73. package/lib/tools/billing.d.ts.map +1 -0
  74. package/lib/tools/billing.js +100 -0
  75. package/lib/tools/billing.js.map +1 -0
  76. package/lib/tools/count-tokens.d.ts +11 -0
  77. package/lib/tools/count-tokens.d.ts.map +1 -0
  78. package/lib/tools/count-tokens.js +84 -0
  79. package/lib/tools/count-tokens.js.map +1 -0
  80. package/lib/tools/data.d.ts +12 -0
  81. package/lib/tools/data.d.ts.map +1 -0
  82. package/lib/tools/data.js +205 -0
  83. package/lib/tools/data.js.map +1 -0
  84. package/lib/tools/docs.d.ts +13 -0
  85. package/lib/tools/docs.d.ts.map +1 -0
  86. package/lib/tools/docs.js +209 -0
  87. package/lib/tools/docs.js.map +1 -0
  88. package/lib/tools/documents.d.ts +10 -0
  89. package/lib/tools/documents.d.ts.map +1 -0
  90. package/lib/tools/documents.js +118 -0
  91. package/lib/tools/documents.js.map +1 -0
  92. package/lib/tools/media.d.ts +28 -0
  93. package/lib/tools/media.d.ts.map +1 -0
  94. package/lib/tools/media.js +599 -0
  95. package/lib/tools/media.js.map +1 -0
  96. package/lib/tools/shared.d.ts +89 -0
  97. package/lib/tools/shared.d.ts.map +1 -0
  98. package/lib/tools/shared.js +128 -0
  99. package/lib/tools/shared.js.map +1 -0
  100. package/lib/tools/x-search.d.ts +11 -0
  101. package/lib/tools/x-search.d.ts.map +1 -0
  102. package/lib/tools/x-search.js +129 -0
  103. package/lib/tools/x-search.js.map +1 -0
  104. package/lib/web/fallback.d.ts +39 -0
  105. package/lib/web/fallback.d.ts.map +1 -0
  106. package/lib/web/fallback.js +79 -0
  107. package/lib/web/fallback.js.map +1 -0
  108. package/lib/web/fetch.d.ts +19 -0
  109. package/lib/web/fetch.d.ts.map +1 -0
  110. package/lib/web/fetch.js +41 -0
  111. package/lib/web/fetch.js.map +1 -0
  112. package/lib/web/search.d.ts +21 -0
  113. package/lib/web/search.d.ts.map +1 -0
  114. package/lib/web/search.js +47 -0
  115. package/lib/web/search.js.map +1 -0
  116. package/package.json +84 -0
package/lib/catalog.js ADDED
@@ -0,0 +1,445 @@
1
+ /**
2
+ * Kenari 模型目录:`GET /v1/models` 同步 + TTL 缓存 + dsh 模型元数据映射。
3
+ *
4
+ * Kenari 目录是**唯一**的模型事实来源(价格、上下文窗口、模态、推理档位、下线时间)。
5
+ * 所有消费方(工具、设置卡片、LlmAdapter)都从这里取,避免各自解析 rc 期字段。
6
+ * 端点与字段名变动只改本文件。
7
+ * @module dsh-kenari-plugin/catalog
8
+ */
9
+ import { kenariPublicGet, siteEndpoint } from './http.js';
10
+ /** 可当会话模型的端点(其余端点归为专用能力目录)。 */
11
+ const CHAT_ENDPOINT = 'chat';
12
+ /** 价格单位(Kenari 固定 micro-IDR per 1M tokens)。 */
13
+ const MICRO_PER_IDR = 1_000_000;
14
+ /** 目录解析失败时的可读原因(供工具与设置卡片展示)。 */
15
+ export class CatalogError extends Error {
16
+ constructor(message, options) {
17
+ super(message, options);
18
+ this.name = 'CatalogError';
19
+ }
20
+ }
21
+ /** 免费判定:`:free` 后缀或 `pricing.free`。 */
22
+ export function isFreeModel(model) {
23
+ return model.id.endsWith(':free') || model.pricing?.free === true;
24
+ }
25
+ /** 会话可用判定:endpoints 含 `chat`;目录未给 endpoints 时按 modality 兜底。 */
26
+ export function isChatCapable(model) {
27
+ const endpoints = model.endpoints;
28
+ if (endpoints !== undefined && endpoints.length > 0)
29
+ return endpoints.includes(CHAT_ENDPOINT);
30
+ return model.modality === undefined && (model.modalities?.output ?? []).includes('text');
31
+ }
32
+ /** dsh 只声明 text/image 两种输入模态;Kenari 的 audio/video/pdf 输入不映射(避免过度声明)。 */
33
+ export function inputModalitiesOf(model) {
34
+ const declared = model.modalities?.input ?? [];
35
+ const out = ['text'];
36
+ if (declared.includes('image'))
37
+ out.push('image');
38
+ return out;
39
+ }
40
+ /** 标签展示顺序(固定,避免每次渲染顺序漂移)。 */
41
+ export const CAPABILITY_TAGS = ['image', 'audio', 'video', 'pdf', 'embedding'];
42
+ /**
43
+ * 目录行 → 能力标签。
44
+ *
45
+ * 判定用的是**输入**能力与端点两处事实,因为 Kenari 对同一件事只在一处给字段:
46
+ * - 生成类(图像/视频/语音合成)只有 `endpoints`,`modalities.input` 仍是 text
47
+ * - 理解类(看图/听音频/读 PDF)只有 `modalities.input`
48
+ * - embedding 模型只在 `?modality=embedding` 目录里,带 `modality: 'embedding'`
49
+ * 所以两个来源都要看,且都只看事实、不猜(缺字段就不打标签)。
50
+ */
51
+ export function capabilityTagsOf(model) {
52
+ const input = model.modalities?.input ?? [];
53
+ const endpoints = model.endpoints ?? [];
54
+ const tags = [];
55
+ if (input.includes('image') || endpoints.includes('images'))
56
+ tags.push('image');
57
+ if (input.includes('audio') || endpoints.includes('audio_speech') || endpoints.includes('audio_transcription'))
58
+ tags.push('audio');
59
+ if (input.includes('video') || endpoints.includes('videos'))
60
+ tags.push('video');
61
+ if (input.includes('pdf'))
62
+ tags.push('pdf');
63
+ if (model.modality === 'embedding' || endpoints.includes('embeddings'))
64
+ tags.push('embedding');
65
+ return tags;
66
+ }
67
+ /** dsh `ModelThinkingLevel` 的键集合:reasoningEfforts 里写别的键会让设置写入被拒。 */
68
+ const THINKING_LEVELS = new Set(['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max']);
69
+ /**
70
+ * Kenari `reasoning_options` → pi-ai `reasoningEfforts`。
71
+ * Kenari 用 `none` 表示"支持但不发参数",dsh 的对应键是 `off`;其余同名直通。
72
+ * 两侧都不认识的取值直接丢弃——写进设置会让 schemastery 校验整节失败。
73
+ */
74
+ export function reasoningEffortsOf(model) {
75
+ if (model.reasoning !== true)
76
+ return undefined;
77
+ const efforts = {};
78
+ for (const option of model.reasoning_options ?? []) {
79
+ const level = option === 'none' ? 'off' : option;
80
+ if (THINKING_LEVELS.has(level))
81
+ efforts[level] = option;
82
+ }
83
+ return Object.keys(efforts).length > 0 ? efforts : undefined;
84
+ }
85
+ /**
86
+ * slug 片段 → 展示名里的写法。查表优先,其次识别版本号与参数量后缀,最后首字母大写。
87
+ * 表里只放**会读错**的词:缩写(`gpt`/`glm`/`tts`)与品牌大小写(`deepseek`/`mimo`/`minimax`)。
88
+ */
89
+ const NAME_TOKENS = {
90
+ gpt: 'GPT', glm: 'GLM', ai: 'AI', tts: 'TTS', asr: 'ASR', stt: 'STT', ocr: 'OCR', pdf: 'PDF',
91
+ hd: 'HD', xs: 'XS', xl: 'XL', oss: 'OSS', api: 'API', llm: 'LLM', mcp: 'MCP', vlm: 'VLM', it: 'IT',
92
+ deepseek: 'DeepSeek', mimo: 'MiMo', minimax: 'MiniMax', openai: 'OpenAI', kimi: 'Kimi', qwen: 'Qwen',
93
+ };
94
+ /** 一个 slug 片段的写法。 */
95
+ function spellNameToken(token) {
96
+ const known = NAME_TOKENS[token];
97
+ if (known !== undefined)
98
+ return known;
99
+ if (/^\d+(\.\d+)*$/.test(token))
100
+ return token;
101
+ if (/^v\d+(\.\d+)*$/.test(token))
102
+ return token;
103
+ if (/^[a-z]\d+[a-z]{0,3}$/.test(token))
104
+ return token.toUpperCase();
105
+ if (/^\d+[a-z]{1,3}$/.test(token))
106
+ return token.toUpperCase();
107
+ return token[0].toUpperCase() + token.slice(1);
108
+ }
109
+ /** `glm-5-3-flash` → `GLM 5.3 Flash`;版本号还原成点分,参数量后缀转大写。 */
110
+ function slugToDisplayName(id) {
111
+ const colon = id.indexOf(':');
112
+ const base = colon === -1 ? id : id.slice(0, colon);
113
+ const tokens = base.split(/[-_]/).filter((token) => token.length > 0);
114
+ const merged = [];
115
+ for (let index = 0; index < tokens.length; index += 1) {
116
+ const token = tokens[index];
117
+ if (/^\d+$/.test(token)) {
118
+ const run = [token];
119
+ while (index + 1 < tokens.length && /^\d+$/.test(tokens[index + 1])) {
120
+ index += 1;
121
+ run.push(tokens[index]);
122
+ }
123
+ merged.push(run.join('.'));
124
+ continue;
125
+ }
126
+ // 版本前缀:单个字母 + 数字(`v2`、`m2`)把紧跟的数字并进同一个版本号(`v2-5` → `v2.5`)。
127
+ const versioned = /^(v|[a-z])\d+$/.exec(token);
128
+ if (versioned !== null) {
129
+ const run = [token.slice(versioned[1].length)];
130
+ while (index + 1 < tokens.length && /^\d+$/.test(tokens[index + 1])) {
131
+ index += 1;
132
+ run.push(tokens[index]);
133
+ }
134
+ merged.push(versioned[1] + run.join('.'));
135
+ continue;
136
+ }
137
+ merged.push(token);
138
+ }
139
+ return merged.map(spellNameToken).join(' ');
140
+ }
141
+ /**
142
+ * 一个模型的展示名。
143
+ *
144
+ * 目录里只有 8 个模型自带 `name`,而且**都不是会话模型**(语音、图像、视频那几个),
145
+ * 所以会话模型的展示名必须能从 id 还原。这里的推导不是猜:Kenari 给出 `name` 的那 8 个
146
+ * 模型与本函数的输出**逐字一致**(Veo 3.1 Lite、MiniMax Speech 2.8 Turbo、
147
+ * Nano Banana Pro…),`test/catalog-view.mjs` 用它们反测。
148
+ *
149
+ * dsh 的「显示名称」是纯展示字段(不参与请求),所以还原值即使不完美也只是可改的默认值。
150
+ */
151
+ export function displayNameOf(model) {
152
+ const named = typeof model.name === 'string' ? model.name.trim() : '';
153
+ return named.length > 0 ? named : slugToDisplayName(model.id);
154
+ }
155
+ /**
156
+ * 每请求最大输出 token 的推荐值。
157
+ *
158
+ * 目录里**没有**这个字段:对全部 76 行实测过,只有 `context_length`,没有
159
+ * `max_output_tokens` / `max_completion_tokens` / `top_provider`。所以它是推出来的:
160
+ *
161
+ * - **窗口的 1/4**:一次回答最多占多少窗口的常见取法,给提示词留 3/4,请求才不会被
162
+ * 自己的上限顶出窗口。厂商公开的比例落在 6%(Gemini 2.5 的 65k / 1M)到
163
+ * 32%(Claude 的 64k / 200k)之间,取 1/4 偏宽——写代码时"被截断"比"留得多"更常见。
164
+ * - **不设下限**:下限看着无害,其实会算错小窗口的模型。目录里 `bge-m3` 的窗口只有 8192,
165
+ * 若按 8192 兜底就等于把整个窗口都推荐成输出。(全部会话模型的窗口都 ≥128K,所以
166
+ * 真正的会话模型从来用不到下限。)
167
+ * - **向下取整到 1K**:窗口不都是 2 的幂(有 128000、255976 这种),直接算会得到
168
+ * 32000 / 63994 这类数字。向下取整同时保证推荐值不超过窗口的 1/4。
169
+ * - **上限 65536**:主流厂商公开的单次输出上限最大就在这一档,再大不代表任何真实能力。
170
+ * 实测网关自己不做这个校验(`max_tokens` 给到 262144 仍返回 200),dsh 也不会把它从
171
+ * 输入预算里扣掉,所以这个上限是"别写没有意义的数",不是"写了会出错"。
172
+ *
173
+ * 目录不公布窗口的模型(18 个,其中只有 `qwen3-8-max` 是会话模型)不写这个字段:
174
+ * 没有窗口可依据时,任何数值都是编的,不如沿用 pi-ai 自己的默认值。
175
+ */
176
+ export function recommendedMaxTokens(contextWindow) {
177
+ if (typeof contextWindow !== 'number' || !Number.isFinite(contextWindow) || contextWindow <= 0)
178
+ return undefined;
179
+ const share = Math.floor(contextWindow / 4 / 1024) * 1024;
180
+ return Math.min(65536, Math.max(1024, share));
181
+ }
182
+ /**
183
+ * 目录行 → pi-ai provider profile 的 model 条目。
184
+ *
185
+ * 这是「把目录里的模型加进 `llm-pi-ai` 路由」时写进用户设置的形状,
186
+ * 字段必须逐个来自目录:pi-ai 的 schema 会拒绝多余或拼错的键。
187
+ *
188
+ * `name` 与 `maxTokens` 都写:前者是 dsh 模型目录里那个「显示名称」框(不写就空着),
189
+ * 后者是每请求输出上限(不写则整条路由共用 pi-ai 的 `defaultMaxTokens` 32768,
190
+ * 于是每个模型看到的都是同一个数)。两者的推导见上面两个函数。
191
+ */
192
+ export function toModelProfile(model) {
193
+ const efforts = reasoningEffortsOf(model);
194
+ const contextWindow = typeof model.context_length === 'number' && model.context_length > 0
195
+ ? model.context_length
196
+ : undefined;
197
+ const maxTokens = recommendedMaxTokens(contextWindow);
198
+ return {
199
+ id: model.id,
200
+ name: displayNameOf(model),
201
+ ...(contextWindow === undefined ? {} : { contextWindow }),
202
+ ...(maxTokens === undefined ? {} : { maxTokens }),
203
+ input: inputModalitiesOf(model),
204
+ ...(efforts === undefined ? {} : { reasoningEfforts: efforts }),
205
+ };
206
+ }
207
+ /** micro-IDR → IDR/1M tokens 的十进制字符串(整数不带小数点)。 */
208
+ export function microToIdrPerMillion(micro) {
209
+ const idr = micro / MICRO_PER_IDR;
210
+ return Number.isInteger(idr) ? idr.toLocaleString('id-ID') : idr.toFixed(6).replace(/0+$/, '').replace(/\.$/, '');
211
+ }
212
+ /** token 价格行:免费与浮动单列,缺失如实说"未公布"。 */
213
+ export function formatTokenPrice(model) {
214
+ const pricing = model.pricing;
215
+ if (pricing === undefined)
216
+ return '价格:目录未公布';
217
+ if (pricing.free === true)
218
+ return '价格:免费';
219
+ if (pricing.varies === true)
220
+ return '价格:浮动';
221
+ const parts = [];
222
+ if (typeof pricing.input === 'number')
223
+ parts.push(`入 ${microToIdrPerMillion(pricing.input)}`);
224
+ if (typeof pricing.output === 'number')
225
+ parts.push(`出 ${microToIdrPerMillion(pricing.output)}`);
226
+ if (typeof pricing.cache_read === 'number')
227
+ parts.push(`缓存读 ${microToIdrPerMillion(pricing.cache_read)}`);
228
+ if (typeof pricing.cache_write === 'number')
229
+ parts.push(`缓存写 ${microToIdrPerMillion(pricing.cache_write)}`);
230
+ if (parts.length === 0)
231
+ return '价格:目录未公布';
232
+ return `价格:${parts.join(' / ')} IDR per 1M tokens`;
233
+ }
234
+ /** 非 token 端点单价行;无常驻行时返回空数组。 */
235
+ export function formatPricingLines(model) {
236
+ const lines = model.pricing_lines ?? [];
237
+ return lines
238
+ .filter((line) => line.micro_idr !== undefined)
239
+ .map((line) => {
240
+ const idr = (line.micro_idr ?? 0) / MICRO_PER_IDR;
241
+ const unit = line.unit ?? 'request';
242
+ const variant = line.variant === null || line.variant === undefined ? '' : ` ${line.variant}`;
243
+ const per = unit === 'token_1m' ? 'per 1M tokens' : unit === '1k_chars' ? 'per 1k 字符' : unit === 'second' ? 'per 秒' : unit === 'image' ? 'per 张' : unit === 'song' ? 'per 首' : unit === 'megapixel' ? 'per 百万像素' : 'per 次';
244
+ return `${line.endpoint ?? '?'}${variant}:Rp ${idr.toLocaleString('id-ID')} ${per}`;
245
+ });
246
+ }
247
+ /** 端点 → 命中的计费行(variant 优先精确匹配,缺失取第一档)。 */
248
+ export function priceLineOf(model, endpoint, variant) {
249
+ const lines = (model.pricing_lines ?? []).filter((line) => line.endpoint === endpoint && line.micro_idr !== undefined);
250
+ if (lines.length === 0)
251
+ return undefined;
252
+ if (variant !== undefined) {
253
+ const exact = lines.find((line) => line.variant === variant);
254
+ if (exact !== undefined)
255
+ return exact;
256
+ }
257
+ return lines[0];
258
+ }
259
+ /** 端点 → 可读单价(micro-IDR);variant 优先精确匹配,缺失取第一档。 */
260
+ export function unitPriceMicro(model, endpoint, variant) {
261
+ return priceLineOf(model, endpoint, variant)?.micro_idr;
262
+ }
263
+ /** 计费单位的人读名称(回显口径用)。 */
264
+ export function unitLabel(unit) {
265
+ switch (unit) {
266
+ case 'token_1m': return '1M tokens';
267
+ case '1k_chars': return '1k 字符';
268
+ case 'second': return '秒';
269
+ case 'image': return '张';
270
+ case 'song': return '首';
271
+ case 'megapixel': return '百万像素';
272
+ case 'request': return '次';
273
+ default: return unit ?? '次';
274
+ }
275
+ }
276
+ /** 按计费行单价 × 数量(数量以该行 unit 为单位)预估费用(micro-IDR)。 */
277
+ export function estimateMicro(model, endpoint, quantity, variant) {
278
+ const unit = unitPriceMicro(model, endpoint, variant);
279
+ return unit === undefined ? undefined : Math.round(unit * quantity);
280
+ }
281
+ /** epoch 秒 → `YYYY-MM-DD`(本地时区无关,仅作展示)。 */
282
+ function formatEpochSeconds(epochSeconds) {
283
+ return new Date(epochSeconds * 1000).toISOString().slice(0, 10);
284
+ }
285
+ /** 模型体检:beta 标注与下线告警(到期前仍服务,故只提示不拒绝)。 */
286
+ export function warningsOf(model, now = Date.now()) {
287
+ const warnings = [];
288
+ if (model.beta === true)
289
+ warnings.push('beta:实验性模型,接口与价格可能变动');
290
+ const sunsetAt = model.sunset_at;
291
+ if (typeof sunsetAt === 'number' && sunsetAt > 0) {
292
+ const days = Math.ceil((sunsetAt * 1000 - now) / 86_400_000);
293
+ const when = formatEpochSeconds(sunsetAt);
294
+ if (days <= 0)
295
+ warnings.push(`已到下线时间 ${when}:随时可能停止服务`);
296
+ else
297
+ warnings.push(`将于 ${when} 下线(还剩 ${days} 天):到期前仍正常服务,请尽早迁移`);
298
+ }
299
+ return warnings;
300
+ }
301
+ /** 模型别名:`kenari/<id>` 限定形式 + 配置别名 + 去除限定后的裸 id。 */
302
+ export function aliasesOf(model, configured) {
303
+ const aliases = new Set([model.id, `kenari/${model.id}`]);
304
+ for (const [alias, target] of Object.entries(configured)) {
305
+ if (target === model.id)
306
+ aliases.add(alias);
307
+ }
308
+ return [...aliases];
309
+ }
310
+ /** 别名归一:去掉 `kenari/` 或 `kenari:` 限定前缀,再查配置别名表。 */
311
+ export function normalizeModelId(raw, configured) {
312
+ const trimmed = raw.trim();
313
+ const stripped = trimmed.startsWith('kenari/') || trimmed.startsWith('kenari:') ? trimmed.slice(7) : trimmed;
314
+ return configured[stripped] ?? configured[trimmed] ?? stripped;
315
+ }
316
+ /**
317
+ * 目录客户端:进程内 TTL 缓存(目录是公开端点,无需 key)。
318
+ * 并发请求合并到同一次抓取,避免工具族同时冷启动打爆目录端点。
319
+ */
320
+ export class KenariCatalog {
321
+ deps;
322
+ cache = new Map();
323
+ inflight = new Map();
324
+ constructor(deps) {
325
+ this.deps = deps;
326
+ }
327
+ /** 缓存有效期取自实时配置:设置页改完下一次读取即生效。 */
328
+ get ttlMs() {
329
+ return this.deps.config.catalogCacheTtlMs ?? 3_600_000;
330
+ }
331
+ /** 模型别名表取自实时配置。 */
332
+ get aliases() {
333
+ return this.deps.config.modelAliases ?? {};
334
+ }
335
+ /** 目录抓取(公开端点):TTL 内直接命中缓存。 */
336
+ async list(modality, signal) {
337
+ const key = modality ?? 'chat';
338
+ const cached = this.cache.get(key);
339
+ const ttlMs = this.ttlMs;
340
+ if (ttlMs > 0 && cached !== undefined && Date.now() - cached.fetchedAt < ttlMs) {
341
+ return cached.models;
342
+ }
343
+ const pending = this.inflight.get(key);
344
+ if (pending !== undefined)
345
+ return pending;
346
+ const query = modality === undefined ? undefined : { modality };
347
+ // 目录无需 key(实测:带 key 与不带 key 的响应逐字节相同),所以走公开 GET:
348
+ // 没配 key 的部署也能列出模型——设置页正是用户还没有 key 时最需要它的地方。
349
+ const qs = query === undefined ? '' : `?${new URLSearchParams(query).toString()}`;
350
+ const request = kenariPublicGet(this.deps, `${siteEndpoint(this.deps.config.baseURL, '/v1/models')}${qs}`, signal, { retries: 1 })
351
+ .then((payload) => {
352
+ const models = (payload.data ?? []).filter((model) => typeof model?.id === 'string');
353
+ this.cache.set(key, { models, fetchedAt: Date.now() });
354
+ return models;
355
+ })
356
+ .finally(() => {
357
+ this.inflight.delete(key);
358
+ });
359
+ this.inflight.set(key, request);
360
+ return request;
361
+ }
362
+ /** 会话模型目录(endpoints 含 chat),缓存与 list 共用。 */
363
+ async chatModels(signal) {
364
+ return (await this.list(undefined, signal)).filter(isChatCapable);
365
+ }
366
+ /**
367
+ * 目录解析:别名归一 → 精确匹配 → 大小写不敏感兜底。
368
+ *
369
+ * 默认(无 modality)目录只含 chat 与生成类模型;embedding / rerank /
370
+ * moderation 只在 `?modality=` 目录里。给了 modality 就只多查那一本,
371
+ * 没给则三本都查(各自的响应都走 TTL 缓存,故代价一次性)。
372
+ */
373
+ async resolve(rawId, modality, signal) {
374
+ const target = normalizeModelId(rawId, this.aliases);
375
+ const search = (models) => models.find((row) => row.id === target) ?? models.find((row) => row.id.toLowerCase() === target.toLowerCase());
376
+ const direct = search(await this.list(undefined, signal));
377
+ if (direct !== undefined)
378
+ return this.describe(direct);
379
+ const modalities = modality === undefined ? ['embedding', 'rerank', 'moderation'] : [modality];
380
+ for (const candidate of modalities) {
381
+ const found = search(await this.list(candidate, signal));
382
+ if (found !== undefined)
383
+ return this.describe(found);
384
+ }
385
+ return undefined;
386
+ }
387
+ /** 目录行 → 解析后条目。 */
388
+ describe(model) {
389
+ return {
390
+ model,
391
+ id: model.id,
392
+ free: isFreeModel(model),
393
+ chatCapable: isChatCapable(model),
394
+ aliases: aliasesOf(model, this.aliases),
395
+ warnings: warningsOf(model),
396
+ };
397
+ }
398
+ /** 免费会话模型(`新账户默认 step-3-7-flash:free` 的选型依据)。 */
399
+ async freeModels(signal) {
400
+ return (await this.chatModels(signal)).filter(isFreeModel);
401
+ }
402
+ /** 清空缓存(测试与强制刷新用)。 */
403
+ invalidate() {
404
+ this.cache.clear();
405
+ }
406
+ }
407
+ /** 目录行 → dsh `LlmModelInfo` 形状(adapter 的 listModels 与设置卡片共用)。 */
408
+ export function toLlmModelInfo(model, provider) {
409
+ const annotations = [];
410
+ if (isFreeModel(model))
411
+ annotations.push('免费');
412
+ if (model.beta === true)
413
+ annotations.push('beta');
414
+ const description = annotations.length > 0 ? annotations.join(' · ') : undefined;
415
+ return {
416
+ provider,
417
+ id: model.id,
418
+ name: model.name ?? model.id,
419
+ ...(description !== undefined ? { description } : {}),
420
+ inputModalities: inputModalitiesOf(model),
421
+ };
422
+ }
423
+ /**
424
+ * 目录行 → dsh `LlmResolvedModelInfo` 形状:上下文窗口 + reasoning 档位 + 默认输出上限。
425
+ * `reasoning_options` 的每个值既是 dsh 的 effort id 也是 Kenari 线上的写法,故一一对应。
426
+ */
427
+ export function toResolvedModelInfo(model, provider) {
428
+ const base = toLlmModelInfo(model, provider);
429
+ const context = typeof model.context_length === 'number' && model.context_length > 0
430
+ ? { contextWindow: model.context_length }
431
+ : undefined;
432
+ const options = model.reasoning === true ? (model.reasoning_options ?? []) : [];
433
+ const reasoning = options.length > 0
434
+ ? {
435
+ efforts: options.map((effort) => ({ id: effort, name: effort })),
436
+ ...(options.includes('medium') ? { defaultEffort: 'medium' } : {}),
437
+ }
438
+ : undefined;
439
+ return {
440
+ ...base,
441
+ ...(context !== undefined ? { context } : {}),
442
+ ...(reasoning !== undefined ? { reasoning } : {}),
443
+ };
444
+ }
445
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * 默认路由:按当前 key 的套餐把「免缓存额度」模型填进 `kenari` 路由。
3
+ *
4
+ * 背景:`cordis.patch.yml` 是**静态 YAML**,没法在运行时按 key 的套餐算出默认值,
5
+ * 所以预设只放一份兜底清单(`deepseek-v4-flash` / `glm-5-3-flash` / `gpt-5-6-luna`
6
+ * / `mimo-v2-5`,正好是 Kreator 与 Studio 两档的免费缓存模型)。真正的默认值在这里算:
7
+ *
8
+ * 1. 用户层已经自己写过 `providers.kenari.models` → 一律不动(用户的东西最大)
9
+ * 2. 取不到 key、`/v1/account/quota` 失败或 `plan` 为 null → 保持预设(不写设置)
10
+ * 3. 拿到套餐名 → 从套餐表取该套餐的 `free_cache_models` → 逐个查目录补全 profile
11
+ * 4. 算出来的集合与预设**一致**时不写设置——否则每次启动都把同样的清单固化进
12
+ * `settings.yaml`,纯属污染;只有真的不一样(如 Indie 只有 2 个)才写一次
13
+ *
14
+ * 写进去之后就变成用户层覆盖,下次启动第 1 步直接返回,所以这是**一次性的物化**,
15
+ * 不会反复改写;用户在 Models 页改过之后也永远不会被这里覆盖。
16
+ * @module dsh-kenari-plugin/default-route
17
+ */
18
+ import type { Context } from '@deepseek-ai/cordis';
19
+ import type { KenariCatalog } from './catalog.js';
20
+ import type { KenariPlans } from './plans.js';
21
+ import type { KenariHttpDeps } from './http.js';
22
+ /** 读取依赖。 */
23
+ export interface DefaultRouteDeps {
24
+ readonly http: KenariHttpDeps;
25
+ readonly catalog: KenariCatalog;
26
+ readonly plans: KenariPlans;
27
+ }
28
+ /**
29
+ * 按套餐算一次默认路由;只在需要且能算出来时写设置。
30
+ * 任何失败都只记日志:默认值算不出来不该影响插件其余能力。
31
+ * @param ctx - 已注入 `settings` 的上下文。
32
+ * @param deps - 目录、套餐表与 HTTP 依赖。
33
+ */
34
+ export declare function applyPlanDefaultRoute(ctx: Context, deps: DefaultRouteDeps): Promise<void>;
35
+ //# sourceMappingURL=default-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default-route.d.ts","sourceRoot":"","sources":["../src/default-route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAKlD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAW/C,YAAY;AACZ,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;IAC7B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAA;IAC/B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAA;CAC5B;AA4BD;;;;;GAKG;AACH,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+D/F"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * 默认路由:按当前 key 的套餐把「免缓存额度」模型填进 `kenari` 路由。
3
+ *
4
+ * 背景:`cordis.patch.yml` 是**静态 YAML**,没法在运行时按 key 的套餐算出默认值,
5
+ * 所以预设只放一份兜底清单(`deepseek-v4-flash` / `glm-5-3-flash` / `gpt-5-6-luna`
6
+ * / `mimo-v2-5`,正好是 Kreator 与 Studio 两档的免费缓存模型)。真正的默认值在这里算:
7
+ *
8
+ * 1. 用户层已经自己写过 `providers.kenari.models` → 一律不动(用户的东西最大)
9
+ * 2. 取不到 key、`/v1/account/quota` 失败或 `plan` 为 null → 保持预设(不写设置)
10
+ * 3. 拿到套餐名 → 从套餐表取该套餐的 `free_cache_models` → 逐个查目录补全 profile
11
+ * 4. 算出来的集合与预设**一致**时不写设置——否则每次启动都把同样的清单固化进
12
+ * `settings.yaml`,纯属污染;只有真的不一样(如 Indie 只有 2 个)才写一次
13
+ *
14
+ * 写进去之后就变成用户层覆盖,下次启动第 1 步直接返回,所以这是**一次性的物化**,
15
+ * 不会反复改写;用户在 Models 页改过之后也永远不会被这里覆盖。
16
+ * @module dsh-kenari-plugin/default-route
17
+ */
18
+ import { isChatCapable, toModelProfile } from './catalog.js';
19
+ import { kenariGet } from './http.js';
20
+ /** pi-ai 的 profile 数组路径(相对 `llm-pi-ai` 节根)。 */
21
+ const ROUTE = { ns: 'llm-pi-ai', provider: 'kenari' };
22
+ const MODELS_PATH = ['providers', ROUTE.provider, 'models'];
23
+ /** 嵌套取值:路径上任何一段不是对象就返回 undefined。 */
24
+ function pathGet(root, path) {
25
+ let node = root;
26
+ for (const segment of path) {
27
+ if (typeof node !== 'object' || node === null)
28
+ return undefined;
29
+ node = node[segment];
30
+ }
31
+ return node;
32
+ }
33
+ /** 一条 `models` 数组里的 id 列表(形状不对就当空)。 */
34
+ function idsOf(value) {
35
+ if (!Array.isArray(value))
36
+ return [];
37
+ return value
38
+ .map((entry) => (typeof entry === 'object' && entry !== null ? entry.id : undefined))
39
+ .filter((id) => typeof id === 'string');
40
+ }
41
+ /** 两个 id 列表是否同集合(顺序无关,重复无关)。 */
42
+ function sameIds(a, b) {
43
+ if (a.length !== b.length)
44
+ return false;
45
+ const left = [...a].sort();
46
+ const right = [...b].sort();
47
+ return left.every((id, index) => id === right[index]);
48
+ }
49
+ /**
50
+ * 按套餐算一次默认路由;只在需要且能算出来时写设置。
51
+ * 任何失败都只记日志:默认值算不出来不该影响插件其余能力。
52
+ * @param ctx - 已注入 `settings` 的上下文。
53
+ * @param deps - 目录、套餐表与 HTTP 依赖。
54
+ */
55
+ export async function applyPlanDefaultRoute(ctx, deps) {
56
+ const log = deps.http.logger;
57
+ try {
58
+ const descriptors = ctx.settings.describe({ redactSecrets: true });
59
+ const entry = descriptors.find((descriptor) => descriptor.ns === ROUTE.ns);
60
+ if (entry === undefined)
61
+ return;
62
+ // 用户自己写过这个数组 → 永远不碰
63
+ if (Array.isArray(pathGet(entry.user, MODELS_PATH))) {
64
+ log?.info('kenari: 路由模型列表由用户层持有,跳过默认值计算');
65
+ return;
66
+ }
67
+ const presetIds = idsOf(pathGet(entry.base, MODELS_PATH));
68
+ const apiKey = await deps.http.resolveApiKey();
69
+ if (apiKey === undefined || apiKey.length === 0) {
70
+ log?.info('kenari: 未配置 key,路由保持预置的默认模型');
71
+ return;
72
+ }
73
+ const quota = await kenariGet(deps.http, '/account/quota').catch(() => undefined);
74
+ const planName = quota?.plan?.name;
75
+ if (typeof planName !== 'string' || planName.length === 0) {
76
+ log?.info('kenari: 该 key 读不到套餐(无套餐或分享页 key),路由保持预置的默认模型');
77
+ return;
78
+ }
79
+ const plan = (await deps.plans.list()).find((candidate) => candidate.name === planName);
80
+ const freeCacheIds = plan?.freeCacheModels ?? [];
81
+ if (freeCacheIds.length === 0) {
82
+ log?.info(`kenari: 套餐 ${planName} 没有免缓存模型清单,路由保持预置的默认模型`);
83
+ return;
84
+ }
85
+ // 目录补全:只收可当会话模型的(免缓存清单里可能混入 embedding 之类),
86
+ // 目录里查不到的 id 直接丢——写进路由只会在请求时才发现不可用。
87
+ const profiles = [];
88
+ for (const id of freeCacheIds) {
89
+ const resolved = await deps.catalog.resolve(id);
90
+ if (resolved === undefined || !isChatCapable(resolved.model))
91
+ continue;
92
+ profiles.push(toModelProfile(resolved.model));
93
+ }
94
+ if (profiles.length === 0) {
95
+ log?.warn(`kenari: 套餐 ${planName} 的免缓存模型在目录里一个都对不上,路由保持预置的默认模型`);
96
+ return;
97
+ }
98
+ const nextIds = profiles.map((profile) => profile.id);
99
+ if (sameIds(nextIds, presetIds)) {
100
+ log?.info(`kenari: 套餐 ${planName} 的免缓存模型与预置默认一致(${nextIds.length} 个),无需写入设置`);
101
+ return;
102
+ }
103
+ await ctx.settings.mutate(ROUTE.ns, [{ op: 'set', path: [...MODELS_PATH], value: profiles }], entry.revision);
104
+ log?.info(`kenari: 已按套餐 ${planName} 的免缓存模型写入默认路由(${nextIds.length} 个)`);
105
+ }
106
+ catch (err) {
107
+ // 默认值写不进去只是没默认值,不该影响插件加载
108
+ deps.http.logger?.warn(`kenari: 默认路由计算失败,保持预置默认:${err instanceof Error ? err.message : String(err)}`);
109
+ }
110
+ }
111
+ //# sourceMappingURL=default-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default-route.js","sourceRoot":"","sources":["../src/default-route.ts"],"names":[],"mappings":"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"}