Skip to content

Commit 6af70fb

Browse files
committed
Update pydantic and dataclass in function handling
1 parent e445e56 commit 6af70fb

File tree

6 files changed

+445
-25
lines changed

6 files changed

+445
-25
lines changed

libs/agno/agno/agent/agent.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6157,7 +6157,6 @@ def register_agent(self) -> None:
61576157
if not self.session_id:
61586158
self.session_id = str(uuid4())
61596159

6160-
log_debug(f"Creating Agent on Platform: {self.name}, {self.agent_id}, {self.team_id}, {self.workflow_id}")
61616160
create_agent(
61626161
agent=AgentCreate(
61636162
name=self.name,
@@ -6168,7 +6167,6 @@ def register_agent(self) -> None:
61686167
config=self.get_agent_config_dict(),
61696168
)
61706169
)
6171-
log_debug(f"Agent created: {self.name}, {self.agent_id}, {self.team_id}, {self.workflow_id}")
61726170
except Exception as e:
61736171
log_warning(f"Could not create Agent: {e}")
61746172

@@ -6180,7 +6178,6 @@ async def _aregister_agent(self) -> None:
61806178
from agno.api.agent import AgentCreate, acreate_agent
61816179

61826180
try:
6183-
log_debug(f"Creating Agent on Platform: {self.name}, {self.agent_id}, {self.team_id},")
61846181
await acreate_agent(
61856182
agent=AgentCreate(
61866183
name=self.name,
@@ -6193,7 +6190,6 @@ async def _aregister_agent(self) -> None:
61936190
)
61946191
except Exception as e:
61956192
log_debug(f"Could not create Agent app: {e}")
6196-
log_debug(f"Agent app created: {self.name}, {self.agent_id}, {self.team_id},")
61976193

61986194
def _log_agent_run(self, session_id: str, user_id: Optional[str] = None) -> None:
61996195
self.set_monitoring()

libs/agno/agno/api/agent.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ async def acreate_agent_run(run: AgentRunCreate, monitor: bool = False) -> None:
4848
)
4949
except Exception as e:
5050
log_debug(f"Could not create Agent run: {e}")
51+
return
5152

5253

5354
def create_agent(agent: AgentCreate) -> None:
@@ -60,8 +61,11 @@ def create_agent(agent: AgentCreate) -> None:
6061
ApiRoutes.AGENT_CREATE,
6162
json=agent.model_dump(exclude_none=True),
6263
)
64+
65+
log_debug(f"Created Agent on Platform. ID: {agent.agent_id}")
6366
except Exception as e:
6467
log_debug(f"Could not create Agent: {e}")
68+
return
6569

6670

6771
async def acreate_agent(agent: AgentCreate) -> None:
@@ -74,5 +78,7 @@ async def acreate_agent(agent: AgentCreate) -> None:
7478
ApiRoutes.AGENT_CREATE,
7579
json=agent.model_dump(exclude_none=True),
7680
)
81+
log_debug(f"Created Agent on Platform. ID: {agent.agent_id}")
7782
except Exception as e:
7883
log_debug(f"Could not create Agent: {e}")
84+
return

