agno 2.0.11__py3-none-any.whl → 2.1.1__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 (93) hide show
  1. agno/agent/agent.py +607 -176
  2. agno/db/in_memory/in_memory_db.py +42 -29
  3. agno/db/mongo/mongo.py +65 -66
  4. agno/db/postgres/postgres.py +6 -4
  5. agno/db/utils.py +50 -22
  6. agno/exceptions.py +62 -1
  7. agno/guardrails/__init__.py +6 -0
  8. agno/guardrails/base.py +19 -0
  9. agno/guardrails/openai.py +144 -0
  10. agno/guardrails/pii.py +94 -0
  11. agno/guardrails/prompt_injection.py +51 -0
  12. agno/knowledge/embedder/aws_bedrock.py +9 -4
  13. agno/knowledge/embedder/azure_openai.py +54 -0
  14. agno/knowledge/embedder/base.py +2 -0
  15. agno/knowledge/embedder/cohere.py +184 -5
  16. agno/knowledge/embedder/google.py +79 -1
  17. agno/knowledge/embedder/huggingface.py +9 -4
  18. agno/knowledge/embedder/jina.py +63 -0
  19. agno/knowledge/embedder/mistral.py +78 -11
  20. agno/knowledge/embedder/ollama.py +5 -0
  21. agno/knowledge/embedder/openai.py +18 -54
  22. agno/knowledge/embedder/voyageai.py +69 -16
  23. agno/knowledge/knowledge.py +11 -4
  24. agno/knowledge/reader/pdf_reader.py +4 -3
  25. agno/knowledge/reader/website_reader.py +3 -2
  26. agno/models/base.py +125 -32
  27. agno/models/cerebras/cerebras.py +1 -0
  28. agno/models/cerebras/cerebras_openai.py +1 -0
  29. agno/models/dashscope/dashscope.py +1 -0
  30. agno/models/google/gemini.py +27 -5
  31. agno/models/openai/chat.py +13 -4
  32. agno/models/openai/responses.py +1 -1
  33. agno/models/perplexity/perplexity.py +2 -3
  34. agno/models/requesty/__init__.py +5 -0
  35. agno/models/requesty/requesty.py +49 -0
  36. agno/models/vllm/vllm.py +1 -0
  37. agno/models/xai/xai.py +1 -0
  38. agno/os/app.py +98 -126
  39. agno/os/interfaces/__init__.py +1 -0
  40. agno/os/interfaces/agui/agui.py +21 -5
  41. agno/os/interfaces/base.py +4 -2
  42. agno/os/interfaces/slack/slack.py +13 -8
  43. agno/os/interfaces/whatsapp/router.py +2 -0
  44. agno/os/interfaces/whatsapp/whatsapp.py +12 -5
  45. agno/os/mcp.py +2 -2
  46. agno/os/middleware/__init__.py +7 -0
  47. agno/os/middleware/jwt.py +233 -0
  48. agno/os/router.py +182 -46
  49. agno/os/routers/home.py +2 -2
  50. agno/os/routers/memory/memory.py +23 -1
  51. agno/os/routers/memory/schemas.py +1 -1
  52. agno/os/routers/session/session.py +20 -3
  53. agno/os/utils.py +74 -8
  54. agno/run/agent.py +120 -77
  55. agno/run/base.py +2 -13
  56. agno/run/team.py +115 -72
  57. agno/run/workflow.py +5 -15
  58. agno/session/summary.py +9 -10
  59. agno/session/team.py +2 -1
  60. agno/team/team.py +721 -169
  61. agno/tools/firecrawl.py +4 -4
  62. agno/tools/function.py +42 -2
  63. agno/tools/knowledge.py +3 -3
  64. agno/tools/searxng.py +2 -2
  65. agno/tools/serper.py +2 -2
  66. agno/tools/spider.py +2 -2
  67. agno/tools/workflow.py +4 -5
  68. agno/utils/events.py +66 -1
  69. agno/utils/hooks.py +57 -0
  70. agno/utils/media.py +11 -9
  71. agno/utils/print_response/agent.py +43 -5
  72. agno/utils/print_response/team.py +48 -12
  73. agno/utils/serialize.py +32 -0
  74. agno/vectordb/cassandra/cassandra.py +44 -4
  75. agno/vectordb/chroma/chromadb.py +79 -8
  76. agno/vectordb/clickhouse/clickhousedb.py +43 -6
  77. agno/vectordb/couchbase/couchbase.py +76 -5
  78. agno/vectordb/lancedb/lance_db.py +38 -3
  79. agno/vectordb/milvus/milvus.py +76 -4
  80. agno/vectordb/mongodb/mongodb.py +76 -4
  81. agno/vectordb/pgvector/pgvector.py +50 -6
  82. agno/vectordb/pineconedb/pineconedb.py +39 -2
  83. agno/vectordb/qdrant/qdrant.py +76 -26
  84. agno/vectordb/singlestore/singlestore.py +77 -4
  85. agno/vectordb/upstashdb/upstashdb.py +42 -2
  86. agno/vectordb/weaviate/weaviate.py +39 -3
  87. agno/workflow/types.py +5 -6
  88. agno/workflow/workflow.py +58 -2
  89. {agno-2.0.11.dist-info → agno-2.1.1.dist-info}/METADATA +4 -3
  90. {agno-2.0.11.dist-info → agno-2.1.1.dist-info}/RECORD +93 -82
  91. {agno-2.0.11.dist-info → agno-2.1.1.dist-info}/WHEEL +0 -0
  92. {agno-2.0.11.dist-info → agno-2.1.1.dist-info}/licenses/LICENSE +0 -0
  93. {agno-2.0.11.dist-info → agno-2.1.1.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  import time
2
+ from copy import deepcopy
2
3
  from datetime import date, datetime, timedelta, timezone
3
4
  from typing import Any, Dict, List, Optional, Tuple, Union
4
5
  from uuid import uuid4
@@ -107,15 +108,17 @@ class InMemoryDb(BaseDb):
107
108
  if session_data.get("session_type") != session_type_value:
108
109
  continue
109
110
 
111
+ session_data_copy = deepcopy(session_data)
112
+
110
113
  if not deserialize:
111
- return session_data
114
+ return session_data_copy
112
115
 
113
116
  if session_type == SessionType.AGENT:
114
- return AgentSession.from_dict(session_data)
117
+ return AgentSession.from_dict(session_data_copy)
115
118
  elif session_type == SessionType.TEAM:
116
- return TeamSession.from_dict(session_data)
119
+ return TeamSession.from_dict(session_data_copy)
117
120
  else:
118
- return WorkflowSession.from_dict(session_data)
121
+ return WorkflowSession.from_dict(session_data_copy)
119
122
 
120
123
  return None
121
124
 
@@ -188,7 +191,7 @@ class InMemoryDb(BaseDb):
188
191
  if session_data.get("session_type") != session_type_value:
189
192
  continue
190
193
 
191
- filtered_sessions.append(session_data)
194
+ filtered_sessions.append(deepcopy(session_data))
192
195
 
193
196
  total_count = len(filtered_sessions)
194
197
 
@@ -233,15 +236,16 @@ class InMemoryDb(BaseDb):
233
236
 
234
237
  log_debug(f"Renamed session with id '{session_id}' to '{session_name}'")
235
238
 
239
+ session_copy = deepcopy(session)
236
240
  if not deserialize:
237
- return session
241
+ return session_copy
238
242
 
239
243
  if session_type == SessionType.AGENT:
240
- return AgentSession.from_dict(session)
244
+ return AgentSession.from_dict(session_copy)
241
245
  elif session_type == SessionType.TEAM:
242
- return TeamSession.from_dict(session)
246
+ return TeamSession.from_dict(session_copy)
243
247
  else:
244
- return WorkflowSession.from_dict(session)
248
+ return WorkflowSession.from_dict(session_copy)
245
249
 
246
250
  return None
247
251
 
@@ -269,22 +273,26 @@ class InMemoryDb(BaseDb):
269
273
  if existing_session.get("session_id") == session_dict.get("session_id") and self._matches_session_key(
270
274
  existing_session, session
271
275
  ):
272
- # Update existing session
273
276
  session_dict["updated_at"] = int(time.time())
274
- self._sessions[i] = session_dict
277
+ self._sessions[i] = deepcopy(session_dict)
275
278
  session_updated = True
276
279
  break
277
280
 
278
281
  if not session_updated:
279
- # Add new session
280
282
  session_dict["created_at"] = session_dict.get("created_at", int(time.time()))
281
283
  session_dict["updated_at"] = session_dict.get("created_at")
282
- self._sessions.append(session_dict)
284
+ self._sessions.append(deepcopy(session_dict))
283
285
 
286
+ session_dict_copy = deepcopy(session_dict)
284
287
  if not deserialize:
285
- return session_dict
288
+ return session_dict_copy
286
289
 
287
- return session
290
+ if session_dict_copy["session_type"] == SessionType.AGENT:
291
+ return AgentSession.from_dict(session_dict_copy)
292
+ elif session_dict_copy["session_type"] == SessionType.TEAM:
293
+ return TeamSession.from_dict(session_dict_copy)
294
+ else:
295
+ return WorkflowSession.from_dict(session_dict_copy)
288
296
 
289
297
  except Exception as e:
290
298
  log_error(f"Exception upserting session: {e}")
@@ -378,9 +386,10 @@ class InMemoryDb(BaseDb):
378
386
  try:
379
387
  for memory_data in self._memories:
380
388
  if memory_data.get("memory_id") == memory_id:
389
+ memory_data_copy = deepcopy(memory_data)
381
390
  if not deserialize:
382
- return memory_data
383
- return UserMemory.from_dict(memory_data)
391
+ return memory_data_copy
392
+ return UserMemory.from_dict(memory_data_copy)
384
393
 
385
394
  return None
386
395
 
@@ -420,7 +429,7 @@ class InMemoryDb(BaseDb):
420
429
  if search_content.lower() not in memory_content.lower():
421
430
  continue
422
431
 
423
- filtered_memories.append(memory_data)
432
+ filtered_memories.append(deepcopy(memory_data))
424
433
 
425
434
  total_count = len(filtered_memories)
426
435
 
@@ -499,9 +508,11 @@ class InMemoryDb(BaseDb):
499
508
  if not memory_updated:
500
509
  self._memories.append(memory_dict)
501
510
 
511
+ memory_dict_copy = deepcopy(memory_dict)
502
512
  if not deserialize:
503
- return memory_dict
504
- return UserMemory.from_dict(memory_dict)
513
+ return memory_dict_copy
514
+
515
+ return UserMemory.from_dict(memory_dict_copy)
505
516
 
506
517
  except Exception as e:
507
518
  log_warning(f"Exception upserting user memory: {e}")
@@ -657,8 +668,8 @@ class InMemoryDb(BaseDb):
657
668
  # Only include necessary fields for metrics
658
669
  filtered_session = {
659
670
  "user_id": session.get("user_id"),
660
- "session_data": session.get("session_data"),
661
- "runs": session.get("runs"),
671
+ "session_data": deepcopy(session.get("session_data")),
672
+ "runs": deepcopy(session.get("runs")),
662
673
  "created_at": session.get("created_at"),
663
674
  "session_type": session.get("session_type"),
664
675
  }
@@ -688,7 +699,7 @@ class InMemoryDb(BaseDb):
688
699
  if ending_date and metric_date > ending_date:
689
700
  continue
690
701
 
691
- filtered_metrics.append(metric)
702
+ filtered_metrics.append(deepcopy(metric))
692
703
 
693
704
  updated_at = metric.get("updated_at")
694
705
  if updated_at and (latest_updated_at is None or updated_at > latest_updated_at):
@@ -763,7 +774,7 @@ class InMemoryDb(BaseDb):
763
774
  Exception: If an error occurs during retrieval.
764
775
  """
765
776
  try:
766
- knowledge_items = self._knowledge.copy()
777
+ knowledge_items = [deepcopy(item) for item in self._knowledge]
767
778
 
768
779
  total_count = len(knowledge_items)
769
780
 
@@ -858,9 +869,10 @@ class InMemoryDb(BaseDb):
858
869
  try:
859
870
  for run_data in self._eval_runs:
860
871
  if run_data.get("run_id") == eval_run_id:
872
+ run_data_copy = deepcopy(run_data)
861
873
  if not deserialize:
862
- return run_data
863
- return EvalRunRecord.model_validate(run_data)
874
+ return run_data_copy
875
+ return EvalRunRecord.model_validate(run_data_copy)
864
876
 
865
877
  return None
866
878
 
@@ -906,7 +918,7 @@ class InMemoryDb(BaseDb):
906
918
  elif filter_type == EvalFilterType.WORKFLOW and run_data.get("workflow_id") is None:
907
919
  continue
908
920
 
909
- filtered_runs.append(run_data)
921
+ filtered_runs.append(deepcopy(run_data))
910
922
 
911
923
  total_count = len(filtered_runs)
912
924
 
@@ -945,10 +957,11 @@ class InMemoryDb(BaseDb):
945
957
 
946
958
  log_debug(f"Renamed eval run with id '{eval_run_id}' to '{name}'")
947
959
 
960
+ run_data_copy = deepcopy(run_data)
948
961
  if not deserialize:
949
- return run_data
962
+ return run_data_copy
950
963
 
951
- return EvalRunRecord.model_validate(run_data)
964
+ return EvalRunRecord.model_validate(run_data_copy)
952
965
 
953
966
  return None
954
967
 
agno/db/mongo/mongo.py CHANGED
@@ -16,7 +16,7 @@ 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, serialize_session_json_fields
19
+ from agno.db.utils import deserialize_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
22
  from agno.utils.string import generate_id
@@ -282,7 +282,6 @@ class MongoDb(BaseDb):
282
282
  return None
283
283
 
284
284
  session = deserialize_session_json_fields(result)
285
-
286
285
  if not deserialize:
287
286
  return session
288
287
 
@@ -385,7 +384,6 @@ class MongoDb(BaseDb):
385
384
  records = list(cursor)
386
385
  if records is None:
387
386
  return [] if deserialize else ([], 0)
388
-
389
387
  sessions_raw = [deserialize_session_json_fields(record) for record in records]
390
388
 
391
389
  if not deserialize:
@@ -489,25 +487,25 @@ class MongoDb(BaseDb):
489
487
  if collection is None:
490
488
  return None
491
489
 
492
- serialized_session_dict = serialize_session_json_fields(session.to_dict())
490
+ session_dict = session.to_dict()
493
491
 
494
492
  if isinstance(session, AgentSession):
495
493
  record = {
496
- "session_id": serialized_session_dict.get("session_id"),
494
+ "session_id": session_dict.get("session_id"),
497
495
  "session_type": SessionType.AGENT.value,
498
- "agent_id": serialized_session_dict.get("agent_id"),
499
- "user_id": serialized_session_dict.get("user_id"),
500
- "runs": serialized_session_dict.get("runs"),
501
- "agent_data": serialized_session_dict.get("agent_data"),
502
- "session_data": serialized_session_dict.get("session_data"),
503
- "summary": serialized_session_dict.get("summary"),
504
- "metadata": serialized_session_dict.get("metadata"),
505
- "created_at": serialized_session_dict.get("created_at"),
496
+ "agent_id": session_dict.get("agent_id"),
497
+ "user_id": session_dict.get("user_id"),
498
+ "runs": session_dict.get("runs"),
499
+ "agent_data": session_dict.get("agent_data"),
500
+ "session_data": session_dict.get("session_data"),
501
+ "summary": session_dict.get("summary"),
502
+ "metadata": session_dict.get("metadata"),
503
+ "created_at": session_dict.get("created_at"),
506
504
  "updated_at": int(time.time()),
507
505
  }
508
506
 
509
507
  result = collection.find_one_and_replace(
510
- filter={"session_id": serialized_session_dict.get("session_id")},
508
+ filter={"session_id": session_dict.get("session_id")},
511
509
  replacement=record,
512
510
  upsert=True,
513
511
  return_document=ReturnDocument.AFTER,
@@ -515,7 +513,7 @@ class MongoDb(BaseDb):
515
513
  if not result:
516
514
  return None
517
515
 
518
- session = deserialize_session_json_fields(result) # type: ignore
516
+ session = result # type: ignore
519
517
 
520
518
  if not deserialize:
521
519
  return session
@@ -524,21 +522,21 @@ class MongoDb(BaseDb):
524
522
 
525
523
  elif isinstance(session, TeamSession):
526
524
  record = {
527
- "session_id": serialized_session_dict.get("session_id"),
525
+ "session_id": session_dict.get("session_id"),
528
526
  "session_type": SessionType.TEAM.value,
529
- "team_id": serialized_session_dict.get("team_id"),
530
- "user_id": serialized_session_dict.get("user_id"),
531
- "runs": serialized_session_dict.get("runs"),
532
- "team_data": serialized_session_dict.get("team_data"),
533
- "session_data": serialized_session_dict.get("session_data"),
534
- "summary": serialized_session_dict.get("summary"),
535
- "metadata": serialized_session_dict.get("metadata"),
536
- "created_at": serialized_session_dict.get("created_at"),
527
+ "team_id": session_dict.get("team_id"),
528
+ "user_id": session_dict.get("user_id"),
529
+ "runs": session_dict.get("runs"),
530
+ "team_data": session_dict.get("team_data"),
531
+ "session_data": session_dict.get("session_data"),
532
+ "summary": session_dict.get("summary"),
533
+ "metadata": session_dict.get("metadata"),
534
+ "created_at": session_dict.get("created_at"),
537
535
  "updated_at": int(time.time()),
538
536
  }
539
537
 
540
538
  result = collection.find_one_and_replace(
541
- filter={"session_id": serialized_session_dict.get("session_id")},
539
+ filter={"session_id": session_dict.get("session_id")},
542
540
  replacement=record,
543
541
  upsert=True,
544
542
  return_document=ReturnDocument.AFTER,
@@ -546,7 +544,8 @@ class MongoDb(BaseDb):
546
544
  if not result:
547
545
  return None
548
546
 
549
- session = deserialize_session_json_fields(result) # type: ignore
547
+ # MongoDB stores native objects, no deserialization needed for document fields
548
+ session = result # type: ignore
550
549
 
551
550
  if not deserialize:
552
551
  return session
@@ -555,21 +554,21 @@ class MongoDb(BaseDb):
555
554
 
556
555
  else:
557
556
  record = {
558
- "session_id": serialized_session_dict.get("session_id"),
557
+ "session_id": session_dict.get("session_id"),
559
558
  "session_type": SessionType.WORKFLOW.value,
560
- "workflow_id": serialized_session_dict.get("workflow_id"),
561
- "user_id": serialized_session_dict.get("user_id"),
562
- "runs": serialized_session_dict.get("runs"),
563
- "workflow_data": serialized_session_dict.get("workflow_data"),
564
- "session_data": serialized_session_dict.get("session_data"),
565
- "summary": serialized_session_dict.get("summary"),
566
- "metadata": serialized_session_dict.get("metadata"),
567
- "created_at": serialized_session_dict.get("created_at"),
559
+ "workflow_id": session_dict.get("workflow_id"),
560
+ "user_id": session_dict.get("user_id"),
561
+ "runs": session_dict.get("runs"),
562
+ "workflow_data": session_dict.get("workflow_data"),
563
+ "session_data": session_dict.get("session_data"),
564
+ "summary": session_dict.get("summary"),
565
+ "metadata": session_dict.get("metadata"),
566
+ "created_at": session_dict.get("created_at"),
568
567
  "updated_at": int(time.time()),
569
568
  }
570
569
 
571
570
  result = collection.find_one_and_replace(
572
- filter={"session_id": serialized_session_dict.get("session_id")},
571
+ filter={"session_id": session_dict.get("session_id")},
573
572
  replacement=record,
574
573
  upsert=True,
575
574
  return_document=ReturnDocument.AFTER,
@@ -577,7 +576,7 @@ class MongoDb(BaseDb):
577
576
  if not result:
578
577
  return None
579
578
 
580
- session = deserialize_session_json_fields(result) # type: ignore
579
+ session = result # type: ignore
581
580
 
582
581
  if not deserialize:
583
582
  return session
@@ -628,48 +627,48 @@ class MongoDb(BaseDb):
628
627
  if session is None:
629
628
  continue
630
629
 
631
- serialized_session_dict = serialize_session_json_fields(session.to_dict())
630
+ session_dict = session.to_dict()
632
631
 
633
632
  if isinstance(session, AgentSession):
634
633
  record = {
635
- "session_id": serialized_session_dict.get("session_id"),
634
+ "session_id": session_dict.get("session_id"),
636
635
  "session_type": SessionType.AGENT.value,
637
- "agent_id": serialized_session_dict.get("agent_id"),
638
- "user_id": serialized_session_dict.get("user_id"),
639
- "runs": serialized_session_dict.get("runs"),
640
- "agent_data": serialized_session_dict.get("agent_data"),
641
- "session_data": serialized_session_dict.get("session_data"),
642
- "summary": serialized_session_dict.get("summary"),
643
- "metadata": serialized_session_dict.get("metadata"),
644
- "created_at": serialized_session_dict.get("created_at"),
636
+ "agent_id": session_dict.get("agent_id"),
637
+ "user_id": session_dict.get("user_id"),
638
+ "runs": session_dict.get("runs"),
639
+ "agent_data": session_dict.get("agent_data"),
640
+ "session_data": session_dict.get("session_data"),
641
+ "summary": session_dict.get("summary"),
642
+ "metadata": session_dict.get("metadata"),
643
+ "created_at": session_dict.get("created_at"),
645
644
  "updated_at": int(time.time()),
646
645
  }
647
646
  elif isinstance(session, TeamSession):
648
647
  record = {
649
- "session_id": serialized_session_dict.get("session_id"),
648
+ "session_id": session_dict.get("session_id"),
650
649
  "session_type": SessionType.TEAM.value,
651
- "team_id": serialized_session_dict.get("team_id"),
652
- "user_id": serialized_session_dict.get("user_id"),
653
- "runs": serialized_session_dict.get("runs"),
654
- "team_data": serialized_session_dict.get("team_data"),
655
- "session_data": serialized_session_dict.get("session_data"),
656
- "summary": serialized_session_dict.get("summary"),
657
- "metadata": serialized_session_dict.get("metadata"),
658
- "created_at": serialized_session_dict.get("created_at"),
650
+ "team_id": session_dict.get("team_id"),
651
+ "user_id": session_dict.get("user_id"),
652
+ "runs": session_dict.get("runs"),
653
+ "team_data": session_dict.get("team_data"),
654
+ "session_data": session_dict.get("session_data"),
655
+ "summary": session_dict.get("summary"),
656
+ "metadata": session_dict.get("metadata"),
657
+ "created_at": session_dict.get("created_at"),
659
658
  "updated_at": int(time.time()),
660
659
  }
661
660
  elif isinstance(session, WorkflowSession):
662
661
  record = {
663
- "session_id": serialized_session_dict.get("session_id"),
662
+ "session_id": session_dict.get("session_id"),
664
663
  "session_type": SessionType.WORKFLOW.value,
665
- "workflow_id": serialized_session_dict.get("workflow_id"),
666
- "user_id": serialized_session_dict.get("user_id"),
667
- "runs": serialized_session_dict.get("runs"),
668
- "workflow_data": serialized_session_dict.get("workflow_data"),
669
- "session_data": serialized_session_dict.get("session_data"),
670
- "summary": serialized_session_dict.get("summary"),
671
- "metadata": serialized_session_dict.get("metadata"),
672
- "created_at": serialized_session_dict.get("created_at"),
664
+ "workflow_id": session_dict.get("workflow_id"),
665
+ "user_id": session_dict.get("user_id"),
666
+ "runs": session_dict.get("runs"),
667
+ "workflow_data": session_dict.get("workflow_data"),
668
+ "session_data": session_dict.get("session_data"),
669
+ "summary": session_dict.get("summary"),
670
+ "metadata": session_dict.get("metadata"),
671
+ "created_at": session_dict.get("created_at"),
673
672
  "updated_at": int(time.time()),
674
673
  }
675
674
  else:
@@ -688,7 +687,7 @@ class MongoDb(BaseDb):
688
687
  cursor = collection.find({"session_id": {"$in": session_ids}})
689
688
 
690
689
  for doc in cursor:
691
- session_dict = deserialize_session_json_fields(doc)
690
+ session_dict = doc
692
691
 
693
692
  if deserialize:
694
693
  session_type = doc.get("session_type")
@@ -756,7 +756,7 @@ class PostgresDb(BaseDb):
756
756
  )
757
757
 
758
758
  with self.Session() as sess, sess.begin():
759
- stmt = postgresql.insert(table)
759
+ stmt: Any = postgresql.insert(table)
760
760
  update_columns = {
761
761
  col.name: stmt.excluded[col.name]
762
762
  for col in table.columns
@@ -1263,13 +1263,15 @@ class PostgresDb(BaseDb):
1263
1263
  results: List[Union[UserMemory, Dict[str, Any]]] = []
1264
1264
 
1265
1265
  with self.Session() as sess, sess.begin():
1266
- stmt = postgresql.insert(table)
1266
+ insert_stmt = postgresql.insert(table)
1267
1267
  update_columns = {
1268
- col.name: stmt.excluded[col.name]
1268
+ col.name: insert_stmt.excluded[col.name]
1269
1269
  for col in table.columns
1270
1270
  if col.name not in ["memory_id"] # Don't update primary key
1271
1271
  }
1272
- stmt = stmt.on_conflict_do_update(index_elements=["memory_id"], set_=update_columns).returning(table)
1272
+ stmt = insert_stmt.on_conflict_do_update(index_elements=["memory_id"], set_=update_columns).returning(
1273
+ table
1274
+ )
1273
1275
 
1274
1276
  result = sess.execute(stmt, memory_records)
1275
1277
  for row in result.fetchall():
agno/db/utils.py CHANGED
@@ -4,7 +4,6 @@ import json
4
4
  from datetime import date, datetime
5
5
  from uuid import UUID
6
6
 
7
- from agno.db.base import SessionType
8
7
  from agno.models.message import Message
9
8
  from agno.models.metrics import Metrics
10
9
 
@@ -55,34 +54,63 @@ def serialize_session_json_fields(session: dict) -> dict:
55
54
 
56
55
 
57
56
  def deserialize_session_json_fields(session: dict) -> dict:
58
- """Deserialize all JSON fields in the given Session dictionary.
57
+ """Deserialize JSON fields in the given Session dictionary.
59
58
 
60
59
  Args:
61
60
  session (dict): The dictionary to deserialize.
62
61
 
63
62
  Returns:
64
- dict: The dictionary with JSON fields deserialized.
63
+ dict: The dictionary with JSON string fields deserialized to objects.
65
64
  """
66
- if session.get("agent_data") is not None:
67
- session["agent_data"] = json.loads(session["agent_data"])
68
- if session.get("team_data") is not None:
69
- session["team_data"] = json.loads(session["team_data"])
70
- if session.get("workflow_data") is not None:
71
- session["workflow_data"] = json.loads(session["workflow_data"])
72
- if session.get("metadata") is not None:
73
- session["metadata"] = json.loads(session["metadata"])
74
- if session.get("chat_history") is not None:
75
- session["chat_history"] = json.loads(session["chat_history"])
76
- if session.get("summary") is not None:
77
- session["summary"] = json.loads(session["summary"])
65
+ from agno.utils.log import log_warning
66
+
67
+ if session.get("agent_data") is not None and isinstance(session["agent_data"], str):
68
+ try:
69
+ session["agent_data"] = json.loads(session["agent_data"])
70
+ except (json.JSONDecodeError, TypeError) as e:
71
+ log_warning(f"Warning: Could not parse agent_data as JSON, keeping as string: {e}")
72
+
73
+ if session.get("team_data") is not None and isinstance(session["team_data"], str):
74
+ try:
75
+ session["team_data"] = json.loads(session["team_data"])
76
+ except (json.JSONDecodeError, TypeError) as e:
77
+ log_warning(f"Warning: Could not parse team_data as JSON, keeping as string: {e}")
78
+
79
+ if session.get("workflow_data") is not None and isinstance(session["workflow_data"], str):
80
+ try:
81
+ session["workflow_data"] = json.loads(session["workflow_data"])
82
+ except (json.JSONDecodeError, TypeError) as e:
83
+ log_warning(f"Warning: Could not parse workflow_data as JSON, keeping as string: {e}")
84
+
85
+ if session.get("metadata") is not None and isinstance(session["metadata"], str):
86
+ try:
87
+ session["metadata"] = json.loads(session["metadata"])
88
+ except (json.JSONDecodeError, TypeError) as e:
89
+ log_warning(f"Warning: Could not parse metadata as JSON, keeping as string: {e}")
90
+
91
+ if session.get("chat_history") is not None and isinstance(session["chat_history"], str):
92
+ try:
93
+ session["chat_history"] = json.loads(session["chat_history"])
94
+ except (json.JSONDecodeError, TypeError) as e:
95
+ log_warning(f"Warning: Could not parse chat_history as JSON, keeping as string: {e}")
96
+
97
+ if session.get("summary") is not None and isinstance(session["summary"], str):
98
+ try:
99
+ session["summary"] = json.loads(session["summary"])
100
+ except (json.JSONDecodeError, TypeError) as e:
101
+ log_warning(f"Warning: Could not parse summary as JSON, keeping as string: {e}")
102
+
78
103
  if session.get("session_data") is not None and isinstance(session["session_data"], str):
79
- session["session_data"] = json.loads(session["session_data"])
80
- if session.get("runs") is not None:
81
- if session["session_type"] == SessionType.AGENT.value:
82
- session["runs"] = json.loads(session["runs"])
83
- if session["session_type"] == SessionType.TEAM.value:
84
- session["runs"] = json.loads(session["runs"])
85
- if session["session_type"] == SessionType.WORKFLOW.value:
104
+ try:
105
+ session["session_data"] = json.loads(session["session_data"])
106
+ except (json.JSONDecodeError, TypeError) as e:
107
+ log_warning(f"Warning: Could not parse session_data as JSON, keeping as string: {e}")
108
+
109
+ # Handle runs field with session type checking
110
+ if session.get("runs") is not None and isinstance(session["runs"], str):
111
+ try:
86
112
  session["runs"] = json.loads(session["runs"])
113
+ except (json.JSONDecodeError, TypeError) as e:
114
+ log_warning(f"Warning: Could not parse runs as JSON, keeping as string: {e}")
87
115
 
88
116
  return session