mod request;
mod response;
use serde_json::{Value, json};
const DEFAULT_REGION: &str = "us-east-1";
#[derive(Debug, Clone)]
pub struct BedrockAdapter {
region: String,
model_id: String,
api_url: String,
}
impl BedrockAdapter {
pub fn new(model_id: impl Into<String>) -> Self {
let model_id = model_id.into();
let region = std::env::var("AWS_REGION").unwrap_or_else(|_| DEFAULT_REGION.to_string());
let api_url = Self::build_url(®ion, &model_id);
Self {
region,
model_id,
api_url,
}
}
pub fn with_region(model_id: impl Into<String>, region: impl Into<String>) -> Self {
let model_id = model_id.into();
let region = region.into();
let api_url = Self::build_url(®ion, &model_id);
Self {
region,
model_id,
api_url,
}
}
fn build_url(region: &str, model_id: &str) -> String {
format!("https://bedrock-runtime.{region}.amazonaws.com/model/{model_id}/invoke")
}
pub fn region(&self) -> &str {
&self.region
}
pub fn model_id(&self) -> &str {
&self.model_id
}
#[allow(dead_code)]
fn sigv4_headers(&self, _payload: &[u8]) -> Vec<(String, String)> {
let _access_key = std::env::var("AWS_ACCESS_KEY_ID").unwrap_or_default();
let _secret_key = std::env::var("AWS_SECRET_ACCESS_KEY").unwrap_or_default();
let _session_token = std::env::var("AWS_SESSION_TOKEN").ok();
vec![
("Content-Type".into(), "application/json".into()),
("Accept".into(), "application/json".into()),
]
}
}
#[async_trait::async_trait]
impl super::base::ProviderAdapter for BedrockAdapter {
fn provider_name(&self) -> &str {
"bedrock"
}
fn convert_request(&self, mut payload: Value) -> Value {
payload
.as_object_mut()
.map(|obj| obj.remove("_reasoning_effort"));
request::extract_system(&mut payload);
request::convert_tools(&mut payload);
request::convert_tool_messages(&mut payload);
request::ensure_max_tokens(&mut payload);
if let Some(obj) = payload.as_object_mut() {
obj.remove("model");
obj.remove("n");
obj.remove("frequency_penalty");
obj.remove("presence_penalty");
obj.remove("logprobs");
obj.remove("stream");
}
payload["anthropic_version"] = json!("bedrock-2023-05-31");
payload
}
fn convert_response(&self, response: Value) -> Value {
response::response_to_chat_completions(response, &self.model_id)
}
fn api_url(&self) -> &str {
&self.api_url
}
fn extra_headers(&self) -> Vec<(String, String)> {
vec![
("Content-Type".into(), "application/json".into()),
("Accept".into(), "application/json".into()),
]
}
}
#[cfg(test)]
mod tests;