@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[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsonstudio/llms",
3
- "version": "0.6.1733",
3
+ "version": "0.6.1739",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",