Rojak
Python library for building durable and scalable multi-agent orchestrations.
Install / Use
/learn @StreetLamb/RojakREADME
See it in action:
https://github.com/user-attachments/assets/d61b8893-3c33-4002-bca9-740f403f51f1
Features
- 🛡️ Durable and Fault-Tolerant - Agents always completes, even when the server crashes or managing long-running tasks that span weeks, months, or even years.
- 🗂️ State Management - Messages, contexts and other states are automatically managed and preserved, even during failures. No complex database transactions required.
- 🤝 MCP Support - Supports calling tools via Model Context Protocol (MCP) servers.
- 🧑💻 Human-In-The-Loop - Integrates human intervention for approving, rejecting, or modifying tool invocations and decisions, ensuring control over critical workflows.
- 📈 Scalable - Manage unlimited agents, and handle multiple chat sessions in parallel.
- ⏰ Scheduling - Schedule to run your agents at specific times, days, date or intervals.
- 👁️ Visiblity - Track your agents’ past and current actions in real time through a user-friendly browser-based UI.
- 🌐 Universal Deployment - Deploy and run locally or on any cloud platform.
Table of Contents
- Table of Contents
- Overview
- Examples
- Understanding Rojak’s Architecture
- Running Rojak
Install
Install the core Rojak library:
pip install rojak
Install dependencies for the specific model providers you need:
# For OpenAI models or models supporting OpenAI format e.g. DeepSeek, Ollama.
pip install rojak[openai]
# For Anthropic and Anthropic Bedrock models.
pip install rojak[anthropic]
# To install both
pip install rojak[openai,anthropic]
Rojak also supports retrievers to retrieve context from vector stores. Install the dependencies if required:
# For Qdrant
pip install rojak[qdrant-client]
Usage
Start the Temporal development server.
temporal server start-dev
# main.py
import asyncio
import uuid
from temporalio.client import Client
from rojak import Rojak
from rojak.agents import OpenAIAgentActivities, OpenAIAgentOptions, OpenAIAgent
from rojak.workflows import TaskParams
def transfer_to_agent_b():
"""Handoff to Agent B"""
return agent_b
agent_a = OpenAIAgent(
name="Agent A",
instructions="You are a helpful agent.",
functions=["transfer_to_agent_b"]
)
agent_b = OpenAIAgent(
name="Agent B",
instructions="Only speak in Haikus."
)
async def main():
# Connect to the Temporal service
temporal_client = await Client.connect("localhost:7233")
# Initialize the Rojak client
rojak = Rojak(temporal_client, task_queue="tasks")
# Configure agent activities
openai_activities = OpenAIAgentActivities(
OpenAIAgentOptions(
api_key="YOUR_API_KEY_HERE", # Replace with your OpenAI API key or specify OPENAI_API_KEY in .env
all_functions=[transfer_to_agent_b]
)
)
# Create a worker for handling agent activities
worker = await rojak.create_worker([openai_activities])
async with worker:
# Run the workflow with agent A and a handoff to agent B
response = await rojak.run(
id=str(uuid.uuid4()),
type="stateless",
task=TaskParams(
agent=agent_a,
messages=[{"role": "user", "content": "I want to talk to agent B."}]
),
)
print(response.result.messages[-1].content)
if __name__ == "__main__":
asyncio.run(main())
Agent B is here,
Ready to chat and assist,
What do you wish for?
View this completed workflow at http://localhost:8233.
Overview
Rojak simplifies the orchestration of reliable multi-agent systems by leveraging Temporal as its backbone. Designed to address the real-world challenges of agentic systems, such as network outages, unreliable endpoints, failures, and long-running processes, Rojak ensures reliability and scalability.
Much like OpenAI’s Swarm, Rojak employs two key concepts:
- Agents: These function like individual team members, each responsible for specific tasks and equipped with the necessary tools to accomplish them.
- Handoffs: These facilitate seamless transitions, allowing one Agent to pass responsibility or context to another effortlessly.
Examples
Basic examples can be found in the /examples directory:
weather: A straightforward example demonstrating tool calling and the use ofcontext_variables.mcp_weatherAn example demonstrating connecting to MCP servers and executing tools through them.pizzaA comprehensive example showcasing the use of multiple agents with human-in-the-loop interventions to help users seamlessly order food.
Understanding Rojak’s Architecture

Rojak is built on Temporal workflows and activities, and orchestrates agents via the Orchestrator Workflow.
- The Orchestrator Workflow is responsible for receiving the user’s query, managing the overall execution process, and orchestrating tasks such as retrieving responses from LLM models, executing tools or functions, and handling any necessary Activities.
Activities are method functions grouped by class, with each class representing actions for a specific provider. Base classes like AgentActivities and RetrieverActivities serve as templates, while concrete classes, such as OpenAIAgentActivities for OpenAI and QdrantRetrieverActivities for Qdrant Vector DB, implement provider-specific methods. This design ensures flexibility and seamless integration with various providers.
After completing its tasks, the Orchestrator Workflow generates a result containing the agent’s response and the next agent (if any) to hand off to. This result is passed back to the Orchestrator Workflow, which then continues the process by executing the specified agent in the result.
Every step in the workflows is tracked and recorded in the Temporal Service, which, in the event of failures, allows the workflow to resume from the previous step. This ensures that workflows are durable, reliable, and recoverable.
While the Temporal Service oversees the workflow, Workers are responsible for running the code. Workers poll the Temporal Service for tasks and execute them. If there are no running workers, the workflow will not progress. You can deploy not just one worker, but hundreds or even thousands, if necessary, to scale your system’s performance.
Running Rojak
Ensure that a Temporal Service is running locally. You can find instructions for setting it up here.
$ temporal server start-dev
Once the Temporal Service is running, connect the Temporal client to the Temporal Service and use it to instantiate a Rojak client.
from temporalio.client import Client
from rojak import Rojak
temporal_client = await Client.connect("localhost:7233")
rojak = Rojak(temporal_client, task_queue="tasks")
Workers
Workers are responsible for executing the tasks defined in workflows and activities. They poll the Temporal Service for tasks and run the corresponding activity or workflow logic.
To create and start a worker, you first need to define the activities it will handle. For example, if you’re using an OpenAI agent, you must provide the corresponding OpenAIAgentActivities configured with appropriate options through OpenAIAgentOptions.
Here’s how to create and start a worker:
from rojak.agents import OpenAIAgentActivities, OpenAIAgentOptions
# Initialize Rojak client
rojak = Rojak(temporal_client, task_queue="tasks")
# Initialize an OpenAI agent
agent = OpenAIAgent(name="Agent")
# Define the activities for the OpenAI agent
openai_activities = OpenAIAgentActivities(
OpenAIAgentOptions(api_key="...")
)
# Create a worker to handle tasks for the defined activi
Related Skills
claude-opus-4-5-migration
108.0kMigrate prompts and code from Claude Sonnet 4.0, Sonnet 4.5, or Opus 4.1 to Opus 4.5
model-usage
347.2kUse CodexBar CLI local cost usage to summarize per-model usage for Codex or Claude, including the current (most recent) model or a full model breakdown. Trigger when asked for model-level usage/cost data from codexbar, or when you need a scriptable per-model summary from codexbar cost JSON.
openhue
347.2kControl Philips Hue lights and scenes via the OpenHue CLI.
sag
347.2kElevenLabs text-to-speech with mac-style say UX.
