Skip to content

Commit bb565c0

Browse files
harrytran001Mustafa-Esoofallydirkbrndysolanky
authored
feat: add AsyncMySQLDb (#5425)
## Summary Describe key changes, mention related issues or motivation for the changes. Issue number: #5427 Docs Change PR: [PR](agno-agi/agno-docs#266) ## Type of change - [ ] Bug fix - [X] New feature - [ ] Breaking change - [ ] Improvement - [ ] Model update - [ ] Other: --- ## Checklist - [X] Code complies with style guidelines - [X] Ran format/validation scripts (`./scripts/format.sh` and `./scripts/validate.sh`) - [X] Self-review completed - [X] Documentation updated (comments, docstrings) - [X] Examples and guides: Relevant cookbook examples have been included or updated (if applicable) - [X] Tested in clean environment - [X] Tests added/updated (if applicable) --- ## Additional Notes Add any important context (deployment instructions, screenshots, security considerations, etc.) --------- Co-authored-by: Mustafa Esoofally <[email protected]> Co-authored-by: Dirk Brand <[email protected]> Co-authored-by: Yash Pratap Solanky <[email protected]> Co-authored-by: ysolanky <[email protected]>
1 parent 861919a commit bb565c0

File tree

19 files changed

+3765
-18
lines changed

19 files changed

+3765
-18
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""Example showing how to use AgentOS with a SQLite database"""
2+
3+
from agno.agent import Agent
4+
from agno.db.mysql import AsyncMySQLDb
5+
from agno.models.openai import OpenAIChat
6+
from agno.os import AgentOS
7+
from agno.team.team import Team
8+
9+
db_url = "mysql+asyncmy://ai:ai@localhost:3306/ai"
10+
11+
db = AsyncMySQLDb(
12+
id="mysql-demo",
13+
db_url=db_url,
14+
session_table="sessions",
15+
eval_table="eval_runs",
16+
memory_table="user_memories",
17+
metrics_table="metrics",
18+
)
19+
20+
21+
# Setup a basic agent and a basic team
22+
basic_agent = Agent(
23+
name="Basic Agent",
24+
id="basic-agent",
25+
model=OpenAIChat(id="gpt-4o"),
26+
db=db,
27+
enable_user_memories=True,
28+
enable_session_summaries=True,
29+
add_history_to_context=True,
30+
num_history_runs=3,
31+
add_datetime_to_context=True,
32+
markdown=True,
33+
)
34+
team_agent = Team(
35+
id="basic-team",
36+
name="Team Agent",
37+
model=OpenAIChat(id="gpt-4o"),
38+
db=db,
39+
members=[basic_agent],
40+
)
41+
42+
agent_os = AgentOS(
43+
description="Example OS setup",
44+
agents=[basic_agent],
45+
teams=[team_agent],
46+
)
47+
app = agent_os.get_app()
48+
49+
if __name__ == "__main__":
50+
agent_os.serve(app="async_mysql_demo:app", reload=True)

cookbook/db/mysql/README.md

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,84 @@
11
# MySQL Integration
22

3-
Examples demonstrating MySQL database integration with Agno agents and teams.
3+
Examples demonstrating MySQL database integration with Agno agents, teams, and workflows.
44

55
## Setup
66

7+
### Synchronous MySQL
8+
79
```shell
8-
pip install mysql-connector-python
10+
pip install mysql-connector-python sqlalchemy pymysql
11+
```
12+
13+
### Asynchronous MySQL
14+
15+
```shell
16+
pip install sqlalchemy asyncmy
917
```
1018

1119
## Configuration
1220

21+
### Synchronous MySQL
22+
23+
```python
24+
from agno.agent import Agent
25+
from agno.db.mysql import MySQLDb
26+
27+
db = MySQLDb(db_url="mysql+pymysql://username:password@localhost:3306/database")
28+
29+
agent = Agent(
30+
db=db,
31+
add_history_to_context=True,
32+
)
33+
```
34+
35+
### Asynchronous MySQL
36+
1337
```python
38+
import asyncio
1439
from agno.agent import Agent
15-
from agno.db.mysql import MySqlDb
40+
from agno.db.mysql import AsyncMySQLDb
1641

17-
db = MySqlDb(db_url="mysql+pymysql://username:password@localhost:3306/database")
42+
db = AsyncMySQLDb(db_url="mysql+asyncmy://username:password@localhost:3306/database")
1843

1944
agent = Agent(
2045
db=db,
2146
add_history_to_context=True,
2247
)
48+
49+
asyncio.run(agent.aprint_response("Hello!"))
2350
```
2451

25-
## Examples
52+
## Synchronous Examples
2653

2754
- [`mysql_for_agent.py`](mysql_for_agent.py) - Agent with MySQL storage
2855
- [`mysql_for_team.py`](mysql_for_team.py) - Team with MySQL storage
56+
57+
## Asynchronous Examples
58+
59+
- [`async_mysql/async_mysql_for_agent.py`](async_mysql/async_mysql_for_agent.py) - Agent with Async MySQL storage
60+
- [`async_mysql/async_mysql_for_team.py`](async_mysql/async_mysql_for_team.py) - Team with Async MySQL storage
61+
- [`async_mysql/async_mysql_for_workflow.py`](async_mysql/async_mysql_for_workflow.py) - Workflow with Async MySQL storage
62+
63+
## Database URL Format
64+
65+
### Synchronous Drivers
66+
67+
- **PyMySQL**: `mysql+pymysql://user:password@host:port/database`
68+
- **MySQL Connector/Python**: `mysql+mysqlconnector://user:password@host:port/database`
69+
70+
### Asynchronous Drivers
71+
72+
- **asyncmy**: `mysql+asyncmy://user:password@host:port/database`
73+
74+
## Async vs Sync
75+
76+
Choose **AsyncMySQLDb** when:
77+
- Building high-concurrency applications
78+
- Working with async frameworks (FastAPI, Sanic, etc.)
79+
- Need non-blocking database operations
80+
81+
Choose **MySQLDb** when:
82+
- Building traditional synchronous applications
83+
- Simpler deployment requirements
84+
- Working with sync-only libraries
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Async MySQL integration examples
2+
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""Use Async MySQL as the database for an agent.
2+
Run `pip install openai duckduckgo-search sqlalchemy asyncmy agno` to install dependencies.
3+
"""
4+
5+
import asyncio
6+
import uuid
7+
8+
from agno.agent import Agent
9+
from agno.db.base import SessionType
10+
from agno.db.mysql import AsyncMySQLDb
11+
from agno.tools.duckduckgo import DuckDuckGoTools
12+
13+
db_url = "mysql+asyncmy://ai:ai@localhost:3306/ai"
14+
db = AsyncMySQLDb(db_url=db_url)
15+
16+
agent = Agent(
17+
db=db,
18+
tools=[DuckDuckGoTools()],
19+
add_history_to_context=True,
20+
add_datetime_to_context=True,
21+
)
22+
23+
24+
async def main():
25+
"""Run the agent queries in the same event loop"""
26+
session_id = str(uuid.uuid4())
27+
await agent.aprint_response(
28+
"How many people live in Canada?", session_id=session_id
29+
)
30+
await agent.aprint_response(
31+
"What is their national anthem called?", session_id=session_id
32+
)
33+
session_data = await db.get_session(
34+
session_id=session_id, session_type=SessionType.AGENT
35+
)
36+
print("\n=== SESSION DATA ===")
37+
print(session_data.to_dict())
38+
39+
40+
if __name__ == "__main__":
41+
asyncio.run(main())
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"""Use Async MySQL as the database for a team.
2+
Run `pip install openai duckduckgo-search newspaper4k lxml_html_clean agno sqlalchemy asyncmy` to install the dependencies
3+
"""
4+
5+
import asyncio
6+
import uuid
7+
from typing import List
8+
9+
from agno.agent import Agent
10+
from agno.db.base import SessionType
11+
from agno.db.mysql import AsyncMySQLDb
12+
from agno.team import Team
13+
from agno.tools.duckduckgo import DuckDuckGoTools
14+
from agno.tools.hackernews import HackerNewsTools
15+
from pydantic import BaseModel
16+
17+
db_url = "mysql+asyncmy://ai:ai@localhost:3306/ai"
18+
db = AsyncMySQLDb(db_url=db_url)
19+
20+
21+
class Article(BaseModel):
22+
title: str
23+
summary: str
24+
reference_links: List[str]
25+
26+
27+
hn_researcher = Agent(
28+
name="HackerNews Researcher",
29+
role="Gets top stories from hackernews.",
30+
tools=[HackerNewsTools()],
31+
)
32+
33+
web_searcher = Agent(
34+
name="Web Searcher",
35+
role="Searches the web for information on a topic",
36+
tools=[DuckDuckGoTools()],
37+
add_datetime_to_context=True,
38+
)
39+
40+
41+
hn_team = Team(
42+
name="HackerNews Team",
43+
members=[hn_researcher, web_searcher],
44+
db=db,
45+
instructions=[
46+
"First, search hackernews for what the user is asking about.",
47+
"Then, ask the web searcher to search for each story to get more information.",
48+
"Finally, provide a thoughtful and engaging summary.",
49+
],
50+
output_schema=Article,
51+
markdown=True,
52+
show_members_responses=True,
53+
)
54+
55+
56+
async def main():
57+
"""Run the agent queries in the same event loop"""
58+
session_id = str(uuid.uuid4())
59+
await hn_team.aprint_response(
60+
"Write an article about the top 2 stories on hackernews", session_id=session_id
61+
)
62+
session_data = await db.get_session(
63+
session_id=session_id, session_type=SessionType.TEAM
64+
)
65+
print("\n=== SESSION DATA ===")
66+
print(session_data.to_dict())
67+
68+
69+
if __name__ == "__main__":
70+
asyncio.run(main())
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
"""Use Async MySQL as the database for a workflow.
2+
Run `pip install openai duckduckgo-search sqlalchemy asyncmy agno` to install dependencies.
3+
"""
4+
5+
import asyncio
6+
import uuid
7+
from typing import List
8+
9+
from agno.agent import Agent
10+
from agno.db.base import SessionType
11+
from agno.db.mysql import AsyncMySQLDb
12+
from agno.tools.duckduckgo import DuckDuckGoTools
13+
from agno.workflow.types import WorkflowExecutionInput
14+
from agno.workflow.workflow import Workflow
15+
from pydantic import BaseModel
16+
17+
db_url = "mysql+asyncmy://ai:ai@localhost:3306/ai"
18+
db = AsyncMySQLDb(db_url=db_url)
19+
20+
21+
class ResearchTopic(BaseModel):
22+
topic: str
23+
key_points: List[str]
24+
summary: str
25+
26+
27+
# Create researcher agent
28+
researcher = Agent(
29+
name="Researcher",
30+
tools=[DuckDuckGoTools()],
31+
instructions="Research the given topic thoroughly and provide key insights",
32+
output_schema=ResearchTopic,
33+
)
34+
35+
# Create writer agent
36+
writer = Agent(
37+
name="Writer",
38+
instructions="Write a well-structured blog post based on the research provided",
39+
)
40+
41+
42+
# Define the workflow
43+
async def blog_workflow(workflow: Workflow, execution_input: WorkflowExecutionInput):
44+
"""
45+
A workflow that researches a topic and writes a blog post about it.
46+
"""
47+
topic = execution_input.input
48+
49+
# Step 1: Research the topic
50+
research_result = await researcher.arun(f"Research this topic: {topic}")
51+
52+
# Step 2: Write the blog post
53+
if research_result and research_result.content:
54+
blog_result = await writer.arun(
55+
f"Write a blog post about {topic}. Use this research: {research_result.content.model_dump_json()}"
56+
)
57+
return blog_result.content
58+
59+
return "Failed to complete workflow"
60+
61+
62+
# Create and run the workflow
63+
workflow = Workflow(
64+
name="Blog Generator",
65+
steps=blog_workflow,
66+
db=db,
67+
)
68+
69+
70+
async def main():
71+
"""Run the workflow with a sample topic"""
72+
session_id = str(uuid.uuid4())
73+
74+
await workflow.aprint_response(
75+
input="The future of artificial intelligence",
76+
session_id=session_id,
77+
markdown=True,
78+
)
79+
session_data = await db.get_session(
80+
session_id=session_id, session_type=SessionType.WORKFLOW
81+
)
82+
print("\n=== SESSION DATA ===")
83+
print(session_data.to_dict())
84+
85+
86+
if __name__ == "__main__":
87+
asyncio.run(main())
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from agno.db.mysql.async_mysql import AsyncMySQLDb
12
from agno.db.mysql.mysql import MySQLDb
23

3-
__all__ = ["MySQLDb"]
4+
__all__ = ["MySQLDb", "AsyncMySQLDb"]

0 commit comments

Comments
 (0)