Packages
fnord
0.9.24
0.9.40
0.9.39
0.9.38
0.9.37
0.9.36
0.9.35
0.9.34
0.9.33
0.9.32
0.9.31
0.9.30
0.9.29
0.9.28
0.9.27
0.9.26
0.9.25
0.9.24
0.9.23
0.9.22
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.14
0.9.13
0.9.12
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.99
0.8.98
0.8.97
0.8.96
0.8.95
0.8.94
0.8.93
0.8.92
0.8.91
0.8.90
0.8.89
0.8.88
0.8.87
0.8.86
0.8.85
0.8.84
0.8.83
0.8.82
0.8.81
0.8.80
0.8.79
0.8.78
0.8.77
0.8.76
0.8.75
0.8.74
0.8.73
0.8.72
0.8.71
0.8.70
0.8.69
0.8.68
0.8.67
0.8.66
0.8.65
0.8.64
0.8.63
0.8.62
0.8.61
0.8.60
0.8.59
0.8.58
0.8.57
0.8.56
0.8.55
0.8.54
0.8.53
0.8.52
0.8.51
0.8.50
0.8.49
0.8.48
0.8.47
0.8.46
0.8.45
0.8.44
0.8.43
0.8.42
0.8.41
0.8.40
0.8.39
0.8.38
0.8.37
0.8.36
0.8.35
0.8.34
0.8.33
0.8.32
0.8.31
0.8.30
0.8.29
0.8.27
0.8.26
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.1
0.8.0
0.7.24
0.7.23
0.7.22
0.7.21
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.3
0.7.2
0.7.1
0.7.0
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.1
0.6.0
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.44
0.4.43
0.4.42
0.4.41
0.4.40
0.4.39
0.4.38
0.4.37
0.4.36
0.4.35
0.4.34
0.4.33
0.4.32
0.4.30
0.4.29
0.4.28
0.4.27
0.4.26
0.4.25
0.4.24
0.4.23
0.4.22
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.0
0.2.0
0.1.0
AI code archaeology
Current section
Files
Jump to
Current section
Files
lib/search/files.ex
defmodule Search.Files do
defstruct [
:query,
:limit,
:detail
]
def new(opts) do
%__MODULE__{
query: opts[:query],
limit: opts[:limit],
detail: opts[:detail]
}
end
def get_results(search) do
with {:ok, project} <- Store.get_project(),
{:ok, needle} <- Indexer.impl().get_embeddings(search.query) do
project
|> Store.Project.stored_files()
|> Util.async_stream(fn entry ->
with {:ok, data} <- get_file_data(search, entry),
{:ok, score} <- get_score(needle, data) do
{entry, score, data}
else
_ -> nil
end
end)
|> Enum.reduce([], fn
{:ok, nil}, acc -> acc
{:ok, result}, acc -> [result | acc]
# A task crash (e.g. malformed embedding on disk) shouldn't take the
# whole search down - drop the result and keep going.
_, acc -> acc
end)
|> Enum.sort(fn {_, a, _}, {_, b, _} -> a >= b end)
|> Enum.take(search.limit)
|> then(&{:ok, &1})
else
{:error, reason} -> {:error, reason}
end
end
defp get_file_data(search, entry) do
if search.detail do
Store.Project.Entry.read(entry)
else
with {:ok, embeddings} <- Store.Project.Entry.read_embeddings(entry) do
{:ok, %{"embeddings" => embeddings}}
end
end
end
# Skip entries whose embedding dimension doesn't match the query - most
# likely they were written by the previous model and haven't been
# reindexed yet. Returning {:error, :dim_mismatch} lets the caller drop
# the entry instead of crashing cosine_similarity.
defp get_score(needle, %{"embeddings" => embeddings})
when is_list(needle) and is_list(embeddings) do
if length(needle) == length(embeddings) do
{:ok, AI.Util.cosine_similarity(needle, embeddings)}
else
{:error, :dim_mismatch}
end
end
defp get_score(_needle, _data), do: {:error, :missing_embeddings}
end