feat: Created full Docker Compose project (xpltd_chrysopedia) with Post…

- "docker-compose.yml"
- ".env.example"
- "docker/Dockerfile.api"
- "docker/Dockerfile.web"
- "docker/nginx.conf"
- "backend/main.py"
- "backend/requirements.txt"
- "config/canonical_tags.yaml"

GSD-Task: S01/T01
This commit is contained in:
jlightner 2026-03-29 21:42:56 +00:00
parent 0904939660
commit c404270f49
12 changed files with 332 additions and 0 deletions

37
.env.example Normal file
View file

@ -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

27
.gitignore vendored
View file

@ -4,3 +4,30 @@
.gsd/gsd.db-wal .gsd/gsd.db-wal
.gsd/event-log.jsonl .gsd/event-log.jsonl
.gsd/state-manifest.json .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/

28
backend/main.py Normal file
View file

@ -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"}

11
backend/requirements.txt Normal file
View file

@ -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

View file

@ -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

113
docker-compose.yml Normal file
View file

@ -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

19
docker/Dockerfile.api Normal file
View file

@ -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"]

16
docker/Dockerfile.web Normal file
View file

@ -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;"]

24
docker/nginx.conf Normal file
View file

@ -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;
}
}

10
frontend/package.json Normal file
View file

@ -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 '<!DOCTYPE html><html><head><title>Chrysopedia</title></head><body><h1>Chrysopedia</h1><p>Web UI placeholder</p></body></html>' > dist/index.html"
}
}

2
prompts/README.md Normal file
View file

@ -0,0 +1,2 @@
# Prompt templates for LLM pipeline stages
# These files are bind-mounted read-only into the worker container.

3
whisper/README.md Normal file
View file

@ -0,0 +1,3 @@
# Chrysopedia — Whisper Transcription
Desktop transcription script. See `transcribe.py` for usage.