Преглед на файлове

Merge pull request #159 from langgenius/fix/marshal-any-map

feat: add ConvertAnyMap function and corresponding tests for map conv…
Yeuoly преди 6 месеца
родител
ревизия
77f6e756c5
променени са 3 файла, в които са добавени 124 реда и са изтрити 3 реда
  1. 31 0
      internal/utils/mapping/converts.go
  2. 77 0
      internal/utils/mapping/converts_test.go
  3. 16 3
      pkg/entities/plugin_entities/model_declaration.go

+ 31 - 0
internal/utils/mapping/converts.go

@@ -0,0 +1,31 @@
+package mapping
+
+import "fmt"
+
+// ConvertAnyMap converts a map[any]any to a map[string]any
+// please make sure i is a map[any]any
+func ConvertAnyMap(i any) any {
+	switch v := i.(type) {
+	case map[any]any:
+		m2 := make(map[string]any)
+		for k, val := range v {
+			keyStr := fmt.Sprintf("%v", k)
+			m2[keyStr] = ConvertAnyMap(val)
+		}
+		return m2
+	case map[string]any:
+		m2 := make(map[string]any)
+		for k, val := range v {
+			m2[k] = ConvertAnyMap(val)
+		}
+		return m2
+	case []any:
+		m2 := make([]any, len(v))
+		for i, val := range v {
+			m2[i] = ConvertAnyMap(val)
+		}
+		return m2
+	default:
+		return v
+	}
+}

+ 77 - 0
internal/utils/mapping/converts_test.go

@@ -0,0 +1,77 @@
+package mapping
+
+import (
+	"encoding/json"
+	"reflect"
+	"testing"
+)
+
+func TestConvertAnyMap(t *testing.T) {
+	src := map[any]any{
+		"a": 1,
+		"b": 2,
+		"c": map[any]any{
+			"d": 3,
+			"e": 4,
+			"f": []any{
+				1,
+				2,
+				3,
+			},
+		},
+	}
+
+	dst := ConvertAnyMap(src).(map[string]any)
+
+	// use reflect to check key is string
+	keys := reflect.ValueOf(dst).MapKeys()
+	for _, key := range keys {
+		if key.Kind() != reflect.String {
+			t.Errorf("key is not string: %v", key)
+		}
+	}
+
+	c := dst["c"]
+	keys = reflect.ValueOf(c).MapKeys()
+	for _, key := range keys {
+		if key.Kind() != reflect.String {
+			t.Errorf("key is not string: %v", key)
+		}
+	}
+
+	f := dst["c"].(map[string]any)["f"]
+	if _, ok := f.([]any); !ok {
+		t.Errorf("f is not []any: %v", f)
+	}
+
+	if len(f.([]any)) != 3 {
+		t.Errorf("f is not 3: %v", f)
+	}
+
+	if f.([]any)[0] != 1 {
+		t.Errorf("f[0] is not 1: %v", f)
+	}
+}
+
+func TestConvertAnyMap_JsonMarshal(t *testing.T) {
+	src := map[any]any{
+		"a": 1,
+		"b": 2,
+		"c": map[any]any{
+			"d": 3,
+			"e": 4,
+			"f": []any{
+				1,
+				2,
+				3,
+			},
+		},
+	}
+
+	dst := ConvertAnyMap(src).(map[string]any)
+
+	_, err := json.Marshal(dst)
+	if err != nil {
+		t.Errorf("json.Marshal error: %v", err)
+	}
+}

+ 16 - 3
pkg/entities/plugin_entities/model_declaration.go

@@ -8,6 +8,8 @@ import (
 	ut "github.com/go-playground/universal-translator"
 	"github.com/go-playground/validator/v10"
 	en_translations "github.com/go-playground/validator/v10/translations/en"
+	"github.com/langgenius/dify-plugin-daemon/internal/utils/log"
+	"github.com/langgenius/dify-plugin-daemon/internal/utils/mapping"
 	"github.com/langgenius/dify-plugin-daemon/internal/utils/parser"
 	"github.com/langgenius/dify-plugin-daemon/pkg/validators"
 	"github.com/shopspring/decimal"
@@ -407,19 +409,30 @@ func (m *ModelDeclaration) UnmarshalJSON(data []byte) error {
 	return nil
 }
 
-func (m *ModelDeclaration) MarshalJSON() ([]byte, error) {
+func (m ModelDeclaration) MarshalJSON() ([]byte, error) {
 	type alias ModelDeclaration
 
 	temp := &struct {
-		*alias `json:",inline"`
+		alias `json:",inline"`
 	}{
-		alias: (*alias)(m),
+		alias: (alias)(m),
 	}
 
 	if temp.Label.EnUS == "" {
 		temp.Label.EnUS = temp.Model
 	}
 
+	// to avoid ModelProperties not serializable, we need to convert all the keys to string
+	// includes inner map and slice
+	if temp.ModelProperties != nil {
+		result, ok := mapping.ConvertAnyMap(temp.ModelProperties).(map[string]any)
+		if !ok {
+			log.Error("ModelProperties is not a map[string]any", "model_properties", temp.ModelProperties)
+		} else {
+			temp.ModelProperties = result
+		}
+	}
+
 	return json.Marshal(temp)
 }