fix(ai-proxy): fix bedrock Sigv4 mismatch (#2402)

This commit is contained in:
HaoJie Liu
2025-06-12 10:46:02 +08:00
committed by GitHub
parent d81573e0d2
commit 38068ee43d
3 changed files with 136 additions and 1 deletions

View File

@@ -275,6 +275,16 @@ Google Vertex AI 所对应的 type 为 vertex。它特有的配置字段如下
| `vertexGeminiSafetySetting` | map of string | 非必填 | - | Gemini 模型的内容安全过滤设置。 |
| `vertexTokenRefreshAhead` | number | 非必填 | - | Vertex access token刷新提前时间(单位秒) |
#### AWS Bedrock
AWS Bedrock 所对应的 type 为 bedrock。它特有的配置字段如下
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|----------------|--------|------|-----|------------------------------|
| `awsAccessKey` | string | 必填 | - | AWS Access Key用于身份认证 |
| `awsSecretKey` | string | 必填 | - | AWS Secret Access Key用于身份认证 |
| `awsRegion` | string | 必填 | - | AWS 区域例如us-east-1 |
## 用法示例
### 使用 OpenAI 协议代理 Azure OpenAI 服务
@@ -1704,6 +1714,59 @@ provider:
}
```
### 使用 OpenAI 协议代理 AWS Bedrock 服务
**配置信息**
```yaml
provider:
type: bedrock
awsAccessKey: "YOUR_AWS_ACCESS_KEY_ID"
awsSecretKey: "YOUR_AWS_SECRET_ACCESS_KEY"
awsRegion: "YOUR_AWS_REGION"
```
**请求示例**
```json
{
"model": "arn:aws:bedrock:us-west-2::foundation-model/anthropic.claude-3-5-haiku-20241022-v1:0",
"messages": [
{
"role": "user",
"content": "你好,你是谁?"
}
],
"stream": false
}
```
**响应示例**
```json
{
"id": "dc5812e2-6a62-49d6-829e-5c327b15e4e2",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "你好!我是Claude,一个由Anthropic开发的AI助手。很高兴认识你!我的目标是以诚实、有益且有意义的方式与人类交流。我会尽力提供准确和有帮助的信息,同时保持诚实和正直。请问我今天能为你做些什么呢?"
},
"finish_reason": "stop"
}
],
"created": 1749657608,
"model": "arn:aws:bedrock:us-west-2::foundation-model/anthropic.claude-3-5-haiku-20241022-v1:0",
"object": "chat.completion",
"usage": {
"prompt_tokens": 16,
"completion_tokens": 101,
"total_tokens": 117
}
}
```
## 完整配置示例

View File

@@ -220,6 +220,16 @@ For Vertex, the corresponding `type` is `vertex`. Its unique configuration field
| `vertexGeminiSafetySetting` | map of string | Optional | - | Gemini model content safety filtering settings. |
| `vertexTokenRefreshAhead` | number | Optional | - | Vertex access token refresh ahead time in seconds |
#### AWS Bedrock
For AWS Bedrock, the corresponding `type` is `bedrock`. Its unique configuration field is:
| Name | Data Type | Requirement | Default | Description |
|----------------|-----------|-------------|---------|-----------------------------------------------|
| `awsAccessKey` | string | Required | - | AWS Access Key used for authentication |
| `awsSecretKey` | string | Required | - | AWS Secret Access Key used for authentication |
| `awsRegion` | string | Required | - | AWS region, e.g., us-east-1 |
## Usage Examples
### Using OpenAI Protocol Proxy for Azure OpenAI Service
@@ -1481,6 +1491,55 @@ provider:
}
```
### Utilizing OpenAI Protocol Proxy for AWS Bedrock Services
**Configuration Information**
```yaml
provider:
type: bedrock
awsAccessKey: "YOUR_AWS_ACCESS_KEY_ID"
awsSecretKey: "YOUR_AWS_SECRET_ACCESS_KEY"
awsRegion: "YOUR_AWS_REGION"
```
**Request Example**
```json
{
"model": "arn:aws:bedrock:us-west-2::foundation-model/anthropic.claude-3-5-haiku-20241022-v1:0",
"messages": [
{
"role": "user",
"content": "who are you"
}
],
"stream": false
}
```
**Response Example**
```json
{
"id": "d52da49d-daf3-49d9-a105-0b527481fe14",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "I'm Claude, an AI created by Anthropic. I aim to be helpful, honest, and harmless. I won't pretend to be human, and I'll always try to be direct and truthful about what I am and what I can do."
},
"finish_reason": "stop"
}
],
"created": 1749659050,
"model": "arn:aws:bedrock:us-west-2::foundation-model/anthropic.claude-3-5-haiku-20241022-v1:0",
"object": "chat.completion",
"usage": {
"prompt_tokens": 10,
"completion_tokens": 57,
"total_tokens": 67
}
}
```
## Full Configuration Example
### Kubernetes Example

View File

@@ -760,10 +760,11 @@ func (b *bedrockProvider) buildChatCompletionResponse(ctx wrapper.HttpContext, b
},
}
requestId := ctx.GetStringContext(requestIdHeader, "")
modelId, _ := url.QueryUnescape(ctx.GetStringContext(ctxKeyFinalRequestModel, ""))
return &chatCompletionResponse{
Id: requestId,
Created: time.Now().UnixMilli() / 1000,
Model: ctx.GetStringContext(ctxKeyFinalRequestModel, ""),
Model: modelId,
SystemFingerprint: "",
Object: objectChatCompletion,
Choices: choices,
@@ -908,6 +909,7 @@ func (b *bedrockProvider) setAuthHeaders(body []byte, headers http.Header) {
}
func (b *bedrockProvider) generateSignature(path, amzDate, dateStamp string, body []byte) string {
path = encodeSigV4Path(path)
hashedPayload := sha256Hex(body)
endpoint := fmt.Sprintf(bedrockDefaultDomain, b.config.awsRegion)
@@ -925,6 +927,17 @@ func (b *bedrockProvider) generateSignature(path, amzDate, dateStamp string, bod
return signature
}
func encodeSigV4Path(path string) string {
segments := strings.Split(path, "/")
for i, seg := range segments {
if seg == "" {
continue
}
segments[i] = url.PathEscape(seg)
}
return strings.Join(segments, "/")
}
func getSignatureKey(key, dateStamp, region, service string) []byte {
kDate := hmacSha256([]byte("AWS4"+key), dateStamp)
kRegion := hmacSha256(kDate, region)