Skip to content

Commit ce61bb7

Browse files
authored
refactor(bb-agent): model presets (#16)
1 parent 53adfb8 commit ce61bb7

7 files changed

Lines changed: 110 additions & 41 deletions

File tree

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
"@aws-blocks/bb-agent": minor
3+
---
4+
5+
refactor(bb-agent): capability-based model presets with global inference profiles
6+
7+
New presets:
8+
- `BALANCED` (Claude Sonnet 4.6): recommended default for most workloads
9+
- `SMART` (Claude Opus 4.8): highest capability for hardest tasks
10+
- `FAST` (Claude Haiku 4.5): lowest latency
11+
12+
All presets use `global.` inference profiles for region-agnostic deployment.
13+
14+
Deprecated (non-removing): `DEFAULT` resolves to `BALANCED`, `BUDGET` and `MICRO` resolve to `FAST`. Note this changes the underlying model for existing callers — `DEFAULT` moves from Opus to Sonnet, and `BUDGET`/`MICRO` move from Amazon Nova Pro/Lite to Claude Haiku, so cost and latency profiles differ. The symbols still resolve (no type break), but migrate to `BALANCED`/`FAST` (or a region-scoped profile) explicitly to pin the model you want.

packages/bb-agent/API.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,25 +102,29 @@ export type AgentTool<TContext = DefaultToolContext> = ToolDefinition<TContext,
102102

103103
// @public
104104
export const BedrockModels: {
105-
readonly DEFAULT: {
105+
readonly BALANCED: {
106106
readonly provider: "bedrock";
107-
readonly modelId: "us.anthropic.claude-opus-4-8-20250610-v1:0";
107+
readonly modelId: "global.anthropic.claude-sonnet-4-6";
108108
};
109-
readonly BALANCED: {
109+
readonly SMART: {
110110
readonly provider: "bedrock";
111-
readonly modelId: "us.anthropic.claude-sonnet-4-20250514-v1:0";
111+
readonly modelId: "global.anthropic.claude-opus-4-8";
112112
};
113113
readonly FAST: {
114114
readonly provider: "bedrock";
115-
readonly modelId: "us.anthropic.claude-haiku-4-5-20251001-v1:0";
115+
readonly modelId: "global.anthropic.claude-haiku-4-5-20251001-v1:0";
116+
};
117+
readonly DEFAULT: {
118+
readonly provider: "bedrock";
119+
readonly modelId: "global.anthropic.claude-sonnet-4-6";
116120
};
117121
readonly BUDGET: {
118122
readonly provider: "bedrock";
119-
readonly modelId: "us.amazon.nova-pro-v1:0";
123+
readonly modelId: "global.anthropic.claude-haiku-4-5-20251001-v1:0";
120124
};
121125
readonly MICRO: {
122126
readonly provider: "bedrock";
123-
readonly modelId: "us.amazon.nova-lite-v1:0";
127+
readonly modelId: "global.anthropic.claude-haiku-4-5-20251001-v1:0";
124128
};
125129
};
126130

packages/bb-agent/README.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { Agent, BedrockModels } from '@aws-blocks/bb-agent';
1717
const scope = new Scope('my-app');
1818

1919
const agent = new Agent(scope, 'support-agent', {
20-
model: { deployed: BedrockModels.DEFAULT },
20+
model: { deployed: BedrockModels.BALANCED },
2121
systemPrompt: 'You are a helpful support agent.',
2222
});
2323

@@ -192,30 +192,30 @@ model: {
192192

193193
#### Bedrock Presets
194194

195-
Pre-configured model presets for quick setup. Names are capability-based so the underlying model can be upgraded without breaking your code. These use cross-region inference profiles — work across all AWS regions:
195+
Pre-configured model presets for quick setup. Names are capability-based so the underlying model can be upgraded without breaking your code. These use [global inference profiles](https://docs.aws.amazon.com/bedrock/latest/userguide/cross-region-inference.html) — requests may be routed to any supported AWS region for optimal throughput. If your workload has data residency requirements, specify a region-scoped inference profile explicitly instead of using a preset.
196196

197197
```typescript
198198
import { Agent, BedrockModels} from '@aws-blocks/bb-agent';
199199

200200
const agent = new Agent(scope, 'agent', {
201201
model: {
202-
deployed: BedrockModels.DEFAULT,
202+
deployed: BedrockModels.BALANCED,
203203
},
204204
systemPrompt: '...',
205205
});
206206
```
207207

208208
| Preset | Current Model | Notes |
209209
|--------|---------------|-------|
210-
| `BedrockModels.DEFAULT` | `us.anthropic.claude-opus-4-8-20250610-v1:0` | Highest capability. Recommended default. |
211-
| `BedrockModels.BALANCED` | `us.anthropic.claude-sonnet-4-20250514-v1:0` | Strong quality/cost balance. |
212-
| `BedrockModels.FAST` | `us.anthropic.claude-haiku-4-5-20251001-v1:0` | Fastest, lowest latency. |
213-
| `BedrockModels.BUDGET` | `us.amazon.nova-pro-v1:0` | Low cost per token with acceptable quality. |
214-
| `BedrockModels.MICRO` | `us.amazon.nova-lite-v1:0` | Ultra-cheap for simple tasks. |
210+
| `BedrockModels.BALANCED` | `global.anthropic.claude-sonnet-4-6` | Great tool use, balanced cost. Recommended default for most workloads. |
211+
| `BedrockModels.SMART` | `global.anthropic.claude-opus-4-8` | Highest capability for the hardest tasks. |
212+
| `BedrockModels.FAST` | `global.anthropic.claude-haiku-4-5-20251001-v1:0` | Lowest latency, still strong capabilities. |
213+
214+
> **Migrating?** `DEFAULT``BALANCED` (or `SMART` for highest capability). `BUDGET`/`MICRO` `FAST`. The old presets are still available but deprecated. Please consider upgrading!
215215
216216
Override inference settings with spread:
217217
```typescript
218-
model: { deployed: { ...BedrockModels.DEFAULT, inferenceConfig: { temperature: 0.9, maxTokens: 8192 } } }
218+
model: { deployed: { ...BedrockModels.BALANCED, inferenceConfig: { temperature: 0.9, maxTokens: 8192 } } }
219219
```
220220

221221
#### Ollama Presets
@@ -227,7 +227,7 @@ import { Agent, BedrockModels, OllamaModels} from '@aws-blocks/bb-agent';
227227

228228
const agent = new Agent(scope, 'agent', {
229229
model: {
230-
deployed: BedrockModels.DEFAULT,
230+
deployed: BedrockModels.BALANCED,
231231
local: OllamaModels.SMALL,
232232
},
233233
systemPrompt: '...',
@@ -264,7 +264,7 @@ To see detailed health check logs, pass a logger with `info` level:
264264
import { Logger } from '@aws-blocks/bb-logger';
265265

266266
const agent = new Agent(scope, 'agent', {
267-
model: { deployed: BedrockModels.DEFAULT },
267+
model: { deployed: BedrockModels.BALANCED },
268268
systemPrompt: '...',
269269
logger: new Logger(scope, 'agent-log', { level: 'info' }),
270270
});
@@ -543,7 +543,7 @@ The Agent BB works without a frontend — for scripts, background jobs, or serve
543543
import { Agent, BedrockModels } from '@aws-blocks/bb-agent';
544544

545545
const agent = new Agent(scope, 'summarizer', {
546-
model: { deployed: BedrockModels.DEFAULT },
546+
model: { deployed: BedrockModels.BALANCED },
547547
systemPrompt: 'Summarize the input concisely.',
548548
});
549549

@@ -562,7 +562,7 @@ import { Agent, BedrockModels, InterruptError } from '@aws-blocks/bb-agent';
562562
import { z } from 'zod';
563563

564564
const refundBot = new Agent(scope, 'refunds', {
565-
model: { deployed: BedrockModels.DEFAULT },
565+
model: { deployed: BedrockModels.BALANCED },
566566
systemPrompt: 'You process customer refund requests.',
567567
tools: (tool) => ({
568568
issueRefund: tool({
@@ -689,7 +689,7 @@ import { Agent, BedrockModels } from '@aws-blocks/bb-agent';
689689
const scope = new Scope('my-app');
690690

691691
const agent = new Agent(scope, 'chat', {
692-
model: { deployed: BedrockModels.DEFAULT },
692+
model: { deployed: BedrockModels.BALANCED },
693693
systemPrompt: 'You are a helpful assistant.',
694694
});
695695

@@ -753,7 +753,7 @@ const scope = new Scope('my-app');
753753
const kb = new KnowledgeBase(scope, 'docs', { source: './knowledge' });
754754

755755
const agent = new Agent(scope, 'support', {
756-
model: { deployed: BedrockModels.DEFAULT },
756+
model: { deployed: BedrockModels.BALANCED },
757757
systemPrompt: 'You are a customer support agent. Look up orders and search documentation to help the user.',
758758
toolContextSchema: z.object({ userId: z.string() }),
759759
tools: (tool) => ({

packages/bb-agent/src/index.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,9 +1004,9 @@ describe('checkModelHealth', () => {
10041004
// ── Model Presets ─────────────────────────────────────────────────────────────
10051005

10061006
describe('BedrockModels presets', () => {
1007-
test('DEFAULT resolves to a bedrock provider', async () => {
1008-
assert.strictEqual(BedrockModels.DEFAULT.provider, 'bedrock');
1009-
assert.ok(BedrockModels.DEFAULT.modelId);
1007+
test('BALANCED resolves to a bedrock provider', async () => {
1008+
assert.strictEqual(BedrockModels.BALANCED.provider, 'bedrock');
1009+
assert.ok(BedrockModels.BALANCED.modelId);
10101010
});
10111011

10121012
test('all presets have provider bedrock and a modelId', () => {
@@ -1016,8 +1016,8 @@ describe('BedrockModels presets', () => {
10161016
}
10171017
});
10181018

1019-
test('DEFAULT flows through createStrandsModel to BedrockModel', async () => {
1020-
const model = await createStrandsModel(BedrockModels.DEFAULT);
1019+
test('BALANCED flows through createStrandsModel to BedrockModel', async () => {
1020+
const model = await createStrandsModel(BedrockModels.BALANCED);
10211021
assert.ok(model, 'should create a model instance');
10221022
});
10231023
});

packages/bb-agent/src/models.ts

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,50 @@
33

44
import type { ModelConfig } from './types.js';
55

6+
const BALANCED_MODEL_ID = 'global.anthropic.claude-sonnet-4-6';
7+
const SMART_MODEL_ID = 'global.anthropic.claude-opus-4-8';
8+
const FAST_MODEL_ID = 'global.anthropic.claude-haiku-4-5-20251001-v1:0';
9+
610
/**
7-
* Pre-configured Bedrock model presets using cross-region inference profiles.
11+
* Pre-configured Bedrock model presets using global inference profiles.
812
* Names are capability-based so the underlying model can be upgraded without breaking user code.
13+
*
14+
* **Note:** Global inference profiles route requests to any supported AWS region for
15+
* optimal throughput. If your workload has data residency requirements, specify a
16+
* region-scoped inference profile explicitly.
17+
* @see https://docs.aws.amazon.com/bedrock/latest/userguide/cross-region-inference.html
918
*/
1019
export const BedrockModels = {
11-
/** Highest capability and best performance. Recommended default. Currently: Claude Opus 4.8. */
12-
DEFAULT: {
20+
/** Great tool use, balanced cost — good middle tier for most workloads. Currently: Claude Sonnet 4.6. */
21+
BALANCED: {
1322
provider: 'bedrock',
14-
modelId: 'us.anthropic.claude-opus-4-8-20250610-v1:0',
23+
modelId: BALANCED_MODEL_ID,
1524
},
16-
/** Strong quality/cost balance. Currently: Claude Sonnet 4. */
17-
BALANCED: {
25+
/** Highest capability for the hardest tasks. Currently: Claude Opus 4.8. */
26+
SMART: {
1827
provider: 'bedrock',
19-
modelId: 'us.anthropic.claude-sonnet-4-20250514-v1:0',
28+
modelId: SMART_MODEL_ID,
2029
},
21-
/** Fastest and lowest latency. Currently: Claude Haiku 4.5. */
30+
/** Lowest latency, still strong capabilities. Currently: Claude Haiku 4.5. */
2231
FAST: {
2332
provider: 'bedrock',
24-
modelId: 'us.anthropic.claude-haiku-4-5-20251001-v1:0',
33+
modelId: FAST_MODEL_ID,
34+
},
35+
36+
/** @deprecated Use `BedrockModels.BALANCED` instead. */
37+
DEFAULT: {
38+
provider: 'bedrock',
39+
modelId: BALANCED_MODEL_ID,
2540
},
26-
/** Low cost per token with acceptable quality. Currently: Amazon Nova Pro. */
41+
/** @deprecated Use `BedrockModels.FAST` instead. */
2742
BUDGET: {
2843
provider: 'bedrock',
29-
modelId: 'us.amazon.nova-pro-v1:0',
44+
modelId: FAST_MODEL_ID,
3045
},
31-
/** Ultra-cheap for simple tasks. Currently: Amazon Nova Lite. */
46+
/** @deprecated Use `BedrockModels.FAST` instead. */
3247
MICRO: {
3348
provider: 'bedrock',
34-
modelId: 'us.amazon.nova-lite-v1:0',
49+
modelId: FAST_MODEL_ID,
3550
},
3651
} as const satisfies Record<string, ModelConfig>;
3752

@@ -65,6 +80,7 @@ export const OllamaModels = {
6580
apiKey: 'ollama',
6681
},
6782
/** Strong reasoning at moderate size. Currently: DeepSeek R1 14B (~9 GB, needs 16 GB VRAM). */
83+
// TODO: DeepSeek R1 is strong at reasoning but weak at tool calling — swap to a more tool-capable model.
6884
MEDIUM: {
6985
provider: 'openai-api',
7086
modelId: 'deepseek-r1:14b',

test-apps/comprehensive/aws-blocks/index.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ const validatedJob = new AsyncJob(scope, 'validated-job', {
266266
});
267267

268268
// Agent BB - AI agent with tools and conversation persistence
269-
import { Agent } from '@aws-blocks/bb-agent';
269+
import { Agent, BedrockModels } from '@aws-blocks/bb-agent';
270270

271271
const agent = new Agent(scope, 'agent', {
272272
removalPolicy: 'destroy',
@@ -378,6 +378,23 @@ const cannedAgent = new Agent(scope, 'canned', {
378378
});
379379

380380

381+
// Preset agents — one per live BedrockModels preset, used to verify presets work e2e.
382+
// Skip the deprecated aliases (DEFAULT/BUDGET/MICRO) so we don't provision dead
383+
// agents that the suite never exercises.
384+
const LIVE_PRESETS = ['BALANCED', 'SMART', 'FAST'] as const;
385+
386+
const presetAgents = Object.fromEntries(
387+
LIVE_PRESETS.map((name) => [
388+
name,
389+
new Agent(scope, `preset-${name.toLowerCase()}`, {
390+
removalPolicy: 'destroy',
391+
inferenceOnly: true,
392+
model: { deployed: BedrockModels[name], local: { provider: 'canned' } },
393+
systemPrompt: 'Reply with exactly one word.',
394+
}),
395+
]),
396+
);
397+
381398
// Agent with model fallback — first candidate is unreachable, should fall through to canned
382399
const fallbackAgent = new Agent(scope, 'fallback', {
383400
removalPolicy: 'destroy',
@@ -1663,6 +1680,14 @@ export const api = new ApiNamespace(scope, 'api', (context) => ({
16631680
return { channelId, channel: await fallbackAgent.getChannel(channelId) };
16641681
},
16651682

1683+
async agentPresetStream(presetName: string, message: string) {
1684+
const presetAgent = presetAgents[presetName];
1685+
if (!presetAgent) throw new Error(`Unknown preset: ${presetName}. Available: ${Object.keys(presetAgents).join(', ')}`);
1686+
const result = await presetAgent.stream(message);
1687+
const done = await result.complete();
1688+
return { text: done.text ?? '' };
1689+
},
1690+
16661691
async agentTestApiKeyResolver() {
16671692
// Tests the AppSetting → apiKey resolver pattern used for secure API key storage.
16681693
// Puts a test value into the secret setting, then resolves it via the same

test-apps/comprehensive/test/agent.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,16 @@ export function agentTests(getApi: () => typeof apiType) {
321321
});
322322
});
323323

324+
describe('Bedrock Model Presets', () => {
325+
for (const presetName of ['BALANCED', 'SMART', 'FAST']) {
326+
test(`preset ${presetName} returns a response`, { timeout: 30_000 }, async () => {
327+
const api = getApi();
328+
const { text } = await api.agentPresetStream(presetName, 'hi');
329+
assert.ok(text && text.length > 0, `${presetName} should return a non-empty response`);
330+
});
331+
}
332+
});
333+
324334
describe('HITL — Tool Approval (deterministic)', () => {
325335
test('interrupt chunk arrives for tool with approval: always', { timeout: 15_000 }, async () => {
326336
const api = getApi();

0 commit comments

Comments
 (0)