agno 2.4.0__py3-none-any.whl → 2.4.2__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 (41) hide show
  1. agno/db/firestore/firestore.py +58 -65
  2. agno/db/mysql/async_mysql.py +47 -55
  3. agno/db/postgres/async_postgres.py +52 -61
  4. agno/db/postgres/postgres.py +25 -12
  5. agno/db/sqlite/async_sqlite.py +52 -61
  6. agno/db/sqlite/sqlite.py +24 -11
  7. agno/integrations/discord/client.py +12 -1
  8. agno/knowledge/knowledge.py +1511 -47
  9. agno/knowledge/reader/csv_reader.py +231 -8
  10. agno/knowledge/reader/field_labeled_csv_reader.py +167 -3
  11. agno/knowledge/reader/reader_factory.py +8 -1
  12. agno/knowledge/remote_content/__init__.py +33 -0
  13. agno/knowledge/remote_content/config.py +266 -0
  14. agno/knowledge/remote_content/remote_content.py +105 -17
  15. agno/models/base.py +12 -2
  16. agno/models/cerebras/cerebras.py +34 -2
  17. agno/models/n1n/__init__.py +3 -0
  18. agno/models/n1n/n1n.py +57 -0
  19. agno/models/ollama/__init__.py +2 -0
  20. agno/models/ollama/responses.py +100 -0
  21. agno/models/openai/__init__.py +2 -0
  22. agno/models/openai/chat.py +18 -1
  23. agno/models/openai/open_responses.py +46 -0
  24. agno/models/openrouter/__init__.py +2 -0
  25. agno/models/openrouter/responses.py +146 -0
  26. agno/models/perplexity/perplexity.py +2 -0
  27. agno/os/interfaces/slack/router.py +10 -1
  28. agno/os/interfaces/whatsapp/router.py +6 -0
  29. agno/os/routers/components/components.py +10 -1
  30. agno/os/routers/knowledge/knowledge.py +125 -0
  31. agno/os/routers/knowledge/schemas.py +12 -0
  32. agno/run/agent.py +2 -0
  33. agno/team/team.py +20 -4
  34. agno/vectordb/lightrag/lightrag.py +7 -6
  35. agno/vectordb/milvus/milvus.py +79 -48
  36. agno/vectordb/pgvector/pgvector.py +3 -3
  37. {agno-2.4.0.dist-info → agno-2.4.2.dist-info}/METADATA +4 -1
  38. {agno-2.4.0.dist-info → agno-2.4.2.dist-info}/RECORD +41 -35
  39. {agno-2.4.0.dist-info → agno-2.4.2.dist-info}/WHEEL +1 -1
  40. {agno-2.4.0.dist-info → agno-2.4.2.dist-info}/licenses/LICENSE +0 -0
  41. {agno-2.4.0.dist-info → agno-2.4.2.dist-info}/top_level.txt +0 -0
@@ -263,95 +263,86 @@ class AsyncSqliteDb(AsyncBaseDb):
263
263
 
264
264
  async def _get_table(self, table_type: str, create_table_if_not_found: Optional[bool] = False) -> Optional[Table]:
265
265
  if table_type == "sessions":
266
- if not hasattr(self, "session_table"):
267
- self.session_table = await self._get_or_create_table(
268
- table_name=self.session_table_name,
269
- table_type=table_type,
270
- create_table_if_not_found=create_table_if_not_found,
271
- )
266
+ self.session_table = await self._get_or_create_table(
267
+ table_name=self.session_table_name,
268
+ table_type=table_type,
269
+ create_table_if_not_found=create_table_if_not_found,
270
+ )
272
271
  return self.session_table
273
272
 
274
273
  elif table_type == "memories":
275
- if not hasattr(self, "memory_table"):
276
- self.memory_table = await self._get_or_create_table(
277
- table_name=self.memory_table_name,
278
- table_type="memories",
279
- create_table_if_not_found=create_table_if_not_found,
280
- )
274
+ self.memory_table = await self._get_or_create_table(
275
+ table_name=self.memory_table_name,
276
+ table_type="memories",
277
+ create_table_if_not_found=create_table_if_not_found,
278
+ )
281
279
  return self.memory_table
282
280
 
283
281
  elif table_type == "metrics":
284
- if not hasattr(self, "metrics_table"):
285
- self.metrics_table = await self._get_or_create_table(
286
- table_name=self.metrics_table_name,
287
- table_type="metrics",
288
- create_table_if_not_found=create_table_if_not_found,
289
- )
282
+ self.metrics_table = await self._get_or_create_table(
283
+ table_name=self.metrics_table_name,
284
+ table_type="metrics",
285
+ create_table_if_not_found=create_table_if_not_found,
286
+ )
290
287
  return self.metrics_table
291
288
 
292
289
  elif table_type == "evals":
293
- if not hasattr(self, "eval_table"):
294
- self.eval_table = await self._get_or_create_table(
295
- table_name=self.eval_table_name,
296
- table_type="evals",
297
- create_table_if_not_found=create_table_if_not_found,
298
- )
290
+ self.eval_table = await self._get_or_create_table(
291
+ table_name=self.eval_table_name,
292
+ table_type="evals",
293
+ create_table_if_not_found=create_table_if_not_found,
294
+ )
299
295
  return self.eval_table
300
296
 
301
297
  elif table_type == "knowledge":
302
- if not hasattr(self, "knowledge_table"):
303
- self.knowledge_table = await self._get_or_create_table(
304
- table_name=self.knowledge_table_name,
305
- table_type="knowledge",
306
- create_table_if_not_found=create_table_if_not_found,
307
- )
298
+ self.knowledge_table = await self._get_or_create_table(
299
+ table_name=self.knowledge_table_name,
300
+ table_type="knowledge",
301
+ create_table_if_not_found=create_table_if_not_found,
302
+ )
308
303
  return self.knowledge_table
309
304
 
310
305
  elif table_type == "culture":
311
- if not hasattr(self, "culture_table"):
312
- self.culture_table = await self._get_or_create_table(
313
- table_name=self.culture_table_name,
314
- table_type="culture",
315
- create_table_if_not_found=create_table_if_not_found,
316
- )
306
+ self.culture_table = await self._get_or_create_table(
307
+ table_name=self.culture_table_name,
308
+ table_type="culture",
309
+ create_table_if_not_found=create_table_if_not_found,
310
+ )
317
311
  return self.culture_table
318
312
 
319
313
  elif table_type == "versions":
320
- if not hasattr(self, "versions_table"):
321
- self.versions_table = await self._get_or_create_table(
322
- table_name=self.versions_table_name,
323
- table_type="versions",
324
- create_table_if_not_found=create_table_if_not_found,
325
- )
314
+ self.versions_table = await self._get_or_create_table(
315
+ table_name=self.versions_table_name,
316
+ table_type="versions",
317
+ create_table_if_not_found=create_table_if_not_found,
318
+ )
326
319
  return self.versions_table
327
320
 
328
321
  elif table_type == "traces":
329
- if not hasattr(self, "traces_table"):
330
- self.traces_table = await self._get_or_create_table(
331
- table_name=self.trace_table_name,
332
- table_type="traces",
333
- create_table_if_not_found=create_table_if_not_found,
334
- )
322
+ self.traces_table = await self._get_or_create_table(
323
+ table_name=self.trace_table_name,
324
+ table_type="traces",
325
+ create_table_if_not_found=create_table_if_not_found,
326
+ )
335
327
  return self.traces_table
336
328
 
337
329
  elif table_type == "spans":
338
- if not hasattr(self, "spans_table"):
339
- # Ensure traces table exists first (spans has FK to traces)
330
+ # Ensure traces table exists first (spans has FK to traces)
331
+ if create_table_if_not_found:
340
332
  await self._get_table(table_type="traces", create_table_if_not_found=True)
341
- self.spans_table = await self._get_or_create_table(
342
- table_name=self.span_table_name,
343
- table_type="spans",
344
- create_table_if_not_found=create_table_if_not_found,
345
- )
333
+ self.spans_table = await self._get_or_create_table(
334
+ table_name=self.span_table_name,
335
+ table_type="spans",
336
+ create_table_if_not_found=create_table_if_not_found,
337
+ )
346
338
  return self.spans_table
347
339
 
348
340
  elif table_type == "learnings":
349
- if not hasattr(self, "learnings_table"):
350
- self.learnings_table = await self._get_or_create_table(
351
- table_name=self.learnings_table_name,
352
- table_type="learnings",
353
- create_table_if_not_found=create_table_if_not_found,
354
- )
341
+ self.learnings_table = await self._get_or_create_table(
342
+ table_name=self.learnings_table_name,
343
+ table_type="learnings",
344
+ create_table_if_not_found=create_table_if_not_found,
345
+ )
355
346
  return self.learnings_table
