avantgate 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -20,64 +20,236 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ AvantGateBudgetExceededError: () => BudgetExceededError,
24
+ AvantGateConfigurationError: () => ConfigurationError,
23
25
  AvantGateControlLayer: () => AvantGateControlLayer,
26
+ BudgetExceededError: () => BudgetExceededError,
27
+ CachedPricingAdapter: () => CachedPricingAdapter,
28
+ ConfigurationError: () => ConfigurationError,
24
29
  DEFAULT_MODEL_PRICES: () => DEFAULT_MODEL_PRICES,
30
+ HttpProviderClient: () => HttpProviderClient,
31
+ PricingRegistry: () => PricingRegistry,
25
32
  PromptBuilder: () => PromptBuilder,
26
33
  PromptRegistry: () => PromptRegistry,
27
34
  PromptTemplate: () => PromptTemplate,
35
+ SEED_MODEL_PRICES: () => SEED_MODEL_PRICES,
28
36
  ZenLLMControlLayer: () => AvantGateControlLayer,
29
37
  calculateCostUSD: () => calculateCostUSD,
30
38
  createAvantGate: () => createAvantGate,
39
+ createHttpProviderClient: () => createHttpProviderClient,
31
40
  createLLMControlLayer: () => createLLMControlLayer,
32
41
  extractAndCleanJSON: () => extractAndCleanJSON,
42
+ resolveModelPrice: () => resolveModelPrice,
43
+ resolveModelPriceAsync: () => resolveModelPriceAsync,
33
44
  sanitizePII: () => sanitizePII,
34
45
  validateUserInput: () => validateUserInput,
35
46
  validateWithZod: () => validateWithZod
36
47
  });
37
48
  module.exports = __toCommonJS(index_exports);
38
49
 
50
+ // src/types.ts
51
+ var ConfigurationError = class extends Error {
52
+ constructor(message) {
53
+ super(message);
54
+ this.name = "ConfigurationError";
55
+ }
56
+ };
57
+ var BudgetExceededError = class extends Error {
58
+ constructor(message) {
59
+ super(message);
60
+ this.name = "BudgetExceededError";
61
+ }
62
+ };
63
+
39
64
  // src/pricing.ts
