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

add --upgrade-all flag to refresh any stale models #2179

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft

Conversation

pdevine
Copy link
Contributor

@pdevine pdevine commented Jan 24, 2024

This change allows you to run ollama pull --upgrade-all which will check each of your local models and upgrade any that are out of date. It uses Etags to check if there is a newer manifest, and then pulls that model if it has been updated.

@pdevine pdevine mentioned this pull request Jan 24, 2024
cmd/cmd.go Outdated Show resolved Hide resolved
Username string `json:"username"`
Password string `json:"password"`
Stream *bool `json:"stream,omitempty"`
CurrentDigest string `json:"current_digest,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the advantage of the client sending the current digest versus just re-pulling all the models by name?

Copy link
Contributor Author

@pdevine pdevine Jan 25, 2024

Choose a reason for hiding this comment

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

The digest is needed for the Etag which allows it to skip pulling things unnecessarily. This is what is passed with the "If-None-Match" header.

Copy link

@puffo puffo left a comment

Choose a reason for hiding this comment

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

Looks good!

@@ -357,6 +357,42 @@ func CopyHandler(cmd *cobra.Command, args []string) error {
}

func PullHandler(cmd *cobra.Command, args []string) error {
upgradeAll, err := cmd.Flags().GetBool("upgrade-all")
Copy link

Choose a reason for hiding this comment

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

I know I'm going to fall into the same trap of getting confused between upgrade and update and will end up retyping this 😆

This is a minor non-blocking gripe, but what do you think about something along these lines?

ollama pull --sync-installed
ollama pull --fetch-all
ollama pull --latest

Trying to avoid the above confusion while also not introducing confusion about whether "all" refers to remote or local.

@@ -1105,17 +1115,27 @@ func PullModel(ctx context.Context, name string, regOpts *RegistryOptions, fn fu
return nil
}

func pullModelManifest(ctx context.Context, mp ModelPath, regOpts *RegistryOptions) (*ManifestV2, error) {
func pullModelManifest(ctx context.Context, mp ModelPath, currentDigest string, regOpts *RegistryOptions) (*ManifestV2, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This isn't how I imagined this working. It shouldn't be necessary to pass in the digest. If the manifest exists, it can calculate the digest and set If-None-Match. It will allow any pull request, not just an explicit upgrade, to reuse this logic

if err != nil {
return fmt.Errorf("pull model manifest: %s", err)
}

if currentDigest != "" {
if manifest == nil {
// we already have the model
Copy link
Contributor

Choose a reason for hiding this comment

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

instead of silently returning, this should send something like model update to date

resp, err := makeRequestWithRetry(ctx, http.MethodGet, requestURL, headers, nil, regOpts)
if err != nil {
return nil, err
}
defer resp.Body.Close()

// todo we can potentially read the manifest locally and return it here
if resp.StatusCode == http.StatusNotModified {
return nil, nil
Copy link
Contributor

Choose a reason for hiding this comment

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

returning nil, nil can be error prone. instead it should either return the current manifest, nil, or nil, os.ErrExist. either one can be explicitly handled.

returning the current manifest allows no changes in the upstream code which will print the progress as if it's pulling.

returning an error allows nicer handling for no updates

manifest, err := pullModelManifest()
if errors.Is(err, os.ErrExist) {
  fn(api.ProgressResponse{Status: "model up-to-date"})
  return nil
} else if err != nil {
  return err
}

@ThatOneCalculator
Copy link

Are updates run synchronously or asynchronously? I've found that updating models in parallel is doable on a reasonably strong connection.

@pdevine
Copy link
Contributor Author

pdevine commented Jan 27, 2024

@ThatOneCalculator each model is pulled synchronously, however, the "chunks" of that model are actually pulled asynchronously, which is how we get fast pull times.

@ThatOneCalculator
Copy link

Ah. Would you consider adding multiple downloads at once? That might help speed up things even more.

@pdevine
Copy link
Contributor Author

pdevine commented Jan 27, 2024

@ThatOneCalculator Given how we're already pulling things, I'm not sure that would help a lot. What speeds are you seeing when pulling right now?

@lainedfles
Copy link
Contributor

This would be an immensely useful feature but even better with functionality inside api/client.go maybe something like /api/upgrade/model and /api/upgrade/all?

@KhimairaCrypto
Copy link

What is blocking this PR from getting merged?

@pdevine
Copy link
Contributor Author

pdevine commented Apr 16, 2024

@KhimairaCrypto the etags implementation. It needs to be revamped.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants