agno 2.1.0__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.
agno/agent/agent.py CHANGED
@@ -5244,7 +5244,7 @@ class Agent:
5244
5244
 
5245
5245
  # 3.2.5 Add information about agentic filters if enabled
5246
5246
  if self.knowledge is not None and self.enable_agentic_knowledge_filters:
5247
- valid_filters = getattr(self.knowledge, "valid_metadata_filters", None)
5247
+ valid_filters = self.knowledge.get_valid_filters()
5248
5248
  if valid_filters:
5249
5249
  valid_filters_str = ", ".join(valid_filters)
5250
5250
  additional_information.append(
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")
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
@@ -1357,6 +1357,12 @@ class Knowledge:
1357
1357
  log_error(f"Error searching for documents: {e}")
1358
1358
  return []
1359
1359
 
1360
+ def get_valid_filters(self) -> Set[str]:
1361
+ if self.valid_metadata_filters is None:
1362
+ self.valid_metadata_filters = set()
1363
+ self.valid_metadata_filters.update(self._get_filters_from_db)
1364
+ return self.valid_metadata_filters
1365
+
1360
1366
  def validate_filters(self, filters: Optional[Dict[str, Any]]) -> Tuple[Dict[str, Any], List[str]]:
1361
1367
  if self.valid_metadata_filters is None:
1362
1368
  self.valid_metadata_filters = set()
@@ -45,7 +45,7 @@ class OpenAIResponses(Model):
45
45
  parallel_tool_calls: Optional[bool] = None
46
46
  reasoning: Optional[Dict[str, Any]] = None
47
47
  verbosity: Optional[Literal["low", "medium", "high"]] = None
48
- reasoning_effort: Optional[Literal["minimal", "medium", "high"]] = None
48
+ reasoning_effort: Optional[Literal["minimal", "low", "medium", "high"]] = None
49
49
  reasoning_summary: Optional[Literal["auto", "concise", "detailed"]] = None
50
50
  store: Optional[bool] = None
51
51
  temperature: Optional[float] = None
@@ -0,0 +1 @@
1
+
@@ -1,6 +1,6 @@
1
1
  """Main class for the AG-UI app, used to expose an Agno Agent or Team in an AG-UI compatible format."""
2
2
 
3
- from typing import Optional
3
+ from typing import List, Optional
4
4
 
5
5
  from fastapi.routing import APIRouter
6
6
 
@@ -15,16 +15,32 @@ class AGUI(BaseInterface):
15
15
 
16
16
  router: APIRouter
17
17
 
18
- def __init__(self, agent: Optional[Agent] = None, team: Optional[Team] = None):
18
+ def __init__(
19
+ self,
20
+ agent: Optional[Agent] = None,
21
+ team: Optional[Team] = None,
22
+ prefix: str = "",
23
+ tags: Optional[List[str]] = None,
24
+ ):
25
+ """
26
+ Initialize the AGUI interface.
27
+
28
+ Args:
29
+ agent: The agent to expose via AG-UI
30
+ team: The team to expose via AG-UI
31
+ prefix: Custom prefix for the router (e.g., "/agui/v1", "/chat/public")
32
+ tags: Custom tags for the router (e.g., ["AGUI", "Chat"], defaults to ["AGUI"])
33
+ """
19
34
  self.agent = agent
20
35
  self.team = team
36
+ self.prefix = prefix
37
+ self.tags = tags or ["AGUI"]
21
38
 
22
39
  if not (self.agent or self.team):
23
40
  raise ValueError("AGUI requires an agent or a team")
24
41
 
25
- def get_router(self, **kwargs) -> APIRouter:
26
- # Cannot be overridden
27
- self.router = APIRouter(tags=["AGUI"])
42
+ def get_router(self) -> APIRouter:
43
+ self.router = APIRouter(prefix=self.prefix, tags=self.tags) # type: ignore
28
44
 
29
45
  self.router = attach_routes(router=self.router, agent=self.agent, team=self.team)
30
46
 
@@ -1,5 +1,5 @@
1
1
  from abc import ABC, abstractmethod
2
- from typing import Optional
2
+ from typing import List, Optional
3
3
 
4
4
  from fastapi import APIRouter
5
5
 
@@ -11,11 +11,13 @@ from agno.workflow.workflow import Workflow
11
11
  class BaseInterface(ABC):
12
12
  type: str
13
13
  version: str = "1.0"
14
- router_prefix: str = ""
15
14
  agent: Optional[Agent] = None
16
15
  team: Optional[Team] = None
17
16
  workflow: Optional[Workflow] = None
18
17
 
18
+ prefix: str
19
+ tags: List[str]
20
+
19
21
  router: APIRouter
20
22
 
21
23
  @abstractmethod
@@ -1,5 +1,4 @@
1
- import logging
2
- from typing import Optional
1
+ from typing import List, Optional
3
2
 
4
3
  from fastapi.routing import APIRouter
5
4
 
@@ -9,25 +8,31 @@ from agno.os.interfaces.slack.router import attach_routes
9
8
  from agno.team.team import Team
10
9
  from agno.workflow.workflow import Workflow
11
10
 
12
- logger = logging.getLogger(__name__)
13
-
14
11
 
15
12
  class Slack(BaseInterface):
16
13
  type = "slack"
17
14
 
18
15
  router: APIRouter
19
16
 
20
- def __init__(self, agent: Optional[Agent] = None, team: Optional[Team] = None, workflow: Optional[Workflow] = None):
17
+ def __init__(
18
+ self,
19
+ agent: Optional[Agent] = None,
20
+ team: Optional[Team] = None,
21
+ workflow: Optional[Workflow] = None,
22
+ prefix: str = "/slack",
23
+ tags: Optional[List[str]] = None,
24
+ ):
21
25
  self.agent = agent
22
26
  self.team = team
23
27
  self.workflow = workflow
28
+ self.prefix = prefix
29
+ self.tags = tags or ["Slack"]
24
30
 
25
31
  if not (self.agent or self.team or self.workflow):
26
32
  raise ValueError("Slack requires an agent, team or workflow")
27
33
 
28
- def get_router(self, **kwargs) -> APIRouter:
29
- # Cannot be overridden
30
- self.router = APIRouter(prefix="/slack", tags=["Slack"])
34
+ def get_router(self) -> APIRouter:
35
+ self.router = APIRouter(prefix=self.prefix, tags=self.tags) # type: ignore
31
36
 
32
37
  self.router = attach_routes(router=self.router, agent=self.agent, team=self.team, workflow=self.workflow)
33
38
 
@@ -1,4 +1,4 @@
1
- from typing import Optional
1
+ from typing import List, Optional
2
2
 
3
3
  from fastapi.routing import APIRouter
4
4
 
@@ -13,16 +13,23 @@ class Whatsapp(BaseInterface):
13
13
 
14
14
  router: APIRouter
15
15
 
16
- def __init__(self, agent: Optional[Agent] = None, team: Optional[Team] = None):
16
+ def __init__(
17
+ self,
18
+ agent: Optional[Agent] = None,
19
+ team: Optional[Team] = None,
20
+ prefix: str = "/whatsapp",
21
+ tags: Optional[List[str]] = None,
22
+ ):
17
23
  self.agent = agent
18
24
  self.team = team
25
+ self.prefix = prefix
26
+ self.tags = tags or ["Whatsapp"]
19
27
 
20
28
  if not (self.agent or self.team):
21
29
  raise ValueError("Whatsapp requires an agent or a team")
22
30
 
23
- def get_router(self, **kwargs) -> APIRouter:
24
- # Cannot be overridden
25
- self.router = APIRouter(prefix="/whatsapp", tags=["Whatsapp"])
31
+ def get_router(self) -> APIRouter:
32
+ self.router = APIRouter(prefix=self.prefix, tags=self.tags) # type: ignore
26
33
 
27
34
  self.router = attach_routes(router=self.router, agent=self.agent, team=self.team)
28
35
 
agno/os/mcp.py CHANGED
@@ -68,7 +68,7 @@ def get_mcp_server(
68
68
  teams=[TeamSummaryResponse.from_team(team) for team in os.teams] if os.teams else [],
69
69
  workflows=[WorkflowSummaryResponse.from_workflow(w) for w in os.workflows] if os.workflows else [],
70
70
  interfaces=[
71
- InterfaceResponse(type=interface.type, version=interface.version, route=interface.router_prefix)
71
+ InterfaceResponse(type=interface.type, version=interface.version, route=interface.prefix)
72
72
  for interface in os.interfaces
73
73
  ],
74
74
  )
agno/os/router.py CHANGED
@@ -654,7 +654,7 @@ def get_base_router(
654
654
  teams=[TeamSummaryResponse.from_team(team) for team in os.teams] if os.teams else [],
655
655
  workflows=[WorkflowSummaryResponse.from_workflow(w) for w in os.workflows] if os.workflows else [],
656
656
  interfaces=[
657
- InterfaceResponse(type=interface.type, version=interface.version, route=interface.router_prefix)
657
+ InterfaceResponse(type=interface.type, version=interface.version, route=interface.prefix)
658
658
  for interface in os.interfaces
659
659
  ],
660
660
  )
agno/run/base.py CHANGED
@@ -117,19 +117,8 @@ class BaseRunOutputEvent:
117
117
 
118
118
  def to_json(self, separators=(", ", ": "), indent: Optional[int] = 2) -> str:
119
119
  import json
120
- from datetime import date, datetime, time
121
- from enum import Enum
122
-
123
- def json_serializer(obj):
124
- # Datetime like
125
- if isinstance(obj, (datetime, date, time)):
126
- return obj.isoformat()
127
- # Enums
128
- if isinstance(obj, Enum):
129
- v = obj.value
130
- return v if isinstance(v, (str, int, float, bool, type(None))) else obj.name
131
- # Fallback to string
132
- return str(obj)
120
+
121
+ from agno.utils.serialize import json_serializer
133
122
 
134
123
  try:
135
124
  _dict = self.to_dict()
agno/team/team.py CHANGED
@@ -4866,7 +4866,7 @@ class Team:
4866
4866
  additional_information.append(f"Your name is: {self.name}.")
4867
4867
 
4868
4868
  if self.knowledge is not None and self.enable_agentic_knowledge_filters:
4869
- valid_filters = getattr(self.knowledge, "valid_metadata_filters", None)
4869
+ valid_filters = self.knowledge.get_valid_filters()
4870
4870
  if valid_filters:
4871
4871
  valid_filters_str = ", ".join(valid_filters)
4872
4872
  additional_information.append(
@@ -0,0 +1,32 @@
1
+ """JSON serialization utilities for handling datetime and enum objects."""
2
+
3
+ from datetime import date, datetime, time
4
+ from enum import Enum
5
+ from typing import Any
6
+
7
+
8
+ def json_serializer(obj: Any) -> Any:
9
+ """Custom JSON serializer for objects not serializable by default json module.
10
+
11
+ Handles:
12
+ - datetime, date, time objects -> ISO format strings
13
+ - Enum objects -> their values (or names if values are not JSON-serializable)
14
+ - All other objects -> string representation
15
+
16
+ Args:
17
+ obj: Object to serialize
18
+
19
+ Returns:
20
+ JSON-serializable representation of the object
21
+ """
22
+ # Datetime like
23
+ if isinstance(obj, (datetime, date, time)):
24
+ return obj.isoformat()
25
+
26
+ # Enums
27
+ if isinstance(obj, Enum):
28
+ v = obj.value
29
+ return v if isinstance(v, (str, int, float, bool, type(None))) else obj.name
30
+
31
+ # Fallback to string
32
+ return str(obj)
agno/workflow/types.py CHANGED
@@ -1,3 +1,4 @@
1
+ import json
1
2
  from dataclasses import dataclass
2
3
  from enum import Enum
3
4
  from typing import Any, Dict, List, Optional, Union
@@ -8,6 +9,7 @@ from pydantic import BaseModel
8
9
  from agno.media import Audio, File, Image, Video
9
10
  from agno.models.metrics import Metrics
10
11
  from agno.utils.log import log_warning
12
+ from agno.utils.serialize import json_serializer
11
13
 
12
14
 
13
15
  @dataclass
@@ -443,9 +445,7 @@ class WebSocketHandler:
443
445
  else:
444
446
  data = {"type": "message", "content": str(event)}
445
447
 
446
- import json
447
-
448
- await self.websocket.send_text(self.format_sse_event(json.dumps(data)))
448
+ await self.websocket.send_text(self.format_sse_event(json.dumps(data, default=json_serializer)))
449
449
 
450
450
  except Exception as e:
451
451
  log_warning(f"Failed to handle WebSocket event: {e}")
@@ -466,9 +466,7 @@ class WebSocketHandler:
466
466
  return
467
467
 
468
468
  try:
469
- import json
470
-
471
- await self.websocket.send_text(self.format_sse_event(json.dumps(data)))
469
+ await self.websocket.send_text(self.format_sse_event(json.dumps(data, default=json_serializer)))
472
470
  except Exception as e:
473
471
  log_warning(f"Failed to send WebSocket dict: {e}")
474
472
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agno
3
- Version: 2.1.0
3
+ Version: 2.1.1
4
4
  Summary: Agno: a lightweight library for building Multi-Agent Systems
5
5
  Author-email: Ashpreet Bedi <ashpreet@agno.com>
6
6
  Project-URL: homepage, https://agno.com
@@ -4,7 +4,7 @@ agno/exceptions.py,sha256=-JelI3vzaHTWySKM1LqtLoH1Otm841QqAwsiboztG14,4748
4
4
  agno/media.py,sha256=eTfYb_pwhX_PCIVPSrW4VYRqmoxKABEF1aZClrVvQ30,16500
5
5
  agno/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  agno/agent/__init__.py,sha256=s7S3FgsjZxuaabzi8L5n4aSH8IZAiZ7XaNNcySGR-EQ,1051
7
- agno/agent/agent.py,sha256=BeMoLBY4aP-J1_U-lvJ1oJ7xUcM4_6igX0xSxd-bZjQ,346408
7
+ agno/agent/agent.py,sha256=SwsX4AoO_9r2BEyyLQNHIfwa0T9t8wyNXy2Rmi_rC1A,346387
8
8
  agno/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  agno/api/agent.py,sha256=fKlQ62E_C9Rjd7Zus3Gs3R1RG-IhzFV-ICpkb6SLqYc,932
10
10
  agno/api/api.py,sha256=Z7iWbrjheJcGLeeDYrtTCWiKTVqjH0uJI35UNWOtAXw,973
@@ -29,7 +29,7 @@ agno/cloud/aws/s3/bucket.py,sha256=5dkKhjWVmf8dyQEyCTd6DkQlKADBnm0-_VKQgKqtJQM,7
29
29
  agno/cloud/aws/s3/object.py,sha256=ttZPbLm3o63oGIhKgAr_bIf9DOpJlXRmrEVlkbTcJbk,1766
30
30
  agno/db/__init__.py,sha256=bfd_tpKsIKCjZosnFqID26VoWqy88v8gzkf9kLHToY4,625
31
31
  agno/db/base.py,sha256=JYsminwt6x0MuWxmJIEf9AjNl1WkDELKjlGvCJRdHZ4,8482
32
- agno/db/utils.py,sha256=xjrbR4g2ry0HnkiQC29Ql7jRzfxrorqS4HNlswhzVcM,3478
32
+ agno/db/utils.py,sha256=eL0prfDrTEfOwNlOZeoZE4pu59bNJET22uh8wgzz-cw,4879
33
33
  agno/db/dynamo/__init__.py,sha256=fZ7NwKbyhoIu7_4T6hVz44HkIINXMnTfFrDrgB6bpEo,67
34
34
  agno/db/dynamo/dynamo.py,sha256=Gde62hB1qQx62ey_w00-Ap0_LJhnzwTbwvHJegTr_io,69134
35
35
  agno/db/dynamo/schemas.py,sha256=ZqeR4QdqMVi53iz2qcs-d5Y6MDfmhHSuP7vot__hLoI,11638
@@ -50,7 +50,7 @@ agno/db/json/utils.py,sha256=__c14xMdyw1AlyV7-DAGDr8YicqhfCbSS5C8OkVN2So,6658
50
50
  agno/db/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
51
  agno/db/migrations/v1_to_v2.py,sha256=bBc96nKf8CjkuhTEdcKdr3hUgPpRMVwE71ozVCnux4c,23969
52
52
  agno/db/mongo/__init__.py,sha256=EPa9QkGNVwnuej72LhZDCeASMXa-e0pR20jsgwa9BhY,63
53
- agno/db/mongo/mongo.py,sha256=xyQUWIyXREmr8jgUopSv2me8euQ2UzJB_Gd1i4Tw7Xc,67840
53
+ agno/db/mongo/mongo.py,sha256=HPAmnSDkDxVccViLVG2wnf408n1QZJ4DI0DgI492wzw,67059
54
54
  agno/db/mongo/schemas.py,sha256=MAhfwx7_zxKucnZpgq_YSZANaF5MqiZ6qDByhdIREk8,2054
55
55
  agno/db/mongo/utils.py,sha256=dmVo0HGIG7IPKdCjuRDcO0ZOtyXcNkP6S2G-XBFqzZE,6906
56
56
  agno/db/mysql/__init__.py,sha256=ohBMZ1E6ctioEF0XX5PjC4LtUQrc6lFkjsE4ojyXA8g,63
@@ -93,7 +93,7 @@ agno/integrations/discord/__init__.py,sha256=MS08QSnegGgpDZd9LyLjK4pWIk9dXlbzgD7
93
93
  agno/integrations/discord/client.py,sha256=2IWkA-kCDsDXByKOGq2QJG6MtFsbouzNN105rsXn95A,8375
94
94
  agno/knowledge/__init__.py,sha256=PJCt-AGKGFztzR--Ok2TNKW5QEqllZzqiI_7f8-1u80,79
95
95
  agno/knowledge/content.py,sha256=q2bjcjDhfge_UrQAcygrv5R9ZTk7vozzKnQpatDQWRo,2295
96
- agno/knowledge/knowledge.py,sha256=3WO8L5zJOOChl5sRpzX79Z1u3XXiiUPtBQmbX-dRYjM,68202
96
+ agno/knowledge/knowledge.py,sha256=TW9D1D_sdcdK2ICfEFHp6pboloQzOOoovKtULvVxhio,68457
97
97
  agno/knowledge/types.py,sha256=ciwDLK9MXwi_W_g4nUChEmK6meDQyqTqGK2Ad-wM1os,754
98
98
  agno/knowledge/utils.py,sha256=_uhEFtz4p7-cIKffdj5UZ__c-u96rs2UWP6dv5HpOMk,6490
99
99
  agno/knowledge/chunking/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -217,7 +217,7 @@ agno/models/ollama/chat.py,sha256=jGDjLi6aeJdLCG8ITxnp0r_E7eoWT5qOZWbM9GmzuyU,16
217
217
  agno/models/openai/__init__.py,sha256=OssVgQRpsriU6aJZ3lIp_jFuqvX6y78L4Fd3uTlmI3E,225
218
218
  agno/models/openai/chat.py,sha256=ZdFj0xeCEGI1_UR_q_IsHl_KqoQ076g8LG5g32zJRYc,37110
219
219
  agno/models/openai/like.py,sha256=wmw9PfAVqluBs4MMY73dgjelKn1yl5JDKyCRvaNFjFw,745
220
- agno/models/openai/responses.py,sha256=tOxgILr9GZVJRCQX1fJYG7p06pBe2-h7fASjr7YdxHA,47109
220
+ agno/models/openai/responses.py,sha256=HyrfTZF8AkjdxktKhos9Riuos03nkhSvreeQGAyn81A,47116
221
221
  agno/models/openrouter/__init__.py,sha256=ZpZhNyy_EGSXp58uC9e2iyjnxBctql7GaY8rUG-599I,90
222
222
  agno/models/openrouter/openrouter.py,sha256=FX0P9mPEHyTuiVQNs-c84Ehy0RIGEmvdy6WM_-KhxUQ,907
223
223
  agno/models/perplexity/__init__.py,sha256=JNmOElDLwcZ9_Lk5owkEdgwmAhaH3YJ-VJqOI8rgp5c,90
@@ -242,25 +242,25 @@ agno/os/__init__.py,sha256=h8oQu7vhD5RZf09jkyM_Kt1Kdq_d5kFB9gJju8QPwcY,55
242
242
  agno/os/app.py,sha256=GwOaXkhhmLmgaYzFTL4BWWXxsQLaFowzClh_NscmjtY,25554
243
243
  agno/os/auth.py,sha256=FyBtAKWtg-qSunCas5m5pK1dVEmikOSZvcCp5r25tTA,1844
244
244
  agno/os/config.py,sha256=u4R9yazQXIcKjR3QzEIZw_XAe_OHp3xn0ff7SVkj2jA,2893
245
- agno/os/mcp.py,sha256=p-jaQedbFCmhCSK9yFTnD_IH7TTukDR4uQUXATMxf8A,8111
246
- agno/os/router.py,sha256=3bMvruswBDH2-ZNJVapZ9VtVDXHVoy4WOYaawDK4abU,68972
245
+ agno/os/mcp.py,sha256=w9x8dN-UuIhRrU8azvjPB95RHIcOKh5IXlRCClCfpN4,8104
246
+ agno/os/router.py,sha256=7FCrVW35a4tB_o2-lO-eIjrlgoAdCR4fPYxCdk6F6YQ,68965
247
247
  agno/os/schema.py,sha256=bdmGLQoBJyetiAZeajJG_gEKLaQSSEwiDwKy0q4Pa-M,38278
248
248
  agno/os/settings.py,sha256=Cn5_8lZI8Vx1UaUYqs9h6Qp4IMDFn4f3c35uppiaMy4,1343
249
249
  agno/os/utils.py,sha256=ktRb8U6DSrmAyAGyg_y-hvpgTAIvhOavBOoc-eEBS7Y,16956
250
- agno/os/interfaces/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
251
- agno/os/interfaces/base.py,sha256=giU0y-egtXLqokN1HQyQIWl4NBawTk_ua7ytKGFmZRc,527
250
+ agno/os/interfaces/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
251
+ agno/os/interfaces/base.py,sha256=vXkr1tRjWHTcmBlQFzvQjqURLhObmFtUAx82uij_j48,542
252
252
  agno/os/interfaces/agui/__init__.py,sha256=1zrGICk4roXUINwSFZfqH6sBsbHmD5KjGYVJMGg4fKQ,66
253
- agno/os/interfaces/agui/agui.py,sha256=-KiJaM-IkL9QI9UNzyWqO-uQxdQxsZJnV5aNFmDPcN4,898
253
+ agno/os/interfaces/agui/agui.py,sha256=PKGoDDbtQFmEC0zRwZmsjS_5t9bJWJ-ZGwxEQsu9P-U,1415
254
254
  agno/os/interfaces/agui/router.py,sha256=CgHCqNwA3ktUoGmCt0PoaQPX3hJ4rcXOgtXUGQ5sPIU,4335
255
255
  agno/os/interfaces/agui/utils.py,sha256=bfWxYh2w2O3aroRlaOc2f4ZJXEVavya1IqvCntqY9Xw,19162
256
256
  agno/os/interfaces/slack/__init__.py,sha256=F095kOcgiyk_KzIozNNieKwpVc_NR8HYpuO4bKiCNN0,70
257
257
  agno/os/interfaces/slack/router.py,sha256=RuD0Y0YOdlyqaj37W5outpM7dHTlEX6R63V4IrETSYc,5710
258
258
  agno/os/interfaces/slack/security.py,sha256=nMbW_0g-G_DEMbCQOD8C3PYrRPIpB2cyM6P-xS6GHYk,917
259
- agno/os/interfaces/slack/slack.py,sha256=hBW_n3WDdt9q3Ptbd1gB4Kr9iOX7LBD8zQvMuiyEEro,1042
259
+ agno/os/interfaces/slack/slack.py,sha256=3fYuaWVA0qu435U4EBc9QrIYp96277TyD4awQqxK-os,1153
260
260
  agno/os/interfaces/whatsapp/__init__.py,sha256=-sD2W00qj8hrx72ATVMtaDc7GfAsaCQJMlnRjYPwisg,82
261
261
  agno/os/interfaces/whatsapp/router.py,sha256=JvkJrk2StdkusteTLkfx6sbhHoCaMPPnv-UxZADwT7c,9827
262
262
  agno/os/interfaces/whatsapp/security.py,sha256=WOdjP886O0Wq6CRHSLAv_b39y6pjmn8opPQDSlZf4WA,1735
263
- agno/os/interfaces/whatsapp/whatsapp.py,sha256=lPWPsfK-fenpdWPYPKwXOYzev-tE44H1-LwqCSsExRA,833
263
+ agno/os/interfaces/whatsapp/whatsapp.py,sha256=tNJncEu_hm0lFOHbjaSoz5-VIKORR_pyW26cseBLfbs,989
264
264
  agno/os/middleware/__init__.py,sha256=EYsNzeixFgL3n8kepKWXT42fTTmrNyD8b8rOdXecMRI,94
265
265
  agno/os/middleware/jwt.py,sha256=xw9jQkVFMTTzUVird1k-egAYBmPd174L06YflYCvB5Q,9468
266
266
  agno/os/routers/__init__.py,sha256=du4LO9aZwiY1t59VcV9M6wiAfftFFlUZc-YXsTGy9LI,97
@@ -292,7 +292,7 @@ agno/reasoning/openai.py,sha256=8c4_tVVvY3shx9YEHSMP3qAgcQsOzpvpYXQEE2hDfzU,3083
292
292
  agno/reasoning/step.py,sha256=6DaOb_0DJRz9Yh1w_mxcRaOSVzIQDrj3lQ6rzHLdIwA,1220
293
293
  agno/run/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
294
294
  agno/run/agent.py,sha256=JaQf4cDTwH6hRJdlhgnbOxnJUT8yry7cLRo1PX0u-OE,23899
295
- agno/run/base.py,sha256=z335BSg_-H5ZdM5WpdIYwRe5KysFKaafaW2nCLVMVaA,7481
295
+ agno/run/base.py,sha256=x37ecdhKdb3dbW23d8t2JPBZwrwIC93lRvT1-76CK0c,7059
296
296
  agno/run/cancel.py,sha256=yoSj3fnx8D7Gf-fSngVIgd3GOp3tRaDhHH_4QeHDoAk,2667
297
297
  agno/run/messages.py,sha256=rAC4CLW-xBA6qFS1BOvcjJ9j_qYf0a7sX1mcdY04zMU,1126
298
298
  agno/run/team.py,sha256=uQvd9BrvxJgU8HMM9_3xRgHU9_qD3rCEJdztExt1yKs,24448
@@ -303,7 +303,7 @@ agno/session/summary.py,sha256=THBbzE48V81p4dKUX2W8OvbpsNO5dI6BdtqDyjfcVqw,8433
303
303
  agno/session/team.py,sha256=0lS-9ljtG17iyC0n8sq_7aEy9MZsdfMf8VgObqJ_3tg,10384
304
304
  agno/session/workflow.py,sha256=8dWTona5jen1iPYwjcvxq1XG5EQDFnd28BEjcbqzl4s,5004
305
305
  agno/team/__init__.py,sha256=toHidBOo5M3n_TIVtIKHgcDbLL9HR-_U-YQYuIt_XtE,847
306
- agno/team/team.py,sha256=ugrxTaRYAJY_XCsCUeTWVxinIsMRGQMan5okoNW0CqI,337053
306
+ agno/team/team.py,sha256=2MY_m7jGZebw9GgbZFAaz0eN6jzzoElTt_ZU3Hee9KI,337032
307
307
  agno/tools/__init__.py,sha256=jNll2sELhPPbqm5nPeT4_uyzRO2_KRTW-8Or60kioS0,210
308
308
  agno/tools/agentql.py,sha256=S82Z9aTNr-E5wnA4fbFs76COljJtiQIjf2grjz3CkHU,4104
309
309
  agno/tools/airflow.py,sha256=uf2rOzZpSU64l_qRJ5Raku-R3Gky-uewmYkh6W0-oxg,2610
@@ -455,6 +455,7 @@ agno/utils/reasoning.py,sha256=pW5y674EjJ-R6IX8Fyxi_vN9xLG5dLkujPpcfSVL1jU,3862
455
455
  agno/utils/response.py,sha256=VfMfRK_m3teMjoEbZaq2nFt5hSmBYrWFaf0VSEuol58,7218
456
456
  agno/utils/response_iterator.py,sha256=MgtadrOuMcw2vJcVvhJdMKRzpVddhLWUIkGFbBz7ZCQ,379
457
457
  agno/utils/safe_formatter.py,sha256=zLrW6O-nGUZvXoDkZOTgVpjeUFTmMUj8pk3FLvW_XjM,809
458
+ agno/utils/serialize.py,sha256=XvQA_KSkVd5qI1QuZwdQpCsl1IOKddFu52Jl6WQASqU,904
458
459
  agno/utils/shell.py,sha256=JaY14Fq3ulodG4SeSdLEoOZDI4JJlmCbdgwK5beJuc8,700
459
460
  agno/utils/streamlit.py,sha256=Bn2kQLiTKKqyN0VXj0NBN_sz8V1bV5vImoSk1PCi52g,17966
460
461
  agno/utils/string.py,sha256=iO1etmjHVxnPMTixAKlpR1vnBnRapYL2pm_cjhS06Ww,7557
@@ -528,10 +529,10 @@ agno/workflow/parallel.py,sha256=K_fT51xzYLI2k48dafNHH4uki-HoT6H2aNwCSeHWE2c,298
528
529
  agno/workflow/router.py,sha256=ZAiVsh2F_9ssKj0_RzHWzfgimaZ5hfb3Ni1Xx_SkVR0,26625
529
530
  agno/workflow/step.py,sha256=15vZFN5HlO3IkHqsxkM-2InUzCWC0Ee8BtoJ-OAhS5w,52485
530
531
  agno/workflow/steps.py,sha256=uRE4oGWs2cA-TrX881AEa69zu6rheXH81mNOZiRrNvg,23719
531
- agno/workflow/types.py,sha256=RNhZGS9HVXHZqABhW7dDDeJOZEDPoAYNFHWdCVgs8e8,17571
532
+ agno/workflow/types.py,sha256=ajZg2hQ9VmJQrmqwXpsIAOnkelYQmWNjbb7ZS-Y3Xpo,17632
532
533
  agno/workflow/workflow.py,sha256=Mw8rGlllpkSoi742L3sH-FubgLpTwMYwmMRYmOv5u9I,115102
533
- agno-2.1.0.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
534
- agno-2.1.0.dist-info/METADATA,sha256=mTC2o5-GDM8VK1sMEptrfrzn_tK1wYAgY5dCAIpgR6Y,22051
535
- agno-2.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
536
- agno-2.1.0.dist-info/top_level.txt,sha256=MKyeuVesTyOKIXUhc-d_tPa2Hrh0oTA4LM0izowpx70,5
537
- agno-2.1.0.dist-info/RECORD,,
534
+ agno-2.1.1.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
535
+ agno-2.1.1.dist-info/METADATA,sha256=M0perrwcgs0G78Q5bROugl09x1rXxDVbYUGn-xxKcpQ,22051
536
+ agno-2.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
537
+ agno-2.1.1.dist-info/top_level.txt,sha256=MKyeuVesTyOKIXUhc-d_tPa2Hrh0oTA4LM0izowpx70,5
538
+ agno-2.1.1.dist-info/RECORD,,
File without changes