@vellumai/assistant 0.11.2-dev.202608051838.a2809c6 → 0.11.2-dev.202608051933.deffc0f
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/ARCHITECTURE.md +4 -2
- package/openapi.yaml +22 -0
- package/package.json +1 -1
- package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
- package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -10
- package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
- package/src/__tests__/subagent-tools.test.ts +48 -92
- package/src/config/bundled-skills/subagent/SKILL.md +3 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
- package/src/config/feature-flag-registry.json +0 -16
- package/src/config/llm-resolver.ts +41 -12
- package/src/persistence/conversation-attention-store.ts +30 -10
- package/src/persistence/conversation-queries.ts +74 -1
- package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
- package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
- package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
- package/src/runtime/auth/scopes.ts +12 -0
- package/src/runtime/auth/types.ts +2 -0
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +279 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
- package/src/runtime/routes/conversation-list-routes.ts +25 -0
- package/src/runtime/routes/conversation-query-routes.ts +0 -50
- package/src/tools/subagent/spawn.ts +26 -78
package/ARCHITECTURE.md
CHANGED
|
@@ -67,8 +67,10 @@ All HTTP API requests use a single `Authorization: Bearer <jwt>` header for auth
|
|
|
67
67
|
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
|
|
68
68
|
| `actor_client_v1` | `chat.{read,write}`, `approval.{read,write}`, `settings.{read,write}`, `attachments.{read,write}`, `calls.{read,write}`, `feature_flags.{read,write}` | Desktop, CLI clients |
|
|
69
69
|
| `gateway_ingress_v1` | `ingress.write`, `internal.write` | Gateway channel inbound + webhook forwarding |
|
|
70
|
-
| `gateway_service_v1` | `
|
|
71
|
-
| `
|
|
70
|
+
| `gateway_service_v1` | `chat.{read,write}`, `settings.{read,write}`, `attachments.{read,write}`, `internal.write` | Gateway service-to-daemon calls |
|
|
71
|
+
| `local_v1` | `local.all` | Local (loopback) conversation sessions |
|
|
72
|
+
| `speech_relay_v1` | `speech.relay` | Daemon dial of the gateway speech relay only |
|
|
73
|
+
| `ui_page_v1` | `settings.read` | Served UI pages |
|
|
72
74
|
|
|
73
75
|
**Identity lifecycle:**
|
|
74
76
|
|
package/openapi.yaml
CHANGED
|
@@ -9565,6 +9565,28 @@ paths:
|
|
|
9565
9565
|
required:
|
|
9566
9566
|
- ok
|
|
9567
9567
|
additionalProperties: false
|
|
9568
|
+
/v1/conversations/unread-count:
|
|
9569
|
+
get:
|
|
9570
|
+
operationId: conversations_unreadcount_get
|
|
9571
|
+
summary: Unread conversation count
|
|
9572
|
+
description:
|
|
9573
|
+
"Count of foreground conversations whose latest assistant message is unseen. Matches the sidebar's unread
|
|
9574
|
+
indicators: archived rows and non-surfaced background/scheduled rows are excluded."
|
|
9575
|
+
tags:
|
|
9576
|
+
- conversations
|
|
9577
|
+
responses:
|
|
9578
|
+
"200":
|
|
9579
|
+
description: Successful response
|
|
9580
|
+
content:
|
|
9581
|
+
application/json:
|
|
9582
|
+
schema:
|
|
9583
|
+
type: object
|
|
9584
|
+
properties:
|
|
9585
|
+
count:
|
|
9586
|
+
type: number
|
|
9587
|
+
required:
|
|
9588
|
+
- count
|
|
9589
|
+
additionalProperties: false
|
|
9568
9590
|
/v1/conversations/wake:
|
|
9569
9591
|
post:
|
|
9570
9592
|
operationId: conversations_wake_post
|
package/package.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Verifies that the per-turn `overrideProfile` plumbed into `AgentLoop.run()`
|
|
3
3
|
* surfaces on every `SendMessageOptions.config` the loop emits, and that
|
|
4
|
-
* `SubagentManager.spawn()` propagates an
|
|
5
|
-
*
|
|
4
|
+
* `SubagentManager.spawn()` propagates an `overrideProfile` set on its
|
|
5
|
+
* `SubagentConfig` into the subagent's `runAgentLoop()` call.
|
|
6
6
|
*
|
|
7
|
-
* Together these
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* Together these establish where a pinned profile does and does not travel: it
|
|
8
|
+
* applies to every LLM call within the pinning conversation's own turn, and it
|
|
9
|
+
* stops at the spawn boundary. `executeSubagentSpawn` sets `overrideProfile`
|
|
10
|
+
* on the `SubagentConfig` only for a profile named explicitly on the spawn, so
|
|
11
|
+
* the propagation above carries a caller's choice rather than a parent's pin.
|
|
11
12
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
13
|
+
* With no `overrideProfile` set, the field is omitted from `providerConfig`
|
|
14
|
+
* rather than carrying `undefined`.
|
|
14
15
|
*/
|
|
15
16
|
|
|
16
17
|
import { describe, expect, mock, test } from "bun:test";
|
|
@@ -183,7 +184,7 @@ describe("AgentLoop.run — overrideProfile plumbing", () => {
|
|
|
183
184
|
});
|
|
184
185
|
});
|
|
185
186
|
|
|
186
|
-
// ── Subagent
|
|
187
|
+
// ── Subagent profile forwarding ──────────────────────────────────────────
|
|
187
188
|
|
|
188
189
|
// Capture the SubagentManager → Conversation handshake so we can verify the
|
|
189
190
|
// `overrideProfile` from `SubagentConfig` is forwarded into the spawned
|
|
@@ -329,7 +330,7 @@ seedLlmConfig();
|
|
|
329
330
|
|
|
330
331
|
import { SubagentManager } from "../subagent/manager.js";
|
|
331
332
|
|
|
332
|
-
describe("SubagentManager.spawn
|
|
333
|
+
describe("SubagentManager.spawn: forwards a configured overrideProfile", () => {
|
|
333
334
|
test("forwards overrideProfile from SubagentConfig into runAgentLoop", async () => {
|
|
334
335
|
capturedRunAgentLoopOptions.length = 0;
|
|
335
336
|
|
|
@@ -344,9 +345,9 @@ describe("SubagentManager.spawn — overrideProfile inheritance", () => {
|
|
|
344
345
|
() => {},
|
|
345
346
|
);
|
|
346
347
|
|
|
347
|
-
// The spawned subagent's runAgentLoop
|
|
348
|
-
//
|
|
349
|
-
// `
|
|
348
|
+
// The spawned subagent's runAgentLoop receives the `subagentSpawn`
|
|
349
|
+
// callSite and whatever `overrideProfile` the config carries, which
|
|
350
|
+
// `executeSubagentSpawn` sets only for an explicitly named profile.
|
|
350
351
|
expect(capturedRunAgentLoopOptions).toHaveLength(1);
|
|
351
352
|
const captured = capturedRunAgentLoopOptions[0];
|
|
352
353
|
expect(captured.callSite).toBe("subagentSpawn");
|
|
@@ -399,21 +400,18 @@ describe("SubagentManager.spawn — overrideProfile inheritance", () => {
|
|
|
399
400
|
});
|
|
400
401
|
});
|
|
401
402
|
|
|
402
|
-
// ── Nested subagent spawn
|
|
403
|
+
// ── Nested subagent spawn: no profile crosses the boundary ───────────────
|
|
403
404
|
|
|
404
|
-
//
|
|
405
|
-
//
|
|
406
|
-
//
|
|
407
|
-
//
|
|
408
|
-
//
|
|
409
|
-
//
|
|
410
|
-
//
|
|
411
|
-
// inheritance chain breaks at the second nesting level.
|
|
405
|
+
// When a subagent's agent loop is running under a turn profile, the executor
|
|
406
|
+
// closure plumbs that value into `ToolContext.overrideProfile`. That value
|
|
407
|
+
// describes the spawning turn, so `executeSubagentSpawn` leaves it behind: a
|
|
408
|
+
// child resolves the `subagentSpawn` call site on its own, and a forwarded
|
|
409
|
+
// override would both re-price the child and file its spend as a pin nobody
|
|
410
|
+
// set. These tests cover the nesting levels where a profile could otherwise
|
|
411
|
+
// compound down a chain of children.
|
|
412
412
|
|
|
413
413
|
mock.module("../persistence/conversation-crud.js", () => ({
|
|
414
414
|
setConversationProcessingStartedAt: () => {},
|
|
415
|
-
// Always return undefined for the row read so the test fails fast unless
|
|
416
|
-
// executeSubagentSpawn reads from context.overrideProfile first.
|
|
417
415
|
getConversationOverrideProfile: () => undefined,
|
|
418
416
|
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
419
417
|
}));
|
|
@@ -421,8 +419,8 @@ mock.module("../persistence/conversation-crud.js", () => ({
|
|
|
421
419
|
import { getSubagentManager } from "../subagent/index.js";
|
|
422
420
|
import { executeSubagentSpawn } from "../tools/subagent/spawn.js";
|
|
423
421
|
|
|
424
|
-
describe("executeSubagentSpawn
|
|
425
|
-
test("
|
|
422
|
+
describe("executeSubagentSpawn: the spawn boundary carries no profile", () => {
|
|
423
|
+
test("a subagent spawning a subagent does not pass its own turn profile down", async () => {
|
|
426
424
|
const manager = getSubagentManager();
|
|
427
425
|
const originalSpawn = manager.spawn.bind(manager);
|
|
428
426
|
let capturedConfig: Record<string, unknown> | undefined;
|
|
@@ -432,11 +430,11 @@ describe("executeSubagentSpawn — nested inheritance via context.overrideProfil
|
|
|
432
430
|
};
|
|
433
431
|
|
|
434
432
|
try {
|
|
435
|
-
//
|
|
436
|
-
//
|
|
437
|
-
//
|
|
438
|
-
//
|
|
439
|
-
//
|
|
433
|
+
// The second-level spawn: this tool call happens inside the first
|
|
434
|
+
// subagent's tool context, where `overrideProfile` was populated by
|
|
435
|
+
// `runAgentLoopImpl` from its own turn snapshot. That snapshot describes
|
|
436
|
+
// the parent's turn, so it stops here rather than compounding down a
|
|
437
|
+
// chain of nested children.
|
|
440
438
|
const result = await executeSubagentSpawn(
|
|
441
439
|
{ label: "nested", objective: "do nested work" },
|
|
442
440
|
{
|
|
@@ -450,16 +448,13 @@ describe("executeSubagentSpawn — nested inheritance via context.overrideProfil
|
|
|
450
448
|
|
|
451
449
|
expect(result.isError).toBe(false);
|
|
452
450
|
expect(capturedConfig).toBeDefined();
|
|
453
|
-
|
|
454
|
-
// SubagentManager.spawn forwards it into the nested subagent's
|
|
455
|
-
// runAgentLoop options — preserving inheritance across the chain.
|
|
456
|
-
expect(capturedConfig!.overrideProfile).toBe("fast");
|
|
451
|
+
expect("overrideProfile" in capturedConfig!).toBe(false);
|
|
457
452
|
} finally {
|
|
458
453
|
manager.spawn = originalSpawn;
|
|
459
454
|
}
|
|
460
455
|
});
|
|
461
456
|
|
|
462
|
-
test("
|
|
457
|
+
test("no override anywhere leaves the child on the subagentSpawn default", async () => {
|
|
463
458
|
const manager = getSubagentManager();
|
|
464
459
|
const originalSpawn = manager.spawn.bind(manager);
|
|
465
460
|
let capturedConfig: Record<string, unknown> | undefined;
|
|
@@ -481,22 +476,17 @@ describe("executeSubagentSpawn — nested inheritance via context.overrideProfil
|
|
|
481
476
|
);
|
|
482
477
|
|
|
483
478
|
expect(capturedConfig).toBeDefined();
|
|
484
|
-
|
|
485
|
-
// the invoking call site's resolved default profile — here `mainAgent`
|
|
486
|
-
// with no active profile or pin, which resolves the catalog `balanced`
|
|
487
|
-
// default — so the subagent matches its invoker's model selection.
|
|
488
|
-
expect(capturedConfig!.overrideProfile).toBe("balanced");
|
|
479
|
+
expect("overrideProfile" in capturedConfig!).toBe(false);
|
|
489
480
|
} finally {
|
|
490
481
|
manager.spawn = originalSpawn;
|
|
491
482
|
}
|
|
492
483
|
});
|
|
493
484
|
|
|
494
|
-
test("
|
|
495
|
-
//
|
|
496
|
-
//
|
|
497
|
-
// explicit
|
|
498
|
-
//
|
|
499
|
-
// resolution, overriding the user's explicit call-site choice.
|
|
485
|
+
test("a workspace active profile does not reach the child, pinned or not", async () => {
|
|
486
|
+
// The child resolves `llm.callSites.subagentSpawn` on its own, so the
|
|
487
|
+
// spawn must forward nothing either way: a forwarded override would
|
|
488
|
+
// outrank an explicit call-site pin under single-winner resolution and
|
|
489
|
+
// overturn the operator's choice.
|
|
500
490
|
const manager = getSubagentManager();
|
|
501
491
|
const originalSpawn = manager.spawn.bind(manager);
|
|
502
492
|
let capturedConfig: Record<string, unknown> | undefined;
|
|
@@ -517,7 +507,7 @@ describe("executeSubagentSpawn — nested inheritance via context.overrideProfil
|
|
|
517
507
|
{ label: "nested", objective: "do nested work" },
|
|
518
508
|
baseContext,
|
|
519
509
|
);
|
|
520
|
-
expect(capturedConfig
|
|
510
|
+
expect("overrideProfile" in capturedConfig!).toBe(false);
|
|
521
511
|
|
|
522
512
|
seedLlmConfig({
|
|
523
513
|
activeProfile: "fast",
|
|
@@ -80,16 +80,6 @@ describe("isAssistantFeatureFlagEnabled", () => {
|
|
|
80
80
|
});
|
|
81
81
|
});
|
|
82
82
|
|
|
83
|
-
describe("subagent flags default to disabled", () => {
|
|
84
|
-
for (const flagKey of ["subagent-profile-isolation", "subagent-loop-guard"]) {
|
|
85
|
-
test(`${flagKey} resolves to false with no override`, () => {
|
|
86
|
-
const config = {} as any;
|
|
87
|
-
|
|
88
|
-
expect(isAssistantFeatureFlagEnabled(flagKey, config)).toBe(false);
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
|
|
93
83
|
describe("isAssistantFeatureFlagEnabled with skillFlagKey", () => {
|
|
94
84
|
test("resolves skill flag via canonical path", () => {
|
|
95
85
|
setOverridesForTesting({ [DECLARED_FLAG_KEY]: false });
|
|
@@ -2,6 +2,7 @@ import { describe, expect, test } from "bun:test";
|
|
|
2
2
|
|
|
3
3
|
import { CODE_DEFAULT_PROFILE_ENTRIES } from "../config/default-profile-catalog.js";
|
|
4
4
|
import {
|
|
5
|
+
composeCallSiteTweak,
|
|
5
6
|
type ResolutionFallbackReason,
|
|
6
7
|
resolveCallSiteConfig,
|
|
7
8
|
resolveDefaultProfileKey,
|
|
@@ -178,6 +179,146 @@ describe("fallback and completeness", () => {
|
|
|
178
179
|
});
|
|
179
180
|
});
|
|
180
181
|
|
|
182
|
+
describe("composeCallSiteTweak", () => {
|
|
183
|
+
// `CallSiteDefaultConfig` supports a shipped `model` (and the provider it
|
|
184
|
+
// implies). No entry sets one today, so these pin the invariant the merge
|
|
185
|
+
// depends on rather than waiting for the first entry that would break it.
|
|
186
|
+
test("a workspace entry owns selection: a shipped model/provider is dropped", () => {
|
|
187
|
+
const tweak = composeCallSiteTweak(
|
|
188
|
+
{ model: "shipped-model", provider: "openai", effort: "low" },
|
|
189
|
+
{ profile: "mine" },
|
|
190
|
+
);
|
|
191
|
+
expect("model" in tweak).toBe(false);
|
|
192
|
+
expect("provider" in tweak).toBe(false);
|
|
193
|
+
// The shipped tuning still layers in.
|
|
194
|
+
expect(tweak.effort).toBe("low");
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("with no workspace entry the shipped fragment applies whole", () => {
|
|
198
|
+
const tweak = composeCallSiteTweak(
|
|
199
|
+
{ model: "shipped-model", provider: "openai", effort: "low" },
|
|
200
|
+
undefined,
|
|
201
|
+
);
|
|
202
|
+
expect(tweak.model).toBe("shipped-model");
|
|
203
|
+
expect(tweak.provider).toBe("openai");
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test("a workspace provider/model still applies", () => {
|
|
207
|
+
const tweak = composeCallSiteTweak(
|
|
208
|
+
{ model: "shipped-model", effort: "low" },
|
|
209
|
+
{ provider: "anthropic", model: "chosen-model" },
|
|
210
|
+
);
|
|
211
|
+
expect(tweak.model).toBe("chosen-model");
|
|
212
|
+
expect(tweak.provider).toBe("anthropic");
|
|
213
|
+
expect(tweak.effort).toBe("low");
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test("profile and logitBias never reach the fragment", () => {
|
|
217
|
+
const tweak = composeCallSiteTweak(
|
|
218
|
+
{ effort: "low" },
|
|
219
|
+
{ profile: "mine", logitBias: "suppress-cjk", maxTokens: 10 },
|
|
220
|
+
);
|
|
221
|
+
expect("profile" in tweak).toBe(false);
|
|
222
|
+
expect("logitBias" in tweak).toBe(false);
|
|
223
|
+
expect(tweak.maxTokens).toBe(10);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe("shipped call-site tuning", () => {
|
|
228
|
+
// A workspace entry that only repoints the profile must not cost the call
|
|
229
|
+
// site the tuning it ships with. `recall` carries a full set: a token
|
|
230
|
+
// budget, effort, sampling, thinking, and cache posture.
|
|
231
|
+
test("a profile-only entry keeps the shipped tuning and takes the new model", () => {
|
|
232
|
+
const shipped = resolveCallSiteConfig(
|
|
233
|
+
"recall",
|
|
234
|
+
LLMSchema.parse({ profiles: { mine: completeCustom }, ...anthropicDp }),
|
|
235
|
+
);
|
|
236
|
+
const repointed = resolveCallSiteConfig(
|
|
237
|
+
"recall",
|
|
238
|
+
LLMSchema.parse({
|
|
239
|
+
profiles: { mine: completeCustom },
|
|
240
|
+
callSites: { recall: { profile: "mine" } },
|
|
241
|
+
...anthropicDp,
|
|
242
|
+
}),
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
// The pin is what selects the model.
|
|
246
|
+
expect(repointed.model).toBe("gpt-5.5");
|
|
247
|
+
expect(repointed.model).not.toBe(shipped.model);
|
|
248
|
+
|
|
249
|
+
// Everything the call site ships with survives the repoint.
|
|
250
|
+
expect(repointed.maxTokens).toBe(4096);
|
|
251
|
+
expect(repointed.effort).toBe("low");
|
|
252
|
+
expect(repointed.temperature).toBe(0);
|
|
253
|
+
expect(repointed.disableCache).toBe(true);
|
|
254
|
+
expect(repointed.thinking.enabled).toBe(false);
|
|
255
|
+
expect(repointed.thinking.streamThinking).toBe(false);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test("a profile-only entry keeps a shipped nested budget", () => {
|
|
259
|
+
const resolved = resolveCallSiteConfig(
|
|
260
|
+
"memoryRouter",
|
|
261
|
+
LLMSchema.parse({
|
|
262
|
+
profiles: { mine: completeCustom },
|
|
263
|
+
callSites: { memoryRouter: { profile: "mine" } },
|
|
264
|
+
...anthropicDp,
|
|
265
|
+
}),
|
|
266
|
+
);
|
|
267
|
+
expect(resolved.contextWindow.maxInputTokens).toBe(1000000);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test("an explicit workspace field beats the shipped one, field by field", () => {
|
|
271
|
+
const resolved = resolveCallSiteConfig(
|
|
272
|
+
"recall",
|
|
273
|
+
LLMSchema.parse({
|
|
274
|
+
profiles: { mine: completeCustom },
|
|
275
|
+
callSites: {
|
|
276
|
+
recall: { profile: "mine", maxTokens: 512, effort: "high" },
|
|
277
|
+
},
|
|
278
|
+
...anthropicDp,
|
|
279
|
+
}),
|
|
280
|
+
);
|
|
281
|
+
// The two fields the entry names come from the entry.
|
|
282
|
+
expect(resolved.maxTokens).toBe(512);
|
|
283
|
+
expect(resolved.effort).toBe("high");
|
|
284
|
+
// The ones it does not name still come from the shipped tuning.
|
|
285
|
+
expect(resolved.temperature).toBe(0);
|
|
286
|
+
expect(resolved.disableCache).toBe(true);
|
|
287
|
+
expect(resolved.thinking.enabled).toBe(false);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test("a provider/model entry keeps the shipped tuning too", () => {
|
|
291
|
+
const resolved = resolveCallSiteConfig(
|
|
292
|
+
"recall",
|
|
293
|
+
LLMSchema.parse({
|
|
294
|
+
profiles: { mine: completeCustom },
|
|
295
|
+
callSites: {
|
|
296
|
+
recall: { provider: "openai", model: "gpt-5.5" },
|
|
297
|
+
},
|
|
298
|
+
...anthropicDp,
|
|
299
|
+
}),
|
|
300
|
+
);
|
|
301
|
+
expect(resolved.model).toBe("gpt-5.5");
|
|
302
|
+
expect(resolved.maxTokens).toBe(4096);
|
|
303
|
+
expect(resolved.effort).toBe("low");
|
|
304
|
+
expect(resolved.disableCache).toBe(true);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test("a call site with no shipped tuning is unaffected", () => {
|
|
308
|
+
const resolved = resolveCallSiteConfig(
|
|
309
|
+
"conversationSummarization",
|
|
310
|
+
LLMSchema.parse({
|
|
311
|
+
profiles: { mine: completeCustom },
|
|
312
|
+
callSites: { conversationSummarization: { profile: "mine" } },
|
|
313
|
+
...anthropicDp,
|
|
314
|
+
}),
|
|
315
|
+
);
|
|
316
|
+
expect(resolved.model).toBe("gpt-5.5");
|
|
317
|
+
expect(resolved.maxTokens).toBe(completeCustom.maxTokens);
|
|
318
|
+
expect(resolved.effort).toBe(completeCustom.effort);
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
|
|
181
322
|
describe("composition", () => {
|
|
182
323
|
test("base + winner + site-tweak composition (nested tweaks combine leaf-wise)", () => {
|
|
183
324
|
const llm = LLMSchema.parse({
|