Skip to main content

No project description provided

Project description

Python Package - Handit AI

🐍 Python API and packaging for Handit tracing system

This directory contains the Python interface to the Rust-powered Handit tracing engine. It provides both high-level APIs for easy integration and low-level bindings for advanced usage, packaged as handit-ai on PyPI.

📦 Package Structure

python/
├── pyproject.toml           # 📋 Package configuration & build settings
├── dist/                    # 📦 Built wheels and source distributions
├── handit_ai/              # 🎯 High-level Python API
│   ├── __init__.py         # Main public API
│   └── fastapi.py          # FastAPI middleware integration
└── handit_core/            # ⚙️ Low-level Rust bindings
    ├── __init__.py         # Core tracing functionality
    ├── http_instrumentation.py    # HTTP client patching
    ├── openai_instrumentation.py  # OpenAI API tracing
    └── handit_core_native.so      # Compiled Rust extension

🎯 High-Level API (handit_ai)

Purpose

Provides a clean, developer-friendly interface for integrating Handit into Python applications.

Key Features

  • Zero-config operation with sensible defaults
  • Decorator-based tracing for functions
  • Context manager sessions for request boundaries
  • FastAPI middleware for automatic web app tracing
  • Automatic instrumentation enabling on import

Usage Examples

Basic Function Tracing

import handit

@handit.tracing(agent="payment-processor")
def process_payment(amount: float, token: str) -> dict:
    # Business logic here
    return {"status": "success", "charge_id": "ch_123"}

# Alternative using context manager
with handit.session(tag="checkout-flow"):
    result = process_payment(100.0, "tok_abc123")

Configuration

import handit

# Configure endpoints and API keys
handit.configure(
    HANDIT_ENDPOINT="https://your-endpoint.com/events",
    HANDIT_API_KEY="your-api-key",
    HANDIT_SAMPLE_RATE="0.1",  # Sample 10% of traces
    HANDIT_MAX_STR="500"       # Limit string capture length
)

FastAPI Integration

from fastapi import FastAPI
from handit_ai import HanditMiddleware

app = FastAPI()
app.add_middleware(HanditMiddleware, agent="api-server")

@app.get("/users/{user_id}")
async def get_user(user_id: str):
    # Automatically traced with request context
    return {"user_id": user_id, "name": "John Doe"}

⚙️ Low-Level Bindings (handit_core)

Purpose

Provides direct access to the Rust engine with full control over tracing behavior.

Key Components

Core Session Management

from handit_core import session, start_session, on_call, on_return

# Manual session control
session_id = start_session(tag="custom-session", attrs={"env": "prod"})

# Manual event recording
on_call(session_id, "my_function", "__main__", "/app/main.py", 42, time_ns)
on_return(session_id, "my_function", end_time_ns, duration_ns)

Configuration Access

import handit_core

# Low-level configuration
handit_core.configure(
    HANDIT_INCLUDE="myapp\..*",  # Only trace functions in 'myapp' module
    HANDIT_EXCLUDE="^(requests|urllib3)::",  # Exclude HTTP libraries
    HANDIT_CAPTURE_ONLY_CWD=True,  # Only trace current working directory
    HANDIT_REDACT="(?i)(password|token|secret)"  # PII redaction pattern
)

🌐 HTTP Instrumentation

Automatic Patching

The system automatically instruments popular HTTP clients:

Supported Libraries

  • requests - Synchronous HTTP client
  • httpx - Modern async/sync HTTP client
  • aiohttp - Async HTTP client/server framework

What's Captured

import requests  # Automatically patched on handit import

# This call is automatically traced:
response = requests.post(
    "https://api.stripe.com/v1/charges",
    headers={"Authorization": "Bearer sk_..."},  # Automatically redacted
    json={"amount": 10000, "currency": "usd"}
)

# Generates events:
# 1. http_request - method, URL, headers, body, timestamp
# 2. http_response - status, headers, body, duration, errors

Custom Instrumentation

from handit_core.http_instrumentation import patch_requests

# Manual patching control
patch_requests(capture_request_body=True, capture_response_body=False)

🤖 OpenAI Integration

Automatic API Tracing

import openai  # Automatically patched
import handit

client = openai.OpenAI(api_key="sk-...")

with handit.session(tag="ai-assistant"):
    # Automatically traces both function calls AND HTTP requests
    completion = client.chat.completions.create(
        model="gpt-4",
        messages=[{"role": "user", "content": "Hello!"}],
        temperature=0.7
    )

Captured Data

  • Function calls: client.chat.completions.create() with full parameters
  • HTTP requests: Raw OpenAI API calls with request/response bodies
  • Return values: Complete ChatCompletion objects
  • Timing: Precise duration of LLM calls
  • Error handling: Failed requests and API errors

🏗️ Build System

PyO3 + Maturin

The package uses modern Rust-Python integration:

# pyproject.toml
[build-system]
requires = ["maturin>=1.0,<2.0"]
build-backend = "maturin"

[tool.maturin]
bindings = "pyo3"
manifest-path = "../handit-runtime/crates/py/Cargo.toml"
module-name = "handit_core.handit_core_native"
python-packages = ["handit_ai", "handit_core"]

Building from Source

# Install build dependencies
pip install maturin

# Development build (with Python bindings)
maturin develop --features python

# Release build for distribution
maturin build --release --features python --sdist

# Create wheels for multiple platforms
maturin build --release --features python --target x86_64-unknown-linux-gnu

Local Development

# Install in development mode
cd python/
pip install -e .

# Run with local changes
python examples/basic_demo.py

📊 Event Processing

Event Flow

  1. Python code execution triggers native profiler callbacks
  2. Rust engine captures events with minimal overhead
  3. Background thread processes and buffers events
  4. Export system flushes to files or HTTP endpoints

Event Types Generated

Function Calls

{
  "type": "call",
  "session_id": "sess_abc123",
  "func": "process_payment",
  "module": "myapp.payments",
  "file": "/app/payments.py",
  "line": 45,
  "t0_ns": 1703123456789000000,
  "args_preview": {
    "amount": "100.0",
    "token": "<redacted>"
  }
}

HTTP Requests

{
  "type": "http_request", 
  "session_id": "sess_abc123",
  "method": "POST",
  "url": "https://api.stripe.com/v1/charges",
  "t0_ns": 1703123456789000000,
  "headers": {"authorization": "<redacted>"},
  "bytes_out": 1024,
  "request_body": "{\"amount\": 10000}"
}

Return Values

{
  "type": "return",
  "session_id": "sess_abc123", 
  "func": "process_payment",
  "t1_ns": 1703123456890000000,
  "dt_ns": 101000000,
  "locals_preview": {
    "return": "{\"id\": \"ch_123\", \"status\": \"succeeded\"}"
  }
}

🔧 Configuration Options

Environment Variables

# Core behavior
HANDIT_INCLUDE=".*"                    # Function inclusion pattern
HANDIT_EXCLUDE="^(requests|urllib3)::" # Function exclusion pattern
HANDIT_SAMPLE_RATE="1.0"               # Sampling rate (0.0-1.0)

# Data capture limits
HANDIT_MAX_STR="1000"                  # Max string length
HANDIT_MAX_LOCALS="50"                 # Max local variables to capture

# Export configuration  
HANDIT_OUTPUT_FILE="./handit_events.jsonl"  # Local file output
HANDIT_ENDPOINT="https://api.handit.ai/events"  # HTTP endpoint
HANDIT_API_KEY="your-api-key"          # API authentication

# Security
HANDIT_REDACT="(?i)(api_key|token|password|secret)"  # PII redaction
HANDIT_CAPTURE_ONLY_CWD="false"       # Restrict to current directory

Programmatic Configuration

import handit

handit.configure(
    # Export settings
    HANDIT_ENDPOINT="https://your-endpoint.com/events",
    HANDIT_API_KEY="your-key",
    
    # Performance tuning
    HANDIT_SAMPLE_RATE=0.1,  # 10% sampling
    HANDIT_MAX_STR=500,      # Shorter string previews
    
    # Security
    HANDIT_REDACT=r"(?i)(password|token|key|secret|auth)",
    
    # Filtering
    HANDIT_INCLUDE="myapp\..*",  # Only trace your app
    HANDIT_EXCLUDE="^(requests|urllib3|json)::"  # Skip common libraries
)

🚀 Performance Considerations

Overhead Profile

  • Function calls: ~0.5-1μs per call
  • HTTP requests: ~5-10μs additional overhead
  • Memory usage: ~1-2MB baseline + configurable buffers
  • CPU impact: <1% for typical applications

Optimization Tips

# Reduce data capture for high-volume functions
handit.configure(
    HANDIT_MAX_STR=100,      # Shorter previews
    HANDIT_MAX_LOCALS=10,    # Fewer variables
    HANDIT_SAMPLE_RATE=0.01, # 1% sampling for hot paths
)

# Exclude noisy libraries
handit.configure(
    HANDIT_EXCLUDE=r"^(requests|urllib3|json|logging|threading)::"
)

# Restrict to application code only
handit.configure(
    HANDIT_INCLUDE="myapp\..*",
    HANDIT_CAPTURE_ONLY_CWD=True
)

🧪 Testing

Unit Tests

# Test Python components
cd python/
python -m pytest tests/

# Test specific modules
python -m pytest tests/test_instrumentation.py -v

Integration Tests

# Test with real HTTP calls
python examples/nested_http_demo.py

# Test OpenAI integration (requires API key)
OPENAI_API_KEY=sk-... python examples/openai_test.py

# Test FastAPI middleware
python examples/fastapi_demo.py

Performance Testing

# Benchmark overhead
python benchmarks/function_call_overhead.py

# Memory usage profiling
python -m memory_profiler examples/memory_benchmark.py

📚 Examples Usage

Web Framework Integration

# FastAPI
from fastapi import FastAPI
from handit_ai import HanditMiddleware

app = FastAPI()
app.add_middleware(HanditMiddleware, agent="api")

# Flask (manual)
from flask import Flask
import handit

app = Flask(__name__)

@app.route('/api/users')
@handit.tracing(agent="user-api")
def get_users():
    return {"users": []}

Background Task Tracing

import handit
from celery import Celery

app = Celery('tasks')

@app.task
@handit.tracing(agent="background-worker")
def process_upload(file_id: str):
    # Task processing automatically traced
    return {"status": "processed", "file_id": file_id}

Database Query Tracing

import handit
import psycopg2

@handit.tracing(agent="database")
def get_user_orders(user_id: str):
    with psycopg2.connect(DATABASE_URL) as conn:
        # SQL queries can be traced by decorating helper functions
        return fetch_orders(conn, user_id)

@handit.tracing(agent="sql-query") 
def fetch_orders(conn, user_id):
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM orders WHERE user_id = %s", (user_id,))
    return cursor.fetchall()

🔮 Future Enhancements

Planned Features

  • Database instrumentation for SQLAlchemy, Django ORM
  • Message queue tracing for Celery, RQ, Kafka
  • Template engine instrumentation for Jinja2, Django templates
  • Custom metrics collection beyond function calls

API Improvements

  • Async context managers for better async/await support
  • Type hints for better IDE integration
  • Plugin system for custom instrumentation
  • Real-time streaming for live monitoring dashboards

The Python package provides the friendly developer interface while leveraging Rust's performance for the heavy lifting.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

handit_ai-0.0.30.tar.gz (50.0 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

handit_ai-0.0.30-cp313-cp313-macosx_11_0_arm64.whl (2.6 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

handit_ai-0.0.30-cp312-cp312-macosx_11_0_arm64.whl (2.6 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

handit_ai-0.0.30-cp311-cp311-macosx_11_0_arm64.whl (2.6 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

handit_ai-0.0.30-cp310-cp310-macosx_11_0_arm64.whl (2.6 MB view details)

Uploaded CPython 3.10macOS 11.0+ ARM64

File details

Details for the file handit_ai-0.0.30.tar.gz.

File metadata

  • Download URL: handit_ai-0.0.30.tar.gz
  • Upload date:
  • Size: 50.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.1

File hashes

Hashes for handit_ai-0.0.30.tar.gz
Algorithm Hash digest
SHA256 a2644349cb6abbd8edacdcd372c78a74973cf703d7fd5a2d3a3b6d28d5cf8462
MD5 2db4f3fba22d9c97d18849f187e46492
BLAKE2b-256 5436913a072ee7480f08c3ed1b0f7e7e95e4fa8753c47835e2330fa810684354

See more details on using hashes here.

File details

Details for the file handit_ai-0.0.30-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for handit_ai-0.0.30-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 651a5ce2ea33bed0e68fe6cd1f0b2d1a6339a8ed4fc50ffc6de4ca19af511abb
MD5 9fe761018d8bd33c0050dea2807a3bc3
BLAKE2b-256 b143489f39c892ded0638270d0092f0268829fbbede68cb687e4a90b4f0a77f4

See more details on using hashes here.

File details

Details for the file handit_ai-0.0.30-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for handit_ai-0.0.30-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 5a29ef00e7afe0e5cf8d0269bda499acf9bc45effc21b6ed2749366094df224d
MD5 5704cad9c1ee8d9c09a0e42f9adb0313
BLAKE2b-256 10ecc40349cfb1117f060d90ab0a23fa52cc55977dbdd0d5ff4fb6897a623f44

See more details on using hashes here.

File details

Details for the file handit_ai-0.0.30-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for handit_ai-0.0.30-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 42f4e51b2e1dbde411a7a1b98344c4632d03150e97f2bd69168a7b27d3fc2e72
MD5 32e73f63e288f8bb01ffe404074868a6
BLAKE2b-256 b075c2a5aa82caac7b2f261adf66e44c0ceec1ef4f3950a5b90302d0e5143917

See more details on using hashes here.

File details

Details for the file handit_ai-0.0.30-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for handit_ai-0.0.30-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 6adccdb5cd4e01146400f645ea6e71cc9d1871ebadffffa57d464c4ddb0661c6
MD5 b76f530b15b9fad768d9a84067ef2fc2
BLAKE2b-256 9317ed432fedba32e2777e16c8619704c923f742282f8b61de151dfa8e5786c8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page