fix: keep notification summaries in plain text
- Force notification summaries to avoid markdown formatting - Normalize generated notification text before system display - Cover plain-text notification formatting with tests
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
# Notifications Module Documentation
|
# Notifications Module Documentation
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
This module provides notification message preparation utilities for the web server runtime, including text truncation and optional message summarization for system notifications.
|
This module provides notification message preparation utilities for the web server runtime, including text truncation, plain-text normalization, and optional message summarization for system notifications.
|
||||||
|
|
||||||
## Entrypoints and structure
|
## Entrypoints and structure
|
||||||
- `packages/web/server/lib/notifications/index.js`: public entrypoint imported by `packages/web/server/index.js`.
|
- `packages/web/server/lib/notifications/index.js`: public entrypoint imported by `packages/web/server/index.js`.
|
||||||
@@ -107,6 +107,7 @@ The `settings` parameter for `prepareNotificationLastMessage` supports:
|
|||||||
- Returns empty string for empty/null message.
|
- Returns empty string for empty/null message.
|
||||||
- Returns truncated original message if summarization disabled, message under threshold, or summarization fails.
|
- Returns truncated original message if summarization disabled, message under threshold, or summarization fails.
|
||||||
- Returns truncated summary if summarization succeeds and returns non-empty string.
|
- Returns truncated summary if summarization succeeds and returns non-empty string.
|
||||||
|
- Normalizes markdown-like formatting to plain text before truncation.
|
||||||
- Always applies `maxLastMessageLength` truncation to final result.
|
- Always applies `maxLastMessageLength` truncation to final result.
|
||||||
|
|
||||||
## Notes for contributors
|
## Notes for contributors
|
||||||
|
|||||||
@@ -9,6 +9,26 @@ const resolvePositiveNumber = (value, fallback) => {
|
|||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const normalizeNotificationPlainText = (text) => {
|
||||||
|
if (typeof text !== 'string') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return text
|
||||||
|
.replace(/```[\s\S]*?```/g, ' ')
|
||||||
|
.replace(/`([^`]*)`/g, '$1')
|
||||||
|
.replace(/^[\t ]*[-*+]\s+/gm, '')
|
||||||
|
.replace(/^#{1,6}\s+/gm, '')
|
||||||
|
.replace(/\*\*(.*?)\*\*/g, '$1')
|
||||||
|
.replace(/__(.*?)__/g, '$1')
|
||||||
|
.replace(/\*(.*?)\*/g, '$1')
|
||||||
|
.replace(/_(.*?)_/g, '$1')
|
||||||
|
.replace(/\[(.*?)\]\((.*?)\)/g, '$1')
|
||||||
|
.replace(/\s*\n\s*/g, ' ')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim();
|
||||||
|
};
|
||||||
|
|
||||||
export const truncateNotificationText = (text, maxLength = DEFAULT_NOTIFICATION_MESSAGE_MAX_LENGTH) => {
|
export const truncateNotificationText = (text, maxLength = DEFAULT_NOTIFICATION_MESSAGE_MAX_LENGTH) => {
|
||||||
if (typeof text !== 'string') {
|
if (typeof text !== 'string') {
|
||||||
return '';
|
return '';
|
||||||
@@ -45,5 +65,6 @@ export const prepareNotificationLastMessage = async ({ message, settings, summar
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return truncateNotificationText(messageForNotification, maxLastMessageLength);
|
const plainTextMessage = normalizeNotificationPlainText(messageForNotification) || normalizeNotificationPlainText(originalMessage);
|
||||||
|
return truncateNotificationText(plainTextMessage, maxLastMessageLength);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -56,4 +56,19 @@ describe('notification message helpers', () => {
|
|||||||
|
|
||||||
expect(result).toBe('short summary');
|
expect(result).toBe('short summary');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('normalizes markdown summary to plain text', async () => {
|
||||||
|
const result = await prepareNotificationLastMessage({
|
||||||
|
message: '0123456789',
|
||||||
|
summarize: async () => "**Committed.**\n\n- Commit: `85924b9d`\n- Message: `fix desktop notifications`",
|
||||||
|
settings: {
|
||||||
|
summarizeLastMessage: true,
|
||||||
|
summaryThreshold: 5,
|
||||||
|
summaryLength: 80,
|
||||||
|
maxLastMessageLength: 200,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe('Committed. Commit: 85924b9d Message: fix desktop notifications');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -134,7 +134,6 @@ export const createNotificationTriggerRuntime = (deps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sessionId = extractSessionIdFromPayload(payload);
|
const sessionId = extractSessionIdFromPayload(payload);
|
||||||
|
|
||||||
if (payload.type === 'message.updated') {
|
if (payload.type === 'message.updated') {
|
||||||
const info = payload.properties?.info;
|
const info = payload.properties?.info;
|
||||||
if (info?.role === 'assistant' && info?.finish === 'stop' && sessionId) {
|
if (info?.role === 'assistant' && info?.finish === 'stop' && sessionId) {
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ export const createNotificationTemplateRuntime = (deps) => {
|
|||||||
if (!text || typeof text !== 'string' || text.trim().length === 0) return text;
|
if (!text || typeof text !== 'string' || text.trim().length === 0) return text;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const prompt = `Summarize the following text in approximately ${targetLength} characters. Be concise and capture the key point. Output ONLY the summary text, nothing else.\n\nText:\n${text}`;
|
const prompt = `Summarize the following text in approximately ${targetLength} characters. Be concise and capture the key point. Output plain text only. Do not use markdown, bullets, headings, code fences, backticks, or quotes. Output only the summary text.\n\nText:\n${text}`;
|
||||||
|
|
||||||
const completionTimeout = createTimeoutSignal(15000);
|
const completionTimeout = createTimeoutSignal(15000);
|
||||||
let response;
|
let response;
|
||||||
|
|||||||
Reference in New Issue
Block a user