agno 2.3.9__py3-none-any.whl → 2.3.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agno/agent/agent.py +0 -12
- agno/db/base.py +5 -5
- agno/db/dynamo/dynamo.py +2 -2
- agno/db/firestore/firestore.py +2 -2
- agno/db/gcs_json/gcs_json_db.py +2 -2
- agno/db/in_memory/in_memory_db.py +2 -2
- agno/db/json/json_db.py +2 -2
- agno/db/mongo/async_mongo.py +171 -69
- agno/db/mongo/mongo.py +171 -77
- agno/db/mysql/async_mysql.py +93 -69
- agno/db/mysql/mysql.py +93 -68
- agno/db/postgres/async_postgres.py +104 -78
- agno/db/postgres/postgres.py +97 -69
- agno/db/redis/redis.py +2 -2
- agno/db/singlestore/singlestore.py +91 -66
- agno/db/sqlite/async_sqlite.py +102 -79
- agno/db/sqlite/sqlite.py +97 -69
- agno/db/surrealdb/surrealdb.py +2 -2
- agno/eval/accuracy.py +11 -8
- agno/eval/agent_as_judge.py +9 -8
- agno/knowledge/chunking/fixed.py +4 -1
- agno/knowledge/embedder/openai.py +1 -1
- agno/knowledge/knowledge.py +22 -4
- agno/knowledge/utils.py +52 -7
- agno/models/base.py +34 -1
- agno/models/google/gemini.py +69 -40
- agno/models/message.py +3 -0
- agno/models/openai/chat.py +21 -0
- agno/os/routers/evals/utils.py +15 -37
- agno/os/routers/knowledge/knowledge.py +21 -9
- agno/team/team.py +14 -8
- agno/tools/function.py +37 -23
- agno/tools/shopify.py +1519 -0
- agno/tools/spotify.py +2 -5
- agno/tracing/exporter.py +2 -2
- agno/vectordb/base.py +15 -2
- agno/vectordb/pgvector/pgvector.py +8 -8
- agno/workflow/parallel.py +2 -0
- {agno-2.3.9.dist-info → agno-2.3.11.dist-info}/METADATA +1 -1
- {agno-2.3.9.dist-info → agno-2.3.11.dist-info}/RECORD +43 -42
- {agno-2.3.9.dist-info → agno-2.3.11.dist-info}/WHEEL +0 -0
- {agno-2.3.9.dist-info → agno-2.3.11.dist-info}/licenses/LICENSE +0 -0
- {agno-2.3.9.dist-info → agno-2.3.11.dist-info}/top_level.txt +0 -0
agno/knowledge/utils.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
from typing import Dict, List
|
|
1
|
+
from typing import Any, Dict, List, Optional
|
|
2
2
|
|
|
3
|
+
from agno.knowledge.reader.base import Reader
|
|
3
4
|
from agno.knowledge.reader.reader_factory import ReaderFactory
|
|
4
5
|
from agno.knowledge.types import ContentType
|
|
5
6
|
from agno.utils.log import log_debug
|
|
@@ -75,8 +76,33 @@ def get_reader_info(reader_key: str) -> Dict:
|
|
|
75
76
|
raise ValueError(f"Unknown reader: {reader_key}. Error: {str(e)}")
|
|
76
77
|
|
|
77
78
|
|
|
78
|
-
def
|
|
79
|
-
"""Get information about
|
|
79
|
+
def get_reader_info_from_instance(reader: Reader, reader_id: str) -> Dict:
|
|
80
|
+
"""Get information about a reader instance."""
|
|
81
|
+
try:
|
|
82
|
+
reader_class = reader.__class__
|
|
83
|
+
supported_strategies = reader_class.get_supported_chunking_strategies()
|
|
84
|
+
supported_content_types = reader_class.get_supported_content_types()
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
"id": reader_id,
|
|
88
|
+
"name": getattr(reader, "name", reader_class.__name__),
|
|
89
|
+
"description": getattr(reader, "description", f"Custom {reader_class.__name__}"),
|
|
90
|
+
"chunking_strategies": [strategy.value for strategy in supported_strategies],
|
|
91
|
+
"content_types": [ct.value for ct in supported_content_types],
|
|
92
|
+
}
|
|
93
|
+
except Exception as e:
|
|
94
|
+
raise ValueError(f"Failed to get info for reader '{reader_id}': {str(e)}")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def get_all_readers_info(knowledge_instance: Optional[Any] = None) -> List[Dict]:
|
|
98
|
+
"""Get information about all available readers, including custom readers from a Knowledge instance.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
knowledge_instance: Optional Knowledge instance to include custom readers from.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
List of reader info dictionaries.
|
|
105
|
+
"""
|
|
80
106
|
readers_info = []
|
|
81
107
|
keys = ReaderFactory.get_all_reader_keys()
|
|
82
108
|
for key in keys:
|
|
@@ -88,18 +114,35 @@ def get_all_readers_info() -> List[Dict]:
|
|
|
88
114
|
# Log the error but don't fail the entire request
|
|
89
115
|
log_debug(f"Skipping reader '{key}': {e}")
|
|
90
116
|
continue
|
|
117
|
+
|
|
118
|
+
# Add custom readers from knowledge instance if provided
|
|
119
|
+
if knowledge_instance is not None:
|
|
120
|
+
custom_readers = knowledge_instance.get_readers()
|
|
121
|
+
if isinstance(custom_readers, dict):
|
|
122
|
+
for reader_id, reader in custom_readers.items():
|
|
123
|
+
try:
|
|
124
|
+
reader_info = get_reader_info_from_instance(reader, reader_id)
|
|
125
|
+
# Only add if not already present (custom readers take precedence)
|
|
126
|
+
if not any(r["id"] == reader_id for r in readers_info):
|
|
127
|
+
readers_info.append(reader_info)
|
|
128
|
+
except ValueError as e:
|
|
129
|
+
log_debug(f"Skipping custom reader '{reader_id}': {e}")
|
|
130
|
+
continue
|
|
131
|
+
|
|
91
132
|
return readers_info
|
|
92
133
|
|
|
93
134
|
|
|
94
|
-
def get_content_types_to_readers_mapping() -> Dict[str, List[str]]:
|
|
135
|
+
def get_content_types_to_readers_mapping(knowledge_instance: Optional[Any] = None) -> Dict[str, List[str]]:
|
|
95
136
|
"""Get mapping of content types to list of reader IDs that support them.
|
|
96
137
|
|
|
138
|
+
Args:
|
|
139
|
+
knowledge_instance: Optional Knowledge instance to include custom readers from.
|
|
140
|
+
|
|
97
141
|
Returns:
|
|
98
142
|
Dictionary mapping content type strings (ContentType enum values) to list of reader IDs.
|
|
99
143
|
"""
|
|
100
144
|
content_type_mapping: Dict[str, List[str]] = {}
|
|
101
|
-
readers_info = get_all_readers_info()
|
|
102
|
-
|
|
145
|
+
readers_info = get_all_readers_info(knowledge_instance)
|
|
103
146
|
for reader_info in readers_info:
|
|
104
147
|
reader_id = reader_info["id"]
|
|
105
148
|
content_types = reader_info.get("content_types", [])
|
|
@@ -107,7 +150,9 @@ def get_content_types_to_readers_mapping() -> Dict[str, List[str]]:
|
|
|
107
150
|
for content_type in content_types:
|
|
108
151
|
if content_type not in content_type_mapping:
|
|
109
152
|
content_type_mapping[content_type] = []
|
|
110
|
-
|
|
153
|
+
# Avoid duplicates
|
|
154
|
+
if reader_id not in content_type_mapping[content_type]:
|
|
155
|
+
content_type_mapping[content_type].append(reader_id)
|
|
111
156
|
|
|
112
157
|
return content_type_mapping
|
|
113
158
|
|
agno/models/base.py
CHANGED
|
@@ -1801,6 +1801,17 @@ class Model(ABC):
|
|
|
1801
1801
|
log_error(f"Error while iterating function result generator for {function_call.function.name}: {e}")
|
|
1802
1802
|
function_call.error = str(e)
|
|
1803
1803
|
function_call_success = False
|
|
1804
|
+
|
|
1805
|
+
# For generators, re-capture updated_session_state after consumption
|
|
1806
|
+
# since session_state modifications were made during iteration
|
|
1807
|
+
if function_execution_result.updated_session_state is None:
|
|
1808
|
+
if (
|
|
1809
|
+
function_call.function._run_context is not None
|
|
1810
|
+
and function_call.function._run_context.session_state is not None
|
|
1811
|
+
):
|
|
1812
|
+
function_execution_result.updated_session_state = function_call.function._run_context.session_state
|
|
1813
|
+
elif function_call.function._session_state is not None:
|
|
1814
|
+
function_execution_result.updated_session_state = function_call.function._session_state
|
|
1804
1815
|
else:
|
|
1805
1816
|
from agno.tools.function import ToolResult
|
|
1806
1817
|
|
|
@@ -2327,7 +2338,29 @@ class Model(ABC):
|
|
|
2327
2338
|
log_error(f"Error while iterating function result generator for {function_call.function.name}: {e}")
|
|
2328
2339
|
function_call.error = str(e)
|
|
2329
2340
|
function_call_success = False
|
|
2330
|
-
|
|
2341
|
+
|
|
2342
|
+
# For generators (sync or async), re-capture updated_session_state after consumption
|
|
2343
|
+
# since session_state modifications were made during iteration
|
|
2344
|
+
if async_function_call_output is not None or isinstance(
|
|
2345
|
+
function_call.result,
|
|
2346
|
+
(GeneratorType, collections.abc.Iterator, AsyncGeneratorType, collections.abc.AsyncIterator),
|
|
2347
|
+
):
|
|
2348
|
+
if updated_session_state is None:
|
|
2349
|
+
if (
|
|
2350
|
+
function_call.function._run_context is not None
|
|
2351
|
+
and function_call.function._run_context.session_state is not None
|
|
2352
|
+
):
|
|
2353
|
+
updated_session_state = function_call.function._run_context.session_state
|
|
2354
|
+
elif function_call.function._session_state is not None:
|
|
2355
|
+
updated_session_state = function_call.function._session_state
|
|
2356
|
+
|
|
2357
|
+
if not (
|
|
2358
|
+
async_function_call_output is not None
|
|
2359
|
+
or isinstance(
|
|
2360
|
+
function_call.result,
|
|
2361
|
+
(GeneratorType, collections.abc.Iterator, AsyncGeneratorType, collections.abc.AsyncIterator),
|
|
2362
|
+
)
|
|
2363
|
+
):
|
|
2331
2364
|
from agno.tools.function import ToolResult
|
|
2332
2365
|
|
|
2333
2366
|
if isinstance(function_execution_result.result, ToolResult):
|
agno/models/google/gemini.py
CHANGED
|
@@ -36,6 +36,7 @@ try:
|
|
|
36
36
|
GenerateContentResponseUsageMetadata,
|
|
37
37
|
GoogleSearch,
|
|
38
38
|
GoogleSearchRetrieval,
|
|
39
|
+
GroundingMetadata,
|
|
39
40
|
Operation,
|
|
40
41
|
Part,
|
|
41
42
|
Retrieval,
|
|
@@ -244,8 +245,8 @@ class Gemini(Model):
|
|
|
244
245
|
builtin_tools = []
|
|
245
246
|
|
|
246
247
|
if self.grounding:
|
|
247
|
-
|
|
248
|
-
"Grounding enabled. This is a legacy tool. For Gemini 2.0+ Please use enable `search` flag instead."
|
|
248
|
+
log_debug(
|
|
249
|
+
"Gemini Grounding enabled. This is a legacy tool. For Gemini 2.0+ Please use enable `search` flag instead."
|
|
249
250
|
)
|
|
250
251
|
builtin_tools.append(
|
|
251
252
|
Tool(
|
|
@@ -258,15 +259,15 @@ class Gemini(Model):
|
|
|
258
259
|
)
|
|
259
260
|
|
|
260
261
|
if self.search:
|
|
261
|
-
|
|
262
|
+
log_debug("Gemini Google Search enabled.")
|
|
262
263
|
builtin_tools.append(Tool(google_search=GoogleSearch()))
|
|
263
264
|
|
|
264
265
|
if self.url_context:
|
|
265
|
-
|
|
266
|
+
log_debug("Gemini URL context enabled.")
|
|
266
267
|
builtin_tools.append(Tool(url_context=UrlContext()))
|
|
267
268
|
|
|
268
269
|
if self.vertexai_search:
|
|
269
|
-
|
|
270
|
+
log_debug("Gemini Vertex AI Search enabled.")
|
|
270
271
|
if not self.vertexai_search_datastore:
|
|
271
272
|
log_error("vertexai_search_datastore must be provided when vertexai_search is enabled.")
|
|
272
273
|
raise ValueError("vertexai_search_datastore must be provided when vertexai_search is enabled.")
|
|
@@ -1008,27 +1009,24 @@ class Gemini(Model):
|
|
|
1008
1009
|
citations = Citations()
|
|
1009
1010
|
citations_raw = {}
|
|
1010
1011
|
citations_urls = []
|
|
1012
|
+
web_search_queries: List[str] = []
|
|
1011
1013
|
|
|
1012
1014
|
if response.candidates and response.candidates[0].grounding_metadata is not None:
|
|
1013
|
-
grounding_metadata = response.candidates[0].grounding_metadata
|
|
1014
|
-
citations_raw["grounding_metadata"] = grounding_metadata
|
|
1015
|
+
grounding_metadata: GroundingMetadata = response.candidates[0].grounding_metadata
|
|
1016
|
+
citations_raw["grounding_metadata"] = grounding_metadata.model_dump()
|
|
1015
1017
|
|
|
1016
|
-
chunks = grounding_metadata.
|
|
1017
|
-
|
|
1018
|
+
chunks = grounding_metadata.grounding_chunks or []
|
|
1019
|
+
web_search_queries = grounding_metadata.web_search_queries or []
|
|
1018
1020
|
for chunk in chunks:
|
|
1019
|
-
if not
|
|
1021
|
+
if not chunk:
|
|
1020
1022
|
continue
|
|
1021
|
-
web = chunk.
|
|
1022
|
-
if not
|
|
1023
|
+
web = chunk.web
|
|
1024
|
+
if not web:
|
|
1023
1025
|
continue
|
|
1024
|
-
uri = web.
|
|
1025
|
-
title = web.
|
|
1026
|
+
uri = web.uri
|
|
1027
|
+
title = web.title
|
|
1026
1028
|
if uri:
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
# Create citation objects from filtered pairs
|
|
1030
|
-
grounding_urls = [UrlCitation(url=url, title=title) for url, title in citation_pairs]
|
|
1031
|
-
citations_urls.extend(grounding_urls)
|
|
1029
|
+
citations_urls.append(UrlCitation(url=uri, title=title))
|
|
1032
1030
|
|
|
1033
1031
|
# Handle URLs from URL context tool
|
|
1034
1032
|
if (
|
|
@@ -1036,22 +1034,29 @@ class Gemini(Model):
|
|
|
1036
1034
|
and hasattr(response.candidates[0], "url_context_metadata")
|
|
1037
1035
|
and response.candidates[0].url_context_metadata is not None
|
|
1038
1036
|
):
|
|
1039
|
-
url_context_metadata = response.candidates[0].url_context_metadata
|
|
1040
|
-
citations_raw["url_context_metadata"] = url_context_metadata
|
|
1037
|
+
url_context_metadata = response.candidates[0].url_context_metadata
|
|
1038
|
+
citations_raw["url_context_metadata"] = url_context_metadata.model_dump()
|
|
1041
1039
|
|
|
1042
|
-
url_metadata_list = url_context_metadata.
|
|
1040
|
+
url_metadata_list = url_context_metadata.url_metadata or []
|
|
1043
1041
|
for url_meta in url_metadata_list:
|
|
1044
|
-
retrieved_url = url_meta.
|
|
1045
|
-
status =
|
|
1042
|
+
retrieved_url = url_meta.retrieved_url
|
|
1043
|
+
status = "UNKNOWN"
|
|
1044
|
+
if url_meta.url_retrieval_status:
|
|
1045
|
+
status = url_meta.url_retrieval_status.value
|
|
1046
1046
|
if retrieved_url and status == "URL_RETRIEVAL_STATUS_SUCCESS":
|
|
1047
1047
|
# Avoid duplicate URLs
|
|
1048
1048
|
existing_urls = [citation.url for citation in citations_urls]
|
|
1049
1049
|
if retrieved_url not in existing_urls:
|
|
1050
1050
|
citations_urls.append(UrlCitation(url=retrieved_url, title=retrieved_url))
|
|
1051
1051
|
|
|
1052
|
+
if citations_raw:
|
|
1053
|
+
citations.raw = citations_raw
|
|
1054
|
+
if citations_urls:
|
|
1055
|
+
citations.urls = citations_urls
|
|
1056
|
+
if web_search_queries:
|
|
1057
|
+
citations.search_queries = web_search_queries
|
|
1058
|
+
|
|
1052
1059
|
if citations_raw or citations_urls:
|
|
1053
|
-
citations.raw = citations_raw if citations_raw else None
|
|
1054
|
-
citations.urls = citations_urls if citations_urls else None
|
|
1055
1060
|
model_response.citations = citations
|
|
1056
1061
|
|
|
1057
1062
|
# Extract usage metadata if present
|
|
@@ -1150,28 +1155,52 @@ class Gemini(Model):
|
|
|
1150
1155
|
|
|
1151
1156
|
model_response.tool_calls.append(tool_call)
|
|
1152
1157
|
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
citations.raw = grounding_metadata
|
|
1158
|
+
citations = Citations()
|
|
1159
|
+
citations.raw = {}
|
|
1160
|
+
citations.urls = []
|
|
1157
1161
|
|
|
1162
|
+
if (
|
|
1163
|
+
hasattr(response_delta.candidates[0], "grounding_metadata")
|
|
1164
|
+
and response_delta.candidates[0].grounding_metadata is not None
|
|
1165
|
+
):
|
|
1166
|
+
grounding_metadata = response_delta.candidates[0].grounding_metadata
|
|
1167
|
+
citations.raw["grounding_metadata"] = grounding_metadata.model_dump()
|
|
1168
|
+
citations.search_queries = grounding_metadata.web_search_queries or []
|
|
1158
1169
|
# Extract url and title
|
|
1159
|
-
chunks = grounding_metadata.
|
|
1160
|
-
citation_pairs = []
|
|
1170
|
+
chunks = grounding_metadata.grounding_chunks or []
|
|
1161
1171
|
for chunk in chunks:
|
|
1162
|
-
if not
|
|
1172
|
+
if not chunk:
|
|
1163
1173
|
continue
|
|
1164
|
-
web = chunk.
|
|
1165
|
-
if not
|
|
1174
|
+
web = chunk.web
|
|
1175
|
+
if not web:
|
|
1166
1176
|
continue
|
|
1167
|
-
uri = web.
|
|
1168
|
-
title = web.
|
|
1177
|
+
uri = web.uri
|
|
1178
|
+
title = web.title
|
|
1169
1179
|
if uri:
|
|
1170
|
-
|
|
1180
|
+
citations.urls.append(UrlCitation(url=uri, title=title))
|
|
1181
|
+
|
|
1182
|
+
# Handle URLs from URL context tool
|
|
1183
|
+
if (
|
|
1184
|
+
hasattr(response_delta.candidates[0], "url_context_metadata")
|
|
1185
|
+
and response_delta.candidates[0].url_context_metadata is not None
|
|
1186
|
+
):
|
|
1187
|
+
url_context_metadata = response_delta.candidates[0].url_context_metadata
|
|
1171
1188
|
|
|
1172
|
-
|
|
1173
|
-
|
|
1189
|
+
citations.raw["url_context_metadata"] = url_context_metadata.model_dump()
|
|
1190
|
+
|
|
1191
|
+
url_metadata_list = url_context_metadata.url_metadata or []
|
|
1192
|
+
for url_meta in url_metadata_list:
|
|
1193
|
+
retrieved_url = url_meta.retrieved_url
|
|
1194
|
+
status = "UNKNOWN"
|
|
1195
|
+
if url_meta.url_retrieval_status:
|
|
1196
|
+
status = url_meta.url_retrieval_status.value
|
|
1197
|
+
if retrieved_url and status == "URL_RETRIEVAL_STATUS_SUCCESS":
|
|
1198
|
+
# Avoid duplicate URLs
|
|
1199
|
+
existing_urls = [citation.url for citation in citations.urls]
|
|
1200
|
+
if retrieved_url not in existing_urls:
|
|
1201
|
+
citations.urls.append(UrlCitation(url=retrieved_url, title=retrieved_url))
|
|
1174
1202
|
|
|
1203
|
+
if citations.raw or citations.urls:
|
|
1175
1204
|
model_response.citations = citations
|
|
1176
1205
|
|
|
1177
1206
|
# Extract usage metadata if present
|
agno/models/message.py
CHANGED
|
@@ -42,6 +42,9 @@ class Citations(BaseModel):
|
|
|
42
42
|
# Raw citations from the model
|
|
43
43
|
raw: Optional[Any] = None
|
|
44
44
|
|
|
45
|
+
# Search queries used to retrieve the citations
|
|
46
|
+
search_queries: Optional[List[str]] = None
|
|
47
|
+
|
|
45
48
|
# URLs of the citations.
|
|
46
49
|
urls: Optional[List[UrlCitation]] = None
|
|
47
50
|
|
agno/models/openai/chat.py
CHANGED
|
@@ -820,6 +820,16 @@ class OpenAIChat(Model):
|
|
|
820
820
|
if response.usage is not None:
|
|
821
821
|
model_response.response_usage = self._get_metrics(response.usage)
|
|
822
822
|
|
|
823
|
+
if model_response.provider_data is None:
|
|
824
|
+
model_response.provider_data = {}
|
|
825
|
+
|
|
826
|
+
if response.id:
|
|
827
|
+
model_response.provider_data["id"] = response.id
|
|
828
|
+
if response.system_fingerprint:
|
|
829
|
+
model_response.provider_data["system_fingerprint"] = response.system_fingerprint
|
|
830
|
+
if response.model_extra:
|
|
831
|
+
model_response.provider_data["model_extra"] = response.model_extra
|
|
832
|
+
|
|
823
833
|
return model_response
|
|
824
834
|
|
|
825
835
|
def _parse_provider_response_delta(self, response_delta: ChatCompletionChunk) -> ModelResponse:
|
|
@@ -842,6 +852,17 @@ class OpenAIChat(Model):
|
|
|
842
852
|
if choice_delta.content is not None:
|
|
843
853
|
model_response.content = choice_delta.content
|
|
844
854
|
|
|
855
|
+
# We only want to handle these if content is present
|
|
856
|
+
if model_response.provider_data is None:
|
|
857
|
+
model_response.provider_data = {}
|
|
858
|
+
|
|
859
|
+
if response_delta.id:
|
|
860
|
+
model_response.provider_data["id"] = response_delta.id
|
|
861
|
+
if response_delta.system_fingerprint:
|
|
862
|
+
model_response.provider_data["system_fingerprint"] = response_delta.system_fingerprint
|
|
863
|
+
if response_delta.model_extra:
|
|
864
|
+
model_response.provider_data["model_extra"] = response_delta.model_extra
|
|
865
|
+
|
|
845
866
|
# Add tool calls
|
|
846
867
|
if choice_delta.tool_calls is not None:
|
|
847
868
|
model_response.tool_calls = choice_delta.tool_calls # type: ignore
|
agno/os/routers/evals/utils.py
CHANGED
|
@@ -66,14 +66,14 @@ async def run_agent_as_judge_eval(
|
|
|
66
66
|
|
|
67
67
|
# Run agent/team to get output
|
|
68
68
|
if agent:
|
|
69
|
-
agent_response = await agent.arun(eval_run_input.input)
|
|
69
|
+
agent_response = await agent.arun(eval_run_input.input, stream=False)
|
|
70
70
|
output = str(agent_response.content) if agent_response.content else ""
|
|
71
71
|
model_id = agent.model.id if agent and agent.model else None
|
|
72
72
|
model_provider = agent.model.provider if agent and agent.model else None
|
|
73
73
|
agent_id = agent.id
|
|
74
74
|
team_id = None
|
|
75
75
|
elif team:
|
|
76
|
-
team_response = await team.arun(eval_run_input.input)
|
|
76
|
+
team_response = await team.arun(eval_run_input.input, stream=False)
|
|
77
77
|
output = str(team_response.content) if team_response.content else ""
|
|
78
78
|
model_id = team.model.id if team and team.model else None
|
|
79
79
|
model_provider = team.model.provider if team and team.model else None
|
|
@@ -125,39 +125,21 @@ async def run_performance_eval(
|
|
|
125
125
|
default_model: Optional[Model] = None,
|
|
126
126
|
) -> EvalSchema:
|
|
127
127
|
"""Run a performance evaluation for the given agent or team"""
|
|
128
|
-
|
|
129
|
-
if isinstance(db, AsyncBaseDb):
|
|
130
|
-
if agent:
|
|
131
|
-
|
|
132
|
-
async def run_component(): # type: ignore
|
|
133
|
-
return await agent.arun(eval_run_input.input)
|
|
134
|
-
|
|
135
|
-
model_id = agent.model.id if agent and agent.model else None
|
|
136
|
-
model_provider = agent.model.provider if agent and agent.model else None
|
|
137
|
-
|
|
138
|
-
elif team:
|
|
139
|
-
|
|
140
|
-
async def run_component(): # type: ignore
|
|
141
|
-
return await team.arun(eval_run_input.input)
|
|
142
|
-
|
|
143
|
-
model_id = team.model.id if team and team.model else None
|
|
144
|
-
model_provider = team.model.provider if team and team.model else None
|
|
145
|
-
else:
|
|
146
|
-
if agent:
|
|
128
|
+
if agent:
|
|
147
129
|
|
|
148
|
-
|
|
149
|
-
|
|
130
|
+
async def run_component(): # type: ignore
|
|
131
|
+
return await agent.arun(eval_run_input.input, stream=False)
|
|
150
132
|
|
|
151
|
-
|
|
152
|
-
|
|
133
|
+
model_id = agent.model.id if agent and agent.model else None
|
|
134
|
+
model_provider = agent.model.provider if agent and agent.model else None
|
|
153
135
|
|
|
154
|
-
|
|
136
|
+
elif team:
|
|
155
137
|
|
|
156
|
-
|
|
157
|
-
|
|
138
|
+
async def run_component(): # type: ignore
|
|
139
|
+
return await team.arun(eval_run_input.input, stream=False)
|
|
158
140
|
|
|
159
|
-
|
|
160
|
-
|
|
141
|
+
model_id = team.model.id if team and team.model else None
|
|
142
|
+
model_provider = team.model.provider if team and team.model else None
|
|
161
143
|
|
|
162
144
|
performance_eval = PerformanceEval(
|
|
163
145
|
db=db,
|
|
@@ -171,11 +153,7 @@ async def run_performance_eval(
|
|
|
171
153
|
model_provider=model_provider,
|
|
172
154
|
)
|
|
173
155
|
|
|
174
|
-
|
|
175
|
-
if isinstance(db, AsyncBaseDb):
|
|
176
|
-
result = await performance_eval.arun(print_results=False, print_summary=False)
|
|
177
|
-
else:
|
|
178
|
-
result = performance_eval.run(print_results=False, print_summary=False)
|
|
156
|
+
result = await performance_eval.arun(print_results=False, print_summary=False)
|
|
179
157
|
if not result:
|
|
180
158
|
raise HTTPException(status_code=500, detail="Failed to run performance evaluation")
|
|
181
159
|
|
|
@@ -210,7 +188,7 @@ async def run_reliability_eval(
|
|
|
210
188
|
raise HTTPException(status_code=400, detail="expected_tool_calls is required for reliability evaluations")
|
|
211
189
|
|
|
212
190
|
if agent:
|
|
213
|
-
agent_response = await agent.arun(eval_run_input.input)
|
|
191
|
+
agent_response = await agent.arun(eval_run_input.input, stream=False)
|
|
214
192
|
reliability_eval = ReliabilityEval(
|
|
215
193
|
db=db,
|
|
216
194
|
name=eval_run_input.name,
|
|
@@ -221,7 +199,7 @@ async def run_reliability_eval(
|
|
|
221
199
|
model_provider = agent.model.provider if agent and agent.model else None
|
|
222
200
|
|
|
223
201
|
elif team:
|
|
224
|
-
team_response = await team.arun(eval_run_input.input)
|
|
202
|
+
team_response = await team.arun(eval_run_input.input, stream=False)
|
|
225
203
|
reliability_eval = ReliabilityEval(
|
|
226
204
|
db=db,
|
|
227
205
|
name=eval_run_input.name,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
3
|
import math
|
|
4
|
-
from typing import Dict, List, Optional
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
5
|
|
|
6
6
|
from fastapi import APIRouter, BackgroundTasks, Depends, File, Form, HTTPException, Path, Query, UploadFile
|
|
7
7
|
|
|
@@ -874,8 +874,8 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
|
|
|
874
874
|
) -> ConfigResponseSchema:
|
|
875
875
|
knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
|
|
876
876
|
|
|
877
|
-
# Get factory readers info
|
|
878
|
-
readers_info = get_all_readers_info()
|
|
877
|
+
# Get factory readers info (including custom readers from this knowledge instance)
|
|
878
|
+
readers_info = get_all_readers_info(knowledge)
|
|
879
879
|
reader_schemas = {}
|
|
880
880
|
# Add factory readers
|
|
881
881
|
for reader_info in readers_info:
|
|
@@ -887,7 +887,13 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
|
|
|
887
887
|
)
|
|
888
888
|
|
|
889
889
|
# Add custom readers from knowledge.readers
|
|
890
|
-
|
|
890
|
+
readers_result: Any = knowledge.get_readers() or {}
|
|
891
|
+
print(f"readers_result: {readers_result}")
|
|
892
|
+
# Ensure readers_dict is a dictionary (defensive check)
|
|
893
|
+
if not isinstance(readers_result, dict):
|
|
894
|
+
readers_dict: Dict[str, Reader] = {}
|
|
895
|
+
else:
|
|
896
|
+
readers_dict = readers_result
|
|
891
897
|
if readers_dict:
|
|
892
898
|
for reader_id, reader in readers_dict.items():
|
|
893
899
|
# Get chunking strategies from the reader
|
|
@@ -907,8 +913,8 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
|
|
|
907
913
|
chunkers=chunking_strategies,
|
|
908
914
|
)
|
|
909
915
|
|
|
910
|
-
# Get content types to readers mapping
|
|
911
|
-
types_of_readers = get_content_types_to_readers_mapping()
|
|
916
|
+
# Get content types to readers mapping (including custom readers from this knowledge instance)
|
|
917
|
+
types_of_readers = get_content_types_to_readers_mapping(knowledge)
|
|
912
918
|
chunkers_list = get_all_chunkers_info()
|
|
913
919
|
|
|
914
920
|
# Convert chunkers list to dictionary format expected by schema
|
|
@@ -961,20 +967,26 @@ async def process_content(
|
|
|
961
967
|
try:
|
|
962
968
|
if reader_id:
|
|
963
969
|
reader = None
|
|
964
|
-
|
|
965
|
-
|
|
970
|
+
# Use get_readers() to ensure we get a dict (handles list conversion)
|
|
971
|
+
custom_readers = knowledge.get_readers()
|
|
972
|
+
if custom_readers and reader_id in custom_readers:
|
|
973
|
+
reader = custom_readers[reader_id]
|
|
974
|
+
log_debug(f"Found custom reader: {reader.__class__.__name__}")
|
|
966
975
|
else:
|
|
976
|
+
# Try to resolve from factory readers
|
|
967
977
|
key = reader_id.lower().strip().replace("-", "_").replace(" ", "_")
|
|
968
978
|
candidates = [key] + ([key[:-6]] if key.endswith("reader") else [])
|
|
969
979
|
for cand in candidates:
|
|
970
980
|
try:
|
|
971
981
|
reader = ReaderFactory.create_reader(cand)
|
|
972
|
-
log_debug(f"Resolved reader: {reader.__class__.__name__}")
|
|
982
|
+
log_debug(f"Resolved reader from factory: {reader.__class__.__name__}")
|
|
973
983
|
break
|
|
974
984
|
except Exception:
|
|
975
985
|
continue
|
|
976
986
|
if reader:
|
|
977
987
|
content.reader = reader
|
|
988
|
+
else:
|
|
989
|
+
log_debug(f"Could not resolve reader with id: {reader_id}")
|
|
978
990
|
if chunker and content.reader:
|
|
979
991
|
# Set the chunker name on the reader - let the reader handle it internally
|
|
980
992
|
content.reader.set_chunking_strategy_from_string(chunker, chunk_size=chunk_size, overlap=chunk_overlap)
|
agno/team/team.py
CHANGED
|
@@ -1541,6 +1541,8 @@ class Team:
|
|
|
1541
1541
|
add_history_to_context=add_history_to_context,
|
|
1542
1542
|
add_session_state_to_context=add_session_state_to_context,
|
|
1543
1543
|
add_dependencies_to_context=add_dependencies_to_context,
|
|
1544
|
+
stream=False,
|
|
1545
|
+
stream_events=False,
|
|
1544
1546
|
)
|
|
1545
1547
|
|
|
1546
1548
|
# 3. Prepare run messages
|
|
@@ -1753,6 +1755,8 @@ class Team:
|
|
|
1753
1755
|
add_history_to_context=add_history_to_context,
|
|
1754
1756
|
add_session_state_to_context=add_session_state_to_context,
|
|
1755
1757
|
add_dependencies_to_context=add_dependencies_to_context,
|
|
1758
|
+
stream=True,
|
|
1759
|
+
stream_events=stream_events,
|
|
1756
1760
|
)
|
|
1757
1761
|
|
|
1758
1762
|
# 3. Prepare run messages
|
|
@@ -2182,9 +2186,6 @@ class Team:
|
|
|
2182
2186
|
if stream_events is None:
|
|
2183
2187
|
stream_events = False if self.stream_events is None else self.stream_events
|
|
2184
2188
|
|
|
2185
|
-
self.stream = self.stream or stream
|
|
2186
|
-
self.stream_events = self.stream_events or stream_events
|
|
2187
|
-
|
|
2188
2189
|
self.model = cast(Model, self.model)
|
|
2189
2190
|
|
|
2190
2191
|
if self.metadata is not None:
|
|
@@ -2394,6 +2395,8 @@ class Team:
|
|
|
2394
2395
|
add_history_to_context=add_history_to_context,
|
|
2395
2396
|
add_dependencies_to_context=add_dependencies_to_context,
|
|
2396
2397
|
add_session_state_to_context=add_session_state_to_context,
|
|
2398
|
+
stream=False,
|
|
2399
|
+
stream_events=False,
|
|
2397
2400
|
)
|
|
2398
2401
|
|
|
2399
2402
|
# 5. Prepare run messages
|
|
@@ -2638,6 +2641,10 @@ class Team:
|
|
|
2638
2641
|
files=run_input.files,
|
|
2639
2642
|
debug_mode=debug_mode,
|
|
2640
2643
|
add_history_to_context=add_history_to_context,
|
|
2644
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
2645
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
2646
|
+
stream=True,
|
|
2647
|
+
stream_events=stream_events,
|
|
2641
2648
|
)
|
|
2642
2649
|
|
|
2643
2650
|
# 6. Prepare run messages
|
|
@@ -3046,9 +3053,6 @@ class Team:
|
|
|
3046
3053
|
if stream_events is None:
|
|
3047
3054
|
stream_events = False if self.stream_events is None else self.stream_events
|
|
3048
3055
|
|
|
3049
|
-
self.stream = self.stream or stream
|
|
3050
|
-
self.stream_events = self.stream_events or stream_events
|
|
3051
|
-
|
|
3052
3056
|
self.model = cast(Model, self.model)
|
|
3053
3057
|
|
|
3054
3058
|
if self.metadata is not None:
|
|
@@ -5397,6 +5401,8 @@ class Team:
|
|
|
5397
5401
|
add_history_to_context: Optional[bool] = None,
|
|
5398
5402
|
add_dependencies_to_context: Optional[bool] = None,
|
|
5399
5403
|
add_session_state_to_context: Optional[bool] = None,
|
|
5404
|
+
stream: Optional[bool] = None,
|
|
5405
|
+
stream_events: Optional[bool] = None,
|
|
5400
5406
|
check_mcp_tools: bool = True,
|
|
5401
5407
|
) -> List[Union[Function, dict]]:
|
|
5402
5408
|
# Connect tools that require connection management
|
|
@@ -5491,8 +5497,8 @@ class Team:
|
|
|
5491
5497
|
team_run_context=team_run_context,
|
|
5492
5498
|
input=user_message_content,
|
|
5493
5499
|
user_id=user_id,
|
|
5494
|
-
stream=
|
|
5495
|
-
stream_events=
|
|
5500
|
+
stream=stream or False,
|
|
5501
|
+
stream_events=stream_events or False,
|
|
5496
5502
|
async_mode=async_mode,
|
|
5497
5503
|
images=images, # type: ignore
|
|
5498
5504
|
videos=videos, # type: ignore
|