|
|
import os, uuid, json, zipfile, logging |
|
|
from fastapi import FastAPI, UploadFile, File, Request |
|
|
from fastapi.responses import StreamingResponse, JSONResponse, FileResponse |
|
|
from fastapi.staticfiles import StaticFiles |
|
|
from huggingface_hub import InferenceClient |
|
|
from duckduckgo_search import DDGS |
|
|
import fitz |
|
|
from docx import Document |
|
|
|
|
|
app = FastAPI() |
|
|
UPLOAD_DIR = "static/uploads" |
|
|
os.makedirs(UPLOAD_DIR, exist_ok=True) |
|
|
app.mount("/static", StaticFiles(directory="static"), name="static") |
|
|
|
|
|
|
|
|
HF_TOKEN = os.environ.get("HF_TOKEN") |
|
|
|
|
|
client = InferenceClient(api_key=HF_TOKEN) |
|
|
|
|
|
def extract_file_content(file_id): |
|
|
filepath = os.path.join(UPLOAD_DIR, file_id) |
|
|
if not os.path.exists(filepath): return "" |
|
|
ext = os.path.splitext(file_id)[1].lower() |
|
|
content = "" |
|
|
try: |
|
|
if ext == ".pdf": |
|
|
doc = fitz.open(filepath) |
|
|
content = "\n".join([page.get_text() for page in doc]) |
|
|
elif ext == ".docx": |
|
|
doc = Document(filepath) |
|
|
content = "\n".join([p.text for p in doc.paragraphs]) |
|
|
elif ext == ".zip": |
|
|
with zipfile.ZipFile(filepath, 'r') as z: |
|
|
names = z.namelist() |
|
|
content = f"ملف ZIP يحتوي على: {', '.join(names)}\n" |
|
|
for name in names[:3]: |
|
|
if name.endswith(('.txt', '.py', '.html', '.json')): |
|
|
with z.open(name) as f: |
|
|
content += f"\n-- {name} --\n{f.read().decode('utf-8')[:1000]}" |
|
|
elif ext in [".txt", ".py", ".js", ".html", ".json"]: |
|
|
with open(filepath, "r", encoding="utf-8") as f: |
|
|
content = f.read() |
|
|
except Exception as e: |
|
|
return f"خطأ قراءة: {str(e)}" |
|
|
return content[:15000] |
|
|
|
|
|
@app.get("/") |
|
|
async def read_index(): |
|
|
path = "index.html" if os.path.exists("index.html") else "static/index.html" |
|
|
return FileResponse(path) |
|
|
|
|
|
@app.post("/api/upload") |
|
|
async def upload_file(file: UploadFile = File(...)): |
|
|
ext = os.path.splitext(file.filename)[1] |
|
|
file_id = f"{uuid.uuid4()}{ext}" |
|
|
filepath = os.path.join(UPLOAD_DIR, file_id) |
|
|
with open(filepath, "wb") as f: |
|
|
f.write(await file.read()) |
|
|
return {"file_id": file_id, "url": f"/static/uploads/{file_id}"} |
|
|
|
|
|
@app.post("/api/chat/stream") |
|
|
async def chat_stream(request: Request): |
|
|
data = await request.json() |
|
|
message = data.get("message", "") |
|
|
file_id = data.get("file_id") |
|
|
|
|
|
if not HF_TOKEN: |
|
|
return JSONResponse({"error": "HF_TOKEN missing in Settings!"}, status_code=500) |
|
|
|
|
|
context = "" |
|
|
if file_id: |
|
|
file_text = extract_file_content(file_id) |
|
|
context += f"\n[محتوى الملف]:\n{file_text}\n" |
|
|
|
|
|
messages = [ |
|
|
{"role": "system", "content": "أنت مساعد ذكي. حلل الملفات وأجب بالعربية."}, |
|
|
{"role": "user", "content": f"{context}\n{message}"} |
|
|
] |
|
|
|
|
|
async def gen(): |
|
|
try: |
|
|
|
|
|
stream = client.chat.completions.create( |
|
|
model="huihui-ai/Qwen2.5-72B-Instruct-abliterated", |
|
|
messages=messages, |
|
|
stream=True, |
|
|
max_tokens=2048, |
|
|
temperature=0.7 |
|
|
) |
|
|
for chunk in stream: |
|
|
if chunk.choices[0].delta.content: |
|
|
yield f"data: {json.dumps({'token': chunk.choices[0].delta.content})}\n\n" |
|
|
except Exception as e: |
|
|
|
|
|
error_msg = str(e) |
|
|
yield f"data: {json.dumps({'error': error_msg})}\n\n" |
|
|
|
|
|
return StreamingResponse(gen(), media_type="text/event-stream") |
|
|
|