@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
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
-
import { logger } from "@llmops/core";
|
|
3
|
-
import { randomUUID } from "node:crypto";
|
|
4
2
|
|
|
5
3
|
//#region rolldown:runtime
|
|
6
4
|
var __defProp = Object.defineProperty;
|
|
@@ -12874,7 +12872,7 @@ config(en_default());
|
|
|
12874
12872
|
var zod_default = external_exports;
|
|
12875
12873
|
|
|
12876
12874
|
//#endregion
|
|
12877
|
-
//#region src/telemetry/
|
|
12875
|
+
//#region src/telemetry/types.ts
|
|
12878
12876
|
const guardrailResultSchema = zod_default.object({
|
|
12879
12877
|
checkId: zod_default.string(),
|
|
12880
12878
|
functionId: zod_default.string(),
|
|
@@ -12987,612 +12985,6 @@ const insertSpanEventSchema = zod_default.object({
|
|
|
12987
12985
|
timestamp: zod_default.date(),
|
|
12988
12986
|
attributes: zod_default.record(zod_default.string(), zod_default.unknown()).default({})
|
|
12989
12987
|
});
|
|
12990
|
-
function buildTagFilters(tags, paramOffset) {
|
|
12991
|
-
const conditions = [];
|
|
12992
|
-
const params = [];
|
|
12993
|
-
if (!tags) return {
|
|
12994
|
-
conditions,
|
|
12995
|
-
params
|
|
12996
|
-
};
|
|
12997
|
-
for (const [key, values] of Object.entries(tags)) {
|
|
12998
|
-
if (values.length === 0) continue;
|
|
12999
|
-
if (values.length === 1) {
|
|
13000
|
-
conditions.push(`"tags"->>'${key}' = $${paramOffset + params.length + 1}`);
|
|
13001
|
-
params.push(values[0]);
|
|
13002
|
-
} else {
|
|
13003
|
-
const placeholders = values.map((_, i) => `$${paramOffset + params.length + i + 1}`).join(", ");
|
|
13004
|
-
conditions.push(`"tags"->>'${key}' IN (${placeholders})`);
|
|
13005
|
-
params.push(...values);
|
|
13006
|
-
}
|
|
13007
|
-
}
|
|
13008
|
-
return {
|
|
13009
|
-
conditions,
|
|
13010
|
-
params
|
|
13011
|
-
};
|
|
13012
|
-
}
|
|
13013
|
-
function createLLMRequestsStore(pool) {
|
|
13014
|
-
return {
|
|
13015
|
-
batchInsertRequests: async (requests) => {
|
|
13016
|
-
if (requests.length === 0) return { count: 0 };
|
|
13017
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13018
|
-
const columns = [
|
|
13019
|
-
"id",
|
|
13020
|
-
"requestId",
|
|
13021
|
-
"configId",
|
|
13022
|
-
"variantId",
|
|
13023
|
-
"environmentId",
|
|
13024
|
-
"providerConfigId",
|
|
13025
|
-
"provider",
|
|
13026
|
-
"model",
|
|
13027
|
-
"promptTokens",
|
|
13028
|
-
"completionTokens",
|
|
13029
|
-
"totalTokens",
|
|
13030
|
-
"cachedTokens",
|
|
13031
|
-
"cacheCreationTokens",
|
|
13032
|
-
"cost",
|
|
13033
|
-
"cacheSavings",
|
|
13034
|
-
"inputCost",
|
|
13035
|
-
"outputCost",
|
|
13036
|
-
"endpoint",
|
|
13037
|
-
"statusCode",
|
|
13038
|
-
"latencyMs",
|
|
13039
|
-
"isStreaming",
|
|
13040
|
-
"userId",
|
|
13041
|
-
"tags",
|
|
13042
|
-
"guardrailResults",
|
|
13043
|
-
"traceId",
|
|
13044
|
-
"spanId",
|
|
13045
|
-
"parentSpanId",
|
|
13046
|
-
"sessionId",
|
|
13047
|
-
"createdAt",
|
|
13048
|
-
"updatedAt"
|
|
13049
|
-
];
|
|
13050
|
-
const colNames = columns.map((c) => `"${c}"`).join(", ");
|
|
13051
|
-
const params = [];
|
|
13052
|
-
const valueRows = [];
|
|
13053
|
-
for (const req of requests) {
|
|
13054
|
-
const result = insertLLMRequestSchema.safeParse(req);
|
|
13055
|
-
if (!result.success) {
|
|
13056
|
-
logger.warn(`[batchInsertRequests] Skipping invalid request: ${result.error.message}`);
|
|
13057
|
-
continue;
|
|
13058
|
-
}
|
|
13059
|
-
const r = result.data;
|
|
13060
|
-
const offset = params.length;
|
|
13061
|
-
const placeholders = columns.map((_, i) => `$${offset + i + 1}`).join(", ");
|
|
13062
|
-
valueRows.push(`(${placeholders})`);
|
|
13063
|
-
params.push(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);
|
|
13064
|
-
}
|
|
13065
|
-
if (valueRows.length === 0) return { count: 0 };
|
|
13066
|
-
await pool.query(`INSERT INTO "llm_requests" (${colNames}) VALUES ${valueRows.join(", ")}`, params);
|
|
13067
|
-
return { count: valueRows.length };
|
|
13068
|
-
},
|
|
13069
|
-
insertRequest: async (request) => {
|
|
13070
|
-
const result = insertLLMRequestSchema.safeParse(request);
|
|
13071
|
-
if (!result.success) throw new Error(`Invalid request data: ${result.error.message}`);
|
|
13072
|
-
const r = result.data;
|
|
13073
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13074
|
-
const { rows } = await pool.query(`INSERT INTO "llm_requests" (
|
|
13075
|
-
"id", "requestId", "configId", "variantId", "environmentId",
|
|
13076
|
-
"providerConfigId", "provider", "model", "promptTokens",
|
|
13077
|
-
"completionTokens", "totalTokens", "cachedTokens",
|
|
13078
|
-
"cacheCreationTokens", "cost", "cacheSavings", "inputCost",
|
|
13079
|
-
"outputCost", "endpoint", "statusCode", "latencyMs", "isStreaming",
|
|
13080
|
-
"userId", "tags", "guardrailResults", "traceId", "spanId",
|
|
13081
|
-
"parentSpanId", "sessionId", "createdAt", "updatedAt"
|
|
13082
|
-
) VALUES (
|
|
13083
|
-
$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,
|
|
13084
|
-
$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30
|
|
13085
|
-
) RETURNING *`, [
|
|
13086
|
-
randomUUID(),
|
|
13087
|
-
r.requestId,
|
|
13088
|
-
r.configId ?? null,
|
|
13089
|
-
r.variantId ?? null,
|
|
13090
|
-
r.environmentId ?? null,
|
|
13091
|
-
r.providerConfigId ?? null,
|
|
13092
|
-
r.provider,
|
|
13093
|
-
r.model,
|
|
13094
|
-
r.promptTokens,
|
|
13095
|
-
r.completionTokens,
|
|
13096
|
-
r.totalTokens,
|
|
13097
|
-
r.cachedTokens,
|
|
13098
|
-
r.cacheCreationTokens,
|
|
13099
|
-
r.cost,
|
|
13100
|
-
r.cacheSavings,
|
|
13101
|
-
r.inputCost,
|
|
13102
|
-
r.outputCost,
|
|
13103
|
-
r.endpoint,
|
|
13104
|
-
r.statusCode,
|
|
13105
|
-
r.latencyMs,
|
|
13106
|
-
r.isStreaming,
|
|
13107
|
-
r.userId ?? null,
|
|
13108
|
-
JSON.stringify(r.tags),
|
|
13109
|
-
r.guardrailResults ? JSON.stringify(r.guardrailResults) : null,
|
|
13110
|
-
r.traceId ?? null,
|
|
13111
|
-
r.spanId ?? null,
|
|
13112
|
-
r.parentSpanId ?? null,
|
|
13113
|
-
r.sessionId ?? null,
|
|
13114
|
-
now,
|
|
13115
|
-
now
|
|
13116
|
-
]);
|
|
13117
|
-
return rows[0] ?? null;
|
|
13118
|
-
},
|
|
13119
|
-
listRequests: async (params) => {
|
|
13120
|
-
const { limit = 100, offset = 0, configId, variantId, environmentId, providerConfigId, provider, model, startDate, endDate, tags } = params ?? {};
|
|
13121
|
-
const conditions = ["TRUE"];
|
|
13122
|
-
const queryParams = [];
|
|
13123
|
-
let idx = 0;
|
|
13124
|
-
if (configId) {
|
|
13125
|
-
conditions.push(`"configId" = $${++idx}`);
|
|
13126
|
-
queryParams.push(configId);
|
|
13127
|
-
}
|
|
13128
|
-
if (variantId) {
|
|
13129
|
-
conditions.push(`"variantId" = $${++idx}`);
|
|
13130
|
-
queryParams.push(variantId);
|
|
13131
|
-
}
|
|
13132
|
-
if (environmentId) {
|
|
13133
|
-
conditions.push(`"environmentId" = $${++idx}`);
|
|
13134
|
-
queryParams.push(environmentId);
|
|
13135
|
-
}
|
|
13136
|
-
if (providerConfigId) {
|
|
13137
|
-
conditions.push(`"providerConfigId" = $${++idx}`);
|
|
13138
|
-
queryParams.push(providerConfigId);
|
|
13139
|
-
}
|
|
13140
|
-
if (provider) {
|
|
13141
|
-
conditions.push(`"provider" = $${++idx}`);
|
|
13142
|
-
queryParams.push(provider);
|
|
13143
|
-
}
|
|
13144
|
-
if (model) {
|
|
13145
|
-
conditions.push(`"model" = $${++idx}`);
|
|
13146
|
-
queryParams.push(model);
|
|
13147
|
-
}
|
|
13148
|
-
if (startDate) {
|
|
13149
|
-
conditions.push(`"createdAt" >= $${++idx}`);
|
|
13150
|
-
queryParams.push(startDate.toISOString());
|
|
13151
|
-
}
|
|
13152
|
-
if (endDate) {
|
|
13153
|
-
conditions.push(`"createdAt" <= $${++idx}`);
|
|
13154
|
-
queryParams.push(endDate.toISOString());
|
|
13155
|
-
}
|
|
13156
|
-
const tagFilter = buildTagFilters(tags, idx);
|
|
13157
|
-
conditions.push(...tagFilter.conditions);
|
|
13158
|
-
queryParams.push(...tagFilter.params);
|
|
13159
|
-
idx += tagFilter.params.length;
|
|
13160
|
-
const where = conditions.join(" AND ");
|
|
13161
|
-
const total = (await pool.query(`SELECT COUNT(*)::int AS "total" FROM "llm_requests" WHERE ${where}`, queryParams)).rows[0]?.total ?? 0;
|
|
13162
|
-
return {
|
|
13163
|
-
data: (await pool.query(`SELECT * FROM "llm_requests" WHERE ${where} ORDER BY "createdAt" DESC LIMIT $${++idx} OFFSET $${++idx}`, [
|
|
13164
|
-
...queryParams,
|
|
13165
|
-
limit,
|
|
13166
|
-
offset
|
|
13167
|
-
])).rows,
|
|
13168
|
-
total,
|
|
13169
|
-
limit,
|
|
13170
|
-
offset
|
|
13171
|
-
};
|
|
13172
|
-
},
|
|
13173
|
-
getRequestByRequestId: async (requestId) => {
|
|
13174
|
-
const { rows } = await pool.query(`SELECT * FROM "llm_requests" WHERE "requestId" = $1`, [requestId]);
|
|
13175
|
-
return rows[0] ?? void 0;
|
|
13176
|
-
},
|
|
13177
|
-
getTotalCost: async (params) => {
|
|
13178
|
-
const { startDate, endDate, configId, variantId, environmentId, tags } = params;
|
|
13179
|
-
const conditions = [`"createdAt" >= $1`, `"createdAt" <= $2`];
|
|
13180
|
-
const queryParams = [startDate.toISOString(), endDate.toISOString()];
|
|
13181
|
-
let idx = 2;
|
|
13182
|
-
if (configId) {
|
|
13183
|
-
conditions.push(`"configId" = $${++idx}`);
|
|
13184
|
-
queryParams.push(configId);
|
|
13185
|
-
}
|
|
13186
|
-
if (variantId) {
|
|
13187
|
-
conditions.push(`"variantId" = $${++idx}`);
|
|
13188
|
-
queryParams.push(variantId);
|
|
13189
|
-
}
|
|
13190
|
-
if (environmentId) {
|
|
13191
|
-
conditions.push(`"environmentId" = $${++idx}`);
|
|
13192
|
-
queryParams.push(environmentId);
|
|
13193
|
-
}
|
|
13194
|
-
const tagFilter = buildTagFilters(tags, idx);
|
|
13195
|
-
conditions.push(...tagFilter.conditions);
|
|
13196
|
-
queryParams.push(...tagFilter.params);
|
|
13197
|
-
const where = conditions.join(" AND ");
|
|
13198
|
-
const { rows } = await pool.query(`SELECT
|
|
13199
|
-
COALESCE(SUM("cost"), 0)::int AS "totalCost",
|
|
13200
|
-
COALESCE(SUM("inputCost"), 0)::int AS "totalInputCost",
|
|
13201
|
-
COALESCE(SUM("outputCost"), 0)::int AS "totalOutputCost",
|
|
13202
|
-
COALESCE(SUM("promptTokens"), 0)::int AS "totalPromptTokens",
|
|
13203
|
-
COALESCE(SUM("completionTokens"), 0)::int AS "totalCompletionTokens",
|
|
13204
|
-
COALESCE(SUM("totalTokens"), 0)::int AS "totalTokens",
|
|
13205
|
-
COALESCE(SUM("cachedTokens"), 0)::int AS "totalCachedTokens",
|
|
13206
|
-
COALESCE(SUM("cacheSavings"), 0)::int AS "totalCacheSavings",
|
|
13207
|
-
COUNT(*)::int AS "requestCount"
|
|
13208
|
-
FROM "llm_requests" WHERE ${where}`, queryParams);
|
|
13209
|
-
return rows[0];
|
|
13210
|
-
},
|
|
13211
|
-
getCostByModel: async (params) => {
|
|
13212
|
-
const { rows } = await pool.query(`SELECT "provider", "model",
|
|
13213
|
-
COALESCE(SUM("cost"), 0)::int AS "totalCost",
|
|
13214
|
-
COALESCE(SUM("inputCost"), 0)::int AS "totalInputCost",
|
|
13215
|
-
COALESCE(SUM("outputCost"), 0)::int AS "totalOutputCost",
|
|
13216
|
-
COALESCE(SUM("totalTokens"), 0)::int AS "totalTokens",
|
|
13217
|
-
COUNT(*)::int AS "requestCount",
|
|
13218
|
-
AVG("latencyMs") AS "avgLatencyMs"
|
|
13219
|
-
FROM "llm_requests"
|
|
13220
|
-
WHERE "createdAt" >= $1 AND "createdAt" <= $2
|
|
13221
|
-
GROUP BY "provider", "model"
|
|
13222
|
-
ORDER BY SUM("cost") DESC`, [params.startDate.toISOString(), params.endDate.toISOString()]);
|
|
13223
|
-
return rows;
|
|
13224
|
-
},
|
|
13225
|
-
getCostByProvider: async (params) => {
|
|
13226
|
-
const { rows } = await pool.query(`SELECT "provider",
|
|
13227
|
-
COALESCE(SUM("cost"), 0)::int AS "totalCost",
|
|
13228
|
-
COALESCE(SUM("inputCost"), 0)::int AS "totalInputCost",
|
|
13229
|
-
COALESCE(SUM("outputCost"), 0)::int AS "totalOutputCost",
|
|
13230
|
-
COALESCE(SUM("totalTokens"), 0)::int AS "totalTokens",
|
|
13231
|
-
COUNT(*)::int AS "requestCount",
|
|
13232
|
-
AVG("latencyMs") AS "avgLatencyMs"
|
|
13233
|
-
FROM "llm_requests"
|
|
13234
|
-
WHERE "createdAt" >= $1 AND "createdAt" <= $2
|
|
13235
|
-
GROUP BY "provider"
|
|
13236
|
-
ORDER BY SUM("cost") DESC`, [params.startDate.toISOString(), params.endDate.toISOString()]);
|
|
13237
|
-
return rows;
|
|
13238
|
-
},
|
|
13239
|
-
getDailyCosts: async (params) => {
|
|
13240
|
-
const { rows } = await pool.query(`SELECT DATE("createdAt")::text AS "date",
|
|
13241
|
-
COALESCE(SUM("cost"), 0)::int AS "totalCost",
|
|
13242
|
-
COALESCE(SUM("inputCost"), 0)::int AS "totalInputCost",
|
|
13243
|
-
COALESCE(SUM("outputCost"), 0)::int AS "totalOutputCost",
|
|
13244
|
-
COALESCE(SUM("totalTokens"), 0)::int AS "totalTokens",
|
|
13245
|
-
COUNT(*)::int AS "requestCount"
|
|
13246
|
-
FROM "llm_requests"
|
|
13247
|
-
WHERE "createdAt" >= $1 AND "createdAt" <= $2
|
|
13248
|
-
GROUP BY DATE("createdAt")
|
|
13249
|
-
ORDER BY DATE("createdAt") ASC`, [params.startDate.toISOString(), params.endDate.toISOString()]);
|
|
13250
|
-
return rows;
|
|
13251
|
-
},
|
|
13252
|
-
getCostSummary: async (params) => {
|
|
13253
|
-
const { startDate, endDate, groupBy, configId, variantId, environmentId, tags, tagKeys } = params;
|
|
13254
|
-
const baseParams = [
|
|
13255
|
-
startDate.toISOString(),
|
|
13256
|
-
endDate.toISOString(),
|
|
13257
|
-
configId ?? null,
|
|
13258
|
-
variantId ?? null,
|
|
13259
|
-
environmentId ?? null
|
|
13260
|
-
];
|
|
13261
|
-
if (groupBy === "tags") {
|
|
13262
|
-
const conditions = [
|
|
13263
|
-
`"createdAt" >= $1`,
|
|
13264
|
-
`"createdAt" <= $2`,
|
|
13265
|
-
`($3::uuid IS NULL OR "configId" = $3)`,
|
|
13266
|
-
`($4::uuid IS NULL OR "variantId" = $4)`,
|
|
13267
|
-
`($5::uuid IS NULL OR "environmentId" = $5)`
|
|
13268
|
-
];
|
|
13269
|
-
const queryParams = [...baseParams];
|
|
13270
|
-
let idx = 5;
|
|
13271
|
-
const tagFilter = buildTagFilters(tags, idx);
|
|
13272
|
-
conditions.push(...tagFilter.conditions);
|
|
13273
|
-
queryParams.push(...tagFilter.params);
|
|
13274
|
-
idx += tagFilter.params.length;
|
|
13275
|
-
if (tagKeys && tagKeys.length > 0) {
|
|
13276
|
-
const keyPlaceholders = tagKeys.map((_, i) => `$${idx + i + 1}`).join(", ");
|
|
13277
|
-
conditions.push(`t.key IN (${keyPlaceholders})`);
|
|
13278
|
-
queryParams.push(...tagKeys);
|
|
13279
|
-
}
|
|
13280
|
-
const where = conditions.join(" AND ");
|
|
13281
|
-
const { rows: rows$1 } = await pool.query(`SELECT t.key || ':' || t.value AS "groupKey",
|
|
13282
|
-
COALESCE(SUM("cost"), 0)::int AS "totalCost",
|
|
13283
|
-
COUNT(*)::int AS "requestCount"
|
|
13284
|
-
FROM "llm_requests", jsonb_each_text("tags") t
|
|
13285
|
-
WHERE ${where}
|
|
13286
|
-
GROUP BY t.key, t.value
|
|
13287
|
-
ORDER BY SUM("cost") DESC`, queryParams);
|
|
13288
|
-
return rows$1;
|
|
13289
|
-
}
|
|
13290
|
-
const sqlMap = {
|
|
13291
|
-
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`,
|
|
13292
|
-
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`,
|
|
13293
|
-
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`,
|
|
13294
|
-
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`,
|
|
13295
|
-
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`
|
|
13296
|
-
};
|
|
13297
|
-
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)`;
|
|
13298
|
-
const sql = groupBy ? sqlMap[groupBy] ?? totalSql : totalSql;
|
|
13299
|
-
const { rows } = await pool.query(sql, baseParams);
|
|
13300
|
-
return rows;
|
|
13301
|
-
},
|
|
13302
|
-
getRequestStats: async (params) => {
|
|
13303
|
-
const { startDate, endDate, configId, variantId, environmentId, tags } = params;
|
|
13304
|
-
const conditions = [`"createdAt" >= $1`, `"createdAt" <= $2`];
|
|
13305
|
-
const queryParams = [startDate.toISOString(), endDate.toISOString()];
|
|
13306
|
-
let idx = 2;
|
|
13307
|
-
if (configId) {
|
|
13308
|
-
conditions.push(`"configId" = $${++idx}`);
|
|
13309
|
-
queryParams.push(configId);
|
|
13310
|
-
}
|
|
13311
|
-
if (variantId) {
|
|
13312
|
-
conditions.push(`"variantId" = $${++idx}`);
|
|
13313
|
-
queryParams.push(variantId);
|
|
13314
|
-
}
|
|
13315
|
-
if (environmentId) {
|
|
13316
|
-
conditions.push(`"environmentId" = $${++idx}`);
|
|
13317
|
-
queryParams.push(environmentId);
|
|
13318
|
-
}
|
|
13319
|
-
const tagFilter = buildTagFilters(tags, idx);
|
|
13320
|
-
conditions.push(...tagFilter.conditions);
|
|
13321
|
-
queryParams.push(...tagFilter.params);
|
|
13322
|
-
const where = conditions.join(" AND ");
|
|
13323
|
-
const { rows } = await pool.query(`SELECT
|
|
13324
|
-
COUNT(*)::int AS "totalRequests",
|
|
13325
|
-
COUNT(CASE WHEN "statusCode">=200 AND "statusCode"<300 THEN 1 END)::int AS "successfulRequests",
|
|
13326
|
-
COUNT(CASE WHEN "statusCode">=400 THEN 1 END)::int AS "failedRequests",
|
|
13327
|
-
COUNT(CASE WHEN "isStreaming"=true THEN 1 END)::int AS "streamingRequests",
|
|
13328
|
-
AVG("latencyMs") AS "avgLatencyMs",
|
|
13329
|
-
MAX("latencyMs")::int AS "maxLatencyMs",
|
|
13330
|
-
MIN("latencyMs")::int AS "minLatencyMs"
|
|
13331
|
-
FROM "llm_requests" WHERE ${where}`, queryParams);
|
|
13332
|
-
return rows[0];
|
|
13333
|
-
},
|
|
13334
|
-
getDistinctTags: async () => {
|
|
13335
|
-
const { rows } = await pool.query(`SELECT DISTINCT key, value
|
|
13336
|
-
FROM "llm_requests", jsonb_each_text("tags") AS t(key, value)
|
|
13337
|
-
WHERE "tags" != '{}'::jsonb
|
|
13338
|
-
ORDER BY key, value`);
|
|
13339
|
-
return rows;
|
|
13340
|
-
}
|
|
13341
|
-
};
|
|
13342
|
-
}
|
|
13343
|
-
function createTracesStore(pool) {
|
|
13344
|
-
return {
|
|
13345
|
-
upsertTrace: async (data) => {
|
|
13346
|
-
const result = upsertTraceSchema.safeParse(data);
|
|
13347
|
-
if (!result.success) throw new Error(`Invalid trace data: ${result.error.message}`);
|
|
13348
|
-
const trace = result.data;
|
|
13349
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13350
|
-
await pool.query(`INSERT INTO "traces" (
|
|
13351
|
-
"id","traceId","name","sessionId","userId","status",
|
|
13352
|
-
"startTime","endTime","durationMs","spanCount",
|
|
13353
|
-
"totalInputTokens","totalOutputTokens","totalTokens","totalCost",
|
|
13354
|
-
"tags","metadata","createdAt","updatedAt"
|
|
13355
|
-
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15::jsonb,$16::jsonb,$17,$17)
|
|
13356
|
-
ON CONFLICT ("traceId") DO UPDATE SET
|
|
13357
|
-
"name" = COALESCE(EXCLUDED."name", "traces"."name"),
|
|
13358
|
-
"sessionId" = COALESCE(EXCLUDED."sessionId", "traces"."sessionId"),
|
|
13359
|
-
"userId" = COALESCE(EXCLUDED."userId", "traces"."userId"),
|
|
13360
|
-
"status" = CASE
|
|
13361
|
-
WHEN EXCLUDED."status" = 'error' THEN 'error'
|
|
13362
|
-
WHEN EXCLUDED."status" = 'ok' AND "traces"."status" != 'error' THEN 'ok'
|
|
13363
|
-
ELSE "traces"."status"
|
|
13364
|
-
END,
|
|
13365
|
-
"startTime" = LEAST("traces"."startTime", EXCLUDED."startTime"),
|
|
13366
|
-
"endTime" = GREATEST(
|
|
13367
|
-
COALESCE("traces"."endTime", EXCLUDED."endTime"),
|
|
13368
|
-
COALESCE(EXCLUDED."endTime", "traces"."endTime")
|
|
13369
|
-
),
|
|
13370
|
-
"durationMs" = EXTRACT(EPOCH FROM (
|
|
13371
|
-
GREATEST(COALESCE("traces"."endTime",EXCLUDED."endTime"),COALESCE(EXCLUDED."endTime","traces"."endTime")) -
|
|
13372
|
-
LEAST("traces"."startTime", EXCLUDED."startTime")
|
|
13373
|
-
))::integer * 1000,
|
|
13374
|
-
"spanCount" = "traces"."spanCount" + EXCLUDED."spanCount",
|
|
13375
|
-
"totalInputTokens" = "traces"."totalInputTokens" + EXCLUDED."totalInputTokens",
|
|
13376
|
-
"totalOutputTokens" = "traces"."totalOutputTokens" + EXCLUDED."totalOutputTokens",
|
|
13377
|
-
"totalTokens" = "traces"."totalTokens" + EXCLUDED."totalTokens",
|
|
13378
|
-
"totalCost" = "traces"."totalCost" + EXCLUDED."totalCost",
|
|
13379
|
-
"tags" = "traces"."tags" || EXCLUDED."tags",
|
|
13380
|
-
"metadata" = "traces"."metadata" || EXCLUDED."metadata",
|
|
13381
|
-
"updatedAt" = $17`, [
|
|
13382
|
-
randomUUID(),
|
|
13383
|
-
trace.traceId,
|
|
13384
|
-
trace.name ?? null,
|
|
13385
|
-
trace.sessionId ?? null,
|
|
13386
|
-
trace.userId ?? null,
|
|
13387
|
-
trace.status,
|
|
13388
|
-
trace.startTime.toISOString(),
|
|
13389
|
-
trace.endTime?.toISOString() ?? null,
|
|
13390
|
-
trace.durationMs ?? null,
|
|
13391
|
-
trace.spanCount,
|
|
13392
|
-
trace.totalInputTokens,
|
|
13393
|
-
trace.totalOutputTokens,
|
|
13394
|
-
trace.totalTokens,
|
|
13395
|
-
trace.totalCost,
|
|
13396
|
-
JSON.stringify(trace.tags),
|
|
13397
|
-
JSON.stringify(trace.metadata),
|
|
13398
|
-
now
|
|
13399
|
-
]);
|
|
13400
|
-
},
|
|
13401
|
-
batchInsertSpans: async (spans) => {
|
|
13402
|
-
if (spans.length === 0) return { count: 0 };
|
|
13403
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13404
|
-
const columns = [
|
|
13405
|
-
"id",
|
|
13406
|
-
"traceId",
|
|
13407
|
-
"spanId",
|
|
13408
|
-
"parentSpanId",
|
|
13409
|
-
"name",
|
|
13410
|
-
"kind",
|
|
13411
|
-
"status",
|
|
13412
|
-
"statusMessage",
|
|
13413
|
-
"startTime",
|
|
13414
|
-
"endTime",
|
|
13415
|
-
"durationMs",
|
|
13416
|
-
"provider",
|
|
13417
|
-
"model",
|
|
13418
|
-
"promptTokens",
|
|
13419
|
-
"completionTokens",
|
|
13420
|
-
"totalTokens",
|
|
13421
|
-
"cost",
|
|
13422
|
-
"configId",
|
|
13423
|
-
"variantId",
|
|
13424
|
-
"environmentId",
|
|
13425
|
-
"providerConfigId",
|
|
13426
|
-
"requestId",
|
|
13427
|
-
"source",
|
|
13428
|
-
"input",
|
|
13429
|
-
"output",
|
|
13430
|
-
"attributes",
|
|
13431
|
-
"createdAt",
|
|
13432
|
-
"updatedAt"
|
|
13433
|
-
];
|
|
13434
|
-
const colNames = columns.map((c) => `"${c}"`).join(", ");
|
|
13435
|
-
const params = [];
|
|
13436
|
-
const valueRows = [];
|
|
13437
|
-
for (const span of spans) {
|
|
13438
|
-
const result = insertSpanSchema.safeParse(span);
|
|
13439
|
-
if (!result.success) {
|
|
13440
|
-
logger.warn(`[batchInsertSpans] Skipping invalid span ${span.spanId}: ${result.error.message}`);
|
|
13441
|
-
continue;
|
|
13442
|
-
}
|
|
13443
|
-
const s = result.data;
|
|
13444
|
-
const offset = params.length;
|
|
13445
|
-
const placeholders = columns.map((_, i) => `$${offset + i + 1}`).join(", ");
|
|
13446
|
-
valueRows.push(`(${placeholders})`);
|
|
13447
|
-
params.push(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);
|
|
13448
|
-
}
|
|
13449
|
-
if (valueRows.length === 0) return { count: 0 };
|
|
13450
|
-
await pool.query(`INSERT INTO "spans" (${colNames}) VALUES ${valueRows.join(", ")} ON CONFLICT ("spanId") DO NOTHING`, params);
|
|
13451
|
-
return { count: valueRows.length };
|
|
13452
|
-
},
|
|
13453
|
-
batchInsertSpanEvents: async (events) => {
|
|
13454
|
-
if (events.length === 0) return { count: 0 };
|
|
13455
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13456
|
-
const columns = [
|
|
13457
|
-
"id",
|
|
13458
|
-
"traceId",
|
|
13459
|
-
"spanId",
|
|
13460
|
-
"name",
|
|
13461
|
-
"timestamp",
|
|
13462
|
-
"attributes",
|
|
13463
|
-
"createdAt"
|
|
13464
|
-
];
|
|
13465
|
-
const colNames = columns.map((c) => `"${c}"`).join(", ");
|
|
13466
|
-
const params = [];
|
|
13467
|
-
const valueRows = [];
|
|
13468
|
-
for (const event of events) {
|
|
13469
|
-
const result = insertSpanEventSchema.safeParse(event);
|
|
13470
|
-
if (!result.success) {
|
|
13471
|
-
logger.warn(`[batchInsertSpanEvents] Skipping invalid event: ${result.error.message}`);
|
|
13472
|
-
continue;
|
|
13473
|
-
}
|
|
13474
|
-
const e = result.data;
|
|
13475
|
-
const offset = params.length;
|
|
13476
|
-
const placeholders = columns.map((_, i) => `$${offset + i + 1}`).join(", ");
|
|
13477
|
-
valueRows.push(`(${placeholders})`);
|
|
13478
|
-
params.push(randomUUID(), e.traceId, e.spanId, e.name, e.timestamp.toISOString(), JSON.stringify(e.attributes), now);
|
|
13479
|
-
}
|
|
13480
|
-
if (valueRows.length === 0) return { count: 0 };
|
|
13481
|
-
await pool.query(`INSERT INTO "span_events" (${colNames}) VALUES ${valueRows.join(", ")}`, params);
|
|
13482
|
-
return { count: valueRows.length };
|
|
13483
|
-
},
|
|
13484
|
-
listTraces: async (params) => {
|
|
13485
|
-
const { limit = 50, offset = 0, sessionId, userId, status, name, startDate, endDate, tags } = params ?? {};
|
|
13486
|
-
const conditions = ["TRUE"];
|
|
13487
|
-
const queryParams = [];
|
|
13488
|
-
let idx = 0;
|
|
13489
|
-
if (sessionId) {
|
|
13490
|
-
conditions.push(`"sessionId" = $${++idx}`);
|
|
13491
|
-
queryParams.push(sessionId);
|
|
13492
|
-
}
|
|
13493
|
-
if (userId) {
|
|
13494
|
-
conditions.push(`"userId" = $${++idx}`);
|
|
13495
|
-
queryParams.push(userId);
|
|
13496
|
-
}
|
|
13497
|
-
if (status) {
|
|
13498
|
-
conditions.push(`"status" = $${++idx}`);
|
|
13499
|
-
queryParams.push(status);
|
|
13500
|
-
}
|
|
13501
|
-
if (name) {
|
|
13502
|
-
conditions.push(`"name" ILIKE $${++idx}`);
|
|
13503
|
-
queryParams.push(`%${name}%`);
|
|
13504
|
-
}
|
|
13505
|
-
if (startDate) {
|
|
13506
|
-
conditions.push(`"startTime" >= $${++idx}`);
|
|
13507
|
-
queryParams.push(startDate.toISOString());
|
|
13508
|
-
}
|
|
13509
|
-
if (endDate) {
|
|
13510
|
-
conditions.push(`"startTime" <= $${++idx}`);
|
|
13511
|
-
queryParams.push(endDate.toISOString());
|
|
13512
|
-
}
|
|
13513
|
-
const tagFilter = buildTagFilters(tags, idx);
|
|
13514
|
-
conditions.push(...tagFilter.conditions);
|
|
13515
|
-
queryParams.push(...tagFilter.params);
|
|
13516
|
-
idx += tagFilter.params.length;
|
|
13517
|
-
const where = conditions.join(" AND ");
|
|
13518
|
-
const total = (await pool.query(`SELECT COUNT(*)::int AS "total" FROM "traces" WHERE ${where}`, queryParams)).rows[0]?.total ?? 0;
|
|
13519
|
-
return {
|
|
13520
|
-
data: (await pool.query(`SELECT * FROM "traces" WHERE ${where} ORDER BY "startTime" DESC LIMIT $${++idx} OFFSET $${++idx}`, [
|
|
13521
|
-
...queryParams,
|
|
13522
|
-
limit,
|
|
13523
|
-
offset
|
|
13524
|
-
])).rows,
|
|
13525
|
-
total,
|
|
13526
|
-
limit,
|
|
13527
|
-
offset
|
|
13528
|
-
};
|
|
13529
|
-
},
|
|
13530
|
-
getTraceWithSpans: async (traceId) => {
|
|
13531
|
-
const trace = (await pool.query(`SELECT * FROM "traces" WHERE "traceId" = $1`, [traceId])).rows[0];
|
|
13532
|
-
if (!trace) return void 0;
|
|
13533
|
-
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])]);
|
|
13534
|
-
return {
|
|
13535
|
-
trace,
|
|
13536
|
-
spans: spanResult.rows,
|
|
13537
|
-
events: eventResult.rows
|
|
13538
|
-
};
|
|
13539
|
-
},
|
|
13540
|
-
getTraceStats: async (params) => {
|
|
13541
|
-
const { rows } = await pool.query(`SELECT
|
|
13542
|
-
COUNT(*)::int AS "totalTraces",
|
|
13543
|
-
COALESCE(AVG("durationMs"), 0) AS "avgDurationMs",
|
|
13544
|
-
COUNT(CASE WHEN "status" = 'error' THEN 1 END)::int AS "errorCount",
|
|
13545
|
-
COALESCE(SUM("totalCost"), 0)::int AS "totalCost",
|
|
13546
|
-
COALESCE(SUM("totalTokens"), 0)::int AS "totalTokens",
|
|
13547
|
-
COALESCE(SUM("spanCount"), 0)::int AS "totalSpans"
|
|
13548
|
-
FROM "traces"
|
|
13549
|
-
WHERE "startTime" >= $1 AND "startTime" <= $2
|
|
13550
|
-
AND ($3::varchar IS NULL OR "sessionId" = $3)
|
|
13551
|
-
AND ($4::varchar IS NULL OR "userId" = $4)`, [
|
|
13552
|
-
params.startDate.toISOString(),
|
|
13553
|
-
params.endDate.toISOString(),
|
|
13554
|
-
params.sessionId ?? null,
|
|
13555
|
-
params.userId ?? null
|
|
13556
|
-
]);
|
|
13557
|
-
return rows[0];
|
|
13558
|
-
}
|
|
13559
|
-
};
|
|
13560
|
-
}
|
|
13561
|
-
const pgStoreOptionsSchema = zod_default.object({ schema: zod_default.string().default("llmops") });
|
|
13562
|
-
/**
|
|
13563
|
-
* Create a PostgreSQL-backed telemetry store.
|
|
13564
|
-
*
|
|
13565
|
-
* Usage:
|
|
13566
|
-
* ```ts
|
|
13567
|
-
* import { llmops } from '@llmops/sdk'
|
|
13568
|
-
* import { pgStore } from '@llmops/sdk/store/pg'
|
|
13569
|
-
*
|
|
13570
|
-
* const ops = llmops({
|
|
13571
|
-
* telemetry: pgStore(process.env.DATABASE_URL),
|
|
13572
|
-
* })
|
|
13573
|
-
* ```
|
|
13574
|
-
*/
|
|
13575
|
-
function createPgStore(connectionString, options) {
|
|
13576
|
-
const parsed = zod_default.string().url().safeParse(connectionString);
|
|
13577
|
-
if (!parsed.success) throw new Error(`pgStore: invalid connection string — ${parsed.error.issues[0]?.message ?? "expected a postgres:// URL"}`);
|
|
13578
|
-
const { schema } = pgStoreOptionsSchema.parse(options ?? {});
|
|
13579
|
-
let pool;
|
|
13580
|
-
try {
|
|
13581
|
-
pool = new (__require("pg")).Pool({ connectionString });
|
|
13582
|
-
} catch {
|
|
13583
|
-
throw new Error("pgStore requires the \"pg\" package. Install it with: pnpm add pg");
|
|
13584
|
-
}
|
|
13585
|
-
pool.on("connect", (client) => {
|
|
13586
|
-
client.query(`SET search_path TO "${schema}"`);
|
|
13587
|
-
});
|
|
13588
|
-
logger.debug(`pgStore: initialized with schema "${schema}"`);
|
|
13589
|
-
return {
|
|
13590
|
-
...createLLMRequestsStore(pool),
|
|
13591
|
-
...createTracesStore(pool),
|
|
13592
|
-
_pool: pool,
|
|
13593
|
-
_schema: schema
|
|
13594
|
-
};
|
|
13595
|
-
}
|
|
13596
12988
|
|
|
13597
12989
|
//#endregion
|
|
13598
|
-
export {
|
|
12990
|
+
export { upsertTraceSchema as a, insertSpanSchema as i, insertLLMRequestSchema as n, zod_default as o, insertSpanEventSchema as r, __require as s, COST_SUMMARY_GROUP_BY as t };
|