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

Include usage stats #19

Merged
merged 15 commits into from
May 7, 2024
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,19 @@ Just like with the non-streamed completion, the message will be automatically
appended onto the thread after it has finished streaming, but the stream
allows you to see it as it's coming through.

To include usage (the number of tokens used in the prompt and completion), add set `streamOptions` in the `complete` method. The usage is available as a property of `StreamableChatThread` after the stream has completed.

```swift
let chatThread = ChatThread().withStreaming()
let completionStream = try await chatThread.complete(using: openAIAPIConnection, includeUsage: true)
for try await messageChunk in completionStream {
print("Received message chunk: \(messageChunk)")
}
if let usage = completionStream.usage {
print("Usage: \(usage)")
}
```

Calculate the token count for messages in the chat thread:

```swift
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public struct ChatCompletionRequestParameters: Codable {
public let messages: [ChatMessage]
public let functions: [Function]?
public let functionCallMode: FunctionCallMode?
public let streamOptions: StreamOptions?

public init(model: ChatModel,
temperature: Percentage,
Expand All @@ -25,7 +26,8 @@ public struct ChatCompletionRequestParameters: Codable {
user: String? = nil,
messages: [ChatMessage],
functions: [Function]? = nil,
functionCallMode: FunctionCallMode? = nil) {
functionCallMode: FunctionCallMode? = nil,
streamOptions: StreamOptions? = nil) {
self.model = model
self.temperature = temperature
self.topP = topP
Expand All @@ -38,5 +40,6 @@ public struct ChatCompletionRequestParameters: Codable {
self.messages = messages
self.functions = functions
self.functionCallMode = functionCallMode
self.streamOptions = streamOptions
}
}
12 changes: 6 additions & 6 deletions Sources/CleverBird/chat/ChatCompletionResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

import Foundation

public struct Usage: Codable {
public let promptTokens: Int
public let completionTokens: Int
public let totalTokens: Int
}

struct ChatCompletionResponse: Codable, Identifiable {

struct Choice: Codable {
Expand All @@ -23,12 +29,6 @@ struct ChatCompletionResponse: Codable, Identifiable {
}
}

struct Usage: Codable {
let promptTokens: Int
let completionTokens: Int
let totalTokens: Int
}

let choices: [Choice]
let usage: Usage
let id: String
Expand Down
25 changes: 23 additions & 2 deletions Sources/CleverBird/chat/ChatThread+complete.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,28 @@ extension ChatThread {
frequencyPenalty: Penalty? = nil,
functions: [Function]? = nil,
functionCallMode: FunctionCallMode? = nil) async throws -> ChatMessage {

try await completeIncludeUsage(using: connection,
model: model,
temperature: temperature,
topP: topP,
stop: stop,
maxTokens: maxTokens,
presencePenalty: presencePenalty,
frequencyPenalty: frequencyPenalty,
functions: functions,
functionCallMode: functionCallMode).0
}

public func completeIncludeUsage(using connection: OpenAIAPIConnection,
model: ChatModel = .gpt4,
temperature: Percentage = 0.7,
topP: Percentage? = nil,
stop: [String]? = nil,
maxTokens: Int? = nil,
presencePenalty: Penalty? = nil,
frequencyPenalty: Penalty? = nil,
functions: [Function]? = nil,
functionCallMode: FunctionCallMode? = nil) async throws -> (ChatMessage, Usage) {
let requestBody = ChatCompletionRequestParameters(
model: model,
temperature: temperature,
Expand Down Expand Up @@ -47,7 +68,7 @@ extension ChatThread {
// Append the response message to the thread
addMessage(firstChoiceMessage)

return firstChoiceMessage
return (firstChoiceMessage, completion.usage)

} catch {
throw CleverBirdError.requestFailed(message: error.localizedDescription)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ struct ChatStreamedResponseChunk: Codable, Identifiable {
}
let choices: [Choice]
let id: String
let usage: Usage?
}

extension ChatStreamedResponseChunk {
Expand Down
17 changes: 17 additions & 0 deletions Sources/CleverBird/chat/streaming/StreamOptions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this comment header. These comment headers that repeat the name of the file are just noise and don't add any value to the code.

// StreamOptions.swift
//
//
// Created by Ronald Mannak on 5/6/24.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to leave this Created by line though. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@btfranklin Done!

//

import Foundation

public struct StreamOptions: Codable {

let includeUsage: Bool

public init(includeUsage: Bool) {
self.includeUsage = includeUsage
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ extension StreamableChatThread {
stop: [String]? = nil,
maxTokens: Int? = nil,
presencePenalty: Penalty? = nil,
frequencyPenalty: Penalty? = nil) async throws -> AsyncThrowingStream<String, Swift.Error> {
frequencyPenalty: Penalty? = nil,
includeUsage: Bool = false) async throws -> AsyncThrowingStream<String, Swift.Error> {

let requestBody = ChatCompletionRequestParameters(
model: model,
Expand All @@ -23,7 +24,8 @@ extension StreamableChatThread {
presencePenalty: presencePenalty,
frequencyPenalty: frequencyPenalty,
user: self.chatThread.user,
messages: self.chatThread.messages
messages: self.chatThread.messages,
streamOptions: includeUsage ? StreamOptions(includeUsage: true) : nil
)

// Define the callback closure that appends the message to the chat thread
Expand Down Expand Up @@ -71,6 +73,10 @@ extension StreamableChatThread {

responseMessageId = responseChunk.id

if let usage = responseChunk.usage {
strongSelf.usage = usage
}

if let deltaRole = responseChunk.choices.first?.delta.role {
responseMessageRole = deltaRole
continue
Expand All @@ -89,6 +95,9 @@ extension StreamableChatThread {
} else {
responseMessageContent = deltaContent
}

strongSelf.usage = responseChunk.usage

continuation.yield(deltaContent)
}
// Finished normally
Expand Down
4 changes: 3 additions & 1 deletion Sources/CleverBird/chat/streaming/StreamableChatThread.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Created by B.T. Franklin on 5/11/23

public class StreamableChatThread {
public final class StreamableChatThread {

var streamingTask: Task<Void, Error>?
let chatThread: ChatThread

public var usage: Usage? = nil

init(chatThread: ChatThread) {
self.chatThread = chatThread
Expand Down
Loading