@openclaw/ai 0.0.0 → 2026.7.1-2
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/LICENSE +21 -0
- package/README.md +27 -3
- package/dist/anthropic-B5gZQM5X.mjs +1383 -0
- package/dist/api-registry-BXYnCOIR.d.mts +33 -0
- package/dist/azure-openai-responses-DNoSk8Uy.mjs +141 -0
- package/dist/azure-openai-responses-client-compat-a_O_GVQV.mjs +41 -0
- package/dist/diagnostics-BaTA9eVl.d.mts +25 -0
- package/dist/diagnostics-COpOtRwq.mjs +36 -0
- package/dist/diagnostics.d.mts +2 -0
- package/dist/diagnostics.mjs +2 -0
- package/dist/env-api-keys-CtMlqaQ4.mjs +171 -0
- package/dist/event-stream-0nZeBKl2.d.mts +26 -0
- package/dist/event-stream-ReMmOTzX.mjs +65 -0
- package/dist/event-stream.d.mts +2 -0
- package/dist/event-stream.mjs +2 -0
- package/dist/github-copilot-headers-BsH5cqGj.mjs +48 -0
- package/dist/google-D6sIQ1bL.mjs +55 -0
- package/dist/google-shared-ZPSl2qTi.mjs +548 -0
- package/dist/google-vertex-rDGwkoZK.mjs +111 -0
- package/dist/hash-CHgqbJmD.mjs +16 -0
- package/dist/headers-B_e4-1J0.mjs +9 -0
- package/dist/host-4t713IeR.mjs +37 -0
- package/dist/index-BoTnz8cv.d.mts +74 -0
- package/dist/index.d.mts +69 -0
- package/dist/index.mjs +7 -0
- package/dist/internal/anthropic.d.mts +234 -0
- package/dist/internal/anthropic.mjs +4 -0
- package/dist/internal/openai.d.mts +244 -0
- package/dist/internal/openai.mjs +7 -0
- package/dist/internal/runtime.d.mts +245 -0
- package/dist/internal/runtime.mjs +176 -0
- package/dist/internal/shared.d.mts +48 -0
- package/dist/internal/shared.mjs +3 -0
- package/dist/json-parse-DzNSIQBq.mjs +134 -0
- package/dist/llm-request-activity-CehVkZP-.mjs +35 -0
- package/dist/mistral-CePVNdws.mjs +563 -0
- package/dist/model-utils-DgmOla96.mjs +69 -0
- package/dist/openai-chatgpt-jwt-DhAAzLkj.mjs +39 -0
- package/dist/openai-chatgpt-responses-DVC4Bk_A.mjs +1068 -0
- package/dist/openai-completions-B9QLIq2U.mjs +844 -0
- package/dist/openai-responses-B6LylGxM.mjs +136 -0
- package/dist/openai-responses-shared-sj2YUPYc.mjs +1944 -0
- package/dist/openai-tool-projection-BknoV11q.mjs +195 -0
- package/dist/providers.d.mts +11 -0
- package/dist/providers.mjs +109 -0
- package/dist/reasoning-tag-text-partitioner-axhAdUwg.mjs +394 -0
- package/dist/sanitize-unicode-BZiVbGwK.d.mts +24 -0
- package/dist/sanitize-unicode-DT5o51ur.mjs +26 -0
- package/dist/src-CZ503MYJ.mjs +99 -0
- package/dist/stream-CREqxHgU.mjs +74 -0
- package/dist/stream-first-event-timeout-RjWszj8c.mjs +106 -0
- package/dist/streaming-byte-guard-BrbkbwUu.mjs +46 -0
- package/dist/tool-schema-json-projection-BXtBc_mD.mjs +74 -0
- package/dist/transform-messages-BhGF_fF4.mjs +507 -0
- package/dist/types-BVVgDSdq.d.mts +1 -0
- package/dist/types-DRgdPqaZ.d.mts +587 -0
- package/dist/types.d.mts +6 -0
- package/dist/types.mjs +5 -0
- package/dist/validation-BDMWOr8d.d.mts +9 -0
- package/dist/validation-FrchoOlv.mjs +199 -0
- package/dist/validation.d.mts +2 -0
- package/dist/validation.mjs +2 -0
- package/npm-shrinkwrap.json +645 -0
- package/package.json +74 -2
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
//#region packages/markdown-core/src/fences.ts
|
|
2
|
+
/** Scans fenced-code spans incrementally so chunking can carry an open fence forward. */
|
|
3
|
+
function scanFenceSpans(buffer, state) {
|
|
4
|
+
const spans = [];
|
|
5
|
+
const startsAtLineStart = state?.atLineStart ?? true;
|
|
6
|
+
let open = state?.open ? {
|
|
7
|
+
...state.open,
|
|
8
|
+
start: 0
|
|
9
|
+
} : void 0;
|
|
10
|
+
let offset = 0;
|
|
11
|
+
while (offset <= buffer.length) {
|
|
12
|
+
const nextNewline = buffer.indexOf("\n", offset);
|
|
13
|
+
const lineEnd = nextNewline === -1 ? buffer.length : nextNewline;
|
|
14
|
+
const line = buffer.slice(offset, lineEnd).replace(/\r$/, "");
|
|
15
|
+
const match = line.match(/^( {0,3})(`{3,}|~{3,})(.*)$/);
|
|
16
|
+
if (match && (offset > 0 || startsAtLineStart)) {
|
|
17
|
+
const indent = match[1];
|
|
18
|
+
const marker = match[2];
|
|
19
|
+
const markerChar = marker[0];
|
|
20
|
+
const markerLen = marker.length;
|
|
21
|
+
if (!open) open = {
|
|
22
|
+
start: offset,
|
|
23
|
+
markerChar,
|
|
24
|
+
markerLen,
|
|
25
|
+
openLine: line,
|
|
26
|
+
marker,
|
|
27
|
+
indent
|
|
28
|
+
};
|
|
29
|
+
else if (open.markerChar === markerChar && markerLen >= open.markerLen && /^[ \t]*$/.test(match[3])) {
|
|
30
|
+
const end = lineEnd;
|
|
31
|
+
spans.push({
|
|
32
|
+
start: open.start,
|
|
33
|
+
end,
|
|
34
|
+
openLine: open.openLine,
|
|
35
|
+
marker: open.marker,
|
|
36
|
+
indent: open.indent
|
|
37
|
+
});
|
|
38
|
+
open = void 0;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (nextNewline === -1) break;
|
|
42
|
+
offset = nextNewline + 1;
|
|
43
|
+
}
|
|
44
|
+
if (open) spans.push({
|
|
45
|
+
start: open.start,
|
|
46
|
+
end: buffer.length,
|
|
47
|
+
openLine: open.openLine,
|
|
48
|
+
marker: open.marker,
|
|
49
|
+
indent: open.indent
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
spans,
|
|
53
|
+
state: {
|
|
54
|
+
atLineStart: buffer.length === 0 ? startsAtLineStart : buffer.endsWith("\n"),
|
|
55
|
+
...open ? { open: {
|
|
56
|
+
markerChar: open.markerChar,
|
|
57
|
+
markerLen: open.markerLen,
|
|
58
|
+
openLine: open.openLine,
|
|
59
|
+
marker: open.marker,
|
|
60
|
+
indent: open.indent
|
|
61
|
+
} } : {}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region packages/markdown-core/src/code-spans.ts
|
|
67
|
+
/** Creates the carry-forward state used when scanning inline code across chunks. */
|
|
68
|
+
function createInlineCodeState() {
|
|
69
|
+
return {
|
|
70
|
+
open: false,
|
|
71
|
+
ticks: 0
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/** Builds a lookup for fenced and inline code spans while preserving scanner state. */
|
|
75
|
+
function buildCodeSpanIndex(text, inlineState, fenceState) {
|
|
76
|
+
const { spans: fenceSpans, state: nextFenceState } = scanFenceSpans(text, fenceState);
|
|
77
|
+
const { spans: inlineSpans, state: nextInlineState } = parseInlineCodeSpans(text, fenceSpans, inlineState ? {
|
|
78
|
+
open: inlineState.open,
|
|
79
|
+
ticks: inlineState.ticks
|
|
80
|
+
} : createInlineCodeState());
|
|
81
|
+
return {
|
|
82
|
+
inlineState: nextInlineState,
|
|
83
|
+
fenceState: nextFenceState,
|
|
84
|
+
isInside: (index) => isInsideFenceSpan(index, fenceSpans) || isInsideInlineSpan(index, inlineSpans)
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function parseInlineCodeSpans(text, fenceSpans, initialState) {
|
|
88
|
+
const spans = [];
|
|
89
|
+
let open = initialState.open;
|
|
90
|
+
let ticks = initialState.ticks;
|
|
91
|
+
let openStart = open ? 0 : -1;
|
|
92
|
+
let i = 0;
|
|
93
|
+
while (i < text.length) {
|
|
94
|
+
const fence = findFenceSpanAtInclusive(fenceSpans, i);
|
|
95
|
+
if (fence) {
|
|
96
|
+
i = fence.end;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (text[i] !== "`") {
|
|
100
|
+
i += 1;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const runStart = i;
|
|
104
|
+
let runLength = 0;
|
|
105
|
+
while (i < text.length && text[i] === "`") {
|
|
106
|
+
runLength += 1;
|
|
107
|
+
i += 1;
|
|
108
|
+
}
|
|
109
|
+
if (!open) {
|
|
110
|
+
open = true;
|
|
111
|
+
ticks = runLength;
|
|
112
|
+
openStart = runStart;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (runLength === ticks) {
|
|
116
|
+
spans.push([openStart, i]);
|
|
117
|
+
open = false;
|
|
118
|
+
ticks = 0;
|
|
119
|
+
openStart = -1;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (open) spans.push([openStart, text.length]);
|
|
123
|
+
return {
|
|
124
|
+
spans,
|
|
125
|
+
state: {
|
|
126
|
+
open,
|
|
127
|
+
ticks
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function findFenceSpanAtInclusive(spans, index) {
|
|
132
|
+
return spans.find((span) => index >= span.start && index < span.end);
|
|
133
|
+
}
|
|
134
|
+
function isInsideFenceSpan(index, spans) {
|
|
135
|
+
return spans.some((span) => index >= span.start && index < span.end);
|
|
136
|
+
}
|
|
137
|
+
function isInsideInlineSpan(index, spans) {
|
|
138
|
+
return spans.some(([start, end]) => index >= start && index < end);
|
|
139
|
+
}
|
|
140
|
+
//#endregion
|
|
141
|
+
//#region packages/ai/src/utils/reasoning-tag-text-partitioner.ts
|
|
142
|
+
const REASONING_TAG_RE = /<\s*(\/?)\s*(?:(?:antml:|mm:)?(?:think(?:ing)?|thought|reasoning)|antthinking)\b[^<>]*>/gi;
|
|
143
|
+
const REASONING_TAG_NAMES = [
|
|
144
|
+
"think",
|
|
145
|
+
"thinking",
|
|
146
|
+
"thought",
|
|
147
|
+
"reasoning",
|
|
148
|
+
"antthinking",
|
|
149
|
+
"antml:think",
|
|
150
|
+
"antml:thinking",
|
|
151
|
+
"antml:thought",
|
|
152
|
+
"antml:reasoning",
|
|
153
|
+
"mm:think",
|
|
154
|
+
"mm:thinking",
|
|
155
|
+
"mm:thought",
|
|
156
|
+
"mm:reasoning"
|
|
157
|
+
];
|
|
158
|
+
function createReasoningTagTextPartitioner() {
|
|
159
|
+
let buffer = "";
|
|
160
|
+
let reasoningDepth = 0;
|
|
161
|
+
let strictMode = false;
|
|
162
|
+
let emittedVisibleText = false;
|
|
163
|
+
let inlineCodeState = createInlineCodeState();
|
|
164
|
+
let fenceState;
|
|
165
|
+
let hiddenInlineCodeState = createInlineCodeState();
|
|
166
|
+
let hiddenFenceState;
|
|
167
|
+
let recoverableOpenTagText;
|
|
168
|
+
const consume = (final, recoverFullUnclosed) => {
|
|
169
|
+
const output = [];
|
|
170
|
+
const emit = (kind, text) => {
|
|
171
|
+
if (!text) return;
|
|
172
|
+
if (kind === "text" && text.trim().length > 0) emittedVisibleText = true;
|
|
173
|
+
if (kind === "text") {
|
|
174
|
+
const nextCode = buildCodeSpanIndex(text, inlineCodeState, fenceState);
|
|
175
|
+
inlineCodeState = nextCode.inlineState;
|
|
176
|
+
fenceState = nextCode.fenceState;
|
|
177
|
+
} else {
|
|
178
|
+
const nextCode = buildCodeSpanIndex(text, hiddenInlineCodeState, hiddenFenceState);
|
|
179
|
+
hiddenInlineCodeState = nextCode.inlineState;
|
|
180
|
+
hiddenFenceState = nextCode.fenceState;
|
|
181
|
+
}
|
|
182
|
+
const previous = output[output.length - 1];
|
|
183
|
+
if (previous?.kind === kind) {
|
|
184
|
+
previous.text += text;
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
output.push({
|
|
188
|
+
kind,
|
|
189
|
+
text
|
|
190
|
+
});
|
|
191
|
+
};
|
|
192
|
+
while (buffer) {
|
|
193
|
+
const activeInlineCodeState = reasoningDepth === 0 ? inlineCodeState : hiddenInlineCodeState;
|
|
194
|
+
const activeFenceState = reasoningDepth === 0 ? fenceState : hiddenFenceState;
|
|
195
|
+
const codeSpans = buildCodeSpanIndex(buffer, activeInlineCodeState, activeFenceState);
|
|
196
|
+
const hasUnclosedCode = reasoningDepth === 0 && Boolean(codeSpans.inlineState.open || codeSpans.fenceState.open);
|
|
197
|
+
const hasRawReasoning = hasRawReasoningTag(buffer);
|
|
198
|
+
const tag = findNextReasoningTag(buffer, (index) => final && hasUnclosedCode && hasRawReasoning ? false : codeSpans.isInside(index));
|
|
199
|
+
if (!tag) {
|
|
200
|
+
if (final) {
|
|
201
|
+
const recoverAsText = reasoningDepth > 0 && recoverFullUnclosed && !hasRawReasoningCloseTag(buffer);
|
|
202
|
+
const recoveredText = recoverAsText && recoverableOpenTagText ? recoverableOpenTagText + buffer : buffer;
|
|
203
|
+
emit(reasoningDepth > 0 && !recoverAsText ? "thinking" : "text", recoveredText);
|
|
204
|
+
buffer = "";
|
|
205
|
+
reasoningDepth = 0;
|
|
206
|
+
recoverableOpenTagText = void 0;
|
|
207
|
+
return output;
|
|
208
|
+
}
|
|
209
|
+
if (reasoningDepth > 0 && recoverFullUnclosed && (!emittedVisibleText || recoverableOpenTagText)) return output;
|
|
210
|
+
if (hasUnclosedCode && hasRawReasoning) {
|
|
211
|
+
const openCodeIndex = inlineCodeState.open || fenceState?.open ? 0 : findOpenCodeContextStart(buffer);
|
|
212
|
+
if (openCodeIndex !== -1) {
|
|
213
|
+
emit("text", buffer.slice(0, openCodeIndex));
|
|
214
|
+
buffer = buffer.slice(openCodeIndex);
|
|
215
|
+
return output;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const trailingFenceStart = findTrailingFenceFragmentStart(buffer, activeInlineCodeState, activeFenceState);
|
|
219
|
+
if (trailingFenceStart !== -1) {
|
|
220
|
+
emit(reasoningDepth > 0 ? "thinking" : "text", buffer.slice(0, trailingFenceStart));
|
|
221
|
+
buffer = buffer.slice(trailingFenceStart);
|
|
222
|
+
return output;
|
|
223
|
+
}
|
|
224
|
+
const keepFrom = reasoningTagPrefixSuffixIndex(buffer, (index) => codeSpans.isInside(index));
|
|
225
|
+
if (keepFrom === -1) {
|
|
226
|
+
emit(reasoningDepth > 0 ? "thinking" : "text", buffer);
|
|
227
|
+
buffer = "";
|
|
228
|
+
return output;
|
|
229
|
+
}
|
|
230
|
+
if (reasoningDepth === 0 && keepFrom > 0 && buffer.slice(0, keepFrom).trim().length > 0 && isReasoningCloseTagPrefix(buffer.slice(keepFrom))) return output;
|
|
231
|
+
if (keepFrom > 0) {
|
|
232
|
+
emit(reasoningDepth > 0 ? "thinking" : "text", buffer.slice(0, keepFrom));
|
|
233
|
+
buffer = buffer.slice(keepFrom);
|
|
234
|
+
}
|
|
235
|
+
return output;
|
|
236
|
+
}
|
|
237
|
+
const beforeTag = buffer.slice(0, tag.index);
|
|
238
|
+
const afterTag = buffer.slice(tag.index + tag.text.length);
|
|
239
|
+
if (tag.isClose && reasoningDepth === 0) {
|
|
240
|
+
if (recoverFullUnclosed && beforeTag.trim().length > 0 && afterTag.trim().length > 0) {
|
|
241
|
+
emit("text", beforeTag + tag.text);
|
|
242
|
+
buffer = afterTag;
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (beforeTag.trim().length > 0 && afterTag.trim().length === 0 && !final) return output;
|
|
246
|
+
if (beforeTag.trim().length === 0 || afterTag.trim().length === 0) emit("text", beforeTag);
|
|
247
|
+
buffer = afterTag;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
emit(reasoningDepth > 0 ? "thinking" : "text", buffer.slice(0, tag.index));
|
|
251
|
+
buffer = afterTag;
|
|
252
|
+
if (tag.isClose) {
|
|
253
|
+
reasoningDepth = Math.max(0, reasoningDepth - 1);
|
|
254
|
+
if (reasoningDepth === 0) {
|
|
255
|
+
recoverableOpenTagText = void 0;
|
|
256
|
+
hiddenInlineCodeState = createInlineCodeState();
|
|
257
|
+
hiddenFenceState = void 0;
|
|
258
|
+
}
|
|
259
|
+
} else {
|
|
260
|
+
if (reasoningDepth === 0) {
|
|
261
|
+
recoverableOpenTagText = recoverFullUnclosed && emittedVisibleText ? tag.text : void 0;
|
|
262
|
+
hiddenInlineCodeState = createInlineCodeState();
|
|
263
|
+
hiddenFenceState = void 0;
|
|
264
|
+
}
|
|
265
|
+
reasoningDepth += 1;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return output;
|
|
269
|
+
};
|
|
270
|
+
return {
|
|
271
|
+
markStrict() {
|
|
272
|
+
strictMode = true;
|
|
273
|
+
},
|
|
274
|
+
push(chunk) {
|
|
275
|
+
strictMode = true;
|
|
276
|
+
buffer += chunk;
|
|
277
|
+
return consume(false, false);
|
|
278
|
+
},
|
|
279
|
+
pushVisible(chunk) {
|
|
280
|
+
buffer += chunk;
|
|
281
|
+
return consume(false, true);
|
|
282
|
+
},
|
|
283
|
+
flush() {
|
|
284
|
+
return consume(true, !strictMode);
|
|
285
|
+
},
|
|
286
|
+
hasPending() {
|
|
287
|
+
return buffer.length > 0 || reasoningDepth > 0;
|
|
288
|
+
},
|
|
289
|
+
isInsideReasoning() {
|
|
290
|
+
return reasoningDepth > 0;
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
function hasRawReasoningTag(text) {
|
|
295
|
+
REASONING_TAG_RE.lastIndex = 0;
|
|
296
|
+
return REASONING_TAG_RE.test(text);
|
|
297
|
+
}
|
|
298
|
+
function hasRawReasoningCloseTag(text) {
|
|
299
|
+
REASONING_TAG_RE.lastIndex = 0;
|
|
300
|
+
for (;;) {
|
|
301
|
+
const match = REASONING_TAG_RE.exec(text);
|
|
302
|
+
if (!match) return false;
|
|
303
|
+
if (match[1] === "/") return true;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function findNextReasoningTag(text, isIndexInsideCode) {
|
|
307
|
+
REASONING_TAG_RE.lastIndex = 0;
|
|
308
|
+
for (;;) {
|
|
309
|
+
const match = REASONING_TAG_RE.exec(text);
|
|
310
|
+
if (!match) return null;
|
|
311
|
+
if (!isIndexInsideCode(match.index)) return {
|
|
312
|
+
index: match.index,
|
|
313
|
+
text: match[0],
|
|
314
|
+
isClose: match[1] === "/"
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
function reasoningTagPrefixSuffixIndex(text, isIndexInsideCode) {
|
|
319
|
+
for (let index = text.lastIndexOf("<"); index >= 0;) {
|
|
320
|
+
if (!isIndexInsideCode(index) && isReasoningTagPrefix(text.slice(index))) return index;
|
|
321
|
+
if (index === 0) break;
|
|
322
|
+
index = text.lastIndexOf("<", index - 1);
|
|
323
|
+
}
|
|
324
|
+
return -1;
|
|
325
|
+
}
|
|
326
|
+
function isReasoningTagPrefix(text) {
|
|
327
|
+
const name = normalizeReasoningTagPrefixName(text);
|
|
328
|
+
return REASONING_TAG_NAMES.some((tagName) => {
|
|
329
|
+
if (tagName.startsWith(name)) return true;
|
|
330
|
+
if (!name.startsWith(tagName)) return false;
|
|
331
|
+
const rest = name.slice(tagName.length);
|
|
332
|
+
return rest.length === 0 || /^[\s/>]/.test(rest);
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
function isReasoningCloseTagPrefix(text) {
|
|
336
|
+
return text.replace(/^<\s*/, "<").replace(/^<\s*\//, "</").replace(/^<\/\s*/, "</").toLowerCase().startsWith("</") && isReasoningTagPrefix(text);
|
|
337
|
+
}
|
|
338
|
+
function normalizeReasoningTagPrefixName(text) {
|
|
339
|
+
const normalized = text.replace(/^<\s*/, "<").replace(/^<\s*\//, "</").replace(/^<\/\s*/, "</").toLowerCase();
|
|
340
|
+
return (normalized.startsWith("</") ? normalized.slice(2) : normalized.slice(1)).trimStart();
|
|
341
|
+
}
|
|
342
|
+
function findOpenCodeContextStart(text) {
|
|
343
|
+
const fence = findOpenFenceStart(text);
|
|
344
|
+
const inline = findOpenInlineCodeStart(text);
|
|
345
|
+
if (fence === -1) return inline;
|
|
346
|
+
if (inline === -1) return fence;
|
|
347
|
+
return Math.min(fence, inline);
|
|
348
|
+
}
|
|
349
|
+
function findOpenInlineCodeStart(text) {
|
|
350
|
+
let openStart = -1;
|
|
351
|
+
let openTicks = 0;
|
|
352
|
+
let index = 0;
|
|
353
|
+
while (index < text.length) {
|
|
354
|
+
if (text[index] !== "`") {
|
|
355
|
+
index += 1;
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
const runStart = index;
|
|
359
|
+
let runLength = 0;
|
|
360
|
+
while (index < text.length && text[index] === "`") {
|
|
361
|
+
runLength += 1;
|
|
362
|
+
index += 1;
|
|
363
|
+
}
|
|
364
|
+
if (openStart === -1) {
|
|
365
|
+
openStart = runStart;
|
|
366
|
+
openTicks = runLength;
|
|
367
|
+
} else if (runLength === openTicks) {
|
|
368
|
+
openStart = -1;
|
|
369
|
+
openTicks = 0;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return openStart;
|
|
373
|
+
}
|
|
374
|
+
function findOpenFenceStart(text) {
|
|
375
|
+
const fenceRe = /(^|\n)(```|~~~)[^\n]*(?:\n|$)/g;
|
|
376
|
+
let open = null;
|
|
377
|
+
for (const match of text.matchAll(fenceRe)) {
|
|
378
|
+
const index = (match.index ?? 0) + match[1].length;
|
|
379
|
+
const marker = match[2] ?? "";
|
|
380
|
+
if (open !== null && open.marker === marker) open = null;
|
|
381
|
+
else if (!open) open = {
|
|
382
|
+
marker,
|
|
383
|
+
index
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
return open?.index ?? -1;
|
|
387
|
+
}
|
|
388
|
+
function findTrailingFenceFragmentStart(text, inlineState, fenceState) {
|
|
389
|
+
if (inlineState.open || fenceState?.open) return -1;
|
|
390
|
+
const lineStart = Math.max(text.lastIndexOf("\n") + 1, 0);
|
|
391
|
+
return text.slice(lineStart).match(/^( {0,3})(`{1,2}|~{1,2})$/) ? lineStart : -1;
|
|
392
|
+
}
|
|
393
|
+
//#endregion
|
|
394
|
+
export { createReasoningTagTextPartitioner as t };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region packages/ai/src/utils/sanitize-unicode.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Removes unpaired Unicode surrogate characters from a string.
|
|
4
|
+
*
|
|
5
|
+
* Unpaired surrogates (high surrogates 0xD800-0xDBFF without matching low surrogates 0xDC00-0xDFFF,
|
|
6
|
+
* or vice versa) cause JSON serialization errors in many API providers.
|
|
7
|
+
*
|
|
8
|
+
* Valid emoji and other characters outside the Basic Multilingual Plane use properly paired
|
|
9
|
+
* surrogates and will NOT be affected by this function.
|
|
10
|
+
*
|
|
11
|
+
* @param text - The text to sanitize
|
|
12
|
+
* @returns The sanitized text with unpaired surrogates removed
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* // Valid emoji (properly paired surrogates) are preserved
|
|
16
|
+
* sanitizeSurrogates("Hello 🙈 World") // => "Hello 🙈 World"
|
|
17
|
+
*
|
|
18
|
+
* // Unpaired high surrogate is removed
|
|
19
|
+
* const unpaired = String.fromCharCode(0xD83D); // high surrogate without low
|
|
20
|
+
* sanitizeSurrogates(`Text ${unpaired} here`) // => "Text here"
|
|
21
|
+
*/
|
|
22
|
+
declare function sanitizeSurrogates(text: string): string;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { sanitizeSurrogates as t };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region packages/ai/src/utils/sanitize-unicode.ts
|
|
2
|
+
/**
|
|
3
|
+
* Removes unpaired Unicode surrogate characters from a string.
|
|
4
|
+
*
|
|
5
|
+
* Unpaired surrogates (high surrogates 0xD800-0xDBFF without matching low surrogates 0xDC00-0xDFFF,
|
|
6
|
+
* or vice versa) cause JSON serialization errors in many API providers.
|
|
7
|
+
*
|
|
8
|
+
* Valid emoji and other characters outside the Basic Multilingual Plane use properly paired
|
|
9
|
+
* surrogates and will NOT be affected by this function.
|
|
10
|
+
*
|
|
11
|
+
* @param text - The text to sanitize
|
|
12
|
+
* @returns The sanitized text with unpaired surrogates removed
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* // Valid emoji (properly paired surrogates) are preserved
|
|
16
|
+
* sanitizeSurrogates("Hello 🙈 World") // => "Hello 🙈 World"
|
|
17
|
+
*
|
|
18
|
+
* // Unpaired high surrogate is removed
|
|
19
|
+
* const unpaired = String.fromCharCode(0xD83D); // high surrogate without low
|
|
20
|
+
* sanitizeSurrogates(`Text ${unpaired} here`) // => "Text here"
|
|
21
|
+
*/
|
|
22
|
+
function sanitizeSurrogates(text) {
|
|
23
|
+
return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "");
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { sanitizeSurrogates as t };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import "./validation-FrchoOlv.mjs";
|
|
2
|
+
//#region packages/llm-core/src/model-contracts/anthropic.ts
|
|
3
|
+
function normalizeClaudeModelId(modelId) {
|
|
4
|
+
const normalized = modelId?.trim().toLowerCase() ?? "";
|
|
5
|
+
return (normalized.startsWith("anthropic/") ? normalized.slice(10) : normalized).replace(/[._\s]+/g, "-");
|
|
6
|
+
}
|
|
7
|
+
const CLAUDE_FABLE_5_THINKING_PROFILE = {
|
|
8
|
+
levels: [
|
|
9
|
+
{ id: "off" },
|
|
10
|
+
{ id: "minimal" },
|
|
11
|
+
{ id: "low" },
|
|
12
|
+
{ id: "medium" },
|
|
13
|
+
{ id: "high" },
|
|
14
|
+
{ id: "xhigh" },
|
|
15
|
+
{ id: "adaptive" },
|
|
16
|
+
{ id: "max" }
|
|
17
|
+
],
|
|
18
|
+
defaultLevel: "high",
|
|
19
|
+
preserveWhenCatalogReasoningFalse: true
|
|
20
|
+
};
|
|
21
|
+
const CLAUDE_SONNET_5_THINKING_PROFILE = {
|
|
22
|
+
levels: [
|
|
23
|
+
{ id: "off" },
|
|
24
|
+
{ id: "minimal" },
|
|
25
|
+
{ id: "low" },
|
|
26
|
+
{ id: "medium" },
|
|
27
|
+
{ id: "high" },
|
|
28
|
+
{ id: "xhigh" },
|
|
29
|
+
{ id: "adaptive" },
|
|
30
|
+
{ id: "max" }
|
|
31
|
+
],
|
|
32
|
+
defaultLevel: "high"
|
|
33
|
+
};
|
|
34
|
+
/** Resolve the canonical normalized Claude model id for one runtime model ref. */
|
|
35
|
+
function resolveClaudeModelIdentity(ref) {
|
|
36
|
+
const normalized = normalizeClaudeModelId((typeof ref.params?.canonicalModelId === "string" ? ref.params.canonicalModelId : void 0) ?? ref.id);
|
|
37
|
+
const match = /(?:^|[-/])claude-/.exec(normalized);
|
|
38
|
+
return match ? normalized.slice((match.index ?? 0) + (match[0].startsWith("claude-") ? 0 : 1)) : normalized;
|
|
39
|
+
}
|
|
40
|
+
/** Resolve Claude Fable 5 through direct ids, cloud ids, or deployment metadata. */
|
|
41
|
+
function resolveClaudeFable5ModelIdentity(ref) {
|
|
42
|
+
const normalized = resolveClaudeModelIdentity(ref);
|
|
43
|
+
const match = /(?:^|-)claude-fable-5(?=$|[^a-z0-9])/.exec(normalized);
|
|
44
|
+
if (!match) return;
|
|
45
|
+
return normalized.slice((match.index ?? 0) + (match[0].startsWith("-") ? 1 : 0));
|
|
46
|
+
}
|
|
47
|
+
/** Resolve Claude Mythos 5 through direct ids, cloud ids, or deployment metadata. */
|
|
48
|
+
function resolveClaudeMythos5ModelIdentity(ref) {
|
|
49
|
+
const normalized = resolveClaudeModelIdentity(ref);
|
|
50
|
+
const match = /(?:^|-)claude-mythos-5(?=$|[^a-z0-9])/.exec(normalized);
|
|
51
|
+
if (!match) return;
|
|
52
|
+
return normalized.slice((match.index ?? 0) + (match[0].startsWith("-") ? 1 : 0));
|
|
53
|
+
}
|
|
54
|
+
/** Return whether a Claude model requires adaptive thinking instead of manual budgets. */
|
|
55
|
+
function requiresClaudeMandatoryAdaptiveThinking(ref) {
|
|
56
|
+
const modelId = resolveClaudeModelIdentity(ref);
|
|
57
|
+
return resolveClaudeFable5ModelIdentity(ref) !== void 0 || resolveClaudeMythos5ModelIdentity(ref) !== void 0 || /(?:^|-)claude-mythos-preview(?=$|[^a-z0-9])/.test(modelId);
|
|
58
|
+
}
|
|
59
|
+
/** Resolve Claude Sonnet 5 through direct ids, cloud ids, or deployment metadata. */
|
|
60
|
+
function resolveClaudeSonnet5ModelIdentity(ref) {
|
|
61
|
+
const normalized = resolveClaudeModelIdentity(ref);
|
|
62
|
+
const match = /(?:^|-)claude-sonnet-5(?=$|[^a-z0-9])/.exec(normalized);
|
|
63
|
+
if (!match) return;
|
|
64
|
+
return normalized.slice((match.index ?? 0) + (match[0].startsWith("-") ? 1 : 0));
|
|
65
|
+
}
|
|
66
|
+
/** Return whether a Claude model supports adaptive thinking. */
|
|
67
|
+
function supportsClaudeAdaptiveThinking(ref) {
|
|
68
|
+
const modelId = resolveClaudeModelIdentity(ref);
|
|
69
|
+
return /(?:^|-)claude-(?:fable-5|mythos-(?:5|preview)|opus-4-(?:6|7|8)|sonnet-(?:5|4-6))(?=$|[^a-z0-9])/.test(modelId);
|
|
70
|
+
}
|
|
71
|
+
/** Return whether a Claude model supports native max effort. */
|
|
72
|
+
function supportsClaudeNativeMaxEffort(ref) {
|
|
73
|
+
const modelId = resolveClaudeModelIdentity(ref);
|
|
74
|
+
return /(?:^|-)claude-(?:fable-5|mythos-5|opus-4-(?:6|7|8)|sonnet-(?:5|4-6))(?=$|[^a-z0-9])/.test(modelId);
|
|
75
|
+
}
|
|
76
|
+
/** Return whether a Claude model supports native xhigh effort. */
|
|
77
|
+
function supportsClaudeNativeXhighEffort(ref) {
|
|
78
|
+
const modelId = resolveClaudeModelIdentity(ref);
|
|
79
|
+
return /(?:^|-)claude-(?:fable-5|mythos-5|opus-4-(?:7|8)|sonnet-5)(?=$|[^a-z0-9])/.test(modelId);
|
|
80
|
+
}
|
|
81
|
+
/** Return whether a Claude model rejects caller-selected sampling parameters. */
|
|
82
|
+
function requiresClaudeDefaultSampling(ref) {
|
|
83
|
+
const modelId = resolveClaudeModelIdentity(ref);
|
|
84
|
+
return supportsClaudeNativeXhighEffort(ref) || /(?:^|-)claude-mythos-preview(?=$|[^a-z0-9])/.test(modelId);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Fill native Claude effort mappings only when the provider did not publish a
|
|
88
|
+
* narrower route-specific contract.
|
|
89
|
+
*/
|
|
90
|
+
function resolveClaudeNativeThinkingLevelMap(ref) {
|
|
91
|
+
if (ref.thinkingLevelMap !== void 0) return ref.thinkingLevelMap;
|
|
92
|
+
if (!supportsClaudeNativeMaxEffort(ref)) return;
|
|
93
|
+
return {
|
|
94
|
+
xhigh: supportsClaudeNativeXhighEffort(ref) ? "xhigh" : null,
|
|
95
|
+
max: "max"
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
export { resolveClaudeFable5ModelIdentity as a, resolveClaudeNativeThinkingLevelMap as c, supportsClaudeNativeMaxEffort as d, supportsClaudeNativeXhighEffort as f, requiresClaudeMandatoryAdaptiveThinking as i, resolveClaudeSonnet5ModelIdentity as l, CLAUDE_SONNET_5_THINKING_PROFILE as n, resolveClaudeModelIdentity as o, requiresClaudeDefaultSampling as r, resolveClaudeMythos5ModelIdentity as s, CLAUDE_FABLE_5_THINKING_PROFILE as t, supportsClaudeAdaptiveThinking as u };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
//#region packages/ai/src/api-registry.ts
|
|
2
|
+
function wrapStream(api, stream) {
|
|
3
|
+
return (model, context, options) => {
|
|
4
|
+
if (model.api !== api) throw new Error(`Mismatched api: ${model.api} expected ${api}`);
|
|
5
|
+
return stream(model, context, options);
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
function wrapStreamSimple(api, streamSimple) {
|
|
9
|
+
return (model, context, options) => {
|
|
10
|
+
if (model.api !== api) throw new Error(`Mismatched api: ${model.api} expected ${api}`);
|
|
11
|
+
return streamSimple(model, context, options);
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/** Creates an isolated provider registry for one runtime or tenant. */
|
|
15
|
+
function createApiRegistry() {
|
|
16
|
+
const providers = /* @__PURE__ */ new Map();
|
|
17
|
+
function registerApiProvider(provider, sourceId) {
|
|
18
|
+
providers.set(provider.api, {
|
|
19
|
+
provider: {
|
|
20
|
+
api: provider.api,
|
|
21
|
+
stream: wrapStream(provider.api, provider.stream),
|
|
22
|
+
streamSimple: wrapStreamSimple(provider.api, provider.streamSimple)
|
|
23
|
+
},
|
|
24
|
+
sourceId
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function getApiProvider(api) {
|
|
28
|
+
return providers.get(api)?.provider;
|
|
29
|
+
}
|
|
30
|
+
function getApiProviders() {
|
|
31
|
+
return Array.from(providers.values(), (entry) => entry.provider);
|
|
32
|
+
}
|
|
33
|
+
function unregisterApiProviders(sourceId) {
|
|
34
|
+
for (const [api, entry] of providers.entries()) if (entry.sourceId === sourceId) providers.delete(api);
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
registerApiProvider,
|
|
38
|
+
getApiProvider,
|
|
39
|
+
getApiProviders,
|
|
40
|
+
unregisterApiProviders,
|
|
41
|
+
clearApiProviders: () => providers.clear()
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region packages/ai/src/stream.ts
|
|
46
|
+
/** Creates an isolated LLM runtime backed by the supplied provider registry. */
|
|
47
|
+
function createLlmRuntime(registry = createApiRegistry()) {
|
|
48
|
+
function resolveApiProvider(api) {
|
|
49
|
+
const provider = registry.getApiProvider(api);
|
|
50
|
+
if (!provider) throw new Error(`No API provider registered for api: ${api}`);
|
|
51
|
+
return provider;
|
|
52
|
+
}
|
|
53
|
+
function stream(model, context, options) {
|
|
54
|
+
return resolveApiProvider(model.api).stream(model, context, options);
|
|
55
|
+
}
|
|
56
|
+
async function complete(model, context, options) {
|
|
57
|
+
return stream(model, context, options).result();
|
|
58
|
+
}
|
|
59
|
+
function streamSimple(model, context, options) {
|
|
60
|
+
return resolveApiProvider(model.api).streamSimple(model, context, options);
|
|
61
|
+
}
|
|
62
|
+
async function completeSimple(model, context, options) {
|
|
63
|
+
return streamSimple(model, context, options).result();
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
registry,
|
|
67
|
+
stream,
|
|
68
|
+
complete,
|
|
69
|
+
streamSimple,
|
|
70
|
+
completeSimple
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { createApiRegistry as n, createLlmRuntime as t };
|