Text Classification
Transformers
Safetensors
Thai
English
openthai_systemone
feature-extraction
system-one
decision-model
thai
qwen3.5
quantized
compressed-tensors
llm-compressor
custom_code
Instructions to use iapp/OpenThai-SystemOne-FP8-Dynamic with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use iapp/OpenThai-SystemOne-FP8-Dynamic with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="iapp/OpenThai-SystemOne-FP8-Dynamic", trust_remote_code=True)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("iapp/OpenThai-SystemOne-FP8-Dynamic", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
File size: 6,992 Bytes
d340bf5 | 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 | """Request / response types.
Field names deliberately mirror the TypeSafe `POST /v1/systemone` contract so that
code written against the TypeSafe SDK can be pointed at OpenThai-SystemOne unchanged:
state : str | dict | list -- the thing to judge
questions : {id: Choice|Score|Noul} -- typed questions
answers : {id: ChoiceAnswer|ScoreAnswer|NoulAnswer}
"""
from __future__ import annotations
from typing import Any, Dict, List, Literal, Optional, Union
from pydantic import BaseModel, Field, field_validator, model_validator
MAX_OPTIONS = 255 # single-stage cardinality limit (slots 0..254); slot 255 = abstain
MIN_SCORE_LEVELS = 2
MAX_SCORE_LEVELS = 10
class Noul(BaseModel):
"""A yes/no question. Returns p(yes)."""
type: Literal["noul"] = "noul"
instructions: str
criteria: Optional[Dict[str, Optional[str]]] = None # {"true": "...", "false": "..."}
@field_validator("criteria")
@classmethod
def _check_criteria(cls, v):
if v is None:
return v
extra = set(v) - {"true", "false"}
if extra:
raise ValueError(f"noul criteria keys must be 'true'/'false', got {sorted(extra)}")
return v
class Choice(BaseModel):
"""Pick one option. `criteria` maps option name -> description (or null)."""
type: Literal["choice"] = "choice"
instructions: str
criteria: Dict[str, Optional[str]]
@field_validator("criteria")
@classmethod
def _check_criteria(cls, v):
if len(v) < 1:
raise ValueError("choice needs at least one option")
if len(v) > MAX_OPTIONS:
raise ValueError(f"choice supports at most {MAX_OPTIONS} options in one stage")
for k in v:
if not str(k).strip():
raise ValueError("option names must be non-empty")
return v
class Score(BaseModel):
"""Rate the state against ordered levels; `criteria[i]` describes level i (low -> high)."""
type: Literal["score"] = "score"
instructions: str
criteria: List[str]
@field_validator("criteria")
@classmethod
def _check_criteria(cls, v):
if not (MIN_SCORE_LEVELS <= len(v) <= MAX_SCORE_LEVELS):
raise ValueError(f"score needs {MIN_SCORE_LEVELS}..{MAX_SCORE_LEVELS} levels")
return v
class Point(BaseModel):
"""Vision variant: locate what `instructions` describes on image `image` -> normalised (x, y). Answer may abstain."""
type: Literal["point"] = "point"
instructions: str
image: Optional[str] = None # id of one of the request's images; None = the first image
Question = Union[Noul, Choice, Score, Point]
class NoulAnswer(BaseModel):
type: Literal["noul"] = "noul"
noul: float
class ChoiceAnswer(BaseModel):
type: Literal["choice"] = "choice"
choice: str
probabilities: Dict[str, float]
confidence: float
abstain: Optional[float] = None # OpenThai extension: p(none of the options); not in TypeSafe
class ScoreAnswer(BaseModel):
type: Literal["score"] = "score"
score: float
legend: Dict[int, str]
probabilities: Dict[str, float]
confidence: float
class PointCandidate(BaseModel):
x: float
y: float
p: float
bbox: Optional[List[float]] = None # normalised [x0, y0, x1, y1] of the token cluster
class PointAnswer(BaseModel):
type: Literal["point"] = "point"
x: float
y: float
confidence: float # probability mass of the winning cluster
candidates: List[PointCandidate] = []
abstain: Optional[float] = None # p(target not on the image)
image: Optional[str] = None
Answer = Union[NoulAnswer, ChoiceAnswer, ScoreAnswer, PointAnswer]
class ImageInput(BaseModel):
"""One image attached to the state (vision variant). Give exactly one of data / url / path."""
id: str = "img"
data: Optional[str] = None # data URI or base64
url: Optional[str] = None
path: Optional[str] = None # local file (SDK use only; the server ignores it)
role: Optional[str] = None # screenshot | frame | photo | document | ...
detail: Literal["auto", "high"] = "auto"
@model_validator(mode="after")
def _one_source(self):
if sum(v is not None for v in (self.data, self.url, self.path)) != 1:
raise ValueError("image needs exactly one of data / url / path")
return self
@property
def source(self) -> str:
return self.data or self.url or self.path # type: ignore[return-value]
class Usage(BaseModel):
input_tokens: int
output_tokens: int = 0
permutations: int = 1 # OpenThai extension: number of option orders averaged (order-invariant mode)
class SystemOneRequest(BaseModel):
state: Union[str, Dict[str, Any], List[Any]]
model: str = "openthai-systemone"
questions: Dict[str, Question] = Field(discriminator=None)
# OpenThai extensions. order_invariant=True averages the answer over several option orders (removes position
# bias, ~2x latency); None = automatic (on for choice questions with > 10 options); permutations overrides the count.
order_invariant: Optional[bool] = None
permutations: Optional[int] = Field(default=None, ge=1, le=32)
# vision variant: up to 4 images; text-only models reject requests that carry images or point questions.
images: Optional[List[ImageInput]] = None
@model_validator(mode="after")
def _non_empty(self):
if not self.questions:
raise ValueError("at least one question is required")
if self.images is not None:
if len(self.images) > 4:
raise ValueError("at most 4 images per request")
ids = [im.id for im in self.images]
if len(set(ids)) != len(ids):
raise ValueError("image ids must be unique")
for im in self.images:
if im.path is not None:
raise ValueError("image 'path' is not accepted over the API; send data or url")
for qid, q in self.questions.items():
if isinstance(q, Point):
if not self.images:
raise ValueError(f"point question {qid!r} needs at least one image")
if q.image is not None and q.image not in {im.id for im in self.images}:
raise ValueError(f"point question {qid!r} references unknown image {q.image!r}")
return self
class SystemOneResponse(BaseModel):
model: str
answers: Dict[str, Answer]
usage: Usage
def parse_question(obj: Union[Question, Dict[str, Any]]) -> Question:
"""Accept a dict (raw JSON) or an already-typed question."""
if isinstance(obj, (Noul, Choice, Score, Point)):
return obj
t = obj.get("type")
if t == "noul":
return Noul(**obj)
if t == "choice":
return Choice(**obj)
if t == "score":
return Score(**obj)
if t == "point":
return Point(**obj)
raise ValueError(f"unknown question type: {t!r}")
|