feat: llamacpp health check client

Add internal/llamacpp package with Client type and Health() method.
Client communicates with llama-server via HTTP; Health checks the
/health endpoint and reports readiness. Foundation type for the
streaming methods (Tasks 2-3).

Co-Authored-By: Virgil <virgil@lethean.io>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Claude 2026-02-19 20:50:36 +00:00
parent 9dda860df4
commit 3c756771ec
No known key found for this signature in database
GPG key ID: AF404715446AEB41
4 changed files with 111 additions and 0 deletions

7
go.mod
View file

@ -4,4 +4,11 @@ go 1.25.5
require forge.lthn.ai/core/go-inference v0.0.0
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.11.1
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace forge.lthn.ai/core/go-inference => ../go-inference

10
go.sum Normal file
View file

@ -0,0 +1,10 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -0,0 +1,52 @@
package llamacpp
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
)
// Client communicates with a llama-server instance.
type Client struct {
baseURL string
httpClient *http.Client
}
// NewClient creates a client for the llama-server at the given base URL.
func NewClient(baseURL string) *Client {
return &Client{
baseURL: strings.TrimRight(baseURL, "/"),
httpClient: &http.Client{},
}
}
type healthResponse struct {
Status string `json:"status"`
}
// Health checks whether the llama-server is ready to accept requests.
func (c *Client) Health(ctx context.Context) error {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.baseURL+"/health", nil)
if err != nil {
return err
}
resp, err := c.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("llamacpp: health returned %d", resp.StatusCode)
}
var h healthResponse
if err := json.NewDecoder(resp.Body).Decode(&h); err != nil {
return fmt.Errorf("llamacpp: health decode: %w", err)
}
if h.Status != "ok" {
return fmt.Errorf("llamacpp: server not ready (status: %s)", h.Status)
}
return nil
}

View file

@ -0,0 +1,42 @@
package llamacpp
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestHealth_OK(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/health", r.URL.Path)
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"status":"ok"}`))
}))
defer ts.Close()
c := NewClient(ts.URL)
err := c.Health(context.Background())
require.NoError(t, err)
}
func TestHealth_NotReady(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"status":"loading model"}`))
}))
defer ts.Close()
c := NewClient(ts.URL)
err := c.Health(context.Background())
assert.ErrorContains(t, err, "not ready")
}
func TestHealth_ServerDown(t *testing.T) {
c := NewClient("http://127.0.0.1:1") // nothing listening
err := c.Health(context.Background())
assert.Error(t, err)
}