GridDB、Node.js、Reactを使ってOpenAI APIトークン使用状況のダッシュボードを構築する

## はじめに

組織はアプリケーションを強化するためにOpenAI APIのような高度なAPIに依存しています。これらのAPIはAIや機械学習モデルとの対話を可能にします。しかし、API呼び出しのコストを決定するAPIトークンの管理は困難です。過剰使用は高コストにつながり、一方で過小使用ではリソースを無駄にしてしまいます。

この課題に対処するため、トークン使用状況を可視化するダッシュボードを提案します。データ管理にはGridDB、バックエンド開発にはNode.js、フロントエンドUIにはReactを活用します。このドキュメントは、問題を理解し、解決策を構想し、選択した技術で効果的に実装するためのガイドを提供することを目的としています。

## このブログの目的

このドキュメントの主な目的は、GridDB、Node.js、React を使用して OpenAI API トークンの使用状況ダッシュボードを構築するための徹底的かつ体系的なガイドを提供することです。

## セットアップと実行

[GitHub][1] リポジトリからソースコードをクローンします。

```bash
git clone https://github.com/junwatu/openai-tokens-dashboard.git
```

> GridDB と Node.js LTS 18 がインストールされていることを確認してください。 インストールされていない場合は、このブログ記事の [**インストール**][2] セクションを参照してください。

ディレクトリを `openai-tokens-dashboard` フォルダに変更し、`.env` ファイルを作成または編集します。OpenAI API にアクセスできるはずです。[キーを作成][3]し、作成した OpenAI API キーを `OPENAI_API_KEY` 環境変数に設定します。

```bash
OPENAI_API_KEY=put_your_key_here
```

ルートプロジェクト、`server`ディレクトリ、`ui`ディレクトリにて依存関係含めてnpmパッケージをインストールします。

```bash
cd openai-tokens-dashboard
npm install

cd server
npm install

cd ../ui
npm install
```

ディレクトリを `server` に変更し、ダッシュボードサーバーを実行します。

```bash
cd server
npm run start
```

別のターミナルを開き、ディレクトリをルートプロジェクトから `ui` ディレクトリに変更して、ダッシュボードの UI 開発を実行します。

```bash
cd ui
npm run dev
```

サーバーとクライアントが起動している場合は、ブラウザーを開き、`http://localhost:5173` に移動します。

[<img src="https://griddb.net/en/wp-content/uploads/2023/08/openai-dashboard-ui-init.png" alt="" width="1738" height="762" class="aligncenter size-full wp-image-29752" />][4]

すべてが順調に進んでいれば、上のスクリーンショットのようなウェブ・ユーザー・インターフェイスが表示されます。

## OpenAI APIを深く掘り下げる

トークン使用状況ダッシュボードにOpenAI APIを効果的に統合して利用するには、APIの特徴、機能、利用可能なエンドポイントを理解することが不可欠です。これにより、トークンの使用データを取得し、コストを計算し、ダッシュボードにアップデートを提供することが可能になります。

ここでは、OpenAI APIの主な主要点について詳しく見ていきます。

1.  **APIドキュメント**： OpenAIが提供するAPIの[公式ドキュメント][5]を確認することから始めましょう。このプロジェクトではチャット補完を使います。[チャットAPI][6]のドキュメントには、利用可能なエンドポイント、リクエスト/レスポンスフォーマット、特定のガイドラインや制限事項の概要が記載されています。

2.  **コスト**： OpenAIはリリースしているモデルごとに異なる[価格][7]体系を持っています。私たちのプロジェクトでは、`gpt-3.5-turbo`または`gpt-3.5-turbo-16k`モデルを利用します。これらのモデルのコストテーブルは以下の通りです：
    
    | Model       | Input               | Output             |
    | ----------- | ------------------- | ------------------ |
    | 4K context  | $0.0015 / 1K tokens | $0.002 / 1K tokens |
    | 16K context | $0.003 / 1K tokens  | $0.004 / 1K tokens |

OpenAI APIを深く掘り下げることで、その機能を包括的に理解し、それに応じてトークン使用ダッシュボードを設計することができます。

## 核心的な問題: トークンの使用コスト

### OpenAI API トークンの理解

OpenAI の API は、リクエストを処理する際の基本的な作業単位としてトークンを使用します。基本的に、トークンはAPIが読み取って処理するテキストの塊です。トークンは厳密には単語、文字、バイトに相当しませんが、計算作業の尺度として考えることができます。

テキストをトークンに分割するトークン化プロセスは、モデルが使用する特定のアルゴリズムに従います。例えば英語の場合、トークンは1文字という短いものから、"a "や "apple "のような1単語という長いものまであります。しかし、すべての単語が1つのトークンではないことに注意する必要があります。例えば、"chatbot "という単語は1つのトークンですが、"ChatGPT "のような単語は、テキストをトークン化するためにモデルがどのようにトレーニングされたかに基づいて、複数のトークン（"Chat"、"##G"、"##PT"）に分割されるかもしれません。

APIコールのトークンの数は、コールのコストとコールが全く機能するかどうかの両方に大きく影響します。OpenAIはトークンごとに課金し、モデルには1回の呼び出しで処理できるトークンの上限があります。例えば、2021年9月現在、`gpt-3.5-turbo`モデルは1回の呼び出しで4096トークンが上限です。

トークンの数え方を理解することは、コストを管理し、APIコールが意図したとおりに動作することを保証するために非常に重要です。コストを管理し、OpenAI API を効率的に使用するためには、トークンがどのように機能し、API 呼び出しにどのように影響し、どのように正確にカウントするかを理解することが不可欠です。

### トークン使用によるコストへの影響

OpenAI API を使用するコストは、API 呼び出し中に処理されたトークンの数に直接リンクしています。この価格モデルは、テキストの生成、テキストの翻訳、その他のタスクの実行など、APIリクエストの種類に関係なく当てはまります。

各APIコールは一定数のトークンを消費します。トークンには、モデルに生成を依頼するメッセージ内のトークンだけでなく、提供するプロンプト内のトークンや、特別な指示やフォーマットのために予約されたトークンも含まれます。したがって、API呼び出しで使用されるトークンの総数は、入力プロンプト内のトークン、生成されるメッセージ、および特別な指示の合計となります。

### トークン使用量の監視と管理の必要性

トークンの使用量とコストには直接的な相関関係があり、APIコールの成功にも影響することから、OpenAIのAPIを使用する際にはトークンの使用量を監視・管理することが非常に重要であることは明らかです。これが重要な理由はいくつかあります。

1.  **予算の見積もり**： トークンの使用状況を監視することで、ユーザーは自分の使用パターンが予算とどのように整合しているかを知ることができます。新しいアプリケーションを設計するために、トークンのコストを見積もることができれば、非常に価値があります。

2.  **API利用の最適化**： トークンを監視することで、APIがどの程度効率的に使用されているかについての貴重な洞察を得ることができます。例えば、制御命令やフォーマットのために大量のトークンが使用されている場合、ユーザはアプリケーションの機能を犠牲にすることなく、トークン数を減らすために使用方法を改善できるかもしれません。

## トークン使用状況ダッシュボード

### トークン使用状況ダッシュボードの役割と利点

トークン使用状況ダッシュボードは、ユーザが OpenAI API 呼び出しのトークン使用状況を監視・管理するためのツールです。このようなダッシュボードは、トークンの使用状況に関する洞察を提供し、OpenAI API の使用に関連する意思決定プロセスをサポートする上で重要な役割を果たします。トークン使用状況ダッシュボードを実装する主な利点は以下の通りです。

1.  モニタリング： トークン使用状況ダッシュボードは、トークンの使用状況に関する最新情報を提供することができます。

2.  使いやすさ： 優れたデザインのダッシュボードは、トークンの使用状況を視覚的にわかりやすく表示します。これにより、技術的なユーザもそうでないユーザもアクセスしやすくなり、幅広い理解とより効果的な意思決定が促進されます。

次のセクションでは、このダッシュボードを構築する技術としてGridDB、Node.js、Reactが選ばれた理由と、これらの利点を達成するためにそれらがどのように貢献しているかについて掘り下げていきます。

## なぜGridDB、Node.js、Reactなのか？

効率的でユーザーフレンドリーなトークン使用状況ダッシュボードを構築する上で、テクノロジーの選択は極めて重要です。このプロジェクトでは、 **GridDB**、**Node.js**、**React** を主要なテクノロジーとして選択しました。その理由は以下の通りです。

### GridDB

[GridDB][8]は、IoTやビッグデータ向けに最適化された、拡張性の高いインメモリNoSQL時系列データベースです。大量の構造化データの保存と検索に適しており、トークンの使用データを長期的に管理・追跡するのに最適です。

主な利点は以下の通りです：

*   **スケーラビリティ**： GridDBは優れたスケーラビリティを備えており、大量のデータを容易に扱うことができます。これは、トークンの使用状況をリアルタイムで監視したり、トレンド分析のために過去のデータを保存したりする上で非常に重要です。

*   **スピード**： GridDBはインメモリデータベースであるため、高速なデータ処理が可能です。これにより、リアルタイムのモニタリングや迅速な意思決定に不可欠な、タイムリーな更新と迅速なレスポンスを実現します。

*   **信頼性**： GridDB の堅牢なアーキテクチャは、高可用性とデータの一貫性を保証し、トークン使用状況ダッシュボードの信頼できる基盤を提供します。

### Node.js

[Node.js][9] は、Chrome の V8 JavaScript エンジン上に構築された強力な JavaScript ランタイムです。Node.jsはスケーラブルなネットワーク・アプリケーションを構築するように設計されており、トークン使用量ダッシュボードのバックエンドを実装するのに最適です。Node.jsを選択した理由は以下の通りです。

*   **パフォーマンス**： Node.jsは、イベント駆動、ノンブロッキングI/Oモデルを使用しているため、軽量で効率的です。これは、我々のトークン使用量ダッシュボードのような、分散デバイス間で実行されるデータ集約的なリアルタイム・アプリケーションに最適です。

*   **コミュニティ・サポート**： Node.jsには大規模で活発なコミュニティがあり、開発のスピードアップや一般的な問題を解決するためのライブラリやフレームワークが豊富に用意されています。

*   **統合**： Node.jsはGridDBやReactとシームレスに統合でき、スタック全体で統一されたJavaScript開発環境を提供します。

*   **コスト**： Node.jsの優れた点は、JavaScriptをメイン言語として、バックエンドからクライアントサイドまで、完全に機能するWebアプリケーションを開発できることです。

### React

[React][10]は、ユーザーインターフェイス、特にシングルページのアプリケーションを構築するためのJavaScriptライブラリです。Facebookと個々の開発者や企業のコミュニティによって管理されています。Reactを選ぶ理由は以下の通りです。

*   **コンポーネントベース**： Reactは、開発者が独自の状態を管理するカプセル化されたコンポーネントを構築し、それらを組み合わせて複雑なUIを作ることを可能にします。これは、さまざまなインタラクティブ要素を持つダッシュボードの構築に最適です。

*   **効率性**： Reactは、メモリ内のデータ構造キャッシュを作成し、結果の差分を計算し、ブラウザの表示DOMを効率的に更新します。その結果、高速で応答性の高いユーザー・インターフェースが実現されます。

*   **人気とサポート**： React は広く使用されており、強力なコミュニティがあるため、トークン使用状況ダッシュボードのフロン トエンドを構築するための安全で信頼できる選択肢となります。

GridDB、Node.js、React を組み合わせることで、トークン使用状況ダッシュボードを構築するための強力で効率的、かつ信頼性の高いスタックが提供されます。

## ダッシュボードの設計

### ソリューションの構造化

OpenAI API トークン使用状況ダッシュボードを効果的に実装するには、スケーラビリティ、保守性、拡張性を確保する強固なアーキテクチャ設計を確立する必要があります。

[<img src="https://griddb.net/en/wp-content/uploads/2023/08/openai-tokens-usage-2023-06-27-0509.svg" alt="" width="1249" height="784" class="aligncenter size-full wp-image-29749" />][11]

以下は、提案されているソリューションのアーキテクチャ設計の概要です。

1\. **クライアント・サーバー・アーキテクチャー**：このソリューションはクライアント・サーバー・アーキテクチャーを採用し、クライアント・サイドがユーザー・インターフェースのレンダリングとユーザーとのインタラクションを担当し、サーバー・サイドがデータ処理、ストレージ、外部APIとの通信を担当します。

2\. **フロントエンド（クライアントサイド）**：

*   **React**： フロントエンドはReact JavaScriptライブラリを使用して開発されます。Reactのコンポーネント・ベースのアプローチと仮想DOMは、効率的なレンダリングを可能にし、レスポンシブなユーザー・インターフェースを保証します。

3\. **バックエンド（サーバーサイド）**：

*   **Node.js**： バックエンドは、一般的なサーバーサイドJavaScriptランタイムであるNode.jsを使用して構築されます。Node.jsはイベント駆動、ノンブロッキングI/Oモデルを提供し、同時リクエストやリアルタイム更新の処理に適しています。
*   **Express.js**: Node.js用のウェブ・アプリケーション・フレームワークであるExpress.jsを使用して、RESTful APIの開発を簡素化し、ルーティング、ミドルウェア、リクエスト/レスポンスを処理します。
*   **GridDB**： 高性能インメモリデータベースであるGridDBは、データ管理とストレージに利用されます。GridDBの分散アーキテクチャとスケーラビリティ機能は、大量のトークン利用データの処理に適しています。
*   **OpenAI APIの統合**： サーバーサイドのコードは、トークン利用データを取得するためにOpenAI APIと統合します。この統合には、APIリクエスト、認証処理、レスポンスデータの解析が含まれます。

4\. **データフロー**

*   クライアントサイドは、Node.js バックエンドによって公開される RESTful API エンドポイントを通じてサーバーサイドと通信します。
*   サーバーサイドはGridDBとやり取りし、トークンの利用データを効率的に保存・取得します。
*   また、OpenAIのAPIとも連携し、トークンの利用状況をリアルタイムで取得します。
*   処理されたデータはJSONレスポンスとしてクライアントサイドに返され、ダッシュボードのユーザーインターフェイスを更新するために使用されます。

フロントエンドとバックエンドを明確に分離することで、外部APIとの統合が容易になり、将来的なダッシュボードの拡張や追加にも対応できる強固な基盤を提供します。

## [インストール][2]

### GridDBのセットアップ

GridDBのセットアップは簡単です。Ubuntuへの新規インストールはこちらの[リンク][12]を、WindowsへのWSL（Windows Subsystem Linux）経由のインストールは[こちら][13]を参照してください。

既にインストールされている場合は、以下のコマンドでgriddbサービスが実行されているか確認してください。

```bash
sudo systemctl status gridstore
```

このUbuntu OSのコマンドライン出力は、griddbサービスが実行されていることを示しています。

```bash
● gridstore.service - GridDB database server.
     Loaded: loaded (/lib/systemd/system/gridstore.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2023-07-04 04:47:12 +07; 9h ago
   Main PID: 575 (gsserver)
      Tasks: 34 (limit: 7017)
     Memory: 144.7M
     CGroup: /system.slice/gridstore.service
             └─575 /usr/bin/gsserver --conf /var/lib/gridstore/conf

Jul 04 04:47:08 GenAI systemd[1]: Starting GridDB database server....
Jul 04 04:47:09 GenAI gridstore[381]: Starting gridstore service:
Jul 04 04:47:12 GenAI gridstore[526]: ..
Jul 04 04:47:12 GenAI gridstore[526]: Started node.
Jul 04 04:47:12 GenAI gridstore[381]: [ OK ]
Jul 04 04:47:12 GenAI systemd[1]: Started GridDB database server..
```

### Node.js のセットアップ

このブログ記事で取り上げているプロジェクトでは、Node.js LTS バージョン 18 を使用しています。Node.jsがインストールされているかどうかを確認するには、以下のコマンドを実行してください。

```bash
node --version
```

Node.jsがインストールされていないか、古いバージョンを使っている場合は、公式サイト[nodejs.org][14]からNode.js LTSをアップグレードまたはインストールすることをお勧めします。

## OpenAI ダッシュボードのビルド

このプロジェクトアプリケーションのソースコードは、[こちら][1]から参照できます。

### GridDBとNode.jsの統合

Node.jsはシンプルなサーバーとして機能するが、[Express.js][15]のようなフレームワークを使用することで、データの解析や経路管理を簡素化することができ、メンテナンスが容易になります。Node.jsサーバーとGridDBデータベースを接続するアプリケーションの中核は、`griddb.cjs`ファイルです。このファイルはプロジェクトの `lib` ディレクトリにあります。このファイル内の関数により、GridDB の初期化、データの保存、データのクエリなどを行うことができます。

GridDB を初期化するには、Node.js サーバで `griddb.cjs` をインポートします。

```js
// in the routes.js file

import * as GridDB from './libs/griddb.cjs';

const { collectionDb, store, conInfo, containerName } =
    await GridDB.initGridDbTS();
```

データを保存するには、以下の機能を使います。

```js
async function saveData({ prompt, cost, details }) {
    const id = generateRandomID();

    // Data is saved as an array
    const content = [parseInt(id), prompt, cost, details];

    const saveStatus = await GridDB.insert(content, collectionDb);
    console.log(`GridDB save operation: ${saveStatus.status}`);

    if (saveStatus.status) {
        return { status: saveStatus, operation: 'save' };
    } else {
        return { status: saveStatus, operation: 'save' };
    }
}
```

`saveData`関数は、GridDBのデータを以下のフィールドを持つコレクションとして保存します。

*   **id**： コレクションの ID (整数型)
*   **prompt**： プロンプト（文字列）
*   **cost**： 各 OpenAI API 呼び出しのコスト (文字列)
*   **details**： トークンの詳細データ (文字列)

データは配列 `content` として保存することと、GridDB からすべてのデータをクエリすることが重要です。

```js
const data = await GridDB.queryAll(conInfo, store);
```

接続情報 `conInfo` とストアまたはコレクション `store` は `griddb.cjs` 内の関数によって自動的に提供されます。必要に応じて、そのデータをさらに処理するために利用することができます。

このアプリケーションの Node.js サーバーには 3 つの主要なルートがあります。

| Routes                              | Operation | Purpose                                                           |
| ----------------------------------- | --------- | ----------------------------------------------------------------- |
| http://localhost:2001/api           | GET       | Show all the data in the GridDB database                          |
| http://localhost:2001/api           | POST      | Call OpenAI API and save the data response in the GridDB database |
| http://localhost:2002/api/totalcost | GET       | Get the cost data for all prompts                                 |

### OpenAI APIでトークンを管理する

OpenAI APIとNode.jsの統合は、公式ライブラリ[`openai-node`][16]の助けを借りて実現できます。このプロジェクトで使用するデフォルトのOpenAIモデルは `gpt-3.5-turbo-16k` です。

```js
// services/prompt.js

import { Configuration, OpenAIApi } from 'openai';

const configuration = new Configuration({
    apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);

//...

const completion = await openai.createChatCompletion({
    model: 'gpt-3.5-turbo-16k',
    messages: [
        { role: 'system', content: 'You are a helpful assistant.' },
        { role: 'user', content: prompt },
    ],
    max_tokens: 1000,
});
```

トークンのコスト計算は `priceTag` リファレンスに基づきます。

```js
const priceTag = {
    prompt: 0.003,
    completion: 0.004,
};
```

OpenAIのモデルを変更した場合は、プライスタグリファレンス（[こちら][7]を参照）も変更するようにしてください。

### ダッシュボードのUIをReactで構築する

OpenAI APIトークン使用状況ダッシュボードの直感的でユーザーフレンドリーなインターフェースを確保するために、Reactの力を活用します。

ウェブアプリケーションのユーザーインターフェースは、主に2つの部分から構成されます。

*   **プロンプト入力**： このセクションでは、OpenAI APIのプロンプトを入力します。

*   **ダッシュボード**： このセクションはトークンの使用量を表示し、関連するコストの見積もりを提供します。トークンとコストのデータを表示するための UI 要素は 3 つあります。
    
    *   **Cost**： 現在のプロンプト・コストを示します。
    
    *   **Tokens**： 現在のトークン数を示します。
    
    *   **All Total Cost**： GridDB データベースから取得したコスト合計を示します。

[<img src="https://griddb.net/en/wp-content/uploads/2023/08/openai-dashboard-ui.png" alt="" width="1728" height="833" class="aligncenter size-full wp-image-29751" />][17]

ダッシュボードのユーザーインターフェースのソースコードは `ui` ディレクトリにあります。Reactを使えば、データのためのインタラクティブなユーザーインターフェースを驚くほど簡単に構築できます。先ほどのNode.jsサーバールートで見たように、ダッシュボードからデータを保存したり読み込んだりするために、それらを簡単に利用することができます。Node.js サーバールートを呼び出してレスポンスデータを処理するには、`fetch` WebAPI を使用するだけです。

```js
fetch('http://localhost:2001/api', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
    },
    body: JSON.stringify({ input: inputText }),
})
    .then((response) => response.json())
    .then((data) => {
        console.log(data);
        setUsageData(data.data.usage);
        // Update totalTokens
        const promptTokensInK = data.data.usage.prompt_tokens / 1000;
        const completionTokensInK = data.data.usage.completion_tokens / 1000;
        const promptTokensCost = promptTokensInK * 0.003;
        const completionTokensCost = completionTokensInK * 0.004;
        setTotalTokens(data.data.usage.total_tokens);
        setTotalCost(promptTokensCost + completionTokensCost);
    })
    .catch((error) => {
        console.error(error);
    });
```

`inputText`には、OpenAI APIのプロンプトデータを入力します。OpenAI API には特定のレスポンス・フォーマットがあることに注意してください。

```js
{
    id: 'chatcmpl-7YMGXEuuR4Wi7HKNFa5XAbTL5hWgb',
    object: 'chat.completion',
    created: 1688423097,
    model: 'gpt-3.5-turbo-16k-0613',
    choices: [ { index: 0, message: [Object], finish_reason: 'stop' } ],
    usage: { prompt_tokens: 148, completion_tokens: 6, total_tokens: 154 }
}
```

サーバーサイドでもクライアントサイドでも、データを処理するのは簡単だ。Reactでデータを表示するには、`useState`を使って自動的にUIを更新することができます。

```js
const [totalTokens, setTotalTokens] = useState(0);

// Set total tokens to update Tokens ui
setTotalTokens(data.data.usage.total_tokens);
```

> このプロジェクトでは、ダッシュボード・アプリケーションのために特別に作られた **Tremor** UIを使用しています。 [こちら][18]をご覧ください。

```js
<Card>
    <div className="h-24">
        <Title>Tokens</Title>
        <Metric>{totalTokens}</Metric>
        <Flex className="mt-2">
            {usageData ? (
                <Text>
                    {usageData.prompt_tokens} Prompt{' '}
                    {usageData.completion_tokens} Completion{' '}
                </Text>
            ) : (
                <p>Loading...</p>
            )}
        </Flex>
    </div>
</Card>
```

[<img src="https://griddb.net/en/wp-content/uploads/2023/08/tokens-ui.png" alt="" width="574" height="238" class="aligncenter size-full wp-image-29750" />][19]

## 結論

このブログ記事のプロジェクトはMVPに過ぎません。履歴やログの表示、データの視覚化の改善、より優れた分析ビューなどの機能を追加することで、さらに強化することができます。

 [1]: https://github.com/junwatu/openai-tokens-dashboard
 [2]: #installation
 [3]: https://platform.openai.com/account/api-keys
 [4]: https://griddb.net/en/wp-content/uploads/2023/08/openai-dashboard-ui-init.png
 [5]: https://platform.openai.com/docs/api-reference
 [6]: https://platform.openai.com/docs/api-reference/completions/create
 [7]: https://openai.com/pricing
 [8]: https://griddb.net
 [9]: https://nodejs.org
 [10]: https://react.dev/
 [11]: https://griddb.net/en/wp-content/uploads/2023/08/openai-tokens-usage-2023-06-27-0509.svg
 [12]: https://docs.griddb.net/latest/gettingstarted/using-apt/
 [13]: https://docs.griddb.net/latest/gettingstarted/wsl/
 [14]: https://nodejs.org/en/download
 [15]: https://expressjs.com/
 [16]: https://github.com/openai/openai-node
 [17]: https://griddb.net/en/wp-content/uploads/2023/08/openai-dashboard-ui.png
 [18]: https://github.com/tremorlabs/tremor
 [19]: https://griddb.net/en/wp-content/uploads/2023/08/tokens-ui.png