"""Rendering, candidate aggregation and service logic, without model imports.""" from __future__ import annotations import json import math import re import string from pathlib import Path import threading if __package__: from .backends import new_meter, check_deadline from .quota import QuotaStateError else: from backends import new_meter, check_deadline from quota import QuotaStateError ROOT = Path(__file__).resolve().parent YES_FORMS = ["yes", "Yes", "YES", " yes", " Yes", " YES", "true", "True", " true", " True", "Y", " Y"] NO_FORMS = ["no", "No", "NO", " no", " No", " NO", "false", "False", " false", " False", "N", " N"] ALIASES = {"boolean": "noul", "enum": "choice", "noul": "noul", "choice": "choice", "score": "score"} class ItemFailure(RuntimeError): """Inference for one item failed; the service answers HTTP 422 and keeps running.""" class ServiceFailure(RuntimeError): """Service-level failure; the service answers HTTP 503.""" # The third consecutive item failure is treated as service-level (HTTP 503, service # marked failed), so the harness stop rule for consecutive infrastructure errors applies. ITEM_FAILURE_LIMIT = 3 def unrecoverable_engine_error(exc): """True if the __cause__/__context__ graph of exc holds an unrecoverable vLLM engine error. Classes are matched by name, so the check needs no vLLM import: EngineDeadError, or a vLLM VLLMServerError subclass other than the per-request EngineGenerateError. """ pending, seen = [exc], set() while pending: current = pending.pop() if current is None or id(current) in seen: continue seen.add(id(current)) classes = type(current).__mro__ if isinstance(current, ServiceFailure) or any(cls.__name__ == 'EngineDeadError' for cls in classes): return True vllm_classes = {cls.__name__ for cls in classes if cls.__module__ == 'vllm' or cls.__module__.startswith('vllm.')} if 'VLLMServerError' in vllm_classes and 'EngineGenerateError' not in vllm_classes: return True pending.extend((current.__cause__, current.__context__)) return False def external_path(path): """Resolve an absolute path that is neither inside the package nor an ancestor of it.""" candidate = Path(path) if not candidate.is_absolute(): raise ValueError("Path must be absolute: " + str(path)) result = candidate.resolve() if result == ROOT or result.is_relative_to(ROOT) or ROOT.is_relative_to(result): raise ValueError("Path must be outside the package and must not contain it: " + str(result)) return result def external_output(path): """Validated output location outside the package; callers create files with mode 'x'.""" result = external_path(path) result.parent.mkdir(parents=True, exist_ok=True) return result def load_json(path): def reject(value): raise ValueError("Non-finite JSON value") return json.loads(Path(path).read_text(encoding="utf-8"), parse_constant=reject) def load_config(path=None): config = load_json(path or ROOT / "config.json") apply_profile(config) validate_config(config) return config def apply_profile(config): import copy profile=config.get('profiles',{}).get(config['model']['model_key']) if profile is None: raise ValueError('No profile for model_key') for section,values in profile.items(): config.setdefault(section,{}).update(copy.deepcopy(values)) return apply_budget_tier(config) def apply_budget_tier(config, tier=None): r=config['routing'];tier=tier or r.get('tier','normal') if tier not in ('normal','hard','long'):raise ValueError('Unknown quota tier') r['tier']=tier tiers=r.get('tiers',{}) values=tiers.get(tier,{}) for key in ('reasoning_words','max_new_tokens'): if key in tiers.get('hard',{}):config['generation'][key]=tiers['hard'][key] if 'max_prompt_tokens' in values:config['limits']['max_prompt_tokens']=values['max_prompt_tokens'] for key in ('reasoning_words','max_new_tokens'): if key in values:config['generation'][key]=values[key] # Forced long is a runtime admission switch, preserving fast identity. return config def finite_number(value, name, *, low=None, high=None, positive=False): if isinstance(value, bool) or not isinstance(value, (int, float)) or not math.isfinite(value): raise ValueError(f"{name} must be a finite number") if (low is not None and value < low) or (high is not None and value > high) or (positive and value <= 0): raise ValueError(f"{name} is outside its allowed range") return value def validate_config(c): if __package__: from .quota import cost_settings else: from quota import cost_settings cost_settings(c) if c['model']['model_key'] not in c.get('profiles',{}):raise ValueError('No profile for model_key') if c.get('usage',{}).get('basis','engine') not in ('engine','each_once','submitted'): raise ValueError('Invalid usage basis') m, r, g, p, s = (c[k] for k in ("model", "routing", "generation", "prompt", "server")) if m["backend"] not in ("transformers", "vllm") or m["quant"] not in ("int8", "bf16", "awq"): raise ValueError("Unsupported backend or precision") if m['backend']=='vllm': neutral={'suppress_tokens':None,'begin_suppress_tokens':None,'bad_words_ids':None, 'forced_bos_token_id':None,'forced_eos_token_id':None,'forced_decoder_ids':None, 'no_repeat_ngram_size':0,'min_new_tokens':0,'min_length':0,'length_penalty':1.0,'renormalize_logits':False} if any(g.get(k,v)!=v for k,v in neutral.items()):raise ValueError('Unsupported vLLM decode override') if r["mode"] not in ("routed", "fast_only", "uniform"): raise ValueError("Unsupported routing mode") tiers=r.get('tiers',{}) if tiers: if set(tiers)!={'normal','hard','long'} or r.get('tier','normal') not in tiers:raise ValueError('Require normal/hard/long tiers') if tiers['long'].get('fast_only') is not True:raise ValueError('Long tier must be fast only') for tier in ('normal','long'): value=tiers[tier].get('max_prompt_tokens') if type(value) is not int or not 01: raise ValueError("probdiff threshold must be <=1") if r["margin_kind"] not in ("logratio", "probdiff"): raise ValueError("Invalid margin_kind") if r.get('long_item_slow_policy','fast_only') not in ('fast_only','allow'): raise ValueError('Invalid long item policy') if r["unparsed_policy"] not in ("fast", "force_answer"): raise ValueError("Invalid unparsed_policy") finite_number(r["quota"], "quota", low=0, high=1) finite_number(r["fuse"], "fuse", low=0, high=1) if r["quota"] > r["fuse"]: raise ValueError("quota must not exceed fuse") if r["mode"] == "uniform" and (r["quota"] != 1 or r["fuse"] != 1): raise ValueError("uniform requires explicit quota=1 and fuse=1; it is an unbounded draft experiment") for key in ("reasoning_words", "max_new_tokens"): if type(g[key]) is not int or g[key] <= 0: raise ValueError(f"{key} must be a positive integer") if type(g["seed"]) is not int or type(g["do_sample"]) is not bool: raise ValueError("Invalid generation seed or do_sample") finite_number(g.get("wall_timeout_seconds",60),"slow wall timeout",positive=True) finite_number(g["temperature"], "generation temperature", positive=True) finite_number(g["top_p"], "top_p", positive=True, high=1) finite_number(g["repetition_penalty"], "repetition_penalty", positive=True) if type(g["top_k"]) is not int or g["top_k"] < 0: raise ValueError("top_k must be a nonnegative integer") for key in ("max_request_bytes", "max_questions", "port"): if type(s[key]) is not int or s[key] <= 0: raise ValueError(f"{key} must be a positive integer") if s["port"] > 65535: raise ValueError("Invalid port") finite_number(s["socket_timeout_seconds"], "socket timeout", positive=True) if not isinstance(p["answer_marker"], str) or not p["answer_marker"].strip() or "\n" in p["answer_marker"]: raise ValueError("answer_marker must be a nonempty single line") if type(p["enable_thinking"]) is not bool or not isinstance(p["system"], str): raise ValueError("Invalid prompt settings") for template in p["cot_instructions"].values(): template.format(reasoning_words=g["reasoning_words"]) for value in (c["limits"]["max_prompt_tokens"], r["slow_max_prompt_tokens"]): if type(value) is not int or value <= 0: raise ValueError("Token limits must be positive integers") limits=c['limits'] for name in ('physical_max_prompt_tokens','oom_retry_prompt_tokens','state_chunk_tokens','precompression_trigger_chars_per_token','estimated_chars_per_token'): if type(limits[name]) is not int or limits[name]<=0: raise ValueError('Invalid compression limit: '+name) for name in ('state_head_tokens','state_tail_tokens','compression_reserve_tokens'): if type(limits[name]) is not int or limits[name]<0: raise ValueError('Invalid compression limit: '+name) if limits['physical_max_prompt_tokens']676:raise UnsupportedRequest('At most 676 choices supported') return [a+b for a in string.ascii_uppercase for b in string.ascii_uppercase][:count] def normalize_question(question): if not isinstance(question, dict): raise ValueError("Each question must be an object") qtype = ALIASES.get(question.get("type")) if qtype is None: raise UnsupportedRequest("Unsupported question type") instructions = question.get("instructions", question.get("description")) if not isinstance(instructions, str) or not instructions.strip(): raise ValueError("Question needs instructions or description") criteria = question.get("criteria") q = {"request_type": question["type"], "type": qtype, "instructions": instructions, "criteria": criteria} labels = None if qtype == "noul": if criteria is not None and not isinstance(criteria, dict): raise ValueError("noul criteria must be an object") elif qtype == "score": if not isinstance(criteria,list): raise ValueError("score criteria must be an ordered list") if not criteria: raise UnsupportedRequest("score criteria is empty") else: labels = question.get("labels") if labels is None: labels = list(criteria) if isinstance(criteria, (dict, list)) else None if not isinstance(labels,list): raise ValueError("choice needs labels or ordered criteria") if not 1<=len(labels)<=676: raise UnsupportedRequest("choice needs 1..676 labels") if any(not isinstance(x, str) or not x for x in labels) or len(set(labels)) != len(labels): raise ValueError("choice labels must be distinct nonempty strings") if criteria is not None and not isinstance(criteria, (dict, list)): raise ValueError("choice criteria must be an object or list") return q, labels def normalize_request(payload, max_questions): if not isinstance(payload, dict) or "state" not in payload: raise ValueError("Request needs state and questions") questions = payload.get("questions") if not isinstance(questions, dict) or not 1 <= len(questions) <= max_questions: raise ValueError("questions must be a nonempty object within max_questions") tasks = [] for name, question in questions.items(): if not isinstance(name, str) or not name: raise ValueError("Question names must be nonempty strings") q, labels = normalize_question(question) tasks.append((name, {"state": payload["state"], "question": q, "labels": labels})) return tasks def letter_forms(c): return [c, " " + c, c.lower(), " " + c.lower(), c + ".", "(" + c] def digit_forms(d): return [d, " " + d, d + "."] def render(task): # Source-faithful copy of cache_hidden_v2.render; do not deduplicate forms. q = task["question"]; qtype = q["type"]; crit = q.get("criteria") state = task["state"] if isinstance(task["state"], str) else json.dumps(task["state"], ensure_ascii=False) cq = "Context:\n" + state + "\n\nQuestion: " + q["instructions"] if qtype == "noul": labels = ["no", "yes"]; c = crit or {}; groups = {"yes": YES_FORMS, "no": NO_FORMS} parts = [cq] + (["Answer yes if: " + str(c.get("true", "Yes")) + "\nAnswer no if: " + str(c.get("false", "No"))] if (c.get("true") or c.get("false")) else []) + ["Answer with one word: yes or no."] elif qtype == "score": labels = [str(i) for i in range(len(crit))]; groups = {l: digit_forms(l) for l in labels} parts = [cq, "Levels:\n" + "\n".join(str(i) + ": " + str(t) for i, t in enumerate(crit)), "Answer with one number from 0 to " + str(len(crit) - 1) + "."] else: labels = list(task["labels"]); rubric = crit if isinstance(crit, dict) else {}; codes = choice_codes(len(labels)) groups = {l: letter_forms(c) for c, l in zip(codes, labels)} parts = [cq, "Options:\n" + "\n".join(c + ". " + str(l) + ((": " + str(rubric[l])) if rubric.get(l) else "") for c, l in zip(codes, labels)), "Answer with the letter of the best option."] return labels, "\n\n".join(parts), groups def parse_answer(generation, qtype="noul", labels=None): """Audit only: keep the explicit-last and standalone fallbacks of the measured parser.""" if qtype == "noul": matches = re.findall(r"ANSWER:\s*(yes|no)", generation, re.I) or re.findall(r"\b(yes|no)\b", generation, re.I) return matches[-1].lower() if matches else None if qtype == "choice": mapping = dict(zip(choice_codes(len(labels or [])), labels or [])) explicit, fallback = r"ANSWER:\s*([A-Z]{1,2})\b", r"\b([A-Z]{1,2})\b" elif qtype == "score": mapping = {str(label): str(label) for label in (labels or [])} explicit, fallback = r"ANSWER:\s*(\d+)", r"(?chunk_tokens: pieces.append(chunk);chunk='' chunk+=word if chunk:pieces.append(chunk) chunk='' def add(value): nonlocal cursor size=len(encode(value)) edge=cursortail_start blocks.append((value,size,edge));cursor+=size for piece in pieces: if chunk and len(encode(chunk+piece))>chunk_tokens: add(chunk);chunk='' chunk+=piece if chunk:add(chunk) selected={i for i,(_,_,keep) in enumerate(blocks) if keep} marker_cost=len(encode(OMISSION)) used=sum(blocks[i][1] for i in selected)+marker_cost*(len(selected)+1) ranked=[] for i,(part,size,keep) in enumerate(blocks): terms=lexical_terms(part); overlap=query_terms & terms score=(len(overlap)+2*len(important & terms))/max(1,math.sqrt(len(terms))) if not keep: ranked.append((-score,i)) for _,i in sorted(ranked): if used+blocks[i][1]+marker_cost <= budget: selected.add(i); used+=blocks[i][1]+marker_cost def join(indices): out=[]; omitted=False for i,(part,_,_) in enumerate(blocks): if i in indices: if omitted: out.append(OMISSION) out.append(part); omitted=False else: omitted=True if omitted: out.append(OMISSION) return ''.join(out) result=join(selected) # Recover conservative omission-marker reserves using adjacent blocks. while len(encode(result))<.8*budget: neighbors=sorted({j for i in selected for j in (i-1,i+1) if 0<=jn-t else [] if head: if omitted:out.append(OMISSION);omitted=False out.append(decode(head)) if len(head)+len(tail)200: size=len(encode(json.dumps(v,ensure_ascii=False))) v=shrink_state(v,int(free*size/narrative_size),head_fraction,backend=backend, query=query,head_tokens=head_tokens,tail_tokens=tail_tokens,chunk_tokens=chunk_tokens) result.append(v);omitted=False else: omitted=True return result return state def prepare_task(task, backend, config): def prompts(value): labels,text,groups=render(value) instruction=config['prompt']['cot_instructions'][value['question']['type']].format( reasoning_words=config['generation']['reasoning_words']).replace('ANSWER:',config['prompt']['answer_marker']) system,think=config['prompt']['system'],config['prompt']['enable_thinking'] return labels,groups,backend.prompt_ids(system,text,think),backend.prompt_ids(system,text+'\n'+instruction,think) limits=config['limits']; budget=limits['max_prompt_tokens'];normal_budget=budget long_budget=min(budget,config['routing'].get('tiers',{}).get('long',{}).get('max_prompt_tokens',budget)) query=task['question']['instructions']+' '+json.dumps(task['question'].get('criteria'),ensure_ascii=False)+' '+str(task.get('labels') or '') raw=task['state'] if isinstance(task['state'],str) else json.dumps(task['state'],ensure_ascii=False) precompressed=len(raw)>limits['precompression_trigger_chars_per_token']*budget source=task if precompressed:budget=long_budget if precompressed: ratio=limits['estimated_chars_per_token'] source=dict(task,state=shrink_state(task['state'],budget*ratio,limits['truncation_head_frac'],query=query, head_tokens=limits['state_head_tokens']*ratio,tail_tokens=limits['state_tail_tokens']*ratio, chunk_tokens=limits['state_chunk_tokens']*ratio)) original=prompts(source); original_count=len(original[2]) long_input=precompressed or original_count>normal_budget if long_input:budget=long_budget current,prepared=source,original refilled=False if precompressed and original_count<.8*budget: fixed=len(prompts(dict(task,state=''))[2]) refill=shrink_state(task['state'],max(0,budget-fixed-limits['compression_reserve_tokens']), limits['truncation_head_frac'],backend=backend,query=query, head_tokens=limits['state_head_tokens'],tail_tokens=limits['state_tail_tokens'],chunk_tokens=limits['state_chunk_tokens']) candidate=dict(task,state=refill);candidate_prompts=prompts(candidate) if len(candidate_prompts[2])<=budget: current,prepared=candidate,candidate_prompts;refilled=True if original_count>budget: fixed=len(prompts(dict(task,state=''))[2]) query=task['question']['instructions']+' '+json.dumps(task['question'].get('criteria'),ensure_ascii=False)+' '+str(task.get('labels') or '') allowance=max(0,budget-fixed-limits['compression_reserve_tokens']) for _ in range(3): current=dict(task,state=shrink_state(source['state'],allowance,limits['truncation_head_frac'], backend=backend,query=query,head_tokens=limits['state_head_tokens'], tail_tokens=limits['state_tail_tokens'],chunk_tokens=limits['state_chunk_tokens'])) prepared=prompts(current); size=len(prepared[2]) if size<=budget: break allowance=max(0,allowance-(size-budget)-limits['compression_reserve_tokens']) # Compression cannot remove sacred rule/question/option material. The # final physical guard always bounds tensors, with explicit metadata. kept=len(prepared[2]); physical=limits['physical_max_prompt_tokens'] hard_cut=kept>physical if hard_cut: def cut(ids): h=int(physical*limits['truncation_head_frac']);t=physical-h return ids if len(ids)<=physical else ids[:h]+ids[-t:] prepared=prepared[:2]+(cut(prepared[2]),cut(prepared[3])) return current,prepared,{'original_prompt_tokens':original_count if not precompressed else None, 'original_prompt_tokens_estimate':math.ceil(len(raw)/limits['estimated_chars_per_token']) if precompressed else original_count, 'long_input':long_input,'character_precompressed':precompressed,'adjacent_refill':refilled, 'kept_prompt_tokens':len(prepared[2]), 'slow_prompt_exceeds_budget':len(prepared[3])>min(budget,config['routing']['slow_max_prompt_tokens']), 'truncated':current['state']!=task['state'] or hard_cut, 'protected_content_exceeds_budget':kept>budget,'physical_truncated':hard_cut} def sequence_scores(session, backend, groups, meter=None): """Full-vocabulary chain probabilities; copies branch KV, never prompt prefill. Keep every form (including repeated tokenizations) with its original weight. """ cache = {(): session} continuation_tokens = 0 normalized = {} path_scores = {(): 0.0} def score(ids): nonlocal continuation_tokens prefix = () for token in ids: check_deadline(meter) child = prefix + (token,) if child not in path_scores: parent = cache[prefix] if prefix not in normalized: wanted=sorted({encoded[len(prefix)] for forms in groups.values() for form in forms if (encoded:=backend.encode(form))[:len(prefix)]==list(prefix) and len(encoded)>len(prefix)}) if hasattr(parent,'log_probs'): normalized[prefix]=parent.log_probs(wanted) else: logits=parent.logits; lse=logsumexp(list(logits.values())) normalized[prefix]={t:logits[t]-lse for t in wanted if t in logits} value=normalized[prefix].get(token) if value is None or not math.isfinite(value): raise RuntimeError('Missing candidate token') path_scores[child]=path_scores[prefix]+value prefix = child if prefix not in cache and len(prefix) < len(ids): branch = cache[prefix[:-1]].fork() if meter is not None and not getattr(branch, 'handles_meter', False): meter['output'] += 1 meter['probes'] += 1 if getattr(branch,'meter',None) is None: meter['engine_input']=meter.get('engine_input',0)+1; meter['engine_submitted']+=1 branch.advance([token]) continuation_tokens += 1 cache[prefix] = branch return path_scores[prefix] digit_ids=getattr(backend,'digit_start_ids',None) if digit_ids is None: # Synthetic tokenizer fallback only; real backends scan the vocabulary. digit_ids={ids[0] for d in string.digits if len(ids:=backend.encode(d))==1} terminals={} def termination(ids): nonlocal continuation_tokens key=tuple(ids) if key not in terminals: if key not in cache: branch=cache[key[:-1]].fork() if meter is not None and not getattr(branch,'handles_meter',False): meter['output']+=1;meter['probes']+=1 if getattr(branch,'meter',None) is None: meter['engine_input']=meter.get('engine_input',0)+1;meter['engine_submitted']+=1 branch.advance([key[-1]]);continuation_tokens+=1;cache[key]=branch se=cache[key] if hasattr(se,'non_digit_logprob'): value=se.non_digit_logprob(digit_ids) else: logits=se.logits; z=logsumexp(list(logits.values())) allowed=[v for t,v in logits.items() if t not in digit_ids] value=logsumexp(allowed)-z if allowed else -math.inf terminals[key]=value return terminals[key] result={} for label,forms in groups.items(): is_prefix=label.isdigit() and any(other!=label and other.startswith(label) for other in groups) values=[] for form in forms: ids=backend.encode(form) if not ids: continue value=score(ids) if is_prefix and form.strip().isdigit(): value+=termination(ids) values.append(value) result[label]=logsumexp(values) return result, continuation_tokens def slow_readout(backend, ids, groups, config, meter): """One autoregressive session: one prompt prefill, cached draft/marker/readout.""" if hasattr(backend,'slow_session'): session,parsed=backend.slow_session(ids,config['routing']['unparsed_policy'],meter) if session is None: return None,meter['draft'],False,False,0 # Marginalize textual whitespace forms; no full-vocabulary CPU copy. scores,probes=sequence_scores(session,backend,groups,meter) return scores,meter['draft']+meter['forced'],not parsed,False,probes meter['prompt'] += len(ids) meter['engine_submitted'] += len(ids) meter['engine_input']=meter.get('engine_input',0)+len(ids) meter['engine_calls']=meter.get('engine_calls',0)+1 session = backend.open_session(ids) import random rng = random.Random(config['generation']['seed']) generated = [] marker = config['prompt']['answer_marker'] found = False for _ in range(config['generation']['max_new_tokens']): check_deadline(meter) logits = dict(session.logits) g = config['generation'] for seen in set(generated): value = logits[seen] logits[seen] = value * g['repetition_penalty'] if value < 0 else value / g['repetition_penalty'] token = max(logits, key=logits.get) if g['do_sample']: ordered = sorted(logits, key=logits.get, reverse=True) if g['top_k']: ordered = ordered[:g['top_k']] weights = [math.exp((logits[t]-logits[token])/g['temperature']) for t in ordered] mass, cutoff = 0, sum(weights)*g['top_p'] end = 0 while end < len(weights) and mass < cutoff: mass += weights[end] end += 1 token = rng.choices(ordered[:end], weights=weights[:end])[0] generated.append(token) meter['output'] += 1 meter['draft'] += 1 meter['engine_generated']=meter.get('engine_generated',0)+1 session.advance([token]) text = backend.decode(generated) if re.search(r'(?m)^[ \t]*' + re.escape(marker) + r'$', text): found = True break if token in getattr(backend, 'eos_ids', ()): break if not found and config['routing']['unparsed_policy'] == 'fast': return None, len(generated), False, False, 0 if not found: suffix = backend.encode('\n' + marker) meter['output'] += len(suffix) meter['forced'] += len(suffix) meter['engine_input']=meter.get('engine_input',0)+len(suffix);meter['engine_submitted']+=len(suffix) session.advance(suffix) generated.extend(suffix) # Whitespace is a form branch, never a top-1 destructive advance. whitespace = False scores, probes = sequence_scores(session, backend, groups, meter) return scores, len(generated), not found, whitespace, probes def label_margin(labels, scores, kind="logratio"): """Aggregated label distribution at T=1, before any calibration/floor.""" if len(labels) < 2: return math.inf values = sorted((scores[label] for label in labels), reverse=True) if kind == 'logratio': return values[0] - values[1] if kind != 'probdiff': raise ValueError('Invalid margin kind') weights = [math.exp(v-values[0]) for v in values] return (weights[0]-weights[1])/sum(weights) def fast_readout(backend, ids, labels, groups, meter): meter["prompt"]+=len(ids) if not getattr(backend,"engine_metering",False): meter["engine_submitted"]+=len(ids);meter['engine_input']+=len(ids) meter['hf_prefill_tokens']+=len(ids) try: tokens = candidate_ids(backend, groups) except ValueError: tokens = {label: [] for label in labels} probes = 0 if any(not values for values in tokens.values()): session=backend.open_session(ids,meter=meter) if getattr(backend,'engine_metering',False) else backend.open_session(ids) if hasattr(session,'cache'): session.meter=meter scores, probes = sequence_scores(session, backend, groups, meter) else: wanted = sorted({t for values in tokens.values() for t in values}) logits=backend.next_logits(ids,wanted,meter=meter) if getattr(backend,'engine_metering',False) else backend.next_logits(ids,wanted) scores = aggregate(logits, tokens) if not getattr(backend,'engine_metering',False): meter['output']+=backend.readout_output_tokens;meter['probes']+=backend.readout_output_tokens meter['engine_generated']+=backend.readout_output_tokens return scores, probes + backend.readout_output_tokens def release_failed_session(backend): import gc gc.collect() torch=getattr(backend,'torch',None) cuda=getattr(torch,'cuda',None) if cuda is not None: try: cuda.empty_cache() except Exception: pass def fast_attempt_with_retry(backend,task,config,ids,labels,groups,meter): try: scores,out=fast_readout(backend,ids,labels,groups,meter) return scores,out,None except Exception as exc: if unrecoverable_engine_error(exc): raise cuda=getattr(getattr(backend,'torch',None),'cuda',None) oom_type=getattr(cuda,'OutOfMemoryError',MemoryError) if not isinstance(exc,(MemoryError,oom_type)) and 'out of memory' not in str(exc).lower(): raise # Leave the failed forward traceback before clearing allocator state. release_failed_session(backend) import copy reduced=copy.deepcopy(config) budget=min(config['limits']['oom_retry_prompt_tokens'],max(1,config['limits']['max_prompt_tokens']//2)) reduced['limits']['max_prompt_tokens']=budget reduced['limits']['physical_max_prompt_tokens']=budget _,(_,new_groups,new_ids,_),length=prepare_task(task,backend,reduced) scores,out=fast_readout(backend,new_ids,labels,new_groups,meter) return scores,out,length def slow_attempt(backend,ids,groups,config,meter): # Catch in this frame, then leave except before collecting. The failed # readout/generate traceback (and its KV references) no longer exists. try: return slow_readout(backend,ids,groups,config,meter) except Exception as exc: if unrecoverable_engine_error(exc): raise ServiceFailure('unrecoverable engine failure') from exc timed_out=isinstance(exc,TimeoutError) release_failed_session(backend) if timed_out: raise TimeoutError('Slow deadline exceeded') raise RuntimeError('Slow session failed') def bounded_slow_attempt(backend,ids,groups,config,meter,model_lock, *, on_service_failure=None): """Bound response wait; an in-flight synchronous engine call drains under lock. No worker may start another engine call after cancellation. Timeout usage is explicitly estimated and reconciliation rejects the run. An unrecoverable engine error is reported through on_service_failure even after the response timed out. """ import queue,time result=queue.Queue(maxsize=1) timeout=config['generation'].get('wall_timeout_seconds',60) meter['cancel_event']=threading.Event();meter['deadline']=time.monotonic()+timeout def work(): try: with model_lock: check_deadline(meter) value=slow_attempt(backend,ids,groups,config,meter) check_deadline(meter) result.put((True,value)) except Exception as exc: fatal=unrecoverable_engine_error(exc) if fatal and on_service_failure is not None: on_service_failure() result.put((False,ServiceFailure if fatal else type(exc))) worker=threading.Thread(target=work,daemon=True);worker.start() try: ok,value=result.get(timeout=timeout) except queue.Empty: meter['cancel_event'].set();meter['engine_usage_unknown']=True raise TimeoutError('Slow wall timeout; in-flight call draining') if not ok: if value is ServiceFailure: raise ServiceFailure('unrecoverable engine failure') if value is TimeoutError: raise TimeoutError('Slow wall timeout') raise RuntimeError('Slow session failed') return value def threshold_identity(config): import hashlib import inspect # The renderer source binds label layout, and settings bind both prompts. canonical=json.dumps({'prompt':{k:config['prompt'][k] for k in ('system','enable_thinking')},'render':inspect.getsource(render),'choice_codes':inspect.getsource(choice_codes), 'generation':{k:v for k,v in config['generation'].items() if k not in ('max_new_tokens','reasoning_words','wall_timeout_seconds')}},sort_keys=True,ensure_ascii=False) m=config['model'] return dict(model_key=m['model_key'],quant=m['quant'],adapter=m.get('adapter'), prompt_sha=hashlib.sha256(canonical.encode('utf-8')).hexdigest(),backend=m['backend'], margin_kind=config['routing']['margin_kind'],max_new_tokens=config['generation']['max_new_tokens'], reasoning_words=config['generation']['reasoning_words'], max_prompt_tokens=config['limits']['max_prompt_tokens'],request_contract='typesafe.criteria.v1') def validate_threshold(config,allow_placeholder=False): identity=threshold_identity(config) fit=config['routing'].get('margin_threshold_fitted_for',{}) reasons=[] if fit.get('placeholder',True): reasons.append('placeholder') if any(fit.get(k)!=identity[k] for k in ('model_key','quant','adapter','prompt_sha','margin_kind','max_prompt_tokens','request_contract')): reasons.append('identity mismatch') if fit.get('backend')!=identity['backend']: reasons.append('backend mismatch') if reasons and config['routing']['mode']=='routed' and not allow_placeholder: raise ValueError('Unfitted margin threshold: '+', '.join(reasons)) return ['margin threshold degraded: '+', '.join(reasons)] if reasons else [] class Service: def __init__(self, config, calibration, backend, counter, *, allow_placeholder_threshold=False): validate_config(config) threshold_warnings=validate_threshold(config,allow_placeholder_threshold) self.model_lock = threading.RLock() self._failure_lock = threading.Lock() self.item_failure_streak = 0 self.dead = False self.config, self.backend, self.counter = config, backend, counter counter.configure_cost(config) key = config['model']['model_key'] self.calibration = {path: validate_calibration(calibration, key, path) for path in ('fast', 'slow')} model = calibration['models'][key] self.warnings = ['calibration placeholder: refit required'] if ( model.get('placeholder') or any(b.get('placeholder') for b in self.calibration.values())) else [] self.warnings.extend(threshold_warnings) if not config['routing'].get('cost_fuse',{}).get('enabled',True): self.warnings.append('cost fuse explicitly disabled') if 'fallback' in model: self.calibration['fallback']=validate_calibration(calibration,key,'fallback') else: self.calibration['fallback']=self.calibration['fast'] self.warnings.append('fallback calibration missing: using fast block') def _mark_dead(self): with self._failure_lock: self.dead = True def is_dead(self): """Service failed: an unrecoverable engine error, the engine's own dead flag, or the third consecutive item failure. Reading the flag performs no engine call.""" if self.dead: return True resource = self.backend for name in ('engine', 'llm_engine', 'engine_core', 'resources'): resource = getattr(resource, name, None) if getattr(resource, 'engine_dead', False) is True: self._mark_dead() return self.dead def _raise_if_service_failure(self, exc): if unrecoverable_engine_error(exc) or self.is_dead(): self._mark_dead() raise ServiceFailure('unrecoverable engine failure') from exc def _item_failed(self): with self._failure_lock: self.item_failure_streak += 1 if self.item_failure_streak >= ITEM_FAILURE_LIMIT: self.dead = True return self.item_failure_streak def _item_succeeded(self): with self._failure_lock: self.item_failure_streak = 0 def health(self): dead = self.is_dead() warnings = list(self.warnings) if dead: warnings.append('service failure') reference=getattr(self.backend,'generation_reference',None) if reference and not reference['verified']:warnings.append('generation reference '+reference['status']) if hasattr(self.backend,'forward_counters') and not getattr(self.backend,'stats_available',False):warnings.append('HF forward hook unavailable; usage estimated') if getattr(self.backend,'forward_counters',{}).get('uncounted_forwards',0):warnings.append('HF uncounted forwards; usage estimated') if getattr(self.counter, 'memory_only', False): warnings.append('quota persistence unavailable: memory counts, fast only') return {'status': 'error' if dead else 'warning' if warnings else 'ok', 'warnings': warnings, 'info': {'intended_overrides': (reference or {}).get('intended_overrides',{})}, 'model_key': self.config['model']['model_key'], 'mode':self.config['routing']['mode'], 'quota_tier':self.config['routing'].get('tier','normal'), 'budget_tiers':self.config['routing'].get('tiers',{}), 'threshold_identity':threshold_identity(self.config), 'cost_fuse':self.counter.cost_fuse.snapshot(self.config['routing']['quota']), 'accuracy_only':self.config.get('bench',{}).get('accuracy_only',False), 'generation_reference':({k:v for k,v in reference.items() if k!='intended_overrides'} if reference else reference),'quantization_kernel':getattr(self.backend,'quantization_kernel',None), 'prefix_caching':getattr(self.backend,'prefix_caching',None), 'supports_slow_session':callable(getattr(self.backend,'slow_session',None)) or callable(getattr(self.backend,'open_session',None))} def engine_stats(self): with self.model_lock: return self.backend.engine_stats() def warmup(self): ids = self.backend.prompt_ids(self.config['prompt']['system'], 'Ready?', False) self.backend.next_logits(ids, [self.backend.encode('yes')[0]]) self.backend.generate(ids, 1) meter=new_meter() with self.model_lock: if callable(getattr(self.backend,'slow_session',None)): session,_=self.backend.slow_session(ids,'force_answer',meter, max_new_tokens=self.config['server']['warmup_slow_tokens']) if session is not None: branch=session.fork();branch.advance(self.backend.encode('yes')) if hasattr(branch,'log_probs'): branch.log_probs([self.backend.encode('yes')[0]]) else: _=branch.logits del branch,session elif callable(getattr(self.backend,'open_session',None)): import copy warm=copy.deepcopy(self.config) warm['generation']['max_new_tokens']=self.config['server']['warmup_slow_tokens'] warm['routing']['unparsed_policy']='force_answer' slow_attempt(self.backend,ids,{'yes':['yes',' yes']},warm,meter) def _one(self, task): with self.counter.accounting(): if self.is_dead(): raise ServiceFailure('service failure') try: answer,detail=self._one_impl(task) if self.is_dead(): raise ServiceFailure('service failure') detail['threshold_identity']=threshold_identity(self.config) detail['cost_fuse']=self.counter.record_cost(detail['usage_bases'][detail['accounting']],detail['path']=='slow') try: json.dumps([answer,detail],allow_nan=False) except ValueError as exc: raise ItemFailure('non-finite value in response') from exc except ItemFailure as exc: if self._item_failed() >= ITEM_FAILURE_LIMIT: raise ServiceFailure('consecutive item failures') from exc raise except ServiceFailure: self._mark_dead() raise self._item_succeeded() return answer,detail def _one_impl(self, task): c, backend = self.config, self.backend uncounted_before=getattr(backend,'forward_counters',{}).get('uncounted_forwards',0) labels, _, groups = render(task) qtype = task['question']['type'] detail = {'path':'fast', 'route_reason':'error', 'router_score':0.0, 'prompt_tokens':0, 'completion_tokens':0, 'n_new_tokens':0, 'prompt_tokens_details':{'cached_tokens':0,'engine_submitted_tokens':0}, 'fast_scores':None,'slow_scores':None,'budget_tier':'normal', 'readout_output_tokens':0, 'forced_answer':False, 'draft_prediction':None} warning = None meters = [] try: kept_task, (_, groups, fast_ids, slow_ids), length = prepare_task(task, backend, c) detail.update(length) long_item=length.get('long_input',False) or c['routing'].get('tier')=='long' or length.get('slow_prompt_exceeds_budget',False) or any(length[k] for k in ('truncated','character_precompressed','protected_content_exceeds_budget','physical_truncated')) hard=long_item if c['routing'].get('long_item_slow_policy','fast_only')=='fast_only' else ( length['character_precompressed'] or (length['original_prompt_tokens'] or 0)>c['routing']['slow_max_prompt_tokens'] or length['protected_content_exceeds_budget']) hard=hard or c['routing'].get('tier')=='long' or length.get('slow_prompt_exceeds_budget',False) detail['long_item']=long_item r = c['routing'] fast_scores = None fast_meter=new_meter() meters.append(fast_meter) try: with self.model_lock: fast_scores, out, retry_length = fast_attempt_with_retry(backend,task,c,fast_ids,labels,groups,fast_meter) except Exception as exc: self._raise_if_service_failure(exc) raise ItemFailure('item could not be processed') from exc if retry_length is not None: detail.update(retry_length) detail['fast_oom_retry']=True hard=True detail['prompt_tokens']+=fast_meter['prompt'] detail['completion_tokens']+=fast_meter['output'] detail['readout_output_tokens']+=fast_meter['probes'] detail['prompt_tokens_details'].update(cached_tokens=fast_meter['cached'],engine_submitted_tokens=fast_meter['engine_submitted']) if fast_meter.get('engine_usage_unknown'): detail['usage_estimated']=True if fast_meter.get('cached_unknown'): detail['prompt_tokens_details']['cached_tokens_known']=False detail['fast_prompt_tokens']=fast_meter['prompt'] detail['raw_label_scores']=detail['fast_scores']=fast_scores margin = label_margin(labels, fast_scores, r['margin_kind']) if fast_scores is not None else None detail.update(fast_margin=margin if margin is None or math.isfinite(margin) else None, fast_margin_infinite=margin == math.inf, margin_kind=r['margin_kind']) wants_slow = fast_scores is not None and not hard and ( r['mode'] == 'uniform' or (r['mode'] == 'routed' and margin < r['margin_threshold'])) wants_slow = wants_slow and (callable(getattr(backend,'slow_session',None)) or callable(getattr(backend,'open_session',None))) detail['budget_tier']='long' if hard else 'hard' if wants_slow else 'normal' detail['admission_quota'],detail['admission_quota_source']=self.counter.cost_fuse.quota(r['quota']) with self.counter.decision(wants_slow, quota=detail['admission_quota']) as decision: detail.update(path=decision.path, route_reason='oom_retry' if detail.get('fast_oom_retry') else 'length' if hard else decision.reason, quota_at_reservation=decision.snapshot) scores, block = fast_scores, 'fast' if decision.path == 'slow': meter = new_meter() meters.append(meter) try: scores, count, forced, whitespace, probes = bounded_slow_attempt(backend,slow_ids,groups,c,meter,self.model_lock,on_service_failure=self._mark_dead) detail['slow_raw_label_scores']=detail['slow_scores']=scores block = 'slow' if scores is not None else 'fallback' detail['route'] = ('slow_forced_answer' if forced else 'slow') if scores is not None else 'slow_unparsed_fast' if scores is None: scores = fast_scores detail.update(n_new_tokens=count, forced_answer=forced, whitespace_step=whitespace) except Exception as exc: self._raise_if_service_failure(exc) scores, block = fast_scores, 'fallback' warning = 'slow_failed: fast answer kept' detail.update(fallback='slow_to_fast', route='slow_timeout_fast' if isinstance(exc,TimeoutError) else 'slow_failed_fast') if isinstance(exc,TimeoutError): warning='slow_timeout: fast answer kept' detail['usage_estimated']=True # Detach response accounting from the draining worker. meter=dict(meter);meters[-1]=meter finally: if meter.get('engine_usage_unknown'): detail['usage_estimated']=True if meter.get('cached_unknown'): detail['prompt_tokens_details']['cached_tokens_known']=False detail['prompt_tokens'] += meter['prompt'] detail['completion_tokens'] += meter['output'] detail['n_new_tokens'] = meter['draft'] + meter['forced'] detail['readout_output_tokens'] += meter['probes'] detail['prompt_tokens_details']['cached_tokens'] += meter['cached'] detail['prompt_tokens_details']['engine_submitted_tokens'] += meter['engine_submitted'] detail['calibration_block']=block if scores is None: raise ItemFailure('no label scores') probs = probabilities(labels, scores, qtype, self.calibration.get(block, self.calibration['fast'])) detail['quota_after_completion'] = decision.snapshot except (ItemFailure, ServiceFailure, QuotaStateError): raise except Exception as exc: # No substitute answer: an engine failure is service-level (HTTP 503); any other # failure fails this item (HTTP 422), which the harness scores as wrong. self._raise_if_service_failure(exc) raise ItemFailure('item could not be processed') from exc if (getattr(backend,'forward_counters',{}).get('uncounted_forwards',0)>uncounted_before or hasattr(backend,'forward_counters') and not getattr(backend,'stats_available',False)): detail['usage_estimated']=True warning='HF count unavailable: usage estimated' detail['backend_warnings']=[w for m in meters for w in m.get('warnings',[])] if any(m.get('cached_unknown') for m in meters):detail['usage_estimated']=True submitted=sum(m['engine_submitted'] for m in meters) cached=sum(m['cached'] for m in meters) generated=sum(m.get('engine_generated',m['output']) for m in meters) engine_input=sum(m.get('engine_input',m['engine_submitted']-m['cached']) for m in meters) if detail.get('usage_estimated'): engine_input=max(engine_input,submitted)+c['generation']['max_new_tokens'] generated+=c['generation']['max_new_tokens'] detail['input_tokens_details']=dict(cached_tokens=cached,engine_submitted_tokens=submitted, engine_calls=sum(m.get('engine_calls',0) for m in meters),engine_generated_tokens=generated, cached_tokens_known=not any(m.get('cached_unknown') for m in meters), generate_nonempty_calls=sum(m.get('generate_nonempty_calls',0) for m in meters), hf_prefill_tokens=sum(m.get('hf_prefill_tokens',0) for m in meters)) detail['usage_bases']={ 'engine':dict(input_tokens=engine_input,output_tokens=generated), 'each_once':dict(input_tokens=detail['prompt_tokens'],output_tokens=detail['completion_tokens']), 'submitted':dict(input_tokens=submitted,output_tokens=detail['completion_tokens'])} if detail.get('usage_estimated'): for values in detail['usage_bases'].values(): values['input_tokens']=max(values['input_tokens'],engine_input) values['output_tokens']=max(values['output_tokens'],generated) detail['accounting']=c.get('usage',{}).get('basis','engine') detail['kept_prompt_tokens_max'] = detail.get('kept_prompt_tokens', 0) detail['total_tokens'] = detail['prompt_tokens'] + detail['completion_tokens'] detail['prediction'] = max(labels, key=probs.get) request_type = task['question']['request_type'] if qtype == 'noul': style = c.get('interface', {}).get('boolean_keys', 'request') if style == 'true_false' or (style == 'request' and request_type == 'boolean'): probs = {'false':probs['no'], 'true':probs['yes']} answer = {'type':request_type, 'probabilities':probs} if qtype == 'noul': answer['noul'] = float(probs.get('yes', probs.get('true'))) elif qtype == 'choice': answer['choice'] = detail['prediction'] if warning: answer['warning'] = warning return answer, detail def answer(self, payload): if self.is_dead(): raise ServiceFailure('service failure') tasks = normalize_request(payload, self.config['server']['max_questions']) answers, details, errors, noul, choice = {}, {}, {}, {}, {} for name, task in tasks: answer, detail = self._one(task) answers[name], details[name] = answer, detail if 'warning' in answer: errors[name] = answer['warning'] if task['question']['type'] == 'noul': noul[name] = answer['probabilities'].get('true', answer['probabilities'].get('yes')) else: choice[name] = detail['prediction'] basis=self.config.get('usage',{}).get('basis','engine') alternatives={b:{k:sum(d['usage_bases'][b][k] for d in details.values()) for k in ('input_tokens','output_tokens')} for b in ('engine','each_once','submitted')} p,o=alternatives[basis]['input_tokens'],alternatives[basis]['output_tokens'] token_details={k:sum(d['input_tokens_details'][k] for d in details.values()) for k in ('cached_tokens','engine_submitted_tokens','engine_calls','engine_generated_tokens','generate_nonempty_calls','hf_prefill_tokens')} token_details['cached_tokens_known']=all(d['input_tokens_details']['cached_tokens_known'] for d in details.values()) result={'answers':answers,'model':self.config['model']['name'], 'usage':dict(input_tokens=p,output_tokens=o,prompt_tokens=p,completion_tokens=o,total_tokens=p+o, cost_fuse=next(reversed(details.values()))['cost_fuse'], accounting=basis,usage_estimated=any(d.get('usage_estimated',False) for d in details.values()), alternatives=alternatives,questions=details,input_tokens_details=token_details, prompt_tokens_details=token_details,token_accounting=basis)} for key, values in (('noul',noul),('choice',choice)): if values: result[key] = next(iter(values.values())) if len(tasks) == 1 else values if errors: result['partial_errors'] = errors return result