@realtimex/email-automator 2.12.1 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api/src/services/intelligence.ts +83 -3
- package/api/src/services/processor.ts +18 -1
- package/dist/api/src/services/intelligence.js +53 -3
- package/dist/api/src/services/processor.js +17 -1
- package/dist/assets/{index-Cn89wpYc.js → index-DgzHLGn1.js} +3 -3
- package/dist/index.html +1 -1
- package/package.json +1 -1
- package/supabase/migrations/20260131180000_add_draft_actions_to_rules.sql +133 -0
|
@@ -25,6 +25,8 @@ export const EmailAnalysisSchema = z.object({
|
|
|
25
25
|
.describe('Key points extracted from the email'),
|
|
26
26
|
action_items: z.array(z.string()).optional()
|
|
27
27
|
.describe('Action items mentioned in the email'),
|
|
28
|
+
language: z.string().optional()
|
|
29
|
+
.describe('The primary language of the email (e.g., "English", "Vietnamese", "Japanese", "Spanish")'),
|
|
28
30
|
});
|
|
29
31
|
|
|
30
32
|
export type EmailAnalysis = z.infer<typeof EmailAnalysisSchema>;
|
|
@@ -205,7 +207,26 @@ REQUIRED JSON STRUCTURE:
|
|
|
205
207
|
async generateDraftReply(
|
|
206
208
|
originalEmail: { subject: string; sender: string; body: string },
|
|
207
209
|
instructions?: string,
|
|
208
|
-
llmSettings?: { llm_provider?: string; llm_model?: string }
|
|
210
|
+
llmSettings?: { llm_provider?: string; llm_model?: string },
|
|
211
|
+
richContext?: {
|
|
212
|
+
// User/Account metadata
|
|
213
|
+
myEmail?: string;
|
|
214
|
+
myName?: string;
|
|
215
|
+
myRole?: string;
|
|
216
|
+
myCompany?: string;
|
|
217
|
+
|
|
218
|
+
// Email analysis metadata
|
|
219
|
+
category?: string;
|
|
220
|
+
sentiment?: string;
|
|
221
|
+
priority?: string;
|
|
222
|
+
keyPoints?: string[];
|
|
223
|
+
language?: string;
|
|
224
|
+
|
|
225
|
+
// Sender metadata
|
|
226
|
+
senderEmail?: string;
|
|
227
|
+
senderName?: string;
|
|
228
|
+
receivedDate?: Date;
|
|
229
|
+
}
|
|
209
230
|
): Promise<string | null> {
|
|
210
231
|
const sdk = SDKService.getSDK();
|
|
211
232
|
if (!sdk) return null;
|
|
@@ -216,14 +237,73 @@ REQUIRED JSON STRUCTURE:
|
|
|
216
237
|
});
|
|
217
238
|
|
|
218
239
|
try {
|
|
240
|
+
// Build rich system prompt with context
|
|
241
|
+
let systemPrompt = 'You are an AI email assistant drafting a professional reply.';
|
|
242
|
+
|
|
243
|
+
if (richContext?.myEmail) {
|
|
244
|
+
systemPrompt += `\n\nYou are responding on behalf of ${richContext.myName || 'the user'} (${richContext.myEmail})`;
|
|
245
|
+
if (richContext.myRole) {
|
|
246
|
+
systemPrompt += `, a ${richContext.myRole}`;
|
|
247
|
+
}
|
|
248
|
+
if (richContext.myCompany) {
|
|
249
|
+
systemPrompt += ` at ${richContext.myCompany}`;
|
|
250
|
+
}
|
|
251
|
+
systemPrompt += '.';
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (richContext?.category) {
|
|
255
|
+
systemPrompt += `\n\nThis is a ${richContext.category} email`;
|
|
256
|
+
if (richContext.sentiment) {
|
|
257
|
+
systemPrompt += ` with a ${richContext.sentiment.toLowerCase()} tone`;
|
|
258
|
+
}
|
|
259
|
+
if (richContext.priority) {
|
|
260
|
+
systemPrompt += ` (priority: ${richContext.priority.toLowerCase()})`;
|
|
261
|
+
}
|
|
262
|
+
systemPrompt += '.';
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (instructions) {
|
|
266
|
+
systemPrompt += `\n\nYOUR SPECIFIC TASK: ${instructions}`;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Language handling - CRITICAL for multi-language support
|
|
270
|
+
if (richContext?.language) {
|
|
271
|
+
systemPrompt += `\n\nIMPORTANT: The incoming email is written in ${richContext.language}. You MUST write your reply in ${richContext.language}. Maintain appropriate formality and cultural conventions for ${richContext.language}.`;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
systemPrompt += '\n\nWrite ONLY the email body (no subject line). Be natural, concise, and professional. Match the tone of the incoming email.';
|
|
275
|
+
|
|
276
|
+
// Build user message with email context
|
|
277
|
+
let userMessage = '';
|
|
278
|
+
|
|
279
|
+
if (richContext?.senderEmail || richContext?.senderName) {
|
|
280
|
+
userMessage += `INCOMING EMAIL:\n`;
|
|
281
|
+
userMessage += `From: ${richContext.senderName || originalEmail.sender}`;
|
|
282
|
+
if (richContext.senderEmail && richContext.senderEmail !== originalEmail.sender) {
|
|
283
|
+
userMessage += ` <${richContext.senderEmail}>`;
|
|
284
|
+
}
|
|
285
|
+
userMessage += '\n';
|
|
286
|
+
if (richContext.receivedDate) {
|
|
287
|
+
userMessage += `Received: ${richContext.receivedDate.toLocaleString()}\n`;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
userMessage += `Subject: ${originalEmail.subject}\n\n`;
|
|
292
|
+
|
|
293
|
+
if (richContext?.keyPoints && richContext.keyPoints.length > 0) {
|
|
294
|
+
userMessage += `KEY POINTS:\n${richContext.keyPoints.map(p => `• ${p}`).join('\n')}\n\n`;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
userMessage += `FULL MESSAGE:\n${originalEmail.body}`;
|
|
298
|
+
|
|
219
299
|
const response = await sdk.llm.chat([
|
|
220
300
|
{
|
|
221
301
|
role: 'system',
|
|
222
|
-
content:
|
|
302
|
+
content: systemPrompt,
|
|
223
303
|
},
|
|
224
304
|
{
|
|
225
305
|
role: 'user',
|
|
226
|
-
content:
|
|
306
|
+
content: userMessage,
|
|
227
307
|
},
|
|
228
308
|
], { provider, model });
|
|
229
309
|
|
|
@@ -802,6 +802,23 @@ export class EmailProcessorService {
|
|
|
802
802
|
|
|
803
803
|
const intelligenceService = getIntelligenceService();
|
|
804
804
|
|
|
805
|
+
// Build rich context for better drafts
|
|
806
|
+
const emailDomain = account.email_address?.split('@')[1] || undefined;
|
|
807
|
+
const richContext = {
|
|
808
|
+
myEmail: account.email_address,
|
|
809
|
+
myName: undefined, // Profile not available in this scope
|
|
810
|
+
myRole: settings?.user_role || undefined,
|
|
811
|
+
myCompany: emailDomain,
|
|
812
|
+
category: analysis?.category,
|
|
813
|
+
sentiment: analysis?.sentiment,
|
|
814
|
+
priority: analysis?.priority,
|
|
815
|
+
keyPoints: analysis?.key_points,
|
|
816
|
+
language: analysis?.language, // Multi-language support
|
|
817
|
+
senderEmail: email.sender || undefined,
|
|
818
|
+
senderName: email.sender || undefined,
|
|
819
|
+
receivedDate: email.date ? new Date(email.date) : undefined
|
|
820
|
+
};
|
|
821
|
+
|
|
805
822
|
const customizedDraft = await intelligenceService.generateDraftReply({
|
|
806
823
|
subject: email.subject || '',
|
|
807
824
|
sender: email.sender || '',
|
|
@@ -809,7 +826,7 @@ export class EmailProcessorService {
|
|
|
809
826
|
}, rule.instructions, {
|
|
810
827
|
llm_provider: settings?.llm_provider,
|
|
811
828
|
llm_model: settings?.llm_model
|
|
812
|
-
});
|
|
829
|
+
}, richContext);
|
|
813
830
|
|
|
814
831
|
if (customizedDraft) {
|
|
815
832
|
draftContent = customizedDraft;
|
|
@@ -22,6 +22,8 @@ export const EmailAnalysisSchema = z.object({
|
|
|
22
22
|
.describe('Key points extracted from the email'),
|
|
23
23
|
action_items: z.array(z.string()).optional()
|
|
24
24
|
.describe('Action items mentioned in the email'),
|
|
25
|
+
language: z.string().optional()
|
|
26
|
+
.describe('The primary language of the email (e.g., "English", "Vietnamese", "Japanese", "Spanish")'),
|
|
25
27
|
});
|
|
26
28
|
// Context-Aware Analysis Schema - AI evaluates email against user's rules
|
|
27
29
|
export const ContextAwareAnalysisSchema = z.object({
|
|
@@ -156,7 +158,7 @@ REQUIRED JSON STRUCTURE:
|
|
|
156
158
|
return null;
|
|
157
159
|
}
|
|
158
160
|
}
|
|
159
|
-
async generateDraftReply(originalEmail, instructions, llmSettings) {
|
|
161
|
+
async generateDraftReply(originalEmail, instructions, llmSettings, richContext) {
|
|
160
162
|
const sdk = SDKService.getSDK();
|
|
161
163
|
if (!sdk)
|
|
162
164
|
return null;
|
|
@@ -165,14 +167,62 @@ REQUIRED JSON STRUCTURE:
|
|
|
165
167
|
llm_model: llmSettings?.llm_model
|
|
166
168
|
});
|
|
167
169
|
try {
|
|
170
|
+
// Build rich system prompt with context
|
|
171
|
+
let systemPrompt = 'You are an AI email assistant drafting a professional reply.';
|
|
172
|
+
if (richContext?.myEmail) {
|
|
173
|
+
systemPrompt += `\n\nYou are responding on behalf of ${richContext.myName || 'the user'} (${richContext.myEmail})`;
|
|
174
|
+
if (richContext.myRole) {
|
|
175
|
+
systemPrompt += `, a ${richContext.myRole}`;
|
|
176
|
+
}
|
|
177
|
+
if (richContext.myCompany) {
|
|
178
|
+
systemPrompt += ` at ${richContext.myCompany}`;
|
|
179
|
+
}
|
|
180
|
+
systemPrompt += '.';
|
|
181
|
+
}
|
|
182
|
+
if (richContext?.category) {
|
|
183
|
+
systemPrompt += `\n\nThis is a ${richContext.category} email`;
|
|
184
|
+
if (richContext.sentiment) {
|
|
185
|
+
systemPrompt += ` with a ${richContext.sentiment.toLowerCase()} tone`;
|
|
186
|
+
}
|
|
187
|
+
if (richContext.priority) {
|
|
188
|
+
systemPrompt += ` (priority: ${richContext.priority.toLowerCase()})`;
|
|
189
|
+
}
|
|
190
|
+
systemPrompt += '.';
|
|
191
|
+
}
|
|
192
|
+
if (instructions) {
|
|
193
|
+
systemPrompt += `\n\nYOUR SPECIFIC TASK: ${instructions}`;
|
|
194
|
+
}
|
|
195
|
+
// Language handling - CRITICAL for multi-language support
|
|
196
|
+
if (richContext?.language) {
|
|
197
|
+
systemPrompt += `\n\nIMPORTANT: The incoming email is written in ${richContext.language}. You MUST write your reply in ${richContext.language}. Maintain appropriate formality and cultural conventions for ${richContext.language}.`;
|
|
198
|
+
}
|
|
199
|
+
systemPrompt += '\n\nWrite ONLY the email body (no subject line). Be natural, concise, and professional. Match the tone of the incoming email.';
|
|
200
|
+
// Build user message with email context
|
|
201
|
+
let userMessage = '';
|
|
202
|
+
if (richContext?.senderEmail || richContext?.senderName) {
|
|
203
|
+
userMessage += `INCOMING EMAIL:\n`;
|
|
204
|
+
userMessage += `From: ${richContext.senderName || originalEmail.sender}`;
|
|
205
|
+
if (richContext.senderEmail && richContext.senderEmail !== originalEmail.sender) {
|
|
206
|
+
userMessage += ` <${richContext.senderEmail}>`;
|
|
207
|
+
}
|
|
208
|
+
userMessage += '\n';
|
|
209
|
+
if (richContext.receivedDate) {
|
|
210
|
+
userMessage += `Received: ${richContext.receivedDate.toLocaleString()}\n`;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
userMessage += `Subject: ${originalEmail.subject}\n\n`;
|
|
214
|
+
if (richContext?.keyPoints && richContext.keyPoints.length > 0) {
|
|
215
|
+
userMessage += `KEY POINTS:\n${richContext.keyPoints.map(p => `• ${p}`).join('\n')}\n\n`;
|
|
216
|
+
}
|
|
217
|
+
userMessage += `FULL MESSAGE:\n${originalEmail.body}`;
|
|
168
218
|
const response = await sdk.llm.chat([
|
|
169
219
|
{
|
|
170
220
|
role: 'system',
|
|
171
|
-
content:
|
|
221
|
+
content: systemPrompt,
|
|
172
222
|
},
|
|
173
223
|
{
|
|
174
224
|
role: 'user',
|
|
175
|
-
content:
|
|
225
|
+
content: userMessage,
|
|
176
226
|
},
|
|
177
227
|
], { provider, model });
|
|
178
228
|
// Check if SDK call failed
|
|
@@ -662,6 +662,22 @@ export class EmailProcessorService {
|
|
|
662
662
|
if (eventLogger)
|
|
663
663
|
await eventLogger.info('Thinking', `Generating customized draft based on rule: ${rule.name}`, undefined, email.id);
|
|
664
664
|
const intelligenceService = getIntelligenceService();
|
|
665
|
+
// Build rich context for better drafts
|
|
666
|
+
const emailDomain = account.email_address?.split('@')[1] || undefined;
|
|
667
|
+
const richContext = {
|
|
668
|
+
myEmail: account.email_address,
|
|
669
|
+
myName: undefined, // Profile not available in this scope
|
|
670
|
+
myRole: settings?.user_role || undefined,
|
|
671
|
+
myCompany: emailDomain,
|
|
672
|
+
category: analysis?.category,
|
|
673
|
+
sentiment: analysis?.sentiment,
|
|
674
|
+
priority: analysis?.priority,
|
|
675
|
+
keyPoints: analysis?.key_points,
|
|
676
|
+
language: analysis?.language, // Multi-language support
|
|
677
|
+
senderEmail: email.sender || undefined,
|
|
678
|
+
senderName: email.sender || undefined,
|
|
679
|
+
receivedDate: email.date ? new Date(email.date) : undefined
|
|
680
|
+
};
|
|
665
681
|
const customizedDraft = await intelligenceService.generateDraftReply({
|
|
666
682
|
subject: email.subject || '',
|
|
667
683
|
sender: email.sender || '',
|
|
@@ -669,7 +685,7 @@ export class EmailProcessorService {
|
|
|
669
685
|
}, rule.instructions, {
|
|
670
686
|
llm_provider: settings?.llm_provider,
|
|
671
687
|
llm_model: settings?.llm_model
|
|
672
|
-
});
|
|
688
|
+
}, richContext);
|
|
673
689
|
if (customizedDraft) {
|
|
674
690
|
draftContent = customizedDraft;
|
|
675
691
|
}
|