@jsonstudio/llms 0.6.938 → 0.6.1164
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/conversion/hub/operation-table/operation-table-runner.d.ts +18 -0
- package/dist/conversion/hub/operation-table/operation-table-runner.js +158 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.d.ts +8 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +303 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.d.ts +8 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.js +413 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.d.ts +7 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +841 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.d.ts +21 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +535 -0
- package/dist/conversion/hub/ops/operations.d.ts +19 -0
- package/dist/conversion/hub/ops/operations.js +126 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +9 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +533 -24
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +6 -0
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +6 -3
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +11 -0
- package/dist/conversion/hub/policy/policy-engine.js +41 -9
- package/dist/conversion/hub/policy/protocol-spec.d.ts +25 -0
- package/dist/conversion/hub/policy/protocol-spec.js +73 -23
- package/dist/conversion/hub/process/chat-process.js +252 -41
- package/dist/conversion/hub/response/provider-response.js +175 -2
- package/dist/conversion/hub/response/response-runtime.js +1 -1
- package/dist/conversion/hub/semantic-mappers/anthropic-mapper.d.ts +1 -8
- package/dist/conversion/hub/semantic-mappers/anthropic-mapper.js +1 -365
- package/dist/conversion/hub/semantic-mappers/chat-mapper.d.ts +1 -8
- package/dist/conversion/hub/semantic-mappers/chat-mapper.js +1 -436
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.d.ts +1 -7
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +1 -894
- package/dist/conversion/hub/semantic-mappers/responses-mapper.d.ts +1 -21
- package/dist/conversion/hub/semantic-mappers/responses-mapper.js +1 -593
- package/dist/conversion/hub/tool-surface/tool-surface-engine.d.ts +18 -0
- package/dist/conversion/hub/tool-surface/tool-surface-engine.js +571 -0
- package/dist/conversion/responses/responses-openai-bridge.js +14 -2
- package/dist/conversion/shared/bridge-message-utils.js +2 -8
- package/dist/conversion/shared/bridge-policies.js +5 -105
- package/dist/conversion/shared/gemini-tool-utils.js +121 -4
- package/dist/conversion/shared/protocol-field-allowlists.d.ts +7 -0
- package/dist/conversion/shared/protocol-field-allowlists.js +145 -0
- package/dist/conversion/shared/reasoning-tool-normalizer.js +4 -2
- package/dist/conversion/shared/snapshot-hooks.js +166 -3
- package/dist/conversion/shared/text-markup-normalizer.d.ts +2 -0
- package/dist/conversion/shared/text-markup-normalizer.js +345 -9
- package/dist/conversion/shared/thought-signature-validator.d.ts +52 -0
- package/dist/conversion/shared/thought-signature-validator.js +170 -0
- package/dist/conversion/shared/tool-argument-repairer.d.ts +39 -0
- package/dist/conversion/shared/tool-argument-repairer.js +56 -0
- package/dist/conversion/shared/tool-call-id-manager.d.ts +113 -0
- package/dist/conversion/shared/tool-call-id-manager.js +231 -0
- package/dist/conversion/shared/tool-canonicalizer.js +2 -11
- package/dist/router/virtual-router/bootstrap.js +54 -5
- package/dist/router/virtual-router/engine-selection.js +132 -42
- package/dist/router/virtual-router/engine.d.ts +3 -0
- package/dist/router/virtual-router/engine.js +142 -33
- package/dist/router/virtual-router/health-weighted.d.ts +25 -0
- package/dist/router/virtual-router/health-weighted.js +63 -0
- package/dist/router/virtual-router/load-balancer.d.ts +2 -0
- package/dist/router/virtual-router/load-balancer.js +45 -16
- package/dist/router/virtual-router/routing-instructions.js +17 -1
- package/dist/router/virtual-router/sticky-session-store.js +136 -24
- package/dist/router/virtual-router/stop-message-file-resolver.d.ts +1 -0
- package/dist/router/virtual-router/stop-message-file-resolver.js +74 -0
- package/dist/router/virtual-router/stop-message-state-sync.d.ts +15 -0
- package/dist/router/virtual-router/stop-message-state-sync.js +57 -0
- package/dist/router/virtual-router/types.d.ts +70 -0
- package/dist/servertool/clock/config.d.ts +7 -0
- package/dist/servertool/clock/config.js +27 -0
- package/dist/servertool/clock/daemon.d.ts +3 -0
- package/dist/servertool/clock/daemon.js +79 -0
- package/dist/servertool/clock/io.d.ts +2 -0
- package/dist/servertool/clock/io.js +13 -0
- package/dist/servertool/clock/paths.d.ts +4 -0
- package/dist/servertool/clock/paths.js +25 -0
- package/dist/servertool/clock/session-store.d.ts +3 -0
- package/dist/servertool/clock/session-store.js +56 -0
- package/dist/servertool/clock/state.d.ts +5 -0
- package/dist/servertool/clock/state.js +62 -0
- package/dist/servertool/clock/task-store.d.ts +5 -0
- package/dist/servertool/clock/task-store.js +4 -0
- package/dist/servertool/clock/tasks.d.ts +17 -0
- package/dist/servertool/clock/tasks.js +221 -0
- package/dist/servertool/clock/types.d.ts +36 -0
- package/dist/servertool/clock/types.js +1 -0
- package/dist/servertool/engine.d.ts +2 -0
- package/dist/servertool/engine.js +164 -8
- package/dist/servertool/followup-shadow.d.ts +16 -0
- package/dist/servertool/followup-shadow.js +145 -0
- package/dist/servertool/handlers/apply-patch-guard.js +1 -265
- package/dist/servertool/handlers/clock-auto.d.ts +1 -0
- package/dist/servertool/handlers/clock-auto.js +160 -0
- package/dist/servertool/handlers/clock.d.ts +1 -0
- package/dist/servertool/handlers/clock.js +197 -0
- package/dist/servertool/handlers/exec-command-guard.js +7 -555
- package/dist/servertool/handlers/followup-request-builder.d.ts +15 -7
- package/dist/servertool/handlers/followup-request-builder.js +248 -28
- package/dist/servertool/handlers/gemini-empty-reply-continue.js +62 -169
- package/dist/servertool/handlers/iflow-model-error-retry.js +18 -28
- package/dist/servertool/handlers/recursive-detection-guard.d.ts +1 -0
- package/dist/servertool/handlers/recursive-detection-guard.js +333 -0
- package/dist/servertool/handlers/stop-message-auto.js +47 -175
- package/dist/servertool/handlers/vision.d.ts +7 -1
- package/dist/servertool/handlers/vision.js +61 -117
- package/dist/servertool/handlers/web-search.d.ts +7 -1
- package/dist/servertool/handlers/web-search.js +122 -105
- package/dist/servertool/reenter-backend.d.ts +23 -0
- package/dist/servertool/reenter-backend.js +18 -0
- package/dist/servertool/server-side-tools.d.ts +3 -2
- package/dist/servertool/server-side-tools.js +64 -10
- package/dist/servertool/types.d.ts +92 -3
- package/dist/sse/json-to-sse/event-generators/responses.js +3 -21
- package/dist/sse/shared/serializers/responses-event-serializer.d.ts +8 -0
- package/dist/sse/shared/serializers/responses-event-serializer.js +19 -0
- package/dist/sse/shared/writer.js +24 -7
- package/dist/tools/apply-patch/execution-capturer.js +3 -1
- package/dist/tools/apply-patch/json/parse-loose.d.ts +3 -0
- package/dist/tools/apply-patch/json/parse-loose.js +139 -0
- package/dist/tools/apply-patch/patch-text/context-diff.d.ts +1 -0
- package/dist/tools/apply-patch/patch-text/context-diff.js +173 -0
- package/dist/tools/apply-patch/patch-text/git-diff.d.ts +1 -0
- package/dist/tools/apply-patch/patch-text/git-diff.js +138 -0
- package/dist/tools/apply-patch/patch-text/looks-like-patch.d.ts +1 -0
- package/dist/tools/apply-patch/patch-text/looks-like-patch.js +13 -0
- package/dist/tools/apply-patch/patch-text/normalize.d.ts +3 -0
- package/dist/tools/apply-patch/patch-text/normalize.js +262 -0
- package/dist/tools/apply-patch/structured/coercion.d.ts +3 -0
- package/dist/tools/apply-patch/structured/coercion.js +82 -0
- package/dist/tools/apply-patch/validation/shared.d.ts +3 -0
- package/dist/tools/apply-patch/validation/shared.js +6 -0
- package/dist/tools/apply-patch/validator.d.ts +2 -2
- package/dist/tools/apply-patch/validator.js +6 -556
- package/package.json +1 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { registerServerToolHandler } from '../registry.js';
|
|
2
2
|
import { cloneJson, extractTextFromChatLike } from '../server-side-tools.js';
|
|
3
|
-
import {
|
|
3
|
+
import { extractCapturedChatSeed } from './followup-request-builder.js';
|
|
4
|
+
import { reenterServerToolBackend } from '../reenter-backend.js';
|
|
4
5
|
const FLOW_ID = 'vision_flow';
|
|
5
6
|
const handler = async (ctx) => {
|
|
6
|
-
if (!ctx.
|
|
7
|
+
if (!ctx.capabilities.reenterPipeline) {
|
|
7
8
|
return null;
|
|
8
9
|
}
|
|
9
10
|
if (!shouldRunVisionFlow(ctx)) {
|
|
@@ -17,43 +18,70 @@ const handler = async (ctx) => {
|
|
|
17
18
|
if (!analysisPayload) {
|
|
18
19
|
return null;
|
|
19
20
|
}
|
|
20
|
-
const
|
|
21
|
+
const backend = {
|
|
22
|
+
kind: 'vision_analysis',
|
|
23
|
+
requestIdSuffix: ':vision',
|
|
21
24
|
entryEndpoint: '/v1/chat/completions',
|
|
22
|
-
|
|
23
|
-
body: analysisPayload,
|
|
24
|
-
metadata: {
|
|
25
|
-
routeHint: 'vision',
|
|
26
|
-
serverToolFollowup: true,
|
|
27
|
-
stream: false
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
const visionBody = visionResponse.body && typeof visionResponse.body === 'object'
|
|
31
|
-
? visionResponse.body
|
|
32
|
-
: null;
|
|
33
|
-
if (!visionBody) {
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
const visionSummary = extractTextFromChatLike(visionBody);
|
|
37
|
-
if (!visionSummary) {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
const followupPayload = buildVisionFollowupPayload(captured, visionSummary, ctx.options.entryEndpoint || ctx.adapterContext?.entryEndpoint || '/v1/chat/completions');
|
|
41
|
-
if (!followupPayload) {
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
const execution = {
|
|
45
|
-
flowId: FLOW_ID,
|
|
46
|
-
followup: {
|
|
47
|
-
requestIdSuffix: ':vision_followup',
|
|
48
|
-
payload: followupPayload
|
|
49
|
-
}
|
|
25
|
+
payload: analysisPayload
|
|
50
26
|
};
|
|
51
27
|
return {
|
|
52
|
-
|
|
53
|
-
|
|
28
|
+
flowId: FLOW_ID,
|
|
29
|
+
backend,
|
|
30
|
+
finalize: async ({ backendResult }) => {
|
|
31
|
+
if (!backendResult || backendResult.kind !== 'vision_analysis') {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const body = backendResult.response.body && typeof backendResult.response.body === 'object'
|
|
35
|
+
? backendResult.response.body
|
|
36
|
+
: null;
|
|
37
|
+
if (!body) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const visionSummary = extractTextFromChatLike(body);
|
|
41
|
+
if (!visionSummary) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
// Fail-closed: if we cannot build followup seed, do not intercept.
|
|
45
|
+
const seed = extractCapturedChatSeed(captured);
|
|
46
|
+
if (!seed) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
chatResponse: ctx.base,
|
|
51
|
+
execution: {
|
|
52
|
+
flowId: FLOW_ID,
|
|
53
|
+
followup: {
|
|
54
|
+
requestIdSuffix: ':vision_followup',
|
|
55
|
+
entryEndpoint: ctx.entryEndpoint,
|
|
56
|
+
injection: {
|
|
57
|
+
ops: [
|
|
58
|
+
{ op: 'inject_vision_summary', summary: visionSummary },
|
|
59
|
+
{ op: 'drop_tool_by_name', name: 'vision' }
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
54
66
|
};
|
|
55
67
|
};
|
|
56
68
|
registerServerToolHandler('vision_auto', handler, { trigger: 'auto' });
|
|
69
|
+
export async function executeVisionBackendPlan(args) {
|
|
70
|
+
const plan = args.plan;
|
|
71
|
+
const options = args.options;
|
|
72
|
+
if (!options.reenterPipeline) {
|
|
73
|
+
return { kind: 'vision_analysis', response: {} };
|
|
74
|
+
}
|
|
75
|
+
const response = await reenterServerToolBackend({
|
|
76
|
+
reenterPipeline: options.reenterPipeline,
|
|
77
|
+
entryEndpoint: plan.entryEndpoint,
|
|
78
|
+
requestId: `${options.requestId}${plan.requestIdSuffix}`,
|
|
79
|
+
body: plan.payload,
|
|
80
|
+
providerProtocol: 'openai-chat',
|
|
81
|
+
routeHint: 'vision'
|
|
82
|
+
});
|
|
83
|
+
return { kind: 'vision_analysis', response };
|
|
84
|
+
}
|
|
57
85
|
function shouldRunVisionFlow(ctx) {
|
|
58
86
|
const record = ctx.adapterContext;
|
|
59
87
|
const followupFlag = record.serverToolFollowup === true || record.serverToolFollowup === 'true';
|
|
@@ -194,87 +222,3 @@ function buildVisionUserMessage(source) {
|
|
|
194
222
|
}
|
|
195
223
|
return message;
|
|
196
224
|
}
|
|
197
|
-
function buildVisionFollowupPayload(source, summary, entryEndpoint) {
|
|
198
|
-
const seed = extractCapturedChatSeed(source);
|
|
199
|
-
if (!seed) {
|
|
200
|
-
return null;
|
|
201
|
-
}
|
|
202
|
-
const messages = injectVisionSummary(seed.messages, summary);
|
|
203
|
-
const filteredTools = dropToolByFunctionName(seed.tools, 'vision');
|
|
204
|
-
return buildEntryAwareFollowupPayload({
|
|
205
|
-
entryEndpoint,
|
|
206
|
-
model: seed.model,
|
|
207
|
-
messages,
|
|
208
|
-
...(filteredTools ? { tools: filteredTools } : {}),
|
|
209
|
-
...(seed.parameters ? { parameters: seed.parameters } : {})
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
function injectVisionSummary(source, summary) {
|
|
213
|
-
const messages = Array.isArray(source) ? cloneJson(source) : [];
|
|
214
|
-
let injected = false;
|
|
215
|
-
for (const message of messages) {
|
|
216
|
-
if (!message || typeof message !== 'object')
|
|
217
|
-
continue;
|
|
218
|
-
const content = message.content;
|
|
219
|
-
if (!Array.isArray(content))
|
|
220
|
-
continue;
|
|
221
|
-
const nextParts = [];
|
|
222
|
-
let removed = false;
|
|
223
|
-
for (const part of content) {
|
|
224
|
-
if (part && typeof part === 'object') {
|
|
225
|
-
const typeValue = typeof part.type === 'string'
|
|
226
|
-
? part.type.toLowerCase()
|
|
227
|
-
: '';
|
|
228
|
-
if (typeValue.includes('image')) {
|
|
229
|
-
removed = true;
|
|
230
|
-
continue;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
nextParts.push(part);
|
|
234
|
-
}
|
|
235
|
-
if (removed) {
|
|
236
|
-
nextParts.push({
|
|
237
|
-
type: 'text',
|
|
238
|
-
text: `[Vision] ${summary}`
|
|
239
|
-
});
|
|
240
|
-
message.content = nextParts;
|
|
241
|
-
injected = true;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
if (!injected) {
|
|
245
|
-
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
246
|
-
const msg = messages[i];
|
|
247
|
-
if (!msg || typeof msg !== 'object')
|
|
248
|
-
continue;
|
|
249
|
-
const role = typeof msg.role === 'string'
|
|
250
|
-
? msg.role.toLowerCase()
|
|
251
|
-
: '';
|
|
252
|
-
if (role !== 'user')
|
|
253
|
-
continue;
|
|
254
|
-
const content = msg.content;
|
|
255
|
-
if (Array.isArray(content)) {
|
|
256
|
-
content.push({
|
|
257
|
-
type: 'text',
|
|
258
|
-
text: `[Vision] ${summary}`
|
|
259
|
-
});
|
|
260
|
-
injected = true;
|
|
261
|
-
break;
|
|
262
|
-
}
|
|
263
|
-
if (typeof content === 'string' && content.length) {
|
|
264
|
-
msg.content = `${content}\n[Vision] ${summary}`;
|
|
265
|
-
}
|
|
266
|
-
else {
|
|
267
|
-
msg.content = `[Vision] ${summary}`;
|
|
268
|
-
}
|
|
269
|
-
injected = true;
|
|
270
|
-
break;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
if (!injected) {
|
|
274
|
-
messages.push({
|
|
275
|
-
role: 'system',
|
|
276
|
-
content: `[Vision] ${summary}`
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
return messages;
|
|
280
|
-
}
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import type { ServerSideToolEngineOptions, ServerToolBackendPlan, ServerToolBackendResult } from '../types.js';
|
|
2
|
+
export declare function executeWebSearchBackendPlan(args: {
|
|
3
|
+
plan: Extract<ServerToolBackendPlan, {
|
|
4
|
+
kind: 'web_search';
|
|
5
|
+
}>;
|
|
6
|
+
options: ServerSideToolEngineOptions;
|
|
7
|
+
}): Promise<ServerToolBackendResult>;
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { buildOpenAIChatFromGeminiResponse } from '../../conversion/codecs/gemini-openai-codec.js';
|
|
2
2
|
import { registerServerToolHandler } from '../registry.js';
|
|
3
3
|
import { cloneJson, extractTextFromChatLike } from '../server-side-tools.js';
|
|
4
|
-
import {
|
|
4
|
+
import { extractCapturedChatSeed } from './followup-request-builder.js';
|
|
5
|
+
import { reenterServerToolBackend } from '../reenter-backend.js';
|
|
5
6
|
const FLOW_ID = 'web_search_flow';
|
|
6
7
|
const handler = async (ctx) => {
|
|
7
8
|
const toolCall = ctx.toolCall;
|
|
8
9
|
if (!toolCall) {
|
|
9
10
|
return null;
|
|
10
11
|
}
|
|
11
|
-
if (!ctx.
|
|
12
|
+
if (!ctx.capabilities.providerInvoker && !ctx.capabilities.reenterPipeline) {
|
|
12
13
|
return null;
|
|
13
14
|
}
|
|
14
15
|
const webSearchConfig = getWebSearchConfig(ctx.adapterContext);
|
|
@@ -28,57 +29,61 @@ const handler = async (ctx) => {
|
|
|
28
29
|
return null;
|
|
29
30
|
}
|
|
30
31
|
const resultCount = normalizeResultCount(parsedArgs.count);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
});
|
|
42
|
-
if (backendResult.ok) {
|
|
43
|
-
chosenEngine = engine;
|
|
44
|
-
chosenResult = backendResult;
|
|
45
|
-
break;
|
|
46
|
-
}
|
|
47
|
-
lastFailure = { engine, result: backendResult };
|
|
48
|
-
}
|
|
49
|
-
if (!chosenEngine || !chosenResult) {
|
|
50
|
-
if (!lastFailure) {
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
chosenEngine = lastFailure.engine;
|
|
54
|
-
chosenResult = lastFailure.result;
|
|
55
|
-
}
|
|
56
|
-
const patched = injectWebSearchToolResult(ctx.base, toolCall, chosenEngine, query, chosenResult);
|
|
57
|
-
const followupPayload = buildWebSearchFollowupPayload(ctx.adapterContext, patched, ctx.options.entryEndpoint || ctx.adapterContext?.entryEndpoint || '/v1/chat/completions');
|
|
58
|
-
const execution = {
|
|
32
|
+
const recency = typeof parsedArgs.recency === 'string' && parsedArgs.recency.trim() ? parsedArgs.recency.trim() : undefined;
|
|
33
|
+
const backend = {
|
|
34
|
+
kind: 'web_search',
|
|
35
|
+
requestIdSuffix: ':web_search',
|
|
36
|
+
query,
|
|
37
|
+
...(recency ? { recency } : {}),
|
|
38
|
+
resultCount,
|
|
39
|
+
engines
|
|
40
|
+
};
|
|
41
|
+
return {
|
|
59
42
|
flowId: FLOW_ID,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
metadata: {
|
|
65
|
-
// keep minimal; servertool engine will inject the standard followup metadata defaults
|
|
66
|
-
serverToolFollowup: true
|
|
67
|
-
}
|
|
43
|
+
backend,
|
|
44
|
+
finalize: async ({ backendResult }) => {
|
|
45
|
+
if (!backendResult || backendResult.kind !== 'web_search') {
|
|
46
|
+
return null;
|
|
68
47
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
engineId: chosenEngine.id,
|
|
73
|
-
providerKey: chosenEngine.providerKey,
|
|
74
|
-
summary: chosenResult.summary
|
|
48
|
+
const chosen = backendResult.chosenEngine;
|
|
49
|
+
if (!chosen || !chosen.id || !chosen.providerKey) {
|
|
50
|
+
return null;
|
|
75
51
|
}
|
|
52
|
+
const patched = injectWebSearchToolResult(ctx.base, toolCall, { id: chosen.id, providerKey: chosen.providerKey }, query, backendResult.result);
|
|
53
|
+
const seed = extractCapturedChatSeed(ctx.adapterContext?.capturedChatRequest);
|
|
54
|
+
const assistantMessage = seed ? extractAssistantMessage(patched) : null;
|
|
55
|
+
const toolMessages = seed ? buildToolMessages(patched) : [];
|
|
56
|
+
const canFollowup = Boolean(seed && assistantMessage && toolMessages.length > 0);
|
|
57
|
+
return {
|
|
58
|
+
chatResponse: patched,
|
|
59
|
+
execution: {
|
|
60
|
+
flowId: FLOW_ID,
|
|
61
|
+
...(canFollowup
|
|
62
|
+
? {
|
|
63
|
+
followup: {
|
|
64
|
+
requestIdSuffix: ':web_search_followup',
|
|
65
|
+
entryEndpoint: ctx.entryEndpoint,
|
|
66
|
+
injection: {
|
|
67
|
+
ops: [
|
|
68
|
+
{ op: 'append_assistant_message' },
|
|
69
|
+
{ op: 'append_tool_messages_from_tool_outputs' },
|
|
70
|
+
{ op: 'drop_tool_by_name', name: 'web_search' }
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
: {}),
|
|
76
|
+
context: {
|
|
77
|
+
web_search: {
|
|
78
|
+
engineId: chosen.id,
|
|
79
|
+
providerKey: chosen.providerKey,
|
|
80
|
+
summary: backendResult.result.summary
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
76
85
|
}
|
|
77
86
|
};
|
|
78
|
-
return {
|
|
79
|
-
chatResponse: patched,
|
|
80
|
-
execution
|
|
81
|
-
};
|
|
82
87
|
};
|
|
83
88
|
registerServerToolHandler('web_search', handler);
|
|
84
89
|
function parseToolArguments(toolCall) {
|
|
@@ -220,19 +225,19 @@ function normalizeResultCount(value) {
|
|
|
220
225
|
return 10;
|
|
221
226
|
}
|
|
222
227
|
async function executeWebSearchBackend(args) {
|
|
223
|
-
const {
|
|
228
|
+
const { options, engine, query } = args;
|
|
224
229
|
const recency = typeof args.recency === 'string' && args.recency.trim() ? args.recency.trim() : undefined;
|
|
225
230
|
let summary = '';
|
|
226
231
|
let hits = [];
|
|
227
232
|
let ok = true;
|
|
228
233
|
try {
|
|
229
|
-
logServerToolWebSearch(engine,
|
|
230
|
-
const requestSuffix =
|
|
234
|
+
logServerToolWebSearch(engine, options.requestId, query);
|
|
235
|
+
const requestSuffix = args.requestSuffix;
|
|
231
236
|
// 对于 iFlow,直接通过 providerInvoker 调用 /chat/retrieve,
|
|
232
237
|
// 即使 reenterPipeline 可用,也不走 Chat 模型 + tools。
|
|
233
|
-
if (isIflowWebSearchEngine(engine) &&
|
|
238
|
+
if (isIflowWebSearchEngine(engine) && options.providerInvoker) {
|
|
234
239
|
const backendResult = await executeIflowWebSearchViaProvider({
|
|
235
|
-
|
|
240
|
+
options,
|
|
236
241
|
engine,
|
|
237
242
|
query,
|
|
238
243
|
recency,
|
|
@@ -243,17 +248,15 @@ async function executeWebSearchBackend(args) {
|
|
|
243
248
|
hits = backendResult.hits;
|
|
244
249
|
ok = backendResult.ok;
|
|
245
250
|
}
|
|
246
|
-
else if (
|
|
251
|
+
else if (options.reenterPipeline) {
|
|
247
252
|
const payload = buildWebSearchReenterPayload(engine, query, recency, args.resultCount);
|
|
248
|
-
const followup = await
|
|
253
|
+
const followup = await reenterServerToolBackend({
|
|
254
|
+
reenterPipeline: options.reenterPipeline,
|
|
249
255
|
entryEndpoint: '/v1/chat/completions',
|
|
250
|
-
requestId: `${
|
|
256
|
+
requestId: `${options.requestId}${requestSuffix}`,
|
|
251
257
|
body: payload,
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
serverToolFollowup: true,
|
|
255
|
-
stream: false
|
|
256
|
-
}
|
|
258
|
+
providerProtocol: 'openai-chat',
|
|
259
|
+
routeHint: 'web_search'
|
|
257
260
|
});
|
|
258
261
|
const body = followup.body && typeof followup.body === 'object' ? followup.body : null;
|
|
259
262
|
if (body) {
|
|
@@ -263,7 +266,7 @@ async function executeWebSearchBackend(args) {
|
|
|
263
266
|
try {
|
|
264
267
|
// eslint-disable-next-line no-console
|
|
265
268
|
console.log('\x1b[38;5;27m[server-tool][web_search][backend_debug]' +
|
|
266
|
-
` requestId=${
|
|
269
|
+
` requestId=${options.requestId}${requestSuffix} payload=${JSON.stringify(body).slice(0, 2000)}\x1b[0m`);
|
|
267
270
|
}
|
|
268
271
|
catch {
|
|
269
272
|
/* logging best-effort */
|
|
@@ -271,9 +274,9 @@ async function executeWebSearchBackend(args) {
|
|
|
271
274
|
}
|
|
272
275
|
}
|
|
273
276
|
}
|
|
274
|
-
else if (
|
|
277
|
+
else if (options.providerInvoker) {
|
|
275
278
|
summary = await executeWebSearchViaProvider({
|
|
276
|
-
|
|
279
|
+
options,
|
|
277
280
|
engine,
|
|
278
281
|
query,
|
|
279
282
|
recency,
|
|
@@ -305,7 +308,7 @@ async function executeWebSearchBackend(args) {
|
|
|
305
308
|
try {
|
|
306
309
|
const preview = summary.length > 120 ? `${summary.slice(0, 117)}...` : summary;
|
|
307
310
|
// eslint-disable-next-line no-console
|
|
308
|
-
console.log(`\x1b[38;5;27m[server-tool][web_search][result] requestId=${
|
|
311
|
+
console.log(`\x1b[38;5;27m[server-tool][web_search][result] requestId=${options.requestId} ` +
|
|
309
312
|
`engine=${engine.id} chars=${summary.length} preview=${JSON.stringify(preview)}\x1b[0m`);
|
|
310
313
|
}
|
|
311
314
|
catch {
|
|
@@ -320,6 +323,46 @@ async function executeWebSearchBackend(args) {
|
|
|
320
323
|
}
|
|
321
324
|
return { summary, hits: finalHits, ok };
|
|
322
325
|
}
|
|
326
|
+
export async function executeWebSearchBackendPlan(args) {
|
|
327
|
+
const plan = args.plan;
|
|
328
|
+
const options = args.options;
|
|
329
|
+
const engines = Array.isArray(plan.engines) ? plan.engines : [];
|
|
330
|
+
if (!engines.length) {
|
|
331
|
+
return { kind: 'web_search', result: { ok: false, summary: '', hits: [] } };
|
|
332
|
+
}
|
|
333
|
+
let chosenEngine;
|
|
334
|
+
let chosenResult;
|
|
335
|
+
let lastFailure;
|
|
336
|
+
for (const engine of engines) {
|
|
337
|
+
const requestSuffix = `${plan.requestIdSuffix}:${engine.id}`;
|
|
338
|
+
const backendResult = await executeWebSearchBackend({
|
|
339
|
+
options,
|
|
340
|
+
engine,
|
|
341
|
+
query: plan.query,
|
|
342
|
+
recency: plan.recency,
|
|
343
|
+
resultCount: plan.resultCount,
|
|
344
|
+
requestSuffix
|
|
345
|
+
});
|
|
346
|
+
if (backendResult.ok) {
|
|
347
|
+
chosenEngine = engine;
|
|
348
|
+
chosenResult = backendResult;
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
lastFailure = { engine, result: backendResult };
|
|
352
|
+
}
|
|
353
|
+
if (!chosenEngine || !chosenResult) {
|
|
354
|
+
if (!lastFailure) {
|
|
355
|
+
return { kind: 'web_search', result: { ok: false, summary: '', hits: [] } };
|
|
356
|
+
}
|
|
357
|
+
chosenEngine = lastFailure.engine;
|
|
358
|
+
chosenResult = lastFailure.result;
|
|
359
|
+
}
|
|
360
|
+
return {
|
|
361
|
+
kind: 'web_search',
|
|
362
|
+
chosenEngine: { id: chosenEngine.id, providerKey: chosenEngine.providerKey },
|
|
363
|
+
result: { ok: chosenResult.ok, summary: chosenResult.summary, hits: chosenResult.hits }
|
|
364
|
+
};
|
|
365
|
+
}
|
|
323
366
|
function buildWebSearchReenterPayload(engine, query, recency, resultCount) {
|
|
324
367
|
const systemPrompt = buildWebSearchSystemPrompt(resultCount);
|
|
325
368
|
const basePayload = {
|
|
@@ -350,8 +393,8 @@ function buildWebSearchReenterPayload(engine, query, recency, resultCount) {
|
|
|
350
393
|
};
|
|
351
394
|
}
|
|
352
395
|
async function executeWebSearchViaProvider(args) {
|
|
353
|
-
const {
|
|
354
|
-
if (!
|
|
396
|
+
const { options, engine, query, recency, count, requestSuffix } = args;
|
|
397
|
+
if (!options.providerInvoker) {
|
|
355
398
|
return '';
|
|
356
399
|
}
|
|
357
400
|
if (isGeminiWebSearchEngine(engine)) {
|
|
@@ -373,14 +416,14 @@ async function executeWebSearchViaProvider(args) {
|
|
|
373
416
|
}
|
|
374
417
|
]
|
|
375
418
|
};
|
|
376
|
-
const backend = await
|
|
419
|
+
const backend = await options.providerInvoker({
|
|
377
420
|
providerKey: engine.providerKey,
|
|
378
421
|
providerType: undefined,
|
|
379
422
|
modelId: engine.id,
|
|
380
423
|
providerProtocol: 'gemini-chat',
|
|
381
424
|
payload: geminiPayload,
|
|
382
425
|
entryEndpoint: '/v1/models/gemini:generateContent',
|
|
383
|
-
requestId: `${
|
|
426
|
+
requestId: `${options.requestId}${requestSuffix}`,
|
|
384
427
|
routeHint: 'web_search'
|
|
385
428
|
});
|
|
386
429
|
const providerResponse = backend.providerResponse && typeof backend.providerResponse === 'object'
|
|
@@ -412,14 +455,14 @@ async function executeWebSearchViaProvider(args) {
|
|
|
412
455
|
engine: engine.id
|
|
413
456
|
}
|
|
414
457
|
};
|
|
415
|
-
const backend = await
|
|
458
|
+
const backend = await options.providerInvoker({
|
|
416
459
|
providerKey: engine.providerKey,
|
|
417
460
|
providerType: undefined,
|
|
418
461
|
modelId: undefined,
|
|
419
|
-
providerProtocol:
|
|
462
|
+
providerProtocol: options.providerProtocol,
|
|
420
463
|
payload: backendPayload,
|
|
421
464
|
entryEndpoint: '/v1/chat/completions',
|
|
422
|
-
requestId: `${
|
|
465
|
+
requestId: `${options.requestId}${requestSuffix}`,
|
|
423
466
|
routeHint: 'web_search'
|
|
424
467
|
});
|
|
425
468
|
const providerResponse = backend.providerResponse && typeof backend.providerResponse === 'object'
|
|
@@ -431,8 +474,8 @@ async function executeWebSearchViaProvider(args) {
|
|
|
431
474
|
return extractTextFromChatLike(providerResponse);
|
|
432
475
|
}
|
|
433
476
|
async function executeIflowWebSearchViaProvider(args) {
|
|
434
|
-
const {
|
|
435
|
-
if (!
|
|
477
|
+
const { options, engine, query, count, requestSuffix } = args;
|
|
478
|
+
if (!options.providerInvoker) {
|
|
436
479
|
return {
|
|
437
480
|
summary: '',
|
|
438
481
|
hits: [],
|
|
@@ -457,8 +500,8 @@ async function executeIflowWebSearchViaProvider(args) {
|
|
|
457
500
|
};
|
|
458
501
|
let providerKey = engine.providerKey;
|
|
459
502
|
try {
|
|
460
|
-
const adapter =
|
|
461
|
-
?
|
|
503
|
+
const adapter = options.adapterContext && typeof options.adapterContext === 'object'
|
|
504
|
+
? options.adapterContext
|
|
462
505
|
: null;
|
|
463
506
|
const target = adapter && adapter.target && typeof adapter.target === 'object'
|
|
464
507
|
? adapter.target
|
|
@@ -481,14 +524,14 @@ async function executeIflowWebSearchViaProvider(args) {
|
|
|
481
524
|
routeName: 'web_search'
|
|
482
525
|
}
|
|
483
526
|
};
|
|
484
|
-
const backend = await
|
|
527
|
+
const backend = await options.providerInvoker({
|
|
485
528
|
providerKey,
|
|
486
529
|
providerType: undefined,
|
|
487
530
|
modelId: undefined,
|
|
488
|
-
providerProtocol:
|
|
531
|
+
providerProtocol: options.providerProtocol,
|
|
489
532
|
payload,
|
|
490
533
|
entryEndpoint: '/v1/chat/retrieve',
|
|
491
|
-
requestId: `${
|
|
534
|
+
requestId: `${options.requestId}${requestSuffix}`,
|
|
492
535
|
routeHint: 'web_search'
|
|
493
536
|
});
|
|
494
537
|
const providerResponse = backend.providerResponse && typeof backend.providerResponse === 'object'
|
|
@@ -569,32 +612,6 @@ function injectWebSearchToolResult(base, toolCall, engine, query, backendResult)
|
|
|
569
612
|
];
|
|
570
613
|
return cloned;
|
|
571
614
|
}
|
|
572
|
-
function buildWebSearchFollowupPayload(adapterContext, chatResponse, entryEndpoint) {
|
|
573
|
-
const captured = adapterContext && typeof adapterContext === 'object'
|
|
574
|
-
? adapterContext.capturedChatRequest
|
|
575
|
-
: undefined;
|
|
576
|
-
const seed = extractCapturedChatSeed(captured);
|
|
577
|
-
if (!seed) {
|
|
578
|
-
return null;
|
|
579
|
-
}
|
|
580
|
-
const assistantMessage = extractAssistantMessage(chatResponse);
|
|
581
|
-
if (!assistantMessage) {
|
|
582
|
-
return null;
|
|
583
|
-
}
|
|
584
|
-
const toolMessages = buildToolMessages(chatResponse);
|
|
585
|
-
if (!toolMessages.length) {
|
|
586
|
-
return null;
|
|
587
|
-
}
|
|
588
|
-
const reconstructed = [...seed.messages, assistantMessage, ...toolMessages];
|
|
589
|
-
const filteredTools = dropToolByFunctionName(seed.tools, 'web_search');
|
|
590
|
-
return buildEntryAwareFollowupPayload({
|
|
591
|
-
entryEndpoint,
|
|
592
|
-
model: seed.model,
|
|
593
|
-
messages: reconstructed,
|
|
594
|
-
...(filteredTools ? { tools: filteredTools } : {}),
|
|
595
|
-
...(seed.parameters ? { parameters: seed.parameters } : {})
|
|
596
|
-
});
|
|
597
|
-
}
|
|
598
615
|
function extractAssistantMessage(chatResponse) {
|
|
599
616
|
const choices = Array.isArray(chatResponse.choices)
|
|
600
617
|
? chatResponse.choices
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { JsonObject } from '../conversion/hub/types/json.js';
|
|
2
|
+
export declare function reenterServerToolBackend(args: {
|
|
3
|
+
reenterPipeline: (options: {
|
|
4
|
+
entryEndpoint: string;
|
|
5
|
+
requestId: string;
|
|
6
|
+
body: JsonObject;
|
|
7
|
+
metadata?: JsonObject;
|
|
8
|
+
}) => Promise<{
|
|
9
|
+
body?: JsonObject;
|
|
10
|
+
__sse_responses?: unknown;
|
|
11
|
+
format?: string;
|
|
12
|
+
}>;
|
|
13
|
+
entryEndpoint: string;
|
|
14
|
+
requestId: string;
|
|
15
|
+
body: JsonObject;
|
|
16
|
+
providerProtocol: string;
|
|
17
|
+
routeHint?: string;
|
|
18
|
+
metadata?: JsonObject;
|
|
19
|
+
}): Promise<{
|
|
20
|
+
body?: JsonObject;
|
|
21
|
+
__sse_responses?: unknown;
|
|
22
|
+
format?: string;
|
|
23
|
+
}>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export async function reenterServerToolBackend(args) {
|
|
2
|
+
const routeHint = typeof args.routeHint === 'string' && args.routeHint.trim().length ? args.routeHint.trim() : undefined;
|
|
3
|
+
const merged = {
|
|
4
|
+
providerProtocol: args.providerProtocol,
|
|
5
|
+
serverToolFollowup: true,
|
|
6
|
+
stream: false,
|
|
7
|
+
preserveRouteHint: false,
|
|
8
|
+
disableStickyRoutes: true,
|
|
9
|
+
...(routeHint ? { routeHint } : {}),
|
|
10
|
+
...(args.metadata ?? {})
|
|
11
|
+
};
|
|
12
|
+
return await args.reenterPipeline({
|
|
13
|
+
entryEndpoint: args.entryEndpoint,
|
|
14
|
+
requestId: args.requestId,
|
|
15
|
+
body: args.body,
|
|
16
|
+
metadata: merged
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { JsonObject } from '../conversion/hub/types/json.js';
|
|
2
2
|
import type { ServerSideToolEngineOptions, ServerSideToolEngineResult, ToolCall } from './types.js';
|
|
3
|
-
import './handlers/web-search.js';
|
|
4
|
-
import './handlers/vision.js';
|
|
5
3
|
import './handlers/iflow-model-error-retry.js';
|
|
6
4
|
import './handlers/gemini-empty-reply-continue.js';
|
|
7
5
|
import './handlers/stop-message-auto.js';
|
|
6
|
+
import './handlers/clock.js';
|
|
7
|
+
import './handlers/clock-auto.js';
|
|
8
8
|
import './handlers/exec-command-guard.js';
|
|
9
9
|
import './handlers/apply-patch-guard.js';
|
|
10
|
+
import './handlers/recursive-detection-guard.js';
|
|
10
11
|
export declare function runServerSideToolEngine(options: ServerSideToolEngineOptions): Promise<ServerSideToolEngineResult>;
|
|
11
12
|
export declare function extractToolCalls(chatResponse: JsonObject): ToolCall[];
|
|
12
13
|
export declare function cloneJson<T>(value: T): T;
|