Summary
Three related body-sizing issues:
1. aiohttp json= and FormData report 0 bytes
recost/_interceptor.py:317-325 checks only the data= kwarg. The most common aiohttp POST patterns (session.post(url, json={...}), session.post(url, data=FormData(...)), async-iterable bodies, BytesIO) all fall through and report 0.
2. httpx streaming body silently materialized
_interceptor.py:198-201, 244-247 accesses request.content to compute body size. For ordinary requests built via client methods, content is bytes — fine. For users passing a custom streaming body (httpx.Request("POST", url, content=async_iterator)), accessing content reads and buffers the entire iterator. A large upload silently OOMs the process.
3. Response body size derived from Content-Length only
Chunked / streaming responses (LLM SSE streams) don't set the header; they always report response_bytes=0. The README's "response body size (bytes)" promise is partially false for streams.
Fix
- For aiohttp: when
json= is present, JSON-serialize and measure; for FormData, query its _size; for unknown body types, leave at 0 but document.
- For httpx:
isinstance(request.content, bytes) check before reading. Non-bytes → skip size measurement.
- For responses: document the streaming caveat in README; optionally tee non-streaming response bodies.
Files
recost/_interceptor.py
README.md
tests/test_interceptor.py
Priority
P1 — undercounts bytes for the most common modern HTTP patterns; OOM risk on streaming uploads.
Summary
Three related body-sizing issues:
1. aiohttp
json=andFormDatareport 0 bytesrecost/_interceptor.py:317-325checks only thedata=kwarg. The most common aiohttp POST patterns (session.post(url, json={...}),session.post(url, data=FormData(...)), async-iterable bodies,BytesIO) all fall through and report 0.2. httpx streaming body silently materialized
_interceptor.py:198-201, 244-247accessesrequest.contentto compute body size. For ordinary requests built via client methods,contentis bytes — fine. For users passing a custom streaming body (httpx.Request("POST", url, content=async_iterator)), accessingcontentreads and buffers the entire iterator. A large upload silently OOMs the process.3. Response body size derived from
Content-LengthonlyChunked / streaming responses (LLM SSE streams) don't set the header; they always report
response_bytes=0. The README's "response body size (bytes)" promise is partially false for streams.Fix
json=is present, JSON-serialize and measure; forFormData, query its_size; for unknown body types, leave at 0 but document.isinstance(request.content, bytes)check before reading. Non-bytes → skip size measurement.Files
recost/_interceptor.pyREADME.mdtests/test_interceptor.pyPriority
P1 — undercounts bytes for the most common modern HTTP patterns; OOM risk on streaming uploads.