@x12i/ai-gateway 9.1.6 → 9.3.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.
Files changed (78) hide show
  1. package/README.md +45 -0
  2. package/dist/activity-manager.d.ts +1 -0
  3. package/dist/activity-manager.js +7 -0
  4. package/dist/ai-tools-client.d.ts +20 -0
  5. package/dist/ai-tools-client.js +91 -0
  6. package/dist/flex-md-loader.d.ts +5 -0
  7. package/dist/flex-md-loader.js +16 -0
  8. package/dist/gateway-config.d.ts +2 -0
  9. package/dist/gateway-config.js +2 -1
  10. package/dist/gateway-mode.d.ts +40 -0
  11. package/dist/gateway-mode.js +75 -0
  12. package/dist/gateway-utils.d.ts +57 -1
  13. package/dist/gateway-utils.js +181 -12
  14. package/dist/gateway.d.ts +3 -0
  15. package/dist/gateway.js +47 -15
  16. package/dist/index.d.ts +6 -1
  17. package/dist/index.js +3 -1
  18. package/dist/output-contract-normalizer.d.ts +21 -0
  19. package/dist/output-contract-normalizer.js +121 -0
  20. package/dist/types.d.ts +35 -0
  21. package/dist-cjs/activity-manager.cjs +21 -19
  22. package/dist-cjs/activity-manager.d.ts +1 -0
  23. package/dist-cjs/ai-tools-client.cjs +91 -0
  24. package/dist-cjs/ai-tools-client.d.ts +20 -0
  25. package/dist-cjs/config/activity-tracking-config.cjs +1 -4
  26. package/dist-cjs/content-normalizer/content-normalizer.cjs +3 -8
  27. package/dist-cjs/content-normalizer/index.cjs +1 -7
  28. package/dist-cjs/content-normalizer/types.cjs +1 -2
  29. package/dist-cjs/flex-md-loader.cjs +35 -65
  30. package/dist-cjs/flex-md-loader.d.ts +5 -0
  31. package/dist-cjs/gateway-config.cjs +25 -63
  32. package/dist-cjs/gateway-config.d.ts +2 -0
  33. package/dist-cjs/gateway-conversion.cjs +10 -48
  34. package/dist-cjs/gateway-instructions.cjs +5 -10
  35. package/dist-cjs/gateway-log-meta.cjs +9 -14
  36. package/dist-cjs/gateway-memory.cjs +2 -6
  37. package/dist-cjs/gateway-messages.cjs +3 -6
  38. package/dist-cjs/gateway-meta.cjs +1 -4
  39. package/dist-cjs/gateway-mode.cjs +75 -0
  40. package/dist-cjs/gateway-mode.d.ts +40 -0
  41. package/dist-cjs/gateway-provider-auto-register.cjs +2 -38
  42. package/dist-cjs/gateway-provider.cjs +10 -22
  43. package/dist-cjs/gateway-rate-limiter-constants.cjs +2 -5
  44. package/dist-cjs/gateway-rate-limiter.cjs +5 -9
  45. package/dist-cjs/gateway-retry.cjs +6 -14
  46. package/dist-cjs/gateway-utils.cjs +201 -83
  47. package/dist-cjs/gateway-utils.d.ts +57 -1
  48. package/dist-cjs/gateway-validation.cjs +2 -6
  49. package/dist-cjs/gateway.cjs +100 -72
  50. package/dist-cjs/gateway.d.ts +3 -0
  51. package/dist-cjs/index.cjs +22 -91
  52. package/dist-cjs/index.d.ts +6 -1
  53. package/dist-cjs/instruction-errors.cjs +2 -7
  54. package/dist-cjs/instruction-optimizer.cjs +4 -10
  55. package/dist-cjs/instructions-parser.cjs +5 -10
  56. package/dist-cjs/logger-factory.cjs +3 -6
  57. package/dist-cjs/memory-path-resolution.cjs +8 -18
  58. package/dist-cjs/message-builder.cjs +11 -47
  59. package/dist-cjs/object-types-library-integration.cjs +3 -8
  60. package/dist-cjs/object-types-library.cjs +5 -10
  61. package/dist-cjs/output-auditor.cjs +1 -4
  62. package/dist-cjs/output-contract-normalizer.cjs +121 -0
  63. package/dist-cjs/output-contract-normalizer.d.ts +21 -0
  64. package/dist-cjs/request-report-generator.cjs +1 -4
  65. package/dist-cjs/response-analyzer/format-type-detector.cjs +1 -5
  66. package/dist-cjs/response-analyzer/index.cjs +3 -9
  67. package/dist-cjs/response-analyzer/object-type-detector.cjs +1 -5
  68. package/dist-cjs/response-analyzer/response-analyzer.cjs +6 -10
  69. package/dist-cjs/response-analyzer/types.cjs +1 -2
  70. package/dist-cjs/response-fallback-fixer.cjs +1 -4
  71. package/dist-cjs/runtime-objects.cjs +7 -13
  72. package/dist-cjs/template-parser.cjs +5 -42
  73. package/dist-cjs/template-render-merge.cjs +2 -6
  74. package/dist-cjs/troubleshooting-helper.cjs +13 -28
  75. package/dist-cjs/types.cjs +1 -2
  76. package/dist-cjs/types.d.ts +35 -0
  77. package/dist-cjs/usage-tracker.cjs +3 -7
  78. package/package.json +11 -5
@@ -1,16 +1,12 @@
1
- "use strict";
2
1
  /**
3
2
  * Activity Manager
4
3
  *
5
4
  * Manages activity tracking for LLM requests.
6
5
  * Wraps the ActivityTracker and provides convenience methods.
7
6
  */
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.ActivityManager = void 0;
10
- exports.ensureGatewayRequestIdentity = ensureGatewayRequestIdentity;
11
- const activix_1 = require("@x12i/activix");
12
- const activity_tracking_config_js_1 = require("./config/activity-tracking-config.cjs");
13
- const gateway_log_meta_js_1 = require("./gateway-log-meta.cjs");
7
+ import { Activix, activixActivityIo, activixOuterTier, resolveActivixLogsDatabaseName, resolveActivixMongoUriFromEnv } from '@x12i/activix';
8
+ import { resolveActivityTrackingConfig } from './config/activity-tracking-config.js';
9
+ import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
14
10
  function readAiRequestIdFromRequest(request) {
15
11
  const aiRequestId = request.aiRequestId;
16
12
  if (typeof aiRequestId === 'string' && aiRequestId.trim().length > 0) {
@@ -116,10 +112,10 @@ function resolveSessionIdForRequest(request, incomingIdentity, aiRequestId) {
116
112
  function logUpstreamIdentityWarnings(logger, incomingIdentity, merged) {
117
113
  const aiRequestId = merged.aiRequestId;
118
114
  if (!incomingIdentity || typeof incomingIdentity !== 'object') {
119
- logger?.warn('missingRuntimeIdentityObject', (0, gateway_log_meta_js_1.withActivityIdentity)(merged, {
115
+ logger?.warn('missingRuntimeIdentityObject', withActivityIdentity(merged, {
120
116
  message: 'request.identity is missing; upstream must send the mandatory runtime identity object',
121
117
  aiRequestId,
122
- debugKind: gateway_log_meta_js_1.gatewayLogDebug.anomaly
118
+ debugKind: gatewayLogDebug.anomaly
123
119
  }));
124
120
  return;
125
121
  }
@@ -129,11 +125,11 @@ function logUpstreamIdentityWarnings(logger, incomingIdentity, merged) {
129
125
  if (!trimIdentityString(incomingIdentity.taskId))
130
126
  missing.push('taskId');
131
127
  if (missing.length > 0) {
132
- logger?.warn('missingUpstreamIdentityFields', (0, gateway_log_meta_js_1.withActivityIdentity)(merged, {
128
+ logger?.warn('missingUpstreamIdentityFields', withActivityIdentity(merged, {
133
129
  message: `Upstream identity is missing required field(s): ${missing.join(', ')}. jobId and taskId must be set on request.identity by the client; the gateway does not generate them.`,
134
130
  missingFields: missing,
135
131
  aiRequestId,
136
- debugKind: gateway_log_meta_js_1.gatewayLogDebug.anomaly
132
+ debugKind: gatewayLogDebug.anomaly
137
133
  }));
138
134
  }
139
135
  }
@@ -159,6 +155,12 @@ function pickActivixCompletionRoutingMetadata(response) {
159
155
  if (m.effectiveModelConfig != null && typeof m.effectiveModelConfig === 'object') {
160
156
  out.effectiveModelConfig = m.effectiveModelConfig;
161
157
  }
158
+ if (typeof m.cost === 'number' && Number.isFinite(m.cost))
159
+ out.cost = m.cost;
160
+ if (typeof m.costUsd === 'number' && Number.isFinite(m.costUsd))
161
+ out.costUsd = m.costUsd;
162
+ if (m.costStatus === 'priced' || m.costStatus === 'unpriced')
163
+ out.costStatus = m.costStatus;
162
164
  return out;
163
165
  }
164
166
  function mergeGatewayActivityIdentity(request, aiRequestId, extras) {
@@ -212,7 +214,7 @@ function mergeGatewayActivityIdentity(request, aiRequestId, extras) {
212
214
  *
213
215
  * @param logger - Optional Logxer used to WARN when mandatory upstream `identity.jobId` / `identity.taskId` are absent.
214
216
  */
215
- function ensureGatewayRequestIdentity(request, extras, logger) {
217
+ export function ensureGatewayRequestIdentity(request, extras, logger) {
216
218
  const aiRequestId = readAiRequestIdFromRequest(request);
217
219
  const identity = mergeGatewayActivityIdentity(request, aiRequestId, {
218
220
  ...graphIdentityExtrasFromRequest(request),
@@ -230,12 +232,12 @@ function buildActivixStartIo(aiRequestId, activityType, requestData) {
230
232
  Object.keys(requestData).length > 0
231
233
  ? { activityType, request: requestData }
232
234
  : { activityType };
233
- return (0, activix_1.activixActivityIo)((0, activix_1.activixOuterTier)(input, null, { aiRequestId }));
235
+ return activixActivityIo(activixOuterTier(input, null, { aiRequestId }));
234
236
  }
235
237
  /**
236
238
  * Manages activity tracking lifecycle
237
239
  */
238
- class ActivityManager {
240
+ export class ActivityManager {
239
241
  activix;
240
242
  initPromise;
241
243
  mainCollectionName;
@@ -245,7 +247,7 @@ class ActivityManager {
245
247
  constructor(config) {
246
248
  this.logger = config.logger;
247
249
  if (config.enableActivityTracking) {
248
- const { collectionName, badRequestsCollectionName } = (0, activity_tracking_config_js_1.resolveActivityTrackingConfig)();
250
+ const { collectionName, badRequestsCollectionName } = resolveActivityTrackingConfig();
249
251
  this.mainCollectionName = collectionName;
250
252
  this.badRequestsCollectionName = badRequestsCollectionName;
251
253
  try {
@@ -257,7 +259,7 @@ class ActivityManager {
257
259
  failed: 'failed',
258
260
  timeout: 'timeout'
259
261
  };
260
- this.activix = config.customTracker ?? new activix_1.Activix({
262
+ this.activix = config.customTracker ?? new Activix({
261
263
  // Keep mode explicit for operational clarity (matches integration checklist expectations).
262
264
  storageMode: 'automatic',
263
265
  collections: [
@@ -306,8 +308,8 @@ class ActivityManager {
306
308
  return;
307
309
  }
308
310
  const backend = ax.storageBackend;
309
- const mongoDb = backend === 'database' ? (0, activix_1.resolveActivixLogsDatabaseName)() : undefined;
310
- const mongoUriConfigured = Boolean((0, activix_1.resolveActivixMongoUriFromEnv)());
311
+ const mongoDb = backend === 'database' ? resolveActivixLogsDatabaseName() : undefined;
312
+ const mongoUriConfigured = Boolean(resolveActivixMongoUriFromEnv());
311
313
  this.logger.info('Activity tracking persistence backend ready', {
312
314
  storageBackend: backend,
313
315
  mongoDatabase: mongoDb,
@@ -848,6 +850,7 @@ class ActivityManager {
848
850
  }
849
851
  await this.activix.completeRecord(activity.activityId, {
850
852
  cost: details.cost,
853
+ ...(details.costStatus ? { costStatus: details.costStatus } : {}),
851
854
  response: details.response,
852
855
  outer: {
853
856
  output: details.response,
@@ -1155,4 +1158,3 @@ class ActivityManager {
1155
1158
  }
1156
1159
  }
1157
1160
  }
1158
- exports.ActivityManager = ActivityManager;
@@ -120,6 +120,7 @@ export declare class ActivityManager {
120
120
  */
121
121
  logSuccess(activity: ActivityMetadata | undefined, details: {
122
122
  cost?: number;
123
+ costStatus?: 'priced' | 'unpriced';
123
124
  response: any;
124
125
  endTime: number;
125
126
  duration: number;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Lazy @x12i/ai-tools catalog + cost calculator bootstrap.
3
+ */
4
+ import { AiModelsCatalogClient, CostCalculator, ensureAiModelsCatalog } from '@x12i/ai-tools';
5
+ import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
6
+ let sharedClientPromise = null;
7
+ let sharedConfigKey;
8
+ let bootstrapFailedLogged = false;
9
+ function configKey(config) {
10
+ const injected = config.aiTools?.catalox ? 'injected' : 'env';
11
+ return `${injected}:${config.aiTools?.cacheTtlMs ?? ''}:${config.aiTools?.costIncludeBreakdown ?? ''}`;
12
+ }
13
+ /**
14
+ * Returns catalog + calculator, or null when disabled or bootstrap fails.
15
+ */
16
+ export async function getAiToolsClient(config, logger) {
17
+ if (config.aiTools?.enabled === false) {
18
+ return null;
19
+ }
20
+ const key = configKey(config);
21
+ if (sharedClientPromise && sharedConfigKey !== key) {
22
+ sharedClientPromise = null;
23
+ }
24
+ sharedConfigKey = key;
25
+ if (!sharedClientPromise) {
26
+ sharedClientPromise = bootstrapAiTools(config, logger);
27
+ }
28
+ return sharedClientPromise;
29
+ }
30
+ /** Reset singleton (tests). */
31
+ export function resetAiToolsClientForTests() {
32
+ sharedClientPromise = null;
33
+ sharedConfigKey = undefined;
34
+ bootstrapFailedLogged = false;
35
+ }
36
+ async function bootstrapAiTools(config, logger) {
37
+ try {
38
+ let catalox = config.aiTools?.catalox;
39
+ if (!catalox) {
40
+ const { createCataloxFromEnv } = await import('@x12i/catalox/firebase');
41
+ const bootstrapped = createCataloxFromEnv();
42
+ catalox = bootstrapped.catalox;
43
+ }
44
+ await ensureAiModelsCatalog(catalox);
45
+ const catalog = new AiModelsCatalogClient({
46
+ catalox,
47
+ cacheTtlMs: config.aiTools?.cacheTtlMs
48
+ });
49
+ const calculator = new CostCalculator(catalog, {
50
+ includeBreakdown: config.aiTools?.costIncludeBreakdown === true
51
+ });
52
+ logger.debug('ai-tools catalog client ready', {
53
+ debugKind: gatewayLogDebug.state
54
+ });
55
+ return { catalog, calculator };
56
+ }
57
+ catch (error) {
58
+ if (!bootstrapFailedLogged) {
59
+ bootstrapFailedLogged = true;
60
+ logger.warn('ai-tools catalog bootstrap failed; model resolution and catalog cost calculation disabled', withActivityIdentity(undefined, {
61
+ error: error instanceof Error ? error.message : String(error),
62
+ debugKind: gatewayLogDebug.anomaly
63
+ }));
64
+ }
65
+ return null;
66
+ }
67
+ }
68
+ /**
69
+ * Map catalog resolution to router config provider/model fields.
70
+ */
71
+ export function applyModelResolution(merged, resolution, gatewayDefaultEngine) {
72
+ if (resolution.routedViaOpenRouter) {
73
+ merged.provider = 'openrouter';
74
+ merged.model = resolution.modelId;
75
+ return;
76
+ }
77
+ const slash = resolution.modelId.indexOf('/');
78
+ if (slash > 0) {
79
+ merged.provider = resolution.record?.providerId ?? resolution.modelId.slice(0, slash);
80
+ merged.model = resolution.modelId.slice(slash + 1);
81
+ }
82
+ else {
83
+ merged.model = resolution.modelId;
84
+ if (resolution.record?.providerId) {
85
+ merged.provider = resolution.record.providerId;
86
+ }
87
+ }
88
+ if (!merged.provider && gatewayDefaultEngine) {
89
+ merged.provider = gatewayDefaultEngine;
90
+ }
91
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Lazy @x12i/ai-tools catalog + cost calculator bootstrap.
3
+ */
4
+ import { AiModelsCatalogClient, CostCalculator, type ModelResolutionSuccess } from '@x12i/ai-tools';
5
+ import type { Logxer } from '@x12i/logxer';
6
+ import type { ChatRequest, GatewayConfig } from './types.js';
7
+ export type AiToolsClientBundle = {
8
+ catalog: AiModelsCatalogClient;
9
+ calculator: CostCalculator;
10
+ };
11
+ /**
12
+ * Returns catalog + calculator, or null when disabled or bootstrap fails.
13
+ */
14
+ export declare function getAiToolsClient(config: GatewayConfig, logger: Logxer): Promise<AiToolsClientBundle | null>;
15
+ /** Reset singleton (tests). */
16
+ export declare function resetAiToolsClientForTests(): void;
17
+ /**
18
+ * Map catalog resolution to router config provider/model fields.
19
+ */
20
+ export declare function applyModelResolution(merged: NonNullable<ChatRequest['config']>, resolution: ModelResolutionSuccess, gatewayDefaultEngine?: string): void;
@@ -1,14 +1,11 @@
1
- "use strict";
2
1
  /**
3
2
  * Centralized activity tracking configuration.
4
3
  * Package-level Mongo collection names are fixed literals here (no env override).
5
4
  * Main gateway rows: `ai-actions`; bad requests: `bad-requests` (see constants below).
6
5
  */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.resolveActivityTrackingConfig = resolveActivityTrackingConfig;
9
6
  const ACTIVITY_COLLECTION_NAME = 'ai-actions';
10
7
  const BAD_REQUESTS_COLLECTION_NAME = 'bad-requests';
11
- function resolveActivityTrackingConfig() {
8
+ export function resolveActivityTrackingConfig() {
12
9
  // Collection names are intentionally hardcoded at package level.
13
10
  return {
14
11
  mongoUri: '',
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  /**
3
2
  * Content Normalizer
4
3
  *
@@ -6,10 +5,6 @@
6
5
  * Fixes the "[object Object]" bug by properly handling objects.
7
6
  * Preserves both rawText and parsedContent for downstream consumers.
8
7
  */
9
- Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.normalizeContent = normalizeContent;
11
- exports.isEmptyContent = isEmptyContent;
12
- exports.getResponseDiagnostics = getResponseDiagnostics;
13
8
  /**
14
9
  * Extracts content from router response, checking multiple possible locations
15
10
  *
@@ -187,7 +182,7 @@ function determineContentType(value, parsedContent) {
187
182
  * @param response - Router response object
188
183
  * @returns Normalized content with metadata
189
184
  */
190
- function normalizeContent(response, logger) {
185
+ export function normalizeContent(response, logger) {
191
186
  // Extract content value from response
192
187
  const contentValue = extractContentValue(response);
193
188
  // CRITICAL: Check if router already converted object to "[object Object]" string
@@ -375,7 +370,7 @@ function normalizeContent(response, logger) {
375
370
  * @param normalized - Normalized content result
376
371
  * @returns True if content is empty
377
372
  */
378
- function isEmptyContent(normalized) {
373
+ export function isEmptyContent(normalized) {
379
374
  return !normalized.content || normalized.content.trim().length === 0;
380
375
  }
381
376
  /**
@@ -385,7 +380,7 @@ function isEmptyContent(normalized) {
385
380
  * @param response - Router response object
386
381
  * @returns Diagnostic information
387
382
  */
388
- function getResponseDiagnostics(response) {
383
+ export function getResponseDiagnostics(response) {
389
384
  return {
390
385
  responseKeys: Object.keys(response),
391
386
  hasMessage: !!(response?.message),
@@ -1,12 +1,6 @@
1
- "use strict";
2
1
  /**
3
2
  * Content Normalizer Module
4
3
  *
5
4
  * Exports all public APIs for content normalization
6
5
  */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.getResponseDiagnostics = exports.isEmptyContent = exports.normalizeContent = void 0;
9
- var content_normalizer_js_1 = require("./content-normalizer.cjs");
10
- Object.defineProperty(exports, "normalizeContent", { enumerable: true, get: function () { return content_normalizer_js_1.normalizeContent; } });
11
- Object.defineProperty(exports, "isEmptyContent", { enumerable: true, get: function () { return content_normalizer_js_1.isEmptyContent; } });
12
- Object.defineProperty(exports, "getResponseDiagnostics", { enumerable: true, get: function () { return content_normalizer_js_1.getResponseDiagnostics; } });
6
+ export { normalizeContent, isEmptyContent, getResponseDiagnostics } from './content-normalizer.js';
@@ -1,5 +1,4 @@
1
- "use strict";
2
1
  /**
3
2
  * Type definitions for Content Normalizer
4
3
  */
5
- Object.defineProperty(exports, "__esModule", { value: true });
4
+ export {};
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  /**
3
2
  * @x12i/flex-md Loader
4
3
  *
@@ -14,54 +13,9 @@
14
13
  * - Native output format support: Extracts and uses flex-md native output formats from instructions
15
14
  * based on compliance level when no explicit format is provided
16
15
  */
17
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
- if (k2 === undefined) k2 = k;
19
- var desc = Object.getOwnPropertyDescriptor(m, k);
20
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
- desc = { enumerable: true, get: function() { return m[k]; } };
22
- }
23
- Object.defineProperty(o, k2, desc);
24
- }) : (function(o, m, k, k2) {
25
- if (k2 === undefined) k2 = k;
26
- o[k2] = m[k];
27
- }));
28
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
- Object.defineProperty(o, "default", { enumerable: true, value: v });
30
- }) : function(o, v) {
31
- o["default"] = v;
32
- });
33
- var __importStar = (this && this.__importStar) || (function () {
34
- var ownKeys = function(o) {
35
- ownKeys = Object.getOwnPropertyNames || function (o) {
36
- var ar = [];
37
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
- return ar;
39
- };
40
- return ownKeys(o);
41
- };
42
- return function (mod) {
43
- if (mod && mod.__esModule) return mod;
44
- var result = {};
45
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
- __setModuleDefault(result, mod);
47
- return result;
48
- };
49
- })();
50
- Object.defineProperty(exports, "__esModule", { value: true });
51
- exports.loadFlexMd = loadFlexMd;
52
- exports.extractJsonFromFlexMd = extractJsonFromFlexMd;
53
- exports.isFlexMdAvailable = isFlexMdAvailable;
54
- exports.getModelMaxTokensFromFlexMd = getModelMaxTokensFromFlexMd;
55
- exports.getFlexMdModule = getFlexMdModule;
56
- exports.buildDynamicInstructions = buildDynamicInstructions;
57
- exports.enrichInstructionsWithFlexMd = enrichInstructionsWithFlexMd;
58
- exports.getStrictnessDefaults = getStrictnessDefaults;
59
- exports.enforceFlexMdFormat = enforceFlexMdFormat;
60
- exports.hasFlexMdContract = hasFlexMdContract;
61
- exports.detectComplianceLevel = detectComplianceLevel;
62
- const path = __importStar(require("path"));
63
- const url_1 = require("url");
64
- const logxer_1 = require("@x12i/logxer");
16
+ import * as path from 'path';
17
+ import { pathToFileURL } from 'url';
18
+ import { DebugLogAbstract } from '@x12i/logxer';
65
19
  let flexMdModule = null;
66
20
  let flexMdLoadError = null;
67
21
  let flexMdLoading = null;
@@ -78,7 +32,7 @@ async function importFlexMdModule() {
78
32
  const { createRequire } = await importModule('module');
79
33
  const requireFn = typeof require === 'function' ? require : createRequire(path.join(process.cwd(), 'noop.cjs'));
80
34
  const cjsEntryPath = requireFn.resolve('@x12i/flex-md');
81
- const cjsEntryUrl = (0, url_1.pathToFileURL)(cjsEntryPath).href;
35
+ const cjsEntryUrl = pathToFileURL(cjsEntryPath).href;
82
36
  try {
83
37
  return await importModule(cjsEntryUrl);
84
38
  }
@@ -96,7 +50,7 @@ async function importFlexMdModule() {
96
50
  * Load flex-md module (ES Module) asynchronously
97
51
  * Caches the result to avoid multiple imports
98
52
  */
99
- async function loadFlexMd() {
53
+ export async function loadFlexMd() {
100
54
  // Return cached module if available
101
55
  if (flexMdModule) {
102
56
  return flexMdModule;
@@ -375,7 +329,7 @@ async function extractFlexMdOutputFormatFromInstructions(instructions, complianc
375
329
  * - Other flex-md options
376
330
  * @returns Parsed JSON object and method used, or null if parsing fails
377
331
  */
378
- async function extractJsonFromFlexMd(content, logger) {
332
+ export async function extractJsonFromFlexMd(content, logger) {
379
333
  // ARCHITECTURE: ai-gateway layer only handles unstructured parsing
380
334
  // Schema-driven parsing is handled at skills layer with nx-md-parser
381
335
  // No schema parameter - this layer doesn't know about schemas
@@ -405,8 +359,8 @@ async function extractJsonFromFlexMd(content, logger) {
405
359
  if (result && typeof result === 'object' && result !== null) {
406
360
  const json = result.json || result.data || result.result || result;
407
361
  if (json && typeof json === 'object' && json !== null) {
408
- logger?.debug('extractJsonFromFlexMd: using extractFromMarkdown', { debugKind: logxer_1.DebugLogAbstract.TRACE });
409
- logger?.debug('extractJsonFromFlexMd: parsed json', { json, debugKind: logxer_1.DebugLogAbstract.STATE });
362
+ logger?.debug('extractJsonFromFlexMd: using extractFromMarkdown', { debugKind: DebugLogAbstract.TRACE });
363
+ logger?.debug('extractJsonFromFlexMd: parsed json', { json, debugKind: DebugLogAbstract.STATE });
410
364
  return { json, method: 'extractFromMarkdown' };
411
365
  }
412
366
  }
@@ -530,11 +484,11 @@ function fallbackMarkdownParser(content, logger) {
530
484
  result[key] = parseMarkdownList(currentContent);
531
485
  detectedHeaders.push(key);
532
486
  }
533
- logger?.debug('Fallback parser detected headers', { detectedHeaders, debugKind: logxer_1.DebugLogAbstract.STATE });
487
+ logger?.debug('Fallback parser detected headers', { detectedHeaders, debugKind: DebugLogAbstract.STATE });
534
488
  const expectedHeaders = ['shortAnswer', 'fullAnswer', 'assumptions', 'unknowns', 'evidence'];
535
489
  const missingHeaders = expectedHeaders.filter(h => !detectedHeaders.includes(h));
536
490
  if (missingHeaders.length > 0) {
537
- logger?.debug('Fallback parser missing expected headers', { missingHeaders, debugKind: logxer_1.DebugLogAbstract.STATE });
491
+ logger?.debug('Fallback parser missing expected headers', { missingHeaders, debugKind: DebugLogAbstract.STATE });
538
492
  }
539
493
  // If we extracted some structure, return it
540
494
  if (Object.keys(result).length > 0) {
@@ -549,6 +503,22 @@ function fallbackMarkdownParser(content, logger) {
549
503
  method: 'fallback-raw-text'
550
504
  };
551
505
  }
506
+ /**
507
+ * Section-based markdown → camelCase field map (e.g. `### Short Answer` → `shortAnswer`).
508
+ * Used when output contracts need structured `parsed` fields but flex-md returned only `rawText`.
509
+ */
510
+ export function parseMarkdownSectionsFromContent(content, logger) {
511
+ const parsed = fallbackMarkdownParser(content, logger);
512
+ const json = parsed.json;
513
+ if (json != null && typeof json === 'object' && !Array.isArray(json)) {
514
+ const keys = Object.keys(json);
515
+ if (keys.length === 1 && keys[0] === 'rawText') {
516
+ return {};
517
+ }
518
+ return json;
519
+ }
520
+ return {};
521
+ }
552
522
  /**
553
523
  * Fallback JSON extraction when flex-md is not available
554
524
  */
@@ -580,7 +550,7 @@ function extractJsonFallback(content) {
580
550
  /**
581
551
  * Check if flex-md module is available
582
552
  */
583
- function isFlexMdAvailable() {
553
+ export function isFlexMdAvailable() {
584
554
  return flexMdModule !== null;
585
555
  }
586
556
  /**
@@ -591,7 +561,7 @@ function isFlexMdAvailable() {
591
561
  * @param model - Model name (e.g., 'gpt-4', 'claude-3-opus')
592
562
  * @returns maxTokens if found, null otherwise
593
563
  */
594
- async function getModelMaxTokensFromFlexMd(provider, model) {
564
+ export async function getModelMaxTokensFromFlexMd(provider, model) {
595
565
  try {
596
566
  const flexMd = await loadFlexMd();
597
567
  if (!flexMd || typeof flexMd !== 'object') {
@@ -670,7 +640,7 @@ async function getModelMaxTokensFromFlexMd(provider, model) {
670
640
  /**
671
641
  * Get flex-md module directly (must call loadFlexMd() first)
672
642
  */
673
- function getFlexMdModule() {
643
+ export function getFlexMdModule() {
674
644
  if (!flexMdModule) {
675
645
  throw new Error('flex-md module not loaded. Call loadFlexMd() first.');
676
646
  }
@@ -737,7 +707,7 @@ function generateFormatInstructionsFromSchema(schema, options) {
737
707
  parts.push('\n\nIMPORTANT: Return your answer as JSON inside the markdown fenced block. The JSON must match the schema above exactly.');
738
708
  return parts.join('\n');
739
709
  }
740
- async function buildDynamicInstructions(schemaObject, options) {
710
+ export async function buildDynamicInstructions(schemaObject, options) {
741
711
  try {
742
712
  const flexMd = await loadFlexMd();
743
713
  // Check if dynamic instruction feature is available (flex-md 3.1+)
@@ -769,7 +739,7 @@ async function buildDynamicInstructions(schemaObject, options) {
769
739
  * @param strictnessLevel - Compliance level ('L0' | 'L1' | 'L2' | 'L3')
770
740
  * @returns Enriched instructions with flex-md guidance
771
741
  */
772
- async function enrichInstructionsWithFlexMd(instructions, strictnessLevel = 'L0') {
742
+ export async function enrichInstructionsWithFlexMd(instructions, strictnessLevel = 'L0') {
773
743
  try {
774
744
  const flexMd = await loadFlexMd();
775
745
  // Use native flex-md function if available (flex-md 3.2+)
@@ -797,7 +767,7 @@ async function enrichInstructionsWithFlexMd(instructions, strictnessLevel = 'L0'
797
767
  * @param strictnessLevel - Compliance level ('L0' | 'L1' | 'L2' | 'L3')
798
768
  * @returns Configuration object with level-specific defaults
799
769
  */
800
- async function getStrictnessDefaults(strictnessLevel = 'L0') {
770
+ export async function getStrictnessDefaults(strictnessLevel = 'L0') {
801
771
  try {
802
772
  const flexMd = await loadFlexMd();
803
773
  if (flexMd.strictnessDefaults && typeof flexMd.strictnessDefaults === 'function') {
@@ -818,7 +788,7 @@ async function getStrictnessDefaults(strictnessLevel = 'L0') {
818
788
  * @param strictnessLevel - Compliance level ('L0' | 'L1' | 'L2' | 'L3')
819
789
  * @returns Instructions with flex-md enforcement rules
820
790
  */
821
- async function enforceFlexMdFormat(instructions, outputFormatSpec, strictnessLevel = 'L0') {
791
+ export async function enforceFlexMdFormat(instructions, outputFormatSpec, strictnessLevel = 'L0') {
822
792
  try {
823
793
  const flexMd = await loadFlexMd();
824
794
  if (flexMd.enforceFlexMd && typeof flexMd.enforceFlexMd === 'function') {
@@ -844,7 +814,7 @@ async function enforceFlexMdFormat(instructions, outputFormatSpec, strictnessLev
844
814
  * @param complianceLevel - Required compliance level
845
815
  * @returns true if instructions meet the compliance level
846
816
  */
847
- async function hasFlexMdContract(instructions, complianceLevel = 'L0') {
817
+ export async function hasFlexMdContract(instructions, complianceLevel = 'L0') {
848
818
  if (!instructions || instructions.trim() === '') {
849
819
  return false;
850
820
  }
@@ -912,7 +882,7 @@ async function hasFlexMdContract(instructions, complianceLevel = 'L0') {
912
882
  * @param instructions - Instructions text to check
913
883
  * @returns The highest compliance level met ('L0' | 'L1' | 'L2' | 'L3' | null if none)
914
884
  */
915
- async function detectComplianceLevel(instructions) {
885
+ export async function detectComplianceLevel(instructions) {
916
886
  if (!instructions || instructions.trim() === '') {
917
887
  return null;
918
888
  }
@@ -43,6 +43,11 @@ export declare function extractJsonFromFlexMd(content: string, logger?: Logxer):
43
43
  json: any;
44
44
  method: string;
45
45
  } | null>;
46
+ /**
47
+ * Section-based markdown → camelCase field map (e.g. `### Short Answer` → `shortAnswer`).
48
+ * Used when output contracts need structured `parsed` fields but flex-md returned only `rawText`.
49
+ */
50
+ export declare function parseMarkdownSectionsFromContent(content: string, logger?: Logxer): Record<string, unknown>;
46
51
  /**
47
52
  * Check if flex-md module is available
48
53
  */