ModelContent.swift 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. // Copyright 2023 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. import Foundation
  15. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
  16. extension [ModelContent] {
  17. // TODO: Rename and refactor this.
  18. func throwIfError() throws {
  19. for content in self {
  20. for part in content.parts {
  21. switch part {
  22. case let errorPart as ErrorPart:
  23. throw errorPart.error
  24. default:
  25. break
  26. }
  27. }
  28. }
  29. }
  30. }
  31. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
  32. struct InternalPart: Equatable, Sendable {
  33. enum OneOfData: Equatable, Sendable {
  34. case text(String)
  35. case inlineData(InlineData)
  36. case fileData(FileData)
  37. case functionCall(FunctionCall)
  38. case functionResponse(FunctionResponse)
  39. }
  40. let data: OneOfData
  41. let isThought: Bool?
  42. let thoughtSignature: String?
  43. init(_ data: OneOfData, isThought: Bool?, thoughtSignature: String?) {
  44. self.data = data
  45. self.isThought = isThought
  46. self.thoughtSignature = thoughtSignature
  47. }
  48. }
  49. /// A type describing data in media formats interpretable by an AI model. Each generative AI
  50. /// request or response contains an `Array` of ``ModelContent``s, and each ``ModelContent`` value
  51. /// may comprise multiple heterogeneous ``Part``s.
  52. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
  53. public struct ModelContent: Equatable, Sendable {
  54. /// The role of the entity creating the ``ModelContent``. For user-generated client requests,
  55. /// for example, the role is `user`.
  56. public let role: String?
  57. /// The data parts comprising this ``ModelContent`` value.
  58. public var parts: [any Part] {
  59. return internalParts.map { part -> any Part in
  60. switch part.data {
  61. case let .text(text):
  62. return TextPart(text, isThought: part.isThought, thoughtSignature: part.thoughtSignature)
  63. case let .inlineData(inlineData):
  64. return InlineDataPart(
  65. inlineData, isThought: part.isThought, thoughtSignature: part.thoughtSignature
  66. )
  67. case let .fileData(fileData):
  68. return FileDataPart(
  69. fileData, isThought: part.isThought, thoughtSignature: part.thoughtSignature
  70. )
  71. case let .functionCall(functionCall):
  72. return FunctionCallPart(
  73. functionCall, isThought: part.isThought, thoughtSignature: part.thoughtSignature
  74. )
  75. case let .functionResponse(functionResponse):
  76. return FunctionResponsePart(
  77. functionResponse, isThought: part.isThought, thoughtSignature: part.thoughtSignature
  78. )
  79. }
  80. }
  81. }
  82. // TODO: Refactor this
  83. let internalParts: [InternalPart]
  84. /// Creates a new value from a list of ``Part``s.
  85. public init(role: String? = "user", parts: [any Part]) {
  86. self.role = role
  87. var convertedParts = [InternalPart]()
  88. for part in parts {
  89. switch part {
  90. case let textPart as TextPart:
  91. convertedParts.append(InternalPart(
  92. .text(textPart.text),
  93. isThought: textPart._isThought,
  94. thoughtSignature: textPart.thoughtSignature
  95. ))
  96. case let inlineDataPart as InlineDataPart:
  97. convertedParts.append(InternalPart(
  98. .inlineData(inlineDataPart.inlineData),
  99. isThought: inlineDataPart._isThought,
  100. thoughtSignature: inlineDataPart.thoughtSignature
  101. ))
  102. case let fileDataPart as FileDataPart:
  103. convertedParts.append(InternalPart(
  104. .fileData(fileDataPart.fileData),
  105. isThought: fileDataPart._isThought,
  106. thoughtSignature: fileDataPart.thoughtSignature
  107. ))
  108. case let functionCallPart as FunctionCallPart:
  109. convertedParts.append(InternalPart(
  110. .functionCall(functionCallPart.functionCall),
  111. isThought: functionCallPart._isThought,
  112. thoughtSignature: functionCallPart.thoughtSignature
  113. ))
  114. case let functionResponsePart as FunctionResponsePart:
  115. convertedParts.append(InternalPart(
  116. .functionResponse(functionResponsePart.functionResponse),
  117. isThought: functionResponsePart._isThought,
  118. thoughtSignature: functionResponsePart.thoughtSignature
  119. ))
  120. default:
  121. fatalError()
  122. }
  123. }
  124. internalParts = convertedParts
  125. }
  126. /// Creates a new value from any data interpretable as a ``Part``.
  127. /// See ``PartsRepresentable`` for types that can be interpreted as `Part`s.
  128. public init(role: String? = "user", parts: any PartsRepresentable...) {
  129. let content = parts.flatMap { $0.partsValue }
  130. self.init(role: role, parts: content)
  131. }
  132. init(role: String?, parts: [InternalPart]) {
  133. self.role = role
  134. internalParts = parts
  135. }
  136. }
  137. // MARK: Codable Conformances
  138. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
  139. extension ModelContent: Codable {
  140. enum CodingKeys: String, CodingKey {
  141. case role
  142. case internalParts = "parts"
  143. }
  144. public init(from decoder: any Decoder) throws {
  145. let container = try decoder.container(keyedBy: CodingKeys.self)
  146. role = try container.decodeIfPresent(String.self, forKey: .role)
  147. internalParts = try container.decodeIfPresent([InternalPart].self, forKey: .internalParts) ?? []
  148. }
  149. }
  150. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
  151. extension InternalPart: Codable {
  152. enum CodingKeys: String, CodingKey {
  153. case isThought = "thought"
  154. case thoughtSignature
  155. }
  156. public func encode(to encoder: Encoder) throws {
  157. try data.encode(to: encoder)
  158. var container = encoder.container(keyedBy: CodingKeys.self)
  159. try container.encodeIfPresent(isThought, forKey: .isThought)
  160. try container.encodeIfPresent(thoughtSignature, forKey: .thoughtSignature)
  161. }
  162. public init(from decoder: Decoder) throws {
  163. data = try OneOfData(from: decoder)
  164. let container = try decoder.container(keyedBy: CodingKeys.self)
  165. isThought = try container.decodeIfPresent(Bool.self, forKey: .isThought)
  166. thoughtSignature = try container.decodeIfPresent(String.self, forKey: .thoughtSignature)
  167. }
  168. }
  169. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
  170. extension InternalPart.OneOfData: Codable {
  171. enum CodingKeys: String, CodingKey {
  172. case text
  173. case inlineData
  174. case fileData
  175. case functionCall
  176. case functionResponse
  177. }
  178. public func encode(to encoder: Encoder) throws {
  179. var container = encoder.container(keyedBy: CodingKeys.self)
  180. switch self {
  181. case let .text(text):
  182. try container.encode(text, forKey: .text)
  183. case let .inlineData(inlineData):
  184. try container.encode(inlineData, forKey: .inlineData)
  185. case let .fileData(fileData):
  186. try container.encode(fileData, forKey: .fileData)
  187. case let .functionCall(functionCall):
  188. try container.encode(functionCall, forKey: .functionCall)
  189. case let .functionResponse(functionResponse):
  190. try container.encode(functionResponse, forKey: .functionResponse)
  191. }
  192. }
  193. public init(from decoder: Decoder) throws {
  194. let values = try decoder.container(keyedBy: CodingKeys.self)
  195. if values.contains(.text) {
  196. self = try .text(values.decode(String.self, forKey: .text))
  197. } else if values.contains(.inlineData) {
  198. self = try .inlineData(values.decode(InlineData.self, forKey: .inlineData))
  199. } else if values.contains(.fileData) {
  200. self = try .fileData(values.decode(FileData.self, forKey: .fileData))
  201. } else if values.contains(.functionCall) {
  202. self = try .functionCall(values.decode(FunctionCall.self, forKey: .functionCall))
  203. } else if values.contains(.functionResponse) {
  204. self = try .functionResponse(values.decode(FunctionResponse.self, forKey: .functionResponse))
  205. } else {
  206. let unexpectedKeys = values.allKeys.map { $0.stringValue }
  207. throw DecodingError.dataCorrupted(DecodingError.Context(
  208. codingPath: values.codingPath,
  209. debugDescription: "Unexpected Part type(s): \(unexpectedKeys)"
  210. ))
  211. }
  212. }
  213. }