From 89e9216caebe932b62a4d597eb1f6a7a72cc6d90 Mon Sep 17 00:00:00 2001 From: Piotr Wodecki <44680063+PiotrWodecki@users.noreply.github.com> Date: Mon, 9 Feb 2026 16:52:12 +0100 Subject: [PATCH] Revert "FCE-2750 Add support for agent image capture (#67)" This reverts commit 75ffe1ee8f661f950bae720c5fd2b03eea0e02f4. --- compile_proto.sh | 2 +- examples/multimodal/README.md | 41 ------ examples/multimodal/main.py | 35 ----- examples/multimodal/multimodal/__init__.py | 0 examples/multimodal/multimodal/agent.py | 136 ------------------ examples/multimodal/multimodal/config.py | 18 --- examples/multimodal/multimodal/notifier.py | 71 --------- examples/multimodal/multimodal/room.py | 36 ----- examples/multimodal/multimodal/session.py | 93 ------------ examples/multimodal/multimodal/worker.py | 31 ---- examples/multimodal/pyproject.toml | 14 -- examples/transcription/transcription/agent.py | 3 - fishjam/agent/__init__.py | 2 - fishjam/agent/agent.py | 20 +-- fishjam/events/_protos/fishjam/__init__.py | 48 +------ protos | 2 +- pyproject.toml | 1 - tests/Dockerfile | 1 - uv.lock | 19 --- 19 files changed, 5 insertions(+), 568 deletions(-) delete mode 100644 examples/multimodal/README.md delete mode 100644 examples/multimodal/main.py delete mode 100644 examples/multimodal/multimodal/__init__.py delete mode 100644 examples/multimodal/multimodal/agent.py delete mode 100644 examples/multimodal/multimodal/config.py delete mode 100644 examples/multimodal/multimodal/notifier.py delete mode 100644 examples/multimodal/multimodal/room.py delete mode 100644 examples/multimodal/multimodal/session.py delete mode 100644 examples/multimodal/multimodal/worker.py delete mode 100644 examples/multimodal/pyproject.toml diff --git a/compile_proto.sh b/compile_proto.sh index 987f81c..77f168c 100755 --- a/compile_proto.sh +++ b/compile_proto.sh @@ -11,5 +11,5 @@ printf "DONE\n\n" FILES=("protos/fishjam/agent_notifications.proto" "protos/fishjam/server_notifications.proto") printf "Compiling file: %s\n" "${FILES[@]}" -uv run protoc -I protos --python_betterproto_out="./fishjam/events/_protos" "${FILES[@]}" +protoc -I protos --python_betterproto_out="./fishjam/events/_protos" "${FILES[@]}" printf "\tDONE\n" diff --git a/examples/multimodal/README.md b/examples/multimodal/README.md deleted file mode 100644 index b1eaf3d..0000000 --- a/examples/multimodal/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Multimodal Demo - -A Fishjam SDK example that uses the Gemini Live API for multimodal real-time interaction. Users can speak to ask questions about captured video frames, and Gemini responds via voice. - -## How It Works - -1. A peer connects with both audio and video tracks -2. The agent periodically captures images from video tracks -3. When you speak ("What do you see?", "Describe this"), your audio and the captured images are sent to Gemini -4. Gemini analyzes the visual content and responds with voice - -## Setup - -1. Set environment variables: - -```bash -export FISHJAM_ID="your-fishjam-id" -export FISHJAM_MANAGEMENT_TOKEN="your-token" -export GOOGLE_API_KEY="your-gemini-api-key" -``` - -2. Optionally configure the image capture interval (default: 5 seconds): - -```bash -export IMAGE_CAPTURE_INTERVAL="3.0" -``` - -## Running - -```bash -cd examples/multimodal -uv run uvicorn main:app -``` - -## Usage - -1. The server will start on `http://localhost:8000` -2. GET `/` returns a peer token for connecting a browser client -3. Connect a peer with video and audio tracks -4. Speak questions about what your camera sees -5. Gemini will respond with voice analysis of the captured frames diff --git a/examples/multimodal/main.py b/examples/multimodal/main.py deleted file mode 100644 index 1ea294a..0000000 --- a/examples/multimodal/main.py +++ /dev/null @@ -1,35 +0,0 @@ -from contextlib import asynccontextmanager -from typing import Annotated - -from fastapi import Depends, FastAPI -from multimodal.notifier import make_notifier -from multimodal.room import RoomService, fishjam -from multimodal.worker import async_worker - -_room_service: RoomService | None = None - - -def get_room_service(): - if not _room_service: - raise RuntimeError("Application skipped lifespan events!") - return _room_service - - -@asynccontextmanager -async def lifespan(_app: FastAPI): - async with async_worker() as worker: - global _room_service - _room_service = RoomService(worker) - notifier = make_notifier(_room_service) - worker.run_in_background(notifier.connect()) - - yield - - -app = FastAPI(lifespan=lifespan) - - -@app.get("/") -def get_peer(room_service: Annotated[RoomService, Depends(get_room_service)]): - _peer, token = fishjam.create_peer(room_service.get_room().id) - return token diff --git a/examples/multimodal/multimodal/__init__.py b/examples/multimodal/multimodal/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/examples/multimodal/multimodal/agent.py b/examples/multimodal/multimodal/agent.py deleted file mode 100644 index 00838b3..0000000 --- a/examples/multimodal/multimodal/agent.py +++ /dev/null @@ -1,136 +0,0 @@ -import asyncio - -from fishjam.agent import Agent, AgentSession, IncomingTrackData, IncomingTrackImage -from fishjam.integrations.gemini import GeminiIntegration - -from .config import IMAGE_CAPTURE_INTERVAL -from .session import MultimodalSession -from .worker import BackgroundWorker - - -class MultimodalAgent: - def __init__(self, room_id: str, agent: Agent, worker: BackgroundWorker): - self._room_id = room_id - self._agent = agent - self._worker = worker - self._task: asyncio.Task[None] | None = None - self._capture_task: asyncio.Task[None] | None = None - - # Session management per peer - self._sessions: dict[str, MultimodalSession] = {} - - # Track caching: peer_id -> set of video track_ids - self._peer_tracks: dict[str, set[str]] = {} - # Reverse lookup: track_id -> peer_id - self._track_to_peer: dict[str, str] = {} - - # Agent session reference for capturing images and sending audio - self._agent_session: AgentSession | None = None - - async def _start(self): - async with self._agent.connect() as session: - self._agent_session = session - - # Create output track for Gemini audio responses - - self._output_track = await session.add_track( - GeminiIntegration.GEMINI_OUTPUT_AUDIO_SETTINGS - ) - print(f"Agent connected to room {self._room_id}") - - async for message in session.receive(): - match message: - case IncomingTrackImage(track_id=track_id, data=data): - peer_id = self._track_to_peer.get(track_id) - if peer_id and peer_id in self._sessions: - self._sessions[peer_id].send_image(data) - print(f"Sent image from {track_id} to {peer_id}") - - case IncomingTrackData(peer_id=peer_id, data=data): - if peer_id in self._sessions: - self._sessions[peer_id].send_audio(data) - - self._agent_session = None - print(f"Agent disconnected from room {self._room_id}") - - async def _periodic_capture(self): - while True: - await asyncio.sleep(IMAGE_CAPTURE_INTERVAL) - - if not self._agent_session: - continue - - # Capture images from all known video tracks - for track_id in self._track_to_peer.keys(): - try: - await self._agent_session.capture_image(track_id) - print(f"Requested image capture for track {track_id}") - except Exception as e: - print(f"Error capturing image from track {track_id}: {e}") - - def _handle_audio_response(self, peer_id: str, audio: bytes): - if self._output_track: - self._worker.run_in_background(self._output_track.send_chunk(audio)) - - def on_peer_enter(self, peer_id: str): - if peer_id in self._sessions: - return - - # Initialize track cache for this peer - self._peer_tracks[peer_id] = set() - - # Start agent connection if this is the first peer - if len(self._sessions) == 0: - self._task = self._worker.run_in_background(self._start()) - capture_coro = self._periodic_capture() - self._capture_task = self._worker.run_in_background(capture_coro) - - # Create multimodal session for this peer - def on_audio(audio: bytes, pid: str = peer_id): - self._handle_audio_response(pid, audio) - - session = MultimodalSession(on_audio) - self._sessions[peer_id] = session - self._worker.run_in_background(session.start(peer_id)) - - def on_peer_leave(self, peer_id: str): - if peer_id not in self._sessions: - return - - # Clean up track cache - if peer_id in self._peer_tracks: - for track_id in self._peer_tracks[peer_id]: - self._track_to_peer.pop(track_id, None) - del self._peer_tracks[peer_id] - - # End the session - self._sessions.pop(peer_id).end() - - # Stop agent if no more sessions - if len(self._sessions) == 0: - if self._task is not None: - self._task.cancel() - self._task = None - if self._capture_task is not None: - self._capture_task.cancel() - self._capture_task = None - - def on_track_added(self, peer_id: str, track_id: str, is_video: bool): - if not is_video: - return - - if peer_id not in self._peer_tracks: - self._peer_tracks[peer_id] = set() - - self._peer_tracks[peer_id].add(track_id) - self._track_to_peer[track_id] = peer_id - print(f"Added video track {track_id} for peer {peer_id}") - - def on_track_removed(self, peer_id: str, track_id: str, is_video: bool): - if not is_video: - return - - if peer_id in self._peer_tracks: - self._peer_tracks[peer_id].discard(track_id) - self._track_to_peer.pop(track_id, None) - print(f"Removed video track {track_id} for peer {peer_id}") diff --git a/examples/multimodal/multimodal/config.py b/examples/multimodal/multimodal/config.py deleted file mode 100644 index 1cb5c2b..0000000 --- a/examples/multimodal/multimodal/config.py +++ /dev/null @@ -1,18 +0,0 @@ -import os - -from google.genai.types import LiveConnectConfigDict, Modality - -FISHJAM_ID = os.getenv("FISHJAM_ID", "") -FISHJAM_TOKEN = os.environ["FISHJAM_MANAGEMENT_TOKEN"] - -MULTIMODAL_MODEL = "gemini-2.5-flash-native-audio-preview-12-2025" - -MULTIMODAL_CONFIG: LiveConnectConfigDict = { - "response_modalities": [Modality.AUDIO], - "thinking_config": { - "include_thoughts": False, - }, -} - -# Interval in seconds between image captures -IMAGE_CAPTURE_INTERVAL = float(os.getenv("IMAGE_CAPTURE_INTERVAL", "5.0")) diff --git a/examples/multimodal/multimodal/notifier.py b/examples/multimodal/multimodal/notifier.py deleted file mode 100644 index 53cef64..0000000 --- a/examples/multimodal/multimodal/notifier.py +++ /dev/null @@ -1,71 +0,0 @@ -from fishjam import FishjamNotifier -from fishjam.events import ( - ServerMessagePeerConnected, - ServerMessagePeerDisconnected, - ServerMessagePeerType, - ServerMessageTrackAdded, - ServerMessageTrackRemoved, - TrackType, -) -from fishjam.events.allowed_notifications import AllowedNotification - -from .config import FISHJAM_ID, FISHJAM_TOKEN -from .room import RoomService - - -def make_notifier(room_service: RoomService): - notifier = FishjamNotifier( - FISHJAM_ID, - FISHJAM_TOKEN, - ) - - @notifier.on_server_notification - def _(notification: AllowedNotification): - match notification: - case ServerMessagePeerConnected( - peer_id=peer_id, - room_id=room_id, - peer_type=ServerMessagePeerType.PEER_TYPE_WEBRTC, - ): - handle_peer_connected(peer_id, room_id) - - case ServerMessagePeerDisconnected( - peer_id=peer_id, - room_id=room_id, - peer_type=ServerMessagePeerType.PEER_TYPE_WEBRTC, - ): - handle_peer_disconnected(peer_id, room_id) - - case ServerMessageTrackAdded( - peer_id=peer_id, - room_id=room_id, - track=track, - ): - handle_track_added(peer_id, room_id, track) - - case ServerMessageTrackRemoved( - peer_id=peer_id, - room_id=room_id, - track=track, - ): - handle_track_removed(peer_id, room_id, track) - - def handle_peer_connected(peer_id: str, room_id: str): - if room_id == room_service.room.id: - room_service.agent.on_peer_enter(peer_id) - - def handle_peer_disconnected(peer_id: str, room_id: str): - if room_id == room_service.room.id: - room_service.agent.on_peer_leave(peer_id) - - def handle_track_added(peer_id: str, room_id: str, track): - if room_id == room_service.room.id: - is_video = track.type == TrackType.TRACK_TYPE_VIDEO - room_service.agent.on_track_added(peer_id, track.id, is_video) - - def handle_track_removed(peer_id: str, room_id: str, track): - if room_id == room_service.room.id: - is_video = track.type == TrackType.TRACK_TYPE_VIDEO - room_service.agent.on_track_removed(peer_id, track.id, is_video) - - return notifier diff --git a/examples/multimodal/multimodal/room.py b/examples/multimodal/multimodal/room.py deleted file mode 100644 index 6049327..0000000 --- a/examples/multimodal/multimodal/room.py +++ /dev/null @@ -1,36 +0,0 @@ -from fishjam import AgentOptions, FishjamClient, Room -from fishjam.errors import NotFoundError -from fishjam.integrations.gemini import GeminiIntegration - -from .agent import MultimodalAgent -from .config import FISHJAM_ID, FISHJAM_TOKEN -from .worker import BackgroundWorker - -fishjam = FishjamClient(FISHJAM_ID, FISHJAM_TOKEN) - - -class RoomService: - def __init__(self, worker: BackgroundWorker): - self._worker = worker - self._create_room() - - def get_room(self) -> Room: - try: - self.room = fishjam.get_room(self.room.id) - except NotFoundError: - self._create_room() - return self.room - - def _create_room(self): - self.room = fishjam.create_room() - self._create_agent() - - def _create_agent(self): - self.agent = MultimodalAgent( - self.room.id, - fishjam.create_agent( - self.room.id, - AgentOptions(output=GeminiIntegration.GEMINI_INPUT_AUDIO_SETTINGS), - ), - self._worker, - ) diff --git a/examples/multimodal/multimodal/session.py b/examples/multimodal/multimodal/session.py deleted file mode 100644 index 29c4b3b..0000000 --- a/examples/multimodal/multimodal/session.py +++ /dev/null @@ -1,93 +0,0 @@ -import asyncio -from asyncio import Event, Queue, TaskGroup -from typing import Callable - -from google.genai.live import AsyncSession -from google.genai.types import Blob - -from fishjam.integrations.gemini import GeminiIntegration - -from .config import MULTIMODAL_CONFIG, MULTIMODAL_MODEL - - -class MultimodalSession: - def __init__(self, on_audio_response: Callable[[bytes], None]): - self._gemini = GeminiIntegration.create_client() - self._audio_queue: Queue[bytes] = Queue() - self._image_queue: Queue[bytes] = Queue() - self._end_event = Event() - self._model = MULTIMODAL_MODEL - self._on_audio = on_audio_response - - async def start(self, peer_id: str): - async with self._gemini.aio.live.connect( - model=self._model, - config=MULTIMODAL_CONFIG, - ) as session: - async with TaskGroup() as tg: - send_task = tg.create_task(self._send_loop(session)) - recv_task = tg.create_task(self._recv_loop(session)) - - print(f"Started multimodal session for peer {peer_id}") - - await self._end_event.wait() - - send_task.cancel() - recv_task.cancel() - print(f"Stopped multimodal session for peer {peer_id}") - - def send_audio(self, audio: bytes): - self._audio_queue.put_nowait(audio) - - def send_image(self, image: bytes): - self._image_queue.put_nowait(image) - - def end(self): - self._end_event.set() - - async def _send_loop(self, session: AsyncSession): - while True: - audio_task = asyncio.create_task(self._audio_queue.get()) - image_task = asyncio.create_task(self._image_queue.get()) - - done, pending = await asyncio.wait( - [audio_task, image_task], - return_when=asyncio.FIRST_COMPLETED, - ) - - for task in pending: - task.cancel() - - for task in done: - if task is audio_task: - audio_frame = task.result() - await session.send_realtime_input( - audio=Blob( - data=audio_frame, - mime_type=GeminiIntegration.GEMINI_AUDIO_MIME_TYPE, - ) - ) - elif task is image_task: - print("sending video") - image_data = task.result() - await session.send_realtime_input( - media=Blob( - data=image_data, - mime_type="image/jpeg", - ) - ) - - async def _recv_loop(self, session: AsyncSession): - while True: - async for message in session.receive(): - if ( - (content := message.server_content) - and (model_turn := content.model_turn) - and (parts := model_turn.parts) - ): - for part in parts: - if part.inline_data and part.inline_data.data: - self._on_audio(part.inline_data.data) - if content and content.model_turn: - if content.turn_complete: - print("\n--- Turn Finished ---") diff --git a/examples/multimodal/multimodal/worker.py b/examples/multimodal/multimodal/worker.py deleted file mode 100644 index 3bff365..0000000 --- a/examples/multimodal/multimodal/worker.py +++ /dev/null @@ -1,31 +0,0 @@ -from asyncio import Task, TaskGroup -from contextlib import asynccontextmanager -from typing import Any, Coroutine - - -class BackgroundWorker: - def __init__(self, tg: TaskGroup) -> None: - self._tg = tg - self._tasks: set[Task[None]] = set() - - def run_in_background(self, coro: Coroutine[Any, Any, None]): - task = self._tg.create_task(coro) - task.add_done_callback(self._remove_task) - self._tasks.add(task) - return task - - def _remove_task(self, task: Task[None]): - self._tasks.discard(task) - - def cleanup(self): - for task in self._tasks: - task.cancel() - self._tasks = set() - - -@asynccontextmanager -async def async_worker(): - async with TaskGroup() as tg: - worker = BackgroundWorker(tg) - yield worker - worker.cleanup() diff --git a/examples/multimodal/pyproject.toml b/examples/multimodal/pyproject.toml deleted file mode 100644 index bc4676e..0000000 --- a/examples/multimodal/pyproject.toml +++ /dev/null @@ -1,14 +0,0 @@ -[project] -name = "multimodal" -version = "0.1.0" -description = "Fishjam multimodal demo with Gemini Live API" -readme = "README.md" -requires-python = ">=3.11" -dependencies = [ - "fastapi[standard]==0.116.0", - "fishjam-server-sdk", - "google-genai>=1.31.0", -] - -[tool.uv.sources] -fishjam-server-sdk = { workspace = true } diff --git a/examples/transcription/transcription/agent.py b/examples/transcription/transcription/agent.py index a6d4bd6..33e908e 100644 --- a/examples/transcription/transcription/agent.py +++ b/examples/transcription/transcription/agent.py @@ -1,7 +1,6 @@ import asyncio from fishjam.agent import Agent -from fishjam.agent.agent import IncomingTrackData from transcription.worker import BackgroundWorker from .transcription import TranscriptionSession @@ -20,8 +19,6 @@ async def _start(self): print(f"Agent connected to room {self._room_id}") async for track_data in session.receive(): - if not isinstance(track_data, IncomingTrackData): - continue if track_data.peer_id not in self._sessions: return self._sessions[track_data.peer_id].transcribe(track_data.data) diff --git a/fishjam/agent/__init__.py b/fishjam/agent/__init__.py index de6cfa0..b4d4299 100644 --- a/fishjam/agent/__init__.py +++ b/fishjam/agent/__init__.py @@ -2,7 +2,6 @@ Agent, AgentSession, IncomingTrackData, - IncomingTrackImage, OutgoingAudioTrackOptions, OutgoingTrack, ) @@ -14,7 +13,6 @@ "AgentSession", "AgentAuthError", "IncomingTrackData", - "IncomingTrackImage", "OutgoingTrack", "OutgoingAudioTrackOptions", ] diff --git a/fishjam/agent/agent.py b/fishjam/agent/agent.py index da4c967..56ce707 100644 --- a/fishjam/agent/agent.py +++ b/fishjam/agent/agent.py @@ -16,16 +16,14 @@ AgentRequestAddTrack, AgentRequestAddTrackCodecParameters, AgentRequestAuthRequest, - AgentRequestCaptureImage, AgentRequestInterruptTrack, AgentResponse, ) from fishjam.events._protos.fishjam import AgentRequestTrackData as OutgoingTrackData from fishjam.events._protos.fishjam import AgentResponseTrackData as IncomingTrackData -from fishjam.events._protos.fishjam import AgentResponseTrackImage as IncomingTrackImage from fishjam.events._protos.fishjam.notifications import Track, TrackEncoding, TrackType -IncomingAgentMessage = IncomingTrackData | IncomingTrackImage +IncomingAgentMessage = IncomingTrackData @dataclass @@ -122,8 +120,6 @@ async def receive(self) -> AsyncIterator[IncomingAgentMessage]: match msg: case IncomingTrackData() as content: yield content - case IncomingTrackImage() as content: - yield content async def add_track(self, options: OutgoingAudioTrackOptions) -> OutgoingTrack: """Adds a track to the connected agent with the specified options. @@ -153,20 +149,6 @@ async def add_track(self, options: OutgoingAudioTrackOptions) -> OutgoingTrack: await self._send(message) return OutgoingTrack(id=track_id, session=self, options=options) - async def capture_image(self, track_id: str): - """Requests a image capture from a remote track. - - The captured image will be delivered as an - :class:`IncomingTrackImage` message through :func:`receive`. - - Args: - track_id: The identifier of the track to capture an image from. - """ - message = AgentRequest( - capture_image=AgentRequestCaptureImage(track_id=track_id) - ) - await self._send(message) - async def _send(self, message: AgentRequest): await self._ws.send(bytes(message), text=False) diff --git a/fishjam/events/_protos/fishjam/__init__.py b/fishjam/events/_protos/fishjam/__init__.py index b10d0a9..3d97433 100644 --- a/fishjam/events/_protos/fishjam/__init__.py +++ b/fishjam/events/_protos/fishjam/__init__.py @@ -38,9 +38,6 @@ class AgentRequest(betterproto.Message): interrupt_track: "AgentRequestInterruptTrack" = betterproto.message_field( 5, group="content" ) - capture_image: "AgentRequestCaptureImage" = betterproto.message_field( - 6, group="content" - ) @dataclass(eq=False, repr=False) @@ -98,11 +95,6 @@ class AgentRequestInterruptTrack(betterproto.Message): track_id: str = betterproto.string_field(1) -@dataclass(eq=False, repr=False) -class AgentRequestCaptureImage(betterproto.Message): - track_id: str = betterproto.string_field(1) - - @dataclass(eq=False, repr=False) class AgentResponse(betterproto.Message): """Defines any type of message passed from Fishjam to agent peer""" @@ -111,9 +103,6 @@ class AgentResponse(betterproto.Message): 1, group="content" ) track_data: "AgentResponseTrackData" = betterproto.message_field(2, group="content") - track_image: "AgentResponseTrackImage" = betterproto.message_field( - 3, group="content" - ) @dataclass(eq=False, repr=False) @@ -132,13 +121,6 @@ class AgentResponseTrackData(betterproto.Message): data: bytes = betterproto.bytes_field(3) -@dataclass(eq=False, repr=False) -class AgentResponseTrackImage(betterproto.Message): - track_id: str = betterproto.string_field(1) - content_type: str = betterproto.string_field(2) - data: bytes = betterproto.bytes_field(3) - - @dataclass(eq=False, repr=False) class ServerMessage(betterproto.Message): """Defines any type of message passed between FJ and server peer""" @@ -221,12 +203,6 @@ class ServerMessage(betterproto.Message): streamer_disconnected: "ServerMessageStreamerDisconnected" = ( betterproto.message_field(27, group="content") ) - channel_added: "ServerMessageChannelAdded" = betterproto.message_field( - 28, group="content" - ) - channel_removed: "ServerMessageChannelRemoved" = betterproto.message_field( - 29, group="content" - ) def __post_init__(self) -> None: super().__post_init__() @@ -317,7 +293,7 @@ class ServerMessageAuthRequest(betterproto.Message): @dataclass(eq=False, repr=False) class ServerMessageSubscribeRequest(betterproto.Message): - """Request sent by peer to subscribe for certain message type""" + """Request sent by peer to subsribe for certain message type""" event_type: "ServerMessageEventType" = betterproto.enum_field(1) @@ -354,7 +330,7 @@ class ServerMessageHlsPlayable(betterproto.Message): @dataclass(eq=False, repr=False) class ServerMessageHlsUploaded(betterproto.Message): """ - Notification sent when the HLS recording is successfully uploaded to AWS S3 + Notification sent when the HLS recording is successfully uploded to AWS S3 """ room_id: str = betterproto.string_field(1) @@ -407,26 +383,6 @@ class ServerMessageTrackMetadataUpdated(betterproto.Message): track: "notifications.Track" = betterproto.message_field(4) -@dataclass(eq=False, repr=False) -class ServerMessageChannelAdded(betterproto.Message): - """Notification sent when a peer creates a channel""" - - room_id: str = betterproto.string_field(1) - peer_id: str = betterproto.string_field(2, group="endpoint_info") - component_id: str = betterproto.string_field(3, group="endpoint_info") - channel_id: str = betterproto.string_field(4) - - -@dataclass(eq=False, repr=False) -class ServerMessageChannelRemoved(betterproto.Message): - """Notification sent when a peer deletes a channel""" - - room_id: str = betterproto.string_field(1) - peer_id: str = betterproto.string_field(2, group="endpoint_info") - component_id: str = betterproto.string_field(3, group="endpoint_info") - channel_id: str = betterproto.string_field(4) - - @dataclass(eq=False, repr=False) class ServerMessageStreamConnected(betterproto.Message): """Notification sent when streamer successfully connects""" diff --git a/protos b/protos index 694aad3..40f4ab8 160000 --- a/protos +++ b/protos @@ -1 +1 @@ -Subproject commit 694aad31e504028cfebdeaf2bab317e4ad0b81b6 +Subproject commit 40f4ab8013644de2be5d7d7ff2652725935a2e92 diff --git a/pyproject.toml b/pyproject.toml index ba65512..1f8d304 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,7 +64,6 @@ members = [ ".", "examples/poet_chat", "examples/selective_subscription", - "examples/multimodal", ] [tool.hatch.build.targets.sdist] diff --git a/tests/Dockerfile b/tests/Dockerfile index f9e7cf5..52c6349 100644 --- a/tests/Dockerfile +++ b/tests/Dockerfile @@ -14,7 +14,6 @@ COPY pyproject.toml . COPY examples/transcription/pyproject.toml ./examples/transcription/ COPY examples/poet_chat/pyproject.toml ./examples/poet_chat/ COPY examples/selective_subscription/pyproject.toml ./examples/selective_subscription/ -COPY examples/multimodal/pyproject.toml ./examples/multimodal/ COPY uv.lock . diff --git a/uv.lock b/uv.lock index 37cada9..92efb07 100644 --- a/uv.lock +++ b/uv.lock @@ -5,7 +5,6 @@ requires-python = ">=3.11" [manifest] members = [ "fishjam-server-sdk", - "multimodal", "poet-chat", "selective-subscription-demo", "transcription", @@ -15,7 +14,6 @@ members = [ name = "aenum" version = "3.1.16" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/7a/61ed58e8be9e30c3fe518899cc78c284896d246d51381bab59b5db11e1f3/aenum-3.1.16.tar.gz", hash = "sha256:bfaf9589bdb418ee3a986d85750c7318d9d2839c1b1a1d6fe8fc53ec201cf140", size = 137693, upload-time = "2026-01-12T22:34:38.819Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e3/52/6ad8f63ec8da1bf40f96996d25d5b650fdd38f5975f8c813732c47388f18/aenum-3.1.16-py3-none-any.whl", hash = "sha256:9035092855a98e41b66e3d0998bd7b96280e85ceb3a04cc035636138a1943eaf", size = 165627, upload-time = "2025-04-25T03:17:58.89Z" }, ] @@ -1082,23 +1080,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317, upload-time = "2025-10-06T14:52:29.272Z" }, ] -[[package]] -name = "multimodal" -version = "0.1.0" -source = { virtual = "examples/multimodal" } -dependencies = [ - { name = "fastapi", extra = ["standard"] }, - { name = "fishjam-server-sdk" }, - { name = "google-genai" }, -] - -[package.metadata] -requires-dist = [ - { name = "fastapi", extras = ["standard"], specifier = "==0.116.0" }, - { name = "fishjam-server-sdk", editable = "." }, - { name = "google-genai", specifier = ">=1.31.0" }, -] - [[package]] name = "mypy-extensions" version = "1.1.0"