Text Generation
Transformers
Safetensors
qwen2
godot
gdscript
qlora
game-development
conversational
text-generation-inference
Instructions to use khursheed/VibeGame-8B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use khursheed/VibeGame-8B with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="khursheed/VibeGame-8B") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("khursheed/VibeGame-8B") model = AutoModelForCausalLM.from_pretrained("khursheed/VibeGame-8B", device_map="auto") messages = [ {"role": "user", "content": "Who are you?"}, ] inputs = tokenizer.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=256) print(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use khursheed/VibeGame-8B with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "khursheed/VibeGame-8B" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "khursheed/VibeGame-8B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/khursheed/VibeGame-8B
- SGLang
How to use khursheed/VibeGame-8B 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 "khursheed/VibeGame-8B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "khursheed/VibeGame-8B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'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 "khursheed/VibeGame-8B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "khursheed/VibeGame-8B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use khursheed/VibeGame-8B with Docker Model Runner:
docker model run hf.co/khursheed/VibeGame-8B
Download code/scripts/build_dataset.py from khursheed/VibeGame-8B: direct link, hf CLI and curl.
- Browser
- Download file 18.9 kB
-
https://huggingface.co/khursheed/VibeGame-8B/resolve/main/code/scripts/build_dataset.py
- Command line
-
hf download hf://khursheed/VibeGame-8B/code/scripts/build_dataset.py
-
curl -L -o build_dataset.py https://huggingface.co/khursheed/VibeGame-8B/resolve/main/code/scripts/build_dataset.py
18.9 kB
| #!/usr/bin/env python3 | |
| """Freeze blind prompts and admit only validated whole-project SFT examples. | |
| Usage: | |
| python scripts/build_dataset.py --freeze-eval-only | |
| python scripts/build_dataset.py --input data/synthetic_candidates.jsonl --godot /path/to/godot | |
| The 500 blind prompts are not supplied to a teacher and are never used for loss. | |
| The small development split has complete targets and is used for eval loss. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| from concurrent.futures import ThreadPoolExecutor | |
| import hashlib | |
| import json | |
| from pathlib import Path | |
| import sys | |
| sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src")) | |
| from vibegame.data import Deduper, canonical_output, read_jsonl, sha256, write_jsonl | |
| # Every family has a genuinely different core interaction. Crossed rule modifiers | |
| # vary objective/scoring/state; these are a transparent templated benchmark. | |
| FAMILIES = [ | |
| ("snake", "Steer a snake around a square grid with the arrow keys. Eating food extends its body; touching a wall or its own body ends the round."), | |
| ("breakout", "Move a paddle horizontally to bounce a ball into a wall of bricks. Remove every brick to win, and lose a life when the ball falls below the paddle."), | |
| ("pong", "Play a paddle duel against a simple computer opponent. Use up and down to defend your goal while a bouncing ball speeds up after each paddle hit."), | |
| ("asteroids", "Pilot a small triangular spaceship with rotation, thrust, and a fire button. Destroy drifting asteroids that split into smaller pieces; wrap ships and rocks at screen edges."), | |
| ("invaders", "Move a defender along the bottom and fire upward at descending rows of invaders. Enemies fire back, and the player loses if an enemy reaches the bottom."), | |
| ("flap", "Press space to flap a flying circle upward against gravity. Fly through gaps in pairs of scrolling columns and score once per safely cleared gap."), | |
| ("runner", "Build a side-scrolling runner whose character moves automatically. Space jumps over approaching blocks, and distance traveled increases the score."), | |
| ("dodge", "Move a character in all four directions inside an arena. Survive falling geometric hazards, with collisions removing health and survival time determining score."), | |
| ("maze", "Navigate a grid maze from an entrance to an exit with the arrow keys. Collect three keys before the exit opens, and prevent movement through maze walls."), | |
| ("crate_puzzle", "Push crates one cell at a time through a small warehouse grid. Arrow keys move the worker; a crate cannot be pulled, and every marked goal must contain a crate to win."), | |
| ("turret", "Rotate a stationary central turret and fire at enemies approaching from the arena boundary. Projectiles destroy enemies, while enemies that reach the turret damage its health."), | |
| ("tower_defense", "Let the player place simple towers beside a fixed enemy path using the mouse. Towers automatically shoot nearby enemies; defeated enemies grant coins for more towers."), | |
| ("match_three", "Create a grid of colored tiles that the player swaps with adjacent tiles. Remove horizontal or vertical lines of at least three matching colors, then let new tiles fall into gaps."), | |
| ("memory", "Display face-down cards in a grid and let the player reveal two at a time with the mouse. Matching symbols stay revealed, while different symbols turn face-down after a short delay."), | |
| ("minefield", "Create a small grid with hidden mines and safe tiles showing adjacent mine counts. Left-click reveals a tile, right-click toggles a flag, and revealing a mine loses the round."), | |
| ("lights_out", "Create a grid of lights where clicking a cell toggles itself and its orthogonal neighbors. Start from a solvable scrambled arrangement and win when every light is off."), | |
| ("sliding_tiles", "Arrange numbered tiles in a square grid with one empty cell. Clicking a tile adjacent to the empty cell slides it, and the player wins by restoring numerical order."), | |
| ("rhythm", "Move notes downward in four lanes toward a hit line. The player presses four mapped keys at the right time; accurate hits score points and missed notes break the streak."), | |
| ("typing", "Let short words fall from the top toward a danger line. The player types a displayed word and presses Enter to remove it; escaped words remove health."), | |
| ("artillery", "Let the player adjust a cannon angle and launch a projectile affected by gravity. Place a target across the screen and award points when a shot lands on it."), | |
| ("lander", "Pilot a lunar lander with left/right rotation and upward thrust while gravity pulls it down. Land slowly and upright on a marked platform; hard or tilted contact crashes the ship."), | |
| ("pinball", "Bounce a ball around a geometric pinball table with bumpers and two player-controlled flippers. Score on bumper contact and lose a life when the ball exits below the flippers."), | |
| ("rally", "Drive a top-down car around a closed track using steering and acceleration keys. Checkpoints must be visited in order before a lap counts, and driving off track slows the car."), | |
| ("fishing", "Press a button to cast a line into a pond containing moving fish. When a fish bites, keep a marker inside a moving capture zone to reel it in and earn points."), | |
| ("reaction", "Show several target circles and briefly light one at a time. The player clicks the lit target before it expires; clicking another circle or reacting too slowly counts as a miss."), | |
| ] | |
| MODIFIERS = [ | |
| ("countdown", "The whole round has a visible 90-second countdown; reaching zero ends it and displays the final score."), | |
| ("three_attempts", "Give the player exactly three attempts, show the remaining attempts, and restart the active stage after each failed attempt."), | |
| ("streak_scoring", "Consecutive successful actions build a score multiplier up to five; any failure resets the multiplier to one and the HUD explains the change."), | |
| ("three_stages", "Include three stages with a visible stage number. Each completed stage advances to a harder layout or faster timing, and finishing stage three wins."), | |
| ("practice_mode", "Add a title-screen choice between practice and challenge. Practice permits unlimited retries, while challenge counts failures and reports them on completion."), | |
| ("slow_motion", "Give the player one slow-motion charge per round. Holding Shift spends its three seconds of duration, and a meter shows the remaining charge."), | |
| ("precision_bonus", "Track a precision percentage based on successful versus failed actions. Finishing above 80 percent earns an explicit score bonus shown in the result screen."), | |
| ("checkpoint", "Save one in-memory checkpoint halfway through the objective. A failure after that point resumes at the checkpoint while earlier failures restart the round."), | |
| ("risk_reward", "Add an optional harder objective worth twice the normal points. Make its active state clearly visible, and let the player toggle it with the B key before a round."), | |
| ("speed_choice", "Offer slow, normal, and fast modes on a start screen. All mode-dependent motion or timers use the chosen speed consistently, and fast mode awards extra points."), | |
| ("perfect_round", "Track whether the current round is flawless. Any failure removes the flawless badge; completing a round without one displays a perfect-round banner."), | |
| ("endurance", "Include an endless mode that increases difficulty every 20 seconds. Display the current difficulty and end the run after five failures."), | |
| ("tutorial", "Begin with a short interactive tutorial that waits for each required input before advancing. Allow Enter to skip it, and show the same controls during play."), | |
| ("best_score", "Track the best score for the current application session. Show both current and best score during play and update the record immediately when it is exceeded."), | |
| ("daily_seed", "Use a visible numeric seed for all randomized gameplay choices. Pressing N before a round changes the seed; restarting the same seed reproduces the initial state."), | |
| ("score_attack", "Make score attack a selectable mode with a fixed target score and a countdown. Reaching the target wins immediately, and running out of time loses."), | |
| ("limited_actions", "Offer an expert mode with a visible budget of 30 meaningful player actions. Reaching the game objective within that budget wins; exhausting it ends the attempt."), | |
| ("comeback", "After two consecutive failures, temporarily give a clearly labeled assist that widens timing windows or reduces difficulty. A success removes the assist."), | |
| ("round_series", "Play a series of five short rounds. Preserve cumulative score between rounds, show an intermission after each one, and display a results table after the fifth."), | |
| ("accessibility", "Provide an on-screen toggle for a high-contrast palette and use labels or shapes as well as colors. Include a reduced-motion toggle that removes decorative animation."), | |
| ] | |
| def frozen_eval_rows() -> list[dict]: | |
| rows = [] | |
| for family, description in FAMILIES: | |
| for mechanic, modifier in MODIFIERS: | |
| instruction = (f"Build a complete playable 2D Godot 4 game. {description} {modifier} " | |
| "Use original geometric visuals drawn in code, no external assets. Include visible controls, " | |
| "an unambiguous win or loss state, pause on P, and restart on R. Return only a JSON object " | |
| 'with a files mapping containing project.godot, main.tscn, and every required .gd file.') | |
| rows.append({"id": f"vg8b-eval-v1-{family}-{mechanic}", "family": family, | |
| "mechanic": mechanic, "instruction": instruction, "sha256": sha256(instruction)}) | |
| assert len(rows) == 500 and len({row["sha256"] for row in rows}) == 500 | |
| return rows | |
| def freeze_eval(output: Path) -> list[dict]: | |
| rows = frozen_eval_rows() | |
| target = output / "eval_prompts.jsonl" | |
| if target.exists() and list(read_jsonl(target)) != rows: | |
| raise ValueError("Frozen eval file changed: use a new version/directory, never silently replace a benchmark") | |
| write_jsonl(target, rows) | |
| digest = hashlib.sha256(target.read_bytes()).hexdigest() | |
| manifest = {"version": "vg8b-eval-v1", "count": 500, "sha256": digest, | |
| "families": len(FAMILIES), "rule_modifiers": len(MODIFIERS), | |
| "design": "25 core game interactions crossed with 20 explicit rule modifiers", | |
| "source": "original prompts authored for this repository; templated, not independently human-curated", | |
| "policy": "blind generation benchmark; never train on these prompts, generate teacher targets for them, or use them for checkpoint selection", | |
| "limitations": "Compositional variants share vocabulary and mechanics. 500 rows do not imply 500 independent task families."} | |
| (output / "eval_manifest.json").write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8") | |
| return rows | |
| def split_records(rows: list[dict], dev_fraction: float = 0.1) -> tuple[list[dict], list[dict]]: | |
| """Stable hash split after global prompt/output dedupe, never row duplication.""" | |
| ranked = sorted(rows, key=lambda row: sha256("vibegame-dev-v1:" + row["instruction"])) | |
| if len(ranked) < 2: | |
| raise ValueError("At least two unique validated projects are required for train + dev") | |
| count = max(1, min(len(ranked) - 1, round(len(ranked) * dev_fraction))) | |
| return ranked[count:], ranked[:count] | |
| def successful_cached_validation(directory: Path, files: dict, binary_sha: str, seconds: int) -> dict | None: | |
| """Reuse only matching complete evidence whose original logs still hash.""" | |
| from vibegame.project import project_sha256 | |
| evidence = directory / "validation.json" | |
| if not evidence.is_file(): | |
| return None | |
| try: | |
| result = json.loads(evidence.read_text(encoding="utf-8")) | |
| if not (result.get("compile_success") and result.get("clean_run_60s") | |
| and result.get("godot_binary_sha256") == binary_sha | |
| and result.get("project_sha256") == project_sha256(files) | |
| and result.get("run_seconds", 0) >= seconds | |
| and result.get("runtime", {}).get("wall_seconds", 0) >= seconds): | |
| return None | |
| for phase in ["import", "runtime"]: | |
| item = result[phase] | |
| log = directory / ("import.log" if phase == "import" else "runtime.log") | |
| if not item.get("success") or hashlib.sha256(log.read_bytes()).hexdigest() != item["log_sha256"]: | |
| return None | |
| for name, source in files.items(): | |
| if (directory / "project" / name).read_text(encoding="utf-8") != source: | |
| return None | |
| return {**result, "reused_evidence": str(evidence)} | |
| except (OSError, ValueError, KeyError, TypeError): | |
| return None | |
| def main() -> None: | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument("--input", type=Path, action="append", help="Candidate instruction/output JSONL, repeatable") | |
| parser.add_argument("--output", type=Path, default=Path("data")) | |
| parser.add_argument("--godot", help="Godot 4 executable; required to admit training examples") | |
| parser.add_argument("--run-seconds", type=int, default=60) | |
| parser.add_argument("--workers", type=int, default=2) | |
| parser.add_argument("--dev-fraction", type=float, default=0.1) | |
| parser.add_argument("--freeze-eval-only", action="store_true") | |
| args = parser.parse_args() | |
| if not 0 < args.dev_fraction < 1: | |
| parser.error("dev fraction must be between zero and one") | |
| if args.run_seconds < 60: | |
| parser.error("admitted synthetic pairs require a full 60-second run") | |
| if args.workers < 1 or args.workers > 4: | |
| parser.error("workers must be 1 to 4") | |
| args.output.mkdir(parents=True, exist_ok=True) | |
| evaluation = freeze_eval(args.output) | |
| if args.freeze_eval_only: | |
| print(f"Frozen {len(evaluation)} blind prompts in {args.output / 'eval_prompts.jsonl'}"); return | |
| if not args.input or not args.godot: | |
| parser.error("--input and --godot are required; no unvalidated training data can be written") | |
| from vibegame.validate import validate_project | |
| binary_path = Path(args.godot).expanduser().resolve(strict=True) | |
| binary_sha = hashlib.sha256(binary_path.read_bytes()).hexdigest() | |
| prompts = Deduper(threshold=0.78, width=3) | |
| outputs = Deduper(threshold=0.85, width=5) | |
| for row in evaluation: | |
| prompts.add(row["id"], row["instruction"]) | |
| candidates, rejected = [], [] | |
| for path in args.input: | |
| for number, row in enumerate(read_jsonl(path), 1): | |
| identifier = f"{path}:{number}" | |
| try: | |
| if set(row) != {"instruction", "output"}: | |
| raise ValueError("SFT schema must contain exactly instruction and output") | |
| if not isinstance(row["instruction"], str) or len(row["instruction"].strip()) < 20: | |
| raise ValueError("instruction must be a meaningful string") | |
| canonical, files = canonical_output(row["output"]) | |
| duplicate = prompts.match(row["instruction"]) or outputs.match(canonical) | |
| if duplicate: | |
| raise ValueError("exact/near duplicate of " + duplicate) | |
| prompts.add(identifier, row["instruction"]); outputs.add(identifier, canonical) | |
| candidates.append((identifier, {"instruction": row["instruction"], "output": canonical}, files)) | |
| except (ValueError, TypeError) as exc: | |
| rejected.append({"source": identifier, "reason": str(exc)}) | |
| def check(item): | |
| identifier, row, files = item | |
| fingerprint = sha256(row["output"]) | |
| work_base = args.output / "validation" | |
| try: | |
| for previous in sorted(work_base.glob(fingerprint[:16] + "*")): | |
| cached = successful_cached_validation(previous, files, binary_sha, args.run_seconds) | |
| if cached: | |
| return identifier, row, cached, True | |
| work_dir = work_base / fingerprint[:16] | |
| attempt = 1 | |
| while work_dir.exists(): | |
| work_dir = work_base / f"{fingerprint[:16]}-attempt-{attempt:03d}" | |
| attempt += 1 | |
| result = validate_project(files, work_dir, str(binary_path), run_seconds=args.run_seconds) | |
| passed = bool(result.get("compile_success") and result.get("clean_run_60s")) | |
| return identifier, row, result, passed | |
| except Exception as exc: | |
| return identifier, row, {"error": type(exc).__name__ + ": " + str(exc)}, False | |
| accepted, audits = [], [] | |
| with ThreadPoolExecutor(max_workers=args.workers) as pool: | |
| for identifier, row, result, passed in pool.map(check, candidates): | |
| audits.append({"source": identifier, "instruction_sha256": sha256(row["instruction"]), | |
| "output_sha256": sha256(row["output"]), "accepted": passed, "validation": result}) | |
| print(f"{'PASS' if passed else 'DROP'} {identifier}", flush=True) | |
| if passed: | |
| accepted.append(row) | |
| else: | |
| rejected.append({"source": identifier, "reason": "compile_or_60s_run_failed"}) | |
| write_jsonl(args.output / "validation_manifest.jsonl", audits) | |
| write_jsonl(args.output / "rejected_candidates.jsonl", rejected) | |
| train, dev = split_records(accepted, args.dev_fraction) | |
| write_jsonl(args.output / "train.jsonl", train) | |
| write_jsonl(args.output / "dev.jsonl", dev) | |
| manifest = {"schema_version": 1, "train": len(train), "dev": len(dev), "blind_eval": len(evaluation), | |
| "candidate_count_after_dedupe": len(candidates), "accepted": len(accepted), "rejected": len(rejected), | |
| "training_schema": {"instruction": "string", "output": "JSON string with files mapping"}, | |
| "dedupe": {"instruction_token_3gram_jaccard": 0.78, "output_token_5gram_jaccard": 0.85}, | |
| "validation_run_seconds": args.run_seconds, "sources": [str(path) for path in args.input], | |
| "files_sha256": {name: hashlib.sha256((args.output / name).read_bytes()).hexdigest() | |
| for name in ["train.jsonl", "dev.jsonl", "eval_prompts.jsonl"]}, | |
| "reference_corpus_trained_on": False, | |
| "scope": "bootstrap synthetic data; quantity and diversity are not sufficient to claim a good game-generation model"} | |
| (args.output / "dataset_manifest.json").write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8") | |
| print(json.dumps({"train": len(train), "dev": len(dev), "blind_eval": len(evaluation), "rejected": len(rejected)})) | |
| if __name__ == "__main__": | |
| main() | |