nmysore commited on
Commit
103f6c2
·
verified ·
1 Parent(s): 8d78a36

Upload handler.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. handler.py +192 -8
handler.py CHANGED
@@ -1,28 +1,212 @@
1
- """Minimal handler for debugging."""
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  import torch
4
  from transformers import AutoModelForCausalLM, AutoTokenizer
5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
  class EndpointHandler:
8
  def __init__(self, path):
9
  self.device = "cuda" if torch.cuda.is_available() else "cpu"
10
  dtype = torch.float16 if self.device == "cuda" else torch.float32
 
11
  self.tokenizer = AutoTokenizer.from_pretrained(path)
12
  self.model = AutoModelForCausalLM.from_pretrained(
13
  path, torch_dtype=dtype
14
  ).to(self.device)
15
  self.model.eval()
16
 
17
- def __call__(self, data):
18
- inputs = data.get("inputs", "test")
 
 
 
 
 
 
 
 
 
 
 
19
  prompt = (
20
  f"<start_of_turn>user\n"
21
- f"{inputs}<end_of_turn>\n"
22
  f"<start_of_turn>model\n"
23
  )
24
- tok = self.tokenizer(prompt, return_tensors="pt", truncation=True, max_length=512).to(self.device)
 
 
 
 
 
 
 
25
  with torch.no_grad():
