Fix unmarshalling issues and adjust logging for debugging
Modify `FunctionCall` struct to handle arguments as strings. Add debugging logs to facilitate error tracing and improve JSON unmarshalling in various functions.
This commit is contained in:
parent
a83d5f9822
commit
7a43e3a5c8
@ -6,6 +6,7 @@ import (
|
||||
"answer/pkg/search"
|
||||
"context"
|
||||
gollm "gitea.stevedudenhoeffer.com/steve/go-llm"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/urfave/cli"
|
||||
"log/slog"
|
||||
"os"
|
||||
@ -36,6 +37,12 @@ func main() {
|
||||
Description: "",
|
||||
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "env-file",
|
||||
Value: ".env",
|
||||
Usage: "file to read environment variables from",
|
||||
},
|
||||
|
||||
&cli.StringFlag{
|
||||
Name: "model",
|
||||
Value: "openai/gpt-4o",
|
||||
@ -66,6 +73,11 @@ func main() {
|
||||
if c.NArg() == 0 {
|
||||
return cli.ShowAppHelp(c)
|
||||
}
|
||||
|
||||
if c.String("env-file") != "" {
|
||||
_ = godotenv.Load(c.String("env-file"))
|
||||
}
|
||||
|
||||
var question answer.Question
|
||||
|
||||
question.Question = strings.Join(c.Args(), " ")
|
||||
|
3
go.mod
3
go.mod
@ -4,9 +4,12 @@ go 1.23.2
|
||||
|
||||
replace gitea.stevedudenhoeffer.com/steve/go-llm => ../go-llm
|
||||
|
||||
replace github.com/rocketlaunchr/google-search => github.com/chrisjoyce911/google-search v0.0.0-20230910003754-e501aedf805a
|
||||
|
||||
require (
|
||||
gitea.stevedudenhoeffer.com/steve/go-llm v0.0.0-20241031152103-f603010dee49
|
||||
github.com/advancedlogic/GoOse v0.0.0-20231203033844-ae6b36caf275
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/playwright-community/playwright-go v0.4702.0
|
||||
github.com/rocketlaunchr/google-search v1.1.6
|
||||
github.com/urfave/cli v1.22.16
|
||||
|
@ -44,6 +44,11 @@ type Options struct {
|
||||
// MaxTries is the absolute maximum number of pages to try to get an answer from. For instance, if MaxSearches is 5 and
|
||||
// 5 pages are tried and no answers are found, the function will return ErrMaxTries.
|
||||
MaxTries int
|
||||
|
||||
// OnNewFunction is a callback that, if non-nil, will be called when a new function is called by the LLM.
|
||||
// The "answer" and "no_answer" functions are not included in this callback.
|
||||
// Return an error to stop the function from being called.
|
||||
OnNewFunction func(ctx context.Context, funcName string, question string, parameter string) error
|
||||
}
|
||||
|
||||
var DefaultOptions = Options{
|
||||
@ -152,7 +157,9 @@ 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{}) (string, error) {
|
||||
func(ctx context.Context, args struct {
|
||||
Ignored string `description:"ignored, just here to make sure the function is called. Fill with anything."`
|
||||
}) (string, error) {
|
||||
return "", nil
|
||||
})
|
||||
|
||||
@ -192,6 +199,8 @@ func doesTextAnswerQuestion(ctx context.Context, q Question, text string) (strin
|
||||
}
|
||||
|
||||
func functionSearch(ctx context.Context, q Question, searchTerm string) (string, error) {
|
||||
|
||||
slog.Info("searching", "search", searchTerm, "question", q)
|
||||
res, err := q.Search.Search(ctx, searchTerm)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -207,6 +216,8 @@ func functionSearch(ctx context.Context, q Question, searchTerm string) (string,
|
||||
if trimmed == "" {
|
||||
|
||||
}
|
||||
|
||||
slog.Info("extracting article", "url", trimmed)
|
||||
u, err := url.Parse(trimmed)
|
||||
if err != nil {
|
||||
continue
|
||||
@ -218,6 +229,8 @@ func functionSearch(ctx context.Context, q Question, searchTerm string) (string,
|
||||
continue
|
||||
}
|
||||
|
||||
slog.Info("extracted article", "url", a.URL, "title", a.Title, "body", a.Body)
|
||||
|
||||
if a.Title != "" && a.Body != "" {
|
||||
answer, err := doesTextAnswerQuestion(ctx, q, a.Body)
|
||||
|
||||
@ -296,7 +309,7 @@ func (o Options) Answer(ctx context.Context, q Question) (Answers, error) {
|
||||
"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 {
|
||||
Question string `description:"the question to think about"`
|
||||
Question string `json:"question" description:"the question to think about"`
|
||||
}) (string, error) {
|
||||
q2 := q
|
||||
q2.Question = args.Question
|
||||
@ -308,7 +321,7 @@ func (o Options) Answer(ctx context.Context, q Question) (Answers, error) {
|
||||
"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 {
|
||||
Answer string `description:"the answer to the question"`
|
||||
Answer string `json:"answer" description:"the answer to the question"`
|
||||
}) (string, error) {
|
||||
return args.Answer, nil
|
||||
})
|
||||
@ -354,11 +367,9 @@ func (o Options) Answer(ctx context.Context, q Question) (Answers, error) {
|
||||
res.Choices = res.Choices[:o.MaxSearches]
|
||||
}
|
||||
|
||||
var answers Answers
|
||||
choicesOutput := make(chan string, len(res.Choices))
|
||||
|
||||
var answers []Result
|
||||
for _, choice := range res.Choices {
|
||||
fnChoice := func(choice gollm.ResponseChoice) string {
|
||||
fnChoice := func(choice gollm.ResponseChoice) []Result {
|
||||
var calls []Result
|
||||
var callsOutput = make(chan Result, len(choice.Calls))
|
||||
fnCall := func(call gollm.ToolCall) Result {
|
||||
@ -377,6 +388,15 @@ func (o Options) Answer(ctx context.Context, q Question) (Answers, error) {
|
||||
|
||||
for _, call := range choice.Calls {
|
||||
go func(call gollm.ToolCall) {
|
||||
if o.OnNewFunction != nil {
|
||||
err := o.OnNewFunction(ctx, call.FunctionCall.Name, q.Question, call.FunctionCall.Arguments)
|
||||
if err != nil {
|
||||
callsOutput <- Result{
|
||||
Error: err,
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
callsOutput <- fnCall(call)
|
||||
}(call)
|
||||
}
|
||||
@ -388,13 +408,29 @@ func (o Options) Answer(ctx context.Context, q Question) (Answers, error) {
|
||||
|
||||
close(callsOutput)
|
||||
|
||||
return calls
|
||||
}
|
||||
|
||||
answers = append(answers, fnChoice(choice))
|
||||
|
||||
answers = append(answers, fnChoice(choice)...)
|
||||
}
|
||||
|
||||
return answers, nil
|
||||
var errs []error
|
||||
var results []string
|
||||
|
||||
for _, answer := range answers {
|
||||
if answer.Error != nil {
|
||||
errs = append(errs, answer.Error)
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, answer.Result)
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
|
||||
}
|
||||
|
||||
|
16
pkg/cache/directory.go
vendored
16
pkg/cache/directory.go
vendored
@ -72,6 +72,18 @@ func (d *Directory) AutoCleanupRoutine(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Directory) openFileForWriting(key string) (*os.File, error) {
|
||||
path := d.GetPath(key)
|
||||
|
||||
fp, err := os.Create(path)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return fp, nil
|
||||
}
|
||||
|
||||
func (d *Directory) openFile(key string) (*os.File, error) {
|
||||
path := d.GetPath(key)
|
||||
|
||||
@ -91,7 +103,7 @@ func (d *Directory) Set(key string, value io.Reader) error {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
fp, err := d.openFile(key)
|
||||
fp, err := d.openFileForWriting(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -109,7 +121,7 @@ func (d *Directory) SetJSON(key string, value any) error {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
fp, err := d.openFile(key)
|
||||
fp, err := d.openFileForWriting(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"answer/pkg/cache"
|
||||
"context"
|
||||
googlesearch "github.com/rocketlaunchr/google-search"
|
||||
"log/slog"
|
||||
"sort"
|
||||
)
|
||||
|
||||
@ -20,21 +21,19 @@ func (g Google) Search(ctx context.Context, search string) ([]Result, error) {
|
||||
|
||||
err := g.Cache.GetJSON(key, &res)
|
||||
|
||||
slog.Info("searching", "search", search, "results", res, "err", err)
|
||||
|
||||
if err == nil {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
results, err := googlesearch.Search(ctx, search, googlesearch.SearchOptions{
|
||||
CountryCode: "",
|
||||
LanguageCode: "",
|
||||
Limit: 0,
|
||||
Start: 0,
|
||||
UserAgent: "",
|
||||
OverLimit: false,
|
||||
ProxyAddr: "",
|
||||
FollowNextPage: false,
|
||||
|
||||
UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
|
||||
})
|
||||
|
||||
slog.Info("searched", "search", search, "results", results, "err", err)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user