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:
parent
9dda860df4
commit
3c756771ec
4 changed files with 111 additions and 0 deletions
7
go.mod
7
go.mod
|
|
@ -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
10
go.sum
Normal 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=
|
||||
52
internal/llamacpp/health.go
Normal file
52
internal/llamacpp/health.go
Normal 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
|
||||
}
|
||||
42
internal/llamacpp/health_test.go
Normal file
42
internal/llamacpp/health_test.go
Normal 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)
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue