Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"// PURPOSE": "Makes Cursor / VS Code show ruff + mypy warnings inline while you edit. Install the 'Ruff' and 'Python' extensions from the Cursor extension panel for full effect.",

"python.analysis.typeCheckingMode": "basic",
"python.analysis.autoImportCompletions": true,
"python.analysis.diagnosticMode": "workspace",

"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.ruff": "explicit",
"source.organizeImports.ruff": "explicit"
}
},

"ruff.enable": true,
"ruff.importStrategy": "fromEnvironment",
"ruff.lint.run": "onType",
"ruff.nativeServer": "on",

"files.exclude": {
"**/__pycache__": true,
"**/.mypy_cache": true,
"**/.ruff_cache": true,
"**/.pytest_cache": true
},

"files.associations": {
"*.toml": "toml"
},
"cursorpyright.analysis.autoImportCompletions": true,
"cursorpyright.analysis.diagnosticMode": "workspace",
"cursorpyright.analysis.typeCheckingMode": "basic"
}
67 changes: 16 additions & 51 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,61 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- **Security**
- OTLP bearer token is attached only when the endpoint host is botanu-owned
(`*.botanu.ai`) or a local dev host, preventing tenant API-key leakage
via a customer-supplied `OTEL_EXPORTER_OTLP_ENDPOINT`.
- Authorization / `x-api-key` / `botanu-api-key` headers and `user:pass@`
URL credentials are redacted in logs.
- **Brownfield OTel coexistence**
- `SampledSpanProcessor` preserves the host app's existing TracerProvider
sampling ratio when botanu is bootstrapped into a project that already
has OTel wired up.
- `register.py` entry point for explicit opt-in without decorator-side
provider mutation.
- Bootstrap detects a pre-configured provider and hands off instead of
overriding it.
- **Content capture for eval**
- Workflow-level input/output capture gated by `content_capture_rate`
config and a shared `content_sampler`. Writes
`botanu.eval.input_content` / `botanu.eval.output_content`.
- `set_input_content()` / `set_output_content()` on `LLMTracker` with the
same gate, plus matching helpers on data-tracking helpers.
- **Multi-step workflows**
- `@botanu_workflow(..., step=...)` parameter (stored in `RunContext`,
not yet emitted to span attributes — kept backward compatible until the
collector servicegraph work lands).
- **Resources**
- `ResourceEnricher` span processor for deployment attributes.
- **Release tooling**
- `scripts/pre_publish_check.py` red/green gate: builds sdist + wheel,
runs `twine check`, installs into a fresh venv, validates the public
API surface, runs an end-to-end decorator + `emit_outcome` smoke test.
### Breaking

### Fixed
- Primary API is now `botanu.event(...)` — works as context manager, async context manager, and decorator. The legacy `@botanu_workflow`, `workflow` alias, `run_botanu`, and `@botanu_outcome` decorators are removed.
- `emit_outcome` is keyword-only and no longer accepts a `status` argument. Authoritative event outcome is resolved server-side from SoR connectors, HITL reviews, or eval verdict rollup.
- Lean baggage propagation is removed. All seven baggage keys (plus any retry/deadline keys when set) always propagate. The `BOTANU_PROPAGATION_MODE` env var, the `propagation_mode` field on `BotanuConfig`, `BAGGAGE_KEYS_LEAN`, and the `lean_mode` parameter on `RunContextEnricher` / `RunContext.to_baggage_dict` are all gone.

- `SampledSpanProcessor.on_start` now gates on the same ratio decision as
`on_end`; forwarding `on_start` unconditionally while gating `on_end`
leaked span bookkeeping inside wrapped exporters (QUAL-C1).
### Added

### Initial release contents
- `botanu.event(...)` as context manager, async context manager, and decorator — a single API for marking business events.
- `botanu.step(name)` for nested phase spans inside an event.
- SDK initialises automatically on the first `botanu.event(...)` call. Customers no longer need to call any bootstrap function by hand.
- `botanu.set_correlation(**keys)` for SoR Tier-1 correlation.
- Security: OTLP bearer token is attached only when the endpoint host is botanu-owned (`*.botanu.ai`) or a local dev host, preventing tenant API-key leakage via a customer-supplied `OTEL_EXPORTER_OTLP_ENDPOINT`. Authorization / `x-api-key` / `botanu-api-key` headers and `user:pass@` URL credentials are redacted in logs.
- OTel coexistence: when the host app already has the OTel SDK wired up, botanu preserves the existing sampling ratio and adds itself alongside. `register.py` module for zero-code initialisation in containers / gunicorn / process runners.
- Content capture for eval, gated by `content_capture_rate`. Writes `botanu.eval.input_content` / `botanu.eval.output_content` with in-process PII scrub (regex by default; optional Microsoft Presidio via `pip install botanu[pii-nlp]`).
- `ResourceEnricher` span processor for deployment attributes.
- Release tooling: `scripts/pre_publish_check.py` — builds sdist + wheel, runs `twine check`, installs into a fresh venv, validates the public API surface, runs an end-to-end smoke test.

Carried forward from the pre-tag scaffolding (never published):
### Fixed

- `enable()` / `disable()` bootstrap, `@botanu_workflow`,
`@botanu_outcome`, `emit_outcome()`, `set_business_context()`,
`RunContextEnricher` — with UUIDv7 run_ids.
- LLM tracking aligned with OTel GenAI semconv: `track_llm_call()`,
`track_tool_call()`, token accounting, 15+ provider normalization.
- Data tracking: `track_db_operation()`, `track_storage_operation()`,
`track_messaging_operation()`; 30+ system normalizations.
- W3C Baggage propagation with `RunContext` (retry tracking + deadline).
- Cloud resource detectors via optional extras (`aws`, `gcp`, `azure`,
`container`, `cloud`).
- Auto-instrumentation bundled in the base install — HTTP clients, web
frameworks, databases, messaging, and GenAI providers; instrumentation
packages no-op when their target library is not installed.
- `SampledSpanProcessor.on_start` now gates on the same ratio decision as `on_end`; forwarding `on_start` unconditionally while gating `on_end` leaked span bookkeeping inside wrapped exporters.

[Unreleased]: https://github.com/botanu-ai/botanu-sdk-python/commits/main
109 changes: 64 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ This SDK is built on [OpenTelemetry](https://opentelemetry.io/) for event-level

## Getting Started

An **event** is one business transaction — resolving a support ticket, processing an order, generating a report. Each event may involve multiple **runs** (LLM calls, retries, sub-workflows) across multiple services. By correlating every run to a stable `event_id`, Botanu gives you per-event cost attribution and outcome tracking without sampling artefacts.

## Install

```bash
pip install botanu
```

An **event** is one business transaction — resolving a support ticket, processing
an order, generating a report. Each event may involve multiple **runs** (LLM calls,
Expand All @@ -17,91 +23,104 @@ stable `event_id`, botanu gives you per-event cost attribution and outcome
tracking without sampling artifacts.

```bash
pip install botanu
export BOTANU_API_KEY=<your-api-key>
```

One install. Includes OTel SDK, OTLP exporter, and auto-instrumentation for
50+ libraries.
Wrap your agent:

```python
from botanu import enable, botanu_workflow, emit_outcome
import botanu

with botanu.event(event_id=ticket.id, customer_id=user.id, workflow="Support"):
agent.run(ticket)
```

That single wrap captures every LLM call, HTTP call, and DB call inside and stamps them with `event_id`, `customer_id`, and `workflow`.

enable() # reads config from environment variables
### Decorator form

@botanu_workflow("my-workflow", event_id="evt-001", customer_id="cust-42")
async def do_work():
result = await do_something()
emit_outcome("success")
return result
```python
import botanu

@botanu.event(
workflow="Support",
event_id=lambda ticket: ticket.id,
customer_id=lambda ticket: ticket.user_id,
)
def handle_ticket(ticket):
return agent.run(ticket)
```

Entry points use `@botanu_workflow`. Every other service only needs `enable()`.
All configuration is via environment variables — zero hardcoded values in code.
Works for both sync and `async def` functions.

### Multi-phase workflows

See the [Quick Start](./docs/getting-started/quickstart.md) guide for a full walkthrough.
```python
with botanu.event(event_id=ticket.id, customer_id=user.id, workflow="Support"):
with botanu.step("retrieval"):
docs = vector_db.query(ticket.query)
with botanu.step("generation"):
response = llm.complete(docs)
```

See the [Quickstart](./docs/getting-started/quickstart.md) for the full five-minute walkthrough.

## Documentation

| Topic | Description |
|-------|-------------|
| [Installation](./docs/getting-started/installation.md) | Install and configure the SDK |
| [Quick Start](./docs/getting-started/quickstart.md) | Get up and running in 5 minutes |
| [Configuration](./docs/getting-started/configuration.md) | Environment variables and options |
| [Core Concepts](./docs/concepts/) | Events, runs, context propagation, architecture |
| [LLM Tracking](./docs/tracking/llm-tracking.md) | Track model calls and token usage |
| [Data Tracking](./docs/tracking/data-tracking.md) | Database, storage, and messaging |
| [Outcomes](./docs/tracking/outcomes.md) | Record business outcomes for ROI |
| [Auto-Instrumentation](./docs/integration/auto-instrumentation.md) | Supported libraries and frameworks |
| Topic | |
| --- | --- |
| [Installation](./docs/getting-started/installation.md) | Install and configure |
| [Quickstart](./docs/getting-started/quickstart.md) | Zero-to-first-trace in five minutes |
| [Configuration](./docs/getting-started/configuration.md) | Env vars, YAML, trusted-host auth |
| [Run Context](./docs/concepts/run-context.md) | Events, runs, retries, baggage |
| [Context Propagation](./docs/concepts/context-propagation.md) | Cross-service and queue propagation |
| [Architecture](./docs/concepts/architecture.md) | SDK + collector split |
| [LLM Tracking](./docs/tracking/llm-tracking.md) | Manual LLM instrumentation (usually not needed) |
| [Data Tracking](./docs/tracking/data-tracking.md) | DB, storage, messaging (usually not needed) |
| [Content Capture](./docs/tracking/content-capture.md) | Prompt/response capture for eval, with PII scrubbing |
| [Outcomes](./docs/tracking/outcomes.md) | Diagnostic annotations and server-side resolution |
| [Auto-Instrumentation](./docs/integration/auto-instrumentation.md) | Supported libraries |
| [Kubernetes](./docs/integration/kubernetes.md) | Zero-code instrumentation at scale |
| [API Reference](./docs/api/) | Decorators, tracking API, configuration |
| [Best Practices](./docs/patterns/best-practices.md) | Recommended patterns |
| [Existing OTel / Datadog](./docs/integration/existing-otel.md) | Brownfield coexistence |
| [`event` / `step` API](./docs/api/event.md) | Primary API reference |
| [Best Practices](./docs/patterns/best-practices.md) | Patterns that work |
| [Anti-Patterns](./docs/patterns/anti-patterns.md) | Patterns that break cost attribution |

## Requirements

- Python 3.9+
- OpenTelemetry Collector (recommended for production)
- Python 3.9 or newer
- An OpenTelemetry Collector (Botanu Cloud runs one for you; self-hosted is supported too)

## Contributing

We welcome contributions from the community. Please read our
[Contributing Guide](./CONTRIBUTING.md) before submitting a pull request.
Contributions are welcome. Read the [Contributing Guide](./CONTRIBUTING.md) before opening a pull request.

This project requires [DCO sign-off](https://developercertificate.org/) on all
commits:
All commits require [DCO sign-off](https://developercertificate.org/):

```bash
git commit -s -m "Your commit message"
```

Looking for a place to start? Check the
[good first issues](https://github.com/botanu-ai/botanu-sdk-python/labels/good%20first%20issue).
Looking for a place to start? See the [good first issues](https://github.com/botanu-ai/botanu-sdk-python/labels/good%20first%20issue).

## Community

- [GitHub Discussions](https://github.com/botanu-ai/botanu-sdk-python/discussions) — questions, ideas, show & tell
- [GitHub Issues](https://github.com/botanu-ai/botanu-sdk-python/issues) — bug reports and feature requests
- [GitHub Issues](https://github.com/botanu-ai/botanu-sdk-python/issues) — bugs and feature requests

## Governance

See [GOVERNANCE.md](./GOVERNANCE.md) for details on roles, decision-making,
and the contributor ladder.

Current maintainers are listed in [MAINTAINERS.md](./MAINTAINERS.md).
See [GOVERNANCE.md](./GOVERNANCE.md) for roles, decision-making, and the contributor ladder. Current maintainers are in [MAINTAINERS.md](./MAINTAINERS.md).

## Security

To report a security vulnerability, please use
[GitHub Security Advisories](https://github.com/botanu-ai/botanu-sdk-python/security/advisories/new)
or see [SECURITY.md](./SECURITY.md) for full details. **Do not file a public issue.**
Report security vulnerabilities via [GitHub Security Advisories](https://github.com/botanu-ai/botanu-sdk-python/security/advisories/new) or see [SECURITY.md](./SECURITY.md). **Do not file a public issue.**


## Code of Conduct

This project follows the
[LF Projects Code of Conduct](https://lfprojects.org/policies/code-of-conduct/).
See [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
This project follows the [LF Projects Code of Conduct](https://lfprojects.org/policies/code-of-conduct/). See [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).

## License

[Apache License 2.0](./LICENSE)

5 changes: 3 additions & 2 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,9 @@ python -c "import botanu; print(botanu.__version__)"

# Run quick test
python -c "
from botanu import enable, botanu_workflow
enable(service_name='test')
import botanu
with botanu.event(event_id='smoke-1', customer_id='smoke-cust', workflow='smoke-test'):
pass
print('Botanu SDK loaded successfully!')
"
```
Expand Down
Loading
Loading