yao/openapi/chat/reference.go
Max 6714ef9813 Refactor Citation Handling and Enhance Search Result Storage
- Updated the CitationGenerator to produce simple integer IDs instead of formatted strings, improving clarity and consistency in citation references.
- Enhanced the executeAutoSearch method to save both successful and failed search results, capturing detailed execution data for better traceability.
- Introduced a new SearchExecutionResult type to structure search result data, including query, keywords, configuration, duration, and error information.
- Updated related tests to reflect changes in citation ID format and ensure proper functionality of the new storage mechanisms.
- Revised documentation to clarify the new citation format and search result handling processes.
2025-12-15 17:17:51 +08:00

186 lines
5.4 KiB
Go

package chat
import (
"strconv"
"github.com/gin-gonic/gin"
"github.com/yaoapp/yao/agent/assistant"
storetypes "github.com/yaoapp/yao/agent/store/types"
"github.com/yaoapp/yao/openapi/oauth/authorized"
"github.com/yaoapp/yao/openapi/response"
)
// =============================================================================
// Search Reference Handlers
// =============================================================================
// GetReferences retrieves all search references for a request
// GET /v1/chat/references/:request_id
func GetReferences(c *gin.Context) {
// Get chat store
chatStore := assistant.GetChatStore()
if chatStore == nil {
errorResp := &response.ErrorResponse{
Code: response.ErrServerError.Code,
ErrorDescription: "Chat storage not initialized",
}
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
return
}
// Get request ID from URL parameter
requestID := c.Param("request_id")
if requestID == "" {
errorResp := &response.ErrorResponse{
Code: response.ErrInvalidRequest.Code,
ErrorDescription: "Request ID is required",
}
response.RespondWithError(c, response.StatusBadRequest, errorResp)
return
}
// Get all search records for this request
searches, err := chatStore.GetSearches(requestID)
if err != nil {
errorResp := &response.ErrorResponse{
Code: response.ErrServerError.Code,
ErrorDescription: err.Error(),
}
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
return
}
// If no searches found, return empty result
if len(searches) == 0 {
response.RespondWithSuccess(c, response.StatusOK, gin.H{
"request_id": requestID,
"references": []storetypes.Reference{},
"total": 0,
})
return
}
// Get authorized information and check permission using chat_id from first search
authInfo := authorized.GetInfo(c)
chatID := searches[0].ChatID
if chatID != "" {
hasPermission, err := checkChatPermission(chatStore, authInfo, chatID, true)
if err != nil {
errorResp := &response.ErrorResponse{
Code: response.ErrServerError.Code,
ErrorDescription: err.Error(),
}
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
return
}
if !hasPermission {
errorResp := &response.ErrorResponse{
Code: response.ErrAccessDenied.Code,
ErrorDescription: "Forbidden: No permission to access these references",
}
response.RespondWithError(c, response.StatusForbidden, errorResp)
return
}
}
// Collect all references from all searches
var allRefs []storetypes.Reference
for _, search := range searches {
allRefs = append(allRefs, search.References...)
}
response.RespondWithSuccess(c, response.StatusOK, gin.H{
"request_id": requestID,
"references": allRefs,
"total": len(allRefs),
})
}
// GetReference retrieves a single reference by request ID and index
// GET /v1/chat/references/:request_id/:index
func GetReference(c *gin.Context) {
// Get chat store
chatStore := assistant.GetChatStore()
if chatStore == nil {
errorResp := &response.ErrorResponse{
Code: response.ErrServerError.Code,
ErrorDescription: "Chat storage not initialized",
}
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
return
}
// Get request ID from URL parameter
requestID := c.Param("request_id")
if requestID == "" {
errorResp := &response.ErrorResponse{
Code: response.ErrInvalidRequest.Code,
ErrorDescription: "Request ID is required",
}
response.RespondWithError(c, response.StatusBadRequest, errorResp)
return
}
// Get index from URL parameter
indexStr := c.Param("index")
index, err := strconv.Atoi(indexStr)
if err != nil || index < 1 {
errorResp := &response.ErrorResponse{
Code: response.ErrInvalidRequest.Code,
ErrorDescription: "Invalid reference index, must be a positive integer",
}
response.RespondWithError(c, response.StatusBadRequest, errorResp)
return
}
// Get all search records to check permission first
searches, err := chatStore.GetSearches(requestID)
if err != nil {
errorResp := &response.ErrorResponse{
Code: response.ErrServerError.Code,
ErrorDescription: err.Error(),
}
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
return
}
// Check permission using chat_id from first search
if len(searches) > 0 {
authInfo := authorized.GetInfo(c)
chatID := searches[0].ChatID
if chatID != "" {
hasPermission, err := checkChatPermission(chatStore, authInfo, chatID, true)
if err != nil {
errorResp := &response.ErrorResponse{
Code: response.ErrServerError.Code,
ErrorDescription: err.Error(),
}
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
return
}
if !hasPermission {
errorResp := &response.ErrorResponse{
Code: response.ErrAccessDenied.Code,
ErrorDescription: "Forbidden: No permission to access this reference",
}
response.RespondWithError(c, response.StatusForbidden, errorResp)
return
}
}
}
// Get the specific reference
ref, err := chatStore.GetReference(requestID, index)
if err != nil {
errorResp := &response.ErrorResponse{
Code: response.ErrServerError.Code,
ErrorDescription: err.Error(),
}
response.RespondWithError(c, response.StatusNotFound, errorResp)
return
}
response.RespondWithSuccess(c, response.StatusOK, ref)
}