typellm 0.3.0.dev1__tar.gz → 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {typellm-0.3.0.dev1/typellm.egg-info → typellm-0.3.1}/PKG-INFO +61 -63
- {typellm-0.3.0.dev1 → typellm-0.3.1}/README.md +60 -62
- {typellm-0.3.0.dev1 → typellm-0.3.1}/pyproject.toml +1 -1
- {typellm-0.3.0.dev1 → typellm-0.3.1}/tests/test_batching.py +24 -25
- {typellm-0.3.0.dev1 → typellm-0.3.1}/tests/test_concurrency.py +74 -38
- {typellm-0.3.0.dev1 → typellm-0.3.1}/tests/test_dependencies.py +18 -18
- {typellm-0.3.0.dev1 → typellm-0.3.1}/tests/test_grammar_fields.py +14 -14
- {typellm-0.3.0.dev1 → typellm-0.3.1}/tests/test_images.py +17 -17
- {typellm-0.3.0.dev1 → typellm-0.3.1}/tests/test_nullable.py +12 -12
- {typellm-0.3.0.dev1 → typellm-0.3.1}/tests/test_permutations.py +13 -13
- {typellm-0.3.0.dev1 → typellm-0.3.1}/tests/test_prefill.py +18 -30
- {typellm-0.3.0.dev1 → typellm-0.3.1}/tests/test_probabilities.py +3 -3
- {typellm-0.3.0.dev1 → typellm-0.3.1}/tests/test_prompts.py +5 -14
- {typellm-0.3.0.dev1 → typellm-0.3.1}/tests/test_protocols.py +22 -0
- {typellm-0.3.0.dev1 → typellm-0.3.1}/tests/test_text.py +18 -21
- {typellm-0.3.0.dev1 → typellm-0.3.1}/tests/test_typellm.py +261 -116
- {typellm-0.3.0.dev1 → typellm-0.3.1}/typellm/__init__.py +2 -0
- {typellm-0.3.0.dev1 → typellm-0.3.1}/typellm/cli.py +4 -3
- {typellm-0.3.0.dev1 → typellm-0.3.1}/typellm/runtime.py +177 -98
- {typellm-0.3.0.dev1 → typellm-0.3.1}/typellm/schema.py +10 -23
- {typellm-0.3.0.dev1 → typellm-0.3.1}/typellm/sglang.py +95 -86
- {typellm-0.3.0.dev1 → typellm-0.3.1/typellm.egg-info}/PKG-INFO +61 -63
- {typellm-0.3.0.dev1 → typellm-0.3.1}/LICENSE +0 -0
- {typellm-0.3.0.dev1 → typellm-0.3.1}/setup.cfg +0 -0
- {typellm-0.3.0.dev1 → typellm-0.3.1}/typellm/__main__.py +0 -0
- {typellm-0.3.0.dev1 → typellm-0.3.1}/typellm/benchmark.py +0 -0
- {typellm-0.3.0.dev1 → typellm-0.3.1}/typellm/images.py +0 -0
- {typellm-0.3.0.dev1 → typellm-0.3.1}/typellm/protocol.py +0 -0
- {typellm-0.3.0.dev1 → typellm-0.3.1}/typellm.egg-info/SOURCES.txt +0 -0
- {typellm-0.3.0.dev1 → typellm-0.3.1}/typellm.egg-info/dependency_links.txt +0 -0
- {typellm-0.3.0.dev1 → typellm-0.3.1}/typellm.egg-info/entry_points.txt +0 -0
- {typellm-0.3.0.dev1 → typellm-0.3.1}/typellm.egg-info/requires.txt +0 -0
- {typellm-0.3.0.dev1 → typellm-0.3.1}/typellm.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: typellm
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Type-Safe Decoding for Autoregressive LLMs
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Project-URL: Homepage, https://typellm.ai/
|
|
@@ -23,12 +23,14 @@ Dynamic: license-file
|
|
|
23
23
|
<a href="https://typellm.ai/">Homepage</a> •
|
|
24
24
|
<a href="https://typellm.ai/blog">Blog</a> •
|
|
25
25
|
<a href="https://typellm.ai/docs">Docs</a> •
|
|
26
|
-
<a href="https://typellm.ai/
|
|
26
|
+
<a href="https://typellm.ai/examples">Examples</a> •
|
|
27
|
+
<a href="https://typellm.ai/dashboard">API</a> •
|
|
27
28
|
<a href="https://typellm.ai/contact">Contact</a>
|
|
28
29
|
</h4>
|
|
29
30
|
</div>
|
|
30
31
|
|
|
31
32
|
## Updates
|
|
33
|
+
- **[2026/09/29]** The [TypeLLM API](https://typellm.ai) is live: typed outputs without serving a model yourself, and $5 of credit for new accounts. [Get early access](https://typellm.ai/early-access) · [Quick start](#typellm-api-cloud) · [Examples](https://typellm.ai/examples).
|
|
32
34
|
- **[2026/09/24]** Added [image input](#image-input) for vision-language models, tested with Qwen3.8-27B.
|
|
33
35
|
- **[2026/09/23]** Added [JevBench results](https://github.com/TypeLLM/TypeLLM/blob/main/evals/jevbench/README.md): TypeLLM scored 195/231 without thinking and 228/231 with thinking.
|
|
34
36
|
- **[2026/09/23]** Added [permutation averaging](#per-question-permutation-averaging) to improve the predictive distribution. See the [blog post](https://typellm.ai/blog/fair-die).
|
|
@@ -66,37 +68,33 @@ Evaluated on 231 public [JevBench](https://github.com/fstandhartinger/jevbench)
|
|
|
66
68
|
|
|
67
69
|
## Quick start
|
|
68
70
|
|
|
69
|
-
|
|
71
|
+
```bash
|
|
72
|
+
pip install -U typellm
|
|
73
|
+
```
|
|
70
74
|
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
Call the hosted TypeLLM API, or run TypeLLM against a model you serve yourself.
|
|
76
|
+
Both clients take the same requests.
|
|
73
77
|
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
### TypeLLM API (cloud)
|
|
79
|
+
|
|
80
|
+
[Sign in](https://typellm.ai/login), enter your access code on the
|
|
81
|
+
[API keys](https://typellm.ai/dashboard/keys) page and create a key. No code yet?
|
|
82
|
+
[Get early access](https://typellm.ai/early-access).
|
|
76
83
|
|
|
77
|
-
|
|
84
|
+
```bash
|
|
85
|
+
export TYPELLM_API_KEY="tl-sk-..."
|
|
78
86
|
```
|
|
79
87
|
|
|
80
|
-
|
|
88
|
+
```python
|
|
89
|
+
import os
|
|
90
|
+
from typellm import TypeLLMClient
|
|
81
91
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
A call runs for at most 300 seconds, 60 by default. Set `timeout` on the
|
|
85
|
-
client for a new default, or on `generate()` to override it for one call.
|
|
86
|
-
- Pass `questions`; `schema=`, `cancel` and `print_final_prompt` need your
|
|
87
|
-
own server. Images can be files, bytes, PIL images or data: URIs, not URLs.
|
|
88
|
-
- Each key runs a limited number of calls at once, told with the key. Calls
|
|
89
|
-
beyond it fail right away with HTTP 429.
|
|
90
|
-
- Errors raise `SGLangError` with the HTTP status in `.status`, and a call
|
|
91
|
-
that runs out of time raises `GenerationTimeout`. The service caps the
|
|
92
|
-
context size and the images, answering 400 past them, and stops string
|
|
93
|
-
answers at 128 tokens (`maxLength` is refused).
|
|
94
|
-
- Successful calls set `last_usage` from the service's input and thinking token
|
|
95
|
-
counts. Failed hosted calls may not return usage.
|
|
92
|
+
client = TypeLLMClient(api_key=os.environ["TYPELLM_API_KEY"])
|
|
93
|
+
```
|
|
96
94
|
|
|
97
|
-
|
|
95
|
+
See the [API reference](https://typellm.ai/docs/api) to call it over HTTP instead.
|
|
98
96
|
|
|
99
|
-
###
|
|
97
|
+
### Self-hosted with SGLang
|
|
100
98
|
|
|
101
99
|
Use [SGLang](https://github.com/sgl-project/sglang) to configure and serve a
|
|
102
100
|
compatible autoregressive model on your local GPU server. This example uses
|
|
@@ -106,12 +104,6 @@ to start it with prefix caching enabled.
|
|
|
106
104
|
|
|
107
105
|
See [Supported models](#supported-models) for tested checkpoints and thinking behavior.
|
|
108
106
|
|
|
109
|
-
### 2. Run TypeLLM
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
pip install -U typellm
|
|
113
|
-
```
|
|
114
|
-
|
|
115
107
|
Point `TypeLLMClient` at the SGLang server's HTTP endpoint:
|
|
116
108
|
|
|
117
109
|
```python
|
|
@@ -123,10 +115,12 @@ client = TypeLLMClient(
|
|
|
123
115
|
)
|
|
124
116
|
```
|
|
125
117
|
|
|
126
|
-
|
|
118
|
+
### Make a request
|
|
119
|
+
|
|
120
|
+
With either client:
|
|
127
121
|
|
|
128
122
|
```python
|
|
129
|
-
|
|
123
|
+
response = client.generate(
|
|
130
124
|
context="""
|
|
131
125
|
Receipt from Hilton London
|
|
132
126
|
Total: £324.50
|
|
@@ -158,10 +152,12 @@ result = client.generate(
|
|
|
158
152
|
},
|
|
159
153
|
)
|
|
160
154
|
|
|
161
|
-
print(result)
|
|
155
|
+
print(response.result)
|
|
162
156
|
```
|
|
163
157
|
|
|
164
|
-
|
|
158
|
+
`generate()` returns what the HTTP API does: the typed answers in `.result`, the
|
|
159
|
+
reasoning of each field that thought in `.thinking`, and the call's tokens in
|
|
160
|
+
`.usage`. Example `.result`:
|
|
165
161
|
|
|
166
162
|
```python
|
|
167
163
|
{
|
|
@@ -190,7 +186,7 @@ Enum choices support `string`, `integer`, and `number` types, with at most 24 va
|
|
|
190
186
|
A string without `enum` generates free text:
|
|
191
187
|
|
|
192
188
|
```python
|
|
193
|
-
|
|
189
|
+
response = client.generate(
|
|
194
190
|
context="The train ticket is for a client meeting.",
|
|
195
191
|
questions={
|
|
196
192
|
"summary": {"type": "string", "instructions": "Summarize in one sentence."},
|
|
@@ -199,12 +195,13 @@ result = client.generate(
|
|
|
199
195
|
```
|
|
200
196
|
|
|
201
197
|
Free text stops after 128 tokens. Pass `text_max_tokens=` to the client for
|
|
202
|
-
longer answers,
|
|
198
|
+
longer answers, and ask for the length you want in the field's instructions.
|
|
199
|
+
`maxLength` is not supported.
|
|
203
200
|
|
|
204
201
|
Ask for a numeric answer without enumerating every possible value:
|
|
205
202
|
|
|
206
203
|
```python
|
|
207
|
-
|
|
204
|
+
response = client.generate(
|
|
208
205
|
context="Calculate the requested value accurately.",
|
|
209
206
|
questions={
|
|
210
207
|
"answer": {
|
|
@@ -214,7 +211,7 @@ result = client.generate(
|
|
|
214
211
|
},
|
|
215
212
|
)
|
|
216
213
|
|
|
217
|
-
print(result)
|
|
214
|
+
print(response.result)
|
|
218
215
|
# {"answer": 70.0}
|
|
219
216
|
```
|
|
220
217
|
|
|
@@ -240,7 +237,7 @@ Add `"null"` to the type to allow a missing value. The field returns `None`
|
|
|
240
237
|
when the input has no value for it:
|
|
241
238
|
|
|
242
239
|
```python
|
|
243
|
-
|
|
240
|
+
response = client.generate(
|
|
244
241
|
context="Read the attached receipt.",
|
|
245
242
|
images=["receipt.jpg"],
|
|
246
243
|
questions={
|
|
@@ -266,7 +263,7 @@ Thinking is off by default. Turn it on for the fields that need it; the others
|
|
|
266
263
|
answer at once, and the fields that think reason side by side:
|
|
267
264
|
|
|
268
265
|
```python
|
|
269
|
-
|
|
266
|
+
response = client.generate(context=context, questions={
|
|
270
267
|
"total": {"type": "number"},
|
|
271
268
|
"category": {"type": "string", "enum": ["meal", "travel", "equipment"]},
|
|
272
269
|
"policy_ok": {"type": "boolean", "instructions": "Does it meet the travel policy?",
|
|
@@ -274,13 +271,12 @@ result = client.generate(context=context, questions={
|
|
|
274
271
|
})
|
|
275
272
|
```
|
|
276
273
|
|
|
277
|
-
`thinking_budget` caps a field's reasoning
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
on to the typed answer.
|
|
274
|
+
`thinking_budget` caps a field's reasoning. Without one, a field may reason until
|
|
275
|
+
the model's context is full. When reasoning reaches the budget, TypeLLM closes it
|
|
276
|
+
and moves on to the typed answer.
|
|
281
277
|
|
|
282
|
-
|
|
283
|
-
|
|
278
|
+
`response.thinking` maps each field that thought to its reasoning, and
|
|
279
|
+
`response.usage.thinking_tokens` counts the reasoning tokens.
|
|
284
280
|
|
|
285
281
|
Models with always-on thinking reason on every field; `thinking_budget` applies
|
|
286
282
|
to them too. See [Supported models](#supported-models).
|
|
@@ -291,7 +287,7 @@ Pass images with `images=` alongside the text context. The served model must be
|
|
|
291
287
|
a vision-language model, such as `Qwen/Qwen3.8-27B`.
|
|
292
288
|
|
|
293
289
|
```python
|
|
294
|
-
|
|
290
|
+
response = client.generate(
|
|
295
291
|
context="The customer says this receipt was charged twice.",
|
|
296
292
|
images=["receipt.png"],
|
|
297
293
|
questions={
|
|
@@ -311,7 +307,7 @@ Fields run together by default, and each sees only the original context.
|
|
|
311
307
|
When a field needs earlier results, list them in `depends_on`:
|
|
312
308
|
|
|
313
309
|
```python
|
|
314
|
-
|
|
310
|
+
response = client.generate(
|
|
315
311
|
context="The payments service is returning errors after a deployment.",
|
|
316
312
|
questions={
|
|
317
313
|
"system": {
|
|
@@ -349,7 +345,7 @@ step reuses its parent's cached prompt. Unknown names and cycles raise
|
|
|
349
345
|
Set `return_probabilities` on individual enum or boolean fields:
|
|
350
346
|
|
|
351
347
|
```python
|
|
352
|
-
|
|
348
|
+
response = client.generate(
|
|
353
349
|
context=context,
|
|
354
350
|
questions={
|
|
355
351
|
"expense_type": {
|
|
@@ -377,19 +373,20 @@ result = client.generate(
|
|
|
377
373
|
Only opted-in fields return `value` and `probabilities`; other fields return plain values.
|
|
378
374
|
The option is not supported on open Numeric or Text fields.
|
|
379
375
|
|
|
380
|
-
|
|
376
|
+
`temperature` is 0 by default: each field gets its most likely answer. Above 0,
|
|
377
|
+
TypeLLM samples at that temperature:
|
|
381
378
|
|
|
382
379
|
```python
|
|
383
380
|
client = TypeLLMClient(
|
|
384
381
|
"http://127.0.0.1:30000",
|
|
385
|
-
mode="sample",
|
|
386
382
|
temperature=0.8,
|
|
387
383
|
seed=42,
|
|
388
384
|
)
|
|
389
385
|
```
|
|
390
386
|
|
|
391
387
|
Sampling applies to finite candidates for Choice fields and to token generation
|
|
392
|
-
for Numeric and Text fields. `temperature`
|
|
388
|
+
for Numeric and Text fields. `generate(temperature=...)` sets it for one call.
|
|
389
|
+
The older `mode="argmax"` / `mode="sample"` still works for now but is deprecated.
|
|
393
390
|
|
|
394
391
|
A seed fixes TypeLLM's own random choices. SGLang's log probabilities can shift
|
|
395
392
|
with its prefix cache and batching, so the same seed may still give different
|
|
@@ -400,7 +397,7 @@ For a one-off request, use the convenience function:
|
|
|
400
397
|
```python
|
|
401
398
|
from typellm import run_schema
|
|
402
399
|
|
|
403
|
-
|
|
400
|
+
response = run_schema(
|
|
404
401
|
context=context,
|
|
405
402
|
questions=questions,
|
|
406
403
|
base_url="http://127.0.0.1:30000",
|
|
@@ -413,7 +410,7 @@ result = run_schema(
|
|
|
413
410
|
Add `permutations` to an `enum` question to reduce option-order bias. TypeLLM averages the probabilities and keeps the same return format.
|
|
414
411
|
|
|
415
412
|
```python
|
|
416
|
-
|
|
413
|
+
response = client.generate(
|
|
417
414
|
context="A single roll of a fair die.",
|
|
418
415
|
questions={"roll": {
|
|
419
416
|
"type": "string",
|
|
@@ -440,25 +437,25 @@ support this option.
|
|
|
440
437
|
## Serving
|
|
441
438
|
|
|
442
439
|
One `TypeLLMClient` can be shared by many threads. Each call keeps its own
|
|
443
|
-
prompts and usage, and connections to SGLang are reused.
|
|
440
|
+
prompts and usage, and connections to SGLang or the hosted API are reused.
|
|
444
441
|
|
|
445
442
|
```python
|
|
446
443
|
import threading
|
|
447
444
|
|
|
448
445
|
cancel = threading.Event()
|
|
449
|
-
|
|
446
|
+
response = client.generate(
|
|
450
447
|
context=context,
|
|
451
448
|
questions=questions,
|
|
452
449
|
seed=7, # this call's random choices only
|
|
453
450
|
timeout=30, # seconds for the whole call; raises GenerationTimeout
|
|
454
451
|
cancel=cancel, # set it from another thread; raises GenerationCancelled
|
|
455
452
|
)
|
|
456
|
-
print(
|
|
457
|
-
# Usage(requests=4, prompt_tokens=1830, cached_tokens=1504, completion_tokens=7
|
|
453
|
+
print(response.usage)
|
|
454
|
+
# Usage(input_tokens=410, thinking_tokens=0, requests=4, prompt_tokens=1830, cached_tokens=1504, completion_tokens=7)
|
|
458
455
|
```
|
|
459
456
|
|
|
460
|
-
`
|
|
461
|
-
|
|
457
|
+
`usage` reports the tokens of the call. When a call fails partway, the
|
|
458
|
+
exception's `.usage` holds what it spent. `input_tokens` is what you sent, each part
|
|
462
459
|
counted once: the context, the questions as JSON, and the images. The other
|
|
463
460
|
counts are SGLang's for the call's requests, where every prompt carries the
|
|
464
461
|
shared context. A timeout or cancel stops
|
|
@@ -497,8 +494,9 @@ Other sizes in the Qwen3.5 and Qwen3.8 families are expected to be compatible.
|
|
|
497
494
|
|
|
498
495
|
[Image input](#image-input) has been tested with `Qwen/Qwen3.8-27B`.
|
|
499
496
|
|
|
500
|
-
Use the checkpoint ID as `model=`.
|
|
501
|
-
|
|
497
|
+
Use the checkpoint ID as `model=`. TypeLLM loads the tokenizer from the server's
|
|
498
|
+
paths, then its served model name; if none of those loads locally, set
|
|
499
|
+
`tokenizer=` to the matching Hugging Face ID or local directory.
|
|
502
500
|
The tokenizer must load from standard artifacts without custom model code.
|
|
503
501
|
|
|
504
502
|
## Comparison with Jev-style models
|
|
@@ -8,12 +8,14 @@
|
|
|
8
8
|
<a href="https://typellm.ai/">Homepage</a> •
|
|
9
9
|
<a href="https://typellm.ai/blog">Blog</a> •
|
|
10
10
|
<a href="https://typellm.ai/docs">Docs</a> •
|
|
11
|
-
<a href="https://typellm.ai/
|
|
11
|
+
<a href="https://typellm.ai/examples">Examples</a> •
|
|
12
|
+
<a href="https://typellm.ai/dashboard">API</a> •
|
|
12
13
|
<a href="https://typellm.ai/contact">Contact</a>
|
|
13
14
|
</h4>
|
|
14
15
|
</div>
|
|
15
16
|
|
|
16
17
|
## Updates
|
|
18
|
+
- **[2026/09/29]** The [TypeLLM API](https://typellm.ai) is live: typed outputs without serving a model yourself, and $5 of credit for new accounts. [Get early access](https://typellm.ai/early-access) · [Quick start](#typellm-api-cloud) · [Examples](https://typellm.ai/examples).
|
|
17
19
|
- **[2026/09/24]** Added [image input](#image-input) for vision-language models, tested with Qwen3.8-27B.
|
|
18
20
|
- **[2026/09/23]** Added [JevBench results](https://github.com/TypeLLM/TypeLLM/blob/main/evals/jevbench/README.md): TypeLLM scored 195/231 without thinking and 228/231 with thinking.
|
|
19
21
|
- **[2026/09/23]** Added [permutation averaging](#per-question-permutation-averaging) to improve the predictive distribution. See the [blog post](https://typellm.ai/blog/fair-die).
|
|
@@ -51,37 +53,33 @@ Evaluated on 231 public [JevBench](https://github.com/fstandhartinger/jevbench)
|
|
|
51
53
|
|
|
52
54
|
## Quick start
|
|
53
55
|
|
|
54
|
-
|
|
56
|
+
```bash
|
|
57
|
+
pip install -U typellm
|
|
58
|
+
```
|
|
55
59
|
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
Call the hosted TypeLLM API, or run TypeLLM against a model you serve yourself.
|
|
61
|
+
Both clients take the same requests.
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
### TypeLLM API (cloud)
|
|
64
|
+
|
|
65
|
+
[Sign in](https://typellm.ai/login), enter your access code on the
|
|
66
|
+
[API keys](https://typellm.ai/dashboard/keys) page and create a key. No code yet?
|
|
67
|
+
[Get early access](https://typellm.ai/early-access).
|
|
61
68
|
|
|
62
|
-
|
|
69
|
+
```bash
|
|
70
|
+
export TYPELLM_API_KEY="tl-sk-..."
|
|
63
71
|
```
|
|
64
72
|
|
|
65
|
-
|
|
73
|
+
```python
|
|
74
|
+
import os
|
|
75
|
+
from typellm import TypeLLMClient
|
|
66
76
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
A call runs for at most 300 seconds, 60 by default. Set `timeout` on the
|
|
70
|
-
client for a new default, or on `generate()` to override it for one call.
|
|
71
|
-
- Pass `questions`; `schema=`, `cancel` and `print_final_prompt` need your
|
|
72
|
-
own server. Images can be files, bytes, PIL images or data: URIs, not URLs.
|
|
73
|
-
- Each key runs a limited number of calls at once, told with the key. Calls
|
|
74
|
-
beyond it fail right away with HTTP 429.
|
|
75
|
-
- Errors raise `SGLangError` with the HTTP status in `.status`, and a call
|
|
76
|
-
that runs out of time raises `GenerationTimeout`. The service caps the
|
|
77
|
-
context size and the images, answering 400 past them, and stops string
|
|
78
|
-
answers at 128 tokens (`maxLength` is refused).
|
|
79
|
-
- Successful calls set `last_usage` from the service's input and thinking token
|
|
80
|
-
counts. Failed hosted calls may not return usage.
|
|
77
|
+
client = TypeLLMClient(api_key=os.environ["TYPELLM_API_KEY"])
|
|
78
|
+
```
|
|
81
79
|
|
|
82
|
-
|
|
80
|
+
See the [API reference](https://typellm.ai/docs/api) to call it over HTTP instead.
|
|
83
81
|
|
|
84
|
-
###
|
|
82
|
+
### Self-hosted with SGLang
|
|
85
83
|
|
|
86
84
|
Use [SGLang](https://github.com/sgl-project/sglang) to configure and serve a
|
|
87
85
|
compatible autoregressive model on your local GPU server. This example uses
|
|
@@ -91,12 +89,6 @@ to start it with prefix caching enabled.
|
|
|
91
89
|
|
|
92
90
|
See [Supported models](#supported-models) for tested checkpoints and thinking behavior.
|
|
93
91
|
|
|
94
|
-
### 2. Run TypeLLM
|
|
95
|
-
|
|
96
|
-
```bash
|
|
97
|
-
pip install -U typellm
|
|
98
|
-
```
|
|
99
|
-
|
|
100
92
|
Point `TypeLLMClient` at the SGLang server's HTTP endpoint:
|
|
101
93
|
|
|
102
94
|
```python
|
|
@@ -108,10 +100,12 @@ client = TypeLLMClient(
|
|
|
108
100
|
)
|
|
109
101
|
```
|
|
110
102
|
|
|
111
|
-
|
|
103
|
+
### Make a request
|
|
104
|
+
|
|
105
|
+
With either client:
|
|
112
106
|
|
|
113
107
|
```python
|
|
114
|
-
|
|
108
|
+
response = client.generate(
|
|
115
109
|
context="""
|
|
116
110
|
Receipt from Hilton London
|
|
117
111
|
Total: £324.50
|
|
@@ -143,10 +137,12 @@ result = client.generate(
|
|
|
143
137
|
},
|
|
144
138
|
)
|
|
145
139
|
|
|
146
|
-
print(result)
|
|
140
|
+
print(response.result)
|
|
147
141
|
```
|
|
148
142
|
|
|
149
|
-
|
|
143
|
+
`generate()` returns what the HTTP API does: the typed answers in `.result`, the
|
|
144
|
+
reasoning of each field that thought in `.thinking`, and the call's tokens in
|
|
145
|
+
`.usage`. Example `.result`:
|
|
150
146
|
|
|
151
147
|
```python
|
|
152
148
|
{
|
|
@@ -175,7 +171,7 @@ Enum choices support `string`, `integer`, and `number` types, with at most 24 va
|
|
|
175
171
|
A string without `enum` generates free text:
|
|
176
172
|
|
|
177
173
|
```python
|
|
178
|
-
|
|
174
|
+
response = client.generate(
|
|
179
175
|
context="The train ticket is for a client meeting.",
|
|
180
176
|
questions={
|
|
181
177
|
"summary": {"type": "string", "instructions": "Summarize in one sentence."},
|
|
@@ -184,12 +180,13 @@ result = client.generate(
|
|
|
184
180
|
```
|
|
185
181
|
|
|
186
182
|
Free text stops after 128 tokens. Pass `text_max_tokens=` to the client for
|
|
187
|
-
longer answers,
|
|
183
|
+
longer answers, and ask for the length you want in the field's instructions.
|
|
184
|
+
`maxLength` is not supported.
|
|
188
185
|
|
|
189
186
|
Ask for a numeric answer without enumerating every possible value:
|
|
190
187
|
|
|
191
188
|
```python
|
|
192
|
-
|
|
189
|
+
response = client.generate(
|
|
193
190
|
context="Calculate the requested value accurately.",
|
|
194
191
|
questions={
|
|
195
192
|
"answer": {
|
|
@@ -199,7 +196,7 @@ result = client.generate(
|
|
|
199
196
|
},
|
|
200
197
|
)
|
|
201
198
|
|
|
202
|
-
print(result)
|
|
199
|
+
print(response.result)
|
|
203
200
|
# {"answer": 70.0}
|
|
204
201
|
```
|
|
205
202
|
|
|
@@ -225,7 +222,7 @@ Add `"null"` to the type to allow a missing value. The field returns `None`
|
|
|
225
222
|
when the input has no value for it:
|
|
226
223
|
|
|
227
224
|
```python
|
|
228
|
-
|
|
225
|
+
response = client.generate(
|
|
229
226
|
context="Read the attached receipt.",
|
|
230
227
|
images=["receipt.jpg"],
|
|
231
228
|
questions={
|
|
@@ -251,7 +248,7 @@ Thinking is off by default. Turn it on for the fields that need it; the others
|
|
|
251
248
|
answer at once, and the fields that think reason side by side:
|
|
252
249
|
|
|
253
250
|
```python
|
|
254
|
-
|
|
251
|
+
response = client.generate(context=context, questions={
|
|
255
252
|
"total": {"type": "number"},
|
|
256
253
|
"category": {"type": "string", "enum": ["meal", "travel", "equipment"]},
|
|
257
254
|
"policy_ok": {"type": "boolean", "instructions": "Does it meet the travel policy?",
|
|
@@ -259,13 +256,12 @@ result = client.generate(context=context, questions={
|
|
|
259
256
|
})
|
|
260
257
|
```
|
|
261
258
|
|
|
262
|
-
`thinking_budget` caps a field's reasoning
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
on to the typed answer.
|
|
259
|
+
`thinking_budget` caps a field's reasoning. Without one, a field may reason until
|
|
260
|
+
the model's context is full. When reasoning reaches the budget, TypeLLM closes it
|
|
261
|
+
and moves on to the typed answer.
|
|
266
262
|
|
|
267
|
-
|
|
268
|
-
|
|
263
|
+
`response.thinking` maps each field that thought to its reasoning, and
|
|
264
|
+
`response.usage.thinking_tokens` counts the reasoning tokens.
|
|
269
265
|
|
|
270
266
|
Models with always-on thinking reason on every field; `thinking_budget` applies
|
|
271
267
|
to them too. See [Supported models](#supported-models).
|
|
@@ -276,7 +272,7 @@ Pass images with `images=` alongside the text context. The served model must be
|
|
|
276
272
|
a vision-language model, such as `Qwen/Qwen3.8-27B`.
|
|
277
273
|
|
|
278
274
|
```python
|
|
279
|
-
|
|
275
|
+
response = client.generate(
|
|
280
276
|
context="The customer says this receipt was charged twice.",
|
|
281
277
|
images=["receipt.png"],
|
|
282
278
|
questions={
|
|
@@ -296,7 +292,7 @@ Fields run together by default, and each sees only the original context.
|
|
|
296
292
|
When a field needs earlier results, list them in `depends_on`:
|
|
297
293
|
|
|
298
294
|
```python
|
|
299
|
-
|
|
295
|
+
response = client.generate(
|
|
300
296
|
context="The payments service is returning errors after a deployment.",
|
|
301
297
|
questions={
|
|
302
298
|
"system": {
|
|
@@ -334,7 +330,7 @@ step reuses its parent's cached prompt. Unknown names and cycles raise
|
|
|
334
330
|
Set `return_probabilities` on individual enum or boolean fields:
|
|
335
331
|
|
|
336
332
|
```python
|
|
337
|
-
|
|
333
|
+
response = client.generate(
|
|
338
334
|
context=context,
|
|
339
335
|
questions={
|
|
340
336
|
"expense_type": {
|
|
@@ -362,19 +358,20 @@ result = client.generate(
|
|
|
362
358
|
Only opted-in fields return `value` and `probabilities`; other fields return plain values.
|
|
363
359
|
The option is not supported on open Numeric or Text fields.
|
|
364
360
|
|
|
365
|
-
|
|
361
|
+
`temperature` is 0 by default: each field gets its most likely answer. Above 0,
|
|
362
|
+
TypeLLM samples at that temperature:
|
|
366
363
|
|
|
367
364
|
```python
|
|
368
365
|
client = TypeLLMClient(
|
|
369
366
|
"http://127.0.0.1:30000",
|
|
370
|
-
mode="sample",
|
|
371
367
|
temperature=0.8,
|
|
372
368
|
seed=42,
|
|
373
369
|
)
|
|
374
370
|
```
|
|
375
371
|
|
|
376
372
|
Sampling applies to finite candidates for Choice fields and to token generation
|
|
377
|
-
for Numeric and Text fields. `temperature`
|
|
373
|
+
for Numeric and Text fields. `generate(temperature=...)` sets it for one call.
|
|
374
|
+
The older `mode="argmax"` / `mode="sample"` still works for now but is deprecated.
|
|
378
375
|
|
|
379
376
|
A seed fixes TypeLLM's own random choices. SGLang's log probabilities can shift
|
|
380
377
|
with its prefix cache and batching, so the same seed may still give different
|
|
@@ -385,7 +382,7 @@ For a one-off request, use the convenience function:
|
|
|
385
382
|
```python
|
|
386
383
|
from typellm import run_schema
|
|
387
384
|
|
|
388
|
-
|
|
385
|
+
response = run_schema(
|
|
389
386
|
context=context,
|
|
390
387
|
questions=questions,
|
|
391
388
|
base_url="http://127.0.0.1:30000",
|
|
@@ -398,7 +395,7 @@ result = run_schema(
|
|
|
398
395
|
Add `permutations` to an `enum` question to reduce option-order bias. TypeLLM averages the probabilities and keeps the same return format.
|
|
399
396
|
|
|
400
397
|
```python
|
|
401
|
-
|
|
398
|
+
response = client.generate(
|
|
402
399
|
context="A single roll of a fair die.",
|
|
403
400
|
questions={"roll": {
|
|
404
401
|
"type": "string",
|
|
@@ -425,25 +422,25 @@ support this option.
|
|
|
425
422
|
## Serving
|
|
426
423
|
|
|
427
424
|
One `TypeLLMClient` can be shared by many threads. Each call keeps its own
|
|
428
|
-
prompts and usage, and connections to SGLang are reused.
|
|
425
|
+
prompts and usage, and connections to SGLang or the hosted API are reused.
|
|
429
426
|
|
|
430
427
|
```python
|
|
431
428
|
import threading
|
|
432
429
|
|
|
433
430
|
cancel = threading.Event()
|
|
434
|
-
|
|
431
|
+
response = client.generate(
|
|
435
432
|
context=context,
|
|
436
433
|
questions=questions,
|
|
437
434
|
seed=7, # this call's random choices only
|
|
438
435
|
timeout=30, # seconds for the whole call; raises GenerationTimeout
|
|
439
436
|
cancel=cancel, # set it from another thread; raises GenerationCancelled
|
|
440
437
|
)
|
|
441
|
-
print(
|
|
442
|
-
# Usage(requests=4, prompt_tokens=1830, cached_tokens=1504, completion_tokens=7
|
|
438
|
+
print(response.usage)
|
|
439
|
+
# Usage(input_tokens=410, thinking_tokens=0, requests=4, prompt_tokens=1830, cached_tokens=1504, completion_tokens=7)
|
|
443
440
|
```
|
|
444
441
|
|
|
445
|
-
`
|
|
446
|
-
|
|
442
|
+
`usage` reports the tokens of the call. When a call fails partway, the
|
|
443
|
+
exception's `.usage` holds what it spent. `input_tokens` is what you sent, each part
|
|
447
444
|
counted once: the context, the questions as JSON, and the images. The other
|
|
448
445
|
counts are SGLang's for the call's requests, where every prompt carries the
|
|
449
446
|
shared context. A timeout or cancel stops
|
|
@@ -482,8 +479,9 @@ Other sizes in the Qwen3.5 and Qwen3.8 families are expected to be compatible.
|
|
|
482
479
|
|
|
483
480
|
[Image input](#image-input) has been tested with `Qwen/Qwen3.8-27B`.
|
|
484
481
|
|
|
485
|
-
Use the checkpoint ID as `model=`.
|
|
486
|
-
|
|
482
|
+
Use the checkpoint ID as `model=`. TypeLLM loads the tokenizer from the server's
|
|
483
|
+
paths, then its served model name; if none of those loads locally, set
|
|
484
|
+
`tokenizer=` to the matching Hugging Face ID or local directory.
|
|
487
485
|
The tokenizer must load from standard artifacts without custom model code.
|
|
488
486
|
|
|
489
487
|
## Comparison with Jev-style models
|