Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions docs/mint.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,10 @@
},
{
"group": "Incidents",
"pages": ["incidents/overview", "incidents/facets"]
"pages": [
"incidents/overview",
"incidents/facets"
]
},
{
"group": "Workflow Automation",
Expand Down Expand Up @@ -150,6 +153,7 @@
"providers/documentation/anthropic-provider",
"providers/documentation/appdynamics-provider",
"providers/documentation/asana-provider",
"providers/documentation/bedrock-provider",
"providers/documentation/s3-provider",
"providers/documentation/argocd-provider",
"providers/documentation/auth0-provider",
Expand Down Expand Up @@ -322,14 +326,19 @@
},
{
"group": "Local LLM",
"pages": ["deployment/local-llm/keep-with-litellm"]
"pages": [
"deployment/local-llm/keep-with-litellm"
]
},
"deployment/stress-testing"
]
},
{
"group": "Development",
"pages": ["development/getting-started", "development/external-url"]
"pages": [
"development/getting-started",
"development/external-url"
]
},
{
"group": "Keep CLI",
Expand Down Expand Up @@ -368,7 +377,10 @@
"cli/commands/workflow-runs",
{
"group": "keep workflow runs",
"pages": ["cli/commands/runs-logs", "cli/commands/runs-list"]
"pages": [
"cli/commands/runs-logs",
"cli/commands/runs-list"
]
}
]
},
Expand Down
32 changes: 32 additions & 0 deletions docs/providers/documentation/bedrock-provider.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
title: "AWS Bedrock Provider"
description: "The AWS Bedrock Provider allows querying foundational models (Claude, Llama, Mistral, Titan) via AWS."
---
import AutoGeneratedSnippet from '/snippets/providers/bedrock-snippet-autogenerated.mdx';

<Tip>
The AWS Bedrock Provider supports querying foundational models from Anthropic,
Meta, Mistral, Cohere, and Amazon through your AWS account.
</Tip>

<AutoGeneratedSnippet />

## Connecting with the Provider

To connect to AWS Bedrock, you need AWS credentials with Bedrock access:

1. Ensure AWS Bedrock is enabled in your AWS account for the desired region.
2. Create an IAM user or role with the `bedrock:InvokeModel` permission.
3. Provide the **AWS region**, **access key**, and **secret access key** in the provider configuration.

Alternatively, if running on EC2/ECS/EKS, you can leave the access key fields empty to use the instance's IAM role automatically.

## Supported Models

| Family | Example Model ID |
|--------|-----------------|
| Anthropic Claude | `anthropic.claude-3-haiku-20240307-v1:0` |
| Meta Llama | `meta.llama3-8b-instruct-v1:0` |
| Mistral | `mistral.mistral-7b-instruct-v0:2` |
| Cohere Command | `cohere.command-text-v14` |
| Amazon Titan | `amazon.titan-text-express-v1` |
25 changes: 25 additions & 0 deletions docs/snippets/providers/bedrock-snippet-autogenerated.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{/* This snippet is automatically generated using scripts/docs_render_provider_snippets.py
Do not edit it manually, as it will be overwritten */}

## Authentication
This provider requires authentication.
- **region**: AWS region (e.g. us-east-1) (required: True, sensitive: False)
- **access_key**: AWS access key (leave empty for IAM role) (required: False, sensitive: True)
- **secret_access_key**: AWS secret access key (leave empty for IAM role) (required: False, sensitive: True)


## In workflows

This provider can be used in workflows.


As "step" to query data, example:
```yaml
steps:
- name: Query bedrock
provider: bedrock
config: "{{ provider.my_provider_name }}"
with:
prompt: "Classify this alert severity"
model: "anthropic.claude-3-haiku-20240307-v1:0"
```
Binary file added keep-ui/public/icons/bedrock-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions keep/providers/bedrock_provider/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Bedrock provider
183 changes: 183 additions & 0 deletions keep/providers/bedrock_provider/bedrock_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import json
import dataclasses

import boto3
import pydantic
from botocore.exceptions import ClientError, BotoCoreError

from keep.contextmanager.contextmanager import ContextManager
from keep.exceptions.provider_exception import ProviderException
from keep.providers.base.base_provider import BaseProvider
from keep.providers.models.provider_config import ProviderConfig


@pydantic.dataclasses.dataclass
class BedrockProviderAuthConfig:
region: str = dataclasses.field(
metadata={
"required": True,
"description": "AWS region (e.g. us-east-1)",
"sensitive": False,
"hint": "e.g. us-east-1",
},
)
access_key: str = dataclasses.field(
default=None,
metadata={
"required": False,
"description": "AWS access key (leave empty for IAM role)",
"sensitive": True,
},
)
secret_access_key: str = dataclasses.field(
default=None,
metadata={
"required": False,
"description": "AWS secret access key (leave empty for IAM role)",
"sensitive": True,
},
)


class BedrockProvider(BaseProvider):
PROVIDER_DISPLAY_NAME = "AWS Bedrock"
PROVIDER_CATEGORY = ["AI"]

def __init__(
self, context_manager: ContextManager, provider_id: str, config: ProviderConfig
):
super().__init__(context_manager, provider_id, config)
self._client = None

def validate_config(self):
self.authentication_config = BedrockProviderAuthConfig(
**self.config.authentication
)

def dispose(self):
pass

def validate_scopes(self) -> dict[str, bool | str]:
return {}

def _get_client(self):
if self._client is not None:
return self._client

kwargs = {"region_name": self.authentication_config.region}
if self.authentication_config.access_key:
kwargs["aws_access_key_id"] = self.authentication_config.access_key
if self.authentication_config.secret_access_key:
kwargs["aws_secret_access_key"] = self.authentication_config.secret_access_key

self._client = boto3.client("bedrock-runtime", **kwargs)
return self._client

def _query(
self,
prompt: str,
model: str = "anthropic.claude-3-haiku-20240307-v1:0",
max_tokens: int = 1024,
structured_output_format: dict | None = None,
) -> dict:
client = self._get_client()
model_id = model

# Build request body based on model family
if "anthropic.claude" in model_id:
body = {
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": max_tokens,
"messages": [{"role": "user", "content": prompt}],
}
if structured_output_format:
schema = structured_output_format.get("json_schema", {})
body["system"] = (
f"Respond with valid JSON matching this schema: "
f"{json.dumps(schema)}"
)
elif "meta.llama" in model_id:
body = {"prompt": prompt, "max_gen_len": max_tokens}
elif "mistral" in model_id:
body = {"prompt": f"<s>[INST]{prompt}[/INST]", "max_tokens": max_tokens}
elif "cohere.command" in model_id:
body = {"prompt": prompt, "max_tokens": max_tokens}
else:
# Amazon Titan and other models
body = {
"inputText": prompt,
"textGenerationConfig": {
"maxTokenCount": max_tokens,
"temperature": 0.0,
},
}

try:
response = client.invoke_model(
modelId=model_id,
contentType="application/json",
accept="application/json",
body=json.dumps(body),
)
except ClientError as e:
raise ProviderException(
f"Bedrock error [{e.response['Error']['Code']}]: "
f"{e.response['Error']['Message']}"
)
except BotoCoreError as e:
raise ProviderException(f"Bedrock error: {str(e)}")

raw = json.loads(response["body"].read())

# Parse response based on model family
if "anthropic.claude" in model_id:
text = raw.get("content", [{}])[0].get("text", "")
elif "meta.llama" in model_id:
text = raw.get("generation", "")
elif "mistral" in model_id:
text = raw.get("outputs", [{}])[0].get("text", "")
elif "cohere.command" in model_id:
text = raw.get("generations", [{}])[0].get("text", "")
else:
text = raw.get("results", [{}])[0].get("outputText", "")

if structured_output_format:
try:
text = json.loads(text)
except (json.JSONDecodeError, TypeError):
pass

return {"response": text}


if __name__ == "__main__":
import os
import logging

logging.basicConfig(level=logging.DEBUG, handlers=[logging.StreamHandler()])
context_manager = ContextManager(
tenant_id="singletenant",
workflow_id="test",
)

config = ProviderConfig(
description="AWS Bedrock Provider",
authentication={
"region": os.environ.get("AWS_REGION", "us-east-1"),
"access_key": os.environ.get("AWS_ACCESS_KEY_ID"),
"secret_access_key": os.environ.get("AWS_SECRET_ACCESS_KEY"),
},
)

provider = BedrockProvider(
context_manager=context_manager,
provider_id="bedrock_provider",
config=config,
)

print(
provider.query(
prompt="Classify this alert: Clients are panicking, nothing works.",
model="anthropic.claude-3-haiku-20240307-v1:0",
)
)
1 change: 1 addition & 0 deletions tests/providers/bedrock_provider/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Bedrock provider tests
Loading
Loading