Metadata-Version: 2.5
Name: weftai
Version: 0.6.0
Summary: Composable AI workflows: declarative multi-step plans a model emits and your application validates and executes.
Project-URL: Homepage, https://github.com/tochi-mba/weftai
Project-URL: Documentation, https://github.com/tochi-mba/weftai/tree/main/docs
Author: REX Technologies
License-Expression: MIT
License-File: LICENSE
Keywords: agents,ai,declarative,function-calling,llm,mcp,plans,tool-calling,workflow
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: pydantic<3,>=2.10
Provides-Extra: all
Requires-Dist: weftai-cli==0.6.0; extra == 'all'
Requires-Dist: weftai-mcp==0.6.0; extra == 'all'
Requires-Dist: weftai-providers==0.6.0; extra == 'all'
Description-Content-Type: text/markdown

# weftai

Composable AI workflows. A model emits a declarative plan of named steps; your application
validates and executes it; results flow between steps by `$name` and never travel through the
model.

One tool call answers a question that would otherwise take several, and the model never copies
an identifier from one result into the next request.

```sh
pip install weftai
```

Python 3.12 or newer; pure Python, tested on Linux and Windows. The same library is on npm as
[`weftai`](https://www.npmjs.com/package/weftai), with the same plan format and the same
model-facing text.

## Quick start

Define a collection and the operations that return it, then execute a plan:

```python
import asyncio
from dataclasses import dataclass

from weftai import (
    BaseModel,
    FieldSpec,
    collection,
    create_registry,
    create_runtime,
    define_operation,
    standard_operations,
)
from weftai.schema.spec import object_schema


class Ticket(BaseModel):
    id: str
    title: str
    status: str


Tickets = collection(
    "tickets",
    Ticket,
    label=lambda ticket: ticket.title,
    key=lambda ticket: ticket.id,
    fields=lambda _ctx: [FieldSpec("status", lambda ticket: ticket.status)],
)

find = define_operation(
    {
        "name": "tickets.find",
        "description": "Every ticket. Start here.",
        "input": object_schema({}),
        "output": Tickets,
        "run": lambda run: run.ctx.tickets,
    }
)

runtime = create_runtime(
    {"registry": create_registry({"operations": [find, *standard_operations(Tickets)]})}
)


@dataclass
class Context:
    tickets: list[Ticket]


tickets = [
    Ticket(id="t1", title="Login page times out", status="open"),
    Ticket(id="t2", title="Export drops the last row", status="open"),
    Ticket(id="t3", title="Typo on the pricing page", status="closed"),
]


async def main() -> None:
    result = await runtime.execute(
        {
            "steps": [
                {"id": "all", "op": "tickets.find"},
                {
                    "id": "open",
                    "op": "tickets.filter",
                    "input": {"from": "$all", "filters": [{"field": "status", "value": "open"}]},
                },
            ]
        },
        {"ctx": Context(tickets), "session": {"id": "conversation-1"}},
    )
    print(result["text"])


asyncio.run(main())
```

`result["text"]` is what the model reads:

```text
all (tickets): 3 matched
  1. Login page times out
  2. Export drops the last row
  3. Typo on the pricing page
  [intermediate step - preview only; reference $all to use the full set]
open (tickets): 2 matched
  1. Login page times out
  2. Export drops the last row
```

`result["steps"]` has each step's status, data, notices and timing, and `result["trace"]` is
JSON for the CLI or an inspector. A later plan in the same session can use `$open` or
`$open[2]`: results are kept in a session-scoped store, and positions always index the full set.

## The plan a model writes

A plan is a list of steps. Each has an `id`, an operation `op`, and an `input`; a string such as
`"$all"` in a field that takes a reference is the result of an earlier step, resolved by the
runtime rather than copied by the model:

```json
{ "steps": [
  { "id": "all",  "op": "tickets.find" },
  { "id": "open", "op": "tickets.filter", "input": { "from": "$all", "filters": [{ "field": "status", "value": "open" }] } }
] }
```

## What you get from one definition

- **Validation with actionable errors.** An unknown operation suggests the nearest name, an
  unknown field lists the available ones, and a reference to the wrong kind of result says which
  kind was expected. A wrong question is an error, never an empty result.
- **Typed references.** `ref(Tickets)` renders as a `$stepId` string for the model and arrives in
  the handler as a collection. A whole reference written into a field that does not take one is
  refused before anything runs.
- **Execution in dependency order.** Independent steps run together, a failed step skips only
  its dependents, and timeouts and cancellation are built in.
- **Token-budgeted formatting** that never truncates silently: exact counts, `showing 30 of 35`,
  and a notice for every cap that applied.
- **Standard operations** for every collection: `filter`, `count`, `countBy`, `distinct`,
  `mostCommon`, `first`, `pick` and `details`.
- **Decisions.** A `Decider` answers short, typed questions (a choice, a score, a yes or no)
  with probabilities; `NullDecider` abstains, and `weftai-providers` has an HTTP one.

## Packages

| Package | Import | Purpose |
| --- | --- | --- |
| [`weftai`](https://pypi.org/project/weftai/) | `weftai` | Operations, plans, validation, execution, the result store, formatting and traces |
| [`weftai-providers`](https://pypi.org/project/weftai-providers/) | `weftai.providers` | Present a runtime as a tool on OpenAI, Anthropic, Gemini, Bedrock, Ollama, Cohere and more |
| [`weftai-mcp`](https://pypi.org/project/weftai-mcp/) | `weftai.mcp` | Serve a runtime over the Model Context Protocol |
| [`weftai-cli`](https://pypi.org/project/weftai-cli/) | `weftai.cli` | The `weftai` command: `run`, `validate`, `describe`, `trace`, `mcp` and `init` |
| `weftai-testing` | `weftai.testing` | A test runtime, assertions and a pytest plugin |

`pip install "weftai[all]"` installs the first four. All five are released together and share
a version. The concepts, plan format and formatting rules are described in the
[weftai guides](https://github.com/tochi-mba/weftai/tree/main/docs), written for the npm
package; the Python API spells each name in snake_case.

## License

MIT
