youssefreda9 commited on
Commit
8b888b0
·
1 Parent(s): 0405b6b

fix: Back to InferenceClient with correct method signatures + diagnostics

Browse files
Files changed (1) hide show
  1. src/hf_inference.py +109 -90
src/hf_inference.py CHANGED
@@ -1,8 +1,8 @@
1
  """
2
  HuggingFace Inference API client for Bayan models.
3
 
4
- Uses the `requests` library (via huggingface_hub's internal session)
5
- to call the HF Inference API from within HF Spaces.
6
 
7
  Models:
8
  - bayan10/summarization-model (MBart, summarization pipeline)
@@ -15,51 +15,23 @@ import os
15
  import json
16
  import logging
17
  import time
18
- import requests
19
 
20
  logger = logging.getLogger(__name__)
21
 
22
  HF_API_TOKEN = os.environ.get("HF_API_TOKEN", "")
23
- HF_API_BASE = "https://api-inference.huggingface.co/models/"
24
- HF_TIMEOUT = 120 # seconds — accounts for cold starts
25
 
 
 
26
 
27
- def _headers():
28
- """Build request headers with auth token."""
29
- h = {"Content-Type": "application/json"}
30
- if HF_API_TOKEN:
31
- h["Authorization"] = "Bearer " + HF_API_TOKEN
32
- return h
33
 
34
-
35
- def _call_model(repo_id, payload):
36
- """
37
- Call any HF model via the Inference API.
38
- Uses requests library + wait_for_model option.
39
- Returns parsed JSON response.
40
- """
41
- url = HF_API_BASE + repo_id
42
-
43
- # Tell HF to wait for the model to load instead of returning 503
44
- if "options" not in payload:
45
- payload["options"] = {"wait_for_model": True}
46
-
47
- logger.info("HF API call: %s", repo_id)
48
- resp = requests.post(url, headers=_headers(), json=payload, timeout=HF_TIMEOUT)
49
-
50
- logger.info("HF API response for %s: HTTP %d, size=%d bytes",
51
- repo_id, resp.status_code, len(resp.content))
52
-
53
- if resp.status_code != 200:
54
- logger.error("HF API error for %s: HTTP %d — %s",
55
- repo_id, resp.status_code, resp.text[:500])
56
- raise RuntimeError("HF API error for {} (HTTP {}): {}".format(
57
- repo_id, resp.status_code, resp.text[:300]))
58
-
59
- result = resp.json()
60
- logger.info("HF API result for %s: type=%s, preview=%s",
61
- repo_id, type(result).__name__, str(result)[:150])
62
- return result
63
 
64
 
65
  # ============================================================
@@ -73,83 +45,104 @@ AUTOCOMPLETE_REPO = os.environ.get("AUTOCOMPLETE_REPO_ID", "bayan10/AutoComplete
73
 
74
 
75
  # ============================================================
76
- # Model-specific wrappers
77
  # ============================================================
78
 
79
- def _extract_text(result, fallback=""):
80
- """Extract generated text from various HF response formats."""
81
- if isinstance(result, list) and len(result) > 0:
82
- item = result[0]
83
- if isinstance(item, dict):
84
- return (item.get("summary_text")
85
- or item.get("generated_text")
86
- or item.get("translation_text")
87
- or fallback)
88
- return str(item) if str(item).strip() else fallback
89
-
90
- if isinstance(result, dict):
91
- return (result.get("summary_text")
92
- or result.get("generated_text")
93
- or result.get("translation_text")
94
- or fallback)
95
 
96
- return str(result) if result else fallback
97
 
 
98
 
99
- def hf_summarize(text, max_length=128, min_length=30):
100
- """Summarize Arabic text via HF Inference API."""
101
- result = _call_model(SUMMARIZATION_REPO, {
102
- "inputs": text,
103
- "parameters": {
104
- "max_length": max_length,
105
- "min_length": min_length,
106
- }
107
- })
108
- return _extract_text(result, text[:100])
109
 
110
 
111
  def hf_correct_spelling(text):
112
  """Correct spelling in Arabic text via HF Inference API."""
113
- result = _call_model(SPELLING_REPO, {"inputs": text})
114
- return _extract_text(result, text)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
 
117
  def hf_add_punctuation(text):
118
  """Add punctuation to Arabic text via HF Inference API."""
119
- result = _call_model(PUNCTUATION_REPO, {"inputs": text})
120
- return _extract_text(result, text)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
 
122
 
123
  def hf_autocomplete(text, n=5):
124
  """Get autocomplete suggestions for Arabic text via HF Inference API."""
125
- result = _call_model(AUTOCOMPLETE_REPO, {
126
- "inputs": text,
127
- "parameters": {"max_new_tokens": 20}
128
- })
 
 
129
 
130
  if isinstance(result, str):
131
  completion = result[len(text):].strip() if result.startswith(text) else result
132
  return [completion] if completion else [text]
133
 
134
- if isinstance(result, list):
135
- suggestions = []
136
- for item in result:
137
- gen = item.get("generated_text", "") if isinstance(item, dict) else str(item)
138
- if gen.startswith(text):
139
- gen = gen[len(text):].strip()
140
- if gen:
141
- suggestions.append(gen)
142
- return suggestions if suggestions else [text]
143
-
144
  return [text]
145
 
146
 
147
  def check_hf_api_available():
148
  """Quick check if HF Inference API is reachable."""
149
  try:
150
- resp = requests.get(HF_API_BASE + SUMMARIZATION_REPO,
151
- headers=_headers(), timeout=10)
152
- return resp.status_code == 200
153
  except Exception:
154
  return False
155
 
@@ -157,12 +150,38 @@ def check_hf_api_available():
157
  def debug_test_all_models():
158
  """
159
  Test all HF models and return results dict.
160
- Used by /api/debug-models endpoint for troubleshooting.
161
  """
162
  results = {}
163
  test_text = "هذا نص تجريبي للاختبار"
164
  long_text = (test_text + " ") * 5
165
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  for name, fn, args in [
167
  ("summarization", hf_summarize, (long_text, 30, 10)),
168
  ("spelling", hf_correct_spelling, (test_text,)),
 
1
  """
2
  HuggingFace Inference API client for Bayan models.
3
 
4
+ Uses huggingface_hub.InferenceClient which routes through HF's internal
5
+ network when running inside HF Spaces (bypasses external DNS).
6
 
7
  Models:
8
  - bayan10/summarization-model (MBart, summarization pipeline)
 
15
  import json
16
  import logging
17
  import time
 
18
 
19
  logger = logging.getLogger(__name__)
20
 
21
  HF_API_TOKEN = os.environ.get("HF_API_TOKEN", "")
 
 
22
 
23
+ # Lazy-initialized client
24
+ _client = None
25
 
 
 
 
 
 
 
26
 
27
+ def _get_client():
28
+ """Get or create the InferenceClient singleton."""
29
+ global _client
30
+ if _client is None:
31
+ from huggingface_hub import InferenceClient
32
+ _client = InferenceClient(token=HF_API_TOKEN if HF_API_TOKEN else None)
33
+ logger.info("InferenceClient initialized (token=%s)", "set" if HF_API_TOKEN else "not set")
34
+ return _client
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
 
37
  # ============================================================
 
45
 
46
 
47
  # ============================================================
48
+ # Model-specific wrappers using InferenceClient typed methods
49
  # ============================================================
50
 
51
+ def hf_summarize(text, max_length=128, min_length=30):
52
+ """Summarize Arabic text via HF Inference API."""
53
+ client = _get_client()
54
+ logger.info("Calling summarization: %s", SUMMARIZATION_REPO)
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
+ result = client.summarization(text, model=SUMMARIZATION_REPO)
57
 
58
+ logger.info("Summarization result: %s — %s", type(result).__name__, str(result)[:150])
59
 
60
+ # SummarizationOutput has .summary_text
61
+ if hasattr(result, "summary_text"):
62
+ return result.summary_text
63
+ if isinstance(result, dict):
64
+ return result.get("summary_text", result.get("generated_text", str(result)))
65
+ return str(result)
 
 
 
 
66
 
67
 
68
  def hf_correct_spelling(text):
69
  """Correct spelling in Arabic text via HF Inference API."""
70
+ client = _get_client()
71
+ logger.info("Calling spelling: %s", SPELLING_REPO)
72
+
73
+ # Try text2text_generation first (for seq2seq models), fall back to text_generation
74
+ try:
75
+ result = client.text2text_generation(text, model=SPELLING_REPO)
76
+ logger.info("Spelling result (t2t): %s — %s", type(result).__name__, str(result)[:150])
77
+ if hasattr(result, "generated_text"):
78
+ return result.generated_text
79
+ if isinstance(result, str):
80
+ return result if result.strip() else text
81
+ if isinstance(result, dict):
82
+ return result.get("generated_text", text)
83
+ return text
84
+ except Exception as e1:
85
+ logger.warning("text2text_generation failed for spelling: %s", repr(e1)[:200])
86
+ try:
87
+ result = client.text_generation(text, model=SPELLING_REPO, max_new_tokens=len(text) + 50)
88
+ logger.info("Spelling result (tg): %s — %s", type(result).__name__, str(result)[:150])
89
+ if isinstance(result, str):
90
+ return result if result.strip() else text
91
+ return text
92
+ except Exception as e2:
93
+ logger.error("text_generation also failed for spelling: %s", repr(e2)[:200])
94
+ raise
95
 
96
 
97
  def hf_add_punctuation(text):
98
  """Add punctuation to Arabic text via HF Inference API."""
99
+ client = _get_client()
100
+ logger.info("Calling punctuation: %s", PUNCTUATION_REPO)
101
+
102
+ try:
103
+ result = client.text2text_generation(text, model=PUNCTUATION_REPO)
104
+ logger.info("Punctuation result (t2t): %s — %s", type(result).__name__, str(result)[:150])
105
+ if hasattr(result, "generated_text"):
106
+ return result.generated_text
107
+ if isinstance(result, str):
108
+ return result if result.strip() else text
109
+ if isinstance(result, dict):
110
+ return result.get("generated_text", text)
111
+ return text
112
+ except Exception as e1:
113
+ logger.warning("text2text_generation failed for punctuation: %s", repr(e1)[:200])
114
+ try:
115
+ result = client.text_generation(text, model=PUNCTUATION_REPO, max_new_tokens=len(text) + 50)
116
+ logger.info("Punctuation result (tg): %s — %s", type(result).__name__, str(result)[:150])
117
+ if isinstance(result, str):
118
+ return result if result.strip() else text
119
+ return text
120
+ except Exception as e2:
121
+ logger.error("text_generation also failed for punctuation: %s", repr(e2)[:200])
122
+ raise
123
 
124
 
125
  def hf_autocomplete(text, n=5):
126
  """Get autocomplete suggestions for Arabic text via HF Inference API."""
127
+ client = _get_client()
128
+ logger.info("Calling autocomplete: %s", AUTOCOMPLETE_REPO)
129
+
130
+ result = client.text_generation(text, model=AUTOCOMPLETE_REPO, max_new_tokens=20)
131
+
132
+ logger.info("Autocomplete result: %s — %s", type(result).__name__, str(result)[:150])
133
 
134
  if isinstance(result, str):
135
  completion = result[len(text):].strip() if result.startswith(text) else result
136
  return [completion] if completion else [text]
137
 
 
 
 
 
 
 
 
 
 
 
138
  return [text]
139
 
140
 
141
  def check_hf_api_available():
142
  """Quick check if HF Inference API is reachable."""
143
  try:
144
+ client = _get_client()
145
+ return client is not None
 
146
  except Exception:
147
  return False
148
 
 
150
  def debug_test_all_models():
151
  """
152
  Test all HF models and return results dict.
153
+ Also includes diagnostic info about InferenceClient internals.
154
  """
155
  results = {}
156
  test_text = "هذا نص تجريبي للاختبار"
157
  long_text = (test_text + " ") * 5
158
 
159
+ # Diagnostic info
160
+ try:
161
+ client = _get_client()
162
+ diag = {
163
+ "client_type": type(client).__name__,
164
+ "api_url": getattr(client, "api_url", "N/A"),
165
+ "base_url": getattr(client, "base_url", "N/A"),
166
+ "model": getattr(client, "model", "N/A"),
167
+ }
168
+ # Check available methods
169
+ diag["has_post"] = hasattr(client, "post")
170
+ diag["has_text2text"] = hasattr(client, "text2text_generation")
171
+ diag["has_summarization"] = hasattr(client, "summarization")
172
+ diag["has_text_generation"] = hasattr(client, "text_generation")
173
+ except Exception as e:
174
+ diag = {"error": repr(e)[:200]}
175
+
176
+ results["_diagnostics"] = diag
177
+
178
+ # Test env vars
179
+ results["_env"] = {
180
+ "HF_INFERENCE_ENDPOINT": os.environ.get("HF_INFERENCE_ENDPOINT", "NOT SET"),
181
+ "HF_API_URL": os.environ.get("HF_API_URL", "NOT SET"),
182
+ "SPACE_ID": os.environ.get("SPACE_ID", "NOT SET"),
183
+ }
184
+
185
  for name, fn, args in [
186
  ("summarization", hf_summarize, (long_text, 30, 10)),
187
  ("spelling", hf_correct_spelling, (test_text,)),