Skip to content

Conversation

@chaynabors
Copy link
Member

@chaynabors chaynabors commented Dec 29, 2025

Motivation

This PR integrates the Anthropic SDK to enable support for Claude models (e.g., Sonnet 3.5, Opus). While the SDK currently supports OpenAI and Bedrock, Anthropic is a highly requested provider, particularly for its strong reasoning capabilities, prompt caching, and native PDF support.

This implementation maps the SDK's abstract block types (including CachePointBlock and ReasoningBlock) to Anthropic's specific API requirements, allowing users to switch providers without rewriting message construction logic.

Resolves: #247

Public API Changes

A new AnthropicModel class is now available via a new entry point.

import { AnthropicModel } from '@strands-agents/sdk/anthropic'

// Basic instantiation
const model = new AnthropicModel({
  apiKey: process.env.ANTHROPIC_API_KEY, // Optional if set in env
  modelId: 'claude-sonnet-4-5-20250929',
  maxTokens: 4096,
})

// Support for Thinking (Reasoning)
const thinkingModel = new AnthropicModel({
  modelId: 'claude-sonnet-4-5-20250929',
  params: {
    thinking: { type: 'enabled', budget_tokens: 1024 }
  }
})

Prompt Caching behavior

The provider implements a lookahead strategy to support the SDK's CachePointBlock abstraction. It automatically attaches cache_control to the preceding content block as required by the Anthropic API.

// SDK Abstraction
const content = [
  new TextBlock(hugeContext),
  new CachePointBlock({ cacheType: 'ephemeral' }) 
]

// Transforms to Anthropic request:
// { type: 'text', text: hugeContext, cache_control: { type: 'ephemeral' } }

Use Cases

  • Cost Optimization: Leveraging CachePointBlock with Anthropic's prompt caching to reduce costs on long-context tasks (e.g., RAG or large system prompts).
  • Document Analysis: Native support for PDF processing via DocumentBlock.
  • Complex Reasoning: Access to Claude's "thinking" mode for tasks requiring extended chain-of-thought.

Implementation Notes

  • Binary Handling: Updated encodeBase64 utility to better handle Uint8Array inputs directly. This prevents stack overflow errors previously caused by spreading large arrays into String.fromCharCode when processing high-resolution images or PDFs.

// We convert bytes to binary string in chunks to satisfy btoa()
const CHUNK_SIZE = 0x8000 // 32k chunks
let binary = ''
for (let i = 0; i < input.length; i += CHUNK_SIZE) {
Copy link
Member

Choose a reason for hiding this comment

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

Could we use toBase64 for browser compatibility instead of chunking?

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/toBase64

Copy link
Member Author

Choose a reason for hiding this comment

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

Since ⁨September 2025⁩, this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.

I don't know, you tell me.

name: 'BedrockModel',
get skip() {
return inject('provider-bedrock').shouldSkip
return inject('provider-bedrock')?.shouldSkip ?? true
Copy link
Member

Choose a reason for hiding this comment

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

Why switch to optional chaining here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Inject can return undefined which crashes our test suite if it fails. Not sure why we weren't seeing a type error before this. You tell me if I misread it.

Copy link
Member

Choose a reason for hiding this comment

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

Not sure why we weren't seeing a type error before this.

How did the type error surface? If I pull locally and run:

npm run type-check

I'm not seeing an error with the original code

Inject can return undefined which crashes our test suite if it fails

In that case we might want it to fail - it would indicate that vite failed to inject values which means our tests are misconfigured.

Copy link
Member

Choose a reason for hiding this comment

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

Let's revert this otherwise

Copy link
Collaborator

@strands-agent strands-agent left a comment

Choose a reason for hiding this comment

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

Review Summary

👍 Great implementation! All tests pass and the code quality is excellent. There are 6 review threads that need addressing before this can merge. I've added specific guidance below to help you address each one.


📝 Action Items

1. Update PR Description ⭐ High Priority

Current: PR description is empty
Required: Follow the PR.md template

Suggested PR Description:

## Motivation

Anthropics's Claude models are widely used and offer competitive performance, especially for reasoning-heavy tasks. However, the SDK currently only supports Bedrock and OpenAI providers, requiring developers to either:
1. Use Claude through AWS Bedrock (adding infrastructure dependency)
2. Implement custom workarounds
3. Not use Strands SDK

Adding native Anthropic support enables direct Claude API integration without AWS dependency.

Resolves: #[issue-number if applicable]

## Public API Changes

Adds `AnthropicModel` class for direct Anthropic API integration:

```typescript
import { AnthropicModel } from '@strands-agents/sdk/anthropic'

const model = new AnthropicModel({
  modelId: 'claude-3-5-sonnet-20241022',
  apiKey: process.env.ANTHROPIC_API_KEY,
  temperature: 0.7,
  maxTokens: 4096
})

Key features:

  • Streaming and non-streaming responses
  • Tool/function calling support
  • Image input support (base64 encoding)
  • Context window overflow detection
  • Full TypeScript type safety

Follows OpenAI provider patterns for consistency.

Use Cases

  • Direct Claude API usage: Use Claude without AWS Bedrock infrastructure
  • Model comparison: Easily switch between OpenAI and Anthropic models
  • Cost optimization: Choose Claude models for specific use cases
  • Regional availability: Access Claude in regions where Bedrock isn't available

---

### 2. Make `@anthropic-ai/sdk` Optional Dependency ⭐ **High Priority**

**Current**: Listed in `dependencies`  
**Required**: Make optional (like `openai`)

**Why**: Users shouldn't need to install Anthropic SDK if they only use Bedrock/OpenAI.

**Changes needed**:

**a) `package.json`**: Move to `peerDependencies` + `peerDependenciesMeta`

```json
"peerDependencies": {
  "@anthropic-ai/sdk": "^0.38.0",
  "openai": "^4.73.0"
},
"peerDependenciesMeta": {
  "@anthropic-ai/sdk": {
    "optional": true
  },
  "openai": {
    "optional": true
  }
}

b) Add to package.json exports: (already have ./openai, add ./anthropic)

"./anthropic": {
  "types": "./dist/src/models/anthropic.d.ts",
  "default": "./dist/src/models/anthropic.js"
}

c) Remove from src/index.ts: Don't export from main entry point

d) Users import directly:

// Instead of:
import { AnthropicModel } from '@strands-agents/sdk'

// Users do:
import { AnthropicModel } from '@strands-agents/sdk/anthropic'

This matches the OpenAI pattern exactly! 👍


3. Fix Test File Name ⭐ High Priority

Current: test/integ/anthropic.ts
Required: test/integ/anthropic.test.ts

This ensures the test runner picks it up correctly.


4. API Key Callback Support 🔍 Needs Investigation

Question from @zastrowm: Does Anthropic SDK support API key callbacks like OpenAI?

OpenAI pattern (for reference):

import type { ApiKeySetter } from 'openai/client'

export interface OpenAIModelOptions {
  apiKey?: string | ApiKeySetter  // <-- Can be function
}

This enables:

  • Dynamic key rotation
  • Secret manager integration
  • Runtime credential refresh

Action needed: Check if @anthropic-ai/sdk supports similar functionality.

How to check:

  1. Look at Anthropic SDK types: node_modules/@anthropic-ai/sdk/index.d.ts
  2. Check their ClientOptions interface
  3. If they support it, add the same pattern
  4. If not, document in code comment: // Note: Anthropic SDK does not support dynamic API key callbacks yet

5. Use toBase64 for Browser Compatibility 🔍 Investigate

Current: Custom chunking implementation in media.ts
Suggested: Use built-in Uint8Array.toBase64() method

Why: Better browser compatibility, less custom code.

Reference: MDN - Uint8Array.toBase64()

However: This is a newer API (2023), so check:

  1. Browser compatibility requirements for the SDK
  2. If targeting older browsers, current chunking may be necessary
  3. Could add polyfill or feature detection

Suggested approach:

export function encodeBase64(data: Uint8Array): string {
  // Use native method if available (modern browsers/Node 22+)
  if (typeof data.toBase64 === 'function') {
    return data.toBase64()
  }
  
  // Fallback to chunking for older environments
  return chunkEncode(data)
}

6. Optional Chaining Change in openai.ts 🤔 Clarification Needed

Question from @zastrowm: Why was optional chaining added?

// What changed and why?
myProperty?.something

Please explain the reasoning in the PR thread. If it was unintentional, consider reverting to keep the PR focused on Anthropic support only.


✅ What's Already Great

  • 🎉 All CI checks passing (10/10 green!)
  • 📝 Comprehensive tests: Unit + integration tests
  • 🎯 Follows patterns: Matches OpenAI provider structure
  • 🔒 Type-safe: Full TypeScript support
  • Streaming support: Handles streaming responses correctly
  • 🛠️ Tool calling: Implements function/tool use
  • 🖼️ Image support: Base64 encoding for images
  • 🚨 Error handling: Context window overflow detection

Next Steps

  1. Update PR description (use template above as starting point)
  2. Make dependency optional (follow OpenAI pattern)
  3. Rename test file (anthropic.tsanthropic.test.ts)
  4. Investigate API key callbacks (check Anthropic SDK types)
  5. Consider toBase64 approach (or document why chunking is needed)
  6. Explain optional chaining change (or revert if unintentional)

Once these are addressed, this will be ready to merge! The core implementation is solid. 🚀


Questions?

Feel free to ask if you need clarification on any of these points. The maintainers and community are here to help! 👍

🦆


🤖 This is an experimental AI agent response from the Strands team, powered by Strands Agents. We're exploring how AI agents can help with community support and development. Your feedback helps us improve! If you'd prefer human assistance, please let us know.

expect(fullText.toLowerCase()).toContain('yellow')
})

it('processes PDF document input correctly', async () => {
Copy link
Member

Choose a reason for hiding this comment

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

nit: Can we move this to the agent.test.ts file? This should be tested across all model providers.

})
})

describe('Agent Tool Use', () => {
Copy link
Member

Choose a reason for hiding this comment

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

nit: We already test tools in agent.test.ts, we dont need the duplication here.

Comment on lines -9 to -10

// Import fixtures using Vite's ?url suffix
Copy link
Member

Choose a reason for hiding this comment

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

nit: Why remove this? I wouldnt know what this is without this comment


const request: Anthropic.MessageStreamParams = {
model: this._config.modelId,
max_tokens: this._config.maxTokens ?? 1024,
Copy link
Member

Choose a reason for hiding this comment

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

nit: This should already be set. We have a default of 4096 above, which is honestly kinda low.

I would either require it as input (same as what we do in python), or create a map of different anthropic model ids to their max tokens. Seems like Haiku 3 is the only one that has a max of 4096, so we could also just do a check that if its haiku, we set it to 4096, otherwise we set it to 32k

https://platform.claude.com/docs/en/about-claude/models/overview

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Anthropic Model Provider

4 participants