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")