@x12i/ai-gateway 7.9.1 → 9.0.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 +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 +3 -3
package/README.md
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
# @x12i/ai-gateway
|
|
2
2
|
|
|
3
|
-
Unified gateway for LLM provider routing and management with production-ready features: context propagation, usage tier tracking, activity tracking, and comprehensive metadata. Built on top of `@x12i/ai-providers-router` with integrations for `@x12i/x-models`, `@x12i/activix`
|
|
3
|
+
Unified gateway for LLM provider routing and management with production-ready features: context propagation, usage tier tracking, activity tracking, and comprehensive metadata. Built on top of `@x12i/ai-providers-router` with integrations for `@x12i/x-models`, `@x12i/activix` (see `package.json` for pinned versions), and **`@x12i/logxer`** for structured logging.
|
|
4
|
+
|
|
5
|
+
## Mandatory runtime identity (v9+)
|
|
6
|
+
|
|
7
|
+
Every **`invoke`** / **`invokeChat`** request **must** include **`identity`**: the full runtime envelope from the **upstream client** (not invented inside the gateway).
|
|
8
|
+
|
|
9
|
+
- **`identity.jobId`** and **`identity.taskId`** are **only** taken from that upstream object. The gateway **never** generates, rewrites, or back-fills them from deprecated top-level `jobId` / `taskId` fields.
|
|
10
|
+
- If `identity` is missing or `jobId` / `taskId` are empty, the gateway logs **`warn`** via Logxer (`missingRuntimeIdentityObject` / `missingUpstreamIdentityFields`) when a logger is configured, and still attaches the merged envelope so the rest of the pipeline can proceed.
|
|
11
|
+
- The same merged object is **`request.identity`**, forwarded to the router, returned as **`response.metadata.identity`**, and persisted on Activix as **`runContext`** (same reference as `request.identity`).
|
|
12
|
+
|
|
13
|
+
See [Identity contract](./docs/IDENTITY_OBJECT_CONTRACT.md) and [Logger initialization](./docs/LOGGER_INITIALIZATION.md).
|
|
4
14
|
|
|
5
15
|
## Features
|
|
6
16
|
|
|
7
17
|
- **🔀 Provider Routing**: Dynamic provider registration and automatic routing with fallback support
|
|
8
18
|
- **📊 Context Propagation**: `aiRequestId` and identity propagation for distributed tracing (see [Identity contract](./docs/IDENTITY_OBJECT_CONTRACT.md))
|
|
9
19
|
- **⚡ Usage Tier Tracking**: RPM/TPM limit enforcement via `@x12i/x-models`
|
|
10
|
-
- **📈 Activity Tracking**: Comprehensive activity logging via `@x12i/activix`
|
|
11
|
-
- **📝 Structured Logging**: Production-ready logging via `
|
|
20
|
+
- **📈 Activity Tracking**: Comprehensive activity logging via `@x12i/activix` v6 (xronox-activitix), fixed Mongo collections `ai-activities` / `bad-requests`, validated root-level **`outer` / `inner`** I/O plus **`runContext`** for Activix 6
|
|
21
|
+
- **📝 Structured Logging**: Production-ready logging via **`@x12i/logxer`** (LogMeta `jobId` / `sessionId` / `correlationId`, optional `debugKind`, default `runtimeIdentity` from env) with diagnostic tracing for instruction resolution and propagation debugging
|
|
12
22
|
- **📋 Rich Metadata**: Detailed execution metadata (latency, tokens, model, cost, `aiRequestId`, `identity`)
|
|
13
23
|
- **🏥 Health Checks**: Monitor provider health and availability
|
|
14
24
|
- **🔄 Request/Response Interceptors**: Modify requests and responses
|
|
@@ -122,24 +132,25 @@ gateway.register(new GrokProvider({
|
|
|
122
132
|
apiKey: process.env.GROK_API_KEY
|
|
123
133
|
}));
|
|
124
134
|
|
|
125
|
-
// Invoke with
|
|
135
|
+
// Invoke with mandatory runtime identity (upstream job/task correlation)
|
|
126
136
|
const response = await gateway.invoke({
|
|
127
|
-
|
|
128
|
-
jobId: 'job-123', // Context propagation
|
|
137
|
+
aiRequestId: 'call-001',
|
|
129
138
|
agentId: 'agent-456',
|
|
130
|
-
|
|
139
|
+
instructions: 'Reply briefly.',
|
|
140
|
+
identity: {
|
|
141
|
+
sessionId: 'run-1',
|
|
142
|
+
instance: { instanceId: 'agent-456', type: 'ai-reasoner' },
|
|
143
|
+
aiRequestId: 'call-001',
|
|
144
|
+
jobId: 'job-123',
|
|
145
|
+
taskId: 'task-789',
|
|
146
|
+
agentId: 'agent-456'
|
|
147
|
+
},
|
|
148
|
+
workingMemory: { input: 'Hello!' }
|
|
149
|
+
// … primaryObjectType / flexMdFormat / messages as required by your request type
|
|
131
150
|
});
|
|
132
151
|
|
|
133
|
-
// Response includes comprehensive metadata
|
|
152
|
+
// Response includes comprehensive metadata (including `identity`)
|
|
134
153
|
console.log(response.metadata);
|
|
135
|
-
// {
|
|
136
|
-
// jobId: 'job-123',
|
|
137
|
-
// latencyMs: 1250,
|
|
138
|
-
// tokens: { prompt: 100, completion: 50, total: 150 },
|
|
139
|
-
// model: 'gpt-4o',
|
|
140
|
-
// provider: 'openai',
|
|
141
|
-
// cost: 0.002
|
|
142
|
-
// }
|
|
143
154
|
```
|
|
144
155
|
|
|
145
156
|
### Using Base Router (Direct Access)
|
|
@@ -184,33 +195,26 @@ The AI Gateway can be configured in multiple ways:
|
|
|
184
195
|
- `FLEX_MD_MIN_COMPLIANCE_LEVEL` - Minimum flex-md compliance level for output format validation (default: `L0`). Valid values: `L0`, `L1`, `L2`, `L3`. See [Output Format Validation](#output-format-validation) section for details. When set to `L0` (default), no format validation is required. When set to `L1` or higher, format specifications are required in instructions and validation errors will reject requests.
|
|
185
196
|
4. **Request-Level** - Override gateway defaults per request
|
|
186
197
|
|
|
187
|
-
### 1. Logging Configuration (
|
|
188
|
-
|
|
189
|
-
**Logger Initialization: Two Options**
|
|
190
|
-
|
|
191
|
-
The gateway supports two ways to initialize the logger:
|
|
192
|
-
|
|
193
|
-
1. **Option 1 (Recommended)**: Provide your project's logger instance - ensures consistent logging
|
|
194
|
-
2. **Option 2 (Fallback)**: Let the gateway create a default logger - works out of the box
|
|
198
|
+
### 1. Logging Configuration (@x12i/logxer)
|
|
195
199
|
|
|
196
|
-
|
|
200
|
+
**Logger initialization:** The gateway uses **`@x12i/logxer`**. Pass a **`Logxer`** from **`createLogxer`**, or omit `logger` and let the gateway build a default (still Logxer-based). See **[Logger Initialization Guide](./docs/LOGGER_INITIALIZATION.md)** for mandatory **`identity`** on requests and **`LogMeta`** / **`debugKind`** usage.
|
|
197
201
|
|
|
198
202
|
**Where to configure:**
|
|
199
|
-
- Gateway constructor: `enableLogging`, `packageName`, `logger`
|
|
200
|
-
- Environment variables: **`{PREFIX}_LOGS_LEVEL`** (canonical per-package level
|
|
203
|
+
- Gateway constructor: `enableLogging`, `packageName`, `logger`
|
|
204
|
+
- Environment variables: **`{PREFIX}_LOGS_LEVEL`** (canonical per-package level), legacy **`{PREFIX}_LOG_LEVEL`**, plus **`{PREFIX}_LOG_FORMAT`**, file sinks, unified logger, etc., as documented for **`@x12i/logxer`**.
|
|
201
205
|
|
|
202
206
|
**How to configure:**
|
|
203
207
|
|
|
204
208
|
```typescript
|
|
205
209
|
import { AIGateway } from '@x12i/ai-gateway';
|
|
206
|
-
import {
|
|
210
|
+
import { createLogxer } from '@x12i/logxer';
|
|
207
211
|
|
|
208
212
|
// Create logger once at application startup
|
|
209
|
-
const logger =
|
|
210
|
-
{ packageName: 'MY_APP', envPrefix: 'MY_APP' },
|
|
213
|
+
const logger = createLogxer(
|
|
214
|
+
{ packageName: 'MY_APP', envPrefix: 'MY_APP', debugNamespace: 'my-app' },
|
|
211
215
|
{
|
|
212
216
|
logLevel: 'info', // verbose|debug|info|warn|error
|
|
213
|
-
logFormat: 'json', // text|json|yaml
|
|
217
|
+
logFormat: 'json', // text|json|yaml|table
|
|
214
218
|
logToFile: true,
|
|
215
219
|
logFilePath: '/var/log/app.log',
|
|
216
220
|
enableUnifiedLogger: true,
|
|
@@ -218,27 +222,31 @@ const logger = createLogger(
|
|
|
218
222
|
transports: { papertrail: true },
|
|
219
223
|
service: 'my-app',
|
|
220
224
|
env: 'production'
|
|
225
|
+
},
|
|
226
|
+
runtimeIdentity: {
|
|
227
|
+
service: 'my-app',
|
|
228
|
+
env: process.env.NODE_ENV,
|
|
229
|
+
version: process.env.npm_package_version
|
|
221
230
|
}
|
|
222
231
|
}
|
|
223
232
|
);
|
|
224
233
|
|
|
225
|
-
// Pass the SAME logger instance to gateway (REQUIRED)
|
|
226
234
|
const gateway = new AIGateway({
|
|
227
235
|
enableLogging: true,
|
|
228
|
-
logger
|
|
236
|
+
logger,
|
|
229
237
|
packageName: 'MY_APP'
|
|
230
238
|
});
|
|
231
239
|
```
|
|
232
240
|
|
|
233
|
-
**Why
|
|
241
|
+
**Why use the same Logxer?**
|
|
234
242
|
- Consistent log format across your application
|
|
235
243
|
- Unified logging destination
|
|
236
|
-
-
|
|
244
|
+
- **`LogMeta`** correlation (`jobId`, `sessionId`, `correlationId`, `debugKind`, `runtimeIdentity`) matches gateway and Activix fields
|
|
237
245
|
- Single point of control for log levels
|
|
238
246
|
|
|
239
|
-
**Per-package log level (
|
|
247
|
+
**Per-package log level (`@x12i/logxer`):**
|
|
240
248
|
|
|
241
|
-
- **Canonical:** `{PREFIX}_LOGS_LEVEL` —
|
|
249
|
+
- **Canonical:** `{PREFIX}_LOGS_LEVEL` — same `envPrefix` as **`createLogxer`** / your `packageName` (e.g. `MY_APP` → `MY_APP_LOGS_LEVEL`).
|
|
242
250
|
- **Legacy:** `{PREFIX}_LOG_LEVEL` is used only if `{PREFIX}_LOGS_LEVEL` is **not** set.
|
|
243
251
|
- **Default** when both are unset: **`warn`** (not `info`, not silent).
|
|
244
252
|
- **Silence** this package’s diagnostics: `off`, `none`, or `silent` (case-insensitive).
|
|
@@ -246,27 +254,26 @@ const gateway = new AIGateway({
|
|
|
246
254
|
|
|
247
255
|
If the gateway builds the default logger and you omit `packageName`, the prefix is **`AI_GATEWAY`** → e.g. **`AI_GATEWAY_LOGS_LEVEL`**.
|
|
248
256
|
|
|
249
|
-
**Environment
|
|
257
|
+
**Environment variables (examples):**
|
|
250
258
|
```bash
|
|
251
259
|
MY_APP_LOGS_LEVEL=info # raise verbosity (or use debug / verbose)
|
|
252
260
|
MY_APP_LOGS_LEVEL=off # silence this package’s logs
|
|
253
261
|
# MY_APP_LOG_LEVEL=info # legacy; ignored if MY_APP_LOGS_LEVEL is set
|
|
254
262
|
|
|
255
|
-
MY_APP_LOG_FORMAT=json # text|json|yaml
|
|
263
|
+
MY_APP_LOG_FORMAT=json # text|json|yaml|table
|
|
256
264
|
MY_APP_LOG_TO_FILE=true
|
|
257
265
|
MY_APP_LOG_FILE=/var/log/app.log
|
|
258
266
|
MY_APP_LOG_TO_UNIFIED=true
|
|
259
267
|
DEBUG=my-app # elevates verbose/debug when package is not fully silent
|
|
260
268
|
```
|
|
261
269
|
|
|
262
|
-
|
|
270
|
+
**Diagnostic logging:** When debug level is enabled, the gateway emits diagnostic logs for instruction resolution and propagation through the same Logxer pipeline.
|
|
263
271
|
|
|
264
|
-
**What
|
|
265
|
-
If `logger` is not provided, the gateway will automatically create a default logger. However, for best results (consistent format, unified destination), it's recommended to provide your project's logger instance.
|
|
272
|
+
**What happens if `logger` is not provided:** The gateway creates a default **`Logxer`** via **`createLogxer`**. For production, prefer passing your app’s logger so levels, transports, and **`runtimeIdentity`** stay aligned with the rest of your stack.
|
|
266
273
|
|
|
267
|
-
### 2. Activity Tracking Configuration (xronox-activitix via @x12i/activix
|
|
274
|
+
### 2. Activity Tracking Configuration (xronox-activitix via @x12i/activix v6)
|
|
268
275
|
|
|
269
|
-
**Activix version:** This gateway targets **`@x12i/activix`
|
|
276
|
+
**Activix version:** This gateway targets **`@x12i/activix` v6.x** (built on `@xronoces/xronox-store`). The dependency range is declared in `package.json` (currently `^6.5.1`). Activity I/O is stored at the **document root** as **`outer`** (and optional **`inner`**); the deprecated nested **`structure`** wrapper is not used.
|
|
270
277
|
|
|
271
278
|
**Where to configure:**
|
|
272
279
|
- Gateway constructor: `enableActivityTracking`, `activityTracker`
|
|
@@ -285,8 +292,8 @@ The gateway resolves activity tracking from environment variables via `src/confi
|
|
|
285
292
|
**⚠️ CRITICAL: correlation and identity**
|
|
286
293
|
|
|
287
294
|
- **`aiRequestId`** (required on each gateway request): Primary correlation id for this LLM call; the gateway does **not** invent a `jobId` for you.
|
|
288
|
-
- **Run context** (Activix BSON field `runContext`): `sessionId`
|
|
289
|
-
- **`jobTypeId`**, **`
|
|
295
|
+
- **Run context** (Activix BSON field `runContext`): Same object as **`request.identity`** (including required upstream **`jobId`** and **`taskId`**), plus `sessionId` and nested `instance: { instanceId, type }` when present; see [Identity contract](./docs/IDENTITY_OBJECT_CONTRACT.md).
|
|
296
|
+
- **`jobTypeId`**, **`taskTypeId`**: Optional aggregation / grouping fields (unchanged semantics).
|
|
290
297
|
- **Each activity**: Gets its own **unique database record** with unique `_id` (MongoDB ObjectId).
|
|
291
298
|
- **Two-phase tracking**: `startActivity()` creates a new record; `logSuccess()` / `logFailure()` update the same record by that record’s id.
|
|
292
299
|
|
|
@@ -339,7 +346,7 @@ gateway.register(new OpenAIProvider({
|
|
|
339
346
|
}));
|
|
340
347
|
```
|
|
341
348
|
|
|
342
|
-
**Advanced (custom Activix
|
|
349
|
+
**Advanced (custom Activix v6 instance):**
|
|
343
350
|
|
|
344
351
|
If you pass your own `Activix`, configure **the same collection names** the gateway expects so routing matches persistence:
|
|
345
352
|
|
|
@@ -370,7 +377,7 @@ const gateway = new AIGateway({
|
|
|
370
377
|
```
|
|
371
378
|
|
|
372
379
|
**What gets tracked (persisted when DB is configured):**
|
|
373
|
-
- **Identity**:
|
|
380
|
+
- **Identity**: Fields aligned with **`request.identity`** / Activix **`runContext`**: **`aiRequestId`**, upstream **`jobId`** and **`taskId`**, `sessionId`, `instance`, plus optional `jobTypeId`, `agentId`, `taskTypeId`, etc., as provided
|
|
374
381
|
- **Timing**: `startTime`, `endTime`, `duration`, `status` (`started|success|failed`)
|
|
375
382
|
- **Request data**: Stored in `request` object (instructions, prompt, input, messages, workingMemory)
|
|
376
383
|
- **Config data**: Stored in `config` object (model, provider, temperature, maxTokens)
|
|
@@ -385,18 +392,18 @@ const gateway = new AIGateway({
|
|
|
385
392
|
|
|
386
393
|
**Key design points:**
|
|
387
394
|
- ✅ Each activity = separate database record with unique `_id`
|
|
388
|
-
- ✅ **`aiRequestId`** = per-request correlation (required);
|
|
395
|
+
- ✅ **`aiRequestId`** = per-request correlation (required); **`jobId`** / **`taskId`** come from upstream **`identity`** (required on each request; see v9+ contract above)
|
|
389
396
|
- ✅ Request data sent once in `startActivity()` (creates new record)
|
|
390
397
|
- ✅ Response data sent once in `logSuccess()` (updates same record by `_id`)
|
|
391
398
|
|
|
392
399
|
**Default:** Activity tracking is enabled by default; without DB config it will log but not persist.
|
|
393
400
|
|
|
394
|
-
**✅ Activix
|
|
401
|
+
**✅ Activix v6 integration**
|
|
395
402
|
|
|
396
403
|
1. **Configuration** (`activity-tracking-config.ts`):
|
|
397
404
|
- Mongo connection from env; **collection names** `ai-activities` and `bad-requests` are fixed for consistency across deployments.
|
|
398
405
|
|
|
399
|
-
2. **Lifecycle** (`@x12i/activix`
|
|
406
|
+
2. **Lifecycle** (`@x12i/activix` v6):
|
|
400
407
|
- ✅ `startRecord` / `completeRecord` / `failRecord` (two-phase lifecycle)
|
|
401
408
|
- ✅ Status transitions: `started` → `success` or `failed` (per your `statusValues` mapping)
|
|
402
409
|
- ✅ Persistence via xronox-store queue semantics (see Activix package docs)
|
|
@@ -778,15 +785,15 @@ If a provider package is not installed, auto-registration will skip it gracefull
|
|
|
778
785
|
|
|
779
786
|
```typescript
|
|
780
787
|
import { AIGateway } from '@x12i/ai-gateway';
|
|
781
|
-
import {
|
|
788
|
+
import { createLogxer } from '@x12i/logxer';
|
|
782
789
|
import { Activix } from '@x12i/activix';
|
|
783
790
|
import { OpenAIProvider } from '@x12i/ai-provider-openai';
|
|
784
791
|
|
|
785
792
|
// 1. Configure activity tracker (and reuse its logger)
|
|
786
793
|
// Single source of truth: set up the logger once, pass it to the tracker,
|
|
787
794
|
// then reuse the same logger for the gateway.
|
|
788
|
-
const logger =
|
|
789
|
-
{ packageName: 'MY_APP', envPrefix: 'MY_APP' },
|
|
795
|
+
const logger = createLogxer(
|
|
796
|
+
{ packageName: 'MY_APP', envPrefix: 'MY_APP', debugNamespace: 'my-app' },
|
|
790
797
|
{
|
|
791
798
|
logLevel: 'info',
|
|
792
799
|
logFormat: 'json',
|
|
@@ -949,26 +956,27 @@ console.log(`RPM Limit: ${tierInfo?.rpm}, TPM Limit: ${tierInfo?.tpm}`);
|
|
|
949
956
|
- `tier-4`: 10,000 RPM, 4M TPM
|
|
950
957
|
- `tier-5`: 15,000 RPM, 40M TPM
|
|
951
958
|
|
|
952
|
-
### 3. Activity Tracking (xronox-activitix via @x12i/activix
|
|
959
|
+
### 3. Activity Tracking (xronox-activitix via @x12i/activix v6)
|
|
953
960
|
|
|
954
|
-
The gateway uses **`@x12i/activix`
|
|
961
|
+
The gateway uses **`@x12i/activix` v6** (xronox-activitix) for full lifecycle logging. Recommended: enable MongoDB persistence so tracking is automatic. Default collections: **`ai-activities`**, **`bad-requests`**, **`skill-executions`** (see section 2).
|
|
955
962
|
|
|
956
963
|
#### ⚠️ CRITICAL: correlation, identity, and unique record ids
|
|
957
964
|
|
|
958
965
|
**IMPORTANT DESIGN CONCEPTS:**
|
|
959
966
|
|
|
960
967
|
1. **Per-request correlation**
|
|
961
|
-
- **`aiRequestId`** (required): One id per gateway invocation; used as the primary leaf correlation field (stored on the activity row and inside Activix `runContext
|
|
962
|
-
- **`
|
|
968
|
+
- **`aiRequestId`** (required): One id per gateway invocation; used as the primary leaf correlation field (stored on the activity row and inside Activix `runContext`).
|
|
969
|
+
- **`identity.jobId`** and **`identity.taskId`** (required): Taken only from the upstream **`identity`** object; the gateway does not invent them.
|
|
970
|
+
- **`jobTypeId`**, **`taskTypeId`**: Optional aggregation fields (same ideas as before).
|
|
963
971
|
- **Activity**: Each individual LLM request is a separate **activity** with its own unique record.
|
|
964
972
|
|
|
965
973
|
2. **Mongo `_id` is the unique row key**
|
|
966
|
-
-
|
|
974
|
+
- **`jobId`** / **`taskId`** on the row mirror upstream **`identity`** for correlation; multiple activities may share a **`jobId`** when you intend grouping.
|
|
967
975
|
- Activix updates rows by the **record id** from the start phase, not by `jobId`.
|
|
968
976
|
|
|
969
|
-
3. **Two-phase tracking (Activix
|
|
977
|
+
3. **Two-phase tracking (Activix v6)**
|
|
970
978
|
- **Phase 1 (start)**: Creates a NEW database record with unique `_id`
|
|
971
|
-
- Sends request-side data: `request`, `config`, `runContext`,
|
|
979
|
+
- Sends request-side data: `request`, `config`, `runContext`, root-level **`outer`** (and optional **`inner`**) I/O, `startTime`, `status: 'started'` (plus other gateway metadata)
|
|
972
980
|
- Returns metadata containing the unique record id for completion
|
|
973
981
|
- **Phase 2 (complete / fail)**: Updates the SAME record by that id
|
|
974
982
|
- Sends response/error data: `response`, `endTime`, `duration`, `cost`, `status`
|
|
@@ -982,7 +990,7 @@ The gateway uses **`@x12i/activix` v5** (xronox-activitix) for full lifecycle lo
|
|
|
982
990
|
|
|
983
991
|
**Example: same logical job, three LLM calls**
|
|
984
992
|
|
|
985
|
-
Each call must have a **distinct `aiRequestId
|
|
993
|
+
Each call must have a **distinct `aiRequestId`** and a full **`identity`** (including **`jobId`** and **`taskId`** from upstream). Use the same **`identity.jobId`** (and distinct **`taskId`** per call, or your own convention) if you want to group rows in Mongo.
|
|
986
994
|
|
|
987
995
|
```typescript
|
|
988
996
|
import * as crypto from 'crypto';
|
|
@@ -993,29 +1001,31 @@ function md5(text: string): string {
|
|
|
993
1001
|
|
|
994
1002
|
const jobTypeId = md5('data-processing-job');
|
|
995
1003
|
|
|
1004
|
+
const identityBase = {
|
|
1005
|
+
jobId: 'job-123',
|
|
1006
|
+
sessionId: 'sess-1',
|
|
1007
|
+
instance: { instanceId: 'inst-1', type: 'gateway' }
|
|
1008
|
+
};
|
|
1009
|
+
|
|
996
1010
|
await gateway.invoke({
|
|
997
1011
|
aiRequestId: 'req-001',
|
|
998
|
-
|
|
1012
|
+
identity: { ...identityBase, taskId: 'task-001' },
|
|
999
1013
|
jobTypeId,
|
|
1000
1014
|
agentId: 'agent-1',
|
|
1001
|
-
|
|
1002
|
-
instance: { instanceId: 'inst-1', type: 'gateway' },
|
|
1003
|
-
// ...
|
|
1015
|
+
// objectTypes, messages, ...
|
|
1004
1016
|
});
|
|
1005
1017
|
|
|
1006
1018
|
await gateway.invoke({
|
|
1007
1019
|
aiRequestId: 'req-002',
|
|
1008
|
-
|
|
1020
|
+
identity: { ...identityBase, taskId: 'task-002' },
|
|
1009
1021
|
jobTypeId,
|
|
1010
1022
|
agentId: 'agent-1',
|
|
1011
|
-
|
|
1012
|
-
instance: { instanceId: 'inst-1', type: 'gateway' },
|
|
1013
|
-
// ...
|
|
1023
|
+
// objectTypes, messages, ...
|
|
1014
1024
|
});
|
|
1015
1025
|
|
|
1016
1026
|
// Query in Mongo (main collection name is ai-activities):
|
|
1017
1027
|
// db.getCollection('ai-activities').find({ 'runContext.aiRequestId': 'req-001' })
|
|
1018
|
-
// db.getCollection('ai-activities').find({ 'runContext.jobId': 'job-123' })
|
|
1028
|
+
// db.getCollection('ai-activities').find({ 'runContext.jobId': 'job-123' })
|
|
1019
1029
|
```
|
|
1020
1030
|
|
|
1021
1031
|
#### Configuration
|
|
@@ -1059,7 +1069,7 @@ const gateway = new AIGateway({
|
|
|
1059
1069
|
// Unique identifier (MongoDB auto-generated)
|
|
1060
1070
|
_id: ObjectId('693970636e8d0f171e4aa528'), // ← UNIQUE per activity
|
|
1061
1071
|
|
|
1062
|
-
// Activix
|
|
1072
|
+
// Activix v6: canonical correlation BSON object `runContext` (same reference as `request.identity`, merged with gateway fields)
|
|
1063
1073
|
runContext: {
|
|
1064
1074
|
sessionId: 'sess-1',
|
|
1065
1075
|
instance: { instanceId: 'gw-1', type: 'gateway' },
|
|
@@ -1086,11 +1096,9 @@ const gateway = new AIGateway({
|
|
|
1086
1096
|
graphId: 'graph-456',
|
|
1087
1097
|
nodeId: 'node-789',
|
|
1088
1098
|
|
|
1089
|
-
// Required activity I/O
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
// inner?: { input, output, metadata }
|
|
1093
|
-
},
|
|
1099
|
+
// Required activity I/O: root-level `outer` (optional `inner[]` for steps) — Activix v6 (see @x12i/activix docs)
|
|
1100
|
+
outer: { input: { ... }, output: { ... } | null, metadata: { ... } },
|
|
1101
|
+
// inner: [ { input, output, metadata, startedAt, endedAt, ... }, ... ],
|
|
1094
1102
|
|
|
1095
1103
|
// Timing
|
|
1096
1104
|
startTime: 1765372020804,
|
|
@@ -1142,11 +1150,11 @@ const gateway = new AIGateway({
|
|
|
1142
1150
|
**Key points:**
|
|
1143
1151
|
- ✅ Each activity = separate record with unique `_id`
|
|
1144
1152
|
- ✅ **`aiRequestId`** = per-request correlation (required on invoke)
|
|
1145
|
-
- ✅
|
|
1153
|
+
- ✅ **`runContext.jobId`** / **`runContext.taskId`** = upstream identity (required on invoke since v9+)
|
|
1146
1154
|
- ✅ Request data sent once at activity start; response data on completion
|
|
1147
1155
|
- ✅ Updates use Activix record id / `_id`, not `jobId`
|
|
1148
1156
|
|
|
1149
|
-
#### Retry Tracking (@x12i/activix
|
|
1157
|
+
#### Retry Tracking (@x12i/activix v6)
|
|
1150
1158
|
|
|
1151
1159
|
The gateway automatically retries network errors, server errors (5xx), and throttling (429) with exponential backoff. Retry attempts are tracked and stored in activity records.
|
|
1152
1160
|
|
|
@@ -1374,14 +1382,16 @@ const response = await gateway.invoke({
|
|
|
1374
1382
|
|
|
1375
1383
|
### 5. Structured Logging
|
|
1376
1384
|
|
|
1377
|
-
The gateway uses
|
|
1385
|
+
The gateway uses **`@x12i/logxer`** for structured logging with **`LogMeta`** correlation. See [Logger initialization](./docs/LOGGER_INITIALIZATION.md).
|
|
1378
1386
|
|
|
1379
1387
|
```typescript
|
|
1388
|
+
import { createLogxer } from '@x12i/logxer';
|
|
1389
|
+
|
|
1380
1390
|
const gateway = new AIGateway({
|
|
1381
1391
|
enableLogging: true,
|
|
1382
1392
|
packageName: 'MY_APP',
|
|
1383
|
-
logger:
|
|
1384
|
-
{ packageName: 'MY_APP', envPrefix: 'MY_APP' },
|
|
1393
|
+
logger: createLogxer(
|
|
1394
|
+
{ packageName: 'MY_APP', envPrefix: 'MY_APP', debugNamespace: 'my-app' },
|
|
1385
1395
|
{
|
|
1386
1396
|
logLevel: 'info',
|
|
1387
1397
|
logFormat: 'json',
|
|
@@ -2897,7 +2907,7 @@ const result = await gateway.call({
|
|
|
2897
2907
|
|
|
2898
2908
|
#### Integration with Activity Tracking
|
|
2899
2909
|
|
|
2900
|
-
Contract output parsing integrates seamlessly with `@x12i/activix`
|
|
2910
|
+
Contract output parsing integrates seamlessly with `@x12i/activix` v6 (xronox-activitix):
|
|
2901
2911
|
|
|
2902
2912
|
- **Automatic Processing**: Parsing happens automatically when `expectedSchema` is provided
|
|
2903
2913
|
- **Error Resilience**: Parsing failures don't break activity recording
|
|
@@ -2925,10 +2935,13 @@ const schema = {
|
|
|
2925
2935
|
|
|
2926
2936
|
const response = await gateway.invoke({
|
|
2927
2937
|
aiRequestId: 'analysis-req-001',
|
|
2928
|
-
|
|
2938
|
+
identity: {
|
|
2939
|
+
jobId: 'analysis-123',
|
|
2940
|
+
taskId: 'analysis-task-001',
|
|
2941
|
+
sessionId: 'sess-analyzer',
|
|
2942
|
+
instance: { instanceId: 'analyzer-1', type: 'gateway' }
|
|
2943
|
+
},
|
|
2929
2944
|
agentId: 'analyzer-bot',
|
|
2930
|
-
sessionId: 'sess-analyzer',
|
|
2931
|
-
instance: { instanceId: 'analyzer-1', type: 'gateway' },
|
|
2932
2945
|
instructions: 'Analyze the provided data...',
|
|
2933
2946
|
expectedSchema: schema,
|
|
2934
2947
|
messages: [{ role: 'user', content: data }]
|
|
@@ -2956,8 +2969,8 @@ interface GatewayConfig extends RouterConfig {
|
|
|
2956
2969
|
enableUsageTracking?: boolean; // Default: true
|
|
2957
2970
|
enableLogging?: boolean; // Default: true
|
|
2958
2971
|
contentRegistryConfig?: any; // Content registry config for instruction key resolution
|
|
2959
|
-
logger?:
|
|
2960
|
-
activityTracker?: Activix; // Custom Activix
|
|
2972
|
+
logger?: import('@x12i/logxer').Logxer; // Custom Logxer (from createLogxer)
|
|
2973
|
+
activityTracker?: Activix; // Custom Activix v6 instance (match collection names: ai-activities, bad-requests, skill-executions)
|
|
2961
2974
|
packageName?: string; // Default: 'AI_GATEWAY'
|
|
2962
2975
|
// ... all RouterConfig options
|
|
2963
2976
|
}
|
|
@@ -2978,7 +2991,7 @@ interface GatewayConfig extends RouterConfig {
|
|
|
2978
2991
|
- `checkHealth(provider: LLMProvider): Promise<HealthCheckResult>` - Check provider health
|
|
2979
2992
|
- `checkAllHealth(): Promise<Map<LLMProvider, HealthCheckResult>>` - Check all providers' health
|
|
2980
2993
|
- `getRouter(): LLMProviderRouter` - Get underlying router instance
|
|
2981
|
-
- `getLogger():
|
|
2994
|
+
- `getLogger(): Logxer` - Get logger instance (`@x12i/logxer`)
|
|
2982
2995
|
- `setActivityManager(activityManager)` - Advanced/test hook to replace the internal activity manager; typical apps inject **`Activix` via `activityTracker`** in the constructor instead
|
|
2983
2996
|
- `getContentRegistry(): ContentRegistry | undefined` - Get underlying content-registry instance (returns undefined if not enabled)
|
|
2984
2997
|
- `resolveInstructionKey(key: string, variables?: Record<string, unknown>): Promise<string>` - Resolve instruction key with variables (without making LLM call)
|
|
@@ -3727,10 +3740,10 @@ const gateway = new AIGateway({
|
|
|
3727
3740
|
### Custom Logger
|
|
3728
3741
|
|
|
3729
3742
|
```typescript
|
|
3730
|
-
import {
|
|
3743
|
+
import { createLogxer } from '@x12i/logxer';
|
|
3731
3744
|
|
|
3732
|
-
const customLogger =
|
|
3733
|
-
{ packageName: 'MY_APP', envPrefix: 'MY_APP' },
|
|
3745
|
+
const customLogger = createLogxer(
|
|
3746
|
+
{ packageName: 'MY_APP', envPrefix: 'MY_APP', debugNamespace: 'my-app' },
|
|
3734
3747
|
{
|
|
3735
3748
|
logLevel: 'debug',
|
|
3736
3749
|
logFormat: 'json',
|
|
@@ -4155,8 +4168,8 @@ This package integrates with:
|
|
|
4155
4168
|
- **@x12i/ai-providers-router**: Core routing functionality
|
|
4156
4169
|
- **@xronoces/content-registry**: Instruction key resolution and content management (optional)
|
|
4157
4170
|
- **@x12i/x-models**: Usage tier tracking and model metadata
|
|
4158
|
-
- **@x12i/activix**
|
|
4159
|
-
-
|
|
4171
|
+
- **@x12i/activix** v6 (xronox-activitix): Activity logging and tracking (`^6.5.1` in this package)
|
|
4172
|
+
- **@x12i/logxer**: Structured logging with correlation (`^4.2.1` in this package)
|
|
4160
4173
|
- **nx-config2**: Configuration management (via dependencies)
|
|
4161
4174
|
|
|
4162
4175
|
## Related Packages Status
|
|
@@ -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;
|
package/dist/activity-manager.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { Activix, activixActivityIo, activixOuterTier } from '@x12i/activix';
|
|
8
8
|
import { resolveActivityTrackingConfig } from './config/activity-tracking-config.js';
|
|
9
|
+
import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
|
|
9
10
|
function readAiRequestIdFromRequest(request) {
|
|
10
11
|
const aiRequestId = request.aiRequestId;
|
|
11
12
|
if (typeof aiRequestId === 'string' && aiRequestId.trim().length > 0) {
|
|
@@ -102,40 +103,64 @@ function buildInstanceEnvelope(request) {
|
|
|
102
103
|
function resolveSessionIdForRequest(request, incomingIdentity, aiRequestId) {
|
|
103
104
|
const fromIdentity = trimIdentityString(incomingIdentity?.sessionId);
|
|
104
105
|
const topLevel = trimIdentityString(request.sessionId);
|
|
105
|
-
const
|
|
106
|
+
const identityJobId = trimIdentityString(incomingIdentity?.jobId);
|
|
106
107
|
if (fromIdentity !== undefined && topLevel !== undefined && fromIdentity !== topLevel) {
|
|
107
108
|
throw new Error(`identity.sessionId (${fromIdentity}) and top-level sessionId (${topLevel}) must agree`);
|
|
108
109
|
}
|
|
109
|
-
return fromIdentity ?? topLevel ??
|
|
110
|
+
return fromIdentity ?? topLevel ?? identityJobId ?? aiRequestId;
|
|
111
|
+
}
|
|
112
|
+
function logUpstreamIdentityWarnings(logger, incomingIdentity, merged) {
|
|
113
|
+
const aiRequestId = merged.aiRequestId;
|
|
114
|
+
if (!incomingIdentity || typeof incomingIdentity !== 'object') {
|
|
115
|
+
logger?.warn('missingRuntimeIdentityObject', withActivityIdentity(merged, {
|
|
116
|
+
message: 'request.identity is missing; upstream must send the mandatory runtime identity object',
|
|
117
|
+
aiRequestId,
|
|
118
|
+
debugKind: gatewayLogDebug.anomaly
|
|
119
|
+
}));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const missing = [];
|
|
123
|
+
if (!trimIdentityString(incomingIdentity.jobId))
|
|
124
|
+
missing.push('jobId');
|
|
125
|
+
if (!trimIdentityString(incomingIdentity.taskId))
|
|
126
|
+
missing.push('taskId');
|
|
127
|
+
if (missing.length > 0) {
|
|
128
|
+
logger?.warn('missingUpstreamIdentityFields', withActivityIdentity(merged, {
|
|
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.`,
|
|
130
|
+
missingFields: missing,
|
|
131
|
+
aiRequestId,
|
|
132
|
+
debugKind: gatewayLogDebug.anomaly
|
|
133
|
+
}));
|
|
134
|
+
}
|
|
110
135
|
}
|
|
111
136
|
function mergeGatewayActivityIdentity(request, aiRequestId, extras) {
|
|
112
137
|
const incomingIdentity = request.identity;
|
|
113
138
|
const sessionId = resolveSessionIdForRequest(request, incomingIdentity, aiRequestId);
|
|
114
|
-
const
|
|
139
|
+
const upstreamJobId = trimIdentityString(incomingIdentity?.jobId) ?? '';
|
|
140
|
+
const upstreamTaskId = trimIdentityString(incomingIdentity?.taskId) ?? '';
|
|
115
141
|
const combinedEnrichment = pickGatewayEnrichmentFields({
|
|
116
142
|
...graphIdentityExtrasFromRequest(request),
|
|
117
143
|
...(extras || {})
|
|
118
144
|
});
|
|
119
145
|
const safeEnrichment = mergeActivixEnrichment(incomingIdentity, combinedEnrichment);
|
|
120
|
-
//
|
|
121
|
-
// Gateway enrichment (graph/skill, etc.) fills only keys the outer envelope did not already set.
|
|
146
|
+
// jobId / taskId: upstream `request.identity` only (never top-level request.jobId / request.taskId).
|
|
122
147
|
const merged = {
|
|
123
148
|
jobTypeId: request.jobTypeId,
|
|
124
149
|
agentId: request.agentId,
|
|
125
|
-
taskId: request.taskId,
|
|
126
150
|
taskTypeId: request.taskTypeId,
|
|
127
151
|
...safeEnrichment,
|
|
128
152
|
...(incomingIdentity || {}),
|
|
129
153
|
sessionId,
|
|
130
154
|
instance: buildInstanceEnvelope(request),
|
|
131
|
-
aiRequestId
|
|
155
|
+
aiRequestId,
|
|
156
|
+
jobId: upstreamJobId,
|
|
157
|
+
taskId: upstreamTaskId
|
|
132
158
|
};
|
|
133
159
|
merged.sessionId = sessionId;
|
|
134
160
|
merged.instance = buildInstanceEnvelope(request);
|
|
135
161
|
merged.aiRequestId = aiRequestId;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
162
|
+
merged.jobId = upstreamJobId;
|
|
163
|
+
merged.taskId = upstreamTaskId;
|
|
139
164
|
return merged;
|
|
140
165
|
}
|
|
141
166
|
/**
|
|
@@ -145,13 +170,16 @@ function mergeGatewayActivityIdentity(request, aiRequestId, extras) {
|
|
|
145
170
|
* **Execution context:** treats `request.identity` as received from upstream. Root fields (`sessionId`,
|
|
146
171
|
* `instance`, and any keys already set on that object) are not replaced by gateway defaults; the gateway
|
|
147
172
|
* only fills missing required pieces and adds local context (e.g. graph linkage) where absent.
|
|
173
|
+
*
|
|
174
|
+
* @param logger - Optional Logxer used to WARN when mandatory upstream `identity.jobId` / `identity.taskId` are absent.
|
|
148
175
|
*/
|
|
149
|
-
export function ensureGatewayRequestIdentity(request, extras) {
|
|
176
|
+
export function ensureGatewayRequestIdentity(request, extras, logger) {
|
|
150
177
|
const aiRequestId = readAiRequestIdFromRequest(request);
|
|
151
178
|
const identity = mergeGatewayActivityIdentity(request, aiRequestId, {
|
|
152
179
|
...graphIdentityExtrasFromRequest(request),
|
|
153
180
|
...(extras || {})
|
|
154
181
|
});
|
|
182
|
+
logUpstreamIdentityWarnings(logger, request.identity, identity);
|
|
155
183
|
request.identity = identity;
|
|
156
184
|
return identity;
|
|
157
185
|
}
|
|
@@ -287,7 +315,7 @@ export class ActivityManager {
|
|
|
287
315
|
* @returns Activity metadata (contains unique _id/recordId) or undefined if tracking is disabled
|
|
288
316
|
*/
|
|
289
317
|
async startActivity(request, startTime) {
|
|
290
|
-
const identity = ensureGatewayRequestIdentity(request);
|
|
318
|
+
const identity = ensureGatewayRequestIdentity(request, undefined, this.logger);
|
|
291
319
|
const aiRequestId = identity.aiRequestId;
|
|
292
320
|
// Capture ONLY request data - NO response data at this stage
|
|
293
321
|
// Note: v2.3.2+ excludes flat request/config fields from root level
|
|
@@ -307,9 +335,10 @@ export class ActivityManager {
|
|
|
307
335
|
}
|
|
308
336
|
const activityMetadata = {
|
|
309
337
|
aiRequestId,
|
|
338
|
+
jobId: identity.jobId,
|
|
310
339
|
jobTypeId: request.jobTypeId,
|
|
311
340
|
agentId: request.agentId,
|
|
312
|
-
taskId:
|
|
341
|
+
taskId: identity.taskId,
|
|
313
342
|
taskTypeId: request.taskTypeId,
|
|
314
343
|
startTime,
|
|
315
344
|
status: 'started',
|
|
@@ -503,7 +532,7 @@ export class ActivityManager {
|
|
|
503
532
|
}
|
|
504
533
|
// Determine inference type (will be extracted in gateway.ts and passed via request metadata)
|
|
505
534
|
const inferenceType = aiRequest.inferenceType || 'question-answer';
|
|
506
|
-
const identity = ensureGatewayRequestIdentity(request);
|
|
535
|
+
const identity = ensureGatewayRequestIdentity(request, undefined, this.logger);
|
|
507
536
|
const aiRequestId = identity.aiRequestId;
|
|
508
537
|
if (!this.activix) {
|
|
509
538
|
return undefined;
|
|
@@ -521,9 +550,10 @@ export class ActivityManager {
|
|
|
521
550
|
skillId: skillId ?? skillKey,
|
|
522
551
|
inferenceType, // ✅ Recommended: type of inference
|
|
523
552
|
aiRequestId,
|
|
553
|
+
jobId: identity.jobId,
|
|
524
554
|
jobTypeId: request.jobTypeId,
|
|
525
555
|
agentId: request.agentId,
|
|
526
|
-
taskId:
|
|
556
|
+
taskId: identity.taskId,
|
|
527
557
|
taskTypeId: request.taskTypeId,
|
|
528
558
|
startTime,
|
|
529
559
|
status: 'started',
|
|
@@ -897,7 +927,7 @@ export class ActivityManager {
|
|
|
897
927
|
* @param startTime - When the request started
|
|
898
928
|
*/
|
|
899
929
|
async logBadRequest(request, error, details, startTime) {
|
|
900
|
-
const identity = ensureGatewayRequestIdentity(request);
|
|
930
|
+
const identity = ensureGatewayRequestIdentity(request, undefined, this.logger);
|
|
901
931
|
const aiRequestId = identity.aiRequestId;
|
|
902
932
|
const endTime = details.endTime;
|
|
903
933
|
const duration = details.duration;
|
|
@@ -922,9 +952,10 @@ export class ActivityManager {
|
|
|
922
952
|
const badRequestMetadata = {
|
|
923
953
|
activityType: 'bad-request',
|
|
924
954
|
aiRequestId,
|
|
955
|
+
jobId: identity.jobId,
|
|
925
956
|
jobTypeId: request.jobTypeId,
|
|
926
957
|
agentId: request.agentId,
|
|
927
|
-
taskId:
|
|
958
|
+
taskId: identity.taskId,
|
|
928
959
|
taskTypeId: request.taskTypeId,
|
|
929
960
|
startTime,
|
|
930
961
|
status: 'started',
|