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