Skip to content
Closed
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
174 changes: 79 additions & 95 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,106 +1,90 @@
# OpenMontage - Environment Variables
# Copy this to .env and fill in your keys
# =============================================================================
# Panda AI Video System — environment template
# Copy to `.env` and fill in real values ON THE EC2 BOX. Never commit `.env`.
# `.env` is gitignored; keep every secret here, never in code or the repo.
# =============================================================================

# --- Image + video gateway ---
# FLUX images, Google Veo video, Kling video, MiniMax video, Recraft images.
# Get one at https://fal.ai/dashboard/keys
FAL_KEY=
# Alias for FAL_KEY (some SDKs/docs use this name); either one is read.
FAL_AI_API_KEY=
# -----------------------------------------------------------------------------
# 1) LLM — the agent's brain (Claude Code / Claude Agent SDK)
# We use OpenRouter (no direct Anthropic key). OpenRouter exposes a native
# Anthropic Messages API, so no proxy is needed.
# Use OpenRouter's CLAUDE models (Sonnet/Opus) — the pipeline is tool-heavy.
# -----------------------------------------------------------------------------
ANTHROPIC_BASE_URL=https://openrouter.ai/api
ANTHROPIC_AUTH_TOKEN=sk-or-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ANTHROPIC_API_KEY=
# Optional: pin the model the SDK/CLI requests (OpenRouter model id).
# ANTHROPIC_MODEL=anthropic/claude-opus-4.1

# --- Replicate ---
# Replicate-hosted video gen (seedance_replicate). Needed to make the
# Replicate-backed Seedance path selectable alongside the fal.ai one.
# Get one at https://replicate.com/account/api-tokens
REPLICATE_API_TOKEN=
# -----------------------------------------------------------------------------
# 1b) Dify launcher + runner
# DIFY_RUNNER=mock → fakes script/gen (Dify handshake works, no real AI video)
# DIFY_RUNNER=claude→ drives the REAL agent (Claude Code headless) — needs the LLM env
# above + the Higgsfield MCP below.
# -----------------------------------------------------------------------------
DIFY_RUNNER=mock
DIFY_TOKEN=
DIFY_DATA_DIR=/opt/panda/data
# claude runner only:
CLAUDE_BIN=claude
CLAUDE_EXTRA_ARGS=--dangerously-skip-permissions # headless can't answer permission prompts
CLAUDE_TIMEOUT_S=3600
PANDA_PIPELINE_TYPE=panda-video
# OPENMONTAGE_PROJECTS_DIR=/opt/panda/projects # where checkpoints/artifacts live

# --- Higgsfield ---
# Higgsfield Cloud key (higgsfield_video). Pair with the secret below,
# or use the combined HIGGSFIELD_KEY="<key>:<secret>" form instead.
# -----------------------------------------------------------------------------
# 2) Higgsfield — video/image generation via MCP (NOT the Cloud REST API)
# HIGGSFIELD_MCP_BRIDGE=1 tells the bridge tool "this agent has a working
# Higgsfield MCP connection". On a HEADLESS box you must connect a
# server-friendly MCP (self-hosted key+secret or CLI) — the claude.ai
# browser connector does NOT work headless. See setup guide §5.3 / §8.
# -----------------------------------------------------------------------------
HIGGSFIELD_MCP_BRIDGE=1
# Only needed for the self-hosted MCP server option (headless):
HIGGSFIELD_API_KEY=
HIGGSFIELD_API_SECRET=
# HIGGSFIELD_KEY= # Combined key:secret — set this INSTEAD of the _KEY/_SECRET pair if you prefer.

# --- Kling official direct API ---
# Official Kling API key; enables video, image, TTS, avatar, lip sync.
KLING_API_KEY=
# Optional endpoint override; leave blank for default https://api-singapore.klingai.com
# Mainland China accounts can use https://api-beijing.klingai.com
KLING_API_BASE_URL=

# --- Google (one key unlocks image gen + TTS + video) ---
# Google Imagen images, Google Cloud TTS (700+ voices, 50+ languages),
# Gemini Omni video (generation + conversational editing, paid tier).
# Get one at https://aistudio.google.com/apikey
GOOGLE_API_KEY=
# GEMINI_API_KEY= # Alias for GOOGLE_API_KEY (takes precedence when both are set)
# Alternative to the API key: service-account JSON auth.
# TTS uses Cloud Text-to-Speech; Imagen routes to Vertex AI.
# Path to a service-account JSON key file.
GOOGLE_APPLICATION_CREDENTIALS=
# GCP project id (required for Imagen via Vertex AI).
GOOGLE_CLOUD_PROJECT=
# Vertex AI region, default us-central1.
GOOGLE_CLOUD_LOCATION=
HIGGSFIELD_SECRET=
# NOTE: do NOT set these to use the rejected Cloud REST tool (higgsfield_video.py).
# They exist solely to authenticate a self-hosted Higgsfield MCP server.

# --- Voice ---
# TTS narration, music generation, sound effects.
# -----------------------------------------------------------------------------
# 3) ElevenLabs — voiceover (via MCP or the tool)
# -----------------------------------------------------------------------------
ELEVENLABS_API_KEY=
# OpenAI TTS fallback and GPT Image 2 image generation.
OPENAI_API_KEY=
# Grok image generation/editing and Grok video generation.
XAI_API_KEY=
# Volcengine Doubao Speech TTS (new console API Key).
DOUBAO_SPEECH_API_KEY=
# Default Doubao speaker/voice type, e.g. zh_female_vv_uranus_bigtts.
DOUBAO_SPEECH_VOICE_TYPE=
# Piper local voices do not require env vars; install `piper-tts` via pip

# --- DashScope (Alibaba Cloud Bailian) ---
# Qwen image gen (qwen-image-2.0-pro), TTS (qwen3-tts-flash), ASR with word timestamps (qwen3-asr-flash-filetrans).
# Get one at https://dashscope.aliyun.com/
DASHSCOPE_API_KEY=

# --- Music ---
# Suno AI music generation (full songs, instrumentals, any genre).
SUNO_API_KEY=
# -----------------------------------------------------------------------------
# 4) fal.ai — Seedance video (character-consistent generation), if used
# -----------------------------------------------------------------------------
FAL_KEY=

# --- Video Generation ---
# HeyGen API (VEO, Sora, Runway, Kling, Seedance via single key).
HEYGEN_API_KEY=
# Runway Gen-4 (direct API, alternative to fal.ai routing).
RUNWAY_API_KEY=
# Volcengine Jimeng (即梦 AI) video generation via official API (HMAC-SHA256 V4 signing).
VOLC_ACCESSKEY=
# Secret Access Key paired with VOLC_ACCESSKEY. Get both at https://console.volcengine.com/iam/keymanage
VOLC_SECRETKEY=
# Set to "true" for local video gen (needs GPU + diffusers).
VIDEO_GEN_LOCAL_ENABLED=
# Local model: wan2.1-1.3b, wan2.1-14b, hunyuan-1.5, ltx2-local, cogvideo-5b.
VIDEO_GEN_LOCAL_MODEL=
# Modal self-hosted LTX-2 endpoint (optional).
MODAL_LTX2_ENDPOINT_URL=
# -----------------------------------------------------------------------------
# 5) Render — FOLDED IN, no separate service.
# montage-svc's render code is vendored at vendor/montage_svc and called
# in-process by the panda_render / panda_brand tools. No MONTAGE_SVC_URL.
# Optional overrides (defaults are vendor/brand and vendor/data):
# MONTAGE_BRAND_DIR — brand assets (logo, profiles, fonts)
# MONTAGE_DATA_DIR — render scratch (gitignored)
# A CJK font (msyhbd.ttc) must resolve for Chinese captions (see setup guide).
# -----------------------------------------------------------------------------
# MONTAGE_BRAND_DIR=
# MONTAGE_DATA_DIR=

# --- Stock Media ---
# Pexels stock footage/images (free).
PEXELS_API_KEY=
# Pixabay stock footage/images (free).
PIXABAY_API_KEY=
# Unsplash stock images (free developer key).
UNSPLASH_ACCESS_KEY=
# -----------------------------------------------------------------------------
# 6) AWS S3 — artifact storage (final videos, stills, reports)
# Prefer an EC2 instance role over static keys where possible.
# -----------------------------------------------------------------------------
AWS_REGION=ap-southeast-1
S3_BUCKET=pm-ai-artifacts-prod
# AWS_ACCESS_KEY_ID=
# AWS_SECRET_ACCESS_KEY=

# --- Analysis ---
# HuggingFace token — enables speaker diarization in transcriber.
HF_TOKEN=
# Speech-to-text: optional Azure AI Speech (Fast Transcription). When set, the
# agent prefers azure_stt for cloud STT; the local faster-whisper transcriber
# remains the default offline path.
# Azure AI Speech resource key ('Keys and Endpoint' page).
AZURE_SPEECH_KEY=
# Speech resource region, e.g. eastus.
AZURE_SPEECH_REGION=
# AZURE_SPEECH_ENDPOINT= # Optional: full custom endpoint URL (overrides region)
# -----------------------------------------------------------------------------
# 7) Postgres — the artifact map + review state (job_artifacts, review status)
# -----------------------------------------------------------------------------
# DATABASE_URL=postgresql://user:password@host:5432/panda_ai

# --- Avatar (local installs) ---
# WAV2LIP_PATH= # Path to cloned Wav2Lip repo (for lip sync)
# SADTALKER_PATH= # Path to cloned SadTalker repo (for talking head avatars)
# -----------------------------------------------------------------------------
# 8) WeCom (optional) — WeChat Work delivery (existing Mochi bridge)
# SECURITY: rotate the APP_SECRET that was shared in plaintext earlier.
# -----------------------------------------------------------------------------
# WECOM_CORP_ID=
# WECOM_APP_SECRET=
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,7 @@ venv/

# Backlot local cache (thumbnails)
.backlot/
vendor/data/

# Dify launcher job storage (local artifacts)
data/jobs/
4 changes: 4 additions & 0 deletions NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Notes

Panda AI video engine — work-in-progress on branch `panda-video-scaffold`.
Push/deploy pipeline verified working.
32 changes: 32 additions & 0 deletions config/panda-elements.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"_comment": "SCAFFOLD — Panda brand wiring the agent reads alongside skills/meta/higgsfield-mcp-bridge.md. Fill in the real Higgsfield element/character ids. Keys/secrets do NOT go here — env only.",
"brand": {
"name": "Panda Mobile",
"colors": { "black": "#000000", "yellow": "#fdc50d" },
"notes": "See memory: panda-mobile-brand-assets for exact logo/mascot paths, fonts."
},
"character_references": {
"_note": "Local hero turnaround sheets (multi-view) used as the master references for character consistency across stills/clips. 6336x2688 RGB.",
"panda_mascot": {
"turnaround": "brand/panda-mobile/mascot/Panda-Mobile-Smiling-Hero-Turnaround-v1.png",
"also": "brand/panda-mobile/mascot/Panda Mascot.png",
"views_dir": "brand/panda-mobile/mascot/views/"
},
"customer": {
"turnaround": "brand/panda-mobile/customer/Customer-V3-Hero-Turnaround-v1.png"
}
},
"higgsfield": {
"_note": "Element / character ids used to keep the panda + customer consistent across stills and clips. Confirm live via MCP show_reference_elements / show_characters. Seed these from the local turnaround sheets above.",
"panda_master_character_id": "TODO",
"panda_reference_element_ids": ["TODO"],
"customer_master_character_id": "TODO",
"customer_reference_element_ids": ["TODO"],
"student_ugc_element_id": "TODO — reusable id noted in memory panda-airport-arrival-ugc",
"default_model_identity": "seedance_2_0"
},
"montage_svc": {
"profiles": ["bgc", "ugc"],
"default_profile": "bgc"
}
}
53 changes: 53 additions & 0 deletions deploy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Deploying the Panda launcher on EC2

Get the launcher running on the box and reachable at `dev.om.mvnoc.ai`, so Dify can call it.
The launcher is the API front door; it drives the OpenMontage agent through the 4 gates.

## Steps (on the EC2 box)
```bash
# 1) clone the fork (permanent path — NOT /tmp)
sudo mkdir -p /opt/panda && sudo chown "$USER" /opt/panda
git clone https://github.com/Philipcyrus/OpenMontage-private.git /opt/panda/OpenMontage-prod
cd /opt/panda/OpenMontage-prod
git checkout panda-video-scaffold

# 2) install (system deps + venv + launcher deps + smoke test)
bash deploy/install.sh

# 3) configure env
nano .env # set DIFY_TOKEN (long random), DIFY_RUNNER=mock, DIFY_DATA_DIR=/opt/panda/data

# 4) free port 8501 — retire the old montage-svc (replaced by this engine)
sudo systemctl disable --now montage-svc # skip if it isn't a systemd service

# 5) run the launcher as a service (listens on 8501)
sudo cp deploy/panda-launcher.service /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now panda-launcher
systemctl status panda-launcher --no-pager

# 6) reverse proxy — NO CHANGE NEEDED. dev.om.mvnoc.ai already forwards to 8501;
# the launcher now answers there instead of montage-svc.

# 7) verify it's live
curl -s -H "X-Dify-Token: <token>" https://dev.om.mvnoc.ai/health
# -> {"status":"ok","runner":"mock"}
```

## Then connect Dify
Point Dify at the base URL and follow `dify_launcher/DIFY_INTEGRATION.md`:
- `BASE_URL = https://dev.om.mvnoc.ai` (root — proxy already forwards to 8501)

## Two things to know
1. **Runner:** `DIFY_RUNNER=mock` proves the whole Dify handshake (fakes script/gen, but
REALLY renders a clean video). Switch to `claude` only after the real `ClaudeCodeRunner`
+ Claude Code + OpenRouter + the Higgsfield MCP are wired on the box.
2. **Storage:** local under `DIFY_DATA_DIR` (default `./data`). Artifacts + job state live
there; `data/jobs/` is gitignored. Swap for S3 later (Phase 5) with no API change.

## Files here
| file | purpose |
|---|---|
| `install.sh` | system deps + venv + launcher deps + import/render smoke test |
| `panda-launcher.service` | systemd unit (uvicorn on 127.0.0.1:8600) |
| `nginx-panda.conf` | reverse-proxy block (subpath or subdomain) |
| `requirements-launcher.txt` | minimal deps for launcher + render (mock) |
48 changes: 48 additions & 0 deletions deploy/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
# Prepare the Panda launcher on the EC2 box. Run AFTER cloning the repo, e.g.:
# git clone https://github.com/Philipcyrus/OpenMontage-private.git /opt/panda/OpenMontage-prod
# cd /opt/panda/OpenMontage-prod && bash deploy/install.sh
set -euo pipefail

APP_DIR="${APP_DIR:-$(cd "$(dirname "$0")/.." && pwd)}"
cd "$APP_DIR"
echo "== installing in $APP_DIR =="

# 1) system deps (ffmpeg + python). CJK font is already BUNDLED in the repo
# (vendor/brand/fonts/msyhbd.ttc), so no font install is strictly required.
if command -v dnf >/dev/null 2>&1; then
sudo dnf install -y python3 python3-pip ffmpeg google-noto-sans-cjk-ttc-fonts || true
elif command -v apt >/dev/null 2>&1; then
sudo apt-get update && sudo apt-get install -y python3 python3-venv python3-pip ffmpeg fonts-noto-cjk || true
fi

