A tiny, dependency-light client that gives Anthropic, OpenAI, Gemini, and Moonshot one consistent request and response shape.
GitHubfrom polygate import chat response = chat( provider="anthropic", # or "openai", "gemini", "moonshot" model="claude-sonnet-4-6", messages=[{"role": "user", "content": "Say hi in one word."}],) print(response.content) # "Hi!"print(response.usage) # Usage(prompt_tokens=..., ...)Every adapter translates the same message list into whatever shape the provider actually expects. API keys come from the argument or fall back to the environment.
"anthropic""claude"ANTHROPIC_API_KEY
"openai""gpt"OPENAI_API_KEY
"gemini""google"GEMINI_API_KEY
"moonshot""kimi"MOONSHOT_API_KEY
Adding a provider is one adapter file and one registry line. Contribute one
No more learning four response formats. Anything a provider returns that polygate does not normalize is still there, untouched, under raw.
Extra keyword arguments pass straight through to the provider, so provider-specific options like tools, top_p, or stop sequences keep working without polygate needing to know about them.
A rate limit is per key, not per host. Sleeping through a 429 while another key sits idle wastes the whole backoff window, so polygate rotates first and only backs off once it runs out of keys.
A key the provider rejects outright is dropped for the rest of the process rather than retried forever. Deterministic failures are never retried at all: a 400, 404, or 422 cannot succeed on a second attempt, so sending it again just bills you twice.
Both features are off unless you ask for them. A single api_key with no retry behaves exactly as it always has.
from polygate import chat, Retry response = chat( provider="anthropic", model="claude-sonnet-4-6", messages=[{"role": "user", "content": "Say hi."}], # One key, a list, or a reusable KeyPool. api_key=["sk-1", "sk-2", "sk-3"], # Omit retry= for the old behavior: one request, no retries. retry=Retry(max_attempts=4),)polygate is not trying to out-feature LiteLLM, Portkey, or Helicone. It is the smallest layer that normalizes requests and responses across providers, with each adapter readable in under 100 lines. Key rotation and backoff are opt-in and off by default; caching and routing are still yours to build on top, with whatever opinions your project actually needs.