agno 1.7.4__py3-none-any.whl → 1.7.5__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/tools/function.py CHANGED
@@ -151,7 +151,7 @@ class Function(BaseModel):
151
151
  param_type_hints = {
152
152
  name: type_hints.get(name)
153
153
  for name in sig.parameters
154
- if name != "return" and name not in ["agent", "team"]
154
+ if name != "return" and name not in ["agent", "team", "self"]
155
155
  }
156
156
 
157
157
  # Parse docstring for parameters
@@ -177,7 +177,9 @@ class Function(BaseModel):
177
177
  # If strict=True mark all fields as required
178
178
  # See: https://platform.openai.com/docs/guides/structured-outputs/supported-schemas#all-fields-must-be-required
179
179
  if strict:
180
- parameters["required"] = [name for name in parameters["properties"] if name not in ["agent", "team"]]
180
+ parameters["required"] = [
181
+ name for name in parameters["properties"] if name not in ["agent", "team", "self"]
182
+ ]
181
183
  else:
182
184
  # Mark a field as required if it has no default value (this would include optional fields)
183
185
  parameters["required"] = [
@@ -235,7 +237,7 @@ class Function(BaseModel):
235
237
  # log_info(f"Type hints for {self.name}: {type_hints}")
236
238
 
237
239
  # Filter out return type and only process parameters
238
- excluded_params = ["return", "agent", "team"]
240
+ excluded_params = ["return", "agent", "team", "self"]
239
241
  if self.requires_user_input and self.user_input_fields:
240
242
  if len(self.user_input_fields) == 0:
241
243
  excluded_params.extend(list(type_hints.keys()))
@@ -337,7 +339,9 @@ class Function(BaseModel):
337
339
 
338
340
  def process_schema_for_strict(self):
339
341
  self.parameters["additionalProperties"] = False
340
- self.parameters["required"] = [name for name in self.parameters["properties"] if name not in ["agent", "team"]]
342
+ self.parameters["required"] = [
343
+ name for name in self.parameters["properties"] if name not in ["agent", "team", "self"]
344
+ ]
341
345
 
342
346
  def _get_cache_key(self, entrypoint_args: Dict[str, Any], call_args: Optional[Dict[str, Any]] = None) -> str:
343
347
  """Generate a cache key based on function name and arguments."""
@@ -554,7 +558,7 @@ class FunctionCall(BaseModel):
554
558
  from functools import reduce
555
559
  from inspect import iscoroutinefunction
556
560
 
557
- def execute_entrypoint():
561
+ def execute_entrypoint(name, func, args):
558
562
  """Execute the entrypoint function."""
559
563
  arguments = entrypoint_args.copy()
560
564
  if self.arguments is not None:
@@ -572,9 +576,9 @@ class FunctionCall(BaseModel):
572
576
  # Pass the inner function as next_func to the hook
573
577
  # The hook will call next_func to continue the chain
574
578
  def next_func(**kwargs):
575
- return inner_func()
579
+ return inner_func(name, func, kwargs)
576
580
 
577
- hook_args = self._build_hook_args(hook, name, func, args)
581
+ hook_args = self._build_hook_args(hook, name, next_func, args)
578
582
 
579
583
  return hook(**hook_args)
580
584
 
@@ -716,7 +720,7 @@ class FunctionCall(BaseModel):
716
720
  from functools import reduce
717
721
  from inspect import isasyncgen, isasyncgenfunction, iscoroutinefunction
718
722
 
719
- async def execute_entrypoint_async():
723
+ async def execute_entrypoint_async(name, func, args):
720
724
  """Execute the entrypoint function asynchronously."""
721
725
  arguments = entrypoint_args.copy()
722
726
  if self.arguments is not None:
@@ -729,7 +733,7 @@ class FunctionCall(BaseModel):
729
733
  result = await result
730
734
  return result
731
735
 
732
- def execute_entrypoint():
736
+ def execute_entrypoint(name, func, args):
733
737
  """Execute the entrypoint function synchronously."""
734
738
  arguments = entrypoint_args.copy()
735
739
  if self.arguments is not None:
@@ -750,11 +754,11 @@ class FunctionCall(BaseModel):
750
754
  # The hook will call next_func to continue the chain
751
755
  async def next_func(**kwargs):
752
756
  if iscoroutinefunction(inner_func):
753
- return await inner_func()
757
+ return await inner_func(name, func, kwargs)
754
758
  else:
755
- return inner_func()
759
+ return inner_func(name, func, kwargs)
756
760
 
757
- hook_args = self._build_hook_args(hook, name, func, args)
761
+ hook_args = self._build_hook_args(hook, name, next_func, args)
758
762
 
759
763
  if iscoroutinefunction(hook):
760
764
  return await hook(**hook_args)
agno/utils/pprint.py CHANGED
@@ -163,6 +163,8 @@ async def apprint_run_response(
163
163
  except Exception as e:
164
164
  logger.warning(f"Failed to convert response to Markdown: {e}")
165
165
  else:
166
+ if isinstance(streaming_response_content, JSON):
167
+ streaming_response_content = streaming_response_content.text + "\n" # type: ignore
166
168
  streaming_response_content += resp.content # type: ignore
167
169
 
168
170
  formatted_response = Markdown(streaming_response_content) if markdown else streaming_response_content # type: ignore
@@ -0,0 +1,3 @@
1
+ from agno.vectordb.surrealdb.surrealdb import SurrealDb
2
+
3
+ __all__ = ["SurrealDb"]
@@ -0,0 +1,493 @@
1
+ from typing import Any, Dict, Final, List, Optional, Union
2
+
3
+ try:
4
+ from surrealdb import (
5
+ AsyncHttpSurrealConnection,
6
+ AsyncWsSurrealConnection,
7
+ BlockingHttpSurrealConnection,
8
+ BlockingWsSurrealConnection,
9
+ )
10
+ except ImportError as e:
11
+ msg = "The `surrealdb` package is not installed. Please install it via `pip install surrealdb`."
12
+ raise ImportError(msg) from e
13
+
14
+ from agno.document import Document
15
+ from agno.embedder import Embedder
16
+ from agno.utils.log import log_debug, log_error, log_info
17
+ from agno.vectordb.base import VectorDb
18
+ from agno.vectordb.distance import Distance
19
+
20
+
21
+ class SurrealDb(VectorDb):
22
+ """SurrealDB Vector Database implementation supporting both sync and async operations."""
23
+
24
+ # SQL Query Constants
25
+ CREATE_TABLE_QUERY: Final[str] = """
26
+ DEFINE TABLE IF NOT EXISTS {collection} SCHEMAFUL;
27
+ DEFINE FIELD IF NOT EXISTS content ON {collection} TYPE string;
28
+ DEFINE FIELD IF NOT EXISTS embedding ON {collection} TYPE array<float>;
29
+ DEFINE FIELD IF NOT EXISTS meta_data ON {collection} FLEXIBLE TYPE object;
30
+ DEFINE INDEX IF NOT EXISTS vector_idx ON {collection} FIELDS embedding HNSW DIMENSION {dimensions} DIST {distance};
31
+ """
32
+
33
+ DOC_EXISTS_QUERY: Final[str] = """
34
+ SELECT * FROM {collection}
35
+ WHERE content = $content
36
+ LIMIT 1
37
+ """
38
+
39
+ NAME_EXISTS_QUERY: Final[str] = """
40
+ SELECT * FROM {collection}
41
+ WHERE meta_data.name = $name
42
+ LIMIT 1
43
+ """
44
+
45
+ ID_EXISTS_QUERY: Final[str] = """
46
+ SELECT * FROM {collection}
47
+ WHERE id = $id
48
+ LIMIT 1
49
+ """
50
+
51
+ UPSERT_QUERY: Final[str] = """
52
+ UPSERT {thing}
53
+ SET content = $content,
54
+ embedding = $embedding,
55
+ meta_data = $meta_data
56
+ """
57
+
58
+ SEARCH_QUERY: Final[str] = """
59
+ SELECT
60
+ content,
61
+ meta_data,
62
+ vector::distance::knn() as distance
63
+ FROM {collection}
64
+ WHERE embedding <|{limit}, {search_ef}|> $query_embedding
65
+ {filter_condition}
66
+ ORDER BY distance ASC
67
+ LIMIT {limit};
68
+ """
69
+
70
+ INFO_DB_QUERY: Final[str] = "INFO FOR DB;"
71
+ DROP_TABLE_QUERY: Final[str] = "REMOVE TABLE {collection}"
72
+ DELETE_ALL_QUERY: Final[str] = "DELETE {collection}"
73
+
74
+ def __init__(
75
+ self,
76
+ client: Optional[Union[BlockingWsSurrealConnection, BlockingHttpSurrealConnection]] = None,
77
+ async_client: Optional[Union[AsyncWsSurrealConnection, AsyncHttpSurrealConnection]] = None,
78
+ collection: str = "documents",
79
+ distance: Distance = Distance.cosine,
80
+ efc: int = 150,
81
+ m: int = 12,
82
+ search_ef: int = 40,
83
+ embedder: Optional[Embedder] = None,
84
+ ):
85
+ """Initialize SurrealDB connection.
86
+
87
+ Args:
88
+ url: SurrealDB server URL (e.g. ws://localhost:8000/rpc)
89
+ client: A blocking connection, either HTTP or WS
90
+ async_client: An async connection, either HTTP or WS (default: None)
91
+ collection: Collection name to store documents (default: documents)
92
+ distance: Distance metric to use (default: cosine)
93
+ efc: HNSW construction time/accuracy trade-off (default: 150)
94
+ m: HNSW max number of connections per element (default: 12)
95
+ search_ef: HNSW search time/accuracy trade-off (default: 40)
96
+ embedder: Embedder instance for creating embeddings (default: OpenAIEmbedder)
97
+
98
+ """
99
+ # Embedder for embedding the document contents
100
+ if embedder is None:
101
+ from agno.embedder.openai import OpenAIEmbedder
102
+
103
+ embedder = OpenAIEmbedder()
104
+ log_info("Embedder not provided, using OpenAIEmbedder as default.")
105
+ self.embedder: Embedder = embedder
106
+ self.dimensions = self.embedder.dimensions
107
+ self.collection = collection
108
+
109
+ # Convert Distance enum to SurrealDB distance type
110
+ self.distance = {Distance.cosine: "COSINE", Distance.l2: "EUCLIDEAN", Distance.max_inner_product: "DOT"}[
111
+ distance
112
+ ]
113
+
114
+ self._client: Optional[Union[BlockingHttpSurrealConnection, BlockingWsSurrealConnection]] = client
115
+ self._async_client: Optional[Union[AsyncWsSurrealConnection, AsyncHttpSurrealConnection]] = async_client
116
+
117
+ if self._client is None and self._async_client is None:
118
+ msg = "Client and async client are not provided. Please provide one of them."
119
+ raise RuntimeError(msg)
120
+
121
+ # HNSW index parameters
122
+ self.efc = efc
123
+ self.m = m
124
+ self.search_ef = search_ef
125
+
126
+ @property
127
+ def async_client(self) -> Union[AsyncWsSurrealConnection, AsyncHttpSurrealConnection]:
128
+ """Check if the async client is initialized.
129
+
130
+ Raises:
131
+ RuntimeError: If the async client is not initialized.
132
+
133
+ Returns:
134
+ The async client.
135
+
136
+ """
137
+ if self._async_client is None:
138
+ msg = "Async client is not initialized"
139
+ raise RuntimeError(msg)
140
+ return self._async_client
141
+
142
+ @property
143
+ def client(self) -> Union[BlockingHttpSurrealConnection, BlockingWsSurrealConnection]:
144
+ """Check if the client is initialized.
145
+
146
+ Returns:
147
+ The client.
148
+
149
+ """
150
+ if self._client is None:
151
+ msg = "Client is not initialized"
152
+ raise RuntimeError(msg)
153
+ return self._client
154
+
155
+ @staticmethod
156
+ def _build_filter_condition(filters: Optional[Dict[str, Any]] = None) -> str:
157
+ """Build filter condition for queries.
158
+
159
+ Args:
160
+ filters: A dictionary of filters to apply to the query.
161
+
162
+ Returns:
163
+ A string representing the filter condition.
164
+
165
+ """
166
+ if not filters:
167
+ return ""
168
+ conditions = [f"meta_data.{key} = ${key}" for key in filters]
169
+ return "AND " + " AND ".join(conditions)
170
+
171
+ # Synchronous methods
172
+ def create(self) -> None:
173
+ """Create the vector collection and index."""
174
+ if not self.exists():
175
+ log_debug(f"Creating collection: {self.collection}")
176
+ query = self.CREATE_TABLE_QUERY.format(
177
+ collection=self.collection,
178
+ distance=self.distance,
179
+ dimensions=self.dimensions,
180
+ efc=self.efc,
181
+ m=self.m,
182
+ )
183
+ self.client.query(query)
184
+
185
+ def doc_exists(self, document: Document) -> bool:
186
+ """Check if a document exists by its content.
187
+
188
+ Args:
189
+ document: The document to check.
190
+
191
+ Returns:
192
+ True if the document exists, False otherwise.
193
+
194
+ """
195
+ log_debug(f"Checking if document exists: {document.content}")
196
+ result = self.client.query(
197
+ self.DOC_EXISTS_QUERY.format(collection=self.collection),
198
+ {"content": document.content},
199
+ )
200
+ return bool(self._extract_result(result))
201
+
202
+ def name_exists(self, name: str) -> bool:
203
+ """Check if a document exists by its name.
204
+
205
+ Args:
206
+ name: The name of the document to check.
207
+
208
+ Returns:
209
+ True if the document exists, False otherwise.
210
+
211
+ """
212
+ log_debug(f"Checking if document exists: {name}")
213
+ result = self.client.query(self.NAME_EXISTS_QUERY.format(collection=self.collection), {"name": name})
214
+ return bool(self._extract_result(result))
215
+
216
+ def insert(self, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
217
+ """Insert documents into the vector store.
218
+
219
+ Args:
220
+ documents: A list of documents to insert.
221
+ filters: A dictionary of filters to apply to the query.
222
+
223
+ """
224
+ for doc in documents:
225
+ doc.embed(embedder=self.embedder)
226
+ meta_data: Dict[str, Any] = doc.meta_data if isinstance(doc.meta_data, dict) else {}
227
+ data: Dict[str, Any] = {"content": doc.content, "embedding": doc.embedding, "meta_data": meta_data}
228
+ if filters:
229
+ data["meta_data"].update(filters)
230
+ self.client.create(self.collection, data)
231
+
232
+ def upsert(self, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
233
+ """Upsert documents into the vector store.
234
+
235
+ Args:
236
+ documents: A list of documents to upsert.
237
+ filters: A dictionary of filters to apply to the query.
238
+
239
+ """
240
+ for doc in documents:
241
+ doc.embed(embedder=self.embedder)
242
+ meta_data: Dict[str, Any] = doc.meta_data if isinstance(doc.meta_data, dict) else {}
243
+ data: Dict[str, Any] = {"content": doc.content, "embedding": doc.embedding, "meta_data": meta_data}
244
+ if filters:
245
+ data["meta_data"].update(filters)
246
+ thing = f"{self.collection}:{doc.id}" if doc.id else self.collection
247
+ self.client.query(self.UPSERT_QUERY.format(thing=thing), data)
248
+
249
+ def search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
250
+ """Search for similar documents.
251
+
252
+ Args:
253
+ query: The query to search for.
254
+ limit: The maximum number of documents to return.
255
+ filters: A dictionary of filters to apply to the query.
256
+
257
+ Returns:
258
+ A list of documents that are similar to the query.
259
+
260
+ """
261
+ query_embedding = self.embedder.get_embedding(query)
262
+ if query_embedding is None:
263
+ log_error(f"Error getting embedding for Query: {query}")
264
+ return []
265
+
266
+ filter_condition = self._build_filter_condition(filters)
267
+ log_debug(f"Filter condition: {filter_condition}")
268
+ search_query = self.SEARCH_QUERY.format(
269
+ collection=self.collection,
270
+ limit=limit,
271
+ search_ef=self.search_ef,
272
+ filter_condition=filter_condition,
273
+ distance=self.distance,
274
+ )
275
+ log_debug(f"Search query: {search_query}")
276
+ response = self.client.query(
277
+ search_query,
278
+ {"query_embedding": query_embedding, **filters} if filters else {"query_embedding": query_embedding},
279
+ )
280
+ log_debug(f"Search response: {response}")
281
+
282
+ documents = []
283
+ for item in response:
284
+ if isinstance(item, dict):
285
+ doc = Document(
286
+ content=item.get("content", ""),
287
+ embedding=item.get("embedding", []),
288
+ meta_data=item.get("meta_data", {}),
289
+ embedder=self.embedder,
290
+ )
291
+ documents.append(doc)
292
+ log_debug(f"Found {len(documents)} documents")
293
+ return documents
294
+
295
+ def drop(self) -> None:
296
+ """Drop the vector collection."""
297
+ log_debug(f"Dropping collection: {self.collection}")
298
+ self.client.query(self.DROP_TABLE_QUERY.format(collection=self.collection))
299
+
300
+ def exists(self) -> bool:
301
+ """Check if the vector collection exists.
302
+
303
+ Returns:
304
+ True if the collection exists, False otherwise.
305
+
306
+ """
307
+ log_debug(f"Checking if collection exists: {self.collection}")
308
+ response = self.client.query(self.INFO_DB_QUERY)
309
+ result = self._extract_result(response)
310
+ if isinstance(result, dict) and "tables" in result:
311
+ return self.collection in result["tables"]
312
+ return False
313
+
314
+ def delete(self) -> bool:
315
+ """Delete all documents from the vector store.
316
+
317
+ Returns:
318
+ True if the collection was deleted, False otherwise.
319
+
320
+ """
321
+ self.client.query(self.DELETE_ALL_QUERY.format(collection=self.collection))
322
+ return True
323
+
324
+ @staticmethod
325
+ def _extract_result(query_result: Union[List[Dict[str, Any]], Dict[str, Any]]) -> Union[List[Any], Dict[str, Any]]:
326
+ """Extract the actual result from SurrealDB query response.
327
+
328
+ Args:
329
+ query_result: The query result from SurrealDB.
330
+
331
+ Returns:
332
+ The actual result from SurrealDB query response.
333
+
334
+ """
335
+ log_debug(f"Query result: {query_result}")
336
+ if isinstance(query_result, dict):
337
+ return query_result
338
+ if isinstance(query_result, list):
339
+ if len(query_result) > 0:
340
+ return query_result[0].get("result", {})
341
+ return []
342
+ return []
343
+
344
+ async def async_create(self) -> None:
345
+ """Create the vector collection and index asynchronously."""
346
+ log_debug(f"Creating collection: {self.collection}")
347
+ await self.async_client.query(
348
+ self.CREATE_TABLE_QUERY.format(
349
+ collection=self.collection,
350
+ distance=self.distance,
351
+ dimensions=self.dimensions,
352
+ efc=self.efc,
353
+ m=self.m,
354
+ ),
355
+ )
356
+
357
+ async def async_doc_exists(self, document: Document) -> bool:
358
+ """Check if a document exists by its content asynchronously.
359
+
360
+ Returns:
361
+ True if the document exists, False otherwise.
362
+
363
+ """
364
+ response = await self.async_client.query(
365
+ self.DOC_EXISTS_QUERY.format(collection=self.collection),
366
+ {"content": document.content},
367
+ )
368
+ return bool(self._extract_result(response))
369
+
370
+ async def async_name_exists(self, name: str) -> bool:
371
+ """Check if a document exists by its name asynchronously.
372
+
373
+ Returns:
374
+ True if the document exists, False otherwise.
375
+
376
+ """
377
+ response = await self.async_client.query(
378
+ self.NAME_EXISTS_QUERY.format(collection=self.collection),
379
+ {"name": name},
380
+ )
381
+ return bool(self._extract_result(response))
382
+
383
+ async def async_insert(self, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
384
+ """Insert documents into the vector store asynchronously.
385
+
386
+ Args:
387
+ documents: A list of documents to insert.
388
+ filters: A dictionary of filters to apply to the query.
389
+
390
+ """
391
+ for doc in documents:
392
+ doc.embed(embedder=self.embedder)
393
+ meta_data: Dict[str, Any] = doc.meta_data if isinstance(doc.meta_data, dict) else {}
394
+ data: Dict[str, Any] = {"content": doc.content, "embedding": doc.embedding, "meta_data": meta_data}
395
+ if filters:
396
+ data["meta_data"].update(filters)
397
+ log_debug(f"Inserting document asynchronously: {doc.name} ({doc.meta_data})")
398
+ await self.async_client.create(self.collection, data)
399
+
400
+ async def async_upsert(self, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
401
+ """Upsert documents into the vector store asynchronously.
402
+
403
+ Args:
404
+ documents: A list of documents to upsert.
405
+ filters: A dictionary of filters to apply to the query.
406
+
407
+ """
408
+ for doc in documents:
409
+ doc.embed(embedder=self.embedder)
410
+ meta_data: Dict[str, Any] = doc.meta_data if isinstance(doc.meta_data, dict) else {}
411
+ data: Dict[str, Any] = {"content": doc.content, "embedding": doc.embedding, "meta_data": meta_data}
412
+ if filters:
413
+ data["meta_data"].update(filters)
414
+ log_debug(f"Upserting document asynchronously: {doc.name} ({doc.meta_data})")
415
+ thing = f"{self.collection}:{doc.id}" if doc.id else self.collection
416
+ await self.async_client.query(self.UPSERT_QUERY.format(thing=thing), data)
417
+
418
+ async def async_search(
419
+ self,
420
+ query: str,
421
+ limit: int = 5,
422
+ filters: Optional[Dict[str, Any]] = None,
423
+ ) -> List[Document]:
424
+ """Search for similar documents asynchronously.
425
+
426
+ Args:
427
+ query: The query to search for.
428
+ limit: The maximum number of documents to return.
429
+ filters: A dictionary of filters to apply to the query.
430
+
431
+ Returns:
432
+ A list of documents that are similar to the query.
433
+
434
+ """
435
+ query_embedding = self.embedder.get_embedding(query)
436
+ if query_embedding is None:
437
+ log_error(f"Error getting embedding for Query: {query}")
438
+ return []
439
+
440
+ filter_condition = self._build_filter_condition(filters)
441
+ search_query = self.SEARCH_QUERY.format(
442
+ collection=self.collection,
443
+ limit=limit,
444
+ search_ef=self.search_ef,
445
+ filter_condition=filter_condition,
446
+ distance=self.distance,
447
+ )
448
+ response = await self.async_client.query(
449
+ search_query,
450
+ {"query_embedding": query_embedding, **filters} if filters else {"query_embedding": query_embedding},
451
+ )
452
+ log_debug(f"Search response: {response}")
453
+ documents = []
454
+ for item in response:
455
+ if isinstance(item, dict):
456
+ doc = Document(
457
+ content=item.get("content", ""),
458
+ embedding=item.get("embedding", []),
459
+ meta_data=item.get("meta_data", {}),
460
+ embedder=self.embedder,
461
+ )
462
+ documents.append(doc)
463
+ log_debug(f"Found {len(documents)} documents asynchronously")
464
+ return documents
465
+
466
+ async def async_drop(self) -> None:
467
+ """Drop the vector collection asynchronously."""
468
+ log_debug(f"Dropping collection: {self.collection}")
469
+ await self.async_client.query(self.DROP_TABLE_QUERY.format(collection=self.collection))
470
+
471
+ async def async_exists(self) -> bool:
472
+ """Check if the vector collection exists asynchronously.
473
+
474
+ Returns:
475
+ True if the collection exists, False otherwise.
476
+
477
+ """
478
+ log_debug(f"Checking if collection exists: {self.collection}")
479
+ response = await self.async_client.query(self.INFO_DB_QUERY)
480
+ result = self._extract_result(response)
481
+ if isinstance(result, dict) and "tables" in result:
482
+ return self.collection in result["tables"]
483
+ return False
484
+
485
+ @staticmethod
486
+ def upsert_available() -> bool:
487
+ """Check if upsert is available.
488
+
489
+ Returns:
490
+ True if upsert is available, False otherwise.
491
+
492
+ """
493
+ return True
@@ -413,9 +413,7 @@ class Workflow:
413
413
  return func(**call_kwargs)
414
414
  except TypeError as e:
415
415
  # If signature inspection fails, fall back to original method
416
- logger.warning(
417
- f"Async function signature inspection failed: {e}. Falling back to original calling convention."
418
- )
416
+ logger.warning(f"Function signature inspection failed: {e}. Falling back to original calling convention.")
419
417
  return func(workflow, execution_input, **kwargs)
420
418
 
421
419
  def _execute(
@@ -808,7 +806,7 @@ class Workflow:
808
806
  content += str(chunk)
809
807
  workflow_run_response.content = content
810
808
  else:
811
- workflow_run_response.content = self.steps(self, execution_input, **kwargs)
809
+ workflow_run_response.content = self._call_custom_function(self.steps, self, execution_input, **kwargs)
812
810
  workflow_run_response.status = RunStatus.completed
813
811
 
814
812
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agno
3
- Version: 1.7.4
3
+ Version: 1.7.5
4
4
  Summary: Agno: a lightweight library for building Multi-Agent Systems
5
5
  Author-email: Ashpreet Bedi <ashpreet@agno.com>
6
6
  License: Copyright (c) Agno, Inc.
@@ -573,6 +573,8 @@ Provides-Extra: clickhouse
573
573
  Requires-Dist: clickhouse-connect; extra == "clickhouse"
574
574
  Provides-Extra: pinecone
575
575
  Requires-Dist: pinecone==5.4.2; extra == "pinecone"
576
+ Provides-Extra: surrealdb
577
+ Requires-Dist: surrealdb>=1.0.4; extra == "surrealdb"
576
578
  Provides-Extra: pdf
577
579
  Requires-Dist: pypdf; extra == "pdf"
578
580
  Requires-Dist: rapidocr_onnxruntime; extra == "pdf"
@@ -664,6 +666,7 @@ Requires-Dist: agno[weaviate]; extra == "vectordbs"
664
666
  Requires-Dist: agno[milvusdb]; extra == "vectordbs"
665
667
  Requires-Dist: agno[clickhouse]; extra == "vectordbs"
666
668
  Requires-Dist: agno[pinecone]; extra == "vectordbs"
669
+ Requires-Dist: agno[surrealdb]; extra == "vectordbs"
667
670
  Provides-Extra: knowledge
668
671
  Requires-Dist: agno[pdf]; extra == "knowledge"
669
672
  Requires-Dist: agno[docx]; extra == "knowledge"