239 lines
6.0 KiB
Go
239 lines
6.0 KiB
Go
package gollama
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/ollama/ollama/api"
|
|
)
|
|
|
|
// OptionDoc describes a supported Ollama model option.
|
|
type OptionDoc struct {
|
|
Key string
|
|
Type string
|
|
Description string
|
|
}
|
|
|
|
// OptionSet holds typed generation options for Ollama requests.
|
|
type OptionSet struct {
|
|
opts api.Options
|
|
set map[string]struct{}
|
|
}
|
|
|
|
// NewOptions returns an empty OptionSet.
|
|
func NewOptions() OptionSet { return OptionSet{set: make(map[string]struct{})} }
|
|
|
|
// DefaultOptions returns Ollama's default option values.
|
|
func DefaultOptions() OptionSet {
|
|
return OptionSet{opts: api.DefaultOptions(), set: make(map[string]struct{})}
|
|
}
|
|
|
|
func (o OptionSet) mark(key string) OptionSet {
|
|
if o.set == nil {
|
|
o.set = make(map[string]struct{})
|
|
}
|
|
o.set[key] = struct{}{}
|
|
return o
|
|
}
|
|
|
|
func (o OptionSet) Temperature(v float32) OptionSet {
|
|
o.opts.Temperature = v
|
|
return o.mark("temperature")
|
|
}
|
|
|
|
func (o OptionSet) TopP(v float32) OptionSet {
|
|
o.opts.TopP = v
|
|
return o.mark("top_p")
|
|
}
|
|
|
|
func (o OptionSet) TopK(v int) OptionSet {
|
|
o.opts.TopK = v
|
|
return o.mark("top_k")
|
|
}
|
|
|
|
func (o OptionSet) MinP(v float32) OptionSet {
|
|
o.opts.MinP = v
|
|
return o.mark("min_p")
|
|
}
|
|
|
|
func (o OptionSet) TypicalP(v float32) OptionSet {
|
|
o.opts.TypicalP = v
|
|
return o.mark("typical_p")
|
|
}
|
|
|
|
func (o OptionSet) NumPredict(v int) OptionSet {
|
|
o.opts.NumPredict = v
|
|
return o.mark("num_predict")
|
|
}
|
|
|
|
func (o OptionSet) NumCtx(v int) OptionSet {
|
|
o.opts.NumCtx = v
|
|
return o.mark("num_ctx")
|
|
}
|
|
|
|
func (o OptionSet) NumKeep(v int) OptionSet {
|
|
o.opts.NumKeep = v
|
|
return o.mark("num_keep")
|
|
}
|
|
|
|
func (o OptionSet) NumBatch(v int) OptionSet {
|
|
o.opts.NumBatch = v
|
|
return o.mark("num_batch")
|
|
}
|
|
|
|
func (o OptionSet) NumGPU(v int) OptionSet {
|
|
o.opts.NumGPU = v
|
|
return o.mark("num_gpu")
|
|
}
|
|
|
|
func (o OptionSet) NumThread(v int) OptionSet {
|
|
o.opts.NumThread = v
|
|
return o.mark("num_thread")
|
|
}
|
|
|
|
func (o OptionSet) RepeatPenalty(v float32) OptionSet {
|
|
o.opts.RepeatPenalty = v
|
|
return o.mark("repeat_penalty")
|
|
}
|
|
|
|
func (o OptionSet) RepeatLastN(v int) OptionSet {
|
|
o.opts.RepeatLastN = v
|
|
return o.mark("repeat_last_n")
|
|
}
|
|
|
|
func (o OptionSet) PresencePenalty(v float32) OptionSet {
|
|
o.opts.PresencePenalty = v
|
|
return o.mark("presence_penalty")
|
|
}
|
|
|
|
func (o OptionSet) FrequencyPenalty(v float32) OptionSet {
|
|
o.opts.FrequencyPenalty = v
|
|
return o.mark("frequency_penalty")
|
|
}
|
|
|
|
func (o OptionSet) Seed(v int) OptionSet {
|
|
o.opts.Seed = v
|
|
return o.mark("seed")
|
|
}
|
|
|
|
func (o OptionSet) Stop(seq ...string) OptionSet {
|
|
o.opts.Stop = seq
|
|
return o.mark("stop")
|
|
}
|
|
|
|
// Merge combines base and override; fields set on override win.
|
|
func (base OptionSet) Merge(override OptionSet) OptionSet {
|
|
out := base
|
|
if override.set == nil {
|
|
return out
|
|
}
|
|
if out.set == nil {
|
|
out.set = make(map[string]struct{})
|
|
}
|
|
data, err := optionsToMap(override.opts)
|
|
if err != nil {
|
|
return out
|
|
}
|
|
for key := range override.set {
|
|
var partial api.Options
|
|
if err := partial.FromMap(map[string]any{key: data[key]}); err == nil {
|
|
merged, _ := optionsToMap(out.opts)
|
|
if v, ok := data[key]; ok {
|
|
merged[key] = v
|
|
}
|
|
_ = out.opts.FromMap(merged)
|
|
out.set[key] = struct{}{}
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (o OptionSet) toMap() (map[string]any, error) {
|
|
return optionsToMapFiltered(o.opts, o.set)
|
|
}
|
|
|
|
func optionsToMap(opts api.Options) (map[string]any, error) {
|
|
b, err := json.Marshal(opts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var m map[string]any
|
|
if err := json.Unmarshal(b, &m); err != nil {
|
|
return nil, err
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
func optionsToMapFiltered(opts api.Options, set map[string]struct{}) (map[string]any, error) {
|
|
all, err := optionsToMap(opts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(set) == 0 {
|
|
return map[string]any{}, nil
|
|
}
|
|
out := make(map[string]any, len(set))
|
|
for key := range set {
|
|
if v, ok := all[key]; ok {
|
|
out[key] = v
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// KnownOptions returns documentation for all supported option keys.
|
|
func KnownOptions() []OptionDoc {
|
|
return []OptionDoc{
|
|
{Key: "temperature", Type: "float32", Description: "Sampling temperature"},
|
|
{Key: "top_p", Type: "float32", Description: "Nucleus sampling"},
|
|
{Key: "top_k", Type: "int", Description: "Top-k sampling"},
|
|
{Key: "min_p", Type: "float32", Description: "Minimum probability threshold"},
|
|
{Key: "typical_p", Type: "float32", Description: "Typical sampling"},
|
|
{Key: "num_predict", Type: "int", Description: "Maximum tokens to generate (-1 = unlimited)"},
|
|
{Key: "num_ctx", Type: "int", Description: "Context window size"},
|
|
{Key: "num_keep", Type: "int", Description: "Tokens to keep from prompt start"},
|
|
{Key: "num_batch", Type: "int", Description: "Batch size for prompt processing"},
|
|
{Key: "num_gpu", Type: "int", Description: "Number of GPU layers (-1 = auto)"},
|
|
{Key: "num_thread", Type: "int", Description: "CPU threads (0 = auto)"},
|
|
{Key: "repeat_penalty", Type: "float32", Description: "Penalty for repeated tokens"},
|
|
{Key: "repeat_last_n", Type: "int", Description: "Lookback for repeat penalty"},
|
|
{Key: "presence_penalty", Type: "float32", Description: "Presence penalty"},
|
|
{Key: "frequency_penalty", Type: "float32", Description: "Frequency penalty"},
|
|
{Key: "seed", Type: "int", Description: "Random seed (-1 = random)"},
|
|
{Key: "stop", Type: "[]string", Description: "Stop sequences"},
|
|
}
|
|
}
|
|
|
|
// parseModelfileParameters parses ShowResponse.Parameters into an OptionSet.
|
|
func parseModelfileParameters(parameters string) (OptionSet, error) {
|
|
out := NewOptions()
|
|
lines := strings.Split(parameters, "\n")
|
|
m := make(map[string]any)
|
|
for _, line := range lines {
|
|
line = strings.TrimSpace(line)
|
|
if line == "" {
|
|
continue
|
|
}
|
|
key, val, ok := strings.Cut(line, " ")
|
|
if !ok {
|
|
continue
|
|
}
|
|
key = strings.TrimSpace(key)
|
|
val = strings.TrimSpace(val)
|
|
if key == "" || val == "" {
|
|
continue
|
|
}
|
|
var parsed any
|
|
if err := json.Unmarshal([]byte(val), &parsed); err != nil {
|
|
parsed = val
|
|
}
|
|
m[key] = parsed
|
|
out.set[key] = struct{}{}
|
|
}
|
|
if err := out.opts.FromMap(m); err != nil {
|
|
return OptionSet{}, fmt.Errorf("parse model parameters: %w", err)
|
|
}
|
|
return out, nil
|
|
}
|