@jsonstudio/llms 0.6.123 → 0.6.147

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.
@@ -1,17 +1,17 @@
1
1
  {
2
- "id": "chat:glm",
3
- "protocol": "openai-chat",
4
- "direction": "request",
5
- "mappings": [
6
- {
7
- "action": "rename",
8
- "from": "response_format",
9
- "to": "metadata.generation.response_format"
10
- },
11
- {
12
- "action": "remove",
13
- "path": "metadata.clientModelId"
14
- }
15
- ],
16
- "filters": []
2
+ "id": "chat:glm",
3
+ "protocol": "openai-chat",
4
+ "direction": "request",
5
+ "mappings": [
6
+ {
7
+ "action": "rename",
8
+ "from": "response_format",
9
+ "to": "metadata.generation.response_format"
10
+ },
11
+ {
12
+ "action": "remove",
13
+ "path": "metadata.clientModelId"
14
+ }
15
+ ],
16
+ "filters": []
17
17
  }
@@ -1,36 +1,36 @@
1
1
  {
2
- "id": "chat:iflow",
3
- "protocol": "openai-chat",
4
- "request": {
5
- "mappings": [
6
- {
7
- "action": "remove",
8
- "path": "metadata.toolCallIdStyle"
9
- },
10
- {
11
- "action": "remove",
12
- "path": "metadata.clientModelId"
13
- },
14
- {
15
- "action": "remove",
16
- "path": "metadata.providerHint"
17
- }
18
- ]
19
- },
20
- "response": {
21
- "mappings": [
22
- {
23
- "action": "rename",
24
- "from": "created_at",
25
- "to": "created"
26
- },
27
- {
28
- "action": "convert_responses_output_to_choices"
29
- },
30
- {
31
- "action": "stringify",
32
- "path": "choices[*].message.tool_calls[*].function.arguments"
33
- }
34
- ]
35
- }
2
+ "id": "chat:iflow",
3
+ "protocol": "openai-chat",
4
+ "request": {
5
+ "mappings": [
6
+ {
7
+ "action": "remove",
8
+ "path": "metadata.toolCallIdStyle"
9
+ },
10
+ {
11
+ "action": "remove",
12
+ "path": "metadata.clientModelId"
13
+ },
14
+ {
15
+ "action": "remove",
16
+ "path": "metadata.providerHint"
17
+ }
18
+ ]
19
+ },
20
+ "response": {
21
+ "mappings": [
22
+ {
23
+ "action": "rename",
24
+ "from": "created_at",
25
+ "to": "created"
26
+ },
27
+ {
28
+ "action": "convert_responses_output_to_choices"
29
+ },
30
+ {
31
+ "action": "stringify",
32
+ "path": "choices[*].message.tool_calls[*].function.arguments"
33
+ }
34
+ ]
35
+ }
36
36
  }
@@ -1,37 +1,37 @@
1
1
  {
2
- "id": "chat:lmstudio",
3
- "protocol": "openai-chat",
4
- "request": {
5
- "mappings": [
6
- {
7
- "action": "normalize_tool_choice",
8
- "path": "tool_choice",
9
- "objectReplacement": "required"
10
- }
11
- ]
12
- },
13
- "response": {
14
- "mappings": [
15
- {
16
- "action": "set_default",
17
- "path": "object",
18
- "value": "chat.completion"
19
- },
20
- {
21
- "action": "set_default",
22
- "path": "id",
23
- "valueSource": "chat_completion_id"
24
- },
25
- {
26
- "action": "set_default",
27
- "path": "created",
28
- "valueSource": "timestamp_seconds"
29
- },
30
- {
31
- "action": "set_default",
32
- "path": "model",
33
- "value": "unknown"
34
- }
35
- ]
36
- }
2
+ "id": "chat:lmstudio",
3
+ "protocol": "openai-chat",
4
+ "request": {
5
+ "mappings": [
6
+ {
7
+ "action": "normalize_tool_choice",
8
+ "path": "tool_choice",
9
+ "objectReplacement": "required"
10
+ }
11
+ ]
12
+ },
13
+ "response": {
14
+ "mappings": [
15
+ {
16
+ "action": "set_default",
17
+ "path": "object",
18
+ "value": "chat.completion"
19
+ },
20
+ {
21
+ "action": "set_default",
22
+ "path": "id",
23
+ "valueSource": "chat_completion_id"
24
+ },
25
+ {
26
+ "action": "set_default",
27
+ "path": "created",
28
+ "valueSource": "timestamp_seconds"
29
+ },
30
+ {
31
+ "action": "set_default",
32
+ "path": "model",
33
+ "value": "unknown"
34
+ }
35
+ ]
36
+ }
37
37
  }
@@ -1,18 +1,18 @@
1
1
  {
2
- "id": "chat:qwen",
3
- "protocol": "openai-chat",
4
- "request": {
5
- "mappings": [
6
- {
7
- "action": "parse_json",
8
- "path": "messages[*].tool_calls[*].function.arguments",
9
- "fallback": {}
10
- },
11
- {
12
- "action": "stringify",
13
- "path": "messages[*].tool_calls[*].function.arguments",
14
- "fallback": {}
15
- }
16
- ]
17
- }
2
+ "id": "chat:qwen",
3
+ "protocol": "openai-chat",
4
+ "request": {
5
+ "mappings": [
6
+ {
7
+ "action": "parse_json",
8
+ "path": "messages[*].tool_calls[*].function.arguments",
9
+ "fallback": {}
10
+ },
11
+ {
12
+ "action": "stringify",
13
+ "path": "messages[*].tool_calls[*].function.arguments",
14
+ "fallback": {}
15
+ }
16
+ ]
17
+ }
18
18
  }
@@ -1,45 +1,45 @@
1
1
  {
2
- "id": "responses:c4m",
3
- "protocol": "openai-responses",
4
- "request": {
5
- "mappings": [
6
- {
7
- "action": "remove",
8
- "path": "max_tokens"
9
- },
10
- {
11
- "action": "remove",
12
- "path": "maxTokens"
13
- },
14
- {
15
- "action": "remove",
16
- "path": "max_output_tokens"
17
- },
18
- {
19
- "action": "remove",
20
- "path": "maxOutputTokens"
21
- },
22
- {
23
- "action": "inject_instruction",
24
- "sourcePath": "instructions",
25
- "targetPath": "input",
26
- "role": "system",
27
- "contentType": "input_text",
28
- "stripHtml": true,
29
- "maxLengthEnv": [
30
- "ROUTECODEX_C4M_INSTRUCTIONS_MAX",
31
- "RCC_C4M_INSTRUCTIONS_MAX",
32
- "ROUTECODEX_COMPAT_INSTRUCTIONS_MAX"
2
+ "id": "responses:c4m",
3
+ "protocol": "openai-responses",
4
+ "request": {
5
+ "mappings": [
6
+ {
7
+ "action": "remove",
8
+ "path": "max_tokens"
9
+ },
10
+ {
11
+ "action": "remove",
12
+ "path": "maxTokens"
13
+ },
14
+ {
15
+ "action": "remove",
16
+ "path": "max_output_tokens"
17
+ },
18
+ {
19
+ "action": "remove",
20
+ "path": "maxOutputTokens"
21
+ },
22
+ {
23
+ "action": "inject_instruction",
24
+ "sourcePath": "instructions",
25
+ "targetPath": "input",
26
+ "role": "system",
27
+ "contentType": "input_text",
28
+ "stripHtml": true,
29
+ "maxLengthEnv": [
30
+ "ROUTECODEX_C4M_INSTRUCTIONS_MAX",
31
+ "RCC_C4M_INSTRUCTIONS_MAX",
32
+ "ROUTECODEX_COMPAT_INSTRUCTIONS_MAX"
33
+ ]
34
+ }
33
35
  ]
34
- }
35
- ]
36
- },
37
- "response": {
38
- "filters": [
39
- {
40
- "action": "rate_limit_text",
41
- "needle": "The Codex-For.ME service is available, but you have reached the request limit"
42
- }
43
- ]
44
- }
36
+ },
37
+ "response": {
38
+ "filters": [
39
+ {
40
+ "action": "rate_limit_text",
41
+ "needle": "The Codex-For.ME service is available, but you have reached the request limit"
42
+ }
43
+ ]
44
+ }
45
45
  }
@@ -358,6 +358,18 @@ export function buildAnthropicResponseFromChat(chatResponse, options) {
358
358
  default: return 'end_turn';
359
359
  }
360
360
  })();
361
+ const promptTokens = usage && typeof usage === 'object'
362
+ ? Number(usage.prompt_tokens ?? usage.input_tokens ?? 0)
363
+ : 0;
364
+ const completionTokens = usage && typeof usage === 'object'
365
+ ? Number(usage.completion_tokens ?? usage.output_tokens ?? 0)
366
+ : 0;
367
+ const usagePayload = (promptTokens || completionTokens)
368
+ ? {
369
+ input_tokens: promptTokens,
370
+ output_tokens: completionTokens
371
+ }
372
+ : undefined;
361
373
  const raw = {
362
374
  id: typeof chatResponse.id === 'string' ? chatResponse.id : `resp_${Date.now()}`,
363
375
  type: 'message',
@@ -366,10 +378,7 @@ export function buildAnthropicResponseFromChat(chatResponse, options) {
366
378
  model: typeof chatResponse.model === 'string' ? chatResponse.model : 'unknown',
367
379
  stop_reason: stopReasonMapped,
368
380
  usage: usage && typeof usage === 'object'
369
- ? {
370
- input_tokens: usage.prompt_tokens ?? 0,
371
- output_tokens: usage.completion_tokens ?? 0
372
- }
381
+ ? usagePayload
373
382
  : undefined
374
383
  };
375
384
  const sanitized = sanitizeAnthropicMessage(raw);
@@ -540,7 +540,7 @@ const captureToolResultsAction = (ctx) => {
540
540
  name: typeof entry?.name === 'string' ? entry.name : undefined
541
541
  }));
542
542
  }
543
- if (ctx.stage === 'request_outbound' &&
543
+ if ((ctx.stage === 'request_outbound' || ctx.stage === 'response_outbound') &&
544
544
  Array.isArray(ctx.state.capturedToolResults) &&
545
545
  ctx.state.capturedToolResults.length) {
546
546
  const metadata = ensureMetadataRecord(ctx.state);
@@ -232,6 +232,7 @@ const ANTHROPIC_POLICY = {
232
232
  ],
233
233
  outbound: [
234
234
  reasoningAction('anthropic_reasoning'),
235
+ { name: 'tools.capture-results' },
235
236
  toolCallNormalizationAction('anthropic_tool_call'),
236
237
  { name: 'metadata.extra-fields', options: { allowedKeys: ANTHROPIC_ALLOWED_FIELDS } }
237
238
  ]
@@ -227,9 +227,48 @@ function normalizeResponsesConfig(provider) {
227
227
  if (!node) {
228
228
  return undefined;
229
229
  }
230
+ const config = {};
230
231
  const rawStyle = typeof node.toolCallIdStyle === 'string' ? node.toolCallIdStyle.trim().toLowerCase() : undefined;
231
232
  if (rawStyle === 'fc' || rawStyle === 'preserve') {
232
- return { toolCallIdStyle: rawStyle };
233
+ config.toolCallIdStyle = rawStyle;
234
+ }
235
+ const streaming = normalizeResponsesStreaming(node.streaming);
236
+ if (streaming) {
237
+ config.streaming = streaming;
238
+ }
239
+ const instructionsMode = normalizeResponsesInstructionsMode(node.instructionsMode);
240
+ if (instructionsMode) {
241
+ config.instructionsMode = instructionsMode;
242
+ }
243
+ return Object.keys(config).length ? config : undefined;
244
+ }
245
+ function normalizeResponsesStreaming(value) {
246
+ if (value === true) {
247
+ return 'always';
248
+ }
249
+ if (value === false) {
250
+ return 'never';
251
+ }
252
+ if (typeof value === 'string') {
253
+ const normalized = value.trim().toLowerCase();
254
+ if (normalized === 'always' || normalized === 'true' || normalized === '1' || normalized === 'yes') {
255
+ return 'always';
256
+ }
257
+ if (normalized === 'never' || normalized === 'false' || normalized === '0' || normalized === 'no') {
258
+ return 'never';
259
+ }
260
+ if (normalized === 'auto') {
261
+ return 'auto';
262
+ }
263
+ }
264
+ return undefined;
265
+ }
266
+ function normalizeResponsesInstructionsMode(value) {
267
+ if (value === 'inline') {
268
+ return 'inline';
269
+ }
270
+ if (typeof value === 'string' && value.trim().toLowerCase() === 'inline') {
271
+ return 'inline';
233
272
  }
234
273
  return undefined;
235
274
  }
@@ -31,21 +31,26 @@ export class RoutingClassifier {
31
31
  const hasOtherToolCall = toolCategories.includes('other');
32
32
  const hasToolCall = toolCategories.length > 0;
33
33
  const reachedLongContext = features.estimatedTokens >= (this.config.longContextThresholdTokens ?? DEFAULT_LONG_CONTEXT_THRESHOLD);
34
- const thinkingKeywordHit = features.hasThinkingKeyword ||
35
- containsKeywords(features.userTextSample, this.config.thinkingKeywords ?? []);
36
34
  const routeHint = typeof features.metadata?.routeHint === 'string'
37
35
  ? features.metadata.routeHint.trim().toLowerCase()
38
36
  : undefined;
39
37
  const websearchKeywordHit = containsKeywords(features.userTextSample, WEBSEARCH_HINT_KEYWORDS);
40
38
  const codingContinuation = hasWriteToolCall || lastToolCategory === 'write';
41
- const thinkingContinuation = hasReadToolCall || lastToolCategory === 'read';
42
- const searchContinuation = features.assistantCalledWebSearchTool === true;
39
+ const readingContinuation = hasReadToolCall || lastToolCategory === 'read';
40
+ const userInputDetected = typeof features.userTextSample === 'string'
41
+ ? features.userTextSample.trim().length > 0
42
+ : false;
43
+ const searchContinuation = features.assistantCalledWebSearchTool === true || hasSearchToolCall;
43
44
  const toolsContinuation = hasOtherToolCall ||
44
45
  searchContinuation ||
45
- (hasToolCall && !hasSearchToolCall && !hasWriteToolCall && !hasReadToolCall);
46
+ (hasToolCall && !hasWriteToolCall && !readingContinuation);
46
47
  const toolContinuationReason = hasOtherToolCall
47
48
  ? formatToolContinuationReason(features.lastAssistantToolName, features.lastAssistantToolDetail)
48
- : 'tools:tool-call-detected';
49
+ : searchContinuation
50
+ ? 'tools:last-tool-search'
51
+ : 'tools:tool-call-detected';
52
+ const thinkingContinuation = readingContinuation || (Boolean(features.hasThinkingKeyword) && !toolsContinuation && !codingContinuation);
53
+ const thinkingReason = readingContinuation ? 'thinking:last-tool-read' : 'thinking:keywords';
49
54
  const evaluationMap = {
50
55
  vision: {
51
56
  triggered: features.hasVisionTool && features.hasImageAttachment,
@@ -64,8 +69,8 @@ export class RoutingClassifier {
64
69
  reason: 'coding:last-tool-write'
65
70
  },
66
71
  thinking: {
67
- triggered: thinkingContinuation || thinkingKeywordHit,
68
- reason: thinkingContinuation ? 'thinking:last-tool-read' : 'thinking:keywords'
72
+ triggered: thinkingContinuation,
73
+ reason: thinkingReason
69
74
  },
70
75
  tools: {
71
76
  triggered: toolsContinuation,
@@ -34,6 +34,7 @@ export declare class VirtualRouterEngine {
34
34
  private recordSelectionSnapshot;
35
35
  private buildStickyPlan;
36
36
  private storeStickyPlan;
37
+ private dropStickyForRequest;
37
38
  private resolveStickyDescriptor;
38
39
  private maybeForceStickyFromHistory;
39
40
  private shouldForceApplyPatchSticky;
@@ -108,6 +108,9 @@ export class VirtualRouterEngine {
108
108
  }
109
109
  }
110
110
  handleProviderError(event) {
111
+ if (event?.runtime?.requestId) {
112
+ this.dropStickyForRequest(event.runtime.requestId);
113
+ }
111
114
  const derived = this.mapProviderError(event);
112
115
  if (!derived) {
113
116
  return;
@@ -225,6 +228,13 @@ export class VirtualRouterEngine {
225
228
  this.stickyPlans.delete(requestId);
226
229
  }
227
230
  }
231
+ dropStickyForRequest(requestId) {
232
+ if (!requestId) {
233
+ return;
234
+ }
235
+ this.stickyPlans.delete(requestId);
236
+ this.selectionHistory.delete(requestId);
237
+ }
228
238
  resolveStickyDescriptor(routeName, features) {
229
239
  if (this.shouldForceApplyPatchSticky(features)) {
230
240
  return { strategy: 'target', rounds: 1, reason: 'apply_patch' };
@@ -68,25 +68,11 @@ const SHELL_WRITE_PATTERNS = [
68
68
  'chown',
69
69
  'chgrp',
70
70
  'tar ',
71
- 'git add',
72
- 'git commit',
73
71
  'git apply',
74
72
  'git am',
75
- 'git rebase',
76
73
  'git checkout',
77
74
  'git merge',
78
- 'patch <<',
79
- 'npm install',
80
- 'pnpm install',
81
- 'yarn add',
82
- 'yarn install',
83
- 'pip install',
84
- 'pip3 install',
85
- 'brew install',
86
- 'cargo add',
87
- 'cargo install',
88
- 'go install',
89
- 'make install'
75
+ 'patch <<'
90
76
  ];
91
77
  const SHELL_SEARCH_PATTERNS = [
92
78
  'rg ',
@@ -105,18 +91,13 @@ const SHELL_SEARCH_PATTERNS = [
105
91
  'codesearch'
106
92
  ];
107
93
  const SHELL_READ_PATTERNS = [
108
- 'ls',
109
- 'dir ',
110
- 'pwd',
111
94
  'cat ',
112
95
  'type ',
113
96
  'head ',
114
97
  'tail ',
98
+ 'stat ',
115
99
  'stat',
116
- 'tree',
117
100
  'wc ',
118
- 'du ',
119
- 'printf "',
120
101
  'python - <<',
121
102
  'python -c',
122
103
  'node - <<',
@@ -522,26 +503,31 @@ function classifyShellCommand(command) {
522
503
  if (SHELL_HEREDOC_PATTERN.test(command)) {
523
504
  return 'write';
524
505
  }
525
- const segments = splitCommandSegments(command).map(stripShellWrapper);
526
- if (segments.some((segment) => matchesAnyPattern(segment, SHELL_WRITE_PATTERNS))) {
506
+ const segments = splitCommandSegments(command)
507
+ .map(stripShellWrapper)
508
+ .filter(Boolean);
509
+ const hasWrite = segments.some((segment) => matchesAnyPattern(segment, SHELL_WRITE_PATTERNS));
510
+ const hasRead = segments.some((segment) => matchesAnyPattern(segment, SHELL_READ_PATTERNS));
511
+ if (hasWrite) {
527
512
  return 'write';
528
513
  }
529
- if (segments.some((segment) => matchesAnyPattern(segment, SHELL_SEARCH_PATTERNS))) {
530
- return 'search';
531
- }
532
- if (segments.some((segment) => matchesAnyPattern(segment, SHELL_READ_PATTERNS))) {
514
+ if (hasRead) {
533
515
  return 'read';
534
516
  }
517
+ const hasSearch = segments.some((segment) => matchesAnyPattern(segment, SHELL_SEARCH_PATTERNS));
518
+ if (hasSearch) {
519
+ return 'search';
520
+ }
535
521
  const stripped = stripShellWrapper(command);
536
522
  if (matchesAnyPattern(stripped, SHELL_WRITE_PATTERNS)) {
537
523
  return 'write';
538
524
  }
539
- if (matchesAnyPattern(stripped, SHELL_SEARCH_PATTERNS)) {
540
- return 'search';
541
- }
542
525
  if (matchesAnyPattern(stripped, SHELL_READ_PATTERNS)) {
543
526
  return 'read';
544
527
  }
528
+ if (matchesAnyPattern(stripped, SHELL_SEARCH_PATTERNS)) {
529
+ return 'search';
530
+ }
545
531
  return 'other';
546
532
  }
547
533
  function splitCommandSegments(command) {
@@ -148,8 +148,11 @@ export interface TargetMetadata {
148
148
  processMode?: 'chat' | 'passthrough';
149
149
  responsesConfig?: ResponsesProviderConfig;
150
150
  }
151
+ export type ResponsesStreamingMode = 'auto' | 'always' | 'never';
151
152
  export interface ResponsesProviderConfig {
152
153
  toolCallIdStyle?: 'fc' | 'preserve';
154
+ streaming?: ResponsesStreamingMode;
155
+ instructionsMode?: 'default' | 'inline';
153
156
  }
154
157
  export declare enum VirtualRouterErrorCode {
155
158
  NO_STANDARDIZED_REQUEST = "NO_STANDARDIZED_REQUEST",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsonstudio/llms",
3
- "version": "0.6.123",
3
+ "version": "0.6.147",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",