@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/lib/index.js
CHANGED
|
@@ -6,13 +6,16 @@ import { join } from "node:path";
|
|
|
6
6
|
/**
|
|
7
7
|
* Plugin configuration and shared constants for @fonlan/dsh-model-parameters.
|
|
8
8
|
*
|
|
9
|
-
* The plugin
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* models
|
|
9
|
+
* The plugin's own settings ARE its loader entry config (DSH >= 0.1.7): the
|
|
10
|
+
* profile entry whose id is `model-parameters` (see cordis.patch.yml) is
|
|
11
|
+
* validated with {@link Config}, the resolved document is handed to
|
|
12
|
+
* `apply(ctx, config)`, and the settings page is derived from that schema. The
|
|
13
|
+
* plugin OBSERVES the `llm-pi-ai` namespace: whenever provider models are
|
|
14
|
+
* written there with missing capability fields (which is the norm for a freshly
|
|
15
|
+
* synced gateway), it patches the missing fields from the cached models.dev
|
|
16
|
+
* catalog.
|
|
14
17
|
*/
|
|
15
|
-
/** This plugin's own settings namespace. */
|
|
18
|
+
/** This plugin's own settings namespace: its profile entry id. */
|
|
16
19
|
const PLUGIN_NS = "model-parameters";
|
|
17
20
|
/** The namespace whose models this plugin completes: pi-ai provider profiles. */
|
|
18
21
|
const TARGET_NS = "llm-pi-ai";
|
|
@@ -56,16 +59,17 @@ const REASONING_LEVELS = [
|
|
|
56
59
|
"xhigh",
|
|
57
60
|
"max"
|
|
58
61
|
];
|
|
62
|
+
/** The plugin's `Config` schema, exported for the loader and the settings page. */
|
|
59
63
|
const Config = z.object({
|
|
60
|
-
enabled: z.boolean().default(true),
|
|
61
|
-
ttlDays: z.number().min(1).max(90).default(7),
|
|
62
|
-
fillName: z.boolean().default(true),
|
|
63
|
-
fillContext: z.boolean().default(true),
|
|
64
|
-
fillMaxTokens: z.boolean().default(true),
|
|
65
|
-
fillReasoning: z.boolean().default(true),
|
|
66
|
-
fillInput: z.boolean().default(true),
|
|
67
|
-
providerMap: z.dict(z.string()).default({}),
|
|
68
|
-
officialProviders: z.array(z.string()).default([...DEFAULT_OFFICIAL_PROVIDERS])
|
|
64
|
+
enabled: z.boolean().default(true).volatile(),
|
|
65
|
+
ttlDays: z.number().min(1).max(90).default(7).volatile(),
|
|
66
|
+
fillName: z.boolean().default(true).volatile(),
|
|
67
|
+
fillContext: z.boolean().default(true).volatile(),
|
|
68
|
+
fillMaxTokens: z.boolean().default(true).volatile(),
|
|
69
|
+
fillReasoning: z.boolean().default(true).volatile(),
|
|
70
|
+
fillInput: z.boolean().default(true).volatile(),
|
|
71
|
+
providerMap: z.dict(z.string()).default({}).volatile(),
|
|
72
|
+
officialProviders: z.array(z.string()).default([...DEFAULT_OFFICIAL_PROVIDERS]).volatile()
|
|
69
73
|
});
|
|
70
74
|
const DEFAULT_CONFIG = {
|
|
71
75
|
enabled: true,
|
|
@@ -78,6 +82,44 @@ const DEFAULT_CONFIG = {
|
|
|
78
82
|
providerMap: {},
|
|
79
83
|
officialProviders: [...DEFAULT_OFFICIAL_PROVIDERS]
|
|
80
84
|
};
|
|
85
|
+
/**
|
|
86
|
+
* Read a config node that may be a plain value or a live (`.volatile()`) node.
|
|
87
|
+
*
|
|
88
|
+
* A plugin whose Config declares volatile fields receives a reactive config
|
|
89
|
+
* whose fields expose `get()`, while hand-written config objects and tests pass
|
|
90
|
+
* plain values. Both shapes are supported here so the same code path serves the
|
|
91
|
+
* settings page, `cordis.patch.yml`, and the unit tests.
|
|
92
|
+
*/
|
|
93
|
+
function liveValue(node, fallback) {
|
|
94
|
+
if (node === void 0 || node === null) return fallback;
|
|
95
|
+
const getter = node.get;
|
|
96
|
+
if (typeof getter === "function") {
|
|
97
|
+
const value = getter.call(node);
|
|
98
|
+
return value === void 0 ? fallback : value;
|
|
99
|
+
}
|
|
100
|
+
return node;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Project the loader entry config into the plain document shape, per field
|
|
104
|
+
* falling back to {@link DEFAULT_CONFIG}. The config is re-read on EVERY call,
|
|
105
|
+
* so settings-page edits (which the loader swaps into the live cells in place)
|
|
106
|
+
* take effect immediately. Container fields are copied so callers can never
|
|
107
|
+
* mutate the live document through a returned reference.
|
|
108
|
+
*/
|
|
109
|
+
function readConfig(config) {
|
|
110
|
+
const section = config ?? {};
|
|
111
|
+
return {
|
|
112
|
+
enabled: liveValue(section.enabled, DEFAULT_CONFIG.enabled),
|
|
113
|
+
ttlDays: liveValue(section.ttlDays, DEFAULT_CONFIG.ttlDays),
|
|
114
|
+
fillName: liveValue(section.fillName, DEFAULT_CONFIG.fillName),
|
|
115
|
+
fillContext: liveValue(section.fillContext, DEFAULT_CONFIG.fillContext),
|
|
116
|
+
fillMaxTokens: liveValue(section.fillMaxTokens, DEFAULT_CONFIG.fillMaxTokens),
|
|
117
|
+
fillReasoning: liveValue(section.fillReasoning, DEFAULT_CONFIG.fillReasoning),
|
|
118
|
+
fillInput: liveValue(section.fillInput, DEFAULT_CONFIG.fillInput),
|
|
119
|
+
providerMap: { ...liveValue(section.providerMap, DEFAULT_CONFIG.providerMap) },
|
|
120
|
+
officialProviders: [...liveValue(section.officialProviders, DEFAULT_CONFIG.officialProviders)]
|
|
121
|
+
};
|
|
122
|
+
}
|
|
81
123
|
//#endregion
|
|
82
124
|
//#region src/server/catalog.ts
|
|
83
125
|
/**
|
|
@@ -309,6 +351,7 @@ const TARGET = TARGET_NS;
|
|
|
309
351
|
const OWN = PLUGIN_NS;
|
|
310
352
|
var ModelParametersService = class {
|
|
311
353
|
ctx;
|
|
354
|
+
entryConfig;
|
|
312
355
|
/**
|
|
313
356
|
* The settings service value captured from the inject callback. The plugin
|
|
314
357
|
* itself declares `inject: []` (settings is optional — headless profiles
|
|
@@ -317,42 +360,45 @@ var ModelParametersService = class {
|
|
|
317
360
|
* a sibling fiber. The injected child ctx itself is only valid while its
|
|
318
361
|
* fiber is active — the provider can be reloaded (and every dependent
|
|
319
362
|
* fiber torn down) while an async reconcile is in flight — so all reads
|
|
320
|
-
* and writes go through the captured service
|
|
321
|
-
* object and never walks the fiber chain.
|
|
363
|
+
* and writes of the `llm-pi-ai` namespace go through the captured service
|
|
364
|
+
* value, which is a plain object and never walks the fiber chain.
|
|
322
365
|
*/
|
|
323
366
|
settings;
|
|
324
|
-
scope;
|
|
325
367
|
lastReport = null;
|
|
326
368
|
catalog;
|
|
327
369
|
reconciling = null;
|
|
328
|
-
|
|
370
|
+
/**
|
|
371
|
+
* @param ctx the plugin context.
|
|
372
|
+
* @param entryConfig this plugin's own live entry config (the profile entry
|
|
373
|
+
* `model-parameters`): the resolved `Config` document, whose `.volatile()`
|
|
374
|
+
* fields are live cells that the loader swaps in place on every settings
|
|
375
|
+
* write. Read through {@link readConfig} on each access, never cached.
|
|
376
|
+
*/
|
|
377
|
+
constructor(ctx, entryConfig) {
|
|
329
378
|
this.ctx = ctx;
|
|
379
|
+
this.entryConfig = entryConfig;
|
|
330
380
|
this.catalog = new CatalogManager(ctx.logger);
|
|
331
381
|
}
|
|
332
382
|
start() {
|
|
333
383
|
this.ctx.inject(["settings"], (sctx) => {
|
|
334
384
|
this.settings = sctx.settings;
|
|
335
|
-
this.scope = this.settings.register(OWN, Config);
|
|
336
385
|
this.reconcile();
|
|
337
386
|
});
|
|
338
|
-
this.ctx.on("settings/updated", (ns) => {
|
|
387
|
+
this.ctx.on("settings/document-updated", (ns) => {
|
|
339
388
|
if (ns !== TARGET) return;
|
|
340
389
|
this.reconcile();
|
|
341
390
|
});
|
|
342
391
|
}
|
|
343
392
|
stop() {
|
|
344
|
-
this.scope = void 0;
|
|
345
393
|
this.settings = void 0;
|
|
346
394
|
}
|
|
395
|
+
/**
|
|
396
|
+
* The plugin's current settings, projected from the live entry config.
|
|
397
|
+
* Works with no settings service at all (headless), which is why the plain
|
|
398
|
+
* {@link DEFAULT_CONFIG} fallback lives in {@link readConfig}.
|
|
399
|
+
*/
|
|
347
400
|
config() {
|
|
348
|
-
|
|
349
|
-
return this.scope.get();
|
|
350
|
-
} catch {}
|
|
351
|
-
return {
|
|
352
|
-
...DEFAULT_CONFIG,
|
|
353
|
-
providerMap: {},
|
|
354
|
-
officialProviders: [...DEFAULT_CONFIG.officialProviders]
|
|
355
|
-
};
|
|
401
|
+
return readConfig(this.entryConfig);
|
|
356
402
|
}
|
|
357
403
|
catalogSnapshot() {
|
|
358
404
|
return this.catalog.snapshotOf();
|
|
@@ -365,14 +411,21 @@ var ModelParametersService = class {
|
|
|
365
411
|
await this.catalog.refresh(this.config().ttlDays);
|
|
366
412
|
await this.reconcile();
|
|
367
413
|
}
|
|
368
|
-
/**
|
|
414
|
+
/**
|
|
415
|
+
* Update the plugin's own settings from the settings page. `dsh-settings`
|
|
416
|
+
* addresses the plugin's loader entry by its id and refuses any path the
|
|
417
|
+
* `Config` schema did not mark volatile — every field here is volatile.
|
|
418
|
+
*/
|
|
369
419
|
async updateConfig(patch) {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
420
|
+
const settings = this.settings;
|
|
421
|
+
if (settings === void 0) throw new Error("model-parameters: settings service is not available in this profile");
|
|
422
|
+
const { providerMap, ...rest } = patch;
|
|
423
|
+
if (Object.keys(rest).length > 0) await settings.update(OWN, rest);
|
|
424
|
+
if (providerMap !== void 0) await settings.mutate(OWN, [{
|
|
425
|
+
op: "set",
|
|
426
|
+
path: ["providerMap"],
|
|
427
|
+
value: providerMap
|
|
428
|
+
}]);
|
|
376
429
|
}
|
|
377
430
|
/**
|
|
378
431
|
* Fill missing fields on every provider model from the catalog. Serialized:
|
|
@@ -622,8 +675,8 @@ function stateOf(service) {
|
|
|
622
675
|
//#region src/index.ts
|
|
623
676
|
const name = "@fonlan/dsh-model-parameters";
|
|
624
677
|
const inject = [];
|
|
625
|
-
function apply(ctx) {
|
|
626
|
-
const service = new ModelParametersService(ctx);
|
|
678
|
+
function apply(ctx, config = {}) {
|
|
679
|
+
const service = new ModelParametersService(ctx, config);
|
|
627
680
|
ctx.effect(() => {
|
|
628
681
|
service.start();
|
|
629
682
|
return () => service.stop();
|
package/lib/types/index.d.ts
CHANGED
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
* tokens / reasoning efforts / input modalities (the norm for a freshly
|
|
7
7
|
* synced gateway), it fills the missing fields from a cached models.dev
|
|
8
8
|
* catalog (TTL-refreshed), and serves the fenced JSON API the web settings
|
|
9
|
-
* page calls. The plugin's own toggles
|
|
10
|
-
*
|
|
9
|
+
* page calls. The plugin's own toggles are its loader entry config (the
|
|
10
|
+
* `model-parameters` profile entry, schema {@link Config}).
|
|
11
11
|
*/
|
|
12
12
|
import type { Context } from '@deepseek-ai/cordis';
|
|
13
|
-
import { Config } from './shared/config.js';
|
|
13
|
+
import { Config, type ModelParametersLiveConfig } from './shared/config.js';
|
|
14
14
|
import { CatalogManager, acceptedInputModalities, identityReasoningEfforts, type CatalogEntry, type CatalogSnapshot } from './server/catalog.js';
|
|
15
15
|
export declare const name = "@fonlan/dsh-model-parameters";
|
|
16
16
|
export declare const inject: string[];
|
|
17
17
|
export { Config };
|
|
18
18
|
export { CatalogManager, acceptedInputModalities, identityReasoningEfforts };
|
|
19
19
|
export type { CatalogEntry, CatalogSnapshot };
|
|
20
|
-
export declare function apply(ctx: Context): void;
|
|
20
|
+
export declare function apply(ctx: Context, config?: ModelParametersLiveConfig): void;
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The model-parameters service:
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* The model-parameters service: completes missing model fields in the
|
|
3
|
+
* `llm-pi-ai` settings namespace from the cached models.dev catalog and tracks
|
|
4
|
+
* a fill report. Its own settings ARE this plugin's loader entry config (see
|
|
5
|
+
* shared/config.ts), so there is no per-plugin settings scope to own here.
|
|
5
6
|
*
|
|
6
7
|
* Trigger model:
|
|
7
8
|
* - any committed `llm-pi-ai` settings change (settings UI apply, manual
|
|
8
|
-
*
|
|
9
|
-
* external writes) runs a reconcile;
|
|
9
|
+
* profile-patch edit, model-router-style external writes) runs a reconcile;
|
|
10
10
|
* - plugin start runs a backfill reconcile over the existing configuration.
|
|
11
11
|
* Reconcile is fill-only: it never overwrites a present field, and it writes
|
|
12
12
|
* nothing when nothing is missing — so its own writes cannot re-trigger a
|
|
13
13
|
* second patch (the next event sees the fields present and produces no ops).
|
|
14
14
|
*/
|
|
15
15
|
import type { Context } from '@deepseek-ai/cordis';
|
|
16
|
-
import { type ModelParametersConfig } from '../shared/config.js';
|
|
16
|
+
import { type ModelParametersConfig, type ModelParametersLiveConfig } from '../shared/config.js';
|
|
17
17
|
import { type CatalogSnapshot } from './catalog.js';
|
|
18
18
|
/** One reconcile run's outcome, surfaced to the settings page. */
|
|
19
19
|
export interface FillReport {
|
|
@@ -27,6 +27,7 @@ export interface FillReport {
|
|
|
27
27
|
}
|
|
28
28
|
export declare class ModelParametersService {
|
|
29
29
|
private readonly ctx;
|
|
30
|
+
private readonly entryConfig;
|
|
30
31
|
/**
|
|
31
32
|
* The settings service value captured from the inject callback. The plugin
|
|
32
33
|
* itself declares `inject: []` (settings is optional — headless profiles
|
|
@@ -35,23 +36,38 @@ export declare class ModelParametersService {
|
|
|
35
36
|
* a sibling fiber. The injected child ctx itself is only valid while its
|
|
36
37
|
* fiber is active — the provider can be reloaded (and every dependent
|
|
37
38
|
* fiber torn down) while an async reconcile is in flight — so all reads
|
|
38
|
-
* and writes go through the captured service
|
|
39
|
-
* object and never walks the fiber chain.
|
|
39
|
+
* and writes of the `llm-pi-ai` namespace go through the captured service
|
|
40
|
+
* value, which is a plain object and never walks the fiber chain.
|
|
40
41
|
*/
|
|
41
42
|
private settings;
|
|
42
|
-
private scope;
|
|
43
43
|
private lastReport;
|
|
44
44
|
private readonly catalog;
|
|
45
45
|
private reconciling;
|
|
46
|
-
|
|
46
|
+
/**
|
|
47
|
+
* @param ctx the plugin context.
|
|
48
|
+
* @param entryConfig this plugin's own live entry config (the profile entry
|
|
49
|
+
* `model-parameters`): the resolved `Config` document, whose `.volatile()`
|
|
50
|
+
* fields are live cells that the loader swaps in place on every settings
|
|
51
|
+
* write. Read through {@link readConfig} on each access, never cached.
|
|
52
|
+
*/
|
|
53
|
+
constructor(ctx: Context, entryConfig: ModelParametersLiveConfig | undefined);
|
|
47
54
|
start(): void;
|
|
48
55
|
stop(): void;
|
|
56
|
+
/**
|
|
57
|
+
* The plugin's current settings, projected from the live entry config.
|
|
58
|
+
* Works with no settings service at all (headless), which is why the plain
|
|
59
|
+
* {@link DEFAULT_CONFIG} fallback lives in {@link readConfig}.
|
|
60
|
+
*/
|
|
49
61
|
config(): ModelParametersConfig;
|
|
50
62
|
catalogSnapshot(): CatalogSnapshot;
|
|
51
63
|
report(): FillReport | null;
|
|
52
64
|
/** Force a catalog refresh, then reconcile and report. */
|
|
53
65
|
refreshAndReconcile(): Promise<void>;
|
|
54
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* Update the plugin's own settings from the settings page. `dsh-settings`
|
|
68
|
+
* addresses the plugin's loader entry by its id and refuses any path the
|
|
69
|
+
* `Config` schema did not mark volatile — every field here is volatile.
|
|
70
|
+
*/
|
|
55
71
|
updateConfig(patch: Partial<ModelParametersConfig>): Promise<void>;
|
|
56
72
|
/**
|
|
57
73
|
* Fill missing fields on every provider model from the catalog. Serialized:
|
|
@@ -1,14 +1,17 @@
|
|
|
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
|
-
/** This plugin's own settings namespace. */
|
|
14
|
+
/** This plugin's own settings namespace: its profile entry id. */
|
|
12
15
|
export declare const PLUGIN_NS = "model-parameters";
|
|
13
16
|
/** The namespace whose models this plugin completes: pi-ai provider profiles. */
|
|
14
17
|
export declare const TARGET_NS = "llm-pi-ai";
|
|
@@ -28,7 +31,7 @@ export declare const ACCEPTED_INPUT_MODALITIES: readonly string[];
|
|
|
28
31
|
* this set are written.
|
|
29
32
|
*/
|
|
30
33
|
export declare const REASONING_LEVELS: readonly string[];
|
|
31
|
-
/** Plugin configuration shape. */
|
|
34
|
+
/** Plugin configuration shape: the PLAIN document, with no live handles in it. */
|
|
32
35
|
export interface ModelParametersConfig {
|
|
33
36
|
/** Master switch: when off, no fills happen (listener is inert). */
|
|
34
37
|
enabled: boolean;
|
|
@@ -49,5 +52,84 @@ export interface ModelParametersConfig {
|
|
|
49
52
|
/** models.dev provider ids treated as first-party vendors for tie-breaking. */
|
|
50
53
|
officialProviders: string[];
|
|
51
54
|
}
|
|
52
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Recursively readonly value a live handle returns — cosmokit's
|
|
57
|
+
* `VolatileSnapshot<T>`, spelled locally for the same reason as
|
|
58
|
+
* {@link LiveSettingsField}.
|
|
59
|
+
*/
|
|
60
|
+
export type LiveSettingsSnapshot<T> = T extends object ? {
|
|
61
|
+
readonly [K in keyof T]: LiveSettingsSnapshot<T[K]>;
|
|
62
|
+
} : T;
|
|
63
|
+
/**
|
|
64
|
+
* One `volatile()` config field as the loader hands it to `apply()`: a live
|
|
65
|
+
* handle, not the value it currently holds. Spelled structurally rather than as
|
|
66
|
+
* cosmokit's `Volatile<T>` on purpose — cosmokit is a transitive dependency
|
|
67
|
+
* here, and importing it would put a second schemastery/cosmokit pair into the
|
|
68
|
+
* emitted declaration.
|
|
69
|
+
*/
|
|
70
|
+
export interface LiveSettingsField<T> {
|
|
71
|
+
get(): LiveSettingsSnapshot<T>;
|
|
72
|
+
}
|
|
73
|
+
/** A hand-written entry config (profile patch / test fixture): every field is optional. */
|
|
74
|
+
export type ModelParametersConfigInput = {
|
|
75
|
+
enabled?: boolean | null;
|
|
76
|
+
ttlDays?: number | null;
|
|
77
|
+
fillName?: boolean | null;
|
|
78
|
+
fillContext?: boolean | null;
|
|
79
|
+
fillMaxTokens?: boolean | null;
|
|
80
|
+
fillReasoning?: boolean | null;
|
|
81
|
+
fillInput?: boolean | null;
|
|
82
|
+
providerMap?: Record<string, string> | null;
|
|
83
|
+
officialProviders?: string[] | null;
|
|
84
|
+
};
|
|
85
|
+
/** The resolved entry config `apply()` receives: every field is a live handle. */
|
|
86
|
+
export type ResolvedModelParametersConfig = {
|
|
87
|
+
enabled: LiveSettingsField<boolean>;
|
|
88
|
+
ttlDays: LiveSettingsField<number>;
|
|
89
|
+
fillName: LiveSettingsField<boolean>;
|
|
90
|
+
fillContext: LiveSettingsField<boolean>;
|
|
91
|
+
fillMaxTokens: LiveSettingsField<boolean>;
|
|
92
|
+
fillReasoning: LiveSettingsField<boolean>;
|
|
93
|
+
fillInput: LiveSettingsField<boolean>;
|
|
94
|
+
providerMap: LiveSettingsField<Record<string, string>>;
|
|
95
|
+
officialProviders: LiveSettingsField<string[]>;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Entry config schema. Deliberately NOT annotated `z<ModelParametersConfig>`:
|
|
99
|
+
* schemastery >= 3.18.3 carries a third `Mode` type parameter, so a
|
|
100
|
+
* `.volatile()` field's OUTPUT type is `Volatile<T>` (a live `{ get() }` cell),
|
|
101
|
+
* and that annotation no longer compiles. The plain document shape stays
|
|
102
|
+
* {@link ModelParametersConfig} and is what {@link readConfig} projects to.
|
|
103
|
+
*
|
|
104
|
+
* The annotation below names the type through the package's own default export
|
|
105
|
+
* (`z<...>`), with locally declared type arguments: the bare inferred type of a
|
|
106
|
+
* volatile schema resolves through a schemastery copy this package does not
|
|
107
|
+
* import, so `tsc -p tsconfig.build.json` (`declaration: true`) could not name
|
|
108
|
+
* it (TS2742) — the emitted declaration stays portable this way.
|
|
109
|
+
*
|
|
110
|
+
* `.volatile()` is REQUIRED on every field the settings page may write, not
|
|
111
|
+
* cosmetic: `dsh-settings` derives `volatileForm(schema)` from this schema, and
|
|
112
|
+
* with no volatile field at all it SKIPS this entry in `describe()` (the
|
|
113
|
+
* settings page disappears) and REJECTS every write as a non-volatile path.
|
|
114
|
+
* The profile entry id (`model-parameters`, see cordis.patch.yml) is the
|
|
115
|
+
* namespace those writes address.
|
|
116
|
+
*/
|
|
117
|
+
export type ModelParametersConfigSchema = z<ModelParametersConfigInput, ResolvedModelParametersConfig>;
|
|
118
|
+
/** The plugin's `Config` schema, exported for the loader and the settings page. */
|
|
119
|
+
export declare const Config: ModelParametersConfigSchema;
|
|
53
120
|
export declare const DEFAULT_CONFIG: ModelParametersConfig;
|
|
121
|
+
/**
|
|
122
|
+
* The entry config as `apply()` receives it. Every field may arrive either as a
|
|
123
|
+
* live `Volatile` cell or as a plain value (a hand-built config object, a
|
|
124
|
+
* profile patch written before the volatile marker existed, or a host that
|
|
125
|
+
* resolves the document without the wrapper).
|
|
126
|
+
*/
|
|
127
|
+
export type ModelParametersLiveConfig = Partial<Record<keyof ModelParametersConfig, unknown>>;
|
|
128
|
+
/**
|
|
129
|
+
* Project the loader entry config into the plain document shape, per field
|
|
130
|
+
* falling back to {@link DEFAULT_CONFIG}. The config is re-read on EVERY call,
|
|
131
|
+
* so settings-page edits (which the loader swaps into the live cells in place)
|
|
132
|
+
* take effect immediately. Container fields are copied so callers can never
|
|
133
|
+
* mutate the live document through a returned reference.
|
|
134
|
+
*/
|
|
135
|
+
export declare function readConfig(config: ModelParametersLiveConfig | undefined): ModelParametersConfig;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fonlan/dsh-model-parameters",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "DSH plugin: when a provider's model sync returns only ids, automatically fill display name, context window, max output tokens, reasoning efforts, and input modalities from the models.dev catalog (cached, TTL-refreshed), and surface a settings page with toggles, refresh, and a fill report.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -69,13 +69,13 @@
|
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
71
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
72
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
73
|
-
"@deepseek-ai/dsh-settings": "^0.1.2-alpha.2",
|
|
74
|
-
"@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.2",
|
|
75
|
-
"@deepseek-ai/dsh-llm-pi-ai": "^0.1.2-alpha.2",
|
|
76
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.2",
|
|
77
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.2-alpha.2",
|
|
78
|
-
"@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.2",
|
|
72
|
+
"@deepseek-ai/schemastery": "^3.18.4",
|
|
73
|
+
"@deepseek-ai/dsh-settings": "^0.1.2-alpha.2 || ^0.2.0-rc.1",
|
|
74
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.2 || ^0.2.0-rc.1",
|
|
75
|
+
"@deepseek-ai/dsh-llm-pi-ai": "^0.1.2-alpha.2 || ^0.2.0-rc.1",
|
|
76
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.2 || ^0.2.0-rc.1",
|
|
77
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.2-alpha.2 || ^0.2.0-rc.1",
|
|
78
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.2 || ^0.2.0-rc.1",
|
|
79
79
|
"react": "^18.2.0",
|
|
80
80
|
"react-dom": "^18.2.0"
|
|
81
81
|
},
|
|
@@ -86,13 +86,13 @@
|
|
|
86
86
|
"tsdown": "^0.22.14",
|
|
87
87
|
"typescript": "^5.9.0",
|
|
88
88
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
89
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
90
|
-
"@deepseek-ai/dsh-settings": "
|
|
91
|
-
"@deepseek-ai/dsh-host-webserver": "
|
|
92
|
-
"@deepseek-ai/dsh-llm-pi-ai": "
|
|
93
|
-
"@deepseek-ai/dsh-client-locale": "
|
|
94
|
-
"@deepseek-ai/dsh-client-ui-slots": "
|
|
95
|
-
"@deepseek-ai/dsh-client-ui-settings": "
|
|
89
|
+
"@deepseek-ai/schemastery": "^3.18.4",
|
|
90
|
+
"@deepseek-ai/dsh-settings": "0.2.0-rc.1",
|
|
91
|
+
"@deepseek-ai/dsh-host-webserver": "0.2.0-rc.1",
|
|
92
|
+
"@deepseek-ai/dsh-llm-pi-ai": "0.2.0-rc.1",
|
|
93
|
+
"@deepseek-ai/dsh-client-locale": "0.2.0-rc.1",
|
|
94
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.1",
|
|
95
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.2.0-rc.1"
|
|
96
96
|
},
|
|
97
97
|
"packageManager": "pnpm@11.21.0"
|
|
98
98
|
}
|
package/src/client/index.tsx
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fonlan/dsh-model-parameters client half: the plugin's own Settings
|
|
2
|
+
* @fonlan/dsh-model-parameters client half: the plugin's own Settings section.
|
|
3
3
|
*
|
|
4
|
-
* Registers into the
|
|
5
|
-
* settings
|
|
6
|
-
*
|
|
7
|
-
* exactly like @fonlan/dsh-web-auth. The card draws its own expandable chrome
|
|
8
|
-
* (external plugins cannot import the built-in PluginCard) with styles aligned
|
|
9
|
-
* to the built-in plugin cards.
|
|
4
|
+
* Registers into the settings.section slot, so the page owns one entry in the
|
|
5
|
+
* settings sidebar and renders in the panel's content column. The page itself
|
|
6
|
+
* lives in ./settings-section.
|
|
10
7
|
*
|
|
11
8
|
* Deps resolved from the browser module table: react only. The slots service
|
|
12
9
|
* is reached through the cordis context; types come from devDependencies and
|
|
@@ -17,16 +14,18 @@ type ClientContext = Context
|
|
|
17
14
|
import type {} from '@deepseek-ai/dsh-client-locale/client'
|
|
18
15
|
import type {} from '@deepseek-ai/dsh-client-ui-slots'
|
|
19
16
|
import { LOCALE_NS, zh, en } from './locales'
|
|
20
|
-
import {
|
|
17
|
+
import { makeSettingsSection } from './settings-section'
|
|
21
18
|
|
|
22
|
-
/** The settings
|
|
23
|
-
const
|
|
19
|
+
/** The settings sidebar entry this page owns (must stay stable). */
|
|
20
|
+
const SECTION_ID = 'model-parameters'
|
|
24
21
|
|
|
25
22
|
/** The slots service face (subset of @deepseek-ai/dsh-client-ui-slots). */
|
|
26
23
|
interface SlotEntry {
|
|
27
24
|
name: string
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
id?: string
|
|
26
|
+
order?: number
|
|
27
|
+
label?: () => string
|
|
28
|
+
locale?: string
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
interface Slots {
|
|
@@ -44,12 +43,16 @@ export function apply(ctx: ClientContext): void {
|
|
|
44
43
|
return () => off()
|
|
45
44
|
}, 'model-parameters: dictionaries')
|
|
46
45
|
|
|
47
|
-
const
|
|
46
|
+
const t = ctx.locale.bind(LOCALE_NS) as unknown as (key: string) => string
|
|
47
|
+
const Section = makeSettingsSection(ctx)
|
|
48
48
|
const slots = (ctx as unknown as { slots: Slots }).slots
|
|
49
|
-
slots.inject('settings.
|
|
49
|
+
slots.inject('settings.section', () =>
|
|
50
50
|
slots.register({
|
|
51
|
-
name: 'settings.
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
name: 'settings.section',
|
|
52
|
+
id: SECTION_ID,
|
|
53
|
+
order: 320,
|
|
54
|
+
label: () => t('settingsTitle'),
|
|
55
|
+
locale: LOCALE_NS,
|
|
56
|
+
}, Section),
|
|
54
57
|
)
|
|
55
58
|
}
|
package/src/client/locales.ts
CHANGED
|
@@ -6,9 +6,7 @@ export const LOCALE_NS = 'model-parameters'
|
|
|
6
6
|
|
|
7
7
|
export const zh = {
|
|
8
8
|
settingsTitle: '模型参数补全',
|
|
9
|
-
|
|
10
|
-
collapse: '收起',
|
|
11
|
-
expand: '展开',
|
|
9
|
+
sectionSub: '补全字段、目录刷新、Provider 映射与补全报告。',
|
|
12
10
|
master: '启用自动补全',
|
|
13
11
|
masterDesc: '关闭后不再自动补全任何字段(已补全的值保留)。',
|
|
14
12
|
fields: '补全字段',
|
|
@@ -53,9 +51,7 @@ export const zh = {
|
|
|
53
51
|
|
|
54
52
|
export const en: Record<keyof typeof zh, string> = {
|
|
55
53
|
settingsTitle: 'Model Parameters',
|
|
56
|
-
|
|
57
|
-
collapse: 'Collapse',
|
|
58
|
-
expand: 'Expand',
|
|
54
|
+
sectionSub: 'Field toggles, catalog refresh, provider mapping and the fill report.',
|
|
59
55
|
master: 'Enable auto-fill',
|
|
60
56
|
masterDesc: 'When off, no fields are filled (already-filled values are kept).',
|
|
61
57
|
fields: 'Fields to fill',
|