defmodule GenAI.Message do @moduledoc """ GenAI.Message is now a unified message structure. It's contents may include tool calls, results, images, audio, text blurbs, etc. Depending on compatibility with providers a universal GenAI.Message may need to be converted into a sequence of messages or altered in other ways. A message with tool calls for example would need to be converted into a tool call format for openai """ @vsn 1.0 require GenAI.Records.Directive import GenAI.Records.Directive use GenAI.Graph.NodeBehaviour @derive GenAI.Graph.NodeProtocol # @derive GenAI.Thread.SessionProtocol defnodestruct(role: nil, content: nil, user: nil) defnodetype(role: term, content: term, user: nil) def apply_node_directives(this, graph_link, graph_container, session, context, options) def apply_node_directives(this, _, _, session, context, options) do entry = message_entry(msg: this.id) directive = GenAI.Session.State.Directive.static(entry, this, {:node, this.id}) GenAI.Thread.Session.append_directive(session, directive, context, options) end def inspect_custom_details(subject, opts) do list = [ "role:", Inspect.Algebra.to_doc(subject.role, opts), ", ", "content:", Inspect.Algebra.to_doc(subject.content, opts), ", ", ] if subject.user do ["user:", Inspect.Algebra.to_doc(subject.user, opts), ", " | list] else list end end def message(role, message, options \\ nil) do options = Keyword.merge(options || [], role: role, content: message) new(options) end def user(message, options \\ nil) do message(:user, message, options) end def system(message, options \\ nil) do message(:system, message, options) end def assistant(message, options \\ nil) do message(:assistant, message, options) end @doc """ Load image resource. """ def image(resource, options \\ nil) def image(resource, nil) do GenAI.Message.Content.ImageContent.new(resource) end def image(resource, options) do GenAI.Message.Content.ImageContent.new(resource, options) end defimpl GenAI.MessageProtocol do def message(message), do: message def content(message) def content(%{content: content}) when is_bitstring(content) do content end def content(%{content: content}) when is_list(content) do Enum.map(content, &GenAI.Message.ContentProtocol.content(&1)) end end end