Instructions to use AnalyticsIntelligence/PIDGIN_gemma3 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use AnalyticsIntelligence/PIDGIN_gemma3 with Transformers:
# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("AnalyticsIntelligence/PIDGIN_gemma3", device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- Unsloth Desktop
| #tope_version | |
| # handler.py | |
| # handler.py | |
| # from typing import Any, Dict, List, Union, Optional | |
| # import torch | |
| # from huggingface_inference_toolkit.logging import logger | |
| # from unsloth import FastLanguageModel | |
| # from peft import PeftModel | |
| # # OpenAI-style prompt: | |
| # # - str | |
| # # - [{"role": "...", "content": "..." | [{"type":"text","text":"..."}]}] | |
| # Prompt = Union[str, List[Dict[str, Any]], Dict[str, Any]] | |
| # def _to_block_list(content: Any) -> List[Dict[str, str]]: | |
| # """Ensure content is a list[{'type':'text','text':...}].""" | |
| # if content is None: | |
| # return [{"type": "text", "text": ""}] | |
| # if isinstance(content, list): | |
| # # Assume already blocks | |
| # return content | |
| # # Fallback: stringify | |
| # return [{"type": "text", "text": str(content)}] | |
| # class EndpointHandler: | |
| # """ | |
| # HF Inference Endpoint handler that mirrors your Gradio setup: | |
| # - Loads Unsloth 4-bit Gemma-3-4B IT | |
| # - Attaches LoRA adapter | |
| # - Uses tokenizer.apply_chat_template | |
| # - Always replies in Nigerian Pidgin | |
| # """ | |
| # def __init__(self, model_dir: str, **_: Any): | |
| # logger.info(f"Initializing with model_dir={model_dir}") | |
| # base_model_name = "unsloth/gemma-3-4b-it-unsloth-bnb-4bit" | |
| # model, tokenizer = FastLanguageModel.from_pretrained( | |
| # model_name=base_model_name, | |
| # max_seq_length=2048, | |
| # dtype=torch.float16, | |
| # load_in_4bit=True, | |
| # ) | |
| # lora_repo = "Ephraimmm/PIDGIN_gemma-3" | |
| # model = PeftModel.from_pretrained(model, lora_repo) | |
| # FastLanguageModel.for_inference(model) | |
| # self.model = model.eval() | |
| # self.tokenizer = tokenizer | |
| # self.device = getattr( | |
| # self.model, | |
| # "device", | |
| # torch.device("cuda" if torch.cuda.is_available() else "cpu"), | |
| # ) | |
| # # Safety: some tokenizers lack pad_token_id | |
| # if getattr(self.tokenizer, "pad_token_id", None) is None: | |
| # self.tokenizer.pad_token_id = self.tokenizer.eos_token_id | |
| # logger.info(f"Device: {self.device} | eos_id: {self.tokenizer.eos_token_id}") | |
| # self._system_text = ( | |
| # "You are a Nigerian assistant that speaks PIDGIN ENGLISH. " | |
| # "When asked 'how far', reply 'I dey o, how you dey?'. " | |
| # "Always answer in Pidgin English." | |
| # ) | |
| # def _normalize_messages(self, prompt: Prompt) -> List[Dict[str, Any]]: | |
| # """Return a list of messages with block-style contents only.""" | |
| # # Accept single dict | |
| # if isinstance(prompt, dict): | |
| # prompt = [prompt] | |
| # # Accept raw string | |
| # if isinstance(prompt, str): | |
| # msgs = [ | |
| # {"role": "system", "content": _to_block_list(self._system_text)}, | |
| # {"role": "user", "content": _to_block_list(prompt)}, | |
| # ] | |
| # return msgs | |
| # if not isinstance(prompt, list): | |
| # raise ValueError("`inputs` must be a string, a message dict, or a list of messages.") | |
| # # Normalize all contents to blocks | |
| # norm = [] | |
| # for m in prompt: | |
| # role = m.get("role", "user") | |
| # content = _to_block_list(m.get("content", "")) | |
| # norm.append({"role": role, "content": content}) | |
| # # Ensure a system message exists (prepend if missing) | |
| # if not any(m.get("role") == "system" for m in norm): | |
| # norm = [{"role": "system", "content": _to_block_list(self._system_text)}] + norm | |
| # return norm | |
| # def _build_model_inputs(self, prompt: Prompt): | |
| # messages = self._normalize_messages(prompt) | |
| # # Use the same path as your Gradio script | |
| # model_inputs = self.tokenizer.apply_chat_template( | |
| # messages, | |
| # add_generation_prompt=True, | |
| # return_tensors="pt", | |
| # tokenize=True, | |
| # return_dict=True, | |
| # ).to(self.device) | |
| # return model_inputs | |
| # def __call__(self, data: Dict[str, Any]) -> Dict[str, Any]: | |
| # logger.info(f"Incoming keys: {list(data.keys())}") | |
| # if "inputs" not in data: | |
| # raise ValueError("Missing `inputs` in request body.") | |
| # prompt: Prompt = data["inputs"] | |
| # params: Dict[str, Any] = (data.get("parameters") or {}) | |
| # # Defaults aligned with your Gradio code | |
| # max_new_tokens = int(params.get("max_new_tokens", 256)) # raise per request if needed | |
| # temperature = float(params.get("temperature", 0.1)) | |
| # top_p = float(params.get("top_p", 1.0)) | |
| # top_k: Optional[int] = params.get("top_k", None) | |
| # use_cache = bool(params.get("use_cache", False)) | |
| # repetition_penalty = float(params.get("repetition_penalty", 1.0)) | |
| # eos_token_id = params.get("eos_token_id", self.tokenizer.eos_token_id) | |
| # pad_token_id = params.get("pad_token_id", self.tokenizer.pad_token_id) | |
| # inputs = self._build_model_inputs(prompt) | |
| # gen_kwargs = dict( | |
| # **inputs, | |
| # max_new_tokens=max_new_tokens, | |
| # temperature=temperature, | |
| # top_p=top_p, | |
| # do_sample=True, # streaming-like sampling on | |
| # use_cache=use_cache, | |
| # repetition_penalty=repetition_penalty, | |
| # eos_token_id=eos_token_id, | |
| # pad_token_id=pad_token_id, | |
| # ) | |
| # if top_k is not None: | |
| # gen_kwargs["top_k"] = int(top_k) | |
| # with torch.no_grad(): | |
| # output_ids = self.model.generate(**gen_kwargs) | |
| # # Return ONLY the continuation (like your streamer) | |
| # input_len = inputs["input_ids"].shape[-1] | |
| # generated_ids = output_ids[0, input_len:] | |
| # reply_text = self.tokenizer.decode(generated_ids, skip_special_tokens=True) | |
| # return { | |
| # "reply": [reply_text], | |
| # "usage": { | |
| # "prompt_tokens": int(input_len), | |
| # "generated_tokens": int(generated_ids.shape[-1]), | |
| # }, | |
| # } | |
| # if __name__ == "__main__": | |
| # h = EndpointHandler(model_dir=".") | |
| # # Raw string | |
| # print(h({"inputs": "How far?", "parameters": {"max_new_tokens": 64}})) | |
| # # Block-style content (your Gradio shape) | |
| # print(h({ | |
| # "inputs": [ | |
| # { | |
| # "role": "user", | |
| # "content": [{"type": "text", "text": "Explain why sky dey blue."}] | |
| # } | |
| # ], | |
| # "parameters": {"max_new_tokens": 64} | |
| # })) | |
| from typing import Any, Dict, List, Union, Optional | |
| import torch | |
| import time | |
| from huggingface_inference_toolkit.logging import logger | |
| from unsloth import FastLanguageModel | |
| from peft import PeftModel | |
| # OpenAI-style prompt: | |
| # - str | |
| # - [{"role": "...", "content": "..." | [{"type":"text","text":"..."}]}] | |
| Prompt = Union[str, List[Dict[str, Any]], Dict[str, Any]] | |
| def _to_block_list(content: Any) -> List[Dict[str, str]]: | |
| """Ensure content is a list[{'type':'text','text':...}].""" | |
| if content is None: | |
| return [{"type": "text", "text": ""}] | |
| if isinstance(content, list): | |
| # Check if it's already in block format | |
| if all(isinstance(item, dict) and "type" in item for item in content): | |
| return content | |
| # Otherwise treat as raw list, stringify | |
| return [{"type": "text", "text": str(content)}] | |
| # Fallback: stringify | |
| return [{"type": "text", "text": str(content)}] | |
| class EndpointHandler: | |
| """ | |
| HF Inference Endpoint handler that accepts pure OpenAI API format: | |
| - Accepts OpenAI-style requests with 'messages', 'model', etc. | |
| - Returns OpenAI-style responses with 'choices', 'usage', etc. | |
| - Loads Unsloth 4-bit Gemma-3-4B IT | |
| - Attaches LoRA adapter | |
| - Uses tokenizer.apply_chat_template | |
| - Always replies in Nigerian Pidgin | |
| """ | |
| def __init__(self, model_dir: str, **_: Any): | |
| logger.info(f"Initializing with model_dir={model_dir}") | |
| base_model_name = "unsloth/gemma-3-4b-it-unsloth-bnb-4bit" | |
| model, tokenizer = FastLanguageModel.from_pretrained( | |
| model_name=base_model_name, | |
| max_seq_length=2048, | |
| dtype=torch.float16, | |
| load_in_4bit=True, | |
| ) | |
| lora_repo = "Ephraimmm/PIDGIN_gemma-3" | |
| model = PeftModel.from_pretrained(model, lora_repo) | |
| FastLanguageModel.for_inference(model) | |
| self.model = model.eval() | |
| self.tokenizer = tokenizer | |
| self.device = getattr( | |
| self.model, | |
| "device", | |
| torch.device("cuda" if torch.cuda.is_available() else "cpu"), | |
| ) | |
| # Safety: some tokenizers lack pad_token_id | |
| if getattr(self.tokenizer, "pad_token_id", None) is None: | |
| self.tokenizer.pad_token_id = self.tokenizer.eos_token_id | |
| logger.info(f"Device: {self.device} | eos_id: {self.tokenizer.eos_token_id}") | |
| self._system_text = ( | |
| "You are a Nigerian assistant that speaks PIDGIN ENGLISH. " | |
| "When asked 'how far', reply 'I dey o, how you dey?'. " | |
| "Always answer in Pidgin English." | |
| ) | |
| def _normalize_messages(self, messages: List[Dict[str, Any]]) -> List[Dict[str, Any]]: | |
| """Normalize OpenAI-style messages to block-style contents.""" | |
| norm = [] | |
| for m in messages: | |
| role = m.get("role", "user") | |
| content = m.get("content", "") | |
| # Convert content to block list format for internal processing | |
| if isinstance(content, str): | |
| content = _to_block_list(content) | |
| elif isinstance(content, list): | |
| content = _to_block_list(content) | |
| else: | |
| content = _to_block_list(str(content)) | |
| norm.append({"role": role, "content": content}) | |
| # Ensure a system message exists (prepend if missing) | |
| if not any(m.get("role") == "system" for m in norm): | |
| norm = [{"role": "system", "content": _to_block_list(self._system_text)}] + norm | |
| return norm | |
| def _build_model_inputs(self, messages: List[Dict[str, Any]]): | |
| normalized_messages = self._normalize_messages(messages) | |
| model_inputs = self.tokenizer.apply_chat_template( | |
| normalized_messages, | |
| add_generation_prompt=True, | |
| return_tensors="pt", | |
| tokenize=True, | |
| return_dict=True, | |
| ).to(self.device) | |
| return model_inputs | |
| def __call__(self, data: Dict[str, Any]) -> Dict[str, Any]: | |
| """ | |
| Process requests in either OpenAI or HF format and return OpenAI-style responses. | |
| Input format (OpenAI API - directly from client): | |
| { | |
| "model": "Ephraimmm/PIDGIN_gemma-3", | |
| "messages": [ | |
| {"role": "user", "content": "What is deep learning?"} | |
| ], | |
| "temperature": 0.7, | |
| "max_tokens": 256 | |
| } | |
| OR HF Inference Endpoint format (with inputs wrapper): | |
| { | |
| "inputs": { | |
| "model": "Ephraimmm/PIDGIN_gemma-3", | |
| "messages": [{"role": "user", "content": "What is deep learning?"}] | |
| } | |
| } | |
| Output format (OpenAI API): | |
| { | |
| "id": "chatcmpl-...", | |
| "object": "chat.completion", | |
| "created": 1234567890, | |
| "model": "Ephraimmm/PIDGIN_gemma-3", | |
| "choices": [{ | |
| "index": 0, | |
| "message": { | |
| "role": "assistant", | |
| "content": "Response text" | |
| }, | |
| "finish_reason": "stop" | |
| }], | |
| "usage": { | |
| "prompt_tokens": 10, | |
| "completion_tokens": 20, | |
| "total_tokens": 30 | |
| } | |
| } | |
| """ | |
| logger.info(f"Incoming request keys: {list(data.keys())}") | |
| logger.info(f"Full incoming data: {data}") | |
| # Handle different input formats | |
| # Case 1: HF sends {"inputs": "string"} - treat as user message | |
| if "inputs" in data and isinstance(data["inputs"], str): | |
| messages = [{"role": "user", "content": data["inputs"]}] | |
| model_name = "Ephraimmm/PIDGIN_gemma-3" | |
| params = data.get("parameters", {}) | |
| # Case 2: HF sends {"inputs": {...OpenAI format...}} | |
| elif "inputs" in data and isinstance(data["inputs"], dict): | |
| openai_data = data["inputs"] | |
| # Preserve any parameters at root level | |
| if "parameters" in data: | |
| for key, value in data["parameters"].items(): | |
| if key not in openai_data: | |
| openai_data[key] = value | |
| messages = openai_data.get("messages") | |
| model_name = openai_data.get("model", "Ephraimmm/PIDGIN_gemma-3") | |
| params = openai_data | |
| # Case 3: HF sends {"inputs": [messages array]} | |
| elif "inputs" in data and isinstance(data["inputs"], list): | |
| messages = data["inputs"] | |
| model_name = "Ephraimmm/PIDGIN_gemma-3" | |
| params = data.get("parameters", {}) | |
| # Case 4: Direct OpenAI format {"messages": [...]} | |
| elif "messages" in data: | |
| messages = data["messages"] | |
| model_name = data.get("model", "Ephraimmm/PIDGIN_gemma-3") | |
| params = data | |
| else: | |
| raise ValueError("Missing required field: 'messages' or 'inputs'") | |
| # Validate messages | |
| if not messages: | |
| raise ValueError("'messages' cannot be empty") | |
| messages = data["messages"] | |
| if not isinstance(messages, list): | |
| raise ValueError("'messages' must be a list of message objects") | |
| messages = data["messages"] | |
| if not isinstance(messages, list): | |
| raise ValueError("'messages' must be a list of message objects") | |
| # Extract OpenAI-style parameters with defaults | |
| max_tokens = int(params.get("max_tokens", 256)) | |
| temperature = float(params.get("temperature", 0.1)) | |
| top_p = float(params.get("top_p", 1.0)) | |
| top_k = params.get("top_k", None) | |
| stream = params.get("stream", False) # Not implemented yet | |
| presence_penalty = params.get("presence_penalty", 0.0) | |
| frequency_penalty = params.get("frequency_penalty", 0.0) | |
| # Build model inputs | |
| inputs = self._build_model_inputs(messages) | |
| # Prepare generation parameters | |
| gen_kwargs = dict( | |
| **inputs, | |
| max_new_tokens=max_tokens, | |
| temperature=temperature, | |
| top_p=top_p, | |
| do_sample=temperature > 0, # Use sampling if temperature > 0 | |
| use_cache=True, | |
| repetition_penalty=1.0 + frequency_penalty, # Map frequency_penalty | |
| eos_token_id=self.tokenizer.eos_token_id, | |
| pad_token_id=self.tokenizer.pad_token_id, | |
| ) | |
| if top_k is not None: | |
| gen_kwargs["top_k"] = int(top_k) | |
| # Generate response | |
| with torch.no_grad(): | |
| output_ids = self.model.generate(**gen_kwargs) | |
| # Extract only the generated tokens (exclude prompt) | |
| input_len = inputs["input_ids"].shape[-1] | |
| generated_ids = output_ids[0, input_len:] | |
| reply_text = self.tokenizer.decode(generated_ids, skip_special_tokens=True) | |
| # Calculate token usage | |
| prompt_tokens = int(input_len) | |
| completion_tokens = int(generated_ids.shape[-1]) | |
| total_tokens = prompt_tokens + completion_tokens | |
| # Return OpenAI-compatible response | |
| response = { | |
| "id": f"chatcmpl-{int(time.time() * 1000)}", | |
| "object": "chat.completion", | |
| "created": int(time.time()), | |
| "model": model_name, | |
| "choices": [ | |
| { | |
| "index": 0, | |
| "message": { | |
| "role": "assistant", | |
| "content": reply_text | |
| }, | |
| "finish_reason": "stop" | |
| } | |
| ], | |
| "usage": { | |
| "prompt_tokens": prompt_tokens, | |
| "completion_tokens": completion_tokens, | |
| "total_tokens": total_tokens | |
| } | |
| } | |
| return response | |
| if __name__ == "__main__": | |
| h = EndpointHandler(model_dir=".") | |
| # Test 1: OpenAI format (direct) | |
| print("=== Test 1: OpenAI Format (Direct) ===") | |
| response = h({ | |
| "model": "Ephraimmm/PIDGIN_gemma-3", | |
| "messages": [ | |
| {"role": "user", "content": "What is deep learning?"} | |
| ] | |
| }) | |
| print(response) | |
| print() | |
| # Test 2: HF Inference Endpoint format (with inputs wrapper) | |
| print("=== Test 2: HF Format (With inputs wrapper) ===") | |
| response = h({ | |
| "inputs": { | |
| "model": "Ephraimmm/PIDGIN_gemma-3", | |
| "messages": [ | |
| {"role": "user", "content": "How far?"} | |
| ], | |
| "temperature": 0.7, | |
| "max_tokens": 64 | |
| } | |
| }) | |
| print(response) | |
| print() | |
| # Test 3: Multi-turn conversation | |
| print("=== Test 3: Multi-turn Conversation ===") | |
| response = h({ | |
| "model": "Ephraimmm/PIDGIN_gemma-3", | |
| "messages": [ | |
| {"role": "system", "content": "You are a helpful assistant."}, | |
| {"role": "user", "content": "Explain why the sky is blue"}, | |
| {"role": "assistant", "content": "The sky dey blue because..."}, | |
| {"role": "user", "content": "Tell me more"} | |
| ], | |
| "max_tokens": 100 | |
| }) | |
| print(response) |