@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.
- package/README.md +45 -0
- package/dist/activity-manager.d.ts +1 -0
- package/dist/activity-manager.js +7 -0
- package/dist/ai-tools-client.d.ts +20 -0
- package/dist/ai-tools-client.js +91 -0
- package/dist/flex-md-loader.d.ts +5 -0
- package/dist/flex-md-loader.js +16 -0
- package/dist/gateway-config.d.ts +2 -0
- package/dist/gateway-config.js +2 -1
- package/dist/gateway-mode.d.ts +40 -0
- package/dist/gateway-mode.js +75 -0
- package/dist/gateway-utils.d.ts +57 -1
- package/dist/gateway-utils.js +181 -12
- package/dist/gateway.d.ts +3 -0
- package/dist/gateway.js +47 -15
- package/dist/index.d.ts +6 -1
- package/dist/index.js +3 -1
- package/dist/output-contract-normalizer.d.ts +21 -0
- package/dist/output-contract-normalizer.js +121 -0
- package/dist/types.d.ts +35 -0
- package/dist-cjs/activity-manager.cjs +21 -19
- package/dist-cjs/activity-manager.d.ts +1 -0
- package/dist-cjs/ai-tools-client.cjs +91 -0
- package/dist-cjs/ai-tools-client.d.ts +20 -0
- package/dist-cjs/config/activity-tracking-config.cjs +1 -4
- package/dist-cjs/content-normalizer/content-normalizer.cjs +3 -8
- package/dist-cjs/content-normalizer/index.cjs +1 -7
- package/dist-cjs/content-normalizer/types.cjs +1 -2
- package/dist-cjs/flex-md-loader.cjs +35 -65
- package/dist-cjs/flex-md-loader.d.ts +5 -0
- package/dist-cjs/gateway-config.cjs +25 -63
- package/dist-cjs/gateway-config.d.ts +2 -0
- package/dist-cjs/gateway-conversion.cjs +10 -48
- package/dist-cjs/gateway-instructions.cjs +5 -10
- package/dist-cjs/gateway-log-meta.cjs +9 -14
- package/dist-cjs/gateway-memory.cjs +2 -6
- package/dist-cjs/gateway-messages.cjs +3 -6
- package/dist-cjs/gateway-meta.cjs +1 -4
- package/dist-cjs/gateway-mode.cjs +75 -0
- package/dist-cjs/gateway-mode.d.ts +40 -0
- package/dist-cjs/gateway-provider-auto-register.cjs +2 -38
- package/dist-cjs/gateway-provider.cjs +10 -22
- package/dist-cjs/gateway-rate-limiter-constants.cjs +2 -5
- package/dist-cjs/gateway-rate-limiter.cjs +5 -9
- package/dist-cjs/gateway-retry.cjs +6 -14
- package/dist-cjs/gateway-utils.cjs +201 -83
- package/dist-cjs/gateway-utils.d.ts +57 -1
- package/dist-cjs/gateway-validation.cjs +2 -6
- package/dist-cjs/gateway.cjs +100 -72
- package/dist-cjs/gateway.d.ts +3 -0
- package/dist-cjs/index.cjs +22 -91
- package/dist-cjs/index.d.ts +6 -1
- package/dist-cjs/instruction-errors.cjs +2 -7
- package/dist-cjs/instruction-optimizer.cjs +4 -10
- package/dist-cjs/instructions-parser.cjs +5 -10
- package/dist-cjs/logger-factory.cjs +3 -6
- package/dist-cjs/memory-path-resolution.cjs +8 -18
- package/dist-cjs/message-builder.cjs +11 -47
- package/dist-cjs/object-types-library-integration.cjs +3 -8
- package/dist-cjs/object-types-library.cjs +5 -10
- package/dist-cjs/output-auditor.cjs +1 -4
- package/dist-cjs/output-contract-normalizer.cjs +121 -0
- package/dist-cjs/output-contract-normalizer.d.ts +21 -0
- package/dist-cjs/request-report-generator.cjs +1 -4
- package/dist-cjs/response-analyzer/format-type-detector.cjs +1 -5
- package/dist-cjs/response-analyzer/index.cjs +3 -9
- package/dist-cjs/response-analyzer/object-type-detector.cjs +1 -5
- package/dist-cjs/response-analyzer/response-analyzer.cjs +6 -10
- package/dist-cjs/response-analyzer/types.cjs +1 -2
- package/dist-cjs/response-fallback-fixer.cjs +1 -4
- package/dist-cjs/runtime-objects.cjs +7 -13
- package/dist-cjs/template-parser.cjs +5 -42
- package/dist-cjs/template-render-merge.cjs +2 -6
- package/dist-cjs/troubleshooting-helper.cjs +13 -28
- package/dist-cjs/types.cjs +1 -2
- package/dist-cjs/types.d.ts +35 -0
- package/dist-cjs/usage-tracker.cjs +3 -7
- 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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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',
|
|
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:
|
|
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',
|
|
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:
|
|
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
|
|
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 } =
|
|
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
|
|
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' ?
|
|
310
|
-
const mongoUriConfigured = Boolean(
|
|
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;
|
|
@@ -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
|
-
|
|
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,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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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 =
|
|
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:
|
|
409
|
-
logger?.debug('extractJsonFromFlexMd: parsed json', { json, debugKind:
|
|
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:
|
|
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:
|
|
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
|
*/
|