@llmops/core 0.1.3 → 0.1.5-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/index.cjs CHANGED
@@ -1,6 +1,10 @@
1
1
  const require_db = require('./db-C9-M-kdS.cjs');
2
2
  let __llmops_gateway = require("@llmops/gateway");
3
3
  __llmops_gateway = require_db.__toESM(__llmops_gateway);
4
+ let node_fs_promises = require("node:fs/promises");
5
+ node_fs_promises = require_db.__toESM(node_fs_promises);
6
+ let node_path = require("node:path");
7
+ node_path = require_db.__toESM(node_path);
4
8
  let __better_auth_utils_random = require("@better-auth/utils/random");
5
9
  let node_crypto = require("node:crypto");
6
10
 
@@ -465,6 +469,491 @@ function validateLLMOpsConfig(config) {
465
469
  return result.data;
466
470
  }
467
471
 
472
+ //#endregion
473
+ //#region src/cache/types.ts
474
+ /** Time constants in milliseconds for convenience */
475
+ const MS = {
476
+ "1_MINUTE": 60 * 1e3,
477
+ "5_MINUTES": 300 * 1e3,
478
+ "10_MINUTES": 600 * 1e3,
479
+ "30_MINUTES": 1800 * 1e3,
480
+ "1_HOUR": 3600 * 1e3,
481
+ "6_HOURS": 360 * 60 * 1e3,
482
+ "12_HOURS": 720 * 60 * 1e3,
483
+ "1_DAY": 1440 * 60 * 1e3,
484
+ "7_DAYS": 10080 * 60 * 1e3,
485
+ "30_DAYS": 720 * 60 * 60 * 1e3
486
+ };
487
+
488
+ //#endregion
489
+ //#region src/cache/backends/memory.ts
490
+ var MemoryCacheBackend = class {
491
+ cache = /* @__PURE__ */ new Map();
492
+ stats = {
493
+ hits: 0,
494
+ misses: 0,
495
+ sets: 0,
496
+ deletes: 0,
497
+ size: 0,
498
+ expired: 0
499
+ };
500
+ cleanupInterval;
501
+ maxSize;
502
+ constructor(maxSize = 1e4, cleanupIntervalMs = 6e4) {
503
+ this.maxSize = maxSize;
504
+ this.startCleanup(cleanupIntervalMs);
505
+ }
506
+ startCleanup(intervalMs) {
507
+ this.cleanupInterval = setInterval(() => {
508
+ this.cleanup();
509
+ }, intervalMs);
510
+ }
511
+ getFullKey(key, namespace) {
512
+ return namespace ? `${namespace}:${key}` : key;
513
+ }
514
+ isExpired(entry) {
515
+ return entry.expiresAt !== void 0 && entry.expiresAt <= Date.now();
516
+ }
517
+ evictIfNeeded() {
518
+ if (this.cache.size >= this.maxSize) {
519
+ const entries = Array.from(this.cache.entries());
520
+ entries.sort((a, b) => a[1].createdAt - b[1].createdAt);
521
+ const toRemove = Math.floor(this.maxSize * .1);
522
+ for (let i = 0; i < toRemove && i < entries.length; i++) this.cache.delete(entries[i][0]);
523
+ }
524
+ }
525
+ async get(key, namespace) {
526
+ const fullKey = this.getFullKey(key, namespace);
527
+ const entry = this.cache.get(fullKey);
528
+ if (!entry) {
529
+ this.stats.misses++;
530
+ return null;
531
+ }
532
+ if (this.isExpired(entry)) {
533
+ this.cache.delete(fullKey);
534
+ this.stats.expired++;
535
+ this.stats.misses++;
536
+ return null;
537
+ }
538
+ this.stats.hits++;
539
+ return entry;
540
+ }
541
+ async set(key, value, options = {}) {
542
+ const fullKey = this.getFullKey(key, options.namespace);
543
+ const now = Date.now();
544
+ const entry = {
545
+ value,
546
+ createdAt: now,
547
+ expiresAt: options.ttl ? now + options.ttl : void 0,
548
+ metadata: options.metadata
549
+ };
550
+ this.evictIfNeeded();
551
+ this.cache.set(fullKey, entry);
552
+ this.stats.sets++;
553
+ this.stats.size = this.cache.size;
554
+ }
555
+ async delete(key, namespace) {
556
+ const fullKey = this.getFullKey(key, namespace);
557
+ const deleted = this.cache.delete(fullKey);
558
+ if (deleted) {
559
+ this.stats.deletes++;
560
+ this.stats.size = this.cache.size;
561
+ }
562
+ return deleted;
563
+ }
564
+ async clear(namespace) {
565
+ if (namespace) {
566
+ const prefix = `${namespace}:`;
567
+ const keysToDelete = Array.from(this.cache.keys()).filter((key) => key.startsWith(prefix));
568
+ for (const key of keysToDelete) this.cache.delete(key);
569
+ this.stats.deletes += keysToDelete.length;
570
+ } else {
571
+ this.stats.deletes += this.cache.size;
572
+ this.cache.clear();
573
+ }
574
+ this.stats.size = this.cache.size;
575
+ }
576
+ async has(key, namespace) {
577
+ const fullKey = this.getFullKey(key, namespace);
578
+ const entry = this.cache.get(fullKey);
579
+ if (!entry) return false;
580
+ if (this.isExpired(entry)) {
581
+ this.cache.delete(fullKey);
582
+ this.stats.expired++;
583
+ return false;
584
+ }
585
+ return true;
586
+ }
587
+ async keys(namespace) {
588
+ const allKeys = Array.from(this.cache.keys());
589
+ if (namespace) {
590
+ const prefix = `${namespace}:`;
591
+ return allKeys.filter((key) => key.startsWith(prefix)).map((key) => key.substring(prefix.length));
592
+ }
593
+ return allKeys;
594
+ }
595
+ async getStats(namespace) {
596
+ if (namespace) {
597
+ const prefix = `${namespace}:`;
598
+ const namespaceKeys = Array.from(this.cache.keys()).filter((key) => key.startsWith(prefix));
599
+ let expired = 0;
600
+ for (const key of namespaceKeys) {
601
+ const entry = this.cache.get(key);
602
+ if (entry && this.isExpired(entry)) expired++;
603
+ }
604
+ return {
605
+ ...this.stats,
606
+ size: namespaceKeys.length,
607
+ expired
608
+ };
609
+ }
610
+ return { ...this.stats };
611
+ }
612
+ async cleanup() {
613
+ let expiredCount = 0;
614
+ for (const [key, entry] of this.cache.entries()) if (this.isExpired(entry)) {
615
+ this.cache.delete(key);
616
+ expiredCount++;
617
+ }
618
+ if (expiredCount > 0) {
619
+ this.stats.expired += expiredCount;
620
+ this.stats.size = this.cache.size;
621
+ }
622
+ }
623
+ async close() {
624
+ if (this.cleanupInterval) {
625
+ clearInterval(this.cleanupInterval);
626
+ this.cleanupInterval = void 0;
627
+ }
628
+ this.cache.clear();
629
+ }
630
+ };
631
+
632
+ //#endregion
633
+ //#region src/cache/backends/file.ts
634
+ /**
635
+ * @file src/cache/backends/file.ts
636
+ * File-based cache backend implementation
637
+ */
638
+ var FileCacheBackend = class {
639
+ cacheFile;
640
+ data = {};
641
+ saveTimer;
642
+ cleanupInterval;
643
+ loaded = false;
644
+ loadPromise;
645
+ stats = {
646
+ hits: 0,
647
+ misses: 0,
648
+ sets: 0,
649
+ deletes: 0,
650
+ size: 0,
651
+ expired: 0
652
+ };
653
+ saveInterval;
654
+ constructor(dataDir = "data", fileName = "cache.json", saveIntervalMs = 1e3, cleanupIntervalMs = 6e4) {
655
+ this.cacheFile = node_path.join(process.cwd(), dataDir, fileName);
656
+ this.saveInterval = saveIntervalMs;
657
+ this.loadPromise = this.loadCache();
658
+ this.loadPromise.then(() => {
659
+ this.startCleanup(cleanupIntervalMs);
660
+ });
661
+ }
662
+ /** Ensure cache is loaded before any operation */
663
+ async ensureLoaded() {
664
+ if (!this.loaded) await this.loadPromise;
665
+ }
666
+ async ensureDataDir() {
667
+ const dir = node_path.dirname(this.cacheFile);
668
+ try {
669
+ await node_fs_promises.mkdir(dir, { recursive: true });
670
+ } catch {}
671
+ }
672
+ async loadCache() {
673
+ try {
674
+ const content = await node_fs_promises.readFile(this.cacheFile, "utf-8");
675
+ this.data = JSON.parse(content);
676
+ this.updateStats();
677
+ this.loaded = true;
678
+ } catch {
679
+ this.data = {};
680
+ this.loaded = true;
681
+ }
682
+ }
683
+ async saveCache() {
684
+ try {
685
+ await this.ensureDataDir();
686
+ await node_fs_promises.writeFile(this.cacheFile, JSON.stringify(this.data, null, 2));
687
+ } catch {}
688
+ }
689
+ scheduleSave() {
690
+ if (this.saveTimer) clearTimeout(this.saveTimer);
691
+ this.saveTimer = setTimeout(() => {
692
+ this.saveCache();
693
+ this.saveTimer = void 0;
694
+ }, this.saveInterval);
695
+ }
696
+ startCleanup(intervalMs) {
697
+ this.cleanupInterval = setInterval(() => {
698
+ this.cleanup();
699
+ }, intervalMs);
700
+ }
701
+ isExpired(entry) {
702
+ return entry.expiresAt !== void 0 && entry.expiresAt <= Date.now();
703
+ }
704
+ updateStats() {
705
+ let totalSize = 0;
706
+ let totalExpired = 0;
707
+ for (const namespace of Object.values(this.data)) for (const entry of Object.values(namespace)) {
708
+ totalSize++;
709
+ if (this.isExpired(entry)) totalExpired++;
710
+ }
711
+ this.stats.size = totalSize;
712
+ this.stats.expired = totalExpired;
713
+ }
714
+ getNamespaceData(namespace = "default") {
715
+ if (!this.data[namespace]) this.data[namespace] = {};
716
+ return this.data[namespace];
717
+ }
718
+ async get(key, namespace) {
719
+ await this.ensureLoaded();
720
+ const namespaceData = this.getNamespaceData(namespace);
721
+ const entry = namespaceData[key];
722
+ if (!entry) {
723
+ this.stats.misses++;
724
+ return null;
725
+ }
726
+ if (this.isExpired(entry)) {
727
+ delete namespaceData[key];
728
+ this.stats.expired++;
729
+ this.stats.misses++;
730
+ this.scheduleSave();
731
+ return null;
732
+ }
733
+ this.stats.hits++;
734
+ return entry;
735
+ }
736
+ async set(key, value, options = {}) {
737
+ await this.ensureLoaded();
738
+ const namespace = options.namespace || "default";
739
+ const namespaceData = this.getNamespaceData(namespace);
740
+ const now = Date.now();
741
+ namespaceData[key] = {
742
+ value,
743
+ createdAt: now,
744
+ expiresAt: options.ttl ? now + options.ttl : void 0,
745
+ metadata: options.metadata
746
+ };
747
+ this.stats.sets++;
748
+ this.updateStats();
749
+ this.scheduleSave();
750
+ }
751
+ async delete(key, namespace) {
752
+ const namespaceData = this.getNamespaceData(namespace);
753
+ const existed = key in namespaceData;
754
+ if (existed) {
755
+ delete namespaceData[key];
756
+ this.stats.deletes++;
757
+ this.updateStats();
758
+ this.scheduleSave();
759
+ }
760
+ return existed;
761
+ }
762
+ async clear(namespace) {
763
+ if (namespace) {
764
+ const namespaceData = this.getNamespaceData(namespace);
765
+ const count = Object.keys(namespaceData).length;
766
+ this.data[namespace] = {};
767
+ this.stats.deletes += count;
768
+ } else {
769
+ const totalCount = Object.values(this.data).reduce((sum, ns) => sum + Object.keys(ns).length, 0);
770
+ this.data = {};
771
+ this.stats.deletes += totalCount;
772
+ }
773
+ this.updateStats();
774
+ this.scheduleSave();
775
+ }
776
+ async has(key, namespace) {
777
+ const namespaceData = this.getNamespaceData(namespace);
778
+ const entry = namespaceData[key];
779
+ if (!entry) return false;
780
+ if (this.isExpired(entry)) {
781
+ delete namespaceData[key];
782
+ this.stats.expired++;
783
+ this.scheduleSave();
784
+ return false;
785
+ }
786
+ return true;
787
+ }
788
+ async keys(namespace) {
789
+ if (namespace) {
790
+ const namespaceData = this.getNamespaceData(namespace);
791
+ return Object.keys(namespaceData);
792
+ }
793
+ const allKeys = [];
794
+ for (const namespaceData of Object.values(this.data)) allKeys.push(...Object.keys(namespaceData));
795
+ return allKeys;
796
+ }
797
+ async getStats(namespace) {
798
+ if (namespace) {
799
+ const namespaceData = this.getNamespaceData(namespace);
800
+ const keys = Object.keys(namespaceData);
801
+ let expired = 0;
802
+ for (const key of keys) {
803
+ const entry = namespaceData[key];
804
+ if (this.isExpired(entry)) expired++;
805
+ }
806
+ return {
807
+ ...this.stats,
808
+ size: keys.length,
809
+ expired
810
+ };
811
+ }
812
+ this.updateStats();
813
+ return { ...this.stats };
814
+ }
815
+ async cleanup() {
816
+ let expiredCount = 0;
817
+ let hasChanges = false;
818
+ for (const [, namespaceData] of Object.entries(this.data)) for (const [key, entry] of Object.entries(namespaceData)) if (this.isExpired(entry)) {
819
+ delete namespaceData[key];
820
+ expiredCount++;
821
+ hasChanges = true;
822
+ }
823
+ if (hasChanges) {
824
+ this.stats.expired += expiredCount;
825
+ this.updateStats();
826
+ this.scheduleSave();
827
+ }
828
+ }
829
+ /** Wait for the cache to be ready (file loaded) */
830
+ async waitForReady() {
831
+ await this.loadPromise;
832
+ }
833
+ async close() {
834
+ if (this.saveTimer) {
835
+ clearTimeout(this.saveTimer);
836
+ await this.saveCache();
837
+ }
838
+ if (this.cleanupInterval) {
839
+ clearInterval(this.cleanupInterval);
840
+ this.cleanupInterval = void 0;
841
+ }
842
+ }
843
+ };
844
+
845
+ //#endregion
846
+ //#region src/cache/service.ts
847
+ /**
848
+ * @file src/cache/service.ts
849
+ * Unified cache service with pluggable backends
850
+ */
851
+ var CacheService = class {
852
+ backend;
853
+ defaultTtl;
854
+ constructor(config) {
855
+ this.defaultTtl = config.defaultTtl;
856
+ this.backend = this.createBackend(config);
857
+ }
858
+ createBackend(config) {
859
+ switch (config.backend) {
860
+ case "memory": return new MemoryCacheBackend(config.maxSize, config.cleanupInterval);
861
+ case "file": return new FileCacheBackend(config.dataDir, config.fileName, config.saveInterval, config.cleanupInterval);
862
+ default: throw new Error(`Unsupported cache backend: ${config.backend}`);
863
+ }
864
+ }
865
+ /** Get a value from the cache */
866
+ async get(key, namespace) {
867
+ const entry = await this.backend.get(key, namespace);
868
+ return entry ? entry.value : null;
869
+ }
870
+ /** Get the full cache entry (with metadata) */
871
+ async getEntry(key, namespace) {
872
+ return this.backend.get(key, namespace);
873
+ }
874
+ /** Set a value in the cache */
875
+ async set(key, value, options = {}) {
876
+ const finalOptions = {
877
+ ...options,
878
+ ttl: options.ttl ?? this.defaultTtl
879
+ };
880
+ await this.backend.set(key, value, finalOptions);
881
+ }
882
+ /** Set a value with TTL in seconds (convenience method) */
883
+ async setWithTtl(key, value, ttlSeconds, namespace) {
884
+ await this.set(key, value, {
885
+ ttl: ttlSeconds * 1e3,
886
+ namespace
887
+ });
888
+ }
889
+ /** Delete a value from the cache */
890
+ async delete(key, namespace) {
891
+ return this.backend.delete(key, namespace);
892
+ }
893
+ /** Check if a key exists in the cache */
894
+ async has(key, namespace) {
895
+ return this.backend.has(key, namespace);
896
+ }
897
+ /** Get all keys in a namespace */
898
+ async keys(namespace) {
899
+ return this.backend.keys(namespace);
900
+ }
901
+ /** Clear all entries in a namespace (or all entries if no namespace) */
902
+ async clear(namespace) {
903
+ await this.backend.clear(namespace);
904
+ }
905
+ /** Get cache statistics */
906
+ async getStats(namespace) {
907
+ return this.backend.getStats(namespace);
908
+ }
909
+ /** Manually trigger cleanup of expired entries */
910
+ async cleanup() {
911
+ await this.backend.cleanup();
912
+ }
913
+ /** Wait for the backend to be ready */
914
+ async waitForReady() {
915
+ if ("waitForReady" in this.backend) await this.backend.waitForReady();
916
+ }
917
+ /** Close the cache and cleanup resources */
918
+ async close() {
919
+ await this.backend.close();
920
+ }
921
+ /** Get or set pattern - get value, or compute and cache it if not found */
922
+ async getOrSet(key, factory, options = {}) {
923
+ const existing = await this.get(key, options.namespace);
924
+ if (existing !== null) return existing;
925
+ const value = await factory();
926
+ await this.set(key, value, options);
927
+ return value;
928
+ }
929
+ /** Increment a numeric value (simulated atomic operation) */
930
+ async increment(key, delta = 1, options = {}) {
931
+ const newValue = (await this.get(key, options.namespace) || 0) + delta;
932
+ await this.set(key, newValue, options);
933
+ return newValue;
934
+ }
935
+ /** Set multiple values at once */
936
+ async setMany(entries, defaultOptions = {}) {
937
+ const promises = entries.map(({ key, value, options }) => this.set(key, value, {
938
+ ...defaultOptions,
939
+ ...options
940
+ }));
941
+ await Promise.all(promises);
942
+ }
943
+ /** Get multiple values at once */
944
+ async getMany(keys, namespace) {
945
+ const promises = keys.map(async (key) => ({
946
+ key,
947
+ value: await this.get(key, namespace)
948
+ }));
949
+ return Promise.all(promises);
950
+ }
951
+ /** Get the underlying backend (for advanced use cases) */
952
+ getBackend() {
953
+ return this.backend;
954
+ }
955
+ };
956
+
468
957
  //#endregion
469
958
  //#region src/utils/id.ts
470
959
  const generateId = (size) => {
@@ -1338,6 +1827,39 @@ const createVariantVersionsDataLayer = (db) => {
1338
1827
  };
1339
1828
  };
1340
1829
 
1830
+ //#endregion
1831
+ //#region src/datalayer/workspaceSettings.ts
1832
+ const updateWorkspaceSettings = require_db.zod_default.object({ name: require_db.zod_default.string().nullable().optional() });
1833
+ const createWorkspaceSettingsDataLayer = (db) => {
1834
+ return {
1835
+ getWorkspaceSettings: async () => {
1836
+ let settings = await db.selectFrom("workspace_settings").selectAll().executeTakeFirst();
1837
+ if (!settings) settings = await db.insertInto("workspace_settings").values({
1838
+ id: (0, node_crypto.randomUUID)(),
1839
+ name: null,
1840
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1841
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1842
+ }).returningAll().executeTakeFirst();
1843
+ return settings;
1844
+ },
1845
+ updateWorkspaceSettings: async (params) => {
1846
+ const value = await updateWorkspaceSettings.safeParseAsync(params);
1847
+ if (!value.success) throw new LLMOpsError(`Invalid parameters: ${value.error.message}`);
1848
+ let settings = await db.selectFrom("workspace_settings").selectAll().executeTakeFirst();
1849
+ if (!settings) return db.insertInto("workspace_settings").values({
1850
+ id: (0, node_crypto.randomUUID)(),
1851
+ name: value.data.name ?? null,
1852
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1853
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1854
+ }).returningAll().executeTakeFirst();
1855
+ return db.updateTable("workspace_settings").set({
1856
+ name: value.data.name ?? null,
1857
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1858
+ }).where("id", "=", settings.id).returningAll().executeTakeFirst();
1859
+ }
1860
+ };
1861
+ };
1862
+
1341
1863
  //#endregion
1342
1864
  //#region src/datalayer/index.ts
1343
1865
  const createDataLayer = async (db) => {
@@ -1348,11 +1870,16 @@ const createDataLayer = async (db) => {
1348
1870
  ...createEnvironmentSecretDataLayer(db),
1349
1871
  ...createTargetingRulesDataLayer(db),
1350
1872
  ...createVariantDataLayer(db),
1351
- ...createVariantVersionsDataLayer(db)
1873
+ ...createVariantVersionsDataLayer(db),
1874
+ ...createWorkspaceSettingsDataLayer(db)
1352
1875
  };
1353
1876
  };
1354
1877
 
1355
1878
  //#endregion
1879
+ exports.CacheService = CacheService;
1880
+ exports.FileCacheBackend = FileCacheBackend;
1881
+ exports.MS = MS;
1882
+ exports.MemoryCacheBackend = MemoryCacheBackend;
1356
1883
  exports.SCHEMA_METADATA = require_db.SCHEMA_METADATA;
1357
1884
  exports.SupportedProviders = SupportedProviders;
1358
1885
  exports.chatCompletionCreateParamsBaseSchema = chatCompletionCreateParamsBaseSchema;