> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pinecone.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Multimodal search

> Sample Next.js + FastAPI app for multimodal search across text, images, and videos using Pinecone and Google Vertex AI multimodal embeddings.

export const ArrowNE = () => {
  return <svg xmlns="http://www.w3.org/2000/svg" width="21" height="21" viewBox="0 0 21 21" fill="none">
            <path d="M4.92299 17.1668L3.8335 16.0773L14.5209 5.38992H4.71547V3.8335H17.1668V16.2849H15.6104V6.47941L4.92299 17.1668Z" fill="var(--text-primary)" />
        </svg>;
};

export const GithubIcon = () => {
  return <svg xmlns="http://www.w3.org/2000/svg" width="21" height="21" viewBox="0 0 21 21" fill="none">
            <path fill-rule="evenodd" clip-rule="evenodd" d="M9.70928 4.05007C8.31793 4.21017 6.96422 4.84637 5.94277 5.82016C4.24747 7.43636 3.52414 9.77037 4.00655 12.0678C4.46605 14.2561 6.02227 16.0844 8.11272 16.892C8.44405 17.0199 8.60214 17.0277 8.72515 16.9219L8.81336 16.8461L8.82292 16.1332L8.83247 15.4202L8.73955 15.4406C8.50614 15.4919 8.10282 15.5127 7.86284 15.4858C7.40903 15.435 7.01153 15.2388 6.79946 14.9611C6.73983 14.883 6.61481 14.6617 6.52166 14.4695C6.33116 14.0762 6.15107 13.8536 5.86758 13.6611C5.5353 13.4355 5.54995 13.2709 5.90191 13.2757C6.03736 13.2776 6.13771 13.3031 6.28618 13.3737C6.55388 13.5008 6.72474 13.6554 6.91734 13.9445C7.19338 14.3588 7.39605 14.5261 7.74282 14.6258C7.98116 14.6943 8.38716 14.6732 8.66027 14.5782L8.85297 14.5111L8.88852 14.332C8.93357 14.1048 9.04413 13.8664 9.17182 13.721C9.2859 13.591 9.28559 13.5908 8.97604 13.5556C8.70198 13.5243 8.21426 13.399 7.95469 13.2931C7.5852 13.1423 7.38567 13.008 7.08345 12.7068C6.57847 12.2034 6.3554 11.644 6.28281 10.6989C6.22063 9.88945 6.37347 9.29167 6.78619 8.73013L6.93636 8.5258L6.87447 8.29003C6.79346 7.98149 6.80996 7.41637 6.91027 7.06404C6.99339 6.77209 7.03036 6.74053 7.2572 6.76816C7.62556 6.81299 8.05296 6.98479 8.54616 7.28629L8.81336 7.44963L9.14344 7.38346C9.73562 7.26472 10.0045 7.24354 10.6681 7.26331C11.2515 7.28069 11.4102 7.29927 11.9297 7.4109L12.1225 7.45233L12.3541 7.30892C12.8263 7.01647 13.2498 6.83572 13.5901 6.78137C13.9054 6.73098 13.9339 6.74657 14.0112 7.01148C14.1466 7.47534 14.1575 8.05323 14.0375 8.40003C13.9956 8.52102 13.9958 8.52159 14.1525 8.72787C14.3401 8.97477 14.5238 9.35728 14.5959 9.65114C14.7536 10.2933 14.6496 11.354 14.3681 11.9748C14.0175 12.7481 13.4125 13.2118 12.4285 13.4617C12.2729 13.5012 12.0607 13.5432 11.9569 13.555C11.6405 13.5909 11.6372 13.5933 11.7462 13.708C11.848 13.8152 11.973 14.0466 12.0373 14.2471C12.0657 14.336 12.0829 14.7354 12.0959 15.6095L12.1141 16.8461L12.2023 16.9219C12.3253 17.0277 12.4834 17.0199 12.8148 16.892C15.1496 15.99 16.7734 13.8552 17.0364 11.3417C17.0826 10.9001 17.0576 9.99571 16.9878 9.5789C16.6755 7.71598 15.6173 6.10421 14.016 5.05281C13.2185 4.52921 12.1702 4.15856 11.171 4.0469C10.8383 4.00971 10.0449 4.01144 9.70928 4.05007Z" fill="var(--text-primary)" />
        </svg>;
};

export const InlineCode = ({copyCode, displayCode, copy}) => {
  const copyToClipboard = async e => {
    await navigator.clipboard.writeText(copyCode);
    const button = e.target.closest("button");
    button.innerHTML = `<svg  width="16" height="11" viewBox="0 0 16 11" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform: translateY(-3px)"><path d="M14.7813 1.21873C15.0751 1.51248 15.0751 1.98748 14.7813 2.2781L6.53135 10.5312C6.2376 10.825 5.7626 10.825 5.47197 10.5312L1.21885 6.28123C0.925098 5.98748 0.925098 5.51248 1.21885 5.22185C1.5126 4.93123 1.9876 4.9281 2.27822 5.22185L5.99697 8.9406L13.7188 1.21873C14.0126 0.924976 14.4876 0.924976 14.7782 1.21873H14.7813Z" fill="var(--brand-blue)"></path></svg>`;
    setTimeout(() => {
      button.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="21" height="20" viewBox="0 0 21 20" fill="none">
      <path d="M14.2502 0.833496H4.25016C3.3335 0.833496 2.5835 1.5835 2.5835 2.50016V14.1668H4.25016V2.50016H14.2502V0.833496ZM16.7502 4.16683H7.5835C6.66683 4.16683 5.91683 4.91683 5.91683 5.8335V17.5002C5.91683 18.4168 6.66683 19.1668 7.5835 19.1668H16.7502C17.6668 19.1668 18.4168 18.4168 18.4168 17.5002V5.8335C18.4168 4.91683 17.6668 4.16683 16.7502 4.16683ZM16.7502 17.5002H7.5835V5.8335H16.7502V17.5002Z" fill="var(--brand-blue)" fill-opacity="0.38"/>
      </svg>`;
    }, 2000);
  };
  return <div className="relative">
      <code className="inline-flex gap-2 items-center py-2 pl-3 pr-10 custom-code">
        {displayCode}
      </code>

      {copy && <div className="absolute group right-3 top-1/2 -translate-y-1/2" style={{
    width: "1.375rem",
    height: "1.375rem"
  }}>
          <button onClick={e => copyToClipboard(e)}>
            <svg xmlns="http://www.w3.org/2000/svg" width="21" height="20" viewBox="0 0 21 20" fill="none">
              <path d="M14.2502 0.833496H4.25016C3.3335 0.833496 2.5835 1.5835 2.5835 2.50016V14.1668H4.25016V2.50016H14.2502V0.833496ZM16.7502 4.16683H7.5835C6.66683 4.16683 5.91683 4.91683 5.91683 5.8335V17.5002C5.91683 18.4168 6.66683 19.1668 7.5835 19.1668H16.7502C17.6668 19.1668 18.4168 18.4168 18.4168 17.5002V5.8335C18.4168 4.91683 17.6668 4.16683 16.7502 4.16683ZM16.7502 17.5002H7.5835V5.8335H16.7502V17.5002Z" fill="var(--brand-blue)" className="opacity-40 group-hover:opacity-100 transition-opacity" />
            </svg>
          </button>
        </div>}
    </div>;
};

<div className="sample-app">
  <div className="sample-app-heading">
    <span className="eyebrow">SAMPLE APP</span>

    # Multimodal search

    Multimodal search across text, images, and videos

    <InlineCode copyCode="npx create-pinecone-app@latest --template shop-the-look" displayCode={<span><span style={{color: "var(--brand-blue)"}}>$</span> npx create-pinecone-app@latest --template shop-the-look</span>} copy />
  </div>

  <div className="w-full h-fit rounded-lg " style={{background: "var(--primary-dark)", margin: "4rem 0 3rem"}}>
    <div className="container py-50">
      <iframe id="sample-app-iframe" className="rounded-lg shadow-xl" src="https://shop-the-look.sample-app.pinecone.io/" width="100%" height="700px" allow="clipboard-write" allowTransparency="true" style={{border: "0.5px solid var(--border)"}} />
    </div>
  </div>

  <div className="sample-app-split">
    <div className="content no-margin">
      The Shop The Look app demonstrates how to build a multimodal search engine for finding outfit inspiration using [Pinecone Vector Database](https://www.pinecone.io/?utm_source=shop-the-look\&utm_medium=referral), Google Cloud Vertex AI's [Multimodal Embedding Model](https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings/get-multimodal-embeddings), and assets from [Pexels](https://www.pexels.com/). This application showcases how easy it is to use Pinecone in combining text, image, and video inputs to provide highly relevant outfit recommendations (or other multimodal use cases).
    </div>

    <div className="sidebar">
      <InlineCode copyCode="npx create-pinecone-app@latest --template shop-the-look" displayCode={<span><span style={{color: "#215CCE"}}>$</span> npx create-pinecone-app@latest --template shop-the-look</span>} copy />

      <a href="https://github.com/pinecone-io/sample-apps/tree/main/shop-the-look" target="_blank" className="flex items-center gap-2 no-underline mt-6"><GithubIcon /> Github</a>
      <a href="https://shop-the-look.sample-app.pinecone.io/" target="_blank" className="flex items-center gap-2 no-underline mt-4"><ArrowNE /> Open in a new window</a>
    </div>
  </div>

  ***

  <div className="sample-app-split">
    <div className="content relative mt-8 prose prose-gray dark:prose-invert">
      ## Built with

      * Pinecone Serverless
      * Google Cloud Vertex AI Multimodal Embedding Model
      * Next.js + Tailwind CSS
      * Vercel
      * NodeJS

      ***

      ## Run the sample app

      The fastest way to get started is to use the `create-pinecone-app` CLI tool to run **Shop The Look** in **demo** mode.

      ```bash theme={null}
      npx create-pinecone-app@latest --template shop-the-look
      ```

      ***Note:** Demo mode is for developers who want to quickly deploy and test the Shop The Look application without setting up their own backend services or supply their own image/video assets. This demo deployment includes over 45,000 royalty-free images and videos, allowing you to deploy the front-end locally, while utilizing our hosted backend API (which we have set up with all assets, Pinecone Serverless index, and Google Cloud Vertex AI).*

      ## Full deployment

      For developers who want to deploy a fully customizable **Shop The Look** application with their own images and videos, we offer a full deployment option. This method requires setting up both the frontend and backend components, including Pinecone Serverless, Google Cloud Vertex AI, and Google Cloud Storage, and uploading your own images and videos.

      A short version of the full deployment is listed below.

      **For the complete documentation, click [here](https://github.com/pinecone-io/sample-apps/tree/main/shop-the-look#%EF%B8%8F-full-deployment-30-minutes) for the Shop The Look Full Deployment instructions.**

      ### Get your Pinecone API key

      You need an API key to make calls to your Pinecone project:

      <div style={{minWidth: '450px', minHeight:'120px'}}>
        <div id="pinecone-connect-widget">
          <div class="connect-widget-skeleton">
            <div class="skeleton-content" />
          </div>
        </div>
      </div>

      Then copy your generated key:

      ```
      PINECONE_API_KEY="{{YOUR_API_KEY}}"
      ```

      Alternatively, follow these steps:

      1. Open the Pinecone console.
      2. Select your project.
      3. Go to **API Keys**.
      4. Copy your API key.

      ### Get your Google Cloud credentials

      **We recommend you follow the [full docs for setting up your Google Cloud credentials](https://github.com/pinecone-io/sample-apps/tree/main/shop-the-look#google-cloud-setup)**. Below is a summary of the instructions.

      1. Create an account in [Google Cloud](https://cloud.google.com/) if you don't already have one.
      2. Create a new project in the [Google Cloud Console](https://console.cloud.google.com/).
      3. Enable the `Vertex AI API` and `Cloud Storage API`.
      4. Create a service account with `Vertex AI User` and `Storage Object Viewer` roles.
      5. Generate and download a JSON key for the service account.

      ### Create a Pinecone serverless index

      Create a Pinecone index for this project with the following properties:

      * **dimension**: `1408`
      * **metric**: `cosine`
      * **region**: Choose your preferred region

      You can create the index [in the console](https://app.pinecone.io/organizations/-/projects/-/create-index/serverless?utm_source=shop-the-look\&utm_medium=referral), or by following the instructions [here](https://docs.pinecone.io/guides/get-started/quickstart#4-create-a-serverless-index).

      ### Start the project

      **Requires Node version 14+**

      #### Dependency installation

      From the project root directory, run the following command:

      ```bash theme={null}
      cd shop-the-look && npm install 
      ```

      Make sure you have populated the `.env.development` with relevant keys:

      ```bash theme={null}
      GOOGLE_CLOUD_PROJECT_ID="your-project-id"
      GOOGLE_CLOUD_PROJECT_LOCATION="your-region"
      GOOGLE_CLOUD_STORAGE_BUCKET_NAME="your-bucket-name"
      GOOGLE_CREDENTIALS_BASE64="your-base64-encoded-credentials"

      PINECONE_API_KEY="your-api-key"
      PINECONE_INDEX_NAME="your-index-name"
      PINECONE_TOP_K=20
      ```

      Start the app:

      ```bash theme={null}
      npm run dev
      ```

      ## Project structure

      <img src="https://mintcdn.com/pinecone/r0TaYXrfSrAYZYUj/images/shop-the-look-architecture-diagram.png?fit=max&auto=format&n=r0TaYXrfSrAYZYUj&q=85&s=bc6db4fe8023bbddd6d3d1b104b2b4de" alt="title" width="1136" height="1115" data-path="images/shop-the-look-architecture-diagram.png" />

      Shop The Look uses a NextJS frontend with a FastAPI Python backend. Utility scripts are included to process images and videos for embedding + upserting.

      **Frontend Client**

      The frontend uses Next.js, Tailwind CSS, and custom React components to power the search experience. It leverages custom API routes to make calls to the FastAPI backend for embedding and searching.

      **Backend Server**

      This project uses FastAPI to handle image/video processing, embedding generation, and Pinecone operations.

      **Utility Scripts**

      The image and video embedding processing scripts process a folder of images and videos from Google Cloud Storage, generate embeddings using Vertex AI, and upserts them to Pinecone Index.

      ***

      ### Architecture

      Shop The Look is built using [Pinecone Vector Database](https://www.pinecone.io/?utm_source=shop-the-look\&utm_medium=referral), Google Cloud Vertex AI's [Multimodal Embedding Model](https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings/get-multimodal-embeddings), assets from [Pexels](https://www.pexels.com/), and is hosted on [Vercel](https://vercel.com/).

      Using pseudocode, we will explain how we built key components of Shop The Look.

      **Pinecone Serverless**

      This project uses Pinecone to store all the multimodal embeddings generated by Vertex AI's Multimodal Embedding Model.

      ```python theme={null}
      pc = Pinecone(api_key)
      index = pc.Index(index_name)

      vector = [
          {
              'id': embedding_id,
              'values': embedding_values,
              'metadata': {
                  ...
              }
          }
      ]
      index.upsert(vector)
      ```

      **Google Cloud Vertex AI integration**

      This app uses Google Cloud Vertex AI's Multimodal Embedding Model to generate embeddings for text, images, and videos:

      ```python theme={null}
      embeddings = model.get_embeddings(
          asset=asset,
      )
      ```

      **Search functionality**

      When the user executes a search, their query (text or image/video) is sent to the backend, which uses the Multimodal Embedding Model to convert the query into vector embeddings:

      ```python theme={null}
      url, headers, data = get_embedding_request_data(access_token, asset, query)

      response = requests.post(url, headers, data)

      embedding_data = response.json()
      vector = embedding_data['predictions'][0]['assetEmbedding']
      ```

      These embeddings are then used to perform a similarity search in Pinecone:

      ```python theme={null}
      query_response = index.query(
          vector=vector,
          top_k=top_k,
          include_metadata=True
      )

      matches = query_response['matches']
      ```

      The results are then displayed in the React frontend [`page.tsx`](https://github.com/pinecone-io/sample-apps/blob/main/shop-the-look/app/page.tsx)

      ***

      ## Troubleshooting

      Experiencing any issues with the sample app?
      Read the [Troubleshooting](https://github.com/pinecone-io/sample-apps/tree/main/shop-the-look#-troubleshooting) section of the Shop The Look README. If issues still persist, [submit an issue, create a PR](https://github.com/pinecone-io/sample-apps/), or post in our [community forum](https://community.pinecone.io?utm_source=shop-the-look\&utm_medium=referral)!
    </div>

    <div className="sidebar toc" />
  </div>
</div>
