diff --git a/v2/go.mod b/v2/go.mod index 26a35a9f5..4e6fada0e 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -12,7 +12,7 @@ require ( github.com/klauspost/asmfmt v1.3.2 github.com/mattn/go-runewidth v0.0.16 github.com/sajari/fuzzy v1.0.0 - github.com/spf13/pflag v1.0.5 + github.com/spf13/pflag v1.0.6 github.com/xyproto/autoimport v1.5.2 github.com/xyproto/binary v1.3.3 github.com/xyproto/carveimg v1.4.6 @@ -25,7 +25,7 @@ require ( github.com/xyproto/iferr v1.1.0 github.com/xyproto/lookslikegoasm v1.0.0 github.com/xyproto/mode v0.10.0 - github.com/xyproto/ollamaclient/v2 v2.7.0 + github.com/xyproto/ollamaclient/v2 v2.7.1 github.com/xyproto/stringpainter v1.0.1 github.com/xyproto/syntax v1.12.0 github.com/xyproto/termtitle v1.5.1 diff --git a/v2/go.sum b/v2/go.sum index e641f846d..67fe5a190 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -62,8 +62,8 @@ github.com/sajari/fuzzy v1.0.0 h1:+FmwVvJErsd0d0hAPlj4CxqxUtQY/fOoY0DwX4ykpRY= github.com/sajari/fuzzy v1.0.0/go.mod h1:OjYR6KxoWOe9+dOlXeiCJd4dIbED4Oo8wpS89o0pwOo= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d h1:yKm7XZV6j9Ev6lojP2XaIshpT4ymkqhMeSghO5Ps00E= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -100,8 +100,8 @@ github.com/xyproto/lookslikegoasm v1.0.0 h1:/2nuhAu67tdjqXHkdOjP/UUAH9uJbAdptIkF github.com/xyproto/lookslikegoasm v1.0.0/go.mod h1:4ck0t+iH2rEAmjwnRhMZl72lCeCxK4iylxdcZ3K3FbY= github.com/xyproto/mode v0.10.0 h1:+H8Evj0RQvFmKIdDZKbQjiaMyOvbzTyedaSfKcjIEbc= github.com/xyproto/mode v0.10.0/go.mod h1:Bl7ymMitqlFLl3lgyAY3knkZb0t9/4fzgja7mWp66n0= -github.com/xyproto/ollamaclient/v2 v2.7.0 h1:mlTOmhACueaqLEsoTnxpE2NAvhxRF4TmYprnMIjUhnU= -github.com/xyproto/ollamaclient/v2 v2.7.0/go.mod h1:o1GmnP7/qLen2lmuTdVk4MO+4EgbdblE3SFvC+rcOvU= +github.com/xyproto/ollamaclient/v2 v2.7.1 h1:kYel2Ye2ZfnzG+trjlImq4Hmoeupb5+nfYYDDVVQ4sk= +github.com/xyproto/ollamaclient/v2 v2.7.1/go.mod h1:493pxyifwu57ifEg8DK45cw9aZuzvewXc/mReBlVI2o= github.com/xyproto/palgen v1.6.0 h1:o3NgtiU4vuUvctOgButN80XIR+YGVFVLw33Y143YuiI= github.com/xyproto/palgen v1.6.0/go.mod h1:CNbNEV/7R9UvbF8oiS0hBdANveDfBBc4/pvQIFp5ygg= github.com/xyproto/stringpainter v1.0.1 h1:OpCY4iTqtdyQD5wllMIp9vebVUB5hUrbQH2SQKKuB8w= diff --git a/v2/vendor/github.com/spf13/pflag/.editorconfig b/v2/vendor/github.com/spf13/pflag/.editorconfig new file mode 100644 index 000000000..4492e9f9f --- /dev/null +++ b/v2/vendor/github.com/spf13/pflag/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.go] +indent_style = tab diff --git a/v2/vendor/github.com/spf13/pflag/.golangci.yaml b/v2/vendor/github.com/spf13/pflag/.golangci.yaml new file mode 100644 index 000000000..b274f2484 --- /dev/null +++ b/v2/vendor/github.com/spf13/pflag/.golangci.yaml @@ -0,0 +1,4 @@ +linters: + disable-all: true + enable: + - nolintlint diff --git a/v2/vendor/github.com/spf13/pflag/flag.go b/v2/vendor/github.com/spf13/pflag/flag.go index 24a5036e9..7c058de37 100644 --- a/v2/vendor/github.com/spf13/pflag/flag.go +++ b/v2/vendor/github.com/spf13/pflag/flag.go @@ -160,7 +160,7 @@ type FlagSet struct { args []string // arguments after flags argsLenAtDash int // len(args) when a '--' was located when parsing, or -1 if no -- errorHandling ErrorHandling - output io.Writer // nil means stderr; use out() accessor + output io.Writer // nil means stderr; use Output() accessor interspersed bool // allow interspersed option/non-option args normalizeNameFunc func(f *FlagSet, name string) NormalizedName @@ -255,13 +255,20 @@ func (f *FlagSet) normalizeFlagName(name string) NormalizedName { return n(f, name) } -func (f *FlagSet) out() io.Writer { +// Output returns the destination for usage and error messages. os.Stderr is returned if +// output was not set or was set to nil. +func (f *FlagSet) Output() io.Writer { if f.output == nil { return os.Stderr } return f.output } +// Name returns the name of the flag set. +func (f *FlagSet) Name() string { + return f.name +} + // SetOutput sets the destination for usage and error messages. // If output is nil, os.Stderr is used. func (f *FlagSet) SetOutput(output io.Writer) { @@ -358,7 +365,7 @@ func (f *FlagSet) ShorthandLookup(name string) *Flag { } if len(name) > 1 { msg := fmt.Sprintf("can not look up shorthand which is more than one ASCII character: %q", name) - fmt.Fprintf(f.out(), msg) + fmt.Fprintf(f.Output(), msg) panic(msg) } c := name[0] @@ -482,7 +489,7 @@ func (f *FlagSet) Set(name, value string) error { } if flag.Deprecated != "" { - fmt.Fprintf(f.out(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated) + fmt.Fprintf(f.Output(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated) } return nil } @@ -523,7 +530,7 @@ func Set(name, value string) error { // otherwise, the default values of all defined flags in the set. func (f *FlagSet) PrintDefaults() { usages := f.FlagUsages() - fmt.Fprint(f.out(), usages) + fmt.Fprint(f.Output(), usages) } // defaultIsZeroValue returns true if the default value for this flag represents @@ -758,7 +765,7 @@ func PrintDefaults() { // defaultUsage is the default function to print a usage message. func defaultUsage(f *FlagSet) { - fmt.Fprintf(f.out(), "Usage of %s:\n", f.name) + fmt.Fprintf(f.Output(), "Usage of %s:\n", f.name) f.PrintDefaults() } @@ -844,7 +851,7 @@ func (f *FlagSet) AddFlag(flag *Flag) { _, alreadyThere := f.formal[normalizedFlagName] if alreadyThere { msg := fmt.Sprintf("%s flag redefined: %s", f.name, flag.Name) - fmt.Fprintln(f.out(), msg) + fmt.Fprintln(f.Output(), msg) panic(msg) // Happens only if flags are declared with identical names } if f.formal == nil { @@ -860,7 +867,7 @@ func (f *FlagSet) AddFlag(flag *Flag) { } if len(flag.Shorthand) > 1 { msg := fmt.Sprintf("%q shorthand is more than one ASCII character", flag.Shorthand) - fmt.Fprintf(f.out(), msg) + fmt.Fprintf(f.Output(), msg) panic(msg) } if f.shorthands == nil { @@ -870,7 +877,7 @@ func (f *FlagSet) AddFlag(flag *Flag) { used, alreadyThere := f.shorthands[c] if alreadyThere { msg := fmt.Sprintf("unable to redefine %q shorthand in %q flagset: it's already used for %q flag", c, f.name, used.Name) - fmt.Fprintf(f.out(), msg) + fmt.Fprintf(f.Output(), msg) panic(msg) } f.shorthands[c] = flag @@ -909,7 +916,7 @@ func VarP(value Value, name, shorthand, usage string) { func (f *FlagSet) failf(format string, a ...interface{}) error { err := fmt.Errorf(format, a...) if f.errorHandling != ContinueOnError { - fmt.Fprintln(f.out(), err) + fmt.Fprintln(f.Output(), err) f.usage() } return err @@ -1060,7 +1067,7 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse } if flag.ShorthandDeprecated != "" { - fmt.Fprintf(f.out(), "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated) + fmt.Fprintf(f.Output(), "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated) } err = fn(flag, value) diff --git a/v2/vendor/github.com/spf13/pflag/ip.go b/v2/vendor/github.com/spf13/pflag/ip.go index 3d414ba69..06b8bcb57 100644 --- a/v2/vendor/github.com/spf13/pflag/ip.go +++ b/v2/vendor/github.com/spf13/pflag/ip.go @@ -16,6 +16,9 @@ func newIPValue(val net.IP, p *net.IP) *ipValue { func (i *ipValue) String() string { return net.IP(*i).String() } func (i *ipValue) Set(s string) error { + if s == "" { + return nil + } ip := net.ParseIP(strings.TrimSpace(s)) if ip == nil { return fmt.Errorf("failed to parse IP: %q", s) diff --git a/v2/vendor/github.com/spf13/pflag/ipnet_slice.go b/v2/vendor/github.com/spf13/pflag/ipnet_slice.go new file mode 100644 index 000000000..6b541aa87 --- /dev/null +++ b/v2/vendor/github.com/spf13/pflag/ipnet_slice.go @@ -0,0 +1,147 @@ +package pflag + +import ( + "fmt" + "io" + "net" + "strings" +) + +// -- ipNetSlice Value +type ipNetSliceValue struct { + value *[]net.IPNet + changed bool +} + +func newIPNetSliceValue(val []net.IPNet, p *[]net.IPNet) *ipNetSliceValue { + ipnsv := new(ipNetSliceValue) + ipnsv.value = p + *ipnsv.value = val + return ipnsv +} + +// Set converts, and assigns, the comma-separated IPNet argument string representation as the []net.IPNet value of this flag. +// If Set is called on a flag that already has a []net.IPNet assigned, the newly converted values will be appended. +func (s *ipNetSliceValue) Set(val string) error { + + // remove all quote characters + rmQuote := strings.NewReplacer(`"`, "", `'`, "", "`", "") + + // read flag arguments with CSV parser + ipNetStrSlice, err := readAsCSV(rmQuote.Replace(val)) + if err != nil && err != io.EOF { + return err + } + + // parse ip values into slice + out := make([]net.IPNet, 0, len(ipNetStrSlice)) + for _, ipNetStr := range ipNetStrSlice { + _, n, err := net.ParseCIDR(strings.TrimSpace(ipNetStr)) + if err != nil { + return fmt.Errorf("invalid string being converted to CIDR: %s", ipNetStr) + } + out = append(out, *n) + } + + if !s.changed { + *s.value = out + } else { + *s.value = append(*s.value, out...) + } + + s.changed = true + + return nil +} + +// Type returns a string that uniquely represents this flag's type. +func (s *ipNetSliceValue) Type() string { + return "ipNetSlice" +} + +// String defines a "native" format for this net.IPNet slice flag value. +func (s *ipNetSliceValue) String() string { + + ipNetStrSlice := make([]string, len(*s.value)) + for i, n := range *s.value { + ipNetStrSlice[i] = n.String() + } + + out, _ := writeAsCSV(ipNetStrSlice) + return "[" + out + "]" +} + +func ipNetSliceConv(val string) (interface{}, error) { + val = strings.Trim(val, "[]") + // Emtpy string would cause a slice with one (empty) entry + if len(val) == 0 { + return []net.IPNet{}, nil + } + ss := strings.Split(val, ",") + out := make([]net.IPNet, len(ss)) + for i, sval := range ss { + _, n, err := net.ParseCIDR(strings.TrimSpace(sval)) + if err != nil { + return nil, fmt.Errorf("invalid string being converted to CIDR: %s", sval) + } + out[i] = *n + } + return out, nil +} + +// GetIPNetSlice returns the []net.IPNet value of a flag with the given name +func (f *FlagSet) GetIPNetSlice(name string) ([]net.IPNet, error) { + val, err := f.getFlagType(name, "ipNetSlice", ipNetSliceConv) + if err != nil { + return []net.IPNet{}, err + } + return val.([]net.IPNet), nil +} + +// IPNetSliceVar defines a ipNetSlice flag with specified name, default value, and usage string. +// The argument p points to a []net.IPNet variable in which to store the value of the flag. +func (f *FlagSet) IPNetSliceVar(p *[]net.IPNet, name string, value []net.IPNet, usage string) { + f.VarP(newIPNetSliceValue(value, p), name, "", usage) +} + +// IPNetSliceVarP is like IPNetSliceVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) IPNetSliceVarP(p *[]net.IPNet, name, shorthand string, value []net.IPNet, usage string) { + f.VarP(newIPNetSliceValue(value, p), name, shorthand, usage) +} + +// IPNetSliceVar defines a []net.IPNet flag with specified name, default value, and usage string. +// The argument p points to a []net.IPNet variable in which to store the value of the flag. +func IPNetSliceVar(p *[]net.IPNet, name string, value []net.IPNet, usage string) { + CommandLine.VarP(newIPNetSliceValue(value, p), name, "", usage) +} + +// IPNetSliceVarP is like IPNetSliceVar, but accepts a shorthand letter that can be used after a single dash. +func IPNetSliceVarP(p *[]net.IPNet, name, shorthand string, value []net.IPNet, usage string) { + CommandLine.VarP(newIPNetSliceValue(value, p), name, shorthand, usage) +} + +// IPNetSlice defines a []net.IPNet flag with specified name, default value, and usage string. +// The return value is the address of a []net.IPNet variable that stores the value of that flag. +func (f *FlagSet) IPNetSlice(name string, value []net.IPNet, usage string) *[]net.IPNet { + p := []net.IPNet{} + f.IPNetSliceVarP(&p, name, "", value, usage) + return &p +} + +// IPNetSliceP is like IPNetSlice, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) IPNetSliceP(name, shorthand string, value []net.IPNet, usage string) *[]net.IPNet { + p := []net.IPNet{} + f.IPNetSliceVarP(&p, name, shorthand, value, usage) + return &p +} + +// IPNetSlice defines a []net.IPNet flag with specified name, default value, and usage string. +// The return value is the address of a []net.IP variable that stores the value of the flag. +func IPNetSlice(name string, value []net.IPNet, usage string) *[]net.IPNet { + return CommandLine.IPNetSliceP(name, "", value, usage) +} + +// IPNetSliceP is like IPNetSlice, but accepts a shorthand letter that can be used after a single dash. +func IPNetSliceP(name, shorthand string, value []net.IPNet, usage string) *[]net.IPNet { + return CommandLine.IPNetSliceP(name, shorthand, value, usage) +} diff --git a/v2/vendor/github.com/spf13/pflag/string_array.go b/v2/vendor/github.com/spf13/pflag/string_array.go index 4894af818..d1ff0a96b 100644 --- a/v2/vendor/github.com/spf13/pflag/string_array.go +++ b/v2/vendor/github.com/spf13/pflag/string_array.go @@ -31,11 +31,7 @@ func (s *stringArrayValue) Append(val string) error { func (s *stringArrayValue) Replace(val []string) error { out := make([]string, len(val)) for i, d := range val { - var err error out[i] = d - if err != nil { - return err - } } *s.value = out return nil diff --git a/v2/vendor/github.com/xyproto/ollamaclient/v2/massage.go b/v2/vendor/github.com/xyproto/ollamaclient/v2/massage.go index c4d2caad0..b1e6dbf9c 100644 --- a/v2/vendor/github.com/xyproto/ollamaclient/v2/massage.go +++ b/v2/vendor/github.com/xyproto/ollamaclient/v2/massage.go @@ -4,7 +4,8 @@ import "strings" // Massage will try to extract a shorter message from a longer LLM output // using pretty "hacky" string manipulation techniques. -func Massage(generatedOutput string) string { +// If the optional uppercaseLetter argument is false, the first letter will not be made uppercase. +func Massage(generatedOutput string, uppercaseLetter ...bool) string { s := generatedOutput // Keep the part after ":", if applicable if strings.Contains(s, ":") { @@ -68,8 +69,11 @@ func Massage(generatedOutput string) string { if len(s) < 4 { return strings.TrimSpace(generatedOutput) } - // Let the first letter be uppercase - s = strings.ToUpper(string([]rune(s)[0])) + string([]rune(s)[1:]) + + if !(len(uppercaseLetter) > 0 && !uppercaseLetter[0]) { + // Let the first letter be uppercase + s = strings.ToUpper(string([]rune(s)[0])) + string([]rune(s)[1:]) + } // Trim spaces s = strings.TrimSpace(s) diff --git a/v2/vendor/github.com/xyproto/ollamaclient/v2/ollamaclient.go b/v2/vendor/github.com/xyproto/ollamaclient/v2/ollamaclient.go index ccbd3a7e8..d77de2605 100644 --- a/v2/vendor/github.com/xyproto/ollamaclient/v2/ollamaclient.go +++ b/v2/vendor/github.com/xyproto/ollamaclient/v2/ollamaclient.go @@ -251,6 +251,87 @@ func (oc *Config) GetChatResponse(promptAndOptionalImages ...string) (OutputResp return res, nil } +// GetOutputChatVision sends a request to the Ollama API and returns the generated response. +// It is similar to GetChatResponse, but it adds the images into the Message struct before sending them. +func (oc *Config) GetOutputChatVision(promptAndOptionalImages ...string) (string, error) { + var ( + temperature float64 + seed = oc.SeedOrNegative + ) + if len(promptAndOptionalImages) == 0 { + return "", errors.New("at least one prompt must be given (and then optionally, base64 encoded JPG or PNG image strings)") + } + prompt := promptAndOptionalImages[0] + var images []string + if len(promptAndOptionalImages) > 1 { + images = promptAndOptionalImages[1:] + } + if seed < 0 { + temperature = oc.TemperatureIfNegativeSeed + } + messages := []Message{} + if oc.SystemPrompt != "" { + messages = append(messages, Message{ + Role: "system", + Content: oc.SystemPrompt, + }) + } + messages = append(messages, Message{ + Role: "user", + Content: prompt, + Images: images, + }) + + reqBody := GenerateChatRequest{ + Model: oc.ModelName, + Messages: messages, + Tools: oc.Tools, + Options: RequestOptions{ + Seed: seed, // set to -1 to make it random + Temperature: temperature, // set to 0 together with a specific seed to make output reproducible + }, + } + + if oc.ContextLength != 0 { + reqBody.Options.ContextLength = oc.ContextLength + } + reqBytes, err := json.Marshal(reqBody) + if err != nil { + return "", err + } + if oc.Verbose { + fmt.Printf("Sending request to %s/api/chat: %s\n", oc.ServerAddr, string(reqBytes)) + } + HTTPClient := &http.Client{ + Timeout: oc.HTTPTimeout, + } + resp, err := HTTPClient.Post(oc.ServerAddr+"/api/chat", mimeJSON, bytes.NewBuffer(reqBytes)) + if err != nil { + return "", err + } + defer resp.Body.Close() + var res = "" + var sb strings.Builder + decoder := json.NewDecoder(resp.Body) + for { + var genResp GenerateChatResponse + if err := decoder.Decode(&genResp); err != nil { + break + } + sb.WriteString(genResp.Message.Content) + if genResp.Done { + d, _ := json.Marshal(genResp.Message.ToolCalls) + res = string(d) + break + } + } + res = strings.TrimPrefix(sb.String(), "\n") + if oc.TrimSpace { + res = strings.TrimSpace(res) + } + return res, nil +} + // GetResponse sends a request to the Ollama API and returns the generated response func (oc *Config) GetResponse(promptAndOptionalImages ...string) (OutputResponse, error) { var ( diff --git a/v2/vendor/github.com/xyproto/ollamaclient/v2/stream.go b/v2/vendor/github.com/xyproto/ollamaclient/v2/stream.go index 2071c59f3..b48e8c1dc 100644 --- a/v2/vendor/github.com/xyproto/ollamaclient/v2/stream.go +++ b/v2/vendor/github.com/xyproto/ollamaclient/v2/stream.go @@ -10,8 +10,9 @@ import ( // Message is a chat message type Message struct { - Role string `json:"role"` - Content string `json:"content"` + Role string `json:"role"` + Content string `json:"content"` + Images []string `json:"images,omitempty"` // base64 encoded images (for vision models) } // MessageResponse represents the response data from the generate API call diff --git a/v2/vendor/github.com/xyproto/ollamaclient/v2/vision.go b/v2/vendor/github.com/xyproto/ollamaclient/v2/vision.go new file mode 100644 index 000000000..6032ab46d --- /dev/null +++ b/v2/vendor/github.com/xyproto/ollamaclient/v2/vision.go @@ -0,0 +1,6 @@ +package ollamaclient + +type VisionRequest struct { + Prompt string `json:"prompt"` + Images []string `json:"images"` +} diff --git a/v2/vendor/modules.txt b/v2/vendor/modules.txt index b515d4eb4..0286e48da 100644 --- a/v2/vendor/modules.txt +++ b/v2/vendor/modules.txt @@ -80,7 +80,7 @@ github.com/sajari/fuzzy # github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d ## explicit github.com/sourcegraph/annotate -# github.com/spf13/pflag v1.0.5 +# github.com/spf13/pflag v1.0.6 ## explicit; go 1.12 github.com/spf13/pflag # github.com/stretchr/testify v1.9.0 @@ -130,7 +130,7 @@ github.com/xyproto/lookslikegoasm # github.com/xyproto/mode v0.10.0 ## explicit; go 1.23.2 github.com/xyproto/mode -# github.com/xyproto/ollamaclient/v2 v2.7.0 +# github.com/xyproto/ollamaclient/v2 v2.7.1 ## explicit; go 1.23.1 github.com/xyproto/ollamaclient/v2 # github.com/xyproto/palgen v1.6.0