ModelContent.swift 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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. case executableCode(ExecutableCode)
  40. case codeExecutionResult(CodeExecutionResult)
  41. struct UnsupportedDataError: Error {
  42. let decodingError: DecodingError
  43. var localizedDescription: String {
  44. decodingError.localizedDescription
  45. }
  46. }
  47. }
  48. let data: OneOfData?
  49. let isThought: Bool?
  50. let thoughtSignature: String?
  51. init(_ data: OneOfData, isThought: Bool?, thoughtSignature: String?) {
  52. self.data = data
  53. self.isThought = isThought
  54. self.thoughtSignature = thoughtSignature
  55. }
  56. }
  57. /// A type describing data in media formats interpretable by an AI model. Each generative AI
  58. /// request or response contains an `Array` of ``ModelContent``s, and each ``ModelContent`` value
  59. /// may comprise multiple heterogeneous ``Part``s.
  60. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
  61. public struct ModelContent: Equatable, Sendable {
  62. /// The role of the entity creating the ``ModelContent``. For user-generated client requests,
  63. /// for example, the role is `user`.
  64. public let role: String?
  65. /// The data parts comprising this ``ModelContent`` value.
  66. public var parts: [any Part] {
  67. return internalParts.compactMap { part -> (any Part)? in
  68. switch part.data {
  69. case let .text(text):
  70. return TextPart(text, isThought: part.isThought, thoughtSignature: part.thoughtSignature)
  71. case let .inlineData(inlineData):
  72. return InlineDataPart(
  73. inlineData, isThought: part.isThought, thoughtSignature: part.thoughtSignature
  74. )
  75. case let .fileData(fileData):
  76. return FileDataPart(
  77. fileData, isThought: part.isThought, thoughtSignature: part.thoughtSignature
  78. )
  79. case let .functionCall(functionCall):
  80. return FunctionCallPart(
  81. functionCall, isThought: part.isThought, thoughtSignature: part.thoughtSignature
  82. )
  83. case let .functionResponse(functionResponse):
  84. return FunctionResponsePart(
  85. functionResponse, isThought: part.isThought, thoughtSignature: part.thoughtSignature
  86. )
  87. case let .executableCode(executableCode):
  88. return ExecutableCodePart(
  89. executableCode, isThought: part.isThought, thoughtSignature: part.thoughtSignature
  90. )
  91. case let .codeExecutionResult(codeExecutionResult):
  92. return CodeExecutionResultPart(
  93. codeExecutionResult: codeExecutionResult,
  94. isThought: part.isThought,
  95. thoughtSignature: part.thoughtSignature
  96. )
  97. case .none:
  98. // Filter out parts that contain missing or unrecognized data
  99. return nil
  100. }
  101. }
  102. }
  103. // TODO: Refactor this
  104. let internalParts: [InternalPart]
  105. /// Creates a new value from a list of ``Part``s.
  106. public init(role: String? = "user", parts: [any Part]) {
  107. self.role = role
  108. var convertedParts = [InternalPart]()
  109. for part in parts {
  110. switch part {
  111. case let textPart as TextPart:
  112. convertedParts.append(InternalPart(
  113. .text(textPart.text),
  114. isThought: textPart._isThought,
  115. thoughtSignature: textPart.thoughtSignature
  116. ))
  117. case let inlineDataPart as InlineDataPart:
  118. convertedParts.append(InternalPart(
  119. .inlineData(inlineDataPart.inlineData),
  120. isThought: inlineDataPart._isThought,
  121. thoughtSignature: inlineDataPart.thoughtSignature
  122. ))
  123. case let fileDataPart as FileDataPart:
  124. convertedParts.append(InternalPart(
  125. .fileData(fileDataPart.fileData),
  126. isThought: fileDataPart._isThought,
  127. thoughtSignature: fileDataPart.thoughtSignature
  128. ))
  129. case let functionCallPart as FunctionCallPart:
  130. convertedParts.append(InternalPart(
  131. .functionCall(functionCallPart.functionCall),
  132. isThought: functionCallPart._isThought,
  133. thoughtSignature: functionCallPart.thoughtSignature
  134. ))
  135. case let functionResponsePart as FunctionResponsePart:
  136. convertedParts.append(InternalPart(
  137. .functionResponse(functionResponsePart.functionResponse),
  138. isThought: functionResponsePart._isThought,
  139. thoughtSignature: functionResponsePart.thoughtSignature
  140. ))
  141. default:
  142. fatalError()
  143. }
  144. }
  145. internalParts = convertedParts
  146. }
  147. /// Creates a new value from any data interpretable as a ``Part``.
  148. /// See ``PartsRepresentable`` for types that can be interpreted as `Part`s.
  149. public init(role: String? = "user", parts: any PartsRepresentable...) {
  150. let content = parts.flatMap { $0.partsValue }
  151. self.init(role: role, parts: content)
  152. }
  153. init(role: String?, parts: [InternalPart]) {
  154. self.role = role
  155. internalParts = parts
  156. }
  157. }
  158. // MARK: Codable Conformances
  159. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
  160. extension ModelContent: Codable {
  161. enum CodingKeys: String, CodingKey {
  162. case role
  163. case internalParts = "parts"
  164. }
  165. public init(from decoder: any Decoder) throws {
  166. let container = try decoder.container(keyedBy: CodingKeys.self)
  167. role = try container.decodeIfPresent(String.self, forKey: .role)
  168. internalParts = try container.decodeIfPresent([InternalPart].self, forKey: .internalParts) ?? []
  169. }
  170. }
  171. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
  172. extension InternalPart: Codable {
  173. enum CodingKeys: String, CodingKey {
  174. case isThought = "thought"
  175. case thoughtSignature
  176. }
  177. public func encode(to encoder: Encoder) throws {
  178. try data.encode(to: encoder)
  179. var container = encoder.container(keyedBy: CodingKeys.self)
  180. try container.encodeIfPresent(isThought, forKey: .isThought)
  181. try container.encodeIfPresent(thoughtSignature, forKey: .thoughtSignature)
  182. }
  183. public init(from decoder: Decoder) throws {
  184. do {
  185. data = try OneOfData(from: decoder)
  186. } catch let error as OneOfData.UnsupportedDataError {
  187. AILog.error(code: .decodedUnsupportedPartData, error.localizedDescription)
  188. data = nil
  189. } catch { // Re-throw any other error types
  190. throw error
  191. }
  192. let container = try decoder.container(keyedBy: CodingKeys.self)
  193. isThought = try container.decodeIfPresent(Bool.self, forKey: .isThought)
  194. thoughtSignature = try container.decodeIfPresent(String.self, forKey: .thoughtSignature)
  195. }
  196. }
  197. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
  198. extension InternalPart.OneOfData: Codable {
  199. enum CodingKeys: String, CodingKey {
  200. case text
  201. case inlineData
  202. case fileData
  203. case functionCall
  204. case functionResponse
  205. case executableCode
  206. case codeExecutionResult
  207. }
  208. public func encode(to encoder: Encoder) throws {
  209. var container = encoder.container(keyedBy: CodingKeys.self)
  210. switch self {
  211. case let .text(text):
  212. try container.encode(text, forKey: .text)
  213. case let .inlineData(inlineData):
  214. try container.encode(inlineData, forKey: .inlineData)
  215. case let .fileData(fileData):
  216. try container.encode(fileData, forKey: .fileData)
  217. case let .functionCall(functionCall):
  218. try container.encode(functionCall, forKey: .functionCall)
  219. case let .functionResponse(functionResponse):
  220. try container.encode(functionResponse, forKey: .functionResponse)
  221. case let .executableCode(executableCode):
  222. try container.encode(executableCode, forKey: .executableCode)
  223. case let .codeExecutionResult(codeExecutionResult):
  224. try container.encode(codeExecutionResult, forKey: .codeExecutionResult)
  225. }
  226. }
  227. public init(from decoder: Decoder) throws {
  228. let values = try decoder.container(keyedBy: CodingKeys.self)
  229. if values.contains(.text) {
  230. self = try .text(values.decode(String.self, forKey: .text))
  231. } else if values.contains(.inlineData) {
  232. self = try .inlineData(values.decode(InlineData.self, forKey: .inlineData))
  233. } else if values.contains(.fileData) {
  234. self = try .fileData(values.decode(FileData.self, forKey: .fileData))
  235. } else if values.contains(.functionCall) {
  236. self = try .functionCall(values.decode(FunctionCall.self, forKey: .functionCall))
  237. } else if values.contains(.functionResponse) {
  238. self = try .functionResponse(values.decode(FunctionResponse.self, forKey: .functionResponse))
  239. } else if values.contains(.executableCode) {
  240. self = try .executableCode(values.decode(ExecutableCode.self, forKey: .executableCode))
  241. } else if values.contains(.codeExecutionResult) {
  242. self = try .codeExecutionResult(
  243. values.decode(CodeExecutionResult.self, forKey: .codeExecutionResult)
  244. )
  245. } else {
  246. let unexpectedKeys = values.allKeys.map { $0.stringValue }
  247. throw UnsupportedDataError(decodingError: DecodingError.dataCorrupted(
  248. DecodingError.Context(
  249. codingPath: values.codingPath,
  250. debugDescription: "Unexpected Part type(s): \(unexpectedKeys)"
  251. )
  252. ))
  253. }
  254. }
  255. }