ollama

package
v0.0.0-...-0071670 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 25, 2025 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package ollama implements a client for the Ollama API.

It is described at https://github.com/ollama/ollama/blob/main/docs/api.md and https://pkg.go.dev/github.com/ollama/ollama/api

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ChatRequest

type ChatRequest struct {
	Model    string             `json:"model"`
	Stream   bool               `json:"stream"`
	Messages []Message          `json:"messages"`
	Tools    []Tool             `json:"tools,omitzero"`
	Format   *jsonschema.Schema `json:"format,omitzero"`
	// https://github.com/ollama/ollama/blob/main/docs/modelfile.md#valid-parameters-and-values
	// https://pkg.go.dev/github.com/ollama/ollama/api#Options
	// https://pkg.go.dev/github.com/ollama/ollama/api#Runner
	Options struct {
		Mirostat      int64    `json:"mirostat,omitzero"` // [0, 1, 2]
		MirostatEta   float64  `json:"mirostat_eta,omitzero"`
		MirostatTau   float64  `json:"mirostat_tau,omitzero"`
		NumCtx        int64    `json:"num_ctx,omitzero"`        // Context Window, default 2048
		RepeatLastN   int64    `json:"repeat_last_n,omitzero"`  // Lookback for repeated tokens, default 64
		RepeatPenalty float64  `json:"repeat_penalty,omitzero"` // default 1.1
		Temperature   float64  `json:"temperature,omitzero"`    // default 0.8
		Seed          int64    `json:"seed,omitzero"`
		Stop          []string `json:"stop,omitzero"`        // keywords to stop completion
		NumPredict    int64    `json:"num_predict,omitzero"` // Max tokens
		TopK          int64    `json:"top_k,omitzero"`       // Default: 40
		TopP          float64  `json:"top_p,omitzero"`       // Default: 0.9
		MinP          float64  `json:"min_p,omitzero"`       // Default: 0.0
	} `json:"options,omitzero"`
	KeepAlive string `json:"keep_alive,omitzero"` // Default "5m"
}

https://github.com/ollama/ollama/blob/main/docs/api.md#generate-a-chat-completion https://pkg.go.dev/github.com/ollama/ollama/api#ChatRequest

func (*ChatRequest) Init

func (c *ChatRequest) Init(msgs genai.Messages, opts genai.Validatable) error

Init initializes the provider specific completion request with the generic completion request.

type ChatResponse

type ChatResponse struct {
	Model      string    `json:"model"`
	CreatedAt  time.Time `json:"created_at"`
	Message    Message   `json:"message"`
	DoneReason string    `json:"done_reason"`
	Done       bool      `json:"done"`

	// 	https://pkg.go.dev/github.com/ollama/ollama/api#Metrics
	TotalDuration      time.Duration `json:"total_duration"`
	LoadDuration       time.Duration `json:"load_duration"`
	PromptEvalCount    int64         `json:"prompt_eval_count"`
	PromptEvalDuration time.Duration `json:"prompt_eval_duration"`
	EvalCount          int64         `json:"eval_count"`
	EvalDuration       time.Duration `json:"eval_duration"`
}

https://github.com/ollama/ollama/blob/main/docs/api.md#response-10 https://pkg.go.dev/github.com/ollama/ollama/api#ChatResponse

func (*ChatResponse) ToResult

func (c *ChatResponse) ToResult() (genai.ChatResult, error)

type ChatStreamChunkResponse

type ChatStreamChunkResponse ChatResponse

type Client

type Client struct {
	// Client is exported for testing replay purposes.
	Client httpjson.Client
	// contains filtered or unexported fields
}

Client implements the REST JSON based API.

func New

func New(baseURL, model string) (*Client, error)

New creates a new client to talk to the Ollama API.

To use multiple models, create multiple clients. Use one of the model from https://ollama.com/library

func (*Client) Chat

func (c *Client) Chat(ctx context.Context, msgs genai.Messages, opts genai.Validatable) (genai.ChatResult, error)
Example (Tool_use)
package main

import (
	"context"
	"fmt"
	"log"
	"net"
	"os"
	"path/filepath"

	_ "embed"
	"github.com/maruel/genai"
	"github.com/maruel/genai/ollama"
	"github.com/maruel/genai/ollama/ollamasrv"
)

