Skip to content

Commit f740f89

Browse files
committed
Implement logic to hide content within <think> and </think> blocks from streamed API responses until the first such block is fully processed
1 parent 7abc807 commit f740f89

1 file changed

Lines changed: 83 additions & 5 deletions

File tree

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

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,32 @@ 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>'
911

1012
async function process_stream_chunk(
1113
chunk: string,
1214
buffer: string,
1315
accumulated_content: string,
1416
last_log_time: number,
17+
think_buffer: string,
18+
in_think_block: boolean,
19+
think_block_ended: boolean,
1520
on_chunk?: StreamCallback
1621
): Promise<{
1722
updated_buffer: string
1823
updated_accumulated_content: string
1924
updated_last_log_time: number
25+
updated_think_buffer: string
26+
updated_in_think_block: boolean
27+
updated_think_block_ended: boolean
2028
}> {
2129
let updated_buffer = buffer
2230
let updated_accumulated_content = accumulated_content
2331
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
2435

2536
try {
2637
updated_buffer += chunk
@@ -41,9 +52,64 @@ async function process_stream_chunk(
4152
const new_content = json_data.choices[0].delta.content
4253
updated_accumulated_content += new_content
4354

44-
if (on_chunk) {
45-
on_chunk(new_content)
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+
}
46111
}
112+
// --- End think block handling logic ---
47113

48114
const current_time = Date.now()
49115
if (current_time - updated_last_log_time >= 1000) {
@@ -75,7 +141,10 @@ async function process_stream_chunk(
75141
return {
76142
updated_buffer,
77143
updated_accumulated_content,
78-
updated_last_log_time
144+
updated_last_log_time,
145+
updated_think_buffer,
146+
updated_in_think_block,
147+
updated_think_block_ended
79148
}
80149
}
81150

@@ -92,6 +161,9 @@ export async function make_api_request(
92161
let accumulated_content = ''
93162
let last_log_time = Date.now()
94163
let buffer = ''
164+
let think_buffer = ''
165+
let in_think_block = false
166+
let think_block_ended = false
95167

96168
const response: AxiosResponse<NodeJS.ReadableStream> = await axios.post(
97169
endpoint_url + '/chat/completions',
@@ -112,7 +184,7 @@ export async function make_api_request(
112184
}
113185
)
114186

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

117189
return new Promise((resolve, reject) => {
118190
response.data.on('data', async (chunk: string) => {
@@ -121,11 +193,17 @@ export async function make_api_request(
121193
buffer,
122194
accumulated_content,
123195
last_log_time,
196+
think_buffer,
197+
in_think_block,
198+
think_block_ended,
124199
on_chunk
125200
)
126201
buffer = processing_result.updated_buffer
127202
accumulated_content = processing_result.updated_accumulated_content
128203
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
129207
})
130208

131209
response.data.on('end', () => {
@@ -192,4 +270,4 @@ export async function make_api_request(
192270
})
193271
return null
194272
}
195-
}
273+
}

0 commit comments

Comments
 (0)