From b2c3d44bd1612d119bd393d03df78d9c21f8077a Mon Sep 17 00:00:00 2001 From: Anthony Wat Date: Mon, 13 May 2024 18:13:58 -0400 Subject: [PATCH 1/3] feat: Add function schema support to aws_bedrockagent_agent_action_group --- .changelog/37484.txt | 3 + .../bedrockagent/agent_action_group.go | 155 ++++++++++++++++++ .../bedrockagent/agent_action_group_test.go | 81 ++++++++- ...rockagent_agent_action_group.html.markdown | 86 +++++++++- 4 files changed, 316 insertions(+), 9 deletions(-) create mode 100644 .changelog/37484.txt diff --git a/.changelog/37484.txt b/.changelog/37484.txt new file mode 100644 index 00000000000..8c0f8f40312 --- /dev/null +++ b/.changelog/37484.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_bedrockagent_agent_action_group: Add `function_schema` argument +``` \ No newline at end of file diff --git a/internal/service/bedrockagent/agent_action_group.go b/internal/service/bedrockagent/agent_action_group.go index 62a40042e55..5b9001fe7b8 100644 --- a/internal/service/bedrockagent/agent_action_group.go +++ b/internal/service/bedrockagent/agent_action_group.go @@ -14,6 +14,7 @@ import ( awstypes "github.com/aws/aws-sdk-go-v2/service/bedrockagent/types" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" @@ -150,6 +151,63 @@ func (r *agentActionGroupResource) Schema(ctx context.Context, request resource. }, }, }, + "function_schema": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[functionSchemaModel](ctx), + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "functions": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[functionModel](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + names.AttrName: schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexache.MustCompile(`^([0-9a-zA-Z][_-]?){1,100}$`), "valid characters are a-z, A-Z, 0-9, _ (underscore) and - (hyphen). The name can have up to 100 characters"), + }, + }, + names.AttrDescription: schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 1200), + }, + }, + }, + Blocks: map[string]schema.Block{ + names.AttrParameters: schema.SetNestedBlock{ + CustomType: fwtypes.NewSetNestedObjectTypeOf[parameterDetailModel](ctx), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "map_block_key": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexache.MustCompile(`^([0-9a-zA-Z][_-]?){1,100}$`), "valid characters are a-z, A-Z, 0-9, _ (underscore) and - (hyphen). The name can have up to 100 characters"), + }, + }, + names.AttrType: schema.StringAttribute{ + Required: true, + CustomType: fwtypes.StringEnumType[awstypes.Type](), + }, + names.AttrDescription: schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 500), + }, + }, + "required": schema.BoolAttribute{ + Optional: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, }, } } @@ -190,6 +248,21 @@ func (r *agentActionGroupResource) Create(ctx context.Context, request resource. input.ApiSchema = expandAPISchema(ctx, apiSchemaData) } + if !data.FunctionSchema.IsNull() { + functionSchemaData, diags := data.FunctionSchema.ToPtr(ctx) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + + functionSchema, diags := expandFunctionSchema(ctx, functionSchemaData) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + input.FunctionSchema = functionSchema + } + output, err := conn.CreateAgentActionGroup(ctx, input) if err != nil { @@ -245,6 +318,13 @@ func (r *agentActionGroupResource) Read(ctx context.Context, request resource.Re data.ActionGroupExecutor = flattenActionGroupExecutor(ctx, output.ActionGroupExecutor) data.APISchema = flattenAPISchema(ctx, output.ApiSchema) + functionSchema, diags := flattenFunctionSchema(ctx, output.FunctionSchema) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + data.FunctionSchema = functionSchema + response.Diagnostics.Append(response.State.Set(ctx, &data)...) } @@ -266,6 +346,7 @@ func (r *agentActionGroupResource) Update(ctx context.Context, request resource. !new.ActionGroupState.Equal(old.ActionGroupState) || !new.APISchema.Equal(old.APISchema) || !new.Description.Equal(old.Description) || + !new.FunctionSchema.Equal(old.FunctionSchema) || !new.ParentActionGroupSignature.Equal(old.ParentActionGroupSignature) { input := &bedrockagent.UpdateAgentActionGroupInput{} response.Diagnostics.Append(fwflex.Expand(ctx, new, input)...) @@ -294,6 +375,21 @@ func (r *agentActionGroupResource) Update(ctx context.Context, request resource. input.ApiSchema = expandAPISchema(ctx, apiSchemaData) } + if !new.FunctionSchema.IsNull() { + functionSchemaData, diags := new.FunctionSchema.ToPtr(ctx) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + + functionSchema, diags := expandFunctionSchema(ctx, functionSchemaData) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + input.FunctionSchema = functionSchema + } + _, err := conn.UpdateAgentActionGroup(ctx, input) if err != nil { @@ -378,6 +474,7 @@ type agentActionGroupResourceModel struct { AgentVersion types.String `tfsdk:"agent_version"` APISchema fwtypes.ListNestedObjectValueOf[apiSchemaModel] `tfsdk:"api_schema"` Description types.String `tfsdk:"description"` + FunctionSchema fwtypes.ListNestedObjectValueOf[functionSchemaModel] `tfsdk:"function_schema"` ID types.String `tfsdk:"id"` ParentActionGroupSignature fwtypes.StringEnum[awstypes.ActionGroupSignature] `tfsdk:"parent_action_group_signature"` SkipResourceInUseCheck types.Bool `tfsdk:"skip_resource_in_use_check"` @@ -420,6 +517,22 @@ type s3IdentifierModel struct { S3ObjectKey types.String `tfsdk:"s3_object_key"` } +type functionSchemaModel struct { + Functions fwtypes.ListNestedObjectValueOf[functionModel] `tfsdk:"functions"` +} + +type functionModel struct { + Name types.String `tfsdk:"name"` + Description types.String `tfsdk:"description"` + Parameters fwtypes.SetNestedObjectValueOf[parameterDetailModel] `tfsdk:"parameters"` +} +type parameterDetailModel struct { + MapBlockKey types.String `tfsdk:"map_block_key"` + Type fwtypes.StringEnum[awstypes.Type] `tfsdk:"type"` + Description types.String `tfsdk:"description"` + Required types.Bool `tfsdk:"required"` +} + func expandActionGroupExecutor(_ context.Context, actionGroupExecutorData *actionGroupExecutorModel) awstypes.ActionGroupExecutor { if !actionGroupExecutorData.Lambda.IsNull() { return &awstypes.ActionGroupExecutorMemberLambda{ @@ -488,3 +601,45 @@ func flattenAPISchema(ctx context.Context, apiObject awstypes.APISchema) fwtypes return fwtypes.NewListNestedObjectValueOfPtrMust(ctx, &apiSchemaData) } + +func expandFunctionSchema(ctx context.Context, functionSchemaData *functionSchemaModel) (awstypes.FunctionSchema, diag.Diagnostics) { + var diags diag.Diagnostics + + if !functionSchemaData.Functions.IsNull() { + functions := functionSchemaData.Functions + memberFunctions := make([]awstypes.Function, len(functions.Elements())) + diags.Append(fwflex.Expand(ctx, functions, &memberFunctions)...) + if diags.HasError() { + return nil, diags + } + + return &awstypes.FunctionSchemaMemberFunctions{ + Value: memberFunctions, + }, diags + } + + return nil, diags +} + +func flattenFunctionSchema(ctx context.Context, apiObject awstypes.FunctionSchema) (fwtypes.ListNestedObjectValueOf[functionSchemaModel], diag.Diagnostics) { + var diags diag.Diagnostics + + if apiObject == nil { + return fwtypes.NewListNestedObjectValueOfNull[functionSchemaModel](ctx), diags + } + + var functionSchemaData functionSchemaModel + + switch v := apiObject.(type) { + case *awstypes.FunctionSchemaMemberFunctions: + memberFunctions := fwtypes.NewListNestedObjectValueOfSliceMust[functionModel](ctx, []*functionModel{}) + diags.Append(fwflex.Flatten(ctx, v.Value, &memberFunctions)...) + if diags.HasError() { + return fwtypes.NewListNestedObjectValueOfNull[functionSchemaModel](ctx), diags + } + + functionSchemaData.Functions = memberFunctions + } + + return fwtypes.NewListNestedObjectValueOfPtrMust(ctx, &functionSchemaData), diags +} diff --git a/internal/service/bedrockagent/agent_action_group_test.go b/internal/service/bedrockagent/agent_action_group_test.go index cdffd2273e6..7faf4b6f690 100644 --- a/internal/service/bedrockagent/agent_action_group_test.go +++ b/internal/service/bedrockagent/agent_action_group_test.go @@ -120,6 +120,47 @@ func TestAccBedrockAgentAgentActionGroup_update(t *testing.T) { }) } +func TestAccBedrockAgentAgentActionGroup_functionSchema(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_bedrockagent_agent_action_group.test" + var v awstypes.AgentActionGroup + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t); acctest.PreCheckPartitionHasService(t, names.BedrockEndpointID) }, + ErrorCheck: acctest.ErrorCheck(t, names.BedrockAgentServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckAgentActionGroupDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccAgentActionGroupConfig_functionSchema(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAgentActionGroupExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "action_group_name", rName), + resource.TestCheckResourceAttr(resourceName, "function_schema.#", acctest.CtOne), + resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.#", acctest.CtOne), + resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.name", "sayHello"), + resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.description", "Says Hello"), + resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.#", acctest.CtTwo), + resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.0.map_block_key", names.AttrMessage), + resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.0.type", "string"), + resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.0.description", "The Hello message"), + resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.0.required", "true"), + resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.1.map_block_key", "unused"), + resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.1.type", "integer"), + resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.1.required", "false"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"skip_resource_in_use_check"}, + }, + }, + }) +} + func testAccCheckAgentActionGroupDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).BedrockAgentClient(ctx) @@ -168,7 +209,7 @@ func testAccCheckAgentActionGroupExists(ctx context.Context, n string, v *awstyp func testAccAgentActionGroupConfig_basic(rName string) string { return acctest.ConfigCompose(testAccAgentConfig_basic(rName, "anthropic.claude-v2", "basic claude"), - testAccAgentActionGroupConfig_lamba(rName), + testAccAgentActionGroupConfig_lambda(rName), fmt.Sprintf(` resource "aws_bedrockagent_agent_action_group" "test" { action_group_name = %[1]q @@ -188,7 +229,7 @@ resource "aws_bedrockagent_agent_action_group" "test" { func testAccAgentActionGroupConfig_s3APISchema(rName string) string { return acctest.ConfigCompose(testAccAgentConfig_basic(rName, "anthropic.claude-v2", "basic claude"), - testAccAgentActionGroupConfig_lamba(rName), + testAccAgentActionGroupConfig_lambda(rName), fmt.Sprintf(` resource "aws_s3_bucket" "test" { bucket = %[1]q @@ -219,7 +260,7 @@ resource "aws_bedrockagent_agent_action_group" "test" { `, rName)) } -func testAccAgentActionGroupConfig_lamba(rName string) string { +func testAccAgentActionGroupConfig_lambda(rName string) string { return fmt.Sprintf(` data "aws_iam_policy_document" "lambda_assume" { statement { @@ -251,3 +292,37 @@ resource "aws_lambda_function" "test_lambda" { } `, rName) } + +func testAccAgentActionGroupConfig_functionSchema(rName string) string { + return acctest.ConfigCompose(testAccAgentConfig_basic(rName, "anthropic.claude-v2", "basic claude"), + testAccAgentActionGroupConfig_lambda(rName), + fmt.Sprintf(` +resource "aws_bedrockagent_agent_action_group" "test" { + action_group_name = %[1]q + agent_id = aws_bedrockagent_agent.test.agent_id + agent_version = "DRAFT" + description = "Basic Agent Action" + skip_resource_in_use_check = true + action_group_executor { + lambda = aws_lambda_function.test_lambda.arn + } + function_schema { + functions { + name = "sayHello" + description = "Says Hello" + parameters { + map_block_key = "message" + type = "string" + description = "The Hello message" + required = true + } + parameters { + map_block_key = "unused" + type = "integer" + required = false + } + } + } +} +`, rName)) +} diff --git a/website/docs/r/bedrockagent_agent_action_group.html.markdown b/website/docs/r/bedrockagent_agent_action_group.html.markdown index c0fea52bafe..f65d131bfe4 100644 --- a/website/docs/r/bedrockagent_agent_action_group.html.markdown +++ b/website/docs/r/bedrockagent_agent_action_group.html.markdown @@ -13,6 +13,23 @@ Terraform resource for managing an AWS Agents for Amazon Bedrock Agent Action Gr ### Basic Usage +```terraform +resource "aws_bedrockagent_agent_action_group" "example" { + action_group_name = "example" + agent_id = "GGRRAED6JP" + agent_version = "DRAFT" + skip_resource_in_use_check = true + action_group_executor { + lambda = "arn:aws:lambda:us-west-2:123456789012:function:example-function" + } + api_schema { + payload = file("path/to/schema.yaml") + } +} +``` + +### API Schema in S3 Bucket + ```terraform resource "aws_bedrockagent_agent_action_group" "example" { action_group_name = "example" @@ -29,7 +46,38 @@ resource "aws_bedrockagent_agent_action_group" "example" { } } } +``` + +### Function Details/Schema +```terraform +resource "aws_bedrockagent_agent_action_group" "example" { + action_group_name = "example" + agent_id = "GGRRAED6JP" + agent_version = "DRAFT" + skip_resource_in_use_check = true + action_group_executor { + lambda = "arn:aws:lambda:us-west-2:123456789012:function:example-function" + } + function_schema { + functions { + name = "example-function" + description = "Example function" + parameters { + map_block_key = "param1" + type = "string" + description = "The first parameter" + required = true + } + parameters { + map_block_key = "param2" + type = "integer" + description = "The second parameter" + required = false + } + } + } +} ``` ## Argument Reference @@ -39,36 +87,62 @@ The following arguments are required: * `action_group_name` - (Required) Name of the action group. * `agent_id` - (Required) The unique identifier of the agent for which to create the action group. * `agent_version` - (Required) Version of the agent for which to create the action group. Valid values: `DRAFT`. -* `action_group_executor` - (Required) ARN of the Lambda function containing the business logic that is carried out upon invoking the action or custom control method for handling the information elicited from the user. See [`action_group_executor` block](#action_group_executor-block) for details. -* `api_schema` - (Required) Either details about the S3 object containing the OpenAPI schema for the action group or the JSON or YAML-formatted payload defining the schema. For more information, see [Action group OpenAPI schemas](https://docs.aws.amazon.com/bedrock/latest/userguide/agents-api-schema.html). See [`api_schema` block](#api_schema-block) for details. +* `action_group_executor` - (Required) ARN of the Lambda function containing the business logic that is carried out upon invoking the action or custom control method for handling the information elicited from the user. See [`action_group_executor` Block](#action_group_executor-block) for details. The following arguments are optional: * `action_group_state` - (Optional) Whether the action group is available for the agent to invoke or not when sending an [InvokeAgent](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_agent-runtime_InvokeAgent.html) request. Valid values: `ENABLED`, `DISABLED`. +* `api_schema` - (Optional) Either details about the S3 object containing the OpenAPI schema for the action group or the JSON or YAML-formatted payload defining the schema. For more information, see [Action group OpenAPI schemas](https://docs.aws.amazon.com/bedrock/latest/userguide/agents-api-schema.html). See [`api_schema` Block](#api_schema-block) for details. * `description` - (Optional) Description of the action group. +* `function_schema` - (Optional) Functions that each define parameters that the agent needs to invoke from the user. Each function represents an action in an action group. See [`function_schema` Block](#function_schema-block) for details. * `parent_action_group_signature` - (Optional) To allow your agent to request the user for additional information when trying to complete a task, set this argument to `AMAZON.UserInput`. You must leave the `description`, `api_schema`, and `action_group_executor` arguments blank for this action group. Valid values: `AMAZON.UserInput`. * `skip_resource_in_use_check` - (Optional) Whether the in-use check is skipped when deleting the action group. -### `action_group_executor` block +### `action_group_executor` Block The `action_group_executor` configuration block supports the following arguments: * `lambda` - (Optional) ARN of the Lambda function containing the business logic that is carried out upon invoking the action. -### `api_schema` block +### `api_schema` Block The `api_schema` configuration block supports the following arguments: * `payload` - (Optional) JSON or YAML-formatted payload defining the OpenAPI schema for the action group. -* `s3` - (Optional) Details about the S3 object containing the OpenAPI schema for the action group. See [`s3` block](#s3-block) for details. +* `s3` - (Optional) Details about the S3 object containing the OpenAPI schema for the action group. See [`s3` Block](#s3-block) for details. -### `s3` block +### `s3` Block The `s3` configuration block supports the following arguments: * `s3_bucket_name` - (Optional) Name of the S3 bucket. * `s3_object_key` - (Optional) S3 object key containing the resource. +### `function_schema` Block + +The `function_schema` configuration block supports the following arguments: + +* `functions` - (Optional) Functions that each define an action in the action group. See [`functions` Block](#functions-block) for details. + +### `functions` Block + +The `functions` configuration block supports the following arguments: + +* `name` - (Required) Name for the function. +* `description` - (Optional) Description of the function and its purpose. +* `parameters` - (Optional) Parameters that the agent elicits from the user to fulfill the function. See [`parameters` Block](#parameters-block) for details. + +### `parameters` Block + +The `parameters` configuration block supports the following arguments: + +* `map_block_key` - (Required) Name of the parameter. + + **Note:** The argument name `map_block_key` may seem out of context, but is necessary for backward compatibility reasons in the provider. +* `type` - (Required) Data type of the parameter. Valid values: `string`, `number`, `integer`, `boolean`, `array`. +* `description` - (Optional) Description of the parameter. Helps the foundation model determine how to elicit the parameters from the user. +* `required` - (Optional) Whether the parameter is required for the agent to complete the function for action group invocation. + ## Attribute Reference This resource exports the following attributes in addition to the arguments above: From 751bee7455c84897fa5560507f98093b86610507 Mon Sep 17 00:00:00 2001 From: Anthony Wat Date: Mon, 20 May 2024 19:00:21 -0400 Subject: [PATCH 2/3] feat: Add return of control support to aws_bedrockagent_agent_action_group --- .changelog/37484.txt | 3 + .../bedrockagent/agent_action_group.go | 15 ++++- .../bedrockagent/agent_action_group_test.go | 59 ++++++++++++++++++- ...rockagent_agent_action_group.html.markdown | 22 ++++++- 4 files changed, 93 insertions(+), 6 deletions(-) diff --git a/.changelog/37484.txt b/.changelog/37484.txt index 8c0f8f40312..0316a33fcf0 100644 --- a/.changelog/37484.txt +++ b/.changelog/37484.txt @@ -1,3 +1,6 @@ ```release-note:enhancement resource/aws_bedrockagent_agent_action_group: Add `function_schema` argument +``` +```release-note:enhancement +resource/aws_bedrockagent_agent_action_group: Add `actionGroupExecutor.customControl` argument ``` \ No newline at end of file diff --git a/internal/service/bedrockagent/agent_action_group.go b/internal/service/bedrockagent/agent_action_group.go index 5b9001fe7b8..ced8d11d64b 100644 --- a/internal/service/bedrockagent/agent_action_group.go +++ b/internal/service/bedrockagent/agent_action_group.go @@ -105,6 +105,10 @@ func (r *agentActionGroupResource) Schema(ctx context.Context, request resource. }, NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ + "custom_control": schema.StringAttribute{ + CustomType: fwtypes.StringEnumType[awstypes.CustomControlMethod](), + Optional: true, + }, "lambda": schema.StringAttribute{ CustomType: fwtypes.ARNType, Optional: true, @@ -504,7 +508,8 @@ func (m *agentActionGroupResourceModel) setID() { } type actionGroupExecutorModel struct { - Lambda fwtypes.ARN `tfsdk:"lambda"` + CustomControl types.String `tfsdk:"custom_control"` + Lambda fwtypes.ARN `tfsdk:"lambda"` } type apiSchemaModel struct { @@ -534,7 +539,11 @@ type parameterDetailModel struct { } func expandActionGroupExecutor(_ context.Context, actionGroupExecutorData *actionGroupExecutorModel) awstypes.ActionGroupExecutor { - if !actionGroupExecutorData.Lambda.IsNull() { + if !actionGroupExecutorData.CustomControl.IsNull() { + return &awstypes.ActionGroupExecutorMemberCustomControl{ + Value: fwtypes.StringEnumValue(awstypes.CustomControlMethod(actionGroupExecutorData.CustomControl.ValueString())).ValueEnum(), + } + } else if !actionGroupExecutorData.Lambda.IsNull() { return &awstypes.ActionGroupExecutorMemberLambda{ Value: actionGroupExecutorData.Lambda.ValueString(), } @@ -551,6 +560,8 @@ func flattenActionGroupExecutor(ctx context.Context, apiObject awstypes.ActionGr var actionGroupExecutorData actionGroupExecutorModel switch v := apiObject.(type) { + case *awstypes.ActionGroupExecutorMemberCustomControl: + actionGroupExecutorData.CustomControl = fwtypes.StringEnumValue(v.Value).StringValue case *awstypes.ActionGroupExecutorMemberLambda: actionGroupExecutorData.Lambda = fwtypes.ARNValue(v.Value) } diff --git a/internal/service/bedrockagent/agent_action_group_test.go b/internal/service/bedrockagent/agent_action_group_test.go index 7faf4b6f690..20c9f71baf8 100644 --- a/internal/service/bedrockagent/agent_action_group_test.go +++ b/internal/service/bedrockagent/agent_action_group_test.go @@ -65,6 +65,8 @@ func TestAccBedrockAgentAgentActionGroup_s3APISchema(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAgentActionGroupExists(ctx, resourceName, &v), resource.TestCheckResourceAttr(resourceName, "action_group_name", rName), + resource.TestCheckResourceAttr(resourceName, "action_group_executor.#", acctest.Ct1), + resource.TestCheckResourceAttrSet(resourceName, "action_group_executor.0.lambda"), ), }, { @@ -137,11 +139,11 @@ func TestAccBedrockAgentAgentActionGroup_functionSchema(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAgentActionGroupExists(ctx, resourceName, &v), resource.TestCheckResourceAttr(resourceName, "action_group_name", rName), - resource.TestCheckResourceAttr(resourceName, "function_schema.#", acctest.CtOne), - resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.#", acctest.CtOne), + resource.TestCheckResourceAttr(resourceName, "function_schema.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.#", acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.name", "sayHello"), resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.description", "Says Hello"), - resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.#", acctest.CtTwo), + resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.#", acctest.Ct2), resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.0.map_block_key", names.AttrMessage), resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.0.type", "string"), resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.0.description", "The Hello message"), @@ -161,6 +163,37 @@ func TestAccBedrockAgentAgentActionGroup_functionSchema(t *testing.T) { }) } +func TestAccBedrockAgentAgentActionGroup_returnControl(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_bedrockagent_agent_action_group.test" + var v awstypes.AgentActionGroup + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t); acctest.PreCheckPartitionHasService(t, names.BedrockEndpointID) }, + ErrorCheck: acctest.ErrorCheck(t, names.BedrockAgentServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckAgentActionGroupDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccAgentActionGroupConfig_returnControl(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAgentActionGroupExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "action_group_name", rName), + resource.TestCheckResourceAttr(resourceName, "action_group_executor.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "action_group_executor.0.custom_control", "RETURN_CONTROL"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"skip_resource_in_use_check"}, + }, + }, + }) +} + func testAccCheckAgentActionGroupDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).BedrockAgentClient(ctx) @@ -326,3 +359,23 @@ resource "aws_bedrockagent_agent_action_group" "test" { } `, rName)) } + +func testAccAgentActionGroupConfig_returnControl(rName string) string { + return acctest.ConfigCompose(testAccAgentConfig_basic(rName, "anthropic.claude-v2", "basic claude"), + testAccAgentActionGroupConfig_lambda(rName), + fmt.Sprintf(` +resource "aws_bedrockagent_agent_action_group" "test" { + action_group_name = %[1]q + agent_id = aws_bedrockagent_agent.test.agent_id + agent_version = "DRAFT" + description = "Basic Agent Action" + skip_resource_in_use_check = true + action_group_executor { + custom_control = "RETURN_CONTROL" + } + api_schema { + payload = file("${path.module}/test-fixtures/api_schema.yaml") + } +} +`, rName)) +} diff --git a/website/docs/r/bedrockagent_agent_action_group.html.markdown b/website/docs/r/bedrockagent_agent_action_group.html.markdown index f65d131bfe4..20406285804 100644 --- a/website/docs/r/bedrockagent_agent_action_group.html.markdown +++ b/website/docs/r/bedrockagent_agent_action_group.html.markdown @@ -48,7 +48,7 @@ resource "aws_bedrockagent_agent_action_group" "example" { } ``` -### Function Details/Schema +### Function Schema (Simplified Schema) ```terraform resource "aws_bedrockagent_agent_action_group" "example" { @@ -80,6 +80,23 @@ resource "aws_bedrockagent_agent_action_group" "example" { } ``` +### Return of Control + +```terraform +resource "aws_bedrockagent_agent_action_group" "example" { + action_group_name = "example" + agent_id = "GGRRAED6JP" + agent_version = "DRAFT" + skip_resource_in_use_check = true + action_group_executor { + custom_control = "RETURN_CONTROL" + } + api_schema { + payload = file("path/to/schema.yaml") + } +} +``` + ## Argument Reference The following arguments are required: @@ -102,6 +119,9 @@ The following arguments are optional: The `action_group_executor` configuration block supports the following arguments: +* `custom_control` - (Optional) Custom control method for handling the information elicited from the user. Valid values: `RETURN_CONTROL`. + + To skip using a Lambda function and instead return the predicted action group, in addition to the parameters and information required for it, in the `InvokeAgent` response, specify, specify `RETURN_CONTROL`. * `lambda` - (Optional) ARN of the Lambda function containing the business logic that is carried out upon invoking the action. ### `api_schema` Block From cb2d0efade22bb7bd55f873cb50d824a1f1e4bf0 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 18 Jul 2024 12:24:19 -0400 Subject: [PATCH 3/3] Run 'make fix-constants PKG=bedrockagent'. --- internal/service/bedrockagent/agent_action_group_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/service/bedrockagent/agent_action_group_test.go b/internal/service/bedrockagent/agent_action_group_test.go index 20c9f71baf8..317b3d647c2 100644 --- a/internal/service/bedrockagent/agent_action_group_test.go +++ b/internal/service/bedrockagent/agent_action_group_test.go @@ -147,10 +147,10 @@ func TestAccBedrockAgentAgentActionGroup_functionSchema(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.0.map_block_key", names.AttrMessage), resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.0.type", "string"), resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.0.description", "The Hello message"), - resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.0.required", "true"), + resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.0.required", acctest.CtTrue), resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.1.map_block_key", "unused"), resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.1.type", "integer"), - resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.1.required", "false"), + resource.TestCheckResourceAttr(resourceName, "function_schema.0.functions.0.parameters.1.required", acctest.CtFalse), ), }, {