fix: memmap mode=r, tool parser json.loads, greedy decode
This commit is contained in:
@@ -7,6 +7,7 @@ Subclasses may optionally consume ``token_ids`` for token-level parsing
|
|||||||
(e.g. Harmony / VLM-style parsers).
|
(e.g. Harmony / VLM-style parsers).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
import re
|
import re
|
||||||
import uuid
|
import uuid
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
@@ -117,6 +118,29 @@ def _parse_tool_call_json(json_str: str, complete: bool):
|
|||||||
|
|
||||||
Returns ``(name, args, valid)``.
|
Returns ``(name, args, valid)``.
|
||||||
"""
|
"""
|
||||||
|
if complete:
|
||||||
|
try:
|
||||||
|
obj = json.loads(json_str)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return None, "", False
|
||||||
|
name = obj.get("name")
|
||||||
|
if not isinstance(name, str) or not name:
|
||||||
|
return None, "", False
|
||||||
|
args = obj.get("arguments")
|
||||||
|
if isinstance(args, dict):
|
||||||
|
if not args:
|
||||||
|
args = ""
|
||||||
|
else:
|
||||||
|
args = json.dumps(args, ensure_ascii=False)
|
||||||
|
args = args[1:-1].rstrip()
|
||||||
|
elif isinstance(args, list):
|
||||||
|
args = json.dumps(args, ensure_ascii=False) if args else ""
|
||||||
|
elif isinstance(args, str):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
args = str(args) if args is not None else ""
|
||||||
|
return name, args, True
|
||||||
|
|
||||||
name_match = re.search(r'"name"\s*:\s*"([^"]*)"', json_str)
|
name_match = re.search(r'"name"\s*:\s*"([^"]*)"', json_str)
|
||||||
if not name_match:
|
if not name_match:
|
||||||
return None, "", False
|
return None, "", False
|
||||||
@@ -127,8 +151,6 @@ def _parse_tool_call_json(json_str: str, complete: bool):
|
|||||||
return name, "", True
|
return name, "", True
|
||||||
|
|
||||||
raw = args_match.group(1).rstrip()
|
raw = args_match.group(1).rstrip()
|
||||||
if complete and raw.endswith("}"):
|
|
||||||
raw = raw[:-1].rstrip()
|
|
||||||
if raw.startswith("{"):
|
if raw.startswith("{"):
|
||||||
inner = raw[1:].rstrip()
|
inner = raw[1:].rstrip()
|
||||||
if inner.endswith("}"):
|
if inner.endswith("}"):
|
||||||
@@ -156,9 +178,6 @@ def _find_tool_calls(text: str, start_pos: int = 0):
|
|||||||
break
|
break
|
||||||
|
|
||||||
json_str = text[brace:end]
|
json_str = text[brace:end]
|
||||||
if not _TOOL_CALL_HEAD_RE.search(json_str):
|
|
||||||
pos = end
|
|
||||||
continue
|
|
||||||
|
|
||||||
name, args, valid = _parse_tool_call_json(json_str, complete=True)
|
name, args, valid = _parse_tool_call_json(json_str, complete=True)
|
||||||
if not valid or name is None:
|
if not valid or name is None:
|
||||||
@@ -186,7 +205,7 @@ def _find_partial_tool_call(text: str, start_pos: int = 0):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
json_str = text[brace:]
|
json_str = text[brace:]
|
||||||
if not _TOOL_CALL_HEAD_RE.search(json_str):
|
if '"name"' not in json_str:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
name, args, valid = _parse_tool_call_json(json_str, complete=False)
|
name, args, valid = _parse_tool_call_json(json_str, complete=False)
|
||||||
|
|||||||
@@ -82,8 +82,8 @@ class GenerationRequest:
|
|||||||
raise ValueError("top_k must be a non-negative integer")
|
raise ValueError("top_k must be a non-negative integer")
|
||||||
if not (0.0 <= top_p <= 1.0):
|
if not (0.0 <= top_p <= 1.0):
|
||||||
raise ValueError("top_p must be a float between 0.0 and 1.0")
|
raise ValueError("top_p must be a float between 0.0 and 1.0")
|
||||||
if not (isinstance(temperature, (int, float)) and temperature > 0):
|
if not (isinstance(temperature, (int, float)) and temperature >= 0):
|
||||||
raise ValueError("temperature must be a positive number")
|
raise ValueError("temperature must be a non-negative number")
|
||||||
if not (
|
if not (
|
||||||
isinstance(frequency_penalty, (int, float))
|
isinstance(frequency_penalty, (int, float))
|
||||||
and -2.0 <= frequency_penalty <= 2.0
|
and -2.0 <= frequency_penalty <= 2.0
|
||||||
|
|||||||
@@ -263,6 +263,12 @@ class SamplingPipeline(BaseSamplingStrategy):
|
|||||||
logits = strategy.apply(logits, filter_value, input_ids, input_mask)
|
logits = strategy.apply(logits, filter_value, input_ids, input_mask)
|
||||||
return logits
|
return logits
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _is_greedy(temperature: Union[float, Tensor]) -> bool:
|
||||||
|
if isinstance(temperature, Tensor):
|
||||||
|
return temperature.numel() == 1 and temperature.item() == 0
|
||||||
|
return temperature == 0
|
||||||
|
|
||||||
@torch.inference_mode()
|
@torch.inference_mode()
|
||||||
def sample(
|
def sample(
|
||||||
self,
|
self,
|
||||||
@@ -273,6 +279,9 @@ class SamplingPipeline(BaseSamplingStrategy):
|
|||||||
) -> Tensor:
|
) -> Tensor:
|
||||||
"""Apply strategies then sample (softmax + multinomial).
|
"""Apply strategies then sample (softmax + multinomial).
|
||||||
|
|
||||||
|
Short-circuits to ``argmax`` when temperature is exactly 0
|
||||||
|
(deterministic / greedy decode).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
logits: Raw logits ``[batch, vocab_size]``.
|
logits: Raw logits ``[batch, vocab_size]``.
|
||||||
input_ids: Previously generated token IDs ``[batch, seq_len]``.
|
input_ids: Previously generated token IDs ``[batch, seq_len]``.
|
||||||
@@ -281,6 +290,11 @@ class SamplingPipeline(BaseSamplingStrategy):
|
|||||||
Returns:
|
Returns:
|
||||||
Sampled token IDs ``[batch]``.
|
Sampled token IDs ``[batch]``.
|
||||||
"""
|
"""
|
||||||
|
for s in self.strategies:
|
||||||
|
if isinstance(s, TemperatureStrategy) and self._is_greedy(s.temperature):
|
||||||
|
return logits.argmax(dim=-1)
|
||||||
|
break
|
||||||
|
|
||||||
return torch.multinomial(
|
return torch.multinomial(
|
||||||
torch.softmax(
|
torch.softmax(
|
||||||
self.apply(logits, filter_value, input_ids, input_mask), dim=-1
|
self.apply(logits, filter_value, input_ids, input_mask), dim=-1
|
||||||
@@ -304,6 +318,9 @@ def sample(
|
|||||||
|
|
||||||
Shortcut for ``SamplingPipeline(...).sample(logits)``.
|
Shortcut for ``SamplingPipeline(...).sample(logits)``.
|
||||||
|
|
||||||
|
When **temperature** is exactly 0 (scalar or single-element tensor)
|
||||||
|
the function short-circuits to ``argmax`` for deterministic decode.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
logits: Raw logits ``[batch, vocab_size]``.
|
logits: Raw logits ``[batch, vocab_size]``.
|
||||||
frequency_penalty: Penalty per occurrence for repeated tokens
|
frequency_penalty: Penalty per occurrence for repeated tokens
|
||||||
@@ -314,6 +331,8 @@ def sample(
|
|||||||
Returns:
|
Returns:
|
||||||
Sampled token IDs ``[batch]``.
|
Sampled token IDs ``[batch]``.
|
||||||
"""
|
"""
|
||||||
|
if SamplingPipeline._is_greedy(temperature):
|
||||||
|
return logits.argmax(dim=-1)
|
||||||
return SamplingPipeline(
|
return SamplingPipeline(
|
||||||
[
|
[
|
||||||
TemperatureStrategy(temperature),
|
TemperatureStrategy(temperature),
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ def load_bin(file_path: str) -> Dict[str, List[Tensor]]:
|
|||||||
arr = np.memmap(
|
arr = np.memmap(
|
||||||
os.path.join(file_path, f"{key}.bin"),
|
os.path.join(file_path, f"{key}.bin"),
|
||||||
dtype=info["dtype"],
|
dtype=info["dtype"],
|
||||||
mode="r+",
|
mode="r",
|
||||||
shape=tuple(info["shape"]),
|
shape=tuple(info["shape"]),
|
||||||
)
|
)
|
||||||
segments[key] = [torch.from_numpy(arr)]
|
segments[key] = [torch.from_numpy(arr)]
|
||||||
|
|||||||
Reference in New Issue
Block a user