@flink-app/anthropic-adapter
v2.0.0-alpha.131
Published
Anthropic Claude adapter for Flink AI framework
Readme
@flink-app/anthropic-adapter
Anthropic Claude adapter for the Flink AI framework. Provides seamless integration with Anthropic's Claude models.
Installation
npm install @flink-app/anthropic-adapter
# or
pnpm add @flink-app/anthropic-adapterThe @anthropic-ai/sdk package is included as a dependency, so you don't need to install it separately.
Usage
Basic Setup
import { AnthropicAdapter } from "@flink-app/anthropic-adapter";
import { FlinkApp } from "@flink-app/flink";
const app = new FlinkApp({
ai: {
llms: {
default: new AnthropicAdapter({
apiKey: process.env.ANTHROPIC_API_KEY!,
model: "claude-sonnet-4-5-20250929"
}),
},
},
});
await app.start();Legacy API (still supported):
// Backward-compatible constructor
new AnthropicAdapter(process.env.ANTHROPIC_API_KEY!, "claude-sonnet-4-5-20250929")Agent Instructions
Define your agent's behavior using the instructions property:
// src/agents/support_agent.ts
export const Agent: FlinkAgentProps = {
name: "support_agent",
instructions: "You are a helpful customer support agent.",
tools: ["get_order_status"],
model: { adapterId: "default" },
};How it works:
- Instructions are prepended as a system message to every conversation
- Follows Vercel AI SDK pattern for consistency
- Provides stable agent behavior across all interactions
Dynamic Context with System Messages
For per-request context, add system messages to the conversation:
const result = await ctx.agents.myAgent.execute({
message: [
{ role: "system", content: "Current user tier: Premium" },
{ role: "user", content: "What can I do?" }
]
});Order of messages sent to Anthropic:
- Agent
instructions(as system message) - User-provided system messages (if any)
- Conversation messages
This gives you both static agent behavior and dynamic per-request context.
Multiple Adapters
You can register multiple Anthropic adapters with different configurations:
import { AnthropicAdapter } from "@flink-app/anthropic-adapter";
import { FlinkApp } from "@flink-app/flink";
const app = new FlinkApp({
ai: {
llms: {
// Default Sonnet 4.5 model - best balance of intelligence, speed, and cost
default: new AnthropicAdapter({
apiKey: process.env.ANTHROPIC_API_KEY!,
model: "claude-sonnet-4-5-20250929"
}),
// Fast Haiku 4.5 model for simple tasks - near-frontier performance at lower cost
fast: new AnthropicAdapter({
apiKey: process.env.ANTHROPIC_API_KEY!,
model: "claude-haiku-4-5-20251001"
}),
// Opus 4.5 model for complex tasks - maximum intelligence
smart: new AnthropicAdapter({
apiKey: process.env.ANTHROPIC_API_KEY!,
model: "claude-opus-4-5-20251101"
}),
},
},
});Using in Agents
Reference the adapter by its registered ID in your agent configuration:
// src/agents/support_agent.ts
import { FlinkAgentProps } from "@flink-app/flink";
export const Agent: FlinkAgentProps = {
name: "support_agent",
description: "Customer support assistant",
instructions: "You are a helpful customer support agent.",
tools: ["get_order_status"],
model: {
adapterId: "default", // Uses the "default" adapter
maxTokens: 1000,
temperature: 0.7,
},
};Supported Models
This adapter works with all Claude models. The latest Claude 4.5 models offer significant improvements over previous generations:
Claude 4.5 Models (Recommended)
Claude Sonnet 4.5:
claude-sonnet-4-5-20250929(recommended for most use cases)- Best balance of intelligence, speed, and cost
- Exceptional performance in coding and agentic tasks
- 200K context window (1M in beta with header)
- Pricing: $3/MTok input, $15/MTok output
Claude Haiku 4.5:
claude-haiku-4-5-20251001(fastest)- Near-frontier performance at a fraction of the cost
- Optimized for speed and cost-efficiency
- 200K context window
- Pricing: $1/MTok input, $5/MTok output
Claude Opus 4.5:
claude-opus-4-5-20251101(most capable)- Maximum intelligence for complex tasks
- Industry leader in coding, agents, and enterprise workflows
- 200K context window
- Pricing: $5/MTok input, $25/MTok output
Legacy Models
For backwards compatibility, legacy Claude 3 and 4 models are still supported:
- Claude Opus 4.1:
claude-opus-4-1-20250805 - Claude Sonnet 4:
claude-sonnet-4-20250514 - Claude 3.7 Sonnet:
claude-3-7-sonnet-20250219 - Claude Opus 4:
claude-opus-4-20250514 - Claude 3 Haiku:
claude-3-haiku-20240307
We recommend migrating to Claude 4.5 models for improved performance and capabilities.
Model Aliases vs Specific Versions
Anthropic provides both specific version IDs and aliases:
Specific versions (e.g.,
claude-sonnet-4-5-20250929): Fixed model snapshots that never change- Recommended for production to ensure consistent behavior
- Snapshot date indicates the exact model version
Aliases (e.g.,
claude-sonnet-4-5): Automatically point to the latest snapshot- Useful for development and experimentation
- Updated within a week of new releases
- May change behavior when new snapshots are released
Example:
// Production: Use specific version for stability
new AnthropicAdapter(apiKey, "claude-sonnet-4-5-20250929")
// Development: Use alias to always get latest improvements
new AnthropicAdapter(apiKey, "claude-sonnet-4-5")Cache Metrics & Cost Optimization
The Anthropic adapter automatically captures detailed cache metrics to help you optimize costs and understand performance:
const result = await ctx.agents.myAgent.execute({
message: "Analyze this large document..."
});
console.log(result.usage);
// {
// inputTokens: 3000,
// outputTokens: 500,
// cachedInputTokens: 2000, // Tokens read from cache (10% cost)
// cacheCreationInputTokens: 500 // Tokens written to cache (125% cost)
// }
// Calculate actual billed tokens with Anthropic's pricing
const regularTokens = result.usage.inputTokens -
(result.usage.cachedInputTokens || 0) -
(result.usage.cacheCreationInputTokens || 0);
const cacheReadCost = (result.usage.cachedInputTokens || 0) * 0.1;
const cacheWriteCost = (result.usage.cacheCreationInputTokens || 0) * 1.25;
const regularCost = regularTokens * 1.0;
console.log(`Cost breakdown:`);
console.log(`- Regular tokens: ${regularTokens} × 100% = ${regularCost}`);
console.log(`- Cache reads: ${result.usage.cachedInputTokens} × 10% = ${cacheReadCost}`);
console.log(`- Cache writes: ${result.usage.cacheCreationInputTokens} × 125% = ${cacheWriteCost}`);
console.log(`Total cost factor: ${regularCost + cacheReadCost + cacheWriteCost}`);
// "Total cost factor: 1325" (vs 3000 without caching) - 56% savings after initial cache write!Cache Metrics:
inputTokens: Total input tokens (including cached and cache writes)outputTokens: Total output tokenscachedInputTokens: Input tokens read from cache (10% cost)cacheCreationInputTokens: Input tokens written to cache (125% cost, one-time)
Anthropic Prompt Caching:
- Cache reads: 10% of regular token cost (90% savings!)
- Cache writes: 125% of regular token cost (one-time investment)
- Minimum threshold: 2048 tokens (caching only activates at ≥2048 tokens)
- Cache lifetime: 5 minutes of inactivity
Caching Strategy: For repeated context (system prompts, knowledge bases, few-shot examples):
- First request: Pay 125% to write to cache (
cacheCreationInputTokens) - Subsequent requests (within 5 min): Pay 10% to read from cache (
cachedInputTokens) - Break-even: After 2 cache reads, you start saving money
- Long-term: Massive savings for frequently-used context
When Caching Helps Most:
- Long system prompts used across requests
- Large knowledge bases in context
- Few-shot examples in prompts
- Multi-turn conversations with persistent context
- Documents analyzed multiple times
Example: System Prompt Caching
// First request: Create cache
// inputTokens: 3000, cacheCreationInputTokens: 2500
// Cost: 500×1.0 + 2500×1.25 = 3625 tokens worth
// Second request: Read from cache
// inputTokens: 3000, cachedInputTokens: 2500
// Cost: 500×1.0 + 2500×0.1 = 750 tokens worth (79% savings!)
// Third+ requests: Continue reading from cache
// Same 79% savings as long as cache is activeExtended Thinking
Claude's thinking is configured per adapter and passed through verbatim as the thinking request parameter, with effort sent as output_config.effort. Both use the SDK's own types, so every mode the API supports is available without adapter changes:
const adapter = new AnthropicAdapter({
apiKey: process.env.ANTHROPIC_API_KEY!,
model: "claude-opus-5-5",
thinking: { type: "adaptive", display: "summarized" }, // optional
effort: "high", // optional: low | medium | high | xhigh | max
});When omitted, neither parameter is sent and the model's default applies (the Claude 5 generation runs adaptive thinking by default).
How thinking flows through Flink:
thinkingblocks are streamed asreasoningchunks and surfaced by the agent asreasoning_deltaevents onfullStream. They never appear intextStreamorresult.message. Withdisplay: "omitted"(the default on current models) the blocks arrive with empty text but still carry a signature.- Every
thinkingandredacted_thinkingblock is kept verbatim (providerData.anthropic.thinkingBlocks) on the assistant message and replayed unchanged ahead of the text and tool calls on later turns, which Anthropic requires for signatures to stay valid across tool use. - Thinking traces produced by another provider carry no Anthropic signature and are skipped.
Structured Output and Tool Choice
Both are requested per call by the agent (model.structuredOutput / model.toolChoice, or execute({ model: { ... } })); nothing is configured on the adapter for the common case.
Structured output is fulfilled in one of two ways, chosen by structuredOutputMode:
| Mode | What is sent |
|------|--------------|
| "native" | output_config.format: { type: "json_schema", schema } — the Messages API structured outputs feature (Sonnet 4.5, Opus 4.1, Haiku 4.5 and later) |
| "tool" | A single tool named after the format with the schema as input_schema, forced with tool_choice. The resulting tool call is turned back into a JSON text reply, so the agent sees it exactly like a native reply. Works on every tool-capable model, including Claude 3.x |
| "auto" (default) | "native" when the configured model supports it, otherwise "tool" |
new AnthropicAdapter({
apiKey: process.env.ANTHROPIC_API_KEY!,
model: "claude-3-5-sonnet-20241022",
structuredOutputMode: "tool", // optional, auto-detected from the model by default
});The native path reduces the schema to the structured outputs subset: numeric and length constraints (minimum, maxLength, pattern, ...) are stripped and every object gets additionalProperties: false. Flink still validates the reply against the full schema, so a value outside the bounds fails with StructuredOutputError rather than slipping through.
When the agent also has real tools, the fallback sends tool_choice: { type: "any" } so the model can still call those first and finish with the output tool. A caller's own toolChoice is respected: "none" leaves only the output tool, a forced real tool stays forced for that step. While extended thinking is active (configured, or the Claude 5 default) the API rejects forcing, so the fallback asks for the output tool in the system prompt instead and relies on Flink's validation.
Tool choice maps onto tool_choice: "auto" → auto, "none" → none, "required" → any, { type: "tool", name } → tool. It is only sent when the request carries tools. Note that the API rejects a forcing choice (any / tool) while extended thinking is active, which is the default on the Claude 5 generation; prefer structuredOutput when the goal is a JSON reply.
Features
- ✅ Full tool calling support with automatic schema sanitization
- ✅ Extended thinking passthrough (
thinking,effort) with thinking streamed asreasoning_deltaand replayed verbatim across turns - ✅ Streaming with event-based updates
- ✅ Proper message conversion and system instruction handling
- ✅ Comprehensive error handling with helpful messages
- ✅ Token usage tracking
- ✅ Debug logging for tool calls
- ✅ Support for all Claude 4.5 models
- ✅ 200K token context window (1M in beta)
- ✅ Extended thinking capabilities
Claude 4.5 Capabilities
All Claude 4.5 models support:
- Extended Thinking: Enhanced reasoning for complex problems
- Vision: Process and analyze images alongside text
- Multilingual: Strong performance across multiple languages
- Long Context: 200K tokens standard, 1M tokens in beta
- Tool Use: Sophisticated function calling and tool integration
API
AnthropicAdapter
interface AnthropicAdapterOptions {
apiKey: string;
model: string;
thinking?: Anthropic.ThinkingConfigParam; // e.g. { type: "adaptive", display: "summarized" }
effort?: "low" | "medium" | "high" | "xhigh" | "max";
debug?: boolean;
}
class AnthropicAdapter implements LLMAdapter {
constructor(options: AnthropicAdapterOptions);
constructor(apiKey: string, model: string); // Legacy
}Parameters
apiKey: Your Anthropic API keymodel: The Claude model to use (e.g., "claude-sonnet-4-5-20250929")thinking: Optional extended thinking configuration, sent verbatim asthinkingeffort: Optional effort level, sent asoutput_config.effortdebug: Optional debug logging (defaultfalse)
Error Handling
The adapter provides comprehensive error handling with helpful messages:
- 400 Bad Request: Invalid model name, malformed messages, or invalid tool schemas
- 401 Unauthorized: Invalid API key - check your ANTHROPIC_API_KEY
- 403 Forbidden: Insufficient permissions or account limitations
- 429 Too Many Requests: Rate limit exceeded - consider retry logic
- 500/529 Server Error: Temporary issue with Anthropic's servers - retry recommended
All errors include the model name and specific guidance for resolution.
Model Selection Tips
- Use Sonnet 4.5 (
claude-sonnet-4-5-20250929) as your default - it provides the best balance for most applications - Use Haiku 4.5 (
claude-haiku-4-5-20251001) for high-volume, cost-sensitive workloads where speed matters - Use Opus 4.5 (
claude-opus-4-5-20251101) for complex reasoning, advanced coding, or when maximum intelligence is required
You can also use model aliases like claude-sonnet-4-5 which automatically point to the latest snapshot, though specific version IDs are recommended for production to ensure consistent behavior.
Migrating from Claude 3 to Claude 4.5
If you're currently using Claude 3 models, simply update the model ID in your adapter configuration:
// Before (Claude 3.5 Sonnet)
new AnthropicAdapter(apiKey, "claude-3-5-sonnet-20241022")
// After (Claude Sonnet 4.5)
new AnthropicAdapter(apiKey, "claude-sonnet-4-5-20250929")Benefits of upgrading to Claude 4.5:
- Better performance: Improved reasoning and coding capabilities
- Extended thinking: Built-in support for complex problem-solving
- Lower latency: Faster response times (especially Haiku 4.5)
- Better cost efficiency: Haiku 4.5 offers near-frontier performance at lower cost
The adapter handles all API differences automatically - no code changes required beyond the model ID.
Requirements
- Node.js >= 18
- @flink-app/flink >= 1.0.0
- @anthropic-ai/sdk >= 0.30.0
License
MIT