libs/agno/agno/models/openai/chat.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ def get_request_kwargs(
185185
# Add tools
186186
if tools is not None and len(tools) > 0:
187187
request_params["tools"] = tools
188-
188+
189189
if tool_choice is not None:
190190
request_params["tool_choice"] = tool_choice
191191

libs/agno/agno/utils/json_schema.py

Lines changed: 124 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from typing import Any, Dict, Optional, Union, get_args, get_origin
22

3+
from pydantic import BaseModel
4+
35
from agno.utils.log import logger
46

57

@@ -38,13 +40,86 @@ def get_json_type_for_py_type(arg: str) -> str:
3840
return "object"
3941

4042

41-
def get_json_schema_for_arg(t: Any) -> Optional[Dict[str, Any]]:
43+
def inline_pydantic_schema(schema: Dict[str, Any]) -> Dict[str, Any]:
44+
"""
45+
Recursively inline Pydantic model schemas by replacing $ref with actual schema.
46+
"""
47+
if not isinstance(schema, dict):
48+
return schema
49+
50+
def resolve_ref(ref: str, defs: Dict[str, Any]) -> Dict[str, Any]:
51+
"""Resolve a $ref to its actual schema."""
52+
if not ref.startswith("#/$defs/"):
53+
return {"type": "object"} # Fallback for external refs
54+
55+
def_name = ref.split("/")[-1]
56+
if def_name in defs:
57+
return defs[def_name]
58+
return {"type": "object"} # Fallback if definition not found
59+
60+
def process_schema(s: Dict[str, Any], defs: Dict[str, Any]) -> Dict[str, Any]:
61+
"""Process a schema dictionary, resolving all references."""
62+
if not isinstance(s, dict):
63+
return s
64+
65+
# Handle $ref
66+
if "$ref" in s:
67+
return resolve_ref(s["$ref"], defs)
68+
69+
# Create a new dict to avoid modifying the input
70+
result = s.copy()
71+
72+
# Handle arrays
73+
if "items" in result:
74+
result["items"] = process_schema(result["items"], defs)
75+
76+
# Handle object properties
77+
if "properties" in result:
78+
for prop_name, prop_schema in result["properties"].items():
79+
result["properties"][prop_name] = process_schema(prop_schema, defs)
80+
81+
# Handle anyOf (for Union types)
82+
if "anyOf" in result:
83+
result["anyOf"] = [process_schema(sub_schema, defs) for sub_schema in result["anyOf"]]
84+
85+
# Handle allOf (for inheritance)
86+
if "allOf" in result:
87+
result["allOf"] = [process_schema(sub_schema, defs) for sub_schema in result["allOf"]]
88+
89+
# Handle additionalProperties
90+
if "additionalProperties" in result:
91+
result["additionalProperties"] = process_schema(result["additionalProperties"], defs)
92+
93+
# Handle propertyNames
94+
if "propertyNames" in result:
95+
result["propertyNames"] = process_schema(result["propertyNames"], defs)
96+
97+
return result
98+
99+
# Store definitions for later use
100+
definitions = schema.pop("$defs", {})
101+
102+
# First, resolve any nested references in definitions
103+
resolved_definitions = {}
104+
for def_name, def_schema in definitions.items():
105+
resolved_definitions[def_name] = process_schema(def_schema, definitions)
106+
107+
# Process the main schema with resolved definitions
108+
result = process_schema(schema, resolved_definitions)
109+
110+
# Remove any remaining definitions
111+
if "$defs" in result:
112+
del result["$defs"]
113+
114+
return result
115+
116+
117+
def get_json_schema_for_arg(type_hint: Any) -> Optional[Dict[str, Any]]:
42118
# log_info(f"Getting JSON schema for arg: {t}")
43-
type_args = get_args(t)
119+
type_args = get_args(type_hint)
44120
# log_info(f"Type args: {type_args}")
45-
type_origin = get_origin(t)
121+
type_origin = get_origin(type_hint)
46122
# log_info(f"Type origin: {type_origin}")
47-
48123
if type_origin is not None:
49124
if type_origin in (list, tuple, set, frozenset):
50125
json_schema_for_items = get_json_schema_for_arg(type_args[0]) if type_args else {"type": "string"}
@@ -65,7 +140,39 @@ def get_json_schema_for_arg(t: Any) -> Optional[Dict[str, Any]]:
65140
continue
66141
return {"anyOf": types} if types else None
67142

68-
json_schema: Dict[str, Any] = {"type": get_json_type_for_py_type(t.__name__)}
143+
elif issubclass(type_hint, BaseModel):
144+
# Get the schema and inline it
145+
schema = type_hint.model_json_schema()
146+
return inline_pydantic_schema(schema)
147+
elif hasattr(type_hint, "__dataclass_fields__"):
148+
# Convert dataclass to JSON schema
149+
properties = {}
150+
required = []
151+
152+
for field_name, field in type_hint.__dataclass_fields__.items():
153+
field_type = field.type
154+
field_schema = get_json_schema_for_arg(field_type)
155+
156+
if "anyOf" in field_schema and any(schema["type"] == "null" for schema in field_schema["anyOf"]):
157+
field_schema["type"] = next(schema["type"] for schema in field_schema["anyOf"] if schema["type"] != "null")
158+
field_schema.pop("anyOf")
159+
else:
160+
required.append(field_name)
161+
162+
if field_schema:
163+
properties[field_name] = field_schema
164+
165+
arg_json_schema = {
166+
"type": "object",
167+
"properties": properties,
168+
"additionalProperties": False
169+
}
170+
171+
if required:
172+
arg_json_schema["required"] = required
173+
return arg_json_schema
174+
175+
json_schema: Dict[str, Any] = {"type": get_json_type_for_py_type(type_hint.__name__)}
69176
if json_schema["type"] == "object":
70177
json_schema["properties"] = {}
71178
json_schema["additionalProperties"] = False
@@ -83,38 +190,37 @@ def get_json_schema(
83190
json_schema["additionalProperties"] = False
84191

85192
# We only include the fields in the type_hints dict
86-
for k, v in type_hints.items():
193+
for parameter_name, type_hint in type_hints.items():
87194
# log_info(f"Parsing arg: {k} | {v}")
88-
if k == "return":
195+
if parameter_name == "return":
89196
continue
90197

91198
try:
92199
# Check if type is Optional (Union with NoneType)
93-
type_origin = get_origin(v)
94-
type_args = get_args(v)
200+
type_origin = get_origin(type_hint)
201+
type_args = get_args(type_hint)
95202
is_optional = type_origin is Union and len(type_args) == 2 and any(arg is type(None) for arg in type_args)
96203

97204
# Get the actual type if it's Optional
98205
if is_optional:
99-
v = next(arg for arg in type_args if arg is not type(None))
206+
type_hint = next(arg for arg in type_args if arg is not type(None))
100207

101-
# Handle cases with no type hint
102-
if v:
103-
arg_json_schema = get_json_schema_for_arg(v)
208+
if type_hint:
209+
arg_json_schema = get_json_schema_for_arg(type_hint)
104210
else:
105211
arg_json_schema = {}
106212

107213
if arg_json_schema is not None:
108214
# Add description
109-
if param_descriptions and k in param_descriptions and param_descriptions[k]:
110-
arg_json_schema["description"] = param_descriptions[k]
215+
if param_descriptions and parameter_name in param_descriptions and param_descriptions[parameter_name]:
216+
arg_json_schema["description"] = param_descriptions[parameter_name]
111217

112-
json_schema["properties"][k] = arg_json_schema
218+
json_schema["properties"][parameter_name] = arg_json_schema
113219

114220
else:
115-
logger.warning(f"Could not parse argument {k} of type {v}")
221+
logger.warning(f"Could not parse argument {parameter_name} of type {type_hint}")
116222
except Exception as e:
117-
logger.error(f"Error processing argument {k}: {str(e)}")
223+
logger.error(f"Error processing argument {parameter_name}: {str(e)}")
118224
continue
119225

120226
return json_schema

libs/agno/agno/workflow/workflow.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -680,15 +680,14 @@ def register_workflow(self, force: bool = False) -> None:
680680
from agno.api.workflows import create_workflow
681681

682682
workflow_config = self.to_config_dict()
683-
log_debug(f"Registering workflow: {self.name} (ID: {self.workflow_id})")
684683
# Register the workflow as an app
685684
create_workflow(
686685
workflow=WorkflowCreate(
687686
name=self.name, workflow_id=self.workflow_id, app_id=self.app_id, config=workflow_config
688687
)
689688
)
690689

691-
log_debug(f"Successfully registered workflow: {self.name} (ID: {self.workflow_id})")
690+
log_debug(f"Registered workflow: {self.name} (ID: {self.workflow_id})")
692691
except Exception as e:
693692
log_warning(f"Failed to register workflow: {e}")
694693

0 commit comments

Comments
 (0)