fix: email place holder

This commit is contained in:
Ephraim Atta-Duncan
2025-11-18 17:59:36 +00:00
parent dbed8b362e
commit 13bd5815d9
3 changed files with 8 additions and 74 deletions

View File

@ -9,31 +9,6 @@ export type AiRecipient = {
signingOrder?: number; signingOrder?: number;
}; };
const sanitizeEmailLocalPart = (value: string) => {
return value
.toLowerCase()
.replace(/[^a-z0-9]+/g, '.')
.replace(/\.+/g, '.')
.replace(/^\.+|\.+$/g, '')
.slice(0, 32);
};
export const createPlaceholderRecipientEmail = (
name: string,
envelopeId: string,
position: number,
) => {
const normalizedName = sanitizeEmailLocalPart(name);
const baseLocalPart = normalizedName ? `${normalizedName}.${position}` : `recipient-${position}`;
const envelopeSuffix = envelopeId
.replace(/[^a-z0-9]/gi, '')
.toLowerCase()
.slice(0, 6);
const suffix = envelopeSuffix ? `-${envelopeSuffix}` : '';
return `${baseLocalPart}${suffix}@documenso.ai`;
};
export const analyzeRecipientsFromDocument = async (envelopeId: string): Promise<AiRecipient[]> => { export const analyzeRecipientsFromDocument = async (envelopeId: string): Promise<AiRecipient[]> => {
try { try {
const response = await fetch('/api/ai/analyze-recipients', { const response = await fetch('/api/ai/analyze-recipients', {
@ -65,7 +40,6 @@ export const ensureRecipientEmails = (
recipients: AiRecipient[], recipients: AiRecipient[],
envelopeId: string, envelopeId: string,
): RecipientForCreation[] => { ): RecipientForCreation[] => {
let recipientIndex = 1;
const allowedRoles: RecipientRole[] = [ const allowedRoles: RecipientRole[] = [
RecipientRole.SIGNER, RecipientRole.SIGNER,
RecipientRole.APPROVER, RecipientRole.APPROVER,
@ -73,11 +47,7 @@ export const ensureRecipientEmails = (
]; ];
return recipients.map((recipient) => { return recipients.map((recipient) => {
const email = const email = recipient.email ?? '';
recipient.email ??
createPlaceholderRecipientEmail(recipient.name, envelopeId, recipientIndex);
recipientIndex += 1;
const candidateRole = recipient.role as RecipientRole; const candidateRole = recipient.role as RecipientRole;
const normalizedRole = allowedRoles.includes(candidateRole) const normalizedRole = allowedRoles.includes(candidateRole)

View File

@ -227,7 +227,7 @@ const runFormFieldDetection = async (
const prompt = buildFieldDetectionPrompt(recipients); const prompt = buildFieldDetectionPrompt(recipients);
const result = await generateObject({ const result = await generateObject({
model: 'google/gemini-2.5-pro', model: 'google/gemini-3-pro-preview',
output: 'array', output: 'array',
schema: ZDetectedFormFieldSchema, schema: ZDetectedFormFieldSchema,
messages: [ messages: [
@ -284,33 +284,7 @@ const MAX_PAGES_FOR_RECIPIENT_ANALYSIS = 3;
const recipientEmailSchema = z.string().email(); const recipientEmailSchema = z.string().email();
const sanitizeEmailLocalPart = (value: string) => { const resolveRecipientEmail = (candidateEmail: string | undefined) => {
return value
.toLowerCase()
.replace(/[^a-z0-9]+/g, '.')
.replace(/\.+/g, '.')
.replace(/^\.+|\.+$/g, '')
.slice(0, 32);
};
const createPlaceholderRecipientEmail = (name: string, envelopeId: string, position: number) => {
const normalizedName = sanitizeEmailLocalPart(name);
const baseLocalPart = normalizedName ? `${normalizedName}.${position}` : `recipient-${position}`;
const sanitizedEnvelopeSuffix = envelopeId
.replace(/[^a-z0-9]/gi, '')
.toLowerCase()
.slice(0, 6);
const suffix = sanitizedEnvelopeSuffix ? `-${sanitizedEnvelopeSuffix}` : '';
return `${baseLocalPart}${suffix}@documenso.ai`;
};
const resolveRecipientEmail = (
candidateEmail: string | undefined,
name: string,
envelopeId: string,
position: number,
) => {
if (candidateEmail) { if (candidateEmail) {
const trimmedEmail = candidateEmail.trim(); const trimmedEmail = candidateEmail.trim();
@ -319,7 +293,7 @@ const resolveRecipientEmail = (
} }
} }
return createPlaceholderRecipientEmail(name, envelopeId, position); return undefined;
}; };
const authorizeDocumentAccess = async (envelopeId: string, userId: number) => { const authorizeDocumentAccess = async (envelopeId: string, userId: number) => {
@ -388,13 +362,11 @@ EXTRACTION RULES:
3. Look for "Approved by:", "Reviewed by:", "CC:" sections 3. Look for "Approved by:", "Reviewed by:", "CC:" sections
4. Extract FULL NAMES as they appear in the document 4. Extract FULL NAMES as they appear in the document
5. If an email address is visible near a name, include it exactly in the "email" field 5. If an email address is visible near a name, include it exactly in the "email" field
6. If NO email is found, create a realistic placeholder email using the person's name and the domain "documenso.ai" (e.g., john.doe@documenso.ai). Every recipient MUST have an email. 6. If NO email is found, leave the email field empty.
7. Ensure placeholder emails look unique when multiple people share the same name (append a number if needed) 7. Assign signing order based on document flow (numbered items, "First signer:", "Second signer:", or top-to-bottom sequence)
8. Assign signing order based on document flow (numbered items, "First signer:", "Second signer:", or top-to-bottom sequence)
IMPORTANT: IMPORTANT:
- Only extract recipients explicitly mentioned in the document - Only extract recipients explicitly mentioned in the document
- Email is mandatory for every recipient (real, sample, or placeholder derived from the document text)
- Default role is SIGNER if unclear (signature lines = SIGNER) - Default role is SIGNER if unclear (signature lines = SIGNER)
- Signing order starts at 1 (first signer = 1, second = 2, etc.) - Signing order starts at 1 (first signer = 1, second = 2, etc.)
- If no clear ordering, omit signingOrder - If no clear ordering, omit signingOrder
@ -710,12 +682,7 @@ export const aiRoute = new Hono<HonoEnv>()
const { pageNumber, recipients } = result.value; const { pageNumber, recipients } = result.value;
const recipientsWithEmails = recipients.map((recipient) => { const recipientsWithEmails = recipients.map((recipient) => {
const email = resolveRecipientEmail( const email = resolveRecipientEmail(recipient.email);
recipient.email,
recipient.name,
envelopeId,
recipientIndex,
);
const normalizedRecipient: TDetectedRecipient = { const normalizedRecipient: TDetectedRecipient = {
...recipient, ...recipient,

View File

@ -84,10 +84,7 @@ export const ZDetectedRecipientLLMSchema = createRecipientSchema(
); );
export const ZDetectedRecipientSchema = createRecipientSchema( export const ZDetectedRecipientSchema = createRecipientSchema(
z z.string().email().optional().describe('Email address for the recipient (if found in document).'),
.string()
.email()
.describe('Email address for the recipient (real, sample, or generated placeholder).'),
); );
export const ZAnalyzeRecipientsRequestSchema = z.object({ export const ZAnalyzeRecipientsRequestSchema = z.object({