API Reference

August 21, 2026 · View on GitHub

Agent

The main class for creating AI agents that can use tools.

Constructor

Agent(
    name: str,
    llm: Optional[LLM] = None,
    tools: Optional[List[Callable]] = None,
    system_prompt: Union[str, Path, None] = None,
    api_key: Optional[str] = None,
    model: str = "co/gemini-3.7-flash"
)

Parameters

  • name (str): Unique identifier for the agent
  • llm (Optional[LLM]): Custom LLM instance (defaults to OpenAILLM)
  • tools (Optional[List[Callable]]): List of functions the agent can use
  • system_prompt (Union[str, Path, None]): Agent's personality and behavior definition
    • str: Can be either a direct prompt text or a file path (auto-detected)
    • Path: Path object pointing to a prompt file
    • None: Uses default prompt
  • api_key (Optional[str]): OpenAI API key (if not using custom LLM)
  • model (str): Model to use (default: "co/gemini-3.7-flash")
    • Managed keys: co/gemini-3.7-flash, co/gpt-4o-mini, co/claude-sonnet-4-5
    • Your own key: gpt-4o-mini, claude-sonnet-4-5, gemini-3.7-flash

System Prompt Options

The system_prompt parameter accepts multiple input types:

# 1. Direct string
agent = Agent(name="bot", system_prompt="You are helpful")

# 2. File path as string (auto-detected if file exists)
agent = Agent(name="bot", system_prompt="prompts/assistant.md")

# 3. Path object (must point to existing file)
from pathlib import Path
agent = Agent(name="bot", system_prompt=Path("prompts/assistant.txt"))

# 4. None (uses default)
agent = Agent(name="bot")  # Default: "You are a helpful assistant..."

File Loading Rules:

  • Any text file can be used (no extension restrictions)
  • Files must be valid UTF-8 encoded text
  • Empty files raise ValueError
  • Non-existent Path objects raise FileNotFoundError
  • Strings that don't exist as files are treated as literal prompts

Methods

input(prompt: str, max_iterations: Optional[int] = None) -> str

Provide input to the agent and get response.

Parameters:

  • prompt - User input/task for the agent
  • max_iterations - Override agent's default max_iterations for this task only
result = agent.input("Calculate 2 + 2")

# Override iterations for complex tasks
result = agent.input("Complex multi-step task", max_iterations=25)

add_tool(tool: Callable) -> None

Add a new tool to the agent.

def new_tool(param: str) -> str:
    return f"Processed {param}"

agent.add_tool(new_tool)

remove_tool(tool_name: str) -> None

Remove a tool from the agent.

agent.remove_tool("calculator")

list_tools() -> List[str]

Get list of available tool names.

tools = agent.list_tools()
# ['calculator', 'search', 'get_time']

Tools

Function-Based Tools

Any Python function can be used as a tool:

def my_tool(param: str, optional: int = 10) -> str:
    """Tool description from docstring.

    Args:
        param: The main parameter.

    Returns:
        Formatted result.
    """
    return f"Result: {param} with {optional}"

agent = Agent("bot", tools=[my_tool])

How schemas are generated:

  • name ← function name
  • description ← first paragraph of docstring (Args/Returns NOT included)
  • parameters ← from Python type hints
  • required ← parameters without default values

Everything is optional:

  • No docstring? Description defaults to "Execute the {name} tool."
  • No type hints? Defaults to string type

Tool Conversion

The create_tool_from_function utility converts functions to tools:

from connectonion import create_tool_from_function

def simple_function(x: int) -> int:
    """Double a number."""
    return x * 2

tool = create_tool_from_function(simple_function)
# tool now has: .name, .description, .run(), .to_function_schema()

Logging

Automatic activity logging for agents. Three output destinations:

  • Console: Rich-formatted terminal output
  • Plain text: .co/logs/{name}.log
  • Session YAML: .co/evals/{name}_{timestamp}.yaml

Configuration

Control logging via quiet and log parameters:

# Default: everything on (console + logs + sessions)
agent = Agent("assistant")

# Quiet mode: suppress console, keep sessions for eval
agent = Agent("assistant", quiet=True)

# Disable all file logging (console only)
agent = Agent("assistant", log=False)

# Custom log file path
agent = Agent("assistant", log="my_logs/custom.log")

Logging Modes

quietlogConsolePlain TextSessionsUse Case
FalseTrue/NoneDevelopment (default)
TrueTrue/NoneEval/testing
FalseFalseBenchmarking
False"path"customCustom log path

Log Format

Plain text logs include:

  • Timestamp
  • User input
  • LLM calls with timing, tokens, cost
  • Tool executions with parameters and results
  • Final agent responses

Example log output:

[10:30:15] INPUT: Calculate 2 + 2
[10:30:15] -> LLM Request (gpt-4o-mini) • 2 msgs • 1 tools
[10:30:16] <- LLM Response (234ms) • 1 tools • 156 tokens • \$0.0001
[10:30:16] -> Tool: calculator({"expression": "2+2"})
[10:30:16] <- Result (1ms): 4
[10:30:16] [OK] Complete (1.2s)

Session YAML Format

Sessions are saved for replay and eval:

name: assistant
timestamp: 2024-12-02 10:30:15

turns:
  - input: "Calculate 2 + 2"
    model: "gpt-4o-mini"
    duration_ms: 1200
    tokens: 156
    cost: 0.0001
    tools_called: [calculator]
    result: "The answer is 4"
    messages: '[{"role":"system",...}]'

LLM

Abstract base class for language models. ConnectOnion supports multiple LLM providers through a unified interface.

Model Routing

The create_llm() factory function routes models to the appropriate provider:

from connectonion.core.llm import create_llm

# OpenAI models
llm = create_llm("gpt-4o-mini")      # → OpenAILLM
llm = create_llm("o4-mini")          # → OpenAILLM

# Anthropic models
llm = create_llm("claude-sonnet-4-5") # → AnthropicLLM

# Google Gemini models
llm = create_llm("gemini-3.7-flash")  # → GeminiLLM

# ConnectOnion managed keys (co/ prefix)
llm = create_llm("co/gpt-4o-mini")    # → OpenOnionLLM
llm = create_llm("co/gemini-3.7-flash") # → OpenOnionLLM

co/ Models (Managed Keys)

Models prefixed with co/ use ConnectOnion's managed API keys through the OpenOnion proxy:

from connectonion import Agent

# Uses OpenOnion managed keys - no API key needed
agent = Agent(name="bot", model="co/gemini-3.7-flash")

How it works:

  1. Client detects co/ prefix → routes to OpenOnionLLM
  2. Prefix is stripped before sending to server
  3. Server routes to appropriate provider (OpenAI, Anthropic, Gemini)
  4. Response returned in OpenAI-compatible format

Available co/ models:

  • co/gpt-4o-mini, co/gpt-4o, co/gpt-5, co/gpt-5-mini, co/gpt-5-nano, co/o4-mini
  • co/gemini-3.7-flash (default), co/gemini-3.6-flash, co/gemini-3.5-flash, co/gemini-2.5-pro, co/gemini-2.5-flash
  • co/claude-opus-4-5, co/claude-sonnet-4-5, co/claude-haiku-4-5

Environment variable: OPENONION_API_KEY (auto-loaded from .env)

OpenAILLM

OpenAI API implementation.

from connectonion.core.llm import OpenAILLM

llm = OpenAILLM(
    api_key="your-key",  # or OPENAI_API_KEY env var
    model="gpt-4o-mini",
    temperature=0.7
)

agent = Agent("bot", llm=llm)

AnthropicLLM

Anthropic Claude API implementation.

from connectonion.core.llm import AnthropicLLM

llm = AnthropicLLM(
    api_key="your-key",  # or ANTHROPIC_API_KEY env var
    model="claude-sonnet-4-5"
)

agent = Agent("bot", llm=llm)

GeminiLLM

Google Gemini API implementation (uses OpenAI-compatible endpoint).

from connectonion.core.llm import GeminiLLM

llm = GeminiLLM(
    api_key="your-key",  # or GEMINI_API_KEY env var
    model="gemini-3.7-flash"
)

agent = Agent("bot", llm=llm)

OpenOnionLLM

ConnectOnion managed keys implementation.

from connectonion.core.llm import OpenOnionLLM

llm = OpenOnionLLM(
    api_key="your-token",  # or OPENONION_API_KEY env var
    model="co/gemini-3.7-flash"
)

agent = Agent("bot", llm=llm)

Base URLs:

  • Production: https://oo.openonion.ai/v1
  • Development: http://localhost:8000/v1 (when ENVIRONMENT=development)

Prompts Module

Utilities for loading system prompts.

load_system_prompt(prompt: Union[str, Path, None]) -> str

Load system prompt from various sources.

from connectonion.prompts import load_system_prompt

# From string
prompt = load_system_prompt("You are helpful")

# From file
prompt = load_system_prompt("prompts/assistant.md")

# From Path
from pathlib import Path
prompt = load_system_prompt(Path("prompts/assistant.txt"))

# Default
prompt = load_system_prompt(None)

Raises:

  • FileNotFoundError: If Path object points to non-existent file
  • ValueError: If file is empty or not valid UTF-8
  • TypeError: If invalid type provided

Decorators

@xray

Enable detailed execution tracing.

from connectonion import xray

@xray
def my_agent_function():
    agent = Agent("tracer")
    return agent.input("task")

# After execution:
xray.trace()  # Shows detailed execution flow

@replay

Record and replay agent interactions.

from connectonion import replay

@replay
def workflow():
    agent = Agent("bot")
    return agent.input("task")

# Replay the recorded interaction
replay.last()

Examples

Basic Agent

from connectonion import Agent

# Simple calculator agent
def calculate(expression: str) -> str:
    """Evaluate mathematical expressions."""
    return str(eval(expression))

agent = Agent("calculator", tools=[calculate])
result = agent.input("What is 15 * 4?")

Agent with Custom Prompt File

# prompts/expert.md
"""
You are an expert Python developer with 10 years of experience.
Focus on clean code, best practices, and performance.
"""

# main.py
agent = Agent(
    name="python_expert",
    system_prompt="prompts/expert.md",
    tools=[code_review, suggest_improvement]
)

Multi-Tool Agent

def search(query: str) -> str:
    """Search the web."""
    # Implementation
    
def calculate(expr: str) -> str:
    """Do math."""
    # Implementation
    
def get_time() -> str:
    """Get current time."""
    # Implementation

agent = Agent(
    name="assistant",
    system_prompt="You are a helpful research assistant.",
    tools=[search, calculate, get_time]
)

# Agent can use multiple tools in one request
result = agent.input("Search for Python tutorials, calculate 42*17, and tell me the time")

Networking

Host an agent and connect to it from anywhere. Full guides live in network/; the core surface:

host(create_agent, **kwargs)

Serve an agent over HTTP/WebSocket with relay discovery. create_agent is a factory called once per request (isolation). See network/host.md.

from connectonion import host
host(lambda: Agent("assistant", tools=[...]))

connect(address, *, keys=None, relay_url=...) -> RemoteAgent

Client to a remote agent. See network/connect.md.

from connectonion import connect
remote = connect("0x3d40...")

# LLM-driven task
resp = remote.input("Book a flight")      # -> Response(text, done)

# The Host advertises the exact permission modes this session may select. The setter returns
# only after the Host has durably committed the new policy.
remote.set_session_mode("auto")
print(remote.available_modes)

# Direct tool execution — no LLM (see network/remote-call.md)
res = remote.call("bash", command="co status")   # -> ExecResult
MethodReturnsPurpose
remote.input(prompt, timeout=60)Response(text, done)Hand the remote LLM a task
remote.call(tool, timeout=60, **args)ExecResult(text, status, duration_ms, error)Run one tool directly, no LLM
remote.set_session_mode(mode, timeout=30)NoneDurably select one Host-advertised OIP permission mode

set_session_mode() returning means the Host committed the policy. A TimeoutError is an unknown outcome, not a rollback: the Host may have committed before its acknowledgement was lost. Reconnect and read available_modes plus current_session["mode"] from the next authoritative CONNECTED response before retrying.

ExecResult: .ok (bool), .images (base64 data URLs pulled from .text). Direct execution is gated by the host's .co/host.yaml permission whitelist. From the shell: co call <address> <command...> (see cli/call.md).