Metadata-Version: 2.5
Name: operonx
Version: 1.22.1
Summary: High-performance workflow engine for AI applications
Project-URL: Homepage, https://github.com/batman1m2001-cyber/Operonx
Project-URL: Documentation, https://batman1m2001-cyber.github.io/Operonx/
Project-URL: Repository, https://github.com/batman1m2001-cyber/Operonx
Project-URL: Changelog, https://github.com/batman1m2001-cyber/Operonx/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/batman1m2001-cyber/Operonx/issues
Author: Operon Team
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: ai,async,dag,llm,orchestration,pipeline,workflow
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: orjson>=3.9
Requires-Dist: pydantic>=2.0
Requires-Dist: pyyaml>=6.0.3
Requires-Dist: rich>=13.0
Requires-Dist: tomli>=2.0; python_version < '3.11'
Provides-Extra: all
Requires-Dist: aiohttp>=3.8; extra == 'all'
Requires-Dist: boto3>=1.28; extra == 'all'
Requires-Dist: clickhouse-connect>=0.7; extra == 'all'
Requires-Dist: faiss-cpu>=1.8; extra == 'all'
Requires-Dist: fastapi>=0.100; extra == 'all'
Requires-Dist: google-cloud-aiplatform>=1.38; extra == 'all'
Requires-Dist: httpx>=0.24; extra == 'all'
Requires-Dist: langfuse<3.0.0,>=2.6.0; extra == 'all'
Requires-Dist: numpy>=2.2.6; extra == 'all'
Requires-Dist: onnxruntime<1.20,>=1.15; extra == 'all'
Requires-Dist: openai>=1.0; extra == 'all'
Requires-Dist: psycopg-pool>=3.2; extra == 'all'
Requires-Dist: psycopg[binary]>=3.1; extra == 'all'
Requires-Dist: pymongo>=4.6; extra == 'all'
Requires-Dist: qdrant-client>=1.9; extra == 'all'
Requires-Dist: requests>=2.32; extra == 'all'
Requires-Dist: tokenizers>=0.13; extra == 'all'
Requires-Dist: tritonclient[grpc]>=2.41; extra == 'all'
Requires-Dist: uvicorn[standard]>=0.20; extra == 'all'
Requires-Dist: websockets>=11.0; extra == 'all'
Provides-Extra: anthropic
Requires-Dist: httpx>=0.24; extra == 'anthropic'
Requires-Dist: openai>=1.0; extra == 'anthropic'
Provides-Extra: bedrock
Requires-Dist: boto3>=1.28; extra == 'bedrock'
Requires-Dist: httpx>=0.24; extra == 'bedrock'
Requires-Dist: openai>=1.0; extra == 'bedrock'
Provides-Extra: clickhouse
Requires-Dist: clickhouse-connect>=0.7; extra == 'clickhouse'
Provides-Extra: dev
Requires-Dist: clickhouse-connect>=0.7; extra == 'dev'
Requires-Dist: httpx>=0.24; extra == 'dev'
Requires-Dist: hypothesis>=6.100; extra == 'dev'
Requires-Dist: mongomock>=4.1; extra == 'dev'
Requires-Dist: pre-commit; extra == 'dev'
Requires-Dist: pymongo>=4.6; extra == 'dev'
Requires-Dist: pytest-asyncio>=1.3.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
Requires-Dist: pytest-timeout>=2.4.0; extra == 'dev'
Requires-Dist: pytest>=9.0.2; extra == 'dev'
Requires-Dist: python-dotenv>=1.2.2; extra == 'dev'
Requires-Dist: ruff>=0.16; extra == 'dev'
Requires-Dist: tomli>=2.0; (python_version < '3.11') and extra == 'dev'
Requires-Dist: xmlschema>=3.0; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
Requires-Dist: mkdocs>=1.6; extra == 'docs'
Requires-Dist: mkdocstrings[python]>=0.27; extra == 'docs'
Provides-Extra: faiss
Requires-Dist: faiss-cpu>=1.8; extra == 'faiss'
Requires-Dist: httpx>=0.24; extra == 'faiss'
Requires-Dist: numpy>=2.2.6; extra == 'faiss'
Provides-Extra: gemini
Requires-Dist: google-cloud-aiplatform>=1.38; extra == 'gemini'
Requires-Dist: httpx>=0.24; extra == 'gemini'
Requires-Dist: openai>=1.0; extra == 'gemini'
Requires-Dist: requests>=2.32; extra == 'gemini'
Provides-Extra: huggingface
Requires-Dist: httpx>=0.24; extra == 'huggingface'
Requires-Dist: numpy>=2.2.6; extra == 'huggingface'
Requires-Dist: torch>=2.0; extra == 'huggingface'
Requires-Dist: transformers>=4.30; extra == 'huggingface'
Provides-Extra: langfuse
Requires-Dist: langfuse<3.0.0,>=2.6.0; extra == 'langfuse'
Provides-Extra: mcp
Requires-Dist: mcp>=1.2; extra == 'mcp'
Provides-Extra: mongo
Requires-Dist: pymongo>=4.6; extra == 'mongo'
Provides-Extra: onnx
Requires-Dist: httpx>=0.24; extra == 'onnx'
Requires-Dist: numpy>=2.2.6; extra == 'onnx'
Requires-Dist: onnxruntime<1.20,>=1.15; extra == 'onnx'
Requires-Dist: tokenizers>=0.13; extra == 'onnx'
Provides-Extra: openai
Requires-Dist: httpx>=0.24; extra == 'openai'
Requires-Dist: openai>=1.0; extra == 'openai'
Provides-Extra: pgvector
Requires-Dist: httpx>=0.24; extra == 'pgvector'
Requires-Dist: psycopg-pool>=3.2; extra == 'pgvector'
Requires-Dist: psycopg[binary]>=3.1; extra == 'pgvector'
Provides-Extra: postgres
Requires-Dist: httpx>=0.24; extra == 'postgres'
Requires-Dist: psycopg-pool>=3.2; extra == 'postgres'
Requires-Dist: psycopg[binary]>=3.1; extra == 'postgres'
Provides-Extra: providers
Requires-Dist: aiohttp>=3.8; extra == 'providers'
Requires-Dist: httpx>=0.24; extra == 'providers'
Requires-Dist: numpy>=2.2.6; extra == 'providers'
Requires-Dist: openai>=1.0; extra == 'providers'
Provides-Extra: qdrant
Requires-Dist: httpx>=0.24; extra == 'qdrant'
Requires-Dist: qdrant-client>=1.9; extra == 'qdrant'
Provides-Extra: serve
Requires-Dist: fastapi>=0.100; extra == 'serve'
Requires-Dist: httpx>=0.24; extra == 'serve'
Requires-Dist: uvicorn[standard]>=0.20; extra == 'serve'
Requires-Dist: websockets>=11.0; extra == 'serve'
Provides-Extra: standard
Requires-Dist: aiohttp>=3.8; extra == 'standard'
Requires-Dist: fastapi>=0.100; extra == 'standard'
Requires-Dist: httpx>=0.24; extra == 'standard'
Requires-Dist: langfuse<3.0.0,>=2.6.0; extra == 'standard'
Requires-Dist: numpy>=2.2.6; extra == 'standard'
Requires-Dist: openai>=1.0; extra == 'standard'
Requires-Dist: uvicorn[standard]>=0.20; extra == 'standard'
Requires-Dist: websockets>=11.0; extra == 'standard'
Provides-Extra: triton
Requires-Dist: httpx>=0.24; extra == 'triton'
Requires-Dist: numpy>=2.2.6; extra == 'triton'
Requires-Dist: tokenizers>=0.13; extra == 'triton'
Requires-Dist: tritonclient[grpc]>=2.41; extra == 'triton'
Description-Content-Type: text/markdown

# Operonx

<p align="center">
  <a href="https://github.com/batman1m2001-cyber/Operonx/actions/workflows/tests.yaml"><img src="https://github.com/batman1m2001-cyber/Operonx/actions/workflows/tests.yaml/badge.svg?branch=main" alt="Tests"></a>
  <a href="https://github.com/batman1m2001-cyber/Operonx/actions/workflows/format.yaml"><img src="https://github.com/batman1m2001-cyber/Operonx/actions/workflows/format.yaml/badge.svg?branch=main" alt="Format"></a>
  <a href="https://batman1m2001-cyber.github.io/Operonx/"><img src="https://github.com/batman1m2001-cyber/Operonx/actions/workflows/docs.yaml/badge.svg?branch=main" alt="Docs"></a>
  <a href="https://codecov.io/gh/batman1m2001-cyber/Operonx"><img src="https://codecov.io/gh/batman1m2001-cyber/Operonx/branch/main/graph/badge.svg" alt="Coverage"></a>
  <a href="https://pypi.org/project/operonx/"><img src="https://img.shields.io/pypi/v/operonx?label=PyPI" alt="PyPI"></a>
  <img src="https://img.shields.io/badge/python-3.10%2B-blue" alt="Python">
  <a href="https://github.com/batman1m2001-cyber/Operonx/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-green" alt="License"></a>
</p>

**Operonx** is a Python workflow engine whose ops can `yield`. The same
graph runs as a batch job over a file, as a streaming pipeline (a voice bot:
audio → STT → LLM → TTS), or behind an HTTP or WebSocket service.

You build with it through a coding assistant (Claude Code, Codex, Cursor,
…). `operonx init` makes a project the assistant can work in at once: a
layout, a first feature with its tests, the project's rules in `AGENTS.md`,
and the API guide of your installed operonx beside the code.

## Start a project

With [uv](https://docs.astral.sh/uv/):

```bash
uvx operonx init myapp     # uvx runs operonx without installing it
cd myapp
uv sync
uv run pytest              # the first feature's tests, offline
```

With pip only (no uv, e.g. behind a company mirror):

```bash
pip install operonx
operonx init myapp
cd myapp
python -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate
pip install -e ".[test]"
pytest
```

`operonx init` writes the commands of the toolchain you use into the
project's docs: uv when it is installed, else pip (`--uv` or `--pip` to
choose). The first feature is `--template hello` (pure compute, a job and an
HTTP service), `http`, `chat` (one LLM call) or `agent` (an agent with a
tool).

Then open the folder in your coding assistant and ask for what you want:
*"add a feature that scores each call in `datasets/calls.jsonl` with the
assistant model, and a job that runs it nightly"*.

## What you get

```
myapp/
├── AGENTS.md            the rules your assistant reads first (CLAUDE.md is `@AGENTS.md`)
├── .operonx/guide/      the operonx API guide, for the version installed here
├── app/main.py          the map: every service and job, in one Application
├── src/greeting/        one feature, one folder
│   ├── graph.py         the wiring: which op runs after which, what feeds what
│   └── ops.py           the logic: plain Python functions
├── tests/               the feature's tests; no test calls a real model
├── datasets/            what jobs run over (.jsonl)
├── resources.yaml       models and stores, by key (`llm:assistant`)
├── .env.example         the secrets resources.yaml names; copy to .env
├── operonx.toml         points the operonx command at app/main.py
└── pyproject.toml       works with uv and with pip
```

## What your assistant reads

| File | What it is | Who writes it |
|---|---|---|
| `AGENTS.md` | the project's rules: the layout, the ladder (op → graph → job or service → application), the commands, what never to do | you; start from what `init` wrote |
| `.operonx/guide/` | how every operonx API is used; every example in it is tested against the installed version | operonx, never edit |
| `app/main.py` | what the product runs | you and the assistant |

`AGENTS.md` tells the assistant to read the guide before it uses an operonx
API, so it does not write operonx from memory or from an older version.
The block between `<!-- operonx:guide -->` markers in `AGENTS.md` belongs to
operonx; the rest is yours.

After an upgrade, `operonx guide` refreshes the guide and that block. Any
`operonx` command run in the project notices a stale guide and refreshes it.

```bash
uv lock --upgrade-package operonx && uv sync && uv run operonx guide   # uv
pip install -U operonx && operonx guide                                # pip
```

## Run it, serve it, see it

```bash
uv run operonx run greet_people   # a job: the graph once per line of datasets/people.jsonl
uv run operonx serve              # the services: POST /greet on :8000
uv run operonx studio             # the project in operonx-studio, in the browser
```

With pip, the same commands without `uv run`, in the activated `.venv`.

[operonx-studio](https://github.com/batman1m2001-cyber/operonx-studio)
draws every graph (what runs after what, and which value goes where), lets
you talk to a service turn by turn, and shows runs, evals, jobs and
services. It is a separate tool, installed once from its repository:

```bash
git clone https://github.com/batman1m2001-cyber/operonx-studio
operonx-studio/install.sh            # or, without bash: pip install ./operonx-studio
```

## What the code looks like

An op is a Python function. One that `yield`s runs what follows it once per
item. A `@graph` wires ops with `>>`:

```python
from operonx import END, START, graph, op


@op
def words(text: str):
    for w in text.split():
        yield {"word": w}


@op
def shout(word: str) -> dict:
    return {"loud": word.upper()}


@graph
def flow(text):
    w = words(text=text)
    s = shout(word=w["word"])
    START >> w >> s >> END
```

`app/main.py` decides how a graph runs: a `Job` over a file, or a `Service`
behind `http(...)`, `websocket(...)`, `webhook(...)` or `schedule(...)`.

## Extras

Provider SDKs are extras. A project names its extras on the `operonx[...]`
line of `pyproject.toml` (`init` writes `operonx[serve]`); add one there,
e.g. `operonx[serve,openai]`, then `uv sync` (with pip, `pip install -e ".[test]"`).

| Extra | Contents |
|---|---|
| `openai` | OpenAI and Azure, and any OpenAI-compatible endpoint (vLLM, TEI) |
| `anthropic`, `gemini`, `bedrock` | the other model providers |
| `onnx`, `triton`, `huggingface` | local inference (`huggingface` brings torch, ~2.5 GB) |
| `pgvector`, `qdrant`, `faiss`, `postgres`, `mongo`, `clickhouse` | stores |
| `langfuse` | Langfuse tracing |
| `serve` | the HTTP and WebSocket services |
| `mcp` | MCP tools |
| `standard` | OpenAI, Langfuse and serve |
| `all` | every provider, store and tracer (not `huggingface` or `mcp`) |

[operonx-agents](https://pypi.org/project/operonx-agents/) (agents) and
[operonx-kb](https://pypi.org/project/operonx-kb/) (knowledge bases) are
separate packages; each adds its own pages to `.operonx/guide/`.

## Documentation

| For | Where |
|---|---|
| your coding assistant | [`operonx/guide/`](operonx/guide/README.md), copied into every project as `.operonx/guide/` |
| reading it yourself | [the documentation site](https://batman1m2001-cyber.github.io/Operonx/) |
| runnable examples | [examples/python/](examples/python/) |
| what changed | [CHANGELOG.md](CHANGELOG.md) |

## Contributing

```bash
git clone https://github.com/batman1m2001-cyber/Operonx.git
cd Operonx
uv sync --all-extras
pre-commit install
uv run pytest tests/ -m "not integration"
```

See [CONTRIBUTING.md](CONTRIBUTING.md) for the full contributor guide.

## License

Apache 2.0
