@stackbone/cli 0.9.1 → 0.9.2
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/CHANGELOG.md +20 -0
- package/main.js +326 -76
- package/migrations/0122_model_routing_object.sql +16 -0
- package/package.json +1 -1
- package/stackbone-cli-0.9.2.tgz +0 -0
- package/voice-worker/main.js +171 -49
- package/voice-worker/main.js.map +4 -4
- package/stackbone-cli-0.9.1.tgz +0 -0
package/main.js
CHANGED
|
@@ -3229,7 +3229,7 @@ var init_studio_internal_prompts = __esmMin((() => {
|
|
|
3229
3229
|
}));
|
|
3230
3230
|
//#endregion
|
|
3231
3231
|
//#region ../../libs/shared/validators/src/lib/studio/studio-model-routing.ts
|
|
3232
|
-
var MODEL_PROVIDER_SORTS, MODEL_PROVIDER_SORTS_TEXT, modelProviderSortSchema,
|
|
3232
|
+
var MODEL_PROVIDER_SORTS, MODEL_PROVIDER_SORTS_TEXT, modelProviderSortSchema, MODEL_PROVIDER_QUANTIZATIONS, providerSlugSchema, providerSlugsSchema, percentileTargetSchema, dollarsSchema, modelProviderRoutingSchema, modelRoutingInputSchema, toModelProviderRouting, readModelProviderRouting, plainSortOf, studioModelRoutingPathSchema, studioModelRoutingSchema, putStudioModelRoutingRequestSchema, routingOfPutRequest, studioModelRoutingDeleteResponseSchema;
|
|
3233
3233
|
var init_studio_model_routing = __esmMin((() => {
|
|
3234
3234
|
init_studio_model_selections();
|
|
3235
3235
|
MODEL_PROVIDER_SORTS = [
|
|
@@ -3239,14 +3239,69 @@ var init_studio_model_routing = __esmMin((() => {
|
|
|
3239
3239
|
];
|
|
3240
3240
|
MODEL_PROVIDER_SORTS_TEXT = MODEL_PROVIDER_SORTS.map((sort) => `'${sort}'`).join(", ");
|
|
3241
3241
|
modelProviderSortSchema = z.enum(MODEL_PROVIDER_SORTS);
|
|
3242
|
-
|
|
3242
|
+
MODEL_PROVIDER_QUANTIZATIONS = [
|
|
3243
|
+
"int4",
|
|
3244
|
+
"int8",
|
|
3245
|
+
"fp4",
|
|
3246
|
+
"mxfp4",
|
|
3247
|
+
"nvfp4",
|
|
3248
|
+
"fp6",
|
|
3249
|
+
"fp8",
|
|
3250
|
+
"mxfp8",
|
|
3251
|
+
"fp16",
|
|
3252
|
+
"bf16",
|
|
3253
|
+
"fp32",
|
|
3254
|
+
"unknown"
|
|
3255
|
+
];
|
|
3256
|
+
providerSlugSchema = z.string().regex(/^\S+$/, "a provider slug has no spaces").max(128);
|
|
3257
|
+
providerSlugsSchema = z.array(providerSlugSchema).min(1).max(32);
|
|
3258
|
+
percentileTargetSchema = z.union([z.number().positive(), z.strictObject({
|
|
3259
|
+
p50: z.number().positive(),
|
|
3260
|
+
p75: z.number().positive(),
|
|
3261
|
+
p90: z.number().positive(),
|
|
3262
|
+
p99: z.number().positive()
|
|
3263
|
+
}).partial().refine((value) => Object.keys(value).length > 0, "name at least one percentile")]);
|
|
3264
|
+
dollarsSchema = z.number().nonnegative();
|
|
3265
|
+
modelProviderRoutingSchema = z.strictObject({
|
|
3266
|
+
order: providerSlugsSchema,
|
|
3267
|
+
only: providerSlugsSchema,
|
|
3268
|
+
ignore: providerSlugsSchema,
|
|
3269
|
+
allow_fallbacks: z.boolean(),
|
|
3270
|
+
require_parameters: z.boolean(),
|
|
3271
|
+
data_collection: z.enum(["allow", "deny"]),
|
|
3272
|
+
zdr: z.boolean(),
|
|
3273
|
+
enforce_distillable_text: z.boolean(),
|
|
3274
|
+
quantizations: z.array(z.enum(MODEL_PROVIDER_QUANTIZATIONS)).min(1),
|
|
3275
|
+
sort: z.union([modelProviderSortSchema, z.strictObject({
|
|
3276
|
+
by: modelProviderSortSchema,
|
|
3277
|
+
partition: z.enum(["model", "none"]).optional()
|
|
3278
|
+
})]),
|
|
3279
|
+
preferred_min_throughput: percentileTargetSchema,
|
|
3280
|
+
preferred_max_latency: percentileTargetSchema,
|
|
3281
|
+
max_price: z.strictObject({
|
|
3282
|
+
prompt: dollarsSchema,
|
|
3283
|
+
completion: dollarsSchema,
|
|
3284
|
+
request: dollarsSchema,
|
|
3285
|
+
image: dollarsSchema
|
|
3286
|
+
}).partial()
|
|
3287
|
+
}).partial().refine((value) => Object.keys(value).length > 0, "name at least one routing field");
|
|
3288
|
+
modelRoutingInputSchema = z.union([modelProviderSortSchema, modelProviderRoutingSchema]);
|
|
3289
|
+
toModelProviderRouting = (input) => typeof input === "string" ? { sort: input } : input;
|
|
3290
|
+
readModelProviderRouting = (value) => {
|
|
3291
|
+
const parsed = modelRoutingInputSchema.safeParse(value);
|
|
3292
|
+
return parsed.success ? toModelProviderRouting(parsed.data) : null;
|
|
3293
|
+
};
|
|
3294
|
+
plainSortOf = (routing) => routing && Object.keys(routing).length === 1 && typeof routing.sort === "string" ? routing.sort : null;
|
|
3243
3295
|
studioModelRoutingPathSchema = studioModelSelectionPathSchema.extend({ ownerKind: z.literal("agent") });
|
|
3244
3296
|
studioModelRoutingSchema = studioModelRoutingPathSchema.extend({
|
|
3245
|
-
|
|
3297
|
+
routing: modelProviderRoutingSchema,
|
|
3298
|
+
/** `routing` as a plain sort, for a Studio older than the object; `null` otherwise. */
|
|
3299
|
+
sort: modelProviderSortSchema.nullable(),
|
|
3246
3300
|
updatedBy: z.string().nullable(),
|
|
3247
3301
|
updatedAt: z.iso.datetime()
|
|
3248
3302
|
});
|
|
3249
|
-
putStudioModelRoutingRequestSchema = z.strictObject({ sort: modelProviderSortSchema });
|
|
3303
|
+
putStudioModelRoutingRequestSchema = z.union([z.strictObject({ routing: modelProviderRoutingSchema }), z.strictObject({ sort: modelProviderSortSchema })]);
|
|
3304
|
+
routingOfPutRequest = (body) => "routing" in body ? body.routing : { sort: body.sort };
|
|
3250
3305
|
studioModelRoutingDeleteResponseSchema = studioModelRoutingPathSchema.extend({ status: z.literal("deleted") });
|
|
3251
3306
|
}));
|
|
3252
3307
|
//#endregion
|
|
@@ -3613,7 +3668,7 @@ var init_studio_rag = __esmMin((() => {
|
|
|
3613
3668
|
error: z.string().nullable()
|
|
3614
3669
|
});
|
|
3615
3670
|
ragRebuildStatusResponseSchema = z.object({ rebuild: ragRebuildStatusSchema.nullable() });
|
|
3616
|
-
})), VOICE_STT_MODELS, VOICE_TTS_MODELS, isCartesiaTtsModel, VOICE_BACKGROUND_CLIPS, VOICE_AMBIENT_SOUNDS, VOICE_THINKING_SOUNDS, thresholdSchema, millisecondsSchema, keytermsSchema, vadSchema, turnSchema, interruptionsSchema, ragSchema, volumeSchema, backgroundAudioSchema, VOICE_BACKGROUND_AUDIO_DEFAULTS, effectiveVoiceSettingsObjectSchema, checkCrossFieldRules, effectiveVoiceSettingsSchema, callVoiceSettingsObjectSchema, callVoiceSettingsSchema, toCallVoiceSettings, VOICE_SETTINGS_LEAF_PATHS, VOICE_TTS_PAIR, voiceSettingOriginSchema, effectiveVoiceSettingsViewSchema, checkTtsPair, voiceSettingsSchema, checkStudioTtsPair, studioVoiceSettingsPatchSchema, voiceSettingsResponseSchema, describeVoiceSettingsIssue, deepAgentNameSchema, voiceCallIdSchema, voiceTagsSchema, VOICE_TTS_MARKUP_CONTEXT, VOICE_TTS_MARKUP_GUIDES, isVoiceBoxUrl, VOICE_BOX_URL_MAX, voiceBoxUrlSchema, VOICE_TRACES_PATH, VOICE_WORKER_AGENT_NAME_ENV, VOICE_WORKER_PORT_ENV, VOICE_CALL_TOKEN_MAX, voiceDispatchSchema, voiceRoomRequestSchema, voiceRoomResponseSchema, VOICE_RECORDING_STATUSES, voiceRecordingQuerySchema, VOICE_RECORDING_URL_TTL_SECONDS, voiceRecordingResponseSchema, voiceStatusResponseSchema, HEARD_TEXT_MAX, aguiHeardSchema, VOICE_CALLS_PATH, voiceCallBootstrapSchema, voiceCallTranscriptSchema, VOICE_CALL_TURNS_MAX, VOICE_TURN_LATENCY_MAX_MS, turnLatencyMs, voiceCallTurnMetricsSchema, voiceCallTurnsSchema, readVoiceCallTurns, voiceCallCompleteSchema, voiceCallLifecycleResponseSchema;
|
|
3671
|
+
})), VOICE_STT_MODELS, VOICE_TTS_MODELS, isCartesiaTtsModel, VOICE_BACKGROUND_CLIPS, VOICE_AMBIENT_SOUNDS, VOICE_THINKING_SOUNDS, thresholdSchema, millisecondsSchema, keytermsSchema, vadSchema, turnSchema, VOICE_EAGER_THRESHOLD_DEFAULT, interruptionsSchema, ragSchema, volumeSchema, backgroundAudioSchema, VOICE_BACKGROUND_AUDIO_DEFAULTS, effectiveVoiceSettingsObjectSchema, checkCrossFieldRules, effectiveVoiceSettingsSchema, callVoiceSettingsObjectSchema, callVoiceSettingsSchema, toCallVoiceSettings, VOICE_SETTINGS_LEAF_PATHS, VOICE_TTS_PAIR, voiceSettingOriginSchema, LEAVES_NEWER_THAN_SOME_BOXES, effectiveVoiceSettingsViewSchema, checkTtsPair, voiceSettingsSchema, checkStudioTtsPair, studioVoiceSettingsPatchSchema, voiceSettingsResponseSchema, describeVoiceSettingsIssue, deepAgentNameSchema, voiceCallIdSchema, voiceTagsSchema, VOICE_TTS_MARKUP_CONTEXT, VOICE_TTS_MARKUP_GUIDES, isVoiceBoxUrl, VOICE_BOX_URL_MAX, voiceBoxUrlSchema, VOICE_TRACES_PATH, VOICE_WORKER_AGENT_NAME_ENV, VOICE_WORKER_PORT_ENV, VOICE_CALL_TOKEN_MAX, voiceDispatchSchema, voiceRoomRequestSchema, voiceRoomResponseSchema, VOICE_RECORDING_STATUSES, voiceRecordingQuerySchema, VOICE_RECORDING_URL_TTL_SECONDS, voiceRecordingResponseSchema, voiceStatusResponseSchema, HEARD_TEXT_MAX, aguiHeardSchema, VOICE_CALLS_PATH, voiceCallBootstrapSchema, voiceCallKnowledgeSchema, voiceCallTranscriptSchema, VOICE_CALL_TURNS_MAX, VOICE_TURN_LATENCY_MAX_MS, turnLatencyMs, voiceCallTurnMetricsSchema, voiceCallTurnsSchema, readVoiceCallTurns, voiceCallCompleteSchema, voiceCallLifecycleResponseSchema;
|
|
3617
3672
|
var init_studio_voice = __esmMin((() => {
|
|
3618
3673
|
init_memory_identities();
|
|
3619
3674
|
init_studio_rag();
|
|
@@ -3653,8 +3708,10 @@ var init_studio_voice = __esmMin((() => {
|
|
|
3653
3708
|
mode: z.enum(["stt", "livekit"]),
|
|
3654
3709
|
minEndpointingDelayMs: millisecondsSchema,
|
|
3655
3710
|
maxEndpointingDelayMs: millisecondsSchema,
|
|
3656
|
-
detectorThreshold: thresholdSchema
|
|
3711
|
+
detectorThreshold: thresholdSchema,
|
|
3712
|
+
eagerThreshold: z.number().min(.3).max(.7)
|
|
3657
3713
|
});
|
|
3714
|
+
VOICE_EAGER_THRESHOLD_DEFAULT = .3;
|
|
3658
3715
|
interruptionsSchema = z.strictObject({
|
|
3659
3716
|
enabled: z.boolean(),
|
|
3660
3717
|
minDurationMs: millisecondsSchema,
|
|
@@ -3695,7 +3752,7 @@ var init_studio_voice = __esmMin((() => {
|
|
|
3695
3752
|
sttModel: z.enum(VOICE_STT_MODELS),
|
|
3696
3753
|
keyterms: keytermsSchema.default(() => []),
|
|
3697
3754
|
vad: vadSchema,
|
|
3698
|
-
turn: turnSchema,
|
|
3755
|
+
turn: turnSchema.extend({ eagerThreshold: turnSchema.shape.eagerThreshold.default(VOICE_EAGER_THRESHOLD_DEFAULT) }),
|
|
3699
3756
|
interruptions: interruptionsSchema,
|
|
3700
3757
|
ttsModel: z.enum(VOICE_TTS_MODELS),
|
|
3701
3758
|
/**
|
|
@@ -3774,6 +3831,7 @@ var init_studio_voice = __esmMin((() => {
|
|
|
3774
3831
|
"turn.minEndpointingDelayMs",
|
|
3775
3832
|
"turn.maxEndpointingDelayMs",
|
|
3776
3833
|
"turn.detectorThreshold",
|
|
3834
|
+
"turn.eagerThreshold",
|
|
3777
3835
|
"interruptions.enabled",
|
|
3778
3836
|
"interruptions.minDurationMs",
|
|
3779
3837
|
"interruptions.minWords",
|
|
@@ -3803,9 +3861,10 @@ var init_studio_voice = __esmMin((() => {
|
|
|
3803
3861
|
"code",
|
|
3804
3862
|
"default"
|
|
3805
3863
|
]);
|
|
3864
|
+
LEAVES_NEWER_THAN_SOME_BOXES = new Set(["turn.eagerThreshold"]);
|
|
3806
3865
|
effectiveVoiceSettingsViewSchema = z.strictObject({
|
|
3807
3866
|
value: effectiveVoiceSettingsObjectSchema,
|
|
3808
|
-
origins: z.strictObject(Object.fromEntries(VOICE_SETTINGS_LEAF_PATHS.map((path) => [path, voiceSettingOriginSchema])))
|
|
3867
|
+
origins: z.strictObject(Object.fromEntries(VOICE_SETTINGS_LEAF_PATHS.map((path) => [path, LEAVES_NEWER_THAN_SOME_BOXES.has(path) ? voiceSettingOriginSchema.default("default") : voiceSettingOriginSchema])))
|
|
3809
3868
|
});
|
|
3810
3869
|
checkTtsPair = (value, ctx) => {
|
|
3811
3870
|
if (value.ttsModel !== void 0 && value.ttsVoice === void 0) ctx.addIssue({
|
|
@@ -3915,6 +3974,7 @@ var init_studio_voice = __esmMin((() => {
|
|
|
3915
3974
|
aguiHeardSchema = z.strictObject({ text: z.string().min(1).max(HEARD_TEXT_MAX) });
|
|
3916
3975
|
VOICE_CALLS_PATH = "/voice/v1/calls";
|
|
3917
3976
|
voiceCallBootstrapSchema = z.strictObject({ identities: integrationIdentitiesSchema.optional() });
|
|
3977
|
+
voiceCallKnowledgeSchema = z.strictObject({ text: z.string().min(1).max(HEARD_TEXT_MAX) });
|
|
3918
3978
|
voiceCallTranscriptSchema = z.array(z.strictObject({
|
|
3919
3979
|
role: z.enum(["user", "assistant"]),
|
|
3920
3980
|
text: z.string().trim().min(1).max(16e3)
|
|
@@ -3929,7 +3989,9 @@ var init_studio_voice = __esmMin((() => {
|
|
|
3929
3989
|
transcriptionMs: turnLatencyMs,
|
|
3930
3990
|
llmTtftMs: turnLatencyMs,
|
|
3931
3991
|
ttsTtfbMs: turnLatencyMs,
|
|
3932
|
-
e2eMs: turnLatencyMs
|
|
3992
|
+
e2eMs: turnLatencyMs,
|
|
3993
|
+
/** How long before the final transcript the turn's last eager end of turn left for the box (STA3-94). */
|
|
3994
|
+
preflightLeadMs: turnLatencyMs
|
|
3933
3995
|
});
|
|
3934
3996
|
voiceCallTurnsSchema = z.array(voiceCallTurnMetricsSchema).max(VOICE_CALL_TURNS_MAX);
|
|
3935
3997
|
readVoiceCallTurns = (value) => {
|
|
@@ -3965,7 +4027,8 @@ var init_effective_voice_settings = __esmMin((() => {
|
|
|
3965
4027
|
mode: "stt",
|
|
3966
4028
|
minEndpointingDelayMs: 0,
|
|
3967
4029
|
maxEndpointingDelayMs: 1200,
|
|
3968
|
-
detectorThreshold: .35
|
|
4030
|
+
detectorThreshold: .35,
|
|
4031
|
+
eagerThreshold: VOICE_EAGER_THRESHOLD_DEFAULT
|
|
3969
4032
|
}),
|
|
3970
4033
|
interruptions: Object.freeze({
|
|
3971
4034
|
enabled: true,
|
|
@@ -6018,6 +6081,15 @@ var init_studio_catalog = __esmMin((() => {
|
|
|
6018
6081
|
/** The provider routing an operator picked for this key, or `null` when nobody has. */
|
|
6019
6082
|
selectedProviderSort: modelProviderSortSchema.nullable().default(null),
|
|
6020
6083
|
/**
|
|
6084
|
+
* The full provider routing the code writes for this key, or `null` when it writes
|
|
6085
|
+
* none. `defaultProviderSort` is the same routing as a plain sort, for a Studio
|
|
6086
|
+
* older than the object, and `null` whenever the routing is more than one sort.
|
|
6087
|
+
* Absent from a box that predates it.
|
|
6088
|
+
*/
|
|
6089
|
+
defaultProviderRouting: modelProviderRoutingSchema.nullable().optional(),
|
|
6090
|
+
/** The full provider routing an operator picked, as `defaultProviderRouting` is to the code's. */
|
|
6091
|
+
selectedProviderRouting: modelProviderRoutingSchema.nullable().optional(),
|
|
6092
|
+
/**
|
|
6021
6093
|
* `declared` — the code declares this key. `unused` — only a pick exists, for a
|
|
6022
6094
|
* key no declaration mentions any more; resetting it is the one thing left to do.
|
|
6023
6095
|
* An entry NOTHING scanned never carries an `unused` row: see the box's
|
|
@@ -9479,7 +9551,8 @@ var init_model_routing = __esmMin((() => {
|
|
|
9479
9551
|
ownerKind: text("owner_kind").notNull(),
|
|
9480
9552
|
ownerName: text("owner_name").notNull(),
|
|
9481
9553
|
key: text("key").notNull(),
|
|
9482
|
-
sort: text("sort")
|
|
9554
|
+
sort: text("sort"),
|
|
9555
|
+
routing: jsonb("routing"),
|
|
9483
9556
|
/** The acting user's id, or null on the un-authenticated `stackbone dev` datapath. */
|
|
9484
9557
|
updatedBy: text("updated_by"),
|
|
9485
9558
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
@@ -11406,7 +11479,7 @@ var init_json = __esmMin((() => {
|
|
|
11406
11479
|
//#region ../../libs/shared/model-provider-wire/src/lib/common/provider-routing.ts
|
|
11407
11480
|
var providerRoutingFor, withProviderRouting;
|
|
11408
11481
|
var init_provider_routing = __esmMin((() => {
|
|
11409
|
-
providerRoutingFor = (
|
|
11482
|
+
providerRoutingFor = (routing) => Object.freeze({ ...routing });
|
|
11410
11483
|
withProviderRouting = (body, kept) => {
|
|
11411
11484
|
if (kept || !("provider" in body)) return body;
|
|
11412
11485
|
const next = { ...body };
|
|
@@ -19613,6 +19686,7 @@ var init_learn_control_plane_identity = __esmMin((() => {
|
|
|
19613
19686
|
}));
|
|
19614
19687
|
//#endregion
|
|
19615
19688
|
//#region ../../libs/runtime/workspace-runtime/src/lib/catalog/catalog-projector.ts
|
|
19689
|
+
init_src$26();
|
|
19616
19690
|
/**
|
|
19617
19691
|
* `${kind}:${name}` — the identity a catalog row, a declaration, a prompt owner and
|
|
19618
19692
|
* a selection's owner all agree on. The kind is half of it, never decoration: a deep
|
|
@@ -19774,16 +19848,26 @@ var asDeclaredModel = (ref) => ({
|
|
|
19774
19848
|
overridable: ref.overridable,
|
|
19775
19849
|
file: ref.file,
|
|
19776
19850
|
line: ref.line,
|
|
19777
|
-
|
|
19851
|
+
...defaultRoutingFields(declaredRouting(ref)),
|
|
19778
19852
|
selectedModelId: null,
|
|
19779
19853
|
selectedProviderId: null,
|
|
19780
|
-
|
|
19854
|
+
...selectedRoutingFields(null),
|
|
19781
19855
|
state: "declared"
|
|
19782
19856
|
});
|
|
19857
|
+
/** The routing the code writes; a manifest older than the object carries only its sort. */
|
|
19858
|
+
var declaredRouting = (ref) => readModelProviderRouting(ref.defaultProviderRouting ?? ref.defaultProviderSort);
|
|
19859
|
+
var defaultRoutingFields = (routing) => ({
|
|
19860
|
+
defaultProviderRouting: routing,
|
|
19861
|
+
defaultProviderSort: plainSortOf(routing)
|
|
19862
|
+
});
|
|
19863
|
+
var selectedRoutingFields = (routing) => ({
|
|
19864
|
+
selectedProviderRouting: routing,
|
|
19865
|
+
selectedProviderSort: plainSortOf(routing)
|
|
19866
|
+
});
|
|
19783
19867
|
var selectedFields = (pick) => ({
|
|
19784
19868
|
selectedModelId: pick?.modelId ?? null,
|
|
19785
19869
|
selectedProviderId: pick?.providerId ?? null,
|
|
19786
|
-
|
|
19870
|
+
...selectedRoutingFields(pick?.providerRouting ?? null)
|
|
19787
19871
|
});
|
|
19788
19872
|
/**
|
|
19789
19873
|
* Cross what the code DECLARES against what an operator PICKED, for every row at
|
|
@@ -19817,7 +19901,7 @@ var attachModelSelections = (entries, selections) => entries.map((entry) => {
|
|
|
19817
19901
|
overridable: true,
|
|
19818
19902
|
file: null,
|
|
19819
19903
|
line: null,
|
|
19820
|
-
|
|
19904
|
+
...defaultRoutingFields(null),
|
|
19821
19905
|
...selectedFields(pick),
|
|
19822
19906
|
state: "unused"
|
|
19823
19907
|
}));
|
|
@@ -22674,7 +22758,7 @@ var init_role = __esmMin((() => {
|
|
|
22674
22758
|
var STACKBONE_CLI_BUILD_VERSION, EMULATOR_CAPABILITY_ROUTES, EMULATOR_CAPABILITIES, buildContractResponse;
|
|
22675
22759
|
var init_contract = __esmMin((() => {
|
|
22676
22760
|
init_src$26();
|
|
22677
|
-
STACKBONE_CLI_BUILD_VERSION = "0.9.
|
|
22761
|
+
STACKBONE_CLI_BUILD_VERSION = "0.9.2";
|
|
22678
22762
|
EMULATOR_CAPABILITY_ROUTES = {
|
|
22679
22763
|
"database.postgres_direct": "/api/db",
|
|
22680
22764
|
"rag.basic": "/api/rag",
|
|
@@ -28263,7 +28347,7 @@ var init_api_docs = __esmMin((() => {
|
|
|
28263
28347
|
},
|
|
28264
28348
|
{
|
|
28265
28349
|
name: VOICE_WORKER_TAG,
|
|
28266
|
-
description: "What the LiveKit voice worker calls on the box besides AG-UI: identity bootstrap before output, heard-call completion after awaited closure, and the relay that takes a call's spans to the box's OTLP collector. It takes the call token the room route put in the call's dispatch, and nothing else. Never called by Studio or by the SDK."
|
|
28350
|
+
description: "What the LiveKit voice worker calls on the box besides AG-UI: identity bootstrap before output, the knowledge search started on the caller's eager end of turn, heard-call completion after awaited closure, and the relay that takes a call's spans to the box's OTLP collector. It takes the call token the room route put in the call's dispatch, and nothing else. Never called by Studio or by the SDK."
|
|
28267
28351
|
}
|
|
28268
28352
|
];
|
|
28269
28353
|
SURFACE_TAG_DESCRIPTIONS = {
|
|
@@ -41640,6 +41724,11 @@ var init_mutations_outside_catalog = __esmMin((() => {
|
|
|
41640
41724
|
path: "/voice/v1/calls/{sessionKey}/bootstrap",
|
|
41641
41725
|
reason: "The accepted call freezes structured identity and recalls memory using its own call token, without an operator seat."
|
|
41642
41726
|
},
|
|
41727
|
+
{
|
|
41728
|
+
method: "POST",
|
|
41729
|
+
path: "/voice/v1/calls/{sessionKey}/knowledge",
|
|
41730
|
+
reason: "The call worker starts the knowledge search on the caller's eager end of turn using its own call token, without an operator seat. It persists nothing; its only caller is the LiveKit worker."
|
|
41731
|
+
},
|
|
41643
41732
|
{
|
|
41644
41733
|
method: "POST",
|
|
41645
41734
|
path: "/voice/v1/calls/{sessionKey}/complete",
|
|
@@ -65522,12 +65611,12 @@ var init_registry = __esmMin((() => {
|
|
|
65522
65611
|
};
|
|
65523
65612
|
const instructions = read(() => def.resolvedInstructions?.());
|
|
65524
65613
|
const model = read(() => def.resolvedModel?.());
|
|
65525
|
-
const
|
|
65526
|
-
const {
|
|
65614
|
+
const providerRouting = readModelProviderRouting(read(() => def.resolvedProviderRouting?.()) ?? read(() => def.resolvedProviderSort?.()));
|
|
65615
|
+
const { providerRouting: _previous, ...rest } = metadata;
|
|
65527
65616
|
return {
|
|
65528
65617
|
...rest,
|
|
65529
65618
|
...typeof model === "string" && model !== "" ? { model } : {},
|
|
65530
|
-
...
|
|
65619
|
+
...providerRouting !== null ? { providerRouting } : {},
|
|
65531
65620
|
...typeof instructions === "string" && instructions !== "" ? { instructions } : {}
|
|
65532
65621
|
};
|
|
65533
65622
|
};
|
|
@@ -73127,7 +73216,7 @@ var selectionsSelect = (kinds, names) => sql`
|
|
|
73127
73216
|
SELECT COALESCE(s.owner_kind, r.owner_kind) AS owner_kind,
|
|
73128
73217
|
COALESCE(s.owner_name, r.owner_name) AS owner_name,
|
|
73129
73218
|
COALESCE(s.key, r.key) AS key,
|
|
73130
|
-
s.model_id, s.provider_id, r.sort
|
|
73219
|
+
s.model_id, s.provider_id, r.sort, r.routing
|
|
73131
73220
|
FROM ${modelSelections} s
|
|
73132
73221
|
FULL OUTER JOIN ${modelRouting} r
|
|
73133
73222
|
ON r.owner_kind = s.owner_kind AND r.owner_name = s.owner_name AND r.key = s.key
|
|
@@ -73148,7 +73237,7 @@ var createModelSelectionsReader = (opts) => {
|
|
|
73148
73237
|
keys.set(row.key, {
|
|
73149
73238
|
modelId: row.model_id,
|
|
73150
73239
|
providerId: row.provider_id,
|
|
73151
|
-
|
|
73240
|
+
providerRouting: readModelProviderRouting(row.routing ?? row.sort)
|
|
73152
73241
|
});
|
|
73153
73242
|
byOwner.set(id, keys);
|
|
73154
73243
|
}
|
|
@@ -86683,14 +86772,19 @@ var notFound$1 = {
|
|
|
86683
86772
|
code: "studio_model_selection_not_found",
|
|
86684
86773
|
message: "No model is picked for that key. Its code default already applies."
|
|
86685
86774
|
};
|
|
86686
|
-
var rowToRouting = (row) =>
|
|
86687
|
-
|
|
86688
|
-
|
|
86689
|
-
|
|
86690
|
-
|
|
86691
|
-
|
|
86692
|
-
|
|
86693
|
-
|
|
86775
|
+
var rowToRouting = (row) => {
|
|
86776
|
+
const routing = readModelProviderRouting(row.routing ?? row.sort);
|
|
86777
|
+
if (!routing) throw new Error("model_routing_row_unreadable");
|
|
86778
|
+
return {
|
|
86779
|
+
ownerKind: "agent",
|
|
86780
|
+
ownerName: row.ownerName,
|
|
86781
|
+
key: row.key,
|
|
86782
|
+
routing,
|
|
86783
|
+
sort: plainSortOf(routing),
|
|
86784
|
+
updatedBy: row.updatedBy,
|
|
86785
|
+
updatedAt: row.updatedAt.toISOString()
|
|
86786
|
+
};
|
|
86787
|
+
};
|
|
86694
86788
|
var routingNotFound = {
|
|
86695
86789
|
code: "studio_model_routing_not_found",
|
|
86696
86790
|
message: "No provider routing is picked for that key. The code's `modelRouting` already applies."
|
|
@@ -86820,10 +86914,10 @@ var createModelSelectionsRouter = (opts) => {
|
|
|
86820
86914
|
tags: [MODEL_SELECTIONS_TAG],
|
|
86821
86915
|
middleware: [requireCapability("config:write")],
|
|
86822
86916
|
summary: "Pick the provider routing for one key",
|
|
86823
|
-
description: "Sets which OpenRouter
|
|
86917
|
+
description: "Sets which OpenRouter providers one model key of a deep agent goes to: OpenRouter's `provider` object on its model calls, e.g. `{ \"routing\": { \"only\": [\"deepinfra/fp8\"], \"allow_fallbacks\": false } }`, or `{ \"sort\": \"latency\" }` as shorthand for `{ \"routing\": { \"sort\": \"latency\" } }`. It outranks the code's `modelRouting`, and a subagent key with none follows the agent's. The box drops the field on any gateway that is not OpenRouter. The agent is rebuilt to apply it.",
|
|
86824
86918
|
request: {
|
|
86825
86919
|
params: studioModelRoutingPathSchema,
|
|
86826
|
-
body: jsonBody(named("PutStudioModelRoutingRequest", putStudioModelRoutingRequestSchema), "The provider sort.")
|
|
86920
|
+
body: jsonBody(named("PutStudioModelRoutingRequest", putStudioModelRoutingRequestSchema), "The provider routing, or a plain sort.")
|
|
86827
86921
|
},
|
|
86828
86922
|
responses: {
|
|
86829
86923
|
200: jsonResponse$1(named("StudioModelRouting", studioModelRoutingSchema), "The routing as it now stands."),
|
|
@@ -86832,12 +86926,14 @@ var createModelSelectionsRouter = (opts) => {
|
|
|
86832
86926
|
}
|
|
86833
86927
|
}), async (c) => {
|
|
86834
86928
|
const path = c.req.valid("param");
|
|
86835
|
-
const
|
|
86929
|
+
const routing = routingOfPutRequest(c.req.valid("json"));
|
|
86930
|
+
const sort = plainSortOf(routing);
|
|
86836
86931
|
const actor = readActorUserId(c);
|
|
86837
86932
|
const row = await opts.db.transaction(async (tx) => {
|
|
86838
86933
|
const [written] = await tx.insert(modelRouting).values({
|
|
86839
86934
|
...path,
|
|
86840
86935
|
sort,
|
|
86936
|
+
routing,
|
|
86841
86937
|
updatedBy: actor
|
|
86842
86938
|
}).onConflictDoUpdate({
|
|
86843
86939
|
target: [
|
|
@@ -86847,6 +86943,7 @@ var createModelSelectionsRouter = (opts) => {
|
|
|
86847
86943
|
],
|
|
86848
86944
|
set: {
|
|
86849
86945
|
sort,
|
|
86946
|
+
routing,
|
|
86850
86947
|
updatedBy: actor,
|
|
86851
86948
|
updatedAt: sql`now()`
|
|
86852
86949
|
}
|
|
@@ -86857,7 +86954,8 @@ var createModelSelectionsRouter = (opts) => {
|
|
|
86857
86954
|
subjectType: "model_selection",
|
|
86858
86955
|
payload: {
|
|
86859
86956
|
...selectionSubject(path),
|
|
86860
|
-
sort
|
|
86957
|
+
...sort !== null ? { sort } : {},
|
|
86958
|
+
routing
|
|
86861
86959
|
}
|
|
86862
86960
|
}, tx);
|
|
86863
86961
|
return written;
|
|
@@ -93386,11 +93484,17 @@ var answerOf = async (storage, row) => {
|
|
|
93386
93484
|
};
|
|
93387
93485
|
//#endregion
|
|
93388
93486
|
//#region ../../libs/runtime/workspace-runtime/src/lib/voice/voice-rag.ts
|
|
93389
|
-
var WORD_CHARACTER, spokenWordCount, knowledgeQuery, selectKnowledge;
|
|
93487
|
+
var WORD_CHARACTER, spokenWordCount, knowledgeQuery, PUNCTUATION_OR_SYMBOL_RUN, prefetchSentence, prefetchKey, selectKnowledge;
|
|
93390
93488
|
var init_voice_rag = __esmMin((() => {
|
|
93391
93489
|
WORD_CHARACTER = /[\p{L}\p{N}]/u;
|
|
93392
93490
|
spokenWordCount = (text) => text.trim().split(/\s+/u).filter((token) => WORD_CHARACTER.test(token)).length;
|
|
93393
93491
|
knowledgeQuery = ({ exchanges, sentence }) => [...exchanges.flatMap(({ question, reply }) => [question, reply]), sentence].map((piece) => piece.trim()).filter((piece) => piece !== "").join("\n");
|
|
93492
|
+
PUNCTUATION_OR_SYMBOL_RUN = /[\p{P}\p{S}]+/gu;
|
|
93493
|
+
prefetchSentence = (text) => text.normalize("NFC").toLowerCase().replace(PUNCTUATION_OR_SYMBOL_RUN, " ").replace(/\s+/gu, " ").trim();
|
|
93494
|
+
prefetchKey = ({ exchanges, sentence }) => knowledgeQuery({
|
|
93495
|
+
exchanges,
|
|
93496
|
+
sentence: prefetchSentence(sentence)
|
|
93497
|
+
});
|
|
93394
93498
|
selectKnowledge = (hits, { minScore, topK }) => {
|
|
93395
93499
|
const passages = hits.filter((hit) => Number.isFinite(hit.score) && hit.score >= minScore).map((hit) => hit.content).filter((content) => typeof content === "string" && content.trim() !== "").slice(0, topK);
|
|
93396
93500
|
return passages.length === 0 ? void 0 : JSON.stringify(passages);
|
|
@@ -93436,7 +93540,7 @@ var init_voice_calls = __esmMin((() => {
|
|
|
93436
93540
|
const words = spokenWordCount(text);
|
|
93437
93541
|
return words !== 0 && words >= rag.minWords;
|
|
93438
93542
|
};
|
|
93439
|
-
const searchKnowledge = async (call, text) => {
|
|
93543
|
+
const searchKnowledge = async (call, text, origin = "turn") => {
|
|
93440
93544
|
const { rag } = call;
|
|
93441
93545
|
if (!searchable(rag, text)) return void 0;
|
|
93442
93546
|
const { collection } = rag;
|
|
@@ -93444,6 +93548,7 @@ var init_voice_calls = __esmMin((() => {
|
|
|
93444
93548
|
exchanges: call.exchanges,
|
|
93445
93549
|
sentence: text
|
|
93446
93550
|
});
|
|
93551
|
+
const search = origin === "prefetch" ? "rag_prefetch" : "rag_search";
|
|
93447
93552
|
const warn = (code) => options.logger.warn(`voice ${call.input.agent}/${call.input.sessionKey}: ${code}.`);
|
|
93448
93553
|
try {
|
|
93449
93554
|
const result = await withTimeout(retrieve({
|
|
@@ -93452,7 +93557,7 @@ var init_voice_calls = __esmMin((() => {
|
|
|
93452
93557
|
topK: rag.topK
|
|
93453
93558
|
}), rag.timeoutMs);
|
|
93454
93559
|
if (result.error) {
|
|
93455
|
-
warn(
|
|
93560
|
+
warn(`${search}_failed (${result.error.code})`);
|
|
93456
93561
|
return;
|
|
93457
93562
|
}
|
|
93458
93563
|
if (result.data.length === 0 && !call.ragEmptyWarned) {
|
|
@@ -93461,11 +93566,31 @@ var init_voice_calls = __esmMin((() => {
|
|
|
93461
93566
|
}
|
|
93462
93567
|
return selectKnowledge(result.data, rag);
|
|
93463
93568
|
} catch (error) {
|
|
93464
|
-
if (error instanceof ActionTimeoutError) warn(
|
|
93465
|
-
else warn(
|
|
93569
|
+
if (error instanceof ActionTimeoutError) warn(`${search}_timeout`);
|
|
93570
|
+
else warn(`${search}_failed (${error instanceof Error ? error.name : typeof error})`);
|
|
93466
93571
|
}
|
|
93467
93572
|
};
|
|
93573
|
+
let inputGuard;
|
|
93468
93574
|
return {
|
|
93575
|
+
useInputGuard: (guard) => {
|
|
93576
|
+
inputGuard = guard;
|
|
93577
|
+
},
|
|
93578
|
+
prefetchKnowledge: async (agent, sessionKey, text) => {
|
|
93579
|
+
const call = find(agent, sessionKey);
|
|
93580
|
+
const sequence = ++call.prefetches;
|
|
93581
|
+
const guard = inputGuard;
|
|
93582
|
+
if (!guard || call.completion || !searchable(call.rag, text)) return;
|
|
93583
|
+
const guarded = await Promise.resolve().then(() => guard(agent, text)).catch(() => "skip");
|
|
93584
|
+
if (sequence !== call.prefetches || call.completion) return;
|
|
93585
|
+
if (guarded === "skip" || guarded.blocked || !searchable(call.rag, guarded.content)) return;
|
|
93586
|
+
call.prefetch = {
|
|
93587
|
+
key: prefetchKey({
|
|
93588
|
+
exchanges: call.exchanges,
|
|
93589
|
+
sentence: guarded.content
|
|
93590
|
+
}),
|
|
93591
|
+
promise: searchKnowledge(call, guarded.content, "prefetch")
|
|
93592
|
+
};
|
|
93593
|
+
},
|
|
93469
93594
|
accept: async (input) => {
|
|
93470
93595
|
const registry = options.getRegistry();
|
|
93471
93596
|
if (!registry) throw new Error("Voice call memory service is unavailable.");
|
|
@@ -93479,6 +93604,7 @@ var init_voice_calls = __esmMin((() => {
|
|
|
93479
93604
|
rag: input.rag && Object.freeze({ ...input.rag }),
|
|
93480
93605
|
refusalPhrase: input.refusalPhrase ?? VOICE_SETTINGS_DEFAULTS.refusalPhrase,
|
|
93481
93606
|
exchanges: [],
|
|
93607
|
+
prefetches: 0,
|
|
93482
93608
|
startedAt,
|
|
93483
93609
|
expiresAt: startedAt + CALL_TOKEN_TTL_SECONDS * 1e3,
|
|
93484
93610
|
work: /* @__PURE__ */ new Set()
|
|
@@ -93545,7 +93671,21 @@ var init_voice_calls = __esmMin((() => {
|
|
|
93545
93671
|
done();
|
|
93546
93672
|
};
|
|
93547
93673
|
},
|
|
93548
|
-
knowledgeFor: (text) =>
|
|
93674
|
+
knowledgeFor: (text, notePrefetch) => {
|
|
93675
|
+
const { prefetch } = call;
|
|
93676
|
+
delete call.prefetch;
|
|
93677
|
+
call.prefetches++;
|
|
93678
|
+
if (!searchable(call.rag, text)) return Promise.resolve(void 0);
|
|
93679
|
+
if (prefetch?.key === prefetchKey({
|
|
93680
|
+
exchanges: call.exchanges,
|
|
93681
|
+
sentence: text
|
|
93682
|
+
})) {
|
|
93683
|
+
notePrefetch?.("hit");
|
|
93684
|
+
return prefetch.promise;
|
|
93685
|
+
}
|
|
93686
|
+
notePrefetch?.(prefetch ? "miss" : "none");
|
|
93687
|
+
return searchKnowledge(call, text);
|
|
93688
|
+
}
|
|
93549
93689
|
});
|
|
93550
93690
|
call.context = context;
|
|
93551
93691
|
if (call.input.startRecording) Promise.resolve().then(() => call.input.startRecording(sessionAddressFor(agent, sessionKey, void 0, sessionIdentityFor(identity)))).catch(() => options.logger.warn(`voice ${agent}/${sessionKey}: recording_start_failed.`));
|
|
@@ -93634,7 +93774,7 @@ var createVoiceRoomsRouter = (options) => {
|
|
|
93634
93774
|
middleware: [requireCapability(VOICE_CAPABILITY$1)],
|
|
93635
93775
|
"x-stackbone-catalog": excludeFromCatalog("Answers a LiveKit participant token. The operation catalog never offers an operation whose answer is a credential."),
|
|
93636
93776
|
summary: "Open a voice call with a deep agent",
|
|
93637
|
-
description: "Opens a fresh room `call-<agent>-<8 hex>` and answers the LiveKit URL, the browser's token to join it (it joins within five minutes; a call already running is not cut) and the settings the call runs with. The token carries the room's agent dispatch: joining the room sends the voice worker in with the agent's name, its effective voice settings, the session key `voice-<room>` every turn lands in, the tags each turn's run carries (`channel: voice`, `room`, and `origin`, which the caller states and which defaults to `studio`), the box's public URL the worker posts its turns to, and a call token: the worker's bearer for this call, valid two hours, until the box restarts, on the AG-UI chat of this agent and this session key on the trace relay `POST /voice/v1/traces`, and on this call's identity bootstrap and heard completion at `/voice/v1/calls/{sessionKey}/bootstrap` and `/voice/v1/calls/{sessionKey}/complete`, nowhere else. The box freezes memory policy, the agent's knowledge settings (`rag`) and its refusal phrase (`refusalPhrase`, what the caller hears when a guardrail stops their sentence or the agent's reply), none of them in the dispatch, at room acceptance, so a change saved in Studio reaches the next call; the worker supplies the original SIP participant's phoneNumber before output, or the browser call uses explicit identities supplied on this request. The bootstrap response contains no recalled facts or policy. On a box that exports traces (`OTEL_EXPORTER_OTLP_*`), the dispatch also asks the worker to send the call's spans to that relay with that token. The call is recorded to the box bucket as audio-only OGG only when the agent's `recording` voice setting is on (it is off by default) and the box bucket is not on a loopback or private host; a recording that fails never fails the call. The LiveKit API key and secret are never answered. The call token is answered only inside the room token's dispatch, which anyone holding the room token can read.",
|
|
93777
|
+
description: "Opens a fresh room `call-<agent>-<8 hex>` and answers the LiveKit URL, the browser's token to join it (it joins within five minutes; a call already running is not cut) and the settings the call runs with. The token carries the room's agent dispatch: joining the room sends the voice worker in with the agent's name, its effective voice settings, the session key `voice-<room>` every turn lands in, the tags each turn's run carries (`channel: voice`, `room`, and `origin`, which the caller states and which defaults to `studio`), the box's public URL the worker posts its turns to, and a call token: the worker's bearer for this call, valid two hours, until the box restarts, on the AG-UI chat of this agent and this session key on the trace relay `POST /voice/v1/traces`, and on this call's identity bootstrap, knowledge prefetch and heard completion at `/voice/v1/calls/{sessionKey}/bootstrap`, `/voice/v1/calls/{sessionKey}/knowledge` and `/voice/v1/calls/{sessionKey}/complete`, nowhere else. The box freezes memory policy, the agent's knowledge settings (`rag`) and its refusal phrase (`refusalPhrase`, what the caller hears when a guardrail stops their sentence or the agent's reply), none of them in the dispatch, at room acceptance, so a change saved in Studio reaches the next call; the worker supplies the original SIP participant's phoneNumber before output, or the browser call uses explicit identities supplied on this request. The bootstrap response contains no recalled facts or policy. On a box that exports traces (`OTEL_EXPORTER_OTLP_*`), the dispatch also asks the worker to send the call's spans to that relay with that token. The call is recorded to the box bucket as audio-only OGG only when the agent's `recording` voice setting is on (it is off by default) and the box bucket is not on a loopback or private host; a recording that fails never fails the call. The LiveKit API key and secret are never answered. The call token is answered only inside the room token's dispatch, which anyone holding the room token can read.",
|
|
93638
93778
|
request: { body: jsonBody(named("VoiceRoomRequest", voiceRoomRequestSchema), "The agent to call.") },
|
|
93639
93779
|
responses: {
|
|
93640
93780
|
200: jsonResponse$1(named("VoiceRoomResponse", voiceRoomResponseSchema), "The room, its participant token, and what the dispatch carries."),
|
|
@@ -94092,18 +94232,21 @@ var createVoiceCallsRouter = (options) => {
|
|
|
94092
94232
|
options.logger.warn(principal?.type === "call" ? `voice ${principal.agent}/${principal.sessionKey}: lifecycle_failed.` : "voice lifecycle request failed.");
|
|
94093
94233
|
return c.json(errorEnvelope("voice_lifecycle_failed", "The voice lifecycle operation failed."), 500);
|
|
94094
94234
|
});
|
|
94235
|
+
const callResponses = {
|
|
94236
|
+
200: jsonResponse$1(named("VoiceCallLifecycleResponse", voiceCallLifecycleResponseSchema), "The call operation completed."),
|
|
94237
|
+
400: errorResponse("Invalid bounded call payload."),
|
|
94238
|
+
401: errorResponse("Missing, invalid or expired call token."),
|
|
94239
|
+
403: errorResponse("Only the call token for this session is admitted."),
|
|
94240
|
+
404: errorResponse("No accepted call for this credential."),
|
|
94241
|
+
413: errorResponse("The payload exceeds 1 MiB.")
|
|
94242
|
+
};
|
|
94095
94243
|
const shared = {
|
|
94096
94244
|
method: "post",
|
|
94097
94245
|
tags: ["voice-worker"],
|
|
94098
94246
|
security: [{ voiceCallToken: [] }],
|
|
94099
94247
|
responses: {
|
|
94100
|
-
|
|
94101
|
-
400: errorResponse("Invalid bounded call payload."),
|
|
94102
|
-
401: errorResponse("Missing, invalid or expired call token."),
|
|
94103
|
-
403: errorResponse("Only the call token for this session is admitted."),
|
|
94104
|
-
404: errorResponse("No accepted call for this credential."),
|
|
94248
|
+
...callResponses,
|
|
94105
94249
|
409: errorResponse("The call identity is frozen or the call is completing."),
|
|
94106
|
-
413: errorResponse("The payload exceeds 1 MiB."),
|
|
94107
94250
|
500: errorResponse("Settings, recall or extraction failed.")
|
|
94108
94251
|
}
|
|
94109
94252
|
};
|
|
@@ -94121,6 +94264,22 @@ var createVoiceCallsRouter = (options) => {
|
|
|
94121
94264
|
await options.calls.bootstrap(principal.agent, principal.sessionKey, c.req.valid("json").identities);
|
|
94122
94265
|
return c.json({ ok: true }, 200);
|
|
94123
94266
|
});
|
|
94267
|
+
app.openapi(createRoute({
|
|
94268
|
+
...shared,
|
|
94269
|
+
responses: callResponses,
|
|
94270
|
+
path: "/{sessionKey}/knowledge",
|
|
94271
|
+
summary: "Start the knowledge search on the caller's eager end of turn",
|
|
94272
|
+
description: "Runs the agent's deterministic input guardrails over the sentence the speech model guessed ends the caller's turn, starts the knowledge search on the masked text and answers without waiting for it. The turn whose sentence matches it, ignoring case, spacing and punctuation, takes those passages instead of searching again. An agent without a knowledge collection or with a model-backed input guardrail, a sentence too short to search, a blocked sentence and a completing call answer the same and start nothing. Writes nothing to the conversation and calls no chat model.",
|
|
94273
|
+
request: {
|
|
94274
|
+
params: z$1.object({ sessionKey: z$1.string() }),
|
|
94275
|
+
body: jsonBody(named("VoiceCallKnowledge", voiceCallKnowledgeSchema), "The sentence the speech model guessed ends the caller's turn.")
|
|
94276
|
+
}
|
|
94277
|
+
}), async (c) => {
|
|
94278
|
+
const principal = readRequestPrincipal(c);
|
|
94279
|
+
if (principal.type !== "call") throw new Error("Missing call principal");
|
|
94280
|
+
await options.calls.prefetchKnowledge(principal.agent, principal.sessionKey, c.req.valid("json").text);
|
|
94281
|
+
return c.json({ ok: true }, 200);
|
|
94282
|
+
});
|
|
94124
94283
|
app.openapi(createRoute({
|
|
94125
94284
|
...shared,
|
|
94126
94285
|
path: "/{sessionKey}/complete",
|
|
@@ -94335,7 +94494,7 @@ var modelWarmupBody = (target) => ({
|
|
|
94335
94494
|
content: WARMUP_USER_TEXT
|
|
94336
94495
|
}],
|
|
94337
94496
|
reasoning: { enabled: true },
|
|
94338
|
-
...target.
|
|
94497
|
+
...target.providerRouting ? { provider: providerRoutingFor(target.providerRouting) } : {}
|
|
94339
94498
|
});
|
|
94340
94499
|
var createModelWarmup = (deps) => {
|
|
94341
94500
|
const timeoutMs = deps.timeoutMs ?? 2e4;
|
|
@@ -94346,7 +94505,7 @@ var createModelWarmup = (deps) => {
|
|
|
94346
94505
|
const body = modelWarmupBody({
|
|
94347
94506
|
model: entry.metadata.model,
|
|
94348
94507
|
instructions: entry.metadata.instructions,
|
|
94349
|
-
|
|
94508
|
+
providerRouting: entry.metadata.providerRouting
|
|
94350
94509
|
});
|
|
94351
94510
|
const signal = AbortSignal.timeout(timeoutMs);
|
|
94352
94511
|
try {
|
|
@@ -95146,7 +95305,7 @@ async function probeDurableInterruptState(graph, threadId, logger) {
|
|
|
95146
95305
|
return { supported: false };
|
|
95147
95306
|
}
|
|
95148
95307
|
}
|
|
95149
|
-
var nowIso, CLIENT_MOVED_ON, CLIENT_TOOL_ANSWER_ROUNDS, createDeepAgentChat, hintDurableSessions, voiceCallConfigurable, guardrailHoldReply, toDecisionStep, toRedactionStep, toParkStep;
|
|
95308
|
+
var nowIso, CLIENT_MOVED_ON, CLIENT_TOOL_ANSWER_ROUNDS, createDeepAgentChat, hintDurableSessions, voiceCallConfigurable, guardrailHoldReply, toDecisionStep, toRedactionStep, toParkStep, agentPolicy, evaluateInput;
|
|
95150
95309
|
var init_chat = __esmMin((() => {
|
|
95151
95310
|
init_studio_continuation();
|
|
95152
95311
|
init_abortable();
|
|
@@ -95218,10 +95377,7 @@ var init_chat = __esmMin((() => {
|
|
|
95218
95377
|
async function readPolicy(agentName) {
|
|
95219
95378
|
if (!opts.guardrails) return [];
|
|
95220
95379
|
try {
|
|
95221
|
-
return await opts.guardrails
|
|
95222
|
-
kind: "agent",
|
|
95223
|
-
name: agentName
|
|
95224
|
-
});
|
|
95380
|
+
return await agentPolicy(opts.guardrails, agentName);
|
|
95225
95381
|
} catch (err) {
|
|
95226
95382
|
logger.warn(`deep-agent guardrail policy read failed for "${agentName}" — the turn runs unguarded: ${messageOf$6(err)}`);
|
|
95227
95383
|
return [];
|
|
@@ -95249,13 +95405,7 @@ var init_chat = __esmMin((() => {
|
|
|
95249
95405
|
if (guardrails.length === 0) return unguarded;
|
|
95250
95406
|
try {
|
|
95251
95407
|
const original = lastUserMessage(turn.messages);
|
|
95252
|
-
const
|
|
95253
|
-
const result = await evaluateGuardrails({
|
|
95254
|
-
guardrails,
|
|
95255
|
-
content: original,
|
|
95256
|
-
target: "input",
|
|
95257
|
-
...classify ? { classify } : {}
|
|
95258
|
-
});
|
|
95408
|
+
const result = await evaluateInput(guardrails, original, classifierFor(signal));
|
|
95259
95409
|
return {
|
|
95260
95410
|
turn: result.content === original ? turn : withLastUserMessage(turn, result.content),
|
|
95261
95411
|
blocked: result.allowed ? null : result.decision,
|
|
@@ -95268,6 +95418,27 @@ var init_chat = __esmMin((() => {
|
|
|
95268
95418
|
}
|
|
95269
95419
|
}
|
|
95270
95420
|
/**
|
|
95421
|
+
* The input border's deterministic rules over one sentence that is not a turn yet:
|
|
95422
|
+
* the guess a voice call's knowledge prefetch searches (STA3-94 D2). It never asks
|
|
95423
|
+
* the classifier, which the guardrail ADR spends once per turn, and writes no run
|
|
95424
|
+
* step: the turn that follows evaluates the same rules and records them.
|
|
95425
|
+
*/
|
|
95426
|
+
const guardInput = async (agentName, text) => {
|
|
95427
|
+
let guardrails = [];
|
|
95428
|
+
if (opts.guardrails) try {
|
|
95429
|
+
guardrails = await agentPolicy(opts.guardrails, agentName);
|
|
95430
|
+
} catch {
|
|
95431
|
+
return "skip";
|
|
95432
|
+
}
|
|
95433
|
+
const input = guardrails.filter((rule) => rule.enabled && rule.target === "input");
|
|
95434
|
+
if (input.some((rule) => isModelBackedCheck(rule.checkType))) return "skip";
|
|
95435
|
+
const result = await evaluateInput(input, text);
|
|
95436
|
+
return {
|
|
95437
|
+
blocked: !result.allowed,
|
|
95438
|
+
content: result.content
|
|
95439
|
+
};
|
|
95440
|
+
};
|
|
95441
|
+
/**
|
|
95271
95442
|
* Wrap a turn's stream with the OUTPUT border: the reply is held until the policy
|
|
95272
95443
|
* has judged it, then released, released masked, or dropped with a reason.
|
|
95273
95444
|
*
|
|
@@ -95573,7 +95744,10 @@ var init_chat = __esmMin((() => {
|
|
|
95573
95744
|
const guardedTurn = guarded.turn;
|
|
95574
95745
|
const blocked = guarded.blocked;
|
|
95575
95746
|
const userMessage = lastUserMessage(guardedTurn.messages);
|
|
95576
|
-
|
|
95747
|
+
let prefetch;
|
|
95748
|
+
const knowledge = registered && blocked === null ? voiceCall?.knowledgeFor(userMessage, (outcome) => {
|
|
95749
|
+
prefetch = outcome;
|
|
95750
|
+
}) : void 0;
|
|
95577
95751
|
const runInput = {
|
|
95578
95752
|
trigger,
|
|
95579
95753
|
input: { message: userMessage },
|
|
@@ -95583,6 +95757,7 @@ var init_chat = __esmMin((() => {
|
|
|
95583
95757
|
memoryIdentity,
|
|
95584
95758
|
...voiceCall ? { voiceCall: true } : {},
|
|
95585
95759
|
...voiceCall?.searchesKnowledge ? { voiceKnowledgeRule: true } : {},
|
|
95760
|
+
...prefetch ? { voiceKnowledgePrefetch: prefetch } : {},
|
|
95586
95761
|
...groupingKey !== void 0 ? { sessionKey: groupingKey } : {},
|
|
95587
95762
|
...turn.evalRunId !== void 0 ? evalRunStamp({
|
|
95588
95763
|
evalRunId: turn.evalRunId,
|
|
@@ -96317,6 +96492,7 @@ var init_chat = __esmMin((() => {
|
|
|
96317
96492
|
getMessageHistory,
|
|
96318
96493
|
seedState,
|
|
96319
96494
|
reconcileHeard,
|
|
96495
|
+
guardInput,
|
|
96320
96496
|
runTurn: (turn) => ownedByAgent(turn.agent, runTurn(turn)),
|
|
96321
96497
|
resumeTurn: ownedResumeTurn
|
|
96322
96498
|
};
|
|
@@ -96343,6 +96519,16 @@ var init_chat = __esmMin((() => {
|
|
|
96343
96519
|
...guardrailParkRow(decision, approvalId),
|
|
96344
96520
|
status: "running"
|
|
96345
96521
|
});
|
|
96522
|
+
agentPolicy = (reader, agentName) => reader.effectiveGuardrails({
|
|
96523
|
+
kind: "agent",
|
|
96524
|
+
name: agentName
|
|
96525
|
+
});
|
|
96526
|
+
evaluateInput = (guardrails, content, classify) => evaluateGuardrails({
|
|
96527
|
+
guardrails,
|
|
96528
|
+
content,
|
|
96529
|
+
target: "input",
|
|
96530
|
+
...classify ? { classify } : {}
|
|
96531
|
+
});
|
|
96346
96532
|
}));
|
|
96347
96533
|
//#endregion
|
|
96348
96534
|
//#region ../../libs/runtime/workspace-runtime/src/lib/deep-agents/http/run-tags-header.ts
|
|
@@ -97001,10 +97187,12 @@ var init_mount$1 = __esmMin((() => {
|
|
|
97001
97187
|
...options.classify ? { classify: options.classify } : {},
|
|
97002
97188
|
...options.plantAmbientBridges ? { plantAmbientBridges: options.plantAmbientBridges } : {}
|
|
97003
97189
|
});
|
|
97190
|
+
const voiceCalls = voiceCallsForApp(app);
|
|
97191
|
+
voiceCalls?.useInputGuard(chat.guardInput);
|
|
97004
97192
|
const wire = {
|
|
97005
97193
|
sessions: options.sessions,
|
|
97006
97194
|
invocations: options.invocations,
|
|
97007
|
-
voiceCalls
|
|
97195
|
+
voiceCalls,
|
|
97008
97196
|
chat,
|
|
97009
97197
|
registry,
|
|
97010
97198
|
logger,
|
|
@@ -104335,7 +104523,7 @@ var calls_workflow_ts_default = "import { z } from \"zod\";\nimport { streamDeep
|
|
|
104335
104523
|
var plain_workflow_ts_default = "import { z } from \"zod\";\n\n/**\n * Workflow \"{{name}}\" — a durable pipeline.\n *\n * Each `\"use step\"` is a durable checkpoint: kill the runtime mid-run and it\n * resumes from the last completed step instead of replaying everything.\n */\n\n// THE contract — sibling exports next to the bare workflow function. The build\n// harvests these (by convention) into the manifest + a live validator. The\n// parameter type is DERIVED from `inputSchema` and the RETURN type from\n// `outputSchema` (both `z.infer`), so neither side can drift from the contract\n// the build harvests. Without the return annotation the schema is published to\n// Studio and the generated types and never compared to what the body returns\n// (PLA-1272).\nexport const inputSchema = z.object({\n message: z.string().describe(\"The input to process\"),\n});\n\nexport const outputSchema = z.object({\n message: z.string(),\n processed: z.boolean(),\n});\n\ntype {{Name}}Input = z.infer<typeof inputSchema>;\n\nexport async function {{export}}(input: {{Name}}Input): Promise<z.infer<typeof outputSchema>> {\n \"use workflow\";\n\n const result = await processMessage(input); // step 1 — durable checkpoint\n return result;\n}\n\nasync function processMessage(input: {{Name}}Input) {\n \"use step\";\n // Replace this with the deterministic work of your step (DB write, HTTP call,\n // an LLM call, …). Returning a value records it as the step's checkpoint.\n return { message: input.message, processed: true };\n}\n";
|
|
104336
104524
|
//#endregion
|
|
104337
104525
|
//#region src/services/scaffold/template-loader.ts
|
|
104338
|
-
var SDK_TEMPLATE_VERSION = "0.10.
|
|
104526
|
+
var SDK_TEMPLATE_VERSION = "0.10.2";
|
|
104339
104527
|
var PREFIX = "./templates/";
|
|
104340
104528
|
var templates = new Map(Object.entries(/* @__PURE__ */ Object.assign({
|
|
104341
104529
|
"./templates/deep-agents/index.ts.tmpl": index_ts_default,
|
|
@@ -106849,6 +107037,57 @@ var literalText = (ts, node) => {
|
|
|
106849
107037
|
return null;
|
|
106850
107038
|
};
|
|
106851
107039
|
/**
|
|
107040
|
+
* A value written down as JSON would be: strings, numbers (a leading `-` included),
|
|
107041
|
+
* booleans, `null`, and arrays and object literals of those. `undefined` for
|
|
107042
|
+
* anything else ANYWHERE inside it — an identifier, a spread, a computed key, an
|
|
107043
|
+
* interpolation — because a value read in part is a value guessed at. `as const`
|
|
107044
|
+
* and `satisfies` around it are type-only and looked through.
|
|
107045
|
+
*/
|
|
107046
|
+
var literalJson = (ts, node) => {
|
|
107047
|
+
const K = ts.SyntaxKind;
|
|
107048
|
+
if (!node) return void 0;
|
|
107049
|
+
const text = literalText(ts, node);
|
|
107050
|
+
if (text !== null) return text;
|
|
107051
|
+
switch (node.kind) {
|
|
107052
|
+
case K["AsExpression"]:
|
|
107053
|
+
case K["SatisfiesExpression"]:
|
|
107054
|
+
case K["ParenthesizedExpression"]: return literalJson(ts, node.expression);
|
|
107055
|
+
case K["NumericLiteral"]: return Number(node.text);
|
|
107056
|
+
case K["TrueKeyword"]: return true;
|
|
107057
|
+
case K["FalseKeyword"]: return false;
|
|
107058
|
+
case K["NullKeyword"]: return null;
|
|
107059
|
+
case K["PrefixUnaryExpression"]: {
|
|
107060
|
+
const unary = node;
|
|
107061
|
+
if (unary.operator !== K["MinusToken"] || unary.operand.kind !== K["NumericLiteral"]) return;
|
|
107062
|
+
return -Number(unary.operand.text);
|
|
107063
|
+
}
|
|
107064
|
+
case K["ArrayLiteralExpression"]: {
|
|
107065
|
+
const items = [];
|
|
107066
|
+
for (const element of node.elements) {
|
|
107067
|
+
const item = literalJson(ts, element);
|
|
107068
|
+
if (item === void 0) return void 0;
|
|
107069
|
+
items.push(item);
|
|
107070
|
+
}
|
|
107071
|
+
return items;
|
|
107072
|
+
}
|
|
107073
|
+
case K["ObjectLiteralExpression"]: {
|
|
107074
|
+
const object = {};
|
|
107075
|
+
for (const property of node.properties) {
|
|
107076
|
+
if (property.kind !== K["PropertyAssignment"]) return void 0;
|
|
107077
|
+
const assignment = property;
|
|
107078
|
+
const key = literalKey(ts, assignment.name);
|
|
107079
|
+
const value = literalJson(ts, assignment.initializer);
|
|
107080
|
+
if (key === null || value === void 0) return void 0;
|
|
107081
|
+
object[key] = value;
|
|
107082
|
+
}
|
|
107083
|
+
return object;
|
|
107084
|
+
}
|
|
107085
|
+
default: return;
|
|
107086
|
+
}
|
|
107087
|
+
};
|
|
107088
|
+
/** An object literal key written as a name or a string, never a computed one. */
|
|
107089
|
+
var literalKey = (ts, name) => name.kind === ts.SyntaxKind["Identifier"] || name.kind === ts.SyntaxKind["StringLiteral"] ? name.text ?? null : null;
|
|
107090
|
+
/**
|
|
106852
107091
|
* A plain string literal's text, and nothing else: narrower than `literalText`,
|
|
106853
107092
|
* which also takes a template with no substitution.
|
|
106854
107093
|
*/
|
|
@@ -107080,6 +107319,14 @@ var MODELS_USE_METHOD = "use";
|
|
|
107080
107319
|
var MODEL_PROPERTY = "model";
|
|
107081
107320
|
/** The property an agent and each of its subagents names its provider routing in. */
|
|
107082
107321
|
var ROUTING_PROPERTY = "modelRouting";
|
|
107322
|
+
/**
|
|
107323
|
+
* A key's routing as the manifest records it: the object, and its plain-sort twin,
|
|
107324
|
+
* which is all a runtime older than the object reads.
|
|
107325
|
+
*/
|
|
107326
|
+
var routingFields = (routing) => ({
|
|
107327
|
+
defaultProviderRouting: routing,
|
|
107328
|
+
defaultProviderSort: plainSortOf(routing)
|
|
107329
|
+
});
|
|
107083
107330
|
var MODEL_SCAN_MESSAGES = {
|
|
107084
107331
|
noCompiler: "[models] cannot resolve typescript from the workspace, so the model inventory was not collected — Studio will not know which models this workspace declares. Install it as a dev dependency of the project.",
|
|
107085
107332
|
unparsable: (file) => `[models] could not parse ${file}; the models it declares are missing from the inventory.`
|
|
@@ -107114,7 +107361,7 @@ var scanSource$1 = (source) => {
|
|
|
107114
107361
|
key,
|
|
107115
107362
|
defaultModelId: literalText(ts, call.arguments[1]),
|
|
107116
107363
|
overridable: true,
|
|
107117
|
-
|
|
107364
|
+
...routingFields(null),
|
|
107118
107365
|
file,
|
|
107119
107366
|
line: lineOf(call)
|
|
107120
107367
|
});
|
|
@@ -107138,7 +107385,7 @@ var recordModel = (existing, ref, warn) => {
|
|
|
107138
107385
|
existing.push(ref);
|
|
107139
107386
|
return;
|
|
107140
107387
|
}
|
|
107141
|
-
if (already.defaultModelId !== ref.defaultModelId || already.overridable !== ref.overridable || already.
|
|
107388
|
+
if (already.defaultModelId !== ref.defaultModelId || already.overridable !== ref.overridable || JSON.stringify(already.defaultProviderRouting) !== JSON.stringify(ref.defaultProviderRouting)) warn(`[models] \`${ref.key}\` is declared again in ${ref.file} with a different default. One key is one pick, so the first declaration (${already.file}:${already.line}) is the one an operator sees — make the two agree, or give them different keys.`, {
|
|
107142
107389
|
file: ref.file,
|
|
107143
107390
|
line: ref.line
|
|
107144
107391
|
});
|
|
@@ -107156,16 +107403,19 @@ var collectAgentModels = (source) => {
|
|
|
107156
107403
|
const { ts, file, lineOf, at, warn } = source;
|
|
107157
107404
|
const found = [];
|
|
107158
107405
|
/**
|
|
107159
|
-
* The `modelRouting` the config writes
|
|
107160
|
-
*
|
|
107161
|
-
*
|
|
107406
|
+
* The `modelRouting` the config writes: a sort, or OpenRouter's `provider` object
|
|
107407
|
+
* written as a literal. `defineDeepAgent` refuses a value outside the schema, so
|
|
107408
|
+
* the agent will not load; one built at run time cannot be read here. Both are
|
|
107409
|
+
* recorded as "no routing in the code", and the first is warned.
|
|
107162
107410
|
*/
|
|
107163
107411
|
const routingOf = (label, config) => {
|
|
107164
107412
|
const value = propertyValue(ts, config, ROUTING_PROPERTY);
|
|
107165
107413
|
if (value === void 0) return null;
|
|
107166
|
-
const written =
|
|
107167
|
-
if (written ===
|
|
107168
|
-
|
|
107414
|
+
const written = literalJson(ts, value);
|
|
107415
|
+
if (written === void 0) return null;
|
|
107416
|
+
const parsed = modelRoutingInputSchema.safeParse(written);
|
|
107417
|
+
if (parsed.success) return toModelProviderRouting(parsed.data);
|
|
107418
|
+
warn(`[models] ${label} in ${file} declares \`modelRouting\` ${JSON.stringify(written)}, which is neither one of ${MODEL_PROVIDER_SORTS_TEXT} nor an OpenRouter \`provider\` object. The agent is refused when it is defined.`, at(value));
|
|
107169
107419
|
return null;
|
|
107170
107420
|
};
|
|
107171
107421
|
/**
|
|
@@ -107177,13 +107427,13 @@ var collectAgentModels = (source) => {
|
|
|
107177
107427
|
const declare = (args) => {
|
|
107178
107428
|
const { label, key, config, node } = args;
|
|
107179
107429
|
const declaration = propertyValue(ts, config, MODEL_PROPERTY);
|
|
107180
|
-
const
|
|
107430
|
+
const routing = routingFields(routingOf(label, config));
|
|
107181
107431
|
if (declaration === void 0) {
|
|
107182
107432
|
found.push({
|
|
107183
107433
|
key,
|
|
107184
107434
|
defaultModelId: null,
|
|
107185
107435
|
overridable: true,
|
|
107186
|
-
|
|
107436
|
+
...routing,
|
|
107187
107437
|
file,
|
|
107188
107438
|
line: lineOf(node)
|
|
107189
107439
|
});
|
|
@@ -107196,7 +107446,7 @@ var collectAgentModels = (source) => {
|
|
|
107196
107446
|
key,
|
|
107197
107447
|
defaultModelId: blank ? null : written,
|
|
107198
107448
|
overridable: written !== null,
|
|
107199
|
-
|
|
107449
|
+
...routing,
|
|
107200
107450
|
file,
|
|
107201
107451
|
line: lineOf(declaration)
|
|
107202
107452
|
});
|