Compare commits

...

6 Commits

Author SHA1 Message Date
CaIon 264bc963e0 feat: normalize number handling in model pricing editor #3246 2026-03-14 15:29:47 +08:00
CaIon da8a52f50a feat: add conditional setting for HTTP headers in OpenRouter channel type 2026-03-12 19:05:30 +08:00
CaIon 9fdb0bc248 feat: comment out notify endpoint in relay router 2026-03-12 19:05:30 +08:00
Seefs 24ec27f844 Merge pull request #3221 from RedwindA/chore/updateModelList
chore: update model lists for frequently used channels
2026-03-12 15:13:03 +08:00
CaIon 5e9cc681f5 feat: update header title for OpenRouter channel type 2026-03-12 15:05:58 +08:00
RedwindA 7e68e1b36a chore: update model lists for frequently used channels 2026-03-11 23:39:18 +08:00
10 changed files with 149 additions and 64 deletions
+1
View File
@@ -25,6 +25,7 @@ var ModelList = []string{
"claude-opus-4-6-high",
"claude-opus-4-6-medium",
"claude-opus-4-6-low",
"claude-sonnet-4-6",
}
var ChannelName = "claude"
+2 -1
View File
@@ -8,7 +8,8 @@ import (
var baseModelList = []string{
"gpt-5", "gpt-5-codex", "gpt-5-codex-mini",
"gpt-5.1", "gpt-5.1-codex", "gpt-5.1-codex-max", "gpt-5.1-codex-mini",
"gpt-5.2", "gpt-5.2-codex", "gpt-5.3-codex",
"gpt-5.2", "gpt-5.2-codex", "gpt-5.3-codex", "gpt-5.3-codex-spark",
"gpt-5.4",
}
var ModelList = withCompactModelSuffix(baseModelList)
+25 -20
View File
@@ -2,29 +2,34 @@ package gemini
var ModelList = []string{
// stable version
"gemini-1.5-pro", "gemini-1.5-flash", "gemini-1.5-flash-8b",
"gemini-2.0-flash",
"gemini-2.5-flash", "gemini-2.5-pro", "gemini-2.0-flash",
"gemini-2.0-flash-001", "gemini-2.0-flash-lite-001", "gemini-2.0-flash-lite",
"gemini-2.5-flash-lite",
// latest version
"gemini-1.5-pro-latest", "gemini-1.5-flash-latest",
"gemini-flash-latest", "gemini-flash-lite-latest", "gemini-pro-latest",
"gemini-2.5-flash-native-audio-latest",
// preview version
"gemini-2.0-flash-lite-preview",
"gemini-3-pro-preview",
// gemini exp
"gemini-exp-1206",
// flash exp
"gemini-2.0-flash-exp",
// pro exp
"gemini-2.0-pro-exp",
// thinking exp
"gemini-2.0-flash-thinking-exp",
"gemini-2.5-pro-exp-03-25",
"gemini-2.5-pro-preview-03-25",
// imagen models
"imagen-3.0-generate-002",
"gemini-2.5-flash-preview-tts", "gemini-2.5-pro-preview-tts",
"gemini-2.5-flash-image", "gemini-2.5-flash-lite-preview-09-2025",
"gemini-3-pro-preview", "gemini-3-flash-preview", "gemini-3.1-pro-preview",
"gemini-3.1-pro-preview-customtools", "gemini-3.1-flash-lite-preview",
"gemini-3-pro-image-preview", "nano-banana-pro-preview",
"gemini-3.1-flash-image-preview", "gemini-robotics-er-1.5-preview",
"gemini-2.5-computer-use-preview-10-2025", "deep-research-pro-preview-12-2025",
"gemini-2.5-flash-native-audio-preview-09-2025", "gemini-2.5-flash-native-audio-preview-12-2025",
// gemma models
"gemma-3-1b-it", "gemma-3-4b-it", "gemma-3-12b-it",
"gemma-3-27b-it", "gemma-3n-e4b-it", "gemma-3n-e2b-it",
// embedding models
"gemini-embedding-exp-03-07",
"text-embedding-004",
"embedding-001",
"gemini-embedding-001", "gemini-embedding-2-preview",
// imagen models
"imagen-4.0-generate-001", "imagen-4.0-ultra-generate-001",
"imagen-4.0-fast-generate-001",
// veo models
"veo-2.0-generate-001", "veo-3.0-generate-001", "veo-3.0-fast-generate-001",
"veo-3.1-generate-preview", "veo-3.1-fast-generate-preview",
// other models
"aqa",
}
var SafetySettingList = []string{
+3 -1
View File
@@ -15,8 +15,10 @@ var ModelList = []string{
"speech-01-hd",
"speech-01-turbo",
"MiniMax-M2.1",
"MiniMax-M2.1-lightning",
"MiniMax-M2.1-highspeed",
"MiniMax-M2",
"MiniMax-M2.5",
"MiniMax-M2.5-highspeed",
}
var ChannelName = "minimax"
+5 -3
View File
@@ -1,9 +1,11 @@
package moonshot
var ModelList = []string{
"moonshot-v1-8k",
"moonshot-v1-32k",
"moonshot-v1-128k",
"kimi-k2.5",
"kimi-k2-0905-preview",
"kimi-k2-turbo-preview",
"kimi-k2-thinking",
"kimi-k2-thinking-turbo",
}
var ChannelName = "moonshot"
+6 -2
View File
@@ -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-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
}
+33 -4
View File
@@ -3,14 +3,19 @@ package openai
var ModelList = []string{
"gpt-3.5-turbo", "gpt-3.5-turbo-0613", "gpt-3.5-turbo-1106", "gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k", "gpt-3.5-turbo-16k-0613",
"gpt-3.5-turbo-instruct",
"gpt-3.5-turbo-instruct", "gpt-3.5-turbo-instruct-0914",
"gpt-4", "gpt-4-0613", "gpt-4-1106-preview", "gpt-4-0125-preview",
"gpt-4-32k", "gpt-4-32k-0613",
"gpt-4-turbo-preview", "gpt-4-turbo", "gpt-4-turbo-2024-04-09",
"gpt-4-vision-preview",
"chatgpt-4o-latest",
"gpt-4o", "gpt-4o-2024-05-13", "gpt-4o-2024-08-06", "gpt-4o-2024-11-20",
"gpt-4o-transcribe", "gpt-4o-transcribe-diarize",
"gpt-4o-search-preview", "gpt-4o-search-preview-2025-03-11",
"gpt-4o-mini", "gpt-4o-mini-2024-07-18",
"gpt-4o-mini-transcribe", "gpt-4o-mini-transcribe-2025-03-20", "gpt-4o-mini-transcribe-2025-12-15",
"gpt-4o-mini-tts", "gpt-4o-mini-tts-2025-03-20", "gpt-4o-mini-tts-2025-12-15",
"gpt-4o-mini-search-preview", "gpt-4o-mini-search-preview-2025-03-11",
"gpt-4.5-preview", "gpt-4.5-preview-2025-02-27",
"gpt-4.1", "gpt-4.1-2025-04-14",
"gpt-4.1-mini", "gpt-4.1-mini-2025-04-14",
@@ -31,17 +36,41 @@ var ModelList = []string{
"gpt-5", "gpt-5-2025-08-07", "gpt-5-chat-latest",
"gpt-5-mini", "gpt-5-mini-2025-08-07",
"gpt-5-nano", "gpt-5-nano-2025-08-07",
"gpt-4o-audio-preview", "gpt-4o-audio-preview-2024-10-01",
"gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", "gpt-4o-realtime-preview-2024-12-17",
"gpt-5-codex",
"gpt-5-pro", "gpt-5-pro-2025-10-06",
"gpt-5-search-api", "gpt-5-search-api-2025-10-14",
"gpt-5.1", "gpt-5.1-2025-11-13", "gpt-5.1-chat-latest",
"gpt-5.1-codex", "gpt-5.1-codex-mini", "gpt-5.1-codex-max",
"gpt-5.2", "gpt-5.2-2025-12-11", "gpt-5.2-chat-latest",
"gpt-5.2-pro", "gpt-5.2-pro-2025-12-11",
"gpt-5.2-codex",
"gpt-5.3-chat-latest",
"gpt-5.3-codex",
"gpt-5.4", "gpt-5.4-2026-03-05",
"gpt-5.4-pro", "gpt-5.4-pro-2026-03-05",
"gpt-4o-audio-preview", "gpt-4o-audio-preview-2024-10-01", "gpt-4o-audio-preview-2024-12-17", "gpt-4o-audio-preview-2025-06-03",
"gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", "gpt-4o-realtime-preview-2024-12-17", "gpt-4o-realtime-preview-2025-06-03",
"gpt-4o-mini-realtime-preview", "gpt-4o-mini-realtime-preview-2024-12-17",
"gpt-4o-mini-audio-preview", "gpt-4o-mini-audio-preview-2024-12-17",
"gpt-audio", "gpt-audio-2025-08-28",
"gpt-audio-mini", "gpt-audio-mini-2025-10-06", "gpt-audio-mini-2025-12-15",
"gpt-audio-1.5",
"gpt-realtime", "gpt-realtime-2025-08-28",
"gpt-realtime-mini", "gpt-realtime-mini-2025-10-06", "gpt-realtime-mini-2025-12-15",
"gpt-realtime-1.5",
"text-embedding-ada-002", "text-embedding-3-small", "text-embedding-3-large",
"text-curie-001", "text-babbage-001", "text-ada-001",
"text-moderation-latest", "text-moderation-stable",
"omni-moderation-latest", "omni-moderation-2024-09-26",
"text-davinci-edit-001",
"davinci-002", "babbage-002",
"dall-e-3", "gpt-image-1",
"dall-e-2", "dall-e-3",
"gpt-image-1", "gpt-image-1-mini", "gpt-image-1.5",
"chatgpt-image-latest",
"whisper-1",
"tts-1", "tts-1-1106", "tts-1-hd", "tts-1-hd-1106",
"computer-use-preview", "computer-use-preview-2025-03-11",
"sora-2", "sora-2-pro",
}
var ChannelName = "openai"
+1 -1
View File
@@ -1,7 +1,7 @@
package zhipu_4v
var ModelList = []string{
"glm-4", "glm-4v", "glm-3-turbo", "glm-4-alltools", "glm-4-plus", "glm-4-0520", "glm-4-air", "glm-4-airx", "glm-4-long", "glm-4-flash", "glm-4v-plus", "glm-4.6",
"glm-4", "glm-4v", "glm-3-turbo", "glm-4-alltools", "glm-4-plus", "glm-4-0520", "glm-4-air", "glm-4-airx", "glm-4-long", "glm-4-flash", "glm-4v-plus", "glm-4.6", "glm-4.6v", "glm-4.7", "glm-4.7-flash", "glm-5",
}
var ChannelName = "zhipu_4v"
+1 -1
View File
@@ -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)
@@ -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),
);
}