Ephraimmm commited on
Commit
9ba1aa5
·
verified ·
1 Parent(s): 6431164

Create handler.py

Browse files
Files changed (1) hide show
  1. handler.py +92 -0
handler.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # handler.py
2
+
3
+ from typing import Any, Dict
4
+ import torch
5
+ from transformers import AutoModelForCausalLM, AutoTokenizer
6
+ from huggingface_inference_toolkit.logging import logger
7
+
8
+
9
+ class EndpointHandler:
10
+ """
11
+ Custom handler for Hugging Face Inference Endpoints.
12
+ """
13
+
14
+ def __init__(self, model_dir: str, **kwargs: Any):
15
+ """
16
+ Called once at startup. `model_dir` is the path to the downloaded model repository.
17
+ You can load your model, tokenizer, etc. here.
18
+ """
19
+ logger.info(f"Initializing EndpointHandler with model_dir = {model_dir}")
20
+
21
+ # load tokenizer and model from the model_dir or hub
22
+ # we assume model files are present in model_dir
23
+ try:
24
+ self.tokenizer = AutoTokenizer.from_pretrained(model_dir, use_fast=False)
25
+ self.model = AutoModelForCausalLM.from_pretrained(model_dir)
26
+ except Exception as e:
27
+ # fallback: try loading from hub name
28
+ hf_model_id = os.getenv("MODEL_ID") or "Ephraimmm/PIDGIN_gemma-3"
29
+ logger.warn(f"Loading model from hub id {hf_model_id} because fallback. Exception: {e}")
30
+ self.tokenizer = AutoTokenizer.from_pretrained(hf_model_id, use_fast=False)
31
+ self.model = AutoModelForCausalLM.from_pretrained(hf_model_id)
32
+
33
+ # put the model on GPU if available
34
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
35
+ self.model = self.model.to(self.device)
36
+ self.model.eval()
37
+
38
+ # You could also load extra assets, set up config, etc.
39
+
40
+ def __call__(self, data: Dict[str, Any]) -> Dict[str, Any]:
41
+ """
42
+ Called on each API request. `data` is the parsed JSON body from the request.
43
+ Must return a JSON-serializable structure.
44
+ The `data` dict always has an “inputs” key (unless you change it).
45
+ """
46
+ logger.info(f"EndpointHandler received data: {data}")
47
+
48
+ # Extract the prompt (the "inputs" key)
49
+ prompt = data.get("inputs")
50
+ if prompt is None:
51
+ raise ValueError("Request JSON must contain an `inputs` field with the prompt string.")
52
+
53
+ # optional: support generation parameters from the payload
54
+ max_new_tokens = data.get("parameters", {}).get("max_new_tokens", 50)
55
+ temperature = data.get("parameters", {}).get("temperature", 1.0)
56
+ top_k = data.get("parameters", {}).get("top_k", 50)
57
+ top_p = data.get("parameters", {}).get("top_p", 1.0)
58
+ do_sample = data.get("parameters", {}).get("do_sample", True)
59
+
60
+ # Tokenize
61
+ inputs = self.tokenizer(prompt, return_tensors="pt").to(self.device)
62
+
63
+ # Generate
64
+ output = self.model.generate(
65
+ **inputs,
66
+ max_new_tokens=max_new_tokens,
67
+ temperature=temperature,
68
+ top_k=top_k,
69
+ top_p=top_p,
70
+ do_sample=do_sample,
71
+ )
72
+
73
+ # Decode
74
+ full = self.tokenizer.decode(output[0], skip_special_tokens=True)
75
+
76
+ # Optionally remove the original prompt from the returned text to send only the continuation
77
+ continuation = full[len(prompt):] if full.startswith(prompt) else full
78
+
79
+ # Prepare response in the shape expected by Inference Endpoints
80
+ # The “predictions” key is often used (the default inference service expects it)
81
+ return {
82
+ "predictions": [full],
83
+ "continuations": [continuation],
84
+ }
85
+
86
+
87
+ # For local testing (not used in production)
88
+ if __name__ == "__main__":
89
+ # Quick local test
90
+ h = EndpointHandler(model_dir=".")
91
+ resp = h({"inputs": "Once upon a time", "parameters": {"max_new_tokens": 20}})
92
+ print("Response:", resp)