Skip to content

Commit 1bc2d67

Browse files
committed
Add reasoning content streaming support by parsing delta reasoning_content and using it as thoughts when present
- it's been reported that deepseek's reasoning is not properly detected [Pasted text] https://api-docs.deepseek.com/api/create-chat-completion/
1 parent abae30a commit 1bc2d67

1 file changed

Lines changed: 55 additions & 27 deletions

File tree

‎apps/editor/src/utils/send-llm-message.ts‎

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ const process_stream_chunk = (params: {
1515
}): {
1616
updated_buffer: string
1717
new_content: string
18+
new_reasoning_content: string
1819
} => {
1920
let updated_buffer = params.buffer
2021
let new_content = ''
22+
let new_reasoning_content = ''
2123
try {
2224
updated_buffer += params.chunk
2325
const lines = updated_buffer.split('\n')
@@ -34,10 +36,14 @@ const process_stream_chunk = (params: {
3436

3537
const json_data = JSON.parse(json_string)
3638
const content = json_data.choices?.[0]?.delta?.content
39+
const reasoning_content = json_data.choices?.[0]?.delta?.reasoning_content
3740

3841
if (typeof content == 'string') {
3942
new_content += content
4043
}
44+
if (typeof reasoning_content == 'string') {
45+
new_reasoning_content += reasoning_content
46+
}
4147
} catch (parse_error) {
4248
Logger.warn({
4349
function_name: 'process_stream_chunk',
@@ -57,7 +63,8 @@ const process_stream_chunk = (params: {
5763

5864
return {
5965
updated_buffer,
60-
new_content
66+
new_content,
67+
new_reasoning_content
6168
}
6269
}
6370

@@ -130,6 +137,7 @@ export const send_llm_message = async (params: {
130137
let in_think_block = false
131138
let think_block_closed = false
132139
let processed_think_content_length = 0
140+
let api_thoughts = ''
133141

134142
let last_log_time = Date.now()
135143
let logged_content_length = 0
@@ -291,11 +299,18 @@ export const send_llm_message = async (params: {
291299
let stream_closed = false
292300

293301
response.data.on('data', async (chunk: string) => {
294-
const { updated_buffer, new_content } = process_stream_chunk({
302+
const { updated_buffer, new_content, new_reasoning_content } = process_stream_chunk({
295303
chunk,
296304
buffer
297305
})
298306
buffer = updated_buffer
307+
308+
if (new_reasoning_content) {
309+
api_thoughts += new_reasoning_content
310+
handle_thinking_chunk(new_reasoning_content)
311+
handle_chunk_metrics(new_reasoning_content)
312+
}
313+
299314
process_content(new_content)
300315

301316
const current_time = Date.now()
@@ -321,10 +336,16 @@ export const send_llm_message = async (params: {
321336
if (json_string && json_string !== DONE_TOKEN) {
322337
const json_data = JSON.parse(json_string)
323338
const content = json_data.choices?.[0]?.delta?.content
339+
const reasoning_content = json_data.choices?.[0]?.delta?.reasoning_content
324340

325341
if (typeof content == 'string') {
326342
process_content(content)
327343
}
344+
if (typeof reasoning_content == 'string') {
345+
api_thoughts += reasoning_content
346+
handle_thinking_chunk(reasoning_content)
347+
handle_chunk_metrics(reasoning_content)
348+
}
328349
}
329350
}
330351
} catch (error) {
@@ -339,38 +360,45 @@ export const send_llm_message = async (params: {
339360
let thoughts: string | undefined = undefined
340361
let final_content = content_for_client
341362

342-
const start_match = full_response.match(/<(?:think|thought)>/)
343-
const end_match = full_response.match(/<\/(?:think|thought)>/)
344-
345-
if (start_match && end_match && end_match.index! > start_match.index!) {
346-
const content_start = start_match.index! + start_match[0].length
347-
thoughts = full_response
348-
.substring(content_start, end_match.index!)
349-
.trim()
350-
} else if (start_match && !end_match) {
351-
const content_start = start_match.index! + start_match[0].length
352-
thoughts = full_response.substring(content_start).trim()
353-
} else if (
354-
end_match &&
355-
(!start_match || start_match.index! > end_match.index!)
356-
) {
357-
thoughts = full_response.substring(0, end_match.index!).trim()
358-
final_content = full_response
359-
.substring(end_match.index! + end_match[0].length)
360-
.trim()
361-
Logger.info({
362-
function_name: 'send_llm_message',
363-
message:
364-
'Detected closing tag without opening tag (or before opening tag), stripped content before'
365-
})
363+
if (api_thoughts) {
364+
thoughts = api_thoughts
365+
if (!think_block_closed) {
366+
final_content = full_response
367+
}
368+
} else {
369+
const start_match = full_response.match(/<(?:think|thought)>/)
370+
const end_match = full_response.match(/<\/(?:think|thought)>/)
371+
372+
if (start_match && end_match && end_match.index! > start_match.index!) {
373+
const content_start = start_match.index! + start_match[0].length
374+
thoughts = full_response
375+
.substring(content_start, end_match.index!)
376+
.trim()
377+
} else if (start_match && !end_match) {
378+
const content_start = start_match.index! + start_match[0].length
379+
thoughts = full_response.substring(content_start).trim()
380+
} else if (
381+
end_match &&
382+
(!start_match || start_match.index! > end_match.index!)
383+
) {
384+
thoughts = full_response.substring(0, end_match.index!).trim()
385+
final_content = full_response
386+
.substring(end_match.index! + end_match[0].length)
387+
.trim()
388+
Logger.info({
389+
function_name: 'send_llm_message',
390+
message:
391+
'Detected closing tag without opening tag (or before opening tag), stripped content before'
392+
})
393+
}
366394
}
367395

368396
Logger.info({
369397
function_name: 'send_llm_message',
370398
message: 'Combined code received (full response):',
371399
data: full_response
372400
})
373-
if (in_think_block) {
401+
if (in_think_block || api_thoughts) {
374402
Logger.info({
375403
function_name: 'send_llm_message',
376404
message: 'Combined code received (for client):',

0 commit comments

Comments
 (0)