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
@@ -63,7 +63,7 @@ def _build_dynamic_image_schema(base_params: List[ParameterDefinition], model_en
63
63
  param_type=ParameterType.OBJECT,
64
64
  description=f"Model-specific generation parameters for the configured '{model_identifier}' model.",
65
65
  required=False,
66
- object_schema=config_schema.to_json_schema_dict()
66
+ object_schema=config_schema
67
67
  ))
68
68
  return schema
69
69
 
@@ -99,7 +99,7 @@ class GenerateImageTool(BaseTool):
99
99
  ]
100
100
  return _build_dynamic_image_schema(base_params, cls.MODEL_ENV_VAR, cls.DEFAULT_MODEL)
101
101
 
102
- async def _execute(self, context, prompt: str, generation_config: Optional[dict] = None) -> str:
102
+ async def _execute(self, context, prompt: str, generation_config: Optional[dict] = None) -> List[str]:
103
103
  model_identifier = _get_configured_model_identifier(self.MODEL_ENV_VAR, self.DEFAULT_MODEL)
104
104
  logger.info(f"GenerateImageTool executing with configured model '{model_identifier}'.")
105
105
  client = None
@@ -110,7 +110,7 @@ class GenerateImageTool(BaseTool):
110
110
  if not response.image_urls:
111
111
  raise ValueError("Image generation failed to return any image URLs.")
112
112
 
113
- return f"Image generation successful. URLs: {response.image_urls}"
113
+ return response.image_urls
114
114
  finally:
115
115
  if client:
116
116
  await client.cleanup()
@@ -160,7 +160,7 @@ class EditImageTool(BaseTool):
160
160
  ]
161
161
  return _build_dynamic_image_schema(base_params, cls.MODEL_ENV_VAR, cls.DEFAULT_MODEL)
162
162
 
163
- async def _execute(self, context, prompt: str, input_image_urls: str, generation_config: Optional[dict] = None, mask_image_url: Optional[str] = None) -> str:
163
+ async def _execute(self, context, prompt: str, input_image_urls: str, generation_config: Optional[dict] = None, mask_image_url: Optional[str] = None) -> List[str]:
164
164
  model_identifier = _get_configured_model_identifier(self.MODEL_ENV_VAR, self.DEFAULT_MODEL)
165
165
  logger.info(f"EditImageTool executing with configured model '{model_identifier}'.")
166
166
  client = None
@@ -180,7 +180,7 @@ class EditImageTool(BaseTool):
180
180
  if not response.image_urls:
181
181
  raise ValueError("Image editing failed to return any image URLs.")
182
182
 
183
- return f"Image editing successful. URLs: {response.image_urls}"
183
+ return response.image_urls
184
184
  finally:
185
185
  if client:
186
186
  await client.cleanup()
@@ -1,4 +1,5 @@
1
1
  # file: autobyteus/autobyteus/tools/parameter_schema.py
2
+ from __future__ import annotations
2
3
  import logging
3
4
  from typing import Dict, Any, List, Optional, Union, Type
4
5
  from dataclasses import dataclass, field
@@ -41,8 +42,9 @@ class ParameterDefinition:
41
42
  min_value: Optional[Union[int, float]] = None
42
43
  max_value: Optional[Union[int, float]] = None
43
44
  pattern: Optional[str] = None
44
- array_item_schema: Optional[Dict[str, Any]] = None
45
- object_schema: Optional[Dict[str, Any]] = None
45
+ # FIX: Allow dict for raw JSON schemas, in addition to ParameterType and ParameterSchema.
46
+ array_item_schema: Optional[Union[ParameterType, ParameterSchema, dict]] = None
47
+ object_schema: Optional[ParameterSchema] = None
46
48
 
47
49
  def __post_init__(self):
48
50
  if not self.name or not isinstance(self.name, str):
@@ -54,8 +56,15 @@ class ParameterDefinition:
54
56
  if self.param_type == ParameterType.ENUM and not self.enum_values:
55
57
  raise ValueError(f"ParameterDefinition '{self.name}' of type ENUM must specify enum_values")
56
58
 
59
+ # FIX: Update validation to allow dict for array_item_schema.
60
+ if self.array_item_schema is not None and not isinstance(self.array_item_schema, (ParameterType, ParameterSchema, dict)):
61
+ raise ValueError(f"ParameterDefinition '{self.name}': array_item_schema must be a ParameterType, ParameterSchema, or dict instance.")
62
+
63
+ if self.object_schema is not None and not isinstance(self.object_schema, ParameterSchema):
64
+ raise ValueError(f"ParameterDefinition '{self.name}': object_schema must be a ParameterSchema instance.")
65
+
57
66
  if self.param_type == ParameterType.ARRAY and self.array_item_schema is None:
58
- logger.debug(f"ParameterDefinition '{self.name}' of type ARRAY has no array_item_schema. Will be represented as a generic array.")
67
+ logger.debug(f"ParameterDefinition '{self.name}' of type ARRAY has no item schema. Will be a generic array of any type.")
59
68
 
60
69
  if self.param_type != ParameterType.ARRAY and self.array_item_schema is not None:
61
70
  raise ValueError(f"ParameterDefinition '{self.name}': array_item_schema should only be provided if param_type is ARRAY.")
@@ -71,44 +80,30 @@ class ParameterDefinition:
71
80
  return not self.required
72
81
 
73
82
  if self.param_type == ParameterType.STRING:
74
- if not isinstance(value, str):
75
- return False
76
- if self.pattern:
77
- if not re.match(self.pattern, value):
78
- return False
83
+ if not isinstance(value, str): return False
84
+ if self.pattern and not re.match(self.pattern, value): return False
79
85
 
80
86
  elif self.param_type == ParameterType.INTEGER:
81
- if not isinstance(value, int):
82
- if isinstance(value, bool): return False
83
- return False
84
- if self.min_value is not None and value < self.min_value:
85
- return False
86
- if self.max_value is not None and value > self.max_value:
87
- return False
87
+ if not isinstance(value, int) or isinstance(value, bool): return False
88
+ if self.min_value is not None and value < self.min_value: return False
89
+ if self.max_value is not None and value > self.max_value: return False
88
90
 
89
91
  elif self.param_type == ParameterType.FLOAT:
90
- if not isinstance(value, (float, int)):
91
- return False
92
- if self.min_value is not None and float(value) < self.min_value:
93
- return False
94
- if self.max_value is not None and float(value) > self.max_value:
95
- return False
92
+ if not isinstance(value, (float, int)): return False
93
+ if self.min_value is not None and float(value) < self.min_value: return False
94
+ if self.max_value is not None and float(value) > self.max_value: return False
96
95
 
97
96
  elif self.param_type == ParameterType.BOOLEAN:
98
- if not isinstance(value, bool):
99
- return False
97
+ if not isinstance(value, bool): return False
100
98
 
101
99
  elif self.param_type == ParameterType.ENUM:
102
- if not isinstance(value, str) or value not in (self.enum_values or []):
103
- return False
100
+ if not isinstance(value, str) or value not in (self.enum_values or []): return False
104
101
 
105
102
  elif self.param_type == ParameterType.OBJECT:
106
- if not isinstance(value, dict):
107
- return False
103
+ if not isinstance(value, dict): return False
108
104
 
109
105
  elif self.param_type == ParameterType.ARRAY:
110
- if not isinstance(value, list):
111
- return False
106
+ if not isinstance(value, list): return False
112
107
 
113
108
  return True
114
109
 
@@ -124,17 +119,22 @@ class ParameterDefinition:
124
119
  "max_value": self.max_value,
125
120
  "pattern": self.pattern,
126
121
  }
122
+ # FIX: Correctly serialize dicts for array_item_schema.
127
123
  if self.param_type == ParameterType.ARRAY and self.array_item_schema is not None:
128
- data["array_item_schema"] = self.array_item_schema
124
+ if isinstance(self.array_item_schema, ParameterSchema):
125
+ data["array_item_schema"] = self.array_item_schema.to_dict()
126
+ elif isinstance(self.array_item_schema, dict):
127
+ data["array_item_schema"] = self.array_item_schema
128
+ elif isinstance(self.array_item_schema, ParameterType):
129
+ data["array_item_schema"] = {"type": self.array_item_schema.value}
130
+
129
131
  if self.param_type == ParameterType.OBJECT and self.object_schema is not None:
130
- data["object_schema"] = self.object_schema
132
+ data["object_schema"] = self.object_schema.to_dict()
131
133
  return data
132
134
 
133
135
  def to_json_schema_property_dict(self) -> Dict[str, Any]:
134
136
  if self.param_type == ParameterType.OBJECT and self.object_schema:
135
- # If a detailed object schema is provided, use it directly.
136
- # We add the description at the top level for clarity.
137
- schema = self.object_schema.copy()
137
+ schema = self.object_schema.to_json_schema_dict()
138
138
  schema["description"] = self.description
139
139
  return schema
140
140
 
@@ -148,40 +148,37 @@ class ParameterDefinition:
148
148
  if self.param_type == ParameterType.ENUM and self.enum_values:
149
149
  prop_dict["enum"] = self.enum_values
150
150
 
151
- if self.min_value is not None:
152
- if self.param_type in [ParameterType.INTEGER, ParameterType.FLOAT]:
153
- prop_dict["minimum"] = self.min_value
151
+ if self.min_value is not None and self.param_type in [ParameterType.INTEGER, ParameterType.FLOAT]:
152
+ prop_dict["minimum"] = self.min_value
154
153
 
155
- if self.max_value is not None:
156
- if self.param_type in [ParameterType.INTEGER, ParameterType.FLOAT]:
157
- prop_dict["maximum"] = self.max_value
154
+ if self.max_value is not None and self.param_type in [ParameterType.INTEGER, ParameterType.FLOAT]:
155
+ prop_dict["maximum"] = self.max_value
158
156
 
159
157
  if self.pattern and self.param_type == ParameterType.STRING:
160
158
  prop_dict["pattern"] = self.pattern
161
159
 
160
+ # FIX: Correctly handle dicts when generating the 'items' part of an array schema.
162
161
  if self.param_type == ParameterType.ARRAY:
163
- if self.array_item_schema is not None:
162
+ if isinstance(self.array_item_schema, ParameterSchema):
163
+ prop_dict["items"] = self.array_item_schema.to_json_schema_dict()
164
+ elif isinstance(self.array_item_schema, dict):
164
165
  prop_dict["items"] = self.array_item_schema
166
+ elif isinstance(self.array_item_schema, ParameterType):
167
+ prop_dict["items"] = {"type": self.array_item_schema.to_json_schema_type()}
165
168
  else:
166
169
  prop_dict["items"] = True
167
- logger.debug(f"Parameter '{self.name}' is ARRAY type with no item schema; JSON schema 'items' will be generic.")
168
170
 
169
171
  return prop_dict
170
172
 
171
173
  @dataclass
172
174
  class ParameterSchema:
173
- """
174
- Describes a schema for a set of parameters, either for tool arguments or instantiation configuration.
175
- """
176
175
  parameters: List[ParameterDefinition] = field(default_factory=list)
177
176
 
178
177
  def add_parameter(self, parameter: ParameterDefinition) -> None:
179
178
  if not isinstance(parameter, ParameterDefinition):
180
179
  raise TypeError("parameter must be a ParameterDefinition instance")
181
-
182
180
  if any(p.name == parameter.name for p in self.parameters):
183
181
  raise ValueError(f"Parameter '{parameter.name}' already exists in schema")
184
-
185
182
  self.parameters.append(parameter)
186
183
 
187
184
  def get_parameter(self, name: str) -> Optional[ParameterDefinition]:
@@ -189,67 +186,64 @@ class ParameterSchema:
189
186
 
190
187
  def validate_config(self, config_data: Dict[str, Any]) -> tuple[bool, List[str]]:
191
188
  errors = []
192
-
193
189
  for param_def in self.parameters:
194
190
  if param_def.required and param_def.name not in config_data:
195
191
  errors.append(f"Required parameter '{param_def.name}' is missing.")
196
-
197
192
  for key, value in config_data.items():
198
193
  param_def = self.get_parameter(key)
199
194
  if not param_def:
200
- logger.debug(f"Unknown parameter '{key}' provided. It will be ignored by schema-based processing but passed through if possible.")
195
+ logger.debug(f"Unknown parameter '{key}' provided. It will be ignored.")
201
196
  continue
202
-
203
197
  if not param_def.validate_value(value):
204
198
  errors.append(f"Invalid value for parameter '{param_def.name}': '{str(value)[:50]}...'. Expected type compatible with {param_def.param_type.value}.")
205
-
206
199
  return len(errors) == 0, errors
207
200
 
208
- def get_defaults(self) -> Dict[str, Any]:
209
- return {
210
- param.name: param.default_value
211
- for param in self.parameters
212
- if param.default_value is not None
213
- }
214
-
215
201
  def to_dict(self) -> Dict[str, Any]:
216
- return {
217
- "parameters": [param.to_dict() for param in self.parameters]
218
- }
202
+ return {"parameters": [param.to_dict() for param in self.parameters]}
219
203
 
220
204
  def to_json_schema_dict(self) -> Dict[str, Any]:
221
205
  if not self.parameters:
222
- return {
223
- "type": "object",
224
- "properties": {},
225
- "required": []
226
- }
227
-
228
- properties = {
229
- param.name: param.to_json_schema_property_dict()
230
- for param in self.parameters
231
- }
232
- required = [
233
- param.name for param in self.parameters if param.required
234
- ]
235
-
236
- return {
237
- "type": "object",
238
- "properties": properties,
239
- "required": required,
240
- }
206
+ return {"type": "object", "properties": {}, "required": []}
207
+ properties = {p.name: p.to_json_schema_property_dict() for p in self.parameters}
208
+ required = [p.name for p in self.parameters if p.required]
209
+ return {"type": "object", "properties": properties, "required": required}
241
210
 
242
211
  @classmethod
243
212
  def from_dict(cls, schema_data: Dict[str, Any]) -> 'ParameterSchema':
244
213
  schema = cls()
245
-
246
214
  for param_data in schema_data.get("parameters", []):
247
- param_type_value = param_data["type"]
248
215
  try:
249
- param_type_enum = ParameterType(param_type_value)
216
+ param_type_enum = ParameterType(param_data["type"])
250
217
  except ValueError:
251
- raise ValueError(f"Invalid parameter type string '{param_type_value}' in schema data for parameter '{param_data.get('name')}'.")
218
+ raise ValueError(f"Invalid parameter type string '{param_data['type']}' for param '{param_data.get('name')}'.")
219
+
220
+ array_schema_obj = None
221
+ if "array_item_schema" in param_data and param_data["array_item_schema"] is not None:
222
+ item_schema_data = param_data["array_item_schema"]
223
+
224
+ # FIX: Add robust logic to deserialize array_item_schema correctly.
225
+ if isinstance(item_schema_data, dict):
226
+ if "parameters" in item_schema_data:
227
+ # It's our internal ParameterSchema format.
228
+ array_schema_obj = ParameterSchema.from_dict(item_schema_data)
229
+ elif "type" in item_schema_data and len(item_schema_data) == 1:
230
+ # Heuristic: it's a simple primitive type like {'type': 'string'}.
231
+ try:
232
+ array_schema_obj = ParameterType(item_schema_data["type"])
233
+ except ValueError:
234
+ # Not a valid ParameterType, so treat it as a raw schema dict.
235
+ array_schema_obj = item_schema_data
236
+ else:
237
+ # It's a complex JSON schema dict, store it as is.
238
+ array_schema_obj = item_schema_data
239
+ else:
240
+ # Should not be hit if serialized with to_dict, but handle for robustness.
241
+ array_schema_obj = item_schema_data
252
242
 
243
+ object_schema_obj = None
244
+ if "object_schema" in param_data and param_data["object_schema"] is not None:
245
+ object_schema_obj = ParameterSchema.from_dict(param_data["object_schema"])
246
+
253
247
  param = ParameterDefinition(
254
248
  name=param_data["name"],
255
249
  param_type=param_type_enum,
@@ -260,15 +254,14 @@ class ParameterSchema:
260
254
  min_value=param_data.get("min_value"),
261
255
  max_value=param_data.get("max_value"),
262
256
  pattern=param_data.get("pattern"),
263
- array_item_schema=param_data.get("array_item_schema"),
264
- object_schema=param_data.get("object_schema")
257
+ array_item_schema=array_schema_obj,
258
+ object_schema=object_schema_obj
265
259
  )
266
260
  schema.add_parameter(param)
267
-
268
261
  return schema
269
262
 
270
263
  def __len__(self) -> int:
271
264
  return len(self.parameters)
272
265
 
273
266
  def __bool__(self) -> bool:
274
- return len(self.parameters) > 0
267
+ return bool(self.parameters)
@@ -0,0 +1,81 @@
1
+ # file: autobyteus/autobyteus/tools/pydantic_to_parameter_schema.py
2
+ import logging
3
+ from typing import Type, get_origin, get_args, Union, List, Dict
4
+ from pydantic import BaseModel
5
+ from autobyteus.tools.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+ _PYDANTIC_TYPE_MAP = {
10
+ str: ParameterType.STRING,
11
+ int: ParameterType.INTEGER,
12
+ float: ParameterType.FLOAT,
13
+ bool: ParameterType.BOOLEAN,
14
+ dict: ParameterType.OBJECT,
15
+ list: ParameterType.ARRAY,
16
+ }
17
+
18
+ def pydantic_to_parameter_schema(pydantic_model: Type[BaseModel]) -> ParameterSchema:
19
+ """
20
+ Converts a Pydantic BaseModel into an AutoByteUs ParameterSchema.
21
+
22
+ This function inspects the Pydantic model's fields and recursively builds a
23
+ corresponding ParameterSchema, handling nested models and lists of models.
24
+
25
+ Args:
26
+ pydantic_model: The Pydantic model class to convert.
27
+
28
+ Returns:
29
+ A fully constructed ParameterSchema object.
30
+ """
31
+ schema = ParameterSchema()
32
+ required_fields = {name for name, field_info in pydantic_model.model_fields.items() if field_info.is_required()}
33
+
34
+ for field_name, field_info in pydantic_model.model_fields.items():
35
+ param_type = ParameterType.STRING # Default
36
+ object_schema = None
37
+ array_item_schema = None
38
+
39
+ # Determine if the type is Optional (e.g., Union[str, None])
40
+ is_optional = False
41
+ field_type = field_info.annotation
42
+ origin_type = get_origin(field_type)
43
+
44
+ if origin_type is Union:
45
+ union_args = get_args(field_type)
46
+ if type(None) in union_args:
47
+ is_optional = True
48
+ # Get the non-None type from the Union
49
+ non_none_args = [arg for arg in union_args if arg is not type(None)]
50
+ if len(non_none_args) == 1:
51
+ field_type = non_none_args[0]
52
+ origin_type = get_origin(field_type)
53
+
54
+ if origin_type is list or origin_type is List:
55
+ param_type = ParameterType.ARRAY
56
+ list_item_type = get_args(field_type)[0] if get_args(field_type) else any
57
+ if isinstance(list_item_type, type) and issubclass(list_item_type, BaseModel):
58
+ # FIX: Recursively call the converter for the nested Pydantic model.
59
+ array_item_schema = pydantic_to_parameter_schema(list_item_type)
60
+ else:
61
+ # Fallback for list of primitives
62
+ primitive_type_str = _PYDANTIC_TYPE_MAP.get(list_item_type, ParameterType.STRING).value
63
+ array_item_schema = {"type": primitive_type_str}
64
+
65
+ elif isinstance(field_type, type) and issubclass(field_type, BaseModel):
66
+ param_type = ParameterType.OBJECT
67
+ # Recursively convert the nested Pydantic model
68
+ object_schema = pydantic_to_parameter_schema(field_type)
69
+ else:
70
+ param_type = _PYDANTIC_TYPE_MAP.get(field_type, ParameterType.STRING)
71
+
72
+ schema.add_parameter(ParameterDefinition(
73
+ name=field_name,
74
+ param_type=param_type,
75
+ description=field_info.description or f"Parameter '{field_name}'.",
76
+ required=field_name in required_fields and not is_optional,
77
+ object_schema=object_schema,
78
+ array_item_schema=array_item_schema
79
+ ))
80
+
81
+ return schema
@@ -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