Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Pulsing GitHub Discussions
url: https://github.com/reiase/Pulsing/discussions
url: https://github.com/DeepLink-org/Pulsing/discussions
about: Ask questions and discuss ideas in GitHub Discussions.
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@
- Python 绑定
- 基础文档和示例

[Unreleased]: https://github.com/reiase/pulsing/compare/v0.1.0...HEAD
[0.1.0]: https://github.com/reiase/pulsing/releases/tag/v0.1.0
[Unreleased]: https://github.com/DeepLink-org/Pulsing/compare/v0.1.0...HEAD
[0.1.0]: https://github.com/DeepLink-org/Pulsing/releases/tag/v0.1.0
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

```bash
# 克隆仓库
git clone https://github.com/reiase/pulsing.git
git clone https://github.com/DeepLink-org/Pulsing.git
cd pulsing

# 安装 Python 依赖
Expand All @@ -38,7 +38,7 @@ pytest tests/
```bash
git clone https://github.com/YOUR_USERNAME/pulsing.git
cd pulsing
git remote add upstream https://github.com/reiase/pulsing.git
git remote add upstream https://github.com/DeepLink-org/Pulsing.git
```

### 3. 创建分支
Expand Down Expand Up @@ -136,4 +136,4 @@ pytest tests/actor_system/

## 问题?

如果你有任何问题,请通过 [GitHub Issues](https://github.com/reiase/pulsing/issues) 联系我们。
如果你有任何问题,请通过 [GitHub Issues](https://github.com/DeepLink-org/Pulsing/issues) 联系我们。
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ edition = "2021"
description = "Pulsing - Distributed Actor Framework"
authors = ["Reiase <reiase@gmail.com>"]
license = "Apache-2.0"
repository = "https://github.com/reiase/pulsing"
repository = "https://github.com/DeepLink-org/Pulsing"
keywords = ["actor", "distributed", "async", "inference"]

[workspace.dependencies]
Expand Down
12 changes: 9 additions & 3 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -203,12 +203,18 @@ ci-test:
# Install wheel and dependencies using uv (preferred) or pip
if command -v uv &> /dev/null; then
uv pip install --system dist/*.whl pytest pytest-asyncio
# Use uv run pytest (uses uv-managed Python environment)
uv run pytest tests/python -v
# Use same interpreter as above (where wheel was installed); do not use uv run (project venv has no pulsing)
for py in python3.12 python3.11 python3.10 python3 python; do
if command -v $py &> /dev/null; then
$py -m pytest tests/python -v
exit 0
fi
done
echo "Error: No Python interpreter found"
exit 1
else
# Fallback to pip if uv not available
pip install dist/*.whl pytest pytest-asyncio
# Try to find python executable
for py in python3 python3.12 python3.11 python3.10 python; do
if command -v $py &> /dev/null; then
$py -m pytest tests/python -v
Expand Down
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,19 @@

**[中文文档](README.zh.md)**

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

🚀 **Zero Dependencies** — Pure Rust + Tokio, no NATS/etcd/Redis
**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`.

🌐 **Auto Discovery** — Built-in Gossip protocol for cluster management
🚀 **Zero Dependencies** — Pure Rust + Tokio, no NATS/etcd/Redis

🔀 **Location Transparent** — Same API for local and remote Actors
**Streaming-first** — Native support for streaming responses, built for LLM token generation

**Streaming Ready** — Native support for LLM streaming responses
🌐 **Built-in Discovery** — SWIM/Gossip protocol for automatic cluster management

🤖 **Agent Friendly** — Integrates with AutoGen, LangGraph out of the box
🔀 **Same API Everywhere** — Same `await actor.method()` for local and remote Actors

## 🚀 Get Started in 5 Minutes

Expand Down
14 changes: 8 additions & 6 deletions README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,19 @@

**[English](README.md)**

**Pulsing 是一个分布式 actor 框架,为构建分布式系统提供通信骨干,并为 AI 应用提供专门支持。**
**分布式 AI 系统的通信骨干。**

🚀 **零外部依赖** — 纯 Rust + Tokio,无需 NATS/etcd/Redis
**Actor 运行时。流式优先。零依赖。内置发现。**

Pulsing 是一个用 Rust 构建、为 Python 设计的分布式 Actor 运行时。跨机器连接 AI Agent 和服务——不需要 Redis,不需要 etcd,不需要 YAML。只需 `pip install pulsing`。

🌐 **自动发现** — 内置 Gossip 协议管理集群
🚀 **零外部依赖** — 纯 Rust + Tokio,无需 NATS/etcd/Redis

🔀 **位置透明** — 本地和远程 Actor 使用相同 API
**流式优先** — 原生流式响应支持,为 LLM token 生成而设计

**流式支持** — 原生支持 LLM 流式响应
🌐 **内置发现** — SWIM/Gossip 协议实现自动集群管理

🤖 **Agent 友好** — 开箱即用集成 AutoGen、LangGraph
🔀 **统一 API** — 本地和远程 Actor 使用相同的 `await actor.method()`

## 🚀 5分钟快速体验

Expand Down
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@

## 贡献

欢迎社区贡献!如果你对某个功能感兴趣,请在 [GitHub Issues](https://github.com/reiase/pulsing/issues) 中讨论。
欢迎社区贡献!如果你对某个功能感兴趣,请在 [GitHub Issues](https://github.com/DeepLink-org/Pulsing/issues) 中讨论。

## 参考

Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
### 报告方式

1. **GitHub Private Vulnerability Reporting**(推荐)
- 访问 [Security Advisories](https://github.com/reiase/pulsing/security/advisories)
- 访问 [Security Advisories](https://github.com/DeepLink-org/Pulsing/security/advisories)
- 点击 "Report a vulnerability"

2. **邮件**
Expand Down
9 changes: 7 additions & 2 deletions benchmarks/baseline_throughput.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@
import time

import pulsing as pul
from pulsing.queue import read_queue, write_queue
from pulsing.topic import PublishMode, read_topic, write_topic
from pulsing.streaming import (
read_queue,
write_queue,
PublishMode,
read_topic,
write_topic,
)


def _percentile(sorted_data: list[float], p: float) -> float:
Expand Down
9 changes: 7 additions & 2 deletions benchmarks/concurrency_sweep.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@
import time

import pulsing as pul
from pulsing.queue import read_queue, write_queue
from pulsing.topic import PublishMode, read_topic, write_topic
from pulsing.streaming import (
read_queue,
write_queue,
PublishMode,
read_topic,
write_topic,
)


# =============================================================================
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/large_scale_stress_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from dataclasses import dataclass, field

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

Check notice

Code scanning / CodeQL

Unused import Note test

Import of 'SystemConfig' is not used.


# ============================================================================
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/large_scale_stress_test_pulsing_single.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from dataclasses import dataclass, field

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

Check notice

Code scanning / CodeQL

Unused import Note test

Import of 'SystemConfig' is not used.


# ============================================================================
Expand Down
4 changes: 2 additions & 2 deletions benchmarks/queue_benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
from dataclasses import dataclass, field

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

Check notice

Code scanning / CodeQL

Unused import Note test

Import of 'SystemConfig' is not used.
from pulsing.streaming import read_queue, write_queue


# ============================================================================
Expand Down
9 changes: 7 additions & 2 deletions benchmarks/stress_multiprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@
from multiprocessing import Queue

import pulsing as pul
from pulsing.queue import read_queue, write_queue
from pulsing.topic import PublishMode, read_topic, write_topic
from pulsing.streaming import (
read_queue,
write_queue,
PublishMode,
read_topic,
write_topic,
)


# =============================================================================
Expand Down
2 changes: 1 addition & 1 deletion crates/pulsing-actor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2021"
description = "Lightweight distributed actor framework for Pulsing"
authors.workspace = true
license.workspace = true
repository = "https://github.com/reiase/pulsing"
repository = "https://github.com/DeepLink-org/Pulsing"
keywords = ["actor", "distributed", "cluster", "gossip"]

[features]
Expand Down
2 changes: 1 addition & 1 deletion crates/pulsing-actor/src/supervision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub enum RestartPolicy {
Always,
/// Restart the actor only if it failed (non-normal exit)
OnFailure,
/// Never restart the actor (default). Panic / 不可恢复错误时停止且不恢复
/// Never restart the actor (default). Stop and don't recover on panic / unrecoverable errors
#[default]
Never,
}
Expand Down
2 changes: 1 addition & 1 deletion crates/pulsing-actor/src/system/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ mod tests {
assert!(err.to_string().contains("head_node"));
}

// --- 配置解析 ---
// --- Configuration Parsing ---

#[test]
fn test_config_with_seeds() {
Expand Down
2 changes: 1 addition & 1 deletion crates/pulsing-actor/src/system/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub(crate) async fn run_actor_instance<A: Actor>(
responder.send(Ok(response));
}
Err(e) => {
// 业务错误:receive 返回 Err,只把错误返回给调用者,actor 继续处理下一条消息
// Business error: receive returns Err, only return error to caller, actor continues processing next message
tracing::warn!(actor_id = ?ctx.id(), error = %e, "Receive returned error (returned to caller)");
responder.send(Err(e));
}
Expand Down
6 changes: 3 additions & 3 deletions crates/pulsing-actor/src/transport/http2/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -822,7 +822,7 @@ mod tests {
assert!(Arc::ptr_eq(&client.pool, &cloned.pool));
}

// --- 连接管理 ---
// --- Connection Management ---

#[test]
fn test_client_pool_and_stats() {
Expand Down Expand Up @@ -860,7 +860,7 @@ mod tests {
client.shutdown();
}

// --- 错误恢复:对不可达地址应返回连接错误 ---
// --- Error Recovery: should return connection error for unreachable addresses ---

#[tokio::test]
async fn test_ask_connection_error() {
Expand Down Expand Up @@ -894,7 +894,7 @@ mod tests {
);
}

// --- 错误植入 ---
// --- Fault Injection ---

#[tokio::test]
async fn test_fault_injector_ask() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ mod error_tests {
assert!(result.is_err());
assert_eq!(crash_count.load(Ordering::SeqCst), 1);

// Actor 仍存活,后续消息应正常处理
// Actor still alive, subsequent messages should be processed normally
let result2: Result<Pong, _> = actor_ref.ask(Ping { value: 42 }).await;
assert!(
result2.is_ok(),
Expand Down
2 changes: 1 addition & 1 deletion crates/pulsing-actor/tests/unit/actor/actor_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ mod error_tests {
let result: Result<StateResponse, _> = actor_ref.ask(ErrorMessage).await;
assert!(result.is_err());

// receive 返回 Err 时只把错误返回给调用者,actor 不退出
// When receive returns Err, only return error to caller, actor doesn't exit
let result2: Result<Pong, _> = actor_ref.ask(Ping { value: 1 }).await;
assert!(
result2.is_ok(),
Expand Down
16 changes: 8 additions & 8 deletions crates/pulsing-actor/tests/unit/system/supervision_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ async fn test_restart_on_failure() {
let resp = actor_ref.send(Message::single("ping", b"1")).await;
assert!(resp.is_ok());

// 2nd message - receive 返回 Err,错误返回给调用者,actor 不退出、不重启
// 2nd message - receive returns Err, error returned to caller, actor doesn't exit or restart
let resp = actor_ref.send(Message::single("ping", b"2")).await;
assert!(resp.is_err());

// 3rd message - 同一实例仍存活,继续处理
// 3rd message - same instance still alive, continues processing
let resp = actor_ref.send(Message::single("ping", b"3")).await;
assert!(resp.is_ok());

Expand All @@ -80,7 +80,7 @@ async fn test_restart_on_failure() {

#[tokio::test]
async fn test_max_restarts_exceeded() {
// receive 返回 Err 不会导致 actor 退出,因此不会触发 restartfactory 只被调用一次
// receive returning Err doesn't cause actor to exit, so no restart is triggered; factory only called once
let system = ActorSystem::new(SystemConfig::standalone()).await.unwrap();
let counter = Arc::new(AtomicU32::new(0));

Expand All @@ -89,7 +89,7 @@ async fn test_max_restarts_exceeded() {
counter_clone.fetch_add(1, Ordering::SeqCst);
Ok(FailingActor {
counter: Arc::new(AtomicU32::new(0)),
fail_at: 1, // 第 1 条消息返回 Err
fail_at: 1, // 1st message returns Err
})
};

Expand All @@ -110,13 +110,13 @@ async fn test_max_restarts_exceeded() {
.await
.unwrap();

// 第 1 条消息:receive 返回 Err,只回传错误,actor 不退出
// 1st message: receive returns Err, only return error to caller, actor doesn't exit
let r1 = actor_ref.send(Message::single("ping", b"1")).await;
assert!(r1.is_err());
assert_eq!(counter.load(Ordering::SeqCst), 1); // factory 只调用 1 次
assert_eq!(counter.load(Ordering::SeqCst), 1); // factory only called once

// 第 2 条消息:同一实例,count=2 != fail_at(1),返回 Ok
// 2nd message: same instance, count=2 != fail_at(1), returns Ok
let r2 = actor_ref.send(Message::single("ping", b"2")).await;
assert!(r2.is_ok());
assert_eq!(counter.load(Ordering::SeqCst), 1); // 无重启
assert_eq!(counter.load(Ordering::SeqCst), 1); // no restart
}
1 change: 1 addition & 0 deletions crates/pulsing-py/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ async-trait = { workspace = true }
futures = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
bincode = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true }
tracing = { workspace = true }
Expand Down
Loading
Loading