# How FreeLLMAPI Maps Anthropic's Messages Format: Implementation Guide

> Learn how FreeLLMAPI maps Anthropic's messages format using a three-stage pipeline. Discover its implementation for Claude models, Zod validation, and unified ChatMessage structure.

- Repository: [Tashfeen/freellmapi](https://github.com/tashfeenahmed/freellmapi)
- Tags: implementation-guide
- Published: 2026-08-27

---

**FreeLLMAPI maps Anthropic's messages format through a three-stage pipeline that resolves Claude model families via `resolveAnthropicModel()`, validates requests against a Zod `messagesSchema`, and normalizes system messages and tool calls into a unified internal `ChatMessage` structure before provider dispatch.**

FreeLLMAPI is an open-source LLM proxy that standardizes requests across multiple providers. When handling Anthropic Claude API requests, the service converts the native `/v1/messages` payload into its internal representation using specialized mapping utilities in [`server/src/services/anthropic-map.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/anthropic-map.ts) and route handlers in [`server/src/routes/anthropic.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/routes/anthropic.ts).

## Claude Family Resolution and Model Mapping

### Defining Claude Families and Aliases

In [`server/src/services/anthropic-map.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/anthropic-map.ts), the service maintains the `CLAUDE_FAMILIES` constant defining four supported families: `default`, `opus`, `sonnet`, and `haiku`. The `CLAUDE_FAMILY_ALIASES` mapping associates each family with concrete model identifiers such as `anthropic/claude-3-opus-20240229`. The `claudeFamilyDiscoveryEntries()` function generates discovery metadata used to enumerate available models for client selection.

### Resolving Model Identifiers with resolveAnthropicModel

The `resolveAnthropicModel()` function processes incoming model names to return a `ResolvedAnthropicModel` object. When a client sends a family-style identifier like `claude-3-sonnet`, the function selects the highest-specificity matching model from the alias map. For explicit model IDs, it validates against the map and sets the `pinned` flag to **true**, indicating a user-specified selection rather than a family default. Unknown inputs fall back to the default family entry, ensuring robust request handling.

```typescript
import { resolveAnthropicModel } from './services/anthropic-map.js';

const { modelId, family, pinned } = resolveAnthropicModel('claude-3-opus-20240229');
// modelId: 'anthropic/claude-3-opus-20240229'
// family: 'opus'
// pinned: true

```

## Converting Anthropic Messages to Internal Chat Format

### Request Validation with Zod Schemas

The route handler in [`server/src/routes/anthropic.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/routes/anthropic.ts) validates incoming payloads against `messagesSchema`, a strict Zod schema enforcing the Anthropic API contract. This validation layer ensures type safety for fields like `system`, `messages`, and `tool` definitions before conversion begins.

### Normalizing System Message Placement

Anthropic's API accepts system prompts either as a top-level `system` string or as role-based entries within the messages array. FreeLLMAPI collapses both representations into a single consolidated system message prepended to the internal chat history. The parser first checks for the standalone `system` field, then iterates through the message array to collect any additional system turns, combining them into the standardized `ChatMessage` format with role `system`.

```typescript
import { messagesSchema } from './routes/anthropic.js';

const parsed = messagesSchema.safeParse(req.body);
if (!parsed.success) throw new BadRequestError(parsed.error);

const { system, messages: rawMsgs } = parsed.data;
const chat = [];

if (system) {
  chat.push({ role: 'system', content: system });
}

for (const msg of rawMsgs) {
  if (msg.role === 'system') {
    chat.push({ role: 'system', content: msg.content });
    continue;
  }
  chat.push({ role: msg.role, content: msg.content });
}

```

### Translating Tool Calls and Results

Tool invocations undergo conversion to OpenAI-compatible message structures. When processing a message containing a `tool` object, the handler executes the tool and appends the results as assistant-role messages containing the output content. This transformation allows FreeLLMAPI to handle Anthropic's native tool use format while maintaining compatibility with providers expecting OpenAI-style function call conventions.

```typescript
// Inside the message processing loop
if (msg.tool) {
  const toolResult = await runTool(msg.tool);
  chat.push(...toolResult); // assistant-role messages with tool outputs
}

```

### Processing Document Blocks

For requests containing base64-encoded PDFs or other media types, [`server/src/lib/anthropic-documents.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/anthropic-documents.ts) provides `convertDocumentBlock()` to process Anthropic-style document blocks before they enter the chat payload. This ensures multimedia content is properly formatted for downstream providers that may expect different encoding standards.

## Provider Dispatch Integration

After mapping and normalization, the pipeline forwards the standardized payload to the appropriate provider handler. The resolved `modelId` (e.g., `anthropic/claude-3-sonnet-20240229`) and normalized `chat` array are passed to the streaming completion service, which executes the actual API call using the converted format.

```typescript
import { streamChatCompletion } from './providers/claude.js';

await streamChatCompletion({
  apiKey: req.headers['x-api-key'],
  modelId,
  messages: chat,
  options: { temperature: 0.7 }
});

```

## Summary

- **Model Resolution**: [`server/src/services/anthropic-map.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/anthropic-map.ts) defines `CLAUDE_FAMILIES` and `CLAUDE_FAMILY_ALIASES` to map family identifiers to specific model IDs via `resolveAnthropicModel()`.
- **System Message Handling**: The converter collapses both top-level and in-array system messages into a single prepended system message in the internal format.
- **Tool Translation**: Anthropic tool calls are transformed into OpenAI-compatible assistant-role messages with tool results appended to the chat history.
- **Validation**: Zod schemas in [`server/src/routes/anthropic.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/routes/anthropic.ts) enforce type safety on incoming Anthropic payloads before processing.
- **Document Support**: [`server/src/lib/anthropic-documents.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/anthropic-documents.ts) handles specialized document block conversion for multimodal inputs.

## Frequently Asked Questions

### How does resolveAnthropicModel handle unknown model names?

If the input string does not match any entry in `CLAUDE_FAMILY_ALIASES`, the function falls back to the default family mapping entry as defined in [`server/src/services/anthropic-map.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/anthropic-map.ts). This ensures the request proceeds with a valid model ID rather than failing outright, providing graceful degradation for ambiguous client inputs.

### What happens to system messages in the Anthropic format conversion?

The conversion logic in [`server/src/routes/anthropic.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/routes/anthropic.ts) extracts system content from both the top-level `system` field and any in-array system role entries. It collapses these into a single system message that is prepended to the internal chat history array, maintaining conversation context while standardizing the structure for downstream providers.

### How are tool calls processed in the mapping pipeline?

When the parser encounters a tool invocation in the Anthropic payload, it calls `runTool()` to execute the function and captures the output. The results are appended as assistant-role messages to the chat array, effectively translating Anthropic's native tool format into the OpenAI-compatible tool message structure expected by FreeLLMAPI's provider layer.

### Where is the model-to-family classification defined?

The classification logic and family definitions reside in [`server/src/services/anthropic-map.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/anthropic-map.ts), which exports `CLAUDE_FAMILIES` for the enumeration of supported families and implements resolution helpers that determine which family a specific model ID belongs to based on the predefined alias mappings.