Skip to content

Commit 6b6c361

Browse files
authored
feat: support using JSONSchema to describe tool parameters (cloudwego#402)
1 parent 0cb10ff commit 6b6c361

File tree

7 files changed

+483
-153
lines changed

7 files changed

+483
-153
lines changed

components/tool/utils/create_options.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ func WithMarshalOutput(m MarshalOutput) Option {
5858
}
5959
}
6060

61+
// Deprecated. For more information, see https://github.com/cloudwego/eino/discussions/397.
6162
// SchemaCustomizerFn is the schema customizer function for inferring tool parameter from tagged go struct.
6263
// Within this function, end-user can parse custom go struct tags into corresponding openapi schema field.
6364
// Parameters:
@@ -67,6 +68,7 @@ func WithMarshalOutput(m MarshalOutput) Option {
6768
// 4. schema: the current openapi schema object to be customized.
6869
type SchemaCustomizerFn func(name string, t reflect.Type, tag reflect.StructTag, schema *openapi3.Schema) error
6970

71+
// Deprecated. For more information, see https://github.com/cloudwego/eino/discussions/397.
7072
// WithSchemaCustomizer sets a user-defined schema customizer for inferring tool parameter from tagged go struct.
7173
// If this option is not set, the defaultSchemaCustomizer will be used.
7274
func WithSchemaCustomizer(sc SchemaCustomizerFn) Option {
@@ -86,6 +88,7 @@ func getToolOptions(opt ...Option) *toolOptions {
8688
return opts
8789
}
8890

91+
// Deprecated. For more information, see https://github.com/cloudwego/eino/discussions/397.
8992
// defaultSchemaCustomizer is the default schema customizer when using reflect to infer tool parameter from tagged go struct.
9093
// Supported struct tags:
9194
// 1. jsonschema: "description=xxx"

components/tool/utils/invokable_func.go

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222
"strings"
2323

2424
"github.com/bytedance/sonic"
25-
"github.com/getkin/kin-openapi/openapi3gen"
25+
"github.com/eino-contrib/jsonschema"
2626

2727
"github.com/cloudwego/eino/components/tool"
2828
"github.com/cloudwego/eino/internal/generic"
@@ -80,22 +80,68 @@ func goStruct2ToolInfo[T any](toolName, toolDesc string, opts ...Option) (*schem
8080
}, nil
8181
}
8282

83-
func goStruct2ParamsOneOf[T any](opts ...Option) (*schema.ParamsOneOf, error) {
84-
options := getToolOptions(opts...)
85-
schemaCustomizer := defaultSchemaCustomizer
86-
if options.sc != nil {
87-
schemaCustomizer = options.sc
88-
}
83+
func goStruct2ParamsOneOf[T any](_ ...Option) (*schema.ParamsOneOf, error) {
84+
s := jsonschema.Reflect(generic.NewInstance[T]())
8985

90-
sc, err := openapi3gen.NewSchemaRefForValue(generic.NewInstance[T](), nil, openapi3gen.SchemaCustomizer(schemaCustomizer))
91-
if err != nil {
92-
return nil, fmt.Errorf("new SchemaRef from T failed: %w", err)
86+
rootName := strings.TrimPrefix(s.Ref, "#/$defs/")
87+
rootSchema := s.Definitions[rootName]
88+
if rootSchema == nil {
89+
return nil, fmt.Errorf("jsonschema '%s' not found", rootName)
9390
}
9491

95-
paramsOneOf := schema.NewParamsOneOfByOpenAPIV3(sc.Value)
92+
rootSchema = resolveRef(rootSchema, s.Definitions)
93+
paramsOneOf := schema.NewParamsOneOfByJSONSchema(rootSchema)
9694

9795
return paramsOneOf, nil
96+
}
97+
98+
func resolveRef(s *jsonschema.Schema, defs jsonschema.Definitions) *jsonschema.Schema {
99+
if s == nil {
100+
return nil
101+
}
102+
103+
if s.Ref != "" {
104+
if def, ok := defs[s.Ref[len("#/$defs/"):]]; ok {
105+
s.Ref = "" // Clear the ref after resolution
106+
return resolveRef(def, defs)
107+
}
108+
}
109+
110+
for i, s_ := range s.AllOf {
111+
s.AllOf[i] = resolveRef(s_, defs)
112+
}
113+
for i, s_ := range s.AnyOf {
114+
s.AnyOf[i] = resolveRef(s_, defs)
115+
}
116+
for i, s_ := range s.OneOf {
117+
s.OneOf[i] = resolveRef(s_, defs)
118+
}
119+
for i, s_ := range s.DependentSchemas {
120+
s.DependentSchemas[i] = resolveRef(s_, defs)
121+
}
122+
for i, s_ := range s.PrefixItems {
123+
s.PrefixItems[i] = resolveRef(s_, defs)
124+
}
125+
for i, s_ := range s.PatternProperties {
126+
s.PatternProperties[i] = resolveRef(s_, defs)
127+
}
128+
if s.Properties != nil {
129+
for pair := s.Properties.Oldest(); pair != nil; pair = pair.Next() {
130+
s.Properties.Set(pair.Key, resolveRef(pair.Value, defs))
131+
}
132+
}
98133

134+
s.Not = resolveRef(s.Not, defs)
135+
s.If = resolveRef(s.If, defs)
136+
s.Then = resolveRef(s.Then, defs)
137+
s.Else = resolveRef(s.Else, defs)
138+
s.Items = resolveRef(s.Items, defs)
139+
s.Contains = resolveRef(s.Contains, defs)
140+
s.AdditionalProperties = resolveRef(s.AdditionalProperties, defs)
141+
s.PropertyNames = resolveRef(s.PropertyNames, defs)
142+
s.ContentSchema = resolveRef(s.ContentSchema, defs)
143+
144+
return s
99145
}
100146

101147
// NewTool Create a tool, where the input and output are both in JSON format.

0 commit comments

Comments
 (0)