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/runtime.py ADDED
@@ -0,0 +1,471 @@
1
+ """Binding operations to implementations, and recording what happened.
2
+
3
+ An operation call is a ``Request``. Policy decides which implementations are
4
+ acceptable (hard constraints) and in what order to try them (cascade). Each
5
+ implementation may answer, abstain (``Unknown``), or fail. Every attempt is
6
+ recorded in the active ``Trace``.
7
+
8
+ Unknown quantities stay unknown: a missing cost is never treated as zero, a
9
+ missing quality estimate is never treated as good, and a cost cap excludes
10
+ implementations whose cost is unknown.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import contextlib
16
+ import contextvars
17
+ import hashlib
18
+ import json
19
+ import time
20
+ import uuid
21
+ from dataclasses import asdict, dataclass, field, is_dataclass
22
+ from datetime import datetime, timezone
23
+ from typing import Any, Callable, Iterator, Literal, Mapping, Protocol, Sequence, runtime_checkable
24
+
25
+ from .outcomes import Score, Unknown
26
+
27
+ # ----------------------------------------------------------------- contracts
28
+
29
+
30
+ @dataclass(frozen=True)
31
+ class Request:
32
+ op: str # "parse", "classify", "choose", "rank", "check", "propose"
33
+ subject: Any # the primary input
34
+ target: Any = None # output type, label set, objective, ...
35
+ params: Mapping[str, Any] = field(default_factory=dict)
36
+
37
+
38
+ @dataclass(frozen=True)
39
+ class Output:
40
+ value: Any # an answer or an Unknown (abstention)
41
+ score: Score | None = None
42
+ usd: float | None = None # metered cost of producing this output, if the backend knows it
43
+
44
+
45
+ @dataclass(frozen=True)
46
+ class Failure:
47
+ error: str
48
+
49
+
50
+ @dataclass(frozen=True)
51
+ class Traits:
52
+ """Hard, declared properties used for filtering, never for ranking."""
53
+
54
+ locality: Literal["in_process", "local_service", "remote"] = "in_process"
55
+ egress: bool = False # request data leaves this machine
56
+ deterministic: bool = True # same request, same output (enables caching)
57
+ requires: frozenset[str] = frozenset() # e.g. {"cuda"}, {"sklearn"}
58
+
59
+
60
+ @dataclass(frozen=True)
61
+ class Profile:
62
+ """Measured properties. ``None`` means unmeasured, not zero and not good."""
63
+
64
+ source: str | None = None # the evaluation artifact these numbers came from
65
+ quality: Mapping[str, float] = field(default_factory=dict)
66
+ latency_ms_p50: float | None = None
67
+ latency_ms_p95: float | None = None
68
+ usd_per_call: float | None = None
69
+ peak_memory_mb: float | None = None
70
+
71
+
72
+ @runtime_checkable
73
+ class Implementation(Protocol):
74
+ name: str
75
+ version: str
76
+ op: str
77
+ traits: Traits
78
+ profile: Profile
79
+
80
+ def accepts(self, request: Request) -> bool: ...
81
+
82
+ def run(self, requests: Sequence[Request]) -> Sequence[Output | Failure]: ...
83
+
84
+
85
+ @dataclass
86
+ class FunctionImplementation:
87
+ """Adapts a plain function ``request -> value | Unknown | Output`` (or a batch function)."""
88
+
89
+ op: str
90
+ name: str
91
+ version: str
92
+ fn: Callable[..., Any]
93
+ accepts_fn: Callable[[Request], bool] = lambda request: True
94
+ traits: Traits = Traits()
95
+ profile: Profile = Profile()
96
+ batched: bool = False
97
+
98
+ def accepts(self, request: Request) -> bool:
99
+ return request.op == self.op and self.accepts_fn(request)
100
+
101
+ def run(self, requests: Sequence[Request]) -> list[Output | Failure]:
102
+ raw = self.fn(list(requests)) if self.batched else [self.fn(r) for r in requests]
103
+ return [r if isinstance(r, (Output, Failure)) else Output(r) for r in raw]
104
+
105
+
106
+ def implementation(
107
+ op: str,
108
+ *,
109
+ name: str,
110
+ version: str,
111
+ accepts: Callable[[Request], bool] = lambda request: True,
112
+ traits: Traits = Traits(),
113
+ profile: Profile = Profile(),
114
+ batched: bool = False,
115
+ ) -> Callable[[Callable[..., Any]], FunctionImplementation]:
116
+ def wrap(fn: Callable[..., Any]) -> FunctionImplementation:
117
+ return FunctionImplementation(op, name, version, fn, accepts, traits, profile, batched)
118
+
119
+ return wrap
120
+
121
+
122
+ # -------------------------------------------------------------------- policy
123
+
124
+
125
+ @dataclass(frozen=True)
126
+ class Policy:
127
+ localities: frozenset[str] = frozenset({"in_process", "local_service"})
128
+ allow_egress: bool = False
129
+ available: frozenset[str] = frozenset() # capabilities present on this host
130
+ max_attempts: int = 3 # implementations that may *run* per item
131
+ deadline_ms: float | None = None # per operation call
132
+ max_usd_per_call: float | None = None
133
+ order: Literal["declared", "cheapest", "fastest"] = "declared"
134
+ cache: bool = True
135
+ record_inputs: Literal["full", "digest"] = "full"
136
+
137
+
138
+ class Budget:
139
+ """A shared allowance across many calls (e.g. one agent episode)."""
140
+
141
+ def __init__(self, *, usd: float | None = None, attempts: int | None = None, seconds: float | None = None):
142
+ self.usd, self.attempts = usd, attempts
143
+ self.deadline = time.monotonic() + seconds if seconds is not None else None
144
+ self.spent_usd = 0.0
145
+ self.spent_attempts = 0
146
+ self.unmetered_calls = 0 # calls whose cost is unknown; reported, never summed as zero
147
+
148
+ def refusal(self, profile: Profile) -> str | None:
149
+ if self.deadline is not None and time.monotonic() >= self.deadline:
150
+ return "budget deadline passed"
151
+ if self.attempts is not None and self.spent_attempts >= self.attempts:
152
+ return "budget attempts exhausted"
153
+ if self.usd is not None:
154
+ if profile.usd_per_call is None:
155
+ return "cost unknown under a cost cap"
156
+ if self.spent_usd + profile.usd_per_call > self.usd:
157
+ return "cost would exceed budget"
158
+ return None
159
+
160
+
161
+ def _hard_constraint_violation(impl: Implementation, policy: Policy) -> str | None:
162
+ t = impl.traits
163
+ if t.locality not in policy.localities:
164
+ return f"locality {t.locality} not permitted"
165
+ if t.egress and not policy.allow_egress:
166
+ return "data egress not permitted"
167
+ if missing := t.requires - policy.available:
168
+ return f"missing capability {sorted(missing)}"
169
+ if policy.max_usd_per_call is not None:
170
+ if impl.profile.usd_per_call is None:
171
+ return "cost unknown under a per-call cost cap"
172
+ if impl.profile.usd_per_call > policy.max_usd_per_call:
173
+ return "per-call cost above cap"
174
+ return None
175
+
176
+
177
+ def _ordered(candidates: list[Implementation], policy: Policy) -> list[Implementation]:
178
+ if policy.order == "declared":
179
+ return candidates
180
+ key = "usd_per_call" if policy.order == "cheapest" else "latency_ms_p50"
181
+ # unknown values sort last; they are not assumed cheap or fast
182
+ return sorted(candidates, key=lambda i: (getattr(i.profile, key) is None, getattr(i.profile, key) or 0.0))
183
+
184
+
185
+ # --------------------------------------------------------------------- trace
186
+
187
+
188
+ @dataclass
189
+ class Attempt:
190
+ implementation: str
191
+ version: str
192
+ outcome: Literal["answer", "abstain", "invalid", "error", "skipped", "cache_hit"]
193
+ reason: str = ""
194
+ backend_ms: float | None = None
195
+ usd: float | None = None
196
+ usd_basis: Literal["metered", "profile", "unknown", "none"] = "none"
197
+
198
+
199
+ @dataclass
200
+ class Span:
201
+ id: str
202
+ op: str
203
+ target: str
204
+ input: Any
205
+ input_digest: str
206
+ output: Any = None
207
+ outcome: Literal["answer", "unknown", "event"] = "event"
208
+ attempts: list[Attempt] = field(default_factory=list)
209
+ notes: list[str] = field(default_factory=list)
210
+ started_at: str = ""
211
+ total_ms: float = 0.0
212
+ backend_ms: float = 0.0
213
+ parent: str | None = None
214
+ labels: dict[str, Any] = field(default_factory=dict)
215
+ t0: float = field(default_factory=time.perf_counter, repr=False)
216
+
217
+ def close(self, output: Any, outcome: Literal["answer", "unknown", "event"]) -> Any:
218
+ self.output, self.outcome = output, outcome
219
+ self.total_ms = (time.perf_counter() - self.t0) * 1e3
220
+ return output
221
+
222
+ @property
223
+ def answered_by(self) -> str | None:
224
+ for a in self.attempts:
225
+ if a.outcome in ("answer", "cache_hit"):
226
+ return f"{a.implementation}@{a.version}"
227
+ return None
228
+
229
+
230
+ class Trace:
231
+ def __init__(self) -> None:
232
+ self.spans: list[Span] = []
233
+ self._parent: contextvars.ContextVar[str | None] = contextvars.ContextVar("parent", default=None)
234
+
235
+ @contextlib.contextmanager
236
+ def section(self, name: str, **labels: Any) -> Iterator[Span]:
237
+ span = self.open(name, target="", input=None, labels=labels)
238
+ token = self._parent.set(span.id)
239
+ t0 = time.perf_counter()
240
+ try:
241
+ yield span
242
+ finally:
243
+ span.total_ms = (time.perf_counter() - t0) * 1e3
244
+ self._parent.reset(token)
245
+
246
+ def open(self, op: str, *, target: str, input: Any, labels: Mapping[str, Any] | None = None, digest: str = "") -> Span:
247
+ span = Span(
248
+ id=uuid.uuid4().hex[:12],
249
+ op=op,
250
+ target=target,
251
+ input=input,
252
+ input_digest=digest,
253
+ started_at=datetime.now(timezone.utc).isoformat(timespec="milliseconds"),
254
+ parent=self._parent.get(),
255
+ labels=dict(labels or {}),
256
+ )
257
+ self.spans.append(span)
258
+ return span
259
+
260
+ def of(self, op: str) -> list[Span]:
261
+ return [s for s in self.spans if s.op == op]
262
+
263
+ def to_jsonl(self) -> str:
264
+ return "\n".join(json.dumps(asdict(s), default=_preview) for s in self.spans)
265
+
266
+ def render(self, *, since: int = 0) -> str:
267
+ """One line per span, indented under its section, attempts in order."""
268
+ depth: dict[str | None, int] = {None: 0}
269
+ lines = []
270
+ for s in self.spans[since:]:
271
+ d = depth.get(s.parent, 0)
272
+ depth[s.id] = d + 1
273
+ pad = " " * d
274
+ if s.outcome == "event" and not s.attempts and s.op not in ("invoke", "verify", "choose"):
275
+ labels = " ".join(f"{k}={v}" for k, v in s.labels.items())
276
+ lines.append(f"{pad}{s.op} {labels}".rstrip())
277
+ continue
278
+ out = _preview(s.output, 70)
279
+ lines.append(f"{pad}{s.op:<8} -> {out} [{s.total_ms:.2f} ms total, {s.backend_ms:.2f} ms backend]")
280
+ if s.labels:
281
+ lines.append(f"{pad} · " + " ".join(f"{k}={v}" for k, v in s.labels.items()))
282
+ for a in s.attempts:
283
+ cost = "" if a.usd_basis in ("none", "profile") and not a.usd else f", usd={'?' if a.usd is None else a.usd} ({a.usd_basis})"
284
+ reason = f": {a.reason}" if a.reason else ""
285
+ lines.append(f"{pad} - {a.implementation}@{a.version} {a.outcome}{reason}{cost}")
286
+ for n in s.notes:
287
+ lines.append(f"{pad} · {n}")
288
+ return "\n".join(lines)
289
+
290
+
291
+ def _preview(value: Any, limit: int = 200) -> Any:
292
+ if isinstance(value, Unknown):
293
+ return {"unknown": value.reason, "detail": value.detail}
294
+ if is_dataclass(value) and not isinstance(value, type):
295
+ text = repr(value)
296
+ elif isinstance(value, type):
297
+ text = value.__qualname__
298
+ else:
299
+ text = str(value)
300
+ return text if len(text) <= limit else text[: limit - 1] + "…"
301
+
302
+
303
+ def digest(value: Any) -> str:
304
+ try:
305
+ from .records import _canonical
306
+
307
+ data = json.dumps(_canonical(value), sort_keys=True)
308
+ except Exception: # noqa: BLE001 - unhashable inputs are digested by repr, and flagged
309
+ data = "repr:" + repr(value)
310
+ return hashlib.sha256(data.encode()).hexdigest()[:16]
311
+
312
+
313
+ # ------------------------------------------------------------------- runtime
314
+
315
+
316
+ class Runtime:
317
+ def __init__(
318
+ self,
319
+ implementations: Sequence[Implementation] = (),
320
+ *,
321
+ policy: Policy = Policy(),
322
+ budget: Budget | None = None,
323
+ trace: Trace | None = None,
324
+ ) -> None:
325
+ self.implementations = list(implementations)
326
+ self.policy = policy
327
+ self.budget = budget
328
+ self.trace = trace or Trace()
329
+ self._cache: dict[tuple[str, str, str], Output] = {}
330
+
331
+ def call(self, request: Request, *, validate: Callable[[Any], bool] = lambda v: True, notes: Sequence[str] = ()) -> Output:
332
+ return self.call_many([request], validate=validate, notes=notes)[0]
333
+
334
+ def call_many(
335
+ self, requests: Sequence[Request], *, validate: Callable[[Any], bool] = lambda v: True, notes: Sequence[str] = ()
336
+ ) -> list[Output]:
337
+ t_start = time.perf_counter()
338
+ n = len(requests)
339
+ digests = [digest((r.op, r.subject, _preview(r.target), r.params)) for r in requests]
340
+ spans = [
341
+ self.trace.open(
342
+ r.op,
343
+ target=_preview(r.target),
344
+ input=r.subject if self.policy.record_inputs == "full" else None,
345
+ digest=d,
346
+ )
347
+ for r, d in zip(requests, digests)
348
+ ]
349
+ for s in spans:
350
+ s.notes.extend(notes)
351
+ results: list[Output | None] = [None] * n
352
+ abstentions: list[list[tuple[Any, Score]]] = [[] for _ in range(n)]
353
+ ran = [0] * n
354
+ backend_ms = [0.0] * n
355
+
356
+ candidates = [impl for impl in self.implementations if all(impl.accepts(r) for r in requests)]
357
+ if not candidates:
358
+ for s in spans:
359
+ s.notes.append(f"no implementation accepts op={requests[0].op} target={_preview(requests[0].target)}")
360
+
361
+ for impl in _ordered(candidates, self.policy):
362
+ pending = [i for i in range(n) if results[i] is None and ran[i] < self.policy.max_attempts]
363
+ if not pending:
364
+ break
365
+ refusal = _hard_constraint_violation(impl, self.policy)
366
+ if refusal is None and self.policy.deadline_ms is not None:
367
+ if (time.perf_counter() - t_start) * 1e3 >= self.policy.deadline_ms:
368
+ refusal = "deadline passed"
369
+ if refusal is None and self.budget is not None:
370
+ refusal = self.budget.refusal(impl.profile)
371
+ if refusal:
372
+ for i in pending:
373
+ spans[i].attempts.append(Attempt(impl.name, impl.version, "skipped", refusal))
374
+ continue
375
+
376
+ to_run = []
377
+ for i in pending:
378
+ key = (impl.name, impl.version, digests[i])
379
+ if self.policy.cache and impl.traits.deterministic and key in self._cache:
380
+ cached = self._cache[key]
381
+ if isinstance(cached.value, Unknown):
382
+ spans[i].attempts.append(Attempt(impl.name, impl.version, "abstain", f"cached: {cached.value.reason}"))
383
+ else:
384
+ results[i] = cached
385
+ spans[i].attempts.append(Attempt(impl.name, impl.version, "cache_hit"))
386
+ else:
387
+ to_run.append(i)
388
+ if not to_run:
389
+ continue
390
+
391
+ t0 = time.perf_counter()
392
+ try:
393
+ outs = list(impl.run([requests[i] for i in to_run]))
394
+ if len(outs) != len(to_run):
395
+ raise RuntimeError(f"{impl.name} returned {len(outs)} results for {len(to_run)} requests")
396
+ except Exception as exc: # noqa: BLE001 - a crashing backend is an attempt outcome, not a program crash
397
+ outs = [Failure(f"{type(exc).__name__}: {exc}")] * len(to_run)
398
+ per_item_ms = (time.perf_counter() - t0) * 1e3 / len(to_run)
399
+ if self.budget is not None:
400
+ self.budget.spent_attempts += 1
401
+
402
+ for i, out in zip(to_run, outs):
403
+ ran[i] += 1
404
+ backend_ms[i] += per_item_ms
405
+ usd, basis = self._charge(impl, out)
406
+ attempt = Attempt(impl.name, impl.version, "error", backend_ms=per_item_ms, usd=usd, usd_basis=basis)
407
+ spans[i].attempts.append(attempt)
408
+ if isinstance(out, Failure):
409
+ attempt.reason = out.error
410
+ continue
411
+ if isinstance(out.value, Unknown):
412
+ attempt.outcome, attempt.reason = "abstain", out.value.reason
413
+ abstentions[i].extend(out.value.candidates)
414
+ elif not validate(out.value):
415
+ attempt.outcome, attempt.reason = "invalid", f"output violates contract: {_preview(out.value, 80)}"
416
+ continue # invalid outputs are never cached
417
+ else:
418
+ attempt.outcome = "answer"
419
+ results[i] = out
420
+ if self.policy.cache and impl.traits.deterministic:
421
+ self._cache[(impl.name, impl.version, digests[i])] = out
422
+
423
+ total_ms = (time.perf_counter() - t_start) * 1e3 / n
424
+ final: list[Output] = []
425
+ for i in range(n):
426
+ out = results[i]
427
+ if out is None:
428
+ reasons = [f"{a.implementation}: {a.outcome} ({a.reason})" for a in spans[i].attempts] or spans[i].notes
429
+ ran = [a for a in spans[i].attempts if a.outcome != "skipped"]
430
+ if ran: # the last real attempt's reason is the most specific explanation
431
+ reason = ran[-1].reason.removeprefix("cached: ") if ran[-1].outcome == "abstain" else f"{ran[-1].outcome}"
432
+ else:
433
+ reason = "not_permitted" if spans[i].attempts else "no_implementation"
434
+ out = Output(Unknown(reason, "; ".join(reasons), tuple(abstentions[i])))
435
+ spans[i].output = out.value
436
+ spans[i].outcome = "unknown" if isinstance(out.value, Unknown) else "answer"
437
+ spans[i].total_ms, spans[i].backend_ms = total_ms, backend_ms[i]
438
+ final.append(out)
439
+ return final
440
+
441
+ def _charge(self, impl: Implementation, out: Output | Failure) -> tuple[float | None, Literal["metered", "profile", "unknown"]]:
442
+ if isinstance(out, Output) and out.usd is not None:
443
+ usd, basis = out.usd, "metered"
444
+ elif impl.profile.usd_per_call is not None:
445
+ usd, basis = impl.profile.usd_per_call, "profile"
446
+ else:
447
+ usd, basis = None, "unknown"
448
+ if self.budget is not None:
449
+ if usd is None:
450
+ self.budget.unmetered_calls += 1
451
+ else:
452
+ self.budget.spent_usd += usd
453
+ return usd, basis # type: ignore[return-value]
454
+
455
+
456
+ _current: contextvars.ContextVar[Runtime | None] = contextvars.ContextVar("tensorcode_runtime", default=None)
457
+
458
+
459
+ def current() -> Runtime:
460
+ """The bound runtime, or an empty one: with nothing bound, inferential ops return Unknown."""
461
+ return _current.get() or Runtime()
462
+
463
+
464
+ @contextlib.contextmanager
465
+ def use(runtime: Runtime) -> Iterator[Runtime]:
466
+ """Bind a runtime for the enclosed code. Programs never construct one themselves."""
467
+ token = _current.set(runtime)
468
+ try:
469
+ yield runtime
470
+ finally:
471
+ _current.reset(token)