import os
import json
import uuid
import time
import re
from pathlib import Path
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from gradio import Server
from openai import OpenAI
# Default system prompt for DeepSeek V4 Flash
DEFAULT_SYSTEM_PROMPT = "You are DeepSeek, a helpful AI assistant powered by DeepSeek V4 Flash. You provide accurate, detailed, and thoughtful responses."
# ── In-memory per-user session storage ──────────────────────────────────
USER_DATA = {}
def get_user_sessions(username):
if username not in USER_DATA:
USER_DATA[username] = {"sessions": {}, "active_session": None}
return USER_DATA[username]
# ── Initialize Gradio Server (FastAPI subclass) ─────────────────────────
app = Server()
# Create static directory if it doesn't exist
STATIC_DIR = Path(__file__).parent / "static"
os.makedirs(STATIC_DIR, exist_ok=True)
# ── API Endpoint: Chat ──────────────────────────────────────────────────
@app.api(name="chat_with_deepseek")
def chat_with_deepseek(
messages_json: str,
reasoning_effort: str = "medium",
max_tokens: str = "2048",
temperature: str = "0.7",
system_prompt: str = "",
) -> str:
try:
messages = json.loads(messages_json)
max_tokens = int(max_tokens)
temperature = float(temperature)
key = os.environ.get("SILICONFLOW_API_KEY", "").strip()
if not key:
return json.dumps({
"status": "error",
"message": "SILICONFLOW_API_KEY environment variable is not configured on the server."
})
client = OpenAI(
api_key=key,
base_url="https://api.siliconflow.com/v1",
)
sys_prompt = system_prompt.strip() if system_prompt and system_prompt.strip() else DEFAULT_SYSTEM_PROMPT
messages_no_system = [m for m in messages if m.get("role") != "system"]
final_messages = [{"role": "system", "content": sys_prompt}] + messages_no_system
params = {
"model": "deepseek-ai/DeepSeek-V4-Flash",
"messages": final_messages,
"max_tokens": max_tokens,
"temperature": temperature
}
if reasoning_effort in ["low", "medium", "high"]:
params["reasoning_effort"] = reasoning_effort
response = client.chat.completions.create(**params)
content = response.choices[0].message.content
reasoning_content = getattr(response.choices[0].message, "reasoning_content", "")
if not reasoning_content and content and "]*>(.*?)]*>.*?', '', content, flags=re.DOTALL).strip()
return json.dumps({
"status": "success",
"content": content,
"reasoning_content": reasoning_content or ""
})
except Exception as e:
return json.dumps({
"status": "error",
"message": str(e)
})
# ── API Endpoint: Get User Info (OAuth) ────────────────────────────────
@app.api(name="get_user_info")
def get_user_info(profile) -> str:
"""Return the logged-in user's name, or empty string if not logged in."""
# The Gradio Server passes the OAuth profile automatically
if profile is None:
return json.dumps({"logged_in": False, "username": ""})
return json.dumps({"logged_in": True, "username": getattr(profile, 'name', '')})
# ── API Endpoint: Save Chat Session ────────────────────────────────────
@app.api(name="save_chat_session")
def save_chat_session(
messages_json: str,
title: str,
session_id: str,
profile = None,
) -> str:
if profile is None:
return json.dumps({"status": "error", "message": "Not logged in"})
username = getattr(profile, 'name', '')
ud = get_user_sessions(username)
if not session_id or session_id == "new":
sid = str(uuid.uuid4())[:8]
else:
sid = session_id
messages = json.loads(messages_json) if messages_json else []
if not title or title == "New Chat":
for m in messages:
if m.get("role") == "user":
t = m.get("content", "")
if isinstance(t, str):
title = t[:60] + ("..." if len(t) > 60 else "")
break
ud["sessions"][sid] = {
"title": title,
"messages": messages,
"updated_at": time.time()
}
ud["active_session"] = sid
return json.dumps({"status": "success", "session_id": sid, "title": title})
# ── API Endpoint: Load Chat Session ────────────────────────────────────
@app.api(name="load_chat_session")
def load_chat_session(
session_id: str,
profile = None,
) -> str:
if profile is None:
return json.dumps({"status": "error", "message": "Not logged in"})
username = getattr(profile, 'name', '')
ud = get_user_sessions(username)
if session_id not in ud["sessions"]:
return json.dumps({"status": "error", "message": "Session not found"})
ud["active_session"] = session_id
sess = ud["sessions"][session_id]
return json.dumps({
"status": "success",
"session_id": session_id,
"title": sess["title"],
"messages": sess["messages"]
})
# ── API Endpoint: List Chat Sessions ───────────────────────────────────
@app.api(name="list_chat_sessions")
def list_chat_sessions(profile = None) -> str:
if profile is None:
return json.dumps({"status": "error", "message": "Not logged in", "sessions": []})
username = getattr(profile, 'name', '')
ud = get_user_sessions(username)
sessions = []
for sid, sess in sorted(
ud["sessions"].items(),
key=lambda x: x[1].get("updated_at", 0),
reverse=True
):
sessions.append({
"id": sid,
"title": sess["title"],
"updated_at": sess.get("updated_at", 0),
"message_count": len(sess.get("messages", []))
})
return json.dumps({"status": "success", "sessions": sessions})
# ── API Endpoint: Delete Chat Session ──────────────────────────────────
@app.api(name="delete_chat_session")
def delete_chat_session(
session_id: str,
profile = None,
) -> str:
if profile is None:
return json.dumps({"status": "error", "message": "Not logged in"})
username = getattr(profile, 'name', '')
ud = get_user_sessions(username)
if session_id in ud["sessions"]:
del ud["sessions"][session_id]
if ud["active_session"] == session_id:
ud["active_session"] = None
return json.dumps({"status": "success"})
# ── Serve the main HTML page ────────────────────────────────────────────
@app.get("/")
async def homepage():
html_path = STATIC_DIR / "index.html"
if html_path.exists():
with open(html_path, "r", encoding="utf-8") as f:
return HTMLResponse(content=f.read(), status_code=200)
return HTMLResponse(
content="
Frontend is building. Please refresh in a few seconds...
",
status_code=200
)
# ── Mount static folder for CSS, JS, and image assets ──────────────────
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
# ── Launch ──────────────────────────────────────────────────────────────
if __name__ == "__main__":
app.launch(show_error=True)