Text Classification
MLX
English
decision-model
calibration
lora
activated-lora
adaptive-reasoning
on-device
Instructions to use suhaas-teja/Dev-4B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use suhaas-teja/Dev-4B with MLX:
# Download the model from the Hub pip install huggingface_hub[hf_xet] hf download suhaas-teja/Dev-4B --local-dir Dev-4B
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- Atomic Chat
Engine: streaming auto and chat
Browse files- dmm/engine.py +67 -1
dmm/engine.py
CHANGED
|
@@ -154,9 +154,13 @@ class Engine:
|
|
| 154 |
# Generation --------------------------------------------------------------------------
|
| 155 |
|
| 156 |
@torch.no_grad()
|
| 157 |
-
def _generate(
|
|
|
|
|
|
|
| 158 |
x = torch.tensor([ids], device=self.model.device)
|
| 159 |
kw = {"past_key_values": cache.kv} if cache is not None else {}
|
|
|
|
|
|
|
| 160 |
with lora.disabled(self.model):
|
| 161 |
out = self.model.generate(
|
| 162 |
x,
|
|
@@ -168,6 +172,68 @@ class Engine:
|
|
| 168 |
)
|
| 169 |
return out[0, len(ids) :].tolist()
|
| 170 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
def chat(self, messages: list[dict], max_new_tokens: int = 512) -> list[int]:
|
| 172 |
ids = self.tok.apply_chat_template(
|
| 173 |
messages, add_generation_prompt=True, tokenize=True, return_dict=True
|
|
|
|
| 154 |
# Generation --------------------------------------------------------------------------
|
| 155 |
|
| 156 |
@torch.no_grad()
|
| 157 |
+
def _generate(
|
| 158 |
+
self, ids: list[int], max_new_tokens: int, cache: StateCache | None = None, streamer=None
|
| 159 |
+
):
|
| 160 |
x = torch.tensor([ids], device=self.model.device)
|
| 161 |
kw = {"past_key_values": cache.kv} if cache is not None else {}
|
| 162 |
+
if streamer is not None:
|
| 163 |
+
kw["streamer"] = streamer
|
| 164 |
with lora.disabled(self.model):
|
| 165 |
out = self.model.generate(
|
| 166 |
x,
|
|
|
|
| 172 |
)
|
| 173 |
return out[0, len(ids) :].tolist()
|
| 174 |
|
| 175 |
+
def _stream(self, ids: list[int], max_new_tokens: int, cache: StateCache | None = None):
|
| 176 |
+
"""Generate as _generate does, yielding {"event": "text"} pieces as they decode.
|
| 177 |
+
|
| 178 |
+
Returns the generated token ids (the generator's return value). Generation runs in a
|
| 179 |
+
thread feeding a streamer; the thread is always joined, even if the caller stops early,
|
| 180 |
+
so the model and the state cache are never touched by two requests at once.
|
| 181 |
+
"""
|
| 182 |
+
from transformers import TextIteratorStreamer
|
| 183 |
+
|
| 184 |
+
streamer = TextIteratorStreamer(self.tok, skip_prompt=True, skip_special_tokens=True)
|
| 185 |
+
result: dict = {}
|
| 186 |
+
|
| 187 |
+
def run():
|
| 188 |
+
try:
|
| 189 |
+
result["tokens"] = self._generate(ids, max_new_tokens, cache, streamer)
|
| 190 |
+
except BaseException as e: # surface in the caller's thread
|
| 191 |
+
result["error"] = e
|
| 192 |
+
streamer.end()
|
| 193 |
+
|
| 194 |
+
t = threading.Thread(target=run, daemon=True)
|
| 195 |
+
t.start()
|
| 196 |
+
try:
|
| 197 |
+
for piece in streamer:
|
| 198 |
+
if piece:
|
| 199 |
+
yield {"event": "text", "text": piece}
|
| 200 |
+
finally:
|
| 201 |
+
t.join()
|
| 202 |
+
if "error" in result:
|
| 203 |
+
raise result["error"]
|
| 204 |
+
return result["tokens"]
|
| 205 |
+
|
| 206 |
+
def chat_stream(self, messages: list[dict], max_new_tokens: int = 512):
|
| 207 |
+
"""chat(), streamed: {"event": "text"} pieces, then {"event": "done", "tokens": [...]}."""
|
| 208 |
+
ids = self.tok.apply_chat_template(
|
| 209 |
+
messages, add_generation_prompt=True, tokenize=True, return_dict=True
|
| 210 |
+
)["input_ids"]
|
| 211 |
+
with self.lock:
|
| 212 |
+
tokens = yield from self._stream(ids, max_new_tokens)
|
| 213 |
+
yield {"event": "done", "tokens": tokens}
|
| 214 |
+
|
| 215 |
+
def auto_stream(self, state: str, name: str, question: dict, max_new_tokens: int = 512):
|
| 216 |
+
"""auto(), streamed. Yields {"event": "decision", ...} as soon as the quick decision and
|
| 217 |
+
the router's call are known, then {"event": "text"} reasoning pieces when escalated, then
|
| 218 |
+
{"event": "done", ...} carrying exactly what auto() returns."""
|
| 219 |
+
item = to_item(state, name, question)
|
| 220 |
+
with self.lock:
|
| 221 |
+
cache = StateCache(self.model, self.tpl.state_ids(item))
|
| 222 |
+
r = self._decide_on(cache, item)
|
| 223 |
+
out = {
|
| 224 |
+
"decision": self._answer(item, r),
|
| 225 |
+
"escalated": r["escalate"],
|
| 226 |
+
"state_prefills": 1,
|
| 227 |
+
}
|
| 228 |
+
yield {"event": "decision", **out}
|
| 229 |
+
if r["escalate"]:
|
| 230 |
+
ids = cache.ids + self.tpl.encode(chat_text(item))
|
| 231 |
+
tokens = yield from self._stream(ids, max_new_tokens, cache)
|
| 232 |
+
out["generation_tokens"] = tokens
|
| 233 |
+
out["generation"] = self.tok.decode(tokens, skip_special_tokens=True)
|
| 234 |
+
cache.kv.crop(len(cache))
|
| 235 |
+
yield {"event": "done", **out}
|
| 236 |
+
|
| 237 |
def chat(self, messages: list[dict], max_new_tokens: int = 512) -> list[int]:
|
| 238 |
ids = self.tok.apply_chat_template(
|
| 239 |
messages, add_generation_prompt=True, tokenize=True, return_dict=True
|