typellm 0.2.5__tar.gz → 0.3.0.dev2__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.
Files changed (33) hide show
  1. {typellm-0.2.5/typellm.egg-info → typellm-0.3.0.dev2}/PKG-INFO +31 -1
  2. {typellm-0.2.5 → typellm-0.3.0.dev2}/README.md +30 -0
  3. {typellm-0.2.5 → typellm-0.3.0.dev2}/pyproject.toml +1 -1
  4. {typellm-0.2.5 → typellm-0.3.0.dev2}/tests/test_typellm.py +116 -0
  5. {typellm-0.2.5 → typellm-0.3.0.dev2}/typellm/runtime.py +103 -19
  6. {typellm-0.2.5 → typellm-0.3.0.dev2/typellm.egg-info}/PKG-INFO +31 -1
  7. {typellm-0.2.5 → typellm-0.3.0.dev2}/LICENSE +0 -0
  8. {typellm-0.2.5 → typellm-0.3.0.dev2}/setup.cfg +0 -0
  9. {typellm-0.2.5 → typellm-0.3.0.dev2}/tests/test_batching.py +0 -0
  10. {typellm-0.2.5 → typellm-0.3.0.dev2}/tests/test_concurrency.py +0 -0
  11. {typellm-0.2.5 → typellm-0.3.0.dev2}/tests/test_dependencies.py +0 -0
  12. {typellm-0.2.5 → typellm-0.3.0.dev2}/tests/test_grammar_fields.py +0 -0
  13. {typellm-0.2.5 → typellm-0.3.0.dev2}/tests/test_images.py +0 -0
  14. {typellm-0.2.5 → typellm-0.3.0.dev2}/tests/test_nullable.py +0 -0
  15. {typellm-0.2.5 → typellm-0.3.0.dev2}/tests/test_permutations.py +0 -0
  16. {typellm-0.2.5 → typellm-0.3.0.dev2}/tests/test_prefill.py +0 -0
  17. {typellm-0.2.5 → typellm-0.3.0.dev2}/tests/test_probabilities.py +0 -0
  18. {typellm-0.2.5 → typellm-0.3.0.dev2}/tests/test_prompts.py +0 -0
  19. {typellm-0.2.5 → typellm-0.3.0.dev2}/tests/test_protocols.py +0 -0
  20. {typellm-0.2.5 → typellm-0.3.0.dev2}/tests/test_text.py +0 -0
  21. {typellm-0.2.5 → typellm-0.3.0.dev2}/typellm/__init__.py +0 -0
  22. {typellm-0.2.5 → typellm-0.3.0.dev2}/typellm/__main__.py +0 -0
  23. {typellm-0.2.5 → typellm-0.3.0.dev2}/typellm/benchmark.py +0 -0
  24. {typellm-0.2.5 → typellm-0.3.0.dev2}/typellm/cli.py +0 -0
  25. {typellm-0.2.5 → typellm-0.3.0.dev2}/typellm/images.py +0 -0
  26. {typellm-0.2.5 → typellm-0.3.0.dev2}/typellm/protocol.py +0 -0
  27. {typellm-0.2.5 → typellm-0.3.0.dev2}/typellm/schema.py +0 -0
  28. {typellm-0.2.5 → typellm-0.3.0.dev2}/typellm/sglang.py +0 -0
  29. {typellm-0.2.5 → typellm-0.3.0.dev2}/typellm.egg-info/SOURCES.txt +0 -0
  30. {typellm-0.2.5 → typellm-0.3.0.dev2}/typellm.egg-info/dependency_links.txt +0 -0
  31. {typellm-0.2.5 → typellm-0.3.0.dev2}/typellm.egg-info/entry_points.txt +0 -0
  32. {typellm-0.2.5 → typellm-0.3.0.dev2}/typellm.egg-info/requires.txt +0 -0
  33. {typellm-0.2.5 → typellm-0.3.0.dev2}/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.2.5
3
+ Version: 0.3.0.dev2
4
4
  Summary: Type-Safe Decoding for Autoregressive LLMs
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Homepage, https://typellm.ai/
@@ -66,6 +66,36 @@ Evaluated on 231 public [JevBench](https://github.com/fstandhartinger/jevbench)
66
66
 
67
67
  ## Quick start
68
68
 
69
+ ### Hosted API (early access)
70
+
71
+ No GPU? [Early-access](https://typellm.ai/early-access) users get an API key
72
+ by email, and the hosted API runs the model:
73
+
74
+ ```python
75
+ from typellm import TypeLLMClient
76
+
77
+ client = TypeLLMClient(api_key="YOUR_API_KEY") # calls https://api.typellm.ai
78
+ ```
79
+
80
+ `generate()` then works as in the example below, with these differences:
81
+
82
+ - The service compiles and runs the schema. Use `model`, `temperature`, `seed`
83
+ and `timeout`; local compilation settings do not apply.
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.
96
+
97
+ To run on your own GPU instead, start here:
98
+
69
99
  ### 1. Serve a model with SGLang
70
100
 
71
101
  Use [SGLang](https://github.com/sgl-project/sglang) to configure and serve a
@@ -51,6 +51,36 @@ Evaluated on 231 public [JevBench](https://github.com/fstandhartinger/jevbench)
51
51
 
52
52
  ## Quick start
53
53
 
54
+ ### Hosted API (early access)
55
+
56
+ No GPU? [Early-access](https://typellm.ai/early-access) users get an API key
57
+ by email, and the hosted API runs the model:
58
+
59
+ ```python
60
+ from typellm import TypeLLMClient
61
+
62
+ client = TypeLLMClient(api_key="YOUR_API_KEY") # calls https://api.typellm.ai
63
+ ```
64
+
65
+ `generate()` then works as in the example below, with these differences:
66
+
67
+ - The service compiles and runs the schema. Use `model`, `temperature`, `seed`
68
+ and `timeout`; local compilation settings do not apply.
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.
81
+
82
+ To run on your own GPU instead, start here:
83
+
54
84
  ### 1. Serve a model with SGLang
55
85
 
56
86
  Use [SGLang](https://github.com/sgl-project/sglang) to configure and serve a
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "typellm"
7
- version = "0.2.5"
7
+ version = "0.3.0.dev2"
8
8
  description = "Type-Safe Decoding for Autoregressive LLMs"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -1,9 +1,12 @@
1
+ import json
2
+ import random
1
3
  import unittest
2
4
 
3
5
  import httpx
4
6
 
5
7
  from typellm.runtime import numeric_pattern
6
8
  from typellm import (
9
+ GenerationTimeout,
7
10
  SGLangClient,
8
11
  SGLangError,
9
12
  SchemaError,
@@ -907,6 +910,119 @@ class JsonSchemaExecutionTests(unittest.TestCase):
907
910
  self.assertEqual(scored[0][0], {65: -0.1, 66: -2.0})
908
911
  self.assertEqual(scored[1][0], {65: -3.0, 66: -0.2})
909
912
 
913
+
914
+ class HostedApiTests(unittest.TestCase):
915
+ def client(self, handler, **options):
916
+ client = TypeLLMClient(api_key="k", **options)
917
+ client._transport = httpx.MockTransport(handler)
918
+ return client
919
+
920
+ def test_a_call_is_one_request_to_the_hosted_api(self):
921
+ seen = []
922
+
923
+ def handler(request):
924
+ seen.append(request)
925
+ return httpx.Response(200, json={
926
+ "id": "gen_1", "model": "typellm-latest", "result": {"total": 12.5},
927
+ "thinking": {"total": "The receipt says 12.50."},
928
+ "usage": {"input_tokens": 40, "thinking_tokens": 9}, "elapsed": 0.5,
929
+ })
930
+
931
+ client = self.client(handler, seed=7)
932
+ questions = {"total": {"type": "number", "thinking": True}}
933
+ image = "data:image/png;base64,iVBORw0KGgo="
934
+ result = client.generate(context="Total: 12.50", questions=questions, images=[image], timeout=90)
935
+
936
+ self.assertEqual(result, {"total": 12.5})
937
+ [request] = seen
938
+ self.assertEqual(str(request.url), "https://api.typellm.ai/v1/generate")
939
+ self.assertEqual(request.headers["authorization"], "Bearer k")
940
+ self.assertEqual(json.loads(request.content), {
941
+ "context": "Total: 12.50", "questions": questions, "images": [image], "timeout": 90,
942
+ # Each call draws its seed from the client's seeded stream.
943
+ "options": {"mode": "argmax", "seed": random.Random(7).randrange(2**32)},
944
+ })
945
+ self.assertEqual((client.last_usage.input_tokens, client.last_usage.thinking_tokens), (40, 9))
946
+ self.assertEqual(client.last_thinking, {"total": "The receipt says 12.50."})
947
+
948
+ def test_hosted_temperature_only_applies_to_sampling(self):
949
+ bodies = []
950
+
951
+ def handler(request):
952
+ bodies.append(json.loads(request.content))
953
+ return httpx.Response(200, json={
954
+ "result": {"a": True}, "usage": {"input_tokens": 1, "thinking_tokens": 0},
955
+ })
956
+
957
+ self.client(handler, temperature=0).generate(
958
+ context="x", questions={"a": {"type": "boolean"}})
959
+ self.client(handler, temperature=0.7).generate(
960
+ context="x", questions={"a": {"type": "boolean"}})
961
+
962
+ self.assertNotIn("temperature", bodies[0]["options"])
963
+ self.assertEqual(bodies[1]["options"]["temperature"], 0.7)
964
+
965
+ def test_constructor_timeout_sets_hosted_limit_with_per_call_override(self):
966
+ seen = []
967
+
968
+ def handler(request):
969
+ seen.append(request)
970
+ return httpx.Response(200, json={
971
+ "result": {"a": True}, "usage": {"input_tokens": 1, "thinking_tokens": 0},
972
+ })
973
+
974
+ questions = {"a": {"type": "boolean"}}
975
+ client = self.client(handler, timeout=240)
976
+ client.generate(context="x", questions=questions)
977
+ client.generate(context="x", questions=questions, timeout=30)
978
+ self.client(handler).generate(context="x", questions=questions)
979
+
980
+ self.assertEqual([json.loads(request.content).get("timeout") for request in seen],
981
+ [240, 30, None])
982
+ self.assertEqual([request.extensions["timeout"]["read"] for request in seen],
983
+ [300, 90, 180])
984
+ self.assertEqual(TypeLLMClient().sglang.timeout, 120)
985
+
986
+ def test_hosted_rejects_invalid_timeouts_before_sending(self):
987
+ sent = []
988
+ client = self.client(lambda request: sent.append(request))
989
+ questions = {"a": {"type": "boolean"}}
990
+
991
+ for timeout in (0, -1, True, "5", float("nan")):
992
+ with self.subTest(timeout=timeout), self.assertRaises(ValueError):
993
+ client.generate(context="x", questions=questions, timeout=timeout)
994
+ with self.assertRaises(ValueError):
995
+ self.client(lambda request: sent.append(request), timeout=0).generate(
996
+ context="x", questions=questions)
997
+ self.assertEqual(sent, [])
998
+
999
+ def test_api_errors_keep_their_status(self):
1000
+ for status, error in ((401, SGLangError), (429, SGLangError), (504, GenerationTimeout)):
1001
+ client = self.client(lambda request: httpx.Response(status, json={"error": {"message": "no"}}))
1002
+ with self.assertRaises(error) as caught:
1003
+ client.generate(context="x", questions={"a": {"type": "boolean"}})
1004
+ self.assertEqual(caught.exception.status, status)
1005
+ with self.assertRaises(ValueError): # only local compilation takes a raw schema
1006
+ client.generate(context="x", schema={"type": "object", "properties": {"a": {"type": "boolean"}}})
1007
+
1008
+ def test_hosted_compile_schema_requires_own_server(self):
1009
+ client = TypeLLMClient(api_key="k")
1010
+ with self.assertRaisesRegex(ValueError, "compile_schema needs your own server"):
1011
+ client.compile_schema({"type": "object", "properties": {"a": {"type": "boolean"}}})
1012
+
1013
+ def test_hosted_invalid_responses_fail_clearly(self):
1014
+ questions = {"a": {"type": "boolean"}}
1015
+ client = TypeLLMClient(api_key="k")
1016
+ for response in (httpx.Response(200, text="not JSON"),
1017
+ httpx.Response(200, json={"result": {"a": True}}),
1018
+ httpx.Response(302, headers={"Location": "/elsewhere"})):
1019
+ with self.subTest(status=response.status_code):
1020
+ client._transport = httpx.MockTransport(lambda request: response)
1021
+ with self.assertRaises(SGLangError) as caught:
1022
+ client.generate(context="x", questions=questions)
1023
+ self.assertEqual(caught.exception.status, response.status_code)
1024
+
1025
+
910
1026
  if __name__ == "__main__":
911
1027
  unittest.main()
912
1028
 
@@ -17,6 +17,8 @@ from itertools import permutations as all_permutations
17
17
  from string import ascii_uppercase, digits
18
18
  from typing import Any, Mapping, Sequence
19
19
 
20
+ import httpx
21
+
20
22
  from .schema import (
21
23
  MAX_ENUM_CHOICES,
22
24
  SchemaError,
@@ -24,10 +26,12 @@ from .schema import (
24
26
  dependency_layers,
25
27
  )
26
28
  from .images import encode_images
27
- from .sglang import SGLangClient, Usage, call_scope
29
+ from .sglang import GenerationTimeout, SGLangClient, SGLangError, Usage, call_scope
28
30
 
29
31
 
30
32
  LOG = logging.getLogger("typellm")
33
+ HOSTED_URL = "https://api.typellm.ai"
34
+ DEFAULT_SOCKET_TIMEOUT = 120.0
31
35
 
32
36
 
33
37
  def _closed_answer(decision: "Choice", value_json: str) -> str:
@@ -141,40 +145,57 @@ class Choice:
141
145
 
142
146
 
143
147
  class TypeLLMClient:
144
- """Compile ordered schemas into constrained single-token decisions."""
148
+ """Generate typed answers with local SGLang or the hosted API."""
145
149
 
146
150
  DEFAULT_LABEL_POOL = tuple(ascii_uppercase + digits)
147
151
 
148
152
  def __init__(
149
153
  self,
150
- base_url: str = "http://127.0.0.1:30000",
154
+ base_url: str | None = None,
151
155
  model: str | None = None,
152
156
  *,
157
+ api_key: str | None = None,
153
158
  mode: str | None = None,
154
159
  temperature: float | None = None,
155
160
  seed: int | None = None,
156
- timeout: float = 120.0,
161
+ timeout: float | None = None,
157
162
  label_pool: Sequence[str] | None = None,
158
163
  numeric_max_digits: int = 32,
159
164
  tokenizer: str | None = None,
160
165
  thinking_budget: int | None = None,
161
166
  text_max_tokens: int = 128,
162
167
  ) -> None:
163
- """temperature 0 (the default) picks the most likely answer; above 0
164
- samples at that temperature. mode is deprecated: temperature alone decides.
168
+ """Run on your own SGLang server, by default http://127.0.0.1:30000.
169
+
170
+ With api_key, calls go to the hosted API instead, by default
171
+ https://api.typellm.ai. It compiles and runs the schema itself. Model
172
+ chooses one of its models; temperature, seed and timeout apply.
173
+ A hosted timeout set here is the default for generate() calls; without
174
+ one, the service uses its own default.
175
+
176
+ temperature 0 (the default) picks the most likely answer; above 0 samples
177
+ at that temperature. mode is deprecated: temperature alone decides.
165
178
  """
166
179
  mode, temperature = _resolve_decoding(mode, temperature)
167
180
  if type(numeric_max_digits) is not int or numeric_max_digits <= 0:
168
181
  raise ValueError("numeric_max_digits must be a positive integer")
169
- self.sglang = SGLangClient(
170
- base_url,
171
- model,
172
- timeout,
173
- tokenizer=tokenizer,
174
- thinking_budget=thinking_budget,
175
- text_max_tokens=text_max_tokens,
176
- answer_reserve_tokens=numeric_max_digits + 3,
177
- )
182
+ self.api_key = api_key
183
+ if api_key is None:
184
+ self.sglang = SGLangClient(
185
+ base_url or "http://127.0.0.1:30000",
186
+ model,
187
+ DEFAULT_SOCKET_TIMEOUT if timeout is None else timeout,
188
+ tokenizer=tokenizer,
189
+ thinking_budget=thinking_budget,
190
+ text_max_tokens=text_max_tokens,
191
+ answer_reserve_tokens=numeric_max_digits + 3,
192
+ )
193
+ else:
194
+ self.sglang = None
195
+ self.base_url = (base_url or HOSTED_URL).rstrip("/")
196
+ self.model = model
197
+ self.timeout = timeout
198
+ self._transport: httpx.BaseTransport | None = None # tests put a MockTransport here
178
199
  self.mode = mode
179
200
  self.temperature = temperature
180
201
  self.rng = random.Random(seed)
@@ -219,7 +240,8 @@ class TypeLLMClient:
219
240
  def last_usage(self) -> Usage | None:
220
241
  """Requests and tokens of the last generate() call made in this thread or task.
221
242
 
222
- Set even when the call raised, so partial work is still counted.
243
+ Local calls set this even when they raise, so partial work is counted.
244
+ Hosted success responses set input_tokens and thinking_tokens.
223
245
  """
224
246
  return self._last_usage.get()
225
247
 
@@ -246,6 +268,8 @@ class TypeLLMClient:
246
268
 
247
269
  def compile_schema(self, schema: Mapping[str, Any]) -> list[Choice]:
248
270
  """Compile standard JSON Schema or the original ordered-list format."""
271
+ if self.api_key is not None:
272
+ raise ValueError("compile_schema needs your own server when using api_key")
249
273
  if not isinstance(schema, Mapping):
250
274
  raise SchemaError("schema must be a mapping")
251
275
  if schema.get("type", "object") != "object":
@@ -373,9 +397,10 @@ class TypeLLMClient:
373
397
  A seed fixes this call's own random choices and leaves the client's shared
374
398
  stream alone; without one, calls share that stream. The server's numerics
375
399
  can still vary with its cache and batching, so a seed does not guarantee
376
- identical results. timeout caps the whole call in seconds and
377
- raises GenerationTimeout; setting cancel raises GenerationCancelled.
378
- Both stop the call before its next request to SGLang.
400
+ identical results. timeout caps the call and raises GenerationTimeout.
401
+ In local mode, setting cancel raises GenerationCancelled; timeout and
402
+ cancel stop the call before its next request to SGLang. Hosted mode
403
+ sends timeout to the service and does not support cancel.
379
404
  """
380
405
  self._last_usage.set(None)
381
406
  self._last_thinking.set({})
@@ -398,6 +423,12 @@ class TypeLLMClient:
398
423
  if temperature is None:
399
424
  temperature = self.temperature or None
400
425
  active_mode, active_temperature = _resolve_decoding(mode, temperature)
426
+ if self.api_key is not None:
427
+ if questions is None or cancel is not None or print_final_prompt:
428
+ raise ValueError("with api_key, generate() takes questions; schema, cancel and "
429
+ "print_final_prompt need your own server")
430
+ return self._generate_hosted(context, questions, encoded_images, active_mode,
431
+ active_temperature, seed, timeout)
401
432
  rng = self.rng if seed is None else random.Random(seed)
402
433
  # The time budget covers compiling too: labels are tokenized by SGLang.
403
434
  with call_scope(timeout, cancel) as scope:
@@ -445,6 +476,59 @@ class TypeLLMClient:
445
476
  _print_final_prompts(prompts)
446
477
  return output
447
478
 
479
+ def _generate_hosted(self, context: str, questions: Mapping[str, Any], images: Sequence[str],
480
+ mode: str, temperature: float, seed: int | None,
481
+ timeout: float | None) -> dict[str, Any]:
482
+ """One POST /v1/generate. HTTP errors carry their status in SGLangError."""
483
+ active_timeout = self.timeout if timeout is None else timeout
484
+ if active_timeout is not None and (type(active_timeout) not in (int, float) or
485
+ not active_timeout > 0):
486
+ raise ValueError("timeout must be a positive number of seconds or None")
487
+ options: dict[str, Any] = {
488
+ "mode": mode,
489
+ # Without a seed, calls draw theirs from the client's stream, as locally.
490
+ "seed": self.rng.randrange(2**32) if seed is None else seed,
491
+ }
492
+ if mode == "sample":
493
+ options["temperature"] = temperature
494
+ body: dict[str, Any] = {
495
+ "context": context,
496
+ "questions": questions,
497
+ "images": list(images),
498
+ "options": options,
499
+ }
500
+ if self.model is not None:
501
+ body["model"] = self.model
502
+ if active_timeout is not None:
503
+ body["timeout"] = active_timeout # the service's own default (60 s) otherwise
504
+ try:
505
+ with httpx.Client(transport=self._transport) as http:
506
+ # The service times the call itself. The margin covers its queue, image
507
+ # scaling and the grace it gives its own workers past the timeout.
508
+ response = http.post(self.base_url + "/v1/generate", json=body,
509
+ headers={"Authorization": f"Bearer {self.api_key}"},
510
+ timeout=(active_timeout if active_timeout is not None
511
+ else DEFAULT_SOCKET_TIMEOUT) + 60)
512
+ except httpx.HTTPError as exc:
513
+ raise SGLangError(f"Could not reach the TypeLLM API at {self.base_url}: {exc!r}") from exc
514
+ if response.status_code != 200:
515
+ error = GenerationTimeout if response.status_code == 504 else SGLangError
516
+ raise error(f"TypeLLM API returned HTTP {response.status_code}: {response.text}",
517
+ status=response.status_code)
518
+ try:
519
+ data = response.json()
520
+ usage = data["usage"]
521
+ result = data["result"]
522
+ thinking = data.get("thinking") or {}
523
+ input_tokens = usage["input_tokens"]
524
+ thinking_tokens = usage["thinking_tokens"]
525
+ except (ValueError, TypeError, KeyError) as exc:
526
+ raise SGLangError("TypeLLM API returned an invalid response",
527
+ status=response.status_code) from exc
528
+ self._last_usage.set(Usage(input_tokens=input_tokens, thinking_tokens=thinking_tokens))
529
+ self._last_thinking.set(thinking)
530
+ return result
531
+
448
532
 
449
533
  def candidate_softmax(
450
534
  logprobs: Mapping[str, float], temperature: float = 1.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: typellm
3
- Version: 0.2.5
3
+ Version: 0.3.0.dev2
4
4
  Summary: Type-Safe Decoding for Autoregressive LLMs
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Homepage, https://typellm.ai/
@@ -66,6 +66,36 @@ Evaluated on 231 public [JevBench](https://github.com/fstandhartinger/jevbench)
66
66
 
67
67
  ## Quick start
68
68
 
69
+ ### Hosted API (early access)
70
+
71
+ No GPU? [Early-access](https://typellm.ai/early-access) users get an API key
72
+ by email, and the hosted API runs the model:
73
+
74
+ ```python
75
+ from typellm import TypeLLMClient
76
+
77
+ client = TypeLLMClient(api_key="YOUR_API_KEY") # calls https://api.typellm.ai
78
+ ```
79
+
80
+ `generate()` then works as in the example below, with these differences:
81
+
82
+ - The service compiles and runs the schema. Use `model`, `temperature`, `seed`
83
+ and `timeout`; local compilation settings do not apply.
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.
96
+
97
+ To run on your own GPU instead, start here:
98
+
69
99
  ### 1. Serve a model with SGLang
70
100
 
71
101
  Use [SGLang](https://github.com/sgl-project/sglang) to configure and serve a
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes