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.
- typevet/__init__.py +67 -0
- typevet/_version.py +43 -0
- typevet/adapters/__init__.py +16 -0
- typevet/adapters/diagnostics/__init__.py +59 -0
- typevet/adapters/diagnostics/fields.py +108 -0
- typevet/adapters/diagnostics/generation_events.py +75 -0
- typevet/adapters/diagnostics/http_events.py +91 -0
- typevet/adapters/diagnostics/logs.py +116 -0
- typevet/adapters/diagnostics/redaction.py +94 -0
- typevet/adapters/diagnostics/settings.py +77 -0
- typevet/adapters/inbound/__init__.py +65 -0
- typevet/adapters/inbound/api.py +61 -0
- typevet/adapters/inbound/backend_settings.py +608 -0
- typevet/adapters/inbound/cord_semantic_acceptance_cli.py +135 -0
- typevet/adapters/inbound/helpers.py +67 -0
- typevet/adapters/inbound/settings.py +154 -0
- typevet/adapters/outbound/__init__.py +62 -0
- typevet/adapters/outbound/async_fake.py +123 -0
- typevet/adapters/outbound/async_llama_cpp.py +168 -0
- typevet/adapters/outbound/chat_completion.py +148 -0
- typevet/adapters/outbound/fake.py +119 -0
- typevet/adapters/outbound/gemma/__init__.py +84 -0
- typevet/adapters/outbound/gemma/answer_binding.py +290 -0
- typevet/adapters/outbound/gemma/scoring_prefix.py +102 -0
- typevet/adapters/outbound/gemma/served_template.py +131 -0
- typevet/adapters/outbound/gemma_native_vision_factory.py +286 -0
- typevet/adapters/outbound/generation_finite.py +50 -0
- typevet/adapters/outbound/http_errors.py +38 -0
- typevet/adapters/outbound/judgment_scoring.py +471 -0
- typevet/adapters/outbound/llama_cpp.py +187 -0
- typevet/adapters/outbound/llama_cpp_http.py +103 -0
- typevet/adapters/outbound/llama_cpp_multimodal.py +145 -0
- typevet/adapters/outbound/llama_cpp_scoring.py +333 -0
- typevet/adapters/outbound/vllm_content.py +63 -0
- typevet/adapters/outbound/vllm_generation.py +163 -0
- typevet/adapters/outbound/vllm_generation_async.py +257 -0
- typevet/adapters/outbound/vllm_http.py +121 -0
- typevet/adapters/outbound/vllm_judgment_factory.py +210 -0
- typevet/adapters/outbound/vllm_scoring.py +372 -0
- typevet/domain/__init__.py +193 -0
- typevet/domain/candidate_scoring_request.py +134 -0
- typevet/domain/candidate_scoring_response.py +96 -0
- typevet/domain/candidate_scoring_validate.py +197 -0
- typevet/domain/decision_compile.py +368 -0
- typevet/domain/decision_execute.py +229 -0
- typevet/domain/decisions.py +129 -0
- typevet/domain/errors.py +235 -0
- typevet/domain/field_instructions.py +134 -0
- typevet/domain/judgment_answers.py +198 -0
- typevet/domain/judgment_normalize.py +243 -0
- typevet/domain/judgment_questions.py +187 -0
- typevet/domain/judgment_response.py +102 -0
- typevet/domain/media.py +101 -0
- typevet/domain/models.py +115 -0
- typevet/domain/question_schema.py +254 -0
- typevet/domain/scoring_stage.py +34 -0
- typevet/evaluation/__init__.py +19 -0
- typevet/evaluation/consumer_http_accounting.py +135 -0
- typevet/evaluation/cord_expense_call_accounting.py +108 -0
- typevet/evaluation/cord_expense_live_harness.py +75 -0
- typevet/evaluation/cord_expense_receipt_requirement.py +212 -0
- typevet/evaluation/cord_expense_smoke.py +328 -0
- typevet/evaluation/cord_semantic_acceptance.py +463 -0
- typevet/evaluation/cord_semantic_acceptance_report.py +90 -0
- typevet/evaluation/cord_semantic_metrics.py +57 -0
- typevet/evaluation/datasets/__init__.py +30 -0
- typevet/evaluation/datasets/banking77.py +270 -0
- typevet/evaluation/datasets/boolq.py +550 -0
- typevet/evaluation/datasets/boolq_download.py +132 -0
- typevet/evaluation/datasets/civil_comments.py +450 -0
- typevet/evaluation/datasets/clinc.py +125 -0
- typevet/evaluation/datasets/clinc_domains.json +172 -0
- typevet/evaluation/datasets/clinc_download.py +103 -0
- typevet/evaluation/datasets/clinc_plus_intent_names.json +153 -0
- typevet/evaluation/datasets/clinc_rows.py +306 -0
- typevet/evaluation/datasets/clinc_shard.py +156 -0
- typevet/evaluation/datasets/cord_expense.py +381 -0
- typevet/evaluation/datasets/difraud.py +256 -0
- typevet/evaluation/datasets/go_emotions.py +368 -0
- typevet/evaluation/datasets/go_emotions_download.py +154 -0
- typevet/evaluation/datasets/hyperpartisan.py +562 -0
- typevet/evaluation/datasets/partner_guard.py +170 -0
- typevet/evaluation/datasets/psai.py +415 -0
- typevet/evaluation/datasets/psai_download.py +167 -0
- typevet/evaluation/datasets/psai_evidence_pilot.py +570 -0
- typevet/evaluation/datasets/psai_schema.py +157 -0
- typevet/evaluation/datasets/psai_stream.py +166 -0
- typevet/evaluation/datasets/psai_vision.py +534 -0
- typevet/evaluation/datasets/psai_vision_controls.py +502 -0
- typevet/evaluation/datasets/pubmedqa.py +420 -0
- typevet/evaluation/experiment_identity.py +677 -0
- typevet/evaluation/psai_vision_consumer_accounting.py +190 -0
- typevet/evaluation/psai_vision_consumer_dispatch.py +216 -0
- typevet/evaluation/psai_vision_consumer_harness.py +342 -0
- typevet/evaluation/psai_vision_consumer_live.py +332 -0
- typevet/evaluation/psai_vision_consumer_live_identity.py +156 -0
- typevet/evaluation/psai_vision_consumer_live_receipt.py +132 -0
- typevet/evaluation/psai_vision_consumer_live_router.py +162 -0
- typevet/evaluation/psai_vision_consumer_offline.py +418 -0
- typevet/evaluation/psai_vision_consumer_outcomes.py +224 -0
- typevet/evaluation/psai_vision_consumer_protocol.py +69 -0
- typevet/evaluation/psai_vision_consumer_receipt.py +161 -0
- typevet/evaluation/psai_vision_consumer_receipt_structure.py +263 -0
- typevet/evaluation/psai_vision_probability_evidence.py +336 -0
- typevet/evaluation/runner/__init__.py +56 -0
- typevet/evaluation/runner/core.py +104 -0
- typevet/evaluation/runner/datasets.py +186 -0
- typevet/evaluation/runner/live_gate.py +149 -0
- typevet/evaluation/runner/report.py +116 -0
- typevet/evaluation/tpjep/__init__.py +81 -0
- typevet/evaluation/tpjep/loader.py +237 -0
- typevet/evaluation/tpjep/outcome.py +101 -0
- typevet/evaluation/tpjep/records.py +428 -0
- typevet/evaluation/tpjep/runner.py +325 -0
- typevet/ports/__init__.py +39 -0
- typevet/ports/async_generation.py +57 -0
- typevet/ports/framing.py +59 -0
- typevet/ports/generation.py +58 -0
- typevet/ports/judgment.py +71 -0
- typevet/ports/scoring.py +64 -0
- typevet/py.typed +0 -0
- typevet/runtime/__init__.py +50 -0
- typevet/runtime/categorical.py +196 -0
- typevet/runtime/judgment.py +17 -0
- typevet/runtime/llama_cpp_gemma_vision.py +28 -0
- typevet/runtime/scoring_prefix.py +14 -0
- typevet/runtime/vllm_judgment.py +28 -0
- typevet/testing/__init__.py +22 -0
- typevet/testing/fakes.py +152 -0
- typevet-0.1.0.dev1.dist-info/METADATA +125 -0
- typevet-0.1.0.dev1.dist-info/RECORD +133 -0
- typevet-0.1.0.dev1.dist-info/WHEEL +4 -0
- 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)
|