diff --git a/docs/en/resources/tools/_index.md b/docs/en/resources/tools/_index.md index 7ef4e4f70d..2c54fe3cb1 100644 --- a/docs/en/resources/tools/_index.md +++ b/docs/en/resources/tools/_index.md @@ -79,11 +79,12 @@ the parameter. description: Airline unique 2 letter identifier ``` -| **field** | **type** | **required** | **description** | -|-------------|:--------:|:------------:|----------------------------------------------------------------------------| -| name | string | true | Name of the parameter. | -| type | string | true | Must be one of "string", "integer", "float", "boolean" "array" | -| description | string | true | Natural language description of the parameter to describe it to the agent. | +| **field** | **type** | **required** | **description** | +|-------------|:---------------:|:------------:|-----------------------------------------------------------------------------| +| name | string | true | Name of the parameter. | +| type | string | true | Must be one of "string", "integer", "float", "boolean" "array" | +| default | parameter type | false | Default value of the parameter. If provided, the parameter is not required. | +| description | string | true | Natural language description of the parameter to describe it to the agent. | ### Array Parameters @@ -102,12 +103,17 @@ in the list using the items field: description: Name of the airline. ``` -| **field** | **type** | **required** | **description** | -|-------------|:----------------:|:------------:|----------------------------------------------------------------------------| -| name | string | true | Name of the parameter. | -| type | string | true | Must be "array" | -| description | string | true | Natural language description of the parameter to describe it to the agent. | -| items | parameter object | true | Specify a Parameter object for the type of the values in the array. | +| **field** | **type** | **required** | **description** | +|-------------|:----------------:|:------------:|-----------------------------------------------------------------------------| +| name | string | true | Name of the parameter. | +| type | string | true | Must be "array" | +| default | parameter type | false | Default value of the parameter. If provided, the parameter is not required. | +| description | string | true | Natural language description of the parameter to describe it to the agent. | +| items | parameter object | true | Specify a Parameter object for the type of the values in the array. | + +{{< notice note >}} +Items in array should not have a default value. If provided, it will be ignored. +{{< /notice >}} ### Authenticated Parameters diff --git a/internal/tools/parameters.go b/internal/tools/parameters.go index 5663c06df2..7b74511e8c 100644 --- a/internal/tools/parameters.go +++ b/internal/tools/parameters.go @@ -130,7 +130,10 @@ func ParseParams(ps Parameters, data map[string]any, claimsMap map[string]map[st var ok bool v, ok = data[name] if !ok { - return nil, fmt.Errorf("parameter %q is required", name) + v = p.GetDefault() + if v == nil { + return nil, fmt.Errorf("parameter %q is required", name) + } } } else { // parse authenticated parameter @@ -253,6 +256,7 @@ type Parameter interface { // but this is done to differentiate it from the fields in CommonParameter. GetName() string GetType() string + GetDefault() any GetAuthServices() []ParamAuthService Parse(any) (any, error) Manifest() ParameterManifest @@ -382,8 +386,10 @@ func (ps Parameters) McpManifest() McpToolsSchema { for _, p := range ps { name := p.GetName() properties[name] = p.McpManifest() - // all parameters are added to the required field - required = append(required, name) + // parameters that doesn't have a default value are added to the required field + if p.GetDefault() == nil { + required = append(required, name) + } } return McpToolsSchema{ @@ -397,6 +403,7 @@ func (ps Parameters) McpManifest() McpToolsSchema { type ParameterManifest struct { Name string `json:"name"` Type string `json:"type"` + Required bool `json:"required"` Description string `json:"description"` AuthServices []string `json:"authSources"` Items *ParameterManifest `json:"items,omitempty"` @@ -413,6 +420,7 @@ type ParameterMcpManifest struct { type CommonParameter struct { Name string `yaml:"name" validate:"required"` Type string `yaml:"type" validate:"required"` + Default any `yaml:"default"` Desc string `yaml:"description" validate:"required"` AuthServices []ParamAuthService `yaml:"authServices"` AuthSources []ParamAuthService `yaml:"authSources"` // Deprecated: Kept for compatibility. @@ -428,6 +436,10 @@ func (p *CommonParameter) GetType() string { return p.Type } +func (p *CommonParameter) GetDefault() any { + return p.Default +} + // Manifest returns the manifest for the Parameter. func (p *CommonParameter) Manifest() ParameterManifest { // only list ParamAuthService names (without fields) in manifest @@ -435,9 +447,14 @@ func (p *CommonParameter) Manifest() ParameterManifest { for i, a := range p.AuthServices { authNames[i] = a.Name } + var required bool + if p.Default == nil { + required = true + } return ParameterManifest{ Name: p.Name, Type: p.Type, + Required: required, Description: p.Desc, AuthServices: authNames, } @@ -468,7 +485,7 @@ type ParamAuthService struct { } // NewStringParameter is a convenience function for initializing a StringParameter. -func NewStringParameter(name, desc string) *StringParameter { +func NewStringParameter(name string, desc string) *StringParameter { return &StringParameter{ CommonParameter: CommonParameter{ Name: name, @@ -479,8 +496,21 @@ func NewStringParameter(name, desc string) *StringParameter { } } +// NewStringParameterWithDefault is a convenience function for initializing a StringParameter with default value. +func NewStringParameterWithDefault(name string, defaultV, desc string) *StringParameter { + return &StringParameter{ + CommonParameter: CommonParameter{ + Name: name, + Type: typeString, + Default: defaultV, + Desc: desc, + AuthServices: nil, + }, + } +} + // NewStringParameterWithAuth is a convenience function for initializing a StringParameter with a list of ParamAuthService. -func NewStringParameterWithAuth(name, desc string, authServices []ParamAuthService) *StringParameter { +func NewStringParameterWithAuth(name string, desc string, authServices []ParamAuthService) *StringParameter { return &StringParameter{ CommonParameter: CommonParameter{ Name: name, @@ -511,7 +541,7 @@ func (p *StringParameter) GetAuthServices() []ParamAuthService { } // NewIntParameter is a convenience function for initializing a IntParameter. -func NewIntParameter(name, desc string) *IntParameter { +func NewIntParameter(name string, desc string) *IntParameter { return &IntParameter{ CommonParameter: CommonParameter{ Name: name, @@ -522,8 +552,21 @@ func NewIntParameter(name, desc string) *IntParameter { } } +// NewIntParameterWithDefault is a convenience function for initializing a IntParameter with default value. +func NewIntParameterWithDefault(name string, defaultV any, desc string) *IntParameter { + return &IntParameter{ + CommonParameter: CommonParameter{ + Name: name, + Type: typeInt, + Default: defaultV, + Desc: desc, + AuthServices: nil, + }, + } +} + // NewIntParameterWithAuth is a convenience function for initializing a IntParameter with a list of ParamAuthService. -func NewIntParameterWithAuth(name, desc string, authServices []ParamAuthService) *IntParameter { +func NewIntParameterWithAuth(name string, desc string, authServices []ParamAuthService) *IntParameter { return &IntParameter{ CommonParameter: CommonParameter{ Name: name, @@ -567,7 +610,7 @@ func (p *IntParameter) GetAuthServices() []ParamAuthService { } // NewFloatParameter is a convenience function for initializing a FloatParameter. -func NewFloatParameter(name, desc string) *FloatParameter { +func NewFloatParameter(name string, desc string) *FloatParameter { return &FloatParameter{ CommonParameter: CommonParameter{ Name: name, @@ -578,8 +621,21 @@ func NewFloatParameter(name, desc string) *FloatParameter { } } +// NewFloatParameterWithDefault is a convenience function for initializing a FloatParameter with default value. +func NewFloatParameterWithDefault(name string, defaultV float64, desc string) *FloatParameter { + return &FloatParameter{ + CommonParameter: CommonParameter{ + Name: name, + Type: typeFloat, + Default: defaultV, + Desc: desc, + AuthServices: nil, + }, + } +} + // NewFloatParameterWithAuth is a convenience function for initializing a FloatParameter with a list of ParamAuthService. -func NewFloatParameterWithAuth(name, desc string, authServices []ParamAuthService) *FloatParameter { +func NewFloatParameterWithAuth(name string, desc string, authServices []ParamAuthService) *FloatParameter { return &FloatParameter{ CommonParameter: CommonParameter{ Name: name, @@ -621,7 +677,7 @@ func (p *FloatParameter) GetAuthServices() []ParamAuthService { } // NewBooleanParameter is a convenience function for initializing a BooleanParameter. -func NewBooleanParameter(name, desc string) *BooleanParameter { +func NewBooleanParameter(name string, desc string) *BooleanParameter { return &BooleanParameter{ CommonParameter: CommonParameter{ Name: name, @@ -632,8 +688,21 @@ func NewBooleanParameter(name, desc string) *BooleanParameter { } } +// NewBooleanParameterWithDefault is a convenience function for initializing a BooleanParameter with default value. +func NewBooleanParameterWithDefault(name string, defaultV bool, desc string) *BooleanParameter { + return &BooleanParameter{ + CommonParameter: CommonParameter{ + Name: name, + Type: typeBool, + Default: defaultV, + Desc: desc, + AuthServices: nil, + }, + } +} + // NewBooleanParameterWithAuth is a convenience function for initializing a BooleanParameter with a list of ParamAuthService. -func NewBooleanParameterWithAuth(name, desc string, authServices []ParamAuthService) *BooleanParameter { +func NewBooleanParameterWithAuth(name string, desc string, authServices []ParamAuthService) *BooleanParameter { return &BooleanParameter{ CommonParameter: CommonParameter{ Name: name, @@ -664,7 +733,7 @@ func (p *BooleanParameter) GetAuthServices() []ParamAuthService { } // NewArrayParameter is a convenience function for initializing a ArrayParameter. -func NewArrayParameter(name, desc string, items Parameter) *ArrayParameter { +func NewArrayParameter(name string, desc string, items Parameter) *ArrayParameter { return &ArrayParameter{ CommonParameter: CommonParameter{ Name: name, @@ -676,8 +745,22 @@ func NewArrayParameter(name, desc string, items Parameter) *ArrayParameter { } } +// NewArrayParameterWithDefault is a convenience function for initializing a ArrayParameter with default value. +func NewArrayParameterWithDefault(name string, defaultV any, desc string, items Parameter) *ArrayParameter { + return &ArrayParameter{ + CommonParameter: CommonParameter{ + Name: name, + Type: typeArray, + Default: defaultV, + Desc: desc, + AuthServices: nil, + }, + Items: items, + } +} + // NewArrayParameterWithAuth is a convenience function for initializing a ArrayParameter with a list of ParamAuthService. -func NewArrayParameterWithAuth(name, desc string, items Parameter, authServices []ParamAuthService) *ArrayParameter { +func NewArrayParameterWithAuth(name string, desc string, items Parameter, authServices []ParamAuthService) *ArrayParameter { return &ArrayParameter{ CommonParameter: CommonParameter{ Name: name, @@ -746,9 +829,15 @@ func (p *ArrayParameter) Manifest() ParameterManifest { authNames[i] = a.Name } items := p.Items.Manifest() + var required bool + if p.Default == nil { + required = true + items.Required = true + } return ParameterManifest{ Name: p.Name, Type: p.Type, + Required: required, Description: p.Desc, AuthServices: authNames, Items: &items, diff --git a/internal/tools/parameters_test.go b/internal/tools/parameters_test.go index fc487ad902..f66eebc592 100644 --- a/internal/tools/parameters_test.go +++ b/internal/tools/parameters_test.go @@ -125,6 +125,100 @@ func TestParametersMarshal(t *testing.T) { tools.NewArrayParameter("my_array", "this param is an array of floats", tools.NewFloatParameter("my_float", "float item")), }, }, + { + name: "string default", + in: []map[string]any{ + { + "name": "my_string", + "type": "string", + "default": "foo", + "description": "this param is a string", + }, + }, + want: tools.Parameters{ + tools.NewStringParameterWithDefault("my_string", "foo", "this param is a string"), + }, + }, + { + name: "int default", + in: []map[string]any{ + { + "name": "my_integer", + "type": "integer", + "default": 5, + "description": "this param is an int", + }, + }, + want: tools.Parameters{ + tools.NewIntParameterWithDefault("my_integer", uint64(5), "this param is an int"), + }, + }, + { + name: "float default", + in: []map[string]any{ + { + "name": "my_float", + "type": "float", + "default": 1.1, + "description": "my param is a float", + }, + }, + want: tools.Parameters{ + tools.NewFloatParameterWithDefault("my_float", 1.1, "my param is a float"), + }, + }, + { + name: "bool default", + in: []map[string]any{ + { + "name": "my_bool", + "type": "boolean", + "default": true, + "description": "this param is a boolean", + }, + }, + want: tools.Parameters{ + tools.NewBooleanParameterWithDefault("my_bool", true, "this param is a boolean"), + }, + }, + { + name: "string array default", + in: []map[string]any{ + { + "name": "my_array", + "type": "array", + "default": `["foo", "bar"]`, + "description": "this param is an array of strings", + "items": map[string]string{ + "name": "my_string", + "type": "string", + "description": "string item", + }, + }, + }, + want: tools.Parameters{ + tools.NewArrayParameterWithDefault("my_array", `["foo", "bar"]`, "this param is an array of strings", tools.NewStringParameter("my_string", "string item")), + }, + }, + { + name: "float array default", + in: []map[string]any{ + { + "name": "my_array", + "type": "array", + "default": "[1.0, 1.1]", + "description": "this param is an array of floats", + "items": map[string]string{ + "name": "my_float", + "type": "float", + "description": "float item", + }, + }, + }, + want: tools.Parameters{ + tools.NewArrayParameterWithDefault("my_array", "[1.0, 1.1]", "this param is an array of floats", tools.NewFloatParameter("my_float", "float item")), + }, + }, } for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { @@ -539,6 +633,46 @@ func TestParametersParse(t *testing.T) { "my_bool": 1.5, }, }, + { + name: "string default", + params: tools.Parameters{ + tools.NewStringParameterWithDefault("my_string", "foo", "this param is a string"), + }, + in: map[string]any{}, + want: tools.ParamValues{tools.ParamValue{Name: "my_string", Value: "foo"}}, + }, + { + name: "int default", + params: tools.Parameters{ + tools.NewIntParameterWithDefault("my_int", 100, "this param is an int"), + }, + in: map[string]any{}, + want: tools.ParamValues{tools.ParamValue{Name: "my_int", Value: 100}}, + }, + { + name: "int (big)", + params: tools.Parameters{ + tools.NewIntParameterWithDefault("my_big_int", math.MaxInt64, "this param is an int"), + }, + in: map[string]any{}, + want: tools.ParamValues{tools.ParamValue{Name: "my_big_int", Value: math.MaxInt64}}, + }, + { + name: "float default", + params: tools.Parameters{ + tools.NewFloatParameterWithDefault("my_float", 1.1, "this param is a float"), + }, + in: map[string]any{}, + want: tools.ParamValues{tools.ParamValue{Name: "my_float", Value: 1.1}}, + }, + { + name: "bool default", + params: tools.Parameters{ + tools.NewBooleanParameterWithDefault("my_bool", true, "this param is a bool"), + }, + in: map[string]any{}, + want: tools.ParamValues{tools.ParamValue{Name: "my_bool", Value: true}}, + }, } for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { @@ -808,22 +942,22 @@ func TestParamManifest(t *testing.T) { { name: "string", in: tools.NewStringParameter("foo-string", "bar"), - want: tools.ParameterManifest{Name: "foo-string", Type: "string", Description: "bar", AuthServices: []string{}}, + want: tools.ParameterManifest{Name: "foo-string", Type: "string", Required: true, Description: "bar", AuthServices: []string{}}, }, { name: "int", in: tools.NewIntParameter("foo-int", "bar"), - want: tools.ParameterManifest{Name: "foo-int", Type: "integer", Description: "bar", AuthServices: []string{}}, + want: tools.ParameterManifest{Name: "foo-int", Type: "integer", Required: true, Description: "bar", AuthServices: []string{}}, }, { name: "float", in: tools.NewFloatParameter("foo-float", "bar"), - want: tools.ParameterManifest{Name: "foo-float", Type: "float", Description: "bar", AuthServices: []string{}}, + want: tools.ParameterManifest{Name: "foo-float", Type: "float", Required: true, Description: "bar", AuthServices: []string{}}, }, { name: "boolean", in: tools.NewBooleanParameter("foo-bool", "bar"), - want: tools.ParameterManifest{Name: "foo-bool", Type: "boolean", Description: "bar", AuthServices: []string{}}, + want: tools.ParameterManifest{Name: "foo-bool", Type: "boolean", Required: true, Description: "bar", AuthServices: []string{}}, }, { name: "array", @@ -831,9 +965,42 @@ func TestParamManifest(t *testing.T) { want: tools.ParameterManifest{ Name: "foo-array", Type: "array", + Required: true, Description: "bar", AuthServices: []string{}, - Items: &tools.ParameterManifest{Name: "foo-string", Type: "string", Description: "bar", AuthServices: []string{}}, + Items: &tools.ParameterManifest{Name: "foo-string", Type: "string", Required: true, Description: "bar", AuthServices: []string{}}, + }, + }, + { + name: "string default", + in: tools.NewStringParameterWithDefault("foo-string", "foo", "bar"), + want: tools.ParameterManifest{Name: "foo-string", Type: "string", Required: false, Description: "bar", AuthServices: []string{}}, + }, + { + name: "int default", + in: tools.NewIntParameterWithDefault("foo-int", 1, "bar"), + want: tools.ParameterManifest{Name: "foo-int", Type: "integer", Required: false, Description: "bar", AuthServices: []string{}}, + }, + { + name: "float default", + in: tools.NewFloatParameterWithDefault("foo-float", 1.1, "bar"), + want: tools.ParameterManifest{Name: "foo-float", Type: "float", Required: false, Description: "bar", AuthServices: []string{}}, + }, + { + name: "boolean default", + in: tools.NewBooleanParameterWithDefault("foo-bool", true, "bar"), + want: tools.ParameterManifest{Name: "foo-bool", Type: "boolean", Required: false, Description: "bar", AuthServices: []string{}}, + }, + { + name: "array default", + in: tools.NewArrayParameterWithDefault("foo-array", `["foo", "bar"]`, "bar", tools.NewStringParameter("foo-string", "bar")), + want: tools.ParameterManifest{ + Name: "foo-array", + Type: "array", + Required: false, + Description: "bar", + AuthServices: []string{}, + Items: &tools.ParameterManifest{Name: "foo-string", Type: "string", Required: true, Description: "bar", AuthServices: []string{}}, }, }, } @@ -893,6 +1060,54 @@ func TestParamMcpManifest(t *testing.T) { } } +func TestMcpManifest(t *testing.T) { + tcs := []struct { + name string + in tools.Parameters + want tools.McpToolsSchema + }{ + { + name: "string", + in: tools.Parameters{ + tools.NewStringParameterWithDefault("foo-string", "foo", "bar"), + tools.NewStringParameter("foo-string2", "bar"), + tools.NewIntParameterWithDefault("foo-int", 1, "bar"), + tools.NewIntParameter("foo-int2", "bar"), + tools.NewArrayParameterWithDefault("foo-array", []string{"hello", "world"}, "bar", tools.NewStringParameter("foo-string", "bar")), + tools.NewArrayParameter("foo-array2", "bar", tools.NewStringParameter("foo-string", "bar")), + }, + want: tools.McpToolsSchema{ + Type: "object", + Properties: map[string]tools.ParameterMcpManifest{ + "foo-string": tools.ParameterMcpManifest{Type: "string", Description: "bar"}, + "foo-string2": tools.ParameterMcpManifest{Type: "string", Description: "bar"}, + "foo-int": tools.ParameterMcpManifest{Type: "integer", Description: "bar"}, + "foo-int2": tools.ParameterMcpManifest{Type: "integer", Description: "bar"}, + "foo-array": tools.ParameterMcpManifest{ + Type: "array", + Description: "bar", + Items: &tools.ParameterMcpManifest{Type: "string", Description: "bar"}, + }, + "foo-array2": tools.ParameterMcpManifest{ + Type: "array", + Description: "bar", + Items: &tools.ParameterMcpManifest{Type: "string", Description: "bar"}, + }, + }, + Required: []string{"foo-string2", "foo-int2", "foo-array2"}, + }, + }, + } + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + got := tc.in.McpManifest() + if !reflect.DeepEqual(got, tc.want) { + t.Fatalf("unexpected manifest: got %+v, want %+v", got, tc.want) + } + }) + } +} + func TestFailParametersUnmarshal(t *testing.T) { ctx, err := testutils.ContextWithNewLogger() if err != nil { diff --git a/tests/alloydbainl/alloydb_ai_nl_integration_test.go b/tests/alloydbainl/alloydb_ai_nl_integration_test.go index 5d4623be78..5aa89c2ee5 100644 --- a/tests/alloydbainl/alloydb_ai_nl_integration_test.go +++ b/tests/alloydbainl/alloydb_ai_nl_integration_test.go @@ -118,6 +118,7 @@ func runAiNlToolGetTest(t *testing.T) { map[string]any{ "name": "question", "type": "string", + "required": true, "description": "The natural language question to ask.", "authSources": []any{}, },