@llmops/core 0.1.5 → 0.1.6

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/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { A as object, C as zod_default, D as boolean, E as array, M as string, N as union, O as literal, P as unknown, S as workspaceSettingsSchema, T as any, _ as llmRequestsSchema, a as matchType, b as variantVersionsSchema, c as parsePartialTableData, d as validateTableData, f as SCHEMA_METADATA, g as environmentsSchema, h as environmentSecretsSchema, i as getMigrations, j as record, k as number, l as parseTableData, m as configsSchema, n as createDatabaseFromConnection, o as runAutoMigrations, p as configVariantsSchema, r as detectDatabaseType, s as logger, t as createDatabase, u as validatePartialTableData, v as schemas, w as _enum, x as variantsSchema, y as targetingRulesSchema } from "./db-DSzwrW4p.mjs";
1
+ import { A as object, C as zod_default, D as boolean, E as array, M as string, N as union, O as literal, P as unknown, S as workspaceSettingsSchema, T as any, _ as llmRequestsSchema, a as matchType, b as variantVersionsSchema, c as parsePartialTableData, d as validateTableData, f as SCHEMA_METADATA, g as environmentsSchema, h as environmentSecretsSchema, i as getMigrations, j as record, k as number, l as parseTableData, m as configsSchema, n as createDatabaseFromConnection, o as runAutoMigrations, p as configVariantsSchema, r as detectDatabaseType, s as logger, t as createDatabase, u as validatePartialTableData, v as schemas, w as _enum, x as variantsSchema, y as targetingRulesSchema } from "./db-D78x_Elf.mjs";
2
2
  import gateway from "@llmops/gateway";
3
3
  import { sql } from "kysely";
4
4
  import * as fs from "node:fs/promises";
@@ -1283,7 +1283,8 @@ const createConfigVariantDataLayer = (db) => {
1283
1283
  return {
1284
1284
  ...versionData,
1285
1285
  configId: resolvedConfigId,
1286
- variantId: configVariant.variantId
1286
+ variantId: configVariant.variantId,
1287
+ environmentId
1287
1288
  };
1288
1289
  }
1289
1290
  };
@@ -1453,6 +1454,7 @@ const insertLLMRequestSchema = zod_default.object({
1453
1454
  requestId: zod_default.string().uuid(),
1454
1455
  configId: zod_default.string().uuid().nullable().optional(),
1455
1456
  variantId: zod_default.string().uuid().nullable().optional(),
1457
+ environmentId: zod_default.string().uuid().nullable().optional(),
1456
1458
  provider: zod_default.string(),
1457
1459
  model: zod_default.string(),
1458
1460
  promptTokens: zod_default.number().int().default(0),
@@ -1476,17 +1478,24 @@ const listRequestsSchema = zod_default.object({
1476
1478
  limit: zod_default.number().int().positive().max(1e3).default(100),
1477
1479
  offset: zod_default.number().int().nonnegative().default(0),
1478
1480
  configId: zod_default.string().uuid().optional(),
1481
+ variantId: zod_default.string().uuid().optional(),
1482
+ environmentId: zod_default.string().uuid().optional(),
1479
1483
  provider: zod_default.string().optional(),
1480
1484
  model: zod_default.string().optional(),
1481
1485
  startDate: zod_default.date().optional(),
1482
- endDate: zod_default.date().optional()
1486
+ endDate: zod_default.date().optional(),
1487
+ tags: zod_default.record(zod_default.string(), zod_default.array(zod_default.string())).optional()
1483
1488
  });
1484
1489
  /**
1485
- * Schema for date range queries
1490
+ * Schema for date range queries with optional filters
1486
1491
  */
1487
1492
  const dateRangeSchema = zod_default.object({
1488
1493
  startDate: zod_default.date(),
1489
- endDate: zod_default.date()
1494
+ endDate: zod_default.date(),
1495
+ configId: zod_default.string().uuid().optional(),
1496
+ variantId: zod_default.string().uuid().optional(),
1497
+ environmentId: zod_default.string().uuid().optional(),
1498
+ tags: zod_default.record(zod_default.string(), zod_default.array(zod_default.string())).optional()
1490
1499
  });
1491
1500
  /**
1492
1501
  * Schema for cost summary with grouping
@@ -1494,6 +1503,10 @@ const dateRangeSchema = zod_default.object({
1494
1503
  const costSummarySchema = zod_default.object({
1495
1504
  startDate: zod_default.date(),
1496
1505
  endDate: zod_default.date(),
1506
+ configId: zod_default.string().uuid().optional(),
1507
+ variantId: zod_default.string().uuid().optional(),
1508
+ environmentId: zod_default.string().uuid().optional(),
1509
+ tags: zod_default.record(zod_default.string(), zod_default.array(zod_default.string())).optional(),
1497
1510
  groupBy: zod_default.enum([
1498
1511
  "day",
1499
1512
  "hour",
@@ -1523,6 +1536,7 @@ const createLLMRequestsDataLayer = (db) => {
1523
1536
  requestId: req.requestId,
1524
1537
  configId: req.configId ?? null,
1525
1538
  variantId: req.variantId ?? null,
1539
+ environmentId: req.environmentId ?? null,
1526
1540
  provider: req.provider,
1527
1541
  model: req.model,
1528
1542
  promptTokens: req.promptTokens,
@@ -1554,6 +1568,7 @@ const createLLMRequestsDataLayer = (db) => {
1554
1568
  requestId: req.requestId,
1555
1569
  configId: req.configId ?? null,
1556
1570
  variantId: req.variantId ?? null,
1571
+ environmentId: req.environmentId ?? null,
1557
1572
  provider: req.provider,
1558
1573
  model: req.model,
1559
1574
  promptTokens: req.promptTokens,
@@ -1576,14 +1591,43 @@ const createLLMRequestsDataLayer = (db) => {
1576
1591
  listRequests: async (params) => {
1577
1592
  const result = await listRequestsSchema.safeParseAsync(params || {});
1578
1593
  if (!result.success) throw new LLMOpsError(`Invalid parameters: ${result.error.message}`);
1579
- const { limit, offset, configId, provider, model, startDate, endDate } = result.data;
1594
+ const { limit, offset, configId, variantId, environmentId, provider, model, startDate, endDate, tags } = result.data;
1595
+ console.log("[listRequests] Parsed filters:", {
1596
+ configId,
1597
+ variantId,
1598
+ environmentId,
1599
+ provider,
1600
+ model
1601
+ });
1580
1602
  let baseQuery = db.selectFrom("llm_requests");
1581
- if (configId) baseQuery = baseQuery.where("configId", "=", configId);
1603
+ if (configId) {
1604
+ console.log("[listRequests] Adding configId filter:", configId);
1605
+ baseQuery = baseQuery.where("configId", "=", configId);
1606
+ }
1607
+ if (variantId) {
1608
+ console.log("[listRequests] Adding variantId filter:", variantId);
1609
+ baseQuery = baseQuery.where("variantId", "=", variantId);
1610
+ }
1611
+ if (environmentId) {
1612
+ console.log("[listRequests] Adding environmentId filter:", environmentId);
1613
+ baseQuery = baseQuery.where("environmentId", "=", environmentId);
1614
+ }
1582
1615
  if (provider) baseQuery = baseQuery.where("provider", "=", provider);
1583
1616
  if (model) baseQuery = baseQuery.where("model", "=", model);
1584
1617
  if (startDate) baseQuery = baseQuery.where(sql`${col("createdAt")} >= ${startDate.toISOString()}`);
1585
1618
  if (endDate) baseQuery = baseQuery.where(sql`${col("createdAt")} <= ${endDate.toISOString()}`);
1586
- const countResult = await baseQuery.select(sql`COUNT(*)`.as("total")).executeTakeFirst();
1619
+ if (tags && Object.keys(tags).length > 0) for (const [key, values] of Object.entries(tags)) {
1620
+ if (values.length === 0) continue;
1621
+ if (values.length === 1) baseQuery = baseQuery.where(sql`${col("tags")}->>${key} = ${values[0]}`);
1622
+ else {
1623
+ const valueList = sql.join(values.map((v) => sql`${v}`));
1624
+ baseQuery = baseQuery.where(sql`${col("tags")}->>${key} IN (${valueList})`);
1625
+ }
1626
+ }
1627
+ const countQuery = baseQuery.select(sql`COUNT(*)`.as("total"));
1628
+ console.log("[listRequests] Count SQL:", countQuery.compile().sql);
1629
+ console.log("[listRequests] Count params:", countQuery.compile().parameters);
1630
+ const countResult = await countQuery.executeTakeFirst();
1587
1631
  const total = Number(countResult?.total ?? 0);
1588
1632
  return {
1589
1633
  data: await baseQuery.selectAll().orderBy("createdAt", "desc").limit(limit).offset(offset).execute(),
@@ -1598,8 +1642,8 @@ const createLLMRequestsDataLayer = (db) => {
1598
1642
  getTotalCost: async (params) => {
1599
1643
  const result = await dateRangeSchema.safeParseAsync(params);
1600
1644
  if (!result.success) throw new LLMOpsError(`Invalid parameters: ${result.error.message}`);
1601
- const { startDate, endDate } = result.data;
1602
- return await db.selectFrom("llm_requests").select([
1645
+ const { startDate, endDate, configId, variantId, environmentId, tags } = result.data;
1646
+ let query = db.selectFrom("llm_requests").select([
1603
1647
  sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
1604
1648
  sql`COALESCE(SUM(${col("inputCost")}), 0)`.as("totalInputCost"),
1605
1649
  sql`COALESCE(SUM(${col("outputCost")}), 0)`.as("totalOutputCost"),
@@ -1607,7 +1651,19 @@ const createLLMRequestsDataLayer = (db) => {
1607
1651
  sql`COALESCE(SUM(${col("completionTokens")}), 0)`.as("totalCompletionTokens"),
1608
1652
  sql`COALESCE(SUM(${col("totalTokens")}), 0)`.as("totalTokens"),
1609
1653
  sql`COUNT(*)`.as("requestCount")
1610
- ]).where(sql`${col("createdAt")} >= ${startDate.toISOString()}`).where(sql`${col("createdAt")} <= ${endDate.toISOString()}`).executeTakeFirst();
1654
+ ]).where(sql`${col("createdAt")} >= ${startDate.toISOString()}`).where(sql`${col("createdAt")} <= ${endDate.toISOString()}`);
1655
+ if (configId) query = query.where("configId", "=", configId);
1656
+ if (variantId) query = query.where("variantId", "=", variantId);
1657
+ if (environmentId) query = query.where("environmentId", "=", environmentId);
1658
+ if (tags && Object.keys(tags).length > 0) for (const [key, values] of Object.entries(tags)) {
1659
+ if (values.length === 0) continue;
1660
+ if (values.length === 1) query = query.where(sql`${col("tags")}->>${key} = ${values[0]}`);
1661
+ else {
1662
+ const valueList = sql.join(values.map((v) => sql`${v}`));
1663
+ query = query.where(sql`${col("tags")}->>${key} IN (${valueList})`);
1664
+ }
1665
+ }
1666
+ return await query.executeTakeFirst();
1611
1667
  },
1612
1668
  getCostByModel: async (params) => {
1613
1669
  const result = await dateRangeSchema.safeParseAsync(params);
@@ -1673,18 +1729,31 @@ const createLLMRequestsDataLayer = (db) => {
1673
1729
  getCostSummary: async (params) => {
1674
1730
  const result = await costSummarySchema.safeParseAsync(params);
1675
1731
  if (!result.success) throw new LLMOpsError(`Invalid parameters: ${result.error.message}`);
1676
- const { startDate, endDate, groupBy } = result.data;
1677
- const baseQuery = db.selectFrom("llm_requests").where(sql`${col("createdAt")} >= ${startDate.toISOString()}`).where(sql`${col("createdAt")} <= ${endDate.toISOString()}`);
1732
+ const { startDate, endDate, groupBy, configId, variantId, environmentId, tags } = result.data;
1733
+ let baseQuery = db.selectFrom("llm_requests").where(sql`${col("createdAt")} >= ${startDate.toISOString()}`).where(sql`${col("createdAt")} <= ${endDate.toISOString()}`);
1734
+ if (configId) baseQuery = baseQuery.where("configId", "=", configId);
1735
+ if (variantId) baseQuery = baseQuery.where("variantId", "=", variantId);
1736
+ if (environmentId) baseQuery = baseQuery.where("environmentId", "=", environmentId);
1737
+ if (tags && Object.keys(tags).length > 0) for (const [key, values] of Object.entries(tags)) {
1738
+ if (values.length === 0) continue;
1739
+ if (values.length === 1) baseQuery = baseQuery.where(sql`${col("tags")}->>${key} = ${values[0]}`);
1740
+ else {
1741
+ const valueList = sql.join(values.map((v) => sql`${v}`));
1742
+ baseQuery = baseQuery.where(sql`${col("tags")}->>${key} IN (${valueList})`);
1743
+ }
1744
+ }
1678
1745
  switch (groupBy) {
1679
1746
  case "day": return baseQuery.select([
1680
1747
  sql`DATE(${col("createdAt")})`.as("groupKey"),
1681
1748
  sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
1682
- sql`COUNT(*)`.as("requestCount")
1749
+ sql`COUNT(*)`.as("requestCount"),
1750
+ sql`COALESCE(SUM(${col("totalTokens")}), 0)`.as("totalTokens")
1683
1751
  ]).groupBy(sql`DATE(${col("createdAt")})`).orderBy(sql`DATE(${col("createdAt")})`, "asc").execute();
1684
1752
  case "hour": return baseQuery.select([
1685
1753
  sql`DATE_TRUNC('hour', ${col("createdAt")})`.as("groupKey"),
1686
1754
  sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
1687
- sql`COUNT(*)`.as("requestCount")
1755
+ sql`COUNT(*)`.as("requestCount"),
1756
+ sql`COALESCE(SUM(${col("totalTokens")}), 0)`.as("totalTokens")
1688
1757
  ]).groupBy(sql`DATE_TRUNC('hour', ${col("createdAt")})`).orderBy(sql`DATE_TRUNC('hour', ${col("createdAt")})`, "asc").execute();
1689
1758
  case "model": return baseQuery.select([
1690
1759
  sql`${col("provider")} || '/' || ${col("model")}`.as("groupKey"),
@@ -1711,8 +1780,8 @@ const createLLMRequestsDataLayer = (db) => {
1711
1780
  getRequestStats: async (params) => {
1712
1781
  const result = await dateRangeSchema.safeParseAsync(params);
1713
1782
  if (!result.success) throw new LLMOpsError(`Invalid parameters: ${result.error.message}`);
1714
- const { startDate, endDate } = result.data;
1715
- return await db.selectFrom("llm_requests").select([
1783
+ const { startDate, endDate, configId, variantId, environmentId, tags } = result.data;
1784
+ let query = db.selectFrom("llm_requests").select([
1716
1785
  sql`COUNT(*)`.as("totalRequests"),
1717
1786
  sql`COUNT(CASE WHEN ${col("statusCode")} >= 200 AND ${col("statusCode")} < 300 THEN 1 END)`.as("successfulRequests"),
1718
1787
  sql`COUNT(CASE WHEN ${col("statusCode")} >= 400 THEN 1 END)`.as("failedRequests"),
@@ -1720,7 +1789,27 @@ const createLLMRequestsDataLayer = (db) => {
1720
1789
  sql`AVG(${col("latencyMs")})`.as("avgLatencyMs"),
1721
1790
  sql`MAX(${col("latencyMs")})`.as("maxLatencyMs"),
1722
1791
  sql`MIN(${col("latencyMs")})`.as("minLatencyMs")
1723
- ]).where(sql`${col("createdAt")} >= ${startDate.toISOString()}`).where(sql`${col("createdAt")} <= ${endDate.toISOString()}`).executeTakeFirst();
1792
+ ]).where(sql`${col("createdAt")} >= ${startDate.toISOString()}`).where(sql`${col("createdAt")} <= ${endDate.toISOString()}`);
1793
+ if (configId) query = query.where("configId", "=", configId);
1794
+ if (variantId) query = query.where("variantId", "=", variantId);
1795
+ if (environmentId) query = query.where("environmentId", "=", environmentId);
1796
+ if (tags && Object.keys(tags).length > 0) for (const [key, values] of Object.entries(tags)) {
1797
+ if (values.length === 0) continue;
1798
+ if (values.length === 1) query = query.where(sql`${col("tags")}->>${key} = ${values[0]}`);
1799
+ else {
1800
+ const valueList = sql.join(values.map((v) => sql`${v}`));
1801
+ query = query.where(sql`${col("tags")}->>${key} IN (${valueList})`);
1802
+ }
1803
+ }
1804
+ return await query.executeTakeFirst();
1805
+ },
1806
+ getDistinctTags: async () => {
1807
+ return (await sql`
1808
+ SELECT DISTINCT key, value
1809
+ FROM llm_requests, jsonb_each_text(tags) AS t(key, value)
1810
+ WHERE tags != '{}'::jsonb
1811
+ ORDER BY key, value
1812
+ `.execute(db)).rows;
1724
1813
  }
1725
1814
  };
1726
1815
  };
@@ -1,4 +1,4 @@
1
- const require_db = require('./db-eEfIe5dO.cjs');
1
+ const require_db = require('./db-C_u1BuaR.cjs');
2
2
  let kysely = require("kysely");
3
3
 
4
4
  //#region src/db/node-sqlite-dialect.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llmops/core",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Core LLMOps functionality and utilities",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -51,7 +51,7 @@
51
51
  "hono": "^4.10.7",
52
52
  "kysely": "^0.28.8",
53
53
  "pino": "^10.1.0",
54
- "@llmops/gateway": "^0.1.5"
54
+ "@llmops/gateway": "^0.1.6"
55
55
  },
56
56
  "scripts": {
57
57
  "build": "tsdown",