@vellumai/assistant 0.8.12-dev.202606150824.4ddebe7 → 0.8.12-dev.202606151312.6aebfa4

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/openapi.yaml CHANGED
@@ -8206,14 +8206,13 @@ paths:
8206
8206
  /v1/conversations/{id}/compaction:
8207
8207
  get:
8208
8208
  operationId: conversations_by_id_compaction_get
8209
- summary: Get the compaction trail leading up to an LLM call
8209
+ summary: Get the compaction(s) attributed to an LLM call
8210
8210
  description:
8211
- Return the chronological list of compaction events that ran in the same agent turn as the call identified
8212
- by `callId`. Turn bounds are walked from the `messages` table (real user messages — tool-result user messages
8213
- are not boundaries). Served from the first-class compaction log when `compactionLogs.destination = "clickhouse"`
8214
- is configured, falling back to the legacy projection over `llm_request_logs` rows where `call_site =
8215
- "compactionAgent"`. When the conversation has no other messages around the selected call, every compaction
8216
- strictly before the call's `createdAt` is in scope. Drives the Inspector's Compaction tab.
8211
+ Return the chronological list of compactions attributed to the call identified by `callId` — those that ran
8212
+ strictly between the previous real (non-`compactionAgent`) LLM call and the selected call. Served from the
8213
+ first-class compaction log when `compactionLogs.destination = "clickhouse"` is configured, falling back to the
8214
+ legacy projection over `llm_request_logs` rows where `call_site = "compactionAgent"`. Usually empty or a single
8215
+ compaction; the recovery cascade can fire several before one call lands. Drives the Inspector's Compaction tab.
8217
8216
  tags:
8218
8217
  - conversations
8219
8218
  responses:
@@ -8235,54 +8234,84 @@ paths:
8235
8234
  type: string
8236
8235
  createdAt:
8237
8236
  type: number
8238
- model:
8237
+ trigger:
8239
8238
  anyOf:
8240
8239
  - type: string
8241
8240
  - type: "null"
8242
- provider:
8241
+ compacted:
8242
+ anyOf:
8243
+ - type: boolean
8244
+ - type: "null"
8245
+ summaryFailed:
8246
+ anyOf:
8247
+ - type: boolean
8248
+ - type: "null"
8249
+ skipReason:
8243
8250
  anyOf:
8244
8251
  - type: string
8245
8252
  - type: "null"
8246
- inputTokens:
8253
+ contextTokensBefore:
8247
8254
  anyOf:
8248
8255
  - type: number
8249
8256
  - type: "null"
8250
- outputTokens:
8257
+ contextTokensAfter:
8251
8258
  anyOf:
8252
8259
  - type: number
8253
8260
  - type: "null"
8254
- durationMs:
8261
+ messagesBefore:
8255
8262
  anyOf:
8256
8263
  - type: number
8257
8264
  - type: "null"
8258
- responsePreview:
8265
+ messagesAfter:
8259
8266
  anyOf:
8260
- - type: string
8267
+ - type: number
8261
8268
  - type: "null"
8262
- requestMessageCount:
8269
+ compactedMessages:
8263
8270
  anyOf:
8264
8271
  - type: number
8265
8272
  - type: "null"
8266
- stopReason:
8273
+ preservedTailMessages:
8274
+ anyOf:
8275
+ - type: number
8276
+ - type: "null"
8277
+ durationMs:
8278
+ anyOf:
8279
+ - type: number
8280
+ - type: "null"
8281
+ summaryModel:
8267
8282
  anyOf:
8268
8283
  - type: string
8269
8284
  - type: "null"
8270
- estimatedCostUsd:
8285
+ summaryInputTokens:
8271
8286
  anyOf:
8272
8287
  - type: number
8273
8288
  - type: "null"
8289
+ summaryOutputTokens:
8290
+ anyOf:
8291
+ - type: number
8292
+ - type: "null"
8293
+ summaryText:
8294
+ anyOf:
8295
+ - type: string
8296
+ - type: "null"
8274
8297
  required:
8275
8298
  - id
8276
8299
  - createdAt
8277
- - model
8278
- - provider
8279
- - inputTokens
8280
- - outputTokens
8300
+ - trigger
8301
+ - compacted
8302
+ - summaryFailed
8303
+ - skipReason
8304
+ - contextTokensBefore
8305
+ - contextTokensAfter
8306
+ - messagesBefore
8307
+ - messagesAfter
8308
+ - compactedMessages
8309
+ - preservedTailMessages
8281
8310
  - durationMs
8282
- - responsePreview
8283
- - requestMessageCount
8284
- - stopReason
8285
- - estimatedCostUsd
8311
+ - summaryModel
8312
+ - summaryInputTokens
8313
+ - summaryOutputTokens
8314
+ - summaryText
8286
8315
  additionalProperties: false
8287
8316
  required:
8288
8317
  - conversationId
@@ -8303,7 +8332,7 @@ paths:
8303
8332
  required: true
8304
8333
  schema:
8305
8334
  type: string
8306
- description: ID of the selected LLM call from the rail. Defines the chronological cutoff for the trail.
8335
+ description: ID of the selected LLM call from the rail. Defines the chronological cutoff for the attributed compactions.
8307
8336
  /v1/conversations/{id}/inference-profile:
8308
8337
  put:
8309
8338
  operationId: conversations_by_id_inferenceprofile_put
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.8.12-dev.202606150824.4ddebe7",
3
+ "version": "0.8.12-dev.202606151312.6aebfa4",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -213,10 +213,12 @@ export class ClickHouseCompactionLogStore {
213
213
 
214
214
  /**
215
215
  * Read all compaction attempts whose `started_at` falls strictly inside
216
- * the given window, pairing start/end rows by `compaction_id`. Mirrors
217
- * the strict `>` / `<` predicate contract of
218
- * `getCompactionLogsBetween` on the llm-request-log sources, so the
219
- * route can apply the same ±1ms boundary shifts to both.
216
+ * the open window `(afterStartedAt, beforeStartedAt)`, pairing start/end
217
+ * rows by `compaction_id`. Mirrors the strict `>` / `<` predicate
218
+ * contract of `getCompactionLogsBetween` on the llm-request-log sources
219
+ * so the compaction route can scope both stores to the same call window
220
+ * (floor = previous real call's `createdAt`, ceiling = selected call's
221
+ * `createdAt`).
220
222
  */
221
223
  async getEventsBetween(
222
224
  conversationId: string,
@@ -256,6 +256,34 @@ export class ClickHouseLlmRequestLogSource implements LlmRequestLogSource {
256
256
  return rows.map((r) => this.toLogRow(r));
257
257
  }
258
258
 
259
+ async getPreviousNonCompactionCallCreatedAt(
260
+ conversationId: string,
261
+ beforeCreatedAt: number,
262
+ ): Promise<number | null> {
263
+ const aid = await this.assistantId();
264
+ // `call_site != 'compactionAgent'` keeps the pre-migration-264 rows
265
+ // (stored as the empty-string default) in scope as real calls and
266
+ // excludes only the summarizer's own logs, matching the local
267
+ // store's `isNull(callSite) OR callSite != 'compactionAgent'`.
268
+ const sql = `SELECT
269
+ toUnixTimestamp64Milli(created_at) AS created_at
270
+ FROM ${this.tableRef()}
271
+ WHERE assistant_id = {assistant_id:String}
272
+ AND conversation_id = {conversation_id:String}
273
+ AND call_site != {call_site:String}
274
+ AND created_at < fromUnixTimestamp64Milli({before_created_at:Int64})
275
+ ORDER BY created_at DESC, id DESC
276
+ LIMIT 1
277
+ FORMAT JSONEachRow`;
278
+ const rows = await this.exec<{ created_at: string }>(sql, {
279
+ assistant_id: aid,
280
+ conversation_id: conversationId,
281
+ call_site: "compactionAgent",
282
+ before_created_at: String(beforeCreatedAt),
283
+ });
284
+ return rows[0] ? Number(rows[0].created_at) : null;
285
+ }
286
+
259
287
  async getCompactionLogsBetween(
260
288
  conversationId: string,
261
289
  afterCreatedAt: number | null,
@@ -12,6 +12,7 @@ import type { LlmRequestLogSource } from "./llm-request-log-source.js";
12
12
  import {
13
13
  type CompactionAgentLogRow,
14
14
  getCompactionLogsBetween,
15
+ getPreviousNonCompactionCallCreatedAt,
15
16
  getRequestLogById,
16
17
  getRequestLogMetaById,
17
18
  getRequestLogsByConversationId,
@@ -39,6 +40,16 @@ export class LocalLlmRequestLogSource implements LlmRequestLogSource {
39
40
  return getRequestLogsByConversationId(conversationId);
40
41
  }
41
42
 
43
+ async getPreviousNonCompactionCallCreatedAt(
44
+ conversationId: string,
45
+ beforeCreatedAt: number,
46
+ ): Promise<number | null> {
47
+ return getPreviousNonCompactionCallCreatedAt(
48
+ conversationId,
49
+ beforeCreatedAt,
50
+ );
51
+ }
52
+
42
53
  async getCompactionLogsBetween(
43
54
  conversationId: string,
44
55
  afterCreatedAt: number | null,
@@ -51,15 +51,32 @@ export interface LlmRequestLogSource {
51
51
  */
52
52
  getRequestLogsByConversationId(conversationId: string): Promise<LogRow[]>;
53
53
 
54
+ /**
55
+ * Return the `createdAt` of the most recent **non-`compactionAgent`**
56
+ * LLM call in the conversation that ran strictly before
57
+ * `beforeCreatedAt`, or `null` when the selected call is the first real
58
+ * call in the conversation.
59
+ *
60
+ * Drives the call-scoped floor for the Inspector's Compaction tab: a
61
+ * compaction is attributed to the next real call that ran after it, so
62
+ * the compactions for a given call are those that landed strictly
63
+ * between the previous real call and the selected call.
64
+ */
65
+ getPreviousNonCompactionCallCreatedAt(
66
+ conversationId: string,
67
+ beforeCreatedAt: number,
68
+ ): Promise<number | null>;
69
+
54
70
  /**
55
71
  * Fetch every `callSite = "compactionAgent"` log row in the conversation
56
72
  * whose `createdAt` falls in the **open window**
57
73
  * `(afterCreatedAt, beforeCreatedAt)`, ordered chronologically.
58
74
  *
59
- * Drives the Inspector's Compaction tab. The route handler computes
60
- * the window from the turn the selected call belongs to via
61
- * `getTurnTimeBounds` in `memory/conversation-crud.ts`; this source
62
- * method is bound-agnostic. See `getCompactionLogsBetween` in
75
+ * The legacy fallback for the Inspector's Compaction tab, used when the
76
+ * ClickHouse compaction log is unavailable. The route handler resolves
77
+ * the floor from `getPreviousNonCompactionCallCreatedAt` and the ceiling
78
+ * from the selected call's `createdAt`; this source method is
79
+ * bound-agnostic. See `getCompactionLogsBetween` in
63
80
  * `llm-request-log-store.ts` for the SQL.
64
81
  */
65
82
  getCompactionLogsBetween(
@@ -9,6 +9,8 @@ import {
9
9
  isNull,
10
10
  lt,
11
11
  lte,
12
+ ne,
13
+ or,
12
14
  sql,
13
15
  } from "drizzle-orm";
14
16
  import { v4 as uuid } from "uuid";
@@ -413,6 +415,43 @@ function selectUnlinkedLogsInRange(
413
415
  .all();
414
416
  }
415
417
 
418
+ /**
419
+ * Return the `createdAt` of the most recent **non-`compactionAgent`** LLM
420
+ * call in the conversation that ran strictly before `beforeCreatedAt`, or
421
+ * `null` when no such call exists (the selected call is the first real
422
+ * call in the conversation).
423
+ *
424
+ * Drives the call-scoped floor for the Inspector's Compaction tab. A
425
+ * compaction is attributed to the next real call that runs after it, so
426
+ * the trail for a given call is the set of compactions that landed
427
+ * strictly between the previous real call and the selected call. NULL
428
+ * `callSite` rows (pre-migration-264) are treated as real calls — only
429
+ * `compactionAgent` rows are excluded.
430
+ */
431
+ export function getPreviousNonCompactionCallCreatedAt(
432
+ conversationId: string,
433
+ beforeCreatedAt: number,
434
+ ): number | null {
435
+ const db = getDb();
436
+ const row = db
437
+ .select({ createdAt: llmRequestLogs.createdAt })
438
+ .from(llmRequestLogs)
439
+ .where(
440
+ and(
441
+ eq(llmRequestLogs.conversationId, conversationId),
442
+ lt(llmRequestLogs.createdAt, beforeCreatedAt),
443
+ or(
444
+ isNull(llmRequestLogs.callSite),
445
+ ne(llmRequestLogs.callSite, "compactionAgent"),
446
+ ),
447
+ ),
448
+ )
449
+ .orderBy(desc(llmRequestLogs.createdAt), desc(llmRequestLogs.id))
450
+ .limit(1)
451
+ .get();
452
+ return row?.createdAt ?? null;
453
+ }
454
+
416
455
  /**
417
456
  * Fetch every `compactionAgent` log row in the conversation whose
418
457
  * `createdAt` falls in the **open window** `(afterCreatedAt, beforeCreatedAt)`,