tensorcode 0.1.0a1__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 (53) hide show
  1. tensorcode/__init__.py +84 -0
  2. tensorcode/actions.py +137 -0
  3. tensorcode/answer_type.py +222 -0
  4. tensorcode/awareness.py +344 -0
  5. tensorcode/backends/__init__.py +0 -0
  6. tensorcode/backends/builtin.py +167 -0
  7. tensorcode/backends/hf_local.py +89 -0
  8. tensorcode/backends/linear.py +133 -0
  9. tensorcode/backends/neural.py +361 -0
  10. tensorcode/causal.py +262 -0
  11. tensorcode/change.py +566 -0
  12. tensorcode/chunking.py +195 -0
  13. tensorcode/cognition.py +311 -0
  14. tensorcode/context.py +97 -0
  15. tensorcode/control.py +291 -0
  16. tensorcode/cues.py +192 -0
  17. tensorcode/expectation.py +270 -0
  18. tensorcode/frames.py +232 -0
  19. tensorcode/language/__init__.py +36 -0
  20. tensorcode/language/chart.py +558 -0
  21. tensorcode/language/discourse.py +132 -0
  22. tensorcode/language/domains/__init__.py +0 -0
  23. tensorcode/language/domains/desktop.py +552 -0
  24. tensorcode/language/english.py +459 -0
  25. tensorcode/language/features.py +112 -0
  26. tensorcode/language/generate.py +574 -0
  27. tensorcode/language/grammar.py +893 -0
  28. tensorcode/language/semantics.py +349 -0
  29. tensorcode/learning/__init__.py +30 -0
  30. tensorcode/learning/certificate.py +148 -0
  31. tensorcode/learning/induce.py +304 -0
  32. tensorcode/learning/library.py +217 -0
  33. tensorcode/learning/literals.py +126 -0
  34. tensorcode/learning/verify.py +253 -0
  35. tensorcode/memory.py +303 -0
  36. tensorcode/metacognition.py +351 -0
  37. tensorcode/ops.py +207 -0
  38. tensorcode/outcomes.py +99 -0
  39. tensorcode/permanence.py +376 -0
  40. tensorcode/priming.py +191 -0
  41. tensorcode/py.typed +0 -0
  42. tensorcode/quantity.py +311 -0
  43. tensorcode/records.py +728 -0
  44. tensorcode/relation.py +771 -0
  45. tensorcode/runtime.py +471 -0
  46. tensorcode/semantics_bridge.py +308 -0
  47. tensorcode/social.py +380 -0
  48. tensorcode/temporal.py +189 -0
  49. tensorcode/wants.py +185 -0
  50. tensorcode-0.1.0a1.dist-info/METADATA +196 -0
  51. tensorcode-0.1.0a1.dist-info/RECORD +53 -0
  52. tensorcode-0.1.0a1.dist-info/WHEEL +4 -0
  53. tensorcode-0.1.0a1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,351 @@
1
+ """Knowing what one knows: competence, decomposed confidence, repair, and agency.
2
+
3
+ Four faculties that a system needs about *itself*, each built because a measurement said
4
+ the thing it replaces does not work:
5
+
6
+ * :class:`SelfModel` — competence per *kind* of thing attempted, learned from outcomes and
7
+ consulted before trying. This is not the per-item capability router that
8
+ ``docs/revival/13`` measured at AUC 0.55 (SQuAD) and 0.46 (HotpotQA, below chance): that
9
+ asked "will I get *this item* right" from features of the item. A competence prior asks
10
+ the cheaper question "how do I do on *this kind of thing*", which needs no per-item
11
+ signal at all — only that the kind is knowable before attempting, and that accuracy
12
+ actually varies by kind. Where it does not vary, the prior is worthless, and
13
+ :func:`SelfModel.competence` says so rather than inventing a number.
14
+
15
+ * :class:`Confidences` — one scalar cannot say "I am sure what you asked for but unsure
16
+ which file you meant" (``docs/revival/15`` §15.8). Confidence is carried per *commitment*,
17
+ so a gate can act on the part that is weak: an uncertain slot becomes a question about
18
+ that slot instead of a refusal of the whole request.
19
+
20
+ * :class:`Monitor` — noticing "that did not work", naming the failure, and choosing a
21
+ repair from a repertoire. The pathology it exists to stop is measured: a teacher loop
22
+ that re-issued an identical failing command three times, and an assistant that stopped
23
+ dead rather than trying differently.
24
+
25
+ * :func:`attribute` — every observed change is mine (predicted from my own action) or the
26
+ world's (unpredicted). Without this distinction surprise is meaningless, because every
27
+ consequence of one's own action looks like news.
28
+
29
+ Nothing here introspects on a model's own posterior: ``docs/revival/15`` measured that at
30
+ +0.9 points of selective accuracy for 2.8% refusals, with 446 of 464 items in the top
31
+ confidence bin. The signals here are external — outcome history, tier disagreement,
32
+ observed failure, and efference copy.
33
+ """
34
+
35
+ from __future__ import annotations
36
+
37
+ from dataclasses import dataclass, field
38
+ from math import sqrt
39
+ from typing import Any, Iterable, Literal, Mapping, Sequence
40
+
41
+ from .outcomes import Score, Unknown, Verdict
42
+
43
+ MIN_TRIALS = 8 # below this a rate is not a competence estimate; back off to a coarser kind
44
+
45
+
46
+ # --------------------------------------------------------------- competence
47
+
48
+
49
+ def _wilson_lower(correct: int, attempts: int, z: float = 1.96) -> float:
50
+ """The conservative end of a Wilson interval: what I can claim, not what I hope."""
51
+ if attempts == 0:
52
+ return 0.0
53
+ p = correct / attempts
54
+ d = 1 + z * z / attempts
55
+ centre = p + z * z / (2 * attempts)
56
+ spread = z * sqrt(p * (1 - p) / attempts + z * z / (4 * attempts * attempts))
57
+ return max(0.0, (centre - spread) / d)
58
+
59
+
60
+ @dataclass(frozen=True)
61
+ class Competence:
62
+ """How I have done at one kind of thing, and how sure that estimate is."""
63
+
64
+ kind: str
65
+ attempts: int
66
+ correct: int
67
+ backed_off_from: str | None = None # the finer kind that had too little history
68
+
69
+ @property
70
+ def rate(self) -> float:
71
+ return self.correct / self.attempts if self.attempts else 0.0
72
+
73
+ @property
74
+ def lower(self) -> float:
75
+ return _wilson_lower(self.correct, self.attempts)
76
+
77
+ def score(self) -> Score:
78
+ """The conservative estimate, as a probability whose basis names the history."""
79
+ basis = f"self-model:{self.kind}@{self.attempts}"
80
+ if self.backed_off_from:
81
+ basis += f" (backed off from {self.backed_off_from})"
82
+ return Score(self.lower, "probability", basis=basis)
83
+
84
+ def describe(self) -> str:
85
+ via = f", via {self.backed_off_from}" if self.backed_off_from else ""
86
+ return f"{self.kind}: {self.correct}/{self.attempts} = {self.rate:.3f} (lower {self.lower:.3f}{via})"
87
+
88
+
89
+ @dataclass
90
+ class SelfModel:
91
+ """Outcome history per kind, with backoff from a specific kind to a coarser family.
92
+
93
+ ``kinds`` are caller-chosen strings ordered specific-to-general, e.g.
94
+ ``("squad2/how_many", "squad2", "extractive_qa")``. Backoff is what makes the model
95
+ usable on a kind it has never seen: a never-attempted question type still inherits the
96
+ dataset's rate, and an unknown dataset inherits the family's.
97
+ """
98
+
99
+ name: str = "self-model"
100
+ attempts: dict[str, int] = field(default_factory=dict)
101
+ correct: dict[str, int] = field(default_factory=dict)
102
+
103
+ def record(self, kinds: Sequence[str], ok: bool) -> None:
104
+ """Credit an outcome to every level of the hierarchy at once."""
105
+ for kind in kinds:
106
+ self.attempts[kind] = self.attempts.get(kind, 0) + 1
107
+ self.correct[kind] = self.correct.get(kind, 0) + (1 if ok else 0)
108
+
109
+ def competence(self, kinds: Sequence[str], *, min_trials: int = MIN_TRIALS) -> Competence | Unknown:
110
+ """The most specific kind with enough history to speak for, or ``Unknown``."""
111
+ finest = kinds[0] if kinds else "?"
112
+ for i, kind in enumerate(kinds):
113
+ n = self.attempts.get(kind, 0)
114
+ if n >= min_trials:
115
+ return Competence(kind, n, self.correct.get(kind, 0), backed_off_from=finest if i else None)
116
+ return Unknown("no_competence_history", f"fewer than {min_trials} attempts at any of {list(kinds)}")
117
+
118
+ def worth_attempting(self, kinds: Sequence[str], *, floor: float, **kw: Any) -> Verdict:
119
+ """Should I try? ``unknown`` when I have no history — which is not the same as no.
120
+
121
+ A caller that treats ``unknown`` as a refusal will never attempt anything new; one
122
+ that treats it as permission is merely uninformed. The distinction is the point.
123
+ """
124
+ got = self.competence(kinds, **kw)
125
+ if isinstance(got, Unknown):
126
+ return Verdict("unknown", (got.detail,))
127
+ if got.lower >= floor:
128
+ return Verdict("holds", (f"{got.describe()} at or above floor {floor:.2f}",))
129
+ return Verdict("fails", (f"{got.describe()} below floor {floor:.2f}",))
130
+
131
+ def table(self, *, min_trials: int = MIN_TRIALS) -> list[Competence]:
132
+ rows = [Competence(k, n, self.correct.get(k, 0)) for k, n in self.attempts.items() if n >= min_trials]
133
+ return sorted(rows, key=lambda c: c.lower)
134
+
135
+ def spread(self, kinds: Iterable[str], *, min_trials: int = MIN_TRIALS) -> float:
136
+ """How much competence varies across these kinds — how much a prior could buy.
137
+
138
+ Near zero means every kind is alike and a competence prior is worthless however
139
+ well estimated. This is the number to look at *before* building a gate.
140
+ """
141
+ rates = [self.correct.get(k, 0) / self.attempts[k] for k in kinds if self.attempts.get(k, 0) >= min_trials]
142
+ return max(rates) - min(rates) if len(rates) > 1 else 0.0
143
+
144
+
145
+ # ------------------------------------------------------ decomposed confidence
146
+
147
+ Commitment = Literal["speech_act", "act", "slot", "value", "evidence"]
148
+ #: what a gate should do about the weakest commitment of each kind
149
+ REPAIRABLE: dict[str, str] = {"slot": "ask", "value": "refuse", "act": "ask", "speech_act": "refuse", "evidence": "refuse"}
150
+
151
+
152
+ @dataclass(frozen=True)
153
+ class Belief:
154
+ """One thing the system has committed to, with its own confidence."""
155
+
156
+ commitment: Commitment
157
+ about: str # which slot / which act / which value
158
+ value: Any
159
+ score: Score | None = None # None means "no confidence reported", not zero
160
+
161
+ @property
162
+ def strength(self) -> float:
163
+ return self.score.value if self.score else 0.0
164
+
165
+ def describe(self) -> str:
166
+ s = f"{self.score.value:.2f}" if self.score else "unreported"
167
+ return f"{self.commitment}:{self.about}={self.value!r} ({s})"
168
+
169
+
170
+ @dataclass(frozen=True)
171
+ class Gate:
172
+ """What to do, and about what."""
173
+
174
+ decision: Literal["act", "ask", "refuse"]
175
+ about: str = ""
176
+ why: str = ""
177
+
178
+ def describe(self) -> str:
179
+ target = f" about {self.about}" if self.about else ""
180
+ return f"{self.decision}{target}: {self.why}"
181
+
182
+
183
+ @dataclass(frozen=True)
184
+ class Confidences:
185
+ """Confidence per commitment, and a gate that acts on the weakest one."""
186
+
187
+ parts: tuple[Belief, ...]
188
+
189
+ def weakest(self) -> Belief | None:
190
+ return min(self.parts, key=lambda b: b.strength) if self.parts else None
191
+
192
+ def of(self, commitment: str) -> tuple[Belief, ...]:
193
+ return tuple(b for b in self.parts if b.commitment == commitment)
194
+
195
+ def gate(self, *, act_at: float, ask_below: float) -> Gate:
196
+ """Act when every commitment is strong; otherwise repair the weakest.
197
+
198
+ The difference from a single scalar: a weak *slot* asks a question about that slot,
199
+ while a weak *value* or unreadable *speech act* refuses. A flat gate cannot tell
200
+ those apart and refuses all three.
201
+ """
202
+ weak = self.weakest()
203
+ if weak is None:
204
+ return Gate("refuse", why="nothing was committed to")
205
+ if weak.strength >= act_at:
206
+ return Gate("act", why=f"weakest commitment {weak.describe()} at or above {act_at:.2f}")
207
+ how = REPAIRABLE.get(weak.commitment, "refuse")
208
+ if how == "ask" and weak.strength < ask_below:
209
+ return Gate("refuse", weak.about, f"{weak.describe()} too weak even to ask about")
210
+ if how == "ask":
211
+ return Gate("ask", weak.about, f"{weak.describe()} is the weak part; the rest is fine")
212
+ return Gate("refuse", weak.about, f"{weak.describe()} below {act_at:.2f} and not a question I can ask")
213
+
214
+
215
+ def agreement(readings: Sequence[Any], *, basis: str) -> Score:
216
+ """Confidence from independent readers agreeing, not from one reader's posterior.
217
+
218
+ ``docs/revival/15`` measured a trained model's own posterior as nearly useless for
219
+ abstention (+0.9 points for 2.8% refusals). Disagreement between differently-built
220
+ readers is an external signal about the same commitment.
221
+ """
222
+ kept = [r for r in readings if r is not None]
223
+ if not kept:
224
+ return Score(0.0, "vote_share", basis=f"{basis}: nothing read")
225
+ top = max(kept.count(r) for r in kept)
226
+ return Score(top / len(readings), "vote_share", basis=f"{basis}: {top}/{len(readings)} readers agree")
227
+
228
+
229
+ # -------------------------------------------------------- error and repair
230
+
231
+ FailureKind = Literal[
232
+ "no_effect", # the act went through and nothing changed
233
+ "error_output", # the environment said no
234
+ "target_missing", # what I aimed at is not there
235
+ "stale_reference", # it was there and has moved or changed
236
+ "timeout", # it never finished
237
+ "wrong_result", # it finished and the result is not what was expected
238
+ "crashed", # my own machinery broke
239
+ ]
240
+ RepairKind = Literal["retry", "retry_differently", "reperceive", "ask", "give_up"]
241
+
242
+ #: first repair to consider per failure, before history is taken into account
243
+ FIRST_REPAIR: dict[str, RepairKind] = {
244
+ "no_effect": "retry_differently",
245
+ "error_output": "retry_differently",
246
+ "target_missing": "ask",
247
+ "stale_reference": "reperceive",
248
+ "timeout": "retry",
249
+ "wrong_result": "retry_differently",
250
+ "crashed": "give_up",
251
+ }
252
+
253
+
254
+ @dataclass(frozen=True)
255
+ class Attempt:
256
+ action: str
257
+ failure: FailureKind | None = None
258
+ detail: str = ""
259
+
260
+ @property
261
+ def failed(self) -> bool:
262
+ return self.failure is not None
263
+
264
+
265
+ @dataclass(frozen=True)
266
+ class Repair:
267
+ kind: RepairKind
268
+ why: str
269
+ avoid: tuple[str, ...] = () # actions already known not to work
270
+
271
+ def describe(self) -> str:
272
+ skip = f" (not {', '.join(self.avoid)})" if self.avoid else ""
273
+ return f"{self.kind}: {self.why}{skip}"
274
+
275
+
276
+ @dataclass
277
+ class Monitor:
278
+ """A record of what has already failed, and a repertoire of what to do next.
279
+
280
+ The one rule it will not break: never propose repeating an action that has already
281
+ failed the same way. That is the measured pathology — an identical failing command
282
+ re-issued three times — and it is a property of the *repertoire*, not of any model.
283
+ """
284
+
285
+ history: list[Attempt] = field(default_factory=list)
286
+ patience: int = 2 # identical-failure retries allowed before escalating the repair
287
+
288
+ def note(self, action: str, failure: FailureKind | None = None, detail: str = "") -> Attempt:
289
+ attempt = Attempt(action, failure, detail)
290
+ self.history.append(attempt)
291
+ return attempt
292
+
293
+ def failed_before(self, action: str, failure: FailureKind | None = None) -> int:
294
+ return sum(1 for a in self.history if a.action == action and a.failed and (failure is None or a.failure == failure))
295
+
296
+ def dead_ends(self) -> tuple[str, ...]:
297
+ return tuple(dict.fromkeys(a.action for a in self.history if a.failed))
298
+
299
+ def repair(self, action: str, failure: FailureKind, *, detail: str = "") -> Repair:
300
+ """Choose a repair, escalating as the same failure recurs."""
301
+ seen = self.failed_before(action, failure)
302
+ first = FIRST_REPAIR.get(failure, "retry_differently")
303
+ avoid = self.dead_ends()
304
+ if seen > self.patience:
305
+ return Repair("give_up", f"{action!r} failed {seen}x with {failure}; no repair left to try", avoid)
306
+ if first == "retry" and seen >= 1:
307
+ return Repair("retry_differently", f"{action!r} already failed {seen}x with {failure}; a plain retry is the same act", avoid)
308
+ if first == "retry_differently" and seen >= self.patience:
309
+ return Repair("ask", f"{action!r} failed {seen}x with {failure}; I cannot find a different way alone", avoid)
310
+ why = f"{failure}" + (f": {detail}" if detail else "")
311
+ return Repair(first, why, avoid)
312
+
313
+
314
+ # ---------------------------------------------------------------- agency
315
+
316
+
317
+ Attribution = Literal["self", "world", "both", "unexplained"]
318
+
319
+
320
+ def attribute(observed: Mapping[str, Any], *, before: Mapping[str, Any], predicted: Mapping[str, Any] | None = None,
321
+ acted: bool = True) -> dict[str, Attribution]:
322
+ """Which changes were mine, and which the world's.
323
+
324
+ ``predicted`` is the efference copy: what my own action said would change. A changed
325
+ aspect that my action predicted is mine; one it did not predict is the world's; an
326
+ aspect that changed while I did nothing is the world's by construction. Predicting a
327
+ change that did not happen is *not* a change, and is the business of
328
+ :func:`tensorcode.expectation.check`, not of this function.
329
+ """
330
+ predicted = predicted or {}
331
+ out: dict[str, Attribution] = {}
332
+ for aspect, now in observed.items():
333
+ if aspect in before and before[aspect] == now:
334
+ continue # nothing changed; nothing to attribute
335
+ if not acted:
336
+ out[aspect] = "world"
337
+ continue
338
+ if aspect not in predicted:
339
+ out[aspect] = "world"
340
+ elif predicted[aspect] == now:
341
+ out[aspect] = "self"
342
+ else:
343
+ # my action touched this aspect but the result is not what it predicted:
344
+ # something of mine and something else both moved it
345
+ out[aspect] = "both"
346
+ return out
347
+
348
+
349
+ def surprising(attributions: Mapping[str, Attribution]) -> tuple[str, ...]:
350
+ """The aspects worth attention: the ones I did not cause."""
351
+ return tuple(a for a, kind in attributions.items() if kind in ("world", "both", "unexplained"))
tensorcode/ops.py ADDED
@@ -0,0 +1,207 @@
1
+ """Typed operation facades.
2
+
3
+ Each facade fixes the *meaning* of an operation and validates outputs against it.
4
+ Which code actually runs (rules, a parser, a classifier, a solver, a model) is
5
+ decided by the bound ``Runtime``; with nothing bound, inferential facades return
6
+ ``Unknown(reason="no_implementation")``. None of them implies a model call.
7
+
8
+ Families (see docs/revival/03-operations-and-backends.md):
9
+ infer: parse, classify, choose, rank
10
+ check: check, verify
11
+ rewrite: propose (``Store.apply`` commits)
12
+ invoke: invoke (actions.py)
13
+ convert: to_records, from_records, pack (records.py, context.py)
14
+ query: Store.claims / match / neighborhood (records.py)
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import enum
20
+ from dataclasses import dataclass
21
+ from typing import Any, Callable, Sequence, TypeVar
22
+
23
+ from .outcomes import Receipt, Score, Unknown, Verdict
24
+ from .records import Patch
25
+ from .runtime import Request, current
26
+
27
+ T = TypeVar("T")
28
+ A = TypeVar("A")
29
+
30
+
31
+ def _is_instance_of(target: Any) -> Callable[[Any], bool]:
32
+ def check(value: Any) -> bool:
33
+ try:
34
+ return isinstance(value, target)
35
+ except TypeError: # parametrized generics, Literal, ...: the implementation owns validation
36
+ return True
37
+
38
+ return check
39
+
40
+
41
+ # ------------------------------------------------------------------- infer
42
+
43
+
44
+ def parse(source: Any, into: type[T], **params: Any) -> T | Unknown:
45
+ """Interpret ``source`` (text, a document, an observation) as a value of type ``into``.
46
+
47
+ Fails closed: an output that is not an ``into`` is treated as a failed attempt.
48
+ """
49
+ return current().call(Request("parse", source, into, params), validate=_is_instance_of(into)).value
50
+
51
+
52
+ def _parse_many(sources: Sequence[Any], into: type[T], **params: Any) -> list[T | Unknown]:
53
+ outs = current().call_many([Request("parse", s, into, params) for s in sources], validate=_is_instance_of(into))
54
+ return [o.value for o in outs]
55
+
56
+
57
+ parse.many = _parse_many # type: ignore[attr-defined]
58
+
59
+
60
+ def classify(item: Any, labels: type[T], **params: Any) -> T | Unknown:
61
+ """Estimate which label *is true* of ``item``. Not a decision about what to do."""
62
+ return current().call(Request("classify", item, labels, params), validate=_label_check(labels)).value
63
+
64
+
65
+ def _classify_many(items: Sequence[Any], labels: type[T], **params: Any) -> list[T | Unknown]:
66
+ outs = current().call_many([Request("classify", x, labels, params) for x in items], validate=_label_check(labels))
67
+ return [o.value for o in outs]
68
+
69
+
70
+ def _classify_scored(item: Any, labels: type[T], **params: Any) -> tuple[T | Unknown, Score | None]:
71
+ """The label *and* the confidence its implementation reported, for callers that gate on it.
72
+
73
+ An implementation that reports no confidence yields ``None`` — which a gate must treat as
74
+ "unknown confidence", not as zero, or an accurate tier with no score silently becomes an
75
+ escalation. Measured: a keyword tier answering 173/3,080 at 97.1% precision made a cascade
76
+ *worse* than the learned tier alone until its precision was reported as a probability.
77
+ """
78
+ out = current().call(Request("classify", item, labels, params), validate=_label_check(labels))
79
+ return out.value, out.score
80
+
81
+
82
+ def _classify_many_scored(items: Sequence[Any], labels: type[T], **params: Any) -> list[tuple[T | Unknown, Score | None]]:
83
+ outs = current().call_many([Request("classify", x, labels, params) for x in items], validate=_label_check(labels))
84
+ return [(o.value, o.score) for o in outs]
85
+
86
+
87
+ classify.many = _classify_many # type: ignore[attr-defined]
88
+ classify.scored = _classify_scored # type: ignore[attr-defined]
89
+ classify.many_scored = _classify_many_scored # type: ignore[attr-defined]
90
+
91
+
92
+ def _label_check(labels: Any) -> Callable[[Any], bool]:
93
+ if isinstance(labels, type) and issubclass(labels, enum.Enum):
94
+ return lambda v: isinstance(v, labels)
95
+ return lambda v: v in labels
96
+
97
+
98
+ @dataclass(frozen=True)
99
+ class Objective:
100
+ """What makes one feasible option better than another."""
101
+
102
+ name: str
103
+ description: str
104
+ utility: Callable[[Any, Any], float] | None = None # (option, given) -> utility, when computable
105
+
106
+
107
+ @dataclass(frozen=True)
108
+ class Constraint:
109
+ """A hard requirement checked by TensorCode itself, never delegated to a backend."""
110
+
111
+ name: str
112
+ test: Callable[[Any, Any], bool | Unknown] # (option, given)
113
+
114
+
115
+ def choose(options: Sequence[A], *, objective: Objective, given: Any = None, constraints: Sequence[Constraint] = ()) -> A | Unknown:
116
+ """Select an action. Unlike ``classify``, this involves an objective and hard constraints.
117
+
118
+ Constraints are evaluated here, before any backend sees the options. An
119
+ undetermined constraint (``Unknown``) excludes the option. The backend can only
120
+ return one of the feasible options; anything else is an invalid attempt.
121
+ """
122
+ feasible, notes = [], []
123
+ for option in options:
124
+ failed = []
125
+ for c in constraints:
126
+ ok = c.test(option, given)
127
+ if isinstance(ok, Unknown):
128
+ failed.append(f"{c.name}=unknown({ok.reason})")
129
+ elif not ok:
130
+ failed.append(c.name)
131
+ if failed:
132
+ notes.append(f"excluded {option!r}: {', '.join(failed)}")
133
+ else:
134
+ feasible.append(option)
135
+ rt = current()
136
+ if not feasible:
137
+ span = rt.trace.open("choose", target=objective.name, input=tuple(options))
138
+ span.notes.extend(notes or ["no options were proposed"])
139
+ return span.close(Unknown("no_feasible_option", "; ".join(notes)), "unknown")
140
+ if len(feasible) == 1:
141
+ span = rt.trace.open("choose", target=objective.name, input=tuple(options))
142
+ span.notes.extend(notes + ["only one feasible option; no backend consulted"])
143
+ return span.close(feasible[0], "answer")
144
+ request = Request("choose", tuple(feasible), objective, {"given": given})
145
+ return rt.call(request, validate=lambda v: any(v is o or v == o for o in feasible), notes=notes).value
146
+
147
+
148
+ def rank(query: Any, candidates: Sequence[A], *, limit: int | None = None, **params: Any) -> list[tuple[A, Score]] | Unknown:
149
+ """Order candidates by relevance to ``query``. Scores are comparable only within this ranking."""
150
+
151
+ def valid(v: Any) -> bool:
152
+ return isinstance(v, list) and all(
153
+ isinstance(pair, tuple) and len(pair) == 2 and isinstance(pair[1], Score) and pair[1].kind in ("relevance", "similarity")
154
+ for pair in v
155
+ )
156
+
157
+ out = current().call(Request("rank", query, None, {"candidates": tuple(candidates), **params}), validate=valid).value
158
+ return out if isinstance(out, Unknown) or limit is None else out[:limit]
159
+
160
+
161
+ # ------------------------------------------------------------------- check
162
+
163
+
164
+ def check(proposition: Any, *, evidence: Sequence[Any] = (), **params: Any) -> Verdict:
165
+ """Evaluate a claim, candidate, or constraint against evidence. Unknown is not false."""
166
+ out = current().call(Request("check", proposition, None, {"evidence": tuple(evidence), **params}), validate=lambda v: isinstance(v, Verdict)).value
167
+ return Verdict("unknown", (f"{out.reason}: {out.detail}",)) if isinstance(out, Unknown) else out
168
+
169
+
170
+ def _check_many(propositions: Sequence[Any], *, evidence: Sequence[Any] = (), **params: Any) -> list[Verdict]:
171
+ requests = [Request("check", p, None, {"evidence": tuple(evidence), **params}) for p in propositions]
172
+ outs = current().call_many(requests, validate=lambda v: isinstance(v, Verdict))
173
+ return [Verdict("unknown", (f"{o.value.reason}: {o.value.detail}",)) if isinstance(o.value, Unknown) else o.value for o in outs]
174
+
175
+
176
+ check.many = _check_many # type: ignore[attr-defined]
177
+
178
+
179
+ def verify(receipt: Receipt, *, observe: Callable[[], Any], expect: Callable[[Any], bool | Unknown]) -> Verdict:
180
+ """Did an invoked action have its intended effect, according to a fresh observation?
181
+
182
+ A receipt saying ``applied`` is the executor's report, not verification.
183
+ """
184
+ rt = current()
185
+ span = rt.trace.open("verify", target=type(receipt.action).__name__, input=receipt.status)
186
+ if receipt.status in ("rejected", "failed"):
187
+ verdict = Verdict("fails", (f"receipt status {receipt.status}: {receipt.error}",))
188
+ else:
189
+ observation = observe()
190
+ if isinstance(observation, Unknown):
191
+ verdict = Verdict("unknown", (f"observation unavailable: {observation.reason}",))
192
+ else:
193
+ ok = expect(observation)
194
+ if isinstance(ok, Unknown):
195
+ verdict = Verdict("unknown", (ok.reason,), (observation,))
196
+ else:
197
+ verdict = Verdict("holds" if ok else "fails", ("postcondition observed" if ok else "postcondition not observed",), (observation,))
198
+ return span.close(verdict, "answer" if verdict.status != "unknown" else "unknown")
199
+
200
+
201
+ # ----------------------------------------------------------------- rewrite
202
+
203
+
204
+ def propose(state: Any, *, goal: Any, base_revision: int, **params: Any) -> Patch | Unknown:
205
+ """Propose a change to structured state. The result is inert until applied."""
206
+ request = Request("propose", state, goal, {"base_revision": base_revision, **params})
207
+ return current().call(request, validate=lambda v: isinstance(v, Patch) and v.base_revision == base_revision).value
tensorcode/outcomes.py ADDED
@@ -0,0 +1,99 @@
1
+ """Outcome values returned by operations.
2
+
3
+ These are ordinary values, not execution wrappers. Each one encodes a semantic
4
+ distinction that a caller must not silently collapse:
5
+
6
+ * ``Unknown`` is not ``False`` and not a low-confidence guess.
7
+ * ``Verdict`` has three states; ``fails`` and ``unknown`` are different.
8
+ * ``Receipt`` distinguishes "did not happen" from "may have happened".
9
+ * ``Score`` says what kind of number it is (a similarity is not a probability).
10
+
11
+ Execution metadata (backend, timing, cost, escalation) lives in the trace, not here.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from dataclasses import dataclass, field
17
+ from datetime import datetime, timezone
18
+ from typing import Any, Literal
19
+
20
+
21
+ def _no_truth_value(self: Any) -> bool:
22
+ raise TypeError(
23
+ f"{type(self).__name__} has no truth value; handle it explicitly "
24
+ "(e.g. `isinstance(x, Unknown)` or `verdict.status == 'holds'`)"
25
+ )
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ class Unknown:
30
+ """An operation could not produce an answer it is entitled to give.
31
+
32
+ ``candidates`` are best-effort guesses with their scores. They are *not* answers.
33
+ """
34
+
35
+ reason: str
36
+ detail: str = ""
37
+ candidates: tuple[tuple[Any, "Score"], ...] = ()
38
+
39
+ __bool__ = _no_truth_value
40
+
41
+
42
+ ScoreKind = Literal[
43
+ "probability", # calibrated on a named dataset (see ``basis``)
44
+ "uncalibrated", # model-internal confidence; ordering only
45
+ "similarity", # geometric closeness; not a probability of anything
46
+ "relevance", # retrieval score; comparable only within one ranking
47
+ "utility", # objective value under a declared objective
48
+ "vote_share", # fraction of voters/samples
49
+ ]
50
+
51
+
52
+ @dataclass(frozen=True)
53
+ class Score:
54
+ value: float
55
+ kind: ScoreKind
56
+ basis: str = "" # e.g. "banking77/validation@2026-09-16" for a calibrated probability
57
+
58
+ def __post_init__(self) -> None:
59
+ if self.kind == "probability" and not self.basis:
60
+ raise ValueError("a calibrated probability must name its calibration basis")
61
+
62
+
63
+ @dataclass(frozen=True)
64
+ class Verdict:
65
+ """Result of evaluating a proposition, candidate, or constraint against evidence."""
66
+
67
+ status: Literal["holds", "fails", "unknown"]
68
+ reasons: tuple[str, ...] = ()
69
+ evidence: tuple[Any, ...] = () # Refs or observations actually consulted
70
+
71
+ __bool__ = _no_truth_value
72
+
73
+ @property
74
+ def holds(self) -> bool:
75
+ return self.status == "holds"
76
+
77
+
78
+ ReceiptStatus = Literal[
79
+ "applied", # the effect happened and the executor observed it
80
+ "rejected", # the executor refused before any effect (policy, validation, auth)
81
+ "failed", # attempted; known not to have taken effect
82
+ "indeterminate", # attempted; may or may not have taken effect (timeouts, lost replies)
83
+ ]
84
+
85
+
86
+ @dataclass(frozen=True)
87
+ class Receipt:
88
+ """What an executor can honestly say about one invocation of an action."""
89
+
90
+ action: Any
91
+ status: ReceiptStatus
92
+ retryable: bool = False # safe to retry *as far as the executor knows*
93
+ idempotency_key: str | None = None
94
+ effect_id: str | None = None
95
+ error: str | None = None
96
+ retry_after_s: float | None = None
97
+ at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
98
+
99
+ __bool__ = _no_truth_value