ConnectOnionConnectOnion
NEW

Reusable Event Bundles

Package event handlers into reusable plugins. A plugin is just an event list you can use across multiple agents.

A Plugin is an Event List: on_events takes one event list → custom for this agent. plugins takes a list of event lists → reusable across agents.

Quick Start (60 seconds)

main.py
1from connectonion import Agent 2from connectonion.useful_plugins import reflection 3 4# Use built-in reflection plugin 5agent = Agent("assistant", tools=[search], plugins=[reflection]) 6 7agent.input("Search for Python") 8
Python REPL
Interactive
💭 We learned that Python is a popular programming language...
'Python is a high-level programming language...'

That's it! Use built-in plugins or create your own.

What is a Plugin?

A plugin is an event list:

main.py
1from connectonion import after_llm, after_tool 2 3# This is a plugin (one event list) 4reflection = [after_tool(add_reflection)] 5 6# This is also a plugin (one event list with multiple events) 7logger = [after_llm(log_llm), after_tool(log_tool)] 8 9# Use them (plugins takes a list of plugins) 10agent = Agent("assistant", tools=[search], plugins=[reflection, logger]) 11

Just like tools:

  • • Tools: Agent(tools=[search, calculate])
  • • Plugins: Agent(plugins=[reflection, logger])

Plugin vs on_events

  • on_events: Takes one event list (custom for this agent)
  • plugins: Takes a list of event lists (reusable across agents)
main.py
1from datetime import datetime 2 3# Reusable plugin (an event list) 4logger = [after_llm(log_llm)] 5 6# Use both 7agent = Agent( 8 "assistant", 9 tools=[search], 10 plugins=[logger], # List of event lists 11 on_events=[after_llm(add_timestamp), after_tool(log_tool)] # One event list 12) 13

Built-in Plugins (useful_plugins)

ConnectOnion provides ready-to-use plugins that you can import and use immediately.

Reflection Plugin

Reflects on tool execution results to generate insights:

main.py
1from connectonion import Agent 2from connectonion.useful_plugins import reflection 3 4agent = Agent("assistant", tools=[search], plugins=[reflection]) 5 6agent.input("Search for Python") 7# After each successful tool execution: 8# 💭 We learned that Python is a popular high-level programming language known for simplicity

ReAct Plugin

Uses ReAct-style reasoning to plan next steps:

main.py
1from connectonion import Agent 2from connectonion.useful_plugins import react 3 4agent = Agent("assistant", tools=[search], plugins=[react]) 5 6agent.input("Search for Python and explain it") 7# After each tool execution: 8# 🤔 We learned Python is widely used. We should next explain its key features and use cases.

Image Result Formatter Plugin

Automatically converts base64 image results to proper image message format for vision models:

main.py
1from connectonion import Agent 2from connectonion.useful_plugins import image_result_formatter 3 4agent = Agent("assistant", tools=[take_screenshot], plugins=[image_result_formatter]) 5 6agent.input("Take a screenshot of the homepage and describe what you see") 7# 🖼️ Formatted tool result as image (image/png) 8# Agent can now see and analyze the actual image, not just base64 text!

When to use:

  • • Tools that return screenshots as base64
  • • Image generation tools
  • • Any tool that returns visual data

What it does:

  • • Detects base64 images in tool results (data URLs or plain base64)
  • • Converts to OpenAI vision API format
  • • Allows multimodal LLMs to see images visually instead of as text
  • • Supports PNG, JPEG, WebP, GIF formats

Using Multiple Plugins Together

main.py
1from connectonion import Agent 2from connectonion.useful_plugins import reflection, react, image_result_formatter 3 4# Combine plugins for powerful agents 5agent = Agent( 6 name="visual_researcher", 7 tools=[take_screenshot, search, analyze], 8 plugins=[image_result_formatter, reflection, react] 9) 10 11# Now you get: 12# 🖼️ Image formatting for screenshots 13# 💭 Reflection: What we learned 14# 🤔 ReAct: What to do next

Writing Custom Plugins

Learn by example - here's how the reflection plugin is implemented:

Step 1: Message Compression Helper

main.py
1from typing import List, Dict 2 3def _compress_messages(messages: List[Dict], tool_result_limit: int = 150) -> str: 4 """ 5 Compress conversation messages with structure: 6 - USER messages → Keep FULL 7 - ASSISTANT tool_calls → Keep parameters FULL 8 - ASSISTANT text → Keep FULL 9 - TOOL results → Truncate to tool_result_limit chars 10 """ 11 lines = [] 12 13 for msg in messages: 14 role = msg['role'] 15 16 if role == 'user': 17 lines.append(f"USER: {msg['content']}") 18 19 elif role == 'assistant': 20 if 'tool_calls' in msg: 21 tools = [f"{tc['function']['name']}({tc['function']['arguments']})" 22 for tc in msg['tool_calls']] 23 lines.append(f"ASSISTANT: {', '.join(tools)}") 24 else: 25 lines.append(f"ASSISTANT: {msg['content']}") 26 27 elif role == 'tool': 28 result = msg['content'] 29 if len(result) > tool_result_limit: 30 result = result[:tool_result_limit] + '...' 31 lines.append(f"TOOL: {result}") 32 33 return "\n".join(lines)

Why this works:

  • • Keep user messages FULL (need to know what they asked)
  • • Keep tool parameters FULL (exactly what actions were taken)
  • • Keep assistant text FULL (reasoning/responses)
  • • Truncate tool results (save tokens while maintaining overview)

Step 2: Event Handler Function

