Archived
feat: LiveKit voice session, control channel, transcripts (Phase 2)
- VoiceSession: token->connect with fresh-room retry on timeout, agent discovery, agent_ready wait, mic enable/mute, text send over lk.chat - ControlChannel: outbound set_voice/set_model/get_models/set_mode/ media_state; inbound agent_ready/models/model_changed/task_event - TranscriptHandler: transcription segments + transcript topic finals, partial/final dedupe per role - ConversationView/OrbView: status orb, mic/text-mode toggles, composer - ServerConfig: plain ws:// signaling for private IPs (self-signed TLS not injectable into SDK WebSocket); Info.plist local networking
This commit is contained in:
1 parent
378b8cb36f
commit
4986b6d7dc
14 files changed
+900
-50
No files matched your search
@@ -0,0 +1,38 @@
|
||||
import Foundation
|
||||
import LiveKit
|
||||
|
||||
/// Observes the agent participant's audio level for UI state inference.
|
||||
@MainActor
|
||||
final class AgentMonitor: ObservableObject {
|
||||
@Published private(set) var agentSpeaking = false
|
||||
@Published private(set) var localSpeaking = false
|
||||
|
||||
private let session: VoiceSession
|
||||
private var pollTask: Task<Void, Never>?
|
||||
|
||||
init(session: VoiceSession) {
|
||||
self.session = session
|
||||
}
|
||||
|
||||
func start() {
|
||||
stop()
|
||||
pollTask = Task { [weak self] in
|
||||
while !Task.isCancelled {
|
||||
guard let self else { return }
|
||||
let remote = self.session.room.remoteParticipants.values.first(where: {
|
||||
$0.kind == .agent || $0.identity?.stringValue == "voice-assistant"
|
||||
})
|
||||
self.agentSpeaking = remote?.isSpeaking ?? false
|
||||
self.localSpeaking = self.session.room.localParticipant.isSpeaking
|
||||
try? await Task.sleep(for: .milliseconds(250))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stop() {
|
||||
pollTask?.cancel()
|
||||
pollTask = nil
|
||||
agentSpeaking = false
|
||||
localSpeaking = false
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user