Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
496b8f6
feat: enhance WhatsApp interface with new tools, security hardening, …
Mustafa-Esoofally Feb 10, 2026
a8bb5ab
Merge branch 'main' into update/whatsapp-interface-v2
Mustafa-Esoofally Feb 12, 2026
d685a1b
Merge branch 'main' into update/whatsapp-interface-v2
Mustafa-Esoofally Feb 17, 2026
e5a7b2b
Merge branch 'main' into update/whatsapp-interface-v2
Mustafa-Esoofally Feb 18, 2026
5fec24d
Merge branch 'main' into update/whatsapp-interface-v2
Mustafa-Esoofally Feb 19, 2026
6331c9f
cookbook: add whatsapp_all_tools.py demonstrating all 9 sync tools
github-actions[bot] Feb 20, 2026
d1aa565
Show reasoning in WhatsApp and update model
VirusDumb Feb 20, 2026
01d3899
Merge branch 'update/whatsapp-interface-v2' of https://github.com/agn…
VirusDumb Feb 20, 2026
1661e50
Merge branch 'main' into update/whatsapp-interface-v2
VirusDumb Feb 23, 2026
e542ad0
tested and works
VirusDumb Feb 23, 2026
50b9b49
Update test_whatsapp_router.py
VirusDumb Feb 23, 2026
48767b6
Merge branch 'main' into update/whatsapp-interface-v2
VirusDumb Feb 24, 2026
865bc03
Add interactive WhatsApp support & tools
VirusDumb Feb 24, 2026
2063453
Merge branch 'main' into update/whatsapp-interface-v2
VirusDumb Feb 24, 2026
e29750e
Add WhatsApp audio/document handling and examples
VirusDumb Feb 25, 2026
c28bd10
Merge branch 'update/whatsapp-interface-v2' of https://github.com/agn…
VirusDumb Feb 25, 2026
d88d5c5
Merge branch 'main' into update/whatsapp-interface-v2
VirusDumb Feb 25, 2026
5a2addf
Merge branch 'main' into update/whatsapp-interface-v2
uzaxirr Feb 26, 2026
51c6405
style fix
VirusDumb Mar 2, 2026
54d6087
Merge branch 'main' into update/whatsapp-interface-v2
Mustafa-Esoofally Mar 3, 2026
e617920
refactor: extract WhatsApp router helpers and add async text sending
Mustafa-Esoofally Mar 3, 2026
f147cbe
fix: cookbook serve params, test isolation, and router cleanup from E…
Mustafa-Esoofally Mar 4, 2026
bfaedc0
fix: keep WhatsApp typing indicator active during long agent runs
Mustafa-Esoofally Mar 4, 2026
d34fc55
cookbook: add 4 new WhatsApp cookbooks
Mustafa-Esoofally Mar 4, 2026
54cc894
cookbook: add geocode MCP and formatting fix to interactive concierge
Mustafa-Esoofally Mar 4, 2026
64b24c1
Merge branch 'main' into update/whatsapp-interface-v2
Mustafa-Esoofally Mar 4, 2026
52f4053
Merge branch 'main' into update/whatsapp-interface-v2
VirusDumb Mar 5, 2026
c48a3e1
refactor: WhatsApp interface improvements from review
Mustafa-Esoofally Mar 5, 2026
ea5c9ef
Merge branch 'main' into update/whatsapp-interface-v2
Mustafa-Esoofally Mar 5, 2026
81c167e
style: add WHY comments to WhatsApp interface + clean up run_kwargs
Mustafa-Esoofally Mar 5, 2026
775a98d
refactor: consolidate WhatsApp helpers, hash phone numbers, fix capti…
Mustafa-Esoofally Mar 5, 2026
2e6301a
feat: add /new session reset command and session isolation for WhatsApp
Mustafa-Esoofally Mar 5, 2026
035166b
refactor: production cleanup — hoist constant, remove dead code, fix …
Mustafa-Esoofally Mar 5, 2026
78f2c11
refactor: align WhatsApp cookbooks with Slack naming and conventions
Mustafa-Esoofally Mar 5, 2026
fd5ed7b
fix: remove unused imports flagged by CI ruff check
Mustafa-Esoofally Mar 5, 2026
7c0ae75
refactor: clean up WhatsApp helpers and fix router media handling
Mustafa-Esoofally Mar 5, 2026
5a791ec
chore: remove redundant WhatsApp cookbooks
Mustafa-Esoofally Mar 5, 2026
20e79f9
refactor: clean up WhatsApp helpers — rename APIs, extract shared PCM…
Mustafa-Esoofally Mar 5, 2026
c8a2c1c
fix: update test patch targets for renamed _send_text, remove unused …
Mustafa-Esoofally Mar 5, 2026
3648d36
fix: remove unused File import from helpers.py (ruff F401)
Mustafa-Esoofally Mar 5, 2026
43ab33f
fix: remove replay protection and fix flaky router tests
Mustafa-Esoofally Mar 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,3 @@ def get_user_location() -> str:
)

pprint.pprint_run_response(run_response)

Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,4 @@
# ---------------------------------------------------------------------------

if __name__ == "__main__":
"""Run your AgentOS.

You can see the configuration and available apps at:
http://localhost:7777/config

"""
agent_os.serve(app="agent_with_media:app", reload=True)
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
First introduce yourself and ask for their name then, ask about themeselves, their hobbies, what they like to do and what they like to talk about.
Use DuckDuckGo search tool to find latest information about things in the conversations
"""),
debug_mode=True,
)


Expand All @@ -65,10 +64,4 @@
# ---------------------------------------------------------------------------

if __name__ == "__main__":
"""Run your AgentOS.

You can see the configuration and available apps at:
http://localhost:7777/config

"""
agent_os.serve(app="agent_with_user_memory:app", reload=True)
6 changes: 0 additions & 6 deletions cookbook/05_agent_os/interfaces/whatsapp/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,4 @@
# ---------------------------------------------------------------------------

if __name__ == "__main__":
"""Run your AgentOS.

You can see the configuration and available apps at:
http://localhost:7777/config

"""
agent_os.serve(app="basic:app", reload=True)
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@
id="image_generation_model",
db=agent_db,
model=Gemini(
id="models/gemini-2.5-flash-image",
id="gemini-3-pro-image-preview",
response_modalities=["Text", "Image"],
),
debug_mode=True,
)


Expand All @@ -40,10 +39,4 @@
# ---------------------------------------------------------------------------

if __name__ == "__main__":
"""Run your AgentOS.

You can see the configuration and available apps at:
http://localhost:7777/config

"""
agent_os.serve(app="image_generation_model:app", reload=True)

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""
Interactive Concierge
=====================

A WhatsApp concierge that uses every interactive UI feature to help
users find restaurants, activities, and entertainment.

Showcases: reply buttons, list messages, location pins, reactions,
mark-as-read, and image sending — all in one conversational flow.

Requires:
WHATSAPP_ACCESS_TOKEN, WHATSAPP_PHONE_NUMBER_ID
ANTHROPIC_API_KEY
uvx (for geocode-mcp server)
"""

from agno.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.anthropic import Claude
from agno.os.app import AgentOS
from agno.os.interfaces.whatsapp import Whatsapp
from agno.tools.mcp import MCPTools
from agno.tools.websearch import WebSearchTools
from agno.tools.whatsapp import WhatsAppTools

agent_db = SqliteDb(db_file="tmp/concierge.db")

concierge_agent = Agent(
name="Concierge",
model=Claude(id="claude-sonnet-4-6"),
tools=[
WhatsAppTools(
enable_send_reply_buttons=True,
enable_send_list_message=True,
enable_send_location=True,
enable_send_reaction=True,
enable_mark_as_read=True,
enable_send_image=True,
),
WebSearchTools(),
MCPTools(command="uvx --python 3.12 geocode-mcp"),
],
db=agent_db,
instructions=[
"You are a friendly concierge that helps users find restaurants, activities, and entertainment.",
"Use WhatsApp interactive features to create a smooth, tap-driven experience.",
"Follow this flow:",
"1. Mark the incoming message as read using mark_as_read.",
"2. Greet the user and ask what they are in the mood for using send_reply_buttons "
"with options like: Dinner, Drinks, Entertainment.",
"3. When they pick, ask a follow-up preference using send_reply_buttons "
"(e.g., cuisine type for dinner, vibe for drinks).",
"4. Ask for their location or neighborhood (they can type it).",
"5. Search the web for matching venues in their area.",
"6. Present the top results using send_list_message with sections "
"(e.g., 'Top Picks' and 'Hidden Gems'), each row having a title and short description.",
"7. When they pick a venue from the list, use mcp_geocoding_get_coordinates to get "
"accurate latitude and longitude, then send the location using send_location.",
"8. Send an image of the venue if available using send_image with a URL from search results.",
"9. React to their original message with a contextual emoji using send_reaction.",
"Keep messages short and conversational. Use interactive elements instead of asking "
"the user to type whenever possible.",
"IMPORTANT: Do NOT send a long text summary that repeats what's already in an interactive message. "
"When sending reply buttons or list messages, only add a brief one-line intro — the interactive "
"element IS the message. Never use asterisks (*) around emoji for bold formatting.",
],
add_history_to_context=True,
num_history_runs=10,
add_datetime_to_context=True,
markdown=True,
)

agent_os = AgentOS(
agents=[concierge_agent],
interfaces=[Whatsapp(agent=concierge_agent, send_user_number_to_context=True)],
)
app = agent_os.get_app()

