Record user feedback against a generation or session β the substrate for evals and fine-tuning datasets. @bryel/feedback runs server-side with zero dependencies.
Record feedback
Call recordFeedback with the target youβre scoring and the kind of signal you captured.
import { recordFeedback, thumbsUp } from "@bryel/feedback";
await recordFeedback({
apiKey: process.env.BRYEL_KEY!,
target: { type: "message", id: messageId }, // or { type: "session", id: sessionId }
kind: "thumb", // thumb | score | comment | correction | label
score: 1, // π=1 / π=0, or a scalar
userId, // β annotator
source: "end_user", // end_user | labeler | model_judge | code
});
// shorthands
await thumbsUp({ apiKey, target: { type: "message", id: messageId } });
See the feedback API reference for every field and shorthand.
The messageId handshake
Feedback arrives after the answer, so the client needs a handle that also exists on the trace. Mint a messageId per turn, pass it into telemetry metadata (bryel stamps it as bryel.interaction.id), and return it to the client.
Mint, stamp, and return on the server
const messageId = crypto.randomUUID();
streamText({ /* β¦ */ experimental_telemetry: { isEnabled: true, metadata: { sessionId, userId, messageId } } });
return result.toTextStreamResponse({ headers: { "x-bryel-message-id": messageId } });
Hold the id on the client
The browser keeps messageId (from the header) and POSTs a π/π to your backend.
Proxy to bryel from your backend
await recordFeedback({ apiKey: process.env.BRYEL_KEY!, target: { type: "message", id: messageId }, kind: "thumb", score: 1, userId, source: "end_user" });
Keep your API key server-side. Proxy browser clicks through your backend β never ship BRYEL_KEY to the client.
To capture the messageId upstream in the first place, see the Vercel instrumentation guide.