dsh-model-params 0.1.6 → 0.1.7
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 +5 -1
- package/lib/client.js +187 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
For every configured `llm-pi-ai` provider card (custom OpenAI-compatible gateways such as the ones you add under Settings → Models), a `models.dev 参数` control fetches the official models.dev records for the provider's configured model ids and proposes the metadata pi-ai needs: context window, max output tokens, and reasoning-effort levels. One click writes the merged `models` array back to that provider profile through the official revision-aware Settings API.
|
|
8
8
|
|
|
9
|
+
On hosts that declare the root-scope `settings.section` list seat, that same card is also registered as a first-class settings page — identity `YOTK · Model Params`, id `yotk-model-params`, order `63` — additive beside the Models-page extension and never an activation gate. The page needs nothing selected elsewhere: it reads the `llm-pi-ai` providers you have configured through the same settings scope, addresses the first one (with a picker whenever several exist), and states plainly when the namespace is loading, unserved, or has no provider yet.
|
|
10
|
+
|
|
9
11
|
Safety boundaries:
|
|
10
12
|
|
|
11
13
|
- The control never auto-writes. You open the panel, review the per-model diff, then click **应用并写入该 provider** — it writes only that provider's `models` array in the `llm-pi-ai` namespace.
|
|
@@ -20,6 +22,8 @@ models.dev model ids may collide across catalog providers; the matcher prefers t
|
|
|
20
22
|
|
|
21
23
|
面向 DeepSeek Harness Web 官方 **设置 → 模型** 页的 models.dev 参数助手。每个已配置的 `llm-pi-ai` provider 卡片内提供 `models.dev 参数` 入口:按该 provider 的模型 id 在 models.dev 官方目录中匹配,预览上下文窗口 / max 输出 / 推理档位,确认后一次性写回该 provider 的 `models` 配置(官方 revision-aware Settings API,仅写 `llm-pi-ai` 命名空间下当前 provider 的数组)。
|
|
22
24
|
|
|
25
|
+
在声明根级 `settings.section` 列表座的宿主上,同一张卡片还会注册为设置页的一级入口 —— 标识 `YOTK · Model Params`、id `yotk-model-params`、order `63` —— 与「设置 → 模型」页的扩展并存,且从不成为激活门槛。该页不依赖别处选中的 provider:它通过同一设置作用域读取已配置的 `llm-pi-ai` provider,默认处理第一个(配置多个时可在页内切换),并在命名空间仍在读取、宿主未提供或尚未配置 provider 时明确说明。
|
|
26
|
+
|
|
23
27
|
- 默认仅补缺失参数;勾选「覆盖已有值」才替换差异值。
|
|
24
28
|
- 窗口顶部可启用一个所有 provider 共用的 models.dev 请求代理并编辑地址;它只影响下一次参数查询,不改变任何 LLM provider 的 `baseURL`。
|
|
25
29
|
- Host 只负责拉取并缓存 models.dev 目录(6 小时,支持公共代理或 `HTTPS_PROXY` 回退),不接触任何 LLM 配置与凭据。
|
|
@@ -31,7 +35,7 @@ models.dev model ids may collide across catalog providers; the matcher prefers t
|
|
|
31
35
|
dsh plugin --profile web add dsh-model-params
|
|
32
36
|
```
|
|
33
37
|
|
|
34
|
-
Restart the existing DSH Web process afterwards: the Host scans the browser plugin roster at startup, so the provider-card control appears only after that restart. Then open **Settings → Models** and look for `models.dev 参数` on a configured provider card.
|
|
38
|
+
Restart the existing DSH Web process afterwards: the Host scans the browser plugin roster at startup, so the provider-card control appears only after that restart. Then open **Settings → Models** and look for `models.dev 参数` on a configured provider card. Where the host also exposes the settings page seat, the same control is one click deep in **Settings** as **YOTK · Model Params**.
|
|
35
39
|
|
|
36
40
|
Local development, from this package directory:
|
|
37
41
|
|
package/lib/client.js
CHANGED
|
@@ -3,12 +3,18 @@
|
|
|
3
3
|
// Renders a compact "models.dev 参数" affordance inside every official
|
|
4
4
|
// llm-pi-ai provider card on the Models settings page
|
|
5
5
|
// (settings.models.provider-card, keyed by the pi-ai settings namespace).
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
6
|
+
// The same card is ALSO registered on the root-scope `settings.section` list
|
|
7
|
+
// seat (id `yotk-model-params`), which makes it a first-class settings page of
|
|
8
|
+
// its own on hosts that declare that seat. That seat's owner share is
|
|
9
|
+
// `{ close }` alone, so the page is handed no provider: it resolves one of the
|
|
10
|
+
// routes the llm-pi-ai namespace itself declares, from the same scope and
|
|
11
|
+
// never through a second read path.
|
|
12
|
+
// Either way it reads the current provider profile from the official llm-pi-ai
|
|
13
|
+
// Settings scope, asks the Host for the matching models.dev records, previews the
|
|
14
|
+
// proposed context / max-output / reasoning-effort values per model, and — only
|
|
15
|
+
// on an explicit "应用" click — writes the merged models array back through the
|
|
16
|
+
// revision-aware official Settings API. Default policy fills missing parameters
|
|
17
|
+
// only; "覆盖已有值" also replaces differing ones.
|
|
12
18
|
// The plugin never auto-writes, never touches other providers/namespaces,
|
|
13
19
|
// and never sends credentials anywhere.
|
|
14
20
|
|
|
@@ -64,6 +70,8 @@ window.__ModuleLoader__.load({
|
|
|
64
70
|
'.dpmProxy{display:flex;flex-direction:column;gap:6px;padding:7px 8px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-2)}',
|
|
65
71
|
'.dpmProxy label{display:flex;align-items:center;gap:6px;color:var(--dsw-alias-label-secondary);cursor:pointer}',
|
|
66
72
|
'.dpmProxy input[type=text]{width:100%;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:4px 6px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:11px}',
|
|
73
|
+
'.dpmRoute{display:flex;align-items:center;gap:6px;color:var(--dsw-alias-label-secondary)}',
|
|
74
|
+
'.dpmRoute select{font:inherit;font-size:11px;max-width:100%;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:3px 6px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary)}',
|
|
67
75
|
'.dpmHint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:11px;line-height:1.5}',
|
|
68
76
|
'.dpmErr{color:var(--dsw-alias-state-error-primary);margin:0;font-size:11px;word-break:break-all}',
|
|
69
77
|
'.dpmList{display:flex;flex-direction:column;gap:6px}',
|
|
@@ -95,6 +103,48 @@ window.__ModuleLoader__.load({
|
|
|
95
103
|
const clean = (value) => typeof value === 'string' && value.trim() ? value.trim() : undefined
|
|
96
104
|
const intersectLevels = (values) => (values || []).filter((value) => LEVELS.includes(value))
|
|
97
105
|
|
|
106
|
+
// ── the section page's own provider (the `settings.section` seat) ─────────
|
|
107
|
+
//
|
|
108
|
+
// The root-scope section seat's owner share is `{ close }` ALONE
|
|
109
|
+
// (SettingsSectionOwnerProps), so that page is handed NO provider while its
|
|
110
|
+
// Models-page sibling receives one per card. It therefore resolves its
|
|
111
|
+
// provider from the ONE settings transport this package already reads: the
|
|
112
|
+
// `llm-pi-ai` section itself, whose `providers` dict IS keyed by provider
|
|
113
|
+
// route (PiAiProviderProfile). Every route this page addresses is one the
|
|
114
|
+
// namespace really declares — never a synthesized id, never a second read or
|
|
115
|
+
// write path — and the profile supplies the real display name when it has one.
|
|
116
|
+
function piAiValue(snapshot) {
|
|
117
|
+
return snapshot && snapshot.status === 'ready' && snapshot.value && typeof snapshot.value === 'object' ? snapshot.value : undefined
|
|
118
|
+
}
|
|
119
|
+
function piAiRoutes(snapshot) {
|
|
120
|
+
const value = piAiValue(snapshot)
|
|
121
|
+
const providers = value && value.providers && typeof value.providers === 'object' && !Array.isArray(value.providers) ? value.providers : undefined
|
|
122
|
+
return providers ? Object.keys(providers).filter((route) => typeof route === 'string' && route.length > 0) : []
|
|
123
|
+
}
|
|
124
|
+
function piAiProfile(snapshot, route) {
|
|
125
|
+
const value = piAiValue(snapshot)
|
|
126
|
+
const profile = value && value.providers && typeof value.providers === 'object' ? value.providers[route] : undefined
|
|
127
|
+
return profile && typeof profile === 'object' ? profile : undefined
|
|
128
|
+
}
|
|
129
|
+
function piAiModels(snapshot, route) {
|
|
130
|
+
const profile = piAiProfile(snapshot, route)
|
|
131
|
+
return profile && Array.isArray(profile.models) ? profile.models : []
|
|
132
|
+
}
|
|
133
|
+
function piAiDisplayName(snapshot, route) {
|
|
134
|
+
const profile = piAiProfile(snapshot, route)
|
|
135
|
+
return (profile && clean(profile.displayName)) || route
|
|
136
|
+
}
|
|
137
|
+
// What the page says when it has no card to draw. A namespace still loading, a
|
|
138
|
+
// namespace this host does not serve, and a namespace with no provider
|
|
139
|
+
// configured are three different facts, and none may look like a blank page.
|
|
140
|
+
function sectionPageHint(snapshot, scope) {
|
|
141
|
+
if (!scope) return '设置传输不可用:本机未挂载 settingsScope / configForms,无法读写 llm-pi-ai 配置。'
|
|
142
|
+
const status = snapshot && snapshot.status
|
|
143
|
+
if (status === 'loading') return '正在读取 llm-pi-ai 设置…'
|
|
144
|
+
if (status !== 'ready') return '当前 Host 未提供 llm-pi-ai 设置,无法读写模型参数。'
|
|
145
|
+
return 'llm-pi-ai 命名空间下还没有已配置的 provider;请先在 设置 → 模型 中添加一个自定义 provider。'
|
|
146
|
+
}
|
|
147
|
+
|
|
98
148
|
function paramLine(label, value) {
|
|
99
149
|
if (value === undefined || value === null || value === '') return null
|
|
100
150
|
return e('span', { key: label }, label + ': ' + value)
|
|
@@ -103,11 +153,23 @@ window.__ModuleLoader__.load({
|
|
|
103
153
|
function ModelParamsExtension(props) {
|
|
104
154
|
const row = props.provider
|
|
105
155
|
const entry = row && row.entry && typeof row.entry === 'object' ? row.entry : row
|
|
106
|
-
|
|
107
|
-
|
|
156
|
+
// The provider the Models page hands this card through the seat's own owner
|
|
157
|
+
// props. The section seat hands NONE (`{ close }` is its whole owner share),
|
|
158
|
+
// so there this card IS the page and picks its own provider from the routes
|
|
159
|
+
// the namespace declares — see the resolvers above.
|
|
160
|
+
const seatRoute = entry && (entry.provider || row && row.provider)
|
|
108
161
|
const api = props.api
|
|
109
162
|
const scope = props.scope
|
|
110
|
-
|
|
163
|
+
// The panel starts COLLAPSED on the Models-page provider card, which renders
|
|
164
|
+
// one control per provider beside the others; it starts EXPANDED wherever the
|
|
165
|
+
// card is the whole page (`settings.section` passes `defaultOpen: true`), so
|
|
166
|
+
// the single-card seat opens with its content already visible. The trigger
|
|
167
|
+
// button below keeps folding it either way.
|
|
168
|
+
const [open, setOpen] = React.useState(props.defaultOpen === true)
|
|
169
|
+
// Which real route this card addresses on the page seat. Ignored while a
|
|
170
|
+
// provider arrives from the host, because a provider CARD is bound to the
|
|
171
|
+
// row it extends and must never follow a module-level selection.
|
|
172
|
+
const [picked, setPicked] = React.useState('')
|
|
111
173
|
const [busy, setBusy] = React.useState(false)
|
|
112
174
|
const [failed, setFailed] = React.useState('')
|
|
113
175
|
const [saved, setSaved] = React.useState('')
|
|
@@ -118,7 +180,22 @@ window.__ModuleLoader__.load({
|
|
|
118
180
|
proxyState.listeners.add(rerender)
|
|
119
181
|
return () => proxyState.listeners.delete(rerender)
|
|
120
182
|
}, [])
|
|
121
|
-
|
|
183
|
+
const [, refreshScope] = React.useState(0)
|
|
184
|
+
// The page's only reason to re-render beyond its own clicks: the namespace
|
|
185
|
+
// may still be loading at first paint, and a provider added in 设置 → 模型
|
|
186
|
+
// must appear here without a reload. ONE subscription on the same transport,
|
|
187
|
+
// disposed with the card.
|
|
188
|
+
React.useEffect(() => {
|
|
189
|
+
if (!scope || typeof scope.subscribe !== 'function') return undefined
|
|
190
|
+
const unsubscribe = scope.subscribe(() => refreshScope((value) => value + 1))
|
|
191
|
+
return () => { if (typeof unsubscribe === 'function') unsubscribe() }
|
|
192
|
+
}, [scope])
|
|
193
|
+
// A lookup answer is stamped with the route it was requested for: this card
|
|
194
|
+
// can change its addressed provider (the page seat's chooser, or the
|
|
195
|
+
// namespace dropping the picked route) while a request is in flight, and the
|
|
196
|
+
// previous provider's matches must never be previewed beside this one's
|
|
197
|
+
// models.
|
|
198
|
+
const [rawResult, setRawResult] = React.useState(null) // { route, matched: [], unmatched: [] }
|
|
122
199
|
const ref = React.useRef(null)
|
|
123
200
|
|
|
124
201
|
React.useEffect(() => {
|
|
@@ -133,29 +210,47 @@ window.__ModuleLoader__.load({
|
|
|
133
210
|
}
|
|
134
211
|
}, [open])
|
|
135
212
|
|
|
136
|
-
if (!route || settingsNs !== PI_NS) return null
|
|
137
|
-
|
|
138
213
|
const snap = scope && typeof scope.getSnapshot === 'function' ? scope.getSnapshot() : { status: 'unavailable' }
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
214
|
+
// Two seats, ONE card. Where the host supplies a provider, this is the
|
|
215
|
+
// per-provider control it always was. Where none arrives (the
|
|
216
|
+
// `settings.section` page), this card is the page: it addresses the first
|
|
217
|
+
// route the llm-pi-ai namespace declares, or the one the user picked from the
|
|
218
|
+
// namespace's real route list.
|
|
219
|
+
const sectionSeat = !seatRoute
|
|
220
|
+
const routes = sectionSeat ? piAiRoutes(snap) : []
|
|
221
|
+
const route = sectionSeat ? (routes.includes(picked) ? picked : routes[0]) : seatRoute
|
|
222
|
+
const settingsNs = sectionSeat ? PI_NS : entry && entry.settingsNs
|
|
223
|
+
if (!route || settingsNs !== PI_NS) {
|
|
224
|
+
// A provider CARD whose namespace is not ours renders nothing (the seat
|
|
225
|
+
// dispatches by settingsNs, so this is defensive). The page seat owns the
|
|
226
|
+
// whole surface instead, so it always explains what it cannot draw rather
|
|
227
|
+
// than showing an empty page.
|
|
228
|
+
if (!sectionSeat) return null
|
|
229
|
+
return e('div', { className: 'dpmWrap' }, e('p', { className: 'dpmHint' }, sectionPageHint(snap, scope)))
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const models = piAiModels(snap, route)
|
|
233
|
+
// Only this route's answer is this route's preview.
|
|
234
|
+
const result = rawResult && rawResult.route === route ? rawResult : null
|
|
142
235
|
const writable = snap.status === 'ready' && snap.writable === true && !!api && !!api.settings
|
|
143
236
|
|
|
144
|
-
async function load() {
|
|
145
|
-
|
|
237
|
+
async function load(routeOverride) {
|
|
238
|
+
const targetRoute = routeOverride || route
|
|
239
|
+
const targetModels = routeOverride ? piAiModels(snap, routeOverride) : models
|
|
240
|
+
if (!targetModels.length || busy) return
|
|
146
241
|
setBusy(true)
|
|
147
242
|
setFailed('')
|
|
148
243
|
setSaved('')
|
|
149
|
-
|
|
244
|
+
setRawResult(null)
|
|
150
245
|
const controller = new AbortController()
|
|
151
246
|
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS)
|
|
152
247
|
try {
|
|
153
|
-
const ids =
|
|
248
|
+
const ids = targetModels.map((model) => model.id).filter(Boolean)
|
|
154
249
|
const query = '?ids=' + encodeURIComponent(ids.join(',')) + (proxyState.enabled ? '&proxy=' + encodeURIComponent((proxyState.url.trim() || PROXY_DEFAULT)) : '')
|
|
155
250
|
const response = await fetch(API + query, { cache: 'no-store', signal: controller.signal })
|
|
156
251
|
const data = await response.json()
|
|
157
252
|
if (!data || data.ok !== true) throw new Error((data && data.error) || 'lookup failed')
|
|
158
|
-
|
|
253
|
+
setRawResult({ route: targetRoute, matched: data.matched || [], unmatched: data.unmatched || [] })
|
|
159
254
|
} catch (error) {
|
|
160
255
|
setFailed(String((error && error.name === 'AbortError') ? '请求超时' : (error && error.message) || error))
|
|
161
256
|
} finally {
|
|
@@ -221,7 +316,7 @@ window.__ModuleLoader__.load({
|
|
|
221
316
|
}
|
|
222
317
|
setSaved('已写入 provider "' + route + '" 的模型参数。')
|
|
223
318
|
setOpen(false)
|
|
224
|
-
|
|
319
|
+
setRawResult(null)
|
|
225
320
|
} catch (error) {
|
|
226
321
|
setFailed(String((error && error.message) || error))
|
|
227
322
|
} finally {
|
|
@@ -229,7 +324,10 @@ window.__ModuleLoader__.load({
|
|
|
229
324
|
}
|
|
230
325
|
}
|
|
231
326
|
|
|
232
|
-
|
|
327
|
+
// The Models page joins the official directory, so its `displayName` is the
|
|
328
|
+
// richest one. The page seat has only the namespace's own profile, which may
|
|
329
|
+
// declare one; otherwise the route itself is the honest label.
|
|
330
|
+
const displayName = (entry && entry.displayName) || piAiDisplayName(snap, route)
|
|
233
331
|
const hasModels = models.length > 0
|
|
234
332
|
const summary = result && result.matched ? result.matched.length + '/' + models.length + ' 匹配' : ''
|
|
235
333
|
|
|
@@ -238,6 +336,24 @@ window.__ModuleLoader__.load({
|
|
|
238
336
|
busy ? '查询中…' : ('models.dev 参数' + (summary ? ' · ' + summary : ''))),
|
|
239
337
|
open && e('div', { className: 'dpmPanel', role: 'dialog', 'aria-label': displayName + ' models.dev 参数' },
|
|
240
338
|
e('h4', null, displayName + ' · models.dev 官方参数'),
|
|
339
|
+
// A namespace may declare SEVERAL llm-pi-ai providers. This page holds ONE
|
|
340
|
+
// card, so it offers the namespace's real routes instead of silently
|
|
341
|
+
// addressing whichever one is declared first; a single-route namespace gets
|
|
342
|
+
// no chooser at all, because there is nothing to choose.
|
|
343
|
+
routes.length > 1 && e('label', { className: 'dpmRoute' }, 'provider',
|
|
344
|
+
e('select', {
|
|
345
|
+
value: route,
|
|
346
|
+
'aria-label': '选择要处理的 llm-pi-ai provider',
|
|
347
|
+
onChange: (event) => {
|
|
348
|
+
const next = event.target.value
|
|
349
|
+
setPicked(next)
|
|
350
|
+
setRawResult(null)
|
|
351
|
+
setSaved('')
|
|
352
|
+
setFailed('')
|
|
353
|
+
void load(next)
|
|
354
|
+
},
|
|
355
|
+
}, routes.map((item) => e('option', { key: item, value: item }, piAiDisplayName(snap, item)))),
|
|
356
|
+
),
|
|
241
357
|
e('div', { className: 'dpmProxy' },
|
|
242
358
|
e('label', null,
|
|
243
359
|
e('input', { type: 'checkbox', checked: proxyState.enabled, onChange: (ev) => { proxyState.enabled = ev.target.checked; notifyProxy() } }),
|
|
@@ -303,6 +419,9 @@ window.__ModuleLoader__.load({
|
|
|
303
419
|
// mounts resolves; see resolveSettingsScopeFrom.
|
|
304
420
|
let scope
|
|
305
421
|
let cardRegistered = false
|
|
422
|
+
// Slot-registration disposers, released from apply's own disposer on stop /
|
|
423
|
+
// update / HMR so no seat entry outlives the plugin.
|
|
424
|
+
const disposeSlots = []
|
|
306
425
|
// The provider card MUST register from inside a context that has the
|
|
307
426
|
// settings transport, i.e. from the `ctx.inject([...], cb)` callback and on
|
|
308
427
|
// the CHILD context it hands us. Registering
|
|
@@ -316,16 +435,60 @@ window.__ModuleLoader__.load({
|
|
|
316
435
|
if (resolved === undefined) return
|
|
317
436
|
scope = resolved
|
|
318
437
|
cardRegistered = true
|
|
319
|
-
sctx.slots.inject('settings.models.provider-card', () => sctx.slots.register(
|
|
438
|
+
disposeSlots.push(sctx.slots.inject('settings.models.provider-card', () => sctx.slots.register(
|
|
320
439
|
{ name: 'settings.models.provider-card', key: PI_NS },
|
|
321
440
|
(props) => e(ModelParamsExtension, { ...props, api, scope }),
|
|
322
|
-
))
|
|
441
|
+
)))
|
|
323
442
|
}
|
|
324
443
|
// The first wait fires only when settingsScope exists; the second covers a
|
|
325
444
|
// host that renamed the transport, and its guard keeps the card from
|
|
326
445
|
// registering twice when both names exist.
|
|
327
446
|
ctx.inject(['settingsScope'], registerCard)
|
|
328
447
|
ctx.inject(['configForms'], (sctx) => { if (!cardRegistered) registerCard(sctx) })
|
|
448
|
+
|
|
449
|
+
// ── additive seat: the `settings.section` page ─────────────────────────
|
|
450
|
+
//
|
|
451
|
+
// 0.1.7-rc.2 also declares the root-scope LIST slot `settings.section`
|
|
452
|
+
// ("one settings page per list entry"), which puts this card one click deep
|
|
453
|
+
// inside 设置 as a first-class page of its own. It is purely ADDITIVE: the
|
|
454
|
+
// provider-card extension above stays the Models-page affordance, and both
|
|
455
|
+
// render the SAME `ModelParamsExtension` through the SAME resolved transport
|
|
456
|
+
// — no second settings UI and no second read/write path. This package owns
|
|
457
|
+
// no bundle row, so there is no `plugins.row.config` occupant to mirror:
|
|
458
|
+
// its card is the official Models-page extension.
|
|
459
|
+
//
|
|
460
|
+
// That seat's owner share is `{ close }` alone, so this page is handed NO
|
|
461
|
+
// provider. It addresses one of the routes the llm-pi-ai namespace itself
|
|
462
|
+
// declares — read from the SAME resolved scope, so no second read path and no
|
|
463
|
+
// synthesized route — and explains a loading, unavailable or provider-less
|
|
464
|
+
// namespace instead of rendering a blank page. The page passes no `provider`
|
|
465
|
+
// prop, which is exactly how the card knows it is the whole page.
|
|
466
|
+
//
|
|
467
|
+
// The wait on `slots` is NON-GATING (the seat is host-version dependent: a
|
|
468
|
+
// host that does not declare it simply never fires the inner `slots.inject`,
|
|
469
|
+
// so this registration can never gate the plugin), the callback RETURNS the
|
|
470
|
+
// registration disposer, and that disposer is pushed onto disposeSlots so
|
|
471
|
+
// the entry is owned by the registering fiber and released with the rest on
|
|
472
|
+
// stop / update / HMR.
|
|
473
|
+
const registerSettingsSection = (sctx) => {
|
|
474
|
+
disposeSlots.push(sctx.slots.inject('settings.section', () => sctx.slots.register({
|
|
475
|
+
name: 'settings.section',
|
|
476
|
+
id: 'yotk-model-params',
|
|
477
|
+
order: 63,
|
|
478
|
+
label: () => 'YOTK · Model Params',
|
|
479
|
+
}, function ModelParamsSettingsSection() {
|
|
480
|
+
// The seat owner shares `close` and nothing else; this card needs
|
|
481
|
+
// neither it nor the host-owned optional `form` prop, because values
|
|
482
|
+
// keep flowing through the one resolved transport — so the page is the
|
|
483
|
+
// same card component the provider-card seat renders, with the panel
|
|
484
|
+
// expanded: one card alone on the page. The trigger button still folds
|
|
485
|
+
// it back up.
|
|
486
|
+
return e(ModelParamsExtension, { api, scope, defaultOpen: true })
|
|
487
|
+
})))
|
|
488
|
+
}
|
|
489
|
+
ctx.inject(['slots'], registerSettingsSection)
|
|
490
|
+
|
|
491
|
+
return () => { for (const dispose of disposeSlots) { try { dispose() } catch (_) {} } }
|
|
329
492
|
}
|
|
330
493
|
|
|
331
494
|
exports.name = 'dsh-model-params'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-model-params",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "EN: models.dev parameter assistant for the official DeepSeek Harness Models page: per-provider one-click context / max-output / reasoning-effort fill. ZH: 官方模型设置页的 models.dev 参数助手:按 provider 一键补全上下文、max 输出与推理档位。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|