@llmops/core 0.1.4 → 0.1.5-beta.2

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.cjs CHANGED
@@ -1,6 +1,11 @@
1
- const require_db = require('./db-C9-M-kdS.cjs');
1
+ const require_db = require('./db-eEfIe5dO.cjs');
2
2
  let __llmops_gateway = require("@llmops/gateway");
3
3
  __llmops_gateway = require_db.__toESM(__llmops_gateway);
4
+ let kysely = require("kysely");
5
+ let node_fs_promises = require("node:fs/promises");
6
+ node_fs_promises = require_db.__toESM(node_fs_promises);
7
+ let node_path = require("node:path");
8
+ node_path = require_db.__toESM(node_path);
4
9
  let __better_auth_utils_random = require("@better-auth/utils/random");
5
10
  let node_crypto = require("node:crypto");
6
11
 
@@ -465,6 +470,491 @@ function validateLLMOpsConfig(config) {
465
470
  return result.data;
466
471
  }
467
472
 
473
+ //#endregion
474
+ //#region src/cache/types.ts
475
+ /** Time constants in milliseconds for convenience */
476
+ const MS = {
477
+ "1_MINUTE": 60 * 1e3,
478
+ "5_MINUTES": 300 * 1e3,
479
+ "10_MINUTES": 600 * 1e3,
480
+ "30_MINUTES": 1800 * 1e3,
481
+ "1_HOUR": 3600 * 1e3,
482
+ "6_HOURS": 360 * 60 * 1e3,
483
+ "12_HOURS": 720 * 60 * 1e3,
484
+ "1_DAY": 1440 * 60 * 1e3,
485
+ "7_DAYS": 10080 * 60 * 1e3,
486
+ "30_DAYS": 720 * 60 * 60 * 1e3
487
+ };
488
+
489
+ //#endregion
490
+ //#region src/cache/backends/memory.ts
491
+ var MemoryCacheBackend = class {
492
+ cache = /* @__PURE__ */ new Map();
493
+ stats = {
494
+ hits: 0,
495
+ misses: 0,
496
+ sets: 0,
497
+ deletes: 0,
498
+ size: 0,
499
+ expired: 0
500
+ };
501
+ cleanupInterval;
502
+ maxSize;
503
+ constructor(maxSize = 1e4, cleanupIntervalMs = 6e4) {
504
+ this.maxSize = maxSize;
505
+ this.startCleanup(cleanupIntervalMs);
506
+ }
507
+ startCleanup(intervalMs) {
508
+ this.cleanupInterval = setInterval(() => {
509
+ this.cleanup();
510
+ }, intervalMs);
511
+ }
512
+ getFullKey(key, namespace) {
513
+ return namespace ? `${namespace}:${key}` : key;
514
+ }
515
+ isExpired(entry) {
516
+ return entry.expiresAt !== void 0 && entry.expiresAt <= Date.now();
517
+ }
518
+ evictIfNeeded() {
519
+ if (this.cache.size >= this.maxSize) {
520
+ const entries = Array.from(this.cache.entries());
521
+ entries.sort((a, b) => a[1].createdAt - b[1].createdAt);
522
+ const toRemove = Math.floor(this.maxSize * .1);
523
+ for (let i = 0; i < toRemove && i < entries.length; i++) this.cache.delete(entries[i][0]);
524
+ }
525
+ }
526
+ async get(key, namespace) {
527
+ const fullKey = this.getFullKey(key, namespace);
528
+ const entry = this.cache.get(fullKey);
529
+ if (!entry) {
530
+ this.stats.misses++;
531
+ return null;
532
+ }
533
+ if (this.isExpired(entry)) {
534
+ this.cache.delete(fullKey);
535
+ this.stats.expired++;
536
+ this.stats.misses++;
537
+ return null;
538
+ }
539
+ this.stats.hits++;
540
+ return entry;
541
+ }
542
+ async set(key, value, options = {}) {
543
+ const fullKey = this.getFullKey(key, options.namespace);
544
+ const now = Date.now();
545
+ const entry = {
546
+ value,
547
+ createdAt: now,
548
+ expiresAt: options.ttl ? now + options.ttl : void 0,
549
+ metadata: options.metadata
550
+ };
551
+ this.evictIfNeeded();
552
+ this.cache.set(fullKey, entry);
553
+ this.stats.sets++;
554
+ this.stats.size = this.cache.size;
555
+ }
556
+ async delete(key, namespace) {
557
+ const fullKey = this.getFullKey(key, namespace);
558
+ const deleted = this.cache.delete(fullKey);
559
+ if (deleted) {
560
+ this.stats.deletes++;
561
+ this.stats.size = this.cache.size;
562
+ }
563
+ return deleted;
564
+ }
565
+ async clear(namespace) {
566
+ if (namespace) {
567
+ const prefix = `${namespace}:`;
568
+ const keysToDelete = Array.from(this.cache.keys()).filter((key) => key.startsWith(prefix));
569
+ for (const key of keysToDelete) this.cache.delete(key);
570
+ this.stats.deletes += keysToDelete.length;
571
+ } else {
572
+ this.stats.deletes += this.cache.size;
573
+ this.cache.clear();
574
+ }
575
+ this.stats.size = this.cache.size;
576
+ }
577
+ async has(key, namespace) {
578
+ const fullKey = this.getFullKey(key, namespace);
579
+ const entry = this.cache.get(fullKey);
580
+ if (!entry) return false;
581
+ if (this.isExpired(entry)) {
582
+ this.cache.delete(fullKey);
583
+ this.stats.expired++;
584
+ return false;
585
+ }
586
+ return true;
587
+ }
588
+ async keys(namespace) {
589
+ const allKeys = Array.from(this.cache.keys());
590
+ if (namespace) {
591
+ const prefix = `${namespace}:`;
592
+ return allKeys.filter((key) => key.startsWith(prefix)).map((key) => key.substring(prefix.length));
593
+ }
594
+ return allKeys;
595
+ }
596
+ async getStats(namespace) {
597
+ if (namespace) {
598
+ const prefix = `${namespace}:`;
599
+ const namespaceKeys = Array.from(this.cache.keys()).filter((key) => key.startsWith(prefix));
600
+ let expired = 0;
601
+ for (const key of namespaceKeys) {
602
+ const entry = this.cache.get(key);
603
+ if (entry && this.isExpired(entry)) expired++;
604
+ }
605
+ return {
606
+ ...this.stats,
607
+ size: namespaceKeys.length,
608
+ expired
609
+ };
610
+ }
611
+ return { ...this.stats };
612
+ }
613
+ async cleanup() {
614
+ let expiredCount = 0;
615
+ for (const [key, entry] of this.cache.entries()) if (this.isExpired(entry)) {
616
+ this.cache.delete(key);
617
+ expiredCount++;
618
+ }
619
+ if (expiredCount > 0) {
620
+ this.stats.expired += expiredCount;
621
+ this.stats.size = this.cache.size;
622
+ }
623
+ }
624
+ async close() {
625
+ if (this.cleanupInterval) {
626
+ clearInterval(this.cleanupInterval);
627
+ this.cleanupInterval = void 0;
628
+ }
629
+ this.cache.clear();
630
+ }
631
+ };
632
+
633
+ //#endregion
634
+ //#region src/cache/backends/file.ts
635
+ /**
636
+ * @file src/cache/backends/file.ts
637
+ * File-based cache backend implementation
638
+ */
639
+ var FileCacheBackend = class {
640
+ cacheFile;
641
+ data = {};
642
+ saveTimer;
643
+ cleanupInterval;
644
+ loaded = false;
645
+ loadPromise;
646
+ stats = {
647
+ hits: 0,
648
+ misses: 0,
649
+ sets: 0,
650
+ deletes: 0,
651
+ size: 0,
652
+ expired: 0
653
+ };
654
+ saveInterval;
655
+ constructor(dataDir = "data", fileName = "cache.json", saveIntervalMs = 1e3, cleanupIntervalMs = 6e4) {
656
+ this.cacheFile = node_path.join(process.cwd(), dataDir, fileName);
657
+ this.saveInterval = saveIntervalMs;
658
+ this.loadPromise = this.loadCache();
659
+ this.loadPromise.then(() => {
660
+ this.startCleanup(cleanupIntervalMs);
661
+ });
662
+ }
663
+ /** Ensure cache is loaded before any operation */
664
+ async ensureLoaded() {
665
+ if (!this.loaded) await this.loadPromise;
666
+ }
667
+ async ensureDataDir() {
668
+ const dir = node_path.dirname(this.cacheFile);
669
+ try {
670
+ await node_fs_promises.mkdir(dir, { recursive: true });
671
+ } catch {}
672
+ }
673
+ async loadCache() {
674
+ try {
675
+ const content = await node_fs_promises.readFile(this.cacheFile, "utf-8");
676
+ this.data = JSON.parse(content);
677
+ this.updateStats();
678
+ this.loaded = true;
679
+ } catch {
680
+ this.data = {};
681
+ this.loaded = true;
682
+ }
683
+ }
684
+ async saveCache() {
685
+ try {
686
+ await this.ensureDataDir();
687
+ await node_fs_promises.writeFile(this.cacheFile, JSON.stringify(this.data, null, 2));
688
+ } catch {}
689
+ }
690
+ scheduleSave() {
691
+ if (this.saveTimer) clearTimeout(this.saveTimer);
692
+ this.saveTimer = setTimeout(() => {
693
+ this.saveCache();
694
+ this.saveTimer = void 0;
695
+ }, this.saveInterval);
696
+ }
697
+ startCleanup(intervalMs) {
698
+ this.cleanupInterval = setInterval(() => {
699
+ this.cleanup();
700
+ }, intervalMs);
701
+ }
702
+ isExpired(entry) {
703
+ return entry.expiresAt !== void 0 && entry.expiresAt <= Date.now();
704
+ }
705
+ updateStats() {
706
+ let totalSize = 0;
707
+ let totalExpired = 0;
708
+ for (const namespace of Object.values(this.data)) for (const entry of Object.values(namespace)) {
709
+ totalSize++;
710
+ if (this.isExpired(entry)) totalExpired++;
711
+ }
712
+ this.stats.size = totalSize;
713
+ this.stats.expired = totalExpired;
714
+ }
715
+ getNamespaceData(namespace = "default") {
716
+ if (!this.data[namespace]) this.data[namespace] = {};
717
+ return this.data[namespace];
718
+ }
719
+ async get(key, namespace) {
720
+ await this.ensureLoaded();
721
+ const namespaceData = this.getNamespaceData(namespace);
722
+ const entry = namespaceData[key];
723
+ if (!entry) {
724
+ this.stats.misses++;
725
+ return null;
726
+ }
727
+ if (this.isExpired(entry)) {
728
+ delete namespaceData[key];
729
+ this.stats.expired++;
730
+ this.stats.misses++;
731
+ this.scheduleSave();
732
+ return null;
733
+ }
734
+ this.stats.hits++;
735
+ return entry;
736
+ }
737
+ async set(key, value, options = {}) {
738
+ await this.ensureLoaded();
739
+ const namespace = options.namespace || "default";
740
+ const namespaceData = this.getNamespaceData(namespace);
741
+ const now = Date.now();
742
+ namespaceData[key] = {
743
+ value,
744
+ createdAt: now,
745
+ expiresAt: options.ttl ? now + options.ttl : void 0,
746
+ metadata: options.metadata
747
+ };
748
+ this.stats.sets++;
749
+ this.updateStats();
750
+ this.scheduleSave();
751
+ }
752
+ async delete(key, namespace) {
753
+ const namespaceData = this.getNamespaceData(namespace);
754
+ const existed = key in namespaceData;
755
+ if (existed) {
756
+ delete namespaceData[key];
757
+ this.stats.deletes++;
758
+ this.updateStats();
759
+ this.scheduleSave();
760
+ }
761
+ return existed;
762
+ }
763
+ async clear(namespace) {
764
+ if (namespace) {
765
+ const namespaceData = this.getNamespaceData(namespace);
766
+ const count = Object.keys(namespaceData).length;
767
+ this.data[namespace] = {};
768
+ this.stats.deletes += count;
769
+ } else {
770
+ const totalCount = Object.values(this.data).reduce((sum, ns) => sum + Object.keys(ns).length, 0);
771
+ this.data = {};
772
+ this.stats.deletes += totalCount;
773
+ }
774
+ this.updateStats();
775
+ this.scheduleSave();
776
+ }
777
+ async has(key, namespace) {
778
+ const namespaceData = this.getNamespaceData(namespace);
779
+ const entry = namespaceData[key];
780
+ if (!entry) return false;
781
+ if (this.isExpired(entry)) {
782
+ delete namespaceData[key];
783
+ this.stats.expired++;
784
+ this.scheduleSave();
785
+ return false;
786
+ }
787
+ return true;
788
+ }
789
+ async keys(namespace) {
790
+ if (namespace) {
791
+ const namespaceData = this.getNamespaceData(namespace);
792
+ return Object.keys(namespaceData);
793
+ }
794
+ const allKeys = [];
795
+ for (const namespaceData of Object.values(this.data)) allKeys.push(...Object.keys(namespaceData));
796
+ return allKeys;
797
+ }
798
+ async getStats(namespace) {
799
+ if (namespace) {
800
+ const namespaceData = this.getNamespaceData(namespace);
801
+ const keys = Object.keys(namespaceData);
802
+ let expired = 0;
803
+ for (const key of keys) {
804
+ const entry = namespaceData[key];
805
+ if (this.isExpired(entry)) expired++;
806
+ }
807
+ return {
808
+ ...this.stats,
809
+ size: keys.length,
810
+ expired
811
+ };
812
+ }
813
+ this.updateStats();
814
+ return { ...this.stats };
815
+ }
816
+ async cleanup() {
817
+ let expiredCount = 0;
818
+ let hasChanges = false;
819
+ for (const [, namespaceData] of Object.entries(this.data)) for (const [key, entry] of Object.entries(namespaceData)) if (this.isExpired(entry)) {
820
+ delete namespaceData[key];
821
+ expiredCount++;
822
+ hasChanges = true;
823
+ }
824
+ if (hasChanges) {
825
+ this.stats.expired += expiredCount;
826
+ this.updateStats();
827
+ this.scheduleSave();
828
+ }
829
+ }
830
+ /** Wait for the cache to be ready (file loaded) */
831
+ async waitForReady() {
832
+ await this.loadPromise;
833
+ }
834
+ async close() {
835
+ if (this.saveTimer) {
836
+ clearTimeout(this.saveTimer);
837
+ await this.saveCache();
838
+ }
839
+ if (this.cleanupInterval) {
840
+ clearInterval(this.cleanupInterval);
841
+ this.cleanupInterval = void 0;
842
+ }
843
+ }
844
+ };
845
+
846
+ //#endregion
847
+ //#region src/cache/service.ts
848
+ /**
849
+ * @file src/cache/service.ts
850
+ * Unified cache service with pluggable backends
851
+ */
852
+ var CacheService = class {
853
+ backend;
854
+ defaultTtl;
855
+ constructor(config) {
856
+ this.defaultTtl = config.defaultTtl;
857
+ this.backend = this.createBackend(config);
858
+ }
859
+ createBackend(config) {
860
+ switch (config.backend) {
861
+ case "memory": return new MemoryCacheBackend(config.maxSize, config.cleanupInterval);
862
+ case "file": return new FileCacheBackend(config.dataDir, config.fileName, config.saveInterval, config.cleanupInterval);
863
+ default: throw new Error(`Unsupported cache backend: ${config.backend}`);
864
+ }
865
+ }
866
+ /** Get a value from the cache */
867
+ async get(key, namespace) {
868
+ const entry = await this.backend.get(key, namespace);
869
+ return entry ? entry.value : null;
870
+ }
871
+ /** Get the full cache entry (with metadata) */
872
+ async getEntry(key, namespace) {
873
+ return this.backend.get(key, namespace);
874
+ }
875
+ /** Set a value in the cache */
876
+ async set(key, value, options = {}) {
877
+ const finalOptions = {
878
+ ...options,
879
+ ttl: options.ttl ?? this.defaultTtl
880
+ };
881
+ await this.backend.set(key, value, finalOptions);
882
+ }
883
+ /** Set a value with TTL in seconds (convenience method) */
884
+ async setWithTtl(key, value, ttlSeconds, namespace) {
885
+ await this.set(key, value, {
886
+ ttl: ttlSeconds * 1e3,
887
+ namespace
888
+ });
889
+ }
890
+ /** Delete a value from the cache */
891
+ async delete(key, namespace) {
892
+ return this.backend.delete(key, namespace);
893
+ }
894
+ /** Check if a key exists in the cache */
895
+ async has(key, namespace) {
896
+ return this.backend.has(key, namespace);
897
+ }
898
+ /** Get all keys in a namespace */
899
+ async keys(namespace) {
900
+ return this.backend.keys(namespace);
901
+ }
902
+ /** Clear all entries in a namespace (or all entries if no namespace) */
903
+ async clear(namespace) {
904
+ await this.backend.clear(namespace);
905
+ }
906
+ /** Get cache statistics */
907
+ async getStats(namespace) {
908
+ return this.backend.getStats(namespace);
909
+ }
910
+ /** Manually trigger cleanup of expired entries */
911
+ async cleanup() {
912
+ await this.backend.cleanup();
913
+ }
914
+ /** Wait for the backend to be ready */
915
+ async waitForReady() {
916
+ if ("waitForReady" in this.backend) await this.backend.waitForReady();
917
+ }
918
+ /** Close the cache and cleanup resources */
919
+ async close() {
920
+ await this.backend.close();
921
+ }
922
+ /** Get or set pattern - get value, or compute and cache it if not found */
923
+ async getOrSet(key, factory, options = {}) {
924
+ const existing = await this.get(key, options.namespace);
925
+ if (existing !== null) return existing;
926
+ const value = await factory();
927
+ await this.set(key, value, options);
928
+ return value;
929
+ }
930
+ /** Increment a numeric value (simulated atomic operation) */
931
+ async increment(key, delta = 1, options = {}) {
932
+ const newValue = (await this.get(key, options.namespace) || 0) + delta;
933
+ await this.set(key, newValue, options);
934
+ return newValue;
935
+ }
936
+ /** Set multiple values at once */
937
+ async setMany(entries, defaultOptions = {}) {
938
+ const promises = entries.map(({ key, value, options }) => this.set(key, value, {
939
+ ...defaultOptions,
940
+ ...options
941
+ }));
942
+ await Promise.all(promises);
943
+ }
944
+ /** Get multiple values at once */
945
+ async getMany(keys, namespace) {
946
+ const promises = keys.map(async (key) => ({
947
+ key,
948
+ value: await this.get(key, namespace)
949
+ }));
950
+ return Promise.all(promises);
951
+ }
952
+ /** Get the underlying backend (for advanced use cases) */
953
+ getBackend() {
954
+ return this.backend;
955
+ }
956
+ };
957
+
468
958
  //#endregion
