GitHub Actions
Deploy Emily Pantheon backend from GitHub Actions
acc03ea
Raw
History Blame Contribute Delete
5.98 kB
import os
from typing import Optional
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from groq import Groq
from huggingface_hub import InferenceClient
from pydantic import BaseModel, Field
from config import (
DEFAULT_GROQ_MODEL,
DEFAULT_HF_MODEL,
FALLBACK_GROQ_MODEL,
TAROT_DECK,
get_active_provider,
get_groq_api_key,
get_groq_client,
get_hf_token,
list_available_models,
)
from personas import get_dynamic_persona, normalize_lang
app = FastAPI(title="AI Pantheon API", version="0.2.5")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
class TarotRequest(BaseModel):
cards: list[str]
topic: str
query: str
lang: str = Field(default="한국어")
class FengShuiRequest(BaseModel):
year: int
gender: str
door_dir: str
head_dir: str
query: str
lang: str = Field(default="한국어")
address: Optional[str] = None
family_info: Optional[str] = None
class SajuRequest(BaseModel):
year: int
month: int
day: int
hour: int
minute: int
calendar_type: str
query: str
lang: str = Field(default="한국어")
def _call_groq(system_prompt: str, user_prompt: str, temperature: float = 0.85) -> str:
client = get_groq_client()
models = [DEFAULT_GROQ_MODEL, FALLBACK_GROQ_MODEL]
for model in list_available_models():
if model not in models:
models.append(model)
last_error: Exception | None = None
for model in models:
try:
response = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
],
temperature=temperature,
max_tokens=2048,
)
content = response.choices[0].message.content
if content:
return content.strip()
except Exception as exc:
last_error = exc
continue
raise RuntimeError(f"All Groq models failed: {last_error}")
def _call_hf_inference(system_prompt: str, user_prompt: str, temperature: float = 0.85) -> str:
token = get_hf_token()
if not token:
raise RuntimeError("HF_TOKEN is not set")
client = InferenceClient(model=DEFAULT_HF_MODEL, token=token, timeout=90)
response = client.chat_completion(
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
],
max_tokens=1200,
temperature=temperature,
)
content = response.choices[0].message.content
if not content:
raise RuntimeError("Empty response from Hugging Face Inference")
return content.strip()
def _call_llm(system_prompt: str, user_prompt: str, temperature: float = 0.85) -> str:
provider = get_active_provider()
if provider == "groq":
return _call_groq(system_prompt, user_prompt, temperature)
if provider == "huggingface":
return _call_hf_inference(system_prompt, user_prompt, temperature)
raise HTTPException(
status_code=503,
detail="No AI provider configured. Set GROQ_API_KEY or HF_TOKEN in Space secrets.",
)
@app.get("/")
def read_root():
return {"message": "Server is Running!"}
@app.get("/health")
def health():
provider = get_active_provider()
return {
"status": "ok" if provider != "none" else "degraded",
"provider": provider,
"version": app.version,
}
@app.get("/models")
def get_models():
provider = get_active_provider()
models = list_available_models()
if provider == "groq":
return {"provider": "groq", "models": models, "default": DEFAULT_GROQ_MODEL, "fallback": FALLBACK_GROQ_MODEL}
return {"provider": provider, "models": models, "default": DEFAULT_HF_MODEL}
@app.get("/tarot/deck")
def get_tarot_deck():
return TAROT_DECK
@app.post("/tarot/read")
def read_tarot(request: TarotRequest):
try:
lang = normalize_lang(request.lang)
system = get_dynamic_persona(lang, "tarot")
cards_text = ", ".join(request.cards)
user = (
f"Topic: {request.topic}\n"
f"Selected cards: {cards_text}\n"
f"Question: {request.query}\n\n"
f"Give a tarot reading as Emily. Interpret each card for this topic and weave them together."
)
result = _call_llm(system, user)
return {"result": result}
except HTTPException:
raise
except Exception as exc:
raise HTTPException(status_code=502, detail=str(exc)) from exc
@app.post("/fengshui/analyze")
def analyze_fengshui(request: FengShuiRequest):
lang = normalize_lang(request.lang)
system = get_dynamic_persona(lang, "fengshui")
user = (
f"Birth year: {request.year}\n"
f"Gender: {request.gender}\n"
f"Front door direction: {request.door_dir}\n"
f"Sleeping head direction: {request.head_dir}\n"
f"Question: {request.query}"
)
result = _call_llm(system, user)
return {"result": result}
@app.post("/shaman/read")
def read_saju(request: SajuRequest):
lang = normalize_lang(request.lang)
system = get_dynamic_persona(lang, "shaman")
user = (
f"Birth: {request.year}-{request.month:02d}-{request.day:02d} "
f"{request.hour:02d}:{request.minute:02d} ({request.calendar_type})\n"
f"Question: {request.query}\n\n"
f"Deliver a spirit oracle (공수) as Emily the young shaman. "
f"Reference birth elements naturally but stay in Emily's voice."
)
result = _call_llm(system, user, temperature=0.9)
return {"result": result}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=int(os.getenv("PORT", "7860")))