{"id":44621378,"url":"https://github.com/traccia-ai/traccia-py","last_synced_at":"2026-04-12T16:23:05.034Z","repository":{"id":334739018,"uuid":"1139177212","full_name":"traccia-ai/traccia-py","owner":"traccia-ai","description":"OpenTelemetry-based tracing SDK for AI agents and LLM applications","archived":false,"fork":false,"pushed_at":"2026-02-14T12:51:13.000Z","size":284,"stargazers_count":28,"open_issues_count":0,"forks_count":12,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-14T21:07:10.160Z","etag":null,"topics":["agent","agent-governance","agent-monitoring","agent-optimization","agent-policies","agentic-ai","observability"],"latest_commit_sha":null,"homepage":"https://traccia.ai/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/traccia-ai.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-21T16:12:57.000Z","updated_at":"2026-02-14T12:50:17.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/traccia-ai/traccia-py","commit_stats":null,"previous_names":["traccia-ai/traccia","traccia-ai/traccia-py"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/traccia-ai/traccia-py","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/traccia-ai%2Ftraccia-py","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/traccia-ai%2Ftraccia-py/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/traccia-ai%2Ftraccia-py/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/traccia-ai%2Ftraccia-py/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/traccia-ai","download_url":"https://codeload.github.com/traccia-ai/traccia-py/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/traccia-ai%2Ftraccia-py/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29718466,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-22T15:10:41.462Z","status":"ssl_error","status_checked_at":"2026-02-22T15:10:04.636Z","response_time":110,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent","agent-governance","agent-monitoring","agent-optimization","agent-policies","agentic-ai","observability"],"created_at":"2026-02-14T14:11:56.569Z","updated_at":"2026-04-01T18:39:02.606Z","avatar_url":"https://github.com/traccia-ai.png","language":"Python","funding_links":[],"categories":["Observability"],"sub_categories":["Streaming Operations"],"readme":"# Traccia\n\n**Production-ready distributed tracing for AI agents and LLM applications**\n\nTraccia is a lightweight, high-performance Python SDK for observability and tracing of AI agents, LLM applications, and complex distributed systems. Built on OpenTelemetry standards with specialized instrumentation for AI workloads.\n\n[Traccia](https://pypi.org/project/traccia/) is available on PyPI.\n\n## ✨ Features\n\n- **🔍 Automatic Instrumentation**: Auto-patch OpenAI, Anthropic, requests, and HTTP libraries\n- **🤖 Framework Integrations**: Support for LangChain, CrewAI, and OpenAI Agents SDK\n- **📊 LLM-Aware Tracing**: Track tokens, costs, prompts, and completions automatically\n- **📈 OpenTelemetry Metrics**: Emit OTEL-compliant metrics for accurate cost/token tracking (independent of sampling)\n- **⚡ Zero-Config Start**: Simple `init()` call with automatic config discovery\n- **🎯 Decorator-Based**: Trace any function with `@observe` decorator\n- **🔧 Multiple Exporters**: OTLP (compatible with Grafana Tempo, Jaeger, Zipkin), Console, or File\n- **🛡️ Production-Ready**: Rate limiting, error handling, config validation, robust \nflushing\n- **🛡️ Guardrail Detection**: Passive, zero-overhead detection of guardrails in traces — explicit, provider-native, and heuristic\n- **📝 Type-Safe**: Full Pydantic validation for configuration\n- **🚀 High Performance**: Efficient batching, async support, minimal overhead\n- **🔐 Secure**: No secrets in logs, configurable data truncation\n\n---\n\n## 🚀 Quick Start\n\n### Installation\n\n```bash\npip install traccia\n```\n\n### Basic Usage\n\n```python\nfrom traccia import init, observe\n\n# Initialize (auto-loads from traccia.toml if present)\ninit()\n\n# Trace any function\n@observe()\ndef my_function(x, y):\n    return x + y\n\n# That's it! Traces are automatically created and exported\nresult = my_function(2, 3)\n```\n\n### With LLM Calls\n\n```python\nfrom traccia import init, observe\nfrom openai import OpenAI\n\ninit()  # Auto-patches OpenAI\n\nclient = OpenAI()\n\n@observe(as_type=\"llm\")\ndef generate_text(prompt: str) -\u003e str:\n    response = client.chat.completions.create(\n        model=\"gpt-4\",\n        messages=[{\"role\": \"user\", \"content\": prompt}]\n    )\n    return response.choices[0].message.content\n\n# Automatically tracks: model, tokens, cost, prompt, completion, latency\ntext = generate_text(\"Write a haiku about Python\")\n```\n\n### LangChain\n\nCreate a callback handler and pass it to `config={\"callbacks\": [traccia_handler]}`. Install the optional extra: `pip install traccia[langchain]`.\n\n```python\nfrom traccia import init\nfrom traccia.integrations.langchain import CallbackHandler  # or TracciaCallbackHandler\nfrom langchain_openai import ChatOpenAI\n\ninit()\n\n# Create Traccia handler (no args)\ntraccia_handler = CallbackHandler()\n\n# Use with any LangChain runnable\nllm = ChatOpenAI(model=\"gpt-4o-mini\")\nresult = llm.invoke(\n    \"Tell me a joke\",\n    config={\"callbacks\": [traccia_handler]}\n)\n```\n\nSpans for LLM/chat model runs are created automatically with the same attributes as direct OpenAI instrumentation (model, prompt, usage, cost).\n\n**Note:** `pip install traccia[langchain]` installs traccia plus `langchain-core`; you need this extra to use the callback handler. If you already have `langchain-core` (e.g. from `langchain` or `langchain-openai`), base `pip install traccia` may be enough at runtime, but `traccia[langchain]` is the supported way to get a compatible dependency.\n\n### OpenAI Agents SDK\n\nTraccia **automatically** detects and instruments the OpenAI Agents SDK when installed. No extra code needed:\n\n```python\nfrom traccia import init\nfrom agents import Agent, Runner\n\ninit()  # Automatically enables Agents SDK tracing\n\nagent = Agent(\n    name=\"Assistant\",\n    instructions=\"You are a helpful assistant\"\n)\nresult = Runner.run_sync(agent, \"Write a haiku about recursion\")\n```\n\n**Configuration**: Auto-enabled by default when `openai-agents` is installed. To disable:\n\n```python\ninit(openai_agents=False)  # Explicit parameter\n# OR set environment variable: TRACCIA_OPENAI_AGENTS=false\n# OR in traccia.toml under [instrumentation]: openai_agents = false\n```\n\n**Compatibility**: If you have `openai-agents` installed but don't use it (e.g., using LangChain or pure OpenAI instead), the integration is registered but never invoked—no overhead or extra spans.\n\n### CrewAI\n\nTraccia **automatically** instruments [CrewAI](https://docs.crewai.com/) when it is installed in your environment.\n\n```python\nfrom traccia import init\nfrom crewai import Agent, Task, Crew, Process\n\ninit()  # Auto-enables CrewAI tracing when CrewAI is installed\n\nresearcher = Agent(role=\"Research Analyst\", goal=\"Research a topic\", llm=\"gpt-4o-mini\")\ntask = Task(description=\"Research Shawn Michaels\", agent=researcher)\n\ncrew = Crew(agents=[researcher], tasks=[task], process=Process.sequential, verbose=True)\nresult = crew.kickoff()\n```\n\nTraccia will create spans for the crew (`crewai.crew.kickoff`), each task (`crewai.task.*`), agents (`crewai.agent.*`), and underlying LLM calls, which nest under the existing OpenAI spans.\n\n**Configuration**: Auto-enabled by default when `crewai` is installed. To disable:\n\n```python\ninit(crewai=False)  # Explicit parameter\n# OR set environment variable: TRACCIA_CREWAI=false\n# OR in traccia.toml under [instrumentation]: crewai = false\n```\n\n---\n\n## 🛡️ Guardrail Detection\n\nTraccia includes a passive guardrail detection engine that runs as a span processor — no runtime enforcement, no changes required to existing agent code. It inspects every span as it ends, classifies guardrail signals into structured findings, and writes results back onto spans so they appear in any configured exporter.\n\n### How it works\n\nDetection is automatic once `traccia.init()` is called. The processor runs three tiers against every span's attributes:\n\n| Tier | Source | Confidence | How |\n|------|--------|------------|-----|\n| A — Explicit | `explicit` | `high` | `@observe(as_type=\"guardrail\")` or `guardrail_span()` |\n| B — Provider-native | `provider_native` | `high`/`medium` | LLM finish reason, stop reason, safety ratings |\n| C — Heuristic | `heuristic` | always `low` | Denial keywords in tool error messages |\n\nAt the end of each trace (when the root span ends), the processor evaluates which guardrail categories should exist given the agent's observed capabilities (LLM calls, tool use, user-provided text) and reports which are missing.\n\n### Annotating guardrails explicitly (Tier A)\n\n**Option 1: `guardrail_span` context manager** — recommended for inline checks\n\n```python\nfrom traccia.guardrails import guardrail_span\n\nwith guardrail_span(\"pii_check\", category=\"pii\", enforcement_mode=\"warn\") as span:\n    result = run_pii_check(user_input)\n    span.set_attribute(\"guardrail.triggered\", result.found_pii)\n```\n\n**Option 2: `@observe(as_type=\"guardrail\")` decorator** — for function-level guardrails\n\nIf your guardrail function returns a `bool`, `guardrail.triggered` is set automatically:\n\n```python\nfrom traccia import observe\n\n@observe(\n    as_type=\"guardrail\",\n    attributes={\n        \"guardrail.name\": \"prompt_injection_check\",\n        \"guardrail.category\": \"prompt_injection\",\n        \"guardrail.enforcement_mode\": \"block\",\n    }\n)\ndef check_injection(text: str) -\u003e bool:\n    return any(kw in text.lower() for kw in INJECTION_KEYWORDS)\n    # True → triggered (blocked), False → not triggered\n```\n\nFor non-bool returns, set `guardrail.triggered` manually on the current span.\n\n### Suppressing false positive missing-guardrail warnings\n\nBatch pipelines or internal-only agents often get flagged for `prompt_injection` / `input_validation` because they make LLM calls with `llm.prompt`. Suppress specific categories for a run:\n\n```python\nfrom traccia.guardrails import guardrail_span\n\n# Convenience: suppress_missing on guardrail_span\nwith guardrail_span(\"root\", category=\"unknown\", suppress_missing=[\"prompt_injection\", \"input_validation\"]):\n    run_batch_pipeline()\n\n# Or directly on any span\nfrom traccia.guardrails import ATTR_GUARDRAIL_SUPPRESS_MISSING\nspan.set_attribute(ATTR_GUARDRAIL_SUPPRESS_MISSING, [\"prompt_injection\"])\n```\n\n### What appears in traces\n\n**Per span** (when a guardrail signal is found):\n- `guardrail.finding.count` — number of findings on this span\n- `guardrail.findings` — JSON array of `GuardrailFinding` objects\n\n**On the root span** (aggregated summary of the entire run):\n- `guardrail.summary` — full `GuardrailSummary` JSON\n- `guardrail.summary.coverage_confidence` — `\"high\"`, `\"medium\"`, or `\"low\"`\n- `guardrail.summary.missing_count` — number of expected-but-missing guardrail categories\n- `guardrail.summary.detected_categories` — list of detected category strings\n\n### Provider-native detection (Tier B, automatic)\n\nThese signals are detected automatically from LLM span attributes — no annotation required:\n\n| Provider | Signal | Attribute |\n|----------|--------|-----------|\n| OpenAI | `finish_reason = \"content_filter\"` | `llm.finish_reason` |\n| Azure OpenAI | `finish_reason = \"content_filtered\"` | `llm.finish_reason` |\n| Google GenAI | `finish_reason = \"SAFETY\"` | `llm.finish_reason` |\n| Anthropic | `stop_reason = \"content_filter\"` | `llm.stop_reason` |\n| Anthropic | Policy violation error message | `error.message` + `llm.vendor=anthropic` |\n| Google/LangChain | `\"blocked\": true` or `\"probability\": \"HIGH\"` in safety ratings | `llm.safety_ratings` |\n\n### Disabling Tier C (heuristic) detection\n\nTier A/B stay on. Turn off tool-error keyword heuristics if you do not want Tier C at all:\n\n```python\ninit(guardrail_heuristics=False)\n```\n\nOr `TRACCIA_GUARDRAIL_HEURISTICS=false`, or in `traccia.toml`: `[instrumentation]` → `guardrail_heuristics = false`.\n\n### Hard limits — what cannot be captured\n\n- Guardrails running outside the traced process (API gateways, proxies, external validators) are invisible unless they write span attributes.\n- A guardrail that exists but never fires cannot be distinguished from a missing guardrail. Only explicit annotation proves presence.\n- Prompt injection detection requires an explicit span — the model's output alone cannot reliably indicate whether a check ran.\n- Capability inference (`handles_user_text`) is inferred from `llm.prompt` being present; batch pipelines and user-facing agents look the same. Use suppression to opt out.\n\n---\n\n## 📖 Configuration\n\n### Configuration Precedence\n\nTraccia merges configuration from multiple sources with the following priority (highest to lowest):\n\n1. **Explicit parameters** — `init(endpoint=\"...\", agent_id=\"...\")` or `start_tracing(...)`\n2. **Environment variables** — `TRACCIA_ENDPOINT`, `TRACCIA_AGENT_ID`, etc.\n3. **Config file** — `traccia.toml` (current directory) or `~/.traccia/config.toml`\n4. **Defaults** — Built-in SDK defaults\n\n**Example**: If you set `TRACCIA_ENDPOINT` in your environment *and* pass `endpoint=...` to `init()`, the explicit parameter wins.\n\n---\n\n### Configuration File\n\nCreate a `traccia.toml` file in your project root:\n\n```bash\ntraccia config init\n```\n\nThis creates a template config file:\n\n```toml\n[tracing]\n# API key — required for the Traccia platform, not needed for local OTLP backends\napi_key = \"\"\n\n# Endpoint URL for OTLP trace ingestion (default: Traccia platform)\n# For local OTLP backends use e.g. endpoint = \"http://localhost:4318/v1/traces\"\nendpoint = \"https://api.traccia.ai/v2/traces\"\n\nsample_rate = 1.0           # 0.0 to 1.0\nauto_start_trace = true     # Auto-start root trace on init\nauto_trace_name = \"root\"    # Name for auto-started trace\nuse_otlp = true             # Use OTLP exporter\n# service_name = \"my-app\"   # Optional service name\n# service_role has no env var — pass via init(service_role=\"orchestrator\")\n\n[exporters]\n# Only enable ONE exporter at a time\nenable_console = false        # Print traces to console\nenable_file = false           # Write traces to file\nfile_exporter_path = \"traces.jsonl\"\nreset_trace_file = false      # Reset file on initialization\n\n[instrumentation]\nenable_patching = true          # Auto-patch libraries (OpenAI, Anthropic, requests)\nenable_token_counting = true    # Count tokens for LLM calls\nenable_costs = true             # Calculate costs\nopenai_agents = true            # Auto-enable OpenAI Agents SDK integration\ncrewai = true                   # Auto-enable CrewAI integration\nguardrail_heuristics = true     # Tier C heuristic guardrail detection (tool error keywords)\nauto_instrument_tools = false   # Auto-instrument tool calls (experimental)\nmax_tool_spans = 100            # Max tool spans to create\nmax_span_depth = 10             # Max nested span depth\n\n[rate_limiting]\n# Optional: limit spans per second\n# max_spans_per_second = 100.0\nmax_queue_size = 5000           # Max buffered spans\nmax_block_ms = 100              # Max ms to block before dropping\nmax_export_batch_size = 512     # Spans per export batch\nschedule_delay_millis = 5000    # Delay between batches\n\n[metrics]\nenable_metrics = true           # Enable OpenTelemetry metrics\n# metrics_endpoint = \"\"         # Defaults to {traces_base}/v2/metrics\nmetrics_sample_rate = 1.0       # Metrics sampling rate (1.0 = 100%)\n\n[runtime]\n# Optional runtime metadata (agent identity: prefer init(agent_id=..., agent_name=..., env=...) or TRACCIA_* env)\n# session_id = \"\"\n# user_id = \"\"\n# tenant_id = \"\"\n# project_id = \"\"\n# agent_id = \"\"   # Single-agent: set in code or TRACCIA_AGENT_ID\n# agent_name = \"\"\n# env = \"\"        # e.g. production, staging, dev\n\n[logging]\ndebug = false                   # Enable debug logging\nenable_span_logging = false     # Enable span-level logging\n\n[advanced]\n# attr_truncation_limit = 1000  # Max attribute value length\n```\n\n### Default endpoint\n\nIf you do not set `endpoint` (in config, environment, or when calling `init()` / `start_tracing()`), the SDK uses the **Traccia platform** by default (`https://api.traccia.ai/v2/traces`). You can override it to send traces to your own OTLP-compatible backend.\n\nThe default is defined in `traccia.config`: `DEFAULT_OTLP_TRACE_ENDPOINT`. The alias `DEFAULT_ENDPOINT` is kept for backward compatibility (same value).\n\n### OTLP Backend Compatibility\n\nTraccia is fully OTLP-compatible and works with:\n- **Grafana Tempo** - `http://tempo:4318/v1/traces`\n- **Jaeger** - `http://jaeger:4318/v1/traces`\n- **Zipkin** - Configure via OTLP endpoint\n- **SigNoz** - Self-hosted observability platform\n- **Traccia Platform** - `https://api.traccia.ai/v2/traces` (requires API key)\n\n### Environment Variables\n\nAll config parameters can be set via environment variables with the `TRACCIA_` prefix:\n\n**Tracing**: `TRACCIA_API_KEY`, `TRACCIA_ENDPOINT`, `TRACCIA_SAMPLE_RATE`, `TRACCIA_AUTO_START_TRACE`, `TRACCIA_AUTO_TRACE_NAME`, `TRACCIA_USE_OTLP`, `TRACCIA_SERVICE_NAME`\n\n**Exporters**: `TRACCIA_ENABLE_CONSOLE`, `TRACCIA_ENABLE_FILE`, `TRACCIA_FILE_PATH`, `TRACCIA_RESET_TRACE_FILE`\n\n**Instrumentation**: `TRACCIA_ENABLE_PATCHING`, `TRACCIA_ENABLE_TOKEN_COUNTING`, `TRACCIA_ENABLE_COSTS`, `TRACCIA_AUTO_INSTRUMENT_TOOLS`, `TRACCIA_MAX_TOOL_SPANS`, `TRACCIA_MAX_SPAN_DEPTH`, `TRACCIA_OPENAI_AGENTS`, `TRACCIA_CREWAI`, `TRACCIA_GUARDRAIL_HEURISTICS`\n\n**Rate Limiting**: `TRACCIA_MAX_SPANS_PER_SECOND`, `TRACCIA_MAX_QUEUE_SIZE`, `TRACCIA_MAX_BLOCK_MS`, `TRACCIA_MAX_EXPORT_BATCH_SIZE`, `TRACCIA_SCHEDULE_DELAY_MILLIS`\n\n**Runtime**: `TRACCIA_SESSION_ID`, `TRACCIA_USER_ID`, `TRACCIA_TENANT_ID`, `TRACCIA_PROJECT_ID`, `TRACCIA_AGENT_ID`, `TRACCIA_AGENT_NAME`, `TRACCIA_ENV`\n\nLegacy alias: `TRACCIA_PROJECT` (maps to `project_id`)\n\n**Logging**: `TRACCIA_DEBUG`, `TRACCIA_ENABLE_SPAN_LOGGING`\n\n**Advanced**: `TRACCIA_ATTR_TRUNCATION_LIMIT`\n\n### Programmatic Configuration\n\n```python\nfrom traccia import init\n\n# Override config programmatically (including agent identity for single-agent services)\ninit(\n    endpoint=\"http://tempo:4318/v1/traces\",\n    sample_rate=0.5,\n    enable_costs=True,\n    max_spans_per_second=100.0,\n    agent_id=\"my-agent\",\n    agent_name=\"My Agent\",\n    env=\"production\",\n)\n```\n\n### Multi-Agent Orchestrator Services\n\nFor services that orchestrate many logical agents in one process, set a service role and scope per-run identity:\n\n```python\nfrom traccia import init, runtime_config\n\ninit(\n    service_name=\"my-multi-agent-api\",\n    service_role=\"orchestrator\",\n    auto_start_trace=False,\n)\n\nwith runtime_config.run_identity(agent_id=\"billing-agent\", agent_name=\"Billing Agent\", env=\"production\"):\n    # run one logical agent task\n    ...\n```\n\nThis prevents the host service from being registered as a synthetic agent in the Traccia platform.\n\n### Safe Parallel Runs in One Process\n\nIf one **process** runs many agents concurrently (for example, an API server or orchestrator), use this pattern:\n\n- Call `init()` **once per process** (for example at startup), not per request.\n- Wrap each logical \"run\" in `runtime_config.run_identity(...)` to set agent id/name/env for that run.\n- Do **not** call `stop_tracing()` per request; use `force_flush()` to flush spans/metrics after a run without shutting down the provider.\n\n```python\nfrom traccia import init, span, force_flush, runtime_config\n\ninit(service_name=\"multi-agent-service\", auto_start_trace=False)\n\ndef run_agent(agent_id: str, env: str, payload: dict):\n    # Scope identity to this run only\n    with runtime_config.run_identity(agent_id=agent_id, agent_name=agent_id, env=env):\n        with span(\"agent.run\") as root:\n            root.set_attribute(\"agent.id\", agent_id)\n            root.set_attribute(\"agent.run.mode\", \"api\")\n            # ... your agent logic here ...\n\n    # Flush without tearing down the global provider\n    force_flush(5.0)\n```\n\n---\n\n## 🎯 Usage Guide\n\n### The `@observe` Decorator\n\nThe `@observe` decorator is the primary way to instrument your code:\n\n```python\nfrom traccia import observe\n\n# Basic usage\n@observe()\ndef process_data(data):\n    return transform(data)\n\n# Custom span name\n@observe(name=\"data_pipeline\")\ndef process_data(data):\n    return transform(data)\n\n# Add custom attributes\n@observe(attributes={\"version\": \"2.0\", \"env\": \"prod\"})\ndef process_data(data):\n    return transform(data)\n\n# Specify span type\n@observe(as_type=\"llm\")  # \"span\", \"llm\", \"tool\"\ndef call_llm():\n    pass\n\n# Skip capturing specific arguments\n@observe(skip_args=[\"password\", \"secret\"])\ndef authenticate(username, password):\n    pass\n\n# Skip capturing result (for large returns)\n@observe(skip_result=True)\ndef fetch_large_dataset():\n    return huge_data\n```\n\n**Available Parameters**:\n- `name` (str, optional): Custom span name (defaults to function name)\n- `attributes` (dict, optional): Initial span attributes\n- `as_type` (str): Span type - `\"span\"`, `\"llm\"`, `\"tool\"`, or `\"guardrail\"`\n- `skip_args` (list, optional): List of argument names to skip capturing\n- `skip_result` (bool): Skip capturing the return value\n\n### Async Functions\n\n`@observe` works seamlessly with async functions:\n\n```python\n@observe()\nasync def async_task(x):\n    await asyncio.sleep(1)\n    return x * 2\n\nresult = await async_task(5)\n```\n\n### Manual Span Creation\n\nFor more control, create spans manually:\n\n```python\nfrom traccia import get_tracer, span\n\n# Using convenience function\nwith span(\"operation_name\") as s:\n    s.set_attribute(\"key\", \"value\")\n    s.add_event(\"checkpoint_reached\")\n    do_work()\n\n# Using tracer directly\ntracer = get_tracer(\"my_service\")\nwith tracer.start_as_current_span(\"operation\") as s:\n    s.set_attribute(\"user_id\", 123)\n    do_work()\n```\n\n### Error Handling\n\nTraccia automatically captures and records errors:\n\n```python\n@observe()\ndef failing_function():\n    raise ValueError(\"Something went wrong\")\n\n# Span will contain:\n# - error.type: \"ValueError\"\n# - error.message: \"Something went wrong\"\n# - error.stack_trace: (truncated stack trace)\n# - span status: ERROR\n```\n\n### Nested Spans\n\nSpans are automatically nested based on call hierarchy:\n\n```python\n@observe()\ndef parent_operation():\n    child_operation()\n    return \"done\"\n\n@observe()\ndef child_operation():\n    grandchild_operation()\n\n@observe()\ndef grandchild_operation():\n    pass\n\n# Creates nested span hierarchy:\n# parent_operation\n#   └── child_operation\n#       └── grandchild_operation\n```\n\n---\n\n## 🛠️ CLI Tools\n\nTraccia includes a powerful CLI for configuration and diagnostics:\n\n### `traccia config init`\n\nCreate a new `traccia.toml` configuration file:\n\n```bash\ntraccia config init\ntraccia config init --force  # Overwrite existing\n```\n\n### `traccia doctor`\n\nValidate configuration and diagnose issues:\n\n```bash\ntraccia doctor\n\n# Output:\n# 🩺 Running Traccia configuration diagnostics...\n# \n# ✅ Found config file: ./traccia.toml\n# ✅ Configuration is valid\n# \n# 📊 Configuration summary:\n#    • API Key: ❌ Not set (optional)\n#    • Endpoint: https://api.traccia.ai/v2/traces\n#    • Sample Rate: 1.0\n#    • OTLP Exporter: ✅ Enabled\n```\n\n### `traccia check`\n\nTest connectivity to your exporter endpoint:\n\n```bash\ntraccia check\ntraccia check --endpoint http://tempo:4318/v1/traces\n```\n\n---\n\n## 🎨 Advanced Features\n\n### Rate Limiting\n\nProtect your infrastructure with built-in rate limiting:\n\n```toml\n[rate_limiting]\nmax_spans_per_second = 100.0  # Limit to 100 spans/sec\nmax_queue_size = 5000         # Max buffered spans\nmax_block_ms = 100             # Block up to 100ms before dropping\n```\n\n**Behavior**:\n1. Try to acquire capacity immediately\n2. If unavailable, block for up to `max_block_ms`\n3. If still unavailable, drop span and log warning\n\nWhen spans are dropped due to rate limiting, warnings are logged to help you monitor and adjust limits.\n\n### Sampling\n\nControl trace volume with sampling:\n\n```python\n# Sample 10% of traces\ninit(sample_rate=0.1)\n\n# Sampling is applied at trace creation time\n# Traces are either fully included or fully excluded\n```\n\n### Token Counting \u0026 Cost Calculation\n\nAutomatic for supported LLM providers (OpenAI, Anthropic):\n\n```python\n@observe(as_type=\"llm\")\ndef call_openai(prompt):\n    response = client.chat.completions.create(\n        model=\"gpt-4\",\n        messages=[{\"role\": \"user\", \"content\": prompt}]\n    )\n    return response.choices[0].message.content\n\n# Span automatically includes:\n# - llm.token.prompt_tokens\n# - llm.token.completion_tokens\n# - llm.token.total_tokens\n# - llm.cost.total (in USD)\n```\n\n### Metrics\n\nTraccia emits OTEL-compliant metrics for accurate cost and token tracking, independent of trace sampling.\n\n#### Why Metrics?\n\nWith trace sampling (e.g., `sample_rate=0.1`), only 10% of traces are exported. Cost calculated from traces will be **10x underestimated**. Metrics solve this by recording data for **every** LLM call, regardless of sampling.\n\n#### Default Metrics\n\nTraccia automatically emits these metrics:\n\n| Metric | Type | Unit | Description |\n|--------|------|------|-------------|\n| `gen_ai.client.token.usage` | Histogram | `{token}` | Input/output tokens per call |\n| `gen_ai.client.operation.duration` | Histogram | `s` | LLM operation duration |\n| `gen_ai.client.operation.cost` | Histogram | `usd` | Cost per call (USD) |\n| `gen_ai.client.completions.exceptions` | Counter | `1` | Exception count |\n| `gen_ai.agent.runs` | Counter | `1` | Agent runs (CrewAI, OpenAI Agents) |\n| `gen_ai.agent.turns` | Counter | `1` | Agent turns |\n| `gen_ai.agent.execution_time` | Histogram | `s` | Agent execution time |\n\n**Attributes**: `gen_ai.system` (openai, anthropic), `gen_ai.request.model`, `gen_ai.agent.id`, `gen_ai.agent.name`\n\n#### Configuration\n\n```python\nfrom traccia import init\n\ninit(\n    enable_metrics=True,  # Default: True\n    metrics_endpoint=\"https://your-backend.com/v2/metrics\",  \n    metrics_sample_rate=1.0,  # Default: 1.0 (100%)\n)\n```\n\nOr via `traccia.toml`:\n\n```toml\n[metrics]\nenable_metrics = true\nmetrics_endpoint = \"https://your-backend.com/v2/metrics\"\nmetrics_sample_rate = 1.0\n```\n\nOr via environment variables:\n\n```bash\nexport TRACCIA_ENABLE_METRICS=true\nexport TRACCIA_METRICS_ENDPOINT=https://your-backend.com/v2/metrics\nexport TRACCIA_METRICS_SAMPLE_RATE=1.0\n```\n\n#### Custom Metrics\n\nRecord your own metrics:\n\n```python\nfrom traccia.metrics import record_counter, record_histogram\n\n# Record a counter\nrecord_counter(\"my_custom_events\", 1, {\"event_type\": \"user_action\"})\n\n# Record a histogram\nrecord_histogram(\"my_custom_latency\", 0.123, {\"service\": \"api\"}, unit=\"s\")\n```\n\n#### Agent Metrics vs. Plain LLM Calls\n\nAgent-level metrics (such as `gen_ai.agent.runs` and `gen_ai.agent.execution_time`) are only emitted when Traccia can\nsee a real **agent lifecycle** (for example, CrewAI crews or OpenAI Agents SDK runs). For plain OpenAI/Anthropic calls\nand most simple LangChain usages, you will still get full LLM metrics (`gen_ai.client.*`), but no agent metrics unless\nyou build an explicit agent abstraction on top.\n\n---\n\n## 🔧 Troubleshooting\n\n### Enable Debug Logging\n\n```python\nimport logging\nlogging.basicConfig(level=logging.DEBUG)\n\n# Or via config\ninit(debug=True)\n\n# Or via env var\n# TRACCIA_DEBUG=1 python your_script.py\n```\n\n### Common Issues\n\n#### **Traces not appearing**\n\n1. Check connectivity: `traccia check`\n2. Validate config: `traccia doctor`\n3. Enable debug logging\n4. Verify endpoint is correct and accessible\n\n#### **High memory usage**\n\n- Reduce `max_queue_size` in rate limiting config\n- Lower `sample_rate` to reduce volume\n- Enable rate limiting with `max_spans_per_second`\n\n#### **Spans being dropped**\n\n- Check rate limiter logs for warnings\n- Increase `max_spans_per_second` if set\n- Increase `max_queue_size` if spans are queued\n- Check `traccia doctor` output\n\n---\n\n## 📚 API Reference\n\n### Core Functions\n\n#### `init(**kwargs) -\u003e TracerProvider`\n\nInitialize the Traccia SDK. All parameters are optional; configuration is merged from `traccia.toml` → env vars → explicit parameters (highest wins).\n\n**Parameters**:\n\n*Tracing*\n- `endpoint` (str): OTLP endpoint URL (default: `https://api.traccia.ai/v2/traces`)\n- `api_key` (str): API key for the Traccia platform\n- `sample_rate` (float): Sampling rate 0.0–1.0 (default: 1.0)\n- `auto_start_trace` (bool): Auto-start a root trace on init (default: True)\n- `auto_trace_name` (str): Name for the auto-started trace (default: `\"root\"`)\n- `use_otlp` (bool): Use OTLP exporter (default: True)\n- `service_name` (str): Service name (auto-detected if not set)\n- `service_role` (str): `\"orchestrator\"` to prevent this service being registered as an agent\n- `config_file` (str): Path to a custom `traccia.toml`\n\n*Exporters*\n- `enable_console_exporter` (bool): Print spans to stdout (default: False)\n- `enable_file_exporter` (bool): Write spans to file (default: False)\n- `file_exporter_path` (str): Path for file exporter (default: `\"traces.jsonl\"`)\n- `reset_trace_file` (bool): Clear file on init (default: False)\n\n*Instrumentation*\n- `enable_patching` (bool): Auto-patch OpenAI, Anthropic, requests (default: True)\n- `enable_token_counting` (bool): Count tokens (default: True)\n- `enable_costs` (bool): Calculate costs (default: True)\n- `openai_agents` (bool): Auto-enable OpenAI Agents SDK integration (default: True)\n- `crewai` (bool): Auto-enable CrewAI integration (default: True)\n- `guardrail_heuristics` (bool): Enable Tier C heuristic guardrail detection (default: True)\n- `auto_instrument_tools` (bool): Experimental tool auto-instrumentation (default: False)\n- `max_tool_spans` (int): Max tool spans per trace (default: 100)\n- `max_span_depth` (int): Max nested span depth (default: 10)\n\n*Agent identity (single-agent services)*\n- `agent_id` (str): Logical agent identifier\n- `agent_name` (str): Human-readable agent name\n- `env` (str): Deployment environment, e.g. `\"production\"`, `\"staging\"`\n\n*Runtime metadata*\n- `session_id` (str): Session identifier\n- `user_id` (str): User identifier\n- `tenant_id` (str): Tenant / org identifier\n- `project_id` (str): Project identifier\n\n*Metrics*\n- `enable_metrics` (bool): Emit OTEL metrics (default: True)\n- `metrics_endpoint` (str): Metrics endpoint (derived from tracing endpoint if not set)\n- `metrics_sample_rate` (float): Metrics sampling rate (default: 1.0)\n\n*Rate limiting*\n- `max_spans_per_second` (float): Rate limit spans/sec (default: None = unlimited)\n- `max_block_ms` (int): Max ms to block before dropping a span (default: 100)\n- `max_queue_size` (int): Max buffered spans (default: 5000)\n- `max_export_batch_size` (int): Spans per export batch (default: 512)\n- `schedule_delay_millis` (int): Batch export interval ms (default: 5000)\n\n*Misc*\n- `debug` (bool): Enable debug logging (default: False)\n- `attr_truncation_limit` (int): Max attribute value length (default: None)\n\n**Returns**: TracerProvider instance\n\n#### `stop_tracing(flush_timeout: float = 1.0) -\u003e None`\n\nStop tracing and flush pending spans.\n\n**Parameters**:\n- `flush_timeout` (float): Max seconds to wait for flush\n\n#### `get_tracer(name: str = \"default\") -\u003e Tracer`\n\nGet a tracer instance.\n\n**Parameters**:\n- `name` (str): Tracer name (typically module/service name)\n\n**Returns**: Tracer instance\n\n#### `span(name: str, attributes: dict = None) -\u003e Span`\n\nCreate a span context manager.\n\n**Parameters**:\n- `name` (str): Span name\n- `attributes` (dict, optional): Initial attributes\n\n**Returns**: Span context manager\n\n### Decorator\n\n#### `@observe(name=None, *, attributes=None, tags=None, as_type=\"span\", skip_args=None, skip_result=False)`\n\nDecorate a function to create spans automatically.\n\n**Parameters**:\n- `name` (str, optional): Span name (default: function name)\n- `attributes` (dict, optional): Initial attributes\n- `tags` (list[str], optional): User-defined identifiers for the observed method\n- `as_type` (str): Span type (`\"span\"`, `\"llm\"`, `\"tool\"`, `\"guardrail\"`)\n- `skip_args` (list, optional): Arguments to skip capturing\n- `skip_result` (bool): Skip capturing return value\n\n### Configuration\n\n#### `load_config(config_file=None, overrides=None) -\u003e TracciaConfig`\n\nLoad and validate configuration.\n\n**Parameters**:\n- `config_file` (str, optional): Path to config file\n- `overrides` (dict, optional): Override values\n\n**Returns**: Validated TracciaConfig instance\n\n**Raises**: `ConfigError` if invalid\n\n#### `validate_config(config_file=None, overrides=None) -\u003e tuple[bool, str, TracciaConfig | None]`\n\nValidate configuration without loading.\n\n**Returns**: Tuple of (is_valid, message, config_or_none)\n\n---\n\n## 🏗️ Architecture\n\n### Data Flow\n\n```\nApplication Code (@observe)\n        ↓\n   Span Creation\n        ↓\n   Processors (token counting, cost, enrichment)\n        ↓\n   Rate Limiter (optional)\n        ↓\n   Batch Processor (buffering)\n        ↓\n   Exporter (OTLP/Console/File)\n        ↓\n   Backend (Grafana Tempo / Jaeger / Zipkin / etc.)\n```\n\n### Instrumentation vs Integrations\n\n- **`traccia.instrumentation.*`**: Infrastructure and vendor instrumentation.\n  - HTTP client/server helpers (including FastAPI middleware).\n  - Vendor SDK hooks and monkey patching (e.g., OpenAI, Anthropic, `requests`).\n  - Decorators and utilities used for auto-instrumenting arbitrary functions.\n\n- **`traccia.integrations.*`**: AI/agent framework integrations.\n  - Adapters that plug into higher-level frameworks via their official extension points (e.g., LangChain callbacks).\n  - Work at the level of chains, tools, agents, and workflows rather than raw HTTP or SDK calls.\n\n---\n\n## 🤝 Contributing\n\nContributions are welcome! Whether it's bug fixes, new features, documentation improvements, or examples - we appreciate your help.\n\n### How to Contribute\n\n1. **Fork the repository**\n2. **Create a feature branch**: `git checkout -b feature/amazing-feature`\n3. **Make your changes** and add tests\n4. **Run tests**: `pytest traccia/tests/`\n5. **Lint your code**: `ruff check traccia/`\n6. **Commit**: `git commit -m \"Add amazing feature\"`\n7. **Push**: `git push origin feature/amazing-feature`\n8. **Open a Pull Request**\n\n### Development Setup\n\n```bash\n# Clone the repository (Python SDK)\ngit clone https://github.com/traccia-ai/traccia-py.git\ncd traccia-py\n\n# Create virtual environment\npython -m venv venv\nsource venv/bin/activate  # On Windows: venv\\Scripts\\activate\n\n# Install in editable mode with dev dependencies\npip install -e \".[dev]\"\n\n# Run tests\npytest traccia/tests/ -v\n\n# Run with coverage\npytest traccia/tests/ --cov=traccia --cov-report=html\n```\n\n### Code Style\n\n- Follow PEP 8\n- Use type hints where appropriate\n- Add docstrings for public APIs\n- Write tests for new features\n- Keep PRs focused and atomic\n\n### Areas We'd Love Help With\n\n- **Integrations**: Add support for more LLM providers (Cohere, AI21, local models)\n- **Backends**: Test and document setup with different OTLP backends\n- **Examples**: Real-world examples of agent instrumentation\n- **Documentation**: Tutorials, guides, video walkthroughs\n- **Performance**: Optimize hot paths, reduce overhead\n- **Testing**: Improve test coverage, add integration tests\n\n---\n\n## 📄 License\n\nApache License 2.0 - see [LICENSE](LICENSE) for full terms and conditions.\n\n---\n\n## 🙏 Acknowledgments\n\nBuilt with:\n- [OpenTelemetry](https://opentelemetry.io/) - Vendor-neutral observability framework\n- [Pydantic](https://pydantic.dev/) - Data validation\n- [tiktoken](https://github.com/openai/tiktoken) - Token counting\n\nInspired by observability tools in the ecosystem and designed to work seamlessly with the OTLP standard.\n\n---\n\n## 📞 Support \u0026 Community\n\n- **Issues**: [GitHub Issues](https://github.com/traccia-ai/traccia-py/issues) - Report bugs or request features\n- **Discussions**: [GitHub Discussions](https://github.com/traccia-ai/traccia-py/discussions) - Ask questions, share ideas\n\n---\n\n**Made with ❤️ for the AI agent community**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftraccia-ai%2Ftraccia-py","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftraccia-ai%2Ftraccia-py","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftraccia-ai%2Ftraccia-py/lists"}