@jsonstudio/llms 0.6.954 → 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 +489 -19
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +6 -0
- 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 -467
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.d.ts +1 -7
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +1 -903
- 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 +89 -15
- 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 +161 -7
- 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
|
@@ -4,6 +4,7 @@ import type { JsonObject } from '../types/json.js';
|
|
|
4
4
|
import type { VirtualRouterConfig, RoutingDecision, RoutingDiagnostics, TargetMetadata, VirtualRouterHealthStore, ProviderQuotaView } from '../../../router/virtual-router/types.js';
|
|
5
5
|
import { type HubProcessNodeResult } from '../process/chat-process.js';
|
|
6
6
|
import { type HubPolicyConfig } from '../policy/policy-engine.js';
|
|
7
|
+
import { type HubToolSurfaceConfig } from '../tool-surface/tool-surface-engine.js';
|
|
7
8
|
export interface HubPipelineConfig {
|
|
8
9
|
virtualRouter: VirtualRouterConfig;
|
|
9
10
|
/**
|
|
@@ -11,6 +12,12 @@ export interface HubPipelineConfig {
|
|
|
11
12
|
* Must remain config-driven and default to off.
|
|
12
13
|
*/
|
|
13
14
|
policy?: HubPolicyConfig;
|
|
15
|
+
/**
|
|
16
|
+
* Optional: tool surface rollout controls.
|
|
17
|
+
* - shadow: compute & record diffs, do not modify outbound payload
|
|
18
|
+
* - enforce: rewrite outbound payload to match canonical tool surface
|
|
19
|
+
*/
|
|
20
|
+
toolSurface?: HubToolSurfaceConfig;
|
|
14
21
|
/**
|
|
15
22
|
* 可选:供 VirtualRouterEngine 使用的健康状态持久化存储。
|
|
16
23
|
* 当提供时,VirtualRouterEngine 将在初始化时恢复上一次快照,并在 cooldown/熔断变化时调用 persistSnapshot。
|
|
@@ -79,6 +86,8 @@ export declare class HubPipeline {
|
|
|
79
86
|
dispose(): void;
|
|
80
87
|
private executeRequestStagePipeline;
|
|
81
88
|
private resolveClientProtocol;
|
|
89
|
+
private coerceStandardizedRequestFromPayload;
|
|
90
|
+
private executeChatProcessEntryPipeline;
|
|
82
91
|
execute(request: HubPipelineRequest): Promise<HubPipelineResult>;
|
|
83
92
|
private captureAnthropicAliasMap;
|
|
84
93
|
private shouldCaptureAnthropicAlias;
|
|
@@ -26,6 +26,54 @@ 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
|
+
}
|
|
29
77
|
function extractHubPolicyOverride(metadata) {
|
|
30
78
|
const raw = metadata ? metadata.__hubPolicyOverride : undefined;
|
|
31
79
|
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
@@ -42,6 +90,22 @@ function extractHubPolicyOverride(metadata) {
|
|
|
42
90
|
...(sampleRate !== undefined ? { sampleRate } : {})
|
|
43
91
|
};
|
|
44
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
|
+
}
|
|
45
109
|
export class HubPipeline {
|
|
46
110
|
routerEngine;
|
|
47
111
|
config;
|
|
@@ -120,6 +184,11 @@ export class HubPipeline {
|
|
|
120
184
|
// not the governed/augmented tools.
|
|
121
185
|
try {
|
|
122
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
|
+
}
|
|
123
192
|
if (toolsRaw && toolsRaw.length > 0) {
|
|
124
193
|
normalized.metadata = normalized.metadata || {};
|
|
125
194
|
normalized.metadata.clientToolsRaw = jsonClone(toolsRaw);
|
|
@@ -133,6 +202,7 @@ export class HubPipeline {
|
|
|
133
202
|
normalized.metadata.compactionRequest = true;
|
|
134
203
|
}
|
|
135
204
|
const effectivePolicy = normalized.policyOverride ?? this.config.policy;
|
|
205
|
+
const shadowCompareBaselineMode = normalized.shadowCompare?.baselineMode;
|
|
136
206
|
const inboundAdapterContext = this.buildAdapterContext(normalized);
|
|
137
207
|
const inboundRecorder = this.maybeCreateStageRecorder(inboundAdapterContext, normalized.entryEndpoint, {
|
|
138
208
|
disableSnapshots: normalized.disableSnapshots === true
|
|
@@ -166,6 +236,15 @@ export class HubPipeline {
|
|
|
166
236
|
stageRecorder: inboundRecorder
|
|
167
237
|
});
|
|
168
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
|
+
}
|
|
169
248
|
const inboundEnd = Date.now();
|
|
170
249
|
const nodeResults = [];
|
|
171
250
|
nodeResults.push({
|
|
@@ -195,6 +274,10 @@ export class HubPipeline {
|
|
|
195
274
|
if (execCommandGuard) {
|
|
196
275
|
metaBase.execCommandGuard = execCommandGuard;
|
|
197
276
|
}
|
|
277
|
+
const clockConfig = this.config.virtualRouter?.clock;
|
|
278
|
+
if (clockConfig) {
|
|
279
|
+
metaBase.clock = clockConfig;
|
|
280
|
+
}
|
|
198
281
|
normalized.metadata = metaBase;
|
|
199
282
|
let processedRequest;
|
|
200
283
|
if (normalized.processMode !== 'passthrough') {
|
|
@@ -207,11 +290,22 @@ export class HubPipeline {
|
|
|
207
290
|
stageRecorder: inboundRecorder
|
|
208
291
|
});
|
|
209
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
|
+
}
|
|
210
304
|
if (processResult.nodeResult) {
|
|
211
305
|
nodeResults.push(this.convertProcessNodeResult('req_process_stage1_tool_governance', processResult.nodeResult));
|
|
212
306
|
}
|
|
213
307
|
}
|
|
214
|
-
|
|
308
|
+
let workingRequest = processedRequest ?? standardizedRequest;
|
|
215
309
|
// 使用与 VirtualRouter 一致的 tiktoken 计数逻辑,对标准化请求进行一次
|
|
216
310
|
// 上下文 token 估算,供后续 usage 归一化与统计使用。
|
|
217
311
|
try {
|
|
@@ -267,6 +361,12 @@ export class HubPipeline {
|
|
|
267
361
|
const stopMessageState = this.routerEngine.getStopMessageState(metadataInput);
|
|
268
362
|
if (stopMessageState && normalized.metadata && typeof normalized.metadata === 'object') {
|
|
269
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
|
+
]);
|
|
270
370
|
}
|
|
271
371
|
// Emit virtual router hit log for debugging (orange [virtual-router] ...)
|
|
272
372
|
try {
|
|
@@ -282,7 +382,7 @@ export class HubPipeline {
|
|
|
282
382
|
// logging must not break routing
|
|
283
383
|
}
|
|
284
384
|
const outboundStream = this.resolveOutboundStreamIntent(routing.target?.streaming);
|
|
285
|
-
this.applyOutboundStreamPreference(workingRequest, outboundStream);
|
|
385
|
+
workingRequest = this.applyOutboundStreamPreference(workingRequest, outboundStream);
|
|
286
386
|
const outboundAdapterContext = this.buildAdapterContext(normalized, routing.target);
|
|
287
387
|
if (routing.target?.compatibilityProfile) {
|
|
288
388
|
outboundAdapterContext.compatibilityProfile = routing.target.compatibilityProfile;
|
|
@@ -324,6 +424,43 @@ export class HubPipeline {
|
|
|
324
424
|
adapterContext: outboundAdapterContext,
|
|
325
425
|
stageRecorder: outboundRecorder
|
|
326
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
|
+
});
|
|
327
464
|
providerPayload = applyHubProviderOutboundPolicy({
|
|
328
465
|
policy: effectivePolicy,
|
|
329
466
|
providerProtocol: outboundProtocol,
|
|
@@ -331,6 +468,13 @@ export class HubPipeline {
|
|
|
331
468
|
stageRecorder: outboundRecorder,
|
|
332
469
|
requestId: normalized.id
|
|
333
470
|
});
|
|
471
|
+
providerPayload = applyProviderOutboundToolSurface({
|
|
472
|
+
config: this.config.toolSurface,
|
|
473
|
+
providerProtocol: outboundProtocol,
|
|
474
|
+
payload: providerPayload,
|
|
475
|
+
stageRecorder: outboundRecorder,
|
|
476
|
+
requestId: normalized.id
|
|
477
|
+
});
|
|
334
478
|
recordHubPolicyObservation({
|
|
335
479
|
policy: effectivePolicy,
|
|
336
480
|
providerProtocol: outboundProtocol,
|
|
@@ -387,7 +531,17 @@ export class HubPipeline {
|
|
|
387
531
|
processMode: normalized.processMode,
|
|
388
532
|
routeHint: normalized.routeHint,
|
|
389
533
|
target: routing.target,
|
|
390
|
-
...(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
|
+
: {})
|
|
391
545
|
};
|
|
392
546
|
return {
|
|
393
547
|
requestId: normalized.id,
|
|
@@ -409,8 +563,310 @@ export class HubPipeline {
|
|
|
409
563
|
return 'anthropic-messages';
|
|
410
564
|
return 'openai-chat';
|
|
411
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
|
+
}
|
|
412
865
|
async execute(request) {
|
|
413
866
|
const normalized = await this.normalizeRequest(request);
|
|
867
|
+
if (normalized.direction === 'request' && normalized.hubEntryMode === 'chat_process') {
|
|
868
|
+
return await this.executeChatProcessEntryPipeline(normalized);
|
|
869
|
+
}
|
|
414
870
|
const hooks = this.resolveProtocolHooks(normalized.providerProtocol);
|
|
415
871
|
if (!hooks) {
|
|
416
872
|
throw new Error(`Unsupported provider protocol for hub pipeline: ${normalized.providerProtocol}`);
|
|
@@ -540,6 +996,12 @@ export class HubPipeline {
|
|
|
540
996
|
adapterContext.clientToolsRaw = metadata
|
|
541
997
|
.clientToolsRaw;
|
|
542
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
|
+
}
|
|
543
1005
|
const sessionId = typeof metadata.sessionId === 'string'
|
|
544
1006
|
? metadata.sessionId.trim()
|
|
545
1007
|
: '';
|
|
@@ -629,10 +1091,23 @@ export class HubPipeline {
|
|
|
629
1091
|
if (Object.prototype.hasOwnProperty.call(metadataRecord, '__hubPolicyOverride')) {
|
|
630
1092
|
delete metadataRecord.__hubPolicyOverride;
|
|
631
1093
|
}
|
|
1094
|
+
const shadowCompare = extractHubShadowCompareConfig(metadataRecord);
|
|
1095
|
+
if (Object.prototype.hasOwnProperty.call(metadataRecord, '__hubShadowCompare')) {
|
|
1096
|
+
delete metadataRecord.__hubShadowCompare;
|
|
1097
|
+
}
|
|
632
1098
|
const disableSnapshots = metadataRecord.__disableHubSnapshots === true;
|
|
633
1099
|
if (Object.prototype.hasOwnProperty.call(metadataRecord, '__disableHubSnapshots')) {
|
|
634
1100
|
delete metadataRecord.__disableHubSnapshots;
|
|
635
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
|
+
}
|
|
636
1111
|
const entryEndpoint = typeof metadataRecord.entryEndpoint === 'string'
|
|
637
1112
|
? normalizeEndpoint(metadataRecord.entryEndpoint)
|
|
638
1113
|
: endpoint;
|
|
@@ -671,12 +1146,14 @@ export class HubPipeline {
|
|
|
671
1146
|
payload,
|
|
672
1147
|
metadata: normalizedMetadata,
|
|
673
1148
|
policyOverride: policyOverride ?? undefined,
|
|
1149
|
+
shadowCompare: shadowCompare ?? undefined,
|
|
674
1150
|
disableSnapshots,
|
|
675
1151
|
processMode,
|
|
676
1152
|
direction,
|
|
677
1153
|
stage,
|
|
678
1154
|
stream,
|
|
679
|
-
routeHint
|
|
1155
|
+
routeHint,
|
|
1156
|
+
...(hubEntryMode ? { hubEntryMode } : {})
|
|
680
1157
|
};
|
|
681
1158
|
}
|
|
682
1159
|
convertProcessNodeResult(id, result) {
|
|
@@ -772,25 +1249,18 @@ export class HubPipeline {
|
|
|
772
1249
|
}
|
|
773
1250
|
applyOutboundStreamPreference(request, stream) {
|
|
774
1251
|
if (!request || typeof request !== 'object') {
|
|
775
|
-
return;
|
|
1252
|
+
return request;
|
|
776
1253
|
}
|
|
777
|
-
const
|
|
778
|
-
const nextParameters = { ...parameters };
|
|
1254
|
+
const ops = [];
|
|
779
1255
|
if (typeof stream === 'boolean') {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
else if ('stream' in nextParameters) {
|
|
783
|
-
delete nextParameters.stream;
|
|
1256
|
+
ops.push({ op: 'set_request_parameter_fields', fields: { stream } });
|
|
1257
|
+
ops.push({ op: 'set_request_metadata_fields', fields: { outboundStream: stream } });
|
|
784
1258
|
}
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
request.metadata.outboundStream = stream;
|
|
789
|
-
}
|
|
790
|
-
else if ('outboundStream' in request.metadata) {
|
|
791
|
-
delete request.metadata.outboundStream;
|
|
792
|
-
}
|
|
1259
|
+
else {
|
|
1260
|
+
ops.push({ op: 'unset_request_parameter_keys', keys: ['stream'] });
|
|
1261
|
+
ops.push({ op: 'unset_request_metadata_keys', keys: ['outboundStream'] });
|
|
793
1262
|
}
|
|
1263
|
+
return applyHubOperations(request, ops);
|
|
794
1264
|
}
|
|
795
1265
|
}
|
|
796
1266
|
function normalizeToolCallIdStyleCandidate(value) {
|