mirror of
https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools.git
synced 2026-06-18 15:29:36 +00:00
Copy full prompt library into SaaS runtime path, add prospecting run_id for isolation tests, and fix async SelfImprovementFlow calls (API, startup worker, LangGraph node). Made-with: Cursor
160 lines
5.2 KiB
Python
160 lines
5.2 KiB
Python
# ── SQLite Patch (must be first!) ─────────────────────────────
|
|
from app.sqlite_patch import apply_patch
|
|
apply_patch()
|
|
# ──────────────────────────────────────────────────────────────
|
|
|
|
from pathlib import Path
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.responses import RedirectResponse
|
|
from fastapi.staticfiles import StaticFiles
|
|
from contextlib import asynccontextmanager
|
|
import asyncio
|
|
|
|
from app.config import get_settings
|
|
from app.api.v1.router import api_router
|
|
from app.flows.self_improvement_flow import self_improvement_flow
|
|
from app.middleware.internal_api import InternalApiTokenMiddleware
|
|
|
|
settings = get_settings()
|
|
|
|
|
|
def _cors_origins() -> list[str]:
|
|
base = [
|
|
settings.FRONTEND_URL,
|
|
"http://localhost:3000",
|
|
"http://localhost:5173",
|
|
"https://dealix.sa",
|
|
"https://app.dealix.sa",
|
|
]
|
|
extra = [x.strip() for x in (settings.CORS_EXTRA_ORIGINS or "").split(",") if x.strip()]
|
|
seen: set[str] = set()
|
|
out: list[str] = []
|
|
for o in base + extra:
|
|
if o not in seen:
|
|
seen.add(o)
|
|
out.append(o)
|
|
return out
|
|
|
|
|
|
def _openapi_urls() -> tuple[str | None, str | None, str | None]:
|
|
if not settings.EXPOSE_OPENAPI:
|
|
return None, None, None
|
|
return "/api/docs", "/api/redoc", "/api/openapi.json"
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
"""Application startup and shutdown events."""
|
|
stop_event = asyncio.Event()
|
|
|
|
async def _self_improvement_worker():
|
|
while not stop_event.is_set():
|
|
await self_improvement_flow.run("system_tenant", None)
|
|
await asyncio.sleep(max(60, settings.SELF_IMPROVEMENT_INTERVAL_SECONDS))
|
|
|
|
worker_task = asyncio.create_task(_self_improvement_worker())
|
|
|
|
# Startup
|
|
print(f"[startup] {settings.APP_NAME} starting...")
|
|
print(f" Environment: {settings.ENVIRONMENT}")
|
|
print(f" LLM Primary: {settings.LLM_PRIMARY_PROVIDER}")
|
|
print(f" LLM Fallback: {settings.LLM_FALLBACK_PROVIDER}")
|
|
yield
|
|
# Shutdown
|
|
stop_event.set()
|
|
worker_task.cancel()
|
|
print(f"[shutdown] {settings.APP_NAME} shutting down...")
|
|
|
|
|
|
_docs, _redoc, _openapi = _openapi_urls()
|
|
|
|
_docs_static_dir = Path(__file__).resolve().parent / "static" / "docs"
|
|
_swagger_ui_parameters = None
|
|
if _docs and (_docs_static_dir / "swagger-dealix.css").is_file():
|
|
_swagger_ui_parameters = {
|
|
"persistAuthorization": True,
|
|
"displayRequestDuration": True,
|
|
"filter": True,
|
|
"tryItOutEnabled": True,
|
|
"customCssUrl": "/api/docs-assets/swagger-dealix.css",
|
|
}
|
|
|
|
app = FastAPI(
|
|
title=f"{settings.APP_NAME} API",
|
|
description=(
|
|
"AI-powered B2B Revenue Operating System for the Saudi market. "
|
|
"Lead management, AI agents, affiliate system, meeting automation, "
|
|
"deal pipeline, and commission processing — all driven by 18 specialized AI agents."
|
|
),
|
|
version="2.0.0",
|
|
docs_url=_docs,
|
|
redoc_url=_redoc,
|
|
openapi_url=_openapi,
|
|
lifespan=lifespan,
|
|
swagger_ui_parameters=_swagger_ui_parameters,
|
|
)
|
|
|
|
app.add_middleware(InternalApiTokenMiddleware)
|
|
# CORS runs outermost (added last) so browser preflight is handled first
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=_cors_origins(),
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
# API Routes
|
|
app.include_router(api_router, prefix="/api/v1")
|
|
|
|
if _docs and _docs_static_dir.is_dir():
|
|
app.mount(
|
|
"/api/docs-assets",
|
|
StaticFiles(directory=str(_docs_static_dir)),
|
|
name="docs_assets",
|
|
)
|
|
|
|
|
|
@app.get("/", include_in_schema=False)
|
|
async def root_redirect():
|
|
"""Avoid bare 404 on API origin; send developers to interactive docs."""
|
|
if _docs:
|
|
return RedirectResponse(url=_docs, status_code=307)
|
|
return {
|
|
"service": settings.APP_NAME,
|
|
"api": "/api/v1",
|
|
"health": "/api/v1/health",
|
|
"note": "OpenAPI UI disabled (EXPOSE_OPENAPI=false).",
|
|
}
|
|
|
|
|
|
# ── Static marketing assets (browse + direct download) ─────────
|
|
def _resolve_salesflow_root() -> Path:
|
|
if settings.MARKETING_STATIC_ROOT.strip():
|
|
return Path(settings.MARKETING_STATIC_ROOT).resolve()
|
|
# backend/app/main.py -> parents: app, backend, salesflow-saas
|
|
return Path(__file__).resolve().parent.parent.parent
|
|
|
|
|
|
_salesflow_root = _resolve_salesflow_root()
|
|
_marketing_dir = _salesflow_root / "sales_assets"
|
|
_presentations_dir = _salesflow_root / "presentations" / "dealix-2026-sectors"
|
|
|
|
if settings.MARKETING_STATIC_ENABLED:
|
|
if _marketing_dir.is_dir():
|
|
app.mount(
|
|
"/dealix-marketing",
|
|
StaticFiles(directory=str(_marketing_dir), html=True),
|
|
name="dealix_marketing",
|
|
)
|
|
print(" Marketing static: /dealix-marketing/ (index, ZIP, use cases)")
|
|
if _presentations_dir.is_dir():
|
|
app.mount(
|
|
"/dealix-presentations",
|
|
StaticFiles(directory=str(_presentations_dir), html=True),
|
|
name="dealix_presentations",
|
|
)
|
|
print(" Marketing static: /dealix-presentations/ (sector HTML)")
|