nvikou commited on
Commit
7a3f8b6
·
verified ·
1 Parent(s): c486c85

UI: sidebar + insight panel; metrics retrieval/time; fix InferenceClient provider

Browse files
Files changed (1) hide show
  1. app/crag/llm_client.py +54 -31
app/crag/llm_client.py CHANGED
@@ -1,17 +1,20 @@
1
  """Client LLM unique pour tout le pipeline CRAG.
2
 
3
- Modèle de production : deepseek-ai/DeepSeek-V4.1-Flash, servi via l'API
4
- d'inférence Hugging Face (huggingface_hub.InferenceClient, chat.completions).
5
- Un mode "mock" (LLM_PROVIDER=mock) permet de faire tourner l'app, les tests et
6
- la démo sans token HF ni appel réseau — utile en CI ou pour un premier test
7
- local avant configuration du Space.
8
  """
9
  from __future__ import annotations
10
 
 
11
  import json
12
  import logging
13
 
14
- from tenacity import retry, stop_after_attempt, wait_exponential
 
 
 
 
 
15
 
16
  from app.core.config import get_settings
17
 
@@ -23,12 +26,7 @@ class LLMError(RuntimeError):
23
 
24
 
25
  def _mock_complete(system_prompt: str, user_prompt: str, json_mode: bool) -> str:
26
- """Réponses déterministes pour les tests / la démo hors-ligne.
27
-
28
- Volontairement simple : ce n'est PAS un modèle de langage, seulement un
29
- filet de sécurité pour que `docker build && docker run` fonctionne sans
30
- HF_TOKEN configuré, et pour des tests unitaires rapides et déterministes.
31
- """
32
  if json_mode:
33
  if "score" in system_prompt.lower() or "pertinence" in system_prompt.lower():
34
  return json.dumps({
@@ -43,35 +41,58 @@ def _mock_complete(system_prompt: str, user_prompt: str, json_mode: bool) -> str
43
  )
44
 
45
 
46
- @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=8))
47
- def _hf_complete(system_prompt: str, user_prompt: str, json_mode: bool) -> str:
48
  from huggingface_hub import InferenceClient
49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  settings = get_settings()
51
  if not settings.hf_token:
52
  raise LLMError(
53
- "HF_TOKEN manquant. Définissez HF_TOKEN dans .env ou dans les "
54
- "secrets du Space Hugging Face (Settings > Variables and secrets)."
55
  )
56
 
57
- client_kwargs = {"model": settings.hf_model_id, "token": settings.hf_token}
58
- if settings.hf_inference_provider:
59
- # API multi-fournisseurs de Hugging Face ("Inference Providers") :
60
- # certains modèles volumineux (ex. DeepSeek, grands MoE) ne sont pas
61
- # servis par l'API serverless HF "historique" mais routés vers un
62
- # fournisseur tiers (together, novita, fireworks-ai, nebius, ...).
63
- # Voir https://huggingface.co/docs/inference-providers
64
- client_kwargs["provider"] = settings.hf_inference_provider
65
- client = InferenceClient(**client_kwargs)
66
  messages = [
67
  {"role": "system", "content": system_prompt},
68
  {"role": "user", "content": user_prompt},
69
  ]
70
- completion = client.chat_completion(
71
- messages=messages,
72
- max_tokens=800,
73
- temperature=0.1 if json_mode else 0.3,
74
- )
 
 
 
 
 
 
 
 
 
75
  return completion.choices[0].message.content
76
 
77
 
@@ -82,6 +103,8 @@ def complete(system_prompt: str, user_prompt: str, json_mode: bool = False) -> s
82
  return _mock_complete(system_prompt, user_prompt, json_mode)
83
  try:
84
  return _hf_complete(system_prompt, user_prompt, json_mode)
85
- except Exception as exc: # noqa: BLE001 - on veut journaliser puis remonter
 
 
86
  logger.error("Échec appel LLM (%s): %s", settings.hf_model_id, exc)
87
  raise LLMError(str(exc)) from exc
 
1
  """Client LLM unique pour tout le pipeline CRAG.
2
 
3
+ Modèle : deepseek-ai/DeepSeek-V4.1-Flash via huggingface_hub.InferenceClient.
4
+ Mode mock (LLM_PROVIDER=mock) pour CI / démo sans token.
 
 
 
5
  """
6
  from __future__ import annotations
7
 
8
+ import inspect
9
  import json
10
  import logging
11
 
12
+ from tenacity import (
13
+ retry,
14
+ retry_if_exception_type,
15
+ stop_after_attempt,
16
+ wait_exponential,
17
+ )
18
 
19
  from app.core.config import get_settings
20
 
 
26
 
27
 
28
  def _mock_complete(system_prompt: str, user_prompt: str, json_mode: bool) -> str:
29
+ """Réponses déterministes hors-ligne (tests / démo sans HF_TOKEN)."""
 
 
 
 
 
30
  if json_mode:
31
  if "score" in system_prompt.lower() or "pertinence" in system_prompt.lower():
32
  return json.dumps({
 
41
  )
42
 
43
 
44
+ def _build_inference_client(settings):
 
45
  from huggingface_hub import InferenceClient
46
 
47
+ kwargs: dict = {"token": settings.hf_token}
48
+ # Certaines versions acceptent model= au constructeur, d'autres non.
49
+ init_params = inspect.signature(InferenceClient.__init__).parameters
50
+ if "model" in init_params:
51
+ kwargs["model"] = settings.hf_model_id
52
+ if settings.hf_inference_provider and "provider" in init_params:
53
+ kwargs["provider"] = settings.hf_inference_provider
54
+ elif settings.hf_inference_provider:
55
+ logger.warning(
56
+ "HF_INFERENCE_PROVIDER=%s ignoré : cette version de "
57
+ "huggingface_hub ne supporte pas provider= sur InferenceClient. "
58
+ "Mettez à jour huggingface_hub ou videz la variable.",
59
+ settings.hf_inference_provider,
60
+ )
61
+ return InferenceClient(**kwargs), settings.hf_model_id
62
+
63
+
64
+ @retry(
65
+ stop=stop_after_attempt(3),
66
+ wait=wait_exponential(multiplier=1, min=1, max=8),
67
+ retry=retry_if_exception_type((TimeoutError, ConnectionError, OSError)),
68
+ reraise=True,
69
+ )
70
+ def _hf_complete(system_prompt: str, user_prompt: str, json_mode: bool) -> str:
71
  settings = get_settings()
72
  if not settings.hf_token:
73
  raise LLMError(
74
+ "HF_TOKEN manquant. Définissez HF_TOKEN dans les secrets du Space."
 
75
  )
76
 
77
+ client, model_id = _build_inference_client(settings)
 
 
 
 
 
 
 
 
78
  messages = [
79
  {"role": "system", "content": system_prompt},
80
  {"role": "user", "content": user_prompt},
81
  ]
82
+ try:
83
+ completion = client.chat_completion(
84
+ messages=messages,
85
+ model=model_id,
86
+ max_tokens=800,
87
+ temperature=0.1 if json_mode else 0.3,
88
+ )
89
+ except TypeError:
90
+ # Anciennes signatures sans model= dans chat_completion
91
+ completion = client.chat_completion(
92
+ messages=messages,
93
+ max_tokens=800,
94
+ temperature=0.1 if json_mode else 0.3,
95
+ )
96
  return completion.choices[0].message.content
97
 
98
 
 
103
  return _mock_complete(system_prompt, user_prompt, json_mode)
104
  try:
105
  return _hf_complete(system_prompt, user_prompt, json_mode)
106
+ except LLMError:
107
+ raise
108
+ except Exception as exc: # noqa: BLE001
109
  logger.error("Échec appel LLM (%s): %s", settings.hf_model_id, exc)
110
  raise LLMError(str(exc)) from exc