autobyteus 1.1.6__py3-none-any.whl → 1.1.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.
Files changed (43) hide show
  1. autobyteus/agent/context/agent_runtime_state.py +7 -1
  2. autobyteus/agent/handlers/tool_result_event_handler.py +121 -89
  3. autobyteus/agent/llm_response_processor/provider_aware_tool_usage_processor.py +7 -1
  4. autobyteus/agent/tool_invocation.py +25 -1
  5. autobyteus/agent_team/agent_team_builder.py +22 -1
  6. autobyteus/agent_team/context/agent_team_runtime_state.py +0 -2
  7. autobyteus/llm/llm_factory.py +25 -57
  8. autobyteus/llm/ollama_provider_resolver.py +1 -0
  9. autobyteus/llm/providers.py +1 -0
  10. autobyteus/llm/token_counter/token_counter_factory.py +2 -0
  11. autobyteus/multimedia/audio/audio_model.py +2 -1
  12. autobyteus/multimedia/image/image_model.py +2 -1
  13. autobyteus/task_management/tools/publish_task_plan.py +4 -16
  14. autobyteus/task_management/tools/update_task_status.py +4 -19
  15. autobyteus/tools/__init__.py +2 -4
  16. autobyteus/tools/base_tool.py +98 -29
  17. autobyteus/tools/browser/standalone/__init__.py +0 -1
  18. autobyteus/tools/google_search.py +149 -0
  19. autobyteus/tools/mcp/schema_mapper.py +29 -71
  20. autobyteus/tools/multimedia/audio_tools.py +3 -3
  21. autobyteus/tools/multimedia/image_tools.py +5 -5
  22. autobyteus/tools/parameter_schema.py +82 -89
  23. autobyteus/tools/pydantic_schema_converter.py +81 -0
  24. autobyteus/tools/usage/formatters/default_json_example_formatter.py +89 -20
  25. autobyteus/tools/usage/formatters/default_xml_example_formatter.py +115 -41
  26. autobyteus/tools/usage/formatters/default_xml_schema_formatter.py +50 -20
  27. autobyteus/tools/usage/formatters/gemini_json_example_formatter.py +55 -22
  28. autobyteus/tools/usage/formatters/google_json_example_formatter.py +54 -21
  29. autobyteus/tools/usage/formatters/openai_json_example_formatter.py +53 -23
  30. autobyteus/tools/usage/parsers/default_xml_tool_usage_parser.py +270 -94
  31. autobyteus/tools/usage/providers/tool_manifest_provider.py +39 -14
  32. autobyteus-1.1.8.dist-info/METADATA +204 -0
  33. {autobyteus-1.1.6.dist-info → autobyteus-1.1.8.dist-info}/RECORD +39 -40
  34. examples/run_google_slides_agent.py +2 -2
  35. examples/run_mcp_google_slides_client.py +1 -1
  36. examples/run_sqlite_agent.py +1 -1
  37. autobyteus/tools/ask_user_input.py +0 -40
  38. autobyteus/tools/browser/standalone/factory/google_search_factory.py +0 -25
  39. autobyteus/tools/browser/standalone/google_search_ui.py +0 -126
  40. autobyteus-1.1.6.dist-info/METADATA +0 -161
  41. {autobyteus-1.1.6.dist-info → autobyteus-1.1.8.dist-info}/WHEEL +0 -0
  42. {autobyteus-1.1.6.dist-info → autobyteus-1.1.8.dist-info}/licenses/LICENSE +0 -0
  43. {autobyteus-1.1.6.dist-info → autobyteus-1.1.8.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,8 @@
1
1
  # file: autobyteus/autobyteus/tools/usage/formatters/default_json_example_formatter.py
2
- from typing import Dict, Any, TYPE_CHECKING, List, Optional
2
+ import json
3
+ from typing import Dict, Any, TYPE_CHECKING, List, Optional, Union
3
4
 
4
- from autobyteus.tools.parameter_schema import ParameterType, ParameterDefinition
5
+ from autobyteus.tools.parameter_schema import ParameterType, ParameterDefinition, ParameterSchema
5
6
  from .base_formatter import BaseExampleFormatter
6
7
 
7
8
  if TYPE_CHECKING:
@@ -10,20 +11,47 @@ if TYPE_CHECKING:
10
11
  class DefaultJsonExampleFormatter(BaseExampleFormatter):
11
12
  """
12
13
  Formats a tool usage example into a generic JSON format.
13
- It intelligently generates detailed examples for complex object schemas.
14
+ It intelligently generates detailed examples for complex object schemas, providing
15
+ both a basic (required parameters only) and an advanced (all parameters) example
16
+ if optional parameters are available.
14
17
  """
15
18
 
16
- def provide(self, tool_definition: 'ToolDefinition') -> Dict:
19
+ def provide(self, tool_definition: 'ToolDefinition') -> str:
20
+ """
21
+ Generates a formatted string containing basic and optionally an advanced usage example for the tool.
22
+ """
23
+ basic_example_dict = self._create_example_structure(tool_definition, mode='basic')
24
+ basic_example_str = "### Example 1: Basic Call (Required Arguments)\n"
25
+ basic_example_str += "```json\n"
26
+ basic_example_str += json.dumps(basic_example_dict, indent=2)
27
+ basic_example_str += "\n```"
28
+
29
+ if not self._schema_has_advanced_params(tool_definition.argument_schema):
30
+ return basic_example_str
31
+
32
+ advanced_example_dict = self._create_example_structure(tool_definition, mode='advanced')
33
+ advanced_example_str = "### Example 2: Advanced Call (With Optional Arguments)\n"
34
+ advanced_example_str += "```json\n"
35
+ advanced_example_str += json.dumps(advanced_example_dict, indent=2)
36
+ advanced_example_str += "\n```"
37
+
38
+ return f"{basic_example_str}\n\n{advanced_example_str}"
39
+
40
+
41
+ def _create_example_structure(self, tool_definition: 'ToolDefinition', mode: str) -> Dict:
42
+ """Helper to create the full tool call example structure for a given mode."""
17
43
  tool_name = tool_definition.name
18
44
  arg_schema = tool_definition.argument_schema
19
45
  arguments = {}
20
46
 
21
47
  if arg_schema and arg_schema.parameters:
22
- for param_def in arg_schema.parameters:
23
- # Always include required parameters in the example.
24
- # Also include optional parameters that have a default value to show common usage.
25
- if param_def.required or param_def.default_value is not None:
26
- arguments[param_def.name] = self._generate_placeholder_value(param_def)
48
+ params_to_render = arg_schema.parameters
49
+ if mode == 'basic':
50
+ # In basic mode, we only render required parameters.
51
+ params_to_render = [p for p in arg_schema.parameters if p.required]
52
+
53
+ for param_def in params_to_render:
54
+ arguments[param_def.name] = self._generate_placeholder_value(param_def, mode=mode)
27
55
 
28
56
  return {
29
57
  "tool": {
@@ -32,12 +60,34 @@ class DefaultJsonExampleFormatter(BaseExampleFormatter):
32
60
  },
33
61
  }
34
62
 
35
- def _generate_placeholder_value(self, param_def: ParameterDefinition) -> Any:
63
+ def _schema_has_advanced_params(self, schema: Optional[ParameterSchema]) -> bool:
64
+ """Recursively checks if a schema or any of its sub-schemas have non-required parameters."""
65
+ if not schema:
66
+ return False
67
+ for param in schema.parameters:
68
+ if not param.required:
69
+ return True # Found an optional param at this level
70
+ if param.object_schema and self._schema_has_advanced_params(param.object_schema):
71
+ return True # Found an optional param in a nested object
72
+ if isinstance(param.array_item_schema, ParameterSchema) and self._schema_has_advanced_params(param.array_item_schema):
73
+ return True # Found an optional param in an array of objects
74
+ return False
75
+
76
+ def _generate_placeholder_value(self, param_def: ParameterDefinition, mode: str = 'basic') -> Any:
77
+ """
78
+ Generates a placeholder value for a parameter, recursing for complex types.
79
+ The mode determines whether to include optional fields in nested structures.
80
+ """
36
81
  # If an object parameter has a detailed schema, generate a structured example from it.
37
82
  if param_def.param_type == ParameterType.OBJECT and param_def.object_schema:
38
- # We pass the full schema document to allow for resolving $refs
39
- return DefaultJsonExampleFormatter._generate_example_from_schema(param_def.object_schema, param_def.object_schema)
83
+ return DefaultJsonExampleFormatter._generate_example_from_schema(param_def.object_schema, param_def.object_schema, mode=mode)
40
84
 
85
+ # Handle arrays with a detailed item schema.
86
+ if param_def.param_type == ParameterType.ARRAY and param_def.array_item_schema:
87
+ # Generate one example item for the array to keep it concise.
88
+ example_item = DefaultJsonExampleFormatter._generate_example_from_schema(param_def.array_item_schema, param_def.array_item_schema, mode=mode)
89
+ return [example_item]
90
+
41
91
  # Fallback to simple placeholder generation for primitives or objects without schemas.
42
92
  if param_def.default_value is not None: return param_def.default_value
43
93
  if param_def.param_type == ParameterType.STRING: return f"example_{param_def.name}"
@@ -46,15 +96,34 @@ class DefaultJsonExampleFormatter(BaseExampleFormatter):
46
96
  if param_def.param_type == ParameterType.BOOLEAN: return True
47
97
  if param_def.param_type == ParameterType.ENUM: return param_def.enum_values[0] if param_def.enum_values else "enum_val"
48
98
  if param_def.param_type == ParameterType.OBJECT: return {"key": "value"}
49
- if param_def.param_type == ParameterType.ARRAY: return ["item1", "item2"]
99
+ if param_def.param_type == ParameterType.ARRAY: return ["item1", "item2"] # This now only applies to generic arrays
50
100
  return "placeholder"
51
101
 
52
102
  @staticmethod
53
- def _generate_example_from_schema(sub_schema: Dict[str, Any], full_schema: Dict[str, Any]) -> Any:
103
+ def _generate_example_from_schema(
104
+ sub_schema: Union[Dict[str, Any], 'ParameterSchema', 'ParameterType'],
105
+ full_schema: Union[Dict[str, Any], 'ParameterSchema', 'ParameterType'],
106
+ mode: str = 'basic'
107
+ ) -> Any:
54
108
  """
55
109
  Recursively generates an example value from a JSON schema dictionary.
56
110
  This is a static method so it can be reused by other formatters.
111
+ The 'mode' parameter controls whether optional fields are included in nested objects.
112
+ The default mode is 'basic' to maintain backward compatibility.
57
113
  """
114
+ # FIX: Handle primitive ParameterType for array items directly.
115
+ if isinstance(sub_schema, ParameterType):
116
+ if sub_schema == ParameterType.STRING: return "example_string"
117
+ if sub_schema == ParameterType.INTEGER: return 1
118
+ if sub_schema == ParameterType.FLOAT: return 1.1
119
+ if sub_schema == ParameterType.BOOLEAN: return True
120
+ return "unknown_primitive"
121
+
122
+ if isinstance(sub_schema, ParameterSchema):
123
+ sub_schema = sub_schema.to_json_schema_dict()
124
+ if isinstance(full_schema, ParameterSchema):
125
+ full_schema = full_schema.to_json_schema_dict()
126
+
58
127
  if "$ref" in sub_schema:
59
128
  ref_path = sub_schema["$ref"]
60
129
  try:
@@ -63,7 +132,7 @@ class DefaultJsonExampleFormatter(BaseExampleFormatter):
63
132
  resolved_schema = full_schema
64
133
  for part in parts:
65
134
  resolved_schema = resolved_schema[part]
66
- return DefaultJsonExampleFormatter._generate_example_from_schema(resolved_schema, full_schema)
135
+ return DefaultJsonExampleFormatter._generate_example_from_schema(resolved_schema, full_schema, mode=mode)
67
136
  except (KeyError, IndexError):
68
137
  return {"error": f"Could not resolve schema reference: {ref_path}"}
69
138
 
@@ -80,16 +149,16 @@ class DefaultJsonExampleFormatter(BaseExampleFormatter):
80
149
  properties = sub_schema.get("properties", {})
81
150
  required_fields = sub_schema.get("required", [])
82
151
  for prop_name, prop_schema in properties.items():
83
- # Include required fields and a subset of optional fields for a concise example.
84
- if prop_name in required_fields:
85
- example_obj[prop_name] = DefaultJsonExampleFormatter._generate_example_from_schema(prop_schema, full_schema)
152
+ # Include fields if in 'advanced' mode or if they are required.
153
+ if mode == 'advanced' or prop_name in required_fields:
154
+ example_obj[prop_name] = DefaultJsonExampleFormatter._generate_example_from_schema(prop_schema, full_schema, mode=mode)
86
155
  return example_obj
87
156
 
88
157
  elif schema_type == "array":
89
158
  items_schema = sub_schema.get("items")
90
159
  if isinstance(items_schema, dict):
91
- # Generate one example item for the array to keep it concise
92
- return [DefaultJsonExampleFormatter._generate_example_from_schema(items_schema, full_schema)]
160
+ # Generate one example item for the array to keep it concise.
161
+ return [DefaultJsonExampleFormatter._generate_example_from_schema(items_schema, full_schema, mode=mode)]
93
162
  else:
94
163
  return ["example_item_1"]
95
164
 
@@ -1,65 +1,103 @@
1
1
  # file: autobyteus/autobyteus/tools/usage/formatters/default_xml_example_formatter.py
2
2
  import xml.sax.saxutils
3
- import json # Import json for embedding complex objects
4
- from typing import Any, TYPE_CHECKING
3
+ import re
4
+ from typing import Any, TYPE_CHECKING, List, Optional
5
5
 
6
- from autobyteus.tools.parameter_schema import ParameterType, ParameterDefinition
6
+ from autobyteus.tools.parameter_schema import ParameterType, ParameterDefinition, ParameterSchema
7
7
  from .base_formatter import BaseExampleFormatter
8
- from .default_json_example_formatter import DefaultJsonExampleFormatter # Import for reuse
9
8
 
10
9
  if TYPE_CHECKING:
11
10
  from autobyteus.tools.registry import ToolDefinition
12
11
 
13
12
  class DefaultXmlExampleFormatter(BaseExampleFormatter):
14
- """Formats a tool usage example into a standardized XML <tool> string."""
13
+ """Formats a tool usage example into a standardized, nested XML <tool> string."""
15
14
 
16
15
  def provide(self, tool_definition: 'ToolDefinition') -> str:
17
- tool_name = tool_definition.name
18
- arg_schema = tool_definition.argument_schema
16
+ """
17
+ Generates a multi-shot example string for the given tool, including
18
+ a basic and an advanced usage case.
19
+ """
20
+ basic_example = self._generate_basic_example(tool_definition)
21
+ advanced_example = self._generate_advanced_example(tool_definition)
19
22
 
20
- example_xml_parts = [f'<tool name="{tool_name}">']
21
- arguments_part = []
23
+ examples = [basic_example]
24
+ if advanced_example:
25
+ examples.append(advanced_example)
26
+
27
+ return "\n\n".join(examples)
22
28
 
23
- if arg_schema and arg_schema.parameters:
24
- for param_def in arg_schema.parameters:
25
- if param_def.required or param_def.default_value is not None:
26
- placeholder_value = self._generate_placeholder_value(param_def)
27
- # For complex objects/arrays, we now get a dict/list.
28
- # Convert it to a JSON string for embedding in XML.
29
- if isinstance(placeholder_value, (dict, list)):
30
- value_str = json.dumps(placeholder_value, indent=2)
31
- else:
32
- value_str = str(placeholder_value)
33
-
34
- escaped_value = xml.sax.saxutils.escape(value_str)
35
-
36
- # Add newlines for readability if the value is a multiline JSON string
37
- if '\n' in escaped_value:
38
- arguments_part.append(f' <arg name="{param_def.name}">\n{escaped_value}\n </arg>')
39
- else:
40
- arguments_part.append(f' <arg name="{param_def.name}">{escaped_value}</arg>')
41
-
42
-
43
- if arguments_part:
29
+ def _schema_has_advanced_params(self, schema: Optional[ParameterSchema]) -> bool:
30
+ """Recursively checks if a schema or any of its sub-schemas have non-required parameters."""
31
+ if not schema:
32
+ return False
33
+ for param in schema.parameters:
34
+ if not param.required:
35
+ return True # Found an optional param at this level
36
+ if param.object_schema and self._schema_has_advanced_params(param.object_schema):
37
+ return True # Found an optional param in a nested object
38
+ if isinstance(param.array_item_schema, ParameterSchema) and self._schema_has_advanced_params(param.array_item_schema):
39
+ return True # Found an optional param in an array of objects
40
+ return False
41
+
42
+ def _generate_basic_example(self, tool_def: 'ToolDefinition') -> str:
43
+ """Generates an XML example including only the required parameters."""
44
+ tool_name = tool_def.name
45
+ arg_schema = tool_def.argument_schema
46
+
47
+ example_xml_parts = [
48
+ "### Example 1: Basic Call (Required Arguments)",
49
+ f'<tool name="{tool_name}">'
50
+ ]
51
+
52
+ if arg_schema and any(p.required for p in arg_schema.parameters):
44
53
  example_xml_parts.append(" <arguments>")
54
+ arguments_part = self._generate_arguments_xml(arg_schema.parameters, 2, mode='basic')
45
55
  example_xml_parts.extend(arguments_part)
46
56
  example_xml_parts.append(" </arguments>")
47
57
  else:
48
- example_xml_parts.append(" <!-- This tool takes no arguments -->")
58
+ example_xml_parts.append(" <!-- This tool has no required arguments. -->")
49
59
 
50
60
  example_xml_parts.append("</tool>")
51
61
  return "\n".join(example_xml_parts)
52
62
 
53
- def _generate_placeholder_value(self, param_def: ParameterDefinition) -> Any:
54
- # REUSE a more intelligent generator for complex objects
55
- if param_def.param_type == ParameterType.OBJECT and param_def.object_schema:
56
- return DefaultJsonExampleFormatter._generate_example_from_schema(param_def.object_schema, param_def.object_schema)
63
+ def _generate_advanced_example(self, tool_def: 'ToolDefinition') -> Optional[str]:
64
+ """
65
+ Generates a more complex example if the schema has any optional parameters
66
+ at any level of nesting.
67
+ """
68
+ arg_schema = tool_def.argument_schema
69
+ if not self._schema_has_advanced_params(arg_schema):
70
+ return None
71
+
72
+ tool_name = tool_def.name
73
+ example_xml_parts = [
74
+ "### Example 2: Advanced Call (With Optional & Nested Arguments)",
75
+ f'<tool name="{tool_name}">'
76
+ ]
77
+
78
+ if arg_schema and arg_schema.parameters:
79
+ example_xml_parts.append(" <arguments>")
80
+ arguments_part = self._generate_arguments_xml(arg_schema.parameters, 2, mode='advanced')
81
+ example_xml_parts.extend(arguments_part)
82
+ example_xml_parts.append(" </arguments>")
83
+ else:
84
+ return None
85
+
86
+ example_xml_parts.append("</tool>")
87
+ return "\n".join(example_xml_parts)
57
88
 
58
- # Fallback for primitives
89
+ def _generate_placeholder_value(self, param_def: ParameterDefinition) -> Any:
90
+ """Generates a descriptive placeholder value."""
59
91
  if param_def.default_value is not None:
60
92
  return param_def.default_value
93
+
94
+ if param_def.description:
95
+ match = re.search(r"e\.g\.,?\s*[`']([^`']+)[`']", param_def.description)
96
+ if match:
97
+ return match.group(1)
98
+
61
99
  if param_def.param_type == ParameterType.STRING:
62
- return f"example_{param_def.name}"
100
+ return f"A valid string for '{param_def.name}'"
63
101
  if param_def.param_type == ParameterType.INTEGER:
64
102
  return 123
65
103
  if param_def.param_type == ParameterType.FLOAT:
@@ -68,8 +106,44 @@ class DefaultXmlExampleFormatter(BaseExampleFormatter):
68
106
  return True
69
107
  if param_def.param_type == ParameterType.ENUM:
70
108
  return param_def.enum_values[0] if param_def.enum_values else "enum_val"
71
- if param_def.param_type == ParameterType.OBJECT:
72
- return {"key": "value"} # Fallback if no schema
73
- if param_def.param_type == ParameterType.ARRAY:
74
- return ["item1", "item2"]
75
109
  return "placeholder"
110
+
111
+ def _generate_arguments_xml(self, params: List[ParameterDefinition], indent_level: int, mode: str) -> List[str]:
112
+ """Recursively generates XML for a list of parameter definitions based on the mode."""
113
+ xml_lines = []
114
+ indent = " " * indent_level
115
+
116
+ params_to_render = params
117
+ if mode == 'basic':
118
+ params_to_render = [p for p in params if p.required]
119
+
120
+ for param_def in params_to_render:
121
+ param_name = param_def.name
122
+
123
+ if param_def.param_type == ParameterType.OBJECT and param_def.object_schema:
124
+ xml_lines.append(f'{indent}<arg name="{param_name}">')
125
+ xml_lines.extend(self._generate_arguments_xml(param_def.object_schema.parameters, indent_level + 1, mode=mode))
126
+ xml_lines.append(f'{indent}</arg>')
127
+
128
+ elif param_def.param_type == ParameterType.ARRAY:
129
+ xml_lines.append(f'{indent}<arg name="{param_name}">')
130
+
131
+ if isinstance(param_def.array_item_schema, ParameterSchema): # Array of objects
132
+ xml_lines.append(f'{indent} <item>')
133
+ xml_lines.extend(self._generate_arguments_xml(param_def.array_item_schema.parameters, indent_level + 2, mode=mode))
134
+ xml_lines.append(f'{indent} </item>')
135
+ xml_lines.append(f'{indent} <!-- (more items as needed) -->')
136
+ else: # Array of primitives
137
+ placeholder_1 = self._generate_placeholder_value(ParameterDefinition(name=f"{param_name}_item_1", param_type=ParameterType.STRING, description="An item from the list."))
138
+ placeholder_2 = self._generate_placeholder_value(ParameterDefinition(name=f"{param_name}_item_2", param_type=ParameterType.STRING, description="An item from the list."))
139
+ xml_lines.append(f'{indent} <item>{xml.sax.saxutils.escape(str(placeholder_1))}</item>')
140
+ xml_lines.append(f'{indent} <item>{xml.sax.saxutils.escape(str(placeholder_2))}</item>')
141
+
142
+ xml_lines.append(f'{indent}</arg>')
143
+
144
+ else: # Primitive types
145
+ placeholder_value = self._generate_placeholder_value(param_def)
146
+ escaped_value = xml.sax.saxutils.escape(str(placeholder_value))
147
+ xml_lines.append(f'{indent}<arg name="{param_name}">{escaped_value}</arg>')
148
+
149
+ return xml_lines
@@ -1,20 +1,20 @@
1
1
  # file: autobyteus/autobyteus/tools/usage/formatters/default_xml_schema_formatter.py
2
2
  import xml.sax.saxutils
3
- from typing import TYPE_CHECKING
3
+ from typing import TYPE_CHECKING, List
4
4
 
5
- from autobyteus.tools.parameter_schema import ParameterType
5
+ from autobyteus.tools.parameter_schema import ParameterType, ParameterDefinition, ParameterSchema
6
6
  from .base_formatter import BaseSchemaFormatter
7
7
 
8
8
  if TYPE_CHECKING:
9
9
  from autobyteus.tools.registry import ToolDefinition
10
10
 
11
11
  class DefaultXmlSchemaFormatter(BaseSchemaFormatter):
12
- """Formats a tool's schema into a standardized XML string."""
12
+ """Formats a tool's schema into a standardized, potentially nested, XML string."""
13
13
 
14
14
  def provide(self, tool_definition: 'ToolDefinition') -> str:
15
- arg_schema = tool_definition.argument_schema
16
15
  tool_name = tool_definition.name
17
16
  description = tool_definition.description
17
+ arg_schema = tool_definition.argument_schema
18
18
 
19
19
  escaped_description = xml.sax.saxutils.escape(description) if description else ""
20
20
  tool_tag = f'<tool name="{tool_name}" description="{escaped_description}">'
@@ -22,25 +22,55 @@ class DefaultXmlSchemaFormatter(BaseSchemaFormatter):
22
22
 
23
23
  if arg_schema and arg_schema.parameters:
24
24
  xml_parts.append(" <arguments>")
25
- for param in arg_schema.parameters:
26
- arg_tag = f' <arg name="{param.name}"'
27
- arg_tag += f' type="{param.param_type.value}"'
28
- if param.description:
29
- escaped_param_desc = xml.sax.saxutils.escape(param.description)
30
- arg_tag += f' description="{escaped_param_desc}"'
31
- arg_tag += f" required=\"{'true' if param.required else 'false'}\""
32
-
33
- if param.default_value is not None:
34
- arg_tag += f' default="{xml.sax.saxutils.escape(str(param.default_value))}"'
35
- if param.param_type == ParameterType.ENUM and param.enum_values:
36
- escaped_enum_values = [xml.sax.saxutils.escape(ev) for ev in param.enum_values]
37
- arg_tag += f' enum_values="{",".join(escaped_enum_values)}"'
38
-
39
- arg_tag += " />"
40
- xml_parts.append(arg_tag)
25
+ xml_parts.extend(self._format_params_recursively(arg_schema.parameters, 2))
41
26
  xml_parts.append(" </arguments>")
42
27
  else:
43
28
  xml_parts.append(" <!-- This tool takes no arguments -->")
44
29
 
45
30
  xml_parts.append("</tool>")
46
31
  return "\n".join(xml_parts)
32
+
33
+ def _format_params_recursively(self, params: List[ParameterDefinition], indent_level: int) -> List[str]:
34
+ """Recursively formats parameter definitions into XML strings."""
35
+ xml_lines = []
36
+ indent = " " * indent_level
37
+
38
+ for param in params:
39
+ attrs = [
40
+ f'name="{param.name}"',
41
+ f'type="{param.param_type.value}"'
42
+ ]
43
+ if param.description:
44
+ attrs.append(f'description="{xml.sax.saxutils.escape(param.description)}"')
45
+
46
+ attrs.append(f"required=\"{'true' if param.required else 'false'}\"")
47
+
48
+ if param.default_value is not None:
49
+ attrs.append(f'default="{xml.sax.saxutils.escape(str(param.default_value))}"')
50
+ if param.param_type == ParameterType.ENUM and param.enum_values:
51
+ escaped_enum = [xml.sax.saxutils.escape(ev) for ev in param.enum_values]
52
+ attrs.append(f'enum_values="{",".join(escaped_enum)}"')
53
+
54
+ is_object = param.param_type == ParameterType.OBJECT and param.object_schema
55
+ is_array = param.param_type == ParameterType.ARRAY and param.array_item_schema
56
+
57
+ if is_object:
58
+ xml_lines.append(f'{indent}<arg {" ".join(attrs)}>')
59
+ xml_lines.extend(self._format_params_recursively(param.object_schema.parameters, indent_level + 1))
60
+ xml_lines.append(f'{indent}</arg>')
61
+ elif is_array:
62
+ xml_lines.append(f'{indent}<arg {" ".join(attrs)}>')
63
+ if isinstance(param.array_item_schema, ParameterSchema):
64
+ # Array of objects
65
+ xml_lines.append(f'{indent} <items type="object">')
66
+ xml_lines.extend(self._format_params_recursively(param.array_item_schema.parameters, indent_level + 2))
67
+ xml_lines.append(f'{indent} </items>')
68
+ elif isinstance(param.array_item_schema, ParameterType):
69
+ # Array of primitives
70
+ xml_lines.append(f'{indent} <items type="{param.array_item_schema.value}" />')
71
+ xml_lines.append(f'{indent}</arg>')
72
+ else:
73
+ # This is a simple/primitive type or a generic array
74
+ xml_lines.append(f'{indent}<arg {" ".join(attrs)} />')
75
+
76
+ return xml_lines
@@ -1,7 +1,8 @@
1
1
  # file: autobyteus/autobyteus/tools/usage/formatters/gemini_json_example_formatter.py
2
- from typing import Dict, Any, TYPE_CHECKING
2
+ import json
3
+ from typing import Dict, Any, TYPE_CHECKING, Optional
3
4
 
4
- from autobyteus.tools.parameter_schema import ParameterType, ParameterDefinition
5
+ from autobyteus.tools.parameter_schema import ParameterSchema, ParameterDefinition
5
6
  from .base_formatter import BaseExampleFormatter
6
7
  from .default_json_example_formatter import DefaultJsonExampleFormatter # Import for reuse
7
8
 
@@ -9,32 +10,64 @@ if TYPE_CHECKING:
9
10
  from autobyteus.tools.registry import ToolDefinition
10
11
 
11
12
  class GeminiJsonExampleFormatter(BaseExampleFormatter):
12
- """Formats a tool usage example into the Google Gemini tool_calls format."""
13
+ """
14
+ Formats a tool usage example into the Google Gemini tool_calls format.
15
+ Provides both basic (required only) and advanced (all) examples if optional
16
+ parameters exist for the tool.
17
+ """
13
18
 
14
- def provide(self, tool_definition: 'ToolDefinition') -> Dict:
19
+ def provide(self, tool_definition: 'ToolDefinition') -> str:
20
+ """
21
+ Generates a formatted string containing basic and optionally an advanced usage example for the tool.
22
+ """
23
+ basic_example_dict = self._create_example_structure(tool_definition, mode='basic')
24
+ basic_example_str = "### Example 1: Basic Call (Required Arguments)\n"
25
+ basic_example_str += "```json\n"
26
+ basic_example_str += json.dumps(basic_example_dict, indent=2)
27
+ basic_example_str += "\n```"
28
+
29
+ if not self._schema_has_advanced_params(tool_definition.argument_schema):
30
+ return basic_example_str
31
+
32
+ advanced_example_dict = self._create_example_structure(tool_definition, mode='advanced')
33
+ advanced_example_str = "### Example 2: Advanced Call (With Optional Arguments)\n"
34
+ advanced_example_str += "```json\n"
35
+ advanced_example_str += json.dumps(advanced_example_dict, indent=2)
36
+ advanced_example_str += "\n```"
37
+
38
+ return f"{basic_example_str}\n\n{advanced_example_str}"
39
+
40
+ def _create_example_structure(self, tool_definition: 'ToolDefinition', mode: str) -> Dict:
41
+ """Helper to create a single Gemini tool call example for a given mode."""
15
42
  tool_name = tool_definition.name
16
43
  arg_schema = tool_definition.argument_schema
17
44
  arguments = {}
18
45
 
19
46
  if arg_schema and arg_schema.parameters:
20
- for param_def in arg_schema.parameters:
21
- if param_def.required or param_def.default_value is not None:
22
- arguments[param_def.name] = self._generate_placeholder_value(param_def)
23
-
47
+ params_to_render = arg_schema.parameters
48
+ if mode == 'basic':
49
+ params_to_render = [p for p in arg_schema.parameters if p.required]
50
+
51
+ for param_def in params_to_render:
52
+ # Use the intelligent placeholder generator from the default formatter
53
+ arguments[param_def.name] = DefaultJsonExampleFormatter._generate_example_from_schema(
54
+ param_def.object_schema or param_def.array_item_schema or param_def.param_type,
55
+ param_def.object_schema or arg_schema,
56
+ mode=mode
57
+ ) if param_def.object_schema or param_def.array_item_schema else self._generate_simple_placeholder(param_def)
58
+
24
59
  return {"name": tool_name, "args": arguments}
25
60
 
26
- def _generate_placeholder_value(self, param_def: ParameterDefinition) -> Any:
27
- # REUSE a more intelligent generator for complex objects
28
- if param_def.param_type == ParameterType.OBJECT and param_def.object_schema:
29
- return DefaultJsonExampleFormatter._generate_example_from_schema(param_def.object_schema, param_def.object_schema)
30
-
31
- # Fallback for primitives
61
+ def _schema_has_advanced_params(self, schema: Optional[ParameterSchema]) -> bool:
62
+ """Recursively checks if a schema or any of its sub-schemas have non-required parameters."""
63
+ if not schema: return False
64
+ for param in schema.parameters:
65
+ if not param.required: return True
66
+ if param.object_schema and self._schema_has_advanced_params(param.object_schema): return True
67
+ if isinstance(param.array_item_schema, ParameterSchema) and self._schema_has_advanced_params(param.array_item_schema): return True
68
+ return False
69
+
70
+ def _generate_simple_placeholder(self, param_def: ParameterDefinition) -> Any:
71
+ """Generates a simple placeholder for primitive types."""
32
72
  if param_def.default_value is not None: return param_def.default_value
33
- if param_def.param_type == ParameterType.STRING: return f"example_{param_def.name}"
34
- if param_def.param_type == ParameterType.INTEGER: return 123
35
- if param_def.param_type == ParameterType.FLOAT: return 123.45
36
- if param_def.param_type == ParameterType.BOOLEAN: return True
37
- if param_def.param_type == ParameterType.ENUM: return param_def.enum_values[0] if param_def.enum_values else "enum_val"
38
- if param_def.param_type == ParameterType.OBJECT: return {"key": "value"}
39
- if param_def.param_type == ParameterType.ARRAY: return ["item1", "item2"]
40
- return "placeholder"
73
+ return DefaultJsonExampleFormatter._generate_example_from_schema(param_def.param_type, param_def.param_type, mode='basic')