SOLWYN
Providers

OpenAI-compatible providers

Use Solwyn with any OpenAI-compatible endpoint — xAI, DeepSeek, Mistral, Groq, Z.AI, Together, Azure OpenAI, OpenRouter, Ollama, vLLM, and more

Any provider that speaks the OpenAI Chat Completions dialect works with Solwyn: point an openai.OpenAI client at the endpoint via base_url and wrap it as usual. Solwyn detects the real provider from the URL, so budgets, per-agent attribution, failover, and the cost dashboard all see the actual provider (e.g. groq) -- not "openai":

from openai import OpenAI
from solwyn import Solwyn

client = Solwyn(
    OpenAI(base_url="https://api.groq.com/openai/v1", api_key="gsk_..."),
    api_key="sk_proj_...",
)
response = client.chat.completions.create(
    model="llama-3.3-70b-versatile",
    messages=[{"role": "user", "content": "Hello!"}],
)

Available from SDK v0.1.7. No new install extras -- compat providers use the same openai client package as the OpenAI provider, installed via pip install "solwyn[openai]".

Using Together's native Together or AsyncTogether client instead? See the dedicated Together AI provider guide. The OpenAI-client route below remains supported when you intentionally use Together's compatible endpoint.

Auto-detected providers

Detection runs at construction, from the client's base_url (or, for Azure, the client class). Each provider also carries its own streaming-usage policy -- Solwyn injects stream_options={"include_usage": True} only where that is documented-safe:

ProviderDetected fromStreaming usage
xAI (Grok)api.x.aiautomatic (final chunk); stream_options is never sent -- xAI rejects it
DeepSeekapi.deepseek.cominclude_usage injected
Mistralapi.mistral.aistream_options never sent (strict validation); final-chunk usage or estimate
Qwen (DashScope compatible mode)dashscope.aliyuncs.com / dashscope-intl / dashscope-usinclude_usage injected
Z.AIapi.z.aiinclude_usage injected
Groqapi.groq.cominclude_usage injected; legacy x_groq.usage final-chunk fallback also handled
Together AI (native client guide)api.together.xyz / api.together.aiautomatic terminal usage when present; stream_options is never injected; otherwise estimate
Fireworksapi.fireworks.aiautomatic (final chunk)
Perplexity (Sonar)api.perplexity.aiusage rides streamed chunks; stream_options never sent
Azure OpenAI*.openai.azure.com / *.cognitiveservices.azure.com, or AzureOpenAI / AsyncAzureOpenAI client classinclude_usage injected -- skipped for "on your data" data_sources requests, which reject it
OpenRouteropenrouter.aiautomatic (final chunk); stream_options is deprecated there
Ollamalocalhost:11434include_usage injected (older versions ignore it → estimate)
vLLMlocalhost:8000include_usage injected
LM Studiolocalhost:1234include_usage injected (older versions omit usage → estimate)
Anything elseany non-OpenAI base_urlgeneric openai_compatible; stream_options never sent

Notes on detection:

  • Local servers (Ollama, vLLM, LM Studio) are recognized by their conventional default ports on a local host (localhost, 127.0.0.1, 0.0.0.0, ::1). When identity is guessed from a port, the SDK logs a one-time INFO message naming the detected provider (never the URL) and suggesting provider= if the guess is wrong. A non-default port still works via the generic catch-all -- use the explicit override to name it.
  • The catch-all claims any http(s) base URL that is not OpenAI's own (api.openai.com, including regional eu. / us. subdomains) and not a named provider above. A client with no custom base_url remains plain openai.
  • Model prefixes supplement URL detection for providers with distinctive naming (grok-, deepseek-, the Mistral family, qwen / qwq- / qvq-, glm-, accounts/fireworks/, sonar). Providers that serve shared open-model catalogs (Groq, Together, OpenRouter, local servers) deliberately have no model prefixes -- a bare llama-3-8b is never silently claimed for any of them.

Explicit provider identity

For endpoints auto-detection cannot name -- e.g. vLLM on a non-default port, or a private gateway -- pass the provider explicitly. On the constructor for the primary client, or as the 4th element of a fallback spec:

client = Solwyn(
    OpenAI(base_url="http://gpu-box:8080/v1", api_key="-"),
    api_key="sk_proj_...",
    provider="vllm",
    fallback=[
        (OpenAI(base_url="https://openrouter.ai/api/v1", api_key="sk-or-..."), "openrouter/auto"),
        (other_client, "my-model", {}, "ollama"),
    ],
)

Fallback specs accept (client, model), (client, model, default_params), or (client, model, default_params, provider).

An override relabels attribution within the client's API dialect -- it cannot make an Anthropic client speak the OpenAI wire shape. The SDK fails loud at construction with solwyn.exceptions.ConfigurationError for:

  • an unknown provider name (the message lists the known values),
  • a cross-dialect override (e.g. provider="anthropic" on an openai.OpenAI client),
  • an override on a client object that is not a recognized provider SDK client,
  • a malformed fallback spec.

See the exceptions reference for details.

Z.AI

Z.AI's OpenAI-compatible endpoint serves the GLM model family. Point the client at it and wrap as usual — detection keys off the api.z.ai host, with glm- model prefixes as a supplement:

client = Solwyn(
    OpenAI(base_url="https://api.z.ai/api/paas/v4", api_key="..."),
    api_key="sk_proj_...",
)
response = client.chat.completions.create(
    model="glm-4.6",
    messages=[{"role": "user", "content": "Hello!"}],
)

Tracked calls are attributed as provider="zai". Z.AI is priced for text only — the provider × modality matrix has the full picture. Streamed calls settle on exact usage, not estimates: Z.AI supports stream_options.include_usage, so Solwyn injects it and reads usage from the final chunk.

Z.AI detection and streaming-usage injection require Solwyn 0.1.11 or newer.

Azure OpenAI

Azure clients are detected by class (AzureOpenAI / AsyncAzureOpenAI) or by host suffix, and attributed as provider="azure_openai".

Azure pricing follows the one thing an Azure request actually carries: your deployment name, an arbitrary string you chose — not a model catalog id. A deployment named after the OpenAI catalog model it serves (the common convention — a deployment called gpt-4o serving gpt-4o) prices at OpenAI catalog rates. Any other deployment name is accepted and recorded unpriced — real spend the platform declined to guess at, visible on the dashboard's unpriced lane, never rejected and never a silent $0. Name deployments after their catalog models if you want priced events.

Streaming usage and estimation

Sync and async streaming are fully supported (AsyncSolwyn mirrors Solwyn). Token accounting for a stream resolves in three tiers:

  1. Standard usage block -- the last chunk whose usage parses to non-zero counts wins; zeroed placeholder blocks never latch.
  2. Groq legacy x_groq.usage -- the older final-chunk shape is read when the standard block is absent.
  3. Flagged estimation -- if the provider reports no usage at all, the SDK falls back to a length-based estimate: input tokens from the pre-call length estimate, output tokens from accumulated delta character lengths at 4.0 characters per token.

Estimation is explicitly marked: the wire payload carries token_details.is_estimated = true, and the SDK logs a one-time WARNING per provider. Budgets still enforce, but per-call costs are approximate. Degraded accounting is loud and flagged, never silently zero.

Non-streaming responses behave the same way: missing usage, a zeroed usage block alongside real content, or garbage counts (negative, non-integer) fall back to flagged estimation. Provider-reported non-zero usage is never overridden, and an all-zero block on a genuinely empty response is taken as provider truth, not estimated. Usage extraction never raises -- a deliverable response is never settled as a failure over broken usage metadata.

is_estimated is serialized only when true, so payloads from providers that always report usage are unchanged.

stream_options drop-in contract

The injection policy in the table above is Solwyn's own behavior. A stream_options you pass explicitly always reaches your configured provider untouched. It is stripped only when a cross-provider failover hop lands on a provider known to reject it (and the data_sources exception applies on hops onto Azure).

Token detail fields

Compat adapters reuse the full OpenAI Chat Completions extractor: input_tokens, output_tokens, cached_input_tokens (from prompt_tokens_details.cached_tokens), reasoning_tokens, audio input/output tokens, and accepted/rejected prediction tokens -- see the OpenAI token detail table for the exact source fields. A returned service_tier is captured (bounded and string-only), as on OpenAI.

