agno 2.3.3__py3-none-any.whl → 2.3.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.
Files changed (108) hide show
  1. agno/agent/agent.py +177 -41
  2. agno/culture/manager.py +2 -2
  3. agno/db/base.py +330 -8
  4. agno/db/dynamo/dynamo.py +722 -2
  5. agno/db/dynamo/schemas.py +127 -0
  6. agno/db/firestore/firestore.py +573 -1
  7. agno/db/firestore/schemas.py +40 -0
  8. agno/db/gcs_json/gcs_json_db.py +446 -1
  9. agno/db/in_memory/in_memory_db.py +143 -1
  10. agno/db/json/json_db.py +438 -1
  11. agno/db/mongo/async_mongo.py +522 -0
  12. agno/db/mongo/mongo.py +523 -1
  13. agno/db/mongo/schemas.py +29 -0
  14. agno/db/mysql/mysql.py +536 -3
  15. agno/db/mysql/schemas.py +38 -0
  16. agno/db/postgres/async_postgres.py +546 -14
  17. agno/db/postgres/postgres.py +535 -2
  18. agno/db/postgres/schemas.py +38 -0
  19. agno/db/redis/redis.py +468 -1
  20. agno/db/redis/schemas.py +32 -0
  21. agno/db/singlestore/schemas.py +38 -0
  22. agno/db/singlestore/singlestore.py +523 -1
  23. agno/db/sqlite/async_sqlite.py +548 -9
  24. agno/db/sqlite/schemas.py +38 -0
  25. agno/db/sqlite/sqlite.py +537 -5
  26. agno/db/sqlite/utils.py +6 -8
  27. agno/db/surrealdb/models.py +25 -0
  28. agno/db/surrealdb/surrealdb.py +548 -1
  29. agno/eval/accuracy.py +10 -4
  30. agno/eval/performance.py +10 -4
  31. agno/eval/reliability.py +22 -13
  32. agno/exceptions.py +11 -0
  33. agno/hooks/__init__.py +3 -0
  34. agno/hooks/decorator.py +164 -0
  35. agno/knowledge/chunking/semantic.py +2 -2
  36. agno/models/aimlapi/aimlapi.py +17 -0
  37. agno/models/anthropic/claude.py +19 -12
  38. agno/models/aws/bedrock.py +3 -4
  39. agno/models/aws/claude.py +5 -1
  40. agno/models/azure/ai_foundry.py +2 -2
  41. agno/models/azure/openai_chat.py +8 -0
  42. agno/models/cerebras/cerebras.py +61 -4
  43. agno/models/cerebras/cerebras_openai.py +17 -0
  44. agno/models/cohere/chat.py +5 -1
  45. agno/models/cometapi/cometapi.py +18 -1
  46. agno/models/dashscope/dashscope.py +2 -3
  47. agno/models/deepinfra/deepinfra.py +18 -1
  48. agno/models/deepseek/deepseek.py +2 -3
  49. agno/models/fireworks/fireworks.py +18 -1
  50. agno/models/google/gemini.py +8 -2
  51. agno/models/groq/groq.py +5 -2
  52. agno/models/internlm/internlm.py +18 -1
  53. agno/models/langdb/langdb.py +13 -1
  54. agno/models/litellm/chat.py +2 -2
  55. agno/models/litellm/litellm_openai.py +18 -1
  56. agno/models/meta/llama_openai.py +19 -2
  57. agno/models/nebius/nebius.py +2 -3
  58. agno/models/nvidia/nvidia.py +20 -3
  59. agno/models/openai/chat.py +17 -2
  60. agno/models/openai/responses.py +17 -2
  61. agno/models/openrouter/openrouter.py +21 -2
  62. agno/models/perplexity/perplexity.py +17 -1
  63. agno/models/portkey/portkey.py +7 -6
  64. agno/models/requesty/requesty.py +19 -2
  65. agno/models/response.py +2 -1
  66. agno/models/sambanova/sambanova.py +20 -3
  67. agno/models/siliconflow/siliconflow.py +19 -2
  68. agno/models/together/together.py +20 -3
  69. agno/models/vercel/v0.py +20 -3
  70. agno/models/vllm/vllm.py +19 -14
  71. agno/models/xai/xai.py +19 -2
  72. agno/os/app.py +104 -0
  73. agno/os/config.py +13 -0
  74. agno/os/interfaces/whatsapp/router.py +0 -1
  75. agno/os/mcp.py +1 -0
  76. agno/os/router.py +31 -0
  77. agno/os/routers/traces/__init__.py +3 -0
  78. agno/os/routers/traces/schemas.py +414 -0
  79. agno/os/routers/traces/traces.py +499 -0
  80. agno/os/schema.py +22 -1
  81. agno/os/utils.py +57 -0
  82. agno/run/agent.py +1 -0
  83. agno/run/base.py +17 -0
  84. agno/run/team.py +4 -0
  85. agno/session/team.py +1 -0
  86. agno/table.py +10 -0
  87. agno/team/team.py +215 -65
  88. agno/tools/function.py +10 -8
  89. agno/tools/nano_banana.py +1 -1
  90. agno/tracing/__init__.py +12 -0
  91. agno/tracing/exporter.py +157 -0
  92. agno/tracing/schemas.py +276 -0
  93. agno/tracing/setup.py +111 -0
  94. agno/utils/agent.py +4 -4
  95. agno/utils/hooks.py +56 -1
  96. agno/vectordb/qdrant/qdrant.py +22 -22
  97. agno/workflow/condition.py +8 -0
  98. agno/workflow/loop.py +8 -0
  99. agno/workflow/parallel.py +8 -0
  100. agno/workflow/router.py +8 -0
  101. agno/workflow/step.py +20 -0
  102. agno/workflow/steps.py +8 -0
  103. agno/workflow/workflow.py +83 -17
  104. {agno-2.3.3.dist-info → agno-2.3.5.dist-info}/METADATA +2 -2
  105. {agno-2.3.3.dist-info → agno-2.3.5.dist-info}/RECORD +108 -98
  106. {agno-2.3.3.dist-info → agno-2.3.5.dist-info}/WHEEL +0 -0
  107. {agno-2.3.3.dist-info → agno-2.3.5.dist-info}/licenses/LICENSE +0 -0
  108. {agno-2.3.3.dist-info → agno-2.3.5.dist-info}/top_level.txt +0 -0
agno/db/base.py CHANGED
@@ -1,9 +1,12 @@
1
1
  from abc import ABC, abstractmethod
2
- from datetime import date
2
+ from datetime import date, datetime
3
3
  from enum import Enum
4
- from typing import Any, Dict, List, Optional, Tuple, Union
4
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
5
5
  from uuid import uuid4
6
6
 
7
+ if TYPE_CHECKING:
8
+ from agno.tracing.schemas import Span, Trace
9
+
7
10
  from agno.db.schemas import UserMemory
8
11
  from agno.db.schemas.culture import CulturalKnowledge
9
12
  from agno.db.schemas.evals import EvalFilterType, EvalRunRecord, EvalType
@@ -31,6 +34,8 @@ class BaseDb(ABC):
31
34
  metrics_table: Optional[str] = None,
32
35
  eval_table: Optional[str] = None,
33
36
  knowledge_table: Optional[str] = None,
37
+ traces_table: Optional[str] = None,
38
+ spans_table: Optional[str] = None,
34
39
  versions_table: Optional[str] = None,
35
40
  id: Optional[str] = None,
36
41
  ):
@@ -41,6 +46,8 @@ class BaseDb(ABC):
41
46
  self.metrics_table_name = metrics_table or "agno_metrics"
42
47
  self.eval_table_name = eval_table or "agno_eval_runs"
43
48
  self.knowledge_table_name = knowledge_table or "agno_knowledge"
49
+ self.trace_table_name = traces_table or "agno_traces"
50
+ self.span_table_name = spans_table or "agno_spans"
44
51
  self.versions_table_name = versions_table or "agno_schema_versions"
45
52
 
46
53
  @abstractmethod
@@ -299,6 +306,159 @@ class BaseDb(ABC):
299
306
  ) -> Optional[Union[EvalRunRecord, Dict[str, Any]]]:
300
307
  raise NotImplementedError
301
308
 
309
+ # --- Traces ---
310
+ @abstractmethod
311
+ def create_trace(self, trace: "Trace") -> None:
312
+ """Create a single trace record in the database.
313
+
314
+ Args:
315
+ trace: The Trace object to store (one per trace_id).
316
+ """
317
+ raise NotImplementedError
318
+
319
+ @abstractmethod
320
+ def get_trace(
321
+ self,
322
+ trace_id: Optional[str] = None,
323
+ run_id: Optional[str] = None,
324
+ session_id: Optional[str] = None,
325
+ user_id: Optional[str] = None,
326
+ agent_id: Optional[str] = None,
327
+ ):
328
+ """Get a single trace by trace_id or other filters.
329
+
330
+ Args:
331
+ trace_id: The unique trace identifier.
332
+ run_id: Filter by run ID (returns first match).
333
+ session_id: Filter by session ID (returns first match).
334
+ user_id: Filter by user ID (returns first match).
335
+ agent_id: Filter by agent ID (returns first match).
336
+
337
+ Returns:
338
+ Optional[Trace]: The trace if found, None otherwise.
339
+
340
+ Note:
341
+ If multiple filters are provided, trace_id takes precedence.
342
+ For other filters, the most recent trace is returned.
343
+ """
344
+ raise NotImplementedError
345
+
346
+ @abstractmethod
347
+ def get_traces(
348
+ self,
349
+ run_id: Optional[str] = None,
350
+ session_id: Optional[str] = None,
351
+ user_id: Optional[str] = None,
352
+ agent_id: Optional[str] = None,
353
+ team_id: Optional[str] = None,
354
+ workflow_id: Optional[str] = None,
355
+ status: Optional[str] = None,
356
+ start_time: Optional[datetime] = None,
357
+ end_time: Optional[datetime] = None,
358
+ limit: Optional[int] = 20,
359
+ page: Optional[int] = 1,
360
+ ) -> tuple[List, int]:
361
+ """Get traces matching the provided filters with pagination.
362
+
363
+ Args:
364
+ run_id: Filter by run ID.
365
+ session_id: Filter by session ID.
366
+ user_id: Filter by user ID.
367
+ agent_id: Filter by agent ID.
368
+ team_id: Filter by team ID.
369
+ workflow_id: Filter by workflow ID.
370
+ status: Filter by status (OK, ERROR).
371
+ start_time: Filter traces starting after this datetime.
372
+ end_time: Filter traces ending before this datetime.
373
+ limit: Maximum number of traces to return per page.
374
+ page: Page number (1-indexed).
375
+
376
+ Returns:
377
+ tuple[List[Trace], int]: Tuple of (list of matching traces with datetime fields, total count).
378
+ """
379
+ raise NotImplementedError
380
+
381
+ @abstractmethod
382
+ def get_trace_stats(
383
+ self,
384
+ user_id: Optional[str] = None,
385
+ agent_id: Optional[str] = None,
386
+ team_id: Optional[str] = None,
387
+ workflow_id: Optional[str] = None,
388
+ start_time: Optional[datetime] = None,
389
+ end_time: Optional[datetime] = None,
390
+ limit: Optional[int] = 20,
391
+ page: Optional[int] = 1,
392
+ ) -> tuple[List[Dict[str, Any]], int]:
393
+ """Get trace statistics grouped by session.
394
+
395
+ Args:
396
+ user_id: Filter by user ID.
397
+ agent_id: Filter by agent ID.
398
+ team_id: Filter by team ID.
399
+ workflow_id: Filter by workflow ID.
400
+ start_time: Filter sessions with traces created after this datetime.
401
+ end_time: Filter sessions with traces created before this datetime.
402
+ limit: Maximum number of sessions to return per page.
403
+ page: Page number (1-indexed).
404
+
405
+ Returns:
406
+ tuple[List[Dict], int]: Tuple of (list of session stats dicts, total count).
407
+ Each dict contains: session_id, user_id, agent_id, team_id, total_traces,
408
+ first_trace_at (datetime), last_trace_at (datetime).
409
+ """
410
+ raise NotImplementedError
411
+
412
+ # --- Spans ---
413
+ @abstractmethod
414
+ def create_span(self, span: "Span") -> None:
415
+ """Create a single span in the database.
416
+
417
+ Args:
418
+ span: The Span object to store.
419
+ """
420
+ raise NotImplementedError
421
+
422
+ @abstractmethod
423
+ def create_spans(self, spans: List) -> None:
424
+ """Create multiple spans in the database as a batch.
425
+
426
+ Args:
427
+ spans: List of Span objects to store.
428
+ """
429
+ raise NotImplementedError
430
+
431
+ @abstractmethod
432
+ def get_span(self, span_id: str):
433
+ """Get a single span by its span_id.
434
+
435
+ Args:
436
+ span_id: The unique span identifier.
437
+
438
+ Returns:
439
+ Optional[Span]: The span if found, None otherwise.
440
+ """
441
+ raise NotImplementedError
442
+
443
+ @abstractmethod
444
+ def get_spans(
445
+ self,
446
+ trace_id: Optional[str] = None,
447
+ parent_span_id: Optional[str] = None,
448
+ limit: Optional[int] = 1000,
449
+ ) -> List:
450
+ """Get spans matching the provided filters.
451
+
452
+ Args:
453
+ trace_id: Filter by trace ID.
454
+ parent_span_id: Filter by parent span ID.
455
+ limit: Maximum number of spans to return.
456
+
457
+ Returns:
458
+ List[Span]: List of matching spans.
459
+ """
460
+ raise NotImplementedError
461
+
302
462
  # --- Cultural Knowledge ---
303
463
  @abstractmethod
304
464
  def clear_cultural_knowledge(self) -> None:
@@ -341,6 +501,8 @@ class AsyncBaseDb(ABC):
341
501
  metrics_table: Optional[str] = None,
342
502
  eval_table: Optional[str] = None,
343
503
  knowledge_table: Optional[str] = None,
504
+ traces_table: Optional[str] = None,
505
+ spans_table: Optional[str] = None,
344
506
  culture_table: Optional[str] = None,
345
507
  versions_table: Optional[str] = None,
346
508
  ):
@@ -350,6 +512,8 @@ class AsyncBaseDb(ABC):
350
512
  self.metrics_table_name = metrics_table or "agno_metrics"
351
513
  self.eval_table_name = eval_table or "agno_eval_runs"
352
514
  self.knowledge_table_name = knowledge_table or "agno_knowledge"
515
+ self.trace_table_name = traces_table or "agno_traces"
516
+ self.span_table_name = spans_table or "agno_spans"
353
517
  self.culture_table_name = culture_table or "agno_culture"
354
518
  self.versions_table_name = versions_table or "agno_schema_versions"
355
519
 
@@ -399,7 +563,7 @@ class AsyncBaseDb(ABC):
399
563
  @abstractmethod
400
564
  async def get_sessions(
401
565
  self,
402
- session_type: SessionType,
566
+ session_type: Optional[SessionType] = None,
403
567
  user_id: Optional[str] = None,
404
568
  component_id: Optional[str] = None,
405
569
  session_name: Optional[str] = None,
@@ -593,6 +757,159 @@ class AsyncBaseDb(ABC):
593
757
  ) -> Optional[Union[EvalRunRecord, Dict[str, Any]]]:
594
758
  raise NotImplementedError
595
759
 
760
+ # --- Traces ---
761
+ @abstractmethod
762
+ async def create_trace(self, trace) -> None:
763
+ """Create a single trace record in the database.
764
+
765
+ Args:
766
+ trace: The Trace object to store (one per trace_id).
767
+ """
768
+ raise NotImplementedError
769
+
770
+ @abstractmethod
771
+ async def get_trace(
772
+ self,
773
+ trace_id: Optional[str] = None,
774
+ run_id: Optional[str] = None,
775
+ session_id: Optional[str] = None,
776
+ user_id: Optional[str] = None,
777
+ agent_id: Optional[str] = None,
778
+ ):
779
+ """Get a single trace by trace_id or other filters.
780
+
781
+ Args:
782
+ trace_id: The unique trace identifier.
783
+ run_id: Filter by run ID (returns first match).
784
+ session_id: Filter by session ID (returns first match).
785
+ user_id: Filter by user ID (returns first match).
786
+ agent_id: Filter by agent ID (returns first match).
787
+
788
+ Returns:
789
+ Optional[Trace]: The trace if found, None otherwise.
790
+
791
+ Note:
792
+ If multiple filters are provided, trace_id takes precedence.
793
+ For other filters, the most recent trace is returned.
794
+ """
795
+ raise NotImplementedError
796
+
797
+ @abstractmethod
798
+ async def get_traces(
799
+ self,
800
+ run_id: Optional[str] = None,
801
+ session_id: Optional[str] = None,
802
+ user_id: Optional[str] = None,
803
+ agent_id: Optional[str] = None,
804
+ team_id: Optional[str] = None,
805
+ workflow_id: Optional[str] = None,
806
+ status: Optional[str] = None,
807
+ start_time: Optional[datetime] = None,
808
+ end_time: Optional[datetime] = None,
809
+ limit: Optional[int] = 20,
810
+ page: Optional[int] = 1,
811
+ ) -> tuple[List, int]:
812
+ """Get traces matching the provided filters with pagination.
813
+
814
+ Args:
815
+ run_id: Filter by run ID.
816
+ session_id: Filter by session ID.
817
+ user_id: Filter by user ID.
818
+ agent_id: Filter by agent ID.
819
+ team_id: Filter by team ID.
820
+ workflow_id: Filter by workflow ID.
821
+ status: Filter by status (OK, ERROR).
822
+ start_time: Filter traces starting after this datetime.
823
+ end_time: Filter traces ending before this datetime.
824
+ limit: Maximum number of traces to return per page.
825
+ page: Page number (1-indexed).
826
+
827
+ Returns:
828
+ tuple[List[Trace], int]: Tuple of (list of matching traces with datetime fields, total count).
829
+ """
830
+ raise NotImplementedError
831
+
832
+ @abstractmethod
833
+ async def get_trace_stats(
834
+ self,
835
+ user_id: Optional[str] = None,
836
+ agent_id: Optional[str] = None,
837
+ team_id: Optional[str] = None,
838
+ workflow_id: Optional[str] = None,
839
+ start_time: Optional[datetime] = None,
840
+ end_time: Optional[datetime] = None,
841
+ limit: Optional[int] = 20,
842
+ page: Optional[int] = 1,
843
+ ) -> tuple[List[Dict[str, Any]], int]:
844
+ """Get trace statistics grouped by session.
845
+
846
+ Args:
847
+ user_id: Filter by user ID.
848
+ agent_id: Filter by agent ID.
849
+ team_id: Filter by team ID.
850
+ workflow_id: Filter by workflow ID.
851
+ start_time: Filter sessions with traces created after this datetime.
852
+ end_time: Filter sessions with traces created before this datetime.
853
+ limit: Maximum number of sessions to return per page.
854
+ page: Page number (1-indexed).
855
+
856
+ Returns:
857
+ tuple[List[Dict], int]: Tuple of (list of session stats dicts, total count).
858
+ Each dict contains: session_id, user_id, agent_id, team_id, total_traces,
859
+ first_trace_at (datetime), last_trace_at (datetime).
860
+ """
861
+ raise NotImplementedError
862
+
863
+ # --- Spans ---
864
+ @abstractmethod
865
+ async def create_span(self, span) -> None:
866
+ """Create a single span in the database.
867
+
868
+ Args:
869
+ span: The Span object to store.
870
+ """
871
+ raise NotImplementedError
872
+
873
+ @abstractmethod
874
+ async def create_spans(self, spans: List) -> None:
875
+ """Create multiple spans in the database as a batch.
876
+
877
+ Args:
878
+ spans: List of Span objects to store.
879
+ """
880
+ raise NotImplementedError
881
+
882
+ @abstractmethod
883
+ async def get_span(self, span_id: str):
884
+ """Get a single span by its span_id.
885
+
886
+ Args:
887
+ span_id: The unique span identifier.
888
+
889
+ Returns:
890
+ Optional[Span]: The span if found, None otherwise.
891
+ """
892
+ raise NotImplementedError
893
+
894
+ @abstractmethod
895
+ async def get_spans(
896
+ self,
897
+ trace_id: Optional[str] = None,
898
+ parent_span_id: Optional[str] = None,
899
+ limit: Optional[int] = 1000,
900
+ ) -> List:
901
+ """Get spans matching the provided filters.
902
+
903
+ Args:
904
+ trace_id: Filter by trace ID.
905
+ parent_span_id: Filter by parent span ID.
906
+ limit: Maximum number of spans to return.
907
+
908
+ Returns:
909
+ List[Span]: List of matching spans.
910
+ """
911
+ raise NotImplementedError
912
+
596
913
  # --- Cultural Notions ---
