mirror of
https://github.com/ollama/ollama.git
synced 2026-01-02 04:29:51 -05:00
Compare commits
5 Commits
implement-
...
mxyng/iter
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d896e53c08 | ||
|
|
9bee2450e9 | ||
|
|
929542140f | ||
|
|
a3df958032 | ||
|
|
ec8a9f11ae |
@@ -294,25 +294,6 @@ func (c *Client) Chat(ctx context.Context, req *ChatRequest, fn ChatResponseFunc
|
||||
})
|
||||
}
|
||||
|
||||
// PullProgressFunc is a function that [Client.Pull] invokes every time there
|
||||
// is progress with a "pull" request sent to the service. If this function
|
||||
// returns an error, [Client.Pull] will stop the process and return this error.
|
||||
type PullProgressFunc func(ProgressResponse) error
|
||||
|
||||
// Pull downloads a model from the ollama library. fn is called each time
|
||||
// progress is made on the request and can be used to display a progress bar,
|
||||
// etc.
|
||||
func (c *Client) Pull(ctx context.Context, req *PullRequest, fn PullProgressFunc) error {
|
||||
return c.stream(ctx, http.MethodPost, "/api/pull", req, func(bts []byte) error {
|
||||
var resp ProgressResponse
|
||||
if err := json.Unmarshal(bts, &resp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return fn(resp)
|
||||
})
|
||||
}
|
||||
|
||||
// PushProgressFunc is a function that [Client.Push] invokes when progress is
|
||||
// made.
|
||||
// It's similar to other progress function types like [PullProgressFunc].
|
||||
@@ -352,15 +333,6 @@ func (c *Client) Create(ctx context.Context, req *CreateRequest, fn CreateProgre
|
||||
})
|
||||
}
|
||||
|
||||
// List lists models that are available locally.
|
||||
func (c *Client) List(ctx context.Context) (*ListResponse, error) {
|
||||
var lr ListResponse
|
||||
if err := c.do(ctx, http.MethodGet, "/api/tags", nil, &lr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lr, nil
|
||||
}
|
||||
|
||||
// ListRunning lists running models.
|
||||
func (c *Client) ListRunning(ctx context.Context) (*ProcessResponse, error) {
|
||||
var lr ProcessResponse
|
||||
@@ -379,14 +351,6 @@ func (c *Client) Copy(ctx context.Context, req *CopyRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete deletes a model and its data.
|
||||
func (c *Client) Delete(ctx context.Context, req *DeleteRequest) error {
|
||||
if err := c.do(ctx, http.MethodDelete, "/api/delete", req, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Show obtains model information, including details, modelfile, license etc.
|
||||
func (c *Client) Show(ctx context.Context, req *ShowRequest) (*ShowResponse, error) {
|
||||
var resp ShowResponse
|
||||
@@ -429,19 +393,6 @@ func (c *Client) CreateBlob(ctx context.Context, digest string, r io.Reader) err
|
||||
return c.do(ctx, http.MethodPost, fmt.Sprintf("/api/blobs/%s", digest), r, nil)
|
||||
}
|
||||
|
||||
// Version returns the Ollama server version as a string.
|
||||
func (c *Client) Version(ctx context.Context) (string, error) {
|
||||
var version struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
if err := c.do(ctx, http.MethodGet, "/api/version", nil, &version); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return version.Version, nil
|
||||
}
|
||||
|
||||
// Signout will signout a client for a local ollama server.
|
||||
func (c *Client) Signout(ctx context.Context) error {
|
||||
return c.do(ctx, http.MethodPost, "/api/signout", nil, nil)
|
||||
|
||||
188
client/client.go
Normal file
188
client/client.go
Normal file
@@ -0,0 +1,188 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"iter"
|
||||
"maps"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"runtime"
|
||||
|
||||
"github.com/ollama/ollama/api"
|
||||
"github.com/ollama/ollama/envconfig"
|
||||
"github.com/ollama/ollama/version"
|
||||
)
|
||||
|
||||
type Error struct {
|
||||
Status string
|
||||
StatusCode int
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
return e.Status
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
baseURL *url.URL
|
||||
header http.Header
|
||||
}
|
||||
|
||||
// WithBaseURL sets the base URL. It panics if it is invalid.
|
||||
func WithBaseURL(s string) func(*Client) {
|
||||
return func(c *Client) {
|
||||
parsed, err := url.Parse(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c.baseURL = parsed
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeader sets custom HTTP headers.
|
||||
func WithHeader(h http.Header) func(*Client) {
|
||||
return func(c *Client) {
|
||||
c.header = h
|
||||
}
|
||||
}
|
||||
|
||||
func New(opts ...func(*Client) error) *Client {
|
||||
c := Client{
|
||||
baseURL: envconfig.Host(),
|
||||
header: http.Header{
|
||||
"Content-Type": {"application/json"},
|
||||
"User-Agent": {userAgent},
|
||||
},
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(&c)
|
||||
}
|
||||
return &c
|
||||
}
|
||||
|
||||
func (c *Client) Ping(ctx context.Context) error {
|
||||
_, err := do[struct{}](c, ctx, http.MethodHead, "/", nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) Chat(ctx context.Context, r api.ChatRequest) (iter.Seq2[api.ChatResponse, error], error) {
|
||||
return doSeq[api.ChatResponse](c, ctx, http.MethodPost, "/api/chat", r)
|
||||
}
|
||||
|
||||
// Pull downloads a model from a remote repository to the Ollama server.
|
||||
func (c *Client) Pull(ctx context.Context, r api.PullRequest) (iter.Seq2[api.ProgressResponse, error], error) {
|
||||
return doSeq[api.ProgressResponse](c, ctx, http.MethodPost, "/api/pull", r)
|
||||
}
|
||||
|
||||
// Push uploads a model from the Ollama server to a remote repository.
|
||||
func (c *Client) Push(ctx context.Context, r api.PushRequest) (iter.Seq2[api.ProgressResponse, error], error) {
|
||||
return doSeq[api.ProgressResponse](c, ctx, http.MethodPost, "/api/push", r)
|
||||
}
|
||||
|
||||
// Create builds a new model on the Ollama server.
|
||||
func (c *Client) Create(ctx context.Context, r api.CreateRequest) (iter.Seq2[api.ProgressResponse, error], error) {
|
||||
return doSeq[api.ProgressResponse](c, ctx, http.MethodPost, "/api/create", r)
|
||||
}
|
||||
|
||||
// List returns the list of models from the Ollama server.
|
||||
func (c *Client) List(ctx context.Context) (api.ListResponse, error) {
|
||||
return do[api.ListResponse](c, ctx, http.MethodGet, "/api/tags", nil)
|
||||
}
|
||||
|
||||
// Delete removes a model from the Ollama server.
|
||||
func (c *Client) Delete(ctx context.Context, r api.DeleteRequest) error {
|
||||
_, err := do[struct{}](c, ctx, http.MethodDelete, "/api/delete", r)
|
||||
return err
|
||||
}
|
||||
|
||||
// Version returns the Ollama server version.
|
||||
func (c *Client) Version(ctx context.Context) (string, error) {
|
||||
version, err := do[struct {
|
||||
Version string `json:"version"`
|
||||
}](c, ctx, "GET", "/api/version", nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return version.Version, nil
|
||||
}
|
||||
|
||||
var userAgent = fmt.Sprintf("ollama/%s (%s %s) Go/%s", version.Version, runtime.GOARCH, runtime.GOOS, runtime.Version())
|
||||
|
||||
// do sends the specified HTTP request and returns the raw HTTP response. header are merged with the client's default headers.
|
||||
func (c *Client) do(ctx context.Context, method, path string, body any, header http.Header) (*http.Response, error) {
|
||||
var b bytes.Buffer
|
||||
if err := json.NewEncoder(&b).Encode(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r, err := http.NewRequestWithContext(ctx, method, c.baseURL.JoinPath(path).String(), &b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// copy headers into the request in order. later headers override earlier ones.
|
||||
for _, header := range []http.Header{c.header, header} {
|
||||
maps.Copy(r.Header, header)
|
||||
}
|
||||
|
||||
w, err := http.DefaultClient.Do(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if w.StatusCode >= 400 {
|
||||
return nil, Error{
|
||||
Status: w.Status,
|
||||
StatusCode: w.StatusCode,
|
||||
}
|
||||
}
|
||||
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// do sends the specified HTTP request and returns the JSON response as type T
|
||||
func do[T any](c *Client, ctx context.Context, method, path string, body any) (t T, err error) {
|
||||
w, err := c.do(ctx, method, path, body, http.Header{"Accept": {"application/json"}})
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
defer w.Body.Close()
|
||||
|
||||
if w.ContentLength > 0 && method != http.MethodHead {
|
||||
if err := json.NewDecoder(w.Body).Decode(&t); err != nil {
|
||||
return t, err
|
||||
}
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// doSeq sends the specified HTTP request and returns an iterator that yields the JSON response chunks as type T
|
||||
func doSeq[T any](c *Client, ctx context.Context, method, path string, body any) (iter.Seq2[T, error], error) {
|
||||
w, err := c.do(ctx, method, path, body, http.Header{"Accept": {"application/jsonl", "application/x-ndjson"}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return func(yield func(T, error) bool) {
|
||||
defer w.Body.Close()
|
||||
|
||||
bts := make([]byte, 0, 512<<10)
|
||||
s := bufio.NewScanner(w.Body)
|
||||
s.Buffer(bts, len(bts))
|
||||
for s.Scan() {
|
||||
var t T
|
||||
if err := json.Unmarshal(s.Bytes(), &t); err != nil {
|
||||
yield(t, err)
|
||||
break
|
||||
}
|
||||
|
||||
if !yield(t, nil) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
194
cmd/cmd.go
194
cmd/cmd.go
@@ -10,6 +10,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"iter"
|
||||
"log"
|
||||
"math"
|
||||
"net"
|
||||
@@ -35,6 +36,7 @@ import (
|
||||
"golang.org/x/term"
|
||||
|
||||
"github.com/ollama/ollama/api"
|
||||
"github.com/ollama/ollama/client"
|
||||
"github.com/ollama/ollama/envconfig"
|
||||
"github.com/ollama/ollama/format"
|
||||
"github.com/ollama/ollama/parser"
|
||||
@@ -412,7 +414,7 @@ func RunHandler(cmd *cobra.Command, args []string) error {
|
||||
info, err := client.Show(cmd.Context(), showReq)
|
||||
var se api.StatusError
|
||||
if errors.As(err, &se) && se.StatusCode == http.StatusNotFound {
|
||||
if err := PullHandler(cmd, []string{name}); err != nil {
|
||||
if err := pullHandler(cmd, []string{name}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client.Show(cmd.Context(), &api.ShowRequest{Name: name})
|
||||
@@ -619,46 +621,6 @@ func PushHandler(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ListHandler(cmd *cobra.Command, args []string) error {
|
||||
client, err := api.ClientFromEnvironment()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
models, err := client.List(cmd.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var data [][]string
|
||||
|
||||
for _, m := range models.Models {
|
||||
if len(args) == 0 || strings.HasPrefix(strings.ToLower(m.Name), strings.ToLower(args[0])) {
|
||||
var size string
|
||||
if m.RemoteModel != "" {
|
||||
size = "-"
|
||||
} else {
|
||||
size = format.HumanBytes(m.Size)
|
||||
}
|
||||
|
||||
data = append(data, []string{m.Name, m.Digest[:12], size, format.HumanTime(m.ModifiedAt, "Never")})
|
||||
}
|
||||
}
|
||||
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"NAME", "ID", "SIZE", "MODIFIED"})
|
||||
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetHeaderLine(false)
|
||||
table.SetBorder(false)
|
||||
table.SetNoWhiteSpace(true)
|
||||
table.SetTablePadding(" ")
|
||||
table.AppendBulk(data)
|
||||
table.Render()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ListRunningHandler(cmd *cobra.Command, args []string) error {
|
||||
client, err := api.ClientFromEnvironment()
|
||||
if err != nil {
|
||||
@@ -714,33 +676,6 @@ func ListRunningHandler(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteHandler(cmd *cobra.Command, args []string) error {
|
||||
client, err := api.ClientFromEnvironment()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Unload the model if it's running before deletion
|
||||
opts := &runOptions{
|
||||
Model: args[0],
|
||||
KeepAlive: &api.Duration{Duration: 0},
|
||||
}
|
||||
if err := loadOrUnloadModel(cmd, opts); err != nil {
|
||||
if !strings.Contains(strings.ToLower(err.Error()), "not found") {
|
||||
fmt.Fprintf(os.Stderr, "Warning: unable to stop model '%s'\n", args[0])
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range args {
|
||||
req := api.DeleteRequest{Name: name}
|
||||
if err := client.Delete(cmd.Context(), &req); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("deleted '%s'\n", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ShowHandler(cmd *cobra.Command, args []string) error {
|
||||
client, err := api.ClientFromEnvironment()
|
||||
if err != nil {
|
||||
@@ -1024,79 +959,38 @@ func CopyHandler(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func PullHandler(cmd *cobra.Command, args []string) error {
|
||||
insecure, err := cmd.Flags().GetBool("insecure")
|
||||
func must[T any](v T, err error) T {
|
||||
if err != nil {
|
||||
return err
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
client, err := api.ClientFromEnvironment()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p := progress.NewProgress(os.Stderr)
|
||||
defer p.Stop()
|
||||
|
||||
bars := make(map[string]*progress.Bar)
|
||||
|
||||
func progressHandler(p *progress.Progress, w iter.Seq2[api.ProgressResponse, error]) error {
|
||||
var status string
|
||||
var spinner *progress.Spinner
|
||||
|
||||
fn := func(resp api.ProgressResponse) error {
|
||||
if resp.Digest != "" {
|
||||
if resp.Completed == 0 {
|
||||
// This is the initial status update for the
|
||||
// layer, which the server sends before
|
||||
// beginning the download, for clients to
|
||||
// compute total size and prepare for
|
||||
// downloads, if needed.
|
||||
//
|
||||
// Skipping this here to avoid showing a 0%
|
||||
// progress bar, which *should* clue the user
|
||||
// into the fact that many things are being
|
||||
// downloaded and that the current active
|
||||
// download is not that last. However, in rare
|
||||
// cases it seems to be triggering to some, and
|
||||
// it isn't worth explaining, so just ignore
|
||||
// and regress to the old UI that keeps giving
|
||||
// you the "But wait, there is more!" after
|
||||
// each "100% done" bar, which is "better."
|
||||
return nil
|
||||
var state progress.State
|
||||
for c := range w {
|
||||
if c.Status != status {
|
||||
if s, ok := state.(*progress.Spinner); ok {
|
||||
s.Stop()
|
||||
}
|
||||
|
||||
if spinner != nil {
|
||||
spinner.Stop()
|
||||
status = c.Status
|
||||
if c.Digest != "" {
|
||||
state = progress.NewBar(status, c.Total, c.Completed)
|
||||
} else {
|
||||
state = progress.NewSpinner(status)
|
||||
}
|
||||
|
||||
bar, ok := bars[resp.Digest]
|
||||
if !ok {
|
||||
name, isDigest := strings.CutPrefix(resp.Digest, "sha256:")
|
||||
name = strings.TrimSpace(name)
|
||||
if isDigest {
|
||||
name = name[:min(12, len(name))]
|
||||
}
|
||||
bar = progress.NewBar(fmt.Sprintf("pulling %s:", name), resp.Total, resp.Completed)
|
||||
bars[resp.Digest] = bar
|
||||
p.Add(resp.Digest, bar)
|
||||
}
|
||||
|
||||
bar.Set(resp.Completed)
|
||||
} else if status != resp.Status {
|
||||
if spinner != nil {
|
||||
spinner.Stop()
|
||||
}
|
||||
|
||||
status = resp.Status
|
||||
spinner = progress.NewSpinner(status)
|
||||
p.Add(status, spinner)
|
||||
p.Add(status, state)
|
||||
}
|
||||
|
||||
return nil
|
||||
if b, ok := state.(*progress.Bar); ok {
|
||||
b.Set(c.Completed)
|
||||
}
|
||||
}
|
||||
|
||||
request := api.PullRequest{Name: args[0], Insecure: insecure}
|
||||
return client.Pull(cmd.Context(), &request, fn)
|
||||
return nil
|
||||
}
|
||||
|
||||
type generateContextKey string
|
||||
@@ -1575,12 +1469,7 @@ func checkServerHeartbeat(cmd *cobra.Command, _ []string) error {
|
||||
}
|
||||
|
||||
func versionHandler(cmd *cobra.Command, _ []string) {
|
||||
client, err := api.ClientFromEnvironment()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
serverVersion, err := client.Version(cmd.Context())
|
||||
serverVersion, err := client.New().Version(cmd.Context())
|
||||
if err != nil {
|
||||
fmt.Println("Warning: could not connect to a running Ollama instance")
|
||||
}
|
||||
@@ -1696,16 +1585,6 @@ func NewCLI() *cobra.Command {
|
||||
RunE: RunServer,
|
||||
}
|
||||
|
||||
pullCmd := &cobra.Command{
|
||||
Use: "pull MODEL",
|
||||
Short: "Pull a model from a registry",
|
||||
Args: cobra.ExactArgs(1),
|
||||
PreRunE: checkServerHeartbeat,
|
||||
RunE: PullHandler,
|
||||
}
|
||||
|
||||
pullCmd.Flags().Bool("insecure", false, "Use an insecure registry")
|
||||
|
||||
pushCmd := &cobra.Command{
|
||||
Use: "push MODEL",
|
||||
Short: "Push a model to a registry",
|
||||
@@ -1732,14 +1611,6 @@ func NewCLI() *cobra.Command {
|
||||
RunE: SignoutHandler,
|
||||
}
|
||||
|
||||
listCmd := &cobra.Command{
|
||||
Use: "list",
|
||||
Aliases: []string{"ls"},
|
||||
Short: "List models",
|
||||
PreRunE: checkServerHeartbeat,
|
||||
RunE: ListHandler,
|
||||
}
|
||||
|
||||
psCmd := &cobra.Command{
|
||||
Use: "ps",
|
||||
Short: "List running models",
|
||||
@@ -1754,14 +1625,6 @@ func NewCLI() *cobra.Command {
|
||||
RunE: CopyHandler,
|
||||
}
|
||||
|
||||
deleteCmd := &cobra.Command{
|
||||
Use: "rm MODEL [MODEL...]",
|
||||
Short: "Remove a model",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
PreRunE: checkServerHeartbeat,
|
||||
RunE: DeleteHandler,
|
||||
}
|
||||
|
||||
runnerCmd := &cobra.Command{
|
||||
Use: "runner",
|
||||
Hidden: true,
|
||||
@@ -1783,12 +1646,9 @@ func NewCLI() *cobra.Command {
|
||||
showCmd,
|
||||
runCmd,
|
||||
stopCmd,
|
||||
pullCmd,
|
||||
pushCmd,
|
||||
listCmd,
|
||||
psCmd,
|
||||
copyCmd,
|
||||
deleteCmd,
|
||||
serveCmd,
|
||||
} {
|
||||
switch cmd {
|
||||
@@ -1824,14 +1684,14 @@ func NewCLI() *cobra.Command {
|
||||
showCmd,
|
||||
runCmd,
|
||||
stopCmd,
|
||||
pullCmd,
|
||||
cmdPull(),
|
||||
pushCmd,
|
||||
signinCmd,
|
||||
signoutCmd,
|
||||
listCmd,
|
||||
cmdList(),
|
||||
psCmd,
|
||||
copyCmd,
|
||||
deleteCmd,
|
||||
cmdRemove(),
|
||||
runnerCmd,
|
||||
)
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ func generateInteractive(cmd *cobra.Command, opts runOptions) error {
|
||||
continue
|
||||
case strings.HasPrefix(line, "/list"):
|
||||
args := strings.Fields(line)
|
||||
if err := ListHandler(cmd, args[1:]); err != nil {
|
||||
if err := listHandler(cmd, args[1:]); err != nil {
|
||||
return err
|
||||
}
|
||||
case strings.HasPrefix(line, "/load"):
|
||||
|
||||
58
cmd/list.go
Normal file
58
cmd/list.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/ollama/ollama/client"
|
||||
"github.com/ollama/ollama/format"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func cmdList() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "list [pattern]",
|
||||
Aliases: []string{"ls"},
|
||||
Short: "List available models in the local repository",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
PreRunE: checkServerHeartbeat,
|
||||
RunE: listHandler,
|
||||
}
|
||||
}
|
||||
|
||||
func listHandler(cmd *cobra.Command, args []string) error {
|
||||
c := client.New()
|
||||
w, err := c.List(cmd.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"NAME", "ID", "SIZE", "MODIFIED"})
|
||||
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetHeaderLine(false)
|
||||
table.SetBorder(false)
|
||||
table.SetNoWhiteSpace(true)
|
||||
table.SetTablePadding(" ")
|
||||
|
||||
for _, m := range w.Models {
|
||||
if len(args) == 0 || strings.HasPrefix(strings.ToLower(m.Name), strings.ToLower(args[0])) {
|
||||
size := format.HumanBytes(m.Size)
|
||||
if m.RemoteModel != "" {
|
||||
size = "-"
|
||||
}
|
||||
table.Append([]string{
|
||||
m.Model,
|
||||
m.Digest[:12],
|
||||
size,
|
||||
format.HumanTime(m.ModifiedAt, "Never"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
table.Render()
|
||||
|
||||
return nil
|
||||
}
|
||||
37
cmd/pull.go
Normal file
37
cmd/pull.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/ollama/ollama/api"
|
||||
"github.com/ollama/ollama/client"
|
||||
"github.com/ollama/ollama/progress"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func cmdPull() *cobra.Command {
|
||||
cmd := cobra.Command{
|
||||
Use: "pull [model]",
|
||||
Short: "Pull a model from a remote repository",
|
||||
Args: cobra.ExactArgs(1),
|
||||
PreRunE: checkServerHeartbeat,
|
||||
RunE: pullHandler,
|
||||
}
|
||||
cmd.Flags().Bool("insecure", false, "Allow insecure server connections when pulling models")
|
||||
return &cmd
|
||||
}
|
||||
|
||||
func pullHandler(cmd *cobra.Command, args []string) error {
|
||||
c := client.New()
|
||||
w, err := c.Pull(cmd.Context(), api.PullRequest{
|
||||
Name: args[0],
|
||||
Insecure: must(cmd.Flags().GetBool("insecure")),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p := progress.NewProgress(os.Stderr)
|
||||
defer p.Stop()
|
||||
return progressHandler(p, w)
|
||||
}
|
||||
32
cmd/remove.go
Normal file
32
cmd/remove.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ollama/ollama/api"
|
||||
"github.com/ollama/ollama/client"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func cmdRemove() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "remove [model]...",
|
||||
Aliases: []string{"rm"},
|
||||
Short: "Remove one or more models from the local repository",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
PreRunE: checkServerHeartbeat,
|
||||
RunE: removeHandler,
|
||||
}
|
||||
}
|
||||
|
||||
func removeHandler(cmd *cobra.Command, args []string) error {
|
||||
c := client.New()
|
||||
for _, arg := range args {
|
||||
// TODO: stop model if it's running; skip if model is cloud
|
||||
if err := c.Delete(cmd.Context(), api.DeleteRequest{Model: arg}); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("deleted", arg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user