All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.yudichev.jiotty.connector.google.assistant.GoogleAssistantClientImpl Maven / Gradle / Ivy

package net.yudichev.jiotty.connector.google.assistant;

import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.assistant.embedded.v1alpha2.*;
import com.google.auth.Credentials;
import com.google.inject.BindingAnnotation;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.auth.MoreCallCredentials;
import io.grpc.stub.StreamObserver;
import net.yudichev.jiotty.common.inject.BaseLifecycleComponent;
import net.yudichev.jiotty.connector.google.common.ResolvedGoogleApiAuthSettings;
import net.yudichev.jiotty.connector.google.common.impl.GoogleAuthorization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.io.ByteArrayOutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static net.yudichev.jiotty.common.lang.MoreThrowables.asUnchecked;
import static net.yudichev.jiotty.common.lang.MoreThrowables.getAsUnchecked;
import static net.yudichev.jiotty.connector.google.common.impl.Bindings.Settings;

final class GoogleAssistantClientImpl extends BaseLifecycleComponent implements GoogleAssistantClient {
    private static final Logger logger = LoggerFactory.getLogger(GoogleAssistantClientImpl.class);

    private static final String SCOPE_ASSISTANT = "https://www.googleapis.com/auth/assistant-sdk-prototype";
    private static final String HOSTNAME = "embeddedassistant.googleapis.com";
    private final ResolvedGoogleApiAuthSettings settings;
    private final AudioOutConfig audioOutConfig;
    private final DialogStateIn dialogStateIn;
    private final DeviceConfig deviceConfig;

    private EmbeddedAssistantGrpc.EmbeddedAssistantStub stub;
    private ManagedChannel channel;

    @Inject
    GoogleAssistantClientImpl(@Settings ResolvedGoogleApiAuthSettings settings,
                              @Dependency AudioOutConfig audioOutConfig,
                              @Dependency DialogStateIn dialogStateIn,
                              @Dependency DeviceConfig deviceConfig) {
        this.settings = checkNotNull(settings);
        this.audioOutConfig = checkNotNull(audioOutConfig);
        this.dialogStateIn = checkNotNull(dialogStateIn);
        this.deviceConfig = checkNotNull(deviceConfig);
    }

    @Override
    public CompletableFuture assist(String phrase) {
        return whenStartedAndNotLifecycling(() -> {
            CompletableFuture result = new CompletableFuture<>();
            @SuppressWarnings("resource") ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            StreamObserver requestStreamObserver = stub.assist(new StreamObserver() {
                @Override
                public void onNext(AssistResponse assistResponse) {
                    logger.debug("assist response onNext: {}", assistResponse);
                    if (assistResponse.hasAudioOut()) {
                        asUnchecked(() -> assistResponse.getAudioOut().writeTo(outputStream));
                    }
                }

                @Override
                public void onError(Throwable throwable) {
                    logger.debug("assist response onError", throwable);
                    result.completeExceptionally(throwable);
                }

                @Override
                public void onCompleted() {
                    logger.debug("assist response onCompleted");
                    result.complete(outputStream.toByteArray());
                }
            });

            AssistRequest assistRequest = AssistRequest.newBuilder()
                    .setConfig(AssistConfig.newBuilder()
                            .setTextQuery(phrase)
                            .setAudioOutConfig(audioOutConfig)
                            .setDialogStateIn(dialogStateIn)
                            .setDeviceConfig(deviceConfig)
                            .build())
                    .build();

            logger.debug("Issuing assist request: {}", assistRequest);
            requestStreamObserver.onNext(assistRequest);

            return result;
        });
    }

    @Override
    protected void doStart() {
        Credentials credentials = GoogleAuthorization.builder()
                .setHttpTransport(getAsUnchecked(GoogleNetHttpTransport::newTrustedTransport))
                .setAuthDataStoreRootDir(settings.authDataStoreRootDir())
                .setApiName("assistant")
                .setCredentialsUrl(settings.credentialsUrl())
                .addRequiredScope(SCOPE_ASSISTANT)
                .withBrowser(settings.authorizationBrowser())
                .build()
                .getCredentials();

        channel = ManagedChannelBuilder.forTarget(HOSTNAME).build();
        stub = EmbeddedAssistantGrpc.newStub(channel)
                .withCallCredentials(MoreCallCredentials.from(credentials));
    }

    @Override
    protected void doStop() {
        if (channel != null) {
            boolean success = getAsUnchecked(() -> channel.shutdown().awaitTermination(5, TimeUnit.SECONDS));
            if (!success) {
                logger.warn("Were not able to shut down Assistant channel in 5 seconds");
            }
        }
    }

    @BindingAnnotation
    @Target({FIELD, PARAMETER, METHOD})
    @Retention(RUNTIME)
    @interface Dependency {
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy