Skip to main content

AntFlow: Async execution library with concurrent.futures-style API and advanced pipelines

Project description

AntFlow Logo

AntFlow

Why AntFlow?

I was processing massive amounts of data using OpenAI's Batch API. The workflow had four steps:

  1. Upload batches to OpenAI
  2. Wait for processing
  3. Download results
  4. Save to database

I was running 10 batches at a time with basic async. The problem: I had to wait for all 10 to finish before starting the next group.

In practice, 9 batches would finish in 5 minutes and one would take 30. That one slow batch blocked everything — 25 minutes of idle time, repeated across hundreds of batches.

AntFlow fixes this with a worker pool model: each worker picks up the next task as soon as it finishes, so slow tasks never block fast ones. Worker count, retry logic, and stage configuration stay in your hands.

My batch processing went from hours to a fraction of the time.

AntFlow Workers

AntFlow Demo


Install

pip install AntFlow

OpenTelemetry support (optional):

pip install AntFlow[opentelemetry]

Quick Start

Three ways to create a pipeline — pick what fits:

Fluent builder

import asyncio
from antflow import Pipeline

async def fetch(x):
    await asyncio.sleep(0.1)
    return f"data_{x}"

async def main():
    results = await (
        Pipeline.create()
        .add("Fetch", fetch, workers=5, retries=3)
        .run(range(10), progress=True)
    )
    print(f"Processed {len(results)} items")

asyncio.run(main())

Stage objects

import asyncio
from antflow import Pipeline, Stage

async def process(x):
    await asyncio.sleep(0.1)
    return x * 2

async def main():
    stage = Stage(name="Process", workers=5, tasks=[process])
    pipeline = Pipeline(stages=[stage])
    results = await pipeline.run(range(10), progress=True)
    print(f"Processed {len(results)} items")

asyncio.run(main())

One-liner

import asyncio
from antflow import Pipeline

async def simple_task(x):
    return x + 1

async def main():
    results = await Pipeline.quick(range(10), simple_task, workers=5, progress=True)
    print(f"Processed {len(results)} items")

asyncio.run(main())
Method When to use
Stage objects Fine-grained control, custom callbacks, per-task concurrency limits
Fluent builder Multi-stage pipelines, quick prototyping
Pipeline.quick() Single-task scripts

Dashboards

Pipelines run silently by default. Pass progress=True for a progress bar, or dashboard= for more detail:

results = await Pipeline.quick(items, task, workers=5, dashboard="detailed")

Options: "compact", "detailed", "full". Use "detailed" on multi-stage pipelines to spot bottlenecks per stage.


Streaming results

import asyncio
from antflow import Pipeline

async def process(x):
    await asyncio.sleep(0.1)
    return f"result_{x}"

async def main():
    pipeline = Pipeline.create().add("Process", process, workers=5).build()

    async for result in pipeline.stream(range(10)):
        print(f"Got: {result.value}")

asyncio.run(main())

AsyncExecutor

For simple parallel execution without pipelines:

import asyncio
from antflow import AsyncExecutor

async def process_item(x):
    await asyncio.sleep(0.1)
    return x * 2

async def main():
    async with AsyncExecutor(max_workers=10) as executor:
        results = await executor.map(process_item, range(100), retries=3)
        print(f"Processed {len(results)} items")

asyncio.run(main())

retries=3 means up to 4 total attempts with exponential backoff.


Multi-stage pipeline

import asyncio
from antflow import Pipeline, Stage

async def fetch(x):
    await asyncio.sleep(0.1)
    return f"data_{x}"

async def process(x):
    await asyncio.sleep(0.1)
    return x.upper()

async def save(x):
    await asyncio.sleep(0.1)
    return f"saved_{x}"

async def main():
    fetch_stage = Stage(
        name="Fetch",
        workers=10,
        tasks=[fetch],
        task_concurrency_limits={"fetch": 2}  # avoid rate limits
    )
    process_stage = Stage(name="Process", workers=5, tasks=[process])
    save_stage = Stage(name="Save", workers=3, tasks=[save])

    pipeline = Pipeline(stages=[fetch_stage, process_stage, save_stage])
    results = await pipeline.run(range(50), progress=True)

    print(f"Completed: {len(results)} items")
    print(f"Stats: {pipeline.get_stats()}")

asyncio.run(main())

Worker counts follow the workload: more for I/O-bound stages, fewer where you're rate-limited.


StatusTracker

Track every item as it moves through stages:

from antflow import Pipeline, Stage, StatusTracker
import asyncio

async def fetch(x): return x
async def process(x): return x * 2
async def save(x): return x

async def log_event(event):
    print(f"Item {event.item_id}: {event.status} @ {event.stage}")

tracker = StatusTracker(on_status_change=log_event)

pipeline = Pipeline(
    stages=[
        Stage(name="Fetch", workers=5, tasks=[fetch]),
        Stage(name="Process", workers=3, tasks=[process]),
        Stage(name="Save", workers=5, tasks=[save]),
    ],
    status_tracker=tracker
)

async def main():
    await pipeline.run(range(50))

    stats = tracker.get_stats()
    print(f"Completed: {stats['completed']}, Failed: {stats['failed']}")

    history = tracker.get_history(item_id=0)

asyncio.run(main())

Dashboard vs StatusTracker: dashboards poll on an interval for visual output; StatusTracker fires async callbacks on each event. Use dashboards for interactive debugging, StatusTracker for logging to external systems.


Features

  • Worker pool per stage — workers never block each other
  • Per-task retry with exponential backoff
  • Per-stage retry for transactional operations
  • Priority queues — bypass sequential order when needed
  • Interactive control — resume pipelines, inject items mid-run
  • OpenTelemetry auto-instrumentation (optional)
  • concurrent.futures-style API (submit, map, as_completed)

Documentation

Full docs at rodolfonobrega.github.io/AntFlow:

Local docs:

pip install mkdocs-material
mkdocs serve

Requirements

  • Python 3.9+
  • tenacity >= 8.0.0

Python 3.9–3.10 installs the taskgroup backport automatically.


Tests

pip install -e ".[dev]"
pytest

Contributing

See CONTRIBUTING.md.


License

MIT — see LICENSE.

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

antflow-0.10.0.tar.gz (72.9 kB view details)

Uploaded Source

Built Distribution

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

antflow-0.10.0-py3-none-any.whl (47.0 kB view details)

Uploaded Python 3

File details

Details for the file antflow-0.10.0.tar.gz.

File metadata

  • Download URL: antflow-0.10.0.tar.gz
  • Upload date:
  • Size: 72.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for antflow-0.10.0.tar.gz
Algorithm Hash digest
SHA256 425dd6574c7ffdb35a6dce21cbac4be157b9ce37fbdee9ece3927aef66c4343d
MD5 18f355567971d7946a1d16dbd6c90e66
BLAKE2b-256 b6f30958a701ee2c4caa4847b2a9ce0b161cd9538324a42429f5092a5c5680f1

See more details on using hashes here.

Provenance

The following attestation bundles were made for antflow-0.10.0.tar.gz:

Publisher: pypi.yml on rodolfonobrega/AntFlow

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

File details

Details for the file antflow-0.10.0-py3-none-any.whl.

File metadata

  • Download URL: antflow-0.10.0-py3-none-any.whl
  • Upload date:
  • Size: 47.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for antflow-0.10.0-py3-none-any.whl
Algorithm Hash digest
SHA256 75a90e202fb9247114b112d19e513168a30e260356d3b2e2ee7c5c516c5c8f1d
MD5 6eb591da6ac0757c001091fc2599aa6d
BLAKE2b-256 bd1fa9bb48043efc85fffea67497028728a0f387c4df4f74f16394aa78e07b2f

See more details on using hashes here.

Provenance

The following attestation bundles were made for antflow-0.10.0-py3-none-any.whl:

Publisher: pypi.yml on rodolfonobrega/AntFlow

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

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page