@jsonstudio/llms 0.6.1739 → 0.6.1890
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/compat/actions/deepseek-web-request.d.ts +3 -0
- package/dist/conversion/compat/actions/deepseek-web-request.js +350 -0
- package/dist/conversion/compat/actions/deepseek-web-response.d.ts +3 -0
- package/dist/conversion/compat/actions/deepseek-web-response.js +886 -0
- package/dist/conversion/compat/actions/gemini-cli-request.js +3 -1
- package/dist/conversion/compat/profiles/chat-deepseek-web.json +18 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +166 -2
- package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +169 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +6 -0
- package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +12 -0
- package/dist/conversion/hub/pipeline/compat/compat-profile-resolver.js +1 -0
- package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +4 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +365 -144
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +9 -0
- package/dist/conversion/hub/policy/policy-engine.d.ts +2 -0
- package/dist/conversion/hub/policy/policy-engine.js +8 -0
- package/dist/conversion/hub/process/chat-process.js +466 -16
- package/dist/conversion/hub/response/provider-response.js +0 -35
- package/dist/conversion/responses/responses-openai-bridge.d.ts +2 -0
- package/dist/conversion/responses/responses-openai-bridge.js +166 -8
- package/dist/conversion/shared/anthropic-message-utils.js +10 -1
- package/dist/conversion/shared/protocol-field-allowlists.d.ts +2 -2
- package/dist/conversion/shared/protocol-field-allowlists.js +4 -0
- package/dist/conversion/shared/tool-governor.js +102 -0
- package/dist/guidance/index.js +17 -0
- package/dist/router/virtual-router/bootstrap.js +46 -1
- package/dist/router/virtual-router/classifier.js +59 -4
- package/dist/router/virtual-router/engine/health/index.js +6 -6
- package/dist/router/virtual-router/engine/routing-state/store.js +16 -3
- package/dist/router/virtual-router/engine-logging.js +62 -24
- package/dist/router/virtual-router/engine-selection/route-utils.js +20 -20
- package/dist/router/virtual-router/engine-selection/tier-selection.js +2 -2
- package/dist/router/virtual-router/engine.d.ts +3 -1
- package/dist/router/virtual-router/engine.js +359 -39
- package/dist/router/virtual-router/features.js +2 -1
- package/dist/router/virtual-router/pre-command-file-resolver.d.ts +2 -0
- package/dist/router/virtual-router/pre-command-file-resolver.js +90 -0
- package/dist/router/virtual-router/provider-registry.js +3 -1
- package/dist/router/virtual-router/routing-instructions.d.ts +15 -1
- package/dist/router/virtual-router/routing-instructions.js +110 -151
- package/dist/router/virtual-router/routing-pre-command-actions.d.ts +3 -0
- package/dist/router/virtual-router/routing-pre-command-actions.js +26 -0
- package/dist/router/virtual-router/routing-pre-command-parser.d.ts +2 -0
- package/dist/router/virtual-router/routing-pre-command-parser.js +85 -0
- package/dist/router/virtual-router/routing-pre-command-state-codec.d.ts +3 -0
- package/dist/router/virtual-router/routing-pre-command-state-codec.js +24 -0
- package/dist/router/virtual-router/routing-stop-message-actions.d.ts +2 -0
- package/dist/router/virtual-router/routing-stop-message-actions.js +96 -0
- package/dist/router/virtual-router/routing-stop-message-parser.d.ts +3 -0
- package/dist/router/virtual-router/routing-stop-message-parser.js +142 -0
- package/dist/router/virtual-router/routing-stop-message-state-codec.d.ts +4 -0
- package/dist/router/virtual-router/routing-stop-message-state-codec.js +85 -0
- package/dist/router/virtual-router/sticky-session-store.js +206 -57
- package/dist/router/virtual-router/stop-message-stage-template-files.d.ts +12 -0
- package/dist/router/virtual-router/stop-message-stage-template-files.js +67 -0
- package/dist/router/virtual-router/stop-message-state-sync.d.ts +1 -1
- package/dist/router/virtual-router/stop-message-state-sync.js +5 -0
- package/dist/router/virtual-router/token-file-scanner.d.ts +9 -0
- package/dist/router/virtual-router/token-file-scanner.js +64 -3
- package/dist/router/virtual-router/tool-signals.d.ts +5 -0
- package/dist/router/virtual-router/tool-signals.js +42 -3
- package/dist/router/virtual-router/types.d.ts +19 -1
- package/dist/router/virtual-router/types.js +1 -0
- package/dist/servertool/clock/config.d.ts +1 -1
- package/dist/servertool/clock/config.js +27 -4
- package/dist/servertool/clock/state.js +41 -2
- package/dist/servertool/clock/task-store.d.ts +2 -2
- package/dist/servertool/clock/task-store.js +1 -1
- package/dist/servertool/clock/tasks.d.ts +3 -1
- package/dist/servertool/clock/tasks.js +209 -18
- package/dist/servertool/clock/types.d.ts +17 -0
- package/dist/servertool/continue-execution/log.d.ts +3 -0
- package/dist/servertool/continue-execution/log.js +13 -0
- package/dist/servertool/engine.js +414 -68
- package/dist/servertool/handlers/antigravity-thought-signature-bootstrap.js +6 -6
- package/dist/servertool/handlers/clock-auto.js +54 -71
- package/dist/servertool/handlers/clock.js +121 -6
- package/dist/servertool/handlers/continue-execution.d.ts +1 -0
- package/dist/servertool/handlers/continue-execution.js +91 -0
- package/dist/servertool/handlers/followup-request-builder.js +13 -0
- package/dist/servertool/handlers/gemini-empty-reply-continue.js +1 -1
- package/dist/servertool/handlers/iflow-model-error-retry.js +1 -1
- package/dist/servertool/handlers/recursive-detection-guard.js +1 -1
- package/dist/servertool/handlers/stop-message-auto.js +386 -257
- package/dist/servertool/handlers/stop-message-stage-policy.d.ts +43 -0
- package/dist/servertool/handlers/stop-message-stage-policy.js +684 -0
- package/dist/servertool/handlers/vision.js +1 -1
- package/dist/servertool/log/progress-file.d.ts +14 -0
- package/dist/servertool/log/progress-file.js +88 -0
- package/dist/servertool/pre-command-hooks.d.ts +17 -0
- package/dist/servertool/pre-command-hooks.js +491 -0
- package/dist/servertool/registry.d.ts +23 -6
- package/dist/servertool/registry.js +66 -1
- package/dist/servertool/server-side-tools.d.ts +1 -0
- package/dist/servertool/server-side-tools.js +216 -14
- package/dist/servertool/stop-gateway-context.d.ts +14 -0
- package/dist/servertool/stop-gateway-context.js +167 -0
- package/dist/servertool/stop-message-compare-context.d.ts +24 -0
- package/dist/servertool/stop-message-compare-context.js +133 -0
- package/dist/servertool/types.d.ts +12 -0
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +1 -0
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.js +36 -1
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +3 -0
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +3 -0
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.js +118 -1
- package/dist/tools/apply-patch/args-normalizer/default-actions.js +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Readable } from 'node:stream';
|
|
2
2
|
import { isJsonObject, jsonClone } from '../types/json.js';
|
|
3
3
|
import { VirtualRouterEngine } from '../../../router/virtual-router/engine.js';
|
|
4
|
+
import { parseRoutingInstructions } from '../../../router/virtual-router/routing-instructions.js';
|
|
4
5
|
import { providerErrorCenter } from '../../../router/virtual-router/error-center.js';
|
|
5
6
|
import { providerSuccessCenter } from '../../../router/virtual-router/success-center.js';
|
|
6
7
|
import { defaultSseCodecRegistry } from '../../../sse/index.js';
|
|
@@ -108,6 +109,83 @@ function extractHubShadowCompareConfig(metadata) {
|
|
|
108
109
|
}
|
|
109
110
|
return { baselineMode: modeCandidate };
|
|
110
111
|
}
|
|
112
|
+
const PASSTHROUGH_CANONICAL_CHAT_KEYS = new Set([
|
|
113
|
+
'model',
|
|
114
|
+
'messages',
|
|
115
|
+
'tools',
|
|
116
|
+
'parameters',
|
|
117
|
+
'metadata',
|
|
118
|
+
'semantics',
|
|
119
|
+
'stream'
|
|
120
|
+
]);
|
|
121
|
+
function cloneJsonRecord(payload) {
|
|
122
|
+
try {
|
|
123
|
+
return jsonClone(payload);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return { ...payload };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function collectPassthroughTodoTopLevelKeys(payload) {
|
|
130
|
+
return Object.keys(payload)
|
|
131
|
+
.filter((key) => !PASSTHROUGH_CANONICAL_CHAT_KEYS.has(key))
|
|
132
|
+
.sort();
|
|
133
|
+
}
|
|
134
|
+
function buildPassthroughAudit(rawInbound, providerProtocol) {
|
|
135
|
+
return {
|
|
136
|
+
raw: {
|
|
137
|
+
inbound: cloneJsonRecord(rawInbound)
|
|
138
|
+
},
|
|
139
|
+
todo: {
|
|
140
|
+
inbound: {
|
|
141
|
+
unmappedTopLevelKeys: collectPassthroughTodoTopLevelKeys(rawInbound),
|
|
142
|
+
providerProtocol,
|
|
143
|
+
note: 'passthrough_mode_parse_record_only'
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function annotatePassthroughGovernanceSkip(audit) {
|
|
149
|
+
const todo = audit.todo && typeof audit.todo === 'object' && !Array.isArray(audit.todo)
|
|
150
|
+
? audit.todo
|
|
151
|
+
: (audit.todo = {});
|
|
152
|
+
todo.governance = {
|
|
153
|
+
skipped: true,
|
|
154
|
+
reason: 'process_mode_passthrough'
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function attachPassthroughProviderInputAudit(audit, providerPayload, providerProtocol) {
|
|
158
|
+
const raw = audit.raw && typeof audit.raw === 'object' && !Array.isArray(audit.raw)
|
|
159
|
+
? audit.raw
|
|
160
|
+
: (audit.raw = {});
|
|
161
|
+
raw.providerInput = cloneJsonRecord(providerPayload);
|
|
162
|
+
const todo = audit.todo && typeof audit.todo === 'object' && !Array.isArray(audit.todo)
|
|
163
|
+
? audit.todo
|
|
164
|
+
: (audit.todo = {});
|
|
165
|
+
todo.outbound = {
|
|
166
|
+
unmappedTopLevelKeys: collectPassthroughTodoTopLevelKeys(providerPayload),
|
|
167
|
+
providerProtocol,
|
|
168
|
+
note: 'provider_payload_not_mapped_back_to_chat_semantics'
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function hasInstructionRequestedPassthrough(messages) {
|
|
172
|
+
if (!Array.isArray(messages) || messages.length === 0) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
const instructions = parseRoutingInstructions(messages);
|
|
177
|
+
return instructions.some((instruction) => instruction.processMode === 'passthrough');
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function resolveActiveProcessMode(baseMode, messages) {
|
|
184
|
+
if (baseMode === 'passthrough') {
|
|
185
|
+
return 'passthrough';
|
|
186
|
+
}
|
|
187
|
+
return hasInstructionRequestedPassthrough(messages) ? 'passthrough' : 'chat';
|
|
188
|
+
}
|
|
111
189
|
export class HubPipeline {
|
|
112
190
|
routerEngine;
|
|
113
191
|
config;
|
|
@@ -277,6 +355,15 @@ export class HubPipeline {
|
|
|
277
355
|
catch {
|
|
278
356
|
// best-effort: do not block request handling due to metadata propagation failures
|
|
279
357
|
}
|
|
358
|
+
const activeProcessMode = resolveActiveProcessMode(normalized.processMode, standardizedRequest.messages);
|
|
359
|
+
if (activeProcessMode !== normalized.processMode) {
|
|
360
|
+
normalized.processMode = activeProcessMode;
|
|
361
|
+
normalized.metadata = normalized.metadata || {};
|
|
362
|
+
normalized.metadata.processMode = activeProcessMode;
|
|
363
|
+
}
|
|
364
|
+
const passthroughAudit = activeProcessMode === 'passthrough'
|
|
365
|
+
? buildPassthroughAudit(rawRequest, normalized.providerProtocol)
|
|
366
|
+
: undefined;
|
|
280
367
|
const inboundEnd = Date.now();
|
|
281
368
|
const nodeResults = [];
|
|
282
369
|
nodeResults.push({
|
|
@@ -313,7 +400,7 @@ export class HubPipeline {
|
|
|
313
400
|
}
|
|
314
401
|
normalized.metadata = metaBase;
|
|
315
402
|
let processedRequest;
|
|
316
|
-
if (
|
|
403
|
+
if (activeProcessMode !== 'passthrough') {
|
|
317
404
|
assertNoMappableSemanticsInMetadata(metaBase, 'chat_process.request.entry');
|
|
318
405
|
const processResult = await runReqProcessStage1ToolGovernance({
|
|
319
406
|
request: standardizedRequest,
|
|
@@ -339,6 +426,20 @@ export class HubPipeline {
|
|
|
339
426
|
nodeResults.push(this.convertProcessNodeResult('chat_process.req.stage4.tool_governance', processResult.nodeResult));
|
|
340
427
|
}
|
|
341
428
|
}
|
|
429
|
+
else {
|
|
430
|
+
nodeResults.push({
|
|
431
|
+
id: 'chat_process.req.stage4.tool_governance',
|
|
432
|
+
success: true,
|
|
433
|
+
metadata: {
|
|
434
|
+
node: 'chat_process.req.stage4.tool_governance',
|
|
435
|
+
skipped: true,
|
|
436
|
+
reason: 'process_mode_passthrough_parse_record_only'
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
if (passthroughAudit) {
|
|
440
|
+
annotatePassthroughGovernanceSkip(passthroughAudit);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
342
443
|
let workingRequest = processedRequest ?? standardizedRequest;
|
|
343
444
|
// 使用与 VirtualRouter 一致的 tiktoken 计数逻辑,对标准化请求进行一次
|
|
344
445
|
// 上下文 token 估算,供后续 usage 归一化与统计使用。
|
|
@@ -405,9 +506,15 @@ export class HubPipeline {
|
|
|
405
506
|
stageRecorder: inboundRecorder
|
|
406
507
|
});
|
|
407
508
|
const stopMessageState = this.routerEngine.getStopMessageState(metadataInput);
|
|
408
|
-
|
|
509
|
+
const preCommandState = this.routerEngine.getPreCommandState(metadataInput);
|
|
510
|
+
if ((stopMessageState || preCommandState) && normalized.metadata && typeof normalized.metadata === 'object') {
|
|
409
511
|
const rt = ensureRuntimeMetadata(normalized.metadata);
|
|
410
|
-
|
|
512
|
+
if (stopMessageState) {
|
|
513
|
+
rt.stopMessageState = stopMessageState;
|
|
514
|
+
}
|
|
515
|
+
if (preCommandState) {
|
|
516
|
+
rt.preCommandState = preCommandState;
|
|
517
|
+
}
|
|
411
518
|
}
|
|
412
519
|
// Emit virtual router hit log for debugging (orange [virtual-router] ...)
|
|
413
520
|
try {
|
|
@@ -423,21 +530,15 @@ export class HubPipeline {
|
|
|
423
530
|
// logging must not break routing
|
|
424
531
|
}
|
|
425
532
|
const outboundStream = this.resolveOutboundStreamIntent(routing.target?.streaming);
|
|
426
|
-
workingRequest = this.applyOutboundStreamPreference(workingRequest, outboundStream);
|
|
533
|
+
workingRequest = this.applyOutboundStreamPreference(workingRequest, outboundStream, activeProcessMode);
|
|
427
534
|
const outboundAdapterContext = this.buildAdapterContext(normalized, routing.target);
|
|
428
535
|
if (routing.target?.compatibilityProfile) {
|
|
429
536
|
outboundAdapterContext.compatibilityProfile = routing.target.compatibilityProfile;
|
|
430
537
|
}
|
|
431
538
|
const outboundProtocol = outboundAdapterContext.providerProtocol;
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
throw new Error(`[HubPipeline] Unsupported provider protocol for hub pipeline: ${outboundProtocol}`);
|
|
436
|
-
}
|
|
437
|
-
const outboundSemanticMapper = protocolSwitch ? outboundHooks.createSemanticMapper() : semanticMapper;
|
|
438
|
-
const outboundFormatAdapter = protocolSwitch ? outboundHooks.createFormatAdapter() : formatAdapter;
|
|
439
|
-
const outboundContextMetadataKey = protocolSwitch ? outboundHooks.contextMetadataKey : hooks.contextMetadataKey;
|
|
440
|
-
const outboundContextSnapshot = protocolSwitch ? undefined : contextSnapshot;
|
|
539
|
+
if (activeProcessMode === 'passthrough' && outboundProtocol !== normalized.providerProtocol) {
|
|
540
|
+
throw new Error(`[HubPipeline] passthrough requires matching protocols: entry=${normalized.providerProtocol}, target=${outboundProtocol}`);
|
|
541
|
+
}
|
|
441
542
|
// Snapshots must be grouped by entry endpoint (client-facing protocol), not by provider protocol.
|
|
442
543
|
// Otherwise one request would be split across multiple folders (e.g. openai-responses + anthropic-messages),
|
|
443
544
|
// which breaks codex-samples correlation.
|
|
@@ -446,83 +547,118 @@ export class HubPipeline {
|
|
|
446
547
|
});
|
|
447
548
|
const outboundStart = Date.now();
|
|
448
549
|
let providerPayload;
|
|
449
|
-
const outboundStage1 = await runReqOutboundStage1SemanticMap({
|
|
450
|
-
request: workingRequest,
|
|
451
|
-
adapterContext: outboundAdapterContext,
|
|
452
|
-
semanticMapper: outboundSemanticMapper,
|
|
453
|
-
contextSnapshot: outboundContextSnapshot,
|
|
454
|
-
contextMetadataKey: outboundContextMetadataKey,
|
|
455
|
-
stageRecorder: outboundRecorder
|
|
456
|
-
});
|
|
457
|
-
let formattedPayload = await runReqOutboundStage2FormatBuild({
|
|
458
|
-
formatEnvelope: outboundStage1.formatEnvelope,
|
|
459
|
-
adapterContext: outboundAdapterContext,
|
|
460
|
-
formatAdapter: outboundFormatAdapter,
|
|
461
|
-
stageRecorder: outboundRecorder
|
|
462
|
-
});
|
|
463
|
-
formattedPayload = await runReqOutboundStage3Compat({
|
|
464
|
-
payload: formattedPayload,
|
|
465
|
-
adapterContext: outboundAdapterContext,
|
|
466
|
-
stageRecorder: outboundRecorder
|
|
467
|
-
});
|
|
468
550
|
let shadowBaselineProviderPayload;
|
|
469
|
-
if (
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
551
|
+
if (activeProcessMode === 'passthrough') {
|
|
552
|
+
providerPayload = cloneJsonRecord(rawRequest);
|
|
553
|
+
if (typeof outboundStream === 'boolean') {
|
|
554
|
+
providerPayload.stream = outboundStream;
|
|
555
|
+
}
|
|
556
|
+
if (passthroughAudit) {
|
|
557
|
+
attachPassthroughProviderInputAudit(passthroughAudit, providerPayload, outboundProtocol);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
const protocolSwitch = outboundProtocol !== normalized.providerProtocol;
|
|
562
|
+
const outboundHooks = protocolSwitch ? this.resolveProtocolHooks(outboundProtocol) : hooks;
|
|
563
|
+
if (!outboundHooks) {
|
|
564
|
+
throw new Error(`[HubPipeline] Unsupported provider protocol for hub pipeline: ${outboundProtocol}`);
|
|
565
|
+
}
|
|
566
|
+
const outboundSemanticMapper = protocolSwitch ? outboundHooks.createSemanticMapper() : semanticMapper;
|
|
567
|
+
const outboundFormatAdapter = protocolSwitch ? outboundHooks.createFormatAdapter() : formatAdapter;
|
|
568
|
+
const outboundContextMetadataKey = protocolSwitch ? outboundHooks.contextMetadataKey : hooks.contextMetadataKey;
|
|
569
|
+
const outboundContextSnapshot = protocolSwitch ? undefined : contextSnapshot;
|
|
570
|
+
const outboundStage1 = await runReqOutboundStage1SemanticMap({
|
|
571
|
+
request: workingRequest,
|
|
572
|
+
adapterContext: outboundAdapterContext,
|
|
573
|
+
semanticMapper: outboundSemanticMapper,
|
|
574
|
+
contextSnapshot: outboundContextSnapshot,
|
|
575
|
+
contextMetadataKey: outboundContextMetadataKey,
|
|
576
|
+
stageRecorder: outboundRecorder
|
|
577
|
+
});
|
|
578
|
+
let formattedPayload = await runReqOutboundStage2FormatBuild({
|
|
579
|
+
formatEnvelope: outboundStage1.formatEnvelope,
|
|
580
|
+
adapterContext: outboundAdapterContext,
|
|
581
|
+
formatAdapter: outboundFormatAdapter,
|
|
582
|
+
stageRecorder: outboundRecorder
|
|
583
|
+
});
|
|
584
|
+
formattedPayload = await runReqOutboundStage3Compat({
|
|
585
|
+
payload: formattedPayload,
|
|
586
|
+
adapterContext: outboundAdapterContext,
|
|
587
|
+
stageRecorder: outboundRecorder
|
|
588
|
+
});
|
|
589
|
+
if (shadowCompareBaselineMode) {
|
|
590
|
+
const baselinePolicy = {
|
|
591
|
+
...(effectivePolicy ?? {}),
|
|
592
|
+
mode: shadowCompareBaselineMode
|
|
593
|
+
};
|
|
594
|
+
// Compute a baseline provider payload in the *same execution*, without recording
|
|
595
|
+
// snapshots/diffs and without re-running the full pipeline. This avoids side effects
|
|
596
|
+
// (conversation store, followup captures, etc.) that a second execute() would trigger.
|
|
597
|
+
const baselineFormatted = typeof globalThis.structuredClone === 'function'
|
|
598
|
+
? globalThis.structuredClone(formattedPayload)
|
|
599
|
+
: jsonClone(formattedPayload);
|
|
600
|
+
let baselinePayload = applyHubProviderOutboundPolicy({
|
|
601
|
+
policy: baselinePolicy,
|
|
602
|
+
providerProtocol: outboundProtocol,
|
|
603
|
+
compatibilityProfile: typeof outboundAdapterContext.compatibilityProfile === 'string'
|
|
604
|
+
? outboundAdapterContext.compatibilityProfile
|
|
605
|
+
: undefined,
|
|
606
|
+
payload: baselineFormatted,
|
|
607
|
+
stageRecorder: undefined,
|
|
608
|
+
requestId: normalized.id
|
|
609
|
+
});
|
|
610
|
+
baselinePayload = applyProviderOutboundToolSurface({
|
|
611
|
+
config: this.config.toolSurface,
|
|
612
|
+
providerProtocol: outboundProtocol,
|
|
613
|
+
payload: baselinePayload,
|
|
614
|
+
stageRecorder: undefined,
|
|
615
|
+
requestId: normalized.id
|
|
616
|
+
});
|
|
617
|
+
shadowBaselineProviderPayload = baselinePayload;
|
|
618
|
+
}
|
|
619
|
+
// Phase 0/1: observe provider outbound payload violations before any enforcement rewrites.
|
|
620
|
+
// This provides black-box visibility into what the pipeline would have sent upstream.
|
|
621
|
+
recordHubPolicyObservation({
|
|
622
|
+
policy: effectivePolicy,
|
|
623
|
+
providerProtocol: outboundProtocol,
|
|
624
|
+
compatibilityProfile: typeof outboundAdapterContext.compatibilityProfile === 'string'
|
|
625
|
+
? outboundAdapterContext.compatibilityProfile
|
|
626
|
+
: undefined,
|
|
627
|
+
payload: formattedPayload,
|
|
628
|
+
stageRecorder: outboundRecorder,
|
|
629
|
+
requestId: normalized.id
|
|
630
|
+
});
|
|
631
|
+
providerPayload = applyHubProviderOutboundPolicy({
|
|
632
|
+
policy: effectivePolicy,
|
|
482
633
|
providerProtocol: outboundProtocol,
|
|
483
|
-
|
|
484
|
-
|
|
634
|
+
compatibilityProfile: typeof outboundAdapterContext.compatibilityProfile === 'string'
|
|
635
|
+
? outboundAdapterContext.compatibilityProfile
|
|
636
|
+
: undefined,
|
|
637
|
+
payload: formattedPayload,
|
|
638
|
+
stageRecorder: outboundRecorder,
|
|
485
639
|
requestId: normalized.id
|
|
486
640
|
});
|
|
487
|
-
|
|
641
|
+
providerPayload = applyProviderOutboundToolSurface({
|
|
488
642
|
config: this.config.toolSurface,
|
|
489
643
|
providerProtocol: outboundProtocol,
|
|
490
|
-
payload:
|
|
491
|
-
stageRecorder:
|
|
644
|
+
payload: providerPayload,
|
|
645
|
+
stageRecorder: outboundRecorder,
|
|
646
|
+
requestId: normalized.id
|
|
647
|
+
});
|
|
648
|
+
recordHubPolicyObservation({
|
|
649
|
+
policy: effectivePolicy,
|
|
650
|
+
providerProtocol: outboundProtocol,
|
|
651
|
+
compatibilityProfile: typeof outboundAdapterContext.compatibilityProfile === 'string'
|
|
652
|
+
? outboundAdapterContext.compatibilityProfile
|
|
653
|
+
: undefined,
|
|
654
|
+
payload: providerPayload,
|
|
655
|
+
stageRecorder: outboundRecorder,
|
|
492
656
|
requestId: normalized.id
|
|
493
657
|
});
|
|
494
|
-
|
|
658
|
+
if (passthroughAudit) {
|
|
659
|
+
attachPassthroughProviderInputAudit(passthroughAudit, providerPayload, outboundProtocol);
|
|
660
|
+
}
|
|
495
661
|
}
|
|
496
|
-
// Phase 0/1: observe provider outbound payload violations before any enforcement rewrites.
|
|
497
|
-
// This provides black-box visibility into what the pipeline would have sent upstream.
|
|
498
|
-
recordHubPolicyObservation({
|
|
499
|
-
policy: effectivePolicy,
|
|
500
|
-
providerProtocol: outboundProtocol,
|
|
501
|
-
payload: formattedPayload,
|
|
502
|
-
stageRecorder: outboundRecorder,
|
|
503
|
-
requestId: normalized.id
|
|
504
|
-
});
|
|
505
|
-
providerPayload = applyHubProviderOutboundPolicy({
|
|
506
|
-
policy: effectivePolicy,
|
|
507
|
-
providerProtocol: outboundProtocol,
|
|
508
|
-
payload: formattedPayload,
|
|
509
|
-
stageRecorder: outboundRecorder,
|
|
510
|
-
requestId: normalized.id
|
|
511
|
-
});
|
|
512
|
-
providerPayload = applyProviderOutboundToolSurface({
|
|
513
|
-
config: this.config.toolSurface,
|
|
514
|
-
providerProtocol: outboundProtocol,
|
|
515
|
-
payload: providerPayload,
|
|
516
|
-
stageRecorder: outboundRecorder,
|
|
517
|
-
requestId: normalized.id
|
|
518
|
-
});
|
|
519
|
-
recordHubPolicyObservation({
|
|
520
|
-
policy: effectivePolicy,
|
|
521
|
-
providerProtocol: outboundProtocol,
|
|
522
|
-
payload: providerPayload,
|
|
523
|
-
stageRecorder: outboundRecorder,
|
|
524
|
-
requestId: normalized.id
|
|
525
|
-
});
|
|
526
662
|
const outboundEnd = Date.now();
|
|
527
663
|
nodeResults.push({
|
|
528
664
|
id: 'req_outbound',
|
|
@@ -570,6 +706,7 @@ export class HubPipeline {
|
|
|
570
706
|
providerProtocol: outboundProtocol,
|
|
571
707
|
stream: normalized.stream,
|
|
572
708
|
processMode: normalized.processMode,
|
|
709
|
+
...(passthroughAudit ? { passthroughAudit } : {}),
|
|
573
710
|
routeHint: normalized.routeHint,
|
|
574
711
|
target: routing.target,
|
|
575
712
|
...(typeof outboundStream === 'boolean' ? { providerStream: outboundStream } : {}),
|
|
@@ -702,6 +839,15 @@ export class HubPipeline {
|
|
|
702
839
|
}
|
|
703
840
|
normalized.metadata = metaBase;
|
|
704
841
|
const standardizedRequest = standardizedRequestBase;
|
|
842
|
+
const activeProcessMode = resolveActiveProcessMode(normalized.processMode, standardizedRequest.messages);
|
|
843
|
+
if (activeProcessMode !== normalized.processMode) {
|
|
844
|
+
normalized.processMode = activeProcessMode;
|
|
845
|
+
normalized.metadata = normalized.metadata || {};
|
|
846
|
+
normalized.metadata.processMode = activeProcessMode;
|
|
847
|
+
}
|
|
848
|
+
const passthroughAudit = activeProcessMode === 'passthrough'
|
|
849
|
+
? buildPassthroughAudit(rawPayload, normalized.providerProtocol)
|
|
850
|
+
: undefined;
|
|
705
851
|
// Semantic Gate (chat_process entry): lift any mappable protocol semantics from metadata into request.semantics.
|
|
706
852
|
// This is the last chance before entering chat_process; after this point we fail-fast on banned metadata keys.
|
|
707
853
|
try {
|
|
@@ -739,7 +885,7 @@ export class HubPipeline {
|
|
|
739
885
|
disableSnapshots: normalized.disableSnapshots === true
|
|
740
886
|
});
|
|
741
887
|
let processedRequest;
|
|
742
|
-
if (
|
|
888
|
+
if (activeProcessMode !== 'passthrough') {
|
|
743
889
|
assertNoMappableSemanticsInMetadata(metaBase, 'chat_process.request.entry');
|
|
744
890
|
const processResult = await runReqProcessStage1ToolGovernance({
|
|
745
891
|
request: standardizedRequest,
|
|
@@ -765,6 +911,20 @@ export class HubPipeline {
|
|
|
765
911
|
nodeResults.push(this.convertProcessNodeResult('chat_process.req.stage4.tool_governance', processResult.nodeResult));
|
|
766
912
|
}
|
|
767
913
|
}
|
|
914
|
+
else {
|
|
915
|
+
nodeResults.push({
|
|
916
|
+
id: 'chat_process.req.stage4.tool_governance',
|
|
917
|
+
success: true,
|
|
918
|
+
metadata: {
|
|
919
|
+
node: 'chat_process.req.stage4.tool_governance',
|
|
920
|
+
skipped: true,
|
|
921
|
+
reason: 'process_mode_passthrough_parse_record_only'
|
|
922
|
+
}
|
|
923
|
+
});
|
|
924
|
+
if (passthroughAudit) {
|
|
925
|
+
annotatePassthroughGovernanceSkip(passthroughAudit);
|
|
926
|
+
}
|
|
927
|
+
}
|
|
768
928
|
let workingRequest = processedRequest ?? standardizedRequest;
|
|
769
929
|
// Token estimate for stats/diagnostics (best-effort).
|
|
770
930
|
try {
|
|
@@ -827,9 +987,15 @@ export class HubPipeline {
|
|
|
827
987
|
stageRecorder
|
|
828
988
|
});
|
|
829
989
|
const stopMessageState = this.routerEngine.getStopMessageState(metadataInput);
|
|
830
|
-
|
|
990
|
+
const preCommandState = this.routerEngine.getPreCommandState(metadataInput);
|
|
991
|
+
if ((stopMessageState || preCommandState) && normalized.metadata && typeof normalized.metadata === 'object') {
|
|
831
992
|
const rt = ensureRuntimeMetadata(normalized.metadata);
|
|
832
|
-
|
|
993
|
+
if (stopMessageState) {
|
|
994
|
+
rt.stopMessageState = stopMessageState;
|
|
995
|
+
}
|
|
996
|
+
if (preCommandState) {
|
|
997
|
+
rt.preCommandState = preCommandState;
|
|
998
|
+
}
|
|
833
999
|
}
|
|
834
1000
|
// Emit virtual router hit log for debugging (same as inbound path).
|
|
835
1001
|
try {
|
|
@@ -845,75 +1011,101 @@ export class HubPipeline {
|
|
|
845
1011
|
// ignore
|
|
846
1012
|
}
|
|
847
1013
|
const outboundStream = this.resolveOutboundStreamIntent(routing.target?.streaming);
|
|
848
|
-
workingRequest = this.applyOutboundStreamPreference(workingRequest, outboundStream);
|
|
1014
|
+
workingRequest = this.applyOutboundStreamPreference(workingRequest, outboundStream, activeProcessMode);
|
|
849
1015
|
const outboundAdapterContext = this.buildAdapterContext(normalized, routing.target);
|
|
850
1016
|
if (routing.target?.compatibilityProfile) {
|
|
851
1017
|
outboundAdapterContext.compatibilityProfile = routing.target.compatibilityProfile;
|
|
852
1018
|
}
|
|
853
1019
|
const outboundProtocol = outboundAdapterContext.providerProtocol;
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
throw new Error(`[HubPipeline] Unsupported provider protocol for hub pipeline: ${outboundProtocol}`);
|
|
858
|
-
}
|
|
859
|
-
const outboundSemanticMapper = protocolSwitch ? outboundHooks.createSemanticMapper() : hooks.createSemanticMapper();
|
|
860
|
-
const outboundFormatAdapter = protocolSwitch ? outboundHooks.createFormatAdapter() : hooks.createFormatAdapter();
|
|
861
|
-
const outboundContextMetadataKey = protocolSwitch ? outboundHooks.contextMetadataKey : hooks.contextMetadataKey;
|
|
862
|
-
const outboundContextSnapshot = undefined;
|
|
1020
|
+
if (activeProcessMode === 'passthrough' && outboundProtocol !== normalized.providerProtocol) {
|
|
1021
|
+
throw new Error(`[HubPipeline] passthrough requires matching protocols: entry=${normalized.providerProtocol}, target=${outboundProtocol}`);
|
|
1022
|
+
}
|
|
863
1023
|
const outboundRecorder = this.maybeCreateStageRecorder(outboundAdapterContext, normalized.entryEndpoint, {
|
|
864
1024
|
disableSnapshots: normalized.disableSnapshots === true
|
|
865
1025
|
});
|
|
866
1026
|
const outboundStart = Date.now();
|
|
867
1027
|
let providerPayload;
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
1028
|
+
if (activeProcessMode === 'passthrough') {
|
|
1029
|
+
providerPayload = cloneJsonRecord(rawPayloadInput);
|
|
1030
|
+
if (typeof outboundStream === 'boolean') {
|
|
1031
|
+
providerPayload.stream = outboundStream;
|
|
1032
|
+
}
|
|
1033
|
+
if (passthroughAudit) {
|
|
1034
|
+
attachPassthroughProviderInputAudit(passthroughAudit, providerPayload, outboundProtocol);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
else {
|
|
1038
|
+
const protocolSwitch = outboundProtocol !== normalized.providerProtocol;
|
|
1039
|
+
const outboundHooks = protocolSwitch ? this.resolveProtocolHooks(outboundProtocol) : hooks;
|
|
1040
|
+
if (!outboundHooks) {
|
|
1041
|
+
throw new Error(`[HubPipeline] Unsupported provider protocol for hub pipeline: ${outboundProtocol}`);
|
|
1042
|
+
}
|
|
1043
|
+
const outboundSemanticMapper = protocolSwitch ? outboundHooks.createSemanticMapper() : hooks.createSemanticMapper();
|
|
1044
|
+
const outboundFormatAdapter = protocolSwitch ? outboundHooks.createFormatAdapter() : hooks.createFormatAdapter();
|
|
1045
|
+
const outboundContextMetadataKey = protocolSwitch ? outboundHooks.contextMetadataKey : hooks.contextMetadataKey;
|
|
1046
|
+
const outboundContextSnapshot = undefined;
|
|
1047
|
+
const outboundStage1 = await runReqOutboundStage1SemanticMap({
|
|
1048
|
+
request: workingRequest,
|
|
1049
|
+
adapterContext: outboundAdapterContext,
|
|
1050
|
+
semanticMapper: outboundSemanticMapper,
|
|
1051
|
+
contextSnapshot: outboundContextSnapshot,
|
|
1052
|
+
contextMetadataKey: outboundContextMetadataKey,
|
|
1053
|
+
stageRecorder: outboundRecorder
|
|
1054
|
+
});
|
|
1055
|
+
let formattedPayload = await runReqOutboundStage2FormatBuild({
|
|
1056
|
+
formatEnvelope: outboundStage1.formatEnvelope,
|
|
1057
|
+
adapterContext: outboundAdapterContext,
|
|
1058
|
+
formatAdapter: outboundFormatAdapter,
|
|
1059
|
+
stageRecorder: outboundRecorder
|
|
1060
|
+
});
|
|
1061
|
+
formattedPayload = await runReqOutboundStage3Compat({
|
|
1062
|
+
payload: formattedPayload,
|
|
1063
|
+
adapterContext: outboundAdapterContext,
|
|
1064
|
+
stageRecorder: outboundRecorder
|
|
1065
|
+
});
|
|
1066
|
+
// Phase 0/1: observe + enforce provider outbound policy and tool surface (same as inbound path).
|
|
1067
|
+
const effectivePolicy = normalized.policyOverride ?? this.config.policy;
|
|
1068
|
+
recordHubPolicyObservation({
|
|
1069
|
+
policy: effectivePolicy,
|
|
1070
|
+
providerProtocol: outboundProtocol,
|
|
1071
|
+
compatibilityProfile: typeof outboundAdapterContext.compatibilityProfile === 'string'
|
|
1072
|
+
? outboundAdapterContext.compatibilityProfile
|
|
1073
|
+
: undefined,
|
|
1074
|
+
payload: formattedPayload,
|
|
1075
|
+
stageRecorder: outboundRecorder,
|
|
1076
|
+
requestId: normalized.id
|
|
1077
|
+
});
|
|
1078
|
+
providerPayload = applyHubProviderOutboundPolicy({
|
|
1079
|
+
policy: effectivePolicy,
|
|
1080
|
+
providerProtocol: outboundProtocol,
|
|
1081
|
+
compatibilityProfile: typeof outboundAdapterContext.compatibilityProfile === 'string'
|
|
1082
|
+
? outboundAdapterContext.compatibilityProfile
|
|
1083
|
+
: undefined,
|
|
1084
|
+
payload: formattedPayload,
|
|
1085
|
+
stageRecorder: outboundRecorder,
|
|
1086
|
+
requestId: normalized.id
|
|
1087
|
+
});
|
|
1088
|
+
providerPayload = applyProviderOutboundToolSurface({
|
|
1089
|
+
config: this.config.toolSurface,
|
|
1090
|
+
providerProtocol: outboundProtocol,
|
|
1091
|
+
payload: providerPayload,
|
|
1092
|
+
stageRecorder: outboundRecorder,
|
|
1093
|
+
requestId: normalized.id
|
|
1094
|
+
});
|
|
1095
|
+
recordHubPolicyObservation({
|
|
1096
|
+
policy: effectivePolicy,
|
|
1097
|
+
providerProtocol: outboundProtocol,
|
|
1098
|
+
compatibilityProfile: typeof outboundAdapterContext.compatibilityProfile === 'string'
|
|
1099
|
+
? outboundAdapterContext.compatibilityProfile
|
|
1100
|
+
: undefined,
|
|
1101
|
+
payload: providerPayload,
|
|
1102
|
+
stageRecorder: outboundRecorder,
|
|
1103
|
+
requestId: normalized.id
|
|
1104
|
+
});
|
|
1105
|
+
if (passthroughAudit) {
|
|
1106
|
+
attachPassthroughProviderInputAudit(passthroughAudit, providerPayload, outboundProtocol);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
917
1109
|
const outboundEnd = Date.now();
|
|
918
1110
|
nodeResults.push({
|
|
919
1111
|
id: 'req_outbound',
|
|
@@ -943,6 +1135,7 @@ export class HubPipeline {
|
|
|
943
1135
|
providerProtocol: outboundProtocol,
|
|
944
1136
|
stream: normalized.stream,
|
|
945
1137
|
processMode: normalized.processMode,
|
|
1138
|
+
...(passthroughAudit ? { passthroughAudit } : {}),
|
|
946
1139
|
routeHint: normalized.routeHint,
|
|
947
1140
|
target: routing.target,
|
|
948
1141
|
...(typeof outboundStream === 'boolean' ? { providerStream: outboundStream } : {})
|
|
@@ -1075,6 +1268,17 @@ export class HubPipeline {
|
|
|
1075
1268
|
toolCallIdStyle,
|
|
1076
1269
|
...(compatibilityProfile ? { compatibilityProfile } : {})
|
|
1077
1270
|
};
|
|
1271
|
+
const targetDeepseek = isJsonObject(target?.deepseek)
|
|
1272
|
+
? jsonClone(target.deepseek)
|
|
1273
|
+
: undefined;
|
|
1274
|
+
if (targetDeepseek) {
|
|
1275
|
+
adapterContext.deepseek = targetDeepseek;
|
|
1276
|
+
const rtCarrier = isJsonObject(adapterContext.__rt)
|
|
1277
|
+
? { ...adapterContext.__rt }
|
|
1278
|
+
: {};
|
|
1279
|
+
rtCarrier.deepseek = targetDeepseek;
|
|
1280
|
+
adapterContext.__rt = rtCarrier;
|
|
1281
|
+
}
|
|
1078
1282
|
const runtime = metadata.runtime;
|
|
1079
1283
|
if (runtime && typeof runtime === 'object' && !Array.isArray(runtime)) {
|
|
1080
1284
|
adapterContext.runtime = jsonClone(runtime);
|
|
@@ -1100,10 +1304,24 @@ export class HubPipeline {
|
|
|
1100
1304
|
if (typeof metadata.assignedModelId === 'string') {
|
|
1101
1305
|
adapterContext.modelId = metadata.assignedModelId;
|
|
1102
1306
|
}
|
|
1307
|
+
const estimatedInputTokens = Number(metadata.estimatedInputTokens ??
|
|
1308
|
+
metadata.estimated_tokens ??
|
|
1309
|
+
metadata.estimatedTokens);
|
|
1310
|
+
if (Number.isFinite(estimatedInputTokens) && estimatedInputTokens > 0) {
|
|
1311
|
+
adapterContext.estimatedInputTokens = Math.max(1, Math.round(estimatedInputTokens));
|
|
1312
|
+
}
|
|
1103
1313
|
const rt = cloneRuntimeMetadata(metadata);
|
|
1104
1314
|
if (rt) {
|
|
1105
1315
|
adapterContext.__rt = rt;
|
|
1106
1316
|
}
|
|
1317
|
+
const capturedChatRequest = metadata.capturedChatRequest &&
|
|
1318
|
+
typeof metadata.capturedChatRequest === 'object' &&
|
|
1319
|
+
!Array.isArray(metadata.capturedChatRequest)
|
|
1320
|
+
? jsonClone(metadata.capturedChatRequest)
|
|
1321
|
+
: undefined;
|
|
1322
|
+
if (capturedChatRequest) {
|
|
1323
|
+
adapterContext.capturedChatRequest = capturedChatRequest;
|
|
1324
|
+
}
|
|
1107
1325
|
const sessionId = typeof metadata.sessionId === 'string'
|
|
1108
1326
|
? metadata.sessionId.trim()
|
|
1109
1327
|
: '';
|
|
@@ -1324,10 +1542,13 @@ export class HubPipeline {
|
|
|
1324
1542
|
}
|
|
1325
1543
|
return undefined;
|
|
1326
1544
|
}
|
|
1327
|
-
applyOutboundStreamPreference(request, stream) {
|
|
1545
|
+
applyOutboundStreamPreference(request, stream, processMode) {
|
|
1328
1546
|
if (!request || typeof request !== 'object') {
|
|
1329
1547
|
return request;
|
|
1330
1548
|
}
|
|
1549
|
+
if (processMode === 'passthrough' && stream === undefined) {
|
|
1550
|
+
return request;
|
|
1551
|
+
}
|
|
1331
1552
|
const ops = [];
|
|
1332
1553
|
if (typeof stream === 'boolean') {
|
|
1333
1554
|
ops.push({ op: 'set_request_parameter_fields', fields: { stream } });
|