// Ollama build to use.
const version = "v0.6.2"

func main() {
	// Download and start the server.
	ctx := context.Background()
	srv, err := startServer(ctx)
	if err != nil {
		log.Print(err)
		return
	}
	defer srv.Close()
	// Connect the client.
	c, err := ollama.New(srv.URL(), "llama3.1:8b")
	if err != nil {
		log.Print(err)
		return
	}
	msgs := genai.Messages{
		genai.NewTextMessage(genai.User, "I wonder if Canada is a better country than the US? Call the tool best_country to tell me which country is the best one."),
	}
	var got struct {
		Country string `json:"country" jsonschema:"enum=Canada,enum=USA"`
	}
	opts := genai.ChatOptions{
		Seed:        1,
		Temperature: 0.01,
		MaxTokens:   50,
		Tools: []genai.ToolDef{
			{
				Name:        "best_country",
				Description: "A tool to determine the best country",
				InputsAs:    &got,
			},
		},
	}
	resp, err := c.Chat(context.Background(), msgs, &opts)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Raw response: %#v", resp)
	if len(resp.ToolCalls) != 1 || resp.ToolCalls[0].Name != "best_country" {
		log.Fatal("Unexpected response")
	}
	if err := resp.ToolCalls[0].Decode(&got); err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Best: %v\n", got.Country)
}

func findFreePort() int {
	l, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		panic(err)
	}
	defer l.Close()
	return l.Addr().(*net.TCPAddr).Port
}

func startServer(ctx context.Context) (*ollamasrv.Server, error) {
	cache, err := filepath.Abs("testdata/tmp")
	if err != nil {
		return nil, err
	}
	if err = os.MkdirAll(cache, 0o755); err != nil {
		return nil, err
	}

	exe, err := ollamasrv.DownloadRelease(ctx, cache, version)
	if err != nil {
		return nil, err
	}
	port := findFreePort()
	l, err := os.Create(filepath.Join(cache, "ollama.log"))
	if err != nil {
		return nil, err
	}
	defer l.Close()
	return ollamasrv.NewServer(ctx, exe, l, port)
}
Output:

Example (Vision_and_JSON)
package main

import (
	"bytes"
	"context"
	"fmt"
	"log"
	"net"
	"os"
	"path/filepath"

	_ "embed"
	"github.com/maruel/genai"
	"github.com/maruel/genai/ollama"
	"github.com/maruel/genai/ollama/ollamasrv"
)

// Ollama build to use.
const version = "v0.6.2"

// See the 3kib banana jpg online at
// https://github.com/maruel/genai/blob/main/ollama/testdata/banana.jpg
//
//go:embed testdata/banana.jpg
var bananaJpg []byte

func main() {
	// Download and start the server.
	ctx := context.Background()
	srv, err := startServer(ctx)
	if err != nil {
		log.Print(err)
		return
	}
	defer srv.Close()
	// Connect the client.
	c, err := ollama.New(srv.URL(), "gemma3:4b")
	if err != nil {
		log.Print(err)
		return
	}
	msgs := genai.Messages{
		{
			Role: genai.User,
			Contents: []genai.Content{
				{Text: "Is it a banana? Reply as JSON."},
				{Filename: "banana.jpg", Document: bytes.NewReader(bananaJpg)},
			},
		},
	}
	var got struct {
		Banana bool `json:"banana"`
	}
	opts := genai.ChatOptions{
		Seed:        1,
		Temperature: 0.01,
		MaxTokens:   50,
		DecodeAs:    &got,
	}
	resp, err := c.Chat(ctx, msgs, &opts)
	if err != nil {
		log.Print(err)
		return
	}
	log.Printf("Raw response: %#v", resp)
	if len(resp.Contents) != 1 {
		log.Print("Unexpected response")
		return
	}
	if err := resp.Contents[0].Decode(&got); err != nil {
		log.Print(err)
		return
	}
	fmt.Printf("Banana: %v\n", got.Banana)
}

func findFreePort() int {
	l, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		panic(err)
	}
	defer l.Close()
	return l.Addr().(*net.TCPAddr).Port
}

func startServer(ctx context.Context) (*ollamasrv.Server, error) {
	cache, err := filepath.Abs("testdata/tmp")
	if err != nil {
		return nil, err
	}
	if err = os.MkdirAll(cache, 0o755); err != nil {
		return nil, err
	}

	exe, err := ollamasrv.DownloadRelease(ctx, cache, version)
	if err != nil {
		return nil, err
	}
	port := findFreePort()
	l, err := os.Create(filepath.Join(cache, "ollama.log"))
	if err != nil {
		return nil, err
	}
	defer l.Close()
	return ollamasrv.NewServer(ctx, exe, l, port)
}
Output:

func (*Client) ChatRaw

func (c *Client) ChatRaw(ctx context.Context, in *ChatRequest, out *ChatResponse) error

func (*Client) ChatStream

func (c *Client) ChatStream(ctx context.Context, msgs genai.Messages, opts genai.Validatable, chunks chan<- genai.MessageFragment) error
Example
package main

import (
	"context"
	"fmt"
	"log"
	"net"
	"os"
	"path/filepath"

	_ "embed"
	"github.com/maruel/genai"
	"github.com/maruel/genai/ollama"
	"github.com/maruel/genai/ollama/ollamasrv"
)

// Ollama build to use.
const version = "v0.6.2"

func main() {
	// Download and start the server.
	ctx := context.Background()
	srv, err := startServer(ctx)
	if err != nil {
		log.Print(err)
		return
	}
	defer srv.Close()
	// Connect the client.
	c, err := ollama.New(srv.URL(), "gemma3:1b")
	if err != nil {
		log.Print(err)
		return
	}
	msgs := genai.Messages{
		genai.NewTextMessage(genai.User, "Say hello. Use only one word."),
	}
	opts := genai.ChatOptions{
		Seed:        1,
		Temperature: 0.01,
		MaxTokens:   50,
	}
	chunks := make(chan genai.MessageFragment)
	end := make(chan genai.Message, 10)
	go func() {
		var pendingMsgs genai.Messages
		defer func() {
			for _, m := range pendingMsgs {
				end <- m
			}
			close(end)
		}()
		for {
			select {
			case <-ctx.Done():
				return
			case pkt, ok := <-chunks:
				if !ok {
					return
				}
				var err2 error
				if pendingMsgs, err2 = pkt.Accumulate(pendingMsgs); err2 != nil {
					end <- genai.NewTextMessage(genai.Assistant, fmt.Sprintf("Error: %v", err2))
					return
				}
			}
		}
	}()
	err = c.ChatStream(ctx, msgs, &opts, chunks)
	close(chunks)
	var responses genai.Messages
	for m := range end {
		responses = append(responses, m)
	}
	log.Printf("Raw responses: %#v", responses)
	if err != nil {
		log.Print(err)
		return
	}
	if len(responses) != 1 {
		log.Print("Unexpected responses")
		return
	}
	resp := responses[0]
	if len(resp.Contents) != 1 {
		log.Print("Unexpected response")
		return
	}
}

func findFreePort() int {
	l, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		panic(err)
	}
	defer l.Close()
	return l.Addr().(*net.TCPAddr).Port
}

func startServer(ctx context.Context) (*ollamasrv.Server, error) {
	cache, err := filepath.Abs("testdata/tmp")
	if err != nil {
		return nil, err
	}
	if err = os.MkdirAll(cache, 0o755); err != nil {
		return nil, err
	}

	exe, err := ollamasrv.DownloadRelease(ctx, cache, version)
	if err != nil {
		return nil, err
	}
	port := findFreePort()
	l, err := os.Create(filepath.Join(cache, "ollama.log"))
	if err != nil {
		return nil, err
	}
	defer l.Close()
	return ollamasrv.NewServer(ctx, exe, l, port)
}
Output:

func (*Client) ChatStreamRaw

func (c *Client) ChatStreamRaw(ctx context.Context, in *ChatRequest, out chan<- ChatStreamChunkResponse) error

func (*Client) ListModels

func (c *Client) ListModels(ctx context.Context) ([]genai.Model, error)
Example
package main

import (
	"context"
	"fmt"
	"log"
	"net"
	"os"
	"path/filepath"

	_ "embed"
	"github.com/maruel/genai/ollama"
	"github.com/maruel/genai/ollama/ollamasrv"
)

// Ollama build to use.
const version = "v0.6.2"

func main() {
	// Download and start the server.
	ctx := context.Background()
	srv, err := startServer(ctx)
	if err != nil {
		log.Print(err)
		return
	}
	defer srv.Close()
	// Connect the client.
	c, err := ollama.New(srv.URL(), "")
	if err != nil {
		log.Print(err)
		return
	}
	models, err := c.ListModels(ctx)
	if err != nil {
		fmt.Printf("Failed to get models: %v\n", err)
		return
	}
	for _, model := range models {
		// The list of models will change over time. Print them to stderr so the
		// test doesn't capture them.
		fmt.Fprintf(os.Stderr, "- %s\n", model)
	}
}

func findFreePort() int {
	l, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		panic(err)
	}
	defer l.Close()
	return l.Addr().(*net.TCPAddr).Port
}

func startServer(ctx context.Context) (*ollamasrv.Server, error) {
	cache, err := filepath.Abs("testdata/tmp")
	if err != nil {
		return nil, err
	}
	if err = os.MkdirAll(cache, 0o755); err != nil {
		return nil, err
	}

	exe, err := ollamasrv.DownloadRelease(ctx, cache, version)
	if err != nil {
		return nil, err
	}
	port := findFreePort()
	l, err := os.Create(filepath.Join(cache, "ollama.log"))
	if err != nil {
		return nil, err
	}
	defer l.Close()
	return ollamasrv.NewServer(ctx, exe, l, port)
}
Output:

func (*Client) PullModel

func (c *Client) PullModel(ctx context.Context, model string) error

PullModel is the equivalent of "ollama pull".

type Message

type Message struct {
	Role      string     `json:"role,omitzero"` // "system", "assistant", "user"
	Content   string     `json:"content,omitzero"`
	Images    [][]byte   `json:"images,omitzero"` // List of images as base64 encoded strings.
	ToolCalls []ToolCall `json:"tool_calls,omitzero"`
}

https://github.com/ollama/ollama/blob/main/docs/api.md#parameters-1

func (*Message) From

func (m *Message) From(in *genai.Message) error

func (*Message) To

func (m *Message) To(out *genai.Message) error

type Model

type Model struct {
	Name       string    `json:"name"`
	Model      string    `json:"model"`
	ModifiedAt time.Time `json:"modified_at"`
	Size       int64     `json:"size"`
	Digest     string    `json:"digest"`
	// https://pkg.go.dev/github.com/ollama/ollama/api#ModelDetails
	Details struct {
		ParentModel       string   `json:"parent_model"`
		Format            string   `json:"format"`
		Family            string   `json:"family"`
		Families          []string `json:"families"`
		ParameterSize     string   `json:"parameter_size"`
		QuantizationLevel string   `json:"quantization_level"`
	} `json:"details"`
}

https://pkg.go.dev/github.com/ollama/ollama/api#ListModelResponse

func (*Model) Context

func (m *Model) Context() int64

func (*Model) GetID

func (m *Model) GetID() string

func (*Model) String

func (m *Model) String() string

type Tool

type Tool struct {
	Type     string `json:"type,omitzero"` // "function"
	Function struct {
		Description string             `json:"description,omitzero"`
		Name        string             `json:"name,omitzero"`
		Parameters  *jsonschema.Schema `json:"parameters,omitzero"`
	} `json:"function,omitzero"`
}

https://github.com/ollama/ollama/blob/main/docs/api.md#chat-request-with-tools https://pkg.go.dev/github.com/ollama/ollama/api#Tool

type ToolCall

type ToolCall struct {
	Function struct {
		Name      string `json:"name"`
		Arguments any    `json:"arguments"`
	} `json:"function"`
}

https://github.com/ollama/ollama/blob/main/docs/api.md#response-16 https://pkg.go.dev/github.com/ollama/ollama/api#ToolCall

func (*ToolCall) From

func (t *ToolCall) From(in *genai.ToolCall) error

func (*ToolCall) To

func (t *ToolCall) To(out *genai.ToolCall) error

Directories

Path Synopsis
Package ollamasrv downloads and starts ollama directly from GitHub releases.
Package ollamasrv downloads and starts ollama directly from GitHub releases.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL