【Gemini×React】Gemini Pro APIを叩いてレスポンスを画面上に表示させてみた

# はじめに
12月発表されたGeminiが発表されました。
Gemini APIを叩いて返ってくるレスポンスのテキストを画面上に表示するところまで、
Reactで作成しようと思います。
何かの参考になれば幸いです。

# 本記事のゴール
テキストボックスに値に入力されたテキストを基にGemini APIを叩いて、
返ってきたレスポンスを画面上に表示する。
イメージは次の通りです。

空白文字等のバリデーションチェックまではしておりません。

# 作るもの（デモ）
![sample_qit_gm.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1247619/3bb3545f-c25a-f9ba-d420-6132b5737a17.gif)

# Gemini（ジェミニ）とは？
2023年12月に発表されたマルチモーダルなAIモデルです。
2024年1/27では、Gemini Nano、Pro、Ultraの3種類があります。
詳細は下記Geminiの[リンク](https://deepmind.google/technologies/gemini/#introduction)に記載されていますが、いくつかのベンチマーク項目でGPT-4より高いパフォーマンスが出てるみたいです。

https://deepmind.google/technologies/gemini/#introduction

https://japan.googleblog.com/2023/12/gemini.html

# 環境
- Vite: 5.0.8
- Raect：18.2
- typescript：5.2.2
- tailwindcss：3.4.1

# APIキーの取得

Gemini proをしようするため、以下リンク内の
`Get API key in Google AI Studio`からAPI keyキーを生成しておきます。

https://ai.google.dev/

:::note warn
APIキーについて
生成したAPIキーは無くさないように保管してください。
:::

# Viteプロジェクトの作成
以下のコマンドでViteプロジェクトを作成します。
```
npm create vite@latest
```
https://ja.vitejs.dev/guide/

# ソース（APIの呼び出し部分）

### ◆SDKのインストール

```
npm install @google/generative-ai
```

https://github.com/google/generative-ai-js

https://www.npmjs.com/package/@google/generative-ai

### ◆Geminiモデルを使用してテキストを生成

次にGeminiAPiを呼び出すメソッド（今回は`startGemini`関数）を作成していきます。

```typescript:gemini.ts
import { GoogleGenerativeAI } from "@google/generative-ai";

// 環境変数の読み込み
const API_KEY: string =  import.meta.env.VITE_GEMINI_KEY;

// インスタンスの作成
const genAI = new GoogleGenerativeAI(API_KEY);

// Geminiモデルを使用してテキストを生成するための関数
export async function startGemini(prompt:string) {
  // gemini-pro モデルを使用
  const model = genAI.getGenerativeModel({ model: "gemini-pro"});

  // プロンプトに基づいてテキストを生成
  const result = await model.generateContent(prompt);

  // 生成されたテキストを取得
  const response = await result.response;

  // テキストを抽出
  const text = response.text();

  return text;
}
```
:::note warn
環境変数の設定について
APIキーは外部に公開したくないので、
`.env`ファイルを作成し環境変数としておきます。
:::

# ソース（フロント部分）

### ◆Material UIのインストール
Material UIを用いてテキストボックスとボタンを作成していきます。
次のコマンドでインストールします。

```
npm install @mui/icons-material @mui/material @emotion/styled @emotion/react
```

https://mui.com/material-ui/material-icons/

### ◆Tailwind CSSのインストール
Tailwind CSSのVite用ドキュメントを見ながら、インストールします。

https://tailwindcss.com/docs/guides/vite


### ◆テキスト生成の生成を実行
状態変数`inputText`でテキストボックスに入力された値を保持します。
一方で、状態変数`outputText`はGemini APIからの返っきたレスポンスのテキストを保持するようにします。

`callGemini`ではGemini APIを使用してテキストを生成する`startGemini`関数を呼び出して、
そのレスポンスをoutputTextに保存しています。

```tsx:App.tsx
import { useState } from 'react';

// 外部関数のインポート
import { startGemini } from './utils/genmi';

const [inputText, setInputText] = useState('');
const [outputText, setOutputText] = useState('応答結果を出力');

// Gemini APIを使用してテキストを生成
const callGemini = async () => {
    const response = await startGemini(inputText);
    setOutputText(response)
};
```

### ◆テキストを入力する部分の作成
Material UIのコンポーネントを使用してUIを構築しています。
テキストボックスの変更はイベントハンドラー`onChange`で、
入力された際に状態を更新しています。

```tsx:App.tsx
import TextField from '@mui/material/TextField';
import IconButton from '@mui/material/IconButton';
import SendIcon from '@mui/icons-material/Send';

<div className='w-1/2 mt-20 flex flex-row justify-center'>
    {/* テキスト入力部分 */}
    <TextField 
        fullWidth  
        label="input message" 
        variant="filled" 
        color="primary"
        onChange={(e) => setInputText(e.target.value)}/>
    {/* 送信ボタン */}
    <div className='bg-blue-400'>
        <IconButton 
          color="primary" 
          aria-label="add to shopping cart" 
          size="large"
          onClick={callGemini}>
            <SendIcon fontSize="inherit" className='text-white'/>
        </IconButton>
    </div>
</div>
```

### ◆フロント部分全体のコード

送信ボタンがクリックされた際に、`callGemini`を実行し、
状態変数`outputText`に生成されたテキストを保存し、
画面上に表示しています。

```tsx:App.tsx
import { useState } from 'react';

// 外部関数のインポート
import { startGemini } from './utils/genmi';

//コンポーネントのインポート
import TextField from '@mui/material/TextField';
import IconButton from '@mui/material/IconButton';
import SendIcon from '@mui/icons-material/Send';

const App = () => {
  const [inputText, setInputText] = useState('');
  const [outputText, setOutputText] = useState('応答結果を出力');

  const callGemini = async () => {
    const response = await startGemini(inputText);
    setOutputText(response)};

  return (
    <main className='w-full h-screen flex flex-col justify-center items-center bg-sky-100'>
      <h1 className='text-gray-700 text-5xl font-bold'>Sample</h1>
      <div className='flex justify-center items-center font-bold text-3xl 
       w-3/4 h-1/3 mt-20 bg-white rounded-lg shadow-lg'>
        <h2>{outputText}</h2>
      </div>
      <div className='w-1/2 mt-20 flex flex-row justify-center'>
         <TextField 
          fullWidth  
          label="input message" 
          variant="filled" 
          color="primary"
          onChange={(e) => setInputText(e.target.value)}/>
         <div className='bg-blue-400'>
            <IconButton 
              color="primary" 
              aria-label="add to shopping cart" 
              size="large"
              onClick={callGemini}>
                <SendIcon fontSize="inherit" className='text-white'/>
            </IconButton>
         </div>
      </div>
    </main>
  )
}

export default App
```

# 動かしてみる
次のコマンドで起動し動かしてみます。
```
npm run dev
```

![sample_qit_gm.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1247619/3bb3545f-c25a-f9ba-d420-6132b5737a17.gif)

# 終わりに
GeminiはマルチモーダルなAIモデルなので、テキスト以外にも動画や画像、音声等も扱えます。
今回はGemini APIを使用しテキストのみ試してみましたが、多彩な使い方ができそうです。

# 参考
- https://ai.google.dev/tutorials/web_quickstart?hl=ja#generate-text-from-text-input
- https://deepmind.google/technologies/gemini/#introduction
- https://japan.googleblog.com/2023/12/gemini.html
- https://ja.vitejs.dev/guide/
- https://github.com/google/generative-ai-js
- https://www.npmjs.com/package/@google/generative-ai
- https://mui.com/material-ui/material-icons/
- https://tailwindcss.com/docs/guides/vite