Ogentic-Adapter-SDKConnecting
0/ 100
Binding backends to one contract000%
ogentic·adapter-sdk
Open source · Apache-2.0 · Wave 2

One contract,
any LLM
backend

The Adapter protocol — the stable, zero-dependency contract any LLM backend implements so ogentic-router, and the rest of the suite, can call it uniformly. Implement one async method; hand back the provider's response, unchanged.

Apache-2.0Wave 2Python 3.11+Zero-dependencySemver-stable
Scroll to connect
Live

Every backend, one call path.

OpenAI, Anthropic, Ollama, or a backend you wrote yourself — the caller makes the same call. Each adapter returns its provider's native response, unchanged. The contract standardizes the call, not the payload.

A new backend is one async method away — implement chat(), pass the conformance suite, and the whole suite can reach it.

$ await adapter.chat(messages, model=…)OpenAIAdapter
the suite──▶OpenAIAdapter·gpt-4o-mini
provider-native response
{"choices":[{"message":{"role":"assistant","content":"…"}}],"usage":{"total_tokens":38}}
class OpenAIAdapter: async def chat(messages, *, model=None)
✓ Conforms to the Adapter contract · response returned unchanged
One method, one call path. The contract standardizes the call, never the payload.
// illustrative — the contract is being finalizedFollow the repo ↗
Why it exists

Five things a contract has to be.

A contract is only worth building on if it's tiny, stable, and honest about the payload. These are the properties that let the whole suite — and your own code — depend on it without regret.

01no transitive deps

Zero runtime dependencies

The contract layer pulls in nothing else — import it without dragging in shield, router, or audit. It's the base every other package can depend on without a dependency cycle. Optional integrations live behind extras.

02one interface

One method to implement

A backend is a single async method — chat(messages, …). Implement it and the whole suite can call your provider. No base class to inherit, no framework to adopt — just satisfy the protocol.

03no lossy wrapper

Provider-native passthrough

Adapters hand back the provider's response unchanged. The contract standardizes the call, not the payload — so you never lose a field to a lowest-common-denominator wrapper.

04semver

Stable by semver

Once a symbol is exported from the package, it follows semver — breaking changes ship a major bump and a deprecation notice first. Build on it without chasing churn.

05the seam

The seam the suite is built on

Router already calls its backends through this contract; your own code can too. It's the seam that keeps the whole pipeline decoupled from any single vendor's SDK — swap the provider, keep the callers.

It's a contract, not a gateway: there's no hosted service and no runtime in the path — just the interface and the types every backend agrees to.

The protocol

One method. That's the contract.

No base class, no framework, no runtime. The Adapter is a typed Protocol with a single async method — anything that implements it is a backend the whole suite can call.

ogentic_adapter_sdk.Adapter
class Adapter(Protocol):
    async def chat(
        self,
        messages: list[Message],
        *,
        model: str | None = None,
    ) -> ChatResponse: ...
structural typing — anything with this method is an adapter. Nothing to inherit, nothing to register.
The shape
Interface
one async chat(messages, …)
Returns
provider-native response, unchanged
Deps
zero runtime deps on ogentic-*
Stability
semver on the public API
Extras
opt-in integrations ([router], …)
Typing
Pydantic v2 models · fully typed

Because it depends on nothing, every other package can depend on it — the base of the suite that never causes a dependency cycle.

Scenarios

One seam, many payoffs.

Everything a stable provider abstraction buys you — provider swaps, custom backends, conformance, and freedom from lock-in — falls out of the same one-method contract.

Swap providers

Change the backend, not the callers

Move from one provider to another — or run several — without touching a line of caller code. Same contract, a different adapter behind it.

no caller changes
Custom backend

Wrap anything that generates

Bring an in-house model, an internal gateway, or a niche provider into the suite by implementing one method against the protocol.

one method
Conformance

Prove it behaves

A shared conformance suite checks your adapter against the same expectations the reference adapters meet — so a new backend is a known quantity before it ships.

conformance suite
Decouple

Independent of any vendor

Keep the suite — and your application — free of any single provider's SDK in the hot path. The contract is the seam that makes the swap cheap.

no lock-in
Plug in

Implement, conform, register, call.

How a new backend joins the suite. The whole ceremony is one method and a test run — after that, anything that routes by backend id can reach it.

