Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Structured Outputs for function calling #226

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions Sources/OpenAI/Public/Models/ChatQuery.swift
Original file line number Diff line number Diff line change
Expand Up @@ -676,14 +676,19 @@ public struct ChatQuery: Equatable, Codable, Streamable {
/// **Python library defines only [String: Object] dictionary.
public let parameters: Self.FunctionParameters?

/// Whether to enable strict schema adherence when generating the function call. If set to true, the model will follow the exact schema defined in the parameters field. Only a subset of JSON Schema is supported when strict is true.
public let strict: Bool?

public init(
name: String,
description: String? = nil,
parameters: Self.FunctionParameters? = nil
parameters: Self.FunctionParameters? = nil,
strict: Bool? = nil
) {
self.name = name
self.description = description
self.parameters = parameters
self.strict = strict
}

/// See the [guide](/docs/guides/gpt/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format.
Expand All @@ -698,6 +703,7 @@ public struct ChatQuery: Equatable, Codable, Streamable {
public let multipleOf: Int?
public let minimum: Int?
public let maximum: Int?
public let additionalProperties: Bool?

public init(
type: Self.JSONType,
Expand All @@ -708,7 +714,8 @@ public struct ChatQuery: Equatable, Codable, Streamable {
enum: [String]? = nil,
multipleOf: Int? = nil,
minimum: Int? = nil,
maximum: Int? = nil
maximum: Int? = nil,
additionalProperties: Bool? = nil
) {
self.type = type
self.properties = properties
Expand All @@ -719,6 +726,7 @@ public struct ChatQuery: Equatable, Codable, Streamable {
self.multipleOf = multipleOf
self.minimum = minimum
self.maximum = maximum
self.additionalProperties = additionalProperties
}

public struct Property: Codable, Equatable {
Expand Down
66 changes: 66 additions & 0 deletions Tests/OpenAITests/OpenAITestsDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,72 @@ class OpenAITestsDecoder: XCTestCase {
XCTAssertEqual(chatQueryAsDict, expectedValueAsDict)
}

func testChatQueryWithFunctionCallStrict() async throws {
let chatQuery = ChatQuery(
messages: [
.user(.init(content: .string("What's the weather like in Boston?")))
],
model: .gpt3_5Turbo,
responseFormat: ChatQuery.ResponseFormat.jsonObject,
tools: [
.init(function: .init(
name: "get_current_weather",
description: "Get the current weather in a given location",
parameters: .init(
type: .object,
properties: [
"location": .init(type: .string, description: "The city and state, e.g. San Francisco, CA"),
"unit": .init(type: .string, enum: ["celsius", "fahrenheit"])
],
required: ["location"],
additionalProperties: false
),
strict: true
))
]
)
let expectedValue = """
{
"model": "gpt-3.5-turbo",
"messages": [
{ "role": "user", "content": "What's the weather like in Boston?" }
],
"response_format": {
"type": "json_object"
},
"tools": [
{
"function": {
"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"],
"additionalProperties": false
},
"strict": true
},
"type": "function"
}
],
"stream": false
}
"""

// To compare serialized JSONs we first convert them both into NSDictionary which are comparable (unline native swift dictionaries)
let chatQueryAsDict = try jsonDataAsNSDictionary(JSONEncoder().encode(chatQuery))
let expectedValueAsDict = try jsonDataAsNSDictionary(expectedValue.data(using: .utf8)!)

XCTAssertEqual(chatQueryAsDict, expectedValueAsDict)
}

func testChatCompletionWithFunctionCall() async throws {
let data = """
{
Expand Down
Loading