Manage Messages
Recipe for implementing clear, delete, edit, resend, and insert operations on chat messages with Agno agents.
This recipe shows how to implement common message management operations using the setMessages API when using Agno agents on the backend. You'll build UI controls for clearing conversations, deleting messages, editing content, and resending failed messages.
Clearing the Conversation
const { setMessages } = useStreamChat({ api: "/api/chat-agno" });
function handleClear() {
setMessages([]);
}If your Agno backend tracks conversation state, clear it too:
async function handleClear() {
await fetch("/api/chat-agno/clear", { method: "POST" });
setMessages([]);
}Backend Clear Endpoint (FastAPI + Agno)
from fastapi import APIRouter
from agno.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.openrouter import OpenRouter
router = APIRouter()
SESSION_ID = "default-agno"
storage = SqliteDb(
session_table="agno_agent_sessions",
db_file="./dev.db",
)
@router.post("/api/chat-agno/clear")
async def clear_history():
agent = Agent(
model=OpenRouter(id="moonshotai/kimi-k2.5"),
db=storage,
session_id=SESSION_ID,
)
agent.db.delete_session(session_id=SESSION_ID)
return {"status": "cleared"}Removing a Message
Remove a specific message by ID:
function handleDelete(messageId: string) {
setMessages((prev) => prev.filter((msg) => msg.id !== messageId));
}Remove the last message:
function handleRemoveLast() {
setMessages((prev) => prev.slice(0, -1));
}Editing a Message
Replace a message's content:
function handleEdit(messageId: string, newText: string) {
setMessages((prev) =>
prev.map((msg) =>
msg.id === messageId
? { ...msg, parts: [{ type: "text", text: newText }] }
: msg,
),
);
}Resending a Message
A common pattern is editing the last user message and re-triggering the response. Remove the last assistant message, then send again:
const { messages, setMessages, sendMessage } = useStreamChat({
api: "/api/chat-agno",
});
function handleResend() {
const lastUserMsg = [...messages].reverse().find((m) => m.role === "user");
if (!lastUserMsg) return;
const lastUserText = lastUserMsg.parts
.filter((p) => p.type === "text")
.map((p) => p.text)
.join("");
// Remove the last assistant response (and optionally the user message)
setMessages((prev) => {
const idx = prev.findLastIndex((m) => m.role === "user");
return prev.slice(0, idx);
});
// Re-send (triggers a new stream)
sendMessage(lastUserText);
}Inserting a System-Style Message
You can programmatically insert messages without triggering a stream:
function addNotice(text: string) {
setMessages((prev) => [
...prev,
{
id: `notice_${Date.now()}`,
role: "assistant" as const,
parts: [{ type: "text" as const, text }],
},
]);
}
// Usage
addNotice("The model has been switched to GPT-4.");Functional Updates
setMessages supports functional updates just like React's useState. Always use the functional form when your update depends on the current state:
// Correct - uses the latest state
setMessages((prev) => prev.filter((msg) => msg.id !== id));
// Avoid - may use stale state
setMessages(messages.filter((msg) => msg.id !== id));