1 · implementimplement

Satisfy the protocol

Write one async method — chat(messages, …) — that calls your provider and returns its native response. No base class, no framework.

2 · conformconform

Run the conformance suite

The shared conformance tests verify your adapter matches the contract — the same bar the reference adapters clear.

3 · registerregister

Give it a backend id

Register the adapter under an id in config. Anything that routes by backend id — like ogentic-router — can now reach it.

4 · callcall

Call it through the contract

The suite (or your own code) calls the adapter through the protocol — provider-agnostic, one code path for every backend.

5 · evolveevolve

Stay compatible

The public API only changes by semver — your adapter keeps working across minor and patch versions without edits.

implement conform register call evolve

// The only thing you depend on is the contract — no provider stack pulled into your process, no framework to adopt.

Backends

Reference adapters, one interface.

The suite ships adapters for the common providers, all satisfying the same protocol. Yours joins them the moment it implements the method — no privileged position for the built-ins.

reference✓ chat()

OpenAIAdapter

providerOpenAI
modelgpt-4o-mini

Implements the Adapter protocol; returns the provider's response unchanged.

reference✓ chat()

AnthropicAdapter

providerAnthropic
modelclaude-sonnet-4-5

Implements the Adapter protocol; returns the provider's response unchanged.

reference · local✓ chat()

OllamaAdapter

providerOllama
modelllama3.2:3b

Implements the Adapter protocol; returns the provider's response unchanged.

one method away✓ chat()

MyBackend

providerYour provider
modelanything

Implement one async method and it conforms — a first-class backend, same as the rest.

How it compares

A contract, not a wrapper.

Most provider abstractions are fat wrappers or hosted gateways — they normalize responses (and lose fields), pull in a stack, and tie you to the vendor. The Adapter is the opposite: a thin protocol you implement, that gets out of the way.

A provider SDK / gateway
wrapper · normalized
Shape
A fat wrapper / gateway
Dependencies
Pulls in a provider stack
Response
Normalized (often lossy)
Lock-in
Tied to the vendor
Stability
Follows the vendor's SDK
ogentic-adapter-sdk
protocol · native passthrough
Shape
A thin protocol
Dependencies
Zero runtime deps
Response
Provider-native, unchanged
Lock-in
Swap adapters freely
Stability
Semver on the contract

The contract is the seam. Because it's tiny and depends on nothing, it's what ogentic-router ↗ calls backends through — and what keeps your own code free of any single provider's SDK in the hot path.

Code

The whole contract is one method.

Python 3.11+, Pydantic v2, fully typed, Apache-2.0. The examples below are illustrative — the public API is being finalized for the Wave 2 release.

protocol.pyContract
from typing import Protocol
from ogentic_adapter_sdk import Message, ChatResponse

class Adapter(Protocol):
    async def chat(
        self,
        messages: list[Message],
        *,
        model: str | None = None,
    ) -> ChatResponse: ...

# That's the whole contract. Anything with this method is an adapter.
custom.pyYour backend
from ogentic_adapter_sdk import Message, ChatResponse

class MyBackend:
    async def chat(self, messages, *, model=None) -> ChatResponse:
        raw = await my_provider.generate(messages, model=model)
        return ChatResponse.from_provider(raw)   # native passthrough

# No base class, no registration ceremony — it just conforms.
installGet it
pip install ogentic-adapter-sdk    # Wave 2 — coming soon

# zero runtime deps; opt into integrations with extras:
pip install "ogentic-adapter-sdk[router]"
The ogentic stack

The base the whole suite plugs into.

Composable Apache-2.0 primitives for regulated AI. Shield, Redact, Router, and Audit each do one job — and Router reaches every model through this one contract. The adapter-sdk is the seam underneath, so nothing is tied to a single vendor.

The contract that ogentic-router ↗ calls its backends through — alongside ogentic-shield ↗ and ogentic-redact ↗, part of the OgenticAI ↗ OSS suite.

Wave 2

The contract lands in Wave 2.

The zero-dependency backend contract for the OgenticAI suite — semver-stable, Apache-2.0. Router already routes through it; the standalone package ships in Wave 2. Follow the repo.

$pip install ogentic-adapter-sdkSoon