26
- out = self.model.generate(**tok, max_new_tokens=100, pad_token_id=self.tokenizer.eos_token_id)
27
- text = self.tokenizer.decode(out[0][tok["input_ids"].shape[1]:], skip_special_tokens=True).strip()
28
- return {"generated_text": text}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Custom handler for HF Inference Endpoints.
2
+
3
+ Loads the pre-merged Gemma 2B IT model and exposes the same inference
4
+ logic as server.py — Gemma chat template, INSTRUCTION prompt, and
5
+ generation parameters.
6
+
7
+ Supports MC Dropout for Bayesian uncertainty estimation. Since the model
8
+ is pre-merged (no LoRA dropout layers), we inject DropoutWrapper modules
9
+ around attention projection layers at init time.
10
+ """
11
+
12
+ import os
13
+ import sys
14
 
15
  import torch
16
  from transformers import AutoModelForCausalLM, AutoTokenizer
17
 
18
+ # On HF Inference Endpoints, handler.py and mc_dropout.py are in the model
19
+ # repo root. Ensure that directory is on sys.path for all imports.
20
+ _handler_dir = os.path.dirname(os.path.abspath(__file__))
21
+ if _handler_dir not in sys.path:
22
+ sys.path.insert(0, _handler_dir)
23
+
24
+ # MC Dropout import — gracefully degrade if unavailable
25
+ _MC_AVAILABLE = False
26
+ try:
27
+ from mc_dropout import (
28
+ aggregate_beliefs,
29
+ disable_mc_dropout,
30
+ enable_mc_dropout,
31
+ inject_dropout,
32
+ )
33
+ _MC_AVAILABLE = True
34
+ except Exception as _mc_err:
35
+ # Try local development path
36
+ try:
37
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
38
+ from mc_dropout import (
39
+ aggregate_beliefs,
40
+ disable_mc_dropout,
41
+ enable_mc_dropout,
42
+ inject_dropout,
43
+ )
44
+ _MC_AVAILABLE = True
45
+ except Exception:
46
+ print(f"WARNING: MC Dropout not available: {_mc_err}")
47
+ aggregate_beliefs = None
48
+ disable_mc_dropout = None
49
+ enable_mc_dropout = None
50
+ inject_dropout = None
51
+
52
+ INSTRUCTION = """\
53
+ Narrative format: {timestamp} {hash8} {refs} {actors} :: {subject} | {sym}{file}+N/-N@{funcs} ...
54
+ Symbols: + added, ~ modified, - deleted, > renamed, = copied
55
+ Roles: (a+c) author+committer, (a) author, (c) committer
56
+ Funcs: @{name1,name2} for modified functions/classes
57
+ Merges: {timestamp} {hash8} MERGE {merged}→{main} :: {subject}
58
+ Renames: >{old}→{new}(N%)+N/-N Large commits: ...+Nmore when >50 files
59
+
60
+ Read the repository events below. Identify recurring patterns in workflow, code ownership, commit discipline, and architecture.
61
+
62
+ For each pattern found, output:
63
+ Belief: <clear statement about a development practice or pattern>
64
+ Evidence:
65
+ - Commit: <hash(es) that support this belief>
66
+ - Files: <file paths involved>
67
+ - Pattern: <what pattern was observed and why it matters>
68
+ Confidence: high | medium | low"""
69
+
70
+ CHUNK_SIZE = 4
71
+
72
 
73
  class EndpointHandler:
74
  def __init__(self, path):
75
  self.device = "cuda" if torch.cuda.is_available() else "cpu"
76
  dtype = torch.float16 if self.device == "cuda" else torch.float32
77
+
78
  self.tokenizer = AutoTokenizer.from_pretrained(path)
79
  self.model = AutoModelForCausalLM.from_pretrained(
80
  path, torch_dtype=dtype
81
  ).to(self.device)
82
  self.model.eval()
83
 
84
+ # Inject dropout wrappers for MC Dropout support on merged model
85
+ self.mc_dropout_available = False
86
+ if _MC_AVAILABLE and inject_dropout is not None:
87
+ mc_dropout_rate = float(os.environ.get("MC_DROPOUT_RATE", "0.1"))
88
+ n_injected = inject_dropout(self.model, dropout_rate=mc_dropout_rate)
89
+ print(f"Injected {n_injected} dropout wrappers (rate={mc_dropout_rate})")
90
+ self.mc_dropout_available = n_injected > 0
91
+ else:
92
+ print("MC Dropout not available — running in standard mode")
93
+
94
+ def _run_inference(self, text):
95
+ """Run inference on a single text chunk using Gemma chat template."""
96
+ user_content = f"{INSTRUCTION}\n\n{text}"
97
  prompt = (
98
  f"<start_of_turn>user\n"
99
+ f"{user_content}<end_of_turn>\n"
100
  f"<start_of_turn>model\n"
101
  )
102
+
103
+ inputs = self.tokenizer(
104
+ prompt,
105
+ return_tensors="pt",
106
+ truncation=True,
107
+ max_length=2048,
108
+ ).to(self.device)
109
+
110
  with torch.no_grad():
111
+ outputs = self.model.generate(
112
+ **inputs,
113
+ max_new_tokens=400,
114
+ do_sample=False,
115
+ pad_token_id=self.tokenizer.eos_token_id,
116
+ eos_token_id=[self.tokenizer.eos_token_id, 107],
117
+ )
118
+
119
+ generated = self.tokenizer.decode(
120
+ outputs[0][inputs["input_ids"].shape[1] :],
121
+ skip_special_tokens=True,
122
+ ).strip()
123
+
124
+ return generated
125
+
126
+ @staticmethod
127
+ def _chunk_narrative(narrative, chunk_size=CHUNK_SIZE):
128
+ """Split narrative into non-overlapping chunks."""
129
+ lines = [
130
+ line.rstrip("\n") for line in narrative.splitlines() if line.strip()
131
+ ]
132
+ chunks = []
133
+ for i in range(0, len(lines), chunk_size):
134
+ chunk_lines = lines[i : i + chunk_size]
135
+ if chunk_lines:
136
+ chunks.append("\n".join(chunk_lines))
137
+ return chunks
138
+
139
+ def _run_mc_inference(self, text, n_passes):
140
+ """Run MC Dropout inference: N passes with dropout enabled."""
141
+ user_content = f"{INSTRUCTION}\n\n{text}"
142
+ prompt = (
143
+ f"<start_of_turn>user\n"
144
+ f"{user_content}<end_of_turn>\n"
145
+ f"<start_of_turn>model\n"
146
+ )
147
+
148
+ enable_mc_dropout(self.model)
149
+ pass_texts = []
150
+ for _ in range(n_passes):
151
+ inputs = self.tokenizer(
152
+ prompt,
153
+ return_tensors="pt",
154
+ truncation=True,
155
+ max_length=2048,
156
+ ).to(self.device)
157
+
158
+ with torch.no_grad():
159
+ outputs = self.model.generate(
160
+ **inputs,
161
+ max_new_tokens=400,
162
+ temperature=0.7,
163
+ do_sample=True,
164
+ top_p=0.9,
165
+ pad_token_id=self.tokenizer.eos_token_id,
166
+ eos_token_id=[self.tokenizer.eos_token_id, 107],
167
+ )
168
+ generated = self.tokenizer.decode(
169
+ outputs[0][inputs["input_ids"].shape[1]:],
170
+ skip_special_tokens=True,
171
+ ).strip()
172
+ pass_texts.append(generated)
173
+
174
+ disable_mc_dropout(self.model)
175
+ return aggregate_beliefs(pass_texts, n_passes)
176
+
177
+ def __call__(self, data):
178
+ inputs = data.get("inputs", "")
179
+ parameters = data.get("parameters", {})
180
+ mode = parameters.get("mode", "predict")
181
+ mc_passes = int(parameters.get("mc_passes", 0))
182
+
183
+ # Diagnostic: return handler info when requested
184
+ if parameters.get("info"):
185
+ return {
186
+ "handler_version": "2.0-mc",
187
+ "mc_dropout_available": self.mc_dropout_available,
188
+ "device": str(self.device),
189
+ }
190
+
191
+ if mode == "batch":
192
+ chunks = self._chunk_narrative(inputs)
193
+ results = []
194
+ for i, chunk in enumerate(chunks):
195
+ if mc_passes > 0 and self.mc_dropout_available:
196
+ mc_beliefs = self._run_mc_inference(chunk, mc_passes)
197
+ results.append({
198
+ "chunk_index": i,
199
+ "mc_beliefs": mc_beliefs,
200
+ "mc_passes": mc_passes,
201
+ })
202
+ else:
203
+ generated = self._run_inference(chunk)
204
+ results.append({"chunk_index": i, "generated_text": generated})
205
+ return {"total_chunks": len(chunks), "results": results}
206
+
207
+ if mc_passes > 0 and self.mc_dropout_available:
208
+ mc_beliefs = self._run_mc_inference(inputs, mc_passes)
209
+ return {"mc_beliefs": mc_beliefs, "mc_passes": mc_passes}
210
+
211
+ generated = self._run_inference(inputs)
212
+ return {"generated_text": generated}