RemoteWorkflow
Execute workflows hosted on a remote AgentOS or A2A server.
RemoteWorkflow runs workflows hosted on a remote AgentOS instance or A2A-compatible server.
Installation
uv pip install -U 'agno[os]'
export OS_SECURITY_KEY=your_os_security_key_hereStart the Python client example server on port 7778. Its setup installs the server dependencies, sets OPENAI_API_KEY, and registers qa-workflow. Use the same OS_SECURITY_KEY in the client terminal.
Basic Usage
import asyncio
import os
from agno.workflow import RemoteWorkflow
async def main():
# Create a remote workflow pointing to a remote AgentOS instance
workflow = RemoteWorkflow(
base_url="http://localhost:7778",
workflow_id="qa-workflow",
)
# Run the workflow (async)
response = await workflow.arun("What are the benefits of Python?", auth_token=os.environ["OS_SECURITY_KEY"])
print(response.content)
asyncio.run(main())Unless labeled as complete programs, later snippets are fragments for this async main() with the same workflow and os imports.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
base_url | str | Required | Base URL of the remote AgentOS instance (e.g., "http://localhost:7778") |
workflow_id | str | Required | ID of the remote workflow to execute |
timeout | float | 300.0 | Request timeout in seconds |
protocol | Literal["agentos", "a2a"] | "agentos" | Communication protocol: AgentOS REST API or A2A for cross-framework communication |
a2a_protocol | Literal["json-rpc", "rest"] | "rest" | Transport used when protocol="a2a": JSON-RPC or REST |
config_ttl | float | 300.0 | Time-to-live for cached configuration in seconds |
Properties
Metadata access can make synchronous requests when the cache is empty or expired. Properties and configuration methods do not forward the run's auth_token; they require metadata access without a bearer credential. For the protected example server, use AgentOSClient.aget_workflow() with explicit Authorization headers. See client reference.
id
Returns the workflow ID.
print(workflow.id) # "qa-workflow"name
Returns the workflow's name from the remote configuration.
print(workflow.name) # "QA Workflow"description
Returns the workflow's description from the remote configuration.
print(workflow.description) # "A Q&A workflow for answering questions"db
Returns a RemoteDb instance if the workflow has a database configured.
if workflow.db:
print(f"Database ID: {workflow.db.id}")Methods
arun
Execute the remote workflow asynchronously.
# Non-streaming
response = await workflow.arun(
"Explain machine learning",
auth_token=os.environ["OS_SECURITY_KEY"],
user_id="user-123",
session_id="session-456",
)
print(response.content)
print(f"Status: {response.status}")
# Streaming
async for event in workflow.arun(
"Generate a report",
auth_token=os.environ["OS_SECURITY_KEY"],
stream=True,
user_id="user-123",
):
if event.event == "WorkflowCompleted":
print(event.content)
elif event.event in {"WorkflowError", "WorkflowCancelled", "WorkflowPaused"}:
print(event.event)Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
input | str | Dict | List | BaseModel | Required | The input for the workflow |
additional_data | Optional[Dict] | None | Additional data to pass to the workflow |
user_id | Optional[str] | None | User ID for the run |
run_id | Optional[str] | None | Custom run ID |
session_id | Optional[str] | None | Session ID for context persistence |
session_state | Optional[Dict] | None | Session state dictionary |
images | Optional[List[Image]] | None | Images to include |
audio | Optional[List[Audio]] | None | Audio to include |
videos | Optional[List[Video]] | None | Videos to include |
files | Optional[List[File]] | None | Files to include |
stream | bool | False | Whether to stream the response |
stream_events | Optional[bool] | None | Whether to stream events |
auth_token | Optional[str] | None | JWT token for authentication |
Returns:
WorkflowRunOutputwhenstream=FalseAsyncIterator[WorkflowRunOutputEvent]whenstream=True
acontinue_run
Continue a paused workflow run through the AgentOS protocol. The example below assumes paused is the actual WorkflowRunOutput from a server workflow with confirmation pauses and persistent storage. The basic QA server does not pause automatically. Keep the original requirements, run ID, session ID, and authenticated identity. Different pause kinds need their matching resolution methods; see workflow HITL.
from agno.run.base import RunStatus
if paused.status != RunStatus.paused or not paused.run_id or not paused.session_id:
raise ValueError("Expected the original persisted paused workflow run")
for requirement in paused.active_step_requirements:
if not requirement.needs_confirmation:
raise ValueError("Resolve this pause using its matching HITL handler")
if input("Approve this step? [y/N] ").lower() == "y":
requirement.confirm()
else:
requirement.reject()
response = await workflow.acontinue_run(
run_response=paused,
auth_token=os.environ["OS_SECURITY_KEY"],
)Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
run_response | Optional[WorkflowRunOutput] | None | Paused run output containing the run ID, session ID, and requirements |
run_id | Optional[str] | None | Run ID. Required unless run_response supplies one |
session_id | Optional[str] | None | Session ID. Overrides the ID from run_response |
step_requirements | Optional[List[Any]] | None | Resolved requirements. Overrides requirements from run_response |
stream | bool | False | Whether to stream the continued run |
auth_token | Optional[str] | None | JWT token for authentication |
Returns:
WorkflowRunOutputwhenstream=FalseAsyncIterator[WorkflowRunOutputEvent]whenstream=True
acancel_run
Request cancellation through the AgentOS protocol using a real saved_run_id from a still-running execution. A successful request records intent; it does not prove execution has stopped.
success = await workflow.acancel_run(run_id=saved_run_id, auth_token=os.environ["OS_SECURITY_KEY"])
if success:
print("Cancellation requested")Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
run_id | str | Required | ID of the run to cancel |
auth_token | Optional[str] | None | JWT token for authentication |
Returns: bool — whether the request succeeded. Errors return False.
The wrapper cannot supply a session_id. For scoped authentication that requires the original session, send direct HTTP POST /workflows/qa-workflow/runs/{run_id}/cancel?session_id={session_id} with the same bearer identity. Continuation and cancellation are AgentOS-protocol operations, not A2A operations.
get_workflow_config
Get the workflow configuration from the remote server (always fetches fresh).
config = await workflow.get_workflow_config()
print(f"Workflow name: {config.name}")
print(f"Steps: {config.steps}")Returns: WorkflowResponse
refresh_config
Force refresh the cached workflow configuration through the AgentOS protocol.
config = await workflow.refresh_config()Returns: WorkflowResponse
A2A Protocol Support
RemoteWorkflow supports the A2A REST and JSON-RPC transports below. The A2A execution path forwards message, media, user identity, and the session as context_id. AgentOS-specific run IDs, session state, additional data, and stream-event controls are not forwarded.
Connecting to Agno A2A Servers
Install uv pip install -U "agno[os,a2a]". This fragment requires a separate A2A server exposing my-workflow at the displayed path; it is not provided by the Python client example server. See A2A setup.
from agno.workflow import RemoteWorkflow
# Connect to an Agno AgentOS with A2A interface
workflow = RemoteWorkflow(
base_url="http://localhost:7001/a2a/workflows/my-workflow",
workflow_id="my-workflow",
protocol="a2a",
)
response = await workflow.arun("Hello!")
print(response.content)Protocol Options
| Protocol | a2a_protocol | Use Case |
|---|---|---|
"agentos" | N/A | Default. Connect to Agno AgentOS REST API |
"a2a" | "rest" | Connect to A2A servers using REST endpoints |
"a2a" | "json-rpc" | Connect to Google ADK or pure JSON-RPC A2A servers |
Using in AgentOS Gateway
This gateway configuration fragment requires the two deployed servers below. Discovery has the same unauthenticated metadata limitation described above:
from agno.workflow import RemoteWorkflow
from agno.os import AgentOS
agent_os = AgentOS(
workflows=[
RemoteWorkflow(base_url="http://server-1:7777", workflow_id="qa-workflow"),
RemoteWorkflow(base_url="http://server-2:7777", workflow_id="analysis-workflow"),
],
)See AgentOS Gateway for more details.
Streaming Example
Complete client program for the protected QA example server. Read the workflow's final content once; this also works for function-only workflows that emit no model-content deltas.
import asyncio
import os
from agno.workflow import RemoteWorkflow
async def main():
workflow = RemoteWorkflow(base_url="http://localhost:7778", workflow_id="qa-workflow")
async for event in workflow.arun(
"Explain machine learning",
stream=True,
auth_token=os.environ["OS_SECURITY_KEY"],
):
if event.event == "WorkflowCompleted":
print(event.content)
elif event.event in {"WorkflowError", "WorkflowCancelled", "WorkflowPaused"}:
print(event.event)
asyncio.run(main())For streaming continuation, await workflow.acontinue_run(..., stream=True) first, then iterate the returned asynchronous iterator.
Error Handling
from agno.exceptions import RemoteServerUnavailableError
try:
response = await workflow.arun("Hello", auth_token=os.environ["OS_SECURITY_KEY"])
except RemoteServerUnavailableError as e:
print(f"Remote server unavailable: {e.message}")Authentication
For authenticated AgentOS instances, pass the auth_token parameter:
response = await workflow.arun(
"Process this request",
auth_token="your-jwt-token",
)Notes
Remote Workflows via WebSocket are not yet supported. Use HTTP streaming instead.