Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: ListModels Filtering Upgrade #2773

Merged
merged 67 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
d170bb7
seperate the filtering from the middleware changes
dave-gray101 Jul 11, 2024
9012344
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Jul 11, 2024
d5d6837
merge with 2772, since ListModels now has an easy way to include loos…
dave-gray101 Jul 11, 2024
688cddc
fix backwards bool, add documentation to explain why
dave-gray101 Jul 11, 2024
d9443cf
fix another backwards bool
dave-gray101 Jul 11, 2024
41c1ecc
fix another backwards bool
dave-gray101 Jul 11, 2024
0ced357
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Jul 12, 2024
d2699d2
clean
dave-gray101 Jul 12, 2024
380282d
pay attention while coding
dave-gray101 Jul 12, 2024
865aad4
pay attention while coding
dave-gray101 Jul 12, 2024
fd92e81
fix
dave-gray101 Jul 12, 2024
7115941
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Jul 12, 2024
bbd56bd
fix
dave-gray101 Jul 12, 2024
aace492
Merge branch 'gw-list-model-filter-upgrade' of ghgray101:/dave-gray10…
dave-gray101 Jul 12, 2024
ffe1006
bad merge
dave-gray101 Jul 12, 2024
cffb148
fix
dave-gray101 Jul 12, 2024
845bb83
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Jul 12, 2024
46b651b
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Jul 13, 2024
ca462c8
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Jul 15, 2024
39ab1fd
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Jul 28, 2024
474a417
fix pointer change
dave-gray101 Jul 28, 2024
7d731f5
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Jul 29, 2024
794c359
fix merge error in a test
dave-gray101 Jul 29, 2024
b23f0c0
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Jul 30, 2024
1d279ff
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Jul 30, 2024
c6ca55f
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Aug 1, 2024
5c1b603
ALWAYS_INCLUDE ==> SKIP_IF_CONFIGURED see #3107 and #3011
dave-gray101 Aug 1, 2024
f3e9759
automerge
dave-gray101 Aug 2, 2024
579ee54
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Aug 6, 2024
a56dda7
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Aug 9, 2024
6182737
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Aug 9, 2024
a9e97ae
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Aug 13, 2024
569f03b
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Aug 14, 2024
6357b41
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Aug 15, 2024
a0f7888
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Aug 19, 2024
f48f62f
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Aug 20, 2024
0ad9b72
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Aug 21, 2024
bec39f2
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Aug 24, 2024
a16790a
update for SoundGeneration
dave-gray101 Aug 24, 2024
7cb71c7
missed file
dave-gray101 Aug 24, 2024
f2c4fb4
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Aug 25, 2024
66f7826
re-add fix lost in DCO force push
dave-gray101 Aug 25, 2024
6c8c00f
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Aug 31, 2024
5ad8943
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Sep 6, 2024
9cb2d0c
add cli command
dave-gray101 Sep 6, 2024
2e8c010
manual merge
dave-gray101 Sep 17, 2024
5c22b95
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Sep 23, 2024
8aa637f
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Sep 24, 2024
595bc8f
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Sep 26, 2024
67d6bf2
experimental workflow fix to unbreak
dave-gray101 Sep 26, 2024
d68dcca
revert
dave-gray101 Sep 26, 2024
320ab30
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Sep 26, 2024
f9dd9d2
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Sep 27, 2024
1b50daa
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Sep 27, 2024
0e77ac2
allow manually adding usecases
dave-gray101 Sep 27, 2024
898b6be
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Sep 27, 2024
258c9bd
fix
dave-gray101 Sep 27, 2024
204d6a1
fix
dave-gray101 Sep 28, 2024
0b43fad
fix
dave-gray101 Sep 28, 2024
64d2f63
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Sep 28, 2024
f68d54e
test change
dave-gray101 Sep 29, 2024
e67fcce
Merge branch 'gw-list-model-filter-upgrade' of ghgray101:/dave-gray10…
dave-gray101 Sep 29, 2024
7589128
test fix
dave-gray101 Sep 29, 2024
45c809c
oops
dave-gray101 Sep 29, 2024
d4224e3
oops
dave-gray101 Sep 29, 2024
d750dc6
Merge branch 'master' into gw-list-model-filter-upgrade
mudler Oct 1, 2024
68a1006
Merge branch 'master' into gw-list-model-filter-upgrade
dave-gray101 Oct 1, 2024
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
38 changes: 36 additions & 2 deletions core/cli/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ import (
)

type UtilCMD struct {
GGUFInfo GGUFInfoCMD `cmd:"" name:"gguf-info" help:"Get information about a GGUF file"`
HFScan HFScanCMD `cmd:"" name:"hf-scan" help:"Checks installed models for known security issues. WARNING: this is a best-effort feature and may not catch everything!"`
GGUFInfo GGUFInfoCMD `cmd:"" name:"gguf-info" help:"Get information about a GGUF file"`
HFScan HFScanCMD `cmd:"" name:"hf-scan" help:"Checks installed models for known security issues. WARNING: this is a best-effort feature and may not catch everything!"`
UsecaseHeuristic UsecaseHeuristicCMD `cmd:"" name:"usecase-heuristic" help:"Checks a specific model config and prints what usecase LocalAI will offer for it."`
}

type GGUFInfoCMD struct {
Expand All @@ -30,6 +31,11 @@ type HFScanCMD struct {
ToScan []string `arg:""`
}

type UsecaseHeuristicCMD struct {
ConfigName string `name:"The config file to check"`
ModelsPath string `env:"LOCALAI_MODELS_PATH,MODELS_PATH" type:"path" default:"${basepath}/models" help:"Path containing models used for inferencing" group:"storage"`
}

func (u *GGUFInfoCMD) Run(ctx *cliContext.Context) error {
if u.Args == nil || len(u.Args) == 0 {
return fmt.Errorf("no GGUF file provided")
Expand Down Expand Up @@ -99,3 +105,31 @@ func (hfscmd *HFScanCMD) Run(ctx *cliContext.Context) error {
return nil
}
}

func (uhcmd *UsecaseHeuristicCMD) Run(ctx *cliContext.Context) error {
if len(uhcmd.ConfigName) == 0 {
log.Error().Msg("ConfigName is a required parameter")
return fmt.Errorf("config name is a required parameter")
}
if len(uhcmd.ModelsPath) == 0 {
log.Error().Msg("ModelsPath is a required parameter")
return fmt.Errorf("model path is a required parameter")
}
bcl := config.NewBackendConfigLoader(uhcmd.ModelsPath)
err := bcl.LoadBackendConfig(uhcmd.ConfigName)
if err != nil {
log.Error().Err(err).Str("ConfigName", uhcmd.ConfigName).Msg("error while loading backend")
return err
}
bc, exists := bcl.GetBackendConfig(uhcmd.ConfigName)
if !exists {
log.Error().Str("ConfigName", uhcmd.ConfigName).Msg("ConfigName not found")
}
for name, uc := range config.GetAllBackendConfigUsecases() {
if bc.HasUsecases(uc) {
log.Info().Str("Usecase", name)
}
}
log.Info().Msg("---")
return nil
}
147 changes: 140 additions & 7 deletions core/config/backend_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package config
import (
"os"
"regexp"
"slices"
"strings"

"github.com/mudler/LocalAI/core/schema"
"github.com/mudler/LocalAI/pkg/downloader"
"github.com/mudler/LocalAI/pkg/functions"
"gopkg.in/yaml.v3"
)

const (
Expand All @@ -27,13 +29,15 @@ type BackendConfig struct {
schema.PredictionOptions `yaml:"parameters"`
Name string `yaml:"name"`

F16 *bool `yaml:"f16"`
Threads *int `yaml:"threads"`
Debug *bool `yaml:"debug"`
Roles map[string]string `yaml:"roles"`
Embeddings *bool `yaml:"embeddings"`
Backend string `yaml:"backend"`
TemplateConfig TemplateConfig `yaml:"template"`
F16 *bool `yaml:"f16"`
Threads *int `yaml:"threads"`
Debug *bool `yaml:"debug"`
Roles map[string]string `yaml:"roles"`
Embeddings *bool `yaml:"embeddings"`
Backend string `yaml:"backend"`
TemplateConfig TemplateConfig `yaml:"template"`
KnownUsecaseStrings []string `yaml:"known_usecases"`
KnownUsecases *BackendConfigUsecases `yaml:"-"`

PromptStrings, InputStrings []string `yaml:"-"`
InputToken [][]int `yaml:"-"`
Expand Down Expand Up @@ -194,6 +198,17 @@ type TemplateConfig struct {
JoinChatMessagesByCharacter *string `yaml:"join_chat_messages_by_character"`
}

func (c *BackendConfig) UnmarshalYAML(value *yaml.Node) error {
type BCAlias BackendConfig
var aux BCAlias
if err := value.Decode(&aux); err != nil {
return err
}
*c = BackendConfig(aux)
c.KnownUsecases = GetUsecasesFromYAML(c.KnownUsecaseStrings)
return nil
}

func (c *BackendConfig) SetFunctionCallString(s string) {
c.functionCallString = s
}
Expand Down Expand Up @@ -410,3 +425,121 @@ func (c *BackendConfig) Validate() bool {
func (c *BackendConfig) HasTemplate() bool {
return c.TemplateConfig.Completion != "" || c.TemplateConfig.Edit != "" || c.TemplateConfig.Chat != "" || c.TemplateConfig.ChatMessage != ""
}

type BackendConfigUsecases int

const (
FLAG_ANY BackendConfigUsecases = 0b000000000
FLAG_CHAT BackendConfigUsecases = 0b000000001
FLAG_COMPLETION BackendConfigUsecases = 0b000000010
FLAG_EDIT BackendConfigUsecases = 0b000000100
FLAG_EMBEDDINGS BackendConfigUsecases = 0b000001000
FLAG_RERANK BackendConfigUsecases = 0b000010000
FLAG_IMAGE BackendConfigUsecases = 0b000100000
FLAG_TRANSCRIPT BackendConfigUsecases = 0b001000000
FLAG_TTS BackendConfigUsecases = 0b010000000
FLAG_SOUND_GENERATION BackendConfigUsecases = 0b100000000

// Common Subsets
FLAG_LLM BackendConfigUsecases = FLAG_CHAT & FLAG_COMPLETION & FLAG_EDIT
)

func GetAllBackendConfigUsecases() map[string]BackendConfigUsecases {
return map[string]BackendConfigUsecases{
"FLAG_ANY": FLAG_ANY,
"FLAG_CHAT": FLAG_CHAT,
"FLAG_COMPLETION": FLAG_COMPLETION,
"FLAG_EDIT": FLAG_EDIT,
"FLAG_EMBEDDINGS": FLAG_EMBEDDINGS,
"FLAG_RERANK": FLAG_RERANK,
"FLAG_IMAGE": FLAG_IMAGE,
"FLAG_TRANSCRIPT": FLAG_TRANSCRIPT,
"FLAG_TTS": FLAG_TTS,
"FLAG_SOUND_GENERATION": FLAG_SOUND_GENERATION,
"FLAG_LLM": FLAG_LLM,
}
}

func GetUsecasesFromYAML(input []string) *BackendConfigUsecases {
if len(input) == 0 {
return nil
}
result := FLAG_ANY
flags := GetAllBackendConfigUsecases()
for _, str := range input {
flag, exists := flags["FLAG_"+strings.ToUpper(str)]
if exists {
result |= flag
}
}
return &result
}

// HasUsecases examines a BackendConfig and determines which endpoints have a chance of success.
func (c *BackendConfig) HasUsecases(u BackendConfigUsecases) bool {
if (c.KnownUsecases != nil) && ((u & *c.KnownUsecases) == u) {
return true
}
return c.GuessUsecases(u)
}

// GuessUsecases is a **heuristic based** function, as the backend in question may not be loaded yet, and the config may not record what it's useful at.
// In its current state, this function should ideally check for properties of the config like templates, rather than the direct backend name checks for the lower half.
// This avoids the maintenance burden of updating this list for each new backend - but unfortunately, that's the best option for some services currently.
func (c *BackendConfig) GuessUsecases(u BackendConfigUsecases) bool {
if (u & FLAG_CHAT) == FLAG_CHAT {
if c.TemplateConfig.Chat == "" && c.TemplateConfig.ChatMessage == "" {
return false
}
}
if (u & FLAG_COMPLETION) == FLAG_COMPLETION {
if c.TemplateConfig.Completion == "" {
return false
}
}
if (u & FLAG_EDIT) == FLAG_EDIT {
if c.TemplateConfig.Edit == "" {
return false
}
}
if (u & FLAG_EMBEDDINGS) == FLAG_EMBEDDINGS {
if c.Embeddings == nil || !*c.Embeddings {
return false
}
}
if (u & FLAG_IMAGE) == FLAG_IMAGE {
imageBackends := []string{"diffusers", "tinydream", "stablediffusion"}
if !slices.Contains(imageBackends, c.Backend) {
return false
}

if c.Backend == "diffusers" && c.Diffusers.PipelineType == "" {
return false
}

}
if (u & FLAG_RERANK) == FLAG_RERANK {
if c.Backend != "rerankers" {
return false
}
}
if (u & FLAG_TRANSCRIPT) == FLAG_TRANSCRIPT {
if c.Backend != "whisper" {
return false
}
}
if (u & FLAG_TTS) == FLAG_TTS {
ttsBackends := []string{"piper", "transformers-musicgen", "parler-tts"}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another optimization down the line is to extract the backend list here at the top of the file in a map or either a list for easing out maintenance

Copy link
Collaborator Author

@dave-gray101 dave-gray101 Oct 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that idea. I plan to do some more refactoring around the services, and I think the best place to locate that list will be over there. Thanks!

ttsService.GetKnownBackends() or something similar - not ruling out putting it above here, just seems a bit strange to maintain an ongoing list of backends in the config subdir :)

if !slices.Contains(ttsBackends, c.Backend) {
return false
}
}

if (u & FLAG_SOUND_GENERATION) == FLAG_SOUND_GENERATION {
if c.Backend != "transformers-musicgen" {
return false
}
}

return true
}
35 changes: 35 additions & 0 deletions core/config/backend_config_filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package config

import "regexp"

type BackendConfigFilterFn func(string, *BackendConfig) bool

func NoFilterFn(_ string, _ *BackendConfig) bool { return true }

func BuildNameFilterFn(filter string) (BackendConfigFilterFn, error) {
if filter == "" {
return NoFilterFn, nil
}
rxp, err := regexp.Compile(filter)
if err != nil {
return nil, err
}
return func(name string, config *BackendConfig) bool {
if config != nil {
return rxp.MatchString(config.Name)
}
return rxp.MatchString(name)
}, nil
}

func BuildUsecaseFilterFn(usecases BackendConfigUsecases) BackendConfigFilterFn {
if usecases == FLAG_ANY {
return NoFilterFn
}
return func(name string, config *BackendConfig) bool {
if config == nil {
return false // TODO: Potentially make this a param, for now, no known usecase to include
}
return config.HasUsecases(usecases)
}
}
20 changes: 20 additions & 0 deletions core/config/backend_config_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,26 @@ func (bcl *BackendConfigLoader) GetAllBackendConfigs() []BackendConfig {
return res
}

func (bcl *BackendConfigLoader) GetBackendConfigsByFilter(filter BackendConfigFilterFn) []BackendConfig {
bcl.Lock()
defer bcl.Unlock()
var res []BackendConfig

if filter == nil {
filter = NoFilterFn
}

for n, v := range bcl.configs {
if filter(n, &v) {
res = append(res, v)
}
}

// TODO: I don't think this one needs to Sort on name... but we'll see what breaks.

return res
}

func (bcl *BackendConfigLoader) RemoveBackendConfig(m string) {
bcl.Lock()
defer bcl.Unlock()
Expand Down
Loading