c5228acbab
移除会话策略和代理配置相关字段,仅保留标题和描述。会话现在作为独立的对话容器,配置由自定义代理在查询时动态提供。
300 lines
9.4 KiB
Go
300 lines
9.4 KiB
Go
package session
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/Tencent/WeKnora/internal/config"
|
|
"github.com/Tencent/WeKnora/internal/errors"
|
|
"github.com/Tencent/WeKnora/internal/logger"
|
|
"github.com/Tencent/WeKnora/internal/types"
|
|
"github.com/Tencent/WeKnora/internal/types/interfaces"
|
|
secutils "github.com/Tencent/WeKnora/internal/utils"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// Handler handles all HTTP requests related to conversation sessions
|
|
type Handler struct {
|
|
messageService interfaces.MessageService // Service for managing messages
|
|
sessionService interfaces.SessionService // Service for managing sessions
|
|
streamManager interfaces.StreamManager // Manager for handling streaming responses
|
|
config *config.Config // Application configuration
|
|
knowledgebaseService interfaces.KnowledgeBaseService // Service for managing knowledge bases
|
|
customAgentService interfaces.CustomAgentService // Service for managing custom agents
|
|
}
|
|
|
|
// NewHandler creates a new instance of Handler with all necessary dependencies
|
|
func NewHandler(
|
|
sessionService interfaces.SessionService,
|
|
messageService interfaces.MessageService,
|
|
streamManager interfaces.StreamManager,
|
|
config *config.Config,
|
|
knowledgebaseService interfaces.KnowledgeBaseService,
|
|
customAgentService interfaces.CustomAgentService,
|
|
) *Handler {
|
|
return &Handler{
|
|
sessionService: sessionService,
|
|
messageService: messageService,
|
|
streamManager: streamManager,
|
|
config: config,
|
|
knowledgebaseService: knowledgebaseService,
|
|
customAgentService: customAgentService,
|
|
}
|
|
}
|
|
|
|
// CreateSession godoc
|
|
// @Summary 创建会话
|
|
// @Description 创建新的对话会话
|
|
// @Tags 会话
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param request body CreateSessionRequest true "会话创建请求"
|
|
// @Success 201 {object} map[string]interface{} "创建的会话"
|
|
// @Failure 400 {object} errors.AppError "请求参数错误"
|
|
// @Security Bearer
|
|
// @Security ApiKeyAuth
|
|
// @Router /sessions [post]
|
|
func (h *Handler) CreateSession(c *gin.Context) {
|
|
ctx := c.Request.Context()
|
|
// Parse and validate the request body
|
|
var request CreateSessionRequest
|
|
if err := c.ShouldBindJSON(&request); err != nil {
|
|
logger.Error(ctx, "Failed to validate session creation parameters", err)
|
|
c.Error(errors.NewBadRequestError(err.Error()))
|
|
return
|
|
}
|
|
|
|
// Get tenant ID from context
|
|
tenantID, exists := c.Get(types.TenantIDContextKey.String())
|
|
if !exists {
|
|
logger.Error(ctx, "Failed to get tenant ID")
|
|
c.Error(errors.NewUnauthorizedError("Unauthorized"))
|
|
return
|
|
}
|
|
|
|
// Sessions are now knowledge-base-independent:
|
|
// - All configuration comes from custom agent at query time
|
|
// - Session only stores basic info (tenant ID, title, description)
|
|
logger.Infof(
|
|
ctx,
|
|
"Processing session creation request, tenant ID: %d",
|
|
tenantID,
|
|
)
|
|
|
|
// Create session object with base properties
|
|
createdSession := &types.Session{
|
|
TenantID: tenantID.(uint64),
|
|
Title: request.Title,
|
|
Description: request.Description,
|
|
}
|
|
|
|
// Call service to create session
|
|
logger.Infof(ctx, "Calling session service to create session")
|
|
createdSession, err := h.sessionService.CreateSession(ctx, createdSession)
|
|
if err != nil {
|
|
logger.ErrorWithFields(ctx, err, nil)
|
|
c.Error(errors.NewInternalServerError(err.Error()))
|
|
return
|
|
}
|
|
|
|
// Return created session
|
|
logger.Infof(ctx, "Session created successfully, ID: %s", createdSession.ID)
|
|
c.JSON(http.StatusCreated, gin.H{
|
|
"success": true,
|
|
"data": createdSession,
|
|
})
|
|
}
|
|
|
|
// GetSession godoc
|
|
// @Summary 获取会话详情
|
|
// @Description 根据ID获取会话详情
|
|
// @Tags 会话
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param id path string true "会话ID"
|
|
// @Success 200 {object} map[string]interface{} "会话详情"
|
|
// @Failure 404 {object} errors.AppError "会话不存在"
|
|
// @Security Bearer
|
|
// @Security ApiKeyAuth
|
|
// @Router /sessions/{id} [get]
|
|
func (h *Handler) GetSession(c *gin.Context) {
|
|
ctx := c.Request.Context()
|
|
|
|
logger.Info(ctx, "Start retrieving session")
|
|
|
|
// Get session ID from URL parameter
|
|
id := secutils.SanitizeForLog(c.Param("id"))
|
|
if id == "" {
|
|
logger.Error(ctx, "Session ID is empty")
|
|
c.Error(errors.NewBadRequestError(errors.ErrInvalidSessionID.Error()))
|
|
return
|
|
}
|
|
|
|
// Call service to get session details
|
|
logger.Infof(ctx, "Retrieving session, ID: %s", id)
|
|
session, err := h.sessionService.GetSession(ctx, id)
|
|
if err != nil {
|
|
if err == errors.ErrSessionNotFound {
|
|
logger.Warnf(ctx, "Session not found, ID: %s", id)
|
|
c.Error(errors.NewNotFoundError(err.Error()))
|
|
return
|
|
}
|
|
logger.ErrorWithFields(ctx, err, nil)
|
|
c.Error(errors.NewInternalServerError(err.Error()))
|
|
return
|
|
}
|
|
|
|
// Return session data
|
|
logger.Infof(ctx, "Session retrieved successfully, ID: %s", id)
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"data": session,
|
|
})
|
|
}
|
|
|
|
// GetSessionsByTenant godoc
|
|
// @Summary 获取会话列表
|
|
// @Description 获取当前租户的会话列表,支持分页
|
|
// @Tags 会话
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param page query int false "页码"
|
|
// @Param page_size query int false "每页数量"
|
|
// @Success 200 {object} map[string]interface{} "会话列表"
|
|
// @Failure 400 {object} errors.AppError "请求参数错误"
|
|
// @Security Bearer
|
|
// @Security ApiKeyAuth
|
|
// @Router /sessions [get]
|
|
func (h *Handler) GetSessionsByTenant(c *gin.Context) {
|
|
ctx := c.Request.Context()
|
|
|
|
// Parse pagination parameters from query
|
|
var pagination types.Pagination
|
|
if err := c.ShouldBindQuery(&pagination); err != nil {
|
|
logger.Error(ctx, "Failed to parse pagination parameters", err)
|
|
c.Error(errors.NewBadRequestError(err.Error()))
|
|
return
|
|
}
|
|
|
|
// Use paginated query to get sessions
|
|
result, err := h.sessionService.GetPagedSessionsByTenant(ctx, &pagination)
|
|
if err != nil {
|
|
logger.ErrorWithFields(ctx, err, nil)
|
|
c.Error(errors.NewInternalServerError(err.Error()))
|
|
return
|
|
}
|
|
|
|
// Return sessions with pagination data
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"data": result.Data,
|
|
"total": result.Total,
|
|
"page": result.Page,
|
|
"page_size": result.PageSize,
|
|
})
|
|
}
|
|
|
|
// UpdateSession godoc
|
|
// @Summary 更新会话
|
|
// @Description 更新会话属性
|
|
// @Tags 会话
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param id path string true "会话ID"
|
|
// @Param request body types.Session true "会话信息"
|
|
// @Success 200 {object} map[string]interface{} "更新后的会话"
|
|
// @Failure 404 {object} errors.AppError "会话不存在"
|
|
// @Security Bearer
|
|
// @Security ApiKeyAuth
|
|
// @Router /sessions/{id} [put]
|
|
func (h *Handler) UpdateSession(c *gin.Context) {
|
|
ctx := c.Request.Context()
|
|
|
|
// Get session ID from URL parameter
|
|
id := secutils.SanitizeForLog(c.Param("id"))
|
|
if id == "" {
|
|
logger.Error(ctx, "Session ID is empty")
|
|
c.Error(errors.NewBadRequestError(errors.ErrInvalidSessionID.Error()))
|
|
return
|
|
}
|
|
|
|
// Verify tenant ID from context for authorization
|
|
tenantID, exists := c.Get(types.TenantIDContextKey.String())
|
|
if !exists {
|
|
logger.Error(ctx, "Failed to get tenant ID")
|
|
c.Error(errors.NewUnauthorizedError("Unauthorized"))
|
|
return
|
|
}
|
|
|
|
// Parse request body to session object
|
|
var session types.Session
|
|
if err := c.ShouldBindJSON(&session); err != nil {
|
|
logger.Error(ctx, "Failed to parse session data", err)
|
|
c.Error(errors.NewBadRequestError(err.Error()))
|
|
return
|
|
}
|
|
|
|
session.ID = id
|
|
session.TenantID = tenantID.(uint64)
|
|
|
|
// Call service to update session
|
|
if err := h.sessionService.UpdateSession(ctx, &session); err != nil {
|
|
if err == errors.ErrSessionNotFound {
|
|
logger.Warnf(ctx, "Session not found, ID: %s", id)
|
|
c.Error(errors.NewNotFoundError(err.Error()))
|
|
return
|
|
}
|
|
logger.ErrorWithFields(ctx, err, nil)
|
|
c.Error(errors.NewInternalServerError(err.Error()))
|
|
return
|
|
}
|
|
|
|
// Return updated session
|
|
logger.Infof(ctx, "Session updated successfully, ID: %s", id)
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"data": session,
|
|
})
|
|
}
|
|
|
|
// DeleteSession godoc
|
|
// @Summary 删除会话
|
|
// @Description 删除指定的会话
|
|
// @Tags 会话
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param id path string true "会话ID"
|
|
// @Success 200 {object} map[string]interface{} "删除成功"
|
|
// @Failure 404 {object} errors.AppError "会话不存在"
|
|
// @Security Bearer
|
|
// @Security ApiKeyAuth
|
|
// @Router /sessions/{id} [delete]
|
|
func (h *Handler) DeleteSession(c *gin.Context) {
|
|
ctx := c.Request.Context()
|
|
|
|
// Get session ID from URL parameter
|
|
id := secutils.SanitizeForLog(c.Param("id"))
|
|
if id == "" {
|
|
logger.Error(ctx, "Session ID is empty")
|
|
c.Error(errors.NewBadRequestError(errors.ErrInvalidSessionID.Error()))
|
|
return
|
|
}
|
|
|
|
// Call service to delete session
|
|
if err := h.sessionService.DeleteSession(ctx, id); err != nil {
|
|
if err == errors.ErrSessionNotFound {
|
|
logger.Warnf(ctx, "Session not found, ID: %s", id)
|
|
c.Error(errors.NewNotFoundError(err.Error()))
|
|
return
|
|
}
|
|
logger.ErrorWithFields(ctx, err, nil)
|
|
c.Error(errors.NewInternalServerError(err.Error()))
|
|
return
|
|
}
|
|
|
|
// Return success message
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"message": "Session deleted successfully",
|
|
})
|
|
}
|