Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions common/api_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ func ChannelType2APIType(channelType int) (int, bool) {
apiType = constant.APITypeJimeng
case constant.ChannelTypeMoonshot:
apiType = constant.APITypeMoonshot
case constant.ChannelTypeSubmodel:
apiType = constant.APITypeSubmodel
}
if apiType == -1 {
return constant.APITypeOpenAI, false
Expand Down
1 change: 1 addition & 0 deletions constant/api_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ const (
APITypeJimeng
APITypeMoonshot // this one is only for count, do not add any channel after this
APITypeDummy // this one is only for count, do not add any channel after this
APITypeSubmodel
)
Comment on lines +36 to 37

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.

💡 Verification agent

🧩 Analysis chain

Do not add after APITypeDummy — move APITypeSubmodel before Dummy.

APITypeDummy is a sentinel (“do not add any channel after this”). Adding Submodel after Dummy breaks that contract and may exclude it from counts/loops.

Apply this reordering:

  APITypeJimeng
- APITypeMoonshot // this one is only for count, do not add any channel after this
- APITypeDummy    // this one is only for count, do not add any channel after this
- APITypeSubmodel
+ APITypeMoonshot
+ APITypeSubmodel
+ APITypeDummy    // this one is only for count, do not add any channel after this

🏁 Script executed:

#!/bin/bash
# Check for places treating APITypeDummy as an upper bound
rg -nP '(APITypeDummy).*?do not add|for\s*\(.*<\s*constant\.APITypeDummy' -C2

Length of output: 317


Move APITypeSubmodel before APITypeDummy (constant/api_type.go:34-36)

APITypeDummy is a sentinel ("do not add any channel after this"); Submodel after it violates that contract and can be excluded from counts/loops.

  APITypeJimeng
- APITypeMoonshot // this one is only for count, do not add any channel after this
- APITypeDummy    // this one is only for count, do not add any channel after this
- APITypeSubmodel
+ APITypeMoonshot
+ APITypeSubmodel
+ APITypeDummy    // this one is only for count, do not add any channel after this
📝 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
APITypeSubmodel
)
APITypeJimeng
APITypeMoonshot
APITypeSubmodel
APITypeDummy // this one is only for count, do not add any channel after this
)
🤖 Prompt for AI Agents
In constant/api_type.go around lines 34 to 37, APITypeSubmodel is declared after
the sentinel APITypeDummy which breaks the sentinel contract; move the
APITypeSubmodel constant declaration so it appears before APITypeDummy (i.e.,
reorder the constants so APITypeDummy remains the final sentinel value), and
ensure any comments/documentation reflect that APITypeDummy is the terminal
sentinel and that Submodel is excluded from sentinel-based counts/loops.

3 changes: 3 additions & 0 deletions constant/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@ const (
ChannelTypeKling = 50
ChannelTypeJimeng = 51
ChannelTypeVidu = 52
ChannelTypeSubmodel = 53
ChannelTypeDummy // this one is only for count, do not add any channel after this


)

var ChannelBaseURLs = []string{
Expand Down Expand Up @@ -108,4 +110,5 @@ var ChannelBaseURLs = []string{
"https://api.klingai.com", //50
"https://visual.volcengineapi.com", //51
"https://api.vidu.cn", //52
"https://llm.submodel.ai", //53
}
82 changes: 82 additions & 0 deletions relay/channel/submodel/adaptor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package submodel

import (
"errors"
"io"
"net/http"
"one-api/dto"
"one-api/relay/channel"
"one-api/relay/channel/openai"
relaycommon "one-api/relay/common"
"one-api/types"

"github.com/gin-gonic/gin"
)

type Adaptor struct {
}

func (a *Adaptor) ConvertClaudeRequest(*gin.Context, *relaycommon.RelayInfo, *dto.ClaudeRequest) (any, error) {
return nil, errors.New("submodel channel: endpoint not supported")
}

func (a *Adaptor) ConvertAudioRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.AudioRequest) (io.Reader, error) {
return nil, errors.New("submodel channel: endpoint not supported")
}

func (a *Adaptor) ConvertImageRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.ImageRequest) (any, error) {
return nil, errors.New("submodel channel: endpoint not supported")
}

func (a *Adaptor) Init(info *relaycommon.RelayInfo) {
}

func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
return relaycommon.GetFullRequestURL(info.BaseUrl, info.RequestURLPath, info.ChannelType), nil
}

func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Header, info *relaycommon.RelayInfo) error {
channel.SetupApiRequestHeader(info, c, req)
req.Set("Authorization", "Bearer "+info.ApiKey)
return nil
}

func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayInfo, request *dto.GeneralOpenAIRequest) (any, error) {
if request == nil {
return nil, errors.New("request is nil")
}
return request, nil
}

func (a *Adaptor) ConvertRerankRequest(c *gin.Context, relayMode int, request dto.RerankRequest) (any, error) {
return nil, errors.New("submodel channel: endpoint not supported")
}

func (a *Adaptor) ConvertEmbeddingRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.EmbeddingRequest) (any, error) {
return nil, errors.New("submodel channel: endpoint not supported")
}

func (a *Adaptor) ConvertOpenAIResponsesRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.OpenAIResponsesRequest) (any, error) {
return nil, errors.New("submodel channel: endpoint not supported")
}

func (a *Adaptor) DoRequest(c *gin.Context, info *relaycommon.RelayInfo, requestBody io.Reader) (any, error) {
return channel.DoApiRequest(a, c, info, requestBody)
}

func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (usage any, err *types.NewAPIError) {
if info.IsStream {
usage, err = openai.OaiStreamHandler(c, info, resp)
} else {
usage, err = openai.OpenaiHandler(c, info, resp)
}
return
}

func (a *Adaptor) GetModelList() []string {
return ModelList
}

func (a *Adaptor) GetChannelName() string {
return ChannelName
}
16 changes: 16 additions & 0 deletions relay/channel/submodel/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package submodel

var ModelList = []string{
"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",
}

const ChannelName = "submodel"
6 changes: 6 additions & 0 deletions relay/relay_adaptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,12 @@ import (
"one-api/relay/channel/zhipu"
"one-api/relay/channel/zhipu_4v"
"strconv"
<<<<<<< HEAD
"one-api/relay/channel/submodel"
=======

"github.com/gin-gonic/gin"
>>>>>>> 4f760a8d407d321bf7f011331ecffb2744b555fd
Comment on lines +41 to +45

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

Unresolved merge conflict and missing imports — blocks build.

Conflict markers present; gin import is dropped; submodel import only on one side. Resolve and include both imports.

Apply this fix:

 import (
   "one-api/constant"
   "one-api/relay/channel"
   "one-api/relay/channel/ali"
@@
   "one-api/relay/channel/zhipu_4v"
   "strconv"
-<<<<<<< HEAD
-    "one-api/relay/channel/submodel"
-=======
-
-	"github.com/gin-gonic/gin"
->>>>>>> 4f760a8d407d321bf7f011331ecffb2744b555fd
+  "github.com/gin-gonic/gin"
+  "one-api/relay/channel/submodel"
 )
📝 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
"one-api/relay/channel/submodel"
=======
"github.com/gin-gonic/gin"
>>>>>>> 4f760a8d407d321bf7f011331ecffb2744b555fd
"github.com/gin-gonic/gin"
"one-api/relay/channel/submodel"
🤖 Prompt for AI Agents
In relay/relay_adaptor.go around lines 41 to 45, remove the unresolved git
conflict markers and restore a proper import block that includes both
"one-api/relay/channel/submodel" and "github.com/gin-gonic/gin"; ensure there
are no leftover <<<<<<<, =======, >>>>>>> lines, place both packages as separate
entries in the import section, run gofmt/goimports to fix formatting and then
rebuild to confirm the missing import and conflict errors are resolved.

)

func GetAdaptor(apiType int) channel.Adaptor {
Expand Down Expand Up @@ -103,6 +107,8 @@ func GetAdaptor(apiType int) channel.Adaptor {
return &jimeng.Adaptor{}
case constant.APITypeMoonshot:
return &moonshot.Adaptor{} // Moonshot uses Claude API
case constant.APITypeSubmodel:
return &submodel.Adaptor{}
}
Comment on lines +110 to 112

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.

💡 Verification agent

🧩 Analysis chain

Adaptor wiring for SubModel — OK once imports are fixed.

Case is correct; will work after resolving imports.


🏁 Script executed:

#!/bin/bash
# Ensure no other conflict markers remain
rg -n '^(<{7}|={7}|>{7})' -S

Length of output: 147


Resolve git merge conflicts and fix imports in relay/relay_adaptor.go

Submodel adaptor case (lines 110–112) is correct, but relay/relay_adaptor.go contains unresolved git conflict markers at lines 40, 42, 45 — remove the conflict markers and fix imports so the file compiles.

🤖 Prompt for AI Agents
In relay/relay_adaptor.go around lines 40, 42, 45 and 110–112, there are
unresolved git conflict markers that must be removed and the import block fixed
so the file compiles; delete the conflict marker lines (e.g. <<<<<<<, =======,
>>>>>>>) at the referenced lines, ensure the import list includes the submodel
package import path used by the case constant.APITypeSubmodel and any other
packages referenced in the file, remove any duplicate or unused imports, and run
go build or go vet to confirm the file compiles cleanly.

return nil
}
Expand Down
11 changes: 11 additions & 0 deletions setting/ratio_setting/model_ratio.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,17 @@ var defaultModelRatio = map[string]float64{
"grok-vision-beta": 2.5,
"grok-3-fast-beta": 2.5,
"grok-3-mini-fast-beta": 0.3,
// submodel
"NousResearch/Hermes-4-405B-FP8": 0.8,
"Qwen/Qwen3-235B-A22B-Thinking-2507": 0.6,
"Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8": 0.8,
"Qwen/Qwen3-235B-A22B-Instruct-2507": 0.3,
"zai-org/GLM-4.5-FP8": 0.8,
"openai/gpt-oss-120b": 0.5,
"deepseek-ai/DeepSeek-R1-0528": 0.8,
"deepseek-ai/DeepSeek-R1": 0.8,
"deepseek-ai/DeepSeek-V3-0324": 0.8,
"deepseek-ai/DeepSeek-V3.1": 0.8,
}

