-
Notifications
You must be signed in to change notification settings - Fork 0
Amir deris/support continuous conversation #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,6 +1,7 @@ | ||||||
| package main | ||||||
|
|
||||||
| import ( | ||||||
| "bufio" | ||||||
| "context" | ||||||
| "errors" | ||||||
| "fmt" | ||||||
|
|
@@ -22,19 +23,22 @@ type Gromit struct { | |||||
| cli.Command | ||||||
| AssisterCreator | ||||||
| messagePrinter | ||||||
| *configuration | ||||||
| } | ||||||
|
|
||||||
| type messagePrinter struct { | ||||||
| config *configuration | ||||||
| w io.Writer | ||||||
| promptPrefix string | ||||||
| } | ||||||
|
|
||||||
| type configuration struct { | ||||||
| promptPrefix string | ||||||
| w io.Writer | ||||||
| promptPrefix string | ||||||
| w io.Writer | ||||||
| askForConfirmation bool | ||||||
| } | ||||||
|
|
||||||
| func (m *messagePrinter) print(s string) { | ||||||
| fmt.Fprintf(m.config.w, "%s %s\n", m.config.promptPrefix, s) | ||||||
| fmt.Fprintf(m.w, "%s %s\n", m.promptPrefix, s) | ||||||
| } | ||||||
|
|
||||||
| type ConfigurationModifier func(*configuration) error | ||||||
|
|
@@ -53,17 +57,51 @@ func WithWriter(writer io.Writer) ConfigurationModifier { | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| func (g *Gromit) actionGromit(ctx context.Context, command *cli.Command) error { | ||||||
| assister, err := g.AssisterCreator.GetAssister(g.String("agent"), g.String("model")) | ||||||
| if err != nil { | ||||||
| return err | ||||||
| func WithAskForConfirmation(confirm bool) ConfigurationModifier { | ||||||
| return func(c *configuration) error { | ||||||
| c.askForConfirmation = confirm | ||||||
| return nil | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| func (g *Gromit) actionGromit(ctx context.Context, command *cli.Command) error { | ||||||
| commandArgs := command.Args().Slice() | ||||||
| query := strings.Join(commandArgs, " ") | ||||||
| if query == "" { | ||||||
| g.print("Please run ./gromit --help to see usage") | ||||||
| return nil | ||||||
| } | ||||||
| err := g.handleUserQuery(ctx, query) | ||||||
| if err != nil { | ||||||
| return err | ||||||
| } | ||||||
| for { | ||||||
| confirmation, err := g.askConfirmation("Can I help you with anything else?") | ||||||
| if err != nil { | ||||||
| return err | ||||||
| } | ||||||
| if confirmation.confirm { | ||||||
| g.print("How can I help?") | ||||||
| reader := bufio.NewReader(os.Stdin) | ||||||
| query, err := reader.ReadString('\n') | ||||||
| if err != nil { | ||||||
| return err | ||||||
| } | ||||||
| if err = g.handleUserQuery(ctx, query); err != nil { | ||||||
| return err | ||||||
| } | ||||||
| } else { | ||||||
| break | ||||||
| } | ||||||
| } | ||||||
| return nil | ||||||
| } | ||||||
|
|
||||||
| func (g *Gromit) handleUserQuery(ctx context.Context, query string) error { | ||||||
| assister, err := g.AssisterCreator.GetAssister(g.String("agent"), g.String("model")) | ||||||
| if err != nil { | ||||||
| return err | ||||||
| } | ||||||
| prompt := g.String("systemPrompt") | ||||||
| if prompt == "" { | ||||||
| prompt = systemPrompt | ||||||
|
|
@@ -74,40 +112,66 @@ func (g *Gromit) actionGromit(ctx context.Context, command *cli.Command) error { | |||||
| } | ||||||
| g.print("In order to do that, you need to run:") | ||||||
| g.print(exeCommand) | ||||||
| g.print("Would you like to run this command?") | ||||||
|
|
||||||
| confirmation, err := g.askConfirmation("Would you like to run this command?") | ||||||
| if err != nil { | ||||||
| g.print("Error reading your response") | ||||||
| return err | ||||||
| } | ||||||
| if confirmation.confirm { | ||||||
| err = g.executeCommand(exeCommand) | ||||||
| if err != nil { | ||||||
| return err | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the future work we could keep the conversation continuous and try to resolve this with more AI calls? Please feel free to capture an issue if that feature makes sense.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure sounds good. Created issue #19 |
||||||
| } | ||||||
| } else { | ||||||
| g.print("You chose not to execute this command.") | ||||||
| } | ||||||
| return nil | ||||||
| } | ||||||
|
|
||||||
| type userConfirmation struct { | ||||||
| confirm bool | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe confirmed is a better name?
Suggested change
|
||||||
| } | ||||||
|
|
||||||
| func (g *Gromit) askConfirmation(message string) (userConfirmation, error) { | ||||||
| if !g.configuration.askForConfirmation { | ||||||
| return userConfirmation{ | ||||||
| confirm: true, | ||||||
| }, nil | ||||||
| } | ||||||
| g.print(message) | ||||||
| var userConfirmation userConfirmation | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would pick a different name for this variable to make the code easier to read. |
||||||
| var userResponse string | ||||||
| n, err := fmt.Scanln(&userResponse) | ||||||
| userResponse = strings.ToLower(userResponse) | ||||||
| switch { | ||||||
| case n == 0: | ||||||
| g.print("You didn't specify whether you want to run this command!") | ||||||
| return nil | ||||||
| g.print("You didn't confirm your choice! Please reply with yes(y) or no(n).") | ||||||
| return g.askConfirmation(message) | ||||||
| case err != nil: | ||||||
| g.print("Error reading your response") | ||||||
| return err | ||||||
| return userConfirmation, err | ||||||
| case userResponse == "yes" || userResponse == "y": | ||||||
| g.print("Running the command...") | ||||||
| err := g.executeCommand(exeCommand) | ||||||
| if err != nil { | ||||||
| g.print(fmt.Sprintf("error running the command: %s", err.Error())) | ||||||
| return err | ||||||
| } else { | ||||||
| g.print("Done!") | ||||||
| } | ||||||
| userConfirmation.confirm = true | ||||||
| case userResponse == "no" || userResponse == "n": | ||||||
| g.print("You chose not to execute this command.") | ||||||
| userConfirmation.confirm = false | ||||||
| } | ||||||
| return nil | ||||||
| return userConfirmation, nil | ||||||
| } | ||||||
|
|
||||||
| func (g *Gromit) executeCommand(command string) error { | ||||||
| g.print("Running the command...") | ||||||
| c := exec.Command("sh", "-c", command) | ||||||
| output, err := c.CombinedOutput() | ||||||
| if err != nil { | ||||||
| g.print(fmt.Sprintf("error running the command: %s", err.Error())) | ||||||
| return err | ||||||
| } else { | ||||||
| const lineWidth = 50 | ||||||
| g.print("Command output:") | ||||||
| g.print(strings.Repeat("-", lineWidth)) | ||||||
| g.print(string(output)) | ||||||
| g.print(strings.Repeat("-", lineWidth)) | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the future it would make sense to decouple the text output formatting from actions. This is where they return structs of data then a printer turns them into text output. For now as is, is more than fine 👍
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for feedback. Sure, created issue #20 |
||||||
| return nil | ||||||
| } | ||||||
| } | ||||||
|
|
@@ -139,25 +203,30 @@ func NewGromit(a AssisterCreator, mods ...ConfigurationModifier) (*Gromit, error | |||||
| Usage: "The system prompt for the AI agent. Defaults to command line helper in a linux environment.", | ||||||
| }, | ||||||
| } | ||||||
| config := configuration{ | ||||||
| promptPrefix: "⚡️🐶", | ||||||
| w: os.Stdout, | ||||||
| askForConfirmation: true, | ||||||
| } | ||||||
| gromit := Gromit{ | ||||||
| AssisterCreator: a, | ||||||
| Command: cli.Command{ | ||||||
| Usage: "A command line helper that uses generative AI to generate commands based on user input.", | ||||||
| Name: "gromit", | ||||||
| Flags: flags, | ||||||
| }, | ||||||
| } | ||||||
| gromit.Action = gromit.actionGromit | ||||||
| gromit.messagePrinter = messagePrinter{ | ||||||
| config: &configuration{ | ||||||
| promptPrefix: "🐶", | ||||||
| w: os.Stdout, | ||||||
| }, | ||||||
| configuration: &config, | ||||||
| } | ||||||
| for _, apply := range mods { | ||||||
| if err := apply(gromit.messagePrinter.config); err != nil { | ||||||
| if err := apply(gromit.configuration); err != nil { | ||||||
| return nil, err | ||||||
| } | ||||||
| } | ||||||
| gromit.Action = gromit.actionGromit | ||||||
| gromit.messagePrinter = messagePrinter{ | ||||||
| promptPrefix: gromit.configuration.promptPrefix, | ||||||
| w: gromit.configuration.w, | ||||||
| } | ||||||
|
|
||||||
| return &gromit, nil | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.