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