var defaultModelPrice = map[string]float64{
Expand Down
3 changes: 3 additions & 0 deletions web/src/components/table/channels/modals/EditTagModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ const EditTagModal = (props) => {
case 36:
localModels = ['suno_music', 'suno_lyrics'];
break;
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;
default:
localModels = getChannelModels(value);
break;
Expand Down
5 changes: 5 additions & 0 deletions web/src/constants/channel.constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@ export const CHANNEL_OPTIONS = [
color: 'purple',
label: 'Vidu',
},
{
value: 53,
color: 'blue',
label: 'SubModel',
},
];

export const MODEL_TABLE_PAGE_SIZE = 10;
139 changes: 71 additions & 68 deletions web/src/helpers/render.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import {
FastGPT,
Kling,
Jimeng,
SubModel,
} from '@lobehub/icons';
Comment on lines +57 to 58

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.

💡 Verification agent

🧩 Analysis chain

Verify SubModel icon export exists in @lobehub/icons.

Import compiles only if SubModel is exported in your installed version.


🌐 Web query:

Does @lobehub/icons export a SubModel icon component? Which package version includes it, and is the component name exactly "SubModel"?

💡 Result:

Short answer: No.

  • I couldn't find any exported component named "SubModel" in the @lobehub/icons package or its repository. [1][2].
  • The npm package pages and the lobe-icons GitHub repo/releases list show the package and recent versions, but there is no mention or file named SubModel in the source or changelog. [1][3].

If you want, I can search a specific package version tarball or list the package's exported names to double-check—which version should I inspect?

Sources:

  • @lobehub/icons (npm) package page. [1]
  • lobehub / lobe-icons repository (GitHub). [2]
  • lobe-icons releases / changelog (GitHub Releases). [3]

SubModel is not exported by @lobehub/icons — remove or replace import

The @lobehub/icons package does not export a component named "SubModel"; the import in web/src/helpers/render.jsx (lines 57–58) will fail to compile.

  • Remove "SubModel" from the import and use an available exported icon or add/point to the package version that provides it.
🤖 Prompt for AI Agents
In web/src/helpers/render.jsx around lines 57-58, the import includes "SubModel"
which is not exported by @lobehub/icons; remove "SubModel" from the import
statement and either replace its usage in this file with an icon that exists in
@lobehub/icons (search current exports and pick a suitable replacement) or
update/package.json to a version of @lobehub/icons that actually exports
SubModel and run npm/yarn install; ensure any JSX references to <SubModel /> are
updated to the chosen replacement component name.


import {
Expand Down Expand Up @@ -342,6 +343,8 @@ export function getChannelIcon(channelType) {
return <FastGPT.Color size={iconSize} />;
case 21: // 知识库:AI Proxy
case 44: // 嵌入模型:MokaAI M3E
case 53: // 嵌入模型:SubModel
return <SubModel size={iconSize} />;
Comment on lines +346 to +347

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

Guard SubModel usage with feature-detect; provide fallback icon

Avoid hard dependency on a possibly-missing icon component. Use LobeIcons.SubModel if present, otherwise a generic Layers icon.

-    case 53: // 嵌入模型:SubModel 
-      return <SubModel size={iconSize} />;
+    case 53: { // 子模型 / SubModel
+      const Icon = LobeIcons.SubModel;
+      return Icon ? <Icon size={iconSize} /> : <Layers size={iconSize} />;
+    }
📝 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
case 53: // 嵌入模型:SubModel
return <SubModel size={iconSize} />;
case 53: { // 子模型 / SubModel
const Icon = LobeIcons.SubModel;
return Icon ? <Icon size={iconSize} /> : <Layers size={iconSize} />;
}
🤖 Prompt for AI Agents
In web/src/helpers/render.jsx around lines 346-347, the code unconditionally
returns <SubModel /> which can break if that icon component is missing; update
the return to guard feature-detect LobeIcons.SubModel and fall back to a generic
Layers icon: ensure LobeIcons (and the fallback Icons/Layers component) are in
scope, then replace the unconditional return with a conditional usage such as
using a local variable Component = (LobeIcons && LobeIcons.SubModel) ?
LobeIcons.SubModel : Icons.Layers and return <Component size={iconSize} /> so
the code safely uses the SubModel icon when available and a Layers icon
otherwise.

default:
return null; // 未知类型或自定义渠道不显示图标
}
Expand Down Expand Up @@ -1200,25 +1203,25 @@ export function renderModelPrice(
const extraServices = [
webSearch && webSearchCallCount > 0
? i18next.t(
' + Web搜索 {{count}}次 / 1K 次 * ${{price}} * {{ratioType}} {{ratio}}',
{
count: webSearchCallCount,
price: webSearchPrice,
ratio: groupRatio,
ratioType: ratioLabel,
},
)
' + Web搜索 {{count}}次 / 1K 次 * ${{price}} * {{ratioType}} {{ratio}}',
{
count: webSearchCallCount,
price: webSearchPrice,
ratio: groupRatio,
ratioType: ratioLabel,
},
)
: '',
fileSearch && fileSearchCallCount > 0
? i18next.t(
' + 文件搜索 {{count}}次 / 1K 次 * ${{price}} * {{ratioType}} {{ratio}}',
{
count: fileSearchCallCount,
price: fileSearchPrice,
ratio: groupRatio,
ratioType: ratioLabel,
},
)
' + 文件搜索 {{count}}次 / 1K 次 * ${{price}} * {{ratioType}} {{ratio}}',
{
count: fileSearchCallCount,
price: fileSearchPrice,
ratio: groupRatio,
ratioType: ratioLabel,
},
)
: '',
imageGenerationCall && imageGenerationCallPrice > 0
? i18next.t(
Expand Down Expand Up @@ -1398,10 +1401,10 @@ export function renderAudioModelPrice(
let audioPrice =
(audioInputTokens / 1000000) * inputRatioPrice * audioRatio * groupRatio +
(audioCompletionTokens / 1000000) *
inputRatioPrice *
audioRatio *
audioCompletionRatio *
groupRatio;
inputRatioPrice *
audioRatio *
audioCompletionRatio *
groupRatio;
let price = textPrice + audioPrice;
return (
<>
Expand Down Expand Up @@ -1457,27 +1460,27 @@ export function renderAudioModelPrice(
<p>
{cacheTokens > 0
? 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),
},
)
'文字提示 {{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(
'文字提示 {{input}} tokens / 1M tokens * ${{price}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}',
{
input: inputTokens,
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),
},
)}
</p>
<p>
{i18next.t(
Expand Down Expand Up @@ -1617,35 +1620,35 @@ export function renderClaudeModelPrice(
<p>
{cacheTokens > 0 || cacheCreationTokens > 0
? 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),
},
)
'提示 {{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(
'提示 {{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),
},
)}
'提示 {{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),
},
)}
</p>
<p>{i18next.t('仅供参考,以实际扣费为准')}</p>
</article>
Expand Down