@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/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { A as literal, C as variantsSchema, D as any, E as _enum, F as union, I as unknown, M as object, N as record, O as array, P as string, S as variantVersionsSchema, T as zod_default, _ as environmentsSchema, a as matchType, b as schemas, c as logger, d as validatePartialTableData, f as validateTableData, g as environmentSecretsSchema, h as configsSchema, i as getMigrations, j as number, k as boolean, l as parsePartialTableData, m as configVariantsSchema, n as createDatabaseFromConnection, o as runAutoMigrations, p as SCHEMA_METADATA, r as detectDatabaseType, s as getAuthClientOptions, t as createDatabase, u as parseTableData, v as llmRequestsSchema, w as workspaceSettingsSchema, x as targetingRulesSchema, y as providerConfigsSchema } from "./db-DohlAqJU.mjs";
1
+ import { A as literal, C as variantsSchema, D as any, E as _enum, F as union, I as unknown, M as object, N as record, O as array, P as string, S as variantVersionsSchema, T as zod_default, _ as environmentsSchema, a as matchType, b as schemas, c as logger, d as validatePartialTableData, f as validateTableData, g as environmentSecretsSchema, h as configsSchema, i as getMigrations, j as number, k as boolean, l as parsePartialTableData, m as configVariantsSchema, n as createDatabaseFromConnection, o as runAutoMigrations, p as SCHEMA_METADATA, r as detectDatabaseType, s as getAuthClientOptions, t as createDatabase, u as parseTableData, v as llmRequestsSchema, w as workspaceSettingsSchema, x as targetingRulesSchema, y as providerConfigsSchema } from "./db-B-EsQtOz.mjs";
2
2
  import { n as executeWithSchema, t as createNeonDialect } from "./neon-dialect-DySGBYUi.mjs";
3
3
  import gateway from "@llmops/gateway";
4
4
  import { sql } from "kysely";
@@ -6,6 +6,7 @@ import * as fs from "node:fs/promises";
6
6
  import * as path from "node:path";
7
7
  import { createRandomStringGenerator } from "@better-auth/utils/random";
8
8
  import { randomBytes, randomUUID } from "node:crypto";
9
+ import jsonLogic from "json-logic-js";
9
10
 
10
11
  //#region src/providers/supported-providers.ts
11
12
  /**
@@ -837,7 +838,10 @@ var CacheService = class {
837
838
  }
838
839
  createBackend(config) {
839
840
  switch (config.backend) {
840
- case "memory": return new MemoryCacheBackend(config.maxSize, config.cleanupInterval);
841
+ case "memory": return new MemoryCacheBackend({
842
+ maxSize: config.maxSize,
843
+ cleanupIntervalMs: config.cleanupInterval
844
+ });
841
845
  case "file": return new FileCacheBackend(config.dataDir, config.fileName, config.saveInterval, config.cleanupInterval);
842
846
  default: throw new Error(`Unsupported cache backend: ${config.backend}`);
843
847
  }
@@ -1226,9 +1230,9 @@ const createConfigVariantDataLayer = (db) => {
1226
1230
  const value = await getVariantJsonDataForConfig.safeParseAsync(params);
1227
1231
  if (!value.success) throw new LLMOpsError(`Invalid parameters: ${value.error.message}`);
1228
1232
  const { configId: configIdOrSlug, envSecret } = value.data;
1229
- const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1233
+ 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;
1230
1234
  let resolvedConfigId;
1231
- if (UUID_REGEX.test(configIdOrSlug)) resolvedConfigId = configIdOrSlug;
1235
+ if (UUID_REGEX$1.test(configIdOrSlug)) resolvedConfigId = configIdOrSlug;
1232
1236
  else {
1233
1237
  const config = await db.selectFrom("configs").select("id").where("slug", "=", configIdOrSlug).executeTakeFirst();
1234
1238
  if (!config) throw new LLMOpsError(`Config not found: ${configIdOrSlug}`);
@@ -1264,7 +1268,7 @@ const createConfigVariantDataLayer = (db) => {
1264
1268
  if (!versionData) throw new LLMOpsError(`No variant version found for variant ${configVariant.variantId}`);
1265
1269
  let finalProvider = versionData.provider;
1266
1270
  let providerConfigId = null;
1267
- if (UUID_REGEX.test(versionData.provider)) {
1271
+ if (UUID_REGEX$1.test(versionData.provider)) {
1268
1272
  const providerConfig = await db.selectFrom("provider_configs").select(["id", "providerId"]).where("id", "=", versionData.provider).executeTakeFirst();
1269
1273
  if (providerConfig) {
1270
1274
  finalProvider = providerConfig.providerId;
@@ -1795,20 +1799,40 @@ const createLLMRequestsDataLayer = (db) => {
1795
1799
 
1796
1800
  //#endregion
1797
1801
  //#region src/datalayer/providerConfigs.ts
1802
+ /**
1803
+ * Generate a unique slug for a provider config.
1804
+ * If the base slug already exists, appends -01, -02, etc.
1805
+ */
1806
+ async function generateUniqueSlug(db, baseSlug) {
1807
+ const existing = await db.selectFrom("provider_configs").select("slug").where("slug", "like", `${baseSlug}%`).execute();
1808
+ if (existing.length === 0) return baseSlug;
1809
+ const existingSlugs = new Set(existing.map((e) => e.slug));
1810
+ if (!existingSlugs.has(baseSlug)) return baseSlug;
1811
+ let counter = 1;
1812
+ while (counter < 100) {
1813
+ const candidateSlug = `${baseSlug}-${counter.toString().padStart(2, "0")}`;
1814
+ if (!existingSlugs.has(candidateSlug)) return candidateSlug;
1815
+ counter++;
1816
+ }
1817
+ return `${baseSlug}-${randomUUID().slice(0, 8)}`;
1818
+ }
1798
1819
  const createProviderConfig = zod_default.object({
1799
1820
  providerId: zod_default.string().min(1),
1821
+ slug: zod_default.string().nullable().optional(),
1800
1822
  name: zod_default.string().nullable().optional(),
1801
1823
  config: zod_default.record(zod_default.string(), zod_default.unknown()),
1802
1824
  enabled: zod_default.boolean().optional().default(true)
1803
1825
  });
1804
1826
  const updateProviderConfig = zod_default.object({
1805
1827
  id: zod_default.uuidv4(),
1828
+ slug: zod_default.string().nullable().optional(),
1806
1829
  name: zod_default.string().nullable().optional(),
1807
1830
  config: zod_default.record(zod_default.string(), zod_default.unknown()).optional(),
1808
1831
  enabled: zod_default.boolean().optional()
1809
1832
  });
1810
1833
  const getProviderConfigById = zod_default.object({ id: zod_default.uuidv4() });
1811
1834
  const getProviderConfigByProviderId = zod_default.object({ providerId: zod_default.string().min(1) });
1835
+ const getProviderConfigBySlug = zod_default.object({ slug: zod_default.string().min(1) });
1812
1836
  const deleteProviderConfig = zod_default.object({ id: zod_default.uuidv4() });
1813
1837
  const listProviderConfigs = zod_default.object({
1814
1838
  limit: zod_default.number().int().positive().optional(),
@@ -1819,10 +1843,12 @@ const createProviderConfigsDataLayer = (db) => {
1819
1843
  createProviderConfig: async (params) => {
1820
1844
  const value = await createProviderConfig.safeParseAsync(params);
1821
1845
  if (!value.success) throw new LLMOpsError(`Invalid parameters: ${value.error.message}`);
1822
- const { providerId, name, config, enabled } = value.data;
1846
+ const { providerId, slug, name, config, enabled } = value.data;
1847
+ const finalSlug = slug ?? await generateUniqueSlug(db, providerId);
1823
1848
  return db.insertInto("provider_configs").values({
1824
1849
  id: randomUUID(),
1825
1850
  providerId,
1851
+ slug: finalSlug,
1826
1852
  name: name ?? null,
1827
1853
  config: JSON.stringify(config),
1828
1854
  enabled,
@@ -1833,8 +1859,9 @@ const createProviderConfigsDataLayer = (db) => {
1833
1859
  updateProviderConfig: async (params) => {
1834
1860
  const value = await updateProviderConfig.safeParseAsync(params);
1835
1861
  if (!value.success) throw new LLMOpsError(`Invalid parameters: ${value.error.message}`);
1836
- const { id, name, config, enabled } = value.data;
1862
+ const { id, slug, name, config, enabled } = value.data;
1837
1863
  const updateData = { updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
1864
+ if (slug !== void 0) updateData.slug = slug;
1838
1865
  if (name !== void 0) updateData.name = name;
1839
1866
  if (config !== void 0) updateData.config = JSON.stringify(config);
1840
1867
  if (enabled !== void 0) updateData.enabled = enabled;
@@ -1852,6 +1879,12 @@ const createProviderConfigsDataLayer = (db) => {
1852
1879
  const { providerId } = value.data;
1853
1880
  return db.selectFrom("provider_configs").selectAll().where("providerId", "=", providerId).executeTakeFirst();
1854
1881
  },
1882
+ getProviderConfigBySlug: async (params) => {
1883
+ const value = await getProviderConfigBySlug.safeParseAsync(params);
1884
+ if (!value.success) throw new LLMOpsError(`Invalid parameters: ${value.error.message}`);
1885
+ const { slug } = value.data;
1886
+ return db.selectFrom("provider_configs").selectAll().where("slug", "=", slug).executeTakeFirst();
1887
+ },
1855
1888
  deleteProviderConfig: async (params) => {
1856
1889
  const value = await deleteProviderConfig.safeParseAsync(params);
1857
1890
  if (!value.success) throw new LLMOpsError(`Invalid parameters: ${value.error.message}`);
@@ -1871,17 +1904,23 @@ const createProviderConfigsDataLayer = (db) => {
1871
1904
  upsertProviderConfig: async (params) => {
1872
1905
  const value = await createProviderConfig.safeParseAsync(params);
1873
1906
  if (!value.success) throw new LLMOpsError(`Invalid parameters: ${value.error.message}`);
1874
- const { providerId, name, config, enabled } = value.data;
1907
+ const { providerId, slug, name, config, enabled } = value.data;
1875
1908
  const existing = await db.selectFrom("provider_configs").selectAll().where("providerId", "=", providerId).executeTakeFirst();
1876
- if (existing) return db.updateTable("provider_configs").set({
1877
- name: name ?? existing.name,
1878
- config: JSON.stringify(config),
1879
- enabled,
1880
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1881
- }).where("id", "=", existing.id).returningAll().executeTakeFirst();
1909
+ if (existing) {
1910
+ const finalSlug$1 = slug ?? existing.slug ?? await generateUniqueSlug(db, providerId);
1911
+ return db.updateTable("provider_configs").set({
1912
+ slug: finalSlug$1,
1913
+ name: name ?? existing.name,
1914
+ config: JSON.stringify(config),
1915
+ enabled,
1916
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1917
+ }).where("id", "=", existing.id).returningAll().executeTakeFirst();
1918
+ }
1919
+ const finalSlug = slug ?? await generateUniqueSlug(db, providerId);
1882
1920
  return db.insertInto("provider_configs").values({
1883
1921
  id: randomUUID(),
1884
1922
  providerId,
1923
+ slug: finalSlug,
1885
1924
  name: name ?? null,
1886
1925
  config: JSON.stringify(config),
1887
1926
  enabled,
@@ -2578,4 +2617,332 @@ function getDefaultPricingProvider() {
2578
2617
  }
2579
2618
 
2580
2619
  //#endregion
2581
- export { CacheService, FileCacheBackend, MS, MemoryCacheBackend, ModelsDevPricingProvider, SCHEMA_METADATA, SupportedProviders, calculateCost, chatCompletionCreateParamsBaseSchema, configVariantsSchema, configsSchema, createDataLayer, createDatabase, createDatabaseFromConnection, createLLMRequestsDataLayer, createNeonDialect, createProviderConfigsDataLayer, createWorkspaceSettingsDataLayer, detectDatabaseType, dollarsToMicroDollars, environmentSecretsSchema, environmentsSchema, executeWithSchema, formatCost, gateway, generateId, getAuthClientOptions, getDefaultPricingProvider, getMigrations, llmRequestsSchema, llmopsConfigSchema, logger, matchType, microDollarsToDollars, parsePartialTableData, parseTableData, providerConfigsSchema, runAutoMigrations, schemas, targetingRulesSchema, validateLLMOpsConfig, validatePartialTableData, validateTableData, variantJsonDataSchema, variantVersionsSchema, variantsSchema, workspaceSettingsSchema };
2620
+ //#region src/manifest/builder.ts
2621
+ const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2622
+ /**
2623
+ * Builds the gateway routing manifest from database
2624
+ */
2625
+ var ManifestBuilder = class {
2626
+ constructor(db) {
2627
+ this.db = db;
2628
+ }
2629
+ /**
2630
+ * Build the complete routing manifest from database
2631
+ */
2632
+ async build() {
2633
+ const [configs, environments, environmentSecrets, targetingRules, configVariants, variantVersions, providerConfigs] = await Promise.all([
2634
+ this.db.selectFrom("configs").selectAll().execute(),
2635
+ this.db.selectFrom("environments").selectAll().execute(),
2636
+ this.db.selectFrom("environment_secrets").selectAll().execute(),
2637
+ this.db.selectFrom("targeting_rules").where("enabled", "=", true).selectAll().execute(),
2638
+ this.db.selectFrom("config_variants").selectAll().execute(),
2639
+ this.db.selectFrom("variant_versions").selectAll().execute(),
2640
+ this.db.selectFrom("provider_configs").selectAll().execute()
2641
+ ]);
2642
+ const manifestConfigs = {};
2643
+ const configsBySlug = {};
2644
+ for (const config of configs) {
2645
+ manifestConfigs[config.id] = {
2646
+ id: config.id,
2647
+ slug: config.slug,
2648
+ name: config.name ?? null
2649
+ };
2650
+ configsBySlug[config.slug] = config.id;
2651
+ }
2652
+ const manifestEnvironments = {};
2653
+ const environmentsBySlug = {};
2654
+ for (const env of environments) {
2655
+ manifestEnvironments[env.id] = {
2656
+ id: env.id,
2657
+ slug: env.slug,
2658
+ name: env.name,
2659
+ isProd: env.isProd
2660
+ };
2661
+ environmentsBySlug[env.slug] = env.id;
2662
+ }
2663
+ const secretToEnvironment = {};
2664
+ for (const secret of environmentSecrets) secretToEnvironment[secret.keyValue] = secret.environmentId;
2665
+ const configVariantMap = new Map(configVariants.map((cv) => [cv.id, cv]));
2666
+ const providerConfigMap = new Map(providerConfigs.map((pc) => [pc.id, pc]));
2667
+ const versionsByVariant = /* @__PURE__ */ new Map();
2668
+ for (const vv of variantVersions) {
2669
+ const list = versionsByVariant.get(vv.variantId) || [];
2670
+ list.push(vv);
2671
+ versionsByVariant.set(vv.variantId, list);
2672
+ }
2673
+ for (const list of versionsByVariant.values()) list.sort((a, b) => b.version - a.version);
2674
+ const versionById = new Map(variantVersions.map((vv) => [vv.id, vv]));
2675
+ const resolveProvider = (provider) => {
2676
+ if (UUID_REGEX.test(provider)) {
2677
+ const pc = providerConfigMap.get(provider);
2678
+ if (pc) return {
2679
+ providerId: pc.providerId,
2680
+ providerConfigId: pc.id
2681
+ };
2682
+ }
2683
+ return {
2684
+ providerId: provider,
2685
+ providerConfigId: null
2686
+ };
2687
+ };
2688
+ const buildVersion = (vv) => {
2689
+ const { providerId, providerConfigId } = resolveProvider(vv.provider);
2690
+ const jsonData = typeof vv.jsonData === "string" ? JSON.parse(vv.jsonData) : vv.jsonData;
2691
+ return {
2692
+ id: vv.id,
2693
+ variantId: vv.variantId,
2694
+ version: vv.version,
2695
+ provider: providerId,
2696
+ providerConfigId,
2697
+ modelName: vv.modelName,
2698
+ jsonData
2699
+ };
2700
+ };
2701
+ const routingTable = {};
2702
+ for (const rule of targetingRules) {
2703
+ const configVariant = configVariantMap.get(rule.configVariantId);
2704
+ if (!configVariant) continue;
2705
+ const variantId = configVariant.variantId;
2706
+ let resolvedVersionData;
2707
+ if (rule.variantVersionId) resolvedVersionData = versionById.get(rule.variantVersionId);
2708
+ else resolvedVersionData = versionsByVariant.get(variantId)?.[0];
2709
+ if (!resolvedVersionData) continue;
2710
+ let conditions = null;
2711
+ if (rule.conditions) {
2712
+ const conditionsObj = typeof rule.conditions === "string" ? JSON.parse(rule.conditions) : rule.conditions;
2713
+ if (conditionsObj && Object.keys(conditionsObj).length > 0) conditions = conditionsObj;
2714
+ }
2715
+ const manifestRule = {
2716
+ id: rule.id,
2717
+ configVariantId: rule.configVariantId,
2718
+ variantVersionId: rule.variantVersionId,
2719
+ weight: rule.weight,
2720
+ priority: rule.priority,
2721
+ enabled: rule.enabled,
2722
+ conditions,
2723
+ resolvedVersion: buildVersion(resolvedVersionData)
2724
+ };
2725
+ if (!routingTable[rule.configId]) routingTable[rule.configId] = {};
2726
+ if (!routingTable[rule.configId][rule.environmentId]) routingTable[rule.configId][rule.environmentId] = [];
2727
+ routingTable[rule.configId][rule.environmentId].push(manifestRule);
2728
+ }
2729
+ for (const configRules of Object.values(routingTable)) for (const envRules of Object.values(configRules)) envRules.sort((a, b) => {
2730
+ if (b.priority !== a.priority) return b.priority - a.priority;
2731
+ return b.weight - a.weight;
2732
+ });
2733
+ return {
2734
+ version: Date.now(),
2735
+ builtAt: (/* @__PURE__ */ new Date()).toISOString(),
2736
+ configs: manifestConfigs,
2737
+ configsBySlug,
2738
+ environments: manifestEnvironments,
2739
+ environmentsBySlug,
2740
+ routingTable,
2741
+ secretToEnvironment
2742
+ };
2743
+ }
2744
+ };
2745
+
2746
+ //#endregion
2747
+ //#region src/manifest/service.ts
2748
+ const MANIFEST_CACHE_KEY = "manifest";
2749
+ const MANIFEST_NAMESPACE = "gateway";
2750
+ const DEFAULT_TTL_MS = 300 * 1e3;
2751
+ const log = logger.child({ module: "ManifestService" });
2752
+ var ManifestService = class {
2753
+ builder;
2754
+ constructor(cache, db, ttlMs = DEFAULT_TTL_MS) {
2755
+ this.cache = cache;
2756
+ this.ttlMs = ttlMs;
2757
+ this.builder = new ManifestBuilder(db);
2758
+ log.debug({ ttlMs }, "ManifestService initialized");
2759
+ }
2760
+ /**
2761
+ * Get the current manifest, building if necessary
2762
+ */
2763
+ async getManifest() {
2764
+ log.debug("Getting manifest from cache or building");
2765
+ const manifest = await this.cache.getOrSet(MANIFEST_CACHE_KEY, async () => {
2766
+ log.info("Building new manifest");
2767
+ const built = await this.builder.build();
2768
+ log.info({
2769
+ version: built.version,
2770
+ configCount: Object.keys(built.configs).length,
2771
+ environmentCount: Object.keys(built.environments).length
2772
+ }, "Manifest built successfully");
2773
+ return built;
2774
+ }, {
2775
+ namespace: MANIFEST_NAMESPACE,
2776
+ ttl: this.ttlMs
2777
+ });
2778
+ log.debug({ version: manifest.version }, "Manifest retrieved");
2779
+ return manifest;
2780
+ }
2781
+ /**
2782
+ * Force invalidate the manifest (called on mutations)
2783
+ */
2784
+ async invalidate() {
2785
+ log.info("Invalidating manifest cache");
2786
+ await this.cache.delete(MANIFEST_CACHE_KEY, MANIFEST_NAMESPACE);
2787
+ }
2788
+ /**
2789
+ * Invalidate and immediately rebuild (atomic refresh)
2790
+ */
2791
+ async refresh() {
2792
+ log.info("Refreshing manifest (invalidate + rebuild)");
2793
+ await this.invalidate();
2794
+ return this.getManifest();
2795
+ }
2796
+ /**
2797
+ * Get manifest version without fetching full manifest
2798
+ * Useful for checking if manifest is stale
2799
+ */
2800
+ async getVersion() {
2801
+ const version = (await this.cache.get(MANIFEST_CACHE_KEY, MANIFEST_NAMESPACE))?.version ?? null;
2802
+ log.debug({ version }, "Got manifest version");
2803
+ return version;
2804
+ }
2805
+ /**
2806
+ * Check if manifest exists in cache
2807
+ */
2808
+ async hasManifest() {
2809
+ const exists = await this.cache.has(MANIFEST_CACHE_KEY, MANIFEST_NAMESPACE);
2810
+ log.debug({ exists }, "Checked manifest existence");
2811
+ return exists;
2812
+ }
2813
+ };
2814
+
2815
+ //#endregion
2816
+ //#region src/manifest/router.ts
2817
+ /**
2818
+ * Router for evaluating the gateway manifest and selecting variants
2819
+ */
2820
+ var ManifestRouter = class {
2821
+ constructor(manifest) {
2822
+ this.manifest = manifest;
2823
+ }
2824
+ /**
2825
+ * Resolve a config identifier (UUID or slug) to config ID
2826
+ */
2827
+ resolveConfigId(configIdOrSlug) {
2828
+ if (this.manifest.configs[configIdOrSlug]) return configIdOrSlug;
2829
+ return this.manifest.configsBySlug[configIdOrSlug] ?? null;
2830
+ }
2831
+ /**
2832
+ * Resolve environment from secret value
2833
+ */
2834
+ resolveEnvironmentFromSecret(secretValue) {
2835
+ return this.manifest.secretToEnvironment[secretValue] ?? null;
2836
+ }
2837
+ /**
2838
+ * Get production environment ID
2839
+ */
2840
+ getProductionEnvironmentId() {
2841
+ for (const env of Object.values(this.manifest.environments)) if (env.isProd) return env.id;
2842
+ return null;
2843
+ }
2844
+ /**
2845
+ * Get environment by ID
2846
+ */
2847
+ getEnvironment(environmentId) {
2848
+ return this.manifest.environments[environmentId] ?? null;
2849
+ }
2850
+ /**
2851
+ * Get config by ID
2852
+ */
2853
+ getConfig(configId) {
2854
+ return this.manifest.configs[configId] ?? null;
2855
+ }
2856
+ /**
2857
+ * Route a request to the appropriate variant version (first match wins)
2858
+ */
2859
+ route(configIdOrSlug, environmentId, context = {}) {
2860
+ const configId = this.resolveConfigId(configIdOrSlug);
2861
+ if (!configId) return null;
2862
+ const rules = this.manifest.routingTable[configId]?.[environmentId];
2863
+ if (!rules || rules.length === 0) return null;
2864
+ const evalContext = {
2865
+ ...context,
2866
+ timestamp: context.timestamp ?? Date.now()
2867
+ };
2868
+ for (const rule of rules) {
2869
+ if (!rule.enabled) continue;
2870
+ if (rule.conditions) try {
2871
+ if (!jsonLogic.apply(rule.conditions, evalContext)) continue;
2872
+ } catch (error) {
2873
+ console.warn(`JSONLogic evaluation error for rule ${rule.id}:`, error);
2874
+ continue;
2875
+ }
2876
+ return {
2877
+ configId,
2878
+ environmentId,
2879
+ variantId: rule.resolvedVersion.variantId,
2880
+ version: rule.resolvedVersion,
2881
+ rule
2882
+ };
2883
+ }
2884
+ return null;
2885
+ }
2886
+ /**
2887
+ * Route with weighted random selection among matching rules of same priority
2888
+ */
2889
+ routeWithWeights(configIdOrSlug, environmentId, context = {}) {
2890
+ const configId = this.resolveConfigId(configIdOrSlug);
2891
+ if (!configId) return null;
2892
+ const rules = this.manifest.routingTable[configId]?.[environmentId];
2893
+ if (!rules || rules.length === 0) return null;
2894
+ const evalContext = {
2895
+ ...context,
2896
+ timestamp: context.timestamp ?? Date.now()
2897
+ };
2898
+ const rulesByPriority = /* @__PURE__ */ new Map();
2899
+ for (const rule$1 of rules) {
2900
+ if (!rule$1.enabled) continue;
2901
+ if (rule$1.conditions) try {
2902
+ if (!jsonLogic.apply(rule$1.conditions, evalContext)) continue;
2903
+ } catch {
2904
+ continue;
2905
+ }
2906
+ const list = rulesByPriority.get(rule$1.priority) || [];
2907
+ list.push(rule$1);
2908
+ rulesByPriority.set(rule$1.priority, list);
2909
+ }
2910
+ const priorities = Array.from(rulesByPriority.keys()).sort((a, b) => b - a);
2911
+ if (priorities.length === 0) return null;
2912
+ const topPriorityRules = rulesByPriority.get(priorities[0]);
2913
+ if (topPriorityRules.length === 1) {
2914
+ const rule$1 = topPriorityRules[0];
2915
+ return {
2916
+ configId,
2917
+ environmentId,
2918
+ variantId: rule$1.resolvedVersion.variantId,
2919
+ version: rule$1.resolvedVersion,
2920
+ rule: rule$1
2921
+ };
2922
+ }
2923
+ const totalWeight = topPriorityRules.reduce((sum, r) => sum + r.weight, 0);
2924
+ if (totalWeight === 0) return null;
2925
+ let random = Math.random() * totalWeight;
2926
+ for (const rule$1 of topPriorityRules) {
2927
+ random -= rule$1.weight;
2928
+ if (random <= 0) return {
2929
+ configId,
2930
+ environmentId,
2931
+ variantId: rule$1.resolvedVersion.variantId,
2932
+ version: rule$1.resolvedVersion,
2933
+ rule: rule$1
2934
+ };
2935
+ }
2936
+ const rule = topPriorityRules[0];
2937
+ return {
2938
+ configId,
2939
+ environmentId,
2940
+ variantId: rule.resolvedVersion.variantId,
2941
+ version: rule.resolvedVersion,
2942
+ rule
2943
+ };
2944
+ }
2945
+ };
2946
+
2947
+ //#endregion
2948
+ export { CacheService, FileCacheBackend, MS, ManifestBuilder, ManifestRouter, ManifestService, MemoryCacheBackend, ModelsDevPricingProvider, SCHEMA_METADATA, SupportedProviders, calculateCost, chatCompletionCreateParamsBaseSchema, configVariantsSchema, configsSchema, createDataLayer, createDatabase, createDatabaseFromConnection, createLLMRequestsDataLayer, createNeonDialect, createProviderConfigsDataLayer, createWorkspaceSettingsDataLayer, detectDatabaseType, dollarsToMicroDollars, environmentSecretsSchema, environmentsSchema, executeWithSchema, formatCost, gateway, generateId, getAuthClientOptions, getDefaultPricingProvider, getMigrations, llmRequestsSchema, llmopsConfigSchema, logger, matchType, microDollarsToDollars, parsePartialTableData, parseTableData, providerConfigsSchema, runAutoMigrations, schemas, targetingRulesSchema, validateLLMOpsConfig, validatePartialTableData, validateTableData, variantJsonDataSchema, variantVersionsSchema, variantsSchema, workspaceSettingsSchema };
@@ -1,3 +1,3 @@
1
- const require_neon_dialect = require('./neon-dialect-BQey5lUw.cjs');
1
+ const require_neon_dialect = require('./neon-dialect-BR1nZmKX.cjs');
2
2
 
3
3
  exports.createNeonDialect = require_neon_dialect.createNeonDialect;
@@ -1,4 +1,4 @@
1
- const require_db = require('./db-BOe6mM51.cjs');
1
+ const require_db = require('./db-Du2xmkGS.cjs');
2
2
  let kysely_neon = require("kysely-neon");
3
3
  require("@neondatabase/serverless");
4
4
 
@@ -1,4 +1,4 @@
1
- const require_db = require('./db-BOe6mM51.cjs');
1
+ const require_db = require('./db-Du2xmkGS.cjs');
2
2
  let kysely = require("kysely");
3
3
 
4
4
  //#region src/db/node-sqlite-dialect.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llmops/core",
3
- "version": "0.2.13",
3
+ "version": "0.3.0-beta.1",
4
4
  "description": "Core LLMOps functionality and utilities",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -51,12 +51,14 @@
51
51
  "@neondatabase/serverless": "^1.0.2",
52
52
  "better-auth": "^1.4.10",
53
53
  "hono": "^4.10.7",
54
+ "json-logic-js": "^2.0.5",
54
55
  "kysely": "^0.28.8",
55
56
  "kysely-neon": "^2.0.2",
56
57
  "pino": "^10.1.0",
57
- "@llmops/gateway": "^0.2.13"
58
+ "@llmops/gateway": "^0.3.0-beta.1"
58
59
  },
59
60
  "devDependencies": {
61
+ "@types/json-logic-js": "^2.0.8",
60
62
  "@types/ws": "^8.18.1"
61
63
  },
62
64
  "scripts": {