Unified Agent + reusable Go agent core.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(bedrock): add AWS profile and session token support

Adds llm.bedrock.aws_profile and llm.bedrock.aws_session_token config
keys so users can authenticate to AWS Bedrock via:

- a named AWS profile (~/.aws/config)
- temporary session credentials (STS / assumed role / SSO)
- the standard AWS credential chain (env, instance metadata)

Changes:
- internal/llmutil: read aws_profile / aws_session_token from config
and llm.profiles; pass through to the uniai provider
- providers/uniai/client.go: add AwsProfile / AwsSessionToken fields;
call ResolveBedrockCredentials before creating the uniai client
- providers/uniai/bedrock_credentials.go: new file — resolve AWS
credentials via AWS SDK v2 before calling uniai
- go.mod: add aws-sdk-go-v2/config + credentials; bump uniai to latest

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

authored by

Cheyan
Claude Sonnet 4.6
and committed by
Lyric Wai
f60d8387 64c59957

+131 -17
+15 -1
go.mod
··· 9 9 github.com/lyricat/goutils v1.2.3 10 10 github.com/modelcontextprotocol/go-sdk v1.4.0 11 11 github.com/nickalie/go-webpbin v0.0.0-20220110095747-f10016bf2dc1 12 - github.com/quailyquaily/uniai v0.1.19 12 + github.com/quailyquaily/uniai v0.1.21-0.20260429012404-a0f84e98d4ec 13 13 github.com/spf13/cast v1.10.0 14 14 github.com/spf13/cobra v1.8.1 15 15 github.com/spf13/viper v1.19.0 ··· 31 31 github.com/ProtonMail/go-crypto v1.3.0 // indirect 32 32 github.com/adrg/xdg v0.5.3 // indirect 33 33 github.com/aws/aws-sdk-go v1.55.8 // indirect 34 + github.com/aws/aws-sdk-go-v2 v1.41.6 // indirect 35 + github.com/aws/aws-sdk-go-v2/config v1.32.16 // indirect 36 + github.com/aws/aws-sdk-go-v2/credentials v1.19.15 // indirect 37 + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.22 // indirect 38 + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 // indirect 39 + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 // indirect 40 + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.23 // indirect 41 + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8 // indirect 42 + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22 // indirect 43 + github.com/aws/aws-sdk-go-v2/service/signin v1.0.10 // indirect 44 + github.com/aws/aws-sdk-go-v2/service/sso v1.30.16 // indirect 45 + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.20 // indirect 46 + github.com/aws/aws-sdk-go-v2/service/sts v1.42.0 // indirect 47 + github.com/aws/smithy-go v1.25.0 // indirect 34 48 github.com/bep/debounce v1.2.1 // indirect 35 49 github.com/cloudflare/circl v1.6.3 // indirect 36 50 github.com/coder/websocket v1.8.14 // indirect
+30
go.sum
··· 21 21 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 22 22 github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ= 23 23 github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk= 24 + github.com/aws/aws-sdk-go-v2 v1.41.6 h1:1AX0AthnBQzMx1vbmir3Y4WsnJgiydmnJjiLu+LvXOg= 25 + github.com/aws/aws-sdk-go-v2 v1.41.6/go.mod h1:dy0UzBIfwSeot4grGvY1AqFWN5zgziMmWGzysDnHFcQ= 26 + github.com/aws/aws-sdk-go-v2/config v1.32.16 h1:Q0iQ7quUgJP0F/SCRTieScnaMdXr9h/2+wze1u3cNeM= 27 + github.com/aws/aws-sdk-go-v2/config v1.32.16/go.mod h1:duCCnJEFqpt2RC6no1iK6q+8HpwOAkiUua0pY507dQc= 28 + github.com/aws/aws-sdk-go-v2/credentials v1.19.15 h1:fyvgWTszojq8hEnMi8PPBTvZdTtEVmAVyo+NFLHBhH4= 29 + github.com/aws/aws-sdk-go-v2/credentials v1.19.15/go.mod h1:gJiYyMOjNg8OEdRWOf3CrFQxM2a98qmrtjx1zuiQfB8= 30 + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.22 h1:IOGsJ1xVWhsi+ZO7/NW8OuZZBtMJLZbk4P5HDjJO0jQ= 31 + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.22/go.mod h1:b+hYdbU+jGKfXE8kKM6g1+h+L/Go3vMvzlxBsiuGsxg= 32 + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 h1:GmLa5Kw1ESqtFpXsx5MmC84QWa/ZrLZvlJGa2y+4kcQ= 33 + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22/go.mod h1:6sW9iWm9DK9YRpRGga/qzrzNLgKpT2cIxb7Vo2eNOp0= 34 + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 h1:dY4kWZiSaXIzxnKlj17nHnBcXXBfac6UlsAx2qL6XrU= 35 + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22/go.mod h1:KIpEUx0JuRZLO7U6cbV204cWAEco2iC3l061IxlwLtI= 36 + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.23 h1:FPXsW9+gMuIeKmz7j6ENWcWtBGTe1kH8r9thNt5Uxx4= 37 + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.23/go.mod h1:7J8iGMdRKk6lw2C+cMIphgAnT8uTwBwNOsGkyOCm80U= 38 + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8 h1:HtOTYcbVcGABLOVuPYaIihj6IlkqubBwFj10K5fxRek= 39 + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8/go.mod h1:VsK9abqQeGlzPgUr+isNWzPlK2vKe9INMLWnY65f5Xs= 40 + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22 h1:PUmZeJU6Y1Lbvt9WFuJ0ugUK2xn6hIWUBBbKuOWF30s= 41 + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22/go.mod h1:nO6egFBoAaoXze24a2C0NjQCvdpk8OueRoYimvEB9jo= 42 + github.com/aws/aws-sdk-go-v2/service/signin v1.0.10 h1:a1Fq/KXn75wSzoJaPQTgZO0wHGqE9mjFnylnqEPTchA= 43 + github.com/aws/aws-sdk-go-v2/service/signin v1.0.10/go.mod h1:p6+MXNxW7IA6dMgHfTAzljuwSKD0NCm/4lbS4t6+7vI= 44 + github.com/aws/aws-sdk-go-v2/service/sso v1.30.16 h1:x6bKbmDhsgSZwv6q19wY/u3rLk/3FGjJWyqKcIRufpE= 45 + github.com/aws/aws-sdk-go-v2/service/sso v1.30.16/go.mod h1:CudnEVKRtLn0+3uMV0yEXZ+YZOKnAtUJ5DmDhilVnIw= 46 + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.20 h1:oK/njaL8GtyEihkWMD4k3VgHCT64RQKkZwh0DG5j8ak= 47 + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.20/go.mod h1:JHs8/y1f3zY7U5WcuzoJ/yAYGYtNIVPKLIbp61euvmg= 48 + github.com/aws/aws-sdk-go-v2/service/sts v1.42.0 h1:ks8KBcZPh3PYISr5dAiXCM5/Thcuxk8l+PG4+A0exds= 49 + github.com/aws/aws-sdk-go-v2/service/sts v1.42.0/go.mod h1:pFw33T0WLvXU3rw1WBkpMlkgIn54eCB5FYLhjDc9Foo= 50 + github.com/aws/smithy-go v1.25.0 h1:Sz/XJ64rwuiKtB6j98nDIPyYrV1nVNJ4YU74gttcl5U= 51 + github.com/aws/smithy-go v1.25.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= 24 52 github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= 25 53 github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= 26 54 github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= ··· 159 187 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 160 188 github.com/quailyquaily/uniai v0.1.19 h1:UZvM8kFJgZKgn0YNLocRZpo5fHsszjZfv+9defugNoY= 161 189 github.com/quailyquaily/uniai v0.1.19/go.mod h1:C3kQuLcZ+QvU1+uRmRXkHx3Jo8gaZxa6Da54aUI9nj4= 190 + github.com/quailyquaily/uniai v0.1.21-0.20260429012404-a0f84e98d4ec h1:HUPauulekNmeyuTJHai9qx90MT/98PxWhhX1ML87vxo= 191 + github.com/quailyquaily/uniai v0.1.21-0.20260429012404-a0f84e98d4ec/go.mod h1:C3kQuLcZ+QvU1+uRmRXkHx3Jo8gaZxa6Da54aUI9nj4= 162 192 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 163 193 github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 164 194 github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+18 -12
internal/llmutil/llmutil.go
··· 38 38 Profiles map[string]ProfileConfig 39 39 Routes RoutesConfig 40 40 41 - BedrockAWSKey string `config:"llm.bedrock.aws_key"` 42 - BedrockAWSSecret string `config:"llm.bedrock.aws_secret"` 43 - BedrockAWSRegion string `config:"llm.bedrock.region"` 44 - BedrockModelARN string `config:"llm.bedrock.model_arn"` 41 + BedrockAWSKey string `config:"llm.bedrock.aws_key"` 42 + BedrockAWSSecret string `config:"llm.bedrock.aws_secret"` 43 + BedrockAWSSessionToken string `config:"llm.bedrock.aws_session_token"` 44 + BedrockAWSProfile string `config:"llm.bedrock.aws_profile"` 45 + BedrockAWSRegion string `config:"llm.bedrock.region"` 46 + BedrockModelARN string `config:"llm.bedrock.model_arn"` 45 47 CloudflareAccountID string `config:"llm.cloudflare.account_id"` 46 48 CloudflareAPIToken string `config:"llm.cloudflare.api_token"` 47 49 } ··· 68 70 ConfigPath: strings.TrimSpace(r.GetString("config")), 69 71 Profiles: loadLLMProfilesFromReader(r), 70 72 Routes: loadLLMRoutesFromReader(r), 71 - BedrockAWSKey: firstNonEmpty(r.GetString("llm.bedrock.aws_key"), r.GetString("llm.aws.key")), 72 - BedrockAWSSecret: firstNonEmpty(r.GetString("llm.bedrock.aws_secret"), r.GetString("llm.aws.secret")), 73 - BedrockAWSRegion: firstNonEmpty(r.GetString("llm.bedrock.region"), r.GetString("llm.aws.region")), 74 - BedrockModelARN: firstNonEmpty(r.GetString("llm.bedrock.model_arn"), r.GetString("llm.aws.bedrock_model_arn")), 73 + BedrockAWSKey: firstNonEmpty(r.GetString("llm.bedrock.aws_key"), r.GetString("llm.aws.key")), 74 + BedrockAWSSecret: firstNonEmpty(r.GetString("llm.bedrock.aws_secret"), r.GetString("llm.aws.secret")), 75 + BedrockAWSSessionToken: strings.TrimSpace(r.GetString("llm.bedrock.aws_session_token")), 76 + BedrockAWSProfile: strings.TrimSpace(r.GetString("llm.bedrock.aws_profile")), 77 + BedrockAWSRegion: firstNonEmpty(r.GetString("llm.bedrock.region"), r.GetString("llm.aws.region")), 78 + BedrockModelARN: firstNonEmpty(r.GetString("llm.bedrock.model_arn"), r.GetString("llm.aws.bedrock_model_arn")), 75 79 CloudflareAccountID: firstNonEmpty( 76 80 r.GetString("llm.cloudflare.account_id"), 77 81 ), ··· 182 186 AzureAPIKey: strings.TrimSpace(cfg.APIKey), 183 187 AzureEndpoint: strings.TrimSpace(cfg.Endpoint), 184 188 AzureDeployment: strings.TrimSpace(cfg.Model), 185 - AwsKey: firstNonEmpty(values.BedrockAWSKey), 186 - AwsSecret: firstNonEmpty(values.BedrockAWSSecret), 187 - AwsRegion: firstNonEmpty(values.BedrockAWSRegion), 188 - AwsBedrockModelArn: firstNonEmpty(values.BedrockModelARN), 189 + AwsKey: firstNonEmpty(values.BedrockAWSKey), 190 + AwsSecret: firstNonEmpty(values.BedrockAWSSecret), 191 + AwsSessionToken: firstNonEmpty(values.BedrockAWSSessionToken), 192 + AwsProfile: firstNonEmpty(values.BedrockAWSProfile), 193 + AwsRegion: firstNonEmpty(values.BedrockAWSRegion), 194 + AwsBedrockModelArn: firstNonEmpty(values.BedrockModelARN), 189 195 CloudflareAccountID: firstNonEmpty( 190 196 values.CloudflareAccountID, 191 197 ),
+10 -4
internal/llmutil/routes.go
··· 36 36 Deployment string `mapstructure:"deployment"` 37 37 } `mapstructure:"azure"` 38 38 Bedrock struct { 39 - AWSKey string `mapstructure:"aws_key"` 40 - AWSSecret string `mapstructure:"aws_secret"` 41 - Region string `mapstructure:"region"` 42 - ModelARN string `mapstructure:"model_arn"` 39 + AWSKey string `mapstructure:"aws_key"` 40 + AWSSecret string `mapstructure:"aws_secret"` 41 + AWSSessionToken string `mapstructure:"aws_session_token"` 42 + AWSProfile string `mapstructure:"aws_profile"` 43 + Region string `mapstructure:"region"` 44 + ModelARN string `mapstructure:"model_arn"` 43 45 } `mapstructure:"bedrock"` 44 46 Cloudflare struct { 45 47 AccountID string `mapstructure:"account_id"` ··· 308 310 cfg.Azure.Deployment = strings.TrimSpace(cfg.Azure.Deployment) 309 311 cfg.Bedrock.AWSKey = strings.TrimSpace(cfg.Bedrock.AWSKey) 310 312 cfg.Bedrock.AWSSecret = strings.TrimSpace(cfg.Bedrock.AWSSecret) 313 + cfg.Bedrock.AWSSessionToken = strings.TrimSpace(cfg.Bedrock.AWSSessionToken) 314 + cfg.Bedrock.AWSProfile = strings.TrimSpace(cfg.Bedrock.AWSProfile) 311 315 cfg.Bedrock.Region = strings.TrimSpace(cfg.Bedrock.Region) 312 316 cfg.Bedrock.ModelARN = strings.TrimSpace(cfg.Bedrock.ModelARN) 313 317 cfg.Cloudflare.AccountID = strings.TrimSpace(cfg.Cloudflare.AccountID) ··· 403 407 applyStringOverride(&out.AzureDeployment, override.Azure.Deployment) 404 408 applyStringOverride(&out.BedrockAWSKey, override.Bedrock.AWSKey) 405 409 applyStringOverride(&out.BedrockAWSSecret, override.Bedrock.AWSSecret) 410 + applyStringOverride(&out.BedrockAWSSessionToken, override.Bedrock.AWSSessionToken) 411 + applyStringOverride(&out.BedrockAWSProfile, override.Bedrock.AWSProfile) 406 412 applyStringOverride(&out.BedrockAWSRegion, override.Bedrock.Region) 407 413 applyStringOverride(&out.BedrockModelARN, override.Bedrock.ModelARN) 408 414 applyStringOverride(&out.CloudflareAccountID, override.Cloudflare.AccountID)
+50
providers/uniai/bedrock_credentials.go
··· 1 + package uniai 2 + 3 + import ( 4 + "context" 5 + "fmt" 6 + "strings" 7 + 8 + awsconfig "github.com/aws/aws-sdk-go-v2/config" 9 + awscredentials "github.com/aws/aws-sdk-go-v2/credentials" 10 + ) 11 + 12 + func ResolveBedrockCredentials(ctx context.Context, cfg *Config) error { 13 + if cfg == nil { 14 + return fmt.Errorf("bedrock config is nil") 15 + } 16 + 17 + opts := []func(*awsconfig.LoadOptions) error{} 18 + if region := strings.TrimSpace(cfg.AwsRegion); region != "" { 19 + opts = append(opts, awsconfig.WithRegion(region)) 20 + } 21 + if profile := strings.TrimSpace(cfg.AwsProfile); profile != "" { 22 + opts = append(opts, awsconfig.WithSharedConfigProfile(profile)) 23 + } 24 + 25 + accessKey := strings.TrimSpace(cfg.AwsKey) 26 + secretKey := strings.TrimSpace(cfg.AwsSecret) 27 + sessionToken := strings.TrimSpace(cfg.AwsSessionToken) 28 + if accessKey != "" || secretKey != "" || sessionToken != "" { 29 + opts = append(opts, awsconfig.WithCredentialsProvider( 30 + awscredentials.NewStaticCredentialsProvider(accessKey, secretKey, sessionToken), 31 + )) 32 + } 33 + 34 + awsCfg, err := awsconfig.LoadDefaultConfig(ctx, opts...) 35 + if err != nil { 36 + return fmt.Errorf("resolve bedrock aws config: %w", err) 37 + } 38 + creds, err := awsCfg.Credentials.Retrieve(ctx) 39 + if err != nil { 40 + return fmt.Errorf("retrieve bedrock aws credentials: %w", err) 41 + } 42 + 43 + cfg.AwsKey = strings.TrimSpace(creds.AccessKeyID) 44 + cfg.AwsSecret = strings.TrimSpace(creds.SecretAccessKey) 45 + cfg.AwsSessionToken = strings.TrimSpace(creds.SessionToken) 46 + if strings.TrimSpace(cfg.AwsRegion) == "" { 47 + cfg.AwsRegion = strings.TrimSpace(awsCfg.Region) 48 + } 49 + return nil 50 + }
+8
providers/uniai/client.go
··· 17 17 uniaichat "github.com/quailyquaily/uniai/chat" 18 18 ) 19 19 20 + 20 21 type Config struct { 21 22 Provider string 22 23 Endpoint string ··· 38 39 AzureDeployment string 39 40 AwsKey string 40 41 AwsSecret string 42 + AwsSessionToken string 43 + AwsProfile string 41 44 AwsRegion string 42 45 AwsBedrockModelArn string 43 46 CloudflareAccountID string ··· 68 71 pricing = uniaiapi.DefaultPricingCatalog() 69 72 } 70 73 74 + if provider == "bedrock" { 75 + _ = ResolveBedrockCredentials(context.Background(), &cfg) 76 + } 77 + 71 78 openAIBase := normalizeOpenAIBase(cfg.Endpoint) 72 79 openAIKey := strings.TrimSpace(cfg.APIKey) 73 80 ··· 94 101 AnthropicModel: strings.TrimSpace(anthropicModel), 95 102 AwsKey: strings.TrimSpace(cfg.AwsKey), 96 103 AwsSecret: strings.TrimSpace(cfg.AwsSecret), 104 + AwsSessionToken: strings.TrimSpace(cfg.AwsSessionToken), 97 105 AwsRegion: strings.TrimSpace(cfg.AwsRegion), 98 106 AwsBedrockModelArn: strings.TrimSpace(cfg.AwsBedrockModelArn), 99 107 CloudflareAccountID: strings.TrimSpace(cfg.CloudflareAccountID),