Skip to content

Agno

Available · Function per spec · approval from your port · pip install "agent-adapter[agno]"

Functions

Function (agent_adapter.integrations.agno) Returns
agno_function(spec, *, executor=None) one agno.tools.function.Function running spec through the executor
agno_functions(registry, *, executor=None) one per registry tool exported to agno
await resolve_requirements(output, port, registry) None; confirms or rejects each confirmation output paused on
INTEGRATION get_integration("agno"); bind_context(function_call) gives ids only

The function takes the neutral JSON schema as parameters (skip_entrypoint_processing=True). Results reach Agno as JSON text, because Agno shows str(result) to the model. agno_function refuses (IntegrationError) an Agno outside the extra's range or without what the bridge reads.

Wiring

port = CallbackApproval(reviewer)


def build_agent(model: Model) -> Agent:
    tools = agno_functions(registry, executor=Executor(registry, approval=port))
    # send_invoice has confirm=True, so it is exported with requires_confirmation=True
    return Agent(model=model, tools=tools, telemetry=False)


async def run_with_approvals(agent: Agent, prompt: str) -> Any:
    output = await agent.arun(prompt)
    while output.is_paused:  # Agno paused: a confirm tool wants to run
        await resolve_requirements(output, port, registry)  # confirm() / reject() from the port
        output = await agent.acontinue_run(run_response=output)
    return [(tool.tool_name, tool.confirmed, tool.result) for tool in output.tools or []]

Approval

Any confirm rule sets requires_confirmation=True, so the agent pauses (output.is_paused; a callable rule is asked every time). Agno's confirmation is only an extra gate: resolve it with resolve_requirements, which asks your approval port, then agent.acontinue_run(run_response=output).

sequenceDiagram
  participant App
  participant A as Agno Agent
  participant P as ApprovalPort
  participant F as Function
  participant Ex as Executor
  App->>A: arun(prompt)
  A-->>App: paused: active_requirements
  App->>P: resolve_requirements(output, port, registry)
  P-->>App: confirm() / reject() (+ one-use token per approval)
  App->>A: acontinue_run(run_response=output)
  A->>F: entrypoint(fc, **args)
  F->>Ex: run_spec(ctx.approval = token, if it matches)
  Ex-->>F: no token: asks its own port
Case What happens
Confirmed by resolve_requirements a one-use token bound to output.run_id, the tool_call_id, the tool and the validated arguments; the call that matches takes it and the executor does not ask again
requirement.confirm() by hand, edited tool_args, another tool_call_id, a replayed requirement (result=None) no token: the executor asks its own port (no port: denied)
DeferredApproval port two-phase: ApprovalPending until every call is decided, then each record is consumed once
Edited arguments refused (rejected): Agno cannot apply them

Trust assumptions

Agno (and AgentOS continue_run) rebuild requirements from what the client sends, so a confirmation is never taken as an approval. Resolve pauses with resolve_requirements in the process that runs the agent.

Calls and errors

Item Behaviour
Call id FunctionCall.call_id → ctx.call_id
Run the agent's run_id → ctx.run_id (a continued run keeps it); session_id → ctx.thread_id
ToolArgsInvalid, ToolError, ApprovalDenied, GuardrailBlocked, RunLimitExceeded returned as {"error": {"kind", "message"}} (JSON text)
Other errors raised; Agno reports a failed tool call to the model
CancelledError propagates

Agno injects the FunctionCall into a parameter named fc, so a tool argument named fc is refused with ToolDefinitionError.