Merge pull request #1115 from trheyi/main
Refactor API routes for collections and segments management
This commit is contained in:
commit
1714f47c48
6 changed files with 1269 additions and 28 deletions
206
openapi/kb/graph.go
Normal file
206
openapi/kb/graph.go
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
package kb
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yaoapp/yao/kb"
|
||||
"github.com/yaoapp/yao/openapi/response"
|
||||
)
|
||||
|
||||
// Graph Management Handlers
|
||||
|
||||
// GetSegmentGraph gets the graph (entities and relationships) for a specific segment
|
||||
func GetSegmentGraph(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract segmentID from URL path
|
||||
segmentID := c.Param("segmentID")
|
||||
if segmentID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Segment ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if kb.Instance is available
|
||||
if kb.Instance == nil {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrServerError.Code,
|
||||
ErrorDescription: "Knowledge base not initialized",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse query parameters for graph options
|
||||
options := make(map[string]interface{})
|
||||
|
||||
// Include entities (default: true)
|
||||
if includeEntities := c.Query("include_entities"); includeEntities == "false" {
|
||||
options["include_entities"] = false
|
||||
} else {
|
||||
options["include_entities"] = true
|
||||
}
|
||||
|
||||
// Include relationships (default: true)
|
||||
if includeRelationships := c.Query("include_relationships"); includeRelationships == "false" {
|
||||
options["include_relationships"] = false
|
||||
} else {
|
||||
options["include_relationships"] = true
|
||||
}
|
||||
|
||||
// Include metadata (default: true)
|
||||
if includeMetadata := c.Query("include_metadata"); includeMetadata == "false" {
|
||||
options["include_metadata"] = false
|
||||
} else {
|
||||
options["include_metadata"] = true
|
||||
}
|
||||
|
||||
// TODO: Implement document permission validation for docID
|
||||
// TODO: Implement get segment graph logic
|
||||
// TODO: Call kb.Instance.GetSegmentGraph(c.Request.Context(), segmentID, options)
|
||||
|
||||
// Return mock response for now
|
||||
result := gin.H{
|
||||
"entities": []interface{}{},
|
||||
"relationships": []interface{}{},
|
||||
"document_id": docID,
|
||||
"segment_id": segmentID,
|
||||
"options": options,
|
||||
}
|
||||
|
||||
response.RespondWithSuccess(c, response.StatusOK, result)
|
||||
}
|
||||
|
||||
// ExtractSegmentEntities re-extracts entities and relationships for a specific segment (synchronous)
|
||||
func ExtractSegmentEntities(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract segmentID from URL path
|
||||
segmentID := c.Param("segmentID")
|
||||
if segmentID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Segment ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if kb.Instance is available
|
||||
if kb.Instance == nil {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrServerError.Code,
|
||||
ErrorDescription: "Knowledge base not initialized",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse extraction options from request body (optional)
|
||||
var extractOptions map[string]interface{}
|
||||
if err := c.ShouldBindJSON(&extractOptions); err != nil {
|
||||
// If no body provided, use default options
|
||||
extractOptions = make(map[string]interface{})
|
||||
}
|
||||
|
||||
// TODO: Implement document permission validation for docID
|
||||
// TODO: Implement extract segment entities logic
|
||||
// TODO: Call kb.Instance.ExtractSegmentEntities(c.Request.Context(), segmentID, extractOptions)
|
||||
|
||||
// Return mock response for now
|
||||
result := gin.H{
|
||||
"message": "Entities and relationships extracted successfully",
|
||||
"document_id": docID,
|
||||
"segment_id": segmentID,
|
||||
"entities_count": 0,
|
||||
"relationships_count": 0,
|
||||
"extraction_options": extractOptions,
|
||||
}
|
||||
|
||||
response.RespondWithSuccess(c, response.StatusOK, result)
|
||||
}
|
||||
|
||||
// ExtractSegmentEntitiesAsync re-extracts entities and relationships for a specific segment (asynchronous)
|
||||
func ExtractSegmentEntitiesAsync(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract segmentID from URL path
|
||||
segmentID := c.Param("segmentID")
|
||||
if segmentID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Segment ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if kb.Instance is available
|
||||
if kb.Instance == nil {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrServerError.Code,
|
||||
ErrorDescription: "Knowledge base not initialized",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse extraction options from request body (optional)
|
||||
var extractOptions map[string]interface{}
|
||||
if err := c.ShouldBindJSON(&extractOptions); err != nil {
|
||||
// If no body provided, use default options
|
||||
extractOptions = make(map[string]interface{})
|
||||
}
|
||||
|
||||
// TODO: Implement document permission validation for docID
|
||||
|
||||
// Create and run job
|
||||
job := NewJob()
|
||||
jobID := job.Run(func() {
|
||||
// TODO: Implement async extract segment entities logic
|
||||
// err := ExtractSegmentEntitiesProcess(context.Background(), segmentID, extractOptions, job.ID)
|
||||
// For now, just simulate async processing
|
||||
// if err != nil {
|
||||
// log.Error("Async entity extraction failed: %v", err)
|
||||
// }
|
||||
})
|
||||
|
||||
// Return job ID for status tracking
|
||||
result := gin.H{
|
||||
"job_id": jobID,
|
||||
"message": "Entity extraction started",
|
||||
"document_id": docID,
|
||||
"segment_id": segmentID,
|
||||
}
|
||||
|
||||
response.RespondWithSuccess(c, response.StatusCreated, result)
|
||||
}
|
||||
362
openapi/kb/hit.go
Normal file
362
openapi/kb/hit.go
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
package kb
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yaoapp/yao/openapi/response"
|
||||
)
|
||||
|
||||
// Hit Management Handlers
|
||||
|
||||
// ScrollHits scrolls hits with iterator-style pagination for a specific segment
|
||||
func ScrollHits(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract segmentID from URL path
|
||||
segmentID := c.Param("segmentID")
|
||||
if segmentID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Segment ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse query parameters for scroll options
|
||||
options := map[string]interface{}{
|
||||
"document_id": docID,
|
||||
"segment_id": segmentID,
|
||||
"limit": 100, // Default limit
|
||||
}
|
||||
|
||||
// Parse limit (default: 100)
|
||||
if limitStr := c.Query("limit"); limitStr != "" {
|
||||
if limit, err := strconv.Atoi(limitStr); err == nil && limit > 0 {
|
||||
options["limit"] = limit
|
||||
}
|
||||
}
|
||||
|
||||
// Parse scroll_id parameter for continuing pagination
|
||||
if scrollID := strings.TrimSpace(c.Query("scroll_id")); scrollID != "" {
|
||||
options["scroll_id"] = scrollID
|
||||
}
|
||||
|
||||
// Parse order_by parameter
|
||||
if orderBy := strings.TrimSpace(c.Query("order_by")); orderBy != "" {
|
||||
orderByFields := strings.Split(orderBy, ",")
|
||||
// Trim spaces from each field
|
||||
for i, field := range orderByFields {
|
||||
orderByFields[i] = strings.TrimSpace(field)
|
||||
}
|
||||
options["order_by"] = orderByFields
|
||||
}
|
||||
|
||||
// Parse filter parameters
|
||||
filter := make(map[string]interface{})
|
||||
if hitType := c.Query("hit_type"); hitType != "" {
|
||||
filter["hit_type"] = hitType
|
||||
}
|
||||
if userID := c.Query("user_id"); userID != "" {
|
||||
filter["user_id"] = userID
|
||||
}
|
||||
if sessionID := c.Query("session_id"); sessionID != "" {
|
||||
filter["session_id"] = sessionID
|
||||
}
|
||||
if len(filter) > 0 {
|
||||
options["filter"] = filter
|
||||
}
|
||||
|
||||
// TODO: Implement document permission validation for docID
|
||||
// TODO: Implement scroll hits logic with GraphRag or database
|
||||
// TODO: Call kb.Instance.ScrollHits(c.Request.Context(), segmentID, options)
|
||||
|
||||
// Return mock response for now
|
||||
result := gin.H{
|
||||
"hits": []interface{}{},
|
||||
"scroll_id": nil,
|
||||
"has_more": false,
|
||||
"total": 0,
|
||||
"options": options,
|
||||
}
|
||||
|
||||
response.RespondWithSuccess(c, response.StatusOK, result)
|
||||
}
|
||||
|
||||
// GetHits gets hits for a specific segment (simple list, no pagination)
|
||||
func GetHits(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract segmentID from URL path
|
||||
segmentID := c.Param("segmentID")
|
||||
if segmentID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Segment ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse basic filter parameters
|
||||
filter := make(map[string]interface{})
|
||||
if hitType := c.Query("hit_type"); hitType != "" {
|
||||
filter["hit_type"] = hitType
|
||||
}
|
||||
if userID := c.Query("user_id"); userID != "" {
|
||||
filter["user_id"] = userID
|
||||
}
|
||||
if sessionID := c.Query("session_id"); sessionID != "" {
|
||||
filter["session_id"] = sessionID
|
||||
}
|
||||
|
||||
// Parse limit parameter (optional, for basic limiting without pagination)
|
||||
var limit int
|
||||
if limitStr := c.Query("limit"); limitStr != "" {
|
||||
if l, err := strconv.Atoi(limitStr); err == nil && l > 0 {
|
||||
limit = l
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement document permission validation for docID
|
||||
// TODO: Implement get hits logic (simple query without pagination)
|
||||
// TODO: Call kb.Instance.GetHits(c.Request.Context(), segmentID, filter, limit)
|
||||
|
||||
// Return mock response for now
|
||||
result := gin.H{
|
||||
"hits": []interface{}{},
|
||||
"document_id": docID,
|
||||
"segment_id": segmentID,
|
||||
"total": 0,
|
||||
}
|
||||
|
||||
if len(filter) > 0 {
|
||||
result["filter"] = filter
|
||||
}
|
||||
if limit > 0 {
|
||||
result["limit"] = limit
|
||||
}
|
||||
|
||||
response.RespondWithSuccess(c, response.StatusOK, result)
|
||||
}
|
||||
|
||||
// GetHit gets a specific hit by ID
|
||||
func GetHit(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract segmentID from URL path
|
||||
segmentID := c.Param("segmentID")
|
||||
if segmentID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Segment ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract hitID from URL path
|
||||
hitID := c.Param("hitID")
|
||||
if hitID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Hit ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Implement document permission validation for docID
|
||||
// TODO: Implement get hit detail logic
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"hit": nil,
|
||||
"document_id": docID,
|
||||
"segment_id": segmentID,
|
||||
"hit_id": hitID,
|
||||
})
|
||||
}
|
||||
|
||||
// AddHits adds new hits to a segment
|
||||
func AddHits(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract segmentID from URL path
|
||||
segmentID := c.Param("segmentID")
|
||||
if segmentID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Segment ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Implement document permission validation for docID
|
||||
// TODO: Implement add hit logic
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Hit added successfully",
|
||||
"document_id": docID,
|
||||
"segment_id": segmentID,
|
||||
"hit_id": "placeholder-hit-id",
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateHits updates hits in batch
|
||||
func UpdateHits(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract segmentID from URL path
|
||||
segmentID := c.Param("segmentID")
|
||||
if segmentID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Segment ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse hit_ids from query parameter or request body
|
||||
var hitIDs []string
|
||||
|
||||
// Try query parameter first (comma-separated)
|
||||
if hitIDsParam := strings.TrimSpace(c.Query("hit_ids")); hitIDsParam != "" {
|
||||
hitIDs = strings.Split(hitIDsParam, ",")
|
||||
for i, id := range hitIDs {
|
||||
hitIDs[i] = strings.TrimSpace(id)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Also support request body with hit data for batch updates
|
||||
// TODO: Implement document permission validation for docID
|
||||
// TODO: Implement batch update hit logic
|
||||
|
||||
result := gin.H{
|
||||
"message": "Hits updated successfully",
|
||||
"document_id": docID,
|
||||
"segment_id": segmentID,
|
||||
"updated_count": len(hitIDs),
|
||||
}
|
||||
|
||||
if len(hitIDs) > 0 {
|
||||
result["hit_ids"] = hitIDs
|
||||
}
|
||||
|
||||
response.RespondWithSuccess(c, response.StatusOK, result)
|
||||
}
|
||||
|
||||
// RemoveHits removes hits from a segment in batch
|
||||
func RemoveHits(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract segmentID from URL path
|
||||
segmentID := c.Param("segmentID")
|
||||
if segmentID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Segment ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse hit_ids from query parameter (comma-separated)
|
||||
hitIDsParam := strings.TrimSpace(c.Query("hit_ids"))
|
||||
if hitIDsParam == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "hit_ids query parameter is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Split comma-separated hit IDs
|
||||
hitIDs := strings.Split(hitIDsParam, ",")
|
||||
var validHitIDs []string
|
||||
for _, id := range hitIDs {
|
||||
id = strings.TrimSpace(id)
|
||||
if id != "" {
|
||||
validHitIDs = append(validHitIDs, id)
|
||||
}
|
||||
}
|
||||
|
||||
if len(validHitIDs) == 0 {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "At least one valid hit ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Implement document permission validation for docID
|
||||
// TODO: Implement batch remove hit logic
|
||||
|
||||
result := gin.H{
|
||||
"message": "Hits removed successfully",
|
||||
"document_id": docID,
|
||||
"segment_id": segmentID,
|
||||
"hit_ids": validHitIDs,
|
||||
"removed_count": len(validHitIDs),
|
||||
}
|
||||
|
||||
response.RespondWithSuccess(c, response.StatusOK, result)
|
||||
}
|
||||
|
|
@ -20,39 +20,58 @@ func Attach(group *gin.RouterGroup, oauth types.OAuth) {
|
|||
group.Use(oauth.Guard)
|
||||
|
||||
// Collection Management
|
||||
group.POST("/collections", CreateCollection)
|
||||
group.DELETE("/collections/:collectionID", RemoveCollection)
|
||||
group.GET("/collections", GetCollections)
|
||||
group.GET("/collections/:collectionID", GetCollection)
|
||||
group.GET("/collections/:collectionID/exists", CollectionExists)
|
||||
group.GET("/collections", GetCollections)
|
||||
group.POST("/collections", CreateCollection)
|
||||
group.PUT("/collections/:collectionID/metadata", UpdateCollectionMetadata)
|
||||
group.DELETE("/collections/:collectionID", RemoveCollection)
|
||||
|
||||
// Document Management
|
||||
group.GET("/documents", ListDocuments)
|
||||
group.GET("/documents/:docID", GetDocument)
|
||||
group.POST("/collections/:collectionID/documents/file", AddFile)
|
||||
group.POST("/collections/:collectionID/documents/file/async", AddFileAsync)
|
||||
group.POST("/collections/:collectionID/documents/text", AddText)
|
||||
group.POST("/collections/:collectionID/documents/text/async", AddTextAsync)
|
||||
group.POST("/collections/:collectionID/documents/url", AddURL)
|
||||
group.POST("/collections/:collectionID/documents/url/async", AddURLAsync)
|
||||
group.GET("/documents", ListDocuments)
|
||||
group.GET("/documents/:docID", GetDocument)
|
||||
group.DELETE("/documents", RemoveDocs)
|
||||
|
||||
// Segment Management
|
||||
group.POST("/documents/:docID/segments", AddSegments)
|
||||
group.PUT("/documents/:docID/segments", UpdateSegments)
|
||||
group.DELETE("/documents/:docID/segments", RemoveSegmentsByDocID)
|
||||
group.GET("/documents/:docID/segments", ScrollSegments)
|
||||
group.GET("/documents/:docID/segments/search", GetSegments)
|
||||
group.GET("/documents/:docID/segments/:segmentID", GetSegment)
|
||||
group.GET("/documents/:docID/segments/:segmentID/graph", GetSegmentGraph)
|
||||
group.GET("/documents/:docID/segments/:segmentID/parents", GetSegmentParents)
|
||||
group.POST("/documents/:docID/segments", AddSegments)
|
||||
group.POST("/documents/:docID/segments/async", AddSegmentsAsync)
|
||||
group.POST("/documents/:docID/segments/:segmentID/extract", ExtractSegmentEntities)
|
||||
group.POST("/documents/:docID/segments/:segmentID/extract/async", ExtractSegmentEntitiesAsync)
|
||||
group.PUT("/documents/:docID/segments", UpdateSegments)
|
||||
group.PUT("/documents/:docID/segments/async", UpdateSegmentsAsync)
|
||||
group.DELETE("/documents/:docID/segments", RemoveSegments)
|
||||
group.DELETE("/documents/:docID/segments/all", RemoveSegmentsByDocID)
|
||||
|
||||
// Global segment operations (not tied to specific document)
|
||||
group.DELETE("/segments", RemoveSegments)
|
||||
group.GET("/segments", GetSegments)
|
||||
group.GET("/segments/:segmentID", GetSegment)
|
||||
// Segment score and weight management
|
||||
group.PUT("/documents/:docID/segments/:segmentID/score", UpdateScore)
|
||||
group.PUT("/documents/:docID/segments/:segmentID/weight", UpdateWeight)
|
||||
|
||||
// Segment Voting, Scoring, Weighting
|
||||
group.PUT("/segments/vote", UpdateVote)
|
||||
group.PUT("/segments/score", UpdateScore)
|
||||
group.PUT("/segments/weight", UpdateWeight)
|
||||
// Segment votes management
|
||||
group.GET("/documents/:docID/segments/:segmentID/votes", ScrollVotes)
|
||||
group.GET("/documents/:docID/segments/:segmentID/votes/search", GetVotes)
|
||||
group.GET("/documents/:docID/segments/:segmentID/votes/:voteID", GetVote)
|
||||
group.POST("/documents/:docID/segments/:segmentID/votes", AddVotes)
|
||||
group.PUT("/documents/:docID/segments/:segmentID/votes", UpdateVotes)
|
||||
group.DELETE("/documents/:docID/segments/:segmentID/votes", RemoveVotes)
|
||||
|
||||
// Segment hits management
|
||||
group.GET("/documents/:docID/segments/:segmentID/hits", ScrollHits)
|
||||
group.GET("/documents/:docID/segments/:segmentID/hits/search", GetHits)
|
||||
group.GET("/documents/:docID/segments/:segmentID/hits/:hitID", GetHit)
|
||||
group.POST("/documents/:docID/segments/:segmentID/hits", AddHits)
|
||||
group.PUT("/documents/:docID/segments/:segmentID/hits", UpdateHits)
|
||||
group.DELETE("/documents/:docID/segments/:segmentID/hits", RemoveHits)
|
||||
|
||||
// Search Management
|
||||
group.POST("/search", Search)
|
||||
|
|
|
|||
|
|
@ -279,6 +279,17 @@ func UpdateSegments(c *gin.Context) {
|
|||
|
||||
// RemoveSegments removes segments by IDs
|
||||
func RemoveSegments(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse segment_ids from query parameter (comma-separated string)
|
||||
segmentIDsParam := strings.TrimSpace(c.Query("segment_ids"))
|
||||
if segmentIDsParam == "" {
|
||||
|
|
@ -386,14 +397,56 @@ func RemoveSegmentsByDocID(c *gin.Context) {
|
|||
|
||||
// GetSegments gets segments by IDs
|
||||
func GetSegments(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Implement document permission validation for docID
|
||||
// TODO: Implement get segments logic
|
||||
c.JSON(http.StatusOK, gin.H{"segments": []interface{}{}})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"segments": []interface{}{},
|
||||
"document_id": docID,
|
||||
})
|
||||
}
|
||||
|
||||
// GetSegment gets a single segment by ID
|
||||
func GetSegment(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract segmentID from URL path
|
||||
segmentID := c.Param("segmentID")
|
||||
if segmentID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Segment ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Implement document permission validation for docID
|
||||
// TODO: Implement get single segment logic
|
||||
c.JSON(http.StatusOK, gin.H{"segment": nil})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"segment": nil,
|
||||
"document_id": docID,
|
||||
"segment_id": segmentID,
|
||||
})
|
||||
}
|
||||
|
||||
// ScrollSegments scrolls segments with iterator-style pagination
|
||||
|
|
@ -503,3 +556,203 @@ func ScrollSegments(c *gin.Context) {
|
|||
// Return success response
|
||||
response.RespondWithSuccess(c, response.StatusOK, result)
|
||||
}
|
||||
|
||||
// AddSegmentsAsync adds segments to a document asynchronously
|
||||
func AddSegmentsAsync(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if kb.Instance is available
|
||||
if kb.Instance == nil {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrServerError.Code,
|
||||
ErrorDescription: "Knowledge base not initialized",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
var req AddSegmentsRequest
|
||||
|
||||
// Parse and bind JSON request
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Invalid request format: " + err.Error(),
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Set docID from URL parameter
|
||||
req.DocID = docID
|
||||
|
||||
// Validate request
|
||||
if err := req.Validate(); err != nil {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: err.Error(),
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Implement document permission validation for docID
|
||||
|
||||
// Create and run job
|
||||
job := NewJob()
|
||||
jobID := job.Run(func() {
|
||||
// TODO: Implement async add segments logic
|
||||
// This should call the same logic as AddSegments but in background
|
||||
// err := AddSegmentsProcess(context.Background(), &req, job.ID)
|
||||
// For now, just simulate async processing
|
||||
// if err != nil {
|
||||
// log.Error("Async segments addition failed: %v", err)
|
||||
// }
|
||||
})
|
||||
|
||||
// Return job ID for status tracking
|
||||
result := gin.H{
|
||||
"job_id": jobID,
|
||||
"message": "Segments addition started",
|
||||
"document_id": docID,
|
||||
}
|
||||
|
||||
response.RespondWithSuccess(c, response.StatusCreated, result)
|
||||
}
|
||||
|
||||
// UpdateSegmentsAsync updates segments in a document asynchronously
|
||||
func UpdateSegmentsAsync(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if kb.Instance is available
|
||||
if kb.Instance == nil {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrServerError.Code,
|
||||
ErrorDescription: "Knowledge base not initialized",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse request body for segments data
|
||||
var requestBody map[string]interface{}
|
||||
if err := c.ShouldBindJSON(&requestBody); err != nil {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Invalid request format: " + err.Error(),
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Validate request body
|
||||
// TODO: Implement document permission validation for docID
|
||||
|
||||
// Create and run job
|
||||
job := NewJob()
|
||||
jobID := job.Run(func() {
|
||||
// TODO: Implement async update segments logic
|
||||
// This should call the same logic as UpdateSegments but in background
|
||||
// err := UpdateSegmentsProcess(context.Background(), docID, requestBody, job.ID)
|
||||
// For now, just simulate async processing
|
||||
// if err != nil {
|
||||
// log.Error("Async segments update failed: %v", err)
|
||||
// }
|
||||
})
|
||||
|
||||
// Return job ID for status tracking
|
||||
result := gin.H{
|
||||
"job_id": jobID,
|
||||
"message": "Segments update started",
|
||||
"document_id": docID,
|
||||
}
|
||||
|
||||
response.RespondWithSuccess(c, response.StatusCreated, result)
|
||||
}
|
||||
|
||||
// GetSegmentParents gets the parent segments for a specific segment
|
||||
func GetSegmentParents(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract segmentID from URL path
|
||||
segmentID := c.Param("segmentID")
|
||||
if segmentID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Segment ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if kb.Instance is available
|
||||
if kb.Instance == nil {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrServerError.Code,
|
||||
ErrorDescription: "Knowledge base not initialized",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse query parameters for parent options
|
||||
options := make(map[string]interface{})
|
||||
|
||||
// Include metadata (default: true)
|
||||
if includeMetadata := c.Query("include_metadata"); includeMetadata == "false" {
|
||||
options["include_metadata"] = false
|
||||
} else {
|
||||
options["include_metadata"] = true
|
||||
}
|
||||
|
||||
// Depth level (default: 1 - direct parents only)
|
||||
depth := 1
|
||||
if depthStr := c.Query("depth"); depthStr != "" {
|
||||
if d, err := strconv.Atoi(depthStr); err == nil && d > 0 {
|
||||
depth = d
|
||||
}
|
||||
}
|
||||
options["depth"] = depth
|
||||
|
||||
// TODO: Implement document permission validation for docID
|
||||
// TODO: Implement get segment parents logic
|
||||
// TODO: Call kb.Instance.GetSegmentParents(c.Request.Context(), segmentID, options)
|
||||
|
||||
// Return mock response for now
|
||||
result := gin.H{
|
||||
"parents": []interface{}{},
|
||||
"document_id": docID,
|
||||
"segment_id": segmentID,
|
||||
"depth": depth,
|
||||
"total": 0,
|
||||
}
|
||||
|
||||
response.RespondWithSuccess(c, response.StatusOK, result)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,20 +10,63 @@ import (
|
|||
|
||||
// Segment Voting, Scoring, Weighting Handlers
|
||||
|
||||
// UpdateVote updates votes for segments
|
||||
func UpdateVote(c *gin.Context) {
|
||||
// TODO: Implement update vote logic
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Vote updated"})
|
||||
}
|
||||
|
||||
// UpdateScore updates scores for segments
|
||||
// UpdateScore updates score for a specific segment
|
||||
func UpdateScore(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract segmentID from URL path
|
||||
segmentID := c.Param("segmentID")
|
||||
if segmentID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Segment ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Implement document permission validation for docID
|
||||
// TODO: Implement update score logic
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Score updated"})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Score updated successfully",
|
||||
"document_id": docID,
|
||||
"segment_id": segmentID,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateWeight updates weights for segments
|
||||
// UpdateWeight updates weight for a specific segment
|
||||
func UpdateWeight(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract segmentID from URL path
|
||||
segmentID := c.Param("segmentID")
|
||||
if segmentID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Segment ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
var req UpdateWeightRequest
|
||||
|
||||
// Parse and bind JSON request
|
||||
|
|
@ -69,7 +112,9 @@ func UpdateWeight(c *gin.Context) {
|
|||
|
||||
// Return success response
|
||||
result := gin.H{
|
||||
"message": "Segment weights updated successfully",
|
||||
"message": "Segment weight updated successfully",
|
||||
"document_id": docID,
|
||||
"segment_id": segmentID,
|
||||
"segments": req.Segments,
|
||||
"updated_count": updatedCount,
|
||||
}
|
||||
|
|
|
|||
356
openapi/kb/vote.go
Normal file
356
openapi/kb/vote.go
Normal file
|
|
@ -0,0 +1,356 @@
|
|||
package kb
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yaoapp/yao/openapi/response"
|
||||
)
|
||||
|
||||
// Vote Management Handlers
|
||||
|
||||
// ScrollVotes scrolls votes with iterator-style pagination for a specific segment
|
||||
func ScrollVotes(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract segmentID from URL path
|
||||
segmentID := c.Param("segmentID")
|
||||
if segmentID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Segment ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse query parameters for scroll options
|
||||
options := map[string]interface{}{
|
||||
"document_id": docID,
|
||||
"segment_id": segmentID,
|
||||
"limit": 100, // Default limit
|
||||
}
|
||||
|
||||
// Parse limit (default: 100)
|
||||
if limitStr := c.Query("limit"); limitStr != "" {
|
||||
if limit, err := strconv.Atoi(limitStr); err == nil && limit > 0 {
|
||||
options["limit"] = limit
|
||||
}
|
||||
}
|
||||
|
||||
// Parse scroll_id parameter for continuing pagination
|
||||
if scrollID := strings.TrimSpace(c.Query("scroll_id")); scrollID != "" {
|
||||
options["scroll_id"] = scrollID
|
||||
}
|
||||
|
||||
// Parse order_by parameter
|
||||
if orderBy := strings.TrimSpace(c.Query("order_by")); orderBy != "" {
|
||||
orderByFields := strings.Split(orderBy, ",")
|
||||
// Trim spaces from each field
|
||||
for i, field := range orderByFields {
|
||||
orderByFields[i] = strings.TrimSpace(field)
|
||||
}
|
||||
options["order_by"] = orderByFields
|
||||
}
|
||||
|
||||
// Parse filter parameters
|
||||
filter := make(map[string]interface{})
|
||||
if voteType := c.Query("vote_type"); voteType != "" {
|
||||
filter["vote_type"] = voteType
|
||||
}
|
||||
if userID := c.Query("user_id"); userID != "" {
|
||||
filter["user_id"] = userID
|
||||
}
|
||||
if len(filter) > 0 {
|
||||
options["filter"] = filter
|
||||
}
|
||||
|
||||
// TODO: Implement document permission validation for docID
|
||||
// TODO: Implement scroll votes logic with GraphRag or database
|
||||
// TODO: Call kb.Instance.ScrollVotes(c.Request.Context(), segmentID, options)
|
||||
|
||||
// Return mock response for now
|
||||
result := gin.H{
|
||||
"votes": []interface{}{},
|
||||
"scroll_id": nil,
|
||||
"has_more": false,
|
||||
"total": 0,
|
||||
"options": options,
|
||||
}
|
||||
|
||||
response.RespondWithSuccess(c, response.StatusOK, result)
|
||||
}
|
||||
|
||||
// GetVotes gets votes for a specific segment (simple list, no pagination)
|
||||
func GetVotes(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract segmentID from URL path
|
||||
segmentID := c.Param("segmentID")
|
||||
if segmentID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Segment ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse basic filter parameters
|
||||
filter := make(map[string]interface{})
|
||||
if voteType := c.Query("vote_type"); voteType != "" {
|
||||
filter["vote_type"] = voteType
|
||||
}
|
||||
if userID := c.Query("user_id"); userID != "" {
|
||||
filter["user_id"] = userID
|
||||
}
|
||||
|
||||
// Parse limit parameter (optional, for basic limiting without pagination)
|
||||
var limit int
|
||||
if limitStr := c.Query("limit"); limitStr != "" {
|
||||
if l, err := strconv.Atoi(limitStr); err == nil && l > 0 {
|
||||
limit = l
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement document permission validation for docID
|
||||
// TODO: Implement get votes logic (simple query without pagination)
|
||||
// TODO: Call kb.Instance.GetVotes(c.Request.Context(), segmentID, filter, limit)
|
||||
|
||||
// Return mock response for now
|
||||
result := gin.H{
|
||||
"votes": []interface{}{},
|
||||
"document_id": docID,
|
||||
"segment_id": segmentID,
|
||||
"total": 0,
|
||||
}
|
||||
|
||||
if len(filter) > 0 {
|
||||
result["filter"] = filter
|
||||
}
|
||||
if limit > 0 {
|
||||
result["limit"] = limit
|
||||
}
|
||||
|
||||
response.RespondWithSuccess(c, response.StatusOK, result)
|
||||
}
|
||||
|
||||
// GetVote gets a specific vote by ID
|
||||
func GetVote(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract segmentID from URL path
|
||||
segmentID := c.Param("segmentID")
|
||||
if segmentID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Segment ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract voteID from URL path
|
||||
voteID := c.Param("voteID")
|
||||
if voteID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Vote ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Implement document permission validation for docID
|
||||
// TODO: Implement get vote detail logic
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"vote": nil,
|
||||
"document_id": docID,
|
||||
"segment_id": segmentID,
|
||||
"vote_id": voteID,
|
||||
})
|
||||
}
|
||||
|
||||
// AddVotes adds new votes to a segment
|
||||
func AddVotes(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract segmentID from URL path
|
||||
segmentID := c.Param("segmentID")
|
||||
if segmentID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Segment ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Implement document permission validation for docID
|
||||
// TODO: Implement add vote logic
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Vote added successfully",
|
||||
"document_id": docID,
|
||||
"segment_id": segmentID,
|
||||
"vote_id": "placeholder-vote-id",
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateVotes updates votes in batch
|
||||
func UpdateVotes(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract segmentID from URL path
|
||||
segmentID := c.Param("segmentID")
|
||||
if segmentID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Segment ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse vote_ids from query parameter or request body
|
||||
var voteIDs []string
|
||||
|
||||
// Try query parameter first (comma-separated)
|
||||
if voteIDsParam := strings.TrimSpace(c.Query("vote_ids")); voteIDsParam != "" {
|
||||
voteIDs = strings.Split(voteIDsParam, ",")
|
||||
for i, id := range voteIDs {
|
||||
voteIDs[i] = strings.TrimSpace(id)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Also support request body with vote data for batch updates
|
||||
// TODO: Implement document permission validation for docID
|
||||
// TODO: Implement batch update vote logic
|
||||
|
||||
result := gin.H{
|
||||
"message": "Votes updated successfully",
|
||||
"document_id": docID,
|
||||
"segment_id": segmentID,
|
||||
"updated_count": len(voteIDs),
|
||||
}
|
||||
|
||||
if len(voteIDs) > 0 {
|
||||
result["vote_ids"] = voteIDs
|
||||
}
|
||||
|
||||
response.RespondWithSuccess(c, response.StatusOK, result)
|
||||
}
|
||||
|
||||
// RemoveVotes removes votes from a segment in batch
|
||||
func RemoveVotes(c *gin.Context) {
|
||||
// Extract docID from URL path
|
||||
docID := c.Param("docID")
|
||||
if docID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Document ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract segmentID from URL path
|
||||
segmentID := c.Param("segmentID")
|
||||
if segmentID == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "Segment ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse vote_ids from query parameter (comma-separated)
|
||||
voteIDsParam := strings.TrimSpace(c.Query("vote_ids"))
|
||||
if voteIDsParam == "" {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "vote_ids query parameter is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// Split comma-separated vote IDs
|
||||
voteIDs := strings.Split(voteIDsParam, ",")
|
||||
var validVoteIDs []string
|
||||
for _, id := range voteIDs {
|
||||
id = strings.TrimSpace(id)
|
||||
if id != "" {
|
||||
validVoteIDs = append(validVoteIDs, id)
|
||||
}
|
||||
}
|
||||
|
||||
if len(validVoteIDs) == 0 {
|
||||
errorResp := &response.ErrorResponse{
|
||||
Code: response.ErrInvalidRequest.Code,
|
||||
ErrorDescription: "At least one valid vote ID is required",
|
||||
}
|
||||
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Implement document permission validation for docID
|
||||
// TODO: Implement batch remove vote logic
|
||||
|
||||
result := gin.H{
|
||||
"message": "Votes removed successfully",
|
||||
"document_id": docID,
|
||||
"segment_id": segmentID,
|
||||
"vote_ids": validVoteIDs,
|
||||
"removed_count": len(validVoteIDs),
|
||||
}
|
||||
|
||||
response.RespondWithSuccess(c, response.StatusOK, result)
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue