Ash ai error when generating embddding
When trying to use Ash AI to generate a embedding, I got the error:
* (Oban.CrashError) (throw) {DBConnection, #Reference<0.1746484685.788004865.129140>, #Ash.Changeset<domain: Qst.Templates, action_type: :update, action: :ash_ai_update_embeddings, attributes: %{full_text_vector: Ash.Vector.new([0.007031387183815241, -0.009794930927455425, ...]), updated_at: ~U[2025-06-21 06:34:27.031518Z]}, relationships: %{}, errors: [%Ash.Error.Unknown.UnknownError{error: "** (FunctionClauseError) no function clause matching in Pgvector.new/1", field: nil, value: nil, splode: Ash.Error, bread_crumbs: [], vars: [], path: [], stacktrace: #splode.Stacktrace<>, class: :unknown}], data: %Qst.Templates.QuestTemplate{id: "97eee691-64df-47e0-900a-9e8eaff83a25", name: "Dragon Slayer", description: "Epic dragon quest", inserted_at: ~U[2025-06-21 06:34:26.256243Z], updated_at: ~U[2025-06-21 06:34:26.256243Z], creator_id: "0a622c8d-6d03-4ca6-bef9-84795f3d8d7d", full_text_vector: #Ash.NotLoaded<:attribute, field: :full_text_vector>, task_templates: #Ash.NotLoaded<:relationship, field: :task_templates>, creator: #Ash.NotLoaded<:relationship, field: :creator>, meta: #Ecto.Schema.Metadata<:loaded, "quest_templates">}, context: %{changed?: true}, valid?: false>}
It seems the embedding is returned correctly but can't convert to Pgvector. Does anyone has similar issue? Thanks a lot.
14 Replies
What does your embedding model look like?
defmodule Qst.Ai.GeminiEmbeddingModel do
use AshAi.EmbeddingModel
@dimensions 768
@endpoint "https://generativelanguage.googleapis.com/v1beta/models/embedding-001:embedContent"
@impl true
def dimensions(_opts), do: @dimensions
@impl true
def generate(texts, _opts) when is_list(texts) do
api_key = System.fetch_env!("GEMINI_API_KEY")
body = %{"content" => %{"parts" => [%{"text" => Enum.join(texts, "\n\n")}]}}
case Req.post(@endpoint <> "?key=" <> api_key, json: body) do
{:ok, %Req.Response{status: 200, body: %{"embedding" => %{"values" => values}}}} ->
{:ok, [values]}
{:ok, %Req.Response{status: status, body: body}} ->
{:error, {status, body}}
{:error, error} ->
{:error, error}
end
end
end
vectorize do
full_text do
text(fn r -> "#{r.name} #{r.description}" end)
used_attributes([:name, :description])
end
strategy :after_action
embedding_model GeminiEmbeddingModel
end
And above is the vectorize part. I use the strategy :after_action when testing.
You want to return one vector embedding per input
So I think maybe you are doing
[values]
But you just want values
The values is indeed a list of float. However, if I only return values, I will get the following error:
Unknown Error
* ** (FunctionClauseError) no function clause matching in Ash.Vector.new/1
(ash 3.5.23) lib/ash/vector.ex:19: Ash.Vector.new(0.007031387)
(ash 3.5.23) lib/ash/type/vector.ex:37: Ash.Type.Vector.cast_input/2
(ash 3.5.23) lib/ash/type/type.ex:1025: Ash.Type.cast_input/3
(ash 3.5.23) lib/ash/changeset/changeset.ex:6214: Ash.Changeset.do_change_attribute/4
(elixir 1.18.4) lib/enum.ex:2546: Enum."-reduce/3-lists^foldl/2-0-"/3
(ash 3.5.23) lib/ash/changeset/changeset.ex:3256: Ash.Changeset.run_change_or_validation/6
(ash 3.5.23) lib/ash/changeset/changeset.ex:3109: anonymous fn/6 in Ash.Changeset.run_action_changes/6
(elixir 1.18.4) lib/enum.ex:2546: Enum."-reduce/3-lists^foldl/2-0-"/3
(ash 3.5.23) lib/ash/changeset/changeset.ex:3105: Ash.Changeset.run_action_changes/6
(ash 3.5.23) lib/ash/changeset/changeset.ex:2544: Ash.Changeset.do_for_action/4
(qst 0.1.0) deps/ash_oban/lib/transformers/define_schedulers.ex:1027: Qst.Templates.QuestTemplate.AshOban.Worker.ReindexEmbedding.perform/1
(oban 2.19.4) lib/oban/queue/executor.ex:145: Oban.Queue.Executor.perform/1
(oban 2.19.4) lib/oban/queue/executor.ex:77: Oban.Queue.Executor.call/1
(oban 2.19.4) lib/oban/engines/inline.ex:97: Oban.Engines.Inline.execute_job/2
(oban 2.19.4) lib/oban/engines/inline.ex:37: Oban.Engines.Inline.insert_job/3
(oban 2.19.4) lib/oban/engine.ex:210: anonymous fn/4 in Oban.Engine.insert_job/3
(oban 2.19.4) lib/oban/engine.ex:387: anonymous fn/3 in Oban.Engine.with_span/4
Ah, okay, so
I don't know how Gemini works
But it looks like you're joining the texts into one string
You need to return a list of one vector per string in the input list
#Ash.Changeset<
domain: Qst.Templates,
action_type: :update,
action: :ash_ai_update_embeddings,
attributes: %{
vectorized_name: Ash.Vector.new([-0.09240056574344635, 0.006294603459537029,
-0.012871873565018177]),
vectorized_description: Ash.Vector.new([-0.08480250090360641,
0.10270774364471436, -0.027802953496575356]),
full_text_vector: Ash.Vector.new([-0.0896868035197258, 0.012641902081668377,
-8.565243915654719e-4])
},
relationships: %{},
errors: [],
data: %Qst.Templates.QuestTemplate{
id: "29a256f5-97b7-4566-abc4-21346436a362",
name: "Dragon Slayer",
description: "Epic dragon quest",
inserted_at: ~U[2025-06-21 19:58:21.762675Z],
updated_at: ~U[2025-06-21 19:58:21.762675Z],
creator_id: "f1e2e966-1faf-4776-a90e-c36d3ca65295",
vectorized_name: #Ash.NotLoaded<:attribute, field: :vectorized_name>,
vectorized_description: #Ash.NotLoaded<:attribute, field: :vectorized_description>,
full_text_vector: #Ash.NotLoaded<:attribute, field: :full_text_vector>,
task_templates: #Ash.NotLoaded<:relationship, field: :task_templates>,
creator: #Ash.NotLoaded<:relationship, field: :creator>,
meta: #Ecto.Schema.Metadata<:loaded, "quest_templates">
},
valid?: true
>
Here is the changeset I printed for debug. I remove some floats as it's too long. I do not join the text but generated vector for each text now for (vectorized_name / vectorized_description and full_text_vector)
The error is still:
%Ash.Error.Unknown.UnknownError{error: "** (FunctionClauseError) no function clause matching in Pgvector.new/1", field: nil, value: nil, splode: Ash.Error, bread_crumbs: [], vars: [], path: [], stacktrace: #splode.Stacktrace<>, class: :unknown}], data: %Qst.Templates.QuestTemplate{id: "29a256f5-97b7-4566-abc4-21346436a362", name: "Dragon Slayer", description: "Epic dragon quest", inserted_at: ~U[2025-06-21 19:58:21.762675Z
Can I see the latest version of your model?
Use triple backticks to make code easier to read
defmodule Qst.Templates.QuestTemplate do
use Ash.Resource,
otp_app: :qst,
domain: Qst.Templates,
data_layer: AshPostgres.DataLayer,
extensions: [AshAi, AshOban]
require Ash.Query
postgres do
table "quest_templates"
repo Qst.Repo
end
vectorize do
full_text do
text(fn r ->
"""
Name: #{r.name}
Description: #{r.description}
"""
end)
used_attributes([:name, :description])
end
strategy :after_action
attributes(name: :vectorized_name, description: :vectorized_description)
embedding_model(GeminiEmbeddingModel)
end
actions do
defaults [:create, :read, :update, :destroy]
default_accept [:name, :description, :creator_id]
read :search do
argument :query, :string, allow_nil?: false
prepare before_action(fn query, context ->
case GeminiEmbeddingModel.generate([query.arguments.query], []) do
{:ok, search_vector} ->
IO.inspect(search_vector, label: "search_vector")
query
|> Ash.Query.filter(
not is_nil(full_text_vector) and
vector_cosine_distance(full_text_vector, ^search_vector) < 0.5
)
|> Ash.Query.sort(
{calc(expr(vector_cosine_distance(full_text_vector, ^search_vector)),
type: :float
), :asc}
)
|> Ash.Query.limit(10)
{:error, error} ->
{:error, error}
end
end)
end
end
attributes do
uuid_primary_key :id
attribute :name, :string, allow_nil?: false
attribute :description, :string
timestamps()
end
end
Here it is. Really appreciate your time. Thanks.Sorry, I mean, your EmbeddingModel
defmodule Qst.Ai.GeminiEmbeddingModel do
use AshAi.EmbeddingModel
@dimensions 768
@endpoint "https://generativelanguage.googleapis.com/v1beta/models/text-embedding-004:embedContent"
@impl true
def dimensions(_opts), do: @dimensions
@impl true
def generate(texts, _opts) when is_list(texts) do
{:ok,
Enum.map(texts, fn text ->
{:ok, values} = generate_one(text, _opts)
values
end)}
end
def generate_one(text, _opts) do
api_key = System.fetch_env!("GEMINI_API_KEY")
body = %{"content" => %{"parts" => %{"text" => text}}, "taskType" => "SEMANTIC_SIMILARITY"}
case Req.post(@endpoint <> "?key=" <> api_key, json: body) do
{:ok,
%Req.Response{status: 200, body: %{"embedding" => %{"values" => values}} = response_body}} ->
{:ok, values}
{:ok, %Req.Response{status: status, body: body}} ->
{:error, {status, body}}
{:error, error} ->
{:error, error}
end
end
end
This is the EmbeddingModel. Thanks.Can you drop an
IO.inspect
at the end to show the return value? I'm confident something is wrong with that module ultimately.generate result: {:ok,
[
[-0.092400566, 0.0062946035, -0.10562682, 0.056037433, 0.09453738,
0.025487343, 0.01596396, -0.017847484, 0.047371704, 0.046806518, 0.0326265,
-0.027291933, -0.025053283, 0.0058116233, -0.025200406, 0.026335014,
-0.05078257, 0.020042488, -0.08787288, 0.040349603, -0.022270834, ...],
[-0.0848025, -0.008653622, -0.07089128, 0.022988718, 0.071521655,
0.042495444, 0.011308266, -0.033617016, 0.039227992, -0.0023086653,
0.00406599, -0.023387631, 0.0057275696, -0.02201007, -0.052452102,
0.028986359, -0.061633606, -0.010335462, -0.057401214, -0.009707467, ...],
[-0.0896868, 0.012641902, -0.06116206, 0.033900578, 0.064447895, 0.044433285,
0.011002703, -0.026450213, 0.036916945, 0.052138157, 0.025357082,
-0.040204428, -0.0046259044, -0.0029942, -0.021914972, -0.017778914,
-0.04252304, -0.0077015706, -0.013262843, -0.051168382, 0.010696245,
-0.06892958, -0.011229938, -0.07094381, ...]
]}
Here is the result. I am not sure what's the expected returns here.Huh. That looks right to be honestly. Is this repo something you can share?
Could you maybe put a repro together for me?
Will work on the repo.
For now, I think the main issue is when call Pgvector.new, the Ash.Vector.new([...]) was passed. I don't know why it happens. Maybe I miss some config for type convert?
Finally, I found the root cause. It's my bad, I am using Pgvector.Extensions.Vector instead of AshPostgres.Extensions.Vector for postgres types.
Thanks so much for your time.