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

Add support to watchOS from 4.0 #182

Open
wants to merge 4 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
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ let package = Package(
.iOS(.v11),
.macOS(.v10_13),
.macCatalyst(.v13),
.watchOS(.v4)
],
products: [
.library(
Expand Down
8 changes: 4 additions & 4 deletions Sources/GoogleAI/Chat.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import Foundation

/// An object that represents a back-and-forth chat with a model, capturing the history and saving
/// the context in memory between each message sent.
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, watchOS 7.0, *)
public class Chat {
private let model: GenerativeModel

Expand Down Expand Up @@ -84,7 +84,7 @@ public class Chat {
/// and response will be added to the history. If unsuccessful, history will remain unchanged.
/// - Parameter parts: The new content to send as a single chat message.
/// - Returns: A stream containing the model's response or an error if an error occurred.
@available(macOS 12.0, *)
@available(macOS 12.0, watchOS 8.0, *)
public func sendMessageStream(_ parts: any ThrowingPartsRepresentable...)
-> AsyncThrowingStream<GenerateContentResponse, Error> {
return try sendMessageStream([ModelContent(parts: parts)])
Expand All @@ -94,7 +94,7 @@ public class Chat {
/// and response will be added to the history. If unsuccessful, history will remain unchanged.
/// - Parameter content: The new content to send as a single chat message.
/// - Returns: A stream containing the model's response or an error if an error occurred.
@available(macOS 12.0, *)
@available(macOS 12.0, watchOS 8.0, *)
public func sendMessageStream(_ content: @autoclosure () throws -> [ModelContent])
-> AsyncThrowingStream<GenerateContentResponse, Error> {
let resolvedContent: [ModelContent]
Expand Down Expand Up @@ -188,4 +188,4 @@ public class Chat {
return ModelContent(role: "user", parts: content.parts)
}
}
}
}
2 changes: 1 addition & 1 deletion Sources/GoogleAI/CountTokensRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ extension CountTokensRequest: Encodable {
}

@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
extension CountTokensResponse: Decodable {}
extension CountTokensResponse: Decodable {}
2 changes: 1 addition & 1 deletion Sources/GoogleAI/Errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -186,4 +186,4 @@ enum RPCErrorMessage: String {
enum InvalidCandidateError: Error {
case emptyContent(underlyingError: Error)
case malformedContent(underlyingError: Error)
}
}
2 changes: 1 addition & 1 deletion Sources/GoogleAI/FunctionCalling.swift
Original file line number Diff line number Diff line change
Expand Up @@ -292,4 +292,4 @@ extension FunctionCallingConfig.Mode: Encodable {}

extension ToolConfig: Encodable {}

extension FunctionResponse: Encodable {}
extension FunctionResponse: Encodable {}
4 changes: 2 additions & 2 deletions Sources/GoogleAI/GenerateContentError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import Foundation

/// Errors that occur when generating content from a model.
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, watchOS 7.0, *)
public enum GenerateContentError: Error {
/// An error occurred when constructing the prompt. Examine the related error for details.
case promptImageContentError(underlying: ImageConversionError)
Expand All @@ -41,4 +41,4 @@ public enum GenerateContentError: Error {
/// - Important: The API is only available in
/// [specific regions](https://ai.google.dev/available_regions#available_regions).
case unsupportedUserLocation
}
}
4 changes: 2 additions & 2 deletions Sources/GoogleAI/GenerateContentRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ extension GenerateContentRequest: Encodable {
}
}

@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, watchOS 7.0, *)
extension GenerateContentRequest: GenerativeAIRequest {
typealias Response = GenerateContentResponse

Expand All @@ -53,4 +53,4 @@ extension GenerateContentRequest: GenerativeAIRequest {
return URL(string: "\(modelURL):generateContent")!
}
}
}
}
12 changes: 6 additions & 6 deletions Sources/GoogleAI/GenerateContentResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import Foundation

/// The model's response to a generate content request.
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, watchOS 7.0, *)
public struct GenerateContentResponse {
/// Token usage metadata for processing the generate content request.
public struct UsageMetadata {
Expand Down Expand Up @@ -188,7 +188,7 @@ public struct PromptFeedback {

// MARK: - Codable Conformances

@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, watchOS 7.0, *)
extension GenerateContentResponse: Decodable {
enum CodingKeys: CodingKey {
case candidates
Expand Down Expand Up @@ -222,7 +222,7 @@ extension GenerateContentResponse: Decodable {
}
}

@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, watchOS 7.0, *)
extension GenerateContentResponse.UsageMetadata: Decodable {
enum CodingKeys: CodingKey {
case promptTokenCount
Expand Down Expand Up @@ -312,7 +312,7 @@ extension Citation: Decodable {
}
}

@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, watchOS 7.0, *)
extension FinishReason: Decodable {
public init(from decoder: Decoder) throws {
let value = try decoder.singleValueContainer().decode(String.self)
Expand All @@ -327,7 +327,7 @@ extension FinishReason: Decodable {
}
}

@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, watchOS 7.0, *)
extension PromptFeedback.BlockReason: Decodable {
public init(from decoder: Decoder) throws {
let value = try decoder.singleValueContainer().decode(String.self)
Expand Down Expand Up @@ -364,4 +364,4 @@ extension PromptFeedback: Decodable {
safetyRatings = []
}
}
}
}
2 changes: 1 addition & 1 deletion Sources/GoogleAI/GenerationConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,4 @@ public struct GenerationConfig {
// MARK: - Codable Conformances

@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
extension GenerationConfig: Encodable {}
extension GenerationConfig: Encodable {}
2 changes: 1 addition & 1 deletion Sources/GoogleAI/GenerativeAIRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ public struct RequestOptions {
self.timeout = timeout
self.apiVersion = apiVersion
}
}
}
9 changes: 6 additions & 3 deletions Sources/GoogleAI/GenerativeAIService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import Foundation

