Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a new gemini-demo workspace that provides a minimal end-to-end demo of a Fishjam video call with a Gemini Live voice agent, including a React (Vite) frontend, a Fastify+tRPC backend, and containerized deployment via Docker Compose + Nginx.
Changes:
- Add a Yarn-workspaces-based demo project with backend (Fastify + tRPC + Fishjam SDK + Gemini Live) and web (React + Vite) apps.
- Add Dockerfiles and
docker-compose.yml+nginx.confto run the demo as three services (backend/web/nginx). - Add TypeScript configs, Vite config, and lockfile for reproducible installs.
Reviewed changes
Copilot reviewed 34 out of 37 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| gemini-demo/yarn.lock | Adds the dependency lockfile for the demo workspace. |
| gemini-demo/package.json | Defines the Yarn workspace layout and build/typecheck scripts. |
| gemini-demo/.yarnrc.yml | Configures Yarn node-modules linker for the demo workspace. |
| gemini-demo/.gitignore | Ignores node_modules, dist, env, logs for the demo. |
| gemini-demo/.env.example | Example environment variables for backend + Vite. |
| gemini-demo/.dockerignore | Reduces Docker build context for the demo. |
| gemini-demo/README.md | Documents demo purpose, setup, and architecture. |
| gemini-demo/docker-compose.yml | Defines backend, web, and nginx services for the demo. |
| gemini-demo/nginx.conf | Reverse-proxy config for /api and frontend. |
| gemini-demo/backend/.yarnrc.yml | Yarn config for backend workspace. |
| gemini-demo/backend/package.json | Backend dependencies/scripts and type export for AppRouter. |
| gemini-demo/backend/tsconfig.json | Backend TS compiler settings for Node ESM build output. |
| gemini-demo/backend/Dockerfile | Multi-stage backend build/run container setup. |
| gemini-demo/backend/src/main.ts | Fastify server startup + tRPC HTTP + WS setup. |
| gemini-demo/backend/src/router.ts | tRPC router with getPeerToken and createAgent procedures. |
| gemini-demo/backend/src/config.ts | Env loading and validation for required backend config. |
| gemini-demo/backend/src/clients.ts | Initializes Fishjam and Gemini clients from config. |
| gemini-demo/backend/src/peers.ts | Room creation/lookup and peer token issuance. |
| gemini-demo/backend/src/agents.ts | Gemini Live session wiring to Fishjam agent tracks + tools. |
| gemini-demo/web/package.json | Web app dependencies and Vite/TS build scripts. |
| gemini-demo/web/index.html | Vite entry HTML for the web app. |
| gemini-demo/web/vite.config.ts | Vite config with dev proxy to backend under /api. |
| gemini-demo/web/tsconfig.json | TS project references for app + node configs. |
| gemini-demo/web/tsconfig.app.json | Web app TS compiler options and include paths. |
| gemini-demo/web/tsconfig.node.json | TS config for Vite config build/typechecking. |
| gemini-demo/web/Dockerfile | Multi-stage static build and runtime serving for web. |
| gemini-demo/web/src/vite-env.d.ts | Declares VITE_FISHJAM_ID env typing. |
| gemini-demo/web/src/trpc.ts | Sets up tRPC client with HTTP batch + WS split link. |
| gemini-demo/web/src/main.tsx | React entry point wiring FishjamProvider. |
| gemini-demo/web/src/App.tsx | Simple lobby/call view routing and leave handling. |
| gemini-demo/web/src/components/Lobby.tsx | UI for room/name input, peer token fetch, device init, join. |
| gemini-demo/web/src/components/CallView.tsx | In-call UI with peer tiles, controls, agent start modal. |
| gemini-demo/web/src/components/PeerTile.tsx | Video/audio tile for a peer. |
| gemini-demo/web/src/components/AgentTile.tsx | Agent tile rendering + audio playback and animation. |
| gemini-demo/web/src/components/SystemPromptModal.tsx | Modal to set agent system prompt. |
| gemini-demo/web/src/components/Toolbar.tsx | Header bar with room info and agent/leave actions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "dependencies": { | ||
| "@fishjam-cloud/react-client": "^0.22.0", | ||
| "@trpc/client": "^11.6.0", | ||
| "@trpc/server": "^11.6.0", | ||
| "gemini-demo-backend": "workspace:*", | ||
| "react": "^19.1.1", | ||
| "react-dom": "^19.1.1" | ||
| }, |
There was a problem hiding this comment.
@trpc/server is listed as a dependency but doesn’t appear to be used anywhere in the web app. Removing unused deps reduces install size and avoids accidentally bundling server-only code.
| peerName: trimmedPeerName, | ||
| }); | ||
|
|
||
| props.onJoined(roomId, roomName); |
There was a problem hiding this comment.
onJoined is called with the untrimmed roomName state even though you compute trimmedRoomName. This can lead to displaying a room name with leading/trailing spaces even though the backend uses the trimmed value; pass trimmedRoomName instead.
| props.onJoined(roomId, roomName); | |
| props.onJoined(roomId, trimmedRoomName); |
| @@ -0,0 +1,22 @@ | |||
| import { initTRPC } from "@trpc/server"; | |||
| import z from "zod"; | |||
There was a problem hiding this comment.
zod does not provide a default export in v4; import z from "zod" will fail typechecking and likely break at runtime. Switch to a named import (e.g. import { z } from "zod") or a namespace import.
| import z from "zod"; | |
| import { z } from "zod"; |
| import z from "zod"; | ||
| import dotenv from "dotenv"; | ||
|
|
||
| dotenv.config({ path: "../.env", quiet: true }); |
There was a problem hiding this comment.
dotenv.config options type doesn’t include quiet, so this will fail TypeScript typechecking with strict enabled. Remove quiet (or gate it behind a cast) and rely on dotenv’s standard options (e.g. debug) if needed.
| dotenv.config({ path: "../.env", quiet: true }); | |
| dotenv.config({ path: "../.env" }); |
| ENTRYPOINT ["dumb-init", "--"] | ||
| CMD ["npx", "serve", "-s", "web/dist", "-l", "tcp://0.0.0.0:3000"] |
There was a problem hiding this comment.
This image runs npx serve ... but serve isn’t installed in the image, so startup will download it from the network at runtime (often disallowed in prod) and makes builds non-reproducible. Install serve during the image build (or use a dedicated static server like nginx) and invoke it without npx fetching.
| <input | ||
| style={styles.input} | ||
| placeholder="Room name" | ||
| value={roomName} | ||
| onChange={(e) => setRoomName(e.target.value)} | ||
| onKeyDown={(e) => e.key === "Enter" && handleSubmit()} | ||
| autoFocus |
There was a problem hiding this comment.
Pressing Enter triggers handleSubmit() even when joining is not allowed (empty fields or already loading). Gate the Enter handler on canJoin and/or add an early if (!canJoin) return; guard inside handleSubmit to prevent invalid backend calls.
|
|
||
| export const appRouter = t.router({ | ||
| getPeerToken: t.procedure | ||
| .input(z.object({ roomName: z.string().min(1), peerName: z.string() })) |
There was a problem hiding this comment.
Server-side validation currently allows an empty peerName (z.string()), even though the UI blocks empty names. Consider enforcing peerName: z.string().min(1) to prevent creating peers with blank names via direct API calls or Enter-key submission.
| .input(z.object({ roomName: z.string().min(1), peerName: z.string() })) | |
| .input(z.object({ roomName: z.string().min(1), peerName: z.string().min(1) })) |
| @@ -0,0 +1,13 @@ | |||
| import z from "zod"; | |||
There was a problem hiding this comment.
zod does not provide a default export in v4; import z from "zod" will fail typechecking and likely break at runtime. Use a named import (import { z } from "zod") or namespace import instead.
| import z from "zod"; | |
| import { z } from "zod"; |
| "lib": ["ES2023", "DOM", "DOM.Iterable"], | ||
| "module": "ESNext", | ||
| "types": ["vite/client"], | ||
| "skipLibCheck": true, | ||
| "moduleResolution": "bundler", |
There was a problem hiding this comment.
tsconfig.app.json restricts global type packages to only vite/client via "types", but the code uses the React namespace types (e.g. React.CSSProperties, React.ReactNode). This will cause typecheck/build failures unless you add React types here (e.g. include react/react-dom) or remove the types restriction.
| 2. Install dependencies: | ||
|
|
||
| ``` | ||
| cd backend && npm install | ||
| cd ../web && npm install | ||
| ``` | ||
|
|
||
| 3. Start the backend: | ||
|
|
||
| ``` | ||
| cd backend && npm run start | ||
| ``` | ||
|
|
||
| 4. Start the frontend (in another terminal): | ||
|
|
||
| ``` | ||
| cd web && npm run start | ||
| ``` |
There was a problem hiding this comment.
README setup steps use npm install/npm run, but this demo is configured for Yarn workspaces (packageManager: yarn@4.12.0) and relies on the workspace-level yarn.lock. Update the instructions to use Yarn commands (e.g. yarn install, yarn workspace gemini-demo-backend start, yarn workspace gemini-demo-web start).
No description provided.