typevet 0.1.0.dev1__tar.gz → 0.3.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.
Files changed (137) hide show
  1. {typevet-0.1.0.dev1 → typevet-0.3.0}/PKG-INFO +13 -11
  2. {typevet-0.1.0.dev1 → typevet-0.3.0}/README.md +9 -9
  3. {typevet-0.1.0.dev1 → typevet-0.3.0}/pyproject.toml +55 -8
  4. {typevet-0.1.0.dev1 → typevet-0.3.0}/pyproject.toml.orig +50 -10
  5. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/inbound/backend_settings.py +80 -75
  6. typevet-0.3.0/src/typevet/adapters/inbound/judgevet.py +698 -0
  7. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/inbound/settings.py +3 -2
  8. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/outbound/__init__.py +10 -11
  9. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/outbound/async_fake.py +1 -1
  10. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/outbound/chat_completion.py +1 -1
  11. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/outbound/http_errors.py +2 -2
  12. typevet-0.3.0/src/typevet/adapters/outbound/llama_cpp/__init__.py +41 -0
  13. {typevet-0.1.0.dev1/src/typevet/adapters/outbound → typevet-0.3.0/src/typevet/adapters/outbound/llama_cpp}/gemma_native_vision_factory.py +104 -23
  14. typevet-0.1.0.dev1/src/typevet/adapters/outbound/llama_cpp.py → typevet-0.3.0/src/typevet/adapters/outbound/llama_cpp/generation.py +2 -2
  15. typevet-0.1.0.dev1/src/typevet/adapters/outbound/async_llama_cpp.py → typevet-0.3.0/src/typevet/adapters/outbound/llama_cpp/generation_async.py +8 -6
  16. typevet-0.1.0.dev1/src/typevet/adapters/outbound/llama_cpp_http.py → typevet-0.3.0/src/typevet/adapters/outbound/llama_cpp/http_mapping.py +57 -2
  17. typevet-0.1.0.dev1/src/typevet/adapters/outbound/llama_cpp_multimodal.py → typevet-0.3.0/src/typevet/adapters/outbound/llama_cpp/multimodal.py +10 -9
  18. typevet-0.1.0.dev1/src/typevet/adapters/outbound/llama_cpp_scoring.py → typevet-0.3.0/src/typevet/adapters/outbound/llama_cpp/scoring.py +11 -12
  19. typevet-0.3.0/src/typevet/adapters/outbound/vllm/__init__.py +44 -0
  20. typevet-0.1.0.dev1/src/typevet/adapters/outbound/vllm_content.py → typevet-0.3.0/src/typevet/adapters/outbound/vllm/content.py +3 -3
  21. typevet-0.1.0.dev1/src/typevet/adapters/outbound/vllm_generation.py → typevet-0.3.0/src/typevet/adapters/outbound/vllm/generation.py +7 -7
  22. typevet-0.1.0.dev1/src/typevet/adapters/outbound/vllm_generation_async.py → typevet-0.3.0/src/typevet/adapters/outbound/vllm/generation_async.py +8 -8
  23. typevet-0.1.0.dev1/src/typevet/adapters/outbound/vllm_http.py → typevet-0.3.0/src/typevet/adapters/outbound/vllm/http_mapping.py +3 -3
  24. typevet-0.1.0.dev1/src/typevet/adapters/outbound/vllm_judgment_factory.py → typevet-0.3.0/src/typevet/adapters/outbound/vllm/judgment_factory.py +6 -6
  25. typevet-0.1.0.dev1/src/typevet/adapters/outbound/vllm_scoring.py → typevet-0.3.0/src/typevet/adapters/outbound/vllm/scoring.py +5 -5
  26. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/domain/candidate_scoring_request.py +9 -1
  27. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/domain/errors.py +1 -1
  28. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/domain/field_instructions.py +31 -9
  29. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/domain/judgment_normalize.py +22 -5
  30. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/domain/question_schema.py +11 -7
  31. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/ports/async_generation.py +1 -1
  32. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/ports/framing.py +12 -1
  33. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/runtime/llama_cpp_gemma_vision.py +3 -3
  34. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/runtime/vllm_judgment.py +3 -3
  35. typevet-0.1.0.dev1/src/typevet/adapters/inbound/cord_semantic_acceptance_cli.py +0 -135
  36. typevet-0.1.0.dev1/src/typevet/evaluation/__init__.py +0 -19
  37. typevet-0.1.0.dev1/src/typevet/evaluation/consumer_http_accounting.py +0 -135
  38. typevet-0.1.0.dev1/src/typevet/evaluation/cord_expense_call_accounting.py +0 -108
  39. typevet-0.1.0.dev1/src/typevet/evaluation/cord_expense_live_harness.py +0 -75
  40. typevet-0.1.0.dev1/src/typevet/evaluation/cord_expense_receipt_requirement.py +0 -212
  41. typevet-0.1.0.dev1/src/typevet/evaluation/cord_expense_smoke.py +0 -328
  42. typevet-0.1.0.dev1/src/typevet/evaluation/cord_semantic_acceptance.py +0 -463
  43. typevet-0.1.0.dev1/src/typevet/evaluation/cord_semantic_acceptance_report.py +0 -90
  44. typevet-0.1.0.dev1/src/typevet/evaluation/cord_semantic_metrics.py +0 -57
  45. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/__init__.py +0 -30
  46. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/banking77.py +0 -270
  47. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/boolq.py +0 -550
  48. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/boolq_download.py +0 -132
  49. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/civil_comments.py +0 -450
  50. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/clinc.py +0 -125
  51. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/clinc_domains.json +0 -172
  52. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/clinc_download.py +0 -103
  53. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/clinc_plus_intent_names.json +0 -153
  54. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/clinc_rows.py +0 -306
  55. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/clinc_shard.py +0 -156
  56. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/cord_expense.py +0 -381
  57. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/difraud.py +0 -256
  58. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/go_emotions.py +0 -368
  59. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/go_emotions_download.py +0 -154
  60. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/hyperpartisan.py +0 -562
  61. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/partner_guard.py +0 -170
  62. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/psai.py +0 -415
  63. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/psai_download.py +0 -167
  64. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/psai_evidence_pilot.py +0 -570
  65. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/psai_schema.py +0 -157
  66. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/psai_stream.py +0 -166
  67. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/psai_vision.py +0 -534
  68. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/psai_vision_controls.py +0 -502
  69. typevet-0.1.0.dev1/src/typevet/evaluation/datasets/pubmedqa.py +0 -420
  70. typevet-0.1.0.dev1/src/typevet/evaluation/experiment_identity.py +0 -677
  71. typevet-0.1.0.dev1/src/typevet/evaluation/psai_vision_consumer_accounting.py +0 -190
  72. typevet-0.1.0.dev1/src/typevet/evaluation/psai_vision_consumer_dispatch.py +0 -216
  73. typevet-0.1.0.dev1/src/typevet/evaluation/psai_vision_consumer_harness.py +0 -342
  74. typevet-0.1.0.dev1/src/typevet/evaluation/psai_vision_consumer_live.py +0 -332
  75. typevet-0.1.0.dev1/src/typevet/evaluation/psai_vision_consumer_live_identity.py +0 -156
  76. typevet-0.1.0.dev1/src/typevet/evaluation/psai_vision_consumer_live_receipt.py +0 -132
  77. typevet-0.1.0.dev1/src/typevet/evaluation/psai_vision_consumer_live_router.py +0 -162
  78. typevet-0.1.0.dev1/src/typevet/evaluation/psai_vision_consumer_offline.py +0 -418
  79. typevet-0.1.0.dev1/src/typevet/evaluation/psai_vision_consumer_outcomes.py +0 -224
  80. typevet-0.1.0.dev1/src/typevet/evaluation/psai_vision_consumer_protocol.py +0 -69
  81. typevet-0.1.0.dev1/src/typevet/evaluation/psai_vision_consumer_receipt.py +0 -161
  82. typevet-0.1.0.dev1/src/typevet/evaluation/psai_vision_consumer_receipt_structure.py +0 -263
  83. typevet-0.1.0.dev1/src/typevet/evaluation/psai_vision_probability_evidence.py +0 -336
  84. typevet-0.1.0.dev1/src/typevet/evaluation/runner/__init__.py +0 -56
  85. typevet-0.1.0.dev1/src/typevet/evaluation/runner/core.py +0 -104
  86. typevet-0.1.0.dev1/src/typevet/evaluation/runner/datasets.py +0 -186
  87. typevet-0.1.0.dev1/src/typevet/evaluation/runner/live_gate.py +0 -149
  88. typevet-0.1.0.dev1/src/typevet/evaluation/runner/report.py +0 -116
  89. typevet-0.1.0.dev1/src/typevet/evaluation/tpjep/__init__.py +0 -81
  90. typevet-0.1.0.dev1/src/typevet/evaluation/tpjep/loader.py +0 -237
  91. typevet-0.1.0.dev1/src/typevet/evaluation/tpjep/outcome.py +0 -101
  92. typevet-0.1.0.dev1/src/typevet/evaluation/tpjep/records.py +0 -428
  93. typevet-0.1.0.dev1/src/typevet/evaluation/tpjep/runner.py +0 -325
  94. {typevet-0.1.0.dev1 → typevet-0.3.0}/LICENSE +0 -0
  95. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/__init__.py +0 -0
  96. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/_version.py +0 -0
  97. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/__init__.py +0 -0
  98. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/diagnostics/__init__.py +0 -0
  99. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/diagnostics/fields.py +0 -0
  100. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/diagnostics/generation_events.py +0 -0
  101. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/diagnostics/http_events.py +0 -0
  102. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/diagnostics/logs.py +0 -0
  103. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/diagnostics/redaction.py +0 -0
  104. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/diagnostics/settings.py +0 -0
  105. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/inbound/__init__.py +0 -0
  106. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/inbound/api.py +0 -0
  107. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/inbound/helpers.py +0 -0
  108. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/outbound/fake.py +0 -0
  109. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/outbound/gemma/__init__.py +0 -0
  110. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/outbound/gemma/answer_binding.py +0 -0
  111. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/outbound/gemma/scoring_prefix.py +0 -0
  112. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/outbound/gemma/served_template.py +0 -0
  113. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/outbound/generation_finite.py +0 -0
  114. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/adapters/outbound/judgment_scoring.py +0 -0
  115. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/domain/__init__.py +0 -0
  116. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/domain/candidate_scoring_response.py +0 -0
  117. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/domain/candidate_scoring_validate.py +0 -0
  118. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/domain/decision_compile.py +0 -0
  119. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/domain/decision_execute.py +0 -0
  120. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/domain/decisions.py +0 -0
  121. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/domain/judgment_answers.py +0 -0
  122. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/domain/judgment_questions.py +0 -0
  123. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/domain/judgment_response.py +0 -0
  124. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/domain/media.py +0 -0
  125. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/domain/models.py +0 -0
  126. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/domain/scoring_stage.py +0 -0
  127. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/ports/__init__.py +0 -0
  128. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/ports/generation.py +0 -0
  129. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/ports/judgment.py +0 -0
  130. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/ports/scoring.py +0 -0
  131. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/py.typed +0 -0
  132. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/runtime/__init__.py +0 -0
  133. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/runtime/categorical.py +0 -0
  134. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/runtime/judgment.py +0 -0
  135. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/runtime/scoring_prefix.py +0 -0
  136. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/testing/__init__.py +0 -0
  137. {typevet-0.1.0.dev1 → typevet-0.3.0}/src/typevet/testing/fakes.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: typevet
3
- Version: 0.1.0.dev1
4
- Summary: Type-safe structured generation under hexagonal architecture (llama.cpp first)
3
+ Version: 0.3.0
4
+ Summary: Typed answers from model next-token probabilities, on llama.cpp and vLLM
5
5
  Keywords: evaluation,gemma,json-schema,llama-cpp,llm,structured-generation,structured-output,type-safe,vllm
6
6
  Author: Alberto-Codes
7
7
  Author-email: Alberto-Codes <alberto.codes.dev@gmail.com>
@@ -20,6 +20,7 @@ Requires-Dist: httpx>=0.28
20
20
  Requires-Dist: jsonschema>=4.26.0
21
21
  Requires-Dist: structlog>=24.1
22
22
  Requires-Dist: typer>=0.12 ; extra == 'cli'
23
+ Requires-Dist: judgevet>=0.14,<0.16 ; extra == 'judgevet'
23
24
  Requires-Python: >=3.12
24
25
  Project-URL: Homepage, https://github.com/Alberto-Codes/typevet
25
26
  Project-URL: Documentation, https://alberto-codes.github.io/typevet/
@@ -27,10 +28,12 @@ Project-URL: Repository, https://github.com/Alberto-Codes/typevet
27
28
  Project-URL: Issues, https://github.com/Alberto-Codes/typevet/issues
28
29
  Project-URL: Changelog, https://github.com/Alberto-Codes/typevet/blob/main/CHANGELOG.md
29
30
  Provides-Extra: cli
31
+ Provides-Extra: judgevet
30
32
  Description-Content-Type: text/markdown
31
33
 
32
34
  [![CI](https://img.shields.io/github/actions/workflow/status/Alberto-Codes/typevet/ci.yml?branch=main&label=CI)](https://github.com/Alberto-Codes/typevet/actions/workflows/ci.yml)
33
35
  [![Docs](https://img.shields.io/github/actions/workflow/status/Alberto-Codes/typevet/docs.yml?branch=main&label=docs)](https://alberto-codes.github.io/typevet/)
36
+ [![PyPI](https://img.shields.io/pypi/v/typevet)](https://pypi.org/project/typevet/)
34
37
  [![Python](https://img.shields.io/badge/python-3.12-blue)](https://github.com/Alberto-Codes/typevet/blob/main/pyproject.toml)
35
38
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
36
39
  [![docs vetted](https://img.shields.io/badge/docs%20vetted-docvet-purple)](https://github.com/Alberto-Codes/docvet)
@@ -49,18 +52,17 @@ Read the documentation at <https://alberto-codes.github.io/typevet/>.
49
52
 
50
53
  ## Status
51
54
 
52
- - typevet is pre-1.0. The package version is `0.1.0`.
53
- - typevet is not on PyPI yet.
54
- Build a wheel from a checkout and install it: see
55
- [Install typevet](https://github.com/Alberto-Codes/typevet/blob/main/docs/how-to/install.md).
55
+ - typevet is pre-1.0. The package version is `0.2.0`.
56
+ - Install typevet from PyPI: `pip install typevet` or `uv add typevet`.
57
+ See [Install typevet](https://alberto-codes.github.io/typevet/how-to/install/).
56
58
  - typevet requires Python 3.12 or later.
57
- - Each backend has one tested model pin.
58
- The receipts give the full pin and its limits.
59
+ - Each tested pin has one receipt.
60
+ The receipt gives the full pin and its limits.
59
61
 
60
62
  | Backend | Tested pin | Receipt |
61
63
  |---|---|---|
62
64
  | vLLM | `vllm/vllm-openai:v0.30.0`, BF16 `google/gemma-4-31B-it`, one H100 80 GB | [#170](https://github.com/Alberto-Codes/typevet/issues/170#issuecomment-5884707915) |
63
- | llama.cpp | Build `b11223-4da633776`, local alias `gemma-4-31b-kv9-q4km-mm` | [#203](https://github.com/Alberto-Codes/typevet/issues/203#issuecomment-5882379255) |
65
+ | llama.cpp | Build `b11223-4da633776`, local alias `gemma-4-31b-kv9-q4km-mm`: file `gemma-4-31b-24gib-kv9-decoder.gguf`, ftype `Q2_K - Medium` (not Q4_K_M), 16.0 GB, `--chat-template-file` override ([#233](https://github.com/Alberto-Codes/typevet/issues/233)) | [#203](https://github.com/Alberto-Codes/typevet/issues/203#issuecomment-5882379255) |
64
66
  | llama.cpp grammar | Build `b11243-fc07d781e`, Gemma 4 31B QAT Q4_0 GGUF | [#129](https://github.com/Alberto-Codes/typevet/issues/129#issuecomment-5892208050) |
65
67
 
66
68
  Performance: on one H100 at concurrency level 64, 480 Banking77 records took 12.1 s at 39.6 records/s.
@@ -75,8 +77,8 @@ The receipts are small samples.
75
77
  Get one offline typed judgment from a scripted fake. This step needs no model.
76
78
 
77
79
  ```bash
78
- uv sync
79
- uv run python -c "
80
+ pip install typevet
81
+ python -c "
80
82
  from typevet.domain import Noul
81
83
  from typevet.runtime import ScoringJudgmentAdapter
82
84
  from typevet.testing import ScriptedScoringFake
@@ -1,5 +1,6 @@
1
1
  [![CI](https://img.shields.io/github/actions/workflow/status/Alberto-Codes/typevet/ci.yml?branch=main&label=CI)](https://github.com/Alberto-Codes/typevet/actions/workflows/ci.yml)
2
2
  [![Docs](https://img.shields.io/github/actions/workflow/status/Alberto-Codes/typevet/docs.yml?branch=main&label=docs)](https://alberto-codes.github.io/typevet/)
3
+ [![PyPI](https://img.shields.io/pypi/v/typevet)](https://pypi.org/project/typevet/)
3
4
  [![Python](https://img.shields.io/badge/python-3.12-blue)](https://github.com/Alberto-Codes/typevet/blob/main/pyproject.toml)
4
5
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
5
6
  [![docs vetted](https://img.shields.io/badge/docs%20vetted-docvet-purple)](https://github.com/Alberto-Codes/docvet)
@@ -18,18 +19,17 @@ Read the documentation at <https://alberto-codes.github.io/typevet/>.
18
19
 
19
20
  ## Status
20
21
 
21
- - typevet is pre-1.0. The package version is `0.1.0`.
22
- - typevet is not on PyPI yet.
23
- Build a wheel from a checkout and install it: see
24
- [Install typevet](https://github.com/Alberto-Codes/typevet/blob/main/docs/how-to/install.md).
22
+ - typevet is pre-1.0. The package version is `0.2.0`.
23
+ - Install typevet from PyPI: `pip install typevet` or `uv add typevet`.
24
+ See [Install typevet](https://alberto-codes.github.io/typevet/how-to/install/).
25
25
  - typevet requires Python 3.12 or later.
26
- - Each backend has one tested model pin.
27
- The receipts give the full pin and its limits.
26
+ - Each tested pin has one receipt.
27
+ The receipt gives the full pin and its limits.
28
28
 
29
29
  | Backend | Tested pin | Receipt |
30
30
  |---|---|---|
31
31
  | vLLM | `vllm/vllm-openai:v0.30.0`, BF16 `google/gemma-4-31B-it`, one H100 80 GB | [#170](https://github.com/Alberto-Codes/typevet/issues/170#issuecomment-5884707915) |
32
- | llama.cpp | Build `b11223-4da633776`, local alias `gemma-4-31b-kv9-q4km-mm` | [#203](https://github.com/Alberto-Codes/typevet/issues/203#issuecomment-5882379255) |
32
+ | llama.cpp | Build `b11223-4da633776`, local alias `gemma-4-31b-kv9-q4km-mm`: file `gemma-4-31b-24gib-kv9-decoder.gguf`, ftype `Q2_K - Medium` (not Q4_K_M), 16.0 GB, `--chat-template-file` override ([#233](https://github.com/Alberto-Codes/typevet/issues/233)) | [#203](https://github.com/Alberto-Codes/typevet/issues/203#issuecomment-5882379255) |
33
33
  | llama.cpp grammar | Build `b11243-fc07d781e`, Gemma 4 31B QAT Q4_0 GGUF | [#129](https://github.com/Alberto-Codes/typevet/issues/129#issuecomment-5892208050) |
34
34
 
35
35
  Performance: on one H100 at concurrency level 64, 480 Banking77 records took 12.1 s at 39.6 records/s.
@@ -44,8 +44,8 @@ The receipts are small samples.
44
44
  Get one offline typed judgment from a scripted fake. This step needs no model.
45
45
 
46
46
  ```bash
47
- uv sync
48
- uv run python -c "
47
+ pip install typevet
48
+ python -c "
49
49
  from typevet.domain import Noul
50
50
  from typevet.runtime import ScoringJudgmentAdapter
51
51
  from typevet.testing import ScriptedScoringFake
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "typevet"
3
- version = "0.1.0.dev1"
4
- description = "Type-safe structured generation under hexagonal architecture (llama.cpp first)"
3
+ version = "0.3.0"
4
+ description = "Typed answers from model next-token probabilities, on llama.cpp and vLLM"
5
5
  readme = "README.md"
6
6
  license = "MIT"
7
7
  license-files = ["LICENSE"]
@@ -40,6 +40,7 @@ email = "alberto.codes.dev@gmail.com"
40
40
 
41
41
  [project.optional-dependencies]
42
42
  cli = ["typer>=0.12"]
43
+ judgevet = ["judgevet>=0.14,<0.16"]
43
44
 
44
45
  [project.urls]
45
46
  Homepage = "https://github.com/Alberto-Codes/typevet"
@@ -119,7 +120,7 @@ convention = "google"
119
120
  ]
120
121
  "evals/src/typevet_evals/wheel_isolated.py" = ["S603"]
121
122
  "scripts/check_commit_msg.py" = ["S603"]
122
- "src/typevet/evaluation/datasets/partner_guard.py" = [
123
+ "evals/src/typevet_evals/datasets/partner_guard.py" = [
123
124
  "S603",
124
125
  "S607",
125
126
  ]
@@ -149,7 +150,6 @@ addopts = [
149
150
  "-ra",
150
151
  "--strict-markers",
151
152
  "--strict-config",
152
- "--showlocals",
153
153
  "-m",
154
154
  "not live",
155
155
  ]
@@ -186,7 +186,7 @@ name = "Hexagonal layers"
186
186
  type = "layers"
187
187
  layers = [
188
188
  "typevet.runtime",
189
- "typevet.evaluation : typevet.adapters.inbound",
189
+ "typevet.adapters.inbound",
190
190
  "typevet.adapters.outbound",
191
191
  "typevet.adapters.diagnostics",
192
192
  "typevet.testing",
@@ -201,10 +201,56 @@ source_modules = ["typevet"]
201
201
  forbidden_modules = ["typevet_evals"]
202
202
 
203
203
  [[tool.importlinter.contracts]]
204
- name = "runtime_must_not_import_evaluation"
204
+ name = "Only the judgevet bridge imports judgevet"
205
205
  type = "forbidden"
206
- source_modules = ["typevet.runtime"]
207
- forbidden_modules = ["typevet.evaluation"]
206
+ source_modules = [
207
+ "typevet",
208
+ "typevet_evals",
209
+ ]
210
+ forbidden_modules = ["judgevet"]
211
+ ignore_imports = ["typevet.adapters.inbound.judgevet -> judgevet"]
212
+
213
+ [[tool.importlinter.contracts]]
214
+ name = "No typevet module imports the judgevet bridge"
215
+ type = "forbidden"
216
+ source_modules = [
217
+ "typevet",
218
+ "typevet.**",
219
+ "typevet_evals",
220
+ "typevet_evals.**",
221
+ ]
222
+ forbidden_modules = ["typevet.adapters.inbound.judgevet"]
223
+ as_packages = false
224
+
225
+ [[tool.importlinter.contracts]]
226
+ name = "Evaluation families"
227
+ type = "layers"
228
+ layers = [
229
+ "typevet_evals.cli | typevet_evals.gemma_native_vision_wheel_smoke | typevet_evals.psai_vision_probability_evidence | typevet_evals.wheel_isolated",
230
+ "typevet_evals.throughput",
231
+ "typevet_evals.vllm_acceptance",
232
+ "typevet_evals.instruction_variant | typevet_evals.cord",
233
+ "typevet_evals.psai_vision_consumer | typevet_evals.outcome_replay_metrics",
234
+ "typevet_evals.runner | typevet_evals.tpjep | typevet_evals.experiment_identity | typevet_evals.face_match",
235
+ "typevet_evals.datasets",
236
+ ]
237
+
238
+ [[tool.importlinter.contracts]]
239
+ name = "Model framing stays off the serving backends"
240
+ type = "forbidden"
241
+ source_modules = ["typevet.adapters.outbound.gemma"]
242
+ forbidden_modules = [
243
+ "typevet.adapters.outbound.llama_cpp",
244
+ "typevet.adapters.outbound.vllm",
245
+ ]
246
+
247
+ [[tool.importlinter.contracts]]
248
+ name = "Serving backends stay independent"
249
+ type = "independence"
250
+ modules = [
251
+ "typevet.adapters.outbound.llama_cpp",
252
+ "typevet.adapters.outbound.vllm",
253
+ ]
208
254
 
209
255
  [[tool.importlinter.contracts]]
210
256
  name = "Fakes stay off the adapters"
@@ -257,6 +303,7 @@ coauthor-grandfather-through = "1b1d9a3cfde019c5dbfe164c1660f1d3be28b5ba"
257
303
  dev = [
258
304
  "docvet[griffe]>=1.16.0",
259
305
  "import-linter>=2.15",
306
+ "judgevet[mcp,conformance]>=0.15,<0.16",
260
307
  "pre-commit>=4.6.2",
261
308
  "pytest>=9.1.1",
262
309
  "pytest-cov>=7.1.0",
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "typevet"
3
- version = "0.1.0.dev1"
4
- description = "Type-safe structured generation under hexagonal architecture (llama.cpp first)"
3
+ version = "0.3.0"
4
+ description = "Typed answers from model next-token probabilities, on llama.cpp and vLLM"
5
5
  readme = "README.md"
6
6
  license = "MIT"
7
7
  license-files = ["LICENSE"]
@@ -37,6 +37,7 @@ dependencies = [
37
37
 
38
38
  [project.optional-dependencies]
39
39
  cli = ["typer>=0.12"]
40
+ judgevet = ["judgevet>=0.14,<0.16"]
40
41
 
41
42
  [project.urls]
42
43
  Homepage = "https://github.com/Alberto-Codes/typevet"
@@ -56,6 +57,7 @@ include = ["src/typevet/py.typed"]
56
57
  dev = [
57
58
  "docvet[griffe]>=1.16.0",
58
59
  "import-linter>=2.15",
60
+ "judgevet[mcp,conformance]>=0.15,<0.16",
59
61
  "pre-commit>=4.6.2",
60
62
  "pytest>=9.1.1",
61
63
  "pytest-cov>=7.1.0",
@@ -131,7 +133,7 @@ convention = "google"
131
133
  ]
132
134
  "evals/src/typevet_evals/wheel_isolated.py" = ["S603"]
133
135
  "scripts/check_commit_msg.py" = ["S603"]
134
- "src/typevet/evaluation/datasets/partner_guard.py" = ["S603", "S607"]
136
+ "evals/src/typevet_evals/datasets/partner_guard.py" = ["S603", "S607"]
135
137
 
136
138
  [tool.ruff.lint.isort]
137
139
  known-first-party = ["typevet", "typevet_evals"]
@@ -152,7 +154,6 @@ addopts = [
152
154
  "-ra",
153
155
  "--strict-markers",
154
156
  "--strict-config",
155
- "--showlocals",
156
157
  "-m",
157
158
  "not live",
158
159
  ]
@@ -177,9 +178,7 @@ name = "Hexagonal layers"
177
178
  type = "layers"
178
179
  layers = [
179
180
  "typevet.runtime",
180
- # Evaluation sits beside the inbound adapters: the eval CLI is an inbound
181
- # adapter over evaluation, and the live gate reads inbound settings.
182
- "typevet.evaluation : typevet.adapters.inbound",
181
+ "typevet.adapters.inbound",
183
182
  "typevet.adapters.outbound",
184
183
  "typevet.adapters.diagnostics",
185
184
  "typevet.testing",
@@ -194,10 +193,51 @@ source_modules = ["typevet"]
194
193
  forbidden_modules = ["typevet_evals"]
195
194
 
196
195
  [[tool.importlinter.contracts]]
197
- name = "runtime_must_not_import_evaluation"
196
+ # The judgevet bridge (#284) is the one module that may import judgevet.
197
+ name = "Only the judgevet bridge imports judgevet"
198
198
  type = "forbidden"
199
- source_modules = ["typevet.runtime"]
200
- forbidden_modules = ["typevet.evaluation"]
199
+ source_modules = ["typevet", "typevet_evals"]
200
+ forbidden_modules = ["judgevet"]
201
+ ignore_imports = ["typevet.adapters.inbound.judgevet -> judgevet"]
202
+
203
+ [[tool.importlinter.contracts]]
204
+ # No package init or other module imports the bridge, so bare typevet stays judgevet-free.
205
+ name = "No typevet module imports the judgevet bridge"
206
+ type = "forbidden"
207
+ # Module by module: as packages, typevet.adapters.inbound would contain the bridge and be skipped.
208
+ source_modules = ["typevet", "typevet.**", "typevet_evals", "typevet_evals.**"]
209
+ forbidden_modules = ["typevet.adapters.inbound.judgevet"]
210
+ as_packages = false
211
+
212
+ [[tool.importlinter.contracts]]
213
+ name = "Evaluation families"
214
+ type = "layers"
215
+ layers = [
216
+ "typevet_evals.cli | typevet_evals.gemma_native_vision_wheel_smoke | typevet_evals.psai_vision_probability_evidence | typevet_evals.wheel_isolated",
217
+ "typevet_evals.throughput",
218
+ "typevet_evals.vllm_acceptance",
219
+ "typevet_evals.instruction_variant | typevet_evals.cord",
220
+ "typevet_evals.psai_vision_consumer | typevet_evals.outcome_replay_metrics",
221
+ "typevet_evals.runner | typevet_evals.tpjep | typevet_evals.experiment_identity | typevet_evals.face_match",
222
+ "typevet_evals.datasets",
223
+ ]
224
+
225
+ [[tool.importlinter.contracts]]
226
+ name = "Model framing stays off the serving backends"
227
+ type = "forbidden"
228
+ source_modules = ["typevet.adapters.outbound.gemma"]
229
+ forbidden_modules = [
230
+ "typevet.adapters.outbound.llama_cpp",
231
+ "typevet.adapters.outbound.vllm",
232
+ ]
233
+
234
+ [[tool.importlinter.contracts]]
235
+ name = "Serving backends stay independent"
236
+ type = "independence"
237
+ modules = [
238
+ "typevet.adapters.outbound.llama_cpp",
239
+ "typevet.adapters.outbound.vllm",
240
+ ]
201
241
 
202
242
  [[tool.importlinter.contracts]]
203
243
  name = "Fakes stay off the adapters"
@@ -15,14 +15,19 @@ The configured key never reaches the caller in clear text. ``VllmSettings``
15
15
  leaves it out of ``repr``. The adapters that ``generation_adapter`` and
16
16
  ``async_vllm_generation_adapter`` return catch each ``GenerationError`` from
17
17
  ``generate``, and the port that ``open_judgment`` yields catches each one from
18
- ``judge``. When the error text, an attribute such as a parsed payload, or the
19
- cause chain holds the raw or JSON-escaped key, the error is raised again as
20
- the same type with the key replaced by ``***`` and with no cause or context.
21
- A server that echoes the ``Authorization`` header therefore cannot put the key
22
- into a ``BackendHttpError``. Successful results are not changed.
23
- The HTTP clients are built the same way with or without a key, so environment
24
- proxy settings apply in both cases. Error messages name variables, never
25
- values, and a parse failure raises with no cause or context.
18
+ ``judge``. When a key is configured, each such error is raised again as a copy
19
+ of the same type with no cause or context. In the copy, the raw or JSON-escaped
20
+ key in the text or an attribute, such as a parsed payload, is replaced by
21
+ ``***``. Strings and bytes inside dicts, lists, tuples, sets and frozensets are
22
+ masked too, and masked bytes stay bytes. The copy is made even when the key
23
+ text is absent, because an httpx error in the cause chain holds the request
24
+ headers. A server that echoes the ``Authorization`` header therefore cannot put
25
+ the key into a ``BackendHttpError``. Without a key, errors pass through
26
+ unchanged. Successful results are not changed. The HTTP clients are built the
27
+ same way with or without a key, so environment proxy settings apply in both
28
+ cases. ``TYPEVET_VLLM__TIMEOUT`` must be finite and positive, so ``nan`` and
29
+ ``inf`` fail. Error messages name variables, never values, and a parse failure
30
+ raises with no cause or context.
26
31
 
27
32
  Examples:
28
33
  ```python
@@ -41,9 +46,11 @@ Examples:
41
46
 
42
47
  See Also:
43
48
  - [typevet.adapters.inbound.settings][]: ``TYPEVET_LLAMA__*`` settings
44
- - [typevet.adapters.outbound.vllm_generation][]: vLLM generation adapter
45
- - [typevet.adapters.outbound.vllm_generation_async][]: Async vLLM adapter
46
- - [typevet.adapters.outbound.vllm_judgment_factory][]: vLLM judgment factory
49
+ - [typevet.adapters.outbound.llama_cpp][]: llama.cpp adapters and the Gemma
50
+ native vision factory module
51
+ - [typevet.adapters.outbound.vllm.generation][]: vLLM generation adapter
52
+ - [typevet.adapters.outbound.vllm.generation_async][]: Async vLLM adapter
53
+ - [typevet.adapters.outbound.vllm.judgment_factory][]: vLLM judgment factory
47
54
  - [typevet.adapters.diagnostics.redaction][]: ``REDACTED`` (``***``) mask
48
55
  - docs/reference/configuration.md: Environment variable reference
49
56
  """
@@ -51,6 +58,7 @@ See Also:
51
58
  from __future__ import annotations
52
59
 
53
60
  import json
61
+ import math
54
62
  import os
55
63
  from collections.abc import Iterator, Mapping
56
64
  from contextlib import contextmanager
@@ -61,14 +69,14 @@ import httpx
61
69
 
62
70
  from typevet.adapters.diagnostics.redaction import REDACTED
63
71
  from typevet.adapters.inbound.settings import llama_cpp_adapter, load_llama_settings
64
- from typevet.adapters.outbound.gemma_native_vision_factory import (
72
+ from typevet.adapters.outbound.llama_cpp.gemma_native_vision_factory import (
65
73
  GemmaNativeVisionSession,
66
74
  open_gemma_native_vision_judgment,
67
75
  )
68
- from typevet.adapters.outbound.llama_cpp import LlamaCppGenerationAdapter
69
- from typevet.adapters.outbound.vllm_generation import VllmGenerationAdapter
70
- from typevet.adapters.outbound.vllm_generation_async import AsyncVllmGenerationAdapter
71
- from typevet.adapters.outbound.vllm_judgment_factory import (
76
+ from typevet.adapters.outbound.llama_cpp.generation import LlamaCppGenerationAdapter
77
+ from typevet.adapters.outbound.vllm.generation import VllmGenerationAdapter
78
+ from typevet.adapters.outbound.vllm.generation_async import AsyncVllmGenerationAdapter
79
+ from typevet.adapters.outbound.vllm.judgment_factory import (
72
80
  VllmJudgmentSession,
73
81
  open_vllm_judgment,
74
82
  )
@@ -117,63 +125,54 @@ class VllmSettings:
117
125
  user_agent: str | None = None
118
126
 
119
127
 
120
- def _masked(value: Any, needles: tuple[str, ...]) -> Any:
121
- """Replace each key form in ``value`` with ``MASK``.
128
+ _PLAIN_CONTAINERS: tuple[type, ...] = (list, tuple, set, frozenset)
129
+
122
130
 
123
- Strings are masked in place of each needle. Dicts, lists and tuples are
124
- copied as plain containers with each key and item masked, so a parsed payload that holds the
125
- key loses it. Other values are returned unchanged.
131
+ def _masked_text(value: str | bytes, needles: tuple[str, ...]) -> str | bytes:
132
+ """Replace each needle in ``value`` with ``MASK``, keeping its type.
133
+
134
+ Bytes stay bytes: each needle and ``MASK`` are ASCII-encoded first, which
135
+ is safe because the key must be ASCII.
126
136
 
127
137
  Args:
128
- value: String, container or other attribute value.
138
+ value: String or bytes to mask.
129
139
  needles: Key forms to replace.
130
140
 
131
141
  Returns:
132
- The masked value.
142
+ The masked string or bytes.
133
143
  """
134
144
  if isinstance(value, str):
135
145
  for needle in needles:
136
146
  value = value.replace(needle, MASK)
137
147
  return value
138
- if isinstance(value, dict):
139
- return {_masked(k, needles): _masked(v, needles) for k, v in value.items()}
140
- if isinstance(value, list):
141
- return [_masked(item, needles) for item in value]
142
- if isinstance(value, tuple):
143
- return tuple(_masked(item, needles) for item in value)
148
+ for needle in needles:
149
+ value = value.replace(needle.encode("ascii"), MASK.encode("ascii"))
144
150
  return value
145
151
 
146
152
 
147
- def _texts(value: Any) -> Iterator[str]:
148
- """Yield every string inside ``value``, including dict keys.
153
+ def _masked(value: Any, needles: tuple[str, ...]) -> Any:
154
+ """Replace each key form in ``value`` with ``MASK``.
155
+
156
+ Strings and bytes are masked by ``_masked_text`` and keep their type.
157
+ Dicts, lists, tuples, sets and frozensets are copied as plain containers
158
+ of the same kind with each key and item masked, so a parsed payload that
159
+ holds the key loses it. Other values are returned unchanged.
149
160
 
150
161
  Args:
151
- value: String, container or other attribute value.
162
+ value: String, bytes, container or other attribute value.
163
+ needles: Key forms to replace.
152
164
 
153
- Yields:
154
- Each string that ``_masked`` would mask.
165
+ Returns:
166
+ The masked value.
155
167
  """
156
- if isinstance(value, str):
157
- yield value
158
- elif isinstance(value, dict):
159
- for key, item in value.items():
160
- yield from _texts(key)
161
- yield from _texts(item)
162
- elif isinstance(value, list | tuple):
163
- for item in value:
164
- yield from _texts(item)
165
-
166
-
167
- def _chain_text(exc: BaseException) -> str:
168
- parts: list[str] = []
169
- seen: set[int] = set()
170
- current: BaseException | None = exc
171
- while current is not None and id(current) not in seen:
172
- seen.add(id(current))
173
- parts += [str(current), repr(current)]
174
- parts += [text for v in vars(current).values() for text in _texts(v)]
175
- current = current.__cause__ or current.__context__
176
- return "\n".join(parts)
168
+ if isinstance(value, (str, bytes)):
169
+ return _masked_text(value, needles)
170
+ if isinstance(value, dict):
171
+ return {_masked(k, needles): _masked(v, needles) for k, v in value.items()}
172
+ for kind in _PLAIN_CONTAINERS:
173
+ if isinstance(value, kind):
174
+ return kind(_masked(item, needles) for item in value)
175
+ return value
177
176
 
178
177
 
179
178
  def _masked_error(exc: GenerationError, needles: tuple[str, ...]) -> GenerationError:
@@ -188,7 +187,8 @@ def _masked_error(exc: GenerationError, needles: tuple[str, ...]) -> GenerationE
188
187
 
189
188
  Returns:
190
189
  A new error whose arguments and attributes are masked, including
191
- strings inside dict, list and tuple values such as a payload.
190
+ strings and bytes inside dict, list, tuple, set and frozenset
191
+ values such as a payload.
192
192
  """
193
193
  masked = type(exc).__new__(type(exc))
194
194
  masked.args = _masked(exc.args, needles)
@@ -206,17 +206,20 @@ def _key_needles(key: str | None) -> tuple[str, ...]:
206
206
  def _masked_if_keyed(
207
207
  exc: GenerationError, needles: tuple[str, ...]
208
208
  ) -> GenerationError | None:
209
- """Return a masked copy of ``exc`` when its chain holds a key form.
209
+ """Return a masked copy of ``exc`` when a key is configured.
210
+
211
+ The copy is made whether or not the key text appears in ``exc``. The
212
+ cause chain can hold the key where no text check sees it, for example in
213
+ the headers of an httpx request, so the copy drops the chain in all cases.
210
214
 
211
215
  Args:
212
216
  exc: Error raised by a vLLM adapter.
213
- needles: Key forms from ``_key_needles``.
217
+ needles: Key forms from ``_key_needles``, or empty without a key.
214
218
 
215
219
  Returns:
216
- The masked copy, or ``None`` when no key form is present.
220
+ The masked copy, or ``None`` when no key is configured.
217
221
  """
218
- chain = _chain_text(exc)
219
- if not any(needle in chain for needle in needles):
222
+ if not needles:
220
223
  return None
221
224
  return _masked_error(exc, needles)
222
225
 
@@ -258,7 +261,7 @@ class _KeyMaskingJudgmentPort:
258
261
  The response from the wrapped port, unchanged.
259
262
 
260
263
  Raises:
261
- GenerationError: The port error, masked when it holds the key.
264
+ GenerationError: The port error, as a masked copy when a key is set.
262
265
  """
263
266
  try:
264
267
  return self._port.judge(state, questions, model, media=media)
@@ -291,10 +294,11 @@ class _ClientOwningVllmAdapter(VllmGenerationAdapter):
291
294
  def generate(self, request: GenerationRequest) -> GenerationResult:
292
295
  """Generate, and mask the configured key in any raised error.
293
296
 
294
- A successful result is returned unchanged. ``_masked_if_keyed``
295
- checks each error: one whose text or cause chain holds the key is
296
- raised again as the same type with the key replaced by ``MASK`` and
297
- with no cause or context.
297
+ A successful result is returned unchanged. When a key is configured,
298
+ ``_masked_if_keyed`` copies each error as the same type with the key
299
+ replaced by ``MASK`` and with no cause or context. The copy is raised
300
+ outside the ``except`` block, so the original error is not reachable
301
+ from it.
298
302
 
299
303
  Args:
300
304
  request: Prompt, schema and served model name.
@@ -303,7 +307,7 @@ class _ClientOwningVllmAdapter(VllmGenerationAdapter):
303
307
  The result from ``VllmGenerationAdapter.generate``.
304
308
 
305
309
  Raises:
306
- GenerationError: The adapter error, masked when it holds the key.
310
+ GenerationError: The adapter error, as a masked copy when a key is set.
307
311
  """
308
312
  try:
309
313
  return super().generate(request)
@@ -348,10 +352,10 @@ class _ClientOwningAsyncVllmAdapter(AsyncVllmGenerationAdapter):
348
352
  async def generate(self, request: GenerationRequest) -> GenerationResult:
349
353
  """Generate, and mask the configured key in any raised error.
350
354
 
351
- Masking follows ``_ClientOwningVllmAdapter.generate``: an error whose
352
- text, attributes or cause chain holds the key is raised again as the
353
- same type with the key replaced by ``MASK`` and with no cause or
354
- context. A successful result is returned unchanged.
355
+ Masking follows ``_ClientOwningVllmAdapter.generate``: when a key is
356
+ configured, each error is raised again as a copy of the same type
357
+ with the key replaced by ``MASK`` and with no cause or context. A
358
+ successful result is returned unchanged.
355
359
 
356
360
  Args:
357
361
  request: Prompt, schema and served model name.
@@ -360,7 +364,7 @@ class _ClientOwningAsyncVllmAdapter(AsyncVllmGenerationAdapter):
360
364
  The result from ``AsyncVllmGenerationAdapter.generate``.
361
365
 
362
366
  Raises:
363
- GenerationError: The adapter error, masked when it holds the key.
367
+ GenerationError: The adapter error, as a masked copy when a key is set.
364
368
  RuntimeError: When the call runs on a different event loop from
365
369
  the first call. It is not masked, because it holds no key.
366
370
  """
@@ -386,8 +390,8 @@ def _read_timeout(source: Mapping[str, str], name: str) -> float:
386
390
  timeout = float(raw)
387
391
  except ValueError:
388
392
  timeout = 0.0
389
- if timeout <= 0:
390
- msg = f"{name} must be a positive number of seconds"
393
+ if not math.isfinite(timeout) or timeout <= 0:
394
+ msg = f"{name} must be a finite positive number of seconds"
391
395
  raise ValueError(msg) from None
392
396
  return timeout
393
397
 
@@ -449,7 +453,8 @@ def load_vllm_settings(environ: Mapping[str, str] | None = None) -> VllmSettings
449
453
 
450
454
  Raises:
451
455
  ValueError: When ``TYPEVET_VLLM__BASE_URL`` or ``TYPEVET_VLLM__MODEL``
452
- is missing, ``TYPEVET_VLLM__TIMEOUT`` is not a positive number,
456
+ is missing, ``TYPEVET_VLLM__TIMEOUT`` is not a finite positive
457
+ number,
453
458
  ``TYPEVET_VLLM__MAX_CONCURRENCY`` is not a positive integer, or
454
459
  ``TYPEVET_VLLM__API_KEY`` holds a non-ASCII character.
455
460
  """