@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, watchOS 7.0, *)
struct GenerativeAIService {
/// Gives permission to talk to the backend.
private let apiKey: String
Expand Down Expand Up @@ -52,7 +52,7 @@ struct GenerativeAIService {
return try parseResponse(T.Response.self, from: data)
}

@available(macOS 12.0, *)
@available(macOS 12.0, watchOS 8.0, *)
func loadRequestStream<T: GenerativeAIRequest>(request: T)
-> AsyncThrowingStream<T.Response, Error> {
return AsyncThrowingStream { continuation in
Expand Down Expand Up @@ -161,6 +161,7 @@ struct GenerativeAIService {
return urlRequest
}

@available(watchOS 7.0, *)
private func httpResponse(urlResponse: URLResponse) throws -> HTTPURLResponse {
// Verify the status code is 200
guard let response = urlResponse as? HTTPURLResponse else {
Expand Down Expand Up @@ -209,6 +210,7 @@ struct GenerativeAIService {
}
}

@available(watchOS 7.0, *)
private func parseResponse<T: Decodable>(_ type: T.Type, from data: Data) throws -> T {
do {
return try JSONDecoder().decode(type, from: data)
Expand All @@ -222,6 +224,7 @@ struct GenerativeAIService {
}

#if DEBUG
@available(watchOS 7.0, *)
private func cURLCommand(from request: URLRequest) -> String {
var returnValue = "curl "
if let allHeaders = request.allHTTPHeaderFields {
Expand Down Expand Up @@ -251,4 +254,4 @@ struct GenerativeAIService {
""")
}
#endif // DEBUG
}
}
2 changes: 1 addition & 1 deletion Sources/GoogleAI/GenerativeAISwift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ public enum GenerativeAISwift {
public static let version = "0.5.4"
/// The Google AI backend endpoint URL.
static let baseURL = "https://generativelanguage.googleapis.com"
}
}
8 changes: 4 additions & 4 deletions Sources/GoogleAI/GenerativeModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import Foundation

/// A type that represents a remote multimodal model (like Gemini), with the ability to generate
/// content based on various input types.
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, watchOS 7.0, *)
public final class GenerativeModel {
// The prefix for a model resource in the Gemini API.
private static let modelResourcePrefix = "models/"
Expand Down Expand Up @@ -220,7 +220,7 @@ public final class GenerativeModel {
/// for conforming types).
/// - Returns: A stream wrapping content generated by the model or a ``GenerateContentError``
/// error if an error occurred.
@available(macOS 12.0, *)
@available(macOS 12.0, watchOS 8.0, *)
public func generateContentStream(_ parts: any ThrowingPartsRepresentable...)
-> AsyncThrowingStream<GenerateContentResponse, Error> {
return try generateContentStream([ModelContent(parts: parts)])
Expand All @@ -231,7 +231,7 @@ public final class GenerativeModel {
/// - Parameter content: The input(s) given to the model as a prompt.
/// - Returns: A stream wrapping content generated by the model or a ``GenerateContentError``
/// error if an error occurred.
@available(macOS 12.0, *)
@available(macOS 12.0, watchOS 8.0, *)
public func generateContentStream(_ content: @autoclosure () throws -> [ModelContent])
-> AsyncThrowingStream<GenerateContentResponse, Error> {
let evaluatedContent: [ModelContent]
Expand Down Expand Up @@ -373,4 +373,4 @@ public final class GenerativeModel {
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
public enum CountTokensError: Error {
case internalError(underlying: Error)
}
}
2 changes: 1 addition & 1 deletion Sources/GoogleAI/JSONValue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,4 @@ extension JSONValue: Encodable {
}
}

extension JSONValue: Equatable {}
extension JSONValue: Equatable {}
4 changes: 2 additions & 2 deletions Sources/GoogleAI/Logging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import Foundation
import OSLog

@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, watchOS 7.0, *)
struct Logging {
/// Subsystem that should be used for all Loggers.
static let subsystem = "com.google.generative-ai"
Expand Down Expand Up @@ -53,4 +53,4 @@ struct Logging {
return Logger(.disabled)
}
}()
}
}
2 changes: 1 addition & 1 deletion Sources/GoogleAI/ModelContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -188,4 +188,4 @@ extension ModelContent.Part: Codable {
))
}
}
}
}
13 changes: 10 additions & 3 deletions Sources/GoogleAI/PartsRepresentable+Image.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ import UniformTypeIdentifiers
import AppKit // For NSImage extensions.
#endif

#if os(watchOS)
import ImageIO
import CoreGraphics
#endif

private let imageCompressionQuality: CGFloat = 0.8

/// An enum describing failures that can occur when converting image types to model content data.
Expand All @@ -38,7 +43,7 @@ public enum ImageConversionError: Error {

#if canImport(UIKit)
/// Enables images to be representable as ``ThrowingPartsRepresentable``.
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, watchOS 8.0, *)
extension UIImage: ThrowingPartsRepresentable {
public func tryPartsValue() throws -> [ModelContent.Part] {
guard let data = jpegData(compressionQuality: imageCompressionQuality) else {
Expand All @@ -50,7 +55,7 @@ public enum ImageConversionError: Error {

#elseif canImport(AppKit)
/// Enables images to be representable as ``ThrowingPartsRepresentable``.
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, watchOS 8.0, *)
extension NSImage: ThrowingPartsRepresentable {
public func tryPartsValue() throws -> [ModelContent.Part] {
guard let cgImage = cgImage(forProposedRect: nil, context: nil, hints: nil) else {
Expand All @@ -67,7 +72,7 @@ public enum ImageConversionError: Error {
#endif

/// Enables `CGImages` to be representable as model content.
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, watchOS 9.0, *)
extension CGImage: ThrowingPartsRepresentable {
public func tryPartsValue() throws -> [ModelContent.Part] {
let output = NSMutableData()
Expand All @@ -88,6 +93,7 @@ extension CGImage: ThrowingPartsRepresentable {
}

/// Enables `CIImages` to be representable as model content.
#if !os(watchOS)
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
extension CIImage: ThrowingPartsRepresentable {
public func tryPartsValue() throws -> [ModelContent.Part] {
Expand All @@ -105,3 +111,4 @@ extension CIImage: ThrowingPartsRepresentable {
throw ImageConversionError.couldNotConvertToJPEG(self)
}
}
#endif
2 changes: 1 addition & 1 deletion Sources/GoogleAI/PartsRepresentable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,4 @@ extension String: PartsRepresentable {
public var partsValue: [ModelContent.Part] {
return [.text(self)]
}
}
}
8 changes: 4 additions & 4 deletions Sources/GoogleAI/Safety.swift
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public struct SafetySetting {

// MARK: - Codable Conformances

@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, watchOS 7.0, *)
extension SafetyRating.HarmProbability: Codable {
public init(from decoder: Decoder) throws {
let value = try decoder.singleValueContainer().decode(String.self)
Expand All @@ -164,7 +164,7 @@ extension SafetyRating: Decodable {}
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
extension SafetyFeedback: Decodable {}

@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, watchOS 7.0, *)
extension SafetySetting.HarmCategory: Codable {
public init(from decoder: Decoder) throws {
let value = try decoder.singleValueContainer().decode(String.self)
Expand All @@ -179,7 +179,7 @@ extension SafetySetting.HarmCategory: Codable {
}
}

@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, watchOS 7.0, *)
extension SafetySetting.BlockThreshold: Codable {
public init(from decoder: Decoder) throws {
let value = try decoder.singleValueContainer().decode(String.self)
Expand All @@ -195,4 +195,4 @@ extension SafetySetting.BlockThreshold: Codable {
}

@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
extension SafetySetting: Codable {}
extension SafetySetting: Codable {}