Skip to content

Commit 20e19e0

Browse files
committed
feat(mcp): add scheduling HTTP client support
Assisted-by: Hephaestus:openai/gpt-5.5 Signed-off-by: Owen Adirah <owenadira@gmail.com>
1 parent d160532 commit 20e19e0

2 files changed

Lines changed: 100 additions & 0 deletions

File tree

pkg/mcp/localaitools/httpapi/client.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,49 @@ func (c *Client) ListNodes(ctx context.Context) ([]localaitools.Node, error) {
483483
return out, nil
484484
}
485485

486+
func (c *Client) ListScheduling(ctx context.Context) ([]localaitools.ModelSchedulingConfig, error) {
487+
var out []localaitools.ModelSchedulingConfig
488+
if err := c.do(ctx, http.MethodGet, routeScheduling, nil, &out); err != nil {
489+
if errors.Is(err, ErrHTTPNotFound) {
490+
return []localaitools.ModelSchedulingConfig{}, nil
491+
}
492+
return nil, err
493+
}
494+
return out, nil
495+
}
496+
497+
func (c *Client) GetScheduling(ctx context.Context, modelName string) (*localaitools.ModelSchedulingConfig, error) {
498+
if modelName == "" {
499+
return nil, errors.New("model_name is required")
500+
}
501+
var out localaitools.ModelSchedulingConfig
502+
if err := c.do(ctx, http.MethodGet, routeModelScheduling(modelName), nil, &out); err != nil {
503+
if errors.Is(err, ErrHTTPNotFound) {
504+
return nil, nil
505+
}
506+
return nil, err
507+
}
508+
return &out, nil
509+
}
510+
511+
func (c *Client) SetScheduling(ctx context.Context, req localaitools.SetSchedulingRequest) (*localaitools.ModelSchedulingConfig, error) {
512+
if req.ModelName == "" {
513+
return nil, errors.New("model_name is required")
514+
}
515+
var out localaitools.ModelSchedulingConfig
516+
if err := c.do(ctx, http.MethodPost, routeScheduling, req, &out); err != nil {
517+
return nil, err
518+
}
519+
return &out, nil
520+
}
521+
522+
func (c *Client) DeleteScheduling(ctx context.Context, modelName string) error {
523+
if modelName == "" {
524+
return errors.New("model_name is required")
525+
}
526+
return c.do(ctx, http.MethodDelete, routeModelScheduling(modelName), nil, nil)
527+
}
528+
486529
func (c *Client) SetNodeVRAMBudget(ctx context.Context, nodeID, budget string) error {
487530
// PUT with an empty value clears the override server-side (Task 9), so we
488531
// use PUT uniformly rather than switching to DELETE for the clear case.

pkg/mcp/localaitools/httpapi/client_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,34 @@ func fakeLocalAI() *httptest.Server {
8484
})
8585
})
8686

87+
mux.HandleFunc("/api/nodes/scheduling", func(w http.ResponseWriter, r *http.Request) {
88+
switch r.Method {
89+
case http.MethodGet:
90+
_ = json.NewEncoder(w).Encode([]map[string]any{{"model_name": "qwen", "min_replicas": 1, "max_replicas": 2}})
91+
case http.MethodPost:
92+
var body map[string]any
93+
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
94+
http.Error(w, err.Error(), http.StatusBadRequest)
95+
return
96+
}
97+
body["id"] = "sched-1"
98+
_ = json.NewEncoder(w).Encode(body)
99+
default:
100+
http.Error(w, "method", http.StatusMethodNotAllowed)
101+
}
102+
})
103+
104+
mux.HandleFunc("/api/nodes/scheduling/qwen", func(w http.ResponseWriter, r *http.Request) {
105+
switch r.Method {
106+
case http.MethodGet:
107+
_ = json.NewEncoder(w).Encode(map[string]any{"model_name": "qwen", "spread_all": true})
108+
case http.MethodDelete:
109+
w.WriteHeader(http.StatusNoContent)
110+
default:
111+
http.Error(w, "method", http.StatusMethodNotAllowed)
112+
}
113+
})
114+
87115
return httptest.NewServer(mux)
88116
}
89117

@@ -197,6 +225,35 @@ var _ = Describe("httpapi.Client against the LocalAI admin REST surface", func()
197225
Expect(bs[0].Installed).To(BeTrue())
198226
})
199227
})
228+
229+
Describe("Scheduling", func() {
230+
It("lists scheduling configs", func() {
231+
out, err := c.ListScheduling(ctx)
232+
Expect(err).ToNot(HaveOccurred())
233+
Expect(out).To(HaveLen(1))
234+
Expect(out[0].ModelName).To(Equal("qwen"))
235+
Expect(out[0].MinReplicas).To(Equal(1))
236+
})
237+
238+
It("gets one scheduling config", func() {
239+
out, err := c.GetScheduling(ctx, "qwen")
240+
Expect(err).ToNot(HaveOccurred())
241+
Expect(out.ModelName).To(Equal("qwen"))
242+
Expect(out.SpreadAll).To(BeTrue())
243+
})
244+
245+
It("sets a scheduling config", func() {
246+
out, err := c.SetScheduling(ctx, localaitools.SetSchedulingRequest{ModelName: "qwen", MinReplicas: 1, MaxReplicas: 2})
247+
Expect(err).ToNot(HaveOccurred())
248+
Expect(out.ID).To(Equal("sched-1"))
249+
Expect(out.ModelName).To(Equal("qwen"))
250+
Expect(out.MaxReplicas).To(Equal(2))
251+
})
252+
253+
It("deletes a scheduling config", func() {
254+
Expect(c.DeleteScheduling(ctx, "qwen")).To(Succeed())
255+
})
256+
})
200257
})
201258

202259
var _ = Describe("Model aliases", func() {

0 commit comments

Comments
 (0)