mcpField
R
runtime

runtime

github.com/assistant-ui開発296.3k 回インストール1 日前に更新
リポジトリ

runtime について

assistant-ui Runtime は、アシスタントのチャット UI を構築する開発者向けの状態・操作レイヤーです。メッセージストアをローカルで管理する方式と外部ストアを使う方式があり、共通のクライアント API からスレッド、分岐、実行ライフサイクルを扱えます。

主な機能

  1. 01ローカル実行時と外部ストアの選択肢
  2. 02メッセージ、スレッド、実行ライフサイクルの状態
  3. 03分岐、編集、再生成
  4. 04リアクティブな状態とイベント Hook
  5. 05フレームワークおよびプロトコルアダプター

活用シーン

  1. 01チャット UI とモデルアダプターの接続
  2. 02既存のメッセージストアを使ったチャットコンポーネントの利用
  3. 03スレッド状態の監視と実行制御

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/assistant-ui/skills --skill runtime

インストール後、Claude Code で/runtimeを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
adapters.md
external-store.md
local-runtime.md
runtime-concepts.md
state-hooks.md
types.md
voice.md

詳細

assistant-ui ランタイム

最新の API については、常に assistant-ui.com/llms.txt を参照してください。

ランタイムはチャット UI の背後にある状態とアクションのレイヤーです。メッセージ、スレッド、ブランチ、実行ライフサイクルを管理し、すべてのプリミティブとフックが統一された AssistantClient を通じてそこから読み取ります。構築方法は 2 つあります。LocalRuntime(useLocalRuntime)はメッセージストアを自動で管理します。1 つの ChatModelAdapter.run 関数を実装するだけで、ブランチ、編集、再生成が無料で利用できます。ExternalStoreRuntime(useExternalStoreRuntime)はその逆で、メッセージを自分で管理し、提供するコールバックに応じて UI 機能が有効になります。useChatRuntime(@assistant-ui/ai-sdk)などのフレームワークアダプターや、useAssistantTransportRuntime などのプロトコルランタイムは、この 2 つのいずれかの上に構築されています。AssistantRuntimeProvider の下にマウントされると、すべてのランタイムは useAui、useAuiState、useAuiEvent を通じて同じ方法で読み取られ、操作されます。

リファレンス

  • ./references/local-runtime.md -- useLocalRuntime の詳細: ストリーミング、ツール呼び出し、human-in-the-loop、承認ゲート、resumeRun
  • ./references/external-store.md -- useExternalStoreRuntime の詳細: ハンドラーマトリクス、ブランチ、キューイング、ツール結果
  • ./references/state-hooks.md -- useAui、useAuiState、useAuiEvent、状態の形状、削除されたレガシーフック
  • ./references/types.md -- ThreadMessage、MessageStatus、MessagePart、添付ファイル、機能の型
  • ./references/adapters.md -- 添付ファイル、音声、ディクテーション、フィードバック、サジェスチョン、履歴のアダプター
  • ./references/voice.md -- RealtimeVoiceAdapter によるリアルタイム双方向音声
  • ./references/runtime-concepts.md -- unstable_ ポリシー、useAssistantTransportRuntime、MessageNotSentError、reloadMainThread、メッセージのタイミング

ランタイム階層

AssistantRuntime
├── ThreadListRuntime (threads)
│   └── ThreadListItemRuntime[] (threadListItem)
└── ThreadRuntime (thread)
    ├── ComposerRuntime (composer)        new-message input
    ├── SuggestionRuntime[] (suggestion)  follow-up prompts, via thread.suggestions
    └── MessageRuntime[] (message)
        ├── ComposerRuntime (composer)    edit-message input
        ├── MessagePartRuntime[] (part)
        ├── ChainOfThoughtRuntime (chainOfThought)
        │   └── MessagePartRuntime[] (part)
        └── AttachmentRuntime[] (attachment)

modelContext と tools はスレッドスコープとは独立して解決されます。詳細は runtime-concepts.md と tools スキルを参照してください。ツリー内のすべてのノードは、aui.<scope>(命令型)または useAuiState セレクター内の s.<scope>(リアクティブ)から到達可能で、呼び出し元コンポーネントがレンダリングされる場所に自動的にスコープされます。

useAui、useAuiState、useAuiEvent

useAui() は現在の AssistantClient を返します。状態を購読しないため、その同一性は構造的な変更(たとえばスレッドの切り替え)のときのみ変化します。イベントハンドラーや命令型コードで使用してください。

import { useAui } from "@assistant-ui/react";

const aui = useAui();
aui.thread.append({ role: "user", content: [{ type: "text", text: "Hello!" }] });
aui.thread.cancelRun();

useAuiState(selector) は AssistantState の一部を購読し、選択された値が変化したとき(Object.is で比較)にのみ再レンダリングします。セレクターはストアの更新ごとに実行されるため、プリミティブまたは安定した参照を返す必要があり、新しいオブジェクトや配列リテラルを返してはならず、また状態全体を返してもいけません(それは例外をスローします)。

import { useAuiState } from "@assistant-ui/react";

const isRunning = useAuiState((s) => s.thread.isRunning);   // primitive: correct
const messages = useAuiState((s) => s.thread.messages);      // stable array reference: correct

// Wrong: a new object literal every call re-renders on every store update
const bad = useAuiState((s) => ({ isRunning: s.thread.isRunning, text: s.composer.text }));

useAuiState は値ごとに 1 回呼び出してください(または複数の呼び出しを組み合わせてください)。値をまとめるためにスコープを新しいオブジェクトに展開しないでください。

useAuiEvent(nameOrSelector, callback) はコンポーネントのライフタイムの間購読します。コールバックはエフェクトイベントのシム内で実行されるため、メモ化された参照なしで最新のクロージャが発火します。

import { useAuiEvent } from "@assistant-ui/react";

useAuiEvent("thread.modelContextUpdate", ({ threadId }) => {
  console.log("Model context updated", threadId);
});

スコープアクセサーはプロパティです

0.15 以降、aui.<scope> は呼び出しではなくプロパティです。呼び出し(aui.thread())はまだ動作しますが非推奨です。スコープ上のメソッドは括弧を保持します。

aui.thread.getState();             // property accessor
aui.threads.switchToNewThread();
aui.thread.composer().send();      // composer() is a method of the thread scope
aui.thread.message({ index: 0 });  // selector object, not a bare index

利用できないスコープを選択しても例外はスローされなくなりました。aui.message は常に真値です。使用前に source で利用可能性を確認してください。スコープがマウントされていない場合、source は null になります。

if (aui.message.source != null) {
  aui.message.reload();
}

source、query、name は予約されたアクセサリープロパティであり、スコープのメソッドに解決されることはありません。

AuiConfig と AuiProvider

AuiProvider は、サブツリーに対して AssistantClient をマウントします。その config プロパティは、@assistant-ui/react からインポートした AuiConfig({...}) で構築する必要があります(生のオブジェクトリテラルは型エラーになります)。トップレベルでは、config だけでそのサブツリーのクライアントが作成されます。親プロバイダーの下にネストする場合、extends は必須です。extends={aui} は親クライアントを拡張し、extends={null} は新しいルートを分離します(開発時に強制されます)。AssistantRuntimeProvider は内部的に AuiProvider をインストールし、さらに config を受け付けて、ランタイム自身のスコープに加えて追加のスコープ(ツールキットなど)をアタッチします。ランタイムの周りに AuiProvider を手動で配線する代わりに、こちらを使用してください。

import { AssistantRuntimeProvider, AuiConfig, AuiProvider, Tools, useAui } from "@assistant-ui/react";

// Runtime root: config attaches extra scopes next to the runtime's own
const config = AuiConfig({ tools: Tools({ toolkit }) });
<AssistantRuntimeProvider runtime={runtime} config={config}>{children}</AssistantRuntimeProvider>;

// Nested scope: extend the parent client
function MessageScope({ children }: { children: React.ReactNode }) {
  const aui = useAui();
  const nested = AuiConfig({ tools: Tools({ toolkit: extraToolkit }) });
  return <AuiProvider extends={aui} config={nested}>{children}</AuiProvider>;
}

// Isolated root: detach from any parent client
const isolated = AuiConfig({});
<AuiProvider extends={null} config={isolated}>{children}</AuiProvider>;

config は単なるデータです。モジュールスコープに巻き上げたり、レンダリングごとにインラインで構築したり、メモ化したりできます。プロバイダーが config の同一性に依存することはありません。AuiProvider の ref は、マウント後に生成されたクライアントを受け取ります。

スレッドとメッセージの操作

const aui = useAui();
const thread = aui.thread;

thread.append({ role: "user", content: [{ type: "text", text: "Hello" }] });
thread.startRun({ parentId: null });
thread.cancelRun();

const state = thread.getState();   // { messages, isRunning, capabilities, composer, ... }

const message = thread.message({ index: 0 });   // or { id: messageId }
message.reload();
message.switchToBranch({ position: "next" });
message.submitFeedback({ type: "positive" });

const editComposer = message.composer();
editComposer.beginEdit();
editComposer.setText("Updated");
editComposer.send();

イベント

ほとんどのイベントは非推奨であり、代わりに useAuiState から同じ遷移を導出することが推奨されます。これは、初回レンダリング時とリプレイ時に、イベントハンドラーとは異なり正しく動作します。

イベントステータス
threads.selectionChanged現行: メインスレッドの切り替えごとに { threadId, previousThreadId } を伴って 1 回発火します。マウント時に最初に選択されたスレッドに対しては発火しません
thread.modelContextUpdate現行: モデルコンテキストは state ではなくプロバイダーに存在するため、state から導出できる等価なものがありません
composer.attachmentAddError現行: reason は "no-adapter" | "not-accepted" | "adapter-error" です
composer.send、composer.attachmentAdd非推奨: composer の text / attachments を監視してください
thread.runStart、thread.runEnd非推奨: s.threadRunning の切り替わりを監視してください
thread.initialize非推奨: s.thread.messages が空でなくなるのを監視してください
threadListItem.switchedTo、threadListItem.switchedAway非推奨: threads.selectionChanged を使用し、必要に応じてアイテムごとのスコープ内で id によるフィルタリングを行ってください

非推奨のペアは引き続き発火し、次のメジャーまで動作し続けます。threads.selectionChanged は、古いペアが発火しなかった状況でも発火します。たとえば switchToNewThread() や、マウント後に解決されるディープリンクされた初期スレッドなどです。

オプションのスコープ

s.optional.<scope> は、スコープがマウントされていない場合に例外をスローする代わりに undefined に解決されます。これは、スコープの内側と外側の両方でレンダリングされるコンポーネントからスコープを読み取る安全な方法です。

const partType = useAuiState((s) => s.optional.part?.type);

命令型の同等物は aui.<scope>.source != null です。

ケイパビリティ

const capabilities = useAuiState((s) => s.thread.capabilities);

RuntimeCapabilities(@assistant-ui/core から): switchToBranch、switchBranchDuringRun、edit、reload、delete、cancel、refetchThread、unstable_copy、speech、dictation、voice、attachments、feedback、queue。ランタイムはこれらのほぼすべてを、明示的なオプションではなく、指定したもの(コールバック、アダプター)から導出します。unstable_copy は、ExternalStoreRuntime が unstable_capabilities を介して強制的にオフにできる唯一のフラグです。refetchThread は、aui.threads.reloadMainThread() が開いているスレッドをその場で更新するのか、それともランタイムの再マウントにフォールバックするのかを報告します。詳細は runtime-concepts.md を参照してください。

よくある落とし穴

「undefined のプロパティを読み取れません」

  • フックが AssistantRuntimeProvider(または設定がスコープを提供する AuiProvider)の内部で呼び出されていることを確認してください。
  • まだマウントされていない可能性があるスコープについては、s.optional.<scope> を読み取るか、aui.<scope>.source != null でガードしてください。

useAuiState による無限再レンダリング

  • セレクターが新しいオブジェクトまたは配列リテラルを返していました。これにはスコープをオブジェクトにスプレッドする場合も含まれます。プリミティブは個別の呼び出しで選択するか、メモ化された参照を返してください。

レガシーフックのインポートが解決できない

  • useAssistantRuntime、useThreadRuntime、useThread、useMessage、useComposer、useMessagePart、useAttachment、useThreadListItem などは 0.15 で削除されました。完全なマッピングについては state-hooks.md を、または update スキルを参照してください。

状態が更新されない

  • レンダー内で getState() を読み取るのではなく、useAuiState でセレクターを使用してください。getState() は一度きりのスナップショットであり、サブスクリプションではありません。

複数のスレッドまたは会話サイドバー

  • このスキルは単一スレッドの状態とランタイムフックを扱います。スレッドの作成、切り替え、アーカイブ、およびスレッド一覧のレンダリングについては、代わりに thread-list スキルを使用してください。

関連スキル

  • thread-list -- マルチスレッド UI、スレッドの CRUD、threads.selectionChanged のコンシューマー
  • tools -- ツールキットの作成、ツール呼び出し UI、承認ゲートとヒューマンツールの詳細
  • elements -- この状態を自動的に読み取る、スタイル済みの Thread およびコンポーザーコンポーネント
  • cloud -- AssistantCloud、マネージド永続化、および useChatRuntime({ cloud })
  • streaming -- プロトコルランタイムの基盤となる DataStream および AssistantTransport のワイヤプロトコル

コメント