2023-07-17 00:02:22 +00:00
package server
import (
"bytes"
2023-07-25 21:08:51 +00:00
"context"
2023-07-17 00:02:22 +00:00
"crypto/sha256"
2023-08-29 03:50:24 +00:00
"encoding/hex"
2023-07-17 00:02:22 +00:00
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
2023-08-22 01:38:31 +00:00
"net/url"
2023-07-17 00:02:22 +00:00
"os"
"path/filepath"
2023-07-17 19:08:10 +00:00
"reflect"
2023-08-22 01:24:42 +00:00
"runtime"
2023-07-17 00:02:22 +00:00
"strconv"
"strings"
2023-09-01 22:12:20 +00:00
"text/template"
2023-07-17 00:02:22 +00:00
2023-09-02 18:38:51 +00:00
"golang.org/x/exp/slices"
2023-07-17 00:02:22 +00:00
"github.com/jmorganca/ollama/api"
2023-07-21 20:33:56 +00:00
"github.com/jmorganca/ollama/llm"
2023-07-17 00:02:22 +00:00
"github.com/jmorganca/ollama/parser"
2023-08-22 01:24:42 +00:00
"github.com/jmorganca/ollama/version"
2023-07-17 00:02:22 +00:00
)
2023-07-21 22:42:19 +00:00
type RegistryOptions struct {
Insecure bool
Username string
Password string
2023-08-10 18:34:25 +00:00
Token string
2023-07-21 22:42:19 +00:00
}
2023-07-17 00:02:22 +00:00
type Model struct {
2023-09-06 18:04:17 +00:00
Name string ` json:"name" `
ShortName string
ModelPath string
OriginalModel string
AdapterPaths [ ] string
Template string
System string
License [ ] string
Digest string
Options map [ string ] interface { }
2023-07-17 00:02:22 +00:00
}
2023-10-16 15:07:37 +00:00
func ( m * Model ) Prompt ( request api . GenerateRequest ) ( string , error ) {
2023-08-08 04:55:34 +00:00
t := m . Template
if request . Template != "" {
t = request . Template
}
tmpl , err := template . New ( "" ) . Parse ( t )
2023-07-17 21:21:27 +00:00
if err != nil {
return "" , err
}
var vars struct {
2023-07-20 06:22:19 +00:00
First bool
2023-07-17 21:21:27 +00:00
System string
Prompt string
}
2023-07-22 03:45:32 +00:00
vars . First = len ( request . Context ) == 0
2023-11-04 00:26:45 +00:00
vars . System = m . System
2023-07-17 21:21:27 +00:00
vars . Prompt = request . Prompt
2023-08-08 04:55:34 +00:00
if request . System != "" {
vars . System = request . System
}
2023-07-17 21:21:27 +00:00
var sb strings . Builder
if err := tmpl . Execute ( & sb , vars ) ; err != nil {
return "" , err
}
return sb . String ( ) , nil
}
2023-07-17 00:02:22 +00:00
type ManifestV2 struct {
SchemaVersion int ` json:"schemaVersion" `
MediaType string ` json:"mediaType" `
Config Layer ` json:"config" `
Layers [ ] * Layer ` json:"layers" `
}
type Layer struct {
MediaType string ` json:"mediaType" `
Digest string ` json:"digest" `
2023-09-28 17:00:34 +00:00
Size int64 ` json:"size" `
2023-08-14 22:07:00 +00:00
From string ` json:"from,omitempty" `
2023-07-17 00:02:22 +00:00
}
2023-07-19 00:14:12 +00:00
type LayerReader struct {
2023-07-17 00:02:22 +00:00
Layer
2023-07-19 00:14:12 +00:00
io . Reader
2023-07-17 00:02:22 +00:00
}
type ConfigV2 struct {
2023-09-12 17:01:20 +00:00
ModelFormat string ` json:"model_format" `
ModelFamily string ` json:"model_family" `
ModelType string ` json:"model_type" `
FileType string ` json:"file_type" `
RootFS RootFS ` json:"rootfs" `
2023-07-21 20:33:56 +00:00
// required by spec
2023-07-17 00:02:22 +00:00
Architecture string ` json:"architecture" `
OS string ` json:"os" `
}
type RootFS struct {
Type string ` json:"type" `
DiffIDs [ ] string ` json:"diff_ids" `
}
2023-09-28 17:00:34 +00:00
func ( m * ManifestV2 ) GetTotalSize ( ) ( total int64 ) {
2023-07-18 16:09:45 +00:00
for _ , layer := range m . Layers {
total += layer . Size
}
2023-09-28 17:00:34 +00:00
2023-07-18 16:09:45 +00:00
total += m . Config . Size
return total
}
2023-08-29 03:50:24 +00:00
func GetManifest ( mp ModelPath ) ( * ManifestV2 , string , error ) {
2023-10-27 14:19:59 +00:00
fp , err := mp . GetManifestPath ( )
2023-07-17 18:03:55 +00:00
if err != nil {
2023-08-29 03:50:24 +00:00
return nil , "" , err
2023-07-17 18:03:55 +00:00
}
2023-07-17 21:21:27 +00:00
2023-07-22 06:02:12 +00:00
if _ , err = os . Stat ( fp ) ; err != nil {
2023-08-29 03:50:24 +00:00
return nil , "" , err
2023-07-17 00:02:22 +00:00
}
var manifest * ManifestV2
2023-07-17 21:21:27 +00:00
bts , err := os . ReadFile ( fp )
2023-07-17 00:02:22 +00:00
if err != nil {
2023-08-29 03:50:24 +00:00
return nil , "" , fmt . Errorf ( "couldn't open file '%s'" , fp )
2023-07-17 00:02:22 +00:00
}
2023-08-29 03:50:24 +00:00
shaSum := sha256 . Sum256 ( bts )
shaStr := hex . EncodeToString ( shaSum [ : ] )
2023-07-17 21:21:27 +00:00
if err := json . Unmarshal ( bts , & manifest ) ; err != nil {
2023-08-29 03:50:24 +00:00
return nil , "" , err
2023-07-17 00:02:22 +00:00
}
2023-08-29 03:50:24 +00:00
return manifest , shaStr , nil
2023-07-17 00:02:22 +00:00
}
func GetModel ( name string ) ( * Model , error ) {
2023-08-22 16:39:42 +00:00
mp := ParseModelPath ( name )
2023-08-29 03:50:24 +00:00
manifest , digest , err := GetManifest ( mp )
2023-07-17 00:02:22 +00:00
if err != nil {
return nil , err
}
model := & Model {
2023-10-19 14:39:58 +00:00
Name : mp . GetFullTagname ( ) ,
ShortName : mp . GetShortTagname ( ) ,
Digest : digest ,
Template : "{{ .Prompt }}" ,
License : [ ] string { } ,
2023-07-17 00:02:22 +00:00
}
for _ , layer := range manifest . Layers {
2023-07-18 05:44:21 +00:00
filename , err := GetBlobsPath ( layer . Digest )
2023-07-17 18:03:55 +00:00
if err != nil {
return nil , err
}
2023-07-17 00:02:22 +00:00
switch layer . MediaType {
case "application/vnd.ollama.image.model" :
model . ModelPath = filename
2023-09-06 18:04:17 +00:00
model . OriginalModel = layer . From
2023-08-04 22:56:40 +00:00
case "application/vnd.ollama.image.embed" :
2023-10-16 15:07:37 +00:00
// Deprecated in versions > 0.1.2
// TODO: remove this warning in a future version
log . Print ( "WARNING: model contains embeddings, but embeddings in modelfiles have been deprecated and will be ignored." )
2023-08-04 00:16:05 +00:00
case "application/vnd.ollama.image.adapter" :
model . AdapterPaths = append ( model . AdapterPaths , filename )
2023-07-17 21:21:27 +00:00
case "application/vnd.ollama.image.template" :
bts , err := os . ReadFile ( filename )
if err != nil {
return nil , err
}
model . Template = string ( bts )
case "application/vnd.ollama.image.system" :
bts , err := os . ReadFile ( filename )
2023-07-17 00:02:22 +00:00
if err != nil {
return nil , err
}
2023-07-17 21:21:27 +00:00
model . System = string ( bts )
2023-07-20 02:43:00 +00:00
case "application/vnd.ollama.image.prompt" :
bts , err := os . ReadFile ( filename )
if err != nil {
return nil , err
}
model . Template = string ( bts )
2023-07-17 00:02:22 +00:00
case "application/vnd.ollama.image.params" :
2023-07-17 19:08:10 +00:00
params , err := os . Open ( filename )
if err != nil {
return nil , err
}
defer params . Close ( )
2023-07-17 00:02:22 +00:00
2023-07-31 19:07:04 +00:00
// parse model options parameters into a map so that we can see which fields have been specified explicitly
2023-08-01 17:36:31 +00:00
if err = json . NewDecoder ( params ) . Decode ( & model . Options ) ; err != nil {
2023-07-31 19:07:04 +00:00
return nil , err
}
2023-09-06 18:04:17 +00:00
case "application/vnd.ollama.image.license" :
bts , err := os . ReadFile ( filename )
if err != nil {
return nil , err
}
model . License = append ( model . License , string ( bts ) )
2023-07-17 00:02:22 +00:00
}
}
return model , nil
}
2023-08-04 22:56:40 +00:00
func filenameWithPath ( path , f string ) ( string , error ) {
// if filePath starts with ~/, replace it with the user's home directory.
2023-08-30 20:01:23 +00:00
if strings . HasPrefix ( f , fmt . Sprintf ( "~%s" , string ( os . PathSeparator ) ) ) {
parts := strings . Split ( f , string ( os . PathSeparator ) )
2023-08-04 22:56:40 +00:00
home , err := os . UserHomeDir ( )
if err != nil {
return "" , fmt . Errorf ( "failed to open file: %v" , err )
}
f = filepath . Join ( home , filepath . Join ( parts [ 1 : ] ... ) )
}
// if filePath is not an absolute path, make it relative to the modelfile path
if ! filepath . IsAbs ( f ) {
f = filepath . Join ( filepath . Dir ( path ) , f )
}
return f , nil
}
2023-11-14 20:30:34 +00:00
func realpath ( p string ) string {
abspath , err := filepath . Abs ( p )
if err != nil {
return p
2023-09-11 18:46:35 +00:00
}
2023-11-14 20:30:34 +00:00
home , err := os . UserHomeDir ( )
2023-07-20 04:55:15 +00:00
if err != nil {
2023-11-14 20:30:34 +00:00
return abspath
2023-07-17 00:02:22 +00:00
}
2023-11-14 20:30:34 +00:00
if p == "~" {
return home
} else if strings . HasPrefix ( p , "~/" ) {
return filepath . Join ( home , p [ 2 : ] )
2023-07-17 00:02:22 +00:00
}
2023-11-14 20:30:34 +00:00
return abspath
}
func CreateModel ( ctx context . Context , name string , commands [ ] parser . Command , fn func ( resp api . ProgressResponse ) ) error {
2023-07-21 20:33:56 +00:00
config := ConfigV2 {
OS : "linux" ,
2023-11-14 20:30:34 +00:00
Architecture : "amd64" ,
2023-07-21 20:33:56 +00:00
}
2023-11-14 20:30:34 +00:00
deleteMap := make ( map [ string ] struct { } )
2023-07-19 00:14:12 +00:00
var layers [ ] * LayerReader
2023-11-14 20:30:34 +00:00
2023-07-28 15:29:00 +00:00
params := make ( map [ string ] [ ] string )
2023-11-14 20:30:34 +00:00
fromParams := make ( map [ string ] any )
2023-07-17 00:02:22 +00:00
for _ , c := range commands {
2023-11-14 20:30:34 +00:00
log . Printf ( "[%s] - %s" , c . Name , c . Args )
mediatype := fmt . Sprintf ( "application/vnd.ollama.image.%s" , c . Name )
2023-07-17 00:02:22 +00:00
switch c . Name {
case "model" :
2023-11-15 18:59:38 +00:00
if strings . HasPrefix ( c . Args , "@" ) {
blobPath , err := GetBlobsPath ( strings . TrimPrefix ( c . Args , "@" ) )
if err != nil {
return err
}
c . Args = blobPath
}
2023-11-14 20:30:34 +00:00
bin , err := os . Open ( realpath ( c . Args ) )
2023-07-17 00:02:22 +00:00
if err != nil {
2023-11-14 20:30:34 +00:00
// not a file on disk so must be a model reference
modelpath := ParseModelPath ( c . Args )
manifest , _ , err := GetManifest ( modelpath )
switch {
case errors . Is ( err , os . ErrNotExist ) :
fn ( api . ProgressResponse { Status : "pulling model" } )
if err := PullModel ( ctx , c . Args , & RegistryOptions { } , fn ) ; err != nil {
2023-07-25 18:25:13 +00:00
return err
}
2023-11-14 20:30:34 +00:00
manifest , _ , err = GetManifest ( modelpath )
2023-07-21 20:33:56 +00:00
if err != nil {
return err
}
2023-11-14 20:30:34 +00:00
case err != nil :
return err
2023-07-17 00:02:22 +00:00
}
2023-07-21 20:33:56 +00:00
2023-10-06 20:05:32 +00:00
fn ( api . ProgressResponse { Status : "reading model metadata" } )
2023-11-14 20:30:34 +00:00
fromConfigPath , err := GetBlobsPath ( manifest . Config . Digest )
2023-08-18 04:52:11 +00:00
if err != nil {
return err
}
2023-11-14 20:30:34 +00:00
fromConfigFile , err := os . Open ( fromConfigPath )
2023-08-18 04:52:11 +00:00
if err != nil {
return err
}
2023-11-14 20:30:34 +00:00
defer fromConfigFile . Close ( )
2023-08-18 04:52:11 +00:00
2023-11-14 20:30:34 +00:00
var fromConfig ConfigV2
if err := json . NewDecoder ( fromConfigFile ) . Decode ( & fromConfig ) ; err != nil {
2023-08-18 04:52:11 +00:00
return err
}
2023-11-14 20:30:34 +00:00
config . ModelFormat = fromConfig . ModelFormat
config . ModelFamily = fromConfig . ModelFamily
config . ModelType = fromConfig . ModelType
config . FileType = fromConfig . FileType
2023-08-18 04:52:11 +00:00
2023-11-14 20:30:34 +00:00
for _ , layer := range manifest . Layers {
deleteMap [ layer . Digest ] = struct { } { }
if layer . MediaType == "application/vnd.ollama.image.params" {
fromParamsPath , err := GetBlobsPath ( layer . Digest )
2023-09-05 18:05:03 +00:00
if err != nil {
return err
}
2023-11-14 20:30:34 +00:00
fromParamsFile , err := os . Open ( fromParamsPath )
2023-09-05 18:05:03 +00:00
if err != nil {
return err
}
2023-11-14 20:30:34 +00:00
defer fromParamsFile . Close ( )
2023-09-05 18:05:03 +00:00
2023-11-14 20:30:34 +00:00
if err := json . NewDecoder ( fromParamsFile ) . Decode ( & fromParams ) ; err != nil {
2023-09-05 18:05:03 +00:00
return err
}
}
2023-11-14 20:30:34 +00:00
layer , err := GetLayerWithBufferFromLayer ( layer )
2023-07-17 00:02:22 +00:00
if err != nil {
return err
}
2023-11-14 20:30:34 +00:00
layer . From = modelpath . GetShortTagname ( )
layers = append ( layers , layer )
2023-07-17 00:02:22 +00:00
}
2023-11-14 20:30:34 +00:00
deleteMap [ manifest . Config . Digest ] = struct { } { }
continue
2023-07-17 00:02:22 +00:00
}
2023-11-14 20:30:34 +00:00
defer bin . Close ( )
2023-08-04 00:16:05 +00:00
2023-11-14 20:30:34 +00:00
fn ( api . ProgressResponse { Status : "creating model layer" } )
ggml , err := llm . DecodeGGML ( bin )
2023-08-30 20:01:23 +00:00
if err != nil {
return err
2023-08-04 00:16:05 +00:00
}
2023-11-14 20:30:34 +00:00
config . ModelFormat = ggml . Name ( )
config . ModelFamily = ggml . ModelFamily ( )
config . ModelType = ggml . ModelType ( )
config . FileType = ggml . FileType ( )
bin . Seek ( 0 , io . SeekStart )
layer , err := CreateLayer ( bin )
if err != nil {
return err
}
2023-08-04 00:16:05 +00:00
2023-11-14 20:30:34 +00:00
layer . MediaType = mediatype
layers = append ( layers , layer )
case "adapter" :
fn ( api . ProgressResponse { Status : "creating adapter layer" } )
bin , err := os . Open ( realpath ( c . Args ) )
2023-08-04 00:16:05 +00:00
if err != nil {
2023-11-14 20:30:34 +00:00
return err
2023-08-04 00:16:05 +00:00
}
2023-11-14 20:30:34 +00:00
defer bin . Close ( )
2023-08-04 00:16:05 +00:00
2023-11-14 20:30:34 +00:00
layer , err := CreateLayer ( bin )
2023-08-04 00:16:05 +00:00
if err != nil {
2023-11-14 20:30:34 +00:00
return err
2023-08-04 00:16:05 +00:00
}
2023-08-08 20:56:48 +00:00
2023-11-14 20:30:34 +00:00
if layer . Size > 0 {
layer . MediaType = mediatype
layers = append ( layers , layer )
}
case "license" :
fn ( api . ProgressResponse { Status : "creating license layer" } )
2023-08-08 20:56:48 +00:00
layer , err := CreateLayer ( strings . NewReader ( c . Args ) )
if err != nil {
return err
}
2023-09-18 20:47:45 +00:00
if layer . Size > 0 {
2023-11-14 20:30:34 +00:00
layer . MediaType = mediatype
2023-09-18 20:47:45 +00:00
layers = append ( layers , layer )
}
2023-11-14 20:30:34 +00:00
case "template" , "system" :
fn ( api . ProgressResponse { Status : fmt . Sprintf ( "creating %s layer" , c . Name ) } )
2023-11-15 23:15:36 +00:00
// remove duplicate layers
2023-11-14 20:30:34 +00:00
layers = removeLayerFromLayers ( layers , mediatype )
2023-07-17 00:02:22 +00:00
2023-07-17 21:21:27 +00:00
layer , err := CreateLayer ( strings . NewReader ( c . Args ) )
2023-07-17 00:02:22 +00:00
if err != nil {
2023-07-17 21:21:27 +00:00
return err
2023-07-17 00:02:22 +00:00
}
2023-07-17 21:21:27 +00:00
2023-09-18 20:47:45 +00:00
if layer . Size > 0 {
2023-11-14 20:30:34 +00:00
layer . MediaType = mediatype
2023-09-18 20:47:45 +00:00
layers = append ( layers , layer )
}
2023-07-17 00:02:22 +00:00
default :
2023-07-28 15:29:00 +00:00
params [ c . Name ] = append ( params [ c . Name ] , c . Args )
2023-07-17 00:02:22 +00:00
}
}
2023-07-17 19:08:10 +00:00
if len ( params ) > 0 {
2023-11-14 20:30:34 +00:00
fn ( api . ProgressResponse { Status : "creating parameters layer" } )
2023-09-02 18:38:51 +00:00
2023-08-04 22:56:40 +00:00
formattedParams , err := formatParams ( params )
2023-07-17 00:02:22 +00:00
if err != nil {
2023-11-14 20:30:34 +00:00
return err
2023-07-17 00:02:22 +00:00
}
2023-08-04 22:56:40 +00:00
2023-11-14 20:30:34 +00:00
for k , v := range fromParams {
2023-09-05 18:05:03 +00:00
if _ , ok := formattedParams [ k ] ; ! ok {
formattedParams [ k ] = v
}
}
2023-09-12 17:52:57 +00:00
if config . ModelType == "65B" {
2023-11-14 20:30:34 +00:00
if gqa , ok := formattedParams [ "gqa" ] . ( int ) ; ok && gqa == 8 {
2023-09-12 17:52:57 +00:00
config . ModelType = "70B"
}
}
2023-11-14 20:30:34 +00:00
var b bytes . Buffer
if err := json . NewEncoder ( & b ) . Encode ( formattedParams ) ; err != nil {
2023-08-04 22:56:40 +00:00
return err
}
2023-11-14 20:30:34 +00:00
fn ( api . ProgressResponse { Status : "creating config layer" } )
layer , err := CreateLayer ( bytes . NewReader ( b . Bytes ( ) ) )
2023-07-17 00:02:22 +00:00
if err != nil {
2023-11-14 20:30:34 +00:00
return err
2023-07-17 00:02:22 +00:00
}
2023-11-14 20:30:34 +00:00
layer . MediaType = "application/vnd.ollama.image.params"
layers = append ( layers , layer )
2023-08-04 22:56:40 +00:00
}
2023-07-17 00:02:22 +00:00
digests , err := getLayerDigests ( layers )
if err != nil {
return err
}
2023-11-14 20:30:34 +00:00
configLayer , err := createConfigLayer ( config , digests )
2023-07-17 00:02:22 +00:00
if err != nil {
return err
}
2023-11-14 20:30:34 +00:00
layers = append ( layers , configLayer )
delete ( deleteMap , configLayer . Digest )
2023-07-17 00:02:22 +00:00
2023-08-14 22:07:00 +00:00
if err := SaveLayers ( layers , fn , false ) ; err != nil {
2023-07-17 00:02:22 +00:00
return err
}
2023-11-14 20:30:34 +00:00
var contentLayers [ ] * Layer
for _ , layer := range layers {
contentLayers = append ( contentLayers , & layer . Layer )
delete ( deleteMap , layer . Digest )
}
2023-07-25 18:25:13 +00:00
fn ( api . ProgressResponse { Status : "writing manifest" } )
2023-11-14 20:30:34 +00:00
if err := CreateManifest ( name , configLayer , contentLayers ) ; err != nil {
2023-07-17 00:02:22 +00:00
return err
}
2023-11-14 20:30:34 +00:00
if noprune := os . Getenv ( "OLLAMA_NOPRUNE" ) ; noprune == "" {
if err := deleteUnusedLayers ( nil , deleteMap , false ) ; err != nil {
2023-09-11 18:46:35 +00:00
return err
}
}
2023-07-25 18:25:13 +00:00
fn ( api . ProgressResponse { Status : "success" } )
2023-07-17 00:02:22 +00:00
return nil
}
2023-07-19 00:14:12 +00:00
func removeLayerFromLayers ( layers [ ] * LayerReader , mediaType string ) [ ] * LayerReader {
2023-09-02 18:38:51 +00:00
return slices . DeleteFunc ( layers , func ( layer * LayerReader ) bool {
return layer . MediaType == mediaType
} )
2023-07-17 00:02:22 +00:00
}
2023-07-25 18:25:13 +00:00
func SaveLayers ( layers [ ] * LayerReader , fn func ( resp api . ProgressResponse ) , force bool ) error {
2023-07-17 00:02:22 +00:00
// Write each of the layers to disk
for _ , layer := range layers {
2023-07-18 05:44:21 +00:00
fp , err := GetBlobsPath ( layer . Digest )
2023-07-17 18:03:55 +00:00
if err != nil {
return err
}
2023-07-17 00:02:22 +00:00
_ , err = os . Stat ( fp )
2023-10-16 15:07:37 +00:00
if os . IsNotExist ( err ) || force {
2023-07-25 18:25:13 +00:00
fn ( api . ProgressResponse { Status : fmt . Sprintf ( "writing layer %s" , layer . Digest ) } )
2023-07-17 00:02:22 +00:00
out , err := os . Create ( fp )
if err != nil {
log . Printf ( "couldn't create %s" , fp )
return err
}
defer out . Close ( )
2023-07-19 00:14:12 +00:00
if _ , err = io . Copy ( out , layer . Reader ) ; err != nil {
2023-07-17 00:02:22 +00:00
return err
}
2023-07-19 00:14:12 +00:00
2023-07-17 00:02:22 +00:00
} else {
2023-07-25 18:25:13 +00:00
fn ( api . ProgressResponse { Status : fmt . Sprintf ( "using already created layer %s" , layer . Digest ) } )
2023-07-17 00:02:22 +00:00
}
}
return nil
}
2023-07-19 00:14:12 +00:00
func CreateManifest ( name string , cfg * LayerReader , layers [ ] * Layer ) error {
2023-08-22 16:39:42 +00:00
mp := ParseModelPath ( name )
2023-07-17 00:02:22 +00:00
manifest := ManifestV2 {
SchemaVersion : 2 ,
MediaType : "application/vnd.docker.distribution.manifest.v2+json" ,
Config : Layer {
MediaType : cfg . MediaType ,
Size : cfg . Size ,
Digest : cfg . Digest ,
} ,
Layers : layers ,
}
manifestJSON , err := json . Marshal ( manifest )
if err != nil {
return err
}
2023-10-27 14:19:59 +00:00
fp , err := mp . GetManifestPath ( )
2023-07-17 00:02:22 +00:00
if err != nil {
return err
}
2023-10-27 14:19:59 +00:00
if err := os . MkdirAll ( filepath . Dir ( fp ) , 0 o755 ) ; err != nil {
return err
}
2023-07-17 18:03:55 +00:00
return os . WriteFile ( fp , manifestJSON , 0 o644 )
2023-07-17 00:02:22 +00:00
}
2023-07-19 00:14:12 +00:00
func GetLayerWithBufferFromLayer ( layer * Layer ) ( * LayerReader , error ) {
2023-07-18 05:44:21 +00:00
fp , err := GetBlobsPath ( layer . Digest )
2023-07-17 00:02:22 +00:00
if err != nil {
return nil , err
}
file , err := os . Open ( fp )
if err != nil {
return nil , fmt . Errorf ( "could not open blob: %w" , err )
}
defer file . Close ( )
newLayer , err := CreateLayer ( file )
if err != nil {
return nil , err
}
newLayer . MediaType = layer . MediaType
return newLayer , nil
}
2023-08-04 22:56:40 +00:00
// formatParams converts specified parameter options to their correct types
func formatParams ( params map [ string ] [ ] string ) ( map [ string ] interface { } , error ) {
2023-08-01 17:36:31 +00:00
opts := api . Options { }
valueOpts := reflect . ValueOf ( & opts ) . Elem ( ) // names of the fields in the options struct
typeOpts := reflect . TypeOf ( opts ) // types of the fields in the options struct
2023-07-17 19:08:10 +00:00
2023-08-01 17:36:31 +00:00
// build map of json struct tags to their types
2023-07-17 19:08:10 +00:00
jsonOpts := make ( map [ string ] reflect . StructField )
for _ , field := range reflect . VisibleFields ( typeOpts ) {
jsonTag := strings . Split ( field . Tag . Get ( "json" ) , "," ) [ 0 ]
if jsonTag != "" {
jsonOpts [ jsonTag ] = field
}
}
2023-08-01 17:36:31 +00:00
out := make ( map [ string ] interface { } )
2023-07-17 19:08:10 +00:00
// iterate params and set values based on json struct tags
2023-07-28 15:29:00 +00:00
for key , vals := range params {
2023-07-17 19:08:10 +00:00
if opt , ok := jsonOpts [ key ] ; ok {
field := valueOpts . FieldByName ( opt . Name )
if field . IsValid ( ) && field . CanSet ( ) {
switch field . Kind ( ) {
case reflect . Float32 :
2023-07-28 15:29:00 +00:00
floatVal , err := strconv . ParseFloat ( vals [ 0 ] , 32 )
2023-07-17 19:08:10 +00:00
if err != nil {
2023-07-28 15:29:00 +00:00
return nil , fmt . Errorf ( "invalid float value %s" , vals )
2023-07-17 19:08:10 +00:00
}
2023-09-12 17:52:57 +00:00
out [ key ] = float32 ( floatVal )
2023-07-17 19:08:10 +00:00
case reflect . Int :
2023-09-12 17:52:57 +00:00
intVal , err := strconv . ParseInt ( vals [ 0 ] , 10 , 64 )
2023-07-17 19:08:10 +00:00
if err != nil {
2023-07-28 15:29:00 +00:00
return nil , fmt . Errorf ( "invalid int value %s" , vals )
2023-07-17 19:08:10 +00:00
}
2023-10-05 18:58:04 +00:00
out [ key ] = intVal
2023-07-17 19:08:10 +00:00
case reflect . Bool :
2023-07-28 15:29:00 +00:00
boolVal , err := strconv . ParseBool ( vals [ 0 ] )
2023-07-17 19:08:10 +00:00
if err != nil {
2023-07-28 15:29:00 +00:00
return nil , fmt . Errorf ( "invalid bool value %s" , vals )
2023-07-17 19:08:10 +00:00
}
2023-08-01 17:36:31 +00:00
out [ key ] = boolVal
2023-07-17 19:08:10 +00:00
case reflect . String :
2023-08-01 17:36:31 +00:00
out [ key ] = vals [ 0 ]
2023-07-27 21:02:14 +00:00
case reflect . Slice :
2023-08-01 17:36:31 +00:00
// TODO: only string slices are supported right now
out [ key ] = vals
2023-07-17 19:08:10 +00:00
default :
return nil , fmt . Errorf ( "unknown type %s for %s" , field . Kind ( ) , key )
}
}
}
}
2023-08-04 22:56:40 +00:00
return out , nil
2023-07-17 00:02:22 +00:00
}
2023-07-19 00:14:12 +00:00
func getLayerDigests ( layers [ ] * LayerReader ) ( [ ] string , error ) {
2023-07-17 00:02:22 +00:00
var digests [ ] string
for _ , l := range layers {
if l . Digest == "" {
return nil , fmt . Errorf ( "layer is missing a digest" )
}
digests = append ( digests , l . Digest )
}
return digests , nil
}
// CreateLayer creates a Layer object from a given file
2023-07-19 00:14:12 +00:00
func CreateLayer ( f io . ReadSeeker ) ( * LayerReader , error ) {
digest , size := GetSHA256Digest ( f )
2023-07-21 20:33:56 +00:00
f . Seek ( 0 , io . SeekStart )
2023-07-17 00:02:22 +00:00
2023-07-19 00:14:12 +00:00
layer := & LayerReader {
2023-07-17 00:02:22 +00:00
Layer : Layer {
MediaType : "application/vnd.docker.image.rootfs.diff.tar" ,
Digest : digest ,
Size : size ,
} ,
2023-07-19 00:14:12 +00:00
Reader : f ,
2023-07-17 00:02:22 +00:00
}
return layer , nil
}
2023-07-24 15:27:28 +00:00
func CopyModel ( src , dest string ) error {
2023-08-22 16:39:42 +00:00
srcModelPath := ParseModelPath ( src )
2023-10-27 14:19:59 +00:00
srcPath , err := srcModelPath . GetManifestPath ( )
2023-08-22 04:56:56 +00:00
if err != nil {
return err
}
2023-08-22 16:39:42 +00:00
destModelPath := ParseModelPath ( dest )
2023-10-27 14:19:59 +00:00
destPath , err := destModelPath . GetManifestPath ( )
2023-07-24 15:27:28 +00:00
if err != nil {
return err
}
2023-10-27 14:19:59 +00:00
if err := os . MkdirAll ( filepath . Dir ( destPath ) , 0 o755 ) ; err != nil {
return err
}
2023-07-24 15:27:28 +00:00
// copy the file
2023-07-28 17:38:15 +00:00
input , err := os . ReadFile ( srcPath )
2023-07-24 15:27:28 +00:00
if err != nil {
fmt . Println ( "Error reading file:" , err )
return err
}
2023-07-28 17:38:15 +00:00
err = os . WriteFile ( destPath , input , 0 o644 )
2023-07-24 15:27:28 +00:00
if err != nil {
fmt . Println ( "Error reading file:" , err )
return err
}
return nil
}
2023-11-14 20:30:34 +00:00
func deleteUnusedLayers ( skipModelPath * ModelPath , deleteMap map [ string ] struct { } , dryRun bool ) error {
2023-07-20 23:09:23 +00:00
fp , err := GetManifestPath ( )
if err != nil {
return err
}
2023-08-30 18:31:12 +00:00
walkFunc := func ( path string , info os . FileInfo , _ error ) error {
if info . IsDir ( ) {
return nil
2023-07-20 23:09:23 +00:00
}
2023-08-30 18:31:12 +00:00
dir , file := filepath . Split ( path )
dir = strings . Trim ( strings . TrimPrefix ( dir , fp ) , string ( os . PathSeparator ) )
tag := strings . Join ( [ ] string { dir , file } , ":" )
fmp := ParseModelPath ( tag )
2023-07-20 23:09:23 +00:00
2023-08-30 18:31:12 +00:00
// skip the manifest we're trying to delete
2023-09-11 18:46:35 +00:00
if skipModelPath != nil && skipModelPath . GetFullTagname ( ) == fmp . GetFullTagname ( ) {
2023-08-30 18:31:12 +00:00
return nil
2023-07-20 23:09:23 +00:00
}
2023-08-30 18:31:12 +00:00
// save (i.e. delete from the deleteMap) any files used in other manifests
manifest , _ , err := GetManifest ( fmp )
if err != nil {
return nil
}
for _ , layer := range manifest . Layers {
delete ( deleteMap , layer . Digest )
}
delete ( deleteMap , manifest . Config . Digest )
2023-07-20 23:09:23 +00:00
return nil
2023-08-30 18:31:12 +00:00
}
if err := filepath . Walk ( fp , walkFunc ) ; err != nil {
2023-07-31 22:26:18 +00:00
return err
}
2023-07-20 23:09:23 +00:00
// only delete the files which are still in the deleteMap
2023-11-14 20:30:34 +00:00
for k := range deleteMap {
fp , err := GetBlobsPath ( k )
if err != nil {
log . Printf ( "couldn't get file path for '%s': %v" , k , err )
continue
}
if ! dryRun {
if err := os . Remove ( fp ) ; err != nil {
log . Printf ( "couldn't remove file '%s': %v" , fp , err )
2023-07-22 00:30:40 +00:00
continue
}
2023-11-14 20:30:34 +00:00
} else {
log . Printf ( "wanted to remove: %s" , fp )
2023-07-20 23:09:23 +00:00
}
}
2023-09-11 18:46:35 +00:00
return nil
}
func PruneLayers ( ) error {
2023-11-14 20:30:34 +00:00
deleteMap := make ( map [ string ] struct { } )
2023-09-11 18:46:35 +00:00
p , err := GetBlobsPath ( "" )
if err != nil {
return err
}
blobs , err := os . ReadDir ( p )
if err != nil {
log . Printf ( "couldn't read dir '%s': %v" , p , err )
return err
}
for _ , blob := range blobs {
name := blob . Name ( )
if runtime . GOOS == "windows" {
name = strings . ReplaceAll ( name , "-" , ":" )
}
2023-11-14 22:27:51 +00:00
if strings . HasPrefix ( name , "sha256:" ) {
deleteMap [ name ] = struct { } { }
}
2023-09-11 18:46:35 +00:00
}
log . Printf ( "total blobs: %d" , len ( deleteMap ) )
err = deleteUnusedLayers ( nil , deleteMap , false )
if err != nil {
return err
}
log . Printf ( "total unused blobs removed: %d" , len ( deleteMap ) )
return nil
}
2023-09-27 00:28:14 +00:00
func PruneDirectory ( path string ) error {
info , err := os . Lstat ( path )
if err != nil {
return err
}
if info . IsDir ( ) && info . Mode ( ) & os . ModeSymlink == 0 {
entries , err := os . ReadDir ( path )
if err != nil {
return err
}
for _ , entry := range entries {
if err := PruneDirectory ( filepath . Join ( path , entry . Name ( ) ) ) ; err != nil {
return err
}
}
entries , err = os . ReadDir ( path )
if err != nil {
return err
}
if len ( entries ) > 0 {
return nil
}
return os . Remove ( path )
}
return nil
}
2023-09-11 18:46:35 +00:00
func DeleteModel ( name string ) error {
mp := ParseModelPath ( name )
manifest , _ , err := GetManifest ( mp )
if err != nil {
return err
}
2023-11-14 20:30:34 +00:00
deleteMap := make ( map [ string ] struct { } )
2023-09-11 18:46:35 +00:00
for _ , layer := range manifest . Layers {
2023-11-14 20:30:34 +00:00
deleteMap [ layer . Digest ] = struct { } { }
2023-09-11 18:46:35 +00:00
}
2023-11-14 20:30:34 +00:00
deleteMap [ manifest . Config . Digest ] = struct { } { }
2023-09-11 18:46:35 +00:00
err = deleteUnusedLayers ( & mp , deleteMap , false )
if err != nil {
return err
}
2023-10-27 14:19:59 +00:00
fp , err := mp . GetManifestPath ( )
2023-07-20 23:09:23 +00:00
if err != nil {
return err
}
err = os . Remove ( fp )
if err != nil {
log . Printf ( "couldn't remove manifest file '%s': %v" , fp , err )
return err
}
return nil
}
2023-09-06 18:04:17 +00:00
func ShowModelfile ( model * Model ) ( string , error ) {
2023-10-17 22:40:06 +00:00
var mt struct {
2023-09-06 18:04:17 +00:00
* Model
2023-10-17 22:40:06 +00:00
From string
2023-10-17 22:53:46 +00:00
Parameters map [ string ] [ ] any
2023-09-06 18:04:17 +00:00
}
2023-10-17 22:53:46 +00:00
mt . Parameters = make ( map [ string ] [ ] any )
2023-09-06 18:04:17 +00:00
for k , v := range model . Options {
2023-10-17 22:53:46 +00:00
if s , ok := v . ( [ ] any ) ; ok {
mt . Parameters [ k ] = s
continue
2023-09-06 18:04:17 +00:00
}
2023-10-17 22:53:46 +00:00
mt . Parameters [ k ] = [ ] any { v }
2023-09-06 18:04:17 +00:00
}
2023-10-17 22:40:06 +00:00
mt . Model = model
mt . From = model . ModelPath
2023-09-06 18:04:17 +00:00
2023-10-17 22:40:06 +00:00
if model . OriginalModel != "" {
2023-11-10 20:21:35 +00:00
mt . From = model . OriginalModel
2023-09-06 18:04:17 +00:00
}
modelFile := ` # Modelfile generated by "ollama show"
# To build a new Modelfile based on this one , replace the FROM line with :
# FROM { { . ShortName } }
FROM { { . From } }
TEMPLATE "" "{{ .Template }}" ""
2023-10-17 22:25:43 +00:00
{ { - if . System } }
2023-09-06 18:04:17 +00:00
SYSTEM "" "{{ .System }}" ""
2023-10-17 22:25:43 +00:00
{ { - end } }
2023-10-17 22:28:38 +00:00
{ { - range $ adapter := . AdapterPaths } }
ADAPTER { { $ adapter } }
{ { - end } }
2023-10-17 22:40:06 +00:00
2023-10-17 22:53:46 +00:00
{ { - range $ k , $ v := . Parameters } }
{ { - range $ parameter := $ v } }
PARAMETER { { $ k } } { { printf "%#v" $ parameter } }
{ { - end } }
2023-10-17 22:40:06 +00:00
{ { - end } } `
2023-09-06 18:04:17 +00:00
tmpl , err := template . New ( "" ) . Parse ( modelFile )
if err != nil {
log . Printf ( "error parsing template: %q" , err )
return "" , err
}
var buf bytes . Buffer
if err = tmpl . Execute ( & buf , mt ) ; err != nil {
log . Printf ( "error executing template: %q" , err )
return "" , err
}
return buf . String ( ) , nil
}
2023-08-11 22:41:55 +00:00
func PushModel ( ctx context . Context , name string , regOpts * RegistryOptions , fn func ( api . ProgressResponse ) ) error {
2023-08-22 16:39:42 +00:00
mp := ParseModelPath ( name )
2023-07-19 01:51:30 +00:00
fn ( api . ProgressResponse { Status : "retrieving manifest" } )
2023-08-22 16:39:42 +00:00
if mp . ProtocolScheme == "http" && ! regOpts . Insecure {
return fmt . Errorf ( "insecure protocol http" )
}
2023-08-29 03:50:24 +00:00
manifest , _ , err := GetManifest ( mp )
2023-07-17 00:02:22 +00:00
if err != nil {
2023-07-19 01:51:30 +00:00
fn ( api . ProgressResponse { Status : "couldn't retrieve manifest" } )
2023-07-17 00:02:22 +00:00
return err
}
var layers [ ] * Layer
2023-08-01 01:37:40 +00:00
layers = append ( layers , manifest . Layers ... )
2023-07-17 00:02:22 +00:00
layers = append ( layers , & manifest . Config )
for _ , layer := range layers {
2023-10-09 17:24:27 +00:00
if err := uploadBlob ( ctx , mp , layer , regOpts , fn ) ; err != nil {
2023-07-17 00:02:22 +00:00
log . Printf ( "error uploading blob: %v" , err )
2023-11-16 21:44:18 +00:00
if errors . Is ( err , errUnauthorized ) {
return fmt . Errorf ( "unable to push %s, make sure this namespace exists and you are authorized to push to it" , ParseModelPath ( name ) . GetNamespaceRepository ( ) )
}
2023-07-17 00:02:22 +00:00
return err
}
2023-07-19 01:51:30 +00:00
}
2023-07-23 00:31:26 +00:00
fn ( api . ProgressResponse { Status : "pushing manifest" } )
2023-08-22 01:38:31 +00:00
requestURL := mp . BaseURL ( )
requestURL = requestURL . JoinPath ( "v2" , mp . GetNamespaceRepository ( ) , "manifests" , mp . Tag )
2023-07-17 00:02:22 +00:00
manifestJSON , err := json . Marshal ( manifest )
if err != nil {
return err
}
2023-08-22 01:24:42 +00:00
headers := make ( http . Header )
headers . Set ( "Content-Type" , "application/vnd.docker.distribution.manifest.v2+json" )
2023-11-02 20:10:58 +00:00
resp , err := makeRequestWithRetry ( ctx , http . MethodPut , requestURL , headers , bytes . NewReader ( manifestJSON ) , regOpts )
2023-07-17 00:02:22 +00:00
if err != nil {
return err
}
defer resp . Body . Close ( )
2023-07-23 00:31:26 +00:00
fn ( api . ProgressResponse { Status : "success" } )
2023-07-17 00:02:22 +00:00
return nil
}
2023-07-25 21:08:51 +00:00
func PullModel ( ctx context . Context , name string , regOpts * RegistryOptions , fn func ( api . ProgressResponse ) ) error {
2023-08-22 16:39:42 +00:00
mp := ParseModelPath ( name )
2023-09-11 18:46:35 +00:00
var manifest * ManifestV2
var err error
var noprune string
// build deleteMap to prune unused layers
2023-11-14 20:30:34 +00:00
deleteMap := make ( map [ string ] struct { } )
2023-09-11 18:46:35 +00:00
if noprune = os . Getenv ( "OLLAMA_NOPRUNE" ) ; noprune == "" {
manifest , _ , err = GetManifest ( mp )
if err != nil && ! errors . Is ( err , os . ErrNotExist ) {
return err
}
if manifest != nil {
for _ , l := range manifest . Layers {
2023-11-14 20:30:34 +00:00
deleteMap [ l . Digest ] = struct { } { }
2023-09-11 18:46:35 +00:00
}
2023-11-14 20:30:34 +00:00
deleteMap [ manifest . Config . Digest ] = struct { } { }
2023-09-11 18:46:35 +00:00
}
}
2023-08-22 16:39:42 +00:00
if mp . ProtocolScheme == "http" && ! regOpts . Insecure {
return fmt . Errorf ( "insecure protocol http" )
2023-08-22 04:56:56 +00:00
}
2023-07-17 00:02:22 +00:00
2023-07-19 01:51:30 +00:00
fn ( api . ProgressResponse { Status : "pulling manifest" } )
2023-07-17 00:02:22 +00:00
2023-09-11 18:46:35 +00:00
manifest , err = pullModelManifest ( ctx , mp , regOpts )
2023-07-17 00:02:22 +00:00
if err != nil {
2023-07-24 21:48:17 +00:00
return fmt . Errorf ( "pull model manifest: %s" , err )
2023-07-17 00:02:22 +00:00
}
var layers [ ] * Layer
2023-07-20 18:18:00 +00:00
layers = append ( layers , manifest . Layers ... )
2023-07-17 00:02:22 +00:00
layers = append ( layers , & manifest . Config )
for _ , layer := range layers {
2023-08-15 18:07:19 +00:00
if err := downloadBlob (
ctx ,
downloadOpts {
mp : mp ,
digest : layer . Digest ,
regOpts : regOpts ,
fn : fn ,
} ) ; err != nil {
2023-07-17 00:02:22 +00:00
return err
}
2023-09-11 18:46:35 +00:00
delete ( deleteMap , layer . Digest )
2023-07-17 00:02:22 +00:00
}
2023-09-11 18:46:35 +00:00
delete ( deleteMap , manifest . Config . Digest )
2023-07-17 00:02:22 +00:00
2023-07-20 18:44:05 +00:00
fn ( api . ProgressResponse { Status : "verifying sha256 digest" } )
for _ , layer := range layers {
if err := verifyBlob ( layer . Digest ) ; err != nil {
2023-07-24 18:53:01 +00:00
if errors . Is ( err , errDigestMismatch ) {
// something went wrong, delete the blob
fp , err := GetBlobsPath ( layer . Digest )
if err != nil {
return err
}
if err := os . Remove ( fp ) ; err != nil {
// log this, but return the original error
log . Printf ( "couldn't remove file with digest mismatch '%s': %v" , fp , err )
}
}
2023-07-20 18:44:05 +00:00
return err
}
}
2023-07-19 01:51:30 +00:00
fn ( api . ProgressResponse { Status : "writing manifest" } )
2023-07-17 00:02:22 +00:00
2023-07-17 18:03:55 +00:00
manifestJSON , err := json . Marshal ( manifest )
2023-07-17 00:02:22 +00:00
if err != nil {
return err
}
2023-10-27 14:19:59 +00:00
fp , err := mp . GetManifestPath ( )
2023-07-17 00:02:22 +00:00
if err != nil {
return err
}
2023-10-27 14:19:59 +00:00
if err := os . MkdirAll ( filepath . Dir ( fp ) , 0 o755 ) ; err != nil {
return err
}
2023-07-17 00:02:22 +00:00
2023-07-20 18:18:00 +00:00
err = os . WriteFile ( fp , manifestJSON , 0 o644 )
2023-07-17 00:02:22 +00:00
if err != nil {
log . Printf ( "couldn't write to %s" , fp )
return err
}
2023-09-11 18:46:35 +00:00
if noprune == "" {
fn ( api . ProgressResponse { Status : "removing any unused layers" } )
err = deleteUnusedLayers ( nil , deleteMap , false )
if err != nil {
return err
}
}
2023-07-19 01:51:30 +00:00
fn ( api . ProgressResponse { Status : "success" } )
2023-07-17 00:02:22 +00:00
return nil
}
2023-08-11 22:41:55 +00:00
func pullModelManifest ( ctx context . Context , mp ModelPath , regOpts * RegistryOptions ) ( * ManifestV2 , error ) {
2023-08-22 01:38:31 +00:00
requestURL := mp . BaseURL ( ) . JoinPath ( "v2" , mp . GetNamespaceRepository ( ) , "manifests" , mp . Tag )
2023-07-17 00:02:22 +00:00
2023-08-22 01:24:42 +00:00
headers := make ( http . Header )
headers . Set ( "Accept" , "application/vnd.docker.distribution.manifest.v2+json" )
2023-11-02 20:13:32 +00:00
resp , err := makeRequestWithRetry ( ctx , http . MethodGet , requestURL , headers , nil , regOpts )
2023-07-17 00:02:22 +00:00
if err != nil {
return nil , err
}
defer resp . Body . Close ( )
var m * ManifestV2
if err := json . NewDecoder ( resp . Body ) . Decode ( & m ) ; err != nil {
return nil , err
}
return m , err
}
2023-07-21 20:33:56 +00:00
func createConfigLayer ( config ConfigV2 , layers [ ] string ) ( * LayerReader , error ) {
config . RootFS = RootFS {
Type : "layers" ,
DiffIDs : layers ,
2023-07-17 00:02:22 +00:00
}
configJSON , err := json . Marshal ( config )
if err != nil {
return nil , err
}
2023-07-19 07:47:55 +00:00
digest , size := GetSHA256Digest ( bytes . NewBuffer ( configJSON ) )
2023-07-17 00:02:22 +00:00
2023-07-19 00:14:12 +00:00
layer := & LayerReader {
2023-07-17 00:02:22 +00:00
Layer : Layer {
MediaType : "application/vnd.docker.container.image.v1+json" ,
Digest : digest ,
Size : size ,
} ,
2023-07-19 07:47:55 +00:00
Reader : bytes . NewBuffer ( configJSON ) ,
2023-07-17 00:02:22 +00:00
}
return layer , nil
}
// GetSHA256Digest returns the SHA256 hash of a given buffer and returns it, and the size of buffer
2023-09-28 17:00:34 +00:00
func GetSHA256Digest ( r io . Reader ) ( string , int64 ) {
2023-07-19 00:14:12 +00:00
h := sha256 . New ( )
n , err := io . Copy ( h , r )
if err != nil {
log . Fatal ( err )
}
2023-09-28 17:00:34 +00:00
return fmt . Sprintf ( "sha256:%x" , h . Sum ( nil ) ) , n
2023-07-17 00:02:22 +00:00
}
2023-11-16 21:44:18 +00:00
var errUnauthorized = fmt . Errorf ( "unauthorized" )
2023-08-22 01:38:31 +00:00
func makeRequestWithRetry ( ctx context . Context , method string , requestURL * url . URL , headers http . Header , body io . ReadSeeker , regOpts * RegistryOptions ) ( * http . Response , error ) {
2023-11-17 19:22:35 +00:00
resp , err := makeRequest ( ctx , method , requestURL , headers , body , regOpts )
if err != nil {
if ! errors . Is ( err , context . Canceled ) {
log . Printf ( "request failed: %v" , err )
}
return nil , err
}
switch {
case resp . StatusCode == http . StatusUnauthorized :
// Handle authentication error with one retry
auth := resp . Header . Get ( "www-authenticate" )
authRedir := ParseAuthRedirectString ( auth )
token , err := getAuthToken ( ctx , authRedir )
2023-08-17 19:35:29 +00:00
if err != nil {
return nil , err
}
2023-11-17 19:22:35 +00:00
regOpts . Token = token
if body != nil {
_ , err = body . Seek ( 0 , io . SeekStart )
2023-08-17 19:35:29 +00:00
if err != nil {
return nil , err
}
}
2023-11-17 19:22:35 +00:00
return makeRequest ( ctx , method , requestURL , headers , body , regOpts )
case resp . StatusCode == http . StatusNotFound :
return nil , os . ErrNotExist
case resp . StatusCode >= http . StatusBadRequest :
responseBody , err := io . ReadAll ( resp . Body )
if err != nil {
return nil , fmt . Errorf ( "%d: %s" , resp . StatusCode , err )
}
return nil , fmt . Errorf ( "%d: %s" , resp . StatusCode , responseBody )
2023-08-17 19:35:29 +00:00
}
2023-11-17 19:22:35 +00:00
return resp , nil
2023-08-17 19:35:29 +00:00
}
2023-08-22 01:38:31 +00:00
func makeRequest ( ctx context . Context , method string , requestURL * url . URL , headers http . Header , body io . Reader , regOpts * RegistryOptions ) ( * http . Response , error ) {
2023-09-08 00:24:31 +00:00
if requestURL . Scheme != "http" && regOpts != nil && regOpts . Insecure {
2023-08-22 01:38:31 +00:00
requestURL . Scheme = "http"
2023-07-21 22:42:19 +00:00
}
2023-08-22 01:38:31 +00:00
req , err := http . NewRequestWithContext ( ctx , method , requestURL . String ( ) , body )
2023-07-17 00:02:22 +00:00
if err != nil {
return nil , err
}
2023-08-22 01:24:42 +00:00
if headers != nil {
req . Header = headers
}
2023-09-07 18:49:36 +00:00
if regOpts != nil {
if regOpts . Token != "" {
req . Header . Set ( "Authorization" , "Bearer " + regOpts . Token )
} else if regOpts . Username != "" && regOpts . Password != "" {
req . SetBasicAuth ( regOpts . Username , regOpts . Password )
}
2023-07-17 00:02:22 +00:00
}
2023-08-22 01:24:42 +00:00
req . Header . Set ( "User-Agent" , fmt . Sprintf ( "ollama/%s (%s %s) Go/%s" , version . Version , runtime . GOARCH , runtime . GOOS , runtime . Version ( ) ) )
2023-07-17 00:02:22 +00:00
2023-09-14 17:05:29 +00:00
if s := req . Header . Get ( "Content-Length" ) ; s != "" {
contentLength , err := strconv . ParseInt ( s , 10 , 64 )
if err != nil {
return nil , err
}
req . ContentLength = contentLength
}
2023-10-09 18:42:36 +00:00
proxyURL , err := http . ProxyFromEnvironment ( req )
if err != nil {
return nil , err
}
client := http . Client {
Transport : & http . Transport {
Proxy : http . ProxyURL ( proxyURL ) ,
} ,
}
resp , err := client . Do ( req )
2023-07-17 00:02:22 +00:00
if err != nil {
return nil , err
}
return resp , nil
}
2023-07-20 18:44:05 +00:00
2023-08-10 18:34:25 +00:00
func getValue ( header , key string ) string {
startIdx := strings . Index ( header , key + "=" )
if startIdx == - 1 {
return ""
}
// Move the index to the starting quote after the key.
startIdx += len ( key ) + 2
endIdx := startIdx
for endIdx < len ( header ) {
if header [ endIdx ] == '"' {
if endIdx + 1 < len ( header ) && header [ endIdx + 1 ] != ',' { // If the next character isn't a comma, continue
endIdx ++
continue
}
break
}
endIdx ++
}
return header [ startIdx : endIdx ]
}
func ParseAuthRedirectString ( authStr string ) AuthRedirect {
authStr = strings . TrimPrefix ( authStr , "Bearer " )
return AuthRedirect {
Realm : getValue ( authStr , "realm" ) ,
Service : getValue ( authStr , "service" ) ,
Scope : getValue ( authStr , "scope" ) ,
}
}
2023-07-24 18:53:01 +00:00
var errDigestMismatch = fmt . Errorf ( "digest mismatch, file must be downloaded again" )
2023-07-20 18:44:05 +00:00
func verifyBlob ( digest string ) error {
fp , err := GetBlobsPath ( digest )
if err != nil {
return err
}
f , err := os . Open ( fp )
if err != nil {
return err
}
defer f . Close ( )
fileDigest , _ := GetSHA256Digest ( f )
if digest != fileDigest {
2023-07-24 18:53:01 +00:00
return fmt . Errorf ( "%w: want %s, got %s" , errDigestMismatch , digest , fileDigest )
2023-07-20 18:44:05 +00:00
}
return nil
}