Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion deep-sea-stories/.env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
FISHJAM_ID=
FISHJAM_MANAGEMENT_TOKEN=
ELEVENLABS_API_KEY=
GEMINI_API_KEY=
80 changes: 79 additions & 1 deletion deep-sea-stories/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,79 @@
# Deep Sea Stories Example App
# Deep Sea Stories

Deep Sea Stories is a real-time, audio-driven game where players solve mysteries with the help of an AI riddle master.

https://github.com/user-attachments/assets/a88f872e-f707-4014-8855-0a55375ce998

Check out our [blog post](https://fishjam.swmansion.com/blog/voice-ai-how-we-built-a-multi-speaker-ai-agent-using-gemini) to learn more!

## Running Locally

Before running locally, make sure to copy `.env.example` to `.env` and set the following values:

```bash
FISHJAM_ID=...
FISHJAM_MANAGEMENT_TOKEN=...
GEMINI_API_KEY=...
```

- You can get `FISHJAM_ID` and `FISHJAM_MANAGEMENT_TOKEN` for free by logging in at <https://fishjam.io/app>.
- You can generate `GEMINI_API_KEY` for free at <https://aistudio.google.com/api-keys>.

### Docker Compose (Recommended)

The easiest way to run the app is with [Docker Compose](https://docs.docker.com/compose/install/).

```bash
docker compose up --build
```

You can then access the UI at <http://localhost:5000>

### Running Manually

If you can't use [docker compose](#docker-compose-recommended) to run the project, then you can follow the following steps to run the demo.

### Requirements
- [Node.js](https://nodejs.org/en/download) `>= 24`
- Yarn `4.9.2` (via [Corepack](https://github.com/nodejs/corepack))

1. Install dependencies from the repo root:
```bash
corepack enable
yarn install
```
2. Configure environment variables:
- Backend (`packages/backend/.env`):
```bash
FISHJAM_ID="your-fishjam-id"
FISHJAM_MANAGEMENT_TOKEN="your-management-token"
GEMINI_API_KEY="your-gemini-api-key"
```
- Web (`packages/web/.env` or `packages/web/.env.local`):
```bash
VITE_FISHJAM_ID="your-fishjam-id"
VITE_BACKEND_URL="http://localhost:8000/api/v1"
```
3. Start the backend (from repo root):
```bash
yarn workspace @deep-sea-stories/backend start
```
4. Start the web client in another terminal (from repo root):
```bash
yarn workspace @deep-sea-stories/web start
```
5. Open the UI at <http://localhost:5173>.

## Repo Structure

- `packages/backend`: Fishjam Agent which talks with Google Gemini Live API.
- `packages/web`: Web client, which connects to the backend and Fishjam in the browser.
- `packages/common`: Shared TypeScript types and utilities.
- `docker-compose.yml` + `nginx.conf`: Container setup with a reverse proxy.

### Tech Stack

- Yarn workspaces monorepo with Fastify + tRPC (backend), React + Vite (frontend).
- [Fishjam](https://fishjam.swmansion.com) for real-time videoconferencing capabilities.
- [Gemini Live API](https://ai.google.dev/gemini-api/docs/live) for AI riddle master backend.

3 changes: 0 additions & 3 deletions deep-sea-stories/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ services:
- PORT=8000
- FISHJAM_ID=${FISHJAM_ID}
- FISHJAM_MANAGEMENT_TOKEN=${FISHJAM_MANAGEMENT_TOKEN}
- ELEVENLABS_API_KEY=${ELEVENLABS_API_KEY}
- GEMINI_API_KEY=${GEMINI_API_KEY}
restart: unless-stopped

Expand All @@ -21,8 +20,6 @@ services:
args:
- VITE_FISHJAM_ID=${FISHJAM_ID}
- VITE_BACKEND_URL=${BACKEND_URL:-/api/v1}
environment:
- VITE_BACKEND_URL=/api/v1
restart: unless-stopped
depends_on:
- backend
Expand Down
1 change: 0 additions & 1 deletion deep-sea-stories/packages/backend/.env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
FISHJAM_ID="your-fishjam-id"
FISHJAM_MANAGEMENT_TOKEN="your-management-token"
ELEVENLABS_API_KEY="your-elevenlabs-api-key"
GEMINI_API_KEY="your-gemini-api-key"
1 change: 0 additions & 1 deletion deep-sea-stories/packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
"types": "./src/index.ts",
"dependencies": {
"@deep-sea-stories/common": "workspace:*",
"@elevenlabs/elevenlabs-js": "^2.20.0",
"@fastify/cors": "^11.1.0",
"@fishjam-cloud/js-server-sdk": "^0.22.0",
"@google/genai": "^1.30.0",
Expand Down
1 change: 0 additions & 1 deletion deep-sea-stories/packages/backend/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export const configSchema = z.object({
PORT: z.coerce.number().int().default(8000),
FISHJAM_ID: z.string(),
FISHJAM_MANAGEMENT_TOKEN: z.string(),
ELEVENLABS_API_KEY: z.string(),
GEMINI_API_KEY: z.string().optional(),
GOOGLE_GENAI_USE_VERTEXAI: z
.string()
Expand Down
11 changes: 0 additions & 11 deletions deep-sea-stories/packages/backend/tests/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,10 @@ describe('Configuration', () => {
});
}, 'Should throw when required FISHJAM_MANAGEMENT_TOKEN is missing');

assert.throws(() => {
configSchema.parse({
FISHJAM_ID: 'test-id',
FISHJAM_MANAGEMENT_TOKEN: 'test-token',
GEMINI_API_KEY: 'test-gemini-key',
});
}, 'Should throw when required ELEVENLABS_API_KEY is missing');

assert.doesNotThrow(() => {
configSchema.parse({
FISHJAM_ID: 'test-id',
FISHJAM_MANAGEMENT_TOKEN: 'test-token',
ELEVENLABS_API_KEY: 'test-api-key',
GEMINI_API_KEY: 'test-gemini-key',
});
}, 'Should not throw with all required fields');
Expand All @@ -87,7 +78,6 @@ describe('Configuration', () => {
const config = configSchema.parse({
FISHJAM_ID: 'test-id',
FISHJAM_MANAGEMENT_TOKEN: 'test-token',
ELEVENLABS_API_KEY: 'test-api-key',
GEMINI_API_KEY: 'test-gemini-key',
});

Expand All @@ -98,7 +88,6 @@ describe('Configuration', () => {
const config = configSchema.parse({
FISHJAM_ID: 'test-id',
FISHJAM_MANAGEMENT_TOKEN: 'test-token',
ELEVENLABS_API_KEY: 'test-api-key',
GEMINI_API_KEY: 'test-gemini-key',
PORT: '3000',
});
Expand Down
59 changes: 1 addition & 58 deletions deep-sea-stories/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,6 @@ __metadata:
resolution: "@deep-sea-stories/backend@workspace:packages/backend"
dependencies:
"@deep-sea-stories/common": "workspace:*"
"@elevenlabs/elevenlabs-js": "npm:^2.20.0"
"@fastify/cors": "npm:^11.1.0"
"@fishjam-cloud/js-server-sdk": "npm:^0.22.0"
"@google/genai": "npm:^1.30.0"
Expand Down Expand Up @@ -385,17 +384,6 @@ __metadata:
languageName: unknown
linkType: soft

"@elevenlabs/elevenlabs-js@npm:^2.20.0":
version: 2.22.0
resolution: "@elevenlabs/elevenlabs-js@npm:2.22.0"
dependencies:
command-exists: "npm:^1.2.9"
node-fetch: "npm:^2.7.0"
ws: "npm:^8.18.3"
checksum: 10c0/398fcc850b8d9b1735d090fc0579138b54923ac945ec11941d3375c3733857ed37e126db289010f2de039b55f82cbff8f7596f36d049ffa728e86b85b6eb6a6b
languageName: node
linkType: hard

"@emnapi/core@npm:^1.5.0":
version: 1.5.0
resolution: "@emnapi/core@npm:1.5.0"
Expand Down Expand Up @@ -2215,13 +2203,6 @@ __metadata:
languageName: node
linkType: hard

"command-exists@npm:^1.2.9":
version: 1.2.9
resolution: "command-exists@npm:1.2.9"
checksum: 10c0/75040240062de46cd6cd43e6b3032a8b0494525c89d3962e280dde665103f8cc304a8b313a5aa541b91da2f5a9af75c5959dc3a77893a2726407a5e9a0234c16
languageName: node
linkType: hard

"commander@npm:^5.1.0":
version: 5.1.0
resolution: "commander@npm:5.1.0"
Expand Down Expand Up @@ -3521,20 +3502,6 @@ __metadata:
languageName: node
linkType: hard

"node-fetch@npm:^2.7.0":
version: 2.7.0
resolution: "node-fetch@npm:2.7.0"
dependencies:
whatwg-url: "npm:^5.0.0"
peerDependencies:
encoding: ^0.1.0
peerDependenciesMeta:
encoding:
optional: true
checksum: 10c0/b55786b6028208e6fbe594ccccc213cab67a72899c9234eb59dba51062a299ea853210fcf526998eaa2867b0963ad72338824450905679ff0fa304b8c5093ae8
languageName: node
linkType: hard

"node-fetch@npm:^3.3.2":
version: 3.3.2
resolution: "node-fetch@npm:3.3.2"
Expand Down Expand Up @@ -4312,13 +4279,6 @@ __metadata:
languageName: node
linkType: hard

"tr46@npm:~0.0.3":
version: 0.0.3
resolution: "tr46@npm:0.0.3"
checksum: 10c0/047cb209a6b60c742f05c9d3ace8fa510bff609995c129a37ace03476a9b12db4dbf975e74600830ef0796e18882b2381fb5fb1f6b4f96b832c374de3ab91a11
languageName: node
linkType: hard

"tslib@npm:^2.0.0, tslib@npm:^2.1.0, tslib@npm:^2.4.0":
version: 2.8.1
resolution: "tslib@npm:2.8.1"
Expand Down Expand Up @@ -4529,23 +4489,6 @@ __metadata:
languageName: node
linkType: hard

"webidl-conversions@npm:^3.0.0":
version: 3.0.1
resolution: "webidl-conversions@npm:3.0.1"
checksum: 10c0/5612d5f3e54760a797052eb4927f0ddc01383550f542ccd33d5238cfd65aeed392a45ad38364970d0a0f4fea32e1f4d231b3d8dac4a3bdd385e5cf802ae097db
languageName: node
linkType: hard

"whatwg-url@npm:^5.0.0":
version: 5.0.0
resolution: "whatwg-url@npm:5.0.0"
dependencies:
tr46: "npm:~0.0.3"
webidl-conversions: "npm:^3.0.0"
checksum: 10c0/1588bed84d10b72d5eec1d0faa0722ba1962f1821e7539c535558fb5398d223b0c50d8acab950b8c488b4ba69043fd833cc2697056b167d8ad46fac3995a55d5
languageName: node
linkType: hard

"which@npm:^2.0.1":
version: 2.0.2
resolution: "which@npm:2.0.2"
Expand Down Expand Up @@ -4597,7 +4540,7 @@ __metadata:
languageName: node
linkType: hard

"ws@npm:^8.18.0, ws@npm:^8.18.3":
"ws@npm:^8.18.0":
version: 8.18.3
resolution: "ws@npm:8.18.3"
peerDependencies:
Expand Down