Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
182 commits
Select commit Hold shift + click to select a range
69de99e
支持即梦2.0的全能提示词
xuanyustudio Apr 19, 2026
0c194c6
1
xuanyustudio Apr 22, 2026
805d5fd
轮询使用原始data
xuanyustudio Apr 22, 2026
6253847
2
xuanyustudio Apr 22, 2026
f61cc74
xai
xuanyustudio Apr 24, 2026
a523a60
修改bug
xuanyustudio Apr 24, 2026
cc0126d
11
xuanyustudio Apr 27, 2026
f311fde
增加链接
xuanyustudio Apr 30, 2026
60ea930
gemini tts
xuanyustudio May 6, 2026
e5d54b3
增加token日志
xuanyustudio May 13, 2026
388782b
Merge remote-tracking branch 'upstream/main'
xuanyustudio May 15, 2026
5074ba8
bun的配置
xuanyustudio May 15, 2026
b79e385
金额问题
xuanyustudio May 15, 2026
a897e44
音频适配
xuanyustudio May 15, 2026
4812ced
增加两个渠道,和按秒计费
xuanyustudio May 20, 2026
2b18ce3
首页统计bug
xuanyustudio May 21, 2026
48c1b36
火山协议自定义
xuanyustudio May 23, 2026
621bb49
草稿字段
xuanyustudio May 25, 2026
592d8fb
豆包特殊中转站
xuanyustudio May 26, 2026
eafa416
1
xuanyustudio May 26, 2026
95c8c32
修改视频异步退款和改余额冲突的bug
xuanyustudio May 28, 2026
beca8bf
手工改令牌额度做记录
xuanyustudio May 28, 2026
58aecba
针对seedance2的视频输入这个特殊功能做了价格折扣的选项
xuanyustudio Jun 6, 2026
e3afadf
增加agnes模型的支持
xuanyustudio Jun 15, 2026
e00b48b
agnes
xuanyustudio Jun 16, 2026
a4a2eb8
预览视频
xuanyustudio Jun 21, 2026
fca17ff
前端修改
xuanyustudio Jun 21, 2026
9acbc0b
增加模型vyroseedance
xuanyustudio Jun 25, 2026
10a8086
agens的bug
xuanyustudio Jun 28, 2026
2c4420f
vyro 的新模型支持
xuanyustudio Jul 1, 2026
4103302
增强
xuanyustudio Jul 2, 2026
0878d57
image images
xuanyustudio Jul 2, 2026
9247b31
增加83zi厂商
xuanyustudio Jul 2, 2026
55198b7
轮询误判的错误
xuanyustudio Jul 2, 2026
b24da75
改名
xuanyustudio Jul 2, 2026
b43f4b0
接入7tai渠道
xuanyustudio Jul 3, 2026
fb47700
模型限制的查询权限
xuanyustudio Jul 8, 2026
7e39560
不同分辨率不同价格
xuanyustudio Jul 10, 2026
9ec5829
前端
xuanyustudio Jul 10, 2026
48eb9a2
做了测试用例和文档。
xuanyustudio Jul 10, 2026
cfa1622
docs: add XunhuPay (虎皮椒) top-up design spec
xuanyustudio Jul 10, 2026
c20deab
增加支付
xuanyustudio Jul 10, 2026
f66f143
修bug
xuanyustudio Jul 10, 2026
001d35d
bug
xuanyustudio Jul 10, 2026
a348bd0
修bug
xuanyustudio Jul 11, 2026
e3091ac
修改bug
xuanyustudio Jul 11, 2026
7e0c3a3
修改bug
xuanyustudio Jul 11, 2026
e3fd48e
计费bug修改
xuanyustudio Jul 12, 2026
f84a97c
格式转换
xuanyustudio Jul 12, 2026
b1a20dd
480-》720
xuanyustudio Jul 12, 2026
3a4f3f6
强化图片的bug
xuanyustudio Jul 12, 2026
1c3bf07
修改了83zi厂商的格式。
xuanyustudio Jul 13, 2026
52ddf2f
文档修改
xuanyustudio Jul 14, 2026
3ef86ef
热更
xuanyustudio Jul 14, 2026
1c55941
multipart 覆盖模型名变了
xuanyustudio Jul 15, 2026
6867ad1
docs: add Seedance asset management API design
xuanyustudio Jul 15, 2026
074cde8
docs: add Seedance asset APIs implementation plan
xuanyustudio Jul 15, 2026
767aad1
认证
xuanyustudio Jul 15, 2026
dad56d1
增加文件
xuanyustudio Jul 15, 2026
5cd8055
增加渠道管理
xuanyustudio Jul 17, 2026
978ce72
调试信息修改
xuanyustudio Jul 17, 2026
bdabcc8
bug
xuanyustudio Jul 17, 2026
a01c86e
模型
xuanyustudio Jul 18, 2026
19150df
过人脸
xuanyustudio Jul 18, 2026
d350e21
网址
xuanyustudio Jul 18, 2026
1d1f5e9
模型调试
xuanyustudio Jul 20, 2026
88e54e3
增加渠道
xuanyustudio Jul 20, 2026
6fe74a9
docs: add MegaByAI video channel design spec
xuanyustudio Jul 20, 2026
429c2ff
docs: add MegaByAI video channel implementation plan
xuanyustudio Jul 20, 2026
46ba45d
test: megabyai create-body field mapping
xuanyustudio Jul 20, 2026
ba5bdaa
feat: register megabyai channel type 65
xuanyustudio Jul 20, 2026
4eb0e54
feat: megabyai Sora-based video task adaptor
xuanyustudio Jul 20, 2026
f39fff7
feat(web): add megabyai channel type in default UI
xuanyustudio Jul 20, 2026
12b55f1
feat(web): add megabyai channel type in classic UI
xuanyustudio Jul 20, 2026
cfd7532
test
xuanyustudio Jul 20, 2026
724c5ac
改bug
xuanyustudio Jul 20, 2026
bd758ff
docs: add MegaByAI channel face-pass design
xuanyustudio Jul 21, 2026
db1c1e3
docs: megabyai face-pass local 1600 WebP preprocess
xuanyustudio Jul 21, 2026
1f07b30
feat: megabyai channel face-pass with local 1600 WebP preprocess
xuanyustudio Jul 21, 2026
6412471
修改
xuanyustudio Jul 21, 2026
04fad77
1
xuanyustudio Jul 21, 2026
e47a841
fix: megabyai face-pass use both-eyes full-face mask for upstream
xuanyustudio Jul 21, 2026
43b2ce9
0
xuanyustudio Jul 21, 2026
38733bf
11
xuanyustudio Jul 21, 2026
8f2d8f5
1
xuanyustudio Jul 21, 2026
9ba7237
docs: seedance-debug UX polish design (assets, help, auth probe)
xuanyustudio Jul 21, 2026
4552cdf
22
xuanyustudio Jul 22, 2026
68ae643
docs: OpenAI/Sora channel face-pass design
xuanyustudio Jul 22, 2026
4bcd77f
改人脸
xuanyustudio Jul 22, 2026
92b5e90
修复
xuanyustudio Jul 23, 2026
18f5844
docs: Sora2U async video channel design
xuanyustudio Jul 24, 2026
7c1819c
docs: Sora2U channel implementation plan
xuanyustudio Jul 24, 2026
f4c1c98
docs: point Sora2U design at versioned plan path
xuanyustudio Jul 24, 2026
2186986
feat: sora2u normalize and task parse helpers
xuanyustudio Jul 24, 2026
5d1fb15
feat: register sora2u channel type 66 and task adaptor
xuanyustudio Jul 24, 2026
cbe44d3
feat(web): add Sora2U channel type in default and classic UI
xuanyustudio Jul 24, 2026
15f031f
优化首页
xuanyustudio Jul 28, 2026
c5e4589
音频支持
xuanyustudio Jul 29, 2026
bdcb70d
22
xuanyustudio Jul 29, 2026
314c34e
22
xuanyustudio Aug 2, 2026
fe53bff
111
xuanyustudio Aug 2, 2026
8648045
333
xuanyustudio Aug 2, 2026
3446112
11
xuanyustudio Aug 2, 2026
53b7e47
预扣价格调整。
xuanyustudio Aug 2, 2026
f32ddb6
修复suceess没url的bug
xuanyustudio Aug 4, 2026
12cb559
minimax h3
xuanyustudio Aug 4, 2026
4b647ba
调试页
xuanyustudio Aug 4, 2026
ede4511
xuanyustudio Aug 5, 2026
05e4afb
Add design for admin failed-request error logs.
xuanyustudio Aug 7, 2026
6c105d1
Add implementation plan for admin error logs.
xuanyustudio Aug 7, 2026
b2f0f7c
feat(error-log): default-on ErrorLogEnabled and error_category constants
xuanyustudio Aug 7, 2026
7e644b6
feat(error-log): add ErrorLogEnabled system setting toggle
xuanyustudio Aug 7, 2026
bf960d1
feat(error-log): add RecordRequestErrorLog helper and wire upstream path
xuanyustudio Aug 7, 2026
aeceeca
feat(error-log): record auth, rate_limit, and channel middleware fail…
xuanyustudio Aug 7, 2026
d54d888
feat(error-log): cover relay early exits, quota, and task/MJ failures
xuanyustudio Aug 7, 2026
13f0a29
feat(error-log): admin GET /api/error-log and hide errors from user logs
xuanyustudio Aug 7, 2026
200433e
feat(error-log): admin Error Logs page with category filters
xuanyustudio Aug 7, 2026
9196b04
chore(i18n): translations for admin error logs
xuanyustudio Aug 7, 2026
31a594a
feat(error-log): add Classic admin Error Logs page and settings
xuanyustudio Aug 7, 2026
961c890
fix(error-log): include errorLog in classic personal sidebar modules
xuanyustudio Aug 7, 2026
b6e8dd3
滚滚滚
xuanyustudio Aug 7, 2026
01d3dbb
docs: add Seedance official asset direct-connect design
xuanyustudio Aug 8, 2026
86f1364
方法
xuanyustudio Aug 8, 2026
9c6af40
22
xuanyustudio Aug 8, 2026
bc5328e
方法
xuanyustudio Aug 8, 2026
a9a4001
33
xuanyustudio Aug 8, 2026
e21db73
方法
xuanyustudio Aug 8, 2026
426cf12
3333
xuanyustudio Aug 8, 2026
987ae0b
44
xuanyustudio Aug 8, 2026
d58194e
333
xuanyustudio Aug 8, 2026
4c56614
4444
xuanyustudio Aug 8, 2026
3332da5
333
xuanyustudio Aug 10, 2026
25f28f6
极简
xuanyustudio Aug 10, 2026
24cc7e3
发发发
xuanyustudio Aug 10, 2026
6b684b0
docs: add mao Seedance adaptive resolution channel design
xuanyustudio Aug 11, 2026
daf4f3d
docs: add mao Seedance adaptive channel implementation plan
xuanyustudio Aug 11, 2026
d778cf8
test: mao resolution tier and upstream model mapping
xuanyustudio Aug 11, 2026
7a95ed8
fix: mao normalizeTier prefer resolution and model error list
xuanyustudio Aug 11, 2026
d2b5601
test: mao create and poll response parsing
xuanyustudio Aug 11, 2026
39fe5fe
feat: mao upstream payload builder with adaptive model
xuanyustudio Aug 11, 2026
de67495
feat: register mao channel type 68 and task adaptor
xuanyustudio Aug 11, 2026
9275a12
feat: add mao channel UI options in default and classic
xuanyustudio Aug 11, 2026
716435b
i18n: add classic zh mao_key_prompt for channel 68
xuanyustudio Aug 11, 2026
3773636
docs: add mao async failure same-channel retry design
xuanyustudio Aug 11, 2026
75b1bd7
docs: add mao async same-channel retry implementation plan
xuanyustudio Aug 11, 2026
a935a3c
feat: mao async retry helpers and TaskPrivateData fields
xuanyustudio Aug 11, 2026
b8cd039
feat: persist mao upstream request body for async retry
xuanyustudio Aug 11, 2026
26b2bce
feat: mao TryResubmitOnFailure same-channel resubmit
xuanyustudio Aug 11, 2026
2feea0d
feat: short-circuit video poll refund when async resubmit succeeds
xuanyustudio Aug 11, 2026
fbba473
33
xuanyustudio Aug 11, 2026
df8f071
fix: mao async retry stop after 3 failures (no 4th attempt)
xuanyustudio Aug 11, 2026
bdc13dc
docs: add mao Volc official content normalize design
xuanyustudio Aug 11, 2026
cedca9a
docs: add mao volc official content normalize plan
xuanyustudio Aug 11, 2026
abf3001
feat: mao volc official content normalize helpers
xuanyustudio Aug 11, 2026
92f27c3
feat: wire mao volc normalize into BuildRequestBody
xuanyustudio Aug 11, 2026
097fc1e
fix: mao merge top-level camera_fixed into metadata
xuanyustudio Aug 11, 2026
25a3274
333
xuanyustudio Aug 11, 2026
4236621
发发发
xuanyustudio Aug 11, 2026
16891ed
docs: add async task cross-channel failover design
xuanyustudio Aug 12, 2026
82f898a
docs: add async task cross-channel failover implementation plan
xuanyustudio Aug 12, 2026
58f3601
feat: classify async task fail reasons for failover
xuanyustudio Aug 12, 2026
782af88
feat: add task failover system options
xuanyustudio Aug 12, 2026
35c0d0c
feat: resolve ordered channel candidates for task failover
xuanyustudio Aug 12, 2026
4139735
feat: snapshot failover state and use ordered channels on task submit
xuanyustudio Aug 12, 2026
e59bcc9
feat: orchestrate async failure same-channel retry via failover service
xuanyustudio Aug 12, 2026
35846d8
feat: recreate failed async tasks on next failover channel
xuanyustudio Aug 12, 2026
cbcb313
refactor: align mao async retry with shared failover classification
xuanyustudio Aug 12, 2026
2bd0e3a
feat(web): task failover toggles and model channel order settings
xuanyustudio Aug 12, 2026
a659f51
feat(classic): task failover settings and drag-order channel list
xuanyustudio Aug 12, 2026
f195a8e
天天天天
xuanyustudio Aug 12, 2026
423d054
docs: add yk-video (KYY model-center) channel design
xuanyustudio Aug 12, 2026
6855dbd
方法
xuanyustudio Aug 12, 2026
45ac3e6
天天
xuanyustudio Aug 12, 2026
5ee4f1b
00
xuanyustudio Aug 13, 2026
f021fa8
xuanyustudio Aug 13, 2026
fcad02e
xuanyustudio Aug 13, 2026
ce25b76
docs: add yk-sd channel design with forced Seedance assets
xuanyustudio Aug 13, 2026
4af3454
增加渠道
xuanyustudio Aug 13, 2026
dbb839a
xuanyustudio Aug 13, 2026
30f02e9
方法
xuanyustudio Aug 13, 2026
6e92f21
对的
xuanyustudio Aug 13, 2026
a03b305
33
xuanyustudio Aug 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,9 @@ data/
.test
token_estimator_test.go
skills-lock.json
...
- 副本.env

.superpowers/
finance-reconciliation.html
cmd/test_seedance_official_auth/main.go
179 changes: 179 additions & 0 deletions 0cmd/test_seedance_official_auth/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// 本地直连测试 BytePlus/火山官方素材 CreateAssetGroup 鉴权。
//
// 用法(PowerShell):
// $env:SEEDANCE_AK="你的AccessKeyID"
// $env:SEEDANCE_SK="你的SecretAccessKey"
// $env:SEEDANCE_PLATFORM="overseas" # 或 cn
// $env:SEEDANCE_PROXY="http://user:pass@host:port" # 可选
// go run ./cmd/test_seedance_official_auth
//
// 请勿把 AK/SK 写进代码或发到聊天里。
package main

import (
"bytes"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"time"

"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/pkg/volc/sign"
"golang.org/x/net/proxy"
)

func main() {
ak := strings.TrimSpace(os.Getenv("SEEDANCE_AK"))
sk := strings.TrimSpace(os.Getenv("SEEDANCE_SK"))
platform := strings.ToLower(strings.TrimSpace(os.Getenv("SEEDANCE_PLATFORM")))
proxyURL := strings.TrimSpace(os.Getenv("SEEDANCE_PROXY"))
if ak == "" || sk == "" {
fmt.Println("请设置环境变量 SEEDANCE_AK / SEEDANCE_SK 后重试(不要把密钥写进代码)")
os.Exit(2)
}

host := "ark.cn-beijing.volcengineapi.com"
region := "cn-beijing"
if platform == "overseas" || platform == "byteplus" {
host = "ark.ap-southeast-1.byteplusapi.com"
region = "ap-southeast-1"
}
if r := strings.TrimSpace(os.Getenv("SEEDANCE_REGION")); r != "" {
region = r
}

project := strings.TrimSpace(os.Getenv("SEEDANCE_PROJECT"))
if project == "" {
project = "default"
}
action := strings.TrimSpace(os.Getenv("SEEDANCE_ACTION"))
if action == "" {
action = "CreateAssetGroup"
}
var bodyObj map[string]any
switch action {
case "GetAsset":
assetID := strings.TrimSpace(os.Getenv("SEEDANCE_ASSET_ID"))
if assetID == "" {
fmt.Println("GetAsset 需要环境变量 SEEDANCE_ASSET_ID")
os.Exit(2)
}
bodyObj = map[string]any{
"Id": assetID,
"ProjectName": project,
}
default:
bodyObj = map[string]any{
"Name": "debug-aigc",
"GroupType": "AIGC",
"ProjectName": project,
}
action = "CreateAssetGroup"
}
body, err := common.Marshal(bodyObj)
if err != nil {
fatal(err)
}
q := url.Values{}
q.Set("Action", action)
q.Set("Version", "2024-01-01")
fullURL := fmt.Sprintf("https://%s/?%s", host, q.Encode())

req, err := http.NewRequest(http.MethodPost, fullURL, bytes.NewReader(body))
if err != nil {
fatal(err)
}
req.Header.Set("Content-Type", "application/json")
req.Host = host

if err = sign.SignRequest(req, sign.Credentials{
AccessKeyID: ak,
SecretAccessKey: sk,
Region: region,
Service: "ark",
}, body, time.Now().UTC()); err != nil {
fatal(err)
}

client, err := buildClient(proxyURL)
if err != nil {
fatal(err)
}

fmt.Printf("POST %s\n", fullURL)
fmt.Printf("platform=%s region=%s host=%s proxy=%v ak_prefix=%s...\n",
platform, region, host, proxyURL != "", trimPrefix(ak, 8))

resp, err := client.Do(req)
if err != nil {
fatal(err)
}
defer resp.Body.Close()
raw, _ := io.ReadAll(resp.Body)
fmt.Printf("HTTP %d\n%s\n", resp.StatusCode, string(raw))

var parsed map[string]any
_ = common.Unmarshal(raw, &parsed)
if meta, ok := parsed["ResponseMetadata"].(map[string]any); ok {
if errObj, ok := meta["Error"].(map[string]any); ok {
fmt.Printf("\n解析错误: Code=%v Message=%v\n", errObj["Code"], errObj["Message"])
os.Exit(1)
}
}
if resp.StatusCode >= 400 {
os.Exit(1)
}
fmt.Println("\n鉴权看起来通过了(未返回 Error)")
}

