Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 69551ab2de | |||
| 8aa8b81e03 | |||
| bc80477b1a | |||
| 5db25f47f1 | |||
| a4fd2246ba | |||
| 4e5e7b5828 | |||
| 95738594b4 | |||
| efab41c476 | |||
| c77c82421e | |||
| e4144d60f8 | |||
| 63f4595ef8 | |||
| 5e856f0263 | |||
| b9f1d01e00 | |||
| 5d620b9640 | |||
| 264bc963e0 | |||
| 9fbb782230 | |||
| da8a52f50a | |||
| 9fdb0bc248 |
@@ -7,14 +7,23 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**例行检查**
|
||||
## 提交前必读(请勿删除本节)
|
||||
|
||||
- 文档:https://docs.newapi.ai/
|
||||
- 使用问题先看或先问:https://deepwiki.com/QuantumNous/new-api
|
||||
- 警告:删除本模板、删除小节标题或随意清空内容的 issue,可能会被直接关闭;重复恶意提交者可能会被 block。
|
||||
|
||||
**您当前的 newapi 版本**
|
||||
|
||||
请填写,例如:`v1.0.0`
|
||||
|
||||
**提交确认**
|
||||
|
||||
[//]: # (方框内删除已有的空格,填 x 号)
|
||||
+ [ ] 我已确认目前没有类似 issue
|
||||
+ [ ] 我已确认我已升级到最新版本
|
||||
+ [ ] 我已完整查看过项目 README,尤其是常见问题部分
|
||||
+ [ ] 我理解并愿意跟进此 issue,协助测试和提供反馈
|
||||
+ [ ] 我理解并认可上述内容,并理解项目维护者精力有限,**不遵循规则的 issue 可能会被无视或直接关闭**
|
||||
+ [ ] 我已完整查看过文档 https://docs.newapi.ai/ 和项目 README,尤其是常见问题部分
|
||||
+ [ ] 我未删除此模板中的任何引导内容或小节标题,并会按要求完整填写
|
||||
+ [ ] 我理解项目维护者精力有限,不遵循模板要求的 issue 可能会被无视或直接关闭
|
||||
|
||||
**问题描述**
|
||||
|
||||
@@ -23,4 +32,3 @@ assignees: ''
|
||||
**预期结果**
|
||||
|
||||
**相关截图**
|
||||
如果没有的话,请删除此节。
|
||||
@@ -7,14 +7,23 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Routine Checks**
|
||||
## Read This First (Do Not Remove This Section)
|
||||
|
||||
- Docs: https://docs.newapi.ai/
|
||||
- Usage questions first: https://deepwiki.com/QuantumNous/new-api
|
||||
- Warning: issues with this template removed, section headings deleted, or content cleared may be closed directly. Repeated abusive submissions may result in a block.
|
||||
|
||||
**Your current newapi version**
|
||||
|
||||
Please fill this in, for example: `v1.0.0`
|
||||
|
||||
**Submission Checks**
|
||||
|
||||
[//]: # (Remove the space in the box and fill with an x)
|
||||
+ [ ] I have confirmed there are no similar issues currently
|
||||
+ [ ] I have confirmed I have upgraded to the latest version
|
||||
+ [ ] I have thoroughly read the project README, especially the FAQ section
|
||||
+ [ ] I understand and am willing to follow up on this issue, assist with testing and provide feedback
|
||||
+ [ ] I understand and acknowledge the above, and understand that project maintainers have limited time and energy, **issues that do not follow the rules may be ignored or closed directly**
|
||||
+ [ ] I have confirmed there are no similar issues
|
||||
+ [ ] I have thoroughly read the docs at https://docs.newapi.ai/ and the project README, especially the FAQ section
|
||||
+ [ ] I have not removed any guidance or section headings from this template and will complete it as requested
|
||||
+ [ ] I understand that maintainers have limited time and issues that do not follow this template may be ignored or closed directly
|
||||
|
||||
**Issue Description**
|
||||
|
||||
@@ -23,4 +32,3 @@ assignees: ''
|
||||
**Expected Result**
|
||||
|
||||
**Related Screenshots**
|
||||
If none, please delete this section.
|
||||
@@ -1,5 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 项目群聊
|
||||
url: https://private-user-images.githubusercontent.com/61247483/283011625-de536a8a-0161-47a7-a0a2-66ef6de81266.jpeg?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MDIyMjQzOTAsIm5iZiI6MTcwMjIyNDA5MCwicGF0aCI6Ii82MTI0NzQ4My8yODMwMTE2MjUtZGU1MzZhOGEtMDE2MS00N2E3LWEwYTItNjZlZjZkZTgxMjY2LmpwZWc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBSVdOSllBWDRDU1ZFSDUzQSUyRjIwMjMxMjEwJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDIzMTIxMFQxNjAxMzBaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT02MGIxYmM3ZDQyYzBkOTA2ZTYyYmVmMzQ1NjY4NjM1YjY0NTUzNTM5NjE1NDZkYTIzODdhYTk4ZjZjODJmYzY2JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.TJ8CTfOSwR0-CHS1KLfomqgL0e4YH1luy8lSLrkv5Zg
|
||||
about: QQ 群:629454374
|
||||
- name: 使用文档 / Documentation
|
||||
url: https://docs.newapi.ai/
|
||||
about: 提交 issue 前请先查阅文档,确认现有说明无法解决你的问题。
|
||||
- name: 使用问题 / Usage Questions
|
||||
url: https://deepwiki.com/QuantumNous/new-api
|
||||
about: 使用、配置、接入等问题请优先在 DeepWiki 查询或提问。
|
||||
|
||||
@@ -7,14 +7,23 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**例行检查**
|
||||
## 提交前必读(请勿删除本节)
|
||||
|
||||
- 文档:https://docs.newapi.ai/
|
||||
- 使用问题先看或先问:https://deepwiki.com/QuantumNous/new-api
|
||||
- 警告:删除本模板、删除小节标题或随意清空内容的 issue,可能会被直接关闭;重复恶意提交者可能会被 block。
|
||||
|
||||
**您当前的 newapi 版本**
|
||||
|
||||
请填写,例如:`v1.0.0`
|
||||
|
||||
**提交确认**
|
||||
|
||||
[//]: # (方框内删除已有的空格,填 x 号)
|
||||
+ [ ] 我已确认目前没有类似 issue
|
||||
+ [ ] 我已确认我已升级到最新版本
|
||||
+ [ ] 我已完整查看过项目 README,已确定现有版本无法满足需求
|
||||
+ [ ] 我理解并愿意跟进此 issue,协助测试和提供反馈
|
||||
+ [ ] 我理解并认可上述内容,并理解项目维护者精力有限,**不遵循规则的 issue 可能会被无视或直接关闭**
|
||||
+ [ ] 我已完整查看过文档 https://docs.newapi.ai/ 和项目 README,已确定现有版本无法满足需求
|
||||
+ [ ] 我未删除此模板中的任何引导内容或小节标题,并会按要求完整填写
|
||||
+ [ ] 我理解项目维护者精力有限,不遵循模板要求的 issue 可能会被无视或直接关闭
|
||||
|
||||
**功能描述**
|
||||
|
||||
|
||||
@@ -7,16 +7,24 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Routine Checks**
|
||||
## Read This First (Do Not Remove This Section)
|
||||
|
||||
- Docs: https://docs.newapi.ai/
|
||||
- Usage questions first: https://deepwiki.com/QuantumNous/new-api
|
||||
- Warning: issues with this template removed, section headings deleted, or content cleared may be closed directly. Repeated abusive submissions may result in a block.
|
||||
|
||||
**Your current newapi version**
|
||||
|
||||
Please fill this in, for example: `v1.0.0`
|
||||
|
||||
**Submission Checks**
|
||||
|
||||
[//]: # (Remove the space in the box and fill with an x)
|
||||
+ [ ] I have confirmed there are no similar issues currently
|
||||
+ [ ] I have confirmed I have upgraded to the latest version
|
||||
+ [ ] I have thoroughly read the project README and confirmed the current version cannot meet my needs
|
||||
+ [ ] I understand and am willing to follow up on this issue, assist with testing and provide feedback
|
||||
+ [ ] I understand and acknowledge the above, and understand that project maintainers have limited time and energy, **issues that do not follow the rules may be ignored or closed directly**
|
||||
+ [ ] I have confirmed there are no similar issues
|
||||
+ [ ] I have thoroughly read the docs at https://docs.newapi.ai/ and the project README, and confirmed the current version cannot meet my needs
|
||||
+ [ ] I have not removed any guidance or section headings from this template and will complete it as requested
|
||||
+ [ ] I understand that maintainers have limited time and issues that do not follow this template may be ignored or closed directly
|
||||
|
||||
**Feature Description**
|
||||
|
||||
**Use Case**
|
||||
|
||||
|
||||
@@ -470,6 +470,15 @@ func PasskeyVerifyFinish(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
session := sessions.Default(c)
|
||||
// Mark passkey as ready; /api/verify will convert this into the final secure verification session.
|
||||
session.Set(PasskeyReadySessionKey, time.Now().Unix())
|
||||
session.Delete(SecureVerificationSessionKey)
|
||||
if err := session.Save(); err != nil {
|
||||
common.ApiError(c, fmt.Errorf("保存验证状态失败: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Passkey 验证成功",
|
||||
|
||||
@@ -7,18 +7,19 @@ import (
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
passkeysvc "github.com/QuantumNous/new-api/service/passkey"
|
||||
"github.com/QuantumNous/new-api/setting/system_setting"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const (
|
||||
// SecureVerificationSessionKey 安全验证的 session key
|
||||
// SecureVerificationSessionKey means the user has fully passed secure verification.
|
||||
SecureVerificationSessionKey = "secure_verified_at"
|
||||
// PasskeyReadySessionKey means WebAuthn finished and /api/verify can finalize step-up verification.
|
||||
PasskeyReadySessionKey = "secure_passkey_ready_at"
|
||||
// SecureVerificationTimeout 验证有效期(秒)
|
||||
SecureVerificationTimeout = 300 // 5分钟
|
||||
// PasskeyReadyTimeout passkey ready 标记有效期(秒)
|
||||
PasskeyReadyTimeout = 60
|
||||
)
|
||||
|
||||
type UniversalVerifyRequest struct {
|
||||
@@ -76,6 +77,7 @@ func UniversalVerify(c *gin.Context) {
|
||||
// 根据验证方式进行验证
|
||||
var verified bool
|
||||
var verifyMethod string
|
||||
var err error
|
||||
|
||||
switch req.Method {
|
||||
case "2fa":
|
||||
@@ -95,10 +97,16 @@ func UniversalVerify(c *gin.Context) {
|
||||
common.ApiError(c, fmt.Errorf("用户未启用Passkey"))
|
||||
return
|
||||
}
|
||||
// Passkey 验证需要先调用 PasskeyVerifyBegin 和 PasskeyVerifyFinish
|
||||
// 这里只是验证 Passkey 验证流程是否已经完成
|
||||
// 实际上,前端应该先调用这两个接口,然后再调用本接口
|
||||
verified = true // Passkey 验证逻辑已在 PasskeyVerifyFinish 中完成
|
||||
// Passkey branch only trusts the short-lived marker written by PasskeyVerifyFinish.
|
||||
verified, err = consumePasskeyReady(c)
|
||||
if err != nil {
|
||||
common.ApiError(c, fmt.Errorf("Passkey 验证状态异常: %v", err))
|
||||
return
|
||||
}
|
||||
if !verified {
|
||||
common.ApiError(c, fmt.Errorf("请先完成 Passkey 验证"))
|
||||
return
|
||||
}
|
||||
verifyMethod = "Passkey"
|
||||
|
||||
default:
|
||||
@@ -112,10 +120,8 @@ func UniversalVerify(c *gin.Context) {
|
||||
}
|
||||
|
||||
// 验证成功,在 session 中记录时间戳
|
||||
session := sessions.Default(c)
|
||||
now := time.Now().Unix()
|
||||
session.Set(SecureVerificationSessionKey, now)
|
||||
if err := session.Save(); err != nil {
|
||||
now, err := setSecureVerificationSession(c)
|
||||
if err != nil {
|
||||
common.ApiError(c, fmt.Errorf("保存验证状态失败: %v", err))
|
||||
return
|
||||
}
|
||||
@@ -133,94 +139,37 @@ func UniversalVerify(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// PasskeyVerifyAndSetSession Passkey 验证完成后设置 session
|
||||
// 这是一个辅助函数,供 PasskeyVerifyFinish 调用
|
||||
func PasskeyVerifyAndSetSession(c *gin.Context) {
|
||||
func setSecureVerificationSession(c *gin.Context) (int64, error) {
|
||||
session := sessions.Default(c)
|
||||
session.Delete(PasskeyReadySessionKey)
|
||||
now := time.Now().Unix()
|
||||
session.Set(SecureVerificationSessionKey, now)
|
||||
_ = session.Save()
|
||||
if err := session.Save(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return now, nil
|
||||
}
|
||||
|
||||
// PasskeyVerifyForSecure 用于安全验证的 Passkey 验证流程
|
||||
// 整合了 begin 和 finish 流程
|
||||
func PasskeyVerifyForSecure(c *gin.Context) {
|
||||
if !system_setting.GetPasskeySettings().Enabled {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": false,
|
||||
"message": "管理员未启用 Passkey 登录",
|
||||
})
|
||||
return
|
||||
func consumePasskeyReady(c *gin.Context) (bool, error) {
|
||||
session := sessions.Default(c)
|
||||
readyAtRaw := session.Get(PasskeyReadySessionKey)
|
||||
if readyAtRaw == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
userId := c.GetInt("id")
|
||||
if userId == 0 {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"success": false,
|
||||
"message": "未登录",
|
||||
})
|
||||
return
|
||||
readyAt, ok := readyAtRaw.(int64)
|
||||
if !ok {
|
||||
session.Delete(PasskeyReadySessionKey)
|
||||
_ = session.Save()
|
||||
return false, fmt.Errorf("无效的 Passkey 验证状态")
|
||||
}
|
||||
|
||||
user := &model.User{Id: userId}
|
||||
if err := user.FillUserById(); err != nil {
|
||||
common.ApiError(c, fmt.Errorf("获取用户信息失败: %v", err))
|
||||
return
|
||||
session.Delete(PasskeyReadySessionKey)
|
||||
if err := session.Save(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if user.Status != common.UserStatusEnabled {
|
||||
common.ApiError(c, fmt.Errorf("该用户已被禁用"))
|
||||
return
|
||||
// Expired ready markers cannot be reused.
|
||||
if time.Now().Unix()-readyAt >= PasskeyReadyTimeout {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
credential, err := model.GetPasskeyByUserID(userId)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": false,
|
||||
"message": "该用户尚未绑定 Passkey",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
wa, err := passkeysvc.BuildWebAuthn(c.Request)
|
||||
if err != nil {
|
||||
common.ApiError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
waUser := passkeysvc.NewWebAuthnUser(user, credential)
|
||||
sessionData, err := passkeysvc.PopSessionData(c, passkeysvc.VerifySessionKey)
|
||||
if err != nil {
|
||||
common.ApiError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = wa.FinishLogin(waUser, *sessionData, c.Request)
|
||||
if err != nil {
|
||||
common.ApiError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 更新凭证的最后使用时间
|
||||
now := time.Now()
|
||||
credential.LastUsedAt = &now
|
||||
if err := model.UpsertPasskeyCredential(credential); err != nil {
|
||||
common.ApiError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证成功,设置 session
|
||||
PasskeyVerifyAndSetSession(c)
|
||||
|
||||
// 记录日志
|
||||
model.RecordLog(userId, model.LogTypeSystem, "Passkey 安全验证成功")
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Passkey 验证成功",
|
||||
"data": gin.H{
|
||||
"verified": true,
|
||||
"expires_at": time.Now().Unix() + SecureVerificationTimeout,
|
||||
},
|
||||
})
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -225,8 +225,12 @@ func (a *Adaptor) SetupRequestHeader(c *gin.Context, header *http.Header, info *
|
||||
}
|
||||
}
|
||||
if info.ChannelType == constant.ChannelTypeOpenRouter {
|
||||
header.Set("HTTP-Referer", "https://www.newapi.ai")
|
||||
header.Set("X-OpenRouter-Title", "New API")
|
||||
if header.Get("HTTP-Referer") == "" {
|
||||
header.Set("HTTP-Referer", "https://www.newapi.ai")
|
||||
}
|
||||
if header.Get("X-OpenRouter-Title") == "" {
|
||||
header.Set("X-OpenRouter-Title", "New API")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+231
-3
@@ -21,10 +21,23 @@ var negativeIndexRegexp = regexp.MustCompile(`\.(-\d+)`)
|
||||
const (
|
||||
paramOverrideContextRequestHeaders = "request_headers"
|
||||
paramOverrideContextHeaderOverride = "header_override"
|
||||
paramOverrideContextAuditRecorder = "__param_override_audit_recorder"
|
||||
)
|
||||
|
||||
var errSourceHeaderNotFound = errors.New("source header does not exist")
|
||||
|
||||
var paramOverrideKeyAuditPaths = map[string]struct{}{
|
||||
"model": {},
|
||||
"original_model": {},
|
||||
"upstream_model": {},
|
||||
"service_tier": {},
|
||||
"inference_geo": {},
|
||||
}
|
||||
|
||||
type paramOverrideAuditRecorder struct {
|
||||
lines []string
|
||||
}
|
||||
|
||||
type ConditionOperation struct {
|
||||
Path string `json:"path"` // JSON路径
|
||||
Mode string `json:"mode"` // full, prefix, suffix, contains, gt, gte, lt, lte
|
||||
@@ -118,6 +131,7 @@ func ApplyParamOverride(jsonData []byte, paramOverride map[string]interface{}, c
|
||||
if len(paramOverride) == 0 {
|
||||
return jsonData, nil
|
||||
}
|
||||
auditRecorder := getParamOverrideAuditRecorder(conditionContext)
|
||||
|
||||
// 尝试断言为操作格式
|
||||
if operations, ok := tryParseOperations(paramOverride); ok {
|
||||
@@ -125,7 +139,7 @@ func ApplyParamOverride(jsonData []byte, paramOverride map[string]interface{}, c
|
||||
workingJSON := jsonData
|
||||
var err error
|
||||
if len(legacyOverride) > 0 {
|
||||
workingJSON, err = applyOperationsLegacy(workingJSON, legacyOverride)
|
||||
workingJSON, err = applyOperationsLegacy(workingJSON, legacyOverride, auditRecorder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -137,7 +151,7 @@ func ApplyParamOverride(jsonData []byte, paramOverride map[string]interface{}, c
|
||||
}
|
||||
|
||||
// 直接使用旧方法
|
||||
return applyOperationsLegacy(jsonData, paramOverride)
|
||||
return applyOperationsLegacy(jsonData, paramOverride, auditRecorder)
|
||||
}
|
||||
|
||||
func buildLegacyParamOverride(paramOverride map[string]interface{}) map[string]interface{} {
|
||||
@@ -161,14 +175,200 @@ func ApplyParamOverrideWithRelayInfo(jsonData []byte, info *RelayInfo) ([]byte,
|
||||
}
|
||||
|
||||
overrideCtx := BuildParamOverrideContext(info)
|
||||
var recorder *paramOverrideAuditRecorder
|
||||
if shouldEnableParamOverrideAudit(paramOverride) {
|
||||
recorder = ¶mOverrideAuditRecorder{}
|
||||
overrideCtx[paramOverrideContextAuditRecorder] = recorder
|
||||
}
|
||||
result, err := ApplyParamOverride(jsonData, paramOverride, overrideCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
syncRuntimeHeaderOverrideFromContext(info, overrideCtx)
|
||||
if info != nil {
|
||||
if recorder != nil {
|
||||
info.ParamOverrideAudit = recorder.lines
|
||||
} else {
|
||||
info.ParamOverrideAudit = nil
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func shouldEnableParamOverrideAudit(paramOverride map[string]interface{}) bool {
|
||||
if common.DebugEnabled {
|
||||
return true
|
||||
}
|
||||
if len(paramOverride) == 0 {
|
||||
return false
|
||||
}
|
||||
if operations, ok := tryParseOperations(paramOverride); ok {
|
||||
for _, operation := range operations {
|
||||
if shouldAuditParamPath(strings.TrimSpace(operation.Path)) ||
|
||||
shouldAuditParamPath(strings.TrimSpace(operation.To)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for key := range buildLegacyParamOverride(paramOverride) {
|
||||
if shouldAuditParamPath(strings.TrimSpace(key)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
for key := range paramOverride {
|
||||
if shouldAuditParamPath(strings.TrimSpace(key)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getParamOverrideAuditRecorder(context map[string]interface{}) *paramOverrideAuditRecorder {
|
||||
if context == nil {
|
||||
return nil
|
||||
}
|
||||
recorder, _ := context[paramOverrideContextAuditRecorder].(*paramOverrideAuditRecorder)
|
||||
return recorder
|
||||
}
|
||||
|
||||
func (r *paramOverrideAuditRecorder) recordOperation(mode, path, from, to string, value interface{}) {
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
line := buildParamOverrideAuditLine(mode, path, from, to, value)
|
||||
if line == "" {
|
||||
return
|
||||
}
|
||||
if lo.Contains(r.lines, line) {
|
||||
return
|
||||
}
|
||||
r.lines = append(r.lines, line)
|
||||
}
|
||||
|
||||
func shouldAuditParamPath(path string) bool {
|
||||
path = strings.TrimSpace(path)
|
||||
if path == "" {
|
||||
return false
|
||||
}
|
||||
if common.DebugEnabled {
|
||||
return true
|
||||
}
|
||||
_, ok := paramOverrideKeyAuditPaths[path]
|
||||
return ok
|
||||
}
|
||||
|
||||
func shouldAuditOperation(mode, path, from, to string) bool {
|
||||
if common.DebugEnabled {
|
||||
return true
|
||||
}
|
||||
for _, candidate := range []string{path, to} {
|
||||
if shouldAuditParamPath(candidate) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func formatParamOverrideAuditValue(value interface{}) string {
|
||||
switch typed := value.(type) {
|
||||
case nil:
|
||||
return "<empty>"
|
||||
case string:
|
||||
return typed
|
||||
default:
|
||||
return common.GetJsonString(typed)
|
||||
}
|
||||
}
|
||||
|
||||
func buildParamOverrideAuditLine(mode, path, from, to string, value interface{}) string {
|
||||
mode = strings.TrimSpace(mode)
|
||||
path = strings.TrimSpace(path)
|
||||
from = strings.TrimSpace(from)
|
||||
to = strings.TrimSpace(to)
|
||||
|
||||
if !shouldAuditOperation(mode, path, from, to) {
|
||||
return ""
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case "set":
|
||||
if path == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("set %s = %s", path, formatParamOverrideAuditValue(value))
|
||||
case "delete":
|
||||
if path == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("delete %s", path)
|
||||
case "copy":
|
||||
if from == "" || to == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("copy %s -> %s", from, to)
|
||||
case "move":
|
||||
if from == "" || to == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("move %s -> %s", from, to)
|
||||
case "prepend":
|
||||
if path == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("prepend %s with %s", path, formatParamOverrideAuditValue(value))
|
||||
case "append":
|
||||
if path == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("append %s with %s", path, formatParamOverrideAuditValue(value))
|
||||
case "trim_prefix", "trim_suffix", "ensure_prefix", "ensure_suffix":
|
||||
if path == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s %s with %s", mode, path, formatParamOverrideAuditValue(value))
|
||||
case "trim_space", "to_lower", "to_upper":
|
||||
if path == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s %s", mode, path)
|
||||
case "replace", "regex_replace":
|
||||
if path == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s %s from %s to %s", mode, path, from, to)
|
||||
case "set_header":
|
||||
if path == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("set_header %s = %s", path, formatParamOverrideAuditValue(value))
|
||||
case "delete_header":
|
||||
if path == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("delete_header %s", path)
|
||||
case "copy_header", "move_header":
|
||||
if from == "" || to == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s %s -> %s", mode, from, to)
|
||||
case "pass_headers":
|
||||
return fmt.Sprintf("pass_headers %s", formatParamOverrideAuditValue(value))
|
||||
case "sync_fields":
|
||||
if from == "" || to == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("sync_fields %s -> %s", from, to)
|
||||
case "return_error":
|
||||
return fmt.Sprintf("return_error %s", formatParamOverrideAuditValue(value))
|
||||
default:
|
||||
if path == "" {
|
||||
return mode
|
||||
}
|
||||
return fmt.Sprintf("%s %s", mode, path)
|
||||
}
|
||||
}
|
||||
|
||||
func getParamOverrideMap(info *RelayInfo) map[string]interface{} {
|
||||
if info == nil || info.ChannelMeta == nil {
|
||||
return nil
|
||||
@@ -455,7 +655,7 @@ func compareNumeric(jsonValue, targetValue gjson.Result, operator string) (bool,
|
||||
}
|
||||
|
||||
// applyOperationsLegacy 原参数覆盖方法
|
||||
func applyOperationsLegacy(jsonData []byte, paramOverride map[string]interface{}) ([]byte, error) {
|
||||
func applyOperationsLegacy(jsonData []byte, paramOverride map[string]interface{}, auditRecorder *paramOverrideAuditRecorder) ([]byte, error) {
|
||||
reqMap := make(map[string]interface{})
|
||||
err := common.Unmarshal(jsonData, &reqMap)
|
||||
if err != nil {
|
||||
@@ -464,6 +664,7 @@ func applyOperationsLegacy(jsonData []byte, paramOverride map[string]interface{}
|
||||
|
||||
for key, value := range paramOverride {
|
||||
reqMap[key] = value
|
||||
auditRecorder.recordOperation("set", key, "", "", value)
|
||||
}
|
||||
|
||||
return common.Marshal(reqMap)
|
||||
@@ -471,6 +672,7 @@ func applyOperationsLegacy(jsonData []byte, paramOverride map[string]interface{}
|
||||
|
||||
func applyOperations(jsonStr string, operations []ParamOperation, conditionContext map[string]interface{}) (string, error) {
|
||||
context := ensureContextMap(conditionContext)
|
||||
auditRecorder := getParamOverrideAuditRecorder(context)
|
||||
contextJSON, err := marshalContextJSON(context)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to marshal condition context: %v", err)
|
||||
@@ -506,6 +708,7 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
auditRecorder.recordOperation("delete", path, "", "", nil)
|
||||
}
|
||||
case "set":
|
||||
for _, path := range opPaths {
|
||||
@@ -516,11 +719,15 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
auditRecorder.recordOperation("set", path, "", "", op.Value)
|
||||
}
|
||||
case "move":
|
||||
opFrom := processNegativeIndex(result, op.From)
|
||||
opTo := processNegativeIndex(result, op.To)
|
||||
result, err = moveValue(result, opFrom, opTo)
|
||||
if err == nil {
|
||||
auditRecorder.recordOperation("move", "", opFrom, opTo, nil)
|
||||
}
|
||||
case "copy":
|
||||
if op.From == "" || op.To == "" {
|
||||
return "", fmt.Errorf("copy from/to is required")
|
||||
@@ -528,12 +735,16 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
|
||||
opFrom := processNegativeIndex(result, op.From)
|
||||
opTo := processNegativeIndex(result, op.To)
|
||||
result, err = copyValue(result, opFrom, opTo)
|
||||
if err == nil {
|
||||
auditRecorder.recordOperation("copy", "", opFrom, opTo, nil)
|
||||
}
|
||||
case "prepend":
|
||||
for _, path := range opPaths {
|
||||
result, err = modifyValue(result, path, op.Value, op.KeepOrigin, true)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
auditRecorder.recordOperation("prepend", path, "", "", op.Value)
|
||||
}
|
||||
case "append":
|
||||
for _, path := range opPaths {
|
||||
@@ -541,6 +752,7 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
auditRecorder.recordOperation("append", path, "", "", op.Value)
|
||||
}
|
||||
case "trim_prefix":
|
||||
for _, path := range opPaths {
|
||||
@@ -548,6 +760,7 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
auditRecorder.recordOperation("trim_prefix", path, "", "", op.Value)
|
||||
}
|
||||
case "trim_suffix":
|
||||
for _, path := range opPaths {
|
||||
@@ -555,6 +768,7 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
auditRecorder.recordOperation("trim_suffix", path, "", "", op.Value)
|
||||
}
|
||||
case "ensure_prefix":
|
||||
for _, path := range opPaths {
|
||||
@@ -562,6 +776,7 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
auditRecorder.recordOperation("ensure_prefix", path, "", "", op.Value)
|
||||
}
|
||||
case "ensure_suffix":
|
||||
for _, path := range opPaths {
|
||||
@@ -569,6 +784,7 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
auditRecorder.recordOperation("ensure_suffix", path, "", "", op.Value)
|
||||
}
|
||||
case "trim_space":
|
||||
for _, path := range opPaths {
|
||||
@@ -576,6 +792,7 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
auditRecorder.recordOperation("trim_space", path, "", "", nil)
|
||||
}
|
||||
case "to_lower":
|
||||
for _, path := range opPaths {
|
||||
@@ -583,6 +800,7 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
auditRecorder.recordOperation("to_lower", path, "", "", nil)
|
||||
}
|
||||
case "to_upper":
|
||||
for _, path := range opPaths {
|
||||
@@ -590,6 +808,7 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
auditRecorder.recordOperation("to_upper", path, "", "", nil)
|
||||
}
|
||||
case "replace":
|
||||
for _, path := range opPaths {
|
||||
@@ -597,6 +816,7 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
auditRecorder.recordOperation("replace", path, op.From, op.To, nil)
|
||||
}
|
||||
case "regex_replace":
|
||||
for _, path := range opPaths {
|
||||
@@ -604,8 +824,10 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
auditRecorder.recordOperation("regex_replace", path, op.From, op.To, nil)
|
||||
}
|
||||
case "return_error":
|
||||
auditRecorder.recordOperation("return_error", op.Path, "", "", op.Value)
|
||||
returnErr, parseErr := parseParamOverrideReturnError(op.Value)
|
||||
if parseErr != nil {
|
||||
return "", parseErr
|
||||
@@ -621,11 +843,13 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
|
||||
case "set_header":
|
||||
err = setHeaderOverrideInContext(context, op.Path, op.Value, op.KeepOrigin)
|
||||
if err == nil {
|
||||
auditRecorder.recordOperation("set_header", op.Path, "", "", op.Value)
|
||||
contextJSON, err = marshalContextJSON(context)
|
||||
}
|
||||
case "delete_header":
|
||||
err = deleteHeaderOverrideInContext(context, op.Path)
|
||||
if err == nil {
|
||||
auditRecorder.recordOperation("delete_header", op.Path, "", "", nil)
|
||||
contextJSON, err = marshalContextJSON(context)
|
||||
}
|
||||
case "copy_header":
|
||||
@@ -642,6 +866,7 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
|
||||
err = nil
|
||||
}
|
||||
if err == nil {
|
||||
auditRecorder.recordOperation("copy_header", "", sourceHeader, targetHeader, nil)
|
||||
contextJSON, err = marshalContextJSON(context)
|
||||
}
|
||||
case "move_header":
|
||||
@@ -658,6 +883,7 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
|
||||
err = nil
|
||||
}
|
||||
if err == nil {
|
||||
auditRecorder.recordOperation("move_header", "", sourceHeader, targetHeader, nil)
|
||||
contextJSON, err = marshalContextJSON(context)
|
||||
}
|
||||
case "pass_headers":
|
||||
@@ -675,11 +901,13 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
auditRecorder.recordOperation("pass_headers", "", "", "", headerNames)
|
||||
contextJSON, err = marshalContextJSON(context)
|
||||
}
|
||||
case "sync_fields":
|
||||
result, err = syncFieldsBetweenTargets(result, context, op.From, op.To)
|
||||
if err == nil {
|
||||
auditRecorder.recordOperation("sync_fields", "", op.From, op.To, nil)
|
||||
contextJSON, err = marshalContextJSON(context)
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
common2 "github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/types"
|
||||
|
||||
"github.com/QuantumNous/new-api/dto"
|
||||
@@ -2066,6 +2067,105 @@ func TestRemoveDisabledFieldsAllowInferenceGeo(t *testing.T) {
|
||||
assertJSONEqual(t, `{"inference_geo":"eu","store":true}`, string(out))
|
||||
}
|
||||
|
||||
func TestApplyParamOverrideWithRelayInfoRecordsOperationAuditInDebugMode(t *testing.T) {
|
||||
originalDebugEnabled := common2.DebugEnabled
|
||||
common2.DebugEnabled = true
|
||||
t.Cleanup(func() {
|
||||
common2.DebugEnabled = originalDebugEnabled
|
||||
})
|
||||
|
||||
info := &RelayInfo{
|
||||
ChannelMeta: &ChannelMeta{
|
||||
ParamOverride: map[string]interface{}{
|
||||
"operations": []interface{}{
|
||||
map[string]interface{}{
|
||||
"mode": "copy",
|
||||
"from": "metadata.target_model",
|
||||
"to": "model",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"mode": "set",
|
||||
"path": "service_tier",
|
||||
"value": "flex",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"mode": "set",
|
||||
"path": "temperature",
|
||||
"value": 0.1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
out, err := ApplyParamOverrideWithRelayInfo([]byte(`{
|
||||
"model":"gpt-4.1",
|
||||
"temperature":0.7,
|
||||
"metadata":{"target_model":"gpt-4.1-mini"}
|
||||
}`), info)
|
||||
if err != nil {
|
||||
t.Fatalf("ApplyParamOverrideWithRelayInfo returned error: %v", err)
|
||||
}
|
||||
assertJSONEqual(t, `{
|
||||
"model":"gpt-4.1-mini",
|
||||
"temperature":0.1,
|
||||
"service_tier":"flex",
|
||||
"metadata":{"target_model":"gpt-4.1-mini"}
|
||||
}`, string(out))
|
||||
|
||||
expected := []string{
|
||||
"copy metadata.target_model -> model",
|
||||
"set service_tier = flex",
|
||||
"set temperature = 0.1",
|
||||
}
|
||||
if !reflect.DeepEqual(info.ParamOverrideAudit, expected) {
|
||||
t.Fatalf("unexpected param override audit, got %#v", info.ParamOverrideAudit)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyParamOverrideWithRelayInfoRecordsOnlyKeyOperationsWhenDebugDisabled(t *testing.T) {
|
||||
originalDebugEnabled := common2.DebugEnabled
|
||||
common2.DebugEnabled = false
|
||||
t.Cleanup(func() {
|
||||
common2.DebugEnabled = originalDebugEnabled
|
||||
})
|
||||
|
||||
info := &RelayInfo{
|
||||
ChannelMeta: &ChannelMeta{
|
||||
ParamOverride: map[string]interface{}{
|
||||
"operations": []interface{}{
|
||||
map[string]interface{}{
|
||||
"mode": "copy",
|
||||
"from": "metadata.target_model",
|
||||
"to": "model",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"mode": "set",
|
||||
"path": "temperature",
|
||||
"value": 0.1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := ApplyParamOverrideWithRelayInfo([]byte(`{
|
||||
"model":"gpt-4.1",
|
||||
"temperature":0.7,
|
||||
"metadata":{"target_model":"gpt-4.1-mini"}
|
||||
}`), info)
|
||||
if err != nil {
|
||||
t.Fatalf("ApplyParamOverrideWithRelayInfo returned error: %v", err)
|
||||
}
|
||||
|
||||
expected := []string{
|
||||
"copy metadata.target_model -> model",
|
||||
}
|
||||
if !reflect.DeepEqual(info.ParamOverrideAudit, expected) {
|
||||
t.Fatalf("unexpected param override audit, got %#v", info.ParamOverrideAudit)
|
||||
}
|
||||
}
|
||||
|
||||
func assertJSONEqual(t *testing.T, want, got string) {
|
||||
t.Helper()
|
||||
|
||||
|
||||
@@ -149,6 +149,7 @@ type RelayInfo struct {
|
||||
LastError *types.NewAPIError
|
||||
RuntimeHeadersOverride map[string]interface{}
|
||||
UseRuntimeHeadersOverride bool
|
||||
ParamOverrideAudit []string
|
||||
|
||||
PriceData types.PriceData
|
||||
|
||||
|
||||
@@ -214,7 +214,7 @@ func registerMjRouterGroup(relayMjRouter *gin.RouterGroup) {
|
||||
relayMjRouter.POST("/submit/blend", controller.RelayMidjourney)
|
||||
relayMjRouter.POST("/submit/edits", controller.RelayMidjourney)
|
||||
relayMjRouter.POST("/submit/video", controller.RelayMidjourney)
|
||||
relayMjRouter.POST("/notify", controller.RelayMidjourney)
|
||||
//relayMjRouter.POST("/notify", controller.RelayMidjourney)
|
||||
relayMjRouter.GET("/task/:id/fetch", controller.RelayMidjourney)
|
||||
relayMjRouter.GET("/task/:id/image-seed", controller.RelayMidjourney)
|
||||
relayMjRouter.POST("/task/list-by-condition", controller.RelayMidjourney)
|
||||
|
||||
@@ -74,9 +74,17 @@ func GenerateTextOtherInfo(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, m
|
||||
appendRequestPath(ctx, relayInfo, other)
|
||||
appendRequestConversionChain(relayInfo, other)
|
||||
appendBillingInfo(relayInfo, other)
|
||||
appendParamOverrideInfo(relayInfo, other)
|
||||
return other
|
||||
}
|
||||
|
||||
func appendParamOverrideInfo(relayInfo *relaycommon.RelayInfo, other map[string]interface{}) {
|
||||
if relayInfo == nil || other == nil || len(relayInfo.ParamOverrideAudit) == 0 {
|
||||
return
|
||||
}
|
||||
other["po"] = relayInfo.ParamOverrideAudit
|
||||
}
|
||||
|
||||
func appendBillingInfo(relayInfo *relaycommon.RelayInfo, other map[string]interface{}) {
|
||||
if relayInfo == nil || other == nil {
|
||||
return
|
||||
|
||||
@@ -537,7 +537,12 @@ export const getChannelsColumns = ({
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
content={t('剩余额度$') + record.balance + t(',点击更新')}
|
||||
content={
|
||||
t('剩余额度') +
|
||||
': ' +
|
||||
renderQuotaWithAmount(record.balance) +
|
||||
t(',点击更新')
|
||||
}
|
||||
>
|
||||
<Tag
|
||||
color='white'
|
||||
|
||||
@@ -25,21 +25,14 @@ import {
|
||||
Tooltip,
|
||||
Popover,
|
||||
Typography,
|
||||
Button
|
||||
} from '@douyinfe/semi-ui';
|
||||
import {
|
||||
timestamp2string,
|
||||
renderGroup,
|
||||
renderQuota,
|
||||
stringToColor,
|
||||
getLogOther,
|
||||
renderModelTag,
|
||||
renderClaudeLogContent,
|
||||
renderLogContent,
|
||||
renderModelPriceSimple,
|
||||
renderAudioModelPrice,
|
||||
renderClaudeModelPrice,
|
||||
renderModelPrice,
|
||||
} from '../../../helpers';
|
||||
import { IconHelpCircle } from '@douyinfe/semi-icons';
|
||||
import { Route, Sparkles } from 'lucide-react';
|
||||
@@ -330,6 +323,142 @@ function getPromptCacheSummary(other) {
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeDetailText(detail) {
|
||||
return String(detail || '')
|
||||
.replace(/\n\r/g, '\n')
|
||||
.replace(/\r\n/g, '\n');
|
||||
}
|
||||
|
||||
function getUsageLogGroupSummary(groupRatio, userGroupRatio, t) {
|
||||
const parsedUserGroupRatio = Number(userGroupRatio);
|
||||
const useUserGroupRatio =
|
||||
Number.isFinite(parsedUserGroupRatio) && parsedUserGroupRatio !== -1;
|
||||
const ratio = useUserGroupRatio ? userGroupRatio : groupRatio;
|
||||
if (ratio === undefined || ratio === null || ratio === '') {
|
||||
return '';
|
||||
}
|
||||
return `${useUserGroupRatio ? t('专属倍率') : t('分组')} ${formatRatio(ratio)}x`;
|
||||
}
|
||||
|
||||
function renderCompactDetailSummary(summarySegments) {
|
||||
const segments = Array.isArray(summarySegments)
|
||||
? summarySegments.filter((segment) => segment?.text)
|
||||
: [];
|
||||
if (!segments.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
maxWidth: 180,
|
||||
lineHeight: 1.35,
|
||||
}}
|
||||
>
|
||||
{segments.map((segment, index) => (
|
||||
<Typography.Text
|
||||
key={`${segment.text}-${index}`}
|
||||
type={segment.tone === 'secondary' ? 'tertiary' : undefined}
|
||||
size={segment.tone === 'secondary' ? 'small' : undefined}
|
||||
style={{
|
||||
display: 'block',
|
||||
maxWidth: '100%',
|
||||
fontSize: 12,
|
||||
marginTop: index === 0 ? 0 : 2,
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
}}
|
||||
>
|
||||
{segment.text}
|
||||
</Typography.Text>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function getUsageLogDetailSummary(record, text, billingDisplayMode, t) {
|
||||
const other = getLogOther(record.other);
|
||||
|
||||
if (record.type === 6) {
|
||||
return {
|
||||
segments: [{ text: t('异步任务退款'), tone: 'primary' }],
|
||||
};
|
||||
}
|
||||
|
||||
if (other == null || record.type !== 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
other?.violation_fee === true ||
|
||||
Boolean(other?.violation_fee_code) ||
|
||||
Boolean(other?.violation_fee_marker)
|
||||
) {
|
||||
const feeQuota = other?.fee_quota ?? record?.quota;
|
||||
const groupText = getUsageLogGroupSummary(
|
||||
other?.group_ratio,
|
||||
other?.user_group_ratio,
|
||||
t,
|
||||
);
|
||||
return {
|
||||
segments: [
|
||||
groupText ? { text: groupText, tone: 'primary' } : null,
|
||||
{ text: t('违规扣费'), tone: 'primary' },
|
||||
{
|
||||
text: `${t('扣费')}:${renderQuota(feeQuota, 6)}`,
|
||||
tone: 'secondary',
|
||||
},
|
||||
text ? { text: `${t('详情')}:${text}`, tone: 'secondary' } : null,
|
||||
].filter(Boolean),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
segments: other?.claude
|
||||
? renderModelPriceSimple(
|
||||
other.model_ratio,
|
||||
other.model_price,
|
||||
other.group_ratio,
|
||||
other?.user_group_ratio,
|
||||
other.cache_tokens || 0,
|
||||
other.cache_ratio || 1.0,
|
||||
other.cache_creation_tokens || 0,
|
||||
other.cache_creation_ratio || 1.0,
|
||||
other.cache_creation_tokens_5m || 0,
|
||||
other.cache_creation_ratio_5m || other.cache_creation_ratio || 1.0,
|
||||
other.cache_creation_tokens_1h || 0,
|
||||
other.cache_creation_ratio_1h || other.cache_creation_ratio || 1.0,
|
||||
false,
|
||||
1.0,
|
||||
other?.is_system_prompt_overwritten,
|
||||
'claude',
|
||||
billingDisplayMode,
|
||||
'segments',
|
||||
)
|
||||
: renderModelPriceSimple(
|
||||
other.model_ratio,
|
||||
other.model_price,
|
||||
other.group_ratio,
|
||||
other?.user_group_ratio,
|
||||
other.cache_tokens || 0,
|
||||
other.cache_ratio || 1.0,
|
||||
0,
|
||||
1.0,
|
||||
0,
|
||||
1.0,
|
||||
0,
|
||||
1.0,
|
||||
false,
|
||||
1.0,
|
||||
other?.is_system_prompt_overwritten,
|
||||
'openai',
|
||||
billingDisplayMode,
|
||||
'segments',
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
export const getLogsColumns = ({
|
||||
t,
|
||||
COLUMN_KEYS,
|
||||
@@ -375,7 +504,10 @@ export const getLogsColumns = ({
|
||||
}
|
||||
|
||||
return isAdminUser &&
|
||||
(record.type === 0 || record.type === 2 || record.type === 5 || record.type === 6) ? (
|
||||
(record.type === 0 ||
|
||||
record.type === 2 ||
|
||||
record.type === 5 ||
|
||||
record.type === 6) ? (
|
||||
<Space>
|
||||
<span style={{ position: 'relative', display: 'inline-block' }}>
|
||||
<Tooltip content={record.channel_name || t('未知渠道')}>
|
||||
@@ -466,7 +598,10 @@ export const getLogsColumns = ({
|
||||
title: t('令牌'),
|
||||
dataIndex: 'token_name',
|
||||
render: (text, record, index) => {
|
||||
return record.type === 0 || record.type === 2 || record.type === 5 || record.type === 6 ? (
|
||||
return record.type === 0 ||
|
||||
record.type === 2 ||
|
||||
record.type === 5 ||
|
||||
record.type === 6 ? (
|
||||
<div>
|
||||
<Tag
|
||||
color='grey'
|
||||
@@ -489,7 +624,12 @@ export const getLogsColumns = ({
|
||||
title: t('分组'),
|
||||
dataIndex: 'group',
|
||||
render: (text, record, index) => {
|
||||
if (record.type === 0 || record.type === 2 || record.type === 5 || record.type === 6) {
|
||||
if (
|
||||
record.type === 0 ||
|
||||
record.type === 2 ||
|
||||
record.type === 5 ||
|
||||
record.type === 6
|
||||
) {
|
||||
if (record.group) {
|
||||
return <>{renderGroup(record.group)}</>;
|
||||
} else {
|
||||
@@ -529,7 +669,10 @@ export const getLogsColumns = ({
|
||||
title: t('模型'),
|
||||
dataIndex: 'model_name',
|
||||
render: (text, record, index) => {
|
||||
return record.type === 0 || record.type === 2 || record.type === 5 || record.type === 6 ? (
|
||||
return record.type === 0 ||
|
||||
record.type === 2 ||
|
||||
record.type === 5 ||
|
||||
record.type === 6 ? (
|
||||
<>{renderModelName(record, copyText, t)}</>
|
||||
) : (
|
||||
<></>
|
||||
@@ -596,7 +739,10 @@ export const getLogsColumns = ({
|
||||
cacheText = `${t('缓存写')} ${formatTokenCount(cacheSummary.cacheWriteTokens)}`;
|
||||
}
|
||||
|
||||
return record.type === 0 || record.type === 2 || record.type === 5 || record.type === 6 ? (
|
||||
return record.type === 0 ||
|
||||
record.type === 2 ||
|
||||
record.type === 5 ||
|
||||
record.type === 6 ? (
|
||||
<div
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
@@ -630,7 +776,10 @@ export const getLogsColumns = ({
|
||||
dataIndex: 'completion_tokens',
|
||||
render: (text, record, index) => {
|
||||
return parseInt(text) > 0 &&
|
||||
(record.type === 0 || record.type === 2 || record.type === 5 || record.type === 6) ? (
|
||||
(record.type === 0 ||
|
||||
record.type === 2 ||
|
||||
record.type === 5 ||
|
||||
record.type === 6) ? (
|
||||
<>{<span> {text} </span>}</>
|
||||
) : (
|
||||
<></>
|
||||
@@ -642,7 +791,14 @@ export const getLogsColumns = ({
|
||||
title: t('花费'),
|
||||
dataIndex: 'quota',
|
||||
render: (text, record, index) => {
|
||||
if (!(record.type === 0 || record.type === 2 || record.type === 5 || record.type === 6)) {
|
||||
if (
|
||||
!(
|
||||
record.type === 0 ||
|
||||
record.type === 2 ||
|
||||
record.type === 5 ||
|
||||
record.type === 6
|
||||
)
|
||||
) {
|
||||
return <></>;
|
||||
}
|
||||
const other = getLogOther(record.other);
|
||||
@@ -709,9 +865,9 @@ export const getLogsColumns = ({
|
||||
}
|
||||
if (other.admin_info !== undefined) {
|
||||
if (
|
||||
other.admin_info.use_channel !== null &&
|
||||
other.admin_info.use_channel !== undefined &&
|
||||
other.admin_info.use_channel !== ''
|
||||
other.admin_info.use_channel !== null &&
|
||||
other.admin_info.use_channel !== undefined &&
|
||||
other.admin_info.use_channel !== ''
|
||||
) {
|
||||
let useChannel = other.admin_info.use_channel;
|
||||
let useChannelStr = useChannel.join('->');
|
||||
@@ -727,19 +883,16 @@ export const getLogsColumns = ({
|
||||
title: t('详情'),
|
||||
dataIndex: 'content',
|
||||
fixed: 'right',
|
||||
width: 200,
|
||||
render: (text, record, index) => {
|
||||
let other = getLogOther(record.other);
|
||||
if (record.type === 6) {
|
||||
return (
|
||||
<Typography.Paragraph
|
||||
ellipsis={{ rows: 2 }}
|
||||
style={{ maxWidth: 240 }}
|
||||
>
|
||||
{t('异步任务退款')}
|
||||
</Typography.Paragraph>
|
||||
);
|
||||
}
|
||||
if (other == null || record.type !== 2) {
|
||||
const detailSummary = getUsageLogDetailSummary(
|
||||
record,
|
||||
text,
|
||||
billingDisplayMode,
|
||||
t,
|
||||
);
|
||||
|
||||
if (!detailSummary) {
|
||||
return (
|
||||
<Typography.Paragraph
|
||||
ellipsis={{
|
||||
@@ -749,96 +902,14 @@ export const getLogsColumns = ({
|
||||
opts: { style: { width: 240 } },
|
||||
},
|
||||
}}
|
||||
style={{ maxWidth: 240 }}
|
||||
style={{ maxWidth: 200, marginBottom: 0 }}
|
||||
>
|
||||
{text}
|
||||
</Typography.Paragraph>
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
other?.violation_fee === true ||
|
||||
Boolean(other?.violation_fee_code) ||
|
||||
Boolean(other?.violation_fee_marker)
|
||||
) {
|
||||
const feeQuota = other?.fee_quota ?? record?.quota;
|
||||
const summary = [
|
||||
t('违规扣费'),
|
||||
`${t('扣费')}:${renderQuota(feeQuota, 6)}`,
|
||||
`${t('分组倍率')}:${formatRatio(other?.group_ratio)}`,
|
||||
text ? `${t('详情')}:${text}` : null,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join('\n');
|
||||
return (
|
||||
<Typography.Paragraph
|
||||
ellipsis={{
|
||||
rows: 2,
|
||||
showTooltip: {
|
||||
type: 'popover',
|
||||
opts: { style: { width: 240 } },
|
||||
},
|
||||
}}
|
||||
style={{ maxWidth: 240, whiteSpace: 'pre-line' }}
|
||||
>
|
||||
{summary}
|
||||
</Typography.Paragraph>
|
||||
);
|
||||
}
|
||||
|
||||
let content = other?.claude
|
||||
? renderModelPriceSimple(
|
||||
other.model_ratio,
|
||||
other.model_price,
|
||||
other.group_ratio,
|
||||
other?.user_group_ratio,
|
||||
other.cache_tokens || 0,
|
||||
other.cache_ratio || 1.0,
|
||||
other.cache_creation_tokens || 0,
|
||||
other.cache_creation_ratio || 1.0,
|
||||
other.cache_creation_tokens_5m || 0,
|
||||
other.cache_creation_ratio_5m ||
|
||||
other.cache_creation_ratio ||
|
||||
1.0,
|
||||
other.cache_creation_tokens_1h || 0,
|
||||
other.cache_creation_ratio_1h ||
|
||||
other.cache_creation_ratio ||
|
||||
1.0,
|
||||
false,
|
||||
1.0,
|
||||
other?.is_system_prompt_overwritten,
|
||||
'claude',
|
||||
billingDisplayMode,
|
||||
)
|
||||
: renderModelPriceSimple(
|
||||
other.model_ratio,
|
||||
other.model_price,
|
||||
other.group_ratio,
|
||||
other?.user_group_ratio,
|
||||
other.cache_tokens || 0,
|
||||
other.cache_ratio || 1.0,
|
||||
0,
|
||||
1.0,
|
||||
0,
|
||||
1.0,
|
||||
0,
|
||||
1.0,
|
||||
false,
|
||||
1.0,
|
||||
other?.is_system_prompt_overwritten,
|
||||
'openai',
|
||||
billingDisplayMode,
|
||||
);
|
||||
return (
|
||||
<Typography.Paragraph
|
||||
ellipsis={{
|
||||
rows: 3,
|
||||
}}
|
||||
style={{ maxWidth: 240, whiteSpace: 'pre-line' }}
|
||||
>
|
||||
{content}
|
||||
</Typography.Paragraph>
|
||||
);
|
||||
return renderCompactDetailSummary(detailSummary.segments);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -102,7 +102,7 @@ const LogsTable = (logsData) => {
|
||||
loading={loading}
|
||||
scroll={compactMode ? undefined : { x: 'max-content' }}
|
||||
className='rounded-xl overflow-hidden'
|
||||
size='middle'
|
||||
size='small'
|
||||
empty={
|
||||
<Empty
|
||||
image={<IllustrationNoResult style={{ width: 150, height: 150 }} />}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Copyright (C) 2025 QuantumNous
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
For commercial licensing, please contact support@quantumnous.com
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Typography } from '@douyinfe/semi-ui';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
const ParamOverrideEntry = ({ count, onOpen, t }) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
flexWrap: 'wrap',
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
type='tertiary'
|
||||
size='small'
|
||||
style={{ fontVariantNumeric: 'tabular-nums' }}
|
||||
>
|
||||
{t('{{count}} 项操作', { count })}
|
||||
</Text>
|
||||
<Text
|
||||
link
|
||||
size='small'
|
||||
style={{ fontWeight: 600 }}
|
||||
onClick={onOpen}
|
||||
>
|
||||
{t('查看详情')}
|
||||
</Text>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ParamOverrideEntry);
|
||||
@@ -25,6 +25,7 @@ import LogsFilters from './UsageLogsFilters';
|
||||
import ColumnSelectorModal from './modals/ColumnSelectorModal';
|
||||
import UserInfoModal from './modals/UserInfoModal';
|
||||
import ChannelAffinityUsageCacheModal from './modals/ChannelAffinityUsageCacheModal';
|
||||
import ParamOverrideModal from './modals/ParamOverrideModal';
|
||||
import { useLogsData } from '../../../hooks/usage-logs/useUsageLogsData';
|
||||
import { useIsMobile } from '../../../hooks/common/useIsMobile';
|
||||
import { createCardProPagination } from '../../../helpers/utils';
|
||||
@@ -39,6 +40,7 @@ const LogsPage = () => {
|
||||
<ColumnSelectorModal {...logsData} />
|
||||
<UserInfoModal {...logsData} />
|
||||
<ChannelAffinityUsageCacheModal {...logsData} />
|
||||
<ParamOverrideModal {...logsData} />
|
||||
|
||||
{/* Main Content */}
|
||||
<CardPro
|
||||
|
||||
@@ -36,6 +36,10 @@ const ColumnSelectorModal = ({
|
||||
showUserInfoFunc,
|
||||
t,
|
||||
}) => {
|
||||
const handleBillingDisplayModeChange = (eventOrValue) => {
|
||||
setBillingDisplayMode(eventOrValue?.target?.value ?? eventOrValue);
|
||||
};
|
||||
|
||||
const isTokensDisplay =
|
||||
typeof localStorage !== 'undefined' &&
|
||||
localStorage.getItem('quota_display_type') === 'TOKENS';
|
||||
@@ -73,7 +77,7 @@ const ColumnSelectorModal = ({
|
||||
<RadioGroup
|
||||
type='button'
|
||||
value={billingDisplayMode}
|
||||
onChange={(value) => setBillingDisplayMode(value)}
|
||||
onChange={handleBillingDisplayModeChange}
|
||||
>
|
||||
<Radio value='price'>
|
||||
{isTokensDisplay ? t('价格模式') : t('价格模式(默认)')}
|
||||
|
||||
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
Copyright (C) 2025 QuantumNous
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
For commercial licensing, please contact support@quantumnous.com
|
||||
*/
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import {
|
||||
Modal,
|
||||
Button,
|
||||
Empty,
|
||||
Divider,
|
||||
Typography,
|
||||
} from '@douyinfe/semi-ui';
|
||||
import { IconCopy } from '@douyinfe/semi-icons';
|
||||
import { copy, showError, showSuccess } from '../../../../helpers';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
const parseAuditLine = (line) => {
|
||||
if (typeof line !== 'string') {
|
||||
return null;
|
||||
}
|
||||
const firstSpaceIndex = line.indexOf(' ');
|
||||
if (firstSpaceIndex <= 0) {
|
||||
return { action: line, content: line };
|
||||
}
|
||||
return {
|
||||
action: line.slice(0, firstSpaceIndex),
|
||||
content: line.slice(firstSpaceIndex + 1),
|
||||
};
|
||||
};
|
||||
|
||||
const getActionLabel = (action, t) => {
|
||||
switch ((action || '').toLowerCase()) {
|
||||
case 'set':
|
||||
return t('设置');
|
||||
case 'delete':
|
||||
return t('删除');
|
||||
case 'copy':
|
||||
return t('复制');
|
||||
case 'move':
|
||||
return t('移动');
|
||||
case 'append':
|
||||
return t('追加');
|
||||
case 'prepend':
|
||||
return t('前置');
|
||||
case 'trim_prefix':
|
||||
return t('去前缀');
|
||||
case 'trim_suffix':
|
||||
return t('去后缀');
|
||||
case 'ensure_prefix':
|
||||
return t('保前缀');
|
||||
case 'ensure_suffix':
|
||||
return t('保后缀');
|
||||
case 'trim_space':
|
||||
return t('去空格');
|
||||
case 'to_lower':
|
||||
return t('转小写');
|
||||
case 'to_upper':
|
||||
return t('转大写');
|
||||
case 'replace':
|
||||
return t('替换');
|
||||
case 'regex_replace':
|
||||
return t('正则替换');
|
||||
case 'set_header':
|
||||
return t('设请求头');
|
||||
case 'delete_header':
|
||||
return t('删请求头');
|
||||
case 'copy_header':
|
||||
return t('复制请求头');
|
||||
case 'move_header':
|
||||
return t('移动请求头');
|
||||
case 'pass_headers':
|
||||
return t('透传请求头');
|
||||
case 'sync_fields':
|
||||
return t('同步字段');
|
||||
case 'return_error':
|
||||
return t('返回错误');
|
||||
default:
|
||||
return action;
|
||||
}
|
||||
};
|
||||
|
||||
const ParamOverrideModal = ({
|
||||
showParamOverrideModal,
|
||||
setShowParamOverrideModal,
|
||||
paramOverrideTarget,
|
||||
t,
|
||||
}) => {
|
||||
const lines = Array.isArray(paramOverrideTarget?.lines)
|
||||
? paramOverrideTarget.lines
|
||||
: [];
|
||||
|
||||
const parsedLines = useMemo(() => {
|
||||
return lines.map(parseAuditLine);
|
||||
}, [lines]);
|
||||
|
||||
const copyAll = async () => {
|
||||
const content = lines.join('\n');
|
||||
if (!content) {
|
||||
return;
|
||||
}
|
||||
if (await copy(content)) {
|
||||
showSuccess(t('参数覆盖已复制'));
|
||||
return;
|
||||
}
|
||||
showError(t('无法复制到剪贴板,请手动复制'));
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={t('参数覆盖详情')}
|
||||
visible={showParamOverrideModal}
|
||||
onCancel={() => setShowParamOverrideModal(false)}
|
||||
footer={null}
|
||||
centered
|
||||
closable
|
||||
maskClosable
|
||||
width={640}
|
||||
>
|
||||
<div style={{ padding: '8px 20px 20px' }}>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'flex-start',
|
||||
gap: 12,
|
||||
marginBottom: 10,
|
||||
}}
|
||||
>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<div style={{ marginBottom: 4 }}>
|
||||
<Text style={{ fontWeight: 600 }}>
|
||||
{t('{{count}} 项操作', { count: lines.length })}
|
||||
</Text>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
gap: 8,
|
||||
fontSize: 12,
|
||||
color: 'var(--semi-color-text-2)',
|
||||
}}
|
||||
>
|
||||
{paramOverrideTarget?.modelName ? (
|
||||
<Text type='tertiary' size='small'>
|
||||
{paramOverrideTarget.modelName}
|
||||
</Text>
|
||||
) : null}
|
||||
{paramOverrideTarget?.requestId ? (
|
||||
<Text type='tertiary' size='small'>
|
||||
{t('Request ID')}: {paramOverrideTarget.requestId}
|
||||
</Text>
|
||||
) : null}
|
||||
{paramOverrideTarget?.requestPath ? (
|
||||
<Text type='tertiary' size='small'>
|
||||
{t('请求路径')}: {paramOverrideTarget.requestPath}
|
||||
</Text>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
icon={<IconCopy />}
|
||||
theme='borderless'
|
||||
type='tertiary'
|
||||
size='small'
|
||||
onClick={copyAll}
|
||||
disabled={lines.length === 0}
|
||||
>
|
||||
{t('复制')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Divider margin='12px' />
|
||||
|
||||
{lines.length === 0 ? (
|
||||
<Empty
|
||||
description={t('暂无参数覆盖记录')}
|
||||
style={{ padding: '24px 0 8px' }}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 8,
|
||||
maxHeight: '56vh',
|
||||
overflowY: 'auto',
|
||||
paddingRight: 2,
|
||||
}}
|
||||
>
|
||||
{parsedLines.map((item, index) => {
|
||||
if (!item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
key={`${item.action}-${index}`}
|
||||
style={{
|
||||
padding: '10px 12px',
|
||||
borderRadius: 10,
|
||||
border: '1px solid var(--semi-color-border)',
|
||||
background: 'var(--semi-color-fill-0)',
|
||||
display: 'flex',
|
||||
gap: 12,
|
||||
alignItems: 'flex-start',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
flex: '0 0 auto',
|
||||
minWidth: 74,
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
fontSize: 11,
|
||||
fontWeight: 700,
|
||||
lineHeight: '20px',
|
||||
padding: '0 8px',
|
||||
borderRadius: 999,
|
||||
background: 'rgba(var(--semi-blue-5), 0.12)',
|
||||
color: 'var(--semi-color-primary)',
|
||||
}}
|
||||
>
|
||||
{getActionLabel(item.action, t)}
|
||||
</Text>
|
||||
</div>
|
||||
<Text
|
||||
style={{
|
||||
flex: 1,
|
||||
minWidth: 0,
|
||||
fontFamily:
|
||||
'ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, monospace',
|
||||
fontSize: 12,
|
||||
lineHeight: 1.6,
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-word',
|
||||
color: 'var(--semi-color-text-0)',
|
||||
}}
|
||||
>
|
||||
{item.content}
|
||||
</Text>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ParamOverrideModal;
|
||||
Vendored
+1011
-872
File diff suppressed because it is too large
Load Diff
+36
@@ -39,6 +39,7 @@ import {
|
||||
} from '../../helpers';
|
||||
import { ITEMS_PER_PAGE } from '../../constants';
|
||||
import { useTableCompactMode } from '../common/useTableCompactMode';
|
||||
import ParamOverrideEntry from '../../components/table/usage-logs/components/ParamOverrideEntry';
|
||||
|
||||
export const useLogsData = () => {
|
||||
const { t } = useTranslation();
|
||||
@@ -181,6 +182,8 @@ export const useLogsData = () => {
|
||||
] = useState(false);
|
||||
const [channelAffinityUsageCacheTarget, setChannelAffinityUsageCacheTarget] =
|
||||
useState(null);
|
||||
const [showParamOverrideModal, setShowParamOverrideModal] = useState(false);
|
||||
const [paramOverrideTarget, setParamOverrideTarget] = useState(null);
|
||||
|
||||
// Initialize default column visibility
|
||||
const initDefaultColumns = () => {
|
||||
@@ -345,6 +348,20 @@ export const useLogsData = () => {
|
||||
setShowChannelAffinityUsageCacheModal(true);
|
||||
};
|
||||
|
||||
const openParamOverrideModal = (log, other) => {
|
||||
const lines = Array.isArray(other?.po) ? other.po.filter(Boolean) : [];
|
||||
if (lines.length === 0) {
|
||||
return;
|
||||
}
|
||||
setParamOverrideTarget({
|
||||
lines,
|
||||
modelName: log?.model_name || '',
|
||||
requestId: log?.request_id || '',
|
||||
requestPath: other?.request_path || '',
|
||||
});
|
||||
setShowParamOverrideModal(true);
|
||||
};
|
||||
|
||||
// Format logs data
|
||||
const setLogsFormat = (logs) => {
|
||||
const requestConversionDisplayValue = (conversionChain) => {
|
||||
@@ -584,6 +601,21 @@ export const useLogsData = () => {
|
||||
value: other.request_path,
|
||||
});
|
||||
}
|
||||
if (Array.isArray(other?.po) && other.po.length > 0) {
|
||||
expandDataLocal.push({
|
||||
key: t('参数覆盖'),
|
||||
value: (
|
||||
<ParamOverrideEntry
|
||||
count={other.po.length}
|
||||
t={t}
|
||||
onOpen={(event) => {
|
||||
event.stopPropagation();
|
||||
openParamOverrideModal(logs[i], other);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
});
|
||||
}
|
||||
if (other?.billing_source === 'subscription') {
|
||||
const planId = other?.subscription_plan_id;
|
||||
const planTitle = other?.subscription_plan_title || '';
|
||||
@@ -811,6 +843,9 @@ export const useLogsData = () => {
|
||||
setShowChannelAffinityUsageCacheModal,
|
||||
channelAffinityUsageCacheTarget,
|
||||
openChannelAffinityUsageCacheModal,
|
||||
showParamOverrideModal,
|
||||
setShowParamOverrideModal,
|
||||
paramOverrideTarget,
|
||||
|
||||
// Functions
|
||||
loadLogs,
|
||||
@@ -822,6 +857,7 @@ export const useLogsData = () => {
|
||||
setLogsFormat,
|
||||
hasExpandableRows,
|
||||
setLogType,
|
||||
openParamOverrideModal,
|
||||
|
||||
// Translation
|
||||
t,
|
||||
|
||||
Vendored
+34
-23
@@ -18,7 +18,6 @@
|
||||
"(输入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "(Input {{input}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"(输入 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + 音频输入 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}": "(Input {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + Audio input {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}",
|
||||
"(输入 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}": "(Input {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + Cache {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}",
|
||||
"(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens * {{imageRatio}} / 1M tokens * {{symbol}}{{price}}": "(Input {{nonImageInput}} tokens + Image input {{imageInput}} tokens * {{imageRatio}} / 1M tokens * {{symbol}}{{price}}",
|
||||
"[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "The maximum value of [Maximum request count] and [Maximum request completion count] is 2147483647.",
|
||||
"[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[Maximum request count] must be greater than or equal to 0, [Maximum request completion count] must be greater than or equal to 1.",
|
||||
"{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}",
|
||||
@@ -155,10 +154,10 @@
|
||||
"JSON格式错误": "JSON format error",
|
||||
"JSON编辑": "JSON Editor",
|
||||
"JSON解析错误:": "JSON parsing error:",
|
||||
"Key": "",
|
||||
"Key": "Key",
|
||||
"Key 或 Path": "",
|
||||
"Key 指纹": "",
|
||||
"Key 摘要": "",
|
||||
"Key 摘要": "Key summary",
|
||||
"Key 来源": "",
|
||||
"Key 来源类型": "",
|
||||
"Linux DO Client ID": "Linux DO Client ID",
|
||||
@@ -915,7 +914,6 @@
|
||||
"图片输入价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (图片倍率: {{imageRatio}})": "Image input price: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (Image ratio: {{imageRatio}})",
|
||||
"图片输入价格:{{symbol}}{{price}} / 1M tokens": "Image input price: {{symbol}}{{price}} / 1M tokens",
|
||||
"图片输入价格 {{symbol}}{{price}} / 1M tokens": "Image input price {{symbol}}{{price}} / 1M tokens",
|
||||
"图片输入 {{price}}": "Image input {{price}}",
|
||||
"图片输入倍率(仅部分模型支持该计费)": "Image input ratio (only supported by some models for billing)",
|
||||
"图片输入相关的倍率设置,键为模型名称,值为倍率,仅部分模型支持该计费": "Ratio settings related to image input, key is model name, value is ratio, only supported by some models for billing",
|
||||
"图生文": "Describe",
|
||||
@@ -1371,7 +1369,7 @@
|
||||
"打开 CC Switch": "Open CC Switch",
|
||||
"打开侧边栏": "Open sidebar",
|
||||
"打开授权页面": "",
|
||||
"扣费": "",
|
||||
"扣费": "Charge",
|
||||
"执行 GC": "Run GC",
|
||||
"执行中": "processing",
|
||||
"扫描二维码": "Scan QR code",
|
||||
@@ -1406,7 +1404,6 @@
|
||||
"按倍率类型筛选": "Filter by ratio type",
|
||||
"按倍率设置": "Set by ratio",
|
||||
"按次": "Per request",
|
||||
"按次 {{price}} / 次": "Per request {{price}} / request",
|
||||
"按次计费": "Pay per request",
|
||||
"按照如下格式输入:AccessKey|SecretAccessKey|Region": "Enter in the format: AccessKey|SecretAccessKey|Region",
|
||||
"按量计费": "Pay as you go",
|
||||
@@ -1799,9 +1796,11 @@
|
||||
"模型价格": "Model price",
|
||||
"模型价格 {{symbol}}{{price}},{{ratioType}} {{ratio}}": "Model price {{symbol}}{{price}}, {{ratioType}} {{ratio}}",
|
||||
"模型价格 {{symbol}}{{price}} / 次": "Model price {{symbol}}{{price}} / request",
|
||||
"按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Per request {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"模型价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "Model price: {{symbol}}{{price}} * {{ratioType}}: {{ratio}} = {{symbol}}{{total}}",
|
||||
"按次:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "Per request: {{symbol}}{{price}} * {{ratioType}}: {{ratio}} = {{symbol}}{{total}}",
|
||||
"模型价格:{{symbol}}{{price}} / 次": "Model price: {{symbol}}{{price}} / request",
|
||||
"输入 {{price}} / 1M tokens": "Input {{price}} / 1M tokens",
|
||||
"按次:{{symbol}}{{price}}": "Per request: {{symbol}}{{price}}",
|
||||
"模型倍率": "Model ratio",
|
||||
"模型倍率 {{modelRatio}}": "Model ratio {{modelRatio}}",
|
||||
"模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}}": "Model ratio {{modelRatio}}, cache ratio {{cacheRatio}}, completion ratio {{completionRatio}}, {{ratioType}} {{ratio}}",
|
||||
@@ -1997,7 +1996,7 @@
|
||||
"渠道": "Channel",
|
||||
"渠道 ID": "Channel ID",
|
||||
"渠道ID,名称,密钥,API地址": "Channel ID, name, key, Base URL",
|
||||
"渠道亲和性": "",
|
||||
"渠道亲和性": "Channel affinity",
|
||||
"渠道亲和性:上游缓存命中": "",
|
||||
"渠道亲和性会基于从请求上下文或 JSON Body 提取的 Key,优先复用上一次成功的渠道。": "",
|
||||
"渠道优先级": "Channel Priority",
|
||||
@@ -2099,7 +2098,7 @@
|
||||
"用户账户管理": "User account management",
|
||||
"用时/首字": "Time/first word",
|
||||
"由全站货币展示设置统一控制": "Controlled by the site-wide currency display settings",
|
||||
"由订阅抵扣": "",
|
||||
"由订阅抵扣": "Deducted by subscription",
|
||||
"界面语言和其他个人偏好": "Interface language and other personal preferences",
|
||||
"留空使用系统临时目录": "Leave empty to use system temp directory",
|
||||
"留空则使用账号绑定的邮箱": "If left blank, the email address bound to the account will be used",
|
||||
@@ -2360,8 +2359,6 @@
|
||||
"缓存价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})": "Cache price: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (Cache ratio: {{cacheRatio}})",
|
||||
"缓存读取价格:{{symbol}}{{price}} / 1M tokens": "Cache read price: {{symbol}}{{price}} / 1M tokens",
|
||||
"缓存读取价格 {{symbol}}{{price}} / 1M tokens": "Cache read price {{symbol}}{{price}} / 1M tokens",
|
||||
"缓存读取 {{price}}": "Cache read {{price}}",
|
||||
"缓存读取 {{price}}": "Cache read {{price}}",
|
||||
"缓存倍率": "Cache ratio",
|
||||
"缓存倍率 {{cacheRatio}}": "Cache ratio {{cacheRatio}}",
|
||||
"缓存写": "Cache Write",
|
||||
@@ -2371,12 +2368,6 @@
|
||||
"缓存创建: 1h {{cacheCreationRatio1h}}": "Cache creation: 1h {{cacheCreationRatio1h}}",
|
||||
"缓存创建: 5m {{cacheCreationRatio5m}}": "Cache creation: 5m {{cacheCreationRatio5m}}",
|
||||
"缓存创建: 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Cache creation: 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}",
|
||||
"缓存创建 {{price}}": "Cache creation {{price}}",
|
||||
"5m缓存创建 {{price}}": "5m cache creation {{price}}",
|
||||
"1h缓存创建 {{price}}": "1h cache creation {{price}}",
|
||||
"缓存创建 {{price}}": "Cache creation {{price}}",
|
||||
"5m缓存创建 {{price}}": "5m cache creation {{price}}",
|
||||
"1h缓存创建 {{price}}": "1h cache creation {{price}}",
|
||||
"缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})": "Cache creation price: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (Cache creation ratio: {{cacheCreationRatio}})",
|
||||
"缓存创建价格:{{symbol}}{{price}} / 1M tokens": "Cache creation price: {{symbol}}{{price}} / 1M tokens",
|
||||
"缓存创建价格 {{symbol}}{{price}} / 1M tokens": "Cache creation price {{symbol}}{{price}} / 1M tokens",
|
||||
@@ -2510,7 +2501,6 @@
|
||||
"输入与缓存价格合计 * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Input and cache pricing subtotal * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"补全价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})": "Completion price: {{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (Completion ratio: {{completionRatio}})",
|
||||
"补全价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens": "Completion price: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens",
|
||||
"补全价格 {{symbol}}{{price}} / 1M tokens": "Completion price {{symbol}}{{price}} / 1M tokens",
|
||||
"补全倍率": "Completion ratio",
|
||||
"补全倍率值": "Completion Ratio Value",
|
||||
"补单": "Complete Order",
|
||||
@@ -2521,7 +2511,7 @@
|
||||
"覆盖模式:将完全替换现有的所有密钥": "Overwrite mode: completely replace all existing keys",
|
||||
"覆盖模板": "",
|
||||
"覆盖现有密钥": "Overwrite existing key",
|
||||
"规则": "",
|
||||
"规则": "Rule",
|
||||
"规则 JSON": "",
|
||||
"规则 JSON 格式不正确": "",
|
||||
"规则 ttl_seconds 为 0 时使用。0 表示使用后端默认 TTL:3600 秒。": "",
|
||||
@@ -2551,7 +2541,7 @@
|
||||
"订阅套餐": "Subscription Plans",
|
||||
"订阅套餐管理": "Subscription Plan Management",
|
||||
"订阅实例": "",
|
||||
"订阅抵扣": "",
|
||||
"订阅抵扣": "Subscription deduction",
|
||||
"订阅管理": "Subscription Management",
|
||||
"订阅结算": "",
|
||||
"订阅说明": "",
|
||||
@@ -2942,7 +2932,7 @@
|
||||
"进度": "Progress",
|
||||
"进行中": "Ongoing",
|
||||
"进行该操作时,可能导致渠道访问错误,请仅在数据库出现问题时使用": "When performing this operation, it may cause channel access errors. Please only use it when there is a problem with the database.",
|
||||
"违规扣费": "",
|
||||
"违规扣费": "Violation deduction",
|
||||
"违规扣费金额": "Violation deduction amount",
|
||||
"连接保活设置": "Connection Keep-alive Settings",
|
||||
"连接已断开": "Connection Disconnected",
|
||||
@@ -3292,12 +3282,33 @@
|
||||
"音频输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * 音频补全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Audio output: {{tokens}} / 1M * model ratio {{modelRatio}} * audio ratio {{audioRatio}} * audio completion ratio {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"合计:文字部分 {{textTotal}} + 音频部分 {{audioTotal}} = {{total}}": "Total: text {{textTotal}} + audio {{audioTotal}} = {{total}}",
|
||||
"模型倍率 {{modelRatio}},输出倍率 {{completionRatio}},缓存倍率 {{cacheRatio}},{{ratioType}} {{ratio}}": "Model ratio {{modelRatio}}, output ratio {{completionRatio}}, cache ratio {{cacheRatio}}, {{ratioType}} {{ratio}}",
|
||||
"缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Cache creation ratio 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}",
|
||||
"缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Cache read: {{tokens}} / 1M * model ratio {{modelRatio}} * cache ratio {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Cache creation: {{tokens}} / 1M * model ratio {{modelRatio}} * cache creation ratio {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "5m cache creation: {{tokens}} / 1M * model ratio {{modelRatio}} * 5m cache creation ratio {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "1h cache creation: {{tokens}} / 1M * model ratio {{modelRatio}} * 1h cache creation ratio {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Output: {{tokens}} / 1M * model ratio {{modelRatio}} * output ratio {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"空": "Empty"
|
||||
"空": "Empty",
|
||||
"{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x",
|
||||
"模型价格:{{symbol}}{{price}}": "Model price: {{symbol}}{{price}}",
|
||||
"模型价格 {{price}}": "Model price {{price}}",
|
||||
"缓存读 {{price}} / 1M tokens": "Cache read {{price}} / 1M tokens",
|
||||
"5m缓存创建 {{price}} / 1M tokens": "5m cache creation {{price}} / 1M tokens",
|
||||
"1h缓存创建 {{price}} / 1M tokens": "1h cache creation {{price}} / 1M tokens",
|
||||
"缓存创建 {{price}} / 1M tokens": "Cache creation {{price}} / 1M tokens",
|
||||
"图片输入 {{price}} / 1M tokens": "Image input {{price}} / 1M tokens",
|
||||
"输入 {{price}} / 1M tokens": "Input {{price}} / 1M tokens",
|
||||
"缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Cache creation {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "5m cache creation {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "1h cache creation {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}": "(Input {{nonImageInput}} tokens + Image input {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"图片输入价格:{{symbol}}{{total}} / 1M tokens": "Image input price: {{symbol}}{{total}} / 1M tokens",
|
||||
"文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音频提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Text prompt {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + Text completion {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + Audio prompt {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + Audio completion {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"缓存读取价格:{{symbol}}{{total}} / 1M tokens": "Cache read price: {{symbol}}{{total}} / 1M tokens",
|
||||
"补全 {{completion}} tokens * 输出倍率 {{completionRatio}}": "Completion {{completion}} tokens * Output ratio {{completionRatio}}",
|
||||
"补全倍率 {{completionRatio}}": "Completion ratio {{completionRatio}}",
|
||||
"输入价格:{{symbol}}{{price}} / 1M tokens": "Input Price: {{symbol}}{{price}} / 1M tokens",
|
||||
"输出价格 {{symbol}}{{price}} / 1M tokens": "Output Price {{symbol}}{{price}} / 1M tokens",
|
||||
"输出价格:{{symbol}}{{price}} / 1M tokens": "Output Price: {{symbol}}{{price}} / 1M tokens",
|
||||
"输出价格:{{symbol}}{{total}} / 1M tokens": "Output Price: {{symbol}}{{total}} / 1M tokens"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+37
-13
@@ -21,7 +21,6 @@
|
||||
"(输入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "(Entrée {{input}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"(输入 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + 音频输入 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}": "(Entrée {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + Entrée audio {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}",
|
||||
"(输入 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}": "(Entrée {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + Cache {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}",
|
||||
"(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens * {{imageRatio}} / 1M tokens * {{symbol}}{{price}}": "(Entrée {{nonImageInput}} tokens + Entrée image {{imageInput}} tokens * {{imageRatio}} / 1M tokens * {{symbol}}{{price}}",
|
||||
"[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "La valeur maximale de [Nombre maximal de requêtes] et [Nombre maximal d'achèvements de requêtes] est 2147483647.",
|
||||
"[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[Nombre maximal de requêtes] doit être supérieur ou égal à 0, [Nombre maximal d'achèvements de requêtes] doit être supérieur ou égal à 1.",
|
||||
"{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}",
|
||||
@@ -155,10 +154,10 @@
|
||||
"JSON格式错误": "Erreur de format JSON",
|
||||
"JSON编辑": "Édition JSON",
|
||||
"JSON解析错误:": "Erreur d'analyse JSON :",
|
||||
"Key": "",
|
||||
"Key": "Key",
|
||||
"Key 或 Path": "",
|
||||
"Key 指纹": "",
|
||||
"Key 摘要": "",
|
||||
"Key 摘要": "Résumé de Key",
|
||||
"Key 来源": "",
|
||||
"Key 来源类型": "",
|
||||
"Linux DO Client ID": "ID client Linux DO",
|
||||
@@ -1366,7 +1365,7 @@
|
||||
"打开 CC Switch": "",
|
||||
"打开侧边栏": "Ouvrir la barre latérale",
|
||||
"打开授权页面": "",
|
||||
"扣费": "",
|
||||
"扣费": "Déduction",
|
||||
"执行 GC": "",
|
||||
"执行中": "En cours",
|
||||
"扫描二维码": "Scanner le code QR",
|
||||
@@ -1782,6 +1781,7 @@
|
||||
"模型价格": "Prix du modèle",
|
||||
"模型价格 {{symbol}}{{price}},{{ratioType}} {{ratio}}": "Prix du modèle {{symbol}}{{price}}, {{ratioType}} {{ratio}}",
|
||||
"模型价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "Prix du modèle : {{symbol}}{{price}} * {{ratioType}} : {{ratio}} = {{symbol}}{{total}}",
|
||||
"按次:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "Par requête : {{symbol}}{{price}} * {{ratioType}} : {{ratio}} = {{symbol}}{{total}}",
|
||||
"模型倍率": "Ratio",
|
||||
"模型倍率 {{modelRatio}}": "Ratio du modèle {{modelRatio}}",
|
||||
"模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}}": "Ratio du modèle {{modelRatio}}, ratio de cache {{cacheRatio}}, ratio de complétion {{completionRatio}}, {{ratioType}} {{ratio}}",
|
||||
@@ -1976,7 +1976,7 @@
|
||||
"渠道": "Canal",
|
||||
"渠道 ID": "ID du Canal",
|
||||
"渠道ID,名称,密钥,API地址": "ID du canal, nom, clé, URL de base",
|
||||
"渠道亲和性": "",
|
||||
"渠道亲和性": "Affinité de canal",
|
||||
"渠道亲和性:上游缓存命中": "",
|
||||
"渠道亲和性会基于从请求上下文或 JSON Body 提取的 Key,优先复用上一次成功的渠道。": "",
|
||||
"渠道优先级": "Priorité du canal",
|
||||
@@ -2075,7 +2075,7 @@
|
||||
"用户账户管理": "Comptes utilisateurs",
|
||||
"用时/首字": "Temps/premier mot",
|
||||
"由全站货币展示设置统一控制": "Contrôlé par les paramètres globaux d'affichage des devises",
|
||||
"由订阅抵扣": "",
|
||||
"由订阅抵扣": "Déduit par l'abonnement",
|
||||
"界面语言和其他个人偏好": "",
|
||||
"留空使用系统临时目录": "",
|
||||
"留空则使用账号绑定的邮箱": "Si ce champ est laissé vide, l'adresse e-mail liée au compte sera utilisée",
|
||||
@@ -2348,7 +2348,7 @@
|
||||
"缓存创建倍率 {{cacheCreationRatio}}": "Ratio de création de cache {{cacheCreationRatio}}",
|
||||
"缓存创建倍率 1h {{cacheCreationRatio1h}}": "Multiplicateur de création de cache 1h {{cacheCreationRatio1h}}",
|
||||
"缓存创建倍率 5m {{cacheCreationRatio5m}}": "Multiplicateur de création de cache 5m {{cacheCreationRatio5m}}",
|
||||
"缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Ratio de création de cache 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}",
|
||||
"缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Ratio de création du cache 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}",
|
||||
"缓存条目数": "",
|
||||
"缓存目录": "",
|
||||
"缓存目录磁盘空间": "",
|
||||
@@ -2471,7 +2471,7 @@
|
||||
"覆盖模式:将完全替换现有的所有密钥": "Mode de remplacement : remplacera complètement toutes les clés existantes",
|
||||
"覆盖模板": "",
|
||||
"覆盖现有密钥": "Remplacer les clés existantes",
|
||||
"规则": "",
|
||||
"规则": "Règle",
|
||||
"规则 JSON": "",
|
||||
"规则 JSON 格式不正确": "",
|
||||
"规则 ttl_seconds 为 0 时使用。0 表示使用后端默认 TTL:3600 秒。": "",
|
||||
@@ -2501,7 +2501,7 @@
|
||||
"订阅套餐": "Plans d'abonnement",
|
||||
"订阅套餐管理": "Gestion des plans d'abonnement",
|
||||
"订阅实例": "",
|
||||
"订阅抵扣": "",
|
||||
"订阅抵扣": "Déduction d'abonnement",
|
||||
"订阅管理": "Gestion des abonnements",
|
||||
"订阅结算": "",
|
||||
"订阅说明": "",
|
||||
@@ -2888,7 +2888,7 @@
|
||||
"进度": "calendrier",
|
||||
"进行中": "En cours",
|
||||
"进行该操作时,可能导致渠道访问错误,请仅在数据库出现问题时使用": "Lors de cette opération, cela peut entraîner des erreurs d'accès au canal. Veuillez ne l'utiliser que lorsqu'il y a un problème avec la base de données.",
|
||||
"违规扣费": "",
|
||||
"违规扣费": "Déduction pour violation",
|
||||
"违规扣费金额": "Montant de la déduction de violation",
|
||||
"连接保活设置": "Maintien connexion",
|
||||
"连接已断开": "Connexion interrompue",
|
||||
@@ -3208,7 +3208,9 @@
|
||||
"计费显示模式": "Mode d'affichage de la facturation",
|
||||
"价格模式(默认)": "Mode prix (par défaut)",
|
||||
"模型价格 {{symbol}}{{price}} / 次": "Prix du modèle {{symbol}}{{price}} / requête",
|
||||
"按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Par requête {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"模型价格:{{symbol}}{{price}} / 次": "Prix du modèle : {{symbol}}{{price}} / requête",
|
||||
"按次:{{symbol}}{{price}}": "Par requête : {{symbol}}{{price}}",
|
||||
"实际结算金额:{{symbol}}{{total}}(已包含分组价格调整)": "Montant facturé réel : {{symbol}}{{total}} (ajustement tarifaire de groupe inclus)",
|
||||
"缓存读取价格:{{symbol}}{{price}} / 1M tokens": "Prix de lecture du cache : {{symbol}}{{price}} / 1M tokens",
|
||||
"缓存读取价格 {{symbol}}{{price}} / 1M tokens": "Prix de lecture du cache {{symbol}}{{price}} / 1M tokens",
|
||||
@@ -3221,7 +3223,6 @@
|
||||
"图片输入价格:{{symbol}}{{price}} / 1M tokens": "Prix d'entrée image : {{symbol}}{{price}} / 1M tokens",
|
||||
"图片输入价格 {{symbol}}{{price}} / 1M tokens": "Prix d'entrée image {{symbol}}{{price}} / 1M tokens",
|
||||
"输入价格 {{symbol}}{{price}} / 1M tokens": "Prix d'entrée {{symbol}}{{price}} / 1M tokens",
|
||||
"补全价格 {{symbol}}{{price}} / 1M tokens": "Prix de complétion {{symbol}}{{price}} / 1M tokens",
|
||||
"音频输入价格:{{symbol}}{{price}} / 1M tokens": "Prix d'entrée audio : {{symbol}}{{price}} / 1M tokens",
|
||||
"音频补全价格:{{symbol}}{{price}} / 1M tokens": "Prix de complétion audio : {{symbol}}{{price}} / 1M tokens",
|
||||
"Web 搜索调用 {{webSearchCallCount}} 次": "Recherche Web appelée {{webSearchCallCount}} fois",
|
||||
@@ -3242,12 +3243,35 @@
|
||||
"音频输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * 音频补全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Sortie audio : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio audio {{audioRatio}} * ratio de complétion audio {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"合计:文字部分 {{textTotal}} + 音频部分 {{audioTotal}} = {{total}}": "Total : partie texte {{textTotal}} + partie audio {{audioTotal}} = {{total}}",
|
||||
"模型倍率 {{modelRatio}},输出倍率 {{completionRatio}},缓存倍率 {{cacheRatio}},{{ratioType}} {{ratio}}": "Ratio du modèle {{modelRatio}}, ratio de sortie {{completionRatio}}, ratio du cache {{cacheRatio}}, {{ratioType}} {{ratio}}",
|
||||
"缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Ratio de création du cache 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}",
|
||||
"缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Lecture du cache : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio du cache {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Création du cache : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio de création du cache {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "Création du cache 5m : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio de création du cache 5m {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "Création du cache 1h : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio de création du cache 1h {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Sortie : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio de sortie {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"空": "Vide"
|
||||
"空": "Vide",
|
||||
"{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x",
|
||||
"模型价格:{{symbol}}{{price}}": "Prix du modèle : {{symbol}}{{price}}",
|
||||
"模型价格 {{price}}": "Prix du modèle {{price}}",
|
||||
"缓存读 {{price}} / 1M tokens": "Lecture du cache {{price}} / 1M tokens",
|
||||
"5m缓存创建 {{price}} / 1M tokens": "Création de cache 5m {{price}} / 1M tokens",
|
||||
"1h缓存创建 {{price}} / 1M tokens": "Création de cache 1h {{price}} / 1M tokens",
|
||||
"缓存创建 {{price}} / 1M tokens": "Création de cache {{price}} / 1M tokens",
|
||||
"图片输入 {{price}} / 1M tokens": "Entrée image {{price}} / 1M tokens",
|
||||
"输入 {{price}} / 1M tokens": "Entrée {{price}} / 1M tokens",
|
||||
"缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Cache {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Création de cache {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Création de cache 5m {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Création de cache 1h {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}": "(Entrée {{nonImageInput}} tokens + Entrée image {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"图片输入价格:{{symbol}}{{total}} / 1M tokens": "Prix d'entrée image : {{symbol}}{{total}} / 1M tokens",
|
||||
"文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音频提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Prompt texte {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + Complétion texte {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + Prompt audio {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + Complétion audio {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"模型价格 {{symbol}}{{price}} / 次 * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Prix du modèle {{symbol}}{{price}} / requête * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"缓存读取价格:{{symbol}}{{total}} / 1M tokens": "Prix de lecture du cache : {{symbol}}{{total}} / 1M tokens",
|
||||
"补全 {{completion}} tokens * 输出倍率 {{completionRatio}}": "Complétion {{completion}} tokens * Ratio de sortie {{completionRatio}}",
|
||||
"补全倍率 {{completionRatio}}": "Ratio de complétion {{completionRatio}}",
|
||||
"输入价格:{{symbol}}{{price}} / 1M tokens": "Prix d'entrée : {{symbol}}{{price}} / 1M tokens",
|
||||
"输出价格 {{symbol}}{{price}} / 1M tokens": "Prix de sortie {{symbol}}{{price}} / 1M tokens",
|
||||
"输出价格:{{symbol}}{{price}} / 1M tokens": "Prix de sortie : {{symbol}}{{price}} / 1M tokens",
|
||||
"输出价格:{{symbol}}{{total}} / 1M tokens": "Prix de sortie : {{symbol}}{{total}} / 1M tokens"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+37
-13
@@ -17,7 +17,6 @@
|
||||
"(输入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "(入力 {{input}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"(输入 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + 音频输入 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}": "(入力 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + オーディオ入力 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}",
|
||||
"(输入 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}": "(入力 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + キャッシュ {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}",
|
||||
"(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens * {{imageRatio}} / 1M tokens * {{symbol}}{{price}}": "(入力 {{nonImageInput}} tokens + 画像入力 {{imageInput}} tokens * {{imageRatio}} / 1M tokens * {{symbol}}{{price}}",
|
||||
"[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "[最大リクエスト数]と[最大成功リクエスト数]の最大値は2147483647です",
|
||||
"[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[最大リクエスト数]は0以上、[最大成功リクエスト数]は1以上である必要があります",
|
||||
"{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}",
|
||||
@@ -151,10 +150,10 @@
|
||||
"JSON格式错误": "JSON形式エラー",
|
||||
"JSON编辑": "JSON編集",
|
||||
"JSON解析错误:": "JSONの解析エラー:",
|
||||
"Key": "",
|
||||
"Key": "Key",
|
||||
"Key 或 Path": "",
|
||||
"Key 指纹": "",
|
||||
"Key 摘要": "",
|
||||
"Key 摘要": "Key 要約",
|
||||
"Key 来源": "",
|
||||
"Key 来源类型": "",
|
||||
"Linux DO Client ID": "Linux DO Client ID",
|
||||
@@ -1349,7 +1348,7 @@
|
||||
"打开 CC Switch": "",
|
||||
"打开侧边栏": "サイドバーを展開",
|
||||
"打开授权页面": "",
|
||||
"扣费": "",
|
||||
"扣费": "課金",
|
||||
"执行 GC": "",
|
||||
"执行中": "実行中",
|
||||
"扫描二维码": "QRコードスキャン",
|
||||
@@ -1765,6 +1764,7 @@
|
||||
"模型价格": "モデル料金",
|
||||
"模型价格 {{symbol}}{{price}},{{ratioType}} {{ratio}}": "モデル料金 {{symbol}}{{price}}、{{ratioType}} {{ratio}}",
|
||||
"模型价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "モデル料金:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}",
|
||||
"按次:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "リクエストごと:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}",
|
||||
"模型倍率": "モデル倍率",
|
||||
"模型倍率 {{modelRatio}}": "Model ratio {{modelRatio}}",
|
||||
"模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}}": "モデル倍率 {{modelRatio}}、キャッシュ倍率 {{cacheRatio}}、補完倍率 {{completionRatio}}、{{ratioType}} {{ratio}}",
|
||||
@@ -1959,7 +1959,7 @@
|
||||
"渠道": "チャネル",
|
||||
"渠道 ID": "チャネルID",
|
||||
"渠道ID,名称,密钥,API地址": "チャネルID\\名称\\キー\\ベースURL",
|
||||
"渠道亲和性": "",
|
||||
"渠道亲和性": "チャネル親和性",
|
||||
"渠道亲和性:上游缓存命中": "",
|
||||
"渠道亲和性会基于从请求上下文或 JSON Body 提取的 Key,优先复用上一次成功的渠道。": "",
|
||||
"渠道优先级": "チャネル優先度",
|
||||
@@ -2058,7 +2058,7 @@
|
||||
"用户账户管理": "ユーザーアカウント管理",
|
||||
"用时/首字": "所要時間 / 初回トークン",
|
||||
"由全站货币展示设置统一控制": "サイト全体の通貨表示設定で統一して管理",
|
||||
"由订阅抵扣": "",
|
||||
"由订阅抵扣": "サブスクリプションで相殺",
|
||||
"界面语言和其他个人偏好": "",
|
||||
"留空使用系统临时目录": "",
|
||||
"留空则使用账号绑定的邮箱": "未入力の場合、アカウントに登録されているメールアドレスが使用されます",
|
||||
@@ -2329,7 +2329,7 @@
|
||||
"缓存创建倍率 {{cacheCreationRatio}}": "Cache creation ratio {{cacheCreationRatio}}",
|
||||
"缓存创建倍率 1h {{cacheCreationRatio1h}}": "キャッシュ作成倍率 1h {{cacheCreationRatio1h}}",
|
||||
"缓存创建倍率 5m {{cacheCreationRatio5m}}": "キャッシュ作成倍率 5m {{cacheCreationRatio5m}}",
|
||||
"缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Cache creation ratio 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}",
|
||||
"缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "キャッシュ作成倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}",
|
||||
"缓存条目数": "",
|
||||
"缓存目录": "",
|
||||
"缓存目录磁盘空间": "",
|
||||
@@ -2452,7 +2452,7 @@
|
||||
"覆盖模式:将完全替换现有的所有密钥": "上書きモード:既存のすべてのAPIキーを完全に置き換えます",
|
||||
"覆盖模板": "",
|
||||
"覆盖现有密钥": "既存のAPIキーを上書き",
|
||||
"规则": "",
|
||||
"规则": "ルール",
|
||||
"规则 JSON": "",
|
||||
"规则 JSON 格式不正确": "",
|
||||
"规则 ttl_seconds 为 0 时使用。0 表示使用后端默认 TTL:3600 秒。": "",
|
||||
@@ -2482,7 +2482,7 @@
|
||||
"订阅套餐": "サブスクリプションプラン",
|
||||
"订阅套餐管理": "サブスクリプションプラン管理",
|
||||
"订阅实例": "",
|
||||
"订阅抵扣": "",
|
||||
"订阅抵扣": "サブスクリプション控除",
|
||||
"订阅管理": "サブスクリプション管理",
|
||||
"订阅结算": "",
|
||||
"订阅说明": "",
|
||||
@@ -2869,7 +2869,7 @@
|
||||
"进度": "進捗",
|
||||
"进行中": "進行中",
|
||||
"进行该操作时,可能导致渠道访问错误,请仅在数据库出现问题时使用": "この操作の実行時、チャネルへのアクセスエラーが発生する可能性があります。データベースに問題がある場合のみ使用してください",
|
||||
"违规扣费": "",
|
||||
"违规扣费": "違反課金",
|
||||
"违规扣费金额": "違反課金金額",
|
||||
"连接保活设置": "接続キープアライブ設定",
|
||||
"连接已断开": "接続が切断されました",
|
||||
@@ -3189,7 +3189,9 @@
|
||||
"计费显示模式": "課金表示モード",
|
||||
"价格模式(默认)": "価格モード(デフォルト)",
|
||||
"模型价格 {{symbol}}{{price}} / 次": "モデル価格 {{symbol}}{{price}} / リクエスト",
|
||||
"按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "リクエストごと {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"模型价格:{{symbol}}{{price}} / 次": "モデル価格:{{symbol}}{{price}} / リクエスト",
|
||||
"按次:{{symbol}}{{price}}": "リクエストごと:{{symbol}}{{price}}",
|
||||
"实际结算金额:{{symbol}}{{total}}(已包含分组价格调整)": "実際の請求額:{{symbol}}{{total}}(グループ価格調整込み)",
|
||||
"缓存读取价格:{{symbol}}{{price}} / 1M tokens": "キャッシュ読み取り価格:{{symbol}}{{price}} / 1M tokens",
|
||||
"缓存读取价格 {{symbol}}{{price}} / 1M tokens": "キャッシュ読み取り価格 {{symbol}}{{price}} / 1M tokens",
|
||||
@@ -3202,7 +3204,6 @@
|
||||
"图片输入价格:{{symbol}}{{price}} / 1M tokens": "画像入力価格:{{symbol}}{{price}} / 1M tokens",
|
||||
"图片输入价格 {{symbol}}{{price}} / 1M tokens": "画像入力価格 {{symbol}}{{price}} / 1M tokens",
|
||||
"输入价格 {{symbol}}{{price}} / 1M tokens": "入力価格 {{symbol}}{{price}} / 1M tokens",
|
||||
"补全价格 {{symbol}}{{price}} / 1M tokens": "補完価格 {{symbol}}{{price}} / 1M tokens",
|
||||
"音频输入价格:{{symbol}}{{price}} / 1M tokens": "音声入力価格:{{symbol}}{{price}} / 1M tokens",
|
||||
"音频补全价格:{{symbol}}{{price}} / 1M tokens": "音声補完価格:{{symbol}}{{price}} / 1M tokens",
|
||||
"Web 搜索调用 {{webSearchCallCount}} 次": "Web 検索呼び出し {{webSearchCallCount}} 回",
|
||||
@@ -3223,12 +3224,35 @@
|
||||
"音频输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * 音频补全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "音声出力: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * 音声倍率 {{audioRatio}} * 音声補完倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"合计:文字部分 {{textTotal}} + 音频部分 {{audioTotal}} = {{total}}": "合計: テキスト部分 {{textTotal}} + 音声部分 {{audioTotal}} = {{total}}",
|
||||
"模型倍率 {{modelRatio}},输出倍率 {{completionRatio}},缓存倍率 {{cacheRatio}},{{ratioType}} {{ratio}}": "モデル倍率 {{modelRatio}}、出力倍率 {{completionRatio}}、キャッシュ倍率 {{cacheRatio}}、{{ratioType}} {{ratio}}",
|
||||
"缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "キャッシュ作成倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}",
|
||||
"缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "キャッシュ読み取り: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * キャッシュ倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "キャッシュ作成: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * キャッシュ作成倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "5m キャッシュ作成: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * 5m キャッシュ作成倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "1h キャッシュ作成: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * 1h キャッシュ作成倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "出力: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * 出力倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"空": "空"
|
||||
"空": "空",
|
||||
"{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x",
|
||||
"模型价格:{{symbol}}{{price}}": "モデル価格:{{symbol}}{{price}}",
|
||||
"模型价格 {{price}}": "モデル価格 {{price}}",
|
||||
"缓存读 {{price}} / 1M tokens": "キャッシュ読み取り {{price}} / 1M tokens",
|
||||
"5m缓存创建 {{price}} / 1M tokens": "5m キャッシュ作成 {{price}} / 1M tokens",
|
||||
"1h缓存创建 {{price}} / 1M tokens": "1h キャッシュ作成 {{price}} / 1M tokens",
|
||||
"缓存创建 {{price}} / 1M tokens": "キャッシュ作成 {{price}} / 1M tokens",
|
||||
"图片输入 {{price}} / 1M tokens": "画像入力 {{price}} / 1M tokens",
|
||||
"输入 {{price}} / 1M tokens": "入力 {{price}} / 1M tokens",
|
||||
"缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "キャッシュ {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "キャッシュ作成 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "5m キャッシュ作成 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "1h キャッシュ作成 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}": "(入力 {{nonImageInput}} tokens + 画像入力 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"图片输入价格:{{symbol}}{{total}} / 1M tokens": "画像入力価格:{{symbol}}{{total}} / 1M tokens",
|
||||
"文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音频提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "テキストプロンプト {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + テキスト補完 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音声プロンプト {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音声補完 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"模型价格 {{symbol}}{{price}} / 次 * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "モデル価格 {{symbol}}{{price}} / リクエスト * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"缓存读取价格:{{symbol}}{{total}} / 1M tokens": "キャッシュ読み取り価格:{{symbol}}{{total}} / 1M tokens",
|
||||
"补全 {{completion}} tokens * 输出倍率 {{completionRatio}}": "補完 {{completion}} tokens * 出力倍率 {{completionRatio}}",
|
||||
"补全倍率 {{completionRatio}}": "補完倍率 {{completionRatio}}",
|
||||
"输入价格:{{symbol}}{{price}} / 1M tokens": "入力価格:{{symbol}}{{price}} / 1M tokens",
|
||||
"输出价格 {{symbol}}{{price}} / 1M tokens": "補完料金 {{symbol}}{{price}} / 1M tokens",
|
||||
"输出价格:{{symbol}}{{price}} / 1M tokens": "補完料金:{{symbol}}{{price}} / 1M tokens",
|
||||
"输出价格:{{symbol}}{{total}} / 1M tokens": "補完料金:{{symbol}}{{total}} / 1M tokens"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+36
-12
@@ -24,7 +24,6 @@
|
||||
"(输入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "(Ввод {{input}} токенов / 1M токенов * {{symbol}}{{price}}",
|
||||
"(输入 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + 音频输入 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}": "(Ввод {{nonAudioInput}} токенов / 1M токенов * {{symbol}}{{price}} + аудио ввод {{audioInput}} токенов / 1M токенов * {{symbol}}{{audioPrice}}",
|
||||
"(输入 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}": "(Ввод {{nonCacheInput}} токенов / 1M токенов * {{symbol}}{{price}} + кэш {{cacheInput}} токенов / 1M токенов * {{symbol}}{{cachePrice}}",
|
||||
"(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens * {{imageRatio}} / 1M tokens * {{symbol}}{{price}}": "(Ввод {{nonImageInput}} токенов + ввод изображения {{imageInput}} токенов * {{imageRatio}} / 1M токенов * {{symbol}}{{price}}",
|
||||
"[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "[Максимальное количество запросов] и [Максимальное количество выполненных запросов] имеют максимальное значение 2147483647.",
|
||||
"[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[Максимальное количество запросов] должно быть больше или равно 0, [Максимальное количество выполненных запросов] должно быть больше или равно 1.",
|
||||
"{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}",
|
||||
@@ -158,10 +157,10 @@
|
||||
"JSON格式错误": "Ошибка формата JSON",
|
||||
"JSON编辑": "Редактирование JSON",
|
||||
"JSON解析错误:": "Ошибка парсинга JSON:",
|
||||
"Key": "",
|
||||
"Key": "Key",
|
||||
"Key 或 Path": "",
|
||||
"Key 指纹": "",
|
||||
"Key 摘要": "",
|
||||
"Key 摘要": "Сводка Key",
|
||||
"Key 来源": "",
|
||||
"Key 来源类型": "",
|
||||
"Linux DO Client ID": "ID клиента Linux DO",
|
||||
@@ -1378,7 +1377,7 @@
|
||||
"打开 CC Switch": "",
|
||||
"打开侧边栏": "Открыть боковую панель",
|
||||
"打开授权页面": "",
|
||||
"扣费": "",
|
||||
"扣费": "Списание",
|
||||
"执行 GC": "",
|
||||
"执行中": "Выполняется",
|
||||
"扫描二维码": "Сканировать QR-код",
|
||||
@@ -1794,6 +1793,7 @@
|
||||
"模型价格": "Цена модели",
|
||||
"模型价格 {{symbol}}{{price}},{{ratioType}} {{ratio}}": "Цена модели {{symbol}}{{price}}, {{ratioType}} {{ratio}}",
|
||||
"模型价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "Цена модели: {{symbol}}{{price}} * {{ratioType}}: {{ratio}} = {{symbol}}{{total}}",
|
||||
"按次:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "За запрос: {{symbol}}{{price}} * {{ratioType}}: {{ratio}} = {{symbol}}{{total}}",
|
||||
"模型倍率": "Коэффициент модели",
|
||||
"模型倍率 {{modelRatio}}": "Коэффициент модели {{modelRatio}}",
|
||||
"模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}}": "Коэффициент модели {{modelRatio}}, коэффициент кэша {{cacheRatio}}, коэффициент вывода {{completionRatio}}, {{ratioType}} {{ratio}}",
|
||||
@@ -1988,7 +1988,7 @@
|
||||
"渠道": "Канал",
|
||||
"渠道 ID": "ID канала",
|
||||
"渠道ID,名称,密钥,API地址": "ID Канала, имя, Токен, адрес API",
|
||||
"渠道亲和性": "",
|
||||
"渠道亲和性": "Аффинитет канала",
|
||||
"渠道亲和性:上游缓存命中": "",
|
||||
"渠道亲和性会基于从请求上下文或 JSON Body 提取的 Key,优先复用上一次成功的渠道。": "",
|
||||
"渠道优先级": "Приоритет канала",
|
||||
@@ -2087,7 +2087,7 @@
|
||||
"用户账户管理": "Управление аккаунтами пользователей",
|
||||
"用时/首字": "Время/первый символ",
|
||||
"由全站货币展示设置统一控制": "Управляется глобальными настройками отображения валюты",
|
||||
"由订阅抵扣": "",
|
||||
"由订阅抵扣": "Списано по подписке",
|
||||
"界面语言和其他个人偏好": "",
|
||||
"留空使用系统临时目录": "",
|
||||
"留空则使用账号绑定的邮箱": "Если оставить пустым, будет использован email, привязанный к аккаунту",
|
||||
@@ -2485,7 +2485,7 @@
|
||||
"覆盖模式:将完全替换现有的所有密钥": "Режим перезаписи: полностью заменит все существующие ключи",
|
||||
"覆盖模板": "",
|
||||
"覆盖现有密钥": "Перезаписать существующие ключи",
|
||||
"规则": "",
|
||||
"规则": "Правило",
|
||||
"规则 JSON": "",
|
||||
"规则 JSON 格式不正确": "",
|
||||
"规则 ttl_seconds 为 0 时使用。0 表示使用后端默认 TTL:3600 秒。": "",
|
||||
@@ -2515,7 +2515,7 @@
|
||||
"订阅套餐": "Планы подписки",
|
||||
"订阅套餐管理": "Управление тарифами подписки",
|
||||
"订阅实例": "",
|
||||
"订阅抵扣": "",
|
||||
"订阅抵扣": "Списание по подписке",
|
||||
"订阅管理": "Управление подписками",
|
||||
"订阅结算": "",
|
||||
"订阅说明": "",
|
||||
@@ -2902,7 +2902,7 @@
|
||||
"进度": "Прогресс",
|
||||
"进行中": "В процессе",
|
||||
"进行该操作时,可能导致渠道访问错误,请仅在数据库出现问题时使用": "При выполнении этой операции могут возникнуть ошибки доступа к каналам, используйте только при проблемах с базой данных",
|
||||
"违规扣费": "",
|
||||
"违规扣费": "Удержание за нарушение",
|
||||
"违规扣费金额": "Сумма удержания за нарушение",
|
||||
"连接保活设置": "Настройки поддержания соединения",
|
||||
"连接已断开": "Соединение разорвано",
|
||||
@@ -3222,7 +3222,9 @@
|
||||
"计费显示模式": "Режим отображения тарификации",
|
||||
"价格模式(默认)": "Режим цен (по умолчанию)",
|
||||
"模型价格 {{symbol}}{{price}} / 次": "Цена модели {{symbol}}{{price}} / запрос",
|
||||
"按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "За запрос {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"模型价格:{{symbol}}{{price}} / 次": "Цена модели: {{symbol}}{{price}} / запрос",
|
||||
"按次:{{symbol}}{{price}}": "За запрос: {{symbol}}{{price}}",
|
||||
"实际结算金额:{{symbol}}{{total}}(已包含分组价格调整)": "Фактическое списание: {{symbol}}{{total}} (включая групповую ценовую корректировку)",
|
||||
"缓存读取价格:{{symbol}}{{price}} / 1M tokens": "Цена чтения кеша: {{symbol}}{{price}} / 1M tokens",
|
||||
"缓存读取价格 {{symbol}}{{price}} / 1M tokens": "Цена чтения кеша {{symbol}}{{price}} / 1M tokens",
|
||||
@@ -3235,7 +3237,6 @@
|
||||
"图片输入价格:{{symbol}}{{price}} / 1M tokens": "Цена входного изображения: {{symbol}}{{price}} / 1M tokens",
|
||||
"图片输入价格 {{symbol}}{{price}} / 1M tokens": "Цена входного изображения {{symbol}}{{price}} / 1M tokens",
|
||||
"输入价格 {{symbol}}{{price}} / 1M tokens": "Цена ввода {{symbol}}{{price}} / 1M tokens",
|
||||
"补全价格 {{symbol}}{{price}} / 1M tokens": "Цена завершения {{symbol}}{{price}} / 1M tokens",
|
||||
"音频输入价格:{{symbol}}{{price}} / 1M tokens": "Цена входного аудио: {{symbol}}{{price}} / 1M tokens",
|
||||
"音频补全价格:{{symbol}}{{price}} / 1M tokens": "Цена завершения аудио: {{symbol}}{{price}} / 1M tokens",
|
||||
"Web 搜索调用 {{webSearchCallCount}} 次": "Web-поиск вызван {{webSearchCallCount}} раз",
|
||||
@@ -3256,12 +3257,35 @@
|
||||
"音频输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * 音频补全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Аудиовывод: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * аудио-коэффициент {{audioRatio}} * коэффициент аудиозавершения {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"合计:文字部分 {{textTotal}} + 音频部分 {{audioTotal}} = {{total}}": "Итого: текстовая часть {{textTotal}} + аудиочасть {{audioTotal}} = {{total}}",
|
||||
"模型倍率 {{modelRatio}},输出倍率 {{completionRatio}},缓存倍率 {{cacheRatio}},{{ratioType}} {{ratio}}": "Коэффициент модели {{modelRatio}}, коэффициент вывода {{completionRatio}}, коэффициент кэша {{cacheRatio}}, {{ratioType}} {{ratio}}",
|
||||
"缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Коэффициент создания кэша 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}",
|
||||
"缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Чтение кэша: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * коэффициент кэша {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Создание кэша: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * коэффициент создания кэша {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "Создание кэша 5m: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * коэффициент создания кэша 5m {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "Создание кэша 1h: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * коэффициент создания кэша 1h {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Вывод: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * коэффициент вывода {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"空": "Пусто"
|
||||
"空": "Пусто",
|
||||
"{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x",
|
||||
"模型价格:{{symbol}}{{price}}": "Цена модели: {{symbol}}{{price}}",
|
||||
"模型价格 {{price}}": "Цена модели {{price}}",
|
||||
"缓存读 {{price}} / 1M tokens": "Чтение кеша {{price}} / 1M tokens",
|
||||
"5m缓存创建 {{price}} / 1M tokens": "Создание кэша 5m {{price}} / 1M tokens",
|
||||
"1h缓存创建 {{price}} / 1M tokens": "Создание кэша 1h {{price}} / 1M tokens",
|
||||
"缓存创建 {{price}} / 1M tokens": "Создание кэша {{price}} / 1M tokens",
|
||||
"图片输入 {{price}} / 1M tokens": "Ввод изображения {{price}} / 1M tokens",
|
||||
"输入 {{price}} / 1M tokens": "Вход {{price}} / 1M tokens",
|
||||
"缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Кэш {{tokens}} токенов / 1M токенов * {{symbol}}{{price}}",
|
||||
"缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Создание кэша {{tokens}} токенов / 1M токенов * {{symbol}}{{price}}",
|
||||
"5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Создание кэша 5m {{tokens}} токенов / 1M токенов * {{symbol}}{{price}}",
|
||||
"1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Создание кэша 1h {{tokens}} токенов / 1M токенов * {{symbol}}{{price}}",
|
||||
"(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}": "(Ввод {{nonImageInput}} токенов + ввод изображения {{imageInput}} токенов / 1M токенов * {{symbol}}{{price}}",
|
||||
"图片输入价格:{{symbol}}{{total}} / 1M tokens": "Цена входного изображения: {{symbol}}{{total}} / 1M tokens",
|
||||
"文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音频提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Текстовый промпт {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + Текстовое дополнение {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + Аудио промпт {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + Аудио дополнение {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"模型价格 {{symbol}}{{price}} / 次 * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Цена модели {{symbol}}{{price}} / запрос * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"缓存读取价格:{{symbol}}{{total}} / 1M tokens": "Цена чтения кеша: {{symbol}}{{total}} / 1M tokens",
|
||||
"补全 {{completion}} tokens * 输出倍率 {{completionRatio}}": "Дополнение {{completion}} токенов * коэффициент вывода {{completionRatio}}",
|
||||
"补全倍率 {{completionRatio}}": "Коэффициент вывода {{completionRatio}}",
|
||||
"输入价格:{{symbol}}{{price}} / 1M tokens": "Цена ввода: {{symbol}}{{price}} / 1M tokens",
|
||||
"输出价格 {{symbol}}{{price}} / 1M tokens": "Цена вывода {{symbol}}{{price}} / 1M tokens",
|
||||
"输出价格:{{symbol}}{{price}} / 1M tokens": "Цена вывода: {{symbol}}{{price}} / 1M tokens",
|
||||
"输出价格:{{symbol}}{{total}} / 1M tokens": "Цена вывода: {{symbol}}{{total}} / 1M tokens"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+36
-14
@@ -17,7 +17,6 @@
|
||||
"(输入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "(Đầu vào {{input}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"(输入 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + 音频输入 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}": "(Đầu vào {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + Đầu vào âm thanh {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}",
|
||||
"(输入 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}": "(Đầu vào {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + Bộ nhớ đệm {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}",
|
||||
"(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens * {{imageRatio}} / 1M tokens * {{symbol}}{{price}}": "(Đầu vào {{nonImageInput}} tokens + Đầu vào hình ảnh {{imageInput}} tokens * {{imageRatio}} / 1M tokens * {{symbol}}{{price}}",
|
||||
"[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "Giá trị tối đa của [Số lần yêu cầu tối đa] và [Số lần hoàn thành yêu cầu tối đa] là 2147483647.",
|
||||
"[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[Số lần yêu cầu tối đa] phải lớn hơn hoặc bằng 0, [Số lần hoàn thành yêu cầu tối đa] phải lớn hơn hoặc bằng 1.",
|
||||
"{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}",
|
||||
@@ -151,10 +150,10 @@
|
||||
"JSON格式错误": "Lỗi định dạng JSON",
|
||||
"JSON编辑": "Trình chỉnh sửa JSON",
|
||||
"JSON解析错误:": "Lỗi phân tích cú pháp JSON:",
|
||||
"Key": "",
|
||||
"Key": "Key",
|
||||
"Key 或 Path": "",
|
||||
"Key 指纹": "",
|
||||
"Key 摘要": "",
|
||||
"Key 摘要": "Tóm tắt Key",
|
||||
"Key 来源": "",
|
||||
"Key 来源类型": "",
|
||||
"Linux DO Client ID": "Linux DO Client ID",
|
||||
@@ -1350,7 +1349,7 @@
|
||||
"打开 CC Switch": "",
|
||||
"打开侧边栏": "Mở thanh bên",
|
||||
"打开授权页面": "",
|
||||
"扣费": "",
|
||||
"扣费": "Khấu phí",
|
||||
"执行 GC": "",
|
||||
"执行中": "đang xử lý",
|
||||
"扫描二维码": "Quét mã QR",
|
||||
@@ -1766,6 +1765,7 @@
|
||||
"模型价格": "Giá mô hình",
|
||||
"模型价格 {{symbol}}{{price}},{{ratioType}} {{ratio}}": "Giá mô hình {{symbol}}{{price}}, {{ratioType}} {{ratio}}",
|
||||
"模型价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "Giá mô hình: {{symbol}}{{price}} * {{ratioType}}: {{ratio}} = {{symbol}}{{total}}",
|
||||
"按次:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "Theo lượt gọi: {{symbol}}{{price}} * {{ratioType}}: {{ratio}} = {{symbol}}{{total}}",
|
||||
"模型倍率": "Tỷ lệ mô hình",
|
||||
"模型倍率 {{modelRatio}}": "Model ratio {{modelRatio}}",
|
||||
"模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}}": "Tỷ lệ mô hình {{modelRatio}}, tỷ lệ bộ nhớ đệm {{cacheRatio}}, tỷ lệ hoàn thành {{completionRatio}}, {{ratioType}} {{ratio}}",
|
||||
@@ -2068,7 +2068,7 @@
|
||||
"渠道 ID": "ID kênh",
|
||||
"渠道ID": "ID kênh",
|
||||
"渠道ID,名称,密钥,API地址": "ID kênh, tên, khóa, Base URL",
|
||||
"渠道亲和性": "",
|
||||
"渠道亲和性": "Độ ưu tiên kênh",
|
||||
"渠道亲和性:上游缓存命中": "",
|
||||
"渠道亲和性会基于从请求上下文或 JSON Body 提取的 Key,优先复用上一次成功的渠道。": "",
|
||||
"渠道优先级": "Ưu tiên kênh",
|
||||
@@ -2243,7 +2243,7 @@
|
||||
"用时/首字": "Thời gian/từ đầu tiên",
|
||||
"用途": "Mục đích",
|
||||
"由全站货币展示设置统一控制": "Được điều khiển bởi cài đặt hiển thị tiền tệ toàn site",
|
||||
"由订阅抵扣": "",
|
||||
"由订阅抵扣": "Khấu trừ bởi gói đăng ký",
|
||||
"申请": "Đăng ký",
|
||||
"申请时间": "Thời gian đăng ký",
|
||||
"电子邮箱": "Email",
|
||||
@@ -2634,7 +2634,7 @@
|
||||
"缓存创建倍率 {{cacheCreationRatio}}": "Cache creation ratio {{cacheCreationRatio}}",
|
||||
"缓存创建倍率 1h {{cacheCreationRatio1h}}": "Tỷ lệ tạo bộ nhớ đệm 1h {{cacheCreationRatio1h}}",
|
||||
"缓存创建倍率 5m {{cacheCreationRatio5m}}": "Tỷ lệ tạo bộ nhớ đệm 5m {{cacheCreationRatio5m}}",
|
||||
"缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Cache creation ratio 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}",
|
||||
"缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Hệ số tạo bộ nhớ đệm 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}",
|
||||
"缓存条目数": "",
|
||||
"缓存目录": "",
|
||||
"缓存目录磁盘空间": "",
|
||||
@@ -2790,7 +2790,6 @@
|
||||
"补全 {{completion}} tokens / 1M tokens * {{symbol}}{{price}}": "Completion {{completion}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"补全价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})": "Giá hoàn thành: {{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (Tỷ lệ hoàn thành: {{completionRatio}})",
|
||||
"补全价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens": "Giá hoàn thành: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens",
|
||||
"补全价格:{{symbol}}{{price}} / 1M tokens": "Giá hoàn thành: {{symbol}}{{price}} / 1M tokens",
|
||||
"补全倍率": "Tỷ lệ hoàn thành",
|
||||
"补全倍率值": "Giá trị tỷ lệ hoàn thành",
|
||||
"补单": "Bổ sung đơn hàng",
|
||||
@@ -2806,7 +2805,7 @@
|
||||
"覆盖模式:将完全替换现有的所有密钥": "Chế độ ghi đè: sẽ thay thế hoàn toàn tất cả các khóa hiện có",
|
||||
"覆盖模板": "",
|
||||
"覆盖现有密钥": "Ghi đè khóa hiện có",
|
||||
"规则": "",
|
||||
"规则": "Quy tắc",
|
||||
"规则 JSON": "",
|
||||
"规则 JSON 格式不正确": "",
|
||||
"规则 ttl_seconds 为 0 时使用。0 表示使用后端默认 TTL:3600 秒。": "",
|
||||
@@ -2841,7 +2840,7 @@
|
||||
"订阅套餐": "Gói đăng ký",
|
||||
"订阅套餐管理": "Quản lý gói đăng ký",
|
||||
"订阅实例": "",
|
||||
"订阅抵扣": "",
|
||||
"订阅抵扣": "Khấu trừ gói đăng ký",
|
||||
"订阅管理": "Quản lý đăng ký",
|
||||
"订阅结算": "",
|
||||
"订阅说明": "",
|
||||
@@ -3358,7 +3357,7 @@
|
||||
"进度": "Tiến độ",
|
||||
"进行中": "Đang tiến hành",
|
||||
"进行该操作时,可能导致渠道访问错误,请仅在数据库出现问题时使用": "Khi thực hiện thao tác này, có thể gây ra lỗi truy cập kênh. Vui lòng chỉ sử dụng khi có vấn đề với cơ sở dữ liệu.",
|
||||
"违规扣费": "",
|
||||
"违规扣费": "Khấu phí vi phạm",
|
||||
"违规扣费金额": "Số tiền trừ phí vi phạm",
|
||||
"连接保活设置": "Cài đặt giữ kết nối",
|
||||
"连接已断开": "Kết nối đã ngắt",
|
||||
@@ -3761,7 +3760,9 @@
|
||||
"计费显示模式": "Chế độ hiển thị tính phí",
|
||||
"价格模式(默认)": "Chế độ giá (mặc định)",
|
||||
"模型价格 {{symbol}}{{price}} / 次": "Giá mô hình {{symbol}}{{price}} / lượt gọi",
|
||||
"按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Theo lượt gọi {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"模型价格:{{symbol}}{{price}} / 次": "Giá mô hình: {{symbol}}{{price}} / lượt gọi",
|
||||
"按次:{{symbol}}{{price}}": "Theo lượt gọi: {{symbol}}{{price}}",
|
||||
"实际结算金额:{{symbol}}{{total}}(已包含分组价格调整)": "Khoản phí thực tế: {{symbol}}{{total}} (đã bao gồm điều chỉnh giá theo nhóm)",
|
||||
"缓存读取价格:{{symbol}}{{price}} / 1M tokens": "Giá đọc bộ nhớ đệm: {{symbol}}{{price}} / 1M tokens",
|
||||
"缓存读取价格 {{symbol}}{{price}} / 1M tokens": "Giá đọc bộ nhớ đệm {{symbol}}{{price}} / 1M tokens",
|
||||
@@ -3774,7 +3775,6 @@
|
||||
"图片输入价格:{{symbol}}{{price}} / 1M tokens": "Giá đầu vào hình ảnh: {{symbol}}{{price}} / 1M tokens",
|
||||
"图片输入价格 {{symbol}}{{price}} / 1M tokens": "Giá đầu vào hình ảnh {{symbol}}{{price}} / 1M tokens",
|
||||
"输入价格 {{symbol}}{{price}} / 1M tokens": "Giá đầu vào {{symbol}}{{price}} / 1M tokens",
|
||||
"补全价格 {{symbol}}{{price}} / 1M tokens": "Giá hoàn thành {{symbol}}{{price}} / 1M tokens",
|
||||
"音频输入价格:{{symbol}}{{price}} / 1M tokens": "Giá đầu vào âm thanh: {{symbol}}{{price}} / 1M tokens",
|
||||
"音频补全价格:{{symbol}}{{price}} / 1M tokens": "Giá hoàn thành âm thanh: {{symbol}}{{price}} / 1M tokens",
|
||||
"Web 搜索调用 {{webSearchCallCount}} 次": "Đã gọi tìm kiếm Web {{webSearchCallCount}} lần",
|
||||
@@ -3795,12 +3795,34 @@
|
||||
"音频输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * 音频补全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Đầu ra âm thanh: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số âm thanh {{audioRatio}} * hệ số hoàn thành âm thanh {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"合计:文字部分 {{textTotal}} + 音频部分 {{audioTotal}} = {{total}}": "Tổng cộng: phần văn bản {{textTotal}} + phần âm thanh {{audioTotal}} = {{total}}",
|
||||
"模型倍率 {{modelRatio}},输出倍率 {{completionRatio}},缓存倍率 {{cacheRatio}},{{ratioType}} {{ratio}}": "Hệ số mô hình {{modelRatio}}, hệ số đầu ra {{completionRatio}}, hệ số bộ nhớ đệm {{cacheRatio}}, {{ratioType}} {{ratio}}",
|
||||
"缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Hệ số tạo bộ nhớ đệm 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}",
|
||||
"缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Đọc bộ nhớ đệm: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số bộ nhớ đệm {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Tạo bộ nhớ đệm: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số tạo bộ nhớ đệm {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "Tạo bộ nhớ đệm 5m: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số tạo bộ nhớ đệm 5m {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "Tạo bộ nhớ đệm 1h: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số tạo bộ nhớ đệm 1h {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Đầu ra: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số đầu ra {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"空": "Trống"
|
||||
"空": "Trống",
|
||||
"{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x",
|
||||
"模型价格:{{symbol}}{{price}}": "Giá mô hình: {{symbol}}{{price}}",
|
||||
"模型价格 {{price}}": "Giá mô hình {{price}}",
|
||||
"缓存读 {{price}} / 1M tokens": "Đọc bộ nhớ đệm {{price}} / 1M tokens",
|
||||
"5m缓存创建 {{price}} / 1M tokens": "Tạo bộ nhớ đệm 5m {{price}} / 1M tokens",
|
||||
"1h缓存创建 {{price}} / 1M tokens": "Tạo bộ nhớ đệm 1h {{price}} / 1M tokens",
|
||||
"缓存创建 {{price}} / 1M tokens": "Tạo bộ nhớ đệm {{price}} / 1M tokens",
|
||||
"图片输入 {{price}} / 1M tokens": "Đầu vào hình ảnh {{price}} / 1M tokens",
|
||||
"输入 {{price}} / 1M tokens": "Đầu vào {{price}} / 1M tokens",
|
||||
"缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Bộ nhớ đệm {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Tạo bộ nhớ đệm {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Tạo bộ nhớ đệm 5m {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Tạo bộ nhớ đệm 1h {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}": "(Đầu vào {{nonImageInput}} tokens + Đầu vào hình ảnh {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"图片输入价格:{{symbol}}{{total}} / 1M tokens": "Giá đầu vào hình ảnh: {{symbol}}{{total}} / 1M tokens",
|
||||
"文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音频提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Prompt văn bản {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + Hoàn thành văn bản {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + Prompt âm thanh {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + Hoàn thành âm thanh {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"模型价格 {{symbol}}{{price}} / 次 * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Giá mô hình {{symbol}}{{price}} / lượt gọi * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"缓存读取价格:{{symbol}}{{total}} / 1M tokens": "Giá đọc bộ nhớ đệm: {{symbol}}{{total}} / 1M tokens",
|
||||
"补全 {{completion}} tokens * 输出倍率 {{completionRatio}}": "Hoàn thành {{completion}} tokens * Tỷ lệ đầu ra {{completionRatio}}",
|
||||
"补全倍率 {{completionRatio}}": "Tỷ lệ hoàn thành {{completionRatio}}",
|
||||
"输出价格 {{symbol}}{{price}} / 1M tokens": "Giá đầu ra {{symbol}}{{price}} / 1M tokens",
|
||||
"输出价格:{{symbol}}{{price}} / 1M tokens": "Giá đầu ra: {{symbol}}{{price}} / 1M tokens",
|
||||
"输出价格:{{symbol}}{{total}} / 1M tokens": "Giá đầu ra: {{symbol}}{{total}} / 1M tokens"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+36
-11
@@ -13,7 +13,6 @@
|
||||
"(输入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "(输入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"(输入 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + 音频输入 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}": "(输入 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + 音频输入 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}",
|
||||
"(输入 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}": "(输入 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}",
|
||||
"(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens * {{imageRatio}} / 1M tokens * {{symbol}}{{price}}": "(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens * {{imageRatio}} / 1M tokens * {{symbol}}{{price}}",
|
||||
"[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "[最多请求次数]和[最多请求完成次数]的最大值为2147483647。",
|
||||
"[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。",
|
||||
"{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}",
|
||||
@@ -1106,7 +1105,6 @@
|
||||
"按倍率类型筛选": "按倍率类型筛选",
|
||||
"按倍率设置": "按倍率设置",
|
||||
"按次": "按次",
|
||||
"按次 {{price}} / 次": "按次 {{price}} / 次",
|
||||
"按次计费": "按次计费",
|
||||
"按照如下格式输入:AccessKey|SecretAccessKey|Region": "按照如下格式输入:AccessKey|SecretAccessKey|Region",
|
||||
"按量计费": "按量计费",
|
||||
@@ -1430,6 +1428,7 @@
|
||||
"模型价格": "模型价格",
|
||||
"模型价格 {{symbol}}{{price}},{{ratioType}} {{ratio}}": "模型价格 {{symbol}}{{price}},{{ratioType}} {{ratio}}",
|
||||
"模型价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "模型价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}",
|
||||
"按次:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "按次:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}",
|
||||
"模型倍率": "模型倍率",
|
||||
"模型倍率 {{modelRatio}}": "模型倍率 {{modelRatio}}",
|
||||
"模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}}": "模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}}",
|
||||
@@ -2867,26 +2866,21 @@
|
||||
"计费显示模式": "计费显示模式",
|
||||
"价格模式(默认)": "价格模式(默认)",
|
||||
"模型价格 {{symbol}}{{price}} / 次": "模型价格 {{symbol}}{{price}} / 次",
|
||||
"按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"模型价格:{{symbol}}{{price}} / 次": "模型价格:{{symbol}}{{price}} / 次",
|
||||
"输入 {{price}} / 1M tokens": "输入 {{price}} / 1M tokens",
|
||||
"按次:{{symbol}}{{price}}": "按次:{{symbol}}{{price}}",
|
||||
"实际结算金额:{{symbol}}{{total}}(已包含分组价格调整)": "实际结算金额:{{symbol}}{{total}}(已包含分组价格调整)",
|
||||
"缓存读取价格:{{symbol}}{{price}} / 1M tokens": "缓存读取价格:{{symbol}}{{price}} / 1M tokens",
|
||||
"缓存读取价格 {{symbol}}{{price}} / 1M tokens": "缓存读取价格 {{symbol}}{{price}} / 1M tokens",
|
||||
"缓存读取 {{price}}": "缓存读取 {{price}}",
|
||||
"缓存创建价格:{{symbol}}{{price}} / 1M tokens": "缓存创建价格:{{symbol}}{{price}} / 1M tokens",
|
||||
"缓存创建价格 {{symbol}}{{price}} / 1M tokens": "缓存创建价格 {{symbol}}{{price}} / 1M tokens",
|
||||
"缓存创建 {{price}}": "缓存创建 {{price}}",
|
||||
"5m缓存创建价格:{{symbol}}{{price}} / 1M tokens": "5m缓存创建价格:{{symbol}}{{price}} / 1M tokens",
|
||||
"5m缓存创建价格 {{symbol}}{{price}} / 1M tokens": "5m缓存创建价格 {{symbol}}{{price}} / 1M tokens",
|
||||
"5m缓存创建 {{price}}": "5m缓存创建 {{price}}",
|
||||
"1h缓存创建价格:{{symbol}}{{price}} / 1M tokens": "1h缓存创建价格:{{symbol}}{{price}} / 1M tokens",
|
||||
"1h缓存创建价格 {{symbol}}{{price}} / 1M tokens": "1h缓存创建价格 {{symbol}}{{price}} / 1M tokens",
|
||||
"1h缓存创建 {{price}}": "1h缓存创建 {{price}}",
|
||||
"图片输入价格:{{symbol}}{{price}} / 1M tokens": "图片输入价格:{{symbol}}{{price}} / 1M tokens",
|
||||
"图片输入价格 {{symbol}}{{price}} / 1M tokens": "图片输入价格 {{symbol}}{{price}} / 1M tokens",
|
||||
"图片输入 {{price}}": "图片输入 {{price}}",
|
||||
"输入价格 {{symbol}}{{price}} / 1M tokens": "输入价格 {{symbol}}{{price}} / 1M tokens",
|
||||
"补全价格 {{symbol}}{{price}} / 1M tokens": "补全价格 {{symbol}}{{price}} / 1M tokens",
|
||||
"音频输入价格:{{symbol}}{{price}} / 1M tokens": "音频输入价格:{{symbol}}{{price}} / 1M tokens",
|
||||
"音频补全价格:{{symbol}}{{price}} / 1M tokens": "音频补全价格:{{symbol}}{{price}} / 1M tokens",
|
||||
"Web 搜索调用 {{webSearchCallCount}} 次": "Web 搜索调用 {{webSearchCallCount}} 次",
|
||||
@@ -2907,12 +2901,43 @@
|
||||
"音频输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * 音频补全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "音频输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * 音频补全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"合计:文字部分 {{textTotal}} + 音频部分 {{audioTotal}} = {{total}}": "合计:文字部分 {{textTotal}} + 音频部分 {{audioTotal}} = {{total}}",
|
||||
"模型倍率 {{modelRatio}},输出倍率 {{completionRatio}},缓存倍率 {{cacheRatio}},{{ratioType}} {{ratio}}": "模型倍率 {{modelRatio}},输出倍率 {{completionRatio}},缓存倍率 {{cacheRatio}},{{ratioType}} {{ratio}}",
|
||||
"缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}",
|
||||
"缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"空": "空"
|
||||
"空": "空",
|
||||
"{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x",
|
||||
"模型价格:{{symbol}}{{price}}": "模型价格:{{symbol}}{{price}}",
|
||||
"模型价格 {{price}}": "模型价格 {{price}}",
|
||||
"缓存读 {{price}} / 1M tokens": "缓存读 {{price}} / 1M tokens",
|
||||
"5m缓存创建 {{price}} / 1M tokens": "5m缓存创建 {{price}} / 1M tokens",
|
||||
"1h缓存创建 {{price}} / 1M tokens": "1h缓存创建 {{price}} / 1M tokens",
|
||||
"缓存创建 {{price}} / 1M tokens": "缓存创建 {{price}} / 1M tokens",
|
||||
"图片输入 {{price}} / 1M tokens": "图片输入 {{price}} / 1M tokens",
|
||||
"输入 {{price}} / 1M tokens": "输入 {{price}} / 1M tokens",
|
||||
"缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"Key": "Key",
|
||||
"Key 摘要": "Key 摘要",
|
||||
"扣费": "扣费",
|
||||
"渠道亲和性": "渠道亲和性",
|
||||
"由订阅抵扣": "由订阅抵扣",
|
||||
"规则": "规则",
|
||||
"订阅抵扣": "订阅抵扣",
|
||||
"违规扣费": "违规扣费",
|
||||
"(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}": "(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"图片输入价格:{{symbol}}{{total}} / 1M tokens": "图片输入价格:{{symbol}}{{total}} / 1M tokens",
|
||||
"文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音频提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音频提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"模型价格 {{symbol}}{{price}} / 次 * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "模型价格 {{symbol}}{{price}} / 次 * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"缓存读取价格:{{symbol}}{{total}} / 1M tokens": "缓存读取价格:{{symbol}}{{total}} / 1M tokens",
|
||||
"补全 {{completion}} tokens * 输出倍率 {{completionRatio}}": "补全 {{completion}} tokens * 输出倍率 {{completionRatio}}",
|
||||
"补全倍率 {{completionRatio}}": "补全倍率 {{completionRatio}}",
|
||||
"输入价格:{{symbol}}{{price}} / 1M tokens": "输入价格:{{symbol}}{{price}} / 1M tokens",
|
||||
"输出价格 {{symbol}}{{price}} / 1M tokens": "输出价格 {{symbol}}{{price}} / 1M tokens",
|
||||
"输出价格:{{symbol}}{{price}} / 1M tokens": "输出价格:{{symbol}}{{price}} / 1M tokens",
|
||||
"输出价格:{{symbol}}{{total}} / 1M tokens": "输出价格:{{symbol}}{{total}} / 1M tokens"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+42
-11
@@ -13,7 +13,6 @@
|
||||
"(输入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "(輸入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"(输入 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + 音频输入 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}": "(輸入 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + 音訊輸入 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}",
|
||||
"(输入 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}": "(輸入 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 快取 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}",
|
||||
"(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens * {{imageRatio}} / 1M tokens * {{symbol}}{{price}}": "(輸入 {{nonImageInput}} tokens + 圖片輸入 {{imageInput}} tokens * {{imageRatio}} / 1M tokens * {{symbol}}{{price}}",
|
||||
"[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "[最多請求次數]和[最多請求完成次數]的最大值為2147483647。",
|
||||
"[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[最多請求次數]必須大於等於0,[最多請求完成次數]必須大於等於1。",
|
||||
"{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}",
|
||||
@@ -1109,7 +1108,6 @@
|
||||
"按倍率类型筛选": "按倍率類型篩選",
|
||||
"按倍率设置": "按倍率設定",
|
||||
"按次": "按次",
|
||||
"按次 {{price}} / 次": "按次 {{price}} / 次",
|
||||
"按次计费": "按次計費",
|
||||
"按照如下格式输入:AccessKey|SecretAccessKey|Region": "按照如下格式輸入:AccessKey|SecretAccessKey|Region",
|
||||
"按量计费": "按量計費",
|
||||
@@ -1436,6 +1434,7 @@
|
||||
"模型价格": "模型價格",
|
||||
"模型价格 {{symbol}}{{price}},{{ratioType}} {{ratio}}": "模型價格 {{symbol}}{{price}},{{ratioType}} {{ratio}}",
|
||||
"模型价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "模型價格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}",
|
||||
"按次:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "按次:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}",
|
||||
"模型倍率": "模型倍率",
|
||||
"模型倍率 {{modelRatio}}": "模型倍率 {{modelRatio}}",
|
||||
"模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}}": "模型倍率 {{modelRatio}},快取倍率 {{cacheRatio}},輸出倍率 {{completionRatio}},{{ratioType}} {{ratio}}",
|
||||
@@ -2860,26 +2859,21 @@
|
||||
"计费显示模式": "計費顯示模式",
|
||||
"价格模式(默认)": "價格模式(預設)",
|
||||
"模型价格 {{symbol}}{{price}} / 次": "模型價格 {{symbol}}{{price}} / 次",
|
||||
"按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"模型价格:{{symbol}}{{price}} / 次": "模型價格:{{symbol}}{{price}} / 次",
|
||||
"输入 {{price}} / 1M tokens": "輸入 {{price}} / 1M tokens",
|
||||
"按次:{{symbol}}{{price}}": "按次:{{symbol}}{{price}}",
|
||||
"实际结算金额:{{symbol}}{{total}}(已包含分组价格调整)": "實際結算金額:{{symbol}}{{total}}(已包含分組價格調整)",
|
||||
"缓存读取价格:{{symbol}}{{price}} / 1M tokens": "快取讀取價格:{{symbol}}{{price}} / 1M tokens",
|
||||
"缓存读取价格 {{symbol}}{{price}} / 1M tokens": "快取讀取價格 {{symbol}}{{price}} / 1M tokens",
|
||||
"缓存读取 {{price}}": "快取讀取 {{price}}",
|
||||
"缓存创建价格:{{symbol}}{{price}} / 1M tokens": "快取建立價格:{{symbol}}{{price}} / 1M tokens",
|
||||
"缓存创建价格 {{symbol}}{{price}} / 1M tokens": "快取建立價格 {{symbol}}{{price}} / 1M tokens",
|
||||
"缓存创建 {{price}}": "快取建立 {{price}}",
|
||||
"5m缓存创建价格:{{symbol}}{{price}} / 1M tokens": "5m快取建立價格:{{symbol}}{{price}} / 1M tokens",
|
||||
"5m缓存创建价格 {{symbol}}{{price}} / 1M tokens": "5m快取建立價格 {{symbol}}{{price}} / 1M tokens",
|
||||
"5m缓存创建 {{price}}": "5m快取建立 {{price}}",
|
||||
"1h缓存创建价格:{{symbol}}{{price}} / 1M tokens": "1h快取建立價格:{{symbol}}{{price}} / 1M tokens",
|
||||
"1h缓存创建价格 {{symbol}}{{price}} / 1M tokens": "1h快取建立價格 {{symbol}}{{price}} / 1M tokens",
|
||||
"1h缓存创建 {{price}}": "1h快取建立 {{price}}",
|
||||
"图片输入价格:{{symbol}}{{price}} / 1M tokens": "圖片輸入價格:{{symbol}}{{price}} / 1M tokens",
|
||||
"图片输入价格 {{symbol}}{{price}} / 1M tokens": "圖片輸入價格 {{symbol}}{{price}} / 1M tokens",
|
||||
"图片输入 {{price}}": "圖片輸入 {{price}}",
|
||||
"输入价格 {{symbol}}{{price}} / 1M tokens": "輸入價格 {{symbol}}{{price}} / 1M tokens",
|
||||
"补全价格 {{symbol}}{{price}} / 1M tokens": "補全價格 {{symbol}}{{price}} / 1M tokens",
|
||||
"音频输入价格:{{symbol}}{{price}} / 1M tokens": "音訊輸入價格:{{symbol}}{{price}} / 1M tokens",
|
||||
"音频补全价格:{{symbol}}{{price}} / 1M tokens": "音訊補全價格:{{symbol}}{{price}} / 1M tokens",
|
||||
"Web 搜索调用 {{webSearchCallCount}} 次": "Web 搜尋呼叫 {{webSearchCallCount}} 次",
|
||||
@@ -2900,12 +2894,49 @@
|
||||
"音频输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * 音频补全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "音訊輸出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音訊倍率 {{audioRatio}} * 音訊補全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"合计:文字部分 {{textTotal}} + 音频部分 {{audioTotal}} = {{total}}": "合計:文字部分 {{textTotal}} + 音訊部分 {{audioTotal}} = {{total}}",
|
||||
"模型倍率 {{modelRatio}},输出倍率 {{completionRatio}},缓存倍率 {{cacheRatio}},{{ratioType}} {{ratio}}": "模型倍率 {{modelRatio}},輸出倍率 {{completionRatio}},快取倍率 {{cacheRatio}},{{ratioType}} {{ratio}}",
|
||||
"缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "快取建立倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}",
|
||||
"缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "快取讀取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 快取倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "快取建立:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 快取建立倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "5m快取建立:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m快取建立倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "1h快取建立:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h快取建立倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "輸出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 輸出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
|
||||
"空": "空"
|
||||
"空": "空",
|
||||
"{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x",
|
||||
"模型价格:{{symbol}}{{price}}": "模型價格:{{symbol}}{{price}}",
|
||||
"模型价格 {{price}}": "模型價格 {{price}}",
|
||||
"缓存读 {{price}} / 1M tokens": "快取讀 {{price}} / 1M tokens",
|
||||
"5m缓存创建 {{price}} / 1M tokens": "5m快取建立 {{price}} / 1M tokens",
|
||||
"1h缓存创建 {{price}} / 1M tokens": "1h快取建立 {{price}} / 1M tokens",
|
||||
"缓存创建 {{price}} / 1M tokens": "快取建立 {{price}} / 1M tokens",
|
||||
"图片输入 {{price}} / 1M tokens": "圖片輸入 {{price}} / 1M tokens",
|
||||
"输入 {{price}} / 1M tokens": "輸入 {{price}} / 1M tokens",
|
||||
"缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "快取 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "快取建立 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "5m快取建立 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "1h快取建立 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"Key": "Key",
|
||||
"Key 摘要": "Key 摘要",
|
||||
"写": "寫",
|
||||
"异步任务退款": "非同步任務退款",
|
||||
"扣费": "扣費",
|
||||
"根据 Anthropic 协定,/v1/messages 的输入 tokens 仅统计非缓存输入,不包含缓存读取与缓存写入 tokens。": "根據 Anthropic 協定,/v1/messages 的輸入 tokens 僅統計非快取輸入,不包含快取讀取與快取寫入 tokens。",
|
||||
"渠道亲和性": "渠道親和性",
|
||||
"由订阅抵扣": "由訂閱抵扣",
|
||||
"缓存写": "快取寫",
|
||||
"缓存读": "快取讀",
|
||||
"规则": "規則",
|
||||
"订阅抵扣": "訂閱抵扣",
|
||||
"违规扣费": "違規扣費",
|
||||
"退款": "退款",
|
||||
"(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}": "(輸入 {{nonImageInput}} tokens + 圖片輸入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}",
|
||||
"图片输入价格:{{symbol}}{{total}} / 1M tokens": "圖片輸入價格:{{symbol}}{{total}} / 1M tokens",
|
||||
"文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音频提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字補全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音訊提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音訊補全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"模型价格 {{symbol}}{{price}} / 次 * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "模型價格 {{symbol}}{{price}} / 次 * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
|
||||
"缓存读取价格:{{symbol}}{{total}} / 1M tokens": "快取讀取價格:{{symbol}}{{total}} / 1M tokens",
|
||||
"补全 {{completion}} tokens * 输出倍率 {{completionRatio}}": "補全 {{completion}} tokens * 輸出倍率 {{completionRatio}}",
|
||||
"补全倍率 {{completionRatio}}": "補全倍率 {{completionRatio}}",
|
||||
"输入价格:{{symbol}}{{price}} / 1M tokens": "輸入價格:{{symbol}}{{price}} / 1M tokens",
|
||||
"输出价格 {{symbol}}{{price}} / 1M tokens": "輸出價格 {{symbol}}{{price}} / 1M tokens",
|
||||
"输出价格:{{symbol}}{{price}} / 1M tokens": "輸出價格:{{symbol}}{{price}} / 1M tokens",
|
||||
"输出价格:{{symbol}}{{total}} / 1M tokens": "輸出價格:{{symbol}}{{total}} / 1M tokens"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,11 @@ const formatNumber = (value) => {
|
||||
return parseFloat(num.toFixed(12)).toString();
|
||||
};
|
||||
|
||||
const toNormalizedNumber = (value) => {
|
||||
const formatted = formatNumber(value);
|
||||
return formatted === '' ? null : Number(formatted);
|
||||
};
|
||||
|
||||
const parseOptionJSON = (rawValue) => {
|
||||
if (!rawValue || rawValue.trim() === '') {
|
||||
return {};
|
||||
@@ -123,7 +128,11 @@ const buildModelState = (name, sourceMaps) => {
|
||||
lockedCompletionRatio: completionRatioMeta.ratio,
|
||||
completionPrice:
|
||||
inputPriceNumber !== null &&
|
||||
hasValue(completionRatioMeta.locked ? completionRatioMeta.ratio : completionRatio)
|
||||
hasValue(
|
||||
completionRatioMeta.locked
|
||||
? completionRatioMeta.ratio
|
||||
: completionRatio,
|
||||
)
|
||||
? formatNumber(
|
||||
inputPriceNumber *
|
||||
Number(
|
||||
@@ -192,7 +201,9 @@ export const getModelWarnings = (model, t) => {
|
||||
].some(hasValue);
|
||||
|
||||
if (model.hasConflict) {
|
||||
warnings.push(t('当前模型同时存在按次价格和倍率配置,保存时会按当前计费方式覆盖。'));
|
||||
warnings.push(
|
||||
t('当前模型同时存在按次价格和倍率配置,保存时会按当前计费方式覆盖。'),
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -207,11 +218,17 @@ export const getModelWarnings = (model, t) => {
|
||||
].some(hasValue)
|
||||
) {
|
||||
warnings.push(
|
||||
t('当前模型存在未显式设置输入倍率的扩展倍率;填写输入价格后会自动换算为价格字段。'),
|
||||
t(
|
||||
'当前模型存在未显式设置输入倍率的扩展倍率;填写输入价格后会自动换算为价格字段。',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (model.billingMode === 'per-token' && hasDerivedPricing && !hasValue(model.inputPrice)) {
|
||||
if (
|
||||
model.billingMode === 'per-token' &&
|
||||
hasDerivedPricing &&
|
||||
!hasValue(model.inputPrice)
|
||||
) {
|
||||
warnings.push(t('按量计费下需要先填写输入价格,才能保存其它价格项。'));
|
||||
}
|
||||
|
||||
@@ -249,7 +266,8 @@ export const buildSummaryText = (model, t) => {
|
||||
};
|
||||
|
||||
export const buildOptionalFieldToggles = (model) => ({
|
||||
completionPrice: model.completionRatioLocked || hasValue(model.completionPrice),
|
||||
completionPrice:
|
||||
model.completionRatioLocked || hasValue(model.completionPrice),
|
||||
cachePrice: hasValue(model.cachePrice),
|
||||
createCachePrice: hasValue(model.createCachePrice),
|
||||
imagePrice: hasValue(model.imagePrice),
|
||||
@@ -271,7 +289,7 @@ const serializeModel = (model, t) => {
|
||||
|
||||
if (model.billingMode === 'per-request') {
|
||||
if (hasValue(model.fixedPrice)) {
|
||||
result.ModelPrice = Number(model.fixedPrice);
|
||||
result.ModelPrice = toNormalizedNumber(model.fixedPrice);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -296,57 +314,68 @@ const serializeModel = (model, t) => {
|
||||
if (inputPrice === null) {
|
||||
if (hasDependentPrice) {
|
||||
throw new Error(
|
||||
t('模型 {{name}} 缺少输入价格,无法计算补全/缓存/图片/音频价格对应的倍率', {
|
||||
name: model.name,
|
||||
}),
|
||||
t(
|
||||
'模型 {{name}} 缺少输入价格,无法计算补全/缓存/图片/音频价格对应的倍率',
|
||||
{
|
||||
name: model.name,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (hasValue(model.rawRatios.modelRatio)) {
|
||||
result.ModelRatio = Number(model.rawRatios.modelRatio);
|
||||
result.ModelRatio = toNormalizedNumber(model.rawRatios.modelRatio);
|
||||
}
|
||||
if (hasValue(model.rawRatios.completionRatio)) {
|
||||
result.CompletionRatio = Number(model.rawRatios.completionRatio);
|
||||
result.CompletionRatio = toNormalizedNumber(
|
||||
model.rawRatios.completionRatio,
|
||||
);
|
||||
}
|
||||
if (hasValue(model.rawRatios.cacheRatio)) {
|
||||
result.CacheRatio = Number(model.rawRatios.cacheRatio);
|
||||
result.CacheRatio = toNormalizedNumber(model.rawRatios.cacheRatio);
|
||||
}
|
||||
if (hasValue(model.rawRatios.createCacheRatio)) {
|
||||
result.CreateCacheRatio = Number(model.rawRatios.createCacheRatio);
|
||||
result.CreateCacheRatio = toNormalizedNumber(
|
||||
model.rawRatios.createCacheRatio,
|
||||
);
|
||||
}
|
||||
if (hasValue(model.rawRatios.imageRatio)) {
|
||||
result.ImageRatio = Number(model.rawRatios.imageRatio);
|
||||
result.ImageRatio = toNormalizedNumber(model.rawRatios.imageRatio);
|
||||
}
|
||||
if (hasValue(model.rawRatios.audioRatio)) {
|
||||
result.AudioRatio = Number(model.rawRatios.audioRatio);
|
||||
result.AudioRatio = toNormalizedNumber(model.rawRatios.audioRatio);
|
||||
}
|
||||
if (hasValue(model.rawRatios.audioCompletionRatio)) {
|
||||
result.AudioCompletionRatio = Number(model.rawRatios.audioCompletionRatio);
|
||||
result.AudioCompletionRatio = toNormalizedNumber(
|
||||
model.rawRatios.audioCompletionRatio,
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
result.ModelRatio = inputPrice / 2;
|
||||
result.ModelRatio = toNormalizedNumber(inputPrice / 2);
|
||||
|
||||
if (!model.completionRatioLocked && completionPrice !== null) {
|
||||
result.CompletionRatio = completionPrice / inputPrice;
|
||||
result.CompletionRatio = toNormalizedNumber(completionPrice / inputPrice);
|
||||
} else if (
|
||||
model.completionRatioLocked &&
|
||||
hasValue(model.rawRatios.completionRatio)
|
||||
) {
|
||||
result.CompletionRatio = Number(model.rawRatios.completionRatio);
|
||||
result.CompletionRatio = toNormalizedNumber(
|
||||
model.rawRatios.completionRatio,
|
||||
);
|
||||
}
|
||||
if (cachePrice !== null) {
|
||||
result.CacheRatio = cachePrice / inputPrice;
|
||||
result.CacheRatio = toNormalizedNumber(cachePrice / inputPrice);
|
||||
}
|
||||
if (createCachePrice !== null) {
|
||||
result.CreateCacheRatio = createCachePrice / inputPrice;
|
||||
result.CreateCacheRatio = toNormalizedNumber(createCachePrice / inputPrice);
|
||||
}
|
||||
if (imagePrice !== null) {
|
||||
result.ImageRatio = imagePrice / inputPrice;
|
||||
result.ImageRatio = toNormalizedNumber(imagePrice / inputPrice);
|
||||
}
|
||||
if (audioInputPrice !== null) {
|
||||
result.AudioRatio = audioInputPrice / inputPrice;
|
||||
result.AudioRatio = toNormalizedNumber(audioInputPrice / inputPrice);
|
||||
}
|
||||
if (audioOutputPrice !== null) {
|
||||
if (audioInputPrice === null || audioInputPrice === 0) {
|
||||
@@ -356,7 +385,9 @@ const serializeModel = (model, t) => {
|
||||
}),
|
||||
);
|
||||
}
|
||||
result.AudioCompletionRatio = audioOutputPrice / audioInputPrice;
|
||||
result.AudioCompletionRatio = toNormalizedNumber(
|
||||
audioOutputPrice / audioInputPrice,
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -455,7 +486,8 @@ export const buildPreviewRows = (model, t) => {
|
||||
{
|
||||
key: 'CacheRatio',
|
||||
label: 'CacheRatio',
|
||||
value: cachePrice !== null ? formatNumber(cachePrice / inputPrice) : t('空'),
|
||||
value:
|
||||
cachePrice !== null ? formatNumber(cachePrice / inputPrice) : t('空'),
|
||||
},
|
||||
{
|
||||
key: 'CreateCacheRatio',
|
||||
@@ -468,7 +500,8 @@ export const buildPreviewRows = (model, t) => {
|
||||
{
|
||||
key: 'ImageRatio',
|
||||
label: 'ImageRatio',
|
||||
value: imagePrice !== null ? formatNumber(imagePrice / inputPrice) : t('空'),
|
||||
value:
|
||||
imagePrice !== null ? formatNumber(imagePrice / inputPrice) : t('空'),
|
||||
},
|
||||
{
|
||||
key: 'AudioRatio',
|
||||
@@ -482,7 +515,9 @@ export const buildPreviewRows = (model, t) => {
|
||||
key: 'AudioCompletionRatio',
|
||||
label: 'AudioCompletionRatio',
|
||||
value:
|
||||
audioOutputPrice !== null && audioInputPrice !== null && audioInputPrice !== 0
|
||||
audioOutputPrice !== null &&
|
||||
audioInputPrice !== null &&
|
||||
audioInputPrice !== 0
|
||||
? formatNumber(audioOutputPrice / audioInputPrice)
|
||||
: t('空'),
|
||||
},
|
||||
@@ -585,7 +620,8 @@ export function useModelPricingEditorState({
|
||||
}, [currentPage, filteredModels]);
|
||||
|
||||
const selectedModel = useMemo(
|
||||
() => visibleModels.find((model) => model.name === selectedModelName) || null,
|
||||
() =>
|
||||
visibleModels.find((model) => model.name === selectedModelName) || null,
|
||||
[selectedModelName, visibleModels],
|
||||
);
|
||||
|
||||
@@ -605,7 +641,9 @@ export function useModelPricingEditorState({
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedModelNames((previous) =>
|
||||
previous.filter((name) => visibleModels.some((model) => model.name === name)),
|
||||
previous.filter((name) =>
|
||||
visibleModels.some((model) => model.name === name),
|
||||
),
|
||||
);
|
||||
}, [visibleModels]);
|
||||
|
||||
@@ -779,7 +817,9 @@ export function useModelPricingEditorState({
|
||||
delete next[name];
|
||||
return next;
|
||||
});
|
||||
setSelectedModelNames((previous) => previous.filter((item) => item !== name));
|
||||
setSelectedModelNames((previous) =>
|
||||
previous.filter((item) => item !== name),
|
||||
);
|
||||
if (selectedModelName === name) {
|
||||
setSelectedModelName(nextModels[0]?.name || '');
|
||||
}
|
||||
@@ -823,7 +863,8 @@ export function useModelPricingEditorState({
|
||||
hasValue(nextModel.lockedCompletionRatio)
|
||||
) {
|
||||
nextModel.completionPrice = formatNumber(
|
||||
Number(nextModel.inputPrice) * Number(nextModel.lockedCompletionRatio),
|
||||
Number(nextModel.inputPrice) *
|
||||
Number(nextModel.lockedCompletionRatio),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user