feat: Support Structured Outputs (#813)
* feat: Support Structured Outputs * feat: Support Structured Outputs * update imports * add integration test * update JSON schema comments
This commit is contained in:
@@ -4,6 +4,7 @@ package openai_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
@@ -178,3 +179,63 @@ func TestAPIError(t *testing.T) {
|
|||||||
t.Fatal("Empty error message occurred")
|
t.Fatal("Empty error message occurred")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestChatCompletionResponseFormat_JSONSchema(t *testing.T) {
|
||||||
|
apiToken := os.Getenv("OPENAI_TOKEN")
|
||||||
|
if apiToken == "" {
|
||||||
|
t.Skip("Skipping testing against production OpenAI API. Set OPENAI_TOKEN environment variable to enable it.")
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
c := openai.NewClient(apiToken)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
resp, err := c.CreateChatCompletion(
|
||||||
|
ctx,
|
||||||
|
openai.ChatCompletionRequest{
|
||||||
|
Model: openai.GPT4oMini,
|
||||||
|
Messages: []openai.ChatCompletionMessage{
|
||||||
|
{
|
||||||
|
Role: openai.ChatMessageRoleSystem,
|
||||||
|
Content: "Please enter a string, and we will convert it into the following naming conventions:" +
|
||||||
|
"1. PascalCase: Each word starts with an uppercase letter, with no spaces or separators." +
|
||||||
|
"2. CamelCase: The first word starts with a lowercase letter, " +
|
||||||
|
"and subsequent words start with an uppercase letter, with no spaces or separators." +
|
||||||
|
"3. KebabCase: All letters are lowercase, with words separated by hyphens `-`." +
|
||||||
|
"4. SnakeCase: All letters are lowercase, with words separated by underscores `_`.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Role: openai.ChatMessageRoleUser,
|
||||||
|
Content: "Hello World",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ResponseFormat: &openai.ChatCompletionResponseFormat{
|
||||||
|
Type: openai.ChatCompletionResponseFormatTypeJSONSchema,
|
||||||
|
JSONSchema: openai.ChatCompletionResponseFormatJSONSchema{
|
||||||
|
Name: "cases",
|
||||||
|
Schema: jsonschema.Definition{
|
||||||
|
Type: jsonschema.Object,
|
||||||
|
Properties: map[string]jsonschema.Definition{
|
||||||
|
"PascalCase": jsonschema.Definition{Type: jsonschema.String},
|
||||||
|
"CamelCase": jsonschema.Definition{Type: jsonschema.String},
|
||||||
|
"KebabCase": jsonschema.Definition{Type: jsonschema.String},
|
||||||
|
"SnakeCase": jsonschema.Definition{Type: jsonschema.String},
|
||||||
|
},
|
||||||
|
Required: []string{"PascalCase", "CamelCase", "KebabCase", "SnakeCase"},
|
||||||
|
AdditionalProperties: false,
|
||||||
|
},
|
||||||
|
Strict: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
checks.NoError(t, err, "CreateChatCompletion (use json_schema response) returned error")
|
||||||
|
var result = make(map[string]string)
|
||||||
|
err = json.Unmarshal([]byte(resp.Choices[0].Message.Content), &result)
|
||||||
|
checks.NoError(t, err, "CreateChatCompletion (use json_schema response) unmarshal error")
|
||||||
|
for _, key := range []string{"PascalCase", "CamelCase", "KebabCase", "SnakeCase"} {
|
||||||
|
if _, ok := result[key]; !ok {
|
||||||
|
t.Errorf("key:%s does not exist.", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
13
chat.go
13
chat.go
@@ -5,6 +5,8 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Chat message role defined by the OpenAI API.
|
// Chat message role defined by the OpenAI API.
|
||||||
@@ -175,11 +177,20 @@ type ChatCompletionResponseFormatType string
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
ChatCompletionResponseFormatTypeJSONObject ChatCompletionResponseFormatType = "json_object"
|
ChatCompletionResponseFormatTypeJSONObject ChatCompletionResponseFormatType = "json_object"
|
||||||
|
ChatCompletionResponseFormatTypeJSONSchema ChatCompletionResponseFormatType = "json_schema"
|
||||||
ChatCompletionResponseFormatTypeText ChatCompletionResponseFormatType = "text"
|
ChatCompletionResponseFormatTypeText ChatCompletionResponseFormatType = "text"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ChatCompletionResponseFormat struct {
|
type ChatCompletionResponseFormat struct {
|
||||||
Type ChatCompletionResponseFormatType `json:"type,omitempty"`
|
Type ChatCompletionResponseFormatType `json:"type,omitempty"`
|
||||||
|
JSONSchema ChatCompletionResponseFormatJSONSchema `json:"json_schema,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChatCompletionResponseFormatJSONSchema struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
Schema jsonschema.Definition `json:"schema"`
|
||||||
|
Strict bool `json:"strict"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChatCompletionRequest represents a request structure for chat completion API.
|
// ChatCompletionRequest represents a request structure for chat completion API.
|
||||||
|
|||||||
@@ -29,11 +29,17 @@ type Definition struct {
|
|||||||
// one element, where each element is unique. You will probably only use this with strings.
|
// one element, where each element is unique. You will probably only use this with strings.
|
||||||
Enum []string `json:"enum,omitempty"`
|
Enum []string `json:"enum,omitempty"`
|
||||||
// Properties describes the properties of an object, if the schema type is Object.
|
// Properties describes the properties of an object, if the schema type is Object.
|
||||||
Properties map[string]Definition `json:"properties"`
|
Properties map[string]Definition `json:"properties,omitempty"`
|
||||||
// Required specifies which properties are required, if the schema type is Object.
|
// Required specifies which properties are required, if the schema type is Object.
|
||||||
Required []string `json:"required,omitempty"`
|
Required []string `json:"required,omitempty"`
|
||||||
// Items specifies which data type an array contains, if the schema type is Array.
|
// Items specifies which data type an array contains, if the schema type is Array.
|
||||||
Items *Definition `json:"items,omitempty"`
|
Items *Definition `json:"items,omitempty"`
|
||||||
|
// AdditionalProperties is used to control the handling of properties in an object
|
||||||
|
// that are not explicitly defined in the properties section of the schema. example:
|
||||||
|
// additionalProperties: true
|
||||||
|
// additionalProperties: false
|
||||||
|
// additionalProperties: jsonschema.Definition{Type: jsonschema.String}
|
||||||
|
AdditionalProperties any `json:"additionalProperties,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d Definition) MarshalJSON() ([]byte, error) {
|
func (d Definition) MarshalJSON() ([]byte, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user