func buildClient(proxyURL string) (*http.Client, error) {
if proxyURL == "" {
return &http.Client{Timeout: 30 * time.Second}, nil
}
u, err := url.Parse(proxyURL)
if err != nil {
return nil, err
}
switch strings.ToLower(u.Scheme) {
case "http", "https":
return &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{
Proxy: http.ProxyURL(u),
},
}, nil
case "socks5", "socks5h":
var auth *proxy.Auth
if u.User != nil {
pass, _ := u.User.Password()
auth = &proxy.Auth{User: u.User.Username(), Password: pass}
}
dialer, err := proxy.SOCKS5("tcp", u.Host, auth, proxy.Direct)
if err != nil {
return nil, err
}
return &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{
Dial: dialer.Dial,
},
}, nil
default:
return nil, fmt.Errorf("unsupported proxy scheme: %s", u.Scheme)
}
}

func trimPrefix(s string, n int) string {
if len(s) <= n {
return s
}
return s[:n]
}

func fatal(err error) {
fmt.Println("ERROR:", err)
os.Exit(1)
}
2 changes: 1 addition & 1 deletion common/endpoint_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func GetEndpointTypesByChannelType(channelType int, modelName string) []constant
endpointTypes = []constant.EndpointType{constant.EndpointTypeOpenAI}
case constant.ChannelTypeXai:
endpointTypes = []constant.EndpointType{constant.EndpointTypeOpenAI, constant.EndpointTypeOpenAIResponse}
case constant.ChannelTypeSora:
case constant.ChannelTypeSora, constant.ChannelTypeMegabyai, constant.ChannelTypeSora2U, constant.ChannelTypeZiZiDongHua:
endpointTypes = []constant.EndpointType{constant.EndpointTypeOpenAIVideo}
default:
if IsOpenAIResponseOnlyModel(modelName) {
Expand Down
26 changes: 26 additions & 0 deletions common/gin.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,32 @@ func parseMultipartFormData(c *gin.Context, data []byte, v any) error {
}
}

// Support sending text fields (especially long "prompt") as file parts using curl -F "prompt=@file.txt"
// This is very common for video generation with large prompts.
// We read the file content as the string value for these fields.
textFieldsAsFiles := map[string]bool{
"prompt": true, "model": true, "mode": true, "size": true,
"seconds": true, "duration": true, "aspect_ratio": true,
"resolution": true, "image": true,
}
for key, fhs := range form.File {
if !textFieldsAsFiles[key] || len(fhs) == 0 {
continue
}
if existing, ok := formMap[key]; ok {
if s, _ := existing.(string); strings.TrimSpace(s) != "" {
continue // already have a text value
}
}
fh := fhs[0]
if f, err := fh.Open(); err == nil {
if content, err := io.ReadAll(f); err == nil && len(content) > 0 {
formMap[key] = string(content)
}
f.Close()
}
}

return processFormMap(formMap, v)
}

Expand Down
2 changes: 1 addition & 1 deletion common/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func initConstantEnv() {
// GenerateDefaultToken 是否生成初始令牌,默认关闭。
constant.GenerateDefaultToken = GetEnvOrDefaultBool("GENERATE_DEFAULT_TOKEN", false)
// 是否启用错误日志
constant.ErrorLogEnabled = GetEnvOrDefaultBool("ERROR_LOG_ENABLED", false)
constant.ErrorLogEnabled = GetEnvOrDefaultBool("ERROR_LOG_ENABLED", true)
// 任务轮询时查询的最大数量
constant.TaskQueryLimit = GetEnvOrDefault("TASK_QUERY_LIMIT", 1000)
// 异步任务超时时间(分钟),超过此时间未完成的任务将被标记为失败并退款。0 表示禁用。
Expand Down
1 change: 1 addition & 0 deletions common/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ var (
"prefix:imagen-",
"flux-",
"flux.1-",
"agnes-image",
}
OpenAITextModels = []string{
"gpt-",
Expand Down
52 changes: 52 additions & 0 deletions constant/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ const (
ChannelTypeSora = 55
ChannelTypeReplicate = 56
ChannelTypeCodex = 57
ChannelTypeApiWenhao = 58
ChannelTypeApimart = 59
ChannelTypeAgnes = 60
ChannelTypeVyroSeedance = 61
ChannelType83zi = 62
ChannelType7tai = 63
ChannelTypeTh12345ai = 64
ChannelTypeMegabyai = 65
ChannelTypeSora2U = 66
ChannelTypeZiZiDongHua = 67
ChannelTypeMao = 68
ChannelTypeYkVideo = 69
ChannelTypeYkSd = 70
ChannelTypeDummy // this one is only for count, do not add any channel after this

)
Expand Down Expand Up @@ -118,6 +131,19 @@ var ChannelBaseURLs = []string{
"https://api.openai.com", //55
"https://api.replicate.com", //56
"https://chatgpt.com", //57
"https://saast.fuwenhao.com", //58 ApiWenhao
"https://api.apimart.ai", //59 ApiMart (CN: https://api.apib.ai)
"https://apihub.agnes-ai.com", //60 Agnes
"https://996k.cn/v1", //61 VyroSeedance (996k vyro-seedance-2)
"https://sd2.83zi.com", //62 83zi SD2 video
"https://api.7tai.cc/v1", //63 7tai (炳火) video
"https://sd.12345ai.net", //64 th12345ai (TH API) video
"https://newapi.megabyai.cc", //65 megabyai video
"https://sora2u.com", //66 sora2u Seedance video
"https://www.zizidonghua.com", //67 zz (ZiZiDongHua) MiniMax-H3 video
"https://api.catertx.com", //68 mao (catertx Seedance)
"https://zcbservice.aizfw.cn/kyyReactApiServer", //69 yk-video (KYY model-center)
"https://zcbservice.aizfw.cn/kyyReactApiServer", //70 yk-sd (KYY Seedance special/discount)
}

var ChannelTypeNames = map[int]string{
Expand Down Expand Up @@ -175,6 +201,19 @@ var ChannelTypeNames = map[int]string{
ChannelTypeSora: "Sora",
ChannelTypeReplicate: "Replicate",
ChannelTypeCodex: "Codex",
ChannelTypeApiWenhao: "ApiWenhao",
ChannelTypeApimart: "ApiMart",
ChannelTypeAgnes: "Agnes",
ChannelTypeVyroSeedance: "VyroSeedance",
ChannelType83zi: "83zi",
ChannelType7tai: "7tai",
ChannelTypeTh12345ai: "th12345ai",
ChannelTypeMegabyai: "megabyai",
ChannelTypeSora2U: "sora2u",
ChannelTypeZiZiDongHua: "zz",
ChannelTypeMao: "mao",
ChannelTypeYkVideo: "yk-video",
ChannelTypeYkSd: "yk-sd",
}

func GetChannelTypeName(channelType int) string {
Expand All @@ -184,11 +223,24 @@ func GetChannelTypeName(channelType int) string {
return "Unknown"
}

// GetChannelDefaultBaseURL returns the built-in default base URL for a channel type.
// It is safe when channelType is out of range (returns "").
func GetChannelDefaultBaseURL(channelType int) string {
if channelType < 0 || channelType >= len(ChannelBaseURLs) {
return ""
}
return ChannelBaseURLs[channelType]
}

type ChannelSpecialBase struct {
ClaudeBaseURL string
OpenAIBaseURL string
}

// VolcEngineOpenSpeechTTSV3BaseURL 为火山豆包 OpenSpeech「语音合成 HTTP V3(单向 NDJSON)」渠道占位 Base URL。
// 在「字节火山」类渠道中将 API 地址填为此值(或填完整 unidirectional URL)时,/v1/audio/speech 将走 V3 协议(X-Api-App-Id / Access-Key / Resource-Id),密钥格式仍为 appid|access_token。
const VolcEngineOpenSpeechTTSV3BaseURL = "openspeech-tts-v3"

var ChannelSpecialBases = map[string]ChannelSpecialBase{
"glm-coding-plan": {
ClaudeBaseURL: "https://open.bigmodel.cn/api/anthropic",
Expand Down
36 changes: 36 additions & 0 deletions constant/channel_base_url_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package constant

import "testing"

func TestGetChannelDefaultBaseURL(t *testing.T) {
if got := GetChannelDefaultBaseURL(ChannelTypeAgnes); got != "https://apihub.agnes-ai.com" {
t.Fatalf("Agnes default base URL = %q", got)
}
if got := GetChannelDefaultBaseURL(ChannelTypeTh12345ai); got != "https://sd.12345ai.net" {
t.Fatalf("th12345ai default base URL = %q", got)
}
if got := GetChannelDefaultBaseURL(ChannelTypeMegabyai); got != "https://newapi.megabyai.cc" {
t.Fatalf("megabyai default base URL = %q", got)
}
if got := GetChannelDefaultBaseURL(ChannelTypeSora2U); got != "https://sora2u.com" {
t.Fatalf("sora2u default base URL = %q", got)
}
if got := GetChannelDefaultBaseURL(ChannelTypeZiZiDongHua); got != "https://www.zizidonghua.com" {
t.Fatalf("zz default base URL = %q", got)
}
if got := GetChannelDefaultBaseURL(ChannelTypeMao); got != "https://api.catertx.com" {
t.Fatalf("mao base url = %q", got)
}
if got := GetChannelDefaultBaseURL(ChannelTypeYkVideo); got != "https://zcbservice.aizfw.cn/kyyReactApiServer" {
t.Fatalf("yk-video base url = %q", got)
}
if got := GetChannelDefaultBaseURL(ChannelTypeYkSd); got != "https://zcbservice.aizfw.cn/kyyReactApiServer" {
t.Fatalf("yk-sd base url = %q", got)
}
if got := GetChannelDefaultBaseURL(len(ChannelBaseURLs)); got != "" {
t.Fatalf("out of range should return empty, got %q", got)
}
if got := GetChannelDefaultBaseURL(-1); got != "" {
t.Fatalf("negative type should return empty, got %q", got)
}
}
11 changes: 11 additions & 0 deletions constant/error_log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package constant

const (
ErrorCategoryAuth = "auth"
ErrorCategoryRateLimit = "rate_limit"
ErrorCategoryChannel = "channel"
ErrorCategoryValidation = "validation"
ErrorCategoryQuota = "quota"
ErrorCategoryUpstream = "upstream"
ErrorCategoryOther = "other"
)
2 changes: 1 addition & 1 deletion controller/channel-billing.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ func updateChannelMoonshotBalance(channel *model.Channel) (float64, error) {
}

func updateChannelBalance(channel *model.Channel) (float64, error) {
baseURL := constant.ChannelBaseURLs[channel.Type]
baseURL := constant.GetChannelDefaultBaseURL(channel.Type)
if channel.GetBaseURL() == "" {
channel.BaseURL = &baseURL
}
Expand Down
Loading