mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-10 22:45:59 +05:45
209 lines
5.9 KiB
Swift
209 lines
5.9 KiB
Swift
// Copyright 2023 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
import FirebaseVertexAI
|
|
import MarkdownUI
|
|
import SwiftUI
|
|
|
|
extension SafetySetting.HarmCategory: CustomStringConvertible {
|
|
public var description: String {
|
|
switch self {
|
|
case .dangerousContent: "Dangerous content"
|
|
case .harassment: "Harassment"
|
|
case .hateSpeech: "Hate speech"
|
|
case .sexuallyExplicit: "Sexually explicit"
|
|
case .unknown: "Unknown"
|
|
case .unspecified: "Unspecified"
|
|
}
|
|
}
|
|
}
|
|
|
|
extension SafetyRating.HarmProbability: CustomStringConvertible {
|
|
public var description: String {
|
|
switch self {
|
|
case .high: "High"
|
|
case .low: "Low"
|
|
case .medium: "Medium"
|
|
case .negligible: "Negligible"
|
|
case .unknown: "Unknown"
|
|
case .unspecified: "Unspecified"
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct SubtitleFormRow: View {
|
|
var title: String
|
|
var value: String
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading) {
|
|
Text(title)
|
|
.font(.subheadline)
|
|
Text(value)
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct SubtitleMarkdownFormRow: View {
|
|
var title: String
|
|
var value: String
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading) {
|
|
Text(title)
|
|
.font(.subheadline)
|
|
Markdown(value)
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct SafetyRatingsSection: View {
|
|
var ratings: [SafetyRating]
|
|
|
|
var body: some View {
|
|
Section("Safety ratings") {
|
|
List(ratings, id: \.self) { rating in
|
|
HStack {
|
|
Text("\(String(describing: rating.category))")
|
|
.font(.subheadline)
|
|
Spacer()
|
|
Text("\(String(describing: rating.probability))")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ErrorDetailsView: View {
|
|
var error: Error
|
|
|
|
var body: some View {
|
|
NavigationView {
|
|
Form {
|
|
switch error {
|
|
case let GenerateContentError.internalError(underlying: underlyingError):
|
|
Section("Error Type") {
|
|
Text("Internal error")
|
|
}
|
|
|
|
Section("Details") {
|
|
SubtitleFormRow(title: "Error description",
|
|
value: underlyingError.localizedDescription)
|
|
}
|
|
|
|
case let GenerateContentError.promptBlocked(response: generateContentResponse):
|
|
Section("Error Type") {
|
|
Text("Your prompt was blocked")
|
|
}
|
|
|
|
Section("Details") {
|
|
if let reason = generateContentResponse.promptFeedback?.blockReason {
|
|
SubtitleFormRow(title: "Reason for blocking", value: reason.rawValue)
|
|
}
|
|
|
|
if let text = generateContentResponse.text {
|
|
SubtitleMarkdownFormRow(title: "Last chunk for the response", value: text)
|
|
}
|
|
}
|
|
|
|
if let ratings = generateContentResponse.candidates.first?.safetyRatings {
|
|
SafetyRatingsSection(ratings: ratings)
|
|
}
|
|
|
|
case let GenerateContentError.responseStoppedEarly(
|
|
reason: finishReason,
|
|
response: generateContentResponse
|
|
):
|
|
|
|
Section("Error Type") {
|
|
Text("Response stopped early")
|
|
}
|
|
|
|
Section("Details") {
|
|
SubtitleFormRow(title: "Reason for finishing early", value: finishReason.rawValue)
|
|
|
|
if let text = generateContentResponse.text {
|
|
SubtitleMarkdownFormRow(title: "Last chunk for the response", value: text)
|
|
}
|
|
}
|
|
|
|
if let ratings = generateContentResponse.candidates.first?.safetyRatings {
|
|
SafetyRatingsSection(ratings: ratings)
|
|
}
|
|
|
|
default:
|
|
Section("Error Type") {
|
|
Text("Some other error")
|
|
}
|
|
|
|
Section("Details") {
|
|
SubtitleFormRow(title: "Error description", value: error.localizedDescription)
|
|
}
|
|
}
|
|
}
|
|
.navigationTitle("Error details")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
}
|
|
}
|
|
|
|
#Preview("Response Stopped Early") {
|
|
let error = GenerateContentError.responseStoppedEarly(
|
|
reason: .maxTokens,
|
|
response: GenerateContentResponse(candidates: [
|
|
CandidateResponse(content: ModelContent(role: "model", [
|
|
"""
|
|
A _hypothetical_ model response.
|
|
Cillum ex aliqua amet aliquip labore amet eiusmod consectetur reprehenderit sit commodo.
|
|
""",
|
|
]),
|
|
safetyRatings: [
|
|
SafetyRating(category: .dangerousContent, probability: .high),
|
|
SafetyRating(category: .harassment, probability: .low),
|
|
SafetyRating(category: .hateSpeech, probability: .low),
|
|
SafetyRating(category: .sexuallyExplicit, probability: .low),
|
|
],
|
|
finishReason: FinishReason.maxTokens,
|
|
citationMetadata: nil),
|
|
],
|
|
promptFeedback: nil)
|
|
)
|
|
|
|
return ErrorDetailsView(error: error)
|
|
}
|
|
|
|
#Preview("Prompt Blocked") {
|
|
let error = GenerateContentError.promptBlocked(
|
|
response: GenerateContentResponse(candidates: [
|
|
CandidateResponse(content: ModelContent(role: "model", [
|
|
"""
|
|
A _hypothetical_ model response.
|
|
Cillum ex aliqua amet aliquip labore amet eiusmod consectetur reprehenderit sit commodo.
|
|
""",
|
|
]),
|
|
safetyRatings: [
|
|
SafetyRating(category: .dangerousContent, probability: .high),
|
|
SafetyRating(category: .harassment, probability: .low),
|
|
SafetyRating(category: .hateSpeech, probability: .low),
|
|
SafetyRating(category: .sexuallyExplicit, probability: .low),
|
|
],
|
|
finishReason: FinishReason.other,
|
|
citationMetadata: nil),
|
|
],
|
|
promptFeedback: nil)
|
|
)
|
|
|
|
return ErrorDetailsView(error: error)
|
|
}
|