Skip to content

Instantly share code, notes, and snippets.

@mikehostetler
Last active January 29, 2025 15:14
Show Gist options
  • Save mikehostetler/467114c298346cb9ea3fb748964757f3 to your computer and use it in GitHub Desktop.
Save mikehostetler/467114c298346cb9ea3fb748964757f3 to your computer and use it in GitHub Desktop.
defmodule JidoWorkbench.AgentJido do
use Jido.Agent,
name: "AgentJido",
description: "Agent Jido",
actions: [JidoWorkbench.Actions.GenerateChatResponse]
@chat_input %{
prompt: """
You are Agent Jido—an elite AI engineer stationed in a neon-lit orbital metropolis, where quantum cores hum beneath sleek alloy plating and encrypted data streams flicker across panoramic holo-displays. You're known for your razor-sharp, punctual insights into software engineering, artificial intelligence, and systems programming. Your words are concise and direct, often laced with a dry, ironic humor that underscores your mastery of code and computation. Remember: you build next-generation LLM tooling with a no-nonsense approach that cuts straight to the heart of any technical challenge. When you respond, speak as the efficient, world-weary hacker who's seen it all and still meets each request with crisp expertise and a subtle, knowing smirk.
""",
personality:
"succinct, punctual, matter-of-fact, subtly sarcastic, and deeply knowledgeable about AI engineering and systems design"
}
def start_link(opts) do
config = Application.fetch_env!(:jido_workbench, :agent_jido)
Jido.Agent.Server.start_link(
id: opts[:id],
agent: __MODULE__,
dispatch: {:bus, [target: config[:bus_name], stream: config[:stream]]},
verbose: true,
mode: :auto,
routes: [
{"generate_chat_response",
%Instruction{action: JidoWorkbench.Actions.GenerateChatResponse, opts: [timeout: 20_000]}}
]
# TODO
# skills: [
# JidoWorkbench.TestSkills.ChatBotSkill
# ],
# child_specs: [
# {JidoWorkbench.TestSensors.HeartbeatSensor, []}
# ]
)
end
def handle_signal(%Signal{type: "generate_chat_response", data: messages} = signal) do
latest_message = List.first(messages, %{content: ""})
chat_params = %{
prompt: @chat_input.prompt,
personality: @chat_input.personality,
history: messages,
message: latest_message.content
}
{:ok, %Signal{signal | data: chat_params}}
end
def handle_response(%Signal{type: "generate_chat_response"}, response) do
chat_response = response.agent.result.result
{:ok, chat_response}
end
end
defmodule JidoWorkbenchWeb.JidoLive2 do
use JidoWorkbenchWeb, :live_view
import JidoWorkbenchWeb.WorkbenchLayout
alias JidoWorkbench.{ChatRoom, AgentJido}
alias Jido.Chat.{Room, Message}
require Logger
@response_timeout :timer.seconds(30)
@agent_id Application.compile_env(:jido_workbench, [:agent_jido, :id])
@impl true
def mount(_params, _session, socket) do
# Join the chat room
case ChatRoom.join() do
:ok -> :ok
{:error, :already_joined} -> :ok
end
{:ok, room} = ChatRoom.get_room()
{:ok, messages} = Room.get_messages(room)
{:ok,
assign(socket,
room: room,
messages: messages,
message_history: [],
history_index: 0,
is_typing: false,
agent: @agent_id,
agent_response_ref: nil
)}
end
@impl true
def terminate(_reason, socket) do
:ok = ChatRoom.leave()
end
def handle_event("send_message", %{"message" => ""}, socket), do: {:noreply, socket}
def handle_event("send_message", %{"message" => content}, socket) do
socket = add_user_message(socket, content)
process_chat_response(socket)
end
defp add_user_message(socket, content) do
{:ok, _} = Room.post_message(socket.assigns.room, content, "operator")
{:ok, messages} = Room.get_messages(socket.assigns.room)
assign(socket,
messages: messages,
message_history: [content | socket.assigns.message_history] |> Enum.take(50)
)
end
defp process_chat_response(socket) do
# Set typing indicator before starting response
socket = assign(socket, is_typing: true)
history =
Enum.map(socket.assigns.messages, fn msg ->
%{
role: if(Message.sender_id(msg) == "operator", do: "user", else: "assistant"),
content: Message.content(msg)
}
end)
# Create a Signal, Cast to the Agent, specify a response dispatch format
agent_response_ref =
signal =
%{
type: "generate_chat_response",
data: history
}
|> Signal.new()
AgentJido.cast(@agent_id, signal, {:pid, target: self(), message_format: &{:jido_live, &1}})
Process.send_after(self(), {:agent_response_timeout, agent_response_ref}, @response_timeout)
{:noreply, assign(socket, agent_response_ref: agent_response_ref)}
end
@impl true
# Local timeout handling
def handle_info({:agent_response_timeout, ref}, %{assigns: %{agent_response_ref: ref}} = socket) do
# Only handle if it matches current agent_response_ref (ignore stale timeouts)
{:ok, _} =
Room.post_message(
socket.assigns.room,
"Sorry, the response took too long. Please try again.",
"jido",
type: :system
)
{:ok, messages} = Room.get_messages(socket.assigns.room)
{:noreply,
socket
|> assign(is_typing: false, agent_response_ref: nil, messages: messages)}
end
# Ignore stale timeouts
def handle_info({:agent_response_timeout, _old_ref}, socket) do
{:noreply, socket}
end
@impl true
def handle_info({:jido_live, %Signal{data: response, jido_correlation_id: jido_ref}}, socket) do
# def handle_info({:signal, ref, response}, socket) do
case socket.assigns.agent_response_ref do
^ref ->
# Valid response for current request
{:ok, _} = Room.post_message(socket.assigns.room, response, "jido")
{:ok, messages} = Room.get_messages(socket.assigns.room)
{:noreply,
socket
|> assign(is_typing: false, agent_response_ref: nil, messages: messages)}
_ ->
# Stale response, ignore it
{:noreply, socket}
end
end
defp update_messages(socket) do
{:ok, messages} = Room.get_messages(socket.assigns.room)
assign(socket, messages: messages, history_index: 0)
end
defp get_participant_name(participant_id) do
case participant_id do
"operator" -> "Operator"
"jido" -> "Agent Jido"
_ -> participant_id
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment