llm-relay 0.32.2 → 0.34.0

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.
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Recovering tool calls a HOST failed to parse.
3
+ *
4
+ * A model emits tool calls in its own dialect; the serving host is supposed to parse that into the
5
+ * OpenAI `message.tool_calls` field. NIM and OpenRouter do. Some free hosts do not, and hand back
6
+ * the raw envelope as ordinary assistant TEXT. Before this module the consequence was silent and
7
+ * total: no `tool_calls` ⇒ `stop_reason: end_turn` ⇒ zero `tool_use` blocks ⇒ the validator finds
8
+ * nothing malformed and PASSES ⇒ repair never engages ⇒ the client receives markup it treats as a
9
+ * final answer. Measured 2026-08-08: a task wrote its output correctly and then emitted
10
+ * `</|DSML|parameter></|DSML|invoke></|DSML|tool_calls>` as its entire visible response.
11
+ * See docs/tool-call-dialect-leak.md.
12
+ *
13
+ * ⚠ This is PARSING, not inference — the same side of the repair boundary as fixing args that
14
+ * violate a schema. The envelope set is CLOSED (same reasoning as the effort vocabulary in
15
+ * sync-tiers.mjs and the alias list in authEnv.ts): prose that merely mentions a tool name is not a
16
+ * tool call, and promoting it to one would be fabricating intent, which is the one thing the repair
17
+ * path must never do. A dialect we do not recognize yields `detected` at most, never a guess.
18
+ */
19
+ /** JSON Schema fragment, only the parts used to coerce a stringly-typed parameter. */
20
+ interface SchemaLike {
21
+ type?: unknown;
22
+ properties?: Record<string, SchemaLike>;
23
+ items?: SchemaLike;
24
+ }
25
+ export interface DialectToolCall {
26
+ name: string;
27
+ input: Record<string, unknown>;
28
+ }
29
+ export type DialectOutcome =
30
+ /** No tool-call framing present. The text is just text. */
31
+ {
32
+ status: "none";
33
+ }
34
+ /** Framing found and fully parsed. `text` is what remains after removing the envelope. */
35
+ | {
36
+ status: "parsed";
37
+ calls: DialectToolCall[];
38
+ text: string;
39
+ dialect: string;
40
+ }
41
+ /**
42
+ * Framing markers present but unparseable — truncated mid-stream, or a dialect variant we do not
43
+ * model. NEVER guessed at: the caller fails clean so failover reaches a host that parses.
44
+ */
45
+ | {
46
+ status: "detected";
47
+ dialect: string;
48
+ };
49
+ export declare function detectDialect(text: string): string | null;
50
+ /**
51
+ * How much of a partial text stream is safe to forward, and whether a marker has landed.
52
+ *
53
+ * The streaming-needle problem: a marker arrives split across SSE deltas, so forwarding each delta
54
+ * as it comes would emit the first half of an envelope before we know it is one. `safeLen` is
55
+ * everything except the longest trailing run that could still GROW into a marker, so the caller can
56
+ * stream normally while never emitting into an envelope it is about to capture.
57
+ *
58
+ * ⚠ The holdback is bounded by the longest marker, so ordinary prose containing `<` streams with at
59
+ * most that many characters of lag — it does not degrade into buffering the whole response, which
60
+ * would trade this bug for a latency regression on every tool-bearing request.
61
+ */
62
+ export declare function scanForMarker(text: string): {
63
+ safeLen: number;
64
+ hit: boolean;
65
+ };
66
+ /**
67
+ * Index at which a marker begins, for splitting emitted prose from a captured envelope.
68
+ *
69
+ * ⚠ Backs up over the tag opener. Markers deliberately omit the `<` / `</` prefix so a CLOSING tag
70
+ * matches, which means a `|DSML|tool_calls` hit points one character PAST the real start. Capturing
71
+ * from there left the `<` behind as prose and handed `stripEnvelopes` a tag it no longer recognized,
72
+ * so the envelope's outer wrapper survived into the recovered text.
73
+ */
74
+ export declare function markerStart(text: string): number;
75
+ /**
76
+ * Recover tool calls from a text block a host failed to parse.
77
+ *
78
+ * `schemas` supplies declared parameter types for the dialects that carry parameters as strings;
79
+ * without it those values stay strings and the validator reports the type error, which is the
80
+ * correct visible failure rather than a silent coercion.
81
+ */
82
+ export declare function recoverToolCalls(text: string, schemas?: Map<string, SchemaLike>): DialectOutcome;
83
+ export {};
@@ -0,0 +1,253 @@
1
+ /**
2
+ * Recovering tool calls a HOST failed to parse.
3
+ *
4
+ * A model emits tool calls in its own dialect; the serving host is supposed to parse that into the
5
+ * OpenAI `message.tool_calls` field. NIM and OpenRouter do. Some free hosts do not, and hand back
6
+ * the raw envelope as ordinary assistant TEXT. Before this module the consequence was silent and
7
+ * total: no `tool_calls` ⇒ `stop_reason: end_turn` ⇒ zero `tool_use` blocks ⇒ the validator finds
8
+ * nothing malformed and PASSES ⇒ repair never engages ⇒ the client receives markup it treats as a
9
+ * final answer. Measured 2026-08-08: a task wrote its output correctly and then emitted
10
+ * `</|DSML|parameter></|DSML|invoke></|DSML|tool_calls>` as its entire visible response.
11
+ * See docs/tool-call-dialect-leak.md.
12
+ *
13
+ * ⚠ This is PARSING, not inference — the same side of the repair boundary as fixing args that
14
+ * violate a schema. The envelope set is CLOSED (same reasoning as the effort vocabulary in
15
+ * sync-tiers.mjs and the alias list in authEnv.ts): prose that merely mentions a tool name is not a
16
+ * tool call, and promoting it to one would be fabricating intent, which is the one thing the repair
17
+ * path must never do. A dialect we do not recognize yields `detected` at most, never a guess.
18
+ */
19
+ /**
20
+ * Marker substrings that mean "a tool-call envelope is present". Detection is deliberately broader
21
+ * than parsing: a truncated envelope must still be recognized as one, because returning its
22
+ * fragment as an answer is what made this failure read as "the job died".
23
+ */
24
+ /**
25
+ * ⚠ Markers deliberately omit the `<` / `</` prefix so a CLOSING tag matches too. The truncated
26
+ * tail of a stream is mostly closing tags — one of the two measured failure bodies was exactly
27
+ * `</|DSML|parameter></|DSML|invoke></|DSML|tool_calls>`, which an open-tag-only marker set
28
+ * reports as ordinary prose. That is the single case where detection matters most, because there
29
+ * is no call left to recover and the alternative is handing the fragment back as an answer.
30
+ */
31
+ const DIALECT_MARKERS = [
32
+ { dialect: "deepseek", markers: ["|tool▁calls▁begin|", "|tool▁call▁begin|", "|tool▁sep|", "|tool▁call▁end|", "|tool▁calls▁end|"] },
33
+ { dialect: "dsml", markers: ["|DSML|tool_calls", "|DSML|invoke", "|DSML|parameter"] },
34
+ { dialect: "hermes", markers: ["<tool_call>", "</tool_call>"] },
35
+ { dialect: "xml-invoke", markers: ["<function_calls>", "</function_calls>", "<invoke name=", "</invoke>"] },
36
+ { dialect: "functionary", markers: ["<function="] },
37
+ ];
38
+ export function detectDialect(text) {
39
+ for (const { dialect, markers } of DIALECT_MARKERS) {
40
+ if (markers.some((m) => text.includes(m)))
41
+ return dialect;
42
+ }
43
+ return null;
44
+ }
45
+ const ALL_MARKERS = DIALECT_MARKERS.flatMap((d) => d.markers);
46
+ const LONGEST_MARKER = Math.max(...ALL_MARKERS.map((m) => m.length));
47
+ /**
48
+ * How much of a partial text stream is safe to forward, and whether a marker has landed.
49
+ *
50
+ * The streaming-needle problem: a marker arrives split across SSE deltas, so forwarding each delta
51
+ * as it comes would emit the first half of an envelope before we know it is one. `safeLen` is
52
+ * everything except the longest trailing run that could still GROW into a marker, so the caller can
53
+ * stream normally while never emitting into an envelope it is about to capture.
54
+ *
55
+ * ⚠ The holdback is bounded by the longest marker, so ordinary prose containing `<` streams with at
56
+ * most that many characters of lag — it does not degrade into buffering the whole response, which
57
+ * would trade this bug for a latency regression on every tool-bearing request.
58
+ */
59
+ export function scanForMarker(text) {
60
+ if (ALL_MARKERS.some((m) => text.includes(m)))
61
+ return { safeLen: text.length, hit: true };
62
+ const start = Math.max(0, text.length - (LONGEST_MARKER - 1));
63
+ for (let i = start; i < text.length; i++) {
64
+ const tail = text.slice(i);
65
+ if (ALL_MARKERS.some((m) => m.startsWith(tail)))
66
+ return { safeLen: i, hit: false };
67
+ }
68
+ return { safeLen: text.length, hit: false };
69
+ }
70
+ /**
71
+ * Index at which a marker begins, for splitting emitted prose from a captured envelope.
72
+ *
73
+ * ⚠ Backs up over the tag opener. Markers deliberately omit the `<` / `</` prefix so a CLOSING tag
74
+ * matches, which means a `|DSML|tool_calls` hit points one character PAST the real start. Capturing
75
+ * from there left the `<` behind as prose and handed `stripEnvelopes` a tag it no longer recognized,
76
+ * so the envelope's outer wrapper survived into the recovered text.
77
+ */
78
+ export function markerStart(text) {
79
+ let best = -1;
80
+ for (const m of ALL_MARKERS) {
81
+ const i = text.indexOf(m);
82
+ if (i >= 0 && (best === -1 || i < best))
83
+ best = i;
84
+ }
85
+ if (best <= 0)
86
+ return best;
87
+ if (text.startsWith("</", Math.max(0, best - 2)) && best >= 2)
88
+ return best - 2;
89
+ if (text[best - 1] === "<")
90
+ return best - 1;
91
+ return best;
92
+ }
93
+ /** Coerce a stringly-typed dialect parameter using the tool's declared schema. */
94
+ function coerce(raw, schema) {
95
+ const t = Array.isArray(schema?.type) ? schema?.type[0] : schema?.type;
96
+ const s = raw.trim();
97
+ switch (t) {
98
+ case "number":
99
+ case "integer": {
100
+ const n = Number(s);
101
+ return Number.isFinite(n) ? n : raw;
102
+ }
103
+ case "boolean":
104
+ return s === "true" ? true : s === "false" ? false : raw;
105
+ case "object":
106
+ case "array":
107
+ try {
108
+ return JSON.parse(s);
109
+ }
110
+ catch {
111
+ return raw;
112
+ }
113
+ case "string":
114
+ return raw;
115
+ default:
116
+ // No declared type: keep the literal. Guessing a shape here would silently rewrite an
117
+ // argument the model meant as text.
118
+ return raw;
119
+ }
120
+ }
121
+ /** `{"name": "x", "arguments": {...}}` — the JSON payload shape shared by several dialects. */
122
+ function fromJsonPayload(raw) {
123
+ let j;
124
+ try {
125
+ j = JSON.parse(raw.trim());
126
+ }
127
+ catch {
128
+ return null;
129
+ }
130
+ if (typeof j !== "object" || j === null)
131
+ return null;
132
+ const o = j;
133
+ const name = typeof o.name === "string" ? o.name : null;
134
+ if (!name)
135
+ return null;
136
+ const args = o.arguments ?? o.parameters ?? o.input ?? {};
137
+ if (typeof args === "string") {
138
+ try {
139
+ return { name, input: JSON.parse(args) };
140
+ }
141
+ catch {
142
+ return null;
143
+ }
144
+ }
145
+ if (typeof args !== "object" || args === null)
146
+ return null;
147
+ return { name, input: args };
148
+ }
149
+ /** `<invoke name="x"><parameter name="p">v</parameter></invoke>`, with or without DSML markers. */
150
+ function fromInvokeForm(text, schemas) {
151
+ const calls = [];
152
+ const invoke = /<(?:|DSML|)?invoke\s+name="([^"]+)"\s*>([\s\S]*?)<\/(?:|DSML|)?invoke>/g;
153
+ for (const m of text.matchAll(invoke)) {
154
+ const name = m[1];
155
+ const bodyText = m[2];
156
+ if (!name || bodyText === undefined)
157
+ continue;
158
+ const input = {};
159
+ const param = /<(?:|DSML|)?parameter\s+name="([^"]+)"\s*>([\s\S]*?)<\/(?:|DSML|)?parameter>/g;
160
+ for (const p of bodyText.matchAll(param)) {
161
+ const key = p[1];
162
+ const value = p[2];
163
+ if (!key || value === undefined)
164
+ continue;
165
+ input[key] = coerce(value, schemas.get(name)?.properties?.[key]);
166
+ }
167
+ calls.push({ name, input });
168
+ }
169
+ return calls;
170
+ }
171
+ /** DeepSeek native: `…begin|>function<|tool▁sep|>NAME\n```json\n{…}\n``` `. */
172
+ function fromDeepSeekForm(text) {
173
+ const calls = [];
174
+ const re = /<|tool▁sep|>([^\n<]+)\n+(?:```(?:json)?\n)?([\s\S]*?)(?:\n?```)?\s*<|tool▁call▁end|>/g;
175
+ for (const m of text.matchAll(re)) {
176
+ const name = m[1];
177
+ const payload = m[2];
178
+ if (!name || payload === undefined)
179
+ continue;
180
+ let input = {};
181
+ try {
182
+ const parsed = JSON.parse(payload.trim());
183
+ if (typeof parsed === "object" && parsed !== null)
184
+ input = parsed;
185
+ }
186
+ catch { /* an unparseable body leaves `detected` to the caller */
187
+ return [];
188
+ }
189
+ calls.push({ name: name.trim(), input });
190
+ }
191
+ return calls;
192
+ }
193
+ /** `<tool_call>{json}</tool_call>` (Hermes/Qwen) and `<function=NAME>{json}</function>`. */
194
+ function fromTaggedJsonForms(text) {
195
+ const calls = [];
196
+ for (const m of text.matchAll(/<tool_call>([\s\S]*?)<\/tool_call>/g)) {
197
+ if (m[1] === undefined)
198
+ continue;
199
+ const c = fromJsonPayload(m[1]);
200
+ if (c)
201
+ calls.push(c);
202
+ }
203
+ for (const m of text.matchAll(/<function=([^>]+)>([\s\S]*?)<\/function>/g)) {
204
+ const name = m[1];
205
+ const payload = m[2];
206
+ if (!name || payload === undefined)
207
+ continue;
208
+ let input = {};
209
+ try {
210
+ const parsed = JSON.parse(payload.trim());
211
+ if (typeof parsed === "object" && parsed !== null)
212
+ input = parsed;
213
+ }
214
+ catch {
215
+ continue;
216
+ }
217
+ calls.push({ name: name.trim(), input });
218
+ }
219
+ return calls;
220
+ }
221
+ /** Everything the envelope occupied, so the surviving prose can stay a text block. */
222
+ function stripEnvelopes(text) {
223
+ return text
224
+ .replace(/<(?:|DSML|)?function_calls>[\s\S]*?<\/(?:|DSML|)?function_calls>/g, "")
225
+ .replace(/<|DSML|tool_calls>[\s\S]*?(?:<\/|DSML|tool_calls>|$)/g, "")
226
+ .replace(/<(?:|DSML|)?invoke\s+name="[^"]*"\s*>[\s\S]*?<\/(?:|DSML|)?invoke>/g, "")
227
+ .replace(/<|tool▁calls▁begin|>[\s\S]*?(?:<|tool▁calls▁end|>|$)/g, "")
228
+ .replace(/<tool_call>[\s\S]*?<\/tool_call>/g, "")
229
+ .replace(/<function=[^>]+>[\s\S]*?<\/function>/g, "")
230
+ .replace(/<\/?think>/g, "")
231
+ .trim();
232
+ }
233
+ /**
234
+ * Recover tool calls from a text block a host failed to parse.
235
+ *
236
+ * `schemas` supplies declared parameter types for the dialects that carry parameters as strings;
237
+ * without it those values stay strings and the validator reports the type error, which is the
238
+ * correct visible failure rather than a silent coercion.
239
+ */
240
+ export function recoverToolCalls(text, schemas = new Map()) {
241
+ const dialect = detectDialect(text);
242
+ if (!dialect)
243
+ return { status: "none" };
244
+ const calls = [
245
+ ...fromInvokeForm(text, schemas),
246
+ ...fromDeepSeekForm(text),
247
+ ...fromTaggedJsonForms(text),
248
+ ].filter((c) => c.name.length > 0);
249
+ if (calls.length === 0)
250
+ return { status: "detected", dialect };
251
+ return { status: "parsed", calls, text: stripEnvelopes(text), dialect };
252
+ }
253
+ //# sourceMappingURL=tool-dialects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-dialects.js","sourceRoot":"","sources":["../src/tool-dialects.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llm-relay",
3
- "version": "0.32.2",
3
+ "version": "0.34.0",
4
4
  "description": "Loopback bidirectional Anthropic/OpenAI API proxy with tool-call validation and multi-provider routing.",
5
5
  "type": "module",
6
6
  "engines": {