Spaces:
Running on Zero
Running on Zero
Handle Transformers BatchEncoding in ZeroGPU runtime
Browse files- figment/zerogpu_runtime.py +12 -2
- tests/test_zerogpu_runtime.py +81 -0
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 |
-
|
| 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}
|