Custom Agents
Build custom chat and AI streaming interfaces using the usePuckAi() API.
Build a chat interface
Use usePuckAi().chat to hold a conversation with the agent, sending and reading messages. Changes are automatically applied to the canvas.
import { useState } from "react";
import { Puck } from "@puckeditor/core";
import { usePuckAi } from "@puckeditor/plugin-ai";
function ChatPlugin() {
const { chat } = usePuckAi();
const [input, setInput] = useState("");
return (
<div>
{chat.messages.map((message) =>
message.parts.map((part, i) =>
part.type === "text" ? <p key={i}>{part.text}</p> : null
)
)}
<form
onSubmit={(e) => {
e.preventDefault();
chat.sendMessage(input);
setInput("");
}}
>
<input value={input} onChange={(e) => setInput(e.target.value)} />
<button>Send</button>
</form>
</div>
);
}
export function Editor() {
return (
<Puck
config={config}
data={data}
plugins={[{ name: "chat", label: "Chat", render: ChatPlugin }]}
/>
);
}Show the agent’s progress
Look up each tool part in chat.toolStatus to show what the agent is doing.
message.parts.map((part, i) => {
if (part.type === "text") return <p key={i}>{part.text}</p>;
// For example, "Updating page"
if ("toolCallId" in part) {
return <p key={i}>{chat.toolStatus[part.toolCallId]?.label}</p>;
}
return null;
});Request a single change
Use usePuckAi().generate() to request a change to the current page, outside of a conversation.
import { usePuckAi } from "@puckeditor/plugin-ai";
function TranslatePlugin({ language }) {
const { generate } = usePuckAi();
return (
<button onClick={() => generate(`Translate this page to ${language}`)}>
Translate
</button>
);
}Replaying generations
You can replay a generation created headlessly using the server generate() API.
To record a generation, use onPart to store each AI Part:
import { generate } from "@puckeditor/cloud-client";
await generate({
prompt: "Add a pricing section",
config,
pageData,
onPart: (part) => {
// Store the part
},
});To replay the parts into the editor, provide the stored parts to usePuckAi().applyPart:
import { usePuckAi } from "@puckeditor/plugin-ai";
function ApplyPlugin() {
const { applyPart } = usePuckAi();
const apply = async () => {
const parts = getMyParts(); // Get parts from wherever you stored them
for await (const part of parts) {
await applyPart(part); // Apply them
}
};
return <button onClick={apply}>Apply</button>;
}By default, the server’s generate will run a single iteration. See multi-step headless generation to learn more.