Skip to content

braintrustdata/lingua

Repository files navigation

Lingua - A low-level library for translating between LLM formats

Lingua is a library and specification for defining a universal message format for large language model APIs. It enables developers to write messages, model parameters, and tool definitions in a single format that can be translated to and from any model provider's API client-side with zero runtime overhead.

Goals

  • You should be able to write messages, model parameters, and tool definitions in this format, and use them with any version of any model provider.
  • The spec describes how message data is represented, and the implementation converts to-and-from model provider APIs and popular frameworks.
  • ~Zero runtime overhead, because there is no execution logic. The sole purpose of this project is to define a universal message format that can be translated across different model providers.

Anti-goals

  • Framework. This project is explicitly not providing any higher-level abstractions, guidance on how to structure your app, or model execution support. Frameworks can build on top of Lingua to avoid reimplementing model-provider translation.
  • Proxy. This format could be used as the foundation for a proxy implementation, but has no concept of actually running prompts or handling authentication.
  • Optimization. Messages written in this format will execute exactly what you would expect from the model provider. 3rd party optimizers can be built on the format, and those optimizers will naturally work across providers.

Principles

  • Supports 100% of model-provider specific quirks (eg cache breakpoints, stateful responses).
  • Messages you write in this format should be safe to store and survive many years of changes in model behavior and API versions.
  • Zero dependencies and support for many languages including Typescript, Python, Java, Golang. Ideally can cross-compile or trivial for AI to generate support in language N+1.
  • Code and tests are structured to facilitate coding agents to efficiently add new providers and support new features.
  • Has a precise definition of usage (token) reporting that can be used to compute cost from a standard price table across providers.

Architecture

Lingua Universal Format
         ↓
    Capability Detection
         ↓
   Provider Translators
         ↓
OpenAI │ Anthropic │ Google │ Bedrock │ ...

Capabilities

[ ... list the known capabilities ... ]

Compatability matrix

[ .. for each provider, list which capabilities are supported ... ]

Project structure

lingua/
├── src/
│   ├── universal/             # Universal Lingua format definitions
│   ├── providers/             # Provider-specific API types
│   ├── translators/           # Translation logic between formats
│   ├── capabilities/          # Capability detection system
│   ├── wasm.rs                # WebAssembly bindings
│   ├── python.rs              # Python bindings (PyO3)
│   └── lib.rs                 # Main library entry
├── bindings/
│   ├── typescript/            # TypeScript/WASM bindings
│   └── python/                # Python bindings
├── examples/                  # Usage examples
└── tests/typescript/          # TypeScript compatibility tests

Building bindings

First-time setup

Before building WASM bindings for the first time, install the required tools:

# Install WASM build tools (wasm32-unknown-unknown target, wasm-bindgen-cli)
make install-wasm-tools

# Or run the full setup script
./scripts/setup.sh

Building

Use the Makefile for easy building:

# Show all available targets
make help

# Build all bindings
make all

# Build specific bindings
make typescript
make python

# Run tests
make test
make test-rust
make test-typescript
make test-python

# Clean build artifacts
make clean

TypeScript/WASM bindings

cd bindings/typescript
npm install
npm run build
npm test

See bindings/typescript/README.md for details.

Python bindings

cd bindings/python
uv sync --extra dev
uv run pytest tests/

See bindings/python/README.md for details.

Update pipeline

These two primitives make it very easy to, for example, reproduce how tool calls are represented in each of the major model providers, snapshot inputs/outputs, and then fill in logic to translate between them. Some of that has been handwritten, but a lot of it is generated by LLMs. In general, a goal of this project is create enough scaffolding so that you can open a coding agent and say "fix support for parallel tool calls" and there should be enough context for an LLM to add the test cases, generate snapshots, and make the necessary changes.

LLM API pipeline

OpenAI and Anthropic do not have Rust SDKs, but they do publish OpenAPI specs, so we fetch those and use quicktype (with a few hacks) to generate Rust types. These types generally work but do not have really great discriminated unions (like they do in Typescript). Google has protobufs, which we are able to convert to Rust types, and Bedrock actually publishes a Rust SDK.

Testing Strategy

