avantgate 1.0.0 → 1.1.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/README.md +384 -287
- package/dist/chunk-CO26LNFD.mjs +521 -0
- package/dist/finance/index.d.mts +76 -0
- package/dist/finance/index.d.ts +76 -0
- package/dist/finance/index.js +536 -0
- package/dist/finance/index.mjs +20 -0
- package/dist/index.d.mts +127 -3
- package/dist/index.d.ts +127 -3
- package/dist/index.js +929 -13
- package/dist/index.mjs +460 -48
- package/dist/strategy.interface-CB4_ZAuk.d.mts +16 -0
- package/dist/strategy.interface-CB4_ZAuk.d.ts +16 -0
- package/package.json +67 -55
package/dist/index.js
CHANGED
|
@@ -22,6 +22,9 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
AvantGateControlLayer: () => AvantGateControlLayer,
|
|
24
24
|
DEFAULT_MODEL_PRICES: () => DEFAULT_MODEL_PRICES,
|
|
25
|
+
PromptBuilder: () => PromptBuilder,
|
|
26
|
+
PromptRegistry: () => PromptRegistry,
|
|
27
|
+
PromptTemplate: () => PromptTemplate,
|
|
25
28
|
ZenLLMControlLayer: () => AvantGateControlLayer,
|
|
26
29
|
calculateCostUSD: () => calculateCostUSD,
|
|
27
30
|
createAvantGate: () => createAvantGate,
|
|
@@ -84,8 +87,11 @@ function calculateCostUSD(model, promptTokens, completionTokens, cacheHitTokens
|
|
|
84
87
|
// src/sanitizer.ts
|
|
85
88
|
var EMAIL_REGEX = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b/g;
|
|
86
89
|
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
|
|
88
|
-
var
|
|
90
|
+
var NIR_SSN_REGEX = /\b[12]\s*\d{2}\s*(?:0[1-9]|1[0-2]|[2-9]\d)\s*(?:0[1-9]|[1-8]\d|9[0-8]|2[ABab])\s*(?!000)\d{3}\s*(?!000)\d{3}(?:\s*\d{2})?\b/g;
|
|
91
|
+
var SPI_LABELLED_REGEX = /(?:(?:num[ée]ro\s+fiscal|spi|n[°o]\s*fiscal|d[ée]clarant(?: fiscal)?)\s*[:=]?\s*)\b(\d{2}(?:[\s.-]?\d{2}){5}[\s.-]?\d|\d{13})\b/gi;
|
|
92
|
+
var SPI_FORMATTED_REGEX = /\b[0-3]\d(?:\s+\d{2}){5}\s+\d\b/g;
|
|
93
|
+
var IBAN_REGEX = /\b[A-Z]{2}\s*[0-9]{2}(?:[\s\r\n.-]*[A-Z0-9]){11,30}\b/g;
|
|
94
|
+
var BIC_LABELLED_REGEX = /(?:(?:bic|swift)\s*[:=]?\s*)\b([A-Z]{4}[A-Z]{2}[A-Z0-9]{2}(?:[A-Z0-9]{3})?)\b/gi;
|
|
89
95
|
function sanitizePII(input) {
|
|
90
96
|
let count = 0;
|
|
91
97
|
let result = input;
|
|
@@ -97,13 +103,36 @@ function sanitizePII(input) {
|
|
|
97
103
|
count++;
|
|
98
104
|
return "[REDACTED_PHONE]";
|
|
99
105
|
});
|
|
100
|
-
result = result.replace(
|
|
106
|
+
result = result.replace(IBAN_REGEX, (match) => {
|
|
107
|
+
const cleanChars = match.replace(/[\s\r\n.-]/g, "");
|
|
108
|
+
if (cleanChars.length >= 15 && cleanChars.length <= 34) {
|
|
109
|
+
count++;
|
|
110
|
+
return "[REDACTED_IBAN]";
|
|
111
|
+
}
|
|
112
|
+
return match;
|
|
113
|
+
});
|
|
114
|
+
result = result.replace(BIC_LABELLED_REGEX, (_, bicCode) => {
|
|
101
115
|
count++;
|
|
102
|
-
return
|
|
116
|
+
return `[REDACTED_BIC: ${bicCode.slice(0, 4)}****]`;
|
|
103
117
|
});
|
|
104
|
-
result = result.replace(
|
|
118
|
+
result = result.replace(SPI_LABELLED_REGEX, (fullMatch, digits) => {
|
|
105
119
|
count++;
|
|
106
|
-
return "[
|
|
120
|
+
return fullMatch.replace(digits, "[REDACTED_SPI]");
|
|
121
|
+
});
|
|
122
|
+
result = result.replace(NIR_SSN_REGEX, (match) => {
|
|
123
|
+
const rawDigits = match.replace(/\s+/g, "");
|
|
124
|
+
if (rawDigits.length === 13 || rawDigits.length === 15) {
|
|
125
|
+
count++;
|
|
126
|
+
return "[REDACTED_NIR]";
|
|
127
|
+
}
|
|
128
|
+
return match;
|
|
129
|
+
});
|
|
130
|
+
result = result.replace(SPI_FORMATTED_REGEX, (match) => {
|
|
131
|
+
if (!match.includes("[REDACTED")) {
|
|
132
|
+
count++;
|
|
133
|
+
return "[REDACTED_SPI]";
|
|
134
|
+
}
|
|
135
|
+
return match;
|
|
107
136
|
});
|
|
108
137
|
return { text: result, maskedCount: count };
|
|
109
138
|
}
|
|
@@ -138,6 +167,469 @@ function validateUserInput(input, options) {
|
|
|
138
167
|
return { valid: true };
|
|
139
168
|
}
|
|
140
169
|
|
|
170
|
+
// src/finance/strategies/french-pcg.strategy.ts
|
|
171
|
+
var FrenchPCGStrategy = class {
|
|
172
|
+
jurisdictionCode = "FR";
|
|
173
|
+
standard = "PCG";
|
|
174
|
+
defaultCurrency = "EUR";
|
|
175
|
+
cleanNumber(value) {
|
|
176
|
+
if (typeof value === "number") {
|
|
177
|
+
return Number.isFinite(value) ? value : 0;
|
|
178
|
+
}
|
|
179
|
+
if (!value || typeof value !== "string") {
|
|
180
|
+
return 0;
|
|
181
|
+
}
|
|
182
|
+
let str = value.trim();
|
|
183
|
+
let isNegative = false;
|
|
184
|
+
const parenMatch = str.match(/^\(\s*(.+?)\s*\)$/);
|
|
185
|
+
if (parenMatch) {
|
|
186
|
+
isNegative = true;
|
|
187
|
+
str = parenMatch[1];
|
|
188
|
+
} else if (str.startsWith("-")) {
|
|
189
|
+
isNegative = true;
|
|
190
|
+
str = str.substring(1).trim();
|
|
191
|
+
}
|
|
192
|
+
let multiplier = 1;
|
|
193
|
+
if (/[kK](?:€|eur)?\b/i.test(str)) {
|
|
194
|
+
multiplier = 1e3;
|
|
195
|
+
str = str.replace(/[kK](?:€|eur)?\b/gi, "").trim();
|
|
196
|
+
} else if (/[mM](?:€|eur)?\b/i.test(str)) {
|
|
197
|
+
multiplier = 1e6;
|
|
198
|
+
str = str.replace(/[mM](?:€|eur)?\b/gi, "").trim();
|
|
199
|
+
}
|
|
200
|
+
str = str.replace(/[€$£]/g, "").replace(/\s+/g, "").replace(/\u00A0/g, "").trim();
|
|
201
|
+
str = str.replace(",", ".");
|
|
202
|
+
const parsed = parseFloat(str);
|
|
203
|
+
if (isNaN(parsed)) {
|
|
204
|
+
return 0;
|
|
205
|
+
}
|
|
206
|
+
const finalValue = parsed * multiplier;
|
|
207
|
+
return isNegative ? -Math.abs(finalValue) : finalValue;
|
|
208
|
+
}
|
|
209
|
+
cleanJSON(rawText) {
|
|
210
|
+
let result = rawText;
|
|
211
|
+
result = result.replace(
|
|
212
|
+
/(:\s*)\(\s*([0-9][0-9\s.,]*(?:k€|k|keur|m€|m|meur|€)?)\s*\)/gi,
|
|
213
|
+
(_, prefix, amountStr) => {
|
|
214
|
+
const num = this.cleanNumber(`(${amountStr})`);
|
|
215
|
+
return `${prefix}${num}`;
|
|
216
|
+
}
|
|
217
|
+
);
|
|
218
|
+
result = result.replace(
|
|
219
|
+
/(:\s*)"\s*\(\s*([0-9][0-9\s.,]*(?:k€|k|keur|m€|m|meur|€)?)\s*\)\s*"/gi,
|
|
220
|
+
(_, prefix, amountStr) => {
|
|
221
|
+
const num = this.cleanNumber(`(${amountStr})`);
|
|
222
|
+
return `${prefix}${num}`;
|
|
223
|
+
}
|
|
224
|
+
);
|
|
225
|
+
result = result.replace(
|
|
226
|
+
/(:\s*)"\s*([+-]?[0-9][0-9\s.,]*(?:k€|k|keur|m€|m|meur|€))\s*"/gi,
|
|
227
|
+
(_, prefix, amountStr) => {
|
|
228
|
+
const num = this.cleanNumber(amountStr);
|
|
229
|
+
return `${prefix}${num}`;
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
result = result.replace(
|
|
233
|
+
/(:\s*)"\s*([+-]?(?:[0-9]{1,3}(?:\s+[0-9]{3})+(?:,[0-9]+)?|[0-9]+,[0-9]+))\s*"/g,
|
|
234
|
+
(_, prefix, amountStr) => {
|
|
235
|
+
const num = this.cleanNumber(amountStr);
|
|
236
|
+
return `${prefix}${num}`;
|
|
237
|
+
}
|
|
238
|
+
);
|
|
239
|
+
return result;
|
|
240
|
+
}
|
|
241
|
+
detect(text) {
|
|
242
|
+
const lower = text.toLowerCase();
|
|
243
|
+
const frPatterns = [
|
|
244
|
+
/\bcerfa\b/i,
|
|
245
|
+
/\bliasse\s+fiscale\b/i,
|
|
246
|
+
/\bbilan\s+(actif|passif)\b/i,
|
|
247
|
+
/\bcompte\s+de\s+r[ée]sultat\b/i,
|
|
248
|
+
/\bplan\s+comptable\s+g[ée]n[ée]ral\b/i,
|
|
249
|
+
/\bpcg\b/i,
|
|
250
|
+
/\bsiren\b/i,
|
|
251
|
+
/\bsiret\b/i,
|
|
252
|
+
/\b[0-9\s.,]+(?:k€|m€)\b/i,
|
|
253
|
+
/\beur\b/i,
|
|
254
|
+
/€/
|
|
255
|
+
];
|
|
256
|
+
return frPatterns.some((pattern) => pattern.test(lower));
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
// src/finance/strategies/us-gaap.strategy.ts
|
|
261
|
+
var UsGAAPStrategy = class {
|
|
262
|
+
jurisdictionCode = "US";
|
|
263
|
+
standard = "US_GAAP";
|
|
264
|
+
defaultCurrency = "USD";
|
|
265
|
+
cleanNumber(value) {
|
|
266
|
+
if (typeof value === "number") {
|
|
267
|
+
return Number.isFinite(value) ? value : 0;
|
|
268
|
+
}
|
|
269
|
+
if (!value || typeof value !== "string") {
|
|
270
|
+
return 0;
|
|
271
|
+
}
|
|
272
|
+
let str = value.trim();
|
|
273
|
+
let isNegative = false;
|
|
274
|
+
const parenMatch = str.match(/^[(\[]\s*(.+?)\s*[)\]]$/);
|
|
275
|
+
if (parenMatch) {
|
|
276
|
+
isNegative = true;
|
|
277
|
+
str = parenMatch[1];
|
|
278
|
+
} else if (str.startsWith("-")) {
|
|
279
|
+
isNegative = true;
|
|
280
|
+
str = str.substring(1).trim();
|
|
281
|
+
}
|
|
282
|
+
let multiplier = 1;
|
|
283
|
+
if (/[kK]\b/.test(str)) {
|
|
284
|
+
multiplier = 1e3;
|
|
285
|
+
str = str.replace(/[kK]\b/g, "").trim();
|
|
286
|
+
} else if (/[mM]\b/.test(str)) {
|
|
287
|
+
multiplier = 1e6;
|
|
288
|
+
str = str.replace(/[mM]\b/g, "").trim();
|
|
289
|
+
} else if (/[bB]\b/.test(str)) {
|
|
290
|
+
multiplier = 1e9;
|
|
291
|
+
str = str.replace(/[bB]\b/g, "").trim();
|
|
292
|
+
}
|
|
293
|
+
str = str.replace(/[$]/g, "").replace(/\busd\b/gi, "").replace(/,/g, "").replace(/\s+/g, "").trim();
|
|
294
|
+
const parsed = parseFloat(str);
|
|
295
|
+
if (isNaN(parsed)) {
|
|
296
|
+
return 0;
|
|
297
|
+
}
|
|
298
|
+
const finalVal = parsed * multiplier;
|
|
299
|
+
return isNegative ? -Math.abs(finalVal) : finalVal;
|
|
300
|
+
}
|
|
301
|
+
cleanJSON(rawText) {
|
|
302
|
+
let result = rawText;
|
|
303
|
+
result = result.replace(
|
|
304
|
+
/(:\s*)[(\[]\s*([0-9][0-9,.]*(?:k|m|b|\$)?)\s*[)\]]/gi,
|
|
305
|
+
(_, prefix, amountStr) => {
|
|
306
|
+
const num = this.cleanNumber(`(${amountStr})`);
|
|
307
|
+
return `${prefix}${num}`;
|
|
308
|
+
}
|
|
309
|
+
);
|
|
310
|
+
result = result.replace(
|
|
311
|
+
/(:\s*)"\s*[(\[]\s*([0-9][0-9,.]*(?:k|m|b|\$)?)\s*[)\]]\s*"/gi,
|
|
312
|
+
(_, prefix, amountStr) => {
|
|
313
|
+
const num = this.cleanNumber(`(${amountStr})`);
|
|
314
|
+
return `${prefix}${num}`;
|
|
315
|
+
}
|
|
316
|
+
);
|
|
317
|
+
result = result.replace(
|
|
318
|
+
/(:\s*)"\s*([+-]?\$?\s*[0-9][0-9,.]*\s*(?:k|m|b|\$)?)\s*"/gi,
|
|
319
|
+
(_, prefix, amountStr) => {
|
|
320
|
+
if (!amountStr.includes("$") && !/[kmb]/i.test(amountStr)) {
|
|
321
|
+
return `${prefix}"${amountStr}"`;
|
|
322
|
+
}
|
|
323
|
+
const num = this.cleanNumber(amountStr);
|
|
324
|
+
return `${prefix}${num}`;
|
|
325
|
+
}
|
|
326
|
+
);
|
|
327
|
+
return result;
|
|
328
|
+
}
|
|
329
|
+
detect(text) {
|
|
330
|
+
const lower = text.toLowerCase();
|
|
331
|
+
const usPatterns = [
|
|
332
|
+
/\b10-[kq]\b/i,
|
|
333
|
+
/\bsec\s+filing\b/i,
|
|
334
|
+
/\bus[\s_-]?gaap\b/i,
|
|
335
|
+
/\bbalance\s+sheet\b/i,
|
|
336
|
+
/\bincome\s+statement\b/i,
|
|
337
|
+
/\bstatement\s+of\s+cash\s+flows\b/i,
|
|
338
|
+
/\$|usd/i
|
|
339
|
+
];
|
|
340
|
+
return usPatterns.some((pattern) => pattern.test(lower));
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
// src/finance/strategies/uk-ifrs.strategy.ts
|
|
345
|
+
var UkIFRSStrategy = class {
|
|
346
|
+
jurisdictionCode = "UK";
|
|
347
|
+
standard = "IFRS";
|
|
348
|
+
defaultCurrency = "GBP";
|
|
349
|
+
cleanNumber(value) {
|
|
350
|
+
if (typeof value === "number") {
|
|
351
|
+
return Number.isFinite(value) ? value : 0;
|
|
352
|
+
}
|
|
353
|
+
if (!value || typeof value !== "string") {
|
|
354
|
+
return 0;
|
|
355
|
+
}
|
|
356
|
+
let str = value.trim();
|
|
357
|
+
let isNegative = false;
|
|
358
|
+
const parenMatch = str.match(/^\(\s*(.+?)\s*\)$/);
|
|
359
|
+
if (parenMatch) {
|
|
360
|
+
isNegative = true;
|
|
361
|
+
str = parenMatch[1];
|
|
362
|
+
} else if (str.startsWith("-")) {
|
|
363
|
+
isNegative = true;
|
|
364
|
+
str = str.substring(1).trim();
|
|
365
|
+
}
|
|
366
|
+
let multiplier = 1;
|
|
367
|
+
if (/[kK]\b/.test(str)) {
|
|
368
|
+
multiplier = 1e3;
|
|
369
|
+
str = str.replace(/[kK]\b/g, "").trim();
|
|
370
|
+
} else if (/[mM]\b/.test(str)) {
|
|
371
|
+
multiplier = 1e6;
|
|
372
|
+
str = str.replace(/[mM]\b/g, "").trim();
|
|
373
|
+
}
|
|
374
|
+
str = str.replace(/[£]/g, "").replace(/\bgbp\b/gi, "").replace(/,/g, "").replace(/\s+/g, "").trim();
|
|
375
|
+
const parsed = parseFloat(str);
|
|
376
|
+
if (isNaN(parsed)) {
|
|
377
|
+
return 0;
|
|
378
|
+
}
|
|
379
|
+
const finalVal = parsed * multiplier;
|
|
380
|
+
return isNegative ? -Math.abs(finalVal) : finalVal;
|
|
381
|
+
}
|
|
382
|
+
cleanJSON(rawText) {
|
|
383
|
+
let result = rawText;
|
|
384
|
+
result = result.replace(
|
|
385
|
+
/(:\s*)\(\s*([0-9][0-9,.]*(?:k|m|£)?)\s*\)/gi,
|
|
386
|
+
(_, prefix, amountStr) => {
|
|
387
|
+
const num = this.cleanNumber(`(${amountStr})`);
|
|
388
|
+
return `${prefix}${num}`;
|
|
389
|
+
}
|
|
390
|
+
);
|
|
391
|
+
result = result.replace(
|
|
392
|
+
/(:\s*)"\s*\(\s*([0-9][0-9,.]*(?:k|m|£)?)\s*\)\s*"/gi,
|
|
393
|
+
(_, prefix, amountStr) => {
|
|
394
|
+
const num = this.cleanNumber(`(${amountStr})`);
|
|
395
|
+
return `${prefix}${num}`;
|
|
396
|
+
}
|
|
397
|
+
);
|
|
398
|
+
result = result.replace(
|
|
399
|
+
/(:\s*)"\s*([+-]?£\s*[0-9][0-9,.]*\s*(?:k|m)?)\s*"/gi,
|
|
400
|
+
(_, prefix, amountStr) => {
|
|
401
|
+
const num = this.cleanNumber(amountStr);
|
|
402
|
+
return `${prefix}${num}`;
|
|
403
|
+
}
|
|
404
|
+
);
|
|
405
|
+
return result;
|
|
406
|
+
}
|
|
407
|
+
detect(text) {
|
|
408
|
+
const lower = text.toLowerCase();
|
|
409
|
+
const ukPatterns = [
|
|
410
|
+
/\bcompanies\s+house\b/i,
|
|
411
|
+
/\bfrs\s*102\b/i,
|
|
412
|
+
/\bhmrc\b/i,
|
|
413
|
+
/\bprofit\s+and\s+loss\b/i,
|
|
414
|
+
/£|\bgbp\b/i
|
|
415
|
+
];
|
|
416
|
+
return ukPatterns.some((pattern) => pattern.test(lower));
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
// src/finance/strategies/swiss-co.strategy.ts
|
|
421
|
+
var SwissCOStrategy = class {
|
|
422
|
+
jurisdictionCode = "CH";
|
|
423
|
+
standard = "SWISS_CO";
|
|
424
|
+
defaultCurrency = "CHF";
|
|
425
|
+
cleanNumber(value) {
|
|
426
|
+
if (typeof value === "number") {
|
|
427
|
+
return Number.isFinite(value) ? value : 0;
|
|
428
|
+
}
|
|
429
|
+
if (!value || typeof value !== "string") {
|
|
430
|
+
return 0;
|
|
431
|
+
}
|
|
432
|
+
let str = value.trim();
|
|
433
|
+
let isNegative = false;
|
|
434
|
+
const parenMatch = str.match(/^\(\s*(.+?)\s*\)$/);
|
|
435
|
+
if (parenMatch) {
|
|
436
|
+
isNegative = true;
|
|
437
|
+
str = parenMatch[1];
|
|
438
|
+
} else if (str.startsWith("-")) {
|
|
439
|
+
isNegative = true;
|
|
440
|
+
str = str.substring(1).trim();
|
|
441
|
+
}
|
|
442
|
+
let multiplier = 1;
|
|
443
|
+
if (/[kK]\b/.test(str)) {
|
|
444
|
+
multiplier = 1e3;
|
|
445
|
+
str = str.replace(/[kK]\b/g, "").trim();
|
|
446
|
+
} else if (/[mM]\b/.test(str)) {
|
|
447
|
+
multiplier = 1e6;
|
|
448
|
+
str = str.replace(/[mM]\b/g, "").trim();
|
|
449
|
+
}
|
|
450
|
+
str = str.replace(/\bchf\b/gi, "").replace(/['’]/g, "").replace(/\s+/g, "").trim();
|
|
451
|
+
str = str.replace(",", ".");
|
|
452
|
+
const parsed = parseFloat(str);
|
|
453
|
+
if (isNaN(parsed)) {
|
|
454
|
+
return 0;
|
|
455
|
+
}
|
|
456
|
+
const finalVal = parsed * multiplier;
|
|
457
|
+
return isNegative ? -Math.abs(finalVal) : finalVal;
|
|
458
|
+
}
|
|
459
|
+
cleanJSON(rawText) {
|
|
460
|
+
let result = rawText;
|
|
461
|
+
result = result.replace(
|
|
462
|
+
/(:\s*)\(\s*([0-9][0-9'’.,\s]*(?:k|m|chf)?)\s*\)/gi,
|
|
463
|
+
(_, prefix, amountStr) => {
|
|
464
|
+
const num = this.cleanNumber(`(${amountStr})`);
|
|
465
|
+
return `${prefix}${num}`;
|
|
466
|
+
}
|
|
467
|
+
);
|
|
468
|
+
result = result.replace(
|
|
469
|
+
/(:\s*)"\s*\(\s*([0-9][0-9'’.,\s]*(?:k|m|chf)?)\s*\)\s*"/gi,
|
|
470
|
+
(_, prefix, amountStr) => {
|
|
471
|
+
const num = this.cleanNumber(`(${amountStr})`);
|
|
472
|
+
return `${prefix}${num}`;
|
|
473
|
+
}
|
|
474
|
+
);
|
|
475
|
+
result = result.replace(
|
|
476
|
+
/(:\s*)"\s*([+-]?[0-9][0-9'’.,\s]*(?:k|m|chf))\s*"/gi,
|
|
477
|
+
(_, prefix, amountStr) => {
|
|
478
|
+
const num = this.cleanNumber(amountStr);
|
|
479
|
+
return `${prefix}${num}`;
|
|
480
|
+
}
|
|
481
|
+
);
|
|
482
|
+
return result;
|
|
483
|
+
}
|
|
484
|
+
detect(text) {
|
|
485
|
+
const lower = text.toLowerCase();
|
|
486
|
+
const chPatterns = [
|
|
487
|
+
/\bcode\s+des\s+obligations\b/i,
|
|
488
|
+
/\bart\.?\s*725\b/i,
|
|
489
|
+
/\bche-[0-9]{3}\.[0-9]{3}\.[0-9]{3}\b/i,
|
|
490
|
+
/\bchf\b/i,
|
|
491
|
+
/\b[0-9]{1,3}(?:'[0-9]{3})+\b/
|
|
492
|
+
];
|
|
493
|
+
return chPatterns.some((pattern) => pattern.test(lower));
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
// src/finance/strategies/international.strategy.ts
|
|
498
|
+
var InternationalAccountingStrategy = class {
|
|
499
|
+
jurisdictionCode = "INTERNATIONAL";
|
|
500
|
+
standard = "OTHER";
|
|
501
|
+
defaultCurrency = "EUR";
|
|
502
|
+
cleanNumber(value) {
|
|
503
|
+
if (typeof value === "number") {
|
|
504
|
+
return Number.isFinite(value) ? value : 0;
|
|
505
|
+
}
|
|
506
|
+
if (!value || typeof value !== "string") {
|
|
507
|
+
return 0;
|
|
508
|
+
}
|
|
509
|
+
let str = value.trim();
|
|
510
|
+
let isNegative = false;
|
|
511
|
+
const parenMatch = str.match(/^[(\[]\s*(.+?)\s*[)\]]$/);
|
|
512
|
+
if (parenMatch) {
|
|
513
|
+
isNegative = true;
|
|
514
|
+
str = parenMatch[1];
|
|
515
|
+
} else if (str.startsWith("-")) {
|
|
516
|
+
isNegative = true;
|
|
517
|
+
str = str.substring(1).trim();
|
|
518
|
+
}
|
|
519
|
+
let multiplier = 1;
|
|
520
|
+
if (/[kK]\b/.test(str)) {
|
|
521
|
+
multiplier = 1e3;
|
|
522
|
+
str = str.replace(/[kK]\b/g, "").trim();
|
|
523
|
+
} else if (/[mM]\b/.test(str)) {
|
|
524
|
+
multiplier = 1e6;
|
|
525
|
+
str = str.replace(/[mM]\b/g, "").trim();
|
|
526
|
+
}
|
|
527
|
+
str = str.replace(/[€$£]/g, "").replace(/\b(?:eur|usd|gbp|chf)\b/gi, "").trim();
|
|
528
|
+
if (str.includes(",") && str.includes(".")) {
|
|
529
|
+
if (str.lastIndexOf(",") > str.lastIndexOf(".")) {
|
|
530
|
+
str = str.replace(/\./g, "").replace(",", ".");
|
|
531
|
+
} else {
|
|
532
|
+
str = str.replace(/,/g, "");
|
|
533
|
+
}
|
|
534
|
+
} else if (str.includes(",")) {
|
|
535
|
+
if (/,\d{1,2}$/.test(str)) {
|
|
536
|
+
str = str.replace(",", ".");
|
|
537
|
+
} else {
|
|
538
|
+
str = str.replace(",", "");
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
str = str.replace(/\s+/g, "");
|
|
542
|
+
const parsed = parseFloat(str);
|
|
543
|
+
if (isNaN(parsed)) {
|
|
544
|
+
return 0;
|
|
545
|
+
}
|
|
546
|
+
const finalVal = parsed * multiplier;
|
|
547
|
+
return isNegative ? -Math.abs(finalVal) : finalVal;
|
|
548
|
+
}
|
|
549
|
+
cleanJSON(rawText) {
|
|
550
|
+
let result = rawText;
|
|
551
|
+
result = result.replace(
|
|
552
|
+
/(:\s*)[(\[]\s*([0-9][0-9,.\s]*(?:k|m|€|\$|£)?)\s*[)\]]/gi,
|
|
553
|
+
(_, prefix, amountStr) => {
|
|
554
|
+
const num = this.cleanNumber(`(${amountStr})`);
|
|
555
|
+
return `${prefix}${num}`;
|
|
556
|
+
}
|
|
557
|
+
);
|
|
558
|
+
result = result.replace(
|
|
559
|
+
/(:\s*)"\s*[(\[]\s*([0-9][0-9,.\s]*(?:k|m|€|\$|£)?)\s*[)\]]\s*"/gi,
|
|
560
|
+
(_, prefix, amountStr) => {
|
|
561
|
+
const num = this.cleanNumber(`(${amountStr})`);
|
|
562
|
+
return `${prefix}${num}`;
|
|
563
|
+
}
|
|
564
|
+
);
|
|
565
|
+
return result;
|
|
566
|
+
}
|
|
567
|
+
detect(_text) {
|
|
568
|
+
return true;
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
// src/finance/factory.ts
|
|
573
|
+
var AccountingFactory = class {
|
|
574
|
+
static strategies = /* @__PURE__ */ new Map();
|
|
575
|
+
static detectionOrder = [];
|
|
576
|
+
static {
|
|
577
|
+
this.initDefaults();
|
|
578
|
+
}
|
|
579
|
+
static initDefaults() {
|
|
580
|
+
const fr = new FrenchPCGStrategy();
|
|
581
|
+
const us = new UsGAAPStrategy();
|
|
582
|
+
const uk = new UkIFRSStrategy();
|
|
583
|
+
const ch = new SwissCOStrategy();
|
|
584
|
+
const intl = new InternationalAccountingStrategy();
|
|
585
|
+
this.registerStrategy(ch);
|
|
586
|
+
this.registerStrategy(uk);
|
|
587
|
+
this.registerStrategy(us);
|
|
588
|
+
this.registerStrategy(fr);
|
|
589
|
+
this.registerStrategy(intl);
|
|
590
|
+
}
|
|
591
|
+
static registerStrategy(strategy) {
|
|
592
|
+
this.strategies.set(strategy.jurisdictionCode, strategy);
|
|
593
|
+
this.detectionOrder = [
|
|
594
|
+
strategy,
|
|
595
|
+
...this.detectionOrder.filter((s) => s.jurisdictionCode !== strategy.jurisdictionCode)
|
|
596
|
+
];
|
|
597
|
+
}
|
|
598
|
+
static getStrategy(code) {
|
|
599
|
+
if (!code) {
|
|
600
|
+
return this.strategies.get("FR") || new FrenchPCGStrategy();
|
|
601
|
+
}
|
|
602
|
+
const strategy = this.strategies.get(code);
|
|
603
|
+
if (!strategy) {
|
|
604
|
+
return this.strategies.get("FR") || new FrenchPCGStrategy();
|
|
605
|
+
}
|
|
606
|
+
return strategy;
|
|
607
|
+
}
|
|
608
|
+
static detectStrategy(documentText) {
|
|
609
|
+
for (const strategy of this.detectionOrder) {
|
|
610
|
+
if (strategy.jurisdictionCode !== "INTERNATIONAL" && strategy.detect(documentText)) {
|
|
611
|
+
return strategy;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return this.getStrategy("FR");
|
|
615
|
+
}
|
|
616
|
+
static resetDefaults() {
|
|
617
|
+
this.strategies.clear();
|
|
618
|
+
this.detectionOrder = [];
|
|
619
|
+
this.initDefaults();
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
// src/finance/normalizer.ts
|
|
624
|
+
function cleanFinancialJSON(rawText, options) {
|
|
625
|
+
if (!rawText) {
|
|
626
|
+
return "";
|
|
627
|
+
}
|
|
628
|
+
const jsonBlock = extractAndCleanJSON(rawText);
|
|
629
|
+
const strategy = options?.jurisdiction ? AccountingFactory.getStrategy(options.jurisdiction) : options?.autoDetect !== false ? AccountingFactory.detectStrategy(rawText) : AccountingFactory.getStrategy("FR");
|
|
630
|
+
return strategy.cleanJSON(jsonBlock);
|
|
631
|
+
}
|
|
632
|
+
|
|
141
633
|
// src/response-validator.ts
|
|
142
634
|
function extractAndCleanJSON(rawText) {
|
|
143
635
|
let cleaned = rawText.trim();
|
|
@@ -164,12 +656,17 @@ function extractAndCleanJSON(rawText) {
|
|
|
164
656
|
}
|
|
165
657
|
return cleaned;
|
|
166
658
|
}
|
|
167
|
-
function validateWithZod(rawText, schema) {
|
|
168
|
-
|
|
659
|
+
function validateWithZod(rawText, schema, options) {
|
|
660
|
+
let jsonString = extractAndCleanJSON(rawText);
|
|
661
|
+
if (options?.normalizer) {
|
|
662
|
+
jsonString = options.normalizer(jsonString);
|
|
663
|
+
} else if (options?.financialNormalizer) {
|
|
664
|
+
jsonString = cleanFinancialJSON(jsonString, { jurisdiction: options.jurisdiction });
|
|
665
|
+
}
|
|
169
666
|
let parsed;
|
|
170
667
|
try {
|
|
171
668
|
parsed = JSON.parse(jsonString);
|
|
172
|
-
} catch (
|
|
669
|
+
} catch (_error) {
|
|
173
670
|
const sanitized = jsonString.replace(/,\s*([}\]])/g, "$1").replace(/'/g, '"');
|
|
174
671
|
parsed = JSON.parse(sanitized);
|
|
175
672
|
}
|
|
@@ -248,14 +745,15 @@ var AvantGateControlLayer = class {
|
|
|
248
745
|
attempts: 1
|
|
249
746
|
};
|
|
250
747
|
}
|
|
251
|
-
async executeProviderPipeline(messages, query, temperature) {
|
|
748
|
+
async executeProviderPipeline(messages, query, temperature, modelOverride) {
|
|
252
749
|
const chain = this.getProviderChain();
|
|
253
750
|
let lastError;
|
|
254
751
|
for (let index = 0; index < chain.length; index++) {
|
|
255
752
|
const providerConfig = chain[index];
|
|
753
|
+
const targetModel = index === 0 && modelOverride ? modelOverride : providerConfig.model;
|
|
256
754
|
try {
|
|
257
755
|
const response = await providerConfig.client.complete({
|
|
258
|
-
model:
|
|
756
|
+
model: targetModel,
|
|
259
757
|
messages,
|
|
260
758
|
temperature: temperature ?? 0.2
|
|
261
759
|
});
|
|
@@ -263,7 +761,7 @@ var AvantGateControlLayer = class {
|
|
|
263
761
|
return {
|
|
264
762
|
responseText: response.text,
|
|
265
763
|
usage,
|
|
266
|
-
modelUsed:
|
|
764
|
+
modelUsed: targetModel,
|
|
267
765
|
failoverOccurred: index > 0,
|
|
268
766
|
attempts: index + 1
|
|
269
767
|
};
|
|
@@ -331,7 +829,13 @@ var AvantGateControlLayer = class {
|
|
|
331
829
|
temperature: options.temperature ?? 0.1,
|
|
332
830
|
providerOverride: options.providerOverride
|
|
333
831
|
});
|
|
334
|
-
const
|
|
832
|
+
const isFinancial = Boolean(
|
|
833
|
+
this.config.features?.finance?.enableFrenchAccounting || this.config.features?.finance
|
|
834
|
+
);
|
|
835
|
+
const parsedData = validateWithZod(rawResult.text, options.schema, {
|
|
836
|
+
financialNormalizer: isFinancial,
|
|
837
|
+
jurisdiction: this.config.features?.finance?.jurisdiction
|
|
838
|
+
});
|
|
335
839
|
return {
|
|
336
840
|
data: parsedData,
|
|
337
841
|
rawText: rawResult.text,
|
|
@@ -341,15 +845,427 @@ var AvantGateControlLayer = class {
|
|
|
341
845
|
failoverOccurred: rawResult.failoverOccurred
|
|
342
846
|
};
|
|
343
847
|
}
|
|
848
|
+
/**
|
|
849
|
+
* Méthode unifiée de premier niveau pour l'extraction structurée sans code boilerplate.
|
|
850
|
+
* Gère le failover multi-fournisseurs, les retries, la validation Zod et la normalisation financière.
|
|
851
|
+
*/
|
|
852
|
+
async generateStructuredOutput(options) {
|
|
853
|
+
const maxRetries = options.maxRetries ?? this.config.retryOptions?.maxRetries ?? 2;
|
|
854
|
+
const modelToUse = options.model ?? this.config.primary.model;
|
|
855
|
+
const processedMessages = options.messages.map((msg) => {
|
|
856
|
+
if (msg.role === "user") {
|
|
857
|
+
return { ...msg, content: this.applySecurityGuards(msg.content) };
|
|
858
|
+
}
|
|
859
|
+
return msg;
|
|
860
|
+
});
|
|
861
|
+
let lastError;
|
|
862
|
+
let accumulatedPromptTokens = 0;
|
|
863
|
+
let accumulatedCompletionTokens = 0;
|
|
864
|
+
let failoverOccurred = false;
|
|
865
|
+
let modelUsed = modelToUse;
|
|
866
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
867
|
+
try {
|
|
868
|
+
let responseText = "";
|
|
869
|
+
let attemptPromptTokens = 0;
|
|
870
|
+
let attemptCompletionTokens = 0;
|
|
871
|
+
if (options.providerOverride) {
|
|
872
|
+
const res = await options.providerOverride.complete({
|
|
873
|
+
model: modelToUse,
|
|
874
|
+
messages: processedMessages,
|
|
875
|
+
temperature: options.temperature ?? 0.1
|
|
876
|
+
});
|
|
877
|
+
responseText = res.text;
|
|
878
|
+
const usage = this.resolveTokens(res.usage, JSON.stringify(processedMessages), responseText);
|
|
879
|
+
attemptPromptTokens = usage.promptTokens;
|
|
880
|
+
attemptCompletionTokens = usage.completionTokens;
|
|
881
|
+
modelUsed = modelToUse;
|
|
882
|
+
} 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;
|
|
887
|
+
} else {
|
|
888
|
+
const output = await this.executeProviderPipeline(
|
|
889
|
+
processedMessages,
|
|
890
|
+
JSON.stringify(processedMessages),
|
|
891
|
+
options.temperature ?? 0.1,
|
|
892
|
+
options.model
|
|
893
|
+
);
|
|
894
|
+
responseText = output.responseText;
|
|
895
|
+
attemptPromptTokens = output.usage.promptTokens;
|
|
896
|
+
attemptCompletionTokens = output.usage.completionTokens;
|
|
897
|
+
modelUsed = output.modelUsed;
|
|
898
|
+
failoverOccurred = output.failoverOccurred;
|
|
899
|
+
}
|
|
900
|
+
accumulatedPromptTokens += attemptPromptTokens;
|
|
901
|
+
accumulatedCompletionTokens += attemptCompletionTokens;
|
|
902
|
+
const isFinancial = options.financialNormalizer ?? Boolean(
|
|
903
|
+
this.config.features?.finance?.enableFrenchAccounting || this.config.features?.finance
|
|
904
|
+
);
|
|
905
|
+
const parsedData = validateWithZod(responseText, options.schema, {
|
|
906
|
+
financialNormalizer: isFinancial,
|
|
907
|
+
jurisdiction: this.config.features?.finance?.jurisdiction
|
|
908
|
+
});
|
|
909
|
+
const totalTokens = accumulatedPromptTokens + accumulatedCompletionTokens;
|
|
910
|
+
const costUSD = calculateCostUSD(modelUsed, accumulatedPromptTokens, accumulatedCompletionTokens);
|
|
911
|
+
const result = {
|
|
912
|
+
data: parsedData,
|
|
913
|
+
rawText: responseText,
|
|
914
|
+
tokens: {
|
|
915
|
+
prompt: accumulatedPromptTokens,
|
|
916
|
+
completion: accumulatedCompletionTokens,
|
|
917
|
+
total: totalTokens
|
|
918
|
+
},
|
|
919
|
+
costUSD,
|
|
920
|
+
modelUsed,
|
|
921
|
+
failoverOccurred
|
|
922
|
+
};
|
|
923
|
+
await this.notifyAuditSink({
|
|
924
|
+
text: responseText,
|
|
925
|
+
tokens: result.tokens,
|
|
926
|
+
costUSD: result.costUSD,
|
|
927
|
+
modelUsed: result.modelUsed,
|
|
928
|
+
failoverOccurred: result.failoverOccurred,
|
|
929
|
+
attempts: attempt + 1
|
|
930
|
+
});
|
|
931
|
+
return result;
|
|
932
|
+
} catch (err) {
|
|
933
|
+
lastError = err;
|
|
934
|
+
if (attempt < maxRetries) {
|
|
935
|
+
const delay = (this.config.retryOptions?.initialDelayMs ?? 200) * Math.pow(this.config.retryOptions?.backoffFactor ?? 1.5, attempt);
|
|
936
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
throw lastError;
|
|
941
|
+
}
|
|
344
942
|
};
|
|
345
943
|
function createLLMControlLayer(config) {
|
|
346
944
|
return new AvantGateControlLayer(config);
|
|
347
945
|
}
|
|
348
946
|
var createAvantGate = createLLMControlLayer;
|
|
947
|
+
|
|
948
|
+
// src/prompts/prompt-template.ts
|
|
949
|
+
var DEFAULT_JAILBREAK_PATTERNS = [
|
|
950
|
+
/ignore\s+(all\s+)?(previous|prior)\s+(instructions|rules)/i,
|
|
951
|
+
/disregard\s+(all\s+)?(previous|prior)\s+(instructions|rules)/i,
|
|
952
|
+
/system\s+override/i,
|
|
953
|
+
/you\s+are\s+now\s+(unrestricted|DAN|jailbroken)/i,
|
|
954
|
+
/<\s*\|\s*im_start\s*\|>/i,
|
|
955
|
+
/\[SYSTEM_PROMPT\]/i
|
|
956
|
+
];
|
|
957
|
+
var PromptTemplate = class {
|
|
958
|
+
id;
|
|
959
|
+
version;
|
|
960
|
+
label;
|
|
961
|
+
description;
|
|
962
|
+
template;
|
|
963
|
+
inputSchema;
|
|
964
|
+
blockPatterns;
|
|
965
|
+
antiInjectionEnabled;
|
|
966
|
+
customSanitizer;
|
|
967
|
+
constructor(options) {
|
|
968
|
+
this.id = options.id;
|
|
969
|
+
this.version = options.version;
|
|
970
|
+
this.label = options.label || "production";
|
|
971
|
+
this.description = options.description;
|
|
972
|
+
this.template = options.template;
|
|
973
|
+
this.inputSchema = options.inputSchema;
|
|
974
|
+
this.antiInjectionEnabled = options.antiInjection?.enabled !== false;
|
|
975
|
+
this.blockPatterns = options.antiInjection?.blockPatterns || DEFAULT_JAILBREAK_PATTERNS;
|
|
976
|
+
this.customSanitizer = options.antiInjection?.sanitizer;
|
|
977
|
+
}
|
|
978
|
+
validateUserInput(variables) {
|
|
979
|
+
const threats = [];
|
|
980
|
+
if (this.inputSchema) {
|
|
981
|
+
const parsed = this.inputSchema.safeParse(variables);
|
|
982
|
+
if (!parsed.success) {
|
|
983
|
+
return {
|
|
984
|
+
isValid: false,
|
|
985
|
+
threats: parsed.error.issues.map(
|
|
986
|
+
(issue) => `${issue.path.join(".")}: ${issue.message}`
|
|
987
|
+
)
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
if (this.antiInjectionEnabled) {
|
|
992
|
+
for (const [key, val] of Object.entries(variables)) {
|
|
993
|
+
if (typeof val === "string") {
|
|
994
|
+
for (const pattern of this.blockPatterns) {
|
|
995
|
+
if (pattern.test(val)) {
|
|
996
|
+
threats.push(
|
|
997
|
+
`Potential prompt injection in variable "${key}" matching pattern ${pattern}`
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
return { isValid: threats.length === 0, threats };
|
|
1005
|
+
}
|
|
1006
|
+
format(variables) {
|
|
1007
|
+
const check = this.validateUserInput(variables);
|
|
1008
|
+
if (!check.isValid) {
|
|
1009
|
+
throw new Error(
|
|
1010
|
+
`[PromptTemplate:${this.id}] Validation failed: ${check.threats.join(", ")}`
|
|
1011
|
+
);
|
|
1012
|
+
}
|
|
1013
|
+
const interpolated = this.template.replace(/\{\{\s*([\w.-]+)\s*\}\}/g, (_, key) => {
|
|
1014
|
+
let value = variables[key];
|
|
1015
|
+
if (value === void 0 || value === null) {
|
|
1016
|
+
return "";
|
|
1017
|
+
}
|
|
1018
|
+
let stringValue = typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
1019
|
+
if (this.customSanitizer) {
|
|
1020
|
+
stringValue = this.customSanitizer(stringValue);
|
|
1021
|
+
}
|
|
1022
|
+
return stringValue;
|
|
1023
|
+
});
|
|
1024
|
+
return interpolated.split("\n").filter((line, index, arr) => line.trim() !== "" || index > 0 && arr[index - 1].trim() !== "").join("\n");
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
1027
|
+
|
|
1028
|
+
// src/prompts/prompt-builder.ts
|
|
1029
|
+
var import_zod = require("zod");
|
|
1030
|
+
var PromptBuilder = class {
|
|
1031
|
+
personaSlot = "";
|
|
1032
|
+
rulesSlot = [];
|
|
1033
|
+
retryHintSlot = "";
|
|
1034
|
+
fewShotSlot = [];
|
|
1035
|
+
pinnedFactsSlot = "";
|
|
1036
|
+
contextSlot = "";
|
|
1037
|
+
userPayloadSlot = "";
|
|
1038
|
+
schemaContractText = "";
|
|
1039
|
+
constructor(templateOrId) {
|
|
1040
|
+
if (templateOrId instanceof PromptTemplate) {
|
|
1041
|
+
this.personaSlot = templateOrId.template;
|
|
1042
|
+
} else if (typeof templateOrId === "string") {
|
|
1043
|
+
this.personaSlot = templateOrId.trim();
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
withPersona(persona) {
|
|
1047
|
+
this.personaSlot = persona.trim();
|
|
1048
|
+
return this;
|
|
1049
|
+
}
|
|
1050
|
+
withRules(rules) {
|
|
1051
|
+
if (Array.isArray(rules)) {
|
|
1052
|
+
this.rulesSlot.push(...rules.map((rule) => rule.trim()).filter(Boolean));
|
|
1053
|
+
} else if (rules) {
|
|
1054
|
+
this.rulesSlot.push(rules.trim());
|
|
1055
|
+
}
|
|
1056
|
+
return this;
|
|
1057
|
+
}
|
|
1058
|
+
withRetryHint(hint) {
|
|
1059
|
+
this.retryHintSlot = hint.trim();
|
|
1060
|
+
return this;
|
|
1061
|
+
}
|
|
1062
|
+
withFewShot(examples) {
|
|
1063
|
+
this.fewShotSlot = examples;
|
|
1064
|
+
return this;
|
|
1065
|
+
}
|
|
1066
|
+
withPinnedFacts(facts) {
|
|
1067
|
+
if (!facts) return this;
|
|
1068
|
+
if (typeof facts === "string") {
|
|
1069
|
+
this.pinnedFactsSlot = facts.trim();
|
|
1070
|
+
return this;
|
|
1071
|
+
}
|
|
1072
|
+
this.pinnedFactsSlot = Object.entries(facts).filter(([, val]) => val !== void 0 && val !== null && val !== "").map(([key, val]) => `\u2022 ${key} : ${Array.isArray(val) ? val.join(", ") : String(val)}`).join("\n");
|
|
1073
|
+
return this;
|
|
1074
|
+
}
|
|
1075
|
+
withContext(context) {
|
|
1076
|
+
this.contextSlot = context.trim();
|
|
1077
|
+
return this;
|
|
1078
|
+
}
|
|
1079
|
+
withUserPayload(payload) {
|
|
1080
|
+
this.userPayloadSlot = payload.trim();
|
|
1081
|
+
return this;
|
|
1082
|
+
}
|
|
1083
|
+
schemaContract(schema, options) {
|
|
1084
|
+
let shapeDescription = "JSON Object";
|
|
1085
|
+
if (schema instanceof import_zod.z.ZodObject) {
|
|
1086
|
+
shapeDescription = JSON.stringify(Object.keys(schema.shape));
|
|
1087
|
+
}
|
|
1088
|
+
this.schemaContractText = [
|
|
1089
|
+
`[DIRECTIVE DE CONTRAT DE SORTIE JSON STRICT]`,
|
|
1090
|
+
`Tu DOIS renvoyer UNIQUEMENT un objet JSON valide, sans balises markdown (\`\`\`json), sans texte avant ou apr\xE8s.`,
|
|
1091
|
+
`Champs obligatoires et types attendus :`,
|
|
1092
|
+
options?.schemaName ? `Sch\xE9ma : ${options.schemaName}` : "",
|
|
1093
|
+
shapeDescription
|
|
1094
|
+
].filter(Boolean).join("\n");
|
|
1095
|
+
return this;
|
|
1096
|
+
}
|
|
1097
|
+
assembleMessages(pinnedFacts, context, userPayload) {
|
|
1098
|
+
const messages = [];
|
|
1099
|
+
let system0 = this.personaSlot || "Tu es un assistant expert.";
|
|
1100
|
+
if (this.schemaContractText) {
|
|
1101
|
+
system0 += `
|
|
1102
|
+
|
|
1103
|
+
${this.schemaContractText}`;
|
|
1104
|
+
}
|
|
1105
|
+
messages.push({ role: "system", content: system0 });
|
|
1106
|
+
const dynamicParts = [];
|
|
1107
|
+
if (this.rulesSlot.length > 0) {
|
|
1108
|
+
dynamicParts.push(
|
|
1109
|
+
`[R\xC8GLES ET CONSIGNES M\xC9TIER]
|
|
1110
|
+
${this.rulesSlot.map((rule, idx) => `${idx + 1}. ${rule}`).join("\n")}`
|
|
1111
|
+
);
|
|
1112
|
+
}
|
|
1113
|
+
if (this.retryHintSlot) {
|
|
1114
|
+
dynamicParts.push(`[INSTRUCTION DE CORRECTION / RETRY]
|
|
1115
|
+
${this.retryHintSlot}`);
|
|
1116
|
+
}
|
|
1117
|
+
if (dynamicParts.length > 0) {
|
|
1118
|
+
messages.push({ role: "system", content: dynamicParts.join("\n\n") });
|
|
1119
|
+
}
|
|
1120
|
+
if (this.fewShotSlot.length > 0) {
|
|
1121
|
+
const examplesContent = this.fewShotSlot.map(
|
|
1122
|
+
(ex, idx) => `[Exemple ${idx + 1}]
|
|
1123
|
+
Question: ${ex.question}
|
|
1124
|
+
R\xE9ponse attendue: ${ex.answer}`
|
|
1125
|
+
).join("\n\n");
|
|
1126
|
+
messages.push({ role: "system", content: `[EXEMPLES DE R\xC9F\xC9RENCE]
|
|
1127
|
+
${examplesContent}` });
|
|
1128
|
+
}
|
|
1129
|
+
const userParts = [];
|
|
1130
|
+
if (pinnedFacts) {
|
|
1131
|
+
userParts.push(`[FAITS STRUCTUR\xC9S]
|
|
1132
|
+
${pinnedFacts}`);
|
|
1133
|
+
}
|
|
1134
|
+
if (context) {
|
|
1135
|
+
userParts.push(`[CONTEXTE DOCUMENTAIRE]
|
|
1136
|
+
${context}`);
|
|
1137
|
+
}
|
|
1138
|
+
if (userPayload) {
|
|
1139
|
+
const labelNeeded = Boolean(pinnedFacts || context);
|
|
1140
|
+
userParts.push(labelNeeded ? `[DONN\xC9ES \xC0 ANALYSER]
|
|
1141
|
+
${userPayload}` : userPayload);
|
|
1142
|
+
}
|
|
1143
|
+
messages.push({ role: "user", content: userParts.join("\n\n") });
|
|
1144
|
+
return messages;
|
|
1145
|
+
}
|
|
1146
|
+
toMessages() {
|
|
1147
|
+
return this.assembleMessages(this.pinnedFactsSlot, this.contextSlot, this.userPayloadSlot);
|
|
1148
|
+
}
|
|
1149
|
+
build(budget) {
|
|
1150
|
+
if (!budget) {
|
|
1151
|
+
const messages2 = this.toMessages();
|
|
1152
|
+
const promptText2 = messages2.map((msg) => `${msg.role.toUpperCase()}:
|
|
1153
|
+
${msg.content}`).join("\n\n");
|
|
1154
|
+
return {
|
|
1155
|
+
messages: messages2,
|
|
1156
|
+
promptText: promptText2,
|
|
1157
|
+
allocatedTokens: Math.ceil(promptText2.length / 4),
|
|
1158
|
+
isTruncated: false
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
budget.reset();
|
|
1162
|
+
let isTruncated = false;
|
|
1163
|
+
let truncatedSlot;
|
|
1164
|
+
let system0 = this.personaSlot || "Tu es un assistant expert.";
|
|
1165
|
+
if (this.schemaContractText) {
|
|
1166
|
+
system0 += `
|
|
1167
|
+
|
|
1168
|
+
${this.schemaContractText}`;
|
|
1169
|
+
}
|
|
1170
|
+
budget.forceReserve("persona", system0);
|
|
1171
|
+
if (this.rulesSlot.length > 0 || this.retryHintSlot) {
|
|
1172
|
+
const rulesText = this.rulesSlot.map((rule, idx) => `${idx + 1}. ${rule}`).join("\n");
|
|
1173
|
+
budget.forceReserve("rules", `${rulesText}
|
|
1174
|
+
${this.retryHintSlot}`);
|
|
1175
|
+
}
|
|
1176
|
+
let payloadToUse = this.userPayloadSlot;
|
|
1177
|
+
let factsToUse = this.pinnedFactsSlot;
|
|
1178
|
+
let contextToUse = this.contextSlot;
|
|
1179
|
+
if (payloadToUse) {
|
|
1180
|
+
const payloadCost = budget.count(payloadToUse);
|
|
1181
|
+
if (payloadCost > budget.remaining()) {
|
|
1182
|
+
const charLimit = Math.max(100, budget.remaining() * 4);
|
|
1183
|
+
payloadToUse = payloadToUse.slice(0, charLimit);
|
|
1184
|
+
isTruncated = true;
|
|
1185
|
+
truncatedSlot = "user";
|
|
1186
|
+
}
|
|
1187
|
+
budget.reserve("user", payloadToUse);
|
|
1188
|
+
}
|
|
1189
|
+
if (factsToUse) {
|
|
1190
|
+
const factsCost = budget.count(factsToUse);
|
|
1191
|
+
if (factsCost > budget.remaining()) {
|
|
1192
|
+
const charLimit = Math.max(50, budget.remaining() * 4);
|
|
1193
|
+
factsToUse = factsToUse.slice(0, charLimit);
|
|
1194
|
+
isTruncated = true;
|
|
1195
|
+
truncatedSlot = "pinnedFacts";
|
|
1196
|
+
}
|
|
1197
|
+
budget.reserve("pinnedFacts", factsToUse);
|
|
1198
|
+
}
|
|
1199
|
+
if (contextToUse) {
|
|
1200
|
+
const contextCost = budget.count(contextToUse);
|
|
1201
|
+
if (contextCost > budget.remaining()) {
|
|
1202
|
+
const charLimit = Math.max(0, budget.remaining() * 4);
|
|
1203
|
+
contextToUse = contextToUse.slice(0, charLimit);
|
|
1204
|
+
isTruncated = true;
|
|
1205
|
+
truncatedSlot = "context";
|
|
1206
|
+
}
|
|
1207
|
+
budget.reserve("context", contextToUse);
|
|
1208
|
+
}
|
|
1209
|
+
const messages = this.assembleMessages(factsToUse, contextToUse, payloadToUse);
|
|
1210
|
+
const promptText = messages.map((msg) => `${msg.role.toUpperCase()}:
|
|
1211
|
+
${msg.content}`).join("\n\n");
|
|
1212
|
+
return {
|
|
1213
|
+
messages,
|
|
1214
|
+
promptText,
|
|
1215
|
+
allocatedTokens: budget.getAllocated(),
|
|
1216
|
+
isTruncated,
|
|
1217
|
+
truncatedSlot
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
};
|
|
1221
|
+
|
|
1222
|
+
// src/prompts/prompt-registry.ts
|
|
1223
|
+
var PromptRegistry = class {
|
|
1224
|
+
static registry = /* @__PURE__ */ new Map();
|
|
1225
|
+
static register(templateOrOptions) {
|
|
1226
|
+
const template = templateOrOptions instanceof PromptTemplate ? templateOrOptions : new PromptTemplate(templateOrOptions);
|
|
1227
|
+
const existing = this.registry.get(template.id) || [];
|
|
1228
|
+
const filtered = existing.filter((item) => item.version !== template.version);
|
|
1229
|
+
filtered.push(template);
|
|
1230
|
+
filtered.sort((a, b) => b.version - a.version);
|
|
1231
|
+
this.registry.set(template.id, filtered);
|
|
1232
|
+
}
|
|
1233
|
+
static get(id, options) {
|
|
1234
|
+
const templates = this.registry.get(id);
|
|
1235
|
+
if (!templates || templates.length === 0) {
|
|
1236
|
+
throw new Error(`[PromptRegistry] No prompt template registered with id: "${id}"`);
|
|
1237
|
+
}
|
|
1238
|
+
if (options?.version !== void 0) {
|
|
1239
|
+
const match = templates.find((item) => item.version === options.version);
|
|
1240
|
+
if (!match) {
|
|
1241
|
+
throw new Error(
|
|
1242
|
+
`[PromptRegistry] Template "${id}" with version ${options.version} not found`
|
|
1243
|
+
);
|
|
1244
|
+
}
|
|
1245
|
+
return match;
|
|
1246
|
+
}
|
|
1247
|
+
if (options?.label) {
|
|
1248
|
+
const match = templates.find((item) => item.label === options.label);
|
|
1249
|
+
if (match) {
|
|
1250
|
+
return match;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
return templates[0];
|
|
1254
|
+
}
|
|
1255
|
+
static has(id) {
|
|
1256
|
+
return this.registry.has(id);
|
|
1257
|
+
}
|
|
1258
|
+
static clear() {
|
|
1259
|
+
this.registry.clear();
|
|
1260
|
+
}
|
|
1261
|
+
};
|
|
349
1262
|
// Annotate the CommonJS export names for ESM import in node:
|
|
350
1263
|
0 && (module.exports = {
|
|
351
1264
|
AvantGateControlLayer,
|
|
352
1265
|
DEFAULT_MODEL_PRICES,
|
|
1266
|
+
PromptBuilder,
|
|
1267
|
+
PromptRegistry,
|
|
1268
|
+
PromptTemplate,
|
|
353
1269
|
ZenLLMControlLayer,
|
|
354
1270
|
calculateCostUSD,
|
|
355
1271
|
createAvantGate,
|