# How OpenClaude Converts SSE Streams for Different AI Providers

> Discover how OpenClaude unifies SSE streams from OpenAI, Anthropic, and Gemini into a single Anthropic-compatible format. Learn more about its OpenAI shim layer.

- Repository: [Gitlawb/openclaude](https://github.com/Gitlawb/openclaude)
- Tags: how-to-guide
- Published: 2026-09-08

---

**OpenClaude normalizes disparate Server-Sent Events (SSE) streams from OpenAI, Anthropic, and Gemini into a unified Anthropic-compatible message format through its OpenAI shim layer in [`streamConversion.ts`](https://github.com/Gitlawb/openclaude/blob/main/streamConversion.ts).**

Working with multiple large-language-model providers requires handling incompatible streaming protocols. The Gitlawb/openclaude repository solves this through a dedicated conversion pipeline that transforms provider-specific SSE payloads into a consistent internal representation. This ensures the core engine processes every streaming response—whether from OpenAI, Anthropic, or Gemini—using a single standardized schema.

## The OpenAI Shim Architecture

OpenClaude implements an **OpenAI-compatible shim** located at [`src/services/api/openaiShim/streamConversion.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/services/api/openaiShim/streamConversion.ts). This module acts as a protocol adapter, intercepting raw SSE chunks from various providers and restructuring them to match OpenAI's expected format before final conversion to Anthropic-style messages. The shim handles both streaming and non-streaming response paths, ensuring uniform behavior regardless of how the upstream API delivers data.

## Converting SSE Streams in Real-Time

The conversion pipeline operates through several discrete stages, each targeting specific inconsistencies between provider implementations.

### Entry Point and Usage Extraction

The primary entry point for stream processing is `convertOpenAIStreamUsage`, located at lines 102-110. This function extracts token usage metadata from provider-specific `usage` objects and normalizes them into the OpenAI schema. By standardizing consumption metrics at the ingestion point, OpenClaude maintains accurate cost tracking and rate-limit management across heterogeneous APIs.

### Handling Non-Streaming Responses

When a provider returns a complete JSON payload instead of an SSE stream, the shim invokes `convertNonStreamingResponseToAnthropicMessage` (lines 60-68). This helper constructs a synthetic Anthropic-style message from the full response body and target model name, ensuring the downstream pipeline receives the same message structure it would expect from a chunked stream.

### The Streaming Loop and XML Fragment Reassembly

For live SSE connections, OpenClaude processes each chunk through a specialized parsing loop. The implementation at lines 554-618 handles the critical task of **merging partial XML tool-call fragments**. Since XML tags representing tool invocations can split across multiple SSE packets, the shim maintains a buffer that accumulates fragments until a complete tag is detected. Once the closing tag arrives (lines 886-898), the converter transforms the XML into Anthropic `tool_use` blocks and strips the raw markup from the final text output. This prevents malformed tool calls from reaching the agent when providers wrap function invocations in custom XML dialects.

### Provider-Specific Branches

The conversion layer contains dedicated handlers for distinct provider behaviors. For Google Gemini's SSE format, the shim maps text deltas, tool calls, usage statistics, and finish reasons onto the OpenAI-compatible shape. Test cases in [`src/services/api/openaiShim/streamConversion.test.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/services/api/openaiShim/streamConversion.test.ts) (lines 482-490) demonstrate this mapping, showing how Gemini-specific fields like `candidates` and `usageMetadata` translate into standardized streaming tokens.

### Connection Resilience

OpenClaude protects against stalled streams through idle-timeout detection implemented at lines 475-483. If no SSE data arrives within the configured timeout window, the shim logs a warning and aborts the connection, treating the interruption as a dropped stream rather than allowing the client to hang indefinitely.

## Implementation Example

The following pattern demonstrates how to consume a provider-specific SSE stream through OpenClaude's conversion layer:

```typescript
import { convertOpenAIStreamUsage, parseChunk } from './src/services/api/openaiShim/streamConversion.js';

async function* processProviderStream(rawStream: ReadableStream) {
  const reader = rawStream.getReader();
  
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    
    // Parse raw SSE data into JSON
    const chunk = parseChunk(value);
    
    // Normalize to Anthropic-compatible format
    const { message, usage, finishReason } = convertOpenAIStreamUsage(chunk);
    
    // Yield unified message structure
    yield { message, usage, finishReason };
  }
}

```

This implementation handles the fragmentation concerns internally, ensuring that downstream consumers receive complete, normalized messages even when the upstream provider transmits partial XML or unusual SSE formatting.

## Summary

- OpenClaude centralizes SSE conversion in [`src/services/api/openaiShim/streamConversion.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/services/api/openaiShim/streamConversion.ts), providing a single integration point for multiple LLM providers.
- The `convertOpenAIStreamUsage` function normalizes usage statistics and metadata at lines 102-110, while `convertNonStreamingResponseToAnthropicMessage` handles complete payloads at lines 60-68.
- XML fragment reassembly occurs between lines 554-618, ensuring tool calls split across SSE chunks are reconstructed before conversion to Anthropic `tool_use` blocks.
- Provider-specific logic for Gemini and other APIs is validated through comprehensive test coverage in [`streamConversion.test.ts`](https://github.com/Gitlawb/openclaude/blob/main/streamConversion.test.ts).
- Idle timeout protection at lines 475-483 prevents resource leaks from stalled connections.

## Frequently Asked Questions

### How does OpenClaude handle SSE chunks that split XML tool calls across packets?

The shim maintains an internal buffer that accumulates partial XML fragments until a complete tag is detected (lines 554-618). Only after identifying the closing tag does it convert the XML to Anthropic `tool_use` blocks and strip the markup from the text output (lines 886-898), ensuring downstream components receive valid, complete tool invocations.

### What happens when a provider returns a non-streaming JSON response instead of SSE?

The system routes non-streaming responses through `convertNonStreamingResponseToAnthropicMessage` (lines 60-68), which constructs an Anthropic-compatible message object from the complete JSON payload. This normalization ensures the core engine processes streaming and batch responses identically.

### Which providers does the stream conversion layer support?

According to the source code and test suite, the conversion layer explicitly handles OpenAI-compatible APIs, Anthropic's native format, and Google Gemini's SSE dialect. The test file at [`src/services/api/openaiShim/streamConversion.test.ts`](https://github.com/Gitlawb/openclaude/blob/main/src/services/api/openaiShim/streamConversion.test.ts) (lines 482-490) validates Gemini-specific field mappings, while the main converter handles generic OpenAI schema adaptations.

### How does OpenClaude prevent indefinite hangs on stalled SSE connections?

The implementation monitors connection health through an idle timeout mechanism (lines 475-483). If the specified duration elapses without receiving new SSE data, the shim logs a warning and forcibly aborts the stream, treating the condition as a network failure rather than waiting indefinitely.