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.mjs
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractAndCleanJSON,
|
|
3
|
+
validateWithZod
|
|
4
|
+
} from "./chunk-CO26LNFD.mjs";
|
|
5
|
+
|
|
1
6
|
// src/pricing.ts
|
|
2
7
|
var DEFAULT_MODEL_PRICES = {
|
|
3
8
|
// DeepSeek
|
|
@@ -49,8 +54,11 @@ function calculateCostUSD(model, promptTokens, completionTokens, cacheHitTokens
|
|
|
49
54
|
// src/sanitizer.ts
|
|
50
55
|
var EMAIL_REGEX = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b/g;
|
|
51
56
|
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
|
|
53
|
-
var
|
|
57
|
+
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;
|
|
58
|
+
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;
|
|
59
|
+
var SPI_FORMATTED_REGEX = /\b[0-3]\d(?:\s+\d{2}){5}\s+\d\b/g;
|
|
60
|
+
var IBAN_REGEX = /\b[A-Z]{2}\s*[0-9]{2}(?:[\s\r\n.-]*[A-Z0-9]){11,30}\b/g;
|
|
61
|
+
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;
|
|
54
62
|
function sanitizePII(input) {
|
|
55
63
|
let count = 0;
|
|
56
64
|
let result = input;
|
|
@@ -62,13 +70,36 @@ function sanitizePII(input) {
|
|
|
62
70
|
count++;
|
|
63
71
|
return "[REDACTED_PHONE]";
|
|
64
72
|
});
|
|
65
|
-
result = result.replace(
|
|
73
|
+
result = result.replace(IBAN_REGEX, (match) => {
|
|
74
|
+
const cleanChars = match.replace(/[\s\r\n.-]/g, "");
|
|
75
|
+
if (cleanChars.length >= 15 && cleanChars.length <= 34) {
|
|
76
|
+
count++;
|
|
77
|
+
return "[REDACTED_IBAN]";
|
|
78
|
+
}
|
|
79
|
+
return match;
|
|
80
|
+
});
|
|
81
|
+
result = result.replace(BIC_LABELLED_REGEX, (_, bicCode) => {
|
|
66
82
|
count++;
|
|
67
|
-
return
|
|
83
|
+
return `[REDACTED_BIC: ${bicCode.slice(0, 4)}****]`;
|
|
68
84
|
});
|
|
69
|
-
result = result.replace(
|
|
85
|
+
result = result.replace(SPI_LABELLED_REGEX, (fullMatch, digits) => {
|
|
70
86
|
count++;
|
|
71
|
-
return "[
|
|
87
|
+
return fullMatch.replace(digits, "[REDACTED_SPI]");
|
|
88
|
+
});
|
|
89
|
+
result = result.replace(NIR_SSN_REGEX, (match) => {
|
|
90
|
+
const rawDigits = match.replace(/\s+/g, "");
|
|
91
|
+
if (rawDigits.length === 13 || rawDigits.length === 15) {
|
|
92
|
+
count++;
|
|
93
|
+
return "[REDACTED_NIR]";
|
|
94
|
+
}
|
|
95
|
+
return match;
|
|
96
|
+
});
|
|
97
|
+
result = result.replace(SPI_FORMATTED_REGEX, (match) => {
|
|
98
|
+
if (!match.includes("[REDACTED")) {
|
|
99
|
+
count++;
|
|
100
|
+
return "[REDACTED_SPI]";
|
|
101
|
+
}
|
|
102
|
+
return match;
|
|
72
103
|
});
|
|
73
104
|
return { text: result, maskedCount: count };
|
|
74
105
|
}
|
|
@@ -103,44 +134,6 @@ function validateUserInput(input, options) {
|
|
|
103
134
|
return { valid: true };
|
|
104
135
|
}
|
|
105
136
|
|
|
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
137
|
// src/control-layer.ts
|
|
145
138
|
var AvantGateControlLayer = class {
|
|
146
139
|
config;
|
|
@@ -213,14 +206,15 @@ var AvantGateControlLayer = class {
|
|
|
213
206
|
attempts: 1
|
|
214
207
|
};
|
|
215
208
|
}
|
|
216
|
-
async executeProviderPipeline(messages, query, temperature) {
|
|
209
|
+
async executeProviderPipeline(messages, query, temperature, modelOverride) {
|
|
217
210
|
const chain = this.getProviderChain();
|
|
218
211
|
let lastError;
|
|
219
212
|
for (let index = 0; index < chain.length; index++) {
|
|
220
213
|
const providerConfig = chain[index];
|
|
214
|
+
const targetModel = index === 0 && modelOverride ? modelOverride : providerConfig.model;
|
|
221
215
|
try {
|
|
222
216
|
const response = await providerConfig.client.complete({
|
|
223
|
-
model:
|
|
217
|
+
model: targetModel,
|
|
224
218
|
messages,
|
|
225
219
|
temperature: temperature ?? 0.2
|
|
226
220
|
});
|
|
@@ -228,7 +222,7 @@ var AvantGateControlLayer = class {
|
|
|
228
222
|
return {
|
|
229
223
|
responseText: response.text,
|
|
230
224
|
usage,
|
|
231
|
-
modelUsed:
|
|
225
|
+
modelUsed: targetModel,
|
|
232
226
|
failoverOccurred: index > 0,
|
|
233
227
|
attempts: index + 1
|
|
234
228
|
};
|
|
@@ -296,7 +290,13 @@ var AvantGateControlLayer = class {
|
|
|
296
290
|
temperature: options.temperature ?? 0.1,
|
|
297
291
|
providerOverride: options.providerOverride
|
|
298
292
|
});
|
|
299
|
-
const
|
|
293
|
+
const isFinancial = Boolean(
|
|
294
|
+
this.config.features?.finance?.enableFrenchAccounting || this.config.features?.finance
|
|
295
|
+
);
|
|
296
|
+
const parsedData = validateWithZod(rawResult.text, options.schema, {
|
|
297
|
+
financialNormalizer: isFinancial,
|
|
298
|
+
jurisdiction: this.config.features?.finance?.jurisdiction
|
|
299
|
+
});
|
|
300
300
|
return {
|
|
301
301
|
data: parsedData,
|
|
302
302
|
rawText: rawResult.text,
|
|
@@ -306,14 +306,426 @@ var AvantGateControlLayer = class {
|
|
|
306
306
|
failoverOccurred: rawResult.failoverOccurred
|
|
307
307
|
};
|
|
308
308
|
}
|
|
309
|
+
/**
|
|
310
|
+
* Méthode unifiée de premier niveau pour l'extraction structurée sans code boilerplate.
|
|
311
|
+
* Gère le failover multi-fournisseurs, les retries, la validation Zod et la normalisation financière.
|
|
312
|
+
*/
|
|
313
|
+
async generateStructuredOutput(options) {
|
|
314
|
+
const maxRetries = options.maxRetries ?? this.config.retryOptions?.maxRetries ?? 2;
|
|
315
|
+
const modelToUse = options.model ?? this.config.primary.model;
|
|
316
|
+
const processedMessages = options.messages.map((msg) => {
|
|
317
|
+
if (msg.role === "user") {
|
|
318
|
+
return { ...msg, content: this.applySecurityGuards(msg.content) };
|
|
319
|
+
}
|
|
320
|
+
return msg;
|
|
321
|
+
});
|
|
322
|
+
let lastError;
|
|
323
|
+
let accumulatedPromptTokens = 0;
|
|
324
|
+
let accumulatedCompletionTokens = 0;
|
|
325
|
+
let failoverOccurred = false;
|
|
326
|
+
let modelUsed = modelToUse;
|
|
327
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
328
|
+
try {
|
|
329
|
+
let responseText = "";
|
|
330
|
+
let attemptPromptTokens = 0;
|
|
331
|
+
let attemptCompletionTokens = 0;
|
|
332
|
+
if (options.providerOverride) {
|
|
333
|
+
const res = await options.providerOverride.complete({
|
|
334
|
+
model: modelToUse,
|
|
335
|
+
messages: processedMessages,
|
|
336
|
+
temperature: options.temperature ?? 0.1
|
|
337
|
+
});
|
|
338
|
+
responseText = res.text;
|
|
339
|
+
const usage = this.resolveTokens(res.usage, JSON.stringify(processedMessages), responseText);
|
|
340
|
+
attemptPromptTokens = usage.promptTokens;
|
|
341
|
+
attemptCompletionTokens = usage.completionTokens;
|
|
342
|
+
modelUsed = modelToUse;
|
|
343
|
+
} else if (this.getProviderChain().length === 0) {
|
|
344
|
+
const sim = this.executeSimulation(JSON.stringify(processedMessages));
|
|
345
|
+
responseText = sim.text;
|
|
346
|
+
attemptPromptTokens = sim.tokens.prompt;
|
|
347
|
+
attemptCompletionTokens = sim.tokens.completion;
|
|
348
|
+
} else {
|
|
349
|
+
const output = await this.executeProviderPipeline(
|
|
350
|
+
processedMessages,
|
|
351
|
+
JSON.stringify(processedMessages),
|
|
352
|
+
options.temperature ?? 0.1,
|
|
353
|
+
options.model
|
|
354
|
+
);
|
|
355
|
+
responseText = output.responseText;
|
|
356
|
+
attemptPromptTokens = output.usage.promptTokens;
|
|
357
|
+
attemptCompletionTokens = output.usage.completionTokens;
|
|
358
|
+
modelUsed = output.modelUsed;
|
|
359
|
+
failoverOccurred = output.failoverOccurred;
|
|
360
|
+
}
|
|
361
|
+
accumulatedPromptTokens += attemptPromptTokens;
|
|
362
|
+
accumulatedCompletionTokens += attemptCompletionTokens;
|
|
363
|
+
const isFinancial = options.financialNormalizer ?? Boolean(
|
|
364
|
+
this.config.features?.finance?.enableFrenchAccounting || this.config.features?.finance
|
|
365
|
+
);
|
|
366
|
+
const parsedData = validateWithZod(responseText, options.schema, {
|
|
367
|
+
financialNormalizer: isFinancial,
|
|
368
|
+
jurisdiction: this.config.features?.finance?.jurisdiction
|
|
369
|
+
});
|
|
370
|
+
const totalTokens = accumulatedPromptTokens + accumulatedCompletionTokens;
|
|
371
|
+
const costUSD = calculateCostUSD(modelUsed, accumulatedPromptTokens, accumulatedCompletionTokens);
|
|
372
|
+
const result = {
|
|
373
|
+
data: parsedData,
|
|
374
|
+
rawText: responseText,
|
|
375
|
+
tokens: {
|
|
376
|
+
prompt: accumulatedPromptTokens,
|
|
377
|
+
completion: accumulatedCompletionTokens,
|
|
378
|
+
total: totalTokens
|
|
379
|
+
},
|
|
380
|
+
costUSD,
|
|
381
|
+
modelUsed,
|
|
382
|
+
failoverOccurred
|
|
383
|
+
};
|
|
384
|
+
await this.notifyAuditSink({
|
|
385
|
+
text: responseText,
|
|
386
|
+
tokens: result.tokens,
|
|
387
|
+
costUSD: result.costUSD,
|
|
388
|
+
modelUsed: result.modelUsed,
|
|
389
|
+
failoverOccurred: result.failoverOccurred,
|
|
390
|
+
attempts: attempt + 1
|
|
391
|
+
});
|
|
392
|
+
return result;
|
|
393
|
+
} catch (err) {
|
|
394
|
+
lastError = err;
|
|
395
|
+
if (attempt < maxRetries) {
|
|
396
|
+
const delay = (this.config.retryOptions?.initialDelayMs ?? 200) * Math.pow(this.config.retryOptions?.backoffFactor ?? 1.5, attempt);
|
|
397
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
throw lastError;
|
|
402
|
+
}
|
|
309
403
|
};
|
|
310
404
|
function createLLMControlLayer(config) {
|
|
311
405
|
return new AvantGateControlLayer(config);
|
|
312
406
|
}
|
|
313
407
|
var createAvantGate = createLLMControlLayer;
|
|
408
|
+
|
|
409
|
+
// src/prompts/prompt-template.ts
|
|
410
|
+
var DEFAULT_JAILBREAK_PATTERNS = [
|
|
411
|
+
/ignore\s+(all\s+)?(previous|prior)\s+(instructions|rules)/i,
|
|
412
|
+
/disregard\s+(all\s+)?(previous|prior)\s+(instructions|rules)/i,
|
|
413
|
+
/system\s+override/i,
|
|
414
|
+
/you\s+are\s+now\s+(unrestricted|DAN|jailbroken)/i,
|
|
415
|
+
/<\s*\|\s*im_start\s*\|>/i,
|
|
416
|
+
/\[SYSTEM_PROMPT\]/i
|
|
417
|
+
];
|
|
418
|
+
var PromptTemplate = class {
|
|
419
|
+
id;
|
|
420
|
+
version;
|
|
421
|
+
label;
|
|
422
|
+
description;
|
|
423
|
+
template;
|
|
424
|
+
inputSchema;
|
|
425
|
+
blockPatterns;
|
|
426
|
+
antiInjectionEnabled;
|
|
427
|
+
customSanitizer;
|
|
428
|
+
constructor(options) {
|
|
429
|
+
this.id = options.id;
|
|
430
|
+
this.version = options.version;
|
|
431
|
+
this.label = options.label || "production";
|
|
432
|
+
this.description = options.description;
|
|
433
|
+
this.template = options.template;
|
|
434
|
+
this.inputSchema = options.inputSchema;
|
|
435
|
+
this.antiInjectionEnabled = options.antiInjection?.enabled !== false;
|
|
436
|
+
this.blockPatterns = options.antiInjection?.blockPatterns || DEFAULT_JAILBREAK_PATTERNS;
|
|
437
|
+
this.customSanitizer = options.antiInjection?.sanitizer;
|
|
438
|
+
}
|
|
439
|
+
validateUserInput(variables) {
|
|
440
|
+
const threats = [];
|
|
441
|
+
if (this.inputSchema) {
|
|
442
|
+
const parsed = this.inputSchema.safeParse(variables);
|
|
443
|
+
if (!parsed.success) {
|
|
444
|
+
return {
|
|
445
|
+
isValid: false,
|
|
446
|
+
threats: parsed.error.issues.map(
|
|
447
|
+
(issue) => `${issue.path.join(".")}: ${issue.message}`
|
|
448
|
+
)
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if (this.antiInjectionEnabled) {
|
|
453
|
+
for (const [key, val] of Object.entries(variables)) {
|
|
454
|
+
if (typeof val === "string") {
|
|
455
|
+
for (const pattern of this.blockPatterns) {
|
|
456
|
+
if (pattern.test(val)) {
|
|
457
|
+
threats.push(
|
|
458
|
+
`Potential prompt injection in variable "${key}" matching pattern ${pattern}`
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return { isValid: threats.length === 0, threats };
|
|
466
|
+
}
|
|
467
|
+
format(variables) {
|
|
468
|
+
const check = this.validateUserInput(variables);
|
|
469
|
+
if (!check.isValid) {
|
|
470
|
+
throw new Error(
|
|
471
|
+
`[PromptTemplate:${this.id}] Validation failed: ${check.threats.join(", ")}`
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
const interpolated = this.template.replace(/\{\{\s*([\w.-]+)\s*\}\}/g, (_, key) => {
|
|
475
|
+
let value = variables[key];
|
|
476
|
+
if (value === void 0 || value === null) {
|
|
477
|
+
return "";
|
|
478
|
+
}
|
|
479
|
+
let stringValue = typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
480
|
+
if (this.customSanitizer) {
|
|
481
|
+
stringValue = this.customSanitizer(stringValue);
|
|
482
|
+
}
|
|
483
|
+
return stringValue;
|
|
484
|
+
});
|
|
485
|
+
return interpolated.split("\n").filter((line, index, arr) => line.trim() !== "" || index > 0 && arr[index - 1].trim() !== "").join("\n");
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
// src/prompts/prompt-builder.ts
|
|
490
|
+
import { z } from "zod";
|
|
491
|
+
var PromptBuilder = class {
|
|
492
|
+
personaSlot = "";
|
|
493
|
+
rulesSlot = [];
|
|
494
|
+
retryHintSlot = "";
|
|
495
|
+
fewShotSlot = [];
|
|
496
|
+
pinnedFactsSlot = "";
|
|
497
|
+
contextSlot = "";
|
|
498
|
+
userPayloadSlot = "";
|
|
499
|
+
schemaContractText = "";
|
|
500
|
+
constructor(templateOrId) {
|
|
501
|
+
if (templateOrId instanceof PromptTemplate) {
|
|
502
|
+
this.personaSlot = templateOrId.template;
|
|
503
|
+
} else if (typeof templateOrId === "string") {
|
|
504
|
+
this.personaSlot = templateOrId.trim();
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
withPersona(persona) {
|
|
508
|
+
this.personaSlot = persona.trim();
|
|
509
|
+
return this;
|
|
510
|
+
}
|
|
511
|
+
withRules(rules) {
|
|
512
|
+
if (Array.isArray(rules)) {
|
|
513
|
+
this.rulesSlot.push(...rules.map((rule) => rule.trim()).filter(Boolean));
|
|
514
|
+
} else if (rules) {
|
|
515
|
+
this.rulesSlot.push(rules.trim());
|
|
516
|
+
}
|
|
517
|
+
return this;
|
|
518
|
+
}
|
|
519
|
+
withRetryHint(hint) {
|
|
520
|
+
this.retryHintSlot = hint.trim();
|
|
521
|
+
return this;
|
|
522
|
+
}
|
|
523
|
+
withFewShot(examples) {
|
|
524
|
+
this.fewShotSlot = examples;
|
|
525
|
+
return this;
|
|
526
|
+
}
|
|
527
|
+
withPinnedFacts(facts) {
|
|
528
|
+
if (!facts) return this;
|
|
529
|
+
if (typeof facts === "string") {
|
|
530
|
+
this.pinnedFactsSlot = facts.trim();
|
|
531
|
+
return this;
|
|
532
|
+
}
|
|
533
|
+
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");
|
|
534
|
+
return this;
|
|
535
|
+
}
|
|
536
|
+
withContext(context) {
|
|
537
|
+
this.contextSlot = context.trim();
|
|
538
|
+
return this;
|
|
539
|
+
}
|
|
540
|
+
withUserPayload(payload) {
|
|
541
|
+
this.userPayloadSlot = payload.trim();
|
|
542
|
+
return this;
|
|
543
|
+
}
|
|
544
|
+
schemaContract(schema, options) {
|
|
545
|
+
let shapeDescription = "JSON Object";
|
|
546
|
+
if (schema instanceof z.ZodObject) {
|
|
547
|
+
shapeDescription = JSON.stringify(Object.keys(schema.shape));
|
|
548
|
+
}
|
|
549
|
+
this.schemaContractText = [
|
|
550
|
+
`[DIRECTIVE DE CONTRAT DE SORTIE JSON STRICT]`,
|
|
551
|
+
`Tu DOIS renvoyer UNIQUEMENT un objet JSON valide, sans balises markdown (\`\`\`json), sans texte avant ou apr\xE8s.`,
|
|
552
|
+
`Champs obligatoires et types attendus :`,
|
|
553
|
+
options?.schemaName ? `Sch\xE9ma : ${options.schemaName}` : "",
|
|
554
|
+
shapeDescription
|
|
555
|
+
].filter(Boolean).join("\n");
|
|
556
|
+
return this;
|
|
557
|
+
}
|
|
558
|
+
assembleMessages(pinnedFacts, context, userPayload) {
|
|
559
|
+
const messages = [];
|
|
560
|
+
let system0 = this.personaSlot || "Tu es un assistant expert.";
|
|
561
|
+
if (this.schemaContractText) {
|
|
562
|
+
system0 += `
|
|
563
|
+
|
|
564
|
+
${this.schemaContractText}`;
|
|
565
|
+
}
|
|
566
|
+
messages.push({ role: "system", content: system0 });
|
|
567
|
+
const dynamicParts = [];
|
|
568
|
+
if (this.rulesSlot.length > 0) {
|
|
569
|
+
dynamicParts.push(
|
|
570
|
+
`[R\xC8GLES ET CONSIGNES M\xC9TIER]
|
|
571
|
+
${this.rulesSlot.map((rule, idx) => `${idx + 1}. ${rule}`).join("\n")}`
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
if (this.retryHintSlot) {
|
|
575
|
+
dynamicParts.push(`[INSTRUCTION DE CORRECTION / RETRY]
|
|
576
|
+
${this.retryHintSlot}`);
|
|
577
|
+
}
|
|
578
|
+
if (dynamicParts.length > 0) {
|
|
579
|
+
messages.push({ role: "system", content: dynamicParts.join("\n\n") });
|
|
580
|
+
}
|
|
581
|
+
if (this.fewShotSlot.length > 0) {
|
|
582
|
+
const examplesContent = this.fewShotSlot.map(
|
|
583
|
+
(ex, idx) => `[Exemple ${idx + 1}]
|
|
584
|
+
Question: ${ex.question}
|
|
585
|
+
R\xE9ponse attendue: ${ex.answer}`
|
|
586
|
+
).join("\n\n");
|
|
587
|
+
messages.push({ role: "system", content: `[EXEMPLES DE R\xC9F\xC9RENCE]
|
|
588
|
+
${examplesContent}` });
|
|
589
|
+
}
|
|
590
|
+
const userParts = [];
|
|
591
|
+
if (pinnedFacts) {
|
|
592
|
+
userParts.push(`[FAITS STRUCTUR\xC9S]
|
|
593
|
+
${pinnedFacts}`);
|
|
594
|
+
}
|
|
595
|
+
if (context) {
|
|
596
|
+
userParts.push(`[CONTEXTE DOCUMENTAIRE]
|
|
597
|
+
${context}`);
|
|
598
|
+
}
|
|
599
|
+
if (userPayload) {
|
|
600
|
+
const labelNeeded = Boolean(pinnedFacts || context);
|
|
601
|
+
userParts.push(labelNeeded ? `[DONN\xC9ES \xC0 ANALYSER]
|
|
602
|
+
${userPayload}` : userPayload);
|
|
603
|
+
}
|
|
604
|
+
messages.push({ role: "user", content: userParts.join("\n\n") });
|
|
605
|
+
return messages;
|
|
606
|
+
}
|
|
607
|
+
toMessages() {
|
|
608
|
+
return this.assembleMessages(this.pinnedFactsSlot, this.contextSlot, this.userPayloadSlot);
|
|
609
|
+
}
|
|
610
|
+
build(budget) {
|
|
611
|
+
if (!budget) {
|
|
612
|
+
const messages2 = this.toMessages();
|
|
613
|
+
const promptText2 = messages2.map((msg) => `${msg.role.toUpperCase()}:
|
|
614
|
+
${msg.content}`).join("\n\n");
|
|
615
|
+
return {
|
|
616
|
+
messages: messages2,
|
|
617
|
+
promptText: promptText2,
|
|
618
|
+
allocatedTokens: Math.ceil(promptText2.length / 4),
|
|
619
|
+
isTruncated: false
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
budget.reset();
|
|
623
|
+
let isTruncated = false;
|
|
624
|
+
let truncatedSlot;
|
|
625
|
+
let system0 = this.personaSlot || "Tu es un assistant expert.";
|
|
626
|
+
if (this.schemaContractText) {
|
|
627
|
+
system0 += `
|
|
628
|
+
|
|
629
|
+
${this.schemaContractText}`;
|
|
630
|
+
}
|
|
631
|
+
budget.forceReserve("persona", system0);
|
|
632
|
+
if (this.rulesSlot.length > 0 || this.retryHintSlot) {
|
|
633
|
+
const rulesText = this.rulesSlot.map((rule, idx) => `${idx + 1}. ${rule}`).join("\n");
|
|
634
|
+
budget.forceReserve("rules", `${rulesText}
|
|
635
|
+
${this.retryHintSlot}`);
|
|
636
|
+
}
|
|
637
|
+
let payloadToUse = this.userPayloadSlot;
|
|
638
|
+
let factsToUse = this.pinnedFactsSlot;
|
|
639
|
+
let contextToUse = this.contextSlot;
|
|
640
|
+
if (payloadToUse) {
|
|
641
|
+
const payloadCost = budget.count(payloadToUse);
|
|
642
|
+
if (payloadCost > budget.remaining()) {
|
|
643
|
+
const charLimit = Math.max(100, budget.remaining() * 4);
|
|
644
|
+
payloadToUse = payloadToUse.slice(0, charLimit);
|
|
645
|
+
isTruncated = true;
|
|
646
|
+
truncatedSlot = "user";
|
|
647
|
+
}
|
|
648
|
+
budget.reserve("user", payloadToUse);
|
|
649
|
+
}
|
|
650
|
+
if (factsToUse) {
|
|
651
|
+
const factsCost = budget.count(factsToUse);
|
|
652
|
+
if (factsCost > budget.remaining()) {
|
|
653
|
+
const charLimit = Math.max(50, budget.remaining() * 4);
|
|
654
|
+
factsToUse = factsToUse.slice(0, charLimit);
|
|
655
|
+
isTruncated = true;
|
|
656
|
+
truncatedSlot = "pinnedFacts";
|
|
657
|
+
}
|
|
658
|
+
budget.reserve("pinnedFacts", factsToUse);
|
|
659
|
+
}
|
|
660
|
+
if (contextToUse) {
|
|
661
|
+
const contextCost = budget.count(contextToUse);
|
|
662
|
+
if (contextCost > budget.remaining()) {
|
|
663
|
+
const charLimit = Math.max(0, budget.remaining() * 4);
|
|
664
|
+
contextToUse = contextToUse.slice(0, charLimit);
|
|
665
|
+
isTruncated = true;
|
|
666
|
+
truncatedSlot = "context";
|
|
667
|
+
}
|
|
668
|
+
budget.reserve("context", contextToUse);
|
|
669
|
+
}
|
|
670
|
+
const messages = this.assembleMessages(factsToUse, contextToUse, payloadToUse);
|
|
671
|
+
const promptText = messages.map((msg) => `${msg.role.toUpperCase()}:
|
|
672
|
+
${msg.content}`).join("\n\n");
|
|
673
|
+
return {
|
|
674
|
+
messages,
|
|
675
|
+
promptText,
|
|
676
|
+
allocatedTokens: budget.getAllocated(),
|
|
677
|
+
isTruncated,
|
|
678
|
+
truncatedSlot
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
// src/prompts/prompt-registry.ts
|
|
684
|
+
var PromptRegistry = class {
|
|
685
|
+
static registry = /* @__PURE__ */ new Map();
|
|
686
|
+
static register(templateOrOptions) {
|
|
687
|
+
const template = templateOrOptions instanceof PromptTemplate ? templateOrOptions : new PromptTemplate(templateOrOptions);
|
|
688
|
+
const existing = this.registry.get(template.id) || [];
|
|
689
|
+
const filtered = existing.filter((item) => item.version !== template.version);
|
|
690
|
+
filtered.push(template);
|
|
691
|
+
filtered.sort((a, b) => b.version - a.version);
|
|
692
|
+
this.registry.set(template.id, filtered);
|
|
693
|
+
}
|
|
694
|
+
static get(id, options) {
|
|
695
|
+
const templates = this.registry.get(id);
|
|
696
|
+
if (!templates || templates.length === 0) {
|
|
697
|
+
throw new Error(`[PromptRegistry] No prompt template registered with id: "${id}"`);
|
|
698
|
+
}
|
|
699
|
+
if (options?.version !== void 0) {
|
|
700
|
+
const match = templates.find((item) => item.version === options.version);
|
|
701
|
+
if (!match) {
|
|
702
|
+
throw new Error(
|
|
703
|
+
`[PromptRegistry] Template "${id}" with version ${options.version} not found`
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
return match;
|
|
707
|
+
}
|
|
708
|
+
if (options?.label) {
|
|
709
|
+
const match = templates.find((item) => item.label === options.label);
|
|
710
|
+
if (match) {
|
|
711
|
+
return match;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
return templates[0];
|
|
715
|
+
}
|
|
716
|
+
static has(id) {
|
|
717
|
+
return this.registry.has(id);
|
|
718
|
+
}
|
|
719
|
+
static clear() {
|
|
720
|
+
this.registry.clear();
|
|
721
|
+
}
|
|
722
|
+
};
|
|
314
723
|
export {
|
|
315
724
|
AvantGateControlLayer,
|
|
316
725
|
DEFAULT_MODEL_PRICES,
|
|
726
|
+
PromptBuilder,
|
|
727
|
+
PromptRegistry,
|
|
728
|
+
PromptTemplate,
|
|
317
729
|
AvantGateControlLayer as ZenLLMControlLayer,
|
|
318
730
|
calculateCostUSD,
|
|
319
731
|
createAvantGate,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type FinancialJurisdictionCode = "FR" | "US" | "UK" | "CH" | "INTERNATIONAL";
|
|
2
|
+
type AccountingStandard = "PCG" | "US_GAAP" | "IFRS" | "SWISS_CO" | "OTHER";
|
|
3
|
+
type FinancialCurrency = "EUR" | "USD" | "GBP" | "CHF";
|
|
4
|
+
interface IAccountingStrategy {
|
|
5
|
+
readonly jurisdictionCode: FinancialJurisdictionCode;
|
|
6
|
+
readonly standard: AccountingStandard;
|
|
7
|
+
readonly defaultCurrency: FinancialCurrency;
|
|
8
|
+
/** Normalise les formats de nombres et symboles propres au pays */
|
|
9
|
+
cleanNumber(value: string | number): number;
|
|
10
|
+
/** Assainit et répare la syntaxe JSON selon les spécificités du pays */
|
|
11
|
+
cleanJSON(rawText: string): string;
|
|
12
|
+
/** Heuristique de détection automatique à partir du texte brut */
|
|
13
|
+
detect(text: string): boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type { AccountingStandard as A, FinancialJurisdictionCode as F, IAccountingStrategy as I, FinancialCurrency as a };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type FinancialJurisdictionCode = "FR" | "US" | "UK" | "CH" | "INTERNATIONAL";
|
|
2
|
+
type AccountingStandard = "PCG" | "US_GAAP" | "IFRS" | "SWISS_CO" | "OTHER";
|
|
3
|
+
type FinancialCurrency = "EUR" | "USD" | "GBP" | "CHF";
|
|
4
|
+
interface IAccountingStrategy {
|
|
5
|
+
readonly jurisdictionCode: FinancialJurisdictionCode;
|
|
6
|
+
readonly standard: AccountingStandard;
|
|
7
|
+
readonly defaultCurrency: FinancialCurrency;
|
|
8
|
+
/** Normalise les formats de nombres et symboles propres au pays */
|
|
9
|
+
cleanNumber(value: string | number): number;
|
|
10
|
+
/** Assainit et répare la syntaxe JSON selon les spécificités du pays */
|
|
11
|
+
cleanJSON(rawText: string): string;
|
|
12
|
+
/** Heuristique de détection automatique à partir du texte brut */
|
|
13
|
+
detect(text: string): boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type { AccountingStandard as A, FinancialJurisdictionCode as F, IAccountingStrategy as I, FinancialCurrency as a };
|