From faafa4f90eb3014825f60bc53cc1c94d2f2d0f43 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 21 Aug 2025 09:08:14 +0800 Subject: [PATCH] Refactor segment extraction functions and update API routes - Renamed segment extraction functions to better reflect their purpose, changing `ExtractSegmentEntities` to `ExtractSegmentGraph` and `ExtractSegmentEntitiesAsync` to `ExtractSegmentGraphAsync`. - Updated API routes to use the new function names for segment extraction, enhancing clarity and consistency. - Introduced new request structures for batch updates of scores and weights, improving the API's capabilities for segment management. - Removed the outdated `store.go` file to streamline the codebase. --- openapi/kb/graph.go | 20 ++++---- openapi/kb/kb.go | 10 ++-- openapi/kb/score.go | 79 ++++++++++++++++++++++++++++++ openapi/kb/types.go | 26 ++++++++++ openapi/kb/{store.go => weight.go} | 65 ++++-------------------- 5 files changed, 130 insertions(+), 70 deletions(-) create mode 100644 openapi/kb/score.go rename openapi/kb/{store.go => weight.go} (54%) diff --git a/openapi/kb/graph.go b/openapi/kb/graph.go index bf37688b..98241e38 100644 --- a/openapi/kb/graph.go +++ b/openapi/kb/graph.go @@ -82,8 +82,8 @@ func GetSegmentGraph(c *gin.Context) { response.RespondWithSuccess(c, response.StatusOK, result) } -// ExtractSegmentEntities re-extracts entities and relationships for a specific segment (synchronous) -func ExtractSegmentEntities(c *gin.Context) { +// ExtractSegmentGraph re-extracts entities and relationships for a specific segment (synchronous) +func ExtractSegmentGraph(c *gin.Context) { // Extract docID from URL path docID := c.Param("docID") if docID == "" { @@ -124,8 +124,8 @@ func ExtractSegmentEntities(c *gin.Context) { } // TODO: Implement document permission validation for docID - // TODO: Implement extract segment entities logic - // TODO: Call kb.Instance.ExtractSegmentEntities(c.Request.Context(), segmentID, extractOptions) + // TODO: Implement extract segment graph logic + // TODO: Call kb.Instance.ExtractSegmentGraph(c.Request.Context(), segmentID, extractOptions) // Return mock response for now result := gin.H{ @@ -140,8 +140,8 @@ func ExtractSegmentEntities(c *gin.Context) { response.RespondWithSuccess(c, response.StatusOK, result) } -// ExtractSegmentEntitiesAsync re-extracts entities and relationships for a specific segment (asynchronous) -func ExtractSegmentEntitiesAsync(c *gin.Context) { +// ExtractSegmentGraphAsync re-extracts entities and relationships for a specific segment (asynchronous) +func ExtractSegmentGraphAsync(c *gin.Context) { // Extract docID from URL path docID := c.Param("docID") if docID == "" { @@ -186,18 +186,18 @@ func ExtractSegmentEntitiesAsync(c *gin.Context) { // 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) + // TODO: Implement async extract segment graph logic + // err := ExtractSegmentGraphProcess(context.Background(), segmentID, extractOptions, job.ID) // For now, just simulate async processing // if err != nil { - // log.Error("Async entity extraction failed: %v", err) + // log.Error("Async graph extraction failed: %v", err) // } }) // Return job ID for status tracking result := gin.H{ "job_id": jobID, - "message": "Entity extraction started", + "message": "Graph extraction started", "document_id": docID, "segment_id": segmentID, } diff --git a/openapi/kb/kb.go b/openapi/kb/kb.go index b3934630..b03455f4 100644 --- a/openapi/kb/kb.go +++ b/openapi/kb/kb.go @@ -46,16 +46,16 @@ func Attach(group *gin.RouterGroup, oauth types.OAuth) { 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.POST("/documents/:docID/segments/:segmentID/extract", ExtractSegmentGraph) + group.POST("/documents/:docID/segments/:segmentID/extract/async", ExtractSegmentGraphAsync) 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) - // Segment score and weight management - group.PUT("/documents/:docID/segments/:segmentID/score", UpdateScore) - group.PUT("/documents/:docID/segments/:segmentID/weight", UpdateWeight) + // Segment score and weight management (batch operations) + group.PUT("/documents/:docID/segments/scores", UpdateScores) + group.PUT("/documents/:docID/segments/weights", UpdateWeights) // Segment votes management group.GET("/documents/:docID/segments/:segmentID/votes", ScrollVotes) diff --git a/openapi/kb/score.go b/openapi/kb/score.go new file mode 100644 index 00000000..8ef6192a --- /dev/null +++ b/openapi/kb/score.go @@ -0,0 +1,79 @@ +package kb + +import ( + "fmt" + "strings" + + "github.com/gin-gonic/gin" + "github.com/yaoapp/yao/openapi/response" +) + +// Score Management Handlers + +// UpdateScores updates scores for multiple segments in batch +func UpdateScores(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 request body for batch score updates + var req UpdateScoresRequest + 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 + } + + // Validate request + if len(req.Scores) == 0 { + errorResp := &response.ErrorResponse{ + Code: response.ErrInvalidRequest.Code, + ErrorDescription: "At least one score update is required", + } + response.RespondWithError(c, response.StatusBadRequest, errorResp) + return + } + + // Validate each score entry + for i, score := range req.Scores { + if strings.TrimSpace(score.ID) == "" { + errorResp := &response.ErrorResponse{ + Code: response.ErrInvalidRequest.Code, + ErrorDescription: fmt.Sprintf("scores[%d].id is required", i), + } + response.RespondWithError(c, response.StatusBadRequest, errorResp) + return + } + if score.Score < 0 { + errorResp := &response.ErrorResponse{ + Code: response.ErrInvalidRequest.Code, + ErrorDescription: fmt.Sprintf("scores[%d].score cannot be negative", i), + } + response.RespondWithError(c, response.StatusBadRequest, errorResp) + return + } + } + + // TODO: Implement document permission validation for docID + // TODO: Implement batch update scores logic + // TODO: Call kb.Instance.UpdateScores(c.Request.Context(), docID, req.Scores) + + result := gin.H{ + "message": "Scores updated successfully", + "document_id": docID, + "scores": req.Scores, + "updated_count": len(req.Scores), + } + + response.RespondWithSuccess(c, response.StatusOK, result) +} diff --git a/openapi/kb/types.go b/openapi/kb/types.go index 4bbe3432..783d5f4b 100644 --- a/openapi/kb/types.go +++ b/openapi/kb/types.go @@ -144,6 +144,16 @@ type UpdateWeightRequest struct { Segments []types.SegmentWeight `json:"segments" binding:"required"` } +// UpdateScoresRequest represents the request for batch score updates +type UpdateScoresRequest struct { + Scores []types.SegmentScore `json:"scores" binding:"required"` +} + +// UpdateWeightsRequest represents the request for batch weight updates +type UpdateWeightsRequest struct { + Weights []types.SegmentWeight `json:"weights" binding:"required"` +} + // ProviderOption resolves a ProviderConfig to a *kbtypes.ProviderOption // If OptionID is provided, it looks up the option from the provider // If Option is provided directly, it uses the Option field @@ -418,6 +428,22 @@ func (r *UpdateWeightRequest) Validate() error { return nil } +// Validate validates the UpdateWeightsRequest fields +func (r *UpdateWeightsRequest) Validate() error { + if len(r.Weights) == 0 { + return fmt.Errorf("weights is required") + } + for i, weight := range r.Weights { + if strings.TrimSpace(weight.ID) == "" { + return fmt.Errorf("weights[%d].id cannot be empty", i) + } + if weight.Weight < 0 { + return fmt.Errorf("weights[%d].weight cannot be negative", i) + } + } + return nil +} + // AddBaseFields adds common fields from BaseUpsertRequest to data map func (r *BaseUpsertRequest) AddBaseFields(data map[string]interface{}) { if r.Locale != "" { diff --git a/openapi/kb/store.go b/openapi/kb/weight.go similarity index 54% rename from openapi/kb/store.go rename to openapi/kb/weight.go index 99bd0939..9c21ca5d 100644 --- a/openapi/kb/store.go +++ b/openapi/kb/weight.go @@ -1,17 +1,15 @@ package kb import ( - "net/http" - "github.com/gin-gonic/gin" "github.com/yaoapp/yao/kb" "github.com/yaoapp/yao/openapi/response" ) -// Segment Voting, Scoring, Weighting Handlers +// Weight Management Handlers -// UpdateScore updates score for a specific segment -func UpdateScore(c *gin.Context) { +// UpdateWeights updates weights for multiple segments in batch +func UpdateWeights(c *gin.Context) { // Extract docID from URL path docID := c.Param("docID") if docID == "" { @@ -23,51 +21,7 @@ func UpdateScore(c *gin.Context) { 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 successfully", - "document_id": docID, - "segment_id": segmentID, - }) -} - -// 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 + var req UpdateWeightsRequest // Parse and bind JSON request if err := c.ShouldBindJSON(&req); err != nil { @@ -99,8 +53,10 @@ func UpdateWeight(c *gin.Context) { return } - // Perform update weight operation - updatedCount, err := kb.Instance.UpdateWeight(c.Request.Context(), req.Segments) + // TODO: Implement document permission validation for docID + + // Perform batch update weight operation + updatedCount, err := kb.Instance.UpdateWeight(c.Request.Context(), req.Weights) if err != nil { errorResp := &response.ErrorResponse{ Code: response.ErrServerError.Code, @@ -112,10 +68,9 @@ func UpdateWeight(c *gin.Context) { // Return success response result := gin.H{ - "message": "Segment weight updated successfully", + "message": "Segment weights updated successfully", "document_id": docID, - "segment_id": segmentID, - "segments": req.Segments, + "weights": req.Weights, "updated_count": updatedCount, }