Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,4 @@ certs/
memory-bank/epoch/*
!memory-bank/epoch/.gitkeep
models/*.bin
whisper.cpp/
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to OpenStudio will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- **`setup-ai.sh`** one-shot bootstrap script for the AI pipeline. It clones and builds `whisper.cpp`, downloads the default Whisper model into `models/`, and prompts for LLM provider settings in `.env`.

## [0.3.2] - 2026-05-25

### Added
Expand Down
2 changes: 2 additions & 0 deletions QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ cp .env.example .env
cd server && npm install && cd ..
cd web && npm install && cd ..
cp station-manifest.sample.json station-manifest.json
# Optional: bootstrap the AI pipeline (whisper.cpp, model, LLM config)
./setup-ai.sh
```

## Daily Development Workflow
Expand Down
14 changes: 6 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,18 +132,18 @@ OpenStudio includes a complete post-production pipeline — transcription, show

### Prerequisites
- `ffmpeg` and `ffprobe` on your `PATH` (most package managers ship both: `brew install ffmpeg`, `apt install ffmpeg`)
- `cmake` on your `PATH` for the `whisper.cpp` build
- ~1.5 GB free disk space for the default Whisper model

### whisper.cpp setup (one-time)
### One-shot AI setup

```bash
git clone https://github.com/ggerganov/whisper.cpp
cd whisper.cpp && make -j$(nproc)
cd .. && mkdir -p models
wget -O models/ggml-medium.bin https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium.bin
./setup-ai.sh
```

You can swap the model — `ggml-tiny.bin` (~75 MB) is faster but less accurate; `ggml-large.bin` is the other direction. The server looks for `models/ggml-medium.bin` by default.
The script clones and builds `whisper.cpp`, downloads `models/ggml-medium.bin`, and prompts for your LLM provider so `.env` is ready for show notes.

You can swap the model manually if you want — `ggml-tiny.bin` (~75 MB) is faster but less accurate; `ggml-large.bin` is the other direction. The server looks for `models/ggml-medium.bin` by default.

### LLM Provider Examples

Expand Down Expand Up @@ -240,8 +240,6 @@ See [docs/vision.md](docs/vision.md) for the full project vision and philosophy.
Honest about what's there and what isn't:

- **Invite-link UI** — the server can mint scoped invite tokens (host / ops / guest, 4 h TTL), but the host UI doesn't expose a button yet. For now, hosts share the room URL manually. Coming in 0.4.
- **AI pipeline setup is manual** — the whisper.cpp build, model download, and LLM configuration aren't scripted. A `setup-ai.sh` would be a great PR.
- **whisper.cpp gitlink** — the repo references whisper.cpp as a gitlink without a `.gitmodules` entry. Use the manual `git clone` in the AI setup section above; `git submodule update` will fail.
- **Mesh scale ceiling** — WebRTC mesh tops out around 15 participants. Larger rooms need an SFU (planned for 0.5).

## Contributing
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"start": "node server/server.js",
"dev": "node --watch server/server.js",
"test": "node server/test-signaling.js && node server/test-rooms.js",
"test:setup-ai": "bash tests/test-setup-ai.sh",
"install": "cd server && npm install"
},
"engines": {
Expand Down
11 changes: 9 additions & 2 deletions run-pre-validation.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ echo " 3. test-gain-controls.mjs - Per-participant gain controls"
echo " 4. test-program-bus.mjs - Program bus mixing"
echo " 5. test-mix-minus.mjs - Mix-minus calculation (3 peers)"
echo " 6. test-return-feed.mjs - Return feed routing (2 peers)"
echo " 7. test-setup-ai.sh - AI bootstrap script smoke test"
echo ""
echo "================================================================================"
echo ""
Expand All @@ -32,13 +33,18 @@ run_test() {
local test_name=$1
local test_file=$2
local timeout_seconds=${3:-60}
local runner="node"

case "$test_file" in
*.sh) runner="bash" ;;
esac

echo "--- Running: $test_name ---"
echo "File: $test_file"
echo "Timeout: ${timeout_seconds}s"
echo ""

if timeout ${timeout_seconds} node "$test_file"; then
if timeout ${timeout_seconds} "$runner" "$test_file"; then
echo ""
echo "✅ PASSED: $test_name"
echo ""
Expand All @@ -64,6 +70,7 @@ run_test "Gain Controls" "tests/test-gain-controls.mjs" 60
run_test "Program Bus Mixing" "tests/test-program-bus.mjs" 60
run_test "Mix-Minus Calculation" "tests/test-mix-minus.mjs" 60
run_test "Return Feed Routing" "tests/test-return-feed.mjs" 60
run_test "AI Bootstrap Script" "tests/test-setup-ai.sh" 120

# Summary
echo "================================================================================"
Expand All @@ -76,7 +83,7 @@ for test in "${TESTS[@]}"; do
done

echo ""
echo "Results: $PASSED passed, $FAILED failed (out of 6 total)"
echo "Results: $PASSED passed, $FAILED failed (out of 7 total)"
echo ""

# Decision
Expand Down
37 changes: 33 additions & 4 deletions server/lib/whisper-transcriber.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,41 @@ async function downloadModel(model = 'medium') {
*/
async function buildWhisperCpp() {
const whisperDir = path.join(process.cwd(), 'whisper.cpp');
const whisperBin = WHISPER_BIN;
const cmakeLists = path.join(whisperDir, 'CMakeLists.txt');
const makefile = path.join(whisperDir, 'Makefile');

if (!fs.existsSync(whisperDir)) {
logger.info('Cloning whisper.cpp...');
await execAsync(`git clone --depth 1 --recursive https://github.com/ggerganov/whisper.cpp.git ${whisperDir}`);
} else if (!fs.existsSync(cmakeLists) && !fs.existsSync(makefile)) {
const entries = fs.readdirSync(whisperDir);
if (entries.length === 0) {
logger.warn('Found empty whisper.cpp directory, re-cloning...');
fs.rmSync(whisperDir, { recursive: true, force: true });
await execAsync(`git clone --depth 1 --recursive https://github.com/ggerganov/whisper.cpp.git ${whisperDir}`);
} else {
throw new Error('whisper.cpp checkout exists but has no supported build files');
}
}

logger.info('Cloning whisper.cpp...');
await execAsync(`git clone --depth 1 https://github.com/ggerganov/whisper.cpp.git ${whisperDir}`);
if (fs.existsSync(whisperBin)) {
logger.info('whisper.cpp already built');
return;
}

logger.info('Building whisper.cpp...');
await execAsync(`cd ${whisperDir} && make -j$(nproc) 2>&1 | tail -5`);
if (fs.existsSync(cmakeLists)) {
logger.info('Configuring whisper.cpp with CMake...');
await execAsync(`cmake -S ${whisperDir} -B ${whisperDir}/build -DWHISPER_BUILD_TESTS=OFF -DWHISPER_BUILD_EXAMPLES=ON -DWHISPER_BUILD_SERVER=OFF`);

logger.info('Building whisper.cpp...');
await execAsync(`cmake --build ${whisperDir}/build --config Release --target whisper-cli -j$(nproc)`);
} else if (fs.existsSync(makefile)) {
logger.info('Building whisper.cpp...');
await execAsync(`cd ${whisperDir} && make -j$(nproc) 2>&1 | tail -5`);
} else {
throw new Error('whisper.cpp checkout does not contain a supported build system');
}

logger.info('whisper.cpp built successfully');
}
Expand Down
Loading