@dudousxd/nestjs-agent-store-drizzle 0.1.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/LICENSE +21 -0
- package/README.md +53 -0
- package/dist/index.cjs +784 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2099 -0
- package/dist/index.d.ts +2099 -0
- package/dist/index.js +749 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
4
|
+
// src/schema.ts
|
|
5
|
+
import { index, integer, real, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
|
6
|
+
var agentThread = sqliteTable("agent_thread", {
|
|
7
|
+
id: text("id").primaryKey(),
|
|
8
|
+
actorRef: text("actor_ref").notNull(),
|
|
9
|
+
tenantRef: text("tenant_ref"),
|
|
10
|
+
title: text("title").notNull(),
|
|
11
|
+
persona: text("persona").notNull().default("default"),
|
|
12
|
+
transient: integer("transient", {
|
|
13
|
+
mode: "boolean"
|
|
14
|
+
}).notNull().default(false),
|
|
15
|
+
activeStreamId: text("active_stream_id"),
|
|
16
|
+
createdAt: integer("created_at", {
|
|
17
|
+
mode: "timestamp_ms"
|
|
18
|
+
}).notNull(),
|
|
19
|
+
updatedAt: integer("updated_at", {
|
|
20
|
+
mode: "timestamp_ms"
|
|
21
|
+
}).notNull(),
|
|
22
|
+
deletedAt: integer("deleted_at", {
|
|
23
|
+
mode: "timestamp_ms"
|
|
24
|
+
})
|
|
25
|
+
}, (table) => [
|
|
26
|
+
index("agent_thread_actor_updated_idx").on(table.actorRef, table.updatedAt)
|
|
27
|
+
]);
|
|
28
|
+
var agentMessage = sqliteTable("agent_message", {
|
|
29
|
+
id: text("id").primaryKey(),
|
|
30
|
+
threadId: text("thread_id").notNull().references(() => agentThread.id, {
|
|
31
|
+
onDelete: "cascade"
|
|
32
|
+
}),
|
|
33
|
+
role: text("role").$type().notNull(),
|
|
34
|
+
content: text("content").notNull(),
|
|
35
|
+
toolCalls: text("tool_calls", {
|
|
36
|
+
mode: "json"
|
|
37
|
+
}).$type(),
|
|
38
|
+
toolResults: text("tool_results", {
|
|
39
|
+
mode: "json"
|
|
40
|
+
}).$type(),
|
|
41
|
+
followUps: text("follow_ups", {
|
|
42
|
+
mode: "json"
|
|
43
|
+
}).$type(),
|
|
44
|
+
usage: text("usage", {
|
|
45
|
+
mode: "json"
|
|
46
|
+
}).$type(),
|
|
47
|
+
persona: text("persona"),
|
|
48
|
+
createdAt: integer("created_at", {
|
|
49
|
+
mode: "timestamp_ms"
|
|
50
|
+
}).notNull()
|
|
51
|
+
}, (table) => [
|
|
52
|
+
index("agent_message_thread_created_idx").on(table.threadId, table.createdAt)
|
|
53
|
+
]);
|
|
54
|
+
var agentToolCall = sqliteTable("agent_tool_call", {
|
|
55
|
+
id: text("id").primaryKey(),
|
|
56
|
+
messageId: text("message_id").notNull().references(() => agentMessage.id, {
|
|
57
|
+
onDelete: "cascade"
|
|
58
|
+
}),
|
|
59
|
+
toolName: text("tool_name").notNull(),
|
|
60
|
+
toolType: text("tool_type").$type().notNull(),
|
|
61
|
+
input: text("input", {
|
|
62
|
+
mode: "json"
|
|
63
|
+
}),
|
|
64
|
+
output: text("output", {
|
|
65
|
+
mode: "json"
|
|
66
|
+
}),
|
|
67
|
+
status: text("status").$type().notNull(),
|
|
68
|
+
executedByRef: text("executed_by_ref"),
|
|
69
|
+
executionMs: integer("execution_ms"),
|
|
70
|
+
error: text("error"),
|
|
71
|
+
createdAt: integer("created_at", {
|
|
72
|
+
mode: "timestamp_ms"
|
|
73
|
+
}).notNull(),
|
|
74
|
+
executedAt: integer("executed_at", {
|
|
75
|
+
mode: "timestamp_ms"
|
|
76
|
+
})
|
|
77
|
+
});
|
|
78
|
+
var agentTokenUsage = sqliteTable("agent_token_usage", {
|
|
79
|
+
id: text("id").primaryKey(),
|
|
80
|
+
threadId: text("thread_id").notNull().references(() => agentThread.id, {
|
|
81
|
+
onDelete: "cascade"
|
|
82
|
+
}),
|
|
83
|
+
actorRef: text("actor_ref").notNull(),
|
|
84
|
+
messageId: text("message_id"),
|
|
85
|
+
modelId: text("model_id").notNull(),
|
|
86
|
+
purpose: text("purpose").$type().notNull(),
|
|
87
|
+
inputTokens: integer("input_tokens").notNull(),
|
|
88
|
+
outputTokens: integer("output_tokens").notNull(),
|
|
89
|
+
/** Subset of `inputTokens` written to the prompt cache this turn; null when not reported. */
|
|
90
|
+
cacheWriteTokens: integer("cache_write_tokens"),
|
|
91
|
+
/** Subset of `inputTokens` served from the prompt cache this turn; null when not reported. */
|
|
92
|
+
cacheReadTokens: integer("cache_read_tokens"),
|
|
93
|
+
/** Provider-reported actual USD cost for the turn; null when only tokens were reported. */
|
|
94
|
+
costUsd: real("cost_usd"),
|
|
95
|
+
createdAt: integer("created_at", {
|
|
96
|
+
mode: "timestamp_ms"
|
|
97
|
+
}).notNull()
|
|
98
|
+
}, (table) => [
|
|
99
|
+
index("agent_token_usage_actor_created_idx").on(table.actorRef, table.createdAt)
|
|
100
|
+
]);
|
|
101
|
+
var agentModelPricing = sqliteTable("agent_model_pricing", {
|
|
102
|
+
id: text("id").primaryKey(),
|
|
103
|
+
modelId: text("model_id").notNull(),
|
|
104
|
+
inputPricePer1m: real("input_price_per_1m").notNull(),
|
|
105
|
+
outputPricePer1m: real("output_price_per_1m").notNull(),
|
|
106
|
+
/** Per-1M price for cache-write input tokens; null → price them at the input rate. */
|
|
107
|
+
cacheWritePricePer1m: real("cache_write_price_per_1m"),
|
|
108
|
+
/** Per-1M price for cache-read input tokens; null → price them at the input rate. */
|
|
109
|
+
cacheReadPricePer1m: real("cache_read_price_per_1m"),
|
|
110
|
+
effectiveFrom: integer("effective_from", {
|
|
111
|
+
mode: "timestamp_ms"
|
|
112
|
+
}).notNull(),
|
|
113
|
+
isCurrent: integer("is_current", {
|
|
114
|
+
mode: "boolean"
|
|
115
|
+
}).notNull()
|
|
116
|
+
});
|
|
117
|
+
var agentSchema = {
|
|
118
|
+
agentThread,
|
|
119
|
+
agentMessage,
|
|
120
|
+
agentToolCall,
|
|
121
|
+
agentTokenUsage,
|
|
122
|
+
agentModelPricing
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// src/ensure-schema.ts
|
|
126
|
+
import { sql } from "drizzle-orm";
|
|
127
|
+
var statements = [
|
|
128
|
+
`CREATE TABLE IF NOT EXISTS agent_thread (
|
|
129
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
130
|
+
actor_ref TEXT NOT NULL,
|
|
131
|
+
tenant_ref TEXT,
|
|
132
|
+
title TEXT NOT NULL,
|
|
133
|
+
persona TEXT NOT NULL DEFAULT 'default',
|
|
134
|
+
transient INTEGER NOT NULL DEFAULT 0,
|
|
135
|
+
active_stream_id TEXT,
|
|
136
|
+
created_at INTEGER NOT NULL,
|
|
137
|
+
updated_at INTEGER NOT NULL,
|
|
138
|
+
deleted_at INTEGER
|
|
139
|
+
)`,
|
|
140
|
+
`CREATE INDEX IF NOT EXISTS agent_thread_actor_updated_idx
|
|
141
|
+
ON agent_thread (actor_ref, updated_at)`,
|
|
142
|
+
`CREATE TABLE IF NOT EXISTS agent_message (
|
|
143
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
144
|
+
thread_id TEXT NOT NULL REFERENCES agent_thread(id) ON DELETE CASCADE,
|
|
145
|
+
role TEXT NOT NULL,
|
|
146
|
+
content TEXT NOT NULL,
|
|
147
|
+
tool_calls TEXT,
|
|
148
|
+
tool_results TEXT,
|
|
149
|
+
follow_ups TEXT,
|
|
150
|
+
usage TEXT,
|
|
151
|
+
persona TEXT,
|
|
152
|
+
created_at INTEGER NOT NULL
|
|
153
|
+
)`,
|
|
154
|
+
`CREATE INDEX IF NOT EXISTS agent_message_thread_created_idx
|
|
155
|
+
ON agent_message (thread_id, created_at)`,
|
|
156
|
+
`CREATE TABLE IF NOT EXISTS agent_tool_call (
|
|
157
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
158
|
+
message_id TEXT NOT NULL REFERENCES agent_message(id) ON DELETE CASCADE,
|
|
159
|
+
tool_name TEXT NOT NULL,
|
|
160
|
+
tool_type TEXT NOT NULL,
|
|
161
|
+
input TEXT,
|
|
162
|
+
output TEXT,
|
|
163
|
+
status TEXT NOT NULL,
|
|
164
|
+
executed_by_ref TEXT,
|
|
165
|
+
execution_ms INTEGER,
|
|
166
|
+
error TEXT,
|
|
167
|
+
created_at INTEGER NOT NULL,
|
|
168
|
+
executed_at INTEGER
|
|
169
|
+
)`,
|
|
170
|
+
`CREATE TABLE IF NOT EXISTS agent_token_usage (
|
|
171
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
172
|
+
thread_id TEXT NOT NULL REFERENCES agent_thread(id) ON DELETE CASCADE,
|
|
173
|
+
actor_ref TEXT NOT NULL,
|
|
174
|
+
message_id TEXT,
|
|
175
|
+
model_id TEXT NOT NULL,
|
|
176
|
+
purpose TEXT NOT NULL,
|
|
177
|
+
input_tokens INTEGER NOT NULL,
|
|
178
|
+
output_tokens INTEGER NOT NULL,
|
|
179
|
+
cache_write_tokens INTEGER,
|
|
180
|
+
cache_read_tokens INTEGER,
|
|
181
|
+
cost_usd REAL,
|
|
182
|
+
created_at INTEGER NOT NULL
|
|
183
|
+
)`,
|
|
184
|
+
`CREATE INDEX IF NOT EXISTS agent_token_usage_actor_created_idx
|
|
185
|
+
ON agent_token_usage (actor_ref, created_at)`,
|
|
186
|
+
`CREATE TABLE IF NOT EXISTS agent_model_pricing (
|
|
187
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
188
|
+
model_id TEXT NOT NULL,
|
|
189
|
+
input_price_per_1m REAL NOT NULL,
|
|
190
|
+
output_price_per_1m REAL NOT NULL,
|
|
191
|
+
cache_write_price_per_1m REAL,
|
|
192
|
+
cache_read_price_per_1m REAL,
|
|
193
|
+
effective_from INTEGER NOT NULL,
|
|
194
|
+
is_current INTEGER NOT NULL
|
|
195
|
+
)`
|
|
196
|
+
];
|
|
197
|
+
async function ensureAgentSchema(db) {
|
|
198
|
+
for (const statement of statements) {
|
|
199
|
+
await db.run(sql.raw(statement));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
__name(ensureAgentSchema, "ensureAgentSchema");
|
|
203
|
+
|
|
204
|
+
// src/drizzle-agent-store.ts
|
|
205
|
+
import { and, asc, desc, eq, gte, inArray, isNull, lte } from "drizzle-orm";
|
|
206
|
+
var DrizzleAgentStore = class {
|
|
207
|
+
static {
|
|
208
|
+
__name(this, "DrizzleAgentStore");
|
|
209
|
+
}
|
|
210
|
+
db;
|
|
211
|
+
constructor(db) {
|
|
212
|
+
this.db = db;
|
|
213
|
+
}
|
|
214
|
+
async createThread(input) {
|
|
215
|
+
const now = /* @__PURE__ */ new Date();
|
|
216
|
+
const thread = {
|
|
217
|
+
id: crypto.randomUUID(),
|
|
218
|
+
actorRef: input.actor.id,
|
|
219
|
+
tenantRef: input.actor.tenantRef ?? null,
|
|
220
|
+
title: input.title ?? "New chat",
|
|
221
|
+
persona: input.persona,
|
|
222
|
+
transient: input.transient ?? false,
|
|
223
|
+
activeStreamId: null,
|
|
224
|
+
createdAt: now,
|
|
225
|
+
updatedAt: now,
|
|
226
|
+
deletedAt: null
|
|
227
|
+
};
|
|
228
|
+
await this.db.insert(agentThread).values(thread);
|
|
229
|
+
return this.toSummary(thread);
|
|
230
|
+
}
|
|
231
|
+
async getThread(threadId) {
|
|
232
|
+
const [thread] = await this.db.select().from(agentThread).where(and(eq(agentThread.id, threadId), isNull(agentThread.deletedAt)));
|
|
233
|
+
if (thread === void 0) {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
const messages = await this.db.select().from(agentMessage).where(eq(agentMessage.threadId, threadId)).orderBy(asc(agentMessage.createdAt), asc(agentMessage.id));
|
|
237
|
+
const last = messages[messages.length - 1];
|
|
238
|
+
return {
|
|
239
|
+
...this.toSummary(thread, last?.content),
|
|
240
|
+
messages: messages.map((message) => this.toStoredMessage(message)),
|
|
241
|
+
...thread.activeStreamId != null ? {
|
|
242
|
+
activeStreamId: thread.activeStreamId
|
|
243
|
+
} : {}
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
async listThreads(actorRef, limit = 50) {
|
|
247
|
+
const threads = await this.db.select().from(agentThread).where(and(eq(agentThread.actorRef, actorRef), eq(agentThread.transient, false), isNull(agentThread.deletedAt))).orderBy(desc(agentThread.updatedAt)).limit(limit);
|
|
248
|
+
const summaries = [];
|
|
249
|
+
for (const thread of threads) {
|
|
250
|
+
const [last] = await this.db.select().from(agentMessage).where(eq(agentMessage.threadId, thread.id)).orderBy(desc(agentMessage.createdAt), desc(agentMessage.id)).limit(1);
|
|
251
|
+
summaries.push(this.toSummary(thread, last?.content));
|
|
252
|
+
}
|
|
253
|
+
return summaries;
|
|
254
|
+
}
|
|
255
|
+
async softDeleteThread(threadId) {
|
|
256
|
+
await this.db.update(agentThread).set({
|
|
257
|
+
deletedAt: /* @__PURE__ */ new Date()
|
|
258
|
+
}).where(eq(agentThread.id, threadId));
|
|
259
|
+
}
|
|
260
|
+
async forkThread(threadId, fromMessageId) {
|
|
261
|
+
const [source] = await this.db.select().from(agentThread).where(eq(agentThread.id, threadId));
|
|
262
|
+
if (source === void 0) {
|
|
263
|
+
throw new Error(`thread ${threadId} not found`);
|
|
264
|
+
}
|
|
265
|
+
const messages = await this.db.select().from(agentMessage).where(eq(agentMessage.threadId, threadId)).orderBy(asc(agentMessage.createdAt), asc(agentMessage.id));
|
|
266
|
+
const cutoff = messages.findIndex((message) => message.id === fromMessageId);
|
|
267
|
+
const kept = cutoff >= 0 ? messages.slice(0, cutoff + 1) : messages;
|
|
268
|
+
const now = /* @__PURE__ */ new Date();
|
|
269
|
+
const fork = {
|
|
270
|
+
id: crypto.randomUUID(),
|
|
271
|
+
actorRef: source.actorRef,
|
|
272
|
+
tenantRef: source.tenantRef,
|
|
273
|
+
title: source.title,
|
|
274
|
+
persona: source.persona,
|
|
275
|
+
transient: false,
|
|
276
|
+
activeStreamId: null,
|
|
277
|
+
createdAt: now,
|
|
278
|
+
updatedAt: now,
|
|
279
|
+
deletedAt: null
|
|
280
|
+
};
|
|
281
|
+
await this.db.insert(agentThread).values(fork);
|
|
282
|
+
if (kept.length > 0) {
|
|
283
|
+
await this.db.insert(agentMessage).values(kept.map((message) => ({
|
|
284
|
+
id: crypto.randomUUID(),
|
|
285
|
+
threadId: fork.id,
|
|
286
|
+
role: message.role,
|
|
287
|
+
content: message.content,
|
|
288
|
+
toolCalls: message.toolCalls,
|
|
289
|
+
toolResults: message.toolResults,
|
|
290
|
+
followUps: message.followUps,
|
|
291
|
+
usage: message.usage,
|
|
292
|
+
persona: message.persona,
|
|
293
|
+
createdAt: message.createdAt
|
|
294
|
+
})));
|
|
295
|
+
}
|
|
296
|
+
return this.toSummary(fork);
|
|
297
|
+
}
|
|
298
|
+
async ownerOfThread(threadId) {
|
|
299
|
+
const [thread] = await this.db.select({
|
|
300
|
+
actorRef: agentThread.actorRef
|
|
301
|
+
}).from(agentThread).where(and(eq(agentThread.id, threadId), isNull(agentThread.deletedAt)));
|
|
302
|
+
return thread?.actorRef ?? null;
|
|
303
|
+
}
|
|
304
|
+
async ownerOfToolCall(toolCallId) {
|
|
305
|
+
const [row] = await this.db.select({
|
|
306
|
+
actorRef: agentThread.actorRef
|
|
307
|
+
}).from(agentToolCall).innerJoin(agentMessage, eq(agentToolCall.messageId, agentMessage.id)).innerJoin(agentThread, eq(agentMessage.threadId, agentThread.id)).where(eq(agentToolCall.id, toolCallId));
|
|
308
|
+
return row?.actorRef ?? null;
|
|
309
|
+
}
|
|
310
|
+
async runForToolCall(toolCallId) {
|
|
311
|
+
const [row] = await this.db.select({
|
|
312
|
+
activeStreamId: agentThread.activeStreamId
|
|
313
|
+
}).from(agentToolCall).innerJoin(agentMessage, eq(agentToolCall.messageId, agentMessage.id)).innerJoin(agentThread, eq(agentMessage.threadId, agentThread.id)).where(eq(agentToolCall.id, toolCallId));
|
|
314
|
+
return row?.activeStreamId ?? null;
|
|
315
|
+
}
|
|
316
|
+
async ownerOfActiveStream(runId) {
|
|
317
|
+
const [thread] = await this.db.select({
|
|
318
|
+
actorRef: agentThread.actorRef
|
|
319
|
+
}).from(agentThread).where(and(eq(agentThread.activeStreamId, runId), isNull(agentThread.deletedAt)));
|
|
320
|
+
return thread?.actorRef ?? null;
|
|
321
|
+
}
|
|
322
|
+
async setTitle(threadId, title) {
|
|
323
|
+
await this.db.update(agentThread).set({
|
|
324
|
+
title,
|
|
325
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
326
|
+
}).where(eq(agentThread.id, threadId));
|
|
327
|
+
}
|
|
328
|
+
async setActiveStream(threadId, runId) {
|
|
329
|
+
await this.db.update(agentThread).set({
|
|
330
|
+
activeStreamId: runId
|
|
331
|
+
}).where(eq(agentThread.id, threadId));
|
|
332
|
+
}
|
|
333
|
+
async appendMessage(input) {
|
|
334
|
+
const [thread] = await this.db.select().from(agentThread).where(eq(agentThread.id, input.threadId));
|
|
335
|
+
if (thread === void 0) {
|
|
336
|
+
throw new Error(`thread ${input.threadId} not found`);
|
|
337
|
+
}
|
|
338
|
+
const now = /* @__PURE__ */ new Date();
|
|
339
|
+
const message = {
|
|
340
|
+
id: crypto.randomUUID(),
|
|
341
|
+
threadId: input.threadId,
|
|
342
|
+
role: input.role,
|
|
343
|
+
content: input.content,
|
|
344
|
+
toolCalls: input.toolCalls ?? null,
|
|
345
|
+
toolResults: input.toolResults ?? null,
|
|
346
|
+
followUps: input.followUps ?? null,
|
|
347
|
+
usage: input.usage ?? null,
|
|
348
|
+
persona: input.persona ?? null,
|
|
349
|
+
createdAt: now
|
|
350
|
+
};
|
|
351
|
+
await this.db.insert(agentMessage).values(message);
|
|
352
|
+
await this.db.update(agentThread).set({
|
|
353
|
+
updatedAt: now
|
|
354
|
+
}).where(eq(agentThread.id, input.threadId));
|
|
355
|
+
return this.toStoredMessage(message);
|
|
356
|
+
}
|
|
357
|
+
async truncateFrom(threadId, messageId) {
|
|
358
|
+
const messages = await this.db.select().from(agentMessage).where(eq(agentMessage.threadId, threadId)).orderBy(asc(agentMessage.createdAt), asc(agentMessage.id));
|
|
359
|
+
const cutoff = messages.findIndex((message) => message.id === messageId);
|
|
360
|
+
if (cutoff < 0) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
const doomedIds = messages.slice(cutoff).map((message) => message.id);
|
|
364
|
+
await this.db.delete(agentToolCall).where(inArray(agentToolCall.messageId, doomedIds));
|
|
365
|
+
await this.db.delete(agentMessage).where(inArray(agentMessage.id, doomedIds));
|
|
366
|
+
}
|
|
367
|
+
async recordToolCall(input) {
|
|
368
|
+
await this.db.insert(agentToolCall).values({
|
|
369
|
+
id: input.toolCallId,
|
|
370
|
+
messageId: input.messageId,
|
|
371
|
+
toolName: input.toolName,
|
|
372
|
+
toolType: input.toolType,
|
|
373
|
+
input: input.input,
|
|
374
|
+
output: null,
|
|
375
|
+
status: input.status,
|
|
376
|
+
executedByRef: null,
|
|
377
|
+
executionMs: null,
|
|
378
|
+
error: null,
|
|
379
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
380
|
+
executedAt: null
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
async updateToolCall(input) {
|
|
384
|
+
const updates = {
|
|
385
|
+
status: input.status
|
|
386
|
+
};
|
|
387
|
+
if (input.output !== void 0) {
|
|
388
|
+
updates.output = input.output;
|
|
389
|
+
}
|
|
390
|
+
if (input.error !== void 0) {
|
|
391
|
+
updates.error = input.error;
|
|
392
|
+
}
|
|
393
|
+
if (input.executionMs !== void 0) {
|
|
394
|
+
updates.executionMs = input.executionMs;
|
|
395
|
+
}
|
|
396
|
+
if (input.executedByRef !== void 0) {
|
|
397
|
+
updates.executedByRef = input.executedByRef;
|
|
398
|
+
}
|
|
399
|
+
if (input.status === "executed" || input.status === "auto_executed") {
|
|
400
|
+
updates.executedAt = /* @__PURE__ */ new Date();
|
|
401
|
+
}
|
|
402
|
+
await this.db.update(agentToolCall).set(updates).where(eq(agentToolCall.id, input.toolCallId));
|
|
403
|
+
}
|
|
404
|
+
async recordUsage(input) {
|
|
405
|
+
await this.db.insert(agentTokenUsage).values({
|
|
406
|
+
id: crypto.randomUUID(),
|
|
407
|
+
threadId: input.threadId,
|
|
408
|
+
actorRef: input.actorRef,
|
|
409
|
+
messageId: input.messageId ?? null,
|
|
410
|
+
modelId: input.modelId,
|
|
411
|
+
purpose: input.purpose,
|
|
412
|
+
inputTokens: input.usage.inputTokens,
|
|
413
|
+
outputTokens: input.usage.outputTokens,
|
|
414
|
+
cacheWriteTokens: input.usage.cacheWriteTokens ?? null,
|
|
415
|
+
cacheReadTokens: input.usage.cacheReadTokens ?? null,
|
|
416
|
+
costUsd: input.costUsd ?? null,
|
|
417
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
async quotaToday(actorRef, day) {
|
|
421
|
+
const start = /* @__PURE__ */ new Date(`${day}T00:00:00.000Z`);
|
|
422
|
+
const end = /* @__PURE__ */ new Date(`${day}T23:59:59.999Z`);
|
|
423
|
+
const rows = await this.db.select().from(agentTokenUsage).where(and(eq(agentTokenUsage.actorRef, actorRef), gte(agentTokenUsage.createdAt, start), lte(agentTokenUsage.createdAt, end)));
|
|
424
|
+
const usedTokens = rows.reduce((sum, row) => sum + row.inputTokens + row.outputTokens, 0);
|
|
425
|
+
return {
|
|
426
|
+
usedTokens
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
toSummary(thread, lastContent) {
|
|
430
|
+
return {
|
|
431
|
+
id: thread.id,
|
|
432
|
+
title: thread.title,
|
|
433
|
+
persona: thread.persona,
|
|
434
|
+
transient: thread.transient,
|
|
435
|
+
createdAt: thread.createdAt.toISOString(),
|
|
436
|
+
updatedAt: thread.updatedAt.toISOString(),
|
|
437
|
+
...lastContent !== void 0 ? {
|
|
438
|
+
lastMessagePreview: lastContent.slice(0, 120)
|
|
439
|
+
} : {}
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
toStoredMessage(message) {
|
|
443
|
+
return {
|
|
444
|
+
id: message.id,
|
|
445
|
+
role: message.role,
|
|
446
|
+
content: message.content,
|
|
447
|
+
createdAt: message.createdAt.toISOString(),
|
|
448
|
+
...message.toolCalls != null ? {
|
|
449
|
+
toolCalls: message.toolCalls
|
|
450
|
+
} : {},
|
|
451
|
+
...message.toolResults != null ? {
|
|
452
|
+
toolResults: message.toolResults
|
|
453
|
+
} : {},
|
|
454
|
+
...message.followUps != null ? {
|
|
455
|
+
followUps: message.followUps
|
|
456
|
+
} : {},
|
|
457
|
+
...message.usage != null ? {
|
|
458
|
+
usage: message.usage
|
|
459
|
+
} : {}
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
// src/drizzle-governance-queries.ts
|
|
465
|
+
import { and as and2, count, desc as desc2, eq as eq2, gte as gte2, isNull as isNull2, lte as lte2 } from "drizzle-orm";
|
|
466
|
+
function estimateFromTokens(pricing, row) {
|
|
467
|
+
const price = pricing.get(row.modelId);
|
|
468
|
+
if (price === void 0) {
|
|
469
|
+
return 0;
|
|
470
|
+
}
|
|
471
|
+
const cacheWriteTokens = row.cacheWriteTokens ?? 0;
|
|
472
|
+
const cacheReadTokens = row.cacheReadTokens ?? 0;
|
|
473
|
+
const uncachedInputTokens = row.inputTokens - cacheWriteTokens - cacheReadTokens;
|
|
474
|
+
return uncachedInputTokens / 1e6 * price.inputPricePer1m + cacheWriteTokens / 1e6 * (price.cacheWritePricePer1m ?? price.inputPricePer1m) + cacheReadTokens / 1e6 * (price.cacheReadPricePer1m ?? price.inputPricePer1m) + row.outputTokens / 1e6 * price.outputPricePer1m;
|
|
475
|
+
}
|
|
476
|
+
__name(estimateFromTokens, "estimateFromTokens");
|
|
477
|
+
function rowCost(pricing, row) {
|
|
478
|
+
return row.costUsd ?? estimateFromTokens(pricing, row);
|
|
479
|
+
}
|
|
480
|
+
__name(rowCost, "rowCost");
|
|
481
|
+
function dayBounds(range) {
|
|
482
|
+
return {
|
|
483
|
+
start: /* @__PURE__ */ new Date(`${range.fromDay}T00:00:00.000Z`),
|
|
484
|
+
end: /* @__PURE__ */ new Date(`${range.toDay}T23:59:59.999Z`)
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
__name(dayBounds, "dayBounds");
|
|
488
|
+
var DrizzleGovernanceQueries = class {
|
|
489
|
+
static {
|
|
490
|
+
__name(this, "DrizzleGovernanceQueries");
|
|
491
|
+
}
|
|
492
|
+
db;
|
|
493
|
+
constructor(db) {
|
|
494
|
+
this.db = db;
|
|
495
|
+
}
|
|
496
|
+
async loadPricing() {
|
|
497
|
+
const rows = await this.db.select().from(agentModelPricing).where(eq2(agentModelPricing.isCurrent, true));
|
|
498
|
+
const pricing = /* @__PURE__ */ new Map();
|
|
499
|
+
for (const row of rows) {
|
|
500
|
+
pricing.set(row.modelId, {
|
|
501
|
+
inputPricePer1m: row.inputPricePer1m,
|
|
502
|
+
outputPricePer1m: row.outputPricePer1m,
|
|
503
|
+
cacheWritePricePer1m: row.cacheWritePricePer1m,
|
|
504
|
+
cacheReadPricePer1m: row.cacheReadPricePer1m
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
return pricing;
|
|
508
|
+
}
|
|
509
|
+
async usageInRange(range) {
|
|
510
|
+
const { start, end } = dayBounds(range);
|
|
511
|
+
const rows = await this.db.select().from(agentTokenUsage).where(and2(gte2(agentTokenUsage.createdAt, start), lte2(agentTokenUsage.createdAt, end)));
|
|
512
|
+
return rows;
|
|
513
|
+
}
|
|
514
|
+
async spendByModel(range) {
|
|
515
|
+
const pricing = await this.loadPricing();
|
|
516
|
+
const rows = await this.usageInRange(range);
|
|
517
|
+
const byModel = /* @__PURE__ */ new Map();
|
|
518
|
+
for (const row of rows) {
|
|
519
|
+
const bucket = byModel.get(row.modelId) ?? {
|
|
520
|
+
requests: 0,
|
|
521
|
+
inputTokens: 0,
|
|
522
|
+
outputTokens: 0,
|
|
523
|
+
costUsd: 0
|
|
524
|
+
};
|
|
525
|
+
bucket.requests += 1;
|
|
526
|
+
bucket.inputTokens += row.inputTokens;
|
|
527
|
+
bucket.outputTokens += row.outputTokens;
|
|
528
|
+
bucket.costUsd += rowCost(pricing, row);
|
|
529
|
+
byModel.set(row.modelId, bucket);
|
|
530
|
+
}
|
|
531
|
+
const result = [];
|
|
532
|
+
for (const [modelId, bucket] of byModel) {
|
|
533
|
+
result.push({
|
|
534
|
+
modelId,
|
|
535
|
+
requests: bucket.requests,
|
|
536
|
+
inputTokens: bucket.inputTokens,
|
|
537
|
+
outputTokens: bucket.outputTokens,
|
|
538
|
+
costUsd: bucket.costUsd
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
result.sort((left, right) => right.costUsd - left.costUsd || left.modelId.localeCompare(right.modelId));
|
|
542
|
+
return result;
|
|
543
|
+
}
|
|
544
|
+
async spendByActor(range) {
|
|
545
|
+
const pricing = await this.loadPricing();
|
|
546
|
+
const rows = await this.usageInRange(range);
|
|
547
|
+
const byActor = /* @__PURE__ */ new Map();
|
|
548
|
+
for (const row of rows) {
|
|
549
|
+
const bucket = byActor.get(row.actorRef) ?? {
|
|
550
|
+
requests: 0,
|
|
551
|
+
totalTokens: 0,
|
|
552
|
+
costUsd: 0
|
|
553
|
+
};
|
|
554
|
+
bucket.requests += 1;
|
|
555
|
+
bucket.totalTokens += row.inputTokens + row.outputTokens;
|
|
556
|
+
bucket.costUsd += rowCost(pricing, row);
|
|
557
|
+
byActor.set(row.actorRef, bucket);
|
|
558
|
+
}
|
|
559
|
+
const result = [];
|
|
560
|
+
for (const [actorRef, bucket] of byActor) {
|
|
561
|
+
result.push({
|
|
562
|
+
actorRef,
|
|
563
|
+
requests: bucket.requests,
|
|
564
|
+
totalTokens: bucket.totalTokens,
|
|
565
|
+
costUsd: bucket.costUsd
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
result.sort((left, right) => right.costUsd - left.costUsd || left.actorRef.localeCompare(right.actorRef));
|
|
569
|
+
return result;
|
|
570
|
+
}
|
|
571
|
+
async usageTrend(range) {
|
|
572
|
+
const pricing = await this.loadPricing();
|
|
573
|
+
const rows = await this.usageInRange(range);
|
|
574
|
+
const byDay = /* @__PURE__ */ new Map();
|
|
575
|
+
for (const row of rows) {
|
|
576
|
+
const day = row.createdAt.toISOString().slice(0, 10);
|
|
577
|
+
const bucket = byDay.get(day) ?? {
|
|
578
|
+
totalTokens: 0,
|
|
579
|
+
costUsd: 0
|
|
580
|
+
};
|
|
581
|
+
bucket.totalTokens += row.inputTokens + row.outputTokens;
|
|
582
|
+
bucket.costUsd += rowCost(pricing, row);
|
|
583
|
+
byDay.set(day, bucket);
|
|
584
|
+
}
|
|
585
|
+
const result = [];
|
|
586
|
+
for (const [day, bucket] of byDay) {
|
|
587
|
+
result.push({
|
|
588
|
+
day,
|
|
589
|
+
totalTokens: bucket.totalTokens,
|
|
590
|
+
costUsd: bucket.costUsd
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
result.sort((left, right) => left.day.localeCompare(right.day));
|
|
594
|
+
return result;
|
|
595
|
+
}
|
|
596
|
+
async recentToolCalls(limit) {
|
|
597
|
+
const rows = await this.db.select({
|
|
598
|
+
toolCallId: agentToolCall.id,
|
|
599
|
+
toolName: agentToolCall.toolName,
|
|
600
|
+
toolType: agentToolCall.toolType,
|
|
601
|
+
status: agentToolCall.status,
|
|
602
|
+
threadId: agentMessage.threadId,
|
|
603
|
+
createdAt: agentToolCall.createdAt
|
|
604
|
+
}).from(agentToolCall).innerJoin(agentMessage, eq2(agentToolCall.messageId, agentMessage.id)).orderBy(desc2(agentToolCall.createdAt), desc2(agentToolCall.id)).limit(limit);
|
|
605
|
+
return rows.map((row) => ({
|
|
606
|
+
toolCallId: row.toolCallId,
|
|
607
|
+
toolName: row.toolName,
|
|
608
|
+
toolType: row.toolType,
|
|
609
|
+
status: row.status,
|
|
610
|
+
threadId: row.threadId,
|
|
611
|
+
createdAt: row.createdAt.toISOString()
|
|
612
|
+
}));
|
|
613
|
+
}
|
|
614
|
+
async recentThreads(limit) {
|
|
615
|
+
const threads = await this.db.select().from(agentThread).where(isNull2(agentThread.deletedAt)).orderBy(desc2(agentThread.updatedAt), desc2(agentThread.id)).limit(limit);
|
|
616
|
+
const result = [];
|
|
617
|
+
for (const thread of threads) {
|
|
618
|
+
const [messageCountRow] = await this.db.select({
|
|
619
|
+
value: count()
|
|
620
|
+
}).from(agentMessage).where(eq2(agentMessage.threadId, thread.id));
|
|
621
|
+
const usageRows = await this.db.select().from(agentTokenUsage).where(eq2(agentTokenUsage.threadId, thread.id));
|
|
622
|
+
const totalTokens = usageRows.reduce((sum, row) => sum + row.inputTokens + row.outputTokens, 0);
|
|
623
|
+
result.push({
|
|
624
|
+
threadId: thread.id,
|
|
625
|
+
title: thread.title,
|
|
626
|
+
actorRef: thread.actorRef,
|
|
627
|
+
messageCount: messageCountRow?.value ?? 0,
|
|
628
|
+
totalTokens,
|
|
629
|
+
lastActivityAt: thread.updatedAt.toISOString()
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
return result;
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
// src/drizzle-pricing-store.ts
|
|
637
|
+
import { and as and3, eq as eq3 } from "drizzle-orm";
|
|
638
|
+
var DrizzlePricingStore = class {
|
|
639
|
+
static {
|
|
640
|
+
__name(this, "DrizzlePricingStore");
|
|
641
|
+
}
|
|
642
|
+
db;
|
|
643
|
+
constructor(db) {
|
|
644
|
+
this.db = db;
|
|
645
|
+
}
|
|
646
|
+
async upsertModelPrice(input) {
|
|
647
|
+
await this.db.update(agentModelPricing).set({
|
|
648
|
+
isCurrent: false
|
|
649
|
+
}).where(and3(eq3(agentModelPricing.modelId, input.modelId), eq3(agentModelPricing.isCurrent, true)));
|
|
650
|
+
await this.db.insert(agentModelPricing).values({
|
|
651
|
+
id: crypto.randomUUID(),
|
|
652
|
+
modelId: input.modelId,
|
|
653
|
+
inputPricePer1m: input.inputPricePer1m,
|
|
654
|
+
outputPricePer1m: input.outputPricePer1m,
|
|
655
|
+
cacheWritePricePer1m: input.cacheWritePricePer1m ?? null,
|
|
656
|
+
cacheReadPricePer1m: input.cacheReadPricePer1m ?? null,
|
|
657
|
+
effectiveFrom: /* @__PURE__ */ new Date(),
|
|
658
|
+
isCurrent: true
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
async listCurrentPrices() {
|
|
662
|
+
const rows = await this.db.select().from(agentModelPricing).where(eq3(agentModelPricing.isCurrent, true));
|
|
663
|
+
return rows.map((row) => ({
|
|
664
|
+
modelId: row.modelId,
|
|
665
|
+
inputPricePer1m: row.inputPricePer1m,
|
|
666
|
+
outputPricePer1m: row.outputPricePer1m,
|
|
667
|
+
effectiveFrom: row.effectiveFrom.toISOString(),
|
|
668
|
+
...row.cacheWritePricePer1m != null ? {
|
|
669
|
+
cacheWritePricePer1m: row.cacheWritePricePer1m
|
|
670
|
+
} : {},
|
|
671
|
+
...row.cacheReadPricePer1m != null ? {
|
|
672
|
+
cacheReadPricePer1m: row.cacheReadPricePer1m
|
|
673
|
+
} : {}
|
|
674
|
+
}));
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
// src/drizzle-agent-store.module.ts
|
|
679
|
+
import { AGENT_GOVERNANCE_QUERIES, AGENT_PRICING_STORE, AGENT_STORE } from "@dudousxd/nestjs-agent-core";
|
|
680
|
+
import { Module } from "@nestjs/common";
|
|
681
|
+
function _ts_decorate(decorators, target, key, desc3) {
|
|
682
|
+
var c = arguments.length, r = c < 3 ? target : desc3 === null ? desc3 = Object.getOwnPropertyDescriptor(target, key) : desc3, d;
|
|
683
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc3);
|
|
684
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
685
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
686
|
+
}
|
|
687
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
688
|
+
var DrizzleAgentStoreModule = class _DrizzleAgentStoreModule {
|
|
689
|
+
static {
|
|
690
|
+
__name(this, "DrizzleAgentStoreModule");
|
|
691
|
+
}
|
|
692
|
+
static forRoot(options) {
|
|
693
|
+
return {
|
|
694
|
+
module: _DrizzleAgentStoreModule,
|
|
695
|
+
global: true,
|
|
696
|
+
providers: [
|
|
697
|
+
{
|
|
698
|
+
provide: DrizzleAgentStore,
|
|
699
|
+
useFactory: /* @__PURE__ */ __name(() => new DrizzleAgentStore(options.db), "useFactory")
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
provide: AGENT_STORE,
|
|
703
|
+
useExisting: DrizzleAgentStore
|
|
704
|
+
},
|
|
705
|
+
{
|
|
706
|
+
provide: DrizzleGovernanceQueries,
|
|
707
|
+
useFactory: /* @__PURE__ */ __name(() => new DrizzleGovernanceQueries(options.db), "useFactory")
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
provide: AGENT_GOVERNANCE_QUERIES,
|
|
711
|
+
useExisting: DrizzleGovernanceQueries
|
|
712
|
+
},
|
|
713
|
+
{
|
|
714
|
+
provide: DrizzlePricingStore,
|
|
715
|
+
useFactory: /* @__PURE__ */ __name(() => new DrizzlePricingStore(options.db), "useFactory")
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
provide: AGENT_PRICING_STORE,
|
|
719
|
+
useExisting: DrizzlePricingStore
|
|
720
|
+
}
|
|
721
|
+
],
|
|
722
|
+
exports: [
|
|
723
|
+
DrizzleAgentStore,
|
|
724
|
+
AGENT_STORE,
|
|
725
|
+
DrizzleGovernanceQueries,
|
|
726
|
+
AGENT_GOVERNANCE_QUERIES,
|
|
727
|
+
DrizzlePricingStore,
|
|
728
|
+
AGENT_PRICING_STORE
|
|
729
|
+
]
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
DrizzleAgentStoreModule = _ts_decorate([
|
|
734
|
+
Module({})
|
|
735
|
+
], DrizzleAgentStoreModule);
|
|
736
|
+
export {
|
|
737
|
+
DrizzleAgentStore,
|
|
738
|
+
DrizzleAgentStoreModule,
|
|
739
|
+
DrizzleGovernanceQueries,
|
|
740
|
+
DrizzlePricingStore,
|
|
741
|
+
agentMessage,
|
|
742
|
+
agentModelPricing,
|
|
743
|
+
agentSchema,
|
|
744
|
+
agentThread,
|
|
745
|
+
agentTokenUsage,
|
|
746
|
+
agentToolCall,
|
|
747
|
+
ensureAgentSchema
|
|
748
|
+
};
|
|
749
|
+
//# sourceMappingURL=index.js.map
|