Packages

Generative AI Wrapper: access multiple apis through single standardized interface.

Current section

Files

Jump to
genai lib genai_providers gemini encoder_protocol.ex
Raw

lib/genai_providers/gemini/encoder_protocol.ex

defprotocol GenAI.Provider.Gemini.EncoderProtocol do
@moduledoc """
Encoders use their module's EncoderProtocol to prep messages and tool definitions
To make future extensibility for third parties straight forward. If a new message
type, or tool type is added one simply needs to implement a EncoderProtocol for it
and most cases you can simply cast it to generic known type and then invoke the protocol
again.
"""
# ⟦𓇷𓀵𓈙𓍏⟧ encode :: auto-generated pointer for public function encode
def encode(subject, model, session, context, options)
end
defmodule GenAI.Provider.Gemini.EncoderProtocolHelper do
# ⟦𓄎𓆋𓁰𓍲⟧ system_message_markup :: auto-generated pointer for public function system_message_markup
def system_message_markup(message) do
"<|system|>\n" <> message <> "</|system|>"
end
# ⟦𓅙𓍨𓐡𓉨⟧ content :: auto-generated pointer for public function content
def content(content, subject, model, session, context, options)
def content(content, _, _, session, _, options) when is_bitstring(content) do
system_message = options[:system_message]
cond do
system_message -> {%{text: system_message_markup(content)}, session}
:else -> {%{text: content}, session}
end
end
def content(
%GenAI.Message.Content.TextContent{type: type, system: system_content, text: text} =
___content,
_,
_,
session,
_,
options
) do
system_type = type in [:input, :prompt]
system_message = options[:system_message]
cond do
!system_type -> {%{text: text}, session}
!(system_message || system_content) -> {%{text: text}, session}
:else -> {%{text: system_message_markup(text)}, session}
end
end
def content(%GenAI.Message.Content.ImageContent{} = content, _, _, session, _, _) do
{:ok, encoded} = GenAI.Message.Content.ImageContent.base64(content)
content = %{
inlineData: %{
data: encoded,
mimeType: "image/#{content.type}"
}
}
{content, session}
end
def content(%GenAI.Message.Content.AudioContent{} = content, _, _, session, _, _) do
content = %{
text: content.transcript
}
{content, session}
end
def content(%GenAI.Message.ToolCall{} = content, _, _, session, _, _) do
content = %{
function_call: %{
name: content.tool_name,
args: content.arguments
}
}
{content, session}
end
def content(%GenAI.Message.Content.ToolUseContent{} = content, _, _, session, _, _) do
content = %{
function_call: %{
name: content.tool_name,
args: content.arguments
}
}
{content, session}
end
def content(%GenAI.Message.ToolResponse{} = content, _, _, session, _, _) do
content = %{
function_response: %{
name: content.tool_name,
response: %{
name: content.tool_name,
content: content.tool_response
}
}
}
{content, session}
end
def content(%GenAI.Message.Content.ToolResultContent{} = content, _, _, session, _, _) do
content = %{
function_response: %{
name: content.tool_name,
response: %{
name: content.tool_name,
content: content.response
}
}
}
{content, session}
end
end
# -----------------------------
# GenAI.Tool
# -----------------------------
defimpl GenAI.Provider.Gemini.EncoderProtocol, for: GenAI.Tool do
def encode(subject, _model, session, _context, _options) do
encoded = %{
name: subject.name,
description: subject.description,
parameters: subject.parameters
}
{:ok, {encoded, session}}
end
end
# -----------------------------
# GenAI.Message
# -----------------------------
defimpl GenAI.Provider.Gemini.EncoderProtocol, for: GenAI.Message do
import GenAI.Provider.Gemini.EncoderProtocolHelper
def encode(subject, model, session, context, options) do
___roles = %{
user: :user,
assistant: :model,
system: :user
}
role = subject.role
system_message = subject.role == :system
put_in(options || [], [:system_message], system_message)
case subject.content do
content when is_bitstring(content) ->
content =
if system_message,
do: system_message_markup(content),
else: content
{:ok, {%{role: role, content: content}, session}}
content when is_list(content) ->
{content, session} =
Enum.map_reduce(
content,
session,
&content(&1, subject, model, &2, context, options)
)
{:ok, {%{role: role, parts: content}, session}}
end
end
end
# -----------------------------
# GenAI.Message.ToolResponse
# -----------------------------
defimpl GenAI.Provider.Gemini.EncoderProtocol, for: GenAI.Message.ToolResponse do
import GenAI.Provider.Gemini.EncoderProtocolHelper
def encode(subject, model, session, context, options) do
{entry, session} = content(subject, subject, model, session, context, options)
encoded = %{
role: :function,
parts: [entry]
}
{:ok, {encoded, session}}
end
end
# -----------------------------
# GenAI.Message.ToolUsage
# -----------------------------
defimpl GenAI.Provider.Gemini.EncoderProtocol, for: GenAI.Message.ToolUsage do
import GenAI.Provider.Gemini.EncoderProtocolHelper
def encode(subject, model, session, context, options) do
{content, session} =
case subject.content do
nil ->
{[], session}
content when is_bitstring(content) ->
{content, session} = content(content, subject, model, session, context, options)
{[content], session}
content when is_list(content) ->
Enum.map_reduce(
content,
session,
&content(&1, subject, model, &2, context, options)
)
end
{tool_use, session} =
case subject.tool_calls do
nil ->
{[], session}
content when is_bitstring(content) ->
{content, session} = content(content, subject, model, session, context, options)
{[content], session}
content when is_list(content) ->
Enum.map_reduce(
content,
session,
&content(&1, subject, model, &2, context, options)
)
end
encoded = %{role: :model, parts: content ++ tool_use}
{:ok, {encoded, session}}
end
end