From b3d6357bbd47221d6edc48e535b951ee578e2ea7 Mon Sep 17 00:00:00 2001 From: Ryo Date: Thu, 30 Oct 2025 14:05:23 +0800 Subject: [PATCH] feat: model configuration and embedding configuration optimization. (#2414) Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- backend/api/handler/coze/config_service.go | 17 +- backend/api/model/admin/config/config.go | 152 ++++++++++++------ .../conversation/conversation/conversation.go | 2 +- .../conversation/conversation_service.go | 2 +- backend/bizpkg/config/knowledge/knowledge.go | 5 +- backend/bizpkg/llm/modelbuilder/ark.go | 17 +- backend/bizpkg/llm/modelbuilder/claude.go | 13 +- backend/bizpkg/llm/modelbuilder/deepseek.go | 2 +- backend/bizpkg/llm/modelbuilder/gemini.go | 14 +- .../bizpkg/llm/modelbuilder/model_builder.go | 43 ++--- backend/bizpkg/llm/modelbuilder/ollama.go | 13 +- backend/bizpkg/llm/modelbuilder/openai.go | 2 +- backend/bizpkg/llm/modelbuilder/qwen.go | 9 +- backend/conf/admin/index.html | 111 +++++++++++-- backend/infra/embedding/impl/ark/ark.go | 2 +- backend/infra/embedding/impl/http/http.go | 2 +- backend/infra/embedding/impl/impl.go | 4 +- .../infra/embedding/impl/wrap/dense_only.go | 2 +- idl/admin/config.thrift | 9 +- 19 files changed, 309 insertions(+), 112 deletions(-) diff --git a/backend/api/handler/coze/config_service.go b/backend/api/handler/coze/config_service.go index a35eb595c..e78c8ceb9 100644 --- a/backend/api/handler/coze/config_service.go +++ b/backend/api/handler/coze/config_service.go @@ -132,13 +132,28 @@ func UpdateKnowledgeConfig(ctx context.Context, c *app.RequestContext) { return } + if req.KnowledgeConfig.EmbeddingConfig == nil { + invalidParamRequestResponse(c, "EmbeddingConfig is nil") + return + } + + if req.KnowledgeConfig.EmbeddingConfig.Connection == nil { + invalidParamRequestResponse(c, "Connection is nil") + return + } + + if req.KnowledgeConfig.EmbeddingConfig.Connection.EmbeddingInfo == nil { + invalidParamRequestResponse(c, "EmbeddingInfo is nil") + return + } + embedding, err := impl.GetEmbedding(ctx, req.KnowledgeConfig.EmbeddingConfig) if err != nil { invalidParamRequestResponse(c, fmt.Sprintf("get embedding failed: %v", err)) return } - if req.KnowledgeConfig.EmbeddingConfig.Connection.EmbeddingInfo.Dims == 0 { + if req.KnowledgeConfig.EmbeddingConfig.Connection.EmbeddingInfo.Dims <= 0 { req.KnowledgeConfig.EmbeddingConfig.Connection.EmbeddingInfo.Dims = int32(embedding.Dimensions()) embedding, err = impl.GetEmbedding(ctx, req.KnowledgeConfig.EmbeddingConfig) diff --git a/backend/api/model/admin/config/config.go b/backend/api/model/admin/config/config.go index 8356e1eb7..02734288b 100644 --- a/backend/api/model/admin/config/config.go +++ b/backend/api/model/admin/config/config.go @@ -59,6 +59,58 @@ func (p *ModelType) Value() (driver.Value, error) { return int64(*p), nil } +type ThinkingType int64 + +const ( + ThinkingType_Default ThinkingType = 0 + ThinkingType_Enable ThinkingType = 1 + ThinkingType_Disable ThinkingType = 2 + ThinkingType_Auto ThinkingType = 3 +) + +func (p ThinkingType) String() string { + switch p { + case ThinkingType_Default: + return "Default" + case ThinkingType_Enable: + return "Enable" + case ThinkingType_Disable: + return "Disable" + case ThinkingType_Auto: + return "Auto" + } + return "" +} + +func ThinkingTypeFromString(s string) (ThinkingType, error) { + switch s { + case "Default": + return ThinkingType_Default, nil + case "Enable": + return ThinkingType_Enable, nil + case "Disable": + return ThinkingType_Disable, nil + case "Auto": + return ThinkingType_Auto, nil + } + return ThinkingType(0), fmt.Errorf("not a valid ThinkingType string") +} + +func ThinkingTypePtr(v ThinkingType) *ThinkingType { return &v } +func (p *ThinkingType) Scan(value interface{}) (err error) { + var result sql.NullInt64 + err = result.Scan(value) + *p = ThinkingType(result.Int64) + return +} + +func (p *ThinkingType) Value() (driver.Value, error) { + if p == nil { + return nil, nil + } + return int64(*p), nil +} + type ModelStatus int64 const ( @@ -2913,9 +2965,10 @@ func (p *Connection) String() string { } type BaseConnectionInfo struct { - BaseURL string `thrift:"base_url,1" form:"base_url" json:"base_url" query:"base_url"` - APIKey string `thrift:"api_key,2" form:"api_key" json:"api_key" query:"api_key"` - Model string `thrift:"model,3" form:"model" json:"model" query:"model"` + BaseURL string `thrift:"base_url,1" form:"base_url" json:"base_url" query:"base_url"` + APIKey string `thrift:"api_key,2" form:"api_key" json:"api_key" query:"api_key"` + Model string `thrift:"model,3" form:"model" json:"model" query:"model"` + ThinkingType ThinkingType `thrift:"thinking_type,4" form:"thinking_type" json:"thinking_type" query:"thinking_type"` } func NewBaseConnectionInfo() *BaseConnectionInfo { @@ -2937,10 +2990,15 @@ func (p *BaseConnectionInfo) GetModel() (v string) { return p.Model } +func (p *BaseConnectionInfo) GetThinkingType() (v ThinkingType) { + return p.ThinkingType +} + var fieldIDToName_BaseConnectionInfo = map[int16]string{ 1: "base_url", 2: "api_key", 3: "model", + 4: "thinking_type", } func (p *BaseConnectionInfo) Read(iprot thrift.TProtocol) (err error) { @@ -2985,6 +3043,14 @@ func (p *BaseConnectionInfo) Read(iprot thrift.TProtocol) (err error) { } else if err = iprot.Skip(fieldTypeId); err != nil { goto SkipFieldError } + case 4: + if fieldTypeId == thrift.I32 { + if err = p.ReadField4(iprot); err != nil { + goto ReadFieldError + } + } else if err = iprot.Skip(fieldTypeId); err != nil { + goto SkipFieldError + } default: if err = iprot.Skip(fieldTypeId); err != nil { goto SkipFieldError @@ -3047,6 +3113,17 @@ func (p *BaseConnectionInfo) ReadField3(iprot thrift.TProtocol) error { p.Model = _field return nil } +func (p *BaseConnectionInfo) ReadField4(iprot thrift.TProtocol) error { + + var _field ThinkingType + if v, err := iprot.ReadI32(); err != nil { + return err + } else { + _field = ThinkingType(v) + } + p.ThinkingType = _field + return nil +} func (p *BaseConnectionInfo) Write(oprot thrift.TProtocol) (err error) { var fieldId int16 @@ -3066,6 +3143,10 @@ func (p *BaseConnectionInfo) Write(oprot thrift.TProtocol) (err error) { fieldId = 3 goto WriteFieldError } + if err = p.writeField4(oprot); err != nil { + fieldId = 4 + goto WriteFieldError + } } if err = oprot.WriteFieldStop(); err != nil { goto WriteFieldStopError @@ -3132,6 +3213,22 @@ WriteFieldBeginError: WriteFieldEndError: return thrift.PrependError(fmt.Sprintf("%T write field 3 end error: ", p), err) } +func (p *BaseConnectionInfo) writeField4(oprot thrift.TProtocol) (err error) { + if err = oprot.WriteFieldBegin("thinking_type", thrift.I32, 4); err != nil { + goto WriteFieldBeginError + } + if err := oprot.WriteI32(int32(p.ThinkingType)); err != nil { + return err + } + if err = oprot.WriteFieldEnd(); err != nil { + goto WriteFieldEndError + } + return nil +WriteFieldBeginError: + return thrift.PrependError(fmt.Sprintf("%T write field 4 begin error: ", p), err) +WriteFieldEndError: + return thrift.PrependError(fmt.Sprintf("%T write field 4 end error: ", p), err) +} func (p *BaseConnectionInfo) String() string { if p == nil { @@ -3465,9 +3562,8 @@ func (p *ArkConnInfo) String() string { } type OpenAIConnInfo struct { - RequestDims int32 `thrift:"request_dims,5" form:"request_dims" json:"request_dims" query:"request_dims"` - ByAzure bool `thrift:"by_azure,6" form:"by_azure" json:"by_azure" query:"by_azure"` - APIVersion string `thrift:"api_version,7" form:"api_version" json:"api_version" query:"api_version"` + ByAzure bool `thrift:"by_azure,6" form:"by_azure" json:"by_azure" query:"by_azure"` + APIVersion string `thrift:"api_version,7" form:"api_version" json:"api_version" query:"api_version"` } func NewOpenAIConnInfo() *OpenAIConnInfo { @@ -3477,10 +3573,6 @@ func NewOpenAIConnInfo() *OpenAIConnInfo { func (p *OpenAIConnInfo) InitDefault() { } -func (p *OpenAIConnInfo) GetRequestDims() (v int32) { - return p.RequestDims -} - func (p *OpenAIConnInfo) GetByAzure() (v bool) { return p.ByAzure } @@ -3490,7 +3582,6 @@ func (p *OpenAIConnInfo) GetAPIVersion() (v string) { } var fieldIDToName_OpenAIConnInfo = map[int16]string{ - 5: "request_dims", 6: "by_azure", 7: "api_version", } @@ -3513,14 +3604,6 @@ func (p *OpenAIConnInfo) Read(iprot thrift.TProtocol) (err error) { } switch fieldId { - case 5: - if fieldTypeId == thrift.I32 { - if err = p.ReadField5(iprot); err != nil { - goto ReadFieldError - } - } else if err = iprot.Skip(fieldTypeId); err != nil { - goto SkipFieldError - } case 6: if fieldTypeId == thrift.BOOL { if err = p.ReadField6(iprot); err != nil { @@ -3566,17 +3649,6 @@ ReadStructEndError: return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } -func (p *OpenAIConnInfo) ReadField5(iprot thrift.TProtocol) error { - - var _field int32 - if v, err := iprot.ReadI32(); err != nil { - return err - } else { - _field = v - } - p.RequestDims = _field - return nil -} func (p *OpenAIConnInfo) ReadField6(iprot thrift.TProtocol) error { var _field bool @@ -3606,10 +3678,6 @@ func (p *OpenAIConnInfo) Write(oprot thrift.TProtocol) (err error) { goto WriteStructBeginError } if p != nil { - if err = p.writeField5(oprot); err != nil { - fieldId = 5 - goto WriteFieldError - } if err = p.writeField6(oprot); err != nil { fieldId = 6 goto WriteFieldError @@ -3636,22 +3704,6 @@ WriteStructEndError: return thrift.PrependError(fmt.Sprintf("%T write struct end error: ", p), err) } -func (p *OpenAIConnInfo) writeField5(oprot thrift.TProtocol) (err error) { - if err = oprot.WriteFieldBegin("request_dims", thrift.I32, 5); err != nil { - goto WriteFieldBeginError - } - if err := oprot.WriteI32(p.RequestDims); err != nil { - return err - } - if err = oprot.WriteFieldEnd(); err != nil { - goto WriteFieldEndError - } - return nil -WriteFieldBeginError: - return thrift.PrependError(fmt.Sprintf("%T write field 5 begin error: ", p), err) -WriteFieldEndError: - return thrift.PrependError(fmt.Sprintf("%T write field 5 end error: ", p), err) -} func (p *OpenAIConnInfo) writeField6(oprot thrift.TProtocol) (err error) { if err = oprot.WriteFieldBegin("by_azure", thrift.BOOL, 6); err != nil { goto WriteFieldBeginError diff --git a/backend/api/model/conversation/conversation/conversation.go b/backend/api/model/conversation/conversation/conversation.go index a53592057..d44090f7e 100644 --- a/backend/api/model/conversation/conversation/conversation.go +++ b/backend/api/model/conversation/conversation/conversation.go @@ -1,4 +1,4 @@ -// Code generated by thriftgo (0.4.1). DO NOT EDIT. +// Code generated by thriftgo (0.4.2). DO NOT EDIT. package conversation diff --git a/backend/api/model/conversation/conversation/conversation_service.go b/backend/api/model/conversation/conversation/conversation_service.go index d7f946fcf..97ed06f94 100644 --- a/backend/api/model/conversation/conversation/conversation_service.go +++ b/backend/api/model/conversation/conversation/conversation_service.go @@ -1,4 +1,4 @@ -// Code generated by thriftgo (0.4.1). DO NOT EDIT. +// Code generated by thriftgo (0.4.2). DO NOT EDIT. package conversation diff --git a/backend/bizpkg/config/knowledge/knowledge.go b/backend/bizpkg/config/knowledge/knowledge.go index 5c3d8d584..4baaf5901 100644 --- a/backend/bizpkg/config/knowledge/knowledge.go +++ b/backend/bizpkg/config/knowledge/knowledge.go @@ -82,9 +82,8 @@ func getKnowledgeConfigurationFromOldConfig() *config.KnowledgeConfig { APIType: envkey.GetStringD("ARK_EMBEDDING_API_TYPE", "text_api"), }, Openai: &config.OpenAIConnInfo{ - ByAzure: envkey.GetBoolD("OPENAI_EMBEDDING_BY_AZURE", false), - APIVersion: envkey.GetString("OPENAI_EMBEDDING_API_VERSION"), - RequestDims: envkey.GetI32D("OPENAI_EMBEDDING_REQUEST_DIMS", 1024), + ByAzure: envkey.GetBoolD("OPENAI_EMBEDDING_BY_AZURE", false), + APIVersion: envkey.GetString("OPENAI_EMBEDDING_API_VERSION"), }, Gemini: &config.GeminiConnInfo{ diff --git a/backend/bizpkg/llm/modelbuilder/ark.go b/backend/bizpkg/llm/modelbuilder/ark.go index 47c6a8f66..66ef39e63 100644 --- a/backend/bizpkg/llm/modelbuilder/ark.go +++ b/backend/bizpkg/llm/modelbuilder/ark.go @@ -33,7 +33,7 @@ type arkModelBuilder struct { cfg *config.Model } -func newArkModelBuilder(cfg *config.Model) *arkModelBuilder { +func newArkModelBuilder(cfg *config.Model) Service { return &arkModelBuilder{ cfg: cfg, } @@ -84,6 +84,21 @@ func (b *arkModelBuilder) Build(ctx context.Context, params *LLMParams) (ToolCal chatModelConf.BaseURL = base.BaseURL } + switch base.ThinkingType { + case config.ThinkingType_Enable: + chatModelConf.Thinking = &model.Thinking{ + Type: model.ThinkingTypeEnabled, + } + case config.ThinkingType_Disable: + chatModelConf.Thinking = &model.Thinking{ + Type: model.ThinkingTypeDisabled, + } + case config.ThinkingType_Auto: + chatModelConf.Thinking = &model.Thinking{ + Type: model.ThinkingTypeAuto, + } + } + arkConn := b.cfg.Connection.Ark if arkConn != nil { chatModelConf.Region = arkConn.Region diff --git a/backend/bizpkg/llm/modelbuilder/claude.go b/backend/bizpkg/llm/modelbuilder/claude.go index c31ee5ba7..8e9acf759 100644 --- a/backend/bizpkg/llm/modelbuilder/claude.go +++ b/backend/bizpkg/llm/modelbuilder/claude.go @@ -28,7 +28,7 @@ type claudeModelBuilder struct { cfg *config.Model } -func newClaudeModelBuilder(cfg *config.Model) *claudeModelBuilder { +func newClaudeModelBuilder(cfg *config.Model) Service { return &claudeModelBuilder{ cfg: cfg, } @@ -71,6 +71,17 @@ func (c *claudeModelBuilder) Build(ctx context.Context, params *LLMParams) (Tool conf.BaseURL = &base.BaseURL } + switch base.ThinkingType { + case config.ThinkingType_Enable: + conf.Thinking = &claude.Thinking{ + Enable: true, + } + case config.ThinkingType_Disable: + conf.Thinking = &claude.Thinking{ + Enable: false, + } + } + c.applyParamsToChatModelConfig(conf, params) return claude.NewChatModel(ctx, conf) diff --git a/backend/bizpkg/llm/modelbuilder/deepseek.go b/backend/bizpkg/llm/modelbuilder/deepseek.go index cd4ea7845..296305c6d 100644 --- a/backend/bizpkg/llm/modelbuilder/deepseek.go +++ b/backend/bizpkg/llm/modelbuilder/deepseek.go @@ -29,7 +29,7 @@ type deepseekModelBuilder struct { cfg *config.Model } -func newDeepseekModelBuilder(cfg *config.Model) *deepseekModelBuilder { +func newDeepseekModelBuilder(cfg *config.Model) Service { return &deepseekModelBuilder{ cfg: cfg, } diff --git a/backend/bizpkg/llm/modelbuilder/gemini.go b/backend/bizpkg/llm/modelbuilder/gemini.go index e0201a10a..9875ef46e 100644 --- a/backend/bizpkg/llm/modelbuilder/gemini.go +++ b/backend/bizpkg/llm/modelbuilder/gemini.go @@ -30,7 +30,7 @@ type geminiModelBuilder struct { cfg *config.Model } -func newGeminiModelBuilder(cfg *config.Model) *geminiModelBuilder { +func newGeminiModelBuilder(cfg *config.Model) Service { return &geminiModelBuilder{ cfg: cfg, } @@ -67,7 +67,6 @@ func (g *geminiModelBuilder) applyParamsToGeminiConfig(conf *gemini.Config, para if params.EnableThinking != nil { conf.ThinkingConfig = &genai.ThinkingConfig{ IncludeThoughts: *params.EnableThinking, - ThinkingBudget: nil, } } } @@ -96,6 +95,17 @@ func (g *geminiModelBuilder) Build(ctx context.Context, params *LLMParams) (Tool conf.Client = client conf.Model = base.Model + switch base.ThinkingType { + case config.ThinkingType_Enable: + conf.ThinkingConfig = &genai.ThinkingConfig{ + IncludeThoughts: true, + } + case config.ThinkingType_Disable: + conf.ThinkingConfig = &genai.ThinkingConfig{ + IncludeThoughts: false, + } + } + g.applyParamsToGeminiConfig(conf, params) return gemini.NewChatModel(ctx, conf) diff --git a/backend/bizpkg/llm/modelbuilder/model_builder.go b/backend/bizpkg/llm/modelbuilder/model_builder.go index b5e32dc93..7b74ff41a 100644 --- a/backend/bizpkg/llm/modelbuilder/model_builder.go +++ b/backend/bizpkg/llm/modelbuilder/model_builder.go @@ -39,6 +39,16 @@ type Service interface { Build(ctx context.Context, params *LLMParams) (ToolCallingChatModel, error) } +var modelClass2NewModelBuilder = map[developer_api.ModelClass]func(*config.Model) Service{ + developer_api.ModelClass_SEED: newArkModelBuilder, + developer_api.ModelClass_GPT: newOpenaiModelBuilder, + developer_api.ModelClass_Claude: newClaudeModelBuilder, + developer_api.ModelClass_DeekSeek: newDeepseekModelBuilder, + developer_api.ModelClass_Gemini: newGeminiModelBuilder, + developer_api.ModelClass_Llama: newOllamaModelBuilder, + developer_api.ModelClass_QWen: newQwenModelBuilder, +} + func NewModelBuilder(modelClass developer_api.ModelClass, cfg *config.Model) (Service, error) { if cfg == nil { return nil, fmt.Errorf("model config is nil") @@ -52,37 +62,18 @@ func NewModelBuilder(modelClass developer_api.ModelClass, cfg *config.Model) (Se return nil, fmt.Errorf("model base connection is nil") } - switch modelClass { - case developer_api.ModelClass_SEED: - return newArkModelBuilder(cfg), nil - case developer_api.ModelClass_GPT: - return newOpenaiModelBuilder(cfg), nil - case developer_api.ModelClass_Claude: - return newClaudeModelBuilder(cfg), nil - case developer_api.ModelClass_DeekSeek: - return newDeepseekModelBuilder(cfg), nil - case developer_api.ModelClass_Gemini: - return newGeminiModelBuilder(cfg), nil - case developer_api.ModelClass_Llama: - return newOllamaModelBuilder(cfg), nil - case developer_api.ModelClass_QWen: - return newQwenModelBuilder(cfg), nil - default: + buildFn, ok := modelClass2NewModelBuilder[modelClass] + if !ok { return nil, fmt.Errorf("model class %v not supported", modelClass) } + + return buildFn(cfg), nil } func SupportProtocol(modelClass developer_api.ModelClass) bool { - if modelClass == developer_api.ModelClass_GPT || - modelClass == developer_api.ModelClass_Claude || - modelClass == developer_api.ModelClass_DeekSeek || - modelClass == developer_api.ModelClass_SEED || - modelClass == developer_api.ModelClass_Gemini || - modelClass == developer_api.ModelClass_Llama || - modelClass == developer_api.ModelClass_QWen { - return true - } - return false + _, ok := modelClass2NewModelBuilder[modelClass] + + return ok } // BuildModelWithConf for create model scene, params is nil diff --git a/backend/bizpkg/llm/modelbuilder/ollama.go b/backend/bizpkg/llm/modelbuilder/ollama.go index b09766e27..5fc77b148 100644 --- a/backend/bizpkg/llm/modelbuilder/ollama.go +++ b/backend/bizpkg/llm/modelbuilder/ollama.go @@ -30,7 +30,7 @@ type ollamaModelBuilder struct { cfg *config.Model } -func newOllamaModelBuilder(cfg *config.Model) *ollamaModelBuilder { +func newOllamaModelBuilder(cfg *config.Model) Service { return &ollamaModelBuilder{ cfg: cfg, } @@ -84,6 +84,17 @@ func (o *ollamaModelBuilder) Build(ctx context.Context, params *LLMParams) (Tool } conf.Model = base.Model + switch base.ThinkingType { + case config.ThinkingType_Enable: + conf.Thinking = &api.ThinkValue{ + Value: ptr.Of(true), + } + case config.ThinkingType_Disable: + conf.Thinking = &api.ThinkValue{ + Value: ptr.Of(false), + } + } + o.applyParamsToOllamaConfig(conf, params) return ollama.NewChatModel(ctx, conf) diff --git a/backend/bizpkg/llm/modelbuilder/openai.go b/backend/bizpkg/llm/modelbuilder/openai.go index ccd393277..a57f24f3a 100644 --- a/backend/bizpkg/llm/modelbuilder/openai.go +++ b/backend/bizpkg/llm/modelbuilder/openai.go @@ -30,7 +30,7 @@ type openaiModelBuilder struct { cfg *config.Model } -func newOpenaiModelBuilder(cfg *config.Model) *openaiModelBuilder { +func newOpenaiModelBuilder(cfg *config.Model) Service { return &openaiModelBuilder{ cfg: cfg, } diff --git a/backend/bizpkg/llm/modelbuilder/qwen.go b/backend/bizpkg/llm/modelbuilder/qwen.go index 8ec271ebd..509f81a08 100644 --- a/backend/bizpkg/llm/modelbuilder/qwen.go +++ b/backend/bizpkg/llm/modelbuilder/qwen.go @@ -30,7 +30,7 @@ type qwenModelBuilder struct { cfg *config.Model } -func newQwenModelBuilder(cfg *config.Model) *qwenModelBuilder { +func newQwenModelBuilder(cfg *config.Model) Service { return &qwenModelBuilder{ cfg: cfg, } @@ -82,6 +82,13 @@ func (q *qwenModelBuilder) Build(ctx context.Context, params *LLMParams) (ToolCa conf.BaseURL = base.BaseURL conf.Model = base.Model + switch base.ThinkingType { + case config.ThinkingType_Enable: + conf.EnableThinking = ptr.Of(true) + case config.ThinkingType_Disable: + conf.EnableThinking = ptr.Of(false) + } + q.applyParamsToQwenConfig(conf, params) return qwen.NewChatModel(ctx, conf) diff --git a/backend/conf/admin/index.html b/backend/conf/admin/index.html index 10a43b2f1..d1596b7ec 100644 --- a/backend/conf/admin/index.html +++ b/backend/conf/admin/index.html @@ -653,6 +653,11 @@ 'model.field.azure': 'By Azure', 'model.field.api_version': 'API Version', 'model.field.endpoint': '接口地址', + 'model.field.thinking_type': 'Thinking Type', + 'model.thinking_type.default': 'Default', + 'model.thinking_type.enable': 'Enable', + 'model.thinking_type.disable': 'Disable', + 'model.thinking_type.auto': 'Auto', 'model.error.load_list_failed': '加载模型列表失败', 'model.error.render_page_failed': '渲染模型页面失败', @@ -694,6 +699,10 @@ 'model.modal.gemini_project.placeholder': '选填,例如:my-project', 'model.modal.gemini_location.label': 'Location', 'model.modal.gemini_location.placeholder': '选填,例如:us-central1', + 'model.modal.thinking_type.label': 'Thinking Type', + 'model.modal.thinking_type.enable': 'Enable', + 'model.modal.thinking_type.disable': 'Disable', + 'model.modal.thinking_type.auto': 'Auto', // 知识库配置 'knowledge.error.load_failed_json': '加载知识库配置失败: 返回结果不是有效 JSON', @@ -868,6 +877,11 @@ 'model.field.azure': 'By Azure', 'model.field.api_version': 'API Version', 'model.field.endpoint': 'Endpoint', + 'model.field.thinking_type': 'Thinking Type', + 'model.thinking_type.default': 'Default', + 'model.thinking_type.enable': 'Enable', + 'model.thinking_type.disable': 'Disable', + 'model.thinking_type.auto': 'Auto', 'model.error.load_list_failed': 'Failed to load model list', 'model.error.render_page_failed': 'Failed to render model page', @@ -909,6 +923,10 @@ 'model.modal.gemini_project.placeholder': 'Optional, e.g., my-project', 'model.modal.gemini_location.label': 'Location', 'model.modal.gemini_location.placeholder': 'Optional, e.g., us-central1', + 'model.modal.thinking_type.label': 'Thinking Type', + 'model.modal.thinking_type.enable': 'Enable', + 'model.modal.thinking_type.disable': 'Disable', + 'model.modal.thinking_type.auto': 'Auto', // Knowledge Config 'knowledge.error.load_failed_json': 'Load knowledge config failed: invalid JSON response', @@ -2014,6 +2032,17 @@ return s.slice(0, 6) + '***' + s.slice(-4); } + function getThinkingTypeText(thinkingTypeValue) { + const thinkingType = Number(thinkingTypeValue); + switch (thinkingType) { + case 0: return t('model.thinking_type.default'); + case 1: return t('model.thinking_type.enable'); + case 2: return t('model.thinking_type.disable'); + case 3: return t('model.thinking_type.auto'); + default: return '-'; + } + } + function renderModelManagement(payload) { try { const providers = Array.isArray(payload?.provider_model_list) ? payload.provider_model_list : []; @@ -2070,6 +2099,12 @@ + +
@@ -2124,6 +2159,7 @@
${t('model.field.model')}:${conn.model || '-'}
${p?.model_class === 20 ? '' : `
${t('model.field.api_key')}:${apiKeyMasked}
`}
${t('model.field.base64_url')}:${m.enable_base64_url ? t('common.on') : t('common.off')}
+ ${[2, 3, 11, 15, 20].includes(p?.model_class) ? `
${t('model.field.thinking_type')}:${getThinkingTypeText(conn.thinking_type ?? 0)}
` : ''} ${p?.model_class === 2 ? `
${t('model.field.region')}:${m?.connection?.ark?.region || ''}
` : ''} ${p?.model_class === 11 ? `
${t('model.field.backend')}:${m?.connection?.gemini?.backend ?? ''}
${t('model.field.project')}:${m?.connection?.gemini?.project || ''}
${t('model.field.location')}:${m?.connection?.gemini?.location || ''}
` : ''} ${p?.model_class === 1 ? `
${t('model.field.azure')}:${(m?.connection?.openai?.by_azure === undefined) ? '' : (m?.connection?.openai?.by_azure ? 'true' : 'false')}
${t('model.field.api_version')}:${m?.connection?.openai?.api_version || ''}
` : ''} @@ -2241,6 +2277,20 @@ if (geminiProjectInput) geminiProjectInput.style.display = isGemini ? '' : 'none'; if (geminiLocationLabel) geminiLocationLabel.style.display = isGemini ? '' : 'none'; if (geminiLocationInput) geminiLocationInput.style.display = isGemini ? '' : 'none'; + + // Show/hide ThinkingType dropdown + const thinkingTypeLabel = document.getElementById('newModelThinkingTypeLabel'); + const thinkingTypeSelect = document.getElementById('newModelThinkingType'); + const thinkingTypeAutoOption = document.getElementById('thinkingTypeAutoOption'); + const hasThinkingType = [2, 3, 11, 15, 20].includes(providerClass); // Ark, Claude, Gemini, Qwen, Ollama + const isArkProvider = providerClass === 2; + if (thinkingTypeLabel) thinkingTypeLabel.style.display = hasThinkingType ? '' : 'none'; + if (thinkingTypeSelect) { + thinkingTypeSelect.style.display = hasThinkingType ? '' : 'none'; + thinkingTypeSelect.value = '2'; // Reset to Disable + } + if (thinkingTypeAutoOption) thinkingTypeAutoOption.style.display = isArkProvider ? '' : 'none'; + modal.style.display = 'flex'; } function closeAddModelModal() { @@ -2286,6 +2336,16 @@ const baseConn = { model: model }; if (baseUrl) baseConn.base_url = baseUrl; if (requireApiKey && apiKey) baseConn.api_key = apiKey; + + // 读取 ThinkingType(仅 Ark, Claude, Gemini, Qwen, Ollama) + const thinkingTypeSelect = document.getElementById('newModelThinkingType'); + const hasThinkingType = [2, 3, 11, 15, 20].includes(providerClass); + if (hasThinkingType && thinkingTypeSelect) { + const thinkingTypeValue = parseInt(thinkingTypeSelect.value, 10); + if (!Number.isNaN(thinkingTypeValue)) { + baseConn.thinking_type = thinkingTypeValue; + } + } // 读取“启用 Base64 URL”复选框,默认 false const enableBase64UrlEl = document.getElementById('newModelEnableBase64Url'); const enableBase64Url = !!(enableBase64UrlEl && enableBase64UrlEl.checked); @@ -2463,7 +2523,6 @@ openai_api_key: baseConn?.api_key || '', openai_model: baseConn?.model || '', openai_embedding_dims: embeddingInfo?.dims ?? '0', - openai_embedding_request_dims: conn?.openai?.embedding_request_dims ?? conn?.openai?.request_dims ?? '', openai_embedding_by_azure: Boolean(conn?.openai?.embedding_by_azure ?? conn?.openai?.by_azure), openai_embedding_api_version: conn?.openai?.embedding_api_version ?? conn?.openai?.api_version ?? '', ollama_base_url: baseConn?.base_url || '', @@ -2623,7 +2682,7 @@
Dims
- +
API Type
@@ -2653,7 +2712,7 @@
Dims
- +
@@ -2664,10 +2723,6 @@
Embedding API Version
-
-
Embedding Request Dims
- -
@@ -2684,7 +2739,7 @@
Dims
- +
@@ -2711,7 +2766,7 @@
Dims
- +
Project
@@ -2733,7 +2788,7 @@
Dims
- +
@@ -3065,7 +3120,6 @@ api_type: val('kb_ark_embedding_api_type') }, openai: { - request_dims: toNum(val('kb_openai_embedding_request_dims')), by_azure: checked('kb_openai_embedding_by_azure'), api_version: val('kb_openai_embedding_api_version') }, @@ -3113,13 +3167,22 @@ // Embedding 必填项校验(不同类型不同必填要求) const typeVal = Number(document.getElementById('kb_embedding_type')?.value); const get = (id) => (document.getElementById(id)?.value || '').trim(); + const getDims = (id) => { + const el = document.getElementById(id); + return el ? Number(el.value) : 0; + }; + if (typeVal === 0) { // Ark: Base URL、API Key、Model、Dims 必填,API Type 可选 - const dimsVal = Number(document.getElementById('kb_ark_embedding_dims')?.value); + const dimsVal = getDims('kb_ark_embedding_dims'); const missing = []; if (!get('kb_ark_base_url')) missing.push('Base URL'); if (!get('kb_ark_api_key')) missing.push('API Key'); if (!get('kb_ark_model')) missing.push('Model'); + if (dimsVal < 0) { + showError('Dims must be >= 0'); + return; + } if (missing.length > 0) { showError(t('error.missing_required', { list: missing.join(', ') })); @@ -3127,43 +3190,59 @@ } } else if (typeVal === 1) { // OpenAI: Base URL、Model、API Key、Dims 必填 - const dimsVal = Number(document.getElementById('kb_openai_embedding_dims')?.value); + const dimsVal = getDims('kb_openai_embedding_dims'); const missing = []; if (!get('kb_openai_base_url')) missing.push('Base URL'); if (!get('kb_openai_model')) missing.push('Model'); if (!get('kb_openai_api_key')) missing.push('API Key'); + if (dimsVal < 0) { + showError('Dims must be >= 0'); + return; + } if (missing.length > 0) { showError(t('error.missing_required', { list: missing.join(', ') })); return; } } else if (typeVal === 2) { // Ollama: Base URL、Model、Dims 必填 - const dimsVal = Number(document.getElementById('kb_ollama_embedding_dims')?.value); + const dimsVal = getDims('kb_ollama_embedding_dims'); const missing = []; if (!get('kb_ollama_base_url')) missing.push('Base URL'); if (!get('kb_ollama_model')) missing.push('Model'); + if (dimsVal < 0) { + showError('Dims must be >= 0'); + return; + } if (missing.length > 0) { showError(t('error.missing_required', { list: missing.join(', ') })); return; } } else if (typeVal === 3) { // Gemini: Base URL、API Key、Model、Dims、Backend 必填;Project、Location 选填 - const dimsVal = Number(document.getElementById('kb_gemini_embedding_dims')?.value); + const dimsVal = getDims('kb_gemini_embedding_dims'); const backendStr = (document.getElementById('kb_gemini_embedding_backend')?.value || '').trim(); const missing = []; if (!get('kb_gemini_base_url')) missing.push('Base URL'); if (!get('kb_gemini_api_key')) missing.push('API Key'); if (!get('kb_gemini_model')) missing.push('Model'); if (!backendStr) missing.push('Backend'); + if (dimsVal < 0) { + showError('Dims must be >= 0'); + return; + } if (missing.length > 0) { showError(t('error.missing_required', { list: missing.join(', ') })); return; } } else if (typeVal === 4) { // HTTP: Address、Dims 必填 - const dimsVal = Number(document.getElementById('kb_http_dims')?.value); + const dimsVal = getDims('kb_http_dims'); const missing = []; if (!get('kb_http_address')) missing.push('Address'); + if (dimsVal < 0) { + showError('Dims must be >= 0'); + return; + } if (missing.length > 0) { showError(t('error.missing_required', { list: missing.join(', ') })); return; diff --git a/backend/infra/embedding/impl/ark/ark.go b/backend/infra/embedding/impl/ark/ark.go index 22b909e6d..46325a588 100644 --- a/backend/infra/embedding/impl/ark/ark.go +++ b/backend/infra/embedding/impl/ark/ark.go @@ -92,7 +92,7 @@ func (d *embWrap) EmbedStringsHybrid(ctx context.Context, texts []string, opts . } func (d *embWrap) Dimensions() int64 { - if d.dims == 0 { + if d.dims <= 0 { embeddings, err := d.Embedder.EmbedStrings(context.Background(), []string{"test"}) if err != nil { return 0 diff --git a/backend/infra/embedding/impl/http/http.go b/backend/infra/embedding/impl/http/http.go index 3d56b1745..7faff102a 100644 --- a/backend/infra/embedding/impl/http/http.go +++ b/backend/infra/embedding/impl/http/http.go @@ -195,7 +195,7 @@ func (e *embedder) do(req *http.Request) (*embedResp, error) { } func (e *embedder) Dimensions() int64 { - if e.dim == 0 { + if e.dim <= 0 { embeddings, err := e.EmbedStrings(context.Background(), []string{"test"}) if err != nil { return 0 diff --git a/backend/infra/embedding/impl/impl.go b/backend/infra/embedding/impl/impl.go index 308232197..b51a5c842 100644 --- a/backend/infra/embedding/impl/impl.go +++ b/backend/infra/embedding/impl/impl.go @@ -52,9 +52,9 @@ func GetEmbedding(ctx context.Context, cfg *config.EmbeddingConfig) (embedding.E APIVersion: openaiConnCfg.APIVersion, } - if openaiConnCfg.RequestDims > 0 { + if embeddingInfo.Dims > 0 { // some openai model not support request dims - openAICfg.Dimensions = ptr.Of(int(openaiConnCfg.RequestDims)) + openAICfg.Dimensions = ptr.Of(int(embeddingInfo.Dims)) } emb, err = wrap.NewOpenAIEmbedder(ctx, openAICfg, int64(embeddingInfo.Dims), int(cfg.MaxBatchSize)) diff --git a/backend/infra/embedding/impl/wrap/dense_only.go b/backend/infra/embedding/impl/wrap/dense_only.go index adaa49da7..59d4392d4 100644 --- a/backend/infra/embedding/impl/wrap/dense_only.go +++ b/backend/infra/embedding/impl/wrap/dense_only.go @@ -49,7 +49,7 @@ func (d *denseOnlyWrap) EmbedStringsHybrid(ctx context.Context, texts []string, } func (d *denseOnlyWrap) Dimensions() int64 { - if d.dims == 0 { + if d.dims <= 0 { embeddings, err := d.Embedder.EmbedStrings(context.Background(), []string{"test"}) if err != nil { return 0 diff --git a/idl/admin/config.thrift b/idl/admin/config.thrift index c2e0c80d1..e7b265c71 100644 --- a/idl/admin/config.thrift +++ b/idl/admin/config.thrift @@ -62,6 +62,13 @@ struct Model { 10: i64 delete_at_ms } +enum ThinkingType { + Default = 0 + Enable = 1 + Disable = 2 + Auto = 3 +} + enum ModelStatus { StatusDefault = 0 // Default state when not configured, equivalent to StatusInUse @@ -84,6 +91,7 @@ struct BaseConnectionInfo { 1: string base_url 2: string api_key 3: string model + 4: ThinkingType thinking_type } struct EmbeddingInfo { @@ -97,7 +105,6 @@ struct ArkConnInfo { } struct OpenAIConnInfo { - 5: i32 request_dims 6: bool by_azure 7: string api_version }