File size: 4,330 Bytes
703a33a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | from __future__ import annotations
import json
import os
from pathlib import Path
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
ROOT_DIR = Path(__file__).resolve().parents[2]
DATA_DIR = ROOT_DIR / "app" / "data"
PUBLIC_DIR = ROOT_DIR / "app" / "public"
DIST_DIR = ROOT_DIR / "app" / "frontend" / "dist"
ALLOWED_DATA_FILES = {
"features.json",
"faq.json",
"route_model_matrix.json",
"docs_nav.json",
"voice_seed_library.json",
"workflow_examples.json",
"architecture_layers.json",
"site_manifest.json",
}
CONTRACT_FILES = {
"voice_registry": "contracts/voice_registry_shape.json",
"route_model_matrix": "contracts/route_model_matrix_shape.json",
"seed_voice_library": "contracts/seed_voice_library_shape.json",
"provider_config": "contracts/provider_config_shape.json",
}
def load_json(relative_path: str) -> dict | list:
with (DATA_DIR / relative_path).open("r", encoding="utf-8") as handle:
return json.load(handle)
class DemoRequest(BaseModel):
workflow_id: str
app = FastAPI(title="Aether Voice Studio Space API", version="1.0.0")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=False,
allow_methods=["*"],
allow_headers=["*"],
)
if DIST_DIR.exists():
assets_dir = DIST_DIR / "assets"
if assets_dir.exists():
app.mount("/assets", StaticFiles(directory=assets_dir), name="assets")
if PUBLIC_DIR.exists():
app.mount("/public", StaticFiles(directory=PUBLIC_DIR), name="public")
@app.get("/api/health")
def health() -> dict:
return {"ok": True, "demo_mode": os.getenv("DEMO_MODE", "true").lower() == "true"}
@app.get("/api/config")
def config() -> dict:
return {
"demoMode": os.getenv("DEMO_MODE", "true").lower() == "true",
"spaceTitle": "Aether Voice Studio",
"singlePort": int(os.getenv("PORT", "7860")),
}
@app.get("/api/content")
def content() -> dict:
return {
"site": load_json("site_manifest.json"),
"features": load_json("features.json"),
"routes": load_json("route_model_matrix.json"),
"docsNav": load_json("docs_nav.json"),
"faq": load_json("faq.json"),
"seedLibrary": load_json("voice_seed_library.json"),
"workflows": load_json("workflow_examples.json"),
"architecture": load_json("architecture_layers.json"),
}
@app.get("/api/data/{filename}")
def data_file(filename: str) -> dict | list:
if filename not in ALLOWED_DATA_FILES:
raise HTTPException(status_code=404, detail="Unknown data file")
return load_json(filename)
@app.get("/api/contracts")
def contracts() -> dict:
return {
name: {
"endpoint": f"/api/contracts/{name}",
"download": f"/api/contracts/{name}?download=true",
}
for name in CONTRACT_FILES
}
@app.get("/api/contracts/{contract_name}")
def contract_file(contract_name: str, download: bool = False):
relative_path = CONTRACT_FILES.get(contract_name)
if not relative_path:
raise HTTPException(status_code=404, detail="Unknown contract")
file_path = DATA_DIR / relative_path
if download:
return FileResponse(file_path, media_type="application/json", filename=file_path.name)
return load_json(relative_path)
@app.post("/api/demo/simulate")
def simulate_demo(request: DemoRequest) -> dict:
payloads = load_json("demo_payloads.json")
workflow_id = request.workflow_id
selected = payloads.get(workflow_id)
if selected is None:
raise HTTPException(status_code=404, detail="Unknown workflow")
return {
"demoMode": os.getenv("DEMO_MODE", "true").lower() == "true",
"workflowId": workflow_id,
"result": selected,
}
@app.get("/{full_path:path}")
def spa_fallback(full_path: str):
requested = DIST_DIR / full_path
if full_path and requested.exists() and requested.is_file():
return FileResponse(requested)
index_file = DIST_DIR / "index.html"
if index_file.exists():
return FileResponse(index_file)
raise HTTPException(status_code=503, detail="Frontend build is unavailable")
|