@lgrammel/ds4-provider 0.0.1
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/README.md +96 -0
- package/binding.gyp +75 -0
- package/dist/ds4-language-model.d.ts +71 -0
- package/dist/ds4-language-model.d.ts.map +1 -0
- package/dist/ds4-language-model.js +888 -0
- package/dist/ds4-language-model.js.map +1 -0
- package/dist/ds4-provider.d.ts +13 -0
- package/dist/ds4-provider.d.ts.map +1 -0
- package/dist/ds4-provider.js +20 -0
- package/dist/ds4-provider.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/native-binding.d.ts +42 -0
- package/dist/native-binding.d.ts.map +1 -0
- package/dist/native-binding.js +157 -0
- package/dist/native-binding.js.map +1 -0
- package/ds4/LICENSE +22 -0
- package/ds4/ds4.c +18268 -0
- package/ds4/ds4.h +196 -0
- package/ds4/ds4_gpu.h +804 -0
- package/ds4/ds4_metal.m +14657 -0
- package/ds4/metal/argsort.metal +266 -0
- package/ds4/metal/bin.metal +192 -0
- package/ds4/metal/concat.metal +62 -0
- package/ds4/metal/cpy.metal +57 -0
- package/ds4/metal/dense.metal +1121 -0
- package/ds4/metal/dsv4_hc.metal +861 -0
- package/ds4/metal/dsv4_kv.metal +227 -0
- package/ds4/metal/dsv4_misc.metal +1088 -0
- package/ds4/metal/dsv4_rope.metal +155 -0
- package/ds4/metal/flash_attn.metal +1426 -0
- package/ds4/metal/get_rows.metal +54 -0
- package/ds4/metal/glu.metal +36 -0
- package/ds4/metal/moe.metal +1737 -0
- package/ds4/metal/norm.metal +153 -0
- package/ds4/metal/repeat.metal +52 -0
- package/ds4/metal/set_rows.metal +55 -0
- package/ds4/metal/softmax.metal +241 -0
- package/ds4/metal/sum_rows.metal +102 -0
- package/ds4/metal/unary.metal +312 -0
- package/native/binding.cpp +621 -0
- package/package.json +66 -0
- package/scripts/postinstall.cjs +13 -0
- package/scripts/vendor-ds4.cjs +67 -0
|
@@ -0,0 +1,888 @@
|
|
|
1
|
+
import { cancelGeneration, generate, generateStream, loadModel, unloadModel, } from "./native-binding.js";
|
|
2
|
+
export class DS4LanguageModel {
|
|
3
|
+
config;
|
|
4
|
+
specificationVersion = "v4";
|
|
5
|
+
provider = "ds4";
|
|
6
|
+
modelId;
|
|
7
|
+
supportedUrls = {};
|
|
8
|
+
topK;
|
|
9
|
+
minP;
|
|
10
|
+
seed;
|
|
11
|
+
toolReplay = new Map();
|
|
12
|
+
toolReplayIds = new Map();
|
|
13
|
+
modelHandle = null;
|
|
14
|
+
initPromise = null;
|
|
15
|
+
constructor(config) {
|
|
16
|
+
this.config = config;
|
|
17
|
+
this.modelId = config.modelId;
|
|
18
|
+
this.topK = config.topK;
|
|
19
|
+
this.minP = config.minP;
|
|
20
|
+
this.seed = config.seed;
|
|
21
|
+
}
|
|
22
|
+
async doGenerate(options) {
|
|
23
|
+
throwIfAborted(options.abortSignal);
|
|
24
|
+
const handle = await this.ensureModelLoaded();
|
|
25
|
+
const generateOptions = this.buildGenerateOptions(options);
|
|
26
|
+
const result = await this.runWithAbortSignal(handle, options.abortSignal, () => generate(handle, generateOptions));
|
|
27
|
+
const parsed = parseGeneratedContent(result.text, generateOptions.thinkMode !== undefined);
|
|
28
|
+
this.rememberToolCalls(parsed);
|
|
29
|
+
return {
|
|
30
|
+
content: parsed.content,
|
|
31
|
+
finishReason: parsed.finishReason ?? convertFinishReason(result.finishReason),
|
|
32
|
+
usage: convertUsage(result),
|
|
33
|
+
warnings: getWarnings(options),
|
|
34
|
+
request: {
|
|
35
|
+
body: generateOptions,
|
|
36
|
+
},
|
|
37
|
+
response: {
|
|
38
|
+
modelId: this.modelId,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
async doStream(options) {
|
|
43
|
+
throwIfAborted(options.abortSignal);
|
|
44
|
+
const handle = await this.ensureModelLoaded();
|
|
45
|
+
const generateOptions = this.buildGenerateOptions(options);
|
|
46
|
+
const textId = crypto.randomUUID();
|
|
47
|
+
const reasoningId = crypto.randomUUID();
|
|
48
|
+
const warnings = getWarnings(options);
|
|
49
|
+
const stream = this.createStream(handle, generateOptions, textId, reasoningId, warnings, options);
|
|
50
|
+
return {
|
|
51
|
+
stream,
|
|
52
|
+
request: {
|
|
53
|
+
body: generateOptions,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async dispose() {
|
|
58
|
+
if (this.modelHandle !== null) {
|
|
59
|
+
unloadModel(this.modelHandle);
|
|
60
|
+
this.modelHandle = null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async ensureModelLoaded() {
|
|
64
|
+
if (this.modelHandle !== null) {
|
|
65
|
+
return this.modelHandle;
|
|
66
|
+
}
|
|
67
|
+
this.initPromise ??= (async () => {
|
|
68
|
+
const loadOptions = {
|
|
69
|
+
modelPath: this.config.modelPath,
|
|
70
|
+
mtpPath: this.config.mtpPath,
|
|
71
|
+
contextSize: this.config.contextSize,
|
|
72
|
+
threads: this.config.threads,
|
|
73
|
+
backend: this.config.backend,
|
|
74
|
+
mtpDraftTokens: this.config.mtpDraftTokens,
|
|
75
|
+
mtpMargin: this.config.mtpMargin,
|
|
76
|
+
warmWeights: this.config.warmWeights,
|
|
77
|
+
quality: this.config.quality,
|
|
78
|
+
debug: this.config.debug,
|
|
79
|
+
};
|
|
80
|
+
this.modelHandle = await loadModel(loadOptions);
|
|
81
|
+
})();
|
|
82
|
+
await this.initPromise;
|
|
83
|
+
this.initPromise = null;
|
|
84
|
+
if (this.modelHandle === null) {
|
|
85
|
+
throw new Error("Failed to load DS4 model");
|
|
86
|
+
}
|
|
87
|
+
return this.modelHandle;
|
|
88
|
+
}
|
|
89
|
+
buildGenerateOptions(options) {
|
|
90
|
+
const body = {
|
|
91
|
+
messages: this.convertMessages(options.prompt, options),
|
|
92
|
+
};
|
|
93
|
+
if (options.maxOutputTokens !== undefined) {
|
|
94
|
+
body.maxTokens = options.maxOutputTokens;
|
|
95
|
+
}
|
|
96
|
+
if (options.temperature !== undefined) {
|
|
97
|
+
body.temperature = options.temperature;
|
|
98
|
+
}
|
|
99
|
+
if (options.topP !== undefined) {
|
|
100
|
+
body.topP = options.topP;
|
|
101
|
+
}
|
|
102
|
+
if (this.topK !== undefined) {
|
|
103
|
+
body.topK = this.topK;
|
|
104
|
+
}
|
|
105
|
+
if (this.minP !== undefined) {
|
|
106
|
+
body.minP = this.minP;
|
|
107
|
+
}
|
|
108
|
+
if (this.seed !== undefined) {
|
|
109
|
+
body.seed = this.seed;
|
|
110
|
+
}
|
|
111
|
+
if (options.stopSequences?.length) {
|
|
112
|
+
body.stopSequences = options.stopSequences;
|
|
113
|
+
}
|
|
114
|
+
const thinkMode = getThinkMode(options);
|
|
115
|
+
if (thinkMode !== "none") {
|
|
116
|
+
body.thinkMode = thinkMode;
|
|
117
|
+
}
|
|
118
|
+
return body;
|
|
119
|
+
}
|
|
120
|
+
createStream(handle, generateOptions, textId, reasoningId, warnings, options) {
|
|
121
|
+
return new ReadableStream({
|
|
122
|
+
start: async (controller) => {
|
|
123
|
+
let generatedText = "";
|
|
124
|
+
const bufferOutput = shouldParseToolOutput(options);
|
|
125
|
+
let textStarted = false;
|
|
126
|
+
let reasoningStarted = false;
|
|
127
|
+
let inReasoning = generateOptions.thinkMode !== undefined && generateOptions.thinkMode !== "none";
|
|
128
|
+
let parserBuffer = "";
|
|
129
|
+
const emitReasoningDelta = (delta) => {
|
|
130
|
+
if (delta.length === 0) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (!reasoningStarted) {
|
|
134
|
+
controller.enqueue({ type: "reasoning-start", id: reasoningId });
|
|
135
|
+
reasoningStarted = true;
|
|
136
|
+
}
|
|
137
|
+
controller.enqueue({ type: "reasoning-delta", id: reasoningId, delta });
|
|
138
|
+
};
|
|
139
|
+
const endReasoning = () => {
|
|
140
|
+
if (!reasoningStarted) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
controller.enqueue({ type: "reasoning-end", id: reasoningId });
|
|
144
|
+
reasoningStarted = false;
|
|
145
|
+
};
|
|
146
|
+
const emitTextDelta = (delta) => {
|
|
147
|
+
if (delta.length === 0) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (!textStarted) {
|
|
151
|
+
controller.enqueue({ type: "text-start", id: textId });
|
|
152
|
+
textStarted = true;
|
|
153
|
+
}
|
|
154
|
+
controller.enqueue({ type: "text-delta", id: textId, delta });
|
|
155
|
+
};
|
|
156
|
+
const emitParsedDelta = (delta, finish = false) => {
|
|
157
|
+
parserBuffer += delta;
|
|
158
|
+
while (parserBuffer.length > 0) {
|
|
159
|
+
const openingThinkIndex = parserBuffer.indexOf(THINK_OPEN);
|
|
160
|
+
const closingThinkIndex = parserBuffer.indexOf(THINK_CLOSE);
|
|
161
|
+
if (!inReasoning) {
|
|
162
|
+
if (openingThinkIndex === -1) {
|
|
163
|
+
const safeLength = finish
|
|
164
|
+
? parserBuffer.length
|
|
165
|
+
: getSafePrefixLength(parserBuffer, THINK_OPEN);
|
|
166
|
+
if (safeLength === 0) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
emitTextDelta(parserBuffer.slice(0, safeLength));
|
|
170
|
+
parserBuffer = parserBuffer.slice(safeLength);
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
emitTextDelta(parserBuffer.slice(0, openingThinkIndex));
|
|
174
|
+
parserBuffer = parserBuffer.slice(openingThinkIndex + THINK_OPEN.length);
|
|
175
|
+
inReasoning = true;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (closingThinkIndex === -1) {
|
|
179
|
+
const safeLength = finish
|
|
180
|
+
? parserBuffer.length
|
|
181
|
+
: getSafePrefixLength(parserBuffer, THINK_CLOSE);
|
|
182
|
+
if (safeLength === 0) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
emitReasoningDelta(parserBuffer.slice(0, safeLength));
|
|
186
|
+
parserBuffer = parserBuffer.slice(safeLength);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
emitReasoningDelta(parserBuffer.slice(0, closingThinkIndex));
|
|
190
|
+
parserBuffer = parserBuffer.slice(closingThinkIndex + THINK_CLOSE.length);
|
|
191
|
+
inReasoning = false;
|
|
192
|
+
endReasoning();
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
try {
|
|
196
|
+
controller.enqueue({ type: "stream-start", warnings });
|
|
197
|
+
const result = await this.runWithAbortSignal(handle, options.abortSignal, () => generateStream(handle, generateOptions, (delta) => {
|
|
198
|
+
generatedText += delta;
|
|
199
|
+
if (bufferOutput && isCompleteDsmlToolCall(generatedText, generateOptions)) {
|
|
200
|
+
cancelGeneration(handle);
|
|
201
|
+
}
|
|
202
|
+
if (!bufferOutput) {
|
|
203
|
+
emitParsedDelta(delta);
|
|
204
|
+
}
|
|
205
|
+
}));
|
|
206
|
+
const parsed = parseGeneratedContent(generatedText, generateOptions.thinkMode !== undefined);
|
|
207
|
+
this.rememberToolCalls(parsed);
|
|
208
|
+
if (bufferOutput) {
|
|
209
|
+
enqueueContentParts(controller, parsed.content);
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
emitParsedDelta("", true);
|
|
213
|
+
endReasoning();
|
|
214
|
+
if (textStarted) {
|
|
215
|
+
controller.enqueue({ type: "text-end", id: textId });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
controller.enqueue({
|
|
219
|
+
type: "finish",
|
|
220
|
+
finishReason: parsed.finishReason ?? convertFinishReason(result.finishReason),
|
|
221
|
+
usage: convertUsage(result),
|
|
222
|
+
});
|
|
223
|
+
controller.close();
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
controller.error(error);
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
async runWithAbortSignal(handle, signal, run) {
|
|
232
|
+
throwIfAborted(signal);
|
|
233
|
+
if (!signal) {
|
|
234
|
+
return run();
|
|
235
|
+
}
|
|
236
|
+
return new Promise((resolve, reject) => {
|
|
237
|
+
let settled = false;
|
|
238
|
+
let abortListener;
|
|
239
|
+
const settle = (callback) => {
|
|
240
|
+
if (settled) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
settled = true;
|
|
244
|
+
if (abortListener) {
|
|
245
|
+
signal.removeEventListener("abort", abortListener);
|
|
246
|
+
}
|
|
247
|
+
callback();
|
|
248
|
+
};
|
|
249
|
+
abortListener = () => {
|
|
250
|
+
cancelGeneration(handle);
|
|
251
|
+
settle(() => reject(createAbortError()));
|
|
252
|
+
};
|
|
253
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
254
|
+
if (signal.aborted) {
|
|
255
|
+
abortListener();
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
run().then((result) => settle(() => resolve(result)), (error) => settle(() => reject(error)));
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
convertMessages(messages, options = {}) {
|
|
262
|
+
return convertMessages(messages, options, this.toolReplay, this.toolReplayIds);
|
|
263
|
+
}
|
|
264
|
+
rememberToolCalls(parsed) {
|
|
265
|
+
if (!parsed.toolDsml) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
for (const toolCall of parsed.toolCalls) {
|
|
269
|
+
this.toolReplay.set(toolCall.toolCallId, parsed.toolDsml);
|
|
270
|
+
}
|
|
271
|
+
this.toolReplayIds.set(parsed.toolDsml, parsed.toolCalls.map((toolCall) => toolCall.toolCallId));
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
export function convertMessages(messages, options = {}, toolReplay, toolReplayIds) {
|
|
275
|
+
const result = [];
|
|
276
|
+
const toolInstructions = formatToolInstructions(options);
|
|
277
|
+
if (toolInstructions) {
|
|
278
|
+
result.push({ role: "system", content: toolInstructions });
|
|
279
|
+
}
|
|
280
|
+
for (const message of messages) {
|
|
281
|
+
switch (message.role) {
|
|
282
|
+
case "system":
|
|
283
|
+
result.push({ role: "system", content: message.content });
|
|
284
|
+
break;
|
|
285
|
+
case "user":
|
|
286
|
+
result.push({
|
|
287
|
+
role: "user",
|
|
288
|
+
content: message.content
|
|
289
|
+
.map((part) => {
|
|
290
|
+
if (part.type === "text") {
|
|
291
|
+
return part.text;
|
|
292
|
+
}
|
|
293
|
+
return `[Unsupported ${part.type} part omitted]`;
|
|
294
|
+
})
|
|
295
|
+
.join(""),
|
|
296
|
+
});
|
|
297
|
+
break;
|
|
298
|
+
case "assistant": {
|
|
299
|
+
const content = formatAssistantContent(message.content, toolReplay, toolReplayIds);
|
|
300
|
+
if (content.length > 0) {
|
|
301
|
+
result.push({ role: "assistant", content });
|
|
302
|
+
}
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
case "tool": {
|
|
306
|
+
const content = message.content.map(formatToolResult).join("\n\n");
|
|
307
|
+
if (content.length > 0) {
|
|
308
|
+
result.push({ role: "user", content });
|
|
309
|
+
}
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return result;
|
|
315
|
+
}
|
|
316
|
+
const THINK_OPEN = "<think>";
|
|
317
|
+
const THINK_CLOSE = "</think>";
|
|
318
|
+
const DSML = "|DSML|";
|
|
319
|
+
const DSML_SHORT = "DSML|";
|
|
320
|
+
const DSML_TOOL_CALLS_OPEN = `<${DSML}tool_calls>`;
|
|
321
|
+
const DSML_TOOL_CALLS_CLOSE = `</${DSML}tool_calls>`;
|
|
322
|
+
const DSML_INVOKE_OPEN = `<${DSML}invoke`;
|
|
323
|
+
const DSML_INVOKE_CLOSE = `</${DSML}invoke>`;
|
|
324
|
+
const DSML_PARAMETER_OPEN = `<${DSML}parameter`;
|
|
325
|
+
const DSML_PARAMETER_CLOSE = `</${DSML}parameter>`;
|
|
326
|
+
const DSML_TOOL_CALLS_OPEN_SHORT = `<${DSML_SHORT}tool_calls>`;
|
|
327
|
+
const DSML_TOOL_CALLS_CLOSE_SHORT = `</${DSML_SHORT}tool_calls>`;
|
|
328
|
+
const DSML_INVOKE_OPEN_SHORT = `<${DSML_SHORT}invoke`;
|
|
329
|
+
const DSML_INVOKE_CLOSE_SHORT = `</${DSML_SHORT}invoke>`;
|
|
330
|
+
const DSML_PARAMETER_OPEN_SHORT = `<${DSML_SHORT}parameter`;
|
|
331
|
+
const DSML_PARAMETER_CLOSE_SHORT = `</${DSML_SHORT}parameter>`;
|
|
332
|
+
const PLAIN_TOOL_CALLS_OPEN = "<tool_calls>";
|
|
333
|
+
const PLAIN_TOOL_CALLS_CLOSE = "</tool_calls>";
|
|
334
|
+
const PLAIN_INVOKE_OPEN = "<invoke";
|
|
335
|
+
const PLAIN_INVOKE_CLOSE = "</invoke>";
|
|
336
|
+
const PLAIN_PARAMETER_OPEN = "<parameter";
|
|
337
|
+
const PLAIN_PARAMETER_CLOSE = "</parameter>";
|
|
338
|
+
const TOOL_RESULT_CLOSE = "</tool_result>";
|
|
339
|
+
const DSML_SYNTAXES = [
|
|
340
|
+
{
|
|
341
|
+
toolCallsOpen: DSML_TOOL_CALLS_OPEN,
|
|
342
|
+
toolCallsClose: DSML_TOOL_CALLS_CLOSE,
|
|
343
|
+
invokeOpen: DSML_INVOKE_OPEN,
|
|
344
|
+
invokeClose: DSML_INVOKE_CLOSE,
|
|
345
|
+
parameterOpen: DSML_PARAMETER_OPEN,
|
|
346
|
+
parameterClose: DSML_PARAMETER_CLOSE,
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
toolCallsOpen: DSML_TOOL_CALLS_OPEN_SHORT,
|
|
350
|
+
toolCallsClose: DSML_TOOL_CALLS_CLOSE_SHORT,
|
|
351
|
+
invokeOpen: DSML_INVOKE_OPEN_SHORT,
|
|
352
|
+
invokeClose: DSML_INVOKE_CLOSE_SHORT,
|
|
353
|
+
parameterOpen: DSML_PARAMETER_OPEN_SHORT,
|
|
354
|
+
parameterClose: DSML_PARAMETER_CLOSE_SHORT,
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
toolCallsOpen: PLAIN_TOOL_CALLS_OPEN,
|
|
358
|
+
toolCallsClose: PLAIN_TOOL_CALLS_CLOSE,
|
|
359
|
+
invokeOpen: PLAIN_INVOKE_OPEN,
|
|
360
|
+
invokeClose: PLAIN_INVOKE_CLOSE,
|
|
361
|
+
parameterOpen: PLAIN_PARAMETER_OPEN,
|
|
362
|
+
parameterClose: PLAIN_PARAMETER_CLOSE,
|
|
363
|
+
},
|
|
364
|
+
];
|
|
365
|
+
function pushContent(content, type, text) {
|
|
366
|
+
if (text.length === 0) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
content.push(type === "text"
|
|
370
|
+
? {
|
|
371
|
+
type,
|
|
372
|
+
text,
|
|
373
|
+
providerMetadata: undefined,
|
|
374
|
+
}
|
|
375
|
+
: {
|
|
376
|
+
type,
|
|
377
|
+
text,
|
|
378
|
+
providerMetadata: undefined,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
export function parseGeneratedContent(text, initialInReasoning = false) {
|
|
382
|
+
const parsed = parseGeneratedMessage(text, initialInReasoning);
|
|
383
|
+
const content = [];
|
|
384
|
+
pushContent(content, "reasoning", parsed.reasoningText ?? "");
|
|
385
|
+
pushContent(content, "text", parsed.contentText);
|
|
386
|
+
const toolCalls = parsed.calls.map((call) => ({
|
|
387
|
+
type: "tool-call",
|
|
388
|
+
toolCallId: crypto.randomUUID(),
|
|
389
|
+
toolName: call.toolName,
|
|
390
|
+
input: call.input,
|
|
391
|
+
}));
|
|
392
|
+
content.push(...toolCalls);
|
|
393
|
+
return {
|
|
394
|
+
content,
|
|
395
|
+
finishReason: toolCalls.length > 0 ? { unified: "tool-calls", raw: "tool-calls" } : undefined,
|
|
396
|
+
toolCalls,
|
|
397
|
+
toolDsml: parsed.rawDsml,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
function isCompleteDsmlToolCall(text, generateOptions) {
|
|
401
|
+
const requireThinkingClosed = generateOptions.thinkMode !== undefined;
|
|
402
|
+
const searchStart = requireThinkingClosed
|
|
403
|
+
? text.lastIndexOf(THINK_CLOSE) + THINK_CLOSE.length
|
|
404
|
+
: 0;
|
|
405
|
+
if (requireThinkingClosed && searchStart < THINK_CLOSE.length) {
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
const found = findDsmlToolStart(text, searchStart);
|
|
409
|
+
if (!found) {
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
412
|
+
return (text.indexOf(found.syntax.toolCallsClose, found.start + found.syntax.toolCallsOpen.length) !==
|
|
413
|
+
-1);
|
|
414
|
+
}
|
|
415
|
+
function enqueueContentParts(controller, content) {
|
|
416
|
+
for (const part of content) {
|
|
417
|
+
switch (part.type) {
|
|
418
|
+
case "text": {
|
|
419
|
+
const id = crypto.randomUUID();
|
|
420
|
+
controller.enqueue({ type: "text-start", id });
|
|
421
|
+
controller.enqueue({ type: "text-delta", id, delta: part.text });
|
|
422
|
+
controller.enqueue({ type: "text-end", id });
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
case "reasoning": {
|
|
426
|
+
const id = crypto.randomUUID();
|
|
427
|
+
controller.enqueue({ type: "reasoning-start", id });
|
|
428
|
+
controller.enqueue({ type: "reasoning-delta", id, delta: part.text });
|
|
429
|
+
controller.enqueue({ type: "reasoning-end", id });
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
case "tool-call":
|
|
433
|
+
controller.enqueue(part);
|
|
434
|
+
break;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
function shouldParseToolOutput(options) {
|
|
439
|
+
return options.tools?.some((tool) => tool.type === "function") ?? false;
|
|
440
|
+
}
|
|
441
|
+
function formatAssistantContent(content, toolReplay, toolReplayIds) {
|
|
442
|
+
let text = "";
|
|
443
|
+
const toolCalls = [];
|
|
444
|
+
for (const part of content) {
|
|
445
|
+
if (part.type === "text") {
|
|
446
|
+
text += part.text;
|
|
447
|
+
}
|
|
448
|
+
else if (part.type === "reasoning") {
|
|
449
|
+
text += `${THINK_OPEN}${part.text}${THINK_CLOSE}`;
|
|
450
|
+
}
|
|
451
|
+
else if (part.type === "tool-call") {
|
|
452
|
+
toolCalls.push(part);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
if (toolCalls.length === 0) {
|
|
456
|
+
return text;
|
|
457
|
+
}
|
|
458
|
+
const replayed = getReplayedDsml(toolCalls, toolReplay, toolReplayIds);
|
|
459
|
+
return `${text}${replayed ?? formatAssistantToolCalls(toolCalls)}`;
|
|
460
|
+
}
|
|
461
|
+
function formatToolInstructions(options) {
|
|
462
|
+
const tools = options.tools?.filter((tool) => tool.type === "function") ?? [];
|
|
463
|
+
if (tools.length === 0) {
|
|
464
|
+
return undefined;
|
|
465
|
+
}
|
|
466
|
+
const toolChoice = options.toolChoice ?? { type: "auto" };
|
|
467
|
+
const toolChoiceInstruction = toolChoice.type === "none"
|
|
468
|
+
? "Do not call any tools for this response."
|
|
469
|
+
: toolChoice.type === "required"
|
|
470
|
+
? "You must call one of the available tools."
|
|
471
|
+
: toolChoice.type === "tool"
|
|
472
|
+
? `You must call the ${JSON.stringify(toolChoice.toolName)} tool.`
|
|
473
|
+
: "Call a tool only when it is needed to answer the user.";
|
|
474
|
+
return [
|
|
475
|
+
"## Tools",
|
|
476
|
+
"",
|
|
477
|
+
"You have access to a set of tools to help answer the user question. " +
|
|
478
|
+
`You can invoke tools by writing a "${DSML_TOOL_CALLS_OPEN}" block like the following:`,
|
|
479
|
+
"",
|
|
480
|
+
DSML_TOOL_CALLS_OPEN,
|
|
481
|
+
`${DSML_INVOKE_OPEN} name="$TOOL_NAME">`,
|
|
482
|
+
`${DSML_PARAMETER_OPEN} name="$PARAMETER_NAME" string="true|false">$PARAMETER_VALUE${DSML_PARAMETER_CLOSE}`,
|
|
483
|
+
"...",
|
|
484
|
+
DSML_INVOKE_CLOSE,
|
|
485
|
+
`${DSML_INVOKE_OPEN} name="$TOOL_NAME2">`,
|
|
486
|
+
"...",
|
|
487
|
+
DSML_INVOKE_CLOSE,
|
|
488
|
+
DSML_TOOL_CALLS_CLOSE,
|
|
489
|
+
"",
|
|
490
|
+
'String parameters should be specified as raw text and set `string="true"`.',
|
|
491
|
+
"Preserve characters such as `>`, `&`, and `&&` exactly; never replace normal string characters with XML or HTML entity escapes.",
|
|
492
|
+
`Only if a string value itself contains the exact closing parameter tag \`${DSML_PARAMETER_CLOSE}\`, write that tag as \`</${DSML}parameter>\` inside the value.`,
|
|
493
|
+
'For all other types (numbers, booleans, arrays, objects), pass the value in JSON format and set `string="false"`.',
|
|
494
|
+
"",
|
|
495
|
+
`If thinking_mode is enabled (triggered by ${THINK_OPEN}), you MUST output your complete reasoning inside ${THINK_OPEN}...${THINK_CLOSE} BEFORE any tool calls or final response.`,
|
|
496
|
+
"",
|
|
497
|
+
`Otherwise, output directly after ${THINK_CLOSE} with tool calls or final response.`,
|
|
498
|
+
"",
|
|
499
|
+
"### Available Tool Schemas",
|
|
500
|
+
"",
|
|
501
|
+
tools.map(formatToolSchema).join("\n"),
|
|
502
|
+
"",
|
|
503
|
+
"You MUST strictly follow the above defined tool name and parameter schemas to invoke tool calls. Use the exact parameter names from the schemas.",
|
|
504
|
+
toolChoiceInstruction,
|
|
505
|
+
].join("\n");
|
|
506
|
+
}
|
|
507
|
+
function formatToolSchema(tool) {
|
|
508
|
+
return JSON.stringify({
|
|
509
|
+
name: tool.name,
|
|
510
|
+
description: tool.description,
|
|
511
|
+
input_schema: tool.inputSchema,
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
function getReplayedDsml(toolCalls, toolReplay, toolReplayIds) {
|
|
515
|
+
if (!toolReplay || toolCalls.length === 0) {
|
|
516
|
+
return undefined;
|
|
517
|
+
}
|
|
518
|
+
const first = toolReplay.get(toolCalls[0].toolCallId);
|
|
519
|
+
if (!first) {
|
|
520
|
+
return undefined;
|
|
521
|
+
}
|
|
522
|
+
const ids = toolReplayIds?.get(first);
|
|
523
|
+
if (ids && ids.length === toolCalls.length) {
|
|
524
|
+
const sameOrder = toolCalls.every((part, index) => part.toolCallId === ids[index]);
|
|
525
|
+
if (!sameOrder) {
|
|
526
|
+
return undefined;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return toolCalls.every((part) => toolReplay.get(part.toolCallId) === first) ? first : undefined;
|
|
530
|
+
}
|
|
531
|
+
function formatAssistantToolCalls(parts) {
|
|
532
|
+
if (parts.length === 0) {
|
|
533
|
+
return "";
|
|
534
|
+
}
|
|
535
|
+
return `\n\n${DSML_TOOL_CALLS_OPEN}\n${parts.map(formatAssistantToolCall).join("")}${DSML_TOOL_CALLS_CLOSE}`;
|
|
536
|
+
}
|
|
537
|
+
function formatAssistantToolCall(part) {
|
|
538
|
+
return `${DSML_INVOKE_OPEN} name="${escapeDsmlAttribute(part.toolName)}">\n${formatDsmlArguments(stringifyToolInput(part.input))}${DSML_INVOKE_CLOSE}\n`;
|
|
539
|
+
}
|
|
540
|
+
function formatToolResult(part) {
|
|
541
|
+
if (part.type !== "tool-result") {
|
|
542
|
+
return "";
|
|
543
|
+
}
|
|
544
|
+
return `<tool_result>${escapeToolResultText(JSON.stringify({
|
|
545
|
+
id: part.toolCallId,
|
|
546
|
+
name: part.toolName,
|
|
547
|
+
result: formatToolResultOutput(part.output),
|
|
548
|
+
}))}${TOOL_RESULT_CLOSE}`;
|
|
549
|
+
}
|
|
550
|
+
function formatToolResultOutput(part) {
|
|
551
|
+
switch (part.type) {
|
|
552
|
+
case "text":
|
|
553
|
+
case "json":
|
|
554
|
+
case "error-text":
|
|
555
|
+
case "error-json":
|
|
556
|
+
return part.value;
|
|
557
|
+
case "execution-denied":
|
|
558
|
+
return { error: "execution-denied", reason: part.reason };
|
|
559
|
+
case "content":
|
|
560
|
+
return part.value.map((content) => {
|
|
561
|
+
switch (content.type) {
|
|
562
|
+
case "text":
|
|
563
|
+
return { type: "text", text: content.text };
|
|
564
|
+
case "file":
|
|
565
|
+
return {
|
|
566
|
+
type: "file",
|
|
567
|
+
mediaType: content.mediaType,
|
|
568
|
+
filename: content.filename,
|
|
569
|
+
};
|
|
570
|
+
case "custom":
|
|
571
|
+
return { type: "custom" };
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
function formatDsmlArguments(input) {
|
|
577
|
+
const parsed = parseJsonObject(input);
|
|
578
|
+
if (!parsed) {
|
|
579
|
+
return `${DSML_PARAMETER_OPEN} name="arguments" string="true">${escapeDsmlParameterText(input)}${DSML_PARAMETER_CLOSE}\n`;
|
|
580
|
+
}
|
|
581
|
+
let result = "";
|
|
582
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
583
|
+
const isString = typeof value === "string";
|
|
584
|
+
const renderedValue = isString ? value : JSON.stringify(value);
|
|
585
|
+
result += `${DSML_PARAMETER_OPEN} name="${escapeDsmlAttribute(key)}" string="${isString ? "true" : "false"}">${isString ? escapeDsmlParameterText(renderedValue) : escapeDsmlJsonLiteral(renderedValue)}${DSML_PARAMETER_CLOSE}\n`;
|
|
586
|
+
}
|
|
587
|
+
return result;
|
|
588
|
+
}
|
|
589
|
+
function parseGeneratedMessage(text, requireThinkingClosed) {
|
|
590
|
+
const source = text ?? "";
|
|
591
|
+
const searchStart = requireThinkingClosed
|
|
592
|
+
? source.lastIndexOf(THINK_CLOSE) + THINK_CLOSE.length
|
|
593
|
+
: 0;
|
|
594
|
+
if (requireThinkingClosed && searchStart < THINK_CLOSE.length) {
|
|
595
|
+
const content = [];
|
|
596
|
+
pushReasoningAndText(content, source, true);
|
|
597
|
+
return partsToDsmlParseResult(content);
|
|
598
|
+
}
|
|
599
|
+
const found = findDsmlToolStart(source, searchStart);
|
|
600
|
+
if (!found) {
|
|
601
|
+
const content = [];
|
|
602
|
+
pushReasoningAndText(content, source, requireThinkingClosed);
|
|
603
|
+
return partsToDsmlParseResult(content);
|
|
604
|
+
}
|
|
605
|
+
const contentTextEnd = trimTrailingWhitespace(source.slice(0, found.start)).length;
|
|
606
|
+
const beforeTool = source.slice(0, contentTextEnd);
|
|
607
|
+
const parsedPrefix = [];
|
|
608
|
+
pushReasoningAndText(parsedPrefix, beforeTool, requireThinkingClosed);
|
|
609
|
+
const parsedTool = parseDsmlToolCalls(source, found.start, found.syntax);
|
|
610
|
+
if (!parsedTool) {
|
|
611
|
+
return {
|
|
612
|
+
contentText: source,
|
|
613
|
+
calls: [],
|
|
614
|
+
invalidToolCall: true,
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
const base = partsToDsmlParseResult(parsedPrefix);
|
|
618
|
+
return {
|
|
619
|
+
contentText: base.contentText,
|
|
620
|
+
reasoningText: base.reasoningText,
|
|
621
|
+
calls: parsedTool.calls,
|
|
622
|
+
rawDsml: parsedTool.rawDsml,
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
function partsToDsmlParseResult(content) {
|
|
626
|
+
let contentText = "";
|
|
627
|
+
let reasoningText = "";
|
|
628
|
+
for (const part of content) {
|
|
629
|
+
if (part.type === "text") {
|
|
630
|
+
contentText += part.text;
|
|
631
|
+
}
|
|
632
|
+
else if (part.type === "reasoning") {
|
|
633
|
+
reasoningText += part.text;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
return {
|
|
637
|
+
contentText,
|
|
638
|
+
reasoningText: reasoningText.length > 0 ? reasoningText : undefined,
|
|
639
|
+
calls: [],
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
function pushReasoningAndText(content, text, initialInReasoning = false) {
|
|
643
|
+
let remaining = text;
|
|
644
|
+
let inReasoning = initialInReasoning || remaining.startsWith(THINK_OPEN);
|
|
645
|
+
if (inReasoning && remaining.startsWith(THINK_OPEN)) {
|
|
646
|
+
remaining = remaining.slice(THINK_OPEN.length);
|
|
647
|
+
}
|
|
648
|
+
while (remaining.length > 0) {
|
|
649
|
+
if (inReasoning) {
|
|
650
|
+
const endIndex = remaining.indexOf(THINK_CLOSE);
|
|
651
|
+
if (endIndex === -1) {
|
|
652
|
+
pushContent(content, "reasoning", remaining);
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
pushContent(content, "reasoning", remaining.slice(0, endIndex));
|
|
656
|
+
remaining = remaining.slice(endIndex + THINK_CLOSE.length);
|
|
657
|
+
inReasoning = false;
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
660
|
+
const startIndex = remaining.indexOf(THINK_OPEN);
|
|
661
|
+
if (startIndex === -1) {
|
|
662
|
+
pushContent(content, "text", remaining);
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
pushContent(content, "text", remaining.slice(0, startIndex));
|
|
666
|
+
remaining = remaining.slice(startIndex + THINK_OPEN.length);
|
|
667
|
+
inReasoning = true;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
function findDsmlToolStart(text, fromIndex) {
|
|
671
|
+
let best;
|
|
672
|
+
for (const syntax of DSML_SYNTAXES) {
|
|
673
|
+
const index = text.indexOf(syntax.toolCallsOpen, fromIndex);
|
|
674
|
+
if (index !== -1 && (!best || index < best.start)) {
|
|
675
|
+
best = { start: index, syntax };
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
return best;
|
|
679
|
+
}
|
|
680
|
+
function parseDsmlToolCalls(text, start, syntax) {
|
|
681
|
+
let index = start;
|
|
682
|
+
if (!text.startsWith(syntax.toolCallsOpen, index)) {
|
|
683
|
+
return undefined;
|
|
684
|
+
}
|
|
685
|
+
index += syntax.toolCallsOpen.length;
|
|
686
|
+
const calls = [];
|
|
687
|
+
while (index < text.length) {
|
|
688
|
+
index = skipWhitespace(text, index);
|
|
689
|
+
if (text.startsWith(syntax.toolCallsClose, index)) {
|
|
690
|
+
const end = index + syntax.toolCallsClose.length;
|
|
691
|
+
return {
|
|
692
|
+
calls,
|
|
693
|
+
rawDsml: text.slice(start, end),
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
if (!text.startsWith(syntax.invokeOpen, index)) {
|
|
697
|
+
return undefined;
|
|
698
|
+
}
|
|
699
|
+
const invokeTagEnd = text.indexOf(">", index);
|
|
700
|
+
if (invokeTagEnd === -1) {
|
|
701
|
+
return undefined;
|
|
702
|
+
}
|
|
703
|
+
const invokeTag = text.slice(index, invokeTagEnd + 1);
|
|
704
|
+
const toolName = parseXmlAttribute(invokeTag, "name");
|
|
705
|
+
if (!toolName) {
|
|
706
|
+
return undefined;
|
|
707
|
+
}
|
|
708
|
+
index = invokeTagEnd + 1;
|
|
709
|
+
const args = {};
|
|
710
|
+
while (index < text.length) {
|
|
711
|
+
index = skipWhitespace(text, index);
|
|
712
|
+
if (text.startsWith(syntax.invokeClose, index)) {
|
|
713
|
+
index += syntax.invokeClose.length;
|
|
714
|
+
calls.push({ toolName, input: JSON.stringify(args) });
|
|
715
|
+
break;
|
|
716
|
+
}
|
|
717
|
+
const parsedParam = parseDsmlParameter(text, index, syntax);
|
|
718
|
+
if (!parsedParam) {
|
|
719
|
+
return undefined;
|
|
720
|
+
}
|
|
721
|
+
args[parsedParam.name] = parsedParam.value;
|
|
722
|
+
index = parsedParam.end;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
return undefined;
|
|
726
|
+
}
|
|
727
|
+
function parseDsmlParameter(text, start, syntax) {
|
|
728
|
+
if (!text.startsWith(syntax.parameterOpen, start)) {
|
|
729
|
+
return undefined;
|
|
730
|
+
}
|
|
731
|
+
const tagEnd = text.indexOf(">", start);
|
|
732
|
+
if (tagEnd === -1) {
|
|
733
|
+
return undefined;
|
|
734
|
+
}
|
|
735
|
+
const tag = text.slice(start, tagEnd + 1);
|
|
736
|
+
const name = parseXmlAttribute(tag, "name");
|
|
737
|
+
if (!name) {
|
|
738
|
+
return undefined;
|
|
739
|
+
}
|
|
740
|
+
const stringAttribute = parseXmlAttribute(tag, "string");
|
|
741
|
+
const valueStart = tagEnd + 1;
|
|
742
|
+
const valueEnd = text.indexOf(syntax.parameterClose, valueStart);
|
|
743
|
+
if (valueEnd === -1) {
|
|
744
|
+
return undefined;
|
|
745
|
+
}
|
|
746
|
+
const rawValue = text.slice(valueStart, valueEnd);
|
|
747
|
+
const isString = stringAttribute === null || stringAttribute === "true";
|
|
748
|
+
return {
|
|
749
|
+
name,
|
|
750
|
+
value: isString ? unescapeDsmlText(rawValue) : parseJsonValue(rawValue.trim()),
|
|
751
|
+
end: valueEnd + syntax.parameterClose.length,
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
function parseJsonObject(text) {
|
|
755
|
+
const parsed = parseJsonValue(text);
|
|
756
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)
|
|
757
|
+
? parsed
|
|
758
|
+
: undefined;
|
|
759
|
+
}
|
|
760
|
+
function parseJsonValue(text) {
|
|
761
|
+
try {
|
|
762
|
+
return JSON.parse(text);
|
|
763
|
+
}
|
|
764
|
+
catch {
|
|
765
|
+
return text;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
function parseXmlAttribute(tag, name) {
|
|
769
|
+
const match = new RegExp(`${escapeRegExp(name)}="([^"]*)"`).exec(tag);
|
|
770
|
+
return match ? unescapeDsmlText(match[1]) : null;
|
|
771
|
+
}
|
|
772
|
+
function skipWhitespace(text, index) {
|
|
773
|
+
while (index < text.length && /\s/.test(text[index])) {
|
|
774
|
+
index++;
|
|
775
|
+
}
|
|
776
|
+
return index;
|
|
777
|
+
}
|
|
778
|
+
function trimTrailingWhitespace(text) {
|
|
779
|
+
return text.replace(/\s+$/u, "");
|
|
780
|
+
}
|
|
781
|
+
function escapeDsmlAttribute(text) {
|
|
782
|
+
return text
|
|
783
|
+
.replaceAll("&", "&")
|
|
784
|
+
.replaceAll("<", "<")
|
|
785
|
+
.replaceAll(">", ">")
|
|
786
|
+
.replaceAll('"', """);
|
|
787
|
+
}
|
|
788
|
+
function escapeDsmlParameterText(text) {
|
|
789
|
+
return text.replaceAll(DSML_PARAMETER_CLOSE, `</${DSML}parameter>`);
|
|
790
|
+
}
|
|
791
|
+
function escapeDsmlJsonLiteral(text) {
|
|
792
|
+
return text.replaceAll(DSML_PARAMETER_CLOSE, `\\u003c/${DSML}parameter>`);
|
|
793
|
+
}
|
|
794
|
+
function escapeToolResultText(text) {
|
|
795
|
+
return text.replaceAll(TOOL_RESULT_CLOSE, "</tool_result>");
|
|
796
|
+
}
|
|
797
|
+
function unescapeDsmlText(text) {
|
|
798
|
+
return text
|
|
799
|
+
.replaceAll(""", '"')
|
|
800
|
+
.replaceAll("'", "'")
|
|
801
|
+
.replaceAll(">", ">")
|
|
802
|
+
.replaceAll("<", "<")
|
|
803
|
+
.replaceAll("&", "&");
|
|
804
|
+
}
|
|
805
|
+
function escapeRegExp(text) {
|
|
806
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
807
|
+
}
|
|
808
|
+
function stringifyToolInput(input) {
|
|
809
|
+
if (input === undefined) {
|
|
810
|
+
return "{}";
|
|
811
|
+
}
|
|
812
|
+
if (typeof input === "string") {
|
|
813
|
+
return input;
|
|
814
|
+
}
|
|
815
|
+
return JSON.stringify(input) ?? "{}";
|
|
816
|
+
}
|
|
817
|
+
function getSafePrefixLength(buffer, stopMarker) {
|
|
818
|
+
const maxOverlap = Math.min(buffer.length, stopMarker.length - 1);
|
|
819
|
+
for (let overlap = maxOverlap; overlap > 0; overlap--) {
|
|
820
|
+
if (stopMarker.startsWith(buffer.slice(buffer.length - overlap))) {
|
|
821
|
+
return buffer.length - overlap;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
return buffer.length;
|
|
825
|
+
}
|
|
826
|
+
function convertFinishReason(reason) {
|
|
827
|
+
switch (reason) {
|
|
828
|
+
case "stop":
|
|
829
|
+
return { unified: "stop", raw: reason };
|
|
830
|
+
case "length":
|
|
831
|
+
return { unified: "length", raw: reason };
|
|
832
|
+
default:
|
|
833
|
+
return { unified: "other", raw: reason ?? "unknown" };
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
function convertUsage(result) {
|
|
837
|
+
return {
|
|
838
|
+
inputTokens: {
|
|
839
|
+
total: result?.promptTokens,
|
|
840
|
+
noCache: result?.promptTokens,
|
|
841
|
+
cacheRead: undefined,
|
|
842
|
+
cacheWrite: undefined,
|
|
843
|
+
},
|
|
844
|
+
outputTokens: {
|
|
845
|
+
total: result?.completionTokens,
|
|
846
|
+
text: result?.completionTokens,
|
|
847
|
+
reasoning: undefined,
|
|
848
|
+
},
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
function getWarnings(options) {
|
|
852
|
+
const warnings = [];
|
|
853
|
+
if (options.tools?.some((tool) => tool.type === "provider")) {
|
|
854
|
+
warnings.push({
|
|
855
|
+
type: "unsupported",
|
|
856
|
+
feature: "provider tools",
|
|
857
|
+
details: "DS4 only supports client-executed function tools.",
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
return warnings;
|
|
861
|
+
}
|
|
862
|
+
function getThinkMode(options) {
|
|
863
|
+
switch (options.reasoning) {
|
|
864
|
+
case "none":
|
|
865
|
+
return "none";
|
|
866
|
+
case "minimal":
|
|
867
|
+
case "low":
|
|
868
|
+
case "medium":
|
|
869
|
+
case "high":
|
|
870
|
+
return "high";
|
|
871
|
+
case "xhigh":
|
|
872
|
+
return "max";
|
|
873
|
+
case "provider-default":
|
|
874
|
+
case undefined:
|
|
875
|
+
return "high";
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
function throwIfAborted(signal) {
|
|
879
|
+
if (signal?.aborted) {
|
|
880
|
+
throw createAbortError();
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
function createAbortError() {
|
|
884
|
+
const error = new Error("The operation was aborted");
|
|
885
|
+
error.name = "AbortError";
|
|
886
|
+
return error;
|
|
887
|
+
}
|
|
888
|
+
//# sourceMappingURL=ds4-language-model.js.map
|