Skip to content

Amazon.Lambda.AspNetCoreServer.Hosting as Bedrock Agent Action Group Lambda

0

I am building a AWS Bedrock Agent with a Action Group using a OpenAPI schema to talk to my Lambda function, which is hosting a .NET Core Web API application, which is built using Amazon.Lambda.AspNetCoreServer.Hosting nuget package, and deployed with CloudFormation with an API Gateway. I can access and use the api via its generated url without a problem, and I can use the Test feature in the Lambda UI to test the endpoints aswell.

Agents use a specific JSON input to its Lambda functions, specified at the following link: https://docs.aws.amazon.com/bedrock/latest/userguide/agents-lambda.html#agents-lambda-input

This format is not compatible with API Gateway requests, that the AspNetCoreServer.Hosting nuget uses. Link: https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html

The key differences are:

  • apiPath <-> path
  • parameters <-> queryStringParameters (Atleast for my simple usecase using query strings. Any parameters are imcompatible)

Either I am missing some key information, or this is an unsupported use-case. I'm sure its possible to handle this with normal function declarations in the Action Group, and normal .NET Core Lambda functions, but that kind of nullifies the usefulness of using OpenAPI as the schema declaration (Which .NET Core Web API can generate automatically), implemented by a real API, hosted in a lambda.

Here is the error that the Lambda returns when Agent does its request:

{
  "errorType": "NullReferenceException",
  "errorMessage": "Object reference not set to an instance of an object.",
  "stackTrace": [
    "at Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction.MarshallRequest(InvokeFeatures features, APIGatewayProxyRequest apiGatewayRequest, ILambdaContext lambdaContext)",
    "at Amazon.Lambda.AspNetCoreServer.AbstractAspNetCoreFunction`2.FunctionHandlerAsync(TREQUEST request, ILambdaContext lambdaContext)",
    "at Amazon.Lambda.RuntimeSupport.HandlerWrapper.<>c__DisplayClass26_0`2.<<GetHandlerWrapper>b__0>d.MoveNext()",
    "--- End of stack trace from previous location ---",
    "at Amazon.Lambda.RuntimeSupport.LambdaBootstrap.InvokeOnceAsync(CancellationToken cancellationToken)"
  ]
}

Here is the code in my Program.cs:

builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi);

Here is one of my endpoints, using Minimal API:

app.MapGet("/api/employees/search", (string term) =>
{
	// ...
})
.WithName("SearchEmployeesByName")
.WithOpenApi(operation =>
{
	operation.Summary = "Search for employees by name";
	operation.Description = "Returns employees whose names contain the search string (case-insensitive)";
	operation.Tags = new List<OpenApiTag> { new() { Name = "Employees" } };
	operation.Parameters[0].Description = "Name or partial name to search for";
	return operation;
});

Here is my generated OpenAPI spec:

{
  "openapi": "3.0.1",
  "info": {
    "title": "Agda.NetCore.AgdaAssistant.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
    "version": "1.0"
  },
  "paths": {
    "/api/employees/search": {
      "get": {
        "tags": [
          "Employees"
        ],
        "summary": "Search for employees by name",
        "description": "Returns employees whose names contain the search string (case-insensitive)",
        "operationId": "SearchEmployeesByName",
        "parameters": [
          {
            "name": "term",
            "in": "query",
            "description": "Name or partial name to search for",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  }
}

Here is my CF template:

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Transform": "AWS::Serverless-2016-10-31",
  "Description": "An AWS Serverless Application that uses the ASP.NET Core framework running in Amazon Lambda.",
  "Parameters": {},
  "Conditions": {},
  "Resources": {
    "AspNetCoreFunction": {
      "Type": "AWS::Serverless::Function",
      "Properties": {
        "Handler": "Agda.NetCore.AgdaAssistant.Api",
        "Runtime": "dotnet8",
        "CodeUri": "",
        "MemorySize": 512,
        "Timeout": 30,
        "Role": null,
        "Policies": [
          "AWSLambda_FullAccess"
        ],
        "Environment": {
          "Variables": {
            "DOTNET_ENVIRONMENT": "Development",
            "LAMBDA_NET_SERIALIZER_DEBUG": "true"
          }
        },
        "Events": {
          "ProxyResource": {
            "Type": "Api",
            "Properties": {
              "Path": "/{proxy+}",
              "Method": "ANY"
            }
          },
          "RootResource": {
            "Type": "Api",
            "Properties": {
              "Path": "/",
              "Method": "ANY"
            }
          }
        }
      }
    }
  },
  "Outputs": {
    "ApiURL": {
      "Description": "API endpoint URL for Prod environment",
      "Value": {
        "Fn::Sub": "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
      }
    }
  }
}
1 Answer
0

It appears that you're facing a compatibility issue between the input format expected by AWS Bedrock Agents and the format used by API Gateway when integrated with Lambda functions hosting ASP.NET Core applications.

The Amazon.Lambda.AspNetCoreServer.Hosting package is designed to work with API Gateway's input format, which differs from the format used by Bedrock Agents. This discrepancy makes it challenging to use the same Lambda function for both API Gateway and Bedrock Agent integrations without additional handling.

To resolve this issue, you have a few options:

  1. Custom Request Handling: You could create a custom middleware or handler in your ASP.NET Core application that detects the input format and translates the Bedrock Agent format to match what your API expects. This would involve parsing the incoming JSON, extracting the relevant information, and reconstructing the request in a format your API can understand.

  2. Separate Lambda Functions: You could maintain two separate Lambda functions - one for API Gateway using Amazon.Lambda.AspNetCoreServer.Hosting, and another specifically for Bedrock Agent integration that doesn't use this package. The Bedrock Agent function would then call your API Gateway endpoint internally.

  3. API Gateway as Intermediary: You could set up your Bedrock Agent to call your API through API Gateway instead of directly invoking the Lambda function. This would allow you to keep your existing setup intact, but it adds an extra hop in the request flow.

  4. Custom Lambda Function for Bedrock: Create a new Lambda function specifically for Bedrock Agent integration that acts as a translator. This function would receive the Bedrock Agent format, transform it into the format expected by your API, call your API, and then transform the response back to what Bedrock Agent expects.

Given your desire to leverage the OpenAPI schema and maintain a real API hosted in Lambda, option 1 or 4 might be the most suitable. These approaches allow you to keep your existing API structure while adding the necessary logic to handle Bedrock Agent requests.

Remember that whichever approach you choose, you'll need to ensure that the security and permissions are properly set up for the communication between Bedrock Agent, Lambda, and API Gateway as needed.
Sources
Deploy ASP.NET applications - AWS Lambda
Invoking a Lambda function using an Amazon API Gateway endpoint - AWS Lambda
Set up Lambda proxy integration for API Gateway using the AWS CLI - Amazon API Gateway

answered 9 months ago
EXPERT
reviewed 9 months ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.