-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
X-Ray is a decision-reasoning observability system for multi-step pipelines. It captures why decisions were made (candidates, filters, scores, reasoning), not just what functions ran.
View interactive diagram on dbdiagram.io
| Table | Purpose |
|---|---|
| Run | Pipeline execution (id, pipeline_name, status, timestamps, metadata) |
| Step | Single processing step within a run (input/output counts, reasoning, timing) |
| Payload | Externalized large data (lists ≥100 items, strings ≥1KB) |
-
removed_ratio=(input_count - output_count) / input_count— computed at query time -
duration_ms— stored in Step, computed at step end in SDK
Located in shared/types.py:
StepType
| Value | Description |
|---|---|
filter |
Removes candidates by criteria |
rank |
Orders candidates by score |
llm |
LLM/AI model call |
retrieval |
Fetches external data |
transform |
Data format changes |
other |
Catch-all |
RunStatus / StepStatus: running, success, error
DetailLevel: summary (counts + samples), full (complete data with truncation)
Entry point for the SDK. Manages Transport lifecycle and provides start_run() context manager.
from xray_logger import init_xray
client = init_xray(base_url="http://localhost:8000")
with client.start_run("pipeline") as run:
# Steps created hereRepresents a pipeline execution. Creates Steps, manages context via contextvars.
Single processing step. Captures input/output, timing, and reasoning via PayloadCollector and summarize_payload().
Async buffered HTTP sender with fail-open semantics—network errors are logged but never crash the application.
-
@step(name, step_type)— Instrument a function -
@instrument_class(step_type)— Instrument all public methods of a class -
attach_reasoning(data)— Add reasoning to current step -
attach_candidates(candidates)— Add candidate list to current step
-
XRayMiddleware— FastAPI/Starlette middleware for automatic HTTP request instrumentation
Large data is automatically summarized to keep event sizes manageable:
| Data Type | Threshold | Behavior |
|---|---|---|
| Lists | < 100 items | Stored inline |
| Lists | ≥ 100 items | Externalized with preview |
| Strings | < 1KB | Stored inline |
| Strings | ≥ 1KB | Externalized with preview |
| Candidate lists | Any size | Always inline (id + score + reason only) |
- SDK
summarize_payload()detects large data - Creates reference:
{"_ref": "p-000", "_type": "list", "_count": 500, "_preview": [...]} - Event includes
_payloads: {"p-000": [full data]} - API extracts payloads, stores in Payload table
- Step stores summary only; Payload stores full data
{
"event_type": "run_start",
"id": "uuid",
"pipeline_name": "my-pipeline",
"status": "running",
"started_at": "2024-01-01T00:00:00Z",
"input_summary": {...},
"metadata": {"user_id": "123"},
"_payloads": {...}
}{
"event_type": "step_end",
"id": "uuid",
"run_id": "parent-run-uuid",
"step_name": "filter_candidates",
"step_type": "filter",
"status": "success",
"duration_ms": 150,
"input_count": 100,
"output_count": 25,
"reasoning": {"threshold": 0.7, "removed": 75},
"_payloads": null
}sdk:
base_url: http://localhost:8000
api_key: your-api-key # Optional, or set XRAY_API_KEY env var
buffer_size: 1000 # Max events to buffer
flush_interval: 5.0 # Seconds between flushes
batch_size: 100 # Events per batch
http_timeout: 30.0 # HTTP request timeoutfrom xray_logger import init_xray, XRayConfig
client = init_xray(XRayConfig(
base_url="http://localhost:8000",
api_key="your-key",
buffer_size=500,
flush_interval=2.0
))XRAY_DATABASE_URL=postgresql+asyncpg://localhost:5432/xray
XRAY_API_KEY=your-secret-key # Optional, for authenticationConfig discovery searches upward from current directory for xray.config.yaml.
-
Counts as columns —
input_count/output_countare indexed for efficient filtering -
Computed ratios —
removed_ratiocomputed at query time (allows formula changes) - Payload externalization — Large data stored separately, summaries inline (thresholds: 100 items, 1KB strings)
- Candidate preservation — All candidate IDs captured, never truncated
- Fail-open SDK — Network errors never crash the application
- Context managers — Automatic cleanup on exceptions
- No separate Candidate table — Candidates embedded in Step.metadata to avoid join complexity
xray-logger/
├── shared/
│ ├── types.py # StepType, RunStatus, DetailLevel
│ └── config.py # Config discovery utilities
├── sdk/
│ ├── __init__.py # Public API exports
│ ├── client.py # XRayClient, init_xray()
│ ├── config.py # XRayConfig, load_config()
│ ├── types.py # SDK-specific types
│ ├── decorators.py # @step, @instrument_class, attach_*
│ ├── middleware.py # XRayMiddleware for FastAPI/Starlette
│ └── _internal/
│ ├── run.py # Run class implementation
│ ├── step.py # Step, PayloadCollector, summarize_payload()
│ └── transport.py # Async buffered HTTP transport
├── api/
│ ├── __init__.py
│ ├── main.py # FastAPI app
│ ├── routes.py # /health, /ingest, /xray/runs, /xray/steps
│ ├── models.py # SQLAlchemy ORM (Run, Step, Payload)
│ ├── schemas.py # Pydantic request/response schemas
│ ├── config.py # API configuration
│ ├── auth.py # API key authentication
│ └── _internal/
│ ├── database.py # Database session management
│ └── store.py # Database CRUD operations
└── examples/
├── competitor_pipeline/ # RAG pipeline with retrieval + LLM
├── recommendation-pipeline/ # Product recommendation filtering + ranking
├── fastapi-middleware/ # HTTP instrumentation example
└── product-categorization/ # Multi-step categorization pipeline
Optional API key authentication via:
- SDK:
api_keyin config orXRAY_API_KEYenv var - API:
XRAY_API_KEYenv var (when set, all endpoints except/healthrequire auth) - Header:
Authorization: Bearer <api-key>
- Docker Compose (Recommended) - PostgreSQL + X-Ray API
- Docker Run - Single container with SQLite
-
Development - Build from source with
docker-compose.dev.yml
See Quick Start for deployment instructions.
- Quick Start - Installation and first pipeline
- API Reference - Complete endpoint documentation
- GitHub Repository
- PyPI Package
- Docker Image