Files
bDS2/lib/bds/scripting/capabilities/bridges.ex
2026-05-01 17:49:50 +02:00

194 lines
5.3 KiB
Elixir

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