Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
OpenDAS
ollama
Commits
eafc607a
Commit
eafc607a
authored
Jun 29, 2024
by
Michael Yang
Browse files
convert: only extract large files
parent
781fc2d5
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
117 additions
and
197 deletions
+117
-197
convert/convert.go
convert/convert.go
+5
-6
convert/convert_test.go
convert/convert_test.go
+4
-3
convert/fs.go
convert/fs.go
+58
-0
convert/reader.go
convert/reader.go
+5
-5
convert/reader_safetensors.go
convert/reader_safetensors.go
+14
-6
convert/reader_torch.go
convert/reader_torch.go
+2
-1
convert/tokenizer.go
convert/tokenizer.go
+11
-11
convert/tokenizer_spm.go
convert/tokenizer_spm.go
+4
-4
server/model.go
server/model.go
+14
-59
server/model_test.go
server/model_test.go
+0
-102
No files found.
convert/convert.go
View file @
eafc607a
...
@@ -5,9 +5,8 @@ import (
...
@@ -5,9 +5,8 @@ import (
"errors"
"errors"
"fmt"
"fmt"
"io"
"io"
"io/fs"
"log/slog"
"log/slog"
"os"
"path/filepath"
"github.com/ollama/ollama/llm"
"github.com/ollama/ollama/llm"
)
)
...
@@ -67,8 +66,8 @@ type Converter interface {
...
@@ -67,8 +66,8 @@ type Converter interface {
// and files it finds in the input path.
// and files it finds in the input path.
// Supported input model formats include safetensors.
// Supported input model formats include safetensors.
// Supported input tokenizers files include tokenizer.json (preferred) and tokenizer.model.
// Supported input tokenizers files include tokenizer.json (preferred) and tokenizer.model.
func
Convert
(
path
string
,
ws
io
.
WriteSeeker
)
error
{
func
Convert
(
fsys
fs
.
FS
,
ws
io
.
WriteSeeker
)
error
{
bts
,
err
:=
o
s
.
ReadFile
(
f
ilepath
.
Join
(
path
,
"config.json"
)
)
bts
,
err
:=
f
s
.
ReadFile
(
f
sys
,
"config.json"
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
...
@@ -98,7 +97,7 @@ func Convert(path string, ws io.WriteSeeker) error {
...
@@ -98,7 +97,7 @@ func Convert(path string, ws io.WriteSeeker) error {
return
err
return
err
}
}
t
,
err
:=
parseTokenizer
(
path
,
conv
.
specialTokenTypes
())
t
,
err
:=
parseTokenizer
(
fsys
,
conv
.
specialTokenTypes
())
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
...
@@ -114,7 +113,7 @@ func Convert(path string, ws io.WriteSeeker) error {
...
@@ -114,7 +113,7 @@ func Convert(path string, ws io.WriteSeeker) error {
slog
.
Debug
(
"vocabulary"
,
"size"
,
len
(
t
.
Vocabulary
.
Tokens
))
slog
.
Debug
(
"vocabulary"
,
"size"
,
len
(
t
.
Vocabulary
.
Tokens
))
}
}
ts
,
err
:=
parseTensors
(
path
)
ts
,
err
:=
parseTensors
(
fsys
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
...
...
convert/convert_test.go
View file @
eafc607a
...
@@ -6,6 +6,7 @@ import (
...
@@ -6,6 +6,7 @@ import (
"flag"
"flag"
"fmt"
"fmt"
"io"
"io"
"io/fs"
"log/slog"
"log/slog"
"math"
"math"
"os"
"os"
...
@@ -17,7 +18,7 @@ import (
...
@@ -17,7 +18,7 @@ import (
"golang.org/x/exp/maps"
"golang.org/x/exp/maps"
)
)
func
convertFull
(
t
*
testing
.
T
,
d
string
)
(
*
os
.
File
,
llm
.
KV
,
llm
.
Tensors
)
{
func
convertFull
(
t
*
testing
.
T
,
fsys
fs
.
FS
)
(
*
os
.
File
,
llm
.
KV
,
llm
.
Tensors
)
{
t
.
Helper
()
t
.
Helper
()
f
,
err
:=
os
.
CreateTemp
(
t
.
TempDir
(),
"f16"
)
f
,
err
:=
os
.
CreateTemp
(
t
.
TempDir
(),
"f16"
)
...
@@ -26,7 +27,7 @@ func convertFull(t *testing.T, d string) (*os.File, llm.KV, llm.Tensors) {
...
@@ -26,7 +27,7 @@ func convertFull(t *testing.T, d string) (*os.File, llm.KV, llm.Tensors) {
}
}
defer
f
.
Close
()
defer
f
.
Close
()
if
err
:=
Convert
(
d
,
f
);
err
!=
nil
{
if
err
:=
Convert
(
fsys
,
f
);
err
!=
nil
{
t
.
Fatal
(
err
)
t
.
Fatal
(
err
)
}
}
...
@@ -76,7 +77,7 @@ func TestConvertFull(t *testing.T) {
...
@@ -76,7 +77,7 @@ func TestConvertFull(t *testing.T) {
t
.
Skipf
(
"%s not found"
,
p
)
t
.
Skipf
(
"%s not found"
,
p
)
}
}
f
,
kv
,
tensors
:=
convertFull
(
t
,
p
)
f
,
kv
,
tensors
:=
convertFull
(
t
,
os
.
DirFS
(
p
)
)
actual
:=
make
(
map
[
string
]
string
)
actual
:=
make
(
map
[
string
]
string
)
for
k
,
v
:=
range
kv
{
for
k
,
v
:=
range
kv
{
if
s
,
ok
:=
v
.
(
json
.
Marshaler
);
!
ok
{
if
s
,
ok
:=
v
.
(
json
.
Marshaler
);
!
ok
{
...
...
convert/fs.go
0 → 100644
View file @
eafc607a
package
convert
import
(
"archive/zip"
"errors"
"io"
"io/fs"
"os"
"path/filepath"
)
type
ZipReader
struct
{
r
*
zip
.
Reader
p
string
// limit is the maximum size of a file that can be read directly
// from the zip archive. Files larger than this size will be extracted
limit
int64
}
func
NewZipReader
(
r
*
zip
.
Reader
,
p
string
,
limit
int64
)
fs
.
FS
{
return
&
ZipReader
{
r
,
p
,
limit
}
}
func
(
z
*
ZipReader
)
Open
(
name
string
)
(
fs
.
File
,
error
)
{
r
,
err
:=
z
.
r
.
Open
(
name
)
if
err
!=
nil
{
return
nil
,
err
}
defer
r
.
Close
()
if
fi
,
err
:=
r
.
Stat
();
err
!=
nil
{
return
nil
,
err
}
else
if
fi
.
Size
()
<
z
.
limit
{
return
r
,
nil
}
if
!
filepath
.
IsLocal
(
name
)
{
return
nil
,
zip
.
ErrInsecurePath
}
n
:=
filepath
.
Join
(
z
.
p
,
name
)
if
_
,
err
:=
os
.
Stat
(
n
);
errors
.
Is
(
err
,
os
.
ErrNotExist
)
{
w
,
err
:=
os
.
Create
(
n
)
if
err
!=
nil
{
return
nil
,
err
}
defer
w
.
Close
()
if
_
,
err
:=
io
.
Copy
(
w
,
r
);
err
!=
nil
{
return
nil
,
err
}
}
else
if
err
!=
nil
{
return
nil
,
err
}
return
os
.
Open
(
n
)
}
convert/reader.go
View file @
eafc607a
...
@@ -3,7 +3,7 @@ package convert
...
@@ -3,7 +3,7 @@ package convert
import
(
import
(
"errors"
"errors"
"io"
"io"
"
path/filepath
"
"
io/fs
"
"strings"
"strings"
)
)
...
@@ -55,8 +55,8 @@ func (t *tensorBase) SetRepacker(fn repacker) {
...
@@ -55,8 +55,8 @@ func (t *tensorBase) SetRepacker(fn repacker) {
type
repacker
func
(
string
,
[]
float32
,
[]
uint64
)
([]
float32
,
error
)
type
repacker
func
(
string
,
[]
float32
,
[]
uint64
)
([]
float32
,
error
)
func
parseTensors
(
d
string
)
([]
Tensor
,
error
)
{
func
parseTensors
(
fsys
fs
.
FS
)
([]
Tensor
,
error
)
{
patterns
:=
map
[
string
]
func
(
...
string
)
([]
Tensor
,
error
){
patterns
:=
map
[
string
]
func
(
fs
.
FS
,
...
string
)
([]
Tensor
,
error
){
"model-*-of-*.safetensors"
:
parseSafetensors
,
"model-*-of-*.safetensors"
:
parseSafetensors
,
"model.safetensors"
:
parseSafetensors
,
"model.safetensors"
:
parseSafetensors
,
"pytorch_model-*-of-*.bin"
:
parseTorch
,
"pytorch_model-*-of-*.bin"
:
parseTorch
,
...
@@ -65,13 +65,13 @@ func parseTensors(d string) ([]Tensor, error) {
...
@@ -65,13 +65,13 @@ func parseTensors(d string) ([]Tensor, error) {
}
}
for
pattern
,
parseFn
:=
range
patterns
{
for
pattern
,
parseFn
:=
range
patterns
{
matches
,
err
:=
f
ilepath
.
Glob
(
filepath
.
Join
(
d
,
pattern
)
)
matches
,
err
:=
f
s
.
Glob
(
fsys
,
pattern
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
if
len
(
matches
)
>
0
{
if
len
(
matches
)
>
0
{
return
parseFn
(
matches
...
)
return
parseFn
(
fsys
,
matches
...
)
}
}
}
}
...
...
convert/reader_safetensors.go
View file @
eafc607a
...
@@ -6,7 +6,7 @@ import (
...
@@ -6,7 +6,7 @@ import (
"encoding/json"
"encoding/json"
"fmt"
"fmt"
"io"
"io"
"
o
s"
"
io/f
s"
"slices"
"slices"
"github.com/d4l3k/go-bfloat16"
"github.com/d4l3k/go-bfloat16"
...
@@ -20,10 +20,10 @@ type safetensorMetadata struct {
...
@@ -20,10 +20,10 @@ type safetensorMetadata struct {
Offsets
[]
int64
`json:"data_offsets"`
Offsets
[]
int64
`json:"data_offsets"`
}
}
func
parseSafetensors
(
ps
...
string
)
([]
Tensor
,
error
)
{
func
parseSafetensors
(
fsys
fs
.
FS
,
ps
...
string
)
([]
Tensor
,
error
)
{
var
ts
[]
Tensor
var
ts
[]
Tensor
for
_
,
p
:=
range
ps
{
for
_
,
p
:=
range
ps
{
f
,
err
:=
o
s
.
Open
(
p
)
f
,
err
:=
fsy
s
.
Open
(
p
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
...
@@ -50,6 +50,7 @@ func parseSafetensors(ps ...string) ([]Tensor, error) {
...
@@ -50,6 +50,7 @@ func parseSafetensors(ps ...string) ([]Tensor, error) {
for
_
,
key
:=
range
keys
{
for
_
,
key
:=
range
keys
{
if
value
:=
headers
[
key
];
value
.
Type
!=
""
{
if
value
:=
headers
[
key
];
value
.
Type
!=
""
{
ts
=
append
(
ts
,
safetensor
{
ts
=
append
(
ts
,
safetensor
{
fs
:
fsys
,
path
:
p
,
path
:
p
,
dtype
:
value
.
Type
,
dtype
:
value
.
Type
,
offset
:
safetensorsPad
(
n
,
value
.
Offsets
[
0
]),
offset
:
safetensorsPad
(
n
,
value
.
Offsets
[
0
]),
...
@@ -72,6 +73,7 @@ func safetensorsPad(n, offset int64) int64 {
...
@@ -72,6 +73,7 @@ func safetensorsPad(n, offset int64) int64 {
}
}
type
safetensor
struct
{
type
safetensor
struct
{
fs
fs
.
FS
path
string
path
string
dtype
string
dtype
string
offset
int64
offset
int64
...
@@ -80,15 +82,21 @@ type safetensor struct {
...
@@ -80,15 +82,21 @@ type safetensor struct {
}
}
func
(
st
safetensor
)
WriteTo
(
w
io
.
Writer
)
(
int64
,
error
)
{
func
(
st
safetensor
)
WriteTo
(
w
io
.
Writer
)
(
int64
,
error
)
{
f
,
err
:=
o
s
.
Open
(
st
.
path
)
f
,
err
:=
st
.
f
s
.
Open
(
st
.
path
)
if
err
!=
nil
{
if
err
!=
nil
{
return
0
,
err
return
0
,
err
}
}
defer
f
.
Close
()
defer
f
.
Close
()
if
_
,
err
=
f
.
Seek
(
st
.
offset
,
io
.
SeekStart
);
err
!=
nil
{
if
seeker
,
ok
:=
f
.
(
io
.
Seeker
);
ok
{
if
_
,
err
:=
seeker
.
Seek
(
st
.
offset
,
io
.
SeekStart
);
err
!=
nil
{
return
0
,
err
return
0
,
err
}
}
}
else
{
if
_
,
err
:=
io
.
CopyN
(
io
.
Discard
,
f
,
st
.
offset
);
err
!=
nil
{
return
0
,
err
}
}
var
f32s
[]
float32
var
f32s
[]
float32
switch
st
.
dtype
{
switch
st
.
dtype
{
...
...
convert/reader_torch.go
View file @
eafc607a
...
@@ -2,12 +2,13 @@ package convert
...
@@ -2,12 +2,13 @@ package convert
import
(
import
(
"io"
"io"
"io/fs"
"github.com/nlpodyssey/gopickle/pytorch"
"github.com/nlpodyssey/gopickle/pytorch"
"github.com/nlpodyssey/gopickle/types"
"github.com/nlpodyssey/gopickle/types"
)
)
func
parseTorch
(
ps
...
string
)
([]
Tensor
,
error
)
{
func
parseTorch
(
fsys
fs
.
FS
,
ps
...
string
)
([]
Tensor
,
error
)
{
var
ts
[]
Tensor
var
ts
[]
Tensor
for
_
,
p
:=
range
ps
{
for
_
,
p
:=
range
ps
{
pt
,
err
:=
pytorch
.
Load
(
p
)
pt
,
err
:=
pytorch
.
Load
(
p
)
...
...
convert/tokenizer.go
View file @
eafc607a
...
@@ -7,9 +7,9 @@ import (
...
@@ -7,9 +7,9 @@ import (
"encoding/json"
"encoding/json"
"errors"
"errors"
"fmt"
"fmt"
"io/fs"
"log/slog"
"log/slog"
"os"
"os"
"path/filepath"
"slices"
"slices"
)
)
...
@@ -32,8 +32,8 @@ type Tokenizer struct {
...
@@ -32,8 +32,8 @@ type Tokenizer struct {
Template
string
Template
string
}
}
func
parseTokenizer
(
d
string
,
specialTokenTypes
[]
string
)
(
*
Tokenizer
,
error
)
{
func
parseTokenizer
(
fsys
fs
.
FS
,
specialTokenTypes
[]
string
)
(
*
Tokenizer
,
error
)
{
v
,
err
:=
parseVocabulary
(
d
)
v
,
err
:=
parseVocabulary
(
fsys
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
...
@@ -44,7 +44,7 @@ func parseTokenizer(d string, specialTokenTypes []string) (*Tokenizer, error) {
...
@@ -44,7 +44,7 @@ func parseTokenizer(d string, specialTokenTypes []string) (*Tokenizer, error) {
}
}
addedTokens
:=
make
(
map
[
string
]
token
)
addedTokens
:=
make
(
map
[
string
]
token
)
if
f
,
err
:=
o
s
.
Open
(
filepath
.
Join
(
d
,
"tokenizer.json"
)
)
;
errors
.
Is
(
err
,
os
.
ErrNotExist
)
{
if
f
,
err
:=
fsy
s
.
Open
(
"tokenizer.json"
);
errors
.
Is
(
err
,
os
.
ErrNotExist
)
{
}
else
if
err
!=
nil
{
}
else
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
else
{
}
else
{
...
@@ -87,7 +87,7 @@ func parseTokenizer(d string, specialTokenTypes []string) (*Tokenizer, error) {
...
@@ -87,7 +87,7 @@ func parseTokenizer(d string, specialTokenTypes []string) (*Tokenizer, error) {
}
}
}
}
if
f
,
err
:=
o
s
.
Open
(
filepath
.
Join
(
d
,
"tokenizer_config.json"
)
)
;
errors
.
Is
(
err
,
os
.
ErrNotExist
)
{
if
f
,
err
:=
fsy
s
.
Open
(
"tokenizer_config.json"
);
errors
.
Is
(
err
,
os
.
ErrNotExist
)
{
}
else
if
err
!=
nil
{
}
else
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
else
{
}
else
{
...
@@ -172,8 +172,8 @@ type Vocabulary struct {
...
@@ -172,8 +172,8 @@ type Vocabulary struct {
Types
[]
int32
Types
[]
int32
}
}
func
parseVocabularyFromTokenizer
(
p
string
)
(
*
Vocabulary
,
error
)
{
func
parseVocabularyFromTokenizer
(
fsys
fs
.
FS
)
(
*
Vocabulary
,
error
)
{
f
,
err
:=
o
s
.
Open
(
filepath
.
Join
(
p
,
"tokenizer.json"
)
)
f
,
err
:=
fsy
s
.
Open
(
"tokenizer.json"
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
...
@@ -219,20 +219,20 @@ func parseVocabularyFromTokenizer(p string) (*Vocabulary, error) {
...
@@ -219,20 +219,20 @@ func parseVocabularyFromTokenizer(p string) (*Vocabulary, error) {
return
&
v
,
nil
return
&
v
,
nil
}
}
func
parseVocabulary
(
d
string
)
(
*
Vocabulary
,
error
)
{
func
parseVocabulary
(
fsys
fs
.
FS
)
(
*
Vocabulary
,
error
)
{
patterns
:=
map
[
string
]
func
(
string
)
(
*
Vocabulary
,
error
){
patterns
:=
map
[
string
]
func
(
fs
.
FS
)
(
*
Vocabulary
,
error
){
"tokenizer.model"
:
parseSentencePiece
,
"tokenizer.model"
:
parseSentencePiece
,
"tokenizer.json"
:
parseVocabularyFromTokenizer
,
"tokenizer.json"
:
parseVocabularyFromTokenizer
,
}
}
for
pattern
,
parseFn
:=
range
patterns
{
for
pattern
,
parseFn
:=
range
patterns
{
if
_
,
err
:=
o
s
.
Stat
(
f
ilepath
.
Join
(
d
,
pattern
)
)
;
errors
.
Is
(
err
,
os
.
ErrNotExist
)
{
if
_
,
err
:=
f
s
.
Stat
(
f
sys
,
pattern
);
errors
.
Is
(
err
,
os
.
ErrNotExist
)
{
continue
continue
}
else
if
err
!=
nil
{
}
else
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
return
parseFn
(
d
)
return
parseFn
(
fsys
)
}
}
return
nil
,
errors
.
New
(
"unknown tensor format"
)
return
nil
,
errors
.
New
(
"unknown tensor format"
)
...
...
convert/tokenizer_spm.go
View file @
eafc607a
...
@@ -5,8 +5,8 @@ import (
...
@@ -5,8 +5,8 @@ import (
"encoding/json"
"encoding/json"
"errors"
"errors"
"fmt"
"fmt"
"io/fs"
"os"
"os"
"path/filepath"
"slices"
"slices"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/proto"
...
@@ -14,8 +14,8 @@ import (
...
@@ -14,8 +14,8 @@ import (
"github.com/ollama/ollama/convert/sentencepiece"
"github.com/ollama/ollama/convert/sentencepiece"
)
)
func
parseSentencePiece
(
d
string
)
(
*
Vocabulary
,
error
)
{
func
parseSentencePiece
(
fsys
fs
.
FS
)
(
*
Vocabulary
,
error
)
{
bts
,
err
:=
o
s
.
ReadFile
(
f
ilepath
.
Join
(
d
,
"tokenizer.model"
)
)
bts
,
err
:=
f
s
.
ReadFile
(
f
sys
,
"tokenizer.model"
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
...
@@ -41,7 +41,7 @@ func parseSentencePiece(d string) (*Vocabulary, error) {
...
@@ -41,7 +41,7 @@ func parseSentencePiece(d string) (*Vocabulary, error) {
}
}
}
}
f
,
err
:=
o
s
.
Open
(
filepath
.
Join
(
d
,
"added_tokens.json"
)
)
f
,
err
:=
fsy
s
.
Open
(
"added_tokens.json"
)
if
errors
.
Is
(
err
,
os
.
ErrNotExist
)
{
if
errors
.
Is
(
err
,
os
.
ErrNotExist
)
{
return
&
v
,
nil
return
&
v
,
nil
}
else
if
err
!=
nil
{
}
else
if
err
!=
nil
{
...
...
server/model.go
View file @
eafc607a
...
@@ -81,88 +81,43 @@ func parseFromModel(ctx context.Context, name model.Name, fn func(api.ProgressRe
...
@@ -81,88 +81,43 @@ func parseFromModel(ctx context.Context, name model.Name, fn func(api.ProgressRe
return
layers
,
nil
return
layers
,
nil
}
}
func
extractFromZipFile
(
p
string
,
file
*
os
.
File
,
fn
func
(
api
.
ProgressResponse
))
error
{
func
parseFromZipFile
(
_
context
.
Context
,
f
*
os
.
File
,
digest
string
,
fn
func
(
api
.
ProgressResponse
))
(
layers
[]
*
layerGGML
,
err
error
)
{
stat
,
err
:=
file
.
Stat
()
fi
,
err
:=
f
.
Stat
()
if
err
!=
nil
{
return
err
}
r
,
err
:=
zip
.
NewReader
(
file
,
stat
.
Size
())
if
err
!=
nil
{
return
err
}
fn
(
api
.
ProgressResponse
{
Status
:
"unpacking model metadata"
})
for
_
,
f
:=
range
r
.
File
{
if
!
filepath
.
IsLocal
(
f
.
Name
)
{
return
fmt
.
Errorf
(
"%w: %s"
,
zip
.
ErrInsecurePath
,
f
.
Name
)
}
n
:=
filepath
.
Join
(
p
,
f
.
Name
)
if
err
:=
os
.
MkdirAll
(
filepath
.
Dir
(
n
),
0
o750
);
err
!=
nil
{
return
err
}
// TODO(mxyng): this should not write out all files to disk
outfile
,
err
:=
os
.
Create
(
n
)
if
err
!=
nil
{
return
err
}
defer
outfile
.
Close
()
infile
,
err
:=
f
.
Open
()
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
nil
,
err
}
defer
infile
.
Close
()
if
_
,
err
=
io
.
Copy
(
outfile
,
infile
);
err
!=
nil
{
return
err
}
if
err
:=
outfile
.
Close
();
err
!=
nil
{
return
err
}
if
err
:=
infile
.
Close
();
err
!=
nil
{
return
err
}
}
}
return
nil
r
,
err
:=
zip
.
NewReader
(
f
,
fi
.
Size
())
}
func
parseFromZipFile
(
_
context
.
Context
,
file
*
os
.
File
,
digest
string
,
fn
func
(
api
.
ProgressResponse
))
(
layers
[]
*
layerGGML
,
err
error
)
{
tempDir
,
err
:=
os
.
MkdirTemp
(
filepath
.
Dir
(
file
.
Name
()),
""
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
defer
os
.
RemoveAll
(
tempDir
)
if
err
:=
extractFromZipFile
(
tempDir
,
file
,
fn
);
err
!=
nil
{
p
,
err
:=
os
.
MkdirTemp
(
filepath
.
Dir
(
f
.
Name
()),
""
)
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
defer
os
.
RemoveAll
(
p
)
fn
(
api
.
ProgressResponse
{
Status
:
"converting model"
})
fn
(
api
.
ProgressResponse
{
Status
:
"converting model"
})
// TODO(mxyng): this should write directly into a layer
// TODO(mxyng): this should write directly into a layer
// e.g. NewLayer(arch.Reader(), "application/vnd.ollama.image.model")
// e.g. NewLayer(arch.Reader(), "application/vnd.ollama.image.model")
t
emp
,
err
:=
os
.
CreateTemp
(
tempDir
,
"fp16"
)
t
,
err
:=
os
.
CreateTemp
(
p
,
"fp16"
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
defer
t
emp
.
Close
()
defer
t
.
Close
()
defer
os
.
Remove
(
t
emp
.
Name
())
defer
os
.
Remove
(
t
.
Name
())
if
err
:=
convert
.
Convert
(
tempDir
,
temp
);
err
!=
nil
{
fn
(
api
.
ProgressResponse
{
Status
:
"converting model"
})
if
err
:=
convert
.
Convert
(
convert
.
NewZipReader
(
r
,
p
,
32
<<
20
),
t
);
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
if
_
,
err
:=
t
emp
.
Seek
(
0
,
io
.
SeekStart
);
err
!=
nil
{
if
_
,
err
:=
t
.
Seek
(
0
,
io
.
SeekStart
);
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
layer
,
err
:=
NewLayer
(
t
emp
,
"application/vnd.ollama.image.model"
)
layer
,
err
:=
NewLayer
(
t
,
"application/vnd.ollama.image.model"
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
...
...
server/model_test.go
View file @
eafc607a
package
server
package
server
import
(
import
(
"archive/zip"
"bytes"
"bytes"
"encoding/json"
"encoding/json"
"errors"
"fmt"
"fmt"
"io"
"os"
"os"
"path/filepath"
"path/filepath"
"slices"
"strings"
"testing"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp"
...
@@ -18,103 +13,6 @@ import (
...
@@ -18,103 +13,6 @@ import (
"github.com/ollama/ollama/template"
"github.com/ollama/ollama/template"
)
)
func
createZipFile
(
t
*
testing
.
T
,
name
string
)
*
os
.
File
{
t
.
Helper
()
f
,
err
:=
os
.
CreateTemp
(
t
.
TempDir
(),
""
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
zf
:=
zip
.
NewWriter
(
f
)
defer
zf
.
Close
()
zh
,
err
:=
zf
.
CreateHeader
(
&
zip
.
FileHeader
{
Name
:
name
})
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
_
,
err
:=
io
.
Copy
(
zh
,
bytes
.
NewReader
([]
byte
(
""
)));
err
!=
nil
{
t
.
Fatal
(
err
)
}
return
f
}
func
TestExtractFromZipFile
(
t
*
testing
.
T
)
{
cases
:=
[]
struct
{
name
string
expect
[]
string
err
error
}{
{
name
:
"good"
,
expect
:
[]
string
{
"good"
},
},
{
name
:
strings
.
Join
([]
string
{
"path"
,
".."
,
"to"
,
"good"
},
string
(
os
.
PathSeparator
)),
expect
:
[]
string
{
filepath
.
Join
(
"to"
,
"good"
)},
},
{
name
:
strings
.
Join
([]
string
{
"path"
,
".."
,
"to"
,
".."
,
"good"
},
string
(
os
.
PathSeparator
)),
expect
:
[]
string
{
"good"
},
},
{
name
:
strings
.
Join
([]
string
{
"path"
,
"to"
,
".."
,
".."
,
"good"
},
string
(
os
.
PathSeparator
)),
expect
:
[]
string
{
"good"
},
},
{
name
:
strings
.
Join
([]
string
{
".."
,
".."
,
".."
,
".."
,
".."
,
".."
,
".."
,
".."
,
".."
,
".."
,
".."
,
".."
,
".."
,
".."
,
".."
,
".."
,
"bad"
},
string
(
os
.
PathSeparator
)),
err
:
zip
.
ErrInsecurePath
,
},
{
name
:
strings
.
Join
([]
string
{
"path"
,
".."
,
".."
,
"to"
,
"bad"
},
string
(
os
.
PathSeparator
)),
err
:
zip
.
ErrInsecurePath
,
},
}
for
_
,
tt
:=
range
cases
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
f
:=
createZipFile
(
t
,
tt
.
name
)
defer
f
.
Close
()
tempDir
:=
t
.
TempDir
()
if
err
:=
extractFromZipFile
(
tempDir
,
f
,
func
(
api
.
ProgressResponse
)
{});
!
errors
.
Is
(
err
,
tt
.
err
)
{
t
.
Fatal
(
err
)
}
var
matches
[]
string
if
err
:=
filepath
.
Walk
(
tempDir
,
func
(
p
string
,
fi
os
.
FileInfo
,
err
error
)
error
{
if
err
!=
nil
{
return
err
}
if
!
fi
.
IsDir
()
{
matches
=
append
(
matches
,
p
)
}
return
nil
});
err
!=
nil
{
t
.
Fatal
(
err
)
}
var
actual
[]
string
for
_
,
match
:=
range
matches
{
rel
,
err
:=
filepath
.
Rel
(
tempDir
,
match
)
if
err
!=
nil
{
t
.
Error
(
err
)
}
actual
=
append
(
actual
,
rel
)
}
if
!
slices
.
Equal
(
actual
,
tt
.
expect
)
{
t
.
Fatalf
(
"expected %d files, got %d"
,
len
(
tt
.
expect
),
len
(
matches
))
}
})
}
}
func
readFile
(
t
*
testing
.
T
,
base
,
name
string
)
*
bytes
.
Buffer
{
func
readFile
(
t
*
testing
.
T
,
base
,
name
string
)
*
bytes
.
Buffer
{
t
.
Helper
()
t
.
Helper
()
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment