@jsonstudio/llms 0.6.938 → 0.6.1164
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/dist/conversion/hub/operation-table/operation-table-runner.d.ts +18 -0
- package/dist/conversion/hub/operation-table/operation-table-runner.js +158 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.d.ts +8 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +303 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.d.ts +8 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.js +413 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.d.ts +7 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +841 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.d.ts +21 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +535 -0
- package/dist/conversion/hub/ops/operations.d.ts +19 -0
- package/dist/conversion/hub/ops/operations.js +126 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +9 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +533 -24
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +6 -0
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +6 -3
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +11 -0
- package/dist/conversion/hub/policy/policy-engine.js +41 -9
- package/dist/conversion/hub/policy/protocol-spec.d.ts +25 -0
- package/dist/conversion/hub/policy/protocol-spec.js +73 -23
- package/dist/conversion/hub/process/chat-process.js +252 -41
- package/dist/conversion/hub/response/provider-response.js +175 -2
- package/dist/conversion/hub/response/response-runtime.js +1 -1
- package/dist/conversion/hub/semantic-mappers/anthropic-mapper.d.ts +1 -8
- package/dist/conversion/hub/semantic-mappers/anthropic-mapper.js +1 -365
- package/dist/conversion/hub/semantic-mappers/chat-mapper.d.ts +1 -8
- package/dist/conversion/hub/semantic-mappers/chat-mapper.js +1 -436
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.d.ts +1 -7
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +1 -894
- package/dist/conversion/hub/semantic-mappers/responses-mapper.d.ts +1 -21
- package/dist/conversion/hub/semantic-mappers/responses-mapper.js +1 -593
- package/dist/conversion/hub/tool-surface/tool-surface-engine.d.ts +18 -0
- package/dist/conversion/hub/tool-surface/tool-surface-engine.js +571 -0
- package/dist/conversion/responses/responses-openai-bridge.js +14 -2
- package/dist/conversion/shared/bridge-message-utils.js +2 -8
- package/dist/conversion/shared/bridge-policies.js +5 -105
- package/dist/conversion/shared/gemini-tool-utils.js +121 -4
- package/dist/conversion/shared/protocol-field-allowlists.d.ts +7 -0
- package/dist/conversion/shared/protocol-field-allowlists.js +145 -0
- package/dist/conversion/shared/reasoning-tool-normalizer.js +4 -2
- package/dist/conversion/shared/snapshot-hooks.js +166 -3
- package/dist/conversion/shared/text-markup-normalizer.d.ts +2 -0
- package/dist/conversion/shared/text-markup-normalizer.js +345 -9
- package/dist/conversion/shared/thought-signature-validator.d.ts +52 -0
- package/dist/conversion/shared/thought-signature-validator.js +170 -0
- package/dist/conversion/shared/tool-argument-repairer.d.ts +39 -0
- package/dist/conversion/shared/tool-argument-repairer.js +56 -0
- package/dist/conversion/shared/tool-call-id-manager.d.ts +113 -0
- package/dist/conversion/shared/tool-call-id-manager.js +231 -0
- package/dist/conversion/shared/tool-canonicalizer.js +2 -11
- package/dist/router/virtual-router/bootstrap.js +54 -5
- package/dist/router/virtual-router/engine-selection.js +132 -42
- package/dist/router/virtual-router/engine.d.ts +3 -0
- package/dist/router/virtual-router/engine.js +142 -33
- package/dist/router/virtual-router/health-weighted.d.ts +25 -0
- package/dist/router/virtual-router/health-weighted.js +63 -0
- package/dist/router/virtual-router/load-balancer.d.ts +2 -0
- package/dist/router/virtual-router/load-balancer.js +45 -16
- package/dist/router/virtual-router/routing-instructions.js +17 -1
- package/dist/router/virtual-router/sticky-session-store.js +136 -24
- package/dist/router/virtual-router/stop-message-file-resolver.d.ts +1 -0
- package/dist/router/virtual-router/stop-message-file-resolver.js +74 -0
- package/dist/router/virtual-router/stop-message-state-sync.d.ts +15 -0
- package/dist/router/virtual-router/stop-message-state-sync.js +57 -0
- package/dist/router/virtual-router/types.d.ts +70 -0
- package/dist/servertool/clock/config.d.ts +7 -0
- package/dist/servertool/clock/config.js +27 -0
- package/dist/servertool/clock/daemon.d.ts +3 -0
- package/dist/servertool/clock/daemon.js +79 -0
- package/dist/servertool/clock/io.d.ts +2 -0
- package/dist/servertool/clock/io.js +13 -0
- package/dist/servertool/clock/paths.d.ts +4 -0
- package/dist/servertool/clock/paths.js +25 -0
- package/dist/servertool/clock/session-store.d.ts +3 -0
- package/dist/servertool/clock/session-store.js +56 -0
- package/dist/servertool/clock/state.d.ts +5 -0
- package/dist/servertool/clock/state.js +62 -0
- package/dist/servertool/clock/task-store.d.ts +5 -0
- package/dist/servertool/clock/task-store.js +4 -0
- package/dist/servertool/clock/tasks.d.ts +17 -0
- package/dist/servertool/clock/tasks.js +221 -0
- package/dist/servertool/clock/types.d.ts +36 -0
- package/dist/servertool/clock/types.js +1 -0
- package/dist/servertool/engine.d.ts +2 -0
- package/dist/servertool/engine.js +164 -8
- package/dist/servertool/followup-shadow.d.ts +16 -0
- package/dist/servertool/followup-shadow.js +145 -0
- package/dist/servertool/handlers/apply-patch-guard.js +1 -265
- package/dist/servertool/handlers/clock-auto.d.ts +1 -0
- package/dist/servertool/handlers/clock-auto.js +160 -0
- package/dist/servertool/handlers/clock.d.ts +1 -0
- package/dist/servertool/handlers/clock.js +197 -0
- package/dist/servertool/handlers/exec-command-guard.js +7 -555
- package/dist/servertool/handlers/followup-request-builder.d.ts +15 -7
- package/dist/servertool/handlers/followup-request-builder.js +248 -28
- package/dist/servertool/handlers/gemini-empty-reply-continue.js +62 -169
- package/dist/servertool/handlers/iflow-model-error-retry.js +18 -28
- package/dist/servertool/handlers/recursive-detection-guard.d.ts +1 -0
- package/dist/servertool/handlers/recursive-detection-guard.js +333 -0
- package/dist/servertool/handlers/stop-message-auto.js +47 -175
- package/dist/servertool/handlers/vision.d.ts +7 -1
- package/dist/servertool/handlers/vision.js +61 -117
- package/dist/servertool/handlers/web-search.d.ts +7 -1
- package/dist/servertool/handlers/web-search.js +122 -105
- package/dist/servertool/reenter-backend.d.ts +23 -0
- package/dist/servertool/reenter-backend.js +18 -0
- package/dist/servertool/server-side-tools.d.ts +3 -2
- package/dist/servertool/server-side-tools.js +64 -10
- package/dist/servertool/types.d.ts +92 -3
- package/dist/sse/json-to-sse/event-generators/responses.js +3 -21
- package/dist/sse/shared/serializers/responses-event-serializer.d.ts +8 -0
- package/dist/sse/shared/serializers/responses-event-serializer.js +19 -0
- package/dist/sse/shared/writer.js +24 -7
- package/dist/tools/apply-patch/execution-capturer.js +3 -1
- package/dist/tools/apply-patch/json/parse-loose.d.ts +3 -0
- package/dist/tools/apply-patch/json/parse-loose.js +139 -0
- package/dist/tools/apply-patch/patch-text/context-diff.d.ts +1 -0
- package/dist/tools/apply-patch/patch-text/context-diff.js +173 -0
- package/dist/tools/apply-patch/patch-text/git-diff.d.ts +1 -0
- package/dist/tools/apply-patch/patch-text/git-diff.js +138 -0
- package/dist/tools/apply-patch/patch-text/looks-like-patch.d.ts +1 -0
- package/dist/tools/apply-patch/patch-text/looks-like-patch.js +13 -0
- package/dist/tools/apply-patch/patch-text/normalize.d.ts +3 -0
- package/dist/tools/apply-patch/patch-text/normalize.js +262 -0
- package/dist/tools/apply-patch/structured/coercion.d.ts +3 -0
- package/dist/tools/apply-patch/structured/coercion.js +82 -0
- package/dist/tools/apply-patch/validation/shared.d.ts +3 -0
- package/dist/tools/apply-patch/validation/shared.js +6 -0
- package/dist/tools/apply-patch/validator.d.ts +2 -2
- package/dist/tools/apply-patch/validator.js +6 -556
- package/package.json +1 -1
|
@@ -26,6 +26,86 @@ import { extractSessionIdentifiersFromMetadata } from './session-identifiers.js'
|
|
|
26
26
|
import { computeRequestTokens } from '../../../router/virtual-router/token-estimator.js';
|
|
27
27
|
import { isCompactionRequest } from '../../shared/compaction-detect.js';
|
|
28
28
|
import { applyHubProviderOutboundPolicy, recordHubPolicyObservation, setHubPolicyRuntimePolicy } from '../policy/policy-engine.js';
|
|
29
|
+
import { applyProviderOutboundToolSurface } from '../tool-surface/tool-surface-engine.js';
|
|
30
|
+
import { applyHubOperations } from '../ops/operations.js';
|
|
31
|
+
function isTruthyEnv(value) {
|
|
32
|
+
const v = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
33
|
+
return v === '1' || v === 'true' || v === 'yes' || v === 'on';
|
|
34
|
+
}
|
|
35
|
+
function resolveApplyPatchToolModeFromEnv() {
|
|
36
|
+
const rawMode = String(process.env.RCC_APPLY_PATCH_TOOL_MODE || process.env.ROUTECODEX_APPLY_PATCH_TOOL_MODE || '')
|
|
37
|
+
.trim()
|
|
38
|
+
.toLowerCase();
|
|
39
|
+
if (rawMode === 'freeform')
|
|
40
|
+
return 'freeform';
|
|
41
|
+
if (rawMode === 'schema' || rawMode === 'json_schema')
|
|
42
|
+
return 'schema';
|
|
43
|
+
const freeformFlag = process.env.RCC_APPLY_PATCH_FREEFORM || process.env.ROUTECODEX_APPLY_PATCH_FREEFORM;
|
|
44
|
+
if (isTruthyEnv(freeformFlag))
|
|
45
|
+
return 'freeform';
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
function resolveApplyPatchToolModeFromTools(toolsRaw) {
|
|
49
|
+
if (!Array.isArray(toolsRaw) || toolsRaw.length === 0) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
for (const entry of toolsRaw) {
|
|
53
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry))
|
|
54
|
+
continue;
|
|
55
|
+
const record = entry;
|
|
56
|
+
const type = typeof record.type === 'string' ? record.type.trim().toLowerCase() : '';
|
|
57
|
+
if (type && type !== 'function')
|
|
58
|
+
continue;
|
|
59
|
+
const fn = record.function && typeof record.function === 'object' && !Array.isArray(record.function)
|
|
60
|
+
? record.function
|
|
61
|
+
: undefined;
|
|
62
|
+
const name = typeof fn?.name === 'string' ? fn.name.trim().toLowerCase() : '';
|
|
63
|
+
if (name !== 'apply_patch')
|
|
64
|
+
continue;
|
|
65
|
+
const format = typeof record.format === 'string'
|
|
66
|
+
? record.format.trim().toLowerCase()
|
|
67
|
+
: typeof fn?.format === 'string'
|
|
68
|
+
? String(fn.format).trim().toLowerCase()
|
|
69
|
+
: '';
|
|
70
|
+
if (format === 'freeform')
|
|
71
|
+
return 'freeform';
|
|
72
|
+
// If apply_patch is present without explicit freeform marker, default to schema mode.
|
|
73
|
+
return 'schema';
|
|
74
|
+
}
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
function extractHubPolicyOverride(metadata) {
|
|
78
|
+
const raw = metadata ? metadata.__hubPolicyOverride : undefined;
|
|
79
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
const obj = raw;
|
|
83
|
+
const mode = typeof obj.mode === 'string' ? obj.mode.trim().toLowerCase() : '';
|
|
84
|
+
const sampleRate = typeof obj.sampleRate === 'number' && Number.isFinite(obj.sampleRate) ? obj.sampleRate : undefined;
|
|
85
|
+
if (mode !== 'off' && mode !== 'observe' && mode !== 'enforce') {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
mode: mode,
|
|
90
|
+
...(sampleRate !== undefined ? { sampleRate } : {})
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function extractHubShadowCompareConfig(metadata) {
|
|
94
|
+
const raw = metadata ? metadata.__hubShadowCompare : undefined;
|
|
95
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
const obj = raw;
|
|
99
|
+
const modeCandidate = typeof obj.baselineMode === 'string'
|
|
100
|
+
? obj.baselineMode.trim().toLowerCase()
|
|
101
|
+
: typeof obj.mode === 'string'
|
|
102
|
+
? obj.mode.trim().toLowerCase()
|
|
103
|
+
: '';
|
|
104
|
+
if (modeCandidate !== 'off' && modeCandidate !== 'observe' && modeCandidate !== 'enforce') {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
return { baselineMode: modeCandidate };
|
|
108
|
+
}
|
|
29
109
|
export class HubPipeline {
|
|
30
110
|
routerEngine;
|
|
31
111
|
config;
|
|
@@ -104,6 +184,11 @@ export class HubPipeline {
|
|
|
104
184
|
// not the governed/augmented tools.
|
|
105
185
|
try {
|
|
106
186
|
const toolsRaw = Array.isArray(rawRequest?.tools) ? rawRequest.tools : null;
|
|
187
|
+
const applyPatchToolMode = resolveApplyPatchToolModeFromEnv() ?? resolveApplyPatchToolModeFromTools(toolsRaw);
|
|
188
|
+
if (applyPatchToolMode) {
|
|
189
|
+
normalized.metadata = normalized.metadata || {};
|
|
190
|
+
normalized.metadata.applyPatchToolMode = applyPatchToolMode;
|
|
191
|
+
}
|
|
107
192
|
if (toolsRaw && toolsRaw.length > 0) {
|
|
108
193
|
normalized.metadata = normalized.metadata || {};
|
|
109
194
|
normalized.metadata.clientToolsRaw = jsonClone(toolsRaw);
|
|
@@ -116,11 +201,16 @@ export class HubPipeline {
|
|
|
116
201
|
normalized.metadata = normalized.metadata || {};
|
|
117
202
|
normalized.metadata.compactionRequest = true;
|
|
118
203
|
}
|
|
204
|
+
const effectivePolicy = normalized.policyOverride ?? this.config.policy;
|
|
205
|
+
const shadowCompareBaselineMode = normalized.shadowCompare?.baselineMode;
|
|
119
206
|
const inboundAdapterContext = this.buildAdapterContext(normalized);
|
|
120
|
-
const inboundRecorder = this.maybeCreateStageRecorder(inboundAdapterContext, normalized.entryEndpoint
|
|
207
|
+
const inboundRecorder = this.maybeCreateStageRecorder(inboundAdapterContext, normalized.entryEndpoint, {
|
|
208
|
+
disableSnapshots: normalized.disableSnapshots === true
|
|
209
|
+
});
|
|
121
210
|
const inboundStart = Date.now();
|
|
122
211
|
// Phase 0: observe client inbound payload violations (best-effort; no rewrites).
|
|
123
212
|
recordHubPolicyObservation({
|
|
213
|
+
policy: effectivePolicy,
|
|
124
214
|
providerProtocol: this.resolveClientProtocol(normalized.entryEndpoint),
|
|
125
215
|
payload: rawRequest,
|
|
126
216
|
phase: 'client_inbound',
|
|
@@ -146,6 +236,15 @@ export class HubPipeline {
|
|
|
146
236
|
stageRecorder: inboundRecorder
|
|
147
237
|
});
|
|
148
238
|
const standardizedRequest = inboundStage2.standardizedRequest;
|
|
239
|
+
try {
|
|
240
|
+
const mode = String(normalized.metadata?.applyPatchToolMode || '').trim().toLowerCase();
|
|
241
|
+
if (mode === 'freeform' || mode === 'schema') {
|
|
242
|
+
standardizedRequest.metadata.applyPatchToolMode = mode;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
// best-effort: do not block request handling due to metadata propagation failures
|
|
247
|
+
}
|
|
149
248
|
const inboundEnd = Date.now();
|
|
150
249
|
const nodeResults = [];
|
|
151
250
|
nodeResults.push({
|
|
@@ -175,6 +274,10 @@ export class HubPipeline {
|
|
|
175
274
|
if (execCommandGuard) {
|
|
176
275
|
metaBase.execCommandGuard = execCommandGuard;
|
|
177
276
|
}
|
|
277
|
+
const clockConfig = this.config.virtualRouter?.clock;
|
|
278
|
+
if (clockConfig) {
|
|
279
|
+
metaBase.clock = clockConfig;
|
|
280
|
+
}
|
|
178
281
|
normalized.metadata = metaBase;
|
|
179
282
|
let processedRequest;
|
|
180
283
|
if (normalized.processMode !== 'passthrough') {
|
|
@@ -187,11 +290,22 @@ export class HubPipeline {
|
|
|
187
290
|
stageRecorder: inboundRecorder
|
|
188
291
|
});
|
|
189
292
|
processedRequest = processResult.processedRequest;
|
|
293
|
+
// Surface request-side clock reservation into pipeline metadata so response conversion
|
|
294
|
+
// can commit delivery only after a successful response is produced.
|
|
295
|
+
try {
|
|
296
|
+
const reservation = processedRequest?.metadata?.__clockReservation;
|
|
297
|
+
if (reservation && typeof reservation === 'object') {
|
|
298
|
+
metaBase.__clockReservation = reservation;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
// best-effort: do not block request handling due to metadata propagation failures
|
|
303
|
+
}
|
|
190
304
|
if (processResult.nodeResult) {
|
|
191
305
|
nodeResults.push(this.convertProcessNodeResult('req_process_stage1_tool_governance', processResult.nodeResult));
|
|
192
306
|
}
|
|
193
307
|
}
|
|
194
|
-
|
|
308
|
+
let workingRequest = processedRequest ?? standardizedRequest;
|
|
195
309
|
// 使用与 VirtualRouter 一致的 tiktoken 计数逻辑,对标准化请求进行一次
|
|
196
310
|
// 上下文 token 估算,供后续 usage 归一化与统计使用。
|
|
197
311
|
try {
|
|
@@ -247,6 +361,12 @@ export class HubPipeline {
|
|
|
247
361
|
const stopMessageState = this.routerEngine.getStopMessageState(metadataInput);
|
|
248
362
|
if (stopMessageState && normalized.metadata && typeof normalized.metadata === 'object') {
|
|
249
363
|
normalized.metadata.stopMessageState = stopMessageState;
|
|
364
|
+
// Phase 4: model stopMessage as an operation-derived request metadata signal.
|
|
365
|
+
// Response-side servertools can read it from AdapterContext, while request-side
|
|
366
|
+
// diagnostics can read it from StandardizedRequest.metadata.
|
|
367
|
+
workingRequest = applyHubOperations(workingRequest, [
|
|
368
|
+
{ op: 'set_request_metadata_fields', fields: { stopMessageState } }
|
|
369
|
+
]);
|
|
250
370
|
}
|
|
251
371
|
// Emit virtual router hit log for debugging (orange [virtual-router] ...)
|
|
252
372
|
try {
|
|
@@ -262,7 +382,7 @@ export class HubPipeline {
|
|
|
262
382
|
// logging must not break routing
|
|
263
383
|
}
|
|
264
384
|
const outboundStream = this.resolveOutboundStreamIntent(routing.target?.streaming);
|
|
265
|
-
this.applyOutboundStreamPreference(workingRequest, outboundStream);
|
|
385
|
+
workingRequest = this.applyOutboundStreamPreference(workingRequest, outboundStream);
|
|
266
386
|
const outboundAdapterContext = this.buildAdapterContext(normalized, routing.target);
|
|
267
387
|
if (routing.target?.compatibilityProfile) {
|
|
268
388
|
outboundAdapterContext.compatibilityProfile = routing.target.compatibilityProfile;
|
|
@@ -280,7 +400,9 @@ export class HubPipeline {
|
|
|
280
400
|
// Snapshots must be grouped by entry endpoint (client-facing protocol), not by provider protocol.
|
|
281
401
|
// Otherwise one request would be split across multiple folders (e.g. openai-responses + anthropic-messages),
|
|
282
402
|
// which breaks codex-samples correlation.
|
|
283
|
-
const outboundRecorder = this.maybeCreateStageRecorder(outboundAdapterContext, normalized.entryEndpoint
|
|
403
|
+
const outboundRecorder = this.maybeCreateStageRecorder(outboundAdapterContext, normalized.entryEndpoint, {
|
|
404
|
+
disableSnapshots: normalized.disableSnapshots === true
|
|
405
|
+
});
|
|
284
406
|
const outboundStart = Date.now();
|
|
285
407
|
let providerPayload;
|
|
286
408
|
const outboundStage1 = await runReqOutboundStage1SemanticMap({
|
|
@@ -302,15 +424,59 @@ export class HubPipeline {
|
|
|
302
424
|
adapterContext: outboundAdapterContext,
|
|
303
425
|
stageRecorder: outboundRecorder
|
|
304
426
|
});
|
|
427
|
+
let shadowBaselineProviderPayload;
|
|
428
|
+
if (shadowCompareBaselineMode) {
|
|
429
|
+
const baselinePolicy = {
|
|
430
|
+
...(effectivePolicy ?? {}),
|
|
431
|
+
mode: shadowCompareBaselineMode
|
|
432
|
+
};
|
|
433
|
+
// Compute a baseline provider payload in the *same execution*, without recording
|
|
434
|
+
// snapshots/diffs and without re-running the full pipeline. This avoids side effects
|
|
435
|
+
// (conversation store, followup captures, etc.) that a second execute() would trigger.
|
|
436
|
+
const baselineFormatted = typeof globalThis.structuredClone === 'function'
|
|
437
|
+
? globalThis.structuredClone(formattedPayload)
|
|
438
|
+
: jsonClone(formattedPayload);
|
|
439
|
+
let baselinePayload = applyHubProviderOutboundPolicy({
|
|
440
|
+
policy: baselinePolicy,
|
|
441
|
+
providerProtocol: outboundProtocol,
|
|
442
|
+
payload: baselineFormatted,
|
|
443
|
+
stageRecorder: undefined,
|
|
444
|
+
requestId: normalized.id
|
|
445
|
+
});
|
|
446
|
+
baselinePayload = applyProviderOutboundToolSurface({
|
|
447
|
+
config: this.config.toolSurface,
|
|
448
|
+
providerProtocol: outboundProtocol,
|
|
449
|
+
payload: baselinePayload,
|
|
450
|
+
stageRecorder: undefined,
|
|
451
|
+
requestId: normalized.id
|
|
452
|
+
});
|
|
453
|
+
shadowBaselineProviderPayload = baselinePayload;
|
|
454
|
+
}
|
|
455
|
+
// Phase 0/1: observe provider outbound payload violations before any enforcement rewrites.
|
|
456
|
+
// This provides black-box visibility into what the pipeline would have sent upstream.
|
|
457
|
+
recordHubPolicyObservation({
|
|
458
|
+
policy: effectivePolicy,
|
|
459
|
+
providerProtocol: outboundProtocol,
|
|
460
|
+
payload: formattedPayload,
|
|
461
|
+
stageRecorder: outboundRecorder,
|
|
462
|
+
requestId: normalized.id
|
|
463
|
+
});
|
|
305
464
|
providerPayload = applyHubProviderOutboundPolicy({
|
|
306
|
-
policy:
|
|
465
|
+
policy: effectivePolicy,
|
|
307
466
|
providerProtocol: outboundProtocol,
|
|
308
467
|
payload: formattedPayload,
|
|
309
468
|
stageRecorder: outboundRecorder,
|
|
310
469
|
requestId: normalized.id
|
|
311
470
|
});
|
|
471
|
+
providerPayload = applyProviderOutboundToolSurface({
|
|
472
|
+
config: this.config.toolSurface,
|
|
473
|
+
providerProtocol: outboundProtocol,
|
|
474
|
+
payload: providerPayload,
|
|
475
|
+
stageRecorder: outboundRecorder,
|
|
476
|
+
requestId: normalized.id
|
|
477
|
+
});
|
|
312
478
|
recordHubPolicyObservation({
|
|
313
|
-
policy:
|
|
479
|
+
policy: effectivePolicy,
|
|
314
480
|
providerProtocol: outboundProtocol,
|
|
315
481
|
payload: providerPayload,
|
|
316
482
|
stageRecorder: outboundRecorder,
|
|
@@ -365,7 +531,17 @@ export class HubPipeline {
|
|
|
365
531
|
processMode: normalized.processMode,
|
|
366
532
|
routeHint: normalized.routeHint,
|
|
367
533
|
target: routing.target,
|
|
368
|
-
...(typeof outboundStream === 'boolean' ? { providerStream: outboundStream } : {})
|
|
534
|
+
...(typeof outboundStream === 'boolean' ? { providerStream: outboundStream } : {}),
|
|
535
|
+
...(shadowBaselineProviderPayload
|
|
536
|
+
? {
|
|
537
|
+
hubShadowCompare: {
|
|
538
|
+
baselineMode: shadowCompareBaselineMode,
|
|
539
|
+
candidateMode: (effectivePolicy?.mode ?? 'off'),
|
|
540
|
+
providerProtocol: outboundProtocol,
|
|
541
|
+
baselineProviderPayload: shadowBaselineProviderPayload
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
: {})
|
|
369
545
|
};
|
|
370
546
|
return {
|
|
371
547
|
requestId: normalized.id,
|
|
@@ -387,8 +563,310 @@ export class HubPipeline {
|
|
|
387
563
|
return 'anthropic-messages';
|
|
388
564
|
return 'openai-chat';
|
|
389
565
|
}
|
|
566
|
+
coerceStandardizedRequestFromPayload(payload, normalized) {
|
|
567
|
+
const model = typeof payload.model === 'string' && payload.model.trim().length ? payload.model.trim() : '';
|
|
568
|
+
if (!model) {
|
|
569
|
+
throw new Error('[HubPipeline] outbound stage requires payload.model');
|
|
570
|
+
}
|
|
571
|
+
const messages = Array.isArray(payload.messages) ? payload.messages : null;
|
|
572
|
+
if (!messages) {
|
|
573
|
+
throw new Error('[HubPipeline] outbound stage requires payload.messages[]');
|
|
574
|
+
}
|
|
575
|
+
const tools = Array.isArray(payload.tools) ? payload.tools : undefined;
|
|
576
|
+
const parameters = payload.parameters && typeof payload.parameters === 'object' && !Array.isArray(payload.parameters)
|
|
577
|
+
? payload.parameters
|
|
578
|
+
: {};
|
|
579
|
+
const metadataFromPayload = payload.metadata && typeof payload.metadata === 'object' && !Array.isArray(payload.metadata)
|
|
580
|
+
? payload.metadata
|
|
581
|
+
: undefined;
|
|
582
|
+
const standardizedRequest = {
|
|
583
|
+
model,
|
|
584
|
+
messages,
|
|
585
|
+
...(tools ? { tools } : {}),
|
|
586
|
+
parameters,
|
|
587
|
+
metadata: {
|
|
588
|
+
originalEndpoint: normalized.entryEndpoint,
|
|
589
|
+
...(metadataFromPayload ? metadataFromPayload : {}),
|
|
590
|
+
requestId: normalized.id,
|
|
591
|
+
stream: normalized.stream,
|
|
592
|
+
processMode: normalized.processMode,
|
|
593
|
+
...(normalized.routeHint ? { routeHint: normalized.routeHint } : {})
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
// Keep rawPayload minimal and JSON-safe; chat-process only needs the OpenAI-chat-like surface here.
|
|
597
|
+
const rawPayload = {
|
|
598
|
+
model,
|
|
599
|
+
messages,
|
|
600
|
+
...(tools ? { tools } : {}),
|
|
601
|
+
...(parameters && Object.keys(parameters).length ? { parameters } : {})
|
|
602
|
+
};
|
|
603
|
+
return { standardizedRequest, rawPayload };
|
|
604
|
+
}
|
|
605
|
+
async executeChatProcessEntryPipeline(normalized) {
|
|
606
|
+
const hooks = this.resolveProtocolHooks(normalized.providerProtocol);
|
|
607
|
+
if (!hooks) {
|
|
608
|
+
throw new Error(`Unsupported provider protocol for hub pipeline: ${normalized.providerProtocol}`);
|
|
609
|
+
}
|
|
610
|
+
const nodeResults = [];
|
|
611
|
+
nodeResults.push({
|
|
612
|
+
id: 'req_inbound',
|
|
613
|
+
success: true,
|
|
614
|
+
metadata: {
|
|
615
|
+
node: 'req_inbound',
|
|
616
|
+
skipped: true,
|
|
617
|
+
reason: 'stage=outbound',
|
|
618
|
+
dataProcessed: {}
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
const rawPayloadInput = this.asJsonObject(normalized.payload);
|
|
622
|
+
const { standardizedRequest: standardizedRequestBase, rawPayload } = this.coerceStandardizedRequestFromPayload(rawPayloadInput, normalized);
|
|
623
|
+
// Keep metadata injection consistent with the inbound path: servertool/web_search config must be available
|
|
624
|
+
// to chat-process/tool governance even when request enters at outbound stage.
|
|
625
|
+
const metaBase = {
|
|
626
|
+
...(normalized.metadata ?? {})
|
|
627
|
+
};
|
|
628
|
+
const webSearchConfig = this.config.virtualRouter?.webSearch;
|
|
629
|
+
if (webSearchConfig) {
|
|
630
|
+
metaBase.webSearch = webSearchConfig;
|
|
631
|
+
}
|
|
632
|
+
const execCommandGuard = this.config.virtualRouter?.execCommandGuard;
|
|
633
|
+
if (execCommandGuard) {
|
|
634
|
+
metaBase.execCommandGuard = execCommandGuard;
|
|
635
|
+
}
|
|
636
|
+
const clockConfig = this.config.virtualRouter?.clock;
|
|
637
|
+
if (clockConfig) {
|
|
638
|
+
metaBase.clock = clockConfig;
|
|
639
|
+
}
|
|
640
|
+
normalized.metadata = metaBase;
|
|
641
|
+
const standardizedRequest = standardizedRequestBase;
|
|
642
|
+
try {
|
|
643
|
+
const mode = String(metaBase?.applyPatchToolMode || '').trim().toLowerCase();
|
|
644
|
+
if (mode === 'freeform' || mode === 'schema') {
|
|
645
|
+
standardizedRequest.metadata.applyPatchToolMode = mode;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
catch {
|
|
649
|
+
// ignore
|
|
650
|
+
}
|
|
651
|
+
const adapterContext = this.buildAdapterContext(normalized);
|
|
652
|
+
const stageRecorder = this.maybeCreateStageRecorder(adapterContext, normalized.entryEndpoint, {
|
|
653
|
+
disableSnapshots: normalized.disableSnapshots === true
|
|
654
|
+
});
|
|
655
|
+
let processedRequest;
|
|
656
|
+
if (normalized.processMode !== 'passthrough') {
|
|
657
|
+
const processResult = await runReqProcessStage1ToolGovernance({
|
|
658
|
+
request: standardizedRequest,
|
|
659
|
+
rawPayload,
|
|
660
|
+
metadata: metaBase,
|
|
661
|
+
entryEndpoint: normalized.entryEndpoint,
|
|
662
|
+
requestId: normalized.id,
|
|
663
|
+
stageRecorder
|
|
664
|
+
});
|
|
665
|
+
processedRequest = processResult.processedRequest;
|
|
666
|
+
// Surface request-side clock reservation into pipeline metadata so response conversion
|
|
667
|
+
// can commit delivery only after a successful response is produced.
|
|
668
|
+
try {
|
|
669
|
+
const reservation = processedRequest?.metadata?.__clockReservation;
|
|
670
|
+
if (reservation && typeof reservation === 'object') {
|
|
671
|
+
metaBase.__clockReservation = reservation;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
catch {
|
|
675
|
+
// best-effort
|
|
676
|
+
}
|
|
677
|
+
if (processResult.nodeResult) {
|
|
678
|
+
nodeResults.push(this.convertProcessNodeResult('req_process_stage1_tool_governance', processResult.nodeResult));
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
let workingRequest = processedRequest ?? standardizedRequest;
|
|
682
|
+
// Token estimate for stats/diagnostics (best-effort).
|
|
683
|
+
try {
|
|
684
|
+
const estimatedTokens = computeRequestTokens(workingRequest, '');
|
|
685
|
+
if (typeof estimatedTokens === 'number' && Number.isFinite(estimatedTokens) && estimatedTokens > 0) {
|
|
686
|
+
normalized.metadata = normalized.metadata || {};
|
|
687
|
+
normalized.metadata.estimatedInputTokens = estimatedTokens;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
catch {
|
|
691
|
+
// ignore
|
|
692
|
+
}
|
|
693
|
+
const normalizedMeta = normalized.metadata;
|
|
694
|
+
const responsesResume = normalizedMeta && typeof normalizedMeta.responsesResume === 'object'
|
|
695
|
+
? normalizedMeta.responsesResume
|
|
696
|
+
: undefined;
|
|
697
|
+
const stdMetadata = workingRequest?.metadata;
|
|
698
|
+
const hasImageAttachment = (stdMetadata?.hasImageAttachment === true || stdMetadata?.hasImageAttachment === 'true') ||
|
|
699
|
+
(normalizedMeta?.hasImageAttachment === true || normalizedMeta?.hasImageAttachment === 'true');
|
|
700
|
+
const serverToolRequired = stdMetadata?.webSearchEnabled === true ||
|
|
701
|
+
stdMetadata?.serverToolRequired === true;
|
|
702
|
+
const sessionIdentifiers = extractSessionIdentifiersFromMetadata(normalized.metadata);
|
|
703
|
+
if (sessionIdentifiers.sessionId && normalized.metadata && typeof normalized.metadata === 'object') {
|
|
704
|
+
normalized.metadata.sessionId = sessionIdentifiers.sessionId;
|
|
705
|
+
}
|
|
706
|
+
if (sessionIdentifiers.conversationId && normalized.metadata && typeof normalized.metadata === 'object') {
|
|
707
|
+
normalized.metadata.conversationId = sessionIdentifiers.conversationId;
|
|
708
|
+
}
|
|
709
|
+
const metadataInput = {
|
|
710
|
+
requestId: normalized.id,
|
|
711
|
+
entryEndpoint: normalized.entryEndpoint,
|
|
712
|
+
processMode: normalized.processMode,
|
|
713
|
+
stream: normalized.stream,
|
|
714
|
+
direction: normalized.direction,
|
|
715
|
+
providerProtocol: normalized.providerProtocol,
|
|
716
|
+
routeHint: normalized.routeHint,
|
|
717
|
+
stage: normalized.stage,
|
|
718
|
+
responsesResume: responsesResume,
|
|
719
|
+
...(serverToolRequired ? { serverToolRequired: true } : {}),
|
|
720
|
+
...(sessionIdentifiers.sessionId ? { sessionId: sessionIdentifiers.sessionId } : {}),
|
|
721
|
+
...(sessionIdentifiers.conversationId ? { conversationId: sessionIdentifiers.conversationId } : {})
|
|
722
|
+
};
|
|
723
|
+
const routing = runReqProcessStage2RouteSelect({
|
|
724
|
+
routerEngine: this.routerEngine,
|
|
725
|
+
request: workingRequest,
|
|
726
|
+
metadataInput,
|
|
727
|
+
normalizedMetadata: normalized.metadata,
|
|
728
|
+
stageRecorder
|
|
729
|
+
});
|
|
730
|
+
const stopMessageState = this.routerEngine.getStopMessageState(metadataInput);
|
|
731
|
+
if (stopMessageState && normalized.metadata && typeof normalized.metadata === 'object') {
|
|
732
|
+
normalized.metadata.stopMessageState = stopMessageState;
|
|
733
|
+
workingRequest = applyHubOperations(workingRequest, [
|
|
734
|
+
{ op: 'set_request_metadata_fields', fields: { stopMessageState } }
|
|
735
|
+
]);
|
|
736
|
+
}
|
|
737
|
+
// Emit virtual router hit log for debugging (same as inbound path).
|
|
738
|
+
try {
|
|
739
|
+
const routeName = routing.decision?.routeName;
|
|
740
|
+
const providerKey = routing.target?.providerKey;
|
|
741
|
+
const modelId = workingRequest.model;
|
|
742
|
+
const logger = (normalized.metadata && normalized.metadata.logger);
|
|
743
|
+
if (logger && typeof logger.logVirtualRouterHit === 'function' && routeName && providerKey) {
|
|
744
|
+
logger.logVirtualRouterHit(routeName, providerKey, typeof modelId === 'string' ? modelId : undefined);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
catch {
|
|
748
|
+
// ignore
|
|
749
|
+
}
|
|
750
|
+
const outboundStream = this.resolveOutboundStreamIntent(routing.target?.streaming);
|
|
751
|
+
workingRequest = this.applyOutboundStreamPreference(workingRequest, outboundStream);
|
|
752
|
+
const outboundAdapterContext = this.buildAdapterContext(normalized, routing.target);
|
|
753
|
+
if (routing.target?.compatibilityProfile) {
|
|
754
|
+
outboundAdapterContext.compatibilityProfile = routing.target.compatibilityProfile;
|
|
755
|
+
}
|
|
756
|
+
const outboundProtocol = outboundAdapterContext.providerProtocol;
|
|
757
|
+
const protocolSwitch = outboundProtocol !== normalized.providerProtocol;
|
|
758
|
+
const outboundHooks = protocolSwitch ? this.resolveProtocolHooks(outboundProtocol) : hooks;
|
|
759
|
+
if (!outboundHooks) {
|
|
760
|
+
throw new Error(`[HubPipeline] Unsupported provider protocol for hub pipeline: ${outboundProtocol}`);
|
|
761
|
+
}
|
|
762
|
+
const outboundSemanticMapper = protocolSwitch ? outboundHooks.createSemanticMapper() : hooks.createSemanticMapper();
|
|
763
|
+
const outboundFormatAdapter = protocolSwitch ? outboundHooks.createFormatAdapter() : hooks.createFormatAdapter();
|
|
764
|
+
const outboundContextMetadataKey = protocolSwitch ? outboundHooks.contextMetadataKey : hooks.contextMetadataKey;
|
|
765
|
+
const outboundContextSnapshot = undefined;
|
|
766
|
+
const outboundRecorder = this.maybeCreateStageRecorder(outboundAdapterContext, normalized.entryEndpoint, {
|
|
767
|
+
disableSnapshots: normalized.disableSnapshots === true
|
|
768
|
+
});
|
|
769
|
+
const outboundStart = Date.now();
|
|
770
|
+
let providerPayload;
|
|
771
|
+
const outboundStage1 = await runReqOutboundStage1SemanticMap({
|
|
772
|
+
request: workingRequest,
|
|
773
|
+
adapterContext: outboundAdapterContext,
|
|
774
|
+
semanticMapper: outboundSemanticMapper,
|
|
775
|
+
contextSnapshot: outboundContextSnapshot,
|
|
776
|
+
contextMetadataKey: outboundContextMetadataKey,
|
|
777
|
+
stageRecorder: outboundRecorder
|
|
778
|
+
});
|
|
779
|
+
let formattedPayload = await runReqOutboundStage2FormatBuild({
|
|
780
|
+
formatEnvelope: outboundStage1.formatEnvelope,
|
|
781
|
+
adapterContext: outboundAdapterContext,
|
|
782
|
+
formatAdapter: outboundFormatAdapter,
|
|
783
|
+
stageRecorder: outboundRecorder
|
|
784
|
+
});
|
|
785
|
+
formattedPayload = await runReqOutboundStage3Compat({
|
|
786
|
+
payload: formattedPayload,
|
|
787
|
+
adapterContext: outboundAdapterContext,
|
|
788
|
+
stageRecorder: outboundRecorder
|
|
789
|
+
});
|
|
790
|
+
// Phase 0/1: observe + enforce provider outbound policy and tool surface (same as inbound path).
|
|
791
|
+
const effectivePolicy = normalized.policyOverride ?? this.config.policy;
|
|
792
|
+
recordHubPolicyObservation({
|
|
793
|
+
policy: effectivePolicy,
|
|
794
|
+
providerProtocol: outboundProtocol,
|
|
795
|
+
payload: formattedPayload,
|
|
796
|
+
stageRecorder: outboundRecorder,
|
|
797
|
+
requestId: normalized.id
|
|
798
|
+
});
|
|
799
|
+
providerPayload = applyHubProviderOutboundPolicy({
|
|
800
|
+
policy: effectivePolicy,
|
|
801
|
+
providerProtocol: outboundProtocol,
|
|
802
|
+
payload: formattedPayload,
|
|
803
|
+
stageRecorder: outboundRecorder,
|
|
804
|
+
requestId: normalized.id
|
|
805
|
+
});
|
|
806
|
+
providerPayload = applyProviderOutboundToolSurface({
|
|
807
|
+
config: this.config.toolSurface,
|
|
808
|
+
providerProtocol: outboundProtocol,
|
|
809
|
+
payload: providerPayload,
|
|
810
|
+
stageRecorder: outboundRecorder,
|
|
811
|
+
requestId: normalized.id
|
|
812
|
+
});
|
|
813
|
+
recordHubPolicyObservation({
|
|
814
|
+
policy: effectivePolicy,
|
|
815
|
+
providerProtocol: outboundProtocol,
|
|
816
|
+
payload: providerPayload,
|
|
817
|
+
stageRecorder: outboundRecorder,
|
|
818
|
+
requestId: normalized.id
|
|
819
|
+
});
|
|
820
|
+
const outboundEnd = Date.now();
|
|
821
|
+
nodeResults.push({
|
|
822
|
+
id: 'req_outbound',
|
|
823
|
+
success: true,
|
|
824
|
+
metadata: {
|
|
825
|
+
node: 'req_outbound',
|
|
826
|
+
executionTime: outboundEnd - outboundStart,
|
|
827
|
+
startTime: outboundStart,
|
|
828
|
+
endTime: outboundEnd,
|
|
829
|
+
dataProcessed: {
|
|
830
|
+
messages: workingRequest.messages.length,
|
|
831
|
+
tools: workingRequest.tools?.length ?? 0
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
});
|
|
835
|
+
const capturedChatRequest = {
|
|
836
|
+
model: workingRequest.model,
|
|
837
|
+
messages: jsonClone(workingRequest.messages),
|
|
838
|
+
tools: workingRequest.tools ? jsonClone(workingRequest.tools) : workingRequest.tools,
|
|
839
|
+
parameters: workingRequest.parameters ? jsonClone(workingRequest.parameters) : workingRequest.parameters
|
|
840
|
+
};
|
|
841
|
+
const metadata = {
|
|
842
|
+
...normalized.metadata,
|
|
843
|
+
...(hasImageAttachment ? { hasImageAttachment: true } : {}),
|
|
844
|
+
capturedChatRequest,
|
|
845
|
+
entryEndpoint: normalized.entryEndpoint,
|
|
846
|
+
providerProtocol: outboundProtocol,
|
|
847
|
+
stream: normalized.stream,
|
|
848
|
+
processMode: normalized.processMode,
|
|
849
|
+
routeHint: normalized.routeHint,
|
|
850
|
+
target: routing.target,
|
|
851
|
+
...(typeof outboundStream === 'boolean' ? { providerStream: outboundStream } : {})
|
|
852
|
+
};
|
|
853
|
+
return {
|
|
854
|
+
requestId: normalized.id,
|
|
855
|
+
providerPayload,
|
|
856
|
+
standardizedRequest,
|
|
857
|
+
processedRequest,
|
|
858
|
+
routingDecision: routing.decision,
|
|
859
|
+
routingDiagnostics: routing.diagnostics,
|
|
860
|
+
target: routing.target,
|
|
861
|
+
metadata,
|
|
862
|
+
nodeResults
|
|
863
|
+
};
|
|
864
|
+
}
|
|
390
865
|
async execute(request) {
|
|
391
866
|
const normalized = await this.normalizeRequest(request);
|
|
867
|
+
if (normalized.direction === 'request' && normalized.hubEntryMode === 'chat_process') {
|
|
868
|
+
return await this.executeChatProcessEntryPipeline(normalized);
|
|
869
|
+
}
|
|
392
870
|
const hooks = this.resolveProtocolHooks(normalized.providerProtocol);
|
|
393
871
|
if (!hooks) {
|
|
394
872
|
throw new Error(`Unsupported provider protocol for hub pipeline: ${normalized.providerProtocol}`);
|
|
@@ -481,6 +959,10 @@ export class HubPipeline {
|
|
|
481
959
|
streamingHint,
|
|
482
960
|
toolCallIdStyle
|
|
483
961
|
};
|
|
962
|
+
const runtime = metadata.runtime;
|
|
963
|
+
if (runtime && typeof runtime === 'object' && !Array.isArray(runtime)) {
|
|
964
|
+
adapterContext.runtime = jsonClone(runtime);
|
|
965
|
+
}
|
|
484
966
|
const clientRequestId = typeof metadata.clientRequestId === 'string'
|
|
485
967
|
? metadata.clientRequestId.trim()
|
|
486
968
|
: '';
|
|
@@ -514,6 +996,12 @@ export class HubPipeline {
|
|
|
514
996
|
adapterContext.clientToolsRaw = metadata
|
|
515
997
|
.clientToolsRaw;
|
|
516
998
|
}
|
|
999
|
+
const applyPatchToolMode = typeof metadata.applyPatchToolMode === 'string'
|
|
1000
|
+
? metadata.applyPatchToolMode.trim().toLowerCase()
|
|
1001
|
+
: '';
|
|
1002
|
+
if (applyPatchToolMode === 'freeform' || applyPatchToolMode === 'schema') {
|
|
1003
|
+
adapterContext.applyPatchToolMode = applyPatchToolMode;
|
|
1004
|
+
}
|
|
517
1005
|
const sessionId = typeof metadata.sessionId === 'string'
|
|
518
1006
|
? metadata.sessionId.trim()
|
|
519
1007
|
: '';
|
|
@@ -569,7 +1057,10 @@ export class HubPipeline {
|
|
|
569
1057
|
}
|
|
570
1058
|
return adapterContext;
|
|
571
1059
|
}
|
|
572
|
-
maybeCreateStageRecorder(context, endpoint) {
|
|
1060
|
+
maybeCreateStageRecorder(context, endpoint, options) {
|
|
1061
|
+
if (options?.disableSnapshots === true) {
|
|
1062
|
+
return undefined;
|
|
1063
|
+
}
|
|
573
1064
|
if (!shouldRecordSnapshots()) {
|
|
574
1065
|
return undefined;
|
|
575
1066
|
}
|
|
@@ -596,6 +1087,27 @@ export class HubPipeline {
|
|
|
596
1087
|
const metadataRecord = {
|
|
597
1088
|
...(request.metadata ?? {})
|
|
598
1089
|
};
|
|
1090
|
+
const policyOverride = extractHubPolicyOverride(metadataRecord);
|
|
1091
|
+
if (Object.prototype.hasOwnProperty.call(metadataRecord, '__hubPolicyOverride')) {
|
|
1092
|
+
delete metadataRecord.__hubPolicyOverride;
|
|
1093
|
+
}
|
|
1094
|
+
const shadowCompare = extractHubShadowCompareConfig(metadataRecord);
|
|
1095
|
+
if (Object.prototype.hasOwnProperty.call(metadataRecord, '__hubShadowCompare')) {
|
|
1096
|
+
delete metadataRecord.__hubShadowCompare;
|
|
1097
|
+
}
|
|
1098
|
+
const disableSnapshots = metadataRecord.__disableHubSnapshots === true;
|
|
1099
|
+
if (Object.prototype.hasOwnProperty.call(metadataRecord, '__disableHubSnapshots')) {
|
|
1100
|
+
delete metadataRecord.__disableHubSnapshots;
|
|
1101
|
+
}
|
|
1102
|
+
const hubEntryRaw = typeof metadataRecord.__hubEntry === 'string'
|
|
1103
|
+
? String(metadataRecord.__hubEntry).trim().toLowerCase()
|
|
1104
|
+
: '';
|
|
1105
|
+
const hubEntryMode = hubEntryRaw === 'chat_process' || hubEntryRaw === 'chat-process' || hubEntryRaw === 'chatprocess'
|
|
1106
|
+
? 'chat_process'
|
|
1107
|
+
: undefined;
|
|
1108
|
+
if (Object.prototype.hasOwnProperty.call(metadataRecord, '__hubEntry')) {
|
|
1109
|
+
delete metadataRecord.__hubEntry;
|
|
1110
|
+
}
|
|
599
1111
|
const entryEndpoint = typeof metadataRecord.entryEndpoint === 'string'
|
|
600
1112
|
? normalizeEndpoint(metadataRecord.entryEndpoint)
|
|
601
1113
|
: endpoint;
|
|
@@ -633,11 +1145,15 @@ export class HubPipeline {
|
|
|
633
1145
|
providerProtocol,
|
|
634
1146
|
payload,
|
|
635
1147
|
metadata: normalizedMetadata,
|
|
1148
|
+
policyOverride: policyOverride ?? undefined,
|
|
1149
|
+
shadowCompare: shadowCompare ?? undefined,
|
|
1150
|
+
disableSnapshots,
|
|
636
1151
|
processMode,
|
|
637
1152
|
direction,
|
|
638
1153
|
stage,
|
|
639
1154
|
stream,
|
|
640
|
-
routeHint
|
|
1155
|
+
routeHint,
|
|
1156
|
+
...(hubEntryMode ? { hubEntryMode } : {})
|
|
641
1157
|
};
|
|
642
1158
|
}
|
|
643
1159
|
convertProcessNodeResult(id, result) {
|
|
@@ -733,25 +1249,18 @@ export class HubPipeline {
|
|
|
733
1249
|
}
|
|
734
1250
|
applyOutboundStreamPreference(request, stream) {
|
|
735
1251
|
if (!request || typeof request !== 'object') {
|
|
736
|
-
return;
|
|
1252
|
+
return request;
|
|
737
1253
|
}
|
|
738
|
-
const
|
|
739
|
-
const nextParameters = { ...parameters };
|
|
1254
|
+
const ops = [];
|
|
740
1255
|
if (typeof stream === 'boolean') {
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
else if ('stream' in nextParameters) {
|
|
744
|
-
delete nextParameters.stream;
|
|
1256
|
+
ops.push({ op: 'set_request_parameter_fields', fields: { stream } });
|
|
1257
|
+
ops.push({ op: 'set_request_metadata_fields', fields: { outboundStream: stream } });
|
|
745
1258
|
}
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
request.metadata.outboundStream = stream;
|
|
750
|
-
}
|
|
751
|
-
else if ('outboundStream' in request.metadata) {
|
|
752
|
-
delete request.metadata.outboundStream;
|
|
753
|
-
}
|
|
1259
|
+
else {
|
|
1260
|
+
ops.push({ op: 'unset_request_parameter_keys', keys: ['stream'] });
|
|
1261
|
+
ops.push({ op: 'unset_request_metadata_keys', keys: ['outboundStream'] });
|
|
754
1262
|
}
|
|
1263
|
+
return applyHubOperations(request, ops);
|
|
755
1264
|
}
|
|
756
1265
|
}
|
|
757
1266
|
function normalizeToolCallIdStyleCandidate(value) {
|