Skip to content

Commit ad3e6f1

Browse files
committed
Stream all API response content directly; remove any initial <think> block during post-processing
1 parent e06c1f0 commit ad3e6f1

2 files changed

Lines changed: 54 additions & 134 deletions

File tree

‎packages/vscode/src/utils/cleanup-api-response.ts‎

Lines changed: 49 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,61 +3,59 @@
33
* at the beginning and end of the content, without affecting the middle content.
44
*/
55
export function cleanup_api_response(params: { content: string }): string {
6-
try {
7-
let content = params.content
8-
let changed = true
9-
10-
// Continue processing until no more changes are made
11-
while (changed) {
12-
const original_content = content
13-
14-
// Attempt to strip opening wrappers (only from the beginning)
15-
const opening_patterns = [
16-
/^```[^\n]*\n/, // Markdown code block start
17-
/^<files[^>]*>\s*\n?/, // Files wrapper start
18-
/^<file[^>]*>\s*\n?/, // File wrapper start
19-
/^<!\[CDATA\[\s*\n?/, // CDATA start
20-
/^<!DOCTYPE[^>]*>\s*\n?/ // DOCTYPE declaration
21-
]
22-
23-
for (const pattern of opening_patterns) {
24-
const match = content.match(pattern)
25-
if (match && match.index == 0) {
26-
content = content.substring(match[0].length)
27-
break // Only remove one wrapper per iteration
28-
}
29-
}
6+
let content = params.content
7+
let changed = true
308

31-
// Attempt to strip closing wrappers (only from the end)
32-
const closing_patterns = [
33-
/\s*```\s*$/, // Markdown code block end
34-
/\s*<\/files>\s*$/, // Files wrapper end
35-
/\s*<\/file>\s*$/, // File wrapper end
36-
/\s*\]\]>\s*$/, // CDATA end
37-
/\s*\]\]\s*$/ // Potentially incomplete CDATA end (just "]]")
38-
]
39-
40-
for (const pattern of closing_patterns) {
41-
const match = content.match(pattern)
42-
if (
43-
match &&
44-
match.index !== undefined &&
45-
match.index + match[0].length == content.length
46-
) {
47-
content = content.substring(0, match.index)
48-
break // Only remove one wrapper per iteration
49-
}
50-
}
9+
if (content.startsWith('<think>')) {
10+
const think_end_index = content.indexOf('</think>')
11+
if (think_end_index != -1) {
12+
content = content.substring(think_end_index + '</think>'.length)
13+
}
14+
}
5115

52-
// Check if any changes were made in this iteration
53-
changed = content != original_content
16+
while (changed) {
17+
const original_content = content
18+
19+
const opening_patterns = [
20+
/^```[^\n]*\n/, // Markdown code block start
21+
/^<files[^>]*>\s*\n?/, // Files wrapper start
22+
/^<file[^>]*>\s*\n?/, // File wrapper start
23+
/^<!\[CDATA\[\s*\n?/, // CDATA start
24+
/^<!DOCTYPE[^>]*>\s*\n?/ // DOCTYPE declaration
25+
]
26+
27+
for (const pattern of opening_patterns) {
28+
const match = content.match(pattern)
29+
if (match && match.index == 0) {
30+
content = content.substring(match[0].length)
31+
break
32+
}
5433
}
5534

56-
content = content.trim()
35+
const closing_patterns = [
36+
/\s*```\s*$/, // Markdown code block end
37+
/\s*<\/files>\s*$/, // Files wrapper end
38+
/\s*<\/file>\s*$/, // File wrapper end
39+
/\s*\]\]>\s*$/, // CDATA end
40+
/\s*\]\]\s*$/ // Potentially incomplete CDATA end (just "]]")
41+
]
42+
43+
for (const pattern of closing_patterns) {
44+
const match = content.match(pattern)
45+
if (
46+
match &&
47+
match.index !== undefined &&
48+
match.index + match[0].length == content.length
49+
) {
50+
content = content.substring(0, match.index)
51+
break
52+
}
53+
}
5754

58-
return content
59-
} catch (error) {
60-
console.error('Error cleaning up API response:', error)
61-
return params.content // Return original content if an error occurs
55+
changed = content != original_content
6256
}
57+
58+
content = content.trim()
59+
60+
return content
6361
}

‎packages/vscode/src/utils/make-api-request.ts‎

Lines changed: 5 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,21 @@ type StreamCallback = (chunk: string) => void
66

77
const DATA_PREFIX = 'data: '
88
const DONE_TOKEN = '[DONE]'
9-
const THINK_OPEN = '<think>'
10-
const THINK_CLOSE = '</think>'
119

