|
5 | 5 | /// - [ToolParam.integer] for integer parameters |
6 | 6 | /// - [ToolParam.number] for floating-point parameters |
7 | 7 | /// - [ToolParam.boolean] for boolean parameters |
| 8 | +/// - [ToolParam.nullType] for parameters whose only valid value is `null` |
8 | 9 | /// - [ToolParam.enumType] for enum parameters with allowed values |
9 | 10 | /// - [ToolParam.array] for array parameters |
10 | 11 | /// - [ToolParam.object] for nested object parameters |
@@ -52,6 +53,13 @@ sealed class ToolParam { |
52 | 53 | bool required = false, |
53 | 54 | }) => _BooleanParam(name: name, description: description, required: required); |
54 | 55 |
|
| 56 | + /// Creates a parameter whose only valid JSON value is `null`. |
| 57 | + static ToolParam nullType( |
| 58 | + String name, { |
| 59 | + String? description, |
| 60 | + bool required = false, |
| 61 | + }) => _NullParam(name: name, description: description, required: required); |
| 62 | + |
55 | 63 | /// Creates an enum parameter with a list of allowed values. |
56 | 64 | static ToolParam enumType( |
57 | 65 | String name, { |
@@ -95,6 +103,47 @@ sealed class ToolParam { |
95 | 103 | Map<String, dynamic> toJsonSchema(); |
96 | 104 | } |
97 | 105 |
|
| 106 | +/// Returns an actionable identity error for [parameters], or `null` when every |
| 107 | +/// object property name is non-empty and unique within its containing object. |
| 108 | +/// |
| 109 | +/// Array item names are not represented in JSON Schema and are therefore not |
| 110 | +/// validated, but object properties nested inside array items are. |
| 111 | +String? toolParamIdentityError( |
| 112 | + List<ToolParam> parameters, { |
| 113 | + required String path, |
| 114 | +}) { |
| 115 | + final names = <String>{}; |
| 116 | + for (final parameter in parameters) { |
| 117 | + if (parameter.name.isEmpty) { |
| 118 | + return 'Structured tool schemas require non-empty parameter names at ' |
| 119 | + '$path.'; |
| 120 | + } |
| 121 | + if (!names.add(parameter.name)) { |
| 122 | + return 'Structured tool schemas require unique parameter names at ' |
| 123 | + '$path; "${parameter.name}" is declared more than once.'; |
| 124 | + } |
| 125 | + } |
| 126 | + |
| 127 | + for (final parameter in parameters) { |
| 128 | + final nestedPath = '$path.${parameter.name}'; |
| 129 | + final error = _nestedToolParamIdentityError(parameter, nestedPath); |
| 130 | + if (error != null) { |
| 131 | + return error; |
| 132 | + } |
| 133 | + } |
| 134 | + return null; |
| 135 | +} |
| 136 | + |
| 137 | +String? _nestedToolParamIdentityError(ToolParam parameter, String path) { |
| 138 | + if (parameter is _ObjectParam) { |
| 139 | + return toolParamIdentityError(parameter.properties, path: path); |
| 140 | + } |
| 141 | + if (parameter is _ArrayParam) { |
| 142 | + return _nestedToolParamIdentityError(parameter.itemType, '$path[]'); |
| 143 | + } |
| 144 | + return null; |
| 145 | +} |
| 146 | + |
98 | 147 | final class _StringParam extends ToolParam { |
99 | 148 | const _StringParam({required super.name, super.description, super.required}) |
100 | 149 | : super._(); |
@@ -139,6 +188,17 @@ final class _BooleanParam extends ToolParam { |
139 | 188 | }; |
140 | 189 | } |
141 | 190 |
|
| 191 | +final class _NullParam extends ToolParam { |
| 192 | + const _NullParam({required super.name, super.description, super.required}) |
| 193 | + : super._(); |
| 194 | + |
| 195 | + @override |
| 196 | + Map<String, dynamic> toJsonSchema() => { |
| 197 | + 'type': 'null', |
| 198 | + if (description != null) 'description': description, |
| 199 | + }; |
| 200 | +} |
| 201 | + |
142 | 202 | final class _EnumParam extends ToolParam { |
143 | 203 | final List<String> values; |
144 | 204 |
|
|
0 commit comments