@llmops/sdk 1.0.0-beta.7 → 1.0.0-beta.8
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/{d1-store-D664VLg0.d.cts → d1-store-DC1FMAzk.d.mts} +1 -1
- package/dist/{d1-store-DBf99oU8.d.mts → d1-store-Pbuwjx6D.d.cts} +1 -1
- package/dist/index.cjs +3 -4
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{interface-BrJMazBg.d.mts → interface-CNLmcugg.d.mts} +3 -22
- package/dist/{interface-DMuF7YgM.d.cts → interface-SBcQ5H-b.d.cts} +3 -22
- package/dist/pg-store-CQMQHDtm.d.cts +25 -0
- package/dist/pg-store-Dr0F1Jq4.d.mts +25 -0
- package/dist/store/d1.d.cts +2 -2
- package/dist/store/d1.d.mts +2 -2
- package/dist/store/pg.cjs +613 -2
- package/dist/store/pg.d.cts +2 -1
- package/dist/store/pg.d.mts +2 -1
- package/dist/store/pg.mjs +612 -1
- package/dist/{pg-store-sIMdF_Pc.mjs → types-BerUVJ45.mjs} +2 -610
- package/dist/{pg-store-uawkO2hJ.cjs → types-uIsMdtuF.cjs} +27 -611
- package/dist/types.d.cts +3 -2
- package/dist/types.d.mts +3 -2
- package/package.json +3 -3
package/dist/store/pg.cjs
CHANGED
|
@@ -1,5 +1,616 @@
|
|
|
1
|
-
const
|
|
1
|
+
const require_types = require('../types-uIsMdtuF.cjs');
|
|
2
|
+
let __llmops_core = require("@llmops/core");
|
|
3
|
+
let node_crypto = require("node:crypto");
|
|
2
4
|
|
|
5
|
+
//#region src/telemetry/pg-store.ts
|
|
6
|
+
function buildTagFilters(tags, paramOffset) {
|
|
7
|
+
const conditions = [];
|
|
8
|
+
const params = [];
|
|
9
|
+
if (!tags) return {
|
|
10
|
+
conditions,
|
|
11
|
+
params
|
|
12
|
+
};
|
|
13
|
+
for (const [key, values] of Object.entries(tags)) {
|
|
14
|
+
if (values.length === 0) continue;
|
|
15
|
+
if (values.length === 1) {
|
|
16
|
+
conditions.push(`"tags"->>'${key}' = $${paramOffset + params.length + 1}`);
|
|
17
|
+
params.push(values[0]);
|
|
18
|
+
} else {
|
|
19
|
+
const placeholders = values.map((_, i) => `$${paramOffset + params.length + i + 1}`).join(", ");
|
|
20
|
+
conditions.push(`"tags"->>'${key}' IN (${placeholders})`);
|
|
21
|
+
params.push(...values);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
conditions,
|
|
26
|
+
params
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function createLLMRequestsStore(pool) {
|
|
30
|
+
return {
|
|
31
|
+
batchInsertRequests: async (requests) => {
|
|
32
|
+
if (requests.length === 0) return { count: 0 };
|
|
33
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
34
|
+
const columns = [
|
|
35
|
+
"id",
|
|
36
|
+
"requestId",
|
|
37
|
+
"configId",
|
|
38
|
+
"variantId",
|
|
39
|
+
"environmentId",
|
|
40
|
+
"providerConfigId",
|
|
41
|
+
"provider",
|
|
42
|
+
"model",
|
|
43
|
+
"promptTokens",
|
|
44
|
+
"completionTokens",
|
|
45
|
+
"totalTokens",
|
|
46
|
+
"cachedTokens",
|
|
47
|
+
"cacheCreationTokens",
|
|
48
|
+
"cost",
|
|
49
|
+
"cacheSavings",
|
|
50
|
+
"inputCost",
|
|
51
|
+
"outputCost",
|
|
52
|
+
"endpoint",
|
|
53
|
+
"statusCode",
|
|
54
|
+
"latencyMs",
|
|
55
|
+
"isStreaming",
|
|
56
|
+
"userId",
|
|
57
|
+
"tags",
|
|
58
|
+
"guardrailResults",
|
|
59
|
+
"traceId",
|
|
60
|
+
"spanId",
|
|
61
|
+
"parentSpanId",
|
|
62
|
+
"sessionId",
|
|
63
|
+
"createdAt",
|
|
64
|
+
"updatedAt"
|
|
65
|
+
];
|
|
66
|
+
const colNames = columns.map((c) => `"${c}"`).join(", ");
|
|
67
|
+
const params = [];
|
|
68
|
+
const valueRows = [];
|
|
69
|
+
for (const req of requests) {
|
|
70
|
+
const result = require_types.insertLLMRequestSchema.safeParse(req);
|
|
71
|
+
if (!result.success) {
|
|
72
|
+
__llmops_core.logger.warn(`[batchInsertRequests] Skipping invalid request: ${result.error.message}`);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const r = result.data;
|
|
76
|
+
const offset = params.length;
|
|
77
|
+
const placeholders = columns.map((_, i) => `$${offset + i + 1}`).join(", ");
|
|
78
|
+
valueRows.push(`(${placeholders})`);
|
|
79
|
+
params.push((0, node_crypto.randomUUID)(), r.requestId, r.configId ?? null, r.variantId ?? null, r.environmentId ?? null, r.providerConfigId ?? null, r.provider, r.model, r.promptTokens, r.completionTokens, r.totalTokens, r.cachedTokens, r.cacheCreationTokens, r.cost, r.cacheSavings, r.inputCost, r.outputCost, r.endpoint, r.statusCode, r.latencyMs, r.isStreaming, r.userId ?? null, JSON.stringify(r.tags), r.guardrailResults ? JSON.stringify(r.guardrailResults) : null, r.traceId ?? null, r.spanId ?? null, r.parentSpanId ?? null, r.sessionId ?? null, now, now);
|
|
80
|
+
}
|
|
81
|
+
if (valueRows.length === 0) return { count: 0 };
|
|
82
|
+
await pool.query(`INSERT INTO "llm_requests" (${colNames}) VALUES ${valueRows.join(", ")}`, params);
|
|
83
|
+
return { count: valueRows.length };
|
|
84
|
+
},
|
|
85
|
+
insertRequest: async (request) => {
|
|
86
|
+
const result = require_types.insertLLMRequestSchema.safeParse(request);
|
|
87
|
+
if (!result.success) throw new Error(`Invalid request data: ${result.error.message}`);
|
|
88
|
+
const r = result.data;
|
|
89
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
90
|
+
const { rows } = await pool.query(`INSERT INTO "llm_requests" (
|
|
91
|
+
"id", "requestId", "configId", "variantId", "environmentId",
|
|
92
|
+
"providerConfigId", "provider", "model", "promptTokens",
|
|
93
|
+
"completionTokens", "totalTokens", "cachedTokens",
|
|
94
|
+
"cacheCreationTokens", "cost", "cacheSavings", "inputCost",
|
|
95
|
+
"outputCost", "endpoint", "statusCode", "latencyMs", "isStreaming",
|
|
96
|
+
"userId", "tags", "guardrailResults", "traceId", "spanId",
|
|
97
|
+
"parentSpanId", "sessionId", "createdAt", "updatedAt"
|
|
98
|
+
) VALUES (
|
|
99
|
+
$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,
|
|
100
|
+
$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30
|
|
101
|
+
) RETURNING *`, [
|
|
102
|
+
(0, node_crypto.randomUUID)(),
|
|
103
|
+
r.requestId,
|
|
104
|
+
r.configId ?? null,
|
|
105
|
+
r.variantId ?? null,
|
|
106
|
+
r.environmentId ?? null,
|
|
107
|
+
r.providerConfigId ?? null,
|
|
108
|
+
r.provider,
|
|
109
|
+
r.model,
|
|
110
|
+
r.promptTokens,
|
|
111
|
+
r.completionTokens,
|
|
112
|
+
r.totalTokens,
|
|
113
|
+
r.cachedTokens,
|
|
114
|
+
r.cacheCreationTokens,
|
|
115
|
+
r.cost,
|
|
116
|
+
r.cacheSavings,
|
|
117
|
+
r.inputCost,
|
|
118
|
+
r.outputCost,
|
|
119
|
+
r.endpoint,
|
|
120
|
+
r.statusCode,
|
|
121
|
+
r.latencyMs,
|
|
122
|
+
r.isStreaming,
|
|
123
|
+
r.userId ?? null,
|
|
124
|
+
JSON.stringify(r.tags),
|
|
125
|
+
r.guardrailResults ? JSON.stringify(r.guardrailResults) : null,
|
|
126
|
+
r.traceId ?? null,
|
|
127
|
+
r.spanId ?? null,
|
|
128
|
+
r.parentSpanId ?? null,
|
|
129
|
+
r.sessionId ?? null,
|
|
130
|
+
now,
|
|
131
|
+
now
|
|
132
|
+
]);
|
|
133
|
+
return rows[0] ?? null;
|
|
134
|
+
},
|
|
135
|
+
listRequests: async (params) => {
|
|
136
|
+
const { limit = 100, offset = 0, configId, variantId, environmentId, providerConfigId, provider, model, startDate, endDate, tags } = params ?? {};
|
|
137
|
+
const conditions = ["TRUE"];
|
|
138
|
+
const queryParams = [];
|
|
139
|
+
let idx = 0;
|
|
140
|
+
if (configId) {
|
|
141
|
+
conditions.push(`"configId" = $${++idx}`);
|
|
142
|
+
queryParams.push(configId);
|
|
143
|
+
}
|
|
144
|
+
if (variantId) {
|
|
145
|
+
conditions.push(`"variantId" = $${++idx}`);
|
|
146
|
+
queryParams.push(variantId);
|
|
147
|
+
}
|
|
148
|
+
if (environmentId) {
|
|
149
|
+
conditions.push(`"environmentId" = $${++idx}`);
|
|
150
|
+
queryParams.push(environmentId);
|
|
151
|
+
}
|
|
152
|
+
if (providerConfigId) {
|
|
153
|
+
conditions.push(`"providerConfigId" = $${++idx}`);
|
|
154
|
+
queryParams.push(providerConfigId);
|
|
155
|
+
}
|
|
156
|
+
if (provider) {
|
|
157
|
+
conditions.push(`"provider" = $${++idx}`);
|
|
158
|
+
queryParams.push(provider);
|
|
159
|
+
}
|
|
160
|
+
if (model) {
|
|
161
|
+
conditions.push(`"model" = $${++idx}`);
|
|
162
|
+
queryParams.push(model);
|
|
163
|
+
}
|
|
164
|
+
if (startDate) {
|
|
165
|
+
conditions.push(`"createdAt" >= $${++idx}`);
|
|
166
|
+
queryParams.push(startDate.toISOString());
|
|
167
|
+
}
|
|
168
|
+
if (endDate) {
|
|
169
|
+
conditions.push(`"createdAt" <= $${++idx}`);
|
|
170
|
+
queryParams.push(endDate.toISOString());
|
|
171
|
+
}
|
|
172
|
+
const tagFilter = buildTagFilters(tags, idx);
|
|
173
|
+
conditions.push(...tagFilter.conditions);
|
|
174
|
+
queryParams.push(...tagFilter.params);
|
|
175
|
+
idx += tagFilter.params.length;
|
|
176
|
+
const where = conditions.join(" AND ");
|
|
177
|
+
const total = (await pool.query(`SELECT COUNT(*)::int AS "total" FROM "llm_requests" WHERE ${where}`, queryParams)).rows[0]?.total ?? 0;
|
|
178
|
+
return {
|
|
179
|
+
data: (await pool.query(`SELECT * FROM "llm_requests" WHERE ${where} ORDER BY "createdAt" DESC LIMIT $${++idx} OFFSET $${++idx}`, [
|
|
180
|
+
...queryParams,
|
|
181
|
+
limit,
|
|
182
|
+
offset
|
|
183
|
+
])).rows,
|
|
184
|
+
total,
|
|
185
|
+
limit,
|
|
186
|
+
offset
|
|
187
|
+
};
|
|
188
|
+
},
|
|
189
|
+
getRequestByRequestId: async (requestId) => {
|
|
190
|
+
const { rows } = await pool.query(`SELECT * FROM "llm_requests" WHERE "requestId" = $1`, [requestId]);
|
|
191
|
+
return rows[0] ?? void 0;
|
|
192
|
+
},
|
|
193
|
+
getTotalCost: async (params) => {
|
|
194
|
+
const { startDate, endDate, configId, variantId, environmentId, tags } = params;
|
|
195
|
+
const conditions = [`"createdAt" >= $1`, `"createdAt" <= $2`];
|
|
196
|
+
const queryParams = [startDate.toISOString(), endDate.toISOString()];
|
|
197
|
+
let idx = 2;
|
|
198
|
+
if (configId) {
|
|
199
|
+
conditions.push(`"configId" = $${++idx}`);
|
|
200
|
+
queryParams.push(configId);
|
|
201
|
+
}
|
|
202
|
+
if (variantId) {
|
|
203
|
+
conditions.push(`"variantId" = $${++idx}`);
|
|
204
|
+
queryParams.push(variantId);
|
|
205
|
+
}
|
|
206
|
+
if (environmentId) {
|
|
207
|
+
conditions.push(`"environmentId" = $${++idx}`);
|
|
208
|
+
queryParams.push(environmentId);
|
|
209
|
+
}
|
|
210
|
+
const tagFilter = buildTagFilters(tags, idx);
|
|
211
|
+
conditions.push(...tagFilter.conditions);
|
|
212
|
+
queryParams.push(...tagFilter.params);
|
|
213
|
+
const where = conditions.join(" AND ");
|
|
214
|
+
const { rows } = await pool.query(`SELECT
|
|
215
|
+
COALESCE(SUM("cost"), 0)::int AS "totalCost",
|
|
216
|
+
COALESCE(SUM("inputCost"), 0)::int AS "totalInputCost",
|
|
217
|
+
COALESCE(SUM("outputCost"), 0)::int AS "totalOutputCost",
|
|
218
|
+
COALESCE(SUM("promptTokens"), 0)::int AS "totalPromptTokens",
|
|
219
|
+
COALESCE(SUM("completionTokens"), 0)::int AS "totalCompletionTokens",
|
|
220
|
+
COALESCE(SUM("totalTokens"), 0)::int AS "totalTokens",
|
|
221
|
+
COALESCE(SUM("cachedTokens"), 0)::int AS "totalCachedTokens",
|
|
222
|
+
COALESCE(SUM("cacheSavings"), 0)::int AS "totalCacheSavings",
|
|
223
|
+
COUNT(*)::int AS "requestCount"
|
|
224
|
+
FROM "llm_requests" WHERE ${where}`, queryParams);
|
|
225
|
+
return rows[0];
|
|
226
|
+
},
|
|
227
|
+
getCostByModel: async (params) => {
|
|
228
|
+
const { rows } = await pool.query(`SELECT "provider", "model",
|
|
229
|
+
COALESCE(SUM("cost"), 0)::int AS "totalCost",
|
|
230
|
+
COALESCE(SUM("inputCost"), 0)::int AS "totalInputCost",
|
|
231
|
+
COALESCE(SUM("outputCost"), 0)::int AS "totalOutputCost",
|
|
232
|
+
COALESCE(SUM("totalTokens"), 0)::int AS "totalTokens",
|
|
233
|
+
COUNT(*)::int AS "requestCount",
|
|
234
|
+
AVG("latencyMs") AS "avgLatencyMs"
|
|
235
|
+
FROM "llm_requests"
|
|
236
|
+
WHERE "createdAt" >= $1 AND "createdAt" <= $2
|
|
237
|
+
GROUP BY "provider", "model"
|
|
238
|
+
ORDER BY SUM("cost") DESC`, [params.startDate.toISOString(), params.endDate.toISOString()]);
|
|
239
|
+
return rows;
|
|
240
|
+
},
|
|
241
|
+
getCostByProvider: async (params) => {
|
|
242
|
+
const { rows } = await pool.query(`SELECT "provider",
|
|
243
|
+
COALESCE(SUM("cost"), 0)::int AS "totalCost",
|
|
244
|
+
COALESCE(SUM("inputCost"), 0)::int AS "totalInputCost",
|
|
245
|
+
COALESCE(SUM("outputCost"), 0)::int AS "totalOutputCost",
|
|
246
|
+
COALESCE(SUM("totalTokens"), 0)::int AS "totalTokens",
|
|
247
|
+
COUNT(*)::int AS "requestCount",
|
|
248
|
+
AVG("latencyMs") AS "avgLatencyMs"
|
|
249
|
+
FROM "llm_requests"
|
|
250
|
+
WHERE "createdAt" >= $1 AND "createdAt" <= $2
|
|
251
|
+
GROUP BY "provider"
|
|
252
|
+
ORDER BY SUM("cost") DESC`, [params.startDate.toISOString(), params.endDate.toISOString()]);
|
|
253
|
+
return rows;
|
|
254
|
+
},
|
|
255
|
+
getDailyCosts: async (params) => {
|
|
256
|
+
const { rows } = await pool.query(`SELECT DATE("createdAt")::text AS "date",
|
|
257
|
+
COALESCE(SUM("cost"), 0)::int AS "totalCost",
|
|
258
|
+
COALESCE(SUM("inputCost"), 0)::int AS "totalInputCost",
|
|
259
|
+
COALESCE(SUM("outputCost"), 0)::int AS "totalOutputCost",
|
|
260
|
+
COALESCE(SUM("totalTokens"), 0)::int AS "totalTokens",
|
|
261
|
+
COUNT(*)::int AS "requestCount"
|
|
262
|
+
FROM "llm_requests"
|
|
263
|
+
WHERE "createdAt" >= $1 AND "createdAt" <= $2
|
|
264
|
+
GROUP BY DATE("createdAt")
|
|
265
|
+
ORDER BY DATE("createdAt") ASC`, [params.startDate.toISOString(), params.endDate.toISOString()]);
|
|
266
|
+
return rows;
|
|
267
|
+
},
|
|
268
|
+
getCostSummary: async (params) => {
|
|
269
|
+
const { startDate, endDate, groupBy, configId, variantId, environmentId, tags, tagKeys } = params;
|
|
270
|
+
const baseParams = [
|
|
271
|
+
startDate.toISOString(),
|
|
272
|
+
endDate.toISOString(),
|
|
273
|
+
configId ?? null,
|
|
274
|
+
variantId ?? null,
|
|
275
|
+
environmentId ?? null
|
|
276
|
+
];
|
|
277
|
+
if (groupBy === "tags") {
|
|
278
|
+
const conditions = [
|
|
279
|
+
`"createdAt" >= $1`,
|
|
280
|
+
`"createdAt" <= $2`,
|
|
281
|
+
`($3::uuid IS NULL OR "configId" = $3)`,
|
|
282
|
+
`($4::uuid IS NULL OR "variantId" = $4)`,
|
|
283
|
+
`($5::uuid IS NULL OR "environmentId" = $5)`
|
|
284
|
+
];
|
|
285
|
+
const queryParams = [...baseParams];
|
|
286
|
+
let idx = 5;
|
|
287
|
+
const tagFilter = buildTagFilters(tags, idx);
|
|
288
|
+
conditions.push(...tagFilter.conditions);
|
|
289
|
+
queryParams.push(...tagFilter.params);
|
|
290
|
+
idx += tagFilter.params.length;
|
|
291
|
+
if (tagKeys && tagKeys.length > 0) {
|
|
292
|
+
const keyPlaceholders = tagKeys.map((_, i) => `$${idx + i + 1}`).join(", ");
|
|
293
|
+
conditions.push(`t.key IN (${keyPlaceholders})`);
|
|
294
|
+
queryParams.push(...tagKeys);
|
|
295
|
+
}
|
|
296
|
+
const where = conditions.join(" AND ");
|
|
297
|
+
const { rows: rows$1 } = await pool.query(`SELECT t.key || ':' || t.value AS "groupKey",
|
|
298
|
+
COALESCE(SUM("cost"), 0)::int AS "totalCost",
|
|
299
|
+
COUNT(*)::int AS "requestCount"
|
|
300
|
+
FROM "llm_requests", jsonb_each_text("tags") t
|
|
301
|
+
WHERE ${where}
|
|
302
|
+
GROUP BY t.key, t.value
|
|
303
|
+
ORDER BY SUM("cost") DESC`, queryParams);
|
|
304
|
+
return rows$1;
|
|
305
|
+
}
|
|
306
|
+
const sqlMap = {
|
|
307
|
+
day: `SELECT DATE("createdAt")::text AS "groupKey", COALESCE(SUM("cost"),0)::int AS "totalCost", COUNT(*)::int AS "requestCount", COALESCE(SUM("totalTokens"),0)::int AS "totalTokens" FROM "llm_requests" WHERE "createdAt">=$1 AND "createdAt"<=$2 AND ($3::uuid IS NULL OR "configId"=$3) AND ($4::uuid IS NULL OR "variantId"=$4) AND ($5::uuid IS NULL OR "environmentId"=$5) GROUP BY DATE("createdAt") ORDER BY DATE("createdAt") ASC`,
|
|
308
|
+
hour: `SELECT DATE_TRUNC('hour',"createdAt")::text AS "groupKey", COALESCE(SUM("cost"),0)::int AS "totalCost", COUNT(*)::int AS "requestCount", COALESCE(SUM("totalTokens"),0)::int AS "totalTokens" FROM "llm_requests" WHERE "createdAt">=$1 AND "createdAt"<=$2 AND ($3::uuid IS NULL OR "configId"=$3) AND ($4::uuid IS NULL OR "variantId"=$4) AND ($5::uuid IS NULL OR "environmentId"=$5) GROUP BY DATE_TRUNC('hour',"createdAt") ORDER BY DATE_TRUNC('hour',"createdAt") ASC`,
|
|
309
|
+
model: `SELECT "provider"||'/'||"model" AS "groupKey", COALESCE(SUM("cost"),0)::int AS "totalCost", COUNT(*)::int AS "requestCount" FROM "llm_requests" WHERE "createdAt">=$1 AND "createdAt"<=$2 AND ($3::uuid IS NULL OR "configId"=$3) AND ($4::uuid IS NULL OR "variantId"=$4) AND ($5::uuid IS NULL OR "environmentId"=$5) GROUP BY "provider","model" ORDER BY SUM("cost") DESC`,
|
|
310
|
+
provider: `SELECT "provider" AS "groupKey", COALESCE(SUM("cost"),0)::int AS "totalCost", COUNT(*)::int AS "requestCount" FROM "llm_requests" WHERE "createdAt">=$1 AND "createdAt"<=$2 AND ($3::uuid IS NULL OR "configId"=$3) AND ($4::uuid IS NULL OR "variantId"=$4) AND ($5::uuid IS NULL OR "environmentId"=$5) GROUP BY "provider" ORDER BY SUM("cost") DESC`,
|
|
311
|
+
endpoint: `SELECT COALESCE("endpoint",'unknown') AS "groupKey", COALESCE(SUM("cost"),0)::int AS "totalCost", COUNT(*)::int AS "requestCount" FROM "llm_requests" WHERE "createdAt">=$1 AND "createdAt"<=$2 AND ($3::uuid IS NULL OR "configId"=$3) AND ($4::uuid IS NULL OR "variantId"=$4) AND ($5::uuid IS NULL OR "environmentId"=$5) GROUP BY "endpoint" ORDER BY SUM("cost") DESC`
|
|
312
|
+
};
|
|
313
|
+
const totalSql = `SELECT 'total' AS "groupKey", COALESCE(SUM("cost"),0)::int AS "totalCost", COUNT(*)::int AS "requestCount" FROM "llm_requests" WHERE "createdAt">=$1 AND "createdAt"<=$2 AND ($3::uuid IS NULL OR "configId"=$3) AND ($4::uuid IS NULL OR "variantId"=$4) AND ($5::uuid IS NULL OR "environmentId"=$5)`;
|
|
314
|
+
const sql = groupBy ? sqlMap[groupBy] ?? totalSql : totalSql;
|
|
315
|
+
const { rows } = await pool.query(sql, baseParams);
|
|
316
|
+
return rows;
|
|
317
|
+
},
|
|
318
|
+
getRequestStats: async (params) => {
|
|
319
|
+
const { startDate, endDate, configId, variantId, environmentId, tags } = params;
|
|
320
|
+
const conditions = [`"createdAt" >= $1`, `"createdAt" <= $2`];
|
|
321
|
+
const queryParams = [startDate.toISOString(), endDate.toISOString()];
|
|
322
|
+
let idx = 2;
|
|
323
|
+
if (configId) {
|
|
324
|
+
conditions.push(`"configId" = $${++idx}`);
|
|
325
|
+
queryParams.push(configId);
|
|
326
|
+
}
|
|
327
|
+
if (variantId) {
|
|
328
|
+
conditions.push(`"variantId" = $${++idx}`);
|
|
329
|
+
queryParams.push(variantId);
|
|
330
|
+
}
|
|
331
|
+
if (environmentId) {
|
|
332
|
+
conditions.push(`"environmentId" = $${++idx}`);
|
|
333
|
+
queryParams.push(environmentId);
|
|
334
|
+
}
|
|
335
|
+
const tagFilter = buildTagFilters(tags, idx);
|
|
336
|
+
conditions.push(...tagFilter.conditions);
|
|
337
|
+
queryParams.push(...tagFilter.params);
|
|
338
|
+
const where = conditions.join(" AND ");
|
|
339
|
+
const { rows } = await pool.query(`SELECT
|
|
340
|
+
COUNT(*)::int AS "totalRequests",
|
|
341
|
+
COUNT(CASE WHEN "statusCode">=200 AND "statusCode"<300 THEN 1 END)::int AS "successfulRequests",
|
|
342
|
+
COUNT(CASE WHEN "statusCode">=400 THEN 1 END)::int AS "failedRequests",
|
|
343
|
+
COUNT(CASE WHEN "isStreaming"=true THEN 1 END)::int AS "streamingRequests",
|
|
344
|
+
AVG("latencyMs") AS "avgLatencyMs",
|
|
345
|
+
MAX("latencyMs")::int AS "maxLatencyMs",
|
|
346
|
+
MIN("latencyMs")::int AS "minLatencyMs"
|
|
347
|
+
FROM "llm_requests" WHERE ${where}`, queryParams);
|
|
348
|
+
return rows[0];
|
|
349
|
+
},
|
|
350
|
+
getDistinctTags: async () => {
|
|
351
|
+
const { rows } = await pool.query(`SELECT DISTINCT key, value
|
|
352
|
+
FROM "llm_requests", jsonb_each_text("tags") AS t(key, value)
|
|
353
|
+
WHERE "tags" != '{}'::jsonb
|
|
354
|
+
ORDER BY key, value`);
|
|
355
|
+
return rows;
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function createTracesStore(pool) {
|
|
360
|
+
return {
|
|
361
|
+
upsertTrace: async (data) => {
|
|
362
|
+
const result = require_types.upsertTraceSchema.safeParse(data);
|
|
363
|
+
if (!result.success) throw new Error(`Invalid trace data: ${result.error.message}`);
|
|
364
|
+
const trace = result.data;
|
|
365
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
366
|
+
await pool.query(`INSERT INTO "traces" (
|
|
367
|
+
"id","traceId","name","sessionId","userId","status",
|
|
368
|
+
"startTime","endTime","durationMs","spanCount",
|
|
369
|
+
"totalInputTokens","totalOutputTokens","totalTokens","totalCost",
|
|
370
|
+
"tags","metadata","createdAt","updatedAt"
|
|
371
|
+
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15::jsonb,$16::jsonb,$17,$17)
|
|
372
|
+
ON CONFLICT ("traceId") DO UPDATE SET
|
|
373
|
+
"name" = COALESCE(EXCLUDED."name", "traces"."name"),
|
|
374
|
+
"sessionId" = COALESCE(EXCLUDED."sessionId", "traces"."sessionId"),
|
|
375
|
+
"userId" = COALESCE(EXCLUDED."userId", "traces"."userId"),
|
|
376
|
+
"status" = CASE
|
|
377
|
+
WHEN EXCLUDED."status" = 'error' THEN 'error'
|
|
378
|
+
WHEN EXCLUDED."status" = 'ok' AND "traces"."status" != 'error' THEN 'ok'
|
|
379
|
+
ELSE "traces"."status"
|
|
380
|
+
END,
|
|
381
|
+
"startTime" = LEAST("traces"."startTime", EXCLUDED."startTime"),
|
|
382
|
+
"endTime" = GREATEST(
|
|
383
|
+
COALESCE("traces"."endTime", EXCLUDED."endTime"),
|
|
384
|
+
COALESCE(EXCLUDED."endTime", "traces"."endTime")
|
|
385
|
+
),
|
|
386
|
+
"durationMs" = EXTRACT(EPOCH FROM (
|
|
387
|
+
GREATEST(COALESCE("traces"."endTime",EXCLUDED."endTime"),COALESCE(EXCLUDED."endTime","traces"."endTime")) -
|
|
388
|
+
LEAST("traces"."startTime", EXCLUDED."startTime")
|
|
389
|
+
))::integer * 1000,
|
|
390
|
+
"spanCount" = "traces"."spanCount" + EXCLUDED."spanCount",
|
|
391
|
+
"totalInputTokens" = "traces"."totalInputTokens" + EXCLUDED."totalInputTokens",
|
|
392
|
+
"totalOutputTokens" = "traces"."totalOutputTokens" + EXCLUDED."totalOutputTokens",
|
|
393
|
+
"totalTokens" = "traces"."totalTokens" + EXCLUDED."totalTokens",
|
|
394
|
+
"totalCost" = "traces"."totalCost" + EXCLUDED."totalCost",
|
|
395
|
+
"tags" = "traces"."tags" || EXCLUDED."tags",
|
|
396
|
+
"metadata" = "traces"."metadata" || EXCLUDED."metadata",
|
|
397
|
+
"updatedAt" = $17`, [
|
|
398
|
+
(0, node_crypto.randomUUID)(),
|
|
399
|
+
trace.traceId,
|
|
400
|
+
trace.name ?? null,
|
|
401
|
+
trace.sessionId ?? null,
|
|
402
|
+
trace.userId ?? null,
|
|
403
|
+
trace.status,
|
|
404
|
+
trace.startTime.toISOString(),
|
|
405
|
+
trace.endTime?.toISOString() ?? null,
|
|
406
|
+
trace.durationMs ?? null,
|
|
407
|
+
trace.spanCount,
|
|
408
|
+
trace.totalInputTokens,
|
|
409
|
+
trace.totalOutputTokens,
|
|
410
|
+
trace.totalTokens,
|
|
411
|
+
trace.totalCost,
|
|
412
|
+
JSON.stringify(trace.tags),
|
|
413
|
+
JSON.stringify(trace.metadata),
|
|
414
|
+
now
|
|
415
|
+
]);
|
|
416
|
+
},
|
|
417
|
+
batchInsertSpans: async (spans) => {
|
|
418
|
+
if (spans.length === 0) return { count: 0 };
|
|
419
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
420
|
+
const columns = [
|
|
421
|
+
"id",
|
|
422
|
+
"traceId",
|
|
423
|
+
"spanId",
|
|
424
|
+
"parentSpanId",
|
|
425
|
+
"name",
|
|
426
|
+
"kind",
|
|
427
|
+
"status",
|
|
428
|
+
"statusMessage",
|
|
429
|
+
"startTime",
|
|
430
|
+
"endTime",
|
|
431
|
+
"durationMs",
|
|
432
|
+
"provider",
|
|
433
|
+
"model",
|
|
434
|
+
"promptTokens",
|
|
435
|
+
"completionTokens",
|
|
436
|
+
"totalTokens",
|
|
437
|
+
"cost",
|
|
438
|
+
"configId",
|
|
439
|
+
"variantId",
|
|
440
|
+
"environmentId",
|
|
441
|
+
"providerConfigId",
|
|
442
|
+
"requestId",
|
|
443
|
+
"source",
|
|
444
|
+
"input",
|
|
445
|
+
"output",
|
|
446
|
+
"attributes",
|
|
447
|
+
"createdAt",
|
|
448
|
+
"updatedAt"
|
|
449
|
+
];
|
|
450
|
+
const colNames = columns.map((c) => `"${c}"`).join(", ");
|
|
451
|
+
const params = [];
|
|
452
|
+
const valueRows = [];
|
|
453
|
+
for (const span of spans) {
|
|
454
|
+
const result = require_types.insertSpanSchema.safeParse(span);
|
|
455
|
+
if (!result.success) {
|
|
456
|
+
__llmops_core.logger.warn(`[batchInsertSpans] Skipping invalid span ${span.spanId}: ${result.error.message}`);
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
const s = result.data;
|
|
460
|
+
const offset = params.length;
|
|
461
|
+
const placeholders = columns.map((_, i) => `$${offset + i + 1}`).join(", ");
|
|
462
|
+
valueRows.push(`(${placeholders})`);
|
|
463
|
+
params.push((0, node_crypto.randomUUID)(), s.traceId, s.spanId, s.parentSpanId ?? null, s.name, s.kind, s.status, s.statusMessage ?? null, s.startTime.toISOString(), s.endTime?.toISOString() ?? null, s.durationMs ?? null, s.provider ?? null, s.model ?? null, s.promptTokens, s.completionTokens, s.totalTokens, s.cost, s.configId ?? null, s.variantId ?? null, s.environmentId ?? null, s.providerConfigId ?? null, s.requestId ?? null, s.source, s.input != null ? JSON.stringify(s.input) : null, s.output != null ? JSON.stringify(s.output) : null, JSON.stringify(s.attributes), now, now);
|
|
464
|
+
}
|
|
465
|
+
if (valueRows.length === 0) return { count: 0 };
|
|
466
|
+
await pool.query(`INSERT INTO "spans" (${colNames}) VALUES ${valueRows.join(", ")} ON CONFLICT ("spanId") DO NOTHING`, params);
|
|
467
|
+
return { count: valueRows.length };
|
|
468
|
+
},
|
|
469
|
+
batchInsertSpanEvents: async (events) => {
|
|
470
|
+
if (events.length === 0) return { count: 0 };
|
|
471
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
472
|
+
const columns = [
|
|
473
|
+
"id",
|
|
474
|
+
"traceId",
|
|
475
|
+
"spanId",
|
|
476
|
+
"name",
|
|
477
|
+
"timestamp",
|
|
478
|
+
"attributes",
|
|
479
|
+
"createdAt"
|
|
480
|
+
];
|
|
481
|
+
const colNames = columns.map((c) => `"${c}"`).join(", ");
|
|
482
|
+
const params = [];
|
|
483
|
+
const valueRows = [];
|
|
484
|
+
for (const event of events) {
|
|
485
|
+
const result = require_types.insertSpanEventSchema.safeParse(event);
|
|
486
|
+
if (!result.success) {
|
|
487
|
+
__llmops_core.logger.warn(`[batchInsertSpanEvents] Skipping invalid event: ${result.error.message}`);
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
const e = result.data;
|
|
491
|
+
const offset = params.length;
|
|
492
|
+
const placeholders = columns.map((_, i) => `$${offset + i + 1}`).join(", ");
|
|
493
|
+
valueRows.push(`(${placeholders})`);
|
|
494
|
+
params.push((0, node_crypto.randomUUID)(), e.traceId, e.spanId, e.name, e.timestamp.toISOString(), JSON.stringify(e.attributes), now);
|
|
495
|
+
}
|
|
496
|
+
if (valueRows.length === 0) return { count: 0 };
|
|
497
|
+
await pool.query(`INSERT INTO "span_events" (${colNames}) VALUES ${valueRows.join(", ")}`, params);
|
|
498
|
+
return { count: valueRows.length };
|
|
499
|
+
},
|
|
500
|
+
listTraces: async (params) => {
|
|
501
|
+
const { limit = 50, offset = 0, sessionId, userId, status, name, startDate, endDate, tags } = params ?? {};
|
|
502
|
+
const conditions = ["TRUE"];
|
|
503
|
+
const queryParams = [];
|
|
504
|
+
let idx = 0;
|
|
505
|
+
if (sessionId) {
|
|
506
|
+
conditions.push(`"sessionId" = $${++idx}`);
|
|
507
|
+
queryParams.push(sessionId);
|
|
508
|
+
}
|
|
509
|
+
if (userId) {
|
|
510
|
+
conditions.push(`"userId" = $${++idx}`);
|
|
511
|
+
queryParams.push(userId);
|
|
512
|
+
}
|
|
513
|
+
if (status) {
|
|
514
|
+
conditions.push(`"status" = $${++idx}`);
|
|
515
|
+
queryParams.push(status);
|
|
516
|
+
}
|
|
517
|
+
if (name) {
|
|
518
|
+
conditions.push(`"name" ILIKE $${++idx}`);
|
|
519
|
+
queryParams.push(`%${name}%`);
|
|
520
|
+
}
|
|
521
|
+
if (startDate) {
|
|
522
|
+
conditions.push(`"startTime" >= $${++idx}`);
|
|
523
|
+
queryParams.push(startDate.toISOString());
|
|
524
|
+
}
|
|
525
|
+
if (endDate) {
|
|
526
|
+
conditions.push(`"startTime" <= $${++idx}`);
|
|
527
|
+
queryParams.push(endDate.toISOString());
|
|
528
|
+
}
|
|
529
|
+
const tagFilter = buildTagFilters(tags, idx);
|
|
530
|
+
conditions.push(...tagFilter.conditions);
|
|
531
|
+
queryParams.push(...tagFilter.params);
|
|
532
|
+
idx += tagFilter.params.length;
|
|
533
|
+
const where = conditions.join(" AND ");
|
|
534
|
+
const total = (await pool.query(`SELECT COUNT(*)::int AS "total" FROM "traces" WHERE ${where}`, queryParams)).rows[0]?.total ?? 0;
|
|
535
|
+
return {
|
|
536
|
+
data: (await pool.query(`SELECT * FROM "traces" WHERE ${where} ORDER BY "startTime" DESC LIMIT $${++idx} OFFSET $${++idx}`, [
|
|
537
|
+
...queryParams,
|
|
538
|
+
limit,
|
|
539
|
+
offset
|
|
540
|
+
])).rows,
|
|
541
|
+
total,
|
|
542
|
+
limit,
|
|
543
|
+
offset
|
|
544
|
+
};
|
|
545
|
+
},
|
|
546
|
+
getTraceWithSpans: async (traceId) => {
|
|
547
|
+
const trace = (await pool.query(`SELECT * FROM "traces" WHERE "traceId" = $1`, [traceId])).rows[0];
|
|
548
|
+
if (!trace) return void 0;
|
|
549
|
+
const [spanResult, eventResult] = await Promise.all([pool.query(`SELECT * FROM "spans" WHERE "traceId" = $1 ORDER BY "startTime" ASC`, [traceId]), pool.query(`SELECT * FROM "span_events" WHERE "traceId" = $1 ORDER BY "timestamp" ASC`, [traceId])]);
|
|
550
|
+
return {
|
|
551
|
+
trace,
|
|
552
|
+
spans: spanResult.rows,
|
|
553
|
+
events: eventResult.rows
|
|
554
|
+
};
|
|
555
|
+
},
|
|
556
|
+
getTraceStats: async (params) => {
|
|
557
|
+
const { rows } = await pool.query(`SELECT
|
|
558
|
+
COUNT(*)::int AS "totalTraces",
|
|
559
|
+
COALESCE(AVG("durationMs"), 0) AS "avgDurationMs",
|
|
560
|
+
COUNT(CASE WHEN "status" = 'error' THEN 1 END)::int AS "errorCount",
|
|
561
|
+
COALESCE(SUM("totalCost"), 0)::int AS "totalCost",
|
|
562
|
+
COALESCE(SUM("totalTokens"), 0)::int AS "totalTokens",
|
|
563
|
+
COALESCE(SUM("spanCount"), 0)::int AS "totalSpans"
|
|
564
|
+
FROM "traces"
|
|
565
|
+
WHERE "startTime" >= $1 AND "startTime" <= $2
|
|
566
|
+
AND ($3::varchar IS NULL OR "sessionId" = $3)
|
|
567
|
+
AND ($4::varchar IS NULL OR "userId" = $4)`, [
|
|
568
|
+
params.startDate.toISOString(),
|
|
569
|
+
params.endDate.toISOString(),
|
|
570
|
+
params.sessionId ?? null,
|
|
571
|
+
params.userId ?? null
|
|
572
|
+
]);
|
|
573
|
+
return rows[0];
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
const pgStoreOptionsSchema = require_types.zod_default.object({ schema: require_types.zod_default.string().default("llmops") });
|
|
578
|
+
/**
|
|
579
|
+
* Create a PostgreSQL-backed telemetry store.
|
|
580
|
+
*
|
|
581
|
+
* Usage:
|
|
582
|
+
* ```ts
|
|
583
|
+
* import { llmops } from '@llmops/sdk'
|
|
584
|
+
* import { pgStore } from '@llmops/sdk/store/pg'
|
|
585
|
+
*
|
|
586
|
+
* const ops = llmops({
|
|
587
|
+
* telemetry: pgStore(process.env.DATABASE_URL),
|
|
588
|
+
* })
|
|
589
|
+
* ```
|
|
590
|
+
*/
|
|
591
|
+
function createPgStore(connectionString, options) {
|
|
592
|
+
const parsed = require_types.zod_default.string().url().safeParse(connectionString);
|
|
593
|
+
if (!parsed.success) throw new Error(`pgStore: invalid connection string — ${parsed.error.issues[0]?.message ?? "expected a postgres:// URL"}`);
|
|
594
|
+
const { schema } = pgStoreOptionsSchema.parse(options ?? {});
|
|
595
|
+
let pool;
|
|
596
|
+
try {
|
|
597
|
+
pool = new (require("pg")).Pool({ connectionString });
|
|
598
|
+
} catch {
|
|
599
|
+
throw new Error("pgStore requires the \"pg\" package. Install it with: pnpm add pg");
|
|
600
|
+
}
|
|
601
|
+
pool.on("connect", (client) => {
|
|
602
|
+
client.query(`SET search_path TO "${schema}"`);
|
|
603
|
+
});
|
|
604
|
+
__llmops_core.logger.debug(`pgStore: initialized with schema "${schema}"`);
|
|
605
|
+
return {
|
|
606
|
+
...createLLMRequestsStore(pool),
|
|
607
|
+
...createTracesStore(pool),
|
|
608
|
+
_pool: pool,
|
|
609
|
+
_schema: schema
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
//#endregion
|
|
3
614
|
//#region src/store/pg/migrations/000001_2191b051.sql
|
|
4
615
|
var _000001_2191b051_default = "BEGIN;\n\nCREATE TABLE IF NOT EXISTS llm_requests (\n id UUID PRIMARY KEY,\n \"requestId\" UUID NOT NULL,\n \"configId\" UUID,\n \"variantId\" UUID,\n \"environmentId\" UUID,\n \"providerConfigId\" UUID,\n provider VARCHAR(255) NOT NULL,\n model VARCHAR(255) 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 VARCHAR(255) NOT NULL,\n \"statusCode\" INTEGER NOT NULL,\n \"latencyMs\" INTEGER NOT NULL DEFAULT 0,\n \"isStreaming\" BOOLEAN NOT NULL DEFAULT false,\n \"userId\" VARCHAR(255),\n tags JSONB NOT NULL DEFAULT '{}'::jsonb,\n \"guardrailResults\" JSONB,\n \"traceId\" VARCHAR(255),\n \"spanId\" VARCHAR(255),\n \"parentSpanId\" VARCHAR(255),\n \"sessionId\" VARCHAR(255),\n \"createdAt\" TIMESTAMP NOT NULL DEFAULT NOW(),\n \"updatedAt\" TIMESTAMP NOT NULL DEFAULT NOW()\n);\n\nCREATE TABLE IF NOT EXISTS span_events (\n id UUID PRIMARY KEY,\n \"traceId\" VARCHAR(255) NOT NULL,\n \"spanId\" VARCHAR(255) NOT NULL,\n name VARCHAR(255) NOT NULL,\n timestamp TIMESTAMP NOT NULL,\n attributes JSONB NOT NULL DEFAULT '{}'::jsonb,\n \"createdAt\" TIMESTAMP NOT NULL DEFAULT NOW()\n);\n\nCREATE TABLE IF NOT EXISTS spans (\n id UUID PRIMARY KEY,\n \"traceId\" VARCHAR(255) NOT NULL,\n \"spanId\" VARCHAR(255) NOT NULL UNIQUE,\n \"parentSpanId\" VARCHAR(255),\n name VARCHAR(255) NOT NULL,\n kind INTEGER NOT NULL DEFAULT 1,\n status INTEGER NOT NULL DEFAULT 0,\n \"statusMessage\" TEXT,\n \"startTime\" TIMESTAMP NOT NULL,\n \"endTime\" TIMESTAMP,\n \"durationMs\" INTEGER,\n provider VARCHAR(255),\n model VARCHAR(255),\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\" UUID,\n \"variantId\" UUID,\n \"environmentId\" UUID,\n \"providerConfigId\" UUID,\n \"requestId\" UUID,\n source VARCHAR(50) NOT NULL DEFAULT 'gateway',\n input JSONB,\n output JSONB,\n attributes JSONB NOT NULL DEFAULT '{}'::jsonb,\n \"createdAt\" TIMESTAMP NOT NULL DEFAULT NOW(),\n \"updatedAt\" TIMESTAMP NOT NULL DEFAULT NOW()\n);\n\nCREATE TABLE IF NOT EXISTS traces (\n id UUID PRIMARY KEY,\n \"traceId\" VARCHAR(255) NOT NULL UNIQUE,\n name VARCHAR(255),\n \"sessionId\" VARCHAR(255),\n \"userId\" VARCHAR(255),\n status VARCHAR(50) NOT NULL DEFAULT 'unset',\n \"startTime\" TIMESTAMP NOT NULL,\n \"endTime\" TIMESTAMP,\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 JSONB NOT NULL DEFAULT '{}'::jsonb,\n metadata JSONB NOT NULL DEFAULT '{}'::jsonb,\n \"createdAt\" TIMESTAMP NOT NULL DEFAULT NOW(),\n \"updatedAt\" TIMESTAMP NOT NULL DEFAULT NOW()\n);\n\nCOMMIT;\n";
|
|
5
616
|
|
|
@@ -43,5 +654,5 @@ async function runMigrations(pool, schema) {
|
|
|
43
654
|
}
|
|
44
655
|
|
|
45
656
|
//#endregion
|
|
46
|
-
exports.pgStore =
|
|
657
|
+
exports.pgStore = createPgStore;
|
|
47
658
|
exports.runMigrations = runMigrations;
|
package/dist/store/pg.d.cts
CHANGED
package/dist/store/pg.d.mts
CHANGED