agno 2.3.22__py3-none-any.whl → 2.3.23__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 (40) hide show
  1. agno/agent/agent.py +22 -1
  2. agno/agent/remote.py +1 -1
  3. agno/db/mysql/async_mysql.py +5 -7
  4. agno/db/mysql/mysql.py +5 -7
  5. agno/db/mysql/schemas.py +39 -21
  6. agno/db/postgres/async_postgres.py +10 -2
  7. agno/db/postgres/postgres.py +5 -7
  8. agno/db/postgres/schemas.py +39 -21
  9. agno/db/singlestore/schemas.py +41 -21
  10. agno/db/singlestore/singlestore.py +14 -3
  11. agno/db/sqlite/async_sqlite.py +7 -2
  12. agno/db/sqlite/schemas.py +36 -21
  13. agno/db/sqlite/sqlite.py +3 -7
  14. agno/models/base.py +4 -0
  15. agno/models/google/gemini.py +27 -4
  16. agno/os/routers/agents/router.py +1 -1
  17. agno/os/routers/evals/evals.py +2 -2
  18. agno/os/routers/knowledge/knowledge.py +2 -2
  19. agno/os/routers/knowledge/schemas.py +1 -1
  20. agno/os/routers/memory/memory.py +4 -4
  21. agno/os/routers/session/session.py +2 -2
  22. agno/os/routers/teams/router.py +2 -2
  23. agno/os/routers/traces/traces.py +3 -3
  24. agno/os/routers/workflows/router.py +1 -1
  25. agno/os/schema.py +1 -1
  26. agno/remote/base.py +1 -1
  27. agno/team/remote.py +1 -1
  28. agno/team/team.py +19 -1
  29. agno/tools/brandfetch.py +27 -18
  30. agno/tools/browserbase.py +150 -13
  31. agno/tools/function.py +6 -1
  32. agno/tools/mcp/mcp.py +1 -0
  33. agno/tools/toolkit.py +89 -21
  34. agno/workflow/remote.py +1 -1
  35. agno/workflow/workflow.py +14 -0
  36. {agno-2.3.22.dist-info → agno-2.3.23.dist-info}/METADATA +1 -1
  37. {agno-2.3.22.dist-info → agno-2.3.23.dist-info}/RECORD +40 -40
  38. {agno-2.3.22.dist-info → agno-2.3.23.dist-info}/WHEEL +0 -0
  39. {agno-2.3.22.dist-info → agno-2.3.23.dist-info}/licenses/LICENSE +0 -0
  40. {agno-2.3.22.dist-info → agno-2.3.23.dist-info}/top_level.txt +0 -0
agno/agent/agent.py CHANGED
@@ -61,6 +61,9 @@ from agno.run.agent import (
61
61
  RunOutput,
62
62
  RunOutputEvent,
63
63
  )
64
+ from agno.run.cancel import (
65
+ acancel_run as acancel_run_global,
66
+ )
64
67
  from agno.run.cancel import (
65
68
  acleanup_run,
66
69
  araise_if_cancelled,
@@ -2030,6 +2033,7 @@ class Agent:
2030
2033
  run_response=run_response,
2031
2034
  run_context=run_context,
2032
2035
  session=agent_session,
2036
+ async_mode=True,
2033
2037
  )
2034
2038
 
2035
2039
  # 6. Prepare run messages
@@ -2377,6 +2381,7 @@ class Agent:
2377
2381
  run_response=run_response,
2378
2382
  run_context=run_context,
2379
2383
  session=agent_session,
2384
+ async_mode=True,
2380
2385
  )
2381
2386
 
2382
2387
  # 6. Prepare run messages
@@ -3987,6 +3992,7 @@ class Agent:
3987
3992
  run_response=run_response,
3988
3993
  run_context=run_context,
3989
3994
  session=agent_session,
3995
+ async_mode=True,
3990
3996
  )
3991
3997
 
3992
3998
  # 6. Prepare run messages
@@ -4293,6 +4299,7 @@ class Agent:
4293
4299
  run_response=run_response,
4294
4300
  run_context=run_context,
4295
4301
  session=agent_session,
4302
+ async_mode=True,
4296
4303
  )
4297
4304
 
4298
4305
  # 6. Prepare run messages
@@ -6514,6 +6521,7 @@ class Agent:
6514
6521
  run_response: RunOutput,
6515
6522
  run_context: RunContext,
6516
6523
  session: AgentSession,
6524
+ async_mode: bool = False,
6517
6525
  ) -> List[Union[Function, dict]]:
6518
6526
  _function_names = []
6519
6527
  _functions: List[Union[Function, dict]] = []
@@ -6544,7 +6552,8 @@ class Agent:
6544
6552
 
6545
6553
  elif isinstance(tool, Toolkit):
6546
6554
  # For each function in the toolkit and process entrypoint
6547
- for name, _func in tool.functions.items():
6555
+ toolkit_functions = tool.get_async_functions() if async_mode else tool.get_functions()
6556
+ for name, _func in toolkit_functions.items():
6548
6557
  if name in _function_names:
6549
6558
  continue
6550
6559
  _function_names.append(name)
@@ -6774,6 +6783,18 @@ class Agent:
6774
6783
  """
6775
6784
  return cancel_run_global(run_id)
6776
6785
 
6786
+ @staticmethod
6787
+ async def acancel_run(run_id: str) -> bool:
6788
+ """Cancel a running agent execution (async version).
6789
+
6790
+ Args:
6791
+ run_id (str): The run_id to cancel.
6792
+
6793
+ Returns:
6794
+ bool: True if the run was found and marked for cancellation, False otherwise.
6795
+ """
6796
+ return await acancel_run_global(run_id)
6797
+
6777
6798
  # -*- Session Database Functions
6778
6799
  def _read_session(
6779
6800
  self, session_id: str, session_type: SessionType = SessionType.AGENT
agno/agent/remote.py CHANGED
@@ -488,7 +488,7 @@ class RemoteAgent(BaseRemote):
488
488
  else:
489
489
  raise ValueError("No client available")
490
490
 
491
- async def cancel_run(self, run_id: str, auth_token: Optional[str] = None) -> bool:
491
+ async def acancel_run(self, run_id: str, auth_token: Optional[str] = None) -> bool:
492
492
  """Cancel a running agent execution.
493
493
 
494
494
  Args:
@@ -158,7 +158,10 @@ class AsyncMySQLDb(AsyncBaseDb):
158
158
  Table: SQLAlchemy Table object
159
159
  """
160
160
  try:
161
- table_schema = get_table_schema_definition(table_type).copy()
161
+ # Pass traces_table_name and db_schema for spans table foreign key resolution
162
+ table_schema = get_table_schema_definition(
163
+ table_type, traces_table_name=self.trace_table_name, db_schema=self.db_schema
164
+ ).copy()
162
165
 
163
166
  log_debug(f"Creating table {self.db_schema}.{table_name} with schema: {table_schema}")
164
167
 
@@ -183,12 +186,7 @@ class AsyncMySQLDb(AsyncBaseDb):
183
186
 
184
187
  # Handle foreign key constraint
185
188
  if "foreign_key" in col_config:
186
- fk_ref = col_config["foreign_key"]
187
- # For spans table, dynamically replace the traces table reference
188
- # with the actual trace table name configured for this db instance
189
- if table_type == "spans" and "trace_id" in fk_ref:
190
- fk_ref = f"{self.db_schema}.{self.trace_table_name}.trace_id"
191
- column_args.append(ForeignKey(fk_ref))
189
+ column_args.append(ForeignKey(col_config["foreign_key"]))
192
190
 
193
191
  columns.append(Column(*column_args, **column_kwargs)) # type: ignore
194
192
 
agno/db/mysql/mysql.py CHANGED
@@ -155,7 +155,10 @@ class MySQLDb(BaseDb):
155
155
  Table: SQLAlchemy Table object
156
156
  """
157
157
  try:
158
- table_schema = get_table_schema_definition(table_type).copy()
158
+ # Pass traces_table_name and db_schema for spans table foreign key resolution
159
+ table_schema = get_table_schema_definition(
160
+ table_type, traces_table_name=self.trace_table_name, db_schema=self.db_schema
161
+ ).copy()
159
162
 
160
163
  columns: List[Column] = []
161
164
  indexes: List[str] = []
@@ -178,12 +181,7 @@ class MySQLDb(BaseDb):
178
181
 
179
182
  # Handle foreign key constraint
180
183
  if "foreign_key" in col_config:
181
- fk_ref = col_config["foreign_key"]
182
- # For spans table, dynamically replace the traces table reference
183
- # with the actual trace table name configured for this db instance
184
- if table_type == "spans" and "trace_id" in fk_ref:
185
- fk_ref = f"{self.db_schema}.{self.trace_table_name}.trace_id"
186
- column_args.append(ForeignKey(fk_ref))
184
+ column_args.append(ForeignKey(col_config["foreign_key"]))
187
185
 
188
186
  columns.append(Column(*column_args, **column_kwargs)) # type: ignore
189
187
 
agno/db/mysql/schemas.py CHANGED
@@ -136,37 +136,56 @@ TRACE_TABLE_SCHEMA = {
136
136
  "created_at": {"type": lambda: String(128), "nullable": False, "index": True}, # ISO 8601 datetime string
137
137
  }
138
138
 
139
- SPAN_TABLE_SCHEMA = {
140
- "span_id": {"type": lambda: String(128), "primary_key": True, "nullable": False},
141
- "trace_id": {
142
- "type": lambda: String(128),
143
- "nullable": False,
144
- "index": True,
145
- "foreign_key": "agno_traces.trace_id", # Foreign key to traces table
146
- },
147
- "parent_span_id": {"type": lambda: String(128), "nullable": True, "index": True},
148
- "name": {"type": lambda: String(255), "nullable": False},
149
- "span_kind": {"type": lambda: String(50), "nullable": False},
150
- "status_code": {"type": lambda: String(50), "nullable": False},
151
- "status_message": {"type": Text, "nullable": True},
152
- "start_time": {"type": lambda: String(128), "nullable": False, "index": True}, # ISO 8601 datetime string
153
- "end_time": {"type": lambda: String(128), "nullable": False}, # ISO 8601 datetime string
154
- "duration_ms": {"type": BigInteger, "nullable": False},
155
- "attributes": {"type": JSON, "nullable": True},
156
- "created_at": {"type": lambda: String(128), "nullable": False, "index": True}, # ISO 8601 datetime string
157
- }
158
139
 
140
+ def _get_span_table_schema(traces_table_name: str = "agno_traces", db_schema: str = "agno") -> dict[str, Any]:
141
+ """Get the span table schema with the correct foreign key reference.
142
+
143
+ Args:
144
+ traces_table_name: The name of the traces table to reference in the foreign key.
145
+ db_schema: The database schema name.
146
+
147
+ Returns:
148
+ The span table schema dictionary.
149
+ """
150
+ return {
151
+ "span_id": {"type": lambda: String(128), "primary_key": True, "nullable": False},
152
+ "trace_id": {
153
+ "type": lambda: String(128),
154
+ "nullable": False,
155
+ "index": True,
156
+ "foreign_key": f"{db_schema}.{traces_table_name}.trace_id",
157
+ },
158
+ "parent_span_id": {"type": lambda: String(128), "nullable": True, "index": True},
159
+ "name": {"type": lambda: String(255), "nullable": False},
160
+ "span_kind": {"type": lambda: String(50), "nullable": False},
161
+ "status_code": {"type": lambda: String(50), "nullable": False},
162
+ "status_message": {"type": Text, "nullable": True},
163
+ "start_time": {"type": lambda: String(128), "nullable": False, "index": True}, # ISO 8601 datetime string
164
+ "end_time": {"type": lambda: String(128), "nullable": False}, # ISO 8601 datetime string
165
+ "duration_ms": {"type": BigInteger, "nullable": False},
166
+ "attributes": {"type": JSON, "nullable": True},
167
+ "created_at": {"type": lambda: String(128), "nullable": False, "index": True}, # ISO 8601 datetime string
168
+ }
159
169
 
160
- def get_table_schema_definition(table_type: str) -> dict[str, Any]:
170
+
171
+ def get_table_schema_definition(
172
+ table_type: str, traces_table_name: str = "agno_traces", db_schema: str = "agno"
173
+ ) -> dict[str, Any]:
161
174
  """
162
175
  Get the expected schema definition for the given table.
163
176
 
164
177
  Args:
165
178
  table_type (str): The type of table to get the schema for.
179
+ traces_table_name (str): The name of the traces table (used for spans foreign key).
180
+ db_schema (str): The database schema name (used for spans foreign key).
166
181
 
167
182
  Returns:
168
183
  Dict[str, Any]: Dictionary containing column definitions for the table
169
184
  """
185
+ # Handle spans table specially to resolve the foreign key reference
186
+ if table_type == "spans":
187
+ return _get_span_table_schema(traces_table_name, db_schema)
188
+
170
189
  schemas = {
171
190
  "sessions": SESSION_TABLE_SCHEMA,
172
191
  "evals": EVAL_TABLE_SCHEMA,
@@ -176,7 +195,6 @@ def get_table_schema_definition(table_type: str) -> dict[str, Any]:
176
195
  "culture": CULTURAL_KNOWLEDGE_TABLE_SCHEMA,
177
196
  "versions": VERSIONS_TABLE_SCHEMA,
178
197
  "traces": TRACE_TABLE_SCHEMA,
179
- "spans": SPAN_TABLE_SCHEMA,
180
198
  }
181
199
 
182
200
  schema = schemas.get(table_type, {})
@@ -31,7 +31,7 @@ from agno.utils.log import log_debug, log_error, log_info, log_warning
31
31
  from agno.utils.string import sanitize_postgres_string, sanitize_postgres_strings
32
32
 
33
33
  try:
34
- from sqlalchemy import Index, String, Table, UniqueConstraint, and_, case, func, or_, update
34
+ from sqlalchemy import ForeignKey, Index, String, Table, UniqueConstraint, and_, case, func, or_, update
35
35
  from sqlalchemy.dialects import postgresql
36
36
  from sqlalchemy.dialects.postgresql import TIMESTAMP
37
37
  from sqlalchemy.exc import ProgrammingError
@@ -192,7 +192,10 @@ class AsyncPostgresDb(AsyncBaseDb):
192
192
  Table: SQLAlchemy Table object
193
193
  """
194
194
  try:
195
- table_schema = get_table_schema_definition(table_type).copy()
195
+ # Pass traces_table_name and db_schema for spans table foreign key resolution
196
+ table_schema = get_table_schema_definition(
197
+ table_type, traces_table_name=self.trace_table_name, db_schema=self.db_schema
198
+ ).copy()
196
199
 
197
200
  columns: List[Column] = []
198
201
  indexes: List[str] = []
@@ -212,6 +215,11 @@ class AsyncPostgresDb(AsyncBaseDb):
212
215
  if col_config.get("unique", False):
213
216
  column_kwargs["unique"] = True
214
217
  unique_constraints.append(col_name)
218
+
219
+ # Handle foreign key constraint
220
+ if "foreign_key" in col_config:
221
+ column_args.append(ForeignKey(col_config["foreign_key"]))
222
+
215
223
  columns.append(Column(*column_args, **column_kwargs)) # type: ignore
216
224
 
217
225
  # Create the table object
@@ -176,7 +176,10 @@ class PostgresDb(BaseDb):
176
176
  Table: SQLAlchemy Table object
177
177
  """
178
178
  try:
179
- table_schema = get_table_schema_definition(table_type).copy()
179
+ # Pass traces_table_name and db_schema for spans table foreign key resolution
180
+ table_schema = get_table_schema_definition(
181
+ table_type, traces_table_name=self.trace_table_name, db_schema=self.db_schema
182
+ ).copy()
180
183
 
181
184
  columns: List[Column] = []
182
185
  indexes: List[str] = []
@@ -199,12 +202,7 @@ class PostgresDb(BaseDb):
199
202
 
200
203
  # Handle foreign key constraint
201
204
  if "foreign_key" in col_config:
202
- fk_ref = col_config["foreign_key"]
203
- # For spans table, dynamically replace the traces table reference
204
- # with the actual trace table name configured for this db instance
205
- if table_type == "spans" and "trace_id" in fk_ref:
206
- fk_ref = f"{self.db_schema}.{self.trace_table_name}.trace_id"
207
- column_args.append(ForeignKey(fk_ref))
205
+ column_args.append(ForeignKey(col_config["foreign_key"]))
208
206
 
209
207
  columns.append(Column(*column_args, **column_kwargs)) # type: ignore
210
208
 
@@ -137,37 +137,56 @@ TRACE_TABLE_SCHEMA = {
137
137
  "created_at": {"type": String, "nullable": False, "index": True}, # ISO 8601 datetime string
138
138
  }
139
139
 
140
- SPAN_TABLE_SCHEMA = {
141
- "span_id": {"type": String, "primary_key": True, "nullable": False},
142
- "trace_id": {
143
- "type": String,
144
- "nullable": False,
145
- "index": True,
146
- "foreign_key": "agno_traces.trace_id", # Foreign key to traces table
147
- },
148
- "parent_span_id": {"type": String, "nullable": True, "index": True},
149
- "name": {"type": String, "nullable": False},
150
- "span_kind": {"type": String, "nullable": False},
151
- "status_code": {"type": String, "nullable": False},
152
- "status_message": {"type": Text, "nullable": True},
153
- "start_time": {"type": String, "nullable": False, "index": True}, # ISO 8601 datetime string
154
- "end_time": {"type": String, "nullable": False}, # ISO 8601 datetime string
155
- "duration_ms": {"type": BigInteger, "nullable": False},
156
- "attributes": {"type": JSONB, "nullable": True},
157
- "created_at": {"type": String, "nullable": False, "index": True}, # ISO 8601 datetime string
158
- }
159
140
 
141
+ def _get_span_table_schema(traces_table_name: str = "agno_traces", db_schema: str = "agno") -> dict[str, Any]:
142
+ """Get the span table schema with the correct foreign key reference.
143
+
144
+ Args:
145
+ traces_table_name: The name of the traces table to reference in the foreign key.
146
+ db_schema: The database schema name.
147
+
148
+ Returns:
149
+ The span table schema dictionary.
150
+ """
151
+ return {
152
+ "span_id": {"type": String, "primary_key": True, "nullable": False},
153
+ "trace_id": {
154
+ "type": String,
155
+ "nullable": False,
156
+ "index": True,
157
+ "foreign_key": f"{db_schema}.{traces_table_name}.trace_id",
158
+ },
159
+ "parent_span_id": {"type": String, "nullable": True, "index": True},
160
+ "name": {"type": String, "nullable": False},
161
+ "span_kind": {"type": String, "nullable": False},
162
+ "status_code": {"type": String, "nullable": False},
163
+ "status_message": {"type": Text, "nullable": True},
164
+ "start_time": {"type": String, "nullable": False, "index": True}, # ISO 8601 datetime string
165
+ "end_time": {"type": String, "nullable": False}, # ISO 8601 datetime string
166
+ "duration_ms": {"type": BigInteger, "nullable": False},
167
+ "attributes": {"type": JSONB, "nullable": True},
168
+ "created_at": {"type": String, "nullable": False, "index": True}, # ISO 8601 datetime string
169
+ }
160
170
 
161
- def get_table_schema_definition(table_type: str) -> dict[str, Any]:
171
+
172
+ def get_table_schema_definition(
173
+ table_type: str, traces_table_name: str = "agno_traces", db_schema: str = "agno"
174
+ ) -> dict[str, Any]:
162
175
  """
163
176
  Get the expected schema definition for the given table.
164
177
 
165
178
  Args:
166
179
  table_type (str): The type of table to get the schema for.
180
+ traces_table_name (str): The name of the traces table (used for spans foreign key).
181
+ db_schema (str): The database schema name (used for spans foreign key).
167
182
 
168
183
  Returns:
169
184
  Dict[str, Any]: Dictionary containing column definitions for the table
170
185
  """
186
+ # Handle spans table specially to resolve the foreign key reference
187
+ if table_type == "spans":
188
+ return _get_span_table_schema(traces_table_name, db_schema)
189
+
171
190
  schemas = {
172
191
  "sessions": SESSION_TABLE_SCHEMA,
173
192
  "evals": EVAL_TABLE_SCHEMA,
@@ -177,7 +196,6 @@ def get_table_schema_definition(table_type: str) -> dict[str, Any]:
177
196
  "culture": CULTURAL_KNOWLEDGE_TABLE_SCHEMA,
178
197
  "versions": VERSIONS_TABLE_SCHEMA,
179
198
  "traces": TRACE_TABLE_SCHEMA,
180
- "spans": SPAN_TABLE_SCHEMA,
181
199
  }
182
200
 
183
201
  schema = schemas.get(table_type, {})
@@ -131,35 +131,56 @@ TRACE_TABLE_SCHEMA = {
131
131
  "created_at": {"type": lambda: String(64), "nullable": False, "index": True}, # ISO 8601 datetime string
132
132
  }
133
133
 
134
- SPAN_TABLE_SCHEMA = {
135
- "span_id": {"type": lambda: String(128), "primary_key": True, "nullable": False},
136
- "trace_id": {
137
- "type": lambda: String(128),
138
- "nullable": False,
139
- "index": True,
140
- "foreign_key": "agno_traces.trace_id", # Foreign key to traces table
141
- },
142
- "parent_span_id": {"type": lambda: String(128), "nullable": True, "index": True},
143
- "name": {"type": lambda: String(512), "nullable": False},
144
- "span_kind": {"type": lambda: String(50), "nullable": False},
145
- "status_code": {"type": lambda: String(20), "nullable": False},
146
- "status_message": {"type": Text, "nullable": True},
147
- "start_time": {"type": lambda: String(64), "nullable": False, "index": True}, # ISO 8601 datetime string
148
- "end_time": {"type": lambda: String(64), "nullable": False}, # ISO 8601 datetime string
149
- "duration_ms": {"type": BigInteger, "nullable": False},
150
- "attributes": {"type": JSON, "nullable": True},
151
- "created_at": {"type": lambda: String(64), "nullable": False, "index": True}, # ISO 8601 datetime string
152
- }
153
134
 
