diff --git a/docs/swagger.json b/docs/swagger.json index dac2b38fd..851e701aa 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -134,6 +134,77 @@ } } }, + "/answer/admin/api/ai-prompt-config": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get AI prompt configuration", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "get AI prompt configuration", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/handler.RespBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/schema.AIPromptConfig" + } + } + } + ] + } + } + } + }, + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "update AI prompt configuration", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "update AI prompt configuration", + "parameters": [ + { + "description": "AI prompt config", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/schema.AIPromptConfig" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.RespBody" + } + } + } + } + }, "/answer/admin/api/ai-provider": { "get": { "security": [ diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index 9a0d198b3..23ef11930 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -2354,8 +2354,14 @@ ui: model: label: Model msg: Model is required + prompt: + label: Prompt + text: Shows the prompt for the current language. Edit and save to apply. add_success: AI settings updated successfully. conversations: + tabs: + conversations: Conversations + settings: Settings topic: Topic helpful: Helpful unhelpful: Unhelpful @@ -2482,5 +2488,3 @@ ui: copy: Copy to clipboard copied: Copied external_content_warning: External images/media are not displayed. - - diff --git a/i18n/zh_CN.yaml b/i18n/zh_CN.yaml index f16ed9fad..902915d09 100644 --- a/i18n/zh_CN.yaml +++ b/i18n/zh_CN.yaml @@ -1796,7 +1796,7 @@ ui: security: 安全 files: 文件 apikeys: API 密钥 - intelligence: 智力 + intelligence: 智能 ai_assistant: AI 助手 ai_settings: AI 设置 mcp: MCP @@ -2318,8 +2318,14 @@ ui: model: label: 模型 msg: 模型是必需的 + prompt: + label: 提示词 + text: 显示当前语言环境的提示词,可在此修改并保存。 add_success: AI 设置更新成功。 conversations: + tabs: + conversations: 对话 + settings: 设置 topic: 主题 helpful: 有帮助 unhelpful: 没有帮助 @@ -2446,5 +2452,3 @@ ui: copy: 复制到剪贴板 copied: 已复制 external_content_warning: 外部图像/媒体未显示。 - - diff --git a/internal/controller_admin/siteinfo_controller.go b/internal/controller_admin/siteinfo_controller.go index 4575a5d19..7525d7370 100644 --- a/internal/controller_admin/siteinfo_controller.go +++ b/internal/controller_admin/siteinfo_controller.go @@ -618,6 +618,38 @@ func (sc *SiteInfoController) UpdateAIConfig(ctx *gin.Context) { handler.HandleResponse(ctx, err, nil) } +// GetAIPromptConfig get AI prompt configuration +// @Summary get AI prompt configuration +// @Description get AI prompt configuration +// @Security ApiKeyAuth +// @Tags admin +// @Produce json +// @Success 200 {object} handler.RespBody{data=schema.AIPromptConfig} +// @Router /answer/admin/api/ai-prompt-config [get] +func (sc *SiteInfoController) GetAIPromptConfig(ctx *gin.Context) { + resp, err := sc.siteInfoService.GetAIPromptConfig(ctx) + handler.HandleResponse(ctx, err, resp) +} + +// UpdateAIPromptConfig update AI prompt configuration +// @Summary update AI prompt configuration +// @Description update AI prompt configuration +// @Security ApiKeyAuth +// @Tags admin +// @Param data body schema.AIPromptConfig true "AI prompt config" +// @Produce json +// @Success 200 {object} handler.RespBody{} +// @Router /answer/admin/api/ai-prompt-config [put] +func (sc *SiteInfoController) UpdateAIPromptConfig(ctx *gin.Context) { + req := &schema.AIPromptConfig{} + if handler.BindAndCheck(ctx, req) { + return + } + + err := sc.siteInfoService.SaveAIPromptConfig(ctx, req) + handler.HandleResponse(ctx, err, nil) +} + // GetAIProvider get AI provider configuration // @Summary get AI provider configuration // @Description get AI provider configuration diff --git a/internal/router/answer_api_router.go b/internal/router/answer_api_router.go index 84b8b4e1c..29f59fcef 100644 --- a/internal/router/answer_api_router.go +++ b/internal/router/answer_api_router.go @@ -425,6 +425,8 @@ func (a *AnswerAPIRouter) RegisterAnswerAdminAPIRouter(r *gin.RouterGroup) { // ai config r.GET("/ai-config", a.adminSiteInfoController.GetAIConfig) r.PUT("/ai-config", a.adminSiteInfoController.UpdateAIConfig) + r.GET("/ai-prompt-config", a.adminSiteInfoController.GetAIPromptConfig) + r.PUT("/ai-prompt-config", a.adminSiteInfoController.UpdateAIPromptConfig) r.GET("/ai-provider", a.adminSiteInfoController.GetAIProvider) r.POST("/ai-models", a.adminSiteInfoController.RequestAIModels) diff --git a/internal/service/siteinfo/siteinfo_service.go b/internal/service/siteinfo/siteinfo_service.go index 1e25cbaa4..76a8bec1e 100644 --- a/internal/service/siteinfo/siteinfo_service.go +++ b/internal/service/siteinfo/siteinfo_service.go @@ -365,16 +365,52 @@ func (s *SiteInfoService) GetSiteAI(ctx context.Context) (resp *schema.SiteAIRes return resp, nil } +// GetAIPromptConfig get AI prompt configuration +func (s *SiteInfoService) GetAIPromptConfig(ctx context.Context) (resp *schema.AIPromptConfig, err error) { + siteAI, err := s.siteInfoCommonService.GetSiteAI(ctx) + if err != nil { + return nil, err + } + if siteAI.PromptConfig != nil { + return &schema.AIPromptConfig{ + ZhCN: siteAI.PromptConfig.ZhCN, + EnUS: siteAI.PromptConfig.EnUS, + }, nil + } + return &schema.AIPromptConfig{ + ZhCN: constant.DefaultAIPromptConfigZhCN, + EnUS: constant.DefaultAIPromptConfigEnUS, + }, nil +} + +// SaveAIPromptConfig save AI prompt configuration +func (s *SiteInfoService) SaveAIPromptConfig(ctx context.Context, req *schema.AIPromptConfig) (err error) { + siteAI, err := s.siteInfoCommonService.GetSiteAI(ctx) + if err != nil { + return err + } + siteAI.PromptConfig = req + + content, _ := json.Marshal(siteAI) + siteInfo := &entity.SiteInfo{ + Type: constant.SiteTypeAI, + Content: string(content), + Status: 1, + } + return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeAI, siteInfo) +} + // SaveSiteAI save site AI configuration func (s *SiteInfoService) SaveSiteAI(ctx context.Context, req *schema.SiteAIReq) (err error) { if err := s.restoreMaskedAIKeys(ctx, req); err != nil { return err } if req.PromptConfig == nil { - req.PromptConfig = &schema.AIPromptConfig{ - ZhCN: constant.DefaultAIPromptConfigZhCN, - EnUS: constant.DefaultAIPromptConfigEnUS, + promptConfig, err := s.GetAIPromptConfig(ctx) + if err != nil { + return err } + req.PromptConfig = promptConfig } aiProvider, err := s.GetAIProvider(ctx) diff --git a/ui/src/common/constants.ts b/ui/src/common/constants.ts index 862072817..345677ac5 100644 --- a/ui/src/common/constants.ts +++ b/ui/src/common/constants.ts @@ -165,6 +165,11 @@ export const ADMIN_QA_NAV_MENUS = [ { name: 'settings', path: '/admin/qa/settings' }, ]; +export const ADMIN_AI_ASSISTANT_NAV_MENUS = [ + { name: 'conversations', path: '/admin/ai-assistant' }, + { name: 'settings', path: '/admin/ai-assistant/settings' }, +]; + export const ADMIN_TAGS_NAV_MENUS = [ // { name: 'tags', path: '/admin/tags' }, { diff --git a/ui/src/common/interface.ts b/ui/src/common/interface.ts index 308726e80..6497dbd64 100644 --- a/ui/src/common/interface.ts +++ b/ui/src/common/interface.ts @@ -825,6 +825,11 @@ export interface AddOrEditApiKeyParams { id?: number; } +export interface AIPromptConfig { + zh_cn: string; + en_us: string; +} + export interface AiConfig { enabled: boolean; chosen_provider: string; @@ -834,6 +839,7 @@ export interface AiConfig { api_key: string; model: string; }>; + prompt_config?: AIPromptConfig; } export interface AiProviderItem { diff --git a/ui/src/components/TabNav/index.tsx b/ui/src/components/TabNav/index.tsx index 6b1b434df..b45327b18 100644 --- a/ui/src/components/TabNav/index.tsx +++ b/ui/src/components/TabNav/index.tsx @@ -22,8 +22,11 @@ import { Nav } from 'react-bootstrap'; import { NavLink, useLocation } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -const TabNav: FC<{ menus: { name: string; path: string }[] }> = ({ menus }) => { - const { t } = useTranslation('translation', { keyPrefix: 'nav_menus' }); +const TabNav: FC<{ + menus: { name: string; path: string }[]; + i18nKeyPrefix?: string; +}> = ({ menus, i18nKeyPrefix = 'nav_menus' }) => { + const { t } = useTranslation('translation', { keyPrefix: i18nKeyPrefix }); const { pathname } = useLocation(); return (