Skip to content
This repository has been archived by the owner on May 6, 2022. It is now read-only.

Commit

Permalink
feat: Finalize prompts via Spokestack wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
space-pope committed Apr 28, 2021
1 parent b894698 commit 88cf2f6
Show file tree
Hide file tree
Showing 8 changed files with 373 additions and 1 deletion.
61 changes: 60 additions & 1 deletion src/main/java/io/spokestack/spokestack/Spokestack.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import android.content.Context;
import io.spokestack.spokestack.dialogue.DialogueManager;
import io.spokestack.spokestack.dialogue.FinalizedPrompt;
import io.spokestack.spokestack.dialogue.Prompt;
import io.spokestack.spokestack.nlu.NLUManager;
import io.spokestack.spokestack.nlu.NLUResult;
import io.spokestack.spokestack.nlu.tensorflow.parsers.DigitsParser;
Expand Down Expand Up @@ -146,6 +148,12 @@ private Spokestack(Builder builder) throws Exception {
.addTTSListener(this)
.build();
}

if (builder.dialogueBuilder.hasPolicy()
|| builder.speechConfig.containsKey("dialogue-policy-file")
|| builder.speechConfig.containsKey("dialogue-policy-class")) {
this.dialogueManager = builder.dialogueBuilder.build();
}
}

/**
Expand Down Expand Up @@ -349,6 +357,55 @@ public void stopPlayback() {
}
}

// dialogue

/**
* @return The dialogue manager currently in use.
*/
public DialogueManager getDialogueManager() {
return dialogueManager;
}

/**
* Finalize a prompt, interpolating template strings using the current
* conversation data store.
*
* <p>
* This method can only be used if a dialogue manager is active.
* </p>
*
* @param prompt The prompt to be finalized.
* @return The finalized prompt.
*
* @see DialogueManager#finalizePrompt(Prompt)
* @see io.spokestack.spokestack.dialogue.ConversationData
*/
public FinalizedPrompt finalizePrompt(Prompt prompt) {
if (this.dialogueManager != null) {
return dialogueManager.finalizePrompt(prompt);
}
return null;
}

/**
* Stores an object in the conversation data store for use in interpolating
* system prompts.
*
* <p>
* This method can only be used if a dialogue manager is active.
* </p>
*
* @param key The name of the object to store.
* @param value The object to store.
*
* @see io.spokestack.spokestack.dialogue.ConversationData#set(String, Object)
*/
public void putConversationData(String key, Object value) {
if (this.dialogueManager != null) {
dialogueManager.getDataStore().set(key, value);
}
}

// listeners

/**
Expand Down Expand Up @@ -948,7 +1005,9 @@ public Spokestack build() throws Exception {
+ "TTSManager.Builder.setAndroidContext()");
}
}
if (!this.speechConfig.containsKey("dialogue-policy-file")

if (!this.dialogueBuilder.hasPolicy()
&& !this.speechConfig.containsKey("dialogue-policy-file")
&& !this.speechConfig.containsKey("dialogue-policy-class")) {
this.useDialogue = false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,17 @@ public ConversationData getDataStore() {
return this.dataStore;
}

/**
* Finalize a prompt, interpolating template strings using the current
* conversation data store.
*
* @param prompt The prompt to be finalized.
* @return The finalized prompt.
*/
public FinalizedPrompt finalizePrompt(Prompt prompt) {
return prompt.finalizePrompt(this.dataStore);
}

/**
* Dump the dialogue policy's current state to the currently registered data
* store. This can be used in conjunction with {@link #load(String) load()}
Expand Down Expand Up @@ -271,6 +282,15 @@ public Builder withDialoguePolicy(String policyClass) {
return this;
}

/**
* @return whether this builder has a dialogue policy enabled via
* class or JSON file.
*/
public boolean hasPolicy() {
return this.config.containsKey("dialogue-policy-file")
|| this.config.containsKey("dialogye-policy-class");
}

/**
* Specify the data store to use for conversation data.
*
Expand Down
169 changes: 169 additions & 0 deletions src/main/java/io/spokestack/spokestack/dialogue/FinalizedPrompt.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package io.spokestack.spokestack.dialogue;

import androidx.annotation.NonNull;

import java.util.Arrays;
import java.util.List;

/**
* A finalized prompt contains the same fields as a {@link Prompt}, but instead
* of template placeholders, its contents are fully interpolated strings ready
* to be displayed to the user or synthesized by TTS.
*/
public final class FinalizedPrompt {
private final String id;
private final String text;
private final String voice;
private final Proposal proposal;
private final FinalizedPrompt[] reprompts;
private final boolean endsConversation;

private FinalizedPrompt(Builder builder) {
this.id = builder.id;
this.text = builder.text;
if (builder.voice == null) {
this.voice = builder.text;
} else {
this.voice = builder.voice;
}
this.proposal = builder.proposal;
this.reprompts = builder.reprompts;
this.endsConversation = builder.endsConversation;
}

/**
* @return The prompt's ID.
*/
public String getId() {
return id;
}

/**
* Get a version of the prompt formatted for TTS synthesis.
*
* @return A version of the prompt formatted for TTS synthesis.
*/
public String getVoice() {
return voice;
}

/**
* Get a version of the prompt formatted for print.
*
* @return A version of the prompt formatted for print.
*/
public String getText() {
return text;
}

/**
* @return this prompt's proposal.
*/
public Proposal getProposal() {
return proposal;
}

/**
* @return any reprompts associated with this prompt.
*/
public FinalizedPrompt[] getReprompts() {
return reprompts;
}

/**
* @return {@code true} if the conversation should end after the current
* prompt is delivered; {@code false} otherwise.
*/
public boolean endsConversation() {
return endsConversation;
}

@Override
public String toString() {
return "Prompt{"
+ "id='" + id + '\''
+ ", text='" + text + '\''
+ ", voice='" + voice + '\''
+ ", proposal=" + proposal
+ ", reprompts=" + Arrays.toString(reprompts)
+ ", endsConversation=" + endsConversation
+ '}';
}

/**
* Prompt builder API.
*/
public static final class Builder {

private final String id;
private final String text;
private String voice;
private Proposal proposal;
private FinalizedPrompt[] reprompts;
private boolean endsConversation;

/**
* Create a new prompt builder with the minimal set of required data.
*
* @param promptId The prompt's ID.
* @param textReply A reply template formatted for print.
*/
public Builder(@NonNull String promptId, @NonNull String textReply) {
this.id = promptId;
this.text = textReply;
this.reprompts = new FinalizedPrompt[0];
}

/**
* Signals that the prompt to be built should end the conversation with
* the user.
*
* @return the updated builder
*/
public Builder endsConversation() {
this.endsConversation = true;
return this;
}

/**
* Add a reply template formatted for TTS synthesis to the current
* prompt.
*
* @param voiceReply The voice prompt to be added.
* @return the updated builder
*/
public Builder withVoice(@NonNull String voiceReply) {
this.voice = voiceReply;
return this;
}

/**
* Add a proposal to the current prompt.
*
* @param prop The proposal to be added.
* @return the updated builder
*/
public Builder withProposal(@NonNull Proposal prop) {
this.proposal = prop;
return this;
}

/**
* Specify reprompts for the current prompt.
*
* @param prompts The reprompts to attach.
* @return the updated builder
*/
public Builder withReprompts(@NonNull List<FinalizedPrompt> prompts) {
this.reprompts = prompts.toArray(new FinalizedPrompt[0]);
return this;
}

/**
* @return a complete prompt created from the current builder state.
*/
public FinalizedPrompt build() {
return new FinalizedPrompt(this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
/**
* A simple data store for conversation data that resides in memory and lasts
* only as long as the dialogue manager holding it.
*
* <p>
* Formats values for both display and synthesis using {@link
* String#valueOf(Object)}.
* </p>
*/
public class InMemoryConversationData implements ConversationData {

Expand Down
27 changes: 27 additions & 0 deletions src/main/java/io/spokestack/spokestack/dialogue/Prompt.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import androidx.annotation.NonNull;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
Expand Down Expand Up @@ -127,6 +128,32 @@ public boolean endsConversation() {
return endsConversation;
}

/**
* Finalize this prompt, filling in all placeholders with data from the
* conversation's data store.
*
* @param dataStore The current state of the conversation data to use for
* filling placeholders in prompts.
* @return A finalized version of this prompt ready for display/synthesis.
*/
public FinalizedPrompt finalizePrompt(ConversationData dataStore) {
List<FinalizedPrompt> finalReprompts = new ArrayList<>();
for (Prompt prompt : this.reprompts) {
finalReprompts.add(prompt.finalizePrompt(dataStore));
}

FinalizedPrompt.Builder builder = new FinalizedPrompt.Builder(
this.id, this.getText(dataStore))
.withVoice(this.getVoice(dataStore))
.withProposal(this.proposal)
.withReprompts(finalReprompts);

if (this.endsConversation) {
builder.endsConversation();
}
return builder.build();
}

@Override
public String toString() {
return "Prompt{"
Expand Down

0 comments on commit 88cf2f6

Please sign in to comment.