# 2) python venv + launcher deps
python3 -m venv .venv
. .venv/bin/activate
pip install -U pip
pip install -r deploy/requirements-launcher.txt

# 3) env file
if [ ! -f .env ]; then
cp .env.example .env
echo ">> created .env from .env.example — EDIT IT (set DIFY_TOKEN, DIFY_RUNNER, keys)."
fi

# 4) smoke test the render path + launcher import
. .venv/bin/activate
python - <<'PY'
import sys, pathlib
sys.path.insert(0, str(pathlib.Path('vendor').resolve()))
from montage_svc.config import has_ffmpeg
from dify_launcher.app import app # noqa
print("ffmpeg on PATH:", has_ffmpeg())
print("launcher imports: OK")
PY

echo
echo "== done =="
echo "Next:"
echo " 1) edit .env (DIFY_TOKEN, DIFY_RUNNER=mock|claude, DIFY_DATA_DIR)"
echo " 2) sudo cp deploy/panda-launcher.service /etc/systemd/system/ && sudo systemctl daemon-reload && sudo systemctl enable --now panda-launcher"
echo " 3) add deploy/nginx-panda.conf to your reverse proxy for dev.om.mvnoc.ai"
echo " 4) test: curl -s -H \"X-Dify-Token: \$DIFY_TOKEN\" https://dev.om.mvnoc.ai/panda/health"
33 changes: 33 additions & 0 deletions deploy/nginx-panda.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# ── CURRENT SETUP: no reverse-proxy change needed ────────────────────────────
# dev.om.mvnoc.ai already forwards to port 8501. The launcher now listens on 8501
# (replacing montage-svc), so the existing proxy config reaches it unchanged.
# => Dify BASE_URL = https://dev.om.mvnoc.ai
#
# You may want to bump these on the EXISTING dev.om.mvnoc.ai server block, since
# agent runs are long and stills can be large:
# proxy_read_timeout 600s;
# client_max_body_size 200m;
#
# The block below is OPTIONAL — only if you later want the engine on its own
# subdomain instead of reusing 8501.

# ── OPTIONAL: dedicated SUBDOMAIN (only if not reusing 8501) ──────────────────
server {
listen 443 ssl;
server_name panda.om.mvnoc.ai;

# ssl_certificate ... ; # reuse your existing cert/wildcard
# ssl_certificate_key ... ;

location / {
proxy_pass http://127.0.0.1:8501;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_read_timeout 600s;
client_max_body_size 200m;
}

# => Dify BASE_URL = https://panda.om.mvnoc.ai
# (and REMOVE --root-path /panda from panda-launcher.service)
}
19 changes: 19 additions & 0 deletions deploy/panda-launcher.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[Unit]
Description=Panda AI - Dify Launcher (OpenMontage engine front door)
After=network.target

[Service]
# Adjust User + paths to your box. Assumes clone at /opt/panda/OpenMontage-prod.
User=ec2-user
WorkingDirectory=/opt/panda/OpenMontage-prod
EnvironmentFile=/opt/panda/OpenMontage-prod/.env
# Runs on 8501 — the port dev.om.mvnoc.ai already forwards to (replacing montage-svc).
# Proxy points root -> 8501, so the launcher serves at root: no --root-path needed.
# STOP montage-svc first so 8501 is free (see deploy/README.md).
ExecStart=/opt/panda/OpenMontage-prod/.venv/bin/uvicorn dify_launcher.app:app \
--host 127.0.0.1 --port 8501
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target
8 changes: 8 additions & 0 deletions deploy/requirements-launcher.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Minimal deps to run the Dify launcher + the folded render (mock runner).
# The FULL agent (real ClaudeCodeRunner: all tools) additionally needs the engine's
# top-level requirements.txt + Claude Code + the MCP servers.
fastapi>=0.110
uvicorn[standard]>=0.27
pydantic>=2.6
pillow>=10.0
# ffmpeg + ffprobe must be on PATH (installed by install.sh)
Loading