Spaces:
Sleeping
Sleeping
File size: 9,974 Bytes
71b4454 9b613a9 71b4454 dd7e956 71b4454 8b4653e 71b4454 | 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 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | import io
import json
import os
import logging
import uuid
import asyncio
import zipfile
from datetime import datetime
from contextlib import asynccontextmanager
from typing import Any, Optional, List
import asyncpg
import httpx
from fastapi import FastAPI, File, HTTPException, UploadFile, BackgroundTasks
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field
from starlette.concurrency import run_in_threadpool
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("dolor3v-unified-backend")
# ---------------------------------------------------------------------------
# Config: LLM Providers
# ---------------------------------------------------------------------------
GROQ_API_KEY = os.environ.get("GROQ_API_KEY", "")
CEREBRAS_API_KEY = os.environ.get("CEREBRAS_API_KEY", "")
OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY", "")
GROQ_URL = "https://api.groq.com/openai/v1/chat/completions"
CEREBRAS_URL = "https://api.cerebras.ai/v1/chat/completions"
OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions"
VIRTUAL_MODELS = {
"auto": None,
"groq-llama-3.3-70b": ("groq", "llama-3.3-70b-versatile"),
"cerebras-glm-4.7": ("cerebras", "zai-glm-4.7"),
"openrouter-gpt-oss-120b-free": ("openrouter", "openai/gpt-oss-120b:free"),
}
AUTO_CHAIN = [
("groq", "llama-3.3-70b-versatile"),
("cerebras", "zai-glm-4.7"),
("openrouter", "openai/gpt-oss-120b:free"),
]
# ---------------------------------------------------------------------------
# Config: Postgres with Dual-Mode In-Memory Fallbacks
# ---------------------------------------------------------------------------
POSTGRES_SERVER = os.environ.get("POSTGRES_SERVER", "")
POSTGRES_PORT = os.environ.get("POSTGRES_PORT", "5432")
POSTGRES_USER = os.environ.get("POSTGRES_USER", "")
POSTGRES_PASSWORD = os.environ.get("POSTGRES_PASSWORD", "")
POSTGRES_DB = os.environ.get("POSTGRES_DB", "")
POSTGRES_SSL = os.environ.get("POSTGRES_SSL", "false").strip().lower() in ("1", "true", "yes")
db_pool: Optional[asyncpg.Pool] = None
IN_MEMORY_PROJECTS = {}
IN_MEMORY_VERSIONS = {}
IN_MEMORY_BUILDS = {}
IN_MEMORY_ASSETS = []
async def init_db_pool() -> asyncpg.Pool:
database_url = os.environ.get("DATABASE_URL", "").strip()
if database_url:
pool = await asyncpg.create_pool(dsn=database_url, min_size=1, max_size=5,
ssl="require" if os.environ.get("POSTGRES_SSL","").lower() in ("1","true","yes") else None)
return pool
pool = await asyncpg.create_pool(
host=POSTGRES_SERVER,
port=int(POSTGRES_PORT),
user=POSTGRES_USER,
password=POSTGRES_PASSWORD,
database=POSTGRES_DB,
min_size=1,
max_size=5,
timeout=10,
ssl=True if POSTGRES_SSL else None,
)
async with pool.acquire() as conn:
await conn.execute("""CREATE TABLE IF NOT EXISTS dolor3v_projects (page_id TEXT PRIMARY KEY, data JSONB NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now())""")
await conn.execute("""CREATE TABLE IF NOT EXISTS dolor3v_assets (id SERIAL PRIMARY KEY, name TEXT NOT NULL, url TEXT NOT NULL, type TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now())""")
await conn.execute("""CREATE TABLE IF NOT EXISTS dolor3v_versions (id SERIAL PRIMARY KEY, project_id TEXT NOT NULL, name TEXT NOT NULL, data JSONB NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now())""")
await conn.execute("""CREATE TABLE IF NOT EXISTS dolor3v_builds (id TEXT PRIMARY KEY, project_id TEXT NOT NULL, platform TEXT NOT NULL, status TEXT NOT NULL, logs TEXT NOT NULL, download_url TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now())""")
return pool
@asynccontextmanager
async def lifespan(app: FastAPI):
global db_pool
try:
db_pool = await init_db_pool()
logger.info("Database pool initialized — postgres_connected=True")
except Exception as exc:
logger.error(
"Failed to initialize Postgres pool. Falling back to memory: %s",
exc,
)
db_pool = None
yield
if db_pool is not None:
await db_pool.close()
app = FastAPI(title="Dolor3v AI Workspace Core Engine", lifespan=lifespan)
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])
# ---------------------------------------------------------------------------
# Config: Google Drive
# ---------------------------------------------------------------------------
GDRIVE_SERVICE_ACCOUNT_JSON = os.environ.get("GDRIVE_SERVICE_ACCOUNT_JSON", "")
GDRIVE_FOLDER_ID = os.environ.get("GDRIVE_FOLDER_ID", "")
_drive_service_cache: Optional[Any] = None
def _get_drive_service_sync() -> Any:
global _drive_service_cache
if _drive_service_cache is not None:
return _drive_service_cache
if not GDRIVE_SERVICE_ACCOUNT_JSON:
raise RuntimeError("GDRIVE_SERVICE_ACCOUNT_JSON is not configured")
from google.oauth2 import service_account
from googleapiclient.discovery import build
info = json.loads(GDRIVE_SERVICE_ACCOUNT_JSON)
credentials = service_account.Credentials.from_service_account_info(info, scopes=["https://www.googleapis.com/auth/drive"])
_drive_service_cache = build("drive", "v3", credentials=credentials, cache_discovery=False)
return _drive_service_cache
def _upload_one_sync(service: Any, filename: str, mimetype: str, content: bytes) -> dict:
from googleapiclient.http import MediaIoBaseUpload
media = MediaIoBaseUpload(io.BytesIO(content), mimetype=mimetype or "application/octet-stream", resumable=False)
file_metadata = {"name": filename, "parents": [GDRIVE_FOLDER_ID]}
created = service.files().create(body=file_metadata, media_body=media, fields="id, name, webContentLink, webViewLink").execute()
service.permissions().create(fileId=created["id"], body={"role": "reader", "type": "anyone"}).execute()
src = created.get("webContentLink") or f"https://drive.google.com/uc?export=view&id={created['id']}"
return {"src": src, "name": created.get("name", filename), "type": "image"}
# ---------------------------------------------------------------------------
# Models
# ---------------------------------------------------------------------------
class ChatMessage(BaseModel): role: str; content: str
class ChatRequest(BaseModel): model: str = "auto"; messages: list[ChatMessage]; temperature: Optional[float] = None; max_tokens: Optional[int] = None
class ProjectSaveRequest(BaseModel): pageId: str; data: dict[str, Any]
class PublishRequest(BaseModel): project_id: str; platform: str = "web"
class NativeGenerateRequest(BaseModel): prompt: str; platform: str = "flutter"; project_id: str
class NativeBuildRequest(BaseModel): project_id: str; platform: str; config: Optional[dict[str, Any]] = None
class CodeGenerateRequest(BaseModel): prompt: str; language: str; context: Optional[str] = None
class CodeRefactorRequest(BaseModel): code: str; instructions: str; language: str
class CodeFixRequest(BaseModel): code: str; error: str; language: str
class VersionSaveRequest(BaseModel): project_id: str; name: str; data: dict[str, Any]
class VersionRestoreRequest(BaseModel): project_id: str; version_id: int
# ---------------------------------------------------------------------------
# AI Engine Fallback & Completions
# ---------------------------------------------------------------------------
async def call_groq(model: str, messages: list[dict], **kwargs) -> dict[str, Any]:
if not GROQ_API_KEY: raise RuntimeError("GROQ_API_KEY not configured")
async with httpx.AsyncClient(timeout=60) as client:
resp = await client.post(GROQ_URL, headers={"Authorization": f"Bearer {GROQ_API_KEY}"}, json={"model": model, "messages": messages, **kwargs})
if resp.status_code!= 200: raise RuntimeError(f"Groq {resp.status_code}: {resp.text[:500]}")
return resp.json()
async def call_cerebras(model: str, messages: list[dict], **kwargs) -> dict[str, Any]:
if not CEREBRAS_API_KEY: raise RuntimeError("CEREBRAS_API_KEY not configured")
async with httpx.AsyncClient(timeout=60) as client:
resp = await client.post(CEREBRAS_URL, headers={"Authorization": f"Bearer {CEREBRAS_API_KEY}"}, json={"model": model, "messages": messages, **kwargs})
if resp.status_code!= 200: raise RuntimeError(f"Cerebras {resp.status_code}: {resp.text[:500]}")
return resp.json()
async def call_openrouter(model: str, messages: list[dict], **kwargs) -> dict[str, Any]:
if not OPENROUTER_API_KEY: raise RuntimeError("OPENROUTER_API_KEY not configured")
async with httpx.AsyncClient(timeout=60) as client:
resp = await client.post(OPENROUTER_URL, headers={"Authorization": f"Bearer {OPENROUTER_API_KEY}"}, json={"model": model, "messages": messages, **kwargs})
if resp.status_code!= 200: raise RuntimeError(f"OpenRouter {resp.status_code}: {resp.text[:500]}")
return resp.json()
PROVIDER_FUNCS = {"groq": call_groq, "cerebras": call_cerebras, "openrouter": call_openrouter}
async def get_ai_completion(system_prompt: str, user_prompt: str, temperature: float = 0.3) -> str:
messages = [{"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}]
errors = []
for provider, real_model in AUTO_CHAIN:
try:
result = await PROVIDER_FUNCS[provider](real_model, messages, temperature=temperature)
return result.get("choices", [])[0].get("message", {}).get("content", "")
except Exception as exc:
logger.warning("Provider %s failed: %s", provider, exc); errors.append(str(exc))
raise HTTPException(status_code=502, detail={"error": "All AI providers failed", "details": errors})
BUILD_JOBS = {}
from mcp_routes import router as mcp_router
app.include_router(mcp_router)
from builder_routes import router as builder_router
app.include_router(builder_router)
|