Preprocess and convert function arguments before invocation. Currently handles: - Converting JSON dictionaries to Pydantic model instances where expected Future extensions could include: - Type coercion for other complex types - Validation and sanitization - Custom conversi
(self, args: dict[str, Any])
| 104 | return function_decl |
| 105 | |
| 106 | def _preprocess_args(self, args: dict[str, Any]) -> dict[str, Any]: |
| 107 | """Preprocess and convert function arguments before invocation. |
| 108 | |
| 109 | Currently handles: |
| 110 | - Converting JSON dictionaries to Pydantic model instances where expected |
| 111 | |
| 112 | Future extensions could include: |
| 113 | - Type coercion for other complex types |
| 114 | - Validation and sanitization |
| 115 | - Custom conversion logic |
| 116 | |
| 117 | Args: |
| 118 | args: Raw arguments from the LLM tool call |
| 119 | |
| 120 | Returns: |
| 121 | Processed arguments ready for function invocation |
| 122 | """ |
| 123 | signature = inspect.signature(self.func) |
| 124 | converted_args = args.copy() |
| 125 | try: |
| 126 | type_hints = get_type_hints(self.func) |
| 127 | except (TypeError, NameError): |
| 128 | # NameError: unresolved forward refs (e.g. recursive type aliases). |
| 129 | # TypeError: non-function callables. |
| 130 | if hasattr(self.func, '__call__'): |
| 131 | try: |
| 132 | type_hints = get_type_hints(self.func.__call__) |
| 133 | except (TypeError, NameError): |
| 134 | type_hints = {} |
| 135 | else: |
| 136 | type_hints = {} |
| 137 | |
| 138 | for param_name, param in signature.parameters.items(): |
| 139 | if param_name in args: |
| 140 | target_type = type_hints.get(param_name, param.annotation) |
| 141 | if target_type != inspect.Parameter.empty: |
| 142 | |
| 143 | # Handle Optional[PydanticModel] types |
| 144 | if get_origin(param.annotation) is Union: |
| 145 | union_args = get_args(param.annotation) |
| 146 | # Find the non-None type in Optional[T] (which is Union[T, None]) |
| 147 | non_none_types = [ |
| 148 | arg for arg in union_args if arg is not type(None) |
| 149 | ] |
| 150 | if len(non_none_types) == 1: |
| 151 | target_type = non_none_types[0] |
| 152 | elif len(non_none_types) > 1 and all( |
| 153 | inspect.isclass(t) and issubclass(t, pydantic.BaseModel) |
| 154 | for t in non_none_types |
| 155 | ): |
| 156 | if args[param_name] is None or isinstance( |
| 157 | args[param_name], tuple(non_none_types) |
| 158 | ): |
| 159 | continue |
| 160 | try: |
| 161 | converted_args[param_name] = pydantic.TypeAdapter( |
| 162 | param.annotation |
| 163 | ).validate_python(args[param_name]) |