135
+ def _get_span_table_schema(traces_table_name: str = "agno_traces", db_schema: str = "agno") -> dict[str, Any]:
136
+ """Get the span table schema with the correct foreign key reference.
137
+
138
+ Args:
139
+ traces_table_name: The name of the traces table to reference in the foreign key.
140
+ db_schema: The database schema name.
141
+
142
+ Returns:
143
+ The span table schema dictionary.
144
+ """
145
+ return {
146
+ "span_id": {"type": lambda: String(128), "primary_key": True, "nullable": False},
147
+ "trace_id": {
148
+ "type": lambda: String(128),
149
+ "nullable": False,
150
+ "index": True,
151
+ "foreign_key": f"{db_schema}.{traces_table_name}.trace_id",
152
+ },
153
+ "parent_span_id": {"type": lambda: String(128), "nullable": True, "index": True},
154
+ "name": {"type": lambda: String(512), "nullable": False},
155
+ "span_kind": {"type": lambda: String(50), "nullable": False},
156
+ "status_code": {"type": lambda: String(20), "nullable": False},
157
+ "status_message": {"type": Text, "nullable": True},
158
+ "start_time": {"type": lambda: String(64), "nullable": False, "index": True}, # ISO 8601 datetime string
159
+ "end_time": {"type": lambda: String(64), "nullable": False}, # ISO 8601 datetime string
160
+ "duration_ms": {"type": BigInteger, "nullable": False},
161
+ "attributes": {"type": JSON, "nullable": True},
162
+ "created_at": {"type": lambda: String(64), "nullable": False, "index": True}, # ISO 8601 datetime string
163
+ }
154
164
 
155
- def get_table_schema_definition(table_type: str) -> dict[str, Any]:
165
+
166
+ def get_table_schema_definition(
167
+ table_type: str, traces_table_name: str = "agno_traces", db_schema: str = "agno"
168
+ ) -> dict[str, Any]:
156
169
  """
157
170
  Get the expected schema definition for the given table.
171
+
158
172
  Args:
159
173
  table_type (str): The type of table to get the schema for.
174
+ traces_table_name (str): The name of the traces table (used for spans foreign key).
175
+ db_schema (str): The database schema name (used for spans foreign key).
176
+
160
177
  Returns:
161
178
  Dict[str, Any]: Dictionary containing column definitions for the table
162
179
  """
180
+ # Handle spans table specially to resolve the foreign key reference
181
+ if table_type == "spans":
182
+ return _get_span_table_schema(traces_table_name, db_schema)
183
+
163
184
  schemas = {
164
185
  "sessions": SESSION_TABLE_SCHEMA,
165
186
  "evals": EVAL_TABLE_SCHEMA,
@@ -169,7 +190,6 @@ def get_table_schema_definition(table_type: str) -> dict[str, Any]:
169
190
  "culture": CULTURAL_KNOWLEDGE_TABLE_SCHEMA,
170
191
  "versions": VERSIONS_TABLE_SCHEMA,
171
192
  "traces": TRACE_TABLE_SCHEMA,
172
- "spans": SPAN_TABLE_SCHEMA,
173
193
  }
174
194
  schema = schemas.get(table_type, {})
175
195
 
@@ -31,7 +31,7 @@ from agno.utils.log import log_debug, log_error, log_info, log_warning
31
31
  from agno.utils.string import generate_id
32
32
 
33
33
  try:
34
- from sqlalchemy import Index, UniqueConstraint, and_, func, select, update
34
+ from sqlalchemy import ForeignKey, Index, UniqueConstraint, and_, func, select, update
35
35
  from sqlalchemy.dialects import mysql
36
36
  from sqlalchemy.engine import Engine, create_engine
37
37
  from sqlalchemy.orm import scoped_session, sessionmaker
@@ -151,7 +151,10 @@ class SingleStoreDb(BaseDb):
151
151
  Table: SQLAlchemy Table object with column definitions
152
152
  """
153
153
  try:
154
- table_schema = get_table_schema_definition(table_type)
154
+ # Pass traces_table_name and db_schema for spans table foreign key resolution
155
+ table_schema = get_table_schema_definition(
156
+ table_type, traces_table_name=self.trace_table_name, db_schema=self.db_schema or "agno"
157
+ )
155
158
 
156
159
  columns: List[Column] = []
157
160
  # Get the columns from the table schema
@@ -207,7 +210,10 @@ class SingleStoreDb(BaseDb):
207
210
  """
208
211
  table_ref = f"{self.db_schema}.{table_name}" if self.db_schema else table_name
209
212
  try:
210
- table_schema = get_table_schema_definition(table_type)
213
+ # Pass traces_table_name and db_schema for spans table foreign key resolution
214
+ table_schema = get_table_schema_definition(
215
+ table_type, traces_table_name=self.trace_table_name, db_schema=self.db_schema or "agno"
216
+ ).copy()
211
217
 
212
218
  columns: List[Column] = []
213
219
  indexes: List[str] = []
@@ -227,6 +233,11 @@ class SingleStoreDb(BaseDb):
227
233
  if col_config.get("unique", False):
228
234
  column_kwargs["unique"] = True
229
235
  unique_constraints.append(col_name)
236
+
237
+ # Handle foreign key constraint
238
+ if "foreign_key" in col_config:
239
+ column_args.append(ForeignKey(col_config["foreign_key"]))
240
+
230
241
  columns.append(Column(*column_args, **column_kwargs))
231
242
 
232
243
  # Create the table object
@@ -31,7 +31,7 @@ from agno.utils.log import log_debug, log_error, log_info, log_warning
31
31
  from agno.utils.string import generate_id
32
32
 
33
33
  try:
34
- from sqlalchemy import Column, MetaData, String, Table, func, select, text
34
+ from sqlalchemy import Column, ForeignKey, MetaData, String, Table, func, select, text
35
35
  from sqlalchemy.dialects import sqlite
36
36
  from sqlalchemy.ext.asyncio import AsyncEngine, async_sessionmaker, create_async_engine
37
37
  from sqlalchemy.schema import Index, UniqueConstraint
@@ -174,7 +174,8 @@ class AsyncSqliteDb(AsyncBaseDb):
174
174
  Table: SQLAlchemy Table object
175
175
  """
176
176
  try:
177
- table_schema = get_table_schema_definition(table_type)
177
+ # Pass traces_table_name for spans table foreign key resolution
178
+ table_schema = get_table_schema_definition(table_type, traces_table_name=self.trace_table_name).copy()
178
179
 
179
180
  columns: List[Column] = []
180
181
  indexes: List[str] = []
@@ -196,6 +197,10 @@ class AsyncSqliteDb(AsyncBaseDb):
196
197
  column_kwargs["unique"] = True
197
198
  unique_constraints.append(col_name)
198
199
 
200
+ # Handle foreign key constraint
201
+ if "foreign_key" in col_config:
202
+ column_args.append(ForeignKey(col_config["foreign_key"]))
203
+
199
204
  columns.append(Column(*column_args, **column_kwargs)) # type: ignore
200
205
 
201
206
  # Create the table object
agno/db/sqlite/schemas.py CHANGED
@@ -111,25 +111,36 @@ TRACE_TABLE_SCHEMA = {
111
111
  "created_at": {"type": String, "nullable": False, "index": True}, # ISO 8601 datetime string
112
112
  }
113
113
 
114
- SPAN_TABLE_SCHEMA = {
115
- "span_id": {"type": String, "primary_key": True, "nullable": False},
116
- "trace_id": {
117
- "type": String,
118
- "nullable": False,
119
- "index": True,
120
- "foreign_key": "agno_traces.trace_id", # Foreign key to traces table
121
- },
122
- "parent_span_id": {"type": String, "nullable": True, "index": True},
123
- "name": {"type": String, "nullable": False},
124
- "span_kind": {"type": String, "nullable": False},
125
- "status_code": {"type": String, "nullable": False},
126
- "status_message": {"type": String, "nullable": True},
127
- "start_time": {"type": String, "nullable": False, "index": True}, # ISO 8601 datetime string
128
- "end_time": {"type": String, "nullable": False}, # ISO 8601 datetime string
129
- "duration_ms": {"type": BigInteger, "nullable": False},
130
- "attributes": {"type": JSON, "nullable": True},
131
- "created_at": {"type": String, "nullable": False, "index": True}, # ISO 8601 datetime string
132
- }
114
+
115
+ def _get_span_table_schema(traces_table_name: str = "agno_traces") -> dict[str, Any]:
116
+ """Get the span table schema with the correct foreign key reference.
117
+
118
+ Args:
119
+ traces_table_name: The name of the traces table to reference in the foreign key.
120
+
121
+ Returns:
122
+ The span table schema dictionary.
123
+ """
124
+ return {
125
+ "span_id": {"type": String, "primary_key": True, "nullable": False},
126
+ "trace_id": {
127
+ "type": String,
128
+ "nullable": False,
129
+ "index": True,
130
+ "foreign_key": f"{traces_table_name}.trace_id",
131
+ },
132
+ "parent_span_id": {"type": String, "nullable": True, "index": True},
133
+ "name": {"type": String, "nullable": False},
134
+ "span_kind": {"type": String, "nullable": False},
135
+ "status_code": {"type": String, "nullable": False},
136
+ "status_message": {"type": String, "nullable": True},
137
+ "start_time": {"type": String, "nullable": False, "index": True}, # ISO 8601 datetime string
138
+ "end_time": {"type": String, "nullable": False}, # ISO 8601 datetime string
139
+ "duration_ms": {"type": BigInteger, "nullable": False},
140
+ "attributes": {"type": JSON, "nullable": True},
141
+ "created_at": {"type": String, "nullable": False, "index": True}, # ISO 8601 datetime string
142
+ }
143
+
133
144
 
134
145
  CULTURAL_KNOWLEDGE_TABLE_SCHEMA = {
135
146
  "id": {"type": String, "primary_key": True, "nullable": False},
@@ -152,16 +163,21 @@ VERSIONS_TABLE_SCHEMA = {
152
163
  }
153
164
 
154
165
 
155
- def get_table_schema_definition(table_type: str) -> dict[str, Any]:
166
+ def get_table_schema_definition(table_type: str, traces_table_name: str = "agno_traces") -> dict[str, Any]:
156
167
  """
