Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ require (
github.com/muesli/cancelreader v0.2.2
github.com/natefinch/atomic v1.0.1
github.com/openai/openai-go/v3 v3.61.0
github.com/pb33f/libopenapi v0.40.0
github.com/pb33f/libopenapi v0.41.5
github.com/rivo/uniseg v0.4.7
github.com/rumpl/harness v0.0.0-20261007225003-7683a7eab445
github.com/smacker/go-tree-sitter v0.0.0-20240827094217-dd81d9e9be82
Expand All @@ -84,7 +84,6 @@ require (
go.opentelemetry.io/otel/sdk/metric v1.47.0
go.opentelemetry.io/otel/trace v1.47.0
go.opentelemetry.io/proto/otlp v1.11.0
go.yaml.in/yaml/v4 v4.0.0-rc.6
golang.org/x/crypto v0.57.0
golang.org/x/image v0.46.0
golang.org/x/net v0.59.0
Expand Down Expand Up @@ -190,8 +189,9 @@ require (
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/pb33f/jsonpath v0.8.3 // indirect
github.com/pb33f/ordered-map/v2 v2.3.1 // indirect
github.com/pb33f/go-yaml v0.1.1 // indirect
github.com/pb33f/jsonpath v0.8.4 // indirect
github.com/pb33f/ordered-map/v2 v2.3.2 // indirect
github.com/pjbgf/sha1cd v0.6.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/segmentio/asm v1.1.3 // indirect
Expand Down Expand Up @@ -220,6 +220,7 @@ require (
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.47.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 // indirect
go.yaml.in/yaml/v3 v3.0.5 // indirect
go.yaml.in/yaml/v4 v4.0.0-rc.6 // indirect
golang.org/x/mod v0.41.0 // indirect
golang.org/x/text v0.42.0 // indirect
golang.org/x/time v0.15.0 // indirect
Expand Down
18 changes: 10 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -371,14 +371,16 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/pb33f/jsonpath v0.8.3 h1:gdOUYn31vic8iRSSR4I2SEv7UQf2WxLeDcUnDPpGZNE=
github.com/pb33f/jsonpath v0.8.3/go.mod h1:zBV5LJW4OQOPatmQE2QdKpGQJvhDTlE5IEj6ASaRNTo=
github.com/pb33f/libopenapi v0.40.0 h1:Hnzok6iUwo6PddYLc4YviTyBt0Q61TGeQf80KhiPdgg=
github.com/pb33f/libopenapi v0.40.0/go.mod h1:nISN8KLZBliHRwZY0V53JZJtuzI8P52WGfJ6J56z9Tg=
github.com/pb33f/ordered-map/v2 v2.3.1 h1:5319HDO0aw4DA4gzi+zv4FXU9UlSs3xGZ40wcP1nBjY=
github.com/pb33f/ordered-map/v2 v2.3.1/go.mod h1:qxFQgd0PkVUtOMCkTapqotNgzRhMPL7VvaHKbd1HnmQ=
github.com/pb33f/testify v0.1.0 h1:g48/HDU/jn2COspS4nM0scptxiKTJ4DnbX/4ehK6IZ8=
github.com/pb33f/testify v0.1.0/go.mod h1:nq283P/jJ8hXMmdhAqfj7BJIz0y+6IOHj9q0044rKt4=
github.com/pb33f/go-yaml v0.1.1 h1:yWGmhVdwzionRnSc/hEJwcfq6PaZL2QdYzyReXBbX7Q=
github.com/pb33f/go-yaml v0.1.1/go.mod h1:QvWdkHP5VjFYAeHMQUkaPQqREaQnBNOXmWzAjNrnSdo=
github.com/pb33f/jsonpath v0.8.4 h1:Yx7fxsKl8scgERL22WhKDs2c6gYNNUonPSWEG8Y194s=
github.com/pb33f/jsonpath v0.8.4/go.mod h1:eO6mgdhw5RgzCxm/bXTi75VUs0/cCYJzdGjMCCUEI6M=
github.com/pb33f/libopenapi v0.41.5 h1:kwEFkd/6W3tmTfsWQCVsMa0a9b1x3HYIZvt9Ek4jIDw=
github.com/pb33f/libopenapi v0.41.5/go.mod h1:mUldESjO6ownR1TCcywvoYp7e4v/bCdQyiiIqsgWne0=
github.com/pb33f/ordered-map/v2 v2.3.2 h1:wDyaZ2Pv9QLh64X4utCeD5Zoi9nIv2aW3lwv172O5PQ=
github.com/pb33f/ordered-map/v2 v2.3.2/go.mod h1:1OhFrXu3OYw3kM+FXF+Ug3ugmmZ9LE8z0JfQMLvtV0w=
github.com/pb33f/testify v0.1.1 h1:mnHe7uxKt8dyNYEGUspow72VjD264DB5rOJq4bz5tJw=
github.com/pb33f/testify v0.1.1/go.mod h1:keghMqOLECF1ENzESnfM+cwPcKN5uhTOpvbtjgL6aZg=
github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU=
github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down
17 changes: 9 additions & 8 deletions pkg/tools/builtin/openapi/openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
"github.com/pb33f/libopenapi"
"github.com/pb33f/libopenapi/datamodel/high/base"
v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
"go.yaml.in/yaml/v4"

"github.com/docker/docker-agent/pkg/config"
"github.com/docker/docker-agent/pkg/config/latest"
Expand Down Expand Up @@ -388,13 +387,13 @@ func schemaProxyToProperty(proxy *base.SchemaProxy) map[string]any {
enumValues := make([]any, 0, len(s.Enum))
for _, node := range s.Enum {
if node != nil {
enumValues = append(enumValues, yamlNodeToValue(node))
enumValues = append(enumValues, yamlNodeToValue(node, node.Value))
}
}
prop["enum"] = enumValues
}
if s.Default != nil {
prop["default"] = yamlNodeToValue(s.Default)
prop["default"] = yamlNodeToValue(s.Default, s.Default.Value)
}

return prop
Expand All @@ -410,13 +409,15 @@ func schemaType(s *base.Schema) string {
return "string"
}

// yamlNodeToValue converts a yaml.Node to a native Go value, preserving the
// original type (int, float, bool, null) instead of returning everything as a
// string.
func yamlNodeToValue(node *yaml.Node) any {
type nodeDecoder interface {
Decode(target any) error
}

// yamlNodeToValue preserves decoded types, falling back to the raw value on error.
func yamlNodeToValue(node nodeDecoder, fallback string) any {
var v any
if err := node.Decode(&v); err != nil {
return node.Value
return fallback
}
return v
}
Expand Down
85 changes: 85 additions & 0 deletions pkg/tools/builtin/openapi/openapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package openapi
import (
"context"
"encoding/json"
"errors"
"io"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -626,6 +627,90 @@ func TestOpenAPITool_EnumAndDefaultTypes(t *testing.T) {
assert.Equal(t, 25, limitProp["default"])
}

func TestOpenAPITool_EnumAndDefaultValueTypes(t *testing.T) {
t.Parallel()

for _, tt := range []struct {
name string
schema string
want any
}{
{"float", `{"type":"number","enum":[1.5],"default":1.5}`, 1.5},
{"boolean", `{"type":"boolean","enum":[false],"default":false}`, false},
{"null", `{"type":"null","enum":[null],"default":null}`, nil},
{"sequence", `{"type":"array","enum":[[1,true,null]],"default":[1,true,null]}`, []any{1, true, nil}},
{"mapping", `{"type":"object","enum":[{"enabled":true}],"default":{"enabled":true}}`, map[string]any{"enabled": true}},
} {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

spec := `{
"openapi": "3.1.0",
"info": {"title": "Test", "version": "1.0.0"},
"paths": {
"/items": {
"get": {
"operationId": "listItems",
"parameters": [{"name": "value", "in": "query", "schema": ` + tt.schema + `}],
"responses": {"200": {"description": "ok"}}
}
}
}
}`
specServer := serveSpec(t, spec)
toolsList, err := newOpenAPIToolForTest(specServer.URL+"/openapi.json", nil).Tools(t.Context())
require.NoError(t, err)
require.Len(t, toolsList, 1)

schema := toolsList[0].Parameters.(map[string]any)
prop := schema["properties"].(map[string]any)["value"].(map[string]any)
assert.Equal(t, []any{tt.want}, prop["enum"])
require.Contains(t, prop, "default")
assert.Equal(t, tt.want, prop["default"])
})
}
}

type stubNodeDecoder struct {
value any
err error
}

func (d stubNodeDecoder) Decode(target any) error {
*target.(*any) = d.value
return d.err
}

func TestYAMLNodeToValue(t *testing.T) {
t.Parallel()

for _, tt := range []struct {
name string
value any
}{
{"string", "active"},
{"integer", 25},
{"float", 1.5},
{"boolean", false},
{"null", nil},
{"sequence", []any{1, "two", nil}},
{"mapping", map[string]any{"enabled": true}},
} {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
assert.Equal(t, tt.value, yamlNodeToValue(stubNodeDecoder{value: tt.value}, "fallback"))
})
}
}

func TestYAMLNodeToValue_DecodeError(t *testing.T) {
t.Parallel()

decoder := stubNodeDecoder{value: "partial", err: errors.New("decode failed")}
assert.Equal(t, "raw value", yamlNodeToValue(decoder, "raw value"))
assert.Empty(t, yamlNodeToValue(decoder, ""))
}

type testEnvProvider map[string]string

func (p *testEnvProvider) Get(_ context.Context, name string) (string, bool) {
Expand Down
Loading