ModelContent.swift 5.8 KB

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