ChatGPT javascript application - foolmacky/DevelopDocs GitHub Wiki

ChatGPTでの開発の感覚を掴むため、サンプルコードを書いて動きを確認してみた

■ 開発の準備

■ sample APPの実装

  • 処理の流れ
    • ブラウザ側で音声認識:テキスト化
    • テキストをChatGPTにリクエストして、レスポンスを得る
    • レスポンスで得られたテキストを音声化
    • 音声読み上げ
  • 利用PFなど
    • 音声認識はブラウザからGoogle APIを使う
    • ChatGPT OpenAPIにリクエスト
    • 返答のテキストをVOICEVOXのAPIで音声化
    • VOICEVOXは背後で起動
    • page sourceをブラウザで取得するweb serverをdockerで起動

■ ソース

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.3.4/axios.min.js"></script>
</head>

<body>
<button id="start-button">START</button>
<button id="stop-button">STOP</button>
<audio class="audio" id="nowRes"></audio>
<div id="list">
</div>

<script type="text/javascript">
const recognition = new webkitSpeechRecognition();
recognition.continuous = true;
recognition.interimResults = false;

document.querySelector('#start-button').addEventListener('click', () => {
  recognition.start();
});

document.querySelector('#stop-button').addEventListener('click', () => {
  recognition.stop();
});

document.querySelector('#nowRes').addEventListener('play', () => {
  recognition.stop();
});

document.querySelector('#nowRes').addEventListener('ended', () => {
  recognition.start();
});

recognition.onresult = async (event) => {
  const results = event.results[event.results.length - 1];
  const transcript = results[0].transcript;

  if (transcript != "") {

    const list = document.getElementById('list');
    const add = document.createElement('p');
    add.textContent = "Q : " + transcript;
    list.appendChild(add)

    const responseText = await requestChatAPI(transcript);

    const chat = document.createElement('p');
    chat.textContent = "A : " + responseText;
    list.appendChild(chat)

    await createAudio(responseText);
  }
};

//生成したAPI Keyを入れる(あくまでサンプルなので直書き)
//間違ってもGitにアップしない
const api_key = "<--hogehoge_key-->";

async function requestChatAPI(text) {
  const headers = {
    "Content-Type": "application/json",
    Authorization: `Bearer ${api_key}`,
  };

  const messages = [
    {
      role: "user",
      content: text,
    },
  ];

  const payload = {
    model: "gpt-3.5-turbo",
    //max_tokens: 128,
    messages: messages,
  };
  const response = await axios.post(
    "https://api.openai.com/v1/chat/completions",
    payload,
    {
      headers: headers,
    }
  );
  return response.data.choices[0].message.content;
}

async function createAudio(text) {
  const data = await createVoice(text);
  const audio = document.querySelector(".audio");
  audio.src = URL.createObjectURL(data);
  audio.play();
}
async function createQuery(text) {
  const response = await axios.post(
  	`http://localhost:50021/audio_query?speaker=3&text=${text}`, { headers: { 'Access-Control-Allow-Origin': "*" } }
  );
  return response.data;
}
async function createVoice(text) {
  const query = await createQuery(text);
  const response = await axios.post(
    "http://localhost:50021/synthesis?speaker=3",
    query,
    { responseType: "blob" }
  );
  return response.data;
}

</script>
</body>

</html>

■ 独り言

  • webkitSpecRecognition.interimResultsはfalseにしないと、変換途中のテキストがリターンされて、onresultイベントが複数回発生してしまう
  • それでもまだ中身が「空」の状態でイベントが発生するので、中身が空の場合は処理が実行されないように制御
  • VICEVOXが音声を読み上げている最中は、マイクをOFFにしないと、ChatGPTからの返答を更に認識してしまう
  • あくまでサンプル実装として、localhostのVOICEVOXにリクエストする際は、Access-Control-Allow-Originを設定しないとCORSエラーが発生する。サービスで使うソースからは削除
  • ローカルのファイルを読み込んで実行する場合は、webkitSpeechRecognition.start()をすると、「マイクをブラウザから使うか否か」を確認するダイアログが毎回開くので、Docker上のwebサーバから取得する形にする。そうすると、最初の1回目のみアラートが出る
  • chromeの起動についてはCORSエラーを強制的にOFFにするため、以下のオプションをつける
open -n -a /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --args --user-data-dir=“/Users/foolmacky/tmp” --disable-web-security --use-fake-ui-for-media-stream
⚠️ **GitHub.com Fallback** ⚠️