/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.android.textclassifier; import android.content.res.AssetFileDescriptor; import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.Nullable; /** * Java wrapper for ActionsSuggestions native library interface. This library is used to suggest * actions and replies in a given conversation. * * @hide */ public final class ActionsSuggestionsModel implements AutoCloseable { private final AtomicBoolean isClosed = new AtomicBoolean(false); static { System.loadLibrary("textclassifier"); } private long actionsModelPtr; /** * Creates a new instance of Actions predictor, using the provided model image, given as a file * descriptor. */ public ActionsSuggestionsModel(int fileDescriptor, @Nullable byte[] serializedPreconditions) { actionsModelPtr = nativeNewActionsModel(fileDescriptor, serializedPreconditions); if (actionsModelPtr == 0L) { throw new IllegalArgumentException("Couldn't initialize actions model from file descriptor."); } } public ActionsSuggestionsModel(int fileDescriptor) { this(fileDescriptor, /* serializedPreconditions= */ null); } /** * Creates a new instance of Actions predictor, using the provided model image, given as a file * path. */ public ActionsSuggestionsModel(String path, @Nullable byte[] serializedPreconditions) { actionsModelPtr = nativeNewActionsModelFromPath(path, serializedPreconditions); if (actionsModelPtr == 0L) { throw new IllegalArgumentException("Couldn't initialize actions model from given file."); } } public ActionsSuggestionsModel(String path) { this(path, /* serializedPreconditions= */ null); } /** * Creates a new instance of Actions predictor, using the provided model image, given as an {@link * AssetFileDescriptor}). */ public ActionsSuggestionsModel( AssetFileDescriptor assetFileDescriptor, @Nullable byte[] serializedPreconditions) { actionsModelPtr = nativeNewActionsModelWithOffset( assetFileDescriptor.getParcelFileDescriptor().getFd(), assetFileDescriptor.getStartOffset(), assetFileDescriptor.getLength(), serializedPreconditions); if (actionsModelPtr == 0L) { throw new IllegalArgumentException("Couldn't initialize actions model from file descriptor."); } } public ActionsSuggestionsModel(AssetFileDescriptor assetFileDescriptor) { this(assetFileDescriptor, /* serializedPreconditions= */ null); } /** Suggests actions / replies to the given conversation. */ public ActionSuggestions suggestActions( Conversation conversation, ActionSuggestionOptions options, AnnotatorModel annotator) { return nativeSuggestActions( actionsModelPtr, conversation, options, (annotator != null ? annotator.getNativeAnnotatorPointer() : 0), /* appContext= */ null, /* deviceLocales= */ null, /* generateAndroidIntents= */ false); } public ActionSuggestions suggestActionsWithIntents( Conversation conversation, ActionSuggestionOptions options, Object appContext, String deviceLocales, AnnotatorModel annotator) { return nativeSuggestActions( actionsModelPtr, conversation, options, (annotator != null ? annotator.getNativeAnnotatorPointer() : 0), appContext, deviceLocales, /* generateAndroidIntents= */ true); } /** Frees up the allocated memory. */ @Override public void close() { if (isClosed.compareAndSet(false, true)) { nativeCloseActionsModel(actionsModelPtr); actionsModelPtr = 0L; } } @Override protected void finalize() throws Throwable { try { close(); } finally { super.finalize(); } } /** Returns a comma separated list of locales supported by the model as BCP 47 tags. */ public static String getLocales(int fd) { return nativeGetLocales(fd); } /** Returns a comma separated list of locales supported by the model as BCP 47 tags. */ public static String getLocales(AssetFileDescriptor assetFileDescriptor) { return nativeGetLocalesWithOffset( assetFileDescriptor.getParcelFileDescriptor().getFd(), assetFileDescriptor.getStartOffset(), assetFileDescriptor.getLength()); } /** Returns the version of the model. */ public static int getVersion(int fd) { return nativeGetVersion(fd); } /** Returns the version of the model. */ public static int getVersion(AssetFileDescriptor assetFileDescriptor) { return nativeGetVersionWithOffset( assetFileDescriptor.getParcelFileDescriptor().getFd(), assetFileDescriptor.getStartOffset(), assetFileDescriptor.getLength()); } /** Returns the name of the model. */ public static String getName(int fd) { return nativeGetName(fd); } /** Returns the name of the model. */ public static String getName(AssetFileDescriptor assetFileDescriptor) { return nativeGetNameWithOffset( assetFileDescriptor.getParcelFileDescriptor().getFd(), assetFileDescriptor.getStartOffset(), assetFileDescriptor.getLength()); } /** Initializes conversation intent detection, passing the given serialized config to it. */ public void initializeConversationIntentDetection(byte[] serializedConfig) { if (!nativeInitializeConversationIntentDetection(actionsModelPtr, serializedConfig)) { throw new IllegalArgumentException("Couldn't initialize conversation intent detection"); } } /** Represents a list of suggested actions of a given conversation. */ public static final class ActionSuggestions { /** A list of suggested actionsm sorted by score descendingly. */ public final ActionSuggestion[] actionSuggestions; /** Whether the input conversation is considered as sensitive. */ public final boolean isSensitive; public ActionSuggestions(ActionSuggestion[] actionSuggestions, boolean isSensitive) { this.actionSuggestions = actionSuggestions; this.isSensitive = isSensitive; } } /** Action suggestion that contains a response text and the type of the response. */ public static final class ActionSuggestion { @Nullable private final String responseText; private final String actionType; private final float score; @Nullable private final NamedVariant[] entityData; @Nullable private final byte[] serializedEntityData; @Nullable private final RemoteActionTemplate[] remoteActionTemplates; @Nullable private final Slot[] slots; public ActionSuggestion( @Nullable String responseText, String actionType, float score, @Nullable NamedVariant[] entityData, @Nullable byte[] serializedEntityData, @Nullable RemoteActionTemplate[] remoteActionTemplates, @Nullable Slot[] slots) { this.responseText = responseText; this.actionType = actionType; this.score = score; this.entityData = entityData; this.serializedEntityData = serializedEntityData; this.remoteActionTemplates = remoteActionTemplates; this.slots = slots; } @Nullable public String getResponseText() { return responseText; } public String getActionType() { return actionType; } /** Confidence score between 0 and 1 */ public float getScore() { return score; } @Nullable public NamedVariant[] getEntityData() { return entityData; } @Nullable public byte[] getSerializedEntityData() { return serializedEntityData; } @Nullable public RemoteActionTemplate[] getRemoteActionTemplates() { return remoteActionTemplates; } @Nullable public Slot[] getSlots() { return slots; } } /** Represents a single message in the conversation. */ public static final class ConversationMessage { private final int userId; @Nullable private final String text; private final long referenceTimeMsUtc; @Nullable private final String referenceTimezone; @Nullable private final String detectedTextLanguageTags; public ConversationMessage( int userId, @Nullable String text, long referenceTimeMsUtc, @Nullable String referenceTimezone, @Nullable String detectedTextLanguageTags) { this.userId = userId; this.text = text; this.referenceTimeMsUtc = referenceTimeMsUtc; this.referenceTimezone = referenceTimezone; this.detectedTextLanguageTags = detectedTextLanguageTags; } /** The identifier of the sender */ public int getUserId() { return userId; } @Nullable public String getText() { return text; } /** * Return the reference time of the message, for example, it could be compose time or send time. * {@code 0} means unspecified. */ public long getReferenceTimeMsUtc() { return referenceTimeMsUtc; } @Nullable public String getReferenceTimezone() { return referenceTimezone; } /** Returns a comma separated list of BCP 47 language tags. */ @Nullable public String getDetectedTextLanguageTags() { return detectedTextLanguageTags; } } /** Represents conversation between multiple users. */ public static final class Conversation { public final ConversationMessage[] conversationMessages; public Conversation(ConversationMessage[] conversationMessages) { this.conversationMessages = conversationMessages; } public ConversationMessage[] getConversationMessages() { return conversationMessages; } } /** Represents options for the SuggestActions call. */ public static final class ActionSuggestionOptions { public ActionSuggestionOptions() {} } /** Represents a slot for an {@link ActionSuggestion}. */ public static final class Slot { public final String type; public final int messageIndex; public final int startIndex; public final int endIndex; public final float confidenceScore; public Slot( String type, int messageIndex, int startIndex, int endIndex, float confidenceScore) { this.type = type; this.messageIndex = messageIndex; this.startIndex = startIndex; this.endIndex = endIndex; this.confidenceScore = confidenceScore; } } /** * Retrieves the pointer to the native object. Note: Need to keep the {@code * ActionsSuggestionsModel} alive as long as the pointer is used. */ long getNativeModelPointer() { return nativeGetNativeModelPtr(actionsModelPtr); } private static native long nativeNewActionsModel(int fd, byte[] serializedPreconditions); private static native long nativeNewActionsModelFromPath( String path, byte[] preconditionsOverwrite); private static native long nativeNewActionsModelWithOffset( int fd, long offset, long size, byte[] preconditionsOverwrite); private native boolean nativeInitializeConversationIntentDetection( long actionsModelPtr, byte[] serializedConfig); private static native String nativeGetLocales(int fd); private static native String nativeGetLocalesWithOffset(int fd, long offset, long size); private static native int nativeGetVersion(int fd); private static native int nativeGetVersionWithOffset(int fd, long offset, long size); private static native String nativeGetName(int fd); private static native String nativeGetNameWithOffset(int fd, long offset, long size); private native ActionSuggestions nativeSuggestActions( long context, Conversation conversation, ActionSuggestionOptions options, long annotatorPtr, Object appContext, String deviceLocales, boolean generateAndroidIntents); private native void nativeCloseActionsModel(long ptr); private native long nativeGetNativeModelPtr(long context); }