diff --git a/go.mod b/go.mod index 3e502a6..3d439ed 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,10 @@ go 1.23.2 replace github.com/rocketlaunchr/google-search => github.com/chrisjoyce911/google-search v0.0.0-20230910003754-e501aedf805a -replace gitea.stevedudenhoeffer.com/steve/go-llm => ../go-llm +//replace gitea.stevedudenhoeffer.com/steve/go-llm => ../go-llm require ( - gitea.stevedudenhoeffer.com/steve/go-extractor v0.0.0-20250123020607-964a98a5a884 + gitea.stevedudenhoeffer.com/steve/go-extractor v0.0.0-20250315044602-7c0e44a22f2c gitea.stevedudenhoeffer.com/steve/go-llm v0.0.0-20250123045620-0d909edd44d9 github.com/advancedlogic/GoOse v0.0.0-20231203033844-ae6b36caf275 github.com/joho/godotenv v1.5.1 @@ -17,12 +17,12 @@ require ( ) require ( - cloud.google.com/go v0.118.3 // indirect - cloud.google.com/go/ai v0.10.0 // indirect + cloud.google.com/go v0.119.0 // indirect + cloud.google.com/go/ai v0.10.1 // indirect cloud.google.com/go/auth v0.15.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect cloud.google.com/go/compute/metadata v0.6.0 // indirect - cloud.google.com/go/longrunning v0.6.4 // indirect + cloud.google.com/go/longrunning v0.6.6 // indirect github.com/PuerkitoBio/goquery v1.10.2 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect github.com/antchfx/htmlquery v1.3.4 // indirect @@ -30,11 +30,11 @@ require ( github.com/antchfx/xpath v1.3.3 // indirect github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect - github.com/deckarep/golang-set/v2 v2.7.0 // indirect + github.com/deckarep/golang-set/v2 v2.8.0 // indirect github.com/fatih/set v0.2.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/gigawattio/window v0.0.0-20180317192513-0f5467e35573 // indirect - github.com/go-jose/go-jose/v3 v3.0.3 // indirect + github.com/go-jose/go-jose/v3 v3.0.4 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-resty/resty/v2 v2.16.5 // indirect @@ -49,38 +49,39 @@ require ( github.com/google/generative-ai-go v0.19.0 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/googleapis/gax-go/v2 v2.14.1 // indirect github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 // indirect github.com/kennygrant/sanitize v1.2.4 // indirect - github.com/liushuangls/go-anthropic/v2 v2.13.1 // indirect + github.com/liushuangls/go-anthropic/v2 v2.14.1 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect - github.com/sashabaranov/go-openai v1.37.0 // indirect + github.com/sashabaranov/go-openai v1.38.0 // indirect github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect github.com/temoto/robotstxt v1.1.2 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect - go.opentelemetry.io/otel v1.34.0 // indirect - go.opentelemetry.io/otel/metric v1.34.0 // indirect - go.opentelemetry.io/otel/trace v1.34.0 // indirect - golang.org/x/crypto v0.34.0 // indirect - golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect - golang.org/x/net v0.35.0 // indirect - golang.org/x/oauth2 v0.26.0 // indirect - golang.org/x/sync v0.11.0 // indirect - golang.org/x/sys v0.30.0 // indirect - golang.org/x/text v0.22.0 // indirect - golang.org/x/time v0.10.0 // indirect - google.golang.org/api v0.222.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.starlark.net v0.0.0-20250225190231-0d3f41d403af // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect + golang.org/x/net v0.37.0 // indirect + golang.org/x/oauth2 v0.28.0 // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect + golang.org/x/time v0.11.0 // indirect + google.golang.org/api v0.226.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250219182151-9fdb1cabc7b2 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250219182151-9fdb1cabc7b2 // indirect - google.golang.org/grpc v1.70.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect + google.golang.org/grpc v1.71.0 // indirect google.golang.org/protobuf v1.36.5 // indirect ) diff --git a/pkg/answer/answer.go b/pkg/answer/answer.go index ab77e50..5cb17f7 100644 --- a/pkg/answer/answer.go +++ b/pkg/answer/answer.go @@ -4,6 +4,9 @@ import ( "context" "errors" "fmt" + "go.starlark.net/lib/math" + "go.starlark.net/starlark" + "go.starlark.net/syntax" "log/slog" "net/url" "strings" @@ -74,39 +77,6 @@ type Result struct { Error error } -func fanExecuteToolCalls(ctx context.Context, toolBox *gollm.ToolBox, calls []gollm.ToolCall) []Result { - var results []Result - var resultsOutput = make(chan Result, len(calls)) - - fnCall := func(call gollm.ToolCall) Result { - str, err := toolBox.Execute(ctx, call) - if err != nil { - return Result{ - Error: err, - } - } - - return Result{ - Result: str, - } - } - - for _, call := range calls { - go func(call gollm.ToolCall) { - resultsOutput <- fnCall(call) - }(call) - } - - for i := 0; i < len(calls); i++ { - result := <-resultsOutput - results = append(results, result) - } - - close(resultsOutput) - - return results -} - type article struct { URL string Title string @@ -156,11 +126,11 @@ func extractArticle(ctx context.Context, c cache.Cache, u *url.URL) (res article }, nil } -func doesTextAnswerQuestion(ctx context.Context, q Question, text string) (string, error) { +func doesTextAnswerQuestion(ctx *gollm.Context, q Question, text string) (string, error) { fnAnswer := gollm.NewFunction( "answer", "The answer from the given text that answers the question.", - func(ctx context.Context, args struct { + func(ctx *gollm.Context, args struct { Answer string `description:"the answer to the question, the answer should come from the text"` }) (string, error) { return args.Answer, nil @@ -169,7 +139,7 @@ func doesTextAnswerQuestion(ctx context.Context, q Question, text string) (strin fnNoAnswer := gollm.NewFunction( "no_answer", "Indicate that the text does not answer the question.", - func(ctx context.Context, args struct { + func(ctx *gollm.Context, args struct { Ignored string `description:"ignored, just here to make sure the function is called. Fill with anything."` }) (string, error) { return "", nil @@ -210,8 +180,7 @@ func doesTextAnswerQuestion(ctx context.Context, q Question, text string) (strin return req.Toolbox.Execute(ctx, res.Choices[0].Calls[0]) } -func functionSearch(ctx context.Context, q Question, searchTerm string) (string, error) { - +func functionSearch(ctx *gollm.Context, q Question, searchTerm string) (string, error) { slog.Info("searching", "search", searchTerm, "question", q) res, err := q.Search.Search(ctx, searchTerm) if err != nil { @@ -260,11 +229,11 @@ func functionSearch(ctx context.Context, q Question, searchTerm string) (string, return "", nil } -func functionThink(ctx context.Context, q Question) (string, error) { +func functionThink(ctx *gollm.Context, q Question) (string, error) { fnAnswer := gollm.NewFunction( "answer", "Answer the question.", - func(ctx context.Context, args struct { + func(ctx *gollm.Context, args struct { Answer string `description:"the answer to the question"` }) (string, error) { return args.Answer, nil @@ -307,7 +276,7 @@ func (o Options) Answer(ctx context.Context, q Question) (Answers, error) { fnSearch := gollm.NewFunction( "search", "Search the web for an answer to a question. You can call this function up to "+fmt.Sprint(o.MaxSearches)+" times.", - func(ctx context.Context, args struct { + func(ctx *gollm.Context, args struct { SearchQuery string `description:"what to search the web for for this question"` Question string `description:"what question(s) you are trying to answer with this search"` }) (string, error) { @@ -320,7 +289,7 @@ func (o Options) Answer(ctx context.Context, q Question) (Answers, error) { fnThink := gollm.NewFunction( "think", "Think about a question. This is useful for breaking down complex questions into smaller parts that are easier to answer.", - func(ctx context.Context, args struct { + func(ctx *gollm.Context, args struct { Question string `json:"question" description:"the question to think about"` }) (string, error) { q2 := q @@ -332,13 +301,37 @@ func (o Options) Answer(ctx context.Context, q Question) (Answers, error) { fnAnswer := gollm.NewFunction( "answer", "You definitively answer a question, if you call this it means you know the answer and do not need to search for it or use any other function to find it", - func(ctx context.Context, args struct { + func(ctx *gollm.Context, args struct { Answer string `json:"answer" description:"the answer to the question"` }) (string, error) { return args.Answer, nil }) - var funcs = []*gollm.Function{fnAnswer} + fnWolfram := gollm.NewFunction( + "wolfram", + "Search Wolfram Alpha for an answer to a question.", + func(ctx *gollm.Context, args struct { + Question string `description:"the question to search for"` + }) (string, error) { + return "", nil + }) + + fnCalculate := gollm.NewFunction( + "calculate", + "Calculate a mathematical expression using starlark.", + func(ctx *gollm.Context, args struct { + Expression string `description:"the mathematical expression to calculate, in starlark format"` + }) (string, error) { + fileOpts := syntax.FileOptions{} + v, err := starlark.EvalOptions(&fileOpts, &starlark.Thread{Name: "main"}, "input", args.Expression, math.Module.Members) + if err != nil { + return "", err + } + + return v.String(), nil + }) + + var funcs = []*gollm.Function{fnAnswer, fnWolfram, fnCalculate} if o.MaxSearches > 0 { funcs = append(funcs, fnSearch) @@ -371,6 +364,13 @@ func (o Options) Answer(ctx context.Context, q Question) (Answers, error) { }) } + if q.Question != "" { + messages = append(messages, gollm.Message{ + Role: gollm.RoleUser, + Text: q.Question, + }) + } + req := gollm.Request{ Messages: messages, Toolbox: gollm.NewToolBox(funcs...), @@ -397,7 +397,7 @@ func (o Options) Answer(ctx context.Context, q Question) (Answers, error) { var calls []Result var callsOutput = make(chan Result, len(choice.Calls)) fnCall := func(call gollm.ToolCall) Result { - str, err := req.Toolbox.Execute(ctx, call) + str, err := req.Toolbox.Execute(gollm.NewContext(ctx, req, &choice, &call), call) if err != nil { return Result{