Skip to main content

Probabilistic Python objects: invent attributes, methods, anything out of thin air. An LLM fills the silence, every answer carries confidence, and written code always wins.

Project description

thinair

Probabilistic Python objects. Invent attributes, methods, anything out of thin air; an LLM of your choice (local or hosted) fills in the blanks, with confidence attached.

Code the certain, imagine the rest.

One axiom: an object is a story, and every interaction is a continuation of it. Everything else falls out — see SPEC.md for the full spec.

A Thing in sixty seconds

A Thing is any value with a probability, made from words:

from thinair import Thing

spider = Thing("the number of legs on a spider")

+spider                  # "the number of legs on a spider" — your own words
                         # back; free, no inference
~spider                  # 1.0 — your words are certain

legs = spider @ int      # collapsing happens through typing: one inference
                         # call, and now legs is a Thing carrying 8
+legs                    # 8 — the value, a real int
~legs                    # 0.99 — the probability, a bare float

+ takes the value, ~ takes the probability, @ shapes the Thing — and only @ <type> ever costs inference. Requirements chain, and an unmet one drops the value but keeps the probability, so failures explain themselves:

+(spider @ int @ 0.8)          # 8 — typed AND vouched for at p >= 0.8

car = Thing("a rusty 1990 Toyota Hilux")   # attributes you never defined
guess = car.price_eur @ float @ 0.9        # are imagined on first read —
                                           # so this is a typed, gated guess
+guess                         # None — didn't clear the bar...
~guess                         # 0.1 — ...and this is why
if guess: ...                  # failed Things are falsy: gate whole branches

The type operand scales from primitives through schemas to real classes:

movie = Thing("the Ridley Scott movie with the xenomorph")

+(movie @ {"title": str, "year": int})   # {'title': 'Alien', 'year': 1979}
                                         # — a schema-guaranteed dict

@dataclass
class Record:
    title: str
    year: int

+(movie @ Record)        # Record(title='Alien', year=1979) — the model
                         # imagines the kwargs, YOUR constructor builds it

You can assert your own doubt, and comparisons happen in Thing space — they are judgments, not byte compares:

price = Thing(19_990, confidence=0.4)    # lift a belief into Thing space
+(price @ 0.5)                           # None — you said so yourself

Thing("a car") < Thing("a cat")          # False (p 0.75) — an imagined judgment
+car.price < 20_000                      # take the value out first for plain,
                                         # free Python semantics

Deterministic meets probabilistic

Subclass Thing and write the parts you're sure of. Written code and bare values are the certain skeleton — they run as ordinary Python, cost nothing, and the model can never touch them. Everything else is imagined on demand:

class Car(Thing):
    """A road vehicle."""
    wheels = 4                      # certain by definition

    def horn(self):                 # real code: runs in CPython,
        return "beep"               # inference is never consulted

car = Car("a rusty 1990 Toyota Hilux, engine coughs, "
          "radio stuck on a Finnish schlager station")

car.wheels               # 4 — bare int; no inference ran, nothing was billed
car.horn()               # "beep" — real code, really executed
+car.color               # "brown" — imagined; here the class was silent
~car.color               # 0.1 — and honestly unsure about it

car.owner = "Miska"                  # bare assignment: authoritative, and
                                     # locked — no plan may overwrite it
car.mood = Thing("unknown so far")   # a slot the model MAY manage

Provenance is permission: bare values belong to the programmer, Thing values belong to the imagination.

Call anything

Any method you never wrote is imagined at call time — and it acts: it reads state, writes state (with confidence, journaled), and calls your real methods, which actually execute. Results are Things, so everything chains:

problems = car.list_your_problems(returns=[str])
+problems                # ['Engine is coughing', 'Radio is stuck on a
                         #  Finnish schlager station', 'High rust level']

car.repair_engine()      # no such method — a plan is imagined and ACTED
                         # out; the story now contains the repair

car.diagnose().severity_of_worst_issue()   # results are Things: chain
                                           # imagined calls on imagined calls

Guard the branches that matter: inside with Thing.require(0.9): any resolution below 0.9 raises Thing.LowConfidence instead of flowing:

with Thing.require(0.9):
    if car.can_drive():      # p 0.93 — clears the bar, the branch is trusted
        plan_road_trip(car)
    car.vin_number           # p 0.02 — a guess this wild now raises
                             # Thing.LowConfidence instead of flowing onward

Talk to it

There is no chatbot framework here — and chat is not a built-in either: nobody wrote it, it's imagined at call time like any other missing method. The car is already a chatbot, because a conversation is just more story:

while True:
    print(car.chat(input("> ")))   # `chat` appears out of thin air too
> Why did you break down on me this morning?
Look, mate, it's a 1990 Hilux. The rust is high, the engine was coughing
its guts out, and frankly, I was just trying to listen to some good
Finnish schlager while falling apart.

Every turn is journaled, so the car remembers what you said — and objects can size each other up the same way, by handing Things to a Thing:

customer = Thing("a retired couple with a caravan and two dogs, modest budget")

pick = customer.prefers(sedan, suv, roadster,
                        returns={"choice": str, "why": str})
pick.choice      # "a full-size SUV: seven seats, tow hook, thirsty"
pick.why         # "The caravan requires a tow hook, which only the SUV
                 #  has, and it provides necessary space for the two dogs."

Objects are documents

car.__getstate__() is a JSON-able blob — description, state, story, flags; no code, no weights, no client. blob @ Car casts it back to life with written methods reattached; pickle just works. And __source__ renders any object as the class it currently is:

print(car.__source__)
class Car(Thing):
    """
    A road vehicle.

    a rusty 1990 Toyota Hilux, engine coughs, radio stuck on a Finnish schlager station
    """

    wheels = 4

    owner = 'Miska'  # written (p = 1.0)
    color = 'brown'  # imagined (p = 0.10)
    top_speed_kmh = 130  # imagined (p = 0.10)

    def horn(self):
        return "beep"

__story__ is the other lens: the full journal of every event, answer, and imagined step, in order — consistency and provenance for free.

Setup

pip install thinair

(thinair on PyPI) — no dependencies, one file, stdlib only. Point it at any OpenAI-compatible endpoint (defaults target a local server):

export THINAIR_BASE_URL="http://127.0.0.1:8000/v1"   # default
export THINAIR_API_KEY="1234"
export THINAIR_MODEL="Qwen3.6-35B-A3B-oQ6-mtp"
export THINAIR_MAX_TOKENS=32768                      # answer budget; also caps total thinking
export THINAIR_THINK_CHUNK=2048                      # thinking window size (0 = single shot)

Requests ask for the server's JSON output mode (response_format: json_object) and quietly fall back to freeform if the server doesn't support it. Reasoning models think in fixed windows of THINAIR_THINK_CHUNK tokens; every checkpoint is a wake-up where the model is nudged to answer unless more thought is truly necessary. Thinking that degenerates into verbatim repetition is pruned, the cut loop is named to the model, and a reply it kept rehearsing inside the loop is harvested as the answer. An answer cut mid-way gets a completion grant sized from the draft — never the whole budget in one shot — and a model that only keeps thinking eventually gets a clear budget error, so runaway generation can never capture the budget.

or in code: Thing.defaults(model="...", base_url="...", api_key="..."). A URL, a provider object with complete(messages) -> text, or a bare callable all work per instance too: Thing("a car", model=...).

To see what's actually happening underneath, wrap any block in with Thing.debug(): — every prompt and raw completion is dumped to stderr, labeled by operation (read, imagine, judge, collapse). THINAIR_DEBUG=1 turns it on globally.

Then:

python car_chat.py     # talk to a rusty Hilux — `chat` is imagined, not written

Status

An experiment. Every unresolved attribute costs an inference call; answers are as good as your model. That's the fun part.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

thinair-0.4.0.tar.gz (33.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

thinair-0.4.0-py3-none-any.whl (34.8 kB view details)

Uploaded Python 3

File details

Details for the file thinair-0.4.0.tar.gz.

File metadata

  • Download URL: thinair-0.4.0.tar.gz
  • Upload date:
  • Size: 33.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for thinair-0.4.0.tar.gz
Algorithm Hash digest
SHA256 0526f3c855b50ca1a93cbb3deae126d965fc8a2d8e42eb381918d729e051edaa
MD5 37e285670505532b5e9ac1dd74ea372a
BLAKE2b-256 ed09257e9838255ab6c6a881175bb02e66fb3db38878181b8b83ccf11c19a9e9

See more details on using hashes here.

File details

Details for the file thinair-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: thinair-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 34.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for thinair-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e272269218df04242f9d91dddd12535581165eccb83ff3b92a79b3004989d975
MD5 282851119bf2917403a3a4b3123e3509
BLAKE2b-256 0277466b93bc7a775d8f24b4c0449aea23acfa2e4c0fdb902dfbc583c9578365

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page