@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,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { buildChatRequestFromResponses, buildResponsesRequestFromChat, captureResponsesContext } from '../../conversion/responses/responses-openai-bridge.js';
|
|
1
|
+
import { buildChatRequestFromResponses, captureResponsesContext } from '../../conversion/responses/responses-openai-bridge.js';
|
|
3
2
|
import { cloneJson } from '../server-side-tools.js';
|
|
3
|
+
import { trimOpenAiMessagesForFollowup } from './followup-message-trimmer.js';
|
|
4
4
|
function extractResponsesTopLevelParameters(record) {
|
|
5
5
|
const allowed = new Set([
|
|
6
6
|
'temperature',
|
|
@@ -79,32 +79,6 @@ export function normalizeFollowupParameters(value) {
|
|
|
79
79
|
delete cloned.stream;
|
|
80
80
|
return Object.keys(cloned).length ? cloned : undefined;
|
|
81
81
|
}
|
|
82
|
-
export function buildEntryAwareFollowupPayload(args) {
|
|
83
|
-
const chatPayload = {
|
|
84
|
-
...(args.model ? { model: args.model } : {}),
|
|
85
|
-
messages: args.messages,
|
|
86
|
-
...(args.tools ? { tools: args.tools } : {})
|
|
87
|
-
};
|
|
88
|
-
const normalizedEntry = typeof args.entryEndpoint === 'string' ? args.entryEndpoint.trim().toLowerCase() : '';
|
|
89
|
-
if (normalizedEntry.includes('/v1/responses')) {
|
|
90
|
-
return buildResponsesRequestFromChat(chatPayload, {
|
|
91
|
-
stream: false,
|
|
92
|
-
...(args.parameters ? { parameters: args.parameters } : {})
|
|
93
|
-
}).request;
|
|
94
|
-
}
|
|
95
|
-
if (normalizedEntry.includes('/v1/messages')) {
|
|
96
|
-
const anthropicChatPayload = {
|
|
97
|
-
...chatPayload,
|
|
98
|
-
...(args.parameters ? args.parameters : {})
|
|
99
|
-
};
|
|
100
|
-
return buildAnthropicRequestFromOpenAIChat(anthropicChatPayload);
|
|
101
|
-
}
|
|
102
|
-
const openaiChatPayload = {
|
|
103
|
-
...chatPayload,
|
|
104
|
-
...(args.parameters ? args.parameters : {})
|
|
105
|
-
};
|
|
106
|
-
return openaiChatPayload;
|
|
107
|
-
}
|
|
108
82
|
export function dropToolByFunctionName(tools, dropName) {
|
|
109
83
|
const name = typeof dropName === 'string' ? dropName.trim() : '';
|
|
110
84
|
if (!tools || !tools.length || !name) {
|
|
@@ -120,3 +94,249 @@ export function dropToolByFunctionName(tools, dropName) {
|
|
|
120
94
|
return toolName !== name;
|
|
121
95
|
});
|
|
122
96
|
}
|
|
97
|
+
function extractAssistantMessageFromChatLike(chatResponse) {
|
|
98
|
+
if (!chatResponse || typeof chatResponse !== 'object') {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const choices = Array.isArray(chatResponse.choices)
|
|
102
|
+
? chatResponse.choices
|
|
103
|
+
: [];
|
|
104
|
+
if (choices.length > 0) {
|
|
105
|
+
const first = choices[0] && typeof choices[0] === 'object' && !Array.isArray(choices[0])
|
|
106
|
+
? choices[0]
|
|
107
|
+
: null;
|
|
108
|
+
const msg = first &&
|
|
109
|
+
first.message &&
|
|
110
|
+
typeof first.message === 'object' &&
|
|
111
|
+
!Array.isArray(first.message)
|
|
112
|
+
? first.message
|
|
113
|
+
: null;
|
|
114
|
+
if (msg) {
|
|
115
|
+
return cloneJson(msg);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Responses-like fallback: try output_text.
|
|
119
|
+
const outputText = chatResponse.output_text;
|
|
120
|
+
if (typeof outputText === 'string' && outputText.trim().length) {
|
|
121
|
+
return { role: 'assistant', content: outputText.trim() };
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
function buildToolMessagesFromToolOutputs(chatResponse) {
|
|
126
|
+
const toolOutputs = Array.isArray(chatResponse.tool_outputs)
|
|
127
|
+
? chatResponse.tool_outputs
|
|
128
|
+
: [];
|
|
129
|
+
const messages = [];
|
|
130
|
+
for (const entry of toolOutputs) {
|
|
131
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry))
|
|
132
|
+
continue;
|
|
133
|
+
const record = entry;
|
|
134
|
+
const toolCallId = typeof record.tool_call_id === 'string' ? record.tool_call_id : undefined;
|
|
135
|
+
if (!toolCallId)
|
|
136
|
+
continue;
|
|
137
|
+
const name = typeof record.name === 'string' && record.name.trim() ? record.name.trim() : 'tool';
|
|
138
|
+
const rawContent = record.content;
|
|
139
|
+
let contentText;
|
|
140
|
+
if (typeof rawContent === 'string') {
|
|
141
|
+
contentText = rawContent;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
try {
|
|
145
|
+
contentText = JSON.stringify(rawContent ?? {});
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
contentText = String(rawContent ?? '');
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
messages.push({
|
|
152
|
+
role: 'tool',
|
|
153
|
+
tool_call_id: toolCallId,
|
|
154
|
+
name,
|
|
155
|
+
content: contentText
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
return messages;
|
|
159
|
+
}
|
|
160
|
+
function injectVisionSummaryIntoMessages(source, summary) {
|
|
161
|
+
const messages = Array.isArray(source) ? cloneJson(source) : [];
|
|
162
|
+
let injected = false;
|
|
163
|
+
for (const message of messages) {
|
|
164
|
+
if (!message || typeof message !== 'object')
|
|
165
|
+
continue;
|
|
166
|
+
const content = message.content;
|
|
167
|
+
if (!Array.isArray(content))
|
|
168
|
+
continue;
|
|
169
|
+
const nextParts = [];
|
|
170
|
+
let removed = false;
|
|
171
|
+
for (const part of content) {
|
|
172
|
+
if (part && typeof part === 'object') {
|
|
173
|
+
const typeValue = typeof part.type === 'string'
|
|
174
|
+
? String(part.type).toLowerCase()
|
|
175
|
+
: '';
|
|
176
|
+
if (typeValue.includes('image')) {
|
|
177
|
+
removed = true;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
nextParts.push(part);
|
|
182
|
+
}
|
|
183
|
+
if (removed) {
|
|
184
|
+
nextParts.push({
|
|
185
|
+
type: 'text',
|
|
186
|
+
text: `[Vision] ${summary}`
|
|
187
|
+
});
|
|
188
|
+
message.content = nextParts;
|
|
189
|
+
injected = true;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (!injected) {
|
|
193
|
+
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
194
|
+
const msg = messages[i];
|
|
195
|
+
if (!msg || typeof msg !== 'object')
|
|
196
|
+
continue;
|
|
197
|
+
const role = typeof msg.role === 'string'
|
|
198
|
+
? String(msg.role).toLowerCase()
|
|
199
|
+
: '';
|
|
200
|
+
if (role !== 'user')
|
|
201
|
+
continue;
|
|
202
|
+
const content = msg.content;
|
|
203
|
+
if (Array.isArray(content)) {
|
|
204
|
+
content.push({
|
|
205
|
+
type: 'text',
|
|
206
|
+
text: `[Vision] ${summary}`
|
|
207
|
+
});
|
|
208
|
+
injected = true;
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
if (typeof content === 'string' && content.length) {
|
|
212
|
+
msg.content = `${content}\n[Vision] ${summary}`;
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
msg.content = `[Vision] ${summary}`;
|
|
216
|
+
}
|
|
217
|
+
injected = true;
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (!injected) {
|
|
222
|
+
messages.push({
|
|
223
|
+
role: 'user',
|
|
224
|
+
content: `[Vision] ${summary}`
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
return messages;
|
|
228
|
+
}
|
|
229
|
+
function injectSystemTextIntoMessages(source, text) {
|
|
230
|
+
const messages = Array.isArray(source) ? cloneJson(source) : [];
|
|
231
|
+
const content = typeof text === 'string' ? text : '';
|
|
232
|
+
if (!content.trim().length) {
|
|
233
|
+
return messages;
|
|
234
|
+
}
|
|
235
|
+
const sys = { role: 'system', content };
|
|
236
|
+
let insertAt = 0;
|
|
237
|
+
while (insertAt < messages.length) {
|
|
238
|
+
const msg = messages[insertAt];
|
|
239
|
+
const role = msg && typeof msg === 'object' && !Array.isArray(msg) && typeof msg.role === 'string'
|
|
240
|
+
? String(msg.role).trim().toLowerCase()
|
|
241
|
+
: '';
|
|
242
|
+
if (role === 'system') {
|
|
243
|
+
insertAt += 1;
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
messages.splice(insertAt, 0, sys);
|
|
249
|
+
return messages;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Build a canonical followup request body from injection ops.
|
|
253
|
+
*
|
|
254
|
+
* Important: this returns a protocol-agnostic "chat-like" payload:
|
|
255
|
+
* { model, messages, tools?, parameters? }
|
|
256
|
+
*
|
|
257
|
+
* The followup is expected to re-enter HubPipeline at the chat-process entry,
|
|
258
|
+
* so we must not convert to /v1/responses or /v1/messages here.
|
|
259
|
+
*/
|
|
260
|
+
export function buildServerToolFollowupChatPayloadFromInjection(args) {
|
|
261
|
+
const captured = args.adapterContext && typeof args.adapterContext === 'object'
|
|
262
|
+
? args.adapterContext.capturedChatRequest
|
|
263
|
+
: undefined;
|
|
264
|
+
const seed = extractCapturedChatSeed(captured);
|
|
265
|
+
if (!seed) {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
if (!seed.model || typeof seed.model !== 'string' || !seed.model.trim()) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
let messages = Array.isArray(seed.messages) ? cloneJson(seed.messages) : [];
|
|
272
|
+
let tools = seed.tools ? cloneJson(seed.tools) : undefined;
|
|
273
|
+
const parameters = seed.parameters ? cloneJson(seed.parameters) : undefined;
|
|
274
|
+
const ops = Array.isArray(args.injection?.ops) ? args.injection.ops : [];
|
|
275
|
+
for (const op of ops) {
|
|
276
|
+
if (!op || typeof op !== 'object')
|
|
277
|
+
continue;
|
|
278
|
+
if (op.op === 'trim_openai_messages') {
|
|
279
|
+
const maxNonSystemMessages = typeof op.maxNonSystemMessages === 'number'
|
|
280
|
+
? op.maxNonSystemMessages
|
|
281
|
+
: 16;
|
|
282
|
+
messages = trimOpenAiMessagesForFollowup(messages, { maxNonSystemMessages });
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
if (op.op === 'append_assistant_message') {
|
|
286
|
+
const required = op.required !== false;
|
|
287
|
+
const msg = extractAssistantMessageFromChatLike(args.chatResponse);
|
|
288
|
+
if (!msg) {
|
|
289
|
+
if (required)
|
|
290
|
+
return null;
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
messages.push(msg);
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
if (op.op === 'append_tool_messages_from_tool_outputs') {
|
|
297
|
+
const required = op.required !== false;
|
|
298
|
+
const toolMessages = buildToolMessagesFromToolOutputs(args.chatResponse);
|
|
299
|
+
if (!toolMessages.length) {
|
|
300
|
+
if (required)
|
|
301
|
+
return null;
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
messages.push(...toolMessages);
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
if (op.op === 'inject_system_text') {
|
|
308
|
+
const text = typeof op.text === 'string' ? String(op.text) : '';
|
|
309
|
+
if (text.trim().length) {
|
|
310
|
+
messages = injectSystemTextIntoMessages(messages, text.trim());
|
|
311
|
+
}
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (op.op === 'append_user_text') {
|
|
315
|
+
const text = typeof op.text === 'string' ? String(op.text) : '';
|
|
316
|
+
if (text.trim().length) {
|
|
317
|
+
messages.push({ role: 'user', content: text });
|
|
318
|
+
}
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
if (op.op === 'drop_tool_by_name') {
|
|
322
|
+
const name = typeof op.name === 'string' ? String(op.name) : '';
|
|
323
|
+
if (name.trim().length) {
|
|
324
|
+
tools = dropToolByFunctionName(tools, name.trim());
|
|
325
|
+
}
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
if (op.op === 'inject_vision_summary') {
|
|
329
|
+
const summary = typeof op.summary === 'string' ? String(op.summary) : '';
|
|
330
|
+
if (summary.trim().length) {
|
|
331
|
+
messages = injectVisionSummaryIntoMessages(messages, summary.trim());
|
|
332
|
+
}
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return {
|
|
337
|
+
model: seed.model,
|
|
338
|
+
messages,
|
|
339
|
+
...(tools ? { tools } : {}),
|
|
340
|
+
...(parameters ? { parameters } : {})
|
|
341
|
+
};
|
|
342
|
+
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { registerServerToolHandler } from '../registry.js';
|
|
2
|
-
import { cloneJson } from '../server-side-tools.js';
|
|
3
2
|
import { isCompactionRequest } from './compaction-detect.js';
|
|
4
|
-
import {
|
|
3
|
+
import { extractCapturedChatSeed } from './followup-request-builder.js';
|
|
5
4
|
const FLOW_ID = 'gemini_empty_reply_continue';
|
|
6
5
|
const handler = async (ctx) => {
|
|
7
|
-
if (!ctx.
|
|
6
|
+
if (!ctx.capabilities.reenterPipeline) {
|
|
8
7
|
return null;
|
|
9
8
|
}
|
|
10
9
|
// 避免在 followup 请求里再次触发,防止循环。
|
|
@@ -17,10 +16,10 @@ const handler = async (ctx) => {
|
|
|
17
16
|
return null;
|
|
18
17
|
}
|
|
19
18
|
// 仅针对 gemini-chat 协议 + antigravity.* providerKey 的 /v1/responses 路径启用。
|
|
20
|
-
if (ctx.
|
|
19
|
+
if (ctx.providerProtocol !== 'gemini-chat') {
|
|
21
20
|
return null;
|
|
22
21
|
}
|
|
23
|
-
const entryEndpoint = (ctx.
|
|
22
|
+
const entryEndpoint = (ctx.entryEndpoint || '').toLowerCase();
|
|
24
23
|
if (!entryEndpoint.includes('/v1/responses')) {
|
|
25
24
|
return null;
|
|
26
25
|
}
|
|
@@ -53,6 +52,10 @@ const handler = async (ctx) => {
|
|
|
53
52
|
if (isCompactionRequest(captured)) {
|
|
54
53
|
return null;
|
|
55
54
|
}
|
|
55
|
+
const seed = extractCapturedChatSeed(captured);
|
|
56
|
+
if (!seed) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
56
59
|
// 超过最多 3 次空回复:返回一个 HTTP_HANDLER_ERROR 形状的错误,交由上层错误中心处理。
|
|
57
60
|
if (nextCount > 3) {
|
|
58
61
|
const errorChat = {
|
|
@@ -66,34 +69,37 @@ const handler = async (ctx) => {
|
|
|
66
69
|
}
|
|
67
70
|
};
|
|
68
71
|
return {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
flowId: FLOW_ID,
|
|
73
|
+
finalize: async () => ({
|
|
74
|
+
chatResponse: errorChat,
|
|
75
|
+
execution: {
|
|
76
|
+
flowId: FLOW_ID
|
|
77
|
+
}
|
|
78
|
+
})
|
|
73
79
|
};
|
|
74
80
|
}
|
|
75
|
-
const assistantMessage =
|
|
76
|
-
const followupPayload = buildContinueFollowupPayload(captured, assistantMessage);
|
|
77
|
-
if (!followupPayload) {
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
81
|
+
const assistantMessage = extractAssistantMessageForFollowup(ctx.base);
|
|
80
82
|
return {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
83
|
+
flowId: FLOW_ID,
|
|
84
|
+
finalize: async () => ({
|
|
85
|
+
chatResponse: ctx.base,
|
|
86
|
+
execution: {
|
|
87
|
+
flowId: FLOW_ID,
|
|
88
|
+
followup: {
|
|
89
|
+
requestIdSuffix: ':continue',
|
|
90
|
+
entryEndpoint: ctx.entryEndpoint,
|
|
91
|
+
injection: {
|
|
92
|
+
ops: [
|
|
93
|
+
{ op: 'append_assistant_message', required: false },
|
|
94
|
+
{ op: 'append_user_text', text: '继续执行' }
|
|
95
|
+
]
|
|
96
|
+
},
|
|
97
|
+
metadata: {
|
|
98
|
+
geminiEmptyReplyCount: nextCount
|
|
99
|
+
}
|
|
94
100
|
}
|
|
95
101
|
}
|
|
96
|
-
}
|
|
102
|
+
})
|
|
97
103
|
};
|
|
98
104
|
};
|
|
99
105
|
registerServerToolHandler('gemini_empty_reply_continue', handler, { trigger: 'auto' });
|
|
@@ -153,6 +159,34 @@ function decideEmptyReply(base) {
|
|
|
153
159
|
// 允许 output 为空或仅包含空消息:视作空回复,触发自动续写。
|
|
154
160
|
return { shouldTrigger: true };
|
|
155
161
|
}
|
|
162
|
+
function extractAssistantMessageForFollowup(chatResponse) {
|
|
163
|
+
if (!chatResponse || typeof chatResponse !== 'object' || Array.isArray(chatResponse)) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
const choices = Array.isArray(chatResponse.choices)
|
|
167
|
+
? chatResponse.choices
|
|
168
|
+
: [];
|
|
169
|
+
if (!choices.length) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
const first = choices[0];
|
|
173
|
+
if (!first || typeof first !== 'object' || Array.isArray(first)) {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
const message = first.message;
|
|
177
|
+
if (!message || typeof message !== 'object' || Array.isArray(message)) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
const role = typeof message.role === 'string' ? String(message.role) : '';
|
|
181
|
+
if (role && role.toLowerCase() !== 'assistant') {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
const content = message.content;
|
|
185
|
+
if (typeof content !== 'string' || !content.trim()) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
return { role: 'assistant', content: content.trim() };
|
|
189
|
+
}
|
|
156
190
|
function extractResponsesOutputText(base) {
|
|
157
191
|
const raw = base.output_text;
|
|
158
192
|
if (typeof raw === 'string') {
|
|
@@ -216,112 +250,6 @@ function getCapturedRequest(adapterContext) {
|
|
|
216
250
|
}
|
|
217
251
|
return captured;
|
|
218
252
|
}
|
|
219
|
-
function extractChatSeedFromCapturedRequest(source) {
|
|
220
|
-
const model = typeof source.model === 'string' && source.model.trim()
|
|
221
|
-
? source.model.trim()
|
|
222
|
-
: undefined;
|
|
223
|
-
const rawMessages = Array.isArray(source.messages)
|
|
224
|
-
? source.messages
|
|
225
|
-
: null;
|
|
226
|
-
if (rawMessages) {
|
|
227
|
-
const tools = Array.isArray(source.tools)
|
|
228
|
-
? cloneJson(source.tools)
|
|
229
|
-
: undefined;
|
|
230
|
-
return {
|
|
231
|
-
...(model ? { model } : {}),
|
|
232
|
-
messages: cloneJson(rawMessages),
|
|
233
|
-
...(tools ? { tools } : {})
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
const rawInput = Array.isArray(source.input)
|
|
237
|
-
? source.input
|
|
238
|
-
: null;
|
|
239
|
-
if (rawInput) {
|
|
240
|
-
try {
|
|
241
|
-
const ctx = captureResponsesContext(source);
|
|
242
|
-
if (!ctx.isResponsesPayload) {
|
|
243
|
-
return null;
|
|
244
|
-
}
|
|
245
|
-
const rebuilt = buildChatRequestFromResponses(source, ctx).request;
|
|
246
|
-
const rebuiltModel = typeof rebuilt.model === 'string' && rebuilt.model.trim().length ? String(rebuilt.model).trim() : model;
|
|
247
|
-
const rebuiltMessages = Array.isArray(rebuilt.messages) ? rebuilt.messages : [];
|
|
248
|
-
const rebuiltTools = Array.isArray(rebuilt.tools) ? rebuilt.tools : undefined;
|
|
249
|
-
return {
|
|
250
|
-
...(rebuiltModel ? { model: rebuiltModel } : {}),
|
|
251
|
-
messages: cloneJson(rebuiltMessages),
|
|
252
|
-
...(rebuiltTools ? { tools: cloneJson(rebuiltTools) } : {})
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
catch {
|
|
256
|
-
return null;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
return null;
|
|
260
|
-
}
|
|
261
|
-
function buildContinueFollowupPayload(source, assistant) {
|
|
262
|
-
if (!source || typeof source !== 'object') {
|
|
263
|
-
return null;
|
|
264
|
-
}
|
|
265
|
-
const chatSeed = extractChatSeedFromCapturedRequest(source);
|
|
266
|
-
const model = chatSeed?.model;
|
|
267
|
-
const originalMessages = chatSeed?.messages ? cloneJson(chatSeed.messages) : [];
|
|
268
|
-
const originalTools = chatSeed?.tools ? cloneJson(chatSeed.tools) : undefined;
|
|
269
|
-
const originalParameters = (() => {
|
|
270
|
-
const direct = source.parameters;
|
|
271
|
-
if (direct && typeof direct === 'object' && !Array.isArray(direct)) {
|
|
272
|
-
return cloneJson(direct);
|
|
273
|
-
}
|
|
274
|
-
// Backward/compat: captured request might be a raw `/v1/responses` payload with
|
|
275
|
-
// top-level parameters (max_output_tokens, temperature, ...), not nested under `parameters`.
|
|
276
|
-
const record = source;
|
|
277
|
-
const allowed = new Set([
|
|
278
|
-
'temperature',
|
|
279
|
-
'top_p',
|
|
280
|
-
'max_output_tokens',
|
|
281
|
-
'seed',
|
|
282
|
-
'logit_bias',
|
|
283
|
-
'user',
|
|
284
|
-
'parallel_tool_calls',
|
|
285
|
-
'tool_choice',
|
|
286
|
-
'response_format',
|
|
287
|
-
'stream'
|
|
288
|
-
]);
|
|
289
|
-
const out = {};
|
|
290
|
-
if (record.max_output_tokens === undefined && record.max_tokens !== undefined) {
|
|
291
|
-
out.max_output_tokens = record.max_tokens;
|
|
292
|
-
}
|
|
293
|
-
for (const key of Object.keys(record)) {
|
|
294
|
-
if (!allowed.has(key))
|
|
295
|
-
continue;
|
|
296
|
-
out[key] = record[key];
|
|
297
|
-
}
|
|
298
|
-
return Object.keys(out).length ? cloneJson(out) : undefined;
|
|
299
|
-
})();
|
|
300
|
-
const parametersForFollowup = originalParameters
|
|
301
|
-
? (() => {
|
|
302
|
-
const cloned = cloneJson(originalParameters);
|
|
303
|
-
delete cloned.stream;
|
|
304
|
-
return cloned;
|
|
305
|
-
})()
|
|
306
|
-
: undefined;
|
|
307
|
-
const messages = [...originalMessages];
|
|
308
|
-
if (assistant && typeof assistant === 'object' && !Array.isArray(assistant)) {
|
|
309
|
-
messages.push(cloneJson(assistant));
|
|
310
|
-
}
|
|
311
|
-
messages.push({
|
|
312
|
-
role: 'user',
|
|
313
|
-
content: '继续执行'
|
|
314
|
-
});
|
|
315
|
-
const chatPayload = {
|
|
316
|
-
...(model ? { model } : {}),
|
|
317
|
-
messages,
|
|
318
|
-
...(originalTools ? { tools: originalTools } : {})
|
|
319
|
-
};
|
|
320
|
-
return buildResponsesRequestFromChat(chatPayload, {
|
|
321
|
-
stream: false,
|
|
322
|
-
...(parametersForFollowup ? { parameters: parametersForFollowup } : {})
|
|
323
|
-
}).request;
|
|
324
|
-
}
|
|
325
253
|
function hasCompactionFlag(record) {
|
|
326
254
|
const flag = record.compactionRequest;
|
|
327
255
|
if (flag === true) {
|
|
@@ -332,38 +260,3 @@ function hasCompactionFlag(record) {
|
|
|
332
260
|
}
|
|
333
261
|
return false;
|
|
334
262
|
}
|
|
335
|
-
function extractAssistantMessage(baseResponse) {
|
|
336
|
-
if (!baseResponse || typeof baseResponse !== 'object' || Array.isArray(baseResponse)) {
|
|
337
|
-
return null;
|
|
338
|
-
}
|
|
339
|
-
const base = baseResponse;
|
|
340
|
-
// OpenAI Chat shape
|
|
341
|
-
const choices = Array.isArray(base.choices) ? base.choices : [];
|
|
342
|
-
if (choices.length > 0) {
|
|
343
|
-
const first = choices[0] && typeof choices[0] === 'object' && !Array.isArray(choices[0])
|
|
344
|
-
? choices[0]
|
|
345
|
-
: null;
|
|
346
|
-
const msg = first &&
|
|
347
|
-
first.message &&
|
|
348
|
-
typeof first.message === 'object' &&
|
|
349
|
-
!Array.isArray(first.message)
|
|
350
|
-
? first.message
|
|
351
|
-
: null;
|
|
352
|
-
if (msg) {
|
|
353
|
-
const content = typeof msg.content === 'string' ? msg.content.trim() : '';
|
|
354
|
-
if (!content) {
|
|
355
|
-
return null;
|
|
356
|
-
}
|
|
357
|
-
return cloneJson(msg);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
// OpenAI Responses shape
|
|
361
|
-
const outputText = extractResponsesOutputText(base);
|
|
362
|
-
if (outputText.length > 0) {
|
|
363
|
-
return {
|
|
364
|
-
role: 'assistant',
|
|
365
|
-
content: outputText
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
return null;
|
|
369
|
-
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { registerServerToolHandler } from '../registry.js';
|
|
2
2
|
import { isCompactionRequest } from './compaction-detect.js';
|
|
3
|
-
import {
|
|
3
|
+
import { extractCapturedChatSeed } from './followup-request-builder.js';
|
|
4
4
|
const FLOW_ID = 'iflow_model_error_retry';
|
|
5
5
|
const handler = async (ctx) => {
|
|
6
|
-
if (!ctx.
|
|
6
|
+
if (!ctx.capabilities.reenterPipeline) {
|
|
7
7
|
return null;
|
|
8
8
|
}
|
|
9
9
|
const adapterRecord = ctx.adapterContext;
|
|
@@ -16,10 +16,10 @@ const handler = async (ctx) => {
|
|
|
16
16
|
return null;
|
|
17
17
|
}
|
|
18
18
|
// 仅针对 openai-chat 协议 + iflow.* providerKey 的 /v1/responses 路径启用。
|
|
19
|
-
if (ctx.
|
|
19
|
+
if (ctx.providerProtocol !== 'openai-chat') {
|
|
20
20
|
return null;
|
|
21
21
|
}
|
|
22
|
-
const entryEndpoint = (ctx.
|
|
22
|
+
const entryEndpoint = (ctx.entryEndpoint || '').toLowerCase();
|
|
23
23
|
if (!entryEndpoint.includes('/v1/responses')) {
|
|
24
24
|
return null;
|
|
25
25
|
}
|
|
@@ -46,22 +46,25 @@ const handler = async (ctx) => {
|
|
|
46
46
|
if (isCompactionRequest(captured)) {
|
|
47
47
|
return null;
|
|
48
48
|
}
|
|
49
|
-
const
|
|
50
|
-
if (!
|
|
49
|
+
const seed = extractCapturedChatSeed(captured);
|
|
50
|
+
if (!seed) {
|
|
51
51
|
return null;
|
|
52
52
|
}
|
|
53
53
|
return {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
54
|
+
flowId: FLOW_ID,
|
|
55
|
+
finalize: async () => ({
|
|
56
|
+
chatResponse: ctx.base,
|
|
57
|
+
execution: {
|
|
58
|
+
flowId: FLOW_ID,
|
|
59
|
+
followup: {
|
|
60
|
+
requestIdSuffix: ':retry',
|
|
61
|
+
entryEndpoint: ctx.entryEndpoint,
|
|
62
|
+
injection: {
|
|
63
|
+
ops: []
|
|
64
|
+
}
|
|
62
65
|
}
|
|
63
66
|
}
|
|
64
|
-
}
|
|
67
|
+
})
|
|
65
68
|
};
|
|
66
69
|
};
|
|
67
70
|
registerServerToolHandler('iflow_model_error_retry', handler, { trigger: 'auto' });
|
|
@@ -75,19 +78,6 @@ function getCapturedRequest(adapterContext) {
|
|
|
75
78
|
}
|
|
76
79
|
return captured;
|
|
77
80
|
}
|
|
78
|
-
function buildRetryFollowupPayload(source, entryEndpoint) {
|
|
79
|
-
const seed = extractCapturedChatSeed(source);
|
|
80
|
-
if (!seed) {
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
return buildEntryAwareFollowupPayload({
|
|
84
|
-
entryEndpoint,
|
|
85
|
-
model: seed.model,
|
|
86
|
-
messages: seed.messages,
|
|
87
|
-
...(seed.tools ? { tools: seed.tools } : {}),
|
|
88
|
-
...(seed.parameters ? { parameters: seed.parameters } : {})
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
81
|
function hasCompactionFlag(record) {
|
|
92
82
|
const flag = record.compactionRequest;
|
|
93
83
|
if (flag === true) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|