Skip to content

Commit

Permalink
feat: add multiline mode support
Browse files Browse the repository at this point in the history
  • Loading branch information
kardolus authored Oct 14, 2024
1 parent 7fe974e commit 86684f7
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 23 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ environment variables, a config.yaml file, and default values, in that respectiv
| `track_token_usage` | If set to true, displays the total token usage after each query in --query mode, helping you monitor API usage. | `false` |
| `debug` | If set to true, prints the raw request and response data during API calls, useful for debugging. | `false` |
| `skip_tls_verify` | If set to true, skips TLS certificate verification, allowing insecure HTTPS requests. | `false` |
| `multiline` | If set to true, enables multiline input mode in interactive sessions. | `false` |

### LLM-Specific Configuration

Expand Down
88 changes: 65 additions & 23 deletions cmd/chatgpt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,18 @@ var configMetadata = []ConfigMetadata{
{"models_path", "set-models-path", "/v1/models", "Set the models API endpoint"},
{"auth_header", "set-auth-header", "Authorization", "Set the authorization header"},
{"auth_token_prefix", "set-auth-token-prefix", "Bearer ", "Set the authorization token prefix"},
{"command_prompt", "set-command-prompt", "[%datetime] [Q%counter] [%usage]", "Set the command prompt format"},
{"output_prompt", "set-output-prompt", "", "Set the output prompt format"},
{"command_prompt", "set-command-prompt", "[%datetime] [Q%counter] [%usage]", "Set the command prompt format for interactive mode"},
{"output_prompt", "set-output-prompt", "", "Set the output prompt format for interactive mode"},
{"temperature", "set-temperature", 1.0, "Set the sampling temperature"},
{"top_p", "set-top-p", 1.0, "Set the top-p value for nucleus sampling"},
{"frequency_penalty", "set-frequency-penalty", 0.0, "Set the frequency penalty"},
{"presence_penalty", "set-presence-penalty", 0.0, "Set the presence penalty"},
{"omit_history", "set-omit-history", false, "Omit history in the conversation"},
{"auto_create_new_thread", "set-auto-create-new-thread", true, "Automatically create a new thread for each session"},
{"auto_create_new_thread", "set-auto-create-new-thread", true, "Create a new thread for each interactive session"},
{"track_token_usage", "set-track-token-usage", true, "Track token usage"},
{"skip_tls_verify", "set-skip-tls-verify", false, "Skip TLS certificate verification"},
{"debug", "set-debug", false, "Enable debug mode"},
{"multiline", "set-multiline", false, "Enables multiline mode while in interactive mode"},
{"name", "set-name", "openai", "The prefix for environment variable overrides"},
}

Expand Down Expand Up @@ -244,7 +245,7 @@ func run(cmd *cobra.Command, args []string) error {
}

if interactiveMode {
fmt.Printf("Entering interactive mode. Using thread ‘%s’. Type clear to clear the screen, exit to quit, or press Ctrl+C.\n\n", hs.GetThread())
fmt.Printf("Entering interactive mode. Using thread '%s'. Type 'clear' to clear the screen, 'exit' to quit, or press Ctrl+C.\n\n", hs.GetThread())
rl, err := readline.New("")
if err != nil {
return err
Expand All @@ -259,30 +260,16 @@ func run(cmd *cobra.Command, args []string) error {
for {
rl.SetPrompt(commandPrompt(qNum, usage))

line, err := rl.Readline()
if errors.Is(err, readline.ErrInterrupt) || err == io.EOF {
input, err := readInput(rl, cfg.Multiline)
if err == io.EOF {
fmt.Println("Bye!")
break
}

if line == "clear" {
ansiClearScreenCode := "\033[H\033[2J"
fmt.Print(ansiClearScreenCode)
continue
}

if line == "exit" || line == "/q" {
fmt.Println("Bye!")
if queryMode {
fmt.Printf("Total tokens used: %d\n", usage)
}
break
return nil
}

fmtOutputPrompt := utils.FormatPrompt(client.Config.OutputPrompt, qNum, usage, time.Now())

if queryMode {
result, qUsage, err := client.Query(line)
result, qUsage, err := client.Query(input)
if err != nil {
fmt.Println("Error:", err)
} else {
Expand All @@ -292,7 +279,7 @@ func run(cmd *cobra.Command, args []string) error {
}
} else {
fmt.Print(fmtOutputPrompt)
if err := client.Stream(line); err != nil {
if err := client.Stream(input); err != nil {
fmt.Fprintln(os.Stderr, "Error:", err)
} else {
fmt.Println()
Expand Down Expand Up @@ -370,6 +357,60 @@ func readConfigWithComments(configPath string) (*yaml.Node, error) {
return &rootNode, nil
}

func readInput(rl *readline.Instance, multiline bool) (string, error) {
var lines []string

if multiline {
fmt.Println("Multiline mode enabled. Type 'EOF' on a new line to submit your query.")
}

// Custom keybinding to handle backspace in multiline mode
rl.Config.SetListener(func(line []rune, pos int, key rune) ([]rune, int, bool) {
// Check if backspace is pressed and if multiline mode is enabled
if multiline && key == readline.CharBackspace && pos == 0 && len(lines) > 0 {
fmt.Print("\033[A") // Move cursor up one line

// Print the last line without clearing
lastLine := lines[len(lines)-1]
fmt.Print(lastLine)

// Remove the last line from the slice
lines = lines[:len(lines)-1]

// Set the cursor at the end of the previous line
return []rune(lastLine), len(lastLine), true
}
return line, pos, false // Default behavior for other keys
})

for {
line, err := rl.Readline()
if errors.Is(err, readline.ErrInterrupt) || err == io.EOF {
return "", io.EOF
}

switch line {
case "clear":
fmt.Print("\033[H\033[2J") // ANSI escape code to clear the screen
continue
case "exit", "/q":
return "", io.EOF
}

if multiline {
if line == "EOF" {
break
}
lines = append(lines, line)
} else {
return line, nil
}
}

// Join and return all accumulated lines as a single string
return strings.Join(lines, "\n"), nil
}

func updateConfig(node *yaml.Node, changes map[string]interface{}) error {
// If the node is not a document or has no content, create an empty mapping node.
if node.Kind != yaml.DocumentNode || len(node.Content) == 0 {
Expand Down Expand Up @@ -657,6 +698,7 @@ func createConfigFromViper() types.Config {
TrackTokenUsage: viper.GetBool("track_token_usage"),
SkipTLSVerify: viper.GetBool("skip_tls_verify"),
Debug: viper.GetBool("debug"),
Multiline: viper.GetBool("multiline"),
}
}

Expand Down
1 change: 1 addition & 0 deletions types/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ type Config struct {
TrackTokenUsage bool `yaml:"track_token_usage"`
SkipTLSVerify bool `yaml:"skip_tls_verify"`
Debug bool `yaml:"debug"`
Multiline bool `yaml:"multiline"`
}

0 comments on commit 86684f7

Please sign in to comment.