Conversation
…tructure and remove deprecated features - Updated README files to clarify the usage of the `pulsing actor` command, now requiring full class paths for actor types. - Enhanced documentation for starting actors, including examples for the new Router and worker classes. - Removed references to the deprecated `pulsing actor list` command, replacing it with `pulsing inspect` for actor system observation. - Updated various examples and guides to align with the new command structure and improve user understanding.
…onality - Introduced `pulsing_pingpong.py` demonstrating a simple ping-pong interaction using the @Remote decorator. - Added `pulsing_research.py` showcasing a multi-agent research workflow with Researcher, Analyst, and Reporter agents. - Created `chaos_proof.py` to illustrate actor resilience with automatic restarts on failure, simulating task completion despite crashes. - Implemented `function_to_fleet.py` to demonstrate scaling functions into a fleet of workers for improved throughput. - Updated README with new examples and usage instructions for enhanced clarity and user guidance.
- Updated actor spawning calls across multiple files to use named parameters for better readability, ensuring the actor name is explicitly defined. - Adjusted print statements in various examples to enhance formatting consistency. - Enhanced the `spawn` method in the actor system to support anonymous actors and custom options, improving flexibility in actor management. - Refactored tests to align with the new actor spawning structure, ensuring comprehensive coverage and reliability in functionality.
- Introduced a new documentation file `llms.binding.md` detailing the Pulsing API reference for LLMs, including Python interface examples for Actor System and queue operations. - Updated existing API reference documentation (`api_reference.md` and `api_reference.zh.md`) to reflect the new `pul.actor_system` usage and provide clearer examples. - Revised example scripts for distributed counter and ping-pong interactions to align with the new API structure, enhancing clarity and usability. - Refactored queue documentation to standardize the usage of `system.queue.write` and `system.queue.read` methods, improving consistency across examples and guides. - Enhanced the README files to include updated usage instructions and examples for the new queue API, ensuring comprehensive user guidance.
- Replaced instances of create_actor_system with pul.actor_system across multiple benchmark and test files for consistency. - Updated actor system configuration to utilize named parameters for address and seed nodes, enhancing clarity in system setup. - Improved documentation and test cases to reflect the new actor system initialization method, ensuring comprehensive coverage and usability.
- Introduced `ray.shutdown()` to close the system gracefully. - Added `ray.is_initialized()` to check if the system is initialized. - Updated `ray.get()` to support both single and list ObjectRefs for result retrieval. - Introduced `ray.put()` for wrapping values as ObjectRefs for API compatibility. - Added `ray.wait()` to wait for multiple ObjectRefs to complete, enhancing functionality and usability.
- Added a `public` parameter to the `ActorClass` methods for actor visibility control. - Updated method signatures and documentation to reflect the new `public` argument. - Introduced new test files for API coverage, including tests for actor system creation, spawning, and resolving. - Created tests for both Ray-compatible and Ray-like APIs, ensuring robust functionality and compatibility. - Added fixtures and comprehensive test cases to validate actor behavior and interactions.
- Introduced detailed examples for basic and advanced Actor usage, including synchronous and asynchronous methods. - Added explanations for the @pul.remote decorator, message passing patterns, and Actor lifecycle management. - Included guidance on supervision and restart strategies, as well as streaming responses for Actors. - Enhanced clarity on method definitions and usage scenarios to improve user understanding of the Pulsing API.
- Introduced a new `Generator` variant in `PyActorResponse` to handle both synchronous and asynchronous Python generators. - Implemented logic to iterate over generators in the `Actor` trait, utilizing channels for streaming values. - Enhanced error handling for generator iteration, including checks for `StopIteration` and `StopAsyncIteration`. - Updated the actor's response handling to accommodate the new generator functionality, improving the API's versatility.
- Added `_SyncGeneratorStreamReader` to handle synchronous generator responses in the Actor framework. - Enhanced `_MethodCaller` to recognize stream messages and return appropriate stream readers. - Introduced `_handle_generator_result` method in `_WrappedActor` to manage generator results and provide streaming responses. - Removed outdated test files and added new tests for actor behavior, including comprehensive coverage for synchronous and asynchronous methods, message passing, and actor lifecycle management. - Updated documentation to reflect changes in actor behavior and streaming capabilities.
- Converted WorkerActor, DispatcherActor, CacheActor, and EchoActor to standalone classes, eliminating the need for the Actor base class. - Updated message handling to use dictionaries instead of Message objects for improved simplicity and clarity. - Enhanced the receive methods to support action-based message processing, allowing for more flexible interactions. - Adjusted example scripts to reflect the new class structures and message patterns, ensuring consistency across the codebase.
…ion and API usage - Removed references to `resolve()` in favor of `ClassName.resolve()` for clarity and consistency across documentation. - Updated example scripts to demonstrate the new actor resolution method, ensuring users can easily find and interact with existing actors. - Enhanced README and guide sections to provide clearer instructions on using the `@remote` decorator and actor spawning. - Adjusted code snippets to reflect the latest API changes, improving overall usability and understanding of the Pulsing framework.
- Introduced a new section in the API reference documentation detailing the user-facing contract for Pulsing's Python API, derived from `llms.binding.md`. - Added explanations for concurrency models, streaming, error handling, and trust boundaries to enhance user understanding. - Updated the Chinese API reference to include the same contract and semantics information, ensuring consistency across languages. - Made minor adjustments to the queue manager to support backward compatibility with node status checks.
- Introduced new traits for Actor System extensions: `ActorSystemCoreExt`, `ActorSystemAdvancedExt`, and `ActorSystemOpsExt`, providing core operations, factory-based spawning, and lifecycle management. - Updated the Rust API documentation to reflect the new trait structure, enhancing clarity on spawning, resolving, and actor management. - Added comprehensive examples for the new API methods in both English and Chinese documentation, ensuring consistency and usability. - Made minor adjustments to existing code and documentation for improved readability and backward compatibility.
- Updated the Actor System API to streamline actor spawning, introducing `spawn_named` for named actors and `spawn_anonymous` for anonymous actors. - Enhanced documentation to clarify the usage of new spawning methods and their implications for actor resolution. - Removed the `public` parameter from `SpawnOptions`, as all named actors are now resolvable by default. - Adjusted existing code and tests to reflect the new API structure, ensuring consistency and backward compatibility. - Improved examples in documentation to demonstrate the updated spawning and resolution patterns.
- Introduced a new `ResolveBuilder` for advanced actor resolution options, allowing users to specify load balancing policies, target specific nodes, and filter for alive instances. - Added `resolve_all_instances` method to retrieve all instances of a named actor, with optional filtering for alive actors. - Updated documentation in `llms.binding.md` to include new resolution methods and examples in both English and Chinese, improving clarity and usability. - Enhanced existing traits and methods to support the new resolution patterns, ensuring consistency across the API.
- Updated actor naming logic to ensure names follow the namespace/name format, improving consistency across the API. - Adjusted the Python actor service name to align with the new naming convention. - Enhanced tests to validate the new naming structure and ensure proper registration of system actors. - Updated documentation and test assertions to reflect changes in actor naming and resolution behavior.
- Introduced the `IntoActor` trait, allowing seamless integration of `Behavior<M>` with existing actor spawning methods (`spawn` and `spawn_named`). - Updated documentation and examples to reflect the new behavior spawning capabilities, ensuring clarity on usage patterns. - Refactored actor context and system references to support named actors, improving supervision and resolution mechanisms. - Enhanced tests to validate the new behavior functionality and ensure consistent actor interactions across the API.
- Updated actor naming conventions to use the `SYSTEM_ACTOR_PATH` instead of the deprecated `SYSTEM_ACTOR_LOCAL_NAME`, enhancing clarity in the API. - Adjusted tests and documentation to reflect the new naming structure, ensuring accurate references to system actors. - Improved formatting in code examples for better readability and consistency across the codebase.
…down capabilities - Implemented double-checked locking in `LazyActorRef` to prevent resolution storms during concurrent cache refreshes. - Introduced a cancellation token in `LocalActorHandle` for graceful actor shutdown, allowing actors to stop processing messages cleanly. - Added methods for cleaning up stale node load trackers in `ActorSystem`, improving memory management and preventing leaks. - Updated actor resolution method to `resolve_address` for clarity and consistency across the API. - Enhanced documentation and tests to reflect these changes, ensuring better understanding and usability of the actor system.
- Introduced new error variants in `AddressParseError` for path length and reserved namespace violations. - Implemented validation rules in `ActorPath::new` to enforce maximum path and segment lengths, and to prevent the use of reserved system namespaces. - Updated `ActorPath::new_system` to bypass namespace checks for internal use. - Enhanced tests to cover new validation scenarios, ensuring robust error handling for actor paths.
| 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
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 26 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 asynchronousspawnmethods withawait, discarding the return values. - Leave the
resolve(...)assignments on lines 44–46 unchanged so thatresearcher,analyst, andreporterare defined once, right before use.
No new imports or additional methods/definitions are needed; we only adjust these three lines within run_workflow.
| @@ -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") |
| async with runtime(): | ||
| # Spawn agents with names | ||
| researcher = await ResearcherAgent.spawn(name="researcher") | ||
| analyst = await AnalystAgent.spawn(name="analyst") |
Check warning
Code scanning / CodeQL
Variable defined multiple times Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 26 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
spawncalls but do not assign their results to variables. - Leave the
resolveassignments 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.
| @@ -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") |
| # Spawn agents with names | ||
| researcher = await ResearcherAgent.spawn(name="researcher") | ||
| analyst = await AnalystAgent.spawn(name="analyst") | ||
| reporter = await ReporterAgent.spawn(name="reporter") |
Check warning
Code scanning / CodeQL
Variable defined multiple times Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 26 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, andreporter, since those names are immediately rebound viaresolve(...). - Optionally, one could remove the
spawncalls entirely if the framework guarantees thatresolvewill 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")toawait ResearcherAgent.spawn(name="researcher"), and similarly foranalystandreporter.
- Change
- Leave the
resolve(...)assignments on lines 44–46 unchanged.
No new imports or helper methods are required.
| @@ -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") |
|
|
||
| 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
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 26 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.
| @@ -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 | ||
|
|
||
|
|
||
| # ============================================================================ |
|
|
||
| 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
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 26 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.
| @@ -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 | ||
|
|
||
|
|
||
| # ============================================================================ |
| """ | ||
|
|
||
| import pytest | ||
| import time |
Check notice
Code scanning / CodeQL
Unused import Note test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 26 days ago
In general, unused imports should be removed to keep the code clean and avoid unnecessary dependencies. Here, the time module is not referenced anywhere in the visible tests, so the best fix is simply to delete the import time line.
Concretely, in tests/python/apis/ray_compat/test_ray_compat_api.py, remove line 16 containing import time, leaving the pytest and ray imports unchanged. No other code changes, new methods, or additional imports are required, since the tests do not rely on time.
| @@ -13,7 +13,6 @@ | ||
| """ | ||
|
|
||
| import pytest | ||
| import time | ||
|
|
||
| from pulsing.compat import ray | ||
|
|
| # Setup in background loop | ||
| async def setup(): | ||
| from pulsing.actor import SystemConfig, create_actor_system | ||
| import pulsing as pul |
Check notice
Code scanning / CodeQL
Module is imported more than once Note test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 26 days ago
In general, to fix "module is imported more than once" issues, you remove redundant imports and rely on the existing import, unless you need a different alias or import style. Here, the file already imports pulsing as pul at the top (line 26), and the inner setup coroutine in test_sync_queue_standalone re-imports it with the same alias, which is unnecessary.
The best fix without changing functionality is to delete the inner import pulsing as pul inside the setup coroutine (around line 1013) and keep using pul as defined by the top-level import. The rest of the code in setup already uses pul and write_queue, read_queue exactly as imported at the top of the file, so no further changes are required. We must leave the from pulsing.queue import write_queue, read_queue inside setup intact, because write_queue and read_queue are not directly imported at the top level of the snippet’s function scope; however, even that is arguably redundant given the module-level import, but the static analysis complaint is specifically about pulsing, so we only remove that line.
| @@ -1010,7 +1010,6 @@ | ||
| try: | ||
| # Setup in background loop | ||
| async def setup(): | ||
| import pulsing as pul | ||
| from pulsing.queue import write_queue, read_queue | ||
|
|
||
| system = await pul.actor_system() |
|
|
||
| async def setup(): | ||
| from pulsing.actor import SystemConfig, create_actor_system | ||
| import pulsing as pul |
Check notice
Code scanning / CodeQL
Module is imported more than once Note test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 26 days ago
To fix the problem, remove the redundant imports inside the setup() coroutine and use the already imported names from the module scope. In general, a single import of a module or symbol per file is sufficient; additional imports of the same module/symbol in inner scopes should be removed unless they serve a specific purpose (e.g., conditional or optional dependency loading).
Concretely, in tests/python/test_queue.py, within test_sync_writer_reader_standalone, there is an inner coroutine:
async def setup():
import pulsing as pul
from pulsing.queue import write_queue, read_queue
...You should delete the two import lines inside setup(), leaving the body to use pul, write_queue, and read_queue from the module-level imports defined on lines 26–34. No other code changes are necessary: the names are already available in the function scope as they are defined at module level. This preserves all existing functionality while eliminating redundant imports.
| @@ -1077,8 +1077,6 @@ | ||
| try: | ||
|
|
||
| async def setup(): | ||
| import pulsing as pul | ||
| from pulsing.queue import write_queue, read_queue | ||
|
|
||
| system = await pul.actor_system() | ||
| writer = await write_queue( |
|
|
||
| async def setup(): | ||
| from pulsing.actor import SystemConfig, create_actor_system | ||
| import pulsing as pul |
Check notice
Code scanning / CodeQL
Module is imported more than once Note test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 26 days ago
In general, to fix a “module imported more than once” issue in Python, remove the redundant import and rely on the existing import that already brings the module or symbols into scope. If the inner code genuinely needs an import and there is no existing one, move that import to the top of the file; otherwise, just delete the duplicate.
For this specific file, we should remove the inner imports inside the setup coroutine within test_sync_reader_offset_standalone. At the top of tests/python/test_queue.py, the module already imports import pulsing as pul (line 26) and from pulsing.queue import (..., read_queue, write_queue) (lines 27–34). Therefore, inside setup, we can safely delete:
import pulsing as pul
from pulsing.queue import write_queue, read_queueand rely on the already-imported pul, write_queue, and read_queue. No other functional changes are required; the rest of the code will continue to work as before, since names defined by top-level imports are accessible inside nested functions in the same module.
| @@ -1144,8 +1144,6 @@ | ||
| try: | ||
|
|
||
| async def setup(): | ||
| import pulsing as pul | ||
| from pulsing.queue import write_queue, read_queue | ||
|
|
||
| system = await pul.actor_system() | ||
| writer = await write_queue( |
| assert count == 0 | ||
|
|
||
| # Wait for slow operation to complete | ||
| await slow_task |
Check notice
Code scanning / CodeQL
Statement has no effect Note test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 26 days ago
In general, to fix a “statement has no effect” issue where an awaited coroutine result is intentionally unused, either capture the result in a variable (optionally asserting/inspecting it) or explicitly assign it to _ to signal that it is intentionally ignored. This makes the side effect of “waiting for completion” explicit and resolves the static analysis warning.
For this specific test in tests/python/test_remote_decorator.py, the best minimal change without altering behavior is to bind the awaited value of slow_task to a variable. Optionally, we can assert on it, but that’s not required to preserve existing behavior. The key is to show that the awaited expression is being used.
Concretely:
- In
test_async_method_does_not_block_actor, at line 228, changeawait slow_taskto something likeresult = await slow_task. This preserves all functionality (we still wait for completion), makes the use of the expression explicit, and eliminates the “no effect” warning. No new imports or helper methods are required.
| @@ -225,7 +225,7 @@ | ||
| assert count == 0 | ||
|
|
||
| # Wait for slow operation to complete | ||
| await slow_task | ||
| result = await slow_task | ||
| await asyncio.sleep(0.01) # Let the count update propagate | ||
|
|
||
| count = await service.get_call_count() |
…ookup - Changed local actor storage from a string-based index to a u64 local_id for O(1) lookups, enhancing performance. - Introduced a mapping from actor names to local_ids to facilitate name-based lookups. - Updated methods for finding and managing actors to utilize the new indexing scheme, ensuring consistency and efficiency. - Enhanced documentation and tests to reflect the changes in actor management and lookup behavior.
- Updated the `stop_with_reason` method in `ActorSystem` to support actor names without a "/" by attempting to resolve them with an "actors/" prefix for better compatibility with Python API conventions. - Improved logging to reflect the actual name used for stopping the actor, ensuring accurate debugging information. - Enhanced documentation to clarify the new behavior and its implications for actor management.
Overview:
Details:
Where should the reviewer start?
Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)