Skip to content

Commit d96faad

Browse files
fix: address P1 review findings
- _from_full_stream dict chunks: yield full dict instead of extracting text, matching the attribute-based path. Fixes structured updates being lost for native streaming adapters. Updated _wrapped_stream and _text_only_stream to handle dict chunks for accumulation/fallback. - on_lock_conflict: accept both True and "force" as force-release signal. Restores backward compat with callers returning bool while keeping the new string-based API. Type updated to accept bool in callable returns. - fallback stream final edit already fixed in previous commit (c3cbcff). The reviewer's worktree was stale. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c3cbcff commit d96faad

3 files changed

Lines changed: 9 additions & 4 deletions

File tree

src/chat_sdk/chat.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1515,7 +1515,7 @@ async def _resolve_lock_conflict(
15151515
# Support both sync and async callables
15161516
if asyncio.iscoroutine(result) or asyncio.isfuture(result):
15171517
result = await result
1518-
if result == "force":
1518+
if result == "force" or result is True:
15191519
self._logger.info(
15201520
"on_lock_conflict callback returned 'force', force-releasing lock",
15211521
{"thread_id": thread_id, "lock_key": lock_key},

src/chat_sdk/thread.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,8 @@ async def _wrapped_stream() -> AsyncIterator[str | StreamChunk]:
513513
async for chunk in text_stream:
514514
if isinstance(chunk, str):
515515
accumulated += chunk
516+
elif isinstance(chunk, dict) and chunk.get("type") == "markdown_text":
517+
accumulated += chunk.get("text", "")
516518
elif hasattr(chunk, "type") and chunk.type == "markdown_text":
517519
accumulated += chunk.text
518520
yield chunk
@@ -532,7 +534,9 @@ async def _text_only_stream() -> AsyncIterator[str]:
532534
async for chunk in text_stream:
533535
if isinstance(chunk, str):
534536
yield chunk
535-
elif hasattr(chunk, "type") and chunk.type == "markdown_text":
537+
elif isinstance(chunk, dict) and chunk.get("type") == "markdown_text":
538+
yield chunk.get("text", "")
539+
elif hasattr(chunk, "type") and getattr(chunk, "type", None) == "markdown_text":
536540
yield chunk.text
537541
# Skip non-text chunks in fallback mode
538542

@@ -884,7 +888,7 @@ async def _from_full_stream(raw_stream: Any) -> AsyncIterator[str | StreamChunk]
884888

885889
# Pass through known StreamChunk dict types
886890
if t in ("markdown_text", "task_update", "plan_update"):
887-
yield item.get("text", "")
891+
yield item
888892
continue
889893

890894
if t == "text-delta":

src/chat_sdk/types.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@
4040
LockScope = Literal["thread", "channel"]
4141
ConcurrencyStrategy = Literal["drop", "queue", "debounce", "concurrent"]
4242
OnLockConflict = (
43-
Literal["drop", "force"] | Callable[..., Awaitable[Literal["force", "drop"]] | Literal["force", "drop"]]
43+
Literal["drop", "force"]
44+
| Callable[..., Awaitable[Literal["force", "drop"] | bool] | Literal["force", "drop"] | bool]
4445
)
4546
FetchDirection = Literal["forward", "backward"]
4647

0 commit comments

Comments
 (0)