typellm 0.3.0.dev1__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.3.0.dev1/typellm.egg-info → typellm-0.3.0.dev2}/PKG-INFO +7 -6
  2. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/README.md +6 -5
  3. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/pyproject.toml +1 -1
  4. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/tests/test_concurrency.py +32 -0
  5. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/tests/test_grammar_fields.py +1 -1
  6. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/tests/test_permutations.py +1 -1
  7. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/tests/test_typellm.py +27 -1
  8. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/typellm/cli.py +3 -2
  9. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/typellm/runtime.py +41 -14
  10. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/typellm/sglang.py +33 -20
  11. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2/typellm.egg-info}/PKG-INFO +7 -6
  12. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/LICENSE +0 -0
  13. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/setup.cfg +0 -0
  14. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/tests/test_batching.py +0 -0
  15. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/tests/test_dependencies.py +0 -0
  16. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/tests/test_images.py +0 -0
  17. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/tests/test_nullable.py +0 -0
  18. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/tests/test_prefill.py +0 -0
  19. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/tests/test_probabilities.py +0 -0
  20. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/tests/test_prompts.py +0 -0
  21. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/tests/test_protocols.py +0 -0
  22. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/tests/test_text.py +0 -0
  23. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/typellm/__init__.py +0 -0
  24. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/typellm/__main__.py +0 -0
  25. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/typellm/benchmark.py +0 -0
  26. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/typellm/images.py +0 -0
  27. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/typellm/protocol.py +0 -0
  28. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/typellm/schema.py +0 -0
  29. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/typellm.egg-info/SOURCES.txt +0 -0
  30. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/typellm.egg-info/dependency_links.txt +0 -0
  31. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/typellm.egg-info/entry_points.txt +0 -0
  32. {typellm-0.3.0.dev1 → typellm-0.3.0.dev2}/typellm.egg-info/requires.txt +0 -0
  33. {typellm-0.3.0.dev1 → 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.3.0.dev1
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/
@@ -79,8 +79,8 @@ client = TypeLLMClient(api_key="YOUR_API_KEY") # calls https://api.typellm.ai
79
79
 
80
80
  `generate()` then works as in the example below, with these differences:
81
81
 
82
- - The service compiles and runs the schema. Use `model`, `mode`, `temperature`
83
- (for sampling), `seed` and `timeout`; local compilation settings do not apply.
82
+ - The service compiles and runs the schema. Use `model`, `temperature`, `seed`
83
+ and `timeout`; local compilation settings do not apply.
84
84
  A call runs for at most 300 seconds, 60 by default. Set `timeout` on the
85
85
  client for a new default, or on `generate()` to override it for one call.
86
86
  - Pass `questions`; `schema=`, `cancel` and `print_final_prompt` need your
@@ -377,19 +377,20 @@ result = client.generate(
377
377
  Only opted-in fields return `value` and `probabilities`; other fields return plain values.
378
378
  The option is not supported on open Numeric or Text fields.
379
379
 
380
- Argmax is the default. To enable sampling:
380
+ `temperature` is 0 by default: each field gets its most likely answer. Above 0,
381
+ TypeLLM samples at that temperature:
381
382
 
382
383
  ```python
383
384
  client = TypeLLMClient(
384
385
  "http://127.0.0.1:30000",
385
- mode="sample",
386
386
  temperature=0.8,
387
387
  seed=42,
388
388
  )
389
389
  ```
390
390
 
391
391
  Sampling applies to finite candidates for Choice fields and to token generation
392
- for Numeric and Text fields. `temperature` controls sampling in each case.
392
+ for Numeric and Text fields. `generate(temperature=...)` sets it for one call.
393
+ The older `mode="argmax"` / `mode="sample"` still works for now but is deprecated.
393
394
 
394
395
  A seed fixes TypeLLM's own random choices. SGLang's log probabilities can shift
395
396
  with its prefix cache and batching, so the same seed may still give different
@@ -64,8 +64,8 @@ client = TypeLLMClient(api_key="YOUR_API_KEY") # calls https://api.typellm.ai
64
64
 
65
65
  `generate()` then works as in the example below, with these differences:
66
66
 
67
- - The service compiles and runs the schema. Use `model`, `mode`, `temperature`
68
- (for sampling), `seed` and `timeout`; local compilation settings do not apply.
67
+ - The service compiles and runs the schema. Use `model`, `temperature`, `seed`
68
+ and `timeout`; local compilation settings do not apply.
69
69
  A call runs for at most 300 seconds, 60 by default. Set `timeout` on the
70
70
  client for a new default, or on `generate()` to override it for one call.
71
71
  - Pass `questions`; `schema=`, `cancel` and `print_final_prompt` need your
@@ -362,19 +362,20 @@ result = client.generate(
362
362
  Only opted-in fields return `value` and `probabilities`; other fields return plain values.
363
363
  The option is not supported on open Numeric or Text fields.
364
364
 
365
- Argmax is the default. To enable sampling:
365
+ `temperature` is 0 by default: each field gets its most likely answer. Above 0,
366
+ TypeLLM samples at that temperature:
366
367
 
367
368
  ```python
368
369
  client = TypeLLMClient(
369
370
  "http://127.0.0.1:30000",
370
- mode="sample",
371
371
  temperature=0.8,
372
372
  seed=42,
373
373
  )
374
374
  ```
375
375
 
376
376
  Sampling applies to finite candidates for Choice fields and to token generation
377
- for Numeric and Text fields. `temperature` controls sampling in each case.
377
+ for Numeric and Text fields. `generate(temperature=...)` sets it for one call.
378
+ The older `mode="argmax"` / `mode="sample"` still works for now but is deprecated.
378
379
 
379
380
  A seed fixes TypeLLM's own random choices. SGLang's log probabilities can shift
380
381
  with its prefix cache and batching, so the same seed may still give different
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "typellm"
7
- version = "0.3.0.dev1"
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"
@@ -212,6 +212,38 @@ class DeadlineTests(unittest.TestCase):
212
212
  client._generate({"text": "x"})
213
213
  self.assertNotIsInstance(caught.exception, GenerationTimeout)
214
214
 
215
+ def dropping(self, error, times):
216
+ """A client whose server drops the first `times` requests with `error`."""
217
+ seen = []
218
+
219
+ def handler(request):
220
+ seen.append(request)
221
+ if len(seen) <= times:
222
+ raise error("connection dropped", request=request)
223
+ return httpx.Response(200, json={"text": "ok"})
224
+
225
+ client = SGLangClient()
226
+ client._http_client = httpx.Client(transport=httpx.MockTransport(handler))
227
+ return client, seen
228
+
229
+ def test_a_dropped_connection_is_retried_once(self):
230
+ for error in (httpx.ReadError, httpx.RemoteProtocolError, httpx.WriteError):
231
+ client, seen = self.dropping(error, 1)
232
+ with self.subTest(error=error.__name__):
233
+ self.assertEqual(client._request("/generate", {"text": "x"}), {"text": "ok"})
234
+ self.assertEqual(len(seen), 2)
235
+ client, seen = self.dropping(httpx.ReadError, 2)
236
+ with self.assertRaisesRegex(SGLangError, "Could not reach"):
237
+ client._request("/generate", {"text": "x"})
238
+ self.assertEqual(len(seen), 2)
239
+
240
+ def test_a_refused_connection_is_not_retried(self):
241
+ for error in (httpx.ConnectError, httpx.ReadTimeout):
242
+ client, seen = self.dropping(error, 1)
243
+ with self.subTest(error=error.__name__), self.assertRaises(SGLangError):
244
+ client._request("/generate", {"text": "x"})
245
+ self.assertEqual(len(seen), 1)
246
+
215
247
  def test_invalid_budgets_are_rejected(self):
216
248
  class Recording(FakeServer):
217
249
  def _request(self, path, payload=None, *, allow_text=False):
@@ -87,7 +87,7 @@ class GrammarNumberTests(unittest.TestCase):
87
87
  self.assertIn("null", patterns[3])
88
88
 
89
89
  def test_sampling_passes_the_temperature_and_no_truncation(self):
90
- client = TypeLLMClient(model="fake", mode="sample", temperature=0.7, seed=1)
90
+ client = TypeLLMClient(model="fake", temperature=0.7, seed=1)
91
91
  client.sglang = FakeServer()
92
92
  client.generate(context="Receipt", questions={"a": {"type": "integer"}})
93
93
  [params] = regex_requests(client.sglang)[0]["sampling_params"]
@@ -39,7 +39,7 @@ class PermutationTests(unittest.TestCase):
39
39
 
40
40
  def test_sampling_uses_averaged_distribution(self):
41
41
  with patch('typellm.runtime._sample', return_value='B') as sample:
42
- _client, result = self.run_case(False, ['one', 'two', 'three'], mode='sample', temperature=.5)
42
+ _client, result = self.run_case(False, ['one', 'two', 'three'], temperature=.5)
43
43
  self.assertEqual(result['roll']['value'], 'two')
44
44
  self.assertEqual(sample.call_count, 2) # one final choice plus the boolean
45
45
  for p in sample.call_args_list[0].args[0].values():
@@ -956,7 +956,7 @@ class HostedApiTests(unittest.TestCase):
956
956
 
957
957
  self.client(handler, temperature=0).generate(
958
958
  context="x", questions={"a": {"type": "boolean"}})
959
- self.client(handler, mode="sample", temperature=0.7).generate(
959
+ self.client(handler, temperature=0.7).generate(
960
960
  context="x", questions={"a": {"type": "boolean"}})
961
961
 
962
962
  self.assertNotIn("temperature", bodies[0]["options"])
@@ -1025,3 +1025,29 @@ class HostedApiTests(unittest.TestCase):
1025
1025
 
1026
1026
  if __name__ == "__main__":
1027
1027
  unittest.main()
1028
+
1029
+
1030
+ class DecodingTests(unittest.TestCase):
1031
+ def test_temperature_alone_decides(self):
1032
+ from typellm.runtime import _resolve_decoding
1033
+
1034
+ self.assertEqual(_resolve_decoding(None, None), ("argmax", 0.0))
1035
+ self.assertEqual(_resolve_decoding(None, 0), ("argmax", 0.0))
1036
+ self.assertEqual(_resolve_decoding(None, 0.7), ("sample", 0.7))
1037
+ for bad in (-1, float("nan"), True, "1"):
1038
+ with self.assertRaises(ValueError):
1039
+ _resolve_decoding(None, bad)
1040
+
1041
+ def test_deprecated_mode_still_works(self):
1042
+ from typellm.runtime import _resolve_decoding
1043
+
1044
+ with self.assertWarns(DeprecationWarning):
1045
+ self.assertEqual(_resolve_decoding("sample", None), ("sample", 1.0))
1046
+ with self.assertWarns(DeprecationWarning):
1047
+ self.assertEqual(_resolve_decoding("argmax", 0.7)[0], "argmax")
1048
+ with self.assertWarns(DeprecationWarning), self.assertRaises(ValueError):
1049
+ _resolve_decoding("sample", 0)
1050
+
1051
+ def test_call_temperature_overrides_client(self):
1052
+ client = TypeLLMClient(model="fake", temperature=0.7)
1053
+ self.assertEqual((client.mode, client.temperature), ("sample", 0.7))
@@ -51,8 +51,9 @@ def main() -> None:
51
51
  default=os.environ.get("TYPELLM_TOKENIZER"),
52
52
  help="Tokenizer path or Hugging Face ID; normally discovered from SGLang",
53
53
  )
54
- parser.add_argument("--mode", choices=("argmax", "sample"), default="argmax")
55
- parser.add_argument("--temperature", type=float, default=1.0)
54
+ parser.add_argument("--temperature", type=float, default=0.0,
55
+ help="0 picks the most likely answer; above 0 samples")
56
+ parser.add_argument("--mode", choices=("argmax", "sample"), help="deprecated; use --temperature")
56
57
  parser.add_argument("--numeric-max-digits", type=int, default=32)
57
58
  parser.add_argument("--seed", type=int, default=0)
58
59
  parser.add_argument("--probabilities", action="store_true")
@@ -8,6 +8,7 @@ import math
8
8
  import os
9
9
  import random
10
10
  import threading
11
+ import warnings
11
12
  from contextlib import nullcontext
12
13
  from contextvars import ContextVar
13
14
  from dataclasses import dataclass, replace
@@ -154,8 +155,8 @@ class TypeLLMClient:
154
155
  model: str | None = None,
155
156
  *,
156
157
  api_key: str | None = None,
157
- mode: str = "argmax",
158
- temperature: float = 1.0,
158
+ mode: str | None = None,
159
+ temperature: float | None = None,
159
160
  seed: int | None = None,
160
161
  timeout: float | None = None,
161
162
  label_pool: Sequence[str] | None = None,
@@ -168,11 +169,14 @@ class TypeLLMClient:
168
169
 
169
170
  With api_key, calls go to the hosted API instead, by default
170
171
  https://api.typellm.ai. It compiles and runs the schema itself. Model
171
- chooses one of its models; mode, temperature, seed and timeout apply.
172
+ chooses one of its models; temperature, seed and timeout apply.
172
173
  A hosted timeout set here is the default for generate() calls; without
173
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.
174
178
  """
175
- _validate_decoding(mode, temperature)
179
+ mode, temperature = _resolve_decoding(mode, temperature)
176
180
  if type(numeric_max_digits) is not int or numeric_max_digits <= 0:
177
181
  raise ValueError("numeric_max_digits must be a positive integer")
178
182
  self.api_key = api_key
@@ -412,9 +416,13 @@ class TypeLLMClient:
412
416
  if not isinstance(questions, Mapping):
413
417
  raise SchemaError("questions must be a mapping of field names to definitions")
414
418
  schema = {"type": "object", "properties": questions}
415
- active_mode = self.mode if mode is None else mode
416
- active_temperature = self.temperature if temperature is None else temperature
417
- _validate_decoding(active_mode, active_temperature)
419
+ if mode is None and temperature is None:
420
+ active_mode, active_temperature = self.mode, self.temperature
421
+ else:
422
+ # A call's own temperature decides for it, whatever the client's mode.
423
+ if temperature is None:
424
+ temperature = self.temperature or None
425
+ active_mode, active_temperature = _resolve_decoding(mode, temperature)
418
426
  if self.api_key is not None:
419
427
  if questions is None or cancel is not None or print_final_prompt:
420
428
  raise ValueError("with api_key, generate() takes questions; schema, cancel and "
@@ -546,11 +554,30 @@ def _sample(probs: Mapping[str, float], rng: random.Random) -> str:
546
554
  return last
547
555
 
548
556
 
549
- def _validate_decoding(mode: str, temperature: float) -> None:
550
- if mode not in {"argmax", "sample"}:
551
- raise ValueError("mode must be 'argmax' or 'sample'")
552
- if mode == "sample" and (not math.isfinite(temperature) or temperature <= 0):
553
- raise ValueError("temperature must be finite and > 0 in sample mode")
557
+ def _resolve_decoding(mode: str | None, temperature: float | None) -> tuple[str, float]:
558
+ """The (mode, temperature) that temperature, and the deprecated mode, ask for.
559
+
560
+ temperature 0 picks the most likely answer (argmax); above 0 samples. For
561
+ older code, mode="sample" without a temperature samples at 1.0 and
562
+ mode="argmax" is argmax whatever the temperature.
563
+ """
564
+ if mode is not None:
565
+ warnings.warn("mode is deprecated; temperature 0 picks the most likely answer and "
566
+ "above 0 samples", DeprecationWarning, stacklevel=3)
567
+ if mode not in {"argmax", "sample"}:
568
+ raise ValueError("mode must be 'argmax' or 'sample'")
569
+ if mode == "sample" and temperature is None:
570
+ temperature = 1.0
571
+ if temperature is None:
572
+ temperature = 0.0
573
+ if (isinstance(temperature, bool) or not isinstance(temperature, (int, float))
574
+ or not math.isfinite(temperature) or temperature < 0):
575
+ raise ValueError("temperature must be a finite number >= 0")
576
+ if mode == "argmax":
577
+ return "argmax", float(temperature)
578
+ if mode == "sample" and temperature == 0:
579
+ raise ValueError("temperature must be > 0 in sample mode")
580
+ return ("sample" if temperature > 0 else "argmax"), float(temperature)
554
581
 
555
582
 
556
583
  def _numeric_text_is_complete(text: str, numeric_type: str) -> bool:
@@ -1006,8 +1033,8 @@ def _print_final_prompts(prefixes: Sequence[str]) -> None:
1006
1033
  def run_schema(
1007
1034
  context: str | None = None,
1008
1035
  schema: Mapping[str, Any] | None = None,
1009
- mode: str = "argmax",
1010
- temperature: float = 1.0,
1036
+ mode: str | None = None,
1037
+ temperature: float | None = None,
1011
1038
  *,
1012
1039
  state: str | None = None,
1013
1040
  questions: Mapping[str, Any] | None = None,
@@ -252,9 +252,11 @@ class SGLangClient:
252
252
  def _http(self) -> httpx.Client:
253
253
  with self._load_lock:
254
254
  if self._http_client is None:
255
- # Keep-alive connections: one call makes many small requests.
255
+ # Keep-alive connections: one call makes many small requests. Idle
256
+ # ones are dropped before SGLang closes them (5 s), so a request
257
+ # rarely goes out on a connection the server has just closed.
256
258
  self._http_client = httpx.Client(limits=httpx.Limits(
257
- max_connections=None, max_keepalive_connections=64,
259
+ max_connections=None, max_keepalive_connections=64, keepalive_expiry=2.0,
258
260
  ))
259
261
  return self._http_client
260
262
 
@@ -269,24 +271,15 @@ class SGLangClient:
269
271
  scope = _call_scope.get()
270
272
  timeout = self.timeout if scope is None else scope.socket_timeout(self.timeout)
271
273
  try:
272
- # Streamed so a failed body read still reports the status it follows.
273
- with self._http().stream(
274
- "GET" if payload is None else "POST",
275
- self.base_url + path,
276
- content=body,
277
- headers={"Content-Type": "application/json"},
278
- timeout=timeout,
279
- ) as response:
280
- if response.status_code >= 400:
281
- try:
282
- detail = response.read().decode("utf-8", errors="replace")
283
- except httpx.HTTPError as read_error:
284
- detail = f"Could not read error response: {read_error}"
285
- raise SGLangError(
286
- f"SGLang {path} returned HTTP {response.status_code}: {detail}",
287
- status=response.status_code,
288
- )
289
- raw = response.read().decode("utf-8")
274
+ try:
275
+ raw = self._send(path, payload is None, body, timeout)
276
+ except (httpx.ReadError, httpx.WriteError, httpx.RemoteProtocolError):
277
+ # The connection dropped before an answer, most often a pooled one
278
+ # the server had just closed. SGLang is fine: try once on a new one.
279
+ if scope is not None:
280
+ scope.check()
281
+ timeout = scope.socket_timeout(self.timeout)
282
+ raw = self._send(path, payload is None, body, timeout)
290
283
  except httpx.HTTPError as exc:
291
284
  # Connection failures, timeouts, resets and truncated responses.
292
285
  raise SGLangError(
@@ -301,6 +294,26 @@ class SGLangClient:
301
294
  f"SGLang {path} returned non-JSON data: {raw[:500]}"
302
295
  ) from exc
303
296
 
297
+ def _send(self, path: str, get: bool, body: bytes | None, timeout: float) -> str:
298
+ # Streamed so a failed body read still reports the status it follows.
299
+ with self._http().stream(
300
+ "GET" if get else "POST",
301
+ self.base_url + path,
302
+ content=body,
303
+ headers={"Content-Type": "application/json"},
304
+ timeout=timeout,
305
+ ) as response:
306
+ if response.status_code >= 400:
307
+ try:
308
+ detail = response.read().decode("utf-8", errors="replace")
309
+ except httpx.HTTPError as read_error:
310
+ detail = f"Could not read error response: {read_error}"
311
+ raise SGLangError(
312
+ f"SGLang {path} returned HTTP {response.status_code}: {detail}",
313
+ status=response.status_code,
314
+ )
315
+ return response.read().decode("utf-8")
316
+
304
317
  @contextmanager
305
318
  def images(self, images: Sequence[str]) -> Iterator[None]:
306
319
  """Attach encoded images to every /generate request made in this block."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: typellm
3
- Version: 0.3.0.dev1
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/
@@ -79,8 +79,8 @@ client = TypeLLMClient(api_key="YOUR_API_KEY") # calls https://api.typellm.ai
79
79
 
80
80
  `generate()` then works as in the example below, with these differences:
81
81
 
82
- - The service compiles and runs the schema. Use `model`, `mode`, `temperature`
83
- (for sampling), `seed` and `timeout`; local compilation settings do not apply.
82
+ - The service compiles and runs the schema. Use `model`, `temperature`, `seed`
83
+ and `timeout`; local compilation settings do not apply.
84
84
  A call runs for at most 300 seconds, 60 by default. Set `timeout` on the
85
85
  client for a new default, or on `generate()` to override it for one call.
86
86
  - Pass `questions`; `schema=`, `cancel` and `print_final_prompt` need your
@@ -377,19 +377,20 @@ result = client.generate(
377
377
  Only opted-in fields return `value` and `probabilities`; other fields return plain values.
378
378
  The option is not supported on open Numeric or Text fields.
379
379
 
380
- Argmax is the default. To enable sampling:
380
+ `temperature` is 0 by default: each field gets its most likely answer. Above 0,
381
+ TypeLLM samples at that temperature:
381
382
 
382
383
  ```python
383
384
  client = TypeLLMClient(
384
385
  "http://127.0.0.1:30000",
385
- mode="sample",
386
386
  temperature=0.8,
387
387
  seed=42,
388
388
  )
389
389
  ```
390
390
 
391
391
  Sampling applies to finite candidates for Choice fields and to token generation
392
- for Numeric and Text fields. `temperature` controls sampling in each case.
392
+ for Numeric and Text fields. `generate(temperature=...)` sets it for one call.
393
+ The older `mode="argmax"` / `mode="sample"` still works for now but is deprecated.
393
394
 
394
395
  A seed fixes TypeLLM's own random choices. SGLang's log probabilities can shift
395
396
  with its prefix cache and batching, so the same seed may still give different
File without changes
File without changes