Skip to content

Commit d160532

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

4 files changed

Lines changed: 42 additions & 1 deletion

File tree

pkg/mcp/localaitools/client.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ type LocalAIClient interface {
6464
// ---- System ----
6565
SystemInfo(ctx context.Context) (*SystemInfo, error)
6666
ListNodes(ctx context.Context) ([]Node, error)
67+
ListScheduling(ctx context.Context) ([]ModelSchedulingConfig, error)
68+
GetScheduling(ctx context.Context, modelName string) (*ModelSchedulingConfig, error)
69+
SetScheduling(ctx context.Context, req SetSchedulingRequest) (*ModelSchedulingConfig, error)
70+
DeleteScheduling(ctx context.Context, modelName string) error
6771
// SetNodeVRAMBudget sets (or, with an empty budget, clears) a federated
6872
// node's VRAM allocation cap as a sticky admin override. Only meaningful
6973
// in distributed mode; single-process clients report it as unavailable.

pkg/mcp/localaitools/dto.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package localaitools
22

3-
import "github.com/mudler/LocalAI/core/services/voiceprofile"
3+
import (
4+
"github.com/mudler/LocalAI/core/services/nodes"
5+
"github.com/mudler/LocalAI/core/services/voiceprofile"
6+
)
47

58
// DTOs for the LocalAIClient interface. Where the same shape already exists
69
// elsewhere (config.Gallery, gallery.Metadata, schema.KnownBackend,
@@ -115,6 +118,31 @@ type SetNodeVRAMBudgetRequest struct {
115118
Budget string `json:"budget,omitempty" jsonschema:"VRAM allocation cap as a percentage (e.g. 80%) or absolute amount (e.g. 12GB). Empty string clears the override."`
116119
}
117120

121+
// ModelSchedulingConfig is the REST/MCP wire shape for one per-model
122+
// distributed scheduling rule. The NodeSelector field is a JSON object encoded
123+
// as a string, matching /api/nodes/scheduling responses.
124+
type ModelSchedulingConfig = nodes.ModelSchedulingConfig
125+
126+
// SetSchedulingRequest is the input for set_scheduling. It mirrors
127+
// /api/nodes/scheduling so standalone MCP and REST callers preserve the same
128+
// PATCH-style semantics for the optional prefix-cache routing fields.
129+
type SetSchedulingRequest struct {
130+
ModelName string `json:"model_name" jsonschema:"Installed model name whose distributed scheduling rule should be created or updated."`
131+
NodeSelector map[string]string `json:"node_selector,omitempty" jsonschema:"Optional node-label selector. Empty means any healthy backend node."`
132+
MinReplicas int `json:"min_replicas" jsonschema:"Minimum desired replicas. Mutually exclusive with spread_all."`
133+
MaxReplicas int `json:"max_replicas" jsonschema:"Maximum desired replicas. Must be >= min_replicas when non-zero. Mutually exclusive with spread_all."`
134+
SpreadAll bool `json:"spread_all,omitempty" jsonschema:"When true, keep one replica on every matching node. Mutually exclusive with min_replicas/max_replicas."`
135+
RoutePolicy *string `json:"route_policy,omitempty" jsonschema:"Optional prefix-cache route policy override. Omit to preserve the existing value on updates."`
136+
BalanceAbsThreshold *int `json:"balance_abs_threshold,omitempty" jsonschema:"Optional absolute imbalance threshold override. Omit to preserve the existing value on updates."`
137+
BalanceRelThreshold *float64 `json:"balance_rel_threshold,omitempty" jsonschema:"Optional relative imbalance threshold override. Omit to preserve the existing value on updates."`
138+
MinPrefixMatch *float64 `json:"min_prefix_match,omitempty" jsonschema:"Optional minimum prefix match threshold override. Omit to preserve the existing value on updates."`
139+
}
140+
141+
// DeleteSchedulingRequest identifies the model scheduling rule to remove.
142+
type DeleteSchedulingRequest struct {
143+
ModelName string `json:"model_name" jsonschema:"Installed model name whose scheduling config should be removed."`
144+
}
145+
118146
// ImportModelURIRequest is the input for import_model_uri. It mirrors the
119147
// REST surface (`/models/import-uri`) closely so both clients can produce
120148
// identical responses; the BackendPreference is a flat field rather than the

pkg/mcp/localaitools/httpapi/routes.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const (
2424
routeBackendsKnown = "/backends/known"
2525
routeBackendsApply = "/backends/apply"
2626
routeNodes = "/api/nodes"
27+
routeScheduling = "/api/nodes/scheduling"
2728
routeVRAMEstimate = "/api/models/vram-estimate"
2829
routeBranding = "/api/branding"
2930
routeSettings = "/api/settings"
@@ -66,3 +67,7 @@ func routeVoiceProfileDelete(id string) string {
6667
func routeNodeVRAMBudget(id string) string {
6768
return "/api/nodes/" + url.PathEscape(id) + "/vram-budget"
6869
}
70+
71+
func routeModelScheduling(modelName string) string {
72+
return "/api/nodes/scheduling/" + url.PathEscape(modelName)
73+
}

pkg/mcp/localaitools/tools.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ const (
1717
ToolListKnownBackends = "list_known_backends"
1818
ToolSystemInfo = "system_info"
1919
ToolListNodes = "list_nodes"
20+
ToolListScheduling = "list_scheduling"
21+
ToolGetScheduling = "get_scheduling"
2022
ToolVRAMEstimate = "vram_estimate"
2123
ToolGetBranding = "get_branding"
2224
ToolGetUsageStats = "get_usage_stats"
@@ -42,6 +44,8 @@ const (
4244
ToolCreateVoiceProfile = "create_voice_profile"
4345
ToolDeleteVoiceProfile = "delete_voice_profile"
4446
ToolSetNodeVRAMBudget = "set_node_vram_budget"
47+
ToolSetScheduling = "set_scheduling"
48+
ToolDeleteScheduling = "delete_scheduling"
4549

4650
// ToolListAliases is read-only but lives here so the alias tools stay
4751
// grouped; the catalog tests assert its read-only placement.

0 commit comments

Comments
 (0)