Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: AWS Bedrock provider #318

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

mkranjac
Copy link

Added new model provider as a separate crate because it depends on AWS SDK.
Relates to issue

Important:

Access to Amazon Bedrock foundation models isn't granted by default. You can request access, or modify access, to foundation models only by using the Amazon Bedrock console.

This is what you would get if you ran all example programs:

agent_with_bedrock.txt
document_with_bedrock.txt
embedding_with_bedrock.txt
extractor_with_bedrock.txt
image_with_bedrock.txt
rag_with_bedrock.txt

@0xMochan
Copy link
Contributor

0xMochan commented Feb 24, 2025

Hey, thanks for this PR that tackles #317 🎉, we'll get a review going on this soon but in the meantime, do compare to the other model providers to ensure you've hit all the right traits and patterns!

Also, do make sure you pass clippy and fmt for our CI to pass as well!

Copy link
Contributor

@0xMochan 0xMochan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, great work on this PR. Ik working with some of the AWS libraries and tech requires some boilerplate with their libraries so it's pretty cool to have some integrations with. Pointed out some things we do with our other providers that should be matched here.

Feel free to join our discord if you'd like to chat with us more conversationally about these things otherwise you can leave questions or other things here!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't forget tests! These are usually just mocking the response and testing the serializsation/deserialization aptly the Message <-> Message conversions!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review!
I changed things as mentioned in PR comments (had to do newtype wrapping because I ran into orphan rule problems)

Will add more tests soon.

)),
}?;

if let Some(tool_use) = choice.iter().find_map(|content| match content {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@0xMochan can you please explain why I have to "pick" tool call here in order to get model to execute tool ?

I am aware things will change around tool calls soon in order to support tool results get back to model so I didn't want to poke too much things around here...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you don't have to pick a tool call here, you can process all of the tool calls and convert them to generic forms of tool calls -- the main model can handle it. Something like:

let mut content = content
    .iter()
    .map(|c| match c {
        AssistantContent::Text { text } => completion::AssistantContent::text(text),
        AssistantContent::Refusal { refusal } => {
            completion::AssistantContent::text(refusal)
        }
    })
    .collect::<Vec<_>>();

content.extend(
    tool_calls
        .iter()
        .map(|call| {
            completion::AssistantContent::tool_call(
                &call.function.name,
                &call.function.name,
                call.function.arguments.clone(),
            )
        })
        .collect::<Vec<_>>(),
);
Ok(content)

Currently, rig does not actually call multiple tools, but I have a branch that I'm currently working on to enable that behavior. Returning multiple tools in the general completion response will pass that to the agent completion code which will then dispatch tool-calling.

@edisontim
Copy link
Contributor

Hey! Really nice feature here! Really needed on my end so it's nice to see a PR already open for it :)

@edisontim
Copy link
Contributor

I think there might be a better way of implementing this btw. The Rust types of the bedrock library are very strict and limiting for a lot of features (e.g. prompt caching) when using the Converse API, but if this crate can instead just act as a provider over an existing agent, the wrapper object can just use the Invoke api which takes a json body and allows any field there

@edisontim
Copy link
Contributor

I've made the changes on this PR which also has some MCP tools integration: #328. It seems cleaner that way, what do you guys think?

@mkranjac
Copy link
Author

I think there might be a better way of implementing this btw. The Rust types of the bedrock library are very strict and limiting for a lot of features (e.g. prompt caching) when using the Converse API, but if this crate can instead just act as a provider over an existing agent, the wrapper object can just use the Invoke api which takes a json body and allows any field there

Hi, thank you for helping with this PR!
I agree with you that using directly “Invoke API” has maximum flexibility over Converse API, however with invoke API changing model requires changes on both request and response and that is something that I wanted to avoid.
Given the fact that AWS has huge numbers of models, that could be tiresome to write manually and more error prone.
Even AWS docs strongly suggest using Converse API over Invoke API for that reasons.

I would leave this decision for core maintainers to decide. @0xMochan

@mkranjac
Copy link
Author

I've made the changes on this PR which also has some MCP tools integration: #328. It seems cleaner that way, what do you guys think?

This PR include some breaking changes to core package, I think that deserves it's own issue and PR

@edisontim
Copy link
Contributor

however with invoke API changing model requires changes on both request and response and that is something that I wanted to avoid.

What do you mean by this? With the wrapper I made you can use one of the already existing CompletionModels of the already implemented providers, build_completion then try_into the response and the return type will be the same as the base provider

@cvauclair cvauclair added this to the 2025-03-17 milestone Mar 3, 2025
@cvauclair cvauclair requested a review from marieaurore123 March 3, 2025 16:46
@mkranjac mkranjac force-pushed the feat/aws-bedrock-provider branch 3 times, most recently from bdebb44 to 2e17582 Compare March 4, 2025 18:17
Added new model provider
@mkranjac mkranjac force-pushed the feat/aws-bedrock-provider branch from 2e17582 to 6ba23e3 Compare March 7, 2025 18:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants