Changes: - add Metadata key to ModelConfig - include metadata in /v1/models under meta.llamaswap key - add recursive macro substitution into Metadata - change macros at global and model level to be any scalar type Note: This is the first mostly AI generated change to llama-swap. See #333 for notes about the workflow and approach to AI going forward.
126 lines
3.0 KiB
Go
126 lines
3.0 KiB
Go
package config
|
|
|
|
import (
|
|
"errors"
|
|
"runtime"
|
|
"slices"
|
|
"strings"
|
|
)
|
|
|
|
type ModelConfig struct {
|
|
Cmd string `yaml:"cmd"`
|
|
CmdStop string `yaml:"cmdStop"`
|
|
Proxy string `yaml:"proxy"`
|
|
Aliases []string `yaml:"aliases"`
|
|
Env []string `yaml:"env"`
|
|
CheckEndpoint string `yaml:"checkEndpoint"`
|
|
UnloadAfter int `yaml:"ttl"`
|
|
Unlisted bool `yaml:"unlisted"`
|
|
UseModelName string `yaml:"useModelName"`
|
|
|
|
// #179 for /v1/models
|
|
Name string `yaml:"name"`
|
|
Description string `yaml:"description"`
|
|
|
|
// Limit concurrency of HTTP requests to process
|
|
ConcurrencyLimit int `yaml:"concurrencyLimit"`
|
|
|
|
// Model filters see issue #174
|
|
Filters ModelFilters `yaml:"filters"`
|
|
|
|
// Macros: see #264
|
|
// Model level macros take precedence over the global macros
|
|
Macros MacroList `yaml:"macros"`
|
|
|
|
// Metadata: see #264
|
|
// Arbitrary metadata that can be exposed through the API
|
|
Metadata map[string]any `yaml:"metadata"`
|
|
}
|
|
|
|
func (m *ModelConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|
type rawModelConfig ModelConfig
|
|
defaults := rawModelConfig{
|
|
Cmd: "",
|
|
CmdStop: "",
|
|
Proxy: "http://localhost:${PORT}",
|
|
Aliases: []string{},
|
|
Env: []string{},
|
|
CheckEndpoint: "/health",
|
|
UnloadAfter: 0,
|
|
Unlisted: false,
|
|
UseModelName: "",
|
|
ConcurrencyLimit: 0,
|
|
Name: "",
|
|
Description: "",
|
|
}
|
|
|
|
// the default cmdStop to taskkill /f /t /pid ${PID}
|
|
if runtime.GOOS == "windows" {
|
|
defaults.CmdStop = "taskkill /f /t /pid ${PID}"
|
|
}
|
|
|
|
if err := unmarshal(&defaults); err != nil {
|
|
return err
|
|
}
|
|
|
|
*m = ModelConfig(defaults)
|
|
return nil
|
|
}
|
|
|
|
func (m *ModelConfig) SanitizedCommand() ([]string, error) {
|
|
return SanitizeCommand(m.Cmd)
|
|
}
|
|
|
|
// ModelFilters see issue #174
|
|
type ModelFilters struct {
|
|
StripParams string `yaml:"stripParams"`
|
|
}
|
|
|
|
func (m *ModelFilters) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|
type rawModelFilters ModelFilters
|
|
defaults := rawModelFilters{
|
|
StripParams: "",
|
|
}
|
|
|
|
if err := unmarshal(&defaults); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Try to unmarshal with the old field name for backwards compatibility
|
|
if defaults.StripParams == "" {
|
|
var legacy struct {
|
|
StripParams string `yaml:"strip_params"`
|
|
}
|
|
if legacyErr := unmarshal(&legacy); legacyErr != nil {
|
|
return errors.New("failed to unmarshal legacy filters.strip_params: " + legacyErr.Error())
|
|
}
|
|
defaults.StripParams = legacy.StripParams
|
|
}
|
|
|
|
*m = ModelFilters(defaults)
|
|
return nil
|
|
}
|
|
|
|
func (f ModelFilters) SanitizedStripParams() ([]string, error) {
|
|
if f.StripParams == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
params := strings.Split(f.StripParams, ",")
|
|
cleaned := make([]string, 0, len(params))
|
|
seen := make(map[string]bool)
|
|
|
|
for _, param := range params {
|
|
trimmed := strings.TrimSpace(param)
|
|
if trimmed == "model" || trimmed == "" || seen[trimmed] {
|
|
continue
|
|
}
|
|
seen[trimmed] = true
|
|
cleaned = append(cleaned, trimmed)
|
|
}
|
|
|
|
// sort cleaned
|
|
slices.Sort(cleaned)
|
|
return cleaned, nil
|
|
}
|