LoginSignup
1
0

lagChainを活用したお手軽モダンなチャットアプリ(Vue3)

Posted at

概要

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

typescript chatapi.ts
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;
  }
}

画面

typescript view.ts
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;
};
html view.vue
<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ライフを!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0