@@ -316,3 +316,110 @@ def test_openapi_converter_schema_level_examples_normalized():
316316 assert "example" not in body_param .model_dump (by_alias = True )
317317
318318 assert tool .outputs .examples == [{"id" : "w_1" }]
319+
320+
321+ def test_openapi_converter_array_form_schema_examples ():
322+ """Array-form (JSON Schema / OpenAPI 3.1) schema 'examples' are preserved, not dropped."""
323+ openapi_spec = {
324+ "openapi" : "3.1.0" ,
325+ "info" : {"title" : "Test API" , "version" : "1.0.0" },
326+ "paths" : {
327+ "/gadgets" : {
328+ "post" : {
329+ "operationId" : "createGadget" ,
330+ "requestBody" : {
331+ "content" : {
332+ "application/json" : {
333+ "schema" : {
334+ "type" : "object" ,
335+ "properties" : {"name" : {"type" : "string" }},
336+ # JSON Schema 'examples' keyword: a list of values
337+ "examples" : [{"name" : "Gadget A" }, {"name" : "Gadget B" }],
338+ }
339+ }
340+ }
341+ },
342+ "responses" : {
343+ "200" : {
344+ "description" : "ok" ,
345+ "content" : {
346+ "application/json" : {
347+ "schema" : {
348+ "type" : "string" ,
349+ "examples" : ["ok" , "done" ],
350+ }
351+ }
352+ },
353+ }
354+ },
355+ }
356+ }
357+ },
358+ }
359+
360+ converter = OpenApiConverter (openapi_spec )
361+ manual = converter .convert ()
362+
363+ tool = next ((t for t in manual .tools if t .name == "createGadget" ), None )
364+ assert tool is not None
365+
366+ body_param = tool .inputs .properties .get ("body" )
367+ assert body_param is not None
368+ assert body_param .examples == [{"name" : "Gadget A" }, {"name" : "Gadget B" }]
369+ # examples surface in the normalized field on serialization
370+ assert body_param .model_dump (by_alias = True ).get ("examples" ) == [{"name" : "Gadget A" }, {"name" : "Gadget B" }]
371+
372+ assert tool .outputs .examples == ["ok" , "done" ]
373+
374+
375+ def test_openapi_converter_example_dedup_is_type_aware_and_order_insensitive ():
376+ """De-dup keeps distinct JSON types (true vs 1) and collapses key-reordered objects."""
377+ openapi_spec = {
378+ "openapi" : "3.0.0" ,
379+ "info" : {"title" : "Test API" , "version" : "1.0.0" },
380+ "paths" : {
381+ "/items" : {
382+ "post" : {
383+ "operationId" : "createItem" ,
384+ "requestBody" : {
385+ "content" : {
386+ "application/json" : {
387+ # media-type example with one key order...
388+ "examples" : {"e1" : {"value" : {"a" : 1 , "b" : 2 }}},
389+ "schema" : {
390+ "type" : "object" ,
391+ # ...schema example with the other key order -> collapses to one
392+ "examples" : [{"b" : 2 , "a" : 1 }],
393+ },
394+ }
395+ }
396+ },
397+ "responses" : {
398+ "200" : {
399+ "description" : "ok" ,
400+ "content" : {
401+ "application/json" : {
402+ "schema" : {
403+ "type" : "object" ,
404+ # true and 1 are distinct; duplicate true removed
405+ "examples" : [True , 1 , True ],
406+ }
407+ }
408+ },
409+ }
410+ },
411+ }
412+ }
413+ },
414+ }
415+
416+ converter = OpenApiConverter (openapi_spec )
417+ manual = converter .convert ()
418+
419+ tool = next ((t for t in manual .tools if t .name == "createItem" ), None )
420+ assert tool is not None
421+
422+ # {a:1,b:2} and {b:2,a:1} are the same example -> collapsed to one
423+ assert tool .inputs .properties .get ("body" ).examples == [{"a" : 1 , "b" : 2 }]
424+ # True vs 1 kept distinct (== would have collapsed them); duplicate True removed
425+ assert tool .outputs .examples == [True , 1 ]
0 commit comments