Copilot commented on code in PR #13249:
URL: https://github.com/apache/apisix/pull/13249#discussion_r3108077216
##########
docs/en/latest/plugins/ai-proxy-multi.md:
##########
@@ -58,10 +70,11 @@ In addition, the Plugin also supports logging LLM request
information in the acc
| balancer.key | string | False |
| | Used when `type` is `chash`. When
`hash_on` is set to `header` or `cookie`, `key` is required. When `hash_on` is
set to `consumer`, `key` is not required as the consumer name will be used as
the key automatically. |
| instances | array[object] | True |
| | LLM instance configurations. |
| instances.name | string | True |
| | Name of the LLM service instance. |
-| instances.provider | string | True |
| [openai, deepseek, azure-openai, aimlapi, anthropic,
openrouter, gemini, vertex-ai, openai-compatible] | LLM service provider. When
set to `openai`, the Plugin will proxy the request to `api.openai.com`. When
set to `deepseek`, the Plugin will proxy the request to `api.deepseek.com`.
When set to `aimlapi`, the Plugin uses the OpenAI-compatible driver and proxies
the request to `api.aimlapi.com` by default. When set to `anthropic`, the
Plugin will proxy the request to `api.anthropic.com` by default. When set to
`openrouter`, the Plugin uses the OpenAI-compatible driver and proxies the
request to `openrouter.ai` by default. When set to `gemini`, the Plugin uses
the OpenAI-compatible driver and proxies the request to
`generativelanguage.googleapis.com` by default. When set to `vertex-ai`, the
Plugin will proxy the request to `aiplatform.googleapis.com` by default and
requires `provide
r_conf` or `override`. When set to `openai-compatible`, the Plugin will proxy
the request to the custom endpoint configured in `override`. |
-| instances.provider_conf | object | False |
| | Configuration for the specific
provider. Required when `provider` is set to `vertex-ai` and `override` is not
configured. |
+| instances.provider | string | True |
| [openai, deepseek, azure-openai, aimlapi, anthropic,
openrouter, gemini, vertex-ai, bedrock, openai-compatible] | LLM service
provider. When set to `openai`, the Plugin will proxy the request to
`api.openai.com`. When set to `deepseek`, the Plugin will proxy the request to
`api.deepseek.com`. When set to `aimlapi`, the Plugin uses the
OpenAI-compatible driver and proxies the request to `api.aimlapi.com` by
default. When set to `anthropic`, the Plugin will proxy the request to
`api.anthropic.com` by default. When set to `openrouter`, the Plugin uses the
OpenAI-compatible driver and proxies the request to `openrouter.ai` by default.
When set to `gemini`, the Plugin uses the OpenAI-compatible driver and proxies
the request to `generativelanguage.googleapis.com` by default. When set to
`vertex-ai`, the Plugin will proxy the request to `aiplatform.googleapis.com`
by default and requires
`provider_conf` or `override`. When set to `bedrock`, the Plugin will proxy
the request to the AWS Bedrock Converse API
(`bedrock-runtime.<region>.amazonaws.com`) and signs the request with AWS
SigV4. When set to `openai-compatible`, the Plugin will proxy the request to
the custom endpoint configured in `override`. |
+| instances.provider_conf | object | False |
| | Configuration for the specific
provider. Required when `provider` is set to `vertex-ai` and `override` is not
configured. Required when `provider` is set to `bedrock`. |
| instances.provider_conf.project_id | string | True |
| | Google Cloud Project ID. |
| instances.provider_conf.region | string | True |
| | Google Cloud Region. |
+| instances.provider_conf.region | string | True |
| minLength = 1 | AWS region used to construct the
Bedrock endpoint and to sign the request with SigV4. Required when `provider`
is set to `bedrock`. |
| instances.priority | integer | False | 0
| | Priority of the LLM instance in load
balancing. `priority` takes precedence over `weight`. |
| instances.weight | string | True | 0
| greater or equal to 0 | Weight of the LLM instance in
load balancing. |
| instances.auth | object | True |
| | Authentication configurations. |
Review Comment:
The attributes table now lists `instances.provider_conf.region` twice
(Google Cloud Region vs AWS Region). Duplicate keys in the same table are
ambiguous and make it hard to tell which providers each row applies to. Please
consolidate into a single row with provider-specific semantics/requirements, or
split provider-specific config into separate subsections.
```suggestion
| instances.provider_conf.region | string | True |
| minLength = 1 | Provider-specific region. For
`vertex-ai`, this is the Google Cloud region. For `bedrock`, this is the AWS
region used to construct the Bedrock endpoint and sign the request with SigV4.
Required when `provider` is set to `bedrock`, and required for `vertex-ai` when
`provider_conf` is used. |
| instances.priority | integer | False | 0
| | Priority of the LLM instance in load
balancing. `priority` takes precedence over `weight`. |
| instances.weight | string | True | 0
| greater or equal to 0 | Weight of the LLM instance in
load balancing. |
| instances.auth | object | True |
| | Authentication configurations. |
```
##########
docs/en/latest/plugins/ai-proxy.md:
##########
@@ -47,25 +47,42 @@ In addition, the Plugin also supports logging LLM request
information in the acc
| `messages.role` | String | True | Role of the message (`system`,
`user`, `assistant`).|
| `messages.content` | String | True | Content of the message.
|
+### Bedrock Converse Request Format
+
+When `provider` is set to `bedrock`, the Plugin expects requests in the
[Bedrock Converse
API](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_Converse.html)
format. The request URI must end with `/converse` and the body must contain a
`messages` array.
+
+| Name | Type | Required | Description
|
+| ------------------ | ------ | -------- |
----------------------------------------------------------------------------------------------------
|
+| `messages` | Array | True | An array of message objects.
|
+| `messages.role` | String | True | Role of the message (`user`,
`assistant`). |
+| `messages.content` | Array | True | An array of content blocks. Each
block contains a `text` field (e.g., `[{"text": "What is 1+1?"}]`). |
+| `system` | Array | False | Optional system prompt blocks
(e.g., `[{"text": "You are a helpful assistant."}]`). |
+| `inferenceConfig` | Object | False | Optional inference parameters such
as `maxTokens`, `temperature`, `topP`, etc. |
+
## Attributes
| Name | Type | Required | Default | Valid values
| Description |
|--------------------|--------|----------|---------|------------------------------------------|-------------|
-| provider | string | True | | [openai, deepseek,
azure-openai, aimlapi, anthropic, openrouter, gemini, vertex-ai,
openai-compatible] | LLM service provider. When set to `openai`, the Plugin
will proxy the request to `https://api.openai.com/chat/completions`. When set
to `deepseek`, the Plugin will proxy the request to
`https://api.deepseek.com/chat/completions`. When set to `aimlapi`, the Plugin
uses the OpenAI-compatible driver and proxies the request to
`https://api.aimlapi.com/v1/chat/completions` by default. When set to
`anthropic`, the Plugin will proxy the request to
`https://api.anthropic.com/v1/chat/completions` by default. When set to
`openrouter`, the Plugin uses the OpenAI-compatible driver and proxies the
request to `https://openrouter.ai/api/v1/chat/completions` by default. When set
to `gemini`, the Plugin uses the OpenAI-compatible driver and proxies the
request to
`https://generativelanguage.googleapis.com/v1beta/openai/chat/completions` by
defa
ult. When set to `vertex-ai`, the Plugin will proxy the request to
`https://aiplatform.googleapis.com` by default and requires `provider_conf` or
`override`. When set to `openai-compatible`, the Plugin will proxy the request
to the custom endpoint configured in `override`. |
-| provider_conf | object | False | |
| Configuration for the specific provider. Required when
`provider` is set to `vertex-ai` and `override` is not configured. |
+| provider | string | True | | [openai, deepseek,
azure-openai, aimlapi, anthropic, openrouter, gemini, vertex-ai, bedrock,
openai-compatible] | LLM service provider. When set to `openai`, the Plugin
will proxy the request to `https://api.openai.com/chat/completions`. When set
to `deepseek`, the Plugin will proxy the request to
`https://api.deepseek.com/chat/completions`. When set to `aimlapi`, the Plugin
uses the OpenAI-compatible driver and proxies the request to
`https://api.aimlapi.com/v1/chat/completions` by default. When set to
`anthropic`, the Plugin will proxy the request to
`https://api.anthropic.com/v1/chat/completions` by default. When set to
`openrouter`, the Plugin uses the OpenAI-compatible driver and proxies the
request to `https://openrouter.ai/api/v1/chat/completions` by default. When set
to `gemini`, the Plugin uses the OpenAI-compatible driver and proxies the
request to
`https://generativelanguage.googleapis.com/v1beta/openai/chat/completions
` by default. When set to `vertex-ai`, the Plugin will proxy the request to
`https://aiplatform.googleapis.com` by default and requires `provider_conf` or
`override`. When set to `bedrock`, the Plugin will proxy the request to the AWS
Bedrock Converse API (`https://bedrock-runtime.<region>.amazonaws.com`) and
signs the request with AWS SigV4. When set to `openai-compatible`, the Plugin
will proxy the request to the custom endpoint configured in `override`. |
+| provider_conf | object | False | |
| Configuration for the specific provider. Required when
`provider` is set to `vertex-ai` and `override` is not configured. Required
when `provider` is set to `bedrock`. |
| provider_conf.project_id | string | True | |
| Google Cloud Project ID. |
| provider_conf.region | string | True | |
| Google Cloud Region. |
+| provider_conf.region | string | True | | minLength = 1
| AWS region used to construct the Bedrock endpoint and to sign
the request with SigV4. Required when `provider` is set to `bedrock`. |
Review Comment:
The attributes table now lists `provider_conf.region` twice (once for Google
Cloud Region and once for AWS Region). This is ambiguous for readers and makes
it unclear which providers each row applies to. Please consolidate into a
single `provider_conf.region` row that explains the per-provider
meaning/requirements, or split provider-specific configuration into separate
subsections to avoid duplicate keys.
```suggestion
| provider_conf.region | string | True | | minLength = 1
| Provider-specific region. For `vertex-ai`, this is the
Google Cloud region. For `bedrock`, this is the AWS region used to construct
the Bedrock endpoint and sign the request with SigV4. Required when `provider`
is set to `bedrock`. |
```
##########
apisix/plugins/ai-providers/base.lua:
##########
@@ -187,6 +187,20 @@ function _M.build_request(self, ctx, conf, request_body,
opts)
request_body.model = nil
end
+ -- AWS SigV4 signing (must be last — signs the finalized body)
+ if auth.aws then
+ local auth_aws = require("apisix.plugins.ai-transport.auth-aws")
+ local region = opts.conf and opts.conf.region
+ if not region then
+ return nil, "missing region for AWS SigV4 signing "
+ .. "(provider_conf.region required for bedrock)"
+ end
+ local sign_err = auth_aws.sign_request(params, auth.aws, region)
+ if sign_err then
+ return nil, "failed to sign AWS request: " .. sign_err
+ end
+ end
Review Comment:
AWS SigV4 signing is triggered solely by the presence of `auth.aws`,
regardless of which provider is selected. Because the schema doesn’t currently
restrict `auth.aws` to Bedrock-only, a misconfigured instance could end up
SigV4-signing requests to non-AWS providers (adding/overwriting
`authorization`, `host`, etc.). Consider gating this behind `self`/provider
type (e.g., only sign when provider is `bedrock`) and/or tightening the schema
so `auth.aws` is only allowed/required for Bedrock.
##########
apisix/plugins/ai-proxy/schema.lua:
##########
@@ -120,9 +145,16 @@ local ai_instance_schema = {
properties = {
endpoint = {
type = "string",
- description = "To be specified to override the
endpoint of the AI Instance",
+ description = "Override the endpoint of the AI
Instance. "
+ .. "Typically used for custom hosts (e.g., AWS "
+ .. "PrivateLink, reverse proxies) — provide only
the "
+ .. "scheme + host so the plugin computes the path.
"
+ .. "If you include a path with reserved characters
"
+ .. "(e.g., Bedrock inference profile ARNs
containing "
+ .. "':' or '/'), they must be URL-encoded.",
Review Comment:
The schema description for `override.endpoint` says to “provide only the
scheme + host so the plugin computes the path”, but `ai-providers/base.lua`
explicitly uses `parsed_url.path` when a path is present (and the Bedrock
tests/examples set a full `/model/.../converse` path). This description is
misleading; please update it to reflect that `override.endpoint` may include a
path (and query), and clarify when the plugin computes vs honors the provided
path.
```suggestion
.. "PrivateLink, reverse proxies). You may
provide "
.. "only the scheme + host, in which case the
plugin "
.. "computes the provider-specific path, or
provide "
.. "a full endpoint including path and query, in
"
.. "which case the plugin uses the supplied
path/query. "
.. "If your custom path or query contains
reserved "
.. "characters (e.g., Bedrock inference profile
ARNs "
.. "containing ':' or '/'), they must be
URL-encoded.",
```
##########
apisix/plugins/ai-proxy/schema.lua:
##########
@@ -133,19 +165,51 @@ local ai_instance_schema = {
}
},
required = {"name", "provider", "auth", "weight"},
- ["if"] = {
- properties = { provider = { enum = { "vertex-ai" } } },
- },
- ["then"] = {
- properties = {
- provider_conf = provider_vertex_ai_schema,
+ allOf = {
+ {
+ ["if"] = {
+ properties = { provider = { enum = { "vertex-ai" } } },
+ },
+ ["then"] = {
+ properties = {
+ provider_conf = provider_vertex_ai_schema,
+ },
+ anyOf = {
+ { required = { "provider_conf" } },
+ { required = { "override" } },
+ },
+ },
+ },
+ {
+ ["if"] = {
+ properties = { provider = { enum = { "bedrock" } } },
+ },
+ ["then"] = {
+ properties = {
+ provider_conf = provider_bedrock_schema,
+ },
+ required = { "provider_conf" },
Review Comment:
For `provider=bedrock`, the schema currently enforces `provider_conf.region`
and (optionally) `options.model`, but it does not require `auth.aws`. Given the
provider relies on SigV4 signing, it’s better to fail fast at config time by
requiring `auth.aws` (and potentially disallowing it for non-AWS providers) to
avoid unsigned requests reaching Bedrock and returning confusing upstream
errors.
```suggestion
auth = {
required = { "aws" },
},
},
required = { "provider_conf", "auth" },
```
##########
t/plugin/ai-proxy-bedrock.t:
##########
@@ -0,0 +1,553 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+use t::APISIX 'no_plan';
+
+log_level("info");
+repeat_each(1);
+no_long_string();
+no_shuffle();
+no_root_location();
+
+
+add_block_preprocessor(sub {
+ my ($block) = @_;
+
+ if (!defined $block->request) {
+ $block->set_value("request", "GET /t");
+ }
+
+ my $main_config = $block->main_config // <<_EOC_;
+ env AWS_EC2_METADATA_DISABLED=true;
+_EOC_
+ $block->set_value("main_config", $main_config);
+
+ my $user_yaml_config = <<_EOC_;
+plugins:
+ - ai-proxy-multi
+ - prometheus
+_EOC_
+ $block->set_value("extra_yaml_config", $user_yaml_config);
+
+ my $http_config = $block->http_config // <<_EOC_;
+ server {
+ server_name bedrock;
+ listen 6724;
+
+ default_type 'application/json';
+
+ location ~ ^/model/.+/converse\$ {
+ content_by_lua_block {
+ local json = require("cjson.safe")
+
+ -- Log the raw request URI so tests can assert the exact
+ -- path shape (e.g., that ARN model IDs are URL-encoded
+ -- as a single path segment).
+ ngx.log(ngx.WARN, "[test] received uri: ",
ngx.var.request_uri)
+
+ if ngx.req.get_method() ~= "POST" then
+ ngx.status = 400
+ ngx.say("Unsupported request method: ",
ngx.req.get_method())
+ return
+ end
+
+ -- Check SigV4 auth headers
+ local auth_header = ngx.req.get_headers()["authorization"]
+ local amz_date = ngx.req.get_headers()["x-amz-date"]
+ if not auth_header or not amz_date then
+ ngx.status = 403
+ ngx.say(json.encode({
+ message = "Missing Authentication Token"
+ }))
+ return
+ end
+
+ -- Validate Authorization header structure (SigV4):
+ -- AWS4-HMAC-SHA256
Credential=<AK>/<DATE>/<REGION>/<SERVICE>/aws4_request,
+ -- SignedHeaders=<list>, Signature=<64 hex>
+ if not auth_header:match("^AWS4%-HMAC%-SHA256 ") then
+ ngx.status = 403
+ ngx.say(json.encode({
+ message = "Authorization header missing
AWS4-HMAC-SHA256 algorithm prefix"
+ }))
+ return
+ end
+
+ if not auth_header:match("Credential=[^,]+") then
+ ngx.status = 403
+ ngx.say(json.encode({
+ message = "Authorization header missing Credential
component"
+ }))
+ return
+ end
+
+ -- Strict credential scope:
access_key/<date>/us-east-1/bedrock/aws4_request
+ if not auth_header:match(
+
"Credential=AKIAIOSFODNN7EXAMPLE/%d%d%d%d%d%d%d%d/us%-east%-1/bedrock/aws4_request"
+ ) then
+ ngx.status = 403
+ ngx.say(json.encode({
+ message = "Authorization Credential scope does not
match expected "
+ ..
"AKIAIOSFODNN7EXAMPLE/<DATE>/us-east-1/bedrock/aws4_request"
+ }))
+ return
+ end
+
+ if not auth_header:match("SignedHeaders=[^,]+") then
+ ngx.status = 403
+ ngx.say(json.encode({
+ message = "Authorization header missing
SignedHeaders component"
+ }))
+ return
+ end
+
+ -- Lua patterns don't support {n} quantifiers, so match
+ -- exactly 64 hex chars by repeating %x sixty-four times.
+ local hex64 = string.rep("%x", 64)
+ if not auth_header:match("Signature=" .. hex64) then
+ ngx.status = 403
+ ngx.say(json.encode({
+ message = "Authorization Signature is missing or
not 64 hex chars"
+ }))
+ return
+ end
+
+ -- Validate X-Amz-Date format: YYYYMMDDTHHMMSSZ
+ if not amz_date:match("^%d%d%d%d%d%d%d%dT%d%d%d%d%d%dZ\$")
then
+ ngx.status = 403
+ ngx.say(json.encode({
+ message = "X-Amz-Date header does not match
YYYYMMDDTHHMMSSZ format"
+ }))
+ return
+ end
+
+ -- Capture session token if provided so tests can assert
+ -- that auth.aws.session_token was propagated as
+ -- x-amz-security-token.
+ local session_token =
ngx.req.get_headers()["x-amz-security-token"]
+
+ ngx.req.read_body()
+ local body_data = ngx.req.get_body_data()
+ local body, err = json.decode(body_data)
+
+ if not body then
+ ngx.status = 400
+ ngx.say(json.encode({ message = "Invalid JSON: " ..
(err or "") }))
+ return
+ end
+
+ -- Verify model is NOT in the body (remove_model = true)
+ if body.model then
+ ngx.status = 400
+ ngx.say(json.encode({
+ message = "model field should not be in request
body"
+ }))
+ return
+ end
+
+ -- Verify request has messages
+ if not body.messages or #body.messages < 1 then
+ ngx.status = 400
+ ngx.say(json.encode({ message = "messages is required"
}))
+ return
+ end
+
+ -- Return Bedrock Converse response
+ local assistant_text = "1 + 1 = 2."
+ if session_token then
+ assistant_text = assistant_text
+ .. " session_token_seen=" .. session_token
+ end
+ ngx.status = 200
+ ngx.say(json.encode({
+ output = {
+ message = {
+ role = "assistant",
+ content = {{text = assistant_text}}
+ }
+ },
+ stopReason = "end_turn",
+ usage = {
+ inputTokens = 10,
+ outputTokens = 8,
+ totalTokens = 18
+ }
+ }))
+ }
+ }
+ }
+_EOC_
+
+ $block->set_value("http_config", $http_config);
+});
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: set route with bedrock provider
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "uri": "/ai/converse",
+ "plugins": {
+ "ai-proxy-multi": {
+ "instances": [
+ {
+ "name": "bedrock",
+ "provider": "bedrock",
Review Comment:
This test suite exercises Bedrock via `ai-proxy-multi` only. Since the PR
claims Bedrock support for both `ai-proxy` and `ai-proxy-multi`, it would be
good to add at least one analogous happy-path + schema-validation case for the
single-instance `ai-proxy` plugin (especially around required
`provider_conf.region` and `auth.aws`).
##########
docs/zh/latest/plugins/ai-proxy-multi.md:
##########
@@ -58,10 +70,11 @@ description: ai-proxy-multi 插件通过负载均衡、重试、故障转移和
| balancer.key | string | 否 |
| | 当 `type` 为 `chash` 时使用。当 `hash_on` 设置为
`header` 或 `cookie` 时,需要 `key`。当 `hash_on` 设置为 `consumer` 时,不需要
`key`,因为消费者名称将自动用作键。 |
| instances | array[object] | 是 |
| | LLM 实例配置。 |
| instances.name | string | 是 |
| | LLM 服务实例的名称。 |
-| instances.provider | string | 是 |
| [openai, deepseek, azure-openai, aimlapi, anthropic,
openrouter, gemini, vertex-ai, openai-compatible] | LLM 服务提供商。设置为 `openai`
时,插件将代理请求到 `api.openai.com`。设置为 `deepseek` 时,插件将代理请求到 `api.deepseek.com`。设置为
`aimlapi` 时,插件使用 OpenAI 兼容驱动程序,默认将请求代理到 `api.aimlapi.com`。设置为 `anthropic`
时,插件使用 OpenAI 兼容驱动程序,默认将请求代理到 `api.anthropic.com`。设置为 `openrouter` 时,插件使用
OpenAI 兼容驱动程序,默认将请求代理到 `openrouter.ai`。设置为 `gemini` 时,插件使用 OpenAI
兼容驱动程序,默认将请求代理到 `generativelanguage.googleapis.com`。设置为 `vertex-ai`
时,插件默认将请求代理到 `aiplatform.googleapis.com`,且需要配置 `provider_conf` 或 `override`。设置为
`openai-
compatible` 时,插件将代理请求到在 `override` 中配置的自定义端点。 |
-| instances.provider_conf | object | 否 |
| | 特定提供商的配置。当 `provider` 设置为 `vertex-ai` 且未配置
`override` 时必填。 |
+| instances.provider | string | 是 |
| [openai, deepseek, azure-openai, aimlapi, anthropic,
openrouter, gemini, vertex-ai, bedrock, openai-compatible] | LLM 服务提供商。设置为
`openai` 时,插件将代理请求到 `api.openai.com`。设置为 `deepseek` 时,插件将代理请求到
`api.deepseek.com`。设置为 `aimlapi` 时,插件使用 OpenAI 兼容驱动程序,默认将请求代理到
`api.aimlapi.com`。设置为 `anthropic` 时,插件使用 OpenAI 兼容驱动程序,默认将请求代理到
`api.anthropic.com`。设置为 `openrouter` 时,插件使用 OpenAI 兼容驱动程序,默认将请求代理到
`openrouter.ai`。设置为 `gemini` 时,插件使用 OpenAI 兼容驱动程序,默认将请求代理到
`generativelanguage.googleapis.com`。设置为 `vertex-ai` 时,插件默认将请求代理到
`aiplatform.googleapis.com`,且需要配置 `provider_conf` 或 `override`。设置为
`bedrock` 时,插件将代理请求到 AWS Bedrock Converse
API(`bedrock-runtime.<region>.amazonaws.com`),并使用 AWS SigV4 对请求进行签名。设置为
`openai-compatible` 时,插件将代理请求到在 `override` 中配置的自定义端点。 |
+| instances.provider_conf | object | 否 |
| | 特定提供商的配置。当 `provider` 设置为 `vertex-ai` 且未配置
`override` 时必填。当 `provider` 设置为 `bedrock` 时必填。 |
| instances.provider_conf.project_id | string | 是 |
| | Google Cloud 项目 ID。 |
| instances.provider_conf.region | string | 是 |
| | Google Cloud 区域。 |
+| instances.provider_conf.region | string | 是 |
| minLength = 1 | 用于构造 Bedrock 端点并使用 SigV4 对请求进行签名的 AWS 区域。当
`provider` 设置为 `bedrock` 时必填。 |
Review Comment:
属性表中 `instances.provider_conf.region` 现在出现了两次(一次表示 Google Cloud Region,一次表示
AWS Region)。同一表格内重复键会导致含义不清晰。建议合并为同一行并在描述中注明不同 provider 的语义/必选条件,或将
provider-specific 配置拆分到单独小节。
```suggestion
| instances.provider_conf.region | string | 是 |
| minLength = 1 | 区域配置:当 `provider` 设置为 `vertex-ai` 时表示
Google Cloud 区域;当 `provider` 设置为 `bedrock` 时表示 AWS 区域,用于构造 Bedrock 端点并使用 SigV4
对请求进行签名,且该场景下必填。 |
```
##########
docs/zh/latest/plugins/ai-proxy.md:
##########
@@ -47,25 +47,42 @@ description: ai-proxy 插件通过将插件配置转换为所需的请求格式
| `messages.role` | String | 是 | 消息的角色(`system`、`user`、`assistant`)。|
| `messages.content` | String | 是 | 消息的内容。 |
+### Bedrock Converse 请求格式
+
+当 `provider` 设置为 `bedrock` 时,插件期望请求采用 [Bedrock Converse
API](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_Converse.html)
格式。请求 URI 必须以 `/converse` 结尾,且请求体必须包含 `messages` 数组。
+
+| 名称 | 类型 | 必选项 | 描述
|
+| ------------------ | ------ | -------- |
----------------------------------------------------------------------------------------------------
|
+| `messages` | Array | 是 | 消息对象数组。
|
+| `messages.role` | String | 是 | 消息的角色(`user`、`assistant`)。
|
+| `messages.content` | Array | 是 | 内容块数组。每个块包含一个 `text` 字段(例如 `[{"text":
"What is 1+1?"}]`)。 |
+| `system` | Array | 否 | 可选的系统提示块(例如 `[{"text": "You are a
helpful assistant."}]`)。 |
+| `inferenceConfig` | Object | 否 | 可选的推理参数,如
`maxTokens`、`temperature`、`topP` 等。 |
+
## 属性
| 名称 | 类型 | 必选项 | 默认值 | 有效值 | 描述
|
|--------------------|--------|----------|---------|------------------------------------------|-------------|
-| provider | string | 是 | | [openai, deepseek,
azure-openai, aimlapi, anthropic, openrouter, gemini, vertex-ai,
openai-compatible] | LLM 服务提供商。当设置为 `openai` 时,插件将代理请求到
`https://api.openai.com/chat/completions`。当设置为 `deepseek` 时,插件将代理请求到
`https://api.deepseek.com/chat/completions`。当设置为 `aimlapi` 时,插件使用 OpenAI
兼容驱动程序,默认将请求代理到 `https://api.aimlapi.com/v1/chat/completions`。当设置为 `anthropic`
时,插件将代理请求到 `https://api.anthropic.com/v1/chat/completions`。当设置为 `openrouter`
时,插件使用 OpenAI 兼容驱动程序,默认将请求代理到
`https://openrouter.ai/api/v1/chat/completions`。当设置为 `gemini` 时,插件使用 OpenAI
兼容驱动程序,默认将请求代理到
`https://generativelanguage.googleapis.com/v1beta/openai/chat/completions`。当设置为
`vertex-ai` 时,插件默认将请求
代理到 `https://aiplatform.googleapis.com`,且需要配置 `provider_conf` 或
`override`。当设置为 `openai-compatible` 时,插件将代理请求到在 `override` 中配置的自定义端点。 |
-| provider_conf | object | 否 | |
| 特定提供商的配置。当 `provider` 设置为 `vertex-ai` 且未配置 `override` 时必填。 |
+| provider | string | 是 | | [openai, deepseek,
azure-openai, aimlapi, anthropic, openrouter, gemini, vertex-ai, bedrock,
openai-compatible] | LLM 服务提供商。当设置为 `openai` 时,插件将代理请求到
`https://api.openai.com/chat/completions`。当设置为 `deepseek` 时,插件将代理请求到
`https://api.deepseek.com/chat/completions`。当设置为 `aimlapi` 时,插件使用 OpenAI
兼容驱动程序,默认将请求代理到 `https://api.aimlapi.com/v1/chat/completions`。当设置为 `anthropic`
时,插件将代理请求到 `https://api.anthropic.com/v1/chat/completions`。当设置为 `openrouter`
时,插件使用 OpenAI 兼容驱动程序,默认将请求代理到
`https://openrouter.ai/api/v1/chat/completions`。当设置为 `gemini` 时,插件使用 OpenAI
兼容驱动程序,默认将请求代理到
`https://generativelanguage.googleapis.com/v1beta/openai/chat/completions`。当设置为
`vertex-ai` 时,插件默认
将请求代理到 `https://aiplatform.googleapis.com`,且需要配置 `provider_conf` 或
`override`。当设置为 `bedrock` 时,插件将代理请求到 AWS Bedrock Converse
API(`https://bedrock-runtime.<region>.amazonaws.com`),并使用 AWS SigV4
对请求进行签名。当设置为 `openai-compatible` 时,插件将代理请求到在 `override` 中配置的自定义端点。 |
+| provider_conf | object | 否 | |
| 特定提供商的配置。当 `provider` 设置为 `vertex-ai` 且未配置 `override` 时必填。当
`provider` 设置为 `bedrock` 时必填。 |
| provider_conf.project_id | string | 是 | |
| Google Cloud 项目 ID。 |
| provider_conf.region | string | 是 | |
| Google Cloud 区域。 |
+| provider_conf.region | string | 是 | | minLength = 1
| 用于构造 Bedrock 端点并使用 SigV4 对请求进行签名的 AWS 区域。当 `provider` 设置为
`bedrock` 时必填。 |
Review Comment:
属性表中 `provider_conf.region` 现在出现了两次(一次表示 Google Cloud Region,一次表示 AWS
Region)。这会让读者难以区分该字段在不同 provider 下的含义/必选条件。建议合并为同一行并在描述中注明不同 provider 的语义与要求,或将
provider-specific 配置拆分到单独小节,避免重复键。
```suggestion
| provider_conf.region | string | 是 | | minLength = 1
| 区域配置:当 `provider` 设置为 `vertex-ai` 时,表示 Google Cloud 区域;当
`provider` 设置为 `bedrock` 时,表示 AWS 区域,用于构造 Bedrock 端点并使用 SigV4 对请求进行签名。在
`bedrock` 场景下该字段必填且不能为空。 |
```
##########
docs/en/latest/plugins/ai-proxy-multi.md:
##########
@@ -7,7 +7,7 @@ keywords:
- ai-proxy-multi
- AI
- LLM
-description: The ai-proxy-multi Plugin extends the capabilities of ai-proxy
with load balancing, retries, fallbacks, and health chekcs, simplifying the
integration with OpenAI, DeepSeek, Azure, AIMLAPI, Anthropic, OpenRouter,
Gemini, Vertex AI, and other OpenAI-compatible APIs.
+description: The ai-proxy-multi Plugin extends the capabilities of ai-proxy
with load balancing, retries, fallbacks, and health chekcs, simplifying the
integration with OpenAI, DeepSeek, Azure, AIMLAPI, Anthropic, OpenRouter,
Gemini, Vertex AI, Amazon Bedrock, and other OpenAI-compatible APIs.
Review Comment:
Typo in the front-matter description: “health chekcs” should be “health
checks”.
```suggestion
description: The ai-proxy-multi Plugin extends the capabilities of ai-proxy
with load balancing, retries, fallbacks, and health checks, simplifying the
integration with OpenAI, DeepSeek, Azure, AIMLAPI, Anthropic, OpenRouter,
Gemini, Vertex AI, Amazon Bedrock, and other OpenAI-compatible APIs.
```
##########
apisix/plugins/ai-proxy/schema.lua:
##########
@@ -200,10 +264,14 @@ _M.ai_proxy_schema = {
description = "To be specified to override the endpoint of
the AI Instance",
},
},
+ required = { "endpoint" },
},
},
required = {"provider", "auth"},
- encrypt_fields = {"auth.header", "auth.query",
"auth.gcp.service_account_json"},
+ encrypt_fields = {
+ "auth.header", "auth.query", "auth.gcp.service_account_json",
+ "auth.aws.secret_access_key", "auth.aws.session_token",
+ },
Review Comment:
For the single-instance `ai-proxy` schema, Bedrock-specific requirements
described in this PR (e.g., `provider_conf.region` and `auth.aws` being
required for `provider=bedrock`) are not enforced here—only the encrypt_fields
list was updated. This allows misconfigured Bedrock routes to pass schema
validation and fail later at runtime during SigV4 signing/path construction.
Please add Bedrock-specific conditional requirements to `ai_proxy_schema`
(similar to the `instances` schema in this file).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]