diff --git a/api/api.go b/api/api.go index 6f2ac14..527258b 100644 --- a/api/api.go +++ b/api/api.go @@ -104,7 +104,9 @@ func App(opts ...AppOption) (*fiber.App, error) { // LocalAI API endpoints applier := newGalleryApplier(options.loader.ModelPath) applier.start(options.context, cm) - app.Post("/models/apply", applyModelGallery(options.loader.ModelPath, cm, applier.C)) + + app.Post("/models/apply", applyModelGallery(options.loader.ModelPath, cm, applier.C, options.galleries)) + app.Get("/models/list", listModelFromGallery(options.galleries, options.loader.ModelPath)) app.Get("/models/jobs/:uuid", getOpStatus(applier)) // openAI compatible API endpoint @@ -120,6 +122,7 @@ func App(opts ...AppOption) (*fiber.App, error) { // completion app.Post("/v1/completions", completionEndpoint(cm, options)) app.Post("/completions", completionEndpoint(cm, options)) + app.Post("/v1/engines/:model/completions", completionEndpoint(cm, options)) // embeddings app.Post("/v1/embeddings", embeddingsEndpoint(cm, options)) diff --git a/api/api_test.go b/api/api_test.go index af2193f..05d5e7b 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -13,6 +13,7 @@ import ( "runtime" . "github.com/go-skynet/LocalAI/api" + "github.com/go-skynet/LocalAI/pkg/gallery" "github.com/go-skynet/LocalAI/pkg/model" "github.com/gofiber/fiber/v2" . "github.com/onsi/ginkgo/v2" @@ -24,6 +25,7 @@ import ( ) type modelApplyRequest struct { + ID string `json:"id"` URL string `json:"url"` Name string `json:"name"` Overrides map[string]string `json:"overrides"` @@ -52,6 +54,35 @@ func getModelStatus(url string) (response map[string]interface{}) { } return } + +func getModels(url string) (response []gallery.GalleryModel) { + + //url := "http://localhost:AI/models/apply" + + // Create the request payload + + // Create the HTTP request + resp, err := http.Get(url) + if err != nil { + return nil + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Println("Error reading response body:", err) + return + } + + // Unmarshal the response into a map[string]interface{} + err = json.Unmarshal(body, &response) + if err != nil { + fmt.Println("Error unmarshaling JSON response:", err) + return + } + return +} + func postModelApplyRequest(url string, request modelApplyRequest) (response map[string]interface{}) { //url := "http://localhost:AI/models/apply" @@ -118,7 +149,33 @@ var _ = Describe("API test", func() { modelLoader = model.NewModelLoader(tmpdir) c, cancel = context.WithCancel(context.Background()) - app, err = App(WithContext(c), WithModelLoader(modelLoader), WithBackendAssets(backendAssets), WithBackendAssetsOutput(tmpdir)) + g := []gallery.GalleryModel{ + { + Name: "bert", + URL: "https://raw.githubusercontent.com/go-skynet/model-gallery/main/bert-embeddings.yaml", + }, + { + Name: "bert2", + URL: "https://raw.githubusercontent.com/go-skynet/model-gallery/main/bert-embeddings.yaml", + Overrides: map[string]interface{}{"foo": "bar"}, + AdditionalFiles: []gallery.File{gallery.File{Filename: "foo.yaml", URI: "https://raw.githubusercontent.com/go-skynet/model-gallery/main/bert-embeddings.yaml"}}, + }, + } + out, err := yaml.Marshal(g) + Expect(err).ToNot(HaveOccurred()) + err = ioutil.WriteFile(filepath.Join(tmpdir, "gallery_simple.yaml"), out, 0644) + Expect(err).ToNot(HaveOccurred()) + + galleries := []gallery.Gallery{ + { + Name: "test", + URL: "file://" + filepath.Join(tmpdir, "gallery_simple.yaml"), + }, + } + + app, err = App(WithContext(c), + WithGalleries(galleries), + WithModelLoader(modelLoader), WithBackendAssets(backendAssets), WithBackendAssetsOutput(tmpdir)) Expect(err).ToNot(HaveOccurred()) go app.Listen("127.0.0.1:9090") @@ -143,6 +200,53 @@ var _ = Describe("API test", func() { }) Context("Applying models", func() { + It("applies models from a gallery", func() { + + models := getModels("http://127.0.0.1:9090/models/list") + Expect(len(models)).To(Equal(2), fmt.Sprint(models)) + Expect(models[0].Installed).To(BeFalse(), fmt.Sprint(models)) + Expect(models[1].Installed).To(BeFalse(), fmt.Sprint(models)) + + response := postModelApplyRequest("http://127.0.0.1:9090/models/apply", modelApplyRequest{ + ID: "test@bert2", + }) + + Expect(response["uuid"]).ToNot(BeEmpty(), fmt.Sprint(response)) + + uuid := response["uuid"].(string) + resp := map[string]interface{}{} + Eventually(func() bool { + response := getModelStatus("http://127.0.0.1:9090/models/jobs/" + uuid) + fmt.Println(response) + resp = response + return response["processed"].(bool) + }, "360s").Should(Equal(true)) + Expect(resp["message"]).ToNot(ContainSubstring("error")) + + dat, err := os.ReadFile(filepath.Join(tmpdir, "bert2.yaml")) + Expect(err).ToNot(HaveOccurred()) + + _, err = os.ReadFile(filepath.Join(tmpdir, "foo.yaml")) + Expect(err).ToNot(HaveOccurred()) + + content := map[string]interface{}{} + err = yaml.Unmarshal(dat, &content) + Expect(err).ToNot(HaveOccurred()) + Expect(content["backend"]).To(Equal("bert-embeddings")) + Expect(content["foo"]).To(Equal("bar")) + + models = getModels("http://127.0.0.1:9090/models/list") + Expect(len(models)).To(Equal(2), fmt.Sprint(models)) + Expect(models[0].Name).To(Or(Equal("bert"), Equal("bert2"))) + Expect(models[1].Name).To(Or(Equal("bert"), Equal("bert2"))) + for _, m := range models { + if m.Name == "bert2" { + Expect(m.Installed).To(BeTrue()) + } else { + Expect(m.Installed).To(BeFalse()) + } + } + }) It("overrides models", func() { response := postModelApplyRequest("http://127.0.0.1:9090/models/apply", modelApplyRequest{ URL: "https://raw.githubusercontent.com/go-skynet/model-gallery/main/bert-embeddings.yaml", diff --git a/api/config.go b/api/config.go index c6100db..67aec35 100644 --- a/api/config.go +++ b/api/config.go @@ -97,7 +97,7 @@ func ReadConfig(file string) (*Config, error) { return c, nil } -func (cm ConfigMerger) LoadConfigFile(file string) error { +func (cm *ConfigMerger) LoadConfigFile(file string) error { cm.Lock() defer cm.Unlock() c, err := ReadConfigFile(file) @@ -111,7 +111,7 @@ func (cm ConfigMerger) LoadConfigFile(file string) error { return nil } -func (cm ConfigMerger) LoadConfig(file string) error { +func (cm *ConfigMerger) LoadConfig(file string) error { cm.Lock() defer cm.Unlock() c, err := ReadConfig(file) @@ -123,14 +123,14 @@ func (cm ConfigMerger) LoadConfig(file string) error { return nil } -func (cm ConfigMerger) GetConfig(m string) (Config, bool) { +func (cm *ConfigMerger) GetConfig(m string) (Config, bool) { cm.Lock() defer cm.Unlock() v, exists := cm.configs[m] return v, exists } -func (cm ConfigMerger) ListConfigs() []string { +func (cm *ConfigMerger) ListConfigs() []string { cm.Lock() defer cm.Unlock() var res []string @@ -140,7 +140,7 @@ func (cm ConfigMerger) ListConfigs() []string { return res } -func (cm ConfigMerger) LoadConfigs(path string) error { +func (cm *ConfigMerger) LoadConfigs(path string) error { cm.Lock() defer cm.Unlock() entries, err := os.ReadDir(path) @@ -316,20 +316,32 @@ func readInput(c *fiber.Ctx, loader *model.ModelLoader, randomModel bool) (strin func readConfig(modelFile string, input *OpenAIRequest, cm *ConfigMerger, loader *model.ModelLoader, debug bool, threads, ctx int, f16 bool) (*Config, *OpenAIRequest, error) { // Load a config file if present after the model name modelConfig := filepath.Join(loader.ModelPath, modelFile+".yaml") - if _, err := os.Stat(modelConfig); err == nil { - if err := cm.LoadConfig(modelConfig); err != nil { - return nil, nil, fmt.Errorf("failed loading model config (%s) %s", modelConfig, err.Error()) - } - } var config *Config - cfg, exists := cm.GetConfig(modelFile) - if !exists { + + defaults := func() { config = defaultConfig(modelFile) config.ContextSize = ctx config.Threads = threads config.F16 = f16 config.Debug = debug + } + + cfg, exists := cm.GetConfig(modelFile) + if !exists { + if _, err := os.Stat(modelConfig); err == nil { + if err := cm.LoadConfig(modelConfig); err != nil { + return nil, nil, fmt.Errorf("failed loading model config (%s) %s", modelConfig, err.Error()) + } + cfg, exists = cm.GetConfig(modelFile) + if exists { + config = &cfg + } else { + defaults() + } + } else { + defaults() + } } else { config = &cfg } diff --git a/api/gallery.go b/api/gallery.go index a9a8722..46d92f9 100644 --- a/api/gallery.go +++ b/api/gallery.go @@ -2,26 +2,24 @@ package api import ( "context" - "encoding/json" "fmt" - "io/ioutil" - "net/http" - "net/url" "os" - "strings" "sync" "time" + json "github.com/json-iterator/go" + "github.com/go-skynet/LocalAI/pkg/gallery" "github.com/gofiber/fiber/v2" "github.com/google/uuid" "github.com/rs/zerolog/log" - "gopkg.in/yaml.v3" ) type galleryOp struct { - req ApplyGalleryModelRequest - id string + req gallery.GalleryModel + id string + galleries []gallery.Gallery + galleryName string } type galleryOpStatus struct { @@ -48,49 +46,27 @@ func newGalleryApplier(modelPath string) *galleryApplier { } } -func applyGallery(modelPath string, req ApplyGalleryModelRequest, cm *ConfigMerger, downloadStatus func(string, string, string, float64)) error { - url, err := req.DecodeURL() - if err != nil { - return err - } - - // Send a GET request to the URL - response, err := http.Get(url) - if err != nil { - return err - } - defer response.Body.Close() - - // Read the response body - body, err := ioutil.ReadAll(response.Body) - if err != nil { - return err - } - - // Unmarshal YAML data into a Config struct +// prepareModel applies a +func prepareModel(modelPath string, req gallery.GalleryModel, cm *ConfigMerger, downloadStatus func(string, string, string, float64)) error { var config gallery.Config - err = yaml.Unmarshal(body, &config) + + err := req.Get(&config) if err != nil { return err } config.Files = append(config.Files, req.AdditionalFiles...) - if err := gallery.Apply(modelPath, req.Name, &config, req.Overrides, downloadStatus); err != nil { - return err - } - - // Reload models - return cm.LoadConfigs(modelPath) + return gallery.InstallModel(modelPath, req.Name, &config, req.Overrides, downloadStatus) } -func (g *galleryApplier) updatestatus(s string, op *galleryOpStatus) { +func (g *galleryApplier) updateStatus(s string, op *galleryOpStatus) { g.Lock() defer g.Unlock() g.statuses[s] = op } -func (g *galleryApplier) getstatus(s string) *galleryOpStatus { +func (g *galleryApplier) getStatus(s string) *galleryOpStatus { g.Lock() defer g.Unlock() @@ -104,21 +80,40 @@ func (g *galleryApplier) start(c context.Context, cm *ConfigMerger) { case <-c.Done(): return case op := <-g.C: - g.updatestatus(op.id, &galleryOpStatus{Message: "processing", Progress: 0}) + g.updateStatus(op.id, &galleryOpStatus{Message: "processing", Progress: 0}) + // updates the status with an error updateError := func(e error) { - g.updatestatus(op.id, &galleryOpStatus{Error: e, Processed: true}) + g.updateStatus(op.id, &galleryOpStatus{Error: e, Processed: true, Message: "error: " + e.Error()}) } - if err := applyGallery(g.modelPath, op.req, cm, func(fileName string, current string, total string, percentage float64) { - g.updatestatus(op.id, &galleryOpStatus{Message: "processing", Progress: percentage, TotalFileSize: total, DownloadedFileSize: current}) + // displayDownload displays the download progress + progressCallback := func(fileName string, current string, total string, percentage float64) { + g.updateStatus(op.id, &galleryOpStatus{Message: "processing", Progress: percentage, TotalFileSize: total, DownloadedFileSize: current}) displayDownload(fileName, current, total, percentage) - }); err != nil { + } + + var err error + // if the request contains a gallery name, we apply the gallery from the gallery list + if op.galleryName != "" { + err = gallery.InstallModelFromGallery(op.galleries, op.galleryName, g.modelPath, op.req, progressCallback) + } else { + err = prepareModel(g.modelPath, op.req, cm, progressCallback) + } + + if err != nil { updateError(err) continue } - g.updatestatus(op.id, &galleryOpStatus{Processed: true, Message: "completed", Progress: 100}) + // Reload models + err = cm.LoadConfigs(g.modelPath) + if err != nil { + updateError(err) + continue + } + + g.updateStatus(op.id, &galleryOpStatus{Processed: true, Message: "completed", Progress: 100}) } } }() @@ -154,14 +149,14 @@ func ApplyGalleryFromFile(modelPath, s string, cm *ConfigMerger) error { if err != nil { return err } - var requests []ApplyGalleryModelRequest + var requests []gallery.GalleryModel err = json.Unmarshal(dat, &requests) if err != nil { return err } for _, r := range requests { - if err := applyGallery(modelPath, r, cm, displayDownload); err != nil { + if err := prepareModel(modelPath, r, cm, displayDownload); err != nil { return err } } @@ -170,14 +165,14 @@ func ApplyGalleryFromFile(modelPath, s string, cm *ConfigMerger) error { } func ApplyGalleryFromString(modelPath, s string, cm *ConfigMerger) error { - var requests []ApplyGalleryModelRequest + var requests []gallery.GalleryModel err := json.Unmarshal([]byte(s), &requests) if err != nil { return err } for _, r := range requests { - if err := applyGallery(modelPath, r, cm, displayDownload); err != nil { + if err := prepareModel(modelPath, r, cm, displayDownload); err != nil { return err } } @@ -185,56 +180,10 @@ func ApplyGalleryFromString(modelPath, s string, cm *ConfigMerger) error { return nil } -// endpoints - -type ApplyGalleryModelRequest struct { - URL string `json:"url"` - Name string `json:"name"` - Overrides map[string]interface{} `json:"overrides"` - AdditionalFiles []gallery.File `json:"files"` -} - -const ( - githubURI = "github:" -) - -func (request ApplyGalleryModelRequest) DecodeURL() (string, error) { - input := request.URL - var rawURL string - - if strings.HasPrefix(input, githubURI) { - parts := strings.Split(input, ":") - repoParts := strings.Split(parts[1], "@") - branch := "main" - - if len(repoParts) > 1 { - branch = repoParts[1] - } - - repoPath := strings.Split(repoParts[0], "/") - org := repoPath[0] - project := repoPath[1] - projectPath := strings.Join(repoPath[2:], "/") - - rawURL = fmt.Sprintf("https://raw.githubusercontent.com/%s/%s/%s/%s", org, project, branch, projectPath) - } else if strings.HasPrefix(input, "http://") || strings.HasPrefix(input, "https://") { - // Handle regular URLs - u, err := url.Parse(input) - if err != nil { - return "", fmt.Errorf("invalid URL: %w", err) - } - rawURL = u.String() - } else { - return "", fmt.Errorf("invalid URL format") - } - - return rawURL, nil -} - func getOpStatus(g *galleryApplier) func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error { - status := g.getstatus(c.Params("uuid")) + status := g.getStatus(c.Params("uuid")) if status == nil { return fmt.Errorf("could not find any status for ID") } @@ -243,9 +192,14 @@ func getOpStatus(g *galleryApplier) func(c *fiber.Ctx) error { } } -func applyModelGallery(modelPath string, cm *ConfigMerger, g chan galleryOp) func(c *fiber.Ctx) error { +type GalleryModel struct { + ID string `json:"id"` + gallery.GalleryModel +} + +func applyModelGallery(modelPath string, cm *ConfigMerger, g chan galleryOp, galleries []gallery.Gallery) func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error { - input := new(ApplyGalleryModelRequest) + input := new(GalleryModel) // Get input data from the request body if err := c.BodyParser(input); err != nil { return err @@ -256,8 +210,10 @@ func applyModelGallery(modelPath string, cm *ConfigMerger, g chan galleryOp) fun return err } g <- galleryOp{ - req: *input, - id: uuid.String(), + req: input.GalleryModel, + id: uuid.String(), + galleryName: input.ID, + galleries: galleries, } return c.JSON(struct { ID string `json:"uuid"` @@ -265,3 +221,23 @@ func applyModelGallery(modelPath string, cm *ConfigMerger, g chan galleryOp) fun }{ID: uuid.String(), StatusURL: c.BaseURL() + "/models/jobs/" + uuid.String()}) } } + +func listModelFromGallery(galleries []gallery.Gallery, basePath string) func(c *fiber.Ctx) error { + return func(c *fiber.Ctx) error { + log.Debug().Msgf("Listing models from galleries: %+v", galleries) + + models, err := gallery.AvailableGalleryModels(galleries, basePath) + if err != nil { + return err + } + log.Debug().Msgf("Models found from galleries: %+v", models) + for _, m := range models { + log.Debug().Msgf("Model found from galleries: %+v", m) + } + dat, err := json.Marshal(models) + if err != nil { + return err + } + return c.Send(dat) + } +} diff --git a/api/options.go b/api/options.go index 3d94eaa..b4669bc 100644 --- a/api/options.go +++ b/api/options.go @@ -4,6 +4,7 @@ import ( "context" "embed" + "github.com/go-skynet/LocalAI/pkg/gallery" model "github.com/go-skynet/LocalAI/pkg/model" ) @@ -21,6 +22,8 @@ type Option struct { preloadModelsFromPath string corsAllowOrigins string + galleries []gallery.Gallery + backendAssets embed.FS assetsDestination string } @@ -66,6 +69,12 @@ func WithBackendAssets(f embed.FS) AppOption { } } +func WithGalleries(galleries []gallery.Gallery) AppOption { + return func(o *Option) { + o.galleries = append(o.galleries, galleries...) + } +} + func WithContext(ctx context.Context) AppOption { return func(o *Option) { o.context = ctx diff --git a/go.mod b/go.mod index a1a6ce5..617e17e 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,8 @@ require ( github.com/google/uuid v1.3.0 github.com/hashicorp/go-multierror v1.1.1 github.com/imdario/mergo v0.3.16 + github.com/json-iterator/go v1.1.12 + github.com/mholt/archiver/v3 v3.5.1 github.com/mudler/go-stable-diffusion v0.0.0-20230605122230-d89260f598af github.com/nomic-ai/gpt4all/gpt4all-bindings/golang v0.0.0-20230620230702-09ae04cee90c github.com/onsi/ginkgo/v2 v2.11.0 @@ -29,6 +31,18 @@ require ( gopkg.in/yaml.v3 v3.0.1 ) +require ( + github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect + github.com/golang/snappy v0.0.2 // indirect + github.com/klauspost/pgzip v1.2.5 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/nwaples/rardecode v1.1.0 // indirect + github.com/pierrec/lz4/v4 v4.1.2 // indirect + github.com/ulikunitz/xz v0.5.9 // indirect + github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect +) + require ( github.com/KyleBanks/depth v1.2.1 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect @@ -52,7 +66,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect - github.com/mudler/go-piper v0.0.0-00010101000000-000000000000 // indirect + github.com/mudler/go-piper v0.0.0-20230621222733-56b8a81b4760 github.com/otiai10/mint v1.5.1 // indirect github.com/philhofer/fwd v1.1.2 // indirect github.com/rivo/uniseg v0.2.0 // indirect diff --git a/go.sum b/go.sum index 788eced..081aa9e 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,7 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -16,10 +17,9 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/donomii/go-rwkv.cpp v0.0.0-20230614130248-a57bca3031fb h1:ekua5AlHdmz8LaCOyX2bMp+a1cOEzReUEDFr5A1NOjg= -github.com/donomii/go-rwkv.cpp v0.0.0-20230614130248-a57bca3031fb/go.mod h1:gWy7FIWioqYmYxkaoFyBnaKApeZVrUkHhv9EV9pz4dM= -github.com/donomii/go-rwkv.cpp v0.0.0-20230619005719-f5a8c4539674 h1:G70Yf/QOCEL1v24idWnGd6rJsbqiGkJAJnMaWaolzEg= -github.com/donomii/go-rwkv.cpp v0.0.0-20230619005719-f5a8c4539674/go.mod h1:gWy7FIWioqYmYxkaoFyBnaKApeZVrUkHhv9EV9pz4dM= +github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= +github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= +github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/ggerganov/whisper.cpp/bindings/go v0.0.0-20230606002726-57543c169e27 h1:boeMTUUBtnLU8JElZJHXrsUzROJar9/t6vGOFjkrhhI= github.com/ggerganov/whisper.cpp/bindings/go v0.0.0-20230606002726-57543c169e27/go.mod h1:QIjZ9OktHFG7p+/m3sMvrAJKKdWrr1fZIK0rM6HZlyo= github.com/go-audio/audio v1.0.0 h1:zS9vebldgbQqktK4H0lUqWrG8P0NxCJVqcj7ZpNnwd4= @@ -40,28 +40,18 @@ github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-skynet/bloomz.cpp v0.0.0-20230529155654-1834e77b83fa h1:gxr68r/6EWroay4iI81jxqGCDbKotY4+CiwdUkBz2NQ= -github.com/go-skynet/bloomz.cpp v0.0.0-20230529155654-1834e77b83fa/go.mod h1:wc0fJ9V04yiYTfgKvE5RUUSRQ5Kzi0Bo4I+U3nNOUuA= -github.com/go-skynet/go-bert.cpp v0.0.0-20230607105116-6069103f54b9 h1:wRGbDwNwPmSzoXVw/HLzXY4blpRvPWg7QW2OA0WKezA= -github.com/go-skynet/go-bert.cpp v0.0.0-20230607105116-6069103f54b9/go.mod h1:pXKCpYYXujMeAvgJHU6WoMfvYbr84563+J8+Ebkyr5U= -github.com/go-skynet/go-ggml-transformers.cpp v0.0.0-20230610083154-01b8436f4429 h1:9w0Fy1C8fVbqKZO7Pr9NcdiN4/PSKo2OVvIdQ6meRbA= -github.com/go-skynet/go-ggml-transformers.cpp v0.0.0-20230610083154-01b8436f4429/go.mod h1:k/mT/SdGC6UQnNbSzeJDRyJV6kw0GfEFTpH44uTqflA= -github.com/go-skynet/go-ggml-transformers.cpp v0.0.0-20230617123349-32b9223ccdb1 h1:jVGgzDSfpjD/0jl/ChpGI+O4EHSAeeU6DK7IyhH8PK8= -github.com/go-skynet/go-ggml-transformers.cpp v0.0.0-20230617123349-32b9223ccdb1/go.mod h1:31j1odgFXP8hDSUVfH0zErKI5aYVP18ddYnPkwCso2A= -github.com/go-skynet/go-llama.cpp v0.0.0-20230614112429-a7960253c209 h1:4JNmUNjb1lo7hHZ+Ro680PVoeZ5qvOSofXBfrWMOdQo= -github.com/go-skynet/go-llama.cpp v0.0.0-20230614112429-a7960253c209/go.mod h1:dUZekEbjnGUjk35v9iTIdmSst/NIDQ9s9Pyo4t1aBQg= -github.com/go-skynet/go-llama.cpp v0.0.0-20230616223721-7ad833b67070 h1:T771FjB1yQw8j4P5x4ayFrUPNTglzxRIqDjaNkMVIME= -github.com/go-skynet/go-llama.cpp v0.0.0-20230616223721-7ad833b67070/go.mod h1:tzi97YvT1bVQ+iTG39LvpDkKG1WbizgtljC+orSoM40= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofiber/fiber/v2 v2.46.0 h1:wkkWotblsGVlLjXj2dpgKQAYHtXumsK/HyFugQM68Ns= -github.com/gofiber/fiber/v2 v2.46.0/go.mod h1:DNl0/c37WLe0g92U6lx1VMQuxGUQY5V7EIaVoEsUffc= github.com/gofiber/fiber/v2 v2.47.0 h1:EN5lHVCc+Pyqh5OEsk8fzRiifgwpbrP0rulQ4iNf3fs= github.com/gofiber/fiber/v2 v2.47.0/go.mod h1:mbFMVN1lQuzziTkkakgtKKdjfsXSw9BKR5lmcNksUoU= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= +github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -75,8 +65,15 @@ github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY= github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= +github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -91,41 +88,33 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mudler/go-stable-diffusion v0.0.0-20230605122230-d89260f598af h1:XFq6OUqsWQam0OrEr05okXsJK/TQur3zoZTHbiZD3Ks= -github.com/mudler/go-stable-diffusion v0.0.0-20230605122230-d89260f598af/go.mod h1:8ufRkpz/S/9ahkaxzZ5i4WMgO9w4InEhuRoT7vK5Rnw= +github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= +github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nomic-ai/gpt4all/gpt4all-bindings/golang v0.0.0-20230614000846-8953b7f6a6d0 h1:+QXKRNwzKyJvcJoH6tcCF0KhcG5aBbpLUquSJxdTRCU= -github.com/nomic-ai/gpt4all/gpt4all-bindings/golang v0.0.0-20230614000846-8953b7f6a6d0/go.mod h1:4T3CHXyrt+7FQHXaxULZfPjHbD8/99WuDDJa0YVZARI= -github.com/nomic-ai/gpt4all/gpt4all-bindings/golang v0.0.0-20230616214310-42e80495642c h1:2rQvjlFyeEUvUotYFBIwWvh5zbMVJU1fCTX5RHlYT9E= -github.com/nomic-ai/gpt4all/gpt4all-bindings/golang v0.0.0-20230616214310-42e80495642c/go.mod h1:4T3CHXyrt+7FQHXaxULZfPjHbD8/99WuDDJa0YVZARI= -github.com/nomic-ai/gpt4all/gpt4all-bindings/golang v0.0.0-20230618181029-fd419caa551b h1:/bSjHAjTHZhbs6ak6lDbCKFOLiXTjTEx8TNMgcJEets= -github.com/nomic-ai/gpt4all/gpt4all-bindings/golang v0.0.0-20230618181029-fd419caa551b/go.mod h1:4T3CHXyrt+7FQHXaxULZfPjHbD8/99WuDDJa0YVZARI= -github.com/nomic-ai/gpt4all/gpt4all-bindings/golang v0.0.0-20230619183453-2b6cc99a31a1 h1:vp8KYkNIpkk9/zPchSuB2p5a5q+wTIQ9wcesOY5tH+s= -github.com/nomic-ai/gpt4all/gpt4all-bindings/golang v0.0.0-20230619183453-2b6cc99a31a1/go.mod h1:4T3CHXyrt+7FQHXaxULZfPjHbD8/99WuDDJa0YVZARI= -github.com/nomic-ai/gpt4all/gpt4all-bindings/golang v0.0.0-20230620230702-09ae04cee90c h1:axNtjd5k6Xs4Ck7B7VRRQu6q5lQzTsjdWmaJkDADopU= -github.com/nomic-ai/gpt4all/gpt4all-bindings/golang v0.0.0-20230620230702-09ae04cee90c/go.mod h1:4T3CHXyrt+7FQHXaxULZfPjHbD8/99WuDDJa0YVZARI= -github.com/onsi/ginkgo/v2 v2.10.0 h1:sfUl4qgLdvkChZrWCYndY2EAu9BRIw1YphNAzy1VNWs= -github.com/onsi/ginkgo/v2 v2.10.0/go.mod h1:UDQOh5wbQUlMnkLfVaIUMtQ1Vus92oM+P2JX1aulgcE= +github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= +github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= -github.com/otiai10/openaigo v1.1.1 h1:ZGL13vSYA1WF/9g18JEzfEuyC+MscGyrtPMsilebOY0= -github.com/otiai10/openaigo v1.1.1/go.mod h1:792bx6AWTS61weDi2EzKpHHnTF4eDMAlJ5GvAk/mgPg= github.com/otiai10/openaigo v1.2.0 h1:Whq+uvgqw8NdIsVdixtBKCAI6OdfCJiGPlhUnYJQ6Ag= github.com/otiai10/openaigo v1.2.0/go.mod h1:792bx6AWTS61weDi2EzKpHHnTF4eDMAlJ5GvAk/mgPg= github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= +github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= +github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -136,8 +125,6 @@ github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc= github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sashabaranov/go-openai v1.10.1 h1:6WyHJaNzF266VaEEuW6R4YW+Ei0wpMnqRYPGK7fhuhQ= -github.com/sashabaranov/go-openai v1.10.1/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= github.com/sashabaranov/go-openai v1.11.3 h1:bvwWF8hj4UhPlswBdL9/IfOpaHXfzGCJO8WY8ml9sGc= github.com/sashabaranov/go-openai v1.11.3/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4= @@ -145,7 +132,6 @@ github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3 github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4= github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -155,22 +141,21 @@ github.com/swaggo/swag v1.16.1/go.mod h1:9/LMvHycG3NFHfR6LwvikHv5iFvmPADQ359cKik github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= -github.com/tmc/langchaingo v0.0.0-20230610024316-06cb7b57ea80 h1:Y+a76dNVbdWduw3gznOr2O2OSZkdwDRYPKTDpG/vM9I= -github.com/tmc/langchaingo v0.0.0-20230610024316-06cb7b57ea80/go.mod h1:6l1WoyqVDwkv7cFlY3gfcTv8yVowVyuutKv8PGlQCWI= github.com/tmc/langchaingo v0.0.0-20230616220619-1b3da4433944 h1:EE9fvNENTdRc/yI/1zAs7VFbmDk6JZ7EbBIFl+TsCm0= github.com/tmc/langchaingo v0.0.0-20230616220619-1b3da4433944/go.mod h1:6l1WoyqVDwkv7cFlY3gfcTv8yVowVyuutKv8PGlQCWI= -github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc= -github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I= +github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c= -github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= github.com/valyala/fasthttp v1.48.0 h1:oJWvHb9BIZToTQS3MuQ2R3bJZiNSa2KiNdeI8A+79Tc= github.com/valyala/fasthttp v1.48.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -210,8 +195,6 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -233,6 +216,7 @@ golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/main.go b/main.go index dc6968a..fbdc390 100644 --- a/main.go +++ b/main.go @@ -1,11 +1,13 @@ package main import ( + "encoding/json" "fmt" "os" "path/filepath" api "github.com/go-skynet/LocalAI/api" + "github.com/go-skynet/LocalAI/pkg/gallery" model "github.com/go-skynet/LocalAI/pkg/model" "github.com/rs/zerolog" "github.com/rs/zerolog/log" @@ -53,6 +55,11 @@ func main() { EnvVars: []string{"MODELS_PATH"}, Value: filepath.Join(path, "models"), }, + &cli.StringFlag{ + Name: "galleries", + Usage: "JSON list of galleries", + EnvVars: []string{"GALLERIES"}, + }, &cli.StringFlag{ Name: "preload-models", Usage: "A List of models to apply in JSON at start", @@ -123,8 +130,13 @@ It uses llama.cpp, ggml and gpt4all as backend with golang c bindings. Copyright: "go-skynet authors", Action: func(ctx *cli.Context) error { fmt.Printf("Starting LocalAI using %d threads, with models path: %s\n", ctx.Int("threads"), ctx.String("models-path")) + galls := ctx.String("galleries") + var galleries []gallery.Gallery + err := json.Unmarshal([]byte(galls), &galleries) + fmt.Println(err) app, err := api.App( api.WithConfigFile(ctx.String("config-file")), + api.WithGalleries(galleries), api.WithJSONStringPreload(ctx.String("preload-models")), api.WithYAMLConfigPreload(ctx.String("preload-models-config")), api.WithModelLoader(model.NewModelLoader(ctx.String("models-path"))), diff --git a/pkg/gallery/gallery.go b/pkg/gallery/gallery.go new file mode 100644 index 0000000..d444034 --- /dev/null +++ b/pkg/gallery/gallery.go @@ -0,0 +1,99 @@ +package gallery + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/go-skynet/LocalAI/pkg/utils" + "github.com/imdario/mergo" + "gopkg.in/yaml.v2" +) + +type Gallery struct { + URL string `json:"url" yaml:"url"` + Name string `json:"name" yaml:"name"` +} + +// Installs a model from the gallery (galleryname@modelname) +func InstallModelFromGallery(galleries []Gallery, name string, basePath string, req GalleryModel, downloadStatus func(string, string, string, float64)) error { + models, err := AvailableGalleryModels(galleries, basePath) + if err != nil { + return err + } + + applyModel := func(model *GalleryModel) error { + var config Config + + err := model.Get(&config) + if err != nil { + return err + } + + if req.Name != "" { + model.Name = req.Name + } + + config.Files = append(config.Files, req.AdditionalFiles...) + config.Files = append(config.Files, model.AdditionalFiles...) + + // TODO model.Overrides could be merged with user overrides (not defined yet) + if err := mergo.Merge(&model.Overrides, req.Overrides, mergo.WithOverride); err != nil { + return err + } + + if err := InstallModel(basePath, model.Name, &config, model.Overrides, downloadStatus); err != nil { + return err + } + + return nil + } + + for _, model := range models { + if name == fmt.Sprintf("%s@%s", model.Gallery.Name, model.Name) { + return applyModel(model) + } + } + + return fmt.Errorf("no model found with name %q", name) +} + +// List available models +// Models galleries are a list of json files that are hosted on a remote server (for example github). +// Each json file contains a list of models that can be downloaded and optionally overrides to define a new model setting. +func AvailableGalleryModels(galleries []Gallery, basePath string) ([]*GalleryModel, error) { + var models []*GalleryModel + + // Get models from galleries + for _, gallery := range galleries { + galleryModels, err := getGalleryModels(gallery, basePath) + if err != nil { + return nil, err + } + models = append(models, galleryModels...) + } + + return models, nil +} + +func getGalleryModels(gallery Gallery, basePath string) ([]*GalleryModel, error) { + var models []*GalleryModel = []*GalleryModel{} + + err := utils.GetURI(gallery.URL, func(d []byte) error { + return yaml.Unmarshal(d, &models) + }) + if err != nil { + return models, err + } + + // Add gallery to models + for _, model := range models { + model.Gallery = gallery + // we check if the model was already installed by checking if the config file exists + // TODO: (what to do if the model doesn't install a config file?) + if _, err := os.Stat(filepath.Join(basePath, fmt.Sprintf("%s.yaml", model.Name))); err == nil { + model.Installed = true + } + } + return models, nil +} diff --git a/pkg/gallery/models.go b/pkg/gallery/models.go index 8d4cd29..a354d8b 100644 --- a/pkg/gallery/models.go +++ b/pkg/gallery/models.go @@ -81,7 +81,7 @@ func ReadConfigFile(filePath string) (*Config, error) { return &config, nil } -func Apply(basePath, nameOverride string, config *Config, configOverrides map[string]interface{}, downloadStatus func(string, string, string, float64)) error { +func InstallModel(basePath, nameOverride string, config *Config, configOverrides map[string]interface{}, downloadStatus func(string, string, string, float64)) error { // Create base path if it doesn't exist err := os.MkdirAll(basePath, 0755) if err != nil { @@ -171,6 +171,7 @@ func Apply(basePath, nameOverride string, config *Config, configOverrides map[st // Verify SHA calculatedSHA := fmt.Sprintf("%x", progress.hash.Sum(nil)) if calculatedSHA != file.SHA256 { + log.Debug().Msgf("SHA mismatch for file %q ( calculated: %s != metadata: %s )", file.Filename, calculatedSHA, file.SHA256) return fmt.Errorf("SHA mismatch for file %q ( calculated: %s != metadata: %s )", file.Filename, calculatedSHA, file.SHA256) } } else { @@ -178,6 +179,13 @@ func Apply(basePath, nameOverride string, config *Config, configOverrides map[st } log.Debug().Msgf("File %q downloaded and verified", file.Filename) + if utils.IsArchive(filePath) { + log.Debug().Msgf("File %q is an archive, uncompressing to %s", file.Filename, basePath) + if err := utils.ExtractArchive(filePath, basePath); err != nil { + log.Debug().Msgf("Failed decompressing %q: %s", file.Filename, err.Error()) + return err + } + } } // Write prompt template contents to separate files @@ -211,33 +219,37 @@ func Apply(basePath, nameOverride string, config *Config, configOverrides map[st return err } - configFilePath := filepath.Join(basePath, name+".yaml") + // write config file + if len(configOverrides) != 0 || len(config.ConfigFile) != 0 { + configFilePath := filepath.Join(basePath, name+".yaml") - // Read and update config file as map[string]interface{} - configMap := make(map[string]interface{}) - err = yaml.Unmarshal([]byte(config.ConfigFile), &configMap) - if err != nil { - return fmt.Errorf("failed to unmarshal config YAML: %v", err) - } + // Read and update config file as map[string]interface{} + configMap := make(map[string]interface{}) + err = yaml.Unmarshal([]byte(config.ConfigFile), &configMap) + if err != nil { + return fmt.Errorf("failed to unmarshal config YAML: %v", err) + } - configMap["name"] = name + configMap["name"] = name - if err := mergo.Merge(&configMap, configOverrides, mergo.WithOverride); err != nil { - return err - } + if err := mergo.Merge(&configMap, configOverrides, mergo.WithOverride); err != nil { + return err + } - // Write updated config file - updatedConfigYAML, err := yaml.Marshal(configMap) - if err != nil { - return fmt.Errorf("failed to marshal updated config YAML: %v", err) - } + // Write updated config file + updatedConfigYAML, err := yaml.Marshal(configMap) + if err != nil { + return fmt.Errorf("failed to marshal updated config YAML: %v", err) + } - err = os.WriteFile(configFilePath, updatedConfigYAML, 0644) - if err != nil { - return fmt.Errorf("failed to write updated config file: %v", err) + err = os.WriteFile(configFilePath, updatedConfigYAML, 0644) + if err != nil { + return fmt.Errorf("failed to write updated config file: %v", err) + } + + log.Debug().Msgf("Written config file %s", configFilePath) } - log.Debug().Msgf("Written config file %s", configFilePath) return nil } diff --git a/pkg/gallery/models_test.go b/pkg/gallery/models_test.go index 343bf6a..9ea87a7 100644 --- a/pkg/gallery/models_test.go +++ b/pkg/gallery/models_test.go @@ -1,6 +1,7 @@ package gallery_test import ( + "io/ioutil" "os" "path/filepath" @@ -19,7 +20,7 @@ var _ = Describe("Model test", func() { c, err := ReadConfigFile(filepath.Join(os.Getenv("FIXTURES"), "gallery_simple.yaml")) Expect(err).ToNot(HaveOccurred()) - err = Apply(tempdir, "", c, map[string]interface{}{}, func(string, string, string, float64) {}) + err = InstallModel(tempdir, "", c, map[string]interface{}{}, func(string, string, string, float64) {}) Expect(err).ToNot(HaveOccurred()) for _, f := range []string{"cerebras", "cerebras-completion.tmpl", "cerebras-chat.tmpl", "cerebras.yaml"} { @@ -38,6 +39,51 @@ var _ = Describe("Model test", func() { Expect(content["context_size"]).To(Equal(1024)) }) + It("applies model from gallery correctly", func() { + tempdir, err := os.MkdirTemp("", "test") + Expect(err).ToNot(HaveOccurred()) + defer os.RemoveAll(tempdir) + + gallery := []GalleryModel{{ + Name: "bert", + URL: "https://raw.githubusercontent.com/go-skynet/model-gallery/main/bert-embeddings.yaml", + }} + out, err := yaml.Marshal(gallery) + Expect(err).ToNot(HaveOccurred()) + err = ioutil.WriteFile(filepath.Join(tempdir, "gallery_simple.yaml"), out, 0644) + Expect(err).ToNot(HaveOccurred()) + + galleries := []Gallery{ + { + Name: "test", + URL: "file://" + filepath.Join(tempdir, "gallery_simple.yaml"), + }, + } + + models, err := AvailableGalleryModels(galleries, tempdir) + Expect(err).ToNot(HaveOccurred()) + Expect(len(models)).To(Equal(1)) + Expect(models[0].Name).To(Equal("bert")) + Expect(models[0].URL).To(Equal("https://raw.githubusercontent.com/go-skynet/model-gallery/main/bert-embeddings.yaml")) + Expect(models[0].Installed).To(BeFalse()) + + err = InstallModelFromGallery(galleries, "test@bert", tempdir, GalleryModel{}, func(s1, s2, s3 string, f float64) {}) + Expect(err).ToNot(HaveOccurred()) + + dat, err := os.ReadFile(filepath.Join(tempdir, "bert.yaml")) + Expect(err).ToNot(HaveOccurred()) + + content := map[string]interface{}{} + err = yaml.Unmarshal(dat, &content) + Expect(err).ToNot(HaveOccurred()) + Expect(content["backend"]).To(Equal("bert-embeddings")) + + models, err = AvailableGalleryModels(galleries, tempdir) + Expect(err).ToNot(HaveOccurred()) + Expect(len(models)).To(Equal(1)) + Expect(models[0].Installed).To(BeTrue()) + }) + It("renames model correctly", func() { tempdir, err := os.MkdirTemp("", "test") Expect(err).ToNot(HaveOccurred()) @@ -45,7 +91,7 @@ var _ = Describe("Model test", func() { c, err := ReadConfigFile(filepath.Join(os.Getenv("FIXTURES"), "gallery_simple.yaml")) Expect(err).ToNot(HaveOccurred()) - err = Apply(tempdir, "foo", c, map[string]interface{}{}, func(string, string, string, float64) {}) + err = InstallModel(tempdir, "foo", c, map[string]interface{}{}, func(string, string, string, float64) {}) Expect(err).ToNot(HaveOccurred()) for _, f := range []string{"cerebras", "cerebras-completion.tmpl", "cerebras-chat.tmpl", "foo.yaml"} { @@ -61,7 +107,7 @@ var _ = Describe("Model test", func() { c, err := ReadConfigFile(filepath.Join(os.Getenv("FIXTURES"), "gallery_simple.yaml")) Expect(err).ToNot(HaveOccurred()) - err = Apply(tempdir, "foo", c, map[string]interface{}{"backend": "foo"}, func(string, string, string, float64) {}) + err = InstallModel(tempdir, "foo", c, map[string]interface{}{"backend": "foo"}, func(string, string, string, float64) {}) Expect(err).ToNot(HaveOccurred()) for _, f := range []string{"cerebras", "cerebras-completion.tmpl", "cerebras-chat.tmpl", "foo.yaml"} { @@ -87,7 +133,7 @@ var _ = Describe("Model test", func() { c, err := ReadConfigFile(filepath.Join(os.Getenv("FIXTURES"), "gallery_simple.yaml")) Expect(err).ToNot(HaveOccurred()) - err = Apply(tempdir, "../../../foo", c, map[string]interface{}{}, func(string, string, string, float64) {}) + err = InstallModel(tempdir, "../../../foo", c, map[string]interface{}{}, func(string, string, string, float64) {}) Expect(err).To(HaveOccurred()) }) }) diff --git a/pkg/gallery/request.go b/pkg/gallery/request.go new file mode 100644 index 0000000..2e2da3e --- /dev/null +++ b/pkg/gallery/request.go @@ -0,0 +1,74 @@ +package gallery + +import ( + "fmt" + "net/url" + "strings" + + "github.com/go-skynet/LocalAI/pkg/utils" + "gopkg.in/yaml.v2" +) + +// endpoints + +type GalleryModel struct { + URL string `json:"url" yaml:"url"` + Name string `json:"name" yaml:"name"` + Overrides map[string]interface{} `json:"overrides" yaml:"overrides"` + AdditionalFiles []File `json:"files" yaml:"files"` + Gallery Gallery `json:"gallery" yaml:"gallery"` + Installed bool `json:"installed" yaml:"installed"` +} + +const ( + githubURI = "github:" +) + +func (request GalleryModel) DecodeURL() (string, error) { + input := request.URL + var rawURL string + + if strings.HasPrefix(input, githubURI) { + parts := strings.Split(input, ":") + repoParts := strings.Split(parts[1], "@") + branch := "main" + + if len(repoParts) > 1 { + branch = repoParts[1] + } + + repoPath := strings.Split(repoParts[0], "/") + org := repoPath[0] + project := repoPath[1] + projectPath := strings.Join(repoPath[2:], "/") + + rawURL = fmt.Sprintf("https://raw.githubusercontent.com/%s/%s/%s/%s", org, project, branch, projectPath) + } else if strings.HasPrefix(input, "http://") || strings.HasPrefix(input, "https://") { + // Handle regular URLs + u, err := url.Parse(input) + if err != nil { + return "", fmt.Errorf("invalid URL: %w", err) + } + rawURL = u.String() + // check if it's a file path + } else if strings.HasPrefix(input, "file://") { + return input, nil + } else { + + return "", fmt.Errorf("invalid URL format: %s", input) + } + + return rawURL, nil +} + +// Get fetches a model from a URL and unmarshals it into a struct +func (request GalleryModel) Get(i interface{}) error { + url, err := request.DecodeURL() + if err != nil { + return err + } + + return utils.GetURI(url, func(d []byte) error { + return yaml.Unmarshal(d, i) + }) +} diff --git a/api/gallery_test.go b/pkg/gallery/request_test.go similarity index 56% rename from api/gallery_test.go rename to pkg/gallery/request_test.go index 1c92c0d..12a8d06 100644 --- a/api/gallery_test.go +++ b/pkg/gallery/request_test.go @@ -1,27 +1,39 @@ -package api_test +package gallery_test import ( - . "github.com/go-skynet/LocalAI/api" + . "github.com/go-skynet/LocalAI/pkg/gallery" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) +type example struct { + Name string `yaml:"name"` +} + var _ = Describe("Gallery API tests", func() { + Context("requests", func() { It("parses github with a branch", func() { - req := ApplyGalleryModelRequest{URL: "github:go-skynet/model-gallery/gpt4all-j.yaml@main"} + req := GalleryModel{URL: "github:go-skynet/model-gallery/gpt4all-j.yaml@main"} + var e example + err := req.Get(&e) + Expect(err).ToNot(HaveOccurred()) + Expect(e.Name).To(Equal("gpt4all-j")) + }) + It("parses github without a branch", func() { + req := GalleryModel{URL: "github:go-skynet/model-gallery/gpt4all-j.yaml@main"} str, err := req.DecodeURL() Expect(err).ToNot(HaveOccurred()) Expect(str).To(Equal("https://raw.githubusercontent.com/go-skynet/model-gallery/main/gpt4all-j.yaml")) }) It("parses github without a branch", func() { - req := ApplyGalleryModelRequest{URL: "github:go-skynet/model-gallery/gpt4all-j.yaml"} + req := GalleryModel{URL: "github:go-skynet/model-gallery/gpt4all-j.yaml"} str, err := req.DecodeURL() Expect(err).ToNot(HaveOccurred()) Expect(str).To(Equal("https://raw.githubusercontent.com/go-skynet/model-gallery/main/gpt4all-j.yaml")) }) It("parses URLS", func() { - req := ApplyGalleryModelRequest{URL: "https://raw.githubusercontent.com/go-skynet/model-gallery/main/gpt4all-j.yaml"} + req := GalleryModel{URL: "https://raw.githubusercontent.com/go-skynet/model-gallery/main/gpt4all-j.yaml"} str, err := req.DecodeURL() Expect(err).ToNot(HaveOccurred()) Expect(str).To(Equal("https://raw.githubusercontent.com/go-skynet/model-gallery/main/gpt4all-j.yaml")) diff --git a/pkg/utils/untar.go b/pkg/utils/untar.go new file mode 100644 index 0000000..782b2d1 --- /dev/null +++ b/pkg/utils/untar.go @@ -0,0 +1,56 @@ +package utils + +import ( + "fmt" + + "github.com/mholt/archiver/v3" +) + +func IsArchive(file string) bool { + uaIface, err := archiver.ByExtension(file) + if err != nil { + return false + } + + _, ok := uaIface.(archiver.Unarchiver) + return ok +} + +func ExtractArchive(archive, dst string) error { + uaIface, err := archiver.ByExtension(archive) + if err != nil { + return err + } + + un, ok := uaIface.(archiver.Unarchiver) + if !ok { + return fmt.Errorf("format specified by source filename is not an archive format: %s (%T)", archive, uaIface) + } + + mytar := &archiver.Tar{ + OverwriteExisting: true, + MkdirAll: true, + ImplicitTopLevelFolder: false, + ContinueOnError: true, + } + + switch v := uaIface.(type) { + case *archiver.Tar: + uaIface = mytar + case *archiver.TarBrotli: + v.Tar = mytar + case *archiver.TarBz2: + v.Tar = mytar + case *archiver.TarGz: + v.Tar = mytar + case *archiver.TarLz4: + v.Tar = mytar + case *archiver.TarSz: + v.Tar = mytar + case *archiver.TarXz: + v.Tar = mytar + case *archiver.TarZstd: + v.Tar = mytar + } + return un.Unarchive(archive, dst) +} diff --git a/pkg/utils/uri.go b/pkg/utils/uri.go new file mode 100644 index 0000000..753a283 --- /dev/null +++ b/pkg/utils/uri.go @@ -0,0 +1,37 @@ +package utils + +import ( + "io/ioutil" + "net/http" + "strings" +) + +func GetURI(url string, f func(i []byte) error) error { + if strings.HasPrefix(url, "file://") { + rawURL := strings.TrimPrefix(url, "file://") + // Read the response body + body, err := ioutil.ReadFile(rawURL) + if err != nil { + return err + } + + // Unmarshal YAML data into a struct + return f(body) + } + + // Send a GET request to the URL + response, err := http.Get(url) + if err != nil { + return err + } + defer response.Body.Close() + + // Read the response body + body, err := ioutil.ReadAll(response.Body) + if err != nil { + return err + } + + // Unmarshal YAML data into a struct + return f(body) +}