📋 refactor: Attach Message Context to Langfuse Feedback Scores (#13604)

This commit is contained in:
Danny Avila 2026-06-08 15:54:01 -04:00 committed by GitHub
parent 98755d86c8
commit ae0c187ddd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 56 additions and 2 deletions

View file

@ -401,6 +401,17 @@ router.put('/:conversationId/:messageId/feedback', validateMessageReq, async (re
sendFeedbackScore({
traceId: traceIdForMessage(messageId),
feedback: updatedMessage.feedback,
metadata: {
messageId: updatedMessage.messageId ?? messageId,
parentMessageId: updatedMessage.parentMessageId,
conversationId: updatedMessage.conversationId ?? conversationId,
sessionId: updatedMessage.conversationId ?? conversationId,
userId: req?.user?.id,
endpoint: updatedMessage.endpoint,
sender: updatedMessage.sender,
isCreatedByUser: updatedMessage.isCreatedByUser,
tokenCount: updatedMessage.tokenCount,
},
}).catch((err) => logger.error('[langfuse] feedback score failed:', err));
}

View file

@ -59,6 +59,16 @@ describe('Langfuse feedback scores', () => {
await sendFeedbackScore({
traceId: 'trace-id',
feedback: { rating: 'thumbsUp', tag: 'helpful', text: 'nice' },
metadata: {
messageId: 'message-id',
conversationId: 'conversation-id',
sessionId: 'conversation-id',
userId: 'user-id',
endpoint: 'agents',
empty: '',
missing: undefined,
},
observationId: 'observation-id',
});
expect(getFetchMock()).toHaveBeenCalledWith(
@ -80,8 +90,19 @@ describe('Langfuse feedback scores', () => {
value: 1,
dataType: 'BOOLEAN',
comment: 'helpful — nice',
metadata: { rating: 'thumbsUp', tag: 'helpful' },
observationId: 'observation-id',
metadata: {
rating: 'thumbsUp',
tag: 'helpful',
messageId: 'message-id',
conversationId: 'conversation-id',
sessionId: 'conversation-id',
userId: 'user-id',
endpoint: 'agents',
},
});
expect(JSON.parse(init?.body as string).metadata).not.toHaveProperty('empty');
expect(JSON.parse(init?.body as string).metadata).not.toHaveProperty('missing');
});
it('posts feedback scores to the configured Langfuse host', async () => {

View file

@ -6,9 +6,13 @@ export type LangfuseFeedback = {
text?: string;
};
export type LangfuseFeedbackMetadata = Record<string, string | number | boolean | null | undefined>;
export type SendFeedbackScoreParams = {
traceId: string;
feedback?: LangfuseFeedback | null;
metadata?: LangfuseFeedbackMetadata;
observationId?: string;
};
const DEFAULT_BASE_URL = 'https://cloud.langfuse.com';
@ -42,9 +46,26 @@ const AUTHORIZATION = ENABLED
: undefined;
const ENVIRONMENT = process.env.LANGFUSE_TRACING_ENVIRONMENT;
function cleanMetadata(
metadata: LangfuseFeedbackMetadata,
): Record<string, string | number | boolean> {
return Object.entries(metadata).reduce<Record<string, string | number | boolean>>(
(result, [key, value]) => {
if (value == null || (typeof value === 'string' && value.trim() === '')) {
return result;
}
result[key] = value;
return result;
},
{},
);
}
export async function sendFeedbackScore({
traceId,
feedback,
metadata = {},
observationId,
}: SendFeedbackScoreParams): Promise<void> {
if (!ENABLED || !AUTHORIZATION || !traceId) {
return;
@ -70,7 +91,8 @@ export async function sendFeedbackScore({
value: feedback.rating === 'thumbsUp' ? 1 : 0,
dataType: 'BOOLEAN',
comment: [feedback.tag, feedback.text].filter(Boolean).join(' — ') || undefined,
metadata: { rating: feedback.rating, tag: feedback.tag },
metadata: cleanMetadata({ ...metadata, rating: feedback.rating, tag: feedback.tag }),
...(observationId ? { observationId } : {}),
...(ENVIRONMENT ? { environment: ENVIRONMENT } : {}),
};