From ecc2470a395eeaab248100c242ac9d87858a4e3b Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Tue, 27 May 2025 11:32:46 +0200 Subject: [PATCH 1/7] chore(event-handler): align implementation --- .../BedrockAgentFunctionResolver.ts | 155 ++++++++++------- .../bedrock-agent/BedrockFunctionResponse.ts | 99 +++++++++++ .../event-handler/src/types/bedrock-agent.ts | 57 ++++--- .../BedrockAgentFunctionResolver.test.ts | 156 +++++++++--------- 4 files changed, 302 insertions(+), 165 deletions(-) create mode 100644 packages/event-handler/src/bedrock-agent/BedrockFunctionResponse.ts diff --git a/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts b/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts index 93b30f0ec..b9d3cedd4 100644 --- a/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts +++ b/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts @@ -1,25 +1,34 @@ -import { EnvironmentVariablesService } from '@aws-lambda-powertools/commons'; +import { isNullOrUndefined } from '@aws-lambda-powertools/commons/typeutils'; +import { getStringFromEnv } from '@aws-lambda-powertools/commons/utils/env'; import type { Context } from 'aws-lambda'; import type { BedrockAgentFunctionResponse, Configuration, ParameterValue, ResolverOptions, - ResponseOptions, Tool, ToolFunction, } from '../types/bedrock-agent.js'; import type { GenericLogger } from '../types/common.js'; +import { BedrockFunctionResponse } from './BedrockFunctionResponse.js'; import { assertBedrockAgentFunctionEvent } from './utils.js'; +/** + * Resolver for AWS Bedrock Agent Function invocations. + * + * This resolver is designed to handle function invocations from Bedrock Agents. + * + * + */ export class BedrockAgentFunctionResolver { readonly #tools: Map = new Map(); - readonly #envService: EnvironmentVariablesService; readonly #logger: Pick; constructor(options?: ResolverOptions) { - this.#envService = new EnvironmentVariablesService(); - const alcLogLevel = this.#envService.get('AWS_LAMBDA_LOG_LEVEL'); + const alcLogLevel = getStringFromEnv({ + key: 'AWS_LAMBDA_LOG_LEVEL', + defaultValue: '', + }); this.#logger = options?.logger ?? { debug: alcLogLevel === 'DEBUG' ? console.debug : () => {}, error: console.error, @@ -34,7 +43,9 @@ export class BedrockAgentFunctionResolver { * * @example * ```ts - * import { BedrockAgentFunctionResolver } from '@aws-lambda-powertools/event-handler/bedrock-agent-function'; + * import { + * BedrockAgentFunctionResolver + * } from '@aws-lambda-powertools/event-handler/bedrock-agent'; * * const app = new BedrockAgentFunctionResolver(); * @@ -50,11 +61,36 @@ export class BedrockAgentFunctionResolver { * app.resolve(event, context); * ``` * + * If you know the function signature, you can also use a type parameter to specify the parameters of the tool function: + * + * @example + * ```ts + * import { + * BedrockAgentFunctionResolver, + * } from '@aws-lambda-powertools/event-handler/bedrock-agent'; + * + * const app = new BedrockAgentFunctionResolver(); + * + * app.tool<{ name: string }>(async (params) => { + * const { name } = params; + * // ^ name: string + * return `Hello, ${name}!`; + * }, { + * name: 'greeting', + * description: 'Greets a person by name', + * }); + * + * export const handler = async (event, context) => + * app.resolve(event, context); + * ``` + * * The method also works as a class method decorator: * * @example * ```ts - * import { BedrockAgentFunctionResolver } from '@aws-lambda-powertools/event-handler/bedrock-agent-function'; + * import { + * BedrockAgentFunctionResolver + * } from '@aws-lambda-powertools/event-handler/bedrock-agent'; * * const app = new BedrockAgentFunctionResolver(); * @@ -74,8 +110,38 @@ export class BedrockAgentFunctionResolver { * export const handler = lambda.handler.bind(lambda); * ``` * + * When defining a tool, you can also access the original `event` and `context` objects from the Bedrock Agent function invocation. + * This is useful if you need to access the session attributes or other context-specific information. + * + * @example + * ```ts + * import { + * BedrockAgentFunctionResolver + * } from '@aws-lambda-powertools/event-handler/bedrock-agent'; + * + * const app = new BedrockAgentFunctionResolver(); + * + * app.tool(async (params, { event, context }) => { + * const { name } = params; + * // Access session attributes from the event + * const sessionAttributes = event.sessionAttributes || {}; + * // You can also access the context if needed + * sessionAttributes.requestId = context.awsRequestId; + * + * return `Hello, ${name}!`; + * }, { + * name: 'greetingWithContext', + * description: 'Greets a person by name', + * }); + * + * export const handler = async (event, context) => + * app.resolve(event, context); + * ``` + * * @param fn - The tool function * @param config - The configuration object for the tool + * @param config.name - The name of the tool, which must be unique across all registered tools. + * @param config.description - A description of the tool, which is optional but highly recommended. */ public tool>( fn: ToolFunction, @@ -107,17 +173,9 @@ export class BedrockAgentFunctionResolver { config: Configuration ): void { const { name } = config; - - if (this.#tools.size >= 5) { - this.#logger.warn( - `The maximum number of tools that can be registered is 5. Tool ${name} will not be registered.` - ); - return; - } - if (this.#tools.has(name)) { this.#logger.warn( - `Tool ${name} already registered. Overwriting with new definition.` + `Tool "${name}" already registered. Overwriting with new definition.` ); } @@ -125,36 +183,7 @@ export class BedrockAgentFunctionResolver { handler: handler as ToolFunction, config, }); - this.#logger.debug(`Tool ${name} has been registered.`); - } - - #buildResponse(options: ResponseOptions): BedrockAgentFunctionResponse { - const { - actionGroup, - function: func, - body, - errorType, - sessionAttributes, - promptSessionAttributes, - } = options; - - return { - messageVersion: '1.0', - response: { - actionGroup, - function: func, - functionResponse: { - responseState: errorType, - responseBody: { - TEXT: { - body, - }, - }, - }, - }, - sessionAttributes, - promptSessionAttributes, - }; + this.#logger.debug(`Tool "${name}" has been registered.`); } async resolve( @@ -175,11 +204,13 @@ export class BedrockAgentFunctionResolver { if (tool == null) { this.#logger.error(`Tool ${toolName} has not been registered.`); - return this.#buildResponse({ + return new BedrockFunctionResponse({ actionGroup, - function: toolName, - body: 'Error: tool has not been registered in handler.', - }); + func: toolName, + body: `Error: tool ${toolName} has not been registered.`, + sessionAttributes, + promptSessionAttributes, + }).build(); } const toolParams: Record = {}; @@ -204,24 +235,30 @@ export class BedrockAgentFunctionResolver { } try { - const res = await tool.handler(toolParams, { event, context }); - const body = res == null ? '' : JSON.stringify(res); - return this.#buildResponse({ + const response = await tool.handler(toolParams, { event, context }); + if (response instanceof BedrockFunctionResponse) { + return response.build(); + } + const body = + isNullOrUndefined(response) || response === '' + ? '' + : JSON.stringify(response); + return new BedrockFunctionResponse({ actionGroup, - function: toolName, + func: toolName, body, sessionAttributes, promptSessionAttributes, - }); + }).build(); } catch (error) { this.#logger.error(`An error occurred in tool ${toolName}.`, error); - return this.#buildResponse({ + return new BedrockFunctionResponse({ actionGroup, - function: toolName, - body: `Error when invoking tool: ${error}`, + func: toolName, + body: `Unable to complete tool execution due to ${error instanceof Error ? `${error.name} - ${error.message}` : String(error)}`, sessionAttributes, promptSessionAttributes, - }); + }).build(); } } } diff --git a/packages/event-handler/src/bedrock-agent/BedrockFunctionResponse.ts b/packages/event-handler/src/bedrock-agent/BedrockFunctionResponse.ts new file mode 100644 index 000000000..f53bd1aec --- /dev/null +++ b/packages/event-handler/src/bedrock-agent/BedrockFunctionResponse.ts @@ -0,0 +1,99 @@ +import type { BedrockAgentFunctionResolver } from './BedrockAgentFunctionResolver.js'; + +/** + * Class representing a response from a Bedrock agent function. + * + * You can use this class to customize the response sent back to the Bedrock agent with additional fields like: + * - session attributes + * - prompt session attributes + * - response state (`FAILURE` or `REPROMPT`) + * + * When working with the {@link BedrockAgentFunctionResolver} class, this is built automatically + * when you return anything from your function handler other than an instance of this class. + */ +class BedrockFunctionResponse { + /** + * The name of the action group, this comes from the `event.actionGroup` field + * in the Bedrock agent function event. + */ + readonly actionGroup: string; + /** + * The name of the function returning the response, this comes from the `event.function` field + * in the Bedrock agent function event. + */ + readonly func: string; + /** + * The response object that defines the response from execution of the function. + */ + readonly body: string; + /** + * Optional field to indicate the whether the response is a failure or a reprompt. + * If not provided, the default is undefined, which means no specific response state is set. + * + * - `FAILURE`: The agent throws a `DependencyFailedException` for the current session. + * - `REPROMPT`: The agent passes a response string to the model to reprompt it. + */ + readonly responseState?: 'FAILURE' | 'REPROMPT'; + /** + * Optional field to store session attributes and their values. + * @see {@link https://docs.aws.amazon.com/bedrock/latest/userguide/agents-session-state.html#session-state-attributes | Bedrock Agent Session State Attributes} for more details. + */ + readonly sessionAttributes: Record; + /** + * Optional field to instruct the agent to prompt attributes and their values. + * @see {@link https://docs.aws.amazon.com/bedrock/latest/userguide/agents-session-state.html#session-state-attributes | Bedrock Agent Session State Attributes} for more details. + */ + readonly promptSessionAttributes: Record; + + constructor({ + actionGroup, + func, + body, + responseState = undefined, + sessionAttributes = {}, + promptSessionAttributes = {}, + }: { + actionGroup: string; + func: string; + body: string; + responseState?: 'FAILURE' | 'REPROMPT'; + sessionAttributes?: Record; + promptSessionAttributes?: Record; + }) { + this.actionGroup = actionGroup; + this.func = func; + this.body = body; + this.responseState = responseState; + this.sessionAttributes = sessionAttributes; + this.promptSessionAttributes = promptSessionAttributes; + } + + /** + * Builds the Bedrock function response object according to the Bedrock agent function {@link https://docs.aws.amazon.com/bedrock/latest/userguide/agents-lambda.html#agents-lambda-response | response format}. + */ + build() { + return { + messageVersion: '1.0', + response: { + actionGroup: this.actionGroup, + function: this.func, + functionResponse: { + ...(this.responseState && { responseState: this.responseState }), + responseBody: { + TEXT: { + body: this.body, + }, + }, + }, + }, + ...(this.sessionAttributes && { + sessionAttributes: this.sessionAttributes, + }), + ...(this.promptSessionAttributes && { + promptSessionAttributes: this.promptSessionAttributes, + }), + }; + } +} + +export { BedrockFunctionResponse }; diff --git a/packages/event-handler/src/types/bedrock-agent.ts b/packages/event-handler/src/types/bedrock-agent.ts index f70763688..6b77557ef 100644 --- a/packages/event-handler/src/types/bedrock-agent.ts +++ b/packages/event-handler/src/types/bedrock-agent.ts @@ -1,5 +1,7 @@ import type { JSONValue } from '@aws-lambda-powertools/commons/types'; import type { Context } from 'aws-lambda'; +import type { BedrockAgentFunctionResolver } from '../bedrock-agent/BedrockAgentFunctionResolver.js'; +import type { BedrockFunctionResponse } from '../bedrock-agent/BedrockFunctionResponse.js'; import type { GenericLogger } from '../types/common.js'; type Configuration = { @@ -23,23 +25,30 @@ type ToolFunction> = ( event?: BedrockAgentFunctionEvent; context?: Context; } -) => Promise; +) => Promise; type Tool> = { handler: ToolFunction; config: Configuration; }; -type FunctionIdentifier = { +type FunctionInvocation = { actionGroup: string; function: string; -}; - -type FunctionInvocation = FunctionIdentifier & { parameters?: Array; }; -type BedrockAgentFunctionEvent = FunctionInvocation & { +/** + * Event structure for Bedrock Agent Function invocations. + * + * @example + * ```json + * + * ``` + */ +type BedrockAgentFunctionEvent = { + actionGroup: string; + function: string; messageVersion: string; agent: { name: string; @@ -47,41 +56,33 @@ type BedrockAgentFunctionEvent = FunctionInvocation & { alias: string; version: string; }; + parameters?: Array; inputText: string; sessionId: string; sessionAttributes: Record; promptSessionAttributes: Record; }; -type ResponseState = 'ERROR' | 'REPROMPT'; - -type TextResponseBody = { - TEXT: { - body: string; - }; -}; - -type SessionData = { - sessionAttributes?: Record; - promptSessionAttributes?: Record; -}; +type ResponseState = 'FAILURE' | 'REPROMPT'; -type BedrockAgentFunctionResponse = SessionData & { +type BedrockAgentFunctionResponse = { messageVersion: string; - response: FunctionIdentifier & { + response: { + actionGroup: string; + function: string; functionResponse: { responseState?: ResponseState; - responseBody: TextResponseBody; + responseBody: { + TEXT: { + body: string; + }; + }; }; }; + sessionAttributes?: Record; + promptSessionAttributes?: Record; }; -type ResponseOptions = FunctionIdentifier & - SessionData & { - body: string; - errorType?: ResponseState; - }; - /** * Options for the {@link BedrockAgentFunctionResolver} class */ @@ -100,10 +101,8 @@ export type { ToolFunction, Parameter, ParameterValue, - FunctionIdentifier, FunctionInvocation, BedrockAgentFunctionEvent, BedrockAgentFunctionResponse, - ResponseOptions, ResolverOptions, }; diff --git a/packages/event-handler/tests/unit/bedrock-agent/BedrockAgentFunctionResolver.test.ts b/packages/event-handler/tests/unit/bedrock-agent/BedrockAgentFunctionResolver.test.ts index f0d929a66..f9c602a72 100644 --- a/packages/event-handler/tests/unit/bedrock-agent/BedrockAgentFunctionResolver.test.ts +++ b/packages/event-handler/tests/unit/bedrock-agent/BedrockAgentFunctionResolver.test.ts @@ -1,13 +1,14 @@ import context from '@aws-lambda-powertools/testing-utils/context'; import type { Context } from 'aws-lambda'; import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { BedrockFunctionResponse } from '../../../src/bedrock-agent/BedrockFunctionResponse.js'; import { BedrockAgentFunctionResolver } from '../../../src/bedrock-agent/index.js'; import type { BedrockAgentFunctionEvent, Configuration, Parameter, ToolFunction, -} from '../../../src/types/bedrock-agent'; +} from '../../../src/types/bedrock-agent.js'; function createEvent(functionName: string, parameters?: Parameter[]) { return { @@ -115,58 +116,6 @@ describe('Class: BedrockAgentFunctionResolver', () => { expect(console.debug).toHaveBeenCalled(); }); - it('only allows five tools to be registered', async () => { - // Prepare - const app = new BedrockAgentFunctionResolver(); - - for (const num of [1, 2, 3, 4, 5]) { - app.tool( - async (params: { arg: string }) => { - return params.arg; - }, - { - name: `identity${num}`, - description: 'Returns its arg', - } - ); - } - - app.tool( - async (params: { a: number; b: number }) => { - return params.a + params.b; - }, - { - name: 'mult', - description: 'Multiplies two numbers', - } - ); - - const event = createEvent('mult', [ - { - name: 'a', - type: 'number', - value: '1', - }, - { - name: 'b', - type: 'number', - value: '2', - }, - ]); - - // Act - const actual = await app.resolve(event, context); - - // Assess - expect(console.warn).toHaveBeenLastCalledWith( - 'The maximum number of tools that can be registered is 5. Tool mult will not be registered.' - ); - expect(actual.response.function).toEqual('mult'); - expect(actual.response.functionResponse.responseBody.TEXT.body).toEqual( - 'Error: tool has not been registered in handler.' - ); - }); - it('overwrites tools with the same name and uses the latest definition', async () => { // Prepare const app = new BedrockAgentFunctionResolver(); @@ -400,6 +349,13 @@ describe('Class: BedrockAgentFunctionResolver', () => { description: 'Returns undefined', }, }, + { + toolFunction: async () => '', + toolParams: { + name: 'empty-string', + description: 'Returns empty string', + }, + }, ])( 'handles functions that return $toolParams.name by returning an empty string', async ({ toolFunction, toolParams }) => { @@ -419,6 +375,37 @@ describe('Class: BedrockAgentFunctionResolver', () => { } ); + it('handles functions that return a BedrockAgentFunctionResponse', async () => { + // Prepare + const app = new BedrockAgentFunctionResolver(); + + app.tool( + async () => { + return new BedrockFunctionResponse({ + actionGroup: 'customActionGroup', + func: 'custom-response', + body: 'I am not sure', + responseState: 'REPROMPT', + sessionAttributes: { customAttr: 'value' }, + promptSessionAttributes: { customPromptAttr: 'promptValue' }, + }); + }, + { + name: 'custom-response', + description: 'Returns a custom BedrockAgentFunctionResponse', + } + ); + + // Act + const result = await app.resolve(createEvent('custom-response'), context); + + // Assess + expect(result.response.function).toEqual('custom-response'); + expect(result.response.functionResponse.responseBody.TEXT.body).toEqual( + 'I am not sure' + ); + }); + it('correctly parses boolean parameters', async () => { // Prepare const toolFunction: ToolFunction<{ arg: boolean }> = async ( @@ -579,33 +566,48 @@ describe('Class: BedrockAgentFunctionResolver', () => { ); }); - it('handles functions that throw errors', async () => { - // Prepare - const app = new BedrockAgentFunctionResolver(); + it.each([ + { + label: 'actual error', + toThrow: new Error('Something went wrong'), + expected: + 'Unable to complete tool execution due to Error - Something went wrong', + }, + { + label: 'string', + toThrow: 'Something went wrong', + expected: 'Unable to complete tool execution due to Something went wrong', + }, + ])( + 'handles functions that throw errors $label', + async ({ toThrow, expected }) => { + // Prepare + const app = new BedrockAgentFunctionResolver(); - app.tool( - async (_params, _options) => { - throw new Error('Something went wrong'); - }, - { - name: 'error-tool', - description: 'Throws an error', - } - ); + app.tool( + async (_params, _options) => { + throw toThrow; + }, + { + name: 'error-tool', + description: 'Throws an error', + } + ); - // Act - const actual = await app.resolve(createEvent('error-tool', []), context); + // Act + const actual = await app.resolve(createEvent('error-tool', []), context); - // Assess - expect(actual.response.function).toEqual('error-tool'); - expect(actual.response.functionResponse.responseBody.TEXT.body).toEqual( - 'Error when invoking tool: Error: Something went wrong' - ); - expect(console.error).toHaveBeenCalledWith( - 'An error occurred in tool error-tool.', - new Error('Something went wrong') - ); - }); + // Assess + expect(actual.response.function).toEqual('error-tool'); + expect(actual.response.functionResponse.responseBody.TEXT.body).toEqual( + expected + ); + expect(console.error).toHaveBeenCalledWith( + 'An error occurred in tool error-tool.', + new Error('Something went wrong') + ); + } + ); it('returns a fully structured BedrockAgentFunctionResponse', async () => { // Prepare From d7c233bb62e42079ed4aed25ca727827062463d0 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Wed, 28 May 2025 13:30:57 +0200 Subject: [PATCH 2/7] chore: implementation --- .../BedrockAgentFunctionResolver.ts | 47 +++++++++++++++++-- .../bedrock-agent/BedrockFunctionResponse.ts | 25 ++++------ .../event-handler/src/bedrock-agent/index.ts | 1 + 3 files changed, 54 insertions(+), 19 deletions(-) diff --git a/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts b/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts index b9d3cedd4..a3687600d 100644 --- a/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts +++ b/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts @@ -18,10 +18,36 @@ import { assertBedrockAgentFunctionEvent } from './utils.js'; * * This resolver is designed to handle function invocations from Bedrock Agents. * + * @example + * ```ts + * import { + * BedrockAgentFunctionResolver + * } from '@aws-lambda-powertools/event-handler/bedrock-agent'; * + * const app = new BedrockAgentFunctionResolver(); + * + * app.tool(async (params) => { + * const { name } = params; + * return `Hello, ${name}!`; + * }, { + * name: 'greeting', + * description: 'Greets a person by name', + * }); + * + * export const handler = async (event, context) => + * app.resolve(event, context); + * ``` */ export class BedrockAgentFunctionResolver { + /** + * Registry of tools added to the Bedrock Agent Function Resolver. + */ readonly #tools: Map = new Map(); + /** + * A logger instance to be used for logging debug, warning, and error messages. + * + * When no logger is provided, we'll only log warnings and errors using the global `console` object. + */ readonly #logger: Pick; constructor(options?: ResolverOptions) { @@ -198,6 +224,7 @@ export class BedrockAgentFunctionResolver { actionGroup, sessionAttributes, promptSessionAttributes, + // TODO: add knowledge bases } = event; const tool = this.#tools.get(toolName); @@ -210,7 +237,10 @@ export class BedrockAgentFunctionResolver { body: `Error: tool ${toolName} has not been registered.`, sessionAttributes, promptSessionAttributes, - }).build(); + }).build({ + actionGroup, + func: toolName, + }); } const toolParams: Record = {}; @@ -237,7 +267,10 @@ export class BedrockAgentFunctionResolver { try { const response = await tool.handler(toolParams, { event, context }); if (response instanceof BedrockFunctionResponse) { - return response.build(); + const res = response.build({ + actionGroup, + func: toolName, + }); } const body = isNullOrUndefined(response) || response === '' @@ -249,7 +282,10 @@ export class BedrockAgentFunctionResolver { body, sessionAttributes, promptSessionAttributes, - }).build(); + }).build({ + actionGroup, + func: toolName, + }); } catch (error) { this.#logger.error(`An error occurred in tool ${toolName}.`, error); return new BedrockFunctionResponse({ @@ -258,7 +294,10 @@ export class BedrockAgentFunctionResolver { body: `Unable to complete tool execution due to ${error instanceof Error ? `${error.name} - ${error.message}` : String(error)}`, sessionAttributes, promptSessionAttributes, - }).build(); + }).build({ + actionGroup, + func: toolName, + }); } } } diff --git a/packages/event-handler/src/bedrock-agent/BedrockFunctionResponse.ts b/packages/event-handler/src/bedrock-agent/BedrockFunctionResponse.ts index f53bd1aec..c43d88531 100644 --- a/packages/event-handler/src/bedrock-agent/BedrockFunctionResponse.ts +++ b/packages/event-handler/src/bedrock-agent/BedrockFunctionResponse.ts @@ -12,16 +12,6 @@ import type { BedrockAgentFunctionResolver } from './BedrockAgentFunctionResolve * when you return anything from your function handler other than an instance of this class. */ class BedrockFunctionResponse { - /** - * The name of the action group, this comes from the `event.actionGroup` field - * in the Bedrock agent function event. - */ - readonly actionGroup: string; - /** - * The name of the function returning the response, this comes from the `event.function` field - * in the Bedrock agent function event. - */ - readonly func: string; /** * The response object that defines the response from execution of the function. */ @@ -60,8 +50,6 @@ class BedrockFunctionResponse { sessionAttributes?: Record; promptSessionAttributes?: Record; }) { - this.actionGroup = actionGroup; - this.func = func; this.body = body; this.responseState = responseState; this.sessionAttributes = sessionAttributes; @@ -70,13 +58,20 @@ class BedrockFunctionResponse { /** * Builds the Bedrock function response object according to the Bedrock agent function {@link https://docs.aws.amazon.com/bedrock/latest/userguide/agents-lambda.html#agents-lambda-response | response format}. + * + * @param options - The options for building the response. + * @param options.actionGroup - The action group of the function, this comes from the `event.actionGroup` field in the Bedrock agent function event. + * @param options.func - The name of the function being invoked by the agent, this comes from the `event.function` field in the Bedrock agent function event. */ - build() { + build(options: { + actionGroup: string; + func: string; + }) { return { messageVersion: '1.0', response: { - actionGroup: this.actionGroup, - function: this.func, + actionGroup: options.actionGroup, + function: options.func, functionResponse: { ...(this.responseState && { responseState: this.responseState }), responseBody: { diff --git a/packages/event-handler/src/bedrock-agent/index.ts b/packages/event-handler/src/bedrock-agent/index.ts index a18e9dd72..1c8717f22 100644 --- a/packages/event-handler/src/bedrock-agent/index.ts +++ b/packages/event-handler/src/bedrock-agent/index.ts @@ -1 +1,2 @@ export { BedrockAgentFunctionResolver } from './BedrockAgentFunctionResolver.js'; +export { BedrockFunctionResponse } from './BedrockFunctionResponse.js'; From 310802dcc20a37d999da1750d803f602ebccbec3 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Wed, 28 May 2025 16:35:28 +0200 Subject: [PATCH 3/7] chore: remove decorator usage --- .../BedrockAgentFunctionResolver.ts | 51 ++----------------- .../BedrockAgentFunctionResolver.test.ts | 46 ----------------- 2 files changed, 4 insertions(+), 93 deletions(-) diff --git a/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts b/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts index a3687600d..48982cc0f 100644 --- a/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts +++ b/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts @@ -110,32 +110,6 @@ export class BedrockAgentFunctionResolver { * app.resolve(event, context); * ``` * - * The method also works as a class method decorator: - * - * @example - * ```ts - * import { - * BedrockAgentFunctionResolver - * } from '@aws-lambda-powertools/event-handler/bedrock-agent'; - * - * const app = new BedrockAgentFunctionResolver(); - * - * class Lambda { - * @app.tool({ name: 'greeting', description: 'Greets a person by name' }) - * async greeting(params) { - * const { name } = params; - * return `Hello, ${name}!`; - * } - * - * async handler(event, context) { - * return app.resolve(event, context); - * } - * } - * - * const lambda = new Lambda(); - * export const handler = lambda.handler.bind(lambda); - * ``` - * * When defining a tool, you can also access the original `event` and `context` objects from the Bedrock Agent function invocation. * This is useful if you need to access the session attributes or other context-specific information. * @@ -172,26 +146,9 @@ export class BedrockAgentFunctionResolver { public tool>( fn: ToolFunction, config: Configuration - ): undefined; - public tool>( - config: Configuration - ): MethodDecorator; - public tool>( - fnOrConfig: ToolFunction | Configuration, - config?: Configuration - ): MethodDecorator | undefined { - // When used as a method (not a decorator) - if (typeof fnOrConfig === 'function') { - this.#registerTool(fnOrConfig, config as Configuration); - return; - } - - // When used as a decorator - return (_target, _propertyKey, descriptor: PropertyDescriptor) => { - const toolFn = descriptor.value as ToolFunction; - this.#registerTool(toolFn, fnOrConfig); - return descriptor; - }; + ): undefined { + this.#registerTool(fn, config); + return; } #registerTool>( @@ -267,7 +224,7 @@ export class BedrockAgentFunctionResolver { try { const response = await tool.handler(toolParams, { event, context }); if (response instanceof BedrockFunctionResponse) { - const res = response.build({ + return response.build({ actionGroup, func: toolName, }); diff --git a/packages/event-handler/tests/unit/bedrock-agent/BedrockAgentFunctionResolver.test.ts b/packages/event-handler/tests/unit/bedrock-agent/BedrockAgentFunctionResolver.test.ts index f9c602a72..f8c100db3 100644 --- a/packages/event-handler/tests/unit/bedrock-agent/BedrockAgentFunctionResolver.test.ts +++ b/packages/event-handler/tests/unit/bedrock-agent/BedrockAgentFunctionResolver.test.ts @@ -243,52 +243,6 @@ describe('Class: BedrockAgentFunctionResolver', () => { ); }); - it('can be invoked using the decorator pattern', async () => { - // Prepare - const app = new BedrockAgentFunctionResolver(); - - class Lambda { - @app.tool({ name: 'hello', description: 'Says hello' }) - async helloWorld() { - return 'Hello, world!'; - } - - @app.tool({ name: 'add', description: 'Adds two numbers' }) - async add(params: { a: number; b: number }) { - const { a, b } = params; - return a + b; - } - - public async handler(event: BedrockAgentFunctionEvent, context: Context) { - return app.resolve(event, context); - } - } - - const lambda = new Lambda(); - - const addEvent = createEvent('add', [ - { - name: 'a', - type: 'number', - value: '1', - }, - { - name: 'b', - type: 'number', - value: '2', - }, - ]); - - // Act - const actual = await lambda.handler(addEvent, context); - - // Assess - expect(actual.response.function).toEqual('add'); - expect(actual.response.functionResponse.responseBody.TEXT.body).toEqual( - '3' - ); - }); - it.each([ { toolFunction: async () => ({ From 49b4c74378dc89a4b770f8c4633e1366a3f590c5 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Wed, 28 May 2025 17:43:21 +0200 Subject: [PATCH 4/7] chore: add docstrings --- packages/event-handler/README.md | 4 +- .../BedrockAgentFunctionResolver.ts | 37 ++++++++-- .../bedrock-agent/BedrockFunctionResponse.ts | 15 ++-- .../event-handler/src/types/bedrock-agent.ts | 70 ++++++++++++++++++- packages/event-handler/src/types/index.ts | 1 + .../BedrockAgentFunctionResolver.test.ts | 2 - packages/event-handler/typedoc.json | 1 + 7 files changed, 113 insertions(+), 17 deletions(-) diff --git a/packages/event-handler/README.md b/packages/event-handler/README.md index 3be14212f..c4350745d 100644 --- a/packages/event-handler/README.md +++ b/packages/event-handler/README.md @@ -6,7 +6,7 @@ You can use the library in both TypeScript and JavaScript code bases. ## Intro -Event handler for Amazon API Gateway REST and HTTP APIs, Application Loader Balancer (ALB), Lambda Function URLs, and VPC Lattice. +Event handler for Amazon API Gateway REST and HTTP APIs, Application Loader Balancer (ALB), Lambda Function URLs, VPC Lattice, AWS AppSync Events APIs, and Amazon Bedrock Agent Functions. ## Usage @@ -102,6 +102,8 @@ export const handler = async (event, context) => app.resolve(event, context); ``` +## Bedrock Agent Functions + See the [documentation](https://docs.powertools.aws.dev/lambda/typescript/latest/features/event-handler/appsync-events) for more details on how to use the AppSync event handler. ## Contribute diff --git a/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts b/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts index 48982cc0f..63329ac23 100644 --- a/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts +++ b/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts @@ -169,6 +169,32 @@ export class BedrockAgentFunctionResolver { this.#logger.debug(`Tool "${name}" has been registered.`); } + /** + * Resolve an incoming Bedrock Agent function invocation event. + * + * @example + * ```ts + * import { + * BedrockAgentFunctionResolver + * } from '@aws-lambda-powertools/event-handler/bedrock-agent'; + * + * const app = new BedrockAgentFunctionResolver(); + * + * app.tool(async (params) => { + * const { name } = params; + * return `Hello, ${name}!`; + * }, { + * name: 'greeting', + * description: 'Greets a person by name', + * }); + * + * export const handler = async (event, context) => + * app.resolve(event, context); + * ``` + * + * @param event - The incoming payload of the AWS Lambda function. + * @param context - The context object provided by AWS Lambda, which contains information about the invocation, function, and execution environment. + */ async resolve( event: unknown, context: Context @@ -181,7 +207,7 @@ export class BedrockAgentFunctionResolver { actionGroup, sessionAttributes, promptSessionAttributes, - // TODO: add knowledge bases + knowledgeBasesConfiguration, } = event; const tool = this.#tools.get(toolName); @@ -189,11 +215,10 @@ export class BedrockAgentFunctionResolver { if (tool == null) { this.#logger.error(`Tool ${toolName} has not been registered.`); return new BedrockFunctionResponse({ - actionGroup, - func: toolName, body: `Error: tool ${toolName} has not been registered.`, sessionAttributes, promptSessionAttributes, + knowledgeBasesConfiguration, }).build({ actionGroup, func: toolName, @@ -234,11 +259,10 @@ export class BedrockAgentFunctionResolver { ? '' : JSON.stringify(response); return new BedrockFunctionResponse({ - actionGroup, - func: toolName, body, sessionAttributes, promptSessionAttributes, + knowledgeBasesConfiguration, }).build({ actionGroup, func: toolName, @@ -246,11 +270,10 @@ export class BedrockAgentFunctionResolver { } catch (error) { this.#logger.error(`An error occurred in tool ${toolName}.`, error); return new BedrockFunctionResponse({ - actionGroup, - func: toolName, body: `Unable to complete tool execution due to ${error instanceof Error ? `${error.name} - ${error.message}` : String(error)}`, sessionAttributes, promptSessionAttributes, + knowledgeBasesConfiguration, }).build({ actionGroup, func: toolName, diff --git a/packages/event-handler/src/bedrock-agent/BedrockFunctionResponse.ts b/packages/event-handler/src/bedrock-agent/BedrockFunctionResponse.ts index c43d88531..dee32debd 100644 --- a/packages/event-handler/src/bedrock-agent/BedrockFunctionResponse.ts +++ b/packages/event-handler/src/bedrock-agent/BedrockFunctionResponse.ts @@ -34,26 +34,30 @@ class BedrockFunctionResponse { * @see {@link https://docs.aws.amazon.com/bedrock/latest/userguide/agents-session-state.html#session-state-attributes | Bedrock Agent Session State Attributes} for more details. */ readonly promptSessionAttributes: Record; + /** + * Optional field to configure knowledge bases for the agent. + * @see {@link https://docs.aws.amazon.com/bedrock/latest/userguide/agents-session-state.html#session-state-kb | Bedrock Agent Knowledge Bases} for more details. + */ + readonly knowledgeBasesConfiguration?: Record; constructor({ - actionGroup, - func, body, responseState = undefined, sessionAttributes = {}, promptSessionAttributes = {}, + knowledgeBasesConfiguration = {}, }: { - actionGroup: string; - func: string; body: string; responseState?: 'FAILURE' | 'REPROMPT'; sessionAttributes?: Record; promptSessionAttributes?: Record; + knowledgeBasesConfiguration?: Record; }) { this.body = body; this.responseState = responseState; this.sessionAttributes = sessionAttributes; this.promptSessionAttributes = promptSessionAttributes; + this.knowledgeBasesConfiguration = knowledgeBasesConfiguration; } /** @@ -87,6 +91,9 @@ class BedrockFunctionResponse { ...(this.promptSessionAttributes && { promptSessionAttributes: this.promptSessionAttributes, }), + ...(this.knowledgeBasesConfiguration && { + knowledgeBasesConfiguration: this.knowledgeBasesConfiguration, + }), }; } } diff --git a/packages/event-handler/src/types/bedrock-agent.ts b/packages/event-handler/src/types/bedrock-agent.ts index 6b77557ef..7f3b08610 100644 --- a/packages/event-handler/src/types/bedrock-agent.ts +++ b/packages/event-handler/src/types/bedrock-agent.ts @@ -4,21 +4,45 @@ import type { BedrockAgentFunctionResolver } from '../bedrock-agent/BedrockAgent import type { BedrockFunctionResponse } from '../bedrock-agent/BedrockFunctionResponse.js'; import type { GenericLogger } from '../types/common.js'; +/** + * Configuration for a tool in the Bedrock Agent Function Resolver. + */ type Configuration = { + /** + * The name of the tool, which must be unique across all registered tools. + */ name: string; - description: string; + /** + * A description of the tool, which is optional but highly recommended. + */ + description?: string; }; +/** + * Parameter for a tool function in the Bedrock Agent Function Resolver. + * This is used to define the structure of parameters in tool functions. + */ type Parameter = { name: string; type: 'string' | 'number' | 'integer' | 'boolean' | 'array'; value: string; }; +/** + * Primitive types that can be used as parameter values in tool functions. + * This is used to define the structure of parameters in tool functions. + */ type ParameterPrimitives = string | number | boolean; +/** + * Represents a value for a parameter, which can be a primitive type or an array of values. + * This is used to define the structure of parameters in tool functions. + */ type ParameterValue = ParameterPrimitives | Array; +/** + * Function to handle tool invocations in the Bedrock Agent Function Resolver. + */ type ToolFunction> = ( params: TParams, options?: { @@ -27,11 +51,21 @@ type ToolFunction> = ( } ) => Promise; +/** + * Tool in the Bedrock Agent Function Resolver. + * + * Used to register a tool in {@link BedrockAgentFunctionResolver | `BedrockAgentFunctionResolver`}. + */ type Tool> = { handler: ToolFunction; config: Configuration; }; +/** + * Function invocation in the Bedrock Agent Function Resolver. + * + * This is used to define the structure of function invocations in tool functions. + */ type FunctionInvocation = { actionGroup: string; function: string; @@ -43,7 +77,28 @@ type FunctionInvocation = { * * @example * ```json - * + * { + * "messageVersion": "1.0", + * "actionGroup": "exampleActionGroup", + * "function": "getWeather", + * "agent": { + * "name": "WeatherAgent", + * "id": "agent-id-123", + * "alias": "v1", + * "version": "1.0" + * }, + * "parameters": [{ + * "name": "location", + * "type": "string", + * "value": "Seattle" + * }], + * "inputText": "What's the weather like in Seattle?", + * "sessionId": "session-id-456", + * "sessionAttributes": { + * "userId": "user-789", + * }, + * "promptSessionAttributes": {}, + * } * ``` */ type BedrockAgentFunctionEvent = { @@ -61,10 +116,19 @@ type BedrockAgentFunctionEvent = { sessionId: string; sessionAttributes: Record; promptSessionAttributes: Record; + knowledgeBasesConfiguration?: Record; }; +/** + * Represents the state of the response from a Bedrock agent function: + * - `FAILURE`: The agent throws a `DependencyFailedException` for the current session. + * - `REPROMPT`: The agent passes a response string to the model to reprompt it. + */ type ResponseState = 'FAILURE' | 'REPROMPT'; +/** + * Response structure for a Bedrock agent function. + */ type BedrockAgentFunctionResponse = { messageVersion: string; response: { @@ -84,7 +148,7 @@ type BedrockAgentFunctionResponse = { }; /** - * Options for the {@link BedrockAgentFunctionResolver} class + * Options for the {@link BedrockAgentFunctionResolver | `BedrockAgentFunctionResolver`} class */ type ResolverOptions = { /** diff --git a/packages/event-handler/src/types/index.ts b/packages/event-handler/src/types/index.ts index eee1ee9f8..073a80a47 100644 --- a/packages/event-handler/src/types/index.ts +++ b/packages/event-handler/src/types/index.ts @@ -13,6 +13,7 @@ export type { BedrockAgentFunctionEvent, BedrockAgentFunctionResponse, ResolverOptions, + Parameter, } from './bedrock-agent.js'; export type { diff --git a/packages/event-handler/tests/unit/bedrock-agent/BedrockAgentFunctionResolver.test.ts b/packages/event-handler/tests/unit/bedrock-agent/BedrockAgentFunctionResolver.test.ts index f8c100db3..c42b8eafd 100644 --- a/packages/event-handler/tests/unit/bedrock-agent/BedrockAgentFunctionResolver.test.ts +++ b/packages/event-handler/tests/unit/bedrock-agent/BedrockAgentFunctionResolver.test.ts @@ -336,8 +336,6 @@ describe('Class: BedrockAgentFunctionResolver', () => { app.tool( async () => { return new BedrockFunctionResponse({ - actionGroup: 'customActionGroup', - func: 'custom-response', body: 'I am not sure', responseState: 'REPROMPT', sessionAttributes: { customAttr: 'value' }, diff --git a/packages/event-handler/typedoc.json b/packages/event-handler/typedoc.json index 2ce51a11f..35095bca8 100644 --- a/packages/event-handler/typedoc.json +++ b/packages/event-handler/typedoc.json @@ -4,6 +4,7 @@ ], "entryPoints": [ "./src/appsync-events/index.ts", + "./src/bedrock-agent/index.ts", "./src/types/index.ts", ], "readme": "README.md" From 77d5d90a8848fe4d835aa6bc02dc7f3332998717 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Wed, 28 May 2025 17:52:21 +0200 Subject: [PATCH 5/7] chore: tests and coverage --- .../bedrock-agent/BedrockFunctionResponse.ts | 23 +++++++++------- .../event-handler/src/types/bedrock-agent.ts | 12 +++++---- packages/event-handler/src/types/index.ts | 1 + .../BedrockAgentFunctionResolver.test.ts | 27 +++++++++---------- 4 files changed, 34 insertions(+), 29 deletions(-) diff --git a/packages/event-handler/src/bedrock-agent/BedrockFunctionResponse.ts b/packages/event-handler/src/bedrock-agent/BedrockFunctionResponse.ts index dee32debd..5656a6d6d 100644 --- a/packages/event-handler/src/bedrock-agent/BedrockFunctionResponse.ts +++ b/packages/event-handler/src/bedrock-agent/BedrockFunctionResponse.ts @@ -1,5 +1,8 @@ +import type { + BedrockAgentFunctionEvent, + ResponseState, +} from '../types/bedrock-agent.js'; import type { BedrockAgentFunctionResolver } from './BedrockAgentFunctionResolver.js'; - /** * Class representing a response from a Bedrock agent function. * @@ -23,35 +26,35 @@ class BedrockFunctionResponse { * - `FAILURE`: The agent throws a `DependencyFailedException` for the current session. * - `REPROMPT`: The agent passes a response string to the model to reprompt it. */ - readonly responseState?: 'FAILURE' | 'REPROMPT'; + readonly responseState?: ResponseState; /** * Optional field to store session attributes and their values. * @see {@link https://docs.aws.amazon.com/bedrock/latest/userguide/agents-session-state.html#session-state-attributes | Bedrock Agent Session State Attributes} for more details. */ - readonly sessionAttributes: Record; + readonly sessionAttributes: BedrockAgentFunctionEvent['sessionAttributes']; /** * Optional field to instruct the agent to prompt attributes and their values. * @see {@link https://docs.aws.amazon.com/bedrock/latest/userguide/agents-session-state.html#session-state-attributes | Bedrock Agent Session State Attributes} for more details. */ - readonly promptSessionAttributes: Record; + readonly promptSessionAttributes: BedrockAgentFunctionEvent['promptSessionAttributes']; /** * Optional field to configure knowledge bases for the agent. * @see {@link https://docs.aws.amazon.com/bedrock/latest/userguide/agents-session-state.html#session-state-kb | Bedrock Agent Knowledge Bases} for more details. */ - readonly knowledgeBasesConfiguration?: Record; + readonly knowledgeBasesConfiguration?: BedrockAgentFunctionEvent['knowledgeBasesConfiguration']; constructor({ body, responseState = undefined, sessionAttributes = {}, promptSessionAttributes = {}, - knowledgeBasesConfiguration = {}, + knowledgeBasesConfiguration = undefined, }: { body: string; - responseState?: 'FAILURE' | 'REPROMPT'; - sessionAttributes?: Record; - promptSessionAttributes?: Record; - knowledgeBasesConfiguration?: Record; + responseState?: ResponseState; + sessionAttributes?: BedrockAgentFunctionEvent['sessionAttributes']; + promptSessionAttributes?: BedrockAgentFunctionEvent['promptSessionAttributes']; + knowledgeBasesConfiguration?: BedrockAgentFunctionEvent['knowledgeBasesConfiguration']; }) { this.body = body; this.responseState = responseState; diff --git a/packages/event-handler/src/types/bedrock-agent.ts b/packages/event-handler/src/types/bedrock-agent.ts index 7f3b08610..b3657922d 100644 --- a/packages/event-handler/src/types/bedrock-agent.ts +++ b/packages/event-handler/src/types/bedrock-agent.ts @@ -114,9 +114,9 @@ type BedrockAgentFunctionEvent = { parameters?: Array; inputText: string; sessionId: string; - sessionAttributes: Record; - promptSessionAttributes: Record; - knowledgeBasesConfiguration?: Record; + sessionAttributes: Record; + promptSessionAttributes: Record; + knowledgeBasesConfiguration?: Record; }; /** @@ -143,8 +143,9 @@ type BedrockAgentFunctionResponse = { }; }; }; - sessionAttributes?: Record; - promptSessionAttributes?: Record; + sessionAttributes?: BedrockAgentFunctionEvent['sessionAttributes']; + promptSessionAttributes?: BedrockAgentFunctionEvent['promptSessionAttributes']; + knowledgeBasesConfiguration?: BedrockAgentFunctionEvent['knowledgeBasesConfiguration']; }; /** @@ -169,4 +170,5 @@ export type { BedrockAgentFunctionEvent, BedrockAgentFunctionResponse, ResolverOptions, + ResponseState, }; diff --git a/packages/event-handler/src/types/index.ts b/packages/event-handler/src/types/index.ts index 073a80a47..dfa9f7898 100644 --- a/packages/event-handler/src/types/index.ts +++ b/packages/event-handler/src/types/index.ts @@ -14,6 +14,7 @@ export type { BedrockAgentFunctionResponse, ResolverOptions, Parameter, + ResponseState, } from './bedrock-agent.js'; export type { diff --git a/packages/event-handler/tests/unit/bedrock-agent/BedrockAgentFunctionResolver.test.ts b/packages/event-handler/tests/unit/bedrock-agent/BedrockAgentFunctionResolver.test.ts index c42b8eafd..1b21f6694 100644 --- a/packages/event-handler/tests/unit/bedrock-agent/BedrockAgentFunctionResolver.test.ts +++ b/packages/event-handler/tests/unit/bedrock-agent/BedrockAgentFunctionResolver.test.ts @@ -1,10 +1,8 @@ import context from '@aws-lambda-powertools/testing-utils/context'; -import type { Context } from 'aws-lambda'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { BedrockFunctionResponse } from '../../../src/bedrock-agent/BedrockFunctionResponse.js'; import { BedrockAgentFunctionResolver } from '../../../src/bedrock-agent/index.js'; import type { - BedrockAgentFunctionEvent, Configuration, Parameter, ToolFunction, @@ -575,14 +573,6 @@ describe('Class: BedrockAgentFunctionResolver', () => { } ); - const customSessionAttrs = { - sessionAttr: '12345', - }; - - const customPromptAttrs = { - promptAttr: 'promptAttr', - }; - const customEvent = { ...createEvent('greeting', [ { @@ -592,8 +582,16 @@ describe('Class: BedrockAgentFunctionResolver', () => { }, ]), actionGroup: 'actionGroup', - sessionAttributes: customSessionAttrs, - promptSessionAttributes: customPromptAttrs, + sessionAttributes: { + sessionAttr: '12345', + }, + promptSessionAttributes: { + promptAttr: 'promptAttr', + }, + knowledgeBasesConfiguration: { + knowledgeBase1: { enabled: true }, + knowledgeBase2: { enabled: false }, + }, }; // Act @@ -613,8 +611,9 @@ describe('Class: BedrockAgentFunctionResolver', () => { }, }, }, - sessionAttributes: customSessionAttrs, - promptSessionAttributes: customPromptAttrs, + sessionAttributes: customEvent.sessionAttributes, + promptSessionAttributes: customEvent.promptSessionAttributes, + knowledgeBasesConfiguration: customEvent.knowledgeBasesConfiguration, }); }); }); From 67298ce438f88c0749f5ace6f0ef852b348f1fa3 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Wed, 28 May 2025 17:57:33 +0200 Subject: [PATCH 6/7] chore: move export --- .../src/bedrock-agent/BedrockAgentFunctionResolver.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts b/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts index 63329ac23..92c9505f8 100644 --- a/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts +++ b/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts @@ -38,7 +38,7 @@ import { assertBedrockAgentFunctionEvent } from './utils.js'; * app.resolve(event, context); * ``` */ -export class BedrockAgentFunctionResolver { +class BedrockAgentFunctionResolver { /** * Registry of tools added to the Bedrock Agent Function Resolver. */ @@ -281,3 +281,5 @@ export class BedrockAgentFunctionResolver { } } } + +export { BedrockAgentFunctionResolver }; From b04f5191f105f02473de1c7fcaa489e4d496eb34 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Wed, 28 May 2025 18:06:11 +0200 Subject: [PATCH 7/7] chore: address SonarQube --- .../BedrockAgentFunctionResolver.ts | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts b/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts index 92c9505f8..01ec21074 100644 --- a/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts +++ b/packages/event-handler/src/bedrock-agent/BedrockAgentFunctionResolver.ts @@ -147,14 +147,6 @@ class BedrockAgentFunctionResolver { fn: ToolFunction, config: Configuration ): undefined { - this.#registerTool(fn, config); - return; - } - - #registerTool>( - handler: ToolFunction, - config: Configuration - ): void { const { name } = config; if (this.#tools.has(name)) { this.#logger.warn( @@ -163,7 +155,7 @@ class BedrockAgentFunctionResolver { } this.#tools.set(name, { - handler: handler as ToolFunction, + handler: fn as ToolFunction, config, }); this.#logger.debug(`Tool "${name}" has been registered.`); @@ -213,9 +205,9 @@ class BedrockAgentFunctionResolver { const tool = this.#tools.get(toolName); if (tool == null) { - this.#logger.error(`Tool ${toolName} has not been registered.`); + this.#logger.error(`Tool "${toolName}" has not been registered.`); return new BedrockFunctionResponse({ - body: `Error: tool ${toolName} has not been registered.`, + body: `Error: tool "${toolName}" has not been registered.`, sessionAttributes, promptSessionAttributes, knowledgeBasesConfiguration, @@ -238,7 +230,7 @@ class BedrockAgentFunctionResolver { break; } // this default will also catch array types but we leave them as strings - // because we cannot reliably parse them + // because we cannot reliably parse them - see discussion in #3710 default: { toolParams[param.name] = param.value; break; @@ -269,8 +261,12 @@ class BedrockAgentFunctionResolver { }); } catch (error) { this.#logger.error(`An error occurred in tool ${toolName}.`, error); + const errorMessage = + error instanceof Error + ? `${error.name} - ${error.message}` + : String(error); return new BedrockFunctionResponse({ - body: `Unable to complete tool execution due to ${error instanceof Error ? `${error.name} - ${error.message}` : String(error)}`, + body: `Unable to complete tool execution due to ${errorMessage}`, sessionAttributes, promptSessionAttributes, knowledgeBasesConfiguration,