typevet 0.1.0.dev1__py3-none-any.whl

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 (133) hide show
  1. typevet/__init__.py +67 -0
  2. typevet/_version.py +43 -0
  3. typevet/adapters/__init__.py +16 -0
  4. typevet/adapters/diagnostics/__init__.py +59 -0
  5. typevet/adapters/diagnostics/fields.py +108 -0
  6. typevet/adapters/diagnostics/generation_events.py +75 -0
  7. typevet/adapters/diagnostics/http_events.py +91 -0
  8. typevet/adapters/diagnostics/logs.py +116 -0
  9. typevet/adapters/diagnostics/redaction.py +94 -0
  10. typevet/adapters/diagnostics/settings.py +77 -0
  11. typevet/adapters/inbound/__init__.py +65 -0
  12. typevet/adapters/inbound/api.py +61 -0
  13. typevet/adapters/inbound/backend_settings.py +608 -0
  14. typevet/adapters/inbound/cord_semantic_acceptance_cli.py +135 -0
  15. typevet/adapters/inbound/helpers.py +67 -0
  16. typevet/adapters/inbound/settings.py +154 -0
  17. typevet/adapters/outbound/__init__.py +62 -0
  18. typevet/adapters/outbound/async_fake.py +123 -0
  19. typevet/adapters/outbound/async_llama_cpp.py +168 -0
  20. typevet/adapters/outbound/chat_completion.py +148 -0
  21. typevet/adapters/outbound/fake.py +119 -0
  22. typevet/adapters/outbound/gemma/__init__.py +84 -0
  23. typevet/adapters/outbound/gemma/answer_binding.py +290 -0
  24. typevet/adapters/outbound/gemma/scoring_prefix.py +102 -0
  25. typevet/adapters/outbound/gemma/served_template.py +131 -0
  26. typevet/adapters/outbound/gemma_native_vision_factory.py +286 -0
  27. typevet/adapters/outbound/generation_finite.py +50 -0
  28. typevet/adapters/outbound/http_errors.py +38 -0
  29. typevet/adapters/outbound/judgment_scoring.py +471 -0
  30. typevet/adapters/outbound/llama_cpp.py +187 -0
  31. typevet/adapters/outbound/llama_cpp_http.py +103 -0
  32. typevet/adapters/outbound/llama_cpp_multimodal.py +145 -0
  33. typevet/adapters/outbound/llama_cpp_scoring.py +333 -0
  34. typevet/adapters/outbound/vllm_content.py +63 -0
  35. typevet/adapters/outbound/vllm_generation.py +163 -0
  36. typevet/adapters/outbound/vllm_generation_async.py +257 -0
  37. typevet/adapters/outbound/vllm_http.py +121 -0
  38. typevet/adapters/outbound/vllm_judgment_factory.py +210 -0
  39. typevet/adapters/outbound/vllm_scoring.py +372 -0
  40. typevet/domain/__init__.py +193 -0
  41. typevet/domain/candidate_scoring_request.py +134 -0
  42. typevet/domain/candidate_scoring_response.py +96 -0
  43. typevet/domain/candidate_scoring_validate.py +197 -0
  44. typevet/domain/decision_compile.py +368 -0
  45. typevet/domain/decision_execute.py +229 -0
  46. typevet/domain/decisions.py +129 -0
  47. typevet/domain/errors.py +235 -0
  48. typevet/domain/field_instructions.py +134 -0
  49. typevet/domain/judgment_answers.py +198 -0
  50. typevet/domain/judgment_normalize.py +243 -0
  51. typevet/domain/judgment_questions.py +187 -0
  52. typevet/domain/judgment_response.py +102 -0
  53. typevet/domain/media.py +101 -0
  54. typevet/domain/models.py +115 -0
  55. typevet/domain/question_schema.py +254 -0
  56. typevet/domain/scoring_stage.py +34 -0
  57. typevet/evaluation/__init__.py +19 -0
  58. typevet/evaluation/consumer_http_accounting.py +135 -0
  59. typevet/evaluation/cord_expense_call_accounting.py +108 -0
  60. typevet/evaluation/cord_expense_live_harness.py +75 -0
  61. typevet/evaluation/cord_expense_receipt_requirement.py +212 -0
  62. typevet/evaluation/cord_expense_smoke.py +328 -0
  63. typevet/evaluation/cord_semantic_acceptance.py +463 -0
  64. typevet/evaluation/cord_semantic_acceptance_report.py +90 -0
  65. typevet/evaluation/cord_semantic_metrics.py +57 -0
  66. typevet/evaluation/datasets/__init__.py +30 -0
  67. typevet/evaluation/datasets/banking77.py +270 -0
  68. typevet/evaluation/datasets/boolq.py +550 -0
  69. typevet/evaluation/datasets/boolq_download.py +132 -0
  70. typevet/evaluation/datasets/civil_comments.py +450 -0
  71. typevet/evaluation/datasets/clinc.py +125 -0
  72. typevet/evaluation/datasets/clinc_domains.json +172 -0
  73. typevet/evaluation/datasets/clinc_download.py +103 -0
  74. typevet/evaluation/datasets/clinc_plus_intent_names.json +153 -0
  75. typevet/evaluation/datasets/clinc_rows.py +306 -0
  76. typevet/evaluation/datasets/clinc_shard.py +156 -0
  77. typevet/evaluation/datasets/cord_expense.py +381 -0
  78. typevet/evaluation/datasets/difraud.py +256 -0
  79. typevet/evaluation/datasets/go_emotions.py +368 -0
  80. typevet/evaluation/datasets/go_emotions_download.py +154 -0
  81. typevet/evaluation/datasets/hyperpartisan.py +562 -0
  82. typevet/evaluation/datasets/partner_guard.py +170 -0
  83. typevet/evaluation/datasets/psai.py +415 -0
  84. typevet/evaluation/datasets/psai_download.py +167 -0
  85. typevet/evaluation/datasets/psai_evidence_pilot.py +570 -0
  86. typevet/evaluation/datasets/psai_schema.py +157 -0
  87. typevet/evaluation/datasets/psai_stream.py +166 -0
  88. typevet/evaluation/datasets/psai_vision.py +534 -0
  89. typevet/evaluation/datasets/psai_vision_controls.py +502 -0
  90. typevet/evaluation/datasets/pubmedqa.py +420 -0
  91. typevet/evaluation/experiment_identity.py +677 -0
  92. typevet/evaluation/psai_vision_consumer_accounting.py +190 -0
  93. typevet/evaluation/psai_vision_consumer_dispatch.py +216 -0
  94. typevet/evaluation/psai_vision_consumer_harness.py +342 -0
  95. typevet/evaluation/psai_vision_consumer_live.py +332 -0
  96. typevet/evaluation/psai_vision_consumer_live_identity.py +156 -0
  97. typevet/evaluation/psai_vision_consumer_live_receipt.py +132 -0
  98. typevet/evaluation/psai_vision_consumer_live_router.py +162 -0
  99. typevet/evaluation/psai_vision_consumer_offline.py +418 -0
  100. typevet/evaluation/psai_vision_consumer_outcomes.py +224 -0
  101. typevet/evaluation/psai_vision_consumer_protocol.py +69 -0
  102. typevet/evaluation/psai_vision_consumer_receipt.py +161 -0
  103. typevet/evaluation/psai_vision_consumer_receipt_structure.py +263 -0
  104. typevet/evaluation/psai_vision_probability_evidence.py +336 -0
  105. typevet/evaluation/runner/__init__.py +56 -0
  106. typevet/evaluation/runner/core.py +104 -0
  107. typevet/evaluation/runner/datasets.py +186 -0
  108. typevet/evaluation/runner/live_gate.py +149 -0
  109. typevet/evaluation/runner/report.py +116 -0
  110. typevet/evaluation/tpjep/__init__.py +81 -0
  111. typevet/evaluation/tpjep/loader.py +237 -0
  112. typevet/evaluation/tpjep/outcome.py +101 -0
  113. typevet/evaluation/tpjep/records.py +428 -0
  114. typevet/evaluation/tpjep/runner.py +325 -0
  115. typevet/ports/__init__.py +39 -0
  116. typevet/ports/async_generation.py +57 -0
  117. typevet/ports/framing.py +59 -0
  118. typevet/ports/generation.py +58 -0
  119. typevet/ports/judgment.py +71 -0
  120. typevet/ports/scoring.py +64 -0
  121. typevet/py.typed +0 -0
  122. typevet/runtime/__init__.py +50 -0
  123. typevet/runtime/categorical.py +196 -0
  124. typevet/runtime/judgment.py +17 -0
  125. typevet/runtime/llama_cpp_gemma_vision.py +28 -0
  126. typevet/runtime/scoring_prefix.py +14 -0
  127. typevet/runtime/vllm_judgment.py +28 -0
  128. typevet/testing/__init__.py +22 -0
  129. typevet/testing/fakes.py +152 -0
  130. typevet-0.1.0.dev1.dist-info/METADATA +125 -0
  131. typevet-0.1.0.dev1.dist-info/RECORD +133 -0
  132. typevet-0.1.0.dev1.dist-info/WHEEL +4 -0
  133. typevet-0.1.0.dev1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,286 @@