597
914
  @abstractmethod
598
915
  async def clear_cultural_knowledge(self) -> None:
@@ -603,22 +920,27 @@ class AsyncBaseDb(ABC):
603
920
  raise NotImplementedError
604
921
 
605
922
  @abstractmethod
606
- async def get_cultural_knowledge(self, id: str) -> Optional[CulturalKnowledge]:
923
+ async def get_cultural_knowledge(
924
+ self, id: str, deserialize: Optional[bool] = True
925
+ ) -> Optional[Union[CulturalKnowledge, Dict[str, Any]]]:
607
926
  raise NotImplementedError
608
927
 
609
928
  @abstractmethod
610
929
  async def get_all_cultural_knowledge(
611
930
  self,
931
+ agent_id: Optional[str] = None,
932
+ team_id: Optional[str] = None,
612
933
  name: Optional[str] = None,
613
934
  limit: Optional[int] = None,
614
935
  page: Optional[int] = None,
615
936
  sort_by: Optional[str] = None,
616
937
  sort_order: Optional[str] = None,
617
- agent_id: Optional[str] = None,
618
- team_id: Optional[str] = None,
619
- ) -> Optional[List[CulturalKnowledge]]:
938
+ deserialize: Optional[bool] = True,
939
+ ) -> Union[List[CulturalKnowledge], Tuple[List[Dict[str, Any]], int]]:
620
940
  raise NotImplementedError
621
941
 
622
942
  @abstractmethod
623
- async def upsert_cultural_knowledge(self, cultural_knowledge: CulturalKnowledge) -> Optional[CulturalKnowledge]:
943
+ async def upsert_cultural_knowledge(
944
+ self, cultural_knowledge: CulturalKnowledge, deserialize: Optional[bool] = True
945
+ ) -> Optional[Union[CulturalKnowledge, Dict[str, Any]]]:
624
946
  raise NotImplementedError