llm-bridge-core
Protocol transform library for LLM API translation between Anthropic and OpenAI.
Overview
llm-bridge-core is a Rust library that translates request payloads, response payloads, and streaming SSE events between the Anthropic Messages API and OpenAI-compatible APIs (Chat Completions and Responses). It is library-first, protocol-only, and has zero gateway concerns — no auth, billing, routing, or rate-limiting.
No feature flags are required; all capabilities are included by default. No workspace-internal dependencies — it can be used standalone from crates.io.
Features
- Anthropic → OpenAI Chat Completions: Convert Anthropic Messages requests to OpenAI Chat Completions format
- Anthropic → OpenAI Responses: Convert Anthropic Messages requests to OpenAI Responses format
- OpenAI Chat Completions → Anthropic: Convert OpenAI Chat Completions requests back to Anthropic Messages
- OpenAI Responses → Anthropic: Convert OpenAI Responses requests back to Anthropic Messages
- Streaming: Cross-protocol SSE → SSE translation with cross-chunk state management
- Thinking: Anthropic
thinkingblocks ↔ OpenAIreasoningcontent translation - Tool use: Cross-protocol tool call / tool result translation with semantic equivalence
- Response transforms: Anthropic response → OpenAI/Responses format for upstream response mapping
- Header transforms: Automatic
content-typedetection andx-no-response-completionhandling
Unsupported fields are logged before omission rather than silently dropped.
Installation
[]
= "0.2"
MSRV: Rust 1.80+ (edition 2024).
Quick Start
Non-streaming
use ;
use transform;
let req = builder
.path
.body
.build;
// Anthropic → OpenAI Chat Completions
let response: TransformResponse = anthropic_to_openai?;
// Anthropic → OpenAI Responses
let response: TransformResponse = anthropic_to_openai_responses?;
// OpenAI Chat Completions → Anthropic
let response: TransformResponse = openai_to_anthropic?;
// OpenAI Responses → Anthropic
let response: TransformResponse = responses_to_anthropic?;
Streaming
use ;
use stream;
let mut state = default;
// Parse SSE frames and get structured events
let events = transform_stream_events?;
// Or convert directly to target SSE bytes
let anthropic_sse = transform_stream_to_anthropic_sse?;
let openai_sse = transform_stream_to_openai_sse?;
Key Types
The public API is organized into three modules:
| Module | Key Types | Purpose |
|---|---|---|
model |
TransformRequest, TransformResponse, TransformError, ApiFormat, ContentBlock, StreamEvent, StreamState, Usage, StopReason |
Core data model and error types |
transform |
anthropic_to_openai, openai_to_anthropic, responses_to_anthropic, anthropic_to_openai_responses, transform_stream, etc. |
Non-streaming and streaming protocol transforms |
stream |
transform_stream_events, transform_stream_to_anthropic_sse, transform_stream_to_openai_sse, SseFrame, parse_sse_frames, events_to_sse |
Low-level SSE parsing and streaming transform primitives |
Error Handling
All transforms return Result<_, TransformError>. The error enum provides structured variants you can match on:
| Variant | When |
|---|---|
InvalidFormat |
Malformed JSON or unsupported format |
MissingRequiredField |
Required field absent in request/response |
BufferLimitExceeded |
Input exceeds resource limits (see below) |
StreamInterrupted |
SSE stream ended unexpectedly |
UpstreamError |
Upstream provider returned an error |
LossyDowngrade |
Field unsupported by target protocol (logged, then omitted) |
Use TransformError::sanitized_message() for safe client-facing error strings.
Resource Limits
| Constant | Value | Purpose |
|---|---|---|
MAX_SSE_STREAM_BYTES |
1 MB | Maximum total SSE data processed per stream chunk |
MAX_MESSAGES_COUNT |
10,000 | Maximum messages per request |
MAX_JSON_DEPTH |
64 | Maximum JSON nesting depth for input validation |
Protocol Translation Matrix
Non-streaming
| Source → Target | Function | Status |
|---|---|---|
| Anthropic → OpenAI Chat Completions | transform::anthropic_to_openai |
✓ |
| OpenAI Chat Completions → Anthropic | transform::openai_to_anthropic |
✓ |
| Anthropic → OpenAI Responses | transform::anthropic_to_openai_responses |
✓ |
| OpenAI Responses → Anthropic | transform::responses_to_anthropic |
✓ |
Response Transforms
| Transform | Function |
|---|---|
| Anthropic response → OpenAI Chat format | transform::anthropic_response_to_openai_response |
| Anthropic response → OpenAI Responses format | transform::anthropic_response_to_responses_response |
| OpenAI response → Anthropic format | transform::openai_response_to_anthropic_message |
Streaming
| Source SSE → Target SSE | Function | Status |
|---|---|---|
| OpenAI Chat → Anthropic | stream::transform_stream_to_anthropic_sse |
✓ |
| OpenAI Responses → Anthropic | stream::transform_stream_to_anthropic_sse |
✓ |
| Anthropic → OpenAI Chat Completions | stream::transform_stream_to_openai_sse |
✓ |
| Anthropic → OpenAI Responses | stream::transform_stream_to_openai_responses_sse |
✓ |
Same-protocol passthrough (e.g., Anthropic → Anthropic) is handled outside the transform core — it is a caller/proxy concern.
Crate Structure
crates/core/src/
├── lib.rs # Public API: model, stream, transform modules
├── model.rs # Core types, errors, resource limits, validation
├── transform/ # Non-streaming protocol translation
│ ├── mod.rs # Public re-exports
│ ├── anthropic_to_openai.rs
│ ├── anthropic_to_responses.rs
│ ├── openai_to_anthropic.rs
│ ├── responses_to_anthropic.rs
│ ├── response_transforms.rs
│ ├── header_helpers.rs
│ ├── shared.rs
│ ├── streaming_entry.rs
│ └── tests.rs
├── stream/ # Streaming SSE protocol translation
│ ├── mod.rs # Public re-exports and stream dispatcher
│ ├── sse_parser.rs # SSE frame parser
│ ├── sse_output.rs # SSE serialization (events → SSE)
│ ├── frame_dispatch.rs
│ ├── anthropic_to_openai.rs
│ ├── anthropic_to_responses.rs
│ ├── responses_to_anthropic_stream.rs
│ ├── openai_stream.rs
│ ├── openai_types.rs
│ ├── anthropic_types.rs
│ ├── stream_helpers.rs
│ └── tests.rs
├── examples/ # Runnable examples (see below)
│ ├── basic_nonstream.rs
│ ├── all_transforms.rs
│ ├── streaming_text.rs
│ ├── streaming_tool_use.rs
│ ├── error_handling.rs
│ ├── chat-roundtrip.rs
│ └── http-proxy.rs
└── tests/ # Integration tests
└── end_to_end_fixtures.rs
Running Examples
All examples are self-contained (except http-proxy which needs API keys). No network access required.
For the HTTP proxy example with primary/backup failover, see examples/README.md.
Safety
#![forbid(unsafe_code)] — this crate contains zero unsafe code.
Documentation
- API docs: docs.rs/llm-bridge-core
- Examples: examples/README.md
- Project specs: specs/
- Research docs: docs/
- Server crate: apps/server
Versioning
This crate follows semantic versioning. Breaking changes to the transform API bump the minor version while the major version is 0.
License
MIT