Spaces:
Sleeping
Sleeping
File size: 6,562 Bytes
a547d6c 116524e 1cf3e52 116524e d94bec9 a547d6c 1cf3e52 116524e 1cf3e52 116524e 1cf3e52 116524e c2c9236 1cf3e52 c2c9236 116524e 1cf3e52 fb91a77 116524e c2c9236 a547d6c 1cf3e52 c2c9236 a547d6c c2c9236 a547d6c 6118c36 c2c9236 a547d6c 6118c36 a547d6c c2c9236 a547d6c c2c9236 a547d6c d94bec9 6118c36 d94bec9 6118c36 d94bec9 6118c36 d94bec9 6118c36 1cf3e52 a547d6c 6118c36 c2c9236 aa9a63b 1cf3e52 aa9a63b 1cf3e52 aa9a63b 1cf3e52 aa9a63b 116524e 1cf3e52 | 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 | 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"))
|