Files
bDS2/lib/bds/ai/json_content.ex

43 lines
1.2 KiB
Elixir

defmodule BDS.AI.JsonContent do
@moduledoc """
Decodes JSON object payloads from model responses, tolerating the markdown
code fences and surrounding prose that smaller (local) models often emit
instead of bare JSON.
"""
@fence_pattern ~r/```(?:json)?\s*\n?(.*?)```/is
@spec decode(term()) :: map() | nil
def decode(content) when is_binary(content) do
decode_strict(content) || decode_fenced(content) || decode_embedded_object(content)
end
def decode(_content), do: nil
defp decode_strict(content) do
case Jason.decode(content) do
{:ok, decoded} when is_map(decoded) -> decoded
_other -> nil
end
end
defp decode_fenced(content) do
case Regex.run(@fence_pattern, content, capture: :all_but_first) do
[inner] -> decode_strict(String.trim(inner)) || decode_embedded_object(inner)
_no_fence -> nil
end
end
defp decode_embedded_object(content) do
with {start, _length} <- :binary.match(content, "{"),
[{last, _} | _] <- content |> :binary.matches("}") |> Enum.take(-1),
true <- last > start do
content
|> binary_part(start, last - start + 1)
|> decode_strict()
else
_no_object -> nil
end
end
end