Skip to main content

Dagic

A minimal workflow DAG (Directed Acyclic Graph) definition language and an asynchronous execution engine, implemented in Python.

Dagic lets you describe a computation graph using a tiny, deliberately limited language. The graph's nodes are functions supplied by your host program; the edges are the function arguments. At compile time the graph is type-checked, and at run time it is executed concurrently across its independent branches.

Why

Dagic is aimed at bringing piping capabilities to LLMs.

In traditional tool calling, the agent is itself responsible for shuttling state between tools. Every intermediate result has to round-trip through the model — as an argument (or part of it) — which wastes turns: the agent spends tokens describing values it already produced instead of deciding what to do next.

The common fix is a code execution tool, which lets the model chain calls with ordinary variable assignments. That is trivial in a local harness like a coding CLI, but complicated and expensive for server-side harnesses, which must sandbox arbitrary code, manage runtimes, and defend a large attack surface.

Dagic sits in the gap between simple tool calls and full code execution: a workflow orchestration layer that is more expressive than a single tool call (arbitrary chains, parallelism, type checking) yet far safer and simpler to host than arbitrary code. The language is stripped down to just the two operations you need to define a DAG — assignment and function calls — so the model composes existing tools into controlled, typed, verifiable workflows instead of running free-form code.

Installation

Requires Python 3.10+.

pip install dagic

Syntax

A Dagic program is a sequence of statements. Each statement is one of:

  • an assignment: name = <expression>;
  • a function call: func(<expression>, ...);
result = add(create("1"), create("2"));
store(result);

joined = join(["Hello", "World!"], " ");
print(joined);
  • The functions are the nodes of the DAG.
  • The edges are the function arguments.
  • Every edge has a strict type. Passing a value of the wrong type to a function is a compile-time error.
  • Function definitions and types are provided by your host program (see below).

Data types

The only built-in types are:

  • strings"Hello, World!"
  • arrays["Hello", "World!"] (items must share the exact same type, and the array's type is inferred from them)

Every other type (numbers, objects, custom types, ...) is defined by the host program. An array's type edges come from functions registered for it, so passing a List[float] where a List[int] is expected is rejected at compile time.

Execution model

  1. A Dagic program compiles into a DAG.
  2. Execution starts from the terminal nodes — top-level function calls that return nothing (like print or store) — and runs backwards, resolving every dependency.
  3. Independent branches execute concurrently.
  4. A program without at least one terminal node is invalid. It would have no exit point, so it is rejected at compile time.
  5. Similarly, every named subgraph must be referenced at least once; unused ("orphan") subgraphs are rejected.

Defining functions (the host side)

Functions and their types are defined in Python and given to Dagic when it runs. Register them on a Module with the @node decorator. A node function must:

  • have type annotations on every parameter and the return value;
  • have no *args, **kwargs, default parameters, or keyword-only parameters.
from dagic import Module

math = Module(name="math", desc="Basic arithmetic.")


@math.node
def create(value: str) -> float:
    """Create a float from a string."""
    return float(value)


@math.node
def add(a: float, b: float) -> float:
    """Add two numbers."""
    return a + b

Functions whose -> None return type are terminals. Everything else produces a value that must be consumed by another call.

Using the engine

The package ships a small builtin module, float_math (float arithmetic: add, subtract, multiply, divide, power, modulus, floor_divide, absolute, negate, and a create from-string constructor). It is a Module instance exported as float_math.float_math. Pass any host modules you need to Dagic:

import asyncio
from dagic import Dagic, Module
from dagic.builtins import float_math

sink = []

io = Module(name="io", desc="I/O helpers.")


@io.node
def store(value: float) -> None:
    sink.append(value)


async def main():
    dagic = Dagic([float_math.float_math, io])
    await dagic.run('result = add(create("1"), create("2")); store(result);')
    print(sink)  # [3.0]


asyncio.run(main())

Dagic.run is async: it compiles the source against the registered modules, builds the graph, and executes it concurrently.

Development

make test    # run the test suite (pytest)
make format  # format with ruff

License

MIT.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dagic-0.1.0.tar.gz (27.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dagic-0.1.0-py3-none-any.whl (16.2 kB view details)

Uploaded Python 3

File details

Details for the file dagic-0.1.0.tar.gz.

File metadata

  • Download URL: dagic-0.1.0.tar.gz
  • Upload date:
  • Size: 27.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for dagic-0.1.0.tar.gz
Algorithm Hash digest
SHA256 1b501048197b60cba9499c238c2d18f533c1c0f6b49a66848b96377afc5fb938
MD5 bce8781c010d73fd2281e4baded0fae4
BLAKE2b-256 5785fdb2a32489869d86ba5b0b634ca29fc83da1635c87226e7c15ec93974596

See more details on using hashes here.

Provenance

The following attestation bundles were made for dagic-0.1.0.tar.gz:

Publisher: publish.yml on RohitEdathil/dagic

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dagic-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: dagic-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 16.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for dagic-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3d2189255403180043d27caef6962fe77828be30666a08b0ffe4f664369b2078
MD5 721c4cc4ce403bb8d8bbfd24561e4672
BLAKE2b-256 ffd7c6f771bca1bcfa81138a861066bb083ba113c9b627d1cfe999b385ea96d8

See more details on using hashes here.

Provenance

The following attestation bundles were made for dagic-0.1.0-py3-none-any.whl:

Publisher: publish.yml on RohitEdathil/dagic

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.2.1

2 files

0.2.0

2 files

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page