Skip to content

Commit b1238b1

Browse files
committed
Merge branch 'main' of https://github.com/agno-agi/agno into release-1.5.5
2 parents 9f6c08d + 3233b9c commit b1238b1

File tree

10 files changed

+331
-59
lines changed

10 files changed

+331
-59
lines changed

cookbook/agent_concepts/state/last_n_session_messages.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
storage=SqliteStorage(table_name="agent_sessions_new", db_file="tmp/data.db"),
1414
add_history_to_messages=True,
1515
num_history_runs=3,
16-
search_previous_sessions_history=True, # allow searching previous sessions
17-
num_history_sessions=2, # only include the last 2 sessions in the search to avoid context length issues
16+
search_previous_sessions_history=True, # allow searching previous sessions
17+
num_history_sessions=2, # only include the last 2 sessions in the search to avoid context length issues
1818
show_tool_calls=True,
1919
)
2020

cookbook/reasoning/teams/finance_team_chain_of_thought.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ async def run_team(task: str):
6060

6161

6262
if __name__ == "__main__":
63-
64-
asyncio.run(run_team(
65-
dedent("""\
63+
asyncio.run(
64+
run_team(
65+
dedent("""\
6666
Analyze the impact of recent US tariffs on market performance across these key sectors:
6767
- Steel & Aluminum: (X, NUE, AA)
6868
- Technology Hardware: (AAPL, DELL, HPQ)
@@ -75,4 +75,5 @@ async def run_team(task: str):
7575
3. Analyze companies' strategic responses (reshoring, price adjustments, supplier diversification)
7676
4. Assess analyst outlook changes directly attributed to tariff policies
7777
""")
78-
))
78+
)
79+
)

cookbook/tools/mcp/mem0.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
"""
2+
👩‍💻 Mem0 MCP - Personalized Code Reviewer
3+
4+
This example demonstrates how to use Agno's MCP integration together with Mem0, to build a personalized code reviewer.
5+
6+
- Run your Mem0 MCP server. Full instructions: https://github.com/mem0ai/mem0-mcp
7+
- Run: `pip install agno mcp-sdk` to install the dependencies
8+
"""
9+
10+
import asyncio
11+
from textwrap import dedent
12+
13+
from agno.agent import Agent
14+
from agno.models.openai import OpenAIChat
15+
from agno.tools.mcp import MCPTools
16+
17+
mcp_server_url = "http://localhost:8080/sse"
18+
19+
20+
async def run_agent(message: str) -> None:
21+
async with MCPTools(url=mcp_server_url, transport="sse") as mcp_tools:
22+
agent = Agent(
23+
tools=[mcp_tools],
24+
model=OpenAIChat(id="o4-mini"),
25+
instructions=dedent(
26+
"""
27+
You are a professional code reviewer. You help users keep their code clean and on line with their preferences.
28+
You have access to some tools to keep track of coding preferences you need to enforce when reviewing code.
29+
You will be given a code snippet and you need to review it and provide feedback on it.
30+
"""
31+
),
32+
)
33+
await agent.aprint_response(message, stream=True)
34+
35+
36+
if __name__ == "__main__":
37+
# The agent will use mem0 memory to keep track of the user's preferences.
38+
asyncio.run(
39+
run_agent(
40+
"When possible, use the walrus operator to make the code more readable."
41+
)
42+
)
43+
# The agent will review your code and propose improvements based on your preferences.
44+
asyncio.run(
45+
run_agent(
46+
dedent(
47+
"""
48+
Please, review this Python snippet:
49+
50+
```python
51+
def process_data(data):
52+
length = len(data)
53+
if length > 10:
54+
print(f"Processing {length} items")
55+
return data[:10]
56+
return data
57+
58+
# Example usage
59+
items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
60+
result = process_data(items)
61+
```
62+
"""
63+
)
64+
)
65+
)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""
2+
🗓️ Pipedream Google Calendar MCP
3+
4+
This example shows how to use Pipedream MCP servers (in this case the Google Calendar one) with Agno Agents.
5+
6+
1. Connect your Pipedream and Google Calendar accounts: https://mcp.pipedream.com/app/google-calendar
7+
2. Get your Pipedream MCP server url: https://mcp.pipedream.com/app/google-calendar
8+
3. Set the MCP_SERVER_URL environment variable to the MCP server url you got above
9+
4. Install dependencies: pip install agno mcp-sdk
10+
"""
11+
12+
import asyncio
13+
import os
14+
15+
from agno.agent import Agent
16+
from agno.models.openai import OpenAIChat
17+
from agno.tools.mcp import MCPTools
18+
from agno.utils.log import log_exception
19+
20+
mcp_server_url = os.getenv("MCP_SERVER_URL")
21+
22+
23+
async def run_agent(task: str) -> None:
24+
try:
25+
async with MCPTools(
26+
url=mcp_server_url, transport="sse", timeout_seconds=20
27+
) as mcp:
28+
agent = Agent(
29+
model=OpenAIChat(id="gpt-4o-mini"),
30+
tools=[mcp],
31+
markdown=True,
32+
)
33+
await agent.aprint_response(message=task, stream=True)
34+
except Exception as e:
35+
log_exception(f"Unexpected error: {e}")
36+
37+
38+
if __name__ == "__main__":
39+
asyncio.run(
40+
run_agent("Tell me about all events I have in my calendar for tomorrow")
41+
)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""
2+
💻 Pipedream LinkedIn MCP
3+
4+
This example shows how to use Pipedream MCP servers (in this case the LinkedIn one) with Agno Agents.
5+
6+
1. Connect your Pipedream and LinkedIn accounts: https://mcp.pipedream.com/app/linkedin
7+
2. Get your Pipedream MCP server url: https://mcp.pipedream.com/app/linkedin
8+
3. Set the MCP_SERVER_URL environment variable to the MCP server url you got above
9+
4. Install dependencies: pip install agno mcp-sdk
10+
"""
11+
12+
import asyncio
13+
import os
14+
15+
from agno.agent import Agent
16+
from agno.models.openai import OpenAIChat
17+
from agno.tools.mcp import MCPTools
18+
from agno.utils.log import log_exception
19+
20+
mcp_server_url = os.getenv("MCP_SERVER_URL")
21+
22+
23+
async def run_agent(task: str) -> None:
24+
try:
25+
async with MCPTools(
26+
url=mcp_server_url, transport="sse", timeout_seconds=20
27+
) as mcp:
28+
agent = Agent(
29+
model=OpenAIChat(id="gpt-4o-mini"),
30+
tools=[mcp],
31+
markdown=True,
32+
)
33+
await agent.aprint_response(message=task, stream=True)
34+
except Exception as e:
35+
log_exception(f"Unexpected error: {e}")
36+
37+
38+
if __name__ == "__main__":
39+
asyncio.run(
40+
run_agent("Check the Pipedream organization on LinkedIn and tell me about it")
41+
)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""
2+
💬 Pipedream Slack MCP
3+
4+
This example shows how to use Pipedream MCP servers (in this case the Slack one) with Agno Agents.
5+
6+
1. Connect your Pipedream and Slack accounts: https://mcp.pipedream.com/app/slack
7+
2. Get your Pipedream MCP server url: https://mcp.pipedream.com/app/slack
8+
3. Set the MCP_SERVER_URL environment variable to the MCP server url you got above
9+
4. Install dependencies: pip install agno mcp-sdk
10+
11+
"""
12+
13+
import asyncio
14+
import os
15+
16+
from agno.agent import Agent
17+
from agno.models.openai import OpenAIChat
18+
from agno.tools.mcp import MCPTools
19+
from agno.utils.log import log_exception
20+
21+
mcp_server_url = os.getenv("MCP_SERVER_URL")
22+
23+
24+
async def run_agent(task: str) -> None:
25+
try:
26+
async with MCPTools(
27+
url=mcp_server_url, transport="sse", timeout_seconds=20
28+
) as mcp:
29+
agent = Agent(
30+
model=OpenAIChat(id="gpt-4o-mini"),
31+
tools=[mcp],
32+
markdown=True,
33+
)
34+
await agent.aprint_response(message=task, stream=True)
35+
except Exception as e:
36+
log_exception(f"Unexpected error: {e}")
37+
38+
39+
if __name__ == "__main__":
40+
# The agent can read channels, users, messages, etc.
41+
asyncio.run(run_agent("Show me the latest message in the channel #general"))
42+
43+
# Use your real Slack name for this one to work!
44+
asyncio.run(
45+
run_agent("Send a message to <YOUR_NAME> saying 'Hello, I'm your Agno Agent!'")
46+
)

libs/agno/agno/memory/v2/db/redis.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def __init__(
2020
port: int = 6379,
2121
db: int = 0,
2222
password: Optional[str] = None,
23+
expire: Optional[int] = None,
2324
):
2425
"""
2526
Initialize Redis memory store.
@@ -30,8 +31,10 @@ def __init__(
3031
port (int): Redis port number
3132
db (int): Redis database number
3233
password (Optional[str]): Redis password if authentication is required
34+
expire (Optional[int]): TTL (time to live) in seconds for Redis keys. None means no expiration.
3335
"""
3436
self.prefix = prefix
37+
self.expire = expire
3538
self.redis_client = Redis(
3639
host=host,
3740
port=port,
@@ -45,6 +48,7 @@ def __dict__(self) -> Dict[str, Any]:
4548
return {
4649
"name": "RedisMemoryDb",
4750
"prefix": self.prefix,
51+
"expire": self.expire,
4852
}
4953

5054
def _get_key(self, memory_id: str) -> str:
@@ -128,7 +132,11 @@ def upsert_memory(self, memory: MemoryRow) -> Optional[MemoryRow]:
128132

129133
# Save to Redis
130134
key = self._get_key(memory.id) # type: ignore
131-
self.redis_client.set(key, json.dumps(memory_data))
135+
if self.expire is not None:
136+
self.redis_client.set(key, json.dumps(memory_data), ex=self.expire)
137+
else:
138+
self.redis_client.set(key, json.dumps(memory_data))
139+
132140
return memory
133141

134142
except Exception as e:

libs/agno/agno/storage/redis.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def __init__(
2626
password: Optional[str] = None,
2727
mode: Optional[Literal["agent", "team", "workflow"]] = "agent",
2828
ssl: Optional[bool] = False,
29+
expire: Optional[int] = None,
2930
):
3031
"""
3132
Initialize Redis storage for sessions.
@@ -38,9 +39,11 @@ def __init__(
3839
password (Optional[str]): Redis password if authentication is required
3940
mode (Optional[Literal["agent", "team", "workflow"]]): Storage mode
4041
ssl (Optional[bool]): Whether to use SSL for Redis connection
42+
expire (Optional[int]): TTL (time to live) in seconds for Redis keys. None means no expiration.
4143
"""
4244
super().__init__(mode)
4345
self.prefix = prefix
46+
self.expire = expire
4447
self.redis_client = Redis(
4548
host=host,
4649
port=port,
@@ -267,7 +270,10 @@ def upsert(self, session: Session) -> Optional[Session]:
267270
data["created_at"] = data["updated_at"]
268271

269272
key = self._get_key(session.session_id)
270-
self.redis_client.set(key, self.serialize(data))
273+
if self.expire is not None:
274+
self.redis_client.set(key, self.serialize(data), ex=self.expire)
275+
else:
276+
self.redis_client.set(key, self.serialize(data))
271277
return session
272278
except Exception as e:
273279
logger.error(f"Error upserting session: {e}")

0 commit comments

Comments
 (0)