warmwinter 0.1.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.
- warmwinter-0.1.0/LICENSE +21 -0
- warmwinter-0.1.0/PKG-INFO +49 -0
- warmwinter-0.1.0/README.md +30 -0
- warmwinter-0.1.0/pyproject.toml +28 -0
- warmwinter-0.1.0/setup.cfg +4 -0
- warmwinter-0.1.0/warmwinter.egg-info/PKG-INFO +49 -0
- warmwinter-0.1.0/warmwinter.egg-info/SOURCES.txt +8 -0
- warmwinter-0.1.0/warmwinter.egg-info/dependency_links.txt +1 -0
- warmwinter-0.1.0/warmwinter.egg-info/top_level.txt +1 -0
- warmwinter-0.1.0/warmwinter.py +216 -0
warmwinter-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Warm Winter
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: warmwinter
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for the Warm Winter trust gate — is this AI/agent action trustworthy enough to act on, or escalate?
|
|
5
|
+
Author: Warm Winter
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://warmwinter.io
|
|
8
|
+
Project-URL: Documentation, https://warmwinter.io/gate
|
|
9
|
+
Project-URL: Source, https://github.com/ricovigil1/warmwinter-sdk/tree/main/python
|
|
10
|
+
Keywords: llm,agents,ai,routing,calibration,trust,gateway
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
|
+
Requires-Python: >=3.8
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# warmwinter (Python SDK)
|
|
21
|
+
|
|
22
|
+
The smallest wrapper around an AI/agent call that asks the one question the Warm
|
|
23
|
+
Winter gate exists to answer: **is this cheap answer trustworthy enough to act on
|
|
24
|
+
— or should we escalate, or abstain?** You keep executing; we only judge. Zero
|
|
25
|
+
dependencies (stdlib `urllib`).
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install warmwinter
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from warmwinter import WarmWinter
|
|
33
|
+
|
|
34
|
+
ww = WarmWinter(api_key="ww_...") # mint at /api/v1/gate/keys
|
|
35
|
+
|
|
36
|
+
# one call gates, runs the chosen path, and auto-reports the outcome
|
|
37
|
+
answer = ww.guard(
|
|
38
|
+
domain="compute", decision_type="model_route", stated_confidence=0.82,
|
|
39
|
+
cheap=lambda: small_model(prompt),
|
|
40
|
+
escalate=lambda: big_model(prompt),
|
|
41
|
+
verify=lambda out: out is not None, # your success test
|
|
42
|
+
)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Or drive the two halves yourself with `decide(...)` and `outcome(...)`. See the
|
|
46
|
+
module docstring for the full surface.
|
|
47
|
+
|
|
48
|
+
Seamless alternative: point your existing OpenAI/Anthropic client's `base_url`
|
|
49
|
+
at the gateway and change nothing else — see https://warmwinter.io/gate.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# warmwinter (Python SDK)
|
|
2
|
+
|
|
3
|
+
The smallest wrapper around an AI/agent call that asks the one question the Warm
|
|
4
|
+
Winter gate exists to answer: **is this cheap answer trustworthy enough to act on
|
|
5
|
+
— or should we escalate, or abstain?** You keep executing; we only judge. Zero
|
|
6
|
+
dependencies (stdlib `urllib`).
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pip install warmwinter
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
from warmwinter import WarmWinter
|
|
14
|
+
|
|
15
|
+
ww = WarmWinter(api_key="ww_...") # mint at /api/v1/gate/keys
|
|
16
|
+
|
|
17
|
+
# one call gates, runs the chosen path, and auto-reports the outcome
|
|
18
|
+
answer = ww.guard(
|
|
19
|
+
domain="compute", decision_type="model_route", stated_confidence=0.82,
|
|
20
|
+
cheap=lambda: small_model(prompt),
|
|
21
|
+
escalate=lambda: big_model(prompt),
|
|
22
|
+
verify=lambda out: out is not None, # your success test
|
|
23
|
+
)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Or drive the two halves yourself with `decide(...)` and `outcome(...)`. See the
|
|
27
|
+
module docstring for the full surface.
|
|
28
|
+
|
|
29
|
+
Seamless alternative: point your existing OpenAI/Anthropic client's `base_url`
|
|
30
|
+
at the gateway and change nothing else — see https://warmwinter.io/gate.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "warmwinter"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python SDK for the Warm Winter trust gate — is this AI/agent action trustworthy enough to act on, or escalate?"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Warm Winter" }]
|
|
13
|
+
keywords = ["llm", "agents", "ai", "routing", "calibration", "trust", "gateway"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
19
|
+
]
|
|
20
|
+
dependencies = [] # stdlib only (urllib) — drops into any environment
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://warmwinter.io"
|
|
24
|
+
Documentation = "https://warmwinter.io/gate"
|
|
25
|
+
Source = "https://github.com/ricovigil1/warmwinter-sdk/tree/main/python"
|
|
26
|
+
|
|
27
|
+
[tool.setuptools]
|
|
28
|
+
py-modules = ["warmwinter"]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: warmwinter
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for the Warm Winter trust gate — is this AI/agent action trustworthy enough to act on, or escalate?
|
|
5
|
+
Author: Warm Winter
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://warmwinter.io
|
|
8
|
+
Project-URL: Documentation, https://warmwinter.io/gate
|
|
9
|
+
Project-URL: Source, https://github.com/ricovigil1/warmwinter-sdk/tree/main/python
|
|
10
|
+
Keywords: llm,agents,ai,routing,calibration,trust,gateway
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
|
+
Requires-Python: >=3.8
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# warmwinter (Python SDK)
|
|
21
|
+
|
|
22
|
+
The smallest wrapper around an AI/agent call that asks the one question the Warm
|
|
23
|
+
Winter gate exists to answer: **is this cheap answer trustworthy enough to act on
|
|
24
|
+
— or should we escalate, or abstain?** You keep executing; we only judge. Zero
|
|
25
|
+
dependencies (stdlib `urllib`).
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install warmwinter
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from warmwinter import WarmWinter
|
|
33
|
+
|
|
34
|
+
ww = WarmWinter(api_key="ww_...") # mint at /api/v1/gate/keys
|
|
35
|
+
|
|
36
|
+
# one call gates, runs the chosen path, and auto-reports the outcome
|
|
37
|
+
answer = ww.guard(
|
|
38
|
+
domain="compute", decision_type="model_route", stated_confidence=0.82,
|
|
39
|
+
cheap=lambda: small_model(prompt),
|
|
40
|
+
escalate=lambda: big_model(prompt),
|
|
41
|
+
verify=lambda out: out is not None, # your success test
|
|
42
|
+
)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Or drive the two halves yourself with `decide(...)` and `outcome(...)`. See the
|
|
46
|
+
module docstring for the full surface.
|
|
47
|
+
|
|
48
|
+
Seamless alternative: point your existing OpenAI/Anthropic client's `base_url`
|
|
49
|
+
at the gateway and change nothing else — see https://warmwinter.io/gate.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
warmwinter
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Warm Winter — Python SDK for the live trust gate.
|
|
3
|
+
|
|
4
|
+
The smallest wrapper around an AI/agent call that asks the one question the gate
|
|
5
|
+
exists to answer: *is this cheap answer trustworthy enough to act on — or should
|
|
6
|
+
we escalate, or abstain?* You keep executing; we only judge.
|
|
7
|
+
|
|
8
|
+
Zero dependencies (stdlib `urllib`), so it drops into any environment.
|
|
9
|
+
|
|
10
|
+
Quickstart
|
|
11
|
+
----------
|
|
12
|
+
from warmwinter import WarmWinter
|
|
13
|
+
|
|
14
|
+
ww = WarmWinter(api_key="ww_...") # mint at /api/v1/gate/keys
|
|
15
|
+
|
|
16
|
+
# 1) ask the gate before spending on the expensive path
|
|
17
|
+
d = ww.decide(domain="compute", decision_type="model_route",
|
|
18
|
+
stated_confidence=0.82, stakes="medium")
|
|
19
|
+
|
|
20
|
+
if d.verdict == "act":
|
|
21
|
+
answer = cheap_model(prompt) # trust the cheap path
|
|
22
|
+
else: # "escalate" / "abstain"
|
|
23
|
+
answer = strong_model(prompt) # or a human
|
|
24
|
+
|
|
25
|
+
# 2) when you learn whether it held, close the loop so the cell sharpens
|
|
26
|
+
ww.outcome(d.decision_id, "success" if answer_was_good else "failure")
|
|
27
|
+
|
|
28
|
+
The `guard()` helper does both ends in one call when you can supply both paths
|
|
29
|
+
and a verifier — see its docstring.
|
|
30
|
+
"""
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
import json
|
|
34
|
+
import urllib.error
|
|
35
|
+
import urllib.request
|
|
36
|
+
from dataclasses import dataclass
|
|
37
|
+
from typing import Any, Callable, Optional
|
|
38
|
+
|
|
39
|
+
__version__ = "0.1.0"
|
|
40
|
+
__all__ = ["WarmWinter", "Decision", "WarmWinterError", "DEFAULT_BASE_URL", "__version__"]
|
|
41
|
+
|
|
42
|
+
DEFAULT_BASE_URL = "https://api.warmwinter.io"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class WarmWinterError(RuntimeError):
|
|
46
|
+
def __init__(self, status: int, detail: str):
|
|
47
|
+
super().__init__(f"[{status}] {detail}")
|
|
48
|
+
self.status = status
|
|
49
|
+
self.detail = detail
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass
|
|
53
|
+
class Decision:
|
|
54
|
+
decision_id: str
|
|
55
|
+
verdict: str # "act" | "escalate" | "abstain"
|
|
56
|
+
cell_state: str # "verified" | "provisional" | "ungrounded"
|
|
57
|
+
calibrated_confidence: Optional[float]
|
|
58
|
+
stated_confidence: float
|
|
59
|
+
stakes: str
|
|
60
|
+
reasons: list
|
|
61
|
+
cell: dict
|
|
62
|
+
replayed: bool = False
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def act(self) -> bool:
|
|
66
|
+
"""True when the gate says the cheap/proposed path is trustworthy."""
|
|
67
|
+
return self.verdict == "act"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class WarmWinter:
|
|
71
|
+
def __init__(self, api_key: str, base_url: str = DEFAULT_BASE_URL, timeout: float = 10.0):
|
|
72
|
+
if not api_key:
|
|
73
|
+
raise ValueError("api_key is required (mint one at /api/v1/gate/keys)")
|
|
74
|
+
self.api_key = api_key
|
|
75
|
+
self.base_url = base_url.rstrip("/")
|
|
76
|
+
self.timeout = timeout
|
|
77
|
+
self._policy: Optional[dict] = None # cached decision boundary (local mode)
|
|
78
|
+
self._buffer: list = [] # outcomes pending a flush
|
|
79
|
+
|
|
80
|
+
# ── low-level ──────────────────────────────────────────────────────────────
|
|
81
|
+
def _post(self, path: str, body: dict) -> dict:
|
|
82
|
+
return self._request("POST", path, body)
|
|
83
|
+
|
|
84
|
+
def _request(self, method: str, path: str, body: Optional[dict]) -> dict:
|
|
85
|
+
data = json.dumps(body).encode() if body is not None else None
|
|
86
|
+
req = urllib.request.Request(
|
|
87
|
+
self.base_url + path, data=data, method=method,
|
|
88
|
+
headers={"X-Api-Key": self.api_key, "Content-Type": "application/json"},
|
|
89
|
+
)
|
|
90
|
+
try:
|
|
91
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as resp:
|
|
92
|
+
return json.loads(resp.read().decode() or "{}")
|
|
93
|
+
except urllib.error.HTTPError as e:
|
|
94
|
+
detail = e.read().decode(errors="replace")
|
|
95
|
+
try:
|
|
96
|
+
detail = json.loads(detail).get("detail", detail)
|
|
97
|
+
except Exception:
|
|
98
|
+
pass
|
|
99
|
+
raise WarmWinterError(e.code, detail) from None
|
|
100
|
+
|
|
101
|
+
# ── the gate ───────────────────────────────────────────────────────────────
|
|
102
|
+
def decide(self, *, domain: str, decision_type: str, stated_confidence: float,
|
|
103
|
+
context: Optional[dict] = None, stakes: Optional[str] = None,
|
|
104
|
+
candidate_action: Optional[str] = None,
|
|
105
|
+
idempotency_key: Optional[str] = None,
|
|
106
|
+
on_ungrounded: Optional[str] = None) -> Decision:
|
|
107
|
+
"""Ask whether a proposed action is trustworthy enough to act on."""
|
|
108
|
+
body = {"domain": domain, "decision_type": decision_type,
|
|
109
|
+
"stated_confidence": stated_confidence}
|
|
110
|
+
for k, v in (("context", context), ("stakes", stakes),
|
|
111
|
+
("candidate_action", candidate_action),
|
|
112
|
+
("idempotency_key", idempotency_key),
|
|
113
|
+
("on_ungrounded", on_ungrounded)):
|
|
114
|
+
if v is not None:
|
|
115
|
+
body[k] = v
|
|
116
|
+
r = self._post("/api/v1/gate/decide", body)
|
|
117
|
+
return Decision(
|
|
118
|
+
decision_id=r["decision_id"], verdict=r["verdict"], cell_state=r["cell_state"],
|
|
119
|
+
calibrated_confidence=r.get("calibrated_confidence"),
|
|
120
|
+
stated_confidence=r.get("stated_confidence", stated_confidence),
|
|
121
|
+
stakes=r.get("stakes", stakes or "medium"),
|
|
122
|
+
reasons=r.get("reasons", []), cell=r.get("cell", {}),
|
|
123
|
+
replayed=r.get("replayed", False),
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
def outcome(self, decision_id: str, outcome: str,
|
|
127
|
+
observed: Optional[dict] = None) -> dict:
|
|
128
|
+
"""Close the loop. `outcome` ∈ {success, failure, abstained}."""
|
|
129
|
+
body = {"decision_id": decision_id, "outcome": outcome}
|
|
130
|
+
if observed is not None:
|
|
131
|
+
body["observed"] = observed
|
|
132
|
+
return self._post("/api/v1/gate/outcome", body)
|
|
133
|
+
|
|
134
|
+
def frontier(self) -> dict:
|
|
135
|
+
"""This account's live competence map (+ the cold-start backtest seed)."""
|
|
136
|
+
return self._request("GET", "/api/v1/gate/frontier", None)
|
|
137
|
+
|
|
138
|
+
# ── local-decide mode (Phase 3 — decide at the edge, learn in batches) ───────
|
|
139
|
+
# Pull the policy ONCE, then decide() in-process at ~microsecond latency with
|
|
140
|
+
# no per-call round-trip (serves billions of agents); buffer outcomes and flush
|
|
141
|
+
# them in one batch. The policy ships states only, never calibration (G6).
|
|
142
|
+
def pull_policy(self) -> dict:
|
|
143
|
+
"""Fetch + cache the signed decision boundary. Call once, then periodically."""
|
|
144
|
+
self._policy = self._request("GET", "/api/v1/gate/policy", None)
|
|
145
|
+
return self._policy
|
|
146
|
+
|
|
147
|
+
def decide_local(self, *, domain: str, decision_type: str,
|
|
148
|
+
stakes: str = "medium", on_ungrounded: str = "escalate") -> str:
|
|
149
|
+
"""Reproduce the gate verdict from the cached policy alone — no network.
|
|
150
|
+
Auto-pulls the policy on first use. Returns act | escalate | abstain."""
|
|
151
|
+
if self._policy is None:
|
|
152
|
+
self.pull_policy()
|
|
153
|
+
p = self._policy
|
|
154
|
+
state = p.get("default_state", "ungrounded")
|
|
155
|
+
for c in p.get("cells", []):
|
|
156
|
+
if c["domain"] == domain and c["decision_type"] == decision_type:
|
|
157
|
+
state = c["state"]
|
|
158
|
+
break
|
|
159
|
+
if state == "ungrounded" and on_ungrounded == "abstain":
|
|
160
|
+
return "abstain"
|
|
161
|
+
return p["stakes_rule"][state][stakes]
|
|
162
|
+
|
|
163
|
+
def report_local(self, *, domain: str, decision_type: str,
|
|
164
|
+
stated_confidence: float, outcome: str,
|
|
165
|
+
stakes: str = "medium") -> None:
|
|
166
|
+
"""Buffer a resolved outcome locally (no network). Flush in a batch."""
|
|
167
|
+
self._buffer.append({"domain": domain, "decision_type": decision_type,
|
|
168
|
+
"stated_confidence": stated_confidence,
|
|
169
|
+
"outcome": outcome, "stakes": stakes})
|
|
170
|
+
|
|
171
|
+
def flush(self) -> dict:
|
|
172
|
+
"""Send buffered outcomes in one call. If the server reports a newer policy
|
|
173
|
+
version, refresh the cache automatically (self-healing edge)."""
|
|
174
|
+
if not self._buffer:
|
|
175
|
+
return {"ingested": 0, "stale": False}
|
|
176
|
+
version = (self._policy or {}).get("version")
|
|
177
|
+
body = {"rows": self._buffer, "policy_version": version}
|
|
178
|
+
res = self._post("/api/v1/gate/outcome/bulk", body)
|
|
179
|
+
self._buffer = []
|
|
180
|
+
if res.get("stale"):
|
|
181
|
+
self.pull_policy()
|
|
182
|
+
return res
|
|
183
|
+
|
|
184
|
+
# ── one-call convenience ────────────────────────────────────────────────────
|
|
185
|
+
def guard(self, *, domain: str, decision_type: str, stated_confidence: float,
|
|
186
|
+
cheap: Callable[[], Any], escalate: Callable[[], Any],
|
|
187
|
+
verify: Optional[Callable[[Any], bool]] = None,
|
|
188
|
+
stakes: Optional[str] = None, context: Optional[dict] = None) -> Any:
|
|
189
|
+
"""Gate, run the chosen path, and (if `verify` is given) auto-report the
|
|
190
|
+
outcome — the whole loop in one call.
|
|
191
|
+
|
|
192
|
+
result = ww.guard(
|
|
193
|
+
domain="compute", decision_type="model_route", stated_confidence=0.82,
|
|
194
|
+
cheap=lambda: small_model(prompt),
|
|
195
|
+
escalate=lambda: big_model(prompt),
|
|
196
|
+
verify=lambda out: out is not None, # your success test
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
`act` → run `cheap`; anything else → run `escalate`. When `verify` is
|
|
200
|
+
supplied we report success/failure so the cell learns; otherwise resolve
|
|
201
|
+
it yourself with `outcome(...)`.
|
|
202
|
+
"""
|
|
203
|
+
d = self.decide(domain=domain, decision_type=decision_type,
|
|
204
|
+
stated_confidence=stated_confidence, stakes=stakes,
|
|
205
|
+
context=context)
|
|
206
|
+
took_cheap = d.act
|
|
207
|
+
result = (cheap if took_cheap else escalate)()
|
|
208
|
+
if verify is not None:
|
|
209
|
+
ok = bool(verify(result))
|
|
210
|
+
# Only the cheap path's correctness scores the cell — escalation is the
|
|
211
|
+
# safe fallback, not a prediction we're grading.
|
|
212
|
+
if took_cheap:
|
|
213
|
+
self.outcome(d.decision_id, "success" if ok else "failure")
|
|
214
|
+
else:
|
|
215
|
+
self.outcome(d.decision_id, "abstained")
|
|
216
|
+
return result
|