@bryel/feedback records user feedback against a generation or session โ the
substrate for evals and fine-tuning datasets. Server-side, zero dependencies.
Record feedback
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 } });
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:
Server: mint + stamp + return
const messageId = crypto.randomUUID();
streamText({ /* โฆ */ experimental_telemetry: { isEnabled: true, metadata: { sessionId, userId, messageId } } });
return result.toTextStreamResponse({ headers: { "x-bryel-message-id": messageId } });
Client: hold the id, send the score
The browser keeps messageId (from the header) and POSTs a ๐/๐ to your backend.
Backend: proxy to bryel
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 the key to the client.