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
tensorcode/temporal.py ADDED
@@ -0,0 +1,189 @@
1
+ """Event time and ordering, so tense stops being a label and starts being a question.
2
+
3
+ The grammar reads tense and aspect and puts them in a feature; the projection then drops
4
+ them. So "the grain arrived" and "the grain will arrive" become the same claim, and
5
+ "what did you do before that" has nothing to stand on. Here a parsed tense becomes an
6
+ :class:`~tensorcode.records.Interval` on the claim, and events get times that can be
7
+ ordered, so the graph answers *when* and *in what order*.
8
+
9
+ Two clocks are kept apart, because conflating them is how a record of what was said
10
+ becomes a record of what happened:
11
+
12
+ * ``observed_at`` on evidence: when a source said it. Already in the store.
13
+ * the event's own time: when the thing happened. That is what this module adds.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from dataclasses import dataclass
19
+ from datetime import datetime, timedelta, timezone
20
+ from typing import Any, Iterable, Mapping, Sequence
21
+
22
+ from .outcomes import Unknown
23
+ from .records import Claim, ClaimRecord, Evidence, Interval, Ref, Store
24
+
25
+ #: how a connective relates the clause it introduces to the main clause
26
+ CONNECTIVES: dict[str, str] = {
27
+ "before": "before", "after": "after", "since": "after", "until": "before",
28
+ "while": "during", "when": "during", "whenever": "during", "once": "after",
29
+ "then": "after", "earlier": "before", "later": "after", "meanwhile": "during",
30
+ }
31
+
32
+ AT = "happened_at"
33
+ ENDED = "ended_at"
34
+
35
+
36
+ def interval_for(features: Mapping[str, Any], now: datetime) -> Interval:
37
+ """The interval a tense/aspect commits to, relative to ``now``.
38
+
39
+ Deliberately coarse: past means "ended before now", future means "starts after now",
40
+ present means "holds now". A coarse interval that is true beats a precise one invented.
41
+ """
42
+ tense, aspect = features.get("tense"), features.get("aspect")
43
+ if tense == "past":
44
+ return Interval(None, now) if aspect != "perfect" else Interval(None, now)
45
+ if tense == "future":
46
+ return Interval(now, None)
47
+ if tense == "present":
48
+ return Interval(now, now) if aspect != "progressive" else Interval(now, None)
49
+ return Interval()
50
+
51
+
52
+ @dataclass(frozen=True)
53
+ class Event:
54
+ ref: Ref
55
+ at: datetime
56
+ kind: str | None = None
57
+
58
+ def describe(self) -> str:
59
+ return f"{self.ref.id}{f' ({self.kind})' if self.kind else ''} at {self.at.isoformat(timespec='seconds')}"
60
+
61
+
62
+ def tell_event(mind: Store, ref: Ref, *, at: datetime, kind: str | None = None, source: Ref,
63
+ ended: datetime | None = None, method: str = "temporal") -> Ref:
64
+ """Record when an event happened (as distinct from when it was reported)."""
65
+ evidence = Evidence(source=source, observed_at=datetime.now(timezone.utc), method=method)
66
+ mind.tell(Claim(ref, AT, at, valid=Interval(at, ended or at)), evidence)
67
+ if kind:
68
+ mind.tell(Claim(ref, "is_a", kind, valid=Interval(at, ended or at)), evidence)
69
+ if ended:
70
+ mind.tell(Claim(ref, ENDED, ended, valid=Interval(at, ended)), evidence)
71
+ return ref
72
+
73
+
74
+ def event_time(mind: Store, ref: Ref) -> datetime | Unknown:
75
+ """When an event happened, by its own clock; falls back to nothing, never to a guess."""
76
+ for record in mind.claims(ref, AT):
77
+ value = record.claim.object
78
+ if isinstance(value, datetime):
79
+ return value
80
+ return Unknown("no_event_time", f"{ref.id} has no recorded time of happening")
81
+
82
+
83
+ def events(mind: Store, *, kind: str | None = None) -> list[Event]:
84
+ """Every event with a recorded time, earliest first."""
85
+ out: list[Event] = []
86
+ for record in mind.claims(predicate=AT):
87
+ value = record.claim.object
88
+ if not isinstance(value, datetime):
89
+ continue
90
+ ref = record.claim.subject
91
+ of = next((r.claim.object for r in mind.claims(ref, "is_a")), None)
92
+ if kind is not None and of != kind:
93
+ continue
94
+ out.append(Event(ref, value, of if isinstance(of, str) else None))
95
+ return sorted(out, key=lambda e: (e.at, e.ref.id))
96
+
97
+
98
+ def relate(mind: Store, a: Ref, b: Ref, *, tolerance: timedelta = timedelta(0)) -> str | Unknown:
99
+ """'before', 'after' or 'simultaneous' — or a refusal when either time is unrecorded."""
100
+ ta, tb = event_time(mind, a), event_time(mind, b)
101
+ if isinstance(ta, Unknown):
102
+ return ta
103
+ if isinstance(tb, Unknown):
104
+ return tb
105
+ if abs(ta - tb) <= tolerance:
106
+ return "simultaneous"
107
+ return "before" if ta < tb else "after"
108
+
109
+
110
+ def order(mind: Store, refs: Iterable[Ref]) -> tuple[list[Ref], list[Ref]]:
111
+ """Refs sorted by their event time, and the ones that have no time to sort by."""
112
+ timed: list[tuple[datetime, Ref]] = []
113
+ untimed: list[Ref] = []
114
+ for ref in refs:
115
+ at = event_time(mind, ref)
116
+ (untimed if isinstance(at, Unknown) else timed).append(ref if isinstance(at, Unknown) else (at, ref)) # type: ignore[arg-type]
117
+ return [ref for _, ref in sorted(timed, key=lambda pair: (pair[0], pair[1].id))], untimed
118
+
119
+
120
+ def before(mind: Store, ref: Ref, *, kind: str | None = None) -> list[Event]:
121
+ """Events that happened before this one."""
122
+ at = event_time(mind, ref)
123
+ return [] if isinstance(at, Unknown) else [e for e in events(mind, kind=kind) if e.at < at and e.ref != ref]
124
+
125
+
126
+ def after(mind: Store, ref: Ref, *, kind: str | None = None) -> list[Event]:
127
+ at = event_time(mind, ref)
128
+ return [] if isinstance(at, Unknown) else [e for e in events(mind, kind=kind) if e.at > at and e.ref != ref]
129
+
130
+
131
+ def during(mind: Store, start: datetime, end: datetime, *, kind: str | None = None) -> list[Event]:
132
+ return [e for e in events(mind, kind=kind) if start <= e.at <= end]
133
+
134
+
135
+ def first_seen(record: ClaimRecord) -> datetime | None:
136
+ """The earliest moment any source put this claim on record."""
137
+ times = [e.observed_at for e in record.evidence]
138
+ return min(times) if times else None
139
+
140
+
141
+ def changed_since(mind: Store, when: datetime, *, scope: Ref | None = None) -> list[Claim]:
142
+ """Claims that came on record after ``when`` — what is new since a moment.
143
+
144
+ This reads the *reporting* clock, which is the right one for "what changed since my
145
+ last message": the question is about the record, not about when the world moved.
146
+ """
147
+ out: list[tuple[datetime, Claim]] = []
148
+ for record in mind.claims(scope=scope) if scope is not None else mind.claims():
149
+ seen = first_seen(record)
150
+ if seen is not None and seen > when:
151
+ out.append((seen, record.claim))
152
+ return [claim for _, claim in sorted(out, key=lambda pair: pair[0])]
153
+
154
+
155
+ def since(mind: Store, ref: Ref, *, kind: str | None = None) -> list[Event] | Unknown:
156
+ """Events after a named event — "what happened since the commit"."""
157
+ at = event_time(mind, ref)
158
+ return at if isinstance(at, Unknown) else after(mind, ref, kind=kind)
159
+
160
+
161
+ def connective_relation(word: str) -> str | Unknown:
162
+ """What ordering a connective asserts between its clause and the main one."""
163
+ got = CONNECTIVES.get(word.strip().lower())
164
+ return got if got else Unknown("not_a_temporal_connective", word)
165
+
166
+
167
+ def tell_order(mind: Store, earlier: Ref, later: Ref, *, source: Ref, relation: str = "before",
168
+ method: str = "temporal:connective") -> Claim:
169
+ """Record an ordering asserted by language, when neither event has a clock time.
170
+
171
+ "the grain arrived before the snow came" orders two events without dating either, and
172
+ that is worth keeping: it answers ordering questions that timestamps cannot.
173
+ """
174
+ claim = Claim(earlier, relation, later)
175
+ mind.tell(claim, Evidence(source=source, observed_at=datetime.now(timezone.utc), method=method))
176
+ return claim
177
+
178
+
179
+ def ordered_by_claims(mind: Store, a: Ref, b: Ref) -> str | Unknown:
180
+ """Ordering from asserted ``before``/``after`` claims, for undated events."""
181
+ if mind.claims(a, "before", b):
182
+ return "before"
183
+ if mind.claims(b, "before", a):
184
+ return "after"
185
+ if mind.claims(a, "after", b):
186
+ return "after"
187
+ if mind.claims(b, "after", a):
188
+ return "before"
189
+ return Unknown("no_recorded_order", f"nothing orders {a.id} and {b.id}")
tensorcode/wants.py ADDED
@@ -0,0 +1,185 @@
1
+ """Wants: an open question as a first-class object, and the standing pull toward answering it.
2
+
3
+ ``Unknown`` says an operation could not answer. That is where it stops, which is why a
4
+ mind built on it goes quiet instead of going looking. A ``Want`` is the same admission with
5
+ its consequences attached: what would satisfy it, what provenance the answer must have, and
6
+ which candidate satisfiers exist — a memory to search, a modality to look at, a command to
7
+ run, a person to ask, a derivation to attempt.
8
+
9
+ The runtime can then rank wants by what an answer is worth against what it costs, and hand
10
+ the chosen one to whoever can satisfy it. Abstention stays honest at both ends: looking up
11
+ an answer refuses to pick between disagreeing claims, refuses hearsay where the want asked
12
+ for an observation, and a want nothing can satisfy simply stays open.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import hashlib
18
+ from dataclasses import dataclass, field
19
+ from typing import Any, Iterable, Literal
20
+
21
+ from .outcomes import Score, Unknown
22
+ from .records import ClaimRecord, Ref, Store
23
+
24
+ SatisfierKind = Literal["memory", "perceive", "command", "ask", "derive"]
25
+
26
+ #: which modalities count as first-hand, for a want that requires an observation
27
+ FIRSTHAND = ("pixels", "structure", "text")
28
+
29
+
30
+ @dataclass(frozen=True)
31
+ class Satisfier:
32
+ """One way this want might be answered, with what it would cost to try."""
33
+
34
+ kind: SatisfierKind
35
+ detail: str # a place to look, a command to run, a question to put to someone
36
+ cost: float = 1.0 # caller's own units (seconds, tokens, keystrokes)
37
+ odds: Score = field(default_factory=lambda: Score(0.5, "uncalibrated"))
38
+
39
+ @property
40
+ def worth(self) -> float:
41
+ return self.odds.value / max(self.cost, 1e-6)
42
+
43
+
44
+ @dataclass(frozen=True)
45
+ class Want:
46
+ """A question the mind is holding open."""
47
+
48
+ question: str
49
+ subject: Ref | None = None
50
+ predicate: str | None = None
51
+ expect: type | None = None # what kind of value would satisfy it
52
+ requires: str | None = None # provenance demand: "observed", "not-hearsay", or a modality name
53
+ satisfiers: tuple[Satisfier, ...] = ()
54
+ value: float = 1.0 # how much an answer is worth
55
+ asked_by: Ref | None = None # who wants to know (the user, a rule, a procedure)
56
+
57
+ @property
58
+ def id(self) -> str:
59
+ key = f"{self.question}|{self.subject}|{self.predicate}|{self.requires}"
60
+ return "want:" + hashlib.sha256(key.encode()).hexdigest()[:12]
61
+
62
+ def best_satisfier(self) -> Satisfier | None:
63
+ return max(self.satisfiers, key=lambda s: (s.worth, s.kind), default=None)
64
+
65
+
66
+ @dataclass(frozen=True)
67
+ class Answer:
68
+ """A want satisfied, with the claims that answer it and how they were known."""
69
+
70
+ want_id: str
71
+ value: Any
72
+ claims: tuple[str, ...]
73
+ modality: tuple[str, ...] = ()
74
+
75
+ def __str__(self) -> str:
76
+ return f"{self.value!r} (from {len(self.claims)} claim{'s' * (len(self.claims) != 1)})"
77
+
78
+
79
+ def want_from(unknown: Unknown, question: str, *, subject: Ref | None = None, predicate: str | None = None,
80
+ satisfiers: Iterable[Satisfier] = (), value: float = 1.0) -> Want:
81
+ """Turn a dead-end ``Unknown`` into a want, keeping its reason and any candidates it had."""
82
+ extra = tuple(Satisfier("derive", f"reconsider {cand!r}", cost=0.5, odds=score) for cand, score in unknown.candidates)
83
+ return Want(question=question, subject=subject, predicate=predicate,
84
+ satisfiers=tuple(satisfiers) + extra, value=value,
85
+ asked_by=Ref(f"unknown:{unknown.reason}"))
86
+
87
+
88
+ class Wants:
89
+ """The standing set of open questions."""
90
+
91
+ def __init__(self) -> None:
92
+ self.open: dict[str, Want] = {}
93
+ self.answered: dict[str, Answer] = {}
94
+ self.abandoned: dict[str, str] = {}
95
+
96
+ def add(self, want: Want) -> str:
97
+ if want.id not in self.answered:
98
+ self.open[want.id] = want
99
+ return want.id
100
+
101
+ def drop(self, want_id: str, reason: str) -> None:
102
+ self.open.pop(want_id, None)
103
+ self.abandoned[want_id] = reason
104
+
105
+ def satisfied(self, want_id: str, answer: Answer) -> Answer:
106
+ self.open.pop(want_id, None)
107
+ self.answered[want_id] = answer
108
+ return answer
109
+
110
+ # -- ranking
111
+
112
+ def ranked(self) -> list[tuple[Want, Satisfier | None, float]]:
113
+ """Open wants by what answering them is worth against what it would cost."""
114
+ rows = []
115
+ for want in self.open.values():
116
+ best = want.best_satisfier()
117
+ rows.append((want, best, want.value * (best.worth if best else 0.0)))
118
+ return sorted(rows, key=lambda row: (-row[2], row[0].id))
119
+
120
+ def next_to_pursue(self) -> tuple[Want, Satisfier] | None:
121
+ for want, satisfier, _ in self.ranked():
122
+ if satisfier is not None:
123
+ return want, satisfier
124
+ return None
125
+
126
+ # -- answering from memory
127
+
128
+ def look_up(self, want: Want, mind: Store, *, frames: Any = None) -> Answer | Unknown:
129
+ """Try to answer from what the mind already holds. Refuses to guess."""
130
+ if want.subject is None and want.predicate is None:
131
+ return Unknown("want_underspecified", f"{want.question!r} names neither a subject nor a predicate")
132
+ records = mind.claims(subject=want.subject, predicate=want.predicate)
133
+ if not records:
134
+ return Unknown("not_in_memory", f"nothing recorded for {want.question!r}")
135
+ allowed = [r for r in records if _provenance_ok(r, want.requires, frames)]
136
+ if not allowed:
137
+ kinds = sorted({m for r in records for m in _modalities(r, frames)})
138
+ return Unknown("provenance_unmet", f"{want.question!r} needs {want.requires}; have only {', '.join(kinds) or 'unknown'}",
139
+ candidates=tuple((r.claim.object, Score(0.5, "uncalibrated")) for r in records))
140
+ if want.expect is not None:
141
+ allowed = [r for r in allowed if isinstance(r.claim.object, want.expect)]
142
+ if not allowed:
143
+ return Unknown("wrong_type", f"{want.question!r} expects {want.expect.__name__}")
144
+ distinct = {repr(r.claim.object): r for r in allowed}
145
+ if len(distinct) > 1:
146
+ return Unknown("disagreement", f"memory holds {len(distinct)} different answers to {want.question!r}",
147
+ candidates=tuple((r.claim.object, Score(1 / len(distinct), "vote_share")) for r in distinct.values()))
148
+ record = next(iter(distinct.values()))
149
+ return Answer(want.id, record.claim.object, tuple(sorted(r.id for r in allowed)), _modalities(record, frames))
150
+
151
+ def pursue_from_memory(self, mind: Store, *, frames: Any = None) -> list[Answer]:
152
+ """Answer every open want memory can already settle; leave the rest open."""
153
+ out = []
154
+ for want in list(self.open.values()):
155
+ found = self.look_up(want, mind, frames=frames)
156
+ if isinstance(found, Answer):
157
+ out.append(self.satisfied(want.id, found))
158
+ return out
159
+
160
+ def as_unknown(self, want: Want) -> Unknown:
161
+ """What to hand a caller that wanted an answer now: honest, with what we would try next."""
162
+ best = want.best_satisfier()
163
+ detail = f"{want.question}; next I would {best.kind} ({best.detail})" if best else f"{want.question}; nothing I can do would answer it"
164
+ return Unknown("open_want", detail)
165
+
166
+
167
+ def _modalities(rec: ClaimRecord, frames: Any) -> tuple[str, ...]:
168
+ if frames is not None:
169
+ binding = frames.binding(rec.id)
170
+ if binding is not None:
171
+ return binding.modality
172
+ from .frames import modality_of
173
+
174
+ return modality_of(rec)
175
+
176
+
177
+ def _provenance_ok(rec: ClaimRecord, requires: str | None, frames: Any) -> bool:
178
+ if requires is None:
179
+ return True
180
+ mods = _modalities(rec, frames)
181
+ if requires == "observed":
182
+ return any(m in FIRSTHAND for m in mods)
183
+ if requires == "not-hearsay":
184
+ return "hearsay" not in mods
185
+ return requires in mods
@@ -0,0 +1,196 @@
1
+ Metadata-Version: 2.5
2
+ Name: tensorcode
3
+ Version: 0.1.0a1
4
+ Summary: Typed cognitive operations with swappable implementations: rules, learned models, or general models, chosen by policy
5
+ Project-URL: Homepage, https://github.com/TensaCo/tensacode-py
6
+ Project-URL: Source, https://github.com/TensaCo/tensacode-py
7
+ Project-URL: Issues, https://github.com/TensaCo/tensacode-py/issues
8
+ Project-URL: Design, https://github.com/TensaCo/tensacode-py/tree/main/docs/revival
9
+ Author-email: Jacob Valdez <jacob.valdez@tensaco.ai>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: agents,cascade,cognitive-architecture,neurosymbolic,typed-operations
13
+ Classifier: Development Status :: 2 - Pre-Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.11
26
+ Provides-Extra: dev
27
+ Requires-Dist: numpy>=1.26; extra == 'dev'
28
+ Requires-Dist: pydantic>=2.5; extra == 'dev'
29
+ Requires-Dist: pytest>=8; extra == 'dev'
30
+ Provides-Extra: learned
31
+ Requires-Dist: numpy>=1.26; extra == 'learned'
32
+ Requires-Dist: scikit-learn>=1.5; extra == 'learned'
33
+ Provides-Extra: learned-neural
34
+ Requires-Dist: torch>=2.1; extra == 'learned-neural'
35
+ Requires-Dist: transformers>=4.40; extra == 'learned-neural'
36
+ Provides-Extra: local-model
37
+ Requires-Dist: torch>=2.4; extra == 'local-model'
38
+ Requires-Dist: transformers>=4.45; extra == 'local-model'
39
+ Description-Content-Type: text/markdown
40
+
41
+ # TensorCode
42
+
43
+ **Typed cognitive operations with swappable implementations.** Your program says *what*
44
+ it needs (parse this, classify that, choose an action under these constraints, check this
45
+ claim). A policy decides *which* implementation answers: rules, a learned model, or a
46
+ general model. Every answer is validated against the operation's type, every abstention
47
+ is an explicit value, and every attempt is traced.
48
+
49
+ > **Status: pre-alpha (0.1.0a1).** The API will change. The core has no third-party
50
+ > dependencies. Nothing here calls a language model unless you bind one.
51
+
52
+ ```bash
53
+ pip install --pre tensorcode
54
+ ```
55
+
56
+ Python 3.11+.
57
+
58
+ ## Quick start
59
+
60
+ ```python
61
+ import enum
62
+ import tensorcode as tc
63
+ from tensorcode.backends.builtin import KeywordClassifier
64
+
65
+ class Intent(enum.Enum):
66
+ LOST_CARD = "lost_card"
67
+ REFUND = "refund"
68
+
69
+ print(tc.classify("I lost my card", Intent))
70
+ # Unknown(reason='no_implementation', ...) <- nothing bound: an explicit Unknown, not a guess
71
+
72
+ rules = KeywordClassifier(Intent, {
73
+ Intent.LOST_CARD: [r"\blost\b", r"\bstolen\b"],
74
+ Intent.REFUND: [r"\brefund\b"],
75
+ })
76
+
77
+ @tc.implementation("classify", name="fallback", version="1")
78
+ def ask_a_human(request):
79
+ return tc.Unknown("needs_human", request.subject)
80
+
81
+ with tc.use(tc.Runtime([rules, ask_a_human])) as rt:
82
+ print(tc.classify("I lost my card", Intent)) # Intent.LOST_CARD
83
+ print(tc.classify("where is my parcel?", Intent)) # Unknown(reason='needs_human', ...)
84
+ print(rt.trace.render())
85
+ ```
86
+
87
+ ```text
88
+ classify -> Intent.LOST_CARD [0.04 ms total, 0.01 ms backend]
89
+ - keyword-rules@1 answer
90
+ classify -> {'unknown': 'needs_human', ...} [0.03 ms total, 0.01 ms backend]
91
+ - keyword-rules@1 abstain: no_rule_matched
92
+ - fallback@1 abstain: needs_human, usd=? (unknown)
93
+ ```
94
+
95
+ The program never names a backend. Swap the rules for a scikit-learn classifier or a local
96
+ model by changing the `Runtime`, not the program.
97
+
98
+ ## Core ideas
99
+
100
+ **Operations fix the meaning.** Each facade validates its output and fails closed:
101
+
102
+ | Family | Operations |
103
+ | --- | --- |
104
+ | infer | `parse`, `classify`, `choose`, `rank` |
105
+ | check | `check`, `verify` |
106
+ | rewrite | `propose` (a `Patch`, inert until `Store.apply`) |
107
+ | act | `invoke` (returns a `Receipt`), `Plan`, `run_plan` |
108
+ | context | `pack`, `dedupe` |
109
+
110
+ `classify` estimates what *is true*. `choose` selects what *to do*, under an `Objective`
111
+ and hard `Constraint`s that TensorCode checks itself before any backend sees the options.
112
+
113
+ **Outcome values keep distinctions a caller must not collapse.**
114
+ - `Unknown` is not `False` and not a low-confidence guess. It raises if used as a boolean.
115
+ - `Verdict` has three states. `fails` and `unknown` are different.
116
+ - `Receipt` separates "did not happen" from "may have happened".
117
+ - `Score` says what kind of number it is. A similarity is not a probability, and a
118
+ probability must name the data it was calibrated on.
119
+
120
+ **The runtime binds implementations by policy.** An implementation declares `Traits`
121
+ (locality, egress, determinism, requirements) and a measured `Profile`. `Policy` filters
122
+ on hard constraints and orders a cascade; `Budget` caps cost and attempts across calls.
123
+ Unmeasured means unknown: a missing cost is never counted as zero, and a cost cap excludes
124
+ implementations whose cost is unknown.
125
+
126
+ **Records carry evidence.** `Store` holds entity and claim records with evidence, validity
127
+ intervals and scope, and detects conflicts between them.
128
+
129
+ ## Optional extras
130
+
131
+ | Extra | Installs | For |
132
+ | --- | --- | --- |
133
+ | `learned` | scikit-learn, numpy | `tensorcode.backends.linear` |
134
+ | `local-model` | torch, transformers | `tensorcode.backends.hf_local` |
135
+ | `learned-neural` | torch, transformers | `tensorcode.backends.neural` |
136
+
137
+ ## Examples
138
+
139
+ The examples live in this repository, not in the package. Run them from a checkout:
140
+
141
+ | Example | Command |
142
+ | --- | --- |
143
+ | Recovery after failed or ambiguous actions | `python -m examples.recovery.demo` |
144
+ | Knowledge store: ingest, conflicts, queries | `python -m examples.knowledge.demo` |
145
+ | Packing context into a token budget | `python -m examples.context_select.demo` |
146
+ | Support router (needs the Banking77 train CSV) | `python -m examples.support_router.demo --train banking77_train.csv` |
147
+ | Decision service with HTTP API and operator UI | `python -m examples.decisions.service` |
148
+ | Live browser agents, no model calls (needs `playwright`, `numpy`, `pillow`) | `python -m examples.browser_agents.live` |
149
+
150
+ Expected output is checked in beside each demo (`OUTPUT*.txt`).
151
+
152
+ ## What has been measured, honestly
153
+
154
+ The design notes in [`docs/revival/`](https://github.com/TensaCo/tensacode-py/blob/main/docs/revival/README.md) report measurements,
155
+ including the unflattering ones:
156
+
157
+ - A local zero-shot Qwen3-8B escalation tier **lowered** Banking77 selective accuracy from
158
+ 94.1% to 91.0%. Tiers should be admitted only by measured quality.
159
+ - Recovery logic based on explicit facts made 0 duplicate money movements in 5,000
160
+ simulated episodes, compared with 4.8% for naive retry. The simulator and its fault model
161
+ are the project's own, and with wrong facts about the target system duplicates return (0.9%).
162
+ - The [evidence audit](https://github.com/TensaCo/tensacode-py/blob/main/docs/revival/11-evidence-audit.md) found that 13 of the project's 19
163
+ headline results ran in environments it wrote **and** were graded by code it wrote. On
164
+ public open-domain benchmarks, the cascade did worse than plainly prompting the same
165
+ model on 3 of 4.
166
+
167
+ Treat the agent results as demonstrations, not benchmarks.
168
+
169
+ ## Repository layout
170
+
171
+ ```text
172
+ src/tensorcode/ the package
173
+ tests/ pytest suite (some tests use examples/ and eval/)
174
+ examples/ runnable programs and agents
175
+ eval/ evaluation scripts and result files (eval/results/*.json)
176
+ research/ experiments, including a civilization simulation
177
+ docs/revival/ design notes and measurements
178
+ ```
179
+
180
+ ## Development
181
+
182
+ ```bash
183
+ pip install -e ".[dev,learned]"
184
+ python -m pytest -q
185
+ ```
186
+
187
+ Evaluation scripts read downloaded datasets and trained artifacts from `$TENSORCODE_SCRATCH`
188
+ (default `~/.cache/tensorcode`). Tests that need those artifacts skip when they are absent.
189
+
190
+ The legacy 2023–2024 package (`tensacode`, with `Engine` and TCIR; never published) is
191
+ preserved at the git tag `legacy-2024-11`. It was never functional and is not compatible
192
+ with this one. The PyPI name `tensacode` is a placeholder that installs `tensorcode`.
193
+
194
+ ## License
195
+
196
+ [MIT](https://github.com/TensaCo/tensacode-py/blob/main/LICENSE)
@@ -0,0 +1,53 @@
1
+ tensorcode/__init__.py,sha256=l_9uvB4iV6oudtolL6nAcm9aiuUNtc9OawNb4TeX_lo,1757
2
+ tensorcode/actions.py,sha256=aje5U5B2I5NPS4neaMX3Vonup7_P-r1rF3Esp3RQO9w,5270
3
+ tensorcode/answer_type.py,sha256=VJfyElor9FYWB9uEBASaOm8LDfEY_3R8y_GZw3ID_Wc,11506
4
+ tensorcode/awareness.py,sha256=l-MMW9FiNlzyJybovFiIXVWWmPTIkQ5v7ArLGNgATL4,15478
5
+ tensorcode/causal.py,sha256=hpQZo7nwo8jr7l-J_gctX8ihnoyjHLeclQKy6hmdELg,11339
6
+ tensorcode/change.py,sha256=UidBe9xMxR6wHABgt8jyBFpKYBw_ado21LTmLwqb9Ro,25745
7
+ tensorcode/chunking.py,sha256=PyFZSZd2IB3tU3Epa0R7KFRw2wFCvt-IVJcxrClGnN4,8866
8
+ tensorcode/cognition.py,sha256=8O3yAo1Y_hUS40gVfr8M6namOys8mndICKqr4RZZL20,15248
9
+ tensorcode/context.py,sha256=gaoydZjPlNOyMptOSJgkJcDPLlVO-jB3quxB_TSk3Ow,3289
10
+ tensorcode/control.py,sha256=YMoh4zdfvrvmOIWF337s3U01cV3Z_q-LKcv71QLdppY,14440
11
+ tensorcode/cues.py,sha256=SlzmpCRlfq0IXKdEJWhg1bWAQBMZVagB7EEEamTG7Vs,9084
12
+ tensorcode/expectation.py,sha256=C7t-peMTPFVZ53NYhmw_MB3R1p07sxw7X9GKT_8SbeI,12487
13
+ tensorcode/frames.py,sha256=111aKoy-BsGX4PwYb8Gc82oZEAiTDWZ2CvkZOBCHvA8,10520
14
+ tensorcode/memory.py,sha256=zTc2OPpQ4V6YljwIftcry-Zr6oP-0w2HVX6xEmqqHUU,15431
15
+ tensorcode/metacognition.py,sha256=dDtK0slx9iPUvXDig-elFBbs0OT7b9_2N6Apv385Z3k,15401
16
+ tensorcode/ops.py,sha256=PE_5h-d_s9ya390UChruJ63e1Z7QIRPGlbkDRFjGNfk,9357
17
+ tensorcode/outcomes.py,sha256=jHIz-k4-kMNRzIlYgBn-dZjbaXsYcYbwYV1LksW3T4I,3241
18
+ tensorcode/permanence.py,sha256=AJVx5hHWDiJyt7MD4DOz8xCYN4Lpurj-TDyknYh1GDg,16828
19
+ tensorcode/priming.py,sha256=loygI3tpvXqDwNRFQo2ziL3H1pni8AFNcZN6vQnKtp4,8712
20
+ tensorcode/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ tensorcode/quantity.py,sha256=vE1uHxdt309-ceQJlabUjRI4xvYDcOluum0aSC68aQ4,12712
22
+ tensorcode/records.py,sha256=22PIx2UlR53Qno8wJJd4DzELgCSD0W60IRtFVnHwVQ0,28792
23
+ tensorcode/relation.py,sha256=l0FTze-ehHTnay24MKccHX81VBdTsKjJC68dAwfbJ3A,38445
24
+ tensorcode/runtime.py,sha256=VIrJ6CD3wSfzQw2_OXI3mjttvJTp8TjLpNPUrersHrY,18717
25
+ tensorcode/semantics_bridge.py,sha256=OB9PxMDQd-6uxuPpgooKaYVHBV3bAqRrjEBQvWEr1EA,14044
26
+ tensorcode/social.py,sha256=123-p5sV9EZfpXlSNvpUuUjvVuNs6oB-d2Y680M1BSI,15571
27
+ tensorcode/temporal.py,sha256=TzdXKR0mjT2y4VqWgEzdJHNLwfw3Ea1EFWapiDrEAbs,8131
28
+ tensorcode/wants.py,sha256=PikmA8J4lpmZvBfPVn1erKMwG3AXpUFyvjj4mhgU-A4,7998
29
+ tensorcode/backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ tensorcode/backends/builtin.py,sha256=7PYSON5zIBN8ODewf-2kAPYY0hea7UZkx3nYCPFQxE8,6380
31
+ tensorcode/backends/hf_local.py,sha256=QcEbb1aRG2L32Jf5QwZZ25VnZ6_luMlUbICCBlLw-ag,3859
32
+ tensorcode/backends/linear.py,sha256=SzWO7xSXfeNIZfjAXACCJkSkBT-QxzaiHBExA9hTPCY,5192
33
+ tensorcode/backends/neural.py,sha256=kimyRKZ66u5Gy0n89uGnGt1ZjEJ10lpQqvVK85-K6xY,17664
34
+ tensorcode/language/__init__.py,sha256=gt8RZKBT0V3g06OO4sjJ-STOFV0unnal4HAfiZ6JdxY,1913
35
+ tensorcode/language/chart.py,sha256=yW_J4OVY-fjkcM7wMzgXnTiIWX18850XQeiKUDBszpc,24453
36
+ tensorcode/language/discourse.py,sha256=YMFDI_q-2hKW8THOwED3WpQMwfLSkEIai0lCQSlK-o4,5778
37
+ tensorcode/language/english.py,sha256=5_DubSoy2TY_17NCqL7yTk7qgPxuNuAHlQTgkKkCrUE,30323
38
+ tensorcode/language/features.py,sha256=gEkHNiEwQ2PGP66WIRhB4UPjeHsTjLTbdVXz5Dfc10o,3685
39
+ tensorcode/language/generate.py,sha256=YdeZG_q15bZtpOZ2IjXpMRwKcWPez1Oxo94tyakZFC8,25516
40
+ tensorcode/language/grammar.py,sha256=ohDAc9QjV3VW6SDGLP94DBERdth0Urw3Mmfuo3uDc2Y,38625
41
+ tensorcode/language/semantics.py,sha256=zZbcIo-_diigCV-sNB5sxsTGGM7TEUOA36mzkOZG0z0,14014
42
+ tensorcode/language/domains/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
+ tensorcode/language/domains/desktop.py,sha256=EE5OkwamI42N4K4LOc7G8njuQfWqAVDr0JLfedTpsZI,27227
44
+ tensorcode/learning/__init__.py,sha256=fug2qJJCzkndvGkIEP0Cj_sHnNUGwxPi0yEBntm82X8,1931
45
+ tensorcode/learning/certificate.py,sha256=GO3OIWgHNmtQeKa_ubbzDbcKOpL3FKG6htIBncftLI0,5841
46
+ tensorcode/learning/induce.py,sha256=1Je9ww3LEBYB7455UWYz53VIgBPcLPxsBB4ARkUku3k,12674
47
+ tensorcode/learning/library.py,sha256=1_P7K3hNDArEcqUZCv3OkkzmeMFxeLZwCc6ICbhlzv8,8973
48
+ tensorcode/learning/literals.py,sha256=M2ZNkWc9OsHQ46NsqhQ5S5Zf0zG8ordz9UFQuvSaz0c,5153
49
+ tensorcode/learning/verify.py,sha256=8cZ3th6Ykh1MKq-jjRQGAWTp4PqFlZacRC68pRgGgAY,11216
50
+ tensorcode-0.1.0a1.dist-info/METADATA,sha256=Q6-TmA7oOL24IIWiQgMecILsO7uoKsPKtDFoFZhti1M,8391
51
+ tensorcode-0.1.0a1.dist-info/WHEEL,sha256=THafob7ofN-NsuMN7Mg4qZyHaQI7KkD-QlcQatYhXPo,87
52
+ tensorcode-0.1.0a1.dist-info/licenses/LICENSE,sha256=TiTkvbmxCUbINDzXo4oFwhjC6LR417aOQ8y-wh2pNVM,1074
53
+ tensorcode-0.1.0a1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.3
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023-2026 Jacob Valdez
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.