157
168
  Get the expected schema definition for the given table.
158
169
 
159
170
  Args:
160
171
  table_type (str): The type of table to get the schema for.
172
+ traces_table_name (str): The name of the traces table (used for spans foreign key).
161
173
 
162
174
  Returns:
163
175
  Dict[str, Any]: Dictionary containing column definitions for the table
164
176
  """
177
+ # Handle spans table specially to resolve the foreign key reference
178
+ if table_type == "spans":
179
+ return _get_span_table_schema(traces_table_name)
180
+
165
181
  schemas = {
166
182
  "sessions": SESSION_TABLE_SCHEMA,
167
183
  "evals": EVAL_TABLE_SCHEMA,
@@ -169,7 +185,6 @@ def get_table_schema_definition(table_type: str) -> dict[str, Any]:
169
185
  "memories": USER_MEMORY_TABLE_SCHEMA,
170
186
  "knowledge": KNOWLEDGE_TABLE_SCHEMA,
171
187
  "traces": TRACE_TABLE_SCHEMA,
172
- "spans": SPAN_TABLE_SCHEMA,
173
188
  "culture": CULTURAL_KNOWLEDGE_TABLE_SCHEMA,
174
189
  "versions": VERSIONS_TABLE_SCHEMA,
175
190
  }
agno/db/sqlite/sqlite.py CHANGED
@@ -173,7 +173,8 @@ class SqliteDb(BaseDb):
173
173
  Table: SQLAlchemy Table object
174
174
  """
175
175
  try:
176
- table_schema = get_table_schema_definition(table_type).copy()
176
+ # Pass traces_table_name for spans table foreign key resolution
177
+ table_schema = get_table_schema_definition(table_type, traces_table_name=self.trace_table_name).copy()
177
178
 
178
179
  columns: List[Column] = []
179
180
  indexes: List[str] = []
@@ -197,12 +198,7 @@ class SqliteDb(BaseDb):
197
198
 
198
199
  # Handle foreign key constraint
199
200
  if "foreign_key" in col_config:
200
- fk_ref = col_config["foreign_key"]
201
- # For spans table, dynamically replace the traces table reference
202
- # with the actual trace table name configured for this db instance
203
- if table_type == "spans" and "trace_id" in fk_ref:
204
- fk_ref = f"{self.trace_table_name}.trace_id"
205
- column_args.append(ForeignKey(fk_ref))
201
+ column_args.append(ForeignKey(col_config["foreign_key"]))
206
202
 
207
203
  columns.append(Column(*column_args, **column_kwargs)) # type: ignore
208
204