Compat providers carry no per-region pricing contract, so no provider region is reported.

Pricing

The SDK never computes cost. It reports the served (provider, model) pair verbatim -- for OpenRouter that is the full model slug (e.g. anthropic/claude-sonnet-4.5) -- and the Solwyn Cloud API prices it server-side. Models unknown to the pricing catalog surface as unpriced on the dashboard, never silently costed at $0.

Failover

Compat providers participate fully in provider failover:

  • Same-dialect hops (e.g. Groq → OpenRouter) are native passthrough -- tools, JSON mode, and streaming all survive, with no canonical-subset restriction. max_completion_tokens is rewritten to max_tokens for targets that need the legacy key (and inversely max_tokensmax_completion_tokens for OpenAI/Azure o1 / o3 / o4 / gpt-5 targets).
  • Endpoint-scoped params (extra_headers / extra_query / extra_body) are stripped on cross-provider hops -- they carry gateway credentials authored for the original endpoint -- and the target entry's own default_params versions re-apply. They are untouched on the primary and on same-provider model swaps.
  • Cross-dialect hops (e.g. Groq → Anthropic) use the standard canonical translation subset. Tool-using streams across dialects fail loud with UntranslatableRequestError before any network call.

Known limitations

Circuit-breaker health, latency signals, and failover labeling key off the provider name. Two chain entries that resolve to the same name -- two Azure resources, or two unnamed gateways both detected as openai_compatible -- share one health domain, are reported as model fallbacks of each other, and skip cross-provider request sanitization (stream_options stripping, the max_completion_tokens rewrite, endpoint-scoped param stripping). A header authored for the first endpoint then reaches the second untouched and can fail there. Give distinct endpoints distinct provider= identities -- on the constructor, or as the 4th element of a fallback spec.

chat.completions.create, embeddings.create, images.generate, audio.transcriptions.create, and audio.speech.create are intercepted on the OpenAI dialect. Embedding calls are budget-checked before the request and priced server-side from the response's usage.prompt_tokens, recorded as a cost event with modality embedding; when a compat endpoint returns no usable usage, the SDK falls back to a length-based estimate flagged is_estimated = true -- never a silent $0. Image generation is budget-checked before the request and recorded as a cost event with modality image; because compat image endpoints return no usage, its billing quantities are request-derived (image count × the model's per-image rate) -- measured inside the privacy firewall from config values (the n, size, and quality you pass), never the prompt and never image bytes. Audio calls are budget-checked and recorded as a cost event with modality audio: transcriptions price from their usage token buckets (token-billed models such as gpt-4o-transcribe) or from the whole-second audio duration on the provider's usage block (Whisper-style models -- present only on a JSON response_format; a text/srt/vtt call is recorded but unpriced and warns once), and text-to-speech prices from the input character count measured inside the privacy firewall, never the text itself. Groq is fully covered here -- its Whisper transcription prices per hour of audio and its Orpheus TTS per input character. Token-billed TTS models that report no usage (gpt-4o-mini-tts) and audio.translations warn once per process and then pass through untracked. Video generation (videos.create, Sora) is OpenAI-native only, so on an OpenAI-compatible endpoint it is unsupported — videos.create fails loud with UnsupportedSurfaceError rather than passing through. Other client surfaces (the Responses API as a call surface, etc.) pass through to the underlying client untracked.

Privacy

The same privacy posture applies as for every provider: Solwyn is a wrapper, not a proxy -- calls go direct to the provider, and prompts and responses are never captured, logged, or transmitted. Compat-specific guarantees:

  • Length-based estimation sums string lengths of message text, reasoning content, and tool-call arguments without concatenating, storing, or logging any content -- only an irreversible character count is used.
  • The streaming accumulator extracts usage and service_tier as chunks pass through and never retains chunk objects (deltas carry content).
  • The local-port detection log names the detected provider only, never the base_url, which may carry credentials.
  • Provider credentials live only on your client objects -- Solwyn's provider configuration cannot carry an api_key or base_url.

On this page