Skip to content

Commit 200eb12

Browse files
authored
feat: Add on reasoning event callback function handling in streaming chat completions (#7)
* feat: Add on reasoning event callback function handling in streaming chat completions Signed-off-by: Eden Reich <[email protected]> * docs: Update readme with the new callback Signed-off-by: Eden Reich <[email protected]> --------- Signed-off-by: Eden Reich <[email protected]>
1 parent ee8664d commit 200eb12

File tree

3 files changed

+78
-5
lines changed

3 files changed

+78
-5
lines changed

README.md

+9-5
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,11 @@ const client = new InferenceGatewayClient({
155155
try {
156156
await client.streamChatCompletion(
157157
{
158-
model: 'gpt-4o',
158+
model: 'openai/gpt-4o',
159159
messages: [
160160
{
161161
role: MessageRole.User,
162-
content: 'What's the weather in San Francisco?',
162+
content: "What's the weather in San Francisco?",
163163
},
164164
],
165165
tools: [
@@ -186,10 +186,14 @@ try {
186186
console.log('Tool call:', toolCall.function.name);
187187
console.log('Arguments:', toolCall.function.arguments);
188188
},
189-
onContent: (content) => process.stdout.write(content),
189+
onReasoning: (reasoning) => {
190+
console.log('Reasoning:', reasoning);
191+
},
192+
onContent: (content) => {
193+
console.log('Content:', content);
194+
},
190195
onFinish: () => console.log('\nStream completed'),
191-
},
192-
Provider.OpenAI
196+
}
193197
);
194198
} catch (error) {
195199
console.error('Error:', error);

src/client.ts

+7
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { ChatCompletionToolType } from './types/generated';
1212
interface ChatCompletionStreamCallbacks {
1313
onOpen?: () => void;
1414
onChunk?: (chunk: SchemaCreateChatCompletionStreamResponse) => void;
15+
onReasoning?: (reasoningContent: string) => void;
1516
onContent?: (content: string) => void;
1617
onTool?: (toolCall: SchemaChatCompletionMessageToolCall) => void;
1718
onFinish?: (
@@ -257,6 +258,12 @@ export class InferenceGatewayClient {
257258
JSON.parse(data);
258259
callbacks.onChunk?.(chunk);
259260

261+
const reasoning_content =
262+
chunk.choices[0]?.delta?.reasoning_content;
263+
if (reasoning_content !== undefined) {
264+
callbacks.onReasoning?.(reasoning_content);
265+
}
266+
260267
const content = chunk.choices[0]?.delta?.content;
261268
if (content) {
262269
callbacks.onContent?.(content);

tests/client.test.ts

+62
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,68 @@ describe('InferenceGatewayClient', () => {
263263
);
264264
});
265265

266+
it('should handle streaming chat completions reasoning and content', async () => {
267+
const mockRequest = {
268+
model: 'gpt-4o',
269+
messages: [
270+
{ role: MessageRole.user, content: 'Hello' },
271+
],
272+
stream: true,
273+
};
274+
const mockStream = new TransformStream();
275+
const writer = mockStream.writable.getWriter();
276+
const encoder = new TextEncoder();
277+
mockFetch.mockResolvedValueOnce({
278+
ok: true,
279+
body: mockStream.readable,
280+
});
281+
const callbacks = {
282+
onOpen: jest.fn(),
283+
onChunk: jest.fn(),
284+
onReasoning: jest.fn(),
285+
onContent: jest.fn(),
286+
onFinish: jest.fn(),
287+
};
288+
const streamPromise = client.streamChatCompletion(mockRequest, callbacks);
289+
await writer.write(
290+
encoder.encode(
291+
'data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-4o","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}\n\n' +
292+
'data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"","reasoning_content":"This"},"finish_reason":null}]}\n\n' +
293+
'data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"","reasoning_content":" is"},"finish_reason":null}]}\n\n' +
294+
'data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"","reasoning_content":" a"},"finish_reason":"stop"}]}\n\n' +
295+
'data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"","reasoning_content":" reasoning"},"finish_reason":"stop"}]}\n\n' +
296+
'data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"","reasoning_content":" content"},"finish_reason":"stop"}]}\n\n' +
297+
'data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}]}\n\n' +
298+
'data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"!"},"finish_reason":null}]}\n\n' +
299+
'data: [DONE]\n\n'
300+
)
301+
);
302+
await writer.close();
303+
await streamPromise;
304+
expect(callbacks.onOpen).toHaveBeenCalledTimes(1);
305+
expect(callbacks.onChunk).toHaveBeenCalledTimes(8);
306+
expect(callbacks.onReasoning).toHaveBeenCalledTimes(5);
307+
expect(callbacks.onReasoning).toHaveBeenCalledWith('This');
308+
expect(callbacks.onReasoning).toHaveBeenCalledWith(' is');
309+
expect(callbacks.onReasoning).toHaveBeenCalledWith(' a');
310+
expect(callbacks.onReasoning).toHaveBeenCalledWith(' reasoning');
311+
expect(callbacks.onReasoning).toHaveBeenCalledWith(' content');
312+
expect(callbacks.onContent).toHaveBeenCalledTimes(2);
313+
expect(callbacks.onContent).toHaveBeenCalledWith('Hello');
314+
expect(callbacks.onContent).toHaveBeenCalledWith('!');
315+
expect(callbacks.onFinish).toHaveBeenCalledTimes(1);
316+
expect(mockFetch).toHaveBeenCalledWith(
317+
'http://localhost:8080/v1/chat/completions',
318+
expect.objectContaining({
319+
method: 'POST',
320+
body: JSON.stringify({
321+
...mockRequest,
322+
stream: true,
323+
}),
324+
})
325+
);
326+
});
327+
266328
it('should handle tool calls in streaming chat completions', async () => {
267329
const mockRequest = {
268330
model: 'gpt-4o',

0 commit comments

Comments
 (0)