wire:stream
Livewireでは、wire:stream
API を使用して、リクエストが完了する前にコンテンツをWebページにストリームできます。これは、生成されたレスポンスをストリームするAIチャットボットのようなものにとって非常に便利な機能です。Livewire allows you to stream content to a web page before a request is complete via the wire:stream
API. This is an extremely useful feature for things like AI chat-bots which stream responses as they are generated.
Warning! Laravel Octane との互換性はありません Livewire は現在、Laravel Octane で
wire:stream
を使用することをサポートしていません。[!warning] Not compatible with Laravel Octane Livewire currently does not support usingwire:stream
with Laravel Octane.
wire:stream
の最も基本的な機能を示すために、以下に、ボタンが押されると「3」から「0」までのカウントダウンをユーザーに表示する簡単な CountDown コンポーネントを示します。To demonstrate the most basic functionality of wire:stream
, below is a simple CountDown component that when a button is pressed displays a count-down to the user from "3" to "0":
use Livewire\Component;
class CountDown extends Component
{
public $start = 3;
public function begin()
{
while ($this->start >= 0) {
// 現在のカウントをブラウザにストリームします...
$this->stream( // [tl! highlight:4]
to: 'count',
content: $this->start,
replace: true,
);
// 数字の間を1秒間一時停止します...
sleep(1);
// カウンターを減らします...
$this->start = $this->start - 1;
};
}
public function render()
{
return <<<'HTML'
<div>
<button wire:click="begin">Start count-down</button>
<h1>Count: <span wire:stream="count">{{ $start }}</span></h1> <!-- [tl! highlight] -->
</div>
HTML;
}
}
ユーザーが「Start count-down」を押したときのユーザー視点からの動きは次のとおりです。Here's what's happening from the user's perspective when they press "Start count-down":
- ページに「Count: 3」が表示されます"Count: 3" is shown on the page
- 「Start count-down」ボタンを押しますThey press the "Start count-down" button
- 1秒経過すると「Count: 2」が表示されますOne second elapses and "Count: 2" is shown
- このプロセスは「Count: 0」が表示されるまで続きますThis process continues until "Count: 0" is shown
上記のすべては、単一のネットワークリクエストがサーバに送信されている間に行われます。All of the above happens while a single network request is out to the server.
ボタンが押されたときにシステム側で何が起こっているかは次のとおりです。Here's what's happening from the system's perspective when the button is pressed:
begin()
メソッドを呼び出すリクエストがLivewireに送信されますA request is sent to Livewire to call thebegin()
methodbegin()
メソッドが呼び出され、while
ループが開始されますThebegin()
method is called and thewhile
loop begins$this->stream()
が呼び出され、すぐにブラウザへの「ストリームされたレスポンス」が開始されます$this->stream()
is called and immediately starts a "streamed response" to the browser- ブラウザは、
wire:stream="count"
を持つコンポーネント内の要素を見つけ、そのコンテンツを受信したペイロード(最初のストリームされた数値の場合は "3")で置き換えるように指示されたストリームされたレスポンスを受信しますThe browser receives a streamed response with instructions to find the element in the component withwire:stream="count"
, and replace its contents with the received payload ("3" in the case of the first streamed number) sleep(1)
メソッドにより、サーバは1秒間ハングしますThesleep(1)
method causes the server to hang for one secondwhile
ループが繰り返され、新しい数値を毎秒ストリーミングするプロセスは、while
条件が偽になるまで続きますThewhile
loop is repeated and the process of streaming a new number every second continues until thewhile
condition is falsybegin()
の実行が完了し、すべてのカウントがブラウザにストリーミングされると、Livewireはリクエストライフサイクルを終了し、コンポーネントをレンダリングして、最終的なレスポンスをブラウザに送信しますWhenbegin()
has finished running and all the counts have been streamed to the browser, Livewire finishes it's request lifecycle, rendering the component and sending the final response to the browser
チャットボットのレスポンスのストリーミングStreaming chat-bot responses
wire:stream
の一般的なユースケースは、(OpenAI の ChatGPT のような)OpenAI's ChatGPTストリームされたレスポンスをサポートするAPIから受信したチャットボットのレスポンスをストリーミングすることです。A common use-case for wire:stream
is streaming chat-bot responses as they are received from an API that supports streamed responses (like OpenAI's ChatGPT[https://chat.openai.com/]).
以下は、wire:stream
を使用して ChatGPT のようなインターフェースを実現する例です。Below is an example of using wire:stream
to accomplish a ChatGPT-like interface:
use Livewire\Component;
class ChatBot extends Component
{
public $prompt = '';
public $question = '';
public $answer = '';
function submitPrompt()
{
$this->question = $this->prompt;
$this->prompt = '';
$this->js('$wire.ask()');
}
function ask()
{
$this->answer = OpenAI::ask($this->question, function ($partial) {
$this->stream(to: 'answer', content: $partial); // [tl! highlight]
});
}
public function render()
{
return <<<'HTML'
<div>
<section>
<div>ChatBot</div>
@if ($question)
<article>
<hgroup>
<h3>User</h3>
<p>{{ $question }}</p>
</hgroup>
<hgroup>
<h3>ChatBot</h3>
<p wire:stream="answer">{{ $answer }}</p> <!-- [tl! highlight] -->
</hgroup>
</article>
@endif
</section>
<form wire:submit="submitPrompt">
<input wire:model="prompt" type="text" placeholder="Send a message" autofocus>
</form>
</div>
HTML;
}
}
上記の例で何が起こっているかは次のとおりです。Here's what's going on in the above example:
- ユーザーは「Send a message」というラベルのテキストフィールドに入力して、チャットボットに質問します。A user types into a text field labeled "Send a message" to ask the chat-bot a question.
- [Enter]キーを押します。They press the [Enter] key.
- ネットワークリクエストがサーバに送信され、メッセージが
$question
プロパティに設定され、$prompt
プロパティがクリアされます。A network request is sent to the server, sets the message to the$question
property, and clears the$prompt
property. - レスポンスがブラウザに送り返され、入力がクリアされます。
$this->js('...')
が呼び出されたため、ask()
メソッドを呼び出す新しいリクエストがサーバにトリガーされます。The response is sent back to the browser and the input is cleared. Because$this->js('...')
was called, a new request is triggered to the server calling theask()
method. ask()
メソッドは ChatBot API を呼び出し、コールバックの$partial
パラメータを介してストリームされたレスポンスの部分を受信します。Theask()
method calls on the ChatBot API and receives streamed response partials via the$partial
parameter in the callback.- 各
$partial
は、ページのwire:stream="answer"
要素にブラウザにストリームされ、回答が段階的に明らかになる様子をユーザーに示します。Each$partial
gets streamed to the browser into thewire:stream="answer"
element on the page, showing the answer progressively reveal itself to the user. - レスポンス全体が受信されると、Livewire リクエストが完了し、ユーザーは完全なレスポンスを受信します。When the entire response is received, the Livewire request finishes and the user receives the full response.
Replace vs. appendReplace vs. append
$this->stream()
を使用して要素にコンテンツをストリーミングする場合、Livewire にターゲット要素のコンテンツをストリームされたコンテンツで置き換えるか、既存のコンテンツに追加するかを指示できます。When streaming content to an element using $this->stream()
, you can tell Livewire to either replace the contents of the target element with the streamed contents or append them to the existing contents.
置き換えまたは追加のどちらも、シナリオに応じて望ましい場合があります。たとえば、チャットボットからのレスポンスをストリーミングする場合、通常は追加が望ましい(したがってデフォルトです)。ただし、カウントダウンのようなものを表示する場合は、置き換えの方が適しています。Replacing or appending can both be desirable depending on the scenario. For example, when streaming a response from a chatbot, typically appending is desired (and is therefore the default). However, when showing something like a count-down, replacing is more fitting.
replace:
パラメータにブール値を指定して $this->stream
に渡すことで、いずれかを構成できます。You can configure either by passing the replace:
parameter to $this->stream
with a boolean value:
// コンテンツを追加...
$this->stream(to: 'target', content: '...');
// コンテンツを置き換え...
$this->stream(to: 'target', content: '...', replace: true);
追加/置き換えは、.replace
モディファイアを追加または削除することにより、ターゲット要素レベルで指定することもできます。Append/replace can also be specified at the target element level by appending or removing the .replace
modifier:
// コンテンツを追加...
<div wire:stream="target">
// コンテンツを置き換え...
<div wire:stream.replace="target">