agno 2.3.2__py3-none-any.whl → 2.3.4__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 +513 -185
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +176 -0
- agno/db/dynamo/dynamo.py +11 -0
- agno/db/firestore/firestore.py +5 -1
- agno/db/gcs_json/gcs_json_db.py +5 -2
- agno/db/in_memory/in_memory_db.py +5 -2
- agno/db/json/json_db.py +5 -1
- agno/db/migrations/manager.py +4 -4
- agno/db/mongo/async_mongo.py +158 -34
- agno/db/mongo/mongo.py +6 -2
- agno/db/mysql/mysql.py +48 -54
- agno/db/postgres/async_postgres.py +66 -52
- agno/db/postgres/postgres.py +42 -50
- agno/db/redis/redis.py +5 -0
- agno/db/redis/utils.py +5 -5
- agno/db/singlestore/singlestore.py +99 -108
- agno/db/sqlite/async_sqlite.py +29 -27
- agno/db/sqlite/sqlite.py +30 -26
- agno/knowledge/reader/pdf_reader.py +2 -2
- agno/knowledge/reader/tavily_reader.py +0 -1
- agno/memory/__init__.py +14 -1
- agno/memory/manager.py +217 -4
- agno/memory/strategies/__init__.py +15 -0
- agno/memory/strategies/base.py +67 -0
- agno/memory/strategies/summarize.py +196 -0
- agno/memory/strategies/types.py +37 -0
- agno/models/aimlapi/aimlapi.py +18 -0
- agno/models/anthropic/claude.py +87 -81
- agno/models/aws/bedrock.py +38 -16
- agno/models/aws/claude.py +97 -277
- agno/models/azure/ai_foundry.py +8 -4
- agno/models/base.py +101 -14
- agno/models/cerebras/cerebras.py +25 -9
- agno/models/cerebras/cerebras_openai.py +22 -2
- agno/models/cohere/chat.py +18 -6
- agno/models/cometapi/cometapi.py +19 -1
- agno/models/deepinfra/deepinfra.py +19 -1
- agno/models/fireworks/fireworks.py +19 -1
- agno/models/google/gemini.py +583 -21
- agno/models/groq/groq.py +23 -6
- agno/models/huggingface/huggingface.py +22 -7
- agno/models/ibm/watsonx.py +21 -7
- agno/models/internlm/internlm.py +19 -1
- agno/models/langdb/langdb.py +10 -0
- agno/models/litellm/chat.py +17 -7
- agno/models/litellm/litellm_openai.py +19 -1
- agno/models/message.py +19 -5
- agno/models/meta/llama.py +25 -5
- agno/models/meta/llama_openai.py +18 -0
- agno/models/mistral/mistral.py +13 -5
- agno/models/nvidia/nvidia.py +19 -1
- agno/models/ollama/chat.py +17 -6
- agno/models/openai/chat.py +22 -7
- agno/models/openai/responses.py +28 -10
- agno/models/openrouter/openrouter.py +20 -0
- agno/models/perplexity/perplexity.py +17 -0
- agno/models/requesty/requesty.py +18 -0
- agno/models/sambanova/sambanova.py +19 -1
- agno/models/siliconflow/siliconflow.py +19 -1
- agno/models/together/together.py +19 -1
- agno/models/vercel/v0.py +19 -1
- agno/models/vertexai/claude.py +99 -5
- agno/models/xai/xai.py +18 -0
- agno/os/interfaces/agui/router.py +1 -0
- agno/os/interfaces/agui/utils.py +97 -57
- agno/os/router.py +16 -0
- agno/os/routers/memory/memory.py +143 -0
- agno/os/routers/memory/schemas.py +26 -0
- agno/os/schema.py +33 -6
- agno/os/utils.py +134 -10
- agno/run/base.py +2 -1
- agno/run/workflow.py +1 -1
- agno/team/team.py +566 -219
- agno/tools/mcp/mcp.py +1 -1
- agno/utils/agent.py +119 -1
- agno/utils/models/ai_foundry.py +9 -2
- agno/utils/models/claude.py +12 -5
- agno/utils/models/cohere.py +9 -2
- agno/utils/models/llama.py +9 -2
- agno/utils/models/mistral.py +4 -2
- agno/utils/print_response/agent.py +37 -2
- agno/utils/print_response/team.py +52 -0
- agno/utils/tokens.py +41 -0
- agno/workflow/types.py +2 -2
- {agno-2.3.2.dist-info → agno-2.3.4.dist-info}/METADATA +45 -40
- {agno-2.3.2.dist-info → agno-2.3.4.dist-info}/RECORD +90 -83
- {agno-2.3.2.dist-info → agno-2.3.4.dist-info}/WHEEL +0 -0
- {agno-2.3.2.dist-info → agno-2.3.4.dist-info}/licenses/LICENSE +0 -0
- {agno-2.3.2.dist-info → agno-2.3.4.dist-info}/top_level.txt +0 -0
agno/os/schema.py
CHANGED
|
@@ -24,6 +24,7 @@ from agno.run.agent import RunOutput
|
|
|
24
24
|
from agno.run.team import TeamRunOutput
|
|
25
25
|
from agno.session import AgentSession, TeamSession, WorkflowSession
|
|
26
26
|
from agno.team.team import Team
|
|
27
|
+
from agno.utils.agent import aexecute_instructions, aexecute_system_message
|
|
27
28
|
from agno.workflow.agent import WorkflowAgent
|
|
28
29
|
from agno.workflow.workflow import Workflow
|
|
29
30
|
|
|
@@ -337,12 +338,20 @@ class AgentResponse(BaseModel):
|
|
|
337
338
|
"read_tool_call_history": agent.read_tool_call_history,
|
|
338
339
|
}
|
|
339
340
|
|
|
341
|
+
instructions = agent.instructions if agent.instructions else None
|
|
342
|
+
if instructions and callable(instructions):
|
|
343
|
+
instructions = await aexecute_instructions(instructions=instructions, agent=agent)
|
|
344
|
+
|
|
345
|
+
system_message = agent.system_message if agent.system_message else None
|
|
346
|
+
if system_message and callable(system_message):
|
|
347
|
+
system_message = await aexecute_system_message(system_message=system_message, agent=agent)
|
|
348
|
+
|
|
340
349
|
system_message_info = {
|
|
341
|
-
"system_message": str(
|
|
350
|
+
"system_message": str(system_message) if system_message else None,
|
|
342
351
|
"system_message_role": agent.system_message_role,
|
|
343
352
|
"build_context": agent.build_context,
|
|
344
353
|
"description": agent.description,
|
|
345
|
-
"instructions":
|
|
354
|
+
"instructions": instructions,
|
|
346
355
|
"expected_output": agent.expected_output,
|
|
347
356
|
"additional_context": agent.additional_context,
|
|
348
357
|
"markdown": agent.markdown,
|
|
@@ -560,12 +569,18 @@ class TeamResponse(BaseModel):
|
|
|
560
569
|
"get_member_information_tool": team.get_member_information_tool,
|
|
561
570
|
}
|
|
562
571
|
|
|
563
|
-
team_instructions =
|
|
564
|
-
|
|
565
|
-
|
|
572
|
+
team_instructions = team.instructions if team.instructions else None
|
|
573
|
+
if team_instructions and callable(team_instructions):
|
|
574
|
+
team_instructions = await aexecute_instructions(instructions=team_instructions, agent=team, team=team)
|
|
575
|
+
|
|
576
|
+
team_system_message = team.system_message if team.system_message else None
|
|
577
|
+
if team_system_message and callable(team_system_message):
|
|
578
|
+
team_system_message = await aexecute_system_message(
|
|
579
|
+
system_message=team_system_message, agent=team, team=team
|
|
580
|
+
)
|
|
566
581
|
|
|
567
582
|
system_message_info = {
|
|
568
|
-
"system_message":
|
|
583
|
+
"system_message": team_system_message,
|
|
569
584
|
"system_message_role": team.system_message_role,
|
|
570
585
|
"description": team.description,
|
|
571
586
|
"instructions": team_instructions,
|
|
@@ -883,6 +898,9 @@ class RunSchema(BaseModel):
|
|
|
883
898
|
events: Optional[List[dict]] = Field(None, description="Events generated during the run")
|
|
884
899
|
created_at: Optional[datetime] = Field(None, description="Run creation timestamp")
|
|
885
900
|
references: Optional[List[dict]] = Field(None, description="References cited in the run")
|
|
901
|
+
citations: Optional[Dict[str, Any]] = Field(
|
|
902
|
+
None, description="Citations from the model (e.g., from Gemini grounding/search)"
|
|
903
|
+
)
|
|
886
904
|
reasoning_messages: Optional[List[dict]] = Field(None, description="Reasoning process messages")
|
|
887
905
|
session_state: Optional[dict] = Field(None, description="Session state at the end of the run")
|
|
888
906
|
images: Optional[List[dict]] = Field(None, description="Images included in the run")
|
|
@@ -911,6 +929,7 @@ class RunSchema(BaseModel):
|
|
|
911
929
|
tools=[tool for tool in run_dict.get("tools", [])] if run_dict.get("tools") else None,
|
|
912
930
|
events=[event for event in run_dict["events"]] if run_dict.get("events") else None,
|
|
913
931
|
references=run_dict.get("references", []),
|
|
932
|
+
citations=run_dict.get("citations", None),
|
|
914
933
|
reasoning_messages=run_dict.get("reasoning_messages", []),
|
|
915
934
|
session_state=run_dict.get("session_state"),
|
|
916
935
|
images=run_dict.get("images", []),
|
|
@@ -940,6 +959,9 @@ class TeamRunSchema(BaseModel):
|
|
|
940
959
|
events: Optional[List[dict]] = Field(None, description="Events generated during the run")
|
|
941
960
|
created_at: Optional[datetime] = Field(None, description="Run creation timestamp")
|
|
942
961
|
references: Optional[List[dict]] = Field(None, description="References cited in the run")
|
|
962
|
+
citations: Optional[Dict[str, Any]] = Field(
|
|
963
|
+
None, description="Citations from the model (e.g., from Gemini grounding/search)"
|
|
964
|
+
)
|
|
943
965
|
reasoning_messages: Optional[List[dict]] = Field(None, description="Reasoning process messages")
|
|
944
966
|
session_state: Optional[dict] = Field(None, description="Session state at the end of the run")
|
|
945
967
|
input_media: Optional[Dict[str, Any]] = Field(None, description="Input media attachments")
|
|
@@ -970,6 +992,7 @@ class TeamRunSchema(BaseModel):
|
|
|
970
992
|
if run_dict.get("created_at") is not None
|
|
971
993
|
else None,
|
|
972
994
|
references=run_dict.get("references", []),
|
|
995
|
+
citations=run_dict.get("citations", None),
|
|
973
996
|
reasoning_messages=run_dict.get("reasoning_messages", []),
|
|
974
997
|
session_state=run_dict.get("session_state"),
|
|
975
998
|
images=run_dict.get("images", []),
|
|
@@ -997,6 +1020,9 @@ class WorkflowRunSchema(BaseModel):
|
|
|
997
1020
|
reasoning_content: Optional[str] = Field(None, description="Reasoning content if reasoning was enabled")
|
|
998
1021
|
reasoning_steps: Optional[List[dict]] = Field(None, description="List of reasoning steps")
|
|
999
1022
|
references: Optional[List[dict]] = Field(None, description="References cited in the workflow")
|
|
1023
|
+
citations: Optional[Dict[str, Any]] = Field(
|
|
1024
|
+
None, description="Citations from the model (e.g., from Gemini grounding/search)"
|
|
1025
|
+
)
|
|
1000
1026
|
reasoning_messages: Optional[List[dict]] = Field(None, description="Reasoning process messages")
|
|
1001
1027
|
images: Optional[List[dict]] = Field(None, description="Images included in the workflow")
|
|
1002
1028
|
videos: Optional[List[dict]] = Field(None, description="Videos included in the workflow")
|
|
@@ -1023,6 +1049,7 @@ class WorkflowRunSchema(BaseModel):
|
|
|
1023
1049
|
reasoning_content=run_response.get("reasoning_content", ""),
|
|
1024
1050
|
reasoning_steps=run_response.get("reasoning_steps", []),
|
|
1025
1051
|
references=run_response.get("references", []),
|
|
1052
|
+
citations=run_response.get("citations", None),
|
|
1026
1053
|
reasoning_messages=run_response.get("reasoning_messages", []),
|
|
1027
1054
|
images=run_response.get("images", []),
|
|
1028
1055
|
videos=run_response.get("videos", []),
|
agno/os/utils.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
from typing import Any, Callable, Dict, List, Optional, Set, Union
|
|
1
|
+
from typing import Any, Callable, Dict, List, Optional, Set, Type, Union
|
|
2
2
|
|
|
3
3
|
from fastapi import FastAPI, HTTPException, UploadFile
|
|
4
4
|
from fastapi.routing import APIRoute, APIRouter
|
|
5
|
-
from pydantic import BaseModel
|
|
5
|
+
from pydantic import BaseModel, create_model
|
|
6
6
|
from starlette.middleware.cors import CORSMiddleware
|
|
7
7
|
|
|
8
8
|
from agno.agent.agent import Agent
|
|
@@ -511,8 +511,10 @@ def collect_mcp_tools_from_team(team: Team, mcp_tools: List[Any]) -> None:
|
|
|
511
511
|
# Check the team tools
|
|
512
512
|
if team.tools:
|
|
513
513
|
for tool in team.tools:
|
|
514
|
-
|
|
515
|
-
if
|
|
514
|
+
# Alternate method of using isinstance(tool, (MCPTools, MultiMCPTools)) to avoid imports
|
|
515
|
+
if hasattr(type(tool), "__mro__") and any(
|
|
516
|
+
c.__name__ in ["MCPTools", "MultiMCPTools"] for c in type(tool).__mro__
|
|
517
|
+
):
|
|
516
518
|
if tool not in mcp_tools:
|
|
517
519
|
mcp_tools.append(tool)
|
|
518
520
|
|
|
@@ -522,8 +524,10 @@ def collect_mcp_tools_from_team(team: Team, mcp_tools: List[Any]) -> None:
|
|
|
522
524
|
if isinstance(member, Agent):
|
|
523
525
|
if member.tools:
|
|
524
526
|
for tool in member.tools:
|
|
525
|
-
|
|
526
|
-
if
|
|
527
|
+
# Alternate method of using isinstance(tool, (MCPTools, MultiMCPTools)) to avoid imports
|
|
528
|
+
if hasattr(type(tool), "__mro__") and any(
|
|
529
|
+
c.__name__ in ["MCPTools", "MultiMCPTools"] for c in type(tool).__mro__
|
|
530
|
+
):
|
|
527
531
|
if tool not in mcp_tools:
|
|
528
532
|
mcp_tools.append(tool)
|
|
529
533
|
|
|
@@ -567,8 +571,10 @@ def collect_mcp_tools_from_workflow_step(step: Any, mcp_tools: List[Any]) -> Non
|
|
|
567
571
|
if step.agent:
|
|
568
572
|
if step.agent.tools:
|
|
569
573
|
for tool in step.agent.tools:
|
|
570
|
-
|
|
571
|
-
if
|
|
574
|
+
# Alternate method of using isinstance(tool, (MCPTools, MultiMCPTools)) to avoid imports
|
|
575
|
+
if hasattr(type(tool), "__mro__") and any(
|
|
576
|
+
c.__name__ in ["MCPTools", "MultiMCPTools"] for c in type(tool).__mro__
|
|
577
|
+
):
|
|
572
578
|
if tool not in mcp_tools:
|
|
573
579
|
mcp_tools.append(tool)
|
|
574
580
|
# Check step's team
|
|
@@ -590,8 +596,10 @@ def collect_mcp_tools_from_workflow_step(step: Any, mcp_tools: List[Any]) -> Non
|
|
|
590
596
|
# Direct agent in workflow steps
|
|
591
597
|
if step.tools:
|
|
592
598
|
for tool in step.tools:
|
|
593
|
-
|
|
594
|
-
if
|
|
599
|
+
# Alternate method of using isinstance(tool, (MCPTools, MultiMCPTools)) to avoid imports
|
|
600
|
+
if hasattr(type(tool), "__mro__") and any(
|
|
601
|
+
c.__name__ in ["MCPTools", "MultiMCPTools"] for c in type(tool).__mro__
|
|
602
|
+
):
|
|
595
603
|
if tool not in mcp_tools:
|
|
596
604
|
mcp_tools.append(tool)
|
|
597
605
|
|
|
@@ -628,3 +636,119 @@ def stringify_input_content(input_content: Union[str, Dict[str, Any], List[Any],
|
|
|
628
636
|
return str(input_content)
|
|
629
637
|
else:
|
|
630
638
|
return str(input_content)
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
def _get_python_type_from_json_schema(field_schema: Dict[str, Any], field_name: str = "NestedModel") -> Type:
|
|
642
|
+
"""Map JSON schema type to Python type with recursive handling.
|
|
643
|
+
|
|
644
|
+
Args:
|
|
645
|
+
field_schema: JSON schema dictionary for a single field
|
|
646
|
+
field_name: Name of the field (used for nested model naming)
|
|
647
|
+
|
|
648
|
+
Returns:
|
|
649
|
+
Python type corresponding to the JSON schema type
|
|
650
|
+
"""
|
|
651
|
+
if not isinstance(field_schema, dict):
|
|
652
|
+
return Any
|
|
653
|
+
|
|
654
|
+
json_type = field_schema.get("type")
|
|
655
|
+
|
|
656
|
+
# Handle basic types
|
|
657
|
+
if json_type == "string":
|
|
658
|
+
return str
|
|
659
|
+
elif json_type == "integer":
|
|
660
|
+
return int
|
|
661
|
+
elif json_type == "number":
|
|
662
|
+
return float
|
|
663
|
+
elif json_type == "boolean":
|
|
664
|
+
return bool
|
|
665
|
+
elif json_type == "null":
|
|
666
|
+
return type(None)
|
|
667
|
+
elif json_type == "array":
|
|
668
|
+
# Handle arrays with item type specification
|
|
669
|
+
items_schema = field_schema.get("items")
|
|
670
|
+
if items_schema and isinstance(items_schema, dict):
|
|
671
|
+
item_type = _get_python_type_from_json_schema(items_schema, f"{field_name}Item")
|
|
672
|
+
return List[item_type] # type: ignore
|
|
673
|
+
else:
|
|
674
|
+
# No item type specified - use generic list
|
|
675
|
+
return List[Any]
|
|
676
|
+
elif json_type == "object":
|
|
677
|
+
# Recursively create nested Pydantic model
|
|
678
|
+
nested_properties = field_schema.get("properties", {})
|
|
679
|
+
nested_required = field_schema.get("required", [])
|
|
680
|
+
nested_title = field_schema.get("title", field_name)
|
|
681
|
+
|
|
682
|
+
# Build field definitions for nested model
|
|
683
|
+
nested_fields = {}
|
|
684
|
+
for nested_field_name, nested_field_schema in nested_properties.items():
|
|
685
|
+
nested_field_type = _get_python_type_from_json_schema(nested_field_schema, nested_field_name)
|
|
686
|
+
|
|
687
|
+
if nested_field_name in nested_required:
|
|
688
|
+
nested_fields[nested_field_name] = (nested_field_type, ...)
|
|
689
|
+
else:
|
|
690
|
+
nested_fields[nested_field_name] = (Optional[nested_field_type], None) # type: ignore[assignment]
|
|
691
|
+
|
|
692
|
+
# Create nested model if it has fields
|
|
693
|
+
if nested_fields:
|
|
694
|
+
return create_model(nested_title, **nested_fields) # type: ignore
|
|
695
|
+
else:
|
|
696
|
+
# Empty object schema - use generic dict
|
|
697
|
+
return Dict[str, Any]
|
|
698
|
+
else:
|
|
699
|
+
# Unknown or unspecified type - fallback to Any
|
|
700
|
+
if json_type:
|
|
701
|
+
logger.warning(f"Unknown JSON schema type '{json_type}' for field '{field_name}', using Any")
|
|
702
|
+
return Any
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
def json_schema_to_pydantic_model(schema: Dict[str, Any]) -> Type[BaseModel]:
|
|
706
|
+
"""Convert a JSON schema dictionary to a Pydantic BaseModel class.
|
|
707
|
+
|
|
708
|
+
This function dynamically creates a Pydantic model from a JSON schema specification,
|
|
709
|
+
handling nested objects, arrays, and optional fields.
|
|
710
|
+
|
|
711
|
+
Args:
|
|
712
|
+
schema: JSON schema dictionary with 'properties', 'required', 'type', etc.
|
|
713
|
+
|
|
714
|
+
Returns:
|
|
715
|
+
Dynamically created Pydantic BaseModel class
|
|
716
|
+
"""
|
|
717
|
+
import copy
|
|
718
|
+
|
|
719
|
+
# Deep copy to avoid modifying the original schema
|
|
720
|
+
schema = copy.deepcopy(schema)
|
|
721
|
+
|
|
722
|
+
# Extract schema components
|
|
723
|
+
model_name = schema.get("title", "DynamicModel")
|
|
724
|
+
properties = schema.get("properties", {})
|
|
725
|
+
required_fields = schema.get("required", [])
|
|
726
|
+
|
|
727
|
+
# Validate schema has properties
|
|
728
|
+
if not properties:
|
|
729
|
+
logger.warning(f"JSON schema '{model_name}' has no properties, creating empty model")
|
|
730
|
+
|
|
731
|
+
# Build field definitions for create_model
|
|
732
|
+
field_definitions = {}
|
|
733
|
+
for field_name, field_schema in properties.items():
|
|
734
|
+
try:
|
|
735
|
+
field_type = _get_python_type_from_json_schema(field_schema, field_name)
|
|
736
|
+
|
|
737
|
+
if field_name in required_fields:
|
|
738
|
+
# Required field: (type, ...)
|
|
739
|
+
field_definitions[field_name] = (field_type, ...)
|
|
740
|
+
else:
|
|
741
|
+
# Optional field: (Optional[type], None)
|
|
742
|
+
field_definitions[field_name] = (Optional[field_type], None) # type: ignore[assignment]
|
|
743
|
+
except Exception as e:
|
|
744
|
+
logger.warning(f"Failed to process field '{field_name}' in schema '{model_name}': {e}")
|
|
745
|
+
# Skip problematic fields rather than failing entirely
|
|
746
|
+
continue
|
|
747
|
+
|
|
748
|
+
# Create and return the dynamic model
|
|
749
|
+
try:
|
|
750
|
+
return create_model(model_name, **field_definitions) # type: ignore
|
|
751
|
+
except Exception as e:
|
|
752
|
+
logger.error(f"Failed to create dynamic model '{model_name}': {e}")
|
|
753
|
+
# Return a minimal model as fallback
|
|
754
|
+
return create_model(model_name)
|
agno/run/base.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from dataclasses import asdict, dataclass
|
|
2
2
|
from enum import Enum
|
|
3
|
-
from typing import Any, Dict, List, Optional, Union
|
|
3
|
+
from typing import Any, Dict, List, Optional, Type, Union
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel
|
|
6
6
|
|
|
@@ -22,6 +22,7 @@ class RunContext:
|
|
|
22
22
|
knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
23
23
|
metadata: Optional[Dict[str, Any]] = None
|
|
24
24
|
session_state: Optional[Dict[str, Any]] = None
|
|
25
|
+
output_schema: Optional[Type[BaseModel]] = None
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
@dataclass
|
agno/run/workflow.py
CHANGED
|
@@ -597,7 +597,7 @@ class WorkflowRunOutput:
|
|
|
597
597
|
_dict["input"] = self.input
|
|
598
598
|
|
|
599
599
|
if self.content and isinstance(self.content, BaseModel):
|
|
600
|
-
_dict["content"] = self.content.model_dump(exclude_none=True)
|
|
600
|
+
_dict["content"] = self.content.model_dump(exclude_none=True, mode="json")
|
|
601
601
|
|
|
602
602
|
if self.events is not None:
|
|
603
603
|
_dict["events"] = [e.to_dict() for e in self.events]
|