bh-ab commited on
Commit
c3330ea
·
verified ·
1 Parent(s): 473786e

Upload 2 files

Browse files
app/services/__init__.py ADDED
File without changes
app/services/hf_client.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ خدمة الاتصال بنموذج Hugging Face عبر huggingface_hub InferenceClient.
3
+
4
+ تدعم استدعاء نماذج LLM/Chatbot المستضافة على:
5
+ - Hugging Face Inference API (المجاني/المدفوع)
6
+ - Hugging Face Inference Endpoints (استضافة مخصصة)
7
+ - مزودين خارجيين متوافقين (مثل together, fireworks-ai...) عبر بارامتر provider
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import logging
12
+ from collections.abc import AsyncGenerator
13
+
14
+ from huggingface_hub import AsyncInferenceClient
15
+ from huggingface_hub.errors import HfHubHTTPError
16
+
17
+ from app.config import Settings
18
+ from app.schemas import ChatMessage
19
+
20
+ logger = logging.getLogger("hf_chat_backend.hf_client")
21
+
22
+
23
+ class HFClientError(Exception):
24
+ """خطأ عام أثناء الاتصال بنموذج Hugging Face."""
25
+
26
+
27
+ class HFInferenceService:
28
+ """طبقة عزل (Adapter) فوق huggingface_hub لسهولة الاستبدال أو الاختبار."""
29
+
30
+ def __init__(self, settings: Settings) -> None:
31
+ self._settings = settings
32
+ self._client = AsyncInferenceClient(
33
+ model=settings.hf_model_id,
34
+ token=settings.hf_api_token or None,
35
+ provider=settings.hf_provider,
36
+ timeout=settings.hf_timeout_seconds,
37
+ )
38
+
39
+ @staticmethod
40
+ def _to_hf_messages(messages: list[ChatMessage]) -> list[dict]:
41
+ return [{"role": m.role, "content": m.content} for m in messages]
42
+
43
+ async def chat_completion(
44
+ self,
45
+ messages: list[ChatMessage],
46
+ max_new_tokens: int | None = None,
47
+ temperature: float | None = None,
48
+ top_p: float | None = None,
49
+ ) -> tuple[str, str | None]:
50
+ """يرسل المحادثة كاملة للنموذج ويعيد الرد النهائي (بدون بث)."""
51
+ try:
52
+ completion = await self._client.chat_completion(
53
+ messages=self._to_hf_messages(messages),
54
+ max_tokens=max_new_tokens or self._settings.default_max_new_tokens,
55
+ temperature=(
56
+ temperature
57
+ if temperature is not None
58
+ else self._settings.default_temperature
59
+ ),
60
+ top_p=top_p if top_p is not None else self._settings.default_top_p,
61
+ stream=False,
62
+ )
63
+ choice = completion.choices[0]
64
+ reply = choice.message.content or ""
65
+ finish_reason = choice.finish_reason
66
+ return reply, finish_reason
67
+ except HfHubHTTPError as exc:
68
+ logger.exception("HF Hub HTTP error")
69
+ raise HFClientError(f"فشل الاتصال بنموذج Hugging Face: {exc}") from exc
70
+ except Exception as exc: # noqa: BLE001
71
+ logger.exception("Unexpected error calling HF model")
72
+ raise HFClientError(f"خطأ غير متوقع أثناء استدعاء النموذج: {exc}") from exc
73
+
74
+ async def chat_completion_stream(
75
+ self,
76
+ messages: list[ChatMessage],
77
+ max_new_tokens: int | None = None,
78
+ temperature: float | None = None,
79
+ top_p: float | None = None,
80
+ ) -> AsyncGenerator[str, None]:
81
+ """يبث الرد تدريجياً (Server-Sent Events) عبر مولّد غير متزامن."""
82
+ try:
83
+ stream = await self._client.chat_completion(
84
+ messages=self._to_hf_messages(messages),
85
+ max_tokens=max_new_tokens or self._settings.default_max_new_tokens,
86
+ temperature=(
87
+ temperature
88
+ if temperature is not None
89
+ else self._settings.default_temperature
90
+ ),
91
+ top_p=top_p if top_p is not None else self._settings.default_top_p,
92
+ stream=True,
93
+ )
94
+ async for chunk in stream:
95
+ delta = chunk.choices[0].delta.content
96
+ if delta:
97
+ yield delta
98
+ except HfHubHTTPError as exc:
99
+ logger.exception("HF Hub HTTP error during streaming")
100
+ raise HFClientError(f"فشل الاتصال بنموذج Hugging Face: {exc}") from exc
101
+ except Exception as exc: # noqa: BLE001
102
+ logger.exception("Unexpected error during streaming")
103
+ raise HFClientError(f"خطأ غير متوقع أثناء البث: {exc}") from exc