From 9c12e02cb50ad696aab25170c2cace1ad3eb3691 Mon Sep 17 00:00:00 2001 From: skynono <6811626@qq.com> Date: Mon, 19 May 2025 14:28:29 +0800 Subject: [PATCH 1/5] fix: if default model is not exist, set the first one as default --- web/src/pages/Playground/Playground.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/web/src/pages/Playground/Playground.js b/web/src/pages/Playground/Playground.js index e8138c01..08eada17 100644 --- a/web/src/pages/Playground/Playground.js +++ b/web/src/pages/Playground/Playground.js @@ -64,8 +64,9 @@ const Playground = () => { }, ]; + const defaultModel = 'gpt-4o-mini'; const [inputs, setInputs] = useState({ - model: 'gpt-4o-mini', + model: defaultModel, group: '', max_tokens: 0, temperature: 0, @@ -108,6 +109,11 @@ const Playground = () => { value: model, })); setModels(localModelOptions); + // if default model is not in the list, set the first one as default + const hasDefault = localModelOptions.some(option => option.value === defaultModel); + if (!hasDefault && localModelOptions.length > 0) { + setInputs((inputs) => ({ ...inputs, model: localModelOptions[0].value })); + } } else { showError(t(message)); } From e1190f98e9de90f8f7fa090043bc1ae02cc5a7a7 Mon Sep 17 00:00:00 2001 From: skynono <6811626@qq.com> Date: Mon, 19 May 2025 15:42:36 +0800 Subject: [PATCH 2/5] fix: typo in oidc_enabled field (previously oidc) --- web/src/pages/Home/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/pages/Home/index.js b/web/src/pages/Home/index.js index 599c7930..84fabf6f 100644 --- a/web/src/pages/Home/index.js +++ b/web/src/pages/Home/index.js @@ -158,7 +158,7 @@ const Home = () => {

{t('OIDC 身份验证')}: - {statusState?.status?.oidc === true + {statusState?.status?.oidc_enabled === true ? t('已启用') : t('未启用')}

From 2cc2d4f6526ad809e35cb405e8e6597691e3a5e1 Mon Sep 17 00:00:00 2001 From: skynono <6811626@qq.com> Date: Fri, 23 May 2025 13:51:11 +0800 Subject: [PATCH 3/5] fix: keep BatchDelete and TagMode enabled status --- web/src/components/ChannelsTable.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/web/src/components/ChannelsTable.js b/web/src/components/ChannelsTable.js index 9b1dd602..f490e14a 100644 --- a/web/src/components/ChannelsTable.js +++ b/web/src/components/ChannelsTable.js @@ -888,9 +888,13 @@ const ChannelsTable = () => { const localIdSort = localStorage.getItem('id-sort') === 'true'; const localPageSize = parseInt(localStorage.getItem('page-size')) || ITEMS_PER_PAGE; + const localEnableTagMode = localStorage.getItem('enable-tag-mode') === 'true'; + const localEnableBatchDelete = localStorage.getItem('enable-batch-delete') === 'true'; setIdSort(localIdSort); setPageSize(localPageSize); - loadChannels(0, localPageSize, localIdSort, enableTagMode) + setEnableTagMode(localEnableTagMode); + setEnableBatchDelete(localEnableBatchDelete); + loadChannels(0, localPageSize, localIdSort, localEnableTagMode) .then() .catch((reason) => { showError(reason); @@ -1486,10 +1490,12 @@ const ChannelsTable = () => { {t('开启批量操作')} { + localStorage.setItem('enable-batch-delete', v + ''); setEnableBatchDelete(v); }} /> @@ -1553,6 +1559,7 @@ const ChannelsTable = () => { uncheckedText={t('关')} aria-label={t('是否启用标签聚合')} onChange={(v) => { + localStorage.setItem('enable-tag-mode', v + ''); setEnableTagMode(v); loadChannels(0, pageSize, idSort, v); }} From 368fd75c86aa775a52fcfd6270144213b0c02f2c Mon Sep 17 00:00:00 2001 From: skynono <6811626@qq.com> Date: Mon, 26 May 2025 17:11:45 +0800 Subject: [PATCH 4/5] fix: ali parameter.enable_thinking must be set to false for non-streaming calls --- relay/channel/ali/adaptor.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/relay/channel/ali/adaptor.go b/relay/channel/ali/adaptor.go index ab632d22..31e926d6 100644 --- a/relay/channel/ali/adaptor.go +++ b/relay/channel/ali/adaptor.go @@ -57,6 +57,12 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn if request == nil { return nil, errors.New("request is nil") } + + // fix: ali parameter.enable_thinking must be set to false for non-streaming calls + if !info.IsStream { + request.EnableThinking = false + } + switch info.RelayMode { default: aliReq := requestOpenAI2Ali(*request) From 30d5a11f466d0ec95d86028bb6455b51e39f7be4 Mon Sep 17 00:00:00 2001 From: creamlike1024 Date: Mon, 26 May 2025 18:53:41 +0800 Subject: [PATCH 5/5] fix: search-preview model web search billing --- dto/openai_request.go | 6 ++++++ relay/relay-text.go | 43 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/dto/openai_request.go b/dto/openai_request.go index e8833b3d..e491812a 100644 --- a/dto/openai_request.go +++ b/dto/openai_request.go @@ -53,6 +53,7 @@ type GeneralOpenAIRequest struct { Audio any `json:"audio,omitempty"` EnableThinking any `json:"enable_thinking,omitempty"` // ali ExtraBody any `json:"extra_body,omitempty"` + WebSearchOptions *WebSearchOptions `json:"web_search_options,omitempty"` } type ToolCallRequest struct { @@ -371,6 +372,11 @@ func (m *Message) ParseContent() []MediaContent { return contentList } +type WebSearchOptions struct { + SearchContextSize string `json:"search_context_size,omitempty"` + UserLocation json.RawMessage `json:"user_location,omitempty"` +} + type OpenAIResponsesRequest struct { Model string `json:"model"` Input json.RawMessage `json:"input,omitempty"` diff --git a/relay/relay-text.go b/relay/relay-text.go index 8d5cd384..f1105907 100644 --- a/relay/relay-text.go +++ b/relay/relay-text.go @@ -47,6 +47,20 @@ func getAndValidateTextRequest(c *gin.Context, relayInfo *relaycommon.RelayInfo) if textRequest.Model == "" { return nil, errors.New("model is required") } + if textRequest.WebSearchOptions != nil { + if textRequest.WebSearchOptions.SearchContextSize != "" { + validSizes := map[string]bool{ + "high": true, + "medium": true, + "low": true, + } + if !validSizes[textRequest.WebSearchOptions.SearchContextSize] { + return nil, errors.New("invalid search_context_size, must be one of: high, medium, low") + } + } else { + textRequest.WebSearchOptions.SearchContextSize = "medium" + } + } switch relayInfo.RelayMode { case relayconstant.RelayModeCompletions: if textRequest.Prompt == "" { @@ -76,6 +90,10 @@ func TextHelper(c *gin.Context) (openaiErr *dto.OpenAIErrorWithStatusCode) { // get & validate textRequest 获取并验证文本请求 textRequest, err := getAndValidateTextRequest(c, relayInfo) + if textRequest.WebSearchOptions != nil { + c.Set("chat_completion_web_search_context_size", textRequest.WebSearchOptions.SearchContextSize) + } + if err != nil { common.LogError(c, fmt.Sprintf("getAndValidateTextRequest failed: %s", err.Error())) return service.OpenAIErrorWrapperLocal(err, "invalid_text_request", http.StatusBadRequest) @@ -370,9 +388,20 @@ func postConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, dWebSearchQuota = decimal.NewFromFloat(webSearchPrice). Mul(decimal.NewFromInt(int64(webSearchTool.CallCount))). Div(decimal.NewFromInt(1000)).Mul(dGroupRatio).Mul(dQuotaPerUnit) - extraContent += fmt.Sprintf("Web Search 调用 %d 次,上下文大小 %s,调用花费 $%s", + extraContent += fmt.Sprintf("Web Search 调用 %d 次,上下文大小 %s,调用花费 %s", webSearchTool.CallCount, webSearchTool.SearchContextSize, dWebSearchQuota.String()) } + } else if strings.HasSuffix(modelName, "search-preview") { + // search-preview 模型不支持 response api + searchContextSize := ctx.GetString("chat_completion_web_search_context_size") + if searchContextSize == "" { + searchContextSize = "medium" + } + webSearchPrice = operation_setting.GetWebSearchPricePerThousand(modelName, searchContextSize) + dWebSearchQuota = decimal.NewFromFloat(webSearchPrice). + Div(decimal.NewFromInt(1000)).Mul(dGroupRatio).Mul(dQuotaPerUnit) + extraContent += fmt.Sprintf("Web Search 调用 1 次,上下文大小 %s,调用花费 %s", + searchContextSize, dWebSearchQuota.String()) } // file search tool 计费 var dFileSearchQuota decimal.Decimal @@ -463,10 +492,16 @@ func postConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, other["image_ratio"] = imageRatio other["image_output"] = imageTokens } - if !dWebSearchQuota.IsZero() && relayInfo.ResponsesUsageInfo != nil { - if webSearchTool, exists := relayInfo.ResponsesUsageInfo.BuiltInTools[dto.BuildInToolWebSearchPreview]; exists { + if !dWebSearchQuota.IsZero() { + if relayInfo.ResponsesUsageInfo != nil { + if webSearchTool, exists := relayInfo.ResponsesUsageInfo.BuiltInTools[dto.BuildInToolWebSearchPreview]; exists { + other["web_search"] = true + other["web_search_call_count"] = webSearchTool.CallCount + other["web_search_price"] = webSearchPrice + } + } else if strings.HasSuffix(modelName, "search-preview") { other["web_search"] = true - other["web_search_call_count"] = webSearchTool.CallCount + other["web_search_call_count"] = 1 other["web_search_price"] = webSearchPrice } }