Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions agent/bedrock_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@ def normalize_converse_response(response: Dict) -> SimpleNamespace:
type="function",
function=SimpleNamespace(
name=tu.get("name", ""),
arguments=json.dumps(tu.get("input", {})),
arguments=json.dumps(tu.get("input", {}), ensure_ascii=False),
),
))

Expand Down Expand Up @@ -815,7 +815,7 @@ def stream_converse_with_callbacks(
type="function",
function=SimpleNamespace(
name=current_tool["name"],
arguments=json.dumps(input_dict),
arguments=json.dumps(input_dict, ensure_ascii=False),
),
))
current_tool = None
Expand Down
4 changes: 2 additions & 2 deletions agent/conversation_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -1039,7 +1039,7 @@ def run_conversation(
**tc["function"],
"arguments": json.dumps(
args_obj, separators=(",", ":"),
sort_keys=True,
sort_keys=True, ensure_ascii=False,
),
}}
except Exception:
Expand Down Expand Up @@ -3643,7 +3643,7 @@ def _stop_spinner():
for tc in assistant_message.tool_calls:
args = tc.function.arguments
if isinstance(args, (dict, list)):
tc.function.arguments = json.dumps(args)
tc.function.arguments = json.dumps(args, ensure_ascii=False)
continue
if args is not None and not isinstance(args, str):
tc.function.arguments = str(args)
Expand Down
29 changes: 28 additions & 1 deletion gateway/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -16949,6 +16949,23 @@ def _clarify_callback_sync(question: str, choices) -> str:
except Exception:
pass

# Ordering barrier: flush any assistant commentary still queued in the
# stream consumer so the clarify card never jumps ahead of the text that
# introduces it. Best-effort, bounded — never block the clarify path.
_sc = stream_consumer_holder[0]
if _sc is not None and hasattr(_sc, "drain"):
try:
_drain_fut = safe_schedule_threadsafe(
_sc.drain(timeout=5.0),
_loop_for_step,
logger=logger,
log_message="Clarify pre-flush drain failed to schedule",
)
if _drain_fut is not None:
_drain_fut.result(timeout=6) # slightly > drain's own timeout
except Exception as exc:
logger.debug("Clarify pre-flush drain skipped: %s", exc)

send_ok = False
fut = safe_schedule_threadsafe(
_status_adapter.send_clarify(
Expand Down Expand Up @@ -17486,7 +17503,17 @@ async def _start_stream_consumer():
"""Wait for the stream consumer to be created, then run it."""
for _ in range(200): # Up to 10s wait
if stream_consumer_holder[0] is not None:
await stream_consumer_holder[0].run()
try:
await stream_consumer_holder[0].run()
finally:
# The consumer's run() has finished (or this task was
# cancelled): clear the holder so a clarify firing AFTER
# stream completion does not enqueue a drain barrier onto
# a queue that nobody drains — which would stall the
# worker thread on drain()'s full timeout. The clarify
# callback guards on `_sc is not None`, so resetting here
# makes it skip the (now-pointless) pre-flush drain.
stream_consumer_holder[0] = None
return
await asyncio.sleep(0.05)

Expand Down
Loading
Loading