Packages

fnord

0.8.44

AI code archaeology

Current section

Files

Jump to
fnord lib services background_indexer.ex
Raw

lib/services/background_indexer.ex

defmodule Services.BackgroundIndexer do
@moduledoc """
A silent, cancellable GenServer that incrementally indexes stale files.
"""
use GenServer,
# Do not restart if it crashes; it should stop when done
restart: :temporary
@spec start_link(
opts :: [
project: Store.Project.t(),
files: [Store.Project.Entry.t()]
]
) :: GenServer.on_start()
def start_link(opts \\ []) do
GenServer.start_link(__MODULE__, opts)
end
@spec stop(pid()) :: :ok
def stop(pid) when is_pid(pid) do
if Process.alive?(pid) do
GenServer.stop(pid, :normal)
else
:ok
end
end
@impl true
def init(opts) do
HttpPool.set(:ai_indexer)
project =
case Keyword.get(opts, :project) do
%Store.Project{} = prj ->
prj
_ ->
case Store.get_project() do
{:ok, prj} ->
prj
_ ->
# no project available; initialize with empty queue
nil
end
end
queue =
case Keyword.get(opts, :files) do
files when is_list(files) ->
files
_ ->
if project do
Store.Project.index_status(project).stale
else
[]
end
end
state = %{project: project, queue: queue, impl: Indexer.impl()}
{:ok, state, {:continue, :process_next}}
end
@impl true
def handle_continue(:process_next, %{queue: []} = state) do
{:stop, :normal, state}
end
@impl true
def handle_continue(:process_next, %{queue: [entry | rest]} = state) do
safe_process(entry, state.impl, state.project)
{:noreply, %{state | queue: rest}, {:continue, :process_next}}
end
@impl true
def terminate(_reason, _state) do
HttpPool.clear()
:ok
end
# Safely process a single entry: read, generate summary, outline, embeddings, and save
defp safe_process(entry, impl, _project) do
try do
content =
case Store.Project.Entry.read_source_file(entry) do
{:ok, c} -> c
_ -> ""
end
path = entry.file
with {:ok, summary} = impl.get_summary(path, content),
{:ok, outline} = impl.get_outline(path, content),
embed_str = [summary, outline, content] |> Enum.join("\n\n"),
{:ok, embeddings} = impl.get_embeddings(embed_str) do
Store.Project.Entry.save(entry, summary, outline, embeddings)
UI.end_step("Indexed", entry.file)
end
rescue
_ -> :ok
end
end
end