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"

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=...).

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.2.0.tar.gz (16.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.2.0-py3-none-any.whl (17.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: thinair-0.2.0.tar.gz
  • Upload date:
  • Size: 16.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.2.0.tar.gz
Algorithm Hash digest
SHA256 578818c5344fb61b03b9db447a196a52120ab9037c1d54f333082afa4c1d2b54
MD5 cb5188a63b365a789af63668d78ce15c
BLAKE2b-256 9241c93ec40561818084f62caffcfa7ab6fc0d670563ddde4457c443654f93e7

See more details on using hashes here.

File details

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

File metadata

  • Download URL: thinair-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 17.7 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.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f42798f3c6e8b4ebdd0dded75fef93624cd7551e08d0487b1b44aa027b2366c8
MD5 3a0e0e792b39eb1ab2f73e2195fa4c9a
BLAKE2b-256 c102fa2bfaf19c3492a0c1159b5b87dcab765c5649b273a68a305e4be4c2a2f3

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