Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
C
coderai
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexlab
coderai
Commits
bcd150e2
Commit
bcd150e2
authored
Mar 01, 2026
by
Stefy Lanza (nextime / spora )
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix thinking display with timer thread and parse tool_call tags from content
parent
0c31d3fd
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
144 additions
and
103 deletions
+144
-103
coder
coder
+144
-103
No files found.
coder
View file @
bcd150e2
...
...
@@ -453,6 +453,7 @@ class CoderClient:
"""Handle streaming response from API."""
import
time
import
re
import
threading
full_content
=
""
tool_calls
=
[]
...
...
@@ -461,12 +462,27 @@ class CoderClient:
thinking_start_time
=
0
last_elapsed
=
-
1
thinking_line_printed
=
False
timer_thread
=
None
stop_timer
=
threading
.
Event
()
def
update_timer
():
"""Background thread to update timer every second."""
nonlocal
last_elapsed
while
not
stop_timer
.
is_set
():
if
in_thinking
:
current_time
=
time
.
time
()
elapsed
=
int
(
current_time
-
thinking_start_time
)
if
elapsed
!=
last_elapsed
:
last_elapsed
=
elapsed
print_thinking_line
(
elapsed
,
thinking_content
)
time
.
sleep
(
0.5
)
def
print_thinking_line
(
elapsed
,
content
,
final
=
False
):
"""Print or update the thinking line."""
nonlocal
thinking_line_printed
# Filter out <tool> tags and newlines for display
# Filter out <tool>
and <tool_call>
tags and newlines for display
display_content
=
re
.
sub
(
r'<tool[^>]*>.*?</tool>'
,
''
,
content
,
flags
=
re
.
DOTALL
)
display_content
=
re
.
sub
(
r'<tool_call[^>]*>.*?</tool_call>'
,
''
,
display_content
,
flags
=
re
.
DOTALL
)
display_content
=
display_content
.
replace
(
'
\n
'
,
' '
)
.
replace
(
'
\r
'
,
''
)
display_content
=
display_content
.
strip
()
...
...
@@ -476,33 +492,42 @@ class CoderClient:
line
=
f
"[{elapsed}s] Thinking: [{display_content}]"
if
not
thinking_line_printed
:
# First time - just print
print
(
f
"
\r
{Colors.DIM}{line}{Colors.RESET}"
,
end
=
''
,
flush
=
True
)
thinking_line_printed
=
True
else
:
# Overwrite existing line
# Clear to end of line and print new content
# Clear line and print
print
(
f
"
\r\033
[K{Colors.DIM}{line}{Colors.RESET}"
,
end
=
''
,
flush
=
True
)
thinking_line_printed
=
True
if
final
:
print
()
# Move to new line
def
parse_tool_calls_from_content
(
text
):
"""Parse <tool_call> tags from content."""
pattern
=
r'<tool_call>\s*(\{[^}]+\})\s*</tool_call>'
matches
=
re
.
findall
(
pattern
,
text
,
re
.
DOTALL
)
parsed
=
[]
for
match
in
matches
:
try
:
tool_data
=
json
.
loads
(
match
)
parsed
.
append
({
'id'
:
f
'call_{len(parsed)}'
,
'type'
:
'function'
,
'function'
:
{
'name'
:
tool_data
.
get
(
'name'
,
''
),
'arguments'
:
json
.
dumps
(
tool_data
.
get
(
'arguments'
,
{}))
}
})
except
json
.
JSONDecodeError
:
continue
return
parsed
# Start timer thread
timer_thread
=
threading
.
Thread
(
target
=
update_timer
,
daemon
=
True
)
timer_thread
.
start
()
# Use iter_content with smaller chunk size for better real-time handling
buffer
=
""
last_timer_update
=
time
.
time
()
try
:
for
chunk
in
response
.
iter_content
(
chunk_size
=
256
,
decode_unicode
=
True
):
current_time
=
time
.
time
()
# Update thinking timer every second
if
in_thinking
:
elapsed
=
int
(
current_time
-
thinking_start_time
)
if
elapsed
!=
last_elapsed
and
current_time
-
last_timer_update
>=
1.0
:
last_elapsed
=
elapsed
last_timer_update
=
current_time
print_thinking_line
(
elapsed
,
thinking_content
)
if
chunk
:
buffer
+=
chunk
...
...
@@ -531,11 +556,10 @@ class CoderClient:
if
'<think>'
in
content
:
in_thinking
=
True
thinking_start_time
=
time
.
time
()
last_timer_update
=
thinking_start_time
last_elapsed
=
0
thinking_content
=
""
thinking_line_printed
=
False
# Print initial thinking line
# Print initial thinking line immediately
print_thinking_line
(
0
,
""
)
continue
...
...
@@ -549,23 +573,36 @@ class CoderClient:
thinking_content
+=
think_part
# Show final thinking line
elapsed
=
int
(
current_time
-
thinking_start_time
)
elapsed
=
int
(
time
.
time
()
-
thinking_start_time
)
print_thinking_line
(
elapsed
,
thinking_content
,
final
=
True
)
# Get content after </think>
actual_content
=
parts
[
1
]
if
len
(
parts
)
>
1
else
""
if
actual_content
:
# Check for tool calls in the content
parsed_tools
=
parse_tool_calls_from_content
(
actual_content
)
if
parsed_tools
:
tool_calls
.
extend
(
parsed_tools
)
else
:
print
(
actual_content
,
end
=
''
,
flush
=
True
)
full_content
+=
actual_content
else
:
# Still thinking - accumulate
thinking_content
+=
content
# Update display every few chars
if
len
(
thinking_content
)
%
10
==
0
:
elapsed
=
int
(
time
.
time
()
-
thinking_start_time
)
print_thinking_line
(
elapsed
,
thinking_content
)
else
:
# Check for tool calls in normal content too
parsed_tools
=
parse_tool_calls_from_content
(
content
)
if
parsed_tools
:
tool_calls
.
extend
(
parsed_tools
)
else
:
# Normal content
print
(
content
,
end
=
''
,
flush
=
True
)
full_content
+=
content
# Handle tool calls
# Handle tool calls (OpenAI format)
delta_tool_calls
=
delta
.
get
(
'tool_calls'
)
if
delta_tool_calls
:
for
tc
in
delta_tool_calls
:
...
...
@@ -590,6 +627,10 @@ class CoderClient:
except
json
.
JSONDecodeError
:
continue
finally
:
stop_timer
.
set
()
if
timer_thread
:
timer_thread
.
join
(
timeout
=
1
)
if
in_thinking
:
print
()
# End thinking line if still in thinking
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment