@@ -7,7 +7,11 @@ import { TabsContent } from "@/components/ui/tabs";
7
7
import { Textarea } from "@/components/ui/textarea" ;
8
8
import DynamicJsonForm from "./DynamicJsonForm" ;
9
9
import type { JsonValue , JsonSchemaType } from "@/utils/jsonUtils" ;
10
- import { generateDefaultValue , validateToolOutput , hasOutputSchema } from "@/utils/schemaUtils" ;
10
+ import {
11
+ generateDefaultValue ,
12
+ validateToolOutput ,
13
+ hasOutputSchema ,
14
+ } from "@/utils/schemaUtils" ;
11
15
import {
12
16
CallToolResultSchema ,
13
17
CompatibilityCallToolResult ,
@@ -56,46 +60,54 @@ const ToolsTab = ({
56
60
// Check compatibility between structured and unstructured content
57
61
const checkContentCompatibility = (
58
62
structuredContent : unknown ,
59
- unstructuredContent : Array < { type : string ; text ?: string ; [ key : string ] : unknown } >
63
+ unstructuredContent : Array < {
64
+ type : string ;
65
+ text ?: string ;
66
+ [ key : string ] : unknown ;
67
+ } > ,
60
68
) : { isCompatible : boolean ; message : string } => {
61
69
// Check if unstructured content is a single text block
62
- if ( unstructuredContent . length !== 1 || unstructuredContent [ 0 ] . type !== "text" ) {
70
+ if (
71
+ unstructuredContent . length !== 1 ||
72
+ unstructuredContent [ 0 ] . type !== "text"
73
+ ) {
63
74
return {
64
75
isCompatible : false ,
65
- message : "Unstructured content is not a single text block"
76
+ message : "Unstructured content is not a single text block" ,
66
77
} ;
67
78
}
68
79
69
80
const textContent = unstructuredContent [ 0 ] . text ;
70
81
if ( ! textContent ) {
71
82
return {
72
83
isCompatible : false ,
73
- message : "Text content is empty"
84
+ message : "Text content is empty" ,
74
85
} ;
75
86
}
76
87
77
88
try {
78
89
// Try to parse the text as JSON
79
90
const parsedContent = JSON . parse ( textContent ) ;
80
-
91
+
81
92
// Deep equality check
82
- const isEqual = JSON . stringify ( parsedContent ) === JSON . stringify ( structuredContent ) ;
83
-
93
+ const isEqual =
94
+ JSON . stringify ( parsedContent ) === JSON . stringify ( structuredContent ) ;
95
+
84
96
if ( isEqual ) {
85
97
return {
86
98
isCompatible : true ,
87
- message : "Unstructured content matches structured content"
99
+ message : "Unstructured content matches structured content" ,
88
100
} ;
89
101
} else {
90
102
return {
91
103
isCompatible : false ,
92
- message : "Parsed JSON does not match structured content"
104
+ message : "Parsed JSON does not match structured content" ,
93
105
} ;
94
106
}
95
107
} catch ( e ) {
96
108
return {
97
109
isCompatible : false ,
98
- message : "Unstructured content is not valid JSON"
110
+ message : "Unstructured content is not valid JSON" ,
99
111
} ;
100
112
}
101
113
} ;
@@ -122,31 +134,38 @@ const ToolsTab = ({
122
134
123
135
// Validate structured content if present and tool has output schema
124
136
let validationResult = null ;
125
- const toolHasOutputSchema = selectedTool && hasOutputSchema ( selectedTool . name ) ;
126
-
137
+ const toolHasOutputSchema =
138
+ selectedTool && hasOutputSchema ( selectedTool . name ) ;
139
+
127
140
if ( toolHasOutputSchema ) {
128
141
if ( ! structuredResult . structuredContent && ! isError ) {
129
142
// Tool has output schema but didn't return structured content (and it's not an error)
130
143
validationResult = {
131
144
isValid : false ,
132
- error : "Tool has an output schema but did not return structured content"
145
+ error :
146
+ "Tool has an output schema but did not return structured content" ,
133
147
} ;
134
148
} else if ( structuredResult . structuredContent ) {
135
149
// Validate the structured content
136
- validationResult = validateToolOutput ( selectedTool . name , structuredResult . structuredContent ) ;
150
+ validationResult = validateToolOutput (
151
+ selectedTool . name ,
152
+ structuredResult . structuredContent ,
153
+ ) ;
137
154
}
138
155
}
139
156
140
157
// Check compatibility if both structured and unstructured content exist
141
158
// AND the tool has an output schema
142
159
let compatibilityResult = null ;
143
- if ( structuredResult . structuredContent &&
144
- structuredResult . content . length > 0 &&
145
- selectedTool &&
146
- hasOutputSchema ( selectedTool . name ) ) {
160
+ if (
161
+ structuredResult . structuredContent &&
162
+ structuredResult . content . length > 0 &&
163
+ selectedTool &&
164
+ hasOutputSchema ( selectedTool . name )
165
+ ) {
147
166
compatibilityResult = checkContentCompatibility (
148
167
structuredResult . structuredContent ,
149
- structuredResult . content
168
+ structuredResult . content ,
150
169
) ;
151
170
}
152
171
@@ -162,46 +181,55 @@ const ToolsTab = ({
162
181
</ h4 >
163
182
{ structuredResult . structuredContent && (
164
183
< div className = "mb-4" >
165
- < h5 className = "font-semibold mb-2 text-sm" > Structured Content:</ h5 >
184
+ < h5 className = "font-semibold mb-2 text-sm" >
185
+ Structured Content:
186
+ </ h5 >
166
187
< div className = "bg-gray-50 dark:bg-gray-900 p-3 rounded-lg" >
167
188
< JsonView data = { structuredResult . structuredContent } />
168
189
{ validationResult && (
169
- < div className = { `mt-2 p-2 rounded text-sm ${
170
- validationResult . isValid
171
- ? "bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200"
172
- : "bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200"
173
- } `} >
190
+ < div
191
+ className = { `mt-2 p-2 rounded text-sm ${
192
+ validationResult . isValid
193
+ ? "bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200"
194
+ : "bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200"
195
+ } `}
196
+ >
174
197
{ validationResult . isValid ? (
175
198
"✓ Valid according to output schema"
176
199
) : (
177
- < >
178
- ✗ Validation Error: { validationResult . error }
179
- </ >
200
+ < > ✗ Validation Error: { validationResult . error } </ >
180
201
) }
181
202
</ div >
182
203
) }
183
204
</ div >
184
205
</ div >
185
206
) }
186
- { ! structuredResult . structuredContent && validationResult && ! validationResult . isValid && (
187
- < div className = "mb-4" >
188
- < div className = "bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200 p-2 rounded text-sm" >
189
- ✗ Validation Error: { validationResult . error }
207
+ { ! structuredResult . structuredContent &&
208
+ validationResult &&
209
+ ! validationResult . isValid && (
210
+ < div className = "mb-4" >
211
+ < div className = "bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200 p-2 rounded text-sm" >
212
+ ✗ Validation Error: { validationResult . error }
213
+ </ div >
190
214
</ div >
191
- </ div >
192
- ) }
215
+ ) }
193
216
{ structuredResult . content . length > 0 && (
194
217
< div className = "mb-4" >
195
218
{ structuredResult . structuredContent && (
196
219
< >
197
- < h5 className = "font-semibold mb-2 text-sm" > Unstructured Content:</ h5 >
220
+ < h5 className = "font-semibold mb-2 text-sm" >
221
+ Unstructured Content:
222
+ </ h5 >
198
223
{ compatibilityResult && (
199
- < div className = { `mb-2 p-2 rounded text-sm ${
200
- compatibilityResult . isCompatible
201
- ? "bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200"
202
- : "bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200"
203
- } `} >
204
- { compatibilityResult . isCompatible ? "✓" : "⚠" } { compatibilityResult . message }
224
+ < div
225
+ className = { `mb-2 p-2 rounded text-sm ${
226
+ compatibilityResult . isCompatible
227
+ ? "bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200"
228
+ : "bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200"
229
+ } `}
230
+ >
231
+ { compatibilityResult . isCompatible ? "✓" : "⚠" } { " " }
232
+ { compatibilityResult . message }
205
233
</ div >
206
234
) }
207
235
</ >
@@ -211,25 +239,25 @@ const ToolsTab = ({
211
239
{ item . type === "text" && (
212
240
< JsonView data = { item . text } isError = { isError } />
213
241
) }
214
- { item . type === "image" && (
215
- < img
216
- src = { `data:${ item . mimeType } ;base64,${ item . data } ` }
217
- alt = "Tool result image"
218
- className = "max-w-full h-auto"
219
- />
220
- ) }
221
- { item . type === "resource" &&
222
- ( item . resource ?. mimeType ?. startsWith ( "audio/" ) ? (
223
- < audio
224
- controls
225
- src = { `data:${ item . resource . mimeType } ;base64,${ item . resource . blob } ` }
226
- className = "w-full"
227
- >
228
- < p > Your browser does not support audio playback</ p >
229
- </ audio >
230
- ) : (
231
- < JsonView data = { item . resource } />
232
- ) ) }
242
+ { item . type === "image" && (
243
+ < img
244
+ src = { `data:${ item . mimeType } ;base64,${ item . data } ` }
245
+ alt = "Tool result image"
246
+ className = "max-w-full h-auto"
247
+ />
248
+ ) }
249
+ { item . type === "resource" &&
250
+ ( item . resource ?. mimeType ?. startsWith ( "audio/" ) ? (
251
+ < audio
252
+ controls
253
+ src = { `data:${ item . resource . mimeType } ;base64,${ item . resource . blob } ` }
254
+ className = "w-full"
255
+ >
256
+ < p > Your browser does not support audio playback</ p >
257
+ </ audio >
258
+ ) : (
259
+ < JsonView data = { item . resource } />
260
+ ) ) }
233
261
</ div >
234
262
) ) }
235
263
</ div >
@@ -395,7 +423,9 @@ const ToolsTab = ({
395
423
< Button
396
424
size = "sm"
397
425
variant = "ghost"
398
- onClick = { ( ) => setIsOutputSchemaExpanded ( ! isOutputSchemaExpanded ) }
426
+ onClick = { ( ) =>
427
+ setIsOutputSchemaExpanded ( ! isOutputSchemaExpanded )
428
+ }
399
429
className = "h-6 px-2"
400
430
>
401
431
{ isOutputSchemaExpanded ? (
@@ -411,9 +441,13 @@ const ToolsTab = ({
411
441
) }
412
442
</ Button >
413
443
</ div >
414
- < div className = { `transition-all ${
415
- isOutputSchemaExpanded ? "" : "max-h-[8rem] overflow-y-auto"
416
- } `} >
444
+ < div
445
+ className = { `transition-all ${
446
+ isOutputSchemaExpanded
447
+ ? ""
448
+ : "max-h-[8rem] overflow-y-auto"
449
+ } `}
450
+ >
417
451
< JsonView data = { selectedTool . outputSchema } />
418
452
</ div >
419
453
</ div >
0 commit comments