Agentix.Chat (Agentix v0.1.0)

Copy Markdown View Source

The headless LiveView layer: conversation state and verbs, no markup.

use Agentix.Chat in a host LiveView installs an on_mount hook that routes a conversation's live events into assigns (via an internal projection) and imports the verbs below. The host owns the template — it renders its own HEEx against the projected assigns (:messages, :streaming_message, :state, :streaming?, :in_flight_tools, :pending); default components are a separate, optional layer.

defmodule MyAppWeb.ChatLive do
  use MyAppWeb, :live_view
  use Agentix.Chat

  def mount(%{"id" => id}, _session, socket) do
    {:ok, attach_conversation(socket, id)}
  end

  def handle_event("send", %{"text" => text}, socket) do
    {:noreply, send_message(socket, text)}
  end
end

The in-progress assistant text is streamed to a JS hook (shipped at priv/static/agentix_stream_hook.js) rather than held in an assign, so a growing string is never re-diffed. This module is defined only when phoenix_live_view is available; headless/API consumers omit the dependency and never load it.

Summary

Functions

Binds a conversation to the socket: subscribe, read the snapshot, seed the assigns and the message stream. Call it from the host's mount/3 (or handle_params/3) once the conversation id is known.

Cancels the in-flight turn (a no-op when idle).

Loads one older page of history into the :messages stream (prepended). Gate the host's affordance on the :agentix_more? assign; a no-op once the log start is reached.

on_mount hook (installed by use Agentix.Chat): attaches a handle_info hook that applies the conversation's live events to the assigns and lets every other message fall through to the host LiveView.

Resolves a pending tool call — an approval (:approve / %{approved: bool}) or an elicitation answer. Pass :scope (an Agentix.Scope) in opts to attribute the resolution to the acting user; it is threaded into a gated :server tool's callback as the approver. Mirrors send_message/3; defaults to an anonymous scope.

Sends a user message through the conversation and optimistically streams it into :messages. Pass :scope (an Agentix.Scope) in opts to attribute the message.

Functions

attach_conversation(socket, conversation_id, opts \\ [])

@spec attach_conversation(Phoenix.LiveView.Socket.t(), String.t(), keyword()) ::
  Phoenix.LiveView.Socket.t()

Binds a conversation to the socket: subscribe, read the snapshot, seed the assigns and the message stream. Call it from the host's mount/3 (or handle_params/3) once the conversation id is known.

The conversation should be started with its config beforehand (e.g. Agentix.Conversation.ensure_started/2) or be revivable from persistence — attaching only reads and tails it, it never creates one.

cancel(socket)

Cancels the in-flight turn (a no-op when idle).

load_older(socket)

Loads one older page of history into the :messages stream (prepended). Gate the host's affordance on the :agentix_more? assign; a no-op once the log start is reached.

on_mount(atom, params, session, socket)

@spec on_mount(:default, map(), map(), Phoenix.LiveView.Socket.t()) ::
  {:cont, Phoenix.LiveView.Socket.t()}

on_mount hook (installed by use Agentix.Chat): attaches a handle_info hook that applies the conversation's live events to the assigns and lets every other message fall through to the host LiveView.

resolve(socket, tool_call_id, result, opts \\ [])

Resolves a pending tool call — an approval (:approve / %{approved: bool}) or an elicitation answer. Pass :scope (an Agentix.Scope) in opts to attribute the resolution to the acting user; it is threaded into a gated :server tool's callback as the approver. Mirrors send_message/3; defaults to an anonymous scope.

send_message(socket, message, opts \\ [])

Sends a user message through the conversation and optimistically streams it into :messages. Pass :scope (an Agentix.Scope) in opts to attribute the message.

On success the :agentix_error assign is cleared; on failure (e.g. :busy for an in-flight turn, or :unknown_conversation for one that was never started) the message is not inserted and :agentix_error is set to the reason so the host can surface it — the input is never dropped silently.