add

AI-Driven Development
git clone git://git.lair.cx/add
Log | Files | Refs | README

commit 67d8d41fa88f4c2a222410a30f5427ace716966b
Author: Yongbin Kim <iam@yongbin.kim>
Date:   Sun, 19 Nov 2023 03:46:34 +0900

First commit

Diffstat:
A.gitignore | 24++++++++++++++++++++++++
AREADME | 44++++++++++++++++++++++++++++++++++++++++++++
Acmd/addgen/main.go | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aexamples/helloworld_test.go | 27+++++++++++++++++++++++++++
Ago.mod | 3+++
5 files changed, 236 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,24 @@ +# Allowlisting gitignore template for GO projects prevents us +# from adding various unwanted local files, such as generated +# files, developer configurations or IDE-specific files etc. +# +# Recommended: Go.AllowList.gitignore + +# Ignore everything +* + +# But not these files... +!/.gitignore + +!*.go +!go.sum +!go.mod + +!README +!LICENSE + +# !Makefile + +# ...even if they are in subdirectories +!*/ + diff --git a/README b/README @@ -0,0 +1,44 @@ +# ADD; AI-Driven Development + +Because I am lazy. + +This program uses Huggingface Inference API. It's basically free-to-use, but I recommend pro plan, because it's much faster. + + +# Getting started + +$ go install go.lair.cx/add/cmd/addgen@latest +$ addgen --init # Generate sample configuration + +$ vi greetings.yaml + + +ADD uses yaml to "define" the program. + +- name: Make Greetings + type: Function + input: + - name: String + - entered: + output: + - message: String + behavior: > + This method creates a greetings message. + If the entered variable is true, the message format is: "Hi, ${name}!" + If the entered variable is false, the message format is: "Bye, ${name}!" + + +Use `addgen [yaml]` to generate code. + +$ addgen greetings.yaml +```go +// MakeGreeting generates a personalized greeting message based on the given name and whether the person has entered the room. +func MakeGreeting(name string, entered bool) string { + if entered { + return fmt.Sprintf("Hi, %s!", name) + } else { + return fmt.Sprintf("Bye, %s!", name) + } +} +``` + diff --git a/cmd/addgen/main.go b/cmd/addgen/main.go @@ -0,0 +1,138 @@ +package main + +import ( + "bytes" + "encoding/json" + "errors" + "flag" + "fmt" + "log" + "net/http" + "os" + "strings" +) + +// Config represents the configuration for the program. +type Config struct { + Lang string `json:"lang"` + Endpoint string `json:"endpoint"` + Token string `json:"token"` + Parameters map[string]any `json:"parameters"` + Options map[string]any `json:"options"` +} + +func main() { + flag.Parse() + + args := flag.Args() + if len(args) < 1 { + log.Fatalln("Usage: addgen [source.yaml]") + } + + // Read the configuration file + data, err := os.ReadFile("add.json") + if err != nil { + log.Fatal(err) + } + + var config Config + err = json.Unmarshal(data, &config) + if err != nil { + log.Fatal(err) + } + + // Verify the configuration + if len(config.Lang) == 0 || len(config.Endpoint) == 0 || len(config.Token) == 0 { + log.Fatalf("Invalid configuration: lang=%q, endpoint=%q, token=%q\n", config.Lang, config.Endpoint, config.Token) + } + + // Read the prompt file + prompt, err := os.ReadFile("prompt.txt") + if err != nil { + log.Fatal(err) + } + + // Read the yaml file + inputData, err := os.ReadFile(args[0]) + if err != nil { + log.Fatal(err) + } + + input := appendToPrompt( + // Replace placeholders in the prompt + strings.ReplaceAll(string(prompt), "{{ content }}", string(inputData)), + + // Append the language identifier to the prompt + fmt.Sprintf("```%s\n", config.Lang), + ) + + // Query the endpoint repeatedly until there is no more results + for { + result, err := query(config, input) + if err != nil { + log.Fatal(err) + } + if len(result) == 0 { + break + } + + input = appendToPrompt(input, result) + } +} + +func appendToPrompt(prompt string, s string) string { + fmt.Print(s) + return prompt + s +} + +func query(config Config, prompt string) (string, error) { + // Create the HTTP client + client := http.Client{} + + var queryParams = map[string]any{ + "inputs": prompt, + "parameters": config.Parameters, + "options": config.Options, + } + + body, err := json.Marshal(queryParams) + if err != nil { + return "", nil + } + + // Set up the request + req, err := http.NewRequest("POST", config.Endpoint, bytes.NewBuffer(body)) + if err != nil { + return "", err + } + + // Add the authorization header + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", config.Token)) + req.Header.Set("Content-Type", "application/json") + + // Make the request + resp, err := client.Do(req) + if err != nil { + return "", err + } + + defer resp.Body.Close() + + // Check the status code + if resp.StatusCode != http.StatusOK { + return "", errors.New(fmt.Sprintf("Unexpected status code: %d", resp.StatusCode)) + } + + // Decode the response + var response []struct { + GeneratedText string `json:"generated_text"` + } + + err = json.NewDecoder(resp.Body).Decode(&response) + if err != nil { + return "", err + } + + // Return the generated text + return response[0].GeneratedText, nil +} diff --git a/examples/helloworld_test.go b/examples/helloworld_test.go @@ -0,0 +1,27 @@ +package main + +import ( + "fmt" + "log" + "os" +) + +func main() { + if len(os.Args) < 2 { + log.Fatalf("Usage: %s (name)", os.Args[0]) + } + + fmt.Println(MakeGreeting(os.Args[1], true)) + fmt.Println(MakeGreeting(os.Args[1], false)) +} + +// Below is generated code + +// MakeGreeting generates a personalized greeting message based on the given name and whether the person has entered the room. +func MakeGreeting(name string, entered bool) string { + if entered { + return fmt.Sprintf("Hi, %s!", name) + } else { + return fmt.Sprintf("Bye, %s!", name) + } +} diff --git a/go.mod b/go.mod @@ -0,0 +1,3 @@ +module go.lair.cx/add + +go 1.21.3