diff --git a/core/plugin_registry.go b/core/plugin_registry.go index f926362c..6d27e869 100644 --- a/core/plugin_registry.go +++ b/core/plugin_registry.go @@ -10,6 +10,7 @@ import ( "strconv" "strings" + "github.com/danielmiessler/fabric/plugins/ai/bedrock" "github.com/danielmiessler/fabric/plugins/ai/exolab" "github.com/danielmiessler/fabric/plugins/strategy" @@ -66,6 +67,7 @@ func NewPluginRegistry(db *fsdb.Db) (ret *PluginRegistry, err error) { anthropic.NewClient(), lmstudio.NewClient(), exolab.NewClient(), + bedrock.NewClient(), ) // Add all OpenAI-compatible providers diff --git a/go.mod b/go.mod index 9542a3de..b6224023 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,8 @@ require ( github.com/anaskhan96/soup v1.2.5 github.com/anthropics/anthropic-sdk-go v1.4.0 github.com/atotto/clipboard v0.1.4 + github.com/aws/aws-sdk-go-v2/config v1.27.27 + github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.30.0 github.com/gabriel-vasile/mimetype v1.4.9 github.com/gin-gonic/gin v1.10.1 github.com/go-git/go-git/v5 v5.16.2 @@ -39,6 +41,19 @@ require ( github.com/ProtonMail/go-crypto v1.3.0 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect + github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect + github.com/aws/smithy-go v1.22.2 // indirect github.com/bytedance/sonic v1.13.3 // indirect github.com/bytedance/sonic/loader v0.2.4 // indirect github.com/cloudflare/circl v1.6.1 // indirect diff --git a/go.sum b/go.sum index 36a3d0c7..cf47c59a 100644 --- a/go.sum +++ b/go.sum @@ -31,6 +31,36 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= +github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= +github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14= +github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90= +github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg= +github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI= +github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= +github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.30.0 h1:eMOwQ8ZZK+76+08RfxeaGUtRFN6wxmD1rvqovc2kq2w= +github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.30.0/go.mod h1:0b5Rq7rUvSQFYHI1UO0zFTV/S6j6DUyuykXA80C+YOI= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ= +github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= +github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0= github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= diff --git a/plugins/ai/bedrock/bedrock.go b/plugins/ai/bedrock/bedrock.go new file mode 100644 index 00000000..362a5405 --- /dev/null +++ b/plugins/ai/bedrock/bedrock.go @@ -0,0 +1,308 @@ +// Package bedrock provides a plugin to use Amazon Bedrock models. +// Supported models are defined in the MODELS variable. +// To add additional models, append them to the MODELS array. Models must support the Converse and ConverseStream operations +// Authentication uses the AWS credential provider chain, similar.to the AWS CLI and SDKs +// https://docs.aws.amazon.com/sdkref/latest/guide/standardized-credentials.html +package bedrock + +import ( + "context" + "fmt" + + "github.com/danielmiessler/fabric/common" + "github.com/danielmiessler/fabric/plugins" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/bedrockruntime" + "github.com/aws/aws-sdk-go-v2/service/bedrockruntime/types" + + goopenai "github.com/sashabaranov/go-openai" +) + +// BedrockClient is a plugin to add support for Amazon Bedrock +type BedrockClient struct { + *plugins.PluginBase + client *bedrockruntime.Client +} + +// NewClient returns a new Bedrock plugin client +func NewClient() (ret *BedrockClient) { + vendorName := "Bedrock" + + ctx := context.TODO() + cfg, err := config.LoadDefaultConfig(ctx) + cfg.APIOptions = append(cfg.APIOptions, middleware.AddUserAgentKeyValue("aiosc", "fabric")) + + if err != nil { + fmt.Printf("Unable to load AWS Config: %s\n", err) + } + + client := bedrockruntime.NewFromConfig(cfg) + + ret = &BedrockClient{ + PluginBase: &plugins.PluginBase{ + Name: vendorName, + EnvNamePrefix: plugins.BuildEnvVariablePrefix(vendorName), + }, + client: client, + } + + return +} + +// ListModels lists the models available for use with the Bedrock plugin +func (c *BedrockClient) ListModels() ([]string, error) { + return MODELS, nil +} + +// SendStream sends the messages to the the Bedrock ConverseStream API +func (c *BedrockClient) SendStream(msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions, channel chan string) (err error) { + + messages := c.toMessages(msgs) + + var converseInput = bedrockruntime.ConverseStreamInput{ + ModelId: aws.String(opts.Model), + Messages: messages, + InferenceConfig: &types.InferenceConfiguration{ + Temperature: aws.Float32(float32(opts.Temperature)), + TopP: aws.Float32(float32(opts.TopP))}, + } + + response, err := c.client.ConverseStream(context.TODO(), &converseInput) + if err != nil { + fmt.Printf("Error conversing with Bedrock: %s\n", err) + return + } + + for event := range response.GetStream().Events() { + // Possible ConverseStream event types + // https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-call.html#conversation-inference-call-response-converse-stream + switch v := event.(type) { + + case *types.ConverseStreamOutputMemberContentBlockDelta: + text, ok := v.Value.Delta.(*types.ContentBlockDeltaMemberText) + if ok { + channel <- text.Value + } + + case *types.ConverseStreamOutputMemberMessageStop: + channel <- "\n" + close(channel) + + // Unused Events + case *types.ConverseStreamOutputMemberMessageStart, + *types.ConverseStreamOutputMemberContentBlockStart, + *types.ConverseStreamOutputMemberContentBlockStop, + *types.ConverseStreamOutputMemberMetadata: + + default: + fmt.Printf("Error: Unknown stream event type: %T\n", v) + } + } + + return nil +} + +// Send sends the messages the Bedrock Converse API +func (c *BedrockClient) Send(ctx context.Context, msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions) (ret string, err error) { + + messages := c.toMessages(msgs) + + var converseInput = bedrockruntime.ConverseInput{ + ModelId: aws.String(opts.Model), + Messages: messages, + } + response, err := c.client.Converse(ctx, &converseInput) + if err != nil { + fmt.Printf("Error conversing with Bedrock: %s\n", err) + return "", err + } + + responseText, _ := response.Output.(*types.ConverseOutputMemberMessage) + responseContentBlock := responseText.Value.Content[0] + text, _ := responseContentBlock.(*types.ContentBlockMemberText) + return text.Value, nil +} + +func (c *BedrockClient) NeedsRawMode(modelName string) bool { + return false +} + +// toMessages converts the array of input messages from the ChatCompletionMessageType to the +// Bedrock Converse Message type +// The system role messages are mapped to the user role as they contain a mix of system messages, +// pattern content and user input. +func (c *BedrockClient) toMessages(inputMessages []*goopenai.ChatCompletionMessage) (messages []types.Message) { + for _, msg := range inputMessages { + roles := map[string]types.ConversationRole{ + goopenai.ChatMessageRoleUser: types.ConversationRoleUser, + goopenai.ChatMessageRoleAssistant: types.ConversationRoleAssistant, + goopenai.ChatMessageRoleSystem: types.ConversationRoleUser, + } + + role, ok := roles[msg.Role] + if !ok { + continue + } + + message := types.Message{ + Role: role, + Content: []types.ContentBlock{&types.ContentBlockMemberText{Value: msg.Content}}, + } + messages = append(messages, message) + + } + + return +} + +var MODELS = []string{ + "amazon.nova-micro-v1:0", + "amazon.nova-lite-v1:0", + "amazon.nova-pro-v1:0", + "amazon.nova-premier-v1:0", + + "amazon.titan-tg1-large", + "amazon.titan-text-premier-v1:0", + + "amazon.titan-text-lite-v1", + "amazon.titan-text-express-v1", + + "ai21.jamba-instruct-v1:0", + "ai21.jamba-1-5-large-v1:0", + "ai21.jamba-1-5-mini-v1:0", + + "anthropic.claude-instant-v1", + "anthropic.claude-v2", + "anthropic.claude-v2:1", + "anthropic.claude-3-haiku-20240307-v1:0", + "anthropic.claude-3-sonnet-20240229-v1:0", + "anthropic.claude-3-opus-20240229-v1:0", + "anthropic.claude-3-5-haiku-20241022-v1:0", + "anthropic.claude-3-5-sonnet-20240620-v1:0", + "anthropic.claude-3-5-sonnet-20241022-v2:0", + "anthropic.claude-3-7-sonnet-20250219-v1:0", + "anthropic.claude-sonnet-4-20250514-v1:0", + "anthropic.claude-opus-4-20250514-v1:0", + + "meta.llama3-8b-instruct-v1:0", + "meta.llama3-70b-instruct-v1:0", + "meta.llama3-1-8b-instruct-v1:0", + "meta.llama3-1-70b-instruct-v1:0", + "meta.llama3-2-11b-instruct-v1:0", + "meta.llama3-2-90b-instruct-v1:0", + "meta.llama3-2-1b-instruct-v1:0", + "meta.llama3-2-3b-instruct-v1:0", + "meta.llama3-3-70b-instruct-v1:0", + "meta.llama4-scout-17b-instruct-v1:0", + "meta.llama4-maverick-17b-instruct-v1:0", + + "mistral.mistral-7b-instruct-v0:2", + "mistral.mixtral-8x7b-instruct-v0:1", + "mistral.mistral-small-2402-v1:0", + "mistral.mistral-large-2402-v1:0", + "mistral.pixtral-large-2502-v1:0", + + // Cross Region Inferences Profiles + // https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html#inference-profiles-support-system + "us.amazon.nova-lite-v1:0", + "us.amazon.nova-lite-v1:0", + "us.amazon.nova-micro-v1:0", + "us.amazon.nova-micro-v1:0", + "us.amazon.nova-premier-v1:0", + "us.amazon.nova-premier-v1:0", + "us.amazon.nova-pro-v1:0", + "us.amazon.nova-pro-v1:0", + "us.anthropic.claude-3-5-haiku-20241022-v1:0", + "us.anthropic.claude-3-5-haiku-20241022-v1:0", + "us.anthropic.claude-3-5-sonnet-20240620-v1:0", + "us.anthropic.claude-3-5-sonnet-20240620-v1:0", + "us.anthropic.claude-3-5-sonnet-20241022-v2:0", + "us.anthropic.claude-3-5-sonnet-20241022-v2:0", + "us.anthropic.claude-3-7-sonnet-20250219-v1:0", + "us.anthropic.claude-3-7-sonnet-20250219-v1:0", + "us.anthropic.claude-3-haiku-20240307-v1:0", + "us.anthropic.claude-3-haiku-20240307-v1:0", + "us.anthropic.claude-3-opus-20240229-v1:0", + "us.anthropic.claude-3-opus-20240229-v1:0", + "us.anthropic.claude-3-sonnet-20240229-v1:0", + "us.anthropic.claude-3-sonnet-20240229-v1:0", + "us.anthropic.claude-opus-4-20250514-v1:0", + "us.anthropic.claude-opus-4-20250514-v1:0", + "us.anthropic.claude-sonnet-4-20250514-v1:0", + "us.anthropic.claude-sonnet-4-20250514-v1:0", + "us.deepseek.r1-v1:0", + "us.deepseek.r1-v1:0", + "us.meta.llama3-1-405b-instruct-v1:0", + "us.meta.llama3-1-405b-instruct-v1:0", + "us.meta.llama3-1-70b-instruct-v1:0", + "us.meta.llama3-1-70b-instruct-v1:0", + "us.meta.llama3-1-8b-instruct-v1:0", + "us.meta.llama3-1-8b-instruct-v1:0", + "us.meta.llama3-2-11b-instruct-v1:0", + "us.meta.llama3-2-11b-instruct-v1:0", + "us.meta.llama3-2-1b-instruct-v1:0", + "us.meta.llama3-2-1b-instruct-v1:0", + "us.meta.llama3-2-3b-instruct-v1:0", + "us.meta.llama3-2-3b-instruct-v1:0", + "us.meta.llama3-2-90b-instruct-v1:0", + "us.meta.llama3-2-90b-instruct-v1:0", + "us.meta.llama3-3-70b-instruct-v1:0", + "us.meta.llama3-3-70b-instruct-v1:0", + "us.meta.llama4-maverick-17b-instruct-v1:0", + "us.meta.llama4-maverick-17b-instruct-v1:0", + "us.meta.llama4-scout-17b-instruct-v1:0", + "us.meta.llama4-scout-17b-instruct-v1:0", + "us.mistral.pixtral-large-2502-v1:0", + "us.mistral.pixtral-large-2502-v1:0", + "us.writer.palmyra-x4-v1:0", + "us.writer.palmyra-x4-v1:0", + "us.writer.palmyra-x5-v1:0", + "us.writer.palmyra-x5-v1:0", + "us-gov.anthropic.claude-3-5-sonnet-20240620-v1:0", + "us-gov.anthropic.claude-3-5-sonnet-20240620-v1:0", + "us-gov.anthropic.claude-3-haiku-20240307-v1:0", + "us-gov.anthropic.claude-3-haiku-20240307-v1:0", + "eu.amazon.nova-lite-v1:0", + "eu.amazon.nova-lite-v1:0", + "eu.amazon.nova-micro-v1:0", + "eu.amazon.nova-micro-v1:0", + "eu.amazon.nova-pro-v1:0", + "eu.amazon.nova-pro-v1:0", + "eu.anthropic.claude-3-5-sonnet-20240620-v1:0", + "eu.anthropic.claude-3-5-sonnet-20240620-v1:0", + "eu.anthropic.claude-3-7-sonnet-20250219-v1:0", + "eu.anthropic.claude-3-7-sonnet-20250219-v1:0", + "eu.anthropic.claude-3-haiku-20240307-v1:0", + "eu.anthropic.claude-3-haiku-20240307-v1:0", + "eu.anthropic.claude-3-sonnet-20240229-v1:0", + "eu.anthropic.claude-3-sonnet-20240229-v1:0", + "eu.anthropic.claude-sonnet-4-20250514-v1:0", + "eu.anthropic.claude-sonnet-4-20250514-v1:0", + "eu.meta.llama3-2-1b-instruct-v1:0", + "eu.meta.llama3-2-1b-instruct-v1:0", + "eu.meta.llama3-2-3b-instruct-v1:0", + "eu.meta.llama3-2-3b-instruct-v1:0", + "eu.mistral.pixtral-large-2502-v1:0", + "eu.mistral.pixtral-large-2502-v1:0", + "apac.amazon.nova-lite-v1:0", + "apac.amazon.nova-lite-v1:0", + "apac.amazon.nova-micro-v1:0", + "apac.amazon.nova-micro-v1:0", + "apac.amazon.nova-pro-v1:0", + "apac.amazon.nova-pro-v1:0", + "apac.anthropic.claude-3-5-sonnet-20240620-v1:0", + "apac.anthropic.claude-3-5-sonnet-20240620-v1:0", + "apac.anthropic.claude-3-5-sonnet-20241022-v2:0", + "apac.anthropic.claude-3-5-sonnet-20241022-v2:0", + "apac.anthropic.claude-3-7-sonnet-20250219-v1:0", + "apac.anthropic.claude-3-7-sonnet-20250219-v1:0", + "apac.anthropic.claude-3-haiku-20240307-v1:0", + "apac.anthropic.claude-3-haiku-20240307-v1:0", + "apac.anthropic.claude-3-sonnet-20240229-v1:0", + "apac.anthropic.claude-3-sonnet-20240229-v1:0", + "apac.anthropic.claude-sonnet-4-20250514-v1:0", + "apac.anthropic.claude-sonnet-4-20250514-v1:0", +}