autobyteus 1.1.6__py3-none-any.whl → 1.1.7__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 +100 -88
  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.7.dist-info/METADATA +204 -0
  33. {autobyteus-1.1.6.dist-info → autobyteus-1.1.7.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.7.dist-info}/WHEEL +0 -0
  42. {autobyteus-1.1.6.dist-info → autobyteus-1.1.7.dist-info}/licenses/LICENSE +0 -0
  43. {autobyteus-1.1.6.dist-info → autobyteus-1.1.7.dist-info}/top_level.txt +0 -0
@@ -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')
@@ -1,7 +1,8 @@
1
1
  # file: autobyteus/autobyteus/tools/usage/formatters/google_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
  # Import for reuse of the intelligent example generation logic
7
8
  from .default_json_example_formatter import DefaultJsonExampleFormatter
@@ -10,32 +11,64 @@ if TYPE_CHECKING:
10
11
  from autobyteus.tools.registry import ToolDefinition
11
12
 
12
13
  class GoogleJsonExampleFormatter(BaseExampleFormatter):
13
- """Formats a tool usage example into the Google JSON tool_calls format."""
14
+ """
15
+ Formats a tool usage example into the Google JSON tool_calls format.
16
+ Provides both basic (required only) and advanced (all) examples if optional
17
+ parameters exist for the tool.
18
+ """
14
19
 
15
- def provide(self, tool_definition: 'ToolDefinition') -> Dict:
20
+ def provide(self, tool_definition: 'ToolDefinition') -> str:
21
+ """
22
+ Generates a formatted string containing basic and optionally an advanced usage example for the tool.
23
+ """
24
+ basic_example_dict = self._create_example_structure(tool_definition, mode='basic')
25
+ basic_example_str = "### Example 1: Basic Call (Required Arguments)\n"
26
+ basic_example_str += "```json\n"
27
+ basic_example_str += json.dumps(basic_example_dict, indent=2)
28
+ basic_example_str += "\n```"
29
+
30
+ if not self._schema_has_advanced_params(tool_definition.argument_schema):
31
+ return basic_example_str
32
+
33
+ advanced_example_dict = self._create_example_structure(tool_definition, mode='advanced')
34
+ advanced_example_str = "### Example 2: Advanced Call (With Optional Arguments)\n"
35
+ advanced_example_str += "```json\n"
36
+ advanced_example_str += json.dumps(advanced_example_dict, indent=2)
37
+ advanced_example_str += "\n```"
38
+
39
+ return f"{basic_example_str}\n\n{advanced_example_str}"
40
+
41
+ def _create_example_structure(self, tool_definition: 'ToolDefinition', mode: str) -> Dict:
42
+ """Helper to create a single Google tool call example for a given mode."""
16
43
  tool_name = tool_definition.name
17
44
  arg_schema = tool_definition.argument_schema
18
45
  arguments = {}
19
46
 
20
47
  if arg_schema and arg_schema.parameters:
21
- for param_def in arg_schema.parameters:
22
- if param_def.required or param_def.default_value is not None:
23
- arguments[param_def.name] = self._generate_placeholder_value(param_def)
24
-
48
+ params_to_render = arg_schema.parameters
49
+ if mode == 'basic':
50
+ params_to_render = [p for p in arg_schema.parameters if p.required]
51
+
52
+ for param_def in params_to_render:
53
+ # Use the intelligent placeholder generator from the default formatter
54
+ arguments[param_def.name] = DefaultJsonExampleFormatter._generate_example_from_schema(
55
+ param_def.object_schema or param_def.array_item_schema or param_def.param_type,
56
+ param_def.object_schema or arg_schema,
57
+ mode=mode
58
+ ) if param_def.object_schema or param_def.array_item_schema else self._generate_simple_placeholder(param_def)
59
+
25
60
  return {"name": tool_name, "args": arguments}
26
61
 
27
- def _generate_placeholder_value(self, param_def: ParameterDefinition) -> Any:
28
- # REUSE the intelligent generator for complex objects
29
- if param_def.param_type == ParameterType.OBJECT and param_def.object_schema:
30
- return DefaultJsonExampleFormatter._generate_example_from_schema(param_def.object_schema, param_def.object_schema)
62
+ def _schema_has_advanced_params(self, schema: Optional[ParameterSchema]) -> bool:
63
+ """Recursively checks if a schema or any of its sub-schemas have non-required parameters."""
64
+ if not schema: return False
65
+ for param in schema.parameters:
66
+ if not param.required: return True
67
+ if param.object_schema and self._schema_has_advanced_params(param.object_schema): return True
68
+ if isinstance(param.array_item_schema, ParameterSchema) and self._schema_has_advanced_params(param.array_item_schema): return True
69
+ return False
31
70
 
32
- # Fallback for primitives
71
+ def _generate_simple_placeholder(self, param_def: ParameterDefinition) -> Any:
72
+ """Generates a simple placeholder for primitive types."""
33
73
  if param_def.default_value is not None: return param_def.default_value
34
- if param_def.param_type == ParameterType.STRING: return f"example_{param_def.name}"
35
- if param_def.param_type == ParameterType.INTEGER: return 123
36
- if param_def.param_type == ParameterType.FLOAT: return 123.45
37
- if param_def.param_type == ParameterType.BOOLEAN: return True
38
- if param_def.param_type == ParameterType.ENUM: return param_def.enum_values[0] if param_def.enum_values else "enum_val"
39
- if param_def.param_type == ParameterType.OBJECT: return {"key": "value"}
40
- if param_def.param_type == ParameterType.ARRAY: return ["item1", "item2"]
41
- return "placeholder"
74
+ return DefaultJsonExampleFormatter._generate_example_from_schema(param_def.param_type, param_def.param_type, mode='basic')
@@ -1,8 +1,8 @@
1
1
  # file: autobyteus/autobyteus/tools/usage/formatters/openai_json_example_formatter.py
2
2
  import json
3
- from typing import Dict, Any, TYPE_CHECKING
3
+ from typing import Dict, Any, TYPE_CHECKING, Optional
4
4
 
5
- from autobyteus.tools.parameter_schema import ParameterType, ParameterDefinition
5
+ from autobyteus.tools.parameter_schema import ParameterSchema, ParameterDefinition
6
6
  from .base_formatter import BaseExampleFormatter
7
7
  from .default_json_example_formatter import DefaultJsonExampleFormatter # Import for reuse
8
8
 
@@ -11,41 +11,71 @@ if TYPE_CHECKING:
11
11
 
12
12
  class OpenAiJsonExampleFormatter(BaseExampleFormatter):
13
13
  """
14
- Formats a tool usage example into a format resembling an entry in the
15
- OpenAI JSON 'tool_calls' array, intended for prompting a model.
14
+ Formats a tool usage example into the OpenAI JSON 'tool_calls' format.
15
+ Provides both basic (required only) and advanced (all) examples if optional
16
+ parameters exist for the tool.
16
17
  """
18
+
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
17
31
 
18
- def provide(self, tool_definition: 'ToolDefinition') -> Dict:
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 OpenAI tool call example for a given mode."""
19
42
  tool_name = tool_definition.name
20
43
  arg_schema = tool_definition.argument_schema
21
44
  arguments = {}
22
45
 
23
46
  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
- arguments[param_def.name] = self._generate_placeholder_value(param_def)
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)
27
58
 
28
59
  function_call = {
29
60
  "function": {
30
61
  "name": tool_name,
31
- "arguments": json.dumps(arguments),
62
+ # FIX: Keep arguments as a dictionary for clear examples in the prompt.
63
+ # Do NOT stringify it here.
64
+ "arguments": arguments,
32
65
  },
33
66
  }
34
-
35
67
  return {"tool": function_call}
36
68
 
37
- def _generate_placeholder_value(self, param_def: ParameterDefinition) -> Any:
38
- # REUSE a more intelligent generator for complex objects
39
- if param_def.param_type == ParameterType.OBJECT and param_def.object_schema:
40
- return DefaultJsonExampleFormatter._generate_example_from_schema(param_def.object_schema, param_def.object_schema)
69
+ def _schema_has_advanced_params(self, schema: Optional[ParameterSchema]) -> bool:
70
+ """Recursively checks if a schema or any of its sub-schemas have non-required parameters."""
71
+ if not schema: return False
72
+ for param in schema.parameters:
73
+ if not param.required: return True
74
+ if param.object_schema and self._schema_has_advanced_params(param.object_schema): return True
75
+ if isinstance(param.array_item_schema, ParameterSchema) and self._schema_has_advanced_params(param.array_item_schema): return True
76
+ return False
41
77
 
42
- # Fallback for primitives
78
+ def _generate_simple_placeholder(self, param_def: ParameterDefinition) -> Any:
79
+ """Generates a simple placeholder for primitive types."""
43
80
  if param_def.default_value is not None: return param_def.default_value
44
- if param_def.param_type == ParameterType.STRING: return f"example_{param_def.name}"
45
- if param_def.param_type == ParameterType.INTEGER: return 123
46
- if param_def.param_type == ParameterType.FLOAT: return 123.45
47
- if param_def.param_type == ParameterType.BOOLEAN: return True
48
- if param_def.param_type == ParameterType.ENUM: return param_def.enum_values[0] if param_def.enum_values else "enum_val"
49
- if param_def.param_type == ParameterType.OBJECT: return {"key": "value"}
50
- if param_def.param_type == ParameterType.ARRAY: return ["item1", "item2"]
51
- return "placeholder"
81
+ return DefaultJsonExampleFormatter._generate_example_from_schema(param_def.param_type, param_def.param_type, mode='basic')