Skip to content

Commit

Permalink
ios: Support multi-language (#220)
Browse files Browse the repository at this point in the history
  • Loading branch information
ZongZiWang authored Jul 29, 2023
1 parent cd87327 commit c65a0da
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ struct InteractiveView: View {
VoiceMessageView(openMic: openMic,
messages: $messages,
state: $voiceState,
speechRecognizer: SpeechRecognizer(locale: preferenceSettings.languageOption.locale),
onUpdateUserMessage: { message in
if messages.last?.role == .user {
messages[messages.count - 1].content = message
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ actor SpeechRecognizer: ObservableObject {
Initializes a new speech recognizer. If this is the first time you've used the class, it
requests access to the speech recognizer and the microphone.
*/
init() {
recognizer = SFSpeechRecognizer()
init(locale: Locale = .current) {
recognizer = SFSpeechRecognizer(locale: locale)
guard recognizer != nil else {
transcribe(RecognizerError.nilRecognizer)
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ struct VoiceMessageView: View {
let openMic: Bool
@Binding var messages: [ChatMessage]
@Binding var state: VoiceState
@StateObject var speechRecognizer = SpeechRecognizer()
@StateObject var speechRecognizer: SpeechRecognizer

@State private var isInputUpdated: Bool = true
@State private var timer: Timer? = nil
Expand Down Expand Up @@ -325,6 +325,7 @@ struct VoiceMessageView_Previews: PreviewProvider {
ChatMessage(id: UUID(), role: .assistant, content: "Well thank you, Karina! I like your nam too. Now tell me, where do you live?")
]),
state: .constant(.idle(streamingEnded: true)),
speechRecognizer: SpeechRecognizer(),
onUpdateUserMessage: { _ in },
onSendUserMessage: { _ in },
onTapVoiceButton: { })
Expand Down
13 changes: 9 additions & 4 deletions client/mobile/ios/rac/rac/Network/WebSocketClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ protocol WebSocket: NSObject, ObservableObject {
var onStringReceived: ((String) -> Void)? { get set }
var onDataReceived: ((Data) -> Void)? { get set }
var onErrorReceived: ((Error) -> Void)? { get set }
func connectSession(llmOption: LlmOption, characterId: String, userId: String?, token: String?)
func connectSession(languageOption: LanguageOption, llmOption: LlmOption, characterId: String, userId: String?, token: String?)
func reconnectSession()
func closeSession()
func send(message: String)
Expand All @@ -47,6 +47,7 @@ class WebSocketClient: NSObject, WebSocket, URLSessionWebSocketDelegate {

private lazy var session = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue())

private var lastUsedLanguageOption: LanguageOption = .english
private var lastUsedLlmOption: LlmOption = .gpt35
private var lastUsedCharacterId: String = ""
private var lastUsedUserId: String? = nil
Expand Down Expand Up @@ -86,7 +87,8 @@ class WebSocketClient: NSObject, WebSocket, URLSessionWebSocketDelegate {
super.init()
}

func connectSession(llmOption: LlmOption, characterId: String, userId: String?, token: String?) {
func connectSession(languageOption: LanguageOption, llmOption: LlmOption, characterId: String, userId: String?, token: String?) {
lastUsedLanguageOption = languageOption
lastUsedLlmOption = llmOption
lastUsedCharacterId = characterId
// TODO: Use userId once it's ready
Expand All @@ -96,6 +98,7 @@ class WebSocketClient: NSObject, WebSocket, URLSessionWebSocketDelegate {
connectWebSocket(session: session,
serverUrl: serverUrl,
characterId: characterId,
languageOption: languageOption,
llmOption: llmOption,
clientId: clientId,
token: token)
Expand All @@ -105,6 +108,7 @@ class WebSocketClient: NSObject, WebSocket, URLSessionWebSocketDelegate {
connectWebSocket(session: session,
serverUrl: serverUrl,
characterId: lastUsedCharacterId,
languageOption: lastUsedLanguageOption,
llmOption: lastUsedLlmOption,
clientId: lastUsedUserId ?? String(Int.random(in: 0...1010000000)),
token: lastUsedToken)
Expand All @@ -113,6 +117,7 @@ class WebSocketClient: NSObject, WebSocket, URLSessionWebSocketDelegate {
private func connectWebSocket(session: URLSession,
serverUrl: URL,
characterId: String,
languageOption: LanguageOption,
llmOption: LlmOption,
clientId: String,
token: String?) {
Expand All @@ -122,7 +127,7 @@ class WebSocketClient: NSObject, WebSocket, URLSessionWebSocketDelegate {
lastConnectingDate = Date()

let wsScheme = serverUrl.scheme == "https" ? "wss" : "ws"
let wsPath = "\(wsScheme)://\(serverUrl.host ?? "")\(serverUrl.port.flatMap { ":\($0)" } ?? "")/ws/\(clientId)?platform=mobile&character_id=\(characterId)&llm_model=\(llmOption.rawValue)&token=\(token ?? "")"
let wsPath = "\(wsScheme)://\(serverUrl.host ?? "")\(serverUrl.port.flatMap { ":\($0)" } ?? "")/ws/\(clientId)?platform=mobile&language=\(languageOption.rawValue)&character_id=\(characterId)&llm_model=\(llmOption.rawValue)&token=\(token ?? "")"
print("Connecting websocket: \(wsPath)")
webSocket = session.webSocketTask(with: URL(string: wsPath)!)
webSocket.resume()
Expand Down Expand Up @@ -296,7 +301,7 @@ class MockWebSocket: NSObject, WebSocket {

var onErrorReceived: ((Error) -> Void)?

func connectSession(llmOption: LlmOption, characterId: String, userId: String?, token: String?) {
func connectSession(languageOption: LanguageOption, llmOption: LlmOption, characterId: String, userId: String?, token: String?) {
}

func reconnectSession() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class PreferenceSettings: ObservableObject {
static let hapticFeedbackKey = "enableHapticFeedback"
static let useSearch = "useSearch"
static let llmOptionKey = "largeLanguageModel"
static let languageOption = "languageOption"
static let includedCommunityCharacterIds = "includedCommunityCharacterIds"
}

Expand All @@ -38,6 +39,13 @@ class PreferenceSettings: ObservableObject {
print("Saved large language model preference: \(llmOption.displayName)")
}
}
@Published var languageOption: LanguageOption = LanguageOption(rawValue: UserDefaults.standard.string(forKey: Constants.languageOption) ?? "en-US") ?? .english {
didSet {
guard languageOption != oldValue else { return }
UserDefaults.standard.set(languageOption.rawValue, forKey: Constants.languageOption)
print("Saved language preference: \(languageOption.displayName)")
}
}
@Published var includedCommunityCharacterIds: [String] = (UserDefaults.standard.string(forKey: Constants.includedCommunityCharacterIds) ?? "").split(separator: ",").map { String($0) } {
didSet {
guard includedCommunityCharacterIds != oldValue else { return }
Expand Down
55 changes: 55 additions & 0 deletions client/mobile/ios/rac/rac/Welcome/Settings/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,44 @@ enum LlmOption: RawRepresentable, Hashable, CaseIterable, Identifiable, Codable
}
}

enum LanguageOption: RawRepresentable, Hashable, CaseIterable, Identifiable, Codable {

case english, spanish

init?(rawValue: String) {
for option in LanguageOption.allCases {
if rawValue == option.rawValue {
self = option
return
}
}
return nil
}

var id: String { rawValue }
var rawValue: String {
switch self {
case .english:
return "en-US"
case .spanish:
return "es-ES"
}
}

var displayName: String {
switch self {
case .english:
return "English"
case .spanish:
return "Spanish"
}
}

var locale: Locale {
Locale(identifier: rawValue)
}
}

struct SettingsView: View {
@EnvironmentObject private var userSettings: UserSettings
@EnvironmentObject private var preferenceSettings: PreferenceSettings
Expand Down Expand Up @@ -98,6 +136,23 @@ struct SettingsView: View {
Font.custom("Prompt", size: 18).weight(.medium)
)

Text("Conversation Language?")
.font(
Font.custom("Prompt", size: 16)
)

Picker("Conversation Language", selection: $preferenceSettings.languageOption) {
ForEach(LanguageOption.allCases) { languageOption in
Text(languageOption.displayName)
.font(
Font.custom("Prompt", size: 16)
)
.tag(languageOption)
}
}
.padding(.bottom, 2)
.pickerStyle(.segmented)

Text("LLM Model?")
.font(
Font.custom("Prompt", size: 16)
Expand Down
11 changes: 9 additions & 2 deletions client/mobile/ios/rac/rac/Welcome/WelcomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ struct WelcomeView: View {
VStack {
Button {
if webSocketConnectionStatusObserver.status == .disconnected, let characterId = character?.id {
webSocket.connectSession(llmOption: preferenceSettings.llmOption,
webSocket.connectSession(languageOption: preferenceSettings.languageOption,
llmOption: preferenceSettings.llmOption,
characterId: characterId,
userId: userSettings.userId,
token: userSettings.userToken)
Expand Down Expand Up @@ -122,6 +123,11 @@ struct WelcomeView: View {
reconnectWebSocket(characterId: characterId)
}
}
.onChange(of: preferenceSettings.languageOption) { newValue in
if let characterId = character?.id {
reconnectWebSocket(characterId: characterId)
}
}
.onChange(of: userSettings.isLoggedIn) { newValue in
if let characterId = character?.id {
reconnectWebSocket(characterId: characterId)
Expand All @@ -138,7 +144,8 @@ struct WelcomeView: View {
webSocket.onConnectionChanged = { status in
self.webSocketConnectionStatusObserver.update(status: webSocket.status)
}
webSocket.connectSession(llmOption: preferenceSettings.llmOption,
webSocket.connectSession(languageOption: preferenceSettings.languageOption,
llmOption: preferenceSettings.llmOption,
characterId: characterId,
userId: userSettings.userId,
token: userSettings.userToken)
Expand Down

0 comments on commit c65a0da

Please sign in to comment.