

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

# Memulai aliran percakapan ke bot Amazon Lex V2
<a name="start-stream-conversation"></a>

Anda menggunakan [StartConversation](https://docs.aws.amazon.com/lexv2/latest/APIReference/API_runtime_StartConversation.html)operasi untuk memulai streaming antara pengguna dan bot Amazon Lex V2 di aplikasi Anda. `POST`Permintaan dari aplikasi membuat koneksi antara aplikasi Anda dan bot Amazon Lex V2. Ini memungkinkan aplikasi Anda dan bot untuk mulai bertukar informasi satu sama lain melalui acara.

**catatan**  
Saat menggunakan StartConversation, jika durasi audio ucapan melebihi nilai yang dikonfigurasi untuk`max-length-ms`, Amazon Lex V2 memotong audio pada durasi yang ditentukan.

`StartConversation`Operasi hanya didukung sebagai berikut SDKs: 
+ [AWS SDK for C\$1\$1](https://docs.aws.amazon.com/goto/SdkForCpp/runtime.lex.v2-2020-08-07/StartConversation)
+ [AWS SDK for Java V2](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lexruntimev2/LexRuntimeV2AsyncClient.html)
+ [AWS SDK untuk v3 JavaScript ](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-lex-runtime-v2/index.html#aws-sdkclient-lex-runtime-v2)
+ [AWS SDK for Ruby V3](https://docs.aws.amazon.com/goto/SdkForRubyV3/runtime.lex.v2-2020-08-07/StartConversation)

Acara pertama yang harus dikirim aplikasi Anda ke bot Amazon Lex V2 adalah [ConfigurationEvent](https://docs.aws.amazon.com/lexv2/latest/APIReference/API_runtime_ConfigurationEvent.html). Acara ini mencakup informasi seperti format tipe respons. Berikut ini adalah parameter yang dapat Anda gunakan dalam acara konfigurasi:
+ **responseContentType**— Menentukan apakah bot merespons input pengguna dengan teks atau ucapan.
+ **SessionState** — Informasi yang berkaitan dengan sesi streaming dengan bot seperti maksud yang telah ditentukan atau status dialog.
+ **WelcomeMessages** - Menentukan pesan selamat datang yang bermain untuk pengguna di awal percakapan mereka dengan bot. Pesan-pesan ini diputar sebelum pengguna memberikan masukan apa pun. Untuk mengaktifkan pesan selamat datang, Anda juga harus menentukan nilai untuk `sessionState` dan `dialogAction` parameter. 
+ **DisablePlayback** — Menentukan apakah bot harus menunggu isyarat dari klien sebelum mulai mendengarkan input pemanggil. Secara default, pemutaran diaktifkan, jadi nilai bidang ini adalah`false`.
+ **RequestAttributes** - Menyediakan informasi tambahan untuk permintaan.

Untuk informasi tentang cara menentukan nilai untuk parameter sebelumnya, lihat tipe [ConfigurationEvent](https://docs.aws.amazon.com/lexv2/latest/APIReference/API_runtime_ConfigurationEvent.html)data operasi. [StartConversation](https://docs.aws.amazon.com/lexv2/latest/APIReference/API_runtime_StartConversation.html)

Setiap aliran antara bot dan aplikasi Anda hanya dapat memiliki satu peristiwa konfigurasi. Setelah aplikasi Anda mengirim acara konfigurasi, bot dapat mengambil komunikasi tambahan dari aplikasi Anda.

Jika Anda telah menentukan bahwa pengguna Anda menggunakan audio untuk berkomunikasi dengan bot Amazon Lex V2, aplikasi Anda dapat mengirim peristiwa berikut ke bot selama percakapan itu:
+ [AudioInputEvent](https://docs.aws.amazon.com/lexv2/latest/APIReference/API_runtime_AudioInputEvent.html)- Berisi potongan audio yang memiliki ukuran maksimum 320 byte. Aplikasi Anda harus menggunakan beberapa peristiwa input audio untuk mengirim pesan dari server ke bot. Setiap peristiwa input audio dalam aliran harus memiliki format audio yang sama.
+ [DTMFInputEvent](https://docs.aws.amazon.com/lexv2/latest/APIReference/API_runtime_DTMFInputEvent.html) - Mengirim input DTMF ke bot. Setiap penekanan tombol DTMF sesuai dengan satu peristiwa.
+ [PlaybackCompletionEvent](https://docs.aws.amazon.com/lexv2/latest/APIReference/API_runtime_PlaybackCompletionEvent.html)— Menginformasikan server bahwa respons dari input pengguna telah diputar kembali kepada mereka. Anda harus menggunakan acara penyelesaian pemutaran jika Anda mengirim respons audio ke pengguna. Jika `disablePlayback` peristiwa konfigurasi Anda`true`, Anda tidak dapat menggunakan fitur ini.
+ [DisconnectionEvent](https://docs.aws.amazon.com/lexv2/latest/APIReference/API_runtime_DTMFInputEvent.html)— Menginformasikan bot bahwa pengguna telah terputus dari percakapan.

Jika Anda telah menentukan bahwa pengguna menggunakan teks untuk berkomunikasi dengan bot, aplikasi Anda dapat mengirim peristiwa berikut ke bot selama percakapan itu:
+ [TextInputEvent](https://docs.aws.amazon.com/lexv2/latest/APIReference/API_runtime_TextInputEvent.html)— Teks yang dikirim dari aplikasi Anda ke bot. Anda dapat memiliki hingga 512 karakter dalam acara input teks.
+ [PlaybackCompletionEvent](https://docs.aws.amazon.com/lexv2/latest/APIReference/API_runtime_PlaybackCompletionEvent.html)— Menginformasikan server bahwa respons dari input pengguna telah diputar kembali kepada mereka. Anda harus menggunakan acara ini jika Anda memutar audio kembali ke pengguna. Jika `disablePlayback` peristiwa konfigurasi Anda`true`, Anda tidak dapat menggunakan fitur ini.
+ [DisconnectionEvent](https://docs.aws.amazon.com/lexv2/latest/APIReference/API_runtime_DTMFInputEvent.html)— Menginformasikan bot bahwa pengguna telah terputus dari percakapan.

Anda harus menyandikan setiap acara yang Anda kirim ke bot Amazon Lex V2 dalam format yang benar. Untuk informasi selengkapnya, lihat [Pengkodean aliran acara](event-stream-encoding.md).

Setiap acara memiliki ID acara. Untuk membantu memecahkan masalah apa pun yang mungkin terjadi di aliran, tetapkan ID peristiwa unik untuk setiap peristiwa masukan. Anda kemudian dapat memecahkan masalah kegagalan pemrosesan apa pun dengan bot.

Amazon Lex V2 juga menggunakan stempel waktu untuk setiap acara. Anda dapat menggunakan stempel waktu ini selain ID acara untuk membantu memecahkan masalah transmisi jaringan apa pun.

Selama percakapan antara pengguna dan bot Amazon Lex V2, bot dapat mengirim peristiwa keluar berikut sebagai tanggapan terhadap pengguna:
+ [IntentResultEvent](https://docs.aws.amazon.com/lexv2/latest/APIReference/API_runtime_IntentResultEvent.html)— Berisi maksud bahwa Amazon Lex V2 ditentukan dari ucapan pengguna. Setiap peristiwa hasil internal meliputi:
  + **InputMode** — Jenis ucapan pengguna. Nilai yang valid adalah `Speech`, `DTMF`, atau `Text`.
  + **interpretasi** — Interpretasi yang ditentukan Amazon Lex V2 dari ucapan pengguna.
  + **requestAttributes** - Jika Anda belum memodifikasi atribut permintaan dengan menggunakan fungsi lambda, ini adalah atribut yang sama yang diteruskan pada awal percakapan.
  + **SessionID** — Pengenal sesi yang digunakan untuk percakapan.
  + **SessionState** - Keadaan sesi pengguna dengan Amazon Lex V2.
+ [TranscriptEvent](https://docs.aws.amazon.com/lexv2/latest/APIReference/API_runtime_TranscriptEvent.html)— Jika pengguna memberikan masukan ke aplikasi Anda, acara ini berisi transkrip ucapan pengguna ke bot. Aplikasi Anda tidak menerima `TranscriptEvent` jika tidak ada input pengguna.

  Nilai peristiwa transkrip yang dikirim ke aplikasi Anda tergantung pada apakah Anda telah menentukan audio (ucapan dan DMTF) atau teks sebagai mode percakapan:
  + Transkrip input ucapan — Jika pengguna berbicara dengan bot, peristiwa transkrip adalah transkripsi audio pengguna. Ini adalah transkrip dari semua pidato dari saat pengguna mulai berbicara hingga saat mereka berhenti berbicara.
  + Transkrip input DTMF - Jika pengguna mengetik pada keypad, peristiwa transkrip berisi semua digit yang ditekan pengguna dalam input mereka.
  + Transkrip input teks - Jika pengguna memberikan input teks, peristiwa transkrip berisi semua teks dalam input pengguna.
+ [TextResponseEvent](https://docs.aws.amazon.com/lexv2/latest/APIReference/API_runtime_TextResponseEvent.html)— Berisi respons bot dalam format teks. Respons teks dikembalikan secara default. Jika Anda telah mengonfigurasi Amazon Lex V2 untuk mengembalikan respons audio, teks ini digunakan untuk menghasilkan respons audio. Setiap peristiwa respons teks berisi array objek pesan yang bot kembali ke pengguna.
+ [AudioResponseEvent](https://docs.aws.amazon.com/lexv2/latest/APIReference/API_runtime_AudioResponseEvent.html)— Berisi respons audio yang disintesis dari teks yang dihasilkan di. `TextResponseEvent` Untuk menerima peristiwa respons audio, Anda harus mengonfigurasi Amazon Lex V2 untuk memberikan respons audio. Semua acara respons audio memiliki format audio yang sama. Setiap acara berisi potongan audio tidak lebih dari 100 byte. Amazon Lex V2 mengirimkan potongan audio kosong dengan `bytes` bidang yang disetel `null` untuk menunjukkan bahwa akhir peristiwa respons audio ke aplikasi Anda.
+ [PlaybackInterruptionEvent](https://docs.aws.amazon.com/lexv2/latest/APIReference/API_runtime_PlaybackInterruptionEvent.html)— Ketika pengguna mengganggu respons yang dikirim bot ke aplikasi Anda, Amazon Lex V2 memicu acara ini untuk menghentikan pemutaran respons.
+ [HeartbeatEvent](https://docs.aws.amazon.com/lexv2/latest/APIReference/API_runtime_HeartbeatEvent.html)— Amazon Lex V2 mengirimkan acara ini kembali secara berkala untuk menjaga koneksi antara aplikasi Anda dan bot dari waktu habis.

## Urutan waktu acara untuk percakapan audio saat menggunakan bot Amazon Lex V2
<a name="audio-conversation-sequence"></a>

Diagram berikut menunjukkan percakapan audio streaming antara pengguna dan bot Amazon Lex V2. Aplikasi terus mengalirkan audio ke bot, dan bot mencari input pengguna dari audio. Dalam contoh ini, pengguna dan bot menggunakan ucapan untuk berkomunikasi. Setiap diagram sesuai dengan ucapan pengguna dan respons bot terhadap ucapan itu.

Diagram berikut menunjukkan awal percakapan antara aplikasi dan bot. Aliran dimulai pada waktu nol (t0). 

![\[Timeline showing audio input events from application and various response events from bot during a conversation.\]](http://docs.aws.amazon.com/id_id/lexv2/latest/dg/images/Streaming-Page-1.png)


Daftar berikut menjelaskan peristiwa diagram sebelumnya.
+ t0: Aplikasi mengirimkan peristiwa konfigurasi ke bot untuk memulai streaming.
+ t1: Aplikasi mengalirkan data audio. Data ini dipecah menjadi serangkaian peristiwa masukan dari aplikasi.
+ t2: Untuk *ucapan pengguna 1*, bot mendeteksi peristiwa input audio saat pengguna mulai berbicara.
+ t2: Saat pengguna berbicara, bot mengirimkan acara detak jantung untuk mempertahankan koneksi. Ini mengirimkan peristiwa ini sebentar-sebentar untuk memastikan koneksi tidak habis waktu.
+ t3: Bot mendeteksi akhir ucapan pengguna.
+ t4: Bot mengirimkan kembali peristiwa transkrip yang berisi transkrip pidato pengguna ke aplikasi. Ini adalah awal dari *respons Bot terhadap ucapan pengguna* 1.
+ t5: Bot mengirimkan peristiwa hasil maksud untuk menunjukkan tindakan yang ingin dilakukan pengguna.
+ t6: Bot mulai memberikan responsnya sebagai teks dalam acara respons teks.
+ t7: Bot mengirimkan serangkaian peristiwa respons audio ke aplikasi untuk diputar bagi pengguna.
+ t8: Bot mengirimkan acara detak jantung lain untuk menjaga koneksi sebentar-sebentar.

Diagram berikut merupakan kelanjutan dari diagram sebelumnya. Ini menunjukkan aplikasi mengirim acara penyelesaian pemutaran ke bot untuk menunjukkan bahwa ia telah berhenti memutar respons audio untuk pengguna. Aplikasi ini memutar kembali *respons Bot terhadap ucapan pengguna 1* kepada pengguna. Pengguna merespons *respons Bot terhadap ucapan pengguna 1 dengan ucapan* *Pengguna* 2.

![\[Timeline of audio input events from user and response events from bot, showing interaction flow.\]](http://docs.aws.amazon.com/id_id/lexv2/latest/dg/images/Streaming-Page-2.png)


Daftar berikut menjelaskan peristiwa diagram sebelumnya:
+ t10: Aplikasi mengirimkan acara penyelesaian pemutaran untuk menunjukkan bahwa ia telah selesai memutar pesan bot kepada pengguna.
+ t11: Aplikasi mengirimkan respons pengguna kembali ke bot sebagai *ucapan Pengguna* 2.
+ t12: *Untuk respons Bot terhadap ucapan pengguna 2*, bot menunggu pengguna berhenti berbicara dan kemudian mulai memberikan respons audio.
+ *t13: Sementara *bot mengirimkan respons Bot ke ucapan pengguna 2* ke aplikasi, bot mendeteksi awal ucapan Pengguna 3.* Bot menghentikan *respons Bot terhadap ucapan pengguna 2 dan mengirimkan peristiwa gangguan* pemutaran.
+ t14: Bot mengirimkan peristiwa gangguan pemutaran ke aplikasi untuk memberi sinyal bahwa pengguna telah mengganggu prompt.

Diagram berikut menunjukkan *respons Bot terhadap ucapan pengguna 3*, dan bahwa percakapan berlanjut setelah bot merespons ucapan pengguna.

![\[Diagram showing events flow between application, bot, and user utterances over time.\]](http://docs.aws.amazon.com/id_id/lexv2/latest/dg/images/Streaming-Page-3.png)


# Menggunakan API untuk memulai percakapan streaming
<a name="using-streaming-api"></a>

Saat Anda memulai streaming ke bot Amazon Lex V2, Anda menyelesaikan tugas-tugas berikut:

1. Buat koneksi awal ke server.

1. Konfigurasikan kredenal keamanan dan detail bot. Detail bot mencakup apakah bot mengambil DTMF dan input audio, atau input teks.

1. Kirim acara ke server. Peristiwa ini adalah data teks atau data audio dari pengguna.

1. Proses peristiwa yang dikirim dari server. Pada langkah ini, Anda menentukan apakah output bot disajikan kepada pengguna sebagai teks atau ucapan.

Contoh kode berikut menginisialisasi percakapan streaming dengan bot Amazon Lex V2 dan mesin lokal Anda. Anda dapat memodifikasi kode untuk memenuhi kebutuhan Anda.

Kode berikut adalah contoh permintaan menggunakan AWS SDK untuk Java untuk memulai koneksi ke bot dan mengkonfigurasi rincian bot dan kredensialnya.

```
package com.lex.streaming.sample;

import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.lexruntimev2.LexRuntimeV2AsyncClient;
import software.amazon.awssdk.services.lexruntimev2.model.ConversationMode;
import software.amazon.awssdk.services.lexruntimev2.model.StartConversationRequest;

import java.net.URISyntaxException;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

/**
 * The following code creates a connection with the Amazon Lex bot and configures the bot details and credentials.  
 * Prerequisite: To use this example, you must be familiar with the Reactive streams programming model.
 * For more information, see
 * https://github.com/reactive-streams/reactive-streams-jvm.
 * This example uses AWS SDK for Java for Amazon Lex V2.
 * <p>
 * The following sample application interacts with an Amazon Lex bot with the streaming API. It uses the Audio
 * conversation mode to return audio responses to the user's input.
 * <p>
 * The code in this example accomplishes the following:
 * <p>
 * 1. Configure details about the conversation between the user and the Amazon Lex bot. These details include the conversation mode and the specific bot the user is speaking with.
 * 2. Create an events publisher that passes the audio events to the Amazon Lex bot after you establish the connection. The code we provide in this example tells your computer to pick up the audio from
 * your microphone and send that audio data to Amazon Lex.
 * 3. Create a response handler that handles the audio responses from the Amazon Lex bot and plays back the audio to you.
 */
public class LexBidirectionalStreamingExample {

    public static void main(String[] args) throws URISyntaxException, InterruptedException {
        String botId = "";
        String botAliasId = "";
        String localeId = "";
        String accessKey = "";
        String secretKey = "";
        String sessionId = UUID.randomUUID().toString();
        Region region = Region.region_name; // Choose an AWS Region where the Amazon Lex Streaming API is available.

        AwsCredentialsProvider awsCredentialsProvider = StaticCredentialsProvider
                .create(AwsBasicCredentials.create(accessKey, secretKey));

        // Create a new SDK client. You need to use an asynchronous client.
        System.out.println("step 1: creating a new Lex SDK client");
        LexRuntimeV2AsyncClient lexRuntimeServiceClient = LexRuntimeV2AsyncClient.builder()
                .region(region)
                .credentialsProvider(awsCredentialsProvider)
                .build();


        // Configure the bot, alias and locale that you'll use to have a conversation.
        System.out.println("step 2: configuring bot details");
        StartConversationRequest.Builder startConversationRequestBuilder = StartConversationRequest.builder()
                .botId(botId)
                .botAliasId(botAliasId)
                .localeId(localeId);

        // Configure the conversation mode of the bot. By default, the
        // conversation mode is audio.
        System.out.println("step 3: choosing conversation mode");
        startConversationRequestBuilder = startConversationRequestBuilder.conversationMode(ConversationMode.AUDIO);

        // Assign a unique identifier for the conversation.
        System.out.println("step 4: choosing a unique conversation identifier");
        startConversationRequestBuilder = startConversationRequestBuilder.sessionId(sessionId);

        // Start the initial request.
        StartConversationRequest startConversationRequest = startConversationRequestBuilder.build();

        // Create a stream of audio data to the Amazon Lex bot. The stream will start after the connection is established with the bot.
        EventsPublisher eventsPublisher = new EventsPublisher();

        // Create a class to handle responses from bot. After the server processes the user data you've streamed, the server responds
        // on another stream.
        BotResponseHandler botResponseHandler = new BotResponseHandler(eventsPublisher);

        // Start a connection and pass in the publisher that streams the audio and process the responses from the bot.
        System.out.println("step 5: starting the conversation ...");
        CompletableFuture<Void> conversation = lexRuntimeServiceClient.startConversation(
                startConversationRequest,
                eventsPublisher,
                botResponseHandler);

        // Wait until the conversation finishes. The conversation finishes if the dialog state reaches the "Closed" state.
        // The client stops the connection. If an exception occurs during the conversation, the
        // client sends a disconnection event.
        conversation.whenComplete((result, exception) -> {
            if (exception != null) {
                eventsPublisher.disconnect();
            }
        });

        // The conversation finishes when the dialog state is closed and last prompt has been played.
        while (!botResponseHandler.isConversationComplete()) {
            Thread.sleep(100);
        }

        // Randomly sleep for 100 milliseconds to prevent JVM from exiting.
        // You won't need this in your production code because your JVM is
        // likely to always run.
        // When the conversation finishes, the following code block stops publishing more data and informs the Amazon Lex bot that there is no more data to send.
        if (botResponseHandler.isConversationComplete()) {
            System.out.println("conversation is complete.");
            eventsPublisher.stop();
        }
    }
}
```

Kode berikut adalah contoh permintaan menggunakan AWS SDK untuk Java untuk mengirim acara ke bot. Kode dalam contoh ini menggunakan mikrofon di komputer Anda untuk mengirim acara audio.

```
package com.lex.streaming.sample;

import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import software.amazon.awssdk.services.lexruntimev2.model.StartConversationRequestEventStream;

/**
 * You use the Events publisher to send events to the Amazon Lex bot. When you establish a connection, the bot uses the
 * subscribe() method and enables the events publisher starts sending events to
 * your computer. The bot uses the "request" method of the subscription to make more requests. For more information on the request method, see https://github.com/reactive-streams/reactive-streams-jvm. 
 */
public class EventsPublisher implements Publisher<StartConversationRequestEventStream> {

    private AudioEventsSubscription audioEventsSubscription;

    @Override
    public void subscribe(Subscriber<? super StartConversationRequestEventStream> subscriber) {
        if (audioEventsSubscription == null) {

            audioEventsSubscription = new AudioEventsSubscription(subscriber);
            subscriber.onSubscribe(audioEventsSubscription);

        } else {
            throw new IllegalStateException("received unexpected subscription request");
        }
    }

    public void disconnect() {
        if (audioEventsSubscription != null) {
            audioEventsSubscription.disconnect();
        }
    }

    public void stop() {
        if (audioEventsSubscription != null) {
            audioEventsSubscription.stop();
        }
    }

    public void playbackFinished() {
        if (audioEventsSubscription != null) {
            audioEventsSubscription.playbackFinished();
        }
    }
}
```

Kode berikut adalah contoh permintaan menggunakan AWS SDK untuk Java untuk menangani tanggapan dari bot. Kode dalam contoh ini mengonfigurasi Amazon Lex V2 untuk memutar respons audio kembali kepada Anda.

```
package com.lex.streaming.sample;

import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.player.advanced.AdvancedPlayer;
import javazoom.jl.player.advanced.PlaybackEvent;
import javazoom.jl.player.advanced.PlaybackListener;
import software.amazon.awssdk.core.async.SdkPublisher;
import software.amazon.awssdk.services.lexruntimev2.model.AudioResponseEvent;
import software.amazon.awssdk.services.lexruntimev2.model.DialogActionType;
import software.amazon.awssdk.services.lexruntimev2.model.IntentResultEvent;
import software.amazon.awssdk.services.lexruntimev2.model.PlaybackInterruptionEvent;
import software.amazon.awssdk.services.lexruntimev2.model.StartConversationResponse;
import software.amazon.awssdk.services.lexruntimev2.model.StartConversationResponseEventStream;
import software.amazon.awssdk.services.lexruntimev2.model.StartConversationResponseHandler;
import software.amazon.awssdk.services.lexruntimev2.model.TextResponseEvent;
import software.amazon.awssdk.services.lexruntimev2.model.TranscriptEvent;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.concurrent.CompletableFuture;

/**
 * The following class is responsible for processing events sent from the Amazon Lex bot. The bot sends multiple audio events,
 * so the following code concatenates those audio events and uses a publicly available Java audio player to play out the message to
 * the user.
 */
public class BotResponseHandler implements StartConversationResponseHandler {

    private final EventsPublisher eventsPublisher;

    private boolean lastBotResponsePlayedBack;
    private boolean isDialogStateClosed;
    private AudioResponse audioResponse;


    public BotResponseHandler(EventsPublisher eventsPublisher) {
        this.eventsPublisher = eventsPublisher;
        this.lastBotResponsePlayedBack = false;// At the start, we have not played back last response from bot.
        this.isDialogStateClosed = false; // At the start, the dialog state is open.
    }

    @Override
    public void responseReceived(StartConversationResponse startConversationResponse) {
        System.out.println("successfully established the connection with server. request id:" + startConversationResponse.responseMetadata().requestId()); // would have 2XX, request id.
    }

    @Override
    public void onEventStream(SdkPublisher<StartConversationResponseEventStream> sdkPublisher) {

        sdkPublisher.subscribe(event -> {
            if (event instanceof PlaybackInterruptionEvent) {
                handle((PlaybackInterruptionEvent) event);
            } else if (event instanceof TranscriptEvent) {
                handle((TranscriptEvent) event);
            } else if (event instanceof IntentResultEvent) {
                handle((IntentResultEvent) event);
            } else if (event instanceof TextResponseEvent) {
                handle((TextResponseEvent) event);
            } else if (event instanceof AudioResponseEvent) {
                handle((AudioResponseEvent) event);
            }
        });
    }

    @Override
    public void exceptionOccurred(Throwable throwable) {
        System.err.println("got an exception:" + throwable);
    }

    @Override
    public void complete() {
        System.out.println("on complete");
    }

    private void handle(PlaybackInterruptionEvent event) {
        System.out.println("Got a PlaybackInterruptionEvent: " + event);
    }

    private void handle(TranscriptEvent event) {
        System.out.println("Got a TranscriptEvent: " + event);
    }


    private void handle(IntentResultEvent event) {
        System.out.println("Got an IntentResultEvent: " + event);
        isDialogStateClosed = DialogActionType.CLOSE.equals(event.sessionState().dialogAction().type());
    }

    private void handle(TextResponseEvent event) {
        System.out.println("Got an TextResponseEvent: " + event);
        event.messages().forEach(message -> {
            System.out.println("Message content type:" + message.contentType());
            System.out.println("Message content:" + message.content());
        });
    }

    private void handle(AudioResponseEvent event) {//Synthesize speech
        // System.out.println("Got a AudioResponseEvent: " + event);
        if (audioResponse == null) {
            audioResponse = new AudioResponse();
            //Start an audio player in a different thread.
            CompletableFuture.runAsync(() -> {
                try {
                    AdvancedPlayer audioPlayer = new AdvancedPlayer(audioResponse);

                    audioPlayer.setPlayBackListener(new PlaybackListener() {
                        @Override
                        public void playbackFinished(PlaybackEvent evt) {
                            super.playbackFinished(evt);

                            // Inform the Amazon Lex bot that the playback has finished.
                            eventsPublisher.playbackFinished();
                            if (isDialogStateClosed) {
                                lastBotResponsePlayedBack = true;
                            }
                        }
                    });
                    audioPlayer.play();
                } catch (JavaLayerException e) {
                    throw new RuntimeException("got an exception when using audio player", e);
                }
            });
        }

        if (event.audioChunk() != null) {
            audioResponse.write(event.audioChunk().asByteArray());
        } else {
            // The audio audio prompt has ended when the audio response has no
            // audio bytes.
            try {
                audioResponse.close();
                audioResponse = null;  // Prepare for the next audio prompt.
            } catch (IOException e) {
                throw new UncheckedIOException("got an exception when closing the audio response", e);
            }
        }
    }

    // The conversation with the Amazon Lex bot is complete when the bot marks the Dialog as DialogActionType.CLOSE
    // and any prompt playback is finished. For more information, see
    // https://docs.aws.amazon.com/lexv2/latest/dg/API_runtime_DialogAction.html.
    public boolean isConversationComplete() {
        return isDialogStateClosed && lastBotResponsePlayedBack;
    }

}
```

Untuk mengonfigurasi bot untuk merespons peristiwa input dengan audio, Anda harus terlebih dahulu berlangganan acara audio dari Amazon Lex V2 dan kemudian mengonfigurasi bot untuk memberikan respons audio ke peristiwa input dari pengguna.

Kode berikut adalah AWS SDK untuk Java contoh untuk berlangganan acara audio dari Amazon Lex V2.

```
package com.lex.streaming.sample;

import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.services.lexruntimev2.model.AudioInputEvent;
import software.amazon.awssdk.services.lexruntimev2.model.ConfigurationEvent;
import software.amazon.awssdk.services.lexruntimev2.model.DisconnectionEvent;
import software.amazon.awssdk.services.lexruntimev2.model.PlaybackCompletionEvent;
import software.amazon.awssdk.services.lexruntimev2.model.StartConversationRequestEventStream;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.TargetDataLine;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicLong;

public class AudioEventsSubscription implements Subscription {
    private static final AudioFormat MIC_FORMAT = new AudioFormat(8000, 16, 1, true, false);
    private static final String AUDIO_CONTENT_TYPE = "audio/lpcm; sample-rate=8000; sample-size-bits=16; channel-count=1; is-big-endian=false";
    //private static final String RESPONSE_TYPE = "audio/pcm; sample-rate=8000";
    private static final String RESPONSE_TYPE = "audio/mpeg";
    private static final int BYTES_IN_AUDIO_CHUNK = 320;
    private static final AtomicLong eventIdGenerator = new AtomicLong(0);

    private final AudioInputStream audioInputStream;
    private final Subscriber<? super StartConversationRequestEventStream> subscriber;
    private final EventWriter eventWriter;
    private CompletableFuture eventWriterFuture;


    public AudioEventsSubscription(Subscriber<? super StartConversationRequestEventStream> subscriber) {
        this.audioInputStream = getMicStream();
        this.subscriber = subscriber;
        this.eventWriter = new EventWriter(subscriber, audioInputStream);
        configureConversation();
    }

    private AudioInputStream getMicStream() {
        try {
            DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, MIC_FORMAT);
            TargetDataLine targetDataLine = (TargetDataLine) AudioSystem.getLine(dataLineInfo);

            targetDataLine.open(MIC_FORMAT);
            targetDataLine.start();

            return new AudioInputStream(targetDataLine);
        } catch (LineUnavailableException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void request(long demand) {
        // If a thread to write events has not been started, start it.
        if (eventWriterFuture == null) {
            eventWriterFuture = CompletableFuture.runAsync(eventWriter);
        }
        eventWriter.addDemand(demand);
    }

    @Override
    public void cancel() {
        subscriber.onError(new RuntimeException("stream was cancelled"));
        try {
            audioInputStream.close();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    public void configureConversation() {
        String eventId = "ConfigurationEvent-" + String.valueOf(eventIdGenerator.incrementAndGet());

        ConfigurationEvent configurationEvent = StartConversationRequestEventStream
                .configurationEventBuilder()
                .eventId(eventId)
                .clientTimestampMillis(System.currentTimeMillis())
                .responseContentType(RESPONSE_TYPE)
                .build();

        System.out.println("writing config event");
        eventWriter.writeConfigurationEvent(configurationEvent);
    }

    public void disconnect() {

        String eventId = "DisconnectionEvent-" + String.valueOf(eventIdGenerator.incrementAndGet());

        DisconnectionEvent disconnectionEvent = StartConversationRequestEventStream
                .disconnectionEventBuilder()
                .eventId(eventId)
                .clientTimestampMillis(System.currentTimeMillis())
                .build();

        eventWriter.writeDisconnectEvent(disconnectionEvent);

        try {
            audioInputStream.close();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }

    }
    //Notify the subscriber that we've finished.
    public void stop() {
        subscriber.onComplete();
    }

    public void playbackFinished() {
        String eventId = "PlaybackCompletion-" + String.valueOf(eventIdGenerator.incrementAndGet());

        PlaybackCompletionEvent playbackCompletionEvent = StartConversationRequestEventStream
                .playbackCompletionEventBuilder()
                .eventId(eventId)
                .clientTimestampMillis(System.currentTimeMillis())
                .build();

        eventWriter.writePlaybackFinishedEvent(playbackCompletionEvent);
    }

    private static class EventWriter implements Runnable {
        private final BlockingQueue<StartConversationRequestEventStream> eventQueue;
        private final AudioInputStream audioInputStream;
        private final AtomicLong demand;
        private final Subscriber subscriber;

        private boolean conversationConfigured;

        public EventWriter(Subscriber subscriber, AudioInputStream audioInputStream) {
            this.eventQueue = new LinkedBlockingQueue<>();

            this.demand = new AtomicLong(0);
            this.subscriber = subscriber;
            this.audioInputStream = audioInputStream;
        }

        public void writeConfigurationEvent(ConfigurationEvent configurationEvent) {
            eventQueue.add(configurationEvent);
        }

        public void writeDisconnectEvent(DisconnectionEvent disconnectionEvent) {
            eventQueue.add(disconnectionEvent);
        }

        public void writePlaybackFinishedEvent(PlaybackCompletionEvent playbackCompletionEvent) {
            eventQueue.add(playbackCompletionEvent);
        }

        void addDemand(long l) {
            this.demand.addAndGet(l);
        }

        @Override
        public void run() {
            try {

                while (true) {
                    long currentDemand = demand.get();

                    if (currentDemand > 0) {
                        // Try to read from queue of events.
                        // If nothing is in queue at this point, read the audio events directly from audio stream.
                        for (long i = 0; i < currentDemand; i++) {

                            if (eventQueue.peek() != null) {
                                subscriber.onNext(eventQueue.take());
                                demand.decrementAndGet();
                            } else {
                                writeAudioEvent();
                            }
                        }
                    }
                }
            } catch (InterruptedException e) {
                throw new RuntimeException("interrupted when reading data to be sent to server");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        private void writeAudioEvent() {
            byte[] bytes = new byte[BYTES_IN_AUDIO_CHUNK];

            int numBytesRead = 0;
            try {
                numBytesRead = audioInputStream.read(bytes);
                if (numBytesRead != -1) {
                    byte[] byteArrayCopy = Arrays.copyOf(bytes, numBytesRead);

                    String eventId = "AudioEvent-" + String.valueOf(eventIdGenerator.incrementAndGet());

                    AudioInputEvent audioInputEvent = StartConversationRequestEventStream
                            .audioInputEventBuilder()
                            .audioChunk(SdkBytes.fromByteBuffer(ByteBuffer.wrap(byteArrayCopy)))
                            .contentType(AUDIO_CONTENT_TYPE)
                            .clientTimestampMillis(System.currentTimeMillis())
                            .eventId(eventId).build();

                    //System.out.println("sending audio event:" + audioInputEvent);
                    subscriber.onNext(audioInputEvent);
                    demand.decrementAndGet();
                    //System.out.println("sent audio event:" + audioInputEvent);
                } else {
                    subscriber.onComplete();
                    System.out.println("audio stream has ended");
                }

            } catch (IOException e) {
                System.out.println("got an exception when reading from audio stream");
                System.err.println(e);
                subscriber.onError(e);
            }
        }
    }
}
```

 AWS SDK untuk Java Contoh berikut mengonfigurasi bot Amazon Lex V2 untuk memberikan respons audio terhadap peristiwa masukan.

```
package com.lex.streaming.sample;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.Optional;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

public class AudioResponse extends InputStream{

    // Used to convert byte, which is signed in Java, to positive integer (unsigned)
    private static final int UNSIGNED_BYTE_MASK = 0xFF;
    private static final long POLL_INTERVAL_MS = 10;

    private final LinkedBlockingQueue<Integer> byteQueue = new LinkedBlockingQueue<>();

    private volatile boolean closed;

    @Override
    public int read() throws IOException {
        try {
            Optional<Integer> maybeInt;
            while (true) {
                maybeInt = Optional.ofNullable(this.byteQueue.poll(POLL_INTERVAL_MS, TimeUnit.MILLISECONDS));

                // If we get an integer from the queue, return it.
                if (maybeInt.isPresent()) {
                    return maybeInt.get();
                }

                // If the stream is closed and there is nothing queued up, return -1.
                if (this.closed) {
                    return -1;
                }
            }
        } catch (InterruptedException e) {
            throw new IOException(e);
        }
    }

    /**
     * Writes data into the stream to be offered on future read() calls.
     */
    public void write(byte[] byteArray) {
        // Don't write into the stream if it is already closed.
        if (this.closed) {
            throw new UncheckedIOException(new IOException("Stream already closed when attempting to write into it."));
        }

        for (byte b : byteArray) {
            this.byteQueue.add(b & UNSIGNED_BYTE_MASK);
        }
    }

    @Override
    public void close() throws IOException {
        this.closed = true;
        super.close();
    }
}
```