A high-performance Python SDK for logging and monitoring OpenAI API calls to Crucible
Project description
Crucible SDK
A high-performance Python SDK for logging and monitoring OpenAI API calls to Crucible warehouse.
Features
- Seamless Integration: Drop-in replacement for OpenAI's Python client
- Background Logging: Non-blocking, batched logging with configurable intervals
- Streaming Support: Efficient handling of streaming responses with memory optimization
- Error Resilience: Logging failures never break your application
- Performance Optimized: <1ms overhead per request, <50MB memory usage
- Async Support: Full async/await support for high-concurrency applications
- Rich Tagging: Flexible metadata system for organizing and filtering logs
- Circuit Breaker: Automatic failure detection and recovery
- Compression: Optional request/response compression for network efficiency
- LangChain Integration: Seamless integration with LangChain workflows
- Google GenAI Integration: Seamless integration with Google's Generative AI (Gemini)
Installation
pip install crucible-ai-sdk
Quick Start
Basic Usage
from crucible import CrucibleOpenAI
# Initialize client (uses warehouse.usecrucible.ai by default)
client = CrucibleOpenAI(api_key="your-crucible-api-key")
# Make API calls (automatically logged)
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "Hello, world!"}],
crucible_metadata={
"thread_id": "thread_123",
"task_id": "task_456",
"user_id": "user_789"
}
)
print(response.choices[0].message.content)
# Clean up
client.close()
Async Usage
import asyncio
from crucible import CrucibleAsyncOpenAI
async def main():
client = CrucibleAsyncOpenAI(api_key="your-crucible-api-key")
response = await client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "Hello, async world!"}],
crucible_metadata={
"thread_id": "async_thread_123",
"task_id": "async_task_456"
}
)
print(response.choices[0].message.content)
await client.close()
asyncio.run(main())
Streaming Support
from crucible import CrucibleOpenAI
client = CrucibleOpenAI(api_key="your-crucible-api-key")
# Streaming responses are automatically logged
stream = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "Count from 1 to 5"}],
stream=True,
crucible_metadata={
"session_id": "session_abc",
"experiment": "streaming_test"
}
)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
Custom Domain
from crucible import CrucibleOpenAI
# Use custom domain
client = CrucibleOpenAI(
api_key="your-crucible-api-key",
domain="custom.warehouse.com"
)
# Make API call with metadata
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "Hello from custom domain!"}],
crucible_metadata={
"domain": "custom",
"environment": "production"
}
)
Context Manager
from crucible import CrucibleOpenAI
# Automatic cleanup with context manager
with CrucibleOpenAI(api_key="your-crucible-api-key") as client:
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "Hello!"}],
crucible_metadata={
"context_test": True,
"session_id": "context_session"
}
)
print(response.choices[0].message.content)
# Logs are automatically flushed when exiting context
Configuration
Environment Variables
export CRUCIBLE_API_KEY="your-api-key"
export CRUCIBLE_DOMAIN="warehouse.usecrucible.ai" # Optional, defaults to warehouse.usecrucible.ai
export OPENAI_API_KEY="your-openai-api-key"
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
api_key |
str | None | Crucible API key |
domain |
str | "warehouse.usecrucible.ai" | Crucible warehouse domain |
batch_size |
int | 10 | Number of requests to batch together |
flush_interval |
float | 5.0 | Seconds between batch flushes |
max_retries |
int | 3 | Number of retry attempts |
timeout |
float | 30.0 | Request timeout in seconds |
enable_logging |
bool | True | Enable/disable logging |
enable_compression |
bool | True | Enable request compression |
max_memory_mb |
int | 50 | Maximum memory usage in MB |
max_queue_size |
int | 1000 | Maximum queue size for background logging |
Metadata System
Use metadata to organize and filter your logged requests:
client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "What is the capital of France?"}],
crucible_metadata={
"thread_id": "thread_123",
"task_id": "task_456",
"user_id": "user_789",
"session_id": "session_abc",
"question_type": "geography",
"difficulty": "easy",
"experiment": "knowledge_test"
}
)
Error Handling
Crucible SDK is designed to be resilient. Logging failures never break your application:
from crucible import CrucibleOpenAI
client = CrucibleOpenAI(api_key="your-crucible-api-key")
try:
# This will fail, but error will be logged
response = client.chat.completions.create(
model="gpt-3.5-turbo-invalid",
messages=[{"role": "user", "content": "This will fail"}],
crucible_metadata={
"error_test": True,
"task_id": "error_test_123"
}
)
except Exception as e:
print(f"API call failed: {e}")
# Error was automatically logged to Crucible warehouse
Performance Monitoring
Monitor the performance of your logging system:
from crucible import CrucibleOpenAI
client = CrucibleOpenAI(api_key="your-crucible-api-key")
# Make some API calls...
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "Hello!"}],
crucible_metadata={"test": "performance"}
)
# Clean up
client.close()
LangChain Integration
Crucible provides seamless integration with LangChain for automatic logging of LLM interactions:
from crucible.langchain_llm import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema.runnable import RunnableSequence
from langchain.schema.output_parser import StrOutputParser
# Initialize Crucible ChatOpenAI
llm = ChatOpenAI(
model="gpt-3.5-turbo",
temperature=0.7,
crucible_kwargs={
"api_key": "your-crucible-api-key",
"domain": "warehouse.usecrucible.ai"
}
)
# Create a prompt template
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant."),
("human", "Explain {topic} in simple terms.")
])
# Create a chain
chain = prompt | llm | StrOutputParser()
# Run with metadata
result = chain.invoke(
{"topic": "machine learning"},
crucible_metadata={
"langchain_example": True,
"topic": "machine_learning",
"user_id": "user_123"
}
)
print(result)
# Clean up
llm.close()
LangChain Features
- Metadata Support: Pass
crucible_metadatato any LangChain operation - Stored Metadata: Use
with_metadata()to store metadata for all operations - Streaming Support: Full support for LangChain streaming
- Async Support: Compatible with LangChain's async operations
- Context Management: Automatic cleanup with
close()method
# Store metadata for all operations
llm = ChatOpenAI(...).with_metadata(
session_id="session_123",
experiment="langchain_test"
)
# Streaming with metadata
for chunk in chain.stream(
{"topic": "AI"},
crucible_metadata={"streaming": True}
):
print(chunk.content, end="")
Google GenAI Integration
Crucible provides seamless integration with Google's Generative AI (Gemini) for automatic logging of LLM interactions:
Installation
pip install crucible-ai-sdk[genai]
Basic Usage
from crucible import CrucibleGenAI
# Initialize client
client = CrucibleGenAI(
api_key="your-crucible-api-key",
genai_api_key="your-google-api-key" # or set GOOGLE_API_KEY env var
)
# Generate content with automatic logging
response = client.models.generate_content(
model="gemini-2.5-flash",
contents="Explain quantum computing in simple terms.",
crucible_metadata={
"thread_id": "thread_123",
"task_id": "task_456",
"user_id": "user_789"
}
)
print(response.text)
# Clean up
client.close()
Async Usage
import asyncio
from crucible import CrucibleAsyncGenAI
async def main():
client = CrucibleAsyncGenAI(
api_key="your-crucible-api-key",
genai_api_key="your-google-api-key"
)
response = await client.models.generate_content(
model="gemini-2.5-flash",
contents="Hello, async world!",
crucible_metadata={
"thread_id": "async_thread_123",
"task_id": "async_task_456"
}
)
print(response.text)
await client.close()
asyncio.run(main())
Streaming Support
from crucible import CrucibleGenAI
client = CrucibleGenAI(
api_key="your-crucible-api-key",
genai_api_key="your-google-api-key"
)
# Streaming responses are automatically logged
stream = client.models.generate_content(
model="gemini-2.5-flash",
contents="Count from 1 to 5",
stream=True,
crucible_metadata={
"session_id": "session_abc",
"experiment": "streaming_test"
}
)
for chunk in stream:
if chunk.text:
print(chunk.text, end="", flush=True)
Custom Generation Config
from crucible import CrucibleGenAI
client = CrucibleGenAI(
api_key="your-crucible-api-key",
genai_api_key="your-google-api-key"
)
response = client.models.generate_content(
model="gemini-2.5-flash",
contents="Write a short story.",
generation_config={
"temperature": 0.7,
"top_p": 0.9,
"top_k": 40,
"max_output_tokens": 500,
},
crucible_metadata={
"task_type": "creative_writing",
"temperature": 0.7,
"max_tokens": 500
}
)
print(response.text)
client.close()
Context Manager
from crucible import CrucibleGenAI
# Automatic cleanup with context manager
with CrucibleGenAI(
api_key="your-crucible-api-key",
genai_api_key="your-google-api-key"
) as client:
response = client.models.generate_content(
model="gemini-2.5-flash",
contents="Hello!",
crucible_metadata={
"context_test": True,
"session_id": "context_session"
}
)
print(response.text)
# Logs are automatically flushed when exiting context
Alternative Syntax: GenerativeModel
You can also use the GenerativeModel syntax if you prefer:
from crucible import CrucibleGenAI
client = CrucibleGenAI(
api_key="your-crucible-api-key",
genai_api_key="your-google-api-key"
)
# Create a model instance
model = client.GenerativeModel("gemini-2.5-flash")
# Generate content with automatic logging
response = model.generate_content(
"Explain quantum computing in simple terms.",
crucible_metadata={
"thread_id": "thread_123",
"task_id": "task_456"
}
)
print(response.text)
client.close()
Both syntaxes (client.models.generate_content() and client.GenerativeModel().generate_content()) are fully supported and provide identical functionality.
GenAI Features
- Metadata Support: Pass
crucible_metadatato any GenAI operation - Streaming Support: Full support for GenAI streaming
- Async Support: Compatible with GenAI's async operations
- Context Management: Automatic cleanup with
close()method - Error Handling: Automatic error logging without breaking your application
- Generation Config: Support for all GenAI generation configuration options
Environment Variables
export CRUCIBLE_API_KEY="your-crucible-api-key"
export GOOGLE_API_KEY="your-google-api-key" # or GOOGLE_GENAI_API_KEY
export CRUCIBLE_DOMAIN="warehouse.usecrucible.ai" # Optional
Advanced Usage
Manual Logging
from crucible import CrucibleLogger, LogRequest, CrucibleConfig
import time
config = CrucibleConfig(api_key="your-api-key", domain="warehouse.usecrucible.ai")
logger = CrucibleLogger(config)
# Create log request manually
log_request = LogRequest(
requested_at=int(time.time() * 1000),
received_at=int(time.time() * 1000),
req_payload={"model": "gpt-3.5-turbo", "messages": [...]},
resp_payload={"choices": [...]},
status_code=200,
metadata={"manual": "true", "task_id": "manual_123"}
)
# Log manually
logger.log_request(log_request)
# Clean up
logger.close()
Streaming Statistics
from crucible import StreamingMerger
merger = StreamingMerger(max_memory_mb=100)
# Process chunks...
for chunk in stream:
assembled = merger.merge_chunk(assembled, chunk)
# Get streaming statistics
stats = merger.get_stats()
print(f"Memory usage: {stats['current_size']} bytes")
print(f"Chunks processed: {stats['total_chunks_processed']}")
Development
Running Tests
pip install pytest
pytest tests/
Running Examples
# Set environment variables
export OPENAI_API_KEY="your-openai-key"
export CRUCIBLE_API_KEY="your-crucible-key"
# Run examples
python examples/basic_usage.py
python examples/async_usage.py
Deployment to PyPI
The SDK includes a simple deployment script for publishing to PyPI.
Prerequisites
- PyPI Account: Ensure you have a PyPI account with upload permissions
- API Token: Create a PyPI API token at https://pypi.org/manage/account/token/
- Version Update: Update the version in
pyproject.tomlandcrucible/__init__.pybefore deploying
Deployment Steps
-
Set PyPI Credentials:
export TWINE_USERNAME=__token__ export TWINE_PASSWORD=pypi-your-api-token-here
-
Run Deployment Script:
cd crucible-python-sdk ./deploy.sh
The script will:
- Clean previous builds
- Build the package (source distribution and wheel)
- Validate the package
- Upload to PyPI
-
Verify Deployment:
pip install crucible-ai-sdk==<version>
Version Bumping
Before deploying, update the version in two places:
-
pyproject.toml:version = "0.0.14"
-
crucible/__init__.py:__version__ = "0.0.14"
Follow semantic versioning:
- Patch (0.0.13 → 0.0.14): Bug fixes and new features (bind_tools support)
- Minor (0.0.9 → 0.1.0): New features, backward compatible
- Major (0.0.9 → 1.0.0): Breaking changes
Manual Deployment
If you prefer to deploy manually:
# Clean previous builds
rm -rf dist/ build/ *.egg-info/
# Build package
python3 -m build
# Check package
python3 -m twine check dist/*
# Upload to PyPI
python3 -m twine upload dist/*
Troubleshooting
- Authentication Errors: Ensure
TWINE_USERNAMEandTWINE_PASSWORDare set correctly - Version Already Exists: PyPI doesn't allow overwriting versions. Bump the version number
- Build Errors: Ensure all dependencies are installed:
pip install build twine
API Reference
CrucibleOpenAI
Main client class for synchronous operations.
Methods
chat.completions.create(**kwargs): Create chat completion with loggingclose(): Close client and flush logsflush_logs(): Force flush pending logsget_logging_stats(): Get logging statisticsis_healthy(): Check if logger is healthy
CrucibleAsyncOpenAI
Async client class for asynchronous operations.
Methods
chat.completions.create(**kwargs): Create async chat completion with loggingclose(): Close client and flush logsflush_logs(): Force flush pending logsget_logging_stats(): Get logging statisticsis_healthy(): Check if logger is healthy
CrucibleConfig
Configuration class for Crucible SDK.
Properties
api_key: Crucible API keydomain: Crucible warehouse domain (defaults to warehouse.usecrucible.ai)batch_size: Batch size for loggingflush_interval: Flush interval in secondsenable_logging: Enable/disable loggingenable_compression: Enable/disable compression
License
MIT License - see LICENSE file for details.
Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
Support
- Documentation: https://docs.crucible.ai
- Issues: https://github.com/crucible/crucible-python-sdk/issues
- Email: support@crucible.ai
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file crucible_ai_sdk-0.0.18.tar.gz.
File metadata
- Download URL: crucible_ai_sdk-0.0.18.tar.gz
- Upload date:
- Size: 34.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ef1017da264c5b3b0ace5cb292681ee23b5ba04ff749d9e0f8c77fbc7f03815b
|
|
| MD5 |
fd85871d7728e58d409a2a17dcecacc1
|
|
| BLAKE2b-256 |
fd36630ae4fe3a96712af81bba425102f9b23d3771b7dc8b83e6f989079db849
|
File details
Details for the file crucible_ai_sdk-0.0.18-py3-none-any.whl.
File metadata
- Download URL: crucible_ai_sdk-0.0.18-py3-none-any.whl
- Upload date:
- Size: 38.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2e146b60c9c3c1b0e0c5a59be6b35b70b5517d052263afac89ec48f0c1dfb054
|
|
| MD5 |
496c5e1ef6770a196e1ca7438224a017
|
|
| BLAKE2b-256 |
6c8478316e889a6cb88d357ca15daab594a45e3408d95dd81f3aff69c45dd2fa
|