Skip to main content

🎯 A Thai Jev is here! OpenThai-SystemOne

Announced 20 September 2026

A Thai Jev is here: an open-source model by the OpenThai team, named OpenThai-SystemOne

0.8B parametersv0.374.3 public benchFree, Apache-2.0Thai + English

Text + questions in → answers with a confidence score out, in a single pass.

Five days after TypeSafe launched Jev (a "System One model" that generates no text but answers decisions with probabilities in one pass), the OpenThai team has released an open-source version for Thai + English, named OpenThai-SystemOne.

  • 0.8B parameters, runs on a laptop
  • Qwen3.5-0.8B base, continued-pretrained on ~5B Thai tokens, with the 248k-word LM head replaced by a "256-slot decision head"
  • Feed it a state (text/JSON) + choice / score / yes-no questions → every question answered with confidence in one forward pass
  • On the same 13-subset public benchmark as Bespoke-Nimble-9B: ours 74.3 (v0.3) / Nimble-9B 74.8 / Jev 76.0 (within 0.5 of the 9B, ahead on 8 of 13 subsets). Thai tasks: intent 90.0%, news topic 98.1%, XNLI-th 77.1%
  • Use it for: Text Classification, Sentiment Analysis, Agentic Computer Use (picking the action for an AI agent)
  • Apache-2.0, all of it open: weights, training scripts, configs and synthetic data

The API contract mirrors TypeSafe AI's POST /v1/systemone (Jev), so existing code can point at this model unchanged.

Try it in your browser​

Play with OpenThai-SystemOne in your browser + API docsiapp.co.th/docs/llm/openthai-systemone

Download and install​

OpenThai-SystemOne — weights and model card with benchmark tableshuggingface.co/iapp/OpenThai-SystemOne

Code, configs and every training script — GitHubgithub.com/iapp-technology/openthai-systemone

pip install openthai-systemone # or: pip install "git+https://github.com/iapp-technology/openthai-systemone"

Try it​

A real exchange with the model: one customer ticket, two questions, both answered in a single forward pass (91 input tokens, 0 output tokens).

curl -s https://api.iapp.co.th/v3/store/openthai/systemone -H "apikey: $IAPP_API_KEY" -H "content-type: application/json" -d '{
"state": {"ticket": "โดนหักเงินซ้ำสองครั้ง ขอเงินคืนด่วน"},
"questions": {
"department": {"type": "choice", "instructions": "ทีมใดควรรับผิดชอบ",
"criteria": {"billing": "การเงิน/คืนเงิน", "technical": "ระบบใช้งานไม่ได้", "sales": null}},
"refund": {"type": "noul", "instructions": "ลูกค้าขอเงินคืนหรือไม่"}
}}'
{"answers": {"department": {"choice": "billing", "probabilities": {"billing": 0.972, "technical": 0.009, "sales": 0.018}, "confidence": 0.87},
"refund": {"noul": 0.954}}, "usage": {"input_tokens": 91, "output_tokens": 0}}

🔑 Public API (free preview)

The endpoint https://api.iapp.co.th/v3/store/openthai/systemone takes any iApp API key (free to create). During the preview it costs 0 credits, limited to 1,000 decisions per day per key and 100 requests per minute; per-decision pricing will be announced in October 2026. The API runs v0.3, the same checkpoint as the Hugging Face weights. Details and a playground are at iapp.co.th/docs/llm/openthai-systemone, or run the model on your own machine as shown below.

Run it locally (CUDA, Apple MPS or CPU):

from openthai_systemone import SystemOneClient, Choice, Score, Noul

c = SystemOneClient("iapp/OpenThai-SystemOne")
r = c.system_one(
state={"ticket": "โดนหักเงินซ้ำสองครั้ง ขอเงินคืนด่วน โทรไปสามรอบแล้วไม่มีใครรับ"},
questions={
"department": Choice(instructions="ทีมใดควรรับผิดชอบ", criteria={"billing": "การเงิน/คืนเงิน", "technical": "ระบบใช้งานไม่ได้", "sales": None}),
"frustration": Score(instructions="ลูกค้าหงุดหงิดแค่ไหน", criteria=["ใจเย็น", "หงุดหงิดแต่สุภาพ", "โกรธมาก"]),
"refund": Noul(instructions="ลูกค้าขอเงินคืนอย่างชัดเจนหรือไม่"),
},
)
print(r.answers["department"].choice, r.answers["department"].probabilities)
print(r.answers["frustration"].score, r.answers["refund"].noul)

Serve it over HTTP with the same contract as POST /v1/systemone:

OPENTHAI_SYSTEMONE_MODEL=iapp/OpenThai-SystemOne uvicorn openthai_systemone.server:app --port 8000

Or load it with plain transformers (the remote code ships in the model repo):

from transformers import AutoModel
model = AutoModel.from_pretrained("iapp/OpenThai-SystemOne", trust_remote_code=True)

🎮 Demo: playing Doom, no vision, no text​

The model controls a Doom marine (ViZDoom) in real time. Every 4 game tics the engine's symbolic state is serialised to text, for example:

health 100/100 | ammo 50 | kills 1
crosshair: empty; nearest visible enemy 39deg to the RIGHT
enemies: Zombieman 5m right -39deg VISIBLE; ChaingunGuy 19m right -24deg; Zombieman 19m ahead -12deg
items: GreenArmor 41m right -18deg
depth ahead: 35/255 (obstacle near)
last actions: ATTACK ATTACK ATTACK ATTACK

and the model answers two typed questions in one forward pass: a choice over the 7 actions (the key that gets pressed) and a noul "is an enemy in the crosshair". About 41 ms per decision on one H100 (about 24 decisions per second), 0 output tokens, and no Doom data in training: everything comes from reading the state and the option descriptions. It misses shots and dies on hard levels; the point is the speed and the calibrated probabilities, the same mechanics that route tickets or pick UI elements for an agent.

Run it on your machine (iApp API key or the local weights, live HUD in Thai or English, optional recording): github.com/iapp-technology/openthai-systemone-doom

🇹🇭 Thai showcase (real v0.3 outputs via the API, 22 September 2026)​

Seven everyday Thai tasks, each answered in one forward pass; the probabilities are the model's actual output.

TaskState (abridged)QuestionsAnswers
Support ticket triage"Transfer app broken since last night, error 502 every time … I need to pay my kid's tuition tomorrow" (Thai)which team (4) · urgency (score) · is the wording politetechnical 72% (billing 27%) · 2.13 = urgent (85%) · polite 1% (wrong, see note)
News topic"Cabinet approves a 400-baht nationwide minimum wage from 1 January; industry federation worried about SMEs"topic (8) · is it about laboureconomy 62% (labour 36%) · 98%
Assistant intent"Set an alarm for 6:30 tomorrow, and play some soft music when I wake up"intent (10)alarm set 99.5%
Comment moderationan abusive Thai commenttoxic · sentimenttoxic 94% · negative 97%
Grounded QApassage about Rama VIII Bridge (opened 7 May 2002, 475 m long)"how long is the bridge" answerable · "is the construction budget stated"98.5% · 0.6%
Agent tool selectiontask "book a table for 4 tonight at 8pm"; history shows search_restaurants already found a free tablenext tool (5)make_reservation 96.5%
RAG relevance + extractionquestion on withholding tax for building rent; passage states 5%passage relevant · rate98% · 5% (76%)

Note on the first row: the ticket uses the polite "รบกวน…นะครับ", so the correct answer is yes; the model said no at 99%. Politeness judgement on formal Thai is a known gap and gets a targeted set in v0.4. We keep the miss here because a showcase that only shows hits is not useful.

Three question types​

TypeYou giveYou get
choiceinstructions + up to 255 options (name → description or null)choice, probabilities for every option, confidence
scoreinstructions + 2–10 ordered level descriptionsscore (probability-weighted, can be fractional), probabilities, confidence
noula yes/no questionnoul = p(yes)

Every question in a request is answered together in one forward pass. confidence is 1 − normalised entropy, and abstain is the probability that none of the offered options fits.

📊 Numbers (v0.3, 22 September 2026)​

All numbers are zero-shot: the model sees only the state, the instructions and the option names and descriptions. They are for the calibrated v0.3 release; earlier versions are in the versions table below.

Bespoke Labs' 13-subset public benchmark (same sources, splits, instructions and sampler used to compare against Jev). choice and noul report accuracy, score reports exact-level match. Bold marks subsets where the 0.8B model is ahead of Nimble-9B.

SubsetTypenOpenThai-SystemOne 0.8BNimble-9BJev 1.13.0our ECE
aegis2noul25083.281.280.40.065
boolqnoul30079.786.089.70.049
civil_commentsnoul30079.070.381.00.087
helpsteer2score25041.639.034.10.371
massive-de-DEchoice35088.383.486.90.057
massive-en-USchoice35088.386.987.40.070
multinlichoice29989.085.382.90.053
pawsnoul25094.082.889.20.035
pubmedqachoice25064.075.677.20.259
squad2noul29989.380.682.90.041
summeval-consistencyscore14475.075.781.20.076
summeval-relevancescore24021.749.235.00.356
vitaminc-devchoice59972.576.680.10.118
Macro average74.374.876.0

Honest reading: the 0.8B model averages 74.3, within 0.5 points of Nimble-9B (74.8), and is ahead on 8 of 13 subsets (aegis2, civil_comments, helpsteer2, massive-de, massive-en, multinli, paws, squad2). It is still clearly behind on pubmedqa (64.0) and on both summary-rating tasks (relevance 21.7; consistency 75.0, a regression from 84.0 in v0.2). ECE improved on 11 of 13 subsets. Nimble-9B and Jev numbers are as published by Bespoke Labs (18 September 2026); ours are measured with scripts/06_eval.py on scripts/06b_public_benchmarks.py rebuilds of the same subsets. Note: from v0.3 the train splits of SQuAD2, BoolQ, PAWS, Aegis2 and MASSIVE-de are in the training mix; the benchmark subsets still use validation/test splits only. For scale, Bespoke reports raw Qwen3.5-0.8B at 45.4 on their private 324-item holdout (Nimble-9B 90.1, Jev 93.2), not on this bench.

Thai held-out sets (full eval sets; sets marked * were held out as whole datasets; lower ECE means confidence is more trustworthy):

SetTaskAccuracyECENote
Prachathai67knews topic (choice)98.10.004v0.2: 97.6
Prachathai67kper-topic yes/no (noul)94.20.008
MASSIVE-th60-way intent (choice)90.00.043v0.2: 88.6
XNLI-thnatural language inference (choice)77.10.045
XNLI-thentailment yes/no (noul)84.30.049
SIB-200 Thai*7-way topic (choice)77.90.084recovered from 74.0 in v0.2
Thai contrastive pairs (one-fact flips)choice / noul / score80.7 / 83.5 / 78.60.098 / 0.109 / 0.156
Wongnaireview stars 1–5 (score)63.5 exact (MAE 0.44)0.039
xLAM tool selection (English)tool choice (choice)99.40.006
Wisesight*4-class social sentiment (choice)51.60.353still the weakest Thai set; use order-invariant mode
banking77* (English)77-way intent (choice)45.40.236v0.2: 37.8 (61.7 with order-invariant mode on v0.2)

Treat confidence as reliable on the Thai sets and on NLI and topic tasks (ECE at or below 0.05). On the English multi-level rating tasks (helpsteer2, summeval-relevance) ECE is still high, so route low-confidence cases to a bigger model or a human.

Order-invariant mode — like Jev, a single forward pass is sensitive to the order of the options (measured on v0.2: 5–12% of arg-max choices flip under a random permutation for up to 10 options, 72% on 77-way banking77). The client and the API have an order-invariant mode that averages the answer over several cyclic option orders in one batched pass (order_invariant: true / permutations: n, automatic for choice questions with more than 10 options). On 360 held-out Thai questions: flips 18.9% → 3.3%, mean probability shift 0.19 → 0.08, accuracy 68.6 → 73.6, banking77 41.7 → 63.3. Cost is about 2× latency (batched), never more tokens in usage.input_tokens.

Latency — a 3-question request takes about 40 ms on an H100 and 154 ms on a MacBook M3 Max (Apple GPU via MPS); 170–220 ms round trip from a laptop through the API including network. A 255-option question takes 44 ms on an H100 and 1.5 s on the MacBook. Pure CPU is still slow (about 4.6 s), so use a GPU or MPS.

⚙️ How it works​

Architecture: state and questions → Qwen3.5-0.8B text tower → hidden state at each ts_answer token → 256-slot decision head → masked softmax → choice / score / noul

  • Backbone — the text tower of Qwen3.5-0.8B-Base (24 layers, hybrid Gated-DeltaNet / attention, 262k context) with the vision encoder removed, then continued-pretrained on ~5B tokens of Thai (web, Wikipedia, parallel Thai↔English, and machine-state text such as accessibility trees and JSON).
  • Decision head — the 248k-token LM head is replaced by a 256-way slot head. Options are introduced by control tokens <|ts_opt_0|> … <|ts_opt_254|>; the hidden state at each question's <|ts_answer|> token is projected to 256 logits, slots beyond the number of options are masked, and a softmax gives the distribution. Slot 255 is abstain (none of the options fits).
  • Reading the answer — choice = argmax, score = Σ pᵢ·i over the ordered levels, noul = p(yes).
  • No position bias — option order is shuffled in training and abstain examples are mixed in, so this is an architecture, not a trick of reading logprobs over the letters A/B/C.

🔬 Training recipe​

Every stage is in the GitHub repo, one script per stage, reproducible on a single H100.

  1. scripts/00_prepare_base.py — drop Qwen3.5-0.8B's vision encoder, keep the text tower (752M parameters).
  2. scripts/01_cpt_data.py + scripts/02_cpt_train.py — Thai-heavy continued pretraining (filtered FineWeb-2 Thai, Thai Wikipedia, Thai instruction and summary text, OPUS Thai↔English, FineWeb-Edu replay, synthetic accessibility trees and JSON). One H100, about 40 hours. Held-out perplexity 31.4 → 5.38.
  3. scripts/03_decision_data.py — 1.81M decision records converted from 21 public datasets (Thai: wisesight*, wongnai, prachathai, XNLI-th, MASSIVE-th, SIB-200*, ThaiExam, iapp_wiki_qa, Thai toxicity; English: MNLI, MMLU, ARC, HelpSteer2, AG News, DBpedia, CLINC, banking77*, Mind2Web, xLAM tools). Sets marked * were held out entirely for evaluation.
  4. scripts/03b_synth_generate.py — about 100k synthetic Thai/English decision tasks generated with Qwen3.6-35B-A3B, each blind-relabelled and filtered for answer leakage (dataset iapp/openthai2.0-systemone-synth).
  5. scripts/04_decision_train.py — head swap + 12k-step SFT, then a calibration stage (Brier loss + per-type temperature) so that higher confidence really means higher accuracy. v0.2 continues from v0.1 for 3,000 more steps with a 22k-record synthetic Thai social-sentiment set (4-class with a "question" class, 3/5-class variants, yes/no flags, 5-level score), then re-calibrates. v0.3 continues from v0.2 for 5,000 more steps with 177k real train-split records (SQuAD2, BoolQ, PubMedQA-artificial, PAWS, Aegis2, ToxicChat, XQuAD-th, MASSIVE-de) and 78k targeted synthetic records for the weak spots (grounded yes/no QA, summary rating, 40–120-way near-duplicate intents, safety, adversarial paraphrases), lowers the sentiment-set weight from 4× to 2×, then re-calibrates.
  6. scripts/06b_public_benchmarks.py — rebuilds Bespoke Nimble's 13-subset public benchmark for a like-for-like comparison.

👨‍👩‍👧 The OpenThai 2.0 family​

ModelWhatSizeStatus
OpenThai 2.0Thai chat and reasoning LLM, reads images27Breleased
OpenThai 2.0 LegalThai law model for RAG30B-A3Breleased
OpenThai-SystemOneThai + English decision model (no text generation)0.8Breleased
OpenThai-SystemOne CPTthe Thai-pretrained base underneath (research use)0.8Bprivate for now

⚠️ Limits​

  • Version 0.3 — a 0.8B model is not a reasoning model. It will not do multi-step verification or arithmetic.
  • It can only answer with the options you give, but it can still be wrong — it cannot emit an invalid option, but it can pick the wrong one. Use the confidence field and route low-confidence cases to a bigger model or a human.
  • Text only — no images. Up to 255 options per question (bucket into groups for more) and 64k tokens per request.
  • Pure CPU is still slow — use a GPU or Apple MPS.
  • Known v0.3 weak spots — the two 5-level rating tasks (summary relevance 22, helpfulness 42), fine-grained 77-way English intents (banking77 45 single-order), Thai social sentiment (Wisesight 52), PubMedQA 3-way (64), and politeness judgement on formal Thai (targeted set in v0.4).
  • Sensitive to option order — like Jev, a single-pass answer can change when the options are shuffled. Use order_invariant: true (automatic above 10 options) at about 2× latency.
  • English calibration is weaker than Thai — median ECE 0.15 versus 0.05 or below, because the training mix is Thai-heavy by design.

🗓 Versions​

VersionDateChangePublic macroWisesight
v0.322 Sep 2026+5,000 SFT steps from v0.2 with 177k real train-split records + 78k targeted synthetic records for the weak spots (grounded QA, summary rating, fine-grained intents, safety, paraphrase), re-calibrated74.351.6
v0.221 Sep 2026+3,000 SFT steps from v0.1 with a 22k-record synthetic Thai social-sentiment set, re-calibrated; order-invariant mode in the client and API63.251.5
v0.120 Sep 2026initial release: Thai CPT 4.47B tokens, 12k-step SFT, calibration61.938.7

v0.3 moved SQuAD2 50.2 → 89.3, PAWS 68.0 → 94.0, Aegis2 61.6 → 83.2, MASSIVE-de 67.4 → 88.3, BoolQ 64.7 → 79.7, MASSIVE-en 79.1 → 88.3, PubMedQA 56.4 → 64.0, SummEval-relevance 14.2 → 21.7, VitaminC 68.6 → 72.5 and MultiNLI 87.3 → 89.0, with ECE improving on 11 of 13 subsets. SummEval-consistency regressed 84.0 → 75.0 (the synthetic consistency set only covers levels 1/3/5; levels 2/4 will be added). Thai held-out sets are all flat or up (SIB-200 74.0 → 77.9, banking77 37.8 → 45.4). The full changelog is in the model card.

🤝 Help us build it​

  • More Thai test sets — especially tasks with many options and UI/agent tasks. Open an issue on GitHub.
  • GGUF / ONNX / MLX — the head is a plain Linear layer, so conversions are straightforward if you know the toolchain.
  • Domain LoRA adapters — share yours; the plan is "one base, N adapters".
  • Talk to us on the OpenThaiGPT Discord and the Facebook group.

📄 Licence and credits​

Apache-2.0. Built by iApp Technology / OpenThaiGPT on Qwen3.5-0.8B-Base (Apache-2.0). Inspired by TypeSafe AI's System One models (Jev); this is an independent open re-implementation and is not affiliated with TypeSafe AI.

🙏 Thank you, Siam AI​

Siam AI Corporation

The Thai continued pretraining, SFT and every evaluation ran on NVIDIA H100 GPUs generously provided by Siam AI Corporation. Thank you for your continued support of open Thai AI.