Spaces:
Build error
Build error
pythoninthegrass Claude Sonnet 4.5 commited on
Commit ·
8599bf7
1
Parent(s): bb04104
Fix ZlibError by removing compression headers after httpx decompression
Browse fileshttpx automatically decompresses responses when reading response.content,
but the proxy was forwarding the original Content-Encoding headers. This
caused clients to attempt double-decompression, resulting in ZlibError.
Fixes compression header handling in:
- Non-streaming Anthropic/OpenAI responses
- Cached responses
- Passthrough handler
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- headroom/proxy/server.py +31 -5
headroom/proxy/server.py
CHANGED
|
@@ -1018,9 +1018,14 @@ class HeadroomProxy:
|
|
| 1018 |
cached=True,
|
| 1019 |
)
|
| 1020 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1021 |
return Response(
|
| 1022 |
content=cached.response_body,
|
| 1023 |
-
headers=
|
| 1024 |
media_type="application/json",
|
| 1025 |
)
|
| 1026 |
|
|
@@ -1297,10 +1302,15 @@ class HeadroomProxy:
|
|
| 1297 |
f"(saved {tokens_saved:,} tokens)"
|
| 1298 |
)
|
| 1299 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1300 |
return Response(
|
| 1301 |
content=response.content,
|
| 1302 |
status_code=response.status_code,
|
| 1303 |
-
headers=
|
| 1304 |
)
|
| 1305 |
|
| 1306 |
except Exception as e:
|
|
@@ -1411,7 +1421,13 @@ class HeadroomProxy:
|
|
| 1411 |
latency_ms=(time.time() - start_time) * 1000,
|
| 1412 |
cached=True,
|
| 1413 |
)
|
| 1414 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1415 |
|
| 1416 |
# Token counting
|
| 1417 |
tokenizer = get_tokenizer(model)
|
|
@@ -1535,10 +1551,15 @@ class HeadroomProxy:
|
|
| 1535 |
f"(saved {tokens_saved:,} tokens)"
|
| 1536 |
)
|
| 1537 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1538 |
return Response(
|
| 1539 |
content=response.content,
|
| 1540 |
status_code=response.status_code,
|
| 1541 |
-
headers=
|
| 1542 |
)
|
| 1543 |
except Exception as e:
|
| 1544 |
self.metrics.record_failed()
|
|
@@ -1561,10 +1582,15 @@ class HeadroomProxy:
|
|
| 1561 |
content=body,
|
| 1562 |
)
|
| 1563 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1564 |
return Response(
|
| 1565 |
content=response.content,
|
| 1566 |
status_code=response.status_code,
|
| 1567 |
-
headers=
|
| 1568 |
)
|
| 1569 |
|
| 1570 |
|
|
|
|
| 1018 |
cached=True,
|
| 1019 |
)
|
| 1020 |
|
| 1021 |
+
# Remove compression headers from cached response
|
| 1022 |
+
response_headers = dict(cached.response_headers)
|
| 1023 |
+
response_headers.pop("content-encoding", None)
|
| 1024 |
+
response_headers.pop("content-length", None)
|
| 1025 |
+
|
| 1026 |
return Response(
|
| 1027 |
content=cached.response_body,
|
| 1028 |
+
headers=response_headers,
|
| 1029 |
media_type="application/json",
|
| 1030 |
)
|
| 1031 |
|
|
|
|
| 1302 |
f"(saved {tokens_saved:,} tokens)"
|
| 1303 |
)
|
| 1304 |
|
| 1305 |
+
# Remove compression headers since httpx already decompressed the response
|
| 1306 |
+
response_headers = dict(response.headers)
|
| 1307 |
+
response_headers.pop("content-encoding", None)
|
| 1308 |
+
response_headers.pop("content-length", None) # Length changed after decompression
|
| 1309 |
+
|
| 1310 |
return Response(
|
| 1311 |
content=response.content,
|
| 1312 |
status_code=response.status_code,
|
| 1313 |
+
headers=response_headers,
|
| 1314 |
)
|
| 1315 |
|
| 1316 |
except Exception as e:
|
|
|
|
| 1421 |
latency_ms=(time.time() - start_time) * 1000,
|
| 1422 |
cached=True,
|
| 1423 |
)
|
| 1424 |
+
|
| 1425 |
+
# Remove compression headers from cached response
|
| 1426 |
+
response_headers = dict(cached.response_headers)
|
| 1427 |
+
response_headers.pop("content-encoding", None)
|
| 1428 |
+
response_headers.pop("content-length", None)
|
| 1429 |
+
|
| 1430 |
+
return Response(content=cached.response_body, headers=response_headers)
|
| 1431 |
|
| 1432 |
# Token counting
|
| 1433 |
tokenizer = get_tokenizer(model)
|
|
|
|
| 1551 |
f"(saved {tokens_saved:,} tokens)"
|
| 1552 |
)
|
| 1553 |
|
| 1554 |
+
# Remove compression headers since httpx already decompressed the response
|
| 1555 |
+
response_headers = dict(response.headers)
|
| 1556 |
+
response_headers.pop("content-encoding", None)
|
| 1557 |
+
response_headers.pop("content-length", None) # Length changed after decompression
|
| 1558 |
+
|
| 1559 |
return Response(
|
| 1560 |
content=response.content,
|
| 1561 |
status_code=response.status_code,
|
| 1562 |
+
headers=response_headers,
|
| 1563 |
)
|
| 1564 |
except Exception as e:
|
| 1565 |
self.metrics.record_failed()
|
|
|
|
| 1582 |
content=body,
|
| 1583 |
)
|
| 1584 |
|
| 1585 |
+
# Remove compression headers since httpx already decompressed the response
|
| 1586 |
+
response_headers = dict(response.headers)
|
| 1587 |
+
response_headers.pop("content-encoding", None)
|
| 1588 |
+
response_headers.pop("content-length", None) # Length changed after decompression
|
| 1589 |
+
|
| 1590 |
return Response(
|
| 1591 |
content=response.content,
|
| 1592 |
status_code=response.status_code,
|
| 1593 |
+
headers=response_headers,
|
| 1594 |
)
|
| 1595 |
|
| 1596 |
|