Next.js ChatGPTに質問できるコンポーネントを作成
はじめに
Next.jsで下記のようなコンポーネントを作成してみます。
openai version 4.52.1を使用しています。
APIの使用料金は課金方式を従量制から前払い制に変更になっています。
この変更により、ユーザーはAPIの利用前にクレジットを購入し、そのクレジットを消費する形でサービスを利用することになります。この新しい課金方式では、クレジットがなくなるとサービスの利用が停止されますが、オートチャージ機能を設定することでクレジットが一定以下になった場合に自動でチャージされる設定も可能になっています。
APIキーの取得
https://platform.openai.com/docs/overview にアクセスし、ログインしてください。
Dashboard
API keys
> + Create new secret key
名前と権限を設定して、Create secret key
で、APIキーが発行されます。
APIキーが表示されますが、この画面を閉じてしまうと確認することができません。
閉じる前にコピーして保存しておいてください。
環境変数の設定
OPENAI_API_KEY=
ライブラリのインストール
npm install openai
APIルートの作成
Next.jsのAPIルートを使って、OpenAIのAPIとやり取りするサーバーサイドのエンドポイントを作成します。
mkdir -p src/app/api/chat && touch src/app/api/chat/route.ts
今回はv4を使用するので、Configuration
、OpenAIApi
、createChatCompletion
を使用した書き方は使用していません。
公式のモデル一覧
import { NextRequest, NextResponse } from 'next/server';
import OpenAI from 'openai';
export async function POST(req: NextRequest) {
const { messages } = await req.json();
console.log('messages', messages);
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY || '',
});
try {
const res = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
// stream: true,
// max_tokens: 150,
// messages: [{ role: 'user', content: messages }],
messages,
});
console.log('res', res);
// stream: true;がない時
console.log(
'res.choices[0].message.content',
res.choices[0].message.content
);
return NextResponse.json(res.choices[0].message.content, { status: 200 });
// stream: true;
// const stream = OpenAIStream(res);
// return new StreamingTextResponse(stream);
} catch (err) {
throw err;
}
}
コンポーネントの作成
src/app
と同じディレクトリにコンポーネントフォルダ作成(src/components
)
コンポーネントごとのフォルダを作成
mkdir -p src/components/chat && touch src/components/chat/chat.tsx
まずはレスポンスをストリーミング形式ではなく、回答が全て終わってから表示するようにします。
レスポンスをストリーミング形式(生成されたら順番に少しずつ文字を受け取る)で受け取る場合は、後述のVercel AI SDK
を使用した方が便利です。
import axios from 'axios';
import { useState } from 'react';
interface ChatMessage {
role: 'user' | 'chatGPT';
text: string;
}
export function Chat() {
const [messages, setMessages] = useState<string>('');
const [chatHistory, setChatHistory] = useState<ChatMessage[]>([]);
console.log('chatHistory', chatHistory);
// stream: true;
// const { messages, input, handleInputChange, handleSubmit } = useChat({
// api: '/api/chat',
// });
// stream: true;ではない時;
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
const res = await axios.post('/api/chat', {
messages: [{ role: 'user', content: messages }],
});
setChatHistory((prev) => [
...prev,
{ role: 'user', text: messages },
{ role: 'chatGPT', text: res.data },
]);
setMessages(''); // 入力フィールドをクリア
} catch (err) {
console.error('エラー:', err);
setChatHistory((prev) => [
...prev,
{ role: 'user', text: messages },
{ role: 'chatGPT', text: 'エラーが発生しました。' },
]);
}
};
return (
<div className='flex flex-col w-full max-w-md py-24 mx-auto stretch'>
{/* stream: true; */}
{/* {messages.map((m) => (
<div key={m.id} className='whitespace-pre-wrap'>
{m.role === 'user' ? 'ユーザー名: ' : 'chatGPT: '}
{m.content}
</div>
))} */}
{/* <form onSubmit={handleSubmit}>
<input
className='fixed bottom-0 w-full max-w-md p-2 mb-8 border border-gray-300 rounded shadow-xl'
value={input}
placeholder='Chat GPTにメッセージを送信する'
onChange={handleInputChange}
/>
</form> */}
{/* stream: true;ではない時 */}
{chatHistory.map((msg, index) => (
<div key={index} className={`message ${msg.role}`}>
<span>
{msg.role === 'user' ? 'あなた' : 'Chat GPT'}: {msg.text}
</span>
</div>
))}
<form onSubmit={handleSubmit}>
<input
className='fixed bottom-0 w-full max-w-md p-2 mb-8 border border-gray-300 rounded shadow-xl'
value={messages}
onChange={(e) => setMessages(e.target.value)}
placeholder='メッセージを入力してください'
/>
{/* <button type='submit'>送信</button> */}
</form>
</div>
);
}
支払い情報を登録する
Setting
Billing
> Add payment details
個人で登録する場合はIndividual
、企業で登録する場合はCompany
を選択
https://kimini.jp/ を参考に入力したらContinue
チャージ金額を入力します。$0では進めないので、最低額の$5で入力してContinue
チャージ設定はあとから変更できます。
内容を確認してComfirm payment
$5チャージされました。
デプロイ後は、急にサービスが停止することになるため、automatic rechargeの設定(自動チャージ)をした方が良いと思います。
なお、支払い情報を登録する前の作成されたAPIキーは削除して、新しいAPIキーを生成する必要があります。
支払い情報を登録した際は反映までに時間がかかる場合があるようです。
月間の予算制限とメール通知の設定
Limits
> Usage limitsで下記の設定ができます。
- 月の予算を設定し、予算を超えた場合にAPIリクエストは拒否する設定
- 指定した金額を超えた場合はメール通知で通知する設定
Vercel AI SDK
AI機能を備えたアプリケーションを容易に構築できるようにするためのソフトウェア開発キットです。
インストール
npm install ai
import { useChat } from 'ai/react';
import { useState } from 'react';
export function Chat() {
const { messages, input, handleInputChange, handleSubmit } = useChat({
api: '/api/chat',
});
return (
<div className='flex flex-col w-full max-w-md py-24 mx-auto stretch'>
{messages.map((m) => (
<div key={m.id} className='whitespace-pre-wrap'>
{m.role === 'user' ? 'ユーザー名: ' : 'chatGPT: '}
{m.content}
</div>
))}
<form onSubmit={handleSubmit}>
<input
className='fixed bottom-0 w-full max-w-md p-2 mb-8 border border-gray-300 rounded shadow-xl'
value={input}
placeholder='Chat GPTにメッセージを送信する'
onChange={handleInputChange}
/>
</form>
</div>
);
}
import { OpenAIStream, StreamingTextResponse } from 'ai';
import { NextRequest } from 'next/server';
import OpenAI from 'openai';
export async function POST(req: NextRequest) {
const { messages } = await req.json();
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY || '',
});
try {
const res = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
stream: true,
messages,
});
const stream = OpenAIStream(res);
return new StreamingTextResponse(stream);
} catch (error) {
console.error('error', error);
throw error;
}
}
stream:true;
レスポンスをストリーミング形式(生成されたら順番に少しずつ文字を受け取る)で受け取ることができます。
終わりに
何かありましたらお気軽にコメント等いただけると助かります。
ここまでお読みいただきありがとうございます🎉
Discussion