Skip to content

feat(ai-proxy): support aws bedrock#13249

Open
shreemaan-abhishek wants to merge 35 commits intoapache:masterfrom
shreemaan-abhishek:feat-support-bedrock
Open

feat(ai-proxy): support aws bedrock#13249
shreemaan-abhishek wants to merge 35 commits intoapache:masterfrom
shreemaan-abhishek:feat-support-bedrock

Conversation

@shreemaan-abhishek
Copy link
Copy Markdown
Contributor

@shreemaan-abhishek shreemaan-abhishek commented Apr 17, 2026

Add Amazon Bedrock support to ai-proxy and ai-proxy-multi

Adds a new bedrock provider that proxies requests to Amazon Bedrock's Converse API, with full AWS SigV4 request signing.

What's new

Provider & protocol:

  • New bedrock provider that signs requests with SigV4 and routes them to bedrock-runtime.{region}.amazonaws.com/model/{modelId}/converse
  • New bedrock-converse protocol module that parses Bedrock's native message/content-block format
  • Detection: requests with body containing messages array sent to a URI ending with /converse

Authentication:

  • New auth.aws block with access_key_id, secret_access_key (required), and session_token (optional, for temporary credentials from STS / assumed roles)
  • Sensitive fields (secret_access_key, session_token) are encrypted at the schema level
  • All credential headers (including x-amz-security-token) are redacted from logs

Region & endpoint:

  • provider_conf.region is required for bedrock (no env-var fallback or hardcoded default — fails fast on misconfiguration)
  • override.endpoint supported for AWS PrivateLink, custom proxies, etc.

Model identifiers (options.model):

  • Foundation model IDs (e.g., anthropic.claude-3-5-sonnet-20240620-v1:0)
  • Cross-region inference profile IDs (e.g., us.anthropic.claude-3-5-sonnet-20240620-v1:0)
  • Application inference profile ARNs (e.g., arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/abc123)

ARN model IDs are URL-encoded automatically so reserved characters (:, /) stay as a single path segment.

Example

curl http://127.0.0.1:9180/apisix/admin/routes/1 -X PUT \
  -H 'X-API-KEY: ...' -d '{
    "uri": "/ai/converse",
    "plugins": {
      "ai-proxy": {
        "provider": "bedrock",
        "auth": {
          "aws": {
            "access_key_id": "AKIA...",
            "secret_access_key": "..."
          }
        },
        "provider_conf": { "region": "us-east-1" },
        "options": {
          "model": "anthropic.claude-3-5-sonnet-20240620-v1:0"
        }
      }
    }
  }'
curl http://127.0.0.1:9080/ai/converse -X POST \
  -H 'Content-Type: application/json' -d '{
    "messages": [{"role": "user", "content": [{"text": "What is 1+1?"}]}],
    "inferenceConfig": {"maxTokens": 256}
  }'

Implementation highlights

  • AWS SigV4 signing uses resty.aws.request.sign (already a dependency)
  • Path encoding handles AWS's double-encoding requirement for canonical URIs (e.g., :%3A on the wire → %253A in canonical URI)
  • path:gsub preserves slash structure including trailing slashes and empty segments
  • Header values from the signer are written back using lowercase keys to match construct_forward_headers() and avoid duplicates

Limitations / future work

  • Non-streaming only. Bedrock's ConverseStream (which uses AWS EventStream binary protocol, not SSE) is not yet supported.
  • Bedrock is the only AWS service currently — other AWS services (e.g., SageMaker) would need separate provider modules.

Checklist

  • I have explained the need for this PR and the problem it solves
  • I have explained the changes or the new features added to this PR
  • I have added tests corresponding to this change
  • I have updated the documentation to reflect this change
  • I have verified that this change is backward compatible (If not, please discuss on the APISIX mailing list first)

Signed-off-by: Abhishek Choudhary <shreemaan.abhishek@gmail.com>
Rename double_encode_path to encode_path_for_canonical_uri since the
function only does a single ngx.escape_uri pass. The double-encoding
required by AWS SigV4 is achieved by combining this pass with the
upstream encoding done by bedrock.lua on the model ID.

