youssefreda9 commited on
Commit
82a77dd
·
1 Parent(s): dc7f39b

fix: Use _inner_post transport from InferenceClient v1.19.0

Browse files
Files changed (1) hide show
  1. src/hf_inference.py +60 -90
src/hf_inference.py CHANGED
@@ -1,8 +1,8 @@
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.
6
  """
7
 
8
  import os
@@ -22,7 +22,7 @@ def _get_client():
22
  if _client is None:
23
  from huggingface_hub import InferenceClient
24
  _client = InferenceClient(token=HF_API_TOKEN if HF_API_TOKEN else None)
25
- logger.info("InferenceClient initialized")
26
  return _client
27
 
28
 
@@ -33,54 +33,51 @@ PUNCTUATION_REPO = os.environ.get("PUNCTUATION_REPO_ID", "bayan10/PuncAra-v1")
33
  AUTOCOMPLETE_REPO = os.environ.get("AUTOCOMPLETE_REPO_ID", "bayan10/AutoComplete")
34
 
35
 
36
- def _raw_inference(repo_id, payload):
37
  """
38
- Make a raw inference call using whatever transport InferenceClient uses.
39
- Tries multiple approaches to find one that works.
40
  """
41
  client = _get_client()
42
 
43
- # Approach 1: Try the internal _post method
44
- if hasattr(client, '_post'):
45
- logger.info("Using client._post for %s", repo_id)
46
- response = client._post(json=payload, model=repo_id)
47
- return json.loads(response) if isinstance(response, (bytes, str)) else response
48
 
49
- # Approach 2: Get the session from the client and use it directly
50
- session = None
51
- for attr in ['_session', 'session', '_client', 'client', '_http_client']:
52
- if hasattr(client, attr):
53
- session = getattr(client, attr)
54
- break
55
-
56
- if session and hasattr(session, 'post'):
57
- # Find the base API URL
58
- api_url = None
59
- for attr in ['api_url', 'base_url', '_api_url', 'inference_url', '_base_url']:
60
- val = getattr(client, attr, None)
61
- if val and isinstance(val, str):
62
- api_url = val
63
- break
64
-
65
- if not api_url:
66
- api_url = "https://api-inference.huggingface.co/models"
67
-
68
- url = api_url.rstrip('/') + '/' + repo_id if '/models' in api_url else api_url + '/models/' + repo_id
69
- logger.info("Using session.post to %s", url)
70
-
71
- headers = {"Content-Type": "application/json"}
72
- if HF_API_TOKEN:
73
- headers["Authorization"] = "Bearer " + HF_API_TOKEN
74
-
75
- resp = session.post(url, json=payload, headers=headers, timeout=120)
76
- return resp.json()
77
-
78
- raise RuntimeError("No usable transport found on InferenceClient")
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
- # ============================================================
82
- # Model wrappers
83
- # ============================================================
84
 
85
  def _extract_text(result, fallback=""):
86
  """Extract text from various HF response formats."""
@@ -93,6 +90,8 @@ def _extract_text(result, fallback=""):
93
  or fallback)
94
  return str(item) if str(item).strip() else fallback
95
  if isinstance(result, dict):
 
 
96
  return (result.get("summary_text")
97
  or result.get("generated_text")
98
  or result.get("translation_text")
@@ -100,41 +99,37 @@ def _extract_text(result, fallback=""):
100
  return str(result) if result else fallback
101
 
102
 
 
 
 
 
103
  def hf_summarize(text, max_length=128, min_length=30):
104
  """Summarize Arabic text."""
105
- result = _raw_inference(SUMMARIZATION_REPO, {
106
  "inputs": text,
107
  "parameters": {"max_length": max_length, "min_length": min_length},
108
- "options": {"wait_for_model": True},
109
- })
110
  return _extract_text(result, text[:100])
111
 
112
 
113
  def hf_correct_spelling(text):
114
  """Correct spelling in Arabic text."""
115
- result = _raw_inference(SPELLING_REPO, {
116
- "inputs": text,
117
- "options": {"wait_for_model": True},
118
- })
119
  return _extract_text(result, text)
120
 
121
 
122
  def hf_add_punctuation(text):
123
  """Add punctuation to Arabic text."""
124
- result = _raw_inference(PUNCTUATION_REPO, {
125
- "inputs": text,
126
- "options": {"wait_for_model": True},
127
- })
128
  return _extract_text(result, text)
129
 
130
 
131
  def hf_autocomplete(text, n=5):
132
  """Get autocomplete suggestions."""
133
- result = _raw_inference(AUTOCOMPLETE_REPO, {
134
  "inputs": text,
135
  "parameters": {"max_new_tokens": 20},
136
- "options": {"wait_for_model": True},
137
- })
138
 
139
  if isinstance(result, str):
140
  c = result[len(text):].strip() if result.startswith(text) else result
@@ -159,46 +154,21 @@ def check_hf_api_available():
159
 
160
 
161
  def debug_test_all_models():
162
- """Full diagnostic debug."""
163
  results = {}
164
  test_text = "هذا نص تجريبي للاختبار"
165
  long_text = (test_text + " ") * 5
166
 
167
- # Deep diagnostics
168
  try:
169
- client = _get_client()
170
  import huggingface_hub
171
- all_attrs = [a for a in dir(client) if not a.startswith('__')]
172
- private_attrs = {a: str(type(getattr(client, a, None)).__name__)
173
- for a in all_attrs if a.startswith('_') and not a.startswith('__')}
174
- public_methods = [a for a in all_attrs if not a.startswith('_')
175
- and callable(getattr(client, a, None))]
176
-
177
- diag = {
178
- "hf_hub_version": getattr(huggingface_hub, '__version__', 'unknown'),
179
- "has__post": hasattr(client, '_post'),
180
- "has_session": hasattr(client, '_session') or hasattr(client, 'session'),
181
- "private_attrs": private_attrs,
182
- "public_methods": public_methods[:30],
183
  }
184
-
185
- # Try to find internal session/transport
186
- for attr in ['_session', 'session', '_client', 'client', '_http_client',
187
- '_api_url', 'api_url', 'base_url', '_base_url', 'inference_url',
188
- 'headers', '_headers']:
189
- val = getattr(client, attr, 'NOT_FOUND')
190
- if val != 'NOT_FOUND':
191
- diag['found_' + attr] = str(val)[:200] if not callable(val) else 'callable'
192
  except Exception as e:
193
- diag = {"error": repr(e)[:300]}
194
-
195
- results["_diagnostics"] = diag
196
- results["_env"] = {
197
- "HF_INFERENCE_ENDPOINT": os.environ.get("HF_INFERENCE_ENDPOINT", "NOT SET"),
198
- "SPACE_ID": os.environ.get("SPACE_ID", "NOT SET"),
199
- }
200
 
201
- # Test each model
202
  for name, fn, args in [
203
  ("summarization", hf_summarize, (long_text, 30, 10)),
204
  ("spelling", hf_correct_spelling, (test_text,)),
@@ -211,7 +181,7 @@ def debug_test_all_models():
211
  elapsed = time.time() - t0
212
  results[name] = {
213
  "status": "ok",
214
- "result": str(result)[:200],
215
  "time_seconds": round(elapsed, 2),
216
  }
217
  except Exception as e:
 
1
  """
