File size: 3,232 Bytes
d9d9db5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
"""Custom Hugging Face Inference Endpoints handler for ProCreations/grug-27b.

Deploy: create an Inference Endpoint from this repo with a GPU holding >=60GB
VRAM for bf16 (1x A100-80GB / H100 / H200). The endpoint auto-detects this
handler.

Request formats accepted:
  {"inputs": "plain user prompt", "parameters": {...}}
  {"inputs": {"messages": [{"role": "user", "content": "..."}],
              "tools": [...optional OpenAI-style tool schemas...]},
   "parameters": {"max_new_tokens": 1024, "temperature": 0.6, ...}}

Response:
  [{"generated_text": "<full raw completion>",
    "think": "<grug reasoning>", "answer": "<final answer>"}]
"""
from typing import Any, Dict, List

import torch
from transformers import AutoModelForImageTextToText, AutoTokenizer

DEFAULTS = {"max_new_tokens": 1024, "temperature": 0.6, "top_p": 0.95, "top_k": 20,
            "do_sample": True}
MAX_NEW_TOKENS_CAP = 8192


class EndpointHandler:
    def __init__(self, path: str = ""):
        self.tokenizer = AutoTokenizer.from_pretrained(path)
        self.model = AutoModelForImageTextToText.from_pretrained(
            path, dtype=torch.bfloat16, device_map="auto",
            attn_implementation="sdpa")
        self.model.eval()

    def __call__(self, data: Dict[str, Any]) -> List[Dict[str, str]]:
        inputs = data.get("inputs", "")
        params = dict(DEFAULTS)
        params.update(data.get("parameters") or {})
        params["max_new_tokens"] = min(int(params.get("max_new_tokens", 1024)),
                                       MAX_NEW_TOKENS_CAP)
        if params.get("temperature", 1.0) <= 0:
            params["do_sample"] = False
            params.pop("temperature", None)
            params.pop("top_p", None)
            params.pop("top_k", None)

        tools = None
        if isinstance(inputs, dict):
            messages = inputs.get("messages") or [
                {"role": "user", "content": str(inputs.get("text", ""))}]
            tools = inputs.get("tools") or None
        elif isinstance(inputs, list):
            messages = inputs
        else:
            messages = [{"role": "user", "content": str(inputs)}]

        prompt = self.tokenizer.apply_chat_template(
            messages, tools=tools, add_generation_prompt=True, tokenize=False)
        enc = self.tokenizer(prompt, return_tensors="pt").to(self.model.device)
        gen_kwargs = {k: v for k, v in params.items()
                      if k in ("max_new_tokens", "temperature", "top_p", "top_k",
                               "do_sample", "repetition_penalty", "seed")}
        gen_kwargs.pop("seed", None)
        with torch.inference_mode():
            out = self.model.generate(**enc, **gen_kwargs,
                                      pad_token_id=self.tokenizer.pad_token_id)
        text = self.tokenizer.decode(out[0][enc["input_ids"].shape[1]:],
                                     skip_special_tokens=True)
        if "</think>" in text:
            think, answer = text.split("</think>", 1)
            think = think.replace("<think>", "").strip()
            answer = answer.strip()
        else:
            think, answer = "", text.strip()
        return [{"generated_text": text, "think": think, "answer": answer}]