@llmops/core 0.5.2-beta.1 → 0.5.3-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 +411 -72
- package/dist/index.d.cts +265 -8
- package/dist/index.d.mts +265 -8
- package/dist/index.mjs +399 -72
- 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
|
|
@@ -2028,6 +2046,18 @@ const dateRangeSchema = require_db.zod_default.object({
|
|
|
2028
2046
|
tags: require_db.zod_default.record(require_db.zod_default.string(), require_db.zod_default.array(require_db.zod_default.string())).optional()
|
|
2029
2047
|
});
|
|
2030
2048
|
/**
|
|
2049
|
+
* Valid groupBy values for cost summary queries
|
|
2050
|
+
*/
|
|
2051
|
+
const COST_SUMMARY_GROUP_BY = [
|
|
2052
|
+
"day",
|
|
2053
|
+
"hour",
|
|
2054
|
+
"model",
|
|
2055
|
+
"provider",
|
|
2056
|
+
"config",
|
|
2057
|
+
"endpoint",
|
|
2058
|
+
"tags"
|
|
2059
|
+
];
|
|
2060
|
+
/**
|
|
2031
2061
|
* Schema for cost summary with grouping
|
|
2032
2062
|
*/
|
|
2033
2063
|
const costSummarySchema = require_db.zod_default.object({
|
|
@@ -2037,19 +2067,13 @@ const costSummarySchema = require_db.zod_default.object({
|
|
|
2037
2067
|
variantId: require_db.zod_default.string().uuid().optional(),
|
|
2038
2068
|
environmentId: require_db.zod_default.string().uuid().optional(),
|
|
2039
2069
|
tags: require_db.zod_default.record(require_db.zod_default.string(), require_db.zod_default.array(require_db.zod_default.string())).optional(),
|
|
2040
|
-
groupBy: require_db.zod_default.enum(
|
|
2041
|
-
"day",
|
|
2042
|
-
"hour",
|
|
2043
|
-
"model",
|
|
2044
|
-
"provider",
|
|
2045
|
-
"config"
|
|
2046
|
-
]).optional()
|
|
2070
|
+
groupBy: require_db.zod_default.enum(COST_SUMMARY_GROUP_BY).optional()
|
|
2047
2071
|
});
|
|
2048
2072
|
/**
|
|
2049
2073
|
* Helper to create column reference for SQL
|
|
2050
2074
|
* Uses sql.ref() to properly quote column names for the database
|
|
2051
2075
|
*/
|
|
2052
|
-
const col = (name) => kysely.sql.ref(name);
|
|
2076
|
+
const col$1 = (name) => kysely.sql.ref(name);
|
|
2053
2077
|
const tableCol = (table, name) => kysely.sql.ref(`${table}.${name}`);
|
|
2054
2078
|
const createLLMRequestsDataLayer = (db) => {
|
|
2055
2079
|
return {
|
|
@@ -2084,6 +2108,10 @@ const createLLMRequestsDataLayer = (db) => {
|
|
|
2084
2108
|
userId: req.userId ?? null,
|
|
2085
2109
|
tags: JSON.stringify(req.tags),
|
|
2086
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,
|
|
2087
2115
|
createdAt: now,
|
|
2088
2116
|
updatedAt: now
|
|
2089
2117
|
}));
|
|
@@ -2118,6 +2146,10 @@ const createLLMRequestsDataLayer = (db) => {
|
|
|
2118
2146
|
userId: req.userId ?? null,
|
|
2119
2147
|
tags: JSON.stringify(req.tags),
|
|
2120
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,
|
|
2121
2153
|
createdAt: now,
|
|
2122
2154
|
updatedAt: now
|
|
2123
2155
|
}).returningAll().executeTakeFirst();
|
|
@@ -2133,14 +2165,14 @@ const createLLMRequestsDataLayer = (db) => {
|
|
|
2133
2165
|
if (providerConfigId) baseQuery = baseQuery.where("providerConfigId", "=", providerConfigId);
|
|
2134
2166
|
if (provider) baseQuery = baseQuery.where("provider", "=", provider);
|
|
2135
2167
|
if (model) baseQuery = baseQuery.where("model", "=", model);
|
|
2136
|
-
if (startDate) baseQuery = baseQuery.where(kysely.sql`${col("createdAt")} >= ${startDate.toISOString()}`);
|
|
2137
|
-
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()}`);
|
|
2138
2170
|
if (tags && Object.keys(tags).length > 0) for (const [key, values] of Object.entries(tags)) {
|
|
2139
2171
|
if (values.length === 0) continue;
|
|
2140
|
-
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]}`);
|
|
2141
2173
|
else {
|
|
2142
2174
|
const valueList = kysely.sql.join(values.map((v) => kysely.sql`${v}`));
|
|
2143
|
-
baseQuery = baseQuery.where(kysely.sql`${col("tags")}->>${key} IN (${valueList})`);
|
|
2175
|
+
baseQuery = baseQuery.where(kysely.sql`${col$1("tags")}->>${key} IN (${valueList})`);
|
|
2144
2176
|
}
|
|
2145
2177
|
}
|
|
2146
2178
|
const countResult = await baseQuery.select(kysely.sql`COUNT(*)`.as("total")).executeTakeFirst();
|
|
@@ -2160,23 +2192,23 @@ const createLLMRequestsDataLayer = (db) => {
|
|
|
2160
2192
|
if (!result.success) throw new LLMOpsError(`Invalid parameters: ${result.error.message}`);
|
|
2161
2193
|
const { startDate, endDate, configId, variantId, environmentId, tags } = result.data;
|
|
2162
2194
|
let query = db.selectFrom("llm_requests").select([
|
|
2163
|
-
kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
|
|
2164
|
-
kysely.sql`COALESCE(SUM(${col("inputCost")}), 0)`.as("totalInputCost"),
|
|
2165
|
-
kysely.sql`COALESCE(SUM(${col("outputCost")}), 0)`.as("totalOutputCost"),
|
|
2166
|
-
kysely.sql`COALESCE(SUM(${col("promptTokens")}), 0)`.as("totalPromptTokens"),
|
|
2167
|
-
kysely.sql`COALESCE(SUM(${col("completionTokens")}), 0)`.as("totalCompletionTokens"),
|
|
2168
|
-
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"),
|
|
2169
2201
|
kysely.sql`COUNT(*)`.as("requestCount")
|
|
2170
|
-
]).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()}`);
|
|
2171
2203
|
if (configId) query = query.where("configId", "=", configId);
|
|
2172
2204
|
if (variantId) query = query.where("variantId", "=", variantId);
|
|
2173
2205
|
if (environmentId) query = query.where("environmentId", "=", environmentId);
|
|
2174
2206
|
if (tags && Object.keys(tags).length > 0) for (const [key, values] of Object.entries(tags)) {
|
|
2175
2207
|
if (values.length === 0) continue;
|
|
2176
|
-
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]}`);
|
|
2177
2209
|
else {
|
|
2178
2210
|
const valueList = kysely.sql.join(values.map((v) => kysely.sql`${v}`));
|
|
2179
|
-
query = query.where(kysely.sql`${col("tags")}->>${key} IN (${valueList})`);
|
|
2211
|
+
query = query.where(kysely.sql`${col$1("tags")}->>${key} IN (${valueList})`);
|
|
2180
2212
|
}
|
|
2181
2213
|
}
|
|
2182
2214
|
return await query.executeTakeFirst();
|
|
@@ -2188,13 +2220,13 @@ const createLLMRequestsDataLayer = (db) => {
|
|
|
2188
2220
|
return db.selectFrom("llm_requests").select([
|
|
2189
2221
|
"provider",
|
|
2190
2222
|
"model",
|
|
2191
|
-
kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
|
|
2192
|
-
kysely.sql`COALESCE(SUM(${col("inputCost")}), 0)`.as("totalInputCost"),
|
|
2193
|
-
kysely.sql`COALESCE(SUM(${col("outputCost")}), 0)`.as("totalOutputCost"),
|
|
2194
|
-
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"),
|
|
2195
2227
|
kysely.sql`COUNT(*)`.as("requestCount"),
|
|
2196
|
-
kysely.sql`AVG(${col("latencyMs")})`.as("avgLatencyMs")
|
|
2197
|
-
]).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();
|
|
2198
2230
|
},
|
|
2199
2231
|
getCostByProvider: async (params) => {
|
|
2200
2232
|
const result = await dateRangeSchema.safeParseAsync(params);
|
|
@@ -2202,13 +2234,13 @@ const createLLMRequestsDataLayer = (db) => {
|
|
|
2202
2234
|
const { startDate, endDate } = result.data;
|
|
2203
2235
|
return db.selectFrom("llm_requests").select([
|
|
2204
2236
|
"provider",
|
|
2205
|
-
kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
|
|
2206
|
-
kysely.sql`COALESCE(SUM(${col("inputCost")}), 0)`.as("totalInputCost"),
|
|
2207
|
-
kysely.sql`COALESCE(SUM(${col("outputCost")}), 0)`.as("totalOutputCost"),
|
|
2208
|
-
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"),
|
|
2209
2241
|
kysely.sql`COUNT(*)`.as("requestCount"),
|
|
2210
|
-
kysely.sql`AVG(${col("latencyMs")})`.as("avgLatencyMs")
|
|
2211
|
-
]).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();
|
|
2212
2244
|
},
|
|
2213
2245
|
getCostByConfig: async (params) => {
|
|
2214
2246
|
const result = await dateRangeSchema.safeParseAsync(params);
|
|
@@ -2234,61 +2266,90 @@ const createLLMRequestsDataLayer = (db) => {
|
|
|
2234
2266
|
if (!result.success) throw new LLMOpsError(`Invalid parameters: ${result.error.message}`);
|
|
2235
2267
|
const { startDate, endDate } = result.data;
|
|
2236
2268
|
return db.selectFrom("llm_requests").select([
|
|
2237
|
-
kysely.sql`DATE(${col("createdAt")})`.as("date"),
|
|
2238
|
-
kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
|
|
2239
|
-
kysely.sql`COALESCE(SUM(${col("inputCost")}), 0)`.as("totalInputCost"),
|
|
2240
|
-
kysely.sql`COALESCE(SUM(${col("outputCost")}), 0)`.as("totalOutputCost"),
|
|
2241
|
-
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"),
|
|
2242
2274
|
kysely.sql`COUNT(*)`.as("requestCount")
|
|
2243
|
-
]).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();
|
|
2244
2276
|
},
|
|
2245
2277
|
getCostSummary: async (params) => {
|
|
2246
2278
|
const result = await costSummarySchema.safeParseAsync(params);
|
|
2247
2279
|
if (!result.success) throw new LLMOpsError(`Invalid parameters: ${result.error.message}`);
|
|
2248
2280
|
const { startDate, endDate, groupBy, configId, variantId, environmentId, tags } = result.data;
|
|
2249
|
-
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()}`);
|
|
2250
2282
|
if (configId) baseQuery = baseQuery.where("configId", "=", configId);
|
|
2251
2283
|
if (variantId) baseQuery = baseQuery.where("variantId", "=", variantId);
|
|
2252
2284
|
if (environmentId) baseQuery = baseQuery.where("environmentId", "=", environmentId);
|
|
2253
2285
|
if (tags && Object.keys(tags).length > 0) for (const [key, values] of Object.entries(tags)) {
|
|
2254
2286
|
if (values.length === 0) continue;
|
|
2255
|
-
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]}`);
|
|
2256
2288
|
else {
|
|
2257
2289
|
const valueList = kysely.sql.join(values.map((v) => kysely.sql`${v}`));
|
|
2258
|
-
baseQuery = baseQuery.where(kysely.sql`${col("tags")}->>${key} IN (${valueList})`);
|
|
2290
|
+
baseQuery = baseQuery.where(kysely.sql`${col$1("tags")}->>${key} IN (${valueList})`);
|
|
2259
2291
|
}
|
|
2260
2292
|
}
|
|
2261
2293
|
switch (groupBy) {
|
|
2262
2294
|
case "day": return baseQuery.select([
|
|
2263
|
-
kysely.sql`DATE(${col("createdAt")})`.as("groupKey"),
|
|
2264
|
-
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"),
|
|
2265
2297
|
kysely.sql`COUNT(*)`.as("requestCount"),
|
|
2266
|
-
kysely.sql`COALESCE(SUM(${col("totalTokens")}), 0)`.as("totalTokens")
|
|
2267
|
-
]).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();
|
|
2268
2300
|
case "hour": return baseQuery.select([
|
|
2269
|
-
kysely.sql`DATE_TRUNC('hour', ${col("createdAt")})`.as("groupKey"),
|
|
2270
|
-
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"),
|
|
2271
2303
|
kysely.sql`COUNT(*)`.as("requestCount"),
|
|
2272
|
-
kysely.sql`COALESCE(SUM(${col("totalTokens")}), 0)`.as("totalTokens")
|
|
2273
|
-
]).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();
|
|
2274
2306
|
case "model": return baseQuery.select([
|
|
2275
|
-
kysely.sql`${col("provider")} || '/' || ${col("model")}`.as("groupKey"),
|
|
2276
|
-
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"),
|
|
2277
2309
|
kysely.sql`COUNT(*)`.as("requestCount")
|
|
2278
|
-
]).groupBy(["provider", "model"]).orderBy(kysely.sql`SUM(${col("cost")})`, "desc").execute();
|
|
2310
|
+
]).groupBy(["provider", "model"]).orderBy(kysely.sql`SUM(${col$1("cost")})`, "desc").execute();
|
|
2279
2311
|
case "provider": return baseQuery.select([
|
|
2280
|
-
kysely.sql`${col("provider")}`.as("groupKey"),
|
|
2281
|
-
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"),
|
|
2282
2314
|
kysely.sql`COUNT(*)`.as("requestCount")
|
|
2283
|
-
]).groupBy("provider").orderBy(kysely.sql`SUM(${col("cost")})`, "desc").execute();
|
|
2315
|
+
]).groupBy("provider").orderBy(kysely.sql`SUM(${col$1("cost")})`, "desc").execute();
|
|
2284
2316
|
case "config": return baseQuery.select([
|
|
2285
|
-
kysely.sql`COALESCE(${col("configId")}::text, 'no-config')`.as("groupKey"),
|
|
2286
|
-
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"),
|
|
2319
|
+
kysely.sql`COUNT(*)`.as("requestCount")
|
|
2320
|
+
]).groupBy("configId").orderBy(kysely.sql`SUM(${col$1("cost")})`, "desc").execute();
|
|
2321
|
+
case "endpoint": return baseQuery.select([
|
|
2322
|
+
kysely.sql`COALESCE(${col$1("endpoint")}, 'unknown')`.as("groupKey"),
|
|
2323
|
+
kysely.sql`COALESCE(SUM(${col$1("cost")}), 0)`.as("totalCost"),
|
|
2287
2324
|
kysely.sql`COUNT(*)`.as("requestCount")
|
|
2288
|
-
]).groupBy("
|
|
2325
|
+
]).groupBy("endpoint").orderBy(kysely.sql`SUM(${col$1("cost")})`, "desc").execute();
|
|
2326
|
+
case "tags": {
|
|
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}`);
|
|
2331
|
+
if (tags && Object.keys(tags).length > 0) for (const [key, values] of Object.entries(tags)) {
|
|
2332
|
+
if (values.length === 0) continue;
|
|
2333
|
+
if (values.length === 1) conditions.push(kysely.sql`${col$1("tags")}->>${key} = ${values[0]}`);
|
|
2334
|
+
else {
|
|
2335
|
+
const valueList = kysely.sql.join(values.map((v) => kysely.sql`${v}`));
|
|
2336
|
+
conditions.push(kysely.sql`${col$1("tags")}->>${key} IN (${valueList})`);
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
const whereClause = kysely.sql.join(conditions, kysely.sql` AND `);
|
|
2340
|
+
return (await kysely.sql`
|
|
2341
|
+
SELECT t.key || ':' || t.value as "groupKey",
|
|
2342
|
+
COALESCE(SUM(${col$1("cost")}), 0) as "totalCost",
|
|
2343
|
+
COUNT(*) as "requestCount"
|
|
2344
|
+
FROM "llm_requests", jsonb_each_text(${col$1("tags")}) t
|
|
2345
|
+
WHERE ${whereClause}
|
|
2346
|
+
GROUP BY t.key, t.value
|
|
2347
|
+
ORDER BY SUM(${col$1("cost")}) DESC
|
|
2348
|
+
`.execute(db)).rows;
|
|
2349
|
+
}
|
|
2289
2350
|
default: return baseQuery.select([
|
|
2290
2351
|
kysely.sql`'total'`.as("groupKey"),
|
|
2291
|
-
kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
|
|
2352
|
+
kysely.sql`COALESCE(SUM(${col$1("cost")}), 0)`.as("totalCost"),
|
|
2292
2353
|
kysely.sql`COUNT(*)`.as("requestCount")
|
|
2293
2354
|
]).execute();
|
|
2294
2355
|
}
|
|
@@ -2299,22 +2360,22 @@ const createLLMRequestsDataLayer = (db) => {
|
|
|
2299
2360
|
const { startDate, endDate, configId, variantId, environmentId, tags } = result.data;
|
|
2300
2361
|
let query = db.selectFrom("llm_requests").select([
|
|
2301
2362
|
kysely.sql`COUNT(*)`.as("totalRequests"),
|
|
2302
|
-
kysely.sql`COUNT(CASE WHEN ${col("statusCode")} >= 200 AND ${col("statusCode")} < 300 THEN 1 END)`.as("successfulRequests"),
|
|
2303
|
-
kysely.sql`COUNT(CASE WHEN ${col("statusCode")} >= 400 THEN 1 END)`.as("failedRequests"),
|
|
2304
|
-
kysely.sql`COUNT(CASE WHEN ${col("isStreaming")} = true THEN 1 END)`.as("streamingRequests"),
|
|
2305
|
-
kysely.sql`AVG(${col("latencyMs")})`.as("avgLatencyMs"),
|
|
2306
|
-
kysely.sql`MAX(${col("latencyMs")})`.as("maxLatencyMs"),
|
|
2307
|
-
kysely.sql`MIN(${col("latencyMs")})`.as("minLatencyMs")
|
|
2308
|
-
]).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()}`);
|
|
2309
2370
|
if (configId) query = query.where("configId", "=", configId);
|
|
2310
2371
|
if (variantId) query = query.where("variantId", "=", variantId);
|
|
2311
2372
|
if (environmentId) query = query.where("environmentId", "=", environmentId);
|
|
2312
2373
|
if (tags && Object.keys(tags).length > 0) for (const [key, values] of Object.entries(tags)) {
|
|
2313
2374
|
if (values.length === 0) continue;
|
|
2314
|
-
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]}`);
|
|
2315
2376
|
else {
|
|
2316
2377
|
const valueList = kysely.sql.join(values.map((v) => kysely.sql`${v}`));
|
|
2317
|
-
query = query.where(kysely.sql`${col("tags")}->>${key} IN (${valueList})`);
|
|
2378
|
+
query = query.where(kysely.sql`${col$1("tags")}->>${key} IN (${valueList})`);
|
|
2318
2379
|
}
|
|
2319
2380
|
}
|
|
2320
2381
|
return await query.executeTakeFirst();
|
|
@@ -3056,6 +3117,271 @@ const createTargetingRulesDataLayer = (db) => {
|
|
|
3056
3117
|
};
|
|
3057
3118
|
};
|
|
3058
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
|
+
|
|
3059
3385
|
//#endregion
|
|
3060
3386
|
//#region src/datalayer/variants.ts
|
|
3061
3387
|
const createVariant = require_db.zod_default.object({ name: require_db.zod_default.string() });
|
|
@@ -3349,6 +3675,7 @@ function createDataLayer(db) {
|
|
|
3349
3675
|
...createProviderConfigsDataLayer(db),
|
|
3350
3676
|
...createProviderGuardrailOverridesDataLayer(db),
|
|
3351
3677
|
...createTargetingRulesDataLayer(db),
|
|
3678
|
+
...createTracesDataLayer(db),
|
|
3352
3679
|
...createVariantDataLayer(db),
|
|
3353
3680
|
...createVariantVersionsDataLayer(db),
|
|
3354
3681
|
...createWorkspaceSettingsDataLayer(db)
|
|
@@ -3927,9 +4254,17 @@ var ManifestRouter = class {
|
|
|
3927
4254
|
};
|
|
3928
4255
|
|
|
3929
4256
|
//#endregion
|
|
4257
|
+
exports.COST_SUMMARY_GROUP_BY = COST_SUMMARY_GROUP_BY;
|
|
3930
4258
|
exports.CacheService = CacheService;
|
|
3931
4259
|
exports.DEFAULT_PROVIDER_ENV_VARS = DEFAULT_PROVIDER_ENV_VARS;
|
|
3932
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;
|
|
3933
4268
|
exports.MS = MS;
|
|
3934
4269
|
exports.ManifestBuilder = ManifestBuilder;
|
|
3935
4270
|
exports.ManifestRouter = ManifestRouter;
|
|
@@ -3959,6 +4294,7 @@ exports.createPlaygroundRunsDataLayer = createPlaygroundRunsDataLayer;
|
|
|
3959
4294
|
exports.createProviderConfigsDataLayer = createProviderConfigsDataLayer;
|
|
3960
4295
|
exports.createProviderGuardrailOverridesDataLayer = createProviderGuardrailOverridesDataLayer;
|
|
3961
4296
|
exports.createTargetingRulesDataLayer = createTargetingRulesDataLayer;
|
|
4297
|
+
exports.createTracesDataLayer = createTracesDataLayer;
|
|
3962
4298
|
exports.createVariantDataLayer = createVariantDataLayer;
|
|
3963
4299
|
exports.createVariantVersionsDataLayer = createVariantVersionsDataLayer;
|
|
3964
4300
|
exports.createWorkspaceSettingsDataLayer = createWorkspaceSettingsDataLayer;
|
|
@@ -4000,7 +4336,10 @@ exports.providerConfigsSchema = require_db.providerConfigsSchema;
|
|
|
4000
4336
|
exports.providerGuardrailOverridesSchema = require_db.providerGuardrailOverridesSchema;
|
|
4001
4337
|
exports.runAutoMigrations = require_db.runAutoMigrations;
|
|
4002
4338
|
exports.schemas = require_db.schemas;
|
|
4339
|
+
exports.spanEventsSchema = require_db.spanEventsSchema;
|
|
4340
|
+
exports.spansSchema = require_db.spansSchema;
|
|
4003
4341
|
exports.targetingRulesSchema = require_db.targetingRulesSchema;
|
|
4342
|
+
exports.tracesSchema = require_db.tracesSchema;
|
|
4004
4343
|
exports.validateLLMOpsConfig = validateLLMOpsConfig;
|
|
4005
4344
|
exports.validatePartialTableData = require_db.validatePartialTableData;
|
|
4006
4345
|
exports.validateTableData = require_db.validateTableData;
|