Meanblock's picture
Add JEV-CPU: CPU port of SemIf + web UI
7845694 verified
Raw
History Blame Contribute Delete
3.68 kB
"""Create-only JSONL command line scorer."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from .core import load_causal_model, validate_row
from .direct import score as direct_score
from .reranker import score as reranker_score
from .serial import SerialPrefixScorer
from .shared import score_shared
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--mode", choices=("direct", "serial", "shared", "reranker"), required=True)
parser.add_argument("--backend", choices=("torch", "mlx"), default="torch")
parser.add_argument("--mlx-bits", type=int, choices=(4, 8), help="Quantize MLX weights in memory; default preserves source precision")
parser.add_argument("--mlx-cache-limit-mib", type=int,
help="MLX inactive allocation cache in MiB (default: 256; 0 disables caching)")
parser.add_argument("--model", required=True)
parser.add_argument("--revision", required=True)
parser.add_argument("--input", type=Path, required=True)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--max-tokens", type=int, default=4096)
args = parser.parse_args()
if args.output.exists() or args.max_tokens < 1:
parser.error("Output must be new and max-tokens must be positive")
if args.mlx_bits and args.backend != "mlx":
parser.error("--mlx-bits requires --backend mlx")
if args.mlx_cache_limit_mib is not None:
if args.backend != "mlx":
parser.error("--mlx-cache-limit-mib requires --backend mlx")
if args.mlx_cache_limit_mib < 0:
parser.error("--mlx-cache-limit-mib must be nonnegative")
if args.backend == "mlx" and args.mode == "reranker":
parser.error("MLX supports direct, serial, and shared modes; reranker requires torch")
rows = [json.loads(line) for line in args.input.read_text().splitlines() if line.strip()]
if not rows:
parser.error("Input is empty")
for row in rows:
validate_row(row)
direct, serial, shared = direct_score, SerialPrefixScorer, score_shared
if args.backend == "mlx":
from . import mlx_backend
cache_limit_mib = (mlx_backend.DEFAULT_CACHE_LIMIT_MIB if args.mlx_cache_limit_mib is None
else args.mlx_cache_limit_mib)
model, tokenizer, metadata = mlx_backend.load_model(
args.model, args.revision, args.mlx_bits, cache_limit_mib=cache_limit_mib)
direct, serial, shared = mlx_backend.score, mlx_backend.SerialPrefixScorer, mlx_backend.score_shared
else:
model, tokenizer, metadata = load_causal_model(args.model, args.revision)
args.output.parent.mkdir(parents=True, exist_ok=True)
with args.output.open("x") as destination:
if args.mode == "shared":
results, timing = shared(model, tokenizer, rows, metadata, args.max_tokens)
for result in results:
destination.write(json.dumps({**result, "shared_timing": timing}, allow_nan=False) + "\n")
elif args.mode == "serial":
scorer = serial(model, tokenizer, metadata, args.max_tokens)
for row in rows:
destination.write(json.dumps(scorer.score(row), allow_nan=False) + "\n")
destination.flush()
else:
scorer = direct if args.mode == "direct" else reranker_score
for row in rows:
destination.write(json.dumps(scorer(model, tokenizer, row, metadata, args.max_tokens), allow_nan=False) + "\n")
destination.flush()
if __name__ == "__main__":
main()