2
  HuggingFace Inference API client for Bayan models.
3
 
4
+ Uses huggingface_hub.InferenceClient._inner_post (v1.19.0) which handles
5
+ internal routing inside HF Spaces.
6
  """
7
 
8
  import os
 
22
  if _client is None:
23
  from huggingface_hub import InferenceClient
24
  _client = InferenceClient(token=HF_API_TOKEN if HF_API_TOKEN else None)
25
+ logger.info("InferenceClient v1.19.0 initialized")
26
  return _client
27
 
28
 
 
33
  AUTOCOMPLETE_REPO = os.environ.get("AUTOCOMPLETE_REPO_ID", "bayan10/AutoComplete")
34
 
35
 
36
+ def _call_model(repo_id, payload, task=None):
37
  """
38
+ Call HF model using InferenceClient._inner_post.
39
+ This is the raw transport that all typed methods use internally.
40
  """
41
  client = _get_client()
42
 
43
+ if "options" not in payload:
44
+ payload["options"] = {"wait_for_model": True}
 
 
 
45
 
46
+ logger.info("Calling HF model: %s (task=%s)", repo_id, task)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
+ try:
49
+ # Use _inner_post — the internal transport in huggingface_hub 1.19.0
50
+ response = client._inner_post(
51
+ json=payload,
52
+ model=repo_id,
53
+ task=task,
54
+ )
55
+
56
+ # response is bytes
57
+ if isinstance(response, bytes):
58
+ result = json.loads(response.decode("utf-8"))
59
+ elif isinstance(response, str):
60
+ result = json.loads(response)
61
+ else:
62
+ result = response
63
+
64
+ logger.info("HF result for %s: type=%s preview=%s",
65
+ repo_id, type(result).__name__, str(result)[:200])
66
+ return result
67
+
68
+ except TypeError as e:
69
+ # If _inner_post has different signature, try without task
70
+ logger.warning("_inner_post with task failed: %s, retrying without task", e)
71
+ response = client._inner_post(
72
+ json=payload,
73
+ model=repo_id,
74
+ )
75
+ if isinstance(response, bytes):
76
+ return json.loads(response.decode("utf-8"))
77
+ elif isinstance(response, str):
78
+ return json.loads(response)
79
+ return response
80
 
 
 
 
81
 
82
  def _extract_text(result, fallback=""):
83
  """Extract text from various HF response formats."""
 
90
  or fallback)
91
  return str(item) if str(item).strip() else fallback
92
  if isinstance(result, dict):
93
+ if "error" in result:
94
+ raise RuntimeError("HF API error: " + str(result["error"]))
95
  return (result.get("summary_text")
96
  or result.get("generated_text")
97
  or result.get("translation_text")
 
99
  return str(result) if result else fallback
100
 
101
 
102
+ # ============================================================
103
+ # Model wrappers
104
+ # ============================================================
105
+
106
  def hf_summarize(text, max_length=128, min_length=30):
107
  """Summarize Arabic text."""
108
+ result = _call_model(SUMMARIZATION_REPO, {
109
  "inputs": text,
110
  "parameters": {"max_length": max_length, "min_length": min_length},
111
+ }, task="summarization")
 
112
  return _extract_text(result, text[:100])
113
 
114
 
115
  def hf_correct_spelling(text):
116
  """Correct spelling in Arabic text."""
117
+ result = _call_model(SPELLING_REPO, {"inputs": text})
 
 
 
118
  return _extract_text(result, text)
119
 
120
 
121
  def hf_add_punctuation(text):
122
  """Add punctuation to Arabic text."""
123
+ result = _call_model(PUNCTUATION_REPO, {"inputs": text})
 
 
 
124
  return _extract_text(result, text)
125
 
126
 
127
  def hf_autocomplete(text, n=5):
128
  """Get autocomplete suggestions."""
129
+ result = _call_model(AUTOCOMPLETE_REPO, {
130
  "inputs": text,
131
  "parameters": {"max_new_tokens": 20},
132
+ }, task="text-generation")
 
133
 
134
  if isinstance(result, str):
135
  c = result[len(text):].strip() if result.startswith(text) else result
 
154
 
155
 
156
  def debug_test_all_models():
157
+ """Test all HF models with full diagnostics."""
158
  results = {}
159
  test_text = "هذا نص تجريبي للاختبار"
160
  long_text = (test_text + " ") * 5
161
 
162
+ # Version info
163
  try:
 
164
  import huggingface_hub
165
+ results["_info"] = {
166
+ "hf_hub_version": huggingface_hub.__version__,
167
+ "has__inner_post": hasattr(_get_client(), '_inner_post'),
 
 
 
 
 
 
 
 
 
168
  }
 
 
 
 
 
 
 
 
169
  except Exception as e:
170
+ results["_info"] = {"error": repr(e)[:200]}
 
 
 
 
 
 
171
 
 
172
  for name, fn, args in [
173
  ("summarization", hf_summarize, (long_text, 30, 10)),
174
  ("spelling", hf_correct_spelling, (test_text,)),
 
181
  elapsed = time.time() - t0
182
  results[name] = {
183
  "status": "ok",
184
+ "result": str(result)[:300],
185
  "time_seconds": round(elapsed, 2),
186
  }
187
  except Exception as e: