mirror of
https://github.com/mudler/LocalAI.git
synced 2026-01-06 18:49:55 -06:00
fix: handle multi-type arrays in JSON schema to prevent panic (#6495)
Signed-off-by: robert-cronin <robert.owen.cronin@gmail.com>
This commit is contained in:
@@ -61,8 +61,26 @@ func (sc *JSONSchemaConverter) addRule(name, rule string) string {
|
||||
func (sc *JSONSchemaConverter) visit(schema map[string]interface{}, name string, rootSchema map[string]interface{}) (string, error) {
|
||||
st, existType := schema["type"]
|
||||
var schemaType string
|
||||
var schemaTypes []string
|
||||
if existType {
|
||||
schemaType = st.(string)
|
||||
// Handle both single type strings and arrays of types (e.g., ["string", "null"])
|
||||
switch v := st.(type) {
|
||||
case string:
|
||||
// Single type: "type": "string"
|
||||
schemaType = v
|
||||
schemaTypes = []string{v}
|
||||
case []interface{}:
|
||||
// Multiple types: "type": ["string", "null"]
|
||||
for _, item := range v {
|
||||
if typeStr, ok := item.(string); ok {
|
||||
schemaTypes = append(schemaTypes, typeStr)
|
||||
}
|
||||
}
|
||||
// Use the first type as the primary schema type for compatibility
|
||||
if len(schemaTypes) > 0 {
|
||||
schemaType = schemaTypes[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
ruleName := name
|
||||
if name == "" {
|
||||
@@ -176,14 +194,30 @@ func (sc *JSONSchemaConverter) visit(schema map[string]interface{}, name string,
|
||||
rule := `"{" space "}" space`
|
||||
return sc.addRule(ruleName, rule), nil
|
||||
} else {
|
||||
primitiveRule, exists := PRIMITIVE_RULES[schemaType]
|
||||
if !exists {
|
||||
return "", fmt.Errorf("unrecognized schema: %v (type: %s)", schema, schemaType)
|
||||
// Handle primitive types, including multi-type arrays like ["string", "null"]
|
||||
if len(schemaTypes) > 1 {
|
||||
// Generate a union of multiple primitive types
|
||||
var typeRules []string
|
||||
for _, t := range schemaTypes {
|
||||
primitiveRule, exists := PRIMITIVE_RULES[t]
|
||||
if !exists {
|
||||
return "", fmt.Errorf("unrecognized type in multi-type schema: %s (schema: %v)", t, schema)
|
||||
}
|
||||
typeRules = append(typeRules, primitiveRule)
|
||||
}
|
||||
rule := "(" + strings.Join(typeRules, " | ") + ")"
|
||||
return sc.addRule(ruleName, rule), nil
|
||||
} else {
|
||||
// Single type
|
||||
primitiveRule, exists := PRIMITIVE_RULES[schemaType]
|
||||
if !exists {
|
||||
return "", fmt.Errorf("unrecognized schema: %v (type: %s)", schema, schemaType)
|
||||
}
|
||||
if ruleName == "root" {
|
||||
schemaType = "root"
|
||||
}
|
||||
return sc.addRule(schemaType, primitiveRule), nil
|
||||
}
|
||||
if ruleName == "root" {
|
||||
schemaType = "root"
|
||||
}
|
||||
return sc.addRule(schemaType, primitiveRule), nil
|
||||
}
|
||||
}
|
||||
func (sc *JSONSchemaConverter) resolveReference(ref string, rootSchema map[string]interface{}) (map[string]interface{}, error) {
|
||||
|
||||
@@ -476,5 +476,74 @@ realvalue
|
||||
Expect(err).To(BeNil())
|
||||
Expect(grammar).To(ContainSubstring(`root ::= "{" space "}" space`))
|
||||
})
|
||||
|
||||
It("handles multi-type array definitions like [string, null]", func() {
|
||||
// Type defined as an array should not panic
|
||||
multiTypeSchema := `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"street": {
|
||||
"description": "The given street name where the company resides.",
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"city": {
|
||||
"description": "The given city where the company resides.",
|
||||
"type": ["string", "null"]
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
grammar, err := NewJSONSchemaConverter("").GrammarFromBytes([]byte(multiTypeSchema))
|
||||
Expect(err).To(BeNil())
|
||||
// The grammar should contain rules for both string and null types
|
||||
Expect(grammar).To(ContainSubstring("string"))
|
||||
Expect(grammar).To(ContainSubstring("null"))
|
||||
// Should not panic and should generate valid grammar
|
||||
Expect(grammar).ToNot(BeEmpty())
|
||||
})
|
||||
|
||||
It("handles complex nested schema with multi-type arrays (issue #5572)", func() {
|
||||
complexSchema := `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"companylist": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"companyname": {
|
||||
"description": "The given name of the company.",
|
||||
"type": "string"
|
||||
},
|
||||
"street": {
|
||||
"description": "The given street name where the company resides.",
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"city": {
|
||||
"description": "The given city where the company resides.",
|
||||
"type": ["string", "null"]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["companyname", "street", "city"]
|
||||
}
|
||||
},
|
||||
"filter": {
|
||||
"description": "The type we should filter the list of companies by.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["companylist", "filter"],
|
||||
"additionalProperties": false
|
||||
}`
|
||||
|
||||
grammar, err := NewJSONSchemaConverter("").GrammarFromBytes([]byte(complexSchema))
|
||||
Expect(err).To(BeNil())
|
||||
// The grammar should be generated without panic
|
||||
Expect(grammar).ToNot(BeEmpty())
|
||||
// Should contain object and array structures
|
||||
Expect(grammar).To(ContainSubstring("{"))
|
||||
Expect(grammar).To(ContainSubstring("["))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user