@llmops/core 0.2.13 → 0.3.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{bun-sqlite-dialect-Bxz4B97L.cjs → bun-sqlite-dialect-BNaQHMpk.cjs} +1 -1
- package/dist/db/index.cjs +2 -2
- package/dist/db/index.d.cts +1 -1
- package/dist/db/index.d.mts +1 -1
- package/dist/db/index.mjs +1 -1
- package/dist/{db-DohlAqJU.mjs → db-B-EsQtOz.mjs} +5 -0
- package/dist/{db-BOe6mM51.cjs → db-Du2xmkGS.cjs} +8 -3
- package/dist/{index-C5xtb4gO.d.cts → index-COkIT6TH.d.mts} +15 -1
- package/dist/{index-BO5Rse5J.d.mts → index-DX05tkNg.d.cts} +15 -1
- package/dist/index.cjs +386 -15
- package/dist/index.d.cts +214 -2
- package/dist/index.d.mts +214 -2
- package/dist/index.mjs +382 -15
- package/dist/{neon-dialect-C0GZuGot.cjs → neon-dialect-B-Q6mmbI.cjs} +1 -1
- package/dist/{neon-dialect-BQey5lUw.cjs → neon-dialect-BR1nZmKX.cjs} +1 -1
- package/dist/{node-sqlite-dialect-N8j5UsT-.cjs → node-sqlite-dialect-DpdAEbyp.cjs} +1 -1
- package/package.json +4 -2
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const require_db = require('./db-
|
|
2
|
-
const require_neon_dialect = require('./neon-dialect-
|
|
1
|
+
const require_db = require('./db-Du2xmkGS.cjs');
|
|
2
|
+
const require_neon_dialect = require('./neon-dialect-BR1nZmKX.cjs');
|
|
3
3
|
let __llmops_gateway = require("@llmops/gateway");
|
|
4
4
|
__llmops_gateway = require_db.__toESM(__llmops_gateway);
|
|
5
5
|
let kysely = require("kysely");
|
|
@@ -9,6 +9,8 @@ let node_path = require("node:path");
|
|
|
9
9
|
node_path = require_db.__toESM(node_path);
|
|
10
10
|
let __better_auth_utils_random = require("@better-auth/utils/random");
|
|
11
11
|
let node_crypto = require("node:crypto");
|
|
12
|
+
let json_logic_js = require("json-logic-js");
|
|
13
|
+
json_logic_js = require_db.__toESM(json_logic_js);
|
|
12
14
|
|
|
13
15
|
//#region src/providers/supported-providers.ts
|
|
14
16
|
/**
|
|
@@ -840,7 +842,10 @@ var CacheService = class {
|
|
|
840
842
|
}
|
|
841
843
|
createBackend(config) {
|
|
842
844
|
switch (config.backend) {
|
|
843
|
-
case "memory": return new MemoryCacheBackend(
|
|
845
|
+
case "memory": return new MemoryCacheBackend({
|
|
846
|
+
maxSize: config.maxSize,
|
|
847
|
+
cleanupIntervalMs: config.cleanupInterval
|
|
848
|
+
});
|
|
844
849
|
case "file": return new FileCacheBackend(config.dataDir, config.fileName, config.saveInterval, config.cleanupInterval);
|
|
845
850
|
default: throw new Error(`Unsupported cache backend: ${config.backend}`);
|
|
846
851
|
}
|
|
@@ -1229,9 +1234,9 @@ const createConfigVariantDataLayer = (db) => {
|
|
|
1229
1234
|
const value = await getVariantJsonDataForConfig.safeParseAsync(params);
|
|
1230
1235
|
if (!value.success) throw new LLMOpsError(`Invalid parameters: ${value.error.message}`);
|
|
1231
1236
|
const { configId: configIdOrSlug, envSecret } = value.data;
|
|
1232
|
-
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1237
|
+
const UUID_REGEX$1 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1233
1238
|
let resolvedConfigId;
|
|
1234
|
-
if (UUID_REGEX.test(configIdOrSlug)) resolvedConfigId = configIdOrSlug;
|
|
1239
|
+
if (UUID_REGEX$1.test(configIdOrSlug)) resolvedConfigId = configIdOrSlug;
|
|
1235
1240
|
else {
|
|
1236
1241
|
const config = await db.selectFrom("configs").select("id").where("slug", "=", configIdOrSlug).executeTakeFirst();
|
|
1237
1242
|
if (!config) throw new LLMOpsError(`Config not found: ${configIdOrSlug}`);
|
|
@@ -1267,7 +1272,7 @@ const createConfigVariantDataLayer = (db) => {
|
|
|
1267
1272
|
if (!versionData) throw new LLMOpsError(`No variant version found for variant ${configVariant.variantId}`);
|
|
1268
1273
|
let finalProvider = versionData.provider;
|
|
1269
1274
|
let providerConfigId = null;
|
|
1270
|
-
if (UUID_REGEX.test(versionData.provider)) {
|
|
1275
|
+
if (UUID_REGEX$1.test(versionData.provider)) {
|
|
1271
1276
|
const providerConfig = await db.selectFrom("provider_configs").select(["id", "providerId"]).where("id", "=", versionData.provider).executeTakeFirst();
|
|
1272
1277
|
if (providerConfig) {
|
|
1273
1278
|
finalProvider = providerConfig.providerId;
|
|
@@ -1798,20 +1803,40 @@ const createLLMRequestsDataLayer = (db) => {
|
|
|
1798
1803
|
|
|
1799
1804
|
//#endregion
|
|
1800
1805
|
//#region src/datalayer/providerConfigs.ts
|
|
1806
|
+
/**
|
|
1807
|
+
* Generate a unique slug for a provider config.
|
|
1808
|
+
* If the base slug already exists, appends -01, -02, etc.
|
|
1809
|
+
*/
|
|
1810
|
+
async function generateUniqueSlug(db, baseSlug) {
|
|
1811
|
+
const existing = await db.selectFrom("provider_configs").select("slug").where("slug", "like", `${baseSlug}%`).execute();
|
|
1812
|
+
if (existing.length === 0) return baseSlug;
|
|
1813
|
+
const existingSlugs = new Set(existing.map((e) => e.slug));
|
|
1814
|
+
if (!existingSlugs.has(baseSlug)) return baseSlug;
|
|
1815
|
+
let counter = 1;
|
|
1816
|
+
while (counter < 100) {
|
|
1817
|
+
const candidateSlug = `${baseSlug}-${counter.toString().padStart(2, "0")}`;
|
|
1818
|
+
if (!existingSlugs.has(candidateSlug)) return candidateSlug;
|
|
1819
|
+
counter++;
|
|
1820
|
+
}
|
|
1821
|
+
return `${baseSlug}-${(0, node_crypto.randomUUID)().slice(0, 8)}`;
|
|
1822
|
+
}
|
|
1801
1823
|
const createProviderConfig = require_db.zod_default.object({
|
|
1802
1824
|
providerId: require_db.zod_default.string().min(1),
|
|
1825
|
+
slug: require_db.zod_default.string().nullable().optional(),
|
|
1803
1826
|
name: require_db.zod_default.string().nullable().optional(),
|
|
1804
1827
|
config: require_db.zod_default.record(require_db.zod_default.string(), require_db.zod_default.unknown()),
|
|
1805
1828
|
enabled: require_db.zod_default.boolean().optional().default(true)
|
|
1806
1829
|
});
|
|
1807
1830
|
const updateProviderConfig = require_db.zod_default.object({
|
|
1808
1831
|
id: require_db.zod_default.uuidv4(),
|
|
1832
|
+
slug: require_db.zod_default.string().nullable().optional(),
|
|
1809
1833
|
name: require_db.zod_default.string().nullable().optional(),
|
|
1810
1834
|
config: require_db.zod_default.record(require_db.zod_default.string(), require_db.zod_default.unknown()).optional(),
|
|
1811
1835
|
enabled: require_db.zod_default.boolean().optional()
|
|
1812
1836
|
});
|
|
1813
1837
|
const getProviderConfigById = require_db.zod_default.object({ id: require_db.zod_default.uuidv4() });
|
|
1814
1838
|
const getProviderConfigByProviderId = require_db.zod_default.object({ providerId: require_db.zod_default.string().min(1) });
|
|
1839
|
+
const getProviderConfigBySlug = require_db.zod_default.object({ slug: require_db.zod_default.string().min(1) });
|
|
1815
1840
|
const deleteProviderConfig = require_db.zod_default.object({ id: require_db.zod_default.uuidv4() });
|
|
1816
1841
|
const listProviderConfigs = require_db.zod_default.object({
|
|
1817
1842
|
limit: require_db.zod_default.number().int().positive().optional(),
|
|
@@ -1822,10 +1847,12 @@ const createProviderConfigsDataLayer = (db) => {
|
|
|
1822
1847
|
createProviderConfig: async (params) => {
|
|
1823
1848
|
const value = await createProviderConfig.safeParseAsync(params);
|
|
1824
1849
|
if (!value.success) throw new LLMOpsError(`Invalid parameters: ${value.error.message}`);
|
|
1825
|
-
const { providerId, name, config, enabled } = value.data;
|
|
1850
|
+
const { providerId, slug, name, config, enabled } = value.data;
|
|
1851
|
+
const finalSlug = slug ?? await generateUniqueSlug(db, providerId);
|
|
1826
1852
|
return db.insertInto("provider_configs").values({
|
|
1827
1853
|
id: (0, node_crypto.randomUUID)(),
|
|
1828
1854
|
providerId,
|
|
1855
|
+
slug: finalSlug,
|
|
1829
1856
|
name: name ?? null,
|
|
1830
1857
|
config: JSON.stringify(config),
|
|
1831
1858
|
enabled,
|
|
@@ -1836,8 +1863,9 @@ const createProviderConfigsDataLayer = (db) => {
|
|
|
1836
1863
|
updateProviderConfig: async (params) => {
|
|
1837
1864
|
const value = await updateProviderConfig.safeParseAsync(params);
|
|
1838
1865
|
if (!value.success) throw new LLMOpsError(`Invalid parameters: ${value.error.message}`);
|
|
1839
|
-
const { id, name, config, enabled } = value.data;
|
|
1866
|
+
const { id, slug, name, config, enabled } = value.data;
|
|
1840
1867
|
const updateData = { updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1868
|
+
if (slug !== void 0) updateData.slug = slug;
|
|
1841
1869
|
if (name !== void 0) updateData.name = name;
|
|
1842
1870
|
if (config !== void 0) updateData.config = JSON.stringify(config);
|
|
1843
1871
|
if (enabled !== void 0) updateData.enabled = enabled;
|
|
@@ -1855,6 +1883,12 @@ const createProviderConfigsDataLayer = (db) => {
|
|
|
1855
1883
|
const { providerId } = value.data;
|
|
1856
1884
|
return db.selectFrom("provider_configs").selectAll().where("providerId", "=", providerId).executeTakeFirst();
|
|
1857
1885
|
},
|
|
1886
|
+
getProviderConfigBySlug: async (params) => {
|
|
1887
|
+
const value = await getProviderConfigBySlug.safeParseAsync(params);
|
|
1888
|
+
if (!value.success) throw new LLMOpsError(`Invalid parameters: ${value.error.message}`);
|
|
1889
|
+
const { slug } = value.data;
|
|
1890
|
+
return db.selectFrom("provider_configs").selectAll().where("slug", "=", slug).executeTakeFirst();
|
|
1891
|
+
},
|
|
1858
1892
|
deleteProviderConfig: async (params) => {
|
|
1859
1893
|
const value = await deleteProviderConfig.safeParseAsync(params);
|
|
1860
1894
|
if (!value.success) throw new LLMOpsError(`Invalid parameters: ${value.error.message}`);
|
|
@@ -1874,17 +1908,23 @@ const createProviderConfigsDataLayer = (db) => {
|
|
|
1874
1908
|
upsertProviderConfig: async (params) => {
|
|
1875
1909
|
const value = await createProviderConfig.safeParseAsync(params);
|
|
1876
1910
|
if (!value.success) throw new LLMOpsError(`Invalid parameters: ${value.error.message}`);
|
|
1877
|
-
const { providerId, name, config, enabled } = value.data;
|
|
1911
|
+
const { providerId, slug, name, config, enabled } = value.data;
|
|
1878
1912
|
const existing = await db.selectFrom("provider_configs").selectAll().where("providerId", "=", providerId).executeTakeFirst();
|
|
1879
|
-
if (existing)
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1913
|
+
if (existing) {
|
|
1914
|
+
const finalSlug$1 = slug ?? existing.slug ?? await generateUniqueSlug(db, providerId);
|
|
1915
|
+
return db.updateTable("provider_configs").set({
|
|
1916
|
+
slug: finalSlug$1,
|
|
1917
|
+
name: name ?? existing.name,
|
|
1918
|
+
config: JSON.stringify(config),
|
|
1919
|
+
enabled,
|
|
1920
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1921
|
+
}).where("id", "=", existing.id).returningAll().executeTakeFirst();
|
|
1922
|
+
}
|
|
1923
|
+
const finalSlug = slug ?? await generateUniqueSlug(db, providerId);
|
|
1885
1924
|
return db.insertInto("provider_configs").values({
|
|
1886
1925
|
id: (0, node_crypto.randomUUID)(),
|
|
1887
1926
|
providerId,
|
|
1927
|
+
slug: finalSlug,
|
|
1888
1928
|
name: name ?? null,
|
|
1889
1929
|
config: JSON.stringify(config),
|
|
1890
1930
|
enabled,
|
|
@@ -2580,10 +2620,341 @@ function getDefaultPricingProvider() {
|
|
|
2580
2620
|
return defaultProvider;
|
|
2581
2621
|
}
|
|
2582
2622
|
|
|
2623
|
+
//#endregion
|
|
2624
|
+
//#region src/manifest/builder.ts
|
|
2625
|
+
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2626
|
+
/**
|
|
2627
|
+
* Builds the gateway routing manifest from database
|
|
2628
|
+
*/
|
|
2629
|
+
var ManifestBuilder = class {
|
|
2630
|
+
constructor(db) {
|
|
2631
|
+
this.db = db;
|
|
2632
|
+
}
|
|
2633
|
+
/**
|
|
2634
|
+
* Build the complete routing manifest from database
|
|
2635
|
+
*/
|
|
2636
|
+
async build() {
|
|
2637
|
+
const [configs, environments, environmentSecrets, targetingRules, configVariants, variantVersions, providerConfigs] = await Promise.all([
|
|
2638
|
+
this.db.selectFrom("configs").selectAll().execute(),
|
|
2639
|
+
this.db.selectFrom("environments").selectAll().execute(),
|
|
2640
|
+
this.db.selectFrom("environment_secrets").selectAll().execute(),
|
|
2641
|
+
this.db.selectFrom("targeting_rules").where("enabled", "=", true).selectAll().execute(),
|
|
2642
|
+
this.db.selectFrom("config_variants").selectAll().execute(),
|
|
2643
|
+
this.db.selectFrom("variant_versions").selectAll().execute(),
|
|
2644
|
+
this.db.selectFrom("provider_configs").selectAll().execute()
|
|
2645
|
+
]);
|
|
2646
|
+
const manifestConfigs = {};
|
|
2647
|
+
const configsBySlug = {};
|
|
2648
|
+
for (const config of configs) {
|
|
2649
|
+
manifestConfigs[config.id] = {
|
|
2650
|
+
id: config.id,
|
|
2651
|
+
slug: config.slug,
|
|
2652
|
+
name: config.name ?? null
|
|
2653
|
+
};
|
|
2654
|
+
configsBySlug[config.slug] = config.id;
|
|
2655
|
+
}
|
|
2656
|
+
const manifestEnvironments = {};
|
|
2657
|
+
const environmentsBySlug = {};
|
|
2658
|
+
for (const env of environments) {
|
|
2659
|
+
manifestEnvironments[env.id] = {
|
|
2660
|
+
id: env.id,
|
|
2661
|
+
slug: env.slug,
|
|
2662
|
+
name: env.name,
|
|
2663
|
+
isProd: env.isProd
|
|
2664
|
+
};
|
|
2665
|
+
environmentsBySlug[env.slug] = env.id;
|
|
2666
|
+
}
|
|
2667
|
+
const secretToEnvironment = {};
|
|
2668
|
+
for (const secret of environmentSecrets) secretToEnvironment[secret.keyValue] = secret.environmentId;
|
|
2669
|
+
const configVariantMap = new Map(configVariants.map((cv) => [cv.id, cv]));
|
|
2670
|
+
const providerConfigMap = new Map(providerConfigs.map((pc) => [pc.id, pc]));
|
|
2671
|
+
const versionsByVariant = /* @__PURE__ */ new Map();
|
|
2672
|
+
for (const vv of variantVersions) {
|
|
2673
|
+
const list = versionsByVariant.get(vv.variantId) || [];
|
|
2674
|
+
list.push(vv);
|
|
2675
|
+
versionsByVariant.set(vv.variantId, list);
|
|
2676
|
+
}
|
|
2677
|
+
for (const list of versionsByVariant.values()) list.sort((a, b) => b.version - a.version);
|
|
2678
|
+
const versionById = new Map(variantVersions.map((vv) => [vv.id, vv]));
|
|
2679
|
+
const resolveProvider = (provider) => {
|
|
2680
|
+
if (UUID_REGEX.test(provider)) {
|
|
2681
|
+
const pc = providerConfigMap.get(provider);
|
|
2682
|
+
if (pc) return {
|
|
2683
|
+
providerId: pc.providerId,
|
|
2684
|
+
providerConfigId: pc.id
|
|
2685
|
+
};
|
|
2686
|
+
}
|
|
2687
|
+
return {
|
|
2688
|
+
providerId: provider,
|
|
2689
|
+
providerConfigId: null
|
|
2690
|
+
};
|
|
2691
|
+
};
|
|
2692
|
+
const buildVersion = (vv) => {
|
|
2693
|
+
const { providerId, providerConfigId } = resolveProvider(vv.provider);
|
|
2694
|
+
const jsonData = typeof vv.jsonData === "string" ? JSON.parse(vv.jsonData) : vv.jsonData;
|
|
2695
|
+
return {
|
|
2696
|
+
id: vv.id,
|
|
2697
|
+
variantId: vv.variantId,
|
|
2698
|
+
version: vv.version,
|
|
2699
|
+
provider: providerId,
|
|
2700
|
+
providerConfigId,
|
|
2701
|
+
modelName: vv.modelName,
|
|
2702
|
+
jsonData
|
|
2703
|
+
};
|
|
2704
|
+
};
|
|
2705
|
+
const routingTable = {};
|
|
2706
|
+
for (const rule of targetingRules) {
|
|
2707
|
+
const configVariant = configVariantMap.get(rule.configVariantId);
|
|
2708
|
+
if (!configVariant) continue;
|
|
2709
|
+
const variantId = configVariant.variantId;
|
|
2710
|
+
let resolvedVersionData;
|
|
2711
|
+
if (rule.variantVersionId) resolvedVersionData = versionById.get(rule.variantVersionId);
|
|
2712
|
+
else resolvedVersionData = versionsByVariant.get(variantId)?.[0];
|
|
2713
|
+
if (!resolvedVersionData) continue;
|
|
2714
|
+
let conditions = null;
|
|
2715
|
+
if (rule.conditions) {
|
|
2716
|
+
const conditionsObj = typeof rule.conditions === "string" ? JSON.parse(rule.conditions) : rule.conditions;
|
|
2717
|
+
if (conditionsObj && Object.keys(conditionsObj).length > 0) conditions = conditionsObj;
|
|
2718
|
+
}
|
|
2719
|
+
const manifestRule = {
|
|
2720
|
+
id: rule.id,
|
|
2721
|
+
configVariantId: rule.configVariantId,
|
|
2722
|
+
variantVersionId: rule.variantVersionId,
|
|
2723
|
+
weight: rule.weight,
|
|
2724
|
+
priority: rule.priority,
|
|
2725
|
+
enabled: rule.enabled,
|
|
2726
|
+
conditions,
|
|
2727
|
+
resolvedVersion: buildVersion(resolvedVersionData)
|
|
2728
|
+
};
|
|
2729
|
+
if (!routingTable[rule.configId]) routingTable[rule.configId] = {};
|
|
2730
|
+
if (!routingTable[rule.configId][rule.environmentId]) routingTable[rule.configId][rule.environmentId] = [];
|
|
2731
|
+
routingTable[rule.configId][rule.environmentId].push(manifestRule);
|
|
2732
|
+
}
|
|
2733
|
+
for (const configRules of Object.values(routingTable)) for (const envRules of Object.values(configRules)) envRules.sort((a, b) => {
|
|
2734
|
+
if (b.priority !== a.priority) return b.priority - a.priority;
|
|
2735
|
+
return b.weight - a.weight;
|
|
2736
|
+
});
|
|
2737
|
+
return {
|
|
2738
|
+
version: Date.now(),
|
|
2739
|
+
builtAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2740
|
+
configs: manifestConfigs,
|
|
2741
|
+
configsBySlug,
|
|
2742
|
+
environments: manifestEnvironments,
|
|
2743
|
+
environmentsBySlug,
|
|
2744
|
+
routingTable,
|
|
2745
|
+
secretToEnvironment
|
|
2746
|
+
};
|
|
2747
|
+
}
|
|
2748
|
+
};
|
|
2749
|
+
|
|
2750
|
+
//#endregion
|
|
2751
|
+
//#region src/manifest/service.ts
|
|
2752
|
+
const MANIFEST_CACHE_KEY = "manifest";
|
|
2753
|
+
const MANIFEST_NAMESPACE = "gateway";
|
|
2754
|
+
const DEFAULT_TTL_MS = 300 * 1e3;
|
|
2755
|
+
const log = require_db.logger.child({ module: "ManifestService" });
|
|
2756
|
+
var ManifestService = class {
|
|
2757
|
+
builder;
|
|
2758
|
+
constructor(cache, db, ttlMs = DEFAULT_TTL_MS) {
|
|
2759
|
+
this.cache = cache;
|
|
2760
|
+
this.ttlMs = ttlMs;
|
|
2761
|
+
this.builder = new ManifestBuilder(db);
|
|
2762
|
+
log.debug({ ttlMs }, "ManifestService initialized");
|
|
2763
|
+
}
|
|
2764
|
+
/**
|
|
2765
|
+
* Get the current manifest, building if necessary
|
|
2766
|
+
*/
|
|
2767
|
+
async getManifest() {
|
|
2768
|
+
log.debug("Getting manifest from cache or building");
|
|
2769
|
+
const manifest = await this.cache.getOrSet(MANIFEST_CACHE_KEY, async () => {
|
|
2770
|
+
log.info("Building new manifest");
|
|
2771
|
+
const built = await this.builder.build();
|
|
2772
|
+
log.info({
|
|
2773
|
+
version: built.version,
|
|
2774
|
+
configCount: Object.keys(built.configs).length,
|
|
2775
|
+
environmentCount: Object.keys(built.environments).length
|
|
2776
|
+
}, "Manifest built successfully");
|
|
2777
|
+
return built;
|
|
2778
|
+
}, {
|
|
2779
|
+
namespace: MANIFEST_NAMESPACE,
|
|
2780
|
+
ttl: this.ttlMs
|
|
2781
|
+
});
|
|
2782
|
+
log.debug({ version: manifest.version }, "Manifest retrieved");
|
|
2783
|
+
return manifest;
|
|
2784
|
+
}
|
|
2785
|
+
/**
|
|
2786
|
+
* Force invalidate the manifest (called on mutations)
|
|
2787
|
+
*/
|
|
2788
|
+
async invalidate() {
|
|
2789
|
+
log.info("Invalidating manifest cache");
|
|
2790
|
+
await this.cache.delete(MANIFEST_CACHE_KEY, MANIFEST_NAMESPACE);
|
|
2791
|
+
}
|
|
2792
|
+
/**
|
|
2793
|
+
* Invalidate and immediately rebuild (atomic refresh)
|
|
2794
|
+
*/
|
|
2795
|
+
async refresh() {
|
|
2796
|
+
log.info("Refreshing manifest (invalidate + rebuild)");
|
|
2797
|
+
await this.invalidate();
|
|
2798
|
+
return this.getManifest();
|
|
2799
|
+
}
|
|
2800
|
+
/**
|
|
2801
|
+
* Get manifest version without fetching full manifest
|
|
2802
|
+
* Useful for checking if manifest is stale
|
|
2803
|
+
*/
|
|
2804
|
+
async getVersion() {
|
|
2805
|
+
const version = (await this.cache.get(MANIFEST_CACHE_KEY, MANIFEST_NAMESPACE))?.version ?? null;
|
|
2806
|
+
log.debug({ version }, "Got manifest version");
|
|
2807
|
+
return version;
|
|
2808
|
+
}
|
|
2809
|
+
/**
|
|
2810
|
+
* Check if manifest exists in cache
|
|
2811
|
+
*/
|
|
2812
|
+
async hasManifest() {
|
|
2813
|
+
const exists = await this.cache.has(MANIFEST_CACHE_KEY, MANIFEST_NAMESPACE);
|
|
2814
|
+
log.debug({ exists }, "Checked manifest existence");
|
|
2815
|
+
return exists;
|
|
2816
|
+
}
|
|
2817
|
+
};
|
|
2818
|
+
|
|
2819
|
+
//#endregion
|
|
2820
|
+
//#region src/manifest/router.ts
|
|
2821
|
+
/**
|
|
2822
|
+
* Router for evaluating the gateway manifest and selecting variants
|
|
2823
|
+
*/
|
|
2824
|
+
var ManifestRouter = class {
|
|
2825
|
+
constructor(manifest) {
|
|
2826
|
+
this.manifest = manifest;
|
|
2827
|
+
}
|
|
2828
|
+
/**
|
|
2829
|
+
* Resolve a config identifier (UUID or slug) to config ID
|
|
2830
|
+
*/
|
|
2831
|
+
resolveConfigId(configIdOrSlug) {
|
|
2832
|
+
if (this.manifest.configs[configIdOrSlug]) return configIdOrSlug;
|
|
2833
|
+
return this.manifest.configsBySlug[configIdOrSlug] ?? null;
|
|
2834
|
+
}
|
|
2835
|
+
/**
|
|
2836
|
+
* Resolve environment from secret value
|
|
2837
|
+
*/
|
|
2838
|
+
resolveEnvironmentFromSecret(secretValue) {
|
|
2839
|
+
return this.manifest.secretToEnvironment[secretValue] ?? null;
|
|
2840
|
+
}
|
|
2841
|
+
/**
|
|
2842
|
+
* Get production environment ID
|
|
2843
|
+
*/
|
|
2844
|
+
getProductionEnvironmentId() {
|
|
2845
|
+
for (const env of Object.values(this.manifest.environments)) if (env.isProd) return env.id;
|
|
2846
|
+
return null;
|
|
2847
|
+
}
|
|
2848
|
+
/**
|
|
2849
|
+
* Get environment by ID
|
|
2850
|
+
*/
|
|
2851
|
+
getEnvironment(environmentId) {
|
|
2852
|
+
return this.manifest.environments[environmentId] ?? null;
|
|
2853
|
+
}
|
|
2854
|
+
/**
|
|
2855
|
+
* Get config by ID
|
|
2856
|
+
*/
|
|
2857
|
+
getConfig(configId) {
|
|
2858
|
+
return this.manifest.configs[configId] ?? null;
|
|
2859
|
+
}
|
|
2860
|
+
/**
|
|
2861
|
+
* Route a request to the appropriate variant version (first match wins)
|
|
2862
|
+
*/
|
|
2863
|
+
route(configIdOrSlug, environmentId, context = {}) {
|
|
2864
|
+
const configId = this.resolveConfigId(configIdOrSlug);
|
|
2865
|
+
if (!configId) return null;
|
|
2866
|
+
const rules = this.manifest.routingTable[configId]?.[environmentId];
|
|
2867
|
+
if (!rules || rules.length === 0) return null;
|
|
2868
|
+
const evalContext = {
|
|
2869
|
+
...context,
|
|
2870
|
+
timestamp: context.timestamp ?? Date.now()
|
|
2871
|
+
};
|
|
2872
|
+
for (const rule of rules) {
|
|
2873
|
+
if (!rule.enabled) continue;
|
|
2874
|
+
if (rule.conditions) try {
|
|
2875
|
+
if (!json_logic_js.default.apply(rule.conditions, evalContext)) continue;
|
|
2876
|
+
} catch (error) {
|
|
2877
|
+
console.warn(`JSONLogic evaluation error for rule ${rule.id}:`, error);
|
|
2878
|
+
continue;
|
|
2879
|
+
}
|
|
2880
|
+
return {
|
|
2881
|
+
configId,
|
|
2882
|
+
environmentId,
|
|
2883
|
+
variantId: rule.resolvedVersion.variantId,
|
|
2884
|
+
version: rule.resolvedVersion,
|
|
2885
|
+
rule
|
|
2886
|
+
};
|
|
2887
|
+
}
|
|
2888
|
+
return null;
|
|
2889
|
+
}
|
|
2890
|
+
/**
|
|
2891
|
+
* Route with weighted random selection among matching rules of same priority
|
|
2892
|
+
*/
|
|
2893
|
+
routeWithWeights(configIdOrSlug, environmentId, context = {}) {
|
|
2894
|
+
const configId = this.resolveConfigId(configIdOrSlug);
|
|
2895
|
+
if (!configId) return null;
|
|
2896
|
+
const rules = this.manifest.routingTable[configId]?.[environmentId];
|
|
2897
|
+
if (!rules || rules.length === 0) return null;
|
|
2898
|
+
const evalContext = {
|
|
2899
|
+
...context,
|
|
2900
|
+
timestamp: context.timestamp ?? Date.now()
|
|
2901
|
+
};
|
|
2902
|
+
const rulesByPriority = /* @__PURE__ */ new Map();
|
|
2903
|
+
for (const rule$1 of rules) {
|
|
2904
|
+
if (!rule$1.enabled) continue;
|
|
2905
|
+
if (rule$1.conditions) try {
|
|
2906
|
+
if (!json_logic_js.default.apply(rule$1.conditions, evalContext)) continue;
|
|
2907
|
+
} catch {
|
|
2908
|
+
continue;
|
|
2909
|
+
}
|
|
2910
|
+
const list = rulesByPriority.get(rule$1.priority) || [];
|
|
2911
|
+
list.push(rule$1);
|
|
2912
|
+
rulesByPriority.set(rule$1.priority, list);
|
|
2913
|
+
}
|
|
2914
|
+
const priorities = Array.from(rulesByPriority.keys()).sort((a, b) => b - a);
|
|
2915
|
+
if (priorities.length === 0) return null;
|
|
2916
|
+
const topPriorityRules = rulesByPriority.get(priorities[0]);
|
|
2917
|
+
if (topPriorityRules.length === 1) {
|
|
2918
|
+
const rule$1 = topPriorityRules[0];
|
|
2919
|
+
return {
|
|
2920
|
+
configId,
|
|
2921
|
+
environmentId,
|
|
2922
|
+
variantId: rule$1.resolvedVersion.variantId,
|
|
2923
|
+
version: rule$1.resolvedVersion,
|
|
2924
|
+
rule: rule$1
|
|
2925
|
+
};
|
|
2926
|
+
}
|
|
2927
|
+
const totalWeight = topPriorityRules.reduce((sum, r) => sum + r.weight, 0);
|
|
2928
|
+
if (totalWeight === 0) return null;
|
|
2929
|
+
let random = Math.random() * totalWeight;
|
|
2930
|
+
for (const rule$1 of topPriorityRules) {
|
|
2931
|
+
random -= rule$1.weight;
|
|
2932
|
+
if (random <= 0) return {
|
|
2933
|
+
configId,
|
|
2934
|
+
environmentId,
|
|
2935
|
+
variantId: rule$1.resolvedVersion.variantId,
|
|
2936
|
+
version: rule$1.resolvedVersion,
|
|
2937
|
+
rule: rule$1
|
|
2938
|
+
};
|
|
2939
|
+
}
|
|
2940
|
+
const rule = topPriorityRules[0];
|
|
2941
|
+
return {
|
|
2942
|
+
configId,
|
|
2943
|
+
environmentId,
|
|
2944
|
+
variantId: rule.resolvedVersion.variantId,
|
|
2945
|
+
version: rule.resolvedVersion,
|
|
2946
|
+
rule
|
|
2947
|
+
};
|
|
2948
|
+
}
|
|
2949
|
+
};
|
|
2950
|
+
|
|
2583
2951
|
//#endregion
|
|
2584
2952
|
exports.CacheService = CacheService;
|
|
2585
2953
|
exports.FileCacheBackend = FileCacheBackend;
|
|
2586
2954
|
exports.MS = MS;
|
|
2955
|
+
exports.ManifestBuilder = ManifestBuilder;
|
|
2956
|
+
exports.ManifestRouter = ManifestRouter;
|
|
2957
|
+
exports.ManifestService = ManifestService;
|
|
2587
2958
|
exports.MemoryCacheBackend = MemoryCacheBackend;
|
|
2588
2959
|
exports.ModelsDevPricingProvider = ModelsDevPricingProvider;
|
|
2589
2960
|
exports.SCHEMA_METADATA = require_db.SCHEMA_METADATA;
|