Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

103 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Pulsing

CI License Python 3.10+ Rust

δΈ­ζ–‡ζ–‡ζ‘£

Backbone for distributed AI systems.

Actor runtime. Streaming-first. Zero dependencies. Built-in discovery.

Pulsing is a distributed actor runtime built in Rust, designed for Python. Connect AI agents and services across machines β€” no Redis, no etcd, no YAML. Just pip install pulsing.

πŸš€ Zero Dependencies β€” Pure Rust + Tokio, no NATS/etcd/Redis

⚑ Streaming-first β€” Native support for streaming responses, built for LLM token generation

🌐 Built-in Discovery β€” SWIM/Gossip protocol for automatic cluster management

πŸ”€ Same API Everywhere β€” Same await actor.method() for local and remote Actors

πŸš€ Get Started in 5 Minutes

Installation

pip install pulsing

Your First Multi-Agent Application

import asyncio
import pulsing as pul
from pulsing.agent import runtime

@pul.remote
class Greeter:
    def __init__(self, display_name: str):
        self.display_name = display_name

    def greet(self, message: str) -> str:
        return f"[{self.display_name}] Received: {message}"

    async def chat_with(self, peer_name: str, message: str) -> str:
        # Use Greeter.resolve() to get a typed proxy
        peer = await Greeter.resolve(peer_name)
        return await peer.greet(f"From {self.display_name}: {message}")

async def main():
    async with runtime():
        # Create two agents
        alice = await Greeter.spawn(display_name="Alice", name="alice")
        bob = await Greeter.spawn(display_name="Bob", name="bob")

        # Agent communication
        reply = await alice.chat_with("bob", "Hello!")
        print(reply)  # [Bob] Received: From Alice: Hello!

asyncio.run(main())

That's it! @pul.remote turns a regular class into a distributed Actor, and Greeter.resolve() enables agents to discover and communicate with each other.

πŸ’‘ I want to...

Scenario Example Description
Quick start examples/quickstart/ Get started in 10 lines
Multi-Agent collaboration examples/agent/pulsing/ AI debate, brainstorming, role-playing
Distributed LLM inference pulsing actor router/vllm GPU cluster inference service
Integrate AutoGen examples/agent/autogen/ One line to go distributed
Integrate LangGraph examples/agent/langgraph/ Execute graphs across nodes
Agent workspace CLI pulsing agent init Pulsing Agent β€” multi-agent in your repo
Agent tools & environment examples/python/forge_minimal.py Pulsing Forge β€” sandboxed shell, files, plan

πŸ”¨ Pulsing Forge

A general-purpose tool and environment runtime for AI agents β€” run shell commands, edit files, and manage plans inside a configurable sandbox. Embed in any agent framework, or deploy isolated workers via Pulsing Actors.

from pulsing.forge import ForgeEnvironment

env = ForgeEnvironment(cwd=".")
env.runtime().call_tool("shell_command", {"cmd": "pytest -q", "workdir": "."})

Docs: Forge chapter Β· Package README: python/pulsing/forge/README.md

πŸ€– Pulsing Agent

Workspace-scoped multi-agent SDK + CLI β€” init a .pulsing/ workspace, wake agents on the cluster, and collaborate with Forge tools.

pip install pulsing[agent]
pulsing agent init
pulsing agent wake --agents guide
pulsing agent say guide "run pytest"

Docs: workspace demo Β· SDK: from pulsing.agent import Agent, spawn_agent

🎯 Core Capabilities

1. Multi-Agent Collaboration

Multiple AI Agents working in parallel and communicating:

from pulsing.agent import agent, runtime, llm

@agent(role="Researcher", goal="Deep analysis")
class Researcher:
    async def analyze(self, topic: str) -> str:
        client = await llm()
        return await client.ainvoke(f"Analyze: {topic}")

@agent(role="Reviewer", goal="Evaluate proposals")
class Reviewer:
    async def review(self, proposal: str) -> str:
        client = await llm()
        return await client.ainvoke(f"Review: {proposal}")

async with runtime():
    researcher = await Researcher.spawn(name="researcher")
    reviewer = await Reviewer.spawn(name="reviewer")

    # Parallel work and collaboration
    analysis = await researcher.analyze("AI trends")
    feedback = await reviewer.review(analysis)
# Run MBTI personality discussion example
python examples/agent/pulsing/mbti_discussion.py --mock --group-size 6

# Run parallel idea generation example
python examples/agent/pulsing/parallel_ideas_async.py --mock --n-ideas 5

2. One Line to Distributed

Develop locally, scale seamlessly to clusters:

# Standalone mode (development)
async with runtime():
    agent = await MyAgent.spawn(name="agent")

# Distributed mode (production) β€” just add address
async with runtime(addr="0.0.0.0:8001"):
    agent = await MyAgent.spawn(name="agent")

# Other nodes auto-discover
async with runtime(addr="0.0.0.0:8002", seeds=["node1:8001"]):
    agent = await resolve("agent")  # Cross-node transparent call

3. LLM Inference Service

Out-of-the-box GPU cluster inference:

# Start Router (OpenAI-compatible API)
pulsing actor pulsing.serving.Router --addr 0.0.0.0:8000 --http_port 8080 --model_name my-llm

# Start vLLM Worker (can have multiple)
pulsing actor pulsing.serving.VllmWorker --model Qwen/Qwen2.5-0.5B --addr 0.0.0.0:8002 --seeds 127.0.0.1:8000

# Test
curl http://localhost:8080/v1/chat/completions \
  -d '{"model": "my-llm", "messages": [{"role": "user", "content": "Hello"}]}'

4. Agent Framework Integration

Have existing AutoGen/LangGraph code? One-line migration:

# AutoGen: Replace runtime
from pulsing.autogen import PulsingRuntime
runtime = PulsingRuntime(addr="0.0.0.0:8000")

# LangGraph: Wrap the graph
from pulsing.langgraph import with_pulsing
distributed_app = with_pulsing(app, seeds=["gpu-server:8001"])

5. Fast TensorDict Transport

TensorMessage carries opaque metadata and ordered, contiguous CPU buffers without putting tensor payloads in the normal pickle envelope. The caller (for example PulsingQueue) moves CUDA tensors to CPU, makes them contiguous, and encodes dtype, shape, byte order, and TensorDict structure in metadata. Pulsing only transports those bytes and buffers.

from array import array

import pulsing as pul

cpu_buffer = array("f", range(6))
message = pul.TensorMessage(
    metadata=b"...",  # dtype, shape, byte order, and TensorDict structure
    buffers=[memoryview(cpu_buffer).cast("B")],
    version=1,
)

Clear-text remote connections use a pooled raw TCP path by default. It sends the header, metadata, and original buffers with vectored I/O and reads every payload directly into its final receive allocation. TLS and PULSING_TENSOR_TRANSPORT=http2 use the packed HTTP/2 compatibility path.

See the complete transport design and the runnable TCP example.

πŸ“š Example Guide

examples/
β”œβ”€β”€ quickstart/              # ⭐ 5-minute quickstart
β”‚   └── hello_agent.py       #    First Agent
β”œβ”€β”€ agent/
β”‚   β”œβ”€β”€ pulsing/             # ⭐⭐ Multi-Agent apps
β”‚   β”‚   β”œβ”€β”€ mbti_discussion.py      # MBTI personality discussion
β”‚   β”‚   └── parallel_ideas_async.py # Parallel idea generation
β”‚   β”œβ”€β”€ autogen/             # AutoGen integration
β”‚   └── langgraph/           # LangGraph integration
β”œβ”€β”€ python/                  # ⭐⭐ Basic examples
β”‚   β”œβ”€β”€ ping_pong.py         #    Actor basics
β”‚   β”œβ”€β”€ cluster.py           #    Cluster communication
β”‚   β”œβ”€β”€ tensor_message_fast_path.py # Tensor transport
β”‚   └── ...
└── rust/                    # Rust examples

πŸ”§ Technical Features

  • Zero external dependencies: Pure Rust + Tokio, no NATS/etcd/Redis needed
  • Gossip protocol: Built-in SWIM protocol for node discovery and failure detection
  • Location transparency: Same API for local and remote Actors
  • Streaming messages: Native support for streaming requests/responses (LLM-ready)
  • Type safety: Rust Behavior API provides compile-time message type checking

πŸ“¦ Project Structure

Pulsing/
β”œβ”€β”€ crates/                   # Rust core
β”‚   β”œβ”€β”€ pulsing-actor/        #   Actor System
β”‚   └── pulsing-py/           #   Python bindings
β”œβ”€β”€ python/pulsing/           # Python package
β”‚   β”œβ”€β”€ actor/                #   Actor API
β”‚   β”œβ”€β”€ agent/                #   Agent toolkit
β”‚   β”œβ”€β”€ autogen/              #   AutoGen integration
β”‚   └── langgraph/            #   LangGraph integration
β”œβ”€β”€ examples/                 # Example code
└── docs/                     # Documentation

πŸ› οΈ Development

Prerequisites

  • Rust β‰₯ 1.75
  • Python β‰₯ 3.10
  • uv (recommended package manager)
  • just (task runner: cargo install just or brew install just)

Quick Setup

# 1. Install Python dependencies
uv sync --extra dev

# 2. Compile Rust core and install (run again after any Rust changes)
uv run maturin develop

Common Commands

just dev          # Compile and install in development mode
just test         # Run all tests (Rust + Python)
just test-python  # Python tests only
just fmt          # Format code (Rust + Python)
just lint         # Lint check
just check        # Full pre-commit check (format + lint + test)
just cov          # Generate coverage report

See CONTRIBUTING.md for a detailed guide on the development workflow.

πŸ“„ License

Apache-2.0

About

Pulsing is a distributed actor framework that provides a communication backbone for building distributed systems, with specialized support for AI applications.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages