agno 2.0.7__py3-none-any.whl → 2.0.8__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/tools/function.py CHANGED
@@ -124,6 +124,8 @@ class Function(BaseModel):
124
124
  _team: Optional[Any] = None
125
125
  # The session state that the function is associated with
126
126
  _session_state: Optional[Dict[str, Any]] = None
127
+ # The dependencies that the function is associated with
128
+ _dependencies: Optional[Dict[str, Any]] = None
127
129
 
128
130
  # Media context that the function is associated with
129
131
  _images: Optional[Sequence[Image]] = None
@@ -165,6 +167,8 @@ class Function(BaseModel):
165
167
  del type_hints["audios"]
166
168
  if "files" in sig.parameters and "files" in type_hints:
167
169
  del type_hints["files"]
170
+ if "dependencies" in sig.parameters and "dependencies" in type_hints:
171
+ del type_hints["dependencies"]
168
172
  # log_info(f"Type hints for {function_name}: {type_hints}")
169
173
 
170
174
  # Filter out return type and only process parameters
@@ -172,7 +176,8 @@ class Function(BaseModel):
172
176
  name: type_hints.get(name)
173
177
  for name in sig.parameters
174
178
  if name != "return"
175
- and name not in ["agent", "team", "session_state", "self", "images", "videos", "audios", "files"]
179
+ and name
180
+ not in ["agent", "team", "session_state", "self", "images", "videos", "audios", "files", "dependencies"]
176
181
  }
177
182
 
178
183
  # Parse docstring for parameters
@@ -201,7 +206,18 @@ class Function(BaseModel):
201
206
  parameters["required"] = [
202
207
  name
203
208
  for name in parameters["properties"]
204
- if name not in ["agent", "team", "session_state", "self", "images", "videos", "audios", "files"]
209
+ if name
210
+ not in [
211
+ "agent",
212
+ "team",
213
+ "session_state",
214
+ "self",
215
+ "images",
216
+ "videos",
217
+ "audios",
218
+ "files",
219
+ "dependencies",
220
+ ]
205
221
  ]
206
222
  else:
207
223
  # Mark a field as required if it has no default value (this would include optional fields)
@@ -209,7 +225,18 @@ class Function(BaseModel):
209
225
  name
210
226
  for name, param in sig.parameters.items()
211
227
  if param.default == param.empty
212
- and name not in ["agent", "team", "session_state", "self", "images", "videos", "audios", "files"]
228
+ and name
229
+ not in [
230
+ "agent",
231
+ "team",
232
+ "session_state",
233
+ "self",
234
+ "images",
235
+ "videos",
236
+ "audios",
237
+ "files",
238
+ "dependencies",
239
+ ]
213
240
  ]
214
241
 
215
242
  # log_debug(f"JSON schema for {function_name}: {parameters}")
@@ -268,6 +295,8 @@ class Function(BaseModel):
268
295
  del type_hints["audios"]
269
296
  if "files" in sig.parameters and "files" in type_hints:
270
297
  del type_hints["files"]
298
+ if "dependencies" in sig.parameters and "dependencies" in type_hints:
299
+ del type_hints["dependencies"]
271
300
  # log_info(f"Type hints for {self.name}: {type_hints}")
272
301
 
273
302
  # Filter out return type and only process parameters
@@ -281,6 +310,7 @@ class Function(BaseModel):
281
310
  "videos",
282
311
  "audios",
283
312
  "files",
313
+ "dependencies",
284
314
  ]
285
315
  if self.requires_user_input and self.user_input_fields:
286
316
  if len(self.user_input_fields) == 0:
@@ -396,7 +426,8 @@ class Function(BaseModel):
396
426
  self.parameters["required"] = [
397
427
  name
398
428
  for name in self.parameters["properties"]
399
- if name not in ["agent", "team", "session_state", "images", "videos", "audios", "files", "self"]
429
+ if name
430
+ not in ["agent", "team", "session_state", "images", "videos", "audios", "files", "self", "dependencies"]
400
431
  ]
401
432
 
402
433
  def _get_cache_key(self, entrypoint_args: Dict[str, Any], call_args: Optional[Dict[str, Any]] = None) -> str:
@@ -419,6 +450,8 @@ class Function(BaseModel):
419
450
  del copy_entrypoint_args["audios"]
420
451
  if "files" in copy_entrypoint_args:
421
452
  del copy_entrypoint_args["files"]
453
+ if "dependencies" in copy_entrypoint_args:
454
+ del copy_entrypoint_args["dependencies"]
422
455
  args_str = str(copy_entrypoint_args)
423
456
 
424
457
  kwargs_str = str(sorted((call_args or {}).items()))
@@ -599,6 +632,9 @@ class FunctionCall(BaseModel):
599
632
  # Check if the entrypoint has an session_state argument
600
633
  if "session_state" in signature(self.function.entrypoint).parameters: # type: ignore
601
634
  entrypoint_args["session_state"] = self.function._session_state
635
+ # Check if the entrypoint has an dependencies argument
636
+ if "dependencies" in signature(self.function.entrypoint).parameters: # type: ignore
637
+ entrypoint_args["dependencies"] = self.function._dependencies
602
638
  # Check if the entrypoint has an fc argument
603
639
  if "fc" in signature(self.function.entrypoint).parameters: # type: ignore
604
640
  entrypoint_args["fc"] = self
@@ -629,6 +665,9 @@ class FunctionCall(BaseModel):
629
665
  # Check if the hook has an session_state argument
630
666
  if "session_state" in signature(hook).parameters:
631
667
  hook_args["session_state"] = self.function._session_state
668
+ # Check if the hook has an dependencies argument
669
+ if "dependencies" in signature(hook).parameters:
670
+ hook_args["dependencies"] = self.function._dependencies
632
671
 
633
672
  if "name" in signature(hook).parameters:
634
673
  hook_args["name"] = name
agno/tools/mcp.py CHANGED
@@ -8,7 +8,7 @@ from typing import Any, Dict, List, Literal, Optional, Union
8
8
 
9
9
  from agno.tools import Toolkit
10
10
  from agno.tools.function import Function
11
- from agno.utils.log import log_debug, log_info, log_warning, logger
11
+ from agno.utils.log import log_debug, log_error, log_info, log_warning
12
12
  from agno.utils.mcp import get_entrypoint_for_tool
13
13
 
14
14
  try:
@@ -338,12 +338,13 @@ class MCPTools(Toolkit):
338
338
  self.functions[f.name] = f
339
339
  log_debug(f"Function: {f.name} registered with {self.name}")
340
340
  except Exception as e:
341
- logger.error(f"Failed to register tool {tool.name}: {e}")
341
+ log_error(f"Failed to register tool {tool.name}: {e}")
342
342
 
343
343
  log_debug(f"{self.name} initialized with {len(filtered_tools)} tools")
344
344
  self._initialized = True
345
+
345
346
  except Exception as e:
346
- logger.error(f"Failed to get MCP tools: {e}")
347
+ log_error(f"Failed to get MCP tools: {e}")
347
348
  raise
348
349
 
349
350
 
@@ -372,6 +373,7 @@ class MultiMCPTools(Toolkit):
372
373
  client=None,
373
374
  include_tools: Optional[list[str]] = None,
374
375
  exclude_tools: Optional[list[str]] = None,
376
+ allow_partial_failure: bool = False,
375
377
  **kwargs,
376
378
  ):
377
379
  """
@@ -387,6 +389,7 @@ class MultiMCPTools(Toolkit):
387
389
  timeout_seconds: Timeout in seconds for managing timeouts for Client Session if Agent or Tool doesn't respond.
388
390
  include_tools: Optional list of tool names to include (if None, includes all).
389
391
  exclude_tools: Optional list of tool names to exclude (if None, excludes none).
392
+ allow_partial_failure: If True, allows toolkit to initialize even if some MCP servers fail to connect. If False, any failure will raise an exception.
390
393
  """
391
394
  super().__init__(name="MultiMCPTools", **kwargs)
392
395
 
@@ -445,12 +448,16 @@ class MultiMCPTools(Toolkit):
445
448
  self.server_params_list.append(StreamableHTTPClientParams(url=url))
446
449
 
447
450
  self._async_exit_stack = AsyncExitStack()
451
+ self._successful_connections = 0
452
+
448
453
  self._initialized = False
449
454
  self._connection_task = None
450
455
  self._active_contexts: list[Any] = []
451
456
  self._used_as_context_manager = False
452
457
 
453
458
  self._client = client
459
+ self._initialized = False
460
+ self.allow_partial_failure = allow_partial_failure
454
461
 
455
462
  def cleanup():
456
463
  """Cancel active connections"""
@@ -511,39 +518,53 @@ class MultiMCPTools(Toolkit):
511
518
  if self._initialized:
512
519
  return
513
520
 
521
+ server_connection_errors = []
522
+
514
523
  for server_params in self.server_params_list:
515
- # Handle stdio connections
516
- if isinstance(server_params, StdioServerParameters):
517
- stdio_transport = await self._async_exit_stack.enter_async_context(stdio_client(server_params))
518
- self._active_contexts.append(stdio_transport)
519
- read, write = stdio_transport
520
- session = await self._async_exit_stack.enter_async_context(
521
- ClientSession(read, write, read_timeout_seconds=timedelta(seconds=self.timeout_seconds))
522
- )
523
- self._active_contexts.append(session)
524
- await self.initialize(session)
525
- # Handle SSE connections
526
- elif isinstance(server_params, SSEClientParams):
527
- client_connection = await self._async_exit_stack.enter_async_context(
528
- sse_client(**asdict(server_params))
529
- )
530
- self._active_contexts.append(client_connection)
531
- read, write = client_connection
532
- session = await self._async_exit_stack.enter_async_context(ClientSession(read, write))
533
- self._active_contexts.append(session)
534
- await self.initialize(session)
535
- # Handle Streamable HTTP connections
536
- elif isinstance(server_params, StreamableHTTPClientParams):
537
- client_connection = await self._async_exit_stack.enter_async_context(
538
- streamablehttp_client(**asdict(server_params))
539
- )
540
- self._active_contexts.append(client_connection)
541
- read, write = client_connection[0:2]
542
- session = await self._async_exit_stack.enter_async_context(ClientSession(read, write))
543
- self._active_contexts.append(session)
544
- await self.initialize(session)
524
+ try:
525
+ # Handle stdio connections
526
+ if isinstance(server_params, StdioServerParameters):
527
+ stdio_transport = await self._async_exit_stack.enter_async_context(stdio_client(server_params))
528
+ read, write = stdio_transport
529
+ session = await self._async_exit_stack.enter_async_context(
530
+ ClientSession(read, write, read_timeout_seconds=timedelta(seconds=self.timeout_seconds))
531
+ )
532
+ await self.initialize(session)
533
+ self._successful_connections += 1
545
534
 
546
- self._initialized = True
535
+ # Handle SSE connections
536
+ elif isinstance(server_params, SSEClientParams):
537
+ client_connection = await self._async_exit_stack.enter_async_context(
538
+ sse_client(**asdict(server_params))
539
+ )
540
+ read, write = client_connection
541
+ session = await self._async_exit_stack.enter_async_context(ClientSession(read, write))
542
+ await self.initialize(session)
543
+ self._successful_connections += 1
544
+
545
+ # Handle Streamable HTTP connections
546
+ elif isinstance(server_params, StreamableHTTPClientParams):
547
+ client_connection = await self._async_exit_stack.enter_async_context(
548
+ streamablehttp_client(**asdict(server_params))
549
+ )
550
+ read, write = client_connection[0:2]
551
+ session = await self._async_exit_stack.enter_async_context(ClientSession(read, write))
552
+ await self.initialize(session)
553
+ self._successful_connections += 1
554
+
555
+ except Exception as e:
556
+ if not self.allow_partial_failure:
557
+ raise ValueError(f"MCP connection failed: {e}")
558
+
559
+ log_error(f"Failed to initialize MCP server with params {server_params}: {e}")
560
+ server_connection_errors.append(str(e))
561
+ continue
562
+
563
+ if self._successful_connections == 0 and server_connection_errors:
564
+ raise ValueError(f"All MCP connections failed: {server_connection_errors}")
565
+
566
+ if not self._initialized and self._successful_connections > 0:
567
+ self._initialized = True
547
568
 
548
569
  async def close(self) -> None:
549
570
  """Close the MCP connections and clean up resources"""
@@ -563,6 +584,8 @@ class MultiMCPTools(Toolkit):
563
584
  ):
564
585
  """Exit the async context manager."""
565
586
  await self._async_exit_stack.aclose()
587
+ self._initialized = False
588
+ self._successful_connections = 0
566
589
 
567
590
  async def initialize(self, session: ClientSession) -> None:
