mirror of
https://github.com/ollama/ollama.git
synced 2026-07-23 09:10:53 -05:00
flesh out dtypes
This commit is contained in:
@@ -424,8 +424,12 @@ func (t TensorType) BlockSize() uint64 {
|
||||
TensorTypeQ8_0,
|
||||
TensorTypeQ8_1,
|
||||
tensorTypeIQ4_NL,
|
||||
4, TensorTypeMXFP4:
|
||||
TensorTypeMXFP4:
|
||||
return 32
|
||||
case TensorTypeNVFP4:
|
||||
return 64
|
||||
case TensorTypeQ1_0:
|
||||
return 128
|
||||
default:
|
||||
return 256
|
||||
}
|
||||
@@ -497,8 +501,12 @@ func (t TensorType) TypeSize() uint64 {
|
||||
return blockSize/8 + blockSize/16 + blockSize/32
|
||||
case TensorTypeBF16:
|
||||
return 2
|
||||
case 4, TensorTypeMXFP4:
|
||||
case TensorTypeMXFP4:
|
||||
return 1 + blockSize/2
|
||||
case TensorTypeNVFP4:
|
||||
return 4 + blockSize/2
|
||||
case TensorTypeQ1_0:
|
||||
return 2 + blockSize/8
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
|
||||
113
fs/ggml/type.go
113
fs/ggml/type.go
@@ -14,9 +14,9 @@ const (
|
||||
FileTypeF16
|
||||
fileTypeQ4_0
|
||||
fileTypeQ4_1
|
||||
fileTypeMXFP4 // originally fileTypeQ4_1_F16 // unused by GGML
|
||||
fileTypeQ4_2 // unused by GGML
|
||||
fileTypeQ4_3 // unused by GGML
|
||||
fileTypeQ4_1_F16 // removed from GGUF files
|
||||
fileTypeQ4_2 // removed from GGUF files
|
||||
fileTypeQ4_3 // removed from GGUF files
|
||||
FileTypeQ8_0
|
||||
fileTypeQ5_0
|
||||
fileTypeQ5_1
|
||||
@@ -48,6 +48,9 @@ const (
|
||||
fileTypeQ4_0_8_8 // unused by GGML
|
||||
fileTypeTQ1_0
|
||||
fileTypeTQ2_0
|
||||
fileTypeMXFP4_MOE
|
||||
fileTypeNVFP4
|
||||
fileTypeQ1_0
|
||||
|
||||
FileTypeUnknown = 1024
|
||||
)
|
||||
@@ -97,8 +100,6 @@ func (t FileType) String() string {
|
||||
return "Q4_0"
|
||||
case fileTypeQ4_1:
|
||||
return "Q4_1"
|
||||
case fileTypeMXFP4:
|
||||
return "MXFP4"
|
||||
case FileTypeQ8_0:
|
||||
return "Q8_0"
|
||||
case fileTypeQ5_0:
|
||||
@@ -123,10 +124,44 @@ func (t FileType) String() string {
|
||||
return "Q5_K_M"
|
||||
case fileTypeQ6_K:
|
||||
return "Q6_K"
|
||||
case fileTypeIQ2_XXS:
|
||||
return "IQ2_XXS"
|
||||
case fileTypeIQ2_XS:
|
||||
return "IQ2_XS"
|
||||
case fileTypeQ2_K_S:
|
||||
return "Q2_K_S"
|
||||
case fileTypeIQ3_XS:
|
||||
return "IQ3_XS"
|
||||
case fileTypeIQ3_XXS:
|
||||
return "IQ3_XXS"
|
||||
case fileTypeIQ1_S:
|
||||
return "IQ1_S"
|
||||
case fileTypeIQ4_NL:
|
||||
return "IQ4_NL"
|
||||
case fileTypeIQ3_S:
|
||||
return "IQ3_S"
|
||||
case fileTypeIQ3_M:
|
||||
return "IQ3_M"
|
||||
case fileTypeIQ2_S:
|
||||
return "IQ2_S"
|
||||
case fileTypeIQ2_M:
|
||||
return "IQ2_M"
|
||||
case fileTypeIQ4_XS:
|
||||
return "IQ4_XS"
|
||||
case fileTypeIQ1_M:
|
||||
return "IQ1_M"
|
||||
case FileTypeBF16:
|
||||
return "BF16"
|
||||
case fileTypeTQ1_0:
|
||||
return "TQ1_0"
|
||||
case fileTypeTQ2_0:
|
||||
return "TQ2_0"
|
||||
case fileTypeMXFP4_MOE:
|
||||
return "MXFP4_MOE"
|
||||
case fileTypeNVFP4:
|
||||
return "NVFP4"
|
||||
case fileTypeQ1_0:
|
||||
return "Q1_0"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
@@ -170,12 +205,40 @@ func (ftype FileType) ToTensorType() TensorType {
|
||||
return TensorTypeQ5_K
|
||||
case fileTypeQ6_K:
|
||||
return TensorTypeQ6_K
|
||||
case fileTypeIQ2_XXS:
|
||||
return tensorTypeIQ2_XXS
|
||||
case fileTypeIQ2_XS:
|
||||
return tensorTypeIQ2_XS
|
||||
case fileTypeQ2_K_S:
|
||||
return TensorTypeQ2_K
|
||||
case fileTypeIQ3_XS:
|
||||
return tensorTypeIQ3_S
|
||||
case fileTypeIQ3_XXS:
|
||||
return tensorTypeIQ3_XXS
|
||||
case fileTypeIQ1_S:
|
||||
return tensorTypeIQ1_S
|
||||
case fileTypeIQ4_NL:
|
||||
return tensorTypeIQ4_NL
|
||||
case fileTypeIQ3_S, fileTypeIQ3_M:
|
||||
return tensorTypeIQ3_S
|
||||
case fileTypeIQ2_S, fileTypeIQ2_M:
|
||||
return tensorTypeIQ2_S
|
||||
case fileTypeIQ4_XS:
|
||||
return tensorTypeIQ4_XS
|
||||
case fileTypeIQ1_M:
|
||||
return tensorTypeIQ1_M
|
||||
case FileTypeBF16:
|
||||
return TensorTypeBF16
|
||||
case fileTypeMXFP4:
|
||||
case fileTypeTQ1_0:
|
||||
return tensorTypeTQ1_0
|
||||
case fileTypeTQ2_0:
|
||||
return tensorTypeTQ2_0
|
||||
case fileTypeMXFP4_MOE:
|
||||
return TensorTypeMXFP4
|
||||
case fileTypeNVFP4:
|
||||
return TensorTypeNVFP4
|
||||
case fileTypeQ1_0:
|
||||
return TensorTypeQ1_0
|
||||
default:
|
||||
slog.Warn("unsupported file type", "type", ftype)
|
||||
return 0 // F32
|
||||
@@ -227,6 +290,8 @@ const (
|
||||
tensorTypeIQ4_NL_4_8 // unused by GGML
|
||||
tensorTypeIQ4_NL_8_8 // unused by GGML
|
||||
TensorTypeMXFP4
|
||||
TensorTypeNVFP4
|
||||
TensorTypeQ1_0
|
||||
)
|
||||
|
||||
// ParseTensorType parses the provided GGUF tensor type
|
||||
@@ -315,12 +380,46 @@ func (t TensorType) String() string {
|
||||
return "Q6_K"
|
||||
case TensorTypeQ8_K:
|
||||
return "Q8_K"
|
||||
case tensorTypeIQ2_XXS:
|
||||
return "IQ2_XXS"
|
||||
case tensorTypeIQ2_XS:
|
||||
return "IQ2_XS"
|
||||
case tensorTypeIQ3_XXS:
|
||||
return "IQ3_XXS"
|
||||
case tensorTypeIQ1_S:
|
||||
return "IQ1_S"
|
||||
case tensorTypeIQ4_NL:
|
||||
return "IQ4_NL"
|
||||
case tensorTypeIQ3_S:
|
||||
return "IQ3_S"
|
||||
case tensorTypeIQ2_S:
|
||||
return "IQ2_S"
|
||||
case tensorTypeIQ4_XS:
|
||||
return "IQ4_XS"
|
||||
case TensorTypeI8:
|
||||
return "I8"
|
||||
case TensorTypeI16:
|
||||
return "I16"
|
||||
case TensorTypeI32:
|
||||
return "I32"
|
||||
case TensorTypeI64:
|
||||
return "I64"
|
||||
case TensorTypeF64:
|
||||
return "F64"
|
||||
case tensorTypeIQ1_M:
|
||||
return "IQ1_M"
|
||||
case TensorTypeBF16:
|
||||
return "BF16"
|
||||
case 4, TensorTypeMXFP4:
|
||||
case tensorTypeTQ1_0:
|
||||
return "TQ1_0"
|
||||
case tensorTypeTQ2_0:
|
||||
return "TQ2_0"
|
||||
case TensorTypeMXFP4:
|
||||
return "MXFP4"
|
||||
case TensorTypeNVFP4:
|
||||
return "NVFP4"
|
||||
case TensorTypeQ1_0:
|
||||
return "Q1_0"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
115
fs/ggml/type_test.go
Normal file
115
fs/ggml/type_test.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package ggml
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestFileTypeStringMatchesLlamaFType(t *testing.T) {
|
||||
tests := []struct {
|
||||
ftype FileType
|
||||
want string
|
||||
}{
|
||||
{0, "F32"},
|
||||
{1, "F16"},
|
||||
{2, "Q4_0"},
|
||||
{3, "Q4_1"},
|
||||
{7, "Q8_0"},
|
||||
{8, "Q5_0"},
|
||||
{9, "Q5_1"},
|
||||
{10, "Q2_K"},
|
||||
{11, "Q3_K_S"},
|
||||
{12, "Q3_K_M"},
|
||||
{13, "Q3_K_L"},
|
||||
{14, "Q4_K_S"},
|
||||
{15, "Q4_K_M"},
|
||||
{16, "Q5_K_S"},
|
||||
{17, "Q5_K_M"},
|
||||
{18, "Q6_K"},
|
||||
{19, "IQ2_XXS"},
|
||||
{20, "IQ2_XS"},
|
||||
{21, "Q2_K_S"},
|
||||
{22, "IQ3_XS"},
|
||||
{23, "IQ3_XXS"},
|
||||
{24, "IQ1_S"},
|
||||
{25, "IQ4_NL"},
|
||||
{26, "IQ3_S"},
|
||||
{27, "IQ3_M"},
|
||||
{28, "IQ2_S"},
|
||||
{29, "IQ2_M"},
|
||||
{30, "IQ4_XS"},
|
||||
{31, "IQ1_M"},
|
||||
{32, "BF16"},
|
||||
{36, "TQ1_0"},
|
||||
{37, "TQ2_0"},
|
||||
{38, "MXFP4_MOE"},
|
||||
{39, "NVFP4"},
|
||||
{40, "Q1_0"},
|
||||
{FileTypeUnknown, "unknown"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.want, func(t *testing.T) {
|
||||
if got := tt.ftype.String(); got != tt.want {
|
||||
t.Fatalf("FileType(%d).String() = %q, want %q", tt.ftype, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemovedFileTypesAreUnknown(t *testing.T) {
|
||||
for _, ftype := range []FileType{4, 5, 6, 33, 34, 35} {
|
||||
t.Run(ftype.String(), func(t *testing.T) {
|
||||
if got := ftype.String(); got != "unknown" {
|
||||
t.Fatalf("FileType(%d).String() = %q, want unknown", ftype, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTensorTypeStringMatchesGGMLType(t *testing.T) {
|
||||
tests := []struct {
|
||||
tt TensorType
|
||||
want string
|
||||
}{
|
||||
{0, "F32"},
|
||||
{1, "F16"},
|
||||
{2, "Q4_0"},
|
||||
{3, "Q4_1"},
|
||||
{6, "Q5_0"},
|
||||
{7, "Q5_1"},
|
||||
{8, "Q8_0"},
|
||||
{9, "Q8_1"},
|
||||
{10, "Q2_K"},
|
||||
{11, "Q3_K"},
|
||||
{12, "Q4_K"},
|
||||
{13, "Q5_K"},
|
||||
{14, "Q6_K"},
|
||||
{15, "Q8_K"},
|
||||
{16, "IQ2_XXS"},
|
||||
{17, "IQ2_XS"},
|
||||
{18, "IQ3_XXS"},
|
||||
{19, "IQ1_S"},
|
||||
{20, "IQ4_NL"},
|
||||
{21, "IQ3_S"},
|
||||
{22, "IQ2_S"},
|
||||
{23, "IQ4_XS"},
|
||||
{24, "I8"},
|
||||
{25, "I16"},
|
||||
{26, "I32"},
|
||||
{27, "I64"},
|
||||
{28, "F64"},
|
||||
{29, "IQ1_M"},
|
||||
{30, "BF16"},
|
||||
{34, "TQ1_0"},
|
||||
{35, "TQ2_0"},
|
||||
{39, "MXFP4"},
|
||||
{40, "NVFP4"},
|
||||
{41, "Q1_0"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.want, func(t *testing.T) {
|
||||
if got := tt.tt.String(); got != tt.want {
|
||||
t.Fatalf("TensorType(%d).String() = %q, want %q", tt.tt, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -879,6 +879,43 @@ func TestSetupLlamaServerCommandEnv(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilteredEnvLogValue(t *testing.T) {
|
||||
attrs := filteredEnv([]string{
|
||||
"OLLAMA_DEBUG=1",
|
||||
"OLLAMA_API_KEY=ollama-secret",
|
||||
"OPENAI_API_KEY=openai-secret",
|
||||
"HF_TOKEN=hf-secret",
|
||||
"GGML_BACKEND_PATH=/tmp/ggml",
|
||||
"CUDA_VISIBLE_DEVICES=0",
|
||||
"CUDA_API_KEY=cuda-secret",
|
||||
"HIP_VISIBLE_DEVICES=1",
|
||||
"PATH=/bin",
|
||||
}).LogValue().Group()
|
||||
|
||||
got := make(map[string]string, len(attrs))
|
||||
for _, attr := range attrs {
|
||||
got[attr.Key] = attr.Value.String()
|
||||
}
|
||||
|
||||
for _, key := range []string{"OLLAMA_DEBUG", "OLLAMA_API_KEY", "OPENAI_API_KEY", "HF_TOKEN"} {
|
||||
if _, ok := got[key]; ok {
|
||||
t.Fatalf("%s should not be logged: %#v", key, got)
|
||||
}
|
||||
}
|
||||
|
||||
for key, want := range map[string]string{
|
||||
"GGML_BACKEND_PATH": "/tmp/ggml",
|
||||
"CUDA_VISIBLE_DEVICES": "0",
|
||||
"HIP_VISIBLE_DEVICES": "1",
|
||||
"PATH": "/bin",
|
||||
"CUDA_API_KEY": "[redacted]",
|
||||
} {
|
||||
if got[key] != want {
|
||||
t.Fatalf("%s = %q, want %q; attrs=%#v", key, got[key], want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLlamaServerCompletionBOSOwnership(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -25,27 +25,37 @@ func (e filteredEnv) LogValue() slog.Value {
|
||||
var attrs []slog.Attr
|
||||
for _, env := range e {
|
||||
if key, value, ok := strings.Cut(env, "="); ok {
|
||||
switch {
|
||||
case strings.HasPrefix(key, "OLLAMA_"),
|
||||
strings.HasPrefix(key, "CUDA_"),
|
||||
strings.HasPrefix(key, "ROCR_"),
|
||||
strings.HasPrefix(key, "ROCM_"),
|
||||
strings.HasPrefix(key, "HIP_"),
|
||||
strings.HasPrefix(key, "GPU_"),
|
||||
strings.HasPrefix(key, "HSA_"),
|
||||
strings.HasPrefix(key, "GGML_"),
|
||||
slices.Contains([]string{
|
||||
"PATH",
|
||||
"LD_LIBRARY_PATH",
|
||||
"DYLD_LIBRARY_PATH",
|
||||
}, key):
|
||||
attrs = append(attrs, slog.String(key, value))
|
||||
if filteredEnvLogKey(key) {
|
||||
attrs = append(attrs, slog.String(key, filteredEnvLogValue(key, value)))
|
||||
}
|
||||
}
|
||||
}
|
||||
return slog.GroupValue(attrs...)
|
||||
}
|
||||
|
||||
func filteredEnvLogKey(key string) bool {
|
||||
return strings.HasPrefix(key, "CUDA_") ||
|
||||
strings.HasPrefix(key, "ROCR_") ||
|
||||
strings.HasPrefix(key, "ROCM_") ||
|
||||
strings.HasPrefix(key, "HIP_") ||
|
||||
strings.HasPrefix(key, "HSA_") ||
|
||||
strings.HasPrefix(key, "GGML_") ||
|
||||
slices.Contains([]string{
|
||||
"PATH",
|
||||
"LD_LIBRARY_PATH",
|
||||
"DYLD_LIBRARY_PATH",
|
||||
}, key)
|
||||
}
|
||||
|
||||
func filteredEnvLogValue(key, value string) string {
|
||||
for _, token := range []string{"API", "KEY", "TOKEN", "SECRET", "PASSWORD", "PASS", "CREDENTIAL", "AUTH"} {
|
||||
if strings.Contains(strings.ToUpper(key), token) {
|
||||
return "[redacted]"
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
type LlamaServer interface {
|
||||
ModelPath() string
|
||||
Load(ctx context.Context, systemInfo ml.SystemInfo, gpus []ml.DeviceInfo, requireFull bool) ([]ml.DeviceID, error)
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ollama/ollama/api"
|
||||
"github.com/ollama/ollama/fs/ggml"
|
||||
"github.com/ollama/ollama/manifest"
|
||||
"github.com/ollama/ollama/model/parsers"
|
||||
ollamatemplate "github.com/ollama/ollama/template"
|
||||
@@ -353,6 +354,9 @@ func buildModelListSummary(name model.Name, mf *manifest.Manifest) (modelListSum
|
||||
if summary.Details.EmbeddingLength == 0 {
|
||||
summary.Details.EmbeddingLength = info.EmbeddingLength
|
||||
}
|
||||
if isUnknownQuantization(summary.Details.QuantizationLevel) && !isUnknownQuantization(info.FileType) {
|
||||
summary.Details.QuantizationLevel = info.FileType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,6 +459,7 @@ type modelListGGUF struct {
|
||||
Capabilities []model.Capability
|
||||
ContextLength int
|
||||
EmbeddingLength int
|
||||
FileType string
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -554,6 +559,15 @@ func readModelListGGUF(path string) (modelListGGUF, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
if key == "general.file_type" {
|
||||
value, err := readModelListGGUFIntValue(r, byteOrder, version, valueType)
|
||||
if err != nil {
|
||||
return modelListGGUF{}, err
|
||||
}
|
||||
info.FileType = ggml.FileType(value).String()
|
||||
continue
|
||||
}
|
||||
|
||||
if architecture != "" && strings.HasPrefix(key, "tokenizer.") {
|
||||
break
|
||||
}
|
||||
@@ -766,6 +780,10 @@ func appendModelListCapability(capabilities []model.Capability, capability model
|
||||
return append(capabilities, capability)
|
||||
}
|
||||
|
||||
func isUnknownQuantization(quantization string) bool {
|
||||
return quantization == "" || quantization == "unknown"
|
||||
}
|
||||
|
||||
func cloneModelListSummary(summary modelListSummary) modelListSummary {
|
||||
summary.Details.Families = append([]string(nil), summary.Details.Families...)
|
||||
summary.Capabilities = append([]model.Capability(nil), summary.Capabilities...)
|
||||
|
||||
@@ -1514,6 +1514,11 @@ func GetModelInfo(req api.ShowRequest) (*api.ShowResponse, error) {
|
||||
}
|
||||
|
||||
resp.Template = selectedModelTemplate(m, kvData)
|
||||
if isUnknownQuantization(resp.Details.QuantizationLevel) {
|
||||
if fileType := kvData.FileType().String(); !isUnknownQuantization(fileType) {
|
||||
resp.Details.QuantizationLevel = fileType
|
||||
}
|
||||
}
|
||||
|
||||
delete(kvData, "general.name")
|
||||
delete(kvData, "tokenizer.chat_template")
|
||||
|
||||
@@ -586,6 +586,42 @@ func TestGetModelInfo_SafetensorsUsesStoredFileType(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetModelInfoRepairsUnknownGGUFFileType(t *testing.T) {
|
||||
t.Setenv("OLLAMA_MODELS", t.TempDir())
|
||||
|
||||
_, digest := createBinFile(t, ggml.KV{
|
||||
"general.architecture": "llama",
|
||||
"general.file_type": uint32(ggml.FileTypeQ4_K_M),
|
||||
}, nil)
|
||||
modelLayer, err := manifest.NewLayerFromLayer(digest, "application/vnd.ollama.image.model", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
configLayer, err := createConfigLayer([]manifest.Layer{modelLayer}, model.ConfigV2{
|
||||
ModelFormat: "gguf",
|
||||
ModelFamily: "llama",
|
||||
ModelFamilies: []string{"llama"},
|
||||
FileType: "unknown",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
name := model.ParseName("show-unknown-gguf")
|
||||
if err := manifest.WriteManifest(name, *configLayer, []manifest.Layer{modelLayer}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
resp, err := GetModelInfo(api.ShowRequest{Model: name.String()})
|
||||
if err != nil {
|
||||
t.Fatalf("GetModelInfo() error = %v", err)
|
||||
}
|
||||
|
||||
if resp.Details.QuantizationLevel != "Q4_K_M" {
|
||||
t.Fatalf("QuantizationLevel = %q, want %q", resp.Details.QuantizationLevel, "Q4_K_M")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetModelInfo_SafetensorsModelfileUsesShortName(t *testing.T) {
|
||||
t.Setenv("OLLAMA_MODELS", t.TempDir())
|
||||
|
||||
|
||||
Reference in New Issue
Block a user