@happyvertical/smrt-inference 0.60.15 → 0.60.16
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 +10 -5
- package/agents/fusions.md +16 -3
- package/agents/hosted.md +29 -13
- package/agents/settings.md +30 -7
- package/dist/chunks/{InferenceOverrideCollection-wl7sI-3C.js → InferenceOverrideCollection-Dat4TrK5.js} +29 -4
- package/dist/chunks/InferenceOverrideCollection-Dat4TrK5.js.map +1 -0
- package/dist/chunks/{provider-config-4Bx8N52X.js → provider-config-BSpIQRVM.js} +37 -11
- package/dist/chunks/{provider-config-4Bx8N52X.js.map → provider-config-BSpIQRVM.js.map} +1 -1
- package/dist/fusion.d.ts.map +1 -1
- package/dist/hosted.d.ts +31 -24
- package/dist/hosted.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/manifest.json +1 -1
- package/dist/models/InferenceOverride.d.ts.map +1 -1
- package/dist/models.js +1 -1
- package/dist/node.js +86 -33
- package/dist/node.js.map +1 -1
- package/dist/register.d.ts.map +1 -1
- package/dist/resolver.d.ts.map +1 -1
- package/dist/resolver.js +4 -1
- package/dist/resolver.js.map +1 -1
- package/dist/server.d.ts +3 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +194 -41
- package/dist/server.js.map +1 -1
- package/dist/settings/layers.d.ts +14 -0
- package/dist/settings/layers.d.ts.map +1 -1
- package/dist/settings/override-service.d.ts.map +1 -1
- package/dist/settings/resolver.d.ts +7 -0
- package/dist/settings/resolver.d.ts.map +1 -1
- package/dist/settings/settings-service.d.ts +80 -7
- package/dist/settings/settings-service.d.ts.map +1 -1
- package/dist/settings/targets.d.ts +21 -2
- package/dist/settings/targets.d.ts.map +1 -1
- package/dist/settings/transcripts.d.ts +33 -0
- package/dist/settings/transcripts.d.ts.map +1 -0
- package/dist/smrt-knowledge.json +17 -11
- package/dist/smrt-knowledge.okf.md +2 -2
- package/dist/svelte/components/InferenceFusionList.svelte +6 -0
- package/dist/svelte/components/InferenceFusionList.svelte.d.ts.map +1 -1
- package/dist/svelte/components/InferenceSettingsPanel.svelte +49 -2
- package/dist/svelte/components/InferenceSettingsPanel.svelte.d.ts +21 -2
- package/dist/svelte/components/InferenceSettingsPanel.svelte.d.ts.map +1 -1
- package/dist/svelte/components/InferenceTranscriptSettings.svelte +165 -0
- package/dist/svelte/components/InferenceTranscriptSettings.svelte.d.ts +18 -0
- package/dist/svelte/components/InferenceTranscriptSettings.svelte.d.ts.map +1 -0
- package/dist/svelte/components/InferenceUsageTable.svelte +64 -14
- package/dist/svelte/components/InferenceUsageTable.svelte.d.ts +10 -4
- package/dist/svelte/components/InferenceUsageTable.svelte.d.ts.map +1 -1
- package/dist/svelte/index.d.ts +1 -1
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +1 -1
- package/dist/svelte/types.d.ts +66 -1
- package/dist/svelte/types.d.ts.map +1 -1
- package/dist/svelte/types.js +28 -0
- package/package.json +10 -10
- package/dist/chunks/InferenceOverrideCollection-wl7sI-3C.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -67,8 +67,9 @@ the user resolver's `write(level, config)`; never a second secret type.
|
|
|
67
67
|
`./node` provisions per-user gateway keys through `ai.admin` of the
|
|
68
68
|
`bifrost` / `litellm` provider (`SMRT_INFERENCE_HOSTED_*`, read once), storing
|
|
69
69
|
the key only in the user's `inference.provider-config` (`source: 'hosted'`).
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
Plan changes update the key budget, revoke ends local use and then deletes the
|
|
71
|
+
key (failures are owed and retried by reconcile), and the tenant project is
|
|
72
|
+
looked up before one is created (`@happyvertical/ai` >= 0.111.7). `./gate`'s
|
|
72
73
|
`registerHostedInferenceGate({ killSwitch, spend })` is core's
|
|
73
74
|
`InferenceGate`: kill switch `inference.hosted.enabled` (a global DISABLE
|
|
74
75
|
always wins), tenant, entitlement, then plan spend caps; a block throws
|
|
@@ -156,8 +157,9 @@ its own `baseUrl` core's pair rule refuses it.
|
|
|
156
157
|
## Fusions (#4753)
|
|
157
158
|
|
|
158
159
|
`{ fusion: '<key>' }` tier targets run consumer-defined fusions (`single` 1
|
|
159
|
-
call, `pair` 3, `handoff` 2) from
|
|
160
|
-
`
|
|
160
|
+
call, `pair` 3, `handoff` 2) from, highest first, the resolver's stored app
|
|
161
|
+
rows (core's `lookupFusion()`, #4800), `inference.fusions`, or
|
|
162
|
+
`defineFusion()`; every source is re-validated before it runs. SMRT ships the interpreter, never a fusion. Seats are
|
|
161
163
|
smrt-prompts keys run through `createChainClient()`; a dead seat never
|
|
162
164
|
aborts a `pair`, all seats dead throws `InferenceFusionError`; each run writes
|
|
163
165
|
one `_smrt_ai_runs` row. Fusions are defined at app scope only; a tenant
|
|
@@ -188,7 +190,10 @@ the `_smrt_inference_overrides` model (ships through `smrt db:migrate`).
|
|
|
188
190
|
Keys go to smrt-secrets through the host and are bound to their endpoint's
|
|
189
191
|
origin; user choices are the smrt-preferences kind `inference`; app/tenant
|
|
190
192
|
bindings are override rows, tenants limited to the app row's allow-list and
|
|
191
|
-
the `tenantTargetPolicy` hook.
|
|
193
|
+
the `tenantTargetPolicy` hook. The app's transcript setting (#4781) is the
|
|
194
|
+
app-only `transcripts` row, editable in the panel only with `appEditable`,
|
|
195
|
+
applied as core's transcript app settings layer; the Usage tab shows
|
|
196
|
+
transcript counts and links to a host route, never content. Details: [agents/settings.md](agents/settings.md),
|
|
192
197
|
consumer guide [docs/content/inference-settings.md](../../docs/content/inference-settings.md).
|
|
193
198
|
|
|
194
199
|
## Validation
|
package/agents/fusions.md
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
`src/fusion.ts` runs `{ fusion: '<key>' }` tier targets. SMRT ships the
|
|
4
4
|
interpreter and the vocabulary, never a fusion: modes are code, fusions are
|
|
5
|
-
config. A fusion comes from
|
|
5
|
+
config. A fusion comes from, highest first (each replaces a lower one whole):
|
|
6
|
+
the registered resolver's stored layer through core's
|
|
7
|
+
`context.lookupFusion(key)` (#4800; the override resolver's app rows),
|
|
8
|
+
`inference.fusions.<key>` (app config), or
|
|
6
9
|
`defineFusion({ key, mode, seats, gate?, description? })` (process registry
|
|
7
10
|
on `globalThis`). A seat is `{ name?, prompt, target, temperature?, output? }`:
|
|
8
11
|
`prompt` is a smrt-prompts key whose resolved template (code default,
|
|
@@ -88,5 +91,15 @@ await contract.do('Summarize this contract', { tier: 'best' }); // unchanged cal
|
|
|
88
91
|
- **Fusions are app-scope only.** A fusion multiplies hosted model spend
|
|
89
92
|
per call, so a tenant never defines or redefines one; with #4754's
|
|
90
93
|
allow-list a tenant may bind a tier to a listed fusion (`fusion:<n>`).
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
- **Stored fusions (#4800).** Core hands the executor
|
|
95
|
+
`context.lookupFusion(key)`: the instance's (or registered) resolver's
|
|
96
|
+
optional `lookupFusion(key, request, context)`, then the merged
|
|
97
|
+
`inference.fusions`; `undefined` falls to the registry. Without it (older
|
|
98
|
+
cores) the executor reads config, then the registry, and validates at bind
|
|
99
|
+
time; with it the lookup starts at bind time and an unknown or invalid
|
|
100
|
+
fusion fails the first call before any seat runs (nothing recorded). Every
|
|
101
|
+
definition, whatever its source, is validated before it runs (structure
|
|
102
|
+
against the call's aliases, every seat prompt key); a resolver's answer
|
|
103
|
+
fails under path `resolver.fusions.<key>`. The #4752 validating wrapper
|
|
104
|
+
and the #4756 user resolver forward `lookupFusion` to their base /
|
|
105
|
+
`fallback` (stored user and tenant configs never define a fusion).
|
package/agents/hosted.md
CHANGED
|
@@ -11,18 +11,29 @@
|
|
|
11
11
|
hand).
|
|
12
12
|
|
|
13
13
|
- **Gateway.** Admin calls go through `ai.admin` (`AIAdminInterface`) of the
|
|
14
|
-
`bifrost` / `litellm` provider of `@happyvertical/ai
|
|
15
|
-
gateway administration here.
|
|
16
|
-
`
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
`
|
|
24
|
-
`
|
|
25
|
-
`
|
|
14
|
+
`bifrost` / `litellm` provider of `@happyvertical/ai` >= 0.111.7
|
|
15
|
+
(happyvertical/sdk#1494); never reimplement gateway administration here.
|
|
16
|
+
`HostedGatewayAdmin` is `HOSTED_GATEWAY_ADMIN_METHODS` (`createProject`,
|
|
17
|
+
`createVirtualKey`, `updateVirtualKey`, `deleteVirtualKey`,
|
|
18
|
+
`listProjects`); a provider missing one is `HOSTED_GATEWAY_UNSUPPORTED`.
|
|
19
|
+
SMRT persists only `AIVirtualKey.id`, the non-secret handle (Bifrost id,
|
|
20
|
+
LiteLLM `token_id`); an id that equals or looks like a key value (`sk-…`)
|
|
21
|
+
is dropped on create and when older state is read, so that key counts as
|
|
22
|
+
having no id. Budget sync sends
|
|
23
|
+
`updateVirtualKey(id, { budget })` only (Bifrost refuses `models` /
|
|
24
|
+
`metadata`, LiteLLM `providerConfigs`: `ADMIN_UNSUPPORTED_FIELD`);
|
|
25
|
+
`unsupported` now means the grant has no key id. Revoke ends local use
|
|
26
|
+
before touching the gateway (config restored, state retired with the key
|
|
27
|
+
owed, then `deleteVirtualKey`; `false` is `missing`, an already revoked
|
|
28
|
+
key, nothing owed). A deletion the gateway did not perform (`failed`, a
|
|
29
|
+
key a failed provision left, `unsupported`: no admin configured or no key
|
|
30
|
+
id; entries from before 0.111.7 also read `unsupported`) goes to the
|
|
31
|
+
state's `pendingGatewayRevocations` (id + alias, never the key), is
|
|
32
|
+
audited with `manualGatewayRevocation`, and is retried by `revoke()` /
|
|
33
|
+
`reconcileTenant()`; each retry that settles one writes
|
|
34
|
+
`inference.hosted.revoke-retry` with `settled: [{ keyId, keyAlias,
|
|
35
|
+
gateway }]`, closing those records. Entries without an id stay for an
|
|
36
|
+
operator (`acknowledgeGatewayRevocation`).
|
|
26
37
|
- **Config, read once from the environment** (`getHostedGatewayConfig()`;
|
|
27
38
|
unset provider = hosted off): `SMRT_INFERENCE_HOSTED_PROVIDER`
|
|
28
39
|
(`bifrost|litellm`), `_BASE_URL` (the OpenAI-compatible endpoint keys are
|
|
@@ -43,7 +54,12 @@ hand).
|
|
|
43
54
|
`inference.hosted:user:<id>` (category `inference.hosted`) holds status,
|
|
44
55
|
generation, key id and alias, budget and the user's prior config;
|
|
45
56
|
`inference.hosted:project` the tenant's gateway project (created once
|
|
46
|
-
through `runOnce()`,
|
|
57
|
+
through `runOnce()`, name `smrt tenant <tenantId>`, LiteLLM id
|
|
58
|
+
`smrt-<tenantId>-<nonce>`). Without a stored record, `listProjects({
|
|
59
|
+
tenantId })` runs first and adopts the lowest-id project with that name
|
|
60
|
+
(and id prefix on LiteLLM), audited `adopted: true`, so a crash between
|
|
61
|
+
create and persist never makes a second project; a failed lookup fails
|
|
62
|
+
the provision rather than create blind. Every write passes
|
|
47
63
|
`auditDetails.operation` (`inference.hosted.provision|revoke|budget|project`)
|
|
48
64
|
to `SecretAuditLog`.
|
|
49
65
|
- **Idempotency and ordering.** An active grant whose binding is still the
|
package/agents/settings.md
CHANGED
|
@@ -50,7 +50,11 @@ Consumer guide: [docs/content/inference-settings.md](../../../docs/content/infer
|
|
|
50
50
|
`inheritedTarget` = config + app row), plus `getDefaultTierSetting()`,
|
|
51
51
|
`listInferenceAliases()`, `listInferenceFusions()` (registry: the
|
|
52
52
|
`registryFusions` option, default #4753's `defineFusion()` registry;
|
|
53
|
-
config `inference.fusions`; editable app rows)
|
|
53
|
+
config `inference.fusions`; editable app rows), each entry with
|
|
54
|
+
`shadows` (lower sources it replaces by name) and `issues` (what would make
|
|
55
|
+
the executor refuse it, prompt keys included), the executor's precedence
|
|
56
|
+
(#4800). `setFusion()` refuses credential members, then a definition that
|
|
57
|
+
adds a structural issue. Writes go through `InferenceOverrideService`
|
|
54
58
|
(authorizer first, fails closed; without `authorize` the service is
|
|
55
59
|
read-only) and refuse a value that adds a config issue to the layer it
|
|
56
60
|
lands in; removing an app alias or fusion that any row (every tenant) or
|
|
@@ -61,7 +65,19 @@ Consumer guide: [docs/content/inference-settings.md](../../../docs/content/infer
|
|
|
61
65
|
`getEffectiveConfig()` is server-side only. `testConnection()`, `testCandidate()` and `doctor()` reuse
|
|
62
66
|
`./node`; `testCandidate()` reads a key variable only for a candidate
|
|
63
67
|
already in the effective config (an unsaved one is probed keyless);
|
|
64
|
-
`usage()` is `reportAiUsage()` by model (default), tier, prompt
|
|
68
|
+
`usage()` is `reportAiUsage()` by model (default), tier, prompt, fusion,
|
|
69
|
+
template, with `transcripts: true` for per-bucket counts (#4781).
|
|
70
|
+
- **Transcript setting** (#4781, `src/settings/transcripts.ts`): binding
|
|
71
|
+
`transcripts`, app row only (service and model refuse a tenant row),
|
|
72
|
+
value `{ enabled?, sample?, maxBytes? }` checked by core's
|
|
73
|
+
`assertInferenceTranscriptsConfig()`. `setTranscriptSettings()` writes it
|
|
74
|
+
through the authorizer and installs it as core's
|
|
75
|
+
`setAiTranscriptAppSettings()` layer (config < app row < env); the
|
|
76
|
+
override resolver re-applies the app rows on every resolve (rows cached
|
|
77
|
+
for `ttlMs`), `loadInferenceTranscriptSettings()` is the worker startup
|
|
78
|
+
hook. `getTranscriptSettings()` adds the revert preview, the env override
|
|
79
|
+
(detected by forcing both values through the layer) and the sweep's
|
|
80
|
+
retention days, read-only (the sweep runs with its caller's policy).
|
|
65
81
|
- **`createInferenceOverrideResolver({ db, base })`** applies stored
|
|
66
82
|
bindings at call time and defers unbound tiers to `base` or config; an
|
|
67
83
|
app-only alias is returned as its inline candidate chain because the
|
|
@@ -70,10 +86,14 @@ Consumer guide: [docs/content/inference-settings.md](../../../docs/content/infer
|
|
|
70
86
|
allow-list and policy are re-applied on every call, and only the requested
|
|
71
87
|
tier's chain is validated, so one dangling reference fails one tier.
|
|
72
88
|
Compose it as the per-user resolver's `fallback` (see AGENTS.md,
|
|
73
|
-
"Resolver composition").
|
|
74
|
-
executor (
|
|
75
|
-
|
|
76
|
-
|
|
89
|
+
"Resolver composition"). Its `lookupFusion()` (#4800) answers core's
|
|
90
|
+
executor lookup from app rows only (read with no tenant, so a tenant row
|
|
91
|
+
never defines a fusion), validated against config + app rows with every
|
|
92
|
+
seat prompt key (`InferenceConfigError`, paths `fusion:<name>…`), app-only
|
|
93
|
+
seat aliases inlined like tier aliases; an app row that exists but cannot
|
|
94
|
+
be read fails `fusion-invalid` (never its value) rather than deferring (the
|
|
95
|
+
list shows it as the app winner with that issue); no app row defers to
|
|
96
|
+
`base`, then config. An app row shadows a config fusion of the same name.
|
|
77
97
|
- **Panel** (`src/svelte`): built like `PromptSettingsPanel` (presentational,
|
|
78
98
|
`svelte` optional peer, prune script, two-project Vitest, `svelte-check`,
|
|
79
99
|
named `$props()` interfaces, types free of core). The default view is the
|
|
@@ -81,4 +101,7 @@ Consumer guide: [docs/content/inference-settings.md](../../../docs/content/infer
|
|
|
81
101
|
aliases, fusions) is not rendered until opened. The key input is its own
|
|
82
102
|
form, never in state, cleared on submit. Tenant scope disables targets
|
|
83
103
|
off the allow-list (display only) and makes aliases/fusions read-only.
|
|
84
|
-
Rows remount on `contextKey`.
|
|
104
|
+
Rows remount on `contextKey`. The transcript section is read-only unless
|
|
105
|
+
`appEditable` and `onSaveTranscripts` are both passed; the Usage tab
|
|
106
|
+
links counts through the host's `transcriptHref` and renders no
|
|
107
|
+
transcript content (#4781).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { INFERENCE_TIERS, ObjectRegistry, SmrtCollection, SmrtObject, field, inferenceTargetKind, isInferenceTier, smrt } from "@happyvertical/smrt-core";
|
|
1
|
+
import { INFERENCE_TIERS, ObjectRegistry, SmrtCollection, SmrtObject, assertInferenceTranscriptsConfig, field, inferenceTargetKind, isInferenceTier, smrt } from "@happyvertical/smrt-core";
|
|
2
2
|
//#region src/__smrt-register__.ts
|
|
3
|
-
ObjectRegistry.registerPackageManifest(JSON.parse(`{"version":"1.0.0","timestamp":0,"packageName":"@happyvertical/smrt-inference","packageVersion":"0.60.
|
|
3
|
+
ObjectRegistry.registerPackageManifest(JSON.parse(`{"version":"1.0.0","timestamp":0,"packageName":"@happyvertical/smrt-inference","packageVersion":"0.60.16","objects":{"@happyvertical/smrt-inference:InferenceOverride":{"name":"inferenceoverride","className":"InferenceOverride","qualifiedName":"@happyvertical/smrt-inference:InferenceOverride","collection":"inferenceoverrides","filePath":"/home/runner/work/smrt/smrt/packages/inference/src/models/InferenceOverride.ts","packageName":"@happyvertical/smrt-inference","fields":{"binding":{"type":"text","required":true,"default":"","_meta":{"required":true}},"tenantId":{"type":"text","required":false,"_meta":{"sqlType":"UUID","nullable":true}},"target":{"type":"text","required":false,"_meta":{"nullable":true}},"tenantAllowList":{"type":"text","required":false,"_meta":{"nullable":true}}},"methods":{"getValue":{"name":"getValue","async":false,"parameters":[],"returnType":"unknown","isStatic":false,"isPublic":true},"getTenantAllowList":{"name":"getTenantAllowList","async":false,"parameters":[],"returnType":"string[] | null","isStatic":false,"isPublic":true},"save":{"name":"save","async":true,"parameters":[{"name":"options","type":"SmrtSaveOptions","optional":true}],"returnType":"Promise<this>","isStatic":false,"isPublic":true},"delete":{"name":"delete","async":true,"parameters":[],"returnType":"Promise<void>","isStatic":false,"isPublic":true}},"decoratorConfig":{"tableName":"_smrt_inference_overrides","conflictColumns":["binding","context"],"api":false,"cli":false,"mcp":false,"indexes":[{"name":"idx_smrt_inference_overrides_tenant","columns":["tenantId","binding"]}]},"extends":"SmrtObject","exportName":"InferenceOverride","collectionExportName":"InferenceOverrideCollection","validationRules":[{"field":"binding","rule":"required","fieldType":"text"}],"schema":{"tableName":"_smrt_inference_overrides","ddl":"CREATE TABLE IF NOT EXISTS \\"_smrt_inference_overrides\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"binding\\" TEXT NOT NULL DEFAULT '',\\n \\"tenant_id\\" UUID,\\n \\"target\\" TEXT,\\n \\"tenant_allow_list\\" TEXT\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"binding":{"type":"TEXT","notNull":true,"unique":false,"default":""},"tenant_id":{"type":"UUID","notNull":false,"unique":false},"target":{"type":"TEXT","notNull":false,"unique":false},"tenant_allow_list":{"type":"TEXT","notNull":false,"unique":false}},"indexes":[{"name":"_smrt_inference_overrides_binding_context_idx","columns":["binding","context"],"unique":true},{"name":"_smrt_inference_overrides_slug_context_idx","columns":["slug","context"]},{"name":"idx_smrt_inference_overrides_tenant","columns":["tenant_id","binding"]},{"name":"_smrt_inference_overrides_created_at_idx","columns":["created_at"]}],"version":"4868b81b"},"importPath":"@happyvertical/smrt-inference/models"},"@happyvertical/smrt-inference:InferenceOverrideCollection":{"name":"inferenceoverridecollection","className":"InferenceOverrideCollection","qualifiedName":"@happyvertical/smrt-inference:InferenceOverrideCollection","collection":"inferenceoverrides","description":"Reads and unauthorized writes of _smrt_inference_overrides.","filePath":"/home/runner/work/smrt/smrt/packages/inference/src/models/InferenceOverrideCollection.ts","packageName":"@happyvertical/smrt-inference","fields":{},"methods":{"getScopeMap":{"name":"getScopeMap","async":true,"parameters":[{"name":"scopeType","type":"InferenceOverrideScopeType","optional":false,"resolvedType":"'app' | 'tenant'"},{"name":"scopeId","type":"string","optional":false}],"returnType":"Promise<Map<string, InferenceOverride>>","isStatic":false,"isPublic":true},"findByScope":{"name":"findByScope","async":true,"parameters":[{"name":"binding","type":"string","optional":false},{"name":"scopeType","type":"InferenceOverrideScopeType","optional":false,"resolvedType":"'app' | 'tenant'"},{"name":"scopeId","type":"string","optional":false}],"returnType":"Promise<InferenceOverride | null>","isStatic":false,"isPublic":true},"setOverride":{"name":"setOverride","async":true,"parameters":[{"name":"binding","type":"string","optional":false},{"name":"scopeType","type":"InferenceOverrideScopeType","optional":false,"resolvedType":"'app' | 'tenant'"},{"name":"scopeId","type":"string","optional":false},{"name":"change","type":"object","optional":false,"memberTypes":["unknown","string[] | null"]}],"returnType":"Promise<InferenceOverride | null>","isStatic":false,"isPublic":true},"removeOverride":{"name":"removeOverride","async":true,"parameters":[{"name":"binding","type":"string","optional":false},{"name":"scopeType","type":"InferenceOverrideScopeType","optional":false,"resolvedType":"'app' | 'tenant'"},{"name":"scopeId","type":"string","optional":false}],"returnType":"Promise<boolean>","isStatic":false,"isPublic":true}},"decoratorConfig":{"tableName":"_smrt_inference_overrides","api":false,"mcp":false,"cli":false},"extends":"SmrtCollection","extendsTypeArg":"InferenceOverride","exportName":"InferenceOverrideCollection","collectionExportName":"InferenceOverrideCollectionCollection","collectionOf":"@happyvertical/smrt-inference:InferenceOverride","inheritedExposure":["api","mcp","cli"],"schema":{"tableName":"_smrt_inference_overrides","ddl":"CREATE TABLE IF NOT EXISTS \\"_smrt_inference_overrides\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"binding\\" TEXT NOT NULL DEFAULT '',\\n \\"tenant_id\\" UUID,\\n \\"target\\" TEXT,\\n \\"tenant_allow_list\\" TEXT\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"binding":{"type":"TEXT","notNull":true,"unique":false,"default":""},"tenant_id":{"type":"UUID","notNull":false,"unique":false},"target":{"type":"TEXT","notNull":false,"unique":false},"tenant_allow_list":{"type":"TEXT","notNull":false,"unique":false}},"indexes":[{"name":"_smrt_inference_overrides_binding_context_idx","columns":["binding","context"],"unique":true},{"name":"_smrt_inference_overrides_slug_context_idx","columns":["slug","context"]},{"name":"idx_smrt_inference_overrides_tenant","columns":["tenant_id","binding"]},{"name":"_smrt_inference_overrides_created_at_idx","columns":["created_at"]}],"version":"4868b81b"},"importPath":"@happyvertical/smrt-inference/models"}},"moduleType":"smrt","smrtDependencies":["@happyvertical/smrt-core","@happyvertical/smrt-prompts","@happyvertical/smrt-secrets","@happyvertical/smrt-tenancy","@happyvertical/smrt-users"],"browser":{"status":"server-only","issues":["#3617","#3624","#3626","#3627"],"via":["@happyvertical/smrt-profiles","@happyvertical/smrt-secrets","@happyvertical/smrt-tenancy","@happyvertical/smrt-users"]},"stampsConstructors":true}`));
|
|
4
4
|
//#endregion
|
|
5
5
|
//#region src/settings/cache.ts
|
|
6
6
|
var GENERATION_KEY = /* @__PURE__ */ Symbol.for("smrt.inference.overrideGeneration");
|
|
@@ -20,6 +20,7 @@ var INFERENCE_MODES$1 = [
|
|
|
20
20
|
"off"
|
|
21
21
|
];
|
|
22
22
|
var DEFAULT_TIER_BINDING = "default-tier";
|
|
23
|
+
var TRANSCRIPTS_BINDING = "transcripts";
|
|
23
24
|
var INFERENCE_BINDING_NAME_PATTERN = /^[a-z0-9][a-z0-9._-]{0,63}$/i;
|
|
24
25
|
var InferenceBindingValueError = class extends Error {
|
|
25
26
|
constructor(binding, reason) {
|
|
@@ -33,6 +34,7 @@ var InferenceBindingValueError = class extends Error {
|
|
|
33
34
|
function parseInferenceBinding(binding) {
|
|
34
35
|
if (typeof binding !== "string") return null;
|
|
35
36
|
if (binding === "default-tier") return { kind: "default-tier" };
|
|
37
|
+
if (binding === "transcripts") return { kind: "transcripts" };
|
|
36
38
|
const match = /^(tier|alias|fusion):(.+)$/.exec(binding);
|
|
37
39
|
if (!match) return null;
|
|
38
40
|
const [, kind, name] = match;
|
|
@@ -185,6 +187,27 @@ function normalizeDefaultTier(binding, value) {
|
|
|
185
187
|
if (!isInferenceTier(tier)) throw new InferenceBindingValueError(binding, `the default tier must be one of ${INFERENCE_TIERS.join(", ")}`);
|
|
186
188
|
return tier;
|
|
187
189
|
}
|
|
190
|
+
var TRANSCRIPT_SETTING_MEMBERS = /* @__PURE__ */ new Set([
|
|
191
|
+
"enabled",
|
|
192
|
+
"sample",
|
|
193
|
+
"maxBytes"
|
|
194
|
+
]);
|
|
195
|
+
function normalizeTranscriptSettings(binding, value) {
|
|
196
|
+
if (value === null || value === void 0) return null;
|
|
197
|
+
if (!isRecord(value)) throw new InferenceBindingValueError(binding, "the transcript setting must be an object");
|
|
198
|
+
const extra = Object.keys(value).filter((member) => !TRANSCRIPT_SETTING_MEMBERS.has(member));
|
|
199
|
+
if (extra.length > 0) throw new InferenceBindingValueError(binding, `the transcript setting carries enabled, sample and maxBytes only (got ${extra.join(", ")})`);
|
|
200
|
+
try {
|
|
201
|
+
assertInferenceTranscriptsConfig(value);
|
|
202
|
+
} catch (error) {
|
|
203
|
+
throw new InferenceBindingValueError(binding, error instanceof Error ? error.message : String(error));
|
|
204
|
+
}
|
|
205
|
+
const out = {};
|
|
206
|
+
if (value.enabled !== void 0) out.enabled = value.enabled;
|
|
207
|
+
if (value.sample !== void 0) out.sample = value.sample;
|
|
208
|
+
if (value.maxBytes !== void 0) out.maxBytes = value.maxBytes;
|
|
209
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
210
|
+
}
|
|
188
211
|
function inferenceTargetRef(target) {
|
|
189
212
|
const kind = inferenceTargetKind(target);
|
|
190
213
|
const record = target;
|
|
@@ -284,6 +307,7 @@ var InferenceOverride = class extends SmrtObject {
|
|
|
284
307
|
switch (parsed.kind) {
|
|
285
308
|
case "tier": return normalizeTierTarget(this.binding, raw);
|
|
286
309
|
case "default-tier": return normalizeDefaultTier(this.binding, raw);
|
|
310
|
+
case "transcripts": return normalizeTranscriptSettings(this.binding, raw);
|
|
287
311
|
case "alias": return normalizeAliasDefinition(this.binding, raw);
|
|
288
312
|
case "fusion": return normalizeFusionDefinition(this.binding, raw);
|
|
289
313
|
}
|
|
@@ -294,6 +318,7 @@ var InferenceOverride = class extends SmrtObject {
|
|
|
294
318
|
if (this.tenantId !== null) {
|
|
295
319
|
if (typeof this.tenantId !== "string" || !this.tenantId.trim() || this.tenantId !== this.tenantId.trim() || this.tenantId === "__app__") throw new InferenceBindingValueError(this.binding, "invalid tenant id");
|
|
296
320
|
if (parsed.kind === "alias" || parsed.kind === "fusion") throw new InferenceBindingValueError(this.binding, "aliases and fusions are defined at the app scope only");
|
|
321
|
+
if (parsed.kind === "transcripts") throw new InferenceBindingValueError(this.binding, "the transcript setting is app scope only");
|
|
297
322
|
if (this.tenantAllowList !== null) throw new InferenceBindingValueError(this.binding, "the tenant allow-list lives on the app row");
|
|
298
323
|
}
|
|
299
324
|
if (this.tenantAllowList !== null && parsed.kind !== "tier" && parsed.kind !== "default-tier") throw new InferenceBindingValueError(this.binding, "only tier and default-tier bindings carry a tenant allow-list");
|
|
@@ -393,6 +418,6 @@ var InferenceOverrideCollection = class extends SmrtCollection {
|
|
|
393
418
|
}
|
|
394
419
|
};
|
|
395
420
|
//#endregion
|
|
396
|
-
export {
|
|
421
|
+
export { getInferenceOverrideGeneration as C, tierBinding as S, normalizeFusionDefinition as _, INFERENCE_BINDING_NAME_PATTERN as a, parseInferenceBinding as b, TRANSCRIPTS_BINDING as c, inferenceTargetRef as d, isAllowedByList as f, normalizeDefaultTier as g, normalizeAllowList as h, DEFAULT_TIER_BINDING as i, describeInferenceTarget as l, normalizeAliasDefinition as m, APP_INFERENCE_SCOPE_ID as n, INFERENCE_MODES$1 as o, matchesAllowPattern as p, InferenceOverride as r, InferenceBindingValueError as s, InferenceOverrideCollection as t, findKeyMember as u, normalizeTierTarget as v, stripKeyMembers as x, normalizeTranscriptSettings as y };
|
|
397
422
|
|
|
398
|
-
//# sourceMappingURL=InferenceOverrideCollection-
|
|
423
|
+
//# sourceMappingURL=InferenceOverrideCollection-Dat4TrK5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InferenceOverrideCollection-Dat4TrK5.js","names":[],"sources":["../../src/__smrt-register__.ts","../../src/settings/cache.ts","../../src/settings/targets.ts","../../src/models/InferenceOverride.ts","../../src/models/InferenceOverrideCollection.ts"],"sourcesContent":["// Generated by @happyvertical/smrt-core smrtPlugin at library build time.\n// The package manifest is inlined so self-registration cannot miss when a\n// downstream bundler relocates this module away from dist/manifest.json\n// (https://github.com/happyvertical/smrt/issues/1506,\n// https://github.com/happyvertical/smrt/issues/1507).\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\nObjectRegistry.registerPackageManifest(JSON.parse(\"{\\\"version\\\":\\\"1.0.0\\\",\\\"timestamp\\\":0,\\\"packageName\\\":\\\"@happyvertical/smrt-inference\\\",\\\"packageVersion\\\":\\\"0.60.16\\\",\\\"objects\\\":{\\\"@happyvertical/smrt-inference:InferenceOverride\\\":{\\\"name\\\":\\\"inferenceoverride\\\",\\\"className\\\":\\\"InferenceOverride\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-inference:InferenceOverride\\\",\\\"collection\\\":\\\"inferenceoverrides\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/inference/src/models/InferenceOverride.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-inference\\\",\\\"fields\\\":{\\\"binding\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"default\\\":\\\"\\\",\\\"_meta\\\":{\\\"required\\\":true}},\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true}},\\\"target\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"nullable\\\":true}},\\\"tenantAllowList\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"nullable\\\":true}}},\\\"methods\\\":{\\\"getValue\\\":{\\\"name\\\":\\\"getValue\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"unknown\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getTenantAllowList\\\":{\\\"name\\\":\\\"getTenantAllowList\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"string[] | null\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"save\\\":{\\\"name\\\":\\\"save\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"options\\\",\\\"type\\\":\\\"SmrtSaveOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<this>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"delete\\\":{\\\"name\\\":\\\"delete\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"_smrt_inference_overrides\\\",\\\"conflictColumns\\\":[\\\"binding\\\",\\\"context\\\"],\\\"api\\\":false,\\\"cli\\\":false,\\\"mcp\\\":false,\\\"indexes\\\":[{\\\"name\\\":\\\"idx_smrt_inference_overrides_tenant\\\",\\\"columns\\\":[\\\"tenantId\\\",\\\"binding\\\"]}]},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"InferenceOverride\\\",\\\"collectionExportName\\\":\\\"InferenceOverrideCollection\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"binding\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"_smrt_inference_overrides\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"_smrt_inference_overrides\\\\\\\" (\\\\n \\\\\\\"_sync_origin_device_id\\\\\\\" TEXT,\\\\n \\\\\\\"_sync_revision\\\\\\\" INTEGER,\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"binding\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"target\\\\\\\" TEXT,\\\\n \\\\\\\"tenant_allow_list\\\\\\\" TEXT\\\\n);\\\",\\\"columns\\\":{\\\"_sync_origin_device_id\\\":{\\\"type\\\":\\\"TEXT\\\"},\\\"_sync_revision\\\":{\\\"type\\\":\\\"INTEGER\\\"},\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"binding\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"target\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"tenant_allow_list\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"_smrt_inference_overrides_binding_context_idx\\\",\\\"columns\\\":[\\\"binding\\\",\\\"context\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"_smrt_inference_overrides_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"idx_smrt_inference_overrides_tenant\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"binding\\\"]},{\\\"name\\\":\\\"_smrt_inference_overrides_created_at_idx\\\",\\\"columns\\\":[\\\"created_at\\\"]}],\\\"version\\\":\\\"4868b81b\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-inference/models\\\"},\\\"@happyvertical/smrt-inference:InferenceOverrideCollection\\\":{\\\"name\\\":\\\"inferenceoverridecollection\\\",\\\"className\\\":\\\"InferenceOverrideCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-inference:InferenceOverrideCollection\\\",\\\"collection\\\":\\\"inferenceoverrides\\\",\\\"description\\\":\\\"Reads and unauthorized writes of _smrt_inference_overrides.\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/inference/src/models/InferenceOverrideCollection.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-inference\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"getScopeMap\\\":{\\\"name\\\":\\\"getScopeMap\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"scopeType\\\",\\\"type\\\":\\\"InferenceOverrideScopeType\\\",\\\"optional\\\":false,\\\"resolvedType\\\":\\\"'app' | 'tenant'\\\"},{\\\"name\\\":\\\"scopeId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Map<string, InferenceOverride>>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findByScope\\\":{\\\"name\\\":\\\"findByScope\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"binding\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"scopeType\\\",\\\"type\\\":\\\"InferenceOverrideScopeType\\\",\\\"optional\\\":false,\\\"resolvedType\\\":\\\"'app' | 'tenant'\\\"},{\\\"name\\\":\\\"scopeId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<InferenceOverride | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setOverride\\\":{\\\"name\\\":\\\"setOverride\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"binding\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"scopeType\\\",\\\"type\\\":\\\"InferenceOverrideScopeType\\\",\\\"optional\\\":false,\\\"resolvedType\\\":\\\"'app' | 'tenant'\\\"},{\\\"name\\\":\\\"scopeId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"change\\\",\\\"type\\\":\\\"object\\\",\\\"optional\\\":false,\\\"memberTypes\\\":[\\\"unknown\\\",\\\"string[] | null\\\"]}],\\\"returnType\\\":\\\"Promise<InferenceOverride | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"removeOverride\\\":{\\\"name\\\":\\\"removeOverride\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"binding\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"scopeType\\\",\\\"type\\\":\\\"InferenceOverrideScopeType\\\",\\\"optional\\\":false,\\\"resolvedType\\\":\\\"'app' | 'tenant'\\\"},{\\\"name\\\":\\\"scopeId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<boolean>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"_smrt_inference_overrides\\\",\\\"api\\\":false,\\\"mcp\\\":false,\\\"cli\\\":false},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"InferenceOverride\\\",\\\"exportName\\\":\\\"InferenceOverrideCollection\\\",\\\"collectionExportName\\\":\\\"InferenceOverrideCollectionCollection\\\",\\\"collectionOf\\\":\\\"@happyvertical/smrt-inference:InferenceOverride\\\",\\\"inheritedExposure\\\":[\\\"api\\\",\\\"mcp\\\",\\\"cli\\\"],\\\"schema\\\":{\\\"tableName\\\":\\\"_smrt_inference_overrides\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"_smrt_inference_overrides\\\\\\\" (\\\\n \\\\\\\"_sync_origin_device_id\\\\\\\" TEXT,\\\\n \\\\\\\"_sync_revision\\\\\\\" INTEGER,\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"binding\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"target\\\\\\\" TEXT,\\\\n \\\\\\\"tenant_allow_list\\\\\\\" TEXT\\\\n);\\\",\\\"columns\\\":{\\\"_sync_origin_device_id\\\":{\\\"type\\\":\\\"TEXT\\\"},\\\"_sync_revision\\\":{\\\"type\\\":\\\"INTEGER\\\"},\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"binding\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"default\\\":\\\"\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"target\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"tenant_allow_list\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"_smrt_inference_overrides_binding_context_idx\\\",\\\"columns\\\":[\\\"binding\\\",\\\"context\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"_smrt_inference_overrides_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"idx_smrt_inference_overrides_tenant\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"binding\\\"]},{\\\"name\\\":\\\"_smrt_inference_overrides_created_at_idx\\\",\\\"columns\\\":[\\\"created_at\\\"]}],\\\"version\\\":\\\"4868b81b\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-inference/models\\\"}},\\\"moduleType\\\":\\\"smrt\\\",\\\"smrtDependencies\\\":[\\\"@happyvertical/smrt-core\\\",\\\"@happyvertical/smrt-prompts\\\",\\\"@happyvertical/smrt-secrets\\\",\\\"@happyvertical/smrt-tenancy\\\",\\\"@happyvertical/smrt-users\\\"],\\\"browser\\\":{\\\"status\\\":\\\"server-only\\\",\\\"issues\\\":[\\\"#3617\\\",\\\"#3624\\\",\\\"#3626\\\",\\\"#3627\\\"],\\\"via\\\":[\\\"@happyvertical/smrt-profiles\\\",\\\"@happyvertical/smrt-secrets\\\",\\\"@happyvertical/smrt-tenancy\\\",\\\"@happyvertical/smrt-users\\\"]},\\\"stampsConstructors\\\":true}\"));\n","/**\n * Process-wide write generation for `_smrt_inference_overrides` (#4754).\n * Every save or delete of an override row bumps it, so the override\n * resolver's short-lived layer cache never serves a value read before a\n * write in the same process. Kept on `globalThis` so duplicate module copies\n * (HMR, two bundles) share it, like core's resolver registration.\n */\nconst GENERATION_KEY = Symbol.for('smrt.inference.overrideGeneration');\n\ntype GenerationHolder = { [GENERATION_KEY]?: number };\n\n/** The current write generation. */\nexport function getInferenceOverrideGeneration(): number {\n return (globalThis as GenerationHolder)[GENERATION_KEY] ?? 0;\n}\n\n/** Record a write: cached layers read before it are stale. */\nexport function bumpInferenceOverrideGeneration(): void {\n const holder = globalThis as GenerationHolder;\n holder[GENERATION_KEY] = (holder[GENERATION_KEY] ?? 0) + 1;\n}\n","/**\n * The vocabulary of stored inference bindings (#4754): binding ids, the\n * target shapes a settings write may store, the allow-list references a\n * tenant write is checked against, and key-free labels.\n *\n * Pure and browser-safe: no `node:`, no SDK, no smrt-config.\n */\nimport {\n assertInferenceTranscriptsConfig,\n INFERENCE_TIERS,\n type InferenceAlias,\n type InferenceCandidate,\n type InferenceTarget,\n type InferenceTier,\n inferenceTargetKind,\n isInferenceTier,\n} from '@happyvertical/smrt-core';\n\n/** How a user or app gets inference. Stored as a user choice, never a binding. */\nexport const INFERENCE_MODES = ['byo', 'local', 'hosted', 'off'] as const;\nexport type InferenceMode = (typeof INFERENCE_MODES)[number];\n\n/** Binding id of `auto`'s default tier. */\nexport const DEFAULT_TIER_BINDING = 'default-tier';\n\n/**\n * Binding id of the app's transcript setting (#4781). App scope only:\n * tenants cannot override transcripts (owner decision 2026-11-08).\n */\nexport const TRANSCRIPTS_BINDING = 'transcripts';\n\n/** A binding id stored in `_smrt_inference_overrides.binding`. */\nexport type InferenceBindingId =\n | `tier:${InferenceTier}`\n | typeof DEFAULT_TIER_BINDING\n | typeof TRANSCRIPTS_BINDING\n | `alias:${string}`\n | `fusion:${string}`;\n\n/** `inference.aliases` / fusion names: what a binding id may carry after its prefix. */\nexport const INFERENCE_BINDING_NAME_PATTERN = /^[a-z0-9][a-z0-9._-]{0,63}$/i;\n\nexport type InferenceBindingKind =\n | 'tier'\n | 'default-tier'\n | 'transcripts'\n | 'alias'\n | 'fusion';\n\n/** A parsed binding id. */\nexport interface ParsedInferenceBinding {\n kind: InferenceBindingKind;\n /** The tier, alias or fusion name (absent for `default-tier`). */\n name?: string;\n}\n\n/** Thrown for a malformed binding id or an invalid stored value. Nothing is written. */\nexport class InferenceBindingValueError extends Error {\n readonly status = 400;\n constructor(\n readonly binding: string,\n reason: string,\n ) {\n super(`Invalid inference binding \"${binding}\": ${reason}`);\n this.name = 'InferenceBindingValueError';\n }\n}\n\n/** Parse a binding id, or `null` when it is not one this package stores. */\nexport function parseInferenceBinding(\n binding: unknown,\n): ParsedInferenceBinding | null {\n if (typeof binding !== 'string') return null;\n if (binding === DEFAULT_TIER_BINDING) return { kind: 'default-tier' };\n if (binding === TRANSCRIPTS_BINDING) return { kind: 'transcripts' };\n const match = /^(tier|alias|fusion):(.+)$/.exec(binding);\n if (!match) return null;\n const [, kind, name] = match as unknown as [string, string, string];\n if (kind === 'tier') {\n return isInferenceTier(name) ? { kind: 'tier', name } : null;\n }\n if (!INFERENCE_BINDING_NAME_PATTERN.test(name)) return null;\n return { kind: kind as 'alias' | 'fusion', name };\n}\n\n/** The binding id of one tier. */\nexport function tierBinding(tier: InferenceTier): `tier:${InferenceTier}` {\n return `tier:${tier}`;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction text(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() !== ''\n ? value.trim()\n : undefined;\n}\n\n/**\n * Key-shaped members are never stored in a binding row: keys live only in\n * smrt-secrets, written by the host (#4754 storage split). `apiKeyEnv` (the\n * name of a variable) is allowed, and so is `key`: #4753 fusion\n * definitions carry one as their identifier.\n */\nconst KEY_MEMBERS = new Set([\n 'apikey',\n 'api_key',\n 'apikeys',\n 'secret',\n 'token',\n 'password',\n 'authorization',\n 'bearer',\n]);\n\n/**\n * A deep copy of `value` with every credential-shaped member removed. Used\n * on every config-backed value a settings screen receives: config tiers and\n * alias candidates may legitimately carry a literal `apiKey` (resolvers and\n * endpoint-bound config), and none of it may reach a browser.\n */\nexport function stripKeyMembers<T>(value: T): T {\n if (Array.isArray(value)) return value.map(stripKeyMembers) as T;\n if (!isRecord(value)) return value;\n const out: Record<string, unknown> = {};\n for (const [member, inner] of Object.entries(value)) {\n if (KEY_MEMBERS.has(member.toLowerCase())) continue;\n out[member] = stripKeyMembers(inner);\n }\n return out as T;\n}\n\n/** Dotted path of the first key-shaped member anywhere in `value`, or `null`. */\nexport function findKeyMember(value: unknown, path = ''): string | null {\n if (Array.isArray(value)) {\n for (const [index, item] of value.entries()) {\n const found = findKeyMember(item, `${path}[${index}]`);\n if (found) return found;\n }\n return null;\n }\n if (!isRecord(value)) return null;\n for (const [member, inner] of Object.entries(value)) {\n const here = path ? `${path}.${member}` : member;\n if (KEY_MEMBERS.has(member.toLowerCase())) return here;\n const found = findKeyMember(inner, here);\n if (found) return found;\n }\n return null;\n}\n\n/**\n * The tier target a settings write may store. A tier binds exactly one of:\n *\n * - a model: `{ provider?, model }` (no `baseUrl`: an endpoint needs its own\n * key, and keys are never stored here; bind an alias whose candidate names\n * `baseUrl` + `apiKeyEnv` or a preset instead);\n * - an alias: `{ alias }`;\n * - a fusion (#4753): `{ kind: 'fusion', fusion }`.\n *\n * `null` means \"same as default\" (inherit the lower layer).\n */\nexport function normalizeTierTarget(\n binding: string,\n value: unknown,\n): InferenceTarget | null {\n if (value === null || value === undefined) return null;\n if (!isRecord(value)) {\n throw new InferenceBindingValueError(binding, 'a tier target is an object');\n }\n const keyMember = findKeyMember(value);\n if (keyMember) {\n throw new InferenceBindingValueError(\n binding,\n `\"${keyMember}\" looks like a credential; keys are stored only through smrt-secrets`,\n );\n }\n const fusion = text(value.fusion);\n const alias = text(value.alias);\n const model = text(value.model);\n const provider = text(value.provider);\n const kind = value.kind;\n const allowed = new Set(['kind', 'fusion', 'alias', 'model', 'provider']);\n const extra = Object.keys(value).filter((member) => !allowed.has(member));\n if (extra.length > 0) {\n throw new InferenceBindingValueError(\n binding,\n `unsupported member(s) ${extra.join(', ')} (endpoints and inline candidates belong in an alias)`,\n );\n }\n const shapes = [fusion, alias, model].filter(Boolean).length;\n if (shapes !== 1) {\n throw new InferenceBindingValueError(\n binding,\n 'a tier target is exactly one of { model, provider? }, { alias } or { kind: \"fusion\", fusion }',\n );\n }\n if (fusion) {\n if (kind !== undefined && kind !== 'fusion') {\n throw new InferenceBindingValueError(binding, 'fusion kind mismatch');\n }\n if (!INFERENCE_BINDING_NAME_PATTERN.test(fusion)) {\n throw new InferenceBindingValueError(binding, 'invalid fusion name');\n }\n return { kind: 'fusion', fusion } as unknown as InferenceTarget;\n }\n if (alias) {\n if (kind !== undefined && kind !== 'alias') {\n throw new InferenceBindingValueError(binding, 'alias kind mismatch');\n }\n if (!INFERENCE_BINDING_NAME_PATTERN.test(alias)) {\n throw new InferenceBindingValueError(binding, 'invalid alias name');\n }\n return { alias };\n }\n if (kind !== undefined && kind !== 'single') {\n throw new InferenceBindingValueError(binding, 'model kind mismatch');\n }\n return provider ? { provider, model } : { model };\n}\n\n/**\n * An alias definition a settings write may store: `{ candidates, description? }`,\n * every candidate exactly one of provider/model, preset/model or alias, and\n * never a literal key (`apiKeyEnv` names a variable and needs `baseUrl`).\n * Structural checks only; cross-references are checked against the layered\n * config by the settings service.\n */\nexport function normalizeAliasDefinition(\n binding: string,\n value: unknown,\n): InferenceAlias | null {\n if (value === null || value === undefined) return null;\n if (!isRecord(value) || !Array.isArray(value.candidates)) {\n throw new InferenceBindingValueError(\n binding,\n 'an alias is { candidates: [...], description? }',\n );\n }\n const keyMember = findKeyMember(value);\n if (keyMember) {\n throw new InferenceBindingValueError(\n binding,\n `\"${keyMember}\" looks like a credential; keys are stored only through smrt-secrets`,\n );\n }\n if (value.candidates.length === 0) {\n throw new InferenceBindingValueError(binding, 'an alias needs candidates');\n }\n const allowed = new Set([\n 'provider',\n 'model',\n 'baseUrl',\n 'apiKeyEnv',\n 'preset',\n 'alias',\n 'params',\n ]);\n const candidates: InferenceCandidate[] = value.candidates.map(\n (candidate: unknown, index: number) => {\n if (!isRecord(candidate)) {\n throw new InferenceBindingValueError(\n binding,\n `candidates[${index}] must be an object`,\n );\n }\n const extra = Object.keys(candidate).filter(\n (member) => !allowed.has(member),\n );\n if (extra.length > 0) {\n throw new InferenceBindingValueError(\n binding,\n `candidates[${index}] has unsupported member(s) ${extra.join(', ')}`,\n );\n }\n const out: InferenceCandidate = {};\n for (const member of allowed) {\n if (member === 'params') continue;\n const inner = text(candidate[member]);\n if (inner) (out as Record<string, string>)[member] = inner;\n }\n if (candidate.params !== undefined) {\n if (!isRecord(candidate.params)) {\n throw new InferenceBindingValueError(\n binding,\n `candidates[${index}].params must be an object`,\n );\n }\n out.params = { ...candidate.params };\n }\n return out;\n },\n );\n const description = text(value.description);\n return description ? { candidates, description } : { candidates };\n}\n\n/**\n * A fusion config entry (#4753) a settings write may store: a JSON object\n * with no credential members. Its meaning belongs to the fusion interpreter;\n * this package only stores it (app scope only).\n */\nexport function normalizeFusionDefinition(\n binding: string,\n value: unknown,\n): Record<string, unknown> | null {\n if (value === null || value === undefined) return null;\n if (!isRecord(value)) {\n throw new InferenceBindingValueError(binding, 'a fusion is a JSON object');\n }\n const keyMember = findKeyMember(value);\n if (keyMember) {\n throw new InferenceBindingValueError(\n binding,\n `\"${keyMember}\" looks like a credential; keys are stored only through smrt-secrets`,\n );\n }\n return JSON.parse(JSON.stringify(value)) as Record<string, unknown>;\n}\n\n/** `default-tier` stores `{ tier }`. */\nexport function normalizeDefaultTier(\n binding: string,\n value: unknown,\n): InferenceTier | null {\n if (value === null || value === undefined) return null;\n const tier = isRecord(value) ? value.tier : value;\n if (!isInferenceTier(tier)) {\n throw new InferenceBindingValueError(\n binding,\n `the default tier must be one of ${INFERENCE_TIERS.join(', ')}`,\n );\n }\n return tier;\n}\n\n/** The stored transcript setting (#4781): core's app settings layer. */\nexport interface InferenceTranscriptSettingsValue {\n enabled?: boolean;\n /** Success capture rate, 0..1. */\n sample?: number;\n /** Per-row byte cap. */\n maxBytes?: number;\n}\n\nconst TRANSCRIPT_SETTING_MEMBERS = new Set(['enabled', 'sample', 'maxBytes']);\n\n/**\n * `transcripts` stores `{ enabled?, sample?, maxBytes? }`, validated by\n * core's `assertInferenceTranscriptsConfig()`. Per-prompt sampling stays in\n * config; any other member is refused.\n */\nexport function normalizeTranscriptSettings(\n binding: string,\n value: unknown,\n): InferenceTranscriptSettingsValue | null {\n if (value === null || value === undefined) return null;\n if (!isRecord(value)) {\n throw new InferenceBindingValueError(\n binding,\n 'the transcript setting must be an object',\n );\n }\n const extra = Object.keys(value).filter(\n (member) => !TRANSCRIPT_SETTING_MEMBERS.has(member),\n );\n if (extra.length > 0) {\n throw new InferenceBindingValueError(\n binding,\n `the transcript setting carries enabled, sample and maxBytes only (got ${extra.join(', ')})`,\n );\n }\n try {\n assertInferenceTranscriptsConfig(value);\n } catch (error) {\n throw new InferenceBindingValueError(\n binding,\n error instanceof Error ? error.message : String(error),\n );\n }\n const out: InferenceTranscriptSettingsValue = {};\n if (value.enabled !== undefined) out.enabled = value.enabled as boolean;\n if (value.sample !== undefined) out.sample = value.sample as number;\n if (value.maxBytes !== undefined) out.maxBytes = value.maxBytes as number;\n return Object.keys(out).length > 0 ? out : null;\n}\n\n/**\n * The allow-list reference of a tier target: `model:<provider>/<model>`\n * (`model:default/<model>` without a provider), `alias:<name>` or\n * `fusion:<name>`; for the default tier, `tier:<name>`. Key-free.\n */\nexport function inferenceTargetRef(target: InferenceTarget): string {\n const kind = inferenceTargetKind(target);\n const record = target as InferenceTarget & { fusion?: string };\n if (kind === 'fusion') return `fusion:${record.fusion ?? ''}`;\n if (kind === 'alias') return `alias:${target.alias ?? ''}`;\n if (kind === 'candidates') return 'candidates';\n return `model:${target.provider ?? 'default'}/${target.model ?? 'default'}`;\n}\n\n/**\n * Whether `ref` matches one allow-list pattern. A pattern is an exact\n * reference, a prefix ending in `*` (`model:openai/*`, `alias:*`), or `*`.\n */\nexport function matchesAllowPattern(ref: string, pattern: string): boolean {\n if (pattern === '*') return true;\n if (pattern.endsWith('*')) return ref.startsWith(pattern.slice(0, -1));\n return ref === pattern;\n}\n\n/**\n * The tenant allow-list check: a tenant may bind only a reference the app\n * row's list matches. No list (or an empty one) allows nothing, so a tenant\n * cannot bind a tier the operator has not opened.\n */\nexport function isAllowedByList(\n ref: string,\n allowList: readonly string[] | null | undefined,\n): boolean {\n return (allowList ?? []).some((pattern) => matchesAllowPattern(ref, pattern));\n}\n\n/** Validate and normalize an allow-list (patterns are short, key-free references). */\nexport function normalizeAllowList(\n binding: string,\n value: unknown,\n): string[] | null {\n if (value === null || value === undefined) return null;\n if (!Array.isArray(value)) {\n throw new InferenceBindingValueError(\n binding,\n 'an allow-list is an array of references',\n );\n }\n const out: string[] = [];\n for (const entry of value) {\n if (\n typeof entry !== 'string' ||\n !/^(\\*|(model|alias|fusion|tier):[A-Za-z0-9._/:*-]{1,160})$/.test(entry)\n ) {\n throw new InferenceBindingValueError(\n binding,\n `invalid allow-list entry ${JSON.stringify(entry)}`,\n );\n }\n if (!out.includes(entry)) out.push(entry);\n }\n return out;\n}\n\n/** Key-free description of a tier target for screens and reports. */\nexport function describeInferenceTarget(\n target: InferenceTarget | null | undefined,\n): string {\n if (!target || Object.keys(target).length === 0) {\n return 'Same as default (configured client)';\n }\n const kind = inferenceTargetKind(target);\n const record = target as InferenceTarget & { fusion?: string };\n if (kind === 'fusion') return `Fusion ${record.fusion ?? '?'}`;\n if (kind === 'alias') return `Alias ${target.alias ?? '?'}`;\n if (kind === 'candidates') {\n return `${target.candidates?.length ?? 0} inline candidate(s)`;\n }\n return `${target.provider ?? 'default'}/${target.model ?? 'default model'}`;\n}\n","import {\n field,\n SmrtObject,\n type SmrtObjectOptions,\n type SmrtSaveOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { bumpInferenceOverrideGeneration } from '../settings/cache.js';\nimport {\n InferenceBindingValueError,\n normalizeAliasDefinition,\n normalizeAllowList,\n normalizeDefaultTier,\n normalizeFusionDefinition,\n normalizeTierTarget,\n normalizeTranscriptSettings,\n parseInferenceBinding,\n} from '../settings/targets.js';\n\n/** `context` (and `scopeId`) of the app-wide row. */\nexport const APP_INFERENCE_SCOPE_ID = '__app__';\n\nexport interface InferenceOverrideOptions extends SmrtObjectOptions {\n binding?: string;\n tenantId?: string | null;\n target?: string | null;\n tenantAllowList?: string | null;\n}\n\nfunction parseJson(binding: string, raw: string | null, what: string): unknown {\n if (raw === null) return null;\n try {\n return JSON.parse(raw);\n } catch {\n throw new InferenceBindingValueError(binding, `${what} is not valid JSON`);\n }\n}\n\n// One row per (binding, scope): `tier:<tier>`, `default-tier`,\n// `alias:<name>` or `fusion:<name>`, at the app scope (`tenantId = null`) or\n// one tenant's. Rows are authorization state for every scope and `tenantId`\n// is an ordinary settable field, not request-derived tenant scoping, so the\n// generated REST, CLI and MCP surfaces are closed (mirrors\n// `_smrt_prompt_overrides`, #3013). Hosts write through\n// `InferenceOverrideService`, which asks a host authorizer before every write\n// and fails closed. Keys never live here: `save()` refuses any credential-\n// shaped member; keys go to smrt-secrets through the host.\n@smrt({\n tableName: '_smrt_inference_overrides',\n conflictColumns: ['binding', 'context'],\n api: false,\n cli: false,\n mcp: false,\n indexes: [\n {\n name: 'idx_smrt_inference_overrides_tenant',\n columns: ['tenantId', 'binding'],\n },\n ],\n})\nexport class InferenceOverride extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-inference\";\n @field({ required: true })\n binding: string = '';\n\n // Not tenant-scoped (any scope is writable through the authorized service),\n // but the value is a tenant id: uuid on PostgreSQL, like every tenant_id.\n @field({ sqlType: 'UUID', nullable: true })\n tenantId: string | null = null;\n\n /**\n * JSON of the bound value; `null` inherits the lower layer. Tier rows hold\n * a tier target, `default-tier` holds `{ tier }`, alias rows an alias, and\n * fusion rows a fusion config entry.\n */\n @field({ type: 'text', nullable: true })\n target: string | null = null;\n\n /**\n * App rows of `tier:*` and `default-tier` only: JSON array of the target\n * references a tenant may bind (`model:openai/*`, `alias:local`,\n * `fusion:review-pair`, `tier:good`, `*`). No list allows nothing. This is\n * the stored half of the tenant allow-list; the service's\n * `tenantTargetPolicy` hook is the code half (#4667 caps the maximum\n * hosted tier there).\n */\n @field({ type: 'text', nullable: true })\n tenantAllowList: string | null = null;\n\n constructor(options: InferenceOverrideOptions = {}) {\n super(options);\n if (options.binding !== undefined) this.binding = options.binding;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.target !== undefined) this.target = options.target;\n if (options.tenantAllowList !== undefined) {\n this.tenantAllowList = options.tenantAllowList;\n }\n }\n\n /** The parsed, validated stored value (`null` inherits). */\n getValue(): unknown {\n return this.normalizedValue();\n }\n\n /** The parsed allow-list (`null` when the row carries none). */\n getTenantAllowList(): string[] | null {\n return normalizeAllowList(\n this.binding,\n parseJson(this.binding, this.tenantAllowList, 'tenantAllowList'),\n );\n }\n\n override async save(options?: SmrtSaveOptions): Promise<this> {\n this.validateOverride();\n // `context` is the uniqueness scope so the app row stays unique even\n // though `tenantId` is nullable (as in `_smrt_prompt_overrides`).\n this.context = this.tenantId ?? APP_INFERENCE_SCOPE_ID;\n const result = await super.save(options);\n bumpInferenceOverrideGeneration();\n return result;\n }\n\n override async delete(): Promise<void> {\n await super.delete();\n bumpInferenceOverrideGeneration();\n }\n\n private normalizedValue(): unknown {\n const parsed = parseInferenceBinding(this.binding);\n if (!parsed) {\n throw new InferenceBindingValueError(\n String(this.binding),\n 'unknown binding id',\n );\n }\n const raw = parseJson(this.binding, this.target, 'target');\n switch (parsed.kind) {\n case 'tier':\n return normalizeTierTarget(this.binding, raw);\n case 'default-tier':\n return normalizeDefaultTier(this.binding, raw);\n case 'transcripts':\n return normalizeTranscriptSettings(this.binding, raw);\n case 'alias':\n return normalizeAliasDefinition(this.binding, raw);\n case 'fusion':\n return normalizeFusionDefinition(this.binding, raw);\n }\n }\n\n private validateOverride(): void {\n const parsed = parseInferenceBinding(this.binding);\n if (!parsed) {\n throw new InferenceBindingValueError(\n String(this.binding),\n 'unknown binding id',\n );\n }\n if (this.tenantId !== null) {\n if (\n typeof this.tenantId !== 'string' ||\n !this.tenantId.trim() ||\n this.tenantId !== this.tenantId.trim() ||\n this.tenantId === APP_INFERENCE_SCOPE_ID\n ) {\n throw new InferenceBindingValueError(this.binding, 'invalid tenant id');\n }\n // Tenants bind tiers to allow-listed targets; they never define what a\n // name means. A tenant alias or fusion could redefine an allow-listed\n // name and bypass the list.\n if (parsed.kind === 'alias' || parsed.kind === 'fusion') {\n throw new InferenceBindingValueError(\n this.binding,\n 'aliases and fusions are defined at the app scope only',\n );\n }\n // Tenants cannot override transcripts (owner decision, #4781).\n if (parsed.kind === 'transcripts') {\n throw new InferenceBindingValueError(\n this.binding,\n 'the transcript setting is app scope only',\n );\n }\n if (this.tenantAllowList !== null) {\n throw new InferenceBindingValueError(\n this.binding,\n 'the tenant allow-list lives on the app row',\n );\n }\n }\n if (\n this.tenantAllowList !== null &&\n parsed.kind !== 'tier' &&\n parsed.kind !== 'default-tier'\n ) {\n throw new InferenceBindingValueError(\n this.binding,\n 'only tier and default-tier bindings carry a tenant allow-list',\n );\n }\n const value = this.normalizedValue();\n this.target = value === null ? null : JSON.stringify(value);\n const allowList = this.getTenantAllowList();\n this.tenantAllowList =\n allowList === null ? null : JSON.stringify(allowList);\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport {\n APP_INFERENCE_SCOPE_ID,\n InferenceOverride,\n} from './InferenceOverride.js';\n\n/** `app` (the `__app__` row) or one tenant. */\nexport type InferenceOverrideScopeType = 'app' | 'tenant';\n\n/**\n * Reads and unauthorized writes of `_smrt_inference_overrides`. Hosts write\n * through `InferenceOverrideService` (authorizer-gated, fails closed); call\n * {@link setOverride} / {@link removeOverride} directly only after your own\n * authorization decision.\n */\nexport class InferenceOverrideCollection extends SmrtCollection<InferenceOverride> {\n static readonly _itemClass = InferenceOverride;\n\n /**\n * Every row of one scope, keyed by binding id: one bounded read. An\n * enumeration read: build the collection without list bounds (the\n * settings service does) so a host-wide `maxListLimit` cannot drop a row.\n */\n async getScopeMap(\n scopeType: InferenceOverrideScopeType,\n scopeId: string,\n ): Promise<Map<string, InferenceOverride>> {\n const tenantId = scopeType === 'app' ? null : scopeId;\n const rows = await this.list({ where: { tenantId } });\n const result = new Map<string, InferenceOverride>();\n for (const row of rows) result.set(row.binding, row);\n return result;\n }\n\n /** The row for one binding at one scope. */\n async findByScope(\n binding: string,\n scopeType: InferenceOverrideScopeType,\n scopeId: string,\n ): Promise<InferenceOverride | null> {\n const tenantId = scopeType === 'app' ? null : scopeId;\n const rows = await this.list({ where: { binding, tenantId }, limit: 1 });\n return rows[0] ?? null;\n }\n\n /**\n * Set the stored value (and, for app rows, optionally the tenant\n * allow-list) of one binding at one scope, creating the row when needed.\n * `undefined` leaves a column untouched; `null` clears it. A row left with\n * neither a value nor an allow-list is deleted.\n */\n async setOverride(\n binding: string,\n scopeType: InferenceOverrideScopeType,\n scopeId: string,\n change: { target?: unknown; tenantAllowList?: string[] | null },\n ): Promise<InferenceOverride | null> {\n const existing = await this.findByScope(binding, scopeType, scopeId);\n const target =\n change.target === undefined\n ? (existing?.target ?? null)\n : change.target === null\n ? null\n : JSON.stringify(change.target);\n const tenantAllowList =\n change.tenantAllowList === undefined\n ? (existing?.tenantAllowList ?? null)\n : change.tenantAllowList === null\n ? null\n : JSON.stringify(change.tenantAllowList);\n\n if (target === null && tenantAllowList === null) {\n if (existing) await existing.delete();\n return null;\n }\n if (existing) {\n existing.target = target;\n existing.tenantAllowList = tenantAllowList;\n await existing.save();\n return existing;\n }\n const created = await this.create({\n binding,\n tenantId: scopeType === 'app' ? null : scopeId,\n target,\n tenantAllowList,\n });\n await created.save();\n return created;\n }\n\n /** Delete the row for one binding at one scope, if any. */\n async removeOverride(\n binding: string,\n scopeType: InferenceOverrideScopeType,\n scopeId: string,\n ): Promise<boolean> {\n const existing = await this.findByScope(binding, scopeType, scopeId);\n if (!existing) return false;\n await existing.delete();\n return true;\n }\n}\n\nexport { APP_INFERENCE_SCOPE_ID };\n"],"mappings":"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"}
|
|
@@ -1514,15 +1514,15 @@ var FusionRun = class {
|
|
|
1514
1514
|
function isDegraded(seat) {
|
|
1515
1515
|
return seat.status === "failed" || seat.status === "ok" && (seat.substitutions?.length ?? 0) > 0;
|
|
1516
1516
|
}
|
|
1517
|
-
function
|
|
1518
|
-
|
|
1519
|
-
const definition = key ? lookupFusion(key, context.config) : void 0;
|
|
1520
|
-
if (!definition) throw new InferenceConfigError([{
|
|
1517
|
+
function unknownFusion(context, key) {
|
|
1518
|
+
return new InferenceConfigError([{
|
|
1521
1519
|
path: `resolution.${context.resolution.tierResolved}`,
|
|
1522
1520
|
code: "fusion-unknown",
|
|
1523
1521
|
message: `tier "${context.resolution.tierResolved}" names unknown fusion "${key}"`
|
|
1524
1522
|
}]);
|
|
1525
|
-
|
|
1523
|
+
}
|
|
1524
|
+
function validatedFusion(context, key, definition, origin) {
|
|
1525
|
+
const path = origin === "config" ? `inference.fusions.${key}` : origin === "resolver" ? `resolver.fusions.${key}` : `fusions.${key}`;
|
|
1526
1526
|
const issues = [...findFusionDefinitionIssues(definition, path, context.config), ...findFusionDefinitionPromptIssues(definition, path)];
|
|
1527
1527
|
if (issues.length > 0) throw new InferenceConfigError(issues);
|
|
1528
1528
|
return {
|
|
@@ -1530,8 +1530,26 @@ function fusionFor(context) {
|
|
|
1530
1530
|
definition
|
|
1531
1531
|
};
|
|
1532
1532
|
}
|
|
1533
|
-
function
|
|
1534
|
-
const
|
|
1533
|
+
function fusionForSync(context) {
|
|
1534
|
+
const key = String(context.resolution.target.fusion ?? "").trim();
|
|
1535
|
+
const definition = key ? lookupFusion(key, context.config) : void 0;
|
|
1536
|
+
if (!definition) throw unknownFusion(context, key);
|
|
1537
|
+
return validatedFusion(context, key, definition, Object.hasOwn(context.config?.fusions ?? {}, key) ? "config" : "registry");
|
|
1538
|
+
}
|
|
1539
|
+
async function fusionFor(context) {
|
|
1540
|
+
if (typeof context.lookupFusion !== "function") return fusionForSync(context);
|
|
1541
|
+
const key = String(context.resolution.target.fusion ?? "").trim();
|
|
1542
|
+
if (!key) throw unknownFusion(context, key);
|
|
1543
|
+
const found = await context.lookupFusion(key);
|
|
1544
|
+
if (found) return validatedFusion(context, key, found.definition, found.source);
|
|
1545
|
+
const registered = fusionStore().get(key);
|
|
1546
|
+
if (!registered) throw unknownFusion(context, key);
|
|
1547
|
+
return validatedFusion(context, key, registered, "registry");
|
|
1548
|
+
}
|
|
1549
|
+
async function runFusion(context, input, callerOptions = {}, options = {}) {
|
|
1550
|
+
return runResolvedFusion(context, await fusionFor(context), input, callerOptions, options);
|
|
1551
|
+
}
|
|
1552
|
+
function runResolvedFusion(context, { key, definition }, input, callerOptions, options) {
|
|
1535
1553
|
return new FusionRun(context, key, definition, callerOptions, {
|
|
1536
1554
|
retryDelayMs: options.retryDelayMs ?? (() => 200 + Math.random() * 300),
|
|
1537
1555
|
sleep: options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
|
|
@@ -1542,11 +1560,15 @@ function unsupported(method) {
|
|
|
1542
1560
|
throw new TypeError(`A fusion target runs through message(); ${method}() is not supported (#4753).`);
|
|
1543
1561
|
}
|
|
1544
1562
|
function createFusionClient(context, options = {}) {
|
|
1545
|
-
|
|
1563
|
+
let fusion;
|
|
1564
|
+
if (typeof context.lookupFusion === "function") {
|
|
1565
|
+
fusion = fusionFor(context);
|
|
1566
|
+
fusion.catch(() => {});
|
|
1567
|
+
} else fusion = Promise.resolve(fusionForSync(context));
|
|
1546
1568
|
return {
|
|
1547
1569
|
async message(text, callerOptions) {
|
|
1548
1570
|
const { onFusionResult, ...rest } = callerOptions ?? {};
|
|
1549
|
-
const result = await
|
|
1571
|
+
const result = await runResolvedFusion(context, await fusion, text, rest, options);
|
|
1550
1572
|
if (typeof onFusionResult === "function") onFusionResult(result);
|
|
1551
1573
|
return result.text;
|
|
1552
1574
|
},
|
|
@@ -1582,6 +1604,10 @@ function createInferenceResolver(base) {
|
|
|
1582
1604
|
const mode = base.mode.bind(base);
|
|
1583
1605
|
resolver.mode = (request) => mode(request);
|
|
1584
1606
|
}
|
|
1607
|
+
if (base && typeof base.lookupFusion === "function") {
|
|
1608
|
+
const lookupFusion = base.lookupFusion.bind(base);
|
|
1609
|
+
resolver.lookupFusion = (key, request, context) => lookupFusion(key, request, context);
|
|
1610
|
+
}
|
|
1585
1611
|
resolver[OWN_RESOLVER] = true;
|
|
1586
1612
|
return resolver;
|
|
1587
1613
|
}
|
|
@@ -1842,6 +1868,6 @@ function inferenceProviderConfigSecretName(scope) {
|
|
|
1842
1868
|
return userId ? `${INFERENCE_PROVIDER_CONFIG_SECRET_TYPE}:user:${userId}` : `${INFERENCE_PROVIDER_CONFIG_SECRET_TYPE}:tenant`;
|
|
1843
1869
|
}
|
|
1844
1870
|
//#endregion
|
|
1845
|
-
export { candidateLabel as A,
|
|
1871
|
+
export { candidateLabel as A, InferenceConfigError as B, createChainClient as C, EXECUTOR_KINDS as D, CHAIN_KINDS as E, findFusionPromptIssues as F, InferenceFusionError as H, findInferenceConfigIssues as I, hasInferenceChains as L, expandTarget as M, findFusionDefinitionIssues as N, FUSION_GATE_CONTINUE as O, findFusionDefinitionPromptIssues as P, validateInferenceConfig as R, setFusionPromptResolver as S, isInferenceAbort as T, InferenceExhaustedError as V, createFusionClient as _, inferenceProviderBindingFor as a, listFusions as b, inferenceProviderTargets as c, redactInferenceTarget as d, createInferenceResolver as f, createChainExecutor as g, clearFusions as h, InferenceProviderConfigError as i, candidateToStep as j, FUSION_SEAT_COUNTS as k, parseInferenceProviderConfig as l, registerInference as m, INFERENCE_PROVIDER_CONFIG_SOURCES as n, inferenceProviderConfigSecretName as o, isInferenceRegistered as p, INFERENCE_PROVIDER_PARAM_KEYS as r, inferenceProviderSource as s, INFERENCE_PROVIDER_CONFIG_SECRET_TYPE as t, redactInferenceProviderConfig as u, defineFusion as v, classifyInferenceFailure as w, runFusion as x, getFusion as y, validateInferenceTarget as z };
|
|
1846
1872
|
|
|
1847
|
-
//# sourceMappingURL=provider-config-
|
|
1873
|
+
//# sourceMappingURL=provider-config-BSpIQRVM.js.map
|