Instructions to use jithinjames/iol-ai-2026-solver with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Local Apps Settings
- vLLM
How to use jithinjames/iol-ai-2026-solver with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "jithinjames/iol-ai-2026-solver" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "jithinjames/iol-ai-2026-solver", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/jithinjames/iol-ai-2026-solver
- SGLang
How to use jithinjames/iol-ai-2026-solver 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 "jithinjames/iol-ai-2026-solver" \ --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": "jithinjames/iol-ai-2026-solver", "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 "jithinjames/iol-ai-2026-solver" \ --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": "jithinjames/iol-ai-2026-solver", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use jithinjames/iol-ai-2026-solver with Docker Model Runner:
docker model run hf.co/jithinjames/iol-ai-2026-solver
Bump sampling temperature 0.3->0.5 -- single-variable test on v11 (score 0.1303, best-of-day banked)
cb33f4a verified | """ | |
| IOL-AI 2026 submission script. Ships inside the model repo alongside the Qwen2.5-14B-Instruct-AWQ | |
| weights. Reads /tmp/data/test.csv, writes submission.csv (id, pred, explanation) to the working | |
| directory. No internet at runtime -- everything must load from local files ("."). | |
| """ | |
| import os | |
| os.environ["HF_HUB_OFFLINE"] = "1" | |
| os.environ["TRANSFORMERS_OFFLINE"] = "1" | |
| import json | |
| import re | |
| import time | |
| START_TIME = time.time() | |
| TIME_LIMIT_SECONDS = 30 * 60 | |
| SAFETY_MARGIN_SECONDS = 90 # reserve for CSV write + any per-row overrun | |
| DEADLINE = START_TIME + TIME_LIMIT_SECONDS - SAFETY_MARGIN_SECONDS | |
| import pandas as pd | |
| import torch | |
| from transformers import AutoModelForCausalLM, AutoTokenizer | |
| SYSTEM = ( | |
| "You solve International Linguistics Olympiad problems. You will be given data " | |
| "from a language you have never seen before, plus hints, and asked to answer " | |
| "numbered items about it. The ONLY source of truth is the data given to you in " | |
| "this problem -- do not rely on anything you think you know about the language " | |
| "if it conflicts with the examples given here.\n\n" | |
| "Work method:\n" | |
| "1. Go through every single example given, in order. For each word in the " | |
| "unfamiliar language, first split it into its likely component morphemes " | |
| "(stem plus any prefixes/suffixes) even if you are not fully sure of the " | |
| "boundaries -- treat it as a sequence of parts, not one opaque unit. Then, " | |
| "for each one, write down " | |
| "every distinct morpheme, word, particle, or structural pattern (word order, " | |
| "marking, alternation) it contains and what it appears to mean or mark. Do not " | |
| "skip any example, and do not stop early once you have a plausible-looking " | |
| "pattern -- an alternation that looks like it marks one thing (e.g. tense) may " | |
| "actually mark something else (e.g. person, number, or agreement with a " | |
| "different argument), and only the examples you skipped may reveal which.\n" | |
| "2. Write a section titled RULE TABLE: that lists, as a table or bullet list, " | |
| "every distinct piece you identified and its meaning/function -- this must " | |
| "cover every example from step 1, not just the ones similar to the query.\n" | |
| "3. Using ONLY entries from your RULE TABLE, work out the answer to each query " | |
| "item. If the table has no entry for something the query needs, say so and give " | |
| "your best-supported guess rather than leaving it blank.\n" | |
| "4. Before writing your final answers, re-read your RULE TABLE and re-derive " | |
| "each answer from it one more time, checking: did you actually apply every " | |
| "rule you stated (e.g. a plural marker, a tense marker) to every relevant " | |
| "answer, not just some of them? If two answers could plausibly be swapped " | |
| "(e.g. two options assigned to the wrong item), re-check the evidence that " | |
| "distinguishes them specifically.\n\n" | |
| "Output format by task type -- give exactly this, nothing more:\n" | |
| "- translation: the translated form only, in the language the task asks for.\n" | |
| "- fill_blanks: only the missing form for each blank.\n" | |
| "- match_letters: only the option letter (e.g. A, B, C).\n" | |
| "- text_to_num: the number in digits.\n" | |
| "- num_to_text: the number written out in words, in the language asked.\n" | |
| "- any other task type: give exactly what the instruction asks for, nothing else.\n\n" | |
| "Each final answer must be the bare form only -- no surrounding quotes, no " | |
| "trailing period or punctuation that isn't part of the answer itself, no " | |
| "parenthetical notes, no alternate options separated by '/' or 'or'. Pick " | |
| "one single best answer per item.\n\n" | |
| "Follow the work method above, showing your RULE TABLE. Then write a line that " | |
| "says exactly FINAL ANSWERS: and, below it, one answer per line in the order " | |
| "the items are asked -- the bare answer only, no numbering, no quotes, no " | |
| "extra commentary." | |
| ) | |
| def count_expected_items(query): | |
| """Count numbered items (e.g. '17.', '18)') in the query -- the target answer count.""" | |
| return len(re.findall(r"(?m)^\s*\d+[.)]", query)) | |
| MAX_EXPLANATION_CHARS = 2000 | |
| def parse_answers(text, expected_count=None): | |
| """Keep only the lines after the last 'FINAL ANSWERS:' marker, one answer per line. | |
| If expected_count is given, pad with "" or truncate so the row never silently | |
| drops points from a length mismatch against the scorer's positional alignment. | |
| """ | |
| marker = list(re.finditer(r"(?im)^[#*\s]*final answers?[:#*\s]*", text)) | |
| if marker: | |
| text = text[marker[-1].end():] | |
| answers = [] | |
| for line in text.splitlines(): | |
| line = re.sub(r"^\s*\d+[.)]\s*", "", line).strip() | |
| line = line.strip("\"'") | |
| if line: | |
| answers.append(line) | |
| if expected_count: | |
| if len(answers) < expected_count: | |
| answers = answers + [""] * (expected_count - len(answers)) | |
| elif len(answers) > expected_count: | |
| answers = answers[:expected_count] | |
| return answers | |
| def extract_explanation(text): | |
| """The reasoning/RULE TABLE portion before the final-answers marker, for the | |
| Human Evaluation track -- truncated so one long row can't bloat the CSV.""" | |
| marker = re.search(r"(?im)^[#*\s]*final answers?[:#*\s]*$", text) | |
| explanation = text[:marker.start()] if marker else text | |
| explanation = explanation.strip() | |
| if len(explanation) > MAX_EXPLANATION_CHARS: | |
| explanation = explanation[:MAX_EXPLANATION_CHARS].rsplit(" ", 1)[0] + " ..." | |
| return explanation | |
| MODEL_ID = "." | |
| MAX_NEW_TOKENS_FULL = 2048 # normal pass -- room to show a full RULE TABLE | |
| MAX_NEW_TOKENS_FAST = 768 # fallback pass when time is short -- less room to reason | |
| print(f"[{time.time() - START_TIME:.0f}s] loading model...", flush=True) | |
| tok = AutoTokenizer.from_pretrained(MODEL_ID) | |
| model = AutoModelForCausalLM.from_pretrained( | |
| MODEL_ID, torch_dtype=torch.float16, device_map="auto", | |
| ).eval() | |
| PAD_TOKEN_ID = tok.pad_token_id if tok.pad_token_id is not None else tok.eos_token_id | |
| MAX_INPUT_TOKENS = 6000 # conservative guard against the model's context window -- leaves | |
| # headroom for the system prompt, chat template overhead, and | |
| # MAX_NEW_TOKENS_FULL generation on unusually long real IOL problems | |
| def truncate_context(context, query): | |
| """Guard against exceeding the model's context window -- truncates `context` (never | |
| `query`, which holds the actual questions) from the end, keeping as much of the given | |
| data as fits within MAX_INPUT_TOKENS.""" | |
| query_len = len(tok(query, add_special_tokens=False)["input_ids"]) | |
| budget = MAX_INPUT_TOKENS - query_len | |
| if budget <= 0: | |
| return context | |
| context_ids = tok(context, add_special_tokens=False)["input_ids"] | |
| if len(context_ids) <= budget: | |
| return context | |
| return tok.decode(context_ids[:budget], skip_special_tokens=True) | |
| print(f"[{time.time() - START_TIME:.0f}s] model loaded", flush=True) | |
| def generate(system, user, max_new_tokens): | |
| messages = [ | |
| {"role": "system", "content": system}, | |
| {"role": "user", "content": user}, | |
| ] | |
| # tokenize=False + separate tok(...) call, rather than apply_chat_template(..., return_dict=True), | |
| # for compatibility with the eval sandbox's pinned transformers==4.44.1 (return_dict support on | |
| # apply_chat_template was added later). | |
| prompt = tok.apply_chat_template(messages, add_generation_prompt=True, tokenize=False) | |
| enc = tok(prompt, return_tensors="pt").to(model.device) | |
| with torch.no_grad(): | |
| out = model.generate( | |
| **enc, max_new_tokens=max_new_tokens, do_sample=True, temperature=0.5, top_p=0.95, | |
| pad_token_id=PAD_TOKEN_ID, repetition_penalty=1.15, | |
| ) | |
| return tok.decode(out[0][enc["input_ids"].shape[-1]:], skip_special_tokens=True).strip() | |
| def solve_row(problem, expected, avg_row_time): | |
| """One greedy pass, with a shorter token budget if the time budget is running low.""" | |
| full_pass_estimate = avg_row_time if avg_row_time else 45.0 # seconds -- rough guess for row 1 | |
| remaining = DEADLINE - time.time() | |
| if remaining > full_pass_estimate: | |
| text = generate(SYSTEM, problem, MAX_NEW_TOKENS_FULL) | |
| else: | |
| # Running low on time: fewer tokens, same prompt. | |
| text = generate(SYSTEM, problem, MAX_NEW_TOKENS_FAST) | |
| return parse_answers(text, expected_count=expected), extract_explanation(text) | |
| df = pd.read_csv("/tmp/data/test.csv", dtype=str).fillna("") | |
| print(f"[{time.time() - START_TIME:.0f}s] {len(df)} rows to solve", flush=True) | |
| rows_out = [] | |
| row_times = [] | |
| for i, r in df.iterrows(): | |
| row_start = time.time() | |
| problem = f"{truncate_context(r['context'].strip(), r['query'].strip())}\n\n{r['query'].strip()}" | |
| expected = count_expected_items(r["query"]) | |
| avg_row_time = sum(row_times) / len(row_times) if row_times else None | |
| if time.time() > DEADLINE: | |
| # Out of time: best-effort placeholder rather than risking the whole process | |
| # getting killed by the 30-minute hard limit with no submission.csv at all. | |
| answers = [""] * max(expected, 1) | |
| explanation = "" | |
| else: | |
| try: | |
| answers, explanation = solve_row(problem, expected, avg_row_time) | |
| except Exception as e: | |
| print(f"[{time.time() - START_TIME:.0f}s] row {r['id']} failed: {e}", flush=True) | |
| answers = [""] * max(expected, 1) | |
| explanation = "" | |
| rows_out.append({ | |
| "id": r["id"], | |
| "pred": json.dumps(answers, ensure_ascii=False), | |
| "explanation": explanation, | |
| }) | |
| row_times.append(time.time() - row_start) | |
| print( | |
| f"[{time.time() - START_TIME:.0f}s] row {i + 1}/{len(df)} done in {row_times[-1]:.0f}s, " | |
| f"{len(answers)} answers, {DEADLINE - time.time():.0f}s budget left", | |
| flush=True, | |
| ) | |
| pd.DataFrame(rows_out).to_csv("submission.csv", index=False) | |
| print(f"[{time.time() - START_TIME:.0f}s] wrote submission.csv ({len(rows_out)} rows)", flush=True) | |