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"
|
"answer/pkg/search"
|
||||||
"context"
|
"context"
|
||||||
gollm "gitea.stevedudenhoeffer.com/steve/go-llm"
|
gollm "gitea.stevedudenhoeffer.com/steve/go-llm"
|
||||||
|
"github.com/joho/godotenv"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
@ -36,6 +37,12 @@ func main() {
|
|||||||
Description: "",
|
Description: "",
|
||||||
|
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "env-file",
|
||||||
|
Value: ".env",
|
||||||
|
Usage: "file to read environment variables from",
|
||||||
|
},
|
||||||
|
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "model",
|
Name: "model",
|
||||||
Value: "openai/gpt-4o",
|
Value: "openai/gpt-4o",
|
||||||
@ -66,6 +73,11 @@ func main() {
|
|||||||
if c.NArg() == 0 {
|
if c.NArg() == 0 {
|
||||||
return cli.ShowAppHelp(c)
|
return cli.ShowAppHelp(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.String("env-file") != "" {
|
||||||
|
_ = godotenv.Load(c.String("env-file"))
|
||||||
|
}
|
||||||
|
|
||||||
var question answer.Question
|
var question answer.Question
|
||||||
|
|
||||||
question.Question = strings.Join(c.Args(), " ")
|
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 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 (
|
require (
|
||||||
gitea.stevedudenhoeffer.com/steve/go-llm v0.0.0-20241031152103-f603010dee49
|
gitea.stevedudenhoeffer.com/steve/go-llm v0.0.0-20241031152103-f603010dee49
|
||||||
github.com/advancedlogic/GoOse v0.0.0-20231203033844-ae6b36caf275
|
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/playwright-community/playwright-go v0.4702.0
|
||||||
github.com/rocketlaunchr/google-search v1.1.6
|
github.com/rocketlaunchr/google-search v1.1.6
|
||||||
github.com/urfave/cli v1.22.16
|
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
|
// 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.
|
// 5 pages are tried and no answers are found, the function will return ErrMaxTries.
|
||||||
MaxTries int
|
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{
|
var DefaultOptions = Options{
|
||||||
@ -152,7 +157,9 @@ func doesTextAnswerQuestion(ctx context.Context, q Question, text string) (strin
|
|||||||
fnNoAnswer := gollm.NewFunction(
|
fnNoAnswer := gollm.NewFunction(
|
||||||
"no_answer",
|
"no_answer",
|
||||||
"Indicate that the text does not answer the question.",
|
"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
|
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) {
|
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)
|
res, err := q.Search.Search(ctx, searchTerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -207,6 +216,8 @@ func functionSearch(ctx context.Context, q Question, searchTerm string) (string,
|
|||||||
if trimmed == "" {
|
if trimmed == "" {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
slog.Info("extracting article", "url", trimmed)
|
||||||
u, err := url.Parse(trimmed)
|
u, err := url.Parse(trimmed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
@ -218,6 +229,8 @@ func functionSearch(ctx context.Context, q Question, searchTerm string) (string,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
slog.Info("extracted article", "url", a.URL, "title", a.Title, "body", a.Body)
|
||||||
|
|
||||||
if a.Title != "" && a.Body != "" {
|
if a.Title != "" && a.Body != "" {
|
||||||
answer, err := doesTextAnswerQuestion(ctx, q, 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",
|
||||||
"Think about a question. This is useful for breaking down complex questions into smaller parts that are easier to answer.",
|
"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 context.Context, args struct {
|
||||||
Question string `description:"the question to think about"`
|
Question string `json:"question" description:"the question to think about"`
|
||||||
}) (string, error) {
|
}) (string, error) {
|
||||||
q2 := q
|
q2 := q
|
||||||
q2.Question = args.Question
|
q2.Question = args.Question
|
||||||
@ -308,7 +321,7 @@ func (o Options) Answer(ctx context.Context, q Question) (Answers, error) {
|
|||||||
"answer",
|
"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",
|
"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 context.Context, args struct {
|
||||||
Answer string `description:"the answer to the question"`
|
Answer string `json:"answer" description:"the answer to the question"`
|
||||||
}) (string, error) {
|
}) (string, error) {
|
||||||
return args.Answer, nil
|
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]
|
res.Choices = res.Choices[:o.MaxSearches]
|
||||||
}
|
}
|
||||||
|
|
||||||
var answers Answers
|
var answers []Result
|
||||||
choicesOutput := make(chan string, len(res.Choices))
|
|
||||||
|
|
||||||
for _, choice := range res.Choices {
|
for _, choice := range res.Choices {
|
||||||
fnChoice := func(choice gollm.ResponseChoice) string {
|
fnChoice := func(choice gollm.ResponseChoice) []Result {
|
||||||
var calls []Result
|
var calls []Result
|
||||||
var callsOutput = make(chan Result, len(choice.Calls))
|
var callsOutput = make(chan Result, len(choice.Calls))
|
||||||
fnCall := func(call gollm.ToolCall) Result {
|
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 {
|
for _, call := range choice.Calls {
|
||||||
go func(call gollm.ToolCall) {
|
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)
|
callsOutput <- fnCall(call)
|
||||||
}(call)
|
}(call)
|
||||||
}
|
}
|
||||||
@ -388,13 +408,29 @@ func (o Options) Answer(ctx context.Context, q Question) (Answers, error) {
|
|||||||
|
|
||||||
close(callsOutput)
|
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) {
|
func (d *Directory) openFile(key string) (*os.File, error) {
|
||||||
path := d.GetPath(key)
|
path := d.GetPath(key)
|
||||||
|
|
||||||
@ -91,7 +103,7 @@ func (d *Directory) Set(key string, value io.Reader) error {
|
|||||||
d.lock.Lock()
|
d.lock.Lock()
|
||||||
defer d.lock.Unlock()
|
defer d.lock.Unlock()
|
||||||
|
|
||||||
fp, err := d.openFile(key)
|
fp, err := d.openFileForWriting(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -109,7 +121,7 @@ func (d *Directory) SetJSON(key string, value any) error {
|
|||||||
d.lock.Lock()
|
d.lock.Lock()
|
||||||
defer d.lock.Unlock()
|
defer d.lock.Unlock()
|
||||||
|
|
||||||
fp, err := d.openFile(key)
|
fp, err := d.openFileForWriting(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"answer/pkg/cache"
|
"answer/pkg/cache"
|
||||||
"context"
|
"context"
|
||||||
googlesearch "github.com/rocketlaunchr/google-search"
|
googlesearch "github.com/rocketlaunchr/google-search"
|
||||||
|
"log/slog"
|
||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -20,21 +21,19 @@ func (g Google) Search(ctx context.Context, search string) ([]Result, error) {
|
|||||||
|
|
||||||
err := g.Cache.GetJSON(key, &res)
|
err := g.Cache.GetJSON(key, &res)
|
||||||
|
|
||||||
|
slog.Info("searching", "search", search, "results", res, "err", err)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
results, err := googlesearch.Search(ctx, search, googlesearch.SearchOptions{
|
results, err := googlesearch.Search(ctx, search, googlesearch.SearchOptions{
|
||||||
CountryCode: "",
|
|
||||||
LanguageCode: "",
|
UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
|
||||||
Limit: 0,
|
|
||||||
Start: 0,
|
|
||||||
UserAgent: "",
|
|
||||||
OverLimit: false,
|
|
||||||
ProxyAddr: "",
|
|
||||||
FollowNextPage: false,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
slog.Info("searched", "search", search, "results", results, "err", err)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user