Compare commits
12 Commits
main
...
jyan/ollam
Author | SHA1 | Date | |
---|---|---|---|
|
3958d82908 | ||
|
632237139e | ||
|
2f5497301f | ||
|
b0a947cf1f | ||
|
cf7344b221 | ||
|
3cd1c9baa9 | ||
|
e5f03af4d3 | ||
|
b669677be9 | ||
|
86a874fceb | ||
|
18203bf8d8 | ||
|
aaa1c08a5d | ||
|
ab9dfbddea |
2
go.mod
2
go.mod
@ -25,6 +25,7 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||||
github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40 // indirect
|
github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||||
github.com/chewxy/hm v1.0.0 // indirect
|
github.com/chewxy/hm v1.0.0 // indirect
|
||||||
@ -40,6 +41,7 @@ require (
|
|||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
github.com/xtgo/set v1.0.0 // indirect
|
github.com/xtgo/set v1.0.0 // indirect
|
||||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 // indirect
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 // indirect
|
||||||
|
golang.org/x/mod v0.19.0 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
gonum.org/v1/gonum v0.15.0 // indirect
|
gonum.org/v1/gonum v0.15.0 // indirect
|
||||||
gorgonia.org/vecf32 v0.9.0 // indirect
|
gorgonia.org/vecf32 v0.9.0 // indirect
|
||||||
|
4
go.sum
4
go.sum
@ -4,6 +4,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
|
|||||||
gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
|
gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||||
|
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||||
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
|
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
|
||||||
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
|
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
|
||||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||||
@ -240,6 +242,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
|||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
|
||||||
|
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/mod/semver"
|
||||||
"golang.org/x/text/encoding/unicode"
|
"golang.org/x/text/encoding/unicode"
|
||||||
"golang.org/x/text/transform"
|
"golang.org/x/text/transform"
|
||||||
)
|
)
|
||||||
@ -41,6 +42,8 @@ func (c Command) String() string {
|
|||||||
case "message":
|
case "message":
|
||||||
role, message, _ := strings.Cut(c.Args, ": ")
|
role, message, _ := strings.Cut(c.Args, ": ")
|
||||||
fmt.Fprintf(&sb, "MESSAGE %s %s", role, quote(message))
|
fmt.Fprintf(&sb, "MESSAGE %s %s", role, quote(message))
|
||||||
|
case "ollama":
|
||||||
|
fmt.Fprintf(&sb, "OLLAMA %s", c.Args)
|
||||||
default:
|
default:
|
||||||
fmt.Fprintf(&sb, "PARAMETER %s %s", c.Name, quote(c.Args))
|
fmt.Fprintf(&sb, "PARAMETER %s %s", c.Name, quote(c.Args))
|
||||||
}
|
}
|
||||||
@ -57,12 +60,14 @@ const (
|
|||||||
stateParameter
|
stateParameter
|
||||||
stateMessage
|
stateMessage
|
||||||
stateComment
|
stateComment
|
||||||
|
stateVersion
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errMissingFrom = errors.New("no FROM line")
|
errMissingFrom = errors.New("no FROM line")
|
||||||
errInvalidMessageRole = errors.New("message role must be one of \"system\", \"user\", or \"assistant\"")
|
errInvalidMessageRole = errors.New("message role must be one of \"system\", \"user\", or \"assistant\"")
|
||||||
errInvalidCommand = errors.New("command must be one of \"from\", \"license\", \"template\", \"system\", \"adapter\", \"parameter\", or \"message\"")
|
errInvalidCommand = errors.New("command must be one of \"from\", \"license\", \"template\", \"system\", \"adapter\", \"parameter\", or \"message\"")
|
||||||
|
errInvalidVersion = errors.New("invalid OLLAMA version")
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseFile(r io.Reader) (*File, error) {
|
func ParseFile(r io.Reader) (*File, error) {
|
||||||
@ -109,6 +114,9 @@ func ParseFile(r io.Reader) (*File, error) {
|
|||||||
case "message":
|
case "message":
|
||||||
// transition to stateMessage which validates the message role
|
// transition to stateMessage which validates the message role
|
||||||
next = stateMessage
|
next = stateMessage
|
||||||
|
cmd.Name = s
|
||||||
|
case "ollama":
|
||||||
|
next = stateVersion
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
cmd.Name = s
|
cmd.Name = s
|
||||||
@ -123,6 +131,29 @@ func ParseFile(r io.Reader) (*File, error) {
|
|||||||
role = b.String()
|
role = b.String()
|
||||||
case stateComment, stateNil:
|
case stateComment, stateNil:
|
||||||
// pass
|
// pass
|
||||||
|
case stateVersion:
|
||||||
|
s, ok := unquote(strings.TrimSpace(b.String()))
|
||||||
|
if !ok {
|
||||||
|
if _, err := b.WriteRune(r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
} else if isSpace(r) {
|
||||||
|
return nil, errInvalidVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
if s[0] != 'v' {
|
||||||
|
s = "v" + s
|
||||||
|
}
|
||||||
|
|
||||||
|
if !semver.IsValid(s) {
|
||||||
|
return nil, errInvalidVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Args = semver.Canonical(s)
|
||||||
|
f.Commands = append(f.Commands, cmd)
|
||||||
|
|
||||||
case stateValue:
|
case stateValue:
|
||||||
s, ok := unquote(strings.TrimSpace(b.String()))
|
s, ok := unquote(strings.TrimSpace(b.String()))
|
||||||
if !ok || isSpace(r) {
|
if !ok || isSpace(r) {
|
||||||
@ -157,6 +188,22 @@ func ParseFile(r io.Reader) (*File, error) {
|
|||||||
switch curr {
|
switch curr {
|
||||||
case stateComment, stateNil:
|
case stateComment, stateNil:
|
||||||
// pass; nothing to flush
|
// pass; nothing to flush
|
||||||
|
case stateVersion:
|
||||||
|
s, ok := unquote(strings.TrimSpace(b.String()))
|
||||||
|
if !ok {
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
|
||||||
|
if s[0] != 'v' {
|
||||||
|
s = "v" + s
|
||||||
|
}
|
||||||
|
|
||||||
|
if !semver.IsValid(s) {
|
||||||
|
return nil, errInvalidVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Args = semver.Canonical(s)
|
||||||
|
f.Commands = append(f.Commands, cmd)
|
||||||
case stateValue:
|
case stateValue:
|
||||||
s, ok := unquote(strings.TrimSpace(b.String()))
|
s, ok := unquote(strings.TrimSpace(b.String()))
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -236,6 +283,15 @@ func parseRuneForState(r rune, cs state) (state, rune, error) {
|
|||||||
default:
|
default:
|
||||||
return stateComment, 0, nil
|
return stateComment, 0, nil
|
||||||
}
|
}
|
||||||
|
case stateVersion:
|
||||||
|
switch {
|
||||||
|
case isNewline(r), isSpace(r):
|
||||||
|
return stateNil, 0, nil
|
||||||
|
case isAlpha(r), isNumber(r), r == '.', r == '+', r == '-':
|
||||||
|
return stateVersion, r, nil
|
||||||
|
default:
|
||||||
|
return stateNil, r, nil
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return stateNil, 0, errors.New("")
|
return stateNil, 0, errors.New("")
|
||||||
}
|
}
|
||||||
@ -296,7 +352,7 @@ func isValidMessageRole(role string) bool {
|
|||||||
|
|
||||||
func isValidCommand(cmd string) bool {
|
func isValidCommand(cmd string) bool {
|
||||||
switch strings.ToLower(cmd) {
|
switch strings.ToLower(cmd) {
|
||||||
case "from", "license", "template", "system", "adapter", "parameter", "message":
|
case "from", "license", "template", "system", "adapter", "parameter", "message", "ollama":
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
|
@ -374,6 +374,7 @@ func CreateModel(ctx context.Context, name model.Name, modelFileDir, quantizatio
|
|||||||
}
|
}
|
||||||
|
|
||||||
var messages []*api.Message
|
var messages []*api.Message
|
||||||
|
var version string
|
||||||
parameters := make(map[string]any)
|
parameters := make(map[string]any)
|
||||||
|
|
||||||
var layers []*Layer
|
var layers []*Layer
|
||||||
@ -384,7 +385,7 @@ func CreateModel(ctx context.Context, name model.Name, modelFileDir, quantizatio
|
|||||||
case "model", "adapter":
|
case "model", "adapter":
|
||||||
var baseLayers []*layerGGML
|
var baseLayers []*layerGGML
|
||||||
if name := model.ParseName(c.Args); name.IsValid() {
|
if name := model.ParseName(c.Args); name.IsValid() {
|
||||||
baseLayers, err = parseFromModel(ctx, name, fn)
|
baseLayers, version, err = parseFromModel(ctx, name, fn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -529,6 +530,10 @@ func CreateModel(ctx context.Context, name model.Name, modelFileDir, quantizatio
|
|||||||
}
|
}
|
||||||
|
|
||||||
messages = append(messages, &api.Message{Role: role, Content: content})
|
messages = append(messages, &api.Message{Role: role, Content: content})
|
||||||
|
case "ollama":
|
||||||
|
if version == "" {
|
||||||
|
version = c.Args
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
ps, err := api.FormatParams(map[string][]string{c.Name: {c.Args}})
|
ps, err := api.FormatParams(map[string][]string{c.Name: {c.Args}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -642,7 +647,7 @@ func CreateModel(ctx context.Context, name model.Name, modelFileDir, quantizatio
|
|||||||
old, _ := ParseNamedManifest(name)
|
old, _ := ParseNamedManifest(name)
|
||||||
|
|
||||||
fn(api.ProgressResponse{Status: "writing manifest"})
|
fn(api.ProgressResponse{Status: "writing manifest"})
|
||||||
if err := WriteManifest(name, layer, layers); err != nil {
|
if err := WriteManifest(name, version, layer, layers); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ type Manifest struct {
|
|||||||
MediaType string `json:"mediaType"`
|
MediaType string `json:"mediaType"`
|
||||||
Config *Layer `json:"config"`
|
Config *Layer `json:"config"`
|
||||||
Layers []*Layer `json:"layers"`
|
Layers []*Layer `json:"layers"`
|
||||||
|
Ollama string `json:"string,omitempty"`
|
||||||
|
|
||||||
filepath string
|
filepath string
|
||||||
fi os.FileInfo
|
fi os.FileInfo
|
||||||
@ -93,7 +94,7 @@ func ParseNamedManifest(n model.Name) (*Manifest, error) {
|
|||||||
return &m, nil
|
return &m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func WriteManifest(name model.Name, config *Layer, layers []*Layer) error {
|
func WriteManifest(name model.Name, ollama string, config *Layer, layers []*Layer) error {
|
||||||
manifests, err := GetManifestPath()
|
manifests, err := GetManifestPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -115,6 +116,7 @@ func WriteManifest(name model.Name, config *Layer, layers []*Layer) error {
|
|||||||
MediaType: "application/vnd.docker.distribution.manifest.v2+json",
|
MediaType: "application/vnd.docker.distribution.manifest.v2+json",
|
||||||
Config: config,
|
Config: config,
|
||||||
Layers: layers,
|
Layers: layers,
|
||||||
|
Ollama: ollama,
|
||||||
}
|
}
|
||||||
|
|
||||||
return json.NewEncoder(f).Encode(m)
|
return json.NewEncoder(f).Encode(m)
|
||||||
|
@ -30,26 +30,27 @@ type layerGGML struct {
|
|||||||
*llm.GGML
|
*llm.GGML
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseFromModel(ctx context.Context, name model.Name, fn func(api.ProgressResponse)) (layers []*layerGGML, err error) {
|
func parseFromModel(ctx context.Context, name model.Name, fn func(api.ProgressResponse)) (layers []*layerGGML, version string, err error) {
|
||||||
m, err := ParseNamedManifest(name)
|
m, err := ParseNamedManifest(name)
|
||||||
switch {
|
switch {
|
||||||
case errors.Is(err, os.ErrNotExist):
|
case errors.Is(err, os.ErrNotExist):
|
||||||
if err := PullModel(ctx, name.String(), ®istryOptions{}, fn); err != nil {
|
if err := PullModel(ctx, name.String(), ®istryOptions{}, fn); err != nil {
|
||||||
return nil, err
|
return nil, version, err
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err = ParseNamedManifest(name)
|
m, err = ParseNamedManifest(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, version, err
|
||||||
}
|
}
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return nil, err
|
return nil, version, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
version = m.Ollama
|
||||||
for _, layer := range m.Layers {
|
for _, layer := range m.Layers {
|
||||||
layer, err := NewLayerFromLayer(layer.Digest, layer.MediaType, name.DisplayShortest())
|
layer, err := NewLayerFromLayer(layer.Digest, layer.MediaType, name.DisplayShortest())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, version, err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch layer.MediaType {
|
switch layer.MediaType {
|
||||||
@ -58,18 +59,18 @@ func parseFromModel(ctx context.Context, name model.Name, fn func(api.ProgressRe
|
|||||||
"application/vnd.ollama.image.adapter":
|
"application/vnd.ollama.image.adapter":
|
||||||
blobpath, err := GetBlobsPath(layer.Digest)
|
blobpath, err := GetBlobsPath(layer.Digest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, version, err
|
||||||
}
|
}
|
||||||
|
|
||||||
blob, err := os.Open(blobpath)
|
blob, err := os.Open(blobpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, version, err
|
||||||
}
|
}
|
||||||
defer blob.Close()
|
defer blob.Close()
|
||||||
|
|
||||||
ggml, _, err := llm.DecodeGGML(blob, 0)
|
ggml, _, err := llm.DecodeGGML(blob, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, version, err
|
||||||
}
|
}
|
||||||
|
|
||||||
layers = append(layers, &layerGGML{layer, ggml})
|
layers = append(layers, &layerGGML{layer, ggml})
|
||||||
@ -78,7 +79,7 @@ func parseFromModel(ctx context.Context, name model.Name, fn func(api.ProgressRe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return layers, nil
|
return layers, version, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractFromZipFile(p string, file *os.File, fn func(api.ProgressResponse)) error {
|
func extractFromZipFile(p string, file *os.File, fn func(api.ProgressResponse)) error {
|
||||||
|
@ -498,7 +498,7 @@ func TestCreateTemplateSystem(t *testing.T) {
|
|||||||
Modelfile: fmt.Sprintf("FROM %s\nTEMPLATE {{ .Prompt", createBinFile(t, nil, nil)),
|
Modelfile: fmt.Sprintf("FROM %s\nTEMPLATE {{ .Prompt", createBinFile(t, nil, nil)),
|
||||||
Stream: &stream,
|
Stream: &stream,
|
||||||
})
|
})
|
||||||
|
|
||||||
if w.Code != http.StatusBadRequest {
|
if w.Code != http.StatusBadRequest {
|
||||||
t.Fatalf("expected status code 400, actual %d", w.Code)
|
t.Fatalf("expected status code 400, actual %d", w.Code)
|
||||||
}
|
}
|
||||||
@ -510,7 +510,7 @@ func TestCreateTemplateSystem(t *testing.T) {
|
|||||||
Modelfile: fmt.Sprintf("FROM %s\nTEMPLATE {{ if .Prompt }}", createBinFile(t, nil, nil)),
|
Modelfile: fmt.Sprintf("FROM %s\nTEMPLATE {{ if .Prompt }}", createBinFile(t, nil, nil)),
|
||||||
Stream: &stream,
|
Stream: &stream,
|
||||||
})
|
})
|
||||||
|
|
||||||
if w.Code != http.StatusBadRequest {
|
if w.Code != http.StatusBadRequest {
|
||||||
t.Fatalf("expected status code 400, actual %d", w.Code)
|
t.Fatalf("expected status code 400, actual %d", w.Code)
|
||||||
}
|
}
|
||||||
@ -522,7 +522,7 @@ func TestCreateTemplateSystem(t *testing.T) {
|
|||||||
Modelfile: fmt.Sprintf("FROM %s\nTEMPLATE {{ Prompt }}", createBinFile(t, nil, nil)),
|
Modelfile: fmt.Sprintf("FROM %s\nTEMPLATE {{ Prompt }}", createBinFile(t, nil, nil)),
|
||||||
Stream: &stream,
|
Stream: &stream,
|
||||||
})
|
})
|
||||||
|
|
||||||
if w.Code != http.StatusBadRequest {
|
if w.Code != http.StatusBadRequest {
|
||||||
t.Fatalf("expected status code 400, actual %d", w.Code)
|
t.Fatalf("expected status code 400, actual %d", w.Code)
|
||||||
}
|
}
|
||||||
@ -623,3 +623,119 @@ func TestCreateDetectTemplate(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateVersion(t *testing.T) {
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
|
||||||
|
p := t.TempDir()
|
||||||
|
t.Setenv("OLLAMA_MODELS", p)
|
||||||
|
envconfig.LoadConfig()
|
||||||
|
var s Server
|
||||||
|
|
||||||
|
w := createRequest(t, s.CreateModelHandler, api.CreateRequest{
|
||||||
|
Name: "test",
|
||||||
|
Modelfile: fmt.Sprintf("FROM %s\nOLLAMA 0.2.3\nLICENSE MIT\nLICENSE Apache-2.0", createBinFile(t, nil, nil)),
|
||||||
|
Stream: &stream,
|
||||||
|
})
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected status code 200, actual %d", w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkFileExists(t, filepath.Join(p, "manifests", "*", "*", "*", "*"), []string{
|
||||||
|
filepath.Join(p, "manifests", "registry.ollama.ai", "library", "test", "latest"),
|
||||||
|
})
|
||||||
|
|
||||||
|
f, err := os.Open(filepath.Join(p, "manifests", "registry.ollama.ai", "library", "test", "latest"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
bts := json.NewDecoder(f)
|
||||||
|
|
||||||
|
var m Manifest
|
||||||
|
if err := bts.Decode(&m); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Ollama != "v0.2.3" {
|
||||||
|
t.Errorf("got %s != want v0.2.3", m.Ollama)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("no version", func(t *testing.T) {
|
||||||
|
w = createRequest(t, s.CreateModelHandler, api.CreateRequest{
|
||||||
|
Name: "noversion",
|
||||||
|
Modelfile: fmt.Sprintf("FROM %s\nLICENSE MIT\nLICENSE Apache-2.0", createBinFile(t, nil, nil)),
|
||||||
|
Stream: &stream,
|
||||||
|
})
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected status code 200, actual %d", w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkFileExists(t, filepath.Join(p, "manifests", "*", "*", "noversion", "*"), []string{
|
||||||
|
filepath.Join(p, "manifests", "registry.ollama.ai", "library", "noversion", "latest"),
|
||||||
|
})
|
||||||
|
|
||||||
|
f, err := os.Open(filepath.Join(p, "manifests", "registry.ollama.ai", "library", "noversion", "latest"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
bts := json.NewDecoder(f)
|
||||||
|
var m Manifest
|
||||||
|
if err := bts.Decode(&m); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Ollama != "" {
|
||||||
|
t.Errorf("got %s != want empty", m.Ollama)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("invalid version", func(t *testing.T) {
|
||||||
|
w = createRequest(t, s.CreateModelHandler, api.CreateRequest{
|
||||||
|
Name: "invalid",
|
||||||
|
Modelfile: fmt.Sprintf("FROM %s\nOLLAMA 0..400", createBinFile(t, nil, nil)),
|
||||||
|
Stream: &stream,
|
||||||
|
})
|
||||||
|
|
||||||
|
if w.Code != http.StatusBadRequest {
|
||||||
|
t.Fatalf("expected status code 400, actual %d", w.Code)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("from valid version", func(t *testing.T) {
|
||||||
|
w = createRequest(t, s.CreateModelHandler, api.CreateRequest{
|
||||||
|
Name: "fromvalid",
|
||||||
|
Modelfile: "FROM test",
|
||||||
|
Stream: &stream,
|
||||||
|
})
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected status code 200, actual %d", w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkFileExists(t, filepath.Join(p, "manifests", "*", "*", "fromvalid", "*"), []string{
|
||||||
|
filepath.Join(p, "manifests", "registry.ollama.ai", "library", "fromvalid", "latest"),
|
||||||
|
})
|
||||||
|
|
||||||
|
f, err := os.Open(filepath.Join(p, "manifests", "registry.ollama.ai", "library", "fromvalid", "latest"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
bts := json.NewDecoder(f)
|
||||||
|
|
||||||
|
var m Manifest
|
||||||
|
if err := bts.Decode(&m); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Ollama != "v0.2.3" {
|
||||||
|
t.Errorf("got %s != want v0.2.3", m.Ollama)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -99,7 +99,7 @@ func TestDeleteDuplicateLayers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create a manifest with duplicate layers
|
// create a manifest with duplicate layers
|
||||||
if err := WriteManifest(n, config, []*Layer{config}); err != nil {
|
if err := WriteManifest(n, "", config, []*Layer{config}); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user