logic-engine / main.py
ghostdrive1's picture
Upload folder using huggingface_hub
6118c36 verified
Raw
History Blame Contribute Delete
6.56 kB
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
from pydantic import BaseModel
import os
import json
import httpx
import redis
app = FastAPI(title="Manus Clone")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ── Redis connection (2GB, localhost) ──────────────────────────────────────────
try:
r = redis.Redis(host="localhost", port=6379, db=0, decode_responses=True)
r.ping()
REDIS_OK = True
except Exception:
REDIS_OK = False
class ChatRequest(BaseModel):
prompt: str
model: str = "llama-3.1-8b-instant"
api_key: str = ""
provider: str = "Groq"
PROVIDER_BASES = {
"OpenAI": "https://api.openai.com/v1",
"Anthropic": "https://api.anthropic.com/v1",
"Groq": "https://api.groq.com/openai/v1",
"HuggingFace": "https://api-inference.huggingface.co/v1",
"OpenRouter": "https://openrouter.ai/api/v1",
"Nvidia": "https://integrate.api.nvidia.com/v1",
"Mistral": "https://api.mistral.ai/v1",
"Cohere": "https://api.cohere.ai/v1",
}
# ── API endpoints ─────────────────────────────────────────────────────────────
@app.get("/health")
def health():
return {"status": "ok", "redis": REDIS_OK}
@app.post("/chat")
async def chat_endpoint(request: ChatRequest):
api_key = (
request.api_key.strip()
or os.environ.get("GROQ_API_KEY", "")
or os.environ.get("OPENAI_API_KEY", "")
)
if not api_key:
return {
"response": (
f'[Manus] Received: "{request.prompt}"\n\n'
"No API key configured. Please add your key via the Providers panel."
),
"doc": True
}
base_url = PROVIDER_BASES.get(request.provider, PROVIDER_BASES["Groq"])
# Build headers
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
# OpenRouter needs extra headers for some models
if request.provider == "OpenRouter":
headers["HTTP-Referer"] = "https://augment17-logic-engine.hf.space"
headers["X-Title"] = "Manus Clone"
try:
async with httpx.AsyncClient(timeout=60) as client:
resp = await client.post(
f"{base_url}/chat/completions",
headers=headers,
json={
"model": request.model,
"messages": [
{"role": "system", "content": "You are Manus, a helpful and concise autonomous AI agent."},
{"role": "user", "content": request.prompt},
],
"temperature": 0.7,
}
)
resp.raise_for_status()
data = resp.json()
# Robust response extraction
reply = None
try:
choices = data.get("choices", [])
if choices and len(choices) > 0:
choice = choices[0]
msg = choice.get("message") or choice.get("delta") or {}
reply = msg.get("content") or choice.get("text") or ""
except (KeyError, IndexError, TypeError):
pass
# Fallback keys
if not reply:
reply = (
data.get("text")
or data.get("output")
or data.get("response")
or data.get("content")
or data.get("result")
or ""
)
# Empty choices = model not found / not available
if not reply:
raise HTTPException(
status_code=404,
detail=(
f"Model '{request.model}' returned an empty response from {request.provider}. "
f"This usually means the model does not exist on {request.provider}'s API. "
f"Try a different model or switch to OpenRouter which supports more models."
)
)
# Cache to Redis if available
if REDIS_OK:
try:
cache_key = f"chat:{request.provider}:{request.model}:{request.prompt[:100]}"
r.setex(cache_key, 3600, reply)
except Exception:
pass
return {"response": reply, "doc": True}
except HTTPException:
raise
except httpx.HTTPStatusError as e:
status = e.response.status_code
try:
err_body = e.response.json()
err_msg = err_body.get("error", {}).get("message", e.response.text[:300])
except Exception:
err_msg = e.response.text[:300]
if status == 401:
detail = (
f"401 Unauthorized from {request.provider}. "
f"Check that your API key is valid for {request.provider} "
f"and that the model '{request.model}' is available on {request.provider}. "
f"Tip: Make sure the model you selected belongs to the same provider as your API key."
)
elif status == 400:
detail = f"400 Bad Request from {request.provider}: {err_msg}"
elif status == 404:
detail = f"404: Model '{request.model}' not found on {request.provider}. Select a model from the correct provider tab."
else:
detail = f"Provider error {status}: {err_msg}"
raise HTTPException(status_code=status, detail=detail)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
# ── Serve React UI (MUST be last, catches all non-API routes) ─────────────────
static_dir = os.path.join(os.path.dirname(__file__), "static")
if os.path.isdir(static_dir):
@app.get("/{full_path:path}")
async def serve_spa(full_path: str):
file_path = os.path.join(static_dir, full_path)
if full_path and os.path.isfile(file_path):
return FileResponse(file_path)
return FileResponse(os.path.join(static_dir, "index.html"))