> ## Documentation Index
> Fetch the complete documentation index at: https://raindrop.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# AWS Bedrock (Beta)

> Automatic tracing for AWS Bedrock applications with Raindrop

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm install @raindrop-ai/bedrock @aws-sdk/client-bedrock-runtime
  ```

  ```bash pnpm theme={null}
  pnpm add @raindrop-ai/bedrock @aws-sdk/client-bedrock-runtime
  ```

  ```bash pip theme={null}
  pip install raindrop-bedrock boto3
  ```
</CodeGroup>

## Quick Start

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { createRaindropBedrock } from "@raindrop-ai/bedrock";
  import { BedrockRuntimeClient, ConverseCommand } from "@aws-sdk/client-bedrock-runtime";

  const raindrop = createRaindropBedrock({
    writeKey: "your-write-key",
    userId: "user-123",
  });

  const client = new BedrockRuntimeClient({ region: "us-east-1" });
  const wrapped = raindrop.wrap(client);

  const response = await wrapped.send(
    new ConverseCommand({
      modelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
      messages: [
        { role: "user", content: [{ text: "What is the capital of France?" }] },
      ],
    }),
  );

  console.log(response.output?.message?.content?.[0]?.text);
  await raindrop.flush();
  ```

  ```python Python theme={null}
  import boto3
  from raindrop_bedrock import RaindropBedrock

  raindrop = RaindropBedrock(
      api_key="your-write-key",
      user_id="user-123",
  )

  client = boto3.client("bedrock-runtime", region_name="us-east-1")
  raindrop.wrap(client)

  response = client.converse(
      modelId="anthropic.claude-3-5-sonnet-20241022-v2:0",
      messages=[
          {"role": "user", "content": [{"text": "What is the capital of France?"}]},
      ],
  )

  print(response["output"]["message"]["content"][0]["text"])
  raindrop.flush()
  ```
</CodeGroup>

## What Gets Traced

The Bedrock integration automatically captures:

* **Converse API** — input messages, output text, model ID, token usage (inputTokens/outputTokens), stop reason, cached token counts
* **InvokeModel API** — raw request/response, model ID, token usage (supports Claude, Titan, and Llama response formats), stop reason, cached tokens (Claude)
* **Errors** — captured with error status on the span, re-thrown to caller

### Captured Properties

| Property                      | 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 (e.g. `end_turn`, `tool_use`, `max_tokens`) |

## Configuration

<CodeGroup>
  ```typescript TypeScript theme={null}
  const raindrop = createRaindropBedrock({
    writeKey: "your-write-key",       // Optional: your Raindrop write key (omit to disable telemetry)
    endpoint: "...",          // Optional: custom API endpoint
    userId: "user-123",      // Optional: associate events with a user
    convoId: "convo-456",    // Optional: conversation/thread ID
    projectId: "support-prod", // Optional: route events to a specific project (slug)
    debug: false,             // Optional: enable verbose logging
  });
  ```

  ```python Python theme={null}
  raindrop = RaindropBedrock(
      api_key="your-write-key",           # Optional: your Raindrop API key (omit to disable telemetry)
      user_id="user-123",        # Optional: associate events with a user
      convo_id="convo-456",      # Optional: conversation/thread ID
      project_id="support-prod", # Optional: route events to a specific project (slug)
      tracing_enabled=True,      # Optional: enable Raindrop tracing
      bypass_otel_for_tools=True,# Optional: bypass OpenTelemetry for tools
      debug=False,               # Optional: enable verbose logging
  )
  ```
</CodeGroup>

## Projects

Route events to a specific [project](/platform/projects) by passing its slug as `projectId` (`project_id` in Python):

<CodeGroup>
  ```typescript TypeScript theme={null}
  const raindrop = createRaindropBedrock({
    writeKey: "your-write-key",
    projectId: "support-prod",
  });
  ```

  ```python Python theme={null}
  raindrop = RaindropBedrock(
      api_key="your-write-key",
      project_id="support-prod",
  )
  ```
</CodeGroup>

This 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. Single-project orgs need nothing new.

### Multiple projects in one process

Available in `raindrop-ai>=0.0.56`. When one service wraps several Bedrock
clients that should report to **different** projects, create one
`RaindropBedrock` wrapper per project. Each wrapper owns its own
`raindrop.Raindrop` client, so the two route independently — there is no shared
module-level state:

```python theme={null}
from raindrop_bedrock import RaindropBedrock

# One long-lived wrapper per project, created at startup and reused.
rd_support = RaindropBedrock(api_key="your-write-key", project_id="support-prod")
rd_billing = RaindropBedrock(api_key="your-write-key", project_id="billing-prod")

rd_support.wrap(support_client)   # -> support-prod
rd_billing.wrap(billing_client)   # -> billing-prod

rd_support.flush()
rd_billing.flush()
```

Each wrapper owns its configuration and delivery pipeline, so clients handled by
different wrappers route independently. To share a single client across wrappers
(or with the module-level API), construct a `raindrop.Raindrop` yourself and
pass it via `client=`:

```python theme={null}
from raindrop import Raindrop

client = Raindrop(api_key="your-write-key", project_id="support-prod")
raindrop = RaindropBedrock(client=client)
```

## Using InvokeModel

The wrapper also supports the legacy `InvokeModel` API. Token usage extraction works with Claude, Titan, and Llama response formats:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { InvokeModelCommand } from "@aws-sdk/client-bedrock-runtime";

  const response = await wrapped.send(
    new InvokeModelCommand({
      modelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
      contentType: "application/json",
      body: JSON.stringify({
        anthropic_version: "bedrock-2023-05-31",
        messages: [{ role: "user", content: "Hello!" }],
        max_tokens: 256,
      }),
    }),
  );

  const result = JSON.parse(new TextDecoder().decode(response.body));
  console.log(result.content[0].text);
  await raindrop.flush();
  ```

  ```python Python theme={null}
  import json

  response = client.invoke_model(
      modelId="anthropic.claude-3-5-sonnet-20241022-v2:0",
      contentType="application/json",
      body=json.dumps({
          "anthropic_version": "bedrock-2023-05-31",
          "messages": [{"role": "user", "content": "Hello!"}],
          "max_tokens": 256,
      }),
  )

  result = json.loads(response["body"].read())
  print(result["content"][0]["text"])
  raindrop.flush()
  ```
</CodeGroup>

## Identifying Users

Associate a user with optional traits:

<CodeGroup>
  ```typescript TypeScript theme={null}
  raindrop.users.identify({ userId: "user-123", traits: { plan: "pro", company: "Acme" } });
  ```

  ```python Python theme={null}
  raindrop.identify(user_id="user-123", traits={"plan": "pro", "company": "Acme"})
  ```
</CodeGroup>

## Tracking Signals

Track feedback, edits, or custom signals:

<CodeGroup>
  ```typescript TypeScript theme={null}
  raindrop.signals.track({
    eventId: "evt_abc123",
    name: "thumbs_up",
    type: "feedback",
    sentiment: "POSITIVE",
    comment: "Great answer!",
  });
  ```

  ```python Python theme={null}
  raindrop.track_signal(
      event_id="evt_abc123",
      name="thumbs_up",
      signal_type="feedback",
      sentiment="POSITIVE",
      comment="Great answer!",
  )
  ```
</CodeGroup>

## Flushing and Shutdown

Always call `flush()` before your process exits to ensure all telemetry is shipped:

<CodeGroup>
  ```typescript TypeScript theme={null}
  await raindrop.flush();     // flush pending data
  await raindrop.shutdown();  // flush + release resources
  ```

  ```python Python theme={null}
  raindrop.flush()     # flush pending data
  raindrop.shutdown()  # flush + release resources
  ```
</CodeGroup>