1
+ """Gemma native-turn vision judgment factory over llama.cpp ([#174][i174], [#196][i196]).
2
+
3
+ Examples:
4
+ ```python
5
+ from typevet.adapters.inbound.settings import load_llama_settings
6
+ from typevet.adapters.outbound.gemma_native_vision_factory import (
7
+ open_gemma_native_vision_judgment,
8
+ )
9
+
10
+ settings = load_llama_settings()
11
+ with open_gemma_native_vision_judgment(settings=settings) as session:
12
+ session.port.judge("Hello", questions, session.model)
13
+ ```
14
+
15
+ The factory pins ``session.model`` on ``session.port``; other model ids fail
16
+ before tokenization. Optional ``tokenize_content`` and ``scoring_port_wrapper``
17
+ hooks support consumer dispatch ledgers without importing ``typevet.evaluation``.
18
+
19
+ See Also:
20
+ - [typevet.adapters.outbound.judgment_scoring][]: ``ScoringJudgmentAdapter``
21
+ - [typevet.runtime][]: public re-exports for library callers
22
+ - [docs.how-to.connect-gemma4-native-vision-judgment][]: Composition how-to
23
+
24
+ [i174]: https://github.com/Alberto-Codes/typevet/issues/174
25
+ [i196]: https://github.com/Alberto-Codes/typevet/issues/196
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ from collections.abc import Callable, Iterator, Sequence
31
+ from contextlib import contextmanager
32
+ from dataclasses import dataclass
33
+ from typing import Any, Protocol
34
+
35
+ import httpx
36
+
37
+ from typevet.adapters.outbound.gemma import (
38
+ ServedTemplateClass,
39
+ classify_served_template,
40
+ )
41
+ from typevet.adapters.outbound.judgment_scoring import ScoringJudgmentAdapter
42
+ from typevet.adapters.outbound.llama_cpp_multimodal import (
43
+ MediaCapability,
44
+ fetch_media_capability,
45
+ )
46
+ from typevet.adapters.outbound.llama_cpp_scoring import (
47
+ DEFAULT_N_VOCAB,
48
+ LlamaCppCandidateScoringAdapter,
49
+ )
50
+ from typevet.ports.judgment import JudgmentPort
51
+ from typevet.ports.scoring import CandidateScoringPort
52
+
53
+
54
+ class GemmaVisionSettings(Protocol):
55
+ """Router connection options the factory reads from the composition root.
56
+
57
+ Examples:
58
+ ```python
59
+ from typevet.adapters.inbound.settings import load_llama_settings
60
+
61
+ settings = load_llama_settings()
62
+ assert settings.base_url
63
+ ```
64
+ """
65
+
66
+ @property
67
+ def base_url(self) -> str:
68
+ """Router base URL."""
69
+ ...
70
+
71
+ @property
72
+ def timeout(self) -> float:
73
+ """HTTP timeout in seconds."""
74
+ ...
75
+
76
+ @property
77
+ def multimodal_model(self) -> str:
78
+ """Default multimodal model id."""
79
+ ...
80
+
81
+
82
+ _SUPPORTED_NATIVE = frozenset(
83
+ {
84
+ ServedTemplateClass.NATIVE_GEMMA3_TURN,
85
+ ServedTemplateClass.NATIVE_GEMMA4_TURN,
86
+ }
87
+ )
88
+
89
+
90
+ @dataclass(frozen=True, slots=True)
91
+ class GemmaNativeVisionSession:
92
+ """Live router session with a configured ``JudgmentPort``.
93
+
94
+ Attributes:
95
+ port (JudgmentPort): Scoring-backed judgment for native media turns.
96
+ client (httpx.Client): Shared HTTP client for the router base URL.
97
+ model (str): Model id probed for template and vision capability.
98
+ served (ServedTemplateClass): Classified native template family.
99
+ capability (MediaCapability): Router media capability for ``model``.
100
+
101
+ Examples:
102
+ ```python
103
+ from typevet.adapters.outbound.gemma_native_vision_factory import (
104
+ GemmaNativeVisionSession,
105
+ )
106
+
107
+ assert GemmaNativeVisionSession.__dataclass_fields__
108
+ ```
109
+ """
110
+
111
+ port: JudgmentPort
112
+ client: httpx.Client
113
+ model: str
114
+ served: ServedTemplateClass
115
+ capability: MediaCapability
116
+
117
+
118
+ def _classify_native_template(
119
+ client: httpx.Client,
120
+ model: str,
121
+ *,
122
+ require_gemma4: bool,
123
+ ) -> ServedTemplateClass:
124
+ rendered = (
125
+ client.post(
126
+ "/apply-template",
127
+ json={
128
+ "model": model,
129
+ "messages": [{"role": "user", "content": "hello"}],
130
+ "add_generation_prompt": True,
131
+ },
132
+ )
133
+ .raise_for_status()
134
+ .json()["prompt"]
135
+ )
136
+ family = classify_served_template(rendered)
137
+ if require_gemma4 and family is not ServedTemplateClass.NATIVE_GEMMA4_TURN:
138
+ msg = f"expected NATIVE_GEMMA4_TURN, got {family.value}"
139
+ raise ValueError(msg)
140
+ if family not in _SUPPORTED_NATIVE:
141
+ msg = f"unsupported served template for native vision: {family.value}"
142
+ raise ValueError(msg)
143
+ return family
144
+
145
+
146
+ def _tokenize_factory(
147
+ client: httpx.Client,
148
+ model: str,
149
+ ) -> Callable[[str], tuple[int, ...]]:
150
+ def tokenize(text: str) -> tuple[int, ...]:
151
+ """Tokenize ``text`` through the router ``/tokenize`` endpoint.
152
+
153
+ Returns:
154
+ Token id tuple from the router JSON body.
155
+ """
156
+ body = client.post(
157
+ "/tokenize",
158
+ json={"model": model, "content": text, "add_special": False},
159
+ )
160
+ return tuple(body.raise_for_status().json()["tokens"])
161
+
162
+ return tokenize
163
+
164
+
165
+ @contextmanager
166
+ def open_gemma_native_vision_judgment(
167
+ *,
168
+ settings: GemmaVisionSettings,
169
+ model: str | None = None,
170
+ require_gemma4: bool = True,
171
+ n_vocab: int = DEFAULT_N_VOCAB,
172
+ http_client: httpx.Client | None = None,
173
+ tokenize_content: Callable[[str], Sequence[int]] | None = None,
174
+ scoring_port_wrapper: Callable[[CandidateScoringPort], CandidateScoringPort]
175
+ | None = None,
176
+ ) -> Iterator[GemmaNativeVisionSession]:
177
+ """Open a judgment port for Gemma native-turn vision on a llama.cpp router.
178
+
179
+ Probes ``/apply-template`` and media capability before the first ``judge``
180
+ call. Unsupported template families or text-only models raise ``ValueError``
181
+ before scoring dispatch.
182
+
183
+ Args:
184
+ settings: Router connection options from the composition root.
185
+ model: Model id; defaults to ``settings.multimodal_model``.
186
+ require_gemma4: When true, require ``NATIVE_GEMMA4_TURN``.
187
+ n_vocab: ``n_probs`` budget for Gemma 4 class vocab scoring.
188
+ http_client: Optional pre-built client (for tests); not closed on exit.
189
+ tokenize_content: Optional tokenizer hook; defaults to router ``/tokenize``.
190
+ scoring_port_wrapper: Optional wrapper applied before ``JudgmentPort`` wiring.
191
+
192
+ Yields:
193
+ A session holding the configured ``JudgmentPort`` and probe metadata.
194
+
195
+ Raises:
196
+ ValueError: When vision is unavailable or the template is unsupported.
197
+ """
198
+ model_id = model or settings.multimodal_model
199
+ base = settings.base_url.rstrip("/")
200
+
201
+ def _session(client: httpx.Client) -> Iterator[GemmaNativeVisionSession]:
202
+ capability = fetch_media_capability(client, f"{base}/", model_id)
203
+ if not capability.vision:
204
+ msg = "model reports text-only input modalities"
205
+ raise ValueError(msg)
206
+ served = _classify_native_template(
207
+ client, model_id, require_gemma4=require_gemma4
208
+ )
209
+ tokenize = tokenize_content or _tokenize_factory(client, model_id)
210
+ scoring = LlamaCppCandidateScoringAdapter(
211
+ base_url=base,
212
+ timeout=settings.timeout,
213
+ client=client,
214
+ n_vocab=n_vocab,
215
+ media_capabilities={model_id: capability},
216
+ )
217
+ scoring_port: CandidateScoringPort = scoring
218
+ if scoring_port_wrapper is not None:
219
+ scoring_port = scoring_port_wrapper(scoring)
220
+ port: JudgmentPort = ScoringJudgmentAdapter(
221
+ scoring_port,
222
+ tokenize_content=tokenize,
223
+ served_template=served,
224
+ pinned_model=model_id,
225
+ )
226
+ try:
227
+ yield GemmaNativeVisionSession(
228
+ port=port,
229
+ client=client,
230
+ model=model_id,
231
+ served=served,
232
+ capability=capability,
233
+ )
234
+ finally:
235
+ scoring.close()
236
+
237
+ if http_client is not None:
238
+ yield from _session(http_client)
239
+ return
240
+ with httpx.Client(base_url=base, timeout=settings.timeout) as owned:
241
+ yield from _session(owned)
242
+
243
+
244
+ def probe_gemma_native_vision_support(
245
+ *,
246
+ settings: GemmaVisionSettings,
247
+ model: str | None = None,
248
+ require_gemma4: bool = True,
249
+ http_client: httpx.Client | None = None,
250
+ ) -> dict[str, Any]:
251
+ """Return probe metadata without constructing a long-lived judgment port.
252
+
253
+ Args:
254
+ settings: Router connection options.
255
+ model: Model id; defaults to ``settings.multimodal_model``.
256
+ require_gemma4: When true, require ``NATIVE_GEMMA4_TURN``.
257
+ http_client: Optional pre-built client (for tests).
258
+
259
+ Returns:
260
+ Mapping with ``ok``, ``model``, ``served``, and ``vision`` keys.
261
+
262
+ Raises:
263
+ ValueError: When the router rejects the probe (same rules as open).
264
+ """
265
+ model_id = model or settings.multimodal_model
266
+ base = settings.base_url.rstrip("/")
267
+
268
+ def _probe(client: httpx.Client) -> dict[str, Any]:
269
+ capability = fetch_media_capability(client, f"{base}/", model_id)
270
+ if not capability.vision:
271
+ msg = "model reports text-only input modalities"
272
+ raise ValueError(msg)
273
+ served = _classify_native_template(
274
+ client, model_id, require_gemma4=require_gemma4
275
+ )
276
+ return {
277
+ "ok": True,
278
+ "model": model_id,
279
+ "served": served.value,
280
+ "vision": capability.vision,
281
+ }
282
+
283
+ if http_client is not None:
284
+ return _probe(http_client)
285
+ with httpx.Client(base_url=base, timeout=settings.timeout) as client:
286
+ return _probe(client)
@@ -0,0 +1,50 @@
1
+ """Reject non-finite floats in parsed structured generation values.
2
+
3
+ Examples:
4
+ ```python
5
+ from typevet.adapters.outbound.generation_finite import (
6
+ reject_non_finite_numbers,
7
+ )
8
+
9
+ reject_non_finite_numbers({"score": 1.0})
10
+ ```
11
+
12
+ See Also:
13
+ - [typevet.domain.errors][]: SchemaValidationError
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import math
19
+ from typing import Any
20
+
21
+ from typevet.domain.errors import SchemaValidationError
22
+
23
+
24
+ def reject_non_finite_numbers(value: Any) -> None:
25
+ """Raise when a non-finite float appears anywhere in a JSON-like tree.
26
+
27
+ Args:
28
+ value: Parsed model output (objects, arrays, numbers, strings, etc.).
29
+
30
+ Raises:
31
+ SchemaValidationError: When a float is NaN or positive/negative infinity.
32
+ """
33
+ _reject_non_finite(value, root=value)
34
+
35
+
36
+ def _reject_non_finite(node: Any, *, root: Any) -> None:
37
+ if isinstance(node, float):
38
+ if not math.isfinite(node):
39
+ raise SchemaValidationError(
40
+ "structured output contains a non-finite number",
41
+ payload=root,
42
+ )
43
+ return
44
+ if isinstance(node, dict):
45
+ for item in node.values():
46
+ _reject_non_finite(item, root=root)
47
+ return
48
+ if isinstance(node, list):
49
+ for item in node:
50
+ _reject_non_finite(item, root=root)
@@ -0,0 +1,38 @@
1
+ """HTTP error constants that every backend's error mapping shares.
2
+
3
+ A status at or above ``HTTP_ERROR_STATUS`` is a backend failure. The error
4
+ keeps at most ``BODY_SNIPPET_MAX`` characters of the response body.
5
+
6
+ Attributes:
7
+ HTTP_ERROR_STATUS (int): Lowest status that the adapters treat as failure.
8
+ BODY_SNIPPET_MAX (int): Maximum characters of body text in an error.
9
+
10
+ Examples:
11
+ ```python
12
+ from typevet.adapters.outbound.http_errors import body_snippet
13
+
14
+ body_snippet("x" * 600) # first 500 characters
15
+ ```
16
+
17
+ See Also:
18
+ - [typevet.adapters.outbound.llama_cpp_http][]: llama.cpp error mapping
19
+ - [typevet.adapters.outbound.vllm_http][]: vLLM error mapping
20
+ - [typevet.domain.errors][]: BackendHttpError
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ HTTP_ERROR_STATUS = 400
26
+ BODY_SNIPPET_MAX = 500
27
+
28
+
29
+ def body_snippet(text: str) -> str:
30
+ """Return a bounded snippet of response text for error metadata.
31
+
32
+ Args:
33
+ text: Full HTTP response body text.
34
+
35
+ Returns:
36
+ At most ``BODY_SNIPPET_MAX`` characters from ``text``.
37
+ """
38
+ return text[:BODY_SNIPPET_MAX]