Hi there Retool Team,
I've been trying to figure out the best way to pass context from an app to the chatAgent component, and currently it seems veeeery limited.
I was THIS close to actually achieve it by creating my own logic with regards to the messages sent to the agent where I could include an extra message with the context. It actually WORKS, except for the fact that the chatAgent component needs to be linked to a Retool Agents
query.
Here's a video explaining what I managed to do:
In a nutshell below are some comments/feature requests:
- Enable a
setAgentId
function for the chatAgent component - Enable a
setQueryTargetId
function for the chatAgent component - Enable a new editable property within the chatAgent component settings that dynamically allows to define what is the text pushed for the userMessage. It would allow to add some markup that shows that context has been added.
Looking forward to hearing your thoughts. Tagging in particular @timofey and @kent
Below the js query I used, which has a success event handler to re run the js query unless {{ agentInput.value.action === "invoke"}}
const isPolling = agentInput.value?.action === "getLogs";
if (!isPolling) {
const chatMessages = agentChat1.messages || [];
const lastMessage = chatMessages.length > 0 ? chatMessages[chatMessages.length - 1] : null;
const userMessage = lastMessage?.content?.text || "";
const selectedData = gmap1.proximityItems;
const messageHistory = Array.isArray(agentInput.value?.messages) ? agentInput.value.messages : [];
if (!userMessage?.trim()) {
return; // Skip processing if the last user message is empty
}
// Step 1: Build a new array with all valid messages
const messages = [...messageHistory];
if (selectedData?.length) {
messages.push({
role: "user",
content: JSON.stringify(selectedData)
});
// Prepend context to userMessage
messages.push({
role: "user",
content: `**[Context: Map, selected markers]**\n${userMessage.trim()}`
});
} else {
messages.push({
role: "user",
content: userMessage.trim()
});
}
agentInput.setValue({
messages,
action: "invoke"
});
console.log(messageHistory);
console.log(userMessage);
// Step 3: Trigger invokeAgent and set agentInput to getLogs on success
await invokeAgent.trigger({
onSuccess: (data) => {
const agentRunId = data.agentRunId;
if (!agentRunId) {
throw new Error("Missing agentRunId in invokeAgent response.");
}
// Switch to polling mode: action = getLogs
agentInput.setValue({
messages,
action: "getLogs",
agentRunId
});
}
});
// Ensure agentInput.setValue has completed before polling
await new Promise((res) => setTimeout(res, 100));
}
async function getAgentData() {
return new Promise((resolve, reject) => {
invokeAgent.trigger({
onSuccess: async (data) => {
if (data.status === "COMPLETED") {
console.log(data);
const history = Array.isArray(agentInput.value?.messages)
? [...agentInput.value.messages]
: [];
history.push({
role: "assistant",
content: data.resultText,
});
// Set both action and messages
await agentInput.setValue({
...agentInput.value,
action: "stop",
messages: history,
});
}
resolve(data); // Return original data
},
onFailure: (err) => reject(err),
});
});
}
return await getAgentData();