Skip to main content

An easy to use actor model framework in Python

Project description

Spark Framework

Spark is an async-first, zero-dependency, actor model framework for Python.

Install

pip install spark

mailboxes are async queues, wakeups are event-loop timers, fd watching uses the event loop, and remote TCP messaging uses asyncio streams.

Quick Start

import asyncio

from spark import Actor, Syndicate
from spark.core.message import Message


class HelloActor(Actor):
    async def process(self, message: Message) -> None:
        if message.sender is not None:
            await self.tell(f"Hello {message.content}", message.sender)


async def main() -> None:
    async with Syndicate("hello") as syn:
        hello = await syn.create_actor(HelloActor)
        await syn.tell(hello, "World")
        print(await syn.receive(timeout=1.0))


asyncio.run(main())

For synchronous application entrypoints, use the built-in runner:

Syndicate.run(main())

API Shape

async with Syndicate("app") as syn:
    worker = await syn.create_actor(Worker)
    await syn.tell(worker, "fire-and-forget")
    reply = await syn.ask(worker, "request", timeout=5.0)

    async for message in syn.listen():
        ...

Actors use async helpers:

await self.tell(payload, target)
reply = await self.ask(payload, target, timeout=1.0)
child = await self.create_actor(ChildActor)
self.schedule_after(0.5, "timer-payload")
await self.watch(read=(fd,))

Backends

The async in-process backend is the reference runtime. Hybrid local executor backends are also available for explicit stateless actors:

from spark import ActorSpec, Syndicate


async with Syndicate("workers", backend="threaded") as system:
    worker = await system.create_actor_from_spec(
        ActorSpec(actor_class=Worker, execution="system", stateless=True)
    )

backend="threaded" resolves execution="system" to a dedicated worker thread. backend="process" resolves it to a dedicated worker process. Normal actors still run in the async coordinator unless an ActorSpec explicitly asks for execution="thread", execution="process", or execution="system".

Thread/process executor actors are for stateless jobs. They can reply with tell, but full actor APIs such as child actor creation, ask, wakeups, fd watching, and system shutdown are intentionally limited to in-process actors.

Remote Systems

async with Syndicate("left", remote=True) as left, Syndicate("right", remote=True) as right:
    assert left.remote_address is not None
    assert right.remote_address is not None
    await left.connect(right.system_id, *right.remote_address)
    await right.connect(left.system_id, *left.remote_address)

Remote transport is system-level TCP routing by SystemId. The current codec uses pickle and should only be used with trusted peers.

For structured CBOR-native payloads, install spark-actor[cbor2] and opt in on every peer:

async with Syndicate("left", remote=True, transport_codec="cbor2") as left:
    ...

Both sides must use the same codec. The default remains transport_codec="pickle" for compatibility with arbitrary Python message payloads.

WebSocket transport is available as an optional extra for direct ws:// / wss:// routes or relay-backed routes:

async with Syndicate("left", remote=True, remote_transport="websocket") as left:
    await left.connect_uri(right_system_id, "wss://right.example.net/spark")

For networks where neither side can accept inbound connections, run a relay with spark-ws-relay --host 0.0.0.0 --port 8765 and connect each system with await system.connect_relay("wss://relay.example.net"). The websocket transport uses the same trusted-peer pickle codec as TCP.

See examples/websocket_two_networks.py for a runnable relay example where one machine hosts a responder actor and another machine asks it through a shared websocket relay.

LLM Actors

from spark.agent import LLMAgent, LLMRequest, OpenAIResponsesProvider


async with Syndicate("llm") as system:
    provider = OpenAIResponsesProvider()
    agent = await system.create_actor(LLMAgent, provider, instructions="Answer briefly.")
    response = await system.ask(agent, LLMRequest("Explain actors in one sentence."), timeout=120)
    print(response.content)

Provider interfaces are async-native. Streaming providers return async iterators of LLMStreamChunk.

Troupe Worker Execution

spark.contrib.troupe.Troupe can run workers in-process, in threads, or in processes while keeping the manager in the async coordinator:

class Translate(Troupe):
    troupe_max_count = 5
    troupe_idle_count = 5
    troupe_worker_execution = "thread"

Use thread workers for parallel external I/O such as LLM calls, and process workers for picklable CPU-style jobs. See examples/llm_troupe_translation.py and examples/process_troupe_cpu.py.

Development

uv venv -p 3.13
uv pip install -e ".[dev,test]"
uv run pytest
uv run ruff check spark/actor/base.py spark/runtime/async_backend.py spark/runtime/backend.py spark/system.py spark/transport/async_tcp.py spark/contrib/llm spark/contrib/troupe.py tests/async
uv run mypy spark/actor/base.py spark/runtime/async_backend.py spark/runtime/backend.py spark/system.py spark/transport/async_tcp.py spark/contrib/llm spark/contrib/troupe.py

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

spark-0.4.0.tar.gz (100.0 kB view details)

Uploaded Source

Built Distribution

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

spark-0.4.0-py3-none-any.whl (124.5 kB view details)

Uploaded Python 3

File details

Details for the file spark-0.4.0.tar.gz.

File metadata

  • Download URL: spark-0.4.0.tar.gz
  • Upload date:
  • Size: 100.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.2 CPython/3.10.12 Linux/5.15.0-164-generic

File hashes

Hashes for spark-0.4.0.tar.gz
Algorithm Hash digest
SHA256 1cf2c0fd1b4fe54646ff8702f5e75d1e5b26595b046e415b2a99dafd26daecc9
MD5 22fa66f73118de53ec6a97d94064be6c
BLAKE2b-256 97ef10092b292b2d067040e9d45d4dbe3b1f322716fb07d1b6d413d2265a8fc3

See more details on using hashes here.

File details

Details for the file spark-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: spark-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 124.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.2 CPython/3.10.12 Linux/5.15.0-164-generic

File hashes

Hashes for spark-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7c2ead9564d3d0658f456f2b0e59de8f7b064173ebca1b23fecec58c9b14576e
MD5 a42d66eb6373e9b800d41764fb628a0f
BLAKE2b-256 d2227e4932318471cc63302a78a7cfe419688afc81ed09310d57e39881670351

See more details on using hashes here.

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