ctx-zip Python
A Python library for compacting large tool-call results in conversation histories by persisting them to storage and replacing with lightweight references.
This is a Python implementation of the ctx-zip TypeScript library, providing the same functionality for Python-based AI applications.
Features
- Automatic Compaction: Replace large tool outputs with storage references
- Flexible Boundaries: Control which messages get compacted
- Storage Adapters: Pluggable storage backends (filesystem, S3, etc.)
- Reader Tools: Built-in tools to retrieve and search persisted content
- Type Safety: Full type hints and Protocol definitions
- Async Support: Both sync and async APIs available
Installation
pip install ctxzippy
For development:
pip install ctxzippy[dev]
For S3 support:
pip install ctxzippy[s3]
Quick Start
Basic Example
import asyncio
from ctxzippy import compact_messages, CompactOptions
messages = [
{"role": "user", "content": "Analyze this data"},
{
"role": "tool",
"content": [{
"type": "tool-result",
"toolName": "analyze",
"output": {
"type": "json",
"value": {"data": "..." * 10000} # Large payload
}
}]
},
{"role": "assistant", "content": "Analysis complete"}
]
# Compact the messages
options = CompactOptions(
storage="file:///tmp/ctx-storage", # Or use a StorageAdapter instance
boundary="since-last-assistant-or-user-text"
)
compacted = await compact_messages(messages, options)
# The tool result is now replaced with a reference:
# "Written to file: file:///tmp/ctx-storage/abc123.txt. Key: abc123.txt. Use the read/search tools to inspect its contents."
Real-World Example with OpenAI
import json
import asyncio
from openai import OpenAI
from ctxzippy import compact_messages, CompactOptions
client = OpenAI()
async def process_with_tools():
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Analyze our sales data"}
]
# Call OpenAI with tools
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=messages,
tools=[{
"type": "function",
"function": {
"name": "get_sales_data",
"description": "Get sales data",
"parameters": {"type": "object", "properties": {}}
}
}]
)
# Add assistant response with tool calls
messages.append({
"role": "assistant",
"content": response.choices[0].message.content,
"tool_calls": [...] # Include tool_calls from response
})
# Add tool results (simulate large response)
large_sales_data = {"revenue": [{"month": i, "amount": 50000 * i} for i in range(1, 1000)]}
messages.append({
"role": "tool",
"content": [{
"type": "tool-result",
"toolCallId": "call_123",
"output": {"type": "json", "value": large_sales_data}
}]
})
# Add assistant message to enable compaction
messages.append({
"role": "assistant",
"content": "I've analyzed the sales data."
})
# Compact before sending back to OpenAI
compacted = await compact_messages(
messages,
CompactOptions(storage="file:///tmp/llm-storage")
)
# Size reduction: ~500KB → ~1KB (99%+ reduction)
# Continue conversation with compacted messages
return compacted
# Run the example
asyncio.run(process_with_tools())
Synchronous Usage
For non-async contexts, use the synchronous wrapper:
from ctxzippy import compact_messages_sync
compacted = compact_messages_sync(messages, options)
Boundary Strategies
Control which messages get compacted:
Since Last Assistant or User Text (Default)
Compact only tool results since the last conversational message:
options = CompactOptions(boundary="since-last-assistant-or-user-text")
Entire Conversation
Compact all tool results in the entire history:
options = CompactOptions(boundary="entire-conversation")
Keep First N Messages
Preserve system prompts and initial context:
options = CompactOptions(
boundary={"type": "first-n-messages", "count": 3}
)
Storage Adapters
Filesystem Adapter
Store files locally:
from ctxzippy.adapters import FileStorageAdapter
adapter = FileStorageAdapter(
base_dir="/path/to/storage",
prefix="session-123" # Optional subdirectory
)
options = CompactOptions(storage=adapter)
S3 Adapter
Store files in AWS S3 or S3-compatible services:
from ctxzippy.adapters import S3StorageAdapter, S3StorageOptions
adapter = S3StorageAdapter(
S3StorageOptions(
bucket="my-bucket",
prefix="ctx-storage/session-123", # Optional subdirectory
region="us-west-2", # Optional, uses default if not specified
# Optional explicit credentials (uses boto3 defaults if not provided)
aws_access_key_id="...",
aws_secret_access_key="...",
)
)
options = CompactOptions(storage=adapter)
# Or use S3 URI directly
options = CompactOptions(storage="s3://my-bucket/ctx-storage")
Requirements:
- Install boto3:
pip install ctxzippy[s3] - Configure AWS credentials (via environment, ~/.aws/credentials, or IAM role)
- Bucket must exist and be accessible
S3-Compatible Services:
# For MinIO, Wasabi, or other S3-compatible services
adapter = S3StorageAdapter(
S3StorageOptions(
bucket="my-bucket",
endpoint_url="https://s3.wasabisys.com",
aws_access_key_id="...",
aws_secret_access_key="...",
)
)
Creating Custom Adapters
Implement the StorageAdapter protocol:
from ctxzippy.adapters import StorageAdapter, StorageWriteParams, StorageWriteResult
class MyCustomAdapter:
def write(self, params: StorageWriteParams) -> StorageWriteResult:
# Persist params.body with params.key
return StorageWriteResult(key=params.key, url="custom://...")
def read_text(self, params: StorageReadParams) -> str:
# Retrieve and return content
pass
def resolve_key(self, name: str) -> str:
# Apply any prefixing/namespacing
return f"prefix/{name}"
def __str__(self) -> str:
return "custom://my-storage"
Reader Tools
Retrieve and search persisted content:
Read File Tool
from ctxzippy.tools import read_file
# Read a previously stored file
result = read_file(
key="abc123.txt", # The key from "Key: abc123.txt"
options=ReadFileOptions(storage="file:///tmp/ctx-storage")
)
print(result["content"])
Grep and Search Tool
from ctxzippy.tools import grep_and_search_file
# Search for patterns in stored content
result = grep_and_search_file(
key="data.json",
pattern=r'"status":\s*"error"',
flags="i", # Case-insensitive
options=GrepAndSearchFileOptions(storage="file:///tmp/ctx-storage")
)
for match in result["matches"]:
print(f"{match['line_number']}: {match['content']}")
Advanced Configuration
Custom Serialization
Control how objects are converted to strings:
import json
def custom_serializer(value):
if isinstance(value, set):
value = list(value)
return json.dumps(value, indent=2, sort_keys=True)
options = CompactOptions(
serialize_result=custom_serializer
)
Storage Reader Tool Names
Specify which tools are readers (won't be re-persisted):
options = CompactOptions(
storage_reader_tool_names=[
"readFile",
"grepAndSearchFile",
"myCustomReaderTool"
]
)
API Reference
Core Functions
compact_messages(messages, options) -> List[Message]
Compact tool results in a message list by persisting to storage.
CompactOptions
Configuration for the compaction process:
strategy: Compaction strategy (default: "write-tool-results-to-storage")storage: Storage destination (URI string or adapter instance)boundary: Where to start compacting fromserialize_result: Custom serialization functionstorage_reader_tool_names: Tool names that read from storage
Storage Adapters
StorageAdapter Protocol
write(params): Persist contentread_text(params): Retrieve text contentopen_read_stream(params): Open a readable streamresolve_key(name): Apply namespacing to keys__str__(): Human-readable identifier
FileStorageAdapter
Filesystem-based storage implementation.
Reader Tools
read_file(key, options)
Read a previously stored file.
grep_and_search_file(key, pattern, flags, options)
Search for patterns in stored content.
Testing
Run the test suite:
# Install dev dependencies
pip install -e .[dev]
# Run tests
pytest
# With coverage
pytest --cov=ctxzippy
Architecture
The library follows a clean separation of concerns:
- Compaction Logic (
compact.py): Message scanning and replacement - Storage Adapters (
adapters/): Pluggable persistence backends - Strategies (
strategies/): Different compaction approaches - Tools (
tools/): Reader/search utilities - Storage Utilities (
storage/): Key tracking, resolution, grep
This design makes it easy to:
- Add new storage backends
- Implement custom compaction strategies
- Extend with new reader tools
- Integrate with different AI frameworks
Comparison with TypeScript Version
This Python implementation maintains full feature parity with the TypeScript original:
| Feature | TypeScript | Python |
|---|---|---|
| Message Compaction | ✅ | ✅ |
| Boundary Strategies | ✅ | ✅ |
| Filesystem Adapter | ✅ | ✅ |
| Reader Tools | ✅ | ✅ |
| Key Tracking | ✅ | ✅ |
| Custom Serialization | ✅ | ✅ |
| Type Safety | ✅ | ✅ (via type hints) |
| Async Support | ✅ | ✅ |
Contributing
Contributions are welcome! Please ensure:
- All tests pass
- Code is formatted with
black - Type hints are provided
- New features include tests
License
MIT License - see LICENSE file for details.
Acknowledgments
This is a Python port of the excellent ctx-zip TypeScript library. The architecture and design patterns follow the original implementation while adapting to Python idioms.
Metadata
Release files for ctxzippy 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ctxzippy-0.1.0.tar.gz | 35.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ctxzippy-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 59.7 kB
Release files / ctxzippy-0.1.0.tar.gz
| Download URL | ctxzippy-0.1.0.tar.gz |
|---|---|
| Size | 35.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2f43258ed75922ad2b5ac39c1f7e4c878dc518fae0c089530612eefa6ff43f80
|
|
BLAKE2b-256 checksum How to use checksums |
939cff4621a747c6448b29ebf5ab127a0c99781f59a8230daf205b71bd3cfcee
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.7
|
Release files / ctxzippy-0.1.0-py3-none-any.whl
| Download URL | ctxzippy-0.1.0-py3-none-any.whl |
|---|---|
| Size | 24.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4824acc22bc0cce35e68b4bd76181be754784c2efdd2fa059c538acfbee782b7
|
|
BLAKE2b-256 checksum How to use checksums |
61d254e3713847f95d8d91c96b7fe267fcb77e7f9db4d5adc8313375a96b3ba0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.7
|