MLX: wire up scheduler selected context size for ps (#16918)

In the PS output, expose the scheduler selected size (clamped by model context size) instead of always reporting the model max context.  This will help provide a hint to clients to keep the context size below this value to avoid paging and poor performance on smaller VRAM systems.
This commit is contained in:
Daniel Hiltgen
2026-06-26 08:47:03 -07:00
committed by GitHub
parent 2e474c98f9
commit d26a58557d
2 changed files with 25 additions and 16 deletions

View File

@@ -594,7 +594,7 @@ func (s *Scheduler) load(req *LlmRequest, systemInfo ml.SystemInfo, gpus []ml.De
if slices.Contains(req.model.Config.Capabilities, "image") { if slices.Contains(req.model.Config.Capabilities, "image") {
llama, err = imagegen.NewServer(modelName) llama, err = imagegen.NewServer(modelName)
} else { } else {
llama, err = mlxrunner.NewClient(modelName) llama, err = mlxrunner.NewClient(modelName, req.opts.NumCtx)
} }
} }
if err != nil { if err != nil {

View File

@@ -32,29 +32,31 @@ import (
// Client wraps an MLX runner subprocess to implement llm.LlamaServer for LLM models. // Client wraps an MLX runner subprocess to implement llm.LlamaServer for LLM models.
type Client struct { type Client struct {
port int port int
modelName string modelName string
contextLength atomic.Int64 contextLength atomic.Int64
memory atomic.Uint64 softContextLength int // recommended limit to avoid poor performance
done chan struct{} memory atomic.Uint64
doneErr error // valid after done is closed done chan struct{}
client *http.Client doneErr error // valid after done is closed
status *llm.StatusWriter client *http.Client
mu sync.Mutex status *llm.StatusWriter
cmd *exec.Cmd mu sync.Mutex
cmd *exec.Cmd
} }
// NewClient prepares a new MLX runner client for LLM models. // NewClient prepares a new MLX runner client for LLM models.
// The subprocess is not started until Load() is called. // The subprocess is not started until Load() is called.
func NewClient(modelName string) (*Client, error) { func NewClient(modelName string, softContextLength int) (*Client, error) {
if err := imagegen.CheckPlatformSupport(); err != nil { if err := imagegen.CheckPlatformSupport(); err != nil {
return nil, err return nil, err
} }
c := &Client{ c := &Client{
modelName: modelName, modelName: modelName,
done: make(chan struct{}), softContextLength: softContextLength,
client: http.DefaultClient, done: make(chan struct{}),
client: http.DefaultClient,
} }
modelManifest, err := manifest.LoadManifest(modelName) modelManifest, err := manifest.LoadManifest(modelName)
@@ -223,6 +225,13 @@ func (c *Client) ContextLength() int {
return int(c.contextLength.Load()) return int(c.contextLength.Load())
} }
func (c *Client) reportedContextLength(modelContextLength int) int {
if c.softContextLength > 0 && (modelContextLength == 0 || c.softContextLength < modelContextLength) {
return c.softContextLength
}
return modelContextLength
}
// Detokenize implements llm.LlamaServer. // Detokenize implements llm.LlamaServer.
func (c *Client) Detokenize(ctx context.Context, tokens []int) (string, error) { func (c *Client) Detokenize(ctx context.Context, tokens []int) (string, error) {
return "", errors.New("not supported") return "", errors.New("not supported")
@@ -421,7 +430,7 @@ func (c *Client) Ping(ctx context.Context) error {
return err return err
} }
c.contextLength.Store(int64(status.ContextLength)) c.contextLength.Store(int64(c.reportedContextLength(status.ContextLength)))
c.memory.Store(status.Memory) c.memory.Store(status.Memory)
return nil return nil