2

We are implementing Actions on Google with Dialogflow fulfillment using the newly released Java/Kotlin API.

It's called Speech Bank.

While going through the Account Linking process testing on the smartphone, the user is getting MalformedResponse error preventing the completion of the flow and consequent successful hand-off back to the regular flow.

The logs (detailed below) contain the MalformedResponse: Failed to parse Dialogflow response into AppResponse because of empty speech response message, and the user receives Speech bank isn't responding right now. Try again soon. message on her device.

Here's a bit more details on our setup:

enter image description here

The action is configured for Account Linking utilizing our own OAuth 2 compliant mock infrastructure.

enter image description here

There's a single intent (called RawText) configured in Dialogflow, the rest of the interactions are to be taken care of by own internal application via its web hook.

enter image description here

Here's how the state machine is coded in Java so far:

    public class AoGApp extends DialogflowApp {

        private final static Logger log = LoggerFactory.getLogger(AoGApp.class);

       public static final String GREETING = "GOOGLE_ASSISTANT_WELCOME";

        @ForIntent("RawText")
        //@ForIntent("actions.intent.MAIN")
        public ActionResponse launchRequestHandler(ActionRequest request) {

            String userId = request.getAppRequest().getUser().getUserId();
            log.info("userId={}",userId);
            String queryText = request.getWebhookRequest().getQueryResult().getQueryText();
            log.info("queryText={}", queryText);


            String speech = null;

            ResponseBuilder responseBuilder = getResponseBuilder(request);

            if (isBlank(userId) || GREETING.equalsIgnoreCase(queryText)) {

                speech = "\nHi. I sense a great banking experience in your future, I see that your account isn't connected. "
                        + "I've sent a link to your Google Assistant app that will get you started and set up in just several simple steps. "
                        + "Don't worry, I'll be here waiting, just summon me when you're ready.";


                responseBuilder.add(
                        new SignIn()
                            .setContext(speech));
            } else {
                speech = "Welcome. You can say hello.";
                responseBuilder.add(speech);
            }


            return responseBuilder.build();
        }

        @ForIntent("actions.intent.SIGN_IN")
        public ActionResponse getSignInStatus(ActionRequest request) {
          ResponseBuilder responseBuilder = getResponseBuilder(request);
          String text = "Hello from sign-in handler";
          responseBuilder.add(text);
          log.info(text);
          return responseBuilder.build();

        }

    }


and the associated HttpRequest processing: 

    @Override
        protected void handlePOST(final Request request, final HttpServletResponse response) {

            try {
                String rawRequest = ControllerUtils.toString(request.getReader());


                String jsonResponse = app.handleRequest(rawRequest, getHeadersMap(request)).get();
                log.info("Generated response:\n {}", ControllerUtils.prettyPrint(jsonResponse));
                response.setContentType(APPLICATION_JSON.getMimeType());
                response.getWriter().write(jsonResponse);
            } catch (Exception e) {
                handleError(response, e);
            } 
        }

public final class ControllerUtils {

    private final static Logger log = LoggerFactory.getLogger(ControllerUtils.class);
    private static ObjectMapper mapper = new ObjectMapper();

    public static String toString(BufferedReader reader) throws Exception {

        String rawRequest = reader
                .lines()
                //.map(e -> e.concat(System.lineSeparator()))
                .collect(Collectors.joining(System.lineSeparator()));
        log.info("Received AoG Request {}",rawRequest);
        return rawRequest;
    }

    public static String prettyPrint(String json) throws Exception {
        return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(mapper.readValue(json, Object.class));
    }

    public static Map<String, String> getHeadersMap(org.eclipse.jetty.server.Request jettyRequest){
        return Collections.list((Enumeration<String>) jettyRequest.getHeaderNames())
                .stream()
                .collect(Collectors.toMap(
                        name -> name,
                        jettyRequest::getHeader));

    }


}

As configured above, the OAuth authorization code flow undertakes the normal OAuth 2 steps:

  • hits /login endpoint to supply credentials

  • hits /token endpoint to obtain the token (its value is token1 in the below logs. We have a facility to generate & inject our own tokens, this is a testing environment so we produced this token1 value which seem to have been successfully incorporated into the subsequent request.)

Below is the detailed screen shot of failed interaction, with the attached log provided by Actions on Google console:

enter image description here

[
 {
   "textPayload": "Sending request with post data: {\"user\":{\"userId\":\"ABwppHFQHUBr0RrWA_OuL-kK2sxTPUvQtL3D-x2Ydr-7uxLt9zzEFzJrGB-X96d9XY8k9XTJj-RUg9WpzGB9jg\",\"locale\":\"en-US\",\"lastSeen\":\"2019-02-20T21:32:22Z\",\"userStorage\":\"{\\\"data\\\":{}}\"},\"conversation\":{\"conversationId\":\"ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug\",\"type\":\"NEW\"},\"inputs\":[{\"intent\":\"actions.intent.MAIN\",\"rawInputs\":[{\"inputType\":\"VOICE\",\"query\":\"open speech Bank\"}]}],\"surface\":{\"capabilities\":[{\"name\":\"actions.capability.AUDIO_OUTPUT\"},{\"name\":\"actions.capability.MEDIA_RESPONSE_AUDIO\"},{\"name\":\"actions.capability.SCREEN_OUTPUT\"},{\"name\":\"actions.capability.WEB_BROWSER\"}]},\"isInSandbox\":true,\"availableSurfaces\":[{\"capabilities\":[{\"name\":\"actions.capability.AUDIO_OUTPUT\"},{\"name\":\"actions.capability.SCREEN_OUTPUT\"},{\"name\":\"actions.capability.WEB_BROWSER\"}]}]}.",
   "insertId": "f9fzrtf3hjgn4",
   "resource": {
     "type": "assistant_action",
     "labels": {
       "project_id": "speechbank-e8a15",
       "version_id": "",
       "action_id": "actions.intent.MAIN"
     }
   },
   "timestamp": "2019-02-21T13:47:56.713587946Z",
   "severity": "DEBUG",
   "labels": {
     "channel": "preview",
     "source": "AOG_REQUEST_RESPONSE",
     "querystream": "GOOGLE_USER"
   },
   "logName": "projects/speechbank-e8a15/logs/actions.googleapis.com%2Factions",
   "trace": "projects/366800784520/traces/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug",
   "receiveTimestamp": "2019-02-21T13:47:57.205496026Z"
 },
 {
   "textPayload": "Received response from agent with body: HTTP/1.1 200 OK\r\nServer: nginx/1.13.6\r\nDate: Thu, 21 Feb 2019 13:47:57 GMT\r\nContent-Type: application/json;charset=UTF-8\r\nContent-Length: 426\r\nX-Cloud-Trace-Context: d8cb97627afa1d2977b9f567f29598de/11157405402824233090;o=0\r\nGoogle-Actions-API-Version: 2\r\nX-SHARD: shard-2\r\nVia: 1.1 google\r\nAlt-Svc: clear\r\n\r\n{\"conversationToken\":\"[\\\"_actions_on_google\\\"]\",\"expectUserResponse\":true,\"expectedInputs\":[{\"inputPrompt\":{},\"possibleIntents\":[{\"intent\":\"actions.intent.SIGN_IN\",\"inputValueData\":{\"@type\":\"type.googleapis.com/google.actions.v2.SignInValueSpec\"}}]}],\"responseMetadata\":{\"status\":{\"message\":\"Success (200)\"},\"queryMatchInfo\":{\"queryMatched\":true,\"intent\":\"f645f492-f6dc-4e7e-8da6-45711c654ad0\"}},\"userStorage\":\"{\\\"data\\\":{}}\"}.",
   "insertId": "f9fzrtf3hjgn5",
   "resource": {
     "type": "assistant_action",
     "labels": {
       "version_id": "",
       "action_id": "actions.intent.MAIN",
       "project_id": "speechbank-e8a15"
     }
   },
   "timestamp": "2019-02-21T13:47:57.190979036Z",
   "severity": "DEBUG",
   "labels": {
     "source": "AOG_REQUEST_RESPONSE",
     "querystream": "GOOGLE_USER",
     "channel": "preview"
   },
   "logName": "projects/speechbank-e8a15/logs/actions.googleapis.com%2Factions",
   "trace": "projects/366800784520/traces/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug",
   "receiveTimestamp": "2019-02-21T13:47:57.205496026Z"
 },
 {
   "textPayload": "Sending request with post data: {\"user\":{\"userId\":\"ABwppHFQHUBr0RrWA_OuL-kK2sxTPUvQtL3D-x2Ydr-7uxLt9zzEFzJrGB-X96d9XY8k9XTJj-RUg9WpzGB9jg\",\"accessToken\":\"token1\",\"locale\":\"en-US\",\"lastSeen\":\"2019-02-20T21:32:22Z\",\"userStorage\":\"{\\\"data\\\":{}}\"},\"conversation\":{\"conversationId\":\"ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug\",\"type\":\"ACTIVE\",\"conversationToken\":\"[\\\"_actions_on_google\\\"]\"},\"inputs\":[{\"intent\":\"actions.intent.SIGN_IN\",\"rawInputs\":[{}],\"arguments\":[{\"name\":\"SIGN_IN\",\"extension\":{\"@type\":\"type.googleapis.com/google.actions.v2.SignInValue\",\"status\":\"OK\"}},{\"name\":\"text\"}]}],\"surface\":{\"capabilities\":[{\"name\":\"actions.capability.WEB_BROWSER\"},{\"name\":\"actions.capability.SCREEN_OUTPUT\"},{\"name\":\"actions.capability.MEDIA_RESPONSE_AUDIO\"},{\"name\":\"actions.capability.AUDIO_OUTPUT\"}]},\"isInSandbox\":true,\"availableSurfaces\":[{\"capabilities\":[{\"name\":\"actions.capability.WEB_BROWSER\"},{\"name\":\"actions.capability.SCREEN_OUTPUT\"},{\"name\":\"actions.capability.AUDIO_OUTPUT\"}]}]}.",
   "insertId": "120k9w1f3jmw55",
   "resource": {
     "type": "assistant_action",
     "labels": {
       "version_id": "",
       "action_id": "actions.intent.SIGN_IN",
       "project_id": "speechbank-e8a15"
     }
   },
   "timestamp": "2019-02-21T13:48:28.768213970Z",
   "severity": "DEBUG",
   "labels": {
     "source": "AOG_REQUEST_RESPONSE",
     "querystream": "GOOGLE_USER",
     "channel": "preview"
   },
   "logName": "projects/speechbank-e8a15/logs/actions.googleapis.com%2Factions",
   "trace": "projects/366800784520/traces/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug",
   "receiveTimestamp": "2019-02-21T13:48:28.912828815Z"
 },
 {
   "textPayload": "Received response from agent with body: HTTP/1.1 200 OK\r\nServer: nginx/1.13.6\r\nDate: Thu, 21 Feb 2019 13:48:28 GMT\r\nContent-Type: application/json;charset=UTF-8\r\nContent-Length: 570\r\nX-Cloud-Trace-Context: 664d8fdaf9cd3d880d41f11ac2176e0e/16724608154084655134;o=0\r\nGoogle-Actions-API-Version: 2\r\nAssistant-Interaction-Error-Code: -1\r\nAssistant-Interaction-Error-Message: Failed to parse Dialogflow response into AppResponse because of empty speech response\r\nX-SHARD: shard-2\r\nVia: 1.1 google\r\nAlt-Svc: clear\r\n\r\n{\n  \"responseMetadata\": {\n    \"status\": {\n      \"code\": 10,\n      \"message\": \"Failed to parse Dialogflow response into AppResponse because of empty speech response\",\n      \"details\": [{\n        \"@type\": \"type.googleapis.com/google.protobuf.Value\",\n        \"value\": \"{\\\"id\\\":\\\"5d4bed8d-c58c-4429-9838-f758d6f335f2\\\",\\\"timestamp\\\":\\\"2019-02-21T13:48:28.806Z\\\",\\\"lang\\\":\\\"en-us\\\",\\\"result\\\":{},\\\"status\\\":{\\\"code\\\":200,\\\"errorType\\\":\\\"success\\\"},\\\"sessionId\\\":\\\"ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug\\\"}\"\n      }]\n    }\n  }\n}.",
   "insertId": "120k9w1f3jmw56",
   "resource": {
     "type": "assistant_action",
     "labels": {
       "project_id": "speechbank-e8a15",
       "version_id": "",
       "action_id": "actions.intent.SIGN_IN"
     }
   },
   "timestamp": "2019-02-21T13:48:28.899033790Z",
   "severity": "DEBUG",
   "labels": {
     "channel": "preview",
     "source": "AOG_REQUEST_RESPONSE",
     "querystream": "GOOGLE_USER"
   },
   "logName": "projects/speechbank-e8a15/logs/actions.googleapis.com%2Factions",
   "trace": "projects/366800784520/traces/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug",
   "receiveTimestamp": "2019-02-21T13:48:28.912828815Z"
 },
 {
   "textPayload": "MalformedResponse: Failed to parse Dialogflow response into AppResponse because of empty speech response",
   "insertId": "1b6j2e6f39jvuy",
   "resource": {
     "type": "assistant_action",
     "labels": {
       "project_id": "speechbank-e8a15",
       "version_id": "",
       "action_id": "actions.intent.SIGN_IN"
     }
   },
   "timestamp": "2019-02-21T13:48:28.899403302Z",
   "severity": "ERROR",
   "labels": {
     "channel": "preview",
     "source": "JSON_RESPONSE_VALIDATION",
     "querystream": "GOOGLE_USER"
   },
   "logName": "projects/speechbank-e8a15/logs/actions.googleapis.com%2Factions",
   "trace": "projects/366800784520/traces/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug",
   "receiveTimestamp": "2019-02-21T13:48:28.914061262Z"
 }
]

Based on the above setup description, could anyone please give us a hand figuring out what's causing the MalformedResponse exception and what needs to change to eliminate it.

This exception is so obscure that it brings about a myriad questions and places one at one's wits end as to where to start approaching it. I'll list just a few here and would really appreciate some guidance.

  • Should there be any correlation between intent names in AoG and Dialogflow? Should they follow any naming convention? Could the cause of the error lie in their somehow being misnamed?

  • Can the MalformedResponse be interpreted as lack of a particular field on the response? Since Google chose to expose the inner workings of the conversion between different message formats (Dialogflow and AppResponse), is there a listing somewhere of what fields are required on a Dialogflow response?

  • Is this to imply that even the OAuth messages that are being passed around in this case need to contain some speech?

  • Initially, the userId received from Dialogflow seems to always be null but the query text seems to be populated with GOOGLE_ASSISTANT_WELCOME, so we are starting the account linking flow logic based on the assumption that it is null. Is that a right assumption to have?

  • Under which circumstances will the userId be initially populated (like in Alexa where it's autogenerated upon enabling a skill for the user) so that the else condition above could get triggered?

  • Should the OAuth token issued by the authentication infrastructure and supported by AoG be in any particular format, i.e. OIDC or JWT. Could it be any random string? Is token1 still a valid token in AoG parlance (as it is in Alexa)?

  • Any misconfigured Java intent handler(s)? Which intent name in the response from the AoG account linking flow should we be reacting to?

  • Is there a catch-all intent name(s), a handler for which can be incorporated into the Java app to facilitate further debugging of the above?

  • What is meant by "empty speech response", what values are we not providing that are expected and cause the breakage?

  • Anything that we have configured which should not have been configured?

If it matters at all, here's the log from our webhook:

     [java] 02-21-2019 13:47:57 [qtp2056234595-127] INFO  domain.lola.user.utils.http.ControllerUtils [toString:30]    - Received AoG Request {
 [java]   "responseId": "0156911c-d7e8-405b-bf8f-f23320c02030",
 [java]   "queryResult": {
 [java]     "queryText": "GOOGLE_ASSISTANT_WELCOME",
 [java]     "parameters": {
 [java]       "any": ""
 [java]     },
 [java]     "allRequiredParamsPresent": true,
 [java]     "fulfillmentMessages": [{
 [java]       "text": {
 [java]         "text": [""]
 [java]       }
 [java]     }],
 [java]     "outputContexts": [{
 [java]       "name": "projects/speechbank-e8a15/agent/sessions/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug/contexts/google_assistant_welcome",
 [java]       "parameters": {
 [java]         "any.original": "",
 [java]         "any": ""
 [java]       }
 [java]     }, {
 [java]       "name": "projects/speechbank-e8a15/agent/sessions/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug/contexts/actions_capability_screen_output",
 [java]       "parameters": {
 [java]         "any.original": "",
 [java]         "any": ""
 [java]       }
 [java]     }, {
 [java]       "name": "projects/speechbank-e8a15/agent/sessions/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug/contexts/actions_capability_audio_output",
 [java]       "parameters": {
 [java]         "any.original": "",
 [java]         "any": ""
 [java]       }
 [java]     }, {
 [java]       "name": "projects/speechbank-e8a15/agent/sessions/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug/contexts/google_assistant_input_type_voice",
 [java]       "parameters": {
 [java]         "any.original": "",
 [java]         "any": ""
 [java]       }
 [java]     }, {
 [java]       "name": "projects/speechbank-e8a15/agent/sessions/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug/contexts/actions_capability_web_browser",
 [java]       "parameters": {
 [java]         "any.original": "",
 [java]         "any": ""
 [java]       }
 [java]     }, {
 [java]       "name": "projects/speechbank-e8a15/agent/sessions/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug/contexts/actions_capability_media_response_audio",
 [java]       "parameters": {
 [java]         "any.original": "",
 [java]         "any": ""
 [java]       }
 [java]     }],
 [java]     "intent": {
 [java]       "name": "projects/speechbank-e8a15/agent/intents/f645f492-f6dc-4e7e-8da6-45711c654ad0",
 [java]       "displayName": "RawText"
 [java]     },
 [java]     "intentDetectionConfidence": 1.0,
 [java]     "languageCode": "en-us"
 [java]   },
 [java]   "originalDetectIntentRequest": {
 [java]     "source": "google",
 [java]     "version": "2",
 [java]     "payload": {
 [java]       "isInSandbox": true,
 [java]       "surface": {
 [java]         "capabilities": [{
 [java]           "name": "actions.capability.AUDIO_OUTPUT"
 [java]         }, {
 [java]           "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
 [java]         }, {
 [java]           "name": "actions.capability.SCREEN_OUTPUT"
 [java]         }, {
 [java]           "name": "actions.capability.WEB_BROWSER"
 [java]         }]
 [java]       },
 [java]       "inputs": [{
 [java]         "rawInputs": [{
 [java]           "query": "open speech Bank",
 [java]           "inputType": "VOICE"
 [java]         }],
 [java]         "intent": "actions.intent.MAIN"
 [java]       }],
 [java]       "user": {
 [java]         "userStorage": "{\"data\":{}}",
 [java]         "lastSeen": "2019-02-20T21:32:22Z",
 [java]         "locale": "en-US",
 [java]         "userId": "ABwppHFQHUBr0RrWA_OuL-kK2sxTPUvQtL3D-x2Ydr-7uxLt9zzEFzJrGB-X96d9XY8k9XTJj-RUg9WpzGB9jg"
 [java]       },
 [java]       "conversation": {
 [java]         "conversationId": "ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug",
 [java]         "type": "NEW"
 [java]       },
 [java]       "availableSurfaces": [{
 [java]         "capabilities": [{
 [java]           "name": "actions.capability.AUDIO_OUTPUT"
 [java]         }, {
 [java]           "name": "actions.capability.SCREEN_OUTPUT"
 [java]         }, {
 [java]           "name": "actions.capability.WEB_BROWSER"
 [java]         }]
 [java]       }]
 [java]     }
 [java]   },
 [java]   "session": "projects/speechbank-e8a15/agent/sessions/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug"
 [java] }
 [java] 02-21-2019 13:47:57 [qtp2056234595-127] INFO  domain.lola.user.utils.actionsongoogle.AoGApp [launchRequestHandler:26]    - userId=null
 [java] 02-21-2019 13:47:57 [qtp2056234595-127] INFO  domain.lola.user.utils.actionsongoogle.AoGApp [launchRequestHandler:28]    - queryText=GOOGLE_ASSISTANT_WELCOME
 [java] 02-21-2019 13:47:57 [qtp2056234595-127] INFO  domain.lola.user.utils.actionsongoogle.AoGBotService [handlePOST:103]    - Generated response:
 [java]  {
 [java]   "outputContexts" : [ {
 [java]     "lifespanCount" : 99,
 [java]     "name" : "projects/speechbank-e8a15/agent/sessions/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug/contexts/_actions_on_google",
 [java]     "parameters" : {
 [java]       "data" : "{}"
 [java]     }
 [java]   } ],
 [java]   "payload" : {
 [java]     "google" : {
 [java]       "expectUserResponse" : true,
 [java]       "isSsml" : false,
 [java]       "systemIntent" : {
 [java]         "intent" : "actions.intent.SIGN_IN",
 [java]         "data" : {
 [java]           "@type" : "type.googleapis.com/google.actions.v2.SignInValueSpec"
 [java]         }
 [java]       },
 [java]       "userStorage" : "{\"data\":{}}"
 [java]     }
 [java]   }
 [java] }
Simeon Leyzerzon
  • 18,658
  • 9
  • 54
  • 82

1 Answers1

1

this line

responseBuilder.add(new SignIn().setContext(speech));

Will create your response with SIGN_IN event. So in you dialogFlow you need to add another intent with actions_intent_SIGN_IN, and in your Java you need to implement it also, here you can find more info.

example dialogFlow: enter image description here

Aytacworld
  • 145
  • 2
  • 12
  • 1
    btw, event-names in GoogleAssistant **actions.intent.SIGN_IN**, event-names in dialogFlow **actions_intent_SIGN_IN**, https://dialogflow.com/docs/events/platform-events#actions_on_google_events – Aytacworld Feb 22 '19 at 14:16
  • For a little bit of teaching-to-fish instead of handing-a-fish, can you explain how you get from the extremely vague "MalformedResponse" to a specific solution such as this SIGN_IN one? – NH. Feb 22 '19 at 15:59
  • 1
    Using dialogFlow, you can trigger the AccountLinking in 2 ways. 1) console.dialogflow.com -> select your app -> integrations -> Google Assistant -> check the "SignIn required" for Default Welcome Intent and for all the other implicit intents as well. When you launch your action with "hey google, talk to my action", aog will directly ask for AccountLinking, or you won't be able to send anything to dialogFlow. – Aytacworld Feb 22 '19 at 22:37
  • 1
    2) You do the AccountLinking during conversation with your action. So "SignIn required" is unchecked for the Default Welcome Intent, and you do the "userIsLoggedIn"-check yourself in your fulfillment. And you need to use the `new SignIn()` helper that actions-on-google package provides(or you can create the response json yourself, but that's the hard way), to indicate that you want to trigger a SIGN_IN event, you need an intent which will be triggered after the signin. – Aytacworld Feb 22 '19 at 22:38
  • So, he was talking about AccountLinking and by looking at his Java code, he was doing the second option, and he told that he only used one intent(the default intent) for everything. And the `MalformedResponse: Failed to parse Dialogflow response into AppResponse because of empty speech response`, means that the call after AccountLinking can't find an intent with SIGN_IN event, so gets an empty response. – Aytacworld Feb 22 '19 at 22:38
  • Thanks. For completeness, it needs to be pointed out that the name to which the webhook reacts via its `@ForIntent` handler needs to exactly match the name of the intent in Dialogflow, so if the new DF intent is named `SignInIntent` (as per the answer above), the handler should have a corresponding annotation `@ForIntent("SignInIntent")` and not ` @ForIntent("actions.intent.SIGN_IN")` (as was in the original question). – Simeon Leyzerzon Feb 25 '19 at 15:07