Support for app messages

This commit is contained in:
Moishe Lettvin
2024-03-18 10:08:41 -04:00
parent c6dfcb6f7a
commit 8e61fe8e36
3 changed files with 42 additions and 12 deletions

View File

@@ -95,9 +95,19 @@ class OpenAILLMContextFrame(Frame):
context: OpenAILLMContext context: OpenAILLMContext
class AppMessageQueueFrame(Frame): @dataclass()
class ReceivedAppMessageFrame(Frame):
message: Any message: Any
participantId: str sender: str
def __str__(self):
return f"ReceivedAppMessageFrame: sender: {self.sender}, message: {self.message}"
@dataclass()
class SendAppMessageFrame(Frame):
message: Any
participantId: str | None
class UserStartedSpeakingFrame(Frame): class UserStartedSpeakingFrame(Frame):

View File

@@ -8,11 +8,13 @@ import torch
import queue import queue
import threading import threading
import time import time
from typing import AsyncGenerator from typing import Any, AsyncGenerator
from enum import Enum from enum import Enum
from dailyai.pipeline.frame_processor import FrameProcessor from dailyai.pipeline.frame_processor import FrameProcessor
from dailyai.pipeline.frames import ( from dailyai.pipeline.frames import (
ReceivedAppMessageFrame,
SendAppMessageFrame,
AudioFrame, AudioFrame,
EndFrame, EndFrame,
ImageFrame, ImageFrame,
@@ -317,9 +319,10 @@ class BaseTransportService:
self._vad_state == VADState.STARTING self._vad_state == VADState.STARTING
and self._vad_starting_count >= self._vad_start_frames and self._vad_starting_count >= self._vad_start_frames
): ):
asyncio.run_coroutine_threadsafe( if self._loop:
self.receive_queue.put(UserStartedSpeakingFrame()), self._loop asyncio.run_coroutine_threadsafe(
) self.receive_queue.put(UserStartedSpeakingFrame()), self._loop
)
# self.interrupt() # self.interrupt()
self._vad_state = VADState.SPEAKING self._vad_state = VADState.SPEAKING
self._vad_starting_count = 0 self._vad_starting_count = 0
@@ -327,9 +330,10 @@ class BaseTransportService:
self._vad_state == VADState.STOPPING self._vad_state == VADState.STOPPING
and self._vad_stopping_count >= self._vad_stop_frames and self._vad_stopping_count >= self._vad_stop_frames
): ):
asyncio.run_coroutine_threadsafe( if self._loop:
self.receive_queue.put(UserStoppedSpeakingFrame()), self._loop asyncio.run_coroutine_threadsafe(
) self.receive_queue.put(UserStoppedSpeakingFrame()), self._loop
)
self._vad_state = VADState.QUIET self._vad_state = VADState.QUIET
self._vad_stopping_count = 0 self._vad_stopping_count = 0
@@ -375,6 +379,10 @@ class BaseTransportService:
def _set_images(self, images: list[bytes], start_frame=0): def _set_images(self, images: list[bytes], start_frame=0):
self._images = itertools.cycle(images) self._images = itertools.cycle(images)
def send_app_message(self, message: Any, participantId:str|None):
""" Child classes should override this to send a custom message to the room. """
pass
def _run_camera(self): def _run_camera(self):
try: try:
while not self._stop_threads.is_set(): while not self._stop_threads.is_set():
@@ -440,6 +448,8 @@ class BaseTransportService:
self._set_image(frame.image) self._set_image(frame.image)
elif isinstance(frame, SpriteFrame): elif isinstance(frame, SpriteFrame):
self._set_images(frame.images) self._set_images(frame.images)
elif isinstance(frame, SendAppMessageFrame):
self.send_app_message(frame.message, frame.participantId)
elif len(b): elif len(b):
self.write_frame_to_mic(bytes(b)) self.write_frame_to_mic(bytes(b))
b = bytearray() b = bytearray()

View File

@@ -6,8 +6,10 @@ import threading
import types import types
from functools import partial from functools import partial
from typing import Any
from dailyai.pipeline.frames import ( from dailyai.pipeline.frames import (
ReceivedAppMessageFrame,
TranscriptionQueueFrame, TranscriptionQueueFrame,
) )
@@ -124,6 +126,9 @@ class DailyTransportService(BaseTransportService, EventHandler):
def write_frame_to_mic(self, frame: bytes): def write_frame_to_mic(self, frame: bytes):
self.mic.write_frames(frame) self.mic.write_frames(frame)
def send_app_message(self, message: Any, participantId: str | None):
self.client.send_app_message(message, participantId)
def read_audio_frames(self, desired_frame_count): def read_audio_frames(self, desired_frame_count):
bytes = self._speaker.read_frames(desired_frame_count) bytes = self._speaker.read_frames(desired_frame_count)
return bytes return bytes
@@ -219,7 +224,7 @@ class DailyTransportService(BaseTransportService, EventHandler):
pass pass
def call_joined(self, join_data, client_error): def call_joined(self, join_data, client_error):
#self._logger.info(f"Call_joined: {join_data}, {client_error}") # self._logger.info(f"Call_joined: {join_data}, {client_error}")
pass pass
def dialout(self, number): def dialout(self, number):
@@ -243,8 +248,13 @@ class DailyTransportService(BaseTransportService, EventHandler):
if len(self.client.participants()) < self._min_others_count + 1: if len(self.client.participants()) < self._min_others_count + 1:
self._stop_threads.set() self._stop_threads.set()
def on_app_message(self, message, sender): def on_app_message(self, message:Any, sender:str):
pass if self._loop:
frame = ReceivedAppMessageFrame(message, sender)
print(frame)
asyncio.run_coroutine_threadsafe(
self.receive_queue.put(frame), self._loop
)
def on_transcription_message(self, message: dict): def on_transcription_message(self, message: dict):
if self._loop: if self._loop: