Latent bug — config field silently ignored.
The bug
ConcurrencyConfig.max_concurrent is accepted from user config and assigned to self._concurrency_max_concurrent in `Chat.init` at `src/chat_sdk/chat.py:280,287,295`, but no code reads that field anywhere. Users setting `max_concurrent=5` expect a 5-way semaphore and get unlimited concurrency.
```bash
$ grep -n 'max_concurrent|_concurrency_max_concurrent' src/chat_sdk/chat.py
280: self._concurrency_max_concurrent: int | None = None
287: self._concurrency_max_concurrent = None
295: self._concurrency_max_concurrent = concurrency.max_concurrent
```
Three writes, zero reads.
Upstream behavior
Upstream TS uses it as a semaphore limit for the `"concurrent"` strategy:
- `vercel-chat/packages/chat/src/chat.ts:269,278,288` — defaults to `Number.POSITIVE_INFINITY`
- `vercel-chat/packages/chat/src/types.ts:711` — field on `ConcurrencyConfig`
Fix
- Add an `asyncio.Semaphore` initialized to `max_concurrent` (or bypass if `None` / infinity) in `Chat.init`
- Acquire it in the `"concurrent"` strategy path before dispatching handlers
- Release on completion/error
- Regression test: 10 concurrent messages with `max_concurrent=2` should serialize into 5 pairs
Impact
- chinchill-api uses `concurrency="drop"`, unaffected today
- Other consumers with `concurrency=ConcurrencyConfig(strategy="concurrent", max_concurrent=N)` silently get unlimited concurrency
Acceptance
Latent bug — config field silently ignored.
The bug
ConcurrencyConfig.max_concurrentis accepted from user config and assigned toself._concurrency_max_concurrentin `Chat.init` at `src/chat_sdk/chat.py:280,287,295`, but no code reads that field anywhere. Users setting `max_concurrent=5` expect a 5-way semaphore and get unlimited concurrency.```bash
$ grep -n 'max_concurrent|_concurrency_max_concurrent' src/chat_sdk/chat.py
280: self._concurrency_max_concurrent: int | None = None
287: self._concurrency_max_concurrent = None
295: self._concurrency_max_concurrent = concurrency.max_concurrent
```
Three writes, zero reads.
Upstream behavior
Upstream TS uses it as a semaphore limit for the `"concurrent"` strategy:
Fix
Impact
Acceptance