AWS Bedrock
This page leaves you with an AwsBedrock client that sends requests to Amazon Web Services (AWS) Bedrock through the Converse application programming interface (API), and uses it in an indexing pipeline and in an agent with tools.
cargo add swiftide@0.34 --features aws-bedrockcargo add tokio --features macros,rt-multi-threadlet aws_bedrock = integrations::aws_bedrock_v2::AwsBedrock::builder() .default_prompt_model("global.anthropic.claude-haiku-4-5-20251001-v1:0") .build()?;
let memory_storage = MemoryStorage::default();Source: examples/aws_bedrock.rs#L26-L30
Client
Section titled “Client”AwsBedrock lives in swiftide::integrations::aws_bedrock_v2 behind the aws-bedrock feature flag. Since 0.33 it uses Bedrock’s Converse and ConverseStream APIs, so one client covers every model family that Converse supports. Upgrading covers the move from the old module.
default_prompt_model takes a model ID or an Amazon Resource Name (ARN). The global. prefix in the example is a cross-region inference profile ID, which Bedrock accepts in place of a model ID. Without a model, the first call fails with a permanent Model not set error. Building the client does not check it.
The client implements ChatCompletion (complete and complete_stream), SimplePrompt and DynStructuredPrompt. It does not implement EmbeddingModel, so pair it with FastEmbed or another embedder for vectors.
Credentials and region
Section titled “Credentials and region”Without .client(..), the builder loads the AWS configuration with aws_config::load_from_env. That is the AWS SDK (software development kit) credential chain: environment variables, shared profiles, single sign-on (SSO) and IAM (Identity and Access Management) roles. Credentials from aws login (AWS Console sign-in) are not supported; use a profile, SSO or environment variables, or pass your own client. The region comes from the same chain, for example AWS_REGION or your profile. Your account also needs access to the model you name.
The builder runs that loading step inside tokio::task::block_in_place when you call build. So build needs a multi-threaded Tokio runtime. #[tokio::main] gives you one. #[tokio::test] defaults to a current-thread runtime, where build panics; use #[tokio::test(flavor = "multi_thread")] there.
To control the configuration yourself, build an aws_sdk_bedrockruntime::Client and pass it to AwsBedrockBuilder::client. Swiftide does not re-export the SDK, so add aws-config and aws-sdk-bedrockruntime to your own crate at the major versions Swiftide uses (1.x). A client you pass in also skips the blocking load, so build also works on a current-thread runtime.
Pipeline summaries
Section titled “Pipeline summaries”indexing::Pipeline::from_loader(FileLoader::new("./README.md")) .log_nodes() .then_chunk(transformers::ChunkMarkdown::from_chunk_range(100..512)) .then(transformers::MetadataSummary::new(aws_bedrock.clone())) .then_store_with(memory_storage.clone()) .log_all() .run() .await?;Source: examples/aws_bedrock.rs#L34-L41
MetadataSummary takes any SimplePrompt, so AwsBedrock works where other examples use OpenAI. Each chunk costs one Converse call, so wrap the client in LanguageModelWithBackOff for large inputs.
from_chunk_range(100..512) drops chunks shorter than 100 characters, so short README sections never reach the model or the storage. The upper bound of the range is exclusive.
Agents with Claude
Section titled “Agents with Claude”An agent needs the swiftide-agents feature flag, and tool arguments need schemars and serde in your crate:
cargo add swiftide@0.34 --features aws-bedrock,swiftide-agentscargo add schemars@1 serde anyhow tokio --features serde/derive,tokio/macros,tokio/rt-multi-threadDefine the tools:
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]struct FormatTimestampRequest { /// Prefix to prepend to the timestamp. prefix: String, /// Timestamp to format. timestamp: String,}
#[swiftide::tool(description = "Get the current UTC date and time in RFC3339 format")]async fn current_utc_time(context: &dyn AgentContext) -> Result<ToolOutput, ToolError> { let command_output = context .executor() .exec_cmd(&Command::shell("date -u +\"%Y-%m-%dT%H:%M:%SZ\"")) .await?;
Ok(command_output.into())}
#[swiftide::tool( description = "Format a timestamp with a caller-provided prefix", param(name = "request", description = "Timestamp formatting input"))]async fn format_timestamp( _context: &dyn AgentContext, request: FormatTimestampRequest,) -> Result<ToolOutput, ToolError> { Ok(ToolOutput::text(format!( "{}{}", request.prefix, request.timestamp )))}Source: examples/aws_bedrock_agent.rs#L23-L53
The use lines sit above the excerpt in the linked example source. They bring in anyhow::Result, schemars::JsonSchema, serde’s Serialize and Deserialize, and from Swiftide agents, ToolOutput, ToolError, AgentContext, Command and aws_bedrock_v2::AwsBedrock.
Build the client and the agent, then run it:
let bedrock = AwsBedrock::builder() .default_prompt_model("global.anthropic.claude-sonnet-4-6") .build()?;Source: examples/aws_bedrock_agent.rs#L61-L63
let mut agent = agents::Agent::builder() .llm(&bedrock) .tools(vec![current_utc_time(), format_timestamp()]) .on_new_message(|_, msg| { let rendered = msg.to_string(); Box::pin(async move { println!("{rendered}"); Ok(()) }) }) .limit(6) .build()?;Source: examples/aws_bedrock_agent.rs#L67-L78
agent .query( "Call current_utc_time once. Then call format_timestamp with prefix \"UTC now: \" and \ that timestamp. After that, report the formatted result and stop.", ) .await?;Source: examples/aws_bedrock_agent.rs#L82-L87
current_utc_time runs date through the tool executor of the agent’s context, which is local by default. format_timestamp takes a struct argument, and its doc comments become field descriptions in the tool’s JavaScript Object Notation (JSON) Schema. The loop counter behind limit(6) resets when the agent stops, so a second query gets six loops again.
On the way to Bedrock, Swiftide turns the request into Converse messages:
- Every tool is sent with automatic tool choice and strict schema validation. Set
tool_stricttofalseto turn validation off. - Every system message becomes a Converse system block.
- Adjacent tool outputs go back together in one user message.
- Each
ChatMessage::Reasoningin the message history becomes its own assistant message, sent before the assistant text or tool use it belongs to. Reasoning without both text and a signature, such as redacted reasoning, is dropped.
On the way back, the client returns Claude’s reasoning in the reasoning field of ChatCompletionResponse. The agent then adds it to the message history as ChatMessage::Reasoning.
Options
Section titled “Options”Set defaults with AwsBedrockBuilder::default_options, or change them later with options_mut. AWS Bedrock settings lists the fields, the merge rules and the reasoning_effort beta header. A few details only matter here:
| Field | Effect |
|---|---|
prompt_model | model ID or ARN; default_prompt_model sets it |
max_tokens, temperature, top_p, stop_sequences | unset fields are not sent |
additional_model_response_field_paths | JSON Pointer paths of model-specific response fields to return |
additional_model_request_fields takes an aws_smithy_types::Document. Neither Swiftide nor the SDK re-exports that type, so add aws-smithy-types (1.x) to your crate to set it.
ReasoningEffort::Max is meant for Claude Opus 4.6 with adaptive thinking. Set thinking.type to adaptive through additional_model_request_fields to use it.
Reasoning and beta headers
Section titled “Reasoning and beta headers”The swiftide examples do not set these fields, so this section names them without code. Build the value with Options::builder() and pass the result of build() to default_options. The setters take the value itself, not an Option:
reasoning_efforttakesswiftide::integrations::aws_bedrock_v2::ReasoningEffort(Low,Medium,HighorMax). Swiftide writes it tooutput_config.effortin the model request fields.tool_stricttakes abool.falsesends tools without strict schema validation.anthropic_betatakes a list of beta header names. Swiftide adds them to theanthropic_betaarray in the model request fields, without duplicates.additional_model_request_fieldstakes theDocumentthat the two fields above are merged into.
See OptionsBuilder (AWS Bedrock) for every setter.
Structured prompts
Section titled “Structured prompts”AwsBedrock implements DynStructuredPrompt, so structured_prompt::<T> from StructuredPrompt works through the blanket implementation. Swiftide sends the schema as a Converse output configuration with a JSON Schema text format, then parses the reply into JSON. Whether a model accepts that format is up to Bedrock. Structured output shows the call.
Errors and usage
Section titled “Errors and usage”Throttling, unavailable services, models that are not ready, model timeouts, internal server errors, network failures, timeouts and unreadable responses are transient. For complete_stream, a model stream error is transient too. An error in the middle of a stream is transient only for throttling, unavailable services, internal server errors and model stream errors. Everything else is permanent. A response that stops because the context window is full, with no text, tool calls or reasoning, returns ContextLengthExceeded. Wrap the client in LanguageModelWithBackOff to retry transient errors; see Errors, backoff, usage and tokens. The wrapper implements SimplePrompt and ChatCompletion but not DynStructuredPrompt, so a wrapped client loses structured_prompt.
on_usage and on_usage_async receive the token Usage of each call. Its cached_tokens holds the cache-read input tokens. When there are none, it holds the cache-write tokens instead, so a first call that writes the prompt cache also reports cached input. Both callbacks run before the call returns; an Err from either fails the call.