Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a1f7780
Update README and documentation to reflect changes in actor command s…
reiase Jan 18, 2026
ee78a65
Add pulsing examples for multi-agent workflows and chaos-proof functi…
reiase Jan 23, 2026
edec412
Refactor actor spawning to improve clarity and consistency
reiase Jan 23, 2026
c1f3fce
Add LLMs binding documentation and update API references
reiase Jan 23, 2026
9ee5789
Refactor actor system initialization to use pul.actor_system
reiase Jan 24, 2026
4fac2d5
Add new API methods and enhance documentation in llms.binding.md
reiase Jan 24, 2026
be6bcdf
Enhance ActorClass API and add comprehensive tests
reiase Jan 24, 2026
d61b186
Add comprehensive Actor behavior documentation in llms.binding.md
reiase Jan 24, 2026
dcc1cf1
Add generator support to PyActorResponse for streaming values
reiase Jan 24, 2026
4ee2dbb
Implement synchronous and asynchronous generator support in Actor API
reiase Jan 24, 2026
600425d
Refactor Actor classes to remove dependency on Actor base class
reiase Jan 24, 2026
2878335
Update documentation and examples to reflect changes in actor resolut…
reiase Jan 24, 2026
5404820
Add Python API Contract section to documentation
reiase Jan 24, 2026
9c98400
Enhance Actor System API and documentation
reiase Jan 24, 2026
25f6f41
Refactor Actor System API for improved spawning and resolution
reiase Jan 24, 2026
8c17ccd
Enhance actor resolution capabilities in Actor System API
reiase Jan 24, 2026
f74d733
Refactor actor naming conventions and enhance actor resolution
reiase Jan 24, 2026
a4d0a1a
Enhance behavior-based actor support and API consistency
reiase Jan 24, 2026
c6d95c9
Refactor actor naming and improve documentation consistency
reiase Jan 24, 2026
b0a2a2d
docs cleanup
reiase Jan 24, 2026
fb493dd
Enhance actor system with improved cache management and graceful shut…
reiase Jan 24, 2026
7fad4c1
Add path length and namespace validation to ActorPath
reiase Jan 24, 2026
1b6b58c
Refactor actor management in Actor System for improved indexing and l…
reiase Jan 24, 2026
3f4ceed
Enhance actor stopping mechanism with Python compatibility
reiase Jan 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@
**Lightweight distributed framework designed for high-performance AI applications.**

🚀 **Zero Dependencies** — Pure Rust + Tokio, no NATS/etcd/Redis

🌐 **Auto Discovery** — Built-in Gossip protocol for cluster management

🔀 **Location Transparent** — Same API for local and remote Actors

⚡ **Streaming Ready** — Native support for LLM streaming responses

🤖 **Agent Friendly** — Integrates with AutoGen, LangGraph out of the box

## 🚀 Get Started in 5 Minutes
Expand All @@ -27,10 +31,10 @@ pip install pulsing

```python
import asyncio
from pulsing.actor import remote, resolve
import pulsing as pul
from pulsing.agent import runtime

@remote
@pul.remote
class Greeter:
def __init__(self, display_name: str):
self.display_name = display_name
Expand All @@ -39,7 +43,8 @@ class Greeter:
return f"[{self.display_name}] Received: {message}"

async def chat_with(self, peer_name: str, message: str) -> str:
peer = await resolve(peer_name)
# 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():
Expand All @@ -55,7 +60,7 @@ async def main():
asyncio.run(main())
```

**That's it!** `@remote` turns a regular class into a distributed Actor, and `resolve()` enables agents to discover and communicate with each other.
**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...

Expand Down Expand Up @@ -129,10 +134,10 @@ Out-of-the-box GPU cluster inference:

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

# Start vLLM Worker (can have multiple)
pulsing actor vllm --model Qwen/Qwen2.5-0.5B --addr 0.0.0.0:8002 --seeds 127.0.0.1:8000
pulsing actor pulsing.actors.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 \
Expand Down
17 changes: 11 additions & 6 deletions README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@
**轻量级分布式框架,专为高性能 AI 应用设计。**

🚀 **零外部依赖** — 纯 Rust + Tokio,无需 NATS/etcd/Redis

🌐 **自动发现** — 内置 Gossip 协议管理集群

🔀 **位置透明** — 本地和远程 Actor 使用相同 API

⚡ **流式支持** — 原生支持 LLM 流式响应

🤖 **Agent 友好** — 开箱即用集成 AutoGen、LangGraph

## 🚀 5分钟快速体验
Expand All @@ -27,10 +31,10 @@ pip install pulsing

```python
import asyncio
from pulsing.actor import remote, resolve
import pulsing as pul
from pulsing.agent import runtime

@remote
@pul.remote
class Greeter:
def __init__(self, display_name: str):
self.display_name = display_name
Expand All @@ -39,7 +43,8 @@ class Greeter:
return f"[{self.display_name}] 收到: {message}"

async def chat_with(self, peer_name: str, message: str) -> str:
peer = await resolve(peer_name)
# 使用 Greeter.resolve() 获取有类型的代理
peer = await Greeter.resolve(peer_name)
return await peer.greet(f"来自 {self.display_name}: {message}")

async def main():
Expand All @@ -55,7 +60,7 @@ async def main():
asyncio.run(main())
```

**就这么简单!** `@remote` 让普通类变成可分布式部署的 Actor,`resolve()` 让 Agent 互相发现和通信。
**就这么简单!** `@pul.remote` 让普通类变成可分布式部署的 Actor,`Greeter.resolve()` 让 Agent 互相发现和通信。

## 💡 我想做...

Expand Down Expand Up @@ -129,10 +134,10 @@ async with runtime(addr="0.0.0.0:8002", seeds=["node1:8001"]):

```bash
# 启动 Router(OpenAI 兼容 API)
pulsing actor router --addr 0.0.0.0:8000 --http_port 8080 --model_name my-llm
pulsing actor pulsing.actors.Router --addr 0.0.0.0:8000 --http_port 8080 --model_name my-llm

# 启动 vLLM Worker(可多个)
pulsing actor vllm --model Qwen/Qwen2.5-0.5B --addr 0.0.0.0:8002 --seeds 127.0.0.1:8000
pulsing actor pulsing.actors.VllmWorker --model Qwen/Qwen2.5-0.5B --addr 0.0.0.0:8002 --seeds 127.0.0.1:8000

# 测试
curl http://localhost:8080/v1/chat/completions \
Expand Down
14 changes: 8 additions & 6 deletions benchmarks/large_scale_stress_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
from collections import defaultdict
from dataclasses import dataclass, field

from pulsing.actor import Actor, StreamMessage, SystemConfig, create_actor_system
import pulsing as pul
from pulsing.actor import Actor, StreamMessage, SystemConfig

Check notice

Code scanning / CodeQL

Unused import Note test

Import of 'SystemConfig' is not used.

Copilot Autofix

AI 27 days ago

To fix an unused import, the general approach is to remove the unused name from the import statement (or delete the entire import if nothing in it is used). This preserves all existing functionality while cleaning up unnecessary dependencies.

In this case, in benchmarks/large_scale_stress_test.py, the line from pulsing.actor import Actor, StreamMessage, SystemConfig should be modified so that it only imports the names that are actually used: Actor and StreamMessage. Specifically, on line 21, remove SystemConfig from the imported names, resulting in from pulsing.actor import Actor, StreamMessage. No other code changes are required, and no additional methods, imports, or definitions are needed, since SystemConfig is not referenced elsewhere in the provided snippet.

Suggested changeset 1
benchmarks/large_scale_stress_test.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/benchmarks/large_scale_stress_test.py b/benchmarks/large_scale_stress_test.py
--- a/benchmarks/large_scale_stress_test.py
+++ b/benchmarks/large_scale_stress_test.py
@@ -18,7 +18,7 @@
 from dataclasses import dataclass, field
 
 import pulsing as pul
-from pulsing.actor import Actor, StreamMessage, SystemConfig
+from pulsing.actor import Actor, StreamMessage
 
 
 # ============================================================================
EOF
@@ -18,7 +18,7 @@
from dataclasses import dataclass, field

import pulsing as pul
from pulsing.actor import Actor, StreamMessage, SystemConfig
from pulsing.actor import Actor, StreamMessage


# ============================================================================
Copilot is powered by AI and may make mistakes. Always verify output.


# ============================================================================
Expand Down Expand Up @@ -268,14 +269,15 @@

# Configure system
port = (8000 if args.port == 0 else args.port) + rank
config = SystemConfig.with_addr(f"0.0.0.0:{port}")
addr = f"0.0.0.0:{port}"

seeds = None
if args.seed_nodes:
config = config.with_seeds(args.seed_nodes)
seeds = args.seed_nodes
elif rank > 0:
config = config.with_seeds([f"127.0.0.1:{8000 + rank - 1}"])
seeds = [f"127.0.0.1:{8000 + rank - 1}"]

system = await create_actor_system(config)
system = await pul.actor_system(addr=addr, seeds=seeds)
print(f"System started at {system.addr}")

# Wait for cluster to stabilize
Expand All @@ -288,7 +290,7 @@
# Create local workers
worker_refs = {}
for name, cls in WORKERS.items():
ref = await system.spawn(f"{name}_{rank}", cls(), public=True)
ref = await system.spawn(cls(), name=f"{name}_{rank}", public=True)
worker_refs[name] = ref
print(f" Spawned {name}_{rank}")

Expand Down
7 changes: 4 additions & 3 deletions benchmarks/large_scale_stress_test_pulsing_single.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
from collections import defaultdict
from dataclasses import dataclass, field

from pulsing.actor import Actor, StreamMessage, SystemConfig, create_actor_system
import pulsing as pul
from pulsing.actor import Actor, StreamMessage, SystemConfig

Check notice

Code scanning / CodeQL

Unused import Note test

Import of 'SystemConfig' is not used.

Copilot Autofix

AI 27 days ago

To fix an unused import in Python, you remove the unused name from the import statement (or delete the entire import if none of its names are used). This decreases unnecessary dependencies and slightly improves readability.

In this case, we should edit benchmarks/large_scale_stress_test_pulsing_single.py at the import line currently reading from pulsing.actor import Actor, StreamMessage, SystemConfig. The detailed, minimal change is to remove SystemConfig from the imported names, leaving Actor and StreamMessage intact, as both are used by the worker classes shown. No additional methods, imports, or definitions are needed; we are only simplifying the existing import list. The rest of the file’s functionality remains unchanged.

Suggested changeset 1
benchmarks/large_scale_stress_test_pulsing_single.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/benchmarks/large_scale_stress_test_pulsing_single.py b/benchmarks/large_scale_stress_test_pulsing_single.py
--- a/benchmarks/large_scale_stress_test_pulsing_single.py
+++ b/benchmarks/large_scale_stress_test_pulsing_single.py
@@ -17,7 +17,7 @@
 from dataclasses import dataclass, field
 
 import pulsing as pul
-from pulsing.actor import Actor, StreamMessage, SystemConfig
+from pulsing.actor import Actor, StreamMessage
 
 
 # ============================================================================
EOF
@@ -17,7 +17,7 @@
from dataclasses import dataclass, field

import pulsing as pul
from pulsing.actor import Actor, StreamMessage, SystemConfig
from pulsing.actor import Actor, StreamMessage


# ============================================================================
Copilot is powered by AI and may make mistakes. Always verify output.


# ============================================================================
Expand Down Expand Up @@ -235,15 +236,15 @@
)
print(f"{'=' * 50}\n")

system = await create_actor_system(SystemConfig.with_addr(f"0.0.0.0:{args.port}"))
system = await pul.actor_system(addr=f"0.0.0.0:{args.port}")
print(f"System started at {system.addr}")

# Create workers
workers = {}
for name, cls in WORKERS.items():
workers[name] = []
for i in range(args.num_workers):
ref = await system.spawn(f"{name}_{i}", cls())
ref = await system.spawn(cls(), name=f"{name}_{i}")
workers[name].append(ref)
print(f"Created {args.num_workers} {name} workers")

Expand Down
12 changes: 7 additions & 5 deletions benchmarks/queue_benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
from collections import defaultdict
from dataclasses import dataclass, field

from pulsing.actor import SystemConfig, create_actor_system
import pulsing as pul
from pulsing.actor import SystemConfig

Check notice

Code scanning / CodeQL

Unused import Note test

Import of 'SystemConfig' is not used.

Copilot Autofix

AI 27 days ago

In general, the way to fix an unused import is to delete the specific import line (or the unused symbol from a multi-symbol import) so that only actually used dependencies remain. This keeps the code cleaner, avoids unnecessary coupling, and silences the static analysis warning.

For this file, the best fix without changing functionality is to remove the line that imports SystemConfig from pulsing.actor, since nothing in the benchmark appears to use it. We should leave all other imports untouched because they may be used elsewhere in the file. Concretely, in benchmarks/queue_benchmark.py, delete line 33 (from pulsing.actor import SystemConfig) and make no other code changes; the script will behave exactly as before, just without the redundant import.

Suggested changeset 1
benchmarks/queue_benchmark.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/benchmarks/queue_benchmark.py b/benchmarks/queue_benchmark.py
--- a/benchmarks/queue_benchmark.py
+++ b/benchmarks/queue_benchmark.py
@@ -30,7 +30,6 @@
 from dataclasses import dataclass, field
 
 import pulsing as pul
-from pulsing.actor import SystemConfig
 from pulsing.queue import read_queue, write_queue
 
 
EOF
@@ -30,7 +30,6 @@
from dataclasses import dataclass, field

import pulsing as pul
from pulsing.actor import SystemConfig
from pulsing.queue import read_queue, write_queue


Copilot is powered by AI and may make mistakes. Always verify output.
from pulsing.queue import read_queue, write_queue


Expand Down Expand Up @@ -353,17 +354,18 @@
else:
port = args.port + rank

config = SystemConfig.with_addr(f"0.0.0.0:{port}")
addr = f"0.0.0.0:{port}"

# Add seed nodes
seeds = None
if args.seed_nodes:
config = config.with_seeds(args.seed_nodes)
seeds = args.seed_nodes
elif rank > 0:
prev_port = 9000 + (rank - 1)
config = config.with_seeds([f"127.0.0.1:{prev_port}"])
seeds = [f"127.0.0.1:{prev_port}"]

# Create system
system = await create_actor_system(config)
system = await pul.actor_system(addr=addr, seeds=seeds)
print(f"[Process {rank}] ActorSystem started at {system.addr}")

# Wait for cluster to stabilize
Expand Down
27 changes: 27 additions & 0 deletions comparison_examples/pulsing_pingpong.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""
Pulsing Ping-Pong Example using @remote decorator
Same functionality as AutoGen version
"""

from pulsing.actor import remote, runtime


# Define Agent
@remote
class PingPongAgent:
async def ping(self, message: str) -> str:
return f"pong: {message}"


# Run
async def main():
async with runtime():
agent = await PingPongAgent.spawn(name="pingpong")
response = await agent.ping("hello")
print(f"Received: {response}")


if __name__ == "__main__":
import asyncio

asyncio.run(main())
59 changes: 59 additions & 0 deletions comparison_examples/pulsing_research.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""
Pulsing Multi-Agent Research Workflow using @agent decorator
Same functionality as AutoGen version
"""

from pulsing.actor import resolve
from pulsing.agent import agent, runtime


# Researcher Agent
@agent(role="Researcher", goal="Research topics")
class ResearcherAgent:
async def research(self, topic: str) -> list[str]:
return [
f"Research point 1 about {topic}",
f"Research point 2 about {topic}",
]


# Analyst Agent
@agent(role="Analyst", goal="Analyze research results")
class AnalystAgent:
async def analyze(self, points: list[str]) -> str:
combined = " ".join(points)
return f"Analysis: {combined[:50]}..."


# Reporter Agent
@agent(role="Reporter", goal="Write final report")
class ReporterAgent:
async def write(self, summary: str) -> str:
return f"Final Report:\n{summary}"


# Workflow
async def run_workflow():
async with runtime():
# Spawn agents with names
researcher = await ResearcherAgent.spawn(name="researcher")

Check warning

Code scanning / CodeQL

Variable defined multiple times Warning

This assignment to 'researcher' is unnecessary as it is
redefined
before this value is used.

Copilot Autofix

AI 27 days ago

In general, to fix “variable defined multiple times” when the first value is never used, you either remove the first assignment entirely or, if its right-hand side has important side effects, keep the call but drop the unused variable binding. The second assignment then becomes the only definition of the variable prior to its use.

For this specific code in comparison_examples/pulsing_research.py, we want to preserve any potential side effects of ResearcherAgent.spawn, AnalystAgent.spawn, and ReporterAgent.spawn while removing the redundant initial bindings. The best minimal-change fix is:

  • On lines 39–41, remove the variable targets (researcher =, analyst =, reporter =) and just call the asynchronous spawn methods with await, discarding the return values.
  • Leave the resolve(...) assignments on lines 44–46 unchanged so that researcher, analyst, and reporter are defined once, right before use.

No new imports or additional methods/definitions are needed; we only adjust these three lines within run_workflow.

Suggested changeset 1
comparison_examples/pulsing_research.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/comparison_examples/pulsing_research.py b/comparison_examples/pulsing_research.py
--- a/comparison_examples/pulsing_research.py
+++ b/comparison_examples/pulsing_research.py
@@ -36,9 +36,9 @@
 async def run_workflow():
     async with runtime():
         # Spawn agents with names
-        researcher = await ResearcherAgent.spawn(name="researcher")
-        analyst = await AnalystAgent.spawn(name="analyst")
-        reporter = await ReporterAgent.spawn(name="reporter")
+        await ResearcherAgent.spawn(name="researcher")
+        await AnalystAgent.spawn(name="analyst")
+        await ReporterAgent.spawn(name="reporter")
 
         # Resolve by name (automatic load balancing)
         researcher = await resolve("researcher")
EOF
@@ -36,9 +36,9 @@
async def run_workflow():
async with runtime():
# Spawn agents with names
researcher = await ResearcherAgent.spawn(name="researcher")
analyst = await AnalystAgent.spawn(name="analyst")
reporter = await ReporterAgent.spawn(name="reporter")
await ResearcherAgent.spawn(name="researcher")
await AnalystAgent.spawn(name="analyst")
await ReporterAgent.spawn(name="reporter")

# Resolve by name (automatic load balancing)
researcher = await resolve("researcher")
Copilot is powered by AI and may make mistakes. Always verify output.
analyst = await AnalystAgent.spawn(name="analyst")

Check warning

Code scanning / CodeQL

Variable defined multiple times Warning

This assignment to 'analyst' is unnecessary as it is
redefined
before this value is used.

Copilot Autofix

AI 27 days ago

In general, to fix “variable defined multiple times” where the first value is never used, you either remove the first assignment entirely (if it has no side effects) or keep the expression for its side effects and drop the unused variable binding.

Here, researcher, analyst, and reporter are each assigned twice: first via *.spawn(...) and then via resolve(...). The second assignments (from resolve) are the ones actually used later in the workflow. The spawn calls, however, may be necessary to create/register the agents, so they should likely remain, but their return values do not need to be stored since they are never used. The minimal, behavior-preserving fix is:

  • Keep the three spawn calls but do not assign their results to variables.
  • Leave the resolve assignments as they are, since those variables are used in the subsequent workflow.

Concretely, in comparison_examples/pulsing_research.py inside run_workflow, lines 39–41:

  • Replace:
    • researcher = await ResearcherAgent.spawn(name="researcher")
    • analyst = await AnalystAgent.spawn(name="analyst")
    • reporter = await ReporterAgent.spawn(name="reporter")
  • With:
    • await ResearcherAgent.spawn(name="researcher")
    • await AnalystAgent.spawn(name="analyst")
    • await ReporterAgent.spawn(name="reporter")

No new methods or imports are needed.

Suggested changeset 1
comparison_examples/pulsing_research.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/comparison_examples/pulsing_research.py b/comparison_examples/pulsing_research.py
--- a/comparison_examples/pulsing_research.py
+++ b/comparison_examples/pulsing_research.py
@@ -36,9 +36,9 @@
 async def run_workflow():
     async with runtime():
         # Spawn agents with names
-        researcher = await ResearcherAgent.spawn(name="researcher")
-        analyst = await AnalystAgent.spawn(name="analyst")
-        reporter = await ReporterAgent.spawn(name="reporter")
+        await ResearcherAgent.spawn(name="researcher")
+        await AnalystAgent.spawn(name="analyst")
+        await ReporterAgent.spawn(name="reporter")
 
         # Resolve by name (automatic load balancing)
         researcher = await resolve("researcher")
EOF
@@ -36,9 +36,9 @@
async def run_workflow():
async with runtime():
# Spawn agents with names
researcher = await ResearcherAgent.spawn(name="researcher")
analyst = await AnalystAgent.spawn(name="analyst")
reporter = await ReporterAgent.spawn(name="reporter")
await ResearcherAgent.spawn(name="researcher")
await AnalystAgent.spawn(name="analyst")
await ReporterAgent.spawn(name="reporter")

# Resolve by name (automatic load balancing)
researcher = await resolve("researcher")
Copilot is powered by AI and may make mistakes. Always verify output.
reporter = await ReporterAgent.spawn(name="reporter")

Check warning

Code scanning / CodeQL

Variable defined multiple times Warning

This assignment to 'reporter' is unnecessary as it is
redefined
before this value is used.

Copilot Autofix

AI 27 days ago

In general, to fix “variable defined multiple times” when the first definition is never used, you either remove the earlier assignment entirely or keep the expression as a standalone call if you still need its side effects, without assigning its result to the same variable.

In this file, the safest fix that preserves all possible side effects of spawn is:

  • Keep the three await <Agent>.spawn(name=...) calls.
  • Stop assigning their return values to researcher, analyst, and reporter, since those names are immediately rebound via resolve(...).
  • Optionally, one could remove the spawn calls entirely if the framework guarantees that resolve will implicitly spawn missing agents, but we cannot assume that from the snippet.

Concretely, in comparison_examples/pulsing_research.py:

  • On lines 39–41, remove the left-hand-side variables and the = so that they become simple awaited calls:
    • Change researcher = await ResearcherAgent.spawn(name="researcher") to await ResearcherAgent.spawn(name="researcher"), and similarly for analyst and reporter.
  • Leave the resolve(...) assignments on lines 44–46 unchanged.

No new imports or helper methods are required.

Suggested changeset 1
comparison_examples/pulsing_research.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/comparison_examples/pulsing_research.py b/comparison_examples/pulsing_research.py
--- a/comparison_examples/pulsing_research.py
+++ b/comparison_examples/pulsing_research.py
@@ -36,9 +36,9 @@
 async def run_workflow():
     async with runtime():
         # Spawn agents with names
-        researcher = await ResearcherAgent.spawn(name="researcher")
-        analyst = await AnalystAgent.spawn(name="analyst")
-        reporter = await ReporterAgent.spawn(name="reporter")
+        await ResearcherAgent.spawn(name="researcher")
+        await AnalystAgent.spawn(name="analyst")
+        await ReporterAgent.spawn(name="reporter")
 
         # Resolve by name (automatic load balancing)
         researcher = await resolve("researcher")
EOF
@@ -36,9 +36,9 @@
async def run_workflow():
async with runtime():
# Spawn agents with names
researcher = await ResearcherAgent.spawn(name="researcher")
analyst = await AnalystAgent.spawn(name="analyst")
reporter = await ReporterAgent.spawn(name="reporter")
await ResearcherAgent.spawn(name="researcher")
await AnalystAgent.spawn(name="analyst")
await ReporterAgent.spawn(name="reporter")

# Resolve by name (automatic load balancing)
researcher = await resolve("researcher")
Copilot is powered by AI and may make mistakes. Always verify output.

# Resolve by name (automatic load balancing)
researcher = await resolve("researcher")
analyst = await resolve("analyst")
reporter = await resolve("reporter")

# Execute workflow
research = await researcher.research("Quantum Computing")
analysis = await analyst.analyze(research)
report = await reporter.write(analysis)

print(report)


if __name__ == "__main__":
import asyncio

asyncio.run(run_workflow())
12 changes: 7 additions & 5 deletions crates/pulsing-actor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ let system2 = ActorSystem::builder()
.build()
.await?;

// 通过路径解析远程 Actor
let remote = system2.resolve_named("services/echo", None).await?;
// 通过名称解析远程 Actor
let remote = system2.resolve("services/echo").await?;
let resp: String = remote.ask("hello".to_string()).await?;
```

Expand All @@ -97,18 +97,20 @@ let resp: String = remote.ask("hello".to_string()).await?;
除了传统 Actor trait,还提供函数式 Behavior API:

```rust
use pulsing_actor::behavior::{stateful, BehaviorAction, BehaviorSpawner};
use pulsing_actor::behavior::{stateful, Behavior, BehaviorAction};
use pulsing_actor::prelude::*;

fn counter(initial: i32) -> Behavior<i32> {
stateful(initial, |count, n| {
stateful(initial, |count, n, _ctx| {
*count += n;
println!("count = {}", *count);
BehaviorAction::Same
})
}

let system = ActorSystem::builder().build().await?;
let counter_ref = system.spawn_behavior("counter", counter(0)).await?;
// Behavior 实现 IntoActor,可以直接传给 spawn_named
let counter_ref = system.spawn_named("actors/counter", counter(0)).await?;

counter_ref.tell(5).await?; // count = 5
counter_ref.tell(3).await?; // count = 8
Expand Down
Loading
Loading