Implementation:Helicone Helicone Cross Provider Transform Functions
| Knowledge Sources | |
|---|---|
| Domains | LLM Observability, AI Gateway, Cross-Provider Compatibility, Response Normalization |
| Last Updated | 2026-02-14 00:00 GMT |
Overview
Concrete functions for cross-provider format conversion in the AI Gateway: toAnthropic() and toGoogle() transform OpenAI-format requests to provider-native formats, while normalizeAIGatewayResponse() converts provider responses back to OpenAI format. Provided by the llm-mapper/transform module.
Description
These functions form the core of the AI Gateway's provider translation layer. toAnthropic() converts OpenAI Chat Completions request bodies into Anthropic Messages API format, handling system message extraction, content block conversion, tool mapping, reasoning/thinking configuration, cache control breakpoints, and context editing. toGoogle() converts to Gemini's generateContent format with content parts, system instructions, generation config, and tool declarations. normalizeAIGatewayResponse() handles the reverse direction for both streaming (SSE) and non-streaming responses, including AWS Bedrock binary event stream decoding and usage normalization.
Usage
Used by the AI Gateway proxy when routing requests to Anthropic or Google, and when normalizing responses from any provider back to OpenAI format before returning to the caller.
Code Reference
Source Location
- Repository: Helicone
- Files:
packages/llm-mapper/transform/providers/openai/request/toAnthropic.ts-- OpenAI to Anthropic request conversionpackages/llm-mapper/transform/providers/openai/request/toGoogle.ts-- OpenAI to Google Gemini request conversionpackages/llm-mapper/transform/providers/normalizeResponse.ts-- Response normalization (all providers)
Signature (toAnthropic)
export function toAnthropic(
openAIBody: HeliconeChatCreateParams,
providerModelId?: string,
plugins?: Plugin[],
options?: {
includeCacheBreakpoints?: boolean;
}
): AnthropicRequestBody
Signature (toGoogle)
export function toGoogle(
openAIBody: HeliconeChatCreateParams
): GeminiGenerateContentRequest
Signature (normalizeAIGatewayResponse)
export async function normalizeAIGatewayResponse(params: {
responseText: string;
isStream: boolean;
provider: ModelProviderName;
providerModelId: string;
responseFormat: ResponseFormat;
bodyMapping?: BodyMappingType;
}): Promise<string>
Import
import { toAnthropic } from "@helicone-package/llm-mapper/transform/providers/openai/request/toAnthropic";
import { toGoogle } from "@helicone-package/llm-mapper/transform/providers/openai/request/toGoogle";
import { normalizeAIGatewayResponse } from "@helicone-package/llm-mapper/transform/providers/normalizeResponse";
I/O Contract
toAnthropic Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| openAIBody | HeliconeChatCreateParams |
Yes | OpenAI Chat Completions request body including messages, model, max_tokens, temperature, tools, etc. |
| providerModelId | string |
No | Override model ID for Anthropic (e.g. when routing through a gateway that maps model names) |
| plugins | Plugin[] |
No | Array of Helicone plugins; the "web" plugin is mapped to Anthropic's web_search_20250305 tool
|
| options.includeCacheBreakpoints | boolean |
No | Whether to include Anthropic prompt caching annotations (default: true). When enabled, adds cache_control: { type: "ephemeral", ttl: "5m" } to the last content block if no user-provided cache controls exist.
|
toAnthropic Output
| Name | Type | Description |
|---|---|---|
| (return) | AnthropicRequestBody |
Anthropic Messages API request body with model, messages, max_tokens, optional system, tools, tool_choice, thinking, metadata, stop_sequences, stream, and context_management
|
toGoogle Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| openAIBody | HeliconeChatCreateParams |
Yes | OpenAI Chat Completions request body. Must contain at least one message. |
toGoogle Output
| Name | Type | Description |
|---|---|---|
| (return) | GeminiGenerateContentRequest |
Google Gemini request body with contents (user/model parts), optional system_instruction, generationConfig (temperature, topP, topK, maxOutputTokens, thinkingConfig, imageConfig, responseMimeType/responseSchema), tools (functionDeclarations), and toolConfig
|
normalizeAIGatewayResponse Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| responseText | string |
Yes | Raw response text from the provider (JSON for non-streaming, SSE text for streaming) |
| isStream | boolean |
Yes | Whether the response is a streaming (SSE) response |
| provider | ModelProviderName |
Yes | The provider that produced the response (used for usage normalization) |
| providerModelId | string |
Yes | The model ID used with the provider |
| responseFormat | ResponseFormat |
Yes | The format of the response: "ANTHROPIC", "GOOGLE", or "OPENAI"
|
| bodyMapping | BodyMappingType |
No | If "RESPONSES", the output is further converted to OpenAI Responses API format
|
normalizeAIGatewayResponse Output
| Name | Type | Description |
|---|---|---|
| (return) | Promise<string> |
Normalized response as a string: JSON for non-streaming, SSE text for streaming. In OpenAI Chat Completions format (or Responses API format if bodyMapping is "RESPONSES"). |
Key Transformation Details
toAnthropic: System Message Handling
OpenAI messages with role: "system" are extracted and placed in the Anthropic system parameter. A single string system message becomes a plain string; multiple system messages or those with cache_control are converted to an array of AnthropicContentBlock objects.
toAnthropic: Reasoning/Thinking
When openAIBody.reasoning_effort is set, the function enables Anthropic's thinking mode with a budget calculated from reasoning_options.budget_tokens (or half of max_tokens as fallback). Temperature is forced to 1 as required by Anthropic for reasoning.
toAnthropic: Context Editing
When openAIBody.context_editing.enabled is true, the function maps to Anthropic's context_management.edits array, supporting clear_thinking_20251015 and clear_tool_uses_20250919 edit strategies.
toGoogle: Thinking Configuration
Google thinking is configured based on model version: Gemini 3+ models use thinkingLevel ("low" or "high"), while Gemini 2.5 models use thinkingBudget: -1 for dynamic budgeting. A thinkingBudget: 0 explicitly disables thinking when no reasoning effort is requested.
toGoogle: Schema Stripping
OpenAI's strict mode requires additionalProperties: false on tool parameter schemas, but Gemini rejects this field. The stripOpenAISchemaFields function recursively removes these fields from all tool parameter schemas and response format schemas.
normalizeAIGatewayResponse: Streaming
For streaming responses, dedicated converter classes process SSE event lines:
- Anthropic --
AnthropicToOpenAIStreamConverterconverts Anthropic SSE events to OpenAIChatCompletionChunkobjects - Anthropic via Bedrock -- Binary event stream payloads are base64-decoded, then processed through the Anthropic converter
- Google --
GoogleToOpenAIStreamConverterconverts Google streaming events to OpenAI chunks - OpenAI --
OpenAIStreamUsageNormalizernormalizes usage fields using provider-specific usage processors
Usage Examples
Basic Usage
import { toAnthropic } from "@helicone-package/llm-mapper/transform/providers/openai/request/toAnthropic";
const anthropicBody = toAnthropic({
model: "claude-3-opus-20240229",
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "What is the weather?" },
],
max_tokens: 1024,
temperature: 0.7,
});
// Returns:
// {
// model: "claude-3-opus-20240229",
// system: "You are a helpful assistant.",
// messages: [{ role: "user", content: "What is the weather?" }],
// max_tokens: 1024,
// temperature: 0.7,
// }
Google Conversion
import { toGoogle } from "@helicone-package/llm-mapper/transform/providers/openai/request/toGoogle";
const geminiBody = toGoogle({
model: "gemini-1.5-pro",
messages: [
{ role: "system", content: "Be concise." },
{ role: "user", content: "Summarize this article." },
],
max_tokens: 500,
temperature: 0.5,
});
// Returns: {
// contents: [{ role: "user", parts: [{ text: "Summarize this article." }] }],
// system_instruction: { role: "system", parts: [{ text: "Be concise." }] },
// generationConfig: { maxOutputTokens: 500, temperature: 0.5 },
// }
Response Normalization
import { normalizeAIGatewayResponse } from "@helicone-package/llm-mapper/transform/providers/normalizeResponse";
// Non-streaming Anthropic response
const normalized = await normalizeAIGatewayResponse({
responseText: JSON.stringify(anthropicResponseBody),
isStream: false,
provider: "ANTHROPIC",
providerModelId: "claude-3-opus-20240229",
responseFormat: "ANTHROPIC",
});
// Returns JSON string in OpenAI Chat Completions format
// Streaming Google response with Responses API output
const streamNormalized = await normalizeAIGatewayResponse({
responseText: googleSSEText,
isStream: true,
provider: "GOOGLE",
providerModelId: "gemini-1.5-pro",
responseFormat: "GOOGLE",
bodyMapping: "RESPONSES",
});
// Returns SSE text in OpenAI Responses API format