40
- var DEFAULT_MODEL_PRICES = {
41
- // DeepSeek
42
- "deepseek-chat": {
43
- promptUSDPerMillion: 0.14,
44
- completionUSDPerMillion: 0.28,
45
- cacheHitUSDPerMillion: 0.014
46
- },
47
- "deepseek-reasoner": {
48
- promptUSDPerMillion: 0.55,
49
- completionUSDPerMillion: 2.19,
50
- cacheHitUSDPerMillion: 0.14
51
- },
52
- // Mistral
53
- "mistral-small-latest": {
54
- promptUSDPerMillion: 0.2,
55
- completionUSDPerMillion: 0.6
56
- },
57
- "mistral-large-latest": {
58
- promptUSDPerMillion: 2,
59
- completionUSDPerMillion: 6
65
+ var SEED_MODEL_PRICES = {
66
+ // DeepSeek Direct
67
+ "deepseek-chat": { promptUSDPerMillion: 0.14, completionUSDPerMillion: 0.28, cacheHitUSDPerMillion: 0.014 },
68
+ "deepseek-reasoner": { promptUSDPerMillion: 0.55, completionUSDPerMillion: 2.19, cacheHitUSDPerMillion: 0.14 },
69
+ "deepseek/deepseek-chat": { promptUSDPerMillion: 0.14, completionUSDPerMillion: 0.28, cacheHitUSDPerMillion: 0.014 },
70
+ "deepseek/deepseek-reasoner": { promptUSDPerMillion: 0.55, completionUSDPerMillion: 2.19, cacheHitUSDPerMillion: 0.14 },
71
+ // Mistral AI Direct
72
+ "mistral-small-latest": { promptUSDPerMillion: 0.2, completionUSDPerMillion: 0.6 },
73
+ "mistral-large-latest": { promptUSDPerMillion: 2, completionUSDPerMillion: 6 },
74
+ "mistral/mistral-small-latest": { promptUSDPerMillion: 0.2, completionUSDPerMillion: 0.6 },
75
+ "mistral/mistral-large-latest": { promptUSDPerMillion: 2, completionUSDPerMillion: 6 },
76
+ // OpenAI Direct
77
+ "gpt-4o-mini": { promptUSDPerMillion: 0.15, completionUSDPerMillion: 0.6 },
78
+ "gpt-4o": { promptUSDPerMillion: 2.5, completionUSDPerMillion: 10 },
79
+ "openai/gpt-4o-mini": { promptUSDPerMillion: 0.15, completionUSDPerMillion: 0.6 },
80
+ "openai/gpt-4o": { promptUSDPerMillion: 2.5, completionUSDPerMillion: 10 },
81
+ // OpenRouter Direct
82
+ "openrouter/deepseek/deepseek-chat": { promptUSDPerMillion: 0.14, completionUSDPerMillion: 0.28 },
83
+ "openrouter/deepseek/deepseek-r1": { promptUSDPerMillion: 0.55, completionUSDPerMillion: 2.19 },
84
+ "openrouter/anthropic/claude-3.5-sonnet": { promptUSDPerMillion: 3, completionUSDPerMillion: 15 },
85
+ // Local Ollama (Toujours $0)
86
+ "ollama": { promptUSDPerMillion: 0, completionUSDPerMillion: 0 }
87
+ };
88
+ function normalizeKey(identifier) {
89
+ return identifier.trim().toLowerCase().replace(":", "/");
90
+ }
91
+ var CachedPricingAdapter = class {
92
+ cache = /* @__PURE__ */ new Map();
93
+ ttlMs;
94
+ delegate;
95
+ constructor(delegate, ttlMs = 5 * 60 * 1e3) {
96
+ this.delegate = delegate;
97
+ this.ttlMs = ttlMs;
98
+ }
99
+ async fetchPrice(model, provider) {
100
+ const key = provider ? normalizeKey(`${provider}/${model}`) : normalizeKey(model);
101
+ const cached = this.cache.get(key);
102
+ if (cached && Date.now() < cached.expiresAt) {
103
+ return cached.price;
104
+ }
105
+ try {
106
+ const freshPrice = await this.delegate.fetchPrice(model, provider);
107
+ if (freshPrice) {
108
+ this.cache.set(key, { price: freshPrice, expiresAt: Date.now() + this.ttlMs });
109
+ return freshPrice;
110
+ }
111
+ } catch {
112
+ if (cached) {
113
+ return cached.price;
114
+ }
115
+ }
116
+ return void 0;
117
+ }
118
+ peek(model, provider) {
119
+ const key = provider ? normalizeKey(`${provider}/${model}`) : normalizeKey(model);
120
+ return this.cache.get(key)?.price;
121
+ }
122
+ clearCache() {
123
+ this.cache.clear();
124
+ }
125
+ invalidate(model, provider) {
126
+ if (!model) {
127
+ this.clearCache();
128
+ return;
129
+ }
130
+ const key = provider ? normalizeKey(`${provider}/${model}`) : normalizeKey(model);
131
+ this.cache.delete(key);
132
+ }
133
+ };
134
+ var PricingRegistry = class {
135
+ static prices = /* @__PURE__ */ new Map();
136
+ static adapter;
137
+ static registerPrice(identifier, price) {
138
+ this.prices.set(normalizeKey(identifier), price);
139
+ }
140
+ static registerDistributorPrices(distributor, priceMap) {
141
+ for (const [model, price] of Object.entries(priceMap)) {
142
+ this.prices.set(normalizeKey(`${distributor}/${model}`), price);
143
+ }
144
+ }
145
+ static registerPrices(prices) {
146
+ for (const [key, price] of Object.entries(prices)) {
147
+ this.prices.set(normalizeKey(key), price);
148
+ }
149
+ }
150
+ static getPrice(model, provider) {
151
+ if (provider) {
152
+ const distributorKey = normalizeKey(`${provider}/${model}`);
153
+ const directDistributorPrice = this.prices.get(distributorKey);
154
+ if (directDistributorPrice) {
155
+ return directDistributorPrice;
156
+ }
157
+ }
158
+ return this.prices.get(normalizeKey(model));
159
+ }
160
+ static setAdapter(adapter) {
161
+ this.adapter = adapter;
162
+ }
163
+ static getAdapter() {
164
+ return this.adapter;
165
+ }
166
+ static clear() {
167
+ this.prices.clear();
168
+ this.adapter = void 0;
169
+ }
170
+ /**
171
+ * Réinitialise le registre en chargeant le catalogue d'exemple SEED_MODEL_PRICES.
172
+ */
173
+ static loadSeedPrices() {
174
+ this.registerPrices(SEED_MODEL_PRICES);
175
+ }
176
+ static getAllPrices() {
177
+ const out = {};
178
+ for (const [k, v] of this.prices.entries()) {
179
+ out[k] = v;
180
+ }
181
+ return out;
182
+ }
183
+ };
184
+ var DEFAULT_MODEL_PRICES = new Proxy({}, {
185
+ get(_target, prop) {
186
+ return PricingRegistry.getPrice(prop);
60
187
  },
61
- // OpenAI
62
- "gpt-4o-mini": {
63
- promptUSDPerMillion: 0.15,
64
- completionUSDPerMillion: 0.6
188
+ has(_target, prop) {
189
+ return Boolean(PricingRegistry.getPrice(prop));
65
190
  },
66
- "gpt-4o": {
67
- promptUSDPerMillion: 2.5,
68
- completionUSDPerMillion: 10
191
+ ownKeys() {
192
+ return Object.keys(PricingRegistry.getAllPrices());
69
193
  },
70
- // Ollama (local)
71
- "ollama": {
72
- promptUSDPerMillion: 0,
73
- completionUSDPerMillion: 0
194
+ getOwnPropertyDescriptor(_target, prop) {
195
+ const price = PricingRegistry.getPrice(prop);
196
+ if (price) {
197
+ return { value: price, enumerable: true, configurable: true, writable: false };
198
+ }
199
+ return void 0;
200
+ }
201
+ });
202
+ function resolveModelPrice(model, options) {
203
+ const opts = typeof options === "string" ? { provider: options } : options ?? {};
204
+ if (opts.providerPricing) {
205
+ return opts.providerPricing;
206
+ }
207
+ if (opts.customPricing) {
208
+ if (opts.provider) {
209
+ const customDistributorKey = normalizeKey(`${opts.provider}/${model}`);
210
+ if (opts.customPricing[customDistributorKey]) {
211
+ return opts.customPricing[customDistributorKey];
212
+ }
213
+ }
214
+ if (opts.customPricing[normalizeKey(model)] ?? opts.customPricing[model]) {
215
+ return opts.customPricing[normalizeKey(model)] ?? opts.customPricing[model];
216
+ }
217
+ }
218
+ const activeAdapter = opts.adapter ?? PricingRegistry.getAdapter();
219
+ if (activeAdapter instanceof CachedPricingAdapter) {
220
+ const cached = activeAdapter.peek(model, opts.provider);
221
+ if (cached) {
222
+ return cached;
223
+ }
224
+ }
225
+ const registeredPrice = PricingRegistry.getPrice(model, opts.provider);
226
+ if (registeredPrice) {
227
+ return registeredPrice;
228
+ }
229
+ if (opts.provider === "ollama" || model.toLowerCase().includes("ollama")) {
230
+ return { promptUSDPerMillion: 0, completionUSDPerMillion: 0 };
231
+ }
232
+ return void 0;
233
+ }
234
+ async function resolveModelPriceAsync(model, options) {
235
+ const opts = typeof options === "string" ? { provider: options } : options ?? {};
236
+ const activeAdapter = opts.adapter ?? PricingRegistry.getAdapter();
237
+ if (activeAdapter) {
238
+ try {
239
+ const adapterPrice = await activeAdapter.fetchPrice(model, opts.provider);
240
+ if (adapterPrice) {
241
+ return adapterPrice;
242
+ }
243
+ } catch {
244
+ }
245
+ }
246
+ return resolveModelPrice(model, options);
247
+ }
248
+ function calculateCostUSD(model, promptTokens, completionTokens, cacheHitTokens = 0, options) {
249
+ const pricing = resolveModelPrice(model, options);
250
+ if (!pricing) {
251
+ return 0;
74
252
  }
75
- };
76
- function calculateCostUSD(model, promptTokens, completionTokens, cacheHitTokens = 0) {
77
- const pricing = DEFAULT_MODEL_PRICES[model] ?? {
78
- promptUSDPerMillion: 0.5,
79
- completionUSDPerMillion: 1.5
80
- };
81
253
  const promptCost = promptTokens / 1e6 * pricing.promptUSDPerMillion;
82
254
  const completionCost = completionTokens / 1e6 * pricing.completionUSDPerMillion;
83
255
  const cacheDiscount = cacheHitTokens && pricing.cacheHitUSDPerMillion ? cacheHitTokens / 1e6 * (pricing.promptUSDPerMillion - pricing.cacheHitUSDPerMillion) : 0;
@@ -673,11 +845,144 @@ function validateWithZod(rawText, schema, options) {
673
845
  return schema.parse(parsed);
674
846
  }
675
847
 
848
+ // src/providers/http-client.ts
849
+ var DEFAULT_BASE_URLS = {
850
+ deepseek: "https://api.deepseek.com/v1",
851
+ mistral: "https://api.mistral.ai/v1",
852
+ openai: "https://api.openai.com/v1",
853
+ ollama: "http://localhost:11434/v1",
854
+ openrouter: "https://openrouter.ai/api/v1"
855
+ };
856
+ var HttpProviderClient = class {
857
+ name;
858
+ baseUrl;
859
+ apiKey;
860
+ providerType;
861
+ constructor(config) {
862
+ this.providerType = config.provider;
863
+ this.name = `${config.provider}-http`;
864
+ this.apiKey = config.apiKey;
865
+ this.baseUrl = config.baseUrl || DEFAULT_BASE_URLS[config.provider] || "https://api.openai.com/v1";
866
+ }
867
+ resolveEndpoint() {
868
+ const trimmed = this.baseUrl.replace(/\/+$/, "");
869
+ if (trimmed.endsWith("/chat/completions")) {
870
+ return trimmed;
871
+ }
872
+ return `${trimmed}/chat/completions`;
873
+ }
874
+ buildHeaders() {
875
+ const headers = {
876
+ "Content-Type": "application/json"
877
+ };
878
+ if (this.apiKey) {
879
+ headers["Authorization"] = `Bearer ${this.apiKey}`;
880
+ }
881
+ if (this.providerType === "openrouter") {
882
+ headers["HTTP-Referer"] = "https://avantgate.dev";
883
+ headers["X-Title"] = "AvantGate";
884
+ }
885
+ return headers;
886
+ }
887
+ buildPayload(options, model) {
888
+ return {
889
+ model,
890
+ messages: options.messages.map((m) => ({
891
+ role: m.role,
892
+ content: m.content
893
+ })),
894
+ temperature: options.temperature ?? 0.2,
895
+ ...options.responseFormat ? { response_format: options.responseFormat } : {}
896
+ };
897
+ }
898
+ extractUsage(usageData) {
899
+ if (!usageData) {
900
+ return void 0;
901
+ }
902
+ return {
903
+ promptTokens: usageData.prompt_tokens,
904
+ completionTokens: usageData.completion_tokens,
905
+ totalTokens: usageData.total_tokens,
906
+ promptCacheHitTokens: usageData.prompt_tokens_details?.cached_tokens
907
+ };
908
+ }
909
+ async complete(options) {
910
+ const endpoint = this.resolveEndpoint();
911
+ const model = options.model ?? "default";
912
+ const headers = this.buildHeaders();
913
+ const body = JSON.stringify(this.buildPayload(options, model));
914
+ const response = await fetch(endpoint, {
915
+ method: "POST",
916
+ headers,
917
+ body
918
+ });
919
+ if (!response.ok) {
920
+ const errorText = await response.text();
921
+ throw new Error(
922
+ `[AvantGate HTTP Provider ${this.providerType}] HTTP ${response.status} ${response.statusText}: ${errorText}`
923
+ );
924
+ }
925
+ const data = await response.json();
926
+ const text = data.choices?.[0]?.message?.content ?? "";
927
+ const usage = this.extractUsage(data.usage);
928
+ return { text, usage };
929
+ }
930
+ };
931
+ function createHttpProviderClient(config) {
932
+ return new HttpProviderClient(config);
933
+ }
934
+
676
935
  // src/control-layer.ts
677
936
  var AvantGateControlLayer = class {
678
937
  config;
938
+ cachedPricingAdapter;
679
939
  constructor(config) {
680
- this.config = config;
940
+ this.config = {
941
+ ...config,
942
+ primary: this.resolveProviderConfig(config.primary),
943
+ fallback: this.resolveProviderConfig(config.fallback),
944
+ emergencyFallback: this.resolveProviderConfig(config.emergencyFallback)
945
+ };
946
+ if (config.pricingAdapter) {
947
+ this.cachedPricingAdapter = new CachedPricingAdapter(
948
+ config.pricingAdapter,
949
+ config.pricingCacheTtlMs ?? 5 * 60 * 1e3
950
+ );
951
+ }
952
+ }
953
+ resolveProviderConfig(provider) {
954
+ if (!provider) return void 0;
955
+ if (provider.client) return provider;
956
+ if (provider.apiKey || provider.baseUrl || provider.provider === "ollama") {
957
+ return {
958
+ ...provider,
959
+ client: createHttpProviderClient(provider)
960
+ };
961
+ }
962
+ return provider;
963
+ }
964
+ findProviderConfig(provider, model) {
965
+ const list = [this.config.primary, this.config.fallback, this.config.emergencyFallback].filter(
966
+ (p) => Boolean(p)
967
+ );
968
+ if (provider) {
969
+ const matchProvider = list.find((p) => p.provider === provider);
970
+ if (matchProvider) return matchProvider;
971
+ }
972
+ if (model) {
973
+ const matchModel = list.find((p) => p.model === model);
974
+ if (matchModel) return matchModel;
975
+ }
976
+ return this.config.primary;
977
+ }
978
+ calculateCost(model, promptTokens, completionTokens, cacheHitTokens = 0, provider) {
979
+ const providerCfg = this.findProviderConfig(provider, model);
980
+ return calculateCostUSD(model, promptTokens, completionTokens, cacheHitTokens, {
981
+ provider: provider ?? providerCfg?.provider,
982
+ providerPricing: providerCfg?.pricing,
983
+ customPricing: this.config.customPricing,
984
+ adapter: this.cachedPricingAdapter
985
+ });
681
986
  }
682
987
  applySecurityGuards(userQuery) {
683
988
  const guard = validateUserInput(userQuery, {
@@ -692,6 +997,46 @@ var AvantGateControlLayer = class {
692
997
  }
693
998
  return userQuery;
694
999
  }
1000
+ checkPreflightBudget(estimatedPromptTokens, targetModel, provider) {
1001
+ if (this.config.maxTokenBudget !== void 0 && estimatedPromptTokens > this.config.maxTokenBudget) {
1002
+ throw new BudgetExceededError(
1003
+ `[AvantGate Budget Guard] Pre-flight token budget exceeded: estimated prompt (${estimatedPromptTokens} tokens) exceeds maxTokenBudget (${this.config.maxTokenBudget}).`
1004
+ );
1005
+ }
1006
+ if (this.config.maxCostUSD !== void 0) {
1007
+ const isLocalFree = provider === "ollama" || targetModel.toLowerCase().includes("ollama");
1008
+ const providerCfg = this.findProviderConfig(provider, targetModel);
1009
+ const price = resolveModelPrice(targetModel, {
1010
+ provider: provider ?? providerCfg?.provider,
1011
+ providerPricing: providerCfg?.pricing,
1012
+ customPricing: this.config.customPricing,
1013
+ adapter: this.cachedPricingAdapter
1014
+ });
1015
+ if (!isLocalFree && !price) {
1016
+ throw new ConfigurationError(
1017
+ `[AvantGate Configuration Error] 'maxCostUSD' was set to $${this.config.maxCostUSD}, but no pricing was configured for model '${targetModel}'. Please define pricing in ProviderConfig, customPricing, or via PricingAdapter.`
1018
+ );
1019
+ }
1020
+ const estimatedPromptCost = this.calculateCost(targetModel, estimatedPromptTokens, 0, 0, provider);
1021
+ if (estimatedPromptCost > this.config.maxCostUSD) {
1022
+ throw new BudgetExceededError(
1023
+ `[AvantGate Budget Guard] Pre-flight cost budget exceeded: estimated prompt cost ($${estimatedPromptCost.toFixed(6)}) exceeds maxCostUSD ($${this.config.maxCostUSD}).`
1024
+ );
1025
+ }
1026
+ }
1027
+ }
1028
+ checkPostExecutionBudget(tokensTotal, costUSD) {
1029
+ if (this.config.maxTokenBudget !== void 0 && tokensTotal > this.config.maxTokenBudget) {
1030
+ throw new BudgetExceededError(
1031
+ `[AvantGate Budget Guard] Execution total tokens (${tokensTotal}) exceeded maxTokenBudget (${this.config.maxTokenBudget}).`
1032
+ );
1033
+ }
1034
+ if (this.config.maxCostUSD !== void 0 && costUSD > this.config.maxCostUSD) {
1035
+ throw new BudgetExceededError(
1036
+ `[AvantGate Budget Guard] Execution cost ($${costUSD.toFixed(6)}) exceeded maxCostUSD ($${this.config.maxCostUSD}).`
1037
+ );
1038
+ }
1039
+ }
695
1040
  buildMessages(systemPrompt, query) {
696
1041
  const messages = [];
697
1042
  if (systemPrompt) {
@@ -704,7 +1049,12 @@ var AvantGateControlLayer = class {
704
1049
  const promptTokens = rawUsage?.promptTokens ?? Math.ceil(query.length / 4);
705
1050
  const completionTokens = rawUsage?.completionTokens ?? Math.ceil(text.length / 4);
706
1051
  const totalTokens = rawUsage?.totalTokens ?? promptTokens + completionTokens;
707
- return { promptTokens, completionTokens, totalTokens };
1052
+ return {
1053
+ promptTokens,
1054
+ completionTokens,
1055
+ totalTokens,
1056
+ promptCacheHitTokens: rawUsage?.promptCacheHitTokens
1057
+ };
708
1058
  }
709
1059
  getProviderChain() {
710
1060
  return [
@@ -716,7 +1066,7 @@ var AvantGateControlLayer = class {
716
1066
  executeSimulation(query, systemPrompt) {
717
1067
  const promptTokens = Math.ceil(((systemPrompt?.length ?? 0) + query.length) / 4);
718
1068
  const completionTokens = 50;
719
- const cost = calculateCostUSD(this.config.primary.model, promptTokens, completionTokens);
1069
+ const cost = this.calculateCost(this.config.primary.model, promptTokens, completionTokens);
720
1070
  return {
721
1071
  text: `[AvantGate In-Process Engine] Response simulation for model: ${this.config.primary.model}`,
722
1072
  tokens: {
@@ -772,10 +1122,11 @@ var AvantGateControlLayer = class {
772
1122
  throw lastError;
773
1123
  }
774
1124
  assembleResult(output) {
775
- const costUSD = calculateCostUSD(
1125
+ const costUSD = this.calculateCost(
776
1126
  output.modelUsed,
777
1127
  output.usage.promptTokens,
778
- output.usage.completionTokens
1128
+ output.usage.completionTokens,
1129
+ output.usage.promptCacheHitTokens ?? 0
779
1130
  );
780
1131
  return {
781
1132
  text: output.responseText,
@@ -804,18 +1155,27 @@ var AvantGateControlLayer = class {
804
1155
  });
805
1156
  }
806
1157
  /**
807
- * Exécute une requête avec garde d'entrée, masquage PII, et calcul des coûts.
1158
+ * Exécute une requête avec garde d'entrée, masquage PII, garde pré-vol et calcul des coûts.
808
1159
  */
809
1160
  async execute(options) {
810
1161
  const sanitizedQuery = this.applySecurityGuards(options.userQuery);
811
1162
  const messages = this.buildMessages(options.systemPrompt, sanitizedQuery);
1163
+ const promptLength = (options.systemPrompt?.length ?? 0) + sanitizedQuery.length;
1164
+ const estimatedPromptTokens = Math.ceil(promptLength / 4);
1165
+ this.checkPreflightBudget(estimatedPromptTokens, this.config.primary.model, this.config.primary.provider);
812
1166
  if (!options.providerOverride && this.getProviderChain().length === 0) {
813
- const simResult = this.executeSimulation(sanitizedQuery, options.systemPrompt);
814
- await this.notifyAuditSink(simResult);
815
- return simResult;
1167
+ if (this.config.mockSimulation) {
1168
+ const simResult = this.executeSimulation(sanitizedQuery, options.systemPrompt);
1169
+ await this.notifyAuditSink(simResult);
1170
+ return simResult;
1171
+ }
1172
+ throw new ConfigurationError(
1173
+ "[AvantGate Configuration Error] No active LLM provider configured. Provide a client implementing LLMProviderPort or configure credentials (apiKey / baseUrl)."
1174
+ );
816
1175
  }
817
1176
  const output = options.providerOverride ? await this.executeOverrideProvider(options.providerOverride, messages, sanitizedQuery, options.temperature) : await this.executeProviderPipeline(messages, sanitizedQuery, options.temperature);
818
1177
  const result = this.assembleResult(output);
1178
+ this.checkPostExecutionBudget(result.tokens.total, result.costUSD);
819
1179
  await this.notifyAuditSink(result);
820
1180
  return result;
821
1181
  }
@@ -858,6 +1218,9 @@ var AvantGateControlLayer = class {
858
1218
  }
859
1219
  return msg;
860
1220
  });
1221
+ const promptLength = processedMessages.reduce((sum, msg) => sum + msg.content.length, 0);
1222
+ const estimatedPromptTokens = Math.ceil(promptLength / 4);
1223
+ this.checkPreflightBudget(estimatedPromptTokens, modelToUse);
861
1224
  let lastError;
862
1225
  let accumulatedPromptTokens = 0;
863
1226
  let accumulatedCompletionTokens = 0;
@@ -880,10 +1243,16 @@ var AvantGateControlLayer = class {
880
1243
  attemptCompletionTokens = usage.completionTokens;
881
1244
  modelUsed = modelToUse;
882
1245
  } else if (this.getProviderChain().length === 0) {
883
- const sim = this.executeSimulation(JSON.stringify(processedMessages));
884
- responseText = sim.text;
885
- attemptPromptTokens = sim.tokens.prompt;
886
- attemptCompletionTokens = sim.tokens.completion;
1246
+ if (this.config.mockSimulation) {
1247
+ const sim = this.executeSimulation(JSON.stringify(processedMessages));
1248
+ responseText = sim.text;
1249
+ attemptPromptTokens = sim.tokens.prompt;
1250
+ attemptCompletionTokens = sim.tokens.completion;
1251
+ } else {
1252
+ throw new ConfigurationError(
1253
+ "[AvantGate Configuration Error] No active LLM provider configured. Provide a client implementing LLMProviderPort or configure credentials (apiKey / baseUrl)."
1254
+ );
1255
+ }
887
1256
  } else {
888
1257
  const output = await this.executeProviderPipeline(
889
1258
  processedMessages,
@@ -907,7 +1276,8 @@ var AvantGateControlLayer = class {
907
1276
  jurisdiction: this.config.features?.finance?.jurisdiction
908
1277
  });
909
1278
  const totalTokens = accumulatedPromptTokens + accumulatedCompletionTokens;
910
- const costUSD = calculateCostUSD(modelUsed, accumulatedPromptTokens, accumulatedCompletionTokens);
1279
+ const costUSD = this.calculateCost(modelUsed, accumulatedPromptTokens, accumulatedCompletionTokens);
1280
+ this.checkPostExecutionBudget(totalTokens, costUSD);
911
1281
  const result = {
912
1282
  data: parsedData,
913
1283
  rawText: responseText,
@@ -1261,16 +1631,27 @@ var PromptRegistry = class {
1261
1631
  };
1262
1632
  // Annotate the CommonJS export names for ESM import in node:
1263
1633
  0 && (module.exports = {
1634
+ AvantGateBudgetExceededError,
1635
+ AvantGateConfigurationError,
1264
1636
  AvantGateControlLayer,
1637
+ BudgetExceededError,
1638
+ CachedPricingAdapter,
1639
+ ConfigurationError,
1265
1640
  DEFAULT_MODEL_PRICES,
1641
+ HttpProviderClient,
1642
+ PricingRegistry,
1266
1643
  PromptBuilder,
1267
1644
  PromptRegistry,
1268
1645
  PromptTemplate,
1646
+ SEED_MODEL_PRICES,
1269
1647
  ZenLLMControlLayer,
1270
1648
  calculateCostUSD,
1271
1649
  createAvantGate,
1650
+ createHttpProviderClient,
1272
1651
  createLLMControlLayer,
1273
1652
  extractAndCleanJSON,
1653
+ resolveModelPrice,
1654
+ resolveModelPriceAsync,
1274
1655
  sanitizePII,
1275
1656
  validateUserInput,
1276
1657
  validateWithZod