Skip to content

feat: add Bedrock Mantle API format support for harness#1412

Merged
Hweinstock merged 7 commits into
aws:mainfrom
notgitika:feat/bedrock-mantle-api-format
Jun 9, 2026
Merged

feat: add Bedrock Mantle API format support for harness#1412
Hweinstock merged 7 commits into
aws:mainfrom
notgitika:feat/bedrock-mantle-api-format

Conversation

@notgitika

@notgitika notgitika commented May 28, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds apiFormat field to harness model configuration allowing users to select the API format for model invocation
  • Bedrock: converse_stream (default), responses, or chat_completions (via Bedrock Mantle)
  • OpenAI: responses (default) or chat_completions
  • When user selects responses or chat_completions for Bedrock, the CLI defaults model ID to openai.gpt-oss-120b and adds Bedrock Mantle IAM permissions to the harness execution role
  • Feature is gated behind isPreviewEnabled() since harness is in preview
  • Wire format matches the Loopy service Smithy model: apiFormat lives inside provider-specific model config (bedrockModelConfig / openAiModelConfig)

Changes

Area Files
Schema primitives/harness.ts, primitives/index.ts, agentcore-project.ts
API types aws/agentcore-harness.ts
Deploy mapper harness-mapper.ts
TUI wizard types.ts, useAddHarnessWizard.ts, AddHarnessScreen.tsx, AddHarnessFlow.tsx, useCreateFlow.ts
CLI command HarnessPrimitive.ts, harness-action.ts, harness-validate.ts
Validation (add) add/validate.ts
CDK IAM AgentCoreHarnessRole.ts (in agentcore-cdk repo)
CDK asset assets/cdk/bin/cdk.ts

Test plan

  • Unit tests pass (harness-validate: 22 tests, harness-mapper: 37 tests, 122 snapshot tests)
  • TypeScript compiles cleanly
  • CLI validation: bedrock + responses ✅, open_ai + responses ✅, open_ai + chat_completions ✅, open_ai + converse_stream ❌ (correctly rejected), gemini + any format ❌ (correctly rejected)
  • E2E: agentcore add harness --api-format responses → deploy → harness READY (account 998846730471, ap-southeast-2)
  • E2E: Invoke confirms routing through Bedrock Mantle (got IAM permission error on bedrock-mantle:CreateInference as expected — CDK IAM PR needed)
  • CDK repo changes need separate PR (tracked below)

Notes

  • CDK PR: https://github.com/aws/agentcore-l3-cdk-constructs/pull/231
  • Addresses review feedback from @davisarthur: OpenAI also supports apiFormat per the Smithy model (HarnessOpenAiApiFormat enum with responses and chat_completions)
  • converse_stream is omitted from API payloads for Bedrock (it's the default); responses is omitted for OpenAI (it's the default)

@notgitika notgitika requested a review from a team May 28, 2026 15:13
@github-actions github-actions Bot added size/m PR size: M agentcore-harness-reviewing AgentCore Harness review in progress labels May 28, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label May 28, 2026
@github-actions

github-actions Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Package Tarball

aws-agentcore-0.18.0.tgz

How to install

gh release download pr-1412-tarball --repo aws/agentcore-cli --pattern "*.tgz" --dir /tmp/pr-tarball
npm install -g /tmp/pr-tarball/aws-agentcore-0.18.0.tgz

@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label May 28, 2026
@github-actions github-actions Bot added size/m PR size: M and removed agentcore-harness-reviewing AgentCore Harness review in progress size/m PR size: M labels May 28, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label May 28, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label May 28, 2026
@notgitika

Copy link
Copy Markdown
Contributor Author

I have tested this E2E using the tarball

Comment thread src/cli/commands/create/harness-validate.ts Outdated
Comment thread src/cli/commands/create/harness-validate.ts
@github-actions github-actions Bot added size/m PR size: M and removed size/m PR size: M labels Jun 2, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Jun 2, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Jun 2, 2026
Comment thread src/cli/commands/add/validate.ts
padmak30
padmak30 previously approved these changes Jun 2, 2026
Comment thread src/cli/commands/add/validate.ts Outdated
Comment thread src/cli/commands/create/harness-action.ts Outdated
Comment thread src/cli/primitives/HarnessPrimitive.ts Outdated
Comment thread src/schema/schemas/primitives/harness.ts Outdated
notgitika added 4 commits June 8, 2026 13:55
Add apiFormat field to harness bedrockModelConfig that allows users to
select between converse_stream (default Bedrock), responses, or
chat_completions (Bedrock Mantle) when creating a harness.

- Schema: add BedrockApiFormatSchema enum and apiFormat field to HarnessModelSchema
- API types: add apiFormat to BedrockModelConfig interface
- Mapper: include apiFormat in bedrockModelConfig when not converse_stream
- TUI: add api-format wizard step for bedrock provider (gated behind isPreviewEnabled)
- CDK: add BedrockMantleInference/CallWithBearerToken IAM policies when mantle format selected
- CLI: add --api-format flag, default model ID to openai.gpt-oss-120b for mantle formats
- Validation: reject apiFormat for non-bedrock providers

E2E tested against account 998846730471 in ap-southeast-2.
- Schema: accepts/rejects apiFormat for bedrock/non-bedrock providers
- Mapper: verifies apiFormat included in bedrockModelConfig, omitted for converse_stream
- Validate: tests CLI --api-format flag validation
…n to add harness path

Address review feedback:
- Replace hardcoded format array with BedrockApiFormatSchema.options in create harness validation
- Add upfront apiFormat validation to validateAddHarnessOptions (add harness path)
  so invalid values fail early instead of falling through to a Zod ConfigValidationError
The Loopy service Smithy model defines HarnessOpenAiApiFormat with
responses and chat_completions values. This change extends the CLI to
support apiFormat for the open_ai provider in addition to bedrock.

- Add OpenAiApiFormatSchema (responses | chat_completions) and
  HarnessApiFormatSchema (union of all formats) to schema
- Update HarnessModelSchema superRefine to validate per-provider formats
- Add apiFormat field to OpenAiModelConfig API type
- Update deploy mapper to pass apiFormat in openAiModelConfig
- Update CLI validation (create + add) to accept apiFormat for open_ai
- Update TUI wizard to show format step for open_ai with correct options
- Fix default model ID logic: only use Bedrock Mantle model ID for
  bedrock provider, not open_ai
- Add tests for OpenAI apiFormat in validation and mapper
@github-actions github-actions Bot added the size/l PR size: L label Jun 8, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Jun 8, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Jun 8, 2026
Comment thread src/cli/commands/add/validate.ts Outdated
if (!allFormats.includes(options.apiFormat as (typeof allFormats)[number])) {
return {
valid: false,
error: `Invalid API format: ${options.apiFormat}. Use ${allFormats.join(', ')}`,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe OOS,but we should migrate the error to a typed error instead of a string to get better error classification in telemetry.

const VALID_GATEWAY_OUTBOUND_AUTH = ['awsIam', 'none', 'oauth'] as const;

export function validateAddHarnessOptions(options: AddHarnessCliOptions): ValidationResult {
if (options.apiFormat) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this validation logic live on the schema itself as either a discriminated union or part of the superRefine block? curious if we can get zod to do some of the heavy lifting here.

openAiModelConfig: {
modelId,
...(apiKeyArn && { apiKeyArn }),
...(apiFormat && apiFormat !== 'responses' && { apiFormat: apiFormat as 'responses' | 'chat_completions' }),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this cast is only necessary because the the validation logic isn't on the schema.

export const OpenAiApiFormatSchema = z.enum(['responses', 'chat_completions']);
export type OpenAiApiFormat = z.infer<typeof OpenAiApiFormatSchema>;

export const HarnessApiFormatSchema = z.enum(['converse_stream', 'responses', 'chat_completions']);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: isn't this just the union of the above in the general case?

padmak30
padmak30 previously approved these changes Jun 8, 2026
Hweinstock
Hweinstock previously approved these changes Jun 8, 2026
Add validateApiFormat() helper in the schema that delegates to
HarnessModelSchema.safeParse(), eliminating duplicated provider/format
checking in both validateAddHarnessOptions and validateCreateHarnessOptions.

The schema's superRefine remains the single source of truth for which
providers support which formats.
@notgitika notgitika dismissed stale reviews from Hweinstock and padmak30 via 80a2480 June 8, 2026 23:30
@github-actions github-actions Bot added size/m PR size: M and removed size/l PR size: L labels Jun 8, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Jun 8, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Jun 8, 2026
Hweinstock
Hweinstock previously approved these changes Jun 8, 2026
@Hweinstock

Hweinstock commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Nice, I like that the schema is the source of truth and the logic is centralized. Looks like we might be missing some cases based on some failing unit tests?:

⎯⎯⎯⎯⎯⎯⎯ Failed Tests 2 ⎯⎯⎯⎯⎯⎯⎯

 FAIL   unit  src/schema/schemas/primitives/__tests__/harness.test.ts > HarnessSpecSchema > rejects converse_stream for open_ai provider
AssertionError: expected false to be true // Object.is equality

- Expected
+ Received

- true
+ false

 ❯ src/schema/schemas/primitives/__tests__/harness.test.ts:757:110
    755|     expect(result.success).toBe(false);
    756|     if (!result.success) {
    757|       expect(result.error.issues.some(i => i.message.includes('convers…
       |                                                                                                              ^
    758|     }
    759|   });

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/2]⎯

 FAIL   unit  src/schema/schemas/primitives/__tests__/harness.test.ts > HarnessSpecSchema > rejects apiFormat for gemini provider
AssertionError: expected false to be true // Object.is equality

- Expected
+ Received

- true
+ false


 Test Files  1 failed | 107 passed (108)
 ❯ src/schema/schemas/primitives/__tests__/harness.test.ts:773:108
    771|     expect(result.success).toBe(false);
    772|     if (!result.success) {
    773|       expect(result.error.issues.some(i => i.message.includes('not sup…
       |                                                                                                            ^
    774|     }
    775|   });

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/2]⎯

      Tests  2 failed | 1856 passed (1858)
   Start at  23:30:56
   Duration  211.85s (transform 4.21s, setup 0ms, import 44.70s, tests 533.00s, environment 13ms)

@Hweinstock Hweinstock dismissed their stale review June 8, 2026 23:46

CI failing

@github-actions github-actions Bot added size/m PR size: M and removed size/m PR size: M labels Jun 8, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Jun 8, 2026
@notgitika

Copy link
Copy Markdown
Contributor Author

Nice, I like that the schema is the source of truth and the logic is centralized. Looks like we might be missing some cases based on some failing unit tests?:

⎯⎯⎯⎯⎯⎯⎯ Failed Tests 2 ⎯⎯⎯⎯⎯⎯⎯

 FAIL   unit  src/schema/schemas/primitives/__tests__/harness.test.ts > HarnessSpecSchema > rejects converse_stream for open_ai provider
AssertionError: expected false to be true // Object.is equality

- Expected
+ Received

- true
+ false

 ❯ src/schema/schemas/primitives/__tests__/harness.test.ts:757:110
    755|     expect(result.success).toBe(false);
    756|     if (!result.success) {
    757|       expect(result.error.issues.some(i => i.message.includes('convers…
       |                                                                                                              ^
    758|     }
    759|   });

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/2]⎯

 FAIL   unit  src/schema/schemas/primitives/__tests__/harness.test.ts > HarnessSpecSchema > rejects apiFormat for gemini provider
AssertionError: expected false to be true // Object.is equality

- Expected
+ Received

- true
+ false


 Test Files  1 failed | 107 passed (108)
 ❯ src/schema/schemas/primitives/__tests__/harness.test.ts:773:108
    771|     expect(result.success).toBe(false);
    772|     if (!result.success) {
    773|       expect(result.error.issues.some(i => i.message.includes('not sup…
       |                                                                                                            ^
    774|     }
    775|   });

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/2]⎯

      Tests  2 failed | 1856 passed (1858)
   Start at  23:30:56
   Duration  211.85s (transform 4.21s, setup 0ms, import 44.70s, tests 533.00s, environment 13ms)

yeah it was the error message wording that was different between the implementation and test.

@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Jun 8, 2026
@Hweinstock Hweinstock merged commit 78d1d58 into aws:main Jun 9, 2026
25 of 26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m PR size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants