Metadata-Version: 2.4
Name: metatotai
Version: 0.2.2
Summary: Metacognitive Tree-of-Thought Reasoning Engine
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pydantic>=2.0
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Dynamic: license-file

# metatotai

metatotai is a small Python package for building a reasoning tree that can watch its own uncertainty while it thinks.

The short version:

- Standard prompting asks a model for an answer.
- Chain-of-thought asks a model to show one line of reasoning.
- Tree-of-Thought asks a model to try several reasoning branches.
- metatotai adds a metacognitive layer: it scores branches using signals like prediction error, uncertainty, expected free energy, precision, and traceable path selection.

That means metatotai is not just asking, "What is the best answer?"

It is asking:

- What branches should we explore?
- Which branches reduce uncertainty?
- Which branches look confident but brittle?
- Which path did we choose, and why?
- Can we store a trace so another system can audit the reasoning later?

metatotai began inside the Dionysus cognitive architecture and was extracted into an independent package so it can be used as a standalone reasoning engine.

## Install

```bash
pip install metatotai
```

For the engine quickstart, also make sure `numpy` is available in your environment:

```bash
pip install metatotai numpy
```

The package requires Python 3.10+.

## What problem does metatotai solve?

Large language models are good at producing fluent answers. They are less good at telling you how stable an answer is.

For example, ask a model:

> Should we rewrite this service, patch it, or leave it alone?

A normal answer may sound confident even when the choice is fragile. The model may skip hidden tradeoffs. It may choose the familiar path because that path is easy to explain. It may not preserve enough trace data for a later audit.

metatotai gives you a structure for this kind of problem.

Instead of treating reasoning as one answer, it treats reasoning as a search through possible mental actions. A mental action can be something like:

- explore another option
- challenge the current assumption
- evolve a better version of the plan
- integrate what was learned
- step back and observe the reasoning process itself

Each branch gets a state. That state can carry active-inference-style signals:

- prediction error: how far the branch is from what the system expected
- free energy: a compact pressure signal for mismatch and uncertainty
- expected free energy: how costly or useful a branch may be if followed
- precision: how strongly the system should trust a signal
- entropy: how uncertain a belief state is
- confidence: how strongly the selected path should be treated

This does not make the system magically correct. It makes the reasoning more inspectable. That matters when the cost of a bad answer is high.

## A 6th-grade explanation

Imagine you are trying to get through a maze.

A simple AI answer is like a friend saying, "Go left."

Chain-of-thought is like the friend saying, "Go left because I think the door is there."

Tree-of-Thought is like the friend checking left, right, and straight before choosing.

metatotai is like giving that friend a notebook and a stress meter.

The notebook records every path the friend considered. The stress meter rises when the friend is guessing, confused, or seeing signals that do not match the plan. The friend can then say:

> I checked three paths. This path is not just my favorite. It also has lower uncertainty, fewer bad surprises, and a better trace.

That is the point. metatotai helps a system think in branches, measure uncertainty, and leave a record.

## A software-engineer explanation

metatotai exposes a metacognitive Tree-of-Thought engine with Pydantic models, configurable search settings, trace payloads, and optional host-system adapters.

The core engine lives at:

```python
from metatotai.engine.meta_tot_engine import MetaToTEngine, MetaToTConfig
```

The main models live at:

```python
from metatotai.models.meta_tot import (
    ActiveInferenceState,
    MetaToTDecision,
    MetaToTResult,
    MetaToTTracePayload,
)
```

The standalone-safe path is:

- `use_llm=False`
- `persist_trace=False`
- injected adapters only if you want host integration

When used this way, metatotai can run without the original Dionysus `api.*` package on `sys.path`.

The package also contains optional integration surfaces for systems that provide:

- an LLM provider
- a trace sink
- a world-model provider
- a memory provider
- a thought promotion service
- a decision learner

Those are optional boundaries. The standalone package should import without Dionysus.

## Quickstart: run a tiny reasoning tree

Create a file called `quickstart.py`:

```python
import asyncio

from metatotai.engine.meta_tot_engine import MetaToTConfig, MetaToTEngine


class NoopMemory:
    async def route_memory(self, *args, **kwargs):
        return None


async def main() -> None:
    config = MetaToTConfig(
        use_llm=False,
        persist_trace=False,
        max_depth=2,
        branching_factor=2,
        random_seed=7,
        use_field_based_selection=False,
        use_softmax_selection=False,
    )

    engine = MetaToTEngine(config, memory=NoopMemory())

    result, trace = await engine.run(
        problem="Choose a caching strategy for a slow API endpoint.",
        context={
            "goal": "Reduce latency without serving stale user data.",
            "constraints": [
                "must be safe for user-specific data",
                "should be easy to roll back",
                "risk: stale cache entries",
            ],
            "unknowns": [
                "traffic shape",
                "cache invalidation rules",
            ],
        },
    )

    print("session:", result.session_id)
    print("confidence:", round(result.confidence, 3))
    print("best path length:", len(result.best_path))
    print("metrics:", result.metrics)

    if trace is not None:
        print("selected action:", trace.selected_action)
        print("node count:", len(trace.nodes))
        for node in trace.nodes[:5]:
            print(node.depth, node.cpa_domain, round(node.score, 3), node.thought[:80])


if __name__ == "__main__":
    asyncio.run(main())
```

Run it:

```bash
python quickstart.py
```

What this does:

1. Creates a Meta-ToT engine.
2. Turns off LLM calls so the example is local and repeatable.
3. Turns off trace persistence so no database is needed.
4. Injects a no-op memory adapter so the example does not write to any host memory system.
5. Builds a small reasoning tree.
6. Scores branches.
7. Selects a path.
8. Returns both a result and a trace payload.

## Quickstart: decide whether Meta-ToT should run

Some tasks do not need a reasoning tree. If the question is simple, you may not want the cost of extra search.

The decision service gives you a threshold gate:

```python
from metatotai.engine.meta_tot_decision import MetaToTDecisionService

service = MetaToTDecisionService()

decision = service.decide(
    task="Plan a migration with rollback, data safety, and unclear user impact.",
    context={
        "goal_alignment": 0.4,
        "unknowns": ["schema risk", "traffic timing", "rollback window"],
    },
)

print(decision.use_meta_tot)
print(decision.complexity_score)
print(decision.uncertainty_score)
print(decision.rationale)
```

The service checks signals like:

- task length
- constraint words
- domain words
- explicit unknowns
- goal alignment
- question marks
- optional force/disable flags

You can force or disable Meta-ToT from context:

```python
service.decide("simple task", {"force_meta_tot": True})
service.decide("complex task", {"disable_meta_tot": True})
```

## Why this is different from standard approaches

### Standard prompting

Standard prompting asks for one answer.

That is fast. It is also easy to over-trust.

If the model says, "Use Redis," you may not see the branches it skipped: in-process cache, CDN cache, database index, async job, queue, or no cache at all.

Use standard prompting when:

- the task is low risk
- the answer is easy to check
- you do not need an audit trail

### Chain-of-thought

Chain-of-thought asks for a visible line of reasoning.

That can help, but it is still usually one path. If the first assumption is wrong, the whole chain can look neat while going in the wrong direction.

Use chain-of-thought-like reasoning when:

- you need a short explanation
- the search space is small
- you do not need branch comparison

### Tree-of-Thought

Tree-of-Thought creates multiple branches and chooses between them.

That is closer to how real planning works. But many Tree-of-Thought systems still score branches with simple heuristics, model votes, or final-answer quality.

Use Tree-of-Thought when:

- several options may be valid
- the first answer is likely incomplete
- branch comparison matters

### MCTS-style search

Monte Carlo Tree Search and related methods explore a tree by simulating outcomes. They are strong when you have a clear reward signal, a game-like environment, or a simulator.

Many real reasoning tasks do not have that. There may be no clean win/loss reward. The value of a branch may depend on uncertainty, context, identity, goals, and future flexibility.

Use MCTS-style search when:

- you can simulate outcomes
- you have a clear reward function
- repeated rollout is cheap enough

### ReAct-style agents

ReAct systems mix reasoning with tool use. They think, act, observe, and continue.

That is useful for web tasks, coding tasks, and agent loops. But ReAct does not automatically give you a strong model of uncertainty or a clean policy-selection trace.

Use ReAct when:

- the system must use tools
- the world can answer back
- observation after action is central

### metatotai

metatotai is for problems where the system needs to choose a reasoning path under uncertainty.

It is not only about generating branches. It is about treating branches as possible policies and asking which policy reduces uncertainty or expected cost.

Use metatotai when:

- the task is complex or uncertain
- you need branch-level trace data
- you care about why one path was selected
- you want active-inference-style scoring signals
- you may later plug the engine into a larger cognitive system

## Active inference, explained simply

Active inference is a theory of how living systems act and learn.

The simple version:

A system has a model of the world. It expects certain things to happen. When reality does not match the model, the system gets prediction error.

The system can reduce that error in two ways:

1. Update its beliefs.
2. Act on the world to make observations fit better.

A thermostat is a simple example.

It expects the room to be 70 degrees. If the room is 65 degrees, there is an error. The thermostat can turn on the heat. When the room gets closer to 70, the error goes down.

A mind is more complex, but the same basic idea applies:

- It predicts.
- It notices mismatch.
- It updates beliefs.
- It acts.
- It tries to reduce future mismatch.

In active inference language, this pressure is often called free energy.

Do not let the term scare you. In this package, you can think of free energy as a useful warning light:

> This branch may be costly because it leaves too much mismatch, uncertainty, or surprise.

## Expected free energy

Expected free energy is about the future.

It asks:

> If I follow this action or reasoning path, how much uncertainty or mismatch do I expect later?

A branch with lower expected free energy is often more attractive because it may lead to a clearer, safer, or more useful state.

But there is a balance.

Sometimes a branch is useful because it explores. It may not give the fastest answer, but it may reduce important uncertainty. This is why active inference often cares about both:

- pragmatic value: does this help me get what I want?
- epistemic value: does this help me learn what I need to know?

A good reasoning system needs both.

## Precision

Precision means trust or confidence in a signal.

If a smoke alarm is reliable, you treat it as high precision. If it goes off every time someone makes toast, you treat it as lower precision.

In metatotai, precision-like values can shape the search. High precision can make selection sharper. Low precision can make the system explore more broadly.

This matters because not every problem should be searched the same way.

When the system is clear and confident, narrow search may be enough.

When the system is uncertain, wider search may be safer.

## POMDPs, explained without the fog

A POMDP is a Partially Observable Markov Decision Process.

That name sounds bigger than the idea.

It means:

- There is a world state.
- You cannot see the whole state directly.
- You only get observations.
- You have beliefs about what is going on.
- You can take actions.
- Actions change what may happen next.
- You choose actions based on beliefs, not perfect knowledge.

A doctor diagnosing a patient is working in a POMDP-like situation. The doctor cannot directly see every hidden process in the body. They see symptoms, labs, history, and response to treatment. They hold beliefs. They choose tests or treatments. Each action changes what they know next.

A software engineer debugging production is also in a POMDP-like situation. You cannot see the whole system state. You see logs, metrics, traces, alerts, and user reports. You choose actions: inspect a service, roll back, add logging, restart a job, or patch code.

metatotai is not a full POMDP solver. It does not require you to define formal transition matrices, observation matrices, or reward tables.

But it borrows the useful framing:

- reasoning branches are possible policies
- each branch has a state
- observations update uncertainty
- selection should care about future uncertainty, not just current score

## How metatotai maps these ideas into code

Here is the practical mapping:

| Concept | Plain meaning | metatotai object or field |
| --- | --- | --- |
| Problem | The thing you are trying to reason about | `problem` passed to `engine.run(...)` |
| Context | Extra facts, constraints, goals, unknowns | `context` dict |
| Policy | A possible reasoning path or mental action | branch/node path |
| Belief state | What the system currently thinks | `ActiveInferenceState.beliefs` |
| Prediction error | How wrong or surprised the system is | `prediction_error` |
| Free energy | Mismatch/uncertainty pressure | `free_energy` |
| Expected free energy | Estimated future cost/uncertainty | `expected_free_energy` |
| Precision | How strongly to trust a signal | `precision_*`, `attentional_precision` |
| Trace | Record of the reasoning run | `MetaToTTracePayload` |
| Result | Final selected path and metrics | `MetaToTResult` |

## What a trace gives you

A trace is the audit trail.

It can show:

- the session id
- the selected action
- the best path
- branch count
- confidence
- processing time
- prediction error
- free energy
- every recorded node
- each node's depth, type, score, and parent

This is useful when you need to debug reasoning, show why an answer was chosen, or feed the trace into a larger system.

## Configuration reference

`MetaToTConfig` controls the search:

| Field | Meaning | Default |
| --- | --- | --- |
| `max_depth` | How many levels deep the tree can grow | `4` |
| `simulation_count` | Placeholder budget for simulation-style workflows | `32` |
| `exploration_constant` | Exploration pressure for search-like policies | `2.0` |
| `branching_factor` | How many candidates to keep per expansion | `3` |
| `time_budget_seconds` | Soft time budget recorded in metrics | `5.0` |
| `use_llm` | Whether to ask an LLM or host model provider for branches | `True` |
| `llm_model` | Model name passed to the host LLM provider | `gpt-5-nano` |
| `persist_trace` | Whether to store traces through the trace service | `True` |
| `random_seed` | Seed for Python `random` | `None` |
| `selection_temperature` | Softmax temperature | `1.0` |
| `use_softmax_selection` | Use softmax path selection when field selection is off | `True` |
| `use_field_based_selection` | Use field-dynamics-inspired path selection | `True` |
| `habitual_bias` | Small boost for routine/default-style branches | `0.2` |

For standalone local examples, start with:

```python
MetaToTConfig(
    use_llm=False,
    persist_trace=False,
    max_depth=2,
    branching_factor=2,
)
```

Then add host adapters only when you need them.

## Adapter pattern

metatotai can be used alone, but it was designed to sit inside larger systems.

Instead of hard-binding the package to Dionysus internals, the engine accepts optional adapter-like objects:

```python
engine = MetaToTEngine(
    config,
    llm=my_llm_provider,
    trace_sink=my_trace_sink,
    world_model=my_world_model,
    memory=my_memory_provider,
    thoughtseed_promoter=my_promoter,
    decision_learner=my_decision_learner,
)
```

This lets you decide what the engine can touch.

If you do not provide those adapters, the standalone-safe path uses local fallbacks where possible. Some advanced paths still need host services.

If your Python process also has Dionysus `api.*` modules on `sys.path`, metatotai may discover those host adapters. For a strict local tutorial run, inject explicit no-op adapters for any side effect you do not want, especially memory routing.

## Example: a small custom trace sink

If you want to keep traces without Graphiti or another database, provide a tiny trace sink:

```python
import asyncio

from metatotai.engine.meta_tot_engine import MetaToTConfig, MetaToTEngine


class InMemoryTraceSink:
    def __init__(self) -> None:
        self.traces = {}

    async def store_trace(self, payload):
        self.traces[payload.trace_id] = payload
        return payload.trace_id


class NoopMemory:
    async def route_memory(self, *args, **kwargs):
        return None


async def main() -> None:
    sink = InMemoryTraceSink()
    engine = MetaToTEngine(
        MetaToTConfig(
            use_llm=False,
            persist_trace=True,
            max_depth=1,
            use_field_based_selection=False,
            use_softmax_selection=False,
        ),
        trace_sink=sink,
        memory=NoopMemory(),
    )

    result, trace = await engine.run(
        "Pick a rollout plan for a risky migration.",
        {"constraints": ["rollback required", "customer data safety"]},
    )

    print(result.trace_id)
    print(list(sink.traces.keys()))


asyncio.run(main())
```

## Example: API router usage

The package includes a FastAPI router module:

```python
from fastapi import FastAPI
from metatotai.routers.meta_tot import router as meta_tot_router

app = FastAPI()
app.include_router(meta_tot_router)
```

Endpoints:

- `POST /api/meta-tot/run`
- `GET /api/meta-tot/traces/{trace_id}`

The router is meant for host applications. If your host does not provide the needed cognition boundary services, the router may return a service-unavailable style error for those external boundaries.

## When to use metatotai

Use it when the reasoning task has at least one of these features:

- high uncertainty
- multiple valid paths
- hidden tradeoffs
- a need for an audit trail
- a need to choose between mental actions
- a need to compare exploration vs exploitation
- a larger system that can use traces later

Good examples:

- architecture tradeoff analysis
- migration planning
- debugging strategy selection
- agent policy selection
- complex product or research decisions
- deciding whether a task needs deeper reasoning

Poor examples:

- formatting a date
- answering a simple fact
- doing arithmetic
- producing a one-off low-risk sentence
- anything where a direct answer is cheaper and safer

## Current status

v0.2.2 provides:

- standalone package boundary cleanup
- runtime `metatotai.__version__` metadata aligned with the published package version
- metacognitive Tree-of-Thought engine
- active-inference state model
- decision gate service
- trace payload models
- optional adapter boundaries
- local no-LLM mode for basic runs

The package is still young. Treat it as an engine layer, not a finished end-user app.

## Development

Clone the repo:

```bash
git clone https://github.com/bionicbutterfly13/metatotai.git
cd metatotai
```

Install for local development:

```bash
python -m pip install -e '.[dev]'
python -m pip install numpy
```

Run the standalone boundary tests:

```bash
python -m compileall -q src tests
python -m pytest tests/test_standalone_boundary.py -q
```

Build the package:

```bash
python -m build
python -m twine check dist/metatotai-*
```

## Design principle

metatotai is built around one idea:

Reasoning should not only produce an answer. It should preserve enough structure to show how the answer was selected.

That structure is what lets a system improve later.

## License

Apache-2.0.