469
959
  //#region src/utils/id.ts
470
960
  const generateId = (size) => {
@@ -793,7 +1283,11 @@ const createConfigVariantDataLayer = (db) => {
793
1283
  "version"
794
1284
  ]).where("variantId", "=", configVariant.variantId).orderBy("version", "desc").limit(1).executeTakeFirst();
795
1285
  if (!versionData) throw new LLMOpsError(`No variant version found for variant ${configVariant.variantId}`);
796
- return versionData;
1286
+ return {
1287
+ ...versionData,
1288
+ configId: resolvedConfigId,
1289
+ variantId: configVariant.variantId
1290
+ };
797
1291
  }
798
1292
  };
799
1293
  };
@@ -953,6 +1447,287 @@ const createEnvironmentSecretDataLayer = (db) => {
953
1447
  };
954
1448
  };
955
1449
 
1450
+ //#endregion
1451
+ //#region src/datalayer/llmRequests.ts
1452
+ /**
1453
+ * Schema for inserting a new LLM request log
1454
+ */
1455
+ const insertLLMRequestSchema = require_db.zod_default.object({
1456
+ requestId: require_db.zod_default.string().uuid(),
1457
+ configId: require_db.zod_default.string().uuid().nullable().optional(),
1458
+ variantId: require_db.zod_default.string().uuid().nullable().optional(),
1459
+ provider: require_db.zod_default.string(),
1460
+ model: require_db.zod_default.string(),
1461
+ promptTokens: require_db.zod_default.number().int().default(0),
1462
+ completionTokens: require_db.zod_default.number().int().default(0),
1463
+ totalTokens: require_db.zod_default.number().int().default(0),
1464
+ cachedTokens: require_db.zod_default.number().int().default(0),
1465
+ cost: require_db.zod_default.number().int().default(0),
1466
+ inputCost: require_db.zod_default.number().int().default(0),
1467
+ outputCost: require_db.zod_default.number().int().default(0),
1468
+ endpoint: require_db.zod_default.string(),
1469
+ statusCode: require_db.zod_default.number().int(),
1470
+ latencyMs: require_db.zod_default.number().int().default(0),
1471
+ isStreaming: require_db.zod_default.boolean().default(false),
1472
+ userId: require_db.zod_default.string().nullable().optional(),
1473
+ tags: require_db.zod_default.record(require_db.zod_default.string(), require_db.zod_default.string()).default({})
1474
+ });
1475
+ /**
1476
+ * Schema for listing LLM requests
1477
+ */
1478
+ const listRequestsSchema = require_db.zod_default.object({
1479
+ limit: require_db.zod_default.number().int().positive().max(1e3).default(100),
1480
+ offset: require_db.zod_default.number().int().nonnegative().default(0),
1481
+ configId: require_db.zod_default.string().uuid().optional(),
1482
+ provider: require_db.zod_default.string().optional(),
1483
+ model: require_db.zod_default.string().optional(),
1484
+ startDate: require_db.zod_default.date().optional(),
1485
+ endDate: require_db.zod_default.date().optional()
1486
+ });
1487
+ /**
1488
+ * Schema for date range queries
1489
+ */
1490
+ const dateRangeSchema = require_db.zod_default.object({
1491
+ startDate: require_db.zod_default.date(),
1492
+ endDate: require_db.zod_default.date()
1493
+ });
1494
+ /**
1495
+ * Schema for cost summary with grouping
1496
+ */
1497
+ const costSummarySchema = require_db.zod_default.object({
1498
+ startDate: require_db.zod_default.date(),
1499
+ endDate: require_db.zod_default.date(),
1500
+ groupBy: require_db.zod_default.enum([
1501
+ "day",
1502
+ "hour",
1503
+ "model",
1504
+ "provider",
1505
+ "config"
1506
+ ]).optional()
1507
+ });
1508
+ /**
1509
+ * Helper to create column reference for SQL
1510
+ * Uses sql.ref() to properly quote column names for the database
1511
+ */
1512
+ const col = (name) => kysely.sql.ref(name);
1513
+ const tableCol = (table, name) => kysely.sql.ref(`${table}.${name}`);
1514
+ const createLLMRequestsDataLayer = (db) => {
1515
+ return {
1516
+ batchInsertRequests: async (requests) => {
1517
+ if (requests.length === 0) return { count: 0 };
1518
+ const validatedRequests = await Promise.all(requests.map(async (req) => {
1519
+ const result = await insertLLMRequestSchema.safeParseAsync(req);
1520
+ if (!result.success) throw new LLMOpsError(`Invalid request data: ${result.error.message}`);
1521
+ return result.data;
1522
+ }));
1523
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1524
+ const values = validatedRequests.map((req) => ({
1525
+ id: (0, node_crypto.randomUUID)(),
1526
+ requestId: req.requestId,
1527
+ configId: req.configId ?? null,
1528
+ variantId: req.variantId ?? null,
1529
+ provider: req.provider,
1530
+ model: req.model,
1531
+ promptTokens: req.promptTokens,
1532
+ completionTokens: req.completionTokens,
1533
+ totalTokens: req.totalTokens,
1534
+ cachedTokens: req.cachedTokens,
1535
+ cost: req.cost,
1536
+ inputCost: req.inputCost,
1537
+ outputCost: req.outputCost,
1538
+ endpoint: req.endpoint,
1539
+ statusCode: req.statusCode,
1540
+ latencyMs: req.latencyMs,
1541
+ isStreaming: req.isStreaming,
1542
+ userId: req.userId ?? null,
1543
+ tags: JSON.stringify(req.tags),
1544
+ createdAt: now,
1545
+ updatedAt: now
1546
+ }));
1547
+ await db.insertInto("llm_requests").values(values).execute();
1548
+ return { count: values.length };
1549
+ },
1550
+ insertRequest: async (request) => {
1551
+ const result = await insertLLMRequestSchema.safeParseAsync(request);
1552
+ if (!result.success) throw new LLMOpsError(`Invalid request data: ${result.error.message}`);
1553
+ const req = result.data;
1554
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1555
+ return db.insertInto("llm_requests").values({
1556
+ id: (0, node_crypto.randomUUID)(),
1557
+ requestId: req.requestId,
1558
+ configId: req.configId ?? null,
1559
+ variantId: req.variantId ?? null,
1560
+ provider: req.provider,
1561
+ model: req.model,
1562
+ promptTokens: req.promptTokens,
1563
+ completionTokens: req.completionTokens,
1564
+ totalTokens: req.totalTokens,
1565
+ cachedTokens: req.cachedTokens,
1566
+ cost: req.cost,
1567
+ inputCost: req.inputCost,
1568
+ outputCost: req.outputCost,
1569
+ endpoint: req.endpoint,
1570
+ statusCode: req.statusCode,
1571
+ latencyMs: req.latencyMs,
1572
+ isStreaming: req.isStreaming,
1573
+ userId: req.userId ?? null,
1574
+ tags: JSON.stringify(req.tags),
1575
+ createdAt: now,
1576
+ updatedAt: now
1577
+ }).returningAll().executeTakeFirst();
1578
+ },
1579
+ listRequests: async (params) => {
1580
+ const result = await listRequestsSchema.safeParseAsync(params || {});
1581
+ if (!result.success) throw new LLMOpsError(`Invalid parameters: ${result.error.message}`);
1582
+ const { limit, offset, configId, provider, model, startDate, endDate } = result.data;
1583
+ let baseQuery = db.selectFrom("llm_requests");
1584
+ if (configId) baseQuery = baseQuery.where("configId", "=", configId);
1585
+ if (provider) baseQuery = baseQuery.where("provider", "=", provider);
1586
+ if (model) baseQuery = baseQuery.where("model", "=", model);
1587
+ if (startDate) baseQuery = baseQuery.where(kysely.sql`${col("createdAt")} >= ${startDate.toISOString()}`);
1588
+ if (endDate) baseQuery = baseQuery.where(kysely.sql`${col("createdAt")} <= ${endDate.toISOString()}`);
1589
+ const countResult = await baseQuery.select(kysely.sql`COUNT(*)`.as("total")).executeTakeFirst();
1590
+ const total = Number(countResult?.total ?? 0);
1591
+ return {
1592
+ data: await baseQuery.selectAll().orderBy("createdAt", "desc").limit(limit).offset(offset).execute(),
1593
+ total,
1594
+ limit,
1595
+ offset
1596
+ };
1597
+ },
1598
+ getRequestByRequestId: async (requestId) => {
1599
+ return db.selectFrom("llm_requests").selectAll().where("requestId", "=", requestId).executeTakeFirst();
1600
+ },
1601
+ getTotalCost: async (params) => {
1602
+ const result = await dateRangeSchema.safeParseAsync(params);
1603
+ if (!result.success) throw new LLMOpsError(`Invalid parameters: ${result.error.message}`);
1604
+ const { startDate, endDate } = result.data;
1605
+ return await db.selectFrom("llm_requests").select([
1606
+ kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
1607
+ kysely.sql`COALESCE(SUM(${col("inputCost")}), 0)`.as("totalInputCost"),
1608
+ kysely.sql`COALESCE(SUM(${col("outputCost")}), 0)`.as("totalOutputCost"),
1609
+ kysely.sql`COALESCE(SUM(${col("promptTokens")}), 0)`.as("totalPromptTokens"),
1610
+ kysely.sql`COALESCE(SUM(${col("completionTokens")}), 0)`.as("totalCompletionTokens"),
1611
+ kysely.sql`COALESCE(SUM(${col("totalTokens")}), 0)`.as("totalTokens"),
1612
+ kysely.sql`COUNT(*)`.as("requestCount")
1613
+ ]).where(kysely.sql`${col("createdAt")} >= ${startDate.toISOString()}`).where(kysely.sql`${col("createdAt")} <= ${endDate.toISOString()}`).executeTakeFirst();
1614
+ },
1615
+ getCostByModel: async (params) => {
1616
+ const result = await dateRangeSchema.safeParseAsync(params);
1617
+ if (!result.success) throw new LLMOpsError(`Invalid parameters: ${result.error.message}`);
1618
+ const { startDate, endDate } = result.data;
1619
+ return db.selectFrom("llm_requests").select([
1620
+ "provider",
1621
+ "model",
1622
+ kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
1623
+ kysely.sql`COALESCE(SUM(${col("inputCost")}), 0)`.as("totalInputCost"),
1624
+ kysely.sql`COALESCE(SUM(${col("outputCost")}), 0)`.as("totalOutputCost"),
1625
+ kysely.sql`COALESCE(SUM(${col("totalTokens")}), 0)`.as("totalTokens"),
1626
+ kysely.sql`COUNT(*)`.as("requestCount"),
1627
+ kysely.sql`AVG(${col("latencyMs")})`.as("avgLatencyMs")
1628
+ ]).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();
1629
+ },
1630
+ getCostByProvider: async (params) => {
1631
+ const result = await dateRangeSchema.safeParseAsync(params);
1632
+ if (!result.success) throw new LLMOpsError(`Invalid parameters: ${result.error.message}`);
1633
+ const { startDate, endDate } = result.data;
1634
+ return db.selectFrom("llm_requests").select([
1635
+ "provider",
1636
+ kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
1637
+ kysely.sql`COALESCE(SUM(${col("inputCost")}), 0)`.as("totalInputCost"),
1638
+ kysely.sql`COALESCE(SUM(${col("outputCost")}), 0)`.as("totalOutputCost"),
1639
+ kysely.sql`COALESCE(SUM(${col("totalTokens")}), 0)`.as("totalTokens"),
1640
+ kysely.sql`COUNT(*)`.as("requestCount"),
1641
+ kysely.sql`AVG(${col("latencyMs")})`.as("avgLatencyMs")
1642
+ ]).where(kysely.sql`${col("createdAt")} >= ${startDate.toISOString()}`).where(kysely.sql`${col("createdAt")} <= ${endDate.toISOString()}`).groupBy("provider").orderBy(kysely.sql`SUM(${col("cost")})`, "desc").execute();
1643
+ },
1644
+ getCostByConfig: async (params) => {
1645
+ const result = await dateRangeSchema.safeParseAsync(params);
1646
+ if (!result.success) throw new LLMOpsError(`Invalid parameters: ${result.error.message}`);
1647
+ const { startDate, endDate } = result.data;
1648
+ return db.selectFrom("llm_requests").leftJoin("configs", "llm_requests.configId", "configs.id").select([
1649
+ "llm_requests.configId",
1650
+ "configs.name as configName",
1651
+ "configs.slug as configSlug",
1652
+ kysely.sql`COALESCE(SUM(${tableCol("llm_requests", "cost")}), 0)`.as("totalCost"),
1653
+ kysely.sql`COALESCE(SUM(${tableCol("llm_requests", "inputCost")}), 0)`.as("totalInputCost"),
1654
+ kysely.sql`COALESCE(SUM(${tableCol("llm_requests", "outputCost")}), 0)`.as("totalOutputCost"),
1655
+ kysely.sql`COALESCE(SUM(${tableCol("llm_requests", "totalTokens")}), 0)`.as("totalTokens"),
1656
+ kysely.sql`COUNT(*)`.as("requestCount")
1657
+ ]).where(kysely.sql`${tableCol("llm_requests", "createdAt")} >= ${startDate.toISOString()}`).where(kysely.sql`${tableCol("llm_requests", "createdAt")} <= ${endDate.toISOString()}`).groupBy([
1658
+ "llm_requests.configId",
1659
+ "configs.name",
1660
+ "configs.slug"
1661
+ ]).orderBy(kysely.sql`SUM(${tableCol("llm_requests", "cost")})`, "desc").execute();
1662
+ },
1663
+ getDailyCosts: async (params) => {
1664
+ const result = await dateRangeSchema.safeParseAsync(params);
1665
+ if (!result.success) throw new LLMOpsError(`Invalid parameters: ${result.error.message}`);
1666
+ const { startDate, endDate } = result.data;
1667
+ return db.selectFrom("llm_requests").select([
1668
+ kysely.sql`DATE(${col("createdAt")})`.as("date"),
1669
+ kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
1670
+ kysely.sql`COALESCE(SUM(${col("inputCost")}), 0)`.as("totalInputCost"),
1671
+ kysely.sql`COALESCE(SUM(${col("outputCost")}), 0)`.as("totalOutputCost"),
1672
+ kysely.sql`COALESCE(SUM(${col("totalTokens")}), 0)`.as("totalTokens"),
1673
+ kysely.sql`COUNT(*)`.as("requestCount")
1674
+ ]).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();
1675
+ },
1676
+ getCostSummary: async (params) => {
1677
+ const result = await costSummarySchema.safeParseAsync(params);
1678
+ if (!result.success) throw new LLMOpsError(`Invalid parameters: ${result.error.message}`);
1679
+ const { startDate, endDate, groupBy } = result.data;
1680
+ const baseQuery = db.selectFrom("llm_requests").where(kysely.sql`${col("createdAt")} >= ${startDate.toISOString()}`).where(kysely.sql`${col("createdAt")} <= ${endDate.toISOString()}`);
1681
+ switch (groupBy) {
1682
+ case "day": return baseQuery.select([
1683
+ kysely.sql`DATE(${col("createdAt")})`.as("groupKey"),
1684
+ kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
1685
+ kysely.sql`COUNT(*)`.as("requestCount")
1686
+ ]).groupBy(kysely.sql`DATE(${col("createdAt")})`).orderBy(kysely.sql`DATE(${col("createdAt")})`, "asc").execute();
1687
+ case "hour": return baseQuery.select([
1688
+ kysely.sql`DATE_TRUNC('hour', ${col("createdAt")})`.as("groupKey"),
1689
+ kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
1690
+ kysely.sql`COUNT(*)`.as("requestCount")
1691
+ ]).groupBy(kysely.sql`DATE_TRUNC('hour', ${col("createdAt")})`).orderBy(kysely.sql`DATE_TRUNC('hour', ${col("createdAt")})`, "asc").execute();
1692
+ case "model": return baseQuery.select([
1693
+ kysely.sql`${col("provider")} || '/' || ${col("model")}`.as("groupKey"),
1694
+ kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
1695
+ kysely.sql`COUNT(*)`.as("requestCount")
1696
+ ]).groupBy(["provider", "model"]).orderBy(kysely.sql`SUM(${col("cost")})`, "desc").execute();
1697
+ case "provider": return baseQuery.select([
1698
+ kysely.sql`${col("provider")}`.as("groupKey"),
1699
+ kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
1700
+ kysely.sql`COUNT(*)`.as("requestCount")
1701
+ ]).groupBy("provider").orderBy(kysely.sql`SUM(${col("cost")})`, "desc").execute();
1702
+ case "config": return baseQuery.select([
1703
+ kysely.sql`COALESCE(${col("configId")}::text, 'no-config')`.as("groupKey"),
1704
+ kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
1705
+ kysely.sql`COUNT(*)`.as("requestCount")
1706
+ ]).groupBy("configId").orderBy(kysely.sql`SUM(${col("cost")})`, "desc").execute();
1707
+ default: return baseQuery.select([
1708
+ kysely.sql`'total'`.as("groupKey"),
1709
+ kysely.sql`COALESCE(SUM(${col("cost")}), 0)`.as("totalCost"),
1710
+ kysely.sql`COUNT(*)`.as("requestCount")
1711
+ ]).execute();
1712
+ }
1713
+ },
1714
+ getRequestStats: async (params) => {
1715
+ const result = await dateRangeSchema.safeParseAsync(params);
1716
+ if (!result.success) throw new LLMOpsError(`Invalid parameters: ${result.error.message}`);
1717
+ const { startDate, endDate } = result.data;
1718
+ return await db.selectFrom("llm_requests").select([
1719
+ kysely.sql`COUNT(*)`.as("totalRequests"),
1720
+ kysely.sql`COUNT(CASE WHEN ${col("statusCode")} >= 200 AND ${col("statusCode")} < 300 THEN 1 END)`.as("successfulRequests"),
1721
+ kysely.sql`COUNT(CASE WHEN ${col("statusCode")} >= 400 THEN 1 END)`.as("failedRequests"),
1722
+ kysely.sql`COUNT(CASE WHEN ${col("isStreaming")} = true THEN 1 END)`.as("streamingRequests"),
1723
+ kysely.sql`AVG(${col("latencyMs")})`.as("avgLatencyMs"),
1724
+ kysely.sql`MAX(${col("latencyMs")})`.as("maxLatencyMs"),
1725
+ kysely.sql`MIN(${col("latencyMs")})`.as("minLatencyMs")
1726
+ ]).where(kysely.sql`${col("createdAt")} >= ${startDate.toISOString()}`).where(kysely.sql`${col("createdAt")} <= ${endDate.toISOString()}`).executeTakeFirst();
1727
+ }
1728
+ };
1729
+ };
1730
+
956
1731
  //#endregion
957
1732
  //#region src/datalayer/targetingRules.ts
958
1733
  const createTargetingRule = require_db.zod_default.object({
@@ -1379,6 +2154,7 @@ const createDataLayer = async (db) => {
1379
2154
  ...createConfigVariantDataLayer(db),
1380
2155
  ...createEnvironmentDataLayer(db),
1381
2156
  ...createEnvironmentSecretDataLayer(db),
2157
+ ...createLLMRequestsDataLayer(db),
1382
2158
  ...createTargetingRulesDataLayer(db),
1383
2159
  ...createVariantDataLayer(db),
1384
2160
  ...createVariantVersionsDataLayer(db),
@@ -1387,17 +2163,229 @@ const createDataLayer = async (db) => {
1387
2163
  };
1388
2164
 
1389
2165
  //#endregion
2166
+ //#region src/pricing/calculator.ts
2167
+ /**
2168
+ * Calculate the cost of an LLM request in micro-dollars
2169
+ *
2170
+ * Micro-dollars are used to avoid floating-point precision issues:
2171
+ * - 1 dollar = 1,000,000 micro-dollars
2172
+ * - $0.001 = 1,000 micro-dollars
2173
+ * - $0.000001 = 1 micro-dollar
2174
+ *
2175
+ * @param usage - Token usage data from the LLM response
2176
+ * @param pricing - Model pricing information
2177
+ * @returns Cost breakdown in micro-dollars
2178
+ *
2179
+ * @example
2180
+ * ```typescript
2181
+ * const usage = { promptTokens: 1000, completionTokens: 500 };
2182
+ * const pricing = { inputCostPer1M: 2.5, outputCostPer1M: 10.0 };
2183
+ * const cost = calculateCost(usage, pricing);
2184
+ * // cost = { inputCost: 2500, outputCost: 5000, totalCost: 7500 }
2185
+ * // In dollars: $0.0025 input + $0.005 output = $0.0075 total
2186
+ * ```
2187
+ */
2188
+ function calculateCost(usage, pricing) {
2189
+ const inputCost = Math.round(usage.promptTokens * pricing.inputCostPer1M);
2190
+ const outputCost = Math.round(usage.completionTokens * pricing.outputCostPer1M);
2191
+ return {
2192
+ inputCost,
2193
+ outputCost,
2194
+ totalCost: inputCost + outputCost
2195
+ };
2196
+ }
2197
+ /**
2198
+ * Convert micro-dollars to dollars
2199
+ *
2200
+ * @param microDollars - Amount in micro-dollars
2201
+ * @returns Amount in dollars
2202
+ *
2203
+ * @example
2204
+ * ```typescript
2205
+ * microDollarsToDollars(7500); // 0.0075
2206
+ * microDollarsToDollars(1000000); // 1.0
2207
+ * ```
2208
+ */
2209
+ function microDollarsToDollars(microDollars) {
2210
+ return microDollars / 1e6;
2211
+ }
2212
+ /**
2213
+ * Convert dollars to micro-dollars
2214
+ *
2215
+ * @param dollars - Amount in dollars
2216
+ * @returns Amount in micro-dollars (rounded to nearest integer)
2217
+ *
2218
+ * @example
2219
+ * ```typescript
2220
+ * dollarsToMicroDollars(0.0075); // 7500
2221
+ * dollarsToMicroDollars(1.0); // 1000000
2222
+ * ```
2223
+ */
2224
+ function dollarsToMicroDollars(dollars) {
2225
+ return Math.round(dollars * 1e6);
2226
+ }
2227
+ /**
2228
+ * Format micro-dollars as a human-readable dollar string
2229
+ *
2230
+ * @param microDollars - Amount in micro-dollars
2231
+ * @param decimals - Number of decimal places (default: 6)
2232
+ * @returns Formatted dollar string
2233
+ *
2234
+ * @example
2235
+ * ```typescript
2236
+ * formatCost(7500); // "$0.007500"
2237
+ * formatCost(1234567, 2); // "$1.23"
2238
+ * ```
2239
+ */
2240
+ function formatCost(microDollars, decimals = 6) {
2241
+ return `$${microDollarsToDollars(microDollars).toFixed(decimals)}`;
2242
+ }
2243
+
2244
+ //#endregion
2245
+ //#region src/pricing/provider.ts
2246
+ const MODELS_DEV_API = "https://models.dev/api.json";
2247
+ /**
2248
+ * Pricing provider that fetches data from models.dev API
2249
+ *
2250
+ * Features:
2251
+ * - Caches pricing data with configurable TTL (default 5 minutes)
2252
+ * - Supports fallback to local cache on fetch failure
2253
+ * - Thread-safe cache refresh
2254
+ */
2255
+ var ModelsDevPricingProvider = class {
2256
+ cache = /* @__PURE__ */ new Map();
2257
+ lastFetch = 0;
2258
+ cacheTTL;
2259
+ fetchPromise = null;
2260
+ ready = false;
2261
+ /**
2262
+ * Create a new ModelsDevPricingProvider
2263
+ *
2264
+ * @param cacheTTL - Cache TTL in milliseconds (default: 5 minutes)
2265
+ */
2266
+ constructor(cacheTTL = 300 * 1e3) {
2267
+ this.cacheTTL = cacheTTL;
2268
+ }
2269
+ /**
2270
+ * Generate a cache key for a provider/model combination
2271
+ */
2272
+ getCacheKey(provider, model) {
2273
+ return `${provider.toLowerCase()}:${model.toLowerCase()}`;
2274
+ }
2275
+ /**
2276
+ * Fetch pricing data from models.dev API
2277
+ */
2278
+ async fetchPricingData() {
2279
+ try {
2280
+ require_db.logger.debug("[Pricing] Fetching pricing data from models.dev");
2281
+ const response = await fetch(MODELS_DEV_API);
2282
+ if (!response.ok) throw new Error(`Failed to fetch models.dev API: ${response.status}`);
2283
+ const data = await response.json();
2284
+ this.cache.clear();
2285
+ for (const [providerId, provider] of Object.entries(data)) {
2286
+ if (!provider.models) continue;
2287
+ for (const [_modelId, model] of Object.entries(provider.models)) {
2288
+ if (!model.cost) continue;
2289
+ const cacheKey = this.getCacheKey(providerId, model.id);
2290
+ this.cache.set(cacheKey, {
2291
+ inputCostPer1M: model.cost.input ?? 0,
2292
+ outputCostPer1M: model.cost.output ?? 0,
2293
+ cacheReadCostPer1M: model.cost.cache_read,
2294
+ cacheWriteCostPer1M: model.cost.cache_write,
2295
+ reasoningCostPer1M: model.cost.reasoning
2296
+ });
2297
+ const nameKey = this.getCacheKey(providerId, model.name);
2298
+ if (nameKey !== cacheKey) this.cache.set(nameKey, this.cache.get(cacheKey));
2299
+ }
2300
+ }
2301
+ this.lastFetch = Date.now();
2302
+ this.ready = true;
2303
+ require_db.logger.debug(`[Pricing] Cached pricing for ${this.cache.size} models from models.dev`);
2304
+ } catch (error) {
2305
+ require_db.logger.error(`[Pricing] Failed to fetch pricing data: ${error instanceof Error ? error.message : String(error)}`);
2306
+ if (this.cache.size === 0) throw error;
2307
+ }
2308
+ }
2309
+ /**
2310
+ * Ensure cache is fresh, fetching if necessary
2311
+ */
2312
+ async ensureFreshCache() {
2313
+ if (!(Date.now() - this.lastFetch > this.cacheTTL) && this.cache.size > 0) return;
2314
+ if (!this.fetchPromise) this.fetchPromise = this.fetchPricingData().finally(() => {
2315
+ this.fetchPromise = null;
2316
+ });
2317
+ await this.fetchPromise;
2318
+ }
2319
+ /**
2320
+ * Get pricing for a specific model
2321
+ */
2322
+ async getModelPricing(provider, model) {
2323
+ await this.ensureFreshCache();
2324
+ const cacheKey = this.getCacheKey(provider, model);
2325
+ const pricing = this.cache.get(cacheKey);
2326
+ if (!pricing) {
2327
+ require_db.logger.debug(`[Pricing] No pricing found for ${provider}/${model}, trying partial match`);
2328
+ for (const [key, value] of this.cache.entries()) if (key.startsWith(`${provider.toLowerCase()}:`)) {
2329
+ const modelPart = key.split(":")[1];
2330
+ if (model.toLowerCase().includes(modelPart)) {
2331
+ require_db.logger.debug(`[Pricing] Found partial match: ${key}`);
2332
+ return value;
2333
+ }
2334
+ }
2335
+ return null;
2336
+ }
2337
+ return pricing;
2338
+ }
2339
+ /**
2340
+ * Force refresh the pricing cache
2341
+ */
2342
+ async refreshCache() {
2343
+ this.lastFetch = 0;
2344
+ await this.ensureFreshCache();
2345
+ }
2346
+ /**
2347
+ * Check if the provider is ready
2348
+ */
2349
+ isReady() {
2350
+ return this.ready;
2351
+ }
2352
+ /**
2353
+ * Get the number of cached models (for debugging)
2354
+ */
2355
+ getCacheSize() {
2356
+ return this.cache.size;
2357
+ }
2358
+ };
2359
+ let defaultProvider = null;
2360
+ /**
2361
+ * Get the default pricing provider instance
2362
+ */
2363
+ function getDefaultPricingProvider() {
2364
+ if (!defaultProvider) defaultProvider = new ModelsDevPricingProvider();
2365
+ return defaultProvider;
2366
+ }
2367
+
2368
+ //#endregion
2369
+ exports.CacheService = CacheService;
2370
+ exports.FileCacheBackend = FileCacheBackend;
2371
+ exports.MS = MS;
2372
+ exports.MemoryCacheBackend = MemoryCacheBackend;
2373
+ exports.ModelsDevPricingProvider = ModelsDevPricingProvider;
1390
2374
  exports.SCHEMA_METADATA = require_db.SCHEMA_METADATA;
1391
2375
  exports.SupportedProviders = SupportedProviders;
2376
+ exports.calculateCost = calculateCost;
1392
2377
  exports.chatCompletionCreateParamsBaseSchema = chatCompletionCreateParamsBaseSchema;
1393
2378
  exports.configVariantsSchema = require_db.configVariantsSchema;
1394
2379
  exports.configsSchema = require_db.configsSchema;
1395
2380
  exports.createDataLayer = createDataLayer;
1396
2381
  exports.createDatabase = require_db.createDatabase;
1397
2382
  exports.createDatabaseFromConnection = require_db.createDatabaseFromConnection;
2383
+ exports.createLLMRequestsDataLayer = createLLMRequestsDataLayer;
1398
2384
  exports.detectDatabaseType = require_db.detectDatabaseType;
2385
+ exports.dollarsToMicroDollars = dollarsToMicroDollars;
1399
2386
  exports.environmentSecretsSchema = require_db.environmentSecretsSchema;
1400
2387
  exports.environmentsSchema = require_db.environmentsSchema;
2388
+ exports.formatCost = formatCost;
1401
2389
  Object.defineProperty(exports, 'gateway', {
1402
2390
  enumerable: true,
1403
2391
  get: function () {
@@ -1405,10 +2393,13 @@ Object.defineProperty(exports, 'gateway', {
1405
2393
  }
1406
2394
  });
1407
2395
  exports.generateId = generateId;
2396
+ exports.getDefaultPricingProvider = getDefaultPricingProvider;
1408
2397
  exports.getMigrations = require_db.getMigrations;
2398
+ exports.llmRequestsSchema = require_db.llmRequestsSchema;
1409
2399
  exports.llmopsConfigSchema = llmopsConfigSchema;
1410
2400
  exports.logger = require_db.logger;
1411
2401
  exports.matchType = require_db.matchType;
2402
+ exports.microDollarsToDollars = microDollarsToDollars;
1412
2403
  exports.parsePartialTableData = require_db.parsePartialTableData;
1413
2404
  exports.parseTableData = require_db.parseTableData;
1414
2405
  exports.runAutoMigrations = require_db.runAutoMigrations;