agno 2.3.24__py3-none-any.whl → 2.3.26__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 (70) hide show
  1. agno/agent/agent.py +357 -28
  2. agno/db/base.py +214 -0
  3. agno/db/dynamo/dynamo.py +47 -0
  4. agno/db/firestore/firestore.py +47 -0
  5. agno/db/gcs_json/gcs_json_db.py +47 -0
  6. agno/db/in_memory/in_memory_db.py +47 -0
  7. agno/db/json/json_db.py +47 -0
  8. agno/db/mongo/async_mongo.py +229 -0
  9. agno/db/mongo/mongo.py +47 -0
  10. agno/db/mongo/schemas.py +16 -0
  11. agno/db/mysql/async_mysql.py +47 -0
  12. agno/db/mysql/mysql.py +47 -0
  13. agno/db/postgres/async_postgres.py +231 -0
  14. agno/db/postgres/postgres.py +239 -0
  15. agno/db/postgres/schemas.py +19 -0
  16. agno/db/redis/redis.py +47 -0
  17. agno/db/singlestore/singlestore.py +47 -0
  18. agno/db/sqlite/async_sqlite.py +242 -0
  19. agno/db/sqlite/schemas.py +18 -0
  20. agno/db/sqlite/sqlite.py +239 -0
  21. agno/db/surrealdb/surrealdb.py +47 -0
  22. agno/knowledge/chunking/code.py +90 -0
  23. agno/knowledge/chunking/document.py +62 -2
  24. agno/knowledge/chunking/strategy.py +14 -0
  25. agno/knowledge/knowledge.py +7 -1
  26. agno/knowledge/reader/arxiv_reader.py +1 -0
  27. agno/knowledge/reader/csv_reader.py +1 -0
  28. agno/knowledge/reader/docx_reader.py +1 -0
  29. agno/knowledge/reader/firecrawl_reader.py +1 -0
  30. agno/knowledge/reader/json_reader.py +1 -0
  31. agno/knowledge/reader/markdown_reader.py +1 -0
  32. agno/knowledge/reader/pdf_reader.py +1 -0
  33. agno/knowledge/reader/pptx_reader.py +1 -0
  34. agno/knowledge/reader/s3_reader.py +1 -0
  35. agno/knowledge/reader/tavily_reader.py +1 -0
  36. agno/knowledge/reader/text_reader.py +1 -0
  37. agno/knowledge/reader/web_search_reader.py +1 -0
  38. agno/knowledge/reader/website_reader.py +1 -0
  39. agno/knowledge/reader/wikipedia_reader.py +1 -0
  40. agno/knowledge/reader/youtube_reader.py +1 -0
  41. agno/knowledge/utils.py +1 -0
  42. agno/learn/__init__.py +65 -0
  43. agno/learn/config.py +463 -0
  44. agno/learn/curate.py +185 -0
  45. agno/learn/machine.py +690 -0
  46. agno/learn/schemas.py +1043 -0
  47. agno/learn/stores/__init__.py +35 -0
  48. agno/learn/stores/entity_memory.py +3275 -0
  49. agno/learn/stores/learned_knowledge.py +1583 -0
  50. agno/learn/stores/protocol.py +117 -0
  51. agno/learn/stores/session_context.py +1217 -0
  52. agno/learn/stores/user_memory.py +1495 -0
  53. agno/learn/stores/user_profile.py +1220 -0
  54. agno/learn/utils.py +209 -0
  55. agno/models/base.py +59 -0
  56. agno/os/routers/agents/router.py +4 -4
  57. agno/os/routers/knowledge/knowledge.py +7 -0
  58. agno/os/routers/teams/router.py +3 -3
  59. agno/os/routers/workflows/router.py +5 -5
  60. agno/os/utils.py +55 -3
  61. agno/team/team.py +131 -0
  62. agno/tools/browserbase.py +78 -6
  63. agno/tools/google_bigquery.py +11 -2
  64. agno/utils/agent.py +30 -1
  65. agno/workflow/workflow.py +198 -0
  66. {agno-2.3.24.dist-info → agno-2.3.26.dist-info}/METADATA +24 -2
  67. {agno-2.3.24.dist-info → agno-2.3.26.dist-info}/RECORD +70 -56
  68. {agno-2.3.24.dist-info → agno-2.3.26.dist-info}/WHEEL +0 -0
  69. {agno-2.3.24.dist-info → agno-2.3.26.dist-info}/licenses/LICENSE +0 -0
  70. {agno-2.3.24.dist-info → agno-2.3.26.dist-info}/top_level.txt +0 -0
@@ -149,6 +149,7 @@ class AsyncMongoDb(AsyncBaseDb):
149
149
  culture_collection: Optional[str] = None,
150
150
  traces_collection: Optional[str] = None,
151
151
  spans_collection: Optional[str] = None,
152
+ learnings_collection: Optional[str] = None,
152
153
  id: Optional[str] = None,
153
154
  ):
154
155
  """
@@ -172,6 +173,7 @@ class AsyncMongoDb(AsyncBaseDb):
172
173
  culture_collection (Optional[str]): Name of the collection to store cultural knowledge.
173
174
  traces_collection (Optional[str]): Name of the collection to store traces.
174
175
  spans_collection (Optional[str]): Name of the collection to store spans.
176
+ learnings_collection (Optional[str]): Name of the collection to store learnings.
175
177
  id (Optional[str]): ID of the database.
176
178
 
177
179
  Raises:
@@ -194,6 +196,7 @@ class AsyncMongoDb(AsyncBaseDb):
194
196
  culture_table=culture_collection,
195
197
  traces_table=traces_collection,
196
198
  spans_table=spans_collection,
199
+ learnings_table=learnings_collection,
197
200
  )
198
201
 
199
202
  # Detect client type if provided
@@ -452,6 +455,17 @@ class AsyncMongoDb(AsyncBaseDb):
452
455
  )
453
456
  return self.spans_collection
454
457
 
458
+ if table_type == "learnings":
459
+ if reset_cache or not hasattr(self, "learnings_collection"):
460
+ if self.learnings_table_name is None:
461
+ raise ValueError("Learnings collection was not provided on initialization")
462
+ self.learnings_collection = await self._get_or_create_collection(
463
+ collection_name=self.learnings_table_name,
464
+ collection_type="learnings",
465
+ create_collection_if_not_found=create_collection_if_not_found,
466
+ )
467
+ return self.learnings_collection
468
+
455
469
  raise ValueError(f"Unknown table type: {table_type}")
456
470
 
457
471
  async def _get_or_create_collection(
@@ -2769,3 +2783,218 @@ class AsyncMongoDb(AsyncBaseDb):
2769
2783
  except Exception as e:
2770
2784
  log_error(f"Error getting spans: {e}")
2771
2785
  return []
2786
+
2787
+ # -- Learning methods --
2788
+ async def get_learning(
2789
+ self,
2790
+ learning_type: str,
2791
+ user_id: Optional[str] = None,
2792
+ agent_id: Optional[str] = None,
2793
+ team_id: Optional[str] = None,
2794
+ session_id: Optional[str] = None,
2795
+ namespace: Optional[str] = None,
2796
+ entity_id: Optional[str] = None,
2797
+ entity_type: Optional[str] = None,
2798
+ ) -> Optional[Dict[str, Any]]:
2799
+ """Retrieve a learning record.
2800
+
2801
+ Args:
2802
+ learning_type: Type of learning ('user_profile', 'session_context', etc.)
2803
+ user_id: Filter by user ID.
2804
+ agent_id: Filter by agent ID.
2805
+ team_id: Filter by team ID.
2806
+ session_id: Filter by session ID.
2807
+ namespace: Filter by namespace ('user', 'global', or custom).
2808
+ entity_id: Filter by entity ID (for entity-specific learnings).
2809
+ entity_type: Filter by entity type ('person', 'company', etc.).
2810
+
2811
+ Returns:
2812
+ Dict with 'content' key containing the learning data, or None.
2813
+ """
2814
+ try:
2815
+ collection = await self._get_collection(table_type="learnings", create_collection_if_not_found=False)
2816
+ if collection is None:
2817
+ return None
2818
+
2819
+ # Build query
2820
+ query: Dict[str, Any] = {"learning_type": learning_type}
2821
+ if user_id is not None:
2822
+ query["user_id"] = user_id
2823
+ if agent_id is not None:
2824
+ query["agent_id"] = agent_id
2825
+ if team_id is not None:
2826
+ query["team_id"] = team_id
2827
+ if session_id is not None:
2828
+ query["session_id"] = session_id
2829
+ if namespace is not None:
2830
+ query["namespace"] = namespace
2831
+ if entity_id is not None:
2832
+ query["entity_id"] = entity_id
2833
+ if entity_type is not None:
2834
+ query["entity_type"] = entity_type
2835
+
2836
+ result = await collection.find_one(query)
2837
+ if result is None:
2838
+ return None
2839
+
2840
+ # Remove MongoDB's _id field
2841
+ result.pop("_id", None)
2842
+ return {"content": result.get("content")}
2843
+
2844
+ except Exception as e:
2845
+ log_debug(f"Error retrieving learning: {e}")
2846
+ return None
2847
+
2848
+ async def upsert_learning(
2849
+ self,
2850
+ id: str,
2851
+ learning_type: str,
2852
+ content: Dict[str, Any],
2853
+ user_id: Optional[str] = None,
2854
+ agent_id: Optional[str] = None,
2855
+ team_id: Optional[str] = None,
2856
+ session_id: Optional[str] = None,
2857
+ namespace: Optional[str] = None,
2858
+ entity_id: Optional[str] = None,
2859
+ entity_type: Optional[str] = None,
2860
+ metadata: Optional[Dict[str, Any]] = None,
2861
+ ) -> None:
2862
+ """Insert or update a learning record.
2863
+
2864
+ Args:
2865
+ id: Unique identifier for the learning.
2866
+ learning_type: Type of learning ('user_profile', 'session_context', etc.)
2867
+ content: The learning content as a dict.
2868
+ user_id: Associated user ID.
2869
+ agent_id: Associated agent ID.
2870
+ team_id: Associated team ID.
2871
+ session_id: Associated session ID.
2872
+ namespace: Namespace for scoping ('user', 'global', or custom).
2873
+ entity_id: Associated entity ID (for entity-specific learnings).
2874
+ entity_type: Entity type ('person', 'company', etc.).
2875
+ metadata: Optional metadata.
2876
+ """
2877
+ try:
2878
+ collection = await self._get_collection(table_type="learnings", create_collection_if_not_found=True)
2879
+ if collection is None:
2880
+ return
2881
+
2882
+ current_time = int(time.time())
2883
+
2884
+ document = {
2885
+ "learning_id": id,
2886
+ "learning_type": learning_type,
2887
+ "namespace": namespace,
2888
+ "user_id": user_id,
2889
+ "agent_id": agent_id,
2890
+ "team_id": team_id,
2891
+ "session_id": session_id,
2892
+ "entity_id": entity_id,
2893
+ "entity_type": entity_type,
2894
+ "content": content,
2895
+ "metadata": metadata,
2896
+ "updated_at": current_time,
2897
+ }
2898
+
2899
+ # Use upsert to insert or update
2900
+ await collection.update_one(
2901
+ {"learning_id": id},
2902
+ {"$set": document, "$setOnInsert": {"created_at": current_time}},
2903
+ upsert=True,
2904
+ )
2905
+
2906
+ log_debug(f"Upserted learning: {id}")
2907
+
2908
+ except Exception as e:
2909
+ log_debug(f"Error upserting learning: {e}")
2910
+
2911
+ async def delete_learning(self, id: str) -> bool:
2912
+ """Delete a learning record.
2913
+
2914
+ Args:
2915
+ id: The learning ID to delete.
2916
+
2917
+ Returns:
2918
+ True if deleted, False otherwise.
2919
+ """
2920
+ try:
2921
+ collection = await self._get_collection(table_type="learnings", create_collection_if_not_found=False)
2922
+ if collection is None:
2923
+ return False
2924
+
2925
+ result = await collection.delete_one({"learning_id": id})
2926
+ return result.deleted_count > 0
2927
+
2928
+ except Exception as e:
2929
+ log_debug(f"Error deleting learning: {e}")
2930
+ return False
2931
+
2932
+ async def get_learnings(
2933
+ self,
2934
+ learning_type: Optional[str] = None,
2935
+ user_id: Optional[str] = None,
2936
+ agent_id: Optional[str] = None,
2937
+ team_id: Optional[str] = None,
2938
+ session_id: Optional[str] = None,
2939
+ namespace: Optional[str] = None,
2940
+ entity_id: Optional[str] = None,
2941
+ entity_type: Optional[str] = None,
2942
+ limit: Optional[int] = None,
2943
+ ) -> List[Dict[str, Any]]:
2944
+ """Get multiple learning records.
2945
+
2946
+ Args:
2947
+ learning_type: Filter by learning type.
2948
+ user_id: Filter by user ID.
2949
+ agent_id: Filter by agent ID.
2950
+ team_id: Filter by team ID.
2951
+ session_id: Filter by session ID.
2952
+ namespace: Filter by namespace ('user', 'global', or custom).
2953
+ entity_id: Filter by entity ID (for entity-specific learnings).
2954
+ entity_type: Filter by entity type ('person', 'company', etc.).
2955
+ limit: Maximum number of records to return.
2956
+
2957
+ Returns:
2958
+ List of learning records.
2959
+ """
2960
+ try:
2961
+ collection = await self._get_collection(table_type="learnings", create_collection_if_not_found=False)
2962
+ if collection is None:
2963
+ return []
2964
+
2965
+ # Build query
2966
+ query: Dict[str, Any] = {}
2967
+ if learning_type is not None:
2968
+ query["learning_type"] = learning_type
2969
+ if user_id is not None:
2970
+ query["user_id"] = user_id
2971
+ if agent_id is not None:
2972
+ query["agent_id"] = agent_id
2973
+ if team_id is not None:
2974
+ query["team_id"] = team_id
2975
+ if session_id is not None:
2976
+ query["session_id"] = session_id
2977
+ if namespace is not None:
2978
+ query["namespace"] = namespace
2979
+ if entity_id is not None:
2980
+ query["entity_id"] = entity_id
2981
+ if entity_type is not None:
2982
+ query["entity_type"] = entity_type
2983
+
2984
+ cursor = collection.find(query)
2985
+ if limit is not None:
2986
+ cursor = cursor.limit(limit)
2987
+
2988
+ results = await cursor.to_list(length=None)
2989
+
2990
+ learnings = []
2991
+ for row in results:
2992
+ # Remove MongoDB's _id field
2993
+ row.pop("_id", None)
2994
+ learnings.append(row)
2995
+
2996
+ return learnings
2997
+
2998
+ except Exception as e:
2999
+ log_debug(f"Error getting learnings: {e}")
3000
+ return []
agno/db/mongo/mongo.py CHANGED
@@ -2613,3 +2613,50 @@ class MongoDb(BaseDb):
2613
2613
  except Exception as e:
2614
2614
  log_error(f"Error getting spans: {e}")
2615
2615
  return []
2616
+
2617
+ # -- Learning methods (stubs) --
2618
+ def get_learning(
2619
+ self,
2620
+ learning_type: str,
2621
+ user_id: Optional[str] = None,
2622
+ agent_id: Optional[str] = None,
2623
+ team_id: Optional[str] = None,
2624
+ session_id: Optional[str] = None,
2625
+ namespace: Optional[str] = None,
2626
+ entity_id: Optional[str] = None,
2627
+ entity_type: Optional[str] = None,
2628
+ ) -> Optional[Dict[str, Any]]:
2629
+ raise NotImplementedError("Learning methods not yet implemented for MongoDb")
2630
+
2631
+ def upsert_learning(
2632
+ self,
2633
+ id: str,
2634
+ learning_type: str,
2635
+ content: Dict[str, Any],
2636
+ user_id: Optional[str] = None,
2637
+ agent_id: Optional[str] = None,
2638
+ team_id: Optional[str] = None,
2639
+ session_id: Optional[str] = None,
2640
+ namespace: Optional[str] = None,
2641
+ entity_id: Optional[str] = None,
2642
+ entity_type: Optional[str] = None,
2643
+ metadata: Optional[Dict[str, Any]] = None,
2644
+ ) -> None:
2645
+ raise NotImplementedError("Learning methods not yet implemented for MongoDb")
2646
+
2647
+ def delete_learning(self, id: str) -> bool:
2648
+ raise NotImplementedError("Learning methods not yet implemented for MongoDb")
2649
+
2650
+ def get_learnings(
2651
+ self,
2652
+ learning_type: Optional[str] = None,
2653
+ user_id: Optional[str] = None,
2654
+ agent_id: Optional[str] = None,
2655
+ team_id: Optional[str] = None,
2656
+ session_id: Optional[str] = None,
2657
+ namespace: Optional[str] = None,
2658
+ entity_id: Optional[str] = None,
2659
+ entity_type: Optional[str] = None,
2660
+ limit: Optional[int] = None,
2661
+ ) -> List[Dict[str, Any]]:
2662
+ raise NotImplementedError("Learning methods not yet implemented for MongoDb")
agno/db/mongo/schemas.py CHANGED
@@ -98,6 +98,21 @@ SPAN_COLLECTION_SCHEMA = [
98
98
  {"key": "created_at"},
99
99
  ]
100
100
 
101
+ LEARNINGS_COLLECTION_SCHEMA = [
102
+ {"key": "learning_id", "unique": True},
103
+ {"key": "learning_type"},
104
+ {"key": "namespace"},
105
+ {"key": "user_id"},
106
+ {"key": "agent_id"},
107
+ {"key": "team_id"},
108
+ {"key": "workflow_id"},
109
+ {"key": "session_id"},
110
+ {"key": "entity_id"},
111
+ {"key": "entity_type"},
112
+ {"key": "created_at"},
113
+ {"key": "updated_at"},
114
+ ]
115
+
101
116
 
102
117
  def get_collection_indexes(collection_type: str) -> List[Dict[str, Any]]:
103
118
  """Get the index definitions for a specific collection type."""
@@ -110,6 +125,7 @@ def get_collection_indexes(collection_type: str) -> List[Dict[str, Any]]:
110
125
  "culture": CULTURAL_KNOWLEDGE_COLLECTION_SCHEMA,
111
126
  "traces": TRACE_COLLECTION_SCHEMA,
112
127
  "spans": SPAN_COLLECTION_SCHEMA,
128
+ "learnings": LEARNINGS_COLLECTION_SCHEMA,
113
129
  }
114
130
 
115
131
  indexes = index_definitions.get(collection_type)
@@ -2917,3 +2917,50 @@ class AsyncMySQLDb(AsyncBaseDb):
2917
2917
  except Exception as e:
2918
2918
  log_error(f"Error getting spans: {e}")
2919
2919
  return []
2920
+
2921
+ # -- Learning methods (stubs) --
2922
+ async def get_learning(
2923
+ self,
2924
+ learning_type: str,
2925
+ user_id: Optional[str] = None,
2926
+ agent_id: Optional[str] = None,
2927
+ team_id: Optional[str] = None,
2928
+ session_id: Optional[str] = None,
2929
+ namespace: Optional[str] = None,
2930
+ entity_id: Optional[str] = None,
2931
+ entity_type: Optional[str] = None,
2932
+ ) -> Optional[Dict[str, Any]]:
2933
+ raise NotImplementedError("Learning methods not yet implemented for AsyncMySQLDb")
2934
+
2935
+ async def upsert_learning(
2936
+ self,
2937
+ id: str,
2938
+ learning_type: str,
2939
+ content: Dict[str, Any],
2940
+ user_id: Optional[str] = None,
2941
+ agent_id: Optional[str] = None,
2942
+ team_id: Optional[str] = None,
2943
+ session_id: Optional[str] = None,
2944
+ namespace: Optional[str] = None,
2945
+ entity_id: Optional[str] = None,
2946
+ entity_type: Optional[str] = None,
2947
+ metadata: Optional[Dict[str, Any]] = None,
2948
+ ) -> None:
2949
+ raise NotImplementedError("Learning methods not yet implemented for AsyncMySQLDb")
2950
+
2951
+ async def delete_learning(self, id: str) -> bool:
2952
+ raise NotImplementedError("Learning methods not yet implemented for AsyncMySQLDb")
2953
+
2954
+ async def get_learnings(
2955
+ self,
2956
+ learning_type: Optional[str] = None,
2957
+ user_id: Optional[str] = None,
2958
+ agent_id: Optional[str] = None,
2959
+ team_id: Optional[str] = None,
2960
+ session_id: Optional[str] = None,
2961
+ namespace: Optional[str] = None,
2962
+ entity_id: Optional[str] = None,
2963
+ entity_type: Optional[str] = None,
2964
+ limit: Optional[int] = None,
2965
+ ) -> List[Dict[str, Any]]:
2966
+ raise NotImplementedError("Learning methods not yet implemented for AsyncMySQLDb")
agno/db/mysql/mysql.py CHANGED
@@ -2928,3 +2928,50 @@ class MySQLDb(BaseDb):
2928
2928
  except Exception as e:
2929
2929
  log_error(f"Error getting spans: {e}")
2930
2930
  return []
2931
+
2932
+ # -- Learning methods (stubs) --
2933
+ def get_learning(
2934
+ self,
2935
+ learning_type: str,
2936
+ user_id: Optional[str] = None,
2937
+ agent_id: Optional[str] = None,
2938
+ team_id: Optional[str] = None,
2939
+ session_id: Optional[str] = None,
2940
+ namespace: Optional[str] = None,
2941
+ entity_id: Optional[str] = None,
2942
+ entity_type: Optional[str] = None,
2943
+ ) -> Optional[Dict[str, Any]]:
2944
+ raise NotImplementedError("Learning methods not yet implemented for MySQLDb")
2945
+
2946
+ def upsert_learning(
2947
+ self,
2948
+ id: str,
2949
+ learning_type: str,
2950
+ content: Dict[str, Any],
2951
+ user_id: Optional[str] = None,
2952
+ agent_id: Optional[str] = None,
2953
+ team_id: Optional[str] = None,
2954
+ session_id: Optional[str] = None,
2955
+ namespace: Optional[str] = None,
2956
+ entity_id: Optional[str] = None,
2957
+ entity_type: Optional[str] = None,
2958
+ metadata: Optional[Dict[str, Any]] = None,
2959
+ ) -> None:
2960
+ raise NotImplementedError("Learning methods not yet implemented for MySQLDb")
2961
+
2962
+ def delete_learning(self, id: str) -> bool:
2963
+ raise NotImplementedError("Learning methods not yet implemented for MySQLDb")
2964
+
2965
+ def get_learnings(
2966
+ self,
2967
+ learning_type: Optional[str] = None,
2968
+ user_id: Optional[str] = None,
2969
+ agent_id: Optional[str] = None,
2970
+ team_id: Optional[str] = None,
2971
+ session_id: Optional[str] = None,
2972
+ namespace: Optional[str] = None,
2973
+ entity_id: Optional[str] = None,
2974
+ entity_type: Optional[str] = None,
2975
+ limit: Optional[int] = None,
2976
+ ) -> List[Dict[str, Any]]:
2977
+ raise NotImplementedError("Learning methods not yet implemented for MySQLDb")