Skip to content

Instantly share code, notes, and snippets.

@zakkor
Created April 2, 2023 22:01
Show Gist options
  • Save zakkor/6aa1a5d1c747cb59331807c9e1a03c8f to your computer and use it in GitHub Desktop.
Save zakkor/6aa1a5d1c747cb59331807c9e1a03c8f to your computer and use it in GitHub Desktop.
replgpt
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"strings"
)
var task string
func init() {
flag.StringVar(&task, "task", "", "get task to complete")
}
func main() {
flag.Parse()
conversation := map[string]interface{}{
"model": "gpt-3.5-turbo",
"temperature": 0.2,
"messages": []map[string]interface{}{
map[string]interface{}{
"role": "system",
"content": `You are acting today as an automated programming assistant. Your purpose is to create and debug programs by yourself from start to finish, with minimal input required from the user. As a programming assistant, you are not able to communicate using words, and instead can only type COMMANDS, as defined below. You only post exactly the the output of each command based on the syntax and format of each command, making sure to follow the command syntax perfectly. In order to reach your goal of creating a correct program, you are able to ask the user questions, as well as run arbitrary commands to run and verify the program.
---
USER COMMANDS: The possible commands you can receive from the user are:
1) Task - (command is sent only once, at the start of the session, and determines what goal we are trying to achieve)
Command syntax:
Task: <our goal for today's session>
Location: <the location of the file we are going to be working with, which should be taken into account when issuing Run commands>
2) RunOutput - (user will send this command after you ask the user to run a terminal command. You should use the output of this command to determine if the program is correct, or if there are further changes to the code we should make.)
Command syntax:
RunOutput: <terminal output after running terminal command>
---
ASSISTANT COMMANDS: The possible commands you may send to the user in order to receive further information are:
1) Code - (ask the user to replace the current version of the code meant to solve the task at hand with the following piece of code.)
Command syntax:
Code: <program code the user should insert into their filesystem>
2) Run - (ask the user to run a terminal command in order to verify program correctness. DO NOT UNDER ANY CIRCUMSTANCES INCLUDE ANY ADDITIONAL NOTES OR COMMENTS OTHER THAN THE EXACT COMMAND TO RUN)
Command syntax:
Run: <a CLI command the user should run in order to verify the program is correct> Example: "Run: go build && ./main". DO NOT INCLUDE ANY NOTES, OR ADDITIONAL COMMENTS.
3) Done - (after the user has provided a RunOutput command, and you have confirmed the program is correct, signal to the user that our goal has been reached and there are no further steps to take)
Command syntax:
Done: <a message explaining to the user how you determined the program is correct>
---
IMPORTANT: Your first command, before sending any other commands, must a "Code" command.
Every time the user types the word "next", you should pick an appropriate ASSISTANT COMMAND, and send it to the user in order to get closer towards completing our goal.
DO NOT INCLUDE ANY ADDITIONAL INSTRUCTIONS LIKE "Note:" IN YOUR RESPONSES.
DO NOT UNDER ANY CIRCUMSTANCES INCLUDE ANYTHING OTHER THAN THE EXACT COMMAND SYNTAX DEFINED ABOVE IN YOUR RESPONSES TO THE USER.`,
},
map[string]interface{}{
"role": "user",
"content": "Task: Write a Go program that gets today's date in a format like 2015 Jan 15\nLocation: ./repl/main.go",
},
map[string]interface{}{
"role": "assistant",
"content": "Code: \n```\npackage main \n\nimport (\n \"fmt\"\n \"time\"\n)\n\nfunc main() {\n today := time.Now()\n fmt.Println(today.Format(\"2006 Jan 2\"))\n}\n```",
},
map[string]interface{}{
"role": "assistant",
"content": "Run: go run ./repl/main.go",
},
map[string]interface{}{
"role": "user",
"content": "RunOutput: 2023 Apr 1",
},
map[string]interface{}{
"role": "assistant",
"content": "Done: Program output '2023 Apr 1' matches the task goal of getting today's date in a format like '2015 Jan 15'.",
},
map[string]interface{}{
"role": "user",
"content": "Task: " + task + "\nLocation: ./repl/main.go",
},
},
}
conv, done := reply(conversation)
for !done {
conv, done = reply(conv)
}
}
func reply(conversation map[string]any) (map[string]any, bool) {
bodyb, err := json.Marshal(conversation)
check(err)
body := bytes.NewReader(bodyb)
req, err := http.NewRequest("POST", "https://api.openai.com/v1/chat/completions", body)
check(err)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer <API KEY HERE>")
client := &http.Client{}
resp, err := client.Do(req)
check(err)
defer resp.Body.Close()
// Read from resp.Body:
data, err := io.ReadAll(resp.Body)
check(err)
var root Root
err = json.Unmarshal(data, &root)
check(err)
var done bool
for _, choice := range root.Choices {
conversation["messages"] = append(conversation["messages"].([]map[string]interface{}), map[string]interface{}{
"role": choice.Message.Role,
"content": choice.Message.Content,
})
cmd, param, found := strings.Cut(choice.Message.Content, ": ")
if !found {
cmd, param, found = strings.Cut(choice.Message.Content, ":\n")
if !found {
panic(choice.Message.Content)
}
}
switch cmd {
case "Code":
fmt.Println("New code:", param)
fmt.Println()
code := strings.TrimPrefix(param, "```")
code = strings.TrimSuffix(code, "```")
code = strings.TrimSpace(code)
err := os.WriteFile("./repl/main.go", []byte(code), 0644)
check(err)
conversation["messages"] = append(conversation["messages"].([]map[string]interface{}), map[string]interface{}{
"role": "user",
"content": "next",
})
case "Run":
run := strings.TrimSpace(param)
before, _, found := strings.Cut(run, "\n")
if found {
run = before
}
fmt.Println("Running command:", run)
fmt.Println()
cmd := exec.Command("bash", "-c", run)
wd, err := os.Getwd()
check(err)
cmd.Dir = wd
out, _ := cmd.CombinedOutput()
fmt.Println("Replying with 'RunOutput:", string(out), "'")
fmt.Println()
conversation["messages"] = append(conversation["messages"].([]map[string]interface{}), map[string]interface{}{
"role": "user",
"content": "RunOutput: " + string(out),
})
case "Done":
fmt.Println("Done:", param)
done = true
}
}
return conversation, done
}
func check(err error) {
if err != nil {
panic(err)
}
}
type Root struct {
Id string `json:"id"`
Object string `json:"object"`
Created float64 `json:"created"`
Model string `json:"model"`
Usage Usage `json:"usage"`
Choices []Choices `json:"choices"`
}
type Usage struct {
Completion_tokens float64 `json:"completion_tokens"`
Total_tokens float64 `json:"total_tokens"`
Prompt_tokens float64 `json:"prompt_tokens"`
}
type Message struct {
Role string `json:"role"`
Content string `json:"content"`
}
type Choices struct {
Message Message `json:"message"`
Finish_reason string `json:"finish_reason"`
Index float64 `json:"index"`
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment