Skip to content

Conversation

@ps06756
Copy link

@ps06756 ps06756 commented Dec 26, 2025

Summary

Fixes #148

Refactored ContentBlock from a single TypedDict with optional fields to a Union of 9 specific TypedDict classes. This enforces the Bedrock API constraint that each ContentBlock must contain exactly one content type (text, image, document, video, toolUse, toolResult, guardContent, reasoningContent, or citationsContent).

Changes

Type System Refactoring

  • Created specific TypedDict classes for each content type:

    • ContentBlockText - for text content
    • ContentBlockImage - for image content
    • ContentBlockDocument - for document content
    • ContentBlockVideo - for video content
    • ContentBlockToolUse - for tool use requests
    • ContentBlockToolResult - for tool results
    • ContentBlockGuardContent - for guardrail content
    • ContentBlockReasoningContent - for reasoning content
    • ContentBlockCitations - for citations
  • Added TypeGuard functions for type narrowing:

    • is_text_block(), is_image_block(), is_document_block(), etc.
    • These enable mypy to properly narrow the Union type when checking content blocks
  • Added CONTENT_BLOCK_KEYS constant for runtime validation of content block fields

Code Updates

  • Updated all model providers (~10 files) to use type guard functions instead of dictionary key checks
  • Updated test files (~5 files) to use ContentBlockText for instantiation instead of the now-removed ContentBlock() constructor
  • Fixed agent.py to use CONTENT_BLOCK_KEYS instead of accessing __annotations__ on the Union type

Type Safety Improvements

Before:

# ContentBlock allowed empty or multiple fields
content = ContentBlock()  # Valid but wrong
content = ContentBlock(text="hello", image=...)  # Valid but wrong

After:

# Must use specific types - enforces exactly one content field
content = ContentBlockText(text="hello")  # Correct
content = ContentBlockImage(image=...)  # Correct
content = ContentBlock()  # Type error - cannot instantiate Union

Testing

  • ✅ All unit tests passing (1638 passed, 1 pre-existing failure)
  • ✅ Zero mypy type errors across all 113 source files
  • ✅ All linting checks passing (hatch fmt --linter)
  • ✅ All formatting checks passing (hatch fmt --formatter)
  • ✅ Baseline test comparison confirms no regressions introduced

Breaking Changes

⚠️ Minor breaking change for code constructing ContentBlocks:

Code that was using ContentBlock(text="...") must now use ContentBlockText(text="...").

This affects:

  • Direct instantiation of ContentBlock
  • Any code that was relying on the ability to create empty or multi-field ContentBlocks

Migration:

# Old
from strands.types.content import ContentBlock
block = ContentBlock(text="hello")

# New
from strands.types.content import ContentBlockText
block = ContentBlockText(text="hello")

The ContentBlock type itself remains importable as the Union type for type annotations.

Rationale

The previous implementation using TypedDict with total=False was too permissive:

  • Allowed empty ContentBlocks (no content at all)
  • Allowed multiple content types in a single block
  • Did not match the Bedrock API specification

The new Union type implementation:

  • Enforces exactly one content type per block at the type level
  • Provides better IDE autocomplete and type checking
  • Matches the actual API contract
  • Maintains full backward compatibility for type annotations

Refactored ContentBlock from a single TypedDict with optional fields
to a Union of 9 specific TypedDict classes to enforce the Bedrock API
constraint that each ContentBlock must contain exactly one content type.

Changes:
- Created specific TypedDict classes: ContentBlockText, ContentBlockImage,
  ContentBlockDocument, ContentBlockVideo, ContentBlockToolUse,
  ContentBlockToolResult, ContentBlockGuardContent,
  ContentBlockReasoningContent, ContentBlockCitations
- Added TypeGuard functions to enable proper type narrowing
- Added CONTENT_BLOCK_KEYS constant for runtime validation
- Updated all model providers to use type guard functions
- Updated tests to use ContentBlockText for instantiation
- Fixed agent.py to use CONTENT_BLOCK_KEYS instead of __annotations__

This ensures type safety and prevents invalid ContentBlock structures
while maintaining full mypy compliance with zero type errors.
@ps06756 ps06756 force-pushed the fix/issue-148-contentblock-union-type branch from 2e0aece to 771e916 Compare December 26, 2025 20:37
@github-actions github-actions bot added size/l and removed size/l labels Dec 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TASK] Convert ContentBlock TypedDict into Union

1 participant