Spaces:
Runtime error
Runtime error
| import torch | |
| import faiss | |
| import numpy as np | |
| import os | |
| import re | |
| from fastapi import FastAPI | |
| from pydantic import BaseModel | |
| from typing import List, Dict | |
| from transformers import AutoTokenizer, AutoModelForCausalLM | |
| from sentence_transformers import SentenceTransformer | |
| # --- 1. إعداد الـ API --- | |
| app = FastAPI(title="Agricultural Expert API (Free Tier)") | |
| # --- 2. تحميل الموديلات (نسخة خفيفة للـ CPU) --- | |
| model_name = "Qwen/Qwen2.5-1.5B-Instruct" | |
| print("⏳ Loading Models on CPU (This might take a minute)...") | |
| tokenizer = AutoTokenizer.from_pretrained(model_name) | |
| model = AutoModelForCausalLM.from_pretrained( | |
| model_name, | |
| device_map="cpu" # إجبار الموديل يشتغل على البروسيسور | |
| ) | |
| embedding_model = SentenceTransformer("BAAI/bge-m3", device="cpu") # الـ Embedding كمان على الـ CPU | |
| # --- 3. بناء الـ FAISS Index --- | |
| DISEASES_FOLDER = "diseases" | |
| all_chunks = [] | |
| all_embeddings = [] | |
| def split_text(text, chunk_size=600, chunk_overlap=100): | |
| chunks = [] | |
| start = 0 | |
| while start < len(text): | |
| end = start + chunk_size | |
| chunks.append(text[start:end]) | |
| start += chunk_size - chunk_overlap | |
| return chunks | |
| if os.path.exists(DISEASES_FOLDER): | |
| for filename in os.listdir(DISEASES_FOLDER): | |
| if filename.endswith(".txt"): | |
| with open(os.path.join(DISEASES_FOLDER, filename), "r", encoding="utf-8") as f: | |
| text = f.read() | |
| chunks = split_text(text) | |
| embeddings = embedding_model.encode(chunks, convert_to_numpy=True) | |
| all_chunks.extend(chunks) | |
| all_embeddings.append(embeddings) | |
| if all_embeddings: | |
| all_embeddings = np.vstack(all_embeddings) | |
| index = faiss.IndexFlatL2(all_embeddings.shape[1]) | |
| index.add(all_embeddings.astype('float32')) | |
| print(f"✅ Index built successfully with {len(all_chunks)} chunks.") | |
| else: | |
| print("⚠️ Folder 'diseases' not found!") | |
| # --- 4. هيكل الـ Request اللي الفرونت إند هيبعته --- | |
| class ChatRequest(BaseModel): | |
| question: str | |
| chat_history: List[Dict[str, str]] = [] # الفرونت إند هيبعت الذاكرة هنا | |
| k_results: int = 3 | |
| # --- 5. الـ Endpoint اللي هيستقبل الأسئلة --- | |
| async def ask_ai_api(request: ChatRequest): | |
| # 1. البحث في السياق | |
| question_embedding = embedding_model.encode([request.question], convert_to_numpy=True) | |
| distances, indices = index.search(question_embedding.astype('float32'), request.k_results) | |
| context = "\n\n".join([all_chunks[i] for i in indices[0]]) | |
| # 2. بناء الرسائل بنظام "مرآة اللغة" | |
| messages = [ | |
| { | |
| "role": "system", | |
| "content": ( | |
| "You are a professional agricultural assistant.\n" | |
| "STRICT RULE: Always respond in the SAME LANGUAGE as the user's current question.\n" | |
| "- If the user asks in English, your entire response MUST be in English.\n" | |
| "- If the user asks in Arabic, your entire response MUST be in Arabic.\n" | |
| "Use the provided context to answer accurately. Don't use Chinese." | |
| ) | |
| } | |
| ] | |
| # إضافة الذاكرة الجاية من الفرونت إند | |
| messages.extend(request.chat_history) | |
| # إضافة السؤال الحالي مع السياق | |
| messages.append({ | |
| "role": "user", | |
| "content": f"Context: {context}\n\nQuestion: {request.question}\n(أجب بشرح مفصل وجمل واضحة)" | |
| }) | |
| # 3. تجهيز الـ Prompt للموديل | |
| text_input = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) | |
| inputs = tokenizer([text_input], return_tensors="pt").to(model.device) | |
| # 4. التوليد (بإعدادات تمنع التقطيع) | |
| with torch.no_grad(): | |
| outputs = model.generate( | |
| **inputs, | |
| max_new_tokens=800, | |
| temperature=0.5, | |
| top_p=0.92, | |
| repetition_penalty=1.05, | |
| do_sample=True, | |
| pad_token_id=tokenizer.eos_token_id | |
| ) | |
| # استخراج النص | |
| generated_ids = outputs[0][inputs.input_ids.shape[1]:] | |
| answer = tokenizer.decode(generated_ids, skip_special_tokens=True).strip() | |
| # فلتر اللغات الغريبة | |
| answer = re.sub(r'[^\u0600-\u06FF\u0660-\u0669a-zA-Z0-9\s\.,!\?؟\-\%\(\)\:\/\*]', '', answer) | |
| # الرد النهائي اللي هيرجع للفرونت إند كـ JSON | |
| return { | |
| "question": request.question, | |
| "answer": answer, | |
| "retrieved_context": context | |
| } |