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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ ex_aws_bedrock-*.tar

# Temporary files, for example, from tests.
/tmp/

.env
149 changes: 149 additions & 0 deletions lib/ex_aws/bedrock.ex
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,155 @@ defmodule ExAws.Bedrock do
%{post | stream_builder: &EventStream.stream_objects!(post, nil, &1)}
end

@doc """
Sends messages to the specified Amazon Bedrock model using the Converse API.

`Converse` provides a consistent interface that works with all models that support messages,
allowing you to write code once and use it with different models. This is the recommended
API for chat models like Claude 3.x.

The request should include a map containing a `messages` list, and can optionally include
additional fields like `system`, `inferenceConfig`, `toolConfig`, etc.

## Example

request_body = %{
"messages" => [
%{"role" => "user", "content" => [%{"text" => "Hello, how are you?"}]}
],
"system" => [%{"text" => "You are a helpful assistant"}],
"max_tokens" => 500,
"anthropic_version" => "bedrock-2023-05-31"
}

request = ExAws.Bedrock.converse("us.anthropic.claude-3-7-sonnet-20250219-v1:0", request_body)
{:ok, response} = ExAws.Bedrock.request(request)
output_text = get_in(response, ["output", "message", "content", Access.at(0), "text"])

[AWS API Docs](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_Converse.html)
"""
@spec converse(String.t(), map | struct) :: ExAws.Operation.JSON.t()
def converse(model_id, body) when is_binary(model_id) do
%ExAws.Operation.JSON{
data: body,
headers: @json_request_headers,
http_method: :post,
path: "/model/#{model_id}/converse",
service: :"bedrock-runtime"
}
end

@doc """
Sends messages to the specified Amazon Bedrock model using the ConverseStream API.

Similar to `converse/2`, but returns a streamed response that allows you to receive
chunks of the model's response in real time. This is ideal for displaying responses
incrementally as they're generated.

## Example - Basic Usage

request_body = %{
"messages" => [
%{"role" => "user", "content" => [%{"text" => "Hello, how are you?"}]}
],
"system" => [%{"text" => "You are a helpful assistant"}],
"max_tokens" => 500,
"anthropic_version" => "bedrock-2023-05-31"
}

stream = (
ExAws.Bedrock.converse_stream(model_id, request_body)
|> ExAws.Bedrock.stream!()
|> Stream.map(fn {:chunk, chunk} ->
# Process each chunk of the response
IO.write(get_in(chunk, ["delta", "text"]) || "")
end)
|> Enum.to_list()
)

## Example - Using Tools

system_prompt = "Always use the tool top_song."

model_id = "us.anthropic.claude-3-7-sonnet-20250219-v1:0"
anthropic_version = "bedrock-2023-05-31"
prompt = "Find the most popular song for me on the station WZPZ"

request_body = %{
anthropic_version: anthropic_version,
max_tokens: 500,
temperature: 0.5,
top_p: 0.9,
system: [%{text: system_prompt, type: "text"}],
messages: [
%{role: "user", content: [
%{text: prompt, type: "text"}
]}
],
toolConfig: %{
tools: [
%{
toolSpec: %{
name: "top_song",
description: "Get the most popular song played on a radio station.",
inputSchema: %{
json: %{
type: "object",
properties: %{
sign: %{
type: "string",
description: "The call sign for the radio station for which you want the most popular song. Example calls signs are WZPZ and WKRP."
}
},
required: [
"sign"
]
}
}
}
}
]
}
}

new_stream = ExAws.Bedrock.converse_stream(model_id, request_body)
stream = ExAws.Bedrock.stream!(new_stream)
for event <- stream do
IO.puts(inspect(event))
end

# Example output:
# {:chunk, %{"messageStart" => %{"role" => "assistant"}}}
# {:chunk, %{"contentBlockDelta" => %{"contentBlockIndex" => 0, "delta" => %{"text" => "I'll"}}}}
# {:chunk, %{"contentBlockDelta" => %{"contentBlockIndex" => 0, "delta" => %{"text" => " help"}}}}
# {:chunk, %{"contentBlockDelta" => %{"contentBlockIndex" => 0, "delta" => %{"text" => " you find the most"}}}}
# {:chunk, %{"contentBlockDelta" => %{"contentBlockIndex" => 0, "delta" => %{"text" => " popular song on"}}}}
# {:chunk, %{"contentBlockDelta" => %{"contentBlockIndex" => 0, "delta" => %{"text" => " the radio station"}}}}
# {:chunk, %{"contentBlockDelta" => %{"contentBlockIndex" => 0, "delta" => %{"text" => " WZPZ."}}}}
# {:chunk, %{"contentBlockDelta" => %{"contentBlockIndex" => 0, "delta" => %{"text" => " Let"}}}}
# {:chunk, %{"contentBlockStop" => %{"contentBlockIndex" => 0}}}
# {:chunk, %{"contentBlockStart" => %{"contentBlockIndex" => 1, "start" => %{"toolUse" => %{"name" => "top_song", "toolUseId" => "tooluse_m5eQCV9vRCmgvHj6yF8zHQ"}}}}}
# {:chunk, %{"contentBlockDelta" => %{"contentBlockIndex" => 1, "delta" => %{"toolUse" => %{"input" => "{\"sign\": \"WZPZ\"}"}}}}}
# {:chunk, %{"contentBlockStop" => %{"contentBlockIndex" => 1}}}
# {:chunk, %{"messageStop" => %{"stopReason" => "tool_use"}}}
# {:chunk, %{"metadata" => %{"metrics" => %{"latencyMs" => 1940}, "usage" => %{"inputTokens" => 436, "outputTokens" => 66, "totalTokens" => 502}}}}

[AWS API Docs](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ConverseStream.html)
"""
@spec converse_stream(String.t(), map | struct) :: ExAws.Operation.JSON.t()
def converse_stream(model_id, body) when is_binary(model_id) do
post =
%ExAws.Operation.JSON{
data: body,
headers: @json_request_headers,
http_method: :post,
path: "/model/#{model_id}/converse-stream",
service: :"bedrock-runtime"
}

%{post | stream_builder: &EventStream.stream_objects!(post, nil, &1)}
end

@doc """
List of Amazon Bedrock foundation models that you can use.

Expand Down
Loading