"""Official-style Qwen3 reranker readout adapted to declared decision options.""" from __future__ import annotations import inspect import time from .core import digest, softmax, validate_row PREFIX = ( '<|im_start|>system\nJudge whether the Document meets the requirements based on the Query and the ' 'Instruct provided. Note that the answer can only be "yes" or "no".<|im_end|>\n' '<|im_start|>user\n' ) SUFFIX = '<|im_end|>\n<|im_start|>assistant\n\n\n\n\n' DECISION_INSTRUCTION = ( "Given evidence and one possible answer to a question, determine whether the evidence supports that answer " "under the question's criterion. Use only the supplied evidence." ) RETRIEVAL_INSTRUCTION = ( "Given a search query, determine whether the document is relevant and contains evidence that answers the query." ) PROMPT_VERSION = "qwen3-reranker-native-options-v1" def _encode(tokenizer, row, option, max_tokens): experiment = row.get("provenance", {}).get("experiment") instruction = RETRIEVAL_INSTRUCTION if experiment in {"code-rag", "company-brain"} else DECISION_INSTRUCTION body = ( f": {instruction}\n" f": Question: {row['question']}\nCandidate answer: {option['description']}\n" f": {row['state']}" ) text = PREFIX + body + SUFFIX ids = tokenizer.encode(text, add_special_tokens=False) if not ids or len(ids) > max_tokens: raise ValueError(f"Row {row['id']} option {option['id']}: {len(ids)} tokens exceed limit {max_tokens}") return ids, digest(text) def _answer_ids(tokenizer): no = tokenizer.encode("no", add_special_tokens=False) yes = tokenizer.encode("yes", add_special_tokens=False) if len(no) != 1 or len(yes) != 1 or no == yes: raise ValueError("Reranker yes/no answers must be distinct single tokens") if tokenizer.convert_tokens_to_ids("yes") != yes[0] or tokenizer.convert_tokens_to_ids("no") != no[0]: raise ValueError("Tokenizer conversion differs from the official yes/no token contract") return no[0], yes[0] def score_pair_batch(model, tokenizer, specs, max_tokens: int = 4096): """Score independent row/option relevance pairs in one native batch.""" import torch if not specs: raise ValueError("Pair batch is empty") encoded = [_encode(tokenizer, row, option, max_tokens) for row, option in specs] width = max(len(ids) for ids, _ in encoded) pad = tokenizer.pad_token_id if tokenizer.pad_token_id is not None else tokenizer.eos_token_id if pad is None: raise ValueError("Tokenizer requires padding or EOS token") device = next(model.parameters()).device inputs = { "input_ids": torch.tensor([[pad] * (width - len(ids)) + ids for ids, _ in encoded], device=device), "attention_mask": torch.tensor( [[0] * (width - len(ids)) + [1] * len(ids) for ids, _ in encoded], device=device ), } kwargs = dict(inputs, use_cache=False, return_dict=True) if "logits_to_keep" in inspect.signature(model.forward).parameters: kwargs["logits_to_keep"] = 1 no_id, yes_id = _answer_ids(tokenizer) if device.type == "cuda": torch.cuda.synchronize(device) mark = time.perf_counter() with torch.inference_mode(): logits = model(**kwargs).logits[:, -1, :].float() if device.type == "cuda": torch.cuda.synchronize(device) elapsed = time.perf_counter() - mark selected = logits[:, [no_id, yes_id]] odds = selected[:, 1] - selected[:, 0] relevance = selected.softmax(-1)[:, 1] return [ { "option_id": option["id"], "log_odds": float(odds[index]), "binary_relevance": float(relevance[index]), "input_tokens": len(encoded[index][0]), "prompt_sha256": encoded[index][1], } for index, (_, option) in enumerate(specs) ], { "pair_batch_size": len(specs), "forward_seconds": elapsed, "padded_tokens": int(inputs["input_ids"].numel()), } def score(model, tokenizer, row: dict, metadata: dict, max_tokens: int = 4096) -> dict: import torch validate_row(row) started = time.perf_counter() scored, timing = score_pair_batch( model, tokenizer, [(row, option) for option in row["options"]], max_tokens ) log_odds = [item["log_odds"] for item in scored] binary = [item["binary_relevance"] for item in scored] return { "id": row["id"], "option_ids": [option["id"] for option in row["options"]], "probabilities": softmax(log_odds), "option_logits": log_odds, "independent_binary_relevance": binary, "input_tokens": sum(item["input_tokens"] for item in scored), "max_option_input_tokens": max(item["input_tokens"] for item in scored), "forward_seconds": timing["forward_seconds"], "total_seconds": time.perf_counter() - started, "option_prompt_sha256": [item["prompt_sha256"] for item in scored], "pair_batches": [timing], "prompt_version": PROMPT_VERSION, "model": metadata, "readout": "native yes/no log-odds per option, normalized only for relative comparison", "probability_status": "relative option compatibility; uncalibrated as categorical probability", }