Skip to content

Commit 34460f8

Browse files
committed
feat: add gpt feature
1 parent 41dfd68 commit 34460f8

File tree

8 files changed

+169
-6
lines changed

8 files changed

+169
-6
lines changed

cmd/gpt/main.go

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package gpt
2+
3+
import (
4+
"github.com/zcong1993/leetcode-tool/internal/gpt"
5+
"github.com/zcong1993/leetcode-tool/pkg/leetcode"
6+
"log"
7+
)
8+
9+
func Run(lc *leetcode.Leetcode, number string) {
10+
if lc.Config.Gpt == nil || lc.Config.Gpt.ApiKey == "" || lc.Config.Gpt.Model == "" {
11+
log.Fatal("please config gpt api key and model in .leetcode.json")
12+
}
13+
14+
client := gpt.NewOpenai(lc.Config.Gpt.ApiKey, lc.Config.Gpt.Model)
15+
_, err := client.Hint(lc, number)
16+
if err != nil {
17+
log.Fatal(err)
18+
}
19+
}

cmd/main.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ package main
22

33
import (
44
"fmt"
5+
"github.com/zcong1993/leetcode-tool/cmd/gpt"
6+
"github.com/zcong1993/leetcode-tool/internal/config"
57
"log"
68
"os"
79

8-
"github.com/zcong1993/leetcode-tool/internal/config"
9-
1010
"github.com/zcong1993/leetcode-tool/cmd/new"
1111
"github.com/zcong1993/leetcode-tool/cmd/tags"
1212
"github.com/zcong1993/leetcode-tool/cmd/update"
@@ -36,6 +36,9 @@ var (
3636

3737
tagsCmd = app.Command("tags", "Update tag toc files.")
3838
tagsForce = tagsCmd.Flag("force", "force update file").Short('f').Bool()
39+
40+
gptCmd = app.Command("gpt", "Use gpt to solve problem.")
41+
gptNumber = gptCmd.Arg("number", "problem number").Required().String()
3942
)
4043

4144
func showMeta(lc *leetcode.Leetcode, number string) {
@@ -66,6 +69,8 @@ func main() {
6669
showMeta(lc, *metaNumber)
6770
case tagsCmd.FullCommand():
6871
tags.Run(lc, *tagsForce)
72+
case gptCmd.FullCommand():
73+
gpt.Run(lc, *gptNumber)
6974
}
7075
}
7176

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect
88
github.com/bmatcuk/doublestar/v2 v2.0.3
99
github.com/dghubble/sling v1.4.2 // indirect
10+
github.com/sashabaranov/go-openai v1.22.0 // indirect
1011
github.com/tidwall/gjson v1.6.3
1112
gopkg.in/alecthomas/kingpin.v2 v2.2.6
1213
)

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD
1313
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
1414
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1515
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
16+
github.com/sashabaranov/go-openai v1.22.0 h1:bjYkELQCbOBMW9B7zi/KA5L4syPfn/3qRvUoyV49Fvs=
17+
github.com/sashabaranov/go-openai v1.22.0/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
1618
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
1719
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
1820
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=

internal/config/config.go

+6
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,15 @@ import (
55
"io/ioutil"
66
)
77

8+
type Gpt struct {
9+
ApiKey string `json:"api_key"` // eg. sk-xxxxxxxxxx
10+
Model string `json:"model"` // eg. gpt-3.5-turbo
11+
}
12+
813
type Config struct {
914
Lang string `json:"lang"`
1015
Env string `json:"env"` // eg. en, cn
16+
Gpt *Gpt `json:"gpt"`
1117
}
1218

1319
const configPath = ".leetcode.json"

internal/gpt/openai.go

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package gpt
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"errors"
7+
"fmt"
8+
"github.com/sashabaranov/go-openai"
9+
"github.com/zcong1993/leetcode-tool/pkg/leetcode"
10+
"io"
11+
"log"
12+
"text/template"
13+
)
14+
15+
type Openai struct {
16+
model string
17+
client *openai.Client
18+
}
19+
20+
func NewOpenai(apiKey, model string) *Openai {
21+
client := openai.NewClient(apiKey)
22+
return &Openai{
23+
client: client,
24+
model: model,
25+
}
26+
}
27+
28+
func (o *Openai) Chat(content string) (string, error) {
29+
stream, err := o.client.CreateChatCompletionStream(
30+
context.Background(),
31+
openai.ChatCompletionRequest{
32+
Model: o.model,
33+
Messages: []openai.ChatCompletionMessage{
34+
{
35+
Role: openai.ChatMessageRoleUser,
36+
Content: content,
37+
},
38+
},
39+
Stream: true,
40+
},
41+
)
42+
43+
if err != nil {
44+
return "", fmt.Errorf("ChatCompletion error: %v", err)
45+
}
46+
47+
defer stream.Close()
48+
49+
ans := ""
50+
for {
51+
response, err := stream.Recv()
52+
if errors.Is(err, io.EOF) {
53+
break
54+
}
55+
56+
if err != nil {
57+
return "", fmt.Errorf("Stream error: %v", err)
58+
}
59+
60+
words := response.Choices[0].Delta.Content
61+
ans += words
62+
63+
fmt.Printf(words)
64+
}
65+
66+
return ans, nil
67+
}
68+
69+
func (o *Openai) Hint(lc *leetcode.Leetcode, number string) (string, error) {
70+
meta, err := lc.GetMetaByNumber(number)
71+
if err != nil {
72+
return "", err
73+
}
74+
75+
textLang := "中文"
76+
if lc.Config.Lang == "en" {
77+
textLang = "English"
78+
}
79+
80+
var content bytes.Buffer
81+
err = hitTpl.Execute(&content, &HintData{
82+
Lang: lc.Config.Lang,
83+
TextLang: textLang,
84+
Problem: meta.Content,
85+
})
86+
if err != nil {
87+
log.Fatal(err)
88+
}
89+
return o.Chat(content.String())
90+
}
91+
92+
type HintData struct {
93+
Lang string
94+
TextLang string
95+
Problem string
96+
}
97+
98+
var hitTpl = template.Must(template.New("hint").Parse(hitStr))
99+
100+
var hitStr = `
101+
您是一个算法专家,请基于下面的算法题目,给出该算法的思路和复杂度, 使用 {{ .TextLang }} 回答
102+
SETP1. 给出算法的归类,如递归,栈
103+
SETP2. 若是存在暴力解法,给出思路和复杂度
104+
SETP3. 给出最优解法和复杂度
105+
SETP4. 代码实现,使用 {{ .Lang }} 语言,代码带注释和测试样例。
106+
107+
{{ .Problem }}
108+
`

internal/gpt/openai_test.go

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package gpt
2+
3+
import (
4+
"github.com/stretchr/testify/assert"
5+
"testing"
6+
)
7+
8+
func TestOpenai(t *testing.T) {
9+
o := NewOpenai("sk-xxxxxx", "gpt-3.5-turbo")
10+
11+
t.Run("chat", func(t *testing.T) {
12+
ret, err := o.Chat("写一个冒泡排序")
13+
assert.NoError(t, err)
14+
15+
t.Log(ret)
16+
})
17+
}

pkg/leetcode/leetcode.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
"errors"
66
"fmt"
7+
"github.com/sashabaranov/go-openai"
78
"github.com/zcong1993/leetcode-tool/internal/config"
89
"io"
910
"io/ioutil"
@@ -43,12 +44,14 @@ var (
4344
)
4445

4546
type Leetcode struct {
46-
Config *config.Config
47-
Problems []byte
47+
Config *config.Config
48+
GptClient *openai.Client
49+
Problems []byte
4850
}
4951

5052
func NewLeetcode(config *config.Config) *Leetcode {
51-
return &Leetcode{Config: config}
53+
client := openai.NewClient("your token")
54+
return &Leetcode{Config: config, GptClient: client}
5255
}
5356

5457
func DownloadFile(remoteFile string) error {
@@ -108,10 +111,12 @@ func (l *Leetcode) getDetail(number string) (*Meta, error) {
108111
title := "title"
109112
difficulty := problem.Get("difficulty").String()
110113
content := "content.en"
114+
host := "https://leetcode.com"
111115
if l.Config.Env == "cn" {
112116
title = "titleCn"
113117
content = "content.cn"
114118
difficulty = difficultyMap[strings.ToLower(difficulty)]
119+
host = "https://leetcode.cn"
115120
}
116121
title = problem.Get(title).String()
117122
content = problem.Get(content).String()
@@ -121,7 +126,7 @@ func (l *Leetcode) getDetail(number string) (*Meta, error) {
121126
Title: title,
122127
Difficulty: difficulty,
123128
Tags: tags,
124-
Link: fmt.Sprintf("https://leetcode.cn/problems/%s/description/", problem.Get("titleSlug").String()),
129+
Link: fmt.Sprintf("%s/problems/%s/description/", host, problem.Get("titleSlug").String()),
125130
Content: content,
126131
}, nil
127132
}

0 commit comments

Comments
 (0)