Improve handling of JSON Schema in OpenAI API Response Context (#819)

* feat: add jsonschema.Validate and jsonschema.Unmarshal

* fix Sanity check

* remove slices.Contains

* fix Sanity check

* add SchemaWrapper

* update api_integration_test.go

* update method 'reflectSchema' to support 'omitempty' in JSON tag

* add GenerateSchemaForType

* update json_test.go

* update `Warp` to `Wrap`

* fix Sanity check

* fix Sanity check

* update api_internal_test.go

* update README.md

* update README.md

* remove jsonschema.SchemaWrapper

* remove jsonschema.SchemaWrapper

* fix Sanity check

* optimize code formatting
This commit is contained in:
eiixy
2024-08-25 01:06:08 +08:00
committed by GitHub
parent 5162adbbf9
commit a3bd2569ac
7 changed files with 412 additions and 30 deletions

89
jsonschema/validate.go Normal file
View File

@@ -0,0 +1,89 @@
package jsonschema
import (
"encoding/json"
"errors"
)
func VerifySchemaAndUnmarshal(schema Definition, content []byte, v any) error {
var data any
err := json.Unmarshal(content, &data)
if err != nil {
return err
}
if !Validate(schema, data) {
return errors.New("data validation failed against the provided schema")
}
return json.Unmarshal(content, &v)
}
func Validate(schema Definition, data any) bool {
switch schema.Type {
case Object:
return validateObject(schema, data)
case Array:
return validateArray(schema, data)
case String:
_, ok := data.(string)
return ok
case Number: // float64 and int
_, ok := data.(float64)
if !ok {
_, ok = data.(int)
}
return ok
case Boolean:
_, ok := data.(bool)
return ok
case Integer:
_, ok := data.(int)
return ok
case Null:
return data == nil
default:
return false
}
}
func validateObject(schema Definition, data any) bool {
dataMap, ok := data.(map[string]any)
if !ok {
return false
}
for _, field := range schema.Required {
if _, exists := dataMap[field]; !exists {
return false
}
}
for key, valueSchema := range schema.Properties {
value, exists := dataMap[key]
if exists && !Validate(valueSchema, value) {
return false
} else if !exists && contains(schema.Required, key) {
return false
}
}
return true
}
func validateArray(schema Definition, data any) bool {
dataArray, ok := data.([]any)
if !ok {
return false
}
for _, item := range dataArray {
if !Validate(*schema.Items, item) {
return false
}
}
return true
}
func contains[S ~[]E, E comparable](s S, v E) bool {
for i := range s {
if v == s[i] {
return true
}
}
return false
}