avantgate 1.0.0

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 ADDED
@@ -0,0 +1,361 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AvantGateControlLayer: () => AvantGateControlLayer,
24
+ DEFAULT_MODEL_PRICES: () => DEFAULT_MODEL_PRICES,
25
+ ZenLLMControlLayer: () => AvantGateControlLayer,
26
+ calculateCostUSD: () => calculateCostUSD,
27
+ createAvantGate: () => createAvantGate,
28
+ createLLMControlLayer: () => createLLMControlLayer,
29
+ extractAndCleanJSON: () => extractAndCleanJSON,
30
+ sanitizePII: () => sanitizePII,
31
+ validateUserInput: () => validateUserInput,
32
+ validateWithZod: () => validateWithZod
33
+ });
34
+ module.exports = __toCommonJS(index_exports);
35
+
36
+ // src/pricing.ts
37
+ var DEFAULT_MODEL_PRICES = {
38
+ // DeepSeek
39
+ "deepseek-chat": {
40
+ promptUSDPerMillion: 0.14,
41
+ completionUSDPerMillion: 0.28,
42
+ cacheHitUSDPerMillion: 0.014
43
+ },
44
+ "deepseek-reasoner": {
45
+ promptUSDPerMillion: 0.55,
46
+ completionUSDPerMillion: 2.19,
47
+ cacheHitUSDPerMillion: 0.14
48
+ },
49
+ // Mistral
50
+ "mistral-small-latest": {
51
+ promptUSDPerMillion: 0.2,
52
+ completionUSDPerMillion: 0.6
53
+ },
54
+ "mistral-large-latest": {
55
+ promptUSDPerMillion: 2,
56
+ completionUSDPerMillion: 6
57
+ },
58
+ // OpenAI
59
+ "gpt-4o-mini": {
60
+ promptUSDPerMillion: 0.15,
61
+ completionUSDPerMillion: 0.6
62
+ },
63
+ "gpt-4o": {
64
+ promptUSDPerMillion: 2.5,
65
+ completionUSDPerMillion: 10
66
+ },
67
+ // Ollama (local)
68
+ "ollama": {
69
+ promptUSDPerMillion: 0,
70
+ completionUSDPerMillion: 0
71
+ }
72
+ };
73
+ function calculateCostUSD(model, promptTokens, completionTokens, cacheHitTokens = 0) {
74
+ const pricing = DEFAULT_MODEL_PRICES[model] ?? {
75
+ promptUSDPerMillion: 0.5,
76
+ completionUSDPerMillion: 1.5
77
+ };
78
+ const promptCost = promptTokens / 1e6 * pricing.promptUSDPerMillion;
79
+ const completionCost = completionTokens / 1e6 * pricing.completionUSDPerMillion;
80
+ const cacheDiscount = cacheHitTokens && pricing.cacheHitUSDPerMillion ? cacheHitTokens / 1e6 * (pricing.promptUSDPerMillion - pricing.cacheHitUSDPerMillion) : 0;
81
+ return Math.max(0, promptCost + completionCost - cacheDiscount);
82
+ }
83
+
84
+ // src/sanitizer.ts
85
+ var EMAIL_REGEX = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b/g;
86
+ var PHONE_FR_REGEX = /\b(?:(?:\+|00)33|0)\s*[1-9](?:[\s.-]*\d{2}){4}\b/g;
87
+ var NIR_SSN_REGEX = /\b[12]\s*\d{2}\s*\d{2}\s*\d{2}\s*\d{3}\s*\d{3}(?:\s*\d{2})?\b/g;
88
+ var IBAN_REGEX = /\b[A-Z]{2}\d{2}[A-Z0-9]{4}\d{7}([A-Z0-9]?){0,16}\b/g;
89
+ function sanitizePII(input) {
90
+ let count = 0;
91
+ let result = input;
92
+ result = result.replace(EMAIL_REGEX, () => {
93
+ count++;
94
+ return "[REDACTED_EMAIL]";
95
+ });
96
+ result = result.replace(PHONE_FR_REGEX, () => {
97
+ count++;
98
+ return "[REDACTED_PHONE]";
99
+ });
100
+ result = result.replace(NIR_SSN_REGEX, () => {
101
+ count++;
102
+ return "[REDACTED_NIR]";
103
+ });
104
+ result = result.replace(IBAN_REGEX, () => {
105
+ count++;
106
+ return "[REDACTED_IBAN]";
107
+ });
108
+ return { text: result, maskedCount: count };
109
+ }
110
+
111
+ // src/input-guard.ts
112
+ var INJECTION_PATTERNS = [
113
+ /ignore\s+(?:all\s+)?(?:previous|prior)\s+(?:instructions|prompts|rules)/i,
114
+ /disregard\s+(?:all\s+)?(?:previous|prior)\s+(?:instructions|prompts)/i,
115
+ /you\s+are\s+now\s+(?:dan|unrestricted|in\s+developer\s+mode)/i,
116
+ /bypass\s+(?:all\s+)?(?:content\s+filters|safety\s+guidelines)/i,
117
+ /output\s+(?:your\s+)?(?:system\s+prompt|initial\s+instructions)/i,
118
+ /repeat\s+(?:the\s+words\s+above|everything\s+above)/i
119
+ ];
120
+ function validateUserInput(input, options) {
121
+ const maxLength = options?.maxLength ?? 5e4;
122
+ if (input.length > maxLength) {
123
+ return {
124
+ valid: false,
125
+ blockedReason: `Input length (${input.length}) exceeds max allowed (${maxLength})`
126
+ };
127
+ }
128
+ if (options?.detectInjection !== false) {
129
+ for (const pattern of INJECTION_PATTERNS) {
130
+ if (pattern.test(input)) {
131
+ return {
132
+ valid: false,
133
+ blockedReason: `Prompt injection or jailbreak detected: matches security rule [${pattern.source}]`
134
+ };
135
+ }
136
+ }
137
+ }
138
+ return { valid: true };
139
+ }
140
+
141
+ // src/response-validator.ts
142
+ function extractAndCleanJSON(rawText) {
143
+ let cleaned = rawText.trim();
144
+ cleaned = cleaned.replace(/<think>[\s\S]*?<\/think>/gi, "").trim();
145
+ const codeBlockMatch = cleaned.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
146
+ if (codeBlockMatch && codeBlockMatch[1]) {
147
+ cleaned = codeBlockMatch[1].trim();
148
+ }
149
+ const firstBrace = cleaned.indexOf("{");
150
+ const firstBracket = cleaned.indexOf("[");
151
+ let startIndex = -1;
152
+ if (firstBrace !== -1 && firstBracket !== -1) {
153
+ startIndex = Math.min(firstBrace, firstBracket);
154
+ } else if (firstBrace !== -1) {
155
+ startIndex = firstBrace;
156
+ } else if (firstBracket !== -1) {
157
+ startIndex = firstBracket;
158
+ }
159
+ const lastBrace = cleaned.lastIndexOf("}");
160
+ const lastBracket = cleaned.lastIndexOf("]");
161
+ const endIndex = Math.max(lastBrace, lastBracket);
162
+ if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
163
+ cleaned = cleaned.slice(startIndex, endIndex + 1);
164
+ }
165
+ return cleaned;
166
+ }
167
+ function validateWithZod(rawText, schema) {
168
+ const jsonString = extractAndCleanJSON(rawText);
169
+ let parsed;
170
+ try {
171
+ parsed = JSON.parse(jsonString);
172
+ } catch (error) {
173
+ const sanitized = jsonString.replace(/,\s*([}\]])/g, "$1").replace(/'/g, '"');
174
+ parsed = JSON.parse(sanitized);
175
+ }
176
+ return schema.parse(parsed);
177
+ }
178
+
179
+ // src/control-layer.ts
180
+ var AvantGateControlLayer = class {
181
+ config;
182
+ constructor(config) {
183
+ this.config = config;
184
+ }
185
+ applySecurityGuards(userQuery) {
186
+ const guard = validateUserInput(userQuery, {
187
+ detectInjection: this.config.security?.detectPromptInjection,
188
+ maxLength: this.config.security?.maxInputLength
189
+ });
190
+ if (!guard.valid) {
191
+ throw new Error(`[AvantGate Security Guard] Request blocked: ${guard.blockedReason}`);
192
+ }
193
+ if (this.config.security?.maskPII) {
194
+ return sanitizePII(userQuery).text;
195
+ }
196
+ return userQuery;
197
+ }
198
+ buildMessages(systemPrompt, query) {
199
+ const messages = [];
200
+ if (systemPrompt) {
201
+ messages.push({ role: "system", content: systemPrompt });
202
+ }
203
+ messages.push({ role: "user", content: query });
204
+ return messages;
205
+ }
206
+ resolveTokens(rawUsage, query, text) {
207
+ const promptTokens = rawUsage?.promptTokens ?? Math.ceil(query.length / 4);
208
+ const completionTokens = rawUsage?.completionTokens ?? Math.ceil(text.length / 4);
209
+ const totalTokens = rawUsage?.totalTokens ?? promptTokens + completionTokens;
210
+ return { promptTokens, completionTokens, totalTokens };
211
+ }
212
+ getProviderChain() {
213
+ return [
214
+ this.config.primary,
215
+ this.config.fallback,
216
+ this.config.emergencyFallback
217
+ ].filter((provider) => Boolean(provider?.client));
218
+ }
219
+ executeSimulation(query, systemPrompt) {
220
+ const promptTokens = Math.ceil(((systemPrompt?.length ?? 0) + query.length) / 4);
221
+ const completionTokens = 50;
222
+ const cost = calculateCostUSD(this.config.primary.model, promptTokens, completionTokens);
223
+ return {
224
+ text: `[AvantGate In-Process Engine] Response simulation for model: ${this.config.primary.model}`,
225
+ tokens: {
226
+ prompt: promptTokens,
227
+ completion: completionTokens,
228
+ total: promptTokens + completionTokens
229
+ },
230
+ costUSD: cost,
231
+ modelUsed: this.config.primary.model,
232
+ failoverOccurred: false,
233
+ attempts: 1
234
+ };
235
+ }
236
+ async executeOverrideProvider(provider, messages, query, temperature) {
237
+ const response = await provider.complete({
238
+ model: this.config.primary.model,
239
+ messages,
240
+ temperature: temperature ?? 0.2
241
+ });
242
+ const usage = this.resolveTokens(response.usage, query, response.text);
243
+ return {
244
+ responseText: response.text,
245
+ usage,
246
+ modelUsed: this.config.primary.model,
247
+ failoverOccurred: false,
248
+ attempts: 1
249
+ };
250
+ }
251
+ async executeProviderPipeline(messages, query, temperature) {
252
+ const chain = this.getProviderChain();
253
+ let lastError;
254
+ for (let index = 0; index < chain.length; index++) {
255
+ const providerConfig = chain[index];
256
+ try {
257
+ const response = await providerConfig.client.complete({
258
+ model: providerConfig.model,
259
+ messages,
260
+ temperature: temperature ?? 0.2
261
+ });
262
+ const usage = this.resolveTokens(response.usage, query, response.text);
263
+ return {
264
+ responseText: response.text,
265
+ usage,
266
+ modelUsed: providerConfig.model,
267
+ failoverOccurred: index > 0,
268
+ attempts: index + 1
269
+ };
270
+ } catch (err) {
271
+ lastError = err;
272
+ }
273
+ }
274
+ throw lastError;
275
+ }
276
+ assembleResult(output) {
277
+ const costUSD = calculateCostUSD(
278
+ output.modelUsed,
279
+ output.usage.promptTokens,
280
+ output.usage.completionTokens
281
+ );
282
+ return {
283
+ text: output.responseText,
284
+ tokens: {
285
+ prompt: output.usage.promptTokens,
286
+ completion: output.usage.completionTokens,
287
+ total: output.usage.totalTokens
288
+ },
289
+ costUSD,
290
+ modelUsed: output.modelUsed,
291
+ failoverOccurred: output.failoverOccurred,
292
+ attempts: output.attempts
293
+ };
294
+ }
295
+ async notifyAuditSink(result) {
296
+ if (!this.config.auditSink) {
297
+ return;
298
+ }
299
+ await this.config.auditSink.log({
300
+ timestamp: /* @__PURE__ */ new Date(),
301
+ model: result.modelUsed,
302
+ tokens: result.tokens,
303
+ costUSD: result.costUSD,
304
+ failoverOccurred: result.failoverOccurred,
305
+ attempts: result.attempts
306
+ });
307
+ }
308
+ /**
309
+ * Exécute une requête avec garde d'entrée, masquage PII, et calcul des coûts.
310
+ */
311
+ async execute(options) {
312
+ const sanitizedQuery = this.applySecurityGuards(options.userQuery);
313
+ const messages = this.buildMessages(options.systemPrompt, sanitizedQuery);
314
+ if (!options.providerOverride && this.getProviderChain().length === 0) {
315
+ const simResult = this.executeSimulation(sanitizedQuery, options.systemPrompt);
316
+ await this.notifyAuditSink(simResult);
317
+ return simResult;
318
+ }
319
+ const output = options.providerOverride ? await this.executeOverrideProvider(options.providerOverride, messages, sanitizedQuery, options.temperature) : await this.executeProviderPipeline(messages, sanitizedQuery, options.temperature);
320
+ const result = this.assembleResult(output);
321
+ await this.notifyAuditSink(result);
322
+ return result;
323
+ }
324
+ /**
325
+ * Exécute une requête et valide/répare le résultat selon un schéma Zod.
326
+ */
327
+ async executeStructured(options) {
328
+ const rawResult = await this.execute({
329
+ userQuery: options.userQuery,
330
+ systemPrompt: options.systemPrompt,
331
+ temperature: options.temperature ?? 0.1,
332
+ providerOverride: options.providerOverride
333
+ });
334
+ const parsedData = validateWithZod(rawResult.text, options.schema);
335
+ return {
336
+ data: parsedData,
337
+ rawText: rawResult.text,
338
+ tokens: rawResult.tokens,
339
+ costUSD: rawResult.costUSD,
340
+ modelUsed: rawResult.modelUsed,
341
+ failoverOccurred: rawResult.failoverOccurred
342
+ };
343
+ }
344
+ };
345
+ function createLLMControlLayer(config) {
346
+ return new AvantGateControlLayer(config);
347
+ }
348
+ var createAvantGate = createLLMControlLayer;
349
+ // Annotate the CommonJS export names for ESM import in node:
350
+ 0 && (module.exports = {
351
+ AvantGateControlLayer,
352
+ DEFAULT_MODEL_PRICES,
353
+ ZenLLMControlLayer,
354
+ calculateCostUSD,
355
+ createAvantGate,
356
+ createLLMControlLayer,
357
+ extractAndCleanJSON,
358
+ sanitizePII,
359
+ validateUserInput,
360
+ validateWithZod
361
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,325 @@
1
+ // src/pricing.ts
2
+ var DEFAULT_MODEL_PRICES = {
3
+ // DeepSeek
4
+ "deepseek-chat": {
5
+ promptUSDPerMillion: 0.14,
6
+ completionUSDPerMillion: 0.28,
7
+ cacheHitUSDPerMillion: 0.014
8
+ },
9
+ "deepseek-reasoner": {
10
+ promptUSDPerMillion: 0.55,
11
+ completionUSDPerMillion: 2.19,
12
+ cacheHitUSDPerMillion: 0.14
13
+ },
14
+ // Mistral
15
+ "mistral-small-latest": {
16
+ promptUSDPerMillion: 0.2,
17
+ completionUSDPerMillion: 0.6
18
+ },
19
+ "mistral-large-latest": {
20
+ promptUSDPerMillion: 2,
21
+ completionUSDPerMillion: 6
22
+ },
23
+ // OpenAI
24
+ "gpt-4o-mini": {
25
+ promptUSDPerMillion: 0.15,
26
+ completionUSDPerMillion: 0.6
27
+ },
28
+ "gpt-4o": {
29
+ promptUSDPerMillion: 2.5,
30
+ completionUSDPerMillion: 10
31
+ },
32
+ // Ollama (local)
33
+ "ollama": {
34
+ promptUSDPerMillion: 0,
35
+ completionUSDPerMillion: 0
36
+ }
37
+ };
38
+ function calculateCostUSD(model, promptTokens, completionTokens, cacheHitTokens = 0) {
39
+ const pricing = DEFAULT_MODEL_PRICES[model] ?? {
40
+ promptUSDPerMillion: 0.5,
41
+ completionUSDPerMillion: 1.5
42
+ };
43
+ const promptCost = promptTokens / 1e6 * pricing.promptUSDPerMillion;
44
+ const completionCost = completionTokens / 1e6 * pricing.completionUSDPerMillion;
45
+ const cacheDiscount = cacheHitTokens && pricing.cacheHitUSDPerMillion ? cacheHitTokens / 1e6 * (pricing.promptUSDPerMillion - pricing.cacheHitUSDPerMillion) : 0;
46
+ return Math.max(0, promptCost + completionCost - cacheDiscount);
47
+ }
48
+
49
+ // src/sanitizer.ts
50
+ var EMAIL_REGEX = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b/g;
51
+ var PHONE_FR_REGEX = /\b(?:(?:\+|00)33|0)\s*[1-9](?:[\s.-]*\d{2}){4}\b/g;
52
+ var NIR_SSN_REGEX = /\b[12]\s*\d{2}\s*\d{2}\s*\d{2}\s*\d{3}\s*\d{3}(?:\s*\d{2})?\b/g;
53
+ var IBAN_REGEX = /\b[A-Z]{2}\d{2}[A-Z0-9]{4}\d{7}([A-Z0-9]?){0,16}\b/g;
54
+ function sanitizePII(input) {
55
+ let count = 0;
56
+ let result = input;
57
+ result = result.replace(EMAIL_REGEX, () => {
58
+ count++;
59
+ return "[REDACTED_EMAIL]";
60
+ });
61
+ result = result.replace(PHONE_FR_REGEX, () => {
62
+ count++;
63
+ return "[REDACTED_PHONE]";
64
+ });
65
+ result = result.replace(NIR_SSN_REGEX, () => {
66
+ count++;
67
+ return "[REDACTED_NIR]";
68
+ });
69
+ result = result.replace(IBAN_REGEX, () => {
70
+ count++;
71
+ return "[REDACTED_IBAN]";
72
+ });
73
+ return { text: result, maskedCount: count };
74
+ }
75
+
76
+ // src/input-guard.ts
77
+ var INJECTION_PATTERNS = [
78
+ /ignore\s+(?:all\s+)?(?:previous|prior)\s+(?:instructions|prompts|rules)/i,
79
+ /disregard\s+(?:all\s+)?(?:previous|prior)\s+(?:instructions|prompts)/i,
80
+ /you\s+are\s+now\s+(?:dan|unrestricted|in\s+developer\s+mode)/i,
81
+ /bypass\s+(?:all\s+)?(?:content\s+filters|safety\s+guidelines)/i,
82
+ /output\s+(?:your\s+)?(?:system\s+prompt|initial\s+instructions)/i,
83
+ /repeat\s+(?:the\s+words\s+above|everything\s+above)/i
84
+ ];
85
+ function validateUserInput(input, options) {
86
+ const maxLength = options?.maxLength ?? 5e4;
87
+ if (input.length > maxLength) {
88
+ return {
89
+ valid: false,
90
+ blockedReason: `Input length (${input.length}) exceeds max allowed (${maxLength})`
91
+ };
92
+ }
93
+ if (options?.detectInjection !== false) {
94
+ for (const pattern of INJECTION_PATTERNS) {
95
+ if (pattern.test(input)) {
96
+ return {
97
+ valid: false,
98
+ blockedReason: `Prompt injection or jailbreak detected: matches security rule [${pattern.source}]`
99
+ };
100
+ }
101
+ }
102
+ }
103
+ return { valid: true };
104
+ }
105
+
106
+ // src/response-validator.ts
107
+ function extractAndCleanJSON(rawText) {
108
+ let cleaned = rawText.trim();
109
+ cleaned = cleaned.replace(/<think>[\s\S]*?<\/think>/gi, "").trim();
110
+ const codeBlockMatch = cleaned.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
111
+ if (codeBlockMatch && codeBlockMatch[1]) {
112
+ cleaned = codeBlockMatch[1].trim();
113
+ }
114
+ const firstBrace = cleaned.indexOf("{");
115
+ const firstBracket = cleaned.indexOf("[");
116
+ let startIndex = -1;
117
+ if (firstBrace !== -1 && firstBracket !== -1) {
118
+ startIndex = Math.min(firstBrace, firstBracket);
119
+ } else if (firstBrace !== -1) {
120
+ startIndex = firstBrace;
121
+ } else if (firstBracket !== -1) {
122
+ startIndex = firstBracket;
123
+ }
124
+ const lastBrace = cleaned.lastIndexOf("}");
125
+ const lastBracket = cleaned.lastIndexOf("]");
126
+ const endIndex = Math.max(lastBrace, lastBracket);
127
+ if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
128
+ cleaned = cleaned.slice(startIndex, endIndex + 1);
129
+ }
130
+ return cleaned;
131
+ }
132
+ function validateWithZod(rawText, schema) {
133
+ const jsonString = extractAndCleanJSON(rawText);
134
+ let parsed;
135
+ try {
136
+ parsed = JSON.parse(jsonString);
137
+ } catch (error) {
138
+ const sanitized = jsonString.replace(/,\s*([}\]])/g, "$1").replace(/'/g, '"');
139
+ parsed = JSON.parse(sanitized);
140
+ }
141
+ return schema.parse(parsed);
142
+ }
143
+
144
+ // src/control-layer.ts
145
+ var AvantGateControlLayer = class {
146
+ config;
147
+ constructor(config) {
148
+ this.config = config;
149
+ }
150
+ applySecurityGuards(userQuery) {
151
+ const guard = validateUserInput(userQuery, {
152
+ detectInjection: this.config.security?.detectPromptInjection,
153
+ maxLength: this.config.security?.maxInputLength
154
+ });
155
+ if (!guard.valid) {
156
+ throw new Error(`[AvantGate Security Guard] Request blocked: ${guard.blockedReason}`);
157
+ }
158
+ if (this.config.security?.maskPII) {
159
+ return sanitizePII(userQuery).text;
160
+ }
161
+ return userQuery;
162
+ }
163
+ buildMessages(systemPrompt, query) {
164
+ const messages = [];
165
+ if (systemPrompt) {
166
+ messages.push({ role: "system", content: systemPrompt });
167
+ }
168
+ messages.push({ role: "user", content: query });
169
+ return messages;
170
+ }
171
+ resolveTokens(rawUsage, query, text) {
172
+ const promptTokens = rawUsage?.promptTokens ?? Math.ceil(query.length / 4);
173
+ const completionTokens = rawUsage?.completionTokens ?? Math.ceil(text.length / 4);
174
+ const totalTokens = rawUsage?.totalTokens ?? promptTokens + completionTokens;
175
+ return { promptTokens, completionTokens, totalTokens };
176
+ }
177
+ getProviderChain() {
178
+ return [
179
+ this.config.primary,
180
+ this.config.fallback,
181
+ this.config.emergencyFallback
182
+ ].filter((provider) => Boolean(provider?.client));
183
+ }
184
+ executeSimulation(query, systemPrompt) {
185
+ const promptTokens = Math.ceil(((systemPrompt?.length ?? 0) + query.length) / 4);
186
+ const completionTokens = 50;
187
+ const cost = calculateCostUSD(this.config.primary.model, promptTokens, completionTokens);
188
+ return {
189
+ text: `[AvantGate In-Process Engine] Response simulation for model: ${this.config.primary.model}`,
190
+ tokens: {
191
+ prompt: promptTokens,
192
+ completion: completionTokens,
193
+ total: promptTokens + completionTokens
194
+ },
195
+ costUSD: cost,
196
+ modelUsed: this.config.primary.model,
197
+ failoverOccurred: false,
198
+ attempts: 1
199
+ };
200
+ }
201
+ async executeOverrideProvider(provider, messages, query, temperature) {
202
+ const response = await provider.complete({
203
+ model: this.config.primary.model,
204
+ messages,
205
+ temperature: temperature ?? 0.2
206
+ });
207
+ const usage = this.resolveTokens(response.usage, query, response.text);
208
+ return {
209
+ responseText: response.text,
210
+ usage,
211
+ modelUsed: this.config.primary.model,
212
+ failoverOccurred: false,
213
+ attempts: 1
214
+ };
215
+ }
216
+ async executeProviderPipeline(messages, query, temperature) {
217
+ const chain = this.getProviderChain();
218
+ let lastError;
219
+ for (let index = 0; index < chain.length; index++) {
220
+ const providerConfig = chain[index];
221
+ try {
222
+ const response = await providerConfig.client.complete({
223
+ model: providerConfig.model,
224
+ messages,
225
+ temperature: temperature ?? 0.2
226
+ });
227
+ const usage = this.resolveTokens(response.usage, query, response.text);
228
+ return {
229
+ responseText: response.text,
230
+ usage,
231
+ modelUsed: providerConfig.model,
232
+ failoverOccurred: index > 0,
233
+ attempts: index + 1
234
+ };
235
+ } catch (err) {
236
+ lastError = err;
237
+ }
238
+ }
239
+ throw lastError;
240
+ }
241
+ assembleResult(output) {
242
+ const costUSD = calculateCostUSD(
243
+ output.modelUsed,
244
+ output.usage.promptTokens,
245
+ output.usage.completionTokens
246
+ );
247
+ return {
248
+ text: output.responseText,
249
+ tokens: {
250
+ prompt: output.usage.promptTokens,
251
+ completion: output.usage.completionTokens,
252
+ total: output.usage.totalTokens
253
+ },
254
+ costUSD,
255
+ modelUsed: output.modelUsed,
256
+ failoverOccurred: output.failoverOccurred,
257
+ attempts: output.attempts
258
+ };
259
+ }
260
+ async notifyAuditSink(result) {
261
+ if (!this.config.auditSink) {
262
+ return;
263
+ }
264
+ await this.config.auditSink.log({
265
+ timestamp: /* @__PURE__ */ new Date(),
266
+ model: result.modelUsed,
267
+ tokens: result.tokens,
268
+ costUSD: result.costUSD,
269
+ failoverOccurred: result.failoverOccurred,
270
+ attempts: result.attempts
271
+ });
272
+ }
273
+ /**
274
+ * Exécute une requête avec garde d'entrée, masquage PII, et calcul des coûts.
275
+ */
276
+ async execute(options) {
277
+ const sanitizedQuery = this.applySecurityGuards(options.userQuery);
278
+ const messages = this.buildMessages(options.systemPrompt, sanitizedQuery);
279
+ if (!options.providerOverride && this.getProviderChain().length === 0) {
280
+ const simResult = this.executeSimulation(sanitizedQuery, options.systemPrompt);
281
+ await this.notifyAuditSink(simResult);
282
+ return simResult;
283
+ }
284
+ const output = options.providerOverride ? await this.executeOverrideProvider(options.providerOverride, messages, sanitizedQuery, options.temperature) : await this.executeProviderPipeline(messages, sanitizedQuery, options.temperature);
285
+ const result = this.assembleResult(output);
286
+ await this.notifyAuditSink(result);
287
+ return result;
288
+ }
289
+ /**
290
+ * Exécute une requête et valide/répare le résultat selon un schéma Zod.
291
+ */
292
+ async executeStructured(options) {
293
+ const rawResult = await this.execute({
294
+ userQuery: options.userQuery,
295
+ systemPrompt: options.systemPrompt,
296
+ temperature: options.temperature ?? 0.1,
297
+ providerOverride: options.providerOverride
298
+ });
299
+ const parsedData = validateWithZod(rawResult.text, options.schema);
300
+ return {
301
+ data: parsedData,
302
+ rawText: rawResult.text,
303
+ tokens: rawResult.tokens,
304
+ costUSD: rawResult.costUSD,
305
+ modelUsed: rawResult.modelUsed,
306
+ failoverOccurred: rawResult.failoverOccurred
307
+ };
308
+ }
309
+ };
310
+ function createLLMControlLayer(config) {
311
+ return new AvantGateControlLayer(config);
312
+ }
313
+ var createAvantGate = createLLMControlLayer;
314
+ export {
315
+ AvantGateControlLayer,
316
+ DEFAULT_MODEL_PRICES,
317
+ AvantGateControlLayer as ZenLLMControlLayer,
318
+ calculateCostUSD,
319
+ createAvantGate,
320
+ createLLMControlLayer,
321
+ extractAndCleanJSON,
322
+ sanitizePII,
323
+ validateUserInput,
324
+ validateWithZod
325
+ };