avantgate 1.8.0 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,18 +19,12 @@ export declare class AvantGateControlLayer {
19
19
  private executeProviderPipeline;
20
20
  private assembleResult;
21
21
  private notifyAuditSink;
22
- /**
23
- * Exécute une requête avec garde d'entrée, masquage PII, garde pré-vol et calcul des coûts.
24
- */
25
22
  execute: (options: {
26
23
  userQuery: string;
27
24
  systemPrompt?: string;
28
25
  temperature?: number;
29
26
  providerOverride?: LLMProviderPort;
30
27
  }) => Promise<ExecutionResult>;
31
- /**
32
- * Exécute une requête et valide/répare le résultat selon un schéma Zod.
33
- */
34
28
  executeStructured: <T>(options: {
35
29
  userQuery: string;
36
30
  systemPrompt?: string;
@@ -38,10 +32,16 @@ export declare class AvantGateControlLayer {
38
32
  temperature?: number;
39
33
  providerOverride?: LLMProviderPort;
40
34
  }) => Promise<StructuredExecutionResult<T>>;
41
- /**
42
- * Méthode unifiée de premier niveau pour l'extraction structurée sans code boilerplate.
43
- * Gère le failover multi-fournisseurs, les retries, la validation Zod et la normalisation financière.
44
- */
35
+ private sanitizeIncomingMessages;
36
+ private verifyPromptBudget;
37
+ private dispatchOverrideAttempt;
38
+ private dispatchSimulatedAttempt;
39
+ private dispatchPipelineAttempt;
40
+ private dispatchProviderAttempt;
41
+ private parseAndValidateStructuredResult;
42
+ private buildStructuredResult;
43
+ private notifyAuditSinkSafe;
44
+ private waitRetryBackoff;
45
45
  generateStructuredOutput: <T>(options: GenerateStructuredOutputOptions<T>) => Promise<StructuredExecutionResult<T>>;
46
46
  }
47
47
  export declare const createLLMControlLayer: (config: ControlLayerConfig) => AvantGateControlLayer;
@@ -1 +1 @@
1
- {"version":3,"file":"control-layer.d.ts","sourceRoot":"","sources":["../src/control-layer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EACV,kBAAkB,EAClB,eAAe,EACf,yBAAyB,EACzB,+BAA+B,EAC/B,eAAe,EAIhB,MAAM,SAAS,CAAC;AAiBjB,qBAAa,qBAAqB;IAChC,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,oBAAoB,CAAC,CAAuB;gBAExC,MAAM,EAAE,kBAAkB;IAgBtC,OAAO,CAAC,qBAAqB,CAU3B;IAEF,OAAO,CAAC,kBAAkB,CAaxB;IAEF,OAAO,CAAC,aAAa,CAcnB;IAEF,OAAO,CAAC,mBAAmB,CAezB;IAEF,OAAO,CAAC,yBAAyB,CAe/B;IAEF,OAAO,CAAC,oBAAoB,CAkC1B;IAEF,OAAO,CAAC,wBAAwB,CAW9B;IAEF,OAAO,CAAC,aAAa,CAOnB;IAEF,OAAO,CAAC,aAAa,CAUnB;IAEF,OAAO,CAAC,gBAAgB,CAMtB;IAEF,OAAO,CAAC,iBAAiB,CAiBvB;IAEF,OAAO,CAAC,uBAAuB,CAmB7B;IAEF,OAAO,CAAC,uBAAuB,CA+B7B;IAEF,OAAO,CAAC,cAAc,CAqBpB;IAEF,OAAO,CAAC,eAAe,CAarB;IAEF;;OAEG;IACH,OAAO,GAAU,SAAS;QACxB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,gBAAgB,CAAC,EAAE,eAAe,CAAC;KACpC,KAAG,OAAO,CAAC,eAAe,CAAC,CA2B1B;IAEF;;OAEG;IACH,iBAAiB,GAAU,CAAC,EAAE,SAAS;QACrC,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,gBAAgB,CAAC,EAAE,eAAe,CAAC;KACpC,KAAG,OAAO,CAAC,yBAAyB,CAAC,CAAC,CAAC,CAAC,CAyBvC;IAEF;;;OAGG;IACH,wBAAwB,GAAU,CAAC,EACjC,SAAS,+BAA+B,CAAC,CAAC,CAAC,KAC1C,OAAO,CAAC,yBAAyB,CAAC,CAAC,CAAC,CAAC,CAuHtC;CACH;AAED,eAAO,MAAM,qBAAqB,GAAI,QAAQ,kBAAkB,KAAG,qBAElE,CAAC;AAEF,eAAO,MAAM,eAAe,WAJkB,kBAAkB,KAAG,qBAIf,CAAC;AACrD,OAAO,EAAE,qBAAqB,IAAI,kBAAkB,EAAE,CAAC"}
1
+ {"version":3,"file":"control-layer.d.ts","sourceRoot":"","sources":["../src/control-layer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EACV,kBAAkB,EAClB,eAAe,EACf,yBAAyB,EACzB,+BAA+B,EAC/B,eAAe,EAIhB,MAAM,SAAS,CAAC;AAiBjB,qBAAa,qBAAqB;IAChC,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,oBAAoB,CAAC,CAAuB;gBAExC,MAAM,EAAE,kBAAkB;IAgBtC,OAAO,CAAC,qBAAqB,CAU3B;IAEF,OAAO,CAAC,kBAAkB,CAaxB;IAEF,OAAO,CAAC,aAAa,CAcnB;IAEF,OAAO,CAAC,mBAAmB,CAezB;IAEF,OAAO,CAAC,yBAAyB,CAe/B;IAEF,OAAO,CAAC,oBAAoB,CAkC1B;IAEF,OAAO,CAAC,wBAAwB,CAW9B;IAEF,OAAO,CAAC,aAAa,CAOnB;IAEF,OAAO,CAAC,aAAa,CAUnB;IAEF,OAAO,CAAC,gBAAgB,CAMtB;IAEF,OAAO,CAAC,iBAAiB,CAsBvB;IAEF,OAAO,CAAC,uBAAuB,CAmB7B;IAEF,OAAO,CAAC,uBAAuB,CA+B7B;IAEF,OAAO,CAAC,cAAc,CAqBpB;IAEF,OAAO,CAAC,eAAe,CAarB;IAEF,OAAO,GAAU,SAAS;QACxB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,gBAAgB,CAAC,EAAE,eAAe,CAAC;KACpC,KAAG,OAAO,CAAC,eAAe,CAAC,CA2B1B;IAGF,iBAAiB,GAAU,CAAC,EAAE,SAAS;QACrC,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,gBAAgB,CAAC,EAAE,eAAe,CAAC;KACpC,KAAG,OAAO,CAAC,yBAAyB,CAAC,CAAC,CAAC,CAAC,CAyBvC;IAEF,OAAO,CAAC,wBAAwB,CAO9B;IAEF,OAAO,CAAC,kBAAkB,CAIxB;IAEF,OAAO,CAAC,uBAAuB,CAe7B;IAEF,OAAO,CAAC,wBAAwB,CAqB9B;IAEF,OAAO,CAAC,uBAAuB,CAkB7B;IAEF,OAAO,CAAC,uBAAuB,CAY7B;IAEF,OAAO,CAAC,gCAAgC,CAiBtC;IAEF,OAAO,CAAC,qBAAqB,CAwB3B;IAEF,OAAO,CAAC,mBAAmB,CAazB;IAEF,OAAO,CAAC,gBAAgB,CAQtB;IAEF,wBAAwB,GAAU,CAAC,EACjC,SAAS,+BAA+B,CAAC,CAAC,CAAC,KAC1C,OAAO,CAAC,yBAAyB,CAAC,CAAC,CAAC,CAAC,CAwCtC;CACH;AAED,eAAO,MAAM,qBAAqB,GAAI,QAAQ,kBAAkB,KAAG,qBAElE,CAAC;AAEF,eAAO,MAAM,eAAe,WAJkB,kBAAkB,KAAG,qBAIf,CAAC;AACrD,OAAO,EAAE,qBAAqB,IAAI,kBAAkB,EAAE,CAAC"}
package/dist/index.js CHANGED
@@ -1419,19 +1419,20 @@ var AvantGateControlLayer = class {
1419
1419
  this.config.emergencyFallback
1420
1420
  ].filter((provider) => Boolean(provider?.client));
1421
1421
  };
1422
- executeSimulation = (query, systemPrompt) => {
1422
+ executeSimulation = (query, systemPrompt, modelOverride) => {
1423
+ const targetModel = modelOverride ?? this.config.primary.model;
1423
1424
  const promptTokens = Math.ceil(((systemPrompt?.length ?? 0) + query.length) / 4);
1424
1425
  const completionTokens = 50;
1425
- const cost = this.calculateCost(this.config.primary.model, promptTokens, completionTokens);
1426
+ const cost = this.calculateCost(targetModel, promptTokens, completionTokens);
1426
1427
  return {
1427
- text: `[AvantGate In-Process Engine] Response simulation for model: ${this.config.primary.model}`,
1428
+ text: `[AvantGate In-Process Engine] Response simulation for model: ${targetModel}`,
1428
1429
  tokens: {
1429
1430
  prompt: promptTokens,
1430
1431
  completion: completionTokens,
1431
1432
  total: promptTokens + completionTokens
1432
1433
  },
1433
1434
  costUSD: cost,
1434
- modelUsed: this.config.primary.model,
1435
+ modelUsed: targetModel,
1435
1436
  failoverOccurred: false,
1436
1437
  attempts: 1
1437
1438
  };
@@ -1511,9 +1512,6 @@ var AvantGateControlLayer = class {
1511
1512
  attempts: result.attempts
1512
1513
  });
1513
1514
  };
1514
- /**
1515
- * Exécute une requête avec garde d'entrée, masquage PII, garde pré-vol et calcul des coûts.
1516
- */
1517
1515
  execute = async (options) => {
1518
1516
  const sanitizedQuery = this.applySecurityGuards(options.userQuery);
1519
1517
  const messages = this.buildMessages(options.systemPrompt, sanitizedQuery);
@@ -1536,9 +1534,6 @@ var AvantGateControlLayer = class {
1536
1534
  await this.notifyAuditSink(result);
1537
1535
  return result;
1538
1536
  };
1539
- /**
1540
- * Exécute une requête et valide/répare le résultat selon un schéma Zod.
1541
- */
1542
1537
  executeStructured = async (options) => {
1543
1538
  const rawResult = await this.execute({
1544
1539
  userQuery: options.userQuery,
@@ -1562,107 +1557,147 @@ var AvantGateControlLayer = class {
1562
1557
  failoverOccurred: rawResult.failoverOccurred
1563
1558
  };
1564
1559
  };
1565
- /**
1566
- * Méthode unifiée de premier niveau pour l'extraction structurée sans code boilerplate.
1567
- * Gère le failover multi-fournisseurs, les retries, la validation Zod et la normalisation financière.
1568
- */
1569
- generateStructuredOutput = async (options) => {
1570
- const maxRetries = options.maxRetries ?? this.config.retryOptions?.maxRetries ?? 2;
1571
- const modelToUse = options.model ?? this.config.primary.model;
1572
- const processedMessages = options.messages.map((msg) => {
1560
+ sanitizeIncomingMessages = (messages) => {
1561
+ return messages.map((msg) => {
1573
1562
  if (msg.role === "user") {
1574
1563
  return { ...msg, content: this.applySecurityGuards(msg.content) };
1575
1564
  }
1576
1565
  return msg;
1577
1566
  });
1578
- const promptLength = processedMessages.reduce((sum, msg) => sum + msg.content.length, 0);
1567
+ };
1568
+ verifyPromptBudget = async (messages, model) => {
1569
+ const promptLength = messages.reduce((sum, msg) => sum + msg.content.length, 0);
1579
1570
  const estimatedPromptTokens = Math.ceil(promptLength / 4);
1580
- await this.checkPreflightBudget(estimatedPromptTokens, modelToUse);
1571
+ await this.checkPreflightBudget(estimatedPromptTokens, model);
1572
+ };
1573
+ dispatchOverrideAttempt = async (override, messages, model, temperature) => {
1574
+ const res = await override.complete({ model, messages, temperature: temperature ?? 0.1 });
1575
+ const usage = this.resolveTokens(res.usage, JSON.stringify(messages), res.text);
1576
+ return {
1577
+ responseText: res.text,
1578
+ promptTokens: usage.promptTokens,
1579
+ completionTokens: usage.completionTokens,
1580
+ modelUsed: model,
1581
+ failoverOccurred: false
1582
+ };
1583
+ };
1584
+ dispatchSimulatedAttempt = (messages, modelOverride) => {
1585
+ if (!this.config.mockSimulation) {
1586
+ throw new ConfigurationError(
1587
+ "[AvantGate Configuration Error] No active LLM provider configured. Provide a client implementing LLMProviderPort or configure credentials (apiKey / baseUrl)."
1588
+ );
1589
+ }
1590
+ const sim = this.executeSimulation(JSON.stringify(messages), void 0, modelOverride);
1591
+ const responseText = sim.text.includes("{") ? sim.text : JSON.stringify({ simulation: true, model: sim.modelUsed });
1592
+ return {
1593
+ responseText,
1594
+ promptTokens: sim.tokens.prompt,
1595
+ completionTokens: sim.tokens.completion,
1596
+ modelUsed: sim.modelUsed,
1597
+ failoverOccurred: false
1598
+ };
1599
+ };
1600
+ dispatchPipelineAttempt = async (messages, modelOverride, temperature) => {
1601
+ const output = await this.executeProviderPipeline(
1602
+ messages,
1603
+ JSON.stringify(messages),
1604
+ temperature ?? 0.1,
1605
+ modelOverride
1606
+ );
1607
+ return {
1608
+ responseText: output.responseText,
1609
+ promptTokens: output.usage.promptTokens,
1610
+ completionTokens: output.usage.completionTokens,
1611
+ modelUsed: output.modelUsed,
1612
+ failoverOccurred: output.failoverOccurred
1613
+ };
1614
+ };
1615
+ dispatchProviderAttempt = async (options, messages, targetModel) => {
1616
+ if (options.providerOverride) {
1617
+ return this.dispatchOverrideAttempt(options.providerOverride, messages, targetModel, options.temperature);
1618
+ }
1619
+ if (this.getProviderChain().length === 0) {
1620
+ return this.dispatchSimulatedAttempt(messages, options.model);
1621
+ }
1622
+ return this.dispatchPipelineAttempt(messages, options.model ?? targetModel, options.temperature);
1623
+ };
1624
+ parseAndValidateStructuredResult = (rawText, options) => {
1625
+ const sanitizedResponse = this.applyOutputSecurityGuards(rawText);
1626
+ const isFinancial = options.financialNormalizer ?? Boolean(
1627
+ this.config.features?.finance?.enableFrenchAccounting || this.config.features?.finance
1628
+ );
1629
+ const parsedData = validateWithZod(sanitizedResponse, options.schema, {
1630
+ financialNormalizer: isFinancial,
1631
+ jurisdiction: this.config.features?.finance?.jurisdiction
1632
+ });
1633
+ return { parsedData, sanitizedResponse };
1634
+ };
1635
+ buildStructuredResult = (params) => {
1636
+ const totalTokens = params.promptTokens + params.completionTokens;
1637
+ const costUSD = this.calculateCost(params.modelUsed, params.promptTokens, params.completionTokens);
1638
+ this.checkPostExecutionBudget(totalTokens, costUSD);
1639
+ return {
1640
+ data: params.data,
1641
+ rawText: params.rawText,
1642
+ tokens: {
1643
+ prompt: params.promptTokens,
1644
+ completion: params.completionTokens,
1645
+ total: totalTokens
1646
+ },
1647
+ costUSD,
1648
+ modelUsed: params.modelUsed,
1649
+ failoverOccurred: params.failoverOccurred
1650
+ };
1651
+ };
1652
+ notifyAuditSinkSafe = async (sanitizedText, result, attempts) => {
1653
+ await this.notifyAuditSink({
1654
+ text: sanitizedText,
1655
+ tokens: result.tokens,
1656
+ costUSD: result.costUSD,
1657
+ modelUsed: result.modelUsed,
1658
+ failoverOccurred: result.failoverOccurred,
1659
+ attempts
1660
+ });
1661
+ };
1662
+ waitRetryBackoff = async (attempt, maxRetries) => {
1663
+ if (attempt >= maxRetries) {
1664
+ return;
1665
+ }
1666
+ const initialDelay = this.config.retryOptions?.initialDelayMs ?? 200;
1667
+ const factor = this.config.retryOptions?.backoffFactor ?? 1.5;
1668
+ const delay = initialDelay * Math.pow(factor, attempt);
1669
+ await new Promise((resolve) => setTimeout(resolve, delay));
1670
+ };
1671
+ generateStructuredOutput = async (options) => {
1672
+ const maxRetries = options.maxRetries ?? this.config.retryOptions?.maxRetries ?? 2;
1673
+ const targetModel = options.model ?? this.config.primary.model;
1674
+ const processedMessages = this.sanitizeIncomingMessages(options.messages);
1675
+ await this.verifyPromptBudget(processedMessages, targetModel);
1581
1676
  let lastError;
1582
1677
  let accumulatedPromptTokens = 0;
1583
1678
  let accumulatedCompletionTokens = 0;
1584
- let failoverOccurred = false;
1585
- let modelUsed = modelToUse;
1586
1679
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
1587
1680
  try {
1588
- let responseText = "";
1589
- let attemptPromptTokens = 0;
1590
- let attemptCompletionTokens = 0;
1591
- if (options.providerOverride) {
1592
- const res = await options.providerOverride.complete({
1593
- model: modelToUse,
1594
- messages: processedMessages,
1595
- temperature: options.temperature ?? 0.1
1596
- });
1597
- responseText = res.text;
1598
- const usage = this.resolveTokens(res.usage, JSON.stringify(processedMessages), responseText);
1599
- attemptPromptTokens = usage.promptTokens;
1600
- attemptCompletionTokens = usage.completionTokens;
1601
- modelUsed = modelToUse;
1602
- } else if (this.getProviderChain().length === 0) {
1603
- if (this.config.mockSimulation) {
1604
- const sim = this.executeSimulation(JSON.stringify(processedMessages));
1605
- responseText = sim.text;
1606
- attemptPromptTokens = sim.tokens.prompt;
1607
- attemptCompletionTokens = sim.tokens.completion;
1608
- } else {
1609
- throw new ConfigurationError(
1610
- "[AvantGate Configuration Error] No active LLM provider configured. Provide a client implementing LLMProviderPort or configure credentials (apiKey / baseUrl)."
1611
- );
1612
- }
1613
- } else {
1614
- const output = await this.executeProviderPipeline(
1615
- processedMessages,
1616
- JSON.stringify(processedMessages),
1617
- options.temperature ?? 0.1,
1618
- options.model
1619
- );
1620
- responseText = output.responseText;
1621
- attemptPromptTokens = output.usage.promptTokens;
1622
- attemptCompletionTokens = output.usage.completionTokens;
1623
- modelUsed = output.modelUsed;
1624
- failoverOccurred = output.failoverOccurred;
1625
- }
1626
- accumulatedPromptTokens += attemptPromptTokens;
1627
- accumulatedCompletionTokens += attemptCompletionTokens;
1628
- const sanitizedResponse = this.applyOutputSecurityGuards(responseText);
1629
- const isFinancial = options.financialNormalizer ?? Boolean(
1630
- this.config.features?.finance?.enableFrenchAccounting || this.config.features?.finance
1681
+ const output = await this.dispatchProviderAttempt(options, processedMessages, targetModel);
1682
+ accumulatedPromptTokens += output.promptTokens;
1683
+ accumulatedCompletionTokens += output.completionTokens;
1684
+ const { parsedData, sanitizedResponse } = this.parseAndValidateStructuredResult(
1685
+ output.responseText,
1686
+ options
1631
1687
  );
1632
- const parsedData = validateWithZod(sanitizedResponse, options.schema, {
1633
- financialNormalizer: isFinancial,
1634
- jurisdiction: this.config.features?.finance?.jurisdiction
1635
- });
1636
- const totalTokens = accumulatedPromptTokens + accumulatedCompletionTokens;
1637
- const costUSD = this.calculateCost(modelUsed, accumulatedPromptTokens, accumulatedCompletionTokens);
1638
- this.checkPostExecutionBudget(totalTokens, costUSD);
1639
- const result = {
1688
+ const result = this.buildStructuredResult({
1640
1689
  data: parsedData,
1641
1690
  rawText: sanitizedResponse,
1642
- tokens: {
1643
- prompt: accumulatedPromptTokens,
1644
- completion: accumulatedCompletionTokens,
1645
- total: totalTokens
1646
- },
1647
- costUSD,
1648
- modelUsed,
1649
- failoverOccurred
1650
- };
1651
- await this.notifyAuditSink({
1652
- text: responseText,
1653
- tokens: result.tokens,
1654
- costUSD: result.costUSD,
1655
- modelUsed: result.modelUsed,
1656
- failoverOccurred: result.failoverOccurred,
1657
- attempts: attempt + 1
1691
+ promptTokens: accumulatedPromptTokens,
1692
+ completionTokens: accumulatedCompletionTokens,
1693
+ modelUsed: output.modelUsed,
1694
+ failoverOccurred: output.failoverOccurred
1658
1695
  });
1696
+ await this.notifyAuditSinkSafe(sanitizedResponse, result, attempt + 1);
1659
1697
  return result;
1660
1698
  } catch (err) {
1661
1699
  lastError = err;
1662
- if (attempt < maxRetries) {
1663
- const delay = (this.config.retryOptions?.initialDelayMs ?? 200) * Math.pow(this.config.retryOptions?.backoffFactor ?? 1.5, attempt);
1664
- await new Promise((resolve) => setTimeout(resolve, delay));
1665
- }
1700
+ await this.waitRetryBackoff(attempt, maxRetries);
1666
1701
  }
1667
1702
  }
1668
1703
  throw lastError;
package/dist/index.mjs CHANGED
@@ -809,19 +809,20 @@ var AvantGateControlLayer = class {
809
809
  this.config.emergencyFallback
810
810
  ].filter((provider) => Boolean(provider?.client));
811
811
  };
812
- executeSimulation = (query, systemPrompt) => {
812
+ executeSimulation = (query, systemPrompt, modelOverride) => {
813
+ const targetModel = modelOverride ?? this.config.primary.model;
813
814
  const promptTokens = Math.ceil(((systemPrompt?.length ?? 0) + query.length) / 4);
814
815
  const completionTokens = 50;
815
- const cost = this.calculateCost(this.config.primary.model, promptTokens, completionTokens);
816
+ const cost = this.calculateCost(targetModel, promptTokens, completionTokens);
816
817
  return {
817
- text: `[AvantGate In-Process Engine] Response simulation for model: ${this.config.primary.model}`,
818
+ text: `[AvantGate In-Process Engine] Response simulation for model: ${targetModel}`,
818
819
  tokens: {
819
820
  prompt: promptTokens,
820
821
  completion: completionTokens,
821
822
  total: promptTokens + completionTokens
822
823
  },
823
824
  costUSD: cost,
824
- modelUsed: this.config.primary.model,
825
+ modelUsed: targetModel,
825
826
  failoverOccurred: false,
826
827
  attempts: 1
827
828
  };
@@ -901,9 +902,6 @@ var AvantGateControlLayer = class {
901
902
  attempts: result.attempts
902
903
  });
903
904
  };
904
- /**
905
- * Exécute une requête avec garde d'entrée, masquage PII, garde pré-vol et calcul des coûts.
906
- */
907
905
  execute = async (options) => {
908
906
  const sanitizedQuery = this.applySecurityGuards(options.userQuery);
909
907
  const messages = this.buildMessages(options.systemPrompt, sanitizedQuery);
@@ -926,9 +924,6 @@ var AvantGateControlLayer = class {
926
924
  await this.notifyAuditSink(result);
927
925
  return result;
928
926
  };
929
- /**
930
- * Exécute une requête et valide/répare le résultat selon un schéma Zod.
931
- */
932
927
  executeStructured = async (options) => {
933
928
  const rawResult = await this.execute({
934
929
  userQuery: options.userQuery,
@@ -952,107 +947,147 @@ var AvantGateControlLayer = class {
952
947
  failoverOccurred: rawResult.failoverOccurred
953
948
  };
954
949
  };
955
- /**
956
- * Méthode unifiée de premier niveau pour l'extraction structurée sans code boilerplate.
957
- * Gère le failover multi-fournisseurs, les retries, la validation Zod et la normalisation financière.
958
- */
959
- generateStructuredOutput = async (options) => {
960
- const maxRetries = options.maxRetries ?? this.config.retryOptions?.maxRetries ?? 2;
961
- const modelToUse = options.model ?? this.config.primary.model;
962
- const processedMessages = options.messages.map((msg) => {
950
+ sanitizeIncomingMessages = (messages) => {
951
+ return messages.map((msg) => {
963
952
  if (msg.role === "user") {
964
953
  return { ...msg, content: this.applySecurityGuards(msg.content) };
965
954
  }
966
955
  return msg;
967
956
  });
968
- const promptLength = processedMessages.reduce((sum, msg) => sum + msg.content.length, 0);
957
+ };
958
+ verifyPromptBudget = async (messages, model) => {
959
+ const promptLength = messages.reduce((sum, msg) => sum + msg.content.length, 0);
969
960
  const estimatedPromptTokens = Math.ceil(promptLength / 4);
970
- await this.checkPreflightBudget(estimatedPromptTokens, modelToUse);
961
+ await this.checkPreflightBudget(estimatedPromptTokens, model);
962
+ };
963
+ dispatchOverrideAttempt = async (override, messages, model, temperature) => {
964
+ const res = await override.complete({ model, messages, temperature: temperature ?? 0.1 });
965
+ const usage = this.resolveTokens(res.usage, JSON.stringify(messages), res.text);
966
+ return {
967
+ responseText: res.text,
968
+ promptTokens: usage.promptTokens,
969
+ completionTokens: usage.completionTokens,
970
+ modelUsed: model,
971
+ failoverOccurred: false
972
+ };
973
+ };
974
+ dispatchSimulatedAttempt = (messages, modelOverride) => {
975
+ if (!this.config.mockSimulation) {
976
+ throw new ConfigurationError(
977
+ "[AvantGate Configuration Error] No active LLM provider configured. Provide a client implementing LLMProviderPort or configure credentials (apiKey / baseUrl)."
978
+ );
979
+ }
980
+ const sim = this.executeSimulation(JSON.stringify(messages), void 0, modelOverride);
981
+ const responseText = sim.text.includes("{") ? sim.text : JSON.stringify({ simulation: true, model: sim.modelUsed });
982
+ return {
983
+ responseText,
984
+ promptTokens: sim.tokens.prompt,
985
+ completionTokens: sim.tokens.completion,
986
+ modelUsed: sim.modelUsed,
987
+ failoverOccurred: false
988
+ };
989
+ };
990
+ dispatchPipelineAttempt = async (messages, modelOverride, temperature) => {
991
+ const output = await this.executeProviderPipeline(
992
+ messages,
993
+ JSON.stringify(messages),
994
+ temperature ?? 0.1,
995
+ modelOverride
996
+ );
997
+ return {
998
+ responseText: output.responseText,
999
+ promptTokens: output.usage.promptTokens,
1000
+ completionTokens: output.usage.completionTokens,
1001
+ modelUsed: output.modelUsed,
1002
+ failoverOccurred: output.failoverOccurred
1003
+ };
1004
+ };
1005
+ dispatchProviderAttempt = async (options, messages, targetModel) => {
1006
+ if (options.providerOverride) {
1007
+ return this.dispatchOverrideAttempt(options.providerOverride, messages, targetModel, options.temperature);
1008
+ }
1009
+ if (this.getProviderChain().length === 0) {
1010
+ return this.dispatchSimulatedAttempt(messages, options.model);
1011
+ }
1012
+ return this.dispatchPipelineAttempt(messages, options.model ?? targetModel, options.temperature);
1013
+ };
1014
+ parseAndValidateStructuredResult = (rawText, options) => {
1015
+ const sanitizedResponse = this.applyOutputSecurityGuards(rawText);
1016
+ const isFinancial = options.financialNormalizer ?? Boolean(
1017
+ this.config.features?.finance?.enableFrenchAccounting || this.config.features?.finance
1018
+ );
1019
+ const parsedData = validateWithZod(sanitizedResponse, options.schema, {
1020
+ financialNormalizer: isFinancial,
1021
+ jurisdiction: this.config.features?.finance?.jurisdiction
1022
+ });
1023
+ return { parsedData, sanitizedResponse };
1024
+ };
1025
+ buildStructuredResult = (params) => {
1026
+ const totalTokens = params.promptTokens + params.completionTokens;
1027
+ const costUSD = this.calculateCost(params.modelUsed, params.promptTokens, params.completionTokens);
1028
+ this.checkPostExecutionBudget(totalTokens, costUSD);
1029
+ return {
1030
+ data: params.data,
1031
+ rawText: params.rawText,
1032
+ tokens: {
1033
+ prompt: params.promptTokens,
1034
+ completion: params.completionTokens,
1035
+ total: totalTokens
1036
+ },
1037
+ costUSD,
1038
+ modelUsed: params.modelUsed,
1039
+ failoverOccurred: params.failoverOccurred
1040
+ };
1041
+ };
1042
+ notifyAuditSinkSafe = async (sanitizedText, result, attempts) => {
1043
+ await this.notifyAuditSink({
1044
+ text: sanitizedText,
1045
+ tokens: result.tokens,
1046
+ costUSD: result.costUSD,
1047
+ modelUsed: result.modelUsed,
1048
+ failoverOccurred: result.failoverOccurred,
1049
+ attempts
1050
+ });
1051
+ };
1052
+ waitRetryBackoff = async (attempt, maxRetries) => {
1053
+ if (attempt >= maxRetries) {
1054
+ return;
1055
+ }
1056
+ const initialDelay = this.config.retryOptions?.initialDelayMs ?? 200;
1057
+ const factor = this.config.retryOptions?.backoffFactor ?? 1.5;
1058
+ const delay = initialDelay * Math.pow(factor, attempt);
1059
+ await new Promise((resolve) => setTimeout(resolve, delay));
1060
+ };
1061
+ generateStructuredOutput = async (options) => {
1062
+ const maxRetries = options.maxRetries ?? this.config.retryOptions?.maxRetries ?? 2;
1063
+ const targetModel = options.model ?? this.config.primary.model;
1064
+ const processedMessages = this.sanitizeIncomingMessages(options.messages);
1065
+ await this.verifyPromptBudget(processedMessages, targetModel);
971
1066
  let lastError;
972
1067
  let accumulatedPromptTokens = 0;
973
1068
  let accumulatedCompletionTokens = 0;
974
- let failoverOccurred = false;
975
- let modelUsed = modelToUse;
976
1069
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
977
1070
  try {
978
- let responseText = "";
979
- let attemptPromptTokens = 0;
980
- let attemptCompletionTokens = 0;
981
- if (options.providerOverride) {
982
- const res = await options.providerOverride.complete({
983
- model: modelToUse,
984
- messages: processedMessages,
985
- temperature: options.temperature ?? 0.1
986
- });
987
- responseText = res.text;
988
- const usage = this.resolveTokens(res.usage, JSON.stringify(processedMessages), responseText);
989
- attemptPromptTokens = usage.promptTokens;
990
- attemptCompletionTokens = usage.completionTokens;
991
- modelUsed = modelToUse;
992
- } else if (this.getProviderChain().length === 0) {
993
- if (this.config.mockSimulation) {
994
- const sim = this.executeSimulation(JSON.stringify(processedMessages));
995
- responseText = sim.text;
996
- attemptPromptTokens = sim.tokens.prompt;
997
- attemptCompletionTokens = sim.tokens.completion;
998
- } else {
999
- throw new ConfigurationError(
1000
- "[AvantGate Configuration Error] No active LLM provider configured. Provide a client implementing LLMProviderPort or configure credentials (apiKey / baseUrl)."
1001
- );
1002
- }
1003
- } else {
1004
- const output = await this.executeProviderPipeline(
1005
- processedMessages,
1006
- JSON.stringify(processedMessages),
1007
- options.temperature ?? 0.1,
1008
- options.model
1009
- );
1010
- responseText = output.responseText;
1011
- attemptPromptTokens = output.usage.promptTokens;
1012
- attemptCompletionTokens = output.usage.completionTokens;
1013
- modelUsed = output.modelUsed;
1014
- failoverOccurred = output.failoverOccurred;
1015
- }
1016
- accumulatedPromptTokens += attemptPromptTokens;
1017
- accumulatedCompletionTokens += attemptCompletionTokens;
1018
- const sanitizedResponse = this.applyOutputSecurityGuards(responseText);
1019
- const isFinancial = options.financialNormalizer ?? Boolean(
1020
- this.config.features?.finance?.enableFrenchAccounting || this.config.features?.finance
1071
+ const output = await this.dispatchProviderAttempt(options, processedMessages, targetModel);
1072
+ accumulatedPromptTokens += output.promptTokens;
1073
+ accumulatedCompletionTokens += output.completionTokens;
1074
+ const { parsedData, sanitizedResponse } = this.parseAndValidateStructuredResult(
1075
+ output.responseText,
1076
+ options
1021
1077
  );
1022
- const parsedData = validateWithZod(sanitizedResponse, options.schema, {
1023
- financialNormalizer: isFinancial,
1024
- jurisdiction: this.config.features?.finance?.jurisdiction
1025
- });
1026
- const totalTokens = accumulatedPromptTokens + accumulatedCompletionTokens;
1027
- const costUSD = this.calculateCost(modelUsed, accumulatedPromptTokens, accumulatedCompletionTokens);
1028
- this.checkPostExecutionBudget(totalTokens, costUSD);
1029
- const result = {
1078
+ const result = this.buildStructuredResult({
1030
1079
  data: parsedData,
1031
1080
  rawText: sanitizedResponse,
1032
- tokens: {
1033
- prompt: accumulatedPromptTokens,
1034
- completion: accumulatedCompletionTokens,
1035
- total: totalTokens
1036
- },
1037
- costUSD,
1038
- modelUsed,
1039
- failoverOccurred
1040
- };
1041
- await this.notifyAuditSink({
1042
- text: responseText,
1043
- tokens: result.tokens,
1044
- costUSD: result.costUSD,
1045
- modelUsed: result.modelUsed,
1046
- failoverOccurred: result.failoverOccurred,
1047
- attempts: attempt + 1
1081
+ promptTokens: accumulatedPromptTokens,
1082
+ completionTokens: accumulatedCompletionTokens,
1083
+ modelUsed: output.modelUsed,
1084
+ failoverOccurred: output.failoverOccurred
1048
1085
  });
1086
+ await this.notifyAuditSinkSafe(sanitizedResponse, result, attempt + 1);
1049
1087
  return result;
1050
1088
  } catch (err) {
1051
1089
  lastError = err;
1052
- if (attempt < maxRetries) {
1053
- const delay = (this.config.retryOptions?.initialDelayMs ?? 200) * Math.pow(this.config.retryOptions?.backoffFactor ?? 1.5, attempt);
1054
- await new Promise((resolve) => setTimeout(resolve, delay));
1055
- }
1090
+ await this.waitRetryBackoff(attempt, maxRetries);
1056
1091
  }
1057
1092
  }
1058
1093
  throw lastError;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "avantgate",
3
- "version": "1.8.0",
3
+ "version": "1.8.1",
4
4
  "description": "Zero-infrastructure, in-process AI Application Firewall (AI-WAF) & privacy control plane for TypeScript: real-time prompt guardrails, zero-egress PII redaction, agent tool isolation, anti-IDOR defense, pre-flight token budgets, and multi-model failover.",
5
5
  "author": "Gatewall Team",
6
6
  "license": "MIT",