👨‍💻

LLM API GroqをGoで叩いてみる

2024/10/12に公開

はじめ

こんにちは!テンプル大学CS専攻のかいとです!

個人開発でLLM APIを叩きたくなり、無料で使えるGroqのAPIをGoで叩いてる情報がなかったので本記事を書くことにしました!

ちなみにPythonやJavaScriptは既に公式からライブラリが出てるので楽にAPIを叩けますが、今回は勉強がてらにGoで叩いてみます。

https://groq.com/

APIキーの発行

https://console.groq.com/keys

ここからAPIキーを作成してください。APIキーは1回しか表示されないので注意!

.env
GROQ_API_KEY=<ここにAPIキー>

Goのセットアップ

Goが既にインストールされている前提で進めていきます。

go mod init <モジュール名>

今回はgodotenvパッケージを使うのでこちらもインストールします。

go install github.com/joho/godotenv/cmd/godotenv@latest
go mod tidy

main.goも作っておきましょう。

touch main.go

いざAPIを叩く!

今回は標準入力で好きな質問を投げかけて、そのまま返答を出力するようなものを実装していきます。

構造体

ドキュメントを見ると、Request Bodyはこんな感じだそうなので、これを元に構造体を作っていきます。

{
  "messages": [
    {
      "role": "user",
      "content": "Explain the importance of fast language models"
    }
  ],
  "model": "mixtral-8x7b-32768"
}
main.go
type Message struct {
	Role    string `json:"role"`
	Content string `json:"content"`
}

type Payload struct {
	Messages []Message `json:"messages"`
	Model    string    `json:"model"`
}

今回は返答を表示したいのでレスポンスの中のchoicesのmessageだけを取ってくることにします。

main.go
type Choice struct {
	Message Message `json:"message"`
}

type Response struct {
	Choices []Choice `json:"choices"`
}

標準入力

標準入力はbufioパッケージを使っていきます。

main.go
const URL = "https://api.groq.com/openai/v1/chat/completions"

func main() {
        // APIキーの読み込み
	err := godotenv.Load()
	if err != nil {
		log.Fatal("Error when loading .env")
	}
	key := os.Getenv("GROQ_API_KEY")

        // 標準入力
	scanner := bufio.NewScanner(os.Stdin)
	fmt.Print("Q: ")
	scanner.Scan()
	input := scanner.Text()
	if err != nil {
		log.Fatal("Failed to read your input")
	}
}

リクエスト作成

今回モデルは Llama 3 Groq 70B Tool Use (Preview) を使いますが、他にもいろいろあるので試してみてください!

参考: https://console.groq.com/docs/models

main.go
func main() {
        // 省略

	msg := Message{
		Role:    "user",
		Content: input,
	}

	payload := Payload{
		Messages: []Message{msg},
		Model:    "llama3-groq-70b-8192-tool-use-preview",
	}

	data, err := json.Marshal(payload)
	if err != nil {
		log.Fatal("Failed to marshal json")
	}

        // リクエスト作成
	req, err := http.NewRequest("POST", URL, bytes.NewBuffer(data))
	if err != nil {
		log.Fatal("Failed to create a request")
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", key))
}

レスポンスを出力

main.go
func main() {
        // 省略
	
        // クライアント作成して、リクエストを送信する
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal("Failed to send a request")
	}
	defer resp.Body.Close()

        // レスポンスを受け取る
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal("Failed to read a response")
	}

	if resp.StatusCode != http.StatusOK {
		log.Fatal("Unexpected status code")
	}

        // パース
	var res Response
	err = json.Unmarshal(body, &res)
	if err != nil {
		log.Fatal("Failed to parse a response")
	}

	fmt.Printf("A: %s", res.Choices[0].Message.Content)
}

全体のコード

main.go
package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"

	"github.com/joho/godotenv"
)

type Message struct {
	Role    string `json:"role"`
	Content string `json:"content"`
}

type Payload struct {
	Messages []Message `json:"messages"`
	Model    string    `json:"model"`
}

type Choice struct {
	Message Message `json:"message"`
}

type Response struct {
	Choices []Choice `json:"choices"`
}

const URL = "https://api.groq.com/openai/v1/chat/completions"

func main() {
	err := godotenv.Load()
	if err != nil {
		log.Fatal("Error when loading .env")
	}
	key := os.Getenv("GROQ_API_KEY")

	scanner := bufio.NewScanner(os.Stdin)
	fmt.Print("Q: ")
	scanner.Scan()
	input := scanner.Text()
	if err != nil {
		log.Fatal("Failed to read your input")
	}

	msg := Message{
		Role:    "user",
		Content: input,
	}

	payload := Payload{
		Messages: []Message{msg},
		Model:    "llama3-groq-70b-8192-tool-use-preview",
	}

	data, err := json.Marshal(payload)
	if err != nil {
		log.Fatal("Failed to marshal json")
	}

	req, err := http.NewRequest("POST", URL, bytes.NewBuffer(data))
	if err != nil {
		log.Fatal("Failed to create a request")
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", key))

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal("Failed to send a request")
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal("Failed to read a response")
	}

	if resp.StatusCode != http.StatusOK {
		log.Fatal("Unexpected status code")
	}

	var res Response
	err = json.Unmarshal(body, &res)
	if err != nil {
		log.Fatal("Failed to parse a response")
	}

	fmt.Printf("A: %s", res.Choices[0].Message.Content)
}

完成!

実際に動くか確認してみましょう!

go run main.go

実行してみて、質問を投げかけると…

Q: What is the highest mountain in Japan?
A: The highest mountain in Japan is Mount Fuji, which stands at 3,776 meters (12,388 feet) above sea level.

お、いい感じに答えてくれてますね!

しかも日本語もいけそうな感じです!

Q: 日本で一番長い川は何ですか?
A: 日本で一番長い川は信濃川です。長さは367kmです。

さいごに

今回はGoでGroq APIを叩いてみたかったのでやってみましたが、正直断然PythonかJavaScriptで書く方が楽なのでライブラリを使うのをおすすめします(笑)

Groqが提供しているライブラリはPythonとJavaScriptだけなのですが、どうやら他の開発者がC#、Dart、PHP、Ruby向けのライブラリも開発されてるみたいなので要チェックですね!
Goのライブラリも出てきたら良いなぁ…

参考: https://console.groq.com/docs/libraries

ですが、もしGoでやってみたいと思う方がいるなら参考になれば幸いです。また、もっと簡単に書く方法や修正点等ありましたら、優しく指摘してください🙏

GitHubで編集を提案

Discussion