Skip to content

[FirebaseAI ] Integrate conversationkit #1745

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: peterfriese/firebase-ai-quickstart-refresh
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions firebaseai/ChatExample/Models/ChatMessage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,30 @@
import FirebaseAI
import Foundation

enum Participant {
public enum Participant {
case system
case user
}

struct ChatMessage: Identifiable, Equatable {
let id = UUID().uuidString
var message: String
var groundingMetadata: GroundingMetadata?
let participant: Participant
var pending = false
public struct ChatMessage: Identifiable, Equatable {
public let id = UUID().uuidString
public var message: String
public let participant: Participant
public var groundingMetadata: GroundingMetadata?
public var pending = false

static func pending(participant: Participant) -> ChatMessage {
public static func pending(participant: Participant) -> ChatMessage {
Self(message: "", participant: participant, pending: true)
}

// TODO(andrewheard): Add Equatable conformance to GroundingMetadata and remove this
static func == (lhs: ChatMessage, rhs: ChatMessage) -> Bool {
public static func == (lhs: ChatMessage, rhs: ChatMessage) -> Bool {
lhs.id == rhs.id && lhs.message == rhs.message && lhs.participant == rhs.participant && lhs
.pending == rhs.pending
}
}

extension ChatMessage {
public extension ChatMessage {
static var samples: [ChatMessage] = [
.init(message: "Hello. What can I do for you today?", participant: .system),
.init(message: "Show me a simple loop in Swift.", participant: .user),
Expand Down Expand Up @@ -71,7 +71,7 @@ extension ChatMessage {
static var sample = samples[0]
}

extension ChatMessage {
public extension ChatMessage {
static func from(_ modelContent: ModelContent) -> ChatMessage? {
// TODO: add non-text parts to message when multi-model support is added
let text = modelContent.parts.compactMap { ($0 as? TextPart)?.text }.joined()
Expand Down
77 changes: 77 additions & 0 deletions firebaseai/ChatExample/Screens/ChatScreen.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// 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 FirebaseAI
import SwiftUI

struct ChatScreen: View {
let firebaseService: FirebaseAI
@StateObject var viewModel: ChatViewModel

init(firebaseService: FirebaseAI, sample: Sample? = nil) {
self.firebaseService = firebaseService
_viewModel =
StateObject(wrappedValue: ChatViewModel(firebaseService: firebaseService,
sample: sample))
}

var body: some View {
NavigationStack {
ConversationView(messages: $viewModel.messages,
userPrompt: viewModel.initialPrompt) { message in
MessageView(message: message)
}
.disableAttachments()
.errorState(viewModel.error)
.onSendMessage { prompt in
Task {
await viewModel.sendMessage(prompt, streaming: true)
}
}
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button(action: newChat) {
Image(systemName: "square.and.pencil")
}
}
}
.navigationTitle(viewModel.title)
.navigationBarTitleDisplayMode(.inline)
}
}

private func newChat() {
viewModel.startNewChat()
}
}

struct ChatScreen_Previews: PreviewProvider {
struct ContainerView: View {
@StateObject var viewModel = ChatViewModel(firebaseService: FirebaseAI
.firebaseAI(), sample: nil) // Example service init

var body: some View {
ChatScreen(firebaseService: FirebaseAI.firebaseAI())
.onAppear {
viewModel.messages = ChatMessage.samples
}
}
}

static var previews: some View {
NavigationStack {
ChatScreen(firebaseService: FirebaseAI.firebaseAI())
}
}
}
145 changes: 0 additions & 145 deletions firebaseai/ChatExample/Screens/ConversationScreen.swift

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@
import FirebaseAI
import Foundation
import UIKit
import GenerativeAIUIComponents

@MainActor
class ConversationViewModel: ObservableObject {
class ChatViewModel: ObservableObject {
/// This array holds both the user's and the system's chat messages
@Published var messages = [ChatMessage]()

Expand Down
51 changes: 34 additions & 17 deletions firebaseai/ChatExample/Views/MessageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,24 +83,41 @@ struct ResponseTextView: View {
struct MessageView: View {
var message: ChatMessage

private var participantLabel: String {
message.participant == .user ? "User" : "Model"
}

var body: some View {
HStack {
if message.participant == .user {
Spacer()
}
MessageContentView(message: message)
.padding(10)
.background(message.participant == .system
? Color(UIColor.systemFill)
: Color(UIColor.systemBlue))
.roundedCorner(10,
corners: [
.topLeft,
.topRight,
message.participant == .system ? .bottomRight : .bottomLeft,
])
if message.participant == .system {
Spacer()
VStack(alignment: message.participant == .user ? .trailing : .leading, spacing: 4) {
// Sender label
Text(participantLabel)
.font(.caption2)
.fontWeight(.medium)
.foregroundColor(.secondary)
.textCase(.uppercase)
.padding(.horizontal, 8)
.padding(.vertical, 2)
.frame(maxWidth: .infinity, alignment: message.participant == .user ? .trailing : .leading)

// Message content
HStack {
if message.participant == .user {
Spacer()
}
MessageContentView(message: message)
.padding(10)
.background(message.participant == .system
? Color(UIColor.systemFill)
: Color(UIColor.systemBlue))
.roundedCorner(10,
corners: [
.topLeft,
.topRight,
message.participant == .system ? .bottomRight : .bottomLeft,
])
if message.participant == .system {
Spacer()
}
}
}
.listRowSeparator(.hidden)
Expand Down
Loading