@llmops/sdk 1.0.0-beta.4 → 1.0.0-beta.6

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.
Files changed (43) hide show
  1. package/dist/agents.cjs +1 -1
  2. package/dist/agents.d.cts +1 -1
  3. package/dist/agents.d.mts +1 -1
  4. package/dist/agents.mjs +1 -1
  5. package/dist/d1-store-D664VLg0.d.cts +52 -0
  6. package/dist/d1-store-DBf99oU8.d.mts +52 -0
  7. package/dist/express.cjs +1 -1
  8. package/dist/express.d.cts +3 -3
  9. package/dist/express.d.mts +3 -3
  10. package/dist/express.mjs +1 -1
  11. package/dist/hono.d.cts +2 -2
  12. package/dist/hono.d.mts +2 -2
  13. package/dist/{index-DnWGper4.d.cts → index-BHO7MEJx.d.cts} +1 -1
  14. package/dist/{index-05byZKeu.d.mts → index-C0CDqR9v.d.mts} +1 -1
  15. package/dist/{index-Dvz-L2Hf.d.mts → index-DVkfeg2W.d.mts} +1 -1
  16. package/dist/{index-Beb26ZNG.d.cts → index-sjRozlIk.d.cts} +1 -1
  17. package/dist/index.cjs +6 -3
  18. package/dist/index.d.cts +5 -4
  19. package/dist/index.d.mts +5 -4
  20. package/dist/index.mjs +4 -3
  21. package/dist/interface-BrJMazBg.d.mts +240 -0
  22. package/dist/interface-DMuF7YgM.d.cts +240 -0
  23. package/dist/nextjs.d.cts +2 -2
  24. package/dist/nextjs.d.mts +2 -2
  25. package/dist/pg-store-sIMdF_Pc.mjs +13598 -0
  26. package/dist/pg-store-uawkO2hJ.cjs +13607 -0
  27. package/dist/store/d1.cjs +491 -0
  28. package/dist/store/d1.d.cts +12 -0
  29. package/dist/store/d1.d.mts +12 -0
  30. package/dist/store/d1.mjs +490 -0
  31. package/dist/store/pg.cjs +3 -8
  32. package/dist/store/pg.d.cts +2 -2
  33. package/dist/store/pg.d.mts +2 -2
  34. package/dist/store/pg.mjs +3 -3
  35. package/dist/types.d.cts +3 -2
  36. package/dist/types.d.mts +3 -2
  37. package/package.json +15 -5
  38. /package/dist/{agents-exporter-BZHCcFSd.d.mts → agents-exporter-Bn3NtzMO.d.mts} +0 -0
  39. /package/dist/{agents-exporter-BuTq2n2y.cjs → agents-exporter-CEbQkds8.cjs} +0 -0
  40. /package/dist/{agents-exporter-vcpgCF69.mjs → agents-exporter-CGxTzDeQ.mjs} +0 -0
  41. /package/dist/{agents-exporter-uzN3bkth.d.cts → agents-exporter-vuQine9v.d.cts} +0 -0
  42. /package/dist/{express-DMtc0d_Y.mjs → express-ClNV0OG9.mjs} +0 -0
  43. /package/dist/{express-B-wbCza5.cjs → express-D-Nfc61h.cjs} +0 -0
@@ -0,0 +1,491 @@
1
+ let node_crypto = require("node:crypto");
2
+
3
+ //#region src/store/d1/d1-store.ts
4
+ function buildTagFilters(tags) {
5
+ const conditions = [];
6
+ const params = [];
7
+ if (!tags) return {
8
+ conditions,
9
+ params
10
+ };
11
+ for (const [key, values] of Object.entries(tags)) {
12
+ if (values.length === 0) continue;
13
+ if (values.length === 1) {
14
+ conditions.push(`json_extract("tags", '$.' || ?) = ?`);
15
+ params.push(key, values[0]);
16
+ } else {
17
+ const placeholders = values.map(() => "?").join(", ");
18
+ conditions.push(`json_extract("tags", '$.' || ?) IN (${placeholders})`);
19
+ params.push(key, ...values);
20
+ }
21
+ }
22
+ return {
23
+ conditions,
24
+ params
25
+ };
26
+ }
27
+ const D1_BATCH_LIMIT = 100;
28
+ function createD1LLMRequestsStore(db) {
29
+ return {
30
+ batchInsertRequests: async (requests) => {
31
+ if (requests.length === 0) return { count: 0 };
32
+ const now = (/* @__PURE__ */ new Date()).toISOString();
33
+ const stmts = requests.map((req) => db.prepare(`
34
+ INSERT INTO "llm_requests" (
35
+ "id","requestId","configId","variantId","environmentId",
36
+ "providerConfigId","provider","model","promptTokens",
37
+ "completionTokens","totalTokens","cachedTokens",
38
+ "cacheCreationTokens","cost","cacheSavings","inputCost",
39
+ "outputCost","endpoint","statusCode","latencyMs","isStreaming",
40
+ "userId","tags","guardrailResults","traceId","spanId",
41
+ "parentSpanId","sessionId","createdAt","updatedAt"
42
+ ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
43
+ `).bind((0, node_crypto.randomUUID)(), req.requestId, req.configId ?? null, req.variantId ?? null, req.environmentId ?? null, req.providerConfigId ?? null, req.provider, req.model, req.promptTokens ?? 0, req.completionTokens ?? 0, req.totalTokens ?? 0, req.cachedTokens ?? 0, req.cacheCreationTokens ?? 0, req.cost ?? 0, req.cacheSavings ?? 0, req.inputCost ?? 0, req.outputCost ?? 0, req.endpoint, req.statusCode, req.latencyMs ?? 0, req.isStreaming ? 1 : 0, req.userId ?? null, JSON.stringify(req.tags ?? {}), req.guardrailResults ? JSON.stringify(req.guardrailResults) : null, req.traceId ?? null, req.spanId ?? null, req.parentSpanId ?? null, req.sessionId ?? null, now, now));
44
+ for (let i = 0; i < stmts.length; i += D1_BATCH_LIMIT) await db.batch(stmts.slice(i, i + D1_BATCH_LIMIT));
45
+ return { count: requests.length };
46
+ },
47
+ insertRequest: async (req) => {
48
+ const now = (/* @__PURE__ */ new Date()).toISOString();
49
+ return db.prepare(`
50
+ INSERT INTO "llm_requests" (
51
+ "id","requestId","configId","variantId","environmentId",
52
+ "providerConfigId","provider","model","promptTokens",
53
+ "completionTokens","totalTokens","cachedTokens",
54
+ "cacheCreationTokens","cost","cacheSavings","inputCost",
55
+ "outputCost","endpoint","statusCode","latencyMs","isStreaming",
56
+ "userId","tags","guardrailResults","traceId","spanId",
57
+ "parentSpanId","sessionId","createdAt","updatedAt"
58
+ ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
59
+ RETURNING *
60
+ `).bind((0, node_crypto.randomUUID)(), req.requestId, req.configId ?? null, req.variantId ?? null, req.environmentId ?? null, req.providerConfigId ?? null, req.provider, req.model, req.promptTokens ?? 0, req.completionTokens ?? 0, req.totalTokens ?? 0, req.cachedTokens ?? 0, req.cacheCreationTokens ?? 0, req.cost ?? 0, req.cacheSavings ?? 0, req.inputCost ?? 0, req.outputCost ?? 0, req.endpoint, req.statusCode, req.latencyMs ?? 0, req.isStreaming ? 1 : 0, req.userId ?? null, JSON.stringify(req.tags ?? {}), req.guardrailResults ? JSON.stringify(req.guardrailResults) : null, req.traceId ?? null, req.spanId ?? null, req.parentSpanId ?? null, req.sessionId ?? null, now, now).first();
61
+ },
62
+ listRequests: async (params) => {
63
+ const { limit = 100, offset = 0, configId, variantId, environmentId, providerConfigId, provider, model, startDate, endDate, tags } = params ?? {};
64
+ const conditions = ["1=1"];
65
+ const queryParams = [];
66
+ if (configId) {
67
+ conditions.push(`"configId" = ?`);
68
+ queryParams.push(configId);
69
+ }
70
+ if (variantId) {
71
+ conditions.push(`"variantId" = ?`);
72
+ queryParams.push(variantId);
73
+ }
74
+ if (environmentId) {
75
+ conditions.push(`"environmentId" = ?`);
76
+ queryParams.push(environmentId);
77
+ }
78
+ if (providerConfigId) {
79
+ conditions.push(`"providerConfigId" = ?`);
80
+ queryParams.push(providerConfigId);
81
+ }
82
+ if (provider) {
83
+ conditions.push(`"provider" = ?`);
84
+ queryParams.push(provider);
85
+ }
86
+ if (model) {
87
+ conditions.push(`"model" = ?`);
88
+ queryParams.push(model);
89
+ }
90
+ if (startDate) {
91
+ conditions.push(`"createdAt" >= ?`);
92
+ queryParams.push(startDate.toISOString());
93
+ }
94
+ if (endDate) {
95
+ conditions.push(`"createdAt" <= ?`);
96
+ queryParams.push(endDate.toISOString());
97
+ }
98
+ const tagFilter = buildTagFilters(tags);
99
+ conditions.push(...tagFilter.conditions);
100
+ queryParams.push(...tagFilter.params);
101
+ const where = conditions.join(" AND ");
102
+ const total = (await db.prepare(`SELECT COUNT(*) AS "total" FROM "llm_requests" WHERE ${where}`).bind(...queryParams).first())?.total ?? 0;
103
+ const { results: data } = await db.prepare(`SELECT * FROM "llm_requests" WHERE ${where} ORDER BY "createdAt" DESC LIMIT ? OFFSET ?`).bind(...queryParams, limit, offset).all();
104
+ return {
105
+ data,
106
+ total,
107
+ limit,
108
+ offset
109
+ };
110
+ },
111
+ getRequestByRequestId: async (requestId) => {
112
+ return db.prepare(`SELECT * FROM "llm_requests" WHERE "requestId" = ?`).bind(requestId).first();
113
+ },
114
+ getTotalCost: async (params) => {
115
+ const conditions = [`"createdAt" >= ?`, `"createdAt" <= ?`];
116
+ const queryParams = [params.startDate.toISOString(), params.endDate.toISOString()];
117
+ if (params.configId) {
118
+ conditions.push(`"configId" = ?`);
119
+ queryParams.push(params.configId);
120
+ }
121
+ if (params.variantId) {
122
+ conditions.push(`"variantId" = ?`);
123
+ queryParams.push(params.variantId);
124
+ }
125
+ if (params.environmentId) {
126
+ conditions.push(`"environmentId" = ?`);
127
+ queryParams.push(params.environmentId);
128
+ }
129
+ const tagFilter = buildTagFilters(params.tags);
130
+ conditions.push(...tagFilter.conditions);
131
+ queryParams.push(...tagFilter.params);
132
+ const where = conditions.join(" AND ");
133
+ return db.prepare(`
134
+ SELECT
135
+ COALESCE(SUM("cost"), 0) AS "totalCost",
136
+ COALESCE(SUM("inputCost"), 0) AS "totalInputCost",
137
+ COALESCE(SUM("outputCost"), 0) AS "totalOutputCost",
138
+ COALESCE(SUM("promptTokens"), 0) AS "totalPromptTokens",
139
+ COALESCE(SUM("completionTokens"), 0) AS "totalCompletionTokens",
140
+ COALESCE(SUM("totalTokens"), 0) AS "totalTokens",
141
+ COALESCE(SUM("cachedTokens"), 0) AS "totalCachedTokens",
142
+ COALESCE(SUM("cacheSavings"), 0) AS "totalCacheSavings",
143
+ COUNT(*) AS "requestCount"
144
+ FROM "llm_requests" WHERE ${where}
145
+ `).bind(...queryParams).first();
146
+ },
147
+ getCostByModel: async (params) => {
148
+ const { results } = await db.prepare(`
149
+ SELECT "provider", "model",
150
+ COALESCE(SUM("cost"), 0) AS "totalCost",
151
+ COALESCE(SUM("inputCost"), 0) AS "totalInputCost",
152
+ COALESCE(SUM("outputCost"), 0) AS "totalOutputCost",
153
+ COALESCE(SUM("totalTokens"), 0) AS "totalTokens",
154
+ COUNT(*) AS "requestCount",
155
+ AVG("latencyMs") AS "avgLatencyMs"
156
+ FROM "llm_requests"
157
+ WHERE "createdAt" >= ? AND "createdAt" <= ?
158
+ GROUP BY "provider", "model"
159
+ ORDER BY SUM("cost") DESC
160
+ `).bind(params.startDate.toISOString(), params.endDate.toISOString()).all();
161
+ return results;
162
+ },
163
+ getCostByProvider: async (params) => {
164
+ const { results } = await db.prepare(`
165
+ SELECT "provider",
166
+ COALESCE(SUM("cost"), 0) AS "totalCost",
167
+ COALESCE(SUM("inputCost"), 0) AS "totalInputCost",
168
+ COALESCE(SUM("outputCost"), 0) AS "totalOutputCost",
169
+ COALESCE(SUM("totalTokens"), 0) AS "totalTokens",
170
+ COUNT(*) AS "requestCount",
171
+ AVG("latencyMs") AS "avgLatencyMs"
172
+ FROM "llm_requests"
173
+ WHERE "createdAt" >= ? AND "createdAt" <= ?
174
+ GROUP BY "provider"
175
+ ORDER BY SUM("cost") DESC
176
+ `).bind(params.startDate.toISOString(), params.endDate.toISOString()).all();
177
+ return results;
178
+ },
179
+ getDailyCosts: async (params) => {
180
+ const { results } = await db.prepare(`
181
+ SELECT date("createdAt") AS "date",
182
+ COALESCE(SUM("cost"), 0) AS "totalCost",
183
+ COALESCE(SUM("inputCost"), 0) AS "totalInputCost",
184
+ COALESCE(SUM("outputCost"), 0) AS "totalOutputCost",
185
+ COALESCE(SUM("totalTokens"), 0) AS "totalTokens",
186
+ COUNT(*) AS "requestCount"
187
+ FROM "llm_requests"
188
+ WHERE "createdAt" >= ? AND "createdAt" <= ?
189
+ GROUP BY date("createdAt")
190
+ ORDER BY date("createdAt") ASC
191
+ `).bind(params.startDate.toISOString(), params.endDate.toISOString()).all();
192
+ return results;
193
+ },
194
+ getCostSummary: async (params) => {
195
+ const { startDate, endDate, groupBy, configId, variantId, environmentId, tags, tagKeys } = params;
196
+ const conditions = [`"createdAt" >= ?`, `"createdAt" <= ?`];
197
+ const queryParams = [startDate.toISOString(), endDate.toISOString()];
198
+ if (configId) {
199
+ conditions.push(`"configId" = ?`);
200
+ queryParams.push(configId);
201
+ }
202
+ if (variantId) {
203
+ conditions.push(`"variantId" = ?`);
204
+ queryParams.push(variantId);
205
+ }
206
+ if (environmentId) {
207
+ conditions.push(`"environmentId" = ?`);
208
+ queryParams.push(environmentId);
209
+ }
210
+ const tagFilter = buildTagFilters(tags);
211
+ conditions.push(...tagFilter.conditions);
212
+ queryParams.push(...tagFilter.params);
213
+ const where = conditions.join(" AND ");
214
+ if (groupBy === "tags") {
215
+ const tagConditions = [...conditions];
216
+ const tagParams = [...queryParams];
217
+ if (tagKeys && tagKeys.length > 0) {
218
+ tagConditions.push(`json_each.key IN (${tagKeys.map(() => "?").join(",")})`);
219
+ tagParams.push(...tagKeys);
220
+ }
221
+ const tagWhere = tagConditions.join(" AND ");
222
+ const { results: results$1 } = await db.prepare(`
223
+ SELECT json_each.key || ':' || json_each.value AS "groupKey",
224
+ COALESCE(SUM("cost"), 0) AS "totalCost",
225
+ COUNT(*) AS "requestCount"
226
+ FROM "llm_requests", json_each("tags")
227
+ WHERE ${tagWhere}
228
+ GROUP BY json_each.key, json_each.value
229
+ ORDER BY SUM("cost") DESC
230
+ `).bind(...tagParams).all();
231
+ return results$1;
232
+ }
233
+ const sqlMap = {
234
+ day: `SELECT date("createdAt") AS "groupKey", COALESCE(SUM("cost"),0) AS "totalCost", COUNT(*) AS "requestCount", COALESCE(SUM("totalTokens"),0) AS "totalTokens" FROM "llm_requests" WHERE ${where} GROUP BY date("createdAt") ORDER BY date("createdAt") ASC`,
235
+ hour: `SELECT strftime('%Y-%m-%d %H:00:00',"createdAt") AS "groupKey", COALESCE(SUM("cost"),0) AS "totalCost", COUNT(*) AS "requestCount", COALESCE(SUM("totalTokens"),0) AS "totalTokens" FROM "llm_requests" WHERE ${where} GROUP BY strftime('%Y-%m-%d %H:00:00',"createdAt") ORDER BY strftime('%Y-%m-%d %H:00:00',"createdAt") ASC`,
236
+ model: `SELECT "provider"||'/'||"model" AS "groupKey", COALESCE(SUM("cost"),0) AS "totalCost", COUNT(*) AS "requestCount" FROM "llm_requests" WHERE ${where} GROUP BY "provider","model" ORDER BY SUM("cost") DESC`,
237
+ provider: `SELECT "provider" AS "groupKey", COALESCE(SUM("cost"),0) AS "totalCost", COUNT(*) AS "requestCount" FROM "llm_requests" WHERE ${where} GROUP BY "provider" ORDER BY SUM("cost") DESC`,
238
+ endpoint: `SELECT COALESCE("endpoint",'unknown') AS "groupKey", COALESCE(SUM("cost"),0) AS "totalCost", COUNT(*) AS "requestCount" FROM "llm_requests" WHERE ${where} GROUP BY "endpoint" ORDER BY SUM("cost") DESC`
239
+ };
240
+ const totalSql = `SELECT 'total' AS "groupKey", COALESCE(SUM("cost"),0) AS "totalCost", COUNT(*) AS "requestCount" FROM "llm_requests" WHERE ${where}`;
241
+ const sql = groupBy ? sqlMap[groupBy] ?? totalSql : totalSql;
242
+ const { results } = await db.prepare(sql).bind(...queryParams).all();
243
+ return results;
244
+ },
245
+ getRequestStats: async (params) => {
246
+ const conditions = [`"createdAt" >= ?`, `"createdAt" <= ?`];
247
+ const queryParams = [params.startDate.toISOString(), params.endDate.toISOString()];
248
+ if (params.configId) {
249
+ conditions.push(`"configId" = ?`);
250
+ queryParams.push(params.configId);
251
+ }
252
+ if (params.variantId) {
253
+ conditions.push(`"variantId" = ?`);
254
+ queryParams.push(params.variantId);
255
+ }
256
+ if (params.environmentId) {
257
+ conditions.push(`"environmentId" = ?`);
258
+ queryParams.push(params.environmentId);
259
+ }
260
+ const tagFilter = buildTagFilters(params.tags);
261
+ conditions.push(...tagFilter.conditions);
262
+ queryParams.push(...tagFilter.params);
263
+ const where = conditions.join(" AND ");
264
+ return db.prepare(`
265
+ SELECT
266
+ COUNT(*) AS "totalRequests",
267
+ COUNT(CASE WHEN "statusCode">=200 AND "statusCode"<300 THEN 1 END) AS "successfulRequests",
268
+ COUNT(CASE WHEN "statusCode">=400 THEN 1 END) AS "failedRequests",
269
+ COUNT(CASE WHEN "isStreaming"=1 THEN 1 END) AS "streamingRequests",
270
+ AVG("latencyMs") AS "avgLatencyMs",
271
+ MAX("latencyMs") AS "maxLatencyMs",
272
+ MIN("latencyMs") AS "minLatencyMs"
273
+ FROM "llm_requests" WHERE ${where}
274
+ `).bind(...queryParams).first();
275
+ },
276
+ getDistinctTags: async () => {
277
+ const { results } = await db.prepare(`
278
+ SELECT DISTINCT json_each.key AS key, json_each.value AS value
279
+ FROM "llm_requests", json_each("tags")
280
+ WHERE "tags" != '{}'
281
+ ORDER BY json_each.key, json_each.value
282
+ `).all();
283
+ return results;
284
+ }
285
+ };
286
+ }
287
+ function createD1TracesStore(db) {
288
+ return {
289
+ upsertTrace: async (data) => {
290
+ const now = (/* @__PURE__ */ new Date()).toISOString();
291
+ await db.prepare(`
292
+ INSERT INTO "traces" (
293
+ "id","traceId","name","sessionId","userId","status",
294
+ "startTime","endTime","durationMs","spanCount",
295
+ "totalInputTokens","totalOutputTokens","totalTokens","totalCost",
296
+ "tags","metadata","createdAt","updatedAt"
297
+ ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
298
+ ON CONFLICT ("traceId") DO UPDATE SET
299
+ "name" = COALESCE(EXCLUDED."name", "traces"."name"),
300
+ "sessionId" = COALESCE(EXCLUDED."sessionId", "traces"."sessionId"),
301
+ "userId" = COALESCE(EXCLUDED."userId", "traces"."userId"),
302
+ "status" = CASE
303
+ WHEN EXCLUDED."status" = 'error' THEN 'error'
304
+ WHEN EXCLUDED."status" = 'ok' AND "traces"."status" != 'error' THEN 'ok'
305
+ ELSE "traces"."status"
306
+ END,
307
+ "startTime" = MIN("traces"."startTime", EXCLUDED."startTime"),
308
+ "endTime" = MAX(
309
+ COALESCE("traces"."endTime", EXCLUDED."endTime"),
310
+ COALESCE(EXCLUDED."endTime", "traces"."endTime")
311
+ ),
312
+ "durationMs" = CAST(
313
+ (julianday(MAX(
314
+ COALESCE("traces"."endTime", EXCLUDED."endTime"),
315
+ COALESCE(EXCLUDED."endTime", "traces"."endTime")
316
+ )) - julianday(MIN("traces"."startTime", EXCLUDED."startTime")))
317
+ * 86400000 AS INTEGER
318
+ ),
319
+ "spanCount" = "traces"."spanCount" + EXCLUDED."spanCount",
320
+ "totalInputTokens" = "traces"."totalInputTokens" + EXCLUDED."totalInputTokens",
321
+ "totalOutputTokens" = "traces"."totalOutputTokens" + EXCLUDED."totalOutputTokens",
322
+ "totalTokens" = "traces"."totalTokens" + EXCLUDED."totalTokens",
323
+ "totalCost" = "traces"."totalCost" + EXCLUDED."totalCost",
324
+ "tags" = json_patch("traces"."tags", EXCLUDED."tags"),
325
+ "metadata" = json_patch("traces"."metadata", EXCLUDED."metadata"),
326
+ "updatedAt" = ?
327
+ `).bind((0, node_crypto.randomUUID)(), data.traceId, data.name ?? null, data.sessionId ?? null, data.userId ?? null, data.status ?? "unset", data.startTime.toISOString(), data.endTime?.toISOString() ?? null, data.durationMs ?? null, data.spanCount ?? 1, data.totalInputTokens ?? 0, data.totalOutputTokens ?? 0, data.totalTokens ?? 0, data.totalCost ?? 0, JSON.stringify(data.tags ?? {}), JSON.stringify(data.metadata ?? {}), now, now, now).run();
328
+ },
329
+ batchInsertSpans: async (spans) => {
330
+ if (spans.length === 0) return { count: 0 };
331
+ const now = (/* @__PURE__ */ new Date()).toISOString();
332
+ const stmts = spans.map((s) => db.prepare(`
333
+ INSERT OR IGNORE INTO "spans" (
334
+ "id","traceId","spanId","parentSpanId","name","kind",
335
+ "status","statusMessage","startTime","endTime","durationMs",
336
+ "provider","model","promptTokens","completionTokens",
337
+ "totalTokens","cost","configId","variantId","environmentId",
338
+ "providerConfigId","requestId","source","input","output",
339
+ "attributes","createdAt","updatedAt"
340
+ ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
341
+ `).bind((0, node_crypto.randomUUID)(), s.traceId, s.spanId, s.parentSpanId ?? null, s.name, s.kind ?? 1, s.status ?? 0, s.statusMessage ?? null, s.startTime.toISOString(), s.endTime?.toISOString() ?? null, s.durationMs ?? null, s.provider ?? null, s.model ?? null, s.promptTokens ?? 0, s.completionTokens ?? 0, s.totalTokens ?? 0, s.cost ?? 0, s.configId ?? null, s.variantId ?? null, s.environmentId ?? null, s.providerConfigId ?? null, s.requestId ?? null, s.source ?? "gateway", s.input != null ? JSON.stringify(s.input) : null, s.output != null ? JSON.stringify(s.output) : null, JSON.stringify(s.attributes ?? {}), now, now));
342
+ for (let i = 0; i < stmts.length; i += D1_BATCH_LIMIT) await db.batch(stmts.slice(i, i + D1_BATCH_LIMIT));
343
+ return { count: spans.length };
344
+ },
345
+ batchInsertSpanEvents: async (events) => {
346
+ if (events.length === 0) return { count: 0 };
347
+ const now = (/* @__PURE__ */ new Date()).toISOString();
348
+ const stmts = events.map((e) => db.prepare(`
349
+ INSERT INTO "span_events" (
350
+ "id","traceId","spanId","name","timestamp","attributes","createdAt"
351
+ ) VALUES (?,?,?,?,?,?,?)
352
+ `).bind((0, node_crypto.randomUUID)(), e.traceId, e.spanId, e.name, e.timestamp.toISOString(), JSON.stringify(e.attributes ?? {}), now));
353
+ for (let i = 0; i < stmts.length; i += D1_BATCH_LIMIT) await db.batch(stmts.slice(i, i + D1_BATCH_LIMIT));
354
+ return { count: events.length };
355
+ },
356
+ listTraces: async (params) => {
357
+ const { limit = 50, offset = 0, sessionId, userId, status, name, startDate, endDate, tags } = params ?? {};
358
+ const conditions = ["1=1"];
359
+ const queryParams = [];
360
+ if (sessionId) {
361
+ conditions.push(`"sessionId" = ?`);
362
+ queryParams.push(sessionId);
363
+ }
364
+ if (userId) {
365
+ conditions.push(`"userId" = ?`);
366
+ queryParams.push(userId);
367
+ }
368
+ if (status) {
369
+ conditions.push(`"status" = ?`);
370
+ queryParams.push(status);
371
+ }
372
+ if (name) {
373
+ conditions.push(`"name" LIKE ?`);
374
+ queryParams.push(`%${name}%`);
375
+ }
376
+ if (startDate) {
377
+ conditions.push(`"startTime" >= ?`);
378
+ queryParams.push(startDate.toISOString());
379
+ }
380
+ if (endDate) {
381
+ conditions.push(`"startTime" <= ?`);
382
+ queryParams.push(endDate.toISOString());
383
+ }
384
+ const tagFilter = buildTagFilters(tags);
385
+ conditions.push(...tagFilter.conditions);
386
+ queryParams.push(...tagFilter.params);
387
+ const where = conditions.join(" AND ");
388
+ const total = (await db.prepare(`SELECT COUNT(*) AS "total" FROM "traces" WHERE ${where}`).bind(...queryParams).first())?.total ?? 0;
389
+ const { results: data } = await db.prepare(`SELECT * FROM "traces" WHERE ${where} ORDER BY "startTime" DESC LIMIT ? OFFSET ?`).bind(...queryParams, limit, offset).all();
390
+ return {
391
+ data,
392
+ total,
393
+ limit,
394
+ offset
395
+ };
396
+ },
397
+ getTraceWithSpans: async (traceId) => {
398
+ const trace = await db.prepare(`SELECT * FROM "traces" WHERE "traceId" = ?`).bind(traceId).first();
399
+ if (!trace) return void 0;
400
+ const [spanResult, eventResult] = await db.batch([db.prepare(`SELECT * FROM "spans" WHERE "traceId" = ? ORDER BY "startTime" ASC`).bind(traceId), db.prepare(`SELECT * FROM "span_events" WHERE "traceId" = ? ORDER BY "timestamp" ASC`).bind(traceId)]);
401
+ return {
402
+ trace,
403
+ spans: spanResult.results ?? [],
404
+ events: eventResult.results ?? []
405
+ };
406
+ },
407
+ getTraceStats: async (params) => {
408
+ const conditions = [`"startTime" >= ?`, `"startTime" <= ?`];
409
+ const queryParams = [params.startDate.toISOString(), params.endDate.toISOString()];
410
+ if (params.sessionId) {
411
+ conditions.push(`"sessionId" = ?`);
412
+ queryParams.push(params.sessionId);
413
+ }
414
+ if (params.userId) {
415
+ conditions.push(`"userId" = ?`);
416
+ queryParams.push(params.userId);
417
+ }
418
+ const where = conditions.join(" AND ");
419
+ return db.prepare(`
420
+ SELECT
421
+ COUNT(*) AS "totalTraces",
422
+ COALESCE(AVG("durationMs"), 0) AS "avgDurationMs",
423
+ COUNT(CASE WHEN "status" = 'error' THEN 1 END) AS "errorCount",
424
+ COALESCE(SUM("totalCost"), 0) AS "totalCost",
425
+ COALESCE(SUM("totalTokens"), 0) AS "totalTokens",
426
+ COALESCE(SUM("spanCount"), 0) AS "totalSpans"
427
+ FROM "traces" WHERE ${where}
428
+ `).bind(...queryParams).first();
429
+ }
430
+ };
431
+ }
432
+ /**
433
+ * Create a Cloudflare D1-backed telemetry store.
434
+ *
435
+ * Usage:
436
+ * ```ts
437
+ * import { d1Store } from '@llmops/sdk/store/d1'
438
+ *
439
+ * export default {
440
+ * async fetch(request, env) {
441
+ * const ops = llmops({
442
+ * telemetry: d1Store(env.DB),
443
+ * })
444
+ * }
445
+ * }
446
+ * ```
447
+ */
448
+ function createD1Store(db) {
449
+ return {
450
+ ...createD1LLMRequestsStore(db),
451
+ ...createD1TracesStore(db),
452
+ _db: db
453
+ };
454
+ }
455
+
456
+ //#endregion
457
+ //#region src/store/d1/migrations/000001_e44e1c4f.sql
458
+ var _000001_e44e1c4f_default = "CREATE TABLE IF NOT EXISTS llm_requests (\n id TEXT PRIMARY KEY,\n \"requestId\" TEXT NOT NULL,\n \"configId\" TEXT,\n \"variantId\" TEXT,\n \"environmentId\" TEXT,\n \"providerConfigId\" TEXT,\n provider TEXT NOT NULL,\n model TEXT NOT NULL,\n \"promptTokens\" INTEGER NOT NULL DEFAULT 0,\n \"completionTokens\" INTEGER NOT NULL DEFAULT 0,\n \"totalTokens\" INTEGER NOT NULL DEFAULT 0,\n \"cachedTokens\" INTEGER NOT NULL DEFAULT 0,\n \"cacheCreationTokens\" INTEGER NOT NULL DEFAULT 0,\n cost INTEGER NOT NULL DEFAULT 0,\n \"cacheSavings\" INTEGER NOT NULL DEFAULT 0,\n \"inputCost\" INTEGER NOT NULL DEFAULT 0,\n \"outputCost\" INTEGER NOT NULL DEFAULT 0,\n endpoint TEXT NOT NULL,\n \"statusCode\" INTEGER NOT NULL,\n \"latencyMs\" INTEGER NOT NULL DEFAULT 0,\n \"isStreaming\" INTEGER NOT NULL DEFAULT 0,\n \"userId\" TEXT,\n tags TEXT NOT NULL DEFAULT '{}',\n \"guardrailResults\" TEXT,\n \"traceId\" TEXT,\n \"spanId\" TEXT,\n \"parentSpanId\" TEXT,\n \"sessionId\" TEXT,\n \"createdAt\" TEXT NOT NULL DEFAULT (datetime('now')),\n \"updatedAt\" TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\nCREATE TABLE IF NOT EXISTS span_events (\n id TEXT PRIMARY KEY,\n \"traceId\" TEXT NOT NULL,\n \"spanId\" TEXT NOT NULL,\n name TEXT NOT NULL,\n timestamp TEXT NOT NULL,\n attributes TEXT NOT NULL DEFAULT '{}',\n \"createdAt\" TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\nCREATE TABLE IF NOT EXISTS spans (\n id TEXT PRIMARY KEY,\n \"traceId\" TEXT NOT NULL,\n \"spanId\" TEXT NOT NULL UNIQUE,\n \"parentSpanId\" TEXT,\n name TEXT NOT NULL,\n kind INTEGER NOT NULL DEFAULT 1,\n status INTEGER NOT NULL DEFAULT 0,\n \"statusMessage\" TEXT,\n \"startTime\" TEXT NOT NULL,\n \"endTime\" TEXT,\n \"durationMs\" INTEGER,\n provider TEXT,\n model TEXT,\n \"promptTokens\" INTEGER NOT NULL DEFAULT 0,\n \"completionTokens\" INTEGER NOT NULL DEFAULT 0,\n \"totalTokens\" INTEGER NOT NULL DEFAULT 0,\n cost INTEGER NOT NULL DEFAULT 0,\n \"configId\" TEXT,\n \"variantId\" TEXT,\n \"environmentId\" TEXT,\n \"providerConfigId\" TEXT,\n \"requestId\" TEXT,\n source TEXT NOT NULL DEFAULT 'gateway',\n input TEXT,\n output TEXT,\n attributes TEXT NOT NULL DEFAULT '{}',\n \"createdAt\" TEXT NOT NULL DEFAULT (datetime('now')),\n \"updatedAt\" TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\nCREATE TABLE IF NOT EXISTS traces (\n id TEXT PRIMARY KEY,\n \"traceId\" TEXT NOT NULL UNIQUE,\n name TEXT,\n \"sessionId\" TEXT,\n \"userId\" TEXT,\n status TEXT NOT NULL DEFAULT 'unset',\n \"startTime\" TEXT NOT NULL,\n \"endTime\" TEXT,\n \"durationMs\" INTEGER,\n \"spanCount\" INTEGER NOT NULL DEFAULT 0,\n \"totalInputTokens\" INTEGER NOT NULL DEFAULT 0,\n \"totalOutputTokens\" INTEGER NOT NULL DEFAULT 0,\n \"totalTokens\" INTEGER NOT NULL DEFAULT 0,\n \"totalCost\" INTEGER NOT NULL DEFAULT 0,\n tags TEXT NOT NULL DEFAULT '{}',\n metadata TEXT NOT NULL DEFAULT '{}',\n \"createdAt\" TEXT NOT NULL DEFAULT (datetime('now')),\n \"updatedAt\" TEXT NOT NULL DEFAULT (datetime('now'))\n);\n";
459
+
460
+ //#endregion
461
+ //#region src/store/d1/migrations/index.ts
462
+ const migrations = [["000001_e44e1c4f", _000001_e44e1c4f_default]];
463
+
464
+ //#endregion
465
+ //#region src/store/d1/migrate.ts
466
+ /**
467
+ * Run pending migrations against a D1 database.
468
+ */
469
+ async function runD1Migrations(db) {
470
+ await db.prepare(`
471
+ CREATE TABLE IF NOT EXISTS _llmops_migrations (
472
+ name TEXT PRIMARY KEY,
473
+ applied_at TEXT NOT NULL DEFAULT (datetime('now'))
474
+ )
475
+ `).run();
476
+ const { results } = await db.prepare("SELECT name FROM _llmops_migrations ORDER BY name").all();
477
+ const applied = new Set(results.map((r) => r.name));
478
+ const newlyApplied = [];
479
+ for (const [name, sql] of migrations) {
480
+ if (applied.has(name)) continue;
481
+ const statements = sql.split(";").map((s) => s.trim()).filter(Boolean).map((s) => db.prepare(s));
482
+ statements.push(db.prepare("INSERT INTO _llmops_migrations (name) VALUES (?)").bind(name));
483
+ await db.batch(statements);
484
+ newlyApplied.push(name);
485
+ }
486
+ return { applied: newlyApplied };
487
+ }
488
+
489
+ //#endregion
490
+ exports.d1Store = createD1Store;
491
+ exports.runD1Migrations = runD1Migrations;
@@ -0,0 +1,12 @@
1
+ import "../interface-DMuF7YgM.cjs";
2
+ import { n as createD1Store, r as D1Database, t as D1Store } from "../d1-store-D664VLg0.cjs";
3
+
4
+ //#region src/store/d1/migrate.d.ts
5
+ /**
6
+ * Run pending migrations against a D1 database.
7
+ */
8
+ declare function runD1Migrations(db: D1Database): Promise<{
9
+ applied: string[];
10
+ }>;
11
+ //#endregion
12
+ export { type D1Database, type D1Store, createD1Store as d1Store, runD1Migrations };
@@ -0,0 +1,12 @@
1
+ import "../interface-BrJMazBg.mjs";
2
+ import { n as createD1Store, r as D1Database, t as D1Store } from "../d1-store-DBf99oU8.mjs";
3
+
4
+ //#region src/store/d1/migrate.d.ts
5
+ /**
6
+ * Run pending migrations against a D1 database.
7
+ */
8
+ declare function runD1Migrations(db: D1Database): Promise<{
9
+ applied: string[];
10
+ }>;
11
+ //#endregion
12
+ export { type D1Database, type D1Store, createD1Store as d1Store, runD1Migrations };