Metadata-Version: 2.5
Name: raindrop-strands
Version: 0.0.12
Summary: Raindrop integration for Strands Agents
Project-URL: Homepage, https://raindrop.ai
Author-email: Raindrop AI <sdk@raindrop.ai>
License-Expression: MIT
License-File: LICENSE
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: raindrop-ai>=0.0.57
Requires-Dist: strands-agents>=1.0.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: requests>=2.20; extra == 'dev'
Description-Content-Type: text/markdown

# raindrop-strands

Raindrop integration for [Strands Agents](https://strandsagents.com) (Python). Automatically captures agent invocations, model calls, tool usage, and token metrics via the Strands hook system.

## Installation

```bash
pip install raindrop-strands strands-agents
```

`strands-agents` is a required dependency.

## Quick Start

```python
import os
from strands import Agent
from raindrop_strands import RaindropStrands

raindrop = RaindropStrands(
    api_key=os.environ.get("RAINDROP_API_KEY"),
    user_id="user_123",
    convo_id="session_456",
)

agent = Agent(
    model="us.amazon.nova-lite-v1:0",
    system_prompt="You are a helpful assistant.",
)

raindrop.handler.register_hooks(agent)

result = agent("What is the capital of France?")
print(result)

raindrop.flush()
```

Omitting `api_key` disables telemetry shipping (a warning is emitted) but does not crash your application.

## Debug Mode

Enable verbose logging to troubleshoot telemetry issues:

```python
raindrop = RaindropStrands(
    api_key=os.environ.get("RAINDROP_API_KEY"),
    debug=True,
)
```

## Configuration

```python
raindrop = RaindropStrands(
    api_key="rk_...",              # Optional: Raindrop API key
    user_id="user_123",            # Optional: associate events with a user
    convo_id="session_456",        # Optional: conversation/session ID
    project_id="support-prod",     # Optional: route events to a specific project (slug)
    tracing_enabled=True,          # Optional: enable OTEL-based tracing (default: True)
    bypass_otel_for_tools=True,    # Optional: bypass OTEL for tool spans (default: True)
    debug=False,                   # Optional: enable debug logging (default: False)
)
```

## Projects

Route events to a specific [project](https://docs.raindrop.ai/platform/projects) by passing its slug as `project_id`:

```python
raindrop = RaindropStrands(
    api_key="rk_...",
    project_id="support-prod",
)
```

`project_id` sets the `X-Raindrop-Project-Id` header on every event. Omit it (or pass `"default"`) to use your org's default **Production** project, which is the existing behavior. The same option is accepted by the `create_raindrop_strands(...)` factory. Invalid slugs are ignored with a warning and no header is sent.

## Factory Function

A `create_raindrop_strands()` factory function is also available for convenience:

```python
from raindrop_strands import create_raindrop_strands

raindrop = create_raindrop_strands(api_key="rk_...")
agent = Agent(model="us.amazon.nova-lite-v1:0")
raindrop.handler.register_hooks(agent)
result = agent("Hello!")
raindrop.flush()
```

## Identifying Users

```python
raindrop.identify(
    user_id="user_123",
    traits={"plan": "pro", "email": "user@example.com"},
)
```

## Tracking Signals

Track user feedback or other signals on AI responses:

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

## Flush & Shutdown

Always flush before your process exits to ensure all data is sent:

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

## What Gets Captured

- **Agent invocations**: input prompt, output text, model name
- **Token usage**: prompt tokens, completion tokens, and cached tokens (from Bedrock/Anthropic `cacheReadInputTokens` / `cacheCreationInputTokens`)
- **Tool call spans**: individual tool spans tracked via `interaction.track_tool()` with name, input, output, duration, and error
- **Finish reason**: `stop_reason` or `finish_reason` from model responses (e.g., `end_turn`, `tool_use`)
- **Errors**: error type and message captured in event properties
- **Async support**: preserved via Strands' hook system

## API

### `RaindropStrands(api_key, user_id, convo_id, project_id, tracing_enabled, bypass_otel_for_tools, disable_auto_instrument, debug)`

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `api_key` | `str \| None` | `None` | Raindrop API key (`rk_...`). Omit to disable telemetry |
| `user_id` | `str \| None` | `None` | Associate all events with a user |
| `convo_id` | `str \| None` | `None` | Group events into a conversation |
| `project_id` | `str \| None` | `None` | Route events to a specific [project](https://docs.raindrop.ai/platform/projects) (slug); omit for the default **Production** project |
| `tracing_enabled` | `bool` | `True` | Enable OTEL-based tracing |
| `bypass_otel_for_tools` | `bool` | `True` | Bypass OTEL for tool spans |
| `disable_auto_instrument` | `bool` | `True` | Library auto-instrumentation is opt-in (see below) |
| `debug` | `bool` | `False` | Enable debug logging |

### Library auto-instrumentation is opt-in

As of `0.0.3`, `disable_auto_instrument` defaults to `True`: the
integration no longer lets Traceloop monkey-patch every LLM client library
it recognizes in your process (including the botocore machinery Strands' default Bedrock provider drives). The
hook handler captures input/output, token usage, model name, and tool calls
directly from Strands hook events, so no library patching is needed for full
dashboards.

If you specifically want LLM-call-level spans from library instrumentation
and have verified compatibility in your environment, opt back in with
`disable_auto_instrument=False`.

**Properties:**

- `handler` — `RaindropEventHandler` instance to register on agents

**Methods:**

- `flush()` — flush pending telemetry
- `shutdown()` — flush and release resources
- `identify(user_id, traits)` — identify a user with optional traits
- `track_signal(event_id, name, ...)` — track a signal event

## Application Git metadata

`RaindropStrands(...)` and `create_raindrop_strands(...)` accept the keyword-only `app_git` option. It defaults to `True`: explicit Raindrop Git environment or deployment context is applied immediately, and the base SDK may perform one bounded background local-Git lookup from the process working directory. Event capture, flush, and shutdown never wait for that lookup. Pass `False` to disable enrichment, or pass an `AppGitOptions` mapping with `commit_sha`, `commit_dirty`, `branch`, `source_directory`, `detect_branch`, and/or `auto_detect`. Automatic branch discovery remains opt-in through `detect_branch=True` (or `RAINDROP_GIT_DETECT_BRANCH=true`).

For an ordinary in-process application, the process working directory is treated as the application-under-test checkout. A remote, coding, workflow, or observer process must not rely on its own checkout: pass `app_git=False`, provide explicit revision values, or set `source_directory` to the actual application checkout. Canonical per-operation properties remain authoritative. When supplying `client=`, configure `app_git` while constructing that `Raindrop` client; the supplied client is authoritative and the wrapper's `app_git` argument does not reconfigure it.

Release order is deliberate: first publish the base SDK feature, then publish the wrapper feature release with its minimum dependency coordinated to that base release. The existing `raindrop-ai` lower bound remains compatible, but application Git metadata is unavailable on an older core and must not be claimed complete until the base is upgraded. Until coordination assigns a released version, the wrapper checks for an explicit base `app_git` parameter and omits the option when unsupported. Explicit non-default configuration is debug-logged and omitted. Unsupported `app_git` is determined by signature inspection before construction, not by retrying initialization after a `TypeError`; Git configuration adds no initialization attempts and does not change any existing framework-specific initialization fallback.

## Testing

```bash
cd packages/strands-python
pip install -e '.[dev]'
python -m pytest tests/ -v   # unit tests (no external services)
```

End-to-end behavior is verified by the cross-SDK **conformance harness**. This
package ships a thin conformance driver at
[`conformance/driver.py`](conformance/driver.py) that maps the shared scenario
corpus onto the wrapper's public API; known gaps are tracked as ticket-linked
entries in [`conformance/failures.txt`](conformance/failures.txt). The **fault
lane** runs on every PR touching `packages/*-python/**`
(`.github/workflows/conformance-wrappers-python.yml`) against a local capture
server; the **prod lane** verifies delivery by reading back through the public
[Query API](https://docs.raindrop.ai/api-reference/overview). The harness is
pinned by commit SHA (`HARNESS_REF`). See the harness docs:
[HOW-IT-WORKS](https://github.com/invisible-tools/raindrop-sdk-harness/blob/main/docs/HOW-IT-WORKS.md) ·
[AGENTS](https://github.com/invisible-tools/raindrop-sdk-harness/blob/main/AGENTS.md) ·
[README](https://github.com/invisible-tools/raindrop-sdk-harness/blob/main/README.md).

## Full Documentation

See the [Raindrop Strands integration docs](https://docs.raindrop.ai/integrations/strands) for full details.

## License

MIT
