elevenlabs:sdk-migration について
ElevenLabs の client、React、React Native パッケージを次のメジャーバージョンへ移行する開発者向けスキルです。破壊的な API 変更と、任意のフック再構成を扱います。
主な機能
- 01段階的なパッケージ移行
- 02ConversationProvider の設定
- 03Conversation API の更新
- 04粒度の細かい会話フック
- 05React クライアントツールの登録
活用シーン
- 01アプリを次の SDK メジャーバージョンへ更新
- 02削除されたエクスポートの置き換えとインポート更新
- 03React の状態管理を細粒度フックへ再構成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ elevenlabs/ packages --skill elevenlabs:sdk-migrationインストール後、Claude Code で/elevenlabs:sdk-migrationを入力して読み込まれたか確認します。
詳細
SDK 移行ガイド(次のメジャーバージョン)
次のメジャーリリースで予定されている @elevenlabs/client、@elevenlabs/react、@elevenlabs/react-native の破壊的変更に関する移行ガイドです。
移行の順序
以下の手順を順番に進めてください。各手順は前の手順を前提としています。
- インストール — 新しいパッケージをインストールします。
- インポートを修正 — 削除されたエクスポート(
Input、Output、DeviceFormatConfig、DeviceInputConfig、ElevenLabsProvider)を削除し、後継に置き換えます。 ConversationProviderでラップ — すべてのuseConversation呼び出しで、祖先にプロバイダーが必要になりました。- API 呼び出しを更新 — 変更されたシグネチャに対応します(React では
startSessionが同期処理に、Conversationはクラスではなくなるなど)。 - 粒度の細かいフックで最適化 (任意) — ローカル状態を
useConversationStatus、useConversationModeなどに置き換えることで、レンダリングパフォーマンスを向上させます。
インストール
npm install @elevenlabs/client@next @elevenlabs/react@next @elevenlabs/react-native@next
@elevenlabs/client
instanceof Conversation によるチェックを置き換える
Conversation はプレーンな名前空間オブジェクトとなり、TextConversation | VoiceConversation の型エイリアスになりました。これによりツリーシェイキングが可能になり、内部アーキテクチャがシンプルになります。instanceof Conversation はコンパイルできなくなり、サブクラス化もできません。
代わりに、ダックタイピングで型を絞り込みます。
変更前:
import { Conversation } from "@elevenlabs/client";
if (session instanceof Conversation) {
/* ... */
}
class MyConversation extends Conversation {
/* ... */
}
変更後:
import { Conversation } from "@elevenlabs/client";
// startSession call is unchanged
const session: Conversation = await Conversation.startSession(options);
// Narrow using duck-typing instead of instanceof
if ("changeInputDevice" in session) {
// session is VoiceConversation
}
Input と Output の使用を会話メソッドに置き換える
Input クラスと Output クラスは、内部のオーディオノードへ直接アクセスすることで実装の詳細との結合が強くなるため、エクスポートされなくなりました。VoiceConversation の input フィールドと output フィールドは、非公開になりました。すべてのオーディオ操作は、会話インスタンス自体のメソッドになりました。
変更前:
import { Input, Output } from "@elevenlabs/client";
const input: Input = conversation.input;
input.analyser.getByteFrequencyData(data);
input.setMuted(true);
const output: Output = conversation.output;
output.gain.gain.value = 0.5;
output.analyser.getByteFrequencyData(data);
const newInput: Input = await conversation.changeInputDevice(config);
変更後:
conversation.getInputByteFrequencyData(); // replaces input.analyser.getByteFrequencyData
conversation.setMicMuted(true); // replaces input.setMuted
conversation.setVolume({ volume: 0.5 }); // replaces output.gain.gain.value
conversation.getOutputByteFrequencyData(); // replaces output.analyser.getByteFrequencyData
await conversation.changeInputDevice(config); // return value dropped
await conversation.changeOutputDevice(config); // return value dropped
wakeLock への直接アクセスを削除する
誤った干渉を防ぐため、wake lock のライフサイクルは自動的に管理されるようになり、wakeLock フィールドは非公開になりました。無効にするには useWakeLock: false を指定します。
const conversation = await Conversation.startSession({
// ...
useWakeLock: false,
});
@elevenlabs/react
useConversation を ConversationProvider でラップする
useConversation では、祖先に ConversationProvider が必要になりました。プロバイダーは、複数のフックがそれぞれ独立して購読できる共有セッション状態を保持します。これにより、新しい粒度の細かいフックシステムと、より優れたレンダリングパフォーマンスが実現します。
このフックは以前と同じオプションを受け取り、同じ形の値を返します。また、新しいフィールドとして isMuted、setMuted、isListening、mode、message が追加されています。
複数のコンポーネントで useConversation を使用しているコードベースを移行する場合は、次のどちらを希望するかユーザーに確認してください。
- すべての会話コンポーネントをツリー上位の単一の
ConversationProviderでラップする(すべてのコンポーネントで1つのセッションを共有する)、または - 各コンポーネントを個別の
ConversationProviderでラップする(各コンポーネントが独立したセッションを管理する)。
この選択は、セッションの共有、状態の分離、コンポーネントのアーキテクチャに影響します。決めつけず、作業を進める前に確認してください。
変更前:
import { useConversation } from "@elevenlabs/react";
function App() {
const { status, isSpeaking, startSession, endSession } = useConversation({
agentId: "your-agent-id",
onMessage: message => console.log(message),
onError: error => console.error(error),
});
return (
<div>
<p>Status: {status}</p>
<p>{isSpeaking ? "Agent is speaking" : "Agent is listening"}</p>
<button onClick={() => startSession()}>Start</button>
<button onClick={() => endSession()}>Stop</button>
</div>
);
}
変更後:
import { ConversationProvider, useConversation } from "@elevenlabs/react";
function App() {
return (
<ConversationProvider>
<Conversation />
</ConversationProvider>
);
}
function Conversation() {
const { status, isSpeaking, startSession, endSession } = useConversation({
agentId: "your-agent-id",
onMessage: message => console.log(message),
onError: error => console.error(error),
});
return (
<div>
<p>Status: {status}</p>
<p>{isSpeaking ? "Agent is speaking" : "Agent is listening"}</p>
<button onClick={() => startSession()}>Start</button>
<button onClick={() => endSession()}>Stop</button>
</div>
);
}
startSession から await を削除する
useConversation(React)を使用する場合、startSession は同期処理になり、void を返します。セッションのライフサイクルはプロバイダーが管理し、エラーは例外としてスローされるプロミスではなく、コールバックを通じて通知されます。
移行時には、次の対応を行います。
startSession()の呼び出しからawaitを削除します。startSession()が関数内で唯一の await 対象だった場合は、関数からasyncキーワードを削除します。- 接続エラーへの対処として
startSession()の前後をtry/catchで囲んでいた場合は、エラー処理をonErrorコールバックまたはuseConversationStatusフックに移します。同期呼び出しでは、セッションの失敗時に例外がスローされなくなりました。
変更前:
const startConversation = async () => {
try {
await conversation.startSession({ agentId: "..." });
} catch (error) {
console.error("Failed to start:", error);
setStatus("disconnected");
}
};
変更後:
// onError handles failures instead of try/catch
const conversation = useConversation({
onError: (error) => {
console.error("Failed to start:", error);
setStatus("disconnected");
},
});
const startConversation = () => {
conversation.startSession({ agentId: "..." });
};
削除された型インポートを更新する
DeviceFormatConfigを@elevenlabs/clientのFormatConfigに置き換えます。DeviceInputConfigを@elevenlabs/clientのInputDeviceConfigに置き換えます。
再エクスポートの変更
@elevenlabs/react は、従来の選択的な再エクスポートに代わり、export * を通じて @elevenlabs/client のすべてを再エクスポートするようになりました。
粒度の細かい会話フックを導入する(任意)
新しいフックは会話状態の独立した部分を購読するため、モードしか読み取らないコンポーネントはステータスが変化しても再レンダリングされません。これがConversationProviderアーキテクチャの主なメリットです。状態を共有することで、粒度の細かい購読が可能になります。
初回の移行がコンパイルできたら、コンポーネントがローカルの接続状態(例:agentState、isMuted、isSpeaking用のuseState)を保持していて、これらのフックが提供する状態と重複していないか確認してください。重複している場合は、ローカル状態を粒度の細かいフックに置き換えるかユーザーに尋ねてください。
useConversationStatus()は、onStatusChangeコールバックを介して管理されるローカルのagentState/status状態を置き換えます。useConversationInput()は、手動の切り替えで管理されるローカルのisMuted状態を置き換えます。useConversationMode()は、ローカルのisSpeaking/isListening状態を置き換えます。useConversationControls()は、再レンダリングを引き起こさない安定したアクション参照(startSession、endSession、sendUserMessage、getInputVolume、getOutputVolumeなど)を提供します。
粒度の細かいフックにリファクタリングする場合:
- コンポーネントのローカル状態へのアクセスが不要なイベントコールバック(
onConnect、onDisconnect、onError、onMessage)は、ConversationProviderのpropsに移動します。 - コンポーネントのローカル状態を参照するコールバック(例:ローカルの
messages配列を更新するもの)にはuseConversationを維持します。ただし、ステータス/モード/入力状態の読み取りには粒度の細かいフックを使用してください。 - ステータスは
useConversationStatus()を介してリアクティブに取得できるため、startSessionのオプションからonStatusChangeを削除します。
| フック | 戻り値 |
|---|---|
useConversationControls() | 安定したアクションメソッド:startSession、endSession、sendUserMessage、setVolume、changeInputDevice、changeOutputDeviceなど。再レンダリングを引き起こしません。 |
useConversationStatus() | status("disconnected"/"connecting"/"connected"/"error")と省略可能なmessage。 |
useConversationInput() | isMuted状態とsetMutedアクション。 |
useConversationMode() | mode("speaking"/"listening")、isSpeaking、isListening。 |
useConversationFeedback() | canSendFeedback状態とsendFeedback(like: boolean)アクション。 |
useRawConversation() | 生のConversationインスタンス、またはnull(エスケープハッチ)。 |
すべてのフックには、祖先としてConversationProviderが必要です。
useConversationからの対応関係:
useConversationの戻り値 | 粒度の細かいフック |
|---|---|
status、message | useConversationStatus() |
isSpeaking、isListening、mode | useConversationMode() |
canSendFeedback、sendFeedback | useConversationFeedback() |
isMuted、setMuted | useConversationInput() |
startSession、endSession、setVolume、... | useConversationControls() |
例 — 各コンポーネントは、特定の状態が変化したときにのみ再レンダリングされます:
import {
ConversationProvider,
useConversationStatus,
useConversationMode,
useConversationControls,
useConversationInput,
useConversationFeedback,
} from "@elevenlabs/react";
function App() {
return (
<ConversationProvider agentId="your-agent-id">
<StatusBadge />
<Controls />
<MuteButton />
<FeedbackButtons />
<ModeIndicator />
</ConversationProvider>
);
}
/** Only re-renders when status changes. */
function StatusBadge() {
const { status } = useConversationStatus();
return <span className={`badge badge-${status}`}>{status}</span>;
}
/** Never re-renders — controls are stable references. */
function Controls() {
const { startSession, endSession } = useConversationControls();
return (
<div>
<button onClick={() => startSession()}>Start</button>
<button onClick={() => endSession()}>Stop</button>
</div>
);
}
/** Only re-renders when mute state changes. */
function MuteButton() {
const { isMuted, setMuted } = useConversationInput();
return (
<button onClick={() => setMuted(!isMuted)}>
{isMuted ? "Unmute" : "Mute"}
</button>
);
}
/** Only re-renders when feedback availability changes. */
function FeedbackButtons() {
const { canSendFeedback, sendFeedback } = useConversationFeedback();
if (!canSendFeedback) return null;
return (
<div>
<button onClick={() => sendFeedback(true)}>👍</button>
<button onClick={() => sendFeedback(false)}>👎</button>
</div>
);
}
/** Only re-renders when mode changes. */
function ModeIndicator() {
const { isSpeaking, isListening } = useConversationMode();
return (
<p>
{isSpeaking ? "Agent is speaking..." : isListening ? "Listening..." : ""}
</p>
);
}
useConversationClientToolでクライアントツールを登録する
React コンポーネントからクライアントツールを動的に登録する新しいフックです。セッション開始後に追加または削除したツールは、すぐに表示されます。ツール名が重複している場合はエラーが発生します。
import { useConversationClientTool } from "@elevenlabs/react";
// Untyped — parameters are Record<string, unknown>
useConversationClientTool("get_weather", params => {
const city = params["city"];
return `Weather in ${city} is sunny.`;
});
// Type-safe — tool names are constrained, params and return types are inferred
type Tools = {
get_weather: (params: { city: string }) => string;
set_volume: (params: { level: number }) => void;
};
useConversationClientTool<Tools>("get_weather", params => {
return `Weather in ${params.city} is sunny.`;
});
@elevenlabs/react-native
ElevenLabsProviderをConversationProviderに置き換える
LiveKit ベースのカスタム実装(ElevenLabsProvider、useConversation)は完全に削除されました。このパッケージは@elevenlabs/reactから再エクスポートするようになり、ConversationProviderと同じ細分化されたフックを使用して、Web とモバイルの API が統一されます。
React Native では、パッケージのインポート時に副作用が実行されます。WebRTC グローバルのポリフィル、ネイティブの AudioSession の設定、プラットフォーム固有の音声セッション戦略の登録が行われます。Web では、副作用なしで再エクスポートされます。
変更前:
import { ElevenLabsProvider, useConversation } from "@elevenlabs/react-native";
function App() {
return (
<ElevenLabsProvider>
<Conversation />
</ElevenLabsProvider>
);
}
function Conversation() {
const conversation = useConversation({
onConnect: ({ conversationId }) => console.log("Connected", conversationId),
onError: message => console.error(message),
});
return (
<Button
onPress={() => conversation.startSession({ agentId: "your-agent-id" })}
/>
);
}
変更後:
import {
ConversationProvider,
useConversationControls,
useConversationStatus,
} from "@elevenlabs/react-native";
function App() {
return (
<ConversationProvider
onConnect={({ conversationId }) =>
console.log("Connected", conversationId)
}
onError={message => console.error(message)}
>
<Conversation />
</ConversationProvider>
);
}
function Conversation() {
const { startSession } = useConversationControls();
const { status } = useConversationStatus();
return <Button onPress={() => startSession({ agentId: "your-agent-id" })} />;
}