Skip to content

Commit 2bfc79f

Browse files
committed
add_openai
1 parent bd78e2e commit 2bfc79f

File tree

6 files changed

+177
-1
lines changed

6 files changed

+177
-1
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
module golangchain
22

33
go 1.22.4
4+
5+
require github.com/jarcoal/httpmock v1.3.1 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
2+
github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=

pkg/main.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
11
package main
22

3+
import (
4+
"fmt"
5+
"golangchain/pkg/openai"
6+
)
7+
38
func main() {
4-
println("aaa")
9+
llm, err := openai.NewChatOpenAI("gpt-3.5-turbo")
10+
if err != nil {
11+
fmt.Println(err)
12+
}
13+
14+
msg := []openai.Message{
15+
{Role: "system", Content: ""},
16+
{Role: "user", Content: "こんにちは"},
17+
}
18+
19+
response, err := llm.SendMessage(msg)
20+
if err != nil {
21+
fmt.Println(err)
22+
}
23+
fmt.Println("Response:", response.Choices[0].Message.Content)
24+
525
}

pkg/openai/openai.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package openai
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
"os"
10+
)
11+
12+
const openaiURL = "https://api.openai.com/v1/chat/completions"
13+
14+
type ChatOpenAI struct {
15+
apiKey string
16+
model string
17+
httpClient *http.Client
18+
}
19+
20+
func NewChatOpenAI(model string) (*ChatOpenAI, error) {
21+
apiKey := os.Getenv("OPENAI_API_KEY")
22+
if apiKey == "" {
23+
return nil, fmt.Errorf("OPENAI_API_KEY environment variable is required")
24+
}
25+
return &ChatOpenAI{
26+
apiKey,
27+
model,
28+
&http.Client{},
29+
}, nil
30+
}
31+
32+
func (c *ChatOpenAI) SendMessage(message []Message) (*Response, error) {
33+
34+
requestBody := Request{
35+
Model: c.model,
36+
Messages: message,
37+
}
38+
39+
requestBodyJSON, err := json.Marshal(requestBody)
40+
if err != nil {
41+
return nil, fmt.Errorf("Error marshaling request body: %w", err)
42+
}
43+
44+
// Create a new HTTP request
45+
req, err := http.NewRequest("POST", openaiURL, bytes.NewBuffer(requestBodyJSON))
46+
if err != nil {
47+
return nil, fmt.Errorf("Error creating HTTP request: %w", err)
48+
}
49+
50+
// Set the necessary headers
51+
req.Header.Set("Content-Type", "application/json")
52+
req.Header.Set("Authorization", "Bearer "+c.apiKey)
53+
54+
resp, err := c.httpClient.Do(req)
55+
if err != nil {
56+
return nil, fmt.Errorf("Error sending HTTP request: %w", err)
57+
}
58+
defer resp.Body.Close()
59+
60+
// Read the response body
61+
respBody, err := io.ReadAll(resp.Body)
62+
63+
// Unmarshal the response body to the Response struct
64+
var response Response
65+
err = json.Unmarshal(respBody, &response)
66+
if err != nil {
67+
return nil, fmt.Errorf("Error unmarshaling response body: %w", err)
68+
}
69+
return &response, nil
70+
}

pkg/openai/openai_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package openai
2+
3+
import (
4+
"net/http"
5+
"reflect"
6+
"testing"
7+
8+
"github.com/jarcoal/httpmock"
9+
)
10+
11+
func TestSendMessage(t *testing.T) {
12+
tests := []struct {
13+
name string
14+
message []Message
15+
expected *Response
16+
}{
17+
{
18+
name: "normal condition",
19+
message: []Message{
20+
{Role: "system", Content: "mock test server"},
21+
{Role: "user", Content: "mock test question"},
22+
},
23+
expected: &Response{
24+
Choices: []Choice{
25+
{Message: Message{Role: "assistant", Content: "This is the test response"}},
26+
},
27+
},
28+
},
29+
}
30+
// mock
31+
httpmock.Activate()
32+
defer httpmock.DeactivateAndReset()
33+
httpmock.RegisterResponder("POST", openaiURL,
34+
func(req *http.Request) (*http.Response, error) {
35+
36+
response := Response{Choices: []Choice{
37+
{Message: Message{Role: "assistant", Content: "This is the test response"}},
38+
},
39+
}
40+
resp, err := httpmock.NewJsonResponse(200, response)
41+
if err != nil {
42+
return httpmock.NewStringResponse(500, ""), nil
43+
}
44+
return resp, nil
45+
})
46+
47+
client, _ := NewChatOpenAI("gpt-3.5-turbo")
48+
for _, tc := range tests {
49+
t.Run(tc.name, func(t *testing.T) {
50+
have, err := client.SendMessage(tc.message)
51+
if err != nil {
52+
t.Fatalf("Error happens: %v", err)
53+
}
54+
if !reflect.DeepEqual(have, tc.expected) {
55+
t.Fatalf("unexpected entries: %v != %v", have, tc.expected)
56+
}
57+
})
58+
}
59+
}

pkg/openai/struct.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package openai
2+
3+
// message represents a single message in the chat
4+
type Message struct {
5+
Role string `json:"role"`
6+
Content string `json:"content"`
7+
}
8+
9+
// Request represents the OpenAI API request payload
10+
type Request struct {
11+
Model string `json:"model"`
12+
Messages []Message `json:"messages"`
13+
}
14+
15+
// Response represents the OpenAI API response
16+
type Response struct {
17+
Choices []Choice `json:"choices"`
18+
}
19+
20+
// Choice represents a single choice in the OpenAI API response
21+
type Choice struct {
22+
Message Message `json:"message"`
23+
}

0 commit comments

Comments
 (0)