Skip to content
skyl

provider/openaicompat

One adapter, configured per host, reaching ~18 OpenAI-shaped endpoints.

A large part of the industry serves OpenAI's wire format. One adapter reaches all of it — including local runtimes that need no credential at all.

Reference#

func New(opts ...Option) *Provider

There is no positional API key. Everything is an option, because a compatible host may need no credential.

OptionSignatureDefaultDescription
WithBaseURLfunc WithBaseURL(url string) OptionThe host to talk to. Required — New panics without it, because there is no sensible default for "some OpenAI-shaped endpoint".
WithAPIKeyfunc WithAPIKey(key string) OptionThe credential. Optional: omit it for Ollama, LM Studio and llama.cpp, which authenticate nothing.
WithNamefunc WithName(name string) Optionopenai-compatibleThe name that appears in Response.Provider, in errors, and in hook events. Set it, or every host in your fleet reports the same anonymous label.
WithHTTPClientfunc WithHTTPClient(hc *http.Client) Optionhttp.DefaultClientSupplies the *http.Client used for every request.
WithHeaderfunc WithHeader(key, value string) OptionAdds a fixed header — OpenRouter’s HTTP-Referer and X-Title, for instance.
WithMaxTokensFieldfunc WithMaxTokensField(field string) Optionmax_tokensChooses which wire field carries Request.MaxTokens.

Caveats

  • New panics without WithBaseURL. There is no sensible default for "some OpenAI-shaped endpoint", and a panic at construction beats a confusing failure on the first request. It is the only adapter constructor that can fail.
  • Set WithName. It defaults to openai-compatible, so without it every host in your fleet reports the same label in Response.Provider, in errors and in hook events — and your metrics cannot tell Groq from DeepSeek.
  • Omit WithAPIKey for local runtimes. Ollama, LM Studio and llama.cpp authenticate nothing; passing an empty string still sends a header.
  • These hosts implement OpenAI's format, not necessarily its features. Tool calling, streaming and multimodal support vary by host and by model.
  • It shares one implementation with provider/openai, so every caveat there applies here too.

openai versus openaicompat#

They share one implementation. The complete list of differences:

openaiopenaicompat
Output-cap fieldmax_completion_tokensmax_tokens
Provider name in responses, errors and hooksopenaiopenai-compatible, or whatever WithName says
Base URLdefaults to OpenAI's hostrequired — New panics without it
API keypositional argumentoptional — omit it for Ollama, LM Studio, llama.cpp
Extra headersWithOrganization, WithProjectgeneric WithHeader

Everything else — request mapping, response parsing, streaming, error classification — is byte-identical.

Verified endpoints#

HostBase URLKey?Notes
xAI (Grok)https://api.x.ai/v1Required
DeepSeekhttps://api.deepseek.com/v1Required
Mistralhttps://api.mistral.ai/v1Required
Groqhttps://api.groq.com/openai/v1Required
Togetherhttps://api.together.xyz/v1Required
Fireworkshttps://api.fireworks.ai/inference/v1Required
OpenRouterhttps://openrouter.ai/api/v1RequiredBrokers 300+ models on its own, and supplies DisplayName and ContextWindow on model listing.
Perplexityhttps://api.perplexity.aiRequired
Cerebrashttps://api.cerebras.ai/v1Required
DeepInfrahttps://api.deepinfra.com/v1/openaiRequired
Qwen / DashScopehttps://dashscope.aliyuncs.com/compatible-mode/v1Required
Moonshot (Kimi)https://api.moonshot.cn/v1Required
Z.ai (GLM)https://open.bigmodel.cn/api/paas/v4Required
Nvidia NIMhttps://integrate.api.nvidia.com/v1Required
Ollamahttp://localhost:11434/v1NoneLocal. Needs no credential — omit WithAPIKey entirely.
vLLMhttp://localhost:8000/v1NoneSelf-hosted. Returns content as an array of blocks on some builds, which skyl handles.
LM Studiohttp://localhost:1234/v1NoneLocal.
llama.cpphttp://localhost:8080/v1NoneLocal.

Usage#

A hosted provider

goCompiles
p := openaicompat.New(
	openaicompat.WithBaseURL("https://api.groq.com/openai/v1"),
	openaicompat.WithAPIKey(os.Getenv("GROQ_API_KEY")),
	openaicompat.WithName("groq"),
)
p := openaicompat.New(
	openaicompat.WithBaseURL("https://api.groq.com/openai/v1"),
	openaicompat.WithAPIKey(os.Getenv("GROQ_API_KEY")),
	openaicompat.WithName("groq"),
)

A local runtime, with no credential

goCompiles
p := openaicompat.New(
	openaicompat.WithBaseURL("http://localhost:11434/v1"),
	openaicompat.WithName("ollama"),
	// no WithAPIKey — no Authorization header is sent at all
)
p := openaicompat.New(
	openaicompat.WithBaseURL("http://localhost:11434/v1"),
	openaicompat.WithName("ollama"),
	// no WithAPIKey — no Authorization header is sent at all
)

A helper that cannot forget WithName

goCompiles
func compat(name, baseURL, key string) skyl.Provider {
	opts := []openaicompat.Option{
		openaicompat.WithBaseURL(baseURL),
		openaicompat.WithName(name), // positional in our signature, so unforgettable
	}
	if key != "" {
		opts = append(opts, openaicompat.WithAPIKey(key))
	}
	return openaicompat.New(opts...)
}
func compat(name, baseURL, key string) skyl.Provider {
	opts := []openaicompat.Option{
		openaicompat.WithBaseURL(baseURL),
		openaicompat.WithName(name), // positional in our signature, so unforgettable
	}
	if key != "" {
		opts = append(opts, openaicompat.WithAPIKey(key))
	}
	return openaicompat.New(opts...)
}

OpenRouter's attribution headers

goCompiles
p := openaicompat.New(
	openaicompat.WithBaseURL("https://openrouter.ai/api/v1"),
	openaicompat.WithAPIKey(key),
	openaicompat.WithName("openrouter"),
	openaicompat.WithHeader("HTTP-Referer", "https://example.com"),
	openaicompat.WithHeader("X-Title", "My App"),
)
p := openaicompat.New(
	openaicompat.WithBaseURL("https://openrouter.ai/api/v1"),
	openaicompat.WithAPIKey(key),
	openaicompat.WithName("openrouter"),
	openaicompat.WithHeader("HTTP-Referer", "https://example.com"),
	openaicompat.WithHeader("X-Title", "My App"),
)

Troubleshooting#

A panic at startup

WithBaseURL is required. This is the only adapter that panics on construction.

All my hosts report the same provider name

You did not set WithName. It defaults to openai-compatible.

Assistant text is empty on vLLM

Some builds return content as an array of blocks rather than a bare string. The adapter handles both — if you still see empty text, check Response.Raw for a block type other than text, which is discarded.

Tool calling does not work on this host

The host implements OpenAI's format without implementing tool calling, or the model does not support it. skyl surfaces the host's own error rather than inventing one.

Edit this page on GitHub