Ephraimmm commited on
Commit
05adb3f
·
verified ·
1 Parent(s): 4770ee5

Update handler.py

Browse files
Files changed (1) hide show
  1. handler.py +69 -80
handler.py CHANGED
@@ -194,15 +194,18 @@ def _to_block_list(content: Any) -> List[Dict[str, str]]:
194
  if content is None:
195
  return [{"type": "text", "text": ""}]
196
  if isinstance(content, list):
197
- # Assume already blocks
198
- return content
 
 
 
199
  # Fallback: stringify
200
  return [{"type": "text", "text": str(content)}]
201
 
202
 
203
  class EndpointHandler:
204
  """
205
- HF Inference Endpoint handler that mirrors OpenAI API format:
206
  - Accepts OpenAI-style requests with 'messages', 'model', etc.
207
  - Returns OpenAI-style responses with 'choices', 'usage', etc.
208
  - Loads Unsloth 4-bit Gemma-3-4B IT
@@ -252,11 +255,10 @@ class EndpointHandler:
252
  role = m.get("role", "user")
253
  content = m.get("content", "")
254
 
255
- # Convert content to block list format
256
  if isinstance(content, str):
257
  content = _to_block_list(content)
258
  elif isinstance(content, list):
259
- # Already in block format or needs conversion
260
  content = _to_block_list(content)
261
  else:
262
  content = _to_block_list(str(content))
@@ -286,12 +288,14 @@ class EndpointHandler:
286
 
287
  Input format (OpenAI API):
288
  {
289
- "messages": [{"role": "user", "content": "Hello"}],
290
- "model": "gpt-3.5-turbo", # optional
 
 
291
  "temperature": 0.7, # optional
292
  "max_tokens": 256, # optional
293
  "top_p": 1.0, # optional
294
- ...
295
  }
296
 
297
  Output format (OpenAI API):
@@ -299,7 +303,7 @@ class EndpointHandler:
299
  "id": "chatcmpl-...",
300
  "object": "chat.completion",
301
  "created": 1234567890,
302
- "model": "gemma-3-4b-pidgin",
303
  "choices": [{
304
  "index": 0,
305
  "message": {
@@ -315,72 +319,48 @@ class EndpointHandler:
315
  }
316
  }
317
  """
318
- logger.info(f"Incoming keys: {list(data.keys())}")
319
 
320
- # Support both OpenAI format ('messages') and legacy format ('inputs')
321
- if "messages" in data:
322
- messages = data["messages"]
323
- if not isinstance(messages, list):
324
- raise ValueError("`messages` must be a list of message objects.")
325
- elif "inputs" in data:
326
- # Legacy support: convert inputs to messages format
327
- prompt = data["inputs"]
328
- if isinstance(prompt, str):
329
- messages = [{"role": "user", "content": prompt}]
330
- elif isinstance(prompt, dict):
331
- messages = [prompt]
332
- elif isinstance(prompt, list):
333
- messages = prompt
334
- else:
335
- raise ValueError("Invalid `inputs` format.")
336
- else:
337
- raise ValueError("Missing `messages` or `inputs` in request body.")
338
-
339
- # Extract parameters (support both OpenAI and custom parameter names)
340
- params: Dict[str, Any] = data.get("parameters", {})
341
 
342
- # Map OpenAI parameters to model parameters
343
- max_new_tokens = int(
344
- params.get("max_new_tokens") or
345
- data.get("max_tokens") or
346
- params.get("max_tokens") or
347
- 256
348
- )
349
- temperature = float(
350
- params.get("temperature") or
351
- data.get("temperature") or
352
- 0.1
353
- )
354
- top_p = float(
355
- params.get("top_p") or
356
- data.get("top_p") or
357
- 1.0
358
- )
359
- top_k: Optional[int] = (
360
- params.get("top_k") or
361
- data.get("top_k")
362
- )
363
- use_cache = bool(params.get("use_cache", False))
364
- repetition_penalty = float(params.get("repetition_penalty", 1.0))
365
- eos_token_id = params.get("eos_token_id", self.tokenizer.eos_token_id)
366
- pad_token_id = params.get("pad_token_id", self.tokenizer.pad_token_id)
367
-
368
  inputs = self._build_model_inputs(messages)
369
 
 
370
  gen_kwargs = dict(
371
  **inputs,
372
- max_new_tokens=max_new_tokens,
373
  temperature=temperature,
374
  top_p=top_p,
375
- do_sample=True,
376
- use_cache=use_cache,
377
- repetition_penalty=repetition_penalty,
378
- eos_token_id=eos_token_id,
379
- pad_token_id=pad_token_id,
380
  )
 
381
  if top_k is not None:
382
  gen_kwargs["top_k"] = int(top_k)
383
 
 
384
  with torch.no_grad():
385
  output_ids = self.model.generate(**gen_kwargs)
386
 
@@ -389,17 +369,17 @@ class EndpointHandler:
389
  generated_ids = output_ids[0, input_len:]
390
  reply_text = self.tokenizer.decode(generated_ids, skip_special_tokens=True)
391
 
392
- # Calculate tokens
393
  prompt_tokens = int(input_len)
394
  completion_tokens = int(generated_ids.shape[-1])
395
  total_tokens = prompt_tokens + completion_tokens
396
 
397
- # Return OpenAI-style response
398
  response = {
399
- "id": f"chatcmpl-{int(time.time())}",
400
  "object": "chat.completion",
401
  "created": int(time.time()),
402
- "model": data.get("model", "gemma-3-4b-pidgin"),
403
  "choices": [
404
  {
405
  "index": 0,
@@ -423,31 +403,40 @@ class EndpointHandler:
423
  if __name__ == "__main__":
424
  h = EndpointHandler(model_dir=".")
425
 
426
- # Test with OpenAI-style format
427
- print("=== OpenAI Format Test ===")
428
  response = h({
 
429
  "messages": [
430
- {"role": "user", "content": "How far?"}
431
- ],
432
- "temperature": 0.7,
433
- "max_tokens": 64
434
  })
435
  print(response)
 
436
 
437
- print("\n=== OpenAI Format with System Message ===")
 
438
  response = h({
 
439
  "messages": [
440
- {"role": "system", "content": "You are a helpful assistant."},
441
- {"role": "user", "content": "Explain why sky dey blue."}
442
  ],
 
443
  "max_tokens": 64
444
  })
445
  print(response)
 
446
 
447
- # Legacy format still works
448
- print("\n=== Legacy Format Test ===")
449
  response = h({
450
- "inputs": "How far?",
451
- "parameters": {"max_new_tokens": 64}
 
 
 
 
 
 
452
  })
453
  print(response)
 
194
  if content is None:
195
  return [{"type": "text", "text": ""}]
196
  if isinstance(content, list):
197
+ # Check if it's already in block format
198
+ if all(isinstance(item, dict) and "type" in item for item in content):
199
+ return content
200
+ # Otherwise treat as raw list, stringify
201
+ return [{"type": "text", "text": str(content)}]
202
  # Fallback: stringify
203
  return [{"type": "text", "text": str(content)}]
204
 
205
 
206
  class EndpointHandler:
207
  """
208
+ HF Inference Endpoint handler that accepts pure OpenAI API format:
209
  - Accepts OpenAI-style requests with 'messages', 'model', etc.
210
  - Returns OpenAI-style responses with 'choices', 'usage', etc.
211
  - Loads Unsloth 4-bit Gemma-3-4B IT
 
255
  role = m.get("role", "user")
256
  content = m.get("content", "")
257
 
258
+ # Convert content to block list format for internal processing
259
  if isinstance(content, str):
260
  content = _to_block_list(content)
261
  elif isinstance(content, list):
 
262
  content = _to_block_list(content)
263
  else:
264
  content = _to_block_list(str(content))
 
288
 
289
  Input format (OpenAI API):
290
  {
291
+ "model": "Ephraimmm/PIDGIN_gemma-3",
292
+ "messages": [
293
+ {"role": "user", "content": "What is deep learning?"}
294
+ ],
295
  "temperature": 0.7, # optional
296
  "max_tokens": 256, # optional
297
  "top_p": 1.0, # optional
298
+ "stream": false # optional
299
  }
300
 
301
  Output format (OpenAI API):
 
303
  "id": "chatcmpl-...",
304
  "object": "chat.completion",
305
  "created": 1234567890,
306
+ "model": "Ephraimmm/PIDGIN_gemma-3",
307
  "choices": [{
308
  "index": 0,
309
  "message": {
 
319
  }
320
  }
321
  """
322
+ logger.info(f"Incoming request: {data}")
323
 
324
+ # Validate required fields
325
+ if "messages" not in data:
326
+ raise ValueError("Missing required field: 'messages'")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
327
 
328
+ messages = data["messages"]
329
+ if not isinstance(messages, list):
330
+ raise ValueError("'messages' must be a list of message objects")
331
+
332
+ # Extract model name (optional)
333
+ model_name = data.get("model", "Ephraimmm/PIDGIN_gemma-3")
334
+
335
+ # Extract OpenAI-style parameters with defaults
336
+ max_tokens = int(data.get("max_tokens", 256))
337
+ temperature = float(data.get("temperature", 0.1))
338
+ top_p = float(data.get("top_p", 1.0))
339
+ top_k = data.get("top_k", None)
340
+ stream = data.get("stream", False) # Not implemented yet
341
+ presence_penalty = data.get("presence_penalty", 0.0)
342
+ frequency_penalty = data.get("frequency_penalty", 0.0)
343
+
344
+ # Build model inputs
 
 
 
 
 
 
 
 
 
345
  inputs = self._build_model_inputs(messages)
346
 
347
+ # Prepare generation parameters
348
  gen_kwargs = dict(
349
  **inputs,
350
+ max_new_tokens=max_tokens,
351
  temperature=temperature,
352
  top_p=top_p,
353
+ do_sample=temperature > 0, # Use sampling if temperature > 0
354
+ use_cache=True,
355
+ repetition_penalty=1.0 + frequency_penalty, # Map frequency_penalty
356
+ eos_token_id=self.tokenizer.eos_token_id,
357
+ pad_token_id=self.tokenizer.pad_token_id,
358
  )
359
+
360
  if top_k is not None:
361
  gen_kwargs["top_k"] = int(top_k)
362
 
363
+ # Generate response
364
  with torch.no_grad():
365
  output_ids = self.model.generate(**gen_kwargs)
366
 
 
369
  generated_ids = output_ids[0, input_len:]
370
  reply_text = self.tokenizer.decode(generated_ids, skip_special_tokens=True)
371
 
372
+ # Calculate token usage
373
  prompt_tokens = int(input_len)
374
  completion_tokens = int(generated_ids.shape[-1])
375
  total_tokens = prompt_tokens + completion_tokens
376
 
377
+ # Return OpenAI-compatible response
378
  response = {
379
+ "id": f"chatcmpl-{int(time.time() * 1000)}",
380
  "object": "chat.completion",
381
  "created": int(time.time()),
382
+ "model": model_name,
383
  "choices": [
384
  {
385
  "index": 0,
 
403
  if __name__ == "__main__":
404
  h = EndpointHandler(model_dir=".")
405
 
406
+ # Test 1: Simple user message (OpenAI format)
407
+ print("=== Test 1: Simple User Message ===")
408
  response = h({
409
+ "model": "Ephraimmm/PIDGIN_gemma-3",
410
  "messages": [
411
+ {"role": "user", "content": "What is deep learning?"}
412
+ ]
 
 
413
  })
414
  print(response)
415
+ print()
416
 
417
+ # Test 2: With temperature and max_tokens
418
+ print("=== Test 2: With Parameters ===")
419
  response = h({
420
+ "model": "Ephraimmm/PIDGIN_gemma-3",
421
  "messages": [
422
+ {"role": "user", "content": "How far?"}
 
423
  ],
424
+ "temperature": 0.7,
425
  "max_tokens": 64
426
  })
427
  print(response)
428
+ print()
429
 
430
+ # Test 3: Multi-turn conversation
431
+ print("=== Test 3: Multi-turn Conversation ===")
432
  response = h({
433
+ "model": "Ephraimmm/PIDGIN_gemma-3",
434
+ "messages": [
435
+ {"role": "system", "content": "You are a helpful assistant."},
436
+ {"role": "user", "content": "Explain why the sky is blue"},
437
+ {"role": "assistant", "content": "The sky dey blue because..."},
438
+ {"role": "user", "content": "Tell me more"}
439
+ ],
440
+ "max_tokens": 100
441
  })
442
  print(response)