github.com/thinwrap/llm-go
thinwrap/llm-go for Go — one Chat facade over 18 LLM providers (15 OpenAI-compatible + native Anthropic, Bedrock, Gemini) with streaming, plus embeddings on 11. Truly zero deps (net/http only), stateless, BYO *http.Client.
Go constraints
- Runtime dependencies
- Zero runtime dependencies
- BYO HTTP client
- injectable *http.Client via the WithHTTPClient option
- Provenance
- Go module checksum database (sum.golang.org)
- Minimum runtime
- Go 1.18+
- Truly zero dependencies — the standard library net/http is the only HTTP surface, and AWS SigV4 (Bedrock) / JWT signing (FCM, APNs) are hand-rolled on crypto/*.
- Distributed as a Go module with no build step or codegen: the git tag IS the release. The public module proxy serves it directly and integrity is verified through the Go checksum database (sum.golang.org) and your go.sum — there is no separately signed release artifact, and none is needed for `go get`.
- The default *http.Client never follows redirects — a 3xx surfaces as an error rather than silently re-sending auth headers to another host.
30-second install
go get github.com/thinwrap/llm-go2-minute end-to-end example
One Chat facade, every provider. Switching vendor is the provider id plus the model — the ChatInput andChatResult shapes stay identical, and errors are values (*ConnectorError via errors.As).
chat, err := llm.NewChat(llm.OpenAI, llm.OpenAICompatConfig{APIKey: os.Getenv("OPENAI_API_KEY")})
if err != nil { log.Fatal(err) }
res, err := chat.Complete(ctx, llm.ChatInput{
Model: "gpt-4o-mini",
Messages: []llm.ChatMessage{{Role: "user", Content: "Say hi in one word."}},
})
if err != nil {
var ce *llm.ConnectorError
if errors.As(err, &ce) { log.Printf("%s: %s", ce.ProviderCode, ce.ProviderMessage) }
return
}
fmt.Println(res.Message.Content, res.Usage.TotalTokens)The 15 first-class OpenAI-compatible providers all takeOpenAICompatConfig; Anthropic,Bedrock, and Gemini are native adapters with their own config structs. Same Complete(ctx, in) shape throughout.
Supported providers
| Provider | Operations | Auth | README |
|---|---|---|---|
openai OpenAI | chat, embeddings | bearer | docs |
azure-openai Azure OpenAI | chat, embeddings | header | docs |
anthropic Anthropic | chat | header | docs |
gemini Google Gemini | chat | header | docs |
bedrock AWS Bedrock | chat | aws-sig-v4 | docs |
mistral Mistral | chat, embeddings | bearer | docs |
cloudflare Cloudflare Workers AI | chat, embeddings | bearer | docs |
together Together AI | chat, embeddings | bearer | docs |
fireworks Fireworks AI | chat, embeddings | bearer | docs |
deepinfra DeepInfra | chat, embeddings | bearer | docs |
openrouter OpenRouter | chat, embeddings | bearer | docs |
groq Groq | chat | bearer | docs |
deepseek DeepSeek | chat | bearer | docs |
xai xAI (Grok) | chat | bearer | docs |
perplexity Perplexity | chat | bearer | docs |
vllm vLLM | chat, embeddings | bearer | docs |
ollama Ollama | chat, embeddings | bearer | docs |
lmstudio LM Studio | chat, embeddings | bearer | docs |
Migration paths
The in-process complement to a gateway
Thinwrap LLM is a thin, in-process, zero-egress facade — it does not proxy your traffic. It normalizes the request/response shape across vendors so your code stays vendor-neutral; routing, budgets, and observability stay wherever you already run them. Provider-specific request fields ride through Passthrough; the decoded vendor body is always onresult.Raw.
Streaming
stream, err := chat.Stream(ctx, in)
if err != nil { return }
defer stream.Close()
for {
delta, err := stream.Recv()
if err == io.EOF { break }
if err != nil { return }
fmt.Print(delta.ContentDelta)
}Embeddings
emb, _ := llm.NewEmbeddings(llm.OpenAI, llm.OpenAICompatConfig{APIKey: key})
out, _ := emb.Create(ctx, llm.EmbeddingsInput{Model: "text-embedding-3-small", Input: []string{"hello", "world"}})
// out.Embeddings is [][]float64 in input orderEmbeddings ship on the OpenAI-float subset (11 of the 18 providers — see the table above).
Cross-language siblings
The TypeScript (/packages/llm-ts) and PHP (/packages/llm-php) variants ship the same facade shape and provider set — cross-language parity is a design goal, not a hard release gate.