So I've been wanting to try out Golang for a while now and I had a YouTube video about Cobra and how easy it is to make a CLI with it, so I decided to give it a try but instead of making a simple CLI I wanted to use an API to make it more interesting.

What is Cobra ?

Cobra is a CLI library for Go, it provides a simple interface to create powerful CLI interfaces with flags, arguments, and subcommands.

Getting Started

I started by creating a new project with

go mod init

then I followed the instruction to install Cobra

go get github.com/spf13/cobra/cobra

After reading a bit about Golang and it's project structure and package system I went ahead and created a couple files and folders.

  ▾ cligpt/
    ▾ cmd/
        version.go
        ask.go
        root.go
    main.go

Creating our first command

We first have to create the root command which will be the parent of all the other commands, edit the root.go file and add the following code.

var rootCmd = &cobra.Command{
	Use: "cligpt",
	Short: "CLI for GPT-3.5",
	Long: "CLI for GPT-3.5 - OpenAI's API for natural language processing",
}

func Execute() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

Then we need our entry point, edit the main.go file and add the following code.

package main

import "cligpt/cmd/command"

func main() {
	command.Execute()
}

At this point we can run our program and see the help message.

go run main.go

CLI for GPT-3.5 - OpenAI's API for natural language processing

Adding subcomands

Seeing the help message is nice but we want to add some subcommands, edit the version.go file and add the following code.

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Print the version number of CLI-GPT.",
	Long:  "Print the version number of CLI-GPT.",
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println("CLI-GPT v0.0.1")
	},
}

func init() {
	rootCmd.AddCommand(versionCmd)
}

To add a subcommand we first have to declare a variable of type cobra.Command with the Use, Short, Long and Run fields, then we can add it to the rootCmd in its init() function.

Use defines the name of the command, Short and Long are used to display the help message, Run is the function that will be executed when the command is called.

This command will print the version of our CLI, we can now run it and see the result.

go run main.go version
>> CLI-GPT v0.0.1

Adding the main command

Now that we have a working version command we can add the main command, we'll have to edit the ask.go to add call OpenAI's API, we will use a library called go-openai made by Sasha Baranov for this.

Start by installing the library with

go get github.com/sashabaranov/go-openai

Then edit the ask.go file and we'll do the same thing as we did for the version command.

var askCmd = &cobra.Command{
	Use:   "ask",
	Short: "Ask GPT-3.5 a question.",
	Long:  "Ask GPT-3.5 a question.",
	Run: func(cmd *cobra.Command, args []string) {
		client := openai.NewClient(os.Getenv("OPENAI_API_KEY"))
		messages := make([]openai.ChatCompletionMessage, 0)
		text, _ := cmd.Flags().GetString("prompt")
		text = strings.Replace(text, "\n", "", -1)
		messages = append(messages, openai.ChatCompletionMessage{
			Role:    openai.ChatMessageRoleUser,
			Content: text,
		})

		resp, err := client.CreateChatCompletion(
			context.Background(),
			openai.ChatCompletionRequest{
				Model:    openai.GPT3Dot5Turbo,
				Messages: messages,
			},
		)

		if err != nil {
			fmt.Printf("ChatCompletion error: %v\n", err)
			return
		}

		content := resp.Choices[0].Message.Content
		messages = append(messages, openai.ChatCompletionMessage{
			Role:    openai.ChatMessageRoleAssistant,
			Content: content,
		})
		fmt.Println(content)
	},
}

func init() {
	rootCmd.AddCommand(askCmd)
	askCmd.Flags().StringP("prompt", "p", "", "Prompt for GPT-3.5 to complete.")
	askCmd.MarkFlagRequired("prompt")
}

I simply copy pasted the code from the README.md example and modified it a bit to fit our needs.

Bascially what this code does is :

  1. First, we create the command variable that we'll add to the root command as we did before.

  2. This time we add a flag to the command and make it required, this flag will be used to pass the prompt to the API call.

  3. Then in the Run function, create a new client with the API key retrieved from the OPENAI_API_KEY environment variable.

  4. Create a new request with the prompt passed as a flag and the model we want to use.

  5. Send the request to the API and print the result.

Everything should be working now, we can run the command and see the result.

go run main.go ask -p "What is golang ?"

Golang is a programming language that was created by Google in 2009. 
It is a general-purpose language that can be used for a variety of tasks, including 
web development, mobile development, and data science. 
It is also used for machine learning and artificial intelligence applications.

Conclusion

When I started this project I didn't know that OpenAI's API wasn't free, I thought it was like any other API and that you could use it for free as long as you didn't use it too much so I was a bit disappointed when I found out that it wasn't the case. For this, I agree that using a paid API for this use case is a bit overkill so I'll probably try to find a free alternative later on.