-
Notifications
You must be signed in to change notification settings - Fork 0
/
boilerplaite.go
123 lines (104 loc) · 3.05 KB
/
boilerplaite.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package boilerplaite
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"time"
"github.com/rakyll/openai-go"
"github.com/rakyll/openai-go/chat"
yaml "gopkg.in/yaml.v3"
)
var ErrOutputExists = errors.New("Output directory already exists")
const promptTemplate = `You are an AI system called "boilerplaite", your objective is to get me faster in writing code in any programming language, this means generating boilerplate codes. Your answers must be in the format of yaml/yml and it MUST be valid. Don't add any notes. If a file is empty you may skip it. You will provide the code and the filepath where that code should live, so for a task like this "sample package.json file for a nodejs project" you should return something like this:
- filepath: ./package.json
code: |
{
"name": "sample",
"version": "1.0.0",
"main": "index.js",
"license": "MIT"
}
Another example would be: "2 files as txt with the characters a and b in each file"
- filepath: ./a.txt
code: |
a
- filepath: ./b.txt
code: |
b
This is your task: %s`
type entry struct {
Filepath string `yaml:"filepath"`
Code string `yaml:"code"`
}
type Boilerplaite struct {
chatgpt *chat.Client
}
func New(openaiKey, openaiModel string) *Boilerplaite {
sess := openai.NewSession(openaiKey)
sess.HTTPClient.Timeout = 5 * time.Minute
chatgpt := chat.NewClient(sess, openaiModel)
return &Boilerplaite{chatgpt}
}
func (b *Boilerplaite) Complete(ctx context.Context, data string) (string, int, error) {
msgs := []*chat.Message{{Role: "user", Content: fmt.Sprintf(promptTemplate, data)}}
params := &chat.CreateCompletionParams{Messages: msgs}
resp, err := b.chatgpt.CreateCompletion(ctx, params)
if err != nil {
return "", 0, err
}
replymsg := resp.Choices[0].Message
content := replymsg.Content
return content, resp.Usage.TotalTokens, nil
}
func (b *Boilerplaite) prepare(ctx context.Context, outdir string) error {
if _, err := os.Stat(outdir); err == os.ErrExist {
return ErrOutputExists
}
return os.MkdirAll(outdir, 0750)
}
func (b *Boilerplaite) WritePrompt(ctx context.Context, outdir string, data string) error {
if err := b.prepare(ctx, outdir); err != nil {
return err
}
filepath := filepath.Join(outdir, ".boilerplaite.prompt")
f, err := os.Open(filepath)
if err != nil {
return err
}
if _, err = f.Write([]byte(data)); err != nil {
return err
}
return f.Close()
}
func (b *Boilerplaite) WriteFiles(ctx context.Context, outdir string, data string) error {
if err := b.prepare(ctx, outdir); err != nil {
return err
}
var out []entry
if err := yaml.Unmarshal([]byte(data), &out); err != nil {
return err
}
for _, entry := range out {
file := filepath.Join(outdir, entry.Filepath)
dir := filepath.Dir(file)
if err := os.MkdirAll(dir, 0750); err != nil {
return err
}
f, err := os.Create(file)
if err != nil {
if err := os.RemoveAll(outdir); err != nil {
return err
}
return err
}
if _, err = f.WriteString(entry.Code); err != nil {
if err := os.Remove(file); err != nil {
return err
}
return err
}
}
return nil
}