Skip to content

Commit 2e10f00

Browse files
committed
Updated tests and podspec
1 parent c9265fc commit 2e10f00

4 files changed

Lines changed: 118 additions & 93 deletions

File tree

OpenAIKit.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
Pod::Spec.new do |s|
1010
s.name = 'OpenAIKit'
11-
s.version = '1.6.0'
11+
s.version = '1.7.0'
1212
s.summary = 'OpenAI is a community-maintained repository containing Swift implementation over OpenAI public API.'
1313

1414
s.description = <<-DESC

Sources/OpenAIKit/Models/ChatCompletionsRequest.swift

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,27 +26,28 @@ public struct ChatCompletionsRequest: Codable {
2626
public var presencePenalty: Double? = nil
2727
/// Include the log probabilities on the `logprobs` most likely tokens, as well the chosen tokens. For example, if `logprobs` is 5, the API will return a list of the 5 most likely tokens. The API will always return the `logprob` of the sampled token, so there may be up to `logprobs+1` elements in the response. The maximum value for `logprobs` is 5.
2828
public var logprobs: Int? = nil
29-
/// An object specifying the format that the model must output.
30-
/// Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates
31-
/// is valid JSON.
32-
/// **Important:** when using JSON mode, you must also instruct the model to produce JSON yourself via a system or
33-
/// user message. Without this, the model may generate an unending stream of whitespace until the generation reaches
34-
/// the token limit, resulting in a long-running and seemingly "stuck" request. Also note that the message content
35-
/// may be partially cut off if finish_reason="length", which indicates the generation exceeded max_tokens or the
36-
/// conversation exceeded the max context length.
37-
public var responseFormat: ResponseFormat?
29+
/// An object specifying the format that the model must output.
30+
/// Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates
31+
/// is valid JSON.
32+
/// **Important:** when using JSON mode, you must also instruct the model to produce JSON yourself via a system or
33+
/// user message. Without this, the model may generate an unending stream of whitespace until the generation reaches
34+
/// the token limit, resulting in a long-running and seemingly "stuck" request. Also note that the message content
35+
/// may be partially cut off if finish_reason="length", which indicates the generation exceeded max_tokens or the
36+
/// conversation exceeded the max context length.
37+
public var responseFormat: ResponseFormat?
3838
/// Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence.
3939
public var stop: [String]? = nil
4040
/// A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.
4141
public var user: String? = nil
4242
/// Whether to stream back partial progress. If set, tokens will be sent as data-only server-sent events as they become available
4343
public var stream: Bool = false
44-
public struct ResponseFormat: Codable {
45-
internal var type: FormatType?
46-
internal enum FormatType: String, Codable {
47-
case text = "text", jsonObject = "json_object"
48-
}
49-
public static let json = ResponseFormat(type: .jsonObject)
50-
public static let text = ResponseFormat(type: .text)
51-
}
44+
public struct ResponseFormat: Codable {
45+
internal var type: FormatType?
46+
internal enum FormatType: String, Codable {
47+
case text, jsonObject = "json_object"
48+
}
49+
50+
public static let json = ResponseFormat(type: .jsonObject)
51+
public static let text = ResponseFormat(type: .text)
52+
}
5253
}

Sources/OpenAIKit/OpenAIKitRequests/Chat.swift

