概要
langChain(js)先生のおかげて実装がメタクソシンプルになったので共有
世の中にはpython/Node.jsの例はいっぱいあるが、クライアントアプリの事例がすくないので。。
構成
- Node.js
- Vue.js (Vue3 + TypeScript + Composition API)
- Azure OpenAI or OpenAI or OpenAI互換なAPI
- "langchain": "^0.1.11"
- "@langchain/core": "^0.1.20"
- "@langchain/openai": "^0.0.13"
langChainのバージョンは2024年1月末のものです。
メジャーバージョンアップが入らなければ動くと思います。
(こいつら、3日おきとかにリリースしよるので、、、)
コード
API
import { appSetting } from "@/config";
import { ChatOpenAI } from "@langchain/openai";
import { ChatMessage, HumanMessage } from "@langchain/core/messages";
import { useAppStore } from "@/store/app";
import type { ChatCompletionMessageChunk, ImagePoolingResponse } from "@/types";
const app_store = useAppStore();
function getChatModel(
temperature: number,
token: string | undefined
): ChatOpenAI {
const openAIConfig = app_store.openAIConfig!;
// Azure
if (appSetting.login_required) {
return new ChatOpenAI({
temperature: temperature,
// APIキー
azureOpenAIApiKey: openAIConfig.apim_subscription_key,
azureOpenAIBasePath: openAIConfig.endpoint + "/deployments",
// gpt3.5-turboとか
azureOpenAIApiDeploymentName: openAIConfig.deployment,
modelName: openAIConfig.model,
// 2024-02-15-preview とか
azureOpenAIApiVersion: openAIConfig.api_version,
// APIManagement関係の追加ヘッダ
configuration: {
baseOptions: {
headers: {
"Ocp-Apim-Subscription-Key": openAIConfig.apim_subscription_key,
Authorization: "Bearer " + token,
},
},
},
});
} else {
// local
return new ChatOpenAI({
temperature: temperature,
openAIApiKey: openAIConfig.apim_subscription_key,
configuration: {
baseURL: openAIConfig.endpoint,
},
});
}
}
export async function* streamChatCompletion_langChain(
message: ChatMessage[],
temperature: number,
token: string | undefined
) {
const chatModel = getChatModel(temperature, token);
try {
const completion = await chatModel.stream(message);
for await (const chunk of completion) {
yield chunk.content;
}
} catch (error) {
console.error(error);
throw error;
}
}
画面
import { HumanMessage } from "@langchain/core/messages";
import {
streamChatCompletion_langChain,
} from "@/chatapi";
// チャット送信
const sendMessage = async () => {
//API呼び出し
const temp = [0.9, 0.5, 0.1][input.temperature]; //・創造的に・バランス・厳格に
const message = new HumanMessage({
content: "Hello OpenAI!!!",
});
const generator = streamChatCompletion_langChain(
[message] as ChatMessage[],
temp,
appSetting.login_required ? app_store.authToken : undefined
);
// 順次生成の回答を表示
loading_message.loading = true;
for await (let chunk of generator) {
loading_message.content = loading_message.content + chunk;
}
loading_message.loading = false;
};
<v-card v-show="loading_message.loading" flat class="px-4">
<div class="d-flex flex-no-wrap">
<v-icon icon="mdi-loading mdi-spin" size="large"></v-icon>
<v-card-text :class="loading_message.color">{{
loading_message.content
}}</v-card-text>
</div>
</v-card>
わーいシンプル!
と言いつつ、ちょっとだけ(意図的に)めんどくさくしてるとこがあり
以下のコードでChatOpenAIクラスをログインあり・なし切り替えてます。
vllm とかでローカルにLLM APIを立てている場合、ログインとかモデルの設定とかが要らないので、そこを分岐してます。
function getChatModel(
temperature: number,
token: string | undefined
): ChatOpenAI {
const openAIConfig = app_store.openAIConfig!;
if (appSetting.login_required) {
// Azure APIManagement対応
return new ChatOpenAI({
temperature: temperature,
azureOpenAIApiKey: openAIConfig.apim_subscription_key,
azureOpenAIBasePath: openAIConfig.endpoint + "/deployments",
azureOpenAIApiDeploymentName: openAIConfig.deployment,
modelName: openAIConfig.model,
azureOpenAIApiVersion: openAIConfig.api_version,
configuration: {
baseOptions: {
headers: {
"Ocp-Apim-Subscription-Key": openAIConfig.apim_subscription_key,
Authorization: "Bearer " + token,
},
},
},
});
} else {
//ローカルモデル対応
return new ChatOpenAI({
temperature: temperature,
openAIApiKey: openAIConfig.apim_subscription_key,
configuration: {
baseURL: openAIConfig.endpoint,
},
});
}
}
逆に、いや、OpenAIしか呼ばないし。
ログイン管理とか要らんですし、という場合
以下でOKです。
export async function* streamChatCompletion_langChain(
message: ChatMessage[]
) {
const chatModel = new ChatOpenAI({
openAIApiKey: OPEN_AI_API_KEY,
});
try {
const completion = await chatModel.stream(message);
for await (const chunk of completion) {
yield chunk.content;
}
} catch (error) {
console.error(error);
throw error;
}
}
関連情報
AzureADと連携したログインについては、以前に書いてるのでそちら参照。
Azure AD連携を使ってVue.js(Vue3)のAzure OpenAIチャットアプリのアクセス権を制御する #TypeScript - Qiita
なんか最近はAzureのOpenAISDKも充実してきたようで、そちらへの乗り換えも検討中。
でもRetriever連携とか考えるとlangChainで繋いでおきたいんだよなぁ。。
というわけで、よきlangChainライフを!