ModelContent.swift 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  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. /// A type describing data in media formats interpretable by an AI model. Each generative AI
  16. /// request or response contains an `Array` of ``ModelContent``s, and each ``ModelContent`` value
  17. /// may comprise multiple heterogeneous ``ModelContent/Part``s.
  18. @available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
  19. public struct ModelContent: Equatable {
  20. /// A discrete piece of data in a media format intepretable by an AI model. Within a single value
  21. /// of ``Part``, different data types may not mix.
  22. public enum Part: Equatable {
  23. /// Text value.
  24. case text(String)
  25. /// Data with a specified media type. Not all media types may be supported by the AI model.
  26. case data(mimetype: String, Data)
  27. /// A predicted function call returned from the model.
  28. case functionCall(FunctionCall)
  29. /// A response to a function call.
  30. case functionResponse(FunctionResponse)
  31. // MARK: Convenience Initializers
  32. /// Convenience function for populating a Part with JPEG data.
  33. public static func jpeg(_ data: Data) -> Self {
  34. return .data(mimetype: "image/jpeg", data)
  35. }
  36. /// Convenience function for populating a Part with PNG data.
  37. public static func png(_ data: Data) -> Self {
  38. return .data(mimetype: "image/png", data)
  39. }
  40. /// Returns the text contents of this ``Part``, if it contains text.
  41. public var text: String? {
  42. switch self {
  43. case let .text(contents): return contents
  44. default: return nil
  45. }
  46. }
  47. }
  48. /// The role of the entity creating the ``ModelContent``. For user-generated client requests,
  49. /// for example, the role is `user`.
  50. public let role: String?
  51. /// The data parts comprising this ``ModelContent`` value.
  52. public let parts: [Part]
  53. /// Creates a new value from any data or `Array` of data interpretable as a
  54. /// ``Part``. See ``ThrowingPartsRepresentable`` for types that can be interpreted as `Part`s.
  55. public init(role: String? = "user", parts: some ThrowingPartsRepresentable) throws {
  56. self.role = role
  57. try self.parts = parts.tryPartsValue()
  58. }
  59. /// Creates a new value from any data or `Array` of data interpretable as a
  60. /// ``Part``. See ``ThrowingPartsRepresentable`` for types that can be interpreted as `Part`s.
  61. public init(role: String? = "user", parts: some PartsRepresentable) {
  62. self.role = role
  63. self.parts = parts.partsValue
  64. }
  65. /// Creates a new value from a list of ``Part``s.
  66. public init(role: String? = "user", parts: [Part]) {
  67. self.role = role
  68. self.parts = parts
  69. }
  70. /// Creates a new value from any data interpretable as a ``Part``. See
  71. /// ``ThrowingPartsRepresentable``
  72. /// for types that can be interpreted as `Part`s.
  73. public init(role: String? = "user", _ parts: any ThrowingPartsRepresentable...) throws {
  74. let content = try parts.flatMap { try $0.tryPartsValue() }
  75. self.init(role: role, parts: content)
  76. }
  77. /// Creates a new value from any data interpretable as a ``Part``. See
  78. /// ``ThrowingPartsRepresentable``
  79. /// for types that can be interpreted as `Part`s.
  80. public init(role: String? = "user", _ parts: [PartsRepresentable]) {
  81. let content = parts.flatMap { $0.partsValue }
  82. self.init(role: role, parts: content)
  83. }
  84. }
  85. // MARK: Codable Conformances
  86. @available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
  87. extension ModelContent: Codable {}
  88. @available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *)
  89. extension ModelContent.Part: Codable {
  90. enum CodingKeys: String, CodingKey {
  91. case text
  92. case inlineData
  93. case functionCall
  94. case functionResponse
  95. }
  96. enum InlineDataKeys: String, CodingKey {
  97. case mimeType = "mime_type"
  98. case bytes = "data"
  99. }
  100. public func encode(to encoder: Encoder) throws {
  101. var container = encoder.container(keyedBy: CodingKeys.self)
  102. switch self {
  103. case let .text(a0):
  104. try container.encode(a0, forKey: .text)
  105. case let .data(mimetype, bytes):
  106. var inlineDataContainer = container.nestedContainer(
  107. keyedBy: InlineDataKeys.self,
  108. forKey: .inlineData
  109. )
  110. try inlineDataContainer.encode(mimetype, forKey: .mimeType)
  111. try inlineDataContainer.encode(bytes, forKey: .bytes)
  112. case let .functionCall(functionCall):
  113. try container.encode(functionCall, forKey: .functionCall)
  114. case let .functionResponse(functionResponse):
  115. try container.encode(functionResponse, forKey: .functionResponse)
  116. }
  117. }
  118. public init(from decoder: Decoder) throws {
  119. let values = try decoder.container(keyedBy: CodingKeys.self)
  120. if values.contains(.text) {
  121. self = try .text(values.decode(String.self, forKey: .text))
  122. } else if values.contains(.inlineData) {
  123. let dataContainer = try values.nestedContainer(
  124. keyedBy: InlineDataKeys.self,
  125. forKey: .inlineData
  126. )
  127. let mimetype = try dataContainer.decode(String.self, forKey: .mimeType)
  128. let bytes = try dataContainer.decode(Data.self, forKey: .bytes)
  129. self = .data(mimetype: mimetype, bytes)
  130. } else if values.contains(.functionCall) {
  131. self = try .functionCall(values.decode(FunctionCall.self, forKey: .functionCall))
  132. } else {
  133. throw DecodingError.dataCorrupted(.init(
  134. codingPath: [CodingKeys.text, CodingKeys.inlineData],
  135. debugDescription: "No text, inline data or function call was found."
  136. ))
  137. }
  138. }
  139. }