Skip to content

Alpha -> main#1920

Merged
Calcium-Ion merged 23 commits into
mainfrom
alpha
Sep 30, 2025
Merged

Alpha -> main#1920
Calcium-Ion merged 23 commits into
mainfrom
alpha

Conversation

@seefs001
Copy link
Copy Markdown
Collaborator

@seefs001 seefs001 commented Sep 29, 2025

Summary by CodeRabbit

  • New Features

    • Added a new "SubModel" channel, API type, and request adaptor with model list support.
  • UI

    • Enabled selecting "SubModel" in channel list and model selection modal.
    • Slightly delayed skeleton/loading indicators (200ms) in sidebar/header display.
  • Improvements

    • Added many new model entries and updated default pricing and cache ratios across providers.
  • Refactor

    • Some extra-service price descriptions now use raw string concatenation (removed runtime localization).
  • Chores

    • Updated internal mappings and cross-region model support.

danding5 and others added 15 commits September 8, 2025 16:21
# Conflicts:
#	common/api_type.go
#	constant/api_type.go
#	constant/channel.go
#	relay/relay_adaptor.go
#	web/src/constants/channel.constants.js
# Conflicts:
#	relay/relay_adaptor.go
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 29, 2025

Warning

Rate limit exceeded

@seefs001 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 8 minutes and 1 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 7d7ffc0 and 933ab43.

📒 Files selected for processing (7)
  • dto/claude.go (1 hunks)
  • relay/channel/aws/adaptor.go (1 hunks)
  • relay/channel/claude/adaptor.go (2 hunks)
  • relay/common/relay_info.go (2 hunks)
  • web/src/i18n/locales/en.json (1 hunks)
  • web/src/i18n/locales/fr.json (1 hunks)
  • web/src/pages/Setting/Model/SettingClaudeModel.jsx (1 hunks)

Walkthrough

Adds Submodel channel (ID 53) support across backend and frontend: new constants and mapping, ChannelBaseURLs entry, Submodel adaptor and constants, wiring in relay adaptor, model/price/cache entries, UI channel option and tag-modal presets, and some frontend render string adjustments.

Changes

Cohort / File(s) Summary
API / Channel constants & mapping
common/api_type.go, constant/api_type.go, constant/channel.go
Adds APITypeSubmodel and ChannelTypeSubmodel = 53, maps channel→API type, and appends "https://llm.submodel.ai" to ChannelBaseURLs.
Submodel adaptor & constants
relay/channel/submodel/adaptor.go, relay/channel/submodel/constants.go
New submodel.Adaptor with request/response conversion, header/url setup, DoRequest/DoResponse, GetModelList/GetChannelName; adds ModelList and ChannelName. Most non-OpenAI endpoints return "endpoint not supported".
Relay adaptor wiring
relay/relay_adaptor.go
Returns submodel.Adaptor{} for APITypeSubmodel and adds import.
Model lists & pricing/cache ratios
setting/ratio_setting/model_ratio.go, setting/ratio_setting/cache_ratio.go
Adds multiple Submodel-related model entries to defaultModelRatio, defaultCacheRatio, and defaultCreateCacheRatio.
Claude / AWS / Vertex model mappings
relay/channel/aws/constants.go, relay/channel/claude/constants.go, relay/channel/vertex/adaptor.go
Adds "claude-sonnet-4-5-20250929" (and -thinking variant) to maps/lists and AWS cross-region mappings; updates vertex claudeModelMap.
Web UI: channel option & presets
web/src/constants/channel.constants.js, web/src/components/table/channels/modals/EditTagModal.jsx
Adds channel option { value: 53, color: 'blue', label: 'SubModel' } and case 53 to populate localModels with an extensive model list.
Web UI: render strings
web/src/helpers/render.jsx
Replaces several i18next.t calls for extra service descriptions with raw concatenated string literals (removes runtime localization for those lines).
Frontend loading behavior timing & sidebar coordination
web/src/components/layout/SiderBar.jsx, web/src/hooks/common/useHeaderBar.js, web/src/hooks/common/useSidebar.js
Introduces 200ms minimum loading time for skeleton/header loading and adds per-instance sidebar refresh coordination, instance IDs, and skip-loader behavior for cross-instance events.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant RelayAPI as Relay API
  participant Adaptor as Submodel Adaptor
  participant Submodel as Submodel API

  Client->>RelayAPI: HTTP request (channel=Submodel, model, body)
  RelayAPI->>Adaptor: GetAdaptor(APITypeSubmodel) → Convert* / GetRequestURL
  Note over RelayAPI,Adaptor: Setup headers (Authorization) and request URL
  RelayAPI->>Submodel: DoRequest(body, headers)
  Submodel-->>RelayAPI: HTTP response (stream or non-stream)
  RelayAPI->>Adaptor: DoResponse(resp, info)
  alt streaming
    Adaptor-->>Client: stream via OpenAI-style handler
  else non-stream
    Adaptor-->>Client: JSON response via OpenAI handler
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

In a burrow I bounce with a keyboard and grin,
A Submodel door opens — new hops begin.
Lists and headers and a URL so neat,
Streams or JSON, each request I greet.
Carrots, tests, and deploy — a tiny celebratory spin. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The provided title “Alpha -> main” simply indicates the branch merge direction rather than summarizing the substantive changes introduced by the pull request, such as adding the Submodel channel and associated adaptor and constants. It fails to convey any meaningful context about the new feature or its intent, making it unclear to reviewers what the primary change set is. Please revise the title to clearly and concisely reflect the main change, for example “feat: add Submodel channel support” so that a teammate scanning the history immediately understands the purpose of this pull request.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

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
🧪 Early access (Sonnet 4.5): enabled

We are currently testing the Sonnet 4.5 model, which is expected to improve code review quality. However, this model may lead to increased noise levels in the review comments. Please disable the early access features if the noise level causes any inconvenience.

Note:

  • Public repositories are always opted into early access features.
  • You can enable or disable early access features from the CodeRabbit UI or by updating the CodeRabbit configuration file.

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (6)
web/src/constants/channel.constants.js (1)

162-166: Channel option added—OK Entry for value 53 looks good. Optional: align the display casing to “Submodel” to match the backend (ChannelTypeSubmodel) and consider adding i18n support.

relay/channel/submodel/constants.go (2)

3-14: Add brief doc and note sync with pricing ratios.

These identifiers are exported. Add short comments and a reminder to keep in sync with defaultModelRatio to avoid drift.

 package submodel

- var ModelList = []string{
+// ModelList enumerates supported upstream submodels exposed by the relay.
+// Keep in sync with setting/ratio_setting/model_ratio.go: defaultModelRatio.
+var ModelList = []string{

16-16: Name is fine; add a short doc comment.

-const ChannelName = "submodel"
+// ChannelName is the canonical identifier for this channel.
+const ChannelName = "submodel"
setting/ratio_setting/model_ratio.go (2)

254-265: Reduce brittleness from exact versioned names (optional).

Introduce lightweight normalization so minor version bumps don’t fall back to the 37.5 default. For example:

 func FormatMatchingModelName(name string) string {
@@
   if strings.HasPrefix(name, "gpt-4o-gizmo") {
     name = "gpt-4o-gizmo-*"
   }
+  // Submodel families: normalize common versioned variants.
+  if strings.HasPrefix(name, "deepseek-ai/DeepSeek-V3") {
+    // e.g., DeepSeek-V3-0324, DeepSeek-V3.1 -> map to a canonical key
+    name = "deepseek-ai/DeepSeek-V3"
+  }
+  if strings.HasPrefix(name, "openai/gpt-oss-120b") {
+    name = "openai/gpt-oss-120b"
+  }
   return name
 }

Then add a corresponding umbrella entry in defaultModelRatio:

   "deepseek-ai/DeepSeek-V3-0324":                 0.8,
-  "deepseek-ai/DeepSeek-V3.1":                    0.8,
+  "deepseek-ai/DeepSeek-V3.1":                    0.8,
+  "deepseek-ai/DeepSeek-V3":                      0.8,

This keeps existing exact keys working while providing a safe family fallback.


254-265: Avoid aliasing defaults into mutable runtime maps (optional hardening).

During InitRatioSettings, modelRatioMap currently points at defaultModelRatio. Prefer a deep copy to prevent accidental edits to defaults.

// In InitRatioSettings(), replace:
// modelRatioMap = defaultModelRatio
// with:
modelRatioMap = make(map[string]float64, len(defaultModelRatio))
for k, v := range defaultModelRatio {
    modelRatioMap[k] = v
}
relay/channel/submodel/adaptor.go (1)

19-21: Consider marking unused parameters explicitly

The function doesn't use info and request parameters. Consider using underscore prefix to make the intent clear.

-func (a *Adaptor) ConvertGeminiRequest(c *gin.Context, info *relaycommon.RelayInfo, request *dto.GeminiChatRequest) (any, error) {
+func (a *Adaptor) ConvertGeminiRequest(c *gin.Context, _ *relaycommon.RelayInfo, _ *dto.GeminiChatRequest) (any, error) {
 	return nil, errors.New("submodel channel: endpoint not supported")
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4bb4b64 and 25a3896.

📒 Files selected for processing (10)
  • common/api_type.go (1 hunks)
  • constant/api_type.go (1 hunks)
  • constant/channel.go (2 hunks)
  • relay/channel/submodel/adaptor.go (1 hunks)
  • relay/channel/submodel/constants.go (1 hunks)
  • relay/relay_adaptor.go (2 hunks)
  • setting/ratio_setting/model_ratio.go (1 hunks)
  • web/src/components/table/channels/modals/EditTagModal.jsx (1 hunks)
  • web/src/constants/channel.constants.js (1 hunks)
  • web/src/helpers/render.jsx (4 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-08-05T17:14:17.246Z
Learnt from: neotf
PR: QuantumNous/new-api#1511
File: setting/ratio_setting/model_ratio.go:118-123
Timestamp: 2025-08-05T17:14:17.246Z
Learning: Claude models handle "-thinking" variants differently from Gemini models. For Claude models, only the base model (without "-thinking") gets an entry in defaultModelRatio map. The "-thinking" variants rely on the Claude relay handler stripping the suffix using strings.TrimSuffix(textRequest.Model, "-thinking") before looking up the ratio, so they automatically use the base model's ratio.

Applied to files:

  • setting/ratio_setting/model_ratio.go
📚 Learning: 2025-08-27T02:15:25.448Z
Learnt from: AAEE86
PR: QuantumNous/new-api#1658
File: web/src/components/table/channels/modals/EditChannelModal.jsx:555-569
Timestamp: 2025-08-27T02:15:25.448Z
Learning: In EditChannelModal.jsx, the applyModelMapping function transforms the models list by replacing original model names (mapping values) with display names (mapping keys). The database stores this transformed list containing mapped keys. On channel load, data.models contains these mapped display names, making the initialization filter if (data.models.includes(key)) correct.

Applied to files:

  • web/src/components/table/channels/modals/EditTagModal.jsx
📚 Learning: 2025-08-27T02:15:25.448Z
Learnt from: AAEE86
PR: QuantumNous/new-api#1658
File: web/src/components/table/channels/modals/EditChannelModal.jsx:555-569
Timestamp: 2025-08-27T02:15:25.448Z
Learning: In EditChannelModal.jsx, the database stores mapped keys (display names) in the models field after applying model mapping transformations. When loading a channel, data.models contains the mapped keys, not the original model names. The filtering logic if (data.models.includes(key)) in the initialization is correct.

Applied to files:

  • web/src/components/table/channels/modals/EditTagModal.jsx
🧬 Code graph analysis (4)
web/src/helpers/render.jsx (1)
web/src/hooks/model-pricing/useModelPricingData.jsx (1)
  • groupRatio (50-50)
relay/relay_adaptor.go (3)
constant/api_type.go (1)
  • APITypeSubmodel (35-35)
relay/channel/submodel/adaptor.go (1)
  • Adaptor (16-17)
relay/channel/adapter.go (1)
  • Adaptor (13-30)
relay/channel/submodel/adaptor.go (6)
relay/common/relay_info.go (1)
  • RelayInfo (74-120)
relay/common/relay_utils.go (1)
  • GetFullRequestURL (22-34)
relay/channel/api_request.go (2)
  • SetupApiRequestHeader (25-37)
  • DoApiRequest (39-72)
dto/openai_request.go (1)
  • GeneralOpenAIRequest (25-78)
relay/channel/openai/relay-openai.go (2)
  • OaiStreamHandler (112-176)
  • OpenaiHandler (178-274)
relay/channel/submodel/constants.go (2)
  • ModelList (3-14)
  • ChannelName (16-16)
common/api_type.go (2)
constant/channel.go (1)
  • ChannelTypeSubmodel (53-53)
constant/api_type.go (1)
  • APITypeSubmodel (35-35)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Push Docker image to multiple registries
🔇 Additional comments (7)
constant/api_type.go (1)

34-36: Enum insertion before count sentinel looks correct

Placing APITypeSubmodel before APITypeDummy preserves prior values and grows the count as intended. No issues spotted.

If any code persists raw APIType ints (DB/config), confirm no assumptions about APITypeDummy’s previous value.

relay/relay_adaptor.go (1)

40-41: Adaptor implements full channel.Adaptor interface
submodel.Adaptor defines all 15 methods required by channel.Adaptor; no gaps detected.

common/api_type.go (1)

70-71: Submodel mapping verified

Forward (ChannelTypeSubmodel→APITypeSubmodel) and reverse (APITypeSubmodel→submodel.Adaptor) cases are present. Confirm any other APIType→ChannelType or base-URL mappings include Submodel as well.

constant/channel.go (2)

53-53: Index addition looks correct.

Submodel inserted before Dummy; ordinal is 53 as expected.

Consider a tiny test asserting len(ChannelBaseURLs) == int(ChannelTypeDummy)+0 to catch future off‑by‑one slips.


113-114: Base URL mapping added at the correct slot.

Entry aligns with index 53. Ensure downstream config/env overrides, if any, still take precedence.

setting/ratio_setting/model_ratio.go (1)

254-265: Unable to auto-verify submodel ratios and constants
The verification script failed with “/dev/fd/63: No such file or directory.” Please manually confirm that every entry in relay/channel/submodel/constants.go:ModelList has a matching ratio in setting/ratio_setting/model_ratio.go, and that ChannelTypeSubmodel = 53 and the base URL slot comment (//53) are correct.

relay/channel/submodel/adaptor.go (1)

80-86: Resolved: Constants Defined

ModelList and ChannelName are declared in relay/channel/submodel/constants.go; no further changes needed.

Comment on lines +121 to +123
case 53:
localModels = ['NousResearch/Hermes-4-405B-FP8', 'Qwen/Qwen3-235B-A22B-Thinking-2507', 'Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8','Qwen/Qwen3-235B-A22B-Instruct-2507', 'zai-org/GLM-4.5-FP8', 'openai/gpt-oss-120b', 'deepseek-ai/DeepSeek-R1-0528', 'deepseek-ai/DeepSeek-R1', 'deepseek-ai/DeepSeek-V3-0324', 'deepseek-ai/DeepSeek-V3.1'];
break;
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.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Likely unreachable preset + risk of model-name drift

  • This switch runs only when name === 'type', but this modal doesn’t expose a 'type' field; the new case 53 likely never executes. Either wire a 'type' input here or remove and centralize model presets.
  • Keep model IDs in one source of truth to avoid drift with backend relay/channel/submodel/constants.go ModelList.

Two options:

  • Prefer: rely on helpers and add mapping there, then call getChannelModels(value) here for 53 as well.
  • Or: keep list here but add a comment and a test to catch divergence.

🏁 Script executed:

#!/bin/bash
# Check if 'type' is used in this component
rg -n "field=['\"]type['\"]|handleInputChange\('type'" web/src/components/table/channels/modals/EditTagModal.jsx

# Cross‑repo sanity: ensure backend lists Submodel models to match UI
rg -n "ModelList|ChannelName|Submodel" relay/channel/submodel

Length of output: 407


Remove or wire unreachable case 53 preset in EditTagModal.jsx

  • The switch branch for case 53 under handleInputChange('type', …) never executes because this modal doesn’t expose a 'type' field.
  • Either remove the hard-coded preset here and centralize presets via the getChannelModels helper (including case 53), or add a proper 'type' input to this component.
  • To prevent drift, reference the shared ModelList in relay/channel/submodel/constants.go instead of duplicating the list.
🤖 Prompt for AI Agents
In web/src/components/table/channels/modals/EditTagModal.jsx around lines
121-123, there is an unreachable hard-coded `case 53` preset in the switch
handling handleInputChange('type', ...) that never executes because this modal
does not expose a 'type' input; remove this dead branch and instead obtain
presets from the shared helper (getChannelModels) or wire the component with a
real 'type' input if intended to support it; update the code to reference the
centralized ModelList in relay/channel/submodel/constants.go (or the existing
helper that reads it) rather than duplicating the array so presets remain
single-sourced and avoid future drift.

Comment on lines +1203 to +1210
' + Web搜索 {{count}}次 / 1K 次 * ${{price}} * {{ratioType}} {{ratio}}',
{
count: webSearchCallCount,
price: webSearchPrice,
ratio: groupRatio,
ratioType: ratioLabel,
},
)
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

Fix broken localization for Web search description

The change from i18next.t() to string concatenation breaks internationalization and template interpolation. The placeholders like {{count}}, {{price}}, {{ratio}}, and {{ratioType}} will appear as literal text instead of being replaced with actual values.

Apply this fix to restore proper localization:

-                  ? ' + Web搜索 {{count}}次 / 1K 次 * ${{price}} * {{ratioType}} {{ratio}}'
-                    {
-                      count: webSearchCallCount,
-                      price: webSearchPrice,
-                      ratio: groupRatio,
-                      ratioType: ratioLabel,
-                    },
-                  )
+                  ? i18next.t(
+                      ' + Web搜索 {{count}}次 / 1K 次 * ${{price}} * {{ratioType}} {{ratio}}',
+                      {
+                        count: webSearchCallCount,
+                        price: webSearchPrice,
+                        ratio: groupRatio,
+                        ratioType: ratioLabel,
+                      },
+                    )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
' + Web搜索 {{count}}次 / 1K 次 * ${{price}} * {{ratioType}} {{ratio}}',
{
count: webSearchCallCount,
price: webSearchPrice,
ratio: groupRatio,
ratioType: ratioLabel,
},
)
? i18next.t(
' + Web搜索 {{count}}次 / 1K 次 * ${{price}} * {{ratioType}} {{ratio}}',
{
count: webSearchCallCount,
price: webSearchPrice,
ratio: groupRatio,
ratioType: ratioLabel,
},
)
🤖 Prompt for AI Agents
In web/src/helpers/render.jsx around lines 1203 to 1210, the code uses string
concatenation which breaks i18next interpolation so placeholders like {{count}},
{{price}}, {{ratio}} and {{ratioType}} are rendered literally; replace the
concatenated string with a single i18next.t(...) call that uses the translation
key/string containing those placeholders and pass the interpolation object ({
count: webSearchCallCount, price: webSearchPrice, ratio: groupRatio, ratioType:
ratioLabel }) as the second argument so i18next performs proper template
replacement instead of concatenation.

Comment on lines +1214 to +1221
' + 文件搜索 {{count}}次 / 1K 次 * ${{price}} * {{ratioType}} {{ratio}}',
{
count: fileSearchCallCount,
price: fileSearchPrice,
ratio: groupRatio,
ratioType: ratioLabel,
},
)
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

Fix broken localization for file search description

Same issue as the Web search case - removing i18next.t() breaks both localization and template variable interpolation.

Apply this fix:

-                  ? ' + 文件搜索 {{count}}次 / 1K 次 * ${{price}} * {{ratioType}} {{ratio}}'
-                    {
-                      count: fileSearchCallCount,
-                      price: fileSearchPrice,
-                      ratio: groupRatio,
-                      ratioType: ratioLabel,
-                    },
-                  )
+                  ? i18next.t(
+                      ' + 文件搜索 {{count}}次 / 1K 次 * ${{price}} * {{ratioType}} {{ratio}}',
+                      {
+                        count: fileSearchCallCount,
+                        price: fileSearchPrice,
+                        ratio: groupRatio,
+                        ratioType: ratioLabel,
+                      },
+                    )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
' + 文件搜索 {{count}}次 / 1K 次 * ${{price}} * {{ratioType}} {{ratio}}',
{
count: fileSearchCallCount,
price: fileSearchPrice,
ratio: groupRatio,
ratioType: ratioLabel,
},
)
? i18next.t(
' + 文件搜索 {{count}}次 / 1K 次 * ${{price}} * {{ratioType}} {{ratio}}',
{
count: fileSearchCallCount,
price: fileSearchPrice,
ratio: groupRatio,
ratioType: ratioLabel,
},
)
🤖 Prompt for AI Agents
In web/src/helpers/render.jsx around lines 1214 to 1221, the string
concatenation removed the i18next.t() call which breaks localization and
template interpolation; restore usage of i18next.t() (or the correct translation
key) and pass the template variables as the second argument object (count:
fileSearchCallCount, price: fileSearchPrice, ratio: groupRatio, ratioType:
ratioLabel) so i18next performs the interpolation and pluralization correctly.

Comment on lines +1460 to +1470
'文字提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}',
{
nonCacheInput: inputTokens - cacheTokens,
cacheInput: cacheTokens,
cachePrice: inputRatioPrice * cacheRatio,
price: inputRatioPrice,
completion: completionTokens,
compPrice: completionRatioPrice,
total: textPrice.toFixed(6),
},
)
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

Fix broken localization for cached text pricing description

The removal of i18next.t() breaks localization and variable interpolation for the cached text pricing calculation.

Apply this fix:

-              ? '文字提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}'
-                {
-                  nonCacheInput: inputTokens - cacheTokens,
-                  cacheInput: cacheTokens,
-                  cachePrice: inputRatioPrice * cacheRatio,
-                  price: inputRatioPrice,
-                  completion: completionTokens,
-                  compPrice: completionRatioPrice,
-                  total: textPrice.toFixed(6),
-                },
-              )
+              ? i18next.t(
+                  '文字提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}',
+                  {
+                    nonCacheInput: inputTokens - cacheTokens,
+                    cacheInput: cacheTokens,
+                    cachePrice: inputRatioPrice * cacheRatio,
+                    price: inputRatioPrice,
+                    completion: completionTokens,
+                    compPrice: completionRatioPrice,
+                    total: textPrice.toFixed(6),
+                  },
+                )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'文字提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}',
{
nonCacheInput: inputTokens - cacheTokens,
cacheInput: cacheTokens,
cachePrice: inputRatioPrice * cacheRatio,
price: inputRatioPrice,
completion: completionTokens,
compPrice: completionRatioPrice,
total: textPrice.toFixed(6),
},
)
? i18next.t(
'文字提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}',
{
nonCacheInput: inputTokens - cacheTokens,
cacheInput: cacheTokens,
cachePrice: inputRatioPrice * cacheRatio,
price: inputRatioPrice,
completion: completionTokens,
compPrice: completionRatioPrice,
total: textPrice.toFixed(6),
},
)

Comment on lines +1472 to +1480
'文字提示 {{input}} tokens / 1M tokens * ${{price}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}',
{
input: inputTokens,
price: inputRatioPrice,
completion: completionTokens,
compPrice: completionRatioPrice,
total: textPrice.toFixed(6),
},
)}
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

Fix broken localization for non-cached text pricing description

Another instance of removed i18next.t() that breaks localization and template interpolation.

Apply this fix:

-              : '文字提示 {{input}} tokens / 1M tokens * ${{price}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}'
-                {
-                  input: inputTokens,
-                  price: inputRatioPrice,
-                  completion: completionTokens,
-                  compPrice: completionRatioPrice,
-                  total: textPrice.toFixed(6),
-                },
-              )}
+              : i18next.t(
+                  '文字提示 {{input}} tokens / 1M tokens * ${{price}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}',
+                  {
+                    input: inputTokens,
+                    price: inputRatioPrice,
+                    completion: completionTokens,
+                    compPrice: completionRatioPrice,
+                    total: textPrice.toFixed(6),
+                  },
+                )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'文字提示 {{input}} tokens / 1M tokens * ${{price}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}',
{
input: inputTokens,
price: inputRatioPrice,
completion: completionTokens,
compPrice: completionRatioPrice,
total: textPrice.toFixed(6),
},
)}
: i18next.t(
'文字提示 {{input}} tokens / 1M tokens * ${{price}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}',
{
input: inputTokens,
price: inputRatioPrice,
completion: completionTokens,
compPrice: completionRatioPrice,
total: textPrice.toFixed(6),
},
)}
🤖 Prompt for AI Agents
In web/src/helpers/render.jsx around lines 1472 to 1480, the localization call
was replaced with a raw string which breaks i18next interpolation; restore the
i18next.t(...) call around the translated string and pass the interpolation
object as the second argument (input, price, completion, compPrice, total) so
i18next can replace {{input}}, {{price}}, {{completion}}, {{compPrice}}, and
{{total}} correctly; ensure total is formatted (toFixed) before passing and that
the translation key/string matches the placeholders in the i18n resource.

Comment on lines +1620 to +1636
'提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * ${{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * {{ratioType}} {{ratio}} = ${{total}}',
{
nonCacheInput: nonCachedTokens,
cacheInput: cacheTokens,
cacheRatio: cacheRatio,
cacheCreationInput: cacheCreationTokens,
cacheCreationRatio: cacheCreationRatio,
cachePrice: cacheRatioPrice,
cacheCreationPrice: cacheCreationRatioPrice,
price: inputRatioPrice,
completion: completionTokens,
compPrice: completionRatioPrice,
ratio: groupRatio,
ratioType: ratioLabel,
total: price.toFixed(6),
},
)
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

Fix broken localization for Claude cached pricing description

The removal of i18next.t() breaks localization and template interpolation for Claude model pricing calculations.

Apply this fix:

-              ? '提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * ${{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * {{ratioType}} {{ratio}} = ${{total}}'
-                {
-                  nonCacheInput: nonCachedTokens,
-                  cacheInput: cacheTokens,
-                  cacheRatio: cacheRatio,
-                  cacheCreationInput: cacheCreationTokens,
-                  cacheCreationRatio: cacheCreationRatio,
-                  cachePrice: cacheRatioPrice,
-                  cacheCreationPrice: cacheCreationRatioPrice,
-                  price: inputRatioPrice,
-                  completion: completionTokens,
-                  compPrice: completionRatioPrice,
-                  ratio: groupRatio,
-                  ratioType: ratioLabel,
-                  total: price.toFixed(6),
-                },
-              )
+              ? i18next.t(
+                  '提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * ${{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * {{ratioType}} {{ratio}} = ${{total}}',
+                  {
+                    nonCacheInput: nonCachedTokens,
+                    cacheInput: cacheTokens,
+                    cacheRatio: cacheRatio,
+                    cacheCreationInput: cacheCreationTokens,
+                    cacheCreationRatio: cacheCreationRatio,
+                    cachePrice: cacheRatioPrice,
+                    cacheCreationPrice: cacheCreationRatioPrice,
+                    price: inputRatioPrice,
+                    completion: completionTokens,
+                    compPrice: completionRatioPrice,
+                    ratio: groupRatio,
+                    ratioType: ratioLabel,
+                    total: price.toFixed(6),
+                  },
+                )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * ${{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * {{ratioType}} {{ratio}} = ${{total}}',
{
nonCacheInput: nonCachedTokens,
cacheInput: cacheTokens,
cacheRatio: cacheRatio,
cacheCreationInput: cacheCreationTokens,
cacheCreationRatio: cacheCreationRatio,
cachePrice: cacheRatioPrice,
cacheCreationPrice: cacheCreationRatioPrice,
price: inputRatioPrice,
completion: completionTokens,
compPrice: completionRatioPrice,
ratio: groupRatio,
ratioType: ratioLabel,
total: price.toFixed(6),
},
)
? i18next.t(
'提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * ${{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * {{ratioType}} {{ratio}} = ${{total}}',
{
nonCacheInput: nonCachedTokens,
cacheInput: cacheTokens,
cacheRatio: cacheRatio,
cacheCreationInput: cacheCreationTokens,
cacheCreationRatio: cacheCreationRatio,
cachePrice: cacheRatioPrice,
cacheCreationPrice: cacheCreationRatioPrice,
price: inputRatioPrice,
completion: completionTokens,
compPrice: completionRatioPrice,
ratio: groupRatio,
ratioType: ratioLabel,
total: price.toFixed(6),
},
)
🤖 Prompt for AI Agents
In web/src/helpers/render.jsx around lines 1620-1636 the localization string was
left as a raw template which breaks i18next interpolation; wrap the string with
i18next.t(...) and fix the interpolation object keys to exactly match the
placeholders in the string: provide nonCacheInput: nonCachedTokens, price:
inputRatioPrice, cacheInput: cacheTokens, cachePrice: cacheRatioPrice,
cacheCreationInput: cacheCreationTokens, cacheCreationPrice:
cacheCreationRatioPrice, completion: completionTokens, compPrice:
completionRatioPrice, ratioType: ratioLabel, ratio: groupRatio, total:
price.toFixed(6).

Comment on lines +1638 to +1647
'提示 {{input}} tokens / 1M tokens * ${{price}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * {{ratioType}} {{ratio}} = ${{total}}',
{
input: inputTokens,
price: inputRatioPrice,
completion: completionTokens,
compPrice: completionRatioPrice,
ratio: groupRatio,
ratioType: ratioLabel,
total: price.toFixed(6),
},
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

Fix broken localization for Claude non-cached pricing description

Final instance of removed i18next.t() that breaks localization and template interpolation.

Apply this fix:

-              : '提示 {{input}} tokens / 1M tokens * ${{price}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * {{ratioType}} {{ratio}} = ${{total}}'
-                {
-                  input: inputTokens,
-                  price: inputRatioPrice,
-                  completion: completionTokens,
-                  compPrice: completionRatioPrice,
-                  ratio: groupRatio,
-                  ratioType: ratioLabel,
-                  total: price.toFixed(6),
-                },
-              )}
+              : i18next.t(
+                  '提示 {{input}} tokens / 1M tokens * ${{price}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * {{ratioType}} {{ratio}} = ${{total}}',
+                  {
+                    input: inputTokens,
+                    price: inputRatioPrice,
+                    completion: completionTokens,
+                    compPrice: completionRatioPrice,
+                    ratio: groupRatio,
+                    ratioType: ratioLabel,
+                    total: price.toFixed(6),
+                  },
+                )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'提示 {{input}} tokens / 1M tokens * ${{price}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * {{ratioType}} {{ratio}} = ${{total}}',
{
input: inputTokens,
price: inputRatioPrice,
completion: completionTokens,
compPrice: completionRatioPrice,
ratio: groupRatio,
ratioType: ratioLabel,
total: price.toFixed(6),
},
// … other cases …
: i18next.t(
'提示 {{input}} tokens / 1M tokens * ${{price}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * {{ratioType}} {{ratio}} = ${{total}}',
{
input: inputTokens,
price: inputRatioPrice,
completion: completionTokens,
compPrice: completionRatioPrice,
ratio: groupRatio,
ratioType: ratioLabel,
total: price.toFixed(6),
},
)}
// … following cases …
🤖 Prompt for AI Agents
In web/src/helpers/render.jsx around lines 1638 to 1647, the localization call
was removed so the pricing description string is no longer passed through
i18next and template interpolation fails; restore i18next.t(...) around the
template string and pass the existing interpolation object as the second
argument (keeping keys input, price, completion, compPrice, ratio, ratioType,
total) so i18next performs interpolation and localization correctly.

@seefs001 seefs001 changed the title feat: submodel channel Alpha -> main Sep 30, 2025
@Calcium-Ion Calcium-Ion merged commit 595e3fe into main Sep 30, 2025
2 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Oct 18, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Oct 28, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Jan 15, 2026
This was referenced Apr 20, 2026
x22x22 pushed a commit to x22x22/new-api that referenced this pull request Apr 24, 2026
jiutubaba pushed a commit to jiutubaba/fx-api that referenced this pull request May 17, 2026
…earch-tool-types

fix(apicompat): recognize web_search_20250305 / google_search in Responses→Anthropic tool conversion
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.

5 participants