agno 2.0.4__py3-none-any.whl → 2.0.6__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 (76) hide show
  1. agno/agent/agent.py +127 -102
  2. agno/db/dynamo/dynamo.py +9 -7
  3. agno/db/firestore/firestore.py +7 -4
  4. agno/db/gcs_json/gcs_json_db.py +6 -4
  5. agno/db/json/json_db.py +10 -6
  6. agno/db/migrations/v1_to_v2.py +191 -23
  7. agno/db/mongo/mongo.py +67 -6
  8. agno/db/mysql/mysql.py +7 -6
  9. agno/db/mysql/schemas.py +27 -27
  10. agno/db/postgres/postgres.py +7 -6
  11. agno/db/redis/redis.py +3 -3
  12. agno/db/singlestore/singlestore.py +4 -4
  13. agno/db/sqlite/sqlite.py +7 -6
  14. agno/db/utils.py +0 -14
  15. agno/integrations/discord/client.py +1 -0
  16. agno/knowledge/embedder/openai.py +19 -11
  17. agno/knowledge/knowledge.py +11 -10
  18. agno/knowledge/reader/reader_factory.py +7 -3
  19. agno/knowledge/reader/web_search_reader.py +12 -6
  20. agno/knowledge/reader/website_reader.py +33 -16
  21. agno/media.py +70 -0
  22. agno/models/aimlapi/aimlapi.py +2 -2
  23. agno/models/base.py +31 -4
  24. agno/models/cerebras/cerebras_openai.py +2 -2
  25. agno/models/deepinfra/deepinfra.py +2 -2
  26. agno/models/deepseek/deepseek.py +2 -2
  27. agno/models/fireworks/fireworks.py +2 -2
  28. agno/models/internlm/internlm.py +2 -2
  29. agno/models/langdb/langdb.py +4 -4
  30. agno/models/litellm/litellm_openai.py +2 -2
  31. agno/models/message.py +135 -0
  32. agno/models/meta/llama_openai.py +2 -2
  33. agno/models/nebius/nebius.py +2 -2
  34. agno/models/nexus/__init__.py +3 -0
  35. agno/models/nexus/nexus.py +25 -0
  36. agno/models/nvidia/nvidia.py +2 -2
  37. agno/models/openai/responses.py +6 -0
  38. agno/models/openrouter/openrouter.py +2 -2
  39. agno/models/perplexity/perplexity.py +2 -2
  40. agno/models/portkey/portkey.py +3 -3
  41. agno/models/response.py +2 -1
  42. agno/models/sambanova/sambanova.py +2 -2
  43. agno/models/together/together.py +2 -2
  44. agno/models/vercel/v0.py +2 -2
  45. agno/models/xai/xai.py +2 -2
  46. agno/os/app.py +162 -42
  47. agno/os/interfaces/agui/utils.py +98 -134
  48. agno/os/router.py +3 -1
  49. agno/os/routers/health.py +0 -1
  50. agno/os/routers/home.py +52 -0
  51. agno/os/routers/knowledge/knowledge.py +2 -2
  52. agno/os/schema.py +21 -0
  53. agno/os/utils.py +1 -9
  54. agno/run/agent.py +19 -3
  55. agno/run/team.py +18 -3
  56. agno/run/workflow.py +10 -0
  57. agno/team/team.py +70 -45
  58. agno/tools/duckduckgo.py +15 -11
  59. agno/tools/e2b.py +14 -7
  60. agno/tools/file_generation.py +350 -0
  61. agno/tools/function.py +2 -0
  62. agno/tools/googlesearch.py +1 -1
  63. agno/utils/gemini.py +24 -4
  64. agno/utils/string.py +32 -0
  65. agno/utils/tools.py +1 -1
  66. agno/vectordb/chroma/chromadb.py +66 -25
  67. agno/vectordb/lancedb/lance_db.py +15 -4
  68. agno/vectordb/milvus/milvus.py +6 -0
  69. agno/workflow/step.py +4 -3
  70. agno/workflow/workflow.py +4 -0
  71. {agno-2.0.4.dist-info → agno-2.0.6.dist-info}/METADATA +9 -5
  72. {agno-2.0.4.dist-info → agno-2.0.6.dist-info}/RECORD +75 -72
  73. agno/knowledge/reader/url_reader.py +0 -128
  74. {agno-2.0.4.dist-info → agno-2.0.6.dist-info}/WHEEL +0 -0
  75. {agno-2.0.4.dist-info → agno-2.0.6.dist-info}/licenses/LICENSE +0 -0
  76. {agno-2.0.4.dist-info → agno-2.0.6.dist-info}/top_level.txt +0 -0
agno/db/json/json_db.py CHANGED
@@ -17,9 +17,9 @@ from agno.db.json.utils import (
17
17
  from agno.db.schemas.evals import EvalFilterType, EvalRunRecord, EvalType
18
18
  from agno.db.schemas.knowledge import KnowledgeRow
19
19
  from agno.db.schemas.memory import UserMemory
20
- from agno.db.utils import generate_deterministic_id
21
20
  from agno.session import AgentSession, Session, TeamSession, WorkflowSession
22
21
  from agno.utils.log import log_debug, log_error, log_info, log_warning
22
+ from agno.utils.string import generate_id
23
23
 
24
24
 
25
25
  class JsonDb(BaseDb):
@@ -47,7 +47,7 @@ class JsonDb(BaseDb):
47
47
  """
48
48
  if id is None:
49
49
  seed = db_path or "agno_json_db"
50
- id = generate_deterministic_id(seed)
50
+ id = generate_id(seed)
51
51
 
52
52
  super().__init__(
53
53
  id=id,
@@ -168,7 +168,7 @@ class JsonDb(BaseDb):
168
168
  def get_session(
169
169
  self,
170
170
  session_id: str,
171
- session_type: Optional[SessionType] = None,
171
+ session_type: SessionType,
172
172
  user_id: Optional[str] = None,
173
173
  deserialize: Optional[bool] = True,
174
174
  ) -> Optional[Union[AgentSession, TeamSession, WorkflowSession, Dict[str, Any]]]:
@@ -176,7 +176,7 @@ class JsonDb(BaseDb):
176
176
 
177
177
  Args:
178
178
  session_id (str): The ID of the session to read.
179
- session_type (Optional[SessionType]): The type of the session to read.
179
+ session_type (SessionType): The type of the session to read.
180
180
  user_id (Optional[str]): The ID of the user to read the session for.
181
181
  deserialize (Optional[bool]): Whether to deserialize the session.
182
182
 
@@ -208,8 +208,10 @@ class JsonDb(BaseDb):
208
208
  return AgentSession.from_dict(session)
209
209
  elif session_type == SessionType.TEAM:
210
210
  return TeamSession.from_dict(session)
211
- else:
211
+ elif session_type == SessionType.WORKFLOW:
212
212
  return WorkflowSession.from_dict(session)
213
+ else:
214
+ raise ValueError(f"Invalid session type: {session_type}")
213
215
 
214
216
  return None
215
217
 
@@ -338,8 +340,10 @@ class JsonDb(BaseDb):
338
340
  return AgentSession.from_dict(session)
339
341
  elif session_type == SessionType.TEAM:
340
342
  return TeamSession.from_dict(session)
341
- else:
343
+ elif session_type == SessionType.WORKFLOW:
342
344
  return WorkflowSession.from_dict(session)
345
+ else:
346
+ raise ValueError(f"Invalid session type: {session_type}")
343
347
 
344
348
  return None
345
349
 
@@ -1,9 +1,11 @@
1
1
  """Migration utility to migrate your Agno tables from v1 to v2"""
2
2
 
3
+ import json
3
4
  from typing import Any, Dict, List, Optional, Union
4
5
 
5
6
  from sqlalchemy import text
6
7
 
8
+ from agno.db.mongo.mongo import MongoDb
7
9
  from agno.db.mysql.mysql import MySQLDb
8
10
  from agno.db.postgres.postgres import PostgresDb
9
11
  from agno.db.schemas.memory import UserMemory
@@ -12,24 +14,171 @@ from agno.session import AgentSession, TeamSession, WorkflowSession
12
14
  from agno.utils.log import log_error
13
15
 
14
16
 
17
+ def convert_v1_metrics_to_v2(metrics_dict: Dict[str, Any]) -> Dict[str, Any]:
18
+ """Convert v1 metrics dictionary to v2 format by mapping old field names to new ones."""
19
+ if not isinstance(metrics_dict, dict):
20
+ return metrics_dict
21
+
22
+ # Create a copy to avoid modifying the original
23
+ v2_metrics = metrics_dict.copy()
24
+
25
+ # Map v1 field names to v2 field names
26
+ field_mappings = {
27
+ "time": "duration",
28
+ "audio_tokens": "audio_total_tokens",
29
+ "input_audio_tokens": "audio_input_tokens",
30
+ "output_audio_tokens": "audio_output_tokens",
31
+ "cached_tokens": "cache_read_tokens",
32
+ }
33
+
34
+ # Fields to remove (deprecated in v2)
35
+ deprecated_fields = ["prompt_tokens", "completion_tokens", "prompt_tokens_details", "completion_tokens_details"]
36
+
37
+ # Apply field mappings
38
+ for old_field, new_field in field_mappings.items():
39
+ if old_field in v2_metrics:
40
+ v2_metrics[new_field] = v2_metrics.pop(old_field)
41
+
42
+ # Remove deprecated fields
43
+ for field in deprecated_fields:
44
+ v2_metrics.pop(field, None)
45
+
46
+ return v2_metrics
47
+
48
+
49
+ def convert_any_metrics_in_data(data: Any) -> Any:
50
+ """Recursively find and convert any metrics dictionaries in the data structure."""
51
+ if isinstance(data, dict):
52
+ # First filter out deprecated v1 fields
53
+ data = filter_deprecated_v1_fields(data)
54
+
55
+ # Check if this looks like a metrics dictionary
56
+ if _is_metrics_dict(data):
57
+ return convert_v1_metrics_to_v2(data)
58
+
59
+ # Otherwise, recursively process all values
60
+ converted_dict = {}
61
+ for key, value in data.items():
62
+ # Special handling for 'metrics' keys - always convert their values
63
+ if key == "metrics" and isinstance(value, dict):
64
+ converted_dict[key] = convert_v1_metrics_to_v2(value)
65
+ else:
66
+ converted_dict[key] = convert_any_metrics_in_data(value)
67
+ return converted_dict
68
+
69
+ elif isinstance(data, list):
70
+ return [convert_any_metrics_in_data(item) for item in data]
71
+
72
+ else:
73
+ # Not a dict or list, return as-is
74
+ return data
75
+
76
+
77
+ def _is_metrics_dict(data: Dict[str, Any]) -> bool:
78
+ """Check if a dictionary looks like a metrics dictionary based on common field names."""
79
+ if not isinstance(data, dict):
80
+ return False
81
+
82
+ # Common metrics field names (both v1 and v2)
83
+ metrics_indicators = {
84
+ "input_tokens",
85
+ "output_tokens",
86
+ "total_tokens",
87
+ "time",
88
+ "duration",
89
+ "audio_tokens",
90
+ "audio_total_tokens",
91
+ "audio_input_tokens",
92
+ "audio_output_tokens",
93
+ "cached_tokens",
94
+ "cache_read_tokens",
95
+ "cache_write_tokens",
96
+ "reasoning_tokens",
97
+ "prompt_tokens",
98
+ "completion_tokens",
99
+ "time_to_first_token",
100
+ "provider_metrics",
101
+ "additional_metrics",
102
+ }
103
+
104
+ # Deprecated v1 fields that are strong indicators this is a metrics dict
105
+ deprecated_v1_indicators = {"time", "audio_tokens", "cached_tokens", "prompt_tokens", "completion_tokens"}
106
+
107
+ # If we find any deprecated v1 field, it's definitely a metrics dict that needs conversion
108
+ if any(field in data for field in deprecated_v1_indicators):
109
+ return True
110
+
111
+ # Otherwise, if the dict has at least 2 metrics-related fields, consider it a metrics dict
112
+ matching_fields = sum(1 for field in data.keys() if field in metrics_indicators)
113
+ return matching_fields >= 2
114
+
115
+
116
+ def convert_session_data_comprehensively(session_data: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
117
+ """Comprehensively convert any metrics found anywhere in session_data from v1 to v2 format."""
118
+ if not session_data:
119
+ return session_data
120
+
121
+ # Use the recursive converter to find and fix all metrics
122
+ return convert_any_metrics_in_data(session_data)
123
+
124
+
125
+ def safe_get_runs_from_memory(memory_data: Any) -> Any:
126
+ """Safely extract runs data from memory field, handling various data types."""
127
+ if memory_data is None:
128
+ return None
129
+
130
+ # If memory_data is a string, try to parse it as JSON
131
+ if isinstance(memory_data, str):
132
+ try:
133
+ memory_dict = json.loads(memory_data)
134
+ if isinstance(memory_dict, dict):
135
+ return memory_dict.get("runs")
136
+ except (json.JSONDecodeError, AttributeError):
137
+ # If JSON parsing fails, memory_data might just be a string value
138
+ return None
139
+
140
+ # If memory_data is already a dict, access runs directly
141
+ elif isinstance(memory_data, dict):
142
+ return memory_data.get("runs")
143
+
144
+ # For any other type, return None
145
+ return None
146
+
147
+
148
+ def filter_deprecated_v1_fields(data: Dict[str, Any]) -> Dict[str, Any]:
149
+ """Remove v1-only fields that don't exist in v2 models."""
150
+ if not isinstance(data, dict):
151
+ return data
152
+
153
+ # Fields that existed in v1 but were removed in v2
154
+ deprecated_fields = {
155
+ "team_session_id", # RunOutput v1 field, removed in v2
156
+ "formatted_tool_calls", # RunOutput v1 field, removed in v2
157
+ # Add other deprecated fields here as needed
158
+ }
159
+
160
+ # Create a copy and remove deprecated fields
161
+ filtered_data = {k: v for k, v in data.items() if k not in deprecated_fields}
162
+ return filtered_data
163
+
164
+
15
165
  def migrate(
16
- db: Union[PostgresDb, MySQLDb, SqliteDb],
166
+ db: Union[PostgresDb, MySQLDb, SqliteDb, MongoDb],
17
167
  v1_db_schema: str,
18
168
  agent_sessions_table_name: Optional[str] = None,
19
169
  team_sessions_table_name: Optional[str] = None,
20
170
  workflow_sessions_table_name: Optional[str] = None,
21
171
  memories_table_name: Optional[str] = None,
22
172
  ):
23
- """Given a PostgresDb and table names, parse and migrate the tables' content to the corresponding v2 tables.
173
+ """Given a database connection and table/collection names, parse and migrate the content to corresponding v2 tables/collections.
24
174
 
25
175
  Args:
26
- db: The database to migrate
27
- v1_db_schema: The schema of the v1 tables
28
- agent_sessions_table_name: The name of the agent sessions table. If not provided, the agent sessions table will not be migrated.
29
- team_sessions_table_name: The name of the team sessions table. If not provided, the team sessions table will not be migrated.
30
- workflow_sessions_table_name: The name of the workflow sessions table. If not provided, the workflow sessions table will not be migrated.
31
- workflow_v2_sessions_table_name: The name of the workflow v2 sessions table. If not provided, the workflow v2 sessions table will not be migrated.
32
- memories_table_name: The name of the memories table. If not provided, the memories table will not be migrated.
176
+ db: The database to migrate (PostgresDb, MySQLDb, SqliteDb, or MongoDb)
177
+ v1_db_schema: The schema of the v1 tables (leave empty for SQLite and MongoDB)
178
+ agent_sessions_table_name: The name of the agent sessions table/collection. If not provided, agent sessions will not be migrated.
179
+ team_sessions_table_name: The name of the team sessions table/collection. If not provided, team sessions will not be migrated.
180
+ workflow_sessions_table_name: The name of the workflow sessions table/collection. If not provided, workflow sessions will not be migrated.
181
+ memories_table_name: The name of the memories table/collection. If not provided, memories will not be migrated.
33
182
  """
34
183
  if agent_sessions_table_name:
35
184
  db.migrate_table_from_v1_to_v2(
@@ -61,14 +210,33 @@ def migrate(
61
210
 
62
211
 
63
212
  def get_all_table_content(db, db_schema: str, table_name: str) -> list[dict[str, Any]]:
64
- """Get all content from the given table"""
213
+ """Get all content from the given table/collection"""
65
214
  try:
66
- with db.Session() as sess:
67
- result = sess.execute(text(f"SELECT * FROM {db_schema}.{table_name}"))
68
- return [row._asdict() for row in result]
215
+ # Check if this is a MongoDB instance
216
+ if hasattr(db, "database") and hasattr(db, "db_client"):
217
+ # MongoDB implementation
218
+ collection = db.database[table_name]
219
+ # Convert MongoDB documents to dictionaries and handle ObjectId
220
+ documents = list(collection.find({}))
221
+ # Convert ObjectId to string for compatibility
222
+ for doc in documents:
223
+ if "_id" in doc:
224
+ doc["_id"] = str(doc["_id"])
225
+ return documents
226
+ else:
227
+ # SQL database implementation (PostgreSQL, MySQL, SQLite)
228
+ with db.Session() as sess:
229
+ # Handle empty schema by omitting the schema prefix (needed for SQLite)
230
+ if db_schema and db_schema.strip():
231
+ sql_query = f"SELECT * FROM {db_schema}.{table_name}"
232
+ else:
233
+ sql_query = f"SELECT * FROM {table_name}"
234
+
235
+ result = sess.execute(text(sql_query))
236
+ return [row._asdict() for row in result]
69
237
 
70
238
  except Exception as e:
71
- log_error(f"Error getting all content from table {table_name}: {e}")
239
+ log_error(f"Error getting all content from table/collection {table_name}: {e}")
72
240
  return []
73
241
 
74
242
 
@@ -82,9 +250,9 @@ def parse_agent_sessions(v1_content: List[Dict[str, Any]]) -> List[AgentSession]
82
250
  "agent_data": item.get("agent_data"),
83
251
  "session_id": item.get("session_id"),
84
252
  "user_id": item.get("user_id"),
85
- "session_data": item.get("session_data"),
86
- "metadata": item.get("extra_data"),
87
- "runs": item.get("memory", {}).get("runs"),
253
+ "session_data": convert_session_data_comprehensively(item.get("session_data")),
254
+ "metadata": convert_any_metrics_in_data(item.get("extra_data")),
255
+ "runs": convert_any_metrics_in_data(safe_get_runs_from_memory(item.get("memory"))),
88
256
  "created_at": item.get("created_at"),
89
257
  "updated_at": item.get("updated_at"),
90
258
  }
@@ -105,9 +273,9 @@ def parse_team_sessions(v1_content: List[Dict[str, Any]]) -> List[TeamSession]:
105
273
  "team_data": item.get("team_data"),
106
274
  "session_id": item.get("session_id"),
107
275
  "user_id": item.get("user_id"),
108
- "session_data": item.get("session_data"),
109
- "metadata": item.get("extra_data"),
110
- "runs": item.get("memory", {}).get("runs"),
276
+ "session_data": convert_session_data_comprehensively(item.get("session_data")),
277
+ "metadata": convert_any_metrics_in_data(item.get("extra_data")),
278
+ "runs": convert_any_metrics_in_data(safe_get_runs_from_memory(item.get("memory"))),
111
279
  "created_at": item.get("created_at"),
112
280
  "updated_at": item.get("updated_at"),
113
281
  }
@@ -128,13 +296,13 @@ def parse_workflow_sessions(v1_content: List[Dict[str, Any]]) -> List[WorkflowSe
128
296
  "workflow_data": item.get("workflow_data"),
129
297
  "session_id": item.get("session_id"),
130
298
  "user_id": item.get("user_id"),
131
- "session_data": item.get("session_data"),
132
- "metadata": item.get("extra_data"),
299
+ "session_data": convert_session_data_comprehensively(item.get("session_data")),
300
+ "metadata": convert_any_metrics_in_data(item.get("extra_data")),
133
301
  "created_at": item.get("created_at"),
134
302
  "updated_at": item.get("updated_at"),
135
303
  # Workflow v2 specific fields
136
304
  "workflow_name": item.get("workflow_name"),
137
- "runs": item.get("runs"),
305
+ "runs": convert_any_metrics_in_data(item.get("runs")),
138
306
  }
139
307
  workflow_session = WorkflowSession.from_dict(session)
140
308
  if workflow_session is not None:
agno/db/mongo/mongo.py CHANGED
@@ -16,9 +16,10 @@ from agno.db.mongo.utils import (
16
16
  from agno.db.schemas.evals import EvalFilterType, EvalRunRecord, EvalType
17
17
  from agno.db.schemas.knowledge import KnowledgeRow
18
18
  from agno.db.schemas.memory import UserMemory
19
- from agno.db.utils import deserialize_session_json_fields, generate_deterministic_id, serialize_session_json_fields
19
+ from agno.db.utils import deserialize_session_json_fields, serialize_session_json_fields
20
20
  from agno.session import AgentSession, Session, TeamSession, WorkflowSession
21
21
  from agno.utils.log import log_debug, log_error, log_info
22
+ from agno.utils.string import generate_id
22
23
 
23
24
  try:
24
25
  from pymongo import MongoClient, ReturnDocument
@@ -63,7 +64,7 @@ class MongoDb(BaseDb):
63
64
  base_seed = db_url or str(db_client)
64
65
  db_name_suffix = db_name if db_name is not None else "agno"
65
66
  seed = f"{base_seed}#{db_name_suffix}"
66
- id = generate_deterministic_id(seed)
67
+ id = generate_id(seed)
67
68
 
68
69
  super().__init__(
69
70
  id=id,
@@ -252,8 +253,8 @@ class MongoDb(BaseDb):
252
253
 
253
254
  Args:
254
255
  session_id (str): The ID of the session to get.
256
+ session_type (SessionType): The type of session to get.
255
257
  user_id (Optional[str]): The ID of the user to get the session for.
256
- session_type (Optional[SessionType]): The type of session to get.
257
258
  deserialize (Optional[bool]): Whether to serialize the session. Defaults to True.
258
259
 
259
260
  Returns:
@@ -284,12 +285,14 @@ class MongoDb(BaseDb):
284
285
  if not deserialize:
285
286
  return session
286
287
 
287
- if session_type == SessionType.AGENT.value:
288
+ if session_type == SessionType.AGENT:
288
289
  return AgentSession.from_dict(session)
289
- elif session_type == SessionType.TEAM.value:
290
+ elif session_type == SessionType.TEAM:
290
291
  return TeamSession.from_dict(session)
291
- else:
292
+ elif session_type == SessionType.WORKFLOW:
292
293
  return WorkflowSession.from_dict(session)
294
+ else:
295
+ raise ValueError(f"Invalid session type: {session_type}")
293
296
 
294
297
  except Exception as e:
295
298
  log_error(f"Exception reading session: {e}")
@@ -1423,3 +1426,61 @@ class MongoDb(BaseDb):
1423
1426
  except Exception as e:
1424
1427
  log_error(f"Error updating eval run name {eval_run_id}: {e}")
1425
1428
  raise
1429
+
1430
+ def migrate_table_from_v1_to_v2(self, v1_db_schema: str, v1_table_name: str, v1_table_type: str):
1431
+ """Migrate all content in the given collection to the right v2 collection"""
1432
+
1433
+ from typing import List, Sequence, Union
1434
+
1435
+ from agno.db.migrations.v1_to_v2 import (
1436
+ get_all_table_content,
1437
+ parse_agent_sessions,
1438
+ parse_memories,
1439
+ parse_team_sessions,
1440
+ parse_workflow_sessions,
1441
+ )
1442
+
1443
+ # Get all content from the old collection
1444
+ old_content: list[dict[str, Any]] = get_all_table_content(
1445
+ db=self,
1446
+ db_schema=v1_db_schema,
1447
+ table_name=v1_table_name,
1448
+ )
1449
+ if not old_content:
1450
+ log_info(f"No content to migrate from collection {v1_table_name}")
1451
+ return
1452
+
1453
+ # Parse the content into the new format
1454
+ memories: List[UserMemory] = []
1455
+ sessions: Sequence[Union[AgentSession, TeamSession, WorkflowSession]] = []
1456
+ if v1_table_type == "agent_sessions":
1457
+ sessions = parse_agent_sessions(old_content)
1458
+ elif v1_table_type == "team_sessions":
1459
+ sessions = parse_team_sessions(old_content)
1460
+ elif v1_table_type == "workflow_sessions":
1461
+ sessions = parse_workflow_sessions(old_content)
1462
+ elif v1_table_type == "memories":
1463
+ memories = parse_memories(old_content)
1464
+ else:
1465
+ raise ValueError(f"Invalid table type: {v1_table_type}")
1466
+
1467
+ # Insert the new content into the new collection
1468
+ if v1_table_type == "agent_sessions":
1469
+ for session in sessions:
1470
+ self.upsert_session(session)
1471
+ log_info(f"Migrated {len(sessions)} Agent sessions to collection: {self.session_table_name}")
1472
+
1473
+ elif v1_table_type == "team_sessions":
1474
+ for session in sessions:
1475
+ self.upsert_session(session)
1476
+ log_info(f"Migrated {len(sessions)} Team sessions to collection: {self.session_table_name}")
1477
+
1478
+ elif v1_table_type == "workflow_sessions":
1479
+ for session in sessions:
1480
+ self.upsert_session(session)
1481
+ log_info(f"Migrated {len(sessions)} Workflow sessions to collection: {self.session_table_name}")
1482
+
1483
+ elif v1_table_type == "memories":
1484
+ for memory in memories:
1485
+ self.upsert_user_memory(memory)
1486
+ log_info(f"Migrated {len(memories)} memories to collection: {self.memory_table_name}")
agno/db/mysql/mysql.py CHANGED
@@ -20,9 +20,9 @@ from agno.db.mysql.utils import (
20
20
  from agno.db.schemas.evals import EvalFilterType, EvalRunRecord, EvalType
21
21
  from agno.db.schemas.knowledge import KnowledgeRow
22
22
  from agno.db.schemas.memory import UserMemory
23
- from agno.db.utils import generate_deterministic_id
24
23
  from agno.session import AgentSession, Session, TeamSession, WorkflowSession
25
24
  from agno.utils.log import log_debug, log_error, log_info
25
+ from agno.utils.string import generate_id
26
26
 
27
27
  try:
28
28
  from sqlalchemy import TEXT, and_, cast, func, update
@@ -75,7 +75,7 @@ class MySQLDb(BaseDb):
75
75
  base_seed = db_url or str(db_engine.url) # type: ignore
76
76
  schema_suffix = db_schema if db_schema is not None else "ai"
77
77
  seed = f"{base_seed}#{schema_suffix}"
78
- id = generate_deterministic_id(seed)
78
+ id = generate_id(seed)
79
79
 
80
80
  super().__init__(
81
81
  id=id,
@@ -350,8 +350,8 @@ class MySQLDb(BaseDb):
350
350
 
351
351
  Args:
352
352
  session_id (str): ID of the session to read.
353
+ session_type (SessionType): Type of session to get.
353
354
  user_id (Optional[str]): User ID to filter by. Defaults to None.
354
- session_type (Optional[SessionType]): Type of session to read. Defaults to None.
355
355
  deserialize (Optional[bool]): Whether to serialize the session. Defaults to True.
356
356
 
357
357
  Returns:
@@ -415,6 +415,7 @@ class MySQLDb(BaseDb):
415
415
  Get all sessions in the given table. Can filter by user_id and entity_id.
416
416
 
417
417
  Args:
418
+ session_type (Optional[SessionType]): The type of sessions to get.
418
419
  user_id (Optional[str]): The ID of the user to filter by.
419
420
  entity_id (Optional[str]): The ID of the agent / workflow to filter by.
420
421
  start_timestamp (Optional[int]): The start timestamp to filter by.
@@ -1711,17 +1712,17 @@ class MySQLDb(BaseDb):
1711
1712
  if v1_table_type == "agent_sessions":
1712
1713
  for session in sessions:
1713
1714
  self.upsert_session(session)
1714
- log_info(f"Migrated {len(sessions)} Agent sessions to table: {self.session_table}")
1715
+ log_info(f"Migrated {len(sessions)} Agent sessions to table: {self.session_table_name}")
1715
1716
 
1716
1717
  elif v1_table_type == "team_sessions":
1717
1718
  for session in sessions:
1718
1719
  self.upsert_session(session)
1719
- log_info(f"Migrated {len(sessions)} Team sessions to table: {self.session_table}")
1720
+ log_info(f"Migrated {len(sessions)} Team sessions to table: {self.session_table_name}")
1720
1721
 
1721
1722
  elif v1_table_type == "workflow_sessions":
1722
1723
  for session in sessions:
1723
1724
  self.upsert_session(session)
1724
- log_info(f"Migrated {len(sessions)} Workflow sessions to table: {self.session_table}")
1725
+ log_info(f"Migrated {len(sessions)} Workflow sessions to table: {self.session_table_name}")
1725
1726
 
1726
1727
  elif v1_table_type == "memories":
1727
1728
  for memory in memories:
agno/db/mysql/schemas.py CHANGED
@@ -8,12 +8,12 @@ except ImportError:
8
8
  raise ImportError("`sqlalchemy` not installed. Please install it using `pip install sqlalchemy`")
9
9
 
10
10
  SESSION_TABLE_SCHEMA = {
11
- "session_id": {"type": String, "nullable": False},
12
- "session_type": {"type": String, "nullable": False, "index": True},
13
- "agent_id": {"type": String, "nullable": True},
14
- "team_id": {"type": String, "nullable": True},
15
- "workflow_id": {"type": String, "nullable": True},
16
- "user_id": {"type": String, "nullable": True},
11
+ "session_id": {"type": lambda: String(128), "nullable": False},
12
+ "session_type": {"type": lambda: String(20), "nullable": False, "index": True},
13
+ "agent_id": {"type": lambda: String(128), "nullable": True},
14
+ "team_id": {"type": lambda: String(128), "nullable": True},
15
+ "workflow_id": {"type": lambda: String(128), "nullable": True},
16
+ "user_id": {"type": lambda: String(128), "nullable": True},
17
17
  "session_data": {"type": JSON, "nullable": True},
18
18
  "agent_data": {"type": JSON, "nullable": True},
19
19
  "team_data": {"type": JSON, "nullable": True},
@@ -32,50 +32,50 @@ SESSION_TABLE_SCHEMA = {
32
32
  }
33
33
 
34
34
  USER_MEMORY_TABLE_SCHEMA = {
35
- "memory_id": {"type": String, "primary_key": True, "nullable": False},
35
+ "memory_id": {"type": lambda: String(128), "primary_key": True, "nullable": False},
36
36
  "memory": {"type": JSON, "nullable": False},
37
37
  "input": {"type": Text, "nullable": True},
38
- "agent_id": {"type": String, "nullable": True},
39
- "team_id": {"type": String, "nullable": True},
40
- "user_id": {"type": String, "nullable": True, "index": True},
38
+ "agent_id": {"type": lambda: String(128), "nullable": True},
39
+ "team_id": {"type": lambda: String(128), "nullable": True},
40
+ "user_id": {"type": lambda: String(128), "nullable": True, "index": True},
41
41
  "topics": {"type": JSON, "nullable": True},
42
42
  "updated_at": {"type": BigInteger, "nullable": True, "index": True},
43
43
  }
44
44
 
45
45
  EVAL_TABLE_SCHEMA = {
46
- "run_id": {"type": String, "primary_key": True, "nullable": False},
47
- "eval_type": {"type": String, "nullable": False},
46
+ "run_id": {"type": lambda: String(128), "primary_key": True, "nullable": False},
47
+ "eval_type": {"type": lambda: String(50), "nullable": False},
48
48
  "eval_data": {"type": JSON, "nullable": False},
49
49
  "eval_input": {"type": JSON, "nullable": False},
50
- "name": {"type": String, "nullable": True},
51
- "agent_id": {"type": String, "nullable": True},
52
- "team_id": {"type": String, "nullable": True},
53
- "workflow_id": {"type": String, "nullable": True},
54
- "model_id": {"type": String, "nullable": True},
55
- "model_provider": {"type": String, "nullable": True},
56
- "evaluated_component_name": {"type": String, "nullable": True},
50
+ "name": {"type": lambda: String(255), "nullable": True},
51
+ "agent_id": {"type": lambda: String(128), "nullable": True},
52
+ "team_id": {"type": lambda: String(128), "nullable": True},
53
+ "workflow_id": {"type": lambda: String(128), "nullable": True},
54
+ "model_id": {"type": lambda: String(128), "nullable": True},
55
+ "model_provider": {"type": lambda: String(128), "nullable": True},
56
+ "evaluated_component_name": {"type": lambda: String(255), "nullable": True},
57
57
  "created_at": {"type": BigInteger, "nullable": False, "index": True},
58
58
  "updated_at": {"type": BigInteger, "nullable": True},
59
59
  }
60
60
 
61
61
  KNOWLEDGE_TABLE_SCHEMA = {
62
- "id": {"type": String, "primary_key": True, "nullable": False},
63
- "name": {"type": String, "nullable": False},
62
+ "id": {"type": lambda: String(128), "primary_key": True, "nullable": False},
63
+ "name": {"type": lambda: String(255), "nullable": False},
64
64
  "description": {"type": Text, "nullable": False},
65
65
  "metadata": {"type": JSON, "nullable": True},
66
- "type": {"type": String, "nullable": True},
66
+ "type": {"type": lambda: String(50), "nullable": True},
67
67
  "size": {"type": BigInteger, "nullable": True},
68
- "linked_to": {"type": String, "nullable": True},
68
+ "linked_to": {"type": lambda: String(128), "nullable": True},
69
69
  "access_count": {"type": BigInteger, "nullable": True},
70
70
  "created_at": {"type": BigInteger, "nullable": True},
71
71
  "updated_at": {"type": BigInteger, "nullable": True},
72
- "status": {"type": String, "nullable": True},
72
+ "status": {"type": lambda: String(50), "nullable": True},
73
73
  "status_message": {"type": Text, "nullable": True},
74
- "external_id": {"type": String, "nullable": True},
74
+ "external_id": {"type": lambda: String(128), "nullable": True},
75
75
  }
76
76
 
77
77
  METRICS_TABLE_SCHEMA = {
78
- "id": {"type": String, "primary_key": True, "nullable": False},
78
+ "id": {"type": lambda: String(128), "primary_key": True, "nullable": False},
79
79
  "agent_runs_count": {"type": BigInteger, "nullable": False},
80
80
  "team_runs_count": {"type": BigInteger, "nullable": False},
81
81
  "workflow_runs_count": {"type": BigInteger, "nullable": False},
@@ -86,7 +86,7 @@ METRICS_TABLE_SCHEMA = {
86
86
  "token_metrics": {"type": JSON, "nullable": False},
87
87
  "model_metrics": {"type": JSON, "nullable": False},
88
88
  "date": {"type": Date, "nullable": False, "index": True},
89
- "aggregation_period": {"type": String, "nullable": False},
89
+ "aggregation_period": {"type": lambda: String(20), "nullable": False},
90
90
  "created_at": {"type": BigInteger, "nullable": False},
91
91
  "updated_at": {"type": BigInteger, "nullable": True},
92
92
  "completed": {"type": Boolean, "nullable": False},
@@ -18,9 +18,9 @@ from agno.db.postgres.utils import (
18
18
  from agno.db.schemas.evals import EvalFilterType, EvalRunRecord, EvalType
19
19
  from agno.db.schemas.knowledge import KnowledgeRow
20
20
  from agno.db.schemas.memory import UserMemory
21
- from agno.db.utils import generate_deterministic_id
22
21
  from agno.session import AgentSession, Session, TeamSession, WorkflowSession
23
22
  from agno.utils.log import log_debug, log_error, log_info, log_warning
23
+ from agno.utils.string import generate_id
24
24
 
25
25
  try:
26
26
  from sqlalchemy import Index, String, UniqueConstraint, func, update
@@ -82,7 +82,7 @@ class PostgresDb(BaseDb):
82
82
  base_seed = db_url or str(db_engine.url) # type: ignore
83
83
  schema_suffix = db_schema if db_schema is not None else "ai"
84
84
  seed = f"{base_seed}#{schema_suffix}"
85
- id = generate_deterministic_id(seed)
85
+ id = generate_id(seed)
86
86
 
87
87
  super().__init__(
88
88
  id=id,
@@ -345,8 +345,8 @@ class PostgresDb(BaseDb):
345
345
 
346
346
  Args:
347
347
  session_id (str): ID of the session to read.
348
+ session_type (SessionType): Type of session to get.
348
349
  user_id (Optional[str]): User ID to filter by. Defaults to None.
349
- session_type (Optional[SessionType]): Type of session to read. Defaults to None.
350
350
  deserialize (Optional[bool]): Whether to serialize the session. Defaults to True.
351
351
 
352
352
  Returns:
@@ -410,6 +410,7 @@ class PostgresDb(BaseDb):
410
410
  Get all sessions in the given table. Can filter by user_id and entity_id.
411
411
 
412
412
  Args:
413
+ session_type (Optional[SessionType]): The type of session to get.
413
414
  user_id (Optional[str]): The ID of the user to filter by.
414
415
  entity_id (Optional[str]): The ID of the agent / workflow to filter by.
415
416
  start_timestamp (Optional[int]): The start timestamp to filter by.
@@ -1700,17 +1701,17 @@ class PostgresDb(BaseDb):
1700
1701
  if v1_table_type == "agent_sessions":
1701
1702
  for session in sessions:
1702
1703
  self.upsert_session(session)
1703
- log_info(f"Migrated {len(sessions)} Agent sessions to table: {self.session_table}")
1704
+ log_info(f"Migrated {len(sessions)} Agent sessions to table: {self.session_table_name}")
1704
1705
 
1705
1706
  elif v1_table_type == "team_sessions":
1706
1707
  for session in sessions:
1707
1708
  self.upsert_session(session)
1708
- log_info(f"Migrated {len(sessions)} Team sessions to table: {self.session_table}")
1709
+ log_info(f"Migrated {len(sessions)} Team sessions to table: {self.session_table_name}")
1709
1710
 
1710
1711
  elif v1_table_type == "workflow_sessions":
1711
1712
  for session in sessions:
1712
1713
  self.upsert_session(session)
1713
- log_info(f"Migrated {len(sessions)} Workflow sessions to table: {self.session_table}")
1714
+ log_info(f"Migrated {len(sessions)} Workflow sessions to table: {self.session_table_name}")
1714
1715
 
1715
1716
  elif v1_table_type == "memories":
1716
1717
  for memory in memories: