ErrorDetailsView.swift 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  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 FirebaseVertexAI
  15. import MarkdownUI
  16. import SwiftUI
  17. extension HarmCategory: CustomStringConvertible {
  18. public var description: String {
  19. switch self {
  20. case .dangerousContent: "Dangerous content"
  21. case .harassment: "Harassment"
  22. case .hateSpeech: "Hate speech"
  23. case .sexuallyExplicit: "Sexually explicit"
  24. case .unknown: "Unknown"
  25. }
  26. }
  27. }
  28. extension SafetyRating.HarmProbability: CustomStringConvertible {
  29. public var description: String {
  30. switch self {
  31. case .high: "High"
  32. case .low: "Low"
  33. case .medium: "Medium"
  34. case .negligible: "Negligible"
  35. case .unknown: "Unknown"
  36. }
  37. }
  38. }
  39. private struct SubtitleFormRow: View {
  40. var title: String
  41. var value: String
  42. var body: some View {
  43. VStack(alignment: .leading) {
  44. Text(title)
  45. .font(.subheadline)
  46. Text(value)
  47. }
  48. }
  49. }
  50. private struct SubtitleMarkdownFormRow: View {
  51. var title: String
  52. var value: String
  53. var body: some View {
  54. VStack(alignment: .leading) {
  55. Text(title)
  56. .font(.subheadline)
  57. Markdown(value)
  58. }
  59. }
  60. }
  61. private struct SafetyRatingsSection: View {
  62. var ratings: [SafetyRating]
  63. var body: some View {
  64. Section("Safety ratings") {
  65. List(ratings, id: \.self) { rating in
  66. HStack {
  67. Text("\(String(describing: rating.category))")
  68. .font(.subheadline)
  69. Spacer()
  70. Text("\(String(describing: rating.probability))")
  71. }
  72. }
  73. }
  74. }
  75. }
  76. struct ErrorDetailsView: View {
  77. var error: Error
  78. var body: some View {
  79. NavigationView {
  80. Form {
  81. switch error {
  82. case let GenerateContentError.internalError(underlying: underlyingError):
  83. Section("Error Type") {
  84. Text("Internal error")
  85. }
  86. Section("Details") {
  87. SubtitleFormRow(title: "Error description",
  88. value: underlyingError.localizedDescription)
  89. }
  90. case let GenerateContentError.promptBlocked(response: generateContentResponse):
  91. Section("Error Type") {
  92. Text("Your prompt was blocked")
  93. }
  94. Section("Details") {
  95. if let reason = generateContentResponse.promptFeedback?.blockReason {
  96. SubtitleFormRow(title: "Reason for blocking", value: reason.rawValue)
  97. }
  98. if let text = generateContentResponse.text {
  99. SubtitleMarkdownFormRow(title: "Last chunk for the response", value: text)
  100. }
  101. }
  102. if let ratings = generateContentResponse.candidates.first?.safetyRatings {
  103. SafetyRatingsSection(ratings: ratings)
  104. }
  105. case let GenerateContentError.responseStoppedEarly(
  106. reason: finishReason,
  107. response: generateContentResponse
  108. ):
  109. Section("Error Type") {
  110. Text("Response stopped early")
  111. }
  112. Section("Details") {
  113. SubtitleFormRow(title: "Reason for finishing early", value: finishReason.rawValue)
  114. if let text = generateContentResponse.text {
  115. SubtitleMarkdownFormRow(title: "Last chunk for the response", value: text)
  116. }
  117. }
  118. if let ratings = generateContentResponse.candidates.first?.safetyRatings {
  119. SafetyRatingsSection(ratings: ratings)
  120. }
  121. default:
  122. Section("Error Type") {
  123. Text("Some other error")
  124. }
  125. Section("Details") {
  126. SubtitleFormRow(title: "Error description", value: error.localizedDescription)
  127. }
  128. }
  129. }
  130. .navigationTitle("Error details")
  131. .navigationBarTitleDisplayMode(.inline)
  132. }
  133. }
  134. }
  135. #Preview("Response Stopped Early") {
  136. let error = GenerateContentError.responseStoppedEarly(
  137. reason: .maxTokens,
  138. response: GenerateContentResponse(candidates: [
  139. CandidateResponse(content: ModelContent(role: "model", [
  140. """
  141. A _hypothetical_ model response.
  142. Cillum ex aliqua amet aliquip labore amet eiusmod consectetur reprehenderit sit commodo.
  143. """,
  144. ]),
  145. safetyRatings: [
  146. SafetyRating(category: .dangerousContent, probability: .high),
  147. SafetyRating(category: .harassment, probability: .low),
  148. SafetyRating(category: .hateSpeech, probability: .low),
  149. SafetyRating(category: .sexuallyExplicit, probability: .low),
  150. ],
  151. finishReason: FinishReason.maxTokens,
  152. citationMetadata: nil),
  153. ])
  154. )
  155. return ErrorDetailsView(error: error)
  156. }
  157. #Preview("Prompt Blocked") {
  158. let error = GenerateContentError.promptBlocked(
  159. response: GenerateContentResponse(candidates: [
  160. CandidateResponse(content: ModelContent(role: "model", [
  161. """
  162. A _hypothetical_ model response.
  163. Cillum ex aliqua amet aliquip labore amet eiusmod consectetur reprehenderit sit commodo.
  164. """,
  165. ]),
  166. safetyRatings: [
  167. SafetyRating(category: .dangerousContent, probability: .high),
  168. SafetyRating(category: .harassment, probability: .low),
  169. SafetyRating(category: .hateSpeech, probability: .low),
  170. SafetyRating(category: .sexuallyExplicit, probability: .low),
  171. ],
  172. finishReason: FinishReason.other,
  173. citationMetadata: nil),
  174. ])
  175. )
  176. return ErrorDetailsView(error: error)
  177. }