defprotocol GenAI.Provider.LocalLLama.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. """ def encode(subject, model, session, context, options) end #----------------------------- # GenAI.Tool #----------------------------- defimpl GenAI.Provider.LocalLLama.EncoderProtocol, for: GenAI.Tool do def encode(subject, _model, session, _context, _options) do encoded = %{ type: :function, function: %{ name: subject.name, description: subject.description, parameters: subject.parameters } } {:ok, {encoded, session}} end end #----------------------------- # GenAI.Message #----------------------------- defimpl GenAI.Provider.LocalLLama.EncoderProtocol, for: GenAI.Message do def content(content) def content(content) when is_bitstring(content) do %{type: :text, text: content} end def content(%GenAI.Message.Content.TextContent{} = content) do %{type: :text, text: content.text} end def content(%GenAI.Message.Content.ImageContent{} = content) do {:ok, encoded} = GenAI.Message.Content.ImageContent.base64(content) base64 = "data:image/#{content.type};base64," <> encoded %{type: :image_url, image_url: %{url: base64}} end def encode(subject, _model, session, _context, _options) do encoded = case subject.content do x when is_bitstring(x) -> %{role: subject.role, content: subject.content} x when is_list(x) -> content_list = Enum.map(x, &content/1) %{role: subject.role, content: content_list} end {:ok, {encoded, session}} end end #----------------------------- # GenAI.Message.ToolResponse #----------------------------- defimpl GenAI.Provider.LocalLLama.EncoderProtocol, for: GenAI.Message.ToolResponse do def encode(subject, _model, session, _context, _options) do encoded = %{ role: :tool, tool_call_id: subject.tool_call_id, content: Jason.encode!(subject.tool_response) } {:ok, {encoded, session}} end end #----------------------------- # GenAI.Message.ToolUsage #----------------------------- defimpl GenAI.Provider.LocalLLama.EncoderProtocol, for: GenAI.Message.ToolUsage do def encode_call( %GenAI.Message.ToolCall{ id: id, type: type, tool_name: tool_name, arguments: arguments } ) do %{ function: %{name: tool_name, arguments: Jason.encode!(arguments)}, id: id, type: type } end def encode(subject, _model, session, _context, _options) do tool_calls = Enum.map(subject.tool_calls,& encode_call/1) encoded = %{ role: subject.role, content: subject.content, tool_calls: tool_calls } {:ok, {encoded, session}} end end