Alpha -> main#1920
Conversation
# 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>
add submodel.ai
fix : fix submodel adapter
|
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 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. 📒 Files selected for processing (7)
WalkthroughAdds 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
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. 🧪 Early access (Sonnet 4.5): enabledWe 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:
Comment |
There was a problem hiding this comment.
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 explicitlyThe function doesn't use
infoandrequestparameters. 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
📒 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 correctPlacing 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 verifiedForward (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 inrelay/channel/submodel/constants.go:ModelListhas a matching ratio insetting/ratio_setting/model_ratio.go, and thatChannelTypeSubmodel = 53and the base URL slot comment (//53) are correct.relay/channel/submodel/adaptor.go (1)
80-86: Resolved: Constants DefinedModelList and ChannelName are declared in relay/channel/submodel/constants.go; no further changes needed.
| 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; |
There was a problem hiding this comment.
🛠️ 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/submodelLength of output: 407
Remove or wire unreachable case 53 preset in EditTagModal.jsx
- The
switchbranch forcase 53underhandleInputChange('type', …)never executes because this modal doesn’t expose a'type'field. - Either remove the hard-coded preset here and centralize presets via the
getChannelModelshelper (including case 53), or add a proper'type'input to this component. - To prevent drift, reference the shared
ModelListinrelay/channel/submodel/constants.goinstead 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.
| ' + Web搜索 {{count}}次 / 1K 次 * ${{price}} * {{ratioType}} {{ratio}}', | ||
| { | ||
| count: webSearchCallCount, | ||
| price: webSearchPrice, | ||
| ratio: groupRatio, | ||
| ratioType: ratioLabel, | ||
| }, | ||
| ) |
There was a problem hiding this comment.
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.
| ' + 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.
| ' + 文件搜索 {{count}}次 / 1K 次 * ${{price}} * {{ratioType}} {{ratio}}', | ||
| { | ||
| count: fileSearchCallCount, | ||
| price: fileSearchPrice, | ||
| ratio: groupRatio, | ||
| ratioType: ratioLabel, | ||
| }, | ||
| ) |
There was a problem hiding this comment.
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.
| ' + 文件搜索 {{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.
| '文字提示 {{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), | ||
| }, | ||
| ) |
There was a problem hiding this comment.
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.
| '文字提示 {{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), | |
| }, | |
| ) |
| '文字提示 {{input}} tokens / 1M tokens * ${{price}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}', | ||
| { | ||
| input: inputTokens, | ||
| price: inputRatioPrice, | ||
| completion: completionTokens, | ||
| compPrice: completionRatioPrice, | ||
| total: textPrice.toFixed(6), | ||
| }, | ||
| )} |
There was a problem hiding this comment.
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.
| '文字提示 {{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.
| '提示 {{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), | ||
| }, | ||
| ) |
There was a problem hiding this comment.
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.
| '提示 {{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).
| '提示 {{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), | ||
| }, |
There was a problem hiding this comment.
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.
| '提示 {{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.
feat: support claude-sonnet-4-5-20250929
fix: Optimize sidebar refresh to avoid redundant loading states
fix: claude beta=true
feat: claude context editing
Alpha -> main
…earch-tool-types fix(apicompat): recognize web_search_20250305 / google_search in Responses→Anthropic tool conversion
Summary by CodeRabbit
New Features
UI
Improvements
Refactor
Chores