@jsonstudio/llms 0.6.3551 → 0.6.3686
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/antigravity-thought-signature-cache.js +4 -115
- package/dist/conversion/compat/actions/auto-thinking.js +3 -2
- package/dist/conversion/compat/actions/deepseek-web-response.js +15 -49
- package/dist/conversion/compat/actions/gemini-cli-request.d.ts +2 -0
- package/dist/conversion/compat/actions/gemini-cli-request.js +1 -1
- package/dist/conversion/compat/actions/glm-history-image-trim.js +3 -37
- package/dist/conversion/compat/actions/glm-image-content.js +3 -32
- package/dist/conversion/compat/actions/glm-native-compat.d.ts +6 -0
- package/dist/conversion/compat/actions/glm-native-compat.js +34 -0
- package/dist/conversion/compat/actions/glm-vision-prompt.js +3 -76
- package/dist/conversion/compat/actions/glm-web-search.js +10 -43
- package/dist/conversion/compat/actions/iflow-kimi-cli-defaults.js +4 -53
- package/dist/conversion/compat/actions/iflow-kimi-history-media-placeholder.js +5 -141
- package/dist/conversion/compat/actions/iflow-kimi-thinking-reasoning-fill.js +7 -28
- package/dist/conversion/compat/actions/iflow-native-compat.d.ts +6 -0
- package/dist/conversion/compat/actions/iflow-native-compat.js +36 -0
- package/dist/conversion/compat/actions/iflow-response-body-unwrap.js +4 -119
- package/dist/conversion/compat/actions/iflow-web-search.js +14 -55
- package/dist/conversion/hub/operation-table/semantic-mappers/archive/chat-mapper.archive.js +5 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +31 -18
- package/dist/conversion/hub/pipeline/hub-pipeline.js +163 -163
- package/dist/conversion/hub/pipeline/hub-stage-timing.d.ts +6 -0
- package/dist/conversion/hub/pipeline/hub-stage-timing.js +178 -0
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage1_format_parse/index.d.ts +4 -4
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage1_format_parse/index.js +33 -14
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.d.ts +7 -6
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +41 -23
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +44 -1
- package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.js +4 -1
- package/dist/conversion/hub/process/chat-process-continue-execution.js +5 -4
- package/dist/conversion/hub/process/chat-process-governance-orchestration.js +3 -1
- package/dist/conversion/hub/process/chat-process-media.d.ts +3 -1
- package/dist/conversion/hub/process/chat-process-media.js +92 -2
- package/dist/conversion/hub/process/chat-process-session-usage.d.ts +7 -0
- package/dist/conversion/hub/process/chat-process-session-usage.js +147 -0
- package/dist/conversion/hub/response/provider-response.js +13 -0
- package/dist/conversion/responses/responses-openai-bridge/response-payload.js +0 -12
- package/dist/conversion/responses/responses-openai-bridge/types.d.ts +1 -9
- package/dist/conversion/responses/responses-openai-bridge.js +77 -44
- package/dist/conversion/shared/reasoning-normalizer.js +42 -0
- package/dist/conversion/shared/responses-tool-utils.js +2 -3
- package/dist/native/router_hotpath_napi.node +0 -0
- package/dist/router/virtual-router/bootstrap/profile-builder.js +1 -0
- package/dist/router/virtual-router/bootstrap/provider-normalization.d.ts +1 -0
- package/dist/router/virtual-router/bootstrap/provider-normalization.js +6 -0
- package/dist/router/virtual-router/bootstrap.js +1 -6
- package/dist/router/virtual-router/engine-legacy.js +43 -0
- package/dist/router/virtual-router/engine-logging.d.ts +3 -0
- package/dist/router/virtual-router/engine-logging.js +29 -3
- package/dist/router/virtual-router/engine-selection/native-hub-pipeline-edge-stage-semantics.d.ts +2 -2
- package/dist/router/virtual-router/engine-selection/native-hub-pipeline-edge-stage-semantics.js +96 -80
- package/dist/router/virtual-router/engine-selection/native-hub-pipeline-req-process-semantics.d.ts +1 -0
- package/dist/router/virtual-router/engine.js +34 -22
- package/dist/router/virtual-router/provider-registry.js +1 -0
- package/dist/router/virtual-router/routing-instructions/state.js +35 -3
- package/dist/router/virtual-router/routing-instructions/types.d.ts +4 -0
- package/dist/router/virtual-router/types.d.ts +7 -0
- package/dist/servertool/engine.js +3 -34
- package/dist/servertool/handlers/followup-request-builder.js +0 -6
- package/dist/servertool/handlers/gemini-empty-reply-continue.js +3 -274
- package/dist/servertool/handlers/stop-message-auto/runtime-utils.d.ts +0 -3
- package/dist/servertool/handlers/stop-message-auto/runtime-utils.js +0 -29
- package/dist/servertool/handlers/stop-message-auto.js +2 -10
- package/dist/servertool/handlers/vision.js +4 -1
- package/dist/servertool/server-side-tools.js +66 -3
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Readable } from "node:stream";
|
|
2
2
|
import { isJsonObject, jsonClone } from "../types/json.js";
|
|
3
|
+
import { convertMessagesToBridgeInput } from "../../bridge-message-utils.js";
|
|
3
4
|
import { VirtualRouterEngine } from "../../../router/virtual-router/engine.js";
|
|
4
5
|
import { providerErrorCenter } from "../../../router/virtual-router/error-center.js";
|
|
5
6
|
import { providerSuccessCenter } from "../../../router/virtual-router/success-center.js";
|
|
@@ -26,12 +27,15 @@ import { runReqOutboundStage2FormatBuild } from "./stages/req_outbound/req_outbo
|
|
|
26
27
|
import { runReqOutboundStage3Compat } from "./stages/req_outbound/req_outbound_stage3_compat/index.js";
|
|
27
28
|
import { extractSessionIdentifiersFromMetadata } from "./session-identifiers.js";
|
|
28
29
|
import { computeRequestTokens } from "../../../router/virtual-router/token-estimator.js";
|
|
30
|
+
import { estimateSessionBoundTokens } from "../process/chat-process-session-usage.js";
|
|
29
31
|
import { annotatePassthroughGovernanceSkipWithNative, attachPassthroughProviderInputAuditWithNative, buildPassthroughAuditWithNative, applyOutboundStreamPreferenceWithNative, normalizeHubEndpointWithNative, extractAdapterContextMetadataFieldsWithNative, resolveApplyPatchToolModeFromToolsWithNative, resolveHubClientProtocolWithNative, resolveHubPolicyOverrideFromMetadataWithNative, resolveHubProviderProtocolWithNative, resolveOutboundStreamIntentWithNative, resolveHubShadowCompareConfigWithNative, resolveActiveProcessModeWithNative, findMappableSemanticsKeysWithNative, resolveHubSseProtocolFromMetadataWithNative, resolveStopMessageRouterMetadataWithNative, runHubPipelineOrchestrationWithNative, } from "../../../router/virtual-router/engine-selection/native-hub-pipeline-orchestration-semantics.js";
|
|
30
32
|
import { normalizeAliasMapWithNative, resolveAliasMapFromRespSemanticsWithNative, } from "../../../router/virtual-router/engine-selection/native-hub-pipeline-resp-semantics.js";
|
|
31
33
|
import { isCompactionRequest } from "../../compaction-detect.js";
|
|
32
34
|
import { applyHubProviderOutboundPolicy, recordHubPolicyObservation, setHubPolicyRuntimePolicy, } from "../policy/policy-engine.js";
|
|
33
35
|
import { applyProviderOutboundToolSurface, } from "../tool-surface/tool-surface-engine.js";
|
|
34
36
|
import { cloneRuntimeMetadata, ensureRuntimeMetadata, readRuntimeMetadata, } from "../../runtime-metadata.js";
|
|
37
|
+
import { containsImageAttachment, stripHistoricalImageAttachments, stripHistoricalVisualToolOutputs, repairIncompleteToolCalls, } from "../process/chat-process-media.js";
|
|
38
|
+
import { measureHubStage, logHubStageTiming } from "./hub-stage-timing.js";
|
|
35
39
|
function isTruthyEnv(value) {
|
|
36
40
|
const v = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
37
41
|
return v === "1" || v === "true" || v === "yes" || v === "on";
|
|
@@ -52,6 +56,109 @@ function resolveApplyPatchToolModeFromEnv() {
|
|
|
52
56
|
return "freeform";
|
|
53
57
|
return undefined;
|
|
54
58
|
}
|
|
59
|
+
function applyChatProcessEntryMediaCleanup(request) {
|
|
60
|
+
return {
|
|
61
|
+
...request,
|
|
62
|
+
messages: repairIncompleteToolCalls(stripHistoricalVisualToolOutputs(stripHistoricalImageAttachments(request.messages))),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function readResponsesResumeFromMetadata(metadata) {
|
|
66
|
+
if (!metadata || typeof metadata !== "object") {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
const resume = metadata.responsesResume;
|
|
70
|
+
return resume && isJsonObject(resume)
|
|
71
|
+
? resume
|
|
72
|
+
: undefined;
|
|
73
|
+
}
|
|
74
|
+
function readResponsesResumeFromRequestSemantics(request) {
|
|
75
|
+
try {
|
|
76
|
+
const semantics = request?.semantics;
|
|
77
|
+
const responses = semantics &&
|
|
78
|
+
typeof semantics === "object" &&
|
|
79
|
+
!Array.isArray(semantics) &&
|
|
80
|
+
semantics.responses &&
|
|
81
|
+
typeof semantics.responses === "object" &&
|
|
82
|
+
!Array.isArray(semantics.responses)
|
|
83
|
+
? semantics.responses
|
|
84
|
+
: undefined;
|
|
85
|
+
const resume = responses &&
|
|
86
|
+
responses.resume &&
|
|
87
|
+
typeof responses.resume === "object" &&
|
|
88
|
+
!Array.isArray(responses.resume)
|
|
89
|
+
? responses.resume
|
|
90
|
+
: undefined;
|
|
91
|
+
return resume;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function liftResponsesResumeIntoSemantics(request, metadata) {
|
|
98
|
+
const resumeMeta = readResponsesResumeFromMetadata(metadata);
|
|
99
|
+
if (!resumeMeta) {
|
|
100
|
+
return request;
|
|
101
|
+
}
|
|
102
|
+
const next = {
|
|
103
|
+
...request,
|
|
104
|
+
semantics: {
|
|
105
|
+
...(request.semantics ??
|
|
106
|
+
{}),
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
const semantics = next.semantics;
|
|
110
|
+
if (!semantics.responses ||
|
|
111
|
+
typeof semantics.responses !== "object" ||
|
|
112
|
+
Array.isArray(semantics.responses)) {
|
|
113
|
+
semantics.responses = {};
|
|
114
|
+
}
|
|
115
|
+
const responsesNode = semantics.responses;
|
|
116
|
+
if (responsesNode.resume === undefined) {
|
|
117
|
+
responsesNode.resume = jsonClone(resumeMeta);
|
|
118
|
+
}
|
|
119
|
+
delete metadata.responsesResume;
|
|
120
|
+
return next;
|
|
121
|
+
}
|
|
122
|
+
function syncResponsesContextFromCanonicalMessages(request) {
|
|
123
|
+
const semantics = request?.semantics;
|
|
124
|
+
const responsesNode = semantics &&
|
|
125
|
+
typeof semantics === "object" &&
|
|
126
|
+
!Array.isArray(semantics) &&
|
|
127
|
+
semantics.responses &&
|
|
128
|
+
typeof semantics.responses === "object" &&
|
|
129
|
+
!Array.isArray(semantics.responses)
|
|
130
|
+
? semantics.responses
|
|
131
|
+
: undefined;
|
|
132
|
+
const contextNode = responsesNode &&
|
|
133
|
+
responsesNode.context &&
|
|
134
|
+
typeof responsesNode.context === "object" &&
|
|
135
|
+
!Array.isArray(responsesNode.context)
|
|
136
|
+
? responsesNode.context
|
|
137
|
+
: undefined;
|
|
138
|
+
if (!contextNode) {
|
|
139
|
+
return request;
|
|
140
|
+
}
|
|
141
|
+
const bridge = convertMessagesToBridgeInput({
|
|
142
|
+
messages: request.messages ?? [],
|
|
143
|
+
tools: Array.isArray(request.tools)
|
|
144
|
+
? request.tools
|
|
145
|
+
: undefined,
|
|
146
|
+
});
|
|
147
|
+
return {
|
|
148
|
+
...request,
|
|
149
|
+
semantics: {
|
|
150
|
+
...semantics,
|
|
151
|
+
responses: {
|
|
152
|
+
...responsesNode,
|
|
153
|
+
context: {
|
|
154
|
+
...contextNode,
|
|
155
|
+
input: jsonClone(bridge.input),
|
|
156
|
+
originalSystemMessages: jsonClone(bridge.originalSystemMessages),
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
}
|
|
55
162
|
function resolveApplyPatchToolModeFromTools(toolsRaw) {
|
|
56
163
|
return resolveApplyPatchToolModeFromToolsWithNative(toolsRaw);
|
|
57
164
|
}
|
|
@@ -341,16 +448,13 @@ export class HubPipeline {
|
|
|
341
448
|
stageRecorder: inboundRecorder,
|
|
342
449
|
requestId: normalized.id,
|
|
343
450
|
});
|
|
344
|
-
const formatEnvelope = await runReqInboundStage1FormatParse({
|
|
451
|
+
const formatEnvelope = await measureHubStage(normalized.id, "req_inbound.stage1_format_parse", () => runReqInboundStage1FormatParse({
|
|
345
452
|
rawRequest,
|
|
346
453
|
adapterContext: inboundAdapterContext,
|
|
347
454
|
stageRecorder: inboundRecorder,
|
|
348
|
-
});
|
|
349
|
-
const responsesResumeFromMetadata = normalized.metadata
|
|
350
|
-
|
|
351
|
-
? normalized.metadata.responsesResume
|
|
352
|
-
: undefined;
|
|
353
|
-
const inboundStage2 = await runReqInboundStage2SemanticMap({
|
|
455
|
+
}));
|
|
456
|
+
const responsesResumeFromMetadata = readResponsesResumeFromMetadata(normalized.metadata);
|
|
457
|
+
const inboundStage2 = await measureHubStage(normalized.id, "req_inbound.stage2_semantic_map", () => runReqInboundStage2SemanticMap({
|
|
354
458
|
adapterContext: inboundAdapterContext,
|
|
355
459
|
formatEnvelope,
|
|
356
460
|
semanticMapper,
|
|
@@ -358,30 +462,24 @@ export class HubPipeline {
|
|
|
358
462
|
? { responsesResume: responsesResumeFromMetadata }
|
|
359
463
|
: {}),
|
|
360
464
|
stageRecorder: inboundRecorder,
|
|
361
|
-
});
|
|
465
|
+
}));
|
|
362
466
|
// responsesResume must not enter chat_process as metadata; it is lifted into chat.semantics in stage2.
|
|
363
467
|
if (responsesResumeFromMetadata &&
|
|
364
468
|
normalized.metadata &&
|
|
365
469
|
Object.prototype.hasOwnProperty.call(normalized.metadata, "responsesResume")) {
|
|
366
470
|
delete normalized.metadata.responsesResume;
|
|
367
471
|
}
|
|
368
|
-
const contextSnapshot = await
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
472
|
+
const contextSnapshot = await measureHubStage(normalized.id, "req_inbound.stage3_context_capture", () => {
|
|
473
|
+
if (inboundStage2.responsesContext) {
|
|
474
|
+
return inboundStage2.responsesContext;
|
|
475
|
+
}
|
|
476
|
+
return hooks.captureContext({
|
|
477
|
+
rawRequest,
|
|
478
|
+
adapterContext: inboundAdapterContext,
|
|
479
|
+
stageRecorder: inboundRecorder,
|
|
480
|
+
});
|
|
372
481
|
});
|
|
373
|
-
let standardizedRequest = inboundStage2.standardizedRequest;
|
|
374
|
-
// 全局唯一的历史图片清理:在 chat process 入口处,清理非最新 user 消息中的图片信息
|
|
375
|
-
try {
|
|
376
|
-
const { stripHistoricalImageAttachments } = await import("../process/chat-process-media.js");
|
|
377
|
-
standardizedRequest = {
|
|
378
|
-
...standardizedRequest,
|
|
379
|
-
messages: stripHistoricalImageAttachments(standardizedRequest.messages),
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
catch {
|
|
383
|
-
// best-effort: don't block on media cleanup failures
|
|
384
|
-
}
|
|
482
|
+
let standardizedRequest = applyChatProcessEntryMediaCleanup(inboundStage2.standardizedRequest);
|
|
385
483
|
try {
|
|
386
484
|
const rt = readRuntimeMetadata(normalized.metadata);
|
|
387
485
|
const mode = String(rt?.applyPatchToolMode || "")
|
|
@@ -397,9 +495,6 @@ export class HubPipeline {
|
|
|
397
495
|
const activeProcessMode = resolveActiveProcessMode(normalized.processMode, standardizedRequest.messages);
|
|
398
496
|
if (activeProcessMode !== normalized.processMode) {
|
|
399
497
|
normalized.processMode = activeProcessMode;
|
|
400
|
-
normalized.metadata = normalized.metadata || {};
|
|
401
|
-
normalized.metadata.processMode =
|
|
402
|
-
activeProcessMode;
|
|
403
498
|
}
|
|
404
499
|
const passthroughAudit = activeProcessMode === "passthrough"
|
|
405
500
|
? buildPassthroughAudit(rawRequest, normalized.providerProtocol)
|
|
@@ -442,14 +537,14 @@ export class HubPipeline {
|
|
|
442
537
|
let processedRequest;
|
|
443
538
|
if (activeProcessMode !== "passthrough") {
|
|
444
539
|
assertNoMappableSemanticsInMetadata(metaBase, "chat_process.request.entry");
|
|
445
|
-
const processResult = await runReqProcessStage1ToolGovernance({
|
|
540
|
+
const processResult = await measureHubStage(normalized.id, "req_process.stage1_tool_governance", () => runReqProcessStage1ToolGovernance({
|
|
446
541
|
request: standardizedRequest,
|
|
447
542
|
rawPayload: rawRequest,
|
|
448
543
|
metadata: metaBase,
|
|
449
544
|
entryEndpoint: normalized.entryEndpoint,
|
|
450
545
|
requestId: normalized.id,
|
|
451
546
|
stageRecorder: inboundRecorder,
|
|
452
|
-
});
|
|
547
|
+
}));
|
|
453
548
|
processedRequest = processResult.processedRequest;
|
|
454
549
|
// Surface request-side clock reservation into pipeline metadata so response conversion
|
|
455
550
|
// can commit delivery only after a successful response is produced.
|
|
@@ -482,11 +577,11 @@ export class HubPipeline {
|
|
|
482
577
|
annotatePassthroughGovernanceSkip(passthroughAudit);
|
|
483
578
|
}
|
|
484
579
|
}
|
|
485
|
-
let workingRequest = processedRequest ?? standardizedRequest;
|
|
580
|
+
let workingRequest = syncResponsesContextFromCanonicalMessages(processedRequest ?? standardizedRequest);
|
|
486
581
|
// 使用与 VirtualRouter 一致的 tiktoken 计数逻辑,对标准化请求进行一次
|
|
487
582
|
// 上下文 token 估算,供后续 usage 归一化与统计使用。
|
|
488
583
|
try {
|
|
489
|
-
const estimatedTokens = computeRequestTokens(workingRequest, "");
|
|
584
|
+
const estimatedTokens = estimateSessionBoundTokens(workingRequest, normalized.metadata) ?? computeRequestTokens(workingRequest, "");
|
|
490
585
|
if (typeof estimatedTokens === "number" &&
|
|
491
586
|
Number.isFinite(estimatedTokens) &&
|
|
492
587
|
estimatedTokens > 0) {
|
|
@@ -501,30 +596,9 @@ export class HubPipeline {
|
|
|
501
596
|
const normalizedMeta = normalized.metadata;
|
|
502
597
|
// responsesResume is a client-protocol semantic (/v1/responses tool loop) and must live in chat.semantics.
|
|
503
598
|
// Do not read it from metadata once entering chat_process.
|
|
504
|
-
const responsesResume = (
|
|
505
|
-
try {
|
|
506
|
-
const semantics = workingRequest?.semantics;
|
|
507
|
-
const node = semantics &&
|
|
508
|
-
typeof semantics === "object" &&
|
|
509
|
-
!Array.isArray(semantics)
|
|
510
|
-
? semantics.responses
|
|
511
|
-
: undefined;
|
|
512
|
-
const resume = node && typeof node === "object" && !Array.isArray(node)
|
|
513
|
-
? node.resume
|
|
514
|
-
: undefined;
|
|
515
|
-
return resume && typeof resume === "object" && !Array.isArray(resume)
|
|
516
|
-
? resume
|
|
517
|
-
: undefined;
|
|
518
|
-
}
|
|
519
|
-
catch {
|
|
520
|
-
return undefined;
|
|
521
|
-
}
|
|
522
|
-
})();
|
|
599
|
+
const responsesResume = readResponsesResumeFromRequestSemantics(workingRequest);
|
|
523
600
|
const stdMetadata = workingRequest?.metadata;
|
|
524
|
-
const hasImageAttachment =
|
|
525
|
-
stdMetadata?.hasImageAttachment === "true" ||
|
|
526
|
-
normalizedMeta?.hasImageAttachment === true ||
|
|
527
|
-
normalizedMeta?.hasImageAttachment === "true";
|
|
601
|
+
const hasImageAttachment = containsImageAttachment((workingRequest.messages ?? []));
|
|
528
602
|
const serverToolRequired = stdMetadata?.webSearchEnabled === true ||
|
|
529
603
|
stdMetadata?.serverToolRequired === true;
|
|
530
604
|
const sessionIdentifiers = extractSessionIdentifiersFromMetadata(normalized.metadata);
|
|
@@ -573,6 +647,7 @@ export class HubPipeline {
|
|
|
573
647
|
: {}),
|
|
574
648
|
...stopMessageRouterMetadata,
|
|
575
649
|
};
|
|
650
|
+
logHubStageTiming(normalized.id, "req_process.stage2_route_select", "start");
|
|
576
651
|
const routing = runReqProcessStage2RouteSelect({
|
|
577
652
|
routerEngine: this.routerEngine,
|
|
578
653
|
request: workingRequest,
|
|
@@ -580,21 +655,7 @@ export class HubPipeline {
|
|
|
580
655
|
normalizedMetadata: normalized.metadata,
|
|
581
656
|
stageRecorder: inboundRecorder,
|
|
582
657
|
});
|
|
583
|
-
|
|
584
|
-
const preCommandState = this.routerEngine.getPreCommandState(metadataInput);
|
|
585
|
-
if ((stopMessageState || preCommandState) &&
|
|
586
|
-
normalized.metadata &&
|
|
587
|
-
typeof normalized.metadata === "object") {
|
|
588
|
-
const rt = ensureRuntimeMetadata(normalized.metadata);
|
|
589
|
-
if (stopMessageState) {
|
|
590
|
-
rt.stopMessageState =
|
|
591
|
-
stopMessageState;
|
|
592
|
-
}
|
|
593
|
-
if (preCommandState) {
|
|
594
|
-
rt.preCommandState =
|
|
595
|
-
preCommandState;
|
|
596
|
-
}
|
|
597
|
-
}
|
|
658
|
+
logHubStageTiming(normalized.id, "req_process.stage2_route_select", "completed");
|
|
598
659
|
// Emit virtual router hit log for debugging (orange [virtual-router] ...)
|
|
599
660
|
try {
|
|
600
661
|
const routeName = routing.decision?.routeName;
|
|
@@ -660,23 +721,23 @@ export class HubPipeline {
|
|
|
660
721
|
const outboundContextSnapshot = protocolSwitch
|
|
661
722
|
? undefined
|
|
662
723
|
: contextSnapshot;
|
|
663
|
-
const outboundStage1 = await runReqOutboundStage1SemanticMap({
|
|
724
|
+
const outboundStage1 = await measureHubStage(normalized.id, "req_outbound.stage1_semantic_map", () => runReqOutboundStage1SemanticMap({
|
|
664
725
|
request: workingRequest,
|
|
665
726
|
adapterContext: outboundAdapterContext,
|
|
666
727
|
semanticMapper: outboundSemanticMapper,
|
|
667
728
|
contextSnapshot: outboundContextSnapshot,
|
|
668
729
|
contextMetadataKey: outboundContextMetadataKey,
|
|
669
730
|
stageRecorder: outboundRecorder,
|
|
670
|
-
});
|
|
671
|
-
let formattedPayload = await runReqOutboundStage2FormatBuild({
|
|
731
|
+
}));
|
|
732
|
+
let formattedPayload = await measureHubStage(normalized.id, "req_outbound.stage2_format_build", () => runReqOutboundStage2FormatBuild({
|
|
672
733
|
formatEnvelope: outboundStage1.formatEnvelope,
|
|
673
734
|
stageRecorder: outboundRecorder,
|
|
674
|
-
});
|
|
675
|
-
formattedPayload = await runReqOutboundStage3Compat({
|
|
735
|
+
}));
|
|
736
|
+
formattedPayload = await measureHubStage(normalized.id, "req_outbound.stage3_compat", () => runReqOutboundStage3Compat({
|
|
676
737
|
payload: formattedPayload,
|
|
677
738
|
adapterContext: outboundAdapterContext,
|
|
678
739
|
stageRecorder: outboundRecorder,
|
|
679
|
-
});
|
|
740
|
+
}));
|
|
680
741
|
if (shadowCompareBaselineMode) {
|
|
681
742
|
const baselinePolicy = {
|
|
682
743
|
...(effectivePolicy ?? {}),
|
|
@@ -771,7 +832,7 @@ export class HubPipeline {
|
|
|
771
832
|
//
|
|
772
833
|
// 注意:这里不再根据 processMode(passthrough/chat) 做分支判断——即使某些
|
|
773
834
|
// route 将 processMode 标记为 passthrough,我们仍然需要保留一次规范化后的
|
|
774
|
-
// Chat 请求快照,供 stopMessage
|
|
835
|
+
// Chat 请求快照,供 stopMessage 等被动触发型
|
|
775
836
|
// servertool 在响应阶段使用。
|
|
776
837
|
//
|
|
777
838
|
// 之前这里通过 JSON.stringify/parse 做深拷贝,但在部分 Responses/Gemini
|
|
@@ -796,7 +857,6 @@ export class HubPipeline {
|
|
|
796
857
|
};
|
|
797
858
|
const metadata = {
|
|
798
859
|
...normalized.metadata,
|
|
799
|
-
...(hasImageAttachment ? { hasImageAttachment: true } : {}),
|
|
800
860
|
capturedChatRequest,
|
|
801
861
|
entryEndpoint: normalized.entryEndpoint,
|
|
802
862
|
providerProtocol: outboundProtocol,
|
|
@@ -819,6 +879,12 @@ export class HubPipeline {
|
|
|
819
879
|
}
|
|
820
880
|
: {}),
|
|
821
881
|
};
|
|
882
|
+
if (hasImageAttachment) {
|
|
883
|
+
metadata.hasImageAttachment = true;
|
|
884
|
+
}
|
|
885
|
+
else {
|
|
886
|
+
delete metadata.hasImageAttachment;
|
|
887
|
+
}
|
|
822
888
|
return {
|
|
823
889
|
requestId: normalized.id,
|
|
824
890
|
providerPayload,
|
|
@@ -956,25 +1022,11 @@ export class HubPipeline {
|
|
|
956
1022
|
rtBase.clock = clockConfig;
|
|
957
1023
|
}
|
|
958
1024
|
normalized.metadata = metaBase;
|
|
959
|
-
const
|
|
960
|
-
|
|
961
|
-
let cleanedRequest = standardizedRequest;
|
|
962
|
-
try {
|
|
963
|
-
const { stripHistoricalImageAttachments } = await import("../process/chat-process-media.js");
|
|
964
|
-
cleanedRequest = {
|
|
965
|
-
...cleanedRequest,
|
|
966
|
-
messages: stripHistoricalImageAttachments(cleanedRequest.messages),
|
|
967
|
-
};
|
|
968
|
-
}
|
|
969
|
-
catch {
|
|
970
|
-
// best-effort: don't block on media cleanup failures
|
|
971
|
-
}
|
|
1025
|
+
const cleanedRequest = applyChatProcessEntryMediaCleanup(standardizedRequestBase);
|
|
1026
|
+
let standardizedRequest = cleanedRequest;
|
|
972
1027
|
const activeProcessMode = resolveActiveProcessMode(normalized.processMode, cleanedRequest.messages);
|
|
973
1028
|
if (activeProcessMode !== normalized.processMode) {
|
|
974
1029
|
normalized.processMode = activeProcessMode;
|
|
975
|
-
normalized.metadata = normalized.metadata || {};
|
|
976
|
-
normalized.metadata.processMode =
|
|
977
|
-
activeProcessMode;
|
|
978
1030
|
}
|
|
979
1031
|
const passthroughAudit = activeProcessMode === "passthrough"
|
|
980
1032
|
? buildPassthroughAudit(rawPayload, normalized.providerProtocol)
|
|
@@ -982,26 +1034,7 @@ export class HubPipeline {
|
|
|
982
1034
|
// Semantic Gate (chat_process entry): lift any mappable protocol semantics from metadata into request.semantics.
|
|
983
1035
|
// This is the last chance before entering chat_process; after this point we fail-fast on banned metadata keys.
|
|
984
1036
|
try {
|
|
985
|
-
|
|
986
|
-
typeof metaBase.responsesResume === "object" &&
|
|
987
|
-
metaBase.responsesResume
|
|
988
|
-
? metaBase.responsesResume
|
|
989
|
-
: undefined;
|
|
990
|
-
if (resumeMeta) {
|
|
991
|
-
standardizedRequest.semantics =
|
|
992
|
-
standardizedRequest.semantics ?? {};
|
|
993
|
-
const semantics = standardizedRequest.semantics;
|
|
994
|
-
if (!semantics.responses ||
|
|
995
|
-
typeof semantics.responses !== "object" ||
|
|
996
|
-
Array.isArray(semantics.responses)) {
|
|
997
|
-
semantics.responses = {};
|
|
998
|
-
}
|
|
999
|
-
const responsesNode = semantics.responses;
|
|
1000
|
-
if (responsesNode.resume === undefined) {
|
|
1001
|
-
responsesNode.resume = jsonClone(resumeMeta);
|
|
1002
|
-
}
|
|
1003
|
-
delete metaBase.responsesResume;
|
|
1004
|
-
}
|
|
1037
|
+
standardizedRequest = liftResponsesResumeIntoSemantics(standardizedRequest, metaBase);
|
|
1005
1038
|
}
|
|
1006
1039
|
catch {
|
|
1007
1040
|
// best-effort; validation happens below
|
|
@@ -1065,10 +1098,10 @@ export class HubPipeline {
|
|
|
1065
1098
|
annotatePassthroughGovernanceSkip(passthroughAudit);
|
|
1066
1099
|
}
|
|
1067
1100
|
}
|
|
1068
|
-
let workingRequest = processedRequest ?? standardizedRequest;
|
|
1101
|
+
let workingRequest = syncResponsesContextFromCanonicalMessages(processedRequest ?? standardizedRequest);
|
|
1069
1102
|
// Token estimate for stats/diagnostics (best-effort).
|
|
1070
1103
|
try {
|
|
1071
|
-
const estimatedTokens = computeRequestTokens(workingRequest, "");
|
|
1104
|
+
const estimatedTokens = estimateSessionBoundTokens(workingRequest, normalized.metadata) ?? computeRequestTokens(workingRequest, "");
|
|
1072
1105
|
if (typeof estimatedTokens === "number" &&
|
|
1073
1106
|
Number.isFinite(estimatedTokens) &&
|
|
1074
1107
|
estimatedTokens > 0) {
|
|
@@ -1083,30 +1116,9 @@ export class HubPipeline {
|
|
|
1083
1116
|
const normalizedMeta = normalized.metadata;
|
|
1084
1117
|
// responsesResume is a client-protocol semantic (/v1/responses tool loop) and must live in chat.semantics.
|
|
1085
1118
|
// Do not read it from metadata once entering chat_process.
|
|
1086
|
-
const responsesResume = (
|
|
1087
|
-
try {
|
|
1088
|
-
const semantics = workingRequest?.semantics;
|
|
1089
|
-
const node = semantics &&
|
|
1090
|
-
typeof semantics === "object" &&
|
|
1091
|
-
!Array.isArray(semantics)
|
|
1092
|
-
? semantics.responses
|
|
1093
|
-
: undefined;
|
|
1094
|
-
const resume = node && typeof node === "object" && !Array.isArray(node)
|
|
1095
|
-
? node.resume
|
|
1096
|
-
: undefined;
|
|
1097
|
-
return resume && typeof resume === "object" && !Array.isArray(resume)
|
|
1098
|
-
? resume
|
|
1099
|
-
: undefined;
|
|
1100
|
-
}
|
|
1101
|
-
catch {
|
|
1102
|
-
return undefined;
|
|
1103
|
-
}
|
|
1104
|
-
})();
|
|
1119
|
+
const responsesResume = readResponsesResumeFromRequestSemantics(workingRequest);
|
|
1105
1120
|
const stdMetadata = workingRequest?.metadata;
|
|
1106
|
-
const hasImageAttachment =
|
|
1107
|
-
stdMetadata?.hasImageAttachment === "true" ||
|
|
1108
|
-
normalizedMeta?.hasImageAttachment === true ||
|
|
1109
|
-
normalizedMeta?.hasImageAttachment === "true";
|
|
1121
|
+
const hasImageAttachment = containsImageAttachment((workingRequest.messages ?? []));
|
|
1110
1122
|
const serverToolRequired = stdMetadata?.webSearchEnabled === true ||
|
|
1111
1123
|
stdMetadata?.serverToolRequired === true;
|
|
1112
1124
|
const sessionIdentifiers = extractSessionIdentifiersFromMetadata(normalized.metadata);
|
|
@@ -1151,21 +1163,6 @@ export class HubPipeline {
|
|
|
1151
1163
|
normalizedMetadata: normalized.metadata,
|
|
1152
1164
|
stageRecorder,
|
|
1153
1165
|
});
|
|
1154
|
-
const stopMessageState = this.routerEngine.getStopMessageState(metadataInput);
|
|
1155
|
-
const preCommandState = this.routerEngine.getPreCommandState(metadataInput);
|
|
1156
|
-
if ((stopMessageState || preCommandState) &&
|
|
1157
|
-
normalized.metadata &&
|
|
1158
|
-
typeof normalized.metadata === "object") {
|
|
1159
|
-
const rt = ensureRuntimeMetadata(normalized.metadata);
|
|
1160
|
-
if (stopMessageState) {
|
|
1161
|
-
rt.stopMessageState =
|
|
1162
|
-
stopMessageState;
|
|
1163
|
-
}
|
|
1164
|
-
if (preCommandState) {
|
|
1165
|
-
rt.preCommandState =
|
|
1166
|
-
preCommandState;
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
1166
|
// Emit virtual router hit log for debugging (same as inbound path).
|
|
1170
1167
|
try {
|
|
1171
1168
|
const routeName = routing.decision?.routeName;
|
|
@@ -1224,23 +1221,23 @@ export class HubPipeline {
|
|
|
1224
1221
|
? outboundHooks.contextMetadataKey
|
|
1225
1222
|
: hooks.contextMetadataKey;
|
|
1226
1223
|
const outboundContextSnapshot = undefined;
|
|
1227
|
-
const outboundStage1 = await runReqOutboundStage1SemanticMap({
|
|
1224
|
+
const outboundStage1 = await measureHubStage(normalized.id, "req_outbound.stage1_semantic_map", () => runReqOutboundStage1SemanticMap({
|
|
1228
1225
|
request: workingRequest,
|
|
1229
1226
|
adapterContext: outboundAdapterContext,
|
|
1230
1227
|
semanticMapper: outboundSemanticMapper,
|
|
1231
1228
|
contextSnapshot: outboundContextSnapshot,
|
|
1232
1229
|
contextMetadataKey: outboundContextMetadataKey,
|
|
1233
1230
|
stageRecorder: outboundRecorder,
|
|
1234
|
-
});
|
|
1235
|
-
let formattedPayload = await runReqOutboundStage2FormatBuild({
|
|
1231
|
+
}));
|
|
1232
|
+
let formattedPayload = await measureHubStage(normalized.id, "req_outbound.stage2_format_build", () => runReqOutboundStage2FormatBuild({
|
|
1236
1233
|
formatEnvelope: outboundStage1.formatEnvelope,
|
|
1237
1234
|
stageRecorder: outboundRecorder,
|
|
1238
|
-
});
|
|
1239
|
-
formattedPayload = await runReqOutboundStage3Compat({
|
|
1235
|
+
}));
|
|
1236
|
+
formattedPayload = await measureHubStage(normalized.id, "req_outbound.stage3_compat", () => runReqOutboundStage3Compat({
|
|
1240
1237
|
payload: formattedPayload,
|
|
1241
1238
|
adapterContext: outboundAdapterContext,
|
|
1242
1239
|
stageRecorder: outboundRecorder,
|
|
1243
|
-
});
|
|
1240
|
+
}));
|
|
1244
1241
|
// Phase 0/1: observe + enforce provider outbound policy and tool surface (same as inbound path).
|
|
1245
1242
|
const effectivePolicy = normalized.policyOverride ?? this.config.policy;
|
|
1246
1243
|
recordHubPolicyObservation({
|
|
@@ -1312,7 +1309,6 @@ export class HubPipeline {
|
|
|
1312
1309
|
};
|
|
1313
1310
|
const metadata = {
|
|
1314
1311
|
...normalized.metadata,
|
|
1315
|
-
...(hasImageAttachment ? { hasImageAttachment: true } : {}),
|
|
1316
1312
|
capturedChatRequest,
|
|
1317
1313
|
entryEndpoint: normalized.entryEndpoint,
|
|
1318
1314
|
providerProtocol: outboundProtocol,
|
|
@@ -1325,6 +1321,12 @@ export class HubPipeline {
|
|
|
1325
1321
|
? { providerStream: outboundStream }
|
|
1326
1322
|
: {}),
|
|
1327
1323
|
};
|
|
1324
|
+
if (hasImageAttachment) {
|
|
1325
|
+
metadata.hasImageAttachment = true;
|
|
1326
|
+
}
|
|
1327
|
+
else {
|
|
1328
|
+
delete metadata.hasImageAttachment;
|
|
1329
|
+
}
|
|
1328
1330
|
return {
|
|
1329
1331
|
requestId: normalized.id,
|
|
1330
1332
|
providerPayload,
|
|
@@ -1759,11 +1761,9 @@ export class HubPipeline {
|
|
|
1759
1761
|
direction,
|
|
1760
1762
|
stage,
|
|
1761
1763
|
stream,
|
|
1764
|
+
...(routeHint ? { routeHint } : {}),
|
|
1762
1765
|
...(orchestrationResult.metadata ?? {}),
|
|
1763
1766
|
};
|
|
1764
|
-
if (routeHint) {
|
|
1765
|
-
normalizedMetadata.routeHint = routeHint;
|
|
1766
|
-
}
|
|
1767
1767
|
return {
|
|
1768
1768
|
id,
|
|
1769
1769
|
endpoint,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function logHubStageTiming(requestId: string, stage: string, phase: 'start' | 'completed' | 'error', details?: Record<string, unknown>): void;
|
|
2
|
+
export declare function measureHubStage<T>(requestId: string, stage: string, fn: () => Promise<T> | T, options?: {
|
|
3
|
+
startDetails?: Record<string, unknown>;
|
|
4
|
+
mapCompletedDetails?: (value: T) => Record<string, unknown> | undefined;
|
|
5
|
+
mapErrorDetails?: (error: unknown) => Record<string, unknown> | undefined;
|
|
6
|
+
}): Promise<T>;
|