@fonlan/dsh-model-parameters 0.1.4 → 0.1.6
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 +26 -22
- package/README.md +20 -19
- package/lib/client.js +65 -98
- package/lib/client.js.map +1 -1
- package/lib/index.js +92 -39
- package/lib/types/index.d.ts +4 -4
- package/lib/types/server/service.d.ts +27 -11
- package/lib/types/shared/config.d.ts +90 -8
- package/package.json +15 -15
- package/src/client/index.tsx +20 -17
- package/src/client/locales.ts +2 -6
- package/src/client/{settings-card.css → settings-section.css} +24 -79
- package/src/client/{settings-card.tsx → settings-section.tsx} +41 -62
- package/src/index.ts +5 -5
- package/src/server/service.ts +58 -32
- package/src/shared/config.ts +140 -17
package/src/shared/config.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Plugin configuration and shared constants for @fonlan/dsh-model-parameters.
|
|
3
3
|
*
|
|
4
|
-
* The plugin
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* models
|
|
4
|
+
* The plugin's own settings ARE its loader entry config (DSH >= 0.1.7): the
|
|
5
|
+
* profile entry whose id is `model-parameters` (see cordis.patch.yml) is
|
|
6
|
+
* validated with {@link Config}, the resolved document is handed to
|
|
7
|
+
* `apply(ctx, config)`, and the settings page is derived from that schema. The
|
|
8
|
+
* plugin OBSERVES the `llm-pi-ai` namespace: whenever provider models are
|
|
9
|
+
* written there with missing capability fields (which is the norm for a freshly
|
|
10
|
+
* synced gateway), it patches the missing fields from the cached models.dev
|
|
11
|
+
* catalog.
|
|
9
12
|
*/
|
|
10
13
|
import z from '@deepseek-ai/schemastery'
|
|
11
14
|
|
|
12
|
-
/** This plugin's own settings namespace. */
|
|
15
|
+
/** This plugin's own settings namespace: its profile entry id. */
|
|
13
16
|
export const PLUGIN_NS = 'model-parameters'
|
|
14
17
|
|
|
15
18
|
/** The namespace whose models this plugin completes: pi-ai provider profiles. */
|
|
@@ -59,7 +62,7 @@ export const REASONING_LEVELS: readonly string[] = [
|
|
|
59
62
|
'max',
|
|
60
63
|
]
|
|
61
64
|
|
|
62
|
-
/** Plugin configuration shape. */
|
|
65
|
+
/** Plugin configuration shape: the PLAIN document, with no live handles in it. */
|
|
63
66
|
export interface ModelParametersConfig {
|
|
64
67
|
/** Master switch: when off, no fills happen (listener is inert). */
|
|
65
68
|
enabled: boolean
|
|
@@ -81,16 +84,88 @@ export interface ModelParametersConfig {
|
|
|
81
84
|
officialProviders: string[]
|
|
82
85
|
}
|
|
83
86
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Recursively readonly value a live handle returns — cosmokit's
|
|
89
|
+
* `VolatileSnapshot<T>`, spelled locally for the same reason as
|
|
90
|
+
* {@link LiveSettingsField}.
|
|
91
|
+
*/
|
|
92
|
+
export type LiveSettingsSnapshot<T> = T extends object
|
|
93
|
+
? { readonly [K in keyof T]: LiveSettingsSnapshot<T[K]> }
|
|
94
|
+
: T
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* One `volatile()` config field as the loader hands it to `apply()`: a live
|
|
98
|
+
* handle, not the value it currently holds. Spelled structurally rather than as
|
|
99
|
+
* cosmokit's `Volatile<T>` on purpose — cosmokit is a transitive dependency
|
|
100
|
+
* here, and importing it would put a second schemastery/cosmokit pair into the
|
|
101
|
+
* emitted declaration.
|
|
102
|
+
*/
|
|
103
|
+
export interface LiveSettingsField<T> {
|
|
104
|
+
get(): LiveSettingsSnapshot<T>
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** A hand-written entry config (profile patch / test fixture): every field is optional. */
|
|
108
|
+
export type ModelParametersConfigInput = {
|
|
109
|
+
enabled?: boolean | null
|
|
110
|
+
ttlDays?: number | null
|
|
111
|
+
fillName?: boolean | null
|
|
112
|
+
fillContext?: boolean | null
|
|
113
|
+
fillMaxTokens?: boolean | null
|
|
114
|
+
fillReasoning?: boolean | null
|
|
115
|
+
fillInput?: boolean | null
|
|
116
|
+
providerMap?: Record<string, string> | null
|
|
117
|
+
officialProviders?: string[] | null
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** The resolved entry config `apply()` receives: every field is a live handle. */
|
|
121
|
+
export type ResolvedModelParametersConfig = {
|
|
122
|
+
enabled: LiveSettingsField<boolean>
|
|
123
|
+
ttlDays: LiveSettingsField<number>
|
|
124
|
+
fillName: LiveSettingsField<boolean>
|
|
125
|
+
fillContext: LiveSettingsField<boolean>
|
|
126
|
+
fillMaxTokens: LiveSettingsField<boolean>
|
|
127
|
+
fillReasoning: LiveSettingsField<boolean>
|
|
128
|
+
fillInput: LiveSettingsField<boolean>
|
|
129
|
+
providerMap: LiveSettingsField<Record<string, string>>
|
|
130
|
+
officialProviders: LiveSettingsField<string[]>
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Entry config schema. Deliberately NOT annotated `z<ModelParametersConfig>`:
|
|
135
|
+
* schemastery >= 3.18.3 carries a third `Mode` type parameter, so a
|
|
136
|
+
* `.volatile()` field's OUTPUT type is `Volatile<T>` (a live `{ get() }` cell),
|
|
137
|
+
* and that annotation no longer compiles. The plain document shape stays
|
|
138
|
+
* {@link ModelParametersConfig} and is what {@link readConfig} projects to.
|
|
139
|
+
*
|
|
140
|
+
* The annotation below names the type through the package's own default export
|
|
141
|
+
* (`z<...>`), with locally declared type arguments: the bare inferred type of a
|
|
142
|
+
* volatile schema resolves through a schemastery copy this package does not
|
|
143
|
+
* import, so `tsc -p tsconfig.build.json` (`declaration: true`) could not name
|
|
144
|
+
* it (TS2742) — the emitted declaration stays portable this way.
|
|
145
|
+
*
|
|
146
|
+
* `.volatile()` is REQUIRED on every field the settings page may write, not
|
|
147
|
+
* cosmetic: `dsh-settings` derives `volatileForm(schema)` from this schema, and
|
|
148
|
+
* with no volatile field at all it SKIPS this entry in `describe()` (the
|
|
149
|
+
* settings page disappears) and REJECTS every write as a non-volatile path.
|
|
150
|
+
* The profile entry id (`model-parameters`, see cordis.patch.yml) is the
|
|
151
|
+
* namespace those writes address.
|
|
152
|
+
*/
|
|
153
|
+
export type ModelParametersConfigSchema = z<
|
|
154
|
+
ModelParametersConfigInput,
|
|
155
|
+
ResolvedModelParametersConfig
|
|
156
|
+
>
|
|
157
|
+
|
|
158
|
+
/** The plugin's `Config` schema, exported for the loader and the settings page. */
|
|
159
|
+
export const Config: ModelParametersConfigSchema = z.object({
|
|
160
|
+
enabled: z.boolean().default(true).volatile(),
|
|
161
|
+
ttlDays: z.number().min(1).max(90).default(7).volatile(),
|
|
162
|
+
fillName: z.boolean().default(true).volatile(),
|
|
163
|
+
fillContext: z.boolean().default(true).volatile(),
|
|
164
|
+
fillMaxTokens: z.boolean().default(true).volatile(),
|
|
165
|
+
fillReasoning: z.boolean().default(true).volatile(),
|
|
166
|
+
fillInput: z.boolean().default(true).volatile(),
|
|
167
|
+
providerMap: z.dict(z.string()).default({}).volatile(),
|
|
168
|
+
officialProviders: z.array(z.string()).default([...DEFAULT_OFFICIAL_PROVIDERS]).volatile(),
|
|
94
169
|
})
|
|
95
170
|
|
|
96
171
|
export const DEFAULT_CONFIG: ModelParametersConfig = {
|
|
@@ -104,3 +179,51 @@ export const DEFAULT_CONFIG: ModelParametersConfig = {
|
|
|
104
179
|
providerMap: {},
|
|
105
180
|
officialProviders: [...DEFAULT_OFFICIAL_PROVIDERS],
|
|
106
181
|
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* The entry config as `apply()` receives it. Every field may arrive either as a
|
|
185
|
+
* live `Volatile` cell or as a plain value (a hand-built config object, a
|
|
186
|
+
* profile patch written before the volatile marker existed, or a host that
|
|
187
|
+
* resolves the document without the wrapper).
|
|
188
|
+
*/
|
|
189
|
+
export type ModelParametersLiveConfig = Partial<Record<keyof ModelParametersConfig, unknown>>
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Read a config node that may be a plain value or a live (`.volatile()`) node.
|
|
193
|
+
*
|
|
194
|
+
* A plugin whose Config declares volatile fields receives a reactive config
|
|
195
|
+
* whose fields expose `get()`, while hand-written config objects and tests pass
|
|
196
|
+
* plain values. Both shapes are supported here so the same code path serves the
|
|
197
|
+
* settings page, `cordis.patch.yml`, and the unit tests.
|
|
198
|
+
*/
|
|
199
|
+
function liveValue<T>(node: unknown, fallback: T): T {
|
|
200
|
+
if (node === undefined || node === null) return fallback
|
|
201
|
+
const getter = (node as { get?: unknown }).get
|
|
202
|
+
if (typeof getter === 'function') {
|
|
203
|
+
const value = (getter as () => unknown).call(node)
|
|
204
|
+
return value === undefined ? fallback : (value as T)
|
|
205
|
+
}
|
|
206
|
+
return node as T
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Project the loader entry config into the plain document shape, per field
|
|
211
|
+
* falling back to {@link DEFAULT_CONFIG}. The config is re-read on EVERY call,
|
|
212
|
+
* so settings-page edits (which the loader swaps into the live cells in place)
|
|
213
|
+
* take effect immediately. Container fields are copied so callers can never
|
|
214
|
+
* mutate the live document through a returned reference.
|
|
215
|
+
*/
|
|
216
|
+
export function readConfig(config: ModelParametersLiveConfig | undefined): ModelParametersConfig {
|
|
217
|
+
const section = config ?? {}
|
|
218
|
+
return {
|
|
219
|
+
enabled: liveValue(section.enabled, DEFAULT_CONFIG.enabled),
|
|
220
|
+
ttlDays: liveValue(section.ttlDays, DEFAULT_CONFIG.ttlDays),
|
|
221
|
+
fillName: liveValue(section.fillName, DEFAULT_CONFIG.fillName),
|
|
222
|
+
fillContext: liveValue(section.fillContext, DEFAULT_CONFIG.fillContext),
|
|
223
|
+
fillMaxTokens: liveValue(section.fillMaxTokens, DEFAULT_CONFIG.fillMaxTokens),
|
|
224
|
+
fillReasoning: liveValue(section.fillReasoning, DEFAULT_CONFIG.fillReasoning),
|
|
225
|
+
fillInput: liveValue(section.fillInput, DEFAULT_CONFIG.fillInput),
|
|
226
|
+
providerMap: { ...liveValue<Record<string, string>>(section.providerMap, DEFAULT_CONFIG.providerMap) },
|
|
227
|
+
officialProviders: [...liveValue<readonly string[]>(section.officialProviders, DEFAULT_CONFIG.officialProviders)],
|
|
228
|
+
}
|
|
229
|
+
}
|