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
File size: 10,095 Bytes
ef8d39f c02ca54 ef8d39f 117e057 530ac51 1336c78 117e057 1336c78 117e057 ef8d39f 117e057 ef8d39f 3e6db66 6245d15 ef8d39f 17613f9 ef8d39f 465f972 ef8d39f 80cecee 117e057 ef8d39f 117e057 ef8d39f 465f972 ef8d39f 1964d3a 9b580c1 ef8d39f 47da312 ef8d39f 279a593 ef8d39f 117e057 ef8d39f 117e057 ef8d39f 279a593 cb33f4a be726fd 279a593 ef8d39f 279a593 ef8d39f 279a593 117e057 279a593 117e057 279a593 ef8d39f 47da312 17613f9 279a593 ef8d39f 465f972 ef8d39f 279a593 ef8d39f 465f972 ef8d39f 465f972 ef8d39f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | """
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)
|