1210
async function process_stream_chunk(
1311
chunk: string,
1412
buffer: string,
1513
accumulated_content: string,
1614
last_log_time: number,
17-
think_buffer: string,
18-
in_think_block: boolean,
19-
think_block_ended: boolean,
2015
on_chunk?: StreamCallback
2116
): Promise<{
2217
updated_buffer: string
2318
updated_accumulated_content: string
2419
updated_last_log_time: number
25-
updated_think_buffer: string
26-
updated_in_think_block: boolean
27-
updated_think_block_ended: boolean
2820
}> {
2921
let updated_buffer = buffer
3022
let updated_accumulated_content = accumulated_content
3123
let updated_last_log_time = last_log_time
32-
let updated_think_buffer = think_buffer
33-
let updated_in_think_block = in_think_block
34-
let updated_think_block_ended = think_block_ended
3524

3625
try {
3726
updated_buffer += chunk
@@ -52,64 +41,9 @@ async function process_stream_chunk(
5241
const new_content = json_data.choices[0].delta.content
5342
updated_accumulated_content += new_content
5443

55-
// --- Think block handling logic ---
56-
if (updated_think_block_ended) {
57-
// If the first think block has already ended, stream all new content
58-
if (on_chunk) {
59-
on_chunk(new_content)
60-
}
61-
} else {
62-
// We are either before, or inside, the first think block
63-
updated_think_buffer += new_content
64-
65-
if (!updated_in_think_block) {
66-
// We are currently *not* in a think block (haven't seen <think> yet)
67-
const think_open_index =
68-
updated_think_buffer.indexOf(THINK_OPEN)
69-
if (think_open_index !== -1) {
70-
// <think> tag found!
71-
updated_in_think_block = true
72-
// Stream content *before* <think>
73-
const pre_think_content = updated_think_buffer.substring(
74-
0,
75-
think_open_index
76-
)
77-
if (pre_think_content && on_chunk) {
78-
on_chunk(pre_think_content)
79-
}
80-
// The remaining part of updated_think_buffer now starts with <think>
81-
updated_think_buffer =
82-
updated_think_buffer.substring(think_open_index)
83-
} else {
84-
// No <think> tag found yet, stream this content
85-
if (on_chunk) {
86-
on_chunk(new_content)
87-
}
88-
}
89-
}
90-
91-
// Now, if we are in a think block (either just entered or already were)
92-
if (updated_in_think_block) {
93-
const think_close_index =
94-
updated_think_buffer.indexOf(THINK_CLOSE)
95-
if (think_close_index != -1) {
96-
// </think> tag found!
97-
updated_in_think_block = false
98-
updated_think_block_ended = true
99-
// Stream content *after* </think>
100-
const post_think_content = updated_think_buffer.substring(
101-
think_close_index + THINK_CLOSE.length
102-
)
103-
if (post_think_content && on_chunk) {
104-
on_chunk(post_think_content)
105-
}
106-
// Clear the think buffer as the first think block is fully processed
107-
updated_think_buffer = ''
108-
}
109-
// If </think> not found, we remain in_think_block and do not stream.
110-
}
44+
if (on_chunk) {
45+
on_chunk(new_content)
11146
}
112-
// --- End think block handling logic ---
11347

11448
const current_time = Date.now()
11549
if (current_time - updated_last_log_time >= 1000) {
@@ -141,10 +75,7 @@ async function process_stream_chunk(
14175
return {
14276
updated_buffer,
14377
updated_accumulated_content,
144-
updated_last_log_time,
145-
updated_think_buffer,
146-
updated_in_think_block,
147-
updated_think_block_ended
78+
updated_last_log_time
14879
}
14980
}
15081

@@ -161,9 +92,6 @@ export async function make_api_request(
16192
let accumulated_content = ''
16293
let last_log_time = Date.now()
16394
let buffer = ''
164-
let think_buffer = ''
165-
let in_think_block = false
166-
let think_block_ended = false
16795

16896
const response: AxiosResponse<NodeJS.ReadableStream> = await axios.post(
16997
endpoint_url + '/chat/completions',
@@ -184,7 +112,7 @@ export async function make_api_request(
184112
}
185113
)
186114

187-
response.data.setEncoding('utf8');
115+
response.data.setEncoding('utf8')
188116

189117
return new Promise((resolve, reject) => {
190118
response.data.on('data', async (chunk: string) => {
@@ -193,17 +121,11 @@ export async function make_api_request(
193121
buffer,
194122
accumulated_content,
195123
last_log_time,
196-
think_buffer,
197-
in_think_block,
198-
think_block_ended,
199124
on_chunk
200125
)
201126
buffer = processing_result.updated_buffer
202127
accumulated_content = processing_result.updated_accumulated_content
203128
last_log_time = processing_result.updated_last_log_time
204-
think_buffer = processing_result.updated_think_buffer
205-
in_think_block = processing_result.updated_in_think_block
206-
think_block_ended = processing_result.updated_think_block_ended
207129
})
208130

209131
response.data.on('end', () => {
@@ -270,4 +192,4 @@ export async function make_api_request(
270192
})
271193
return null
272194
}
273-
}
195+
}

0 commit comments

Comments
 (0)