• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lucasliet / llm-telegram-bot / 21422070803

28 Jan 2026 02:07AM UTC coverage: 61.924% (+0.3%) from 61.583%
21422070803

push

github

lucasliet
refactor: remove multiple AI model integrations and their associated services and handlers, updating related configurations and tests.

112 of 256 branches covered (43.75%)

Branch coverage included in aggregate %.

55 of 61 new or added lines in 8 files covered. (90.16%)

42 existing lines in 3 files now uncovered.

1903 of 2998 relevant lines covered (63.48%)

14.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

65.32
/src/adapter/ToolUsageAdapter.ts
1
import OpenAi from 'npm:openai';
2
import { executeToolCalls, responseMap as OpenaiResponseMap } from '../service/openai/OpenAIService.ts';
16✔
3

4
export interface ToolOptions {
5
        tools?: OpenAi.Chat.Completions.ChatCompletionTool[];
6
        tool_choice?: OpenAi.Chat.Completions.ChatCompletionToolChoiceOption;
7
        functions?: OpenAi.ChatCompletionCreateParams.Function[];
8
        function_call?: 'auto' | 'none' | { name: string };
9
}
10

11
export type ToolCall = {
12
        index?: number;
13
        id?: string;
14
        type?: string;
15
        function?: { name?: string; arguments?: string };
16
};
17

18
interface ToolCallResult {
19
        choices: Array<{
20
                delta: {
21
                        content: string | null;
22
                        tool_calls?: ToolCall[];
23
                };
24
                finish_reason?: string | null;
25
        }>;
26
}
27

28
export interface OpenAiStreamResponse {
29
        choices: Array<{
30
                delta: {
31
                        content: string | null;
32
                        tool_calls?: ToolCall[];
33
                };
34
                finish_reason: string | null;
35
        }>;
36
}
37

38
const TEXT_DECODER = new TextDecoder();
16✔
39
const TEXT_ENCODER = new TextEncoder();
16✔
40
const FUNCTION_CALL_PATTERN = /```(?:function|json)?\s*\n([\s\S]*?)\n```/g;
16✔
41
const TOOL_CALL_ADAPTER_KEY = '__adapter_tool_calls';
16✔
42
const TOOL_BLOCK_START_REGEX = /```(function|json)?\s*\n?/;
16✔
43
const TOOL_BLOCK_END_REGEX = /\n```/;
16✔
44

45
/**
1✔
46
 * Adapts tool usage for text-only Chatbots, enabling function calling
47
 * by formatting instructions and parsing responses.
48
 */
16✔
49
export class ToolUsageAdapter {
16✔
50
        /**
16✔
51
         * Formats a tool definition for inclusion in the prompt.
52
         */
16✔
53
        private _formatToolForMessage(tool: OpenAi.Chat.Completions.ChatCompletionTool) {
16✔
54
                if (tool.type === 'function' && tool.function) {
17✔
55
                        return {
17✔
56
                                name: tool.function.name,
17✔
57
                                description: tool.function.description || '',
×
58
                                parameters: tool.function.parameters,
17✔
59
                        };
17✔
60
                }
17!
61
                return {
×
62
                        name: 'unknown_tool',
×
63
                        description: '',
×
64
                        parameters: undefined,
×
65
                };
×
66
        }
×
67

68
        /**
16✔
69
         * Formats a function definition for inclusion in the prompt.
70
         */
×
71
        private _formatFunctionForMessage(func: OpenAi.ChatCompletionCreateParams.Function) {
×
72
                return {
×
73
                        name: func.name,
×
74
                        description: func.description || '',
×
75
                        parameters: func.parameters,
×
76
                };
×
77
        }
×
78

79
        /**
16✔
80
         * Validates if an object represents a valid function call structure.
81
         */
16✔
82
        private _isValidFunctionCall(obj: any): obj is { name: string; arguments: object } {
16✔
83
                return (
17✔
84
                        typeof obj === 'object' &&
17✔
85
                        typeof obj.name === 'string' &&
17✔
86
                        obj.name.length > 0 &&
17✔
87
                        typeof obj.arguments === 'object' &&
17✔
88
                        obj.arguments !== null &&
17✔
89
                        !Array.isArray(obj.arguments)
17✔
90
                );
91
        }
17✔
92

93
        /**
16✔
94
         * Extracts structured tool calls from the model's text response.
95
         * Removes the tool call blocks from the original content.
96
         */
16✔
97
        private _extractToolCalls(content: string): {
16✔
98
                toolCalls: ToolCall[] | null;
99
                cleanedContent: string;
100
        } {
16✔
101
                const toolCalls: ToolCall[] = [];
17✔
102
                let cleanedContent = content;
17✔
103
                let match;
17✔
104

105
                FUNCTION_CALL_PATTERN.lastIndex = 0;
17✔
106

107
                while ((match = FUNCTION_CALL_PATTERN.exec(content)) !== null) {
17✔
108
                        const functionData = match[1].trim();
17✔
109
                        try {
17✔
110
                                const functionObj = JSON.parse(functionData);
17✔
111
                                if (this._isValidFunctionCall(functionObj)) {
17✔
112
                                        toolCalls.push({
17✔
113
                                                id: crypto.randomUUID(),
17✔
114
                                                type: 'function',
17✔
115
                                                function: {
17✔
116
                                                        name: functionObj.name,
17✔
117
                                                        arguments: JSON.stringify(functionObj.arguments),
17✔
118
                                                },
17✔
119
                                        });
17✔
120
                                        cleanedContent = cleanedContent.replace(match[0], '');
17✔
121
                                } else {
×
122
                                        console.warn('Parsed function object is not valid:', functionObj);
×
123
                                }
×
124
                        } catch (error) {
×
125
                                console.warn(
×
126
                                        'Failed to parse potential function call JSON:',
×
127
                                        functionData.substring(0, 100),
×
128
                                        'Error:',
×
129
                                        error,
×
130
                                );
131
                        }
×
132
                }
17✔
133

134
                return {
17✔
135
                        toolCalls: toolCalls.length > 0 ? toolCalls : null,
×
136
                        cleanedContent: cleanedContent.trim(),
17✔
137
                };
17✔
138
        }
17✔
139

140
        /**
16✔
141
         * Converts a message with the 'tool' role to an 'assistant' role message
142
         * containing the tool result, formatted for text-only models.
143
         */
16✔
144
        private _convertToolRoleMessage(
16✔
145
                message: OpenAi.Chat.Completions.ChatCompletionToolMessageParam,
16✔
146
        ): OpenAi.Chat.Completions.ChatCompletionAssistantMessageParam {
16✔
147
                const toolCallId = message.tool_call_id;
17✔
148
                const toolResult = message.content;
17✔
149
                const formattedContent = `This was the result of the tool call with ID ${toolCallId}, I will use it to formulate my next response: \`\`\`json\n${
17✔
150
                        JSON.stringify(toolResult)
17✔
151
                }\n\`\`\``;
17✔
152
                return {
17✔
153
                        role: 'assistant',
17✔
154
                        content: formattedContent,
17✔
155
                };
17✔
156
        }
17✔
157

158
        /**
16✔
159
         * Generates the informational text about available tools and how to use them.
160
         */
16✔
161
        private _generateToolsInfoString(toolOptions?: ToolOptions): string {
16✔
162
                if (!toolOptions || (!toolOptions.tools?.length && !toolOptions.functions?.length)) {
×
UNCOV
163
                        return '';
×
UNCOV
164
                }
×
165

166
                let toolsInfo = '\n\nYou have access to the following tools:\n';
17✔
167

168
                if (toolOptions.tools?.length) {
17✔
169
                        toolsInfo += '\nTOOLS:\n';
17✔
170
                        toolOptions.tools.forEach((tool, index) => {
17✔
171
                                const formattedTool = this._formatToolForMessage(tool);
18✔
172
                                toolsInfo += `${index + 1}. ${formattedTool.name}: ${formattedTool.description}\n`;
18✔
173
                                toolsInfo += `   Parameters: ${JSON.stringify(formattedTool.parameters, null, 2)}\n\n`;
18✔
174
                        });
17✔
175
                }
17✔
176

177
                if (toolOptions.functions?.length) {
×
178
                        toolsInfo += '\nFUNCTIONS:\n';
×
179
                        toolOptions.functions.forEach((func, index) => {
×
180
                                const formattedFunc = this._formatFunctionForMessage(func);
×
181
                                toolsInfo += `${index + 1}. ${formattedFunc.name}: ${formattedFunc.description}\n`;
×
182
                                toolsInfo += `   Parameters: ${JSON.stringify(formattedFunc.parameters, null, 2)}\n\n`;
×
183
                        });
×
184
                }
×
185

186
                toolsInfo += '\nTo call a tool, respond using this exact markdown format:\n';
17✔
187
                toolsInfo += '```function\n';
17✔
188
                toolsInfo += '{\n';
17✔
189
                toolsInfo += '  "name": "function_name",\n';
17✔
190
                toolsInfo += '  "arguments": {\n';
17✔
191
                toolsInfo += '    "param1": "value1",\n';
17✔
192
                toolsInfo += '    "param2": "value2"\n';
17✔
193
                toolsInfo += '  }\n';
17✔
194
                toolsInfo += '}\n';
17✔
195
                toolsInfo += '```\n\n';
17✔
196
                toolsInfo += 'Before calling the tool, state what you are going to do. The very last part of your response must be the tool call block.\n';
17✔
197

198
                const choice = toolOptions?.tool_choice;
17✔
199
                const legacyFunctionCall = toolOptions?.function_call;
17✔
200

201
                if (choice) {
×
202
                        if (choice === 'none') {
×
203
                                toolsInfo += 'Do not use any tools unless absolutely necessary.\n\n';
×
204
                        } else if (typeof choice === 'object' && choice.type === 'function' && choice.function?.name) {
×
205
                                toolsInfo += `You must use the tool "${choice.function.name}" to answer this query.\n\n`;
×
206
                        }
×
207
                } else if (legacyFunctionCall) {
×
208
                        if (legacyFunctionCall === 'none') {
×
209
                                toolsInfo += 'Do not use any functions unless absolutely necessary.\n\n';
×
210
                        } else if (typeof legacyFunctionCall === 'object' && legacyFunctionCall.name) {
×
211
                                toolsInfo += `You must use the function "${legacyFunctionCall.name}" to answer this query.\n\n`;
×
212
                        }
×
213
                }
×
214

215
                return toolsInfo;
17✔
216
        }
17✔
217

218
        /**
16✔
219
         * Modifies the message history to include tool information and adapt tool messages.
220
         */
16✔
221
        modifyMessagesWithToolInfo(
16✔
222
                messages: OpenAi.Chat.Completions.ChatCompletionMessageParam[],
16✔
223
                toolOptions?: ToolOptions,
16✔
224
        ): OpenAi.Chat.Completions.ChatCompletionMessageParam[] {
16✔
225
                const modifiedMessages = messages.map((message) => message.role === 'tool' ? this._convertToolRoleMessage(message) : message);
17✔
226

227
                const lastUserMessageIndex = modifiedMessages.findLastIndex(
17✔
228
                        (message) => message.role === 'user',
17✔
229
                );
230

231
                if (lastUserMessageIndex >= 0) {
17✔
232
                        const toolsInfoString = this._generateToolsInfoString(toolOptions);
17✔
233
                        if (toolsInfoString) {
17✔
234
                                const lastUserMessage = modifiedMessages[lastUserMessageIndex];
17✔
235
                                const originalContent = typeof lastUserMessage.content === 'string'
×
236
                                        ? lastUserMessage.content
×
237
                                        : (Array.isArray(lastUserMessage.content) ? lastUserMessage.content.map((part) => part.type === 'text' ? part.text : '').join('') : '');
×
238

239
                                modifiedMessages[lastUserMessageIndex] = {
17✔
240
                                        ...lastUserMessage,
17✔
241
                                        content: `${originalContent}${toolsInfoString}`,
17✔
242
                                };
17✔
243
                        }
17✔
244
                }
17✔
245

246
                return modifiedMessages;
17✔
247
        }
17✔
248

249
        /**
16✔
250
         * Processes the model's response stream, extracts tool calls, executes them,
251
         * and returns a stream compatible with the OpenAI SDK format.
UNCOV
252
         */
×
UNCOV
253
        processModelResponse(
×
UNCOV
254
                generateTextFn: (messages: OpenAi.Chat.Completions.ChatCompletionMessageParam[], ...args: any[]) => Promise<ReadableStreamDefaultReader<Uint8Array>>,
×
UNCOV
255
                initialModelStreamReader: ReadableStreamDefaultReader<Uint8Array>,
×
UNCOV
256
                messages: OpenAi.Chat.ChatCompletionMessageParam[],
×
UNCOV
257
                responseChunkMapFn?: (responseBody: string) => string,
×
UNCOV
258
                ...generateTextArgs: any[]
×
UNCOV
259
        ): ReadableStreamDefaultReader<Uint8Array> {
×
UNCOV
260
                const toolCallExtractionReader = this._extractToolCallsFromStream(initialModelStreamReader, responseChunkMapFn);
×
UNCOV
261
                const openaiFormattedReader = this._formatStreamToOpenAIInterface(toolCallExtractionReader);
×
UNCOV
262
                const finalResponseReader = executeToolCalls(generateTextFn, openaiFormattedReader, messages, ...generateTextArgs);
×
UNCOV
263
                return this.mapResponse(finalResponseReader, false, OpenaiResponseMap);
×
UNCOV
264
        }
×
265

266
        /**
16✔
267
         * Maps the raw text chunks from a stream using an optional mapping function
268
         * and encodes them back to Uint8Array. Optionally formats as OpenAI stream chunks.
269
         */
16✔
270
        mapResponse(
16✔
271
                reader: ReadableStreamDefaultReader<Uint8Array>,
16✔
272
                formatAsOpenAIChunk: boolean = false,
16✔
273
                responseChunkMapFn?: (responseBody: string) => string,
16✔
274
        ): ReadableStreamDefaultReader<Uint8Array> {
16✔
275
                return new ReadableStream<Uint8Array>({
18✔
276
                        async start(controller) {
18✔
277
                                try {
20✔
278
                                        while (true) {
20✔
279
                                                const { done, value } = await reader.read();
25✔
280
                                                if (done) break;
25✔
281

282
                                                const rawChunkText = TEXT_DECODER.decode(value);
28✔
283
                                                const mappedText = responseChunkMapFn ? responseChunkMapFn(rawChunkText) : rawChunkText;
25✔
284

285
                                                if (!mappedText && !(formatAsOpenAIChunk && mappedText === '')) continue;
×
286

287
                                                const outputChunk = formatAsOpenAIChunk ? ToolUsageAdapter._createOpenAIStreamChunk(mappedText) : mappedText;
25✔
288

289
                                                controller.enqueue(TEXT_ENCODER.encode(outputChunk));
25✔
290
                                        }
25✔
291
                                } catch (error) {
×
292
                                        console.error('Error in mapResponse stream:', error);
×
293
                                        controller.error(error);
×
294
                                } finally {
×
295
                                        controller.close();
20✔
296
                                        reader.releaseLock();
20✔
297
                                }
20✔
298
                        },
20✔
299
                }).getReader();
18✔
300
        }
18✔
301

302
        /**
16✔
303
         * Creates a JSON string formatted as an OpenAI stream chunk.
304
         */
16✔
305
        private static _createOpenAIStreamChunk(text: string): string {
16✔
306
                const response: OpenAiStreamResponse = {
17✔
307
                        choices: [{
34✔
308
                                delta: { content: text },
51✔
UNCOV
309
                                finish_reason: text === '' ? 'stop' : null,
×
310
                        }],
34✔
311
                };
17✔
312
                return JSON.stringify(response);
17✔
313
        }
17✔
314

315
        /**
16✔
316
         * Processes a stream chunk containing the special adapter key for tool calls.
317
         * Parses the JSON and formats it into OpenAI SDK compatible tool call chunks.
318
         */
16✔
319
        private _processAdapterToolCallChunk(textChunk: string): {
16✔
320
                processed: boolean;
321
                toolCalls: Array<{ index: number; id?: string; type?: string; function?: { name?: string; arguments?: string } }> | null;
322
                formattedChunks: ToolCallResult[];
323
        } {
16✔
324
                const formattedChunks: ToolCallResult[] = [];
17✔
325
                let toolCalls: Array<{ index: number; id?: string; type?: string; function?: { name?: string; arguments?: string } }> = [];
17✔
326
                let processed = false;
17✔
327

UNCOV
328
                if (textChunk.includes(`"${TOOL_CALL_ADAPTER_KEY}"`) || textChunk.includes('function_call')) {
×
329
                        try {
17✔
330
                                const data = JSON.parse(textChunk);
17✔
331
                                if (
×
332
                                        data[TOOL_CALL_ADAPTER_KEY] ||
×
333
                                        (Array.isArray(data.output) && data.output.some((item: any) => item?.type === 'function_call'))
×
334
                                ) {
×
335
                                        processed = true;
17✔
336
                                        const extractedToolCalls: ToolCall[] = data[TOOL_CALL_ADAPTER_KEY] ||
×
337
                                                (Array.isArray(data.output) ? data.output.filter((item: any) => item?.type === 'function_call') : []);
×
338

339
                                        toolCalls = extractedToolCalls.map((call: any, index) => ({
17✔
340
                                                index: index,
18✔
341
                                                id: call.id,
18✔
342
                                                type: call.type,
18✔
343
                                                function: call.function ||
×
344
                                                        {
×
345
                                                                name: call.name,
×
346
                                                                arguments: typeof call.arguments === 'string' ? call.arguments : JSON.stringify(call.arguments ?? {}),
×
347
                                                        },
×
348
                                        }));
17✔
349

350
                                        for (const toolCall of toolCalls) {
17✔
351
                                                formattedChunks.push({
17✔
352
                                                        choices: [{
34✔
353
                                                                delta: { content: null, tool_calls: [toolCall] },
102✔
354
                                                                finish_reason: null,
17✔
355
                                                        }],
34✔
356
                                                });
17✔
357
                                        }
17✔
358

359
                                        formattedChunks.push({
17✔
360
                                                choices: [{
34✔
361
                                                        delta: { content: null },
51✔
362
                                                        finish_reason: 'tool_calls',
17✔
363
                                                }],
34✔
364
                                        });
17✔
365
                                }
17✔
366
                        } catch (error) {
×
367
                                console.error('Error processing adapter tool call chunk:', error, 'Chunk:', textChunk);
×
368
                                processed = false;
×
369
                                toolCalls = [];
×
370
                        }
×
371
                }
17✔
372

373
                return { processed, toolCalls: toolCalls.length > 0 ? toolCalls : null, formattedChunks };
68!
374
        }
17✔
375

376
        /**
16✔
377
         * Reads a stream, identifies chunks containing adapter-formatted tool calls,
378
         * processes them, and passes through other chunks formatted as OpenAI stream chunks.
379
         */
16✔
380
        private _formatStreamToOpenAIInterface(
16✔
381
                reader: ReadableStreamDefaultReader<Uint8Array>,
16✔
382
        ): ReadableStreamDefaultReader<Uint8Array> {
16✔
383
                // deno-lint-ignore no-this-alias
17✔
384
                const self = this;
17✔
385
                return new ReadableStream<Uint8Array>({
17✔
386
                        async start(controller) {
17✔
387
                                let toolCallsDetected = false;
18✔
388
                                try {
18✔
389
                                        while (true) {
18✔
390
                                                const { done, value } = await reader.read();
20✔
391
                                                if (done) break;
20✔
392

393
                                                const textChunk = TEXT_DECODER.decode(value);
21✔
394

395
                                                const toolProcessingResult = self._processAdapterToolCallChunk(textChunk);
21✔
396

397
                                                if (toolProcessingResult.processed) {
21✔
398
                                                        toolCallsDetected = true;
21✔
399
                                                        for (const result of toolProcessingResult.formattedChunks) {
21✔
400
                                                                controller.enqueue(TEXT_ENCODER.encode(JSON.stringify(result)));
23✔
401
                                                        }
23✔
UNCOV
402
                                                } else {
×
UNCOV
403
                                                        controller.enqueue(TEXT_ENCODER.encode(ToolUsageAdapter._createOpenAIStreamChunk(textChunk)));
×
UNCOV
404
                                                }
×
405
                                        }
20✔
406

UNCOV
407
                                        if (!toolCallsDetected) {
×
UNCOV
408
                                                controller.enqueue(TEXT_ENCODER.encode(ToolUsageAdapter._createOpenAIStreamChunk('')));
×
UNCOV
409
                                        }
×
410
                                } catch (error) {
×
411
                                        console.error('Error in _formatStreamToOpenAIInterface:', error);
×
412
                                        controller.error(error);
×
413
                                } finally {
×
414
                                        controller.close();
18✔
415
                                        reader.releaseLock();
18✔
416
                                }
18✔
417
                        },
18✔
418
                }).getReader();
17✔
419
        }
17✔
420

421
        /**
16✔
422
         * Reads a stream from the model, identifies markdown blocks potentially containing
423
         * function calls, extracts them, and yields either regular text chunks or
424
         * a special JSON chunk containing the extracted tool calls.
425
         */
16✔
426
        private _extractToolCallsFromStream(
16✔
427
                modelStreamReader: ReadableStreamDefaultReader<Uint8Array>,
16✔
428
                responseChunkMapFn?: (responseBody: string) => string,
16✔
429
        ): ReadableStreamDefaultReader<Uint8Array> {
16✔
430
                // deno-lint-ignore no-this-alias
17✔
431
                const self = this;
17✔
432
                return new ReadableStream<Uint8Array>({
17✔
433
                        async start(controller) {
17✔
434
                                let chunkBuffer = '';
18✔
435
                                let toolBlockBuffer = '';
18✔
436
                                let isInsideToolBlock = false;
18✔
437
                                let streamFinished = false;
18✔
438

439
                                try {
18✔
440
                                        while (!streamFinished) {
18✔
441
                                                const { done, value } = await modelStreamReader.read();
20✔
442
                                                if (done) streamFinished = true;
20✔
443

444
                                                const rawChunk = value ? TEXT_DECODER.decode(value, { stream: true }) : '';
61✔
445
                                                const currentChunk = responseChunkMapFn ? responseChunkMapFn(rawChunk) : rawChunk;
×
446

447
                                                if (isInsideToolBlock) {
20✔
448
                                                        toolBlockBuffer += currentChunk;
21✔
449
                                                        const endMatchIndex = toolBlockBuffer.search(TOOL_BLOCK_END_REGEX);
21✔
450

451
                                                        if (endMatchIndex !== -1) {
21✔
452
                                                                const blockEndMarkerLength = toolBlockBuffer.match(TOOL_BLOCK_END_REGEX)![0].length;
21✔
453
                                                                const fullBlock = toolBlockBuffer.slice(0, endMatchIndex + blockEndMarkerLength);
21✔
454
                                                                const remainingChunkPart = toolBlockBuffer.slice(endMatchIndex + blockEndMarkerLength);
21✔
455

456
                                                                const { toolCalls, cleanedContent } = self._extractToolCalls(fullBlock);
21✔
457
                                                                if (cleanedContent) controller.enqueue(TEXT_ENCODER.encode(cleanedContent));
×
458
                                                                if (toolCalls) {
21✔
459
                                                                        controller.enqueue(TEXT_ENCODER.encode(JSON.stringify({ [TOOL_CALL_ADAPTER_KEY]: toolCalls })));
63✔
460
                                                                }
21✔
461

462
                                                                isInsideToolBlock = false;
21✔
463
                                                                toolBlockBuffer = '';
21✔
464
                                                                chunkBuffer = remainingChunkPart;
21✔
465
                                                        }
21✔
466
                                                } else {
20✔
467
                                                        chunkBuffer += currentChunk;
21✔
468
                                                        const startMatch = chunkBuffer.match(TOOL_BLOCK_START_REGEX);
21✔
469

470
                                                        if (startMatch) {
21✔
471
                                                                const blockStartIndex = chunkBuffer.indexOf(startMatch[0]);
21✔
472
                                                                const textBeforeBlock = chunkBuffer.slice(0, blockStartIndex);
21✔
473

474
                                                                if (textBeforeBlock) controller.enqueue(TEXT_ENCODER.encode(textBeforeBlock));
21✔
475

476
                                                                isInsideToolBlock = true;
21✔
477
                                                                toolBlockBuffer = chunkBuffer.slice(blockStartIndex);
21✔
478
                                                                chunkBuffer = '';
21✔
UNCOV
479
                                                        } else if (streamFinished) {
×
480
                                                                if (chunkBuffer) controller.enqueue(TEXT_ENCODER.encode(chunkBuffer));
×
UNCOV
481
                                                                chunkBuffer = '';
×
UNCOV
482
                                                        } else {
×
UNCOV
483
                                                                const lastPotentialStart = chunkBuffer.lastIndexOf('```');
×
484
                                                                const safeEnqueueLength = lastPotentialStart === -1 ? chunkBuffer.length : lastPotentialStart;
×
485

UNCOV
486
                                                                if (safeEnqueueLength > 0) {
×
UNCOV
487
                                                                        controller.enqueue(TEXT_ENCODER.encode(chunkBuffer.slice(0, safeEnqueueLength)));
×
UNCOV
488
                                                                        chunkBuffer = chunkBuffer.slice(safeEnqueueLength);
×
UNCOV
489
                                                                }
×
UNCOV
490
                                                        }
×
491
                                                }
21✔
492
                                        }
20✔
493

494
                                        if (isInsideToolBlock && toolBlockBuffer) {
×
495
                                                console.warn('Stream ended while inside a tool block. Processing incomplete block.');
×
496
                                                const { toolCalls, cleanedContent } = self._extractToolCalls(toolBlockBuffer);
×
497
                                                if (cleanedContent) controller.enqueue(TEXT_ENCODER.encode(cleanedContent));
×
498
                                                if (toolCalls) {
×
499
                                                        controller.enqueue(TEXT_ENCODER.encode(JSON.stringify({ [TOOL_CALL_ADAPTER_KEY]: toolCalls })));
×
500
                                                }
×
501
                                        } else if (chunkBuffer) {
×
502
                                                controller.enqueue(TEXT_ENCODER.encode(chunkBuffer));
18✔
503
                                        }
18✔
504
                                } catch (error) {
×
505
                                        console.error('Error in _extractToolCallsFromStream:', error);
×
506
                                        controller.error(error);
×
507
                                } finally {
×
508
                                        controller.close();
18✔
509
                                        modelStreamReader.releaseLock();
18✔
510
                                }
18✔
511
                        },
18✔
512
                }).getReader();
17✔
513
        }
17✔
514
}
16✔
515

516
export default new ToolUsageAdapter();
16✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc