開発実践ガイド
TypeScriptからローカルLLM APIを呼ぶ方法|最小構成でストリーミングと中止を確認

目次
公式サイトで次に進む
この記事で明示的に扱うソフトだけを表示します。配布・手順は各公式ページで確認してください。
LM Studio
local-gen.jpがファイルを配布するものではなく、互換性を保証するものでもありません。
TypeScriptからローカルLLMを呼ぶ最初の実装は、複雑なエージェント用フレームワークを重ねる前に小さく作った方が問題を切り分けやすい。
最初の目標は、1回リクエストを送り、必要なら途中で止め、返ってきた内容を検証できることだ。
接続先URLとモデルIDを設定へ分ける
LM StudioなどはOpenAI互換APIを提供するが、提供元が違えば細かな挙動まで完全に同じとは限らない。
接続先とモデルIDをコード中の多数の場所へ直接書かず、設定値として分ける。
const baseUrl = 'http://localhost:1234/v1';
const model = 'your-model-id';
最初は同じPCのlocalhostだけで確認し、LAN公開や認証は別の工程にすると原因を分けやすい。
fetchで最小の1回を送る
SDKを入れる前でも、fetchを使えばHTTP通信の形を直接確認できる。
const response = await fetch(`${baseUrl}/chat/completions`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model,
messages: [{ role: 'user', content: 'こんにちは' }],
}),
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
URLや送信項目は一例なので、実際に使うソフトの現在の公式API資料と照合する。
最初はHTTPステータス、Content-Type、返ってきた本文を確認できるようにしておく。
ストリーミングは別の受信処理にする
ストリーミングを有効にすると、最後に完成したJSONが一つだけ返るとは限らない。
通信上の小さな受信単位と、APIが定義する一つのイベントやメッセージを同じものとして扱わず、使用しているAPIの形式に従って組み立てる。
最初はストリーミングを無効にして1回成功させ、その後に同じ入力で有効にすると、通信形式の問題を切り分けやすい。
長い生成を途中で止められるようにする
TypeScriptではAbortControllerを使い、fetchへ停止用のsignalを渡せる。
const controller = new AbortController();
const response = await fetch(url, {
method: 'POST',
signal: controller.signal,
// ...
});
controller.abort();
利用者が中止した場合と、サーバー側のエラーや通信切断は別の状態として記録する。
返ってきた値を使う前に検証する
HTTP 200が返っただけでは、アプリが期待する項目が必ず存在するとは限らない。
必要な項目があるか、型が期待どおりかを確認してから後続処理へ渡す。構造化出力を使っていても、値の意味まで自動的に正しいわけではない。
最初は少なくとも、
- 正常に完了する
- 想定外のレスポンスを検出する
- 利用者が途中で中止する
の3ケースを記録する。
ここまで小さな実装で確認できれば、その上へ画面やSDK、エージェント処理を追加した後でも、API通信そのものの問題を分離しやすい。
curlで最初の通信から確認したい場合は、ローカルLLM APIへcurlで最初のリクエストを送る方法も確認できる。
まとめ
- 接続先URLとモデルIDはコードの奥へ埋め込まず、変更できる設定として分ける
- ストリーミング中のレスポンスは、通常の「完成したJSONが1個返る通信」と同じ読み方をしない
- AbortControllerを使い、利用者が長い生成を途中で止められる経路を用意する
- OpenAI互換APIでも、返ってきた値を無検証のままアプリのデータとして使わない
この記事で扱ったデータ
モデル・GPU・実行ソフトの確認済みデータを、記事とは別に確かめられます。