diff --git a/Demo/DemoChat/Sources/MiscStore.swift b/Demo/DemoChat/Sources/MiscStore.swift index 4879d8e9..e2e732e5 100644 --- a/Demo/DemoChat/Sources/MiscStore.swift +++ b/Demo/DemoChat/Sources/MiscStore.swift @@ -19,6 +19,8 @@ public final class MiscStore: ObservableObject { self.openAIClient = openAIClient } + // MARK: Models + @MainActor func getModels() async { do { @@ -29,4 +31,64 @@ public final class MiscStore: ObservableObject { print(error.localizedDescription) } } + + // MARK: Moderations + + @Published var moderationConversation = Conversation(id: "", messages: []) + @Published var moderationConversationError: Error? + + @MainActor + func sendModerationMessage(_ message: Message) async { + moderationConversation.messages.append(message) + await completeModerationChat(message: message) + } + + @MainActor + func completeModerationChat(message: Message) async { + + moderationConversationError = nil + + do { + let response = try await openAIClient.moderations( + query: ModerationsQuery( + input: message.content, + model: .textModerationLatest + ) + ) + + let categoryResults = response.results + + let existingMessages = moderationConversation.messages + + func circleEmoji(for resultType: Bool) -> String { + resultType ? "🔴" : "🟢" + } + + for result in categoryResults { + let content = """ + \(circleEmoji(for: result.categories.hate)) Hate + \(circleEmoji(for: result.categories.hateThreatening)) Hate/Threatening + \(circleEmoji(for: result.categories.selfHarm)) Self-harm + \(circleEmoji(for: result.categories.sexual)) Sexual + \(circleEmoji(for: result.categories.sexualMinors)) Sexual/Minors + \(circleEmoji(for: result.categories.violence)) Violence + \(circleEmoji(for: result.categories.violenceGraphic)) Violence/Graphic + """ + + let message = Message( + id: response.id, + role: .assistant, + content: content, + createdAt: message.createdAt) + + if existingMessages.contains(message) { + continue + } + moderationConversation.messages.append(message) + } + + } catch { + moderationConversationError = error + } + } } diff --git a/Demo/DemoChat/Sources/UI/Misc/MiscView.swift b/Demo/DemoChat/Sources/UI/Misc/MiscView.swift index 88d1936e..cc69b4ac 100644 --- a/Demo/DemoChat/Sources/UI/Misc/MiscView.swift +++ b/Demo/DemoChat/Sources/UI/Misc/MiscView.swift @@ -21,6 +21,9 @@ public struct MiscView: View { NavigationLink("List Models", destination: ListModelsView(store: store)) NavigationLink("Retrieve Model", destination: RetrieveModelView()) } + Section(header: Text("Moderations")) { + NavigationLink("Moderation Chat", destination: ModerationChatView(store: store)) + } } .listStyle(.insetGrouped) .navigationTitle("Misc") diff --git a/Demo/DemoChat/Sources/UI/ModerationChatView.swift b/Demo/DemoChat/Sources/UI/ModerationChatView.swift new file mode 100644 index 00000000..0bb5e542 --- /dev/null +++ b/Demo/DemoChat/Sources/UI/ModerationChatView.swift @@ -0,0 +1,38 @@ +// +// ModerationChatView.swift +// DemoChat +// +// Created by Aled Samuel on 26/04/2023. +// + +import SwiftUI + +public struct ModerationChatView: View { + @ObservedObject var store: MiscStore + + @Environment(\.dateProviderValue) var dateProvider + @Environment(\.idProviderValue) var idProvider + + public init(store: MiscStore) { + self.store = store + } + + public var body: some View { + DetailView( + conversation: store.moderationConversation, + error: store.moderationConversationError, + sendMessage: { message in + Task { + await store.sendModerationMessage( + Message( + id: idProvider(), + role: .user, + content: message, + createdAt: dateProvider() + ) + ) + } + } + ) + } +} diff --git a/Sources/OpenAI/Public/Models/Models/Models.swift b/Sources/OpenAI/Public/Models/Models/Models.swift index c5a1141f..b96b662a 100644 --- a/Sources/OpenAI/Public/Models/Models/Models.swift +++ b/Sources/OpenAI/Public/Models/Models/Models.swift @@ -69,7 +69,7 @@ public extension Model { /// Almost as capable as the latest model, but slightly older. static let textModerationStable = "text-moderation-stable" - /// Most capable moderation model. Accuracy will be slighlty higher than the stable model. + /// Most capable moderation model. Accuracy will be slightly higher than the stable model. static let textModerationLatest = "text-moderation-latest" static let moderation = "text-moderation-001" }