Update the function comment to accurately describe this two-stage flow.
construct_forward_headers() in http.lua lowercases all header keys, so
writing signed headers (Authorization, X-Amz-Date, X-Amz-Security-Token,
Host) in mixed case could result in duplicate headers depending on the
HTTP client's behavior. Write them back as lowercase to match the
convention used elsewhere.
Add an explicit nil/empty check on params.path at the start of
sign_request() to return a clear error instead of crashing in
encode_path_for_canonical_uri() with "attempt to index a nil value".
This can happen when the Bedrock provider's path capability returns
nil (e.g., when options.model is missing).
Bedrock Converse API only accepts 'user' and 'assistant' roles in
messages[*].role. System prompts must go in the top-level body.system
field as an array of {text: "..."} blocks.

Update prepend_messages and append_messages to split incoming messages
by role: system entries are routed to body.system as text blocks, while
user/assistant entries go to body.messages as before. Without this,
prompt-decorator plugins inserting canonical system messages would
produce invalid Bedrock requests.
Bedrock builds the upstream URL path from the model ID. Without
options.model and without override.endpoint, the path resolves to nil
and the request fails later with a low-signal transport error. Add a
schema-level rule to require options.model when provider=bedrock and
override.endpoint is not set, so misconfigurations fail fast at
config validation time.
TEST 2-4 depend on the route created in TEST 1, and TEST 7 depends on
the route created in TEST 6. Add no_shuffle() so tests run in declared
order and these dependencies are honored consistently.
Switch oneOf to anyOf in the conditional schemas for vertex-ai and
bedrock. With oneOf, users couldn't set both provider_conf and
override.endpoint together. This blocked valid use cases like AWS
PrivateLink (custom endpoint) combined with explicit signing region,
since the code does not parse the region from the override URL.
anyOf still requires at least one of the two to be set.
… endpoint

Add test scenarios covering edge cases introduced by the Bedrock
provider:

- TEST 8/9: model as inference profile ARN (validates URL encoding of
  ":" and "/" in the path and SigV4 canonical URI handling)
- TEST 10/11: auth.aws.session_token propagation as
  x-amz-security-token header to the upstream
- TEST 12: route with provider_conf.region but no override.endpoint
  (validates schema accepts default endpoint construction)

Mock server appends session_token to the response body so TEST 11 can
assert propagation via response_body regex.
@dosubot dosubot bot added size:XXL This PR changes 1000+ lines, ignoring generated files. enhancement New feature or request labels Apr 17, 2026
Move all SigV4 path encoding into auth-aws.lua so callers don't need
to know about the AWS double-encoding rule. The previous design split
encoding between bedrock.lua (pre-encoded the model) and auth-aws.lua
(added the second encoding pass), which produced an incorrect canonical
URI when params.path came from a raw source like override.endpoint.

Now bedrock.lua passes the raw model in the path, and auth-aws.lua
normalizes the input (decode-then-encode is idempotent for both raw
and once-encoded inputs) and produces:
- params.path: once-encoded for HTTP wire transport
- canonicalURI: twice-encoded for SigV4 canonical request

This handles both default-endpoint and override.endpoint paths
correctly regardless of whether the input is raw or pre-encoded.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds Amazon Bedrock support to the ai-proxy / ai-proxy-multi plugin by introducing a Bedrock provider implementation, Bedrock Converse protocol handling, and AWS SigV4 request signing.

Changes:

  • Introduces a new bedrock provider + bedrock-converse protocol adapter (detection, request shaping, response parsing).
  • Adds AWS SigV4 signing support (incl. session token propagation) and updates HTTP transport to accept pre-serialized bodies.
  • Extends ai-proxy schemas and log sanitization, plus adds an end-to-end Bedrock-focused test suite.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
t/plugin/ai-proxy-bedrock.t New test coverage for Bedrock Converse routing, schema validation, ARN model handling, and session token propagation.
apisix/utils/log-sanitize.lua Redacts x-amz-security-token in logged headers.
apisix/plugins/ai-transport/http.lua Allows params.body to be a pre-serialized string (needed after SigV4 signing).
apisix/plugins/ai-transport/auth-aws.lua New AWS SigV4 signing helper for outbound AI provider requests.
apisix/plugins/ai-proxy/schema.lua Adds AWS auth schema + encrypt_fields updates; adds Bedrock provider_conf schema and conditional validation.
apisix/plugins/ai-providers/schema.lua Registers bedrock as a known provider.
apisix/plugins/ai-providers/bedrock.lua New Bedrock provider implementation (host/path generation; remove model from body).
apisix/plugins/ai-providers/base.lua Hooks AWS SigV4 signing into the shared request build pipeline.
apisix/plugins/ai-protocols/init.lua Registers bedrock-converse and adjusts protocol detection order.
apisix/plugins/ai-protocols/bedrock-converse.lua New Bedrock Converse protocol adapter (non-streaming).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apisix/plugins/ai-transport/auth-aws.lua Outdated
Comment thread apisix/plugins/ai-proxy/schema.lua Outdated
Comment thread apisix/plugins/ai-protocols/bedrock-converse.lua
Comment thread apisix/plugins/ai-protocols/bedrock-converse.lua
Comment thread apisix/plugins/ai-protocols/bedrock-converse.lua
Comment thread t/plugin/ai-proxy-bedrock.t Outdated
Previously the override schema didn't require any fields, so a config
like override = {} would pass validation despite being meaningless.
This was particularly problematic because the schema's anyOf accepted
"override" as an alternative to "provider_conf" — letting an empty
override silently bypass the provider_conf requirement and break
downstream request construction.

Make endpoint required in the override schema since it's the only
field and the only purpose of override is to set the endpoint.
Add minLength = 1 to auth.aws.access_key_id, secret_access_key,
session_token, and provider_conf.region for the Bedrock provider.
The 'required' constraint only checks key presence; without minLength,
empty strings pass schema validation and fail later during signing or
upstream URL construction. Catch these as admin-side validation errors
instead.
Signed-off-by: Abhishek Choudhary <shreemaan.abhishek@gmail.com>
Signed-off-by: Abhishek Choudhary <shreemaan.abhishek@gmail.com>
…nsert

prepend_messages and append_messages only checked for nil before
calling core.table.insert(body.system/messages, ...), which would
crash if those fields existed but weren't tables (e.g., a string).
Use type(...) ~= "table" to normalize, preventing request-time
exceptions on malformed input.
Bedrock provider falls back to AWS_REGION env var (or us-east-1
default) when provider_conf.region is not set, so the schema should
not require provider_conf or override. The inner rule still requires
options.model when override.endpoint is not set, ensuring a valid
upstream URL can always be constructed.
The previous code passed n=1 to normalize_and_encode_path for the
canonical URI, but n=1 means decode-then-encode-once, which cancels
out and produces the same once-encoded form as params.path. AWS SigV4
requires reserved characters to be encoded TWICE in the canonical
URI (e.g., raw ":" → "%3A" on the wire → "%253A" in canonicalURI).

Pass n=2 so the function decodes each segment then encodes it twice,
producing the required double-encoded form. Tests pass because the
local mock doesn't validate signatures, but real AWS requests would
have failed with signature mismatch.
Application inference profile ARNs contain "/" (e.g.
"...:application-inference-profile/abc") which would be split as path
segments if interpolated raw into "/model/{id}/converse". Encode the
model with ngx.escape_uri so "/" becomes "%2F" and ":" becomes "%3A",
keeping the model as a single path segment.

This is safe to combine with auth-aws.lua's normalize_and_encode_path
since that function decodes-then-encodes (idempotent for both raw and
once-encoded inputs).
Update the override.endpoint description to:
- Recommend host-only overrides for the typical use case (PrivateLink,
  reverse proxies) so the plugin computes the path with proper encoding
- Document that any reserved characters in an included path (e.g.,
  Bedrock inference profile ARNs with ':' or '/') must be URL-encoded
- Fix test URIs to end with /converse so bedrock-converse protocol
  detection matches (previous URIs like /ai/converse-arn don't end
  with /converse and would fall through to openai-chat)
- Log the request URI in the mock so tests can assert specific paths
- Update TEST 8 override.endpoint to use a properly URL-encoded ARN
  (matching what users should provide per the schema documentation)
- Add an error_log assertion in TEST 9 verifying the wire path keeps
  the ARN as a single percent-encoded segment (%3A and %2F preserved)
The mock server extracted text from the first user message into
first_content but never used it. Remove the dead code to make the
mock handler clearer.
Signed-off-by: Abhishek Choudhary <shreemaan.abhishek@gmail.com>
Signed-off-by: Abhishek Choudhary <shreemaan.abhishek@gmail.com>
Remove the AWS_REGION env var fallback and "us-east-1" default for
the Bedrock provider's region. Silently defaulting to us-east-1 was a
footgun — requests would route to the wrong region and fail SigV4
signature validation with confusing errors.

Now provider_conf.region must be set explicitly for Bedrock at the
schema level. The runtime in base.lua also returns a clear error
("missing region for AWS SigV4 signing (provider_conf.region required
for bedrock)") as a defense-in-depth check.

Also remove the hanging schema comment that explained the (now
removed) fallback behavior. Update docs (en + zh) to remove env var /
default mentions and clarify provider_conf.region is required.

Tests updated to set provider_conf.region in all bedrock instance
configs; AWS_REGION env var dropped from the test main_config.
Validate the structure of the SigV4 Authorization and X-Amz-Date
headers instead of just checking presence. A broken signer would now
fail tests instead of silently passing.

Checks:
- Authorization starts with "AWS4-HMAC-SHA256 "
- Credential matches AKIAIOSFODNN7EXAMPLE/<DATE>/us-east-1/bedrock/aws4_request
- SignedHeaders=... is present
- Signature= is followed by exactly 64 hex chars
- X-Amz-Date matches YYYYMMDDTHHMMSSZ format
normalize_and_encode_path used path:gmatch("[^/]+") which silently
dropped empty segments (e.g., "//foo" became "/foo") and trailing
slashes (e.g., "/foo/" became "/foo"). For paths with these
structures, the wire path and the SigV4 canonical URI would differ
from what AWS computes, breaking signature validation.

Switch to path:gsub("[^/]+", ...) so only non-slash runs are touched
and all "/" characters (including empties and trailing) are preserved
verbatim. Bedrock paths don't currently exercise this case, but the
function should be correct independent of caller.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 10 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +190 to +202
-- 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
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If we can follow this way, it is much better @shreemaan-abhishek

Comment thread docs/en/latest/plugins/ai-proxy.md Outdated
Comment on lines +69 to +70
| 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`. |
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
| 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`. |
| 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`. |

Copilot uses AI. Check for mistakes.
Comment on lines 76 to 80
| 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. |
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
| 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. |
| 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. |

Copilot uses AI. Check for mistakes.
Comment on lines +76 to +77
| instances.provider_conf.region | string | 是 | | | Google Cloud 区域。 |
| instances.provider_conf.region | string | 是 | | minLength = 1 | 用于构造 Bedrock 端点并使用 SigV4 对请求进行签名的 AWS 区域。当 `provider` 设置为 `bedrock` 时必填。 |
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

属性表中 instances.provider_conf.region 现在出现了两次(一次表示 Google Cloud Region,一次表示 AWS Region)。同一表格内重复键会导致含义不清晰。建议合并为同一行并在描述中注明不同 provider 的语义/必选条件,或将 provider-specific 配置拆分到单独小节。

Suggested change
| instances.provider_conf.region | string || | | Google Cloud 区域。 |
| instances.provider_conf.region | string || | minLength = 1 | 用于构造 Bedrock 端点并使用 SigV4 对请求进行签名的 AWS 区域。当 `provider` 设置为 `bedrock` 时必填。 |
| instances.provider_conf.region | string || | minLength = 1 | 区域配置:当 `provider` 设置为 `vertex-ai` 时表示 Google Cloud 区域;当 `provider` 设置为 `bedrock` 时表示 AWS 区域,用于构造 Bedrock 端点并使用 SigV4 对请求进行签名,且该场景下必填。 |

Copilot uses AI. Check for mistakes.
- 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.
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

Typo in the front-matter description: “health chekcs” should be “health checks”.

Suggested change
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.
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.

Copilot uses AI. Check for mistakes.
Comment on lines +210 to +216
"uri": "/ai/converse",
"plugins": {
"ai-proxy-multi": {
"instances": [
{
"name": "bedrock",
"provider": "bedrock",
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

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).

Copilot uses AI. Check for mistakes.
Comment on lines +150 to +154
.. "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.",
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
.. "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.",
.. "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.",

Copilot uses AI. Check for mistakes.
Comment on lines +271 to +274
encrypt_fields = {
"auth.header", "auth.query", "auth.gcp.service_account_json",
"auth.aws.secret_access_key", "auth.aws.session_token",
},
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

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).

Copilot uses AI. Check for mistakes.
Comment thread apisix/plugins/ai-proxy/schema.lua Outdated
Comment on lines +190 to +191
},
required = { "provider_conf" },
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
},
required = { "provider_conf" },
auth = {
required = { "aws" },
},
},
required = { "provider_conf", "auth" },

Copilot uses AI. Check for mistakes.
Comment thread docs/zh/latest/plugins/ai-proxy.md Outdated
Comment on lines +69 to +70
| provider_conf.region | string | 是 | | | Google Cloud 区域。 |
| provider_conf.region | string | 是 | | minLength = 1 | 用于构造 Bedrock 端点并使用 SigV4 对请求进行签名的 AWS 区域。当 `provider` 设置为 `bedrock` 时必填。 |
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

属性表中 provider_conf.region 现在出现了两次(一次表示 Google Cloud Region,一次表示 AWS Region)。这会让读者难以区分该字段在不同 provider 下的含义/必选条件。建议合并为同一行并在描述中注明不同 provider 的语义与要求,或将 provider-specific 配置拆分到单独小节,避免重复键。

Suggested change
| provider_conf.region | string || | | Google Cloud 区域。 |
| provider_conf.region | string || | minLength = 1 | 用于构造 Bedrock 端点并使用 SigV4 对请求进行签名的 AWS 区域。当 `provider` 设置为 `bedrock` 时必填。 |
| provider_conf.region | string || | minLength = 1 | 区域配置:当 `provider` 设置为 `vertex-ai` 时,表示 Google Cloud 区域;当 `provider` 设置为 `bedrock` 时,表示 AWS 区域,用于构造 Bedrock 端点并使用 SigV4 对请求进行签名。在 `bedrock` 场景下该字段必填且不能为空。 |

Copilot uses AI. Check for mistakes.
…edrock

Signed-off-by: Abhishek Choudhary <shreemaan.abhishek@gmail.com>
Signed-off-by: Abhishek Choudhary <shreemaan.abhishek@gmail.com>
Signed-off-by: Abhishek Choudhary <shreemaan.abhishek@gmail.com>
Signed-off-by: Abhishek Choudhary <shreemaan.abhishek@gmail.com>
Three related fixes:

1. The single ai-proxy schema lacked provider_conf entirely and didn't
   carry the Bedrock conditional rules — only ai-proxy-multi enforced
   them. Add provider_conf and the same allOf block to ai_proxy_schema
   so single-instance Bedrock routes fail validation if misconfigured.

2. Require auth.aws (not just any auth) when provider=bedrock. Without
   AWS credentials the signing block silently skips and AWS rejects
   with confusing errors — better to fail fast at config time.

3. Gate SigV4 signing in base.lua on a per-provider aws_sigv4 flag
   (set true on bedrock). Previously a stray auth.aws block on any
   provider would trigger SigV4 signing and overwrite headers.
- Merge the duplicate provider_conf.region rows (vertex-ai vs bedrock)
  into a single row that documents per-provider semantics. The two
  separate rows were ambiguous about which providers each applied to.
- Update override.endpoint description: clarify that the URL may
  include a path (which overrides the plugin-computed path), and add
  a Bedrock-specific tip about URL-encoding reserved characters in
  inference profile ARNs (':' → '%3A', '/' → '%2F').
- Fix "health chekcs" → "health checks" typo in ai-proxy-multi.md
  front-matter description.

Applied to en + zh versions of both ai-proxy.md and ai-proxy-multi.md.
Mirror all 12 ai-proxy-bedrock.t test cases for the single-instance
ai-proxy plugin (the existing test file only exercises ai-proxy-multi).
Both plugins now have parity coverage for:

- Basic Converse request + response
- System prompt (top-level body.system)
- Token usage extraction
- Schema validation (missing auth.aws.secret_access_key)
- Protocol mismatch (URI not ending in /converse)
- ARN model with override.endpoint and URL-encoded path
- session_token propagation as x-amz-security-token
- Default endpoint construction (no override)

Mock server (SigV4 assertions, response shape) is byte-identical to
the existing test file.
Four small fixes to make the Bedrock + AWS SigV4 path more robust:

1. Guard nil conf in bedrock.lua host capability: provider_conf can
   be nil when override.endpoint is the only config; the previous
   conf.region access would crash before the base layer could fall
   back to the parsed override URL.

2. Validate aws_conf and region at the top of sign_request, matching
   the existing params.path validation. Returns a clean error string
   instead of crashing on missing inputs.

3. Initialize params.headers before copying signed values so the
   helper can be reused safely from contexts that don't pre-populate
   headers.

4. Make endpointPrefix configurable via aws_conf.endpoint_prefix
   (default "bedrock") so the helper isn't permanently Bedrock-specific
   and can be reused for future AWS-backed providers (e.g., SageMaker).
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Signed-off-by: Abhishek Choudhary <shreemaan.abhishek@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants