diff --git a/README.md b/README.md index eddcf16..1be0c35 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,7 @@ Application Options: -u, --url= Choose ollama url (default: http://127.0.0.1:11434) -o, --output= Output to file -n, --latest= Number of latest patterns to list (default: 0) + --dry-run Show what would be sent to the model without actually sending it Help Options: -h, --help Show this help message diff --git a/cli/cli.go b/cli/cli.go index f8178de..1a5d4f4 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -143,7 +143,7 @@ func Cli() (message string, err error) { } var chatter *core.Chatter - if chatter, err = fabric.GetChatter(currentFlags.Model, currentFlags.Stream); err != nil { + if chatter, err = fabric.GetChatter(currentFlags.Model, currentFlags.Stream, currentFlags.DryRun); err != nil { return } diff --git a/cli/flags.go b/cli/flags.go index 872f5f9..efbf8f9 100644 --- a/cli/flags.go +++ b/cli/flags.go @@ -37,6 +37,7 @@ type Flags struct { YouTube string `short:"y" long:"youtube" description:"YouTube video url to grab transcript, comments from it and send to chat"` YouTubeTranscript bool `long:"transcript" description:"Grab transcript from YouTube video and send to chat"` YouTubeComments bool `long:"comments" description:"Grab comments from YouTube video and send to chat"` + DryRun bool `long:"dry-run" description:"Show what would be sent to the model without actually sending it"` } // Init Initialize flags. returns a Flags struct and an error diff --git a/core/chatter.go b/core/chatter.go index 0576578..f2ed9b5 100644 --- a/core/chatter.go +++ b/core/chatter.go @@ -13,6 +13,7 @@ type Chatter struct { db *db.Db Stream bool + DryRun bool model string vendor vendors.Vendor diff --git a/core/fabric.go b/core/fabric.go index 7e295d2..7616ea5 100644 --- a/core/fabric.go +++ b/core/fabric.go @@ -3,20 +3,22 @@ package core import ( "bytes" "fmt" + "os" + "strconv" + "strings" + "github.com/atotto/clipboard" "github.com/danielmiessler/fabric/common" "github.com/danielmiessler/fabric/db" "github.com/danielmiessler/fabric/vendors/anthropic" "github.com/danielmiessler/fabric/vendors/azure" + "github.com/danielmiessler/fabric/vendors/dryrun" "github.com/danielmiessler/fabric/vendors/gemini" "github.com/danielmiessler/fabric/vendors/groc" "github.com/danielmiessler/fabric/vendors/ollama" "github.com/danielmiessler/fabric/vendors/openai" "github.com/danielmiessler/fabric/youtube" "github.com/pkg/errors" - "os" - "strconv" - "strings" ) const DefaultPatternsGitRepoUrl = "https://github.com/danielmiessler/fabric.git" @@ -182,13 +184,20 @@ func (o *Fabric) configure() (err error) { return } -func (o *Fabric) GetChatter(model string, stream bool) (ret *Chatter, err error) { +func (o *Fabric) GetChatter(model string, stream bool, dryRun bool) (ret *Chatter, err error) { ret = &Chatter{ db: o.Db, Stream: stream, + DryRun: dryRun, } - if model == "" { + if dryRun { + ret.vendor = dryrun.NewClient() + ret.model = model + if ret.model == "" { + ret.model = o.DefaultModel.Value + } + } else if model == "" { ret.vendor = o.FindByName(o.DefaultVendor.Value) ret.model = o.DefaultModel.Value } else { diff --git a/vendors/dryrun/dryrun.go b/vendors/dryrun/dryrun.go new file mode 100644 index 0000000..c13350c --- /dev/null +++ b/vendors/dryrun/dryrun.go @@ -0,0 +1,89 @@ +package dryrun + +import ( + "bytes" + "context" + "fmt" + + "github.com/danielmiessler/fabric/common" +) + +type Client struct{} + +func NewClient() *Client { + return &Client{} +} + +func (c *Client) GetName() string { + return "DryRun" +} + +func (c *Client) IsConfigured() bool { + return true +} + +func (c *Client) Configure() error { + return nil +} + +func (c *Client) ListModels() ([]string, error) { + return []string{"dry-run-model"}, nil +} + +func (c *Client) SendStream(msgs []*common.Message, opts *common.ChatOptions, channel chan string) error { + output := "Dry run: Would send the following request:\n\n" + + for _, msg := range msgs { + switch msg.Role { + case "system": + output += fmt.Sprintf("System:\n%s\n\n", msg.Content) + case "user": + output += fmt.Sprintf("User:\n%s\n\n", msg.Content) + default: + output += fmt.Sprintf("%s:\n%s\n\n", msg.Role, msg.Content) + } + } + + output += "Options:\n" + output += fmt.Sprintf("Model: %s\n", opts.Model) + output += fmt.Sprintf("Temperature: %f\n", opts.Temperature) + output += fmt.Sprintf("TopP: %f\n", opts.TopP) + output += fmt.Sprintf("PresencePenalty: %f\n", opts.PresencePenalty) + output += fmt.Sprintf("FrequencyPenalty: %f\n", opts.FrequencyPenalty) + + channel <- output + close(channel) + return nil +} + +func (c *Client) Send(ctx context.Context, msgs []*common.Message, opts *common.ChatOptions) (string, error) { + fmt.Println("Dry run: Would send the following request:") + + for _, msg := range msgs { + switch msg.Role { + case "system": + fmt.Printf("System:\n%s\n\n", msg.Content) + case "user": + fmt.Printf("User:\n%s\n\n", msg.Content) + default: + fmt.Printf("%s:\n%s\n\n", msg.Role, msg.Content) + } + } + + fmt.Println("Options:") + fmt.Printf("Model: %s\n", opts.Model) + fmt.Printf("Temperature: %f\n", opts.Temperature) + fmt.Printf("TopP: %f\n", opts.TopP) + fmt.Printf("PresencePenalty: %f\n", opts.PresencePenalty) + fmt.Printf("FrequencyPenalty: %f\n", opts.FrequencyPenalty) + + return "", nil +} + +func (c *Client) Setup() error { + return nil +} + +func (c *Client) SetupFillEnvFileContent(buffer *bytes.Buffer) { + // No environment variables needed for dry run +}