Metadata-Version: 2.4
Name: keel-llm-adapter-bedrock
Version: 0.1.0
Summary: Reference keel-llm-protocol adapter for AWS Bedrock — enterprise gateway. Use Claude / Llama / Titan / Mistral / Cohere models hosted on Bedrock with the Keel vendor-neutral protocol + reliability + OTel stack.
Project-URL: Homepage, https://github.com/keelplatform
Project-URL: Changelog, https://github.com/keelplatform/keel/blob/main/py/packages/llm-adapter-bedrock/CHANGELOG.md
Author: Raj Yakkali
License: MIT
Keywords: adapter,anthropic,aws,bedrock,claude,enterprise,keel,llm
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: botocore>=1.34
Requires-Dist: httpx>=0.27
Requires-Dist: keel-llm-protocol>=0.1.0
Description-Content-Type: text/markdown

# keel-llm-adapter-bedrock

> A reference [`keel-llm-protocol`](https://pypi.org/project/keel-llm-protocol/) adapter for **AWS Bedrock** — the enterprise gateway. Use Claude / Llama / Titan / Mistral / Cohere models hosted on Bedrock with Keel's vendor-neutral protocol + reliability + observability stack.

> **Try it in 5 minutes**: the [agentic reference demo](https://github.com/keelplatform/keel-examples) — `docker compose up` brings up Jaeger + an agent that exercises the full failover stack. (Swap in `BedrockAdapter` for the enterprise version.)

## Is this for you?

**Adopt when** — you're on AWS and your model access is via Bedrock (Anthropic Claude, Meta Llama, Mistral, Cohere, Amazon Titan, or other Bedrock-hosted models). You want one stack across direct providers + Bedrock — same `ResilientClient`, same typed errors, same observability — without writing AWS-specific glue throughout your codebase.
**Skip when** — you call Bedrock directly with `boto3` in a simple single-model app and don't need failover / observability / vendor-neutral wiring. The official AWS SDKs have a wider surface (image generation, RAG knowledge bases, agents-for-bedrock features).

## Install

```bash
pip install keel-llm-adapter-bedrock     # pulls in keel-llm-protocol + httpx + botocore (for SigV4)
```

> **Note on dependencies**: we use `botocore.auth.SigV4Auth` for request signing but send via `httpx`, not boto3. This keeps the [full] OTel story working — `opentelemetry-instrumentation-httpx` (from `keel-llm-otel[full]`) sees each Bedrock call as a real distributed-trace span with `traceparent` propagation.

## Use

```python
import os
import asyncio
from keel_llm_adapter_bedrock import BedrockAdapter
from keel_llm_protocol import user
from keel_llm_protocol.errors import RateLimitError, AdapterError

adapter = BedrockAdapter(
    model="anthropic.claude-3-5-sonnet-20241022-v2:0",
    region="us-east-1",
    # Credentials: explicit kwargs OR env vars (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN).
    aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"],
    aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"],
)

async def main() -> None:
    try:
        resp = await adapter.generate([user("Explain Bedrock briefly.")])
        print(resp.text, resp.usage.total_tokens, resp.finish_reason)
    except RateLimitError as e:
        # Bedrock throttling — healthy but rate-limited; the keel-llm-reliability
        # ResilientClient defers to the rate-limiter instead of tripping the breaker.
        ...
    except AdapterError as e:
        if e.retryable:
            ...      # transient / timeout — retry or fail over
        else:
            raise    # auth / bad-request / context / content — fail fast

asyncio.run(main())
```

### In a multi-provider `ResilientClient` (the enterprise on-ramp)

The reason most enterprise teams reach for this package — mix Bedrock with direct providers in one `ResilientClient`:

```python
from keel_llm_reliability import ResilientClient, Request
from keel_llm_adapter_bedrock import BedrockAdapter
from keel_llm_adapter_openai import OpenAIAdapter
from keel_llm_protocol import user

client = ResilientClient([
    BedrockAdapter(model="anthropic.claude-3-5-sonnet-20241022-v2:0", region="us-east-1"),
    OpenAIAdapter(model="gpt-4o-mini", api_key=os.environ["OPENAI_API_KEY"], provider="openai"),
])

result = await client.failover(Request(messages=[user("Hi")]))
# Bedrock throttles → fails over to OpenAI. Same agent code; same Attempt trail.
```

### Tools

`BedrockAdapter` implements `ToolCallingModelAdapter`. The Converse API's `toolConfig` + `toolUse`/`toolResult` blocks are mapped to / from `ToolSpec` + `ToolCall`:

```python
from keel_llm_protocol import ToolSpec
weather = ToolSpec(
    name="get_weather",
    description="Get weather for a city.",
    parameters={"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
)
resp = await adapter.generate_with_tools([user("Weather in SF?")], [weather])
for call in resp.tool_calls:
    print(call.name, call.arguments)
```

## How Bedrock diverges — and where it's handled

| Bedrock divergence | Absorbed by |
|---|---|
| SigV4 request signing | adapter signs every request via `botocore.auth.SigV4Auth` (boto3 client not required) |
| `messages` / `system` / `content blocks` shape (Converse) | adapter rebuilds from `Message`s; `system` lifted to top-level |
| `toolUse` / `toolResult` content blocks | mapped to / from `ToolCall` + `tool` role messages |
| `toolConfig.toolChoice` (`auto` / `any` / `tool`) | mapped from `ToolChoice` |
| Required `maxTokens` | `default_max_tokens` (4096) supplied when unset |
| AWS error envelope (`__type` + `message`) | mapped to the standard error taxonomy below |
| `stopReason` (`end_turn` / `max_tokens` / `tool_use` / `content_filtered` / `guardrail_intervened` / …) | mapped to `FinishReason` (unmapped → `unknown`) |

## Error mapping

| Bedrock exception / status | Raised |
|---|---|
| `ThrottlingException` / `ServiceQuotaExceededException` / 429 | `RateLimitError(retry_after=…)` — retryable |
| `AccessDeniedException` / `UnrecognizedClientException` / `InvalidSignatureException` / 401 / 403 | `AuthenticationError` — terminal *(persistent model-level; records a breaker failure)* |
| `ValidationException` with context-length keywords | `ContextLengthError` — terminal |
| `ValidationException` (other) / `ResourceNotFoundException` | `BadRequestError` — terminal |
| `ServiceUnavailableException` / `InternalServerException` / `ModelTimeoutException` / `ModelErrorException` / 5xx | `TransientError` — retryable |
| HTTP timeout / connection error | `AdapterTimeoutError` / `TransientError` — retryable |

## Known limitation

**Streaming via `converse-stream` is not in v0.1.0.** Bedrock streams use AWS event-stream binary framing (not SSE), which needs its own parser. Planned for 0.2; `capabilities` correctly excludes `"streaming"` so consumers can route around it via `capabilities` introspection without surprise.

## Status

`0.1.0` — `0.x` while the API stabilizes through year one (breaking changes possible at minor bumps, documented in the CHANGELOG; **pin exact versions**).

## The Keel toolkit

Composable, vendor-neutral LLM reliability libraries on PyPI:
[`keel-llm-reliability`](https://pypi.org/project/keel-llm-reliability/) · [`keel-llm-protocol`](https://pypi.org/project/keel-llm-protocol/) · [`keel-llm-adapter-openai`](https://pypi.org/project/keel-llm-adapter-openai/) · [`keel-llm-adapter-anthropic`](https://pypi.org/project/keel-llm-adapter-anthropic/) · [`keel-llm-adapter-google`](https://pypi.org/project/keel-llm-adapter-google/) · [`keel-llm-adapter-bedrock`](https://pypi.org/project/keel-llm-adapter-bedrock/) · [`keel-circuit-breaker`](https://pypi.org/project/keel-circuit-breaker/) · [`keel-llm-otel`](https://pypi.org/project/keel-llm-otel/)

MIT licensed.