Lines changed: 75 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -26,27 +26,28 @@ public extension OpenAIKit {
2626
/// - logprobs: Include the log probabilities on the `logprobs` most likely tokens, as well the chosen tokens. For example, if `logprobs` is 5, the API will return a list of the 5 most likely tokens. The API will always return the `logprob` of the sampled token, so there may be up to `logprobs+1` elements in the response. The maximum value for `logprobs` is 5.
2727
/// - stop: Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence.
2828
/// - user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.
29-
func sendChatCompletion(newMessage: AIMessage,
30-
previousMessages: [AIMessage] = [],
31-
model: AIModelType,
32-
maxTokens: Int?,
33-
temperature: Double = 1,
34-
n: Int? = nil,
35-
topP: Double? = nil,
36-
frequencyPenalty: Double? = nil,
37-
presencePenalty: Double? = nil,
38-
logprobs: Int? = nil,
39-
stop: [String]? = nil,
40-
responseFormat: ChatCompletionsRequest.ResponseFormat? = nil,
41-
user: String? = nil,
42-
completion: @escaping (Result<AIResponseModel, Error>) -> Void)
43-
{
29+
func sendChatCompletion(
30+
newMessage: AIMessage,
31+
previousMessages: [AIMessage] = [],
32+
model: AIModelType,
33+
maxTokens: Int?,
34+
temperature: Double = 1,
35+
n: Int? = nil,
36+
topP: Double? = nil,
37+
frequencyPenalty: Double? = nil,
38+
presencePenalty: Double? = nil,
39+
logprobs: Int? = nil,
40+
stop: [String]? = nil,
41+
responseFormat: ChatCompletionsRequest.ResponseFormat? = nil,
42+
user: String? = nil,
43+
completion: @escaping (Result<AIResponseModel, Error>) -> Void
44+
) {
4445
let endpoint = OpenAIEndpoint.chatCompletions
4546

4647
var messages = previousMessages
4748
messages.append(newMessage)
4849

49-
let requestBody = ChatCompletionsRequest(model: model, messages: messages, temperature: temperature, n: n, maxTokens: maxTokens, topP: topP, frequencyPenalty: frequencyPenalty, presencePenalty: presencePenalty, logprobs: logprobs, responseFormat: responseFormat, stop: stop, user: user)
50+
let requestBody = ChatCompletionsRequest(model: model, messages: messages, temperature: temperature, n: n, maxTokens: maxTokens, topP: topP, frequencyPenalty: frequencyPenalty, presencePenalty: presencePenalty, logprobs: logprobs, responseFormat: responseFormat, stop: stop, user: user)
5051

5152
let requestData = try? jsonEncoder.encode(requestBody)
5253

@@ -58,36 +59,36 @@ public extension OpenAIKit {
5859
@available(swift 5.5)
5960
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
6061
func sendChatCompletion(
61-
newMessage: AIMessage,
62-
previousMessages: [AIMessage] = [],
63-
model: AIModelType,
64-
maxTokens: Int?,
65-
temperature: Double = 1,
66-
n: Int? = nil,
67-
topP: Double? = nil,
68-
frequencyPenalty: Double? = nil,
69-
presencePenalty: Double? = nil,
70-
logprobs: Int? = nil,
71-
stop: [String]? = nil,
72-
responseFormat: ChatCompletionsRequest.ResponseFormat? = nil,
73-
user: String? = nil
74-
) async -> Result<AIResponseModel, Error> {
62+
newMessage: AIMessage,
63+
previousMessages: [AIMessage] = [],
64+
model: AIModelType,
65+
maxTokens: Int?,
66+
temperature: Double = 1,
67+
n: Int? = nil,
68+
topP: Double? = nil,
69+
frequencyPenalty: Double? = nil,
70+
presencePenalty: Double? = nil,
71+
logprobs: Int? = nil,
72+
stop: [String]? = nil,
73+
responseFormat: ChatCompletionsRequest.ResponseFormat? = nil,
74+
user: String? = nil
75+
) async -> Result<AIResponseModel, Error> {
7576
return await withCheckedContinuation { continuation in
76-
sendChatCompletion(
77-
newMessage: newMessage,
78-
previousMessages: previousMessages,
79-
model: model,
80-
maxTokens: maxTokens,
81-
temperature: temperature,
82-
n: n,
83-
topP: topP,
84-
frequencyPenalty: frequencyPenalty,
85-
presencePenalty: presencePenalty,
86-
logprobs: logprobs,
87-
stop: stop,
88-
responseFormat: responseFormat,
89-
user: user
90-
) { result in
77+
sendChatCompletion(
78+
newMessage: newMessage,
79+
previousMessages: previousMessages,
80+
model: model,
81+
maxTokens: maxTokens,
82+
temperature: temperature,
83+
n: n,
84+
topP: topP,
85+
frequencyPenalty: frequencyPenalty,
86+
presencePenalty: presencePenalty,
87+
logprobs: logprobs,
88+
stop: stop,
89+
responseFormat: responseFormat,
90+
user: user
91+
) { result in
9192
continuation.resume(returning: result)
9293
}
9394
}
@@ -114,20 +115,21 @@ public extension OpenAIKit {
114115
/// - logprobs: Include the log probabilities on the `logprobs` most likely tokens, as well the chosen tokens. For example, if `logprobs` is 5, the API will return a list of the 5 most likely tokens. The API will always return the `logprob` of the sampled token, so there may be up to `logprobs+1` elements in the response. The maximum value for `logprobs` is 5.
115116
/// - stop: Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence.
116117
/// - user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.
117-
func sendStreamChatCompletion(newMessage: AIMessage,
118-
previousMessages: [AIMessage] = [],
119-
model: AIModelType,
120-
maxTokens: Int?,
121-
temperature: Double = 1,
122-
n: Int? = nil,
123-
topP: Double? = nil,
124-
frequencyPenalty: Double? = nil,
125-
presencePenalty: Double? = nil,
126-
logprobs: Int? = nil,
127-
stop: [String]? = nil,
128-
user: String? = nil,
129-
completion: @escaping (Result<AIStreamResponse<AIResponseModel>, Error>) -> Void)
130-
{
118+
func sendStreamChatCompletion(
119+
newMessage: AIMessage,
120+
previousMessages: [AIMessage] = [],
121+
model: AIModelType,
122+
maxTokens: Int?,
123+
temperature: Double = 1,
124+
n: Int? = nil,
125+
topP: Double? = nil,
126+
frequencyPenalty: Double? = nil,
127+
presencePenalty: Double? = nil,
128+
logprobs: Int? = nil,
129+
stop: [String]? = nil,
130+
user: String? = nil,
131+
completion: @escaping (Result<AIStreamResponse<AIResponseModel>, Error>) -> Void
132+
) {
131133
let endpoint = OpenAIEndpoint.chatCompletions
132134

133135
var messages = previousMessages
@@ -146,19 +148,20 @@ public extension OpenAIKit {
146148

147149
@available(swift 5.5)
148150
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
149-
func sendStreamChatCompletion(newMessage: AIMessage,
150-
previousMessages: [AIMessage] = [],
151-
model: AIModelType,
152-
maxTokens: Int?,
153-
temperature: Double = 1,
154-
n: Int? = nil,
155-
topP: Double? = nil,
156-
frequencyPenalty: Double? = nil,
157-
presencePenalty: Double? = nil,
158-
logprobs: Int? = nil,
159-
stop: [String]? = nil,
160-
user: String? = nil) async throws -> AsyncThrowingStream<AIStreamResponse<AIResponseModel>, Error>
161-
{
151+
func sendStreamChatCompletion(
152+
newMessage: AIMessage,
153+
previousMessages: [AIMessage] = [],
154+
model: AIModelType,
155+
maxTokens: Int?,
156+
temperature: Double = 1,
157+
n: Int? = nil,
158+
topP: Double? = nil,
159+
frequencyPenalty: Double? = nil,
160+
presencePenalty: Double? = nil,
161+
logprobs: Int? = nil,
162+
stop: [String]? = nil,
163+
user: String? = nil
164+
) async throws -> AsyncThrowingStream<AIStreamResponse<AIResponseModel>, Error> {
162165
let endpoint = OpenAIEndpoint.chatCompletions
163166

164167
var messages = previousMessages

Tests/OpenAIKitTests/OpenAIKitTests.swift

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,11 @@ final class OpenAIKitTests: XCTestCase {
6767
}
6868
case .failure(let error):
6969
print(error.localizedDescription)
70-
XCTAssertFalse(true)
70+
expectation.fulfill()
7171
}
7272
})
7373

7474
wait(for: [expectation], timeout: 300)
75-
7675
XCTAssertFalse(resultText.isEmpty)
7776
}
7877

@@ -92,7 +91,29 @@ final class OpenAIKitTests: XCTestCase {
9291
}
9392
case .failure(let error):
9493
print(error.localizedDescription)
95-
XCTAssertFalse(true)
94+
expectation.fulfill()
95+
}
96+
}
97+
98+
wait(for: [expectation], timeout: 300)
99+
XCTAssertFalse(resultText.isEmpty)
100+
}
101+
102+
func testChatResponseFormatCompletions() {
103+
let expectation = XCTestExpectation(description: "Async operation completes")
104+
105+
var resultText = ""
106+
107+
openAI?.sendChatCompletion(newMessage: AIMessage(role: .user, content: "Write a 100-word essay about the earth"), previousMessages: [AIMessage(role: .system, content: "You are a helpful assistant designed to output JSON.")], model: .custom("gpt-3.5-turbo-1106"), maxTokens: 300, temperature: 0.7, responseFormat: .json) { result in
108+
109+
switch result {
110+
case .success(let streamResult):
111+
resultText += streamResult.choices.first?.message?.content ?? ""
112+
113+
expectation.fulfill()
114+
case .failure(let error):
115+
print(error.localizedDescription)
116+
expectation.fulfill()
96117
}
97118
}
98119

0 commit comments

Comments
 (0)