@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.
@@ -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/pg-store.ts
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 { createPgStore as n, COST_SUMMARY_GROUP_BY as t };
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 };