A Go client for LM Studio's local server — chat, stream, embed, and manage AI models running on your own machine. Fully compatible with any OpenAI-format endpoint too.
package main
import (
"context"
"fmt"
"github.com/bearaujus/blmstudio"
)
func main() {
client := blmstudio.New() // connects to http://localhost:1234 by default
resp, err := client.ChatCompletions(context.Background(), &blmstudio.ChatCompletionRequest{
Model: "lmstudio-community/meta-llama-3-8b-instruct",
Messages: []blmstudio.Message{
{Role: blmstudio.RoleUser, Content: "Hello, world!"},
},
})
if err != nil {
panic(err)
}
fmt.Println(resp.Choices[0].Message.Content)
}Ready-to-run examples are in the examples/ folder.
| Example | What it shows |
|---|---|
examples/chat |
Single-turn chat (non-streaming) |
examples/streaming |
Token-by-token streaming chat |
examples/embed |
Batch embeddings + cosine similarity |
Every example reads provider config from environment variables so you can switch between LM Studio, OpenAI, or any compatible endpoint without touching code:
# 🏠 LM Studio (local) — default, no env vars needed
LM_MODEL=lmstudio-community/meta-llama-3-8b-instruct go run examples/chat/main.go
# ☁️ OpenAI
LM_BASE_URL=https://api.openai.com LM_API_KEY=sk-... LM_MODEL=gpt-4o-mini go run examples/chat/main.go
# 🔀 Streaming
LM_MODEL=lmstudio-community/meta-llama-3-8b-instruct go run examples/streaming/main.go
# 🔢 Embeddings
LM_EMBED_MODEL=nomic-ai/nomic-embed-text-v1.5 go run examples/embed/main.gogo get github.com/bearaujus/blmstudioRequires Go 1.21+. No C dependencies. No external runtime packages.
All options are set at construction time via functional options:
client := blmstudio.New(
blmstudio.WithBaseURL("http://localhost:1234"), // default
blmstudio.WithAPIToken("your-api-key"), // optional
blmstudio.WithTimeout(2*time.Minute), // default: 5 min
)| Option | Default | Description |
|---|---|---|
WithBaseURL(url) |
http://localhost:1234 |
Server base URL. Trailing slashes stripped. |
WithAPIToken(token) |
"" (no auth) |
Sets Authorization: Bearer <token>. |
WithHTTPClient(hc) |
built-in | Supply your own *http.Client (proxies, TLS, etc.). |
WithTimeout(d) |
5 * time.Minute |
Overrides the HTTP timeout. |
func New(opts ...Option) ClientReturns a thread-safe Client. Create once and share across goroutines.
Chat(ctx, *ChatRequest) (*ChatResponse, error)
ChatStream(ctx, *ChatRequest) (*StreamReader[ChatStreamEvent], error)ChatCompletions(ctx, *ChatCompletionRequest) (*ChatCompletionResponse, error)
ChatCompletionsStream(ctx, *ChatCompletionRequest) (*StreamReader[ChatCompletionChunk], error)Supports: Model, Messages, Temperature, TopP, MaxTokens, Tools, ToolChoice, ResponseFormat, Stop, N, FrequencyPenalty, PresencePenalty.
TextCompletions(ctx, *TextCompletionRequest) (*TextCompletionResponse, error)
TextCompletionsStream(ctx, *TextCompletionRequest) (*StreamReader[TextCompletionChunk], error)Embed(ctx, *EmbeddingRequest) (*EmbeddingResponse, error)// Single string
req := &blmstudio.EmbeddingRequest{
Model: "nomic-ai/nomic-embed-text-v1.5",
Input: blmstudio.NewEmbeddingStringInput("hello"),
}
// Batch
req := &blmstudio.EmbeddingRequest{
Model: "nomic-ai/nomic-embed-text-v1.5",
Input: blmstudio.NewEmbeddingStringsInput([]string{"hello", "world"}),
}
resp, _ := client.Embed(ctx, req)
fmt.Printf("%d-dim vector\n", len(resp.Data[0].Embedding))ListModels(ctx) (*ListModelsResponse, error)
IsModelRunning(ctx, model string) error // nil = running
LoadModel(ctx, *LoadModelRequest) (*LoadModelResponse, error)
UnloadModel(ctx, instanceID string) (*UnloadModelResponse, error)
DownloadModel(ctx, model string) (*DownloadModelResponse, error)
GetDownloadStatus(ctx, jobID string) (*DownloadStatusResponse, error)
ListOpenAIModels(ctx) (*OpenAIListModelsResponse, error)All streaming methods return *StreamReader[T]. Always defer Close().
stream, err := client.ChatCompletionsStream(ctx, req)
if err != nil { ... }
defer stream.Close()
for stream.Next() {
chunk := stream.Event()
for _, choice := range chunk.Choices {
fmt.Print(choice.Delta.Content)
}
}
if err := stream.Err(); err != nil { ... }| Feature | Native /api/v1 |
OpenAI /v1 |
|---|---|---|
| Chat (non-streaming) | ✅ Chat |
✅ ChatCompletions |
| Chat (streaming) | ✅ ChatStream |
✅ ChatCompletionsStream |
| Text completions | ❌ | ✅ TextCompletions / Stream |
| Embeddings | ❌ | ✅ Embed |
| List models (rich) | ✅ ListModels |
✅ ListOpenAIModels |
| Load / unload model | ✅ | ❌ |
| Download model | ✅ | ❌ |
| Check if running | ✅ IsModelRunning |
❌ |
| Error | When returned |
|---|---|
ErrEmptyPrompt |
Chat* / TextCompletion* with no messages |
ErrEmptyText |
Embed with no input |
ErrModelNotFound |
IsModelRunning when model is not loaded |
ErrEmptyModel |
LoadModel / DownloadModel with empty model ID |
ErrEmptyInstanceID |
UnloadModel with empty instance ID |
ErrEmptyJobID |
GetDownloadStatus with empty job ID |
_, err := client.ChatCompletions(ctx, req)
if err != nil {
if ae, ok := blmstudio.IsAPIError(err); ok {
fmt.Printf("HTTP %d: %s\n", ae.StatusCode, ae.Message)
// ae.RawBody contains the full response for debugging
}
// sentinel errors still work with errors.Is:
if errors.Is(err, blmstudio.ErrEmptyPrompt) { ... }
}- Download and install LM Studio.
- Open the Discover tab, find a model (e.g.
meta-llama-3-8b-instruct), and download it. - Go to the Developer tab → click Start Server (default port:
1234). - (Optional) Enable Authentication and copy the API key — pass it via
WithAPIToken.
CLI alternative (headless / CI):
lms server start
lms get lmstudio-community/meta-llama-3-8b-instruct
lms load lmstudio-community/meta-llama-3-8b-instruct┌──────────────────────────────────────────────────────┐
│ blmstudio.Client │ ← public interface
│ │
│ Chat / ChatStream /api/v1/chat │
│ ChatCompletions / Stream /v1/chat/completions │
│ TextCompletions / Stream /v1/completions │
│ Embed /v1/embeddings │
│ ListModels / Load / Unload /api/v1/models │
│ ListOpenAIModels /v1/models │
└────────────────────────┬─────────────────────────────┘
│
┌────────────▼────────────┐
│ *client (private) │ doJSON / doStream
│ baseURL · apiToken │
│ *http.Client │
└────────────┬────────────┘
│ HTTP / SSE
┌────────────▼────────────┐
│ LM Studio Server │ http://localhost:1234
│ (or any OAI proxy) │
└─────────────────────────┘
StreamReader[T] (stream.go)
├── ChatStream → StreamReader[ChatStreamEvent]
├── ChatCompletionsStream → StreamReader[ChatCompletionChunk]
└── TextCompletionsStream → StreamReader[TextCompletionChunk]
See CONTRIBUTING.md for dev setup, branch naming, commit format, and how to add new endpoints or providers.