Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions apps/web/__tests__/ai/reply/draft-follow-up.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,102 @@ describe.runIf(isAiTest)("aiDraftFollowUp", () => {
},
TEST_TIMEOUT,
);

test(
"drafts follow-up in Spanish when thread is in Spanish",
async () => {
const emailAccount = getEmailAccount();
const messages: TestMessage[] = [
{
id: "msg-1",
from: "cliente@example.com",
to: "user@example.com",
subject: "Pregunta sobre el proyecto",
date: new Date(Date.now() - TIMEOUT * 2),
content:
"Hola, ¿podrían enviarme la propuesta actualizada? Necesito revisarla antes de la reunión del viernes.",
},
{
id: "msg-2",
from: "user@example.com",
to: "cliente@example.com",
subject: "Re: Pregunta sobre el proyecto",
date: new Date(Date.now() - TIMEOUT),
content:
"Hola, por supuesto. Te enviaré la propuesta mañana por la mañana. ¿Hay algo específico que debería incluir?",
},
];

const result = await aiDraftFollowUp({
messages,
emailAccount,
writingStyle: null,
});

expect(result).toBeTypeOf("string");
if (typeof result === "string") {
expect(result.length).toBeGreaterThan(0);
const hasSpanishIndicators =
result.includes("Hola") ||
result.includes("seguimiento") ||
result.includes("recordar") ||
result.includes("anterior") ||
result.includes("respuesta") ||
/[áéíóúñ¿¡]/i.test(result);
expect(hasSpanishIndicators).toBe(true);
}
console.debug("Generated Spanish follow-up:\n", result);
},
TEST_TIMEOUT,
);

test(
"drafts follow-up in French when thread is in French",
async () => {
const emailAccount = getEmailAccount();
const messages: TestMessage[] = [
{
id: "msg-1",
from: "client@example.com",
to: "user@example.com",
subject: "Question sur le projet",
date: new Date(Date.now() - TIMEOUT * 2),
content:
"Bonjour, pourriez-vous m'envoyer la proposition mise à jour? J'ai besoin de la revoir avant la réunion de vendredi.",
},
{
id: "msg-2",
from: "user@example.com",
to: "client@example.com",
subject: "Re: Question sur le projet",
date: new Date(Date.now() - TIMEOUT),
content:
"Bonjour, bien sûr. Je vous enverrai la proposition demain matin. Y a-t-il quelque chose de spécifique que je devrais inclure?",
},
];

const result = await aiDraftFollowUp({
messages,
emailAccount,
writingStyle: null,
});

expect(result).toBeTypeOf("string");
if (typeof result === "string") {
expect(result.length).toBeGreaterThan(0);
const hasFrenchIndicators =
result.includes("Bonjour") ||
result.includes("suivi") ||
result.includes("rappeler") ||
result.includes("précédent") ||
result.includes("réponse") ||
/[àâçéèêëïîôùûü]/i.test(result);
expect(hasFrenchIndicators).toBe(true);
}
console.debug("Generated French follow-up:\n", result);
},
TEST_TIMEOUT,
);
});

type TestMessage = EmailForLLM & { to: string };
Expand Down
90 changes: 90 additions & 0 deletions apps/web/__tests__/ai/reply/draft-reply.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,96 @@ describe.runIf(isAiTest)("aiDraftReply", () => {
},
TEST_TIMEOUT,
);

test(
"drafts reply in Spanish when thread is in Spanish",
async () => {
const emailAccount = getEmailAccount();
const messages: TestMessage[] = [
{
id: "msg-1",
from: "cliente@example.com",
to: "user@example.com",
subject: "Consulta sobre servicios",
date: new Date(Date.now() - TIMEOUT),
content:
"Buenos días, estoy interesado en sus servicios de consultoría. ¿Podrían proporcionarme más información sobre sus tarifas y disponibilidad?",
},
];

const result = await aiDraftReply({
messages,
emailAccount,
knowledgeBaseContent: null,
emailHistorySummary: null,
writingStyle: null,
emailHistoryContext: null,
calendarAvailability: null,
mcpContext: null,
meetingContext: null,
});

expect(result).toBeTypeOf("string");
if (typeof result === "string") {
expect(result.length).toBeGreaterThan(0);
const hasSpanishIndicators =
result.includes("Hola") ||
result.includes("Buenos") ||
result.includes("gracias") ||
result.includes("información") ||
result.includes("servicios") ||
/[áéíóúñ¿¡]/i.test(result);
expect(hasSpanishIndicators).toBe(true);
}
console.debug("Generated Spanish reply:\n", result);
},
TEST_TIMEOUT,
);

test(
"drafts reply in German when thread is in German",
async () => {
const emailAccount = getEmailAccount();
const messages: TestMessage[] = [
{
id: "msg-1",
from: "kunde@example.com",
to: "user@example.com",
subject: "Anfrage zu Ihren Dienstleistungen",
date: new Date(Date.now() - TIMEOUT),
content:
"Guten Tag, ich interessiere mich für Ihre Beratungsdienstleistungen. Könnten Sie mir weitere Informationen zu Ihren Preisen und Ihrer Verfügbarkeit geben?",
},
];

const result = await aiDraftReply({
messages,
emailAccount,
knowledgeBaseContent: null,
emailHistorySummary: null,
writingStyle: null,
emailHistoryContext: null,
calendarAvailability: null,
mcpContext: null,
meetingContext: null,
});

expect(result).toBeTypeOf("string");
if (typeof result === "string") {
expect(result.length).toBeGreaterThan(0);
const hasGermanIndicators =
result.includes("Guten") ||
result.includes("Hallo") ||
result.includes("danke") ||
result.includes("Informationen") ||
result.includes("freuen") ||
/[äöüß]/i.test(result);
expect(hasGermanIndicators).toBe(true);
}
console.debug("Generated German reply:\n", result);
},
TEST_TIMEOUT,
);
});

type TestMessage = EmailForLLM & { to: string };
Expand Down
4 changes: 3 additions & 1 deletion apps/web/utils/ai/reply/draft-follow-up.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ Write a friendly follow-up that:
Don't mention that you're an AI.
Don't reply with a Subject. Only reply with the body of the email.

Examples of good follow-up phrases: "Just checking in on this", "Wanted to follow up on my previous email", "Circling back on this"
IMPORTANT: You MUST write your response in the same language as the email thread. Detect the language used in the conversation and match it exactly. For example, if the thread is in Spanish, write the follow-up in Spanish. If it's in French, write in French. Do not default to English unless the thread is in English.

Examples of good follow-up phrases (in English): "Just checking in on this", "Wanted to follow up on my previous email", "Circling back on this"

Return your response in JSON format.
`;
Expand Down
2 changes: 2 additions & 0 deletions apps/web/utils/ai/reply/draft-reply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ Never use placeholders for the user's name. You do not need to sign off with the
Do not invent information.
Don't suggest meeting times or mention availability unless specific calendar information is provided.

IMPORTANT: You MUST write your response in the same language as the email thread. Detect the language used in the conversation and match it exactly. For example, if the thread is in Spanish, write the reply in Spanish. If it's in French, write in French. Do not default to English unless the thread is in English.

Write an email that follows up on the previous conversation.
Your reply should aim to continue the conversation or provide new information based on the context or knowledge base. If you have nothing substantial to add, keep the reply minimal.

Expand Down
Loading