Current section

Files

Jump to
langchain lib message citation.ex
Raw

lib/message/citation.ex

defmodule LangChain.Message.Citation do
@moduledoc """
Represents a citation linking a span of response text to a source.
All four major providers (Anthropic, OpenAI, Google, Perplexity) share the
pattern of linking response text to sources. This struct normalizes those
provider-specific formats into a common shape.
## Fields
- `:cited_text` - The actual text cited from the source (when available)
- `:source` - A `CitationSource` identifying where the citation came from
- `:start_index` - Character offset into the ContentPart's text where this
citation starts
- `:end_index` - Character offset where this citation ends
- `:confidence` - Confidence score (0.0-1.0) when provided (e.g., Gemini)
- `:metadata` - Provider-specific data for round-tripping. String keys for
JSON serialization.
"""
use Ecto.Schema
import Ecto.Changeset
alias LangChain.LangChainError
alias LangChain.Message.CitationSource
alias __MODULE__
@primary_key false
embedded_schema do
field :cited_text, :string
embeds_one :source, CitationSource
field :start_index, :integer
field :end_index, :integer
field :confidence, :float
# Provider-specific data. String keys for JSON serialization.
field :metadata, :map, default: %{}
end
@type t :: %Citation{}
@create_fields [:cited_text, :start_index, :end_index, :confidence, :metadata]
@doc false
def changeset(citation, attrs) do
citation
|> cast(attrs, @create_fields)
|> cast_embed(:source)
end
@spec new(map()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
def new(attrs \\ %{}) do
%Citation{}
|> changeset(attrs)
|> apply_action(:insert)
end
@spec new!(map()) :: t() | no_return()
def new!(attrs \\ %{}) do
case new(attrs) do
{:ok, struct} -> struct
{:error, changeset} -> raise LangChainError, changeset
end
end
@doc """
Returns all unique source URLs from a list of citations.
"""
@spec source_urls([t()]) :: [String.t()]
def source_urls(citations) when is_list(citations) do
citations
|> Enum.map(& &1.source.url)
|> Enum.reject(&is_nil/1)
|> Enum.uniq()
end
@doc """
Filters citations by source type.
"""
@spec filter_by_source_type([t()], atom()) :: [t()]
def filter_by_source_type(citations, type) when is_list(citations) do
Enum.filter(citations, &(&1.source && &1.source.type == type))
end
end