Skip to main content
@bryel/feedback records user feedback against a generation or session โ€” the substrate for evals and fine-tuning datasets. Server-side, zero dependencies.
npm i @bryel/feedback

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:
1

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 } });
2

Client: hold the id, send the score

The browser keeps messageId (from the header) and POSTs a ๐Ÿ‘/๐Ÿ‘Ž to your backend.
3

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.