Repository: sashabaranov/go-openai
Pull request #407 (2023-06-23 14:42:46)

move json schema to directory/package
@sashabaranov When discussing #377 you made [a comment](https://github.com/sashabaranov/go-openai/pull/377#issuecomment-1596097407) about separating out the json schema definitions, which [I did in a subsequent commit](https://github.com/sashabaranov/go-openai/pull/377#issuecomment-1597897146). Although it was added to @stillmatic 's fork before #377 was accepted, it looks like that commit didn't make it into the final merge!

I am including it here for consideration.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/sashabaranov/go-openai/pull/407?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#407](https://app.codecov.io/gh/sashabaranov/go-openai/pull/407?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (e6d27d2) into [master](https://app.codecov.io/gh/sashabaranov/go-openai/commit/5f4ef298e3d4d74784ac53d75d0d43379efa2efc?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (5f4ef29) will **not change** coverage.
> The diff coverage is `n/a`.

```diff
@@           Coverage Diff           @@
##           master     #407   +/-   ##
=======================================
  Coverage   96.95%   96.95%           
=======================================
  Files          17       17           
  Lines         690      690           
=======================================
  Hits          669      669           
  Misses         15       15           
  Partials        6        6           
```


| [Impacted Files](https://app.codecov.io/gh/sashabaranov/go-openai/pull/407?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | Coverage Δ | |
|---|---|---|
| [chat.go](https://app.codecov.io/gh/sashabaranov/go-openai/pull/407?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-Y2hhdC5nbw==) | `100.00% <ø> (ø)` | |


sashabaranov: @jmacwhyte thank you for the PR! Would you be up to adding a README example while we are on it?
stillmatic: oops, it got clobbered in a rebase + force push :| 

fwiw it does break the import path of old code (hence updating the tests). I am fine with it but should make a note!
jmacwhyte: @sashabaranov I've added some details to the README, please let me know if that looks sufficient!

@stillmatic I'm not really sure what details to include about import path breaking, but would be happy to add something if you have a suggestion. 

Patch:
diff --git a/README.md b/README.md
index ef1db98..da1a280 100644
--- a/README.md
+++ b/README.md
@@ -516,6 +516,66 @@ func main() {
 ```
 </details>
 
+<details>
+<summary>JSON Schema for function calling</summary>
+
+It is now possible for chat completion to choose to call a function for more information ([see developer docs here](https://platform.openai.com/docs/guides/gpt/function-calling)).
+
+In order to describe the type of functions that can be called, a JSON schema must be provided. Many JSON schema libraries exist and are more advanced than what we can offer in this library, however we have included a simple `jsonschema` package for those who want to use this feature without formatting their own JSON schema payload.
+
+The developer documents give this JSON schema definition as an example:
+
+```json
+{
+  "name":"get_current_weather",
+  "description":"Get the current weather in a given location",
+  "parameters":{
+    "type":"object",
+    "properties":{
+        "location":{
+          "type":"string",
+          "description":"The city and state, e.g. San Francisco, CA"
+        },
+        "unit":{
+          "type":"string",
+          "enum":[
+              "celsius",
+              "fahrenheit"
+          ]
+        }
+    },
+    "required":[
+        "location"
+    ]
+  }
+}
+```
+
+Using the `jsonschema` package, this schema could be created using structs as such:
+
+```go
+FunctionDefinition{
+  Name: "get_current_weather",
+  Parameters: jsonschema.Definition{
+    Type: jsonschema.Object,
+    Properties: map[string]jsonschema.Definition{
+      "location": {
+        Type: jsonschema.String,
+        Description: "The city and state, e.g. San Francisco, CA",
+      },
+      "unit": {
+        Type: jsonschema.String,
+        Enum: []string{"celcius", "fahrenheit"},
+      },
+    },
+    Required: []string{"location"},
+  },
+}
+```
+
+The `Parameters` field of a `FunctionDefinition` can accept either of the above styles, or even a nested struct from another library (as long as it can be marshalled into JSON).
+</details>
+
 <details>
 <summary>Error handling</summary>
 
diff --git a/chat.go b/chat.go
index b74720d..e4f23df 100644
--- a/chat.go
+++ b/chat.go
@@ -62,47 +62,16 @@ type FunctionDefinition struct {
 	Name        string `json:"name"`
 	Description string `json:"description,omitempty"`
 	// Parameters is an object describing the function.
-	// You can pass a raw byte array describing the schema,
-	// or you can pass in a struct which serializes to the proper JSONSchema.
-	// The JSONSchemaDefinition struct is provided for convenience, but you should
-	// consider another specialized library for more complex schemas.
+	// You can pass a []byte describing the schema,
+	// or you can pass in a struct which serializes to the proper JSON schema.
+	// The jsonschema package is provided for convenience, but you should
+	// consider another specialized library if you require more complex schemas.
 	Parameters any `json:"parameters"`
 }
 
 // Deprecated: use FunctionDefinition instead.
 type FunctionDefine = FunctionDefinition
 
-type JSONSchemaType string
-
-const (
-	JSONSchemaTypeObject  JSONSchemaType = "object"
-	JSONSchemaTypeNumber  JSONSchemaType = "number"
-	JSONSchemaTypeString  JSONSchemaType = "string"
-	JSONSchemaTypeArray   JSONSchemaType = "array"
-	JSONSchemaTypeNull    JSONSchemaType = "null"
-	JSONSchemaTypeBoolean JSONSchemaType = "boolean"
-)
-
-// JSONSchemaDefinition is a struct for JSON Schema.
-// It is fairly limited and you may have better luck using a third-party library.
-type JSONSchemaDefinition struct {
-	// Type is a type of JSON Schema.
-	Type JSONSchemaType `json:"type,omitempty"`
-	// Description is a description of JSON Schema.
-	Description string `json:"description,omitempty"`
-	// Enum is a enum of JSON Schema. It used if Type is JSONSchemaTypeString.
-	Enum []string `json:"enum,omitempty"`
-	// Properties is a properties of JSON Schema. It used if Type is JSONSchemaTypeObject.
-	Properties map[string]JSONSchemaDefinition `json:"properties,omitempty"`
-	// Required is a required of JSON Schema. It used if Type is JSONSchemaTypeObject.
-	Required []string `json:"required,omitempty"`
-	// Items is a property of JSON Schema. It used if Type is JSONSchemaTypeArray.
-	Items *JSONSchemaDefinition `json:"items,omitempty"`
-}
-
-// Deprecated: use JSONSchemaDefinition instead.
-type JSONSchemaDefine = JSONSchemaDefinition
-
 type FinishReason string
 
 const (
diff --git a/jsonschema/json.go b/jsonschema/json.go
new file mode 100644
index 0000000..24af858
--- /dev/null
+++ b/jsonschema/json.go
@@ -0,0 +1,35 @@
+// Package jsonschema provides very simple functionality for representing a JSON schema as a
+// (nested) struct. This struct can be used with the chat completion "function call" feature.
+// For more complicated schemas, it is recommended to use a dedicated JSON schema library
+// and/or pass in the schema in []byte format.
+package jsonschema
+
+type DataType string
+
+const (
+	Object  DataType = "object"
+	Number  DataType = "number"
+	Integer DataType = "integer"
+	String  DataType = "string"
+	Array   DataType = "array"
+	Null    DataType = "null"
+	Boolean DataType = "boolean"
+)
+
+// Definition is a struct for describing a JSON Schema.
+// It is fairly limited and you may have better luck using a third-party library.
+type Definition struct {
+	// Type specifies the data type of the schema.
+	Type DataType `json:"type,omitempty"`
+	// Description is the description of the schema.
+	Description string `json:"description,omitempty"`
+	// Enum is used to restrict a value to a fixed set of values. It must be an array with at least
+	// one element, where each element is unique. You will probably only use this with strings.
+	Enum []string `json:"enum,omitempty"`
+	// Properties describes the properties of an object, if the schema type is Object.
+	Properties map[string]Definition `json:"properties,omitempty"`
+	// Required specifies which properties are required, if the schema type is Object.
+	Required []string `json:"required,omitempty"`
+	// Items specifies which data type an array contains, if the schema type is Array.
+	Items *Definition `json:"items,omitempty"`
+}


Test patch:
diff --git a/chat_test.go b/chat_test.go
index 3c759b3..d5879e6 100644
--- a/chat_test.go
+++ b/chat_test.go
@@ -1,9 +1,6 @@
 package openai_test
 
 import (
-	. "github.com/sashabaranov/go-openai"
-	"github.com/sashabaranov/go-openai/internal/test/checks"
-
 	"context"
 	"encoding/json"
 	"fmt"
@@ -13,6 +10,10 @@ import (
 	"strings"
 	"testing"
 	"time"
+
+	. "github.com/sashabaranov/go-openai"
+	"github.com/sashabaranov/go-openai/internal/test/checks"
+	"github.com/sashabaranov/go-openai/jsonschema"
 )
 
 func TestChatCompletionsWrongModel(t *testing.T) {
@@ -128,22 +129,22 @@ func TestChatCompletionsFunctions(t *testing.T) {
 			},
 			Functions: []FunctionDefinition{{
 				Name: "test",
-				Parameters: &JSONSchemaDefinition{
-					Type: JSONSchemaTypeObject,
-					Properties: map[string]JSONSchemaDefinition{
+				Parameters: &jsonschema.Definition{
+					Type: jsonschema.Object,
+					Properties: map[string]jsonschema.Definition{
 						"count": {
-							Type:        JSONSchemaTypeNumber,
+							Type:        jsonschema.Number,
 							Description: "total number of words in sentence",
 						},
 						"words": {
-							Type:        JSONSchemaTypeArray,
+							Type:        jsonschema.Array,
 							Description: "list of words in sentence",
-							Items: &JSONSchemaDefinition{
-								Type: JSONSchemaTypeString,
+							Items: &jsonschema.Definition{
+								Type: jsonschema.String,
 							},
 						},
 						"enumTest": {
-							Type: JSONSchemaTypeString,
+							Type: jsonschema.String,
 							Enum: []string{"hello", "world"},
 						},
 					},
@@ -165,22 +166,22 @@ func TestChatCompletionsFunctions(t *testing.T) {
 			},
 			Functions: []FunctionDefine{{
 				Name: "test",
-				Parameters: &JSONSchemaDefine{
-					Type: JSONSchemaTypeObject,
-					Properties: map[string]JSONSchemaDefine{
+				Parameters: &jsonschema.Definition{
+					Type: jsonschema.Object,
+					Properties: map[string]jsonschema.Definition{
 						"count": {
-							Type:        JSONSchemaTypeNumber,
+							Type:        jsonschema.Number,
 							Description: "total number of words in sentence",
 						},
 						"words": {
-							Type:        JSONSchemaTypeArray,
+							Type:        jsonschema.Array,
 							Description: "list of words in sentence",
-							Items: &JSONSchemaDefine{
-								Type: JSONSchemaTypeString,
+							Items: &jsonschema.Definition{
+								Type: jsonschema.String,
 							},
 						},
 						"enumTest": {
-							Type: JSONSchemaTypeString,
+							Type: jsonschema.String,
 							Enum: []string{"hello", "world"},
 						},
 					},