Skip to content

Commit 2fc84cf

Browse files
author
Piotr Stankiewicz
committed
Include llama-server output in server error
Signed-off-by: Piotr Stankiewicz <[email protected]>
1 parent 423e1d6 commit 2fc84cf

File tree

4 files changed

+165
-1
lines changed

4 files changed

+165
-1
lines changed

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ require (
1111
github.com/opencontainers/go-digest v1.0.0
1212
github.com/opencontainers/image-spec v1.1.1
1313
github.com/sirupsen/logrus v1.9.3
14+
github.com/stretchr/testify v1.10.0
1415
golang.org/x/sync v0.12.0
1516
)
1617

@@ -20,6 +21,7 @@ require (
2021
github.com/containerd/errdefs v1.0.0 // indirect
2122
github.com/containerd/log v0.1.0 // indirect
2223
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
24+
github.com/davecgh/go-spew v1.1.1 // indirect
2325
github.com/distribution/reference v0.6.0 // indirect
2426
github.com/docker/cli v27.5.0+incompatible // indirect
2527
github.com/docker/distribution v2.8.3+incompatible // indirect
@@ -38,6 +40,7 @@ require (
3840
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
3941
github.com/modern-go/reflect2 v1.0.2 // indirect
4042
github.com/pkg/errors v0.9.1 // indirect
43+
github.com/pmezard/go-difflib v1.0.0 // indirect
4144
github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529 // indirect
4245
github.com/smallnest/ringbuffer v0.0.0-20241116012123-461381446e3d // indirect
4346
github.com/vbatts/tar-split v0.11.6 // indirect

pkg/inference/backends/llamacpp/llamacpp.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,21 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"io"
78
"io/fs"
89
"net/http"
910
"os"
1011
"os/exec"
1112
"path/filepath"
1213
"runtime"
1314
"strconv"
15+
"strings"
1416

1517
"github.com/docker/model-runner/pkg/diskusage"
1618
"github.com/docker/model-runner/pkg/inference"
1719
"github.com/docker/model-runner/pkg/inference/models"
1820
"github.com/docker/model-runner/pkg/logging"
21+
"github.com/docker/model-runner/pkg/tailbuffer"
1922
)
2023

2124
const (
@@ -162,9 +165,11 @@ func (l *llamaCpp) Run(ctx context.Context, socket, model string, mode inference
162165
}
163166
return llamaCppProcess.Process.Signal(os.Interrupt)
164167
}
168+
tailBuf := tailbuffer.NewTailBuffer(1024)
165169
serverLogStream := l.serverLog.Writer()
170+
out := io.MultiWriter(serverLogStream, tailBuf)
166171
llamaCppProcess.Stdout = serverLogStream
167-
llamaCppProcess.Stderr = serverLogStream
172+
llamaCppProcess.Stderr = out
168173

169174
if err := llamaCppProcess.Start(); err != nil {
170175
return fmt.Errorf("unable to start llama.cpp: %w", err)
@@ -174,6 +179,18 @@ func (l *llamaCpp) Run(ctx context.Context, socket, model string, mode inference
174179
go func() {
175180
llamaCppErr := llamaCppProcess.Wait()
176181
serverLogStream.Close()
182+
183+
errOutput := new(strings.Builder)
184+
if _, err := io.Copy(errOutput, tailBuf); err != nil {
185+
l.log.Warnf("failed to read server output tail: %w", err)
186+
}
187+
188+
if len(errOutput.String()) != 0 {
189+
llamaCppErr = fmt.Errorf("llama.cpp exit status: %w\nwith output: %s", llamaCppErr, errOutput.String())
190+
} else {
191+
llamaCppErr = fmt.Errorf("llama.cpp exit status: %w", llamaCppErr)
192+
}
193+
177194
llamaCppErrors <- llamaCppErr
178195
close(llamaCppErrors)
179196
if err := os.Remove(socket); err != nil && !errors.Is(err, fs.ErrNotExist) {

pkg/tailbuffer/tailbuffer.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package tailbuffer
2+
3+
import (
4+
"io"
5+
"sync"
6+
)
7+
8+
type tailBuffer struct {
9+
lock sync.Mutex
10+
buf []byte
11+
capacity uint
12+
size uint
13+
read uint
14+
write uint
15+
}
16+
17+
func NewTailBuffer(size uint) io.ReadWriter {
18+
return &tailBuffer{
19+
buf: make([]byte, size),
20+
capacity: size,
21+
size: 0,
22+
read: 0,
23+
write: 0,
24+
}
25+
}
26+
27+
func (w *tailBuffer) Write(buffer []byte) (int, error) {
28+
w.lock.Lock()
29+
defer w.lock.Unlock()
30+
31+
written := 0
32+
shouldPushRead := false
33+
si := 0
34+
if len(buffer) > int(w.capacity) {
35+
si = len(buffer) - int(w.capacity)
36+
}
37+
for _, b := range buffer[si:] {
38+
if shouldPushRead {
39+
if w.read+1 < w.capacity {
40+
w.read += 1
41+
} else {
42+
w.read = 0
43+
}
44+
}
45+
w.buf[w.write] = b
46+
if w.write+1 < w.capacity {
47+
w.write += 1
48+
} else {
49+
w.write = 0
50+
}
51+
w.size += 1
52+
if w.size > w.capacity {
53+
w.size = w.capacity
54+
}
55+
shouldPushRead = w.write == w.read
56+
written += 1
57+
}
58+
return si + written, nil
59+
}
60+
61+
func (w *tailBuffer) Read(buffer []byte) (int, error) {
62+
w.lock.Lock()
63+
defer w.lock.Unlock()
64+
65+
var err error
66+
read := uint(0)
67+
for read < w.size && int(read) < len(buffer) {
68+
buffer[read] = w.buf[w.read]
69+
if w.read+1 < w.capacity {
70+
w.read += 1
71+
} else {
72+
w.read = 0
73+
}
74+
read += 1
75+
}
76+
w.size -= read
77+
if read == 0 {
78+
err = io.EOF
79+
}
80+
return int(read), err
81+
}

pkg/tailbuffer/tailbuffer_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package tailbuffer
2+
3+
import (
4+
"io"
5+
"strings"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestLogBufferCreation(t *testing.T) {
12+
lb := NewTailBuffer(0)
13+
require.NotNil(t, lb)
14+
}
15+
16+
func TestLogBufferWrite(t *testing.T) {
17+
lb := NewTailBuffer(1024)
18+
n, err := lb.Write([]byte("asdf"))
19+
require.NoError(t, err)
20+
require.Equal(t, 4, n)
21+
}
22+
23+
func TestLogBufferReadEmpty(t *testing.T) {
24+
lb := NewTailBuffer(4)
25+
buf := make([]byte, 4)
26+
_, err := lb.Read(buf)
27+
require.Error(t, err, io.EOF)
28+
}
29+
30+
func TestLogBufferWriteRead(t *testing.T) {
31+
lb := NewTailBuffer(4)
32+
n, err := lb.Write([]byte("asdfg"))
33+
require.NoError(t, err)
34+
require.Equal(t, 5, n)
35+
buf := make([]byte, 4)
36+
n, err = lb.Read(buf)
37+
require.NoError(t, err)
38+
require.Equal(t, 4, n)
39+
require.Equal(t, []byte("sdfg"), buf)
40+
n, err = lb.Write([]byte("hjklzx"))
41+
require.NoError(t, err)
42+
require.Equal(t, 6, n)
43+
buf = make([]byte, 3)
44+
n, err = lb.Read(buf)
45+
require.NoError(t, err)
46+
require.Equal(t, 3, n)
47+
require.Equal(t, []byte("klz"), buf)
48+
n, err = lb.Read(buf)
49+
require.NoError(t, err)
50+
require.Equal(t, 1, n)
51+
}
52+
53+
func TestLogBufferWriteReadString(t *testing.T) {
54+
lb := NewTailBuffer(4)
55+
n, err := lb.Write([]byte("asdfg"))
56+
require.NoError(t, err)
57+
require.Equal(t, 5, n)
58+
str := new(strings.Builder)
59+
nw, err := io.Copy(str, lb)
60+
require.NoError(t, err)
61+
require.Equal(t, int64(4), nw)
62+
require.Equal(t, "sdfg", str.String())
63+
}

0 commit comments

Comments
 (0)