Fix Tag weight should be greater than 0 (#16657)

This commit is contained in:
Wang Qi
2026-07-06 16:06:27 +08:00
committed by GitHub
parent 290ab557a5
commit 57625c919a
7 changed files with 147 additions and 14 deletions

View File

@@ -76,10 +76,10 @@ def validate_tag_features(raw):
if not key:
raise ValueError("keys must be non-empty strings")
if isinstance(value, bool) or not isinstance(value, (int, float)):
raise ValueError("values must be finite numbers")
raise ValueError("values must be finite numbers greater than 0")
numeric = float(value)
if not math.isfinite(numeric):
raise ValueError("values must be finite numbers")
if not math.isfinite(numeric) or numeric <= 0:
raise ValueError("values must be finite numbers greater than 0")
cleaned[key] = numeric
return cleaned

View File

@@ -719,6 +719,19 @@ func (h *ChunkHandler) UpdateChunk(c *gin.Context) {
err := h.chunkService.UpdateChunk(&req, user.ID)
if err != nil {
var coded interface {
Code() common.ErrorCode
}
if errors.As(err, &coded) {
switch coded.Code() {
case common.CodeArgumentError, common.CodeBadRequest, common.CodeDataError:
c.JSON(http.StatusBadRequest, gin.H{
"code": coded.Code(),
"message": err.Error(),
})
return
}
}
c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"message": err.Error(),

View File

@@ -27,6 +27,19 @@ type mockChunkSvc struct {
stopParsingFn func(userID, datasetID string, req service.StopParsingRequest) (*service.StopParsingResponse, common.ErrorCode, error)
}
type codedTestError struct {
code common.ErrorCode
msg string
}
func (e codedTestError) Error() string {
return e.msg
}
func (e codedTestError) Code() common.ErrorCode {
return e.code
}
func (m *mockChunkSvc) RetrievalTest(req *service.RetrievalTestRequest, userID string) (*service.RetrievalTestResponse, error) {
if m.retrievalTestFn != nil {
return m.retrievalTestFn(req, userID)
@@ -276,6 +289,25 @@ func TestChunkHandlerUpdateChunkUsesPathIDs(t *testing.T) {
}
}
func TestChunkHandlerUpdateChunkValidationErrorIsBadRequest(t *testing.T) {
mock := &mockChunkSvc{}
r, h := setupChunkHandlerWithUser("user-1", mock)
r.PATCH("/api/v1/datasets/:dataset_id/documents/:document_id/chunks/:chunk_id", h.UpdateChunk)
mock.updateChunkFn = func(req *service.UpdateChunkRequest, userID string) error {
return codedTestError{code: common.CodeArgumentError, msg: "`tag_feas` values must be finite numbers greater than 0"}
}
req := httptest.NewRequest(http.MethodPatch, "/api/v1/datasets/kb-1/documents/doc-1/chunks/chunk-1", strings.NewReader(`{"tag_feas":{"tag":0}}`))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Fatalf("status = %d, body = %s", w.Code, w.Body.String())
}
}
func TestChunkRetrieval_EmptyQuestion(t *testing.T) {
r, _ := setupChunkRetrievalTest("user1")

View File

@@ -1682,7 +1682,11 @@ func (s *ChunkService) UpdateChunk(req *service.UpdateChunkRequest, userID strin
// Tag features
if req.TagFeas != nil {
d["tag_feas"] = req.TagFeas
tagFeas, err := validateTagFeatures(req.TagFeas)
if err != nil {
return updateChunkError{code: common.CodeArgumentError, message: "`tag_feas` " + err.Error()}
}
d["tag_feas"] = tagFeas
}
// Always include id
@@ -1932,6 +1936,19 @@ type addChunkError struct {
message string
}
type updateChunkError struct {
code common.ErrorCode
message string
}
func (e updateChunkError) Error() string {
return e.message
}
func (e updateChunkError) Code() common.ErrorCode {
return e.code
}
func (e addChunkError) Error() string {
return e.message
}
@@ -1953,27 +1970,42 @@ func validateTagFeatures(raw interface{}) (map[string]float64, error) {
}
switch typed := value.(type) {
case float64:
if math.IsNaN(typed) || math.IsInf(typed, 0) {
return nil, fmt.Errorf("values must be finite numbers")
if math.IsNaN(typed) || math.IsInf(typed, 0) || typed <= 0 {
return nil, fmt.Errorf("values must be finite numbers greater than 0")
}
cleaned[key] = typed
case float32:
if math.IsNaN(float64(typed)) || math.IsInf(float64(typed), 0) {
return nil, fmt.Errorf("values must be finite numbers")
if math.IsNaN(float64(typed)) || math.IsInf(float64(typed), 0) || typed <= 0 {
return nil, fmt.Errorf("values must be finite numbers greater than 0")
}
cleaned[key] = float64(typed)
case int:
if typed <= 0 {
return nil, fmt.Errorf("values must be finite numbers greater than 0")
}
cleaned[key] = float64(typed)
case int8:
if typed <= 0 {
return nil, fmt.Errorf("values must be finite numbers greater than 0")
}
cleaned[key] = float64(typed)
case int16:
if typed <= 0 {
return nil, fmt.Errorf("values must be finite numbers greater than 0")
}
cleaned[key] = float64(typed)
case int32:
if typed <= 0 {
return nil, fmt.Errorf("values must be finite numbers greater than 0")
}
cleaned[key] = float64(typed)
case int64:
if typed <= 0 {
return nil, fmt.Errorf("values must be finite numbers greater than 0")
}
cleaned[key] = float64(typed)
default:
return nil, fmt.Errorf("values must be finite numbers")
return nil, fmt.Errorf("values must be finite numbers greater than 0")
}
}
return cleaned, nil

View File

@@ -20,6 +20,7 @@ import (
"context"
"encoding/json"
"fmt"
"math"
"ragflow/internal/common"
"ragflow/internal/engine/redis"
"ragflow/internal/entity"
@@ -636,7 +637,11 @@ func (s *ChunkService) UpdateChunk(req *UpdateChunkRequest, userID string) error
// Tag features
if req.TagFeas != nil {
d["tag_feas"] = req.TagFeas
tagFeas, err := validateUpdateTagFeatures(req.TagFeas)
if err != nil {
return updateChunkError{code: common.CodeArgumentError, message: "`tag_feas` " + err.Error()}
}
d["tag_feas"] = tagFeas
}
// Always include id
@@ -655,6 +660,57 @@ func (s *ChunkService) UpdateChunk(req *UpdateChunkRequest, userID string) error
return nil
}
type updateChunkError struct {
code common.ErrorCode
message string
}
func (e updateChunkError) Error() string {
return e.message
}
func (e updateChunkError) Code() common.ErrorCode {
return e.code
}
func validateUpdateTagFeatures(raw interface{}) (map[string]float64, error) {
parsed, ok := raw.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("must be an object mapping string tags to finite numeric scores greater than 0")
}
cleaned := make(map[string]float64, len(parsed))
for key, value := range parsed {
key = strings.TrimSpace(key)
if key == "" {
return nil, fmt.Errorf("keys must be non-empty strings")
}
var numeric float64
switch typed := value.(type) {
case float64:
numeric = typed
case float32:
numeric = float64(typed)
case int:
numeric = float64(typed)
case int8:
numeric = float64(typed)
case int16:
numeric = float64(typed)
case int32:
numeric = float64(typed)
case int64:
numeric = float64(typed)
default:
return nil, fmt.Errorf("values must be finite numbers greater than 0")
}
if math.IsNaN(numeric) || math.IsInf(numeric, 0) || numeric <= 0 {
return nil, fmt.Errorf("values must be finite numbers greater than 0")
}
cleaned[key] = numeric
}
return cleaned, nil
}
// RemoveChunksRequest request for removing chunks
type RemoveChunksRequest struct {
DocID string `json:"doc_id"`

View File

@@ -107,7 +107,7 @@ export const TagFeatureItem = () => {
'knowledgeConfiguration.frequency',
)}
max={10}
min={0}
min={1}
/>
</FormControl>
<FormMessage />
@@ -125,7 +125,7 @@ export const TagFeatureItem = () => {
<Button
variant="dashed"
className="w-full flex items-center justify-center gap-2"
onClick={() => append({ tag: '', frequency: 0 })}
onClick={() => append({ tag: '', frequency: 1 })}
>
<Plus size={16} />
{t('knowledgeConfiguration.addTag')}

View File

@@ -107,7 +107,7 @@ export const TagFeatureItem = () => {
'knowledgeConfiguration.frequency',
)}
max={10}
min={0}
min={1}
/>
</FormControl>
<FormMessage />
@@ -125,7 +125,7 @@ export const TagFeatureItem = () => {
<Button
variant="dashed"
className="w-full flex items-center justify-center gap-2"
onClick={() => append({ tag: '', frequency: 0 })}
onClick={() => append({ tag: '', frequency: 1 })}
>
<Plus size={16} />
{t('knowledgeConfiguration.addTag')}