diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..97ffa5a --- /dev/null +++ b/.env.example @@ -0,0 +1,37 @@ +# ─── Chrysopedia Environment Variables ─── + +# PostgreSQL +POSTGRES_USER=chrysopedia +POSTGRES_PASSWORD=changeme +POSTGRES_DB=chrysopedia +DATABASE_URL=postgresql+asyncpg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@chrysopedia-db:5432/${POSTGRES_DB} + +# Redis (Celery broker) +REDIS_URL=redis://chrysopedia-redis:6379/0 + +# LLM endpoint (OpenAI-compatible) +LLM_API_URL=https://friend-openwebui.example.com/api +LLM_API_KEY=sk-changeme +LLM_MODEL=qwen2.5-72b +LLM_FALLBACK_URL=http://localhost:11434/v1 +LLM_FALLBACK_MODEL=qwen2.5:14b-q8_0 + +# Embedding endpoint +EMBEDDING_API_URL=http://localhost:11434/v1 +EMBEDDING_MODEL=nomic-embed-text + +# Qdrant +QDRANT_URL=http://qdrant:6333 +QDRANT_COLLECTION=chrysopedia + +# Application +APP_ENV=production +APP_LOG_LEVEL=info +APP_SECRET_KEY=changeme-generate-a-real-secret + +# File storage paths (inside container) +TRANSCRIPT_STORAGE_PATH=/data/transcripts +VIDEO_METADATA_PATH=/data/video_meta + +# Review mode toggle +REVIEW_MODE=true diff --git a/.gitignore b/.gitignore index 617259b..8b0455e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,30 @@ .gsd/gsd.db-wal .gsd/event-log.jsonl .gsd/state-manifest.json + +# ── GSD baseline (auto-generated) ── +.DS_Store +Thumbs.db +*.swp +*.swo +*~ +.idea/ +.vscode/ +*.code-workspace +.env +.env.* +!.env.example +node_modules/ +.next/ +dist/ +build/ +__pycache__/ +*.pyc +.venv/ +venv/ +target/ +vendor/ +*.log +coverage/ +.cache/ +tmp/ diff --git a/backend/main.py b/backend/main.py new file mode 100644 index 0000000..094d06b --- /dev/null +++ b/backend/main.py @@ -0,0 +1,28 @@ +"""Chrysopedia API — Knowledge extraction and retrieval system.""" + +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware + +app = FastAPI( + title="Chrysopedia API", + description="Knowledge extraction and retrieval for music production content", + version="0.1.0", +) + +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + +@app.get("/health") +async def health_check(): + return {"status": "ok", "service": "chrysopedia-api"} + + +@app.get("/api/v1/health") +async def api_health(): + return {"status": "ok", "version": "0.1.0"} diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000..95a393b --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,11 @@ +fastapi>=0.115.0,<1.0 +uvicorn[standard]>=0.32.0,<1.0 +sqlalchemy[asyncio]>=2.0,<3.0 +asyncpg>=0.30.0,<1.0 +alembic>=1.14.0,<2.0 +pydantic>=2.0,<3.0 +pydantic-settings>=2.0,<3.0 +celery[redis]>=5.4.0,<6.0 +redis>=5.0,<6.0 +python-dotenv>=1.0,<2.0 +httpx>=0.27.0,<1.0 diff --git a/config/canonical_tags.yaml b/config/canonical_tags.yaml new file mode 100644 index 0000000..3b3cc0a --- /dev/null +++ b/config/canonical_tags.yaml @@ -0,0 +1,42 @@ +# Canonical tags — 6 top-level production categories +# Sub-topics grow organically during pipeline extraction +categories: + - name: Sound design + description: Creating and shaping sounds from scratch or samples + sub_topics: [bass, drums, kick, snare, hi-hat, percussion, pads, leads, fx, foley, vocals, textures] + + - name: Mixing + description: Balancing, processing, and spatializing elements + sub_topics: [eq, compression, bus processing, reverb, delay, stereo imaging, gain staging, automation] + + - name: Synthesis + description: Methods of generating sound + sub_topics: [fm, wavetable, granular, additive, subtractive, modular, physical modeling] + + - name: Arrangement + description: Structuring a track from intro to outro + sub_topics: [song structure, transitions, tension, energy flow, breakdowns, drops] + + - name: Workflow + description: Creative process, session management, productivity + sub_topics: [daw setup, templates, creative process, collaboration, file management, resampling] + + - name: Mastering + description: Final stage processing for release + sub_topics: [limiting, stereo width, loudness, format delivery, referencing] + +# Genre taxonomy (assigned to Creators, not techniques) +genres: + - Bass music + - Drum & bass + - Dubstep + - Halftime + - House + - Techno + - IDM + - Glitch + - Downtempo + - Neuro + - Ambient + - Experimental + - Cinematic diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..66300ad --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,113 @@ +# Chrysopedia — Docker Compose +# XPLTD convention: xpltd_chrysopedia project, bind mounts, dedicated bridge +name: xpltd_chrysopedia + +services: + # ── PostgreSQL 16 ── + chrysopedia-db: + image: postgres:16-alpine + container_name: chrysopedia-db + restart: unless-stopped + environment: + POSTGRES_USER: ${POSTGRES_USER:-chrysopedia} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD required} + POSTGRES_DB: ${POSTGRES_DB:-chrysopedia} + volumes: + - /vmPool/r/services/chrysopedia_db:/var/lib/postgresql/data + ports: + - "127.0.0.1:5433:5432" + networks: + - chrysopedia + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-chrysopedia}"] + interval: 10s + timeout: 5s + retries: 5 + + # ── Redis (Celery broker) ── + chrysopedia-redis: + image: redis:7-alpine + container_name: chrysopedia-redis + restart: unless-stopped + volumes: + - /vmPool/r/services/chrysopedia_redis:/data + networks: + - chrysopedia + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + + # ── FastAPI application ── + chrysopedia-api: + build: + context: . + dockerfile: docker/Dockerfile.api + container_name: chrysopedia-api + restart: unless-stopped + env_file: + - path: .env + required: false + environment: + DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-chrysopedia}:${POSTGRES_PASSWORD}@chrysopedia-db:5432/${POSTGRES_DB:-chrysopedia} + REDIS_URL: redis://chrysopedia-redis:6379/0 + volumes: + - ./backend:/app + - /vmPool/r/services/chrysopedia_data:/data + ports: + - "127.0.0.1:8000:8000" + depends_on: + chrysopedia-db: + condition: service_healthy + chrysopedia-redis: + condition: service_healthy + networks: + - chrysopedia + + # ── Celery worker (pipeline stages 2-5) ── + chrysopedia-worker: + build: + context: . + dockerfile: docker/Dockerfile.api + container_name: chrysopedia-worker + restart: unless-stopped + env_file: + - path: .env + required: false + environment: + DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-chrysopedia}:${POSTGRES_PASSWORD}@chrysopedia-db:5432/${POSTGRES_DB:-chrysopedia} + REDIS_URL: redis://chrysopedia-redis:6379/0 + command: ["celery", "-A", "worker", "worker", "--loglevel=info"] + volumes: + - ./backend:/app + - /vmPool/r/services/chrysopedia_data:/data + - ./prompts:/prompts:ro + depends_on: + chrysopedia-db: + condition: service_healthy + chrysopedia-redis: + condition: service_healthy + networks: + - chrysopedia + + # ── React web UI (nginx) ── + chrysopedia-web: + build: + context: . + dockerfile: docker/Dockerfile.web + container_name: chrysopedia-web + restart: unless-stopped + ports: + - "127.0.0.1:3000:80" + depends_on: + - chrysopedia-api + networks: + - chrysopedia + +networks: + chrysopedia: + driver: bridge + ipam: + config: + - subnet: 172.24.0.0/24 diff --git a/docker/Dockerfile.api b/docker/Dockerfile.api new file mode 100644 index 0000000..b0760ae --- /dev/null +++ b/docker/Dockerfile.api @@ -0,0 +1,19 @@ +FROM python:3.12-slim + +WORKDIR /app + +# System deps +RUN apt-get update && apt-get install -y --no-install-recommends \ + gcc libpq-dev \ + && rm -rf /var/lib/apt/lists/* + +# Python deps (cached layer) +COPY backend/requirements.txt /app/requirements.txt +RUN pip install --no-cache-dir -r requirements.txt + +# Application code +COPY backend/ /app/ + +EXPOSE 8000 + +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/docker/Dockerfile.web b/docker/Dockerfile.web new file mode 100644 index 0000000..995be30 --- /dev/null +++ b/docker/Dockerfile.web @@ -0,0 +1,16 @@ +FROM node:22-alpine AS build + +WORKDIR /app +COPY frontend/package*.json ./ +RUN npm ci --ignore-scripts +COPY frontend/ . +RUN npm run build + +FROM nginx:1.27-alpine + +COPY --from=build /app/dist /usr/share/nginx/html +COPY docker/nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/docker/nginx.conf b/docker/nginx.conf new file mode 100644 index 0000000..8efc8a8 --- /dev/null +++ b/docker/nginx.conf @@ -0,0 +1,24 @@ +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # SPA fallback + location / { + try_files $uri $uri/ /index.html; + } + + # API proxy + location /api/ { + proxy_pass http://chrysopedia-api:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /health { + proxy_pass http://chrysopedia-api:8000; + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..2949ec1 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,10 @@ +{ + "name": "chrysopedia-web", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "echo 'placeholder — install a framework first'", + "build": "echo 'placeholder build' && mkdir -p dist && echo 'Chrysopedia

Chrysopedia

Web UI placeholder

' > dist/index.html" + } +} diff --git a/prompts/README.md b/prompts/README.md new file mode 100644 index 0000000..2a73359 --- /dev/null +++ b/prompts/README.md @@ -0,0 +1,2 @@ +# Prompt templates for LLM pipeline stages +# These files are bind-mounted read-only into the worker container. diff --git a/whisper/README.md b/whisper/README.md new file mode 100644 index 0000000..c0c6651 --- /dev/null +++ b/whisper/README.md @@ -0,0 +1,3 @@ +# Chrysopedia — Whisper Transcription + +Desktop transcription script. See `transcribe.py` for usage.