@fonlan/dsh-model-parameters 0.1.0 → 0.1.3
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.en.md +16 -4
- package/README.md +14 -4
- package/lib/client.js +379 -302
- package/lib/client.js.map +1 -1
- package/package.json +5 -2
- package/src/client/index.tsx +35 -14
- package/src/client/locales.ts +6 -2
- package/src/client/settings-card.css +330 -0
- package/src/client/settings-card.tsx +395 -0
- package/src/client/settings-section.css +0 -172
- package/src/client/settings-section.tsx +0 -310
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The Model Parameters settings page: master + per-field toggles, catalog TTL,
|
|
3
|
-
* provider→models.dev mapping overrides, a manual refresh/fill action, the
|
|
4
|
-
* catalog freshness card, and the last fill report (filled / touched /
|
|
5
|
-
* unmatched). All reads/mutations go through the plugin's fenced API; config
|
|
6
|
-
* persists into the `model-parameters` settings namespace (settings.yaml).
|
|
7
|
-
*/
|
|
8
|
-
import { useCallback, useEffect, useMemo, useState, useSyncExternalStore } from 'react'
|
|
9
|
-
import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'
|
|
10
|
-
import type { Translate } from '@deepseek-ai/dsh-client-ui-slots'
|
|
11
|
-
import { LOCALE_NS } from './locales'
|
|
12
|
-
import { api, type PluginState } from './api'
|
|
13
|
-
import type { ModelParametersConfig } from '../shared/config.js'
|
|
14
|
-
import './settings-section.css'
|
|
15
|
-
|
|
16
|
-
function useLocaleRevision(ctx: ClientContext): number {
|
|
17
|
-
const subscribe = useCallback(
|
|
18
|
-
(onChange: () => void) => {
|
|
19
|
-
try {
|
|
20
|
-
return ctx.locale.subscribe(onChange)
|
|
21
|
-
} catch {
|
|
22
|
-
return () => {}
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
[ctx],
|
|
26
|
-
)
|
|
27
|
-
const getSnapshot = useCallback(() => {
|
|
28
|
-
try {
|
|
29
|
-
return ctx.locale.getLocale().revision
|
|
30
|
-
} catch {
|
|
31
|
-
return 0
|
|
32
|
-
}
|
|
33
|
-
}, [ctx])
|
|
34
|
-
return useSyncExternalStore(subscribe, getSnapshot)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function formatTime(epochMs: number): string {
|
|
38
|
-
try {
|
|
39
|
-
return new Date(epochMs).toLocaleString()
|
|
40
|
-
} catch {
|
|
41
|
-
return String(epochMs)
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function makeSettingsSection(ctx: ClientContext): () => JSX.Element {
|
|
46
|
-
const t: Translate = (() => {
|
|
47
|
-
try {
|
|
48
|
-
return ctx.locale.bind(LOCALE_NS) as unknown as Translate
|
|
49
|
-
} catch {
|
|
50
|
-
return (key: string) => key
|
|
51
|
-
}
|
|
52
|
-
})()
|
|
53
|
-
|
|
54
|
-
return function ModelParametersSettingsSection(): JSX.Element {
|
|
55
|
-
useLocaleRevision(ctx)
|
|
56
|
-
const [state, setState] = useState<PluginState | null>(null)
|
|
57
|
-
const [error, setError] = useState<string | null>(null)
|
|
58
|
-
const [loading, setLoading] = useState(true)
|
|
59
|
-
const [busy, setBusy] = useState(false)
|
|
60
|
-
const [saving, setSaving] = useState(false)
|
|
61
|
-
const [mappingRows, setMappingRows] = useState<Array<{ from: string; to: string }>>([])
|
|
62
|
-
const [newFrom, setNewFrom] = useState('')
|
|
63
|
-
const [newTo, setNewTo] = useState('')
|
|
64
|
-
|
|
65
|
-
const load = useCallback(async () => {
|
|
66
|
-
setLoading(true)
|
|
67
|
-
try {
|
|
68
|
-
const next = await api.state()
|
|
69
|
-
setState(next)
|
|
70
|
-
setMappingRows(Object.entries(next.config.providerMap).map(([from, to]) => ({ from, to })))
|
|
71
|
-
setError(null)
|
|
72
|
-
} catch (cause) {
|
|
73
|
-
setError(cause instanceof Error ? cause.message : String(cause))
|
|
74
|
-
} finally {
|
|
75
|
-
setLoading(false)
|
|
76
|
-
}
|
|
77
|
-
}, [])
|
|
78
|
-
|
|
79
|
-
useEffect(() => {
|
|
80
|
-
void load()
|
|
81
|
-
}, [load])
|
|
82
|
-
|
|
83
|
-
const patchConfig = useCallback(async (patch: Partial<ModelParametersConfig>) => {
|
|
84
|
-
setSaving(true)
|
|
85
|
-
try {
|
|
86
|
-
const next = await api.setConfig(patch)
|
|
87
|
-
setState(next)
|
|
88
|
-
setMappingRows(Object.entries(next.config.providerMap).map(([from, to]) => ({ from, to })))
|
|
89
|
-
setError(null)
|
|
90
|
-
} catch (cause) {
|
|
91
|
-
setError(cause instanceof Error ? cause.message : String(cause))
|
|
92
|
-
} finally {
|
|
93
|
-
setSaving(false)
|
|
94
|
-
}
|
|
95
|
-
}, [])
|
|
96
|
-
|
|
97
|
-
const refresh = useCallback(async () => {
|
|
98
|
-
setBusy(true)
|
|
99
|
-
try {
|
|
100
|
-
setState(await api.refresh())
|
|
101
|
-
setError(null)
|
|
102
|
-
} catch (cause) {
|
|
103
|
-
setError(cause instanceof Error ? cause.message : String(cause))
|
|
104
|
-
} finally {
|
|
105
|
-
setBusy(false)
|
|
106
|
-
}
|
|
107
|
-
}, [])
|
|
108
|
-
|
|
109
|
-
const reconcileNow = useCallback(async () => {
|
|
110
|
-
setBusy(true)
|
|
111
|
-
try {
|
|
112
|
-
setState(await api.reconcile())
|
|
113
|
-
setError(null)
|
|
114
|
-
} catch (cause) {
|
|
115
|
-
setError(cause instanceof Error ? cause.message : String(cause))
|
|
116
|
-
} finally {
|
|
117
|
-
setBusy(false)
|
|
118
|
-
}
|
|
119
|
-
}, [])
|
|
120
|
-
|
|
121
|
-
const applyMappings = useCallback(async () => {
|
|
122
|
-
const providerMap: Record<string, string> = {}
|
|
123
|
-
for (const row of mappingRows) {
|
|
124
|
-
const from = row.from.trim()
|
|
125
|
-
const to = row.to.trim()
|
|
126
|
-
if (from.length > 0 && to.length > 0) providerMap[from] = to
|
|
127
|
-
}
|
|
128
|
-
await patchConfig({ providerMap })
|
|
129
|
-
}, [mappingRows, patchConfig])
|
|
130
|
-
|
|
131
|
-
if (error !== null && state === null) {
|
|
132
|
-
return (
|
|
133
|
-
<p className="mp-error">{t('error')}: {error}</p>
|
|
134
|
-
)
|
|
135
|
-
}
|
|
136
|
-
if (loading || state === null) {
|
|
137
|
-
return <p className="mp-muted">{t('saving')}</p>
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const config = state.config
|
|
141
|
-
const catalog = state.catalog
|
|
142
|
-
const report = state.report
|
|
143
|
-
|
|
144
|
-
return (
|
|
145
|
-
<div className="mp-root">
|
|
146
|
-
<p className="mp-desc">{t('description')}</p>
|
|
147
|
-
|
|
148
|
-
<section className="mp-card">
|
|
149
|
-
<h4 className="mp-card-title">{t('fields')}</h4>
|
|
150
|
-
<label className="mp-toggle">
|
|
151
|
-
<input
|
|
152
|
-
type="checkbox"
|
|
153
|
-
checked={config.enabled}
|
|
154
|
-
disabled={saving}
|
|
155
|
-
onChange={(event) => void patchConfig({ enabled: event.target.checked })}
|
|
156
|
-
/>
|
|
157
|
-
<span className="mp-toggle-body">
|
|
158
|
-
<span className="mp-toggle-label">{t('master')}</span>
|
|
159
|
-
<span className="mp-toggle-desc">{t('masterDesc')}</span>
|
|
160
|
-
</span>
|
|
161
|
-
</label>
|
|
162
|
-
{([
|
|
163
|
-
['fillName', t('fillName'), t('fillNameDesc')],
|
|
164
|
-
['fillContext', t('fillContext'), t('fillContextDesc')],
|
|
165
|
-
['fillMaxTokens', t('fillMaxTokens'), t('fillMaxTokensDesc')],
|
|
166
|
-
['fillReasoning', t('fillReasoning'), t('fillReasoningDesc')],
|
|
167
|
-
['fillInput', t('fillInput'), t('fillInputDesc')],
|
|
168
|
-
] as const).map(([key, label, desc]) => (
|
|
169
|
-
<label className="mp-toggle mp-toggle-sub" key={key}>
|
|
170
|
-
<input
|
|
171
|
-
type="checkbox"
|
|
172
|
-
checked={config[key]}
|
|
173
|
-
disabled={saving || !config.enabled}
|
|
174
|
-
onChange={(event) => void patchConfig({ [key]: event.target.checked })}
|
|
175
|
-
/>
|
|
176
|
-
<span className="mp-toggle-body">
|
|
177
|
-
<span className="mp-toggle-label">{label}</span>
|
|
178
|
-
<span className="mp-toggle-desc">{desc}</span>
|
|
179
|
-
</span>
|
|
180
|
-
</label>
|
|
181
|
-
))}
|
|
182
|
-
</section>
|
|
183
|
-
|
|
184
|
-
<section className="mp-card">
|
|
185
|
-
<h4 className="mp-card-title">{t('catalog')}</h4>
|
|
186
|
-
<div className="mp-meta">
|
|
187
|
-
<span>{t('catalogEntries')}: <strong>{catalog.entries.toLocaleString()}</strong></span>
|
|
188
|
-
<span>{t('catalogProviders')}: <strong>{catalog.providers.toLocaleString()}</strong></span>
|
|
189
|
-
<span>
|
|
190
|
-
{t('catalogFetched')}:{' '}
|
|
191
|
-
<strong>{catalog.fetchedAt > 0 ? formatTime(catalog.fetchedAt) : t('catalogNever')}</strong>{' '}
|
|
192
|
-
{catalog.fetchedAt > 0 && (
|
|
193
|
-
<span className={catalog.fresh ? 'mp-badge mp-badge-ok' : 'mp-badge mp-badge-warn'}>
|
|
194
|
-
{catalog.fresh ? t('catalogFresh') : t('catalogStale')}
|
|
195
|
-
</span>
|
|
196
|
-
)}
|
|
197
|
-
</span>
|
|
198
|
-
<label className="mp-ttl">
|
|
199
|
-
{t('ttl')}
|
|
200
|
-
<input
|
|
201
|
-
type="number"
|
|
202
|
-
min={1}
|
|
203
|
-
max={90}
|
|
204
|
-
value={config.ttlDays}
|
|
205
|
-
disabled={saving}
|
|
206
|
-
onChange={(event) => {
|
|
207
|
-
const days = Number(event.target.value)
|
|
208
|
-
if (Number.isFinite(days) && days >= 1 && days <= 90) void patchConfig({ ttlDays: Math.round(days) })
|
|
209
|
-
}}
|
|
210
|
-
/>
|
|
211
|
-
</label>
|
|
212
|
-
</div>
|
|
213
|
-
<div className="mp-actions">
|
|
214
|
-
<button className="mp-button" disabled={busy} onClick={() => void refresh()}>
|
|
215
|
-
{busy ? t('refreshBusy') : t('refresh')}
|
|
216
|
-
</button>
|
|
217
|
-
<button className="mp-button mp-button-ghost" disabled={busy} onClick={() => void reconcileNow()}>
|
|
218
|
-
{t('reconcileNow')}
|
|
219
|
-
</button>
|
|
220
|
-
</div>
|
|
221
|
-
</section>
|
|
222
|
-
|
|
223
|
-
<section className="mp-card">
|
|
224
|
-
<h4 className="mp-card-title">{t('providerMap')}</h4>
|
|
225
|
-
<p className="mp-toggle-desc">{t('providerMapDesc')}</p>
|
|
226
|
-
{mappingRows.map((row, index) => (
|
|
227
|
-
<div className="mp-map-row" key={index}>
|
|
228
|
-
<input
|
|
229
|
-
value={row.from}
|
|
230
|
-
disabled={saving}
|
|
231
|
-
onChange={(event) => {
|
|
232
|
-
const next = [...mappingRows]
|
|
233
|
-
next[index] = { ...row, from: event.target.value }
|
|
234
|
-
setMappingRows(next)
|
|
235
|
-
}}
|
|
236
|
-
/>
|
|
237
|
-
<span className="mp-map-arrow">→</span>
|
|
238
|
-
<input
|
|
239
|
-
value={row.to}
|
|
240
|
-
disabled={saving}
|
|
241
|
-
onChange={(event) => {
|
|
242
|
-
const next = [...mappingRows]
|
|
243
|
-
next[index] = { ...row, to: event.target.value }
|
|
244
|
-
setMappingRows(next)
|
|
245
|
-
}}
|
|
246
|
-
/>
|
|
247
|
-
<button
|
|
248
|
-
className="mp-button mp-button-ghost"
|
|
249
|
-
disabled={saving}
|
|
250
|
-
onClick={() => setMappingRows(mappingRows.filter((_, i) => i !== index))}
|
|
251
|
-
>
|
|
252
|
-
{t('removeMapping')}
|
|
253
|
-
</button>
|
|
254
|
-
</div>
|
|
255
|
-
))}
|
|
256
|
-
<div className="mp-map-row">
|
|
257
|
-
<input
|
|
258
|
-
placeholder="dsh provider"
|
|
259
|
-
value={newFrom}
|
|
260
|
-
onChange={(event) => setNewFrom(event.target.value)}
|
|
261
|
-
/>
|
|
262
|
-
<span className="mp-map-arrow">→</span>
|
|
263
|
-
<input
|
|
264
|
-
placeholder="models.dev provider"
|
|
265
|
-
value={newTo}
|
|
266
|
-
onChange={(event) => setNewTo(event.target.value)}
|
|
267
|
-
/>
|
|
268
|
-
<button
|
|
269
|
-
className="mp-button"
|
|
270
|
-
disabled={newFrom.trim().length === 0 || newTo.trim().length === 0}
|
|
271
|
-
onClick={() => {
|
|
272
|
-
setMappingRows([...mappingRows, { from: newFrom.trim(), to: newTo.trim() }])
|
|
273
|
-
setNewFrom('')
|
|
274
|
-
setNewTo('')
|
|
275
|
-
}}
|
|
276
|
-
>
|
|
277
|
-
{t('addMapping')}
|
|
278
|
-
</button>
|
|
279
|
-
</div>
|
|
280
|
-
<div className="mp-actions">
|
|
281
|
-
<button className="mp-button mp-button-ghost" disabled={saving} onClick={() => void applyMappings()}>
|
|
282
|
-
{saving ? t('saving') : '✓ ' + t('providerMap')}
|
|
283
|
-
</button>
|
|
284
|
-
</div>
|
|
285
|
-
</section>
|
|
286
|
-
|
|
287
|
-
<section className="mp-card">
|
|
288
|
-
<h4 className="mp-card-title">{t('report')}</h4>
|
|
289
|
-
{report === null ? (
|
|
290
|
-
<p className="mp-muted">{t('reportNever')}</p>
|
|
291
|
-
) : (
|
|
292
|
-
<ul className="mp-report">
|
|
293
|
-
<li>{t('reportAt')}: {formatTime(report.at)}</li>
|
|
294
|
-
<li>{t('reportFilled')}: <strong>{report.filled}</strong></li>
|
|
295
|
-
<li>{t('reportTouched')}: <strong>{report.touched}</strong></li>
|
|
296
|
-
<li>
|
|
297
|
-
{t('reportUnmatched')}:{' '}
|
|
298
|
-
{report.unmatched.length === 0
|
|
299
|
-
? t('reportEmpty')
|
|
300
|
-
: <code className="mp-code">{report.unmatched.join(', ')}</code>}
|
|
301
|
-
</li>
|
|
302
|
-
</ul>
|
|
303
|
-
)}
|
|
304
|
-
</section>
|
|
305
|
-
|
|
306
|
-
{error !== null && <p className="mp-error">{error}</p>}
|
|
307
|
-
</div>
|
|
308
|
-
)
|
|
309
|
-
}
|
|
310
|
-
}
|