@x12i/ai-gateway 9.1.5 → 9.2.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.
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ /**
3
+ * Normalizes invoke responses into `output.parsed` when an explicit output contract is forwarded.
4
+ * Does not infer contracts from other request fields — that is upstream (graph-engine / ai-tasks).
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.contractSpecToFieldKeys = contractSpecToFieldKeys;
8
+ exports.resolveOutputContractFieldKeys = resolveOutputContractFieldKeys;
9
+ exports.enrichParsedContentForOutputContract = enrichParsedContentForOutputContract;
10
+ const flex_md_loader_js_1 = require("./flex-md-loader.cjs");
11
+ const memory_path_resolution_js_1 = require("./memory-path-resolution.cjs");
12
+ function isPlainObject(value) {
13
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
14
+ }
15
+ function hasMeaningfulContractValue(value) {
16
+ if (value === undefined || value === null)
17
+ return false;
18
+ if (typeof value === 'string')
19
+ return value.trim().length > 0;
20
+ if (Array.isArray(value))
21
+ return value.length > 0;
22
+ if (typeof value === 'object')
23
+ return Object.keys(value).length > 0;
24
+ return true;
25
+ }
26
+ /**
27
+ * Maps an explicit contract spec to field keys. No inference from descriptions or other request fields.
28
+ */
29
+ function contractSpecToFieldKeys(contract) {
30
+ if (contract == null)
31
+ return [];
32
+ if (Array.isArray(contract)) {
33
+ return contract.filter((k) => typeof k === 'string' && k.trim().length > 0);
34
+ }
35
+ if (!isPlainObject(contract))
36
+ return [];
37
+ const properties = contract.properties;
38
+ if (isPlainObject(properties)) {
39
+ return Object.keys(properties);
40
+ }
41
+ return [];
42
+ }
43
+ /** Graph-engine path: `workingMemory.inputs.outputContract` or merged `input.outputContract`. */
44
+ function readExplicitOutputContractFromWorkingMemory(workingMemory) {
45
+ if (!isPlainObject(workingMemory))
46
+ return undefined;
47
+ const inputs = (0, memory_path_resolution_js_1.extractCallerInputsBag)(workingMemory);
48
+ if (inputs?.outputContract !== undefined)
49
+ return inputs.outputContract;
50
+ const input = (0, memory_path_resolution_js_1.coalesceMergedInputBucket)(workingMemory);
51
+ if (isPlainObject(input) && input.outputContract !== undefined) {
52
+ return input.outputContract;
53
+ }
54
+ const loose = (0, memory_path_resolution_js_1.parseLooseJsonObject)(workingMemory.input);
55
+ if (loose?.outputContract !== undefined)
56
+ return loose.outputContract;
57
+ return undefined;
58
+ }
59
+ /**
60
+ * Resolves field keys only from explicit `outputContract` on the request or graph-forwarded inputs.
61
+ */
62
+ function resolveOutputContractFieldKeys(request) {
63
+ if (request == null || typeof request !== 'object')
64
+ return undefined;
65
+ const r = request;
66
+ const candidates = [
67
+ r.outputContract,
68
+ readExplicitOutputContractFromWorkingMemory(r.workingMemory)
69
+ ];
70
+ for (const candidate of candidates) {
71
+ const keys = contractSpecToFieldKeys(candidate);
72
+ if (keys.length > 0)
73
+ return keys;
74
+ }
75
+ return undefined;
76
+ }
77
+ function asParsedRecord(parsed) {
78
+ if (!isPlainObject(parsed))
79
+ return {};
80
+ return { ...parsed };
81
+ }
82
+ function pickAliasValue(source, key) {
83
+ if (hasMeaningfulContractValue(source[key]))
84
+ return source[key];
85
+ const spaced = key.replace(/([A-Z])/g, ' $1').replace(/^./, (c) => c.toUpperCase());
86
+ const title = spaced.charAt(0).toUpperCase() + spaced.slice(1);
87
+ const aliases = [
88
+ key,
89
+ key.toLowerCase(),
90
+ title,
91
+ title.replace(/\s+/g, ' '),
92
+ key.replace(/([A-Z])/g, '_$1').toLowerCase()
93
+ ];
94
+ for (const alias of aliases) {
95
+ if (hasMeaningfulContractValue(source[alias]))
96
+ return source[alias];
97
+ }
98
+ return undefined;
99
+ }
100
+ /**
101
+ * Fills missing contract keys from markdown sections after flex-md. Only runs when explicit contract keys were supplied.
102
+ */
103
+ async function enrichParsedContentForOutputContract(parsed, rawContent, contractKeys, logger) {
104
+ const base = asParsedRecord(parsed);
105
+ if (!contractKeys?.length)
106
+ return base;
107
+ const missing = contractKeys.filter((k) => !hasMeaningfulContractValue(base[k]));
108
+ if (missing.length === 0)
109
+ return base;
110
+ const content = typeof rawContent === 'string' && rawContent.trim().length > 0
111
+ ? rawContent
112
+ : typeof base.rawText === 'string'
113
+ ? base.rawText
114
+ : '';
115
+ if (!content.trim())
116
+ return base;
117
+ const fromMarkdown = (0, flex_md_loader_js_1.parseMarkdownSectionsFromContent)(content, logger);
118
+ const merged = { ...base };
119
+ for (const key of missing) {
120
+ const value = pickAliasValue(fromMarkdown, key);
121
+ if (hasMeaningfulContractValue(value)) {
122
+ merged[key] = value;
123
+ }
124
+ }
125
+ return merged;
126
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Normalizes invoke responses into `output.parsed` when an explicit output contract is forwarded.
3
+ * Does not infer contracts from other request fields — that is upstream (graph-engine / ai-tasks).
4
+ */
5
+ import type { Logxer } from '@x12i/logxer';
6
+ /** Explicit contract: field names or JSON-schema-style `properties` only. */
7
+ export type OutputContractSpec = string[] | {
8
+ properties: Record<string, unknown>;
9
+ };
10
+ /**
11
+ * Maps an explicit contract spec to field keys. No inference from descriptions or other request fields.
12
+ */
13
+ export declare function contractSpecToFieldKeys(contract: unknown): string[];
14
+ /**
15
+ * Resolves field keys only from explicit `outputContract` on the request or graph-forwarded inputs.
16
+ */
17
+ export declare function resolveOutputContractFieldKeys(request: unknown): string[] | undefined;
18
+ /**
19
+ * Fills missing contract keys from markdown sections after flex-md. Only runs when explicit contract keys were supplied.
20
+ */
21
+ export declare function enrichParsedContentForOutputContract(parsed: unknown, rawContent: string, contractKeys: string[] | undefined, logger?: Logxer): Promise<Record<string, unknown>>;
@@ -41,6 +41,7 @@ var __importStar = (this && this.__importStar) || (function () {
41
41
  Object.defineProperty(exports, "__esModule", { value: true });
42
42
  exports.parseTemplate = parseTemplate;
43
43
  exports.isParserAvailable = isParserAvailable;
44
+ const memory_path_resolution_js_1 = require("./memory-path-resolution.cjs");
44
45
  let rendrixModule = null;
45
46
  let parserLoadPromise = null;
46
47
  async function loadRendrix() {
@@ -85,6 +86,7 @@ async function parseTemplate(template, workingMemory, taskConfig, shortTermMemor
85
86
  if (!workingMemory) {
86
87
  return template;
87
88
  }
89
+ const wmForRender = (0, memory_path_resolution_js_1.prepareWorkingMemoryForTemplateRender)(workingMemory);
88
90
  await loadRendrix();
89
91
  if (!rendrixModule) {
90
92
  return template;
@@ -94,18 +96,18 @@ async function parseTemplate(template, workingMemory, taskConfig, shortTermMemor
94
96
  ? rendrixModule
95
97
  : rendrixModule.default || rendrixModule;
96
98
  if (typeof api.render === 'function') {
97
- return await api.render(template, workingMemory, shortTermMemory, experienceMemory, knowledgeMemory, undefined, // functionsMap
99
+ return await api.render(template, wmForRender, shortTermMemory, experienceMemory, knowledgeMemory, undefined, // functionsMap
98
100
  undefined, // choiceOptions
99
101
  undefined, // filesContent
100
102
  templateRenderOptions);
101
103
  }
102
104
  if (typeof api.parse === 'function') {
103
- return await api.parse(template, workingMemory);
105
+ return await api.parse(template, wmForRender);
104
106
  }
105
107
  else if (typeof api === 'function') {
106
108
  const parser = new api();
107
109
  if (typeof parser.parse === 'function') {
108
- return await parser.parse(template, workingMemory);
110
+ return await parser.parse(template, wmForRender);
109
111
  }
110
112
  }
111
113
  return template;
@@ -626,6 +626,10 @@ interface BaseLLMRequest extends Omit<LLMRequest, 'messages' | 'input' | 'reques
626
626
  * Working memory (optional) - Affects all template parsing
627
627
  * Passed to Rendrix (v4+) for template variable substitution.
628
628
  * Plain {{path}} tokens are MUST paths: undefined after merge throws TemplateResolutionError (rethrown by the gateway).
629
+ *
630
+ * Dual roots (graph-engine / ai-tasks contract): `inputs` is the caller / graph-entry bag;
631
+ * `input` is the merged MAIN payload (object or JSON string). Smart-input and `input.*` / `inputs.*`
632
+ * paths resolve with cross-root fallback before render (see `resolveGatewayMemoryPathValue`).
629
633
  */
630
634
  workingMemory?: unknown;
631
635
  /**
@@ -697,6 +701,14 @@ interface BaseLLMRequest extends Omit<LLMRequest, 'messages' | 'input' | 'reques
697
701
  * attach heavy diagnostic objects or raw provider payloads.
698
702
  */
699
703
  diagnostics?: DiagnosticsOptions;
704
+ /**
705
+ * Explicit output contract from graph-engine / ai-tasks: field names or `{ properties }`.
706
+ * Also accepted on `workingMemory.inputs.outputContract`. When absent, the gateway does not
707
+ * infer contract fields from other request shapes (`expectedSchema`, config, etc.).
708
+ */
709
+ outputContract?: string[] | {
710
+ properties: Record<string, unknown>;
711
+ };
700
712
  }
701
713
  /**
702
714
  * Chat request for conversational use cases
@@ -928,6 +940,12 @@ export interface EnhancedLLMResponse<TContent = unknown> extends Omit<AIResponse
928
940
  * Cost in USD (if available)
929
941
  */
930
942
  cost?: number;
943
+ /**
944
+ * Billing state for Run Analysis / Activix when usage is recorded.
945
+ * - `priced`: {@link cost} / {@link costUsd} is a finite number from the router
946
+ * - `unpriced`: usage exists but no price table / adapter cost was returned
947
+ */
948
+ costStatus?: 'priced' | 'unpriced';
931
949
  /**
932
950
  * Cost in USD (preferred stable key when the router exposes it).
933
951
  * When both are present, costUsd should mirror cost.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x12i/ai-gateway",
3
- "version": "9.1.5",
3
+ "version": "9.2.0",
4
4
  "description": "AI Gateway - Unified interface for LLM provider routing and management",
5
5
  "type": "module",
6
6
  "exports": {
@@ -60,7 +60,7 @@
60
60
  "author": "x12i",
61
61
  "license": "mit",
62
62
  "dependencies": {
63
- "@x12i/ai-providers-router": "^4.7.7",
63
+ "@x12i/ai-providers-router": "^4.8.0",
64
64
  "@x12i/rendrix": "^4.3.0",
65
65
  "@aws-sdk/s3-request-presigner": "^3.953.0",
66
66
  "@x12i/env": "^4.0.1",