agno 2.0.11__py3-none-any.whl → 2.1.1__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.
Files changed (93) hide show
  1. agno/agent/agent.py +607 -176
  2. agno/db/in_memory/in_memory_db.py +42 -29
  3. agno/db/mongo/mongo.py +65 -66
  4. agno/db/postgres/postgres.py +6 -4
  5. agno/db/utils.py +50 -22
  6. agno/exceptions.py +62 -1
  7. agno/guardrails/__init__.py +6 -0
  8. agno/guardrails/base.py +19 -0
  9. agno/guardrails/openai.py +144 -0
  10. agno/guardrails/pii.py +94 -0
  11. agno/guardrails/prompt_injection.py +51 -0
  12. agno/knowledge/embedder/aws_bedrock.py +9 -4
  13. agno/knowledge/embedder/azure_openai.py +54 -0
  14. agno/knowledge/embedder/base.py +2 -0
  15. agno/knowledge/embedder/cohere.py +184 -5
  16. agno/knowledge/embedder/google.py +79 -1
  17. agno/knowledge/embedder/huggingface.py +9 -4
  18. agno/knowledge/embedder/jina.py +63 -0
  19. agno/knowledge/embedder/mistral.py +78 -11
  20. agno/knowledge/embedder/ollama.py +5 -0
  21. agno/knowledge/embedder/openai.py +18 -54
  22. agno/knowledge/embedder/voyageai.py +69 -16
  23. agno/knowledge/knowledge.py +11 -4
  24. agno/knowledge/reader/pdf_reader.py +4 -3
  25. agno/knowledge/reader/website_reader.py +3 -2
  26. agno/models/base.py +125 -32
  27. agno/models/cerebras/cerebras.py +1 -0
  28. agno/models/cerebras/cerebras_openai.py +1 -0
  29. agno/models/dashscope/dashscope.py +1 -0
  30. agno/models/google/gemini.py +27 -5
  31. agno/models/openai/chat.py +13 -4
  32. agno/models/openai/responses.py +1 -1
  33. agno/models/perplexity/perplexity.py +2 -3
  34. agno/models/requesty/__init__.py +5 -0
  35. agno/models/requesty/requesty.py +49 -0
  36. agno/models/vllm/vllm.py +1 -0
  37. agno/models/xai/xai.py +1 -0
  38. agno/os/app.py +98 -126
  39. agno/os/interfaces/__init__.py +1 -0
  40. agno/os/interfaces/agui/agui.py +21 -5
  41. agno/os/interfaces/base.py +4 -2
  42. agno/os/interfaces/slack/slack.py +13 -8
  43. agno/os/interfaces/whatsapp/router.py +2 -0
  44. agno/os/interfaces/whatsapp/whatsapp.py +12 -5
  45. agno/os/mcp.py +2 -2
  46. agno/os/middleware/__init__.py +7 -0
  47. agno/os/middleware/jwt.py +233 -0
  48. agno/os/router.py +182 -46
  49. agno/os/routers/home.py +2 -2
  50. agno/os/routers/memory/memory.py +23 -1
  51. agno/os/routers/memory/schemas.py +1 -1
  52. agno/os/routers/session/session.py +20 -3
  53. agno/os/utils.py +74 -8
  54. agno/run/agent.py +120 -77
  55. agno/run/base.py +2 -13
  56. agno/run/team.py +115 -72
  57. agno/run/workflow.py +5 -15
  58. agno/session/summary.py +9 -10
  59. agno/session/team.py +2 -1
  60. agno/team/team.py +721 -169
  61. agno/tools/firecrawl.py +4 -4
  62. agno/tools/function.py +42 -2
  63. agno/tools/knowledge.py +3 -3
  64. agno/tools/searxng.py +2 -2
  65. agno/tools/serper.py +2 -2
  66. agno/tools/spider.py +2 -2
  67. agno/tools/workflow.py +4 -5
  68. agno/utils/events.py +66 -1
  69. agno/utils/hooks.py +57 -0
  70. agno/utils/media.py +11 -9
  71. agno/utils/print_response/agent.py +43 -5
  72. agno/utils/print_response/team.py +48 -12
  73. agno/utils/serialize.py +32 -0
  74. agno/vectordb/cassandra/cassandra.py +44 -4
  75. agno/vectordb/chroma/chromadb.py +79 -8
  76. agno/vectordb/clickhouse/clickhousedb.py +43 -6
  77. agno/vectordb/couchbase/couchbase.py +76 -5
  78. agno/vectordb/lancedb/lance_db.py +38 -3
  79. agno/vectordb/milvus/milvus.py +76 -4
  80. agno/vectordb/mongodb/mongodb.py +76 -4
  81. agno/vectordb/pgvector/pgvector.py +50 -6
  82. agno/vectordb/pineconedb/pineconedb.py +39 -2
  83. agno/vectordb/qdrant/qdrant.py +76 -26
  84. agno/vectordb/singlestore/singlestore.py +77 -4
  85. agno/vectordb/upstashdb/upstashdb.py +42 -2
  86. agno/vectordb/weaviate/weaviate.py +39 -3
  87. agno/workflow/types.py +5 -6
  88. agno/workflow/workflow.py +58 -2
  89. {agno-2.0.11.dist-info → agno-2.1.1.dist-info}/METADATA +4 -3
  90. {agno-2.0.11.dist-info → agno-2.1.1.dist-info}/RECORD +93 -82
  91. {agno-2.0.11.dist-info → agno-2.1.1.dist-info}/WHEEL +0 -0
  92. {agno-2.0.11.dist-info → agno-2.1.1.dist-info}/licenses/LICENSE +0 -0
  93. {agno-2.0.11.dist-info → agno-2.1.1.dist-info}/top_level.txt +0 -0
@@ -504,8 +504,48 @@ class UpstashVectorDb(VectorDb):
504
504
  _namespace = self.namespace if namespace is None else namespace
505
505
  vectors = []
506
506
 
507
- embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
508
- await asyncio.gather(*embed_tasks, return_exceptions=True)
507
+ if (
508
+ self.embedder
509
+ and self.embedder.enable_batch
510
+ and hasattr(self.embedder, "async_get_embeddings_batch_and_usage")
511
+ ):
512
+ # Use batch embedding when enabled and supported
513
+ try:
514
+ # Extract content from all documents
515
+ doc_contents = [doc.content for doc in documents]
516
+
517
+ # Get batch embeddings and usage
518
+ embeddings, usages = await self.embedder.async_get_embeddings_batch_and_usage(doc_contents)
519
+
520
+ # Process documents with pre-computed embeddings
521
+ for j, doc in enumerate(documents):
522
+ try:
523
+ if j < len(embeddings):
524
+ doc.embedding = embeddings[j]
525
+ doc.usage = usages[j] if j < len(usages) else None
526
+ except Exception as e:
527
+ logger.error(f"Error assigning batch embedding to document '{doc.name}': {e}")
528
+
529
+ except Exception as e:
530
+ # Check if this is a rate limit error - don't fall back as it would make things worse
531
+ error_str = str(e).lower()
532
+ is_rate_limit = any(
533
+ phrase in error_str
534
+ for phrase in ["rate limit", "too many requests", "429", "trial key", "api calls / minute"]
535
+ )
536
+
537
+ if is_rate_limit:
538
+ logger.error(f"Rate limit detected during batch embedding. {e}")
539
+ raise e
540
+ else:
541
+ logger.warning(f"Async batch embedding failed, falling back to individual embeddings: {e}")
542
+ # Fall back to individual embedding
543
+ embed_tasks = [doc.async_embed(embedder=self.embedder) for doc in documents]
544
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
545
+ else:
546
+ # Use individual embedding
547
+ embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
548
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
509
549
 
510
550
  for i, document in enumerate(documents):
511
551
  if document.id is None:
@@ -270,9 +270,45 @@ class Weaviate(VectorDb):
270
270
  if not documents:
271
271
  return
272
272
 
273
- # Embed document
274
- embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
275
- await asyncio.gather(*embed_tasks, return_exceptions=True)
273
+ # Apply batch embedding logic
274
+ if self.embedder.enable_batch and hasattr(self.embedder, "async_get_embeddings_batch_and_usage"):
275
+ # Use batch embedding when enabled and supported
276
+ try:
277
+ # Extract content from all documents
278
+ doc_contents = [doc.content for doc in documents]
279
+
280
+ # Get batch embeddings and usage
281
+ embeddings, usages = await self.embedder.async_get_embeddings_batch_and_usage(doc_contents)
282
+
283
+ # Process documents with pre-computed embeddings
284
+ for j, doc in enumerate(documents):
285
+ try:
286
+ if j < len(embeddings):
287
+ doc.embedding = embeddings[j]
288
+ doc.usage = usages[j] if j < len(usages) else None
289
+ except Exception as e:
290
+ logger.error(f"Error assigning batch embedding to document '{doc.name}': {e}")
291
+
292
+ except Exception as e:
293
+ # Check if this is a rate limit error - don't fall back as it would make things worse
294
+ error_str = str(e).lower()
295
+ is_rate_limit = any(
296
+ phrase in error_str
297
+ for phrase in ["rate limit", "too many requests", "429", "trial key", "api calls / minute"]
298
+ )
299
+
300
+ if is_rate_limit:
301
+ logger.error(f"Rate limit detected during batch embedding. {e}")
302
+ raise e
303
+ else:
304
+ logger.warning(f"Async batch embedding failed, falling back to individual embeddings: {e}")
305
+ # Fall back to individual embedding
306
+ embed_tasks = [doc.async_embed(embedder=self.embedder) for doc in documents]
307
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
308
+ else:
309
+ # Use individual embedding
310
+ embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
311
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
276
312
 
277
313
  client = await self.get_async_client()
278
314
  try:
agno/workflow/types.py CHANGED
@@ -1,3 +1,4 @@
1
+ import json
1
2
  from dataclasses import dataclass
2
3
  from enum import Enum
3
4
  from typing import Any, Dict, List, Optional, Union
@@ -8,6 +9,7 @@ from pydantic import BaseModel
8
9
  from agno.media import Audio, File, Image, Video
9
10
  from agno.models.metrics import Metrics
10
11
  from agno.utils.log import log_warning
12
+ from agno.utils.serialize import json_serializer
11
13
 
12
14
 
13
15
  @dataclass
@@ -57,6 +59,7 @@ class WorkflowExecutionInput:
57
59
  "images": [img.to_dict() for img in self.images] if self.images else None,
58
60
  "videos": [vid.to_dict() for vid in self.videos] if self.videos else None,
59
61
  "audio": [aud.to_dict() for aud in self.audio] if self.audio else None,
62
+ "files": [file.to_dict() for file in self.files] if self.files else None,
60
63
  }
61
64
 
62
65
 
@@ -442,9 +445,7 @@ class WebSocketHandler:
442
445
  else:
443
446
  data = {"type": "message", "content": str(event)}
444
447
 
445
- import json
446
-
447
- await self.websocket.send_text(self.format_sse_event(json.dumps(data)))
448
+ await self.websocket.send_text(self.format_sse_event(json.dumps(data, default=json_serializer)))
448
449
 
449
450
  except Exception as e:
450
451
  log_warning(f"Failed to handle WebSocket event: {e}")
@@ -465,9 +466,7 @@ class WebSocketHandler:
465
466
  return
466
467
 
467
468
  try:
468
- import json
469
-
470
- await self.websocket.send_text(self.format_sse_event(json.dumps(data)))
469
+ await self.websocket.send_text(self.format_sse_event(json.dumps(data, default=json_serializer)))
471
470
  except Exception as e:
472
471
  log_warning(f"Failed to send WebSocket dict: {e}")
473
472
 
agno/workflow/workflow.py CHANGED
@@ -25,7 +25,7 @@ from pydantic import BaseModel
25
25
 
26
26
  from agno.agent.agent import Agent
27
27
  from agno.db.base import BaseDb, SessionType
28
- from agno.exceptions import RunCancelledException
28
+ from agno.exceptions import InputCheckError, OutputCheckError, RunCancelledException
29
29
  from agno.media import Audio, File, Image, Video
30
30
  from agno.models.message import Message
31
31
  from agno.models.metrics import Metrics
@@ -54,6 +54,7 @@ from agno.team.team import Team
54
54
  from agno.utils.common import is_typed_dict, validate_typed_dict
55
55
  from agno.utils.log import (
56
56
  log_debug,
57
+ log_error,
57
58
  log_warning,
58
59
  logger,
59
60
  set_log_level_to_debug,
@@ -1000,8 +1001,14 @@ class Workflow:
1000
1001
  workflow_run_response.audio = output_audio
1001
1002
  workflow_run_response.status = RunStatus.completed
1002
1003
 
1004
+ except (InputCheckError, OutputCheckError) as e:
1005
+ log_error(f"Validation failed: {str(e)} | Check: {e.check_trigger}")
1006
+ # Store error response
1007
+ workflow_run_response.status = RunStatus.error
1008
+ workflow_run_response.content = f"Validation failed: {str(e)} | Check: {e.check_trigger}"
1009
+
1010
+ raise e
1003
1011
  except RunCancelledException as e:
1004
- # Handle run cancellation
1005
1012
  logger.info(f"Workflow run {workflow_run_response.run_id} was cancelled")
1006
1013
  workflow_run_response.status = RunStatus.cancelled
1007
1014
  workflow_run_response.content = str(e)
@@ -1013,6 +1020,7 @@ class Workflow:
1013
1020
  # Store error response
1014
1021
  workflow_run_response.status = RunStatus.error
1015
1022
  workflow_run_response.content = f"Workflow execution failed: {e}"
1023
+ raise e
1016
1024
 
1017
1025
  finally:
1018
1026
  self._update_session_metrics(session=session, workflow_run_response=workflow_run_response)
@@ -1199,6 +1207,24 @@ class Workflow:
1199
1207
  workflow_run_response.audio = output_audio
1200
1208
  workflow_run_response.status = RunStatus.completed
1201
1209
 
1210
+ except (InputCheckError, OutputCheckError) as e:
1211
+ log_error(f"Validation failed: {str(e)} | Check: {e.check_trigger}")
1212
+
1213
+ from agno.run.workflow import WorkflowErrorEvent
1214
+
1215
+ error_event = WorkflowErrorEvent(
1216
+ run_id=workflow_run_response.run_id or "",
1217
+ workflow_id=self.id,
1218
+ workflow_name=self.name,
1219
+ session_id=session.session_id,
1220
+ error=str(e),
1221
+ )
1222
+
1223
+ yield error_event
1224
+
1225
+ # Update workflow_run_response with error
1226
+ workflow_run_response.content = error_event.error
1227
+ workflow_run_response.status = RunStatus.error
1202
1228
  except RunCancelledException as e:
1203
1229
  # Handle run cancellation during streaming
1204
1230
  logger.info(f"Workflow run {workflow_run_response.run_id} was cancelled during streaming")
@@ -1230,6 +1256,7 @@ class Workflow:
1230
1256
  # Update workflow_run_response with error
1231
1257
  workflow_run_response.content = error_event.error
1232
1258
  workflow_run_response.status = RunStatus.error
1259
+ raise e
1233
1260
 
1234
1261
  # Yield workflow completed event
1235
1262
  workflow_completed_event = WorkflowCompletedEvent(
@@ -1436,6 +1463,13 @@ class Workflow:
1436
1463
  workflow_run_response.audio = output_audio
1437
1464
  workflow_run_response.status = RunStatus.completed
1438
1465
 
1466
+ except (InputCheckError, OutputCheckError) as e:
1467
+ log_error(f"Validation failed: {str(e)} | Check: {e.check_trigger}")
1468
+ # Store error response
1469
+ workflow_run_response.status = RunStatus.error
1470
+ workflow_run_response.content = f"Validation failed: {str(e)} | Check: {e.check_trigger}"
1471
+
1472
+ raise e
1439
1473
  except RunCancelledException as e:
1440
1474
  logger.info(f"Workflow run {workflow_run_response.run_id} was cancelled")
1441
1475
  workflow_run_response.status = RunStatus.cancelled
@@ -1444,6 +1478,7 @@ class Workflow:
1444
1478
  logger.error(f"Workflow execution failed: {e}")
1445
1479
  workflow_run_response.status = RunStatus.error
1446
1480
  workflow_run_response.content = f"Workflow execution failed: {e}"
1481
+ raise e
1447
1482
 
1448
1483
  self._update_session_metrics(session=session, workflow_run_response=workflow_run_response)
1449
1484
  session.upsert_run(run=workflow_run_response)
@@ -1471,6 +1506,7 @@ class Workflow:
1471
1506
  from inspect import isasyncgenfunction, iscoroutinefunction, isgeneratorfunction
1472
1507
 
1473
1508
  workflow_run_response.status = RunStatus.running
1509
+
1474
1510
  workflow_started_event = WorkflowStartedEvent(
1475
1511
  run_id=workflow_run_response.run_id or "",
1476
1512
  workflow_name=workflow_run_response.workflow_name,
@@ -1633,6 +1669,24 @@ class Workflow:
1633
1669
  workflow_run_response.audio = output_audio
1634
1670
  workflow_run_response.status = RunStatus.completed
1635
1671
 
1672
+ except (InputCheckError, OutputCheckError) as e:
1673
+ log_error(f"Validation failed: {str(e)} | Check: {e.check_trigger}")
1674
+
1675
+ from agno.run.workflow import WorkflowErrorEvent
1676
+
1677
+ error_event = WorkflowErrorEvent(
1678
+ run_id=workflow_run_response.run_id or "",
1679
+ workflow_id=self.id,
1680
+ workflow_name=self.name,
1681
+ session_id=session.session_id,
1682
+ error=str(e),
1683
+ )
1684
+
1685
+ yield error_event
1686
+
1687
+ # Update workflow_run_response with error
1688
+ workflow_run_response.content = error_event.error
1689
+ workflow_run_response.status = RunStatus.error
1636
1690
  except RunCancelledException as e:
1637
1691
  # Handle run cancellation during streaming
1638
1692
  logger.info(f"Workflow run {workflow_run_response.run_id} was cancelled during streaming")
@@ -1668,6 +1722,7 @@ class Workflow:
1668
1722
  # Update workflow_run_response with error
1669
1723
  workflow_run_response.content = error_event.error
1670
1724
  workflow_run_response.status = RunStatus.error
1725
+ raise e
1671
1726
 
1672
1727
  # Yield workflow completed event
1673
1728
  workflow_completed_event = WorkflowCompletedEvent(
@@ -2175,6 +2230,7 @@ class Workflow:
2175
2230
  audio=audio, # type: ignore
2176
2231
  images=images, # type: ignore
2177
2232
  videos=videos, # type: ignore
2233
+ files=files,
2178
2234
  )
2179
2235
  log_debug(
2180
2236
  f"Created async pipeline input with session state keys: {list(session_state.keys()) if session_state else 'None'}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agno
3
- Version: 2.0.11
3
+ Version: 2.1.1
4
4
  Summary: Agno: a lightweight library for building Multi-Agent Systems
5
5
  Author-email: Ashpreet Bedi <ashpreet@agno.com>
6
6
  Project-URL: homepage, https://agno.com
@@ -48,6 +48,7 @@ Requires-Dist: uvicorn; extra == "dev"
48
48
  Provides-Extra: os
49
49
  Requires-Dist: fastapi; extra == "os"
50
50
  Requires-Dist: uvicorn; extra == "os"
51
+ Requires-Dist: PyJWT; extra == "os"
51
52
  Provides-Extra: integration-tests
52
53
  Requires-Dist: exa_py; extra == "integration-tests"
53
54
  Requires-Dist: ddgs; extra == "integration-tests"
@@ -444,7 +445,7 @@ At Agno, we're obsessed with performance. Why? because even simple AI workflows
444
445
  - Agent instantiation: ~3μs on average
445
446
  - Memory footprint: ~6.5Kib on average
446
447
 
447
- > Tested on an Apple M4 Mackbook Pro.
448
+ > Tested on an Apple M4 MacBook Pro.
448
449
 
449
450
  While an Agent's run-time is bottlenecked by inference, we must do everything possible to minimize execution time, reduce memory usage, and parallelize tool calls. These numbers may seem trivial at first, but our experience shows that they add up even at a reasonably small scale.
450
451
 
@@ -468,7 +469,7 @@ python evals/performance/instantiation_with_tool.py
468
469
  python evals/performance/other/langgraph_instantiation.py
469
470
  ```
470
471
 
471
- > The following evaluation is run on an Apple M4 Mackbook Pro. It also runs as a Github action on this repo.
472
+ > The following evaluation is run on an Apple M4 MacBook Pro. It also runs as a Github action on this repo.
472
473
 
473
474
  LangGraph is on the right, **let's start it first and give it a head start**.
474
475