defmodule BDS.Scripting.Capabilities.Bridges do @moduledoc false import BDS.Scripting.Capabilities.Util alias BDS.AI alias BDS.Embeddings alias BDS.Git alias BDS.Media alias BDS.Posts alias BDS.Publishing # --- Sync / Git --- def sync_available?, do: not is_nil(System.find_executable("git")) def repo_state(project_id, opts) do project_id |> Git.repository(git_opts(opts)) |> unwrap_result() end def repo_status(project_id, opts) do project_id |> Git.status(git_opts(opts)) |> unwrap_result() end def repo_history(project_id, opts) do case Git.repository(project_id, git_opts(opts)) do {:ok, %{current_branch: branch}} when is_binary(branch) and branch != "" -> Git.history(project_id, branch, git_opts(opts)) |> unwrap_result() _other -> %{"commits" => []} end end def repo_fetch(project_id, opts), do: project_id |> Git.fetch(git_opts(opts)) |> unwrap_result() def repo_pull(project_id, opts), do: project_id |> Git.pull(git_opts(opts)) |> unwrap_result() def repo_push(project_id, opts), do: project_id |> Git.push(git_opts(opts)) |> unwrap_result() def repo_commit_all(project_id, message, opts) do project_id |> Git.commit_all(string_or_nil(message) || "", git_opts(opts)) |> unwrap_result() end # --- Publishing --- def upload_site(project_id, credentials, opts) do project_id |> Publishing.upload_site(normalize_map(credentials), publishing_opts(opts)) |> unwrap_result() end # --- AI --- def detect_post_language(title, content, opts) do text = Enum.join([string_or_nil(title) || "", string_or_nil(content) || ""], "\n\n") case AI.detect_language(text, ai_opts(opts)) do {:ok, %{language_code: language_code}} -> %{"success" => true, "language" => language_code} {:error, reason} -> %{"success" => false, "error" => inspect(reason)} end end def analyze_post(post_id, opts) do post_id |> string_or_nil() |> AI.analyze_post(ai_opts(opts)) |> unwrap_result() end def translate_post(post_id, language, opts) do post_id = string_or_nil(post_id) language = string_or_nil(language) || "" with {:ok, translation} <- AI.translate_post(post_id, language, ai_opts(opts)), {:ok, saved_translation} <- Posts.upsert_post_translation(post_id, language, %{ title: translation.title, excerpt: translation.excerpt, content: translation.content }) do sanitize(saved_translation) else _other -> nil end end def analyze_media_image(media_id, opts) do case AI.analyze_image(string_or_nil(media_id), ai_opts(opts)) do {:ok, result} -> sanitize(result) {:error, _reason} -> nil end end def detect_media_language(title, alt, caption, opts) do text = Enum.join([string_or_nil(title) || "", string_or_nil(alt) || "", string_or_nil(caption) || ""], "\n") case AI.detect_language(text, ai_opts(opts)) do {:ok, %{language_code: language_code}} -> %{"success" => true, "language" => language_code} {:error, reason} -> %{"success" => false, "error" => inspect(reason)} end end def translate_media_metadata(media_id, language, opts) do media_id = string_or_nil(media_id) language = string_or_nil(language) || "" with {:ok, translation} <- AI.translate_media(media_id, language, ai_opts(opts)), {:ok, saved_translation} <- Media.upsert_media_translation(media_id, language, %{ title: translation.title, alt: translation.alt, caption: translation.caption }) do sanitize(saved_translation) else _other -> nil end end # --- Embeddings --- def embedding_progress(project_id), do: project_id |> Embeddings.get_indexing_progress() |> unwrap_result() def find_similar(post_id, limit) do post_id |> string_or_nil() |> Embeddings.find_similar(integer_or_default(limit, 5)) |> unwrap_result() end def compute_similarities(post_id, target_ids) do post_id |> string_or_nil() |> Embeddings.compute_similarities(normalize_string_list(target_ids)) |> unwrap_result() end def suggest_tags(post_id, exclude_tags) do post_id |> string_or_nil() |> Embeddings.suggest_tags(normalize_string_list(exclude_tags)) |> unwrap_result() end def find_duplicates(project_id), do: project_id |> Embeddings.find_duplicates() |> unwrap_result() def dismiss_pair(post_id_a, post_id_b), do: atom_result(Embeddings.dismiss_duplicate_pair(string_or_nil(post_id_a) || "", string_or_nil(post_id_b) || ""), :ok) def index_unindexed_posts(project_id), do: project_id |> Embeddings.index_unindexed() |> unwrap_result() # --- Opt builders --- def git_opts(opts) do case Keyword.get(opts, :git_runner) do nil -> [] runner -> [runner: runner] end end def publishing_opts(opts) do case Keyword.get(opts, :publishing_uploader) do nil -> [] uploader -> [uploader: uploader] end end def ai_opts(opts) do [] |> maybe_put_opt(:runtime, Keyword.get(opts, :ai_runtime)) |> maybe_put_opt(:secret_backend, Keyword.get(opts, :ai_secret_backend)) end end