Media Response in Custom Payload (Dialogflow) - actions-on-google

I'm trying to code a Media Response in a Custom Payload with no luck. I'm surely doing something wrong but I have no idea :) The Media Response does not show up when testing. (Please note that I'm trying this in an english action). Here's the JSON code:
{
"platform": "google",
"type": "custom_payload",
"payload": {
"google": {
"richResponse": {
"items": [
{
"mediaResponse": {
"mediaType": "AUDIO",
"mediaObjects": [
{
"name": "Exercises",
"description": "ex",
"largeImage": {
"url": "https://firebasestorage.googleapis.com/...",
"accessibilityText": "image..."
},
"contentUrl": "https://firebasestorage.googleapis.com/..."
}
]
}
}
]
}
}
}
}
UPDATE:
I've updated the JSON to something like this. But I get an error :"API Version 2: Failed to parse JSON response string with 'INVALID_ARGUMENT' error: ": Cannot find field."
{
"platform":"google",
"type":"custom_payload",
"payload":{
"google":{
"richResponse":{
"items":[
{
"simpleResponse":{
"textToSpeech":"Hey! Good to see you."
}
},
{
"mediaResponse":{
"mediaType":"AUDIO",
"mediaObjects":[
{
"name":"Exercises",
"description":"ex",
"largeImage":{
"url":"https://firebasestorage...",
"accessibilityText":"..."
},
"contentUrl":"https://firebasestorage.googleapis.com/..."
}
]
}
}
],
"suggestions":[
{
"title":"chips"
}
]
}
}
And here's the debug information:
{
"audioResponse": "//NExAARWG...",
"conversationToken": "GidzaW11bG...",
"debugInfo": {
"agentToAssistantDebug": {
"agentToAssistantJson": "{\"message\":\"Failed to parse Dialogflow response into AppResponse, exception thrown with message: Empty speech response\",\"apiResponse\":{\"id\":\"cd7204ac-ab80-42aa-9755-6123cbb938a6\",\"timestamp\":\"2018-03-11T09:02:35.827Z\",\"lang\":\"en-us\",\"result\":{},\"status\":{\"code\":200,\"errorType\":\"success\"},\"sessionId\":\"1520758955600\"}}"
},
"assistantToAgentDebug": {
"assistantToAgentJson": "{\"user\":{\"userId\":\"AA9douaa4XGkqtmcU_EDjPy7PQ_9\",\"locale\":\"en-US\",\"lastSeen\":\"2018-03-11T09:02:09Z\"},\"conversation\":{\"conversationId\":\"1520758955600\",\"type\":\"NEW\"},\"inputs\":[{\"intent\":\"actions.intent.MAIN\",\"rawInputs\":[{\"inputType\":\"VOICE\",\"query\":\"Talk to Zen Coach\"}]}],\"surface\":{\"capabilities\":[{\"name\":\"actions.capability.SCREEN_OUTPUT\"},{\"name\":\"actions.capability.MEDIA_RESPONSE_AUDIO\"},{\"name\":\"actions.capability.WEB_BROWSER\"},{\"name\":\"actions.capability.AUDIO_OUTPUT\"}]},\"isInSandbox\":true,\"availableSurfaces\":[{\"capabilities\":[{\"name\":\"actions.capability.SCREEN_OUTPUT\"},{\"name\":\"actions.capability.AUDIO_OUTPUT\"}]}]}",
"curlCommand": "curl -v 'https://api.api.ai/api/integrations/google?token=0def1bb6be4b4bf2810ec68bf6f37a6a' -H 'Content-Type: application/json;charset=UTF-8' -H 'Google-Actions-API-Version: 2' -H 'Authorization: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFjMmI2M2ZhZWZjZjgzNjJmNGM1MjhlN2M3ODQzMzg3OTM4NzAxNmIifQ.eyJhdWQiOiJ6ZW4tY29hY2giLCJhenAiOiI0OTYwOTIwOTE1NzEtMGNhY3VtczVkZ3F1OWpkM2k0dHZpOGFiOTVydXQ2NnQuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJleHAiOjE1MjA3NTkwNzUsImlzcyI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbSIsImp0aSI6IjY4NDc0NThhNTNhZGExODAxZjMwMjAyYjkxZGIyODZhMjk1NzA2YmIiLCJpYXQiOjE1MjA3NTg5NTUsIm5iZiI6MTUyMDc1ODY1NX0.e1cqg96F5L-BvD0yJz3UFgsnX_0TRox0Lu8R9K5NhhXcQVfC7mq1QwCqs2DGrUJGquGdW2GhzBU2lzf4ro2TUeieg4ozak1OmiYAMqtiCH0EodeHy59AXXqzb3a35YuD7CmSDu6qVQRfEp8uaaH2t-Sq9lUchudNOgjucip3ex9Rr2XacHm0qWtV69H1o-Yq5INl5HHR0kNqtEIsxUox961imKvDLN5s--F35yTbAhIWibr6OmaACyzSQW5X7OjrJ2781DSmEdYn73poDbuwMS9E2l9B-QTUHAIpUM5b4WqrFkD6XKALdf2pQFwZlRRhDzRiDKWLA-i1w-mcak0LWw' -A 'Mozilla/5.0 (compatible; Google-Cloud-Functions/2.1; +http://www.google.com/bot.html)' -X POST -d '{\"user\":{\"userId\":\"AA9douaa4XGkqtmcU_EDjPy7PQ_9\",\"locale\":\"en-US\",\"lastSeen\":\"2018-03-11T09:02:09Z\"},\"conversation\":{\"conversationId\":\"1520758955600\",\"type\":\"NEW\"},\"inputs\":[{\"intent\":\"actions.intent.MAIN\",\"rawInputs\":[{\"inputType\":\"VOICE\",\"query\":\"Talk to Zen Coach\"}]}],\"surface\":{\"capabilities\":[{\"name\":\"actions.capability.SCREEN_OUTPUT\"},{\"name\":\"actions.capability.MEDIA_RESPONSE_AUDIO\"},{\"name\":\"actions.capability.WEB_BROWSER\"},{\"name\":\"actions.capability.AUDIO_OUTPUT\"}]},\"isInSandbox\":true,\"availableSurfaces\":[{\"capabilities\":[{\"name\":\"actions.capability.SCREEN_OUTPUT\"},{\"name\":\"actions.capability.AUDIO_OUTPUT\"}]}]}'"
},
"sharedDebugInfo": [
{
"name": "ResponseValidation",
"subDebugEntry": [
{
"debugInfo": "API Version 2: Failed to parse JSON response string with 'INVALID_ARGUMENT' error: \": Cannot find field.\".",
"name": "UnparseableJsonResponse"
}
]
}
]
},
"response": "Zen coach isn't responding right now. Try again soon.",
"visualResponse": {
"visualElements": []
}
}

Are you adding "platform":"google" and "type":"custom_payload" in the custom payload? If so, try removing that.
I made the following work with my Voice Metronome application:
{
"google":{
"richResponse":{
"items":[
{
"simpleResponse":{
"textToSpeech":"Hey! Good to see you."
}
},
{
"mediaResponse":{
"mediaType":"AUDIO",
"mediaObjects":[
{
"name":"Exercises",
"description":"ex",
"largeImage":{
"url":"http://res.freestockphotos.biz/pictures/17/17903-balloons-pv.jpg",
"accessibilityText":"..."
},
"contentUrl":"https://freepd.com/Chill/Chill Air.mp3"
}
]
}
}
],
"suggestions":[
{
"title":"chips"
}
]
}
}
}

The issue is that the richResponse property still needs to follow the rules of the RichResponse object. The first item in it must be a SimpleResponse object. (I haven't tested, but you can probably have that say nothing, but it is a good spot to have an introduction to your audio.)
The error message Failed to parse Dialogflow response into AppResponse, exception thrown with message: Empty speech response suggests that it might also be looking for a speech parameter on the top-level object in the response, which is what Dialogflow v1 expects to duplicate either the simpleResponse ssml or textToSpeech parameters. I'm not sure why that would appear if you're set to v2, but it sounds like something might be confused there. I would make sure you're using v1 and that you have a speech parameter.
Also keep in mind that the reviewers will look for suggestion chips about how to move the conversation forward during or after the audio if this isn't a final response.

Related

Webhooks appoinments facebook

I'm currently trying to get all the appointments users requested on my page.
So I set up a Webhook to get them in real time to then save them into my database:
GET /v3.2/{id_page}/subscribed_apps path send me this response so I'm well subscribed:
{
"data": [
{
"link": "linktopage",
"name": "pagename",
"id": "idpage",
"subscribed_fields": [
"messaging_appointments",
]
}
]
}
Now the problem is, Facebook send me this when someone request an appoinment :
{
"object":"page",
"entry":[
{
"id":"idpage",
"time":1552015328847,
"messaging":[
{
"sender":{
"id":"idsender"
},
"recipient":{
"id":"idpage"
},
"timestamp":1552015327245,
"message":{
"mid":"idmessage",
"seq":223007,
"attachments":[
{
"title":"Appointment Request",
"url":null,
"type":"fallback",
"payload":null
}
]
}
}
]
}
]
}
Facebook doesn't send me the date for the appointment, nor the duration, and I need those informations. (Time and timestamp indicate when the webhook occurred)
When I try to get the message with mid with path GET /v3.2/{message-id}, the api tells me
"(#803) Some of the aliases you requested do not exist: idmessage"
Is there something I'm doing wrong? Thanks

How to write a response message when first command succeeded and second command failed on same EXECUTE intent

I cannot understand how to write a response like following situation.
Precondition
AC_UNIT supports TEMPERATURE_SETTING traits.
AC_UNIT's availableThermostatMode is "off,on,heat,cool"
AC_UNIT's current mode is cool.
When I do a "Set the heat to {temperature}", google send like a following message.
How to write a response when ThermostatSetMode command succeeds and ThermostatTemperatureSetPoint failed?
Am I not able to write a response which contains each commands result?
{
"inputs": [
{
"intent": "action.devices.EXECUTE",
"payload": {
"commands": [
{
"devices": [
{
"id": "device id"
}
],
"execution": [
{
"command": "action.devices.commands.ThermostatSetMode",
"params": {
"thermostatMode": "heat"
}
},
{
"command": "action.devices.commands.ThermostatTemperatureSetpoint",
"params": {
"thermostatTemperatureSetpoint": 32 // this is out of range value.
}
}
]
}
]
}
}
],
"requestId": "requestId"
}
Thanks.
There's not a way to specify the response on a per-command basis, just on the device level. In this case, your best bet would be to return an error in the response with valueOutOfRange which will alert the user.

Why is the carousel not showing in the console simulator?

I am trying to figure out how I can embed Google Actions responses, such as the carousel, in a webhook response for DialogFlow.
I am using V2 of the REST protocol, so I am filling ACTIONS_ON_GOOGLE in the source field and the payload field contains the Google Actions field as specified (as per How can I integrate the Google Actions responses in a webhook response in Dialogflow?). I am sending the following response:
{
"fulfillmentText":"This is a carousel.",
"source":"ACTIONS_ON_GOOGLE",
"payload":{
"conversationToken":"",
"expectUserResponse":true,
"expectedInputs":[
{
"inputPrompt":{
"initialPrompts":[
{
"textToSpeech":"This is a carousel."
}
],
"noInputPrompts":[
]
},
"possibleIntents":[
{
"intent":"actions.intent.OPTION",
"inputValueData":{,
"#type":"type.googleapis.com/google.actions.v2.OptionValueSpec"
"carouselSelect":{
"items":[
{
"optionInfo":{
"key":"key1",
"synonyms":[
"Option 1"
]
},
"title":"Option 1",
"description":"Option 2"
},
{
"optionInfo":{
"key":"key2",
"synonyms":[
"Option 2"
]
},
"title":"Option 2",
"description":"Option 2"
}
]
}
}
}
]
}
]
}
}
When trying this out in the console, no carousel is shown. Only the text This is a carousel. is displayed. For your information, I did not include the image field, as it is optional according to the specification, but even with images there is no carousel displayed.
It's hard to debug this, as my actions.intent.OPTION intent is not displayed in possibleIntents[] array in the response tab. I expected this actions.intent.OPTION intent to be merged with the other intents (such assistant.intent.action.TEXT) as generated by the Dialogflow response.
What am I doing wrong here? Am I maybe shooting myself in the foot by using V2 instead of V1 of the Dialogflow REST protocol?
update after initial feedback by Prisoner
I tried with the following response, but still not getting any carousel:
{
"fulfillmentText":"Here you go.",
"source":"ACTIONS_ON_GOOGLE",
"payload":{
"expectUserResponse":true,
"richResponse":{
"items":[
{
"simpleResponse":{
"textToSpeech":"Here are your results."
}
}
]
},
"systemIntent":{
"intent":"actions.intent.OPTION",
"data":{
"carouselSelect":{
"items":[
{
"optionInfo":{
"key":"Option1",
"synonyms":[
"Option2"
]
},
"title":"Option3",
"description":"Option4"
},
{
"optionInfo":{
"key":"Option5",
"synonyms":[
"Option6"
]
},
"title":"Option7",
"description":"Option8"
}
]
},
"#type":"type.googleapis.com/google.actions.v2.OptionValueSpec"
}
}
}
}
I also tried to manually create an intent in Dialogflow with returns a 'hardcoded' carousel (that is, without fulfillment callback) and this carousel is shown perfectly. So I am sure that the console is correctly configured.
I am also comparing my response with the ones in Google Assistant flow with multiple actions_intent_OPTION handlers, but without success so far.
You haven't shot yourself in the foot - but you have made something that was already a bit complex even more complex. There are two things to look out for:
CORRECTION: Make sure the payload is the same as what used to be data, but other fields have changed format.
You need to make sure the simulator is in Phone mode and not Speaker mode.
Details follow.
Documented Dialogflow Differences
The Actions on Google documentation for the Dialogflow response is a little confusing. Instead of providing the full example, it just says that the response that would be under expectedInputs.possibleIntents should, instead, be under data.google.systemIntent. For V2, that would be payload.google.systemIntent.
The inputPrompt object is also restructured somewhat so you should be sending a richResponse that contains a simpleResponse object.
UPDATE I've tested this. This is the entire JSON that should be returned. Note the contents of payload is exactly what the contents of data used to be. The source field is ignored and has nothing to do with the payload, apparently.
See also https://github.com/dialogflow/fulfillment-webhook-json which contain some examples.
{
"payload": {
"google": {
"expectUserResponse": true,
"richResponse": {
"items": [
{
"simpleResponse": {
"textToSpeech": "This is a carousel"
}
}
]
},
"systemIntent": {
"intent": "actions.intent.OPTION",
"data": {
"#type": "type.googleapis.com/google.actions.v2.OptionValueSpec",
"carouselSelect": {
"items": [
{
"optionInfo": {
"key": "key1",
"synonyms": [
"Option 1"
]
},
"title": "Option 1",
"description": "Option 2"
},
{
"optionInfo": {
"key": "key2",
"synonyms": [
"Option 2"
]
},
"title": "Option 2",
"description": "Option 2"
}
]
}
}
}
}
}
}
Simulator Surface Setting
Make sure that your simulator is set for the "Phone" surface rather than the "Speaker" surface. Options won't display on a Speaker.
The setting should look like this:
Not like this:

Error "Empty speech response"

I tried to connect DialogFlow and Actions on Google, so I created some intents, connected the services, added explicit and implicit invocations etc, but when I try the bot in the simulator https://console.actions.google.com/project/[projectId]/simulator/ it always gives me the error:
"Failed to parse Dialogflow response into AppResponse, exception
thrown with message: Empty speech response"
even tough inputType was "KEYBOARD".
What I tried so far:
I did set "Response from this tab will be sent to the Google Assistant integration" in Dialog Flow (do you have to set it for every single intent?), but I don't see any extra setting for speech.
I disabled the second language, first I had also intents in German
I also turned off the Fullfillment Webhook (implemented in API v1 and then also v2) with no change
I only found this user with the same problem https://productforums.google.com/forum/#!topic/dialogflow/xYjKlz31yW0;context-place=topicsearchin/dialogflow/Empty$20speech$20response but no resolution.
the fulfillment checkbox is checked at the intents
The bot works fine when I use it through "Try it now" on the very right in Dialog Flow or in the Web Demo https://bot.dialogflow.com/994dda8b-4849-4a8a-ab24-c0cd03b5f420
Unfortunately the docs don't say anything about this error. Any ideas?
Here a screenshot of the error on the Actions integration:
This is the full debug output:
{
"agentToAssistantDebug": {
"agentToAssistantJson": {
"message": "Failed to parse Dialogflow response into AppResponse, exception thrown with message: Empty speech response",
"apiResponse": {
"id": "c12e1389-e887-49d4-b399-a332188ca946",
"timestamp": "2018-01-27T03:55:30.931Z",
"lang": "en-us",
"result": {},
"status": {
"code": 200,
"errorType": "success"
},
"sessionId": "1517025330705"
}
}
},
"assistantToAgentDebug": {
"assistantToAgentJson": {
"user": {
"userId": "USER_ID",
"locale": "en-US",
"lastSeen": "2018-01-27T03:55:03Z"
},
"conversation": {
"conversationId": "1517025330705",
"type": "NEW"
},
"inputs": [
{
"intent": "actions.intent.MAIN",
"rawInputs": [
{
"inputType": "KEYBOARD",
"query": "Talk to Mica, the Hipster Cat Bot"
}
]
}
],
"surface": {
"capabilities": [
{
"name": "actions.capability.MEDIA_RESPONSE_AUDIO"
},
{
"name": "actions.capability.WEB_BROWSER"
},
{
"name": "actions.capability.AUDIO_OUTPUT"
},
{
"name": "actions.capability.SCREEN_OUTPUT"
}
]
},
"isInSandbox": true,
"availableSurfaces": [
{
"capabilities": [
{
"name": "actions.capability.AUDIO_OUTPUT"
},
{
"name": "actions.capability.SCREEN_OUTPUT"
}
]
}
]
},
"curlCommand": "curl -v 'https://api.api.ai/api/integrations/google?token=TOKEN' -H 'Content-Type: application/json;charset=UTF-8' -H 'Google-Actions-API-Version: 2' -H 'Authorization: AUTH_TOKEN' -A 'Mozilla/5.0 (compatible; Google-Cloud-Functions/2.1; +http://www.google.com/bot.html)' -X POST -d '{\"user\":{\"userId\":\"USER_ID\",\"locale\":\"en-US\",\"lastSeen\":\"2018-01-27T03:55:03Z\"},\"conversation\":{\"conversationId\":\"1517025330705\",\"type\":\"NEW\"},\"inputs\":[{\"intent\":\"actions.intent.MAIN\",\"rawInputs\":[{\"inputType\":\"KEYBOARD\",\"query\":\"Talk to Mica, the Hipster Cat Bot\"}]}],\"surface\":{\"capabilities\":[{\"name\":\"actions.capability.MEDIA_RESPONSE_AUDIO\"},{\"name\":\"actions.capability.WEB_BROWSER\"},{\"name\":\"actions.capability.AUDIO_OUTPUT\"},{\"name\":\"actions.capability.SCREEN_OUTPUT\"}]},\"isInSandbox\":true,\"availableSurfaces\":[{\"capabilities\":[{\"name\":\"actions.capability.AUDIO_OUTPUT\"},{\"name\":\"actions.capability.SCREEN_OUTPUT\"}]}]}'"
},
"sharedDebugInfo": [
{
"name": "ResponseValidation",
"subDebugEntry": [
{
"debugInfo": "API Version 2: Failed to parse JSON response string with 'INVALID_ARGUMENT' error: \": Cannot find field.\".",
"name": "UnparseableJsonResponse"
}
]
}
]
}
Also "debugInfo" sounds like an internal problem:
"API Version 2: Failed to parse JSON response string with
'INVALID_ARGUMENT' error: \": Cannot find field.\"."
Here a screenshot of the welcome intent:
ps.
It took me AGES to figure out, what
"Query pattern is missing for custom intent"
means - so I just document it here: In Dialog Flow - Intent - "User says" you have to DOUBLE CLICK on a word in the text input field when you want to set it as query parameter - which seems to be required for Actions on Google.
This happened to me. If this happens for an Intent you just added in the Dialogflow console and you are using Webhook fulfillment for the action, check the intent's fulfillment settings and ensure that the Webhook fulfillment slider is on. Evidently new intents don't automatically get webhook fulfillment: you have to opt each one in piecemeal (or at least, that was my experience).
I experienced this situation too.
My problem was that I used a SimpleResponse in my fulfillment index.js without referencing to it. So the solution for me was to add SimpleResponse like this in index.js:
const {dialogflow, SimpleResponse} = require('actions-on-google');
So, always check if you aren't using any dependencies without including it in your js-file.
Probably not the most common cause of the problem, but it can be.
I got this when running through the codelabs tutorial (https://codelabs.developers.google.com/codelabs/actions-1/index.html#4) and didn't name my intent the same name as it is referenced in the webhook script:
I came across this error when trying to develop my own WebHook. I first verified that my code was called by looking into the Nginx log, after which I knew there was a problem in my JSON output because I based my output on outdated examples.
The (up-to-date) documentation for both V1 and V2 of the API can be found here:
https://dialogflow.com/docs/fulfillment/how-it-works
This example response for v2 of the dialogflow webhook API helped me to resolve this error:
{
"payload": {
"google": {
"expectUserResponse": true,
"richResponse": {
"items": [
{
"simpleResponse": {
"textToSpeech": "this is a simple response"
}
}
]
}
}
}
}
Source: https://github.com/dialogflow/fulfillment-webhook-json/blob/master/responses/v2/ActionsOnGoogle/RichResponses/SimpleResponse.json
You can find more examples in the official github repository linked above.
Another possibility is if you have a text response (even an empty one) like so:
Then you need to click the trash can next to the response to clear it out to use the webhook.
The Actions on Google support helped me fix this problem:
I needed to add a text as Default Response to the intent used for Explicit Invocation.

Messenger: attachment type 'video' not working correctly?

According to https://developers.facebook.com/docs/messenger-platform/send-api-reference/video-attachment I should be able to send video's via messenger. I ideally want to send youtube videos with a start and end time, but that does not seem to work.
I am currently trying to get it work in any such way, so i have the video on FB currently and even that isn't working.
python code
data = OrderedDict()
data['sender'] = {"id": APP_ID}
data['recipient'] = {"id": recipient}
data['message'] = {
"attachment": {
"type": "video",
"payload": {"url": "https://www.facebook.com/587721184763189/videos/596530243882283/"}
}
}
data = json.dumps(data)
print("data: ", data)
r = requests.post("https://graph.facebook.com/v2.6/me/messages",
params={"access_token": token},
data=data,
headers={'Content-type': 'application/json'},
timeout=60)
if r.status_code != requests.codes.ok:
print(r.text)
2016-12-20T23:45:40.685949+00:00 app[web.1]: data: {"sender": {"id": 744391742366207}, "recipient": {"id": "1297603110290455"}, "message": {"attachment": {"type": "video", "payload": {"url": "https://www.facebook.com/587721184763189/videos/596530243882283/"}}}}
2016-12-20T23:45:41.396419+00:00 app[web.1]: {"error":{"message":"(#100) Failed to fetch the file from the url","type":"OAuthException","code":100,"error_subcode":2018008,"fbtrace_id":"BjJzB1J8/42"}}
You need to supply the URL to the video file (for example MP4), not the URL of page containing the video (for example a YouTube URL).
This can be seen in the example code in Facebook's documentation
curl -X POST -H "Content-Type: application/json" -d '{
"recipient":{
"id":"USER_ID"
},
"message":{
"attachment":{
"type":"video",
"payload":{
"url":"https://petersapparel.com/bin/clip.mp4"
}
}
}
}' "https://graph.facebook.com/v2.6/me/messages?access_token=PAGE_ACCESS_TOKEN"
Sending Youtube videos in your Facebook Messenger bot is now possible if you use the open graph template.
A message object would then look like this:
"message": {
"attachment": {
"type": "template",
"payload": {
"template_type": "open_graph",
"elements": [
{
"url": "https://www.youtube.com/watch?v=whatever"
}
]
}
}
}
As of today, this works (you need to add the image url of the screenshot):
message: {
text,
attachment: {
type: "template",
payload: {
template_type: "generic",
elements: [
{
title: "El espĂ­a",
image_url: "https://i.ytimg.com/vi/yj2r6KPKV1w/hqdefault.jpg?sqp=-oaymwEcCOADEI4CSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAGKGjilp1gvYUv6c5dL_oveZ8LYg",
default_action: {
type: "web_url",
url: "https://www.youtube.com/watch?v=yj2r6KPKV1w",
},
}
],
},
},
},