Skip to content

Commit 66fa4f1

Browse files
authored
feat: share models by url (#1522)
* feat: allow to pass by models via args * expose it also as an env/arg * docs: enhancements to build/requirements * do not display status always * print download status * not all mesages are debug
1 parent d6565f3 commit 66fa4f1

File tree

9 files changed

+145
-49
lines changed

9 files changed

+145
-49
lines changed

api/api.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"errors"
66
"fmt"
77
"os"
8+
"path/filepath"
89
"strings"
910

1011
config "github.com/go-skynet/LocalAI/api/config"
@@ -16,6 +17,7 @@ import (
1617
"github.com/go-skynet/LocalAI/metrics"
1718
"github.com/go-skynet/LocalAI/pkg/assets"
1819
"github.com/go-skynet/LocalAI/pkg/model"
20+
"github.com/go-skynet/LocalAI/pkg/utils"
1921

2022
"github.com/gofiber/fiber/v2"
2123
"github.com/gofiber/fiber/v2/middleware/cors"
@@ -36,6 +38,26 @@ func Startup(opts ...options.AppOption) (*options.Option, *config.ConfigLoader,
3638
log.Info().Msgf("Starting LocalAI using %d threads, with models path: %s", options.Threads, options.Loader.ModelPath)
3739
log.Info().Msgf("LocalAI version: %s", internal.PrintableVersion())
3840

41+
modelPath := options.Loader.ModelPath
42+
if len(options.ModelsURL) > 0 {
43+
for _, url := range options.ModelsURL {
44+
if utils.LooksLikeURL(url) {
45+
// md5 of model name
46+
md5Name := utils.MD5(url)
47+
48+
// check if file exists
49+
if _, err := os.Stat(filepath.Join(modelPath, md5Name)); errors.Is(err, os.ErrNotExist) {
50+
err := utils.DownloadFile(url, filepath.Join(modelPath, md5Name)+".yaml", "", func(fileName, current, total string, percent float64) {
51+
utils.DisplayDownloadFunction(fileName, current, total, percent)
52+
})
53+
if err != nil {
54+
log.Error().Msgf("error loading model: %s", err.Error())
55+
}
56+
}
57+
}
58+
}
59+
}
60+
3961
cl := config.NewConfigLoader()
4062
if err := cl.LoadConfigs(options.Loader.ModelPath); err != nil {
4163
log.Error().Msgf("error loading config files: %s", err.Error())

api/config/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ func (cm *ConfigLoader) Preload(modelPath string) error {
286286
// check if file exists
287287
if _, err := os.Stat(filepath.Join(modelPath, md5Name)); errors.Is(err, os.ErrNotExist) {
288288
err := utils.DownloadFile(modelURL, filepath.Join(modelPath, md5Name), "", func(fileName, current, total string, percent float64) {
289-
log.Info().Msgf("Downloading %s: %s/%s (%.2f%%)", fileName, current, total, percent)
289+
utils.DisplayDownloadFunction(fileName, current, total, percent)
290290
})
291291
if err != nil {
292292
return err

api/options/options.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,12 @@ type Option struct {
4040
SingleBackend bool
4141
ParallelBackendRequests bool
4242

43-
WatchDogIdle bool
44-
WatchDogBusy bool
45-
WatchDog bool
43+
WatchDogIdle bool
44+
WatchDogBusy bool
45+
WatchDog bool
46+
47+
ModelsURL []string
48+
4649
WatchDogBusyTimeout, WatchDogIdleTimeout time.Duration
4750
}
4851

@@ -63,6 +66,12 @@ func NewOptions(o ...AppOption) *Option {
6366
return opt
6467
}
6568

69+
func WithModelsURL(urls ...string) AppOption {
70+
return func(o *Option) {
71+
o.ModelsURL = urls
72+
}
73+
}
74+
6675
func WithCors(b bool) AppOption {
6776
return func(o *Option) {
6877
o.CORS = b

docs/content/advanced/_index.en.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -359,15 +359,7 @@ docker run --env REBUILD=true localai
359359
docker run --env-file .env localai
360360
```
361361

362-
### Build only a single backend
363362

364-
You can control the backends that are built by setting the `GRPC_BACKENDS` environment variable. For instance, to build only the `llama-cpp` backend only:
365-
366-
```bash
367-
make GRPC_BACKENDS=backend-assets/grpc/llama-cpp build
368-
```
369-
370-
By default, all the backends are built.
371363

372364
### Extra backends
373365

docs/content/build/_index.en.md

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,69 @@ url = '/basics/build/'
77

88
+++
99

10-
### Build locally
10+
### Build
11+
12+
#### Container image
1113

1214
Requirements:
1315

14-
Either Docker/podman, or
15-
- Golang >= 1.21
16-
- Cmake/make
17-
- GCC
16+
- Docker or podman, or a container engine
1817

19-
In order to build the `LocalAI` container image locally you can use `docker`:
18+
In order to build the `LocalAI` container image locally you can use `docker`, for example:
2019

2120
```
2221
# build the image
2322
docker build -t localai .
2423
docker run localai
2524
```
2625

27-
Or you can build the manually binary with `make`:
26+
#### Locally
27+
28+
In order to build LocalAI locally, you need the following requirements:
29+
30+
- Golang >= 1.21
31+
- Cmake/make
32+
- GCC
33+
- GRPC
34+
35+
To install the dependencies follow the instructions below:
36+
37+
{{< tabs >}}
38+
{{% tab name="Apple" %}}
39+
40+
```bash
41+
brew install abseil cmake go grpc protobuf wget
42+
```
43+
44+
{{% /tab %}}
45+
{{% tab name="Debian" %}}
46+
47+
```bash
48+
apt install protobuf-compiler-grpc libgrpc-dev make cmake
49+
```
50+
51+
{{% /tab %}}
52+
{{% tab name="From source" %}}
53+
54+
Specify `BUILD_GRPC_FOR_BACKEND_LLAMA=true` to build automatically the gRPC dependencies
55+
56+
```bash
57+
make ... BUILD_GRPC_FOR_BACKEND_LLAMA=true build
58+
```
59+
60+
{{% /tab %}}
61+
{{< /tabs >}}
62+
63+
64+
To build LocalAI with `make`:
2865

2966
```
3067
git clone https://github.com/go-skynet/LocalAI
3168
cd LocalAI
3269
make build
3370
```
3471

35-
To run: `./local-ai`
72+
This should produce the binary `local-ai`
3673

3774
{{% notice note %}}
3875

@@ -54,7 +91,7 @@ docker run --rm -ti -p 8080:8080 -e DEBUG=true -e MODELS_PATH=/models -e THREADS
5491

5592
{{% /notice %}}
5693

57-
### Build on mac
94+
### Example: Build on mac
5895

5996
Building on Mac (M1 or M2) works, but you may need to install some prerequisites using `brew`.
6097

@@ -188,6 +225,16 @@ make BUILD_TYPE=metal build
188225
# Note: only models quantized with q4_0 are supported!
189226
```
190227

228+
### Build only a single backend
229+
230+
You can control the backends that are built by setting the `GRPC_BACKENDS` environment variable. For instance, to build only the `llama-cpp` backend only:
231+
232+
```bash
233+
make GRPC_BACKENDS=backend-assets/grpc/llama-cpp build
234+
```
235+
236+
By default, all the backends are built.
237+
191238
### Windows compatibility
192239

193240
Make sure to give enough resources to the running container. See https://github.com/go-skynet/LocalAI/issues/2

main.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ func main() {
9999
Usage: "A List of models to apply in JSON at start",
100100
EnvVars: []string{"PRELOAD_MODELS"},
101101
},
102+
&cli.StringFlag{
103+
Name: "models",
104+
Usage: "A List of models URLs configurations.",
105+
EnvVars: []string{"MODELS"},
106+
},
102107
&cli.StringFlag{
103108
Name: "preload-models-config",
104109
Usage: "A List of models to apply at startup. Path to a YAML config file",
@@ -222,6 +227,7 @@ For a list of compatible model, check out: https://localai.io/model-compatibilit
222227
options.WithBackendAssetsOutput(ctx.String("backend-assets-path")),
223228
options.WithUploadLimitMB(ctx.Int("upload-limit")),
224229
options.WithApiKeys(ctx.StringSlice("api-keys")),
230+
options.WithModelsURL(append(ctx.StringSlice("models"), ctx.Args().Slice()...)...),
225231
}
226232

227233
idleWatchDog := ctx.Bool("enable-watchdog-idle")

pkg/model/initializers.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -239,10 +239,10 @@ func (ml *ModelLoader) GreedyLoader(opts ...Option) (*grpc.Client, error) {
239239
for _, b := range o.externalBackends {
240240
allBackendsToAutoLoad = append(allBackendsToAutoLoad, b)
241241
}
242-
log.Debug().Msgf("Loading model '%s' greedly from all the available backends: %s", o.model, strings.Join(allBackendsToAutoLoad, ", "))
242+
log.Info().Msgf("Loading model '%s' greedly from all the available backends: %s", o.model, strings.Join(allBackendsToAutoLoad, ", "))
243243

244244
for _, b := range allBackendsToAutoLoad {
245-
log.Debug().Msgf("[%s] Attempting to load", b)
245+
log.Info().Msgf("[%s] Attempting to load", b)
246246
options := []Option{
247247
WithBackendString(b),
248248
WithModel(o.model),
@@ -257,14 +257,14 @@ func (ml *ModelLoader) GreedyLoader(opts ...Option) (*grpc.Client, error) {
257257

258258
model, modelerr := ml.BackendLoader(options...)
259259
if modelerr == nil && model != nil {
260-
log.Debug().Msgf("[%s] Loads OK", b)
260+
log.Info().Msgf("[%s] Loads OK", b)
261261
return model, nil
262262
} else if modelerr != nil {
263263
err = multierror.Append(err, modelerr)
264-
log.Debug().Msgf("[%s] Fails: %s", b, modelerr.Error())
264+
log.Info().Msgf("[%s] Fails: %s", b, modelerr.Error())
265265
} else if model == nil {
266266
err = multierror.Append(err, fmt.Errorf("backend returned no usable model"))
267-
log.Debug().Msgf("[%s] Fails: %s", b, "backend returned no usable model")
267+
log.Info().Msgf("[%s] Fails: %s", b, "backend returned no usable model")
268268
}
269269
}
270270

pkg/utils/logging.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ func DisplayDownloadFunction(fileName string, current string, total string, perc
2929
}
3030

3131
if total != "" {
32-
log.Debug().Msgf("Downloading %s: %s/%s (%.2f%%) ETA: %s", fileName, current, total, percentage, eta)
32+
log.Info().Msgf("Downloading %s: %s/%s (%.2f%%) ETA: %s", fileName, current, total, percentage, eta)
3333
} else {
34-
log.Debug().Msgf("Downloading: %s", current)
34+
log.Info().Msgf("Downloading: %s", current)
3535
}
3636
}
3737
}

pkg/utils/uri.go

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,8 @@ import (
1515
"github.com/rs/zerolog/log"
1616
)
1717

18-
const (
19-
githubURI = "github:"
20-
)
21-
2218
func GetURI(url string, f func(url string, i []byte) error) error {
23-
if strings.HasPrefix(url, githubURI) {
24-
parts := strings.Split(url, ":")
25-
repoParts := strings.Split(parts[1], "@")
26-
branch := "main"
27-
28-
if len(repoParts) > 1 {
29-
branch = repoParts[1]
30-
}
31-
32-
repoPath := strings.Split(repoParts[0], "/")
33-
org := repoPath[0]
34-
project := repoPath[1]
35-
projectPath := strings.Join(repoPath[2:], "/")
36-
37-
url = fmt.Sprintf("https://raw.githubusercontent.com/%s/%s/%s/%s", org, project, branch, projectPath)
38-
}
19+
url = ConvertURL(url)
3920

4021
if strings.HasPrefix(url, "file://") {
4122
rawURL := strings.TrimPrefix(url, "file://")
@@ -73,14 +54,53 @@ func GetURI(url string, f func(url string, i []byte) error) error {
7354

7455
const (
7556
HuggingFacePrefix = "huggingface://"
57+
HTTPPrefix = "http://"
58+
HTTPSPrefix = "https://"
59+
GithubURI = "github:"
60+
GithubURI2 = "github://"
7661
)
7762

7863
func LooksLikeURL(s string) bool {
79-
return strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://") || strings.HasPrefix(s, HuggingFacePrefix)
64+
return strings.HasPrefix(s, HTTPPrefix) ||
65+
strings.HasPrefix(s, HTTPSPrefix) ||
66+
strings.HasPrefix(s, HuggingFacePrefix) ||
67+
strings.HasPrefix(s, GithubURI) ||
68+
strings.HasPrefix(s, GithubURI2)
8069
}
8170

8271
func ConvertURL(s string) string {
8372
switch {
73+
case strings.HasPrefix(s, GithubURI2):
74+
repository := strings.Replace(s, GithubURI2, "", 1)
75+
76+
repoParts := strings.Split(repository, "@")
77+
branch := "main"
78+
79+
if len(repoParts) > 1 {
80+
branch = repoParts[1]
81+
}
82+
83+
repoPath := strings.Split(repoParts[0], "/")
84+
org := repoPath[0]
85+
project := repoPath[1]
86+
projectPath := strings.Join(repoPath[2:], "/")
87+
88+
return fmt.Sprintf("https://raw.githubusercontent.com/%s/%s/%s/%s", org, project, branch, projectPath)
89+
case strings.HasPrefix(s, GithubURI):
90+
parts := strings.Split(s, ":")
91+
repoParts := strings.Split(parts[1], "@")
92+
branch := "main"
93+
94+
if len(repoParts) > 1 {
95+
branch = repoParts[1]
96+
}
97+
98+
repoPath := strings.Split(repoParts[0], "/")
99+
org := repoPath[0]
100+
project := repoPath[1]
101+
projectPath := strings.Join(repoPath[2:], "/")
102+
103+
return fmt.Sprintf("https://raw.githubusercontent.com/%s/%s/%s/%s", org, project, branch, projectPath)
84104
case strings.HasPrefix(s, HuggingFacePrefix):
85105
repository := strings.Replace(s, HuggingFacePrefix, "", 1)
86106
// convert repository to a full URL.

0 commit comments

Comments
 (0)