main.py
1from connectonion.events import after_tool 2from connectonion.llm_do import llm_do 3 4def _add_reflection(agent) -> None: 5 """Reflect on tool execution result""" 6 trace = agent.current_session['trace'][-1] 7 8 if trace['type'] == 'tool_execution' and trace['status'] == 'success': 9 # Extract current tool execution 10 user_prompt = agent.current_session.get('user_prompt', '') 11 tool_name = trace['tool_name'] 12 tool_args = trace['arguments'] 13 tool_result = trace['result'] 14 15 # Compress conversation messages 16 conversation = _compress_messages(agent.current_session['messages']) 17 18 # Build prompt with conversation context + current execution 19 prompt = f"""CONVERSATION: 20{conversation} 21 22CURRENT EXECUTION: 23User asked: {user_prompt} 24Tool: {tool_name}({tool_args}) 25Result: {tool_result} 26 27Reflect in 1-2 sentences on what we learned:""" 28 29 reflection_text = llm_do( 30 prompt, 31 model="co/gpt-4o", 32 temperature=0.3, 33 system_prompt="You reflect on tool execution results to generate insights." 34 ) 35 36 # Add reflection as assistant message 37 agent.current_session['messages'].append({ 38 'role': 'assistant', 39 'content': f"💭 {reflection_text}" 40 }) 41 42 agent.console.print(f"[dim]💭 {reflection_text}[/dim]")

Key insights:

  • • Access agent state via agent.current_session
  • • Use llm_do() for AI-powered analysis
  • • Add results back to conversation messages
  • • Print to console for user feedback

Step 3: Create Plugin (Event List)

main.py
1# Plugin is an event list 2reflection = [after_tool(_add_reflection)]

That's it! A plugin is just an event list.

Step 4: Use Your Plugin

main.py
1agent = Agent("assistant", tools=[search], plugins=[reflection])

Quick Custom Plugin Example

Build a simple plugin in 3 lines:

main.py
1from connectonion import Agent, after_tool 2 3def log_tool(agent): 4 trace = agent.current_session['trace'][-1] 5 print(f"✓ {trace['tool_name']} completed in {trace['timing']}ms") 6 7# Plugin is an event list 8logger = [after_tool(log_tool)] 9 10# Use it 11agent = Agent("assistant", tools=[search], plugins=[logger])

Example: Reflection Plugin

main.py
1from connectonion import Agent, after_tool, llm_do 2 3def add_reflection(agent): 4 trace = agent.current_session['trace'][-1] 5 6 if trace['type'] == 'tool_execution' and trace['status'] == 'success': 7 result = trace['result'] 8 9 reflection = llm_do( 10 f"Result: {result[:200]}\n\nWhat did we learn?", 11 system_prompt="Be concise.", 12 temperature=0.3 13 ) 14 15 agent.current_session['messages'].append({ 16 'role': 'assistant', 17 'content': f"🤔 {reflection}" 18 }) 19 20 print(f"💭 {reflection}") 21 22# Plugin is an event list 23reflection = [after_tool(add_reflection)] 24 25# Use it 26agent = Agent("researcher", tools=[search], plugins=[reflection]) 27 28agent.input("Search for Python") 29
Python REPL
Interactive
💭 We learned Python is a popular programming language...
'Python is a high-level programming language...'

Example: Todo Plugin

main.py
1from connectonion import Agent, after_user_input, after_tool, llm_do 2from pydantic import BaseModel 3from typing import List 4 5class TodoList(BaseModel): 6 tasks: List[str] 7 8# Store todos 9todos = [] 10 11def create_todos(agent): 12 prompt = agent.current_session['user_prompt'] 13 14 todo_list = llm_do( 15 f"Break into 3-5 steps:\n{prompt}", 16 output=TodoList, 17 temperature=0.2 18 ) 19 20 todos.clear() 21 todos.extend(todo_list.tasks) 22 23 print("📝 Todos:") 24 for i, task in enumerate(todos, 1): 25 print(f" {i}. {task}") 26 27def check_todos(agent): 28 trace = agent.current_session['trace'][-1] 29 30 if trace['type'] == 'tool_execution' and trace['status'] == 'success': 31 result = trace['result'] 32 33 for task in todos: 34 check = llm_do( 35 f"Todo: {task}\nResult: {result[:200]}\n\nDone? (yes/no)", 36 temperature=0 37 ) 38 39 if 'yes' in check.lower(): 40 print(f"✅ {task}") 41 42# Plugin is an event list 43todo = [after_user_input(create_todos), after_tool(check_todos)] 44 45# Use it 46agent = Agent("assistant", tools=[search, analyze], plugins=[todo]) 47 48agent.input("Research Python and summarize") 49
Python REPL
Interactive
📝 Todos:
1. Search for Python
2. Analyze results
3. Summarize findings
✅ Search for Python

Reusing Plugins

Use the same plugin across multiple agents:

main.py
1# Define once 2reflection = [after_tool(add_reflection)] 3logger = [after_llm(log_llm), after_tool(log_tool)] 4 5# Use in multiple agents 6researcher = Agent("researcher", tools=[search], plugins=[reflection, logger]) 7writer = Agent("writer", tools=[generate], plugins=[reflection]) 8analyst = Agent("analyst", tools=[calculate], plugins=[logger]) 9

Summary

A plugin is an event list

main.py
1# Define a plugin (an event list) 2my_plugin = [after_llm(handler1), after_tool(handler2)] 3 4# Use it (plugins takes a list of event lists) 5agent = Agent("assistant", tools=[search], plugins=[my_plugin]) 6

on_events vs plugins:

  • on_events=[after_llm(h1), after_tool(h2)] → one event list
  • plugins=[plugin1, plugin2] → list of event lists

What's Next?