Lingua employs a comprehensive testing strategy to ensure accurate and lossless conversion between provider-specific formats and the universal format.

Roundtrip Testing

The core testing approach uses roundtrip conversion tests to verify that data can be converted from provider format → universal format → provider format without loss:

Provider Payload → Universal ModelMessage → Provider Payload
     (input)            (conversion)          (output)

Key test scenarios:

  1. Request Roundtrips:

    • openai_request → universal → openai_request (should be identical)
    • anthropic_request → universal → anthropic_request (should be identical)
  2. Response Roundtrips:

    • openai_response → universal → openai_response (should be identical)
    • anthropic_response → universal → anthropic_response (should be identical)
  3. Cross-Provider Compatibility:

    • openai_request → universal → anthropic_request (should be equivalent)
    • anthropic_response → universal → openai_response (should be equivalent)

Payload-Based Testing

Tests use real API payloads captured from actual provider interactions:

  • Payload Snapshots: Located in paylods/snapshots/ directory with real request/response examples
  • Comprehensive Coverage: Tests cover simple messages, tool calls, streaming responses, multi-modal content
  • Version Tracking: Payloads are version-controlled to detect breaking changes in provider APIs

Testing Levels

  1. Unit Tests: Individual conversion functions with synthetic data
  2. Integration Tests: Full roundtrip tests using real payload snapshots
  3. Compatibility Tests: Cross-provider conversion validation
  4. Regression Tests: Ensure updates don't break existing functionality

This strategy ensures Lingua maintains 100% fidelity when converting between provider formats while providing confidence that the universal format can represent any provider-specific capability.

Automated Updates

Provider types can be automatically updated using GitHub Actions:

  1. Manual trigger: Go to Actions → "Update Provider Types" → Run workflow
  2. Choose providers: Select all, or specific providers like openai,anthropic
  3. Automatic PR: If changes are detected, a PR will be created automatically

The automation downloads the latest specifications, regenerates types, applies formatting, and creates a pull request for review.

Tests / interesting cases

  • Show token accounting across providers. Ideally we give users a way to access the provider's native usage + a unified format.
  • How does structured outputs + Anthropic work? Translate to tool, and parse the response? Does that require carrying some state across request/response? Maybe we can generate an object when performing the forward translation that can be used in the reverse translation.
  • Audit and remove all remaining todo!() calls

Feature Flags

Lingua supports optional provider dependencies through feature flags to minimize build time and binary size:

Available Features

  • openai - OpenAI API types and translators
  • anthropic - Anthropic API types and translators
  • google - Google Gemini API types and translators
  • bedrock - Amazon Bedrock API types and translators (pulls in AWS SDK)

Usage

Default (all providers):

[dependencies]
lingua = "0.1.0"

Minimal (only OpenAI):

[dependencies]
lingua = { version = "0.1.0", default-features = false, features = ["openai"] }

Without AWS dependencies:

[dependencies]
lingua = { version = "0.1.0", default-features = false, features = ["openai", "anthropic", "google"] }

Only Bedrock:

[dependencies]
lingua = { version = "0.1.0", default-features = false, features = ["bedrock"] }

Conditional Compilation

The translators and types are only available when their respective features are enabled:

#[cfg(feature = "openai")]
use lingua::translators::to_openai_format;

#[cfg(feature = "bedrock")]
use lingua::translators::to_bedrock_format_with_model;

Status

🚧 In Development - Currently building the foundational types and translator architecture.

  • Support parsing streaming responses and combining streaming messages into a single response.

Contributing

This project aims to support the entire ecosystem of LLM providers. Contributions for new providers, capability detection improvements, and format enhancements are welcome.

Developer Setup

Prerequisites: Rust toolchain, Node.js, pnpm.

Run ./scripts/setup.sh from the project root after cloning. If the script succeeds, you should be all set! Otherwise, follow the error messages.

TypeScript Type Generation

TypeScript types for the universal Message format are automatically generated from Rust types using ts-rs:

# Generate TypeScript types from Rust
make generate-types

# Generated files: bindings/typescript/src/generated/*.ts

Important: After modifying Rust types in src/universal/, run make generate-types and commit the updated TypeScript files. CI will verify that generated types are up to date.

License

TBD

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 5