DEV Community

Cover image for Adding Embeddings to a Phoenix App
Byron Salty
Byron Salty

Posted on

Adding Embeddings to a Phoenix App

Let's add OpenAI based Embeddings to a Phoenix app and store the vector in the database so we can use it for similarity searches.

This resulting code for this article is captured in this sample project:
https://github.com/byronsalty/questify

Specifically this commit:
https://github.com/byronsalty/questify/commit/cba277ce8104c6f3b29dc2a6f602158ba4d1f62a


Steps:

  1. Add PG Vector to deps
  2. Add a migration to install Vector db extension
  3. Add Postgrex.Types
  4. Configure OpenAI embedding endpoints
  5. Hit the API endpoint
  6. Setup your schema for vector types
  7. Incorporate vectors into your queries

Add PG Vector to deps

In mix.exs:

      {:pgvector, "~> 0.2.0"},
Enter fullscreen mode Exit fullscreen mode

Add Migration to install the vector extension

Create a migration to install the PGVector extension:

defmodule Questify.Repo.Migrations.AddPgVector do
  use Ecto.Migration

  def up do
    execute "CREATE EXTENSION IF NOT EXISTS vector"
  end

  def down do
    execute "DROP EXTENSION vector"
  end
end
Enter fullscreen mode Exit fullscreen mode

Add Postgrex.Types

This is so you can use the term vector in your type definitions.

Add a file postgrex_types.ex in your /lib folder with the following contents:

Postgrex.Types.define(
  Questify.PostgrexTypes,
  [Pgvector.Extensions.Vector] ++ Ecto.Adapters.Postgres.extensions(),
  []
)
Enter fullscreen mode Exit fullscreen mode

Add this to your config.exs:

config :questify, Questify.Repo, types: Questify.PostgrexTypes
Enter fullscreen mode Exit fullscreen mode

Configure your app with the OpenAI endpoints

Here are the relevant lines to add to your configuration. I put this into the runtime.exs file:

openai_api_key = System.get_env("OPENAI_API_KEY") ||
  raise """
  environment variable OPENAI_API_KEY is missing
  """

config :questify, :openai,
  openai_api_key: openai_api_key,
  embedding_url: "https://api.openai.com/v1/embeddings",
  embedding_model: "text-embedding-ada-002"
Enter fullscreen mode Exit fullscreen mode

Hit the API

The embedding API will simply take in a text string and will return a JSON response with the embedding information.

You can hit the API with HTTPoison like so:

    response =
      HTTPoison.post(
        embedding_url,
        Jason.encode!(%{
          input: text,
          model: Keyword.get(opts, :model, embedding_model)
        }),
        [
          {"Content-Type", "application/json"},
          {"Authorization", "Bearer #{openai_api_key}"}
        ]
      )
Enter fullscreen mode Exit fullscreen mode

See the whole file here: https://github.com/byronsalty/questify/blob/cba277ce8104c6f3b29dc2a6f602158ba4d1f62a/lib/questify/embeddings.ex

Add a vector to one of your schemas

Add a migration like this:

defmodule Questify.Repo.Migrations.AddCommandEmbeddings do
  use Ecto.Migration

  def change do
    alter table(:actions) do
      add :embedding, :vector, size: 1536
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Then update your schema with a new vector type field like this:

    field :embedding, Pgvector.Ecto.Vector

Enter fullscreen mode Exit fullscreen mode

Incorporate Vectors into Queries

You'll need to add this line at the top of an model where you plan to query with PGVector specific functions - importantly including the "distance" ones that we're looking to use.

  import Pgvector.Ecto.Query
Enter fullscreen mode Exit fullscreen mode

Now you can query like the following:

  def get_action_by_text(location, text) do
    embedding = Questify.Embeddings.embed!(text)
    min_distance = 1.0

    Repo.all(
      from a in Action,
        order_by: cosine_distance(a.embedding, ^embedding),
        limit: 1,
        where: cosine_distance(a.embedding, ^embedding) < ^min_distance,
        where: a.from_id == ^location.id
    )
  end
Enter fullscreen mode Exit fullscreen mode

Follow along for more ways to add LLM related capabilities into your Phoenix apps.

See more related content here.

Top comments (0)