Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions config-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,48 @@
"type": "boolean",
"default": false,
"description": "If true the model will not show up in /v1/models responses. It can still be used as normal in API requests."
},
"variants": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"cmdAdd": {
"type": "string",
"description": "Additional command line arguments to append or override. Arguments with the same flag name will override the base cmd."
},
"name": {
"type": "string",
"description": "Display name override for this variant."
},
"description": {
"type": "string",
"description": "Description override for this variant."
},
"env": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Z_][A-Z0-9_]*=.*$"
},
"description": "Additional environment variables for this variant."
},
"aliases": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"description": "Additional aliases for this variant."
},
"unlisted": {
"type": "boolean",
"description": "Override unlisted setting for this variant."
}
},
"additionalProperties": false
},
"description": "Template-based configuration that generates multiple model variants. Each key becomes a suffix to the model name (e.g., 'model-variant'). Variant values can override cmd arguments and other settings. See issue #549."
}
}
}
Expand Down
45 changes: 45 additions & 0 deletions config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,51 @@ models:
# - optional, default: undefined (use global setting)
sendLoadingState: false

# Variants example (issue #549):
# Use variants to generate multiple model configurations from a single template.
# Each variant key becomes a suffix to the model name (e.g., "Qwen3.5-thinking_normal").
# Variant's cmdAdd can override or add command line arguments.
"Qwen3.5-35B-A3B":
cmd: |
llama-server
--port ${PORT}
--model /models/Qwen3.5-35B-A3B.gguf
--temp 0.8
--ctx-size 16384
name: "Qwen3.5 35B"
description: "Qwen3.5 thinking model with multiple parameter variants"

# variants: generates multiple model configurations from this template
# - each key becomes a model name suffix (model-variant)
# - cmdAdd: additional/override command line arguments
# - name, description, env, aliases, unlisted: override base settings
variants:
# Generates: Qwen3.5-35B-A3B-thinking_normal
thinking_normal:
cmdAdd: --temp 1.0
name: "Qwen3.5 35B (Thinking, Normal)"
description: "For general tasks with thinking enabled"

# Generates: Qwen3.5-35B-A3B-thinking_coding
thinking_coding:
cmdAdd: --temp 0.6
name: "Qwen3.5 35B (Thinking, Coding)"
description: "For coding tasks with thinking enabled"

# Generates: Qwen3.5-35B-A3B-nothinking_normal
nothinking_normal:
cmdAdd: "--temp 1.0 --chat-template-kwargs '{\"enable_thinking\": false}'"
name: "Qwen3.5 35B (No Thinking, Normal)"
description: "For general tasks without thinking"

# Generates: Qwen3.5-35B-A3B-nothinking_coding
nothinking_coding:
cmdAdd: "--temp 0.6 --chat-template-kwargs '{\"enable_thinking\": false}'"
name: "Qwen3.5 35B (No Thinking, Coding)"
description: "For coding tasks without thinking"
aliases:
- "qwen-code"

# Unlisted model example:
"qwen-unlisted":
# unlisted: boolean, true or false
Expand Down
44 changes: 44 additions & 0 deletions proxy/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,38 @@ func (c *Config) FindConfig(modelName string) (ModelConfig, string, bool) {
}
}

// substituteTemplateRefsInConfig replaces template model IDs in groups.members and
// hooks.on_startup.preload with the corresponding expanded variant IDs.
func substituteTemplateRefsInConfig(c *Config, templateToVariants map[string][]string) {
if len(templateToVariants) == 0 {
return
}
for groupID := range c.Groups {
group := c.Groups[groupID]
var newMembers []string
for _, member := range group.Members {
if variantIDs, ok := templateToVariants[member]; ok {
newMembers = append(newMembers, variantIDs...)
} else {
newMembers = append(newMembers, member)
}
}
group.Members = newMembers
c.Groups[groupID] = group
}
if len(c.Hooks.OnStartup.Preload) > 0 {
var newPreload []string
for _, modelID := range c.Hooks.OnStartup.Preload {
if variantIDs, ok := templateToVariants[modelID]; ok {
newPreload = append(newPreload, variantIDs...)
} else {
newPreload = append(newPreload, modelID)
}
}
c.Hooks.OnStartup.Preload = newPreload
}
}

func LoadConfig(path string) (Config, error) {
file, err := os.Open(path)
if err != nil {
Expand Down Expand Up @@ -208,6 +240,18 @@ func LoadConfigFromReader(r io.Reader) (Config, error) {
return Config{}, err
}

// Expand model variants before any other processing
// This transforms template models with variants into individual model configs
if config.Models != nil {
expanded, err := ExpandVariants(config.Models)
if err != nil {
return Config{}, err
}
config.Models = expanded.Models
// Substitute template IDs in groups.members and hooks.on_startup.preload with variant IDs
substituteTemplateRefsInConfig(&config, expanded.TemplateToVariants)
}

if config.HealthCheckTimeout < 15 {
config.HealthCheckTimeout = 15
}
Expand Down
27 changes: 27 additions & 0 deletions proxy/config/model_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,33 @@ type ModelConfig struct {

// override global setting
SendLoadingState *bool `yaml:"sendLoadingState"`

// Variants: see issue #549
// Template-based configuration that generates multiple model variants
// Each variant key becomes a suffix to the model name (e.g., "model-variant")
// Variant values can override cmd arguments
Variants map[string]VariantConfig `yaml:"variants"`
}

// VariantConfig holds the configuration overrides for a model variant
type VariantConfig struct {
// CmdAdd contains additional command line arguments to append or override
CmdAdd string `yaml:"cmdAdd"`

// Name override for this variant
Name string `yaml:"name"`

// Description override for this variant
Description string `yaml:"description"`

// Env additional environment variables for this variant
Env []string `yaml:"env"`

// Aliases additional aliases for this variant
Aliases []string `yaml:"aliases"`

// Unlisted override for this variant
Unlisted *bool `yaml:"unlisted"`
}

func (m *ModelConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
Expand Down
Loading
Loading