Text Generation
Transformers
English
structured-generation
parallel-decoding
constrained-decoding
classification
json
Instructions to use shreyansh26/Qwen-2.5-1B-RLCD with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use shreyansh26/Qwen-2.5-1B-RLCD with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="shreyansh26/Qwen-2.5-1B-RLCD")# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("shreyansh26/Qwen-2.5-1B-RLCD", device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use shreyansh26/Qwen-2.5-1B-RLCD with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "shreyansh26/Qwen-2.5-1B-RLCD" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "shreyansh26/Qwen-2.5-1B-RLCD", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/shreyansh26/Qwen-2.5-1B-RLCD
- SGLang
How to use shreyansh26/Qwen-2.5-1B-RLCD with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "shreyansh26/Qwen-2.5-1B-RLCD" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "shreyansh26/Qwen-2.5-1B-RLCD", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "shreyansh26/Qwen-2.5-1B-RLCD" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "shreyansh26/Qwen-2.5-1B-RLCD", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use shreyansh26/Qwen-2.5-1B-RLCD with Docker Model Runner:
docker model run hf.co/shreyansh26/Qwen-2.5-1B-RLCD
Download core/engine.py from shreyansh26/Qwen-2.5-1B-RLCD: direct link, hf CLI and curl.
- Browser
- Download file 17.4 kB
-
https://huggingface.co/shreyansh26/Qwen-2.5-1B-RLCD/resolve/main/core/engine.py
- Command line
-
hf download hf://shreyansh26/Qwen-2.5-1B-RLCD/core/engine.py
-
curl -L -o engine.py https://huggingface.co/shreyansh26/Qwen-2.5-1B-RLCD/resolve/main/core/engine.py
17.4 kB
| """Transformers inference with autoregressive decoding and batched field decisions.""" | |
| import copy | |
| import json | |
| import os | |
| import threading | |
| import time | |
| from functools import wraps | |
| from typing import Any, Dict, Generator | |
| import torch | |
| from torch.nn.attention.flex_attention import create_block_mask | |
| from transformers import AutoModelForCausalLM, AutoTokenizer | |
| from core.prompt_builder import build_naive_json_prompt | |
| from core.schema import StructuredSchema | |
| MODEL_ID = os.environ.get("MODEL_ID", "Qwen/Qwen2.5-1.5B-Instruct") | |
| _model = None | |
| _tokenizer = None | |
| _gpu_lock = threading.Lock() | |
| _load_lock = threading.Lock() | |
| def gpu_locked(fn): | |
| def wrapper(*args, **kwargs): | |
| with _gpu_lock, torch.inference_mode(): | |
| return fn(*args, **kwargs) | |
| return wrapper | |
| def gpu_locked_gen(fn): | |
| def wrapper(*args, **kwargs): | |
| # Inference mode is thread-local. Starlette can resume a sync iterator on | |
| # another worker, so enter it for each next(), never across a yield. | |
| iterator = fn(*args, **kwargs) | |
| _gpu_lock.acquire() | |
| try: | |
| while True: | |
| with torch.inference_mode(): | |
| try: | |
| event = next(iterator) | |
| except StopIteration: | |
| return | |
| yield event | |
| finally: | |
| iterator.close() | |
| _gpu_lock.release() | |
| return wrapper | |
| def get_engine(): | |
| with _load_lock, torch.inference_mode(): | |
| return _load_engine() | |
| def _load_engine(): | |
| global _model, _tokenizer | |
| if _model is None: | |
| device = torch.device(os.environ.get("DEVICE", "cuda" if torch.cuda.is_available() else "cpu")) | |
| dtype = torch.float32 | |
| if device.type == "cuda": | |
| with torch.cuda.device(device): | |
| dtype = torch.bfloat16 if torch.cuda.is_bf16_supported() else torch.float16 | |
| print(f"Loading {MODEL_ID} on {device} ({dtype})...") | |
| tokenizer = AutoTokenizer.from_pretrained(MODEL_ID) | |
| model = AutoModelForCausalLM.from_pretrained(MODEL_ID, dtype=dtype).to(device).eval() | |
| if tokenizer.pad_token_id is None: | |
| tokenizer.pad_token = tokenizer.eos_token | |
| warmup = tokenizer("Warmup", return_tensors="pt").to(device) | |
| model(**warmup, use_cache=True, logits_to_keep=1) | |
| _synchronize(model) | |
| _model, _tokenizer = model, tokenizer | |
| print("Engine ready.") | |
| return _model, _tokenizer | |
| def _synchronize(model): | |
| if model.device.type == "cuda": | |
| torch.cuda.synchronize(model.device) | |
| def _naive_result(text, schema, token_count, passes, elapsed_ms): | |
| parsed = None | |
| error = None | |
| try: | |
| parsed = json.loads(text.strip()) | |
| except ValueError as exc: | |
| error = str(exc) | |
| valid = error is None | |
| missing, invalid = [], [] | |
| if isinstance(parsed, dict): | |
| for name, field in schema.fields.items(): | |
| if name not in parsed: | |
| missing.append(name) | |
| elif (field.field_type == "boolean" and not isinstance(parsed[name], bool)) or ( | |
| field.field_type != "boolean" and parsed[name] not in field.choices | |
| ): | |
| invalid.append(f"{name}={parsed[name]}") | |
| return { | |
| "mode": "naive_autoregressive", "elapsed_ms": round(elapsed_ms, 2), | |
| "total_tokens": token_count, | |
| "tokens_per_second": round(token_count * 1000 / max(elapsed_ms, 1e-6), 1), | |
| "sequential_forward_passes": passes, "is_valid_json": valid, | |
| "schema_match": isinstance(parsed, dict) and set(parsed) == set(schema.fields) and not invalid, | |
| "raw_text": text, "parsed_json": parsed, "parse_error": error, | |
| "missing_keys": missing, "invalid_enums": invalid, | |
| "has_calibrated_probabilities": False, | |
| } | |
| def stream_naive_generation(context: str, schema: StructuredSchema, max_tokens: int = 700, | |
| temperature: float = 0.2) -> Generator[Dict[str, Any], None, None]: | |
| if max_tokens < 1 or temperature < 0: | |
| raise ValueError("max_tokens must be positive and temperature nonnegative") | |
| model, tokenizer = get_engine() | |
| inputs = tokenizer(build_naive_json_prompt(context, schema), add_special_tokens=False, | |
| return_tensors="pt").to(model.device) | |
| t0 = time.perf_counter() | |
| cache = None | |
| generated = [] | |
| current_text = "{\n " | |
| eos = model.generation_config.eos_token_id | |
| stop_tokens = set(eos if isinstance(eos, list) else [eos]) | {tokenizer.eos_token_id} | |
| passes = 0 | |
| emitted = False | |
| for _ in range(max_tokens): | |
| output = model(**inputs, past_key_values=cache, use_cache=True, logits_to_keep=1) | |
| cache = output.past_key_values | |
| passes += 1 | |
| logits = output.logits[0, -1].float() | |
| token = int(torch.argmax(logits) if temperature == 0 else | |
| torch.multinomial(torch.softmax(logits / temperature, dim=-1), 1)[0]) | |
| if token in stop_tokens: | |
| break | |
| generated.append(token) | |
| text = "{\n " + tokenizer.decode(generated, skip_special_tokens=True, | |
| clean_up_tokenization_spaces=False) | |
| # Wait for complete UTF-8 characters before emitting a text delta. | |
| if not text.endswith("\ufffd"): | |
| delta = text[len(current_text):] if emitted else text | |
| emitted = True | |
| current_text = text | |
| yield {"type": "token", "token": delta, "accumulated": text, | |
| "token_count": len(generated), | |
| "elapsed_ms": round((time.perf_counter() - t0) * 1000, 1)} | |
| try: | |
| if isinstance(json.loads(text), dict): | |
| break | |
| except ValueError: | |
| pass | |
| inputs = {"input_ids": torch.tensor([[token]], device=model.device), | |
| "attention_mask": torch.cat([inputs["attention_mask"], | |
| torch.ones((1, 1), dtype=torch.long, device=model.device)], dim=1)} | |
| _synchronize(model) | |
| yield {"type": "done", "result": _naive_result( | |
| current_text, schema, len(generated), passes, (time.perf_counter() - t0) * 1000)} | |
| def run_naive_generation(context: str, schema: StructuredSchema, max_tokens: int = 700, | |
| temperature: float = 0.2) -> Dict[str, Any]: | |
| for event in stream_naive_generation(context, schema, max_tokens, temperature): | |
| if event["type"] == "done": | |
| return event["result"] | |
| def _candidate_scores(model, cache, first_logits, candidates, pad_id): | |
| """Score complete allowed continuations, including their closing delimiter.""" | |
| first_log_probs = first_logits.float().log_softmax(-1) | |
| scores = first_log_probs[[tokens[0] for tokens in candidates]].clone() | |
| passes = 0 | |
| # Bound memory when a field has hundreds of multi-token choices. | |
| for start in range(0, len(candidates), 16): | |
| sequences = candidates[start:start + 16] | |
| width = max(map(len, sequences)) - 1 | |
| if width == 0: | |
| continue | |
| batch = len(sequences) | |
| ids = torch.full((batch, width), pad_id, dtype=torch.long, device=model.device) | |
| mask = torch.zeros_like(ids) | |
| for row, tokens in enumerate(sequences): | |
| ids[row, :len(tokens) - 1] = torch.tensor(tokens[:-1], device=model.device) | |
| mask[row, :len(tokens) - 1] = 1 | |
| branch = copy.deepcopy(cache) | |
| branch.batch_repeat_interleave(batch) | |
| attention = torch.cat([torch.ones((batch, cache.get_seq_length()), | |
| dtype=torch.long, device=model.device), mask], dim=1) | |
| output = model(input_ids=ids, attention_mask=attention, | |
| past_key_values=branch, use_cache=True) | |
| passes += 1 | |
| log_probs = output.logits.float().log_softmax(-1) | |
| for row, tokens in enumerate(sequences): | |
| if len(tokens) > 1: | |
| scores[start + row] += log_probs[row, torch.arange(len(tokens) - 1, | |
| device=model.device), tokens[1:]].sum() | |
| del output, branch, log_probs | |
| return scores, passes | |
| def run_parallel_generation(context: str, schema: StructuredSchema, | |
| temperature: float = 1.0) -> Dict[str, Any]: | |
| if temperature < 0: | |
| raise ValueError("temperature must be nonnegative") | |
| model, tokenizer = get_engine() | |
| t0 = time.perf_counter() | |
| meta = schema.compile_parallel_metadata(tokenizer) | |
| prompt = (f"<|im_start|>system\nClassify JSON attributes:\n{schema.to_parallel_schema_str()}" | |
| f"<|im_end|>\n<|im_start|>user\n{context}<|im_end|>\n" | |
| f"<|im_start|>assistant\n{{\n") | |
| inputs = tokenizer(prompt, add_special_tokens=False, return_tensors="pt").to(model.device) | |
| t_pre = time.perf_counter() | |
| output = model(**inputs, use_cache=True, logits_to_keep=1) | |
| cache = output.past_key_values | |
| prefix_length = cache.get_seq_length() | |
| _synchronize(model) | |
| prefill_ms = (time.perf_counter() - t_pre) * 1000 | |
| del output | |
| cache.batch_repeat_interleave(len(schema)) | |
| suffixes = meta["suffixes_batch"].to(model.device) | |
| suffix_mask = meta["suffix_attention_mask"].to(model.device) | |
| attention = torch.cat([inputs["attention_mask"].repeat(len(schema), 1), suffix_mask], dim=1) | |
| t_suffix = time.perf_counter() | |
| output = model(input_ids=suffixes, attention_mask=attention, past_key_values=cache, use_cache=True) | |
| _synchronize(model) | |
| suffix_ms = (time.perf_counter() - t_suffix) * 1000 | |
| parsed, telemetry = {}, {} | |
| passes = 2 # Prefix prefill plus batched field suffixes. | |
| for i, (name, field) in enumerate(meta["field_items"]): | |
| length = meta["suffix_lengths"][i] | |
| logits = output.logits[i, length - 1] | |
| candidates = meta["candidate_sequences"][i] | |
| if meta["has_collisions"][i]: | |
| branch = copy.deepcopy(cache) | |
| branch.batch_select_indices(torch.tensor([i], device=model.device)) | |
| # Remove right padding before evaluating candidate continuations. | |
| padding = branch.get_seq_length() - prefix_length - length | |
| if padding: | |
| branch.crop(-padding) | |
| scores, extra_passes = _candidate_scores(model, branch, logits, candidates, tokenizer.pad_token_id) | |
| passes += extra_passes | |
| del branch | |
| else: | |
| scores = logits[[tokens[0] for tokens in candidates]].float() | |
| probabilities = torch.softmax(scores / max(temperature, 1e-4), dim=-1).tolist() | |
| winner = max(range(len(probabilities)), key=probabilities.__getitem__) | |
| choice = field.choices[winner] | |
| value = choice == "true" if field.field_type == "boolean" else choice | |
| confidence = round(probabilities[winner], 4) | |
| parsed[name] = {"value": value, "prob": confidence} | |
| top = sorted([{"choice": c, "probability": round(p, 4)} | |
| for c, p in zip(field.choices, probabilities)], | |
| key=lambda item: item["probability"], reverse=True) | |
| telemetry[name] = {"value": value, "type": field.field_type, "confidence": confidence, | |
| "cardinality": field.cardinality, "top_choices": top[:5]} | |
| _synchronize(model) | |
| return {"mode": "parallel_constrained_calibrated", "elapsed_ms": round((time.perf_counter() - t0) * 1000, 2), | |
| "prefill_ms": round(prefill_ms, 2), "suffix_eval_ms": round(suffix_ms, 2), | |
| "total_tokens_generated": 0, "sequential_forward_passes": passes, | |
| "is_valid_json": True, "schema_match": True, "parsed_json": parsed, | |
| "field_telemetry": telemetry, "has_calibrated_probabilities": True, "num_fields": len(schema)} | |
| def _tree_attention_mask(starts, ends, prefix_length): | |
| """Compile the mask predicate into block metadata without a dense token mask.""" | |
| size = starts.shape[0] | |
| limits = torch.tensor([size - 1, prefix_length, prefix_length + size], device=starts.device) | |
| def visible(batch, head, query, key): | |
| # FlexAttention can evaluate padded block indices. Clamp all lookups; | |
| # the validity checks below keep those positions masked out. | |
| q = torch.minimum(query.clamp_min(0), limits[0]) | |
| k = torch.minimum((key - limits[1]).clamp_min(0), limits[0]) | |
| ancestor = (starts[k] <= starts[q]) & (starts[q] < ends[k]) | |
| return ((query <= limits[0]) & (key < limits[2]) | |
| & ((key < limits[1]) | ancestor)) | |
| return create_block_mask(visible, B=None, H=None, Q_LEN=size, | |
| KV_LEN=prefix_length + size, device=starts.device) | |
| def run_parallel_generation_tree(context: str, schema: StructuredSchema, | |
| temperature: float = 1.0) -> Dict[str, Any]: | |
| """Score all complete candidates in one tree pass over a single prefix cache. | |
| Uses a FlexAttention BlockMask with DFS intervals for ancestor visibility. | |
| Scores are mean token log-probabilities over the full candidate, including | |
| shared value prefixes and closing delimiters. The model's original | |
| attention backend is restored afterward. | |
| """ | |
| if temperature < 0: | |
| raise ValueError("temperature must be nonnegative") | |
| model, tokenizer = get_engine() | |
| if "sliding_attention" in getattr(model.config, "layer_types", []): | |
| raise ValueError("Tree generation requires full attention, not a sliding-window cache") | |
| t0 = time.perf_counter() | |
| meta = schema.compile_tree_metadata(tokenizer) | |
| prompt = (f"<|im_start|>system\nClassify JSON attributes:\n{schema.to_parallel_schema_str()}" | |
| f"<|im_end|>\n<|im_start|>user\n{context}<|im_end|>\n" | |
| f"<|im_start|>assistant\n{{\n") | |
| inputs = tokenizer(prompt, add_special_tokens=False, return_tensors="pt").to(model.device) | |
| t_pre = time.perf_counter() | |
| output = model(**inputs, use_cache=True, logits_to_keep=1) | |
| cache = output.past_key_values | |
| prefix_length = cache.get_seq_length() | |
| _synchronize(model) | |
| prefill_ms = (time.perf_counter() - t_pre) * 1000 | |
| del output | |
| t_suffix = time.perf_counter() | |
| ids = meta["input_ids"].to(model.device) | |
| positions = meta["depths"].to(model.device) + prefix_length | |
| size = ids.shape[1] | |
| attention = _tree_attention_mask(meta["dfs_starts"].to(model.device), | |
| meta["dfs_ends"].to(model.device), prefix_length) | |
| original_backend = model.config._attn_implementation | |
| try: | |
| model.set_attn_implementation("flex_attention") | |
| # The short-query GQA decode kernel can pack more queries than fit in | |
| # a BlockMask tile. Use the general FlexAttention kernel for the tree. | |
| output = model(input_ids=ids, attention_mask=attention, position_ids=positions, | |
| past_key_values=cache, use_cache=True, | |
| logits_to_keep=meta["logit_indices"].to(model.device), | |
| kernel_options={"FORCE_USE_FLEX_ATTENTION": True}) | |
| finally: | |
| model.set_attn_implementation(original_backend) | |
| _synchronize(model) | |
| suffix_ms = (time.perf_counter() - t_suffix) * 1000 | |
| # Normalize only the parent logits needed to score candidate edges, without | |
| # allocating another full [nodes, vocabulary] log-probability tensor. | |
| logits = output.logits[0].float() | |
| rows = meta["score_rows"].to(model.device) | |
| tokens = meta["score_tokens"].to(model.device) | |
| edge_scores = logits[rows, tokens] - logits.logsumexp(-1)[rows] | |
| scores = logits.new_zeros(sum(meta["candidate_counts"])) | |
| scores.scatter_add_(0, meta["score_candidates"].to(model.device), edge_scores) | |
| scores /= meta["candidate_lengths"].to(model.device) | |
| del output, cache, logits, attention | |
| parsed, telemetry = {}, {} | |
| for (name, field), field_scores in zip(meta["field_items"], scores.split(meta["candidate_counts"])): | |
| probabilities = torch.softmax(field_scores / max(temperature, 1e-4), dim=-1).tolist() | |
| winner = max(range(len(probabilities)), key=probabilities.__getitem__) | |
| choice = field.choices[winner] | |
| value = choice == "true" if field.field_type == "boolean" else choice | |
| confidence = round(probabilities[winner], 4) | |
| parsed[name] = {"value": value, "prob": confidence} | |
| top = sorted([{"choice": c, "probability": round(p, 4)} | |
| for c, p in zip(field.choices, probabilities)], | |
| key=lambda item: item["probability"], reverse=True) | |
| telemetry[name] = {"value": value, "type": field.field_type, "confidence": confidence, | |
| "cardinality": field.cardinality, "top_choices": top[:5]} | |
| _synchronize(model) | |
| return {"mode": "parallel_constrained_tree", "elapsed_ms": round((time.perf_counter() - t0) * 1000, 2), | |
| "prefill_ms": round(prefill_ms, 2), "suffix_eval_ms": round(suffix_ms, 2), | |
| "total_tokens_generated": 0, "sequential_forward_passes": 2, | |
| "is_valid_json": True, "schema_match": True, "parsed_json": parsed, | |
| "field_telemetry": telemetry, "has_calibrated_probabilities": True, "num_fields": len(schema), | |
| "tree_nodes": size, "prefix_tokens": prefix_length, | |
| "scoring": "mean_token_log_probability"} | |
| run_rlcd_generation = run_parallel_generation | |