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,135 @@
1
+ r"""Operator CLI for offline CORD combined receipt semantic acceptance (#184).
2
+
3
+ A receipt with a top-level ``stopped`` string reports the stop and exits 1
4
+ with no floor table. A ``stopped`` value that is not a string or ``null`` is
5
+ a malformed receipt and exits 2 (#216).
6
+
7
+ Examples:
8
+ ```console
9
+ $ uv run python -m typevet.adapters.inbound.cord_semantic_acceptance_cli \\
10
+ tests/fixtures/cord/semantic_acceptance/labeled_synthetic_pass.json
11
+ | check | bound | limit | measured | n | status |
12
+ ...
13
+ accepted: true
14
+ ```
15
+
16
+ See Also:
17
+ - [typevet.evaluation.cord_semantic_acceptance][]: ``accept_combined_receipt``
18
+ - [typevet.evaluation.cord_semantic_acceptance_report][]: table formatting
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import argparse
24
+ import json
25
+ import sys
26
+ from pathlib import Path
27
+ from typing import Any
28
+
29
+ from typevet.evaluation.cord_semantic_acceptance_report import (
30
+ evaluate_combined_receipt,
31
+ format_semantic_acceptance_report,
32
+ )
33
+
34
+ _EXIT_ACCEPT = 0
35
+ _EXIT_REJECT = 1
36
+ _EXIT_USAGE_OR_MALFORMED = 2
37
+
38
+
39
+ def _build_parser() -> argparse.ArgumentParser:
40
+ return argparse.ArgumentParser(
41
+ description=(
42
+ "Evaluate one saved CORD combined-modality receipt against the "
43
+ "frozen #161 revision 1 semantic floors. Exit 0 only when accepted."
44
+ ),
45
+ )
46
+
47
+
48
+ def _load_receipt(path: Path) -> dict[str, Any]:
49
+ if not path.is_file():
50
+ msg = f"receipt path is not a file: {path}"
51
+ raise ValueError(msg)
52
+ try:
53
+ raw = path.read_text(encoding="utf-8")
54
+ except OSError as exc:
55
+ msg = f"cannot read receipt: {path}"
56
+ raise ValueError(msg) from exc
57
+ try:
58
+ parsed = json.loads(raw)
59
+ except json.JSONDecodeError as exc:
60
+ msg = f"receipt is not valid JSON: {path}"
61
+ raise ValueError(msg) from exc
62
+ if not isinstance(parsed, dict):
63
+ msg = "receipt root must be a JSON object"
64
+ raise TypeError(msg)
65
+ return parsed
66
+
67
+
68
+ def _stopped_exit(stopped: object) -> int | None:
69
+ if stopped is None:
70
+ return None
71
+ if not isinstance(stopped, str):
72
+ msg = "error: malformed receipt: stopped must be a string or null"
73
+ print(msg, file=sys.stderr)
74
+ return _EXIT_USAGE_OR_MALFORMED
75
+ print(f"stopped: {stopped.strip() or '<unspecified>'}", file=sys.stderr)
76
+ return _EXIT_REJECT
77
+
78
+
79
+ def main(argv: list[str] | None = None) -> int:
80
+ """Load a receipt path, print the floor table, return a process exit code.
81
+
82
+ Args:
83
+ argv: CLI args; defaults to ``sys.argv[1:]`` (one receipt path).
84
+
85
+ The top-level ``stopped`` value selects one of four cases:
86
+
87
+ - Missing or ``null``: the floor table is evaluated and printed.
88
+ - A string with text: ``stopped: <reason>`` on stderr, exit ``1``, no
89
+ floor table.
90
+ - An empty or whitespace-only string: ``stopped: <unspecified>`` on
91
+ stderr, exit ``1``, no floor table.
92
+ - Any other value (for example ``true``, an object or a number): a
93
+ malformed-receipt error on stderr, exit ``2``, no floor table.
94
+
95
+ Returns:
96
+ ``0`` when ``accepted`` is true, ``1`` when checks fail or the run
97
+ stopped, ``2`` on usage errors, malformed receipts, or
98
+ ``ValueError`` / ``TypeError`` from parsing.
99
+ """
100
+ parser = _build_parser()
101
+ parser.add_argument(
102
+ "receipt",
103
+ type=Path,
104
+ help="Path to a saved combined CORD outcome JSON file",
105
+ )
106
+ try:
107
+ args = parser.parse_args(argv)
108
+ except SystemExit as exc:
109
+ code = exc.code
110
+ if code in (0, None):
111
+ return _EXIT_ACCEPT
112
+ return (
113
+ _EXIT_USAGE_OR_MALFORMED
114
+ if code == _EXIT_USAGE_OR_MALFORMED
115
+ else _EXIT_REJECT
116
+ )
117
+ try:
118
+ receipt = _load_receipt(args.receipt)
119
+ except (ValueError, TypeError) as exc:
120
+ print(f"error: {exc}", file=sys.stderr)
121
+ return _EXIT_USAGE_OR_MALFORMED
122
+ stopped_code = _stopped_exit(receipt.get("stopped"))
123
+ if stopped_code is not None:
124
+ return stopped_code
125
+ try:
126
+ outcome = evaluate_combined_receipt(receipt)
127
+ except (ValueError, TypeError) as exc:
128
+ print(f"error: {exc}", file=sys.stderr)
129
+ return _EXIT_USAGE_OR_MALFORMED
130
+ print(format_semantic_acceptance_report(outcome))
131
+ return _EXIT_ACCEPT if outcome.accepted else _EXIT_REJECT
132
+
133
+
134
+ if __name__ == "__main__":
135
+ raise SystemExit(main())
@@ -0,0 +1,67 @@
1
+ """Sync bridge for async generation callers (scripts and CLIs).
2
+
3
+ Examples:
4
+ ```python
5
+ from typevet import GenerationRequest
6
+ from typevet.adapters.inbound import run_sync
7
+ from typevet.adapters.outbound import AsyncFakeGenerationAdapter
8
+
9
+ schema = {
10
+ "type": "object",
11
+ "properties": {"ok": {"type": "boolean"}},
12
+ "required": ["ok"],
13
+ "additionalProperties": False,
14
+ }
15
+ port = AsyncFakeGenerationAdapter(value={"ok": True})
16
+ result = run_sync(
17
+ port.generate(GenerationRequest(prompt="Say ok.", schema=schema, model="fake"))
18
+ )
19
+ assert result.value["ok"] is True
20
+ ```
21
+
22
+ See Also:
23
+ - [typevet.ports.async_generation][]: AsyncGenerationPort
24
+ - [typevet.adapters.inbound.api][]: Sync ``generate`` helper
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import asyncio
30
+ from collections.abc import Coroutine
31
+ from typing import Any
32
+
33
+
34
+ def run_sync[T](coro: Coroutine[Any, Any, T]) -> T:
35
+ """Run an async coroutine synchronously and return its result.
36
+
37
+ Use this from plain scripts instead of adding ``*_sync`` methods on ports.
38
+ Pass a coroutine object (for example ``port.generate(request)``), not the
39
+ unbound method.
40
+
41
+ Args:
42
+ coro: Coroutine to execute (typically from ``AsyncGenerationPort.generate``).
43
+
44
+ Returns:
45
+ The coroutine result (for example a ``GenerationResult``).
46
+
47
+ Raises:
48
+ TypeError: When ``coro`` is not a coroutine object.
49
+ RuntimeError: When called from a thread that already has a running loop.
50
+ """
51
+ if not asyncio.iscoroutine(coro):
52
+ msg = (
53
+ f"run_sync() requires a coroutine object, got {type(coro).__name__}. "
54
+ "Call the async method first: run_sync(port.generate(request))"
55
+ )
56
+ raise TypeError(msg)
57
+
58
+ try:
59
+ return asyncio.run(coro)
60
+ except RuntimeError as exc:
61
+ if "asyncio.run() cannot be called from a running event loop" in str(exc):
62
+ msg = (
63
+ "run_sync() cannot run while an event loop is already active. "
64
+ "Await port.generate(...) in async code instead."
65
+ )
66
+ raise RuntimeError(msg) from exc
67
+ raise
@@ -0,0 +1,154 @@
1
+ """Environment-backed llama.cpp settings for the composition root.
2
+
3
+ Outbound adapters take explicit constructor arguments. Only a composition root
4
+ (CLI, MCP server, or live test harness) reads ``TYPEVET_LLAMA__*`` variables
5
+ and passes the values into [typevet.adapters.outbound.llama_cpp][].
6
+
7
+ Diagnostic log settings live in [typevet.adapters.diagnostics.settings][].
8
+
9
+ Examples:
10
+ ```python
11
+ from typevet.adapters.inbound.settings import (
12
+ llama_cpp_adapter,
13
+ load_llama_settings,
14
+ )
15
+
16
+ settings = load_llama_settings()
17
+ with llama_cpp_adapter(settings) as port:
18
+ pass # port.generate(...)
19
+ ```
20
+
21
+ See Also:
22
+ - [typevet.adapters.outbound.llama_cpp][]: Sync llama.cpp adapter
23
+ - [typevet.adapters.diagnostics.settings][]: ``TYPEVET_LOG__*`` settings
24
+ - docs/reference/configuration.md: Environment variable reference
25
+ - docs/how-to/run-a-multimodal-live-smoke.md: ``multimodal_model`` consumer
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import os
31
+ from collections.abc import Mapping
32
+ from dataclasses import dataclass
33
+
34
+ from typevet.adapters.outbound.llama_cpp import LlamaCppGenerationAdapter
35
+
36
+ _DEFAULT_BASE_URL = "http://127.0.0.1:8090"
37
+ _DEFAULT_TIMEOUT = 300.0
38
+ _DEFAULT_MULTIMODAL_MODEL = "gemma-3-4b-it-q4km-mm"
39
+
40
+
41
+ @dataclass(frozen=True, slots=True)
42
+ class LlamaSettings:
43
+ """Connection options for a local llama.cpp OpenAI-compat router.
44
+
45
+ Attributes:
46
+ base_url (str): Router root without a trailing slash.
47
+ timeout (float): HTTP request timeout in seconds.
48
+ default_model (str | None): Default model id when a call omits one.
49
+ multimodal_model (str): Model id the opt-in image smoke asks for.
50
+
51
+ Examples:
52
+ ```python
53
+ from typevet.adapters.inbound.settings import LlamaSettings
54
+
55
+ LlamaSettings(base_url="http://127.0.0.1:8090", timeout=300.0)
56
+ ```
57
+ """
58
+
59
+ base_url: str = _DEFAULT_BASE_URL
60
+ timeout: float = _DEFAULT_TIMEOUT
61
+ default_model: str | None = None
62
+ multimodal_model: str = _DEFAULT_MULTIMODAL_MODEL
63
+
64
+
65
+ def _read_base_url(source: Mapping[str, str]) -> str:
66
+ nested = source.get("TYPEVET_LLAMA__BASE_URL", "").strip()
67
+ if nested:
68
+ return nested.rstrip("/")
69
+ legacy = source.get("TYPEVET_LLAMA_URL", "").strip()
70
+ if legacy:
71
+ return legacy.rstrip("/")
72
+ return _DEFAULT_BASE_URL
73
+
74
+
75
+ def _read_default_model(source: Mapping[str, str]) -> str | None:
76
+ nested = source.get("TYPEVET_LLAMA__DEFAULT_MODEL", "").strip()
77
+ if nested:
78
+ return nested
79
+ legacy = source.get("TYPEVET_GEMMA_MODEL", "").strip()
80
+ return legacy or None
81
+
82
+
83
+ def _read_multimodal_model(source: Mapping[str, str]) -> str:
84
+ nested = source.get("TYPEVET_LLAMA__MULTIMODAL_MODEL", "").strip()
85
+ return nested or _DEFAULT_MULTIMODAL_MODEL
86
+
87
+
88
+ def _read_timeout(source: Mapping[str, str]) -> float:
89
+ raw = source.get("TYPEVET_LLAMA__TIMEOUT", "").strip()
90
+ if not raw:
91
+ return _DEFAULT_TIMEOUT
92
+ try:
93
+ timeout = float(raw)
94
+ except ValueError as exc:
95
+ msg = "TYPEVET_LLAMA__TIMEOUT must be a positive number of seconds"
96
+ raise ValueError(msg) from exc
97
+ if timeout <= 0:
98
+ msg = "TYPEVET_LLAMA__TIMEOUT must be a positive number of seconds"
99
+ raise ValueError(msg)
100
+ return timeout
101
+
102
+
103
+ def load_llama_settings(
104
+ environ: Mapping[str, str] | None = None,
105
+ ) -> LlamaSettings:
106
+ """Read ``TYPEVET_LLAMA__*`` variables for the composition root.
107
+
108
+ Legacy single-segment names ``TYPEVET_LLAMA_URL`` and ``TYPEVET_GEMMA_MODEL``
109
+ remain supported when the nested names are unset.
110
+ ``TYPEVET_LLAMA__MULTIMODAL_MODEL`` names the model id for the opt-in image
111
+ smoke and has no legacy alias.
112
+
113
+ Args:
114
+ environ: Mapping to read. Defaults to ``os.environ``.
115
+
116
+ Returns:
117
+ Frozen settings with defaults for missing keys.
118
+
119
+ Raises:
120
+ ValueError: When ``TYPEVET_LLAMA__TIMEOUT`` is not a positive number.
121
+ """
122
+ source = os.environ if environ is None else environ
123
+ return LlamaSettings(
124
+ base_url=_read_base_url(source),
125
+ timeout=_read_timeout(source),
126
+ default_model=_read_default_model(source),
127
+ multimodal_model=_read_multimodal_model(source),
128
+ )
129
+
130
+
131
+ def llama_cpp_adapter(settings: LlamaSettings) -> LlamaCppGenerationAdapter:
132
+ """Build a sync llama.cpp adapter from composition-root settings.
133
+
134
+ Args:
135
+ settings: Values read from the environment or constructed in tests.
136
+
137
+ Returns:
138
+ An adapter that does not read ``os.environ`` itself.
139
+
140
+ Examples:
141
+ ```python
142
+ from typevet.adapters.inbound.settings import (
143
+ llama_cpp_adapter,
144
+ load_llama_settings,
145
+ )
146
+
147
+ with llama_cpp_adapter(load_llama_settings()) as port:
148
+ pass
149
+ ```
150
+ """
151
+ return LlamaCppGenerationAdapter(
152
+ base_url=settings.base_url,
153
+ timeout=settings.timeout,
154
+ )
@@ -0,0 +1,62 @@
1
+ """Outbound adapter package.
2
+
3
+ Examples:
4
+ ```python
5
+ from typevet.adapters.outbound import (
6
+ AsyncFakeGenerationAdapter,
7
+ AsyncLlamaCppGenerationAdapter,
8
+ AsyncVllmGenerationAdapter,
9
+ ChatContentFraming,
10
+ FakeGenerationAdapter,
11
+ LlamaCppCandidateScoringAdapter,
12
+ LlamaCppGenerationAdapter,
13
+ VllmCandidateScoringAdapter,
14
+ VllmGenerationAdapter,
15
+ )
16
+ ```
17
+
18
+ See Also:
19
+ - [typevet.adapters.outbound.llama_cpp][]: Local llama.cpp router adapter
20
+ - [typevet.adapters.outbound.fake][]: Offline validating fake
21
+ - [typevet.adapters.outbound.vllm_scoring][]: vLLM chat logprob scorer
22
+ - [typevet.adapters.outbound.vllm_generation][]: vLLM structured generation
23
+ - [typevet.adapters.outbound.vllm_generation_async][]: Async vLLM generation
24
+ - [typevet.adapters.outbound.vllm_http][]: vLLM HTTP error mapping
25
+ - [typevet.adapters.outbound.chat_completion][]: Shared content and schema checks
26
+ - [typevet.adapters.outbound.http_errors][]: Shared HTTP error limits
27
+
28
+ Attributes:
29
+ AsyncFakeGenerationAdapter (type): Offline async validating fake.
30
+ AsyncLlamaCppGenerationAdapter (type): Async OpenAI-compat llama.cpp adapter.
31
+ AsyncVllmGenerationAdapter (type): Async vLLM generation with a POST limit.
32
+ ChatContentFraming (type): Plain chat content framing for vLLM scoring.
33
+ FakeGenerationAdapter (type): Offline adapter that validates a fixed value.
34
+ LlamaCppCandidateScoringAdapter (type): Pre-sampling ``/completion`` scorer.
35
+ LlamaCppGenerationAdapter (type): OpenAI-compat llama.cpp adapter.
36
+ VllmCandidateScoringAdapter (type): vLLM chat completions logprob scorer.
37
+ VllmGenerationAdapter (type): vLLM structured-output generation adapter.
38
+ """
39
+
40
+ from typevet.adapters.outbound.async_fake import AsyncFakeGenerationAdapter
41
+ from typevet.adapters.outbound.async_llama_cpp import AsyncLlamaCppGenerationAdapter
42
+ from typevet.adapters.outbound.fake import FakeGenerationAdapter
43
+ from typevet.adapters.outbound.llama_cpp import LlamaCppGenerationAdapter
44
+ from typevet.adapters.outbound.llama_cpp_scoring import LlamaCppCandidateScoringAdapter
45
+ from typevet.adapters.outbound.vllm_generation import VllmGenerationAdapter
46
+ from typevet.adapters.outbound.vllm_generation_async import AsyncVllmGenerationAdapter
47
+ from typevet.adapters.outbound.vllm_scoring import (
48
+ ChatContentFraming,
49
+ VllmCandidateScoringAdapter,
50
+ )
51
+
52
+ __all__ = [
53
+ "AsyncFakeGenerationAdapter",
54
+ "AsyncLlamaCppGenerationAdapter",
55
+ "AsyncVllmGenerationAdapter",
56
+ "ChatContentFraming",
57
+ "FakeGenerationAdapter",
58
+ "LlamaCppCandidateScoringAdapter",
59
+ "LlamaCppGenerationAdapter",
60
+ "VllmCandidateScoringAdapter",
61
+ "VllmGenerationAdapter",
62
+ ]
@@ -0,0 +1,123 @@
1
+ """Offline async fake generation adapter for unit and contract tests.
2
+
3
+ Examples:
4
+ ```python
5
+ from typevet.adapters.outbound.async_fake import AsyncFakeGenerationAdapter
6
+ from typevet.domain.models import GenerationRequest
7
+
8
+ import asyncio
9
+
10
+ asyncio.run(
11
+ AsyncFakeGenerationAdapter(value={"answer": 1}).generate(
12
+ GenerationRequest(
13
+ prompt="n",
14
+ schema={
15
+ "type": "object",
16
+ "properties": {"answer": {"type": "integer"}},
17
+ "required": ["answer"],
18
+ "additionalProperties": False,
19
+ },
20
+ model="fake",
21
+ )
22
+ )
23
+ )
24
+ ```
25
+
26
+ See Also:
27
+ - [typevet.adapters.outbound.generation_finite][]: Non-finite float guard
28
+ - [typevet.adapters.outbound.fake][]: Sync fake
29
+ - [typevet.adapters.outbound.async_llama_cpp][]: Live llama.cpp adapter
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ from collections.abc import Callable, Mapping
35
+ from typing import Any
36
+
37
+ import jsonschema
38
+
39
+ from typevet.adapters.outbound.generation_finite import reject_non_finite_numbers
40
+ from typevet.domain.errors import GenerationError, SchemaValidationError
41
+ from typevet.domain.models import GenerationRequest, GenerationResult
42
+
43
+ AsyncResponder = Callable[[GenerationRequest], Mapping[str, Any]]
44
+
45
+
46
+ class AsyncFakeGenerationAdapter:
47
+ """Return a fixed or callable mapping and validate it against the schema.
48
+
49
+ The fake itself does not read ``request.media``; a responder
50
+ receives the full request.
51
+
52
+ Attributes:
53
+ _value (Mapping[str, Any] | None): Fixed mapping when set.
54
+ _responder (AsyncResponder | None): Callable producer when set.
55
+ _fail (Exception | None): Exception raised instead of a value.
56
+
57
+ Examples:
58
+ ```python
59
+ from typevet.adapters.outbound.async_fake import AsyncFakeGenerationAdapter
60
+
61
+ AsyncFakeGenerationAdapter(value={"answer": 1})
62
+ ```
63
+ """
64
+
65
+ def __init__(
66
+ self,
67
+ value: Mapping[str, Any] | None = None,
68
+ *,
69
+ responder: AsyncResponder | None = None,
70
+ fail: Exception | None = None,
71
+ ) -> None:
72
+ """Configure the fake.
73
+
74
+ Args:
75
+ value: Fixed mapping returned for every call (validated).
76
+ responder: Optional callable that builds the mapping from the request.
77
+ fail: When set, ``generate`` raises this exception instead.
78
+
79
+ Raises:
80
+ ValueError: When value, responder and fail are all omitted.
81
+ """
82
+ if value is None and responder is None and fail is None:
83
+ msg = "provide value, responder, or fail"
84
+ raise ValueError(msg)
85
+ self._value = value
86
+ self._responder = responder
87
+ self._fail = fail
88
+
89
+ async def generate(self, request: GenerationRequest) -> GenerationResult:
90
+ """Return a validated mapping or raise the configured failure.
91
+
92
+ Args:
93
+ request: Typed generation request.
94
+
95
+ Returns:
96
+ GenerationResult with the fake value.
97
+
98
+ Raises:
99
+ Exception: The configured ``fail`` value when set.
100
+ GenerationError: When the fake has no value source.
101
+ SchemaValidationError: When the fake value is non-finite or fails schema.
102
+ """
103
+ if self._fail is not None:
104
+ raise self._fail
105
+ if self._responder is not None:
106
+ raw: Mapping[str, Any] = self._responder(request)
107
+ elif self._value is not None:
108
+ raw = self._value
109
+ else:
110
+ msg = "fake has no value"
111
+ raise GenerationError(msg)
112
+
113
+ schema_obj = dict(request.schema)
114
+ instance = dict(raw)
115
+ reject_non_finite_numbers(instance)
116
+ try:
117
+ jsonschema.validate(instance=instance, schema=schema_obj)
118
+ except jsonschema.ValidationError as exc:
119
+ raise SchemaValidationError(
120
+ f"fake output failed schema: {exc.message}",
121
+ payload=raw,
122
+ ) from exc
123
+ return GenerationResult(value=instance, model=request.model, raw_text=None)