@x12i/ai-gateway 7.9.2 → 9.0.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.
- package/README.md +107 -94
- package/dist/activity-manager.d.ts +3 -1
- package/dist/activity-manager.js +48 -17
- package/dist/content-normalizer/content-normalizer.d.ts +2 -1
- package/dist/content-normalizer/content-normalizer.js +5 -5
- package/dist/flex-md-loader.d.ts +2 -1
- package/dist/flex-md-loader.js +18 -15
- package/dist/gateway-config.d.ts +4 -4
- package/dist/gateway-config.js +21 -25
- package/dist/gateway-conversion.js +2 -2
- package/dist/gateway-log-meta.d.ts +15 -0
- package/dist/gateway-log-meta.js +36 -0
- package/dist/gateway-memory.js +6 -6
- package/dist/gateway-messages.js +4 -4
- package/dist/gateway-meta.d.ts +3 -1
- package/dist/gateway-meta.js +9 -2
- package/dist/gateway-utils.js +9 -9
- package/dist/gateway-validation.js +9 -0
- package/dist/gateway.js +32 -33
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/instruction-optimizer.d.ts +6 -1
- package/dist/instruction-optimizer.js +10 -3
- package/dist/instructions-parser.d.ts +2 -1
- package/dist/instructions-parser.js +2 -2
- package/dist/logger-factory.js +12 -1
- package/dist/message-builder.js +19 -19
- package/dist/request-report-generator.js +1 -1
- package/dist/template-parser.d.ts +3 -1
- package/dist/template-parser.js +3 -2
- package/dist/troubleshooting-helper.d.ts +1 -1
- package/dist/troubleshooting-helper.js +2 -2
- package/dist/types.d.ts +16 -10
- package/dist-cjs/activity-manager.cjs +48 -17
- package/dist-cjs/activity-manager.d.ts +3 -1
- package/dist-cjs/content-normalizer/content-normalizer.cjs +5 -5
- package/dist-cjs/content-normalizer/content-normalizer.d.ts +2 -1
- package/dist-cjs/flex-md-loader.cjs +18 -15
- package/dist-cjs/flex-md-loader.d.ts +2 -1
- package/dist-cjs/gateway-config.cjs +21 -25
- package/dist-cjs/gateway-config.d.ts +4 -4
- package/dist-cjs/gateway-conversion.cjs +2 -2
- package/dist-cjs/gateway-log-meta.cjs +41 -0
- package/dist-cjs/gateway-log-meta.d.ts +15 -0
- package/dist-cjs/gateway-memory.cjs +6 -6
- package/dist-cjs/gateway-messages.cjs +4 -4
- package/dist-cjs/gateway-meta.cjs +9 -2
- package/dist-cjs/gateway-meta.d.ts +3 -1
- package/dist-cjs/gateway-utils.cjs +9 -9
- package/dist-cjs/gateway-validation.cjs +9 -0
- package/dist-cjs/gateway.cjs +31 -32
- package/dist-cjs/index.cjs +6 -1
- package/dist-cjs/index.d.ts +3 -2
- package/dist-cjs/instruction-optimizer.cjs +10 -3
- package/dist-cjs/instruction-optimizer.d.ts +6 -1
- package/dist-cjs/instructions-parser.cjs +2 -2
- package/dist-cjs/instructions-parser.d.ts +2 -1
- package/dist-cjs/logger-factory.cjs +12 -1
- package/dist-cjs/message-builder.cjs +19 -19
- package/dist-cjs/request-report-generator.cjs +1 -1
- package/dist-cjs/template-parser.cjs +3 -2
- package/dist-cjs/template-parser.d.ts +3 -1
- package/dist-cjs/troubleshooting-helper.cjs +2 -2
- package/dist-cjs/troubleshooting-helper.d.ts +1 -1
- package/dist-cjs/types.d.ts +16 -10
- package/package.json +4 -4
|
@@ -10,6 +10,7 @@ exports.ActivityManager = void 0;
|
|
|
10
10
|
exports.ensureGatewayRequestIdentity = ensureGatewayRequestIdentity;
|
|
11
11
|
const activix_1 = require("@x12i/activix");
|
|
12
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");
|
|
13
14
|
function readAiRequestIdFromRequest(request) {
|
|
14
15
|
const aiRequestId = request.aiRequestId;
|
|
15
16
|
if (typeof aiRequestId === 'string' && aiRequestId.trim().length > 0) {
|
|
@@ -106,40 +107,64 @@ function buildInstanceEnvelope(request) {
|
|
|
106
107
|
function resolveSessionIdForRequest(request, incomingIdentity, aiRequestId) {
|
|
107
108
|
const fromIdentity = trimIdentityString(incomingIdentity?.sessionId);
|
|
108
109
|
const topLevel = trimIdentityString(request.sessionId);
|
|
109
|
-
const
|
|
110
|
+
const identityJobId = trimIdentityString(incomingIdentity?.jobId);
|
|
110
111
|
if (fromIdentity !== undefined && topLevel !== undefined && fromIdentity !== topLevel) {
|
|
111
112
|
throw new Error(`identity.sessionId (${fromIdentity}) and top-level sessionId (${topLevel}) must agree`);
|
|
112
113
|
}
|
|
113
|
-
return fromIdentity ?? topLevel ??
|
|
114
|
+
return fromIdentity ?? topLevel ?? identityJobId ?? aiRequestId;
|
|
115
|
+
}
|
|
116
|
+
function logUpstreamIdentityWarnings(logger, incomingIdentity, merged) {
|
|
117
|
+
const aiRequestId = merged.aiRequestId;
|
|
118
|
+
if (!incomingIdentity || typeof incomingIdentity !== 'object') {
|
|
119
|
+
logger?.warn('missingRuntimeIdentityObject', (0, gateway_log_meta_js_1.withActivityIdentity)(merged, {
|
|
120
|
+
message: 'request.identity is missing; upstream must send the mandatory runtime identity object',
|
|
121
|
+
aiRequestId,
|
|
122
|
+
debugKind: gateway_log_meta_js_1.gatewayLogDebug.anomaly
|
|
123
|
+
}));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const missing = [];
|
|
127
|
+
if (!trimIdentityString(incomingIdentity.jobId))
|
|
128
|
+
missing.push('jobId');
|
|
129
|
+
if (!trimIdentityString(incomingIdentity.taskId))
|
|
130
|
+
missing.push('taskId');
|
|
131
|
+
if (missing.length > 0) {
|
|
132
|
+
logger?.warn('missingUpstreamIdentityFields', (0, gateway_log_meta_js_1.withActivityIdentity)(merged, {
|
|
133
|
+
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
|
+
missingFields: missing,
|
|
135
|
+
aiRequestId,
|
|
136
|
+
debugKind: gateway_log_meta_js_1.gatewayLogDebug.anomaly
|
|
137
|
+
}));
|
|
138
|
+
}
|
|
114
139
|
}
|
|
115
140
|
function mergeGatewayActivityIdentity(request, aiRequestId, extras) {
|
|
116
141
|
const incomingIdentity = request.identity;
|
|
117
142
|
const sessionId = resolveSessionIdForRequest(request, incomingIdentity, aiRequestId);
|
|
118
|
-
const
|
|
143
|
+
const upstreamJobId = trimIdentityString(incomingIdentity?.jobId) ?? '';
|
|
144
|
+
const upstreamTaskId = trimIdentityString(incomingIdentity?.taskId) ?? '';
|
|
119
145
|
const combinedEnrichment = pickGatewayEnrichmentFields({
|
|
120
146
|
...graphIdentityExtrasFromRequest(request),
|
|
121
147
|
...(extras || {})
|
|
122
148
|
});
|
|
123
149
|
const safeEnrichment = mergeActivixEnrichment(incomingIdentity, combinedEnrichment);
|
|
124
|
-
//
|
|
125
|
-
// Gateway enrichment (graph/skill, etc.) fills only keys the outer envelope did not already set.
|
|
150
|
+
// jobId / taskId: upstream `request.identity` only (never top-level request.jobId / request.taskId).
|
|
126
151
|
const merged = {
|
|
127
152
|
jobTypeId: request.jobTypeId,
|
|
128
153
|
agentId: request.agentId,
|
|
129
|
-
taskId: request.taskId,
|
|
130
154
|
taskTypeId: request.taskTypeId,
|
|
131
155
|
...safeEnrichment,
|
|
132
156
|
...(incomingIdentity || {}),
|
|
133
157
|
sessionId,
|
|
134
158
|
instance: buildInstanceEnvelope(request),
|
|
135
|
-
aiRequestId
|
|
159
|
+
aiRequestId,
|
|
160
|
+
jobId: upstreamJobId,
|
|
161
|
+
taskId: upstreamTaskId
|
|
136
162
|
};
|
|
137
163
|
merged.sessionId = sessionId;
|
|
138
164
|
merged.instance = buildInstanceEnvelope(request);
|
|
139
165
|
merged.aiRequestId = aiRequestId;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
166
|
+
merged.jobId = upstreamJobId;
|
|
167
|
+
merged.taskId = upstreamTaskId;
|
|
143
168
|
return merged;
|
|
144
169
|
}
|
|
145
170
|
/**
|
|
@@ -149,13 +174,16 @@ function mergeGatewayActivityIdentity(request, aiRequestId, extras) {
|
|
|
149
174
|
* **Execution context:** treats `request.identity` as received from upstream. Root fields (`sessionId`,
|
|
150
175
|
* `instance`, and any keys already set on that object) are not replaced by gateway defaults; the gateway
|
|
151
176
|
* only fills missing required pieces and adds local context (e.g. graph linkage) where absent.
|
|
177
|
+
*
|
|
178
|
+
* @param logger - Optional Logxer used to WARN when mandatory upstream `identity.jobId` / `identity.taskId` are absent.
|
|
152
179
|
*/
|
|
153
|
-
function ensureGatewayRequestIdentity(request, extras) {
|
|
180
|
+
function ensureGatewayRequestIdentity(request, extras, logger) {
|
|
154
181
|
const aiRequestId = readAiRequestIdFromRequest(request);
|
|
155
182
|
const identity = mergeGatewayActivityIdentity(request, aiRequestId, {
|
|
156
183
|
...graphIdentityExtrasFromRequest(request),
|
|
157
184
|
...(extras || {})
|
|
158
185
|
});
|
|
186
|
+
logUpstreamIdentityWarnings(logger, request.identity, identity);
|
|
159
187
|
request.identity = identity;
|
|
160
188
|
return identity;
|
|
161
189
|
}
|
|
@@ -291,7 +319,7 @@ class ActivityManager {
|
|
|
291
319
|
* @returns Activity metadata (contains unique _id/recordId) or undefined if tracking is disabled
|
|
292
320
|
*/
|
|
293
321
|
async startActivity(request, startTime) {
|
|
294
|
-
const identity = ensureGatewayRequestIdentity(request);
|
|
322
|
+
const identity = ensureGatewayRequestIdentity(request, undefined, this.logger);
|
|
295
323
|
const aiRequestId = identity.aiRequestId;
|
|
296
324
|
// Capture ONLY request data - NO response data at this stage
|
|
297
325
|
// Note: v2.3.2+ excludes flat request/config fields from root level
|
|
@@ -311,9 +339,10 @@ class ActivityManager {
|
|
|
311
339
|
}
|
|
312
340
|
const activityMetadata = {
|
|
313
341
|
aiRequestId,
|
|
342
|
+
jobId: identity.jobId,
|
|
314
343
|
jobTypeId: request.jobTypeId,
|
|
315
344
|
agentId: request.agentId,
|
|
316
|
-
taskId:
|
|
345
|
+
taskId: identity.taskId,
|
|
317
346
|
taskTypeId: request.taskTypeId,
|
|
318
347
|
startTime,
|
|
319
348
|
status: 'started',
|
|
@@ -507,7 +536,7 @@ class ActivityManager {
|
|
|
507
536
|
}
|
|
508
537
|
// Determine inference type (will be extracted in gateway.ts and passed via request metadata)
|
|
509
538
|
const inferenceType = aiRequest.inferenceType || 'question-answer';
|
|
510
|
-
const identity = ensureGatewayRequestIdentity(request);
|
|
539
|
+
const identity = ensureGatewayRequestIdentity(request, undefined, this.logger);
|
|
511
540
|
const aiRequestId = identity.aiRequestId;
|
|
512
541
|
if (!this.activix) {
|
|
513
542
|
return undefined;
|
|
@@ -525,9 +554,10 @@ class ActivityManager {
|
|
|
525
554
|
skillId: skillId ?? skillKey,
|
|
526
555
|
inferenceType, // ✅ Recommended: type of inference
|
|
527
556
|
aiRequestId,
|
|
557
|
+
jobId: identity.jobId,
|
|
528
558
|
jobTypeId: request.jobTypeId,
|
|
529
559
|
agentId: request.agentId,
|
|
530
|
-
taskId:
|
|
560
|
+
taskId: identity.taskId,
|
|
531
561
|
taskTypeId: request.taskTypeId,
|
|
532
562
|
startTime,
|
|
533
563
|
status: 'started',
|
|
@@ -901,7 +931,7 @@ class ActivityManager {
|
|
|
901
931
|
* @param startTime - When the request started
|
|
902
932
|
*/
|
|
903
933
|
async logBadRequest(request, error, details, startTime) {
|
|
904
|
-
const identity = ensureGatewayRequestIdentity(request);
|
|
934
|
+
const identity = ensureGatewayRequestIdentity(request, undefined, this.logger);
|
|
905
935
|
const aiRequestId = identity.aiRequestId;
|
|
906
936
|
const endTime = details.endTime;
|
|
907
937
|
const duration = details.duration;
|
|
@@ -926,9 +956,10 @@ class ActivityManager {
|
|
|
926
956
|
const badRequestMetadata = {
|
|
927
957
|
activityType: 'bad-request',
|
|
928
958
|
aiRequestId,
|
|
959
|
+
jobId: identity.jobId,
|
|
929
960
|
jobTypeId: request.jobTypeId,
|
|
930
961
|
agentId: request.agentId,
|
|
931
|
-
taskId:
|
|
962
|
+
taskId: identity.taskId,
|
|
932
963
|
taskTypeId: request.taskTypeId,
|
|
933
964
|
startTime,
|
|
934
965
|
status: 'started',
|
|
@@ -15,8 +15,10 @@ type Request = ChatRequest | AIRequest;
|
|
|
15
15
|
* **Execution context:** treats `request.identity` as received from upstream. Root fields (`sessionId`,
|
|
16
16
|
* `instance`, and any keys already set on that object) are not replaced by gateway defaults; the gateway
|
|
17
17
|
* only fills missing required pieces and adds local context (e.g. graph linkage) where absent.
|
|
18
|
+
*
|
|
19
|
+
* @param logger - Optional Logxer used to WARN when mandatory upstream `identity.jobId` / `identity.taskId` are absent.
|
|
18
20
|
*/
|
|
19
|
-
export declare function ensureGatewayRequestIdentity(request: ChatRequest | AIRequest, extras?: Partial<ActivityIdentity
|
|
21
|
+
export declare function ensureGatewayRequestIdentity(request: ChatRequest | AIRequest, extras?: Partial<ActivityIdentity>, logger?: Logxer): ActivityIdentity;
|
|
20
22
|
type ActivityMetadata = Record<string, any> & {
|
|
21
23
|
activityId?: string;
|
|
22
24
|
recordId?: string;
|
|
@@ -187,14 +187,14 @@ function determineContentType(value, parsedContent) {
|
|
|
187
187
|
* @param response - Router response object
|
|
188
188
|
* @returns Normalized content with metadata
|
|
189
189
|
*/
|
|
190
|
-
function normalizeContent(response) {
|
|
190
|
+
function normalizeContent(response, logger) {
|
|
191
191
|
// Extract content value from response
|
|
192
192
|
const contentValue = extractContentValue(response);
|
|
193
193
|
// CRITICAL: Check if router already converted object to "[object Object]" string
|
|
194
194
|
// This happens when router uses String() on objects before returning
|
|
195
195
|
if (typeof contentValue === 'string' && contentValue === '[object Object]') {
|
|
196
196
|
// Router already broke it - try to recover from other fields
|
|
197
|
-
|
|
197
|
+
logger?.error('[content-normalizer] Router returned "[object Object]" string - attempting recovery', {
|
|
198
198
|
responseKeys: Object.keys(response),
|
|
199
199
|
hasOutput: !!response.output,
|
|
200
200
|
hasRawText: !!response.rawText,
|
|
@@ -235,7 +235,7 @@ function normalizeContent(response) {
|
|
|
235
235
|
};
|
|
236
236
|
}
|
|
237
237
|
// If we can't recover, log error and return empty
|
|
238
|
-
|
|
238
|
+
logger?.error('[content-normalizer] Cannot recover from "[object Object]" - no valid object found in response');
|
|
239
239
|
return {
|
|
240
240
|
content: '',
|
|
241
241
|
rawText: undefined,
|
|
@@ -324,7 +324,7 @@ function normalizeContent(response) {
|
|
|
324
324
|
// Last resort: use String() but this should never happen
|
|
325
325
|
normalizedString = String(contentValue);
|
|
326
326
|
// This will be "[object Object]" but we've exhausted all options
|
|
327
|
-
|
|
327
|
+
logger?.error('[content-normalizer] Failed to stringify object and no rawText available', {
|
|
328
328
|
error,
|
|
329
329
|
contentValueType: typeof contentValue,
|
|
330
330
|
isArray: Array.isArray(contentValue)
|
|
@@ -347,7 +347,7 @@ function normalizeContent(response) {
|
|
|
347
347
|
}
|
|
348
348
|
catch {
|
|
349
349
|
// If this also fails, we're stuck with "[object Object]"
|
|
350
|
-
|
|
350
|
+
logger?.error('[content-normalizer] Cannot recover from "[object Object]" - all recovery attempts failed');
|
|
351
351
|
}
|
|
352
352
|
}
|
|
353
353
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* Preserves both rawText and parsedContent for downstream consumers.
|
|
7
7
|
*/
|
|
8
8
|
import type { LLMResponse } from '../types.js';
|
|
9
|
+
import type { Logxer } from '@x12i/logxer';
|
|
9
10
|
import type { NormalizedContent } from './types.js';
|
|
10
11
|
/**
|
|
11
12
|
* Normalizes content from router response
|
|
@@ -20,7 +21,7 @@ import type { NormalizedContent } from './types.js';
|
|
|
20
21
|
* @param response - Router response object
|
|
21
22
|
* @returns Normalized content with metadata
|
|
22
23
|
*/
|
|
23
|
-
export declare function normalizeContent(response: LLMResponse): NormalizedContent;
|
|
24
|
+
export declare function normalizeContent(response: LLMResponse, logger?: Logxer): NormalizedContent;
|
|
24
25
|
/**
|
|
25
26
|
* Checks if content is empty
|
|
26
27
|
*
|
|
@@ -61,6 +61,7 @@ exports.hasFlexMdContract = hasFlexMdContract;
|
|
|
61
61
|
exports.detectComplianceLevel = detectComplianceLevel;
|
|
62
62
|
const path = __importStar(require("path"));
|
|
63
63
|
const url_1 = require("url");
|
|
64
|
+
const logxer_1 = require("@x12i/logxer");
|
|
64
65
|
let flexMdModule = null;
|
|
65
66
|
let flexMdLoadError = null;
|
|
66
67
|
let flexMdLoading = null;
|
|
@@ -374,7 +375,7 @@ async function extractFlexMdOutputFormatFromInstructions(instructions, complianc
|
|
|
374
375
|
* - Other flex-md options
|
|
375
376
|
* @returns Parsed JSON object and method used, or null if parsing fails
|
|
376
377
|
*/
|
|
377
|
-
async function extractJsonFromFlexMd(content) {
|
|
378
|
+
async function extractJsonFromFlexMd(content, logger) {
|
|
378
379
|
// ARCHITECTURE: ai-gateway layer only handles unstructured parsing
|
|
379
380
|
// Schema-driven parsing is handled at skills layer with nx-md-parser
|
|
380
381
|
// No schema parameter - this layer doesn't know about schemas
|
|
@@ -392,8 +393,9 @@ async function extractJsonFromFlexMd(content) {
|
|
|
392
393
|
}
|
|
393
394
|
}
|
|
394
395
|
catch (error) {
|
|
395
|
-
|
|
396
|
-
|
|
396
|
+
logger?.warn('markdownToJson failed', {
|
|
397
|
+
error: error instanceof Error ? error.message : String(error)
|
|
398
|
+
});
|
|
397
399
|
}
|
|
398
400
|
}
|
|
399
401
|
// Secondary approach: Use extractFromMarkdown for low-level section extraction
|
|
@@ -403,15 +405,16 @@ async function extractJsonFromFlexMd(content) {
|
|
|
403
405
|
if (result && typeof result === 'object' && result !== null) {
|
|
404
406
|
const json = result.json || result.data || result.result || result;
|
|
405
407
|
if (json && typeof json === 'object' && json !== null) {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
+
logger?.debug('extractJsonFromFlexMd: using extractFromMarkdown', { debugKind: logxer_1.DebugLogAbstract.TRACE });
|
|
409
|
+
logger?.debug('extractJsonFromFlexMd: parsed json', { json, debugKind: logxer_1.DebugLogAbstract.STATE });
|
|
408
410
|
return { json, method: 'extractFromMarkdown' };
|
|
409
411
|
}
|
|
410
412
|
}
|
|
411
413
|
}
|
|
412
414
|
catch (error) {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
+
logger?.warn('extractFromMarkdown failed', {
|
|
416
|
+
error: error instanceof Error ? error.message : String(error)
|
|
417
|
+
});
|
|
415
418
|
}
|
|
416
419
|
}
|
|
417
420
|
}
|
|
@@ -419,13 +422,14 @@ async function extractJsonFromFlexMd(content) {
|
|
|
419
422
|
// flex-md loading failed, log once and continue to fallback.
|
|
420
423
|
if (!hasLoggedFlexMdLoadWarning) {
|
|
421
424
|
hasLoggedFlexMdLoadWarning = true;
|
|
422
|
-
|
|
425
|
+
logger?.warn('extractJsonFromFlexMd: flex-md library failed to load, using markdown parser fallback', {
|
|
426
|
+
error: error instanceof Error ? error.message : String(error)
|
|
427
|
+
});
|
|
423
428
|
}
|
|
424
429
|
}
|
|
425
|
-
|
|
426
|
-
console.debug('extractJsonFromFlexMd: content:', content);
|
|
430
|
+
logger?.debug('extractJsonFromFlexMd: using markdown parser fallback', { contentLength: content.length });
|
|
427
431
|
// Final fallback: Parse markdown sections
|
|
428
|
-
return fallbackMarkdownParser(content);
|
|
432
|
+
return fallbackMarkdownParser(content, logger);
|
|
429
433
|
}
|
|
430
434
|
/**
|
|
431
435
|
* Helper function to detect and parse markdown lists into arrays
|
|
@@ -483,7 +487,7 @@ function parseMarkdownList(content) {
|
|
|
483
487
|
/**
|
|
484
488
|
* Simple markdown parser for unstructured content (fallback when flex-md unavailable)
|
|
485
489
|
*/
|
|
486
|
-
function fallbackMarkdownParser(content) {
|
|
490
|
+
function fallbackMarkdownParser(content, logger) {
|
|
487
491
|
const result = {};
|
|
488
492
|
const detectedHeaders = [];
|
|
489
493
|
// Simple section extraction based on markdown headers
|
|
@@ -526,12 +530,11 @@ function fallbackMarkdownParser(content) {
|
|
|
526
530
|
result[key] = parseMarkdownList(currentContent);
|
|
527
531
|
detectedHeaders.push(key);
|
|
528
532
|
}
|
|
529
|
-
|
|
530
|
-
console.log('Fallback parser detected headers:', detectedHeaders);
|
|
533
|
+
logger?.debug('Fallback parser detected headers', { detectedHeaders, debugKind: logxer_1.DebugLogAbstract.STATE });
|
|
531
534
|
const expectedHeaders = ['shortAnswer', 'fullAnswer', 'assumptions', 'unknowns', 'evidence'];
|
|
532
535
|
const missingHeaders = expectedHeaders.filter(h => !detectedHeaders.includes(h));
|
|
533
536
|
if (missingHeaders.length > 0) {
|
|
534
|
-
|
|
537
|
+
logger?.debug('Fallback parser missing expected headers', { missingHeaders, debugKind: logxer_1.DebugLogAbstract.STATE });
|
|
535
538
|
}
|
|
536
539
|
// If we extracted some structure, return it
|
|
537
540
|
if (Object.keys(result).length > 0) {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* - Native output format support: Extracts and uses flex-md native output formats from instructions
|
|
14
14
|
* based on compliance level when no explicit format is provided
|
|
15
15
|
*/
|
|
16
|
+
import { type Logxer } from '@x12i/logxer';
|
|
16
17
|
/**
|
|
17
18
|
* Load flex-md module (ES Module) asynchronously
|
|
18
19
|
* Caches the result to avoid multiple imports
|
|
@@ -38,7 +39,7 @@ export declare function loadFlexMd(): Promise<any>;
|
|
|
38
39
|
* - Other flex-md options
|
|
39
40
|
* @returns Parsed JSON object and method used, or null if parsing fails
|
|
40
41
|
*/
|
|
41
|
-
export declare function extractJsonFromFlexMd(content: string): Promise<{
|
|
42
|
+
export declare function extractJsonFromFlexMd(content: string, logger?: Logxer): Promise<{
|
|
42
43
|
json: any;
|
|
43
44
|
method: string;
|
|
44
45
|
} | null>;
|
|
@@ -92,10 +92,10 @@ const template_render_merge_js_1 = require("./template-render-merge.cjs");
|
|
|
92
92
|
const gateway_rate_limiter_js_1 = require("./gateway-rate-limiter.cjs");
|
|
93
93
|
const gateway_rate_limiter_constants_js_1 = require("./gateway-rate-limiter-constants.cjs");
|
|
94
94
|
/**
|
|
95
|
-
* Loads configuration from JSON files (model config and instructionsBlocks)
|
|
96
|
-
*
|
|
95
|
+
* Loads configuration from JSON files (model config and instructionsBlocks).
|
|
96
|
+
* Pass a {@link Logxer} instance so load diagnostics go through logxer (not console).
|
|
97
97
|
*/
|
|
98
|
-
function loadConfig() {
|
|
98
|
+
function loadConfig(logger) {
|
|
99
99
|
const defaultModelConfig = {};
|
|
100
100
|
const defaultInstructionsBlocks = {};
|
|
101
101
|
let defaultTemplateRendering;
|
|
@@ -120,26 +120,22 @@ function loadConfig() {
|
|
|
120
120
|
const parsed = JSON.parse(content);
|
|
121
121
|
// Use Object.assign to merge, preserving nested structure
|
|
122
122
|
Object.assign(defaultInstructionsBlocks, parsed);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
outputKeys: 'output' in defaultInstructionsBlocks ? Object.keys(defaultInstructionsBlocks.output) : []
|
|
129
|
-
});
|
|
130
|
-
}
|
|
123
|
+
logger.debug('Loaded instructions blocks from defaults', {
|
|
124
|
+
topLevelKeys: Object.keys(defaultInstructionsBlocks),
|
|
125
|
+
hasOutput: 'output' in defaultInstructionsBlocks,
|
|
126
|
+
outputKeys: 'output' in defaultInstructionsBlocks ? Object.keys(defaultInstructionsBlocks.output) : []
|
|
127
|
+
});
|
|
131
128
|
}
|
|
132
129
|
else {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
130
|
+
logger.verbose('Optional instructions blocks file not found; using packaged fallbacks', {
|
|
131
|
+
instructionsBlocksPath
|
|
132
|
+
});
|
|
137
133
|
}
|
|
138
134
|
}
|
|
139
135
|
catch (error) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
136
|
+
logger.warn('Failed to load defaults from JSON files', {
|
|
137
|
+
error: error instanceof Error ? error.message : String(error)
|
|
138
|
+
});
|
|
143
139
|
}
|
|
144
140
|
// Ensure critical blocks exist even if file loading failed
|
|
145
141
|
if (!defaultInstructionsBlocks['outputObjectPrefix']) {
|
|
@@ -167,10 +163,10 @@ function getFlexMdMinComplianceLevel() {
|
|
|
167
163
|
function setupRequestInterceptor(router, logger) {
|
|
168
164
|
logger.debug('Setting up request interceptor for jobId propagation and config cleanup');
|
|
169
165
|
router.addRequestInterceptor(async (request, provider) => {
|
|
170
|
-
|
|
171
|
-
if (
|
|
172
|
-
logger.verbose('Propagating jobId to request metadata', {
|
|
173
|
-
jobId:
|
|
166
|
+
const identityJobId = request?.identity?.jobId;
|
|
167
|
+
if (typeof identityJobId === 'string' && identityJobId.trim().length > 0) {
|
|
168
|
+
logger.verbose('Propagating identity.jobId to request metadata', {
|
|
169
|
+
jobId: identityJobId,
|
|
174
170
|
provider: provider?.getProviderName?.() || 'unknown'
|
|
175
171
|
});
|
|
176
172
|
if (!request.config) {
|
|
@@ -179,7 +175,7 @@ function setupRequestInterceptor(router, logger) {
|
|
|
179
175
|
if (!request.config.metadata) {
|
|
180
176
|
request.config.metadata = {};
|
|
181
177
|
}
|
|
182
|
-
request.config.metadata.jobId =
|
|
178
|
+
request.config.metadata.jobId = identityJobId;
|
|
183
179
|
}
|
|
184
180
|
// Remove 'provider' from config - router uses it for routing but providers don't accept it
|
|
185
181
|
// Router reads config.provider to determine which provider to call, but then passes
|
|
@@ -198,14 +194,14 @@ function setupRequestInterceptor(router, logger) {
|
|
|
198
194
|
/**
|
|
199
195
|
* Initializes gateway components
|
|
200
196
|
*/
|
|
201
|
-
function initializeGatewayComponents(config
|
|
197
|
+
function initializeGatewayComponents(config) {
|
|
202
198
|
// Initialize logger FIRST (before other components that might need it)
|
|
203
199
|
const logger = (0, logger_factory_js_1.createGatewayLogger)({
|
|
204
200
|
enableLogging: config.enableLogging ?? true,
|
|
205
201
|
packageName: config.packageName,
|
|
206
202
|
customLogger: config.logger
|
|
207
203
|
});
|
|
208
|
-
|
|
204
|
+
const { defaultModelConfig, defaultInstructionsBlocks, defaultTemplateRendering } = loadConfig(logger);
|
|
209
205
|
logger.verbose('Gateway initializing', {
|
|
210
206
|
defaultEngine: config.defaultEngine,
|
|
211
207
|
hasDefaultInstructionsBlocks: Object.keys(defaultInstructionsBlocks).length > 0
|
|
@@ -20,10 +20,10 @@ export interface GatewayConfigContext {
|
|
|
20
20
|
messageBuilderConfig: MessageBuilderConfig;
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
* Loads configuration from JSON files (model config and instructionsBlocks)
|
|
24
|
-
*
|
|
23
|
+
* Loads configuration from JSON files (model config and instructionsBlocks).
|
|
24
|
+
* Pass a {@link Logxer} instance so load diagnostics go through logxer (not console).
|
|
25
25
|
*/
|
|
26
|
-
export declare function loadConfig(): {
|
|
26
|
+
export declare function loadConfig(logger: Logxer): {
|
|
27
27
|
defaultModelConfig: Record<string, unknown>;
|
|
28
28
|
defaultInstructionsBlocks: Record<string, any>;
|
|
29
29
|
defaultTemplateRendering?: TemplateRenderOptions;
|
|
@@ -40,7 +40,7 @@ export declare function setupRequestInterceptor(router: LLMProviderRouter, logge
|
|
|
40
40
|
/**
|
|
41
41
|
* Initializes gateway components
|
|
42
42
|
*/
|
|
43
|
-
export declare function initializeGatewayComponents(config: GatewayConfig
|
|
43
|
+
export declare function initializeGatewayComponents(config: GatewayConfig): {
|
|
44
44
|
logger: Logxer;
|
|
45
45
|
router: LLMProviderRouter;
|
|
46
46
|
activityManager: ActivityManager;
|
|
@@ -77,7 +77,7 @@ async function invokeStructuredText(request, gateway) {
|
|
|
77
77
|
// Create a new request with structured-text mode
|
|
78
78
|
const structuredRequest = {
|
|
79
79
|
...request,
|
|
80
|
-
|
|
80
|
+
identity: { ...request.identity }
|
|
81
81
|
};
|
|
82
82
|
// Make the call (this will recursively call invoke, but with structured-text mode)
|
|
83
83
|
return await gateway.invoke(structuredRequest);
|
|
@@ -119,7 +119,7 @@ async function convertStructuredToJson(structuredText, originalRequest, config,
|
|
|
119
119
|
});
|
|
120
120
|
try {
|
|
121
121
|
// Use flex-md 3.0.0 to extract and parse JSON
|
|
122
|
-
const flexMdResult = await (0, flex_md_loader_js_1.extractJsonFromFlexMd)(structuredText);
|
|
122
|
+
const flexMdResult = await (0, flex_md_loader_js_1.extractJsonFromFlexMd)(structuredText, logger);
|
|
123
123
|
if (!flexMdResult || !flexMdResult.json) {
|
|
124
124
|
throw new Error('flex-md SDK could not extract JSON from structured text');
|
|
125
125
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.gatewayLogDebug = void 0;
|
|
4
|
+
exports.activityIdentityToLogMeta = activityIdentityToLogMeta;
|
|
5
|
+
exports.withActivityIdentity = withActivityIdentity;
|
|
6
|
+
const logxer_1 = require("@x12i/logxer");
|
|
7
|
+
/** Subset of identity forwarded on every structured log line (plus full envelope under `identity`). */
|
|
8
|
+
function activityIdentityToLogMeta(identity) {
|
|
9
|
+
if (!identity) {
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
const meta = {
|
|
13
|
+
identity: JSON.stringify({
|
|
14
|
+
sessionId: identity.sessionId,
|
|
15
|
+
aiRequestId: identity.aiRequestId,
|
|
16
|
+
jobId: identity.jobId,
|
|
17
|
+
taskId: identity.taskId,
|
|
18
|
+
agentId: identity.agentId
|
|
19
|
+
}),
|
|
20
|
+
jobId: identity.jobId,
|
|
21
|
+
sessionId: identity.sessionId,
|
|
22
|
+
correlationId: identity.aiRequestId,
|
|
23
|
+
taskId: identity.taskId,
|
|
24
|
+
activityIdentity: identity
|
|
25
|
+
};
|
|
26
|
+
return meta;
|
|
27
|
+
}
|
|
28
|
+
function withActivityIdentity(identity, data, debugKind) {
|
|
29
|
+
return {
|
|
30
|
+
...activityIdentityToLogMeta(identity),
|
|
31
|
+
...(data || {}),
|
|
32
|
+
...(debugKind !== undefined ? { debugKind } : {})
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
exports.gatewayLogDebug = {
|
|
36
|
+
anomaly: logxer_1.DebugLogAbstract.ANOMALY,
|
|
37
|
+
trace: logxer_1.DebugLogAbstract.TRACE,
|
|
38
|
+
intent: logxer_1.DebugLogAbstract.INTENT,
|
|
39
|
+
event: logxer_1.DebugLogAbstract.EVENT,
|
|
40
|
+
state: logxer_1.DebugLogAbstract.STATE
|
|
41
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps gateway {@link ActivityIdentity} into Logxer {@link LogMeta} fields for correlation and querying.
|
|
3
|
+
*/
|
|
4
|
+
import type { LogMeta } from '@x12i/logxer';
|
|
5
|
+
import type { ActivityIdentity } from './types.js';
|
|
6
|
+
/** Subset of identity forwarded on every structured log line (plus full envelope under `identity`). */
|
|
7
|
+
export declare function activityIdentityToLogMeta(identity: Partial<ActivityIdentity> | undefined): LogMeta;
|
|
8
|
+
export declare function withActivityIdentity(identity: Partial<ActivityIdentity> | undefined, data?: LogMeta, debugKind?: LogMeta['debugKind']): LogMeta;
|
|
9
|
+
export declare const gatewayLogDebug: {
|
|
10
|
+
readonly anomaly: "ANOMALY";
|
|
11
|
+
readonly trace: "TRACE";
|
|
12
|
+
readonly intent: "INTENT";
|
|
13
|
+
readonly event: "EVENT";
|
|
14
|
+
readonly state: "STATE";
|
|
15
|
+
};
|
|
@@ -117,8 +117,8 @@ function buildWorkingMemory(request, existingWorkingMemory, otherMemories) {
|
|
|
117
117
|
if (!workingMemory.job.narrative && request.prompt) {
|
|
118
118
|
workingMemory.job.narrative = request.prompt;
|
|
119
119
|
}
|
|
120
|
-
if (!workingMemory.job.id && request.jobId) {
|
|
121
|
-
workingMemory.job.id = request.jobId;
|
|
120
|
+
if (!workingMemory.job.id && request.identity.jobId) {
|
|
121
|
+
workingMemory.job.id = request.identity.jobId;
|
|
122
122
|
}
|
|
123
123
|
// Populate task fields from request if not already present in existing memory
|
|
124
124
|
if (!workingMemory.task.objective && request.instructions) {
|
|
@@ -128,8 +128,8 @@ function buildWorkingMemory(request, existingWorkingMemory, otherMemories) {
|
|
|
128
128
|
workingMemory.task.context = request.context;
|
|
129
129
|
}
|
|
130
130
|
// Input field has been removed - data should come from workingMemory.input
|
|
131
|
-
if (!workingMemory.task.id && request.taskId) {
|
|
132
|
-
workingMemory.task.id = request.taskId;
|
|
131
|
+
if (!workingMemory.task.id && request.identity.taskId) {
|
|
132
|
+
workingMemory.task.id = request.identity.taskId;
|
|
133
133
|
}
|
|
134
134
|
// Add entity information if not present
|
|
135
135
|
if (!workingMemory.entity && request.agentId) {
|
|
@@ -186,7 +186,7 @@ async function resolveTemplateParams(request, config, logger) {
|
|
|
186
186
|
// If memory component fails, fall back to request-only mode
|
|
187
187
|
logger?.warn('Memory component resolution failed, using request-only mode', {
|
|
188
188
|
error: error instanceof Error ? error.message : String(error),
|
|
189
|
-
jobId: request.jobId
|
|
189
|
+
jobId: request.identity.jobId
|
|
190
190
|
});
|
|
191
191
|
}
|
|
192
192
|
}
|
|
@@ -198,7 +198,7 @@ async function resolveTemplateParams(request, config, logger) {
|
|
|
198
198
|
// Rendrix priority: shortTermMemory > workingMemory > experienceMemory > knowledgeMemory
|
|
199
199
|
if (request.templateTokens && Object.keys(request.templateTokens).length > 0) {
|
|
200
200
|
logger?.debug('Merged templateTokens into shortTermMemory (tier 1 - highest priority)', {
|
|
201
|
-
jobId: request.jobId,
|
|
201
|
+
jobId: request.identity.jobId,
|
|
202
202
|
tokenKeys: Object.keys(request.templateTokens)
|
|
203
203
|
});
|
|
204
204
|
}
|
|
@@ -27,7 +27,7 @@ function isAIRequest(request) {
|
|
|
27
27
|
*/
|
|
28
28
|
async function constructMessages(request, config, logger, parsedSnapshot) {
|
|
29
29
|
logger.verbose('Constructing messages from request', {
|
|
30
|
-
jobId: request.jobId,
|
|
30
|
+
jobId: request.identity.jobId,
|
|
31
31
|
agentId: request.agentId,
|
|
32
32
|
hasInstructions: !!request.instructions,
|
|
33
33
|
hasContext: !!request.context,
|
|
@@ -35,11 +35,11 @@ async function constructMessages(request, config, logger, parsedSnapshot) {
|
|
|
35
35
|
hasWorkingMemory: !!request.workingMemory
|
|
36
36
|
});
|
|
37
37
|
// LOGGING: Construct messages phase (instructions should already be resolved)
|
|
38
|
-
const traceId = request.jobId ||
|
|
38
|
+
const traceId = request.identity.jobId || request.aiRequestId;
|
|
39
39
|
const agentId = request.agentId;
|
|
40
40
|
logger.info('instructions.constructMessages.entry', {
|
|
41
41
|
traceId,
|
|
42
|
-
jobId: request.jobId,
|
|
42
|
+
jobId: request.identity.jobId,
|
|
43
43
|
agentId,
|
|
44
44
|
'instructionsType': typeof request.instructions,
|
|
45
45
|
'instructionsLength': typeof request.instructions === 'string' ? request.instructions.length : 'N/A',
|
|
@@ -62,7 +62,7 @@ async function constructMessages(request, config, logger, parsedSnapshot) {
|
|
|
62
62
|
if (typeof finalInstructions === 'string') {
|
|
63
63
|
finalInstructions = `${finalInstructions}\n\nExamples:\n${fewShotExamples}`;
|
|
64
64
|
logger.debug('Added few-shot examples to instructions', {
|
|
65
|
-
jobId: request.jobId,
|
|
65
|
+
jobId: request.identity.jobId,
|
|
66
66
|
exampleCount: Math.min(examples.length, 3),
|
|
67
67
|
totalExamples: examples.length
|
|
68
68
|
});
|
|
@@ -20,12 +20,19 @@ async function testInstructions(instructions, testInput, expectedSchema, options
|
|
|
20
20
|
if (!model) {
|
|
21
21
|
throw new Error('Model must be provided in options.model or configured as default');
|
|
22
22
|
}
|
|
23
|
+
const aiRequestId = `test-instructions-${Date.now()}`;
|
|
24
|
+
const runtimeIdentity = {
|
|
25
|
+
...options.identity,
|
|
26
|
+
aiRequestId,
|
|
27
|
+
agentId
|
|
28
|
+
};
|
|
23
29
|
// Create test request with internal system action config
|
|
24
30
|
const testRequest = {
|
|
25
|
-
|
|
31
|
+
aiRequestId,
|
|
26
32
|
agentId,
|
|
27
33
|
instructions,
|
|
28
|
-
|
|
34
|
+
identity: runtimeIdentity,
|
|
35
|
+
workingMemory: { input: testInput },
|
|
29
36
|
config: {
|
|
30
37
|
model,
|
|
31
38
|
provider,
|