@jsonstudio/llms 0.6.1733 → 0.6.1739
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.
|
@@ -249,6 +249,45 @@ function injectSystemTextIntoMessages(source, text) {
|
|
|
249
249
|
messages.splice(insertAt, 0, sys);
|
|
250
250
|
return messages;
|
|
251
251
|
}
|
|
252
|
+
function compactToolContentValue(value, maxChars) {
|
|
253
|
+
const text = typeof value === 'string'
|
|
254
|
+
? value
|
|
255
|
+
: (() => {
|
|
256
|
+
try {
|
|
257
|
+
return JSON.stringify(value ?? '');
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
return String(value ?? '');
|
|
261
|
+
}
|
|
262
|
+
})();
|
|
263
|
+
if (text.length <= maxChars) {
|
|
264
|
+
return text;
|
|
265
|
+
}
|
|
266
|
+
const keepHead = Math.max(24, Math.floor(maxChars * 0.45));
|
|
267
|
+
const keepTail = Math.max(24, Math.floor(maxChars * 0.35));
|
|
268
|
+
const omitted = Math.max(0, text.length - keepHead - keepTail);
|
|
269
|
+
const head = text.slice(0, keepHead);
|
|
270
|
+
const tail = text.slice(text.length - keepTail);
|
|
271
|
+
return head + '\n...[tool_output_compacted omitted=' + String(omitted) + ']...\n' + tail;
|
|
272
|
+
}
|
|
273
|
+
function compactToolContentInMessages(source, options) {
|
|
274
|
+
const maxChars = Number.isFinite(options.maxChars) ? Math.max(64, Math.floor(options.maxChars)) : 1200;
|
|
275
|
+
const messages = Array.isArray(source) ? cloneJson(source) : [];
|
|
276
|
+
for (const message of messages) {
|
|
277
|
+
if (!message || typeof message !== 'object' || Array.isArray(message)) {
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
const role = typeof message.role === 'string'
|
|
281
|
+
? String(message.role).trim().toLowerCase()
|
|
282
|
+
: '';
|
|
283
|
+
if (role !== 'tool') {
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
const content = message.content;
|
|
287
|
+
message.content = compactToolContentValue(content, maxChars);
|
|
288
|
+
}
|
|
289
|
+
return messages;
|
|
290
|
+
}
|
|
252
291
|
function buildStandardFollowupTools() {
|
|
253
292
|
// Keep this list minimal and stable. Used only as a best-effort fallback when a followup hop
|
|
254
293
|
// would otherwise have no tools at all (which can cause tool-based clients to "break" mid-session).
|
|
@@ -372,6 +411,13 @@ export function buildServerToolFollowupChatPayloadFromInjection(args) {
|
|
|
372
411
|
messages = trimOpenAiMessagesForFollowup(messages, { maxNonSystemMessages });
|
|
373
412
|
continue;
|
|
374
413
|
}
|
|
414
|
+
if (op.op === 'compact_tool_content') {
|
|
415
|
+
const maxChars = typeof op.maxChars === 'number'
|
|
416
|
+
? Math.max(64, Math.floor(op.maxChars))
|
|
417
|
+
: 1200;
|
|
418
|
+
messages = compactToolContentInMessages(messages, { maxChars });
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
375
421
|
if (op.op === 'append_assistant_message') {
|
|
376
422
|
const required = op.required !== false;
|
|
377
423
|
const msg = extractAssistantMessageFromChatLike(args.chatResponse);
|
|
@@ -166,6 +166,20 @@ const handler = async (ctx) => {
|
|
|
166
166
|
state.stopMessageUpdatedAt = now;
|
|
167
167
|
}
|
|
168
168
|
saveRoutingInstructionStateAsync(stickyKey, state);
|
|
169
|
+
const followupProviderKey = resolveStopMessageFollowupProviderKey({ record, runtimeMetadata: rt });
|
|
170
|
+
const followupToolContentMaxChars = resolveStopMessageFollowupToolContentMaxChars({
|
|
171
|
+
providerKey: followupProviderKey,
|
|
172
|
+
model: seed.model
|
|
173
|
+
});
|
|
174
|
+
const followupOps = [];
|
|
175
|
+
if (typeof followupToolContentMaxChars === 'number' &&
|
|
176
|
+
Number.isFinite(followupToolContentMaxChars) &&
|
|
177
|
+
followupToolContentMaxChars > 0) {
|
|
178
|
+
followupOps.push({ op: 'compact_tool_content', maxChars: Math.floor(followupToolContentMaxChars) });
|
|
179
|
+
}
|
|
180
|
+
followupOps.push({ op: 'append_assistant_message', required: false });
|
|
181
|
+
followupOps.push({ op: 'ensure_standard_tools' });
|
|
182
|
+
followupOps.push({ op: 'append_user_text', text });
|
|
169
183
|
return {
|
|
170
184
|
flowId: FLOW_ID,
|
|
171
185
|
finalize: async () => ({
|
|
@@ -176,11 +190,7 @@ const handler = async (ctx) => {
|
|
|
176
190
|
requestIdSuffix: ':stop_followup',
|
|
177
191
|
entryEndpoint,
|
|
178
192
|
injection: {
|
|
179
|
-
ops:
|
|
180
|
-
{ op: 'append_assistant_message', required: false },
|
|
181
|
-
{ op: 'ensure_standard_tools' },
|
|
182
|
-
{ op: 'append_user_text', text }
|
|
183
|
-
]
|
|
193
|
+
ops: followupOps
|
|
184
194
|
},
|
|
185
195
|
metadata: (connectionState ? { clientConnectionState: connectionState } : {})
|
|
186
196
|
}
|
|
@@ -202,6 +212,53 @@ function resolveStickyKey(record) {
|
|
|
202
212
|
}
|
|
203
213
|
return undefined;
|
|
204
214
|
}
|
|
215
|
+
function toNonEmptyText(value) {
|
|
216
|
+
return typeof value === 'string' && value.trim().length ? value.trim() : '';
|
|
217
|
+
}
|
|
218
|
+
function readProviderKeyFromMetadata(value) {
|
|
219
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
220
|
+
return '';
|
|
221
|
+
}
|
|
222
|
+
const metadata = value;
|
|
223
|
+
const direct = toNonEmptyText(metadata.providerKey) ||
|
|
224
|
+
toNonEmptyText(metadata.providerId) ||
|
|
225
|
+
toNonEmptyText(metadata.targetProviderKey);
|
|
226
|
+
if (direct) {
|
|
227
|
+
return direct;
|
|
228
|
+
}
|
|
229
|
+
const target = metadata.target;
|
|
230
|
+
if (target && typeof target === 'object' && !Array.isArray(target)) {
|
|
231
|
+
const targetRecord = target;
|
|
232
|
+
return toNonEmptyText(targetRecord.providerKey) || toNonEmptyText(targetRecord.providerId);
|
|
233
|
+
}
|
|
234
|
+
return '';
|
|
235
|
+
}
|
|
236
|
+
function resolveStopMessageFollowupProviderKey(args) {
|
|
237
|
+
const direct = toNonEmptyText(args.record.providerKey) ||
|
|
238
|
+
toNonEmptyText(args.record.providerId) ||
|
|
239
|
+
readProviderKeyFromMetadata(args.record.metadata) ||
|
|
240
|
+
readProviderKeyFromMetadata(args.runtimeMetadata);
|
|
241
|
+
return direct;
|
|
242
|
+
}
|
|
243
|
+
function resolveStopMessageFollowupToolContentMaxChars(params) {
|
|
244
|
+
const raw = String(process.env.ROUTECODEX_STOPMESSAGE_FOLLOWUP_TOOL_CONTENT_MAX_CHARS || '').trim();
|
|
245
|
+
if (raw) {
|
|
246
|
+
const parsed = Number(raw);
|
|
247
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
248
|
+
return Math.max(64, Math.floor(parsed));
|
|
249
|
+
}
|
|
250
|
+
return undefined;
|
|
251
|
+
}
|
|
252
|
+
const providerKey = typeof params.providerKey === 'string' ? params.providerKey.trim().toLowerCase() : '';
|
|
253
|
+
if (providerKey.startsWith('iflow.')) {
|
|
254
|
+
return 1200;
|
|
255
|
+
}
|
|
256
|
+
const model = typeof params.model === 'string' ? params.model.trim().toLowerCase() : '';
|
|
257
|
+
if (model === 'kimi-k2.5' || model.startsWith('kimi-k2.5-')) {
|
|
258
|
+
return 1200;
|
|
259
|
+
}
|
|
260
|
+
return undefined;
|
|
261
|
+
}
|
|
205
262
|
function isStopFinishReason(base) {
|
|
206
263
|
if (!base || typeof base !== 'object' || Array.isArray(base)) {
|
|
207
264
|
return false;
|
|
@@ -74,6 +74,9 @@ export type ServerToolFollowupInjectionOp = {
|
|
|
74
74
|
} | {
|
|
75
75
|
op: 'trim_openai_messages';
|
|
76
76
|
maxNonSystemMessages: number;
|
|
77
|
+
} | {
|
|
78
|
+
op: 'compact_tool_content';
|
|
79
|
+
maxChars: number;
|
|
77
80
|
};
|
|
78
81
|
export type ServerToolFollowupInjectionPlan = {
|
|
79
82
|
ops: ServerToolFollowupInjectionOp[];
|