@jsonstudio/llms 0.6.1164 → 0.6.1354
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/codecs/gemini-openai-codec.d.ts +3 -1
- package/dist/conversion/codecs/gemini-openai-codec.js +10 -4
- package/dist/conversion/compat/actions/gemini-web-search.d.ts +1 -1
- package/dist/conversion/compat/actions/gemini-web-search.js +5 -2
- package/dist/conversion/compat/actions/iflow-tool-text-fallback.d.ts +12 -0
- package/dist/conversion/compat/actions/iflow-tool-text-fallback.js +199 -0
- package/dist/conversion/compat/actions/iflow-web-search.d.ts +1 -1
- package/dist/conversion/compat/actions/iflow-web-search.js +5 -2
- package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +47 -56
- package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.js +1 -13
- package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +523 -50
- package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +18 -38
- package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +6 -0
- package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +3 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/adapter-context.d.ts +10 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/adapter-context.js +134 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/anthropic-alias-map.d.ts +6 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/anthropic-alias-map.js +79 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/apply-patch-tool-mode.d.ts +3 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/apply-patch-tool-mode.js +46 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/execute-chat-process-entry.d.ts +8 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/execute-chat-process-entry.js +366 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/execute-request-stage.d.ts +9 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/execute-request-stage.js +384 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/node-results.d.ts +3 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/node-results.js +14 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/payload-normalize.d.ts +2 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/payload-normalize.js +144 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/policy.d.ts +4 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/policy.js +32 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/protocol.d.ts +8 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/protocol.js +63 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/resolve-protocol-hooks.d.ts +2 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/resolve-protocol-hooks.js +43 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/semantic-gate.d.ts +1 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/semantic-gate.js +29 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/servertool-runtime-config.d.ts +2 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/servertool-runtime-config.js +16 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/types.d.ts +116 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/types.js +1 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +3 -95
- package/dist/conversion/hub/pipeline/hub-pipeline.js +19 -1281
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage1_format_parse/index.js +1 -1
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.d.ts +7 -0
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +65 -1
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +25 -22
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +1 -1
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_format_build/index.d.ts +1 -1
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_format_build/index.js +2 -2
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.js +2 -2
- package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.js +1 -1
- package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage2_route_select/index.js +1 -1
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js +11 -11
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage2_format_parse/index.js +1 -1
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_semantic_map/index.d.ts +1 -0
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_semantic_map/index.js +4 -2
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.d.ts +1 -0
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +17 -9
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage2_sse_stream/index.js +2 -2
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js +40 -2
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage2_finalize/index.js +1 -1
- package/dist/conversion/hub/pipeline/target-utils.js +9 -5
- package/dist/conversion/hub/process/chat-process.js +256 -16
- package/dist/conversion/hub/response/provider-response.d.ts +8 -0
- package/dist/conversion/hub/response/provider-response.js +85 -27
- package/dist/conversion/hub/response/response-mappers.d.ts +10 -3
- package/dist/conversion/hub/response/response-mappers.js +30 -6
- package/dist/conversion/hub/response/response-runtime.js +4 -38
- package/dist/conversion/hub/snapshot-recorder.js +5 -1
- package/dist/conversion/hub/standardized-bridge.js +23 -15
- package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.js +36 -5
- package/dist/conversion/responses/responses-openai-bridge.js +20 -4
- package/dist/conversion/shared/gemini-tool-utils.d.ts +8 -1
- package/dist/conversion/shared/gemini-tool-utils.js +580 -108
- package/dist/conversion/shared/jsonish.js +1 -1
- package/dist/conversion/shared/mcp-injection.js +67 -33
- package/dist/conversion/shared/openai-finalizer.js +2 -1
- package/dist/conversion/shared/openai-message-normalize.js +76 -21
- package/dist/conversion/shared/responses-output-builder.js +6 -0
- package/dist/conversion/shared/runtime-metadata.d.ts +7 -0
- package/dist/conversion/shared/runtime-metadata.js +23 -0
- package/dist/conversion/shared/text-markup-normalizer.d.ts +2 -0
- package/dist/conversion/shared/text-markup-normalizer.js +284 -4
- package/dist/conversion/shared/tool-canonicalizer.js +2 -1
- package/dist/conversion/shared/tool-governor.js +3 -3
- package/dist/filters/engine.js +5 -5
- package/dist/filters/special/request-tool-list-filter.js +194 -60
- package/dist/filters/special/request-tools-normalize.js +1 -1
- package/dist/filters/special/response-tool-text-canonicalize.d.ts +4 -7
- package/dist/filters/special/response-tool-text-canonicalize.js +7 -35
- package/dist/filters/special/tool-filter-hooks.js +58 -62
- package/dist/guidance/index.js +5 -1
- package/dist/http/sse-response.js +6 -6
- package/dist/router/virtual-router/bootstrap.js +65 -5
- package/dist/router/virtual-router/context-advisor.d.ts +4 -0
- package/dist/router/virtual-router/context-advisor.js +3 -0
- package/dist/router/virtual-router/context-weighted.d.ts +31 -0
- package/dist/router/virtual-router/context-weighted.js +54 -0
- package/dist/router/virtual-router/engine-health.d.ts +1 -1
- package/dist/router/virtual-router/engine-health.js +11 -110
- package/dist/router/virtual-router/engine-selection/alias-selection.d.ts +15 -0
- package/dist/router/virtual-router/engine-selection/alias-selection.js +156 -0
- package/dist/router/virtual-router/engine-selection/context-weight-multipliers.d.ts +11 -0
- package/dist/router/virtual-router/engine-selection/context-weight-multipliers.js +23 -0
- package/dist/router/virtual-router/engine-selection/direct-provider-model.d.ts +9 -0
- package/dist/router/virtual-router/engine-selection/direct-provider-model.js +49 -0
- package/dist/router/virtual-router/engine-selection/instruction-target.d.ts +6 -0
- package/dist/router/virtual-router/engine-selection/instruction-target.js +54 -0
- package/dist/router/virtual-router/engine-selection/key-parsing.d.ts +8 -0
- package/dist/router/virtual-router/engine-selection/key-parsing.js +64 -0
- package/dist/router/virtual-router/engine-selection/route-utils.d.ts +12 -0
- package/dist/router/virtual-router/engine-selection/route-utils.js +150 -0
- package/dist/router/virtual-router/engine-selection/routing-state-filter.d.ts +4 -0
- package/dist/router/virtual-router/engine-selection/routing-state-filter.js +50 -0
- package/dist/router/virtual-router/engine-selection/selection-deps.d.ts +39 -0
- package/dist/router/virtual-router/engine-selection/selection-deps.js +1 -0
- package/dist/router/virtual-router/engine-selection/sticky-pool.d.ts +11 -0
- package/dist/router/virtual-router/engine-selection/sticky-pool.js +109 -0
- package/dist/router/virtual-router/engine-selection/tier-priority.d.ts +12 -0
- package/dist/router/virtual-router/engine-selection/tier-priority.js +55 -0
- package/dist/router/virtual-router/engine-selection/tier-selection-select.d.ts +22 -0
- package/dist/router/virtual-router/engine-selection/tier-selection-select.js +400 -0
- package/dist/router/virtual-router/engine-selection/tier-selection.d.ts +3 -0
- package/dist/router/virtual-router/engine-selection/tier-selection.js +225 -0
- package/dist/router/virtual-router/engine-selection.d.ts +4 -30
- package/dist/router/virtual-router/engine-selection.js +10 -815
- package/dist/router/virtual-router/engine.d.ts +1 -0
- package/dist/router/virtual-router/engine.js +55 -10
- package/dist/router/virtual-router/routing-instructions.js +6 -1
- package/dist/router/virtual-router/stop-message-state-sync.d.ts +5 -0
- package/dist/router/virtual-router/stop-message-state-sync.js +6 -14
- package/dist/router/virtual-router/types.d.ts +53 -1
- package/dist/servertool/clock/config.d.ts +8 -0
- package/dist/servertool/clock/config.js +22 -0
- package/dist/servertool/clock/log.d.ts +3 -0
- package/dist/servertool/clock/log.js +13 -0
- package/dist/servertool/clock/task-store.d.ts +1 -1
- package/dist/servertool/clock/task-store.js +1 -1
- package/dist/servertool/clock/tasks.js +1 -1
- package/dist/servertool/engine.js +146 -21
- package/dist/servertool/handlers/clock-auto.js +11 -6
- package/dist/servertool/handlers/clock.js +36 -10
- package/dist/servertool/handlers/followup-request-builder.js +8 -2
- package/dist/servertool/handlers/gemini-empty-reply-continue.js +15 -9
- package/dist/servertool/handlers/iflow-model-error-retry.js +6 -4
- package/dist/servertool/handlers/recursive-detection-guard.js +4 -2
- package/dist/servertool/handlers/stop-message-auto.js +100 -10
- package/dist/servertool/handlers/vision.js +4 -1
- package/dist/servertool/handlers/web-search.js +3 -1
- package/dist/servertool/pending-session.d.ts +19 -0
- package/dist/servertool/pending-session.js +97 -0
- package/dist/servertool/reenter-backend.js +5 -3
- package/dist/servertool/server-side-tools.js +235 -6
- package/dist/servertool/types.d.ts +13 -0
- package/dist/sse/json-to-sse/event-generators/responses.js +1 -1
- package/dist/sse/shared/chat-serializer.js +2 -2
- package/dist/sse/shared/constants.js +1 -1
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +7 -1
- package/dist/sse/sse-to-json/builders/response-builder.js +16 -0
- package/dist/sse/sse-to-json/responses-sse-to-json-converter.d.ts +1 -1
- package/dist/tools/apply-patch/execution-capturer.js +1 -1
- package/dist/tools/exec-command/normalize.js +4 -0
- package/dist/tools/exec-command/regression-capturer.js +1 -1
- package/package.json +10 -5
|
@@ -10,6 +10,122 @@ import './handlers/clock-auto.js';
|
|
|
10
10
|
import './handlers/exec-command-guard.js';
|
|
11
11
|
import './handlers/apply-patch-guard.js';
|
|
12
12
|
import './handlers/recursive-detection-guard.js';
|
|
13
|
+
function extractToolCallsFromMessage(message) {
|
|
14
|
+
const toolCalls = getArray(message.tool_calls);
|
|
15
|
+
const out = [];
|
|
16
|
+
for (const raw of toolCalls) {
|
|
17
|
+
const tc = asObject(raw);
|
|
18
|
+
if (!tc)
|
|
19
|
+
continue;
|
|
20
|
+
const id = typeof tc.id === 'string' && String(tc.id).trim() ? String(tc.id).trim() : '';
|
|
21
|
+
const fn = asObject(tc.function) ??
|
|
22
|
+
asObject(tc.functionCall) ??
|
|
23
|
+
asObject(tc.function_call);
|
|
24
|
+
const name = fn && typeof fn.name === 'string' && String(fn.name).trim() ? String(fn.name).trim() : '';
|
|
25
|
+
const rawArgs = (fn ? fn.arguments : undefined) ??
|
|
26
|
+
(fn ? fn.args : undefined) ??
|
|
27
|
+
(fn ? fn.input : undefined) ??
|
|
28
|
+
tc.arguments ??
|
|
29
|
+
tc.args ??
|
|
30
|
+
tc.input;
|
|
31
|
+
let args = '';
|
|
32
|
+
if (typeof rawArgs === 'string') {
|
|
33
|
+
args = rawArgs;
|
|
34
|
+
}
|
|
35
|
+
else if (rawArgs && typeof rawArgs === 'object') {
|
|
36
|
+
try {
|
|
37
|
+
args = JSON.stringify(rawArgs);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
args = '';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else if (rawArgs !== undefined && rawArgs !== null) {
|
|
44
|
+
args = String(rawArgs);
|
|
45
|
+
}
|
|
46
|
+
if (!id || !name)
|
|
47
|
+
continue;
|
|
48
|
+
out.push({ raw: tc, parsed: { id, name, arguments: args } });
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
function buildAssistantToolCallMessage(toolCalls) {
|
|
53
|
+
const calls = toolCalls.map((tc) => ({
|
|
54
|
+
id: tc.id,
|
|
55
|
+
type: 'function',
|
|
56
|
+
function: {
|
|
57
|
+
name: tc.name,
|
|
58
|
+
arguments: tc.arguments
|
|
59
|
+
}
|
|
60
|
+
}));
|
|
61
|
+
return { role: 'assistant', content: null, tool_calls: calls };
|
|
62
|
+
}
|
|
63
|
+
function appendToolOutput(base, toolCallId, name, content) {
|
|
64
|
+
const outputs = Array.isArray(base.tool_outputs) ? base.tool_outputs : [];
|
|
65
|
+
outputs.push({ tool_call_id: toolCallId, name, content });
|
|
66
|
+
base.tool_outputs = outputs;
|
|
67
|
+
}
|
|
68
|
+
function buildToolMessagesFromOutputs(base, allowIds) {
|
|
69
|
+
const outputs = Array.isArray(base.tool_outputs) ? base.tool_outputs : [];
|
|
70
|
+
const out = [];
|
|
71
|
+
for (const entry of outputs) {
|
|
72
|
+
if (!entry || typeof entry !== 'object')
|
|
73
|
+
continue;
|
|
74
|
+
const toolCallId = typeof entry.tool_call_id === 'string' ? String(entry.tool_call_id) : '';
|
|
75
|
+
if (!toolCallId || !allowIds.has(toolCallId))
|
|
76
|
+
continue;
|
|
77
|
+
const name = typeof entry.name === 'string' && String(entry.name).trim()
|
|
78
|
+
? String(entry.name).trim()
|
|
79
|
+
: 'tool';
|
|
80
|
+
const content = typeof entry.content === 'string' ? String(entry.content) : JSON.stringify(entry.content ?? {});
|
|
81
|
+
out.push({ role: 'tool', tool_call_id: toolCallId, name, content });
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
function stripToolOutputs(base) {
|
|
86
|
+
try {
|
|
87
|
+
delete base.tool_outputs;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
/* ignore */
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function replaceJsonObjectInPlace(target, next) {
|
|
94
|
+
try {
|
|
95
|
+
for (const key of Object.keys(target)) {
|
|
96
|
+
delete target[key];
|
|
97
|
+
}
|
|
98
|
+
for (const [k, v] of Object.entries(next)) {
|
|
99
|
+
target[k] = v;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// ignore
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function filterOutExecutedToolCalls(chatResponse, executedIds) {
|
|
107
|
+
const choices = getArray(chatResponse.choices);
|
|
108
|
+
for (const choice of choices) {
|
|
109
|
+
const choiceObj = asObject(choice);
|
|
110
|
+
if (!choiceObj)
|
|
111
|
+
continue;
|
|
112
|
+
const message = asObject(choiceObj.message);
|
|
113
|
+
if (!message)
|
|
114
|
+
continue;
|
|
115
|
+
const toolCalls = getArray(message.tool_calls);
|
|
116
|
+
if (!toolCalls.length)
|
|
117
|
+
continue;
|
|
118
|
+
const next = toolCalls.filter((tc) => {
|
|
119
|
+
if (!tc || typeof tc !== 'object' || Array.isArray(tc))
|
|
120
|
+
return true;
|
|
121
|
+
const id = typeof tc.id === 'string' ? String(tc.id).trim() : '';
|
|
122
|
+
if (!id)
|
|
123
|
+
return true;
|
|
124
|
+
return !executedIds.has(id);
|
|
125
|
+
});
|
|
126
|
+
message.tool_calls = next;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
13
129
|
export async function runServerSideToolEngine(options) {
|
|
14
130
|
const base = asObject(options.chatResponse);
|
|
15
131
|
if (!base) {
|
|
@@ -31,20 +147,133 @@ export async function runServerSideToolEngine(options) {
|
|
|
31
147
|
providerInvoker: typeof options.providerInvoker === 'function'
|
|
32
148
|
}
|
|
33
149
|
};
|
|
34
|
-
|
|
150
|
+
// Tool-call servertools: execute all executable servertool calls first, then decide:
|
|
151
|
+
// - if only servertools were present -> followup via reenter (existing behavior)
|
|
152
|
+
// - if mixed (servertool + client tools) -> persist servertool results, return remaining tool_calls to client
|
|
153
|
+
const executedToolCalls = [];
|
|
154
|
+
const executedIds = new Set();
|
|
155
|
+
const baseForExecution = cloneJson(base);
|
|
156
|
+
const executedFlowIds = [];
|
|
157
|
+
let lastExecution;
|
|
158
|
+
const attemptedToolCallsByMessage = [];
|
|
159
|
+
const choices = getArray(base.choices);
|
|
160
|
+
for (const choice of choices) {
|
|
161
|
+
const choiceObj = asObject(choice);
|
|
162
|
+
if (!choiceObj)
|
|
163
|
+
continue;
|
|
164
|
+
const message = asObject(choiceObj.message);
|
|
165
|
+
if (!message)
|
|
166
|
+
continue;
|
|
167
|
+
attemptedToolCallsByMessage.push(...extractToolCallsFromMessage(message));
|
|
168
|
+
}
|
|
169
|
+
for (const { parsed: toolCall } of attemptedToolCallsByMessage) {
|
|
35
170
|
const entry = getServerToolHandler(toolCall.name);
|
|
36
|
-
if (!entry || entry.trigger !== 'tool_call')
|
|
171
|
+
if (!entry || entry.trigger !== 'tool_call') {
|
|
37
172
|
continue;
|
|
38
|
-
|
|
39
|
-
const
|
|
173
|
+
}
|
|
174
|
+
const ctx = { ...contextBase, base: baseForExecution, toolCall };
|
|
175
|
+
let planned = null;
|
|
176
|
+
let lastErr;
|
|
177
|
+
for (let attempt = 1; attempt <= 2; attempt += 1) {
|
|
178
|
+
try {
|
|
179
|
+
planned = await runHandler(entry.handler, ctx);
|
|
180
|
+
lastErr = undefined;
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
lastErr = err;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
40
187
|
const result = planned ? await materializePlannedResult(planned, options) : null;
|
|
41
188
|
if (result) {
|
|
189
|
+
replaceJsonObjectInPlace(baseForExecution, cloneJson(result.chatResponse));
|
|
190
|
+
executedToolCalls.push(toolCall);
|
|
191
|
+
executedIds.add(toolCall.id);
|
|
192
|
+
if (result.execution?.flowId && typeof result.execution.flowId === 'string' && result.execution.flowId.trim()) {
|
|
193
|
+
executedFlowIds.push(result.execution.flowId.trim());
|
|
194
|
+
}
|
|
195
|
+
lastExecution = result.execution;
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (lastErr) {
|
|
199
|
+
// Handler failed: report tool error as a tool_output, but do not crash the whole pipeline.
|
|
200
|
+
const message = lastErr instanceof Error ? lastErr.message : String(lastErr ?? 'unknown');
|
|
201
|
+
appendToolOutput(baseForExecution, toolCall.id, toolCall.name, JSON.stringify({ ok: false, tool: toolCall.name, message, retryable: true }));
|
|
202
|
+
executedToolCalls.push(toolCall);
|
|
203
|
+
executedIds.add(toolCall.id);
|
|
204
|
+
executedFlowIds.push(`${toolCall.name}_error`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (executedToolCalls.length > 0) {
|
|
208
|
+
const clientResponse = cloneJson(base);
|
|
209
|
+
filterOutExecutedToolCalls(clientResponse, executedIds);
|
|
210
|
+
stripToolOutputs(clientResponse);
|
|
211
|
+
// Determine whether any non-executed tool_calls remain (client tools).
|
|
212
|
+
const remainingToolCalls = [];
|
|
213
|
+
const remainingChoices = getArray(clientResponse.choices);
|
|
214
|
+
for (const choice of remainingChoices) {
|
|
215
|
+
const choiceObj = asObject(choice);
|
|
216
|
+
if (!choiceObj)
|
|
217
|
+
continue;
|
|
218
|
+
const message = asObject(choiceObj.message);
|
|
219
|
+
if (!message)
|
|
220
|
+
continue;
|
|
221
|
+
const toolCallsArr = getArray(message.tool_calls);
|
|
222
|
+
for (const tc of toolCallsArr) {
|
|
223
|
+
if (!tc || typeof tc !== 'object' || Array.isArray(tc))
|
|
224
|
+
continue;
|
|
225
|
+
const id = typeof tc.id === 'string' ? String(tc.id).trim() : '';
|
|
226
|
+
if (id)
|
|
227
|
+
remainingToolCalls.push(id);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (remainingToolCalls.length > 0) {
|
|
231
|
+
const sessionId = options.adapterContext && typeof options.adapterContext.sessionId === 'string'
|
|
232
|
+
? String(options.adapterContext.sessionId).trim()
|
|
233
|
+
: '';
|
|
234
|
+
const allowIds = new Set(executedToolCalls.map((t) => t.id));
|
|
235
|
+
const injectionMessages = [
|
|
236
|
+
buildAssistantToolCallMessage(executedToolCalls),
|
|
237
|
+
...buildToolMessagesFromOutputs(baseForExecution, allowIds)
|
|
238
|
+
];
|
|
42
239
|
return {
|
|
43
240
|
mode: 'tool_flow',
|
|
44
|
-
finalChatResponse:
|
|
45
|
-
execution:
|
|
241
|
+
finalChatResponse: clientResponse,
|
|
242
|
+
execution: { flowId: 'servertool_mixed' },
|
|
243
|
+
...(sessionId && injectionMessages.length
|
|
244
|
+
? {
|
|
245
|
+
pendingInjection: {
|
|
246
|
+
sessionId,
|
|
247
|
+
afterToolCallIds: remainingToolCalls,
|
|
248
|
+
messages: injectionMessages
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
: {})
|
|
46
252
|
};
|
|
47
253
|
}
|
|
254
|
+
// Servertool-only: keep tool_outputs and ask orchestration layer to followup/reenter.
|
|
255
|
+
const genericFollowup = {
|
|
256
|
+
requestIdSuffix: ':servertool_followup',
|
|
257
|
+
injection: {
|
|
258
|
+
ops: [
|
|
259
|
+
{ op: 'append_assistant_message', required: true },
|
|
260
|
+
{ op: 'append_tool_messages_from_tool_outputs', required: true }
|
|
261
|
+
]
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
const followup = executedToolCalls.length === 1 && lastExecution?.followup ? lastExecution.followup : genericFollowup;
|
|
265
|
+
const flowId = executedToolCalls.length === 1
|
|
266
|
+
? (lastExecution?.flowId ?? executedFlowIds[0] ?? executedToolCalls[0].name)
|
|
267
|
+
: 'servertool_multi';
|
|
268
|
+
return {
|
|
269
|
+
mode: 'tool_flow',
|
|
270
|
+
finalChatResponse: baseForExecution,
|
|
271
|
+
execution: {
|
|
272
|
+
...(lastExecution && executedToolCalls.length === 1 ? lastExecution : { flowId }),
|
|
273
|
+
flowId,
|
|
274
|
+
followup
|
|
275
|
+
}
|
|
276
|
+
};
|
|
48
277
|
}
|
|
49
278
|
for (const entry of listAutoServerToolHandlers()) {
|
|
50
279
|
const planned = await runHandler(entry.handler, contextBase);
|
|
@@ -50,6 +50,8 @@ export interface ServerSideToolEngineOptions {
|
|
|
50
50
|
}>;
|
|
51
51
|
}
|
|
52
52
|
export type ServerToolFollowupInjectionOp = {
|
|
53
|
+
op: 'preserve_tools';
|
|
54
|
+
} | {
|
|
53
55
|
op: 'append_assistant_message';
|
|
54
56
|
required?: boolean;
|
|
55
57
|
} | {
|
|
@@ -155,6 +157,17 @@ export interface ServerSideToolEngineResult {
|
|
|
155
157
|
mode: 'passthrough' | 'tool_flow';
|
|
156
158
|
finalChatResponse: JsonObject;
|
|
157
159
|
execution?: ServerToolExecution;
|
|
160
|
+
/**
|
|
161
|
+
* When present, indicates a "mixed tools" flow:
|
|
162
|
+
* - servertools were executed and their tool results are persisted
|
|
163
|
+
* - remaining (non-servertool) tool_calls must be returned to client
|
|
164
|
+
* - on next request, servertool results will be injected after client tool results
|
|
165
|
+
*/
|
|
166
|
+
pendingInjection?: {
|
|
167
|
+
sessionId: string;
|
|
168
|
+
afterToolCallIds: string[];
|
|
169
|
+
messages: JsonObject[];
|
|
170
|
+
};
|
|
158
171
|
}
|
|
159
172
|
/**
|
|
160
173
|
* ServerToolHandlerContext:单个工具 handler 的上下文入参。
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* 提供纯函数,将Responses响应数据转换为SSE事件对象,不处理流写入
|
|
4
4
|
*/
|
|
5
5
|
import { TimeUtils, StringUtils } from '../../shared/utils.js';
|
|
6
|
-
const TEXT_CHUNK_BOUNDARY = /[\n\r\t
|
|
6
|
+
const TEXT_CHUNK_BOUNDARY = /[\n\r\t,。、“”‘’!?,.\-:\u3000\s]/;
|
|
7
7
|
function cloneRegex(source) {
|
|
8
8
|
return new RegExp(source.source, source.flags);
|
|
9
9
|
}
|
|
@@ -27,13 +27,13 @@ export function toSSETextStream(objectEventStream) {
|
|
|
27
27
|
try {
|
|
28
28
|
out.write('data: {"error":"chat sse serialization failed"}\n\n');
|
|
29
29
|
}
|
|
30
|
-
catch { }
|
|
30
|
+
catch { /* ignore */ }
|
|
31
31
|
}
|
|
32
32
|
finally {
|
|
33
33
|
try {
|
|
34
34
|
out.end();
|
|
35
35
|
}
|
|
36
|
-
catch { }
|
|
36
|
+
catch { /* ignore */ }
|
|
37
37
|
}
|
|
38
38
|
})();
|
|
39
39
|
return out;
|
|
@@ -306,7 +306,7 @@ export const LOG_LEVELS = {
|
|
|
306
306
|
// 正则表达式常量
|
|
307
307
|
export const REGEX_PATTERNS = {
|
|
308
308
|
// 文本分词边界
|
|
309
|
-
WORD_BOUNDARY: /[\n\r\t,。、"''
|
|
309
|
+
WORD_BOUNDARY: /[\n\r\t,。、"''!?,.\-:\u3000\s]/,
|
|
310
310
|
SENTENCE_BOUNDARY: /[。!?.!?]/,
|
|
311
311
|
PARAGRAPH_BOUNDARY: /[\n\r]{2,}/,
|
|
312
312
|
// JSON验证
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
+
import { DEFAULT_ANTHROPIC_CONVERSION_CONFIG } from '../types/index.js';
|
|
1
2
|
import type { AnthropicMessageResponse, SseToAnthropicJsonOptions } from '../types/index.js';
|
|
3
|
+
type AnthropicSseToJsonConverterConfig = {
|
|
4
|
+
enableEventValidation: boolean;
|
|
5
|
+
strictMode: boolean;
|
|
6
|
+
} & typeof DEFAULT_ANTHROPIC_CONVERSION_CONFIG;
|
|
2
7
|
export declare class AnthropicSseToJsonConverter {
|
|
3
8
|
private config;
|
|
4
9
|
private contexts;
|
|
5
|
-
constructor(config?: Partial<
|
|
10
|
+
constructor(config?: Partial<AnthropicSseToJsonConverterConfig>);
|
|
6
11
|
convertSseToJson(sseStream: AsyncIterable<string | Buffer>, options: SseToAnthropicJsonOptions): Promise<AnthropicMessageResponse>;
|
|
7
12
|
private createContext;
|
|
8
13
|
private chunkStrings;
|
|
9
14
|
private updateStats;
|
|
10
15
|
private wrapError;
|
|
11
16
|
}
|
|
17
|
+
export {};
|
|
@@ -825,6 +825,22 @@ export class ResponsesResponseBuilder {
|
|
|
825
825
|
}
|
|
826
826
|
return { success: true, response: this.response };
|
|
827
827
|
}
|
|
828
|
+
// 进一步容错:少数上游会在输出流结束前没有发送 output_item.done/response.completed/response.done,
|
|
829
|
+
// 但已发送 output_item.added/content_part.* 等事件。此时 outputItemBuilders 中已存在内容,
|
|
830
|
+
// 直接按当前聚合结果构建 output 并视为 completed,避免 SSE_TO_JSON_ERROR: Building not completed。
|
|
831
|
+
if (this.outputItemBuilders.size > 0) {
|
|
832
|
+
this.response.status = 'completed';
|
|
833
|
+
try {
|
|
834
|
+
const cur = this.response.output;
|
|
835
|
+
if (!Array.isArray(cur) || cur.length === 0) {
|
|
836
|
+
this.response.output = this.buildOutputItems();
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
catch {
|
|
840
|
+
this.response.output = this.buildOutputItems();
|
|
841
|
+
}
|
|
842
|
+
return { success: true, response: this.response };
|
|
843
|
+
}
|
|
828
844
|
}
|
|
829
845
|
catch { /* ignore */ }
|
|
830
846
|
return { success: false, error: new Error('Building not completed') };
|
|
@@ -10,7 +10,7 @@ import type { ResponsesResponse, SseToResponsesJsonContext, SseToResponsesJsonOp
|
|
|
10
10
|
export declare class ResponsesSseToJsonConverterRefactored {
|
|
11
11
|
private config;
|
|
12
12
|
private contexts;
|
|
13
|
-
constructor(config?: Partial<
|
|
13
|
+
constructor(config?: Partial<ResponsesSseToJsonConverterRefactored['config']>);
|
|
14
14
|
/**
|
|
15
15
|
* 将SSE流转换为Responses响应
|
|
16
16
|
*/
|
|
@@ -147,7 +147,7 @@ export function captureApplyPatchExecutionFailuresFromProcessedRequest(processed
|
|
|
147
147
|
entryEndpoint,
|
|
148
148
|
providerKey,
|
|
149
149
|
model,
|
|
150
|
-
source: '
|
|
150
|
+
source: 'chat_process.req.stage4.tool_governance',
|
|
151
151
|
meta: { applyPatchToolMode: resolvedMode },
|
|
152
152
|
toolCallId,
|
|
153
153
|
toolCallArgs
|
|
@@ -90,6 +90,10 @@ export function normalizeExecCommandArgs(args) {
|
|
|
90
90
|
const tty = asBoolean(base.tty);
|
|
91
91
|
if (tty !== undefined)
|
|
92
92
|
normalized.tty = tty;
|
|
93
|
+
const timeoutMs = asFiniteNumber(base.timeout_ms) ??
|
|
94
|
+
asFiniteNumber(base.timeoutMs);
|
|
95
|
+
if (timeoutMs !== undefined)
|
|
96
|
+
normalized.timeout_ms = timeoutMs;
|
|
93
97
|
const shell = asNonEmptyString(base.shell);
|
|
94
98
|
if (shell)
|
|
95
99
|
normalized.shell = shell;
|
|
@@ -79,7 +79,7 @@ function redactSecrets(text) {
|
|
|
79
79
|
next = next.replace(/\bcr_[A-Za-z0-9]{20,}\b/g, 'cr_<REDACTED>');
|
|
80
80
|
next = next.replace(/\bAIza[0-9A-Za-z_-]{20,}\b/g, 'AIza<REDACTED>');
|
|
81
81
|
// Authorization headers.
|
|
82
|
-
next = next.replace(/(authorization
|
|
82
|
+
next = next.replace(/(authorization"?\s*:\s*"?bearer\s+)[^"]+/gi, '$1<REDACTED>');
|
|
83
83
|
next = next.replace(/(authorization:\s*bearer\s+)[^\s]+/gi, '$1<REDACTED>');
|
|
84
84
|
// Shell exports of *_KEY/*_TOKEN/*_SECRET.
|
|
85
85
|
next = next.replace(/(\bexport\s+[A-Z0-9_]*(?:KEY|TOKEN|SECRET)[A-Z0-9_]*=)[^\s'"]+/gi, '$1<REDACTED>');
|
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsonstudio/llms",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1354",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"scripts": {
|
|
9
|
-
"build": "node scripts/clean-dist.mjs && node scripts/bump-version.mjs && tsc -p tsconfig.json && node scripts/tools/copy-compat-profiles.mjs",
|
|
10
|
-
"build:dev": "node scripts/clean-dist.mjs && node scripts/bump-version.mjs && tsc -p tsconfig.json && node scripts/tools/copy-compat-profiles.mjs",
|
|
9
|
+
"build": "node scripts/clean-dist.mjs && node scripts/bump-version.mjs && node ./node_modules/typescript/bin/tsc -p tsconfig.json && node scripts/tools/copy-compat-profiles.mjs",
|
|
10
|
+
"build:dev": "node scripts/clean-dist.mjs && node scripts/bump-version.mjs && node ./node_modules/typescript/bin/tsc -p tsconfig.json && node scripts/tools/copy-compat-profiles.mjs",
|
|
11
|
+
"build:ci": "node scripts/clean-dist.mjs && node ./node_modules/typescript/bin/tsc -p tsconfig.json && node scripts/tools/copy-compat-profiles.mjs",
|
|
12
|
+
"build:coverage": "node scripts/clean-dist.mjs && node ./node_modules/typescript/bin/tsc -p tsconfig.coverage.json && node scripts/tools/copy-compat-profiles.mjs",
|
|
11
13
|
"lint": "eslint --no-eslintrc -c .eslintrc.json src --ext .ts --no-cache",
|
|
12
14
|
"lint:fix": "eslint --no-eslintrc -c .eslintrc.json src --ext .ts --no-cache --fix",
|
|
13
15
|
"postbuild": "node scripts/tests/run-matrix-ci.mjs",
|
|
@@ -25,8 +27,10 @@
|
|
|
25
27
|
"test:looprt:gemini": "npm run build && node scripts/tests/loop-rt-gemini.mjs",
|
|
26
28
|
"replay:responses:chat-sse": "node scripts/exp3-responses-sse-to-chat-sse.mjs",
|
|
27
29
|
"replay:responses:loop": "node scripts/exp4-responses-sse-loop.mjs",
|
|
28
|
-
"test:virtual-router": "npm run build:dev && node scripts/tests/virtual-router-prefer-autoclear.mjs && node scripts/tests/virtual-router-dry-run.mjs && node scripts/tests/virtual-router-context.mjs",
|
|
29
|
-
"test:virtual-router-health": "npm run build:dev && node scripts/tests/virtual-router-health.mjs"
|
|
30
|
+
"test:virtual-router": "npm run build:dev && node scripts/tests/virtual-router-prefer-autoclear.mjs && node scripts/tests/virtual-router-dry-run.mjs && node scripts/tests/virtual-router-context.mjs && node scripts/tests/virtual-router-antigravity-alias-pin.mjs",
|
|
31
|
+
"test:virtual-router-health": "npm run build:dev && node scripts/tests/virtual-router-health.mjs",
|
|
32
|
+
"test:golden": "npm run build:ci && node scripts/tests/chat-golden-roundtrip.mjs && node scripts/tests/anthropic-golden-roundtrip.mjs",
|
|
33
|
+
"test:coverage": "node scripts/run-ci-coverage.mjs"
|
|
30
34
|
},
|
|
31
35
|
"dependencies": {
|
|
32
36
|
"ajv": "^8.17.1",
|
|
@@ -55,6 +59,7 @@
|
|
|
55
59
|
"@types/node": "^20.11.25",
|
|
56
60
|
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
|
57
61
|
"@typescript-eslint/parser": "^6.7.4",
|
|
62
|
+
"c8": "^10.1.3",
|
|
58
63
|
"eslint": "^8.50.0",
|
|
59
64
|
"typescript": "^5.4.0"
|
|
60
65
|
},
|