Oracle APEXのページにMicrosoft AI Chat Protocolのクライアントを読み込んで、ローカルLLMをブラウザから直接呼び出してみます。
作成したAPEXアプリケーションは以下のように動作します。
macOSのLM Studioに、モデルとしてmmnga/Llama-3-ELYZA-JP-8B-gguf/Llama-3-ELYZA-JP-8B-Q8_0.ggufをロードしてローカルLLM(ローカル・サーバー)を実行しています。
Oracle APEXのアプリケーションは別のホストよりロードしているため、localhostで動作しているLLMを呼び出そうとするとCORSのエラーが発生します。そのため、LM Studioの設定のCross-Origin-Resource-Sharing (CORS)をONにしています。
せっかくブラウザから直接LLMを呼び出すのでストリーミングを実装するつもりだったのですが(Microsoft AI Chat ProtocolはgetStreamedCompletion APIでストリーミングをサポートしています)、コードに以下のように記載があり、エンドポイントURLの末尾が/streamであることが必須のようでした。そのため、ストリーミングは断念してgetCompletion APIを呼び出しています。
ボタンSUBMITをクリックしたときに、以下のJavaScriptコードを実行します。
const response = await asStream(
this.client.path(`${this.basePath}/stream`).post(request),
);
以下より実装について紹介します。
空のAPEXアプリケーションを作成し、チャットの呼び出しはデフォルトで作成されるホーム・ページに実装しています。
ローカルLLMとやり取りするダイアログは、インライン・ダイアログとして実装しています。
タイプは静的コンテンツ、レイアウトのスロットはDialogs, Drawers and Popupsを選択し、外観のテンプレートにInline Dialogを選択します。これで、動的アクションでリージョンを開くまでは、画面にダイアログは表示されません。
テンプレート・オプションのAuto Heightをチェックし、SizeとしてMedium (600x400)を選択しています。
ページ・プロパティのJavaScriptやCSSに、Microsoft AI Chat Protocolのクライアントを呼び出すために必要な設定や、メッセージの見かけを良くするためのCSSクラスを設定します。
JavaScriptのファイルURLに以下を指定します。Microsoft AI Chat Protocolを扱うクライアント・ライブラリを読み込みます。
https://cdn.jsdelivr.net/npm/@microsoft/ai-chat-protocol/dist/iife/index.min.js
ファンクションおよびグローバル変数の宣言に以下を記述します。
var client; /* Microsoft AI Chat Protocolのクライアント */
var messages; /* 送信するメッセージの配列 */
const contentDiv = document.getElementById("content"); /* チャット履歴を表示する要素 */
/* 表示されたチャット履歴を初期化 */
function removeAllChildren(element) {
while (element.firstChild) {
element.removeChild(element.firstChild);
}
}
CSSのインラインに以下を記述します。
.bubble-left {
max-width: 70%; /* 吹き出しの幅 */
padding: 10px 15px; /* 内側の余白 */
margin: 10px 0; /* 吹き出しの上下の間隔 */
background-color: #e1f5fe; /* 吹き出しの背景色 */
text-align: left; /* 文字を左寄せ */
position: relative; /* 吹き出しの位置を相対的に */
border-radius: 15px 15px 15px 15px; /* 角を丸める */
}
.bubble-right {
max-width: 70%; /* 吹き出しの幅 */
padding: 10px 15px; /* 内側の余白 */
margin: 10px 0; /* 吹き出しの上下間隔 */
background-color: #c8e6c9; /* 吹き出しの背景色(薄い緑色) */
text-align: left; /* 文字は左寄せ */
position: relative; /* 吹き出しの位置を相対的に */
margin-left: auto; /* 吹き出しを右寄せ */
border-radius: 15px 15px 15px 15px; /* 角を丸める */
}
インライン・ダイアログのリージョンを開くボタンOPENを作成し、ボタンをクリックしたときに実行される動的アクションを作成します。
ボタンOPENを押した時に、最初に以下のJavaScriptコードを実行します。メッセージの履歴を消去し、ローカルLLMとのチャットを初期化します。
以下のURLよりLM StudioのローカルLLMを呼び出します。
http://localhost:8080/v1/chat/completions
/* AI Chat Protocolのクライアントを作成する */
client = new ChatProtocol.AIChatProtocolClient(
"http://localhost:8080/v1/chat/completions",
{
"allowInsecureConnection": true
}
);
/* メッセージとチャット履歴を表示するDIV要素も初期化。 */
messages = [];
removeAllChildren(contentDiv);
初期化を行なった後に、リージョンChatを開きます。
リージョンChatには、チャットのやり取りを表示するリージョンとしてContent、ユーザーのメッセージを入力するページ・アイテムとしてP1_MESSAGE、メッセージをローカルLLMに送信するボタンSUBMIT、および、ダイアログを閉じるボタンCLOSEを作成します。
リージョンContentは静的コンテンツとして作成し、ソースのHTMLコードとして以下を記述します。
<div id="content"></div>
修飾を最低限にするため、外観のテンプレートとしてBlank with Attributesを選択します。CSSクラスにh400を設定し、リージョンの高さを400ピクセルに固定します。
詳細のカスタム属性としてstyle="overflow-y: scroll;"を設定し、400ピクセルで表示できないメッセージはスクロールさせて表示するようにします。
メッセージを入力するページ・アイテムP1_MESSAGEは、テキスト領域として作成しています。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 送信メッセージを追加 | |
*/ | |
messages.push({ | |
"role": "user", | |
"content": apex.items.P1_MESSAGE.value | |
}); | |
/* | |
* 送信するメッセージを左に印刷する | |
*/ | |
const messageDiv = document.createElement('div'); | |
messageDiv.classList.add("bubble-left"); | |
messageDiv.textContent = apex.items.P1_MESSAGE.value; | |
contentDiv.appendChild(messageDiv); | |
messageDiv.scrollIntoView({ behavior: 'smooth' }); /* 下にスクロール */ | |
apex.items.P1_MESSAGE.setValue(""); /* 入力したメッセージを消す */ | |
/* | |
* AIに問合せ | |
*/ | |
const result = client.getCompletion(messages); | |
/* | |
* 応答を右に印刷する。 | |
*/ | |
result.then( (response ) => { | |
const replyDiv = document.createElement('div'); | |
replyDiv.classList.add("bubble-right"); | |
console.log(response.choices[0]); | |
replyDiv.textContent = response.choices[0].message.content; | |
contentDiv.appendChild(replyDiv); | |
replyDiv.scrollIntoView({ behavior: 'smooth' }); | |
}); |
今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/sample-microsoft-ai-chat-protocol-call-local-llm.zip
Oracle APEXのアプリケーション作成の参考になれば幸いです。
完