Metadata-Version: 2.4
Name: conjured
Version: 0.1.0
Summary: Conjured engine — a Python engine for handler composition with pipeline-as-training-contract derivation.
License-Expression: Apache-2.0
Keywords: handler-composition,pipeline,typed-dataflow,training-contract,dispatch
Classifier: Development Status :: 3 - Alpha
Classifier: Typing :: Typed
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
Requires-Dist: pydantic<3,>=2
Provides-Extra: server
Requires-Dist: starlette<2,>=1; extra == "server"
Requires-Dist: uvicorn<1,>=0.30; extra == "server"
Requires-Dist: sse-starlette<4,>=3; extra == "server"
Provides-Extra: compilers
Requires-Dist: jinja2<4,>=3.1; extra == "compilers"
Requires-Dist: jsonschema<5,>=4.26; extra == "compilers"
Provides-Extra: dev
Requires-Dist: pytest<9,>=8; extra == "dev"
Requires-Dist: httpx>=0.27; extra == "dev"
Requires-Dist: conjured[server]; extra == "dev"
Requires-Dist: conjured[compilers]; extra == "dev"
Dynamic: license-file

# Conjured

**A Python engine for handler composition with pipeline-as-training-contract derivation.**

Conjured composes handlers into a **typed dataflow graph** — handlers are nodes, and the
state each reads and writes are typed channels between them. The engine type-checks the
whole graph at compose time and dispatches the handlers in declared order at runtime.

Its one load-bearing idea: a composed pipeline is **simultaneously the runtime contract
and the training-data shape**. The schemas that validate a channel at runtime are the same
types that define the training-record shape — not two contracts kept in sync, but one graph
queried two ways. Edit the composition and the training contract re-derives. Everything else
maps to familiar, field-named patterns; this collapse-by-construction is the novel part.

Handlers are ordinary kwargs-in / dict-out functions — they never see a shared mutable
context, and the runner is the sole writer of channel state. That purity is what makes a run
replayable, and replayability is what lets the training corpus be a faithful derived view of
the graph.

## Status

**0.1.0 — alpha.** The engine core is complete and verified: the compose-time validator, the
two-hash integrity scheme, the dispatch runner, the canonical event log, the native trainable
backends, and the server/client surfaces are in place and covered by 962 passing tests. The
documentation surfaces and how-to guides are still in progress, and the public API may change
before 1.0. The full canonical specification ships in the source distribution under `docs/`.

## Install

```sh
pip install conjured
```

The engine core depends only on `pydantic`. Two optional extras pull in backend stacks that
are lazily imported and raise a clear `ImportError` naming the extra when absent:

```sh
pip install "conjured[server]"     # the HTTP + SSE wire surface (Starlette / Uvicorn)
pip install "conjured[compilers]"  # the jinja / json_schema compile affordances
```

## A first pipeline

A pipeline is composed from handlers you declare in TOML. Here is a one-node pipeline whose
single transform handler reads a `name` channel and writes a `greeting`.

The handler is an ordinary function in an importable module, `greet.py`:

```python
def greet(*, name):
    return {"greeting": f"Hello, {name}!"}
```

Compose it into a pipeline, type-check it, and run it:

```python
from conjured.runner import assemble, run
from conjured.validator import DeclarationRegistry, compile_pipeline, loads

# A transform handler's declaration: it reads a `name` channel and writes a `greeting`.
HANDLER_TOML = """
[transform]
[reads]
name = { type = "str" }
[output_schema]
greeting = { type = "str" }
"""

# A pipeline: one node, wired to the pipeline's `name` input and `greeting` output.
PIPELINE_TOML = """
[meta]
name = "demo.hello"
[[nodes]]
kind = "handler"
name = "greet.greet"
[inputs]
name = { type = "str" }
[outputs]
greeting = { type = "str" }
"""

registry = DeclarationRegistry()
registry.add_handler("greet.greet", loads(HANDLER_TOML, "handler", file_path="greet.toml"),
                     toml_path="greet.toml")

pipeline = loads(PIPELINE_TOML, "pipeline", file_path="pipeline.toml")
graph = compile_pipeline(pipeline, registry, pipeline_name="demo.hello", file_path="pipeline.toml")
runnable = assemble(graph, registry)

result = run(runnable, {"name": "world"})
print(result.state["greeting"])  # -> Hello, world!
```

`compile_pipeline` runs the full compose-time type-check — a mismatched channel type or an
unresolvable handler raises `ContractViolation` before any handler dispatches. `run` walks the
graph in declared order and returns a `RunResult` whose `state` carries the written channels.

## Documentation

The full canonical specification — the architecture, the handler and pipeline references, the
hash and trust models, and the error channel — ships in the source distribution under `docs/`.
The `docs/explanation/overview.md` page is the recommended entry point.

## License

Apache-2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE).
