@aws-blocks/bb-agent 0.3.3 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/DESIGN.md CHANGED
@@ -42,6 +42,7 @@ The CDK class mirrors the runtime's BB creation:
42
42
  - **DistributedTable:** `${id}-messages` — conversation history (only when `inferenceOnly: false`)
43
43
  - **Realtime:** `${id}-rt` — streaming namespace `chunks`
44
44
  - **AsyncJob:** `${id}-job` — job payload: `{ message, conversationId?, channelId }`
45
+ - Event source uses `batchSize: 1` / `maxBatchingWindowSeconds: 0`: the caller is blocked on the job starting, so the Agent opts out of AsyncJob's batching defaults (10 / 5s) rather than add up to 5s of latency to an interactive turn.
45
46
 
46
47
  > **Note:** Internal Building Blocks are created on the parent scope (not `this`) to ensure correct nested-scope resolution on AWS.
47
48
 
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
package/dist/agent.js CHANGED
@@ -10,6 +10,7 @@ import { z } from 'zod';
10
10
  import { createStrandsModel, checkModelHealth } from './model-factory.js';
11
11
  import { messageSchema, conversationSchema, agentStreamChunkSchema } from './schemas.js';
12
12
  import { AgentErrors, blocksAgentError, InterruptError } from './errors.js';
13
+ import { INTERACTIVE_JOB_EVENT_SOURCE } from './job-event-source.js';
13
14
  import { BB_NAME, BB_VERSION } from './version.js';
14
15
  import { ulid } from 'ulid';
15
16
  /** Payload submitted to the internal AsyncJob BB. */
@@ -147,6 +148,7 @@ export class AgentBase extends Scope {
147
148
  });
148
149
  this.job = new AsyncJob(this, 'job', {
149
150
  schema: jobPayloadSchema,
151
+ ...INTERACTIVE_JOB_EVENT_SOURCE,
150
152
  handler: async (payload) => {
151
153
  try {
152
154
  await this.runAgent(payload.message, payload.conversationId, payload.channelId, payload.userId, payload.interruptResponses, payload.context);
@@ -1 +1 @@
1
- {"version":3,"file":"index.cdk.d.ts","sourceRoot":"","sources":["../src/index.cdk.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAQpD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAO1D,qBAAa,KAAM,SAAQ,KAAK;IAC/B;;;;;;OAMG;gBACS,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG;CAwCxD"}
1
+ {"version":3,"file":"index.cdk.d.ts","sourceRoot":"","sources":["../src/index.cdk.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AASpD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAO1D,qBAAa,KAAM,SAAQ,KAAK;IAC/B;;;;;;OAMG;gBACS,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG;CAyCxD"}
package/dist/index.cdk.js CHANGED
@@ -7,6 +7,7 @@ import { Realtime } from '@aws-blocks/bb-realtime';
7
7
  import { AsyncJob } from '@aws-blocks/bb-async-job';
8
8
  import { FileBucket } from '@aws-blocks/bb-file-bucket';
9
9
  import { messageSchema, conversationSchema, agentStreamChunkSchema } from './schemas.js';
10
+ import { INTERACTIVE_JOB_EVENT_SOURCE } from './job-event-source.js';
10
11
  import { z } from 'zod';
11
12
  export { AgentErrors } from './errors.js';
12
13
  export { BedrockModels, OllamaModels } from './models.js';
@@ -24,7 +25,7 @@ export class Agent extends Scope {
24
25
  */
25
26
  constructor(scope, id, config) {
26
27
  super(id, { parent: scope });
27
- this.handler.addToRolePolicy(new PolicyStatement({
28
+ this.executionRole.addToPrincipalPolicy(new PolicyStatement({
28
29
  actions: ['bedrock:InvokeModel', 'bedrock:InvokeModelWithResponseStream', 'bedrock:GetFoundationModel', 'bedrock:ListFoundationModels', 'bedrock:GetInferenceProfile'],
29
30
  resources: [
30
31
  'arn:aws:bedrock:*::foundation-model/*',
@@ -54,6 +55,7 @@ export class Agent extends Scope {
54
55
  });
55
56
  new AsyncJob(this, 'job', {
56
57
  schema: jobPayloadSchema,
58
+ ...INTERACTIVE_JOB_EVENT_SOURCE,
57
59
  handler: async () => { },
58
60
  });
59
61
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.cdk.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cdk.test.d.ts","sourceRoot":"","sources":["../src/index.cdk.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,66 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * CDK-side regression tests for the Agent's internal AsyncJob event source.
5
+ *
6
+ * `stream()` submits one job per interactive turn (and a second on HITL resume),
7
+ * so the caller is blocked on that job starting. AsyncJob's defaults
8
+ * (batchSize 10 / maxBatchingWindowSeconds 5) would add up to 5s of SQS
9
+ * batching delay to that human-blocking path, so the Agent opts out at both
10
+ * construction sites. These tests pin the opt-out to the synthesized template:
11
+ * if the defaults are ever inherited again, the latency regression fails here
12
+ * instead of surfacing as a slow agent in production.
13
+ *
14
+ * Must run under `--conditions=cdk`; otherwise the internal BBs resolve to
15
+ * their mock implementations and no CloudFormation resources are produced.
16
+ */
17
+ import { test, afterEach } from 'node:test';
18
+ import assert from 'node:assert';
19
+ import * as cdk from 'aws-cdk-lib';
20
+ import { Template } from 'aws-cdk-lib/assertions';
21
+ import { Scope, DEFAULT_NODE_RUNTIME } from '@aws-blocks/core/cdk';
22
+ import { Agent } from './index.cdk.js';
23
+ class StubBlocksStack extends cdk.Stack {
24
+ handler;
25
+ executionRole;
26
+ id;
27
+ constructor(scope, id) {
28
+ super(scope, id);
29
+ this.id = id;
30
+ globalThis.CURRENT_BLOCKS_STACK = this;
31
+ this.executionRole = new cdk.aws_iam.Role(this, 'BlocksRole', {
32
+ assumedBy: new cdk.aws_iam.ServicePrincipal('lambda.amazonaws.com'),
33
+ });
34
+ this.handler = new cdk.aws_lambda.Function(this, 'StubHandler', {
35
+ runtime: DEFAULT_NODE_RUNTIME,
36
+ handler: 'index.handler',
37
+ code: cdk.aws_lambda.Code.fromInline('exports.handler = async () => {};'),
38
+ role: this.executionRole,
39
+ });
40
+ }
41
+ }
42
+ // synthAgent() installs its own stack as the ambient CURRENT_BLOCKS_STACK; clear it
43
+ // afterwards so no test observes a stack left behind by the previous one (node
44
+ // --test isolates files by default, so this is hygiene against future sharing).
45
+ afterEach(() => {
46
+ delete globalThis.CURRENT_BLOCKS_STACK;
47
+ });
48
+ function synthAgent() {
49
+ const app = new cdk.App();
50
+ const stack = new StubBlocksStack(app, 'teststack');
51
+ const parent = new Scope('app');
52
+ new Agent(parent, 'agent', { inferenceOnly: true });
53
+ const mappings = Template.fromStack(stack).findResources('AWS::Lambda::EventSourceMapping');
54
+ const keys = Object.keys(mappings);
55
+ assert.strictEqual(keys.length, 1, 'exactly one event source mapping expected for the agent job');
56
+ return mappings[keys[0]].Properties;
57
+ }
58
+ test('CDK: the Agent job takes one message per invocation (no batching on the interactive path)', () => {
59
+ assert.strictEqual(synthAgent().BatchSize, 1);
60
+ });
61
+ test('CDK: the Agent job has no SQS batching window (no added latency for the caller)', () => {
62
+ assert.strictEqual(synthAgent().MaximumBatchingWindowInSeconds, 0);
63
+ });
64
+ test('CDK: the Agent job still reports partial batch failures', () => {
65
+ assert.deepStrictEqual(synthAgent().FunctionResponseTypes, ['ReportBatchItemFailures']);
66
+ });
@@ -0,0 +1,19 @@
1
+ /**
2
+ * SQS event source options for the Agent's internal AsyncJob, shared by the
3
+ * runtime (`agent.ts`) and CDK (`index.cdk.ts`) construction sites so an edit to
4
+ * one site's numbers cannot silently miss the other. Only the CDK site's values
5
+ * reach a real event source mapping today — the runtime entry points do not read
6
+ * these options — so this is a single source of truth for the intent, not a
7
+ * runtime/CDK sync guarantee.
8
+ *
9
+ * `stream()` submits one job per interactive turn (and a second on HITL
10
+ * resume) while the caller is blocked on that job starting, so AsyncJob's
11
+ * batching defaults would add up to 5s of delivery latency to a human-facing
12
+ * path. `batchSize: 1` also keeps one failing turn out of a shared batch,
13
+ * which matters because the handler is not idempotent.
14
+ */
15
+ export declare const INTERACTIVE_JOB_EVENT_SOURCE: {
16
+ readonly batchSize: 1;
17
+ readonly maxBatchingWindowSeconds: 0;
18
+ };
19
+ //# sourceMappingURL=job-event-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job-event-source.d.ts","sourceRoot":"","sources":["../src/job-event-source.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,4BAA4B;;;CAG/B,CAAC"}
@@ -0,0 +1,20 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * SQS event source options for the Agent's internal AsyncJob, shared by the
5
+ * runtime (`agent.ts`) and CDK (`index.cdk.ts`) construction sites so an edit to
6
+ * one site's numbers cannot silently miss the other. Only the CDK site's values
7
+ * reach a real event source mapping today — the runtime entry points do not read
8
+ * these options — so this is a single source of truth for the intent, not a
9
+ * runtime/CDK sync guarantee.
10
+ *
11
+ * `stream()` submits one job per interactive turn (and a second on HITL
12
+ * resume) while the caller is blocked on that job starting, so AsyncJob's
13
+ * batching defaults would add up to 5s of delivery latency to a human-facing
14
+ * path. `batchSize: 1` also keeps one failing turn out of a shared batch,
15
+ * which matters because the handler is not idempotent.
16
+ */
17
+ export const INTERACTIVE_JOB_EVENT_SOURCE = {
18
+ batchSize: 1,
19
+ maxBatchingWindowSeconds: 0,
20
+ };
package/dist/version.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export declare const BB_NAME = "Agent";
2
- export declare const BB_VERSION = "0.3.3";
2
+ export declare const BB_VERSION = "0.3.5";
3
3
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // Auto-generated by scripts/generate-version.mjs — do not edit manually
2
2
  export const BB_NAME = 'Agent';
3
- export const BB_VERSION = '0.3.3';
3
+ export const BB_VERSION = '0.3.5';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aws-blocks/bb-agent",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/aws-devtools-labs/aws-blocks.git",
@@ -40,15 +40,15 @@
40
40
  "scripts": {
41
41
  "prebuild": "node ../../scripts/generate-version.mjs Agent",
42
42
  "build": "tsc --build",
43
- "test": "node --test dist/index.test.js"
43
+ "test": "node --test dist/index.test.js && node --conditions=cdk --test dist/index.cdk.test.js"
44
44
  },
45
45
  "dependencies": {
46
- "@aws-blocks/bb-async-job": "^0.1.3",
47
- "@aws-blocks/bb-distributed-table": "^0.1.4",
48
- "@aws-blocks/bb-file-bucket": "^0.1.3",
49
- "@aws-blocks/bb-logger": "^0.1.3",
50
- "@aws-blocks/bb-realtime": "^0.1.2",
51
- "@aws-blocks/core": "^0.1.17",
46
+ "@aws-blocks/bb-async-job": "^0.1.5",
47
+ "@aws-blocks/bb-distributed-table": "^0.1.6",
48
+ "@aws-blocks/bb-file-bucket": "^0.1.5",
49
+ "@aws-blocks/bb-logger": "^0.1.5",
50
+ "@aws-blocks/bb-realtime": "^0.1.5",
51
+ "@aws-blocks/core": "^0.3.0",
52
52
  "@aws-sdk/client-bedrock": "^3.700.0",
53
53
  "@opentelemetry/api": "^1.9.0",
54
54
  "@strands-agents/sdk": "~1.3.0",
package/src/agent.ts CHANGED
@@ -17,6 +17,7 @@ import { createStrandsModel, checkModelHealth } from './model-factory.js';
17
17
  import { messageSchema, conversationSchema, agentStreamChunkSchema } from './schemas.js';
18
18
  import type { AgentConfig, AgentStreamChunk, AgentStreamResult, StreamOptions, Message, Conversation, TokenUsage, ConversationManagerConfig, ModelConfig, JSONValue, InterruptResponse, DefaultToolContext, AgentTool, ToolDefinition } from './types.js';
19
19
  import { AgentErrors, blocksAgentError, InterruptError } from './errors.js';
20
+ import { INTERACTIVE_JOB_EVENT_SOURCE } from './job-event-source.js';
20
21
  import { BB_NAME, BB_VERSION } from './version.js';
21
22
  import { ulid } from 'ulid';
22
23
 
@@ -167,6 +168,7 @@ export class AgentBase<TContext = DefaultToolContext> extends Scope {
167
168
 
168
169
  this.job = new AsyncJob(this, 'job', {
169
170
  schema: jobPayloadSchema,
171
+ ...INTERACTIVE_JOB_EVENT_SOURCE,
170
172
  handler: async (payload) => {
171
173
  try {
172
174
  await this.runAgent(payload.message, payload.conversationId, payload.channelId, payload.userId, payload.interruptResponses, payload.context);
@@ -0,0 +1,75 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /**
5
+ * CDK-side regression tests for the Agent's internal AsyncJob event source.
6
+ *
7
+ * `stream()` submits one job per interactive turn (and a second on HITL resume),
8
+ * so the caller is blocked on that job starting. AsyncJob's defaults
9
+ * (batchSize 10 / maxBatchingWindowSeconds 5) would add up to 5s of SQS
10
+ * batching delay to that human-blocking path, so the Agent opts out at both
11
+ * construction sites. These tests pin the opt-out to the synthesized template:
12
+ * if the defaults are ever inherited again, the latency regression fails here
13
+ * instead of surfacing as a slow agent in production.
14
+ *
15
+ * Must run under `--conditions=cdk`; otherwise the internal BBs resolve to
16
+ * their mock implementations and no CloudFormation resources are produced.
17
+ */
18
+ import { test, afterEach } from 'node:test';
19
+ import assert from 'node:assert';
20
+ import * as cdk from 'aws-cdk-lib';
21
+ import type { Construct } from 'constructs';
22
+ import { Template } from 'aws-cdk-lib/assertions';
23
+ import { Scope, DEFAULT_NODE_RUNTIME } from '@aws-blocks/core/cdk';
24
+ import { Agent } from './index.cdk.js';
25
+
26
+ class StubBlocksStack extends cdk.Stack {
27
+ public readonly handler: cdk.aws_lambda.Function;
28
+ public readonly executionRole: cdk.aws_iam.IRole;
29
+ public readonly id: string;
30
+ constructor(scope: Construct, id: string) {
31
+ super(scope, id);
32
+ this.id = id;
33
+ (globalThis as any).CURRENT_BLOCKS_STACK = this;
34
+ this.executionRole = new cdk.aws_iam.Role(this, 'BlocksRole', {
35
+ assumedBy: new cdk.aws_iam.ServicePrincipal('lambda.amazonaws.com'),
36
+ });
37
+ this.handler = new cdk.aws_lambda.Function(this, 'StubHandler', {
38
+ runtime: DEFAULT_NODE_RUNTIME,
39
+ handler: 'index.handler',
40
+ code: cdk.aws_lambda.Code.fromInline('exports.handler = async () => {};'),
41
+ role: this.executionRole,
42
+ });
43
+ }
44
+ }
45
+
46
+ // synthAgent() installs its own stack as the ambient CURRENT_BLOCKS_STACK; clear it
47
+ // afterwards so no test observes a stack left behind by the previous one (node
48
+ // --test isolates files by default, so this is hygiene against future sharing).
49
+ afterEach(() => {
50
+ delete (globalThis as any).CURRENT_BLOCKS_STACK;
51
+ });
52
+
53
+ function synthAgent(): any {
54
+ const app = new cdk.App();
55
+ const stack = new StubBlocksStack(app, 'teststack');
56
+ const parent = new Scope('app');
57
+ new Agent(parent, 'agent', { inferenceOnly: true });
58
+
59
+ const mappings = Template.fromStack(stack).findResources('AWS::Lambda::EventSourceMapping');
60
+ const keys = Object.keys(mappings);
61
+ assert.strictEqual(keys.length, 1, 'exactly one event source mapping expected for the agent job');
62
+ return mappings[keys[0]].Properties;
63
+ }
64
+
65
+ test('CDK: the Agent job takes one message per invocation (no batching on the interactive path)', () => {
66
+ assert.strictEqual(synthAgent().BatchSize, 1);
67
+ });
68
+
69
+ test('CDK: the Agent job has no SQS batching window (no added latency for the caller)', () => {
70
+ assert.strictEqual(synthAgent().MaximumBatchingWindowInSeconds, 0);
71
+ });
72
+
73
+ test('CDK: the Agent job still reports partial batch failures', () => {
74
+ assert.deepStrictEqual(synthAgent().FunctionResponseTypes, ['ReportBatchItemFailures']);
75
+ });
package/src/index.cdk.ts CHANGED
@@ -9,6 +9,7 @@ import { Realtime } from '@aws-blocks/bb-realtime';
9
9
  import { AsyncJob } from '@aws-blocks/bb-async-job';
10
10
  import { FileBucket } from '@aws-blocks/bb-file-bucket';
11
11
  import { messageSchema, conversationSchema, agentStreamChunkSchema } from './schemas.js';
12
+ import { INTERACTIVE_JOB_EVENT_SOURCE } from './job-event-source.js';
12
13
  import { z } from 'zod';
13
14
 
14
15
  export { AgentErrors } from './errors.js';
@@ -30,7 +31,7 @@ export class Agent extends Scope {
30
31
  constructor(scope: ScopeParent, id: string, config?: any) {
31
32
  super(id, { parent: scope });
32
33
 
33
- this.handler.addToRolePolicy(new PolicyStatement({
34
+ this.executionRole.addToPrincipalPolicy(new PolicyStatement({
34
35
  actions: ['bedrock:InvokeModel', 'bedrock:InvokeModelWithResponseStream', 'bedrock:GetFoundationModel', 'bedrock:ListFoundationModels', 'bedrock:GetInferenceProfile'],
35
36
  resources: [
36
37
  'arn:aws:bedrock:*::foundation-model/*',
@@ -64,6 +65,7 @@ export class Agent extends Scope {
64
65
 
65
66
  new AsyncJob(this, 'job', {
66
67
  schema: jobPayloadSchema,
68
+ ...INTERACTIVE_JOB_EVENT_SOURCE,
67
69
  handler: async () => {},
68
70
  });
69
71
  }
@@ -0,0 +1,21 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /**
5
+ * SQS event source options for the Agent's internal AsyncJob, shared by the
6
+ * runtime (`agent.ts`) and CDK (`index.cdk.ts`) construction sites so an edit to
7
+ * one site's numbers cannot silently miss the other. Only the CDK site's values
8
+ * reach a real event source mapping today — the runtime entry points do not read
9
+ * these options — so this is a single source of truth for the intent, not a
10
+ * runtime/CDK sync guarantee.
11
+ *
12
+ * `stream()` submits one job per interactive turn (and a second on HITL
13
+ * resume) while the caller is blocked on that job starting, so AsyncJob's
14
+ * batching defaults would add up to 5s of delivery latency to a human-facing
15
+ * path. `batchSize: 1` also keeps one failing turn out of a shared batch,
16
+ * which matters because the handler is not idempotent.
17
+ */
18
+ export const INTERACTIVE_JOB_EVENT_SOURCE = {
19
+ batchSize: 1,
20
+ maxBatchingWindowSeconds: 0,
21
+ } as const;
package/src/version.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  // Auto-generated by scripts/generate-version.mjs — do not edit manually
2
2
  export const BB_NAME = 'Agent';
3
- export const BB_VERSION = '0.3.3';
3
+ export const BB_VERSION = '0.3.5';