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
7 changes: 5 additions & 2 deletions docs/02-getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ from basalt.observability import start_observe, observe


@start_observe(
feature_slug="workflow",
name="my_workflow",
identity={
"organization": {"id": "123", "name": "ACME"},
Expand Down Expand Up @@ -89,6 +90,7 @@ from basalt.observability import start_observe, observe
def process_request(user_id, data):
# Create root span with identity
with start_observe(
feature_slug="request-processing",
name="process_request",
identity={"user": user_id},
metadata={"source": "api"}
Expand Down Expand Up @@ -119,6 +121,7 @@ from basalt.observability import start_observe, observe

# Method 1: Set on root span (recommended)
@start_observe(
feature_slug="chat-handler",
name="chat_handler",
identity={
"organization": {"id": "123", "name": "ACME"},
Expand All @@ -131,7 +134,7 @@ def handle_chat(message):


# Method 2: Set dynamically
@start_observe(name="api_handler")
@start_observe(feature_slug="api-handler", name="api_handler")
def handle_request(auth_token):
user_data = verify_token(auth_token) # Returns {"id": "user-123", "name": "John Doe"}
observe.set_identity({"user": user_data})
Expand All @@ -144,7 +147,7 @@ Add custom key-value pairs to your spans:

```python
# On root span
@start_observe(name="workflow", metadata={"model": "gpt-4", "temperature": 0.7})
@start_observe(feature_slug="workflow", name="workflow", metadata={"model": "gpt-4", "temperature": 0.7})
def workflow():
pass

Expand Down
2 changes: 1 addition & 1 deletion docs/03-prompts.md
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ Combine prompt context managers with manual spans:
from basalt import Basalt
from basalt.observability import start_observe, observe

@start_observe(name="QA System", identity={"user": {"id": "user_123"}})
@start_observe(feature_slug="qa-system", name="QA System", identity={"user": {"id": "user_123"}})
def answer_question(question: str):
with basalt.prompts.get_sync("qa-prompt", variables={"question": question}) as prompt:
# Prompt span is a child of "QA System"
Expand Down
8 changes: 7 additions & 1 deletion docs/05-observability.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ from basalt.observability import start_observe, observe, ObserveKind

# Root span with identity tracking
@start_observe(
feature_slug="request-processing",
name="process_request",
identity={"user": {"id": "user_123", "name": "Alice"}, "organization": {"id": "org_abc"}},
metadata={"environment": "production", "version": "2.0"}
Expand All @@ -25,6 +26,7 @@ def process():

# Root span with experiment tracking
@start_observe(
feature_slug="ml-workflow",
name="ml_workflow",
experiment={"id": "exp_456", "name": "Model Comparison"},
identity={"user": "analyst_001"}
Expand All @@ -34,6 +36,7 @@ def run_experiment():

# Context manager form
with start_observe(
feature_slug="batch-processing",
name="batch_job",
identity={"organization": {"id": "org_xyz", "name": "Acme Corp"}},
metadata={"job_id": "batch_123"}
Expand All @@ -43,6 +46,8 @@ with start_observe(
```

**Key features of `start_observe`:**
- **`feature_slug`** (required): A unique identifier for the feature or workflow being traced (e.g., "user-auth", "payment-flow").
- **`name`** (required): A descriptive name for the root span.
- **`identity`**: Dict with `user` and/or `organization` keys for tracking. Can also use simple string format or callables for dynamic resolution.
- **`experiment`**: Dict with `id`, `name`, and `variant` keys for A/B testing and feature tracking.
- **`evaluate_config`**: Configuration for evaluators attached to the root span.
Expand All @@ -57,6 +62,7 @@ from basalt.observability import observe, start_observe, ObserveKind

# Root span (required as entry point)
@start_observe(
feature_slug="request-handler",
name="process_request",
identity={
"organization": {"id": "123", "name": "ACME"},
Expand Down Expand Up @@ -182,7 +188,7 @@ Sometimes you need to set metadata or identity on the root span from deeply nest
from basalt.observability import observe, start_observe


@start_observe(name="API Handler")
@start_observe(feature_slug="api-handler", name="API Handler")
def handle_request(user_id):
# Root span starts here with identity tracking
observe.set_input({"user_id": user_id})
Expand Down
13 changes: 10 additions & 3 deletions docs/06-manual-tracing.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Every trace must begin with a root span created using `start_observe`:
from basalt.observability import start_observe, observe

@start_observe(
feature_slug="data-processing",
name="Data Processing Workflow",
identity={
"organization": {"id": "123", "name": "ACME"},
Expand Down Expand Up @@ -62,6 +63,7 @@ from basalt.observability import start_observe

def process_request():
with start_observe(
feature_slug="request-processing",
name="Request Processing",
identity={
"organization": {"id": "123", "name": "ACME"},
Expand All @@ -85,6 +87,7 @@ from basalt.observability import start_observe

# With identity tracking
@start_observe(
feature_slug="main-workflow",
name="Main Workflow",
identity={
"organization": {"id": "123", "name": "ACME"},
Expand All @@ -97,6 +100,7 @@ def main_workflow(data):

# With experiment tracking
@start_observe(
feature_slug="ab-test",
name="A/B Test",
experiment={"id": "exp_001", "name": "Model Comparison", "variant": "model_a"},
identity={
Expand All @@ -109,6 +113,7 @@ def run_experiment():

# Context manager form
with start_observe(
feature_slug="batch-job",
name="Batch Job",
identity={
"organization": {"id": "123", "name": "ACME"},
Expand All @@ -121,7 +126,8 @@ with start_observe(

**Key `start_observe` parameters:**

- `name`: Span name (defaults to function name if used as decorator)
- `feature_slug` (required): A unique identifier for the feature or workflow being traced
- `name` (required): Span name (defaults to function name if used as decorator)
- `identity`: Dict with `user` and/or `organization` keys for tracking
- `experiment`: Dict with `id`, `name`, and `variant` for A/B testing
- `evaluate_config`: Evaluator configuration for the root span
Expand Down Expand Up @@ -178,6 +184,7 @@ Decorators automatically create parent-child relationships. Always start with `s
from basalt.observability import start_observe, observe

@start_observe(
feature_slug="main-workflow",
name="Main Workflow",
identity={
"organization": {"id": "123", "name": "ACME"},
Expand Down Expand Up @@ -347,7 +354,7 @@ You can mix decorators and context managers:
```python
from basalt.observability import start_observe, observe

@start_observe(name="Main Workflow")
@start_observe(feature_slug="main-workflow", name="Main Workflow")
def main_workflow(data):
# Root span via decorator
prepare_data(data)
Expand Down Expand Up @@ -426,7 +433,7 @@ basalt = Basalt(api_key="your-api-key")
openai_client = OpenAI(api_key="your-openai-key")

# Root trace
with start_observe(name="process_request", feature_slug="support-ticket"):
with start_observe(feature_slug="support-ticket", name="process_request"):
# Fetch prompt with context manager
with basalt.prompts.get_sync("joke-analyzer", variables={"jokeText": "..."}) as prompt:
# Auto-instrumented OpenAI call automatically receives prompt attributes
Expand Down
7 changes: 5 additions & 2 deletions docs/08-async-observability.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import asyncio
from basalt.observability import async_start_observe, async_observe

async def main():
async with async_start_observe(name="async_workflow") as span:
async with async_start_observe(feature_slug="async-workflow", name="async_workflow") as span:
span.set_input({"task": "process_data"})
result = await process_data()
span.set_output(result)
Expand All @@ -42,6 +42,7 @@ from basalt.observability import async_start_observe
# Context manager form
async def workflow():
async with async_start_observe(
feature_slug="user-request",
name="User Request",
identity={"user": {"id": "user_123"}},
metadata={"environment": "production"}
Expand All @@ -52,6 +53,7 @@ async def workflow():

# Decorator form
@async_start_observe(
feature_slug="api-handler",
name="API Handler",
identity={"user": {"id": "user_456"}}
)
Expand Down Expand Up @@ -95,7 +97,7 @@ The `@observe` and `@start_observe` decorators automatically detect async functi
from basalt.observability import observe, start_observe

# These automatically work with async functions
@start_observe(name="Async Root")
@start_observe(feature_slug="async-root", name="Async Root")
async def async_root():
await nested_operation()

Expand Down Expand Up @@ -133,6 +135,7 @@ async def generate_greeting(user: dict) -> str:
async def handle_user_request(user_id: str) -> str:
"""Main handler with root span"""
async with async_start_observe(
feature_slug="user-request",
name="user_request",
identity={"user": {"id": user_id}},
metadata={"version": "2.0"}
Expand Down
2 changes: 2 additions & 0 deletions docs/11-experiments.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ from basalt.observability import start_observe, observe


@start_observe(
feature_slug="ab-test",
name="experiment.variant_a",
experiment="exp-456",
identity={
Expand All @@ -73,6 +74,7 @@ def run_variant_a():

# Or using context manager
with start_observe(
feature_slug="ab-test",
name="experiment.variant_b",
experiment="exp-456",
):
Expand Down
8 changes: 4 additions & 4 deletions docs/12-user-org-tracking.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ You can also set identity dynamically within a span using `observe.set_identity(
```python
from basalt.observability import observe, start_observe

@start_observe(name="workflow")
@start_observe(feature_slug="data-workflow", name="workflow")
def process_data(auth_token):
# Authenticate and get user info
user_info = authenticate(auth_token)
Expand All @@ -105,7 +105,7 @@ When using context managers, you can set identity on the span handle:
from basalt.observability import async_start_observe

async def workflow():
async with async_start_observe(name="process", feature_slug="task") as span:
async with async_start_observe(feature_slug="task", name="process") as span:
# Set identity on the span handle
span.set_identity({
"user": {"id": "user-789"},
Expand Down Expand Up @@ -197,7 +197,7 @@ def get_identity_from_request():
}

@app.route("/api/orders/<order_id>")
@start_observe(name="get_order", feature_slug="orders", identity=get_identity_from_request)
@start_observe(feature_slug="orders", name="get_order", identity=get_identity_from_request)
def get_order(order_id):
# Identity automatically set from request
order = fetch_order(order_id)
Expand All @@ -212,8 +212,8 @@ Identity works seamlessly with async/await:
from basalt.observability import async_start_observe

@async_start_observe(
name="async_workflow",
feature_slug="background-jobs",
name="async_workflow",
identity={"user": {"id": "user-123"}}
)
async def process_async(data):
Expand Down