diff --git a/.vscode/launch.json b/.vscode/launch.json index b427fbd..ac1d53f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,7 +5,16 @@ "version": "0.2.0", "configurations": [ { - "name": "Launch Package", + "name": "Debug `bot`", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${fileDirname}", + "env": {"DEBUG": "1"}, + "console": "integratedTerminal", + }, + { + "name": "Debug `bot nc`", "type": "go", "request": "launch", "mode": "auto", @@ -13,6 +22,26 @@ "env": {"DEBUG": "1"}, "console": "integratedTerminal", "args": ["nc"] + }, + { + "name": "Debug `bot -mode config chron`", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${fileDirname}", + "env": {"DEBUG": "1"}, + "console": "integratedTerminal", + "args": ["-mode","config","cron"] + }, + { + "name": "Debug `bot i`", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${fileDirname}", + "env": {"DEBUG": "1"}, + "console": "integratedTerminal", + "args": ["-i"] } ] } \ No newline at end of file diff --git a/bot.go b/bot.go index 5d2ec26..941ce23 100644 --- a/bot.go +++ b/bot.go @@ -1,6 +1,7 @@ package main import ( + "embed" _ "embed" "flag" "fmt" @@ -11,41 +12,51 @@ import ( "sync" ) -//go:embed prompt_prefix.txt -var promptPrefix string +/* +- The "go:embed" directive must immediately precede a line containing the declaration of a single variable +- Therefore, this program embeds all files in the current folder that match prompt_prefix*.txt into a file system variable "promptPrefixFS" +- Then, depending on the flags passed to the command, the actual "promptPrefix" is defined by reading from "promptPrefixFS" +- This method makes use of the embed directive while allowing the final "promptPrefix" to be conditionally defined +*/ + +//go:embed prompt_prefix*.txt +var promptPrefixFS embed.FS const promptStartText = "\n[EXAMPLES]\n" +var err error + func main() { args := parseArgs() - if len(args.Prompt) == 0 { - usage() - os.Exit(1) + + var promptPrefix []byte + switch args.Config { + case false: + promptPrefix, err = promptPrefixFS.ReadFile("prompt_prefix_command.txt") + errHandler(err, "Error reading prompt_prefix_command") + case true: + promptPrefix, err = promptPrefixFS.ReadFile("prompt_prefix_config.txt") + errHandler(err, "Error reading prompt_prefix_config") } homeDir, err := os.UserHomeDir() - if err != nil { - fmt.Fprintf(os.Stderr, "Error retrieving home directory: %v", err) - os.Exit(1) - } + errHandler(err, "Error retrieving home directory") - configPath := filepath.Join(homeDir, configFilename) - config, err := loadConfig(configPath) - if err != nil { - fmt.Fprintf(os.Stderr, "Error loading config: %v", err) - os.Exit(1) - } + botConfigPath := filepath.Join(homeDir, configFilename) + botConfig, err := loadConfig(botConfigPath) + errHandler(err, "Error loading config") respCh := make(chan string) var wg sync.WaitGroup wg.Add(2) - client := &OpenAIAdapter{APIKey: config.APIKey} + client := &OpenAIAdapter{APIKey: botConfig.APIKey} go func() { defer wg.Done() - err := client.FetchCompletionStream(CreateRequest(args.Prompt), respCh) + err := client.FetchCompletionStream(CreateRequest(string(promptPrefix), args.Prompt), respCh) + errHandler(err) if err != nil { fmt.Fprintf(os.Stderr, "%v", err) os.Exit(1) @@ -61,19 +72,36 @@ func main() { } type Args struct { + Help bool Interactive bool + Config bool Prompt string } func parseArgs() *Args { - interactive := flag.Bool("i", false, "Enter interactive mode (optional)") + + help := flag.Bool("help", false, "Display usage options") + interactive := flag.Bool("i", false, "Enter interactive mode (coming soon!)") + config := flag.Bool("config", false, "Optional flag to operate as config bot") flag.Parse() prompt := strings.Join(flag.Args(), " ") - return &Args{Interactive: *interactive, Prompt: prompt} + + if *help || len(os.Args) < 2 || len(prompt) == 0 { + Usage() + } + + return &Args{Help: *help, Interactive: *interactive, Config: *config, Prompt: prompt} } -func usage() { - fmt.Fprintf(os.Stderr, "Usage: %s [prompt]", os.Args[0]) +func Usage() { + fmt.Println("Simple command-line utility for looking up command usage or config file examples.") + fmt.Println("Flags:") + flag.PrintDefaults() + fmt.Println("Example usage:") + fmt.Println(" bot nc") + fmt.Println(" bot -config cron") + fmt.Println(" bot -help") + os.Exit(1) } func RenderCompletionStreamResponse(w io.Writer, respCh <-chan string) { @@ -82,3 +110,15 @@ func RenderCompletionStreamResponse(w io.Writer, respCh <-chan string) { } fmt.Fprintln(w, "") } + +func errHandler(err error, args ...string) { + errString := "Error" + if len(args) > 0 { + errString = args[0] + } + + if err != nil { + fmt.Fprintf(os.Stderr, "%s: %v", errString, err) + os.Exit(1) + } +} diff --git a/openai_adapter.go b/openai_adapter.go index 5740635..13805f5 100644 --- a/openai_adapter.go +++ b/openai_adapter.go @@ -13,7 +13,7 @@ type OpenAIAdapter struct { APIKey string } -func CreateRequest(prompt string) openai.CompletionRequest { +func CreateRequest(promptPrefix string, prompt string) openai.CompletionRequest { return openai.CompletionRequest{ Model: openai.GPT3TextDavinci003, MaxTokens: 1024, diff --git a/openai_adapter_test.go b/openai_adapter_test.go index 38d27e6..770dea8 100644 --- a/openai_adapter_test.go +++ b/openai_adapter_test.go @@ -27,7 +27,7 @@ func TestFetchCompletionStream(t *testing.T) { go func() { defer wg.Done() - err := client.FetchCompletionStream(CreateRequest("prompt"), respCh) + err := client.FetchCompletionStream(CreateRequest("promptPrefix", "prompt"), respCh) if err != nil { fmt.Println(err) } diff --git a/prompt_prefix.txt b/prompt_prefix_command.txt similarity index 100% rename from prompt_prefix.txt rename to prompt_prefix_command.txt diff --git a/prompt_prefix_config.txt b/prompt_prefix_config.txt new file mode 100644 index 0000000..1472835 --- /dev/null +++ b/prompt_prefix_config.txt @@ -0,0 +1,23 @@ +Provide example usage of configuraiton file information when the user provides the configuration file name or description. Use variable names in place of user-provided arguments. If the configuration file does not exist, respond with [NOT FOUND]. + +COMMAND>.ssh/config +[EXAMPLES] +# Basic use example +Host dev + HostName $HOSTNAME + User $USERNAME + Port $PORT + +# Advanced use example +Host dev + HostName $HOSTNAME + User $USERNAME + Port $PORT + IdentityFile $IDENTITYFILE + LogLevel $LOGLEVEL + Compression $COMPRESSION + +[REFERENCES] +- https://linuxize.com/post/using-the-ssh-config-file/ + +COMMAND>