Metadata-Version: 2.4
Name: raindrop-langchain
Version: 0.0.3
Summary: Raindrop integration for LangChain
Project-URL: Homepage, https://raindrop.ai
Project-URL: Documentation, https://docs.raindrop.ai
Project-URL: Repository, https://github.com/invisible-tools/dawn/tree/main/packages/langchain-python
Author-email: Raindrop AI <sdk@raindrop.ai>
License-Expression: MIT
License-File: LICENSE
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: langchain-core>=0.3.0
Requires-Dist: raindrop-ai>=0.0.42
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# raindrop-langchain

Raindrop integration for LangChain (Python). Automatically captures LLM calls, tool usage, chains, and retrievers via LangChain's callback system.

## Installation

```bash
pip install raindrop-langchain langchain-core
```

## Quick Start

```python
from raindrop_langchain import RaindropLangchain
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage

raindrop = RaindropLangchain(
    api_key="rk_...",
    user_id="user-123",
)

model = ChatOpenAI(model="gpt-4o")

result = model.invoke(
    [HumanMessage(content="Hello!")],
    config={"callbacks": [raindrop.handler]},
)

raindrop.flush()
```

### Factory Function (alternative)

```python
from raindrop_langchain import create_raindrop_langchain

raindrop = create_raindrop_langchain(api_key="rk_...", user_id="user-123")
model = ChatOpenAI(model="gpt-4o")
result = model.invoke("Hello!", config={"callbacks": [raindrop.handler]})
raindrop.flush()
```

## What Gets Captured

- **LLM calls** — model name, input, output, token usage, finish reason
- **Tool calls** — tool name, input arguments, output, duration (via `interaction.track_tool()` spans)
- **Chains** — execution tracking
- **Retrievers** — query and document count
- **Errors** — error type and message captured in event properties
- **Extended token categories** — cached tokens (`ai.usage.cached_tokens`) and reasoning tokens (`ai.usage.thoughts_tokens`) when available from the provider (e.g. OpenAI)
- **Finish reason** — captured as `ai.finish_reason` in event properties (e.g. `"stop"`, `"length"`)

## Debug Mode

Enable verbose logging with `debug=True`:

```python
raindrop = RaindropLangchain(
    api_key="rk_...",
    debug=True,
)
```

## Identify Users

Associate events with a user after initialization:

```python
raindrop.identify("user-123", {"name": "Alice", "plan": "pro"})
```

## Track Signals

Send feedback, edits, or custom signals:

```python
raindrop.track_signal(
    event_id="evt-abc",
    name="thumbs_up",
    signal_type="feedback",
    sentiment="POSITIVE",
)
```

## Flushing and Shutdown

```python
raindrop.flush()     # flush pending data
raindrop.shutdown()  # flush + release resources
```

## API Reference

### `RaindropLangchain`

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `api_key` | `Optional[str]` | `None` | Raindrop API key. If `None`, telemetry is disabled |
| `user_id` | `Optional[str]` | `None` | Associate all events with a user |
| `convo_id` | `Optional[str]` | `None` | Group events into a conversation |
| `trace_chains` | `bool` | `True` | Track chain execution |
| `trace_retrievers` | `bool` | `True` | Track retriever calls |
| `filter_langgraph_internals` | `bool` | `True` | Filter LangGraph-internal chain events and deduplicate LLM callbacks |
| `tracing_enabled` | `bool` | `True` | Enable distributed tracing |
| `bypass_otel_for_tools` | `bool` | `True` | Bypass OTEL for tool spans |
| `debug` | `bool` | `False` | Enable debug logging |

#### Methods

| Method | Description |
|--------|-------------|
| `handler` | Property — the LangChain callback handler to pass into `config={"callbacks": [...]}` |
| `flush()` | Flush all pending events to the Raindrop API |
| `shutdown()` | Flush remaining events and release resources |
| `identify(user_id, traits)` | Identify a user with optional traits |
| `track_signal(event_id, name, ...)` | Track a signal event |

## Async Support

The callback handler inherits from LangChain's `AsyncCallbackHandler` and works with both synchronous and asynchronous LangChain invocations.

```python
result = await model.ainvoke(
    [HumanMessage(content="Hello!")],
    config={"callbacks": [raindrop.handler]},
)
```

## LangGraph Support

Works with LangGraph out of the box. The handler automatically filters LangGraph-internal chain events and deduplicates LLM callbacks. Pass the handler to the model inside your LLM node — **not** to `graph.invoke()`. See `examples/langchain-langgraph-python-basic/` for a full example.

## LangSmith Coexistence

Raindrop and LangSmith can run simultaneously. Set `LANGSMITH_TRACING=false` to disable LangSmith if you only want Raindrop.

## Known Limitations

- **Multi-LLM chain data**: In ReAct loops with multiple child LLMs, only the last child's data survives (Python SDK uses one-shot `track_ai` vs TS's accumulative `EventShipper.patch`).
- **Error-path input loss**: On LLM errors, the input captured during `on_llm_start` is not forwarded to the finalized event.

## Testing

```bash
cd packages/langchain-python
pip install -e .
python -m pytest tests/ -v
```
