@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/dist/register.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAKL,KAAK,iBAAiB,EAGvB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAO1D;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,CAAC,EAAE,iBAAiB,GACvB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAKL,KAAK,iBAAiB,EAGvB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAO1D;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,CAAC,EAAE,iBAAiB,GACvB,iBAAiB,CA8BnB;AAED,6CAA6C;AAC7C,MAAM,WAAW,wBAAwB;IACvC,uDAAuD;IACvD,QAAQ,CAAC,EAAE,oBAAoB,CAAC;CACjC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,GAAE,wBAA6B,GACrC,IAAI,CASN;AAED,+DAA+D;AAC/D,wBAAgB,qBAAqB,IAAI,OAAO,CAG/C"}
|
package/dist/resolver.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"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"}
|
package/dist/resolver.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as inferenceProviderBindingFor, c as inferenceProviderTargets, d as redactInferenceTarget, f as createInferenceResolver, i as InferenceProviderConfigError, l as parseInferenceProviderConfig, n as INFERENCE_PROVIDER_CONFIG_SOURCES, o as inferenceProviderConfigSecretName, r as INFERENCE_PROVIDER_PARAM_KEYS, s as inferenceProviderSource, t as INFERENCE_PROVIDER_CONFIG_SECRET_TYPE, u as redactInferenceProviderConfig } from "./chunks/provider-config-
|
|
1
|
+
import { a as inferenceProviderBindingFor, c as inferenceProviderTargets, d as redactInferenceTarget, f as createInferenceResolver, i as InferenceProviderConfigError, l as parseInferenceProviderConfig, n as INFERENCE_PROVIDER_CONFIG_SOURCES, o as inferenceProviderConfigSecretName, r as INFERENCE_PROVIDER_PARAM_KEYS, s as inferenceProviderSource, t as INFERENCE_PROVIDER_CONFIG_SECRET_TYPE, u as redactInferenceProviderConfig } from "./chunks/provider-config-BSpIQRVM.js";
|
|
2
2
|
import { isInferenceTargetSource, resolveAutoTier, setInferenceResolver } from "@happyvertical/smrt-core";
|
|
3
3
|
import { SecretService } from "@happyvertical/smrt-secrets";
|
|
4
4
|
import { getCurrentTenant, withTenant } from "@happyvertical/smrt-tenancy";
|
|
@@ -203,6 +203,9 @@ function createUserInferenceResolver(options = {}) {
|
|
|
203
203
|
unavailable: "off"
|
|
204
204
|
} : { mode: chosen.mode };
|
|
205
205
|
},
|
|
206
|
+
async lookupFusion(key, request, context) {
|
|
207
|
+
return options.fallback?.lookupFusion?.(key, request, context);
|
|
208
|
+
},
|
|
206
209
|
async resolveBindings(bindingOptions = {}) {
|
|
207
210
|
const redact = bindingOptions.redact !== false;
|
|
208
211
|
const { tenantId, userId } = identityFor({});
|
package/dist/resolver.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolver.js","names":["userId","target","source"],"sources":["../src/resolver.ts"],"sourcesContent":["/**\n * `@happyvertical/smrt-inference/resolver` — per-user and per-tenant\n * inference bindings over smrt-secrets (#4756). Node-only: it reads secrets\n * and the ambient principal, so it stays off the browser-safe root entry.\n *\n * Precedence for each tier, highest first:\n * 1. the current user's stored `inference.provider-config` (tier binding,\n * then the user's default binding);\n * 2. the tenant's stored config (same order);\n * 3. `fallback` (another resolver), else the app's `inference` block;\n * 4. unbound: the configured client (`SMRT_AI_*` / the process `ai` block).\n * Every resolution carries `target.source` (`byo | local | hosted | app`),\n * which core records as the `source` usage tag.\n *\n * Inference mode (#4757): a stored config may carry `mode`. The user's mode,\n * else the tenant's, applies: `off` answers every tier (and untiered calls,\n * through `mode()`) as unavailable; `byo` / `local` / `hosted` bind only a\n * stored binding of that source (user, then tenant) and report\n * `unconfigured` when there is none, never falling through to app config.\n */\nimport {\n type InferenceMode,\n type InferenceModeRequest,\n type InferenceModeState,\n type InferenceRequest,\n type InferenceResolution,\n type InferenceResolver,\n type InferenceResolverContext,\n type InferenceTarget,\n type InferenceTier,\n isInferenceTargetSource,\n resolveAutoTier,\n setInferenceResolver,\n} from '@happyvertical/smrt-core';\nimport { SecretService } from '@happyvertical/smrt-secrets';\nimport { getCurrentTenant, withTenant } from '@happyvertical/smrt-tenancy';\nimport { getCurrentSessionPermissionContext } from '@happyvertical/smrt-users';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport {\n INFERENCE_PROVIDER_CONFIG_SECRET_TYPE,\n type InferenceProviderConfig,\n inferenceProviderConfigSecretName,\n inferenceProviderTargets,\n parseInferenceProviderConfig,\n type RedactedInferenceProviderConfig,\n redactInferenceProviderConfig,\n redactInferenceTarget,\n} from './provider-config.js';\nimport { createInferenceResolver } from './register.js';\n\nexport * from './provider-config.js';\n\n/** `invalidate()` of every live resolver this process created. */\nconst liveResolvers = new Set<WeakRef<UserInferenceResolver['invalidate']>>();\n\n/**\n * Drop cached configs in every user resolver this process created (all, one\n * tenant, or one user). Writers outside a resolver (hosted provisioning,\n * #4759) call this after storing a config; other processes see the change\n * when their cache entry expires (`ttlMs`, 30 s by default).\n */\nexport function invalidateUserInferenceResolvers(scope?: {\n tenantId?: string;\n userId?: string;\n}): void {\n for (const ref of [...liveResolvers]) {\n const invalidate = ref.deref();\n if (invalidate) invalidate(scope);\n else liveResolvers.delete(ref);\n }\n}\n\n/** Default cache lifetime of decrypted configs, per `(tenantId, userId)`. */\nexport const USER_INFERENCE_CACHE_TTL_MS = 30_000;\n\n/**\n * The subset of `SecretService` the resolver uses. Reads go through\n * `retrieve()` (audited, access-counted); writes through `store()`.\n */\nexport interface InferenceSecretStore {\n retrieve(name: string): Promise<{ value: string }>;\n store(\n name: string,\n value: string,\n options?: { category?: string; description?: string },\n ): Promise<unknown>;\n delete(name: string): Promise<boolean>;\n}\n\n/** Who a resolution acts for. */\nexport interface InferencePrincipal {\n tenantId: string;\n /** Absent for service principals and background jobs: no user layer. */\n userId?: string;\n}\n\n/** Options for {@link createUserInferenceResolver}. */\nexport interface UserInferenceResolverOptions {\n /** Database for a `SecretService` built on first use (when no `secrets`). */\n db?: DatabaseInterface;\n /** The secret store; defaults to `SecretService.create({ db })`. */\n secrets?: InferenceSecretStore | SecretService;\n /**\n * Resolver for tiers neither the user nor the tenant binds. Default: the\n * app's `inference` block through core's built-in resolution.\n */\n fallback?: InferenceResolver;\n /** Cache lifetime in ms (default {@link USER_INFERENCE_CACHE_TTL_MS}). */\n ttlMs?: number;\n /** Clock seam for tests. */\n now?: () => number;\n /**\n * Principal seam for tests and hosts with their own identity. Default:\n * the ambient principal, read the way smrt-preferences reads it.\n */\n principal?: () => InferencePrincipal | undefined;\n /**\n * Let a request for another tenant than the principal's (an object of\n * tenant B used by a principal of tenant A) read tenant B's stored config.\n * Default `false`: such a request skips the stored layers and falls through\n * to the app config, because reading B's key for A's action is a\n * cross-tenant read. Principal-less contexts (jobs) always use the\n * request's tenant.\n */\n allowCrossTenant?: boolean;\n}\n\n/** The stored layers for one `(tenantId, userId)`. */\ninterface Layers {\n user?: InferenceProviderConfig;\n tenant?: InferenceProviderConfig;\n}\n\n/** The explicit mode that applies: the user's, else the tenant's. */\nfunction explicitMode(\n layers: Layers,\n): { mode: InferenceMode; layer: 'user' | 'tenant' } | undefined {\n if (layers.user?.mode) return { mode: layers.user.mode, layer: 'user' };\n if (layers.tenant?.mode) return { mode: layers.tenant.mode, layer: 'tenant' };\n return undefined;\n}\n\n/** One tier of {@link InferenceBindingMap}. */\nexport type InferenceBindingEntry = (\n | InferenceTarget\n | (Omit<InferenceTarget, 'apiKey'> & { hasApiKey: boolean })\n) & { layer: 'user' | 'tenant' };\n\n/** What {@link UserInferenceResolver.resolveBindings} returns. */\nexport interface InferenceBindingMap {\n tenantId?: string;\n userId?: string;\n /** Per tier: the stored binding that answers it (absent: app config). */\n tiers: Partial<Record<InferenceTier, InferenceBindingEntry>>;\n /** The stored configs themselves, redacted unless `redact: false`. */\n user?: InferenceProviderConfig | RedactedInferenceProviderConfig;\n tenant?: InferenceProviderConfig | RedactedInferenceProviderConfig;\n /** The explicit mode that applies (#4757) and the layer that set it. */\n mode?: { mode: InferenceMode; layer: 'user' | 'tenant' };\n}\n\n/** The scope a config is written to. */\nexport type InferenceConfigLevel = 'user' | 'tenant';\n\n/** A resolver plus the read/write helpers the settings panel uses. */\nexport interface UserInferenceResolver extends InferenceResolver {\n /**\n * The tier → target map for the ambient principal (never a caller-chosen\n * tenant or user), without\n * keys unless `redact: false` (server-only; never send that to a client).\n */\n resolveBindings(options?: { redact?: boolean }): Promise<InferenceBindingMap>;\n /**\n * Validate and store a config for the current principal through\n * `SecretService.store()`, then invalidate the cache. `user` needs a\n * principal with a user id. Authorization (who may write the tenant\n * config) is the host's server action's job.\n */\n write(level: InferenceConfigLevel, config: unknown): Promise<void>;\n /** Delete the stored config at `level` for the current principal. */\n remove(level: InferenceConfigLevel): Promise<boolean>;\n /**\n * Set (or with `null` clear) the stored mode at `level` (#4757), keeping\n * the stored binding. Works without a key: a mode-only config is valid.\n * A config left with neither binding nor mode is deleted. Invalidates the\n * cache, so the next call sees it.\n */\n setMode(\n level: InferenceConfigLevel,\n mode: InferenceMode | null,\n ): Promise<void>;\n /** The explicit stored mode for untiered calls (core's `mode()` hook). */\n mode(request: InferenceModeRequest): Promise<InferenceModeState | undefined>;\n /**\n * Drop cached configs: all, one tenant's (every user), or one\n * `(tenantId, userId)`. Call after writing a config elsewhere.\n */\n invalidate(scope?: { tenantId?: string; userId?: string }): void;\n}\n\nfunction text(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() !== ''\n ? value.trim()\n : undefined;\n}\n\n/**\n * The ambient principal: the tenancy context first, then the users session\n * permission context (smrt-preferences' order). Never guessed.\n */\nexport function getAmbientInferencePrincipal(): InferencePrincipal | undefined {\n const tenant = getCurrentTenant();\n const tenantId = text(tenant?.tenantId);\n if (tenantId) {\n const userId = text(tenant?.userId);\n return userId ? { tenantId, userId } : { tenantId };\n }\n const session = getCurrentSessionPermissionContext();\n const sessionTenant = text(session?.tenantId);\n if (!sessionTenant) return undefined;\n const userId = text(session?.userId);\n return userId\n ? { tenantId: sessionTenant, userId }\n : { tenantId: sessionTenant };\n}\n\nfunction isAbsentSecretError(error: unknown, name: string): boolean {\n if (!(error instanceof Error)) return false;\n return (\n error.message === `Secret '${name}' not found` ||\n error.message === 'Secret not found' ||\n error.message === 'Secret disabled' ||\n error.message === 'Secret expired'\n );\n}\n\nconst cacheKey = (tenantId: string, userId: string | undefined) =>\n `${tenantId}\\u0000${userId ?? ''}`;\n\n/**\n * Create the per-user / per-tenant {@link InferenceResolver}. Register it\n * with {@link registerUserInferenceResolver} (keeps #4752's validation in\n * front) or `setInferenceResolver()`.\n */\nexport function createUserInferenceResolver(\n options: UserInferenceResolverOptions = {},\n): UserInferenceResolver {\n const ttlMs = options.ttlMs ?? USER_INFERENCE_CACHE_TTL_MS;\n const now = options.now ?? Date.now;\n const principalOf = options.principal ?? getAmbientInferencePrincipal;\n const cache = new Map<string, { expires: number; value: Promise<Layers> }>();\n let secretsPromise: Promise<InferenceSecretStore> | undefined;\n\n const getSecrets = (): Promise<InferenceSecretStore> => {\n if (options.secrets) return Promise.resolve(options.secrets);\n if (!options.db) {\n return Promise.reject(\n new Error(\n 'createUserInferenceResolver needs `secrets` or `db` to read stored inference configs.',\n ),\n );\n }\n secretsPromise ??= SecretService.create({ db: options.db }).catch(\n (error: unknown) => {\n secretsPromise = undefined;\n throw error;\n },\n );\n return secretsPromise;\n };\n\n /** Run `fn` in a tenancy context for `principal` (audit keeps the user). */\n const inTenant = <T>(\n principal: InferencePrincipal,\n fn: () => Promise<T>,\n ): Promise<T> => {\n const current = getCurrentTenant();\n if (\n text(current?.tenantId) === principal.tenantId &&\n text(current?.userId) === principal.userId\n ) {\n return fn();\n }\n return withTenant(\n {\n tenantId: principal.tenantId,\n ...(principal.userId ? { userId: principal.userId } : {}),\n },\n fn,\n );\n };\n\n const readConfig = async (\n secrets: InferenceSecretStore,\n name: string,\n ): Promise<InferenceProviderConfig | undefined> => {\n let value: string;\n try {\n value = (await secrets.retrieve(name)).value;\n } catch (error) {\n if (isAbsentSecretError(error, name)) return undefined;\n throw error;\n }\n return parseInferenceProviderConfig(value);\n };\n\n const loadLayers = async (\n tenantId: string,\n userId: string | undefined,\n ): Promise<Layers> => {\n const secrets = await getSecrets();\n return inTenant({ tenantId, ...(userId ? { userId } : {}) }, async () => {\n const layers: Layers = {};\n if (userId) {\n layers.user = await readConfig(\n secrets,\n inferenceProviderConfigSecretName({ userId }),\n );\n }\n layers.tenant = await readConfig(\n secrets,\n inferenceProviderConfigSecretName({}),\n );\n return layers;\n });\n };\n\n const getLayers = (\n tenantId: string,\n userId: string | undefined,\n ): Promise<Layers> => {\n const key = cacheKey(tenantId, userId);\n const hit = cache.get(key);\n if (hit && hit.expires > now()) return hit.value;\n const value = loadLayers(tenantId, userId);\n const entry = { expires: now() + ttlMs, value };\n cache.set(key, entry);\n value.catch(() => {\n if (cache.get(key) === entry) cache.delete(key);\n });\n return value;\n };\n\n /**\n * Whose stored layers answer this request. With a principal: its tenant\n * (or the request's when they match), its user unless the request says\n * `userId: null` (a client shared beyond one user, e.g. a long-lived\n * agent). A request for another tenant reads nothing stored unless\n * `allowCrossTenant`. Without a principal: the request's tenant, no user.\n */\n const identityFor = (\n request: Pick<InferenceRequest, 'tenantId' | 'userId'>,\n ): { tenantId?: string; userId?: string } => {\n const principal = principalOf();\n const requested = text(request.tenantId);\n if (!principal) return requested ? { tenantId: requested } : {};\n if (requested && requested !== principal.tenantId) {\n return options.allowCrossTenant ? { tenantId: requested } : {};\n }\n const tenantId = principal.tenantId;\n const userId = request.userId === null ? undefined : text(principal.userId);\n return userId ? { tenantId, userId } : { tenantId };\n };\n\n const invalidate: UserInferenceResolver['invalidate'] = (scope) => {\n if (!scope?.tenantId) {\n cache.clear();\n return;\n }\n if (scope.userId) {\n cache.delete(cacheKey(scope.tenantId, scope.userId));\n return;\n }\n const prefix = `${scope.tenantId}\\u0000`;\n for (const key of [...cache.keys()]) {\n if (key.startsWith(prefix)) cache.delete(key);\n }\n };\n\n const writeScope = (level: InferenceConfigLevel): InferencePrincipal => {\n const principal = principalOf();\n if (!principal?.tenantId) {\n throw new Error(\n 'Writing an inference provider config needs an authenticated tenant context.',\n );\n }\n if (level === 'user' && !principal.userId) {\n throw new Error(\n 'Writing a user inference provider config needs a principal with a user id.',\n );\n }\n return principal;\n };\n\n liveResolvers.add(new WeakRef(invalidate));\n\n return {\n async resolve(\n request: InferenceRequest,\n context: InferenceResolverContext,\n ): Promise<InferenceResolution | undefined> {\n const tierResolved =\n request.tier === 'auto'\n ? resolveAutoTier(context.config)\n : request.tier;\n const { tenantId, userId } = identityFor(request);\n if (tenantId) {\n const layers = await getLayers(tenantId, userId);\n const chosen = explicitMode(layers);\n if (chosen?.mode === 'off') {\n return {\n tierResolved,\n target: {},\n via: 'resolver',\n mode: 'off',\n unavailable: 'off',\n };\n }\n const candidates = [\n inferenceProviderTargets(layers.user)[tierResolved],\n inferenceProviderTargets(layers.tenant)[tierResolved],\n ];\n if (chosen) {\n // An explicit mode binds only its own kind of stored binding.\n const target = candidates.find(\n (entry) => entry?.source === chosen.mode,\n );\n if (target) {\n return { tierResolved, target, via: 'resolver', mode: chosen.mode };\n }\n return {\n tierResolved,\n target: {},\n via: 'resolver',\n mode: 'off',\n unavailable: 'unconfigured',\n };\n }\n const target = candidates.find(Boolean);\n if (target) {\n const source = target.source;\n return {\n tierResolved,\n target,\n via: 'resolver',\n ...(source === 'byo' || source === 'local' || source === 'hosted'\n ? { mode: source }\n : {}),\n };\n }\n }\n const fallen =\n (await options.fallback?.resolve(request, context)) ??\n context.resolveFromConfig(request);\n const source = fallen.target?.source;\n return {\n ...fallen,\n target: {\n ...fallen.target,\n source: isInferenceTargetSource(source) ? source : 'app',\n },\n };\n },\n\n async mode(request) {\n const { tenantId, userId } = identityFor(request);\n if (!tenantId) return undefined;\n const chosen = explicitMode(await getLayers(tenantId, userId));\n if (!chosen) return undefined;\n return chosen.mode === 'off'\n ? { mode: 'off', unavailable: 'off' }\n : { mode: chosen.mode };\n },\n\n async resolveBindings(bindingOptions = {}) {\n const redact = bindingOptions.redact !== false;\n // Identity is ambient only: no caller-chosen tenant or user.\n const { tenantId, userId } = identityFor({});\n if (!tenantId) return { tiers: {} };\n const layers = await getLayers(tenantId, userId);\n const map: InferenceBindingMap = {\n tenantId,\n ...(userId ? { userId } : {}),\n tiers: {},\n };\n for (const layer of ['tenant', 'user'] as const) {\n const config = layers[layer];\n if (!config) continue;\n map[layer] = redact ? redactInferenceProviderConfig(config) : config;\n for (const [tier, target] of Object.entries(\n inferenceProviderTargets(config),\n )) {\n map.tiers[tier as InferenceTier] = {\n ...(redact ? redactInferenceTarget(target) : target),\n layer,\n };\n }\n }\n const chosen = explicitMode(layers);\n if (chosen) map.mode = chosen;\n return map;\n },\n\n async write(level, config) {\n const principal = writeScope(level);\n const parsed = parseInferenceProviderConfig(config);\n const secrets = await getSecrets();\n const name = inferenceProviderConfigSecretName(\n level === 'user' ? { userId: principal.userId } : {},\n );\n await inTenant(principal, () =>\n secrets.store(name, JSON.stringify(parsed), {\n category: INFERENCE_PROVIDER_CONFIG_SECRET_TYPE,\n description: `Inference provider config (${level})`,\n }),\n );\n invalidate(\n level === 'user'\n ? { tenantId: principal.tenantId, userId: principal.userId }\n : { tenantId: principal.tenantId },\n );\n },\n\n async remove(level) {\n const principal = writeScope(level);\n const secrets = await getSecrets();\n const name = inferenceProviderConfigSecretName(\n level === 'user' ? { userId: principal.userId } : {},\n );\n const removed = await inTenant(principal, () => secrets.delete(name));\n invalidate(\n level === 'user'\n ? { tenantId: principal.tenantId, userId: principal.userId }\n : { tenantId: principal.tenantId },\n );\n return removed;\n },\n\n async setMode(level, mode) {\n const principal = writeScope(level);\n const secrets = await getSecrets();\n const name = inferenceProviderConfigSecretName(\n level === 'user' ? { userId: principal.userId } : {},\n );\n // Read fresh (not the cache) so a concurrent binding write is kept.\n const current = await inTenant(principal, () =>\n readConfig(secrets, name),\n );\n const next: InferenceProviderConfig = { ...(current ?? {}) };\n if (mode === null) delete next.mode;\n else next.mode = parseInferenceProviderConfig({ mode }).mode;\n const empty = Object.keys(next).every(\n (key) =>\n key === 'version' || key === 'source' || key === 'gatewayKeyAlias',\n );\n await inTenant(principal, async () => {\n if (empty) {\n if (current) await secrets.delete(name);\n return;\n }\n await secrets.store(\n name,\n JSON.stringify(parseInferenceProviderConfig(next)),\n {\n category: INFERENCE_PROVIDER_CONFIG_SECRET_TYPE,\n description: `Inference provider config (${level})`,\n },\n );\n });\n invalidate(\n level === 'user'\n ? { tenantId: principal.tenantId, userId: principal.userId }\n : { tenantId: principal.tenantId },\n );\n },\n\n invalidate,\n };\n}\n\n/**\n * Register `resolver` behind smrt-inference's validating resolver (#4752):\n * chain config is validated first, then the per-user resolver binds, then\n * its `fallback` / the app config. Safe in either import order: a later\n * `registerInference()` keeps this registration.\n */\nexport function registerUserInferenceResolver(\n resolver: UserInferenceResolver,\n): UserInferenceResolver {\n setInferenceResolver(createInferenceResolver(resolver));\n return resolver;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"resolver.js","names":["userId","target","source"],"sources":["../src/resolver.ts"],"sourcesContent":["/**\n * `@happyvertical/smrt-inference/resolver` — per-user and per-tenant\n * inference bindings over smrt-secrets (#4756). Node-only: it reads secrets\n * and the ambient principal, so it stays off the browser-safe root entry.\n *\n * Precedence for each tier, highest first:\n * 1. the current user's stored `inference.provider-config` (tier binding,\n * then the user's default binding);\n * 2. the tenant's stored config (same order);\n * 3. `fallback` (another resolver), else the app's `inference` block;\n * 4. unbound: the configured client (`SMRT_AI_*` / the process `ai` block).\n * Every resolution carries `target.source` (`byo | local | hosted | app`),\n * which core records as the `source` usage tag.\n *\n * Inference mode (#4757): a stored config may carry `mode`. The user's mode,\n * else the tenant's, applies: `off` answers every tier (and untiered calls,\n * through `mode()`) as unavailable; `byo` / `local` / `hosted` bind only a\n * stored binding of that source (user, then tenant) and report\n * `unconfigured` when there is none, never falling through to app config.\n */\nimport {\n type InferenceMode,\n type InferenceModeRequest,\n type InferenceModeState,\n type InferenceRequest,\n type InferenceResolution,\n type InferenceResolver,\n type InferenceResolverContext,\n type InferenceTarget,\n type InferenceTier,\n isInferenceTargetSource,\n resolveAutoTier,\n setInferenceResolver,\n} from '@happyvertical/smrt-core';\nimport { SecretService } from '@happyvertical/smrt-secrets';\nimport { getCurrentTenant, withTenant } from '@happyvertical/smrt-tenancy';\nimport { getCurrentSessionPermissionContext } from '@happyvertical/smrt-users';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport {\n INFERENCE_PROVIDER_CONFIG_SECRET_TYPE,\n type InferenceProviderConfig,\n inferenceProviderConfigSecretName,\n inferenceProviderTargets,\n parseInferenceProviderConfig,\n type RedactedInferenceProviderConfig,\n redactInferenceProviderConfig,\n redactInferenceTarget,\n} from './provider-config.js';\nimport { createInferenceResolver } from './register.js';\n\nexport * from './provider-config.js';\n\n/** `invalidate()` of every live resolver this process created. */\nconst liveResolvers = new Set<WeakRef<UserInferenceResolver['invalidate']>>();\n\n/**\n * Drop cached configs in every user resolver this process created (all, one\n * tenant, or one user). Writers outside a resolver (hosted provisioning,\n * #4759) call this after storing a config; other processes see the change\n * when their cache entry expires (`ttlMs`, 30 s by default).\n */\nexport function invalidateUserInferenceResolvers(scope?: {\n tenantId?: string;\n userId?: string;\n}): void {\n for (const ref of [...liveResolvers]) {\n const invalidate = ref.deref();\n if (invalidate) invalidate(scope);\n else liveResolvers.delete(ref);\n }\n}\n\n/** Default cache lifetime of decrypted configs, per `(tenantId, userId)`. */\nexport const USER_INFERENCE_CACHE_TTL_MS = 30_000;\n\n/**\n * The subset of `SecretService` the resolver uses. Reads go through\n * `retrieve()` (audited, access-counted); writes through `store()`.\n */\nexport interface InferenceSecretStore {\n retrieve(name: string): Promise<{ value: string }>;\n store(\n name: string,\n value: string,\n options?: { category?: string; description?: string },\n ): Promise<unknown>;\n delete(name: string): Promise<boolean>;\n}\n\n/** Who a resolution acts for. */\nexport interface InferencePrincipal {\n tenantId: string;\n /** Absent for service principals and background jobs: no user layer. */\n userId?: string;\n}\n\n/** Options for {@link createUserInferenceResolver}. */\nexport interface UserInferenceResolverOptions {\n /** Database for a `SecretService` built on first use (when no `secrets`). */\n db?: DatabaseInterface;\n /** The secret store; defaults to `SecretService.create({ db })`. */\n secrets?: InferenceSecretStore | SecretService;\n /**\n * Resolver for tiers neither the user nor the tenant binds. Default: the\n * app's `inference` block through core's built-in resolution.\n */\n fallback?: InferenceResolver;\n /** Cache lifetime in ms (default {@link USER_INFERENCE_CACHE_TTL_MS}). */\n ttlMs?: number;\n /** Clock seam for tests. */\n now?: () => number;\n /**\n * Principal seam for tests and hosts with their own identity. Default:\n * the ambient principal, read the way smrt-preferences reads it.\n */\n principal?: () => InferencePrincipal | undefined;\n /**\n * Let a request for another tenant than the principal's (an object of\n * tenant B used by a principal of tenant A) read tenant B's stored config.\n * Default `false`: such a request skips the stored layers and falls through\n * to the app config, because reading B's key for A's action is a\n * cross-tenant read. Principal-less contexts (jobs) always use the\n * request's tenant.\n */\n allowCrossTenant?: boolean;\n}\n\n/** The stored layers for one `(tenantId, userId)`. */\ninterface Layers {\n user?: InferenceProviderConfig;\n tenant?: InferenceProviderConfig;\n}\n\n/** The explicit mode that applies: the user's, else the tenant's. */\nfunction explicitMode(\n layers: Layers,\n): { mode: InferenceMode; layer: 'user' | 'tenant' } | undefined {\n if (layers.user?.mode) return { mode: layers.user.mode, layer: 'user' };\n if (layers.tenant?.mode) return { mode: layers.tenant.mode, layer: 'tenant' };\n return undefined;\n}\n\n/** One tier of {@link InferenceBindingMap}. */\nexport type InferenceBindingEntry = (\n | InferenceTarget\n | (Omit<InferenceTarget, 'apiKey'> & { hasApiKey: boolean })\n) & { layer: 'user' | 'tenant' };\n\n/** What {@link UserInferenceResolver.resolveBindings} returns. */\nexport interface InferenceBindingMap {\n tenantId?: string;\n userId?: string;\n /** Per tier: the stored binding that answers it (absent: app config). */\n tiers: Partial<Record<InferenceTier, InferenceBindingEntry>>;\n /** The stored configs themselves, redacted unless `redact: false`. */\n user?: InferenceProviderConfig | RedactedInferenceProviderConfig;\n tenant?: InferenceProviderConfig | RedactedInferenceProviderConfig;\n /** The explicit mode that applies (#4757) and the layer that set it. */\n mode?: { mode: InferenceMode; layer: 'user' | 'tenant' };\n}\n\n/** The scope a config is written to. */\nexport type InferenceConfigLevel = 'user' | 'tenant';\n\n/** A resolver plus the read/write helpers the settings panel uses. */\nexport interface UserInferenceResolver extends InferenceResolver {\n /**\n * The tier → target map for the ambient principal (never a caller-chosen\n * tenant or user), without\n * keys unless `redact: false` (server-only; never send that to a client).\n */\n resolveBindings(options?: { redact?: boolean }): Promise<InferenceBindingMap>;\n /**\n * Validate and store a config for the current principal through\n * `SecretService.store()`, then invalidate the cache. `user` needs a\n * principal with a user id. Authorization (who may write the tenant\n * config) is the host's server action's job.\n */\n write(level: InferenceConfigLevel, config: unknown): Promise<void>;\n /** Delete the stored config at `level` for the current principal. */\n remove(level: InferenceConfigLevel): Promise<boolean>;\n /**\n * Set (or with `null` clear) the stored mode at `level` (#4757), keeping\n * the stored binding. Works without a key: a mode-only config is valid.\n * A config left with neither binding nor mode is deleted. Invalidates the\n * cache, so the next call sees it.\n */\n setMode(\n level: InferenceConfigLevel,\n mode: InferenceMode | null,\n ): Promise<void>;\n /** The explicit stored mode for untiered calls (core's `mode()` hook). */\n mode(request: InferenceModeRequest): Promise<InferenceModeState | undefined>;\n /**\n * Drop cached configs: all, one tenant's (every user), or one\n * `(tenantId, userId)`. Call after writing a config elsewhere.\n */\n invalidate(scope?: { tenantId?: string; userId?: string }): void;\n}\n\nfunction text(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() !== ''\n ? value.trim()\n : undefined;\n}\n\n/**\n * The ambient principal: the tenancy context first, then the users session\n * permission context (smrt-preferences' order). Never guessed.\n */\nexport function getAmbientInferencePrincipal(): InferencePrincipal | undefined {\n const tenant = getCurrentTenant();\n const tenantId = text(tenant?.tenantId);\n if (tenantId) {\n const userId = text(tenant?.userId);\n return userId ? { tenantId, userId } : { tenantId };\n }\n const session = getCurrentSessionPermissionContext();\n const sessionTenant = text(session?.tenantId);\n if (!sessionTenant) return undefined;\n const userId = text(session?.userId);\n return userId\n ? { tenantId: sessionTenant, userId }\n : { tenantId: sessionTenant };\n}\n\nfunction isAbsentSecretError(error: unknown, name: string): boolean {\n if (!(error instanceof Error)) return false;\n return (\n error.message === `Secret '${name}' not found` ||\n error.message === 'Secret not found' ||\n error.message === 'Secret disabled' ||\n error.message === 'Secret expired'\n );\n}\n\nconst cacheKey = (tenantId: string, userId: string | undefined) =>\n `${tenantId}\\u0000${userId ?? ''}`;\n\n/**\n * Create the per-user / per-tenant {@link InferenceResolver}. Register it\n * with {@link registerUserInferenceResolver} (keeps #4752's validation in\n * front) or `setInferenceResolver()`.\n */\nexport function createUserInferenceResolver(\n options: UserInferenceResolverOptions = {},\n): UserInferenceResolver {\n const ttlMs = options.ttlMs ?? USER_INFERENCE_CACHE_TTL_MS;\n const now = options.now ?? Date.now;\n const principalOf = options.principal ?? getAmbientInferencePrincipal;\n const cache = new Map<string, { expires: number; value: Promise<Layers> }>();\n let secretsPromise: Promise<InferenceSecretStore> | undefined;\n\n const getSecrets = (): Promise<InferenceSecretStore> => {\n if (options.secrets) return Promise.resolve(options.secrets);\n if (!options.db) {\n return Promise.reject(\n new Error(\n 'createUserInferenceResolver needs `secrets` or `db` to read stored inference configs.',\n ),\n );\n }\n secretsPromise ??= SecretService.create({ db: options.db }).catch(\n (error: unknown) => {\n secretsPromise = undefined;\n throw error;\n },\n );\n return secretsPromise;\n };\n\n /** Run `fn` in a tenancy context for `principal` (audit keeps the user). */\n const inTenant = <T>(\n principal: InferencePrincipal,\n fn: () => Promise<T>,\n ): Promise<T> => {\n const current = getCurrentTenant();\n if (\n text(current?.tenantId) === principal.tenantId &&\n text(current?.userId) === principal.userId\n ) {\n return fn();\n }\n return withTenant(\n {\n tenantId: principal.tenantId,\n ...(principal.userId ? { userId: principal.userId } : {}),\n },\n fn,\n );\n };\n\n const readConfig = async (\n secrets: InferenceSecretStore,\n name: string,\n ): Promise<InferenceProviderConfig | undefined> => {\n let value: string;\n try {\n value = (await secrets.retrieve(name)).value;\n } catch (error) {\n if (isAbsentSecretError(error, name)) return undefined;\n throw error;\n }\n return parseInferenceProviderConfig(value);\n };\n\n const loadLayers = async (\n tenantId: string,\n userId: string | undefined,\n ): Promise<Layers> => {\n const secrets = await getSecrets();\n return inTenant({ tenantId, ...(userId ? { userId } : {}) }, async () => {\n const layers: Layers = {};\n if (userId) {\n layers.user = await readConfig(\n secrets,\n inferenceProviderConfigSecretName({ userId }),\n );\n }\n layers.tenant = await readConfig(\n secrets,\n inferenceProviderConfigSecretName({}),\n );\n return layers;\n });\n };\n\n const getLayers = (\n tenantId: string,\n userId: string | undefined,\n ): Promise<Layers> => {\n const key = cacheKey(tenantId, userId);\n const hit = cache.get(key);\n if (hit && hit.expires > now()) return hit.value;\n const value = loadLayers(tenantId, userId);\n const entry = { expires: now() + ttlMs, value };\n cache.set(key, entry);\n value.catch(() => {\n if (cache.get(key) === entry) cache.delete(key);\n });\n return value;\n };\n\n /**\n * Whose stored layers answer this request. With a principal: its tenant\n * (or the request's when they match), its user unless the request says\n * `userId: null` (a client shared beyond one user, e.g. a long-lived\n * agent). A request for another tenant reads nothing stored unless\n * `allowCrossTenant`. Without a principal: the request's tenant, no user.\n */\n const identityFor = (\n request: Pick<InferenceRequest, 'tenantId' | 'userId'>,\n ): { tenantId?: string; userId?: string } => {\n const principal = principalOf();\n const requested = text(request.tenantId);\n if (!principal) return requested ? { tenantId: requested } : {};\n if (requested && requested !== principal.tenantId) {\n return options.allowCrossTenant ? { tenantId: requested } : {};\n }\n const tenantId = principal.tenantId;\n const userId = request.userId === null ? undefined : text(principal.userId);\n return userId ? { tenantId, userId } : { tenantId };\n };\n\n const invalidate: UserInferenceResolver['invalidate'] = (scope) => {\n if (!scope?.tenantId) {\n cache.clear();\n return;\n }\n if (scope.userId) {\n cache.delete(cacheKey(scope.tenantId, scope.userId));\n return;\n }\n const prefix = `${scope.tenantId}\\u0000`;\n for (const key of [...cache.keys()]) {\n if (key.startsWith(prefix)) cache.delete(key);\n }\n };\n\n const writeScope = (level: InferenceConfigLevel): InferencePrincipal => {\n const principal = principalOf();\n if (!principal?.tenantId) {\n throw new Error(\n 'Writing an inference provider config needs an authenticated tenant context.',\n );\n }\n if (level === 'user' && !principal.userId) {\n throw new Error(\n 'Writing a user inference provider config needs a principal with a user id.',\n );\n }\n return principal;\n };\n\n liveResolvers.add(new WeakRef(invalidate));\n\n return {\n async resolve(\n request: InferenceRequest,\n context: InferenceResolverContext,\n ): Promise<InferenceResolution | undefined> {\n const tierResolved =\n request.tier === 'auto'\n ? resolveAutoTier(context.config)\n : request.tier;\n const { tenantId, userId } = identityFor(request);\n if (tenantId) {\n const layers = await getLayers(tenantId, userId);\n const chosen = explicitMode(layers);\n if (chosen?.mode === 'off') {\n return {\n tierResolved,\n target: {},\n via: 'resolver',\n mode: 'off',\n unavailable: 'off',\n };\n }\n const candidates = [\n inferenceProviderTargets(layers.user)[tierResolved],\n inferenceProviderTargets(layers.tenant)[tierResolved],\n ];\n if (chosen) {\n // An explicit mode binds only its own kind of stored binding.\n const target = candidates.find(\n (entry) => entry?.source === chosen.mode,\n );\n if (target) {\n return { tierResolved, target, via: 'resolver', mode: chosen.mode };\n }\n return {\n tierResolved,\n target: {},\n via: 'resolver',\n mode: 'off',\n unavailable: 'unconfigured',\n };\n }\n const target = candidates.find(Boolean);\n if (target) {\n const source = target.source;\n return {\n tierResolved,\n target,\n via: 'resolver',\n ...(source === 'byo' || source === 'local' || source === 'hosted'\n ? { mode: source }\n : {}),\n };\n }\n }\n const fallen =\n (await options.fallback?.resolve(request, context)) ??\n context.resolveFromConfig(request);\n const source = fallen.target?.source;\n return {\n ...fallen,\n target: {\n ...fallen.target,\n source: isInferenceTargetSource(source) ? source : 'app',\n },\n };\n },\n\n async mode(request) {\n const { tenantId, userId } = identityFor(request);\n if (!tenantId) return undefined;\n const chosen = explicitMode(await getLayers(tenantId, userId));\n if (!chosen) return undefined;\n return chosen.mode === 'off'\n ? { mode: 'off', unavailable: 'off' }\n : { mode: chosen.mode };\n },\n\n // Fusions are app-scope only (#4753): stored user and tenant configs\n // never define one, so the lookup is the fallback's (override rows,\n // #4800) or none (core then reads `inference.fusions`).\n async lookupFusion(key, request, context) {\n return options.fallback?.lookupFusion?.(key, request, context);\n },\n\n async resolveBindings(bindingOptions = {}) {\n const redact = bindingOptions.redact !== false;\n // Identity is ambient only: no caller-chosen tenant or user.\n const { tenantId, userId } = identityFor({});\n if (!tenantId) return { tiers: {} };\n const layers = await getLayers(tenantId, userId);\n const map: InferenceBindingMap = {\n tenantId,\n ...(userId ? { userId } : {}),\n tiers: {},\n };\n for (const layer of ['tenant', 'user'] as const) {\n const config = layers[layer];\n if (!config) continue;\n map[layer] = redact ? redactInferenceProviderConfig(config) : config;\n for (const [tier, target] of Object.entries(\n inferenceProviderTargets(config),\n )) {\n map.tiers[tier as InferenceTier] = {\n ...(redact ? redactInferenceTarget(target) : target),\n layer,\n };\n }\n }\n const chosen = explicitMode(layers);\n if (chosen) map.mode = chosen;\n return map;\n },\n\n async write(level, config) {\n const principal = writeScope(level);\n const parsed = parseInferenceProviderConfig(config);\n const secrets = await getSecrets();\n const name = inferenceProviderConfigSecretName(\n level === 'user' ? { userId: principal.userId } : {},\n );\n await inTenant(principal, () =>\n secrets.store(name, JSON.stringify(parsed), {\n category: INFERENCE_PROVIDER_CONFIG_SECRET_TYPE,\n description: `Inference provider config (${level})`,\n }),\n );\n invalidate(\n level === 'user'\n ? { tenantId: principal.tenantId, userId: principal.userId }\n : { tenantId: principal.tenantId },\n );\n },\n\n async remove(level) {\n const principal = writeScope(level);\n const secrets = await getSecrets();\n const name = inferenceProviderConfigSecretName(\n level === 'user' ? { userId: principal.userId } : {},\n );\n const removed = await inTenant(principal, () => secrets.delete(name));\n invalidate(\n level === 'user'\n ? { tenantId: principal.tenantId, userId: principal.userId }\n : { tenantId: principal.tenantId },\n );\n return removed;\n },\n\n async setMode(level, mode) {\n const principal = writeScope(level);\n const secrets = await getSecrets();\n const name = inferenceProviderConfigSecretName(\n level === 'user' ? { userId: principal.userId } : {},\n );\n // Read fresh (not the cache) so a concurrent binding write is kept.\n const current = await inTenant(principal, () =>\n readConfig(secrets, name),\n );\n const next: InferenceProviderConfig = { ...(current ?? {}) };\n if (mode === null) delete next.mode;\n else next.mode = parseInferenceProviderConfig({ mode }).mode;\n const empty = Object.keys(next).every(\n (key) =>\n key === 'version' || key === 'source' || key === 'gatewayKeyAlias',\n );\n await inTenant(principal, async () => {\n if (empty) {\n if (current) await secrets.delete(name);\n return;\n }\n await secrets.store(\n name,\n JSON.stringify(parseInferenceProviderConfig(next)),\n {\n category: INFERENCE_PROVIDER_CONFIG_SECRET_TYPE,\n description: `Inference provider config (${level})`,\n },\n );\n });\n invalidate(\n level === 'user'\n ? { tenantId: principal.tenantId, userId: principal.userId }\n : { tenantId: principal.tenantId },\n );\n },\n\n invalidate,\n };\n}\n\n/**\n * Register `resolver` behind smrt-inference's validating resolver (#4752):\n * chain config is validated first, then the per-user resolver binds, then\n * its `fallback` / the app config. Safe in either import order: a later\n * `registerInference()` keeps this registration.\n */\nexport function registerUserInferenceResolver(\n resolver: UserInferenceResolver,\n): UserInferenceResolver {\n setInferenceResolver(createInferenceResolver(resolver));\n return resolver;\n}\n"],"mappings":"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"}
|
package/dist/server.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export { buildInferenceOverrideLayers, type InferenceOverrideLayers, type Infere
|
|
|
19
19
|
export { assertValidInferenceScope, InferenceOverrideAuthorizationError, type InferenceOverrideAuthorizer, InferenceOverrideService, type InferenceOverrideServiceOptions, type InferenceOverrideWriteRequest, InferenceTenantTargetNotAllowedError, type InferenceTenantTargetPolicy, type InferenceTenantTargetRequest, InvalidInferenceScopeError, } from './settings/override-service.js';
|
|
20
20
|
export { CUSTOMIZE_INFERENCE_PERMISSION, createInferencePreferences, INFERENCE_PERMISSION_DEFINITIONS, INFERENCE_PREFERENCE_KIND, INFERENCE_PREFERENCE_SURFACE, type InferencePreferenceIssue, type InferencePreferenceRead, type InferencePreferenceStoreLike, type InferencePreferences, type InferencePreferencesApi, inferencePreferenceKind, PERSONALIZE_INFERENCE_PERMISSION, validateInferencePreferences, } from './settings/preferences.js';
|
|
21
21
|
export { createInferenceOverrideResolver, type InferenceOverrideResolverOptions, } from './settings/resolver.js';
|
|
22
|
-
export { type InferenceAliasSetting, InferenceBindingInUseError, type InferenceConnectionInput, type InferenceConnectionResult, type InferenceDefaultTierSetting, type InferenceFusionSetting, type InferenceProviderOption, InferenceSettingsConfigError, type InferenceSettingsLevel, InferenceSettingsService, type InferenceSettingsServiceOptions, type InferenceTierSettingsRow, type RegisteredFusionLike, } from './settings/settings-service.js';
|
|
23
|
-
export { DEFAULT_TIER_BINDING, describeInferenceTarget, INFERENCE_MODES, type InferenceBindingId, InferenceBindingValueError, type InferenceMode, inferenceTargetRef, isAllowedByList, matchesAllowPattern, parseInferenceBinding, stripKeyMembers, tierBinding, } from './settings/targets.js';
|
|
22
|
+
export { type InferenceAliasSetting, InferenceBindingInUseError, type InferenceConnectionInput, type InferenceConnectionResult, type InferenceDefaultTierSetting, type InferenceFusionSetting, type InferenceProviderOption, InferenceSettingsConfigError, type InferenceSettingsLevel, InferenceSettingsService, type InferenceSettingsServiceOptions, type InferenceTierSettingsRow, type InferenceTranscriptSettings, type InferenceUsageReportDimension, type RegisteredFusionLike, } from './settings/settings-service.js';
|
|
23
|
+
export { DEFAULT_TIER_BINDING, describeInferenceTarget, INFERENCE_MODES, type InferenceBindingId, InferenceBindingValueError, type InferenceMode, type InferenceTranscriptSettingsValue, inferenceTargetRef, isAllowedByList, matchesAllowPattern, normalizeTranscriptSettings, parseInferenceBinding, stripKeyMembers, TRANSCRIPTS_BINDING, tierBinding, } from './settings/targets.js';
|
|
24
|
+
export { applyStoredTranscriptSettings, loadInferenceTranscriptSettings, storedTranscriptSettings, } from './settings/transcripts.js';
|
|
24
25
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,wBAAwB,CAAC;AAEhC,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,KAAK,wBAAwB,GAC9B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,2BAA2B,EAC3B,KAAK,0BAA0B,GAChC,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,KAAK,kBAAkB,EACvB,eAAe,EACf,kBAAkB,EAClB,8BAA8B,EAC9B,YAAY,EACZ,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,4BAA4B,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,2BAA2B,EAC3B,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,yBAAyB,EACzB,mCAAmC,EACnC,KAAK,2BAA2B,EAChC,wBAAwB,EACxB,KAAK,+BAA+B,EACpC,KAAK,6BAA6B,EAClC,oCAAoC,EACpC,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,EACjC,0BAA0B,GAC3B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,8BAA8B,EAC9B,0BAA0B,EAC1B,gCAAgC,EAChC,yBAAyB,EACzB,4BAA4B,EAC5B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,4BAA4B,EACjC,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,uBAAuB,EACvB,gCAAgC,EAChC,4BAA4B,GAC7B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,+BAA+B,EAC/B,KAAK,gCAAgC,GACtC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,qBAAqB,EAC1B,0BAA0B,EAC1B,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EAChC,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,4BAA4B,EAC5B,KAAK,sBAAsB,EAC3B,wBAAwB,EACxB,KAAK,+BAA+B,EACpC,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,GAC1B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,EACf,KAAK,kBAAkB,EACvB,0BAA0B,EAC1B,KAAK,aAAa,EAClB,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,WAAW,GACZ,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,wBAAwB,CAAC;AAEhC,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,KAAK,wBAAwB,GAC9B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,2BAA2B,EAC3B,KAAK,0BAA0B,GAChC,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,KAAK,kBAAkB,EACvB,eAAe,EACf,kBAAkB,EAClB,8BAA8B,EAC9B,YAAY,EACZ,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,4BAA4B,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,2BAA2B,EAC3B,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,yBAAyB,EACzB,mCAAmC,EACnC,KAAK,2BAA2B,EAChC,wBAAwB,EACxB,KAAK,+BAA+B,EACpC,KAAK,6BAA6B,EAClC,oCAAoC,EACpC,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,EACjC,0BAA0B,GAC3B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,8BAA8B,EAC9B,0BAA0B,EAC1B,gCAAgC,EAChC,yBAAyB,EACzB,4BAA4B,EAC5B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,4BAA4B,EACjC,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,uBAAuB,EACvB,gCAAgC,EAChC,4BAA4B,GAC7B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,+BAA+B,EAC/B,KAAK,gCAAgC,GACtC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,qBAAqB,EAC1B,0BAA0B,EAC1B,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EAChC,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,4BAA4B,EAC5B,KAAK,sBAAsB,EAC3B,wBAAwB,EACxB,KAAK,+BAA+B,EACpC,KAAK,wBAAwB,EAC7B,KAAK,2BAA2B,EAChC,KAAK,6BAA6B,EAClC,KAAK,oBAAoB,GAC1B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,EACf,KAAK,kBAAkB,EACvB,0BAA0B,EAC1B,KAAK,aAAa,EAClB,KAAK,gCAAgC,EACrC,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,2BAA2B,EAC3B,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,WAAW,GACZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,6BAA6B,EAC7B,+BAA+B,EAC/B,wBAAwB,GACzB,MAAM,2BAA2B,CAAC"}
|
package/dist/server.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { B as InferenceConfigError, I as findInferenceConfigIssues, N as findFusionDefinitionIssues, P as findFusionDefinitionPromptIssues, b as listFusions, y as getFusion, z as validateInferenceTarget } from "./chunks/provider-config-BSpIQRVM.js";
|
|
2
2
|
import { checkInferenceBindings, inferenceProbeUrl, probeInferenceCandidate } from "./node.js";
|
|
3
|
-
import { _ as
|
|
4
|
-
import { INFERENCE_PRESETS, INFERENCE_PRESET_IDS, INFERENCE_TIERS, getInferenceConfig, inferenceTargetKind, isInferencePresetId, isInferenceTier, mergeInferenceConfigs, reportAiUsage, resolveAutoTier } from "@happyvertical/smrt-core";
|
|
3
|
+
import { C as getInferenceOverrideGeneration, S as tierBinding, _ as normalizeFusionDefinition, a as INFERENCE_BINDING_NAME_PATTERN, b as parseInferenceBinding, c as TRANSCRIPTS_BINDING, d as inferenceTargetRef, f as isAllowedByList, g as normalizeDefaultTier, h as normalizeAllowList, i as DEFAULT_TIER_BINDING, l as describeInferenceTarget, m as normalizeAliasDefinition, n as APP_INFERENCE_SCOPE_ID, o as INFERENCE_MODES, p as matchesAllowPattern, r as InferenceOverride, s as InferenceBindingValueError, t as InferenceOverrideCollection, u as findKeyMember, v as normalizeTierTarget, x as stripKeyMembers, y as normalizeTranscriptSettings } from "./chunks/InferenceOverrideCollection-Dat4TrK5.js";
|
|
4
|
+
import { DEFAULT_RETENTION_POLICY, DEFAULT_TRANSCRIPT_MAX_BYTES, DEFAULT_TRANSCRIPT_SAMPLE, INFERENCE_PRESETS, INFERENCE_PRESET_IDS, INFERENCE_TIERS, config, getInferenceConfig, getInferenceTranscriptsConfig, inferenceTargetKind, isInferencePresetId, isInferenceTier, mergeInferenceConfigs, reportAiUsage, resolveAutoTier, setAiTranscriptAppSettings } from "@happyvertical/smrt-core";
|
|
5
5
|
import { AI_PROVIDER_TYPES } from "@happyvertical/ai";
|
|
6
6
|
import { getDefaultAIKeyEnvName } from "@happyvertical/smrt-config";
|
|
7
7
|
//#region src/settings/keys.ts
|
|
@@ -160,6 +160,7 @@ function normalizeBindingValue(binding, value) {
|
|
|
160
160
|
switch (parsed.kind) {
|
|
161
161
|
case "tier": return normalizeTierTarget(binding, value);
|
|
162
162
|
case "default-tier": return normalizeDefaultTier(binding, value);
|
|
163
|
+
case "transcripts": return normalizeTranscriptSettings(binding, value);
|
|
163
164
|
case "alias": return normalizeAliasDefinition(binding, value);
|
|
164
165
|
case "fusion": return normalizeFusionDefinition(binding, value);
|
|
165
166
|
}
|
|
@@ -209,6 +210,7 @@ var InferenceOverrideService = class {
|
|
|
209
210
|
const normalized = normalizeBindingValue(binding, value);
|
|
210
211
|
const parsed = parseInferenceBinding(binding);
|
|
211
212
|
if (scopeType === "tenant" && (parsed?.kind === "alias" || parsed?.kind === "fusion")) throw new InferenceBindingValueError(binding, "aliases and fusions are defined at the app scope only");
|
|
213
|
+
if (scopeType === "tenant" && parsed?.kind === "transcripts") throw new InferenceBindingValueError(binding, "the transcript setting is app scope only");
|
|
212
214
|
await this.require({
|
|
213
215
|
operation: normalized === null ? "remove" : "set",
|
|
214
216
|
binding,
|
|
@@ -287,6 +289,9 @@ function layerFromRows(rows, include) {
|
|
|
287
289
|
case "default-tier":
|
|
288
290
|
layer.defaultTier = value;
|
|
289
291
|
break;
|
|
292
|
+
case "transcripts":
|
|
293
|
+
layer.transcripts = value;
|
|
294
|
+
break;
|
|
290
295
|
case "alias":
|
|
291
296
|
layer.aliases ??= {};
|
|
292
297
|
layer.aliases[parsed.name] = value;
|
|
@@ -299,6 +304,22 @@ function layerFromRows(rows, include) {
|
|
|
299
304
|
}
|
|
300
305
|
return layer;
|
|
301
306
|
}
|
|
307
|
+
function isUnreadableFusionRow(row) {
|
|
308
|
+
if (!row || row.target === null || row.target === void 0) return false;
|
|
309
|
+
try {
|
|
310
|
+
row.getValue();
|
|
311
|
+
return false;
|
|
312
|
+
} catch {
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
function unreadableFusionIssue(name) {
|
|
317
|
+
return {
|
|
318
|
+
path: `fusion:${name}`,
|
|
319
|
+
code: "fusion-invalid",
|
|
320
|
+
message: `app row fusion:${name} cannot be read (not a JSON object, or a credential-shaped member); fix or remove it`
|
|
321
|
+
};
|
|
322
|
+
}
|
|
302
323
|
async function readInferenceOverrideRows(overrides, tenantId) {
|
|
303
324
|
const tenant = tenantId ?? null;
|
|
304
325
|
const [appRows, tenantRows] = await Promise.all([overrides.getScopeMap("app", APP_INFERENCE_SCOPE_ID), tenant ? overrides.getScopeMap("tenant", tenant) : Promise.resolve(/* @__PURE__ */ new Map())]);
|
|
@@ -479,22 +500,61 @@ function createInferencePreferences(store, surfaceId = INFERENCE_PREFERENCE_SURF
|
|
|
479
500
|
};
|
|
480
501
|
}
|
|
481
502
|
//#endregion
|
|
503
|
+
//#region src/settings/transcripts.ts
|
|
504
|
+
function storedTranscriptSettings(appRows) {
|
|
505
|
+
const row = appRows.get(TRANSCRIPTS_BINDING);
|
|
506
|
+
if (!row) return null;
|
|
507
|
+
try {
|
|
508
|
+
return row.getValue() ?? null;
|
|
509
|
+
} catch {
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
function applyStoredTranscriptSettings(appRows) {
|
|
514
|
+
const value = storedTranscriptSettings(appRows);
|
|
515
|
+
setAiTranscriptAppSettings(value);
|
|
516
|
+
return value;
|
|
517
|
+
}
|
|
518
|
+
async function loadInferenceTranscriptSettings(options = {}) {
|
|
519
|
+
return applyStoredTranscriptSettings(await (options.overrides ?? await InferenceOverrideCollection.create({
|
|
520
|
+
...options.db ?? {},
|
|
521
|
+
defaultListLimit: void 0,
|
|
522
|
+
maxListLimit: void 0
|
|
523
|
+
})).getScopeMap("app", APP_INFERENCE_SCOPE_ID));
|
|
524
|
+
}
|
|
525
|
+
//#endregion
|
|
482
526
|
//#region src/settings/resolver.ts
|
|
483
527
|
var MAX_CACHED_TENANTS = 500;
|
|
484
528
|
function expandStoredAliases(target, appAliases, configAliases) {
|
|
485
|
-
if (inferenceTargetKind(target) !== "alias") return target;
|
|
486
|
-
const name = target.alias;
|
|
487
|
-
if (!Object.hasOwn(appAliases, name)) return target;
|
|
488
529
|
const expand = (alias, seen) => {
|
|
489
530
|
if (seen.has(alias)) return [];
|
|
490
531
|
const next = new Set(seen).add(alias);
|
|
491
|
-
|
|
532
|
+
const definition = appAliases[alias] ?? configAliases[alias];
|
|
533
|
+
return inline(definition?.candidates ?? [], next);
|
|
534
|
+
};
|
|
535
|
+
const inline = (candidates, seen) => candidates.flatMap((candidate) => candidate.alias && Object.hasOwn(appAliases, candidate.alias) ? expand(candidate.alias, seen) : [candidate]);
|
|
536
|
+
const kind = inferenceTargetKind(target);
|
|
537
|
+
if (kind === "candidates") return {
|
|
538
|
+
...target,
|
|
539
|
+
candidates: inline(target.candidates ?? [], /* @__PURE__ */ new Set())
|
|
492
540
|
};
|
|
541
|
+
if (kind !== "alias") return target;
|
|
542
|
+
const name = target.alias;
|
|
543
|
+
if (!Object.hasOwn(appAliases, name)) return target;
|
|
493
544
|
return {
|
|
494
545
|
kind: "candidates",
|
|
495
546
|
candidates: expand(name, /* @__PURE__ */ new Set())
|
|
496
547
|
};
|
|
497
548
|
}
|
|
549
|
+
function expandStoredFusionAliases(definition, appAliases, configAliases) {
|
|
550
|
+
return {
|
|
551
|
+
...definition,
|
|
552
|
+
seats: definition.seats.map((seat) => typeof seat.target === "string" ? { ...seat } : {
|
|
553
|
+
...seat,
|
|
554
|
+
target: expandStoredAliases({ ...seat.target }, appAliases, configAliases)
|
|
555
|
+
})
|
|
556
|
+
};
|
|
557
|
+
}
|
|
498
558
|
function createInferenceOverrideResolver(options = {}) {
|
|
499
559
|
const ttlMs = options.ttlMs ?? 3e4;
|
|
500
560
|
const cache = /* @__PURE__ */ new Map();
|
|
@@ -526,28 +586,44 @@ function createInferenceOverrideResolver(options = {}) {
|
|
|
526
586
|
}
|
|
527
587
|
return rows;
|
|
528
588
|
};
|
|
529
|
-
return {
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
const
|
|
537
|
-
|
|
538
|
-
|
|
589
|
+
return {
|
|
590
|
+
async resolve(request, context) {
|
|
591
|
+
const tenantId = typeof request.tenantId === "string" && request.tenantId ? request.tenantId : null;
|
|
592
|
+
const rows = await rowsFor(tenantId);
|
|
593
|
+
applyStoredTranscriptSettings(rows.appRows);
|
|
594
|
+
const layers = await buildInferenceOverrideLayers(rows, options.tenantTargetPolicy);
|
|
595
|
+
const merged = mergeSettingsConfigs(context.config, layers.app, layers.tenant);
|
|
596
|
+
const tier = request.tier === "auto" ? resolveAutoTier(merged) : request.tier;
|
|
597
|
+
const stored = layers.tenant.tiers?.[tier] ?? layers.app.tiers?.[tier];
|
|
598
|
+
if (!stored) {
|
|
599
|
+
const next = {
|
|
600
|
+
...request,
|
|
601
|
+
tier
|
|
602
|
+
};
|
|
603
|
+
const deferred = options.base ? await options.base.resolve(next, context) : void 0;
|
|
604
|
+
if (deferred !== void 0) return deferred;
|
|
605
|
+
return request.tier === "auto" ? context.resolveFromConfig(next) : void 0;
|
|
606
|
+
}
|
|
607
|
+
validateInferenceTarget(stored, `inference.tiers.${tier}`, merged);
|
|
608
|
+
return {
|
|
609
|
+
tierResolved: tier,
|
|
610
|
+
target: expandStoredAliases({ ...stored }, layers.app.aliases ?? {}, context.config?.aliases ?? {}),
|
|
611
|
+
via: "resolver"
|
|
539
612
|
};
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
613
|
+
},
|
|
614
|
+
async lookupFusion(key, request, context) {
|
|
615
|
+
const rows = await rowsFor(null);
|
|
616
|
+
if (isUnreadableFusionRow(rows.appRows.get(`fusion:${key}`))) throw new InferenceConfigError([unreadableFusionIssue(key)]);
|
|
617
|
+
const app = (await buildInferenceOverrideLayers(rows)).app;
|
|
618
|
+
const stored = Object.hasOwn(app.fusions ?? {}, key) ? app.fusions?.[key] : void 0;
|
|
619
|
+
if (!stored) return options.base?.lookupFusion ? options.base.lookupFusion(key, request, context) : void 0;
|
|
620
|
+
const merged = mergeSettingsConfigs(context.config, app);
|
|
621
|
+
const path = `fusion:${key}`;
|
|
622
|
+
const issues = [...findFusionDefinitionIssues(stored, path, merged), ...findFusionDefinitionPromptIssues(stored, path)];
|
|
623
|
+
if (issues.length > 0) throw new InferenceConfigError(issues);
|
|
624
|
+
return expandStoredFusionAliases(stored, app.aliases ?? {}, context.config?.aliases ?? {});
|
|
543
625
|
}
|
|
544
|
-
|
|
545
|
-
return {
|
|
546
|
-
tierResolved: tier,
|
|
547
|
-
target: expandStoredAliases({ ...stored }, layers.app.aliases ?? {}, context.config?.aliases ?? {}),
|
|
548
|
-
via: "resolver"
|
|
549
|
-
};
|
|
550
|
-
} };
|
|
626
|
+
};
|
|
551
627
|
}
|
|
552
628
|
//#endregion
|
|
553
629
|
//#region src/settings/settings-service.ts
|
|
@@ -619,6 +695,10 @@ function bindingRefersTo(binding, value, kind, name) {
|
|
|
619
695
|
if (kind === "alias" && binding.startsWith("alias:") && binding !== `alias:${name}`) return Array.isArray(value.candidates) && value.candidates.some((candidate) => isRecord(candidate) && candidate.alias === name);
|
|
620
696
|
return false;
|
|
621
697
|
}
|
|
698
|
+
function transcriptRetentionDays(policy) {
|
|
699
|
+
if (policy?.enabled === false || policy?.aiTranscripts === false) return null;
|
|
700
|
+
return policy?.aiTranscripts?.maxAgeDays ?? DEFAULT_RETENTION_POLICY.aiTranscripts.maxAgeDays ?? null;
|
|
701
|
+
}
|
|
622
702
|
var PRESET_LABELS = {
|
|
623
703
|
openrouter: "OpenRouter",
|
|
624
704
|
lemonade: "Lemonade (local)",
|
|
@@ -739,41 +819,65 @@ var InferenceSettingsService = class InferenceSettingsService {
|
|
|
739
819
|
/**
|
|
740
820
|
* Fusions from the host's registry (code, read-only), the config's
|
|
741
821
|
* `inference.fusions`, and app rows (editable JSON). A later source
|
|
742
|
-
* shadows an earlier one by name
|
|
822
|
+
* shadows an earlier one by name, the executor's precedence (#4800): app
|
|
823
|
+
* row, then config, then registry. Each entry carries the issues that
|
|
824
|
+
* would make the executor refuse it, so the list shows what would run.
|
|
743
825
|
*/
|
|
744
826
|
async listInferenceFusions() {
|
|
745
827
|
const layers = await this.loadLayers(null);
|
|
828
|
+
const config = this.readConfig();
|
|
829
|
+
const merged = mergeSettingsConfigs(config, layers.app);
|
|
746
830
|
const out = /* @__PURE__ */ new Map();
|
|
831
|
+
const put = (setting, raw, path, against) => {
|
|
832
|
+
const previous = out.get(setting.name);
|
|
833
|
+
const shadows = previous ? [...previous.shadows ?? [], previous.source] : [];
|
|
834
|
+
const issues = [...findFusionDefinitionIssues(raw, path, against), ...findFusionDefinitionPromptIssues(raw, path)];
|
|
835
|
+
if (shadows.length > 0) setting.shadows = shadows;
|
|
836
|
+
if (issues.length > 0) setting.issues = issues;
|
|
837
|
+
out.set(setting.name, setting);
|
|
838
|
+
};
|
|
747
839
|
const registry = this.options.registryFusions ?? defaultRegistryFusions;
|
|
748
840
|
for (const fusion of registry()) {
|
|
749
841
|
if (!fusion || typeof fusion.key !== "string") continue;
|
|
750
842
|
const definition = stripKeyMembers(JSON.parse(JSON.stringify(fusion)));
|
|
751
|
-
|
|
843
|
+
const { key: _key, ...raw } = fusion;
|
|
844
|
+
put({
|
|
752
845
|
name: fusion.key,
|
|
753
846
|
source: "registry",
|
|
754
847
|
definition,
|
|
755
848
|
description: typeof fusion.description === "string" ? fusion.description : void 0,
|
|
756
849
|
editable: false
|
|
757
|
-
});
|
|
850
|
+
}, raw, `fusions.${fusion.key}`, config);
|
|
758
851
|
}
|
|
759
|
-
const
|
|
760
|
-
if (isRecord(configFusions)) for (const [name, definition] of Object.entries(configFusions)) {
|
|
852
|
+
if (isRecord(config?.fusions)) for (const [name, definition] of Object.entries(config.fusions)) {
|
|
761
853
|
if (!isRecord(definition)) continue;
|
|
762
|
-
|
|
854
|
+
put({
|
|
763
855
|
name,
|
|
764
856
|
source: "config",
|
|
765
857
|
definition: stripKeyMembers(definition),
|
|
766
858
|
description: typeof definition.description === "string" ? definition.description : void 0,
|
|
767
859
|
editable: false
|
|
768
|
-
});
|
|
860
|
+
}, definition, `inference.fusions.${name}`, config);
|
|
769
861
|
}
|
|
770
|
-
for (const [name, definition] of Object.entries(layers.app.fusions ?? {}))
|
|
862
|
+
for (const [name, definition] of Object.entries(layers.app.fusions ?? {})) put({
|
|
771
863
|
name,
|
|
772
864
|
source: "app",
|
|
773
865
|
definition,
|
|
774
866
|
description: typeof definition.description === "string" ? definition.description : void 0,
|
|
775
867
|
editable: true
|
|
776
|
-
});
|
|
868
|
+
}, definition, `fusion:${name}`, merged);
|
|
869
|
+
for (const [binding, row] of layers.appRows) {
|
|
870
|
+
if (!binding.startsWith("fusion:") || !isUnreadableFusionRow(row)) continue;
|
|
871
|
+
const name = binding.slice(7);
|
|
872
|
+
put({
|
|
873
|
+
name,
|
|
874
|
+
source: "app",
|
|
875
|
+
definition: {},
|
|
876
|
+
editable: true
|
|
877
|
+
}, void 0, binding, merged);
|
|
878
|
+
const entry = out.get(name);
|
|
879
|
+
if (entry) entry.issues = [unreadableFusionIssue(name)];
|
|
880
|
+
}
|
|
777
881
|
return [...out.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
778
882
|
}
|
|
779
883
|
/**
|
|
@@ -805,6 +909,10 @@ var InferenceSettingsService = class InferenceSettingsService {
|
|
|
805
909
|
async setFusion(request) {
|
|
806
910
|
const binding = `fusion:${request.name}`;
|
|
807
911
|
if (!INFERENCE_BINDING_NAME_PATTERN.test(String(request.name))) throw new InferenceBindingValueError(binding, "invalid fusion name");
|
|
912
|
+
if (request.definition !== null) {
|
|
913
|
+
const normalized = normalizeFusionDefinition(binding, request.definition);
|
|
914
|
+
await this.assertConfigStaysValid("app", APP_INFERENCE_SCOPE_ID, { fusions: { [request.name]: normalized } });
|
|
915
|
+
}
|
|
808
916
|
await this.writes.setValue(binding, "app", APP_INFERENCE_SCOPE_ID, request.definition, { beforeWrite: (normalized) => normalized === null ? this.assertNotReferenced("fusion", request.name) : Promise.resolve() });
|
|
809
917
|
}
|
|
810
918
|
/**
|
|
@@ -815,7 +923,7 @@ var InferenceSettingsService = class InferenceSettingsService {
|
|
|
815
923
|
*/
|
|
816
924
|
async assertNotReferenced(kind, name) {
|
|
817
925
|
const config = this.readConfig();
|
|
818
|
-
if (kind === "alias" ? Object.hasOwn(config?.aliases ?? {}, name) : Object.hasOwn(config?.fusions ?? {}, name) || [...this.options.registryFusions
|
|
926
|
+
if (kind === "alias" ? Object.hasOwn(config?.aliases ?? {}, name) : Object.hasOwn(config?.fusions ?? {}, name) || [...(this.options.registryFusions ?? defaultRegistryFusions)()].some((fusion) => fusion?.key === name)) return;
|
|
819
927
|
const referrers = [];
|
|
820
928
|
for (const [tier, target] of Object.entries(config?.tiers ?? {})) if (bindingRefersTo(`tier:${tier}`, target, kind, name)) referrers.push(`config tiers.${tier}`);
|
|
821
929
|
for (const [alias, definition] of Object.entries(config?.aliases ?? {})) if (bindingRefersTo(`alias:${alias}`, definition, kind, name)) referrers.push(`config aliases.${alias}`);
|
|
@@ -920,9 +1028,54 @@ var InferenceSettingsService = class InferenceSettingsService {
|
|
|
920
1028
|
});
|
|
921
1029
|
}
|
|
922
1030
|
/**
|
|
923
|
-
* The
|
|
924
|
-
*
|
|
925
|
-
*
|
|
1031
|
+
* The app's transcript setting (#4781): effective values, the stored app
|
|
1032
|
+
* row, its revert preview, the env override and the retention window.
|
|
1033
|
+
* The same for every tenant (no tenant override).
|
|
1034
|
+
*/
|
|
1035
|
+
async getTranscriptSettings() {
|
|
1036
|
+
const appValue = storedTranscriptSettings(await this.overrides.getScopeMap("app", APP_INFERENCE_SCOPE_ID));
|
|
1037
|
+
const merged = this.options.transcriptsConfig ?? getInferenceTranscriptsConfig;
|
|
1038
|
+
const resolved = (layer) => {
|
|
1039
|
+
const value = merged(layer);
|
|
1040
|
+
return {
|
|
1041
|
+
enabled: value?.enabled === true,
|
|
1042
|
+
sample: value?.sample ?? DEFAULT_TRANSCRIPT_SAMPLE,
|
|
1043
|
+
maxBytes: value?.maxBytes ?? DEFAULT_TRANSCRIPT_MAX_BYTES
|
|
1044
|
+
};
|
|
1045
|
+
};
|
|
1046
|
+
const effective = resolved(appValue);
|
|
1047
|
+
const appRevert = resolved(null);
|
|
1048
|
+
const forcedOn = resolved({ enabled: true }).enabled;
|
|
1049
|
+
const envEnabled = forcedOn === resolved({ enabled: false }).enabled ? forcedOn : null;
|
|
1050
|
+
const config$1 = merged(null);
|
|
1051
|
+
const supplyingLevel = envEnabled !== null ? "env" : appValue?.enabled !== void 0 ? "app" : config$1?.enabled !== void 0 ? "config" : "default";
|
|
1052
|
+
return {
|
|
1053
|
+
binding: TRANSCRIPTS_BINDING,
|
|
1054
|
+
...effective,
|
|
1055
|
+
supplyingLevel,
|
|
1056
|
+
appValue,
|
|
1057
|
+
appRevert,
|
|
1058
|
+
envEnabled,
|
|
1059
|
+
retentionDays: transcriptRetentionDays((this.options.retention ?? (() => config.toJSON().retention))()),
|
|
1060
|
+
promptOverrides: Object.keys(config$1?.prompts ?? {}).sort()
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
/**
|
|
1064
|
+
* Store (or, with `null`, remove) the app's transcript setting. Always
|
|
1065
|
+
* the app row: there is no tenant scope. Goes through the authorizer
|
|
1066
|
+
* (request `{ binding: 'transcripts', scopeType: 'app' }`); on success the
|
|
1067
|
+
* value is installed as core's app settings layer in this process.
|
|
1068
|
+
*/
|
|
1069
|
+
async setTranscriptSettings(request) {
|
|
1070
|
+
await this.writes.setValue(TRANSCRIPTS_BINDING, "app", APP_INFERENCE_SCOPE_ID, request.settings);
|
|
1071
|
+
applyStoredTranscriptSettings(await this.overrides.getScopeMap("app", APP_INFERENCE_SCOPE_ID));
|
|
1072
|
+
}
|
|
1073
|
+
/**
|
|
1074
|
+
* The Usage tab: `reportAiUsage()` (#4751) grouped by model, tier, prompt
|
|
1075
|
+
* key, fusion and/or template hash. Pass `by: ['model']` where tier and
|
|
1076
|
+
* prompt tags do not exist yet; NULL dimensions are their own bucket,
|
|
1077
|
+
* never dropped. `transcripts: true` adds per-bucket transcript counts
|
|
1078
|
+
* (#4781; ids only, never content).
|
|
926
1079
|
*/
|
|
927
1080
|
usage(options = {}) {
|
|
928
1081
|
return reportAiUsage(this.overrides.db, {
|
|
@@ -980,6 +1133,6 @@ function findKeyLike(candidate) {
|
|
|
980
1133
|
return typeof candidate.apiKey === "string" && candidate.apiKey.length > 0;
|
|
981
1134
|
}
|
|
982
1135
|
//#endregion
|
|
983
|
-
export { APP_INFERENCE_SCOPE_ID, CUSTOMIZE_INFERENCE_PERMISSION, DEFAULT_TIER_BINDING, INFERENCE_MODES, INFERENCE_PERMISSION_DEFINITIONS, INFERENCE_PREFERENCE_KIND, INFERENCE_PREFERENCE_SURFACE, InferenceBindingInUseError, InferenceBindingValueError, InferenceKeyOriginError, InferenceOverride, InferenceOverrideAuthorizationError, InferenceOverrideCollection, InferenceOverrideService, InferenceSettingsConfigError, InferenceSettingsService, InferenceTenantTargetNotAllowedError, InvalidInferenceScopeError, PERSONALIZE_INFERENCE_PERMISSION, REDACTED_KEY, assertValidInferenceScope, bindInferenceKey, buildInferenceOverrideLayers, createInferenceOverrideResolver, createInferencePreferences, describeInferenceTarget, inferenceKeyFor, inferenceKeyOrigin, inferencePreferenceKind, inferenceProviderConfigFromKey, inferenceTargetRef, isAllowedByList, loadInferenceOverrideLayers, matchesAllowPattern, mergeSettingsConfigs, parseInferenceBinding, readInferenceOverrideRows, redactInferenceSecret, stripKeyMembers, tierBinding, validateInferencePreferences };
|
|
1136
|
+
export { APP_INFERENCE_SCOPE_ID, CUSTOMIZE_INFERENCE_PERMISSION, DEFAULT_TIER_BINDING, INFERENCE_MODES, INFERENCE_PERMISSION_DEFINITIONS, INFERENCE_PREFERENCE_KIND, INFERENCE_PREFERENCE_SURFACE, InferenceBindingInUseError, InferenceBindingValueError, InferenceKeyOriginError, InferenceOverride, InferenceOverrideAuthorizationError, InferenceOverrideCollection, InferenceOverrideService, InferenceSettingsConfigError, InferenceSettingsService, InferenceTenantTargetNotAllowedError, InvalidInferenceScopeError, PERSONALIZE_INFERENCE_PERMISSION, REDACTED_KEY, TRANSCRIPTS_BINDING, applyStoredTranscriptSettings, assertValidInferenceScope, bindInferenceKey, buildInferenceOverrideLayers, createInferenceOverrideResolver, createInferencePreferences, describeInferenceTarget, inferenceKeyFor, inferenceKeyOrigin, inferencePreferenceKind, inferenceProviderConfigFromKey, inferenceTargetRef, isAllowedByList, loadInferenceOverrideLayers, loadInferenceTranscriptSettings, matchesAllowPattern, mergeSettingsConfigs, normalizeTranscriptSettings, parseInferenceBinding, readInferenceOverrideRows, redactInferenceSecret, storedTranscriptSettings, stripKeyMembers, tierBinding, validateInferencePreferences };
|
|
984
1137
|
|
|
985
1138
|
//# sourceMappingURL=server.js.map
|