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