356
347
 
357
348
  else:
agno/db/sqlite/sqlite.py CHANGED
@@ -3475,7 +3475,7 @@ class SqliteDb(BaseDb):
3475
3475
 
3476
3476
  Args:
3477
3477
  component_id: The component ID.
3478
- version: Specific version number. If None, uses current.
3478
+ version: Specific version number. If None, uses current or latest draft.
3479
3479
  label: Config label to lookup. Ignored if version is provided.
3480
3480
 
3481
3481
  Returns:
@@ -3490,16 +3490,22 @@ class SqliteDb(BaseDb):
3490
3490
 
3491
3491
  with self.Session() as sess:
3492
3492
  # Always verify component exists and is not deleted
3493
- component = sess.execute(
3494
- select(components_table.c.current_version).where(
3495
- components_table.c.component_id == component_id,
3496
- components_table.c.deleted_at.is_(None),
3493
+ component_row = (
3494
+ sess.execute(
3495
+ select(components_table.c.current_version, components_table.c.component_id).where(
3496
+ components_table.c.component_id == component_id,
3497
+ components_table.c.deleted_at.is_(None),
3498
+ )
3497
3499
  )
3498
- ).fetchone()
3500
+ .mappings()
3501
+ .one_or_none()
3502
+ )
3499
3503
 
3500
- if component is None:
3504
+ if component_row is None:
3501
3505
  return None
3502
3506
 
3507
+ current_version = component_row["current_version"]
3508
+
3503
3509
  if version is not None:
3504
3510
  stmt = select(configs_table).where(
3505
3511
  configs_table.c.component_id == component_id,
@@ -3510,12 +3516,19 @@ class SqliteDb(BaseDb):
3510
3516
  configs_table.c.component_id == component_id,
3511
3517
  configs_table.c.label == label,
3512
3518
  )
3513
- else:
3514
- if component.current_version is None:
3515
- return None
3519
+ elif current_version is not None:
3520
+ # Use the current published version
3516
3521
  stmt = select(configs_table).where(
3517
3522
  configs_table.c.component_id == component_id,
3518
- configs_table.c.version == component.current_version,
3523
+ configs_table.c.version == current_version,
3524
+ )
3525
+ else:
3526
+ # No current_version set (draft only) - get the latest version
3527
+ stmt = (
3528
+ select(configs_table)
3529
+ .where(configs_table.c.component_id == component_id)
3530
+ .order_by(configs_table.c.version.desc())
3531
+ .limit(1)
3519
3532
  )
3520
3533
 
3521
3534
  result = sess.execute(stmt).fetchone()
@@ -7,7 +7,7 @@ import requests
7
7
  from agno.agent.agent import Agent, RunOutput
8
8
  from agno.media import Audio, File, Image, Video
9
9
  from agno.team.team import Team, TeamRunOutput
10
- from agno.utils.log import log_info, log_warning
10
+ from agno.utils.log import log_error, log_info, log_warning
11
11
  from agno.utils.message import get_text_from_message
12
12
 
13
13
  try:
@@ -126,6 +126,11 @@ class DiscordClient:
126
126
  audio=[Audio(url=message_audio)] if message_audio else None,
127
127
  files=[File(content=message_file)] if message_file else None,
128
128
  )
129
+ if agent_response.status == "ERROR":
130
+ log_error(agent_response.content)
131
+ agent_response.content = (
132
+ "Sorry, there was an error processing your message. Please try again later."
133
+ )
129
134
  await self._handle_response_in_thread(agent_response, thread)
130
135
  elif self.team:
131
136
  self.team.additional_context = additional_context
@@ -138,6 +143,12 @@ class DiscordClient:
138
143
  audio=[Audio(url=message_audio)] if message_audio else None,
139
144
  files=[File(content=message_file)] if message_file else None,
140
145
  )
146
+ if team_response.status == "ERROR":
147
+ log_error(team_response.content)
148
+ team_response.content = (
149
+ "Sorry, there was an error processing your message. Please try again later."
150
+ )
151
+
141
152
  await self._handle_response_in_thread(team_response, thread)
142
153
 
143
154
  async def handle_hitl(