2024-02-21 01:21:19 +00:00
package config
2023-07-14 23:19:43 +00:00
import (
2024-01-05 17:04:46 +00:00
"errors"
2023-07-14 23:19:43 +00:00
"fmt"
2024-01-05 17:04:46 +00:00
"io/fs"
2024-03-13 09:05:30 +00:00
"math/rand"
2023-07-14 23:19:43 +00:00
"os"
2024-01-05 17:04:46 +00:00
"path/filepath"
"strings"
"sync"
2023-07-14 23:19:43 +00:00
2024-03-01 15:19:53 +00:00
"github.com/go-skynet/LocalAI/core/schema"
2024-01-05 22:16:33 +00:00
"github.com/go-skynet/LocalAI/pkg/downloader"
2023-12-18 17:58:44 +00:00
"github.com/go-skynet/LocalAI/pkg/utils"
2024-01-05 17:04:46 +00:00
"github.com/rs/zerolog/log"
2023-07-14 23:19:43 +00:00
"gopkg.in/yaml.v3"
2024-03-13 20:50:46 +00:00
"github.com/charmbracelet/glamour"
2023-07-14 23:19:43 +00:00
)
2024-03-01 15:19:53 +00:00
type BackendConfig struct {
schema . PredictionOptions ` yaml:"parameters" `
Name string ` yaml:"name" `
2023-08-09 06:38:51 +00:00
2024-03-13 09:05:30 +00:00
F16 * bool ` yaml:"f16" `
Threads * int ` yaml:"threads" `
Debug * bool ` yaml:"debug" `
2023-08-09 06:38:51 +00:00
Roles map [ string ] string ` yaml:"roles" `
Embeddings bool ` yaml:"embeddings" `
Backend string ` yaml:"backend" `
TemplateConfig TemplateConfig ` yaml:"template" `
PromptStrings , InputStrings [ ] string ` yaml:"-" `
InputToken [ ] [ ] int ` yaml:"-" `
functionCallString , functionCallNameString string ` yaml:"-" `
2023-07-14 23:19:43 +00:00
FunctionsConfig Functions ` yaml:"function" `
2023-07-22 15:31:39 +00:00
2023-08-19 14:15:22 +00:00
FeatureFlag FeatureFlag ` yaml:"feature_flags" ` // Feature Flag registry. We move fast, and features may break on a per model/backend basis. Registry for (usually temporary) flags that indicate aborting something early.
2023-08-09 06:38:51 +00:00
// LLM configs (GPT4ALL, Llama.cpp, ...)
LLMConfig ` yaml:",inline" `
// AutoGPTQ specifics
AutoGPTQ AutoGPTQ ` yaml:"autogptq" `
2023-08-02 22:51:08 +00:00
2023-08-09 06:38:51 +00:00
// Diffusers
Diffusers Diffusers ` yaml:"diffusers" `
2023-12-13 18:20:22 +00:00
Step int ` yaml:"step" `
2023-08-15 23:11:32 +00:00
// GRPC Options
GRPC GRPC ` yaml:"grpc" `
2023-09-04 17:25:23 +00:00
// Vall-e-x
VallE VallE ` yaml:"vall-e" `
2023-12-08 14:45:04 +00:00
// CUDA
// Explicitly enable CUDA or not (some backends might need it)
CUDA bool ` yaml:"cuda" `
2024-01-01 13:39:13 +00:00
DownloadFiles [ ] File ` yaml:"download_files" `
2024-01-07 23:37:02 +00:00
Description string ` yaml:"description" `
Usage string ` yaml:"usage" `
2024-01-01 13:39:13 +00:00
}
type File struct {
Filename string ` yaml:"filename" json:"filename" `
SHA256 string ` yaml:"sha256" json:"sha256" `
URI string ` yaml:"uri" json:"uri" `
2023-09-04 17:25:23 +00:00
}
type VallE struct {
AudioPath string ` yaml:"audio_path" `
2023-08-15 23:11:32 +00:00
}
2023-08-19 14:15:22 +00:00
type FeatureFlag map [ string ] * bool
func ( ff FeatureFlag ) Enabled ( s string ) bool {
v , exist := ff [ s ]
return exist && v != nil && * v
}
2023-08-15 23:11:32 +00:00
type GRPC struct {
Attempts int ` yaml:"attempts" `
AttemptsSleepTime int ` yaml:"attempts_sleep_time" `
2023-08-09 06:38:51 +00:00
}
type Diffusers struct {
2023-12-15 23:06:20 +00:00
CUDA bool ` yaml:"cuda" `
2023-08-15 23:11:42 +00:00
PipelineType string ` yaml:"pipeline_type" `
SchedulerType string ` yaml:"scheduler_type" `
EnableParameters string ` yaml:"enable_parameters" ` // A list of comma separated parameters to specify
CFGScale float32 ` yaml:"cfg_scale" ` // Classifier-Free Guidance Scale
2023-08-17 21:38:59 +00:00
IMG2IMG bool ` yaml:"img2img" ` // Image to Image Diffuser
ClipSkip int ` yaml:"clip_skip" ` // Skip every N frames
ClipModel string ` yaml:"clip_model" ` // Clip model to use
ClipSubFolder string ` yaml:"clip_subfolder" ` // Subfolder to use for clip model
2023-12-13 18:20:22 +00:00
ControlNet string ` yaml:"control_net" `
2023-08-09 06:38:51 +00:00
}
type LLMConfig struct {
SystemPrompt string ` yaml:"system_prompt" `
TensorSplit string ` yaml:"tensor_split" `
MainGPU string ` yaml:"main_gpu" `
RMSNormEps float32 ` yaml:"rms_norm_eps" `
NGQA int32 ` yaml:"ngqa" `
PromptCachePath string ` yaml:"prompt_cache_path" `
PromptCacheAll bool ` yaml:"prompt_cache_all" `
PromptCacheRO bool ` yaml:"prompt_cache_ro" `
2024-03-13 09:05:30 +00:00
MirostatETA * float64 ` yaml:"mirostat_eta" `
MirostatTAU * float64 ` yaml:"mirostat_tau" `
Mirostat * int ` yaml:"mirostat" `
NGPULayers * int ` yaml:"gpu_layers" `
MMap * bool ` yaml:"mmap" `
MMlock * bool ` yaml:"mmlock" `
LowVRAM * bool ` yaml:"low_vram" `
2023-08-09 06:38:51 +00:00
Grammar string ` yaml:"grammar" `
StopWords [ ] string ` yaml:"stopwords" `
Cutstrings [ ] string ` yaml:"cutstrings" `
TrimSpace [ ] string ` yaml:"trimspace" `
2024-01-01 13:39:42 +00:00
TrimSuffix [ ] string ` yaml:"trimsuffix" `
2024-03-13 09:05:30 +00:00
ContextSize * int ` yaml:"context_size" `
2024-03-01 21:48:53 +00:00
NUMA bool ` yaml:"numa" `
LoraAdapter string ` yaml:"lora_adapter" `
LoraBase string ` yaml:"lora_base" `
LoraScale float32 ` yaml:"lora_scale" `
NoMulMatQ bool ` yaml:"no_mulmatq" `
DraftModel string ` yaml:"draft_model" `
NDraft int32 ` yaml:"n_draft" `
Quantization string ` yaml:"quantization" `
GPUMemoryUtilization float32 ` yaml:"gpu_memory_utilization" ` // vLLM
TrustRemoteCode bool ` yaml:"trust_remote_code" ` // vLLM
EnforceEager bool ` yaml:"enforce_eager" ` // vLLM
SwapSpace int ` yaml:"swap_space" ` // vLLM
MaxModelLen int ` yaml:"max_model_len" ` // vLLM
MMProj string ` yaml:"mmproj" `
2023-11-11 17:40:48 +00:00
2024-01-25 23:13:21 +00:00
RopeScaling string ` yaml:"rope_scaling" `
ModelType string ` yaml:"type" `
2023-11-11 17:40:48 +00:00
YarnExtFactor float32 ` yaml:"yarn_ext_factor" `
YarnAttnFactor float32 ` yaml:"yarn_attn_factor" `
YarnBetaFast float32 ` yaml:"yarn_beta_fast" `
YarnBetaSlow float32 ` yaml:"yarn_beta_slow" `
2023-08-09 06:38:51 +00:00
}
2023-08-07 20:39:10 +00:00
2023-08-09 06:38:51 +00:00
type AutoGPTQ struct {
2023-08-07 23:10:05 +00:00
ModelBaseName string ` yaml:"model_base_name" `
Device string ` yaml:"device" `
Triton bool ` yaml:"triton" `
UseFastTokenizer bool ` yaml:"use_fast_tokenizer" `
2023-07-14 23:19:43 +00:00
}
type Functions struct {
DisableNoAction bool ` yaml:"disable_no_action" `
NoActionFunctionName string ` yaml:"no_action_function_name" `
NoActionDescriptionName string ` yaml:"no_action_description_name" `
2024-02-20 20:58:45 +00:00
ParallelCalls bool ` yaml:"parallel_calls" `
2023-07-14 23:19:43 +00:00
}
type TemplateConfig struct {
2023-07-22 15:31:39 +00:00
Chat string ` yaml:"chat" `
ChatMessage string ` yaml:"chat_message" `
Completion string ` yaml:"completion" `
Edit string ` yaml:"edit" `
Functions string ` yaml:"function" `
2023-07-14 23:19:43 +00:00
}
2024-03-01 15:19:53 +00:00
func ( c * BackendConfig ) SetFunctionCallString ( s string ) {
2023-07-14 23:19:43 +00:00
c . functionCallString = s
}
2024-03-01 15:19:53 +00:00
func ( c * BackendConfig ) SetFunctionCallNameString ( s string ) {
2023-07-14 23:19:43 +00:00
c . functionCallNameString = s
}
2024-03-01 15:19:53 +00:00
func ( c * BackendConfig ) ShouldUseFunctions ( ) bool {
2023-07-14 23:19:43 +00:00
return ( ( c . functionCallString != "none" || c . functionCallString == "" ) || c . ShouldCallSpecificFunction ( ) )
}
2024-03-01 15:19:53 +00:00
func ( c * BackendConfig ) ShouldCallSpecificFunction ( ) bool {
2023-07-14 23:19:43 +00:00
return len ( c . functionCallNameString ) > 0
}
2024-03-01 15:19:53 +00:00
func ( c * BackendConfig ) FunctionToCall ( ) string {
2023-07-14 23:19:43 +00:00
return c . functionCallNameString
}
2024-03-13 09:05:30 +00:00
func ( cfg * BackendConfig ) SetDefaults ( debug bool , threads , ctx int , f16 bool ) {
defaultTopP := 0.7
defaultTopK := 80
defaultTemp := 0.9
defaultMaxTokens := 2048
defaultMirostat := 2
defaultMirostatTAU := 5.0
defaultMirostatETA := 0.1
// Try to offload all GPU layers (if GPU is found)
defaultNGPULayers := 99999999
trueV := true
falseV := false
if cfg . Seed == nil {
// random number generator seed
defaultSeed := int ( rand . Int31 ( ) )
cfg . Seed = & defaultSeed
}
if cfg . TopK == nil {
cfg . TopK = & defaultTopK
}
if cfg . MMap == nil {
// MMap is enabled by default
cfg . MMap = & trueV
2024-03-01 15:19:53 +00:00
}
2024-03-13 09:05:30 +00:00
if cfg . MMlock == nil {
// MMlock is disabled by default
cfg . MMlock = & falseV
}
if cfg . TopP == nil {
cfg . TopP = & defaultTopP
}
if cfg . Temperature == nil {
cfg . Temperature = & defaultTemp
}
if cfg . Maxtokens == nil {
cfg . Maxtokens = & defaultMaxTokens
}
if cfg . Mirostat == nil {
cfg . Mirostat = & defaultMirostat
}
if cfg . MirostatETA == nil {
cfg . MirostatETA = & defaultMirostatETA
}
if cfg . MirostatTAU == nil {
cfg . MirostatTAU = & defaultMirostatTAU
}
if cfg . NGPULayers == nil {
cfg . NGPULayers = & defaultNGPULayers
}
if cfg . LowVRAM == nil {
cfg . LowVRAM = & falseV
}
// Value passed by the top level are treated as default (no implicit defaults)
// defaults are set by the user
if ctx == 0 {
ctx = 1024
}
if cfg . ContextSize == nil {
cfg . ContextSize = & ctx
}
if threads == 0 {
// Threads can't be 0
threads = 4
}
if cfg . Threads == nil {
cfg . Threads = & threads
}
if cfg . F16 == nil {
cfg . F16 = & f16
}
if debug {
cfg . Debug = & debug
2024-03-01 15:19:53 +00:00
}
}
////// Config Loader ////////
type BackendConfigLoader struct {
configs map [ string ] BackendConfig
sync . Mutex
}
2024-03-13 09:05:30 +00:00
type LoadOptions struct {
debug bool
threads , ctxSize int
f16 bool
}
func LoadOptionDebug ( debug bool ) ConfigLoaderOption {
return func ( o * LoadOptions ) {
o . debug = debug
}
}
func LoadOptionThreads ( threads int ) ConfigLoaderOption {
return func ( o * LoadOptions ) {
o . threads = threads
}
}
func LoadOptionContextSize ( ctxSize int ) ConfigLoaderOption {
return func ( o * LoadOptions ) {
o . ctxSize = ctxSize
}
}
func LoadOptionF16 ( f16 bool ) ConfigLoaderOption {
return func ( o * LoadOptions ) {
o . f16 = f16
}
}
type ConfigLoaderOption func ( * LoadOptions )
func ( lo * LoadOptions ) Apply ( options ... ConfigLoaderOption ) {
for _ , l := range options {
l ( lo )
}
}
2024-02-10 20:37:03 +00:00
// Load a config file for a model
2024-03-13 09:05:30 +00:00
func ( cl * BackendConfigLoader ) LoadBackendConfigFileByName ( modelName , modelPath string , opts ... ConfigLoaderOption ) ( * BackendConfig , error ) {
2024-02-10 20:37:03 +00:00
2024-03-13 09:05:30 +00:00
lo := & LoadOptions { }
lo . Apply ( opts ... )
2024-02-10 20:37:03 +00:00
2024-03-13 09:05:30 +00:00
// Load a config file if present after the model name
cfg := & BackendConfig {
PredictionOptions : schema . PredictionOptions {
Model : modelName ,
} ,
2024-02-10 20:37:03 +00:00
}
2024-03-01 15:19:53 +00:00
cfgExisting , exists := cl . GetBackendConfig ( modelName )
2024-03-13 09:05:30 +00:00
if exists {
cfg = & cfgExisting
} else {
// Try loading a model config file
modelConfig := filepath . Join ( modelPath , modelName + ".yaml" )
2024-02-10 20:37:03 +00:00
if _ , err := os . Stat ( modelConfig ) ; err == nil {
2024-03-01 15:19:53 +00:00
if err := cl . LoadBackendConfig ( modelConfig ) ; err != nil {
2024-02-10 20:37:03 +00:00
return nil , fmt . Errorf ( "failed loading model config (%s) %s" , modelConfig , err . Error ( ) )
}
2024-03-01 15:19:53 +00:00
cfgExisting , exists = cl . GetBackendConfig ( modelName )
2024-02-10 20:37:03 +00:00
if exists {
cfg = & cfgExisting
}
}
}
2024-03-13 09:05:30 +00:00
cfg . SetDefaults ( lo . debug , lo . threads , lo . ctxSize , lo . f16 )
2024-02-10 20:37:03 +00:00
return cfg , nil
}
2024-03-01 15:19:53 +00:00
func NewBackendConfigLoader ( ) * BackendConfigLoader {
return & BackendConfigLoader {
configs : make ( map [ string ] BackendConfig ) ,
2024-01-05 17:04:46 +00:00
}
}
2024-03-13 09:05:30 +00:00
func ReadBackendConfigFile ( file string , opts ... ConfigLoaderOption ) ( [ ] * BackendConfig , error ) {
lo := & LoadOptions { }
lo . Apply ( opts ... )
2024-03-01 15:19:53 +00:00
c := & [ ] * BackendConfig { }
2023-07-14 23:19:43 +00:00
f , err := os . ReadFile ( file )
if err != nil {
return nil , fmt . Errorf ( "cannot read config file: %w" , err )
}
if err := yaml . Unmarshal ( f , c ) ; err != nil {
return nil , fmt . Errorf ( "cannot unmarshal config file: %w" , err )
}
2024-03-13 09:05:30 +00:00
for _ , cc := range * c {
cc . SetDefaults ( lo . debug , lo . threads , lo . ctxSize , lo . f16 )
}
2023-07-14 23:19:43 +00:00
return * c , nil
}
2024-03-13 09:05:30 +00:00
func ReadBackendConfig ( file string , opts ... ConfigLoaderOption ) ( * BackendConfig , error ) {
lo := & LoadOptions { }
lo . Apply ( opts ... )
2024-03-01 15:19:53 +00:00
c := & BackendConfig { }
2023-07-14 23:19:43 +00:00
f , err := os . ReadFile ( file )
if err != nil {
return nil , fmt . Errorf ( "cannot read config file: %w" , err )
}
if err := yaml . Unmarshal ( f , c ) ; err != nil {
return nil , fmt . Errorf ( "cannot unmarshal config file: %w" , err )
}
2024-03-13 09:05:30 +00:00
c . SetDefaults ( lo . debug , lo . threads , lo . ctxSize , lo . f16 )
2023-07-14 23:19:43 +00:00
return c , nil
}
2024-03-13 09:05:30 +00:00
func ( cm * BackendConfigLoader ) LoadBackendConfigFile ( file string , opts ... ConfigLoaderOption ) error {
2024-01-05 17:04:46 +00:00
cm . Lock ( )
defer cm . Unlock ( )
2024-03-13 09:05:30 +00:00
c , err := ReadBackendConfigFile ( file , opts ... )
2024-01-05 17:04:46 +00:00
if err != nil {
return fmt . Errorf ( "cannot load config file: %w" , err )
2024-01-05 14:34:56 +00:00
}
2023-12-30 14:36:46 +00:00
2024-01-05 17:04:46 +00:00
for _ , cc := range c {
cm . configs [ cc . Name ] = * cc
2024-01-05 14:34:56 +00:00
}
2024-01-05 17:04:46 +00:00
return nil
}
2023-12-30 14:36:46 +00:00
2024-03-13 09:05:30 +00:00
func ( cl * BackendConfigLoader ) LoadBackendConfig ( file string , opts ... ConfigLoaderOption ) error {
2024-03-01 15:19:53 +00:00
cl . Lock ( )
defer cl . Unlock ( )
2024-03-13 09:05:30 +00:00
c , err := ReadBackendConfig ( file , opts ... )
2024-01-05 17:04:46 +00:00
if err != nil {
return fmt . Errorf ( "cannot read config file: %w" , err )
2024-01-05 14:34:56 +00:00
}
2024-01-01 13:39:13 +00:00
2024-03-01 15:19:53 +00:00
cl . configs [ c . Name ] = * c
2024-01-05 17:04:46 +00:00
return nil
}
2024-01-01 13:39:13 +00:00
2024-03-01 15:19:53 +00:00
func ( cl * BackendConfigLoader ) GetBackendConfig ( m string ) ( BackendConfig , bool ) {
cl . Lock ( )
defer cl . Unlock ( )
v , exists := cl . configs [ m ]
2024-01-05 17:04:46 +00:00
return v , exists
}
2024-01-05 14:34:56 +00:00
2024-03-01 15:19:53 +00:00
func ( cl * BackendConfigLoader ) GetAllBackendConfigs ( ) [ ] BackendConfig {
cl . Lock ( )
defer cl . Unlock ( )
var res [ ] BackendConfig
for _ , v := range cl . configs {
2024-01-05 17:04:46 +00:00
res = append ( res , v )
2024-01-05 14:34:56 +00:00
}
2024-01-05 17:04:46 +00:00
return res
}
2024-01-05 14:34:56 +00:00
2024-03-01 15:19:53 +00:00
func ( cl * BackendConfigLoader ) ListBackendConfigs ( ) [ ] string {
cl . Lock ( )
defer cl . Unlock ( )
2024-01-05 17:04:46 +00:00
var res [ ] string
2024-03-01 15:19:53 +00:00
for k := range cl . configs {
2024-01-05 17:04:46 +00:00
res = append ( res , k )
2024-01-05 14:34:56 +00:00
}
2024-01-05 17:04:46 +00:00
return res
}
2024-01-05 14:34:56 +00:00
2024-01-05 17:04:46 +00:00
// Preload prepare models if they are not local but url or huggingface repositories
2024-03-01 15:19:53 +00:00
func ( cl * BackendConfigLoader ) Preload ( modelPath string ) error {
cl . Lock ( )
defer cl . Unlock ( )
2024-01-05 14:34:56 +00:00
2024-01-05 17:04:46 +00:00
status := func ( fileName , current , total string , percent float64 ) {
utils . DisplayDownloadFunction ( fileName , current , total , percent )
2024-01-05 14:34:56 +00:00
}
2024-01-05 17:04:46 +00:00
log . Info ( ) . Msgf ( "Preloading models from %s" , modelPath )
2024-01-05 14:34:56 +00:00
2024-03-13 20:50:46 +00:00
renderMode := "dark"
if os . Getenv ( "COLOR" ) != "" {
renderMode = os . Getenv ( "COLOR" )
}
glamText := func ( t string ) {
out , err := glamour . Render ( t , renderMode )
if err == nil && os . Getenv ( "NO_COLOR" ) == "" {
fmt . Println ( out )
} else {
fmt . Println ( t )
}
}
2024-03-01 15:19:53 +00:00
for i , config := range cl . configs {
2024-01-05 14:34:56 +00:00
2024-01-05 17:04:46 +00:00
// Download files and verify their SHA
for _ , file := range config . DownloadFiles {
log . Debug ( ) . Msgf ( "Checking %q exists and matches SHA" , file . Filename )
2024-01-05 14:34:56 +00:00
2024-01-05 17:04:46 +00:00
if err := utils . VerifyPath ( file . Filename , modelPath ) ; err != nil {
return err
}
// Create file path
filePath := filepath . Join ( modelPath , file . Filename )
2024-01-05 14:34:56 +00:00
2024-01-05 22:16:33 +00:00
if err := downloader . DownloadFile ( file . URI , filePath , file . SHA256 , status ) ; err != nil {
2024-01-05 17:04:46 +00:00
return err
2024-01-01 13:39:13 +00:00
}
}
2024-01-05 17:04:46 +00:00
modelURL := config . PredictionOptions . Model
2024-01-05 22:16:33 +00:00
modelURL = downloader . ConvertURL ( modelURL )
2023-12-18 17:58:44 +00:00
2024-01-05 22:16:33 +00:00
if downloader . LooksLikeURL ( modelURL ) {
2024-01-05 17:04:46 +00:00
// md5 of model name
md5Name := utils . MD5 ( modelURL )
2023-12-18 17:58:44 +00:00
2024-01-05 17:04:46 +00:00
// check if file exists
if _ , err := os . Stat ( filepath . Join ( modelPath , md5Name ) ) ; errors . Is ( err , os . ErrNotExist ) {
2024-01-05 22:16:33 +00:00
err := downloader . DownloadFile ( modelURL , filepath . Join ( modelPath , md5Name ) , "" , status )
2024-01-05 17:04:46 +00:00
if err != nil {
return err
2024-01-05 14:34:56 +00:00
}
}
2024-01-05 17:04:46 +00:00
2024-03-01 15:19:53 +00:00
cc := cl . configs [ i ]
2024-01-05 17:04:46 +00:00
c := & cc
c . PredictionOptions . Model = md5Name
2024-03-01 15:19:53 +00:00
cl . configs [ i ] = * c
2023-07-14 23:19:43 +00:00
}
2024-03-01 15:19:53 +00:00
if cl . configs [ i ] . Name != "" {
2024-03-13 20:50:46 +00:00
glamText ( fmt . Sprintf ( "**Model name**: _%s_" , cl . configs [ i ] . Name ) )
2024-01-07 23:37:02 +00:00
}
2024-03-01 15:19:53 +00:00
if cl . configs [ i ] . Description != "" {
2024-03-13 20:50:46 +00:00
//glamText("**Description**")
glamText ( cl . configs [ i ] . Description )
2024-01-07 23:37:02 +00:00
}
2024-03-01 15:19:53 +00:00
if cl . configs [ i ] . Usage != "" {
2024-03-13 20:50:46 +00:00
//glamText("**Usage**")
glamText ( cl . configs [ i ] . Usage )
2024-01-07 23:37:02 +00:00
}
2023-07-14 23:19:43 +00:00
}
2024-01-05 17:04:46 +00:00
return nil
}
2024-01-05 14:34:56 +00:00
2024-03-13 09:05:30 +00:00
// LoadBackendConfigsFromPath reads all the configurations of the models from a path
// (non-recursive)
func ( cm * BackendConfigLoader ) LoadBackendConfigsFromPath ( path string , opts ... ConfigLoaderOption ) error {
2024-01-05 17:04:46 +00:00
cm . Lock ( )
defer cm . Unlock ( )
entries , err := os . ReadDir ( path )
if err != nil {
return err
}
files := make ( [ ] fs . FileInfo , 0 , len ( entries ) )
for _ , entry := range entries {
info , err := entry . Info ( )
if err != nil {
return err
2023-07-14 23:19:43 +00:00
}
2024-01-05 17:04:46 +00:00
files = append ( files , info )
2024-01-05 14:34:56 +00:00
}
2024-01-05 17:04:46 +00:00
for _ , file := range files {
// Skip templates, YAML and .keep files
if ! strings . Contains ( file . Name ( ) , ".yaml" ) && ! strings . Contains ( file . Name ( ) , ".yml" ) {
continue
}
2024-03-13 09:05:30 +00:00
c , err := ReadBackendConfig ( filepath . Join ( path , file . Name ( ) ) , opts ... )
2024-01-05 17:04:46 +00:00
if err == nil {
cm . configs [ c . Name ] = * c
2023-07-14 23:19:43 +00:00
}
}
2024-01-05 17:04:46 +00:00
return nil
2023-07-14 23:19:43 +00:00
}