Python authoring for structured LLM environments powered by a Julia Petri-net runtime
Project description
Peven
Peven is Python authoring for structured LLM environments backed by a Julia Petri-net runtime.
If PydanticAI makes it easy to build agents, Peven makes it easy to build the environment around them: places, transitions, joins, guards, retries, and the topology you want to evaluate.
Why use it
- Author environments in Python, next to the agents and tools you already write.
- Make topology explicit instead of hiding it inside one giant agent loop.
- Run the hard state-machine part on a Julia engine built for Petri nets and concurrent firing.
- Compare workflows: single-shot, judge loops, keyed joins, guarded retries, branch-and-merge topologies.
Install
First install the Python package:
uv add peven
or
pip install peven
Peven also needs a Julia runtime. peven.install_runtime() provisions that layer through juliapkg, including:
- Julia itself if it is not already available
PevenPy.jlPeven.jl
Recommended: do that immediately after install so the one-time Julia download, package resolution, and precompile work does not happen on your first real run.
uv run peven-install
or
uv run peven install-runtime
If you skip that step, the first Env.run() will do the same provisioning automatically.
Quickstart
from pydantic_ai import Agent
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.output import NativeOutput
from pydantic_ai.providers.ollama import OllamaProvider
import peven
agent = Agent(
OpenAIChatModel(
"qwen3.5:9b",
provider=OllamaProvider(base_url="http://127.0.0.1:11434/v1"),
),
output_type=NativeOutput(str),
)
@peven.executor("answer")
async def answer(ctx, prompt):
question = prompt.payload["question"]
result = await agent.run(question)
return ctx.token(
{
"question": question,
"answer": result.output.strip().lower(),
}
)
@peven.env("single_question")
class SingleQuestionEnv(peven.Env):
prompt = peven.place(schema={"kind": "question"})
report = peven.place()
def initial_marking(self) -> peven.Marking:
return peven.marking(
prompt=[{"question": "What planet is known as the red planet?"}]
)
solve = peven.transition(
inputs=["prompt"],
outputs=["report"],
executor="answer",
)
result = SingleQuestionEnv().run()
print(result.status)
print(result.final_marking["report"][0].payload)
That same pattern scales to richer topologies:
- tee one prompt into multiple branches
- join outputs back together by key
- gate transitions with guards
- retry transitions without rewriting control flow
- cap the run with
fuse
Why Julia
The Julia side is not there for novelty. It keeps the engine closer to the real Petri-net model.
Python is a great place to author agents and executors, but it pushes engine code toward shims, wrappers, and dynamic glue. Julia is a better fit for the symbolic runtime: markings, firing rules, joins, guards, retries, and termination stay explicit instead of dissolving into spaghetti soup.
Architecture
Peven has three layers:
peven— Python authoring, executors, integrations, sinks, and runtime ownership.PevenPy.jl— the narrow Julia adapter boundary.Peven.jl— the execution engine.
Technically, Python authors the env and owns transition callbacks. PevenPy.jl lowers the authored env into Julia runtime structures, runs the net, and streams runtime events back. Peven.jl owns the actual Petri-net execution semantics.
Examples
The repo examples are intentionally small but representative:
examples/trace.py— PydanticAI trace integration,fuse, and rich run outputexamples/guarded_batch.py— guarded retries around a batch stepexamples/keyed_join.py— branch, answer in parallel, and keyed-join the results
Inspiration
Peven is inspired by a couple different things. For starters the name is taken from Patricia A. McKillip's Riddle-Master trilogy. Peven of Aum is a king, a ghost, and a master riddler who has only ever lost once. In the Riddle-Master trilogy, riddles are made up of three parts: questions, answers, and strictures. My hope for Peven is that it can help you explore evaluations by providing a runtime where you can ask a question, iterate based on the stricture, and, eventually, get to an answer. "Beware the unanswered Riddle."
My second point of inspiration comes from my time working at The LLM Data Company, where I had the chance to learn and experiment to my heart's content. A lot of my work centered around environments and benchmarks. I often wished I had a reusable framework or package to support my work here, something like a pydantic (which I love) but for evaluations.
Most of the architectural decisions I made regarding the engine are because I thought the math was cool. Peven should give you a pretty clear sense of (1) how I think about evaluations and (2) what types of evaluations I'm interested in.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file peven-0.2.0.tar.gz.
File metadata
- Download URL: peven-0.2.0.tar.gz
- Upload date:
- Size: 284.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8c74f46b8bcbae732128f16a4bba05537d863d452e9b907ec45e3b5a219a3d42
|
|
| MD5 |
caf0eddf068c0b348210b67ef505a2aa
|
|
| BLAKE2b-256 |
8dcbb7ff6c762156f490658a79964d7187ad04abd659a4887a10619b5ef0b815
|
Provenance
The following attestation bundles were made for peven-0.2.0.tar.gz:
Publisher:
publish.yml on jammy-eggs/peven
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
peven-0.2.0.tar.gz -
Subject digest:
8c74f46b8bcbae732128f16a4bba05537d863d452e9b907ec45e3b5a219a3d42 - Sigstore transparency entry: 1364615013
- Sigstore integration time:
-
Permalink:
jammy-eggs/peven@4aff9308e62eb8b36735d8c784c959ba22f2a440 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/jammy-eggs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4aff9308e62eb8b36735d8c784c959ba22f2a440 -
Trigger Event:
push
-
Statement type:
File details
Details for the file peven-0.2.0-py3-none-any.whl.
File metadata
- Download URL: peven-0.2.0-py3-none-any.whl
- Upload date:
- Size: 50.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c8bbbedf94fb73705274d1fa5c1e8a06c33bc68989369b1cd88c79cbe8f986c4
|
|
| MD5 |
273f2723751342ae29988e895c437b7a
|
|
| BLAKE2b-256 |
819de9e03dd0fd95be4a93f533701c259a1af24c8a39c13db49044564cac1fbd
|
Provenance
The following attestation bundles were made for peven-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on jammy-eggs/peven
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
peven-0.2.0-py3-none-any.whl -
Subject digest:
c8bbbedf94fb73705274d1fa5c1e8a06c33bc68989369b1cd88c79cbe8f986c4 - Sigstore transparency entry: 1364615024
- Sigstore integration time:
-
Permalink:
jammy-eggs/peven@4aff9308e62eb8b36735d8c784c959ba22f2a440 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/jammy-eggs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4aff9308e62eb8b36735d8c784c959ba22f2a440 -
Trigger Event:
push
-
Statement type: