{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "# Code Search on Kubeflow\n",
    "\n",
    "This notebook implements an end-to-end Semantic Code Search on top of [Kubeflow](https://www.kubeflow.org/) - given an input query string, get a list of code snippets semantically similar to the query string.\n",
    "\n",
    "**NOTE**: If you haven't already, see [kubeflow/examples/code_search](https://github.com/kubeflow/examples/tree/master/code_search) for instructions on how to get this notebook,."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Install dependencies\n",
    "\n",
    "Let us install all the Python dependencies. Note that everything must be done with `Python 2`. This will take a while the first time."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Verify Version Information"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "echo \"Pip Version Info: \" && python2 --version && python2 -m pip --version && echo\n",
    "echo \"Google Cloud SDK Info: \" && gcloud --version && echo\n",
    "echo \"Ksonnet Version Info: \" && ks version && echo\n",
    "echo \"Kubectl Version Info: \" && kubectl version"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Install Pip Packages"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "! python2 -m pip install -U pip"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Code Search dependencies\n",
    "! python2 -m pip install --user https://github.com/kubeflow/batch-predict/tarball/master\n",
    "! python2 -m pip install --user -r src/requirements.txt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# BigQuery Cell Dependencies\n",
    "! python2 -m pip install --user pandas-gbq"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# NOTE: The RuntimeWarnings (if any) are harmless. See ContinuumIO/anaconda-issues#6678.\n",
    "from pandas.io import gbq"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Configure Variables\n",
    "\n",
    "* This involves setting up the Ksonnet application as well as utility environment variables for various CLI steps.\n",
    "* Set the following variables\n",
    "\n",
    "  * **PROJECT**: Set this to the GCP project you want to use\n",
    "      * If gcloud has a project set this will be used by default\n",
    "      * To use a different project or if gcloud doesn't have a project set you will need to configure one explicitly\n",
    "  * **WORKING_DIR**: Override this if you don't want to use the default configured below     \n",
    "  * **KS_ENV**: Set this to the name of the ksonnet environment you want to create"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import getpass\n",
    "import subprocess\n",
    "# Configuration Variables. Modify as desired.\n",
    "\n",
    "PROJECT = subprocess.check_output([\"gcloud\", \"config\", \"get-value\", \"project\"]).strip()\n",
    "\n",
    "# Dataflow Related Variables.\n",
    "TARGET_DATASET = 'code_search'\n",
    "WORKING_DIR = 'gs://{0}_code_search/workingDir'.format(PROJECT)\n",
    "KS_ENV=getpass.getuser()\n",
    "\n",
    "# DO NOT MODIFY. These are environment variables to be used in a bash shell.\n",
    "%env PROJECT $PROJECT\n",
    "%env TARGET_DATASET $TARGET_DATASET\n",
    "%env WORKING_DIR $WORKING_DIR"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  Setup Authorization\n",
    "\n",
    "In a Kubeflow cluster on GKE, we already have the Google Application Credentials mounted onto each Pod. We can simply point `gcloud` to activate that service account."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "# Activate Service Account provided by Kubeflow.\n",
    "gcloud auth activate-service-account --key-file=${GOOGLE_APPLICATION_CREDENTIALS}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Additionally, to interact with the underlying cluster, we configure `kubectl`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "kubectl config set-cluster kubeflow --server=https://kubernetes.default --certificate-authority=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt\n",
    "kubectl config set-credentials jupyter --token \"$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)\"\n",
    "kubectl config set-context kubeflow --cluster kubeflow --user jupyter\n",
    "kubectl config use-context kubeflow"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Collectively, these allow us to interact with Google Cloud Services as well as the Kubernetes Cluster directly to submit `TFJob`s and execute `Dataflow` pipelines."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Setup Ksonnet Application\n",
    "\n",
    "We now point the Ksonnet application to the underlying Kubernetes cluster."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "cd kubeflow\n",
    "\n",
    "# Update Ksonnet to point to the Kubernetes Cluster\n",
    "ks env add code-search\n",
    "\n",
    "# Update Ksonnet to use the namespace where kubeflow is deployed. By default it's 'kubeflow'\n",
    "ks env set code-search --namespace=kubeflow\n",
    "\n",
    "# Update the Working Directory of the application\n",
    "sed -i'' \"s,gs://example/prefix,${WORKING_DIR},\" components/params.libsonnet\n",
    "\n",
    "# FIXME(sanyamkapoor): This command completely replaces previous configurations.\n",
    "# Hence, using string replacement in file.\n",
    "# ks param set t2t-code-search workingDir ${WORKING_DIR}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## View Github Files\n",
    "\n",
    "This is the query that is run as the first step of the Pre-Processing pipeline and is sent through a set of transformations. This is illustrative of the rows being processed in the pipeline we trigger next.\n",
    "\n",
    "**WARNING**: The table is large and the query can take a few minutes to complete."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "query = \"\"\"\n",
    "  SELECT\n",
    "    MAX(CONCAT(f.repo_name, ' ', f.path)) AS repo_path,\n",
    "    c.content\n",
    "  FROM\n",
    "    `bigquery-public-data.github_repos.files` AS f\n",
    "  JOIN\n",
    "    `bigquery-public-data.github_repos.contents` AS c\n",
    "  ON\n",
    "    f.id = c.id\n",
    "  JOIN (\n",
    "      --this part of the query makes sure repo is watched at least twice since 2017\n",
    "    SELECT\n",
    "      repo\n",
    "    FROM (\n",
    "      SELECT\n",
    "        repo.name AS repo\n",
    "      FROM\n",
    "        `githubarchive.year.2017`\n",
    "      WHERE\n",
    "        type=\"WatchEvent\"\n",
    "      UNION ALL\n",
    "      SELECT\n",
    "        repo.name AS repo\n",
    "      FROM\n",
    "        `githubarchive.month.2018*`\n",
    "      WHERE\n",
    "        type=\"WatchEvent\" )\n",
    "    GROUP BY\n",
    "      1\n",
    "    HAVING\n",
    "      COUNT(*) >= 2 ) AS r\n",
    "  ON\n",
    "    f.repo_name = r.repo\n",
    "  WHERE\n",
    "    f.path LIKE '%.py' AND --with python extension\n",
    "    c.size < 15000 AND --get rid of ridiculously long files\n",
    "    REGEXP_CONTAINS(c.content, r'def ') --contains function definition\n",
    "  GROUP BY\n",
    "    c.content\n",
    "  LIMIT\n",
    "    10\n",
    "\"\"\"\n",
    "\n",
    "gbq.read_gbq(query, dialect='standard', project_id=PROJECT)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Define an experiment\n",
    "\n",
    "* This solution consists of multiple jobs and servers that need to share parameters\n",
    "* To facilitate this we use [experiments.libsonnet](https://github.com/kubeflow/examples/blob/master/code_search/kubeflow/components/experiments.libsonnet) to define sets of parameters\n",
    "* Each set of parameters has a name corresponding to a key in the dictionary defined in **experiments.libsonnet**\n",
    "* You configure an experiment by defining a set of experiments in **experiments.libsonnet**\n",
    "* You then set the global parameter **experiment** to the name of the experiment defining the parameters you want\n",
    "  to use.\n",
    "\n",
    "To get started define your experiment\n",
    "\n",
    "* Create a new entry containing a set of values to be used for your experiment\n",
    "* Pick a suitable name for your experiment.\n",
    "* Set the following values\n",
    "\n",
    "    * **outputDir**: The GCS directory where the output should be written\n",
    "    * **train_steps**: Numer oftraining steps\n",
    "    * **eval_steps**: Number of steps to be used for eval\n",
    "    * **hparams_set**: The set of hyperparameters to use; see some suggestions [here](https://github.com/tensorflow/tensor2tensor#language-modeling).\n",
    "    \n",
    "        * **transformer_tiny** can be used to train a very small model suitable for ensuring the code works.\n",
    "    * **project**: Set this to the GCP project you can use\n",
    "    * **modelDir**: \n",
    "        * After training your model set this to a GCS directory containing the export model\n",
    "        * e.g gs://code-search-demo/models/20181107-dist-sync-gpu/export/1541712907/\n",
    "    * **problem**: set this to \"kf_github_function_docstring\",\n",
    "    * **model**: set this  \"kf_similarity_transformer\",\n",
    "    * **lookupFile**: set this to the GCS location of the CSV produced by the job to create the nmslib index of the embeddings for all GitHub data\n",
    "    * **indexFile*:* set this to the GCS location of the nmslib index for all the data in GitHub\n",
    "\n",
    "Configure your ksonnet environment to use your experiment\n",
    "\n",
    "* Open **kubeflow/environments/${ENVIRONMENT}/globals.libsonnet**\n",
    "* Define the following global parameters\n",
    "\n",
    "  * **experiment**: Name of your experiment; should correspond to a key defined in **experiments.libsonnet**\n",
    "  * **project**: Set this to the GCP project you can use\n",
    "  * **dataDir**: The data directory to be used by T2T  \n",
    "  * **workingDir**: Working directory\n",
    "  \n",
    "* Here's an [example](https://github.com/kubeflow/examples/blob/master/code_search/kubeflow/environments/cs_demo/globals.libsonnet) of what the contents should look like\n",
    "\n",
    "  ```\n",
    "  workingDir: \"gs://code-search-demo/20181104\",\n",
    "  dataDir: \"gs://code-search-demo/20181104/data\",\n",
    "  project: \"code-search-demo\",\n",
    "  experiment: \"demo-trainer-11-07-dist-sync-gpu\",\n",
    "  ```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Pre-Processing Github Files\n",
    "\n",
    "In this step, we use [Google Cloud Dataflow](https://cloud.google.com/dataflow/) to preprocess the data.\n",
    "\n",
    "* We use a K8s Job to run a python program `code_search.dataflow.cli.preprocess_github_dataset` that submits the Dataflow job\n",
    "* Once the job has been created it can be monitored using the Dataflow console\n",
    "* The parameter **target_dataset** specifies a BigQuery dataset to write the data to"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create the BigQuery dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "bq mk ${PROJECT}:${TARGET_DATASET}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Submit the Dataflow Job"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "cd kubeflow\n",
    "ks param set --env=code-search submit-preprocess-job targetDataset ${TARGET_DATASET}\n",
    "ks apply code-search -c submit-preprocess-job"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When completed successfully, this should create a dataset in `BigQuery` named `target_dataset`. Additionally, it also dumps CSV files into `data_dir` which contain training samples (pairs of function and docstrings) for our Tensorflow Model. A representative set of results can be viewed using the following query."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "query = \"\"\"\n",
    "  SELECT * \n",
    "  FROM \n",
    "    {}.token_pairs\n",
    "  LIMIT\n",
    "    10\n",
    "\"\"\".format(TARGET_DATASET)\n",
    "\n",
    "gbq.read_gbq(query, dialect='standard', project_id=PROJECT)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This pipeline also writes a set of CSV files which contain function and docstring pairs delimited by a comma. Here, we list a subset of them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "LIMIT=10\n",
    "\n",
    "gsutil ls ${WORKING_DIR}/data/*.csv | head -n ${LIMIT}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Prepare Dataset for Training\n",
    "\n",
    "We will use `t2t-datagen` to convert the transformed data above into the `TFRecord` format.\n",
    "\n",
    "**TIP**: Use `ks show` to view the Resource Spec submitted."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "cd kubeflow\n",
    "\n",
    "ks show code-search -c t2t-code-search-datagen"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "cd kubeflow\n",
    "\n",
    "ks apply code-search -c t2t-code-search-datagen"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once this job finishes, the data directory should have a vocabulary file and a list of `TFRecords` prefixed by the problem name which in our case is `github_function_docstring_extended`. Here, we list a subset of them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "LIMIT=10\n",
    "\n",
    "gsutil ls ${WORKING_DIR}/data/vocab*\n",
    "gsutil ls ${WORKING_DIR}/data/*train* | head -n ${LIMIT}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Execute Tensorflow Training\n",
    "\n",
    "Once, the `TFRecords` are generated, we will use `t2t-trainer` to execute the training."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "cd kubeflow\n",
    "\n",
    "ks show code-search -c t2t-code-search-trainer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "cd kubeflow\n",
    "\n",
    "ks apply code-search -c t2t-code-search-trainer"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This will generate TensorFlow model checkpoints which is illustrated below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "gsutil ls ${WORKING_DIR}/output/*ckpt*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Export Tensorflow Model\n",
    "\n",
    "We now use `t2t-exporter` to export the `TFModel`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "cd kubeflow\n",
    "\n",
    "ks show code-search -c t2t-code-search-exporter"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "cd kubeflow\n",
    "\n",
    "ks apply code-search -c t2t-code-search-exporter"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once completed, this will generate a TensorFlow `SavedModel` which we will further use for both online (via `TF Serving`) and offline inference (via `Kubeflow Batch Prediction`)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "gsutil ls ${WORKING_DIR}/output/export/Servo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Compute Function Embeddings\n",
    "\n",
    "In this step, we will use the exported model above to compute function embeddings via another `Dataflow` pipeline. A `Python 2` module `code_search.dataflow.cli.create_function_embeddings` has been provided for this purpose. A list of all possible arguments can be seen below."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Configuration\n",
    "\n",
    "First, select a Exported Model version from the `${WORKING_DIR}/output/export/Servo` as seen above. This should be name of a folder with UNIX Seconds Timestamp like `1533685294`. Below, we automatically do that by selecting the folder which represents the latest timestamp."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash --out EXPORT_DIR_LS\n",
    "\n",
    "gsutil ls ${WORKING_DIR}/output/export/Servo | grep -oE \"([0-9]+)/$\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# WARNING: This routine will fail if no export has been completed successfully.\n",
    "MODEL_VERSION = max([int(ts[:-1]) for ts in EXPORT_DIR_LS.split('\\n') if ts])\n",
    "\n",
    "# DO NOT MODIFY. These are environment variables to be used in a bash shell.\n",
    "%env MODEL_VERSION $MODEL_VERSION"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* Modify experiments.libsonnet and set **modelDir** to the directory computed above"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Run the Dataflow Job for Function Embeddings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "cd kubeflow\n",
    "ks apply code-search -c submit-code-embeddings-job        "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When completed successfully, this should create another table in the same `BigQuery` dataset which contains the function embeddings for each existing data sample available from the previous Dataflow Job. Additionally, it also dumps a CSV file containing metadata for each of the function and its embeddings. A representative query result is shown below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "query = \"\"\"\n",
    "  SELECT * \n",
    "  FROM \n",
    "    {}.function_embeddings\n",
    "  LIMIT\n",
    "    10\n",
    "\"\"\".format(TARGET_DATASET)\n",
    "\n",
    "gbq.read_gbq(query, dialect='standard', project_id=PROJECT)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The pipeline also generates a set of CSV files which will be useful to generate the search index."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "LIMIT=10\n",
    "\n",
    "gsutil ls ${WORKING_DIR}/data/*index*.csv | head -n ${LIMIT}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create Search Index\n",
    "\n",
    "We now create the Search Index from the computed embeddings. This facilitates k-Nearest Neighbor search to for semantically similar results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "cd kubeflow\n",
    "\n",
    "ks show code-search -c search-index-creator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "cd kubeflow\n",
    "\n",
    "ks apply code-search -c search-index-creator"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Using the CSV files generated from the previous step, this creates an index using [NMSLib](https://github.com/nmslib/nmslib). A unified CSV file containing all the code examples for a human-readable reverse lookup during the query, is also created."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "gsutil ls ${WORKING_DIR}/code_search_index*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Deploy the Web App\n",
    "\n",
    "* The included web app provides a simple way for users to submit queries\n",
    "* The web app includes two pieces\n",
    "\n",
    "     * A Flask app that serves a simple UI for sending queries\n",
    "        \n",
    "        * The flask app also uses nmslib to provide fast lookups\n",
    "        \n",
    "     * A TF Serving instance to compute the embeddings for search queries\n",
    "* The ksonnet components for the web app are in a separate ksonnet application ks-web-app\n",
    "\n",
    "    * A second web app is used so that we can optionally use [ArgoCD](https://github.com/argoproj/argo-cd) to keep the serving components up to date."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Deploy an Inference Server\n",
    "\n",
    "We've seen offline inference during the computation of embeddings. For online inference, we deploy the exported Tensorflow model above using [Tensorflow Serving](https://www.tensorflow.org/serving/).\n",
    "\n",
    "* You need to set the parameter **modelBasePath** to the GCS directory where the model was exported\n",
    "* This will be a directory produced by the export model step\n",
    "    * e.g gs://code-search-demo/models/20181107-dist-sync-gpu/export/\n",
    "* Here are sample contents\n",
    "  ```\n",
    "  gs://code-search-demo/models/20181107-dist-sync-gpu/export/:\n",
    "  gs://code-search-demo/models/20181107-dist-sync-gpu/export/\n",
    "\n",
    "  gs://code-search-demo/models/20181107-dist-sync-gpu/export/1541712907/:\n",
    "  gs://code-search-demo/models/20181107-dist-sync-gpu/export/1541712907/\n",
    "  gs://code-search-demo/models/20181107-dist-sync-gpu/export/1541712907/saved_model.pbtxt\n",
    "\n",
    "  gs://code-search-demo/models/20181107-dist-sync-gpu/export/1541712907/variables/:\n",
    "  gs://code-search-demo/models/20181107-dist-sync-gpu/export/1541712907/variables/\n",
    "  gs://code-search-demo/models/20181107-dist-sync-gpu/export/1541712907/variables/variables.data-00000-of-00001\n",
    "  gs://code-search-demo/models/20181107-dist-sync-gpu/export/1541712907/variables/variables.index\n",
    "\n",
    "  ```\n",
    "\n",
    "* TFServing expects the modelBasePath to consist of numeric subdirectories corresponding to different versions of the model.\n",
    "* Each subdirectory will contain the saved model in protocol buffer along with the weights. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "cd ks-web-app\n",
    "\n",
    "ks param set --env=code-search modelBasePath ${MODEL_BASE_PATH}\n",
    "ks show code-search -c query-embed-server"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "cd ks-web-app\n",
    "\n",
    "ks apply code-search -c query-embed-server"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Deploy Search UI\n",
    "\n",
    "We finally deploy the Search UI which allows the user to input arbitrary strings and see a list of results corresponding to semantically similar Python functions. This internally uses the inference server we just deployed.\n",
    "\n",
    "* We need to configure the index server to use the lookup file and CSV produced by the create search index step above\n",
    "* The values will be the values of the parameters **lookupFile** and **indexFile** that you set in experiments.libsonnet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "cd ks-web-app\n",
    "ks param set --env=code-search search-index-server lookupFile ${LOOKUP_FILE}\n",
    "ks param set --env=code-search search-index-server indexFile ${INDEX_FILE}\n",
    "ks show code-search -c search-index-server"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "cd kubeflow\n",
    "\n",
    "ks apply code-search -c search-index-server"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The service should now be available at FQDN of the Kubeflow cluster at path `/code-search/`."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
