@llmops/core 0.5.4 → 0.6.1-beta.1
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/{bun-sqlite-dialect-DQa87s1D.cjs → bun-sqlite-dialect-DFKD-iqI.cjs} +1 -1
- package/dist/db/index.cjs +5 -2
- package/dist/db/index.d.cts +2 -2
- package/dist/db/index.d.mts +2 -2
- package/dist/db/index.mjs +2 -2
- package/dist/{db-B9jJgYNw.cjs → db-C6ApWDjW.cjs} +310 -6
- package/dist/{db-DX_QaIkx.mjs → db-CQvUnGBp.mjs} +289 -3
- package/dist/{index-LRmy4sz9.d.mts → index-BosemZ_J.d.mts} +676 -1
- package/dist/{index-BtSgIKup.d.cts → index-DdG7GtcE.d.cts} +676 -1
- package/dist/index.cjs +382 -77
- package/dist/index.d.cts +254 -3
- package/dist/index.d.mts +254 -3
- package/dist/index.mjs +370 -77
- package/dist/{neon-dialect-BBGTCnek.cjs → neon-dialect-DNyVaL-1.cjs} +1 -1
- package/dist/{neon-dialect-BBWePgOv.cjs → neon-dialect-SqAJhPFS.cjs} +1 -1
- package/dist/{node-sqlite-dialect-DLKHH0RE.cjs → node-sqlite-dialect-DI0PJyHV.cjs} +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const require_db = require('./db-
|
|
2
|
-
const require_neon_dialect = require('./neon-dialect-
|
|
1
|
+
const require_db = require('./db-C6ApWDjW.cjs');
|
|
2
|
+
const require_neon_dialect = require('./neon-dialect-SqAJhPFS.cjs');
|
|
3
3
|
let __llmops_gateway = require("@llmops/gateway");
|
|
4
4
|
__llmops_gateway = require_db.__toESM(__llmops_gateway);
|
|
5
5
|
let kysely = require("kysely");
|
|
@@ -12,6 +12,21 @@ let node_crypto = require("node:crypto");
|
|
|
12
12
|
let json_logic_js = require("json-logic-js");
|
|
13
13
|
json_logic_js = require_db.__toESM(json_logic_js);
|
|
14
14
|
|
|
15
|
+
//#region src/constants/headers.ts
|
|
16
|
+
/**
|
|
17
|
+
* Custom HTTP headers used for trace context propagation
|
|
18
|
+
* between SDK clients and the LLMOps gateway.
|
|
19
|
+
*/
|
|
20
|
+
const LLMOPS_TRACE_ID_HEADER = "x-llmops-trace-id";
|
|
21
|
+
const LLMOPS_TRACE_NAME_HEADER = "x-llmops-trace-name";
|
|
22
|
+
const LLMOPS_SPAN_NAME_HEADER = "x-llmops-span-name";
|
|
23
|
+
const LLMOPS_SESSION_ID_HEADER = "x-llmops-session-id";
|
|
24
|
+
const LLMOPS_USER_ID_HEADER = "x-llmops-user-id";
|
|
25
|
+
const LLMOPS_SPAN_ID_HEADER = "x-llmops-span-id";
|
|
26
|
+
const LLMOPS_REQUEST_ID_HEADER = "x-llmops-request-id";
|
|
27
|
+
const LLMOPS_INTERNAL_HEADER = "x-llmops-internal";
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
15
30
|
//#region src/providers/supported-providers.ts
|
|
16
31
|
/**
|
|
17
32
|
* Supported providers - derived from @llmops/gateway providers
|
|
@@ -1998,7 +2013,11 @@ const insertLLMRequestSchema = require_db.zod_default.object({
|
|
|
1998
2013
|
isStreaming: require_db.zod_default.boolean().default(false),
|
|
1999
2014
|
userId: require_db.zod_default.string().nullable().optional(),
|
|
2000
2015
|
tags: require_db.zod_default.record(require_db.zod_default.string(), require_db.zod_default.string()).default({}),
|
|
2001
|
-
guardrailResults: guardrailResultsSchema.nullable().optional()
|
|
2016
|
+
guardrailResults: guardrailResultsSchema.nullable().optional(),
|
|
2017
|
+
traceId: require_db.zod_default.string().nullable().optional(),
|
|
2018
|
+
spanId: require_db.zod_default.string().nullable().optional(),
|
|
2019
|
+
parentSpanId: require_db.zod_default.string().nullable().optional(),
|
|
2020
|
+
sessionId: require_db.zod_default.string().nullable().optional()
|
|
2002
2021
|
});
|
|
2003
2022
|
/**
|
|
2004
2023
|
* Schema for listing LLM requests
|
|
@@ -2055,7 +2074,7 @@ const costSummarySchema = require_db.zod_default.object({
|
|
|
2055
2074
|
* Helper to create column reference for SQL
|
|
2056
2075
|
* Uses sql.ref() to properly quote column names for the database
|
|
2057
2076
|
*/
|
|
2058
|
-
const col = (name) => kysely.sql.ref(name);
|
|
2077
|
+
const col$1 = (name) => kysely.sql.ref(name);
|
|
2059
2078
|
const tableCol = (table, name) => kysely.sql.ref(`${table}.${name}`);
|
|
2060
2079
|
const createLLMRequestsDataLayer = (db) => {
|
|
2061
2080
|
return {
|
|
@@ -2090,6 +2109,10 @@ const createLLMRequestsDataLayer = (db) => {
|
|
|
2090
2109
|
userId: req.userId ?? null,
|
|
2091
2110
|
tags: JSON.stringify(req.tags),
|
|
2092
2111
|
guardrailResults: req.guardrailResults ? JSON.stringify(req.guardrailResults) : null,
|
|
2112
|
+
traceId: req.traceId ?? null,
|
|
2113
|
+
spanId: req.spanId ?? null,
|
|
2114
|
+
parentSpanId: req.parentSpanId ?? null,
|
|
2115
|
+
sessionId: req.sessionId ?? null,
|
|
2093
2116
|
createdAt: now,
|
|
2094
2117
|
updatedAt: now
|
|
2095
2118
|
}));
|
|
@@ -2124,6 +2147,10 @@ const createLLMRequestsDataLayer = (db) => {
|
|
|
2124
2147
|
userId: req.userId ?? null,
|
|
2125
2148
|
tags: JSON.stringify(req.tags),
|
|
2126
2149
|
guardrailResults: req.guardrailResults ? JSON.stringify(req.guardrailResults) : null,
|
|
2150
|
+
traceId: req.traceId ?? null,
|
|
2151
|
+
spanId: req.spanId ?? null,
|
|
2152
|
+
parentSpanId: req.parentSpanId ?? null,
|
|
2153
|
+
sessionId: req.sessionId ?? null,
|
|
2127
2154
|
createdAt: now,
|
|
2128
2155
|
updatedAt: now
|
|
2129
2156
|
}).returningAll().executeTakeFirst();
|
|
@@ -2139,14 +2166,14 @@ const createLLMRequestsDataLayer = (db) => {
|
|
|
2139
2166
|
if (providerConfigId) baseQuery = baseQuery.where("providerConfigId", "=", providerConfigId);
|
|
2140
2167
|
if (provider) baseQuery = baseQuery.where("provider", "=", provider);
|
|
2141
2168
|
if (model) baseQuery = baseQuery.where("model", "=", model);
|
|
2142
|
-
if (startDate) baseQuery = baseQuery.where(kysely.sql`${col("createdAt")} >= ${startDate.toISOString()}`);
|
|
2143
|
-
if (endDate) baseQuery = baseQuery.where(kysely.sql`${col("createdAt")} <= ${endDate.toISOString()}`);
|
|
2169
|
+
if (startDate) baseQuery = baseQuery.where(kysely.sql`${col$1("createdAt")} >= ${startDate.toISOString()}`);
|
|
2170
|
+
if (endDate) baseQuery = baseQuery.where(kysely.sql`${col$1("createdAt")} <= ${endDate.toISOString()}`);
|
|
2144
2171
|
if (tags && Object.keys(tags).length > 0) for (const [key, values] of Object.entries(tags)) {
|
|
2145
2172
|
if (values.length === 0) continue;
|
|
2146
|
-
if (values.length === 1) baseQuery = baseQuery.where(kysely.sql`${col("tags")}->>${key} = ${values[0]}`);
|
|
2173
|
+
if (values.length === 1) baseQuery = baseQuery.where(kysely.sql`${col$1("tags")}->>${key} = ${values[0]}`);
|
|
2147
2174
|
else {
|
|
2148
2175
|
const valueList = kysely.sql.join(values.map((v) => kysely.sql`${v}`));
|
|
2149
|
-
baseQuery = baseQuery.where(kysely.sql`${col("tags")}->>${key} IN (${valueList})`);
|
|
2176
|
+
baseQuery = baseQuery.where(kysely.sql`${col$1("tags")}->>${key} IN (${valueList})`);
|
|
2150
2177
|
}
|
|
2151
2178
|
}
|
|
2152
2179
|
const countResult = await baseQuery.select(kysely.sql`COUNT(*)`.as("total")).executeTakeFirst();
|
|
@@ -2166,23 +2193,23 @@ const createLLMRequestsDataLayer = (db) => {
|
|
|
2166
2193
|
if (!result.success) throw new LLMOpsError(`Invalid parameters: ${result.error.message}`);
|
|
2167
2194
|
const { startDate, endDate, configId, variantId, environmentId, tags } = result.data;
|
|
2168
2195
|
let query = db.selectFrom("llm_requests").select([
|
|
2169
|
-
kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
|
|
2170
|
-
kysely.sql`COALESCE(SUM(${col("inputCost")}), 0)`.as("totalInputCost"),
|
|
2171
|
-
kysely.sql`COALESCE(SUM(${col("outputCost")}), 0)`.as("totalOutputCost"),
|
|
2172
|
-
kysely.sql`COALESCE(SUM(${col("promptTokens")}), 0)`.as("totalPromptTokens"),
|
|
2173
|
-
kysely.sql`COALESCE(SUM(${col("completionTokens")}), 0)`.as("totalCompletionTokens"),
|
|
2174
|
-
kysely.sql`COALESCE(SUM(${col("totalTokens")}), 0)`.as("totalTokens"),
|
|
2196
|
+
kysely.sql`COALESCE(SUM(${col$1("cost")}), 0)`.as("totalCost"),
|
|
2197
|
+
kysely.sql`COALESCE(SUM(${col$1("inputCost")}), 0)`.as("totalInputCost"),
|
|
2198
|
+
kysely.sql`COALESCE(SUM(${col$1("outputCost")}), 0)`.as("totalOutputCost"),
|
|
2199
|
+
kysely.sql`COALESCE(SUM(${col$1("promptTokens")}), 0)`.as("totalPromptTokens"),
|
|
2200
|
+
kysely.sql`COALESCE(SUM(${col$1("completionTokens")}), 0)`.as("totalCompletionTokens"),
|
|
2201
|
+
kysely.sql`COALESCE(SUM(${col$1("totalTokens")}), 0)`.as("totalTokens"),
|
|
2175
2202
|
kysely.sql`COUNT(*)`.as("requestCount")
|
|
2176
|
-
]).where(kysely.sql`${col("createdAt")} >= ${startDate.toISOString()}`).where(kysely.sql`${col("createdAt")} <= ${endDate.toISOString()}`);
|
|
2203
|
+
]).where(kysely.sql`${col$1("createdAt")} >= ${startDate.toISOString()}`).where(kysely.sql`${col$1("createdAt")} <= ${endDate.toISOString()}`);
|
|
2177
2204
|
if (configId) query = query.where("configId", "=", configId);
|
|
2178
2205
|
if (variantId) query = query.where("variantId", "=", variantId);
|
|
2179
2206
|
if (environmentId) query = query.where("environmentId", "=", environmentId);
|
|
2180
2207
|
if (tags && Object.keys(tags).length > 0) for (const [key, values] of Object.entries(tags)) {
|
|
2181
2208
|
if (values.length === 0) continue;
|
|
2182
|
-
if (values.length === 1) query = query.where(kysely.sql`${col("tags")}->>${key} = ${values[0]}`);
|
|
2209
|
+
if (values.length === 1) query = query.where(kysely.sql`${col$1("tags")}->>${key} = ${values[0]}`);
|
|
2183
2210
|
else {
|
|
2184
2211
|
const valueList = kysely.sql.join(values.map((v) => kysely.sql`${v}`));
|
|
2185
|
-
query = query.where(kysely.sql`${col("tags")}->>${key} IN (${valueList})`);
|
|
2212
|
+
query = query.where(kysely.sql`${col$1("tags")}->>${key} IN (${valueList})`);
|
|
2186
2213
|
}
|
|
2187
2214
|
}
|
|
2188
2215
|
return await query.executeTakeFirst();
|
|
@@ -2194,13 +2221,13 @@ const createLLMRequestsDataLayer = (db) => {
|
|
|
2194
2221
|
return db.selectFrom("llm_requests").select([
|
|
2195
2222
|
"provider",
|
|
2196
2223
|
"model",
|
|
2197
|
-
kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
|
|
2198
|
-
kysely.sql`COALESCE(SUM(${col("inputCost")}), 0)`.as("totalInputCost"),
|
|
2199
|
-
kysely.sql`COALESCE(SUM(${col("outputCost")}), 0)`.as("totalOutputCost"),
|
|
2200
|
-
kysely.sql`COALESCE(SUM(${col("totalTokens")}), 0)`.as("totalTokens"),
|
|
2224
|
+
kysely.sql`COALESCE(SUM(${col$1("cost")}), 0)`.as("totalCost"),
|
|
2225
|
+
kysely.sql`COALESCE(SUM(${col$1("inputCost")}), 0)`.as("totalInputCost"),
|
|
2226
|
+
kysely.sql`COALESCE(SUM(${col$1("outputCost")}), 0)`.as("totalOutputCost"),
|
|
2227
|
+
kysely.sql`COALESCE(SUM(${col$1("totalTokens")}), 0)`.as("totalTokens"),
|
|
2201
2228
|
kysely.sql`COUNT(*)`.as("requestCount"),
|
|
2202
|
-
kysely.sql`AVG(${col("latencyMs")})`.as("avgLatencyMs")
|
|
2203
|
-
]).where(kysely.sql`${col("createdAt")} >= ${startDate.toISOString()}`).where(kysely.sql`${col("createdAt")} <= ${endDate.toISOString()}`).groupBy(["provider", "model"]).orderBy(kysely.sql`SUM(${col("cost")})`, "desc").execute();
|
|
2229
|
+
kysely.sql`AVG(${col$1("latencyMs")})`.as("avgLatencyMs")
|
|
2230
|
+
]).where(kysely.sql`${col$1("createdAt")} >= ${startDate.toISOString()}`).where(kysely.sql`${col$1("createdAt")} <= ${endDate.toISOString()}`).groupBy(["provider", "model"]).orderBy(kysely.sql`SUM(${col$1("cost")})`, "desc").execute();
|
|
2204
2231
|
},
|
|
2205
2232
|
getCostByProvider: async (params) => {
|
|
2206
2233
|
const result = await dateRangeSchema.safeParseAsync(params);
|
|
@@ -2208,13 +2235,13 @@ const createLLMRequestsDataLayer = (db) => {
|
|
|
2208
2235
|
const { startDate, endDate } = result.data;
|
|
2209
2236
|
return db.selectFrom("llm_requests").select([
|
|
2210
2237
|
"provider",
|
|
2211
|
-
kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
|
|
2212
|
-
kysely.sql`COALESCE(SUM(${col("inputCost")}), 0)`.as("totalInputCost"),
|
|
2213
|
-
kysely.sql`COALESCE(SUM(${col("outputCost")}), 0)`.as("totalOutputCost"),
|
|
2214
|
-
kysely.sql`COALESCE(SUM(${col("totalTokens")}), 0)`.as("totalTokens"),
|
|
2238
|
+
kysely.sql`COALESCE(SUM(${col$1("cost")}), 0)`.as("totalCost"),
|
|
2239
|
+
kysely.sql`COALESCE(SUM(${col$1("inputCost")}), 0)`.as("totalInputCost"),
|
|
2240
|
+
kysely.sql`COALESCE(SUM(${col$1("outputCost")}), 0)`.as("totalOutputCost"),
|
|
2241
|
+
kysely.sql`COALESCE(SUM(${col$1("totalTokens")}), 0)`.as("totalTokens"),
|
|
2215
2242
|
kysely.sql`COUNT(*)`.as("requestCount"),
|
|
2216
|
-
kysely.sql`AVG(${col("latencyMs")})`.as("avgLatencyMs")
|
|
2217
|
-
]).where(kysely.sql`${col("createdAt")} >= ${startDate.toISOString()}`).where(kysely.sql`${col("createdAt")} <= ${endDate.toISOString()}`).groupBy("provider").orderBy(kysely.sql`SUM(${col("cost")})`, "desc").execute();
|
|
2243
|
+
kysely.sql`AVG(${col$1("latencyMs")})`.as("avgLatencyMs")
|
|
2244
|
+
]).where(kysely.sql`${col$1("createdAt")} >= ${startDate.toISOString()}`).where(kysely.sql`${col$1("createdAt")} <= ${endDate.toISOString()}`).groupBy("provider").orderBy(kysely.sql`SUM(${col$1("cost")})`, "desc").execute();
|
|
2218
2245
|
},
|
|
2219
2246
|
getCostByConfig: async (params) => {
|
|
2220
2247
|
const result = await dateRangeSchema.safeParseAsync(params);
|
|
@@ -2240,90 +2267,90 @@ const createLLMRequestsDataLayer = (db) => {
|
|
|
2240
2267
|
if (!result.success) throw new LLMOpsError(`Invalid parameters: ${result.error.message}`);
|
|
2241
2268
|
const { startDate, endDate } = result.data;
|
|
2242
2269
|
return db.selectFrom("llm_requests").select([
|
|
2243
|
-
kysely.sql`DATE(${col("createdAt")})`.as("date"),
|
|
2244
|
-
kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
|
|
2245
|
-
kysely.sql`COALESCE(SUM(${col("inputCost")}), 0)`.as("totalInputCost"),
|
|
2246
|
-
kysely.sql`COALESCE(SUM(${col("outputCost")}), 0)`.as("totalOutputCost"),
|
|
2247
|
-
kysely.sql`COALESCE(SUM(${col("totalTokens")}), 0)`.as("totalTokens"),
|
|
2270
|
+
kysely.sql`DATE(${col$1("createdAt")})`.as("date"),
|
|
2271
|
+
kysely.sql`COALESCE(SUM(${col$1("cost")}), 0)`.as("totalCost"),
|
|
2272
|
+
kysely.sql`COALESCE(SUM(${col$1("inputCost")}), 0)`.as("totalInputCost"),
|
|
2273
|
+
kysely.sql`COALESCE(SUM(${col$1("outputCost")}), 0)`.as("totalOutputCost"),
|
|
2274
|
+
kysely.sql`COALESCE(SUM(${col$1("totalTokens")}), 0)`.as("totalTokens"),
|
|
2248
2275
|
kysely.sql`COUNT(*)`.as("requestCount")
|
|
2249
|
-
]).where(kysely.sql`${col("createdAt")} >= ${startDate.toISOString()}`).where(kysely.sql`${col("createdAt")} <= ${endDate.toISOString()}`).groupBy(kysely.sql`DATE(${col("createdAt")})`).orderBy(kysely.sql`DATE(${col("createdAt")})`, "asc").execute();
|
|
2276
|
+
]).where(kysely.sql`${col$1("createdAt")} >= ${startDate.toISOString()}`).where(kysely.sql`${col$1("createdAt")} <= ${endDate.toISOString()}`).groupBy(kysely.sql`DATE(${col$1("createdAt")})`).orderBy(kysely.sql`DATE(${col$1("createdAt")})`, "asc").execute();
|
|
2250
2277
|
},
|
|
2251
2278
|
getCostSummary: async (params) => {
|
|
2252
2279
|
const result = await costSummarySchema.safeParseAsync(params);
|
|
2253
2280
|
if (!result.success) throw new LLMOpsError(`Invalid parameters: ${result.error.message}`);
|
|
2254
2281
|
const { startDate, endDate, groupBy, configId, variantId, environmentId, tags } = result.data;
|
|
2255
|
-
let baseQuery = db.selectFrom("llm_requests").where(kysely.sql`${col("createdAt")} >= ${startDate.toISOString()}`).where(kysely.sql`${col("createdAt")} <= ${endDate.toISOString()}`);
|
|
2282
|
+
let baseQuery = db.selectFrom("llm_requests").where(kysely.sql`${col$1("createdAt")} >= ${startDate.toISOString()}`).where(kysely.sql`${col$1("createdAt")} <= ${endDate.toISOString()}`);
|
|
2256
2283
|
if (configId) baseQuery = baseQuery.where("configId", "=", configId);
|
|
2257
2284
|
if (variantId) baseQuery = baseQuery.where("variantId", "=", variantId);
|
|
2258
2285
|
if (environmentId) baseQuery = baseQuery.where("environmentId", "=", environmentId);
|
|
2259
2286
|
if (tags && Object.keys(tags).length > 0) for (const [key, values] of Object.entries(tags)) {
|
|
2260
2287
|
if (values.length === 0) continue;
|
|
2261
|
-
if (values.length === 1) baseQuery = baseQuery.where(kysely.sql`${col("tags")}->>${key} = ${values[0]}`);
|
|
2288
|
+
if (values.length === 1) baseQuery = baseQuery.where(kysely.sql`${col$1("tags")}->>${key} = ${values[0]}`);
|
|
2262
2289
|
else {
|
|
2263
2290
|
const valueList = kysely.sql.join(values.map((v) => kysely.sql`${v}`));
|
|
2264
|
-
baseQuery = baseQuery.where(kysely.sql`${col("tags")}->>${key} IN (${valueList})`);
|
|
2291
|
+
baseQuery = baseQuery.where(kysely.sql`${col$1("tags")}->>${key} IN (${valueList})`);
|
|
2265
2292
|
}
|
|
2266
2293
|
}
|
|
2267
2294
|
switch (groupBy) {
|
|
2268
2295
|
case "day": return baseQuery.select([
|
|
2269
|
-
kysely.sql`DATE(${col("createdAt")})`.as("groupKey"),
|
|
2270
|
-
kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
|
|
2296
|
+
kysely.sql`DATE(${col$1("createdAt")})`.as("groupKey"),
|
|
2297
|
+
kysely.sql`COALESCE(SUM(${col$1("cost")}), 0)`.as("totalCost"),
|
|
2271
2298
|
kysely.sql`COUNT(*)`.as("requestCount"),
|
|
2272
|
-
kysely.sql`COALESCE(SUM(${col("totalTokens")}), 0)`.as("totalTokens")
|
|
2273
|
-
]).groupBy(kysely.sql`DATE(${col("createdAt")})`).orderBy(kysely.sql`DATE(${col("createdAt")})`, "asc").execute();
|
|
2299
|
+
kysely.sql`COALESCE(SUM(${col$1("totalTokens")}), 0)`.as("totalTokens")
|
|
2300
|
+
]).groupBy(kysely.sql`DATE(${col$1("createdAt")})`).orderBy(kysely.sql`DATE(${col$1("createdAt")})`, "asc").execute();
|
|
2274
2301
|
case "hour": return baseQuery.select([
|
|
2275
|
-
kysely.sql`DATE_TRUNC('hour', ${col("createdAt")})`.as("groupKey"),
|
|
2276
|
-
kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
|
|
2302
|
+
kysely.sql`DATE_TRUNC('hour', ${col$1("createdAt")})`.as("groupKey"),
|
|
2303
|
+
kysely.sql`COALESCE(SUM(${col$1("cost")}), 0)`.as("totalCost"),
|
|
2277
2304
|
kysely.sql`COUNT(*)`.as("requestCount"),
|
|
2278
|
-
kysely.sql`COALESCE(SUM(${col("totalTokens")}), 0)`.as("totalTokens")
|
|
2279
|
-
]).groupBy(kysely.sql`DATE_TRUNC('hour', ${col("createdAt")})`).orderBy(kysely.sql`DATE_TRUNC('hour', ${col("createdAt")})`, "asc").execute();
|
|
2305
|
+
kysely.sql`COALESCE(SUM(${col$1("totalTokens")}), 0)`.as("totalTokens")
|
|
2306
|
+
]).groupBy(kysely.sql`DATE_TRUNC('hour', ${col$1("createdAt")})`).orderBy(kysely.sql`DATE_TRUNC('hour', ${col$1("createdAt")})`, "asc").execute();
|
|
2280
2307
|
case "model": return baseQuery.select([
|
|
2281
|
-
kysely.sql`${col("provider")} || '/' || ${col("model")}`.as("groupKey"),
|
|
2282
|
-
kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
|
|
2308
|
+
kysely.sql`${col$1("provider")} || '/' || ${col$1("model")}`.as("groupKey"),
|
|
2309
|
+
kysely.sql`COALESCE(SUM(${col$1("cost")}), 0)`.as("totalCost"),
|
|
2283
2310
|
kysely.sql`COUNT(*)`.as("requestCount")
|
|
2284
|
-
]).groupBy(["provider", "model"]).orderBy(kysely.sql`SUM(${col("cost")})`, "desc").execute();
|
|
2311
|
+
]).groupBy(["provider", "model"]).orderBy(kysely.sql`SUM(${col$1("cost")})`, "desc").execute();
|
|
2285
2312
|
case "provider": return baseQuery.select([
|
|
2286
|
-
kysely.sql`${col("provider")}`.as("groupKey"),
|
|
2287
|
-
kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
|
|
2313
|
+
kysely.sql`${col$1("provider")}`.as("groupKey"),
|
|
2314
|
+
kysely.sql`COALESCE(SUM(${col$1("cost")}), 0)`.as("totalCost"),
|
|
2288
2315
|
kysely.sql`COUNT(*)`.as("requestCount")
|
|
2289
|
-
]).groupBy("provider").orderBy(kysely.sql`SUM(${col("cost")})`, "desc").execute();
|
|
2316
|
+
]).groupBy("provider").orderBy(kysely.sql`SUM(${col$1("cost")})`, "desc").execute();
|
|
2290
2317
|
case "config": return baseQuery.select([
|
|
2291
|
-
kysely.sql`COALESCE(${col("configId")}::text, 'no-config')`.as("groupKey"),
|
|
2292
|
-
kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
|
|
2318
|
+
kysely.sql`COALESCE(${col$1("configId")}::text, 'no-config')`.as("groupKey"),
|
|
2319
|
+
kysely.sql`COALESCE(SUM(${col$1("cost")}), 0)`.as("totalCost"),
|
|
2293
2320
|
kysely.sql`COUNT(*)`.as("requestCount")
|
|
2294
|
-
]).groupBy("configId").orderBy(kysely.sql`SUM(${col("cost")})`, "desc").execute();
|
|
2321
|
+
]).groupBy("configId").orderBy(kysely.sql`SUM(${col$1("cost")})`, "desc").execute();
|
|
2295
2322
|
case "endpoint": return baseQuery.select([
|
|
2296
|
-
kysely.sql`COALESCE(${col("endpoint")}, 'unknown')`.as("groupKey"),
|
|
2297
|
-
kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
|
|
2323
|
+
kysely.sql`COALESCE(${col$1("endpoint")}, 'unknown')`.as("groupKey"),
|
|
2324
|
+
kysely.sql`COALESCE(SUM(${col$1("cost")}), 0)`.as("totalCost"),
|
|
2298
2325
|
kysely.sql`COUNT(*)`.as("requestCount")
|
|
2299
|
-
]).groupBy("endpoint").orderBy(kysely.sql`SUM(${col("cost")})`, "desc").execute();
|
|
2326
|
+
]).groupBy("endpoint").orderBy(kysely.sql`SUM(${col$1("cost")})`, "desc").execute();
|
|
2300
2327
|
case "tags": {
|
|
2301
|
-
const conditions = [kysely.sql`${col("createdAt")} >= ${startDate.toISOString()}`, kysely.sql`${col("createdAt")} <= ${endDate.toISOString()}`];
|
|
2302
|
-
if (configId) conditions.push(kysely.sql`${col("configId")} = ${configId}`);
|
|
2303
|
-
if (variantId) conditions.push(kysely.sql`${col("variantId")} = ${variantId}`);
|
|
2304
|
-
if (environmentId) conditions.push(kysely.sql`${col("environmentId")} = ${environmentId}`);
|
|
2328
|
+
const conditions = [kysely.sql`${col$1("createdAt")} >= ${startDate.toISOString()}`, kysely.sql`${col$1("createdAt")} <= ${endDate.toISOString()}`];
|
|
2329
|
+
if (configId) conditions.push(kysely.sql`${col$1("configId")} = ${configId}`);
|
|
2330
|
+
if (variantId) conditions.push(kysely.sql`${col$1("variantId")} = ${variantId}`);
|
|
2331
|
+
if (environmentId) conditions.push(kysely.sql`${col$1("environmentId")} = ${environmentId}`);
|
|
2305
2332
|
if (tags && Object.keys(tags).length > 0) for (const [key, values] of Object.entries(tags)) {
|
|
2306
2333
|
if (values.length === 0) continue;
|
|
2307
|
-
if (values.length === 1) conditions.push(kysely.sql`${col("tags")}->>${key} = ${values[0]}`);
|
|
2334
|
+
if (values.length === 1) conditions.push(kysely.sql`${col$1("tags")}->>${key} = ${values[0]}`);
|
|
2308
2335
|
else {
|
|
2309
2336
|
const valueList = kysely.sql.join(values.map((v) => kysely.sql`${v}`));
|
|
2310
|
-
conditions.push(kysely.sql`${col("tags")}->>${key} IN (${valueList})`);
|
|
2337
|
+
conditions.push(kysely.sql`${col$1("tags")}->>${key} IN (${valueList})`);
|
|
2311
2338
|
}
|
|
2312
2339
|
}
|
|
2313
2340
|
const whereClause = kysely.sql.join(conditions, kysely.sql` AND `);
|
|
2314
2341
|
return (await kysely.sql`
|
|
2315
2342
|
SELECT t.key || ':' || t.value as "groupKey",
|
|
2316
|
-
COALESCE(SUM(${col("cost")}), 0) as "totalCost",
|
|
2343
|
+
COALESCE(SUM(${col$1("cost")}), 0) as "totalCost",
|
|
2317
2344
|
COUNT(*) as "requestCount"
|
|
2318
|
-
FROM "llm_requests", jsonb_each_text(${col("tags")}) t
|
|
2345
|
+
FROM "llm_requests", jsonb_each_text(${col$1("tags")}) t
|
|
2319
2346
|
WHERE ${whereClause}
|
|
2320
2347
|
GROUP BY t.key, t.value
|
|
2321
|
-
ORDER BY SUM(${col("cost")}) DESC
|
|
2348
|
+
ORDER BY SUM(${col$1("cost")}) DESC
|
|
2322
2349
|
`.execute(db)).rows;
|
|
2323
2350
|
}
|
|
2324
2351
|
default: return baseQuery.select([
|
|
2325
2352
|
kysely.sql`'total'`.as("groupKey"),
|
|
2326
|
-
kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
|
|
2353
|
+
kysely.sql`COALESCE(SUM(${col$1("cost")}), 0)`.as("totalCost"),
|
|
2327
2354
|
kysely.sql`COUNT(*)`.as("requestCount")
|
|
2328
2355
|
]).execute();
|
|
2329
2356
|
}
|
|
@@ -2334,22 +2361,22 @@ const createLLMRequestsDataLayer = (db) => {
|
|
|
2334
2361
|
const { startDate, endDate, configId, variantId, environmentId, tags } = result.data;
|
|
2335
2362
|
let query = db.selectFrom("llm_requests").select([
|
|
2336
2363
|
kysely.sql`COUNT(*)`.as("totalRequests"),
|
|
2337
|
-
kysely.sql`COUNT(CASE WHEN ${col("statusCode")} >= 200 AND ${col("statusCode")} < 300 THEN 1 END)`.as("successfulRequests"),
|
|
2338
|
-
kysely.sql`COUNT(CASE WHEN ${col("statusCode")} >= 400 THEN 1 END)`.as("failedRequests"),
|
|
2339
|
-
kysely.sql`COUNT(CASE WHEN ${col("isStreaming")} = true THEN 1 END)`.as("streamingRequests"),
|
|
2340
|
-
kysely.sql`AVG(${col("latencyMs")})`.as("avgLatencyMs"),
|
|
2341
|
-
kysely.sql`MAX(${col("latencyMs")})`.as("maxLatencyMs"),
|
|
2342
|
-
kysely.sql`MIN(${col("latencyMs")})`.as("minLatencyMs")
|
|
2343
|
-
]).where(kysely.sql`${col("createdAt")} >= ${startDate.toISOString()}`).where(kysely.sql`${col("createdAt")} <= ${endDate.toISOString()}`);
|
|
2364
|
+
kysely.sql`COUNT(CASE WHEN ${col$1("statusCode")} >= 200 AND ${col$1("statusCode")} < 300 THEN 1 END)`.as("successfulRequests"),
|
|
2365
|
+
kysely.sql`COUNT(CASE WHEN ${col$1("statusCode")} >= 400 THEN 1 END)`.as("failedRequests"),
|
|
2366
|
+
kysely.sql`COUNT(CASE WHEN ${col$1("isStreaming")} = true THEN 1 END)`.as("streamingRequests"),
|
|
2367
|
+
kysely.sql`AVG(${col$1("latencyMs")})`.as("avgLatencyMs"),
|
|
2368
|
+
kysely.sql`MAX(${col$1("latencyMs")})`.as("maxLatencyMs"),
|
|
2369
|
+
kysely.sql`MIN(${col$1("latencyMs")})`.as("minLatencyMs")
|
|
2370
|
+
]).where(kysely.sql`${col$1("createdAt")} >= ${startDate.toISOString()}`).where(kysely.sql`${col$1("createdAt")} <= ${endDate.toISOString()}`);
|
|
2344
2371
|
if (configId) query = query.where("configId", "=", configId);
|
|
2345
2372
|
if (variantId) query = query.where("variantId", "=", variantId);
|
|
2346
2373
|
if (environmentId) query = query.where("environmentId", "=", environmentId);
|
|
2347
2374
|
if (tags && Object.keys(tags).length > 0) for (const [key, values] of Object.entries(tags)) {
|
|
2348
2375
|
if (values.length === 0) continue;
|
|
2349
|
-
if (values.length === 1) query = query.where(kysely.sql`${col("tags")}->>${key} = ${values[0]}`);
|
|
2376
|
+
if (values.length === 1) query = query.where(kysely.sql`${col$1("tags")}->>${key} = ${values[0]}`);
|
|
2350
2377
|
else {
|
|
2351
2378
|
const valueList = kysely.sql.join(values.map((v) => kysely.sql`${v}`));
|
|
2352
|
-
query = query.where(kysely.sql`${col("tags")}->>${key} IN (${valueList})`);
|
|
2379
|
+
query = query.where(kysely.sql`${col$1("tags")}->>${key} IN (${valueList})`);
|
|
2353
2380
|
}
|
|
2354
2381
|
}
|
|
2355
2382
|
return await query.executeTakeFirst();
|
|
@@ -3091,6 +3118,271 @@ const createTargetingRulesDataLayer = (db) => {
|
|
|
3091
3118
|
};
|
|
3092
3119
|
};
|
|
3093
3120
|
|
|
3121
|
+
//#endregion
|
|
3122
|
+
//#region src/datalayer/traces.ts
|
|
3123
|
+
const col = (name) => kysely.sql.ref(name);
|
|
3124
|
+
/**
|
|
3125
|
+
* Schema for upserting a trace
|
|
3126
|
+
*/
|
|
3127
|
+
const upsertTraceSchema = require_db.zod_default.object({
|
|
3128
|
+
traceId: require_db.zod_default.string(),
|
|
3129
|
+
name: require_db.zod_default.string().nullable().optional(),
|
|
3130
|
+
sessionId: require_db.zod_default.string().nullable().optional(),
|
|
3131
|
+
userId: require_db.zod_default.string().nullable().optional(),
|
|
3132
|
+
status: require_db.zod_default.enum([
|
|
3133
|
+
"unset",
|
|
3134
|
+
"ok",
|
|
3135
|
+
"error"
|
|
3136
|
+
]).default("unset"),
|
|
3137
|
+
startTime: require_db.zod_default.date(),
|
|
3138
|
+
endTime: require_db.zod_default.date().nullable().optional(),
|
|
3139
|
+
durationMs: require_db.zod_default.number().int().nullable().optional(),
|
|
3140
|
+
spanCount: require_db.zod_default.number().int().default(1),
|
|
3141
|
+
totalInputTokens: require_db.zod_default.number().int().default(0),
|
|
3142
|
+
totalOutputTokens: require_db.zod_default.number().int().default(0),
|
|
3143
|
+
totalTokens: require_db.zod_default.number().int().default(0),
|
|
3144
|
+
totalCost: require_db.zod_default.number().int().default(0),
|
|
3145
|
+
tags: require_db.zod_default.record(require_db.zod_default.string(), require_db.zod_default.string()).default({}),
|
|
3146
|
+
metadata: require_db.zod_default.record(require_db.zod_default.string(), require_db.zod_default.unknown()).default({})
|
|
3147
|
+
});
|
|
3148
|
+
/**
|
|
3149
|
+
* Schema for inserting spans
|
|
3150
|
+
*/
|
|
3151
|
+
const insertSpanSchema = require_db.zod_default.object({
|
|
3152
|
+
traceId: require_db.zod_default.string(),
|
|
3153
|
+
spanId: require_db.zod_default.string(),
|
|
3154
|
+
parentSpanId: require_db.zod_default.string().nullable().optional(),
|
|
3155
|
+
name: require_db.zod_default.string(),
|
|
3156
|
+
kind: require_db.zod_default.number().int().default(1),
|
|
3157
|
+
status: require_db.zod_default.number().int().default(0),
|
|
3158
|
+
statusMessage: require_db.zod_default.string().nullable().optional(),
|
|
3159
|
+
startTime: require_db.zod_default.date(),
|
|
3160
|
+
endTime: require_db.zod_default.date().nullable().optional(),
|
|
3161
|
+
durationMs: require_db.zod_default.number().int().nullable().optional(),
|
|
3162
|
+
provider: require_db.zod_default.string().nullable().optional(),
|
|
3163
|
+
model: require_db.zod_default.string().nullable().optional(),
|
|
3164
|
+
promptTokens: require_db.zod_default.number().int().default(0),
|
|
3165
|
+
completionTokens: require_db.zod_default.number().int().default(0),
|
|
3166
|
+
totalTokens: require_db.zod_default.number().int().default(0),
|
|
3167
|
+
cost: require_db.zod_default.number().int().default(0),
|
|
3168
|
+
configId: require_db.zod_default.string().uuid().nullable().optional(),
|
|
3169
|
+
variantId: require_db.zod_default.string().uuid().nullable().optional(),
|
|
3170
|
+
environmentId: require_db.zod_default.string().uuid().nullable().optional(),
|
|
3171
|
+
providerConfigId: require_db.zod_default.string().uuid().nullable().optional(),
|
|
3172
|
+
requestId: require_db.zod_default.string().uuid().nullable().optional(),
|
|
3173
|
+
source: require_db.zod_default.enum(["gateway", "otlp"]).default("gateway"),
|
|
3174
|
+
input: require_db.zod_default.unknown().nullable().optional(),
|
|
3175
|
+
output: require_db.zod_default.unknown().nullable().optional(),
|
|
3176
|
+
attributes: require_db.zod_default.record(require_db.zod_default.string(), require_db.zod_default.unknown()).default({})
|
|
3177
|
+
});
|
|
3178
|
+
/**
|
|
3179
|
+
* Schema for inserting span events
|
|
3180
|
+
*/
|
|
3181
|
+
const insertSpanEventSchema = require_db.zod_default.object({
|
|
3182
|
+
traceId: require_db.zod_default.string(),
|
|
3183
|
+
spanId: require_db.zod_default.string(),
|
|
3184
|
+
name: require_db.zod_default.string(),
|
|
3185
|
+
timestamp: require_db.zod_default.date(),
|
|
3186
|
+
attributes: require_db.zod_default.record(require_db.zod_default.string(), require_db.zod_default.unknown()).default({})
|
|
3187
|
+
});
|
|
3188
|
+
/**
|
|
3189
|
+
* Schema for listing traces
|
|
3190
|
+
*/
|
|
3191
|
+
const listTracesSchema = require_db.zod_default.object({
|
|
3192
|
+
limit: require_db.zod_default.number().int().positive().max(1e3).default(50),
|
|
3193
|
+
offset: require_db.zod_default.number().int().nonnegative().default(0),
|
|
3194
|
+
sessionId: require_db.zod_default.string().optional(),
|
|
3195
|
+
userId: require_db.zod_default.string().optional(),
|
|
3196
|
+
status: require_db.zod_default.enum([
|
|
3197
|
+
"unset",
|
|
3198
|
+
"ok",
|
|
3199
|
+
"error"
|
|
3200
|
+
]).optional(),
|
|
3201
|
+
name: require_db.zod_default.string().optional(),
|
|
3202
|
+
startDate: require_db.zod_default.date().optional(),
|
|
3203
|
+
endDate: require_db.zod_default.date().optional(),
|
|
3204
|
+
tags: require_db.zod_default.record(require_db.zod_default.string(), require_db.zod_default.array(require_db.zod_default.string())).optional()
|
|
3205
|
+
});
|
|
3206
|
+
/**
|
|
3207
|
+
* Schema for trace stats query
|
|
3208
|
+
*/
|
|
3209
|
+
const traceStatsSchema = require_db.zod_default.object({
|
|
3210
|
+
startDate: require_db.zod_default.date(),
|
|
3211
|
+
endDate: require_db.zod_default.date(),
|
|
3212
|
+
sessionId: require_db.zod_default.string().optional(),
|
|
3213
|
+
userId: require_db.zod_default.string().optional()
|
|
3214
|
+
});
|
|
3215
|
+
const createTracesDataLayer = (db) => {
|
|
3216
|
+
return {
|
|
3217
|
+
upsertTrace: async (data) => {
|
|
3218
|
+
const result = await upsertTraceSchema.safeParseAsync(data);
|
|
3219
|
+
if (!result.success) throw new LLMOpsError(`Invalid trace data: ${result.error.message}`);
|
|
3220
|
+
const trace = result.data;
|
|
3221
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3222
|
+
await kysely.sql`
|
|
3223
|
+
INSERT INTO "traces" (
|
|
3224
|
+
"id", "traceId", "name", "sessionId", "userId", "status",
|
|
3225
|
+
"startTime", "endTime", "durationMs", "spanCount",
|
|
3226
|
+
"totalInputTokens", "totalOutputTokens", "totalTokens", "totalCost",
|
|
3227
|
+
"tags", "metadata", "createdAt", "updatedAt"
|
|
3228
|
+
) VALUES (
|
|
3229
|
+
${(0, node_crypto.randomUUID)()}, ${trace.traceId}, ${trace.name ?? null}, ${trace.sessionId ?? null},
|
|
3230
|
+
${trace.userId ?? null}, ${trace.status},
|
|
3231
|
+
${trace.startTime.toISOString()}, ${trace.endTime?.toISOString() ?? null},
|
|
3232
|
+
${trace.durationMs ?? null}, ${trace.spanCount},
|
|
3233
|
+
${trace.totalInputTokens}, ${trace.totalOutputTokens},
|
|
3234
|
+
${trace.totalTokens}, ${trace.totalCost},
|
|
3235
|
+
${JSON.stringify(trace.tags)}::jsonb, ${JSON.stringify(trace.metadata)}::jsonb,
|
|
3236
|
+
${now}, ${now}
|
|
3237
|
+
)
|
|
3238
|
+
ON CONFLICT ("traceId") DO UPDATE SET
|
|
3239
|
+
"name" = COALESCE(EXCLUDED."name", "traces"."name"),
|
|
3240
|
+
"sessionId" = COALESCE(EXCLUDED."sessionId", "traces"."sessionId"),
|
|
3241
|
+
"userId" = COALESCE(EXCLUDED."userId", "traces"."userId"),
|
|
3242
|
+
"status" = CASE
|
|
3243
|
+
WHEN EXCLUDED."status" = 'error' THEN 'error'
|
|
3244
|
+
WHEN EXCLUDED."status" = 'ok' AND "traces"."status" != 'error' THEN 'ok'
|
|
3245
|
+
ELSE "traces"."status"
|
|
3246
|
+
END,
|
|
3247
|
+
"startTime" = LEAST("traces"."startTime", EXCLUDED."startTime"),
|
|
3248
|
+
"endTime" = GREATEST(
|
|
3249
|
+
COALESCE("traces"."endTime", EXCLUDED."endTime"),
|
|
3250
|
+
COALESCE(EXCLUDED."endTime", "traces"."endTime")
|
|
3251
|
+
),
|
|
3252
|
+
"durationMs" = EXTRACT(EPOCH FROM (
|
|
3253
|
+
GREATEST(
|
|
3254
|
+
COALESCE("traces"."endTime", EXCLUDED."endTime"),
|
|
3255
|
+
COALESCE(EXCLUDED."endTime", "traces"."endTime")
|
|
3256
|
+
) -
|
|
3257
|
+
LEAST("traces"."startTime", EXCLUDED."startTime")
|
|
3258
|
+
))::integer * 1000,
|
|
3259
|
+
"spanCount" = "traces"."spanCount" + EXCLUDED."spanCount",
|
|
3260
|
+
"totalInputTokens" = "traces"."totalInputTokens" + EXCLUDED."totalInputTokens",
|
|
3261
|
+
"totalOutputTokens" = "traces"."totalOutputTokens" + EXCLUDED."totalOutputTokens",
|
|
3262
|
+
"totalTokens" = "traces"."totalTokens" + EXCLUDED."totalTokens",
|
|
3263
|
+
"totalCost" = "traces"."totalCost" + EXCLUDED."totalCost",
|
|
3264
|
+
"tags" = "traces"."tags" || EXCLUDED."tags",
|
|
3265
|
+
"metadata" = "traces"."metadata" || EXCLUDED."metadata",
|
|
3266
|
+
"updatedAt" = ${now}
|
|
3267
|
+
`.execute(db);
|
|
3268
|
+
},
|
|
3269
|
+
batchInsertSpans: async (spans) => {
|
|
3270
|
+
if (spans.length === 0) return { count: 0 };
|
|
3271
|
+
const validatedSpans = await Promise.all(spans.map(async (span) => {
|
|
3272
|
+
const result = await insertSpanSchema.safeParseAsync(span);
|
|
3273
|
+
if (!result.success) throw new LLMOpsError(`Invalid span data: ${result.error.message}`);
|
|
3274
|
+
return result.data;
|
|
3275
|
+
}));
|
|
3276
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3277
|
+
const values = validatedSpans.map((span) => ({
|
|
3278
|
+
id: (0, node_crypto.randomUUID)(),
|
|
3279
|
+
traceId: span.traceId,
|
|
3280
|
+
spanId: span.spanId,
|
|
3281
|
+
parentSpanId: span.parentSpanId ?? null,
|
|
3282
|
+
name: span.name,
|
|
3283
|
+
kind: span.kind,
|
|
3284
|
+
status: span.status,
|
|
3285
|
+
statusMessage: span.statusMessage ?? null,
|
|
3286
|
+
startTime: span.startTime.toISOString(),
|
|
3287
|
+
endTime: span.endTime?.toISOString() ?? null,
|
|
3288
|
+
durationMs: span.durationMs ?? null,
|
|
3289
|
+
provider: span.provider ?? null,
|
|
3290
|
+
model: span.model ?? null,
|
|
3291
|
+
promptTokens: span.promptTokens,
|
|
3292
|
+
completionTokens: span.completionTokens,
|
|
3293
|
+
totalTokens: span.totalTokens,
|
|
3294
|
+
cost: span.cost,
|
|
3295
|
+
configId: span.configId ?? null,
|
|
3296
|
+
variantId: span.variantId ?? null,
|
|
3297
|
+
environmentId: span.environmentId ?? null,
|
|
3298
|
+
providerConfigId: span.providerConfigId ?? null,
|
|
3299
|
+
requestId: span.requestId ?? null,
|
|
3300
|
+
source: span.source,
|
|
3301
|
+
input: span.input != null ? JSON.stringify(span.input) : null,
|
|
3302
|
+
output: span.output != null ? JSON.stringify(span.output) : null,
|
|
3303
|
+
attributes: JSON.stringify(span.attributes),
|
|
3304
|
+
createdAt: now,
|
|
3305
|
+
updatedAt: now
|
|
3306
|
+
}));
|
|
3307
|
+
await db.insertInto("spans").values(values).onConflict((oc) => oc.column("spanId").doNothing()).execute();
|
|
3308
|
+
return { count: values.length };
|
|
3309
|
+
},
|
|
3310
|
+
batchInsertSpanEvents: async (events) => {
|
|
3311
|
+
if (events.length === 0) return { count: 0 };
|
|
3312
|
+
const validatedEvents = await Promise.all(events.map(async (event) => {
|
|
3313
|
+
const result = await insertSpanEventSchema.safeParseAsync(event);
|
|
3314
|
+
if (!result.success) throw new LLMOpsError(`Invalid span event data: ${result.error.message}`);
|
|
3315
|
+
return result.data;
|
|
3316
|
+
}));
|
|
3317
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3318
|
+
const values = validatedEvents.map((event) => ({
|
|
3319
|
+
id: (0, node_crypto.randomUUID)(),
|
|
3320
|
+
traceId: event.traceId,
|
|
3321
|
+
spanId: event.spanId,
|
|
3322
|
+
name: event.name,
|
|
3323
|
+
timestamp: event.timestamp.toISOString(),
|
|
3324
|
+
attributes: JSON.stringify(event.attributes),
|
|
3325
|
+
createdAt: now
|
|
3326
|
+
}));
|
|
3327
|
+
await db.insertInto("span_events").values(values).execute();
|
|
3328
|
+
return { count: values.length };
|
|
3329
|
+
},
|
|
3330
|
+
listTraces: async (params) => {
|
|
3331
|
+
const result = await listTracesSchema.safeParseAsync(params || {});
|
|
3332
|
+
if (!result.success) throw new LLMOpsError(`Invalid parameters: ${result.error.message}`);
|
|
3333
|
+
const { limit, offset, sessionId, userId, status, name, startDate, endDate, tags } = result.data;
|
|
3334
|
+
let baseQuery = db.selectFrom("traces");
|
|
3335
|
+
if (sessionId) baseQuery = baseQuery.where("sessionId", "=", sessionId);
|
|
3336
|
+
if (userId) baseQuery = baseQuery.where("userId", "=", userId);
|
|
3337
|
+
if (status) baseQuery = baseQuery.where("status", "=", status);
|
|
3338
|
+
if (name) baseQuery = baseQuery.where(kysely.sql`${col("name")} ILIKE ${"%" + name + "%"}`);
|
|
3339
|
+
if (startDate) baseQuery = baseQuery.where(kysely.sql`${col("startTime")} >= ${startDate.toISOString()}`);
|
|
3340
|
+
if (endDate) baseQuery = baseQuery.where(kysely.sql`${col("startTime")} <= ${endDate.toISOString()}`);
|
|
3341
|
+
if (tags && Object.keys(tags).length > 0) for (const [key, values] of Object.entries(tags)) {
|
|
3342
|
+
if (values.length === 0) continue;
|
|
3343
|
+
if (values.length === 1) baseQuery = baseQuery.where(kysely.sql`${col("tags")}->>${key} = ${values[0]}`);
|
|
3344
|
+
else {
|
|
3345
|
+
const valueList = kysely.sql.join(values.map((v) => kysely.sql`${v}`));
|
|
3346
|
+
baseQuery = baseQuery.where(kysely.sql`${col("tags")}->>${key} IN (${valueList})`);
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
3349
|
+
const countResult = await baseQuery.select(kysely.sql`COUNT(*)`.as("total")).executeTakeFirst();
|
|
3350
|
+
const total = Number(countResult?.total ?? 0);
|
|
3351
|
+
return {
|
|
3352
|
+
data: await baseQuery.selectAll().orderBy("startTime", "desc").limit(limit).offset(offset).execute(),
|
|
3353
|
+
total,
|
|
3354
|
+
limit,
|
|
3355
|
+
offset
|
|
3356
|
+
};
|
|
3357
|
+
},
|
|
3358
|
+
getTraceWithSpans: async (traceId) => {
|
|
3359
|
+
const trace = await db.selectFrom("traces").selectAll().where("traceId", "=", traceId).executeTakeFirst();
|
|
3360
|
+
if (!trace) return void 0;
|
|
3361
|
+
return {
|
|
3362
|
+
trace,
|
|
3363
|
+
spans: await db.selectFrom("spans").selectAll().where("traceId", "=", traceId).orderBy("startTime", "asc").execute(),
|
|
3364
|
+
events: await db.selectFrom("span_events").selectAll().where("traceId", "=", traceId).orderBy("timestamp", "asc").execute()
|
|
3365
|
+
};
|
|
3366
|
+
},
|
|
3367
|
+
getTraceStats: async (params) => {
|
|
3368
|
+
const result = await traceStatsSchema.safeParseAsync(params);
|
|
3369
|
+
if (!result.success) throw new LLMOpsError(`Invalid parameters: ${result.error.message}`);
|
|
3370
|
+
const { startDate, endDate, sessionId, userId } = result.data;
|
|
3371
|
+
let query = db.selectFrom("traces").select([
|
|
3372
|
+
kysely.sql`COUNT(*)`.as("totalTraces"),
|
|
3373
|
+
kysely.sql`COALESCE(AVG(${col("durationMs")}), 0)`.as("avgDurationMs"),
|
|
3374
|
+
kysely.sql`COUNT(CASE WHEN ${col("status")} = 'error' THEN 1 END)`.as("errorCount"),
|
|
3375
|
+
kysely.sql`COALESCE(SUM(${col("totalCost")}), 0)`.as("totalCost"),
|
|
3376
|
+
kysely.sql`COALESCE(SUM(${col("totalTokens")}), 0)`.as("totalTokens"),
|
|
3377
|
+
kysely.sql`COALESCE(SUM(${col("spanCount")}), 0)`.as("totalSpans")
|
|
3378
|
+
]).where(kysely.sql`${col("startTime")} >= ${startDate.toISOString()}`).where(kysely.sql`${col("startTime")} <= ${endDate.toISOString()}`);
|
|
3379
|
+
if (sessionId) query = query.where("sessionId", "=", sessionId);
|
|
3380
|
+
if (userId) query = query.where("userId", "=", userId);
|
|
3381
|
+
return query.executeTakeFirst();
|
|
3382
|
+
}
|
|
3383
|
+
};
|
|
3384
|
+
};
|
|
3385
|
+
|
|
3094
3386
|
//#endregion
|
|
3095
3387
|
//#region src/datalayer/variants.ts
|
|
3096
3388
|
const createVariant = require_db.zod_default.object({ name: require_db.zod_default.string() });
|
|
@@ -3384,6 +3676,7 @@ function createDataLayer(db) {
|
|
|
3384
3676
|
...createProviderConfigsDataLayer(db),
|
|
3385
3677
|
...createProviderGuardrailOverridesDataLayer(db),
|
|
3386
3678
|
...createTargetingRulesDataLayer(db),
|
|
3679
|
+
...createTracesDataLayer(db),
|
|
3387
3680
|
...createVariantDataLayer(db),
|
|
3388
3681
|
...createVariantVersionsDataLayer(db),
|
|
3389
3682
|
...createWorkspaceSettingsDataLayer(db)
|
|
@@ -3966,6 +4259,14 @@ exports.COST_SUMMARY_GROUP_BY = COST_SUMMARY_GROUP_BY;
|
|
|
3966
4259
|
exports.CacheService = CacheService;
|
|
3967
4260
|
exports.DEFAULT_PROVIDER_ENV_VARS = DEFAULT_PROVIDER_ENV_VARS;
|
|
3968
4261
|
exports.FileCacheBackend = FileCacheBackend;
|
|
4262
|
+
exports.LLMOPS_INTERNAL_HEADER = LLMOPS_INTERNAL_HEADER;
|
|
4263
|
+
exports.LLMOPS_REQUEST_ID_HEADER = LLMOPS_REQUEST_ID_HEADER;
|
|
4264
|
+
exports.LLMOPS_SESSION_ID_HEADER = LLMOPS_SESSION_ID_HEADER;
|
|
4265
|
+
exports.LLMOPS_SPAN_ID_HEADER = LLMOPS_SPAN_ID_HEADER;
|
|
4266
|
+
exports.LLMOPS_SPAN_NAME_HEADER = LLMOPS_SPAN_NAME_HEADER;
|
|
4267
|
+
exports.LLMOPS_TRACE_ID_HEADER = LLMOPS_TRACE_ID_HEADER;
|
|
4268
|
+
exports.LLMOPS_TRACE_NAME_HEADER = LLMOPS_TRACE_NAME_HEADER;
|
|
4269
|
+
exports.LLMOPS_USER_ID_HEADER = LLMOPS_USER_ID_HEADER;
|
|
3969
4270
|
exports.MS = MS;
|
|
3970
4271
|
exports.ManifestBuilder = ManifestBuilder;
|
|
3971
4272
|
exports.ManifestRouter = ManifestRouter;
|
|
@@ -3995,6 +4296,7 @@ exports.createPlaygroundRunsDataLayer = createPlaygroundRunsDataLayer;
|
|
|
3995
4296
|
exports.createProviderConfigsDataLayer = createProviderConfigsDataLayer;
|
|
3996
4297
|
exports.createProviderGuardrailOverridesDataLayer = createProviderGuardrailOverridesDataLayer;
|
|
3997
4298
|
exports.createTargetingRulesDataLayer = createTargetingRulesDataLayer;
|
|
4299
|
+
exports.createTracesDataLayer = createTracesDataLayer;
|
|
3998
4300
|
exports.createVariantDataLayer = createVariantDataLayer;
|
|
3999
4301
|
exports.createVariantVersionsDataLayer = createVariantVersionsDataLayer;
|
|
4000
4302
|
exports.createWorkspaceSettingsDataLayer = createWorkspaceSettingsDataLayer;
|
|
@@ -4036,7 +4338,10 @@ exports.providerConfigsSchema = require_db.providerConfigsSchema;
|
|
|
4036
4338
|
exports.providerGuardrailOverridesSchema = require_db.providerGuardrailOverridesSchema;
|
|
4037
4339
|
exports.runAutoMigrations = require_db.runAutoMigrations;
|
|
4038
4340
|
exports.schemas = require_db.schemas;
|
|
4341
|
+
exports.spanEventsSchema = require_db.spanEventsSchema;
|
|
4342
|
+
exports.spansSchema = require_db.spansSchema;
|
|
4039
4343
|
exports.targetingRulesSchema = require_db.targetingRulesSchema;
|
|
4344
|
+
exports.tracesSchema = require_db.tracesSchema;
|
|
4040
4345
|
exports.validateLLMOpsConfig = validateLLMOpsConfig;
|
|
4041
4346
|
exports.validatePartialTableData = require_db.validatePartialTableData;
|
|
4042
4347
|
exports.validateTableData = require_db.validateTableData;
|