ThomsenDrake commited on
Commit
9bb7b23
·
verified ·
1 Parent(s): 9fb7868

Handle Transformers BatchEncoding in ZeroGPU runtime

Browse files
figment/zerogpu_runtime.py CHANGED
@@ -2,6 +2,7 @@
2
 
3
  from __future__ import annotations
4
 
 
5
  import json
6
  import os
7
  import threading
@@ -91,17 +92,26 @@ class _ZeroGpuRuntime:
91
  def generate_json(self, prompt: str) -> dict[str, Any]:
92
  messages = [{"role": "user", "content": prompt}]
93
  with self.lock:
94
- input_ids = self.tokenizer.apply_chat_template(
95
  messages,
96
  add_generation_prompt=True,
97
  return_tensors="pt",
98
  )
99
  device = next(self.model.parameters()).device
 
 
 
 
 
 
100
  input_ids = input_ids.to(device)
 
 
 
 
101
  input_len = int(input_ids.shape[-1])
102
  available_tokens = max(1, self.max_context_tokens - input_len - 8)
103
  max_new_tokens = max(1, min(self.max_generation_tokens, available_tokens))
104
- attention_mask = self.torch.ones_like(input_ids)
105
  with self.torch.inference_mode():
106
  output_ids = self.model.generate(
107
  input_ids=input_ids,
 
2
 
3
  from __future__ import annotations
4
 
5
+ from collections.abc import Mapping
6
  import json
7
  import os
8
  import threading
 
92
  def generate_json(self, prompt: str) -> dict[str, Any]:
93
  messages = [{"role": "user", "content": prompt}]
94
  with self.lock:
95
+ encoded = self.tokenizer.apply_chat_template(
96
  messages,
97
  add_generation_prompt=True,
98
  return_tensors="pt",
99
  )
100
  device = next(self.model.parameters()).device
101
+ if isinstance(encoded, Mapping):
102
+ input_ids = encoded["input_ids"]
103
+ attention_mask = encoded.get("attention_mask")
104
+ else:
105
+ input_ids = encoded
106
+ attention_mask = None
107
  input_ids = input_ids.to(device)
108
+ if attention_mask is None:
109
+ attention_mask = self.torch.ones_like(input_ids)
110
+ else:
111
+ attention_mask = attention_mask.to(device)
112
  input_len = int(input_ids.shape[-1])
113
  available_tokens = max(1, self.max_context_tokens - input_len - 8)
114
  max_new_tokens = max(1, min(self.max_generation_tokens, available_tokens))
 
115
  with self.torch.inference_mode():
116
  output_ids = self.model.generate(
117
  input_ids=input_ids,
tests/test_zerogpu_runtime.py CHANGED
@@ -62,3 +62,84 @@ def test_zerogpu_runtime_uses_native_transformers_with_mamba_kernels_disabled(mo
62
  assert calls["model"]["kwargs"]["torch_dtype"] == "bf16"
63
  assert calls["device"] == "cuda"
64
  assert calls["eval"] is True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  assert calls["model"]["kwargs"]["torch_dtype"] == "bf16"
63
  assert calls["device"] == "cuda"
64
  assert calls["eval"] is True
65
+
66
+
67
+ def test_zerogpu_runtime_generate_json_accepts_batch_encoding_tokenizer_output() -> None:
68
+ calls: dict[str, Any] = {}
69
+
70
+ class FakeTensor:
71
+ def __init__(self, shape: tuple[int, int]) -> None:
72
+ self.shape = shape
73
+
74
+ def to(self, device: str) -> "FakeTensor":
75
+ calls.setdefault("moved_to", []).append(device)
76
+ return self
77
+
78
+ class FakeGeneratedIds:
79
+ def __getitem__(self, key: Any) -> list[int]:
80
+ calls["generated_slice"] = key
81
+ return [4, 5, 6]
82
+
83
+ class FakeTokenizer:
84
+ pad_token_id = 0
85
+ eos_token_id = 2
86
+
87
+ def apply_chat_template(self, *_: Any, **__: Any) -> dict[str, FakeTensor]:
88
+ return {
89
+ "input_ids": FakeTensor((1, 3)),
90
+ "attention_mask": FakeTensor((1, 3)),
91
+ }
92
+
93
+ def decode(self, generated_ids: list[int], *, skip_special_tokens: bool) -> str:
94
+ calls["decoded"] = {"ids": generated_ids, "skip_special_tokens": skip_special_tokens}
95
+ return '{"protocol_urgency": "monitor", "source_cards": []}'
96
+
97
+ class FakeTorch:
98
+ @staticmethod
99
+ def ones_like(tensor: FakeTensor) -> FakeTensor:
100
+ calls["ones_like"] = tensor
101
+ return FakeTensor(tensor.shape)
102
+
103
+ @staticmethod
104
+ def inference_mode() -> Any:
105
+ class Context:
106
+ def __enter__(self) -> None:
107
+ return None
108
+
109
+ def __exit__(self, *_: Any) -> None:
110
+ return None
111
+
112
+ return Context()
113
+
114
+ class FakeModel:
115
+ def parameters(self) -> Any:
116
+ return iter([SimpleNamespace(device="cuda")])
117
+
118
+ def generate(self, **kwargs: Any) -> FakeGeneratedIds:
119
+ calls["generate"] = kwargs
120
+ return FakeGeneratedIds()
121
+
122
+ class FakeLock:
123
+ def __enter__(self) -> None:
124
+ return None
125
+
126
+ def __exit__(self, *_: Any) -> None:
127
+ return None
128
+
129
+ runtime = object.__new__(_ZeroGpuRuntime)
130
+ runtime.model_id = "model"
131
+ runtime.max_context_tokens = 32
132
+ runtime.max_generation_tokens = 8
133
+ runtime.lock = FakeLock()
134
+ runtime.torch = FakeTorch()
135
+ runtime.tokenizer = FakeTokenizer()
136
+ runtime.model = FakeModel()
137
+
138
+ result = runtime.generate_json("Return JSON.")
139
+
140
+ assert result == {"protocol_urgency": "monitor", "source_cards": []}
141
+ assert calls["moved_to"] == ["cuda", "cuda"]
142
+ assert calls["generate"]["input_ids"].shape == (1, 3)
143
+ assert calls["generate"]["attention_mask"].shape == (1, 3)
144
+ assert calls["generated_slice"] == (0, slice(3, None, None))
145
+ assert calls["decoded"] == {"ids": [4, 5, 6], "skip_special_tokens": True}