Whole file
AgentBudget/agentbudget
The author described this change as “fix: remove dead code and add close() to streaming wrappers”. It counts as a record because the checks below fail on the code as it stood at 3bb401984 and pass on f6acf6c90, with nothing else changed between the two runs.
ProjectAgentBudget/agentbudget
Fix saved2026-03-25
Sharing licenceApache-2.0 · LICENSE
Change size+18 −7
What the code was meant to do, written into the code itself as a save note
fix: remove dead code and add close() to streaming wrappers
The change
| 149 | 149 | except Exception: | |
| 150 | 150 | pass | |
| 151 | 151 | ||
| 152 | + | def close(self) -> None: | |
| 153 | + | """Close the underlying stream (matches Stream.close() interface).""" | |
| 154 | + | close = getattr(self._stream, "close", None) | |
| 155 | + | if close is not None: | |
| 156 | + | try: | |
| 157 | + | close() | |
| 158 | + | except Exception: | |
| 159 | + | pass | |
| 152 | 160 | ||
| 161 | + | ||
| 153 | 162 | class _AsyncOpenAIStreamWrapper: | |
| 154 | 163 | """Wraps an async OpenAI ``AsyncStream`` with full async iterator and | |
| 155 | 164 | async context-manager support. | |
| ⋯ | |||
| 168 | 177 | self._stream = stream | |
| 169 | 178 | self._get_session = get_session | |
| 170 | 179 | ||
| 171 | - | def __aiter__(self) -> "_AsyncOpenAIStreamWrapper": | |
| 172 | - | return self | |
| 173 | - | ||
| 174 | - | async def __anext__(self) -> Any: | |
| 175 | - | # Delegate to the underlying async iterator | |
| 176 | - | raise StopAsyncIteration # overridden by __aiter__ + _iterate below | |
| 177 | - | ||
| 178 | 180 | async def _iterate(self) -> AsyncIterator: | |
| 179 | 181 | model: Optional[str] = None | |
| 180 | 182 | prompt_tokens: Optional[int] = None | |
| ⋯ | |||
| 281 | 283 | return self | |
| 282 | 284 | ||
| 283 | 285 | def __exit__(self, *args: Any) -> None: | |
| 286 | + | close = getattr(self._stream, "close", None) | |
| 287 | + | if close is not None: | |
| 288 | + | try: | |
| 289 | + | close() | |
| 290 | + | except Exception: | |
| 291 | + | pass | |
| 292 | + | ||
| 293 | + | def close(self) -> None: | |
| 294 | + | """Close the underlying stream (matches Stream.close() interface).""" | |
| 284 | 295 | close = getattr(self._stream, "close", None) | |
| 285 | 296 | if close is not None: | |
| 286 | 297 | try: | |
The check that tells the two apart
fail→pass·tests/test_streaming.py::test_anthropic_stream_wrapper_has_close
fail→pass·tests/test_streaming.py::test_openai_stream_wrapper_has_close
Check file tests/test_streaming.py, taken without changes from the fix and copied onto the older code, so the exact same check runs against both versions.
Origin and history
The code before it3bb40198496c147c54c82e0c843b6a7d83b0bccd
Broken version dated2026-03-25
Moduleagentbudget._patch
Units changed_AnthropicStreamWrapper, _AsyncOpenAIStreamWrapper, _OpenAIStreamWrapper
Fingerprintc84f7b981a0d1228
Checked2026-08-18 by goldset/0.1
Every field above is generated by our program. None of it is written by hand.
Other bugs found in AgentBudget/agentbudget
- 2026-03-25LoopDetector