@dudousxd/nestjs-agent-store-drizzle 0.34.0 → 0.35.0
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/dist/index.cjs +216 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1030 -35
- package/dist/index.d.ts +1030 -35
- package/dist/index.js +217 -20
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -178,7 +178,7 @@ var agentQueuedMessage = sqliteTable("agent_queued_message", {
|
|
|
178
178
|
]);
|
|
179
179
|
var agentTokenUsage = sqliteTable("agent_token_usage", {
|
|
180
180
|
id: text("id").primaryKey(),
|
|
181
|
-
threadId: text("thread_id").
|
|
181
|
+
threadId: text("thread_id").references(() => agentThread.id, {
|
|
182
182
|
onDelete: "cascade"
|
|
183
183
|
}),
|
|
184
184
|
actorRef: text("actor_ref").notNull(),
|
|
@@ -195,13 +195,28 @@ var agentTokenUsage = sqliteTable("agent_token_usage", {
|
|
|
195
195
|
costUsd: real("cost_usd"),
|
|
196
196
|
/** Where `cost_usd` came from: `'provider'` (reported) or `'estimate'` (tokens × the price row); null on rows from before it existed (reported). */
|
|
197
197
|
costSource: text("cost_source").$type(),
|
|
198
|
+
/** The run the row belongs to. */
|
|
199
|
+
runId: text("run_id"),
|
|
200
|
+
/** What the spend was for: `agent:<name>` for an agent turn, the app's own otherwise; null → `agent`. */
|
|
201
|
+
feature: text("feature"),
|
|
202
|
+
provider: text("provider"),
|
|
203
|
+
tenantRef: text("tenant_ref"),
|
|
204
|
+
/** Units a row bills by when they are not tokens. */
|
|
205
|
+
audioSeconds: real("audio_seconds"),
|
|
206
|
+
characters: integer("characters"),
|
|
207
|
+
requests: integer("requests"),
|
|
208
|
+
/** The caller's own JSON, never read by the library. */
|
|
209
|
+
metadata: text("metadata", {
|
|
210
|
+
mode: "json"
|
|
211
|
+
}).$type(),
|
|
198
212
|
createdAt: integer("created_at", {
|
|
199
213
|
mode: "timestamp_ms"
|
|
200
214
|
}).notNull()
|
|
201
215
|
}, (table) => [
|
|
202
216
|
index("agent_token_usage_actor_created_idx").on(table.actorRef, table.createdAt),
|
|
203
217
|
index("agent_token_usage_created_idx").on(table.createdAt),
|
|
204
|
-
index("agent_token_usage_thread_idx").on(table.threadId)
|
|
218
|
+
index("agent_token_usage_thread_idx").on(table.threadId),
|
|
219
|
+
index("agent_token_usage_feature_created_idx").on(table.feature, table.createdAt)
|
|
205
220
|
]);
|
|
206
221
|
var agentGenuiCall = sqliteTable("agent_genui_call", {
|
|
207
222
|
id: text("id").primaryKey(),
|
|
@@ -283,7 +298,17 @@ var agentRun = sqliteTable("agent_run", {
|
|
|
283
298
|
* for a detached child, which outlives its parent's turn and so is paired by nothing in the
|
|
284
299
|
* transcript.
|
|
285
300
|
*/
|
|
286
|
-
parentRunId: text("parent_run_id")
|
|
301
|
+
parentRunId: text("parent_run_id"),
|
|
302
|
+
/** Enqueue → body start (ms); null when not read. */
|
|
303
|
+
queueMs: integer("queue_ms"),
|
|
304
|
+
/** Where the completed run's time went: model and tool totals, counts, and the per-step list. */
|
|
305
|
+
modelMs: integer("model_ms"),
|
|
306
|
+
toolsMs: integer("tools_ms"),
|
|
307
|
+
modelCalls: integer("model_calls"),
|
|
308
|
+
toolCalls: integer("tool_calls"),
|
|
309
|
+
timings: text("timings", {
|
|
310
|
+
mode: "json"
|
|
311
|
+
}).$type()
|
|
287
312
|
}, (table) => [
|
|
288
313
|
index("agent_run_started_idx").on(table.startedAt)
|
|
289
314
|
]);
|
|
@@ -629,7 +654,7 @@ var agentQueuedMessage2 = mysqlTable("agent_queued_message", {
|
|
|
629
654
|
]);
|
|
630
655
|
var agentTokenUsage2 = mysqlTable("agent_token_usage", {
|
|
631
656
|
id: key("id").primaryKey(),
|
|
632
|
-
threadId: key("thread_id").
|
|
657
|
+
threadId: key("thread_id").references(() => agentThread2.id, {
|
|
633
658
|
onDelete: "cascade"
|
|
634
659
|
}),
|
|
635
660
|
actorRef: key("actor_ref").notNull(),
|
|
@@ -643,11 +668,26 @@ var agentTokenUsage2 = mysqlTable("agent_token_usage", {
|
|
|
643
668
|
costUsd: double("cost_usd"),
|
|
644
669
|
/** Where `cost_usd` came from: `'provider'` (reported) or `'estimate'` (tokens × the price row); null on rows from before it existed (reported). */
|
|
645
670
|
costSource: key("cost_source").$type(),
|
|
671
|
+
/** The run the row belongs to. */
|
|
672
|
+
runId: key("run_id"),
|
|
673
|
+
/** What the spend was for: `agent:<name>` for an agent turn, the app's own otherwise; null → `agent`. */
|
|
674
|
+
feature: key("feature"),
|
|
675
|
+
provider: key("provider"),
|
|
676
|
+
tenantRef: key("tenant_ref"),
|
|
677
|
+
/** Units a row bills by when they are not tokens. */
|
|
678
|
+
audioSeconds: double("audio_seconds"),
|
|
679
|
+
characters: bigint("characters", {
|
|
680
|
+
mode: "number"
|
|
681
|
+
}),
|
|
682
|
+
requests: int("requests"),
|
|
683
|
+
/** The caller's own JSON, never read by the library. */
|
|
684
|
+
metadata: json("metadata").$type(),
|
|
646
685
|
createdAt: ts("created_at").notNull()
|
|
647
686
|
}, (table) => [
|
|
648
687
|
index2("agent_token_usage_actor_created_idx").on(table.actorRef, table.createdAt),
|
|
649
688
|
index2("agent_token_usage_created_idx").on(table.createdAt),
|
|
650
|
-
index2("agent_token_usage_thread_idx").on(table.threadId)
|
|
689
|
+
index2("agent_token_usage_thread_idx").on(table.threadId),
|
|
690
|
+
index2("agent_token_usage_feature_created_idx").on(table.feature, table.createdAt)
|
|
651
691
|
]);
|
|
652
692
|
var agentGenuiCall2 = mysqlTable("agent_genui_call", {
|
|
653
693
|
id: key("id").primaryKey(),
|
|
@@ -719,7 +759,15 @@ var agentRun2 = mysqlTable("agent_run", {
|
|
|
719
759
|
startedAt: ts("started_at").notNull(),
|
|
720
760
|
settledAt: ts("settled_at"),
|
|
721
761
|
promptHash: key("prompt_hash"),
|
|
722
|
-
parentRunId: key("parent_run_id")
|
|
762
|
+
parentRunId: key("parent_run_id"),
|
|
763
|
+
/** Enqueue → body start (ms); null when not read. */
|
|
764
|
+
queueMs: int("queue_ms"),
|
|
765
|
+
/** Where the completed run's time went: model and tool totals, counts, and the per-step list. */
|
|
766
|
+
modelMs: int("model_ms"),
|
|
767
|
+
toolsMs: int("tools_ms"),
|
|
768
|
+
modelCalls: int("model_calls"),
|
|
769
|
+
toolCalls: int("tool_calls"),
|
|
770
|
+
timings: json("timings").$type()
|
|
723
771
|
}, (table) => [
|
|
724
772
|
index2("agent_run_started_idx").on(table.startedAt)
|
|
725
773
|
]);
|
|
@@ -1091,7 +1139,7 @@ var agentQueuedMessage3 = pgTable("agent_queued_message", {
|
|
|
1091
1139
|
]);
|
|
1092
1140
|
var agentTokenUsage3 = pgTable("agent_token_usage", {
|
|
1093
1141
|
id: text2("id").primaryKey(),
|
|
1094
|
-
threadId: text2("thread_id").
|
|
1142
|
+
threadId: text2("thread_id").references(() => agentThread3.id, {
|
|
1095
1143
|
onDelete: "cascade"
|
|
1096
1144
|
}),
|
|
1097
1145
|
actorRef: text2("actor_ref").notNull(),
|
|
@@ -1105,11 +1153,26 @@ var agentTokenUsage3 = pgTable("agent_token_usage", {
|
|
|
1105
1153
|
costUsd: doublePrecision("cost_usd"),
|
|
1106
1154
|
/** Where `cost_usd` came from: `'provider'` (reported) or `'estimate'` (tokens × the price row); null on rows from before it existed (reported). */
|
|
1107
1155
|
costSource: text2("cost_source").$type(),
|
|
1156
|
+
/** The run the row belongs to. */
|
|
1157
|
+
runId: text2("run_id"),
|
|
1158
|
+
/** What the spend was for: `agent:<name>` for an agent turn, the app's own otherwise; null → `agent`. */
|
|
1159
|
+
feature: text2("feature"),
|
|
1160
|
+
provider: text2("provider"),
|
|
1161
|
+
tenantRef: text2("tenant_ref"),
|
|
1162
|
+
/** Units a row bills by when they are not tokens. */
|
|
1163
|
+
audioSeconds: doublePrecision("audio_seconds"),
|
|
1164
|
+
characters: bigint2("characters", {
|
|
1165
|
+
mode: "number"
|
|
1166
|
+
}),
|
|
1167
|
+
requests: integer2("requests"),
|
|
1168
|
+
/** The caller's own JSON, never read by the library. */
|
|
1169
|
+
metadata: jsonb("metadata").$type(),
|
|
1108
1170
|
createdAt: ts2("created_at").notNull()
|
|
1109
1171
|
}, (table) => [
|
|
1110
1172
|
index3("agent_token_usage_actor_created_idx").on(table.actorRef, table.createdAt),
|
|
1111
1173
|
index3("agent_token_usage_created_idx").on(table.createdAt),
|
|
1112
|
-
index3("agent_token_usage_thread_idx").on(table.threadId)
|
|
1174
|
+
index3("agent_token_usage_thread_idx").on(table.threadId),
|
|
1175
|
+
index3("agent_token_usage_feature_created_idx").on(table.feature, table.createdAt)
|
|
1113
1176
|
]);
|
|
1114
1177
|
var agentGenuiCall3 = pgTable("agent_genui_call", {
|
|
1115
1178
|
id: text2("id").primaryKey(),
|
|
@@ -1181,7 +1244,15 @@ var agentRun3 = pgTable("agent_run", {
|
|
|
1181
1244
|
startedAt: ts2("started_at").notNull(),
|
|
1182
1245
|
settledAt: ts2("settled_at"),
|
|
1183
1246
|
promptHash: text2("prompt_hash"),
|
|
1184
|
-
parentRunId: text2("parent_run_id")
|
|
1247
|
+
parentRunId: text2("parent_run_id"),
|
|
1248
|
+
/** Enqueue → body start (ms); null when not read. */
|
|
1249
|
+
queueMs: integer2("queue_ms"),
|
|
1250
|
+
/** Where the completed run's time went: model and tool totals, counts, and the per-step list. */
|
|
1251
|
+
modelMs: integer2("model_ms"),
|
|
1252
|
+
toolsMs: integer2("tools_ms"),
|
|
1253
|
+
modelCalls: integer2("model_calls"),
|
|
1254
|
+
toolCalls: integer2("tool_calls"),
|
|
1255
|
+
timings: jsonb("timings").$type()
|
|
1185
1256
|
}, (table) => [
|
|
1186
1257
|
index3("agent_run_started_idx").on(table.startedAt)
|
|
1187
1258
|
]);
|
|
@@ -1639,10 +1710,60 @@ function specs(dialect) {
|
|
|
1639
1710
|
}
|
|
1640
1711
|
__name(specs, "specs");
|
|
1641
1712
|
async function existingColumns(db, dialect, table) {
|
|
1642
|
-
|
|
1643
|
-
return new Set(rows.map((row) => String(row.name)));
|
|
1713
|
+
return new Set((await columnNullability(db, dialect, table)).keys());
|
|
1644
1714
|
}
|
|
1645
1715
|
__name(existingColumns, "existingColumns");
|
|
1716
|
+
async function columnNullability(db, dialect, table) {
|
|
1717
|
+
if (dialect === "sqlite") {
|
|
1718
|
+
const rows2 = await querySql(db, dialect, sql.raw(`PRAGMA table_info("${table}")`));
|
|
1719
|
+
return new Map(rows2.map((row) => [
|
|
1720
|
+
String(row.name),
|
|
1721
|
+
Number(row.notnull) === 0
|
|
1722
|
+
]));
|
|
1723
|
+
}
|
|
1724
|
+
const rows = await querySql(db, dialect, sql`select column_name as name, is_nullable as nullable from information_schema.columns where table_schema = ${sql.raw(dialect === "mysql" ? "database()" : "current_schema()")} and table_name = ${table}`);
|
|
1725
|
+
return new Map(rows.map((row) => [
|
|
1726
|
+
String(row.name ?? row.NAME),
|
|
1727
|
+
String(row.nullable ?? row.NULLABLE).toUpperCase() === "YES"
|
|
1728
|
+
]));
|
|
1729
|
+
}
|
|
1730
|
+
__name(columnNullability, "columnNullability");
|
|
1731
|
+
async function relaxNotNull(db, dialect, spec, column, all) {
|
|
1732
|
+
const q = /* @__PURE__ */ __name((identifier) => quote(dialect, identifier), "q");
|
|
1733
|
+
if (dialect === "postgres") {
|
|
1734
|
+
await runSql(db, dialect, sql.raw(`ALTER TABLE ${q(spec.name)} ALTER COLUMN ${q(column.name)} DROP NOT NULL`));
|
|
1735
|
+
return;
|
|
1736
|
+
}
|
|
1737
|
+
if (dialect === "mysql") {
|
|
1738
|
+
await runSql(db, dialect, sql.raw(`ALTER TABLE ${q(spec.name)} MODIFY ${columnDefinition(dialect, column)}`));
|
|
1739
|
+
return;
|
|
1740
|
+
}
|
|
1741
|
+
if (all.some((other) => other.foreignKeys.some((key2) => key2.table === spec.name && other !== spec))) {
|
|
1742
|
+
throw new Error(`agent schema: cannot make ${spec.name}.${column.name} nullable on SQLite \u2014 other tables reference ${spec.name}`);
|
|
1743
|
+
}
|
|
1744
|
+
const scratch = `${spec.name}__rebuild`;
|
|
1745
|
+
const present = [
|
|
1746
|
+
...(await columnNullability(db, dialect, spec.name)).keys()
|
|
1747
|
+
];
|
|
1748
|
+
const list = present.map(q).join(", ");
|
|
1749
|
+
await runSql(db, dialect, sql.raw("SAVEPOINT agent_schema_rebuild"));
|
|
1750
|
+
try {
|
|
1751
|
+
await runSql(db, dialect, sql.raw(`DROP TABLE IF EXISTS ${q(scratch)}`));
|
|
1752
|
+
await runSql(db, dialect, sql.raw(createTableSql(dialect, {
|
|
1753
|
+
...spec,
|
|
1754
|
+
name: scratch
|
|
1755
|
+
})));
|
|
1756
|
+
await runSql(db, dialect, sql.raw(`INSERT INTO ${q(scratch)} (${list}) SELECT ${list} FROM ${q(spec.name)}`));
|
|
1757
|
+
await runSql(db, dialect, sql.raw(`DROP TABLE ${q(spec.name)}`));
|
|
1758
|
+
await runSql(db, dialect, sql.raw(`ALTER TABLE ${q(scratch)} RENAME TO ${q(spec.name)}`));
|
|
1759
|
+
await runSql(db, dialect, sql.raw("RELEASE SAVEPOINT agent_schema_rebuild"));
|
|
1760
|
+
} catch (error) {
|
|
1761
|
+
await runSql(db, dialect, sql.raw("ROLLBACK TO SAVEPOINT agent_schema_rebuild")).catch(() => void 0);
|
|
1762
|
+
await runSql(db, dialect, sql.raw("RELEASE SAVEPOINT agent_schema_rebuild")).catch(() => void 0);
|
|
1763
|
+
throw error;
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
__name(relaxNotNull, "relaxNotNull");
|
|
1646
1767
|
async function indexExists(db, dialect, table, index4) {
|
|
1647
1768
|
let query;
|
|
1648
1769
|
if (dialect === "sqlite") {
|
|
@@ -1656,7 +1777,8 @@ async function indexExists(db, dialect, table, index4) {
|
|
|
1656
1777
|
}
|
|
1657
1778
|
__name(indexExists, "indexExists");
|
|
1658
1779
|
async function heal(db, dialect) {
|
|
1659
|
-
|
|
1780
|
+
const all = specs(dialect);
|
|
1781
|
+
for (const spec of all) {
|
|
1660
1782
|
await runSql(db, dialect, sql.raw(createTableSql(dialect, spec)));
|
|
1661
1783
|
const present = await existingColumns(db, dialect, spec.name);
|
|
1662
1784
|
for (const column of spec.columns) {
|
|
@@ -1664,6 +1786,13 @@ async function heal(db, dialect) {
|
|
|
1664
1786
|
await runSql(db, dialect, sql.raw(`ALTER TABLE ${quote(dialect, spec.name)} ADD COLUMN ${columnDefinition(dialect, column)}`));
|
|
1665
1787
|
}
|
|
1666
1788
|
}
|
|
1789
|
+
const nullable3 = await columnNullability(db, dialect, spec.name);
|
|
1790
|
+
for (const column of spec.columns) {
|
|
1791
|
+
if (column.notNull || spec.primaryKey.includes(column.name)) continue;
|
|
1792
|
+
if (nullable3.get(column.name) === false) {
|
|
1793
|
+
await relaxNotNull(db, dialect, spec, column, all);
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1667
1796
|
for (const index4 of spec.indexes) {
|
|
1668
1797
|
if (!await indexExists(db, dialect, spec.name, index4.name)) {
|
|
1669
1798
|
await runSql(db, dialect, sql.raw(createIndexSql(dialect, spec.name, index4)));
|
|
@@ -2854,7 +2983,13 @@ var DrizzleAgentStore = class {
|
|
|
2854
2983
|
startedAt: /* @__PURE__ */ new Date(),
|
|
2855
2984
|
settledAt: null,
|
|
2856
2985
|
promptHash: run.promptHash ?? null,
|
|
2857
|
-
parentRunId: run.parentRunId ?? null
|
|
2986
|
+
parentRunId: run.parentRunId ?? null,
|
|
2987
|
+
queueMs: run.queueMs !== void 0 ? Math.max(0, Math.round(run.queueMs)) : null,
|
|
2988
|
+
modelMs: null,
|
|
2989
|
+
toolsMs: null,
|
|
2990
|
+
modelCalls: null,
|
|
2991
|
+
toolCalls: null,
|
|
2992
|
+
timings: null
|
|
2858
2993
|
};
|
|
2859
2994
|
await this.db.insert(this.t.agentRun).values(runRow);
|
|
2860
2995
|
}
|
|
@@ -2882,6 +3017,13 @@ var DrizzleAgentStore = class {
|
|
|
2882
3017
|
if (end.errorMessage !== void 0) {
|
|
2883
3018
|
updates.errorMessage = end.errorMessage;
|
|
2884
3019
|
}
|
|
3020
|
+
if (end.timings !== void 0) {
|
|
3021
|
+
updates.modelMs = Math.round(end.timings.modelMs);
|
|
3022
|
+
updates.toolsMs = Math.round(end.timings.toolsMs);
|
|
3023
|
+
updates.modelCalls = end.timings.modelCalls;
|
|
3024
|
+
updates.toolCalls = end.timings.toolCalls;
|
|
3025
|
+
updates.timings = end.timings.steps;
|
|
3026
|
+
}
|
|
2885
3027
|
await this.db.update(this.t.agentRun).set(updates).where(eq2(this.t.agentRun.id, end.runId));
|
|
2886
3028
|
}
|
|
2887
3029
|
/** Bump the run's llm-step retry counter. Atomic `retries = retries + 1`, no read-modify-write. */
|
|
@@ -3084,9 +3226,17 @@ var DrizzleAgentStore = class {
|
|
|
3084
3226
|
async recordUsage(input) {
|
|
3085
3227
|
await this.db.insert(this.t.agentTokenUsage).values({
|
|
3086
3228
|
id: crypto.randomUUID(),
|
|
3087
|
-
threadId: input.threadId,
|
|
3229
|
+
threadId: input.threadId ?? null,
|
|
3088
3230
|
actorRef: input.actorRef,
|
|
3089
3231
|
messageId: input.messageId ?? null,
|
|
3232
|
+
runId: input.runId ?? null,
|
|
3233
|
+
feature: input.feature ?? null,
|
|
3234
|
+
provider: input.provider ?? null,
|
|
3235
|
+
tenantRef: input.tenantRef ?? null,
|
|
3236
|
+
audioSeconds: input.audioSeconds ?? null,
|
|
3237
|
+
characters: input.characters ?? null,
|
|
3238
|
+
requests: input.requests ?? null,
|
|
3239
|
+
metadata: input.metadata ?? null,
|
|
3090
3240
|
modelId: input.modelId,
|
|
3091
3241
|
purpose: input.purpose,
|
|
3092
3242
|
inputTokens: input.usage.inputTokens,
|
|
@@ -3282,7 +3432,7 @@ function toQueuedMessage(row) {
|
|
|
3282
3432
|
__name(toQueuedMessage, "toQueuedMessage");
|
|
3283
3433
|
|
|
3284
3434
|
// src/drizzle-governance-queries.ts
|
|
3285
|
-
import { bucketByActor, bucketByModel, bucketByThread, bucketUsageTrend, dayBoundsUtc, rollupThreadUsage, summarizeGenuiCalls, truncateDetailContent } from "@dudousxd/nestjs-agent-core";
|
|
3435
|
+
import { bucketByActor, bucketByFeature, bucketByModel, bucketByThread, bucketUsageBreakdown, bucketUsageTrend, dayBoundsUtc, rollupThreadUsage, summarizeGenuiCalls, truncateDetailContent } from "@dudousxd/nestjs-agent-core";
|
|
3286
3436
|
import { and as and3, asc as asc3, count, desc as desc2, eq as eq3, gte as gte2, inArray as inArray2, isNull as isNull3, like, lte as lte3, sql as sql4, sum } from "drizzle-orm";
|
|
3287
3437
|
var UNCLASSIFIED_ERROR_CODE = "unknown";
|
|
3288
3438
|
var DEFAULT_AGENT_BUCKET = "(default)";
|
|
@@ -3404,10 +3554,26 @@ function toRecentRunRow(run) {
|
|
|
3404
3554
|
retries: run.retries,
|
|
3405
3555
|
startedAt: run.startedAt.toISOString(),
|
|
3406
3556
|
promptHash: run.promptHash ?? null,
|
|
3407
|
-
parentRunId: run.parentRunId ?? null
|
|
3557
|
+
parentRunId: run.parentRunId ?? null,
|
|
3558
|
+
queueMs: run.queueMs ?? null,
|
|
3559
|
+
modelMs: run.modelMs ?? null,
|
|
3560
|
+
toolsMs: run.toolsMs ?? null,
|
|
3561
|
+
modelCalls: run.modelCalls ?? null,
|
|
3562
|
+
toolCalls: run.toolCalls ?? null
|
|
3408
3563
|
};
|
|
3409
3564
|
}
|
|
3410
3565
|
__name(toRecentRunRow, "toRecentRunRow");
|
|
3566
|
+
function toRunTimings(run) {
|
|
3567
|
+
if (!Array.isArray(run.timings)) return null;
|
|
3568
|
+
return {
|
|
3569
|
+
modelMs: run.modelMs ?? 0,
|
|
3570
|
+
toolsMs: run.toolsMs ?? 0,
|
|
3571
|
+
modelCalls: run.modelCalls ?? 0,
|
|
3572
|
+
toolCalls: run.toolCalls ?? 0,
|
|
3573
|
+
steps: run.timings
|
|
3574
|
+
};
|
|
3575
|
+
}
|
|
3576
|
+
__name(toRunTimings, "toRunTimings");
|
|
3411
3577
|
function toRunToolCallRow(toolCall) {
|
|
3412
3578
|
return {
|
|
3413
3579
|
toolCallId: toolCall.id,
|
|
@@ -3423,6 +3589,12 @@ function toRunToolCallRow(toolCall) {
|
|
|
3423
3589
|
__name(toRunToolCallRow, "toRunToolCallRow");
|
|
3424
3590
|
function toUsageInput(row) {
|
|
3425
3591
|
return {
|
|
3592
|
+
feature: row.feature ?? null,
|
|
3593
|
+
purpose: row.purpose ?? null,
|
|
3594
|
+
costSource: row.costSource ?? null,
|
|
3595
|
+
audioSeconds: row.audioSeconds == null ? null : Number(row.audioSeconds),
|
|
3596
|
+
characters: row.characters == null ? null : Number(row.characters),
|
|
3597
|
+
requests: row.requests ?? null,
|
|
3426
3598
|
modelId: row.modelId,
|
|
3427
3599
|
actorRef: row.actorRef,
|
|
3428
3600
|
threadId: row.threadId,
|
|
@@ -3470,7 +3642,12 @@ var DrizzleGovernanceQueries = class {
|
|
|
3470
3642
|
}
|
|
3471
3643
|
return pricing;
|
|
3472
3644
|
}
|
|
3473
|
-
|
|
3645
|
+
/**
|
|
3646
|
+
* The usage rows in range, with only what the bucketers read: `requests` always (a recorded row
|
|
3647
|
+
* counts as its own requests in every rollup), and the feature/unit columns only for the
|
|
3648
|
+
* per-feature reads that group by them.
|
|
3649
|
+
*/
|
|
3650
|
+
async usageInRange(range, withFeature = false) {
|
|
3474
3651
|
const { start, end } = dayBoundsUtc(range);
|
|
3475
3652
|
const usage = this.t.agentTokenUsage;
|
|
3476
3653
|
const rows = await this.db.select({
|
|
@@ -3482,7 +3659,15 @@ var DrizzleGovernanceQueries = class {
|
|
|
3482
3659
|
outputTokens: usage.outputTokens,
|
|
3483
3660
|
cacheWriteTokens: usage.cacheWriteTokens,
|
|
3484
3661
|
cacheReadTokens: usage.cacheReadTokens,
|
|
3485
|
-
costUsd: usage.costUsd
|
|
3662
|
+
costUsd: usage.costUsd,
|
|
3663
|
+
requests: usage.requests,
|
|
3664
|
+
...withFeature ? {
|
|
3665
|
+
feature: usage.feature,
|
|
3666
|
+
purpose: usage.purpose,
|
|
3667
|
+
costSource: usage.costSource,
|
|
3668
|
+
audioSeconds: usage.audioSeconds,
|
|
3669
|
+
characters: usage.characters
|
|
3670
|
+
} : {}
|
|
3486
3671
|
}).from(usage).where(and3(gte2(this.t.agentTokenUsage.createdAt, start), lte3(this.t.agentTokenUsage.createdAt, end)));
|
|
3487
3672
|
return rows.map(toUsageInput);
|
|
3488
3673
|
}
|
|
@@ -3490,6 +3675,14 @@ var DrizzleGovernanceQueries = class {
|
|
|
3490
3675
|
const pricing = await this.loadPricing();
|
|
3491
3676
|
return bucketByModel(await this.usageInRange(range), pricing);
|
|
3492
3677
|
}
|
|
3678
|
+
async spendByFeature(range) {
|
|
3679
|
+
const pricing = await this.loadPricing();
|
|
3680
|
+
return bucketByFeature(await this.usageInRange(range, true), pricing);
|
|
3681
|
+
}
|
|
3682
|
+
async usageBreakdown(range, filter) {
|
|
3683
|
+
const pricing = await this.loadPricing();
|
|
3684
|
+
return bucketUsageBreakdown(await this.usageInRange(range, true), pricing, filter);
|
|
3685
|
+
}
|
|
3493
3686
|
async spendByActor(range) {
|
|
3494
3687
|
const pricing = await this.loadPricing();
|
|
3495
3688
|
return bucketByActor(await this.usageInRange(range), pricing);
|
|
@@ -3503,7 +3696,9 @@ var DrizzleGovernanceQueries = class {
|
|
|
3503
3696
|
const pricing = await this.loadPricing();
|
|
3504
3697
|
const rows = await this.usageInRange(range);
|
|
3505
3698
|
const threadIds = [
|
|
3506
|
-
...new Set(rows.
|
|
3699
|
+
...new Set(rows.flatMap((row) => row.threadId === null ? [] : [
|
|
3700
|
+
row.threadId
|
|
3701
|
+
]))
|
|
3507
3702
|
];
|
|
3508
3703
|
if (threadIds.length === 0) {
|
|
3509
3704
|
return [];
|
|
@@ -3581,6 +3776,7 @@ var DrizzleGovernanceQueries = class {
|
|
|
3581
3776
|
outputTokens: sum(this.t.agentTokenUsage.outputTokens)
|
|
3582
3777
|
}).from(this.t.agentTokenUsage).where(inArray2(this.t.agentTokenUsage.threadId, threadIds)).groupBy(this.t.agentTokenUsage.threadId);
|
|
3583
3778
|
for (const row of usageTotals) {
|
|
3779
|
+
if (row.threadId === null) continue;
|
|
3584
3780
|
const bucket = rollups.get(row.threadId);
|
|
3585
3781
|
if (bucket !== void 0) {
|
|
3586
3782
|
bucket.totalTokens = Number(row.inputTokens ?? 0) + Number(row.outputTokens ?? 0);
|
|
@@ -3957,7 +4153,8 @@ var DrizzleGovernanceQueries = class {
|
|
|
3957
4153
|
actorRef: row.thread.actorRef,
|
|
3958
4154
|
deleted: row.thread.deletedAt != null
|
|
3959
4155
|
},
|
|
3960
|
-
toolCalls: toolCalls.map(toRunToolCallRow)
|
|
4156
|
+
toolCalls: toolCalls.map(toRunToolCallRow),
|
|
4157
|
+
timings: toRunTimings(row.run)
|
|
3961
4158
|
};
|
|
3962
4159
|
}
|
|
3963
4160
|
/**
|