answer/cmd/agent/cmd.go
Steve Dudenhoeffer 1be5cc047c Fix browser closure timing in search agent
Move defer statement to ensure browser closure occurs only after assigning the browser to the context. This prevents potential issues of premature resource release.
2025-04-12 02:22:10 -04:00

126 lines
2.7 KiB
Go

package main
import (
"context"
"fmt"
"log/slog"
"os"
"strings"
gollm "gitea.stevedudenhoeffer.com/steve/go-llm"
"github.com/joho/godotenv"
"github.com/urfave/cli"
"gitea.stevedudenhoeffer.com/steve/answer/pkg/agents"
)
func getKey(key string, env string) string {
if key != "" {
return key
}
return os.Getenv(env)
}
func main() {
ctx := context.Background()
// Usage: go run cmd/answer.go question...
// - flags:
// --model=[model string such as openai/gpt-4o, anthropic/claude..., google/gemini-1.5. Default: openai/gpt-4o]
// --search-provider=[search provider string such as google, duckduckgo. Default: google]
// --cache-provider=[cache provider string such as memory, redis, file. Default: memory]
var app = cli.App{
Name: "answer",
Usage: "has an llm search the web for you to answer a question",
Version: "0.1",
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-mini",
Usage: "model to use for answering the question, syntax: provider/model such as openai/gpt-4o",
},
&cli.StringFlag{
Name: "llm-key",
Value: "",
Usage: "key for the llm model (if empty, will use env var of PROVIDER_API_KEY, such as OPENAI_API_KEY)",
},
},
Action: func(c *cli.Context) error {
// if there is no question to answer, print usage
if c.NArg() == 0 {
return cli.ShowAppHelp(c)
}
if c.String("env-file") != "" {
_ = godotenv.Load(c.String("env-file"))
}
var llm gollm.LLM
model := c.String("model")
a := strings.Split(model, "/")
if len(a) != 2 {
panic("invalid model, expected: provider/model (such as openai/gpt-4o)")
}
switch a[0] {
case "openai":
llm = gollm.OpenAI(getKey(c.String("llm-key"), "OPENAI_API_KEY"))
case "anthropic":
llm = gollm.Anthropic(getKey(c.String("llm-key"), "ANTHROPIC_API_KEY"))
case "google":
llm = gollm.Google(getKey(c.String("llm-key"), "GOOGLE_API_KEY"))
default:
panic("unknown model provider")
}
m, err := llm.ModelVersion(a[1])
if err != nil {
panic(err)
}
question := strings.Join(c.Args(), " ")
agent := agents.NewAgent(m, gollm.ToolBox{}).WithMaxCalls(200)
knowledge, err := agent.SearchAndRead(ctx, question, []string{question}, true, 10)
if err != nil {
panic(err)
}
slog.Info("knowledge", "knowledge", knowledge)
res, err := agent.AnswerQuestionWithKnowledge(ctx, knowledge)
if err != nil {
panic(err)
}
fmt.Println(res)
return nil
},
}
err := app.Run(os.Args)
if err != nil {
slog.Error("Error: ", err)
}
}