in Sources/GoogleAI/GenerativeAIService.swift [56:145]
func loadRequestStream<T: GenerativeAIRequest>(request: T)
-> AsyncThrowingStream<T.Response, Error> {
return AsyncThrowingStream { continuation in
Task {
let urlRequest: URLRequest
do {
urlRequest = try self.urlRequest(request: request)
} catch {
continuation.finish(throwing: error)
return
}
#if DEBUG
printCURLCommand(from: urlRequest)
#endif
let stream: URLSession.AsyncBytes
let rawResponse: URLResponse
do {
(stream, rawResponse) = try await urlSession.bytes(for: urlRequest)
} catch {
continuation.finish(throwing: error)
return
}
// Verify the status code is 200
let response: HTTPURLResponse
do {
response = try httpResponse(urlResponse: rawResponse)
} catch {
continuation.finish(throwing: error)
return
}
// Verify the status code is 200
guard response.statusCode == 200 else {
Logging.network
.error("[GoogleGenerativeAI] The server responded with an error: \(response)")
var responseBody = ""
for try await line in stream.lines {
responseBody += line + "\n"
}
Logging.default.error("[GoogleGenerativeAI] Response payload: \(responseBody)")
continuation.finish(throwing: parseError(responseBody: responseBody))
return
}
// Received lines that are not server-sent events (SSE); these are not prefixed with "data:"
var extraLines: String = ""
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
for try await line in stream.lines {
Logging.network.debug("[GoogleGenerativeAI] Stream response: \(line)")
if line.hasPrefix("data:") {
// We can assume 5 characters since it's utf-8 encoded, removing `data:`.
let jsonText = String(line.dropFirst(5))
let data: Data
do {
data = try jsonData(jsonText: jsonText)
} catch {
continuation.finish(throwing: error)
return
}
// Handle the content.
do {
let content = try parseResponse(T.Response.self, from: data)
continuation.yield(content)
} catch {
continuation.finish(throwing: error)
return
}
} else {
extraLines += line
}
}
if extraLines.count > 0 {
continuation.finish(throwing: parseError(responseBody: extraLines))
return
}
continuation.finish(throwing: nil)
}
}
}