raindrop-bedrock
Raindrop observability integration for AWS Bedrock (Python). Automatically captures converse() and invoke_model() calls by wrapping the boto3 bedrock-runtime client.
Installation
pip install raindrop-bedrock
For async support with aioboto3:
pip install raindrop-bedrock[async]
Quick Start
import boto3
from raindrop_bedrock import RaindropBedrock
rb = RaindropBedrock(api_key="your-write-key", user_id="user-123")
client = boto3.client("bedrock-runtime", region_name="us-east-1")
rb.wrap(client)
response = client.converse(
modelId="anthropic.claude-3-5-sonnet-20241022-v2:0",
messages=[{"role": "user", "content": [{"text": "Hello!"}]}],
)
rb.flush()
Projects
Route events to a specific project by passing its slug as project_id:
rb = RaindropBedrock(
api_key="your-write-key",
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_bedrock(...) factory. Invalid slugs are ignored with a warning and no header is sent.
Debug Mode
Enable verbose logging with the debug flag:
rb = RaindropBedrock(api_key="your-write-key", user_id="user-123", debug=True)
Async Usage
import aioboto3
from raindrop_bedrock import RaindropBedrock
rb = RaindropBedrock(api_key="rk_...", user_id="user-123")
session = aioboto3.Session()
async with session.client("bedrock-runtime", region_name="us-east-1") as client:
rb.async_wrap(client)
response = await client.converse(
modelId="anthropic.claude-3-5-sonnet-20241022-v2:0",
messages=[{"role": "user", "content": [{"text": "Hello!"}]}],
)
rb.flush()
identify()
Associate a user with optional traits:
rb.identify(user_id="user-123", traits={"plan": "pro", "company": "Acme"})
track_signal()
Track feedback, edits, or custom signals:
rb.track_signal(
event_id="evt_abc123",
name="thumbs_up",
signal_type="feedback",
sentiment="POSITIVE",
comment="Great answer!",
)
flush() / shutdown()
Always call flush() before your process exits to ensure all telemetry is shipped:
rb.flush() # flush pending data
rb.shutdown() # flush + release resources
Legacy Factory Function
The create_raindrop_bedrock() factory function is still supported for backwards compatibility:
from raindrop_bedrock import create_raindrop_bedrock
raindrop = create_raindrop_bedrock(api_key="your-write-key", user_id="user-123")
client = boto3.client("bedrock-runtime", region_name="us-east-1")
raindrop.wrap(client)
What Gets Captured
| Method | Captured Data |
|---|---|
converse() |
Input messages, output text, model ID, token usage (inputTokens/outputTokens), stop reason (stopReason), cached tokens (cacheReadInputTokenCount, cacheWriteInputTokenCount), conversation ID |
invoke_model() |
Raw request/response bodies, model ID, token usage (Claude, Titan, and Llama formats), stop reason (Claude: stop_reason, Llama: stop_reason), cached tokens (Claude: cache_read_input_tokens) |
| Errors | Error type and message are captured in event properties, then the exception is re-raised |
Captured Properties
| Property Key | Source | Description |
|---|---|---|
ai.usage.prompt_tokens |
Both APIs | Input/prompt token count |
ai.usage.completion_tokens |
Both APIs | Output/completion token count |
ai.usage.cached_tokens |
Converse: cacheReadInputTokenCount; Claude InvokeModel: cache_read_input_tokens |
Tokens read from cache |
ai.usage.cache_write_tokens |
Converse: cacheWriteInputTokenCount |
Tokens written to cache |
bedrock.finish_reason |
Converse: stopReason; InvokeModel: varies by model |
Why the model stopped generating |
API Reference
RaindropBedrock(api_key=None, user_id=None, convo_id=None, project_id=None, tracing_enabled=True, bypass_otel_for_tools=True, disable_auto_instrument=True, debug=False)
| Parameter | Type | Default | Description |
|---|---|---|---|
api_key |
str | None |
None |
Raindrop API key. Warns if not provided. |
user_id |
str | None |
None |
Default user ID for events (falls back to "unknown") |
convo_id |
str | None |
None |
Group events into a conversation |
project_id |
str | None |
None |
Route events to a specific project (slug); omit for the default Production project |
tracing_enabled |
bool |
True |
Enable Raindrop tracing |
bypass_otel_for_tools |
bool |
True |
Bypass OpenTelemetry for tool-level instrumentation |
disable_auto_instrument |
bool |
True |
Library auto-instrumentation is opt-in (see below) |
debug |
bool |
False |
Enable verbose DEBUG-level logging |
Library auto-instrumentation is opt-in
As of 0.0.4, disable_auto_instrument defaults to True: the
integration no longer lets Traceloop monkey-patch every LLM client library
it recognizes in your process (botocore client creation, OpenAI, Anthropic, etc.). The
wrapper captures input/output, token usage, model name, and stop reason
directly from the wrapped boto3 client's responses, 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.
Methods
| Method | Description |
|---|---|
wrap(client) |
Instrument a sync boto3 bedrock-runtime client |
async_wrap(client) |
Instrument an async aioboto3 bedrock-runtime client |
identify(user_id, traits=None) |
Identify a user with optional traits |
track_signal(event_id, name, ...) |
Track a signal event |
flush() |
Flush pending events |
shutdown() |
Flush and shut down |
Testing
cd packages/bedrock-python
pip install -e ".[async]"
pip install pytest
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 that maps the shared scenario
corpus onto the wrapper's public API; known gaps are tracked as ticket-linked
entries in 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. The harness is
pinned by commit SHA (HARNESS_REF). See the harness docs:
HOW-IT-WORKS ·
AGENTS ·
README.
Known Limitations
- InvokeModel body replacement: After consuming the response body stream, it's replaced with a
BytesIOobject. Callers usingStreamingBody.read()will get the same bytes, but the originalStreamingBodyAPI is not preserved. - Async support requires the
[async]extra (aioboto3>=12.0.0).
Application Git metadata
RaindropBedrock(...) and create_raindrop_bedrock(...) 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.
Full Documentation
docs.raindrop.ai/integrations/bedrock
License
MIT
Release files for raindrop-bedrock 0.0.10
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| raindrop_bedrock-0.0.10.tar.gz | 31.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| raindrop_bedrock-0.0.10-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 46.6 kB
Release files / raindrop_bedrock-0.0.10.tar.gz
| Download URL | raindrop_bedrock-0.0.10.tar.gz |
|---|---|
| Size | 31.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
35be33e7dc5e825ac60a465be1f046cb1f5c29d524090e2e4757049133964d51
|
|
BLAKE2b-256 checksum How to use checksums |
9d8e1a57b958f26fa247f79e4000444b6ce5f58714b5876e88ebccc884e651da
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Release files / raindrop_bedrock-0.0.10-py3-none-any.whl
| Download URL | raindrop_bedrock-0.0.10-py3-none-any.whl |
|---|---|
| Size | 14.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
416f32cde4bffa5d1e3eaa8c3d97506115de9cb9b026146ae0f155cf0331125c
|
|
BLAKE2b-256 checksum How to use checksums |
01614703eed5f44767064c9b52bc29d07b44ac4c0c2dcea513df792f44872169
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|