Skip to content

feat(aws): Bedrock 智能模型解析与推理模式自适应 - #3183

Closed
keithyt06 wants to merge 5380 commits into
QuantumNous:mainfrom
keithyt06:feat/bedrock-smart-model-resolution
Closed

feat(aws): Bedrock 智能模型解析与推理模式自适应#3183
keithyt06 wants to merge 5380 commits into
QuantumNous:mainfrom
keithyt06:feat/bedrock-smart-model-resolution

Conversation

@keithyt06

@keithyt06 keithyt06 commented Mar 9, 2026

Copy link
Copy Markdown

概述

当前 new-api 的 AWS Bedrock 渠道存在以下限制:

  1. 不支持 global.(全局推理)和 jp.(东京区域)等推理配置前缀
  2. 模型映射表硬编码,每个新模型都需要修改代码
  3. 完整的 Bedrock 模型 ID(如 global.anthropic.claude-opus-4-6-v1)可能被错误处理
  4. 东京区域 ap-northeast-1 的前缀提取不正确
  5. 新旧模型调用模式不同:Claude 4.5+ 和 Nova 2 必须通过推理配置调用,而 Claude 3.x 和 Nova v1 只支持 on-demand 直连

本 PR 通过三层智能解析 + 模型级推理模式自适应,一次性解决以上所有问题。

变更内容

1. 三层智能模型 ID 解析

重写 getAwsModelID(),实现分层解析:

Layer 1: 完整 Bedrock ID 检测 → 直接透传
  - 区域前缀(global./us./eu./jp.)→ 跳过后续处理
  - Provider 前缀(anthropic./amazon.)→ 跳过后续处理

Layer 2: 精确映射(向后兼容)
  - 现有 awsModelIDMap 查找,行为完全不变

Layer 3: 模式匹配(自动构建)
  - claude-* → 自动加 anthropic. 前缀
  - nova-*   → 自动加 amazon. 前缀
  - 新模型无需修改代码即可支持

2. 模型级推理模式自适应

通过实际 API 测试验证,不同模型的调用模式完全不同:

模型 Base ID (on-demand) 推理配置 (global./us.)
Claude 3 Haiku
Claude 4.5 Haiku
Claude 4.6 Opus/Sonnet
Nova Micro/Lite/Pro (v1)
Nova 2 Lite
MiniMax M2/M2.1
DeepSeek R1 ✅ (us.)
DeepSeek V3.2

新增 awsModelsRequireInferenceProfile 映射表,短名称解析后自动选择正确的调用模式:

  • 需要推理配置的模型自动加 global. 前缀
  • 不在映射表中的模型默认使用 on-demand(base ID)

3. 用户体验

修改后,用户可以灵活使用以下任何方式调用模型:

输入方式 示例 行为
短名称(推荐) claude-opus-4-6 自动解析 + 自动选择正确调用模式
完整 Provider ID anthropic.claude-opus-4-6-v1 直接透传,用户自行控制
区域推理配置 us.anthropic.claude-opus-4-6-v1 直接透传
全局推理配置 global.anthropic.claude-opus-4-6-v1 直接透传
未知新模型 claude-future-model-2026 自动加 anthropic. 前缀

变更文件

文件 改动
relay/channel/aws/constants.go 新增前缀列表、模式匹配映射、推理模式映射表
relay/channel/aws/relay-aws.go 重写 getAwsModelID()(三层解析);更新 doAwsClientRequest() 推理模式逻辑
relay/channel/aws/adaptor.go 适配新函数签名,API Key 模式同步支持推理配置
relay/channel/aws/relay_aws_test.go 新增 33 个测试用例覆盖全部场景

向后兼容性

场景 修改前 修改后
claude-opus-4-6 映射 + 区域前缀 global.anthropic.claude-opus-4-6-v1(正确)
nova-micro-v1:0 映射 → amazon.nova-micro-v1:0 行为完全不变(on-demand)
anthropic.claude-opus-4-6-v1 可能被加错误前缀 透传,不做修改(修复)
global.anthropic.claude-opus-4-6-v1 可能被加错误前缀 透传,跳过推理解析(修复)

测试

单元测试

  • 3 个测试函数,33 个子测试,覆盖三层解析全部路径 + 端到端解析流程

集成测试(真实 Bedrock API 验证)

使用 AWS SDK (Go + boto3) 验证:Claude 4.6 Opus/Sonnet、Claude 4.5 Haiku、Claude 3 Haiku、Nova Micro/Lite/Pro、Nova 2 Lite、MiniMax M2/M2.1、DeepSeek R1/V3.2 — 12/12 全部通过

新增模型流程

新模型类型 需要做的
需要推理配置 awsModelsRequireInferenceProfile 加一行
支持 on-demand 无需改代码(默认行为)
新增短名称映射 awsModelIDMap 加一行(可选)

不包含的改动

  • 无数据库 schema 变更
  • 无前端 UI 变更
  • 无其他渠道代码变更
  • 无新依赖引入
  • 无 adaptor 接口变更

Summary by CodeRabbit

  • Improvements

    • Enhanced AWS model ID resolution with improved Bedrock compatibility and automatic inference profile support for applicable models.
  • Tests

    • Added comprehensive tests for AWS model ID resolution and end-to-end model handling workflows.

CaIon and others added 30 commits February 5, 2026 22:53
…al file types for LF normalization and binary detection
Mitigate XSS vulnerabilities in the playground where AI-generated content
is rendered without sanitization, allowing potential script injection via
prompt injection attacks.

MarkdownRenderer.jsx:
- Replace dangerouslySetInnerHTML with a sandboxed iframe for HTML preview
- Use sandbox="allow-same-origin" to block script execution while allowing
  CSS rendering and iframe height auto-sizing
- Add SandboxedHtmlPreview component with automatic height adjustment

CodeViewer.jsx:
- Add escapeHtml() utility to encode HTML entities before rendering
- Rewrite highlightJson() to process tokens iteratively, escaping each
  token and structural text before wrapping in syntax highlighting spans
- Escape non-JSON and very-large content paths that previously bypassed
  sanitization
- Update linkRegex to correctly match URLs containing & entities

These changes only affect the playground (AI output rendering). Admin-
configured content (home page, about page, footer, notices) remains
unaffected as they use separate code paths and are within the trusted
admin boundary.
🔒 fix(security): sanitize AI-generated HTML to prevent XSS in playground
…idation

- Add configurable per-user token creation limit (max_user_tokens)
- Sanitize search input patterns to prevent expensive queries
- Add per-user search rate limiting (by user ID)
- Add pagination to search endpoint with strict page size cap
- Skip empty search fields instead of matching nothing
- Hide internal errors from API responses
- Fix Interface2String float64 formatting causing config parse failures
- Add float-string fallback in config system for int/uint fields
fix: harden token search with pagination, rate limiting and input validation
- Change ESCAPE character from '\' to '!' for compatibility with MySQL/PostgreSQL/SQLite
- Adjust sanitization logic to escape '!' and '_' correctly, improving input validation for search queries
fix: /v1/chat/completions -> /v1/responses json_schema
将散落在多个文件中的预扣费/结算/退款逻辑抽象为统一的 BillingSession 生命周期管理:

- 新增 BillingSettler 接口 (relay/common/billing.go) 避免循环引用
- 新增 FundingSource 接口 + WalletFunding / SubscriptionFunding 实现 (service/funding_source.go)
- 新增 BillingSession 封装预扣/结算/退款原子操作 (service/billing_session.go)
- 新增 SettleBilling 统一结算辅助函数,替换各 handler 中的 quotaDelta 模式
- 重写 PreConsumeBilling 为 BillingSession 工厂入口
- controller/relay.go 退款守卫改用 BillingSession.Refund()

修复的 Bug:
- 令牌额度泄漏:PreConsumeTokenQuota 成功但 DecreaseUserQuota 失败时未回滚
- 订阅退款遗漏:FinalPreConsumedQuota=0 但 SubscriptionPreConsumed>0 时跳过退款
- 订阅多扣费:subConsume 强制为 1 但 FinalPreConsumedQuota 不同步
- 退款路径不统一:钱包/订阅退款逻辑现统一由 FundingSource.Refund 分派
- Settle 部分失败保护:新增 fundingSettled 标记,资金来源提交后
  令牌调整失败不再导致 Refund 误退已结算的资金
- 订阅多扣费修复:trySubscription 传 subConsume 而非 preConsumedQuota
  给 preConsume,保证三者(amount/preConsume/FinalPreConsumedQuota)一致
- 令牌回滚错误记录:preConsume 中 funding 失败时令牌回滚错误不再丢弃
- 移除钱包路径死代码:用户额度不足的 strings.Contains 匹配不可能命中
- WalletFunding.Refund 不重试:IncreaseUserQuota 非幂等,重试会多退
…e recharge card tabs

- Defaulting to subscriptions when available and avoiding initial flash when no plans exist.
- Adjust the wide-screen layout to place wallet and invite sections side by side, simplify the subscription header and controls, and add padding to prevent card borders from clipping.
- Update related i18n strings by adding the new tab label and removing the obsolete subscription blurb.
Seefs and others added 24 commits March 5, 2026 16:43
…ide-wildcard-path

Feature/param override wildcard path
…7aec3eb60f27ca33dbb4dc9610a

fix: fetch model add header passthrough rule key check
feats: repair the thinking of claude to openrouter convert
…d958a777a7e7ac8c1e4b5b3e537

feat: kling cost quota support use FinalUnitDeduction as totalToken
…-top_p

fix: If top_p is not provided, Claude's logic will set to 1
…es-body-no-retry

fix(relay): skip retries for bad response body errors
Keep the model pricing editor wording aligned with the new price-based UI while exposing cache, image, and audio pricing in the marketplace so users can see the full configured pricing model.
Introduce a billing display mode feature allowing users to toggle between price and ratio views. Update relevant components and hooks to support this new functionality, ensuring consistent pricing information is displayed across the application.
Add siteDisplayType prop across various pricing components to conditionally render pricing information based on the selected display type. This update enhances the user experience by ensuring that pricing details are accurately represented according to the chosen display mode, particularly for token-based views.
为渠道参数覆盖可视化规则提供拖拽排序支持
三层智能模型 ID 解析:
- Layer 1: 完整 Bedrock ID 检测(区域/Provider 前缀)直接透传
- Layer 2: 精确映射查找(向后兼容现有 awsModelIDMap)
- Layer 3: 模式匹配自动构建(claude-* → anthropic., nova-* → amazon.)

模型级推理模式自适应:
- Claude 4.5+/Nova 2/DeepSeek R1 等新模型自动使用 global. 推理配置
- Claude 3.x/Nova v1/MiniMax 等旧模型使用 on-demand 直连
- 通过 awsModelsRequireInferenceProfile 映射表精确控制

其他改进:
- 支持 global./jp. 等推理配置前缀
- 完整 Provider ID 透传(用户显式控制)
- API Key 模式同步支持推理配置解析

经 AWS SDK (Go + boto3) 对 Claude 4.6/4.5、Nova v1/v2、
MiniMax M2/M2.1、DeepSeek R1/V3.2 进行真实 API 验证。
@coderabbitai

ghost commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

The AWS Bedrock relay channel undergoes a refactor of its model ID resolution logic. It introduces a layered resolution system supporting Bedrock ID passthrough, exact map lookups, and pattern-based auto-prefixing. Inference profile requirements are now evaluated conditionally, and cross-region handling maps are replaced with a unified architecture. Test coverage is added for the new behavior.

Changes

Cohort / File(s) Summary
Core Adaptor Logic
relay/channel/aws/adaptor.go
Updated GetRequestURL to handle dual return values from getAwsModelID; conditionally applies "global." prefix when inference profile is required.
Configuration & Constants
relay/channel/aws/constants.go
Added Bedrock detection scaffolding (bedrockProviderPrefixes, bedrockRegionPrefixes, modelProviderPatterns); extended awsModelIDMap with nova-2-lite-v1:0; introduced awsModelsRequireInferenceProfile map; removed awsModelCanCrossRegionMap and awsRegionCrossModelPrefixMap.
Model ID Resolution Engine
relay/channel/aws/relay-aws.go
Refactored getAwsModelID with layered resolution (Bedrock passthrough → exact map → pattern-based); changed signature to return (modelID, skipInferenceResolve); replaced cross-region segmentation logic with multi-layer architecture.
Test Coverage
relay/channel/aws/relay_aws_test.go
Added TestGetAwsModelID (table-driven validation of resolution logic) and TestEndToEndModelResolution (end-to-end inference override flow verification).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • seefs001

Poem

🐰 A layered hop through Bedrock's layers deep,
Where models find their profiles to keep,
No more cross-region maps to roam,
Pattern-based paths lead models home! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is in Chinese and describes the core change: Bedrock intelligent model resolution and adaptive inference mode. It directly addresses the main objective of the PR—rewriting model ID resolution with layered parsing and adaptive inference modes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

ghost left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
relay/channel/aws/adaptor.go (1)

104-104: ⚠️ Potential issue | 🔴 Critical

Critical: URL construction has swapped parameters.

The fmt.Sprintf arguments are in the wrong order. The URL format expects {region} in the hostname and {modelId} in the path, but the code provides them in reverse order:

fmt.Sprintf("https://bedrock-runtime.%s.amazonaws.com/model/%s/converse", awsModelId, awsSecret[1])

This produces: https://bedrock-runtime.{modelId}.amazonaws.com/model/{region}/converse

But should be: https://bedrock-runtime.{region}.amazonaws.com/model/{modelId}/converse

🐛 Proposed fix
-		return fmt.Sprintf("https://bedrock-runtime.%s.amazonaws.com/model/%s/converse", awsModelId, awsSecret[1]), nil
+		return fmt.Sprintf("https://bedrock-runtime.%s.amazonaws.com/model/%s/converse", awsSecret[1], awsModelId), nil
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@relay/channel/aws/adaptor.go` at line 104, The URL construction in adaptor.go
uses fmt.Sprintf with its arguments swapped; update the fmt.Sprintf call that
builds the Bedrock runtime URL so the first %s is awsSecret[1] (region) and the
second %s is awsModelId (modelId) — i.e., swap the two arguments passed to
fmt.Sprintf in the function that returns the Bedrock runtime URL so the hostname
contains the region and the path contains the model ID.
relay/channel/aws/relay-aws.go (1)

5-5: ⚠️ Potential issue | 🟡 Minor

Direct encoding/json import violates coding guidelines.

The file imports encoding/json directly and uses it at line 325 (json.Unmarshal). As per coding guidelines, all JSON marshal/unmarshal operations must use wrapper functions from common/json.go.

🛠️ Proposed fix

Remove the direct import:

 import (
 	"context"
-	"encoding/json"
 	"fmt"

And update the usage at line 325:

-	if err := json.Unmarshal(awsResp.Body, &novaResp); err != nil {
+	if err := common.Unmarshal(awsResp.Body, &novaResp); err != nil {

As per coding guidelines: "All JSON marshal/unmarshal operations MUST use wrapper functions in common/json.go (common.Marshal, common.Unmarshal, etc.). Do NOT directly import or call encoding/json in business code."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@relay/channel/aws/relay-aws.go` at line 5, Remove the direct "encoding/json"
import and replace direct calls to json.Unmarshal with the project wrapper
common.Unmarshal (and json.Marshal with common.Marshal if present); update the
import block to remove "encoding/json" and add the common package import, and
change the call at the location using json.Unmarshal to call common.Unmarshal
with the same arguments so the file relies on the common/json.go wrappers
instead of the encoding/json package.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@relay/channel/aws/constants.go`:
- Around line 68-69: awsModelIDMap is missing a short-name entry and
modelProviderPatterns lacks a DeepSeek pattern, causing getAwsModelID to treat
"deepseek.r1-v1:0" as a provider-prefixed ID (skipInferenceResolve=true) and
bypass awsModelsRequireInferenceProfile logic; add a short-name mapping for the
DeepSeek model in awsModelIDMap (so callers can use the short name) and add the
DeepSeek regex to modelProviderPatterns so Layer 1 detection recognizes the
provider correctly, which will allow getAwsModelID to resolve inference profile
requirements and make the model appear in GetModelList (see awsModelIDMap,
modelProviderPatterns, getAwsModelID, awsModelsRequireInferenceProfile,
GetModelList/adaptor.go).

---

Outside diff comments:
In `@relay/channel/aws/adaptor.go`:
- Line 104: The URL construction in adaptor.go uses fmt.Sprintf with its
arguments swapped; update the fmt.Sprintf call that builds the Bedrock runtime
URL so the first %s is awsSecret[1] (region) and the second %s is awsModelId
(modelId) — i.e., swap the two arguments passed to fmt.Sprintf in the function
that returns the Bedrock runtime URL so the hostname contains the region and the
path contains the model ID.

In `@relay/channel/aws/relay-aws.go`:
- Line 5: Remove the direct "encoding/json" import and replace direct calls to
json.Unmarshal with the project wrapper common.Unmarshal (and json.Marshal with
common.Marshal if present); update the import block to remove "encoding/json"
and add the common package import, and change the call at the location using
json.Unmarshal to call common.Unmarshal with the same arguments so the file
relies on the common/json.go wrappers instead of the encoding/json package.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 64d2fda4-0bf4-4f69-b307-11e1c9a08d44

📥 Commits

Reviewing files that changed from the base of the PR and between 9bb2b6a and 4fa439a.

📒 Files selected for processing (4)
  • relay/channel/aws/adaptor.go
  • relay/channel/aws/constants.go
  • relay/channel/aws/relay-aws.go
  • relay/channel/aws/relay_aws_test.go

Comment on lines +68 to +69
// DeepSeek
"deepseek.r1-v1:0": true,

ghost Mar 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Missing awsModelIDMap entry for deepseek.r1-v1:0 causes incorrect invocation.

The model deepseek.r1-v1:0 is listed in awsModelsRequireInferenceProfile, indicating it requires the global. prefix for inference profile invocation. However:

  1. There is no short-name entry in awsModelIDMap for this model, so users must pass the full provider ID deepseek.r1-v1:0.
  2. When passed as deepseek.r1-v1:0, Layer 1 in getAwsModelID() detects the deepseek. provider prefix and returns skipInferenceResolve=true, bypassing the inference profile check entirely.
  3. The model will be invoked without the global. prefix, potentially causing an API error since it requires an inference profile.

Additionally, this model won't appear in GetModelList() (see adaptor.go:179-185).

🐛 Proposed fix: Add short-name mapping and pattern for DeepSeek

Add the mapping in awsModelIDMap:

 	"nova-sonic-v1:0":   "amazon.nova-sonic-v1:0",
+	// DeepSeek models
+	"deepseek-r1":       "deepseek.r1-v1:0",
 }

And add the pattern in modelProviderPatterns:

 var modelProviderPatterns = map[string]string{
 	"claude-": "anthropic.",
 	"nova-":   "amazon.",
+	"deepseek-": "deepseek.",
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@relay/channel/aws/constants.go` around lines 68 - 69, awsModelIDMap is
missing a short-name entry and modelProviderPatterns lacks a DeepSeek pattern,
causing getAwsModelID to treat "deepseek.r1-v1:0" as a provider-prefixed ID
(skipInferenceResolve=true) and bypass awsModelsRequireInferenceProfile logic;
add a short-name mapping for the DeepSeek model in awsModelIDMap (so callers can
use the short name) and add the DeepSeek regex to modelProviderPatterns so Layer
1 detection recognizes the provider correctly, which will allow getAwsModelID to
resolve inference profile requirements and make the model appear in GetModelList
(see awsModelIDMap, modelProviderPatterns, getAwsModelID,
awsModelsRequireInferenceProfile, GetModelList/adaptor.go).

@seefs001

ghost commented Mar 11, 2026

Copy link
Copy Markdown
Collaborator

会破坏现有的默认行为吗?把模型名和地区组装

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.