Go: add get chat (#14025)

### What problem does this PR solve?

As title

### Type of change

- [x] New Feature (non-breaking change which adds functionality)

---------

Signed-off-by: Jin Hai <haijin.chn@gmail.com>
This commit is contained in:
Jin Hai
2026-04-10 13:06:51 +08:00
committed by GitHub
parent aa92abe73c
commit a37605cbd2
4 changed files with 169 additions and 5 deletions

View File

@@ -229,3 +229,12 @@ func (dao *ChatDAO) GetAllDialogIDsByTenantID(tenantID string) ([]string, error)
Pluck("id", &dialogIDs).Error
return dialogIDs, err
}
// QueryByTenantIDAndID checks if a chat exists with given tenant_id and id
// Reference: Python DialogService.query(tenant_id=tenant.tenant_id, id=chat_id, status=StatusEnum.VALID.value)
// Used for permission verification in get_chat API
func (dao *ChatDAO) QueryByTenantIDAndID(tenantID string, chatID string, status string) ([]*entity.Chat, error) {
var chats []*entity.Chat
err := DB.Where("tenant_id = ? AND id = ? AND status = ?", tenantID, chatID, status).Find(&chats).Error
return chats, err
}

View File

@@ -47,7 +47,7 @@ func NewChatHandler(chatService *service.ChatService, userService *service.UserS
// @Accept json
// @Produce json
// @Success 200 {object} service.ListChatsResponse
// @Router /v1/dialog/list [get]
// @Router /api/v1/chats [get]
func (h *ChatHandler) ListChats(c *gin.Context) {
user, errorCode, errorMessage := GetUser(c)
if errorCode != common.CodeSuccess {
@@ -257,3 +257,96 @@ func (h *ChatHandler) RemoveChats(c *gin.Context) {
"message": "success",
})
}
// GetChat get chat detail
// @Summary Get Chat Detail
// @Description Get detail of a chat by ID
// @Tags chat
// @Accept json
// @Produce json
// @Param chat_id path string true "chat ID"
// @Success 200 {object} service.GetChatResponse
// @Router /api/v1/chats/{chat_id} [get]
// Reference: api/apps/restful_apis/chat_api.py::get_chat
// Python implementation details:
// - Route: @manager.route("/chats/<chat_id>", methods=["GET"])
func (h *ChatHandler) GetChat(c *gin.Context) {
// Get current user from context (same as Python current_user)
user, errorCode, errorMessage := GetUser(c)
if errorCode != common.CodeSuccess {
jsonError(c, errorCode, errorMessage)
return
}
userID := user.ID
// Get chat_id from path parameter (same as Python <chat_id>)
chatID := c.Param("chat_id")
if chatID == "" {
c.JSON(http.StatusBadRequest, gin.H{
"code": common.CodeBadRequest,
"data": nil,
"message": "chat_id is required",
})
return
}
// Get chat detail with permission check
chat, err := h.chatService.GetChat(userID, chatID)
if err != nil {
errMsg := err.Error()
// Check if it's an authorization error
if errMsg == "no authorization" {
c.JSON(http.StatusOK, gin.H{
"code": common.CodeAuthenticationError,
"data": false,
"message": "No authorization.",
})
return
}
// Not found error
c.JSON(http.StatusOK, gin.H{
"code": common.CodeDataError,
"data": nil,
"message": err.Error(),
})
return
}
// Build response (same as Python _build_chat_response)
// The service already returns GetChatResponse with DatasetIDs and KBNames
result := map[string]interface{}{
"id": chat.ID,
"tenant_id": chat.TenantID,
"name": chat.Name,
"description": chat.Description,
"icon": chat.Icon,
"language": chat.Language,
"llm_id": chat.LLMID,
"llm_setting": chat.LLMSetting,
"prompt_type": chat.PromptType,
"prompt_config": chat.PromptConfig,
"meta_data_filter": chat.MetaDataFilter,
"similarity_threshold": chat.SimilarityThreshold,
"vector_similarity_weight": chat.VectorSimilarityWeight,
"top_n": chat.TopN,
"top_k": chat.TopK,
"do_refer": chat.DoRefer,
"rerank_id": chat.RerankID,
"dataset_ids": chat.DatasetIDs,
"kb_names": chat.KBNames,
"status": chat.Status,
"create_time": chat.CreateTime,
"create_date": chat.CreateDate,
"update_time": chat.UpdateTime,
"update_date": chat.UpdateDate,
"tenant_llm_id": chat.TenantLLMID,
"tenant_rerank_id": chat.TenantRerankID,
}
// Return success response
c.JSON(http.StatusOK, gin.H{
"code": common.CodeSuccess,
"data": result,
"message": "success",
})
}

View File

@@ -187,10 +187,11 @@ func (r *Router) Setup(engine *gin.Engine) {
// message.GET("/:memory_id/:message_id/content", r.memoryHandler.GetMessageContent)
// }
chats := v1.Group("/chats")
{
chats.GET("", r.chatHandler.ListChats)
}
chats := v1.Group("/chats")
{
chats.GET("", r.chatHandler.ListChats)
chats.GET("/:chat_id", r.chatHandler.GetChat)
}
searches := v1.Group("/searches")
{

View File

@@ -621,3 +621,64 @@ func strPtr(s string) *string {
func (s *ChatService) countRunes(str string) int {
return utf8.RuneCountInString(str)
}
// GetChatResponse get chat response with kb_names
// Reference: Python _build_chat_response
type GetChatResponse struct {
*entity.Chat
DatasetIDs []string `json:"dataset_ids"`
KBNames []string `json:"kb_names"`
}
// GetChat gets chat detail by ID with permission check
func (s *ChatService) GetChat(userID string, chatID string) (*GetChatResponse, error) {
// Step 1: Get user tenants (same as Python UserTenantService.query(user_id=current_user.id))
tenants, err := s.userTenantDAO.GetByUserID(userID)
if err != nil {
return nil, fmt.Errorf("failed to get user tenants: %w", err)
}
// Step 2: Check if user has permission to access this chat
// Python: for tenant in tenants: if DialogService.query(tenant_id=tenant.tenant_id, id=chat_id, status=StatusEnum.VALID.value): break
hasPermission := false
for _, tenant := range tenants {
chats, err := s.chatDAO.QueryByTenantIDAndID(tenant.TenantID, chatID, "1")
if err != nil {
continue // Try next tenant
}
if len(chats) > 0 {
hasPermission = true
break
}
}
if !hasPermission {
return nil, fmt.Errorf("no authorization")
}
// Step 3: Get chat detail (same as Python DialogService.get_by_id(chat_id))
chat, err := s.chatDAO.GetByID(chatID)
if err != nil {
return nil, fmt.Errorf("chat not found")
}
// Step 4: Build response with kb_names (same as Python _build_chat_response)
// Resolve kb_ids to kb_names
kbNames := s.getKBNames(chat.KBIDs)
// Build dataset_ids from kb_ids (same as Python _resolve_kb_names returns ids)
var datasetIDs []string
for _, kbID := range chat.KBIDs {
datasetID, ok := kbID.(string)
if !ok {
continue
}
datasetIDs = append(datasetIDs, datasetID)
}
return &GetChatResponse{
Chat: chat,
DatasetIDs: datasetIDs,
KBNames: kbNames,
}, nil
}