if __name__ == "__main__":
agent_os.serve(app="interactive_concierge:app", reload=True)
74 changes: 74 additions & 0 deletions cookbook/05_agent_os/interfaces/whatsapp/multimodal_team.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"""
Multimodal Team
===============

A coordinated team with a Vision Analyst and a Creative Agent that
handles image analysis, image generation, and web research over WhatsApp.

Send a photo to get it analyzed, or ask for an image to be generated.
For redesign requests, the team analyzes first then creates.

Requires:
WHATSAPP_ACCESS_TOKEN, WHATSAPP_PHONE_NUMBER_ID
OPENAI_API_KEY
"""

from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.os.app import AgentOS
from agno.os.interfaces.whatsapp import Whatsapp
from agno.team import Team
from agno.tools.dalle import DalleTools
from agno.tools.websearch import WebSearchTools

vision_analyst = Agent(
name="Vision Analyst",
model=OpenAIChat(id="gpt-4o"),
role="Analyzes images, files, and visual content in detail.",
instructions=[
"You are an expert visual analyst.",
"When given an image, describe it thoroughly: subjects, colors, composition, text, mood.",
"When given files (CSV, code, text), analyze their content and provide insights.",
"Keep analysis concise but detailed.",
],
markdown=True,
)

creative_agent = Agent(
name="Creative Agent",
model=OpenAIChat(id="gpt-4o"),
role="Generates images with DALL-E and searches the web.",
tools=[DalleTools(), WebSearchTools()],
instructions=[
"You are a creative assistant with image generation abilities.",
"Use DALL-E to generate images when asked.",
"Use web search when you need reference information.",
"Describe generated images briefly after creation.",
],
markdown=True,
)

multimodal_team = Team(
name="Multimodal Team",
mode="coordinate",
model=OpenAIChat(id="gpt-4o"),
members=[vision_analyst, creative_agent],
instructions=[
"Route image analysis and file analysis tasks to Vision Analyst.",
"Route image generation and web search tasks to Creative Agent.",
"If the user sends an image and asks to recreate or modify it, "
"first ask Vision Analyst to describe it, then ask Creative Agent "
"to generate a new version based on that description.",
],
show_members_responses=False,
markdown=True,
)

agent_os = AgentOS(
teams=[multimodal_team],
interfaces=[Whatsapp(team=multimodal_team)],
)
app = agent_os.get_app()

if __name__ == "__main__":
agent_os.serve(app="multimodal_team:app", reload=True)
102 changes: 102 additions & 0 deletions cookbook/05_agent_os/interfaces/whatsapp/multimodal_workflow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"""
Multimodal Workflow
===================

A parallel workflow that runs visual analysis and web research
simultaneously, then synthesizes findings with optional image
generation and PDF output — all delivered over WhatsApp.

Workflow structure:
Parallel:
- Visual Analysis (analyzes input images/files)
- Web Research (searches for related context)
Sequential:
- Creative Synthesis (combines results, generates images/PDFs)

Requires:
WHATSAPP_ACCESS_TOKEN, WHATSAPP_PHONE_NUMBER_ID
OPENAI_API_KEY
"""

from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.os.app import AgentOS
from agno.os.interfaces.whatsapp import Whatsapp
from agno.tools.dalle import DalleTools
from agno.tools.file_generation import FileGenerationTools
from agno.tools.websearch import WebSearchTools
from agno.workflow import Parallel, Step, Workflow

analyst = Agent(
name="Visual Analyst",
model=OpenAIChat(id="gpt-4o"),
instructions=[
"Analyze any images or files provided.",
"Describe visual elements, composition, colors, mood.",
"If no image, analyze the text topic visually.",
"Keep analysis concise but detailed.",
],
markdown=True,
)

researcher = Agent(
name="Web Researcher",
model=OpenAIChat(id="gpt-4o"),
tools=[WebSearchTools()],
instructions=[
"Search the web for information related to the user's request.",
"Provide relevant facts, trends, and context.",
],
markdown=True,
)

synthesizer = Agent(
name="Creative Synthesizer",
model=OpenAIChat(id="gpt-4o"),
tools=[DalleTools(), FileGenerationTools()],
instructions=[
"Combine the analysis and research from previous steps.",
"If the user asked for an image, generate one with DALL-E.",
"If the user asked for a report or document, generate a PDF.",
"Provide a final comprehensive response.",
],
markdown=True,
)

analysis_step = Step(
name="Visual Analysis",
agent=analyst,
description="Analyze input images/files or describe the topic visually",
)

research_step = Step(
name="Web Research",
agent=researcher,
description="Search the web for related context and information",
)

research_phase = Parallel(
analysis_step,
research_step,
name="Research Phase",
)

synthesis_step = Step(
name="Creative Synthesis",
agent=synthesizer,
description="Combine analysis + research into a final response, generate images or PDFs if requested",
)

creative_workflow = Workflow(
name="Creative Pipeline",
steps=[research_phase, synthesis_step],
)

agent_os = AgentOS(
workflows=[creative_workflow],
interfaces=[Whatsapp(workflow=creative_workflow)],
)
app = agent_os.get_app()

if __name__ == "__main__":
agent_os.serve(app="multimodal_workflow:app", reload=True)
Loading