568
591
  """Initialize the MCP toolkit by getting available tools from the MCP server"""
@@ -602,10 +625,10 @@ class MultiMCPTools(Toolkit):
602
625
  self.functions[f.name] = f
603
626
  log_debug(f"Function: {f.name} registered with {self.name}")
604
627
  except Exception as e:
605
- logger.error(f"Failed to register tool {tool.name}: {e}")
628
+ log_error(f"Failed to register tool {tool.name}: {e}")
606
629
 
607
- log_debug(f"{self.name} initialized with {len(filtered_tools)} tools")
630
+ log_debug(f"{self.name} initialized with {len(filtered_tools)} tools from one MCP server")
608
631
  self._initialized = True
609
632
  except Exception as e:
610
- logger.error(f"Failed to get MCP tools: {e}")
633
+ log_error(f"Failed to get MCP tools: {e}")
611
634
  raise
agno/utils/gemini.py CHANGED
@@ -1,5 +1,7 @@
1
1
  from pathlib import Path
2
- from typing import Any, Dict, List, Optional
2
+ from typing import Any, Dict, List, Optional, Type, Union
3
+
4
+ from pydantic import BaseModel
3
5
 
4
6
  from agno.media import Image
5
7
  from agno.utils.log import log_error, log_warning
@@ -9,12 +11,119 @@ try:
9
11
  FunctionDeclaration,
10
12
  Schema,
11
13
  Tool,
12
- Type,
14
+ )
15
+ from google.genai.types import (
16
+ Type as GeminiType,
13
17
  )
14
18
  except ImportError:
15
19
  raise ImportError("`google-genai` not installed. Please install it using `pip install google-genai`")
16
20
 
17
21
 
22
+ def prepare_response_schema(pydantic_model: Type[BaseModel]) -> Union[Type[BaseModel], Schema]:
23
+ """
24
+ Prepare a Pydantic model for use as Gemini response schema.
25
+
26
+ Returns the model directly if Gemini can handle it natively,
27
+ otherwise converts to Gemini's Schema format.
28
+
29
+ Args:
30
+ pydantic_model: A Pydantic model class
31
+
32
+ Returns:
33
+ Either the original Pydantic model or a converted Schema object
34
+ """
35
+ schema_dict = pydantic_model.model_json_schema()
36
+
37
+ # Convert to Gemini Schema if the model has problematic patterns
38
+ if needs_conversion(schema_dict):
39
+ try:
40
+ converted = convert_schema(schema_dict)
41
+ except Exception as e:
42
+ log_warning(f"Failed to convert schema for {pydantic_model}: {e}")
43
+ converted = None
44
+
45
+ if converted is None:
46
+ # If conversion fails, let Gemini handle it directly
47
+ return pydantic_model
48
+ return converted
49
+
50
+ # Gemini can handle this model directly
51
+ return pydantic_model
52
+
53
+
54
+ def needs_conversion(schema_dict: Dict[str, Any]) -> bool:
55
+ """
56
+ Check if a schema needs conversion for Gemini.
57
+
58
+ Returns True if the schema has:
59
+ - Self-references or circular references
60
+ - Dict fields (additionalProperties) that Gemini doesn't handle well
61
+ - Empty object definitions that Gemini rejects
62
+ """
63
+ # Check for dict fields (additionalProperties) anywhere in the schema
64
+ if has_additional_properties(schema_dict):
65
+ return True
66
+
67
+ # Check if schema has $defs with circular references
68
+ if "$defs" in schema_dict:
69
+ defs = schema_dict["$defs"]
70
+ for def_name, def_schema in defs.items():
71
+ ref_path = f"#/$defs/{def_name}"
72
+ if has_self_reference(def_schema, ref_path):
73
+ return True
74
+
75
+ return False
76
+
77
+
78
+ def has_additional_properties(schema: Any) -> bool:
79
+ """Check if schema has additionalProperties (Dict fields)"""
80
+ if isinstance(schema, dict):
81
+ # Direct check
82
+ if "additionalProperties" in schema:
83
+ return True
84
+
85
+ # Check properties recursively
86
+ if "properties" in schema:
87
+ for prop_schema in schema["properties"].values():
88
+ if has_additional_properties(prop_schema):
89
+ return True
90
+
91
+ # Check array items
92
+ if "items" in schema:
93
+ if has_additional_properties(schema["items"]):
94
+ return True
95
+
96
+ return False
97
+
98
+
99
+ def has_self_reference(schema: Dict, target_ref: str) -> bool:
100
+ """Check if a schema references itself (directly or indirectly)"""
101
+ if isinstance(schema, dict):
102
+ # Direct self-reference
103
+ if schema.get("$ref") == target_ref:
104
+ return True
105
+
106
+ # Check properties
107
+ if "properties" in schema:
108
+ for prop_schema in schema["properties"].values():
109
+ if has_self_reference(prop_schema, target_ref):
110
+ return True
111
+
112
+ # Check array items
113
+ if "items" in schema:
114
+ if has_self_reference(schema["items"], target_ref):
115
+ return True
116
+
117
+ # Check anyOf/oneOf/allOf
118
+ for key in ["anyOf", "oneOf", "allOf"]:
119
+ if key in schema:
120
+ for sub_schema in schema[key]:
121
+ if has_self_reference(sub_schema, target_ref):
122
+ return True
123
+
124
+ return False
125
+
126
+
18
127
  def format_image_for_message(image: Image) -> Optional[Dict[str, Any]]:
19
128
  # Case 1: Image is a URL
20
129
  # Download the image from the URL and add it as base64 encoded data
@@ -66,7 +175,9 @@ def format_image_for_message(image: Image) -> Optional[Dict[str, Any]]:
66
175
  return None
67
176
 
68
177
 
69
- def convert_schema(schema_dict: Dict[str, Any], root_schema: Optional[Dict[str, Any]] = None) -> Optional[Schema]:
178
+ def convert_schema(
179
+ schema_dict: Dict[str, Any], root_schema: Optional[Dict[str, Any]] = None, visited_refs: Optional[set] = None
180
+ ) -> Optional[Schema]:
70
181
  """
71
182
  Recursively convert a JSON-like schema dictionary to a types.Schema object.
72
183
 
@@ -74,23 +185,39 @@ def convert_schema(schema_dict: Dict[str, Any], root_schema: Optional[Dict[str,
74
185
  schema_dict (dict): The JSON schema dictionary with keys like "type", "description",
75
186
  "properties", and "required".
76
187
  root_schema (dict, optional): The root schema containing $defs for resolving $ref
188
+ visited_refs (set, optional): Set of visited $ref paths to detect circular references
77
189
 
78
190
  Returns:
79
191
  types.Schema: The converted schema.
80
192
  """
81
193
 
82
- # If this is the initial call, set root_schema to self
194
+ # If this is the initial call, set root_schema to self and initialize visited_refs
83
195
  if root_schema is None:
84
196
  root_schema = schema_dict
197
+ if visited_refs is None:
198
+ visited_refs = set()
85
199
 
86
- # Handle $ref references
200
+ # Handle $ref references with cycle detection
87
201
  if "$ref" in schema_dict:
88
202
  ref_path = schema_dict["$ref"]
203
+
204
+ # Check for circular reference
205
+ if ref_path in visited_refs:
206
+ # Return a basic object schema to break the cycle
207
+ return Schema(
208
+ type=GeminiType.OBJECT,
209
+ description=f"Circular reference to {ref_path}",
210
+ )
211
+
89
212
  if ref_path.startswith("#/$defs/"):
90
213
  def_name = ref_path.split("/")[-1]
91
214
  if "$defs" in root_schema and def_name in root_schema["$defs"]:
215
+ # Add to visited set before recursing
216
+ new_visited = visited_refs.copy()
217
+ new_visited.add(ref_path)
218
+
92
219
  referenced_schema = root_schema["$defs"][def_name]
93
- return convert_schema(referenced_schema, root_schema)
220
+ return convert_schema(referenced_schema, root_schema, new_visited)
94
221
  # If we can't resolve the reference, return None
95
222
  return None
96
223
 
@@ -103,7 +230,7 @@ def convert_schema(schema_dict: Dict[str, Any], root_schema: Optional[Dict[str,
103
230
  # Handle enum types
104
231
  if "enum" in schema_dict:
105
232
  enum_values = schema_dict["enum"]
106
- return Schema(type=Type.STRING, enum=enum_values, description=description, default=default)
233
+ return Schema(type=GeminiType.STRING, enum=enum_values, description=description, default=default)
107
234
 
108
235
  if schema_type == "object":
109
236
  # Handle regular objects with properties
@@ -117,8 +244,8 @@ def convert_schema(schema_dict: Dict[str, Any], root_schema: Optional[Dict[str,
117
244
  prop_def["type"] = prop_type[0]
118
245
  is_nullable = True
119
246
 
120
- # Process property schema (pass root_schema for $ref resolution)
121
- converted_schema = convert_schema(prop_def, root_schema)
247
+ # Process property schema (pass root_schema and visited_refs for $ref resolution)
248
+ converted_schema = convert_schema(prop_def, root_schema, visited_refs)
122
249
  if converted_schema is not None:
123
250
  if is_nullable:
124
251
  converted_schema.nullable = True
@@ -128,14 +255,14 @@ def convert_schema(schema_dict: Dict[str, Any], root_schema: Optional[Dict[str,
128
255
 
129
256
  if properties:
130
257
  return Schema(
131
- type=Type.OBJECT,
258
+ type=GeminiType.OBJECT,
132
259
  properties=properties,
133
260
  required=required,
134
261
  description=description,
135
262
  default=default,
136
263
  )
137
264
  else:
138
- return Schema(type=Type.OBJECT, description=description, default=default)
265
+ return Schema(type=GeminiType.OBJECT, description=description, default=default)
139
266
 
140
267
  # Handle Dict types (objects with additionalProperties but no properties)
141
268
  elif "additionalProperties" in schema_dict:
@@ -170,7 +297,7 @@ def convert_schema(schema_dict: Dict[str, Any], root_schema: Optional[Dict[str,
170
297
  placeholder_properties["example_key"].items = {} # type: ignore
171
298
 
172
299
  return Schema(
173
- type=Type.OBJECT,
300
+ type=GeminiType.OBJECT,
174
301
  properties=placeholder_properties,
175
302
  description=description
176
303
  or f"Dictionary with {value_type.lower()} values{type_description_suffix}. Can contain any number of key-value pairs.",
@@ -178,21 +305,22 @@ def convert_schema(schema_dict: Dict[str, Any], root_schema: Optional[Dict[str,
178
305
  )
179
306
  else:
180
307
  # additionalProperties is false or true
181
- return Schema(type=Type.OBJECT, description=description, default=default)
308
+ return Schema(type=GeminiType.OBJECT, description=description, default=default)
182
309
 
183
310
  # Handle empty objects
184
311
  else:
185
- return Schema(type=Type.OBJECT, description=description, default=default)
312
+ return Schema(type=GeminiType.OBJECT, description=description, default=default)
186
313
 
187
314
  elif schema_type == "array" and "items" in schema_dict:
188
315
  if not schema_dict["items"]: # Handle empty {}
189
- items = Schema(type=Type.STRING)
316
+ items = Schema(type=GeminiType.STRING)
190
317
  else:
191
- items = convert_schema(schema_dict["items"], root_schema)
318
+ converted_items = convert_schema(schema_dict["items"], root_schema, visited_refs)
319
+ items = converted_items if converted_items is not None else Schema(type=GeminiType.STRING)
192
320
  min_items = schema_dict.get("minItems")
193
321
  max_items = schema_dict.get("maxItems")
194
322
  return Schema(
195
- type=Type.ARRAY,
323
+ type=GeminiType.ARRAY,
196
324
  description=description,
197
325
  items=items,
198
326
  min_items=min_items,
@@ -201,7 +329,7 @@ def convert_schema(schema_dict: Dict[str, Any], root_schema: Optional[Dict[str,
201
329
 
202
330
  elif schema_type == "string":
203
331
  schema_kwargs = {
204
- "type": Type.STRING,
332
+ "type": GeminiType.STRING,
205
333
  "description": description,
206
334
  "default": default,
207
335
  }
@@ -224,7 +352,7 @@ def convert_schema(schema_dict: Dict[str, Any], root_schema: Optional[Dict[str,
224
352
  elif schema_type == "" and "anyOf" in schema_dict:
225
353
  any_of = []
226
354
  for sub_schema in schema_dict["anyOf"]:
227
- sub_schema_converted = convert_schema(sub_schema, root_schema)
355
+ sub_schema_converted = convert_schema(sub_schema, root_schema, visited_refs)
228
356
  any_of.append(sub_schema_converted)
229
357
 
230
358
  is_nullable = False
@@ -279,6 +279,15 @@ def format_messages(messages: List[Message]) -> Tuple[List[Dict[str, str]], str]
279
279
  type="tool_use",
280
280
  )
281
281
  )
282
+ elif message.role == "tool":
283
+ content = []
284
+ content.append(
285
+ {
286
+ "type": "tool_result",
287
+ "tool_use_id": message.tool_call_id,
288
+ "content": str(message.content),
289
+ }
290
+ )
282
291
 
283
292
  # Skip empty assistant responses
284
293
  if message.role == "assistant" and not content:
@@ -44,6 +44,8 @@ def print_response_stream(
44
44
  console: Optional[Any] = None,
45
45
  add_history_to_context: Optional[bool] = None,
46
46
  dependencies: Optional[Dict[str, Any]] = None,
47
+ add_dependencies_to_context: Optional[bool] = None,
48
+ add_session_state_to_context: Optional[bool] = None,
47
49
  metadata: Optional[Dict[str, Any]] = None,
48
50
  **kwargs: Any,
49
51
  ):
@@ -90,6 +92,8 @@ def print_response_stream(
90
92
  knowledge_filters=knowledge_filters,
91
93
  debug_mode=debug_mode,
92
94
  add_history_to_context=add_history_to_context,
95
+ add_dependencies_to_context=add_dependencies_to_context,
96
+ add_session_state_to_context=add_session_state_to_context,
93
97
  dependencies=dependencies,
94
98
  metadata=metadata,
95
99
  **kwargs,
@@ -223,6 +227,8 @@ async def aprint_response_stream(
223
227
  console: Optional[Any] = None,
224
228
  add_history_to_context: Optional[bool] = None,
225
229
  dependencies: Optional[Dict[str, Any]] = None,
230
+ add_dependencies_to_context: Optional[bool] = None,
231
+ add_session_state_to_context: Optional[bool] = None,
226
232
  metadata: Optional[Dict[str, Any]] = None,
227
233
  **kwargs: Any,
228
234
  ):
@@ -269,6 +275,8 @@ async def aprint_response_stream(
269
275
  knowledge_filters=knowledge_filters,
270
276
  debug_mode=debug_mode,
271
277
  add_history_to_context=add_history_to_context,
278
+ add_dependencies_to_context=add_dependencies_to_context,
279
+ add_session_state_to_context=add_session_state_to_context,
272
280
  dependencies=dependencies,
273
281
  metadata=metadata,
274
282
  **kwargs,
@@ -490,6 +498,8 @@ def print_response(
490
498
  console: Optional[Any] = None,
491
499
  add_history_to_context: Optional[bool] = None,
492
500
  dependencies: Optional[Dict[str, Any]] = None,
501
+ add_dependencies_to_context: Optional[bool] = None,
502
+ add_session_state_to_context: Optional[bool] = None,
493
503
  metadata: Optional[Dict[str, Any]] = None,
494
504
  **kwargs: Any,
495
505
  ):
@@ -527,6 +537,8 @@ def print_response(
527
537
  knowledge_filters=knowledge_filters,
528
538
  debug_mode=debug_mode,
529
539
  add_history_to_context=add_history_to_context,
540
+ add_dependencies_to_context=add_dependencies_to_context,
541
+ add_session_state_to_context=add_session_state_to_context,
530
542
  dependencies=dependencies,
531
543
  metadata=metadata,
532
544
  **kwargs,
@@ -590,6 +602,8 @@ async def aprint_response(
590
602
  console: Optional[Any] = None,
591
603
  add_history_to_context: Optional[bool] = None,
592
604
  dependencies: Optional[Dict[str, Any]] = None,
605
+ add_dependencies_to_context: Optional[bool] = None,
606
+ add_session_state_to_context: Optional[bool] = None,
593
607
  metadata: Optional[Dict[str, Any]] = None,
594
608
  **kwargs: Any,
595
609
  ):
@@ -627,6 +641,8 @@ async def aprint_response(
627
641
  knowledge_filters=knowledge_filters,
628
642
  debug_mode=debug_mode,
629
643
  add_history_to_context=add_history_to_context,
644
+ add_dependencies_to_context=add_dependencies_to_context,
645
+ add_session_state_to_context=add_session_state_to_context,
630
646
  dependencies=dependencies,
631
647
  metadata=metadata,
632
648
  **kwargs,