Skip to content

Commit

Permalink
Added support for streaming responses.
Browse files Browse the repository at this point in the history
  • Loading branch information
btfranklin committed May 7, 2023
1 parent 8767527 commit 4b11a60
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ let package = Package(
targets: ["CleverBird"]),
],
dependencies: [
.package(url: "https://github.com/kean/Get", from: "2.1.6"),
.package(url: "https://github.com/kean/Get", from: "2.1.6")
],
targets: [
.target(
Expand Down
1 change: 0 additions & 1 deletion Sources/CleverBird/chat/ChatCompletionResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,3 @@ struct ChatCompletionResponse: Codable {
}
let choices: [Choice]
}

37 changes: 37 additions & 0 deletions Sources/CleverBird/chat/ChatStreamedResponseChunk.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Created by B.T. Franklin on 5/5/23

import Foundation

struct ChatStreamedResponseChunk: Codable {
struct Choice: Codable {
struct Delta: Codable {
let role: ChatMessage.Role?
let content: String?
}
let delta: Delta

enum FinishReason: String, Codable {
case stop
case length
case contentFilter
}
let finishReason: FinishReason?
}
let choices: [Choice]
}

extension ChatStreamedResponseChunk {
static private var CHUNK_DECODER: JSONDecoder = {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
return decoder
}()

static func decode(from string: String) -> ChatStreamedResponseChunk? {
guard string.hasPrefix("data: "),
let data = string.dropFirst(6).data(using: .utf8) else {
return nil
}
return try? CHUNK_DECODER.decode(ChatStreamedResponseChunk.self, from: data)
}
}
71 changes: 71 additions & 0 deletions Sources/CleverBird/chat/ChatThread+completeWithStreaming.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Created by B.T. Franklin on 5/5/23

import Foundation

extension ChatThread {
public func completeWithStreaming() async throws -> AsyncThrowingStream<String, Swift.Error> {

let requestBody = ChatCompletionRequestParameters(
model: self.model,
temperature: self.temperature,
topP: self.topP,
stream: true,
stop: self.stop,
presencePenalty: self.presencePenalty,
frequencyPenalty: self.frequencyPenalty,
user: self.user,
messages: self.messages
)

// Define the callback closure that appends the message to the chat thread
let onResponseStreamCompleted: (ChatMessage) -> Void = { message in
_ = self.addMessage(message)
}

let asyncByteStream = try await self.connection.createAsyncByteStream(for: requestBody)
return AsyncThrowingStream { continuation in
Task {

var responseMessageRole: ChatMessage.Role?
var responseMessageContent: String?

do {
for try await line in asyncByteStream.lines {
print(line)
guard let responseChunk = ChatStreamedResponseChunk.decode(from: line) else {
break
}

if let deltaRole = responseChunk.choices.first?.delta.role {
responseMessageRole = deltaRole
continue
}

guard let delta = responseChunk.choices.first?.delta else {
continue
}

guard let deltaContent = delta.content else {
continue
}

if let currentMessageContent = responseMessageContent {
responseMessageContent = currentMessageContent + deltaContent
} else {
responseMessageContent = deltaContent
}
continuation.yield(deltaContent)
}
} catch {
throw CleverBirdError.responseParsingFailed
}

if let responseMessageRole, let responseMessageContent {
onResponseStreamCompleted(ChatMessage(role: responseMessageRole, content: responseMessageContent))
}
continuation.finish()
}
}
}

}

0 comments on commit 4b11a60

Please sign in to comment.