Blog

Xircuits 1.15: Async Comes to Xircuits

Paul Dubs
18 Dec 2024

The release of Xircuits 1.15 brings a feature that’s been lingering on our roadmap for quite some time: first-class support for async components. While this might sound like a minor technical detail, it represents a significant leap forward in Xircuits’ capabilities – particularly for those of us working with modern Python frameworks and services.

The ability to seamlessly integrate async code isn’t just a nice-to-have feature anymore. It’s become table stakes for any serious Python framework in 2024.

The Async Revolution

Python’s async story began a decade ago with the introduction of asyncio in Python 3.4. The following year, Python 3.5 gave us the more elegant async/await syntax, finally making asynchronous programming feel native to Python. This shift enabled developers to write single-threaded applications that could efficiently juggle multiple operations – particularly I/O-bound tasks.

The real-world impact has been profound. Modern service integration libraries, especially those for platforms like Discord, Slack, and Teams, have gone all-in on async. They’re not offering it as an option – they’re requiring it.

The Integration Challenge

Anyone who’s tried to retrofit async code into a synchronous codebase knows the pain. The impedance mismatch between sync and async code creates friction that ripples through your entire application. As we built more integration components for Xircuits, we kept running into this wall. Modern SDKs increasingly leave us no choice: async or bust.

The Solution

Rather than continue fighting this trend, Xircuits 1.15 embraces it. We’ve made asyncio and the async/await syntax first-class citizens in the component development model. The changes required to support this are surprisingly minimal:

# Before: Synchronous Component
class Print(Component):    
    msg: InArg[str]
    def execute(self, ctx):
        print(self.msg.value)

# After: Async Component
class AsyncPrint(AsyncComponent):    
    msg: InArg[str]
    async def execute(self, ctx):
        print(self.msg.value)

This simple change unlocks powerful capabilities. The async keyword allows you to use await within the execute method, seamlessly integrating with any asyncio-based library.

Real-World Benefits

The impact is already visible in our updated Converse library, which provides OpenAI-compatible APIs for agent communication. The EmitMessage component now works exactly as you’d expect – messages emit immediately rather than waiting for a parent component’s permission.

For those building complex components, we’ve also upgraded the SubGraphExecutor. You can now run branches asynchronously with a simple await SubGraphExecutor(start).do_async(ctx) call.

Looking Forward

This update represents more than just technical cleanup – it’s about keeping Xircuits relevant in an increasingly async world. Modern Python development demands async support, and now Xircuits delivers it without compromise.

The best part? The complexity stays hidden behind a clean, simple API. Developers can focus on building great components without wrestling with the underlying async machinery.