From b2f23e9d2f24ba8f56b43f6aee2f8b37e01c7042 Mon Sep 17 00:00:00 2001 From: James Spadafora Date: Fri, 15 May 2026 13:15:59 -0700 Subject: [PATCH 1/2] Add bootstrap.sh to simplify new contributor onboarding Adds a single interactive script at the repo root that walks a new contributor through the full local setup in one command: - Checks that npm and Docker (daemon running) are present - Backs up and copies all three example config files (docker-compose.local.yml, docker-compose.local.vexa.yml, frontend .env) - Interactively prompts for the LLM provider and API key (OpenAI / Gemini / skip) - Interactively prompts for the transcription backend: remote vexa.ai free tier, self-hosted Whisper service, or skip (adds SKIP_TRANSCRIPTION_CHECK=true when skipped) - Runs npm install for the frontend - Starts the Vexa services, waits for readiness, creates a local user, generates a Vexa API key, and writes it into docker-compose.local.yml - Starts the full DNA stack detached (equivalent to make start-local) Compatible with macOS and Linux. Re-runnable: existing config files are backed up with a timestamp suffix before being overwritten. Also updates .gitignore files to exclude the .bak.* backup files the script creates. Signed-off-by: James Spadafora Co-authored-by: Cursor --- .gitignore | 1 + backend/.gitignore | 4 +- bootstrap.sh | 420 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 423 insertions(+), 2 deletions(-) create mode 100755 bootstrap.sh diff --git a/.gitignore b/.gitignore index bde3891d..611deac2 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ dist/ .vscode/ .claude/ .env +.env.bak.* .env.local *.mp3 *.mp4 diff --git a/backend/.gitignore b/backend/.gitignore index e30b5633..c66ad8e6 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -40,5 +40,5 @@ backend/docker-compose.debug.yml backend/docker-compose.vexa.yml # Local environment files -docker-compose.local.yml -docker-compose.local.vexa.yml +docker-compose.local.yml* +docker-compose.local.vexa.yml* diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 00000000..2f5ca1d3 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,420 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: Apache-2.0 +# Copyright Contributors to the Dailies Notes Assistant Project. +# +# Bootstrap script for new contributors. +# Checks prerequisites, copies example configs, installs frontend dependencies, +# generates a local Vexa API key, and starts the full DNA stack. +# +# Usage: +# ./bootstrap.sh +# +# Supported platforms: macOS, Linux + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +BACKEND_DIR="$SCRIPT_DIR/backend" +FRONTEND_DIR="$SCRIPT_DIR/frontend" + +VEXA_ADMIN_URL="http://localhost:8056" +VEXA_ADMIN_TOKEN="your-admin-token" +VEXA_LOCAL_EMAIL="dna-local@example.com" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +BOLD='\033[1m' +NC='\033[0m' + +info() { echo -e "${BLUE}[info]${NC} $*"; } +ok() { echo -e "${GREEN}[ok]${NC} $*"; } +warn() { echo -e "${YELLOW}[warn]${NC} $*"; } +die() { echo -e "${RED}[error]${NC} $*" >&2; exit 1; } + +# ── helpers ──────────────────────────────────────────────────────────────────── + +get_compose_cmd() { + if command -v docker-compose &>/dev/null; then + echo "docker-compose" + else + echo "docker compose" + fi +} + +# Back up $dst if it already exists, then copy $src → $dst. +safe_copy() { + local src="$1" dst="$2" + if [[ -f "$dst" ]]; then + local bak="${dst}.bak.$(date +%Y%m%d%H%M%S)" + warn "$(basename "$dst") already exists — backed up to $(basename "$bak")" + cp "$dst" "$bak" + fi + cp "$src" "$dst" + ok "$(basename "$src") → $(basename "$dst")" +} + +# In-place sed replacement: replace every occurrence of KEY= with KEY=VALUE. +# Uses a backup suffix then deletes it, which works on both macOS and Linux. +set_env_var() { + local key="$1" value="$2" file="$3" + sed -i.bak "s|${key}=.*|${key}=${value}|g" "$file" + rm -f "${file}.bak" +} + +# ── step 1: prerequisites ────────────────────────────────────────────────────── + +check_prerequisites() { + info "Checking prerequisites..." + + command -v npm &>/dev/null \ + || die "npm not found. Install Node.js v18+: https://nodejs.org/en/download" + ok "npm $(npm --version)" + + command -v docker &>/dev/null \ + || die "Docker not found. Install Docker: https://docs.docker.com/get-docker/" + ok "Docker $(docker --version | awk '{gsub(/,/,"",$3); print $3}')" + + docker info &>/dev/null \ + || die "Docker daemon is not running. Start Docker Desktop (or the service) and try again." + ok "Docker daemon is running" +} + +# ── step 2: copy example config files ───────────────────────────────────────── + +copy_config_files() { + info "Copying example config files..." + safe_copy \ + "$BACKEND_DIR/example.docker-compose.local.yml" \ + "$BACKEND_DIR/docker-compose.local.yml" + safe_copy \ + "$BACKEND_DIR/example.docker-compose.local.vexa.yml" \ + "$BACKEND_DIR/docker-compose.local.vexa.yml" + safe_copy \ + "$FRONTEND_DIR/packages/app/.env.example" \ + "$FRONTEND_DIR/packages/app/.env" +} + +# ── step 3: LLM provider setup ───────────────────────────────────────────────── + +configure_llm() { + echo "" + echo -e "${BOLD}LLM provider setup${NC}" + echo " (Press Enter on any prompt to skip and fill in manually later)" + echo "" + echo " 1) OpenAI (default)" + echo " 2) Gemini" + echo " 3) Skip" + echo "" + read -r -p " Choice [1]: " llm_choice + llm_choice="${llm_choice:-1}" + echo "" + + case "$llm_choice" in + 2|[gG]emini) + read -r -p " Gemini API key: " gemini_key + if [[ -n "$gemini_key" ]]; then + # The example file has an OPENAI_API_KEY line; replace it with + # the Gemini key and insert LLM_PROVIDER=gemini above it. + python3 - "$BACKEND_DIR/docker-compose.local.yml" "$gemini_key" <<'PYEOF' +import sys + +path, key = sys.argv[1], sys.argv[2] +with open(path) as f: + lines = f.readlines() +out = [] +for line in lines: + stripped = line.lstrip() + if stripped.startswith('- OPENAI_API_KEY='): + indent = line[: len(line) - len(stripped)] + out.append(f"{indent}- LLM_PROVIDER=gemini\n") + out.append(f"{indent}- GEMINI_API_KEY={key}\n") + else: + out.append(line) +with open(path, 'w') as f: + f.writelines(out) +PYEOF + ok "Gemini API key written to backend/docker-compose.local.yml" + else + warn "Skipped — set GEMINI_API_KEY and LLM_PROVIDER=gemini in backend/docker-compose.local.yml" + fi + ;; + 3|[sS]kip) + warn "Skipped — set your LLM API key in backend/docker-compose.local.yml" + ;; + *) + read -r -p " OpenAI API key: " openai_key + if [[ -n "$openai_key" ]]; then + set_env_var "OPENAI_API_KEY" "$openai_key" "$BACKEND_DIR/docker-compose.local.yml" + ok "OpenAI API key written to backend/docker-compose.local.yml" + else + warn "Skipped — set OPENAI_API_KEY in backend/docker-compose.local.yml" + fi + ;; + esac +} + +# ── step 4: transcription service setup ─────────────────────────────────────── + +# Append SKIP_TRANSCRIPTION_CHECK=true to docker-compose.local.vexa.yml so +# Vexa starts even without a working transcription backend. +add_skip_transcription_check() { + python3 - "$BACKEND_DIR/docker-compose.local.vexa.yml" <<'PYEOF' +import sys + +path = sys.argv[1] +with open(path) as f: + lines = f.readlines() + +if any('SKIP_TRANSCRIPTION_CHECK' in l for l in lines): + sys.exit(0) + +last_env_idx = -1 +for i, line in enumerate(lines): + stripped = line.lstrip() + if stripped.startswith('- ') and '=' in stripped: + last_env_idx = i + +if last_env_idx >= 0: + indent = lines[last_env_idx][: len(lines[last_env_idx]) - len(lines[last_env_idx].lstrip())] + lines.insert(last_env_idx + 1, f"{indent}- SKIP_TRANSCRIPTION_CHECK=true\n") + with open(path, 'w') as f: + f.writelines(lines) +PYEOF + ok "SKIP_TRANSCRIPTION_CHECK=true added to backend/docker-compose.local.vexa.yml" +} + +configure_transcription() { + echo "" + echo -e "${BOLD}Transcription service setup${NC}" + echo " Vexa needs an OpenAI Whisper-compatible transcription backend." + echo "" + echo " 1) Remote service via vexa.ai ${BOLD}(recommended — free tier available)${NC}" + echo " Get a free key at: https://staging.vexa.ai/dashboard/transcription" + echo "" + echo " 2) Self-hosted transcription service" + echo " Requires Docker (GPU recommended). Setup guide:" + echo " https://github.com/Vexa-ai/vexa/tree/main/services/transcription-service" + echo "" + echo " 3) Skip for now (transcription will be disabled at startup)" + echo " You can enable it later by editing backend/docker-compose.local.vexa.yml" + echo "" + read -r -p " Choice [1]: " trans_choice + trans_choice="${trans_choice:-1}" + echo "" + + case "$trans_choice" in + 2|[sS]elf*) + echo " Self-hosted setup steps:" + echo " 1. git clone https://github.com/Vexa-ai/vexa.git" + echo " 2. cd vexa/services/transcription-service" + echo " 3. cp .env.example .env" + echo " 4. Set API_TOKEN in .env and choose GPU or CPU (DEVICE=cpu for no GPU)" + echo " 5. docker compose up -d (or docker compose -f docker-compose.cpu.yml up -d)" + echo " 6. Wait for: 'Model loaded successfully' in the logs" + echo "" + local default_url="http://localhost:8083/v1/audio/transcriptions" + read -r -p " Transcription service URL [${default_url}]: " trans_url + trans_url="${trans_url:-$default_url}" + read -r -p " Transcription service API token (your API_TOKEN value, or Enter to skip): " trans_token + if [[ -n "$trans_token" ]]; then + set_env_var "TRANSCRIBER_URL" "$trans_url" "$BACKEND_DIR/docker-compose.local.vexa.yml" + set_env_var "TRANSCRIBER_API_KEY" "$trans_token" "$BACKEND_DIR/docker-compose.local.vexa.yml" + ok "Self-hosted transcription configured in backend/docker-compose.local.vexa.yml" + else + warn "Skipped — set TRANSCRIBER_URL and TRANSCRIBER_API_KEY in backend/docker-compose.local.vexa.yml" + add_skip_transcription_check + fi + ;; + 3|[sS]kip) + warn "Transcription skipped — Vexa will start without it" + add_skip_transcription_check + ;; + *) + echo " Get your free key at: https://staging.vexa.ai/dashboard/transcription" + echo "" + read -r -p " Transcription API key (press Enter to skip): " trans_key + if [[ -n "$trans_key" ]]; then + set_env_var "TRANSCRIBER_API_KEY" "$trans_key" "$BACKEND_DIR/docker-compose.local.vexa.yml" + ok "Remote transcription API key written to backend/docker-compose.local.vexa.yml" + else + warn "Skipped — set TRANSCRIBER_API_KEY in backend/docker-compose.local.vexa.yml" + warn "Or add SKIP_TRANSCRIPTION_CHECK=true to disable the startup check" + fi + ;; + esac +} + +# ── step 5: frontend dependencies ───────────────────────────────────────────── + +install_frontend() { + info "Installing frontend dependencies..." + (cd "$FRONTEND_DIR" && npm install) + ok "Frontend dependencies installed" +} + +# ── step 6: Vexa API key generation ─────────────────────────────────────────── + +bootstrap_vexa() { + local compose_cmd + compose_cmd="$(get_compose_cmd)" + + info "Starting Vexa services to generate a local API key..." + ( + cd "$BACKEND_DIR" + $compose_cmd \ + -f docker-compose.vexa.yml \ + -f docker-compose.local.vexa.yml \ + up -d vexa vexa-db + ) + + info "Waiting for Vexa admin API on :8057 (may take ~30 s on first pull)..." + local retries=40 + until curl -sf \ + -H "X-Admin-API-Key: ${VEXA_ADMIN_TOKEN}" \ + "${VEXA_ADMIN_URL}/admin/users" \ + -o /dev/null 2>/dev/null; do + retries=$((retries - 1)) + [[ $retries -le 0 ]] \ + && die "Vexa admin API did not become ready in time. Run: docker logs vexa" + sleep 3 + done + ok "Vexa admin API is ready" + + info "Creating local Vexa user (${VEXA_LOCAL_EMAIL})..." + + local tmpfile + tmpfile="$(mktemp)" + + local http_code + http_code="$(curl -s -o "$tmpfile" -w "%{http_code}" \ + -X POST \ + -H "X-Admin-API-Key: ${VEXA_ADMIN_TOKEN}" \ + -H "Content-Type: application/json" \ + -d "{\"email\":\"${VEXA_LOCAL_EMAIL}\",\"name\":\"DNA Local Dev\"}" \ + "${VEXA_ADMIN_URL}/admin/users")" + local create_response + create_response="$(cat "$tmpfile")" + rm -f "$tmpfile" + + local user_id + if [[ "$http_code" == "200" || "$http_code" == "201" ]]; then + user_id="$(echo "$create_response" \ + | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")" + ok "Vexa user created (id: ${user_id})" + elif [[ "$http_code" == "409" ]]; then + warn "User already exists — fetching existing record..." + local list_response + list_response="$(curl -sf \ + -H "X-Admin-API-Key: ${VEXA_ADMIN_TOKEN}" \ + "${VEXA_ADMIN_URL}/admin/users")" + user_id="$(echo "$list_response" | python3 - "$VEXA_LOCAL_EMAIL" <<'PYEOF' +import sys, json +users = json.load(sys.stdin) +email = sys.argv[1] +match = [u for u in users if u.get("email") == email] +print((match or users)[0]["id"]) +PYEOF +)" + ok "Found existing Vexa user (id: ${user_id})" + else + rm -f "$tmpfile" + die "Unexpected response from Vexa admin API (HTTP ${http_code}): ${create_response}" + fi + + info "Generating Vexa API token..." + local token_response vexa_api_key + token_response="$(curl -sf \ + -X POST \ + -H "X-Admin-API-Key: ${VEXA_ADMIN_TOKEN}" \ + "${VEXA_ADMIN_URL}/admin/users/${user_id}/tokens")" + vexa_api_key="$(echo "$token_response" \ + | python3 -c "import sys,json; print(json.load(sys.stdin)['token'])")" + ok "Vexa API key generated" + + set_env_var "VEXA_API_KEY" "$vexa_api_key" "$BACKEND_DIR/docker-compose.local.yml" + ok "Vexa API key written to backend/docker-compose.local.yml" +} + +# ── step 7: start the full stack ─────────────────────────────────────────────── + +start_full_stack() { + local compose_cmd + compose_cmd="$(get_compose_cmd)" + + info "Starting the full DNA stack (first run builds containers — this may take a few minutes)..." + ( + cd "$BACKEND_DIR" + $compose_cmd \ + -f docker-compose.yml \ + -f docker-compose.vexa.yml \ + -f docker-compose.debug.yml \ + -f docker-compose.local.yml \ + -f docker-compose.local.vexa.yml \ + up --build -d + ) + ok "All services started" +} + +# ── main ─────────────────────────────────────────────────────────────────────── + +main() { + echo "" + echo -e "${BOLD}${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${BOLD}${BLUE} DNA — Bootstrap${NC}" + echo -e "${BOLD}${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo "" + + check_prerequisites + echo "" + copy_config_files + echo "" + configure_llm + configure_transcription + install_frontend + echo "" + bootstrap_vexa + echo "" + start_full_stack + + echo "" + echo -e "${BOLD}${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${BOLD}${GREEN} Bootstrap complete!${NC}" + echo -e "${BOLD}${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo "" + echo " Running services:" + echo " DNA API → http://localhost:8000" + echo " API Docs → http://localhost:8000/docs" + echo " Vexa Admin → http://localhost:3001" + echo "" + echo " To start the frontend (in a new terminal):" + echo " cd frontend && npm run dev" + echo " App → http://localhost:5173" + echo "" + echo " To follow backend logs:" + echo " cd backend && make logs-local" + echo "" + local needs_attention=false + if grep -q 'your-openai-api-key\|GEMINI_API_KEY=\*\*\|OPENAI_API_KEY=\*\*' \ + "$BACKEND_DIR/docker-compose.local.yml" 2>/dev/null; then + needs_attention=true + echo -e " ${YELLOW}Action needed:${NC} fill in your LLM API key in:" + echo " backend/docker-compose.local.yml" + echo "" + fi + if grep -q 'TRANSCRIBER_API_KEY=\*\*' \ + "$BACKEND_DIR/docker-compose.local.vexa.yml" 2>/dev/null; then + needs_attention=true + echo -e " ${YELLOW}Action needed:${NC} fill in your transcription API key in:" + echo " backend/docker-compose.local.vexa.yml" + echo " Get a free key at: https://staging.vexa.ai/dashboard/transcription" + echo "" + fi + if [[ "$needs_attention" == "true" ]]; then + echo " After updating, restart with: cd backend && make restart-local" + echo "" + fi +} + +main "$@" From 7f008469ffb069ae86e19f0abe7d51025f7fd859 Mon Sep 17 00:00:00 2001 From: James Spadafora Date: Fri, 15 May 2026 13:19:43 -0700 Subject: [PATCH 2/2] Document the setup process Signed-off-by: James Spadafora --- QUICKSTART.md | 247 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 192 insertions(+), 55 deletions(-) diff --git a/QUICKSTART.md b/QUICKSTART.md index 705e9d9c..629e00a4 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -1,42 +1,72 @@ # DNA Quickstart Guide -This guide will help you run the DNA application stack locally for development. +This guide covers two ways to get the DNA application stack running locally: the automated bootstrap script (recommended) and step-by-step manual setup. -## Prerequisites +--- -- **Docker** and **Docker Compose** installed +## Automated Bootstrap (Recommended) + +The `bootstrap.sh` script handles all setup steps for you. Run it from the repo root: + +```bash +./bootstrap.sh +``` + +It will: + +1. Check that Docker and Node.js v18+ are installed and that the Docker daemon is running +2. Copy example config files into their working locations +3. Prompt you to choose an LLM provider (OpenAI or Gemini) and enter your API key +4. Prompt you to configure the transcription service (remote via vexa.ai, self-hosted, or skip) +5. Install frontend npm dependencies +6. Start the Vexa services, create a local dev user, and generate a Vexa API key automatically +7. Start the full DNA stack with Docker Compose + +After the script finishes, start the frontend in a new terminal: + +```bash +cd frontend && npm run dev +``` + +The app will be available at `http://localhost:5173`. + +--- + +## Manual Setup + +Follow these steps if you prefer to set up each component yourself, or if you need to understand what the bootstrap script does under the hood. + +### Prerequisites + +- **Docker** and **Docker Compose** installed and the Docker daemon running - **Node.js** (v18+) and **npm** for the frontend - **Python 3.11+** (optional, for running tests outside Docker) -## Quick Start - -### 1. Clone and Setup +### 1. Clone the Repository ```bash git clone cd dna ``` -### 2. Configure Environment Variables +### 2. Copy Example Config Files -Copy the example docker-compose.local.yml file: +Copy all three example config files into their working locations. The bootstrap script backs up any existing files before overwriting; do the same if you are re-running setup. ```bash cd backend cp example.docker-compose.local.yml docker-compose.local.yml cp example.docker-compose.local.vexa.yml docker-compose.local.vexa.yml -``` -Edit `docker-compose.local.yml` with your credentials. - -**Production tracking (ShotGrid):** To run without a ShotGrid seat, set **`PRODTRACK_PROVIDER=mock`** in `docker-compose.local.yml`. The mock provider uses read-only SQLite with pre-seeded data. To use real ShotGrid, set `PRODTRACK_PROVIDER=shotgrid` (or leave it unset) and add `SHOTGRID_URL`, `SHOTGRID_SCRIPT_NAME`, and `SHOTGRID_API_KEY`. See [Mock setup](#mock-production-tracking-setup) below for how to refresh or customize the mock data. +cd ../frontend +cp packages/app/.env.example packages/app/.env +``` -**LLM provider:** Set `LLM_PROVIDER` to choose which backend LLM integration to use. +### 3. Configure the LLM Provider -- `openai` (default): requires `OPENAI_API_KEY`; optional `OPENAI_MODEL` and `OPENAI_TIMEOUT` -- `gemini`: requires `GEMINI_API_KEY`; optional `GEMINI_MODEL`, `GEMINI_TIMEOUT`, and `GEMINI_URL` +Edit `backend/docker-compose.local.yml` and set your LLM credentials. The bootstrap script writes these values for you when you provide a key interactively. -Examples: +**OpenAI (default):** requires `OPENAI_API_KEY`; optional `OPENAI_MODEL` and `OPENAI_TIMEOUT` ```yaml services: @@ -47,6 +77,8 @@ services: - OPENAI_MODEL=gpt-4o-mini ``` +**Gemini:** requires `GEMINI_API_KEY`; also set `LLM_PROVIDER=gemini` + ```yaml services: api: @@ -57,71 +89,153 @@ services: - GEMINI_URL=https://generativelanguage.googleapis.com/v1beta/openai/ ``` -**Transcription (Vexa):** To get the transcription service running, you can get a free key from: https://staging.vexa.ai/dashboard/transcription +### 4. Configure the Transcription Service + +Vexa requires an OpenAI Whisper-compatible transcription backend. Edit `backend/docker-compose.local.vexa.yml` for whichever option you choose. + +**Option 1 — Remote via vexa.ai (recommended, free tier available):** -When setting up, skip the Vexa API key for now. Once the stack is running you can get your Vexa API key from the Vexa Dashboard. +Get a free key at https://staging.vexa.ai/dashboard/transcription, then set: ```yaml services: - api: - environment: - - PYTHONUNBUFFERED=1 - - SHOTGRID_URL=https://aswf.shotgrid.autodesk.com/ - - SHOTGRID_API_KEY=************ - - SHOTGRID_SCRIPT_NAME=DNA_local_testing - - LLM_PROVIDER=openai - - VEXA_API_KEY=********** - - VEXA_API_URL=http://vexa:8056 - - OPENAI_API_KEY=your-openai-api-key - vexa: environment: - # From https://staging.vexa.ai/dashboard/transcription - # More details: https://github.com/Vexa-ai/vexa/blob/main/docs/vexa-lite-deployment.md - - TRANSCRIBER_API_KEY=********************** + - TRANSCRIBER_API_KEY=your-transcription-api-key - TRANSCRIBER_URL=https://transcription.vexa.ai/v1/audio/transcriptions ``` -### 3. Start the Backend Stack +**Option 2 — Self-hosted transcription service:** ```bash -cd backend -make start-local +git clone https://github.com/Vexa-ai/vexa.git +cd vexa/services/transcription-service +cp .env.example .env +# Edit .env: set API_TOKEN and optionally DEVICE=cpu for no GPU +docker compose up -d +# Wait for "Model loaded successfully" in: docker logs ``` -This starts: -- **MongoDB** - Database (port 27017) -- **DNA API** - FastAPI backend (port 8000) -- **Vexa** - Transcription service (port 8056) -- **Vexa Dashboard** - Admin UI (port 3001) +Then in `backend/docker-compose.local.vexa.yml`: +```yaml +services: + vexa: + environment: + - TRANSCRIBER_URL=http://localhost:8083/v1/audio/transcriptions + - TRANSCRIBER_API_KEY=your-api-token-value +``` -### 4. Get your Vexa API key +**Option 3 — Skip transcription for now:** -Once the stack is running you can get your Vexa API key from the Vexa Dashboard. http://localhost:3001/ +Add `SKIP_TRANSCRIPTION_CHECK=true` to `backend/docker-compose.local.vexa.yml` so Vexa starts without a working transcription backend: -### 5. Start the Frontend +```yaml +services: + vexa: + environment: + - SKIP_TRANSCRIPTION_CHECK=true +``` -In a new terminal: +You can enable transcription later by removing that line and adding your `TRANSCRIBER_API_KEY`, then restarting with `cd backend && make restart-local`. + +### 5. Install Frontend Dependencies ```bash cd frontend npm install ``` -Copy the example env file: +### 6. Generate a Vexa API Key + +The bootstrap script automates this via the Vexa admin API. To do it manually: + +**a. Start only the Vexa services:** ```bash -cp packages/app/.env.example packages/app/.env +cd backend +docker compose -f docker-compose.vexa.yml -f docker-compose.local.vexa.yml up -d vexa vexa-db +``` + +**b. Wait for the Vexa admin API to become ready** (may take ~30 s on first pull): + +```bash +until curl -sf -H "X-Admin-API-Key: your-admin-token" \ + http://localhost:8056/admin/users -o /dev/null; do + echo "Waiting for Vexa..."; sleep 3 +done +``` + +**c. Create a local dev user:** + +```bash +curl -s -X POST \ + -H "X-Admin-API-Key: your-admin-token" \ + -H "Content-Type: application/json" \ + -d '{"email":"dna-local@example.com","name":"DNA Local Dev"}' \ + http://localhost:8056/admin/users +``` + +Note the `id` field from the response. + +**d. Generate an API token for that user:** + +```bash +curl -s -X POST \ + -H "X-Admin-API-Key: your-admin-token" \ + http://localhost:8056/admin/users//tokens ``` +Note the `token` field from the response. + +**e. Write the token into your local compose file:** + +In `backend/docker-compose.local.yml`, set: + +```yaml +- VEXA_API_KEY= +``` + +Alternatively, you can retrieve a key from the Vexa Dashboard UI at http://localhost:3001 once the stack is running. + +### 7. Start the Full Stack + ```bash +cd backend +make start-local +``` + +This runs: + +```bash +docker compose \ + -f docker-compose.yml \ + -f docker-compose.vexa.yml \ + -f docker-compose.debug.yml \ + -f docker-compose.local.yml \ + -f docker-compose.local.vexa.yml \ + up --build -d +``` + +Services started: + +- **MongoDB** — database (port 27017) +- **DNA API** — FastAPI backend (port 8000) +- **Vexa** — transcription service (port 8056) +- **Vexa Dashboard** — admin UI (port 3001) + +### 8. Start the Frontend + +In a new terminal: + +```bash +cd frontend npm run dev ``` The React app will be available at `http://localhost:5173`. -### 6. Verify Everything is Running +### 9. Verify Everything is Running | Service | URL | Description | |---------|-----|-------------| @@ -130,6 +244,8 @@ The React app will be available at `http://localhost:5173`. | Vexa Dashboard | http://localhost:3001 | Transcription admin | | Frontend | http://localhost:5173 | React application | +--- + ## Environment Variables Reference ### Backend API (`api` service) @@ -162,6 +278,9 @@ The React app will be available at `http://localhost:5173`. | `ADMIN_API_TOKEN` | No | `your-admin-token` | Admin token for Vexa management | | `TRANSCRIBER_URL` | No | (vexa.ai) | Transcription API endpoint | | `TRANSCRIBER_API_KEY` | Yes | - | API key for transcription service | +| `SKIP_TRANSCRIPTION_CHECK` | No | - | Set to `true` to start Vexa without a working transcription backend | + +--- ## Common Commands @@ -225,6 +344,8 @@ npm run format npm run typecheck ``` +--- + ## Architecture Overview ``` @@ -255,10 +376,14 @@ The DNA API serves as the central hub: - Manages Vexa subscriptions for transcription events - Broadcasts segment and bot status events to connected frontend clients +--- + ## Mock Production Tracking Setup When you set **`PRODTRACK_PROVIDER=mock`**, the backend uses a read-only mock provider backed by SQLite (`backend/src/dna/prodtrack_providers/mock_data/mock.db`). The app runs normally with this data so you can develop and test the UI without a ShotGrid seat. +**Production tracking (ShotGrid):** To run without a ShotGrid seat, set **`PRODTRACK_PROVIDER=mock`** in `docker-compose.local.yml`. The mock provider uses read-only SQLite with pre-seeded data. To use real ShotGrid, set `PRODTRACK_PROVIDER=shotgrid` (or leave it unset) and add `SHOTGRID_URL`, `SHOTGRID_SCRIPT_NAME`, and `SHOTGRID_API_KEY`. + ### Using the mock provider - In `docker-compose.local.yml`, set **`PRODTRACK_PROVIDER=mock`**. You do not need to set any ShotGrid variables when using the mock. @@ -275,7 +400,7 @@ cd backend SHOTGRID_API_KEY='your-api-key' make seed-mock-db # Or run the seed script directly in the API container with custom project -docker-compose -f docker-compose.yml -f docker-compose.local.yml run --rm api \ +docker compose -f docker-compose.yml -f docker-compose.local.yml run --rm api \ python -m dna.prodtrack_providers.mock_data.seed_db \ --project-id YOUR_PROJECT_ID \ --url https://yoursite.shotgrid.autodesk.com \ @@ -289,6 +414,8 @@ docker-compose -f docker-compose.yml -f docker-compose.local.yml run --rm api \ The mock provider is **read-only**: it does not write to ShotGrid or to the SQLite file at runtime. Writes such as publishing notes will raise an error when using the mock provider. +--- + ## Docker Compose Files The backend uses multiple compose files that are layered together: @@ -296,20 +423,24 @@ The backend uses multiple compose files that are layered together: | File | Purpose | |------|---------| | `docker-compose.yml` | Base configuration with all services | -| `docker-compose.local.yml` | **Your local overrides** (API keys, credentials) | | `docker-compose.vexa.yml` | Vexa transcription service | -| `docker-compose.debug.yml` | Additional debug services (optional) | +| `docker-compose.debug.yml` | Additional debug services | +| `docker-compose.local.yml` | **Your local overrides** (API keys, LLM credentials) | +| `docker-compose.local.vexa.yml` | **Your local Vexa overrides** (transcription API key) | The `make start-local` command combines these: ```bash -docker-compose -f docker-compose.yml \ - -f docker-compose.local.yml \ +docker compose -f docker-compose.yml \ -f docker-compose.vexa.yml \ -f docker-compose.debug.yml \ - up --build + -f docker-compose.local.yml \ + -f docker-compose.local.vexa.yml \ + up --build -d ``` +--- + ## Accessing Services ### MongoDB @@ -330,6 +461,8 @@ Interactive API documentation is available at: - **Swagger UI:** http://localhost:8000/docs - **ReDoc:** http://localhost:8000/redoc +--- + ## Development Workflow ### Hot Reload @@ -348,7 +481,7 @@ cd backend make test # Run specific test file -docker-compose -f docker-compose.yml -f docker-compose.local.yml \ +docker compose -f docker-compose.yml -f docker-compose.local.yml \ run --rm api python -m pytest tests/test_transcription_service.py -v ``` @@ -367,6 +500,8 @@ npm run test:run npm run test:coverage ``` +--- + ## Troubleshooting ### Container Won't Start @@ -404,6 +539,8 @@ make start-local 2. Ensure the API is running and healthy 3. The frontend connects to `ws://localhost:8000/ws` by default +--- + ## Stopping Everything ```bash @@ -412,5 +549,5 @@ cd backend make stop-local # Remove volumes (clean slate) -docker-compose -f docker-compose.yml -f docker-compose.local.yml down -v +docker compose -f docker-compose.yml -f docker-compose.local.yml down -v ```