Skip to content

v0.8.9: cmdcode streaming tool call fixes - #10

Merged
evangit2 merged 9 commits into
mainfrom
develop
Jul 31, 2026
Merged

v0.8.9: cmdcode streaming tool call fixes#10
evangit2 merged 9 commits into
mainfrom
develop

Conversation

@evangit2

Copy link
Copy Markdown
Owner

Fixes

  1. Add required index field to streaming tool_calls deltas — OpenAI SDK requires index on ChoiceDeltaToolCall; missing it caused ValidationError → APIError
  2. Guard against non-dict tool call arguments in DSML conversionargs.items() crash when tool call arguments are a raw JSON string instead of object ('str' object has no attribute 'items')
  3. DSML buffer overflow guard + fuzzy parser fallback — malformed DSML closing tags caused infinite buffering and tag leakage into chat; now fuzzy-parses and flushes at 8000 chars
  4. Strip hallucinated tool call tags from text output
  5. Use consistent stream chunk ID across all chunks in a stream
  6. Version bump to 0.8.9

Test Results

  • 157/157 tests pass
  • 6/6 end-to-end prod checks pass (health, models, streaming, non-streaming, streaming+tools, non-streaming+tools)

evangit2 added 9 commits July 31, 2026 15:34
…aming path

The async for loop was outside the async with stream context manager,
causing 'Attempted to read or stream content, but the stream has been closed.'
Each streaming chunk was generating a new UUID, which broke OpenAI client
expectations that all chunks in a stream share the same ID. This caused
'str' object has no attribute 'items' errors on some Hermes instances.

Also pass chunk_id to _build_usage_chunk so the final usage chunk matches.
Models sometimes emit malformed tags like </aktool_calls> in text output
instead of using the structured SSE tool events. These leak through to the
user as raw text. Now stripped in both streaming and non-streaming paths.
The OpenAI SDK requires 'index' on every tool_calls delta entry in
streaming chunks (ChoiceDeltaToolCall). Without it, pydantic validation
fails with 'str' object has no attribute 'items' — crashing the Hermes
client after 3 retries.

Non-streaming tool_calls don't need index (different pydantic model).
…ersion

When a model's previous tool call has arguments that are a raw JSON
string (e.g. '"hello"' instead of '{"key": "value"}'), json.loads
returns a string, and calling .items() on it crashes with
'AttributeError: str object has no attribute items'.

This error was caught by Guanaco's streaming error handler and sent
as an SSE error chunk, which the OpenAI SDK wrapped as APIError with
the message 'str object has no attribute items'.

Fix: wrap non-dict args in a {'value': args} dict before iterating.
When the model emits malformed DSML with broken closing tags (e.g.
'</peparameter>' instead of '</|DSML|parameter>'), the standard parser
never matches the close tag and the buffer grows indefinitely. The
model then retries the path character-by-character, producing massive
DSML tag leakage into the chat.

Fix:
- Add 8000-char buffer overflow guard: if DSML buffer exceeds limit
  without completing, try fuzzy parse before flushing as content
- Add _fuzzy_parse_dsml(): extracts invoke names and parameters even
  with broken closing tags, using both standard and broken-tag regexes
- Prevents infinite buffering and tag leakage
@evangit2
evangit2 merged commit 0132b1b into main Jul 31, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant