{"id":24836714,"url":"https://github.com/runtime-blocks/crb","last_synced_at":"2025-08-20T23:13:26.334Z","repository":{"id":226726570,"uuid":"769491042","full_name":"runtime-blocks/crb","owner":"runtime-blocks","description":"CRB 🦀 transactional actors","archived":false,"fork":false,"pushed_at":"2025-01-23T19:06:30.000Z","size":1673,"stargazers_count":96,"open_issues_count":1,"forks_count":7,"subscribers_count":4,"default_branch":"trunk","last_synced_at":"2025-01-23T20:19:43.285Z","etag":null,"topics":["actors","agents","ai","async","coroutines","futures","pipeline","runtime","rust","threads","tokio","wasm"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/runtime-blocks.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-03-09T08:18:48.000Z","updated_at":"2025-01-23T19:06:34.000Z","dependencies_parsed_at":"2024-06-04T22:18:44.438Z","dependency_job_id":"1f4a572b-7cee-47f4-8513-f4d4aa52eddf","html_url":"https://github.com/runtime-blocks/crb","commit_stats":null,"previous_names":["opencrab/crb","rationalintelligence/crb","runtime-blocks/crb"],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runtime-blocks%2Fcrb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runtime-blocks%2Fcrb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runtime-blocks%2Fcrb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runtime-blocks%2Fcrb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/runtime-blocks","download_url":"https://codeload.github.com/runtime-blocks/crb/tar.gz/refs/heads/trunk","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":236464800,"owners_count":19152979,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["actors","agents","ai","async","coroutines","futures","pipeline","runtime","rust","threads","tokio","wasm"],"created_at":"2025-01-31T05:02:29.452Z","updated_at":"2025-01-31T05:03:35.219Z","avatar_url":"https://github.com/runtime-blocks.png","language":"Rust","readme":"\u003cimg src=\"https://runtime-blocks.github.io/website/repo/crb/assets/crb-header.png\" width=\"400px\" /\u003e\n\n# Composable Runtime Blocks\n\n[![Crates.io][crates-badge]][crates-url]\n[![MIT licensed][mit-badge]][mit-url]\n[![Documentation][docs-badge]][docs-url]\n\n[crates-badge]: https://img.shields.io/crates/v/crb.svg\n[crates-url]: https://crates.io/crates/crb\n[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg\n[mit-url]: https://github.com/runtime-blocks/crb/blob/master/LICENSE\n[docs-badge]: https://docs.rs/crb/badge.svg\n[docs-url]: https://docs.rs/crb\n\nA unique framework that implementes **hybrid workloads**, seamlessly combining synchronous and asynchronous activities, state machines, routines, the actor model, and supervisors.\n\nIt’s perfect for building massive applications and serves as an ideal low-level framework for creating your own frameworks, for example AI-agents.\nThe core idea is to ensure all blocks are highly compatible with each other, enabling significant code reuse.\n\n# What is a hybrid workload?\n\nA hybrid workload is a concurrent task capable of switching roles - it can function as a synchronous or asynchronous task, a finite state machine, or as an actor exchanging messages.\n\nThe implementation is designed as a **fully portable solution** that can run in a standard environment, a WASM virtual machine (e.g., in a browser), or a TEE enclave. This approach significantly reduces development costs by allowing you to reuse code across all parts of your application: backend, frontend, agents, and more.\n\n\u003cimg src=\"https://runtime-blocks.github.io/website/repo/crb/assets/crb-arch.png\" width=\"400px\" /\u003e\n\nThe key feature is its ability to combine the roles, enabling the implementation of algorithms with **complex branching** that would be impossible in the flat structure of a standard function. This makes it ideal for building the framework of large-scale applications or implementing complex workflows, such as AI pipelines.\n\n\u003cimg src=\"https://runtime-blocks.github.io/website/repo/crb/assets/crb-hybryd.png\" width=\"400px\" /\u003e\n\n# Projects\n\nThe following projects have been implemented using the framework:\n\n- [Ice-Nine](https://github.com/NethermindEth/ice-nine) - AI agents that work everywhere.\n- [Crateful](https://lab.knowledge.dev/) - Newsletter about AI agent development in Rust.\n- [Knowledge.Dev](https://knowledge.dev/) - An interactive book for learning practical, idiomatic Rust (product is entirely written in Rust).\n\n# Examples\n\nBelow, you'll find numerous examples of building hybrid activities using the framework. These examples are functional but simplified for clarity:\n\n- Type imports are omitted to keep examples clean and focused.\n- The `async_trait` macro is used but not explicitly shown, as exported traits with asynchronous methods are expected in the future.\n- `anyhow::Result` is used instead of the standard `Result` for simplicity.\n- Agent context is not specified, as it is always `AgentSession` in these cases.\n- The output type `Output` is omitted, as it is always `()` here and may become a default in the future.\n\nThe examples demonstrate various combinations of states and modes, but in reality, there are many more possibilities, allowing for any sequence or combination.\n\n### Before diving into the examples...\n\nTo create a universal hybrid activity, you need to define a structure and implement the `Agent` trait for it. By default, the agent starts in a reactive actor mode, ready to receive messages and interact with other actors.\n\nHowever, you can override this behavior by explicitly specifying the agent's initial state in the `begin()` method.\n\n```rust\npub struct Task;\n\nimpl Agent for Task {\n    fn begin(\u0026mut self) -\u003e Next\u003cSelf\u003e {\n        Next::do_async(()) // The next state\n    }\n}\n```\n\nThe next state is defined using the `Next` object, which provides various methods for controlling the state machine. To perform an asynchronous activity, use the `do_async()` method, passing the new state as a parameter (default is `()`).\n\nThen, implement the `DoAsync` trait for your agent by defining the asynchronous `once()` method:\n\n```rust\nimpl DoAsync for Task {\n    async fn once(\u0026mut self, _: \u0026mut ()) -\u003e Result\u003cNext\u003cSelf\u003e\u003e {\n        do_something().await?;\n        Ok(Next::done())\n    }\n}\n```\n\nThe result should specify the next state of the state machine. If you want to terminate the agent, simply return the termination state by calling the `done()` method on the `Next` type.\n\n## Asynchronous tasks\n\nThe simplest example is creating a task that performs an asynchronous activity and then terminates.\n\n```rust\npub struct Task;\n\nimpl Agent for Task {\n    fn begin(\u0026mut self) -\u003e Next\u003cSelf\u003e {\n        Next::do_async(())\n    }\n}\n\nimpl DoAsync for Task {\n    async fn once(\u0026mut self, _: \u0026mut ()) -\u003e Result\u003cNext\u003cSelf\u003e\u003e {\n        reqwest::get(\"https://www.rust-lang.org\").await?.text().await?;\n        Ok(Next::done())\n    }\n}\n```\n\n### Fallbacks\n\nUnlike standard asynchronous activities, you can implement the `fallback()` method to modify the course of actions in case of an error:\n\n```rust\nimpl DoAsync for Task {\n    async fn fallback(\u0026mut self, err: Error) -\u003e Next\u003cSelf\u003e {\n        log::error!(\"Can't load a page: {err}. Trying again...\");\n        Ok(Next::do_async(()))\n    }\n}\n```\n\nThe task will now repeatedly enter the same state until the loading process succeeds.\n\n### Repeated routines\n\nThe agent already implements a persistent routine in the `repeat()` method, which repeatedly attempts to succeed by calling the `once()` method. To achieve the same effect, we can simply override that method:\n\n```rust\nimpl DoAsync for Task {\n    async fn repeat(\u0026mut self, _: \u0026mut ()) -\u003e Result\u003cOption\u003cNext\u003cSelf\u003e\u003e\u003e {\n        reqwest::get(\"https://www.rust-lang.org\").await?.text().await?;\n        Ok(Some(Next::done()))\n    }\n}\n```\n\nThe `repeat()` method will continue to run until it returns the next state for the agent to transition to.\n\n## Synchronous tasks\n\nTo implement a synchronous task simply call the `do_sync()` method on `Next`:\n\n```rust\npub struct Task;\n\nimpl Agent for Task {\n    fn begin(\u0026mut self) -\u003e Next\u003cSelf\u003e {\n        Next::do_sync(())\n    }\n}\n```\n\nNext, implement the `DoSync` trait to run the task in a thread (either the same or a separate one, depending on the platform):\n\n```rust\nimpl DoSync for Task {\n    fn once(\u0026mut self, _: \u0026mut ()) -\u003e Result\u003cNext\u003cSelf\u003e\u003e {\n        let result: u64 = (1u64..=20).map(|x| x.pow(10)).sum();\n        println!(\"{result}\");\n        Ok(Next::done())\n    }\n}\n```\n\nIn the example, it calculates the sum of powers and prints the result to the terminal.\n\n\n## Multiple states (state-machines)\n\nInterestingly, you can define different states and implement unique behavior for each, whether synchronous or asynchronous. This gives you both a state machine and varied execution contexts without the need for manual process management.\n\nLet’s create an agent that prints the content of a webpage to the terminal. The first state the agent should transition to is `GetPage`, which includes the URL of the page to be loaded. This state will be asynchronous, so call the `do_async()` method with the `Next` state.\n\n```rust\npub struct Task;\n\nimpl Agent for Task {\n    fn begin(\u0026mut self) -\u003e Next\u003cSelf\u003e {\n        let url = \"https://www.rust-lang.org\".into();\n        Next::do_async(GetPage { url })\n    }\n}\n```\n\n### Asynchronous state\n\nImplement the `GetPage` state by defining the corresponding structure and using it in the `DoAsync` trait implementation for our agent `Task`:\n\n```rust\nstruct GetPage { url: String }\n\nimpl DoAsync\u003cGetPage\u003e for Task {\n    async fn once(\u0026mut self, state: \u0026mut GetPage) -\u003e Result\u003cNext\u003cSelf\u003e\u003e {\n        let text = reqwest::get(state.url).await?.text().await?;\n        Ok(Next::do_sync(Print { text }))\n    }\n}\n```\n\nIn the `GetPage` state, the webpage will be loaded, and its content will be passed to the next state, `Print`, for printing. Since the next state is synchronous, it is provided as a parameter to the `do_sync()` method.\n\n### Synchronous state\n\nNow, let’s define the `Print` state as a structure and implement the `DoSync` trait for it:\n\n```rust\nstruct Print { text: String }\n\nimpl DoSync\u003cPrint\u003e for Task {\n    fn once(\u0026mut self, state: \u0026mut Print) -\u003e Result\u003cNext\u003cSelf\u003e\u003e {\n        printlnt!(\"{}\", state.text);\n        Ok(Next::done())\n    }\n}\n```\n\nThe result is a state machine with the flexibility to direct its workflow into different states, enabling the implementation of a robust and sometimes highly nonlinear algorithm that would be extremely difficult to achieve within a single asynchronous function.\n\n## Mutable states\n\nPreviously, we didn't modify the data stored in a state. However, states provide a convenient context for managing execution flow or collecting statistics **without cluttering the task's main structure**!\n\n```rust\npub struct Task;\n\nimpl Agent for Task {\n    fn begin(\u0026mut self) -\u003e Next\u003cSelf\u003e {\n        Next::do_async(Monitor)\n    }\n}\n\nstruct Monitor {\n    total: u64,\n    success: u64,\n}\n\nimpl DoAsync\u003cMonitor\u003e for Task {\n    async fn repeat(\u0026mut self, mut state: \u0026mut Monitor) -\u003e Result\u003cOption\u003cNext\u003cSelf\u003e\u003e\u003e {\n        state.total += 1;\n        reqwest::get(\"https://www.rust-lang.org\").await?.error_for_status()?;\n        state.success += 1;\n        sleep(Duration::from_secs(10)).await;\n        Ok(None)\n    }\n}\n```\n\nAbove is an implementation of a monitor that simply polls a website and counts successful attempts. It does this without modifying the `Task` structure while maintaining access to it.\n\n## Concurrent Task\n\nWithin an asynchronous activity, all standard tools for concurrently executing multiple `Futures` are available. For example, in the following code, several web pages are requested simultaneously using the `join_all()` function:\n\n```rust\npub struct ConcurrentTask;\n\nimpl Agent for ConcurrentTask {\n    fn begin(\u0026mut self) -\u003e Next\u003cSelf\u003e {\n        Next::do_async(())\n    }\n}\n\nimpl DoAsync for ConcurrentTask {\n    async fn once(\u0026mut self, _: \u0026mut ()) -\u003e Result\u003cNext\u003cSelf\u003e\u003e {\n        let urls = vec![\n            \"https://www.rust-lang.org\",\n            \"https://www.crates.io\",\n            \"https://crateful.substack.com\",\n            \"https://knowledge.dev\",\n        ];\n        let futures = urls.into_iter().map(|url| reqwest::get(url));\n        future::join_all(futures).await\n        Ok(Next::done())\n    }\n}\n```\n\nThis approach allows for more efficient utilization of the asynchronous runtime while maintaining the workflow without needing to synchronize the retrieval of multiple results.\n\n## Parallel Task\n\nAnother option is parallelizing computations. This is easily achieved by implementing a synchronous state. Since it runs in a separate thread, it doesn't block the asynchronous runtime, allowing other agents to continue executing in parallel.\n\n```rust\npub struct ParallelTask;\n\nimpl Agent for ParallelTask {\n    fn begin(\u0026mut self) -\u003e Next\u003cSelf\u003e {\n        Next::do_sync(())\n    }\n}\n\nimpl DoSync for ParallelTask {\n    fn once(\u0026mut self, _: \u0026mut ()) -\u003e Result\u003cNext\u003cSelf\u003e\u003e {\n        let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n        let squares = numbers.into_par_iter().map(|n| n * n).collect();\n        Ok(Next::done())\n    }\n}\n```\n\nIn the example above, parallel computations are performed using the `rayon` crate. The results are awaited asynchronously by the agent since `DoSync` shifts execution to a thread while continuing to wait for the result asynchronously.\n\n## Subtask Execution\n\nThe framework allows tasks to be reused within other tasks, offering great flexibility in structuring code.\n\n```rust\npub struct RunBoth;\n\nimpl Agent for RunBoth {\n    fn begin(\u0026mut self) -\u003e Next\u003cSelf\u003e {\n        Next::do_async(())\n    }\n}\n\nimpl DoAsync for RunBoth {\n    async fn once(\u0026mut self, _: \u0026mut ()) -\u003e Result\u003cNext\u003cSelf\u003e\u003e {\n        join!(\n            ConcurrentTask.run(),\n            ParallelTask.run(),\n        ).await;\n        Ok(Next::done())\n    }\n}\n```\n\nThe code example implementes an agent that waits for the simultaneous completion of two tasks we implemented earlier: **concurrent** and **parallel**.\n\n\n## Shared state in a state machine\n\nAlthough the states within a group inherently form a state machine, you can define it more explicitly by adding a field to the agent and describing the states with a dedicated `enum`:\n\n```rust\nenum State {\n    First,\n    Second,\n    Third,\n}\n\nstruct Task {\n    state: State,\n}\n```\n\nIn this case, the `State` enumeration can handle transitions between states. Transition rules can be implemented as a function that returns a `Next` instance with the appropriate state handler.\n\n```rust\nimpl State {\n    fn next(\u0026self) -\u003e Next\u003cTask\u003e {\n        match self {\n            State::First =\u003e Next::do_async(First),\n            State::Second =\u003e Next::do_async(Second),\n            State::Third =\u003e Next::do_async(Third),\n        }\n    }\n}\n```\n\nSet the initial state when creating the task (it can even be set in the constructor), and delegate state transitions to the `next()` function, called from the `begin()` method.\n\n```rust\nimpl Agent for Task {\n    fn begin(\u0026mut self) -\u003e Next\u003cSelf\u003e {\n        self.state.next()\n    }\n}\n```\n\nImplement all state handlers, with the `State` field determining the transition to the appropriate handler. Simply use its `next()` method so that whenever the state changes, the transition always leads to the correct handler.\n\n```rust\nstruct First;\n\nimpl DoAsync\u003cFirst\u003e for Task {\n    async fn once(\u0026mut self, _: \u0026mut First) -\u003e Result\u003cNext\u003cSelf\u003e\u003e {\n        self.state = State::Second;\n        Ok(self.state.next())\n    }\n}\n\nstruct Second;\n\nimpl DoAsync\u003cSecond\u003e for Task {\n    async fn once(\u0026mut self, _: \u0026mut Second) -\u003e Result\u003cNext\u003cSelf\u003e\u003e {\n        self.state = State::Third;\n        Ok(self.state.next())\n    }\n}\n\nstruct Third;\n\nimpl DoAsync\u003cThird\u003e for Task {\n    async fn once(\u0026mut self, _: \u0026mut Third) -\u003e Result\u003cNext\u003cSelf\u003e\u003e {\n        Ok(Next::done())\n    }\n}\n```\n\n💡 The framework is so flexible that it allows you to make this logic even more explicit by implementing a custom `Performer` for the agent.\n\n## Actor Model\n\nAgents handle messages asynchronously. Actor behavior is enabled by default if no transition state is specified in the `begin()` method. Alternatively, you can switch to this mode from any state by calling `Next::events()`, which starts message processing.\n\nIn other words, the actor state is the default for the agent, so simply implementing the `Agent` trait is enough:\n\n```rust\nstruct Actor;\n\nimpl Agent for Actor {}\n```\n\nAn actor can accept any data type as a message, as long as it implements the `OnEvent` trait for that type and its `handle()` method. For example, let's teach our actor to accept an `AddUrl` message, which adds an external resource:\n\n```rust\nstruct AddUrl { url: Url }\n\nimpl OnEvent\u003cAddUrl\u003e for Actor {\n    async handle(\u0026mut self, event: AddUrl, ctx: \u0026mut Context\u003cSelf\u003e) -\u003e Result\u003c()\u003e {\n        todo!()\n    }\n}\n```\n\nActors can implement handlers for any number of messages, allowing you to add as many `OnEvent` implementations as needed:\n\n```rust\nstruct DeleteUrl { url: Url }\n\nimpl OnEvent\u003cDeleteUrl\u003e for Actor {\n    async handle(\u0026mut self, event: DeleteUrl, ctx: \u0026mut Context\u003cSelf\u003e) -\u003e Result\u003c()\u003e {\n        todo!()\n    }\n}\n```\n\nThe provided context (`ctx`) allows you to send a message, terminate the actor, or transition to a new state by setting the next state with `Next`.\n\n\u003e State transitions have the highest priority. Even if there are messages in the queue, the state transition will occur first, and messages will wait until the agent returns to the actor state.\n\n## Interactions\n\nThe actor model is designed to let you add custom handler traits for any type of event. For example, this framework supports interactive actor interactions—special messages that include a request and a channel for sending a response.\n\nThe example below implements a server that reserves an `Id` in response to an interactive request and returns it:\n\n```rust\nstruct Server {\n    slab: Slab\u003cRecord\u003e,\n}\n\nstruct GetId;\n\nimpl Request for GetId {\n    type Response = usize;\n}\n\nimpl OnRequest\u003cGetId\u003e for Server {\n    async on_request(\u0026mut self, _: GetId, ctx: \u0026mut Context\u003cSelf\u003e) -\u003e Result\u003cusize\u003e {\n        let record = Record { ... };\n        Ok(self.slab.insert(record))\n    }\n}\n```\n\nThe request must implement the `Request` trait to specify the `Response` type. As you may have noticed, for the `OnRequest` trait, we implemented the `on_request()` method, which expects a response as the result. This eliminates the need to send it manually.\n\nThe following code implements a `Client` that configures itself by sending a request to the server to obtain an `Id` and waits for a response by implementing the `OnResponse` trait.\n\n```rust\nstruct Client {\n    server: Address\u003cServer\u003e,\n}\n\nimpl Agent for Client {\n    fn begin(\u0026mut self) -\u003e Next\u003cSelf\u003e {\n        Next::duty(Configure)\n    }\n}\n\nstruct Configure;\n\nimpl Duty\u003cConfigure\u003e for Client {\n    async fn once(\u0026mut self, _: \u0026mut Configure, ctx: \u0026mut Context\u003cSelf\u003e) -\u003e Result\u003cNext\u003cSelf\u003e\u003e {\n        self.server.request(GetId)?.forward_to(ctx)?;\n        Ok(Next::events())\n    }\n}\n\nimpl OnResponse\u003cGetId\u003e for Client {\n    async on_response(\u0026mut self, id: usize, ctx: \u0026mut Context\u003cSelf\u003e) -\u003e Result\u003c()\u003e {\n        println!(\"Reserved id: {id}\");\n        Ok(())\n    }\n}\n```\n\n## Supervisor\n\nAn actor (or any agent) can be launched from anywhere if it implements the `Standalone` trait. Otherwise, it can only be started within the context of a supervisor.\n\nThe supervisor can also manage all spawned tasks and terminate them in a specific order by implementing the `Supervisor` trait:\n\n```rust\nstruct App;\n\nimpl Agent for App {\n    type Context = SupervisorSession\u003cSelf\u003e;\n}\n\nimpl Supervior for App {\n    type Group = Group;\n}\n```\n\nThe `Group` type defines a grouping for tasks, where the order of its values determines the sequence in which child agents (tasks, actors) are terminated.\n\n```rust\n#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]\nenum Group {\n    Workers,\n    Requests,\n    Server,\n    HealthCheck,\n    DbCleaner,\n    UserSession(Uid),\n}\n```\n\n## Pipelines\n\nThe framework includes an experimental implementation of pipelines that automatically trigger tasks as they process input data from the previous stage.\n\nHowever, creating complex workflows is also possible using just the agent's core implementation.\n\n## Functional activities (`fn` or `Future` as tasks)\n\n\u003e todo\n\n# Key Advantages\n\n## WASM Compatibility\n\nOne of the library's major advantages is its out-of-the-box compatibility with WebAssembly (WASM). This allows you to write full-stack solutions in Rust while reusing the same codebase across different environments.\n\n\u003e Synchronous tasks are currently unavailable in WASM due to its lack of full thread support. However, using them in environments like browsers is generally unnecessary, as they block asynchronous operations.\n\n## Actor Model\n\nThe library includes a complete implementation of the actor model, enabling you to build a hierarchy of actors and facilitate message passing between them. When the application stops, actors gracefully shut down between messages processing phases, and in the specified order.\n\n## Synchronous Tasks\n\nThe framework supports not only asynchronous activities (IO-bound) but also allows running synchronous (CPU-bound) tasks using threads. The results of these tasks can seamlessly be consumed by asynchronous activities.\n\n## Pipelines\n\nThe library offers a Pipeline implementation compatible with actors, routines, and tasks (including synchronous ones), making it ideal for building AI processing workflows.\n\n## Trait-Based Design\n\nUnlike many actor frameworks, this library relies heavily on traits. For example, tasks like interactive communication, message handling, or `Stream` processing are implemented through specific trait implementations.\n\nMore importantly, the library is designed to be extensible, allowing you to define your own traits for various needs while keeping your code modular and elegant. For instance, actor interruption is implemented on top of this model.\n\n## Method Hierarchy\n\nTrait methods are designed and implemented so that you only need to define specific methods to achieve the desired behavior.\n\nAlternatively, you can fully override the behavior and method call order - for instance, customizing an actor’s behavior in any way you like or adding your own intermediate phases and methods.\n\n## Error Handling and Resilience\n\nThe library provides built-in error handling features, such as managing failures during message processing, making it easier to write robust and resilient applications.\n\n# Author\n\nThe project was originally created by [@therustmonk](https://github.com/therustmonk) as a result of extensive experimental research into implementing a hybrid actor model in Rust.\n\n\u003ca href=\"https://crateful.substack.com/\" target=\"_blank\"\u003e\u003cimg src=\"https://runtime-blocks.github.io/website/repo/crb/assets/crateful-logo.png\" width=\"100px\" /\u003e\u003c/a\u003e\n\nTo support the project, consider subscribing to [Crateful](https://crateful.substack.com/), my newsletter focused on AI agent development in Rust. It features insights gathered by a group of crab agents built with this framework.\n\n# License\n\nThis project is licensed under the [MIT license].\n\n[MIT license]: https://github.com/runtime-blocks/crb/blob/master/LICENSE\n","funding_links":[],"categories":["Rust"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruntime-blocks%2Fcrb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fruntime-blocks%2Fcrb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruntime-blocks%2Fcrb/lists"}