Does OnUserTurnCompleted not trigger in a text-onl...
# ask-ai
b
Does OnUserTurnCompleted not trigger in a text-only mode?
t
Thanks for reaching out, RikuKomiya. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — good question. I’m confirming that the
on_user_turn_completed
lifecycle hook does trigger in text-only mode. When a user sends a text message, the session treats that message as a completed user turn. In practice this means: • Text input that reaches final state (for example a final transcription or a submitted chat message) invokes the same turn-completion logic as spoken input. • In implementations that use a `user_input_transcribed`/transcription callback,
is_final = True
corresponds to a completed user turn and aligns with
on_user_turn_completed
being called. • Custom text handlers that interrupt the current turn and then call
session.generate_reply()
follow the same turn-completion flow. If you’re not seeing
on_user_turn_completed
fire in your app, can you share: 1. Whether you’re using the built-in text input flow or a custom text handler. 2. A short snippet of the code where you register the
on_user_turn_completed
hook and where you handle incoming text (redact any secrets). 3. Any logs showing incoming text events or
is_final
values. With those I can help pinpoint why it might not be firing in your specific setup. Sources: Transcriber | LiveKit Documentation | Text and transcriptions | LiveKit Documentation | Pipeline nodes and hooks | LiveKit Documentation
b
LiveKit Agent Playgroundで試したところ、その中でテキストを入力してもonUserTurnCompletedが発火しませんでした。
t
状況共有ありがとうございます — LiveKit Agent Playgroundでテキストを入力しても
onUserTurnCompleted
が発火しなかった件について、一緒に原因を絞っていきましょう。以下の点を確認させてください(短く答えられる項目で大丈夫です)。 まず確認したいこと 1. Playground のどの画面でテキストを送っていますか?(例:チャット入力欄/デバッグ用コンソール/カスタム入力フォーム) 2. 入力したテキストは「送信」ボタンで送っていますか、それともEnter押下で送っていますか? 3. Playground上で音声入力は無効にして「テキストのみ」モードでテストしていますか? 4. ブラウザの開発者ツールでコンソールログやネットワークのイベントは見ていますか?エラーメッセージや関連するログが出ていますか? 簡単なトラブル切り分け手順(まずこれを試してください) • ブラウザのコンソールを開き、ページをリロードしてからテキストを送ってみてください。コンソールに `user_input_transcribed`、`is_final`、または
onUserTurnCompleted
に関するログが出ているか確認してください。 • もしPlaygroundに「開発者向けログ出力」や「イベントトレース」表示があれば、それを有効にしてテキスト送信時のイベントがどう流れているか見てください。 • 別のブラウザ/プライベートウィンドウで同じ手順を試して、ブラウザ拡張やキャッシュの影響を除外してください. もし可能なら共有してください(問題特定が早まります) • テキスト送信時のブラウザコンソールの関連ログ(スクリーンショットでOK)。 • Playground上での具体的な操作手順(どのボタンを押す、など)。 •
onUserTurnCompleted
を確認するための設定やフラグを変更している場合はその内容。 補足(知っておいていただきたいこと) •
onUserTurnCompleted
はテキスト入力でも発火する設計です。通常、テキストが最終状態(`is_final=true` に相当)になればターン完了として扱われます。 • ただし、PlaygroundのUIやカスタムイベントの実装によっては、そのイベントがコンソールに出力されない、もしくは別のイベント名で扱われている可能性があります。 上記の情報を教えていただければ、次に見るべきログや具体的なデバッグ手順(必要ならコンソールで打つコマンドや確認ポイント)をお伝えします。
b
チャットモードでは on_user_turn_completed が呼ばれません。 LiveKit agents v1.3.7 のフローは以下の通り: 音声モード (VoiceAgent) AudioRecognition → on_end_of_turn → _user_turn_completed_task → on_user_turn_completed ✅ チャットモード (ChatAgent) RoomIO._on_user_text_input → _default_text_input_cb → sess.generate_reply(user_input=text) _default_text_input_cb(types.py52 54)は def _default_text_input_cb(sess, ev): sess.interrupt() sess.generate_reply(user_input=ev.text) これは generate_reply を直接呼びます。generate_reply → _generate_reply → _pipeline_reply_task と進みますが、on_user_turn_completed を経由しません。 on_user_turn_completed は AudioRecognition のターン検出パスでのみ呼ばれるため、チャットモードでは記憶検索が一切実行されません。
t
詳しい調査とフローの共有、ありがとうございます。ご指摘の通り、LiveKit Agents v1.3.7 の現在の設計では、*`on_user_turn_completed` は音声経路(AudioRecognition → on_end_of_turn → _user_turn_completed_task)でのみ呼ばれる*ため、チャットモード(テキスト入力)では発火しません。お伝えいただいたフロー図どおりの挙動が仕様となっています。 ポイントを簡潔にまとめます。 • 音声(VoiceAgent): AudioRecognition がターン終了を検出すると
_user_turn_completed_task
を経由して
on_user_turn_completed
が呼ばれる。RAG検索やコンテキスト注入などをここで挟める。 • チャット(ChatAgent):
RoomIO._on_user_text_input
→
_default_text_input_cb
で
sess.interrupt()
と
sess.generate_reply(user_input=...)
が直接呼ばれるため、`on_user_turn_completed` は経由されない。結果として、`on_user_turn_completed` に置いている前処理(例:記憶検索)は実行されない。 チャットで同等の処理を実行するための現実的な回避策 • *カスタムのテキスト入力コールバックを実装して
on_user_turn_completed
相当の処理を明示的に呼び出す*方法をおすすめします。以下はその骨子です(実装例はご利用のエージェントクラス構成に合わせて調整してください):
Copy code
async def custom_text_input_cb(sess, ev):
    # 1) 必要ならセッションを中断
    sess.interrupt()

    # 2) テキスト入力をもとに、on_user_turn_completed で期待するコンテキストを構築
    try:
        # session.agent の API に応じて呼び出す(同期/非同期は合わせてください)
        await sess.agent.on_user_turn_completed(sess.chat_ctx, ev.text)
    except Exception as e:
        logger.warning(f"on_user_turn_completed 実行に失敗しました: {e}")

    # 3) 通常どおり応答生成
    sess.generate_reply(user_input=ev.text)
• この方法で、チャット入力時にも音声経路で行っているのと同等の前処理(RAG検索やツール呼び出しなど)を実行できます。 運用上の注意 •
on_user_turn_completed
を直接呼ぶ場合、エージェント内部の状態(turn context / chat context)の整合性に注意してください。必要なら現在の
chat_ctx
を明示的に渡したり、ロックや例外処理を入れてください。 • 将来的に共通のフック(テキスト・音声どちらも通る統一パス)が実装されればそちらに移行するのが望ましいです。 次に進めるために手伝えること • カスタムコールバックの具体的な実装コード(あなたのエージェント/セッション定義の断片)を共有いただければ、より正確なサンプルを出します(機密情報は伏せてください)。 • 既に実装して試したコードやエラーログがあれば、それを元に原因追及もできます。 この設計差分による不具合でお困りでしたら、上記カスタムハンドラの実装を一緒に詰めていきます。どの部分から始めましょうか? Sources: Pipeline nodes and hooks | LiveKit Documentation | Turns overview | LiveKit Documentation | Text and transcriptions | LiveKit Documentation
b
llm_nodeを使うのはどうですか?
t
良い質問です — 短く結論を先に言うと、*はい、`llm_node` を使うのは有効で実務的な解決策です*。チャット(テキスト)モードで
on_user_turn_completed
が呼ばれない問題を解消し、音声とテキストで同じ前処理(例:RAG検索・コンテキスト注入・出力フィルタ)を実行したい場合に特に有効です。 ポイントを整理します。 1) なぜ有効か •
llm_node
は LLM 推論の直前/直後に必ず実行されるノードです。テキスト入力・音声入力いずれのパスでも LLM を呼ぶ場合に介在できるため、入力モードに依存しない共通処理を入れられます。 • そのため、音声経路で
on_user_turn_completed
に置いている処理(RAG やツール前処理など)を、テキスト経路では
llm_node
側で実行することで挙動の一貫化が可能です。 2) どう使うか(高レベル) • 既存の
on_user_turn_completed
でやっている処理を
llm_node
に移す、または
llm_node
で同等の処理を追加する。 • テキスト入力は
generate_reply()
によって LLM パイプラインに入るので、その直前の
llm_node
で RAG や前処理を行えば、結果は LLM に注入されます。 • LLM 出力の後処理(出力フィルタや構造化解析→TTS 変換など)も
llm_node
で扱えます。 3) 利点と注意点 • 利点: ◦ テキスト/音声を統一的に扱える。 ◦ 出力の検閲やフォーマット制御を確実に挟める。 • 注意点: ◦
llm_node
は
on_user_turn_completed
の「完全な置き換え」ではない点に留意してください。`on_user_turn_completed` はターン検出(VAD/STT に基づく)後に走るライフサイクルフックで、ターン検知に関連するメタ処理には依然意義があります。 ◦
llm_node
内に重い処理(遅い外部検索など)を入れると応答遅延が発生するため、非同期呼び出しやキャッシュ設計を検討してください。 4) 実装イメージ(概念) •
llm_node
の前処理フックで: ◦ ユーザー入力に基づき RAG を実行 → 検索結果をプロンプトに注入 •
llm_node
の後処理フックで: ◦ LLM 出力をフィルタ/構造化 → 必要なら TTS 用テキストへ変換 5) 次に何を手伝えるか • もしよければ、あなたの
on_user_turn_completed
で行っている処理(RAG 呼び出し、ツール実行、出力フィルタ等)を教えてください。`llm_node` に移すための具体的なコードスニペットを用意します(実装例は同期/非同期APIに合わせて作成します)。 • 既に試した実装やパフォーマンス面の懸念があれば共有ください。最適化(非同期検索、結果キャッシュなど)の提案をします。 まとめ:`llm_node` はテキスト入力でも音声入力と同等の前処理・後処理を確実に行うための推奨アプローチです。具体的な移行サンプルが必要なら、現在の処理の概要を教えてください。 Sources: Pipeline nodes and hooks | LiveKit Documentation | LLM-Powered Content Filter | LiveKit Documentation | LLM Output Replacement | LiveKit Documentation | Build a multilingual voice agent that automatically switches languages | Li... | Tool definition and use | LiveKit Documentation