dsh-router-core 0.4.7 → 0.4.9
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/README.md +14 -70
- package/cordis.patch.yml +2 -7
- package/docs/ext.md +3 -17
- package/docs/suppliers.md +1 -1
- package/lib/client-registry.js +137 -590
- package/lib/client.js +137 -590
- package/lib/ext-prompt/index.js +20 -15
- package/lib/index.js +0 -419
- package/lib/types/ext/contract.d.ts +1 -1
- package/lib/types/ext-prompt/content.d.ts +11 -0
- package/lib/types/ext-prompt/index.d.ts +7 -9
- package/lib/types/ext-prompt/mount.d.ts +2 -2
- package/lib/types/ext-prompt/plugin.d.ts +3 -5
- package/lib/types/router/types.d.ts +11 -0
- package/lib/types/shared.d.ts +2 -0
- package/lib/types/suppliers/registry.d.ts +0 -1
- package/package.json +1 -10
- package/src/client/CombosTab.tsx +29 -8
- package/src/client/ExtDetail.tsx +1 -13
- package/src/client/SupplierDetail.tsx +11 -5
- package/src/client/group-controls.test.ts +1 -1
- package/src/client/group-controls.ts +1 -1
- package/src/client/index.tsx +0 -17
- package/src/client/model-label.test.ts +33 -0
- package/src/client/model-label.ts +48 -0
- package/src/client/router-components.test.ts +1 -1
- package/src/client/router.css +24 -412
- package/src/ext/contract.ts +1 -1
- package/src/ext-prompt/content.ts +17 -12
- package/src/ext-prompt/ext-prompt.test.ts +3 -3
- package/src/ext-prompt/index.ts +7 -9
- package/src/ext-prompt/mount.ts +2 -2
- package/src/ext-prompt/plugin.ts +3 -5
- package/src/router/index.ts +2 -3
- package/src/router/models-cache.test.ts +21 -6
- package/src/router/supplier-switch.test.ts +1 -1
- package/src/router/types.ts +11 -0
- package/src/shared.ts +2 -0
- package/src/suppliers/registry.ts +0 -71
- package/lib/contract-D4BWZ7Xe.js +0 -13
- package/lib/ext-test/index.js +0 -36
- package/lib/ext-test/locale/en.json +0 -6
- package/lib/ext-test/locale/zh.json +0 -6
- package/lib/ext-test/package.json +0 -5
- package/lib/types/ext-test/index.d.ts +0 -21
- package/lib/types/ext-test/plugin.d.ts +0 -23
- package/lib/types/suppliers/probe.d.ts +0 -160
- package/src/client/ExtTestPanel.tsx +0 -392
- package/src/client/comp-button-scope.test.ts +0 -58
- package/src/client/comp-panel-layout.test.ts +0 -58
- package/src/client/ext-panels.test.ts +0 -44
- package/src/client/ext-panels.ts +0 -49
- package/src/client/model-choice.test.ts +0 -41
- package/src/client/model-choice.ts +0 -37
- package/src/ext-test/ext-test.test.ts +0 -60
- package/src/ext-test/index.ts +0 -35
- package/src/ext-test/locale/en.json +0 -6
- package/src/ext-test/locale/zh.json +0 -6
- package/src/ext-test/plugin.ts +0 -34
- package/src/ext-test/row.json +0 -5
- package/src/suppliers/probe.test.ts +0 -266
- package/src/suppliers/probe.ts +0 -475
package/src/client/CombosTab.tsx
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
type RouterComboAlias,
|
|
11
11
|
type RouterComboSupplierGroup,
|
|
12
12
|
} from '../shared.ts'
|
|
13
|
+
import { modelLabel, modelMatches } from './model-label.ts'
|
|
13
14
|
import { Modal } from './Modal.tsx'
|
|
14
15
|
|
|
15
16
|
/* ---------------- SVG 图标 ---------------- */
|
|
@@ -67,6 +68,22 @@ export function CombosTab({ combos, aliases, onRefresh }: CombosTabProps): JSX.E
|
|
|
67
68
|
?? groups.find(g => g.models.some(m => m.id === stored))
|
|
68
69
|
return hit === undefined ? stored : `${hit.supplier.alias}/${stored}`
|
|
69
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* 存储值 → 悬停说明。组合里存的、请求要发的都是 `alias/modelId`(上面那行),
|
|
73
|
+
* 但上游 key 往往不是人能读的(`qmodel_38max`),所以把上游的可读名
|
|
74
|
+
* (`Qwen3.8-Max`)放进 title 里,不占行宽也不改变「发什么」。
|
|
75
|
+
*/
|
|
76
|
+
const comboTip = (stored: string): string => {
|
|
77
|
+
const name = displayName(stored)
|
|
78
|
+
const comma = stored.indexOf(',')
|
|
79
|
+
const mid = comma > 0 ? stored.slice(comma + 1) : stored
|
|
80
|
+
const sid = comma > 0 ? stored.slice(0, comma) : ''
|
|
81
|
+
const hit = (sid === '' ? groups : groups.filter(g => g.supplier.id === sid))
|
|
82
|
+
.flatMap(g => g.models)
|
|
83
|
+
.find(m => m.id === mid)
|
|
84
|
+
const readable = (hit?.name ?? '').trim()
|
|
85
|
+
return readable === '' || readable === mid ? name : `${readable}\n${name}(请求里用这个)`
|
|
86
|
+
}
|
|
70
87
|
const [showForm, setShowForm] = useState(false)
|
|
71
88
|
const [editing, setEditing] = useState<RouterCombo | null>(null)
|
|
72
89
|
const [form, setForm] = useState<ComboForm>({ name: '', models: [] })
|
|
@@ -289,7 +306,7 @@ export function CombosTab({ combos, aliases, onRefresh }: CombosTabProps): JSX.E
|
|
|
289
306
|
: (
|
|
290
307
|
<>
|
|
291
308
|
{combo.models.slice(0, 4).map((model, i) => (
|
|
292
|
-
<span key={`${model}-${i}`} className="dshr-comboChip">
|
|
309
|
+
<span key={`${model}-${i}`} className="dshr-comboChip" title={comboTip(model)}>
|
|
293
310
|
<span className="dshr-comboChipNum">{i + 1}</span>
|
|
294
311
|
<span className="dshr-comboChipSupplier">{displayName(model)}</span>
|
|
295
312
|
</span>
|
|
@@ -349,7 +366,7 @@ export function CombosTab({ combos, aliases, onRefresh }: CombosTabProps): JSX.E
|
|
|
349
366
|
>
|
|
350
367
|
<span className="dshr-comboGrip" title="拖拽排序"><Icon d={I.grip} size={14} /></span>
|
|
351
368
|
<span className="dshr-comboStepNum">{index + 1}</span>
|
|
352
|
-
<code className="dshr-comboSelectedModel">{displayName(model)}</code>
|
|
369
|
+
<code className="dshr-comboSelectedModel" title={comboTip(model)}>{displayName(model)}</code>
|
|
353
370
|
<button type="button" className="dshr-iconBtn dshr-iconBtn-sm dshr-comboStepRemove" title="移除" onClick={() => removeModel(index)}>
|
|
354
371
|
<Icon d={I.delete} size={14} />
|
|
355
372
|
</button>
|
|
@@ -379,16 +396,20 @@ export function CombosTab({ combos, aliases, onRefresh }: CombosTabProps): JSX.E
|
|
|
379
396
|
: groups.map(g => {
|
|
380
397
|
const items = g.models.filter(m =>
|
|
381
398
|
!form.models.includes(`${g.supplier.id},${m.id}`) &&
|
|
382
|
-
(modelQuery === '' ||
|
|
399
|
+
(modelQuery === '' || modelMatches(modelQuery, m)))
|
|
383
400
|
if (items.length === 0) return null
|
|
384
401
|
return (
|
|
385
402
|
<div key={g.supplier.id} className="dshr-comboPickGroup">
|
|
386
403
|
<p className="dshr-comboPickGroupTitle">{g.supplier.name} · {g.supplier.alias}</p>
|
|
387
|
-
{items.map(m =>
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
404
|
+
{items.map(m => {
|
|
405
|
+
const lab = modelLabel(m)
|
|
406
|
+
return (
|
|
407
|
+
<button key={m.id} type="button" className="dshr-comboPickItem" title={lab.title} onClick={() => toggleModel(g.supplier.id, m.id)}>
|
|
408
|
+
<span className="dshr-comboPickName">{lab.label}</span>
|
|
409
|
+
{lab.showId && <span className="dshr-comboPickKey">{m.id}</span>}
|
|
410
|
+
</button>
|
|
411
|
+
)
|
|
412
|
+
})}
|
|
392
413
|
</div>
|
|
393
414
|
)
|
|
394
415
|
})}
|
package/src/client/ExtDetail.tsx
CHANGED
|
@@ -13,13 +13,11 @@
|
|
|
13
13
|
*
|
|
14
14
|
* 列表页只列**已启用**的扩展(关闭的连卡片都不出现),所以这里也不必再显示
|
|
15
15
|
* 「已启用/未启用」——那行字只能是常量。布局同供应商详情:返回链接 + 图标 +
|
|
16
|
-
*
|
|
17
|
-
* 按需加独立面板,此处不内置任何具体扩展的专属视图。
|
|
16
|
+
* 名称/副行,下面跟扩展说明卡与未就绪横幅。
|
|
18
17
|
*/
|
|
19
18
|
import { Fragment, useEffect, useState } from 'react'
|
|
20
19
|
import { Switch } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
21
20
|
import { ROUTER_API_BASE, type ExtControlItem, type RouterExtItem, type RouterExtResponse } from '../shared.ts'
|
|
22
|
-
import { extPanel } from './ext-panels.ts'
|
|
23
21
|
import { groupControlRows } from './group-controls.ts'
|
|
24
22
|
import { Modal } from './Modal.tsx'
|
|
25
23
|
|
|
@@ -599,10 +597,6 @@ interface ExtDetailProps {
|
|
|
599
597
|
}
|
|
600
598
|
|
|
601
599
|
export function ExtDetail({ item, onBack }: ExtDetailProps): JSX.Element {
|
|
602
|
-
// 扩展**自带**详情面板时用它(插件自检就是这么做的:选供应商/模型/连接 + 跑测试)。
|
|
603
|
-
// 没有就退回下面这张通用只读页 —— 保留返回链接与标题,导航形状与有没有自定义
|
|
604
|
-
// 面板无关,用户点进去的预期是稳定的。
|
|
605
|
-
const Custom = extPanel(item.id)
|
|
606
600
|
return (
|
|
607
601
|
<div className="dshr-tabBody">
|
|
608
602
|
{/* Header(同供应商详情:返回链接 + 图标 + 名称/副行) */}
|
|
@@ -635,13 +629,9 @@ export function ExtDetail({ item, onBack }: ExtDetailProps): JSX.Element {
|
|
|
635
629
|
</div>
|
|
636
630
|
</div>
|
|
637
631
|
|
|
638
|
-
{/* 扩展自带的面板优先(注册表命中时下面几块都不渲染) */}
|
|
639
|
-
{Custom !== undefined ? <Custom /> : (
|
|
640
|
-
<>
|
|
641
632
|
{/* 子开关(扩展自己的行为细节;没有 controls 就不渲染这一块) */}
|
|
642
633
|
{item.controls !== undefined && item.controls.length > 0 && <ExtControls item={item} />}
|
|
643
634
|
|
|
644
|
-
{/* 实际生效的提示词(只读预览;回答「关掉的真的没进去吗」) */}
|
|
645
635
|
{/* 未就绪警示(同供应商详情的出错横幅) */}
|
|
646
636
|
{item.ready === false && (
|
|
647
637
|
<div className="dshr-alert">
|
|
@@ -649,8 +639,6 @@ export function ExtDetail({ item, onBack }: ExtDetailProps): JSX.Element {
|
|
|
649
639
|
<span>{item.detail !== undefined && item.detail !== '' ? item.detail : '当前不可用'}</span>
|
|
650
640
|
</div>
|
|
651
641
|
)}
|
|
652
|
-
</>
|
|
653
|
-
)}
|
|
654
642
|
</div>
|
|
655
643
|
)
|
|
656
644
|
}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
type RouterSupplierModelsResponse,
|
|
14
14
|
} from '../shared.ts'
|
|
15
15
|
import { statusBadge } from './account-badge.ts'
|
|
16
|
+
import { modelLabel, modelMatches } from './model-label.ts'
|
|
16
17
|
import { Modal } from './Modal.tsx'
|
|
17
18
|
|
|
18
19
|
/* ---------------- SVG 图标 ---------------- */
|
|
@@ -550,7 +551,7 @@ export function SupplierDetail({ supplier, accounts, statusLoading, onBack, onRe
|
|
|
550
551
|
return out
|
|
551
552
|
})()
|
|
552
553
|
const q = modelQuery.trim().toLowerCase()
|
|
553
|
-
const matchQuery = (m: RouterSupplierModel): boolean => q
|
|
554
|
+
const matchQuery = (m: RouterSupplierModel): boolean => modelMatches(q, m)
|
|
554
555
|
const enabledModels = models?.filter(m => m.enabled && matchQuery(m)) ?? []
|
|
555
556
|
const disabledModels = models?.filter(m => !m.enabled && matchQuery(m)) ?? []
|
|
556
557
|
|
|
@@ -763,19 +764,24 @@ export function SupplierDetail({ supplier, accounts, statusLoading, onBack, onRe
|
|
|
763
764
|
{enabledModels.map(model => {
|
|
764
765
|
const status = testResults[model.id]
|
|
765
766
|
const chipCls = status === 'ok' ? ' dshr-modelChip-ok' : status === 'error' ? ' dshr-modelChip-err' : ''
|
|
767
|
+
const lab = modelLabel(model)
|
|
766
768
|
return (
|
|
767
769
|
<div key={model.id} className={`dshr-modelChipWrap${chipCls}`}>
|
|
768
770
|
<button
|
|
769
771
|
type="button"
|
|
770
772
|
className="dshr-modelChip"
|
|
771
|
-
title=
|
|
773
|
+
title={`${lab.title}\n点击禁用`}
|
|
772
774
|
disabled={toggling}
|
|
773
775
|
onClick={() => void toggleModel(model.id, false)}
|
|
774
776
|
>
|
|
775
777
|
<span className="dshr-modelChipStatus">
|
|
776
778
|
<Icon d={status === 'ok' ? I.checkCircle : status === 'error' ? I.cancel : I.robot} size={13} />
|
|
777
779
|
</span>
|
|
778
|
-
<span className="dshr-
|
|
780
|
+
<span className="dshr-modelChipName">{lab.label}</span>
|
|
781
|
+
{lab.showId && <span className="dshr-modelChipKey">{model.id}</span>}
|
|
782
|
+
{typeof model.context_length === 'number' && model.context_length > 0 && (
|
|
783
|
+
<span className="dshr-modelChipCtx">{model.context_length}K</span>
|
|
784
|
+
)}
|
|
779
785
|
</button>
|
|
780
786
|
<div className="dshr-modelChipOps">
|
|
781
787
|
<button
|
|
@@ -819,11 +825,11 @@ export function SupplierDetail({ supplier, accounts, statusLoading, onBack, onRe
|
|
|
819
825
|
key={model.id}
|
|
820
826
|
type="button"
|
|
821
827
|
className="dshr-modelChip dshr-modelChip-off"
|
|
822
|
-
title=
|
|
828
|
+
title={`${modelLabel(model).title}\n点击恢复`}
|
|
823
829
|
disabled={toggling}
|
|
824
830
|
onClick={() => void toggleModel(model.id, true)}
|
|
825
831
|
>
|
|
826
|
-
<span className="dshr-
|
|
832
|
+
<span className="dshr-modelChipName">{modelLabel(model).label}</span>
|
|
827
833
|
<span className="dshr-modelChipRestore">+ 恢复</span>
|
|
828
834
|
</button>
|
|
829
835
|
))}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* 准则分类 + 宿主系统提示词段落),平铺成一长列无从分辨哪条是谁的。
|
|
6
6
|
*
|
|
7
7
|
* 两条规则:
|
|
8
|
-
* ① **无组节恒排最前** —— 没用 `group`
|
|
8
|
+
* ① **无组节恒排最前** —— 没用 `group` 字段的扩展拿到的
|
|
9
9
|
* 分组结果就是原来的平铺列表(一个无名节),面板行为一个像素不变;
|
|
10
10
|
* ② 组的先后 = 组名在 `rows` 里的**首次出现序**,组内保持原顺序 ——
|
|
11
11
|
* 不按字母重排:`rows` 的顺序来自服务端(段落按 prompt 序),重排
|
package/src/client/index.tsx
CHANGED
|
@@ -11,9 +11,6 @@
|
|
|
11
11
|
import { mountRouterWorkspace } from './workspace-mount.tsx'
|
|
12
12
|
import { RouterSettingsSection } from './settings-section.tsx'
|
|
13
13
|
import { RouterComponentsSection } from './RouterComponentsSection.tsx'
|
|
14
|
-
import { ExtTestPanel } from './ExtTestPanel.tsx'
|
|
15
|
-
import { registerExtPanel } from './ext-panels.ts'
|
|
16
|
-
import { EXT_TEST_ID } from '../ext-test/plugin.ts'
|
|
17
14
|
import { registerSettingsNavIcon } from './settings-nav-icon.ts'
|
|
18
15
|
import { rewriteRouterModelHint } from './model-hint-copy.ts'
|
|
19
16
|
import { LastHitDock } from './LastHitDock.tsx'
|
|
@@ -97,19 +94,6 @@ function mountComponentsSection(ctx: Ctx): (() => void) | undefined {
|
|
|
97
94
|
}, RouterComponentsSection))
|
|
98
95
|
}
|
|
99
96
|
|
|
100
|
-
/**
|
|
101
|
-
* 给「插件自检」扩展登记它的详情面板。
|
|
102
|
-
*
|
|
103
|
-
* 面板的**唯一入口**是设置 → 路由 → 扩展 → 点开那张卡片(`ExtDetail` 查注册表拿到
|
|
104
|
-
* 本组件)。**刻意不给插件页那一行再挂一个详情页**:那一行的宿主行开关已经能开/关
|
|
105
|
-
* 它了,再点进去看同一块面板就是同一个东西有两个入口 —— 与「内置扩展不在自绘节里
|
|
106
|
-
* 重复列」是同一条理由。
|
|
107
|
-
*/
|
|
108
|
-
function registerExtTestPanel(ctx: Ctx): void {
|
|
109
|
-
const dispose = registerExtPanel(EXT_TEST_ID, ExtTestPanel)
|
|
110
|
-
ctx.effect(() => dispose, 'dsh-router: ext test panel registry')
|
|
111
|
-
}
|
|
112
|
-
|
|
113
97
|
export function apply(ctx: Ctx): void {
|
|
114
98
|
// 「最近命中」徽章:走官方 conversation.composer.dock(scope: session → 组件
|
|
115
99
|
// props 注入 sessionId,按当前会话取命中)。0.2.0 起这个座位无条件可用 ——
|
|
@@ -128,7 +112,6 @@ export function apply(ctx: Ctx): void {
|
|
|
128
112
|
if (disposeComponents !== undefined) {
|
|
129
113
|
ctx.effect(() => disposeComponents, 'dsh-router: plugin page components')
|
|
130
114
|
}
|
|
131
|
-
registerExtTestPanel(ctx)
|
|
132
115
|
// 换掉宿主的默认齿轮(契约没有 icon 字段,只能注册后认领自己的行)
|
|
133
116
|
ctx.effect(() => registerSettingsNavIcon(SECTION_LABEL), 'dsh-router: settings nav icon')
|
|
134
117
|
// 设置-模型 里 Router 卡片的那句死路提示 → 正确说法(同样只能认领后就地改写)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模型标签/搜索判据测试。
|
|
3
|
+
*
|
|
4
|
+
* 为什么要有:这两条判据错了都不会报错,只会让人在面板上做错事 ——
|
|
5
|
+
* 标签退化成 key(用户还是看不懂),或搜索只认 key(记不清 key 的人搜不到)。
|
|
6
|
+
*/
|
|
7
|
+
import test from 'node:test'
|
|
8
|
+
import assert from 'node:assert/strict'
|
|
9
|
+
import { modelLabel, modelMatches } from './model-label.ts'
|
|
10
|
+
|
|
11
|
+
test('有可读名:名字在前,key 作为副标签', () => {
|
|
12
|
+
const l = modelLabel({ id: 'qmodel_38max', name: 'Qwen3.8-Max' })
|
|
13
|
+
assert.equal(l.label, 'Qwen3.8-Max')
|
|
14
|
+
assert.equal(l.showId, true)
|
|
15
|
+
assert.match(l.title, /qmodel_38max/)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('没有可读名 / 名字与 key 相同:只显示 key,不重复', () => {
|
|
19
|
+
assert.deepEqual(
|
|
20
|
+
{ ...modelLabel({ id: 'gpt-4o' }), title: undefined },
|
|
21
|
+
{ label: 'gpt-4o', showId: false, title: undefined },
|
|
22
|
+
)
|
|
23
|
+
assert.equal(modelLabel({ id: 'gpt-4o', name: 'gpt-4o' }).showId, false)
|
|
24
|
+
assert.equal(modelLabel({ id: 'gpt-4o', name: ' ' }).label, 'gpt-4o')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test('搜索:可读名与 key 都能命中', () => {
|
|
28
|
+
const m = { id: 'kmodel_latest', name: 'Kimi-K3.1-Pro' }
|
|
29
|
+
assert.equal(modelMatches('kimi', m), true)
|
|
30
|
+
assert.equal(modelMatches('KMODEL', m), true)
|
|
31
|
+
assert.equal(modelMatches(' ', m), true)
|
|
32
|
+
assert.equal(modelMatches('glm', m), false)
|
|
33
|
+
})
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模型 chip 的标签 —— 「可读名 + 上游 key」两段。
|
|
3
|
+
*
|
|
4
|
+
* 为什么需要:有些上游的模型 key 是内部标识(Qoder 的 `qmodel_38max`、
|
|
5
|
+
* `kmodel_latest`、`mmodel`),面板上只剩一串猜不出含义的字母;而上游目录里
|
|
6
|
+
* 其实同时下发了可读名(`Qwen3.8-Max`、`Kimi-K3.1-Pro`)。丢掉它,用户点
|
|
7
|
+
* 「获取模型」之后仍然不知道自己勾的是什么。
|
|
8
|
+
*
|
|
9
|
+
* 但两段不能互相替代:**能发给上游的只有 key**。所以显示上可读名在前、key 在后
|
|
10
|
+
* (两者相同时只显示一次),复制按钮复制 key,请求/组合存的也是 key。
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** 一个模型在面板上的呈现。 */
|
|
14
|
+
export interface ModelLabel {
|
|
15
|
+
/** 主标签:上游给的可读名,没有(或与 key 相同)就用 key。 */
|
|
16
|
+
label: string
|
|
17
|
+
/** 是否还要把 key 作为副标签显示 —— 与主标签相同就不重复。 */
|
|
18
|
+
showId: boolean
|
|
19
|
+
/** 悬停提示:两个都给出来,并点明请求要用哪个。 */
|
|
20
|
+
title: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 算出模型的展示标签。
|
|
25
|
+
* @param m 至少要有 `id`;`name` 是可读名(可缺、可空串)
|
|
26
|
+
*/
|
|
27
|
+
export function modelLabel(m: { id: string; name?: string }): ModelLabel {
|
|
28
|
+
const name = (m.name ?? '').trim()
|
|
29
|
+
const label = name === '' || name === m.id ? m.id : name
|
|
30
|
+
const showId = label !== m.id
|
|
31
|
+
return {
|
|
32
|
+
label,
|
|
33
|
+
showId,
|
|
34
|
+
title: showId ? `${label}\n${m.id}(请求与组合里用这个)` : m.id,
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 搜索匹配:可读名与 key **都**参与。
|
|
40
|
+
*
|
|
41
|
+
* 两种说法都要能搜到:记得模型叫什么的人搜 `kimi`,记得上游 key 的人搜
|
|
42
|
+
* `kmodel`;只匹配一个等于把另一半人的搜索框作废。
|
|
43
|
+
*/
|
|
44
|
+
export function modelMatches(query: string, m: { id: string; name?: string }): boolean {
|
|
45
|
+
const q = query.trim().toLowerCase()
|
|
46
|
+
if (q === '') return true
|
|
47
|
+
return m.id.toLowerCase().includes(q) || (m.name ?? '').toLowerCase().includes(q)
|
|
48
|
+
}
|
|
@@ -105,7 +105,7 @@ test('内置扩展不进这一节(它已经是原生「包含的组件」里
|
|
|
105
105
|
const out = groupRouterComponents({ ok: true }, {
|
|
106
106
|
ok: true,
|
|
107
107
|
enhancers: [
|
|
108
|
-
{ id: '
|
|
108
|
+
{ id: 'prompt', name: '分层提示词', source: 'builtin' },
|
|
109
109
|
{ id: 'rtk', name: 'RTK' },
|
|
110
110
|
],
|
|
111
111
|
})
|