Update server.py
Browse files
server.py
CHANGED
|
@@ -1,107 +1,123 @@
|
|
| 1 |
import os
|
| 2 |
import random
|
| 3 |
import logging
|
| 4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
import google.generativeai as genai
|
| 6 |
-
from flask_cors import CORS
|
| 7 |
from dotenv import load_dotenv
|
|
|
|
| 8 |
|
| 9 |
-
#
|
| 10 |
load_dotenv()
|
| 11 |
|
| 12 |
-
|
| 13 |
-
|
| 14 |
|
| 15 |
-
# Cấu hình
|
| 16 |
-
|
| 17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
-
# --- CẤU HÌNH
|
| 20 |
|
| 21 |
-
# 1. Lấy danh sách API KEYS
|
| 22 |
-
#
|
| 23 |
-
# Giá trị là các key cách nhau bằng dấu phẩy: key1,key2,key3
|
| 24 |
api_keys_env = os.getenv("GEMINI_API_KEYS", "")
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
if not API_KEYS:
|
| 28 |
-
logger.warning("CHÚ Ý: Chưa tìm thấy GEMINI_API_KEYS trong biến môi trường! Server sẽ không hoạt động đúng.")
|
| 29 |
|
| 30 |
# 2. Lấy danh sách MODELS
|
| 31 |
-
# Tạo biến GEMINI_MODELS, giá trị ví dụ: gemini-2.5-flash,gemini-1.5-pro
|
| 32 |
models_env = os.getenv("GEMINI_MODELS", "gemini-2.0-flash-exp,gemini-1.5-pro,gemini-1.5-flash")
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
@app.route('/')
|
| 36 |
-
def home():
|
| 37 |
-
return jsonify({
|
| 38 |
-
"status": "online",
|
| 39 |
-
"message": "HT MATH Server is running",
|
| 40 |
-
"models_available": GEMINI_MODELS,
|
| 41 |
-
"keys_count": len(API_KEYS)
|
| 42 |
-
})
|
| 43 |
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
|
|
|
| 48 |
|
| 49 |
-
@app.
|
| 50 |
-
def
|
| 51 |
-
|
| 52 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
|
| 54 |
try:
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
return jsonify({'error': 'No data provided'}), 400
|
| 58 |
-
|
| 59 |
-
prompt = data.get('prompt', '')
|
| 60 |
-
image_data = data.get('image') # Base64 string
|
| 61 |
-
model_name = data.get('model', GEMINI_MODELS[0]) # Mặc định lấy model đầu tiên nếu client không gửi
|
| 62 |
-
|
| 63 |
-
if not prompt and not image_data:
|
| 64 |
-
return jsonify({'error': 'Cần cung cấp nội dung hoặc hình ảnh'}), 400
|
| 65 |
-
|
| 66 |
-
# --- RANDOM KEY SELECTION ---
|
| 67 |
-
# Chọn ngẫu nhiên 1 key để tránh rate limit
|
| 68 |
-
current_key = random.choice(API_KEYS)
|
| 69 |
genai.configure(api_key=current_key)
|
| 70 |
|
| 71 |
-
#
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
# Khởi tạo model
|
| 76 |
model = genai.GenerativeModel(model_name)
|
| 77 |
|
| 78 |
-
#
|
| 79 |
-
content_parts = [prompt]
|
| 80 |
-
|
| 81 |
-
|
|
|
|
| 82 |
try:
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
from PIL import Image
|
| 86 |
-
|
| 87 |
-
image_bytes = base64.b64decode(image_data)
|
| 88 |
image = Image.open(BytesIO(image_bytes))
|
| 89 |
content_parts.append(image)
|
| 90 |
except Exception as e:
|
| 91 |
-
|
| 92 |
-
return jsonify({'error': f'Lỗi xử lý ảnh: {str(e)}'}), 400
|
| 93 |
|
| 94 |
-
# Gọi Google Gemini
|
| 95 |
response = model.generate_content(content_parts)
|
| 96 |
|
| 97 |
if response.text:
|
| 98 |
-
return
|
| 99 |
else:
|
| 100 |
-
|
| 101 |
|
| 102 |
except Exception as e:
|
| 103 |
-
|
| 104 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
|
| 106 |
-
if __name__ ==
|
| 107 |
-
|
|
|
|
|
|
| 1 |
import os
|
| 2 |
import random
|
| 3 |
import logging
|
| 4 |
+
import base64
|
| 5 |
+
from io import BytesIO
|
| 6 |
+
from typing import Optional, List
|
| 7 |
+
|
| 8 |
+
from fastapi import FastAPI, HTTPException, UploadFile, File, Form, Body
|
| 9 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 10 |
+
from pydantic import BaseModel
|
| 11 |
import google.generativeai as genai
|
|
|
|
| 12 |
from dotenv import load_dotenv
|
| 13 |
+
from PIL import Image
|
| 14 |
|
| 15 |
+
# --- LOAD BIẾN MÔI TRƯỜNG ---
|
| 16 |
load_dotenv()
|
| 17 |
|
| 18 |
+
# --- CẤU HÌNH SERVER ---
|
| 19 |
+
app = FastAPI(title="HT MATH UNIFIED SERVER")
|
| 20 |
|
| 21 |
+
# Cấu hình CORS (Cho phép cả Web và Desktop App gọi vào)
|
| 22 |
+
app.add_middleware(
|
| 23 |
+
CORSMiddleware,
|
| 24 |
+
allow_origins=["*"], # Cho phép tất cả các nguồn (Web, App Desktop)
|
| 25 |
+
allow_credentials=True,
|
| 26 |
+
allow_methods=["*"],
|
| 27 |
+
allow_headers=["*"],
|
| 28 |
+
)
|
| 29 |
|
| 30 |
+
# --- PHẦN 1: CẤU HÌNH CHO DESKTOP APP (Key Rotation & Models) ---
|
| 31 |
|
| 32 |
+
# 1. Lấy danh sách API KEYS từ biến môi trường
|
| 33 |
+
# Format trên HuggingFace: Key1,Key2,Key3
|
|
|
|
| 34 |
api_keys_env = os.getenv("GEMINI_API_KEYS", "")
|
| 35 |
+
DESKTOP_API_KEYS = [k.strip() for k in api_keys_env.split(",") if k.strip()]
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
# 2. Lấy danh sách MODELS
|
|
|
|
| 38 |
models_env = os.getenv("GEMINI_MODELS", "gemini-2.0-flash-exp,gemini-1.5-pro,gemini-1.5-flash")
|
| 39 |
+
DESKTOP_MODELS = [m.strip() for m in models_env.split(",") if m.strip()]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
|
| 41 |
+
# Model dữ liệu nhận từ Desktop App
|
| 42 |
+
class DesktopGenerateRequest(BaseModel):
|
| 43 |
+
prompt: str
|
| 44 |
+
model: Optional[str] = "gemini-1.5-flash"
|
| 45 |
+
image: Optional[str] = None # Base64 string
|
| 46 |
|
| 47 |
+
@app.get("/")
|
| 48 |
+
async def root():
|
| 49 |
+
return {
|
| 50 |
+
"status": "online",
|
| 51 |
+
"server": "HT MATH UNIFIED SERVER",
|
| 52 |
+
"desktop_keys_loaded": len(DESKTOP_API_KEYS),
|
| 53 |
+
"web_support": "Active"
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
# --- API 1: Lấy danh sách Model (Cho Desktop App) ---
|
| 57 |
+
@app.get("/api/models")
|
| 58 |
+
async def get_models_desktop():
|
| 59 |
+
return {"models": DESKTOP_MODELS}
|
| 60 |
+
|
| 61 |
+
# --- API 2: Xử lý AI (Cho Desktop App - Có xoay vòng Key) ---
|
| 62 |
+
@app.post("/api/generate")
|
| 63 |
+
async def generate_content_desktop(req: DesktopGenerateRequest):
|
| 64 |
+
if not DESKTOP_API_KEYS:
|
| 65 |
+
raise HTTPException(status_code=500, detail="Server chưa cấu hình GEMINI_API_KEYS")
|
| 66 |
|
| 67 |
try:
|
| 68 |
+
# 1. Chọn ngẫu nhiên 1 Key (Load Balancing)
|
| 69 |
+
current_key = random.choice(DESKTOP_API_KEYS)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
genai.configure(api_key=current_key)
|
| 71 |
|
| 72 |
+
# 2. Chọn Model
|
| 73 |
+
# Nếu model client gửi lên không có trong danh sách hỗ trợ, dùng model mặc định
|
| 74 |
+
model_name = req.model if req.model in DESKTOP_MODELS else DESKTOP_MODELS[0]
|
|
|
|
|
|
|
| 75 |
model = genai.GenerativeModel(model_name)
|
| 76 |
|
| 77 |
+
# 3. Chuẩn bị nội dung gửi đi
|
| 78 |
+
content_parts = [req.prompt]
|
| 79 |
+
|
| 80 |
+
# 4. Xử lý ảnh (nếu có)
|
| 81 |
+
if req.image:
|
| 82 |
try:
|
| 83 |
+
# Desktop App gửi ảnh dạng Base64 string
|
| 84 |
+
image_bytes = base64.b64decode(req.image)
|
|
|
|
|
|
|
|
|
|
| 85 |
image = Image.open(BytesIO(image_bytes))
|
| 86 |
content_parts.append(image)
|
| 87 |
except Exception as e:
|
| 88 |
+
raise HTTPException(status_code=400, detail=f"Lỗi xử lý ảnh base64: {str(e)}")
|
|
|
|
| 89 |
|
| 90 |
+
# 5. Gọi Google Gemini
|
| 91 |
response = model.generate_content(content_parts)
|
| 92 |
|
| 93 |
if response.text:
|
| 94 |
+
return {"result": response.text}
|
| 95 |
else:
|
| 96 |
+
raise HTTPException(status_code=500, detail="Gemini không trả về nội dung text.")
|
| 97 |
|
| 98 |
except Exception as e:
|
| 99 |
+
print(f"Lỗi Desktop API: {e}")
|
| 100 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
# ==============================================================================
|
| 104 |
+
# --- PHẦN 2: KHU VỰC CỦA WEB APP (COPY CODE CŨ VÀO DƯỚI ĐÂY) ---
|
| 105 |
+
# ==============================================================================
|
| 106 |
+
|
| 107 |
+
# Bạn hãy dán các hàm xử lý của Web App vào đây.
|
| 108 |
+
# Ví dụ: import supabase, các hàm convert_file, login, signup...
|
| 109 |
+
# Đảm bảo giữ nguyên logic cũ của Web App.
|
| 110 |
+
|
| 111 |
+
# Ví dụ mẫu (Nếu bạn dùng Supabase):
|
| 112 |
+
# from supabase import create_client, Client
|
| 113 |
+
# url: str = os.environ.get("SUPABASE_URL")
|
| 114 |
+
# key: str = os.environ.get("SUPABASE_KEY")
|
| 115 |
+
# supabase: Client = create_client(url, key)
|
| 116 |
+
|
| 117 |
+
# @app.post("/convert")
|
| 118 |
+
# async def convert_file(...):
|
| 119 |
+
# ... code cũ của bạn ...
|
| 120 |
|
| 121 |
+
if __name__ == "__main__":
|
| 122 |
+
import uvicorn
|
| 123 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|