| """ |
| handler.py β HuggingFace Inference Endpoint handler for SriRamanaAtmic/AtmicIntelv1 |
| Compatible with transformers==4.51.3 (matches model's transformers_version in config.json). |
| |
| Generation parameters (from Section A4 of technical review β do not change): |
| do_sample = False (greedy decoding β matches SFT + DPO training exactly) |
| max_new_tokens = 350 |
| repetition_penalty = 1.01 (sole repetition control) |
| no_repeat_ngram_size = 0 (PERMANENTLY DISABLED β hard-coded, not overridable via API) |
| temperature / top_p (REMOVED β inactive under greedy decoding) |
| |
| Token IDs (from added_tokens.json β verified): |
| <|endoftext|> = 32000 (pad_token_id) |
| <|assistant|> = 32001 (appears in INPUT prompt β must NEVER be eos_token_id) |
| <|end|> = 32007 (turn terminator β correct eos for generation) |
| |
| Critical: generation_config.json in the repo contains eos_token_id=[32000, 32001, 32007]. |
| Token 32001 (<|assistant|>) is present in every input prompt, causing generation to stop |
| at token 0. This handler explicitly overrides generation_config.json by setting |
| self.model.generation_config before any generate() call. |
| |
| Input contract: |
| The caller (pipeline.py via prompt_builder.py) sends a fully-formatted Phi-3 prompt string. |
| This handler does NOT apply any chat template β prompt arrives ready to tokenize. |
| {"inputs": "<|system|>...<|end|>\n<|user|>...<|end|>\n<|assistant|>\n"} |
| """ |
|
|
| |
| |
| import transformers.cache_utils as _cu |
| if not hasattr(_cu.DynamicCache, "get_max_length"): |
| _cu.DynamicCache.get_max_length = lambda self: None |
|
|
| from transformers import DynamicCache |
| if not hasattr(DynamicCache, "get_max_length"): |
| DynamicCache.get_max_length = lambda self: None |
| |
|
|
| from transformers import AutoTokenizer, AutoModelForCausalLM, GenerationConfig |
| import torch |
|
|
|
|
| class EndpointHandler: |
| def __init__(self, path=""): |
| |
| self.tokenizer = AutoTokenizer.from_pretrained( |
| path, |
| trust_remote_code=True, |
| ) |
|
|
| |
| self.model = AutoModelForCausalLM.from_pretrained( |
| path, |
| torch_dtype=torch.bfloat16, |
| device_map="auto", |
| trust_remote_code=True, |
| attn_implementation="eager", |
| ) |
| self.model.eval() |
|
|
| |
| |
| |
| |
| |
| self.model.generation_config = GenerationConfig( |
| do_sample=False, |
| repetition_penalty=1.01, |
| no_repeat_ngram_size=0, |
| eos_token_id=32007, |
| pad_token_id=32000, |
| bos_token_id=1, |
| ) |
|
|
| def __call__(self, data: dict) -> list: |
| |
| inputs = data.get("inputs", "") |
| parameters = data.get("parameters", {}) |
|
|
| max_new_tokens = int(parameters.get("max_new_tokens", 350)) |
| repetition_penalty = float(parameters.get("repetition_penalty", 1.15)) |
|
|
| |
| tokenized = self.tokenizer( |
| inputs, |
| return_tensors="pt", |
| truncation=True, |
| max_length=3500, |
| add_special_tokens=False, |
| ).to(self.model.device) |
|
|
| input_length = tokenized["input_ids"].shape[1] |
|
|
| |
| |
| |
| with torch.inference_mode(): |
| output = self.model.generate( |
| **tokenized, |
| max_new_tokens=max_new_tokens, |
| repetition_penalty=repetition_penalty, |
| do_sample=False, |
| no_repeat_ngram_size=0, |
| eos_token_id=32007, |
| pad_token_id=32000, |
| ) |
|
|
| |
| new_tokens = output[0][input_length:] |
| generated_text = self.tokenizer.decode(new_tokens, skip_special_tokens=True) |
| return [{"generated_text": generated_text}] |