typellm 0.3.0.dev2__tar.gz → 0.4.0__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.dev2/typellm.egg-info → typellm-0.4.0}/PKG-INFO +79 -65
- {typellm-0.3.0.dev2 → typellm-0.4.0}/README.md +78 -64
- {typellm-0.3.0.dev2 → typellm-0.4.0}/pyproject.toml +1 -1
- {typellm-0.3.0.dev2 → typellm-0.4.0}/tests/test_batching.py +129 -25
- {typellm-0.3.0.dev2 → typellm-0.4.0}/tests/test_concurrency.py +42 -38
- {typellm-0.3.0.dev2 → typellm-0.4.0}/tests/test_dependencies.py +18 -18
- {typellm-0.3.0.dev2 → typellm-0.4.0}/tests/test_grammar_fields.py +14 -14
- {typellm-0.3.0.dev2 → typellm-0.4.0}/tests/test_images.py +17 -17
- {typellm-0.3.0.dev2 → typellm-0.4.0}/tests/test_nullable.py +12 -12
- {typellm-0.3.0.dev2 → typellm-0.4.0}/tests/test_permutations.py +12 -12
- {typellm-0.3.0.dev2 → typellm-0.4.0}/tests/test_prefill.py +18 -30
- {typellm-0.3.0.dev2 → typellm-0.4.0}/tests/test_probabilities.py +3 -3
- {typellm-0.3.0.dev2 → typellm-0.4.0}/tests/test_prompts.py +5 -14
- {typellm-0.3.0.dev2 → typellm-0.4.0}/tests/test_protocols.py +22 -0
- {typellm-0.3.0.dev2 → typellm-0.4.0}/tests/test_text.py +18 -21
- {typellm-0.3.0.dev2 → typellm-0.4.0}/tests/test_typellm.py +235 -116
- {typellm-0.3.0.dev2 → typellm-0.4.0}/typellm/__init__.py +2 -0
- {typellm-0.3.0.dev2 → typellm-0.4.0}/typellm/cli.py +1 -1
- {typellm-0.3.0.dev2 → typellm-0.4.0}/typellm/runtime.py +162 -87
- {typellm-0.3.0.dev2 → typellm-0.4.0}/typellm/schema.py +85 -29
- {typellm-0.3.0.dev2 → typellm-0.4.0}/typellm/sglang.py +62 -66
- {typellm-0.3.0.dev2 → typellm-0.4.0/typellm.egg-info}/PKG-INFO +79 -65
- {typellm-0.3.0.dev2 → typellm-0.4.0}/LICENSE +0 -0
- {typellm-0.3.0.dev2 → typellm-0.4.0}/setup.cfg +0 -0
- {typellm-0.3.0.dev2 → typellm-0.4.0}/typellm/__main__.py +0 -0
- {typellm-0.3.0.dev2 → typellm-0.4.0}/typellm/benchmark.py +0 -0
- {typellm-0.3.0.dev2 → typellm-0.4.0}/typellm/images.py +0 -0
- {typellm-0.3.0.dev2 → typellm-0.4.0}/typellm/protocol.py +0 -0
- {typellm-0.3.0.dev2 → typellm-0.4.0}/typellm.egg-info/SOURCES.txt +0 -0
- {typellm-0.3.0.dev2 → typellm-0.4.0}/typellm.egg-info/dependency_links.txt +0 -0
- {typellm-0.3.0.dev2 → typellm-0.4.0}/typellm.egg-info/entry_points.txt +0 -0
- {typellm-0.3.0.dev2 → typellm-0.4.0}/typellm.egg-info/requires.txt +0 -0
- {typellm-0.3.0.dev2 → typellm-0.4.0}/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
|
+
Version: 0.4.0
|
|
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,16 +271,35 @@ result = client.generate(context=context, questions={
|
|
|
274
271
|
})
|
|
275
272
|
```
|
|
276
273
|
|
|
277
|
-
`thinking_budget` caps a field's reasoning
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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.
|
|
277
|
+
|
|
278
|
+
`response.thinking` maps each field that thought to its reasoning, and
|
|
279
|
+
`response.usage.thinking_tokens` counts the reasoning tokens.
|
|
280
|
+
|
|
281
|
+
### Thinking effort
|
|
282
|
+
|
|
283
|
+
`thinking_effort` sets the budget by level instead: `"none"` does not think,
|
|
284
|
+
`"low"` thinks up to 512 tokens, `"medium"` up to 2048 and `"high"` up to 4096.
|
|
281
285
|
|
|
282
|
-
|
|
283
|
-
reasoning,
|
|
286
|
+
With `"thinking": "auto"`, TypeLLM picks the level on each call. It first asks
|
|
287
|
+
the model how much reasoning the field needs, without thinking, then answers the
|
|
288
|
+
field at that level. The field's own prompt is unchanged, and
|
|
289
|
+
`response.thinking_effort` reports the level each `"auto"` field got:
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
response = client.generate(context=context, questions={
|
|
293
|
+
"category": {"type": "string", "enum": ["meal", "travel", "equipment"]},
|
|
294
|
+
"policy_ok": {"type": "boolean", "instructions": "Does it meet the travel policy?",
|
|
295
|
+
"thinking": "auto"},
|
|
296
|
+
})
|
|
297
|
+
response.thinking_effort # {"policy_ok": "low"}
|
|
298
|
+
```
|
|
284
299
|
|
|
285
|
-
|
|
286
|
-
|
|
300
|
+
`"auto"` adds one quick step after the field's dependencies and before the field;
|
|
301
|
+
that step sees the same dependency results. `thinking_effort` and
|
|
302
|
+
`thinking_budget` cannot be combined with `"auto"` or with each other.
|
|
287
303
|
|
|
288
304
|
## Image input
|
|
289
305
|
|
|
@@ -291,7 +307,7 @@ Pass images with `images=` alongside the text context. The served model must be
|
|
|
291
307
|
a vision-language model, such as `Qwen/Qwen3.8-27B`.
|
|
292
308
|
|
|
293
309
|
```python
|
|
294
|
-
|
|
310
|
+
response = client.generate(
|
|
295
311
|
context="The customer says this receipt was charged twice.",
|
|
296
312
|
images=["receipt.png"],
|
|
297
313
|
questions={
|
|
@@ -311,7 +327,7 @@ Fields run together by default, and each sees only the original context.
|
|
|
311
327
|
When a field needs earlier results, list them in `depends_on`:
|
|
312
328
|
|
|
313
329
|
```python
|
|
314
|
-
|
|
330
|
+
response = client.generate(
|
|
315
331
|
context="The payments service is returning errors after a deployment.",
|
|
316
332
|
questions={
|
|
317
333
|
"system": {
|
|
@@ -349,7 +365,7 @@ step reuses its parent's cached prompt. Unknown names and cycles raise
|
|
|
349
365
|
Set `return_probabilities` on individual enum or boolean fields:
|
|
350
366
|
|
|
351
367
|
```python
|
|
352
|
-
|
|
368
|
+
response = client.generate(
|
|
353
369
|
context=context,
|
|
354
370
|
questions={
|
|
355
371
|
"expense_type": {
|
|
@@ -401,7 +417,7 @@ For a one-off request, use the convenience function:
|
|
|
401
417
|
```python
|
|
402
418
|
from typellm import run_schema
|
|
403
419
|
|
|
404
|
-
|
|
420
|
+
response = run_schema(
|
|
405
421
|
context=context,
|
|
406
422
|
questions=questions,
|
|
407
423
|
base_url="http://127.0.0.1:30000",
|
|
@@ -414,7 +430,7 @@ result = run_schema(
|
|
|
414
430
|
Add `permutations` to an `enum` question to reduce option-order bias. TypeLLM averages the probabilities and keeps the same return format.
|
|
415
431
|
|
|
416
432
|
```python
|
|
417
|
-
|
|
433
|
+
response = client.generate(
|
|
418
434
|
context="A single roll of a fair die.",
|
|
419
435
|
questions={"roll": {
|
|
420
436
|
"type": "string",
|
|
@@ -441,25 +457,25 @@ support this option.
|
|
|
441
457
|
## Serving
|
|
442
458
|
|
|
443
459
|
One `TypeLLMClient` can be shared by many threads. Each call keeps its own
|
|
444
|
-
prompts and usage, and connections to SGLang are reused.
|
|
460
|
+
prompts and usage, and connections to SGLang or the hosted API are reused.
|
|
445
461
|
|
|
446
462
|
```python
|
|
447
463
|
import threading
|
|
448
464
|
|
|
449
465
|
cancel = threading.Event()
|
|
450
|
-
|
|
466
|
+
response = client.generate(
|
|
451
467
|
context=context,
|
|
452
468
|
questions=questions,
|
|
453
469
|
seed=7, # this call's random choices only
|
|
454
470
|
timeout=30, # seconds for the whole call; raises GenerationTimeout
|
|
455
471
|
cancel=cancel, # set it from another thread; raises GenerationCancelled
|
|
456
472
|
)
|
|
457
|
-
print(
|
|
458
|
-
# Usage(requests=4, prompt_tokens=1830, cached_tokens=1504, completion_tokens=7
|
|
473
|
+
print(response.usage)
|
|
474
|
+
# Usage(input_tokens=410, thinking_tokens=0, requests=4, prompt_tokens=1830, cached_tokens=1504, completion_tokens=7)
|
|
459
475
|
```
|
|
460
476
|
|
|
461
|
-
`
|
|
462
|
-
|
|
477
|
+
`usage` reports the tokens of the call. When a call fails partway, the
|
|
478
|
+
exception's `.usage` holds what it spent. `input_tokens` is what you sent, each part
|
|
463
479
|
counted once: the context, the questions as JSON, and the images. The other
|
|
464
480
|
counts are SGLang's for the call's requests, where every prompt carries the
|
|
465
481
|
shared context. A timeout or cancel stops
|
|
@@ -490,16 +506,14 @@ server.
|
|
|
490
506
|
| --- | --- |
|
|
491
507
|
| `Qwen/Qwen3.8-27B` | On / off |
|
|
492
508
|
| `Qwen/Qwen3.5-0.8B/4B/9B` | On / off |
|
|
493
|
-
| `openbmb/MiniCPM5-1B` | On / off |
|
|
494
|
-
| `inclusionAI/Ling-mini-2.0` | Off only |
|
|
495
|
-
| `inclusionAI/Ring-mini-2.0` | Always on |
|
|
496
509
|
|
|
497
510
|
Other sizes in the Qwen3.5 and Qwen3.8 families are expected to be compatible.
|
|
498
511
|
|
|
499
512
|
[Image input](#image-input) has been tested with `Qwen/Qwen3.8-27B`.
|
|
500
513
|
|
|
501
|
-
Use the checkpoint ID as `model=`.
|
|
502
|
-
|
|
514
|
+
Use the checkpoint ID as `model=`. TypeLLM loads the tokenizer from the server's
|
|
515
|
+
paths, then its served model name; if none of those loads locally, set
|
|
516
|
+
`tokenizer=` to the matching Hugging Face ID or local directory.
|
|
503
517
|
The tokenizer must load from standard artifacts without custom model code.
|
|
504
518
|
|
|
505
519
|
## 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,16 +256,35 @@ result = client.generate(context=context, questions={
|
|
|
259
256
|
})
|
|
260
257
|
```
|
|
261
258
|
|
|
262
|
-
`thinking_budget` caps a field's reasoning
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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.
|
|
262
|
+
|
|
263
|
+
`response.thinking` maps each field that thought to its reasoning, and
|
|
264
|
+
`response.usage.thinking_tokens` counts the reasoning tokens.
|
|
265
|
+
|
|
266
|
+
### Thinking effort
|
|
267
|
+
|
|
268
|
+
`thinking_effort` sets the budget by level instead: `"none"` does not think,
|
|
269
|
+
`"low"` thinks up to 512 tokens, `"medium"` up to 2048 and `"high"` up to 4096.
|
|
266
270
|
|
|
267
|
-
|
|
268
|
-
reasoning,
|
|
271
|
+
With `"thinking": "auto"`, TypeLLM picks the level on each call. It first asks
|
|
272
|
+
the model how much reasoning the field needs, without thinking, then answers the
|
|
273
|
+
field at that level. The field's own prompt is unchanged, and
|
|
274
|
+
`response.thinking_effort` reports the level each `"auto"` field got:
|
|
275
|
+
|
|
276
|
+
```python
|
|
277
|
+
response = client.generate(context=context, questions={
|
|
278
|
+
"category": {"type": "string", "enum": ["meal", "travel", "equipment"]},
|
|
279
|
+
"policy_ok": {"type": "boolean", "instructions": "Does it meet the travel policy?",
|
|
280
|
+
"thinking": "auto"},
|
|
281
|
+
})
|
|
282
|
+
response.thinking_effort # {"policy_ok": "low"}
|
|
283
|
+
```
|
|
269
284
|
|
|
270
|
-
|
|
271
|
-
|
|
285
|
+
`"auto"` adds one quick step after the field's dependencies and before the field;
|
|
286
|
+
that step sees the same dependency results. `thinking_effort` and
|
|
287
|
+
`thinking_budget` cannot be combined with `"auto"` or with each other.
|
|
272
288
|
|
|
273
289
|
## Image input
|
|
274
290
|
|
|
@@ -276,7 +292,7 @@ Pass images with `images=` alongside the text context. The served model must be
|
|
|
276
292
|
a vision-language model, such as `Qwen/Qwen3.8-27B`.
|
|
277
293
|
|
|
278
294
|
```python
|
|
279
|
-
|
|
295
|
+
response = client.generate(
|
|
280
296
|
context="The customer says this receipt was charged twice.",
|
|
281
297
|
images=["receipt.png"],
|
|
282
298
|
questions={
|
|
@@ -296,7 +312,7 @@ Fields run together by default, and each sees only the original context.
|
|
|
296
312
|
When a field needs earlier results, list them in `depends_on`:
|
|
297
313
|
|
|
298
314
|
```python
|
|
299
|
-
|
|
315
|
+
response = client.generate(
|
|
300
316
|
context="The payments service is returning errors after a deployment.",
|
|
301
317
|
questions={
|
|
302
318
|
"system": {
|
|
@@ -334,7 +350,7 @@ step reuses its parent's cached prompt. Unknown names and cycles raise
|
|
|
334
350
|
Set `return_probabilities` on individual enum or boolean fields:
|
|
335
351
|
|
|
336
352
|
```python
|
|
337
|
-
|
|
353
|
+
response = client.generate(
|
|
338
354
|
context=context,
|
|
339
355
|
questions={
|
|
340
356
|
"expense_type": {
|
|
@@ -386,7 +402,7 @@ For a one-off request, use the convenience function:
|
|
|
386
402
|
```python
|
|
387
403
|
from typellm import run_schema
|
|
388
404
|
|
|
389
|
-
|
|
405
|
+
response = run_schema(
|
|
390
406
|
context=context,
|
|
391
407
|
questions=questions,
|
|
392
408
|
base_url="http://127.0.0.1:30000",
|
|
@@ -399,7 +415,7 @@ result = run_schema(
|
|
|
399
415
|
Add `permutations` to an `enum` question to reduce option-order bias. TypeLLM averages the probabilities and keeps the same return format.
|
|
400
416
|
|
|
401
417
|
```python
|
|
402
|
-
|
|
418
|
+
response = client.generate(
|
|
403
419
|
context="A single roll of a fair die.",
|
|
404
420
|
questions={"roll": {
|
|
405
421
|
"type": "string",
|
|
@@ -426,25 +442,25 @@ support this option.
|
|
|
426
442
|
## Serving
|
|
427
443
|
|
|
428
444
|
One `TypeLLMClient` can be shared by many threads. Each call keeps its own
|
|
429
|
-
prompts and usage, and connections to SGLang are reused.
|
|
445
|
+
prompts and usage, and connections to SGLang or the hosted API are reused.
|
|
430
446
|
|
|
431
447
|
```python
|
|
432
448
|
import threading
|
|
433
449
|
|
|
434
450
|
cancel = threading.Event()
|
|
435
|
-
|
|
451
|
+
response = client.generate(
|
|
436
452
|
context=context,
|
|
437
453
|
questions=questions,
|
|
438
454
|
seed=7, # this call's random choices only
|
|
439
455
|
timeout=30, # seconds for the whole call; raises GenerationTimeout
|
|
440
456
|
cancel=cancel, # set it from another thread; raises GenerationCancelled
|
|
441
457
|
)
|
|
442
|
-
print(
|
|
443
|
-
# Usage(requests=4, prompt_tokens=1830, cached_tokens=1504, completion_tokens=7
|
|
458
|
+
print(response.usage)
|
|
459
|
+
# Usage(input_tokens=410, thinking_tokens=0, requests=4, prompt_tokens=1830, cached_tokens=1504, completion_tokens=7)
|
|
444
460
|
```
|
|
445
461
|
|
|
446
|
-
`
|
|
447
|
-
|
|
462
|
+
`usage` reports the tokens of the call. When a call fails partway, the
|
|
463
|
+
exception's `.usage` holds what it spent. `input_tokens` is what you sent, each part
|
|
448
464
|
counted once: the context, the questions as JSON, and the images. The other
|
|
449
465
|
counts are SGLang's for the call's requests, where every prompt carries the
|
|
450
466
|
shared context. A timeout or cancel stops
|
|
@@ -475,16 +491,14 @@ server.
|
|
|
475
491
|
| --- | --- |
|
|
476
492
|
| `Qwen/Qwen3.8-27B` | On / off |
|
|
477
493
|
| `Qwen/Qwen3.5-0.8B/4B/9B` | On / off |
|
|
478
|
-
| `openbmb/MiniCPM5-1B` | On / off |
|
|
479
|
-
| `inclusionAI/Ling-mini-2.0` | Off only |
|
|
480
|
-
| `inclusionAI/Ring-mini-2.0` | Always on |
|
|
481
494
|
|
|
482
495
|
Other sizes in the Qwen3.5 and Qwen3.8 families are expected to be compatible.
|
|
483
496
|
|
|
484
497
|
[Image input](#image-input) has been tested with `Qwen/Qwen3.8-27B`.
|
|
485
498
|
|
|
486
|
-
Use the checkpoint ID as `model=`.
|
|
487
|
-
|
|
499
|
+
Use the checkpoint ID as `model=`. TypeLLM loads the tokenizer from the server's
|
|
500
|
+
paths, then its served model name; if none of those loads locally, set
|
|
501
|
+
`tokenizer=` to the matching Hugging Face ID or local directory.
|
|
488
502
|
The tokenizer must load from standard artifacts without custom model code.
|
|
489
503
|
|
|
490
504
|
## Comparison with Jev-style models
|