notebooks/official/generative_ai/cambai_intro.ipynb (815 lines of code) (raw):
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"cellView": "form",
"id": "9A9NkTRTfo2I"
},
"outputs": [],
"source": [
"# Copyright 2024 Google LLC\n",
"#\n",
"# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
"# you may not use this file except in compliance with the License.\n",
"# You may obtain a copy of the License at\n",
"#\n",
"# https://www.apache.org/licenses/LICENSE-2.0\n",
"#\n",
"# Unless required by applicable law or agreed to in writing, software\n",
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
"# See the License for the specific language governing permissions and\n",
"# limitations under the License."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "5HQofEWl0LZp"
},
"source": [
"# Getting Started with CAMB.AI Models\n",
"\n",
"<table align=\"left\">\n",
" <td style=\"text-align: center\">\n",
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/official/generative_ai/cambai_intro.ipynb\">\n",
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Google Colaboratory logo\"><br> Open in Colab\n",
" </a>\n",
" </td>\n",
" <td style=\"text-align: center\">\n",
" <a href=\"https://console.cloud.google.com/vertex-ai/colab/import/https:%2F%2Fraw.githubusercontent.com%2FGoogleCloudPlatform%2Fvertex-ai-samples%2Fmain%2Fnotebooks%2Fofficial/generative_ai/cambai_intro.ipynb\">\n",
" <img width=\"32px\" src=\"https://cloud.google.com/ml-engine/images/colab-enterprise-logo-32px.png\" alt=\"Google Cloud Colab Enterprise logo\"><br> Open in Colab Enterprise\n",
" </a>\n",
" </td> \n",
" <td style=\"text-align: center\">\n",
" <a href=\"https://console.cloud.google.com/vertex-ai/workbench/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/official/generative_ai/cambai_intro.ipynb\">\n",
" <img src=\"https://lh3.googleusercontent.com/UiNooY4LUgW_oTvpsNhPpQzsstV5W8F7rYgxgGBD85cWJoLmrOzhVs_ksK_vgx40SHs7jCqkTkCk=e14-rj-sc0xffffff-h130-w32\" alt=\"Vertex AI logo\"><br> Open in Workbench\n",
" </a>\n",
" </td>\n",
" <td style=\"text-align: center\">\n",
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/official/generative_ai/cambai_intro.ipynb\">\n",
" <img src=\"https://cloud.google.com/ml-engine/images/github-logo-32px.png\" alt=\"GitHub logo\"><br> View on GitHub\n",
" </a>\n",
" </td>\n",
"</table>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "8fK_rdvvx1iZ"
},
"source": [
"## Overview\n",
"\n",
"### Camb AI on Vertex AI\n",
"\n",
"You can deploy the Camb AI models in your own endpoint.\n",
"\n",
"### Available Camb AI models\n",
"\n",
"#### MARS7\n",
"(Multilingual AutoRegressive Speech 7) is the latest generation in CAMB.AI’s MARS series of speech synthesis models. MARS7 creates hyper-realistic, prosodic, multilingual text-to-speech (TTS) outputs, featuring optional voice cloning and fine-grained emotional control. Deployed on Google Cloud’s Vertex AI Launchpad, it brings near real-time latency, with an architectural focus on parameter efficiency and global context understanding.\n",
"\n",
"## Objective\n",
"This notebook shows how to use **Vertex AI API** to deploy the Camb AI models.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "nwYvaaW25jYS"
},
"source": [
"## Get Started\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "5FO67G8c0LZq"
},
"source": [
"### Install required packages\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"id": "cvwSgvqQ0LZq"
},
"outputs": [],
"source": [
"! pip3 install --upgrade --quiet google-cloud-aiplatform"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"id": "ZpqsCL0f0LZq"
},
"outputs": [],
"source": [
"! pip3 install -U -q httpx"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "Terpy8VeVIN6"
},
"outputs": [],
"source": [
"! apt-get install jq -y"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "b9f4c57a43f6"
},
"source": [
"### Restart runtime (Colab only)\n",
"\n",
"To use the newly installed packages, you must restart the runtime on Google Colab."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"id": "3b9119a60525"
},
"outputs": [],
"source": [
"import sys\n",
"\n",
"if \"google.colab\" in sys.modules:\n",
"\n",
" import IPython\n",
"\n",
" app = IPython.Application.instance()\n",
" app.kernel.do_shutdown(True)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "e767418763cd"
},
"source": [
"<div class=\"alert alert-block alert-warning\">\n",
"<b>⚠️ The kernel is going to restart. Wait until it's finished before continuing to the next step. ⚠️</b>\n",
"</div>\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "6a5bea26f60f"
},
"source": [
"### Authenticate your notebook environment (Colab only)\n",
"\n",
"Authenticate your environment on Google Colab.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"id": "c97be6a73155"
},
"outputs": [],
"source": [
"import sys\n",
"\n",
"if \"google.colab\" in sys.modules:\n",
"\n",
" from google.colab import auth\n",
"\n",
" auth.authenticate_user()"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "2fxZn4SAbxdl"
},
"source": [
"### Select one of Camb AI models"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"id": "Y8X70FTSbx7U"
},
"outputs": [],
"source": [
"PUBLISHER_NAME = \"cambai\" # @param {type:\"string\"}\n",
"PUBLISHER_MODEL_NAME = \"mars7\"\n",
"available_regions = [\"us-central1\"]"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "bpuX3sKtexlK"
},
"source": [
"### Select a location and a version from the dropdown"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"id": "dHl8xW45ex_O"
},
"outputs": [],
"source": [
"import ipywidgets as widgets\n",
"from IPython.display import display\n",
"\n",
"dropdown_loc = widgets.Dropdown(\n",
" options=available_regions,\n",
" description=\"Select a location:\",\n",
" font_weight=\"bold\",\n",
" style={\"description_width\": \"initial\"},\n",
")\n",
"\n",
"\n",
"def dropdown_loc_eventhandler(change):\n",
" global LOCATION\n",
" if change[\"type\"] == \"change\" and change[\"name\"] == \"value\":\n",
" LOCATION = change.new\n",
" print(\"Selected:\", change.new)\n",
"\n",
"\n",
"LOCATION = dropdown_loc.value\n",
"dropdown_loc.observe(dropdown_loc_eventhandler, names=\"value\")\n",
"display(dropdown_loc)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "JZQFZJOY0LZr"
},
"source": [
"### Set Google Cloud project information\n",
"\n",
"To get started using Vertex AI, you must have an existing Google Cloud project and [enable the Vertex AI API](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com). Learn more about [setting up a project and a development environment](https://cloud.google.com/vertex-ai/docs/start/cloud-environment)."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"id": "Q7jkRBhB0LZr"
},
"outputs": [],
"source": [
"PROJECT_ID = \"[your-project-id]\" # @param {type:\"string\"}\n",
"ENDPOINT = f\"https://{LOCATION}-aiplatform.googleapis.com\"\n",
"\n",
"if not PROJECT_ID or PROJECT_ID == \"[your-project-id]\":\n",
" raise ValueError(\"Please set your PROJECT_ID\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "4NAstKRFBt4N"
},
"source": [
"### Import required libraries"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"id": "QZEFLE6a6bqy"
},
"outputs": [],
"source": [
"import base64\n",
"import json\n",
"import time"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "_FI94lP20LZr"
},
"source": [
"## Using Vertex AI API"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "qjsDpa8jlTRu"
},
"source": [
"### Upload Model"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "y1R2BRsBlu-k"
},
"outputs": [],
"source": [
"UPLOAD_MODEL_PAYLOAD = {\n",
" \"model\": {\n",
" \"displayName\": \"ModelGarden_LaunchPad_Model_\" + time.strftime(\"%Y%m%d-%H%M%S\"),\n",
" \"baseModelSource\": {\n",
" \"modelGardenSource\": {\n",
" \"publicModelName\": f\"publishers/{PUBLISHER_NAME}/models/{PUBLISHER_MODEL_NAME}\",\n",
" }\n",
" },\n",
" }\n",
"}\n",
"\n",
"request = json.dumps(UPLOAD_MODEL_PAYLOAD)\n",
"\n",
"! curl -X POST -H \"Authorization: Bearer $(gcloud auth print-access-token)\" -H \"Content-Type: application/json\" {ENDPOINT}/v1beta1/projects/{PROJECT_ID}/locations/{LOCATION}/models:upload -d '{request}'"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "V2j0nVGwlf9b"
},
"source": [
"### Get Model"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "bxwM0GXTmQhh"
},
"outputs": [],
"source": [
"MODEL_ID = [your - model - id] # @param {type: \"number\"}\n",
"\n",
"! curl -X GET -H \"Authorization: Bearer $(gcloud auth print-access-token)\" -H \"Content-Type: application/json\" {ENDPOINT}/v1/projects/{PROJECT_ID}/locations/{LOCATION}/models/{MODEL_ID}"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "3q3ygq8VlZAp"
},
"source": [
"### Create Endpoint"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "O1ChDOt7mPBQ"
},
"outputs": [],
"source": [
"CREATE_ENDPOINT_PAYLOAD = {\n",
" \"displayName\": \"ModelGarden_LaunchPad_Endpoint_\" + time.strftime(\"%Y%m%d-%H%M%S\"),\n",
"}\n",
"\n",
"request = json.dumps(CREATE_ENDPOINT_PAYLOAD)\n",
"\n",
"! curl -X POST -H \"Authorization: Bearer $(gcloud auth print-access-token)\" -H \"Content-Type: application/json\" {ENDPOINT}/v1/projects/{PROJECT_ID}/locations/{LOCATION}/endpoints -d '{request}'"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "GuMZCdhmlpCE"
},
"source": [
"### Get Endpoint"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "tHq_cLT6mPp_"
},
"outputs": [],
"source": [
"ENDPOINT_ID = [your - endpoint - id] # @param {type: \"number\"}\n",
"\n",
"! curl -X GET -H \"Authorization: Bearer $(gcloud auth print-access-token)\" -H \"Content-Type: application/json\" {ENDPOINT}/v1/projects/{PROJECT_ID}/locations/{LOCATION}/endpoints/{ENDPOINT_ID}"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "G0amEPXolbP7"
},
"source": [
"### Deploy Model"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"id": "Ucj-Xa-fpGrg"
},
"outputs": [],
"source": [
"MACHINE_TYPE = \"g2-standard-4\" # @param {type: \"string\"}\n",
"ACCELERATOR_TYPE = \"NVIDIA_L4\" # @param {type: \"string\"}\n",
"ACCELERATOR_COUNT = 1 # @param {type: \"number\"}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "VGTyCQQhlrAR"
},
"outputs": [],
"source": [
"DEPLOY_PAYLOAD = {\n",
" \"deployedModel\": {\n",
" \"model\": f\"projects/{PROJECT_ID}/locations/{LOCATION}/models/{MODEL_ID}\",\n",
" \"displayName\": \"ModelGarden_LaunchPad_DeployedModel_\"\n",
" + time.strftime(\"%Y%m%d-%H%M%S\"),\n",
" \"dedicatedResources\": {\n",
" \"machineSpec\": {\n",
" \"machineType\": MACHINE_TYPE,\n",
" \"acceleratorType\": ACCELERATOR_TYPE,\n",
" \"acceleratorCount\": ACCELERATOR_COUNT,\n",
" },\n",
" \"minReplicaCount\": 1,\n",
" \"maxReplicaCount\": 1,\n",
" },\n",
" },\n",
" \"trafficSplit\": {\"0\": 100},\n",
"}\n",
"\n",
"request = json.dumps(DEPLOY_PAYLOAD)\n",
"print(\"Request payload to Deploy Model:\")\n",
"print(json.dumps(DEPLOY_PAYLOAD, indent=2))\n",
"print(\"\\nResult:\")\n",
"! curl -X POST -H \"Authorization: Bearer $(gcloud auth print-access-token)\" -H \"Content-Type: application/json\" {ENDPOINT}/v1/projects/{PROJECT_ID}/locations/{LOCATION}/endpoints/{ENDPOINT_ID}:deployModel -d '{request}'"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "5ahw-uFjCAbo"
},
"source": [
"### Generate audio"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "61107099357a"
},
"source": [
"#### Unary call\n",
"\n",
"Sends a POST request to the specified API endpoint to get a response from the model for a joke using the provided payload."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "EOkEkRBiDUpH"
},
"outputs": [],
"source": [
"!curl -o ref.flac \"https://storage.googleapis.com/cambai-prod-public/public/ref.flac\"\n",
"print(\"Downloaded ref.flac\")\n",
"\n",
"text = \"The quick brown fox jumps over the lazy dog\"\n",
"audio_ref_path = \"ref.flac\"\n",
"ref_text = None\n",
"language = \"en-us\"\n",
"\n",
"# Prepare the JSON payload in a separate file\n",
"json_payload_file = \"payload.json\"\n",
"\n",
"with open(audio_ref_path, \"rb\") as audio_file:\n",
" encoded_string = base64.b64encode(audio_file.read()).decode(\"utf-8\")\n",
"\n",
"payload = {\n",
" \"text\": text,\n",
" \"audio_ref\": encoded_string,\n",
" \"ref_text\": ref_text,\n",
" \"language\": language,\n",
"}\n",
"\n",
"with open(json_payload_file, \"w\") as f:\n",
" json.dump(payload, f, indent=2)\n",
"\n",
"save_output_cmd = f\"\"\"\n",
"curl -X POST {ENDPOINT}/v1/projects/{PROJECT_ID}/locations/{LOCATION}/endpoints/{ENDPOINT_ID}:rawPredict \\\\\n",
" -H \"Content-Type: application/json\" \\\\\n",
" -H \"Authorization: Bearer $(gcloud auth print-access-token)\" \\\\\n",
" -d @{json_payload_file} > response.json\n",
"\n",
"# save audio\n",
"cat response.json | jq -r '.predictions[0]' | base64 -d > output.flac\n",
"\"\"\"\n",
"print(save_output_cmd)\n",
"!{save_output_cmd}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import soundfile as sf\n",
"import tempfile\n",
"from IPython.display import Audio\n",
"\n",
"def display_flac_in_notebook(path_to_flac):\n",
" data, samplerate = sf.read(path_to_flac)\n",
" with tempfile.NamedTemporaryFile(suffix=\".wav\", delete=False) as tmp:\n",
" sf.write(tmp.name, data, samplerate, format='WAV')\n",
" return Audio(filename=tmp.name)\n",
"\n",
"display_flac_in_notebook(\"output.flac\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "x2r2YeT70LZt"
},
"source": [
"## Using Vertex AI SDK for *Python*"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "kNons5WV0LZt"
},
"outputs": [],
"source": [
"from google.cloud import aiplatform"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "5RaYIOoR0LZt"
},
"outputs": [],
"source": [
"aiplatform.init(project=PROJECT_ID, location=LOCATION)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "4a4U1cTF0LZt"
},
"source": [
"### Upload Model"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "082M23Nb0LaA"
},
"outputs": [],
"source": [
"model = aiplatform.Model.upload(\n",
" display_name=\"ModelGarden_LaunchPad_Endpoint_\" + time.strftime(\"%Y%m%d-%H%M%S\"),\n",
" model_garden_source_model_name=f\"publishers/{PUBLISHER_NAME}/models/{PUBLISHER_MODEL_NAME}\",\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "-r3_PPU00LaA"
},
"source": [
"### Create Endpoint"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "LvRKh7dl0LaB"
},
"outputs": [],
"source": [
"my_endpoint = aiplatform.Endpoint.create(\n",
" display_name=\"ModelGarden_LaunchPad_Endpoint_\" + time.strftime(\"%Y%m%d-%H%M%S\")\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "IW9tGPMc0LaB"
},
"source": [
"### Deploy Model"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "PMIsYtTr0LaB"
},
"outputs": [],
"source": [
"MACHINE_TYPE = \"g2-standard-4\" # @param {type: \"string\"}\n",
"ACCELERATOR_TYPE = \"NVIDIA_L4\" # @param {type: \"string\"}\n",
"ACCELERATOR_COUNT = 1 # @param {type: \"number\"}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "AghmHYof0LaB"
},
"outputs": [],
"source": [
"model.deploy(\n",
" endpoint=my_endpoint,\n",
" deployed_model_display_name=\"ModelGarden_LaunchPad_DeployedModel_\"\n",
" + time.strftime(\"%Y%m%d-%H%M%S\"),\n",
" traffic_split={\"0\": 100},\n",
" machine_type=MACHINE_TYPE,\n",
" accelerator_type=ACCELERATOR_TYPE,\n",
" accelerator_count=ACCELERATOR_COUNT,\n",
" min_replica_count=1,\n",
" max_replica_count=1,\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "optmPVWs0LaB"
},
"source": [
"### Generate audio"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Stp-MvTy0LaB"
},
"source": [
"#### Unary call"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "rz1n3gfW0LaB"
},
"outputs": [],
"source": [
"def predict_custom_trained_model_sample(\n",
" instances: dict, target_file: str = \"output.flac\"\n",
"):\n",
" # If you created your endpoint using a different method you can set the value by uncommenting the next line and making sure ENDPOINT_ID is set\n",
" # my_endpoint = aiplatform.Endpoint(f'{ENDPOINT_ID}')\n",
" DATA = {\"instances\": [instances]}\n",
" response = my_endpoint.raw_predict(\n",
" body=json.dumps(DATA).encode(\"utf-8\"),\n",
" headers={\"Content-Type\": \"application/json\"},\n",
" )\n",
" with open(target_file, \"wb\") as f:\n",
" audio_bytes = base64.b64decode(json.loads(response.content)[\"predictions\"][0])\n",
" f.write(audio_bytes)\n",
"\n",
"\n",
"with open(\"ref.flac\", \"rb\") as f:\n",
" audio_ref_bytes = base64.b64encode(f.read()).decode(\"utf-8\")\n",
"\n",
"predict_custom_trained_model_sample(\n",
" instances={\n",
" \"text\": \"The quick brown fox jumps over the lazy dog\",\n",
" \"audio_ref\": audio_ref_bytes,\n",
" \"ref_text\": None,\n",
" \"language\": \"en-us\",\n",
" },\n",
" target_file=\"output.flac\",\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "UJyNltvFJeF9"
},
"source": [
"## Cleaning up"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "dCdn4H-jJlNP"
},
"source": [
"##### Cleaning up deployment resources\n",
"To clean up all Google Cloud resources used in this notebook, you can delete the Google Cloud project you used for the tutorial.\n",
"\n",
"Otherwise, you can delete the individual resources you created in this tutorial:\n",
"\n",
"* Model\n",
"* Endpoint\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "00wtIF8lJpKS"
},
"source": [
"##### Undeploy model"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "ovQSxWqfJmMs"
},
"outputs": [],
"source": [
"UNDEPLOY_PAYLOAD = {\"deployedModelId\": MODEL_ID}\n",
"\n",
"request = json.dumps(UNDEPLOY_PAYLOAD)\n",
"\n",
"! curl -X POST -H \"Authorization: Bearer $(gcloud auth print-access-token)\" -H \"Content-Type: application/json\" {ENDPOINT}/v1/projects/{PROJECT_ID}/locations/{LOCATION}/endpoints/{ENDPOINT_ID}:undeployModel -d '{request}'"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "nQ8ERh4uJvr4"
},
"source": [
"##### Delete Endpoint"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "zj37lITNJy-u"
},
"outputs": [],
"source": [
"! curl -X DELETE -H \"Authorization: Bearer $(gcloud auth print-access-token)\" -H \"Content-Type: application/json\" {ENDPOINT}/v1/projects/{PROJECT_ID}/locations/{LOCATION}/endpoints/{ENDPOINT_ID}"
]
}
],
"metadata": {
"colab": {
"name": "cambai_intro.ipynb",
"toc_visible": true
},
"kernelspec": {
"display_name": "Python 3",
"name": "python3"
}
},
"nbformat": 4,
"nbformat_minor": 0
}