@jsonstudio/llms 0.6.1892 → 0.6.2172
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.js +16 -2
- package/dist/conversion/compat/actions/deepseek-web-response.d.ts +7 -1
- package/dist/conversion/compat/actions/deepseek-web-response.js +302 -40
- package/dist/conversion/compat/actions/harvest-tool-calls-from-text.d.ts +5 -0
- package/dist/conversion/compat/actions/harvest-tool-calls-from-text.js +7 -4
- package/dist/conversion/compat/actions/iflow-tool-text-fallback.d.ts +1 -0
- package/dist/conversion/compat/actions/iflow-tool-text-fallback.js +12 -0
- package/dist/conversion/compat/actions/strip-orphan-function-calls-tag.js +1 -1
- package/dist/conversion/compat/actions/tool-text-request-guidance.d.ts +9 -0
- package/dist/conversion/compat/actions/tool-text-request-guidance.js +177 -0
- package/dist/conversion/compat/antigravity-session-signature.d.ts +6 -0
- package/dist/conversion/compat/antigravity-session-signature.js +15 -0
- package/dist/conversion/compat/profiles/chat-deepseek-web.json +52 -1
- package/dist/conversion/compat/profiles/chat-glm.json +22 -0
- package/dist/conversion/compat/profiles/chat-iflow.json +4 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +13 -27
- package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +10 -1
- package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +13 -4
- package/dist/conversion/hub/pipeline/compat/compat-profile-resolver.js +1 -53
- package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +8 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +8 -4
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +191 -9
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js +118 -15
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js +65 -2
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage3_servertool_orchestration/index.d.ts +34 -0
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage3_servertool_orchestration/index.js +75 -0
- package/dist/conversion/hub/process/chat-process.js +85 -18
- package/dist/conversion/hub/response/provider-response.js +21 -50
- package/dist/conversion/hub/response/response-runtime.js +71 -10
- package/dist/conversion/responses/responses-openai-bridge/response-payload.d.ts +3 -0
- package/dist/conversion/responses/responses-openai-bridge/response-payload.js +576 -0
- package/dist/conversion/responses/responses-openai-bridge/types.d.ts +42 -0
- package/dist/conversion/responses/responses-openai-bridge/types.js +1 -0
- package/dist/conversion/responses/responses-openai-bridge.d.ts +3 -44
- package/dist/conversion/responses/responses-openai-bridge.js +193 -504
- package/dist/conversion/shared/anthropic-message-utils.js +82 -2
- package/dist/conversion/shared/bridge-message-utils.js +92 -39
- package/dist/conversion/shared/snapshot-hooks.js +8 -13
- package/dist/conversion/shared/text-markup-normalizer/extractors-apply-patch.d.ts +2 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-apply-patch.js +129 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-json.d.ts +4 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-json.js +637 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-shared.d.ts +21 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-shared.js +177 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-transcript.d.ts +5 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-transcript.js +385 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-xml.d.ts +10 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-xml.js +602 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors.d.ts +5 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors.js +4 -0
- package/dist/conversion/shared/text-markup-normalizer/normalize.d.ts +2 -0
- package/dist/conversion/shared/text-markup-normalizer/normalize.js +76 -0
- package/dist/conversion/shared/text-markup-normalizer.d.ts +3 -25
- package/dist/conversion/shared/text-markup-normalizer.js +2 -1386
- package/dist/conversion/shared/tool-governor.js +136 -10
- package/dist/filters/utils/snapshot-writer.js +3 -3
- package/dist/router/virtual-router/bootstrap/auth-utils.d.ts +6 -0
- package/dist/router/virtual-router/bootstrap/auth-utils.js +288 -0
- package/dist/router/virtual-router/bootstrap/claude-code-helpers.d.ts +11 -0
- package/dist/router/virtual-router/bootstrap/claude-code-helpers.js +18 -0
- package/dist/router/virtual-router/bootstrap/config-defaults.d.ts +5 -0
- package/dist/router/virtual-router/bootstrap/config-defaults.js +13 -0
- package/dist/router/virtual-router/bootstrap/config-normalizers.d.ts +4 -0
- package/dist/router/virtual-router/bootstrap/config-normalizers.js +106 -0
- package/dist/router/virtual-router/bootstrap/profile-builder.d.ts +7 -0
- package/dist/router/virtual-router/bootstrap/profile-builder.js +68 -0
- package/dist/router/virtual-router/bootstrap/provider-normalization.d.ts +40 -0
- package/dist/router/virtual-router/bootstrap/provider-normalization.js +212 -0
- package/dist/router/virtual-router/bootstrap/responses-helpers.d.ts +15 -0
- package/dist/router/virtual-router/bootstrap/responses-helpers.js +65 -0
- package/dist/router/virtual-router/bootstrap/routing-config.d.ts +23 -0
- package/dist/router/virtual-router/bootstrap/routing-config.js +293 -0
- package/dist/router/virtual-router/bootstrap/streaming-helpers.d.ts +12 -0
- package/dist/router/virtual-router/bootstrap/streaming-helpers.js +128 -0
- package/dist/router/virtual-router/bootstrap/utils.d.ts +5 -0
- package/dist/router/virtual-router/bootstrap/utils.js +41 -0
- package/dist/router/virtual-router/bootstrap/web-search-config.d.ts +4 -0
- package/dist/router/virtual-router/bootstrap/web-search-config.js +131 -0
- package/dist/router/virtual-router/bootstrap.d.ts +0 -4
- package/dist/router/virtual-router/bootstrap.js +31 -1275
- package/dist/router/virtual-router/classifier.js +32 -14
- package/dist/router/virtual-router/engine/antigravity/alias-lease.js +2 -2
- package/dist/router/virtual-router/engine/cooldown-manager.d.ts +34 -0
- package/dist/router/virtual-router/engine/cooldown-manager.js +118 -0
- package/dist/router/virtual-router/engine/route-analytics.d.ts +28 -0
- package/dist/router/virtual-router/engine/route-analytics.js +44 -0
- package/dist/router/virtual-router/engine/routing-pools/index.js +165 -4
- package/dist/router/virtual-router/engine/sticky-session-manager.d.ts +29 -0
- package/dist/router/virtual-router/engine/sticky-session-manager.js +55 -0
- package/dist/router/virtual-router/engine-logging.d.ts +42 -1
- package/dist/router/virtual-router/engine-logging.js +82 -15
- package/dist/router/virtual-router/engine-selection/multimodal-capability.d.ts +3 -0
- package/dist/router/virtual-router/engine-selection/multimodal-capability.js +26 -0
- package/dist/router/virtual-router/engine-selection/route-utils.js +6 -2
- package/dist/router/virtual-router/engine-selection/selection-deps.d.ts +1 -0
- package/dist/router/virtual-router/engine-selection/tier-selection.js +31 -1
- package/dist/router/virtual-router/engine.d.ts +21 -7
- package/dist/router/virtual-router/engine.js +198 -194
- package/dist/router/virtual-router/features.js +12 -4
- package/dist/router/virtual-router/message-utils.d.ts +8 -0
- package/dist/router/virtual-router/message-utils.js +170 -45
- package/dist/router/virtual-router/pre-command-file-resolver.js +40 -2
- package/dist/router/virtual-router/routing-instructions.d.ts +8 -0
- package/dist/router/virtual-router/routing-instructions.js +18 -2
- package/dist/router/virtual-router/routing-stop-message-actions.js +34 -10
- package/dist/router/virtual-router/routing-stop-message-state-codec.d.ts +2 -0
- package/dist/router/virtual-router/routing-stop-message-state-codec.js +50 -1
- package/dist/router/virtual-router/stop-message-state-sync.d.ts +1 -1
- package/dist/router/virtual-router/stop-message-state-sync.js +3 -0
- package/dist/router/virtual-router/token-counter.js +51 -10
- package/dist/router/virtual-router/tool-signals.js +4 -0
- package/dist/router/virtual-router/types.d.ts +15 -0
- package/dist/servertool/clock/session-scope.d.ts +3 -0
- package/dist/servertool/clock/session-scope.js +52 -0
- package/dist/servertool/clock/state.js +9 -0
- package/dist/servertool/clock/tasks.js +12 -1
- package/dist/servertool/clock/types.d.ts +3 -0
- package/dist/servertool/engine.js +177 -31
- package/dist/servertool/handlers/clock-auto.js +2 -8
- package/dist/servertool/handlers/clock.js +6 -9
- package/dist/servertool/handlers/recursive-detection-guard.js +53 -14
- package/dist/servertool/handlers/stop-message-auto/blocked-report.d.ts +16 -0
- package/dist/servertool/handlers/stop-message-auto/blocked-report.js +349 -0
- package/dist/servertool/handlers/stop-message-auto/iflow-followup.d.ts +23 -0
- package/dist/servertool/handlers/stop-message-auto/iflow-followup.js +503 -0
- package/dist/servertool/handlers/stop-message-auto/routing-state.d.ts +38 -0
- package/dist/servertool/handlers/stop-message-auto/routing-state.js +149 -0
- package/dist/servertool/handlers/stop-message-auto/runtime-utils.d.ts +67 -0
- package/dist/servertool/handlers/stop-message-auto/runtime-utils.js +387 -0
- package/dist/servertool/handlers/stop-message-auto.d.ts +1 -1
- package/dist/servertool/handlers/stop-message-auto.js +80 -556
- package/dist/servertool/handlers/stop-message-stage-policy/bd-runtime.d.ts +18 -0
- package/dist/servertool/handlers/stop-message-stage-policy/bd-runtime.js +398 -0
- package/dist/servertool/handlers/stop-message-stage-policy/decision.d.ts +9 -0
- package/dist/servertool/handlers/stop-message-stage-policy/decision.js +127 -0
- package/dist/servertool/handlers/stop-message-stage-policy/observation.d.ts +2 -0
- package/dist/servertool/handlers/stop-message-stage-policy/observation.js +179 -0
- package/dist/servertool/handlers/stop-message-stage-policy/templates.d.ts +4 -0
- package/dist/servertool/handlers/stop-message-stage-policy/templates.js +96 -0
- package/dist/servertool/handlers/stop-message-stage-policy/text-utils.d.ts +9 -0
- package/dist/servertool/handlers/stop-message-stage-policy/text-utils.js +89 -0
- package/dist/servertool/handlers/stop-message-stage-policy/types.d.ts +59 -0
- package/dist/servertool/handlers/stop-message-stage-policy/types.js +1 -0
- package/dist/servertool/handlers/stop-message-stage-policy.d.ts +3 -43
- package/dist/servertool/handlers/stop-message-stage-policy.js +2 -684
- package/dist/servertool/handlers/web-search.js +117 -0
- package/dist/servertool/server-side-tools.d.ts +0 -1
- package/dist/servertool/server-side-tools.js +4 -3
- package/dist/sse/sse-to-json/builders/response-builder.js +16 -0
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +1 -0
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.js +110 -37
- package/dist/telemetry/stats-center.d.ts +9 -0
- package/dist/telemetry/stats-center.js +29 -1
- package/dist/tools/apply-patch/structured/coercion.js +3 -11
- package/dist/tools/exec-command/validator.d.ts +1 -0
- package/dist/tools/exec-command/validator.js +132 -0
- package/dist/tools/tool-registry.d.ts +1 -0
- package/dist/tools/tool-registry.js +1 -1
- package/package.json +1 -1
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
import { registerServerToolHandler } from '../registry.js';
|
|
2
|
-
import { loadRoutingInstructionStateSync, saveRoutingInstructionStateAsync, saveRoutingInstructionStateSync } from '../../router/virtual-router/sticky-session-store.js';
|
|
3
2
|
import { isCompactionRequest } from './compaction-detect.js';
|
|
4
3
|
import { extractCapturedChatSeed } from './followup-request-builder.js';
|
|
5
|
-
import { resolveStopMessageStageDecision } from './stop-message-stage-policy.js';
|
|
6
4
|
import { readRuntimeMetadata } from '../../conversion/shared/runtime-metadata.js';
|
|
7
5
|
import { isStopEligibleForServerTool } from '../stop-gateway-context.js';
|
|
8
6
|
import { attachStopMessageCompareContext } from '../stop-message-compare-context.js';
|
|
7
|
+
import { extractStopMessageAutoResponseSnapshot, renderStopMessageAutoFollowupViaIflow } from './stop-message-auto/iflow-followup.js';
|
|
8
|
+
import { getCapturedRequest, hasCompactionFlag, readServerToolFollowupFlowId, resolveClientConnectionState, resolveEntryEndpoint, resolveImplicitGeminiStopMessageSnapshot, resolveStopMessageFollowupProviderKey, resolveStopMessageFollowupToolContentMaxChars } from './stop-message-auto/runtime-utils.js';
|
|
9
|
+
export { extractBlockedReportFromMessagesForTests } from './stop-message-auto/blocked-report.js';
|
|
9
10
|
const STOPMESSAGE_DEBUG = (process.env.ROUTECODEX_STOPMESSAGE_DEBUG || '').trim() === '1';
|
|
10
11
|
const STOPMESSAGE_IMPLICIT_GEMINI = (process.env.ROUTECODEX_STOPMESSAGE_IMPLICIT_GEMINI || '').trim() === '1';
|
|
12
|
+
const STOPMESSAGE_DEFAULT_ENABLED = (() => {
|
|
13
|
+
const raw = process.env.ROUTECODEX_STOPMESSAGE_DEFAULT_ENABLED;
|
|
14
|
+
if (typeof raw === 'string' && raw.trim().length > 0) {
|
|
15
|
+
return raw.trim() !== '0';
|
|
16
|
+
}
|
|
17
|
+
return process.env.NODE_ENV !== 'test';
|
|
18
|
+
})();
|
|
19
|
+
const STOPMESSAGE_DEFAULT_TEXT = (() => {
|
|
20
|
+
const raw = process.env.ROUTECODEX_STOPMESSAGE_DEFAULT_TEXT;
|
|
21
|
+
return typeof raw === 'string' && raw.trim().length > 0 ? raw.trim() : '继续执行';
|
|
22
|
+
})();
|
|
23
|
+
const STOPMESSAGE_DEFAULT_MAX_REPEATS = (() => {
|
|
24
|
+
const raw = process.env.ROUTECODEX_STOPMESSAGE_DEFAULT_MAX_REPEATS;
|
|
25
|
+
const parsed = typeof raw === 'string' ? Number(raw.trim()) : Number.NaN;
|
|
26
|
+
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 1;
|
|
27
|
+
})();
|
|
11
28
|
function debugLog(message, extra) {
|
|
12
29
|
if (!STOPMESSAGE_DEBUG) {
|
|
13
30
|
return;
|
|
@@ -26,7 +43,9 @@ const FLOW_ID = 'stop_message_flow';
|
|
|
26
43
|
const STOP_MESSAGE_EXECUTION_DIRECTIVE = [
|
|
27
44
|
'执行策略(必须遵守):',
|
|
28
45
|
'- 禁止输出口播式状态汇报(如“继续执行中”“我会继续”)。',
|
|
29
|
-
'-
|
|
46
|
+
'- 以当前上下文目标为准,优先推进可验证的工程动作,不要停留在任务管理操作。',
|
|
47
|
+
'- 优先完成真实动作(改代码/补测试/运行验证),再做简短汇报。',
|
|
48
|
+
'- 若遇阻塞,请按 JSON 输出结构化阻塞信息(type=blocked, summary, blocker, impact, next_action, evidence)。',
|
|
30
49
|
'- 如果即将返回 finish_reason=stop 或协议等价 end_turn,先调用 continue_execution 工具,不要直接结束会话。'
|
|
31
50
|
].join('\n');
|
|
32
51
|
const handler = async (ctx) => {
|
|
@@ -78,176 +97,101 @@ const handler = async (ctx) => {
|
|
|
78
97
|
});
|
|
79
98
|
try {
|
|
80
99
|
const followupFlagRaw = rt?.serverToolFollowup;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
100
|
+
const followupFlowId = readServerToolFollowupFlowId(rt);
|
|
101
|
+
const isFollowupRequest = followupFlagRaw === true ||
|
|
102
|
+
(typeof followupFlagRaw === 'string' && followupFlagRaw.trim().toLowerCase() === 'true');
|
|
103
|
+
// stop_message_flow followup is single-hop by design:
|
|
104
|
+
// once one internal followup is sent, the next stop handling must wait for a new client request.
|
|
105
|
+
if (isFollowupRequest) {
|
|
106
|
+
debugLog('skip_followup_request', { followupFlowId });
|
|
107
|
+
return markSkip('skip_followup_request');
|
|
85
108
|
}
|
|
86
109
|
if (hasCompactionFlag(rt)) {
|
|
87
110
|
debugLog('skip_compaction_flag');
|
|
88
111
|
return markSkip('skip_compaction_flag');
|
|
89
112
|
}
|
|
90
113
|
const connectionState = resolveClientConnectionState(record.clientConnectionState);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
typeof state.stopMessageSource === 'string' &&
|
|
105
|
-
state.stopMessageSource.trim().toLowerCase() === 'auto' &&
|
|
106
|
-
!STOPMESSAGE_IMPLICIT_GEMINI) {
|
|
107
|
-
clearStopMessageState(state, Date.now());
|
|
108
|
-
if (stickyKey) {
|
|
109
|
-
persistStopMessageState(stickyKey, state);
|
|
110
|
-
}
|
|
111
|
-
debugLog('skip_auto_state_disabled', { stickyKey });
|
|
112
|
-
return markSkip('skip_auto_state_disabled', { armed: false, mode: 'off' });
|
|
113
|
-
}
|
|
114
|
-
if (!state || !hasArmedStopMessageState(state)) {
|
|
115
|
-
const fallback = resolveStopMessageSnapshot(rt?.stopMessageState);
|
|
116
|
-
if (fallback) {
|
|
117
|
-
state = createStopMessageState(fallback);
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
const implicit = STOPMESSAGE_IMPLICIT_GEMINI ? resolveImplicitGeminiStopMessageSnapshot(ctx, record) : null;
|
|
121
|
-
if (!implicit) {
|
|
122
|
-
debugLog('skip_no_state', { stickyKey });
|
|
123
|
-
return markSkip('skip_no_state', { armed: false, mode: 'off' });
|
|
124
|
-
}
|
|
125
|
-
state = createStopMessageState(implicit);
|
|
126
|
-
}
|
|
127
|
-
if (stickyKey) {
|
|
128
|
-
persistStopMessageState(stickyKey, state);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
updateCompare({ armed: hasArmedStopMessageState(state) });
|
|
132
|
-
const text = typeof state.stopMessageText === 'string' ? state.stopMessageText.trim() : '';
|
|
133
|
-
const maxRepeats = typeof state.stopMessageMaxRepeats === 'number' && Number.isFinite(state.stopMessageMaxRepeats)
|
|
134
|
-
? Math.max(1, Math.floor(state.stopMessageMaxRepeats))
|
|
135
|
-
: 0;
|
|
136
|
-
const stageMode = normalizeStopMessageModeValue(state.stopMessageStageMode);
|
|
137
|
-
const mode = stageMode === 'on' || stageMode === 'auto' || stageMode === 'off' ? stageMode : 'off';
|
|
138
|
-
const allowModeOnlyState = !text && maxRepeats > 0 && (stageMode === 'on' || stageMode === 'auto');
|
|
114
|
+
const implicit = STOPMESSAGE_IMPLICIT_GEMINI
|
|
115
|
+
? resolveImplicitGeminiStopMessageSnapshot(ctx, record)
|
|
116
|
+
: null;
|
|
117
|
+
if (!STOPMESSAGE_DEFAULT_ENABLED && !implicit) {
|
|
118
|
+
debugLog('skip_default_disabled');
|
|
119
|
+
return markSkip('skip_default_disabled', { armed: false, mode: 'off' });
|
|
120
|
+
}
|
|
121
|
+
const text = ((typeof implicit?.text === 'string' && implicit.text.trim().length > 0
|
|
122
|
+
? implicit.text
|
|
123
|
+
: STOPMESSAGE_DEFAULT_TEXT) || '继续执行').trim();
|
|
124
|
+
const maxRepeats = typeof implicit?.maxRepeats === 'number' && Number.isFinite(implicit.maxRepeats) && implicit.maxRepeats > 0
|
|
125
|
+
? Math.floor(implicit.maxRepeats)
|
|
126
|
+
: STOPMESSAGE_DEFAULT_MAX_REPEATS;
|
|
139
127
|
updateCompare({
|
|
140
|
-
|
|
141
|
-
|
|
128
|
+
armed: true,
|
|
129
|
+
mode: 'on',
|
|
130
|
+
allowModeOnly: text.length === 0,
|
|
142
131
|
textLength: text.length,
|
|
143
|
-
maxRepeats
|
|
132
|
+
maxRepeats: Math.max(1, maxRepeats),
|
|
133
|
+
used: 0
|
|
144
134
|
});
|
|
145
|
-
if ((!text && !allowModeOnlyState) || maxRepeats <= 0) {
|
|
146
|
-
debugLog('skip_invalid_text_or_maxRepeats', {
|
|
147
|
-
stickyKey,
|
|
148
|
-
textLength: text.length,
|
|
149
|
-
maxRepeats,
|
|
150
|
-
stageMode
|
|
151
|
-
});
|
|
152
|
-
return markSkip('skip_invalid_text_or_maxRepeats');
|
|
153
|
-
}
|
|
154
|
-
const used = typeof state.stopMessageUsed === 'number' && Number.isFinite(state.stopMessageUsed)
|
|
155
|
-
? Math.max(0, Math.floor(state.stopMessageUsed))
|
|
156
|
-
: 0;
|
|
157
|
-
updateCompare({ used });
|
|
158
|
-
if (used >= maxRepeats) {
|
|
159
|
-
debugLog('skip_reached_max_repeats', {
|
|
160
|
-
stickyKey,
|
|
161
|
-
used,
|
|
162
|
-
maxRepeats
|
|
163
|
-
});
|
|
164
|
-
clearStopMessageState(state, Date.now());
|
|
165
|
-
if (stickyKey) {
|
|
166
|
-
persistStopMessageState(stickyKey, state);
|
|
167
|
-
}
|
|
168
|
-
return markSkip('skip_reached_max_repeats');
|
|
169
|
-
}
|
|
170
135
|
const stopEligible = isStopEligibleForServerTool(ctx.base, ctx.adapterContext);
|
|
171
136
|
updateCompare({ stopEligible });
|
|
172
137
|
if (!stopEligible) {
|
|
173
|
-
debugLog('skip_not_stop_finish_reason'
|
|
174
|
-
stickyKey
|
|
175
|
-
});
|
|
138
|
+
debugLog('skip_not_stop_finish_reason');
|
|
176
139
|
return markSkip('skip_not_stop_finish_reason');
|
|
177
140
|
}
|
|
178
141
|
const captured = getCapturedRequest(ctx.adapterContext);
|
|
179
142
|
updateCompare({ hasCapturedRequest: Boolean(captured) });
|
|
180
143
|
if (!captured) {
|
|
181
|
-
debugLog('skip_no_captured_request'
|
|
182
|
-
stickyKey
|
|
183
|
-
});
|
|
144
|
+
debugLog('skip_no_captured_request');
|
|
184
145
|
return markSkip('skip_no_captured_request');
|
|
185
146
|
}
|
|
186
147
|
const compactionRequest = isCompactionRequest(captured);
|
|
187
148
|
updateCompare({ compactionRequest });
|
|
188
149
|
if (compactionRequest) {
|
|
189
|
-
debugLog('skip_compaction_request'
|
|
150
|
+
debugLog('skip_compaction_request');
|
|
190
151
|
return markSkip('skip_compaction_request');
|
|
191
152
|
}
|
|
192
153
|
const entryEndpoint = resolveEntryEndpoint(record);
|
|
193
154
|
const seed = extractCapturedChatSeed(captured);
|
|
194
155
|
updateCompare({ hasSeed: Boolean(seed) });
|
|
195
156
|
if (!seed) {
|
|
196
|
-
debugLog('skip_failed_build_followup'
|
|
157
|
+
debugLog('skip_failed_build_followup');
|
|
197
158
|
return markSkip('skip_failed_build_followup');
|
|
198
159
|
}
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
160
|
+
const used = 0;
|
|
161
|
+
let followupText = text || '继续执行';
|
|
162
|
+
const autoResponseSnapshot = extractStopMessageAutoResponseSnapshot(ctx.base, ctx.adapterContext);
|
|
163
|
+
const iflowFollowupText = renderStopMessageAutoFollowupViaIflow({
|
|
164
|
+
baseStopMessageText: text,
|
|
165
|
+
candidateFollowupText: followupText,
|
|
166
|
+
responseSnapshot: autoResponseSnapshot,
|
|
167
|
+
requestId: typeof record.requestId === 'string'
|
|
168
|
+
? record.requestId.trim()
|
|
169
|
+
: undefined,
|
|
170
|
+
sessionId: typeof record.sessionId === 'string'
|
|
171
|
+
? record.sessionId.trim()
|
|
172
|
+
: undefined,
|
|
173
|
+
providerKey: resolveStopMessageFollowupProviderKey({ record, runtimeMetadata: rt }),
|
|
174
|
+
model: typeof seed.model === 'string' ? seed.model : undefined,
|
|
175
|
+
usedRepeats: used,
|
|
176
|
+
maxRepeats
|
|
216
177
|
});
|
|
217
|
-
if (
|
|
218
|
-
|
|
219
|
-
if (
|
|
220
|
-
|
|
178
|
+
if (iflowFollowupText) {
|
|
179
|
+
followupText = iflowFollowupText.trim();
|
|
180
|
+
if (text && !followupText.includes(text)) {
|
|
181
|
+
followupText = `${text}\n${followupText}`.trim();
|
|
221
182
|
}
|
|
222
|
-
debugLog('
|
|
223
|
-
|
|
224
|
-
reason: stageDecision.stopReason,
|
|
225
|
-
observationStableCount: stageDecision.observationStableCount,
|
|
226
|
-
bdWorkState: stageDecision.bdWorkState
|
|
183
|
+
debugLog('iflow_automessage_followup_applied', {
|
|
184
|
+
textLength: followupText.length
|
|
227
185
|
});
|
|
228
|
-
return markSkip('stage_policy_stop');
|
|
229
186
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
187
|
+
else {
|
|
188
|
+
followupText = '继续执行';
|
|
189
|
+
debugLog('iflow_automessage_followup_fallback_to_continue_execution');
|
|
190
|
+
}
|
|
233
191
|
if (!followupText) {
|
|
234
|
-
debugLog('skip_empty_followup_text_after_stage'
|
|
235
|
-
stickyKey,
|
|
236
|
-
stage: stageDecision.stage,
|
|
237
|
-
bdWorkState: stageDecision.bdWorkState
|
|
238
|
-
});
|
|
192
|
+
debugLog('skip_empty_followup_text_after_stage');
|
|
239
193
|
return markSkip('skip_empty_followup_text_after_stage');
|
|
240
194
|
}
|
|
241
|
-
const nextUsed = used + 1;
|
|
242
|
-
state.stopMessageUsed = nextUsed;
|
|
243
|
-
state.stopMessageLastUsedAt = Date.now();
|
|
244
|
-
updateCompare({ used: nextUsed });
|
|
245
|
-
if (nextUsed >= maxRepeats) {
|
|
246
|
-
clearStopMessageState(state, Date.now());
|
|
247
|
-
}
|
|
248
|
-
if (stickyKey) {
|
|
249
|
-
persistStopMessageState(stickyKey, state);
|
|
250
|
-
}
|
|
251
195
|
const followupProviderKey = resolveStopMessageFollowupProviderKey({ record, runtimeMetadata: rt });
|
|
252
196
|
const followupToolContentMaxChars = resolveStopMessageFollowupToolContentMaxChars({
|
|
253
197
|
providerKey: followupProviderKey,
|
|
@@ -289,423 +233,3 @@ const handler = async (ctx) => {
|
|
|
289
233
|
}
|
|
290
234
|
};
|
|
291
235
|
registerServerToolHandler('stop_message_auto', handler, { trigger: 'auto', hook: { phase: 'default', priority: 40 } });
|
|
292
|
-
function resolveStickyKey(record, runtimeMetadata) {
|
|
293
|
-
const sessionId = readSessionScopeValue(record, runtimeMetadata, 'sessionId') ||
|
|
294
|
-
readSessionScopeValue(record, runtimeMetadata, 'session_id');
|
|
295
|
-
const conversationId = readSessionScopeValue(record, runtimeMetadata, 'conversationId') ||
|
|
296
|
-
readSessionScopeValue(record, runtimeMetadata, 'conversation_id');
|
|
297
|
-
if (sessionId) {
|
|
298
|
-
return `session:${sessionId}`;
|
|
299
|
-
}
|
|
300
|
-
if (conversationId) {
|
|
301
|
-
return `conversation:${conversationId}`;
|
|
302
|
-
}
|
|
303
|
-
return undefined;
|
|
304
|
-
}
|
|
305
|
-
function persistStopMessageState(stickyKey, state) {
|
|
306
|
-
if (!stickyKey) {
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
saveRoutingInstructionStateSync(stickyKey, state);
|
|
310
|
-
saveRoutingInstructionStateAsync(stickyKey, state);
|
|
311
|
-
}
|
|
312
|
-
function readSessionScopeValue(record, runtimeMetadata, key) {
|
|
313
|
-
const direct = toNonEmptyText(record[key]);
|
|
314
|
-
if (direct) {
|
|
315
|
-
return direct;
|
|
316
|
-
}
|
|
317
|
-
const metadata = asRecord(record.metadata);
|
|
318
|
-
const fromMetadata = metadata ? toNonEmptyText(metadata[key]) : '';
|
|
319
|
-
if (fromMetadata) {
|
|
320
|
-
return fromMetadata;
|
|
321
|
-
}
|
|
322
|
-
const fromMetadataContext = metadata ? toNonEmptyText(asRecord(metadata.context)?.[key]) : '';
|
|
323
|
-
if (fromMetadataContext) {
|
|
324
|
-
return fromMetadataContext;
|
|
325
|
-
}
|
|
326
|
-
const originalRequest = asRecord(record.originalRequest);
|
|
327
|
-
const fromOriginalMetadata = originalRequest
|
|
328
|
-
? toNonEmptyText(asRecord(originalRequest.metadata)?.[key])
|
|
329
|
-
: '';
|
|
330
|
-
if (fromOriginalMetadata) {
|
|
331
|
-
return fromOriginalMetadata;
|
|
332
|
-
}
|
|
333
|
-
const runtime = asRecord(runtimeMetadata);
|
|
334
|
-
const fromRuntime = runtime ? toNonEmptyText(runtime[key]) : '';
|
|
335
|
-
if (fromRuntime) {
|
|
336
|
-
return fromRuntime;
|
|
337
|
-
}
|
|
338
|
-
return '';
|
|
339
|
-
}
|
|
340
|
-
function asRecord(value) {
|
|
341
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
342
|
-
return null;
|
|
343
|
-
}
|
|
344
|
-
return value;
|
|
345
|
-
}
|
|
346
|
-
function toNonEmptyText(value) {
|
|
347
|
-
return typeof value === 'string' && value.trim().length ? value.trim() : '';
|
|
348
|
-
}
|
|
349
|
-
function readProviderKeyFromMetadata(value) {
|
|
350
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
351
|
-
return '';
|
|
352
|
-
}
|
|
353
|
-
const metadata = value;
|
|
354
|
-
const direct = toNonEmptyText(metadata.providerKey) ||
|
|
355
|
-
toNonEmptyText(metadata.providerId) ||
|
|
356
|
-
toNonEmptyText(metadata.targetProviderKey);
|
|
357
|
-
if (direct) {
|
|
358
|
-
return direct;
|
|
359
|
-
}
|
|
360
|
-
const target = metadata.target;
|
|
361
|
-
if (target && typeof target === 'object' && !Array.isArray(target)) {
|
|
362
|
-
const targetRecord = target;
|
|
363
|
-
return toNonEmptyText(targetRecord.providerKey) || toNonEmptyText(targetRecord.providerId);
|
|
364
|
-
}
|
|
365
|
-
return '';
|
|
366
|
-
}
|
|
367
|
-
function hasArmedStopMessageState(state) {
|
|
368
|
-
const maxRepeats = typeof state.stopMessageMaxRepeats === 'number' && Number.isFinite(state.stopMessageMaxRepeats)
|
|
369
|
-
? Math.max(1, Math.floor(state.stopMessageMaxRepeats))
|
|
370
|
-
: 0;
|
|
371
|
-
if (maxRepeats <= 0) {
|
|
372
|
-
return false;
|
|
373
|
-
}
|
|
374
|
-
const text = typeof state.stopMessageText === 'string' ? state.stopMessageText.trim() : '';
|
|
375
|
-
if (text) {
|
|
376
|
-
return true;
|
|
377
|
-
}
|
|
378
|
-
const mode = normalizeStopMessageModeValue(state.stopMessageStageMode);
|
|
379
|
-
return mode === 'on' || mode === 'auto';
|
|
380
|
-
}
|
|
381
|
-
function normalizeStopMessageModeValue(value) {
|
|
382
|
-
if (typeof value !== 'string') {
|
|
383
|
-
return undefined;
|
|
384
|
-
}
|
|
385
|
-
const normalized = value.trim().toLowerCase();
|
|
386
|
-
if (normalized === 'on' || normalized === 'off' || normalized === 'auto') {
|
|
387
|
-
return normalized;
|
|
388
|
-
}
|
|
389
|
-
return undefined;
|
|
390
|
-
}
|
|
391
|
-
function resolveStopMessageFollowupProviderKey(args) {
|
|
392
|
-
const direct = toNonEmptyText(args.record.providerKey) ||
|
|
393
|
-
toNonEmptyText(args.record.providerId) ||
|
|
394
|
-
readProviderKeyFromMetadata(args.record.metadata) ||
|
|
395
|
-
readProviderKeyFromMetadata(args.runtimeMetadata);
|
|
396
|
-
return direct;
|
|
397
|
-
}
|
|
398
|
-
function resolveStopMessageFollowupToolContentMaxChars(params) {
|
|
399
|
-
const raw = String(process.env.ROUTECODEX_STOPMESSAGE_FOLLOWUP_TOOL_CONTENT_MAX_CHARS || '').trim();
|
|
400
|
-
if (raw) {
|
|
401
|
-
const parsed = Number(raw);
|
|
402
|
-
if (Number.isFinite(parsed) && parsed > 0) {
|
|
403
|
-
return Math.max(64, Math.floor(parsed));
|
|
404
|
-
}
|
|
405
|
-
return undefined;
|
|
406
|
-
}
|
|
407
|
-
const providerKey = typeof params.providerKey === 'string' ? params.providerKey.trim().toLowerCase() : '';
|
|
408
|
-
if (providerKey.startsWith('iflow.')) {
|
|
409
|
-
return 1200;
|
|
410
|
-
}
|
|
411
|
-
const model = typeof params.model === 'string' ? params.model.trim().toLowerCase() : '';
|
|
412
|
-
if (model === 'kimi-k2.5' || model.startsWith('kimi-k2.5-')) {
|
|
413
|
-
return 1200;
|
|
414
|
-
}
|
|
415
|
-
return undefined;
|
|
416
|
-
}
|
|
417
|
-
function getCapturedRequest(adapterContext) {
|
|
418
|
-
if (!adapterContext || typeof adapterContext !== 'object') {
|
|
419
|
-
return null;
|
|
420
|
-
}
|
|
421
|
-
const contextRecord = adapterContext;
|
|
422
|
-
const direct = contextRecord.capturedChatRequest;
|
|
423
|
-
if (direct && typeof direct === 'object' && !Array.isArray(direct)) {
|
|
424
|
-
return direct;
|
|
425
|
-
}
|
|
426
|
-
const runtime = readRuntimeMetadata(contextRecord);
|
|
427
|
-
const runtimeCaptured = runtime && typeof runtime === 'object' && !Array.isArray(runtime)
|
|
428
|
-
? runtime.capturedChatRequest
|
|
429
|
-
: undefined;
|
|
430
|
-
if (runtimeCaptured && typeof runtimeCaptured === 'object' && !Array.isArray(runtimeCaptured)) {
|
|
431
|
-
return runtimeCaptured;
|
|
432
|
-
}
|
|
433
|
-
const originalRequest = contextRecord.originalRequest;
|
|
434
|
-
if (originalRequest && typeof originalRequest === 'object' && !Array.isArray(originalRequest)) {
|
|
435
|
-
return originalRequest;
|
|
436
|
-
}
|
|
437
|
-
return null;
|
|
438
|
-
}
|
|
439
|
-
function extractResponsesOutputText(base) {
|
|
440
|
-
const raw = base.output_text;
|
|
441
|
-
if (typeof raw === 'string') {
|
|
442
|
-
return raw.trim();
|
|
443
|
-
}
|
|
444
|
-
if (Array.isArray(raw)) {
|
|
445
|
-
const texts = raw
|
|
446
|
-
.map((entry) => (typeof entry === 'string' ? entry : ''))
|
|
447
|
-
.filter((entry) => entry.trim().length > 0);
|
|
448
|
-
if (texts.length > 0) {
|
|
449
|
-
return texts.join('\n').trim();
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
const output = Array.isArray(base.output) ? (base.output) : [];
|
|
453
|
-
const chunks = [];
|
|
454
|
-
for (const item of output) {
|
|
455
|
-
if (!item || typeof item !== 'object' || Array.isArray(item))
|
|
456
|
-
continue;
|
|
457
|
-
if (typeof item.type !== 'string')
|
|
458
|
-
continue;
|
|
459
|
-
const type = String(item.type).trim().toLowerCase();
|
|
460
|
-
if (type !== 'message')
|
|
461
|
-
continue;
|
|
462
|
-
const content = Array.isArray(item.content) ? (item.content) : [];
|
|
463
|
-
for (const part of content) {
|
|
464
|
-
if (!part || typeof part !== 'object' || Array.isArray(part))
|
|
465
|
-
continue;
|
|
466
|
-
const pType = typeof part.type === 'string'
|
|
467
|
-
? String(part.type).trim().toLowerCase()
|
|
468
|
-
: '';
|
|
469
|
-
if (pType === 'output_text') {
|
|
470
|
-
const text = typeof part.text === 'string' ? String(part.text) : '';
|
|
471
|
-
if (text.trim().length)
|
|
472
|
-
chunks.push(text.trim());
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
return chunks.join('\n').trim();
|
|
477
|
-
}
|
|
478
|
-
function hasToolLikeOutput(value) {
|
|
479
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
480
|
-
return false;
|
|
481
|
-
}
|
|
482
|
-
const typeRaw = value.type;
|
|
483
|
-
const type = typeof typeRaw === 'string' ? typeRaw.trim().toLowerCase() : '';
|
|
484
|
-
if (!type) {
|
|
485
|
-
return false;
|
|
486
|
-
}
|
|
487
|
-
return (type === 'tool_call' ||
|
|
488
|
-
type === 'tool_use' ||
|
|
489
|
-
type === 'function_call' ||
|
|
490
|
-
type.includes('tool'));
|
|
491
|
-
}
|
|
492
|
-
function resolveClientConnectionState(value) {
|
|
493
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
494
|
-
return null;
|
|
495
|
-
}
|
|
496
|
-
return value;
|
|
497
|
-
}
|
|
498
|
-
function resolveStopMessageSnapshot(raw) {
|
|
499
|
-
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
500
|
-
return null;
|
|
501
|
-
}
|
|
502
|
-
const record = raw;
|
|
503
|
-
const text = typeof record.stopMessageText === 'string' ? record.stopMessageText.trim() : '';
|
|
504
|
-
const maxRepeats = typeof record.stopMessageMaxRepeats === 'number' && Number.isFinite(record.stopMessageMaxRepeats)
|
|
505
|
-
? Math.max(1, Math.floor(record.stopMessageMaxRepeats))
|
|
506
|
-
: 0;
|
|
507
|
-
const stageMode = normalizeStopMessageStageMode(record.stopMessageStageMode);
|
|
508
|
-
const allowModeOnlyState = !text && maxRepeats > 0 && (stageMode === 'on' || stageMode === 'auto');
|
|
509
|
-
if ((!text && !allowModeOnlyState) || maxRepeats <= 0) {
|
|
510
|
-
return null;
|
|
511
|
-
}
|
|
512
|
-
const used = typeof record.stopMessageUsed === 'number' && Number.isFinite(record.stopMessageUsed)
|
|
513
|
-
? Math.max(0, Math.floor(record.stopMessageUsed))
|
|
514
|
-
: 0;
|
|
515
|
-
const updatedAt = typeof record.stopMessageUpdatedAt === 'number' && Number.isFinite(record.stopMessageUpdatedAt)
|
|
516
|
-
? record.stopMessageUpdatedAt
|
|
517
|
-
: undefined;
|
|
518
|
-
const lastUsedAt = typeof record.stopMessageLastUsedAt === 'number' && Number.isFinite(record.stopMessageLastUsedAt)
|
|
519
|
-
? record.stopMessageLastUsedAt
|
|
520
|
-
: undefined;
|
|
521
|
-
const source = typeof record.stopMessageSource === 'string' && record.stopMessageSource.trim()
|
|
522
|
-
? record.stopMessageSource.trim()
|
|
523
|
-
: undefined;
|
|
524
|
-
const stage = typeof record.stopMessageStage === 'string' && record.stopMessageStage.trim()
|
|
525
|
-
? record.stopMessageStage.trim()
|
|
526
|
-
: undefined;
|
|
527
|
-
const observationHash = typeof record.stopMessageObservationHash === 'string' && record.stopMessageObservationHash.trim()
|
|
528
|
-
? record.stopMessageObservationHash.trim()
|
|
529
|
-
: undefined;
|
|
530
|
-
const observationStableCount = typeof record.stopMessageObservationStableCount === 'number' && Number.isFinite(record.stopMessageObservationStableCount)
|
|
531
|
-
? Math.max(0, Math.floor(record.stopMessageObservationStableCount))
|
|
532
|
-
: undefined;
|
|
533
|
-
const bdWorkState = typeof record.stopMessageBdWorkState === 'string' && record.stopMessageBdWorkState.trim()
|
|
534
|
-
? record.stopMessageBdWorkState.trim()
|
|
535
|
-
: undefined;
|
|
536
|
-
return {
|
|
537
|
-
text,
|
|
538
|
-
maxRepeats,
|
|
539
|
-
used,
|
|
540
|
-
...(source ? { source } : {}),
|
|
541
|
-
...(updatedAt ? { updatedAt } : {}),
|
|
542
|
-
...(lastUsedAt ? { lastUsedAt } : {}),
|
|
543
|
-
...(stage ? { stage } : {}),
|
|
544
|
-
...(stageMode ? { stageMode } : {}),
|
|
545
|
-
...(observationHash ? { observationHash } : {}),
|
|
546
|
-
...(typeof observationStableCount === 'number' ? { observationStableCount } : {}),
|
|
547
|
-
...(bdWorkState ? { bdWorkState } : {})
|
|
548
|
-
};
|
|
549
|
-
}
|
|
550
|
-
function hasCompactionFlag(rt) {
|
|
551
|
-
const flag = rt && typeof rt === 'object' && !Array.isArray(rt) ? rt.compactionRequest : undefined;
|
|
552
|
-
if (flag === true) {
|
|
553
|
-
return true;
|
|
554
|
-
}
|
|
555
|
-
if (typeof flag === 'string' && flag.trim().toLowerCase() === 'true') {
|
|
556
|
-
return true;
|
|
557
|
-
}
|
|
558
|
-
return false;
|
|
559
|
-
}
|
|
560
|
-
function resolveImplicitGeminiStopMessageSnapshot(ctx, record) {
|
|
561
|
-
try {
|
|
562
|
-
const protoFromCtx = ctx.providerProtocol;
|
|
563
|
-
const protoFromRecord = typeof record.providerProtocol === 'string' && record.providerProtocol.trim()
|
|
564
|
-
? String(record.providerProtocol).trim()
|
|
565
|
-
: undefined;
|
|
566
|
-
const providerProtocol = (protoFromCtx || protoFromRecord || '').toString().toLowerCase();
|
|
567
|
-
if (providerProtocol !== 'gemini-chat') {
|
|
568
|
-
return null;
|
|
569
|
-
}
|
|
570
|
-
const entryFromRecord = typeof record.entryEndpoint === 'string' && record.entryEndpoint.trim()
|
|
571
|
-
? String(record.entryEndpoint).trim()
|
|
572
|
-
: undefined;
|
|
573
|
-
const metaEntry = record.metadata &&
|
|
574
|
-
typeof record.metadata === 'object' &&
|
|
575
|
-
record.metadata.entryEndpoint;
|
|
576
|
-
const entryFromMeta = typeof metaEntry === 'string' && metaEntry.trim() ? metaEntry.trim() : undefined;
|
|
577
|
-
const entryEndpoint = (entryFromRecord || entryFromMeta || '').toLowerCase();
|
|
578
|
-
if (!entryEndpoint.includes('/v1/responses')) {
|
|
579
|
-
return null;
|
|
580
|
-
}
|
|
581
|
-
// 仅在本轮响应被视为“自然结束”(stop/length,且无 tool_calls)时触发,避免干扰正常对话。
|
|
582
|
-
if (!isStopEligibleForServerTool(ctx.base, ctx.adapterContext)) {
|
|
583
|
-
return null;
|
|
584
|
-
}
|
|
585
|
-
// 仅在“空回复”时触发隐式 stopMessage:
|
|
586
|
-
// - 这个场景由 empty_reply_continue 专门处理;
|
|
587
|
-
// - stop_message_auto 里的隐式逻辑只作为兼容兜底(且默认关闭),避免对正常 stop 响应追加“继续执行”。
|
|
588
|
-
if (!isEmptyAssistantReply(ctx.base)) {
|
|
589
|
-
return null;
|
|
590
|
-
}
|
|
591
|
-
return {
|
|
592
|
-
text: '继续执行',
|
|
593
|
-
maxRepeats: 1,
|
|
594
|
-
used: 0,
|
|
595
|
-
source: 'auto'
|
|
596
|
-
};
|
|
597
|
-
}
|
|
598
|
-
catch {
|
|
599
|
-
return null;
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
function isEmptyAssistantReply(base) {
|
|
603
|
-
if (!base || typeof base !== 'object' || Array.isArray(base)) {
|
|
604
|
-
return false;
|
|
605
|
-
}
|
|
606
|
-
const payload = base;
|
|
607
|
-
const choicesRaw = payload.choices;
|
|
608
|
-
if (Array.isArray(choicesRaw) && choicesRaw.length) {
|
|
609
|
-
const first = choicesRaw[0];
|
|
610
|
-
if (!first || typeof first !== 'object' || Array.isArray(first)) {
|
|
611
|
-
return false;
|
|
612
|
-
}
|
|
613
|
-
const finishReasonRaw = first.finish_reason;
|
|
614
|
-
const finishReason = typeof finishReasonRaw === 'string' && finishReasonRaw.trim()
|
|
615
|
-
? finishReasonRaw.trim().toLowerCase()
|
|
616
|
-
: '';
|
|
617
|
-
// 仅接受 stop:length 截断通常并非“空回复”,而是需要续写(由 empty_reply_continue 负责)。
|
|
618
|
-
if (finishReason !== 'stop') {
|
|
619
|
-
return false;
|
|
620
|
-
}
|
|
621
|
-
const message = first.message &&
|
|
622
|
-
typeof first.message === 'object' &&
|
|
623
|
-
!Array.isArray(first.message)
|
|
624
|
-
? first.message
|
|
625
|
-
: null;
|
|
626
|
-
if (!message) {
|
|
627
|
-
return false;
|
|
628
|
-
}
|
|
629
|
-
const toolCalls = Array.isArray(message.tool_calls) ? message.tool_calls : [];
|
|
630
|
-
if (toolCalls.length > 0) {
|
|
631
|
-
return false;
|
|
632
|
-
}
|
|
633
|
-
const contentRaw = message.content;
|
|
634
|
-
const text = typeof contentRaw === 'string' ? contentRaw.trim() : '';
|
|
635
|
-
return text.length === 0;
|
|
636
|
-
}
|
|
637
|
-
// OpenAI Responses shape: treat empty output_text + no tool-like output as empty reply.
|
|
638
|
-
const statusRaw = typeof payload.status === 'string' ? payload.status.trim().toLowerCase() : '';
|
|
639
|
-
if (statusRaw && statusRaw !== 'completed') {
|
|
640
|
-
return false;
|
|
641
|
-
}
|
|
642
|
-
if (payload.required_action && typeof payload.required_action === 'object') {
|
|
643
|
-
return false;
|
|
644
|
-
}
|
|
645
|
-
const outputText = extractResponsesOutputText(payload);
|
|
646
|
-
if (outputText.length > 0) {
|
|
647
|
-
return false;
|
|
648
|
-
}
|
|
649
|
-
const outputRaw = Array.isArray(payload.output) ? payload.output : [];
|
|
650
|
-
if (outputRaw.some((item) => hasToolLikeOutput(item))) {
|
|
651
|
-
return false;
|
|
652
|
-
}
|
|
653
|
-
return true;
|
|
654
|
-
}
|
|
655
|
-
function createStopMessageState(snapshot) {
|
|
656
|
-
return {
|
|
657
|
-
forcedTarget: undefined,
|
|
658
|
-
stickyTarget: undefined,
|
|
659
|
-
allowedProviders: new Set(),
|
|
660
|
-
disabledProviders: new Set(),
|
|
661
|
-
disabledKeys: new Map(),
|
|
662
|
-
disabledModels: new Map(),
|
|
663
|
-
stopMessageSource: snapshot.source && snapshot.source.trim() ? snapshot.source.trim() : 'explicit',
|
|
664
|
-
stopMessageText: snapshot.text,
|
|
665
|
-
stopMessageMaxRepeats: snapshot.maxRepeats,
|
|
666
|
-
stopMessageUsed: snapshot.used,
|
|
667
|
-
stopMessageUpdatedAt: snapshot.updatedAt,
|
|
668
|
-
stopMessageLastUsedAt: snapshot.lastUsedAt,
|
|
669
|
-
stopMessageStage: snapshot.stage,
|
|
670
|
-
stopMessageStageMode: snapshot.stageMode,
|
|
671
|
-
stopMessageObservationHash: snapshot.observationHash,
|
|
672
|
-
stopMessageObservationStableCount: snapshot.observationStableCount,
|
|
673
|
-
stopMessageBdWorkState: snapshot.bdWorkState
|
|
674
|
-
};
|
|
675
|
-
}
|
|
676
|
-
function normalizeStopMessageStageMode(value) {
|
|
677
|
-
if (typeof value !== 'string') {
|
|
678
|
-
return undefined;
|
|
679
|
-
}
|
|
680
|
-
const normalized = value.trim().toLowerCase();
|
|
681
|
-
if (normalized === 'on' || normalized === 'off' || normalized === 'auto') {
|
|
682
|
-
return normalized;
|
|
683
|
-
}
|
|
684
|
-
return undefined;
|
|
685
|
-
}
|
|
686
|
-
function clearStopMessageState(state, now) {
|
|
687
|
-
state.stopMessageText = undefined;
|
|
688
|
-
state.stopMessageMaxRepeats = undefined;
|
|
689
|
-
state.stopMessageUsed = undefined;
|
|
690
|
-
state.stopMessageSource = undefined;
|
|
691
|
-
state.stopMessageStage = undefined;
|
|
692
|
-
state.stopMessageStageMode = undefined;
|
|
693
|
-
state.stopMessageObservationHash = undefined;
|
|
694
|
-
state.stopMessageObservationStableCount = undefined;
|
|
695
|
-
state.stopMessageBdWorkState = undefined;
|
|
696
|
-
state.stopMessageUpdatedAt = now;
|
|
697
|
-
state.stopMessageLastUsedAt = now;
|
|
698
|
-
}
|
|
699
|
-
function resolveEntryEndpoint(record) {
|
|
700
|
-
const raw = typeof record.entryEndpoint === 'string' && record.entryEndpoint.trim()
|
|
701
|
-
? record.entryEndpoint.trim()
|
|
702
|
-
: undefined;
|
|
703
|
-
if (raw) {
|
|
704
|
-
return raw;
|
|
705
|
-
}
|
|
706
|
-
const metaEntry = record.metadata && typeof record.metadata === 'object' && record.metadata.entryEndpoint;
|
|
707
|
-
if (typeof metaEntry === 'string' && metaEntry.trim()) {
|
|
708
|
-
return metaEntry.trim();
|
|
709
|
-
}
|
|
710
|
-
return '/v1/chat/completions';
|
|
711
|
-
}
|