Compare commits
24 Commits
main
...
jyan/reord
Author | SHA1 | Date | |
---|---|---|---|
|
309307c8f9 | ||
|
f378058b51 | ||
|
574c3540cd | ||
|
d069cf753b | ||
|
64405525b4 | ||
|
dea2204b82 | ||
|
6ee22d5080 | ||
|
703ecccc6b | ||
|
873f334783 | ||
|
fa49bfc0bd | ||
|
fc1b3ee9bf | ||
|
25be20949c | ||
|
903e9df46f | ||
|
40c0f9612e | ||
|
15a0215203 | ||
|
faa3c937cf | ||
|
cf57246aba | ||
|
6fafe4f753 | ||
|
d7c8d4f3f4 | ||
|
3d0fd31f0e | ||
|
554f3bdc0e | ||
|
e75fb73839 | ||
|
2fdebffc8d | ||
|
29ecfe493b |
@ -36,6 +36,7 @@ type ggla struct {
|
|||||||
|
|
||||||
kv KV
|
kv KV
|
||||||
tensors []*Tensor
|
tensors []*Tensor
|
||||||
|
offset int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func newGGLA(container *containerGGLA) *ggla {
|
func newGGLA(container *containerGGLA) *ggla {
|
||||||
@ -50,7 +51,10 @@ func (llm *ggla) KV() KV {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (llm *ggla) Tensors() Tensors {
|
func (llm *ggla) Tensors() Tensors {
|
||||||
return llm.tensors
|
return Tensors{
|
||||||
|
Items: llm.tensors,
|
||||||
|
Offset: llm.offset,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (llm *ggla) decode(rs io.ReadSeeker) (retErr error) {
|
func (llm *ggla) decode(rs io.ReadSeeker) (retErr error) {
|
||||||
|
39
llm/ggml.go
39
llm/ggml.go
@ -112,11 +112,38 @@ func (kv KV) ChatTemplate() string {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tensors []*Tensor
|
// Tensors type as a slice of pointers to Tensor
|
||||||
|
// type Tensors []*Tensor
|
||||||
|
|
||||||
|
type Tensors struct {
|
||||||
|
Items []*Tensor
|
||||||
|
Offset int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement the Len method
|
||||||
|
func (ts Tensors) Len() int {
|
||||||
|
return len(ts.Items)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement the Swap method
|
||||||
|
func (ts Tensors) Swap(i, j int) {
|
||||||
|
ts.Items[i], ts.Items[j] = ts.Items[j], ts.Items[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement the Less method
|
||||||
|
func (ts Tensors) Less(i, j int) bool {
|
||||||
|
var x, y int
|
||||||
|
if n, err := fmt.Sscanf(ts.Items[i].Name, "blk.%d", &x); err != nil || n != 1 {
|
||||||
|
return ts.Items[i].Name < ts.Items[j].Name
|
||||||
|
} else if n, err := fmt.Sscanf(ts.Items[j].Name, "blk.%d", &y); err != nil || n != 1 {
|
||||||
|
return ts.Items[i].Name < ts.Items[j].Name
|
||||||
|
}
|
||||||
|
return x < y
|
||||||
|
}
|
||||||
|
|
||||||
func (ts Tensors) Layers() map[string]Layer {
|
func (ts Tensors) Layers() map[string]Layer {
|
||||||
layers := make(map[string]Layer)
|
layers := make(map[string]Layer)
|
||||||
for _, t := range ts {
|
for _, t := range ts.Items {
|
||||||
parts := strings.Split(t.Name, ".")
|
parts := strings.Split(t.Name, ".")
|
||||||
if parts[0] == "blk" {
|
if parts[0] == "blk" {
|
||||||
// join first and second part, e.g. blk.%d
|
// join first and second part, e.g. blk.%d
|
||||||
@ -454,3 +481,11 @@ func (llm GGML) GraphSize(context, batch uint64) (partialOffload, fullOffload ui
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TensorWriter struct {
|
||||||
|
io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tw TensorWriter) WriteTo(w io.Writer) (int64, error) {
|
||||||
|
return io.Copy(w, tw.Reader)
|
||||||
|
}
|
||||||
|
272
llm/gguf.go
272
llm/gguf.go
@ -6,7 +6,12 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
|
"slices"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
)
|
)
|
||||||
|
|
||||||
type containerGGUF struct {
|
type containerGGUF struct {
|
||||||
@ -87,6 +92,7 @@ type gguf struct {
|
|||||||
|
|
||||||
kv KV
|
kv KV
|
||||||
tensors []*Tensor
|
tensors []*Tensor
|
||||||
|
offset int64
|
||||||
|
|
||||||
parameters uint64
|
parameters uint64
|
||||||
|
|
||||||
@ -109,7 +115,10 @@ func (llm *gguf) KV() KV {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (llm *gguf) Tensors() Tensors {
|
func (llm *gguf) Tensors() Tensors {
|
||||||
return llm.tensors
|
return Tensors{
|
||||||
|
Items: llm.tensors,
|
||||||
|
Offset: llm.offset,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (llm *gguf) numTensor() uint64 {
|
func (llm *gguf) numTensor() uint64 {
|
||||||
@ -199,12 +208,13 @@ func (llm *gguf) Decode(rs io.ReadSeeker) error {
|
|||||||
return fmt.Errorf("failed to read tensor dimensions: %w", err)
|
return fmt.Errorf("failed to read tensor dimensions: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
shape := [4]uint64{1, 1, 1, 1}
|
shape := []uint64{}
|
||||||
for i := 0; uint32(i) < dims; i++ {
|
for i := 0; uint32(i) < dims; i++ {
|
||||||
shape[i], err = readGGUF[uint64](llm, rs)
|
shapeVal, err := readGGUF[uint64](llm, rs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read tensor shape: %w", err)
|
return fmt.Errorf("failed to read tensor shape: %w", err)
|
||||||
}
|
}
|
||||||
|
shape = append(shape, shapeVal)
|
||||||
}
|
}
|
||||||
|
|
||||||
kind, err := readGGUF[uint32](llm, rs)
|
kind, err := readGGUF[uint32](llm, rs)
|
||||||
@ -221,7 +231,7 @@ func (llm *gguf) Decode(rs io.ReadSeeker) error {
|
|||||||
Name: name,
|
Name: name,
|
||||||
Kind: kind,
|
Kind: kind,
|
||||||
Offset: offset,
|
Offset: offset,
|
||||||
Shape: shape[:],
|
Shape: shape,
|
||||||
}
|
}
|
||||||
|
|
||||||
llm.tensors = append(llm.tensors, &tensor)
|
llm.tensors = append(llm.tensors, &tensor)
|
||||||
@ -236,6 +246,14 @@ func (llm *gguf) Decode(rs io.ReadSeeker) error {
|
|||||||
alignment = 32
|
alignment = 32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offset, err := rs.Seek(0, io.SeekCurrent)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get current offset: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// align to next 32-byte boundary
|
||||||
|
llm.offset = offset + llm.padding(offset, int64(alignment))
|
||||||
|
|
||||||
for _, tensor := range llm.tensors {
|
for _, tensor := range llm.tensors {
|
||||||
offset, err := rs.Seek(0, io.SeekCurrent)
|
offset, err := rs.Seek(0, io.SeekCurrent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -261,12 +279,12 @@ func readGGUF[T any](llm *gguf, r io.Reader) (T, error) {
|
|||||||
return t, err
|
return t, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeGGUF[V any](llm *gguf, w io.Writer, t uint32, v V) error {
|
func writeGGUF[V any](w io.Writer, t uint32, v V) error {
|
||||||
if err := binary.Write(w, llm.ByteOrder, t); err != nil {
|
if err := binary.Write(w, binary.LittleEndian, t); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return binary.Write(w, llm.ByteOrder, v)
|
return binary.Write(w, binary.LittleEndian, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func readGGUFV1String(llm *gguf, r io.Reader) (string, error) {
|
func readGGUFV1String(llm *gguf, r io.Reader) (string, error) {
|
||||||
@ -330,12 +348,12 @@ func readGGUFString(llm *gguf, r io.Reader) (string, error) {
|
|||||||
return string(buf), nil
|
return string(buf), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeGGUFString(llm *gguf, w io.Writer, s string) error {
|
func writeGGUFString(w io.Writer, s string) error {
|
||||||
if err := binary.Write(w, llm.ByteOrder, ggufTypeString); err != nil {
|
if err := binary.Write(w, binary.LittleEndian, ggufTypeString); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := binary.Write(w, llm.ByteOrder, uint64(len(s))); err != nil {
|
if err := binary.Write(w, binary.LittleEndian, uint64(len(s))); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,6 +364,7 @@ func writeGGUFString(llm *gguf, w io.Writer, s string) error {
|
|||||||
type array struct {
|
type array struct {
|
||||||
size int
|
size int
|
||||||
values []any
|
values []any
|
||||||
|
datatype uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *array) MarshalJSON() ([]byte, error) {
|
func (a *array) MarshalJSON() ([]byte, error) {
|
||||||
@ -425,7 +444,7 @@ func readGGUFArray(llm *gguf, r io.Reader) (*array, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
a := &array{size: int(n)}
|
a := &array{size: int(n), datatype: t}
|
||||||
if llm.canCollectArray(int(n)) {
|
if llm.canCollectArray(int(n)) {
|
||||||
a.values = make([]any, int(n))
|
a.values = make([]any, int(n))
|
||||||
}
|
}
|
||||||
@ -476,21 +495,21 @@ func readGGUFArray(llm *gguf, r io.Reader) (*array, error) {
|
|||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeGGUFArray[S ~[]E, E any](llm *gguf, w io.Writer, t uint32, s S) error {
|
func writeGGUFArray[S ~[]E, E any](w io.Writer, t uint32, s S) error {
|
||||||
if err := binary.Write(w, llm.ByteOrder, ggufTypeArray); err != nil {
|
if err := binary.Write(w, binary.LittleEndian, ggufTypeArray); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := binary.Write(w, llm.ByteOrder, t); err != nil {
|
if err := binary.Write(w, binary.LittleEndian, t); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := binary.Write(w, llm.ByteOrder, uint64(len(s))); err != nil {
|
if err := binary.Write(w, binary.LittleEndian, uint64(len(s))); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, e := range s {
|
for _, e := range s {
|
||||||
if err := binary.Write(w, llm.ByteOrder, e); err != nil {
|
if err := binary.Write(w, binary.LittleEndian, e); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -589,19 +608,19 @@ func (llm *gguf) Encode(ws io.WriteSeeker, kv KV, tensors []Tensor) error {
|
|||||||
var err error
|
var err error
|
||||||
switch v := v.(type) {
|
switch v := v.(type) {
|
||||||
case uint32:
|
case uint32:
|
||||||
err = writeGGUF(llm, ws, ggufTypeUint32, v)
|
err = writeGGUF(ws, ggufTypeUint32, v)
|
||||||
case float32:
|
case float32:
|
||||||
err = writeGGUF(llm, ws, ggufTypeFloat32, v)
|
err = writeGGUF(ws, ggufTypeFloat32, v)
|
||||||
case bool:
|
case bool:
|
||||||
err = writeGGUF(llm, ws, ggufTypeBool, v)
|
err = writeGGUF(ws, ggufTypeBool, v)
|
||||||
case string:
|
case string:
|
||||||
err = writeGGUFString(llm, ws, v)
|
err = writeGGUFString(ws, v)
|
||||||
case []int32:
|
case []int32:
|
||||||
err = writeGGUFArray(llm, ws, ggufTypeInt32, v)
|
err = writeGGUFArray(ws, ggufTypeInt32, v)
|
||||||
case []uint32:
|
case []uint32:
|
||||||
err = writeGGUFArray(llm, ws, ggufTypeUint32, v)
|
err = writeGGUFArray(ws, ggufTypeUint32, v)
|
||||||
case []float32:
|
case []float32:
|
||||||
err = writeGGUFArray(llm, ws, ggufTypeFloat32, v)
|
err = writeGGUFArray(ws, ggufTypeFloat32, v)
|
||||||
case []string:
|
case []string:
|
||||||
if err := binary.Write(ws, llm.ByteOrder, ggufTypeArray); err != nil {
|
if err := binary.Write(ws, llm.ByteOrder, ggufTypeArray); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -634,7 +653,7 @@ func (llm *gguf) Encode(ws io.WriteSeeker, kv KV, tensors []Tensor) error {
|
|||||||
|
|
||||||
for k, v := range kvCheck {
|
for k, v := range kvCheck {
|
||||||
if !v {
|
if !v {
|
||||||
return fmt.Errorf("Didn't know how to write kv %s", k)
|
return fmt.Errorf("didn't know how to write kv %s", k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -696,3 +715,208 @@ func (llm *gguf) Encode(ws io.WriteSeeker, kv KV, tensors []Tensor) error {
|
|||||||
func (gguf) padding(offset, align int64) int64 {
|
func (gguf) padding(offset, align int64) int64 {
|
||||||
return (align - offset%align) % align
|
return (align - offset%align) % align
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reader and WriterTof
|
||||||
|
type GGUFWriter struct {
|
||||||
|
KV
|
||||||
|
Tensors
|
||||||
|
}
|
||||||
|
|
||||||
|
type writeOffset struct {
|
||||||
|
io.Writer
|
||||||
|
offset int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wo *writeOffset) Write(p []byte) (int, error) {
|
||||||
|
n, err := wo.Writer.Write(p)
|
||||||
|
wo.offset += n
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ io.Reader = (*GGUFWriter)(nil)
|
||||||
|
|
||||||
|
var _ io.WriterTo = (*GGUFWriter)(nil)
|
||||||
|
|
||||||
|
func (GGUFWriter) Read([]byte) (int, error) {
|
||||||
|
panic("not implemeneted")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gguf GGUFWriter) WriteTo(w io.Writer) (int64, error) {
|
||||||
|
wo := &writeOffset{Writer: w}
|
||||||
|
|
||||||
|
if err := binary.Write(wo, binary.LittleEndian, []byte("GGUF")); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(wo, binary.LittleEndian, uint32(3)); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(wo, binary.LittleEndian, uint64(len(gguf.Tensors.Items))); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(wo, binary.LittleEndian, uint64(len(gguf.KV)-1)); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := maps.Keys(gguf.KV)
|
||||||
|
slices.Sort(keys)
|
||||||
|
|
||||||
|
for _, key := range keys {
|
||||||
|
switch key {
|
||||||
|
case "general.parameter_count":
|
||||||
|
// don't write general param count as its added in by us
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
if err := ggufWriteKV(wo, key, gguf.KV[key]); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Sort(gguf.Tensors)
|
||||||
|
|
||||||
|
var s uint64
|
||||||
|
for _, t := range gguf.Tensors.Items {
|
||||||
|
t.Offset = s
|
||||||
|
if err := ggufWriteTensorInfo(wo, t); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
s += t.Size()
|
||||||
|
}
|
||||||
|
tensorOffset := wo.offset
|
||||||
|
|
||||||
|
for _, t := range gguf.Tensors.Items {
|
||||||
|
if err := ggufWriteTensor(wo, t, wo.offset); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return int64(tensorOffset), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ggufWriteTensorInfo(ws io.Writer, t *Tensor) error {
|
||||||
|
if err := binary.Write(ws, binary.LittleEndian, uint64(len(t.Name))); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(ws, binary.LittleEndian, []byte(t.Name)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(ws, binary.LittleEndian, uint32(len(t.Shape))); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range len(t.Shape) {
|
||||||
|
if err := binary.Write(ws, binary.LittleEndian, t.Shape[len(t.Shape)-i-1]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(ws, binary.LittleEndian, t.Kind); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return binary.Write(ws, binary.LittleEndian, t.Offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ggufWriteTensor(ws io.Writer, t *Tensor, offset int) error {
|
||||||
|
slog.Debug(t.Name, "kind", t.Kind, "shape", t.Shape, "offset", t.Offset)
|
||||||
|
if err := binary.Write(ws, binary.LittleEndian, bytes.Repeat([]byte{0}, int(ggufPadding(int64(offset), 32)))); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := t.WriteTo(ws)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func ggufWriteKV(ws io.Writer, k string, v any) error {
|
||||||
|
slog.Debug(k, "type", fmt.Sprintf("%T", v))
|
||||||
|
if err := binary.Write(ws, binary.LittleEndian, uint64(len(k))); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(ws, binary.LittleEndian, []byte(k)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
switch v := v.(type) {
|
||||||
|
case uint32:
|
||||||
|
err = writeGGUF(ws, ggufTypeUint32, v)
|
||||||
|
case float32:
|
||||||
|
err = writeGGUF(ws, ggufTypeFloat32, v)
|
||||||
|
case bool:
|
||||||
|
err = writeGGUF(ws, ggufTypeBool, v)
|
||||||
|
case string:
|
||||||
|
err = writeGGUFString(ws, v)
|
||||||
|
case []int32:
|
||||||
|
err = writeGGUFArray(ws, ggufTypeInt32, v)
|
||||||
|
case []uint32:
|
||||||
|
err = writeGGUFArray(ws, ggufTypeUint32, v)
|
||||||
|
case []float32:
|
||||||
|
err = writeGGUFArray(ws, ggufTypeFloat32, v)
|
||||||
|
case []string:
|
||||||
|
if err := binary.Write(ws, binary.LittleEndian, ggufTypeArray); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(ws, binary.LittleEndian, ggufTypeString); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(ws, binary.LittleEndian, uint64(len(v))); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range v {
|
||||||
|
if err := binary.Write(ws, binary.LittleEndian, uint64(len(e))); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(ws, binary.LittleEndian, []byte(e)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *array:
|
||||||
|
if v.size > 0 {
|
||||||
|
switch v.values[0].(type) {
|
||||||
|
case string:
|
||||||
|
if err := binary.Write(ws, binary.LittleEndian, ggufTypeArray); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(ws, binary.LittleEndian, ggufTypeString); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(ws, binary.LittleEndian, uint64(v.size)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range v.values {
|
||||||
|
if err := binary.Write(ws, binary.LittleEndian, uint64(len(e.(string)))); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(ws, binary.LittleEndian, []byte(e.(string))); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
err = writeGGUFArray(ws, v.datatype, v.values)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("improper type for '%s'", k)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func ggufPadding(offset, align int64) int64 {
|
||||||
|
// we mod twice in the case offset%align = 0
|
||||||
|
return (align - offset%align) % align
|
||||||
|
}
|
||||||
|
187
llm/gguf_test.go
Normal file
187
llm/gguf_test.go
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
package llm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestGGUFDecode tests the decoding and rewriting of (unsorted) GGUF files
|
||||||
|
// To run, add GGUF files to /llm/testdata and add the name of the file to the tests slice
|
||||||
|
// This creates a temporary file in /llm/testdata that will deleted only if the test passes
|
||||||
|
// Note: map[Tensor.Name + " offset"] is commented since sorting will reorder the tensors
|
||||||
|
// Comment out sort.Sort(gguf.Tensors) in gguf.go to test offsets
|
||||||
|
func TestGGUFRewrite(t *testing.T) {
|
||||||
|
tests := []string{
|
||||||
|
"phi3.gguf",
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range tests {
|
||||||
|
tt := tests[i]
|
||||||
|
t.Run(tt, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
p := filepath.Join("testdata", tt)
|
||||||
|
|
||||||
|
if _, err := os.Stat(p); err != nil {
|
||||||
|
t.Skip("file not found", p)
|
||||||
|
}
|
||||||
|
|
||||||
|
wantFile, err := os.Open(p)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer wantFile.Close()
|
||||||
|
|
||||||
|
// decode original gguf
|
||||||
|
_, wantGGML, err := decodeGGML(t, wantFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gotFile, err := os.CreateTemp("testdata", tt)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
gotFile.Close()
|
||||||
|
if !t.Failed() {
|
||||||
|
os.Remove(gotFile.Name())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, gotGGML, err := rewriteGGML(t, wantGGML, gotFile, wantFile)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
diff, diff2 := compareGGML(t, gotGGML, wantGGML, gotFile, wantFile)
|
||||||
|
if cmp.Diff(diff, diff2) != "" {
|
||||||
|
t.Fatalf("diff: \n%s", cmp.Diff(diff, diff2))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareGGML(t *testing.T, gotGGML, wantGGML *GGML, f *os.File, f2 *os.File) (map[string]string, map[string]string) {
|
||||||
|
got := make(map[string]string)
|
||||||
|
want := make(map[string]string)
|
||||||
|
|
||||||
|
gotKV := gotGGML.KV()
|
||||||
|
wantKV := wantGGML.KV()
|
||||||
|
|
||||||
|
if len(gotKV) != len(wantKV) {
|
||||||
|
t.Fatalf("got length: %d != want length: %d", len(gotKV), len(wantKV))
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range gotKV {
|
||||||
|
switch t := v.(type) {
|
||||||
|
case *array:
|
||||||
|
if diffy := cmp.Diff(t.values, wantKV[k].(*array).values); diffy != "" {
|
||||||
|
got[k] = diffy
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if v != wantKV[k] {
|
||||||
|
got[k] = fmt.Sprintf("kv1: %v, kv2: %v", v, want[k])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gotTensors := gotGGML.Tensors().Items
|
||||||
|
gotOffset := gotGGML.Tensors().Offset
|
||||||
|
wantTensors := wantGGML.Tensors().Items
|
||||||
|
wantOffset := wantGGML.Tensors().Offset
|
||||||
|
|
||||||
|
if len(gotTensors) != len(wantTensors) {
|
||||||
|
got["lenTensors"] = fmt.Sprintf("t1: %d, t2: %d", len(gotTensors), len(wantTensors))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tensor := range gotTensors {
|
||||||
|
sha256sum := sha256.New()
|
||||||
|
sr := io.NewSectionReader(f, gotOffset+int64(tensor.Offset), int64(tensor.Size()))
|
||||||
|
var s int64
|
||||||
|
s, err := io.Copy(sha256sum, sr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
got[tensor.Name] = fmt.Sprintf("%x", sha256sum.Sum(nil))
|
||||||
|
got[tensor.Name+" size"] = fmt.Sprintf("%d", s)
|
||||||
|
// got[tensor.Name+" offset"] = fmt.Sprintf("%v", tensor.Offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tensor := range wantTensors {
|
||||||
|
sha256sum := sha256.New()
|
||||||
|
var s int64
|
||||||
|
sr := io.NewSectionReader(f2, wantOffset +int64(tensor.Offset), int64(tensor.Size()))
|
||||||
|
s, err := io.Copy(sha256sum, sr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want[tensor.Name] = fmt.Sprintf("%x", sha256sum.Sum(nil))
|
||||||
|
want[tensor.Name+" size"] = fmt.Sprintf("%d", s)
|
||||||
|
// want[tensor.Name+" offset"] = fmt.Sprintf("%v", tensor.Offset)
|
||||||
|
}
|
||||||
|
return got, want
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeGGML(t *testing.T, f *os.File) (int64, *GGML, error) {
|
||||||
|
ggml, n, err := DecodeGGML(f, math.MaxInt)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return n, ggml, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func rewriteGGML(t *testing.T, ggml *GGML, gotFile *os.File, wantFile *os.File) (int64, *GGML, error) {
|
||||||
|
var tensors []*Tensor
|
||||||
|
|
||||||
|
for _, tensor := range ggml.Tensors().Items {
|
||||||
|
shape := make([]uint64, len(tensor.Shape))
|
||||||
|
for i := range len(tensor.Shape) {
|
||||||
|
shape[i] = tensor.Shape[len(tensor.Shape)-i-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
tensors = append(tensors, &Tensor{
|
||||||
|
Name: tensor.Name,
|
||||||
|
Kind: tensor.Kind,
|
||||||
|
Shape: shape,
|
||||||
|
|
||||||
|
WriterTo: TensorWriter{
|
||||||
|
Reader: io.NewSectionReader(wantFile, ggml.Tensors().Offset+int64(tensor.Offset), int64(tensor.Size())),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reader := &GGUFWriter{
|
||||||
|
KV: ggml.KV(),
|
||||||
|
Tensors: Tensors{
|
||||||
|
Items: tensors,
|
||||||
|
Offset: ggml.Tensors().Offset,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := io.Copy(gotFile, reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Open(gotFile.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ggml2, _, err := DecodeGGML(file, math.MaxInt)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, ggml2, nil
|
||||||
|
}
|
@ -13,6 +13,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template/parse"
|
"text/template/parse"
|
||||||
|
|
||||||
@ -231,7 +232,7 @@ func parseFromFile(ctx context.Context, file *os.File, digest string, fn func(ap
|
|||||||
|
|
||||||
var offset int64
|
var offset int64
|
||||||
for offset < stat.Size() {
|
for offset < stat.Size() {
|
||||||
ggml, n, err := llm.DecodeGGML(file, 0)
|
ggml, n, err := llm.DecodeGGML(file, -1)
|
||||||
if errors.Is(err, io.EOF) {
|
if errors.Is(err, io.EOF) {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
@ -245,7 +246,39 @@ func parseFromFile(ctx context.Context, file *os.File, digest string, fn func(ap
|
|||||||
mediatype = "application/vnd.ollama.image.projector"
|
mediatype = "application/vnd.ollama.image.projector"
|
||||||
}
|
}
|
||||||
|
|
||||||
layer, err := NewLayer(io.NewSectionReader(file, offset, n), mediatype)
|
var reader io.Reader = io.NewSectionReader(file, offset, n)
|
||||||
|
if !sort.IsSorted(ggml.Tensors()) {
|
||||||
|
// create a new Tensors containing Tensors that have a writeTo
|
||||||
|
var tensors []*llm.Tensor
|
||||||
|
ggmlTensors := ggml.Tensors()
|
||||||
|
|
||||||
|
for _, tensor := range ggmlTensors.Items {
|
||||||
|
shape := make([]uint64, len(tensor.Shape))
|
||||||
|
for i := range len(tensor.Shape) {
|
||||||
|
shape[i] = tensor.Shape[len(tensor.Shape)-i-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
tensors = append(tensors, &llm.Tensor{
|
||||||
|
Name: tensor.Name,
|
||||||
|
Kind: tensor.Kind,
|
||||||
|
Shape: shape,
|
||||||
|
|
||||||
|
WriterTo: &llm.TensorWriter{
|
||||||
|
Reader: io.NewSectionReader(file, offset+ggmlTensors.Offset+int64(tensor.Offset), int64(tensor.Size())),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reader = &llm.GGUFWriter{
|
||||||
|
KV: ggml.KV(),
|
||||||
|
Tensors: llm.Tensors{
|
||||||
|
Items: tensors,
|
||||||
|
Offset: ggmlTensors.Offset,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
layer, err := NewLayer(reader, mediatype)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user