Metadata-Version: 2.5
Name: dataframer-journey
Version: 0.2.1
Summary: Automatic journey_id propagation for DataFramer — inbound extraction, outbound header injection, Langfuse and LangSmith trace stamping
Project-URL: Repository, https://github.com/aimonlabs/dataframer-journey-py
License: Copyright (c) 2026 AIMon Labs, Inc. All rights reserved.
        
        Permission is granted to use this software solely for the purpose of
        integrating with and sending data to the DataFramer service, whether
        directly or as part of a licensed application.
        
        Redistribution, resale, sublicensing, reverse engineering, or creation
        of derivative works based on this software, in whole or in part, is
        not permitted without the prior written permission of AIMon Labs, Inc.,
        except as expressly permitted under a separate written agreement.
License-File: LICENSE
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# dataframer-journey

Automatic `journey_id` propagation for [DataFramer](https://dataframer.ai) user-signal stitching. One line per service; after that the journey id flows browser → service → service → trace without anyone threading it by hand.

**Full documentation:** [docs.dataframer.ai/user-signals/server-instrumentation](https://docs.dataframer.ai/user-signals/server-instrumentation) — journey id semantics, framework setup, and trace stamping details.

> **New to DataFramer?** It's an AI Workflow Intelligence Platform — once journeys are stitched, see the full timeline of user actions, traces, agents, and events, then turn every accuracy fix into reusable business context.
> [Start free →](https://app.dataframer.ai/?screen=signup&utm_source=pypi&utm_medium=readme&utm_campaign=oss)

## Install

```
pip install dataframer-journey
```

Zero runtime dependencies. Hooks apply only to libraries you already have (`requests`, `httpx`, `langfuse`, `langsmith`).

## Usage

**FastAPI / Starlette:**

```python
from dataframer_journey import instrument

app = FastAPI()
instrument(app)
```

**Django** — call `instrument()` at the end of `settings.py` and add the middleware:

```python
MIDDLEWARE = [
    ...,
    'dataframer_journey.django.JourneyIdMiddleware',
]

import dataframer_journey
dataframer_journey.instrument()
```

That's it. From then on:

- **Inbound**: the journey id is read off each incoming request (`baggage` header → `X-Journey-Id` header → `df_journey_id` cookie set by `@dataframer/signals`) into request-scoped context.
- **Outbound**: every outgoing `requests`/`httpx` call automatically carries it to the next service (both `X-Journey-Id` and W3C `baggage`, so it also rides existing OpenTelemetry propagation). Explicitly-set headers are never overwritten.
- **Traces**: every Langfuse trace and every LangSmith run created during the request gets `metadata.journey_id` (and a `journey:<id>` tag) stamped automatically. Metadata you set on the trace or run yourself is kept; an explicit `journey_id` there wins.

**Non-HTTP entry points** (queue consumers, scheduled jobs, raw `ThreadPoolExecutor` or `run_in_executor` work, WebSocket handlers): take the id from the payload yourself —

```python
from dataframer_journey import with_journey, get_journey_id

with with_journey(payload["journey_id"]):
    ...  # traces created here are stamped; outbound calls carry the id
```

## Notes

- Langfuse SDK v2, v3 (3.9.0 or newer) and v4 are supported, through the v2 `trace()` method or the OpenTelemetry-based `propagate_attributes()` scope. Langfuse 3.0–3.8 has no such scope: the library logs a warning and does not stamp; upgrade.
- LangSmith is supported (tested with 0.3.45 and 0.14.0) via its `tracing_context()` scope: `@traceable` functions, `trace()` blocks, wrapped OpenAI clients, and LangChain/LangGraph runs all pick it up.
- Signals showing as unmatched? Set the `dataframer_journey` logger to `DEBUG`: it names every request that arrived without a journey id and, on Langfuse v2, every trace created outside one.
- Traces are stamped only while the request or `with_journey` block is running; a span or run started from a background task after the request ends carries no id.
- The browser side is [`@dataframer/signals`](https://www.npmjs.com/package/@dataframer/signals), which generates the journey id and sends it on requests to your API.
- CORS: if your frontend and API are on different origins, allow the `X-Journey-Id` (or `baggage`) request header in your API's CORS config. Same-site setups need nothing — the cookie carries it.
