dsh-autotier 0.2.3 → 0.2.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/AGENTS.md +50 -21
- package/CHANGELOG.md +43 -0
- package/README-es.md +95 -13
- package/README-hi.md +94 -14
- package/README-pt.md +97 -16
- package/README-zh.md +84 -11
- package/README.md +111 -18
- package/cordis.patch.yml +2 -2
- package/lib/client.js +72 -40
- package/lib/client.js.map +1 -1
- package/lib/index.js +233 -66
- package/lib/typert.host.js +1 -1
- package/lib/types/client/TierSettingsCard.d.ts +14 -12
- package/lib/types/client/TierSettingsCard.d.ts.map +1 -1
- package/lib/types/client/index.d.ts +5 -4
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/remote.d.ts +12 -12
- package/lib/types/config.d.ts +20 -9
- package/lib/types/config.d.ts.map +1 -1
- package/lib/types/index.d.ts +11 -8
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/judge.d.ts +17 -0
- package/lib/types/judge.d.ts.map +1 -1
- package/lib/types/preflight.d.ts +21 -1
- package/lib/types/preflight.d.ts.map +1 -1
- package/lib/types/routing.d.ts.map +1 -1
- package/lib/types/schema.d.ts +44 -2
- package/lib/types/schema.d.ts.map +1 -1
- package/lib/types/selection.d.ts +36 -10
- package/lib/types/selection.d.ts.map +1 -1
- package/lib/types/service.d.ts +16 -9
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/settings.d.ts +44 -0
- package/lib/types/settings.d.ts.map +1 -0
- package/lib/types/typert.host.d.ts +12 -12
- package/lib/types/wire.d.ts +24 -24
- package/lib/types/wire.d.ts.map +1 -1
- package/lib/{wire-BpKSOHgo.js → wire-BxsY8BiN.js} +22 -20
- package/package.json +58 -52
- package/src/client/TierSettingsCard.tsx +25 -12
- package/src/client/index.ts +29 -16
- package/src/config.ts +70 -12
- package/src/index.ts +23 -19
- package/src/judge.ts +30 -2
- package/src/preflight.ts +68 -1
- package/src/routing.ts +11 -0
- package/src/schema.ts +48 -9
- package/src/selection.ts +54 -20
- package/src/service.ts +20 -18
- package/src/settings.ts +80 -0
- package/src/wire.ts +19 -20
package/lib/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { a as TIER_INVOCATIONS, c as TIER_SESSION_VIEW_SCHEMA, d as TIER_STATUS_SCHEMA, f as COST_MODES, g as TIER_IDS, h as SCENARIOS, i as TIER_CATALOG_SCHEMA, l as TIER_SET_MODE_DESCRIPTOR, m as ROUTING_MODES, n as TIER_AGENT_ID_SCHEMA, o as TIER_MODE_SCHEMA, p as EFFORT_IDS, r as TIER_CATALOG_DESCRIPTOR, s as TIER_NAMESPACE, t as ROUTE_SOURCES, u as TIER_STATUS_DESCRIPTOR } from "./wire-
|
|
1
|
+
import { a as TIER_INVOCATIONS, c as TIER_SESSION_VIEW_SCHEMA, d as TIER_STATUS_SCHEMA, f as COST_MODES, g as TIER_IDS, h as SCENARIOS, i as TIER_CATALOG_SCHEMA, l as TIER_SET_MODE_DESCRIPTOR, m as ROUTING_MODES, n as TIER_AGENT_ID_SCHEMA, o as TIER_MODE_SCHEMA, p as EFFORT_IDS, r as TIER_CATALOG_DESCRIPTOR, s as TIER_NAMESPACE, t as ROUTE_SOURCES, u as TIER_STATUS_DESCRIPTOR } from "./wire-BxsY8BiN.js";
|
|
2
|
+
import { isVolatile } from "@deepseek-ai/cosmokit";
|
|
2
3
|
import z from "@deepseek-ai/schemastery";
|
|
3
|
-
import { BlockAssembler, createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
4
|
+
import { BlockAssembler, boundContextSummary, createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
4
5
|
import { Service } from "@deepseek-ai/cordis";
|
|
5
6
|
import { TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
|
|
6
7
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
@@ -403,15 +404,6 @@ function registerTierCommand(ctx, service, states) {
|
|
|
403
404
|
}
|
|
404
405
|
//#endregion
|
|
405
406
|
//#region src/schema.ts
|
|
406
|
-
/**
|
|
407
|
-
* The raw (possibly partial) configuration surface of dsh-autotier: the
|
|
408
|
-
* Schemastery schema the Loader validates and the settings UI renders, plus the
|
|
409
|
-
* interfaces it resolves to. The judgement that turns a raw config into a
|
|
410
|
-
* resolved one lives in `config.ts`, so this module stays free of executable
|
|
411
|
-
* logic (a schema module must not mix function values into its declarations).
|
|
412
|
-
*
|
|
413
|
-
* @module dsh-autotier/schema
|
|
414
|
-
*/
|
|
415
407
|
/** The default strong tier: the catalog's quality-critical model at high effort. */
|
|
416
408
|
const DEFAULT_STRONG = {
|
|
417
409
|
provider: "deepseek-official",
|
|
@@ -422,14 +414,14 @@ const DEFAULT_STRONG = {
|
|
|
422
414
|
/** The default cheap tier: the catalog's routine/parallel model at low effort. */
|
|
423
415
|
const DEFAULT_CHEAP = {
|
|
424
416
|
provider: "deepseek-official",
|
|
425
|
-
model: "deepseek-
|
|
417
|
+
model: "deepseek-flash",
|
|
426
418
|
effort: "low",
|
|
427
419
|
followSession: true
|
|
428
420
|
};
|
|
429
421
|
/** The default vision landing: the catalog's only image-capable model. */
|
|
430
422
|
const DEFAULT_VISION = {
|
|
431
423
|
provider: "deepseek-official",
|
|
432
|
-
model: "deepseek-
|
|
424
|
+
model: "deepseek-flash"
|
|
433
425
|
};
|
|
434
426
|
/**
|
|
435
427
|
* One tier schema per tier, so a partially-specified tier gets the same
|
|
@@ -457,7 +449,15 @@ const cheapTier = z.object({
|
|
|
457
449
|
model: z.string().default("")
|
|
458
450
|
})).default([])
|
|
459
451
|
});
|
|
460
|
-
/**
|
|
452
|
+
/**
|
|
453
|
+
* Schemastery schema: the loader validates and fills defaults before `apply`.
|
|
454
|
+
*
|
|
455
|
+
* Each top-level section is `.volatile()` so a host settings-form write reaches
|
|
456
|
+
* a running plugin through `loader/volatile-update`. A section that owns no
|
|
457
|
+
* live-switchable field (`routingMode`) stays plain: `.volatile()` is an opt-in
|
|
458
|
+
* that adds a form-editable surface, and inventing one for a field the plugin
|
|
459
|
+
* already switches per session would give the same behaviour two owners.
|
|
460
|
+
*/
|
|
461
461
|
const Config = z.object({
|
|
462
462
|
tiers: z.object({
|
|
463
463
|
strong: strongTier.default({
|
|
@@ -470,7 +470,7 @@ const Config = z.object({
|
|
|
470
470
|
}),
|
|
471
471
|
vision: z.object({
|
|
472
472
|
provider: z.string().default("deepseek-official"),
|
|
473
|
-
model: z.string().default("deepseek-
|
|
473
|
+
model: z.string().default("deepseek-flash")
|
|
474
474
|
}).default({ ...DEFAULT_VISION })
|
|
475
475
|
}).default({
|
|
476
476
|
strong: {
|
|
@@ -482,7 +482,7 @@ const Config = z.object({
|
|
|
482
482
|
fallback: []
|
|
483
483
|
},
|
|
484
484
|
vision: { ...DEFAULT_VISION }
|
|
485
|
-
}),
|
|
485
|
+
}).volatile(),
|
|
486
486
|
intent: z.object({
|
|
487
487
|
ruleThreshold: z.number().min(1e-6).max(1).default(.7),
|
|
488
488
|
attemptBand: z.object({
|
|
@@ -581,7 +581,7 @@ const Config = z.object({
|
|
|
581
581
|
multimodal: true
|
|
582
582
|
},
|
|
583
583
|
costMode: "balanced"
|
|
584
|
-
}),
|
|
584
|
+
}).volatile(),
|
|
585
585
|
guard: z.object({
|
|
586
586
|
enabled: z.boolean().default(true),
|
|
587
587
|
tiers: z.array(z.union(["cheap"])).default(["cheap"]),
|
|
@@ -604,7 +604,7 @@ const Config = z.object({
|
|
|
604
604
|
".github/workflows"
|
|
605
605
|
],
|
|
606
606
|
interopDefend: "auto"
|
|
607
|
-
}),
|
|
607
|
+
}).volatile(),
|
|
608
608
|
escalation: z.object({
|
|
609
609
|
threshold: z.number().step(1).min(1).max(100).default(2),
|
|
610
610
|
windowMs: z.number().min(1).max(864e5).default(6e4),
|
|
@@ -617,7 +617,7 @@ const Config = z.object({
|
|
|
617
617
|
ttlMs: 18e4,
|
|
618
618
|
fallbackTtlMs: 3e5,
|
|
619
619
|
signature: true
|
|
620
|
-
}),
|
|
620
|
+
}).volatile(),
|
|
621
621
|
routingMode: z.union([...ROUTING_MODES]).default("auto")
|
|
622
622
|
});
|
|
623
623
|
//#endregion
|
|
@@ -630,6 +630,47 @@ const Config = z.object({
|
|
|
630
630
|
*
|
|
631
631
|
* @module dsh-autotier/config
|
|
632
632
|
*/
|
|
633
|
+
/**
|
|
634
|
+
* Read one config field that may arrive as a live reference or as plain data.
|
|
635
|
+
* The Loader hands `apply` a `VolatileConfig` (every section is a reference);
|
|
636
|
+
* a programmatic mount, a test, or `scripts/` hands plain data. Both faces feed
|
|
637
|
+
* the same resolver, so neither can drift from the other.
|
|
638
|
+
*
|
|
639
|
+
* `Volatile.get()` answers a deeply-readonly snapshot. The resolvers below read
|
|
640
|
+
* these values and then produce their own frozen output, so the read-only view
|
|
641
|
+
* is exactly the contract wanted; the cast re-widens it to the mutable field
|
|
642
|
+
* type so those signatures do not have to carry `readonly` everywhere.
|
|
643
|
+
*
|
|
644
|
+
* @param value - the field as handed to the plugin.
|
|
645
|
+
* @returns the current snapshot value.
|
|
646
|
+
*/
|
|
647
|
+
function unwrap(value) {
|
|
648
|
+
return isVolatile(value) ? value.get() : value;
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Read the plain-data config out of either accepted face.
|
|
652
|
+
*
|
|
653
|
+
* `exactOptionalPropertyTypes` is on, so an absent section is omitted rather
|
|
654
|
+
* than set to `undefined`: the resolvers below treat "key absent" and "key
|
|
655
|
+
* undefined" alike, but only the omitted form is assignable to `Config`.
|
|
656
|
+
*
|
|
657
|
+
* @param raw - the plugin configuration as received.
|
|
658
|
+
* @returns the plain-data config every resolver below reads.
|
|
659
|
+
*/
|
|
660
|
+
function plain(raw) {
|
|
661
|
+
if (raw === void 0) return {};
|
|
662
|
+
const tiers = unwrap(raw.tiers);
|
|
663
|
+
const intent = unwrap(raw.intent);
|
|
664
|
+
const guard = unwrap(raw.guard);
|
|
665
|
+
const escalation = unwrap(raw.escalation);
|
|
666
|
+
return {
|
|
667
|
+
...tiers === void 0 ? {} : { tiers },
|
|
668
|
+
...intent === void 0 ? {} : { intent },
|
|
669
|
+
...guard === void 0 ? {} : { guard },
|
|
670
|
+
...escalation === void 0 ? {} : { escalation },
|
|
671
|
+
...raw.routingMode === void 0 ? {} : { routingMode: raw.routingMode }
|
|
672
|
+
};
|
|
673
|
+
}
|
|
633
674
|
/** Throw the standard fail-loud config error for one invalid field. */
|
|
634
675
|
function invalid(field, detail) {
|
|
635
676
|
throw new Error(`dsh-autotier: config.${field} ${detail}`);
|
|
@@ -815,12 +856,23 @@ function effectiveLanding(tier) {
|
|
|
815
856
|
* Resolve raw config to the frozen runtime policy, re-judging every default,
|
|
816
857
|
* bound and cross-field requirement.
|
|
817
858
|
*
|
|
818
|
-
*
|
|
859
|
+
* The cross-field judgement is the whole reason this module exists, and it is
|
|
860
|
+
* NOT expressed in the Schemastery schema: the host validates a form write
|
|
861
|
+
* against the schema alone, so a violation this function catches (a strong and
|
|
862
|
+
* a cheap tier landing on the same route, `hysteresis.toCheap >= toStrong`, a
|
|
863
|
+
* rule with neither a pattern nor a tool) would otherwise persist and only fail
|
|
864
|
+
* later. Every actor that can change the configuration therefore routes through
|
|
865
|
+
* here — the Loader at mount, and `settings.ts` on each `loader/volatile-update`
|
|
866
|
+
* — and refuses the change instead of storing something unroutable.
|
|
867
|
+
*
|
|
868
|
+
* @param raw - raw loader config; `undefined` for a bare row. Accepts both the
|
|
869
|
+
* Loader's `VolatileConfig` face and the plain-data `Config` face.
|
|
819
870
|
* @returns the frozen resolved config.
|
|
820
871
|
* @throws {Error} when a value is out of bounds or a cross-field requirement fails.
|
|
821
872
|
*/
|
|
822
873
|
function resolveConfig(raw) {
|
|
823
|
-
const
|
|
874
|
+
const source = plain(raw);
|
|
875
|
+
const tiers = source.tiers ?? {};
|
|
824
876
|
const strong = resolveTier("strong", tiers.strong, DEFAULT_STRONG);
|
|
825
877
|
const cheap = resolveTier("cheap", tiers.cheap, DEFAULT_CHEAP);
|
|
826
878
|
const strongTriple = effectiveLanding(strong);
|
|
@@ -834,20 +886,21 @@ function resolveConfig(raw) {
|
|
|
834
886
|
model: text("tiers.vision.model", tiers.vision?.model, DEFAULT_VISION.model)
|
|
835
887
|
}
|
|
836
888
|
},
|
|
837
|
-
intent: resolveIntent(
|
|
838
|
-
guard: resolveGuard(
|
|
839
|
-
escalation: resolveEscalation(
|
|
840
|
-
routingMode: member("routingMode",
|
|
889
|
+
intent: resolveIntent(source.intent),
|
|
890
|
+
guard: resolveGuard(source.guard),
|
|
891
|
+
escalation: resolveEscalation(source.escalation),
|
|
892
|
+
routingMode: member("routingMode", source.routingMode, "auto", ROUTING_MODES)
|
|
841
893
|
});
|
|
842
894
|
}
|
|
843
895
|
/**
|
|
844
|
-
* Judge a configuration without keeping the resolved value.
|
|
845
|
-
* save-time hook the `autotier` settings namespace registers, so a user write
|
|
846
|
-
* that violates a cross-field requirement is refused at the write instead of
|
|
847
|
-
* silently disabling the plugin.
|
|
896
|
+
* Judge a configuration without keeping the resolved value.
|
|
848
897
|
*
|
|
849
898
|
* @param value - the configuration to judge.
|
|
850
899
|
* @throws {Error} when the configuration is invalid.
|
|
900
|
+
* @deprecated The host no longer offers a `settings.register` validate hook, so
|
|
901
|
+
* this is no longer wired as a save-time gate. {@link resolveConfig} is the
|
|
902
|
+
* single judge and is called directly at mount and on every live update; this
|
|
903
|
+
* alias remains exported because it is part of the plugin's published surface.
|
|
851
904
|
*/
|
|
852
905
|
function validateConfig(value) {
|
|
853
906
|
resolveConfig(value);
|
|
@@ -1987,8 +2040,9 @@ async function runJudge(ctx, config, text, signal) {
|
|
|
1987
2040
|
text: prompt
|
|
1988
2041
|
}],
|
|
1989
2042
|
source: {
|
|
1990
|
-
kind: "
|
|
1991
|
-
|
|
2043
|
+
kind: "dsh-autotier",
|
|
2044
|
+
form: "notice",
|
|
2045
|
+
summary: boundContextSummary(`autotier judge: classify intent (${route.provider}/${route.model})`)
|
|
1992
2046
|
}
|
|
1993
2047
|
})],
|
|
1994
2048
|
temperature: config.intent.judge.temperature,
|
|
@@ -2103,6 +2157,57 @@ var RoutePreflight = class {
|
|
|
2103
2157
|
return safe;
|
|
2104
2158
|
}
|
|
2105
2159
|
};
|
|
2160
|
+
/**
|
|
2161
|
+
* Mount-time default-model catalogue membership check.
|
|
2162
|
+
*
|
|
2163
|
+
* The 0.1.6 line removed the deepseek v4-flash* catalogue ids and introduced
|
|
2164
|
+
* `deepseek-flash` (image-capable). On that catalogue generation a default
|
|
2165
|
+
* tier id that is not in the host catalogue must fail the mount loudly —
|
|
2166
|
+
* silently degrading to a text-only passthrough is exactly the N7 regression
|
|
2167
|
+
* this check guards against.
|
|
2168
|
+
*
|
|
2169
|
+
* Generation gate: hosts whose catalogue predates the new vocabulary (e.g.
|
|
2170
|
+
* `0.1.2-rc.1`, where none of the default ids exist) keep the accepted M2
|
|
2171
|
+
* degradation (unlisted id ⇒ text-only) and skip the check with one
|
|
2172
|
+
* documented log line, so mounting on the old compat rows is preserved.
|
|
2173
|
+
*
|
|
2174
|
+
* @param ctx - the plugin context.
|
|
2175
|
+
* @param tiers - the resolved tier landings to verify.
|
|
2176
|
+
* @throws {Error} when the catalogue is the new generation and a default
|
|
2177
|
+
* model id is not present in it.
|
|
2178
|
+
*/
|
|
2179
|
+
async function assertTierDefaultsInCatalog(ctx, tiers) {
|
|
2180
|
+
const provider = "deepseek-official";
|
|
2181
|
+
const llm = ctx.llm;
|
|
2182
|
+
if (llm === void 0 || typeof llm.listProviders !== "function" || typeof llm.resolveModelInfo !== "function") {
|
|
2183
|
+
ctx.logger.info("dsh-autotier: the llm catalogue seam is unavailable; skipping the default-model catalogue check");
|
|
2184
|
+
return;
|
|
2185
|
+
}
|
|
2186
|
+
if (!llm.listProviders().some((entry) => entry.id === provider)) {
|
|
2187
|
+
ctx.logger.info("dsh-autotier: provider \"%s\" is not registered; skipping the default-model catalogue check", provider);
|
|
2188
|
+
return;
|
|
2189
|
+
}
|
|
2190
|
+
const defaults = [
|
|
2191
|
+
["strong", tiers.strong.model],
|
|
2192
|
+
["cheap", tiers.cheap.model],
|
|
2193
|
+
["vision", tiers.vision.model]
|
|
2194
|
+
];
|
|
2195
|
+
let newGeneration = false;
|
|
2196
|
+
for (const [, model] of defaults) try {
|
|
2197
|
+
await llm.resolveModelInfo(provider, model);
|
|
2198
|
+
newGeneration = true;
|
|
2199
|
+
break;
|
|
2200
|
+
} catch {}
|
|
2201
|
+
if (!newGeneration) {
|
|
2202
|
+
ctx.logger.info("dsh-autotier: the host catalogue predates the deepseek-flash vocabulary; skipping the default-model catalogue check (unlisted ids degrade to text-only on this host)");
|
|
2203
|
+
return;
|
|
2204
|
+
}
|
|
2205
|
+
for (const [tier, model] of defaults) try {
|
|
2206
|
+
await llm.resolveModelInfo(provider, model);
|
|
2207
|
+
} catch {
|
|
2208
|
+
throw new Error(`dsh-autotier: the default ${tier} model "${provider}/${model}" is not in the host catalogue; refusing to mount with a silent text-only landing`);
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2106
2211
|
//#endregion
|
|
2107
2212
|
//#region src/selection.ts
|
|
2108
2213
|
/** Key of one selection, for comparing our own writes with external ones. */
|
|
@@ -2124,28 +2229,47 @@ var SelectionSync = class {
|
|
|
2124
2229
|
constructor(options) {
|
|
2125
2230
|
this.ctx = options.ctx;
|
|
2126
2231
|
this.states = options.states;
|
|
2127
|
-
this.ctx.on("settings/updated", (ns, next) => {
|
|
2128
|
-
if (String(ns) !== "agent-default-model") return;
|
|
2129
|
-
const key = selectionKey(next);
|
|
2130
|
-
if (this.selfWrite === key) {
|
|
2131
|
-
this.selfWrite = void 0;
|
|
2132
|
-
return;
|
|
2133
|
-
}
|
|
2134
|
-
this.delegateLiveSessions(key);
|
|
2135
|
-
});
|
|
2136
2232
|
}
|
|
2137
2233
|
/** The optional default-model service. */
|
|
2138
2234
|
get defaultModel() {
|
|
2139
2235
|
return this.ctx.get("agentDefaultModel");
|
|
2140
2236
|
}
|
|
2141
2237
|
/**
|
|
2142
|
-
*
|
|
2143
|
-
*
|
|
2238
|
+
* Read the documented selection and delegate every live session when it is
|
|
2239
|
+
* no longer the value this module wrote.
|
|
2240
|
+
*
|
|
2241
|
+
* Before this module's first write nothing is delegated, whatever the
|
|
2242
|
+
* document holds: at that point "not ours" describes the composition default
|
|
2243
|
+
* and any selection the user made before the plugin ever routed, and neither
|
|
2244
|
+
* is an override of a routing decision. Once this module owns a write, any
|
|
2245
|
+
* divergence is by construction somebody else's edit.
|
|
2246
|
+
*/
|
|
2247
|
+
observeExternalSelection() {
|
|
2248
|
+
if (this.selfWrite === void 0) return;
|
|
2249
|
+
const service = this.defaultModel;
|
|
2250
|
+
if (service === void 0) return;
|
|
2251
|
+
let current;
|
|
2252
|
+
try {
|
|
2253
|
+
current = service.currentSelection();
|
|
2254
|
+
} catch (error) {
|
|
2255
|
+
this.ctx.logger.debug("dsh-autotier: could not read the default-model selection: %o", error);
|
|
2256
|
+
return;
|
|
2257
|
+
}
|
|
2258
|
+
const key = selectionKey(current);
|
|
2259
|
+
if (key === this.selfWrite) return;
|
|
2260
|
+
this.delegateLiveSessions(key);
|
|
2261
|
+
}
|
|
2262
|
+
/**
|
|
2263
|
+
* Mirror one applied landing into the default-model document. An external
|
|
2264
|
+
* change to that document is honoured first, so routing never overwrites the
|
|
2265
|
+
* model the user picked. Repeated landings with the same value are skipped,
|
|
2266
|
+
* so a long cheap run writes once.
|
|
2144
2267
|
* @param route - the landing actually applied.
|
|
2145
2268
|
*/
|
|
2146
2269
|
noteRoute(route) {
|
|
2147
2270
|
const service = this.defaultModel;
|
|
2148
2271
|
if (service === void 0) return;
|
|
2272
|
+
this.observeExternalSelection();
|
|
2149
2273
|
const selection = {
|
|
2150
2274
|
provider: route.provider,
|
|
2151
2275
|
model: route.model,
|
|
@@ -2229,6 +2353,13 @@ var AutotierRouter = class {
|
|
|
2229
2353
|
this.ctx.on("agent/error", (payload) => this.onAgentError(payload.agent, payload.error));
|
|
2230
2354
|
this.ctx.on("agent/request-error", (payload, next) => this.onRequestError(payload.agent, payload.provider, payload.failure, next));
|
|
2231
2355
|
this.ctx.on("session/event", (session, event) => this.onSessionEvent(session, event));
|
|
2356
|
+
this.ctx.on("agent/created", (payload) => {
|
|
2357
|
+
try {
|
|
2358
|
+
this.states.for(payload.agent);
|
|
2359
|
+
} catch (error) {
|
|
2360
|
+
this.ctx.logger.warn("dsh-autotier: could not backfill routing state for the created agent (%o)", error);
|
|
2361
|
+
}
|
|
2362
|
+
});
|
|
2232
2363
|
this.ctx.effect(() => () => {
|
|
2233
2364
|
this.disposed = true;
|
|
2234
2365
|
this.lifetime.abort();
|
|
@@ -2626,25 +2757,33 @@ function routeOf(tier) {
|
|
|
2626
2757
|
* plugin unloading removes the service with every listener it owns.
|
|
2627
2758
|
*/
|
|
2628
2759
|
var AutotierService = class extends Service {
|
|
2629
|
-
scope;
|
|
2630
2760
|
posteriorTable = new PosteriorTable();
|
|
2631
2761
|
resolved;
|
|
2632
2762
|
compiled;
|
|
2633
2763
|
/**
|
|
2634
|
-
* Register the service as `ctx.autotier
|
|
2635
|
-
* namespace.
|
|
2764
|
+
* Register the service as `ctx.autotier`.
|
|
2636
2765
|
* @param ctx - the owning plugin context.
|
|
2637
|
-
* @param options - the
|
|
2766
|
+
* @param options - the mount-time configuration.
|
|
2638
2767
|
*/
|
|
2639
2768
|
constructor(ctx, options) {
|
|
2640
2769
|
super(ctx, "autotier");
|
|
2641
|
-
this.scope = options.scope;
|
|
2642
2770
|
this.resolved = options.config;
|
|
2643
2771
|
this.compiled = compileRules(options.config.intent.rules);
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2772
|
+
}
|
|
2773
|
+
/**
|
|
2774
|
+
* Adopt a configuration a live settings write produced. The caller has
|
|
2775
|
+
* already judged it through {@link resolveConfig}, so this only swaps the
|
|
2776
|
+
* policy and recompiles the rule table.
|
|
2777
|
+
*
|
|
2778
|
+
* The whole policy is replaced, never merged: a settings form submits the
|
|
2779
|
+
* complete Config, so a field the user cleared must fall back to its schema
|
|
2780
|
+
* default rather than keep the previous live value.
|
|
2781
|
+
*
|
|
2782
|
+
* @param config - the newly resolved configuration.
|
|
2783
|
+
*/
|
|
2784
|
+
reconfigure(config) {
|
|
2785
|
+
this.resolved = config;
|
|
2786
|
+
this.compiled = compileRules(config.intent.rules);
|
|
2648
2787
|
}
|
|
2649
2788
|
/** The live resolved configuration. */
|
|
2650
2789
|
config() {
|
|
@@ -2712,6 +2851,34 @@ var AutotierService = class extends Service {
|
|
|
2712
2851
|
}
|
|
2713
2852
|
};
|
|
2714
2853
|
//#endregion
|
|
2854
|
+
//#region src/settings.ts
|
|
2855
|
+
/**
|
|
2856
|
+
* Claim the settings page policy and follow live configuration edits.
|
|
2857
|
+
*
|
|
2858
|
+
* The page-policy claim is best-effort: `settings` is a hard inject, but a host
|
|
2859
|
+
* line that composes the plugin without the settings service must still route,
|
|
2860
|
+
* so a missing or differently-shaped service degrades to "no page" instead of
|
|
2861
|
+
* failing the mount.
|
|
2862
|
+
*
|
|
2863
|
+
* @param options - the plugin context, the volatile row config and the service.
|
|
2864
|
+
*/
|
|
2865
|
+
function bindSettings(options) {
|
|
2866
|
+
const { ctx, config, service } = options;
|
|
2867
|
+
const settings = ctx.get("settings");
|
|
2868
|
+
if (settings !== void 0 && typeof settings.configure === "function") ctx.effect(() => settings.configure({ auto: false }, ctx.fiber));
|
|
2869
|
+
ctx.on("loader/volatile-update", () => {
|
|
2870
|
+
let next;
|
|
2871
|
+
try {
|
|
2872
|
+
next = resolveConfig(config);
|
|
2873
|
+
} catch (error) {
|
|
2874
|
+
ctx.logger.warn("dsh-autotier: keeping the last good routing policy; the updated configuration was refused: %s", error instanceof Error ? error.message : String(error));
|
|
2875
|
+
return;
|
|
2876
|
+
}
|
|
2877
|
+
service.reconfigure(next);
|
|
2878
|
+
ctx.logger.info("dsh-autotier: routing policy reloaded from a live configuration update");
|
|
2879
|
+
});
|
|
2880
|
+
}
|
|
2881
|
+
//#endregion
|
|
2715
2882
|
//#region src/state.ts
|
|
2716
2883
|
/** The projection key autotier owns. */
|
|
2717
2884
|
const TIER_PROJECTION_KEY = "autotier";
|
|
@@ -3006,25 +3173,25 @@ const inject = [
|
|
|
3006
3173
|
"sessions"
|
|
3007
3174
|
];
|
|
3008
3175
|
/**
|
|
3009
|
-
* Mount the plugin: judge the configuration,
|
|
3010
|
-
*
|
|
3011
|
-
*
|
|
3176
|
+
* Mount the plugin: judge the configuration, claim the settings page policy,
|
|
3177
|
+
* publish the `ctx.autotier` service, and wire the routing listeners, the
|
|
3178
|
+
* `/tier` command and the two read-only tools.
|
|
3012
3179
|
*
|
|
3013
3180
|
* @param ctx - the plugin context.
|
|
3014
|
-
* @param config - the
|
|
3015
|
-
*
|
|
3181
|
+
* @param config - the Loader's volatile row configuration. Also accepts the
|
|
3182
|
+
* plain-data `AutotierConfig` face, so a programmatic mount or a test can
|
|
3183
|
+
* pass literal values.
|
|
3184
|
+
* @throws {Error} when the configuration fails the cross-field judgement, or
|
|
3185
|
+
* when a default tier model id is absent from a new-generation host catalogue.
|
|
3016
3186
|
*/
|
|
3017
|
-
function apply(ctx, config = {}) {
|
|
3187
|
+
async function apply(ctx, config = {}) {
|
|
3018
3188
|
const resolved = resolveConfig(config);
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
}
|
|
3026
|
-
}),
|
|
3027
|
-
config: resolved
|
|
3189
|
+
await assertTierDefaultsInCatalog(ctx, resolved.tiers);
|
|
3190
|
+
const service = new AutotierService(ctx, { config: resolved });
|
|
3191
|
+
bindSettings({
|
|
3192
|
+
ctx,
|
|
3193
|
+
config,
|
|
3194
|
+
service
|
|
3028
3195
|
});
|
|
3029
3196
|
registerTierProjection(ctx);
|
|
3030
3197
|
const states = new AgentStateStore();
|
package/lib/typert.host.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The autotier
|
|
3
|
-
* selector, the read-only tier landings, the live session
|
|
4
|
-
* catalog dropdown fed by `tier/catalog`.
|
|
2
|
+
* The autotier card on the Plugins page (`plugins.item`, id `autotier`): the
|
|
3
|
+
* session routing-mode selector, the read-only tier landings, the live session
|
|
4
|
+
* state, and the model catalog dropdown fed by `tier/catalog`.
|
|
5
5
|
*
|
|
6
|
-
* The
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* The Plugins page renders it in two views: `summary` (the one-liner under the
|
|
7
|
+
* card title) and `page` (the full panel). The selector writes the *session*
|
|
8
|
+
* override for the session currently open in the shell (resolved by the client
|
|
9
|
+
* entry through the sessions store face), so it stays non-persistent exactly
|
|
10
|
+
* like the pill and `/tier`. When no session is open the host answers with an
|
|
11
|
+
* empty session view and the selector is disabled with an explanation instead
|
|
12
|
+
* of failing silently.
|
|
11
13
|
*
|
|
12
14
|
* @module dsh-autotier/client/TierSettingsCard
|
|
13
15
|
*/
|
|
@@ -24,8 +26,8 @@ export interface TierSettingsInjected {
|
|
|
24
26
|
/** Pin or clear the current session's routing mode. */
|
|
25
27
|
setMode: (mode: RoutingMode) => Promise<TierStatus>;
|
|
26
28
|
}
|
|
27
|
-
/** Full component props assembled by the Plugins-
|
|
28
|
-
export type TierSettingsCardProps = PropsRuntime<'
|
|
29
|
-
/** The
|
|
30
|
-
export declare function TierSettingsCard({ status, catalog, setMode, t }: TierSettingsCardProps): ReactNode;
|
|
29
|
+
/** Full component props assembled by the Plugins-page renderer. */
|
|
30
|
+
export type TierSettingsCardProps = PropsRuntime<'plugins.item'> & PropsLocale<'settings.autotier'> & InjectFace<TierSettingsInjected>;
|
|
31
|
+
/** The Plugins-page card body. */
|
|
32
|
+
export declare function TierSettingsCard({ status, catalog, setMode, t, view }: TierSettingsCardProps): ReactNode;
|
|
31
33
|
//# sourceMappingURL=TierSettingsCard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TierSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/TierSettingsCard.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"TierSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/TierSettingsCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAuB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAC7F,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAGzD,kFAAkF;AAClF,MAAM,WAAW,oBAAoB;IACnC,2CAA2C;IAC3C,MAAM,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAA;IACjC,4CAA4C;IAC5C,OAAO,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,CAAA;IACnC,uDAAuD;IACvD,OAAO,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;CACpD;AAED,mEAAmE;AACnE,MAAM,MAAM,qBAAqB,GAC/B,YAAY,CAAC,cAAc,CAAC,GAC1B,WAAW,CAAC,mBAAmB,CAAC,GAChC,UAAU,CAAC,oBAAoB,CAAC,CAAA;AAOpC,kCAAkC;AAClC,wBAAgB,gBAAgB,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,qBAAqB,GAAG,SAAS,CA+IxG"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `dsh-autotier`, browser half: mounts the `tier` Remote contribution, then
|
|
3
3
|
* registers the composer tier pill into `conversation.input.left` and the
|
|
4
|
-
* autotier card
|
|
5
|
-
* `
|
|
6
|
-
* binds the session id the slot factory
|
|
7
|
-
* session currently open in the shell through
|
|
4
|
+
* autotier card on the Plugins page (`plugins.item`, id `autotier`; two views:
|
|
5
|
+
* a `summary` one-liner and the `page` form). All data arrives through the
|
|
6
|
+
* `remote.tier` namespace — the pill binds the session id the slot factory
|
|
7
|
+
* hands it, the card resolves the session currently open in the shell through
|
|
8
|
+
* the sessions store face.
|
|
8
9
|
*
|
|
9
10
|
* @module dsh-autotier/client
|
|
10
11
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAiBnE,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACrE,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AACzF,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,EACL,cAAc,EACd,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,OAAO,EACP,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,OAAO,GACR,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAe5E,iDAAiD;AACjD,eAAO,MAAM,EAAE,sBAAsB,CAAA;AAErC,kFAAkF;AAClF,eAAO,MAAM,IAAI,iBAAiB,CAAA;AAElC,uGAAuG;AACvG,eAAO,MAAM,MAAM,UAAgC,CAAA;AAEnD;;;;;GAKG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAqD7D"}
|
|
@@ -45,15 +45,15 @@ export declare const AUTOTIER_REMOTE: Readonly<{
|
|
|
45
45
|
source: "json";
|
|
46
46
|
codec: Readonly<{
|
|
47
47
|
mode: "strict";
|
|
48
|
-
typeSymbol:
|
|
49
|
-
|
|
48
|
+
typeSymbol: string;
|
|
49
|
+
create: () => import("zod").ZodOptional<import("zod").ZodString>;
|
|
50
50
|
}>;
|
|
51
51
|
acceptsUndefined: true;
|
|
52
52
|
}>[];
|
|
53
53
|
readonly result: Readonly<{
|
|
54
54
|
mode: "strict";
|
|
55
55
|
typeSymbol: string;
|
|
56
|
-
|
|
56
|
+
create: () => import("zod").ZodObject<{
|
|
57
57
|
mode: import("zod").ZodEnum<{
|
|
58
58
|
strong: "strong";
|
|
59
59
|
cheap: "cheap";
|
|
@@ -158,10 +158,10 @@ export declare const AUTOTIER_REMOTE: Readonly<{
|
|
|
158
158
|
kind: "direct";
|
|
159
159
|
}>;
|
|
160
160
|
readonly parameters: readonly never[];
|
|
161
|
-
readonly result: Readonly<{
|
|
161
|
+
readonly result: Readonly<Readonly<{
|
|
162
162
|
mode: "strict";
|
|
163
|
-
typeSymbol:
|
|
164
|
-
|
|
163
|
+
typeSymbol: string;
|
|
164
|
+
create: () => import("zod").ZodArray<import("zod").ZodObject<{
|
|
165
165
|
provider: import("zod").ZodString;
|
|
166
166
|
models: import("zod").ZodArray<import("zod").ZodObject<{
|
|
167
167
|
id: import("zod").ZodString;
|
|
@@ -169,7 +169,7 @@ export declare const AUTOTIER_REMOTE: Readonly<{
|
|
|
169
169
|
inputModalities: import("zod").ZodArray<import("zod").ZodString>;
|
|
170
170
|
}, import("zod/v4/core").$strip>>;
|
|
171
171
|
}, import("zod/v4/core").$strip>>;
|
|
172
|
-
}
|
|
172
|
+
}>>;
|
|
173
173
|
readonly sourceLocation: Readonly<{
|
|
174
174
|
file: "src/wire.ts";
|
|
175
175
|
line: 1;
|
|
@@ -189,8 +189,8 @@ export declare const AUTOTIER_REMOTE: Readonly<{
|
|
|
189
189
|
source: "json";
|
|
190
190
|
codec: Readonly<{
|
|
191
191
|
mode: "strict";
|
|
192
|
-
typeSymbol:
|
|
193
|
-
|
|
192
|
+
typeSymbol: string;
|
|
193
|
+
create: () => import("zod").ZodOptional<import("zod").ZodString>;
|
|
194
194
|
}>;
|
|
195
195
|
acceptsUndefined: true;
|
|
196
196
|
}> | Readonly<{
|
|
@@ -199,8 +199,8 @@ export declare const AUTOTIER_REMOTE: Readonly<{
|
|
|
199
199
|
source: "json";
|
|
200
200
|
codec: Readonly<{
|
|
201
201
|
mode: "strict";
|
|
202
|
-
typeSymbol:
|
|
203
|
-
|
|
202
|
+
typeSymbol: string;
|
|
203
|
+
create: () => import("zod").ZodEnum<{
|
|
204
204
|
strong: "strong";
|
|
205
205
|
cheap: "cheap";
|
|
206
206
|
off: "off";
|
|
@@ -212,7 +212,7 @@ export declare const AUTOTIER_REMOTE: Readonly<{
|
|
|
212
212
|
readonly result: Readonly<{
|
|
213
213
|
mode: "strict";
|
|
214
214
|
typeSymbol: string;
|
|
215
|
-
|
|
215
|
+
create: () => import("zod").ZodObject<{
|
|
216
216
|
mode: import("zod").ZodEnum<{
|
|
217
217
|
strong: "strong";
|
|
218
218
|
cheap: "cheap";
|