Packages
fnord
0.9.38
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/commits.ex
defmodule Search.Commits do
@moduledoc """
Semantic search over indexed git commits.
Given a natural-language query, we embed it and compare against precomputed
commit embeddings using cosine similarity. Results include the commit SHA,
similarity score, and selected metadata.
"""
defstruct [
:query,
:limit
]
@type t :: %__MODULE__{
query: String.t(),
limit: pos_integer()
}
@default_limit 25
@spec new(Keyword.t() | map()) :: t
def new(opts) do
opts = Map.new(opts)
%__MODULE__{
query: Map.get(opts, :query) || Map.get(opts, "query"),
limit: Map.get(opts, :limit) || Map.get(opts, "limit") || @default_limit
}
end
@doc """
Returns {:ok, results} where results is a list of {sha, score, metadata} tuples,
ordered by highest similarity and truncated to the provided limit.
"""
@spec get_results(t) :: {:ok, list({String.t(), number(), map()})} | {:error, any()}
def get_results(%__MODULE__{} = search) do
with {:ok, project} <- Store.get_project(),
{:ok, needle} <- Indexer.impl().get_embeddings(search.query) do
q_len = length(needle)
{scored, dropped} =
project
|> Store.Project.CommitIndex.all_embeddings()
|> Util.async_stream(fn {sha, embeddings, metadata} ->
# Stale-dimension commits (pre-migration, corrupted, or
# mid-migration residue) would crash cosine_similarity;
# return an explicit :dim_mismatch so the caller can count
# how many we skipped and surface it in the log.
if is_list(embeddings) and length(embeddings) == q_len do
score = AI.Util.cosine_similarity(needle, embeddings)
{:scored, {sha, score, metadata}}
else
{:dim_mismatch, sha}
end
end)
|> Enum.reduce({[], 0}, fn
{:ok, {:scored, item}}, {items, drops} -> {[item | items], drops}
{:ok, {:dim_mismatch, _sha}}, {items, drops} -> {items, drops + 1}
_, acc -> acc
end)
if dropped > 0 do
UI.warn(
"[commit search] skipped #{dropped} indexed commit#{if dropped == 1, do: "", else: "s"}" <>
" with stale embedding dimensions; run `fnord index` to rebuild."
)
end
results =
scored
|> Enum.sort(fn {_, a, _}, {_, b, _} -> a >= b end)
|> Enum.take(search.limit)
{:ok, results}
end
end
end