shimaa22 commited on
Commit
f29f8d6
·
verified ·
1 Parent(s): c2f1724

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +57 -140
app.py CHANGED
@@ -1,164 +1,81 @@
 
 
1
  import torch
2
- from fastapi import FastAPI, HTTPException, BackgroundTasks
3
- from fastapi.middleware.cors import CORSMiddleware
4
- from pydantic import BaseModel, Field
5
- from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
6
- from fastapi.responses import StreamingResponse
7
- from threading import Thread
8
- import os
9
 
10
- # ---------------------------------------------------------
11
- # 1. إعداد التطبيق وتفعيل CORS للسماح بالربط من أي منصة
12
- # ---------------------------------------------------------
13
- app = FastAPI(
14
- title="Qwen 2.5 API Server",
15
- description="FastAPI Server to serve Qwen 2.5 (3B / 7B) LLM model from Hugging Face.",
16
- version="1.0.0"
17
- )
18
-
19
- # تفعيل CORS لتشغيل الـ API من أي مكان (Frontend/Mobile App/Other Backend)
20
- app.add_middleware(
21
- CORSMiddleware,
22
- allow_origins=["*"], # يمكن تحديد النطاقات المسموح لها هنا لزيادة الأمان
23
- allow_credentials=True,
24
- allow_methods=["*"],
25
- allow_headers=["*"],
26
- )
27
 
28
- # ---------------------------------------------------------
29
- # 2. تحميل النموذج والـ Tokenizer
30
- # ---------------------------------------------------------
31
- # يمكنك الاختيار بين:
32
- # "Qwen/Qwen2.5-3B-Instruct" (يحتاج حافظة GPU ~6GB - 8GB)
33
- # "Qwen/Qwen2.5-7B-Instruct" (يحتاج حافظة GPU ~14GB - 16GB)
34
- MODEL_NAME = os.getenv("MODEL_NAME", "Qwen/Qwen2.5-3B-Instruct")
35
 
36
  print(f"Loading Model: {MODEL_NAME} ...")
 
 
 
 
 
 
 
 
 
 
37
 
38
- # تحديد جهاز التشغيل (GPU إذا توفر أو CPU)
39
- device = "cuda" if torch.cuda.is_available() else "cpu"
40
-
41
- try:
42
- tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)
43
-
44
- # تحميل النموذج بنظام float16 إذا كان GPU متاح لتوفير الذاكرة والسرعة
45
- model = AutoModelForCausalLM.from_pretrained(
46
- MODEL_NAME,
47
- torch_dtype=torch.float16 if device == "cuda" else torch.float32,
48
- device_map="auto" if device == "cuda" else None,
49
- trust_remote_code=True
50
- )
51
- if device == "cpu":
52
- model.to("cpu")
53
-
54
- print(f"Model successfully loaded on {device.upper()}!")
55
- except Exception as e:
56
- print(f"Error loading model: {e}")
57
-
58
- # ---------------------------------------------------------
59
- # 3. تعريف هيكل البيانات لتلقي الطلبات (Pydantic Models)
60
- # ---------------------------------------------------------
61
  class ChatMessage(BaseModel):
62
- role: str = Field(..., description="دور المرسل: system, user, or assistant")
63
- content: str = Field(..., description="نص الرسالة")
64
 
65
- class ChatCompletionRequest(BaseModel):
66
- messages: list[ChatMessage] = Field(..., description="قائمة بالرسائل السابقة والحالية")
67
- max_tokens: int = Field(default=512, ge=1, le=4096, description="أقصى عدد للكلمات المولدة")
68
- temperature: float = Field(default=0.7, ge=0.0, le=2.0, description="درجة الإبداع/التنوع")
69
- top_p: float = Field(default=0.9, ge=0.0, le=1.0)
70
- stream: bool = Field(default=False, description="هل تريد الرد بنظام Streaming؟")
71
-
72
- # ---------------------------------------------------------
73
- # 4. Endpoints الـ API
74
- # ---------------------------------------------------------
75
-
76
- @app.get("/")
77
- def health_check():
78
- """فحص حالة السيرفر والنموذج"""
79
- return {
80
- "status": "online",
81
- "model_loaded": MODEL_NAME,
82
- "device": device
83
- }
84
 
85
  @app.post("/v1/chat/completions")
86
- async def chat_completion(request: ChatCompletionRequest):
87
- """
88
- Endpoint الأساسي لإرسال استفسار واستلام الرد من Qwen 2.5
89
- """
90
  try:
91
- # تجهيز المدخلات بصيغة Chat Template الخاص بـ Qwen
92
- formatted_messages = [{"role": msg.role, "content": msg.content} for msg in request.messages]
93
 
94
- prompt_text = tokenizer.apply_chat_template(
95
  formatted_messages,
96
  tokenize=False,
97
  add_generation_prompt=True
98
  )
99
-
100
- model_inputs = tokenizer([prompt_text], return_tensors="pt").to(device)
101
-
102
- # ------------------- حالة Streaming -------------------
103
- if request.stream:
104
- streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
105
-
106
- generation_kwargs = dict(
107
- model_inputs,
108
- streamer=streamer,
109
  max_new_tokens=request.max_tokens,
110
- temperature=request.temperature,
111
- top_p=request.top_p,
112
- do_sample=True if request.temperature > 0 else False
113
  )
114
-
115
- # تشغيل توليد النص في Thread منفصل لعدم تجميد FastAPI
116
- thread = Thread(target=model.generate, kwargs=generation_kwargs)
117
- thread.start()
118
-
119
- def event_generator():
120
- for new_text in streamer:
121
- yield new_text
122
-
123
- return StreamingResponse(event_generator(), media_type="text/plain")
124
-
125
- # ------------------- الحالة العادية (Full Response) -------------------
126
- else:
127
- with torch.no_grad():
128
- generated_ids = model.generate(
129
- **model_inputs,
130
- max_new_tokens=request.max_tokens,
131
- temperature=request.temperature,
132
- top_p=request.top_p,
133
- do_sample=True if request.temperature > 0 else False
134
- )
135
-
136
- # إزالة المدخلات للحصول على الإجابة الجديدة فقط
137
- generated_ids = [
138
- output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
139
- ]
140
-
141
- response_text = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
142
-
143
- return {
144
- "model": MODEL_NAME,
145
- "choices": [
146
- {
147
- "message": {
148
- "role": "assistant",
149
- "content": response_text
150
- }
151
  }
152
- ]
153
- }
154
-
155
  except Exception as e:
156
  raise HTTPException(status_code=500, detail=str(e))
157
 
158
- # ---------------------------------------------------------
159
- # 5. تشغيل السيرفر
160
- # ---------------------------------------------------------
 
161
  if __name__ == "__main__":
162
  import uvicorn
163
- # تشغيل السيرفر على البورت 8000
164
- uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=False)
 
1
+ from fastapi import FastAPI, HTTPException
2
+ from pydantic import BaseModel
3
  import torch
4
+ from transformers import AutoModelForCausalLM, AutoTokenizer
 
 
 
 
 
 
5
 
6
+ app = FastAPI(title="Qwen API Server")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
+ # إعدادات المودل (تم اختيار 3B ليتناسب مع الـ CPU)
9
+ MODEL_NAME = "Qwen/Qwen2.5-3B-Instruct"
 
 
 
 
 
10
 
11
  print(f"Loading Model: {MODEL_NAME} ...")
12
+ # تحميل الـ Tokenizer والـ Model مرة واحدة فقط عند بدء تشغيل السيرفر
13
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
14
+
15
+ # تحميل على الـ CPU واستخدام نوع بيانات خفيف (float32 أو bfloat16 إذا مدعوم) لتوفير الذاكرة
16
+ model = AutoModelForCausalLM.from_pretrained(
17
+ MODEL_NAME,
18
+ torch_dtype=torch.float32,
19
+ device_map="cpu"
20
+ )
21
+ print("Model successfully loaded on CPU!")
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  class ChatMessage(BaseModel):
24
+ role: str
25
+ content: str
26
 
27
+ class ChatRequest(BaseModel):
28
+ messages: list[ChatMessage]
29
+ max_tokens: int = 512
30
+ temperature: float = 0.7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
  @app.post("/v1/chat/completions")
33
+ async def chat_completion(request: ChatRequest):
 
 
 
34
  try:
35
+ # تحويل الرسائل إلى صيغة الـ Chat Template الخاصة بـ Qwen
36
+ formatted_messages = [{"role": m.role, "content": m.content} for m in request.messages]
37
 
38
+ text = tokenizer.apply_chat_template(
39
  formatted_messages,
40
  tokenize=False,
41
  add_generation_prompt=True
42
  )
43
+
44
+ inputs = tokenizer([text], return_tensors="pt")
45
+
46
+ # توليد الرد
47
+ with torch.no_grad():
48
+ outputs = model.generate(
49
+ **inputs,
 
 
 
50
  max_new_tokens=request.max_tokens,
51
+ temperature=request.max_tokens, # أو استخدامه بشكل طبيعي
52
+ do_sample=True,
53
+ top_p=0.9
54
  )
55
+
56
+ # استخراج النص المُولد فقط (بدون المدخلات)
57
+ generated_ids = [
58
+ output[len(input_ids):] for input_ids, output in zip(inputs.input_ids, outputs)
59
+ ]
60
+ response_text = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
61
+
62
+ return {
63
+ "choices": [
64
+ {
65
+ "message": {
66
+ "role": "assistant",
67
+ "content": response_text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  }
69
+ }
70
+ ]
71
+ }
72
  except Exception as e:
73
  raise HTTPException(status_code=500, detail=str(e))
74
 
75
+ @app.get("/")
76
+ def home():
77
+ return {"status": "Running", "model": MODEL_NAME}
78
+
79
  if __name__ == "__main__":
80
  import uvicorn
81
+ uvicorn.run(app, host="0.0.0.0", port=7860) # Hugging Face يستخدم غالباً البورت 7860