Packages

Semantic code search and analysis for Elixir projects via MCP (Model Context Protocol)

Current section

Files

Jump to
codicil lib codicil llm voyage.ex
Raw

lib/codicil/llm/voyage.ex

defmodule Codicil.LLM.Voyage do
# Voyage AI client for vector embeddings.
#
# Voyage AI provides embedding models that can be used with Anthropic's
# Claude for retrieval-augmented generation (RAG) use cases.
@moduledoc false
@enforce_keys [:api_key, :model]
defstruct @enforce_keys
@type t :: %__MODULE__{
api_key: String.t(),
model: String.t()
}
use Codicil.Embeddings
alias Codicil.Embeddings.Result
@api_base_url "https://api.voyageai.com/v1"
# Embeddings Implementation
@impl Codicil.Embeddings
def embed(%__MODULE__{api_key: api_key, model: model}, text, opts) do
input_type = Keyword.get(opts, :input_type, "document")
body = %{
model: model,
input: text,
input_type: input_type
}
case make_embeddings_request(api_key, body) do
{:ok, %{"data" => [%{"embedding" => embedding}]}} ->
dimensions = length(embedding)
{:ok, %Result{embedding: embedding, dimensions: dimensions}}
{:error, reason} ->
{:error, reason}
end
end
@impl Codicil.Embeddings
def embed_batch(%__MODULE__{api_key: api_key, model: model}, texts, opts) do
input_type = Keyword.get(opts, :input_type, "document")
body = %{
model: model,
input: texts,
input_type: input_type
}
case make_embeddings_request(api_key, body) do
{:ok, %{"data" => embeddings_data}} ->
results =
Enum.map(embeddings_data, fn %{"embedding" => emb} ->
dimensions = length(emb)
%Result{embedding: emb, dimensions: dimensions}
end)
{:ok, results}
{:error, reason} ->
{:error, reason}
end
end
defp make_embeddings_request(api_key, body) do
case Req.post(
"#{@api_base_url}/embeddings",
json: body,
headers: [
{"authorization", "Bearer #{api_key}"},
{"content-type", "application/json"}
]
) do
{:ok, %{status: 200, body: response}} ->
{:ok, response}
{:ok, %{status: status, body: body}} ->
error_message = get_in(body, ["error", "message"]) || "HTTP #{status}"
{:error, error_message}
{:error, %{status: status, body: body}} when is_map(body) ->
error_message = get_in(body, ["error", "message"]) || "HTTP #{status}"
{:error, error_message}
{:error, reason} ->
{:error, inspect(reason)}
end
end
end