Skip to content

Commit 4022ea1

Browse files
authored
chore: Release 1.7.7 (#4056)
# Changelog ## New Features: - **Morph Tools:** Morph’s Fast Apply model as a Tool, it intelligently merges your original code with update snippets at 98% accuracy and 4500+ tokens/second. ## Improvements: - **LiteLLM File & Image Understanding**: Added support for file and image inputs for `LiteLLM`. ## Bug Fixes: - **OpenAIEmbedder() wrong dimensions for text-embedding-3-large:** Automatically handles default `dimensions` length for `text-embedding-3-small` as 1536 and `text-embedding-3-large` as 3072.
1 parent ccc5203 commit 4022ea1

File tree

10 files changed

+59
-85
lines changed

10 files changed

+59
-85
lines changed

cookbook/observability/langwatch_op.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,17 @@
66
3. Set your LangWatch API key as an environment variables:
77
- export LANGWATCH_API_KEY=<your-key>
88
"""
9+
910
import os
10-
import langwatch
1111

12+
import langwatch
1213
from agno.agent import Agent
1314
from agno.models.openai import OpenAIChat
1415
from agno.tools.yfinance import YFinanceTools
1516
from openinference.instrumentation.agno import AgnoInstrumentor
1617

1718
# Initialize LangWatch and instrument Agno
18-
langwatch.setup(
19-
instrumentors=[AgnoInstrumentor()]
20-
)
19+
langwatch.setup(instrumentors=[AgnoInstrumentor()])
2120

2221
# Create and configure your Agno agent
2322
agent = Agent(
@@ -28,4 +27,4 @@
2827
debug_mode=True,
2928
)
3029

31-
agent.print_response("What is the current price of Tesla?")
30+
agent.print_response("What is the current price of Tesla?")

cookbook/workflows_2/sync/01_basic_workflows/basic_workflow.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
1-
from pydantic import BaseModel, Field
2-
31
from agno.agent import Agent
42
from agno.models.openai import OpenAIChat
53
from agno.storage.postgres import PostgresStorage
64
from agno.workflow.v2.step import Step
75
from agno.workflow.v2.workflow import Workflow
6+
from pydantic import BaseModel, Field
87

98
db_url = "postgresql+psycopg://ai:ai@localhost:5532/ai"
109

10+
1111
# Define response format
1212
class Source(BaseModel):
1313
name: str = Field(description="The name of the source")
1414
page_number: int = Field(description="The page number of the source")
1515

16+
1617
class Response(BaseModel):
1718
response: str = Field(description="The response to the user's query")
18-
sources: list[Source] = Field(description="The sources used to generate the response")
19+
sources: list[Source] = Field(
20+
description="The sources used to generate the response"
21+
)
22+
1923

2024
# Define agents
2125
information_agent = Agent(

libs/agno/agno/agent/agent.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5282,7 +5282,7 @@ def get_relevant_docs_from_knowledge(
52825282
"""
52835283
from agno.document import Document
52845284

5285-
if num_documents is None:
5285+
if num_documents is None and self.knowledge is not None:
52865286
num_documents = self.knowledge.num_documents
52875287
# Validate the filters against known valid filter keys
52885288
if self.knowledge is not None:
@@ -5343,7 +5343,7 @@ async def aget_relevant_docs_from_knowledge(
53435343
"""Get relevant documents from knowledge base asynchronously."""
53445344
from agno.document import Document
53455345

5346-
if num_documents is None:
5346+
if num_documents is None and self.knowledge is not None:
53475347
num_documents = self.knowledge.num_documents
53485348

53495349
# Validate the filters against known valid filter keys

libs/agno/agno/embedder/openai.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,9 @@ class OpenAIEmbedder(Embedder):
2626
client_params: Optional[Dict[str, Any]] = None
2727
openai_client: Optional[OpenAIClient] = None
2828

29-
@property
30-
def _dimensions(self) -> int:
31-
if self.dimensions is not None:
32-
return self.dimensions
33-
return 3072 if self.id == "text-embedding-3-large" else 1536
29+
def __post_init__(self):
30+
if self.dimensions is None:
31+
self.dimensions = 3072 if self.id == "text-embedding-3-large" else 1536
3432

3533
@property
3634
def client(self) -> OpenAIClient:
@@ -57,7 +55,7 @@ def response(self, text: str) -> CreateEmbeddingResponse:
5755
if self.user is not None:
5856
_request_params["user"] = self.user
5957
if self.id.startswith("text-embedding-3"):
60-
_request_params["dimensions"] = self._dimensions
58+
_request_params["dimensions"] = self.dimensions
6159
if self.request_params:
6260
_request_params.update(self.request_params)
6361
return self.client.embeddings.create(**_request_params)

libs/agno/agno/team/team.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7372,7 +7372,7 @@ def get_relevant_docs_from_knowledge(
73727372
"""Return a list of references from the knowledge base"""
73737373
from agno.document import Document
73747374

7375-
if num_documents is None:
7375+
if num_documents is None and self.knowledge is not None:
73767376
num_documents = self.knowledge.num_documents
73777377

73787378
# Validate the filters against known valid filter keys
@@ -7429,7 +7429,7 @@ async def aget_relevant_docs_from_knowledge(
74297429
"""Get relevant documents from knowledge base asynchronously."""
74307430
from agno.document import Document
74317431

7432-
if num_documents is None:
7432+
if num_documents is None and self.knowledge is not None:
74337433
num_documents = self.knowledge.num_documents
74347434

74357435
# Validate the filters against known valid filter keys

libs/agno/agno/tools/jina.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import json
21
from os import getenv
32
from typing import Any, Dict, List, Optional
43

54
import httpx
65
from pydantic import BaseModel, Field, HttpUrl
76

87
from agno.tools import Toolkit
9-
from agno.utils.log import log_info, logger
8+
from agno.utils.log import logger
109

1110

1211
class JinaReaderToolsConfig(BaseModel):

libs/agno/agno/tools/zep.py

Lines changed: 21 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ def initialize(self) -> bool:
118118

119119
# Create session associated with the user
120120
try:
121-
self.zep_client.memory.add_session(session_id=self.session_id, user_id=self.user_id)
121+
self.zep_client.thread.create(thread_id=self.session_id, user_id=self.user_id)
122122
log_debug(f"Created session {self.session_id} for user {self.user_id}")
123123
except Exception as e:
124124
log_debug(f"Session may already exist: {e}")
@@ -148,16 +148,16 @@ def add_zep_message(self, role: str, content: str) -> str:
148148

149149
try:
150150
zep_message = ZepMessage(
151-
role_type=role,
151+
role=role,
152152
content=content,
153153
)
154154

155155
# Prepare ignore_roles if needed
156156
ignore_roles_list = ["assistant"] if self.ignore_assistant_messages else None
157157

158158
# Add message to Zep memory
159-
self.zep_client.memory.add( # type: ignore
160-
session_id=self.session_id,
159+
self.zep_client.thread.add_messages( # type: ignore
160+
thread_id=self.session_id,
161161
messages=[zep_message],
162162
ignore_roles=ignore_roles_list,
163163
)
@@ -171,7 +171,7 @@ def get_zep_memory(self, memory_type: str = "context") -> str:
171171
"""
172172
Retrieves the memory for the current Zep session.
173173
Args:
174-
memory_type: The type of memory to retrieve ('context', 'messages', 'relevant_facts').
174+
memory_type: The type of memory to retrieve ('context', 'messages').
175175
Returns:
176176
The requested memory content as a string, or an error string.
177177
"""
@@ -181,21 +181,16 @@ def get_zep_memory(self, memory_type: str = "context") -> str:
181181

182182
try:
183183
log_debug(f"Getting Zep memory for session {self.session_id}")
184-
memory_data = self.zep_client.memory.get(session_id=self.session_id) # type: ignore
185184

186185
if memory_type == "context":
187186
# Ensure context is a string
188-
return memory_data.context or "No context available."
187+
user_context = self.zep_client.thread.get_user_context(thread_id=self.session_id, mode="basic") # type: ignore
188+
log_debug(f"Memory data: {user_context}")
189+
return user_context.context or "No context available."
189190
elif memory_type == "messages":
191+
messages_list = self.zep_client.thread.get(thread_id=self.session_id) # type: ignore
190192
# Ensure messages string representation is returned
191-
return str(memory_data.messages) if memory_data.messages else "No messages available."
192-
elif memory_type == "relevant_facts":
193-
# Return all relevant facts from memory
194-
if memory_data.relevant_facts:
195-
facts_str = "\n".join([f"- {fact.fact}" for fact in memory_data.relevant_facts])
196-
return f"Relevant facts:\n{facts_str}"
197-
else:
198-
return "No relevant facts available."
193+
return str(messages_list.messages) if messages_list.messages else "No messages available."
199194
else:
200195
warning_msg = f"Unsupported memory_type requested: {memory_type}. Returning empty string."
201196
log_warning(warning_msg)
@@ -328,7 +323,7 @@ async def initialize(self) -> bool:
328323

329324
# Create session associated with the user
330325
try:
331-
await self.zep_client.memory.add_session(session_id=self.session_id, user_id=self.user_id) # type: ignore
326+
await self.zep_client.thread.create(thread_id=self.session_id, user_id=self.user_id) # type: ignore
332327
log_debug(f"Created session {self.session_id} for user {self.user_id}")
333328
except Exception as e:
334329
log_debug(f"Session may already exist: {e}")
@@ -361,16 +356,16 @@ async def add_zep_message(self, role: str, content: str) -> str:
361356

362357
try:
363358
zep_message = ZepMessage(
364-
role_type=role,
359+
role=role,
365360
content=content,
366361
)
367362

368363
# Prepare ignore_roles if needed
369364
ignore_roles_list = ["assistant"] if self.ignore_assistant_messages else None
370365

371366
# Add message to Zep memory
372-
await self.zep_client.memory.add( # type: ignore
373-
session_id=self.session_id,
367+
await self.zep_client.thread.add_messages( # type: ignore
368+
thread_id=self.session_id,
374369
messages=[zep_message],
375370
ignore_roles=ignore_roles_list,
376371
)
@@ -384,7 +379,7 @@ async def get_zep_memory(self, memory_type: str = "context") -> str:
384379
"""
385380
Retrieves the memory for the current Zep session.
386381
Args:
387-
memory_type: The type of memory to retrieve ('context', 'messages', 'relevant_facts').
382+
memory_type: The type of memory to retrieve ('context', 'messages').
388383
Returns:
389384
The requested memory content as a string, or an error string.
390385
"""
@@ -396,25 +391,19 @@ async def get_zep_memory(self, memory_type: str = "context") -> str:
396391
return "Error: Zep client/session not initialized."
397392

398393
try:
399-
memory_data = await self.zep_client.memory.get(session_id=self.session_id) # type: ignore
400-
401394
if memory_type == "context":
402395
# Ensure context is a string
403-
return memory_data.context or "No context available."
396+
user_context = await self.zep_client.thread.get_user_context(thread_id=self.session_id, mode="basic") # type: ignore
397+
log_debug(f"Memory data: {user_context}")
398+
return user_context.context or "No context available."
404399
elif memory_type == "messages":
405400
# Ensure messages string representation is returned
406-
return str(memory_data.messages) if memory_data.messages else "No messages available."
407-
elif memory_type == "relevant_facts":
408-
# Return relevant facts from memory
409-
if memory_data.relevant_facts:
410-
facts_str = "\n".join([f"- {fact.fact}" for fact in memory_data.relevant_facts])
411-
return f"Relevant facts:\n{facts_str}"
412-
else:
413-
return "No relevant facts available."
401+
messages_list = await self.zep_client.thread.get(thread_id=self.session_id) # type: ignore
402+
return str(messages_list.messages) if messages_list.messages else "No messages available."
414403
else:
415404
warning_msg = f"Unsupported memory_type requested: {memory_type}. Returning context."
416405
log_warning(warning_msg)
417-
return memory_data.context or "No context available."
406+
return "No context available."
418407

419408
except Exception as e:
420409
error_msg = f"Failed to get Zep memory for session {self.session_id}: {e}"

libs/agno/pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "agno"
3-
version = "1.7.6"
3+
version = "1.7.7"
44
description = "Agno: a lightweight library for building Multi-Agent Systems"
55
requires-python = ">=3.7,<4"
66
readme = "README.md"
@@ -107,7 +107,7 @@ mcp = ["mcp"]
107107
mem0 = ["mem0ai"]
108108
newspaper = ["newspaper4k", "lxml_html_clean"]
109109
opencv = ["opencv-python"]
110-
psycopg = ["psycopg-binary"]
110+
psycopg = ["psycopg-binary", "psycopg"]
111111
todoist = ["todoist-api-python"]
112112
valyu = ["valyu"]
113113
webex = ["webexpythonsdk"]

libs/agno/tests/unit/tools/test_jina.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,7 @@ def test_read_url_with_truncation(mock_httpx_get):
227227

228228

229229
@patch("agno.tools.jina.httpx.post")
230-
@patch("agno.tools.jina.log_info")
231-
def test_search_query_successful(mock_log_info, mock_httpx_post, sample_search_query_response):
230+
def test_search_query_successful(mock_httpx_post, sample_search_query_response):
232231
"""Test successful search query"""
233232
# Setup mock response
234233
mock_response = MagicMock()

0 commit comments

Comments
 (0)