mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-06-29 15:31:05 +08:00
feat: add ResolveReferenceMetadata utility function (#15663)
Add `ResolveReferenceMetadata` to parse `include_metadata` / `metadata_fields` from request and config payloads. ### Changes - **New**: `internal/common/reference_metadata.go` — pure function, zero dependencies - **New**: `internal/common/reference_metadata_test.go` — 8 test cases Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
64
internal/common/reference_metadata.go
Normal file
64
internal/common/reference_metadata.go
Normal file
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package common
|
||||
|
||||
// ResolveReferenceMetadata resolves metadata include/fields from request and
|
||||
// optional config. Request values take precedence over config values.
|
||||
// Supports legacy request keys: include_metadata / metadata_fields.
|
||||
// Python equivalent: api/utils/reference_metadata_utils.py::resolve_reference_metadata_preferences()
|
||||
func ResolveReferenceMetadata(requestPayload, configPayload map[string]interface{}) (bool, []string) {
|
||||
resolved := make(map[string]interface{})
|
||||
|
||||
// Config reference_metadata
|
||||
if configPayload != nil {
|
||||
if cfg, ok := configPayload["reference_metadata"].(map[string]interface{}); ok {
|
||||
for k, v := range cfg {
|
||||
resolved[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Request reference_metadata (overrides config)
|
||||
if requestPayload != nil {
|
||||
if req, ok := requestPayload["reference_metadata"].(map[string]interface{}); ok {
|
||||
for k, v := range req {
|
||||
resolved[k] = v
|
||||
}
|
||||
}
|
||||
// Legacy keys
|
||||
if v, ok := requestPayload["include_metadata"]; ok {
|
||||
resolved["include"] = v
|
||||
}
|
||||
if v, ok := requestPayload["metadata_fields"]; ok {
|
||||
resolved["fields"] = v
|
||||
}
|
||||
}
|
||||
|
||||
include, _ := resolved["include"].(bool)
|
||||
|
||||
rawFields, ok := resolved["fields"].([]interface{})
|
||||
if !ok {
|
||||
return include, nil
|
||||
}
|
||||
var fields []string
|
||||
for _, f := range rawFields {
|
||||
if s, ok := f.(string); ok {
|
||||
fields = append(fields, s)
|
||||
}
|
||||
}
|
||||
return include, fields
|
||||
}
|
||||
142
internal/common/reference_metadata_test.go
Normal file
142
internal/common/reference_metadata_test.go
Normal file
@@ -0,0 +1,142 @@
|
||||
//
|
||||
// Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestResolveReferenceMetadata_NilInputs(t *testing.T) {
|
||||
include, fields := ResolveReferenceMetadata(nil, nil)
|
||||
if include {
|
||||
t.Error("expected include=false")
|
||||
}
|
||||
if fields != nil {
|
||||
t.Errorf("expected nil fields, got %v", fields)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveReferenceMetadata_EmptyInputs(t *testing.T) {
|
||||
include, fields := ResolveReferenceMetadata(map[string]interface{}{}, map[string]interface{}{})
|
||||
if include {
|
||||
t.Error("expected include=false")
|
||||
}
|
||||
if fields != nil {
|
||||
t.Errorf("expected nil fields, got %v", fields)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveReferenceMetadata_FromConfig(t *testing.T) {
|
||||
config := map[string]interface{}{
|
||||
"reference_metadata": map[string]interface{}{
|
||||
"include": true,
|
||||
"fields": []interface{}{"author", "date"},
|
||||
},
|
||||
}
|
||||
include, fields := ResolveReferenceMetadata(nil, config)
|
||||
if !include {
|
||||
t.Error("expected include=true")
|
||||
}
|
||||
if len(fields) != 2 || fields[0] != "author" || fields[1] != "date" {
|
||||
t.Errorf("expected [author date], got %v", fields)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveReferenceMetadata_RequestOverridesConfig(t *testing.T) {
|
||||
config := map[string]interface{}{
|
||||
"reference_metadata": map[string]interface{}{
|
||||
"include": true,
|
||||
"fields": []interface{}{"config_field"},
|
||||
},
|
||||
}
|
||||
request := map[string]interface{}{
|
||||
"reference_metadata": map[string]interface{}{
|
||||
"include": false,
|
||||
"fields": []interface{}{"request_field"},
|
||||
},
|
||||
}
|
||||
include, fields := ResolveReferenceMetadata(request, config)
|
||||
if include {
|
||||
t.Error("expected include=false (request overrides)")
|
||||
}
|
||||
if len(fields) != 1 || fields[0] != "request_field" {
|
||||
t.Errorf("expected [request_field], got %v", fields)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveReferenceMetadata_LegacyKeys(t *testing.T) {
|
||||
request := map[string]interface{}{
|
||||
"include_metadata": true,
|
||||
"metadata_fields": []interface{}{"author", "category"},
|
||||
}
|
||||
include, fields := ResolveReferenceMetadata(request, nil)
|
||||
if !include {
|
||||
t.Error("expected include=true from legacy key")
|
||||
}
|
||||
if len(fields) != 2 || fields[0] != "author" {
|
||||
t.Errorf("expected [author category], got %v", fields)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveReferenceMetadata_LegacyIncludeFalse(t *testing.T) {
|
||||
request := map[string]interface{}{
|
||||
"include_metadata": false,
|
||||
}
|
||||
include, fields := ResolveReferenceMetadata(request, nil)
|
||||
if include {
|
||||
t.Error("expected include=false")
|
||||
}
|
||||
if fields != nil {
|
||||
t.Errorf("expected nil fields, got %v", fields)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveReferenceMetadata_NoFields(t *testing.T) {
|
||||
config := map[string]interface{}{
|
||||
"reference_metadata": map[string]interface{}{
|
||||
"include": true,
|
||||
},
|
||||
}
|
||||
include, fields := ResolveReferenceMetadata(nil, config)
|
||||
if !include {
|
||||
t.Error("expected include=true")
|
||||
}
|
||||
if fields != nil {
|
||||
t.Errorf("expected nil fields, got %v", fields)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveReferenceMetadata_PartialOverride(t *testing.T) {
|
||||
config := map[string]interface{}{
|
||||
"reference_metadata": map[string]interface{}{
|
||||
"include": true,
|
||||
"fields": []interface{}{"from_config"},
|
||||
},
|
||||
}
|
||||
request := map[string]interface{}{
|
||||
"reference_metadata": map[string]interface{}{
|
||||
"include": false, // override include only, fields stays from config
|
||||
},
|
||||
}
|
||||
include, fields := ResolveReferenceMetadata(request, config)
|
||||
if include {
|
||||
t.Error("expected include=false (request overrides)")
|
||||
}
|
||||
if len(fields) != 1 || fields[0] != "from_config" {
|
||||
t.Errorf("expected [from_config], got %v", fields)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user