@xuda.io/ai_module 1.1.4766 → 1.1.4767

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.
Files changed (2) hide show
  1. package/index.mjs +2715 -569
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -9241,640 +9241,640 @@ Category:`;
9241
9241
  // }
9242
9242
  // };
9243
9243
 
9244
- export const get_person_info = async function (uid, name, email, account_profile_info, email_context = '') {
9245
- // ========== HELPER FUNCTIONS ==========
9244
+ // export const get_person_info = async function (uid, name, email, account_profile_info, email_context = '') {
9245
+ // // ========== HELPER FUNCTIONS ==========
9246
9246
 
9247
- async function hashString(str) {
9248
- // Simple hash for context tracking
9249
- const encoder = new TextEncoder();
9250
- const data = encoder.encode(str);
9251
- const hashBuffer = await crypto.subtle.digest('SHA-256', data);
9252
- const hashArray = Array.from(new Uint8Array(hashBuffer));
9253
- return hashArray
9254
- .map((b) => b.toString(16).padStart(2, '0'))
9255
- .join('')
9256
- .substring(0, 16);
9257
- }
9247
+ // async function hashString(str) {
9248
+ // // Simple hash for context tracking
9249
+ // const encoder = new TextEncoder();
9250
+ // const data = encoder.encode(str);
9251
+ // const hashBuffer = await crypto.subtle.digest('SHA-256', data);
9252
+ // const hashArray = Array.from(new Uint8Array(hashBuffer));
9253
+ // return hashArray
9254
+ // .map((b) => b.toString(16).padStart(2, '0'))
9255
+ // .join('')
9256
+ // .substring(0, 16);
9257
+ // }
9258
9258
 
9259
- function processPersonInfo(data, originalName, originalEmail, emailContext) {
9260
- // Ensure basic fields are populated
9261
- if (!data.person_first_name || !data.person_last_name) {
9262
- const nameParts = originalName.trim().split(/\s+/);
9263
- data.person_first_name = data.person_first_name || nameParts[0] || '';
9264
- data.person_last_name = data.person_last_name || nameParts.slice(1).join(' ') || '';
9265
- }
9259
+ // function processPersonInfo(data, originalName, originalEmail, emailContext) {
9260
+ // // Ensure basic fields are populated
9261
+ // if (!data.person_first_name || !data.person_last_name) {
9262
+ // const nameParts = originalName.trim().split(/\s+/);
9263
+ // data.person_first_name = data.person_first_name || nameParts[0] || '';
9264
+ // data.person_last_name = data.person_last_name || nameParts.slice(1).join(' ') || '';
9265
+ // }
9266
9266
 
9267
- // Set full name if not provided
9268
- if (!data.person_full_name) {
9269
- data.person_full_name = `${data.person_first_name} ${data.person_last_name}`.trim();
9270
- }
9267
+ // // Set full name if not provided
9268
+ // if (!data.person_full_name) {
9269
+ // data.person_full_name = `${data.person_first_name} ${data.person_last_name}`.trim();
9270
+ // }
9271
9271
 
9272
- // Ensure email is set
9273
- data.person_email = data.person_email || originalEmail;
9272
+ // // Ensure email is set
9273
+ // data.person_email = data.person_email || originalEmail;
9274
9274
 
9275
- // Process demographic information
9276
- data.person_age = processAge(data.person_age);
9277
- data.person_gender = data.person_gender || 'unknown';
9278
- data.person_nationality = processNationality(data.person_nationality);
9275
+ // // Process demographic information
9276
+ // data.person_age = processAge(data.person_age);
9277
+ // data.person_gender = data.person_gender || 'unknown';
9278
+ // data.person_nationality = processNationality(data.person_nationality);
9279
9279
 
9280
- // Standardize location fields
9281
- data.person_location_city = cleanText(data.person_location_city);
9282
- data.person_location_state = cleanStateProvince(data.person_location_state);
9283
- data.person_location_country = processCountryCode(data.person_location_country);
9280
+ // // Standardize location fields
9281
+ // data.person_location_city = cleanText(data.person_location_city);
9282
+ // data.person_location_state = cleanStateProvince(data.person_location_state);
9283
+ // data.person_location_country = processCountryCode(data.person_location_country);
9284
9284
 
9285
- // Format full location
9286
- if (!data.person_location_full || data.person_location_full === 'Not available') {
9287
- const locationParts = [];
9288
- if (data.person_location_city && data.person_location_city !== 'Not available') {
9289
- locationParts.push(data.person_location_city);
9290
- }
9291
- if (data.person_location_state && data.person_location_state !== 'Not available') {
9292
- locationParts.push(data.person_location_state);
9293
- }
9294
- if (data.person_location_country && data.person_location_country !== 'Not available') {
9295
- locationParts.push(countryCodeToName(data.person_location_country));
9296
- }
9297
- data.person_location_full = locationParts.length > 0 ? locationParts.join(', ') : 'Not available';
9298
- }
9285
+ // // Format full location
9286
+ // if (!data.person_location_full || data.person_location_full === 'Not available') {
9287
+ // const locationParts = [];
9288
+ // if (data.person_location_city && data.person_location_city !== 'Not available') {
9289
+ // locationParts.push(data.person_location_city);
9290
+ // }
9291
+ // if (data.person_location_state && data.person_location_state !== 'Not available') {
9292
+ // locationParts.push(data.person_location_state);
9293
+ // }
9294
+ // if (data.person_location_country && data.person_location_country !== 'Not available') {
9295
+ // locationParts.push(countryCodeToName(data.person_location_country));
9296
+ // }
9297
+ // data.person_location_full = locationParts.length > 0 ? locationParts.join(', ') : 'Not available';
9298
+ // }
9299
9299
 
9300
- // Clean and validate URLs
9301
- data.person_linkedin = validateURL(data.person_linkedin);
9302
- data.person_twitter = validateURL(data.person_twitter);
9303
- data.person_github = validateURL(data.person_github);
9304
- data.person_website = validateURL(data.person_website);
9300
+ // // Clean and validate URLs
9301
+ // data.person_linkedin = validateURL(data.person_linkedin);
9302
+ // data.person_twitter = validateURL(data.person_twitter);
9303
+ // data.person_github = validateURL(data.person_github);
9304
+ // data.person_website = validateURL(data.person_website);
9305
+
9306
+ // // Clean phone number
9307
+ // data.person_phone = validatePhoneNumber(data.person_phone);
9308
+
9309
+ // // Ensure arrays are properly formatted
9310
+ // data.person_skills = ensureArray(data.person_skills);
9311
+ // data.person_expertise = ensureArray(data.person_expertise);
9312
+ // data.person_education = ensureArray(data.person_education);
9313
+ // data.person_languages = ensureArray(data.person_languages);
9314
+ // data.person_interests = ensureArray(data.person_interests);
9315
+ // data.person_context_insights = ensureArray(data.person_context_insights);
9316
+
9317
+ // // Calculate additional metadata
9318
+ // data.person_name_initials = getInitials(data.person_first_name, data.person_last_name);
9319
+ // data.person_age_range = calculateAgeRange(data.person_age);
9320
+ // data.person_seniority_score = calculateSeniorityScore(data);
9321
+ // data.person_pronouns = getPronounsFromGender(data.person_gender);
9322
+
9323
+ // // Extract insights from email context
9324
+ // if (emailContext && !data.person_context_insights) {
9325
+ // data.person_context_insights = extractContextInsights(emailContext);
9326
+ // }
9305
9327
 
9306
- // Clean phone number
9307
- data.person_phone = validatePhoneNumber(data.person_phone);
9328
+ // // Create structured objects for easier access
9329
+ // data.person_demographics = {
9330
+ // age: data.person_age,
9331
+ // age_range: data.person_age_range,
9332
+ // gender: data.person_gender,
9333
+ // pronouns: data.person_pronouns,
9334
+ // nationality: data.person_nationality,
9335
+ // nationality_name: countryCodeToName(data.person_nationality),
9336
+ // };
9308
9337
 
9309
- // Ensure arrays are properly formatted
9310
- data.person_skills = ensureArray(data.person_skills);
9311
- data.person_expertise = ensureArray(data.person_expertise);
9312
- data.person_education = ensureArray(data.person_education);
9313
- data.person_languages = ensureArray(data.person_languages);
9314
- data.person_interests = ensureArray(data.person_interests);
9315
- data.person_context_insights = ensureArray(data.person_context_insights);
9338
+ // data.person_contact = {
9339
+ // email: data.person_email,
9340
+ // phone: data.person_phone,
9341
+ // location: {
9342
+ // city: data.person_location_city,
9343
+ // state: data.person_location_state,
9344
+ // country: data.person_location_country,
9345
+ // country_name: countryCodeToName(data.person_location_country),
9346
+ // full: data.person_location_full,
9347
+ // },
9348
+ // };
9316
9349
 
9317
- // Calculate additional metadata
9318
- data.person_name_initials = getInitials(data.person_first_name, data.person_last_name);
9319
- data.person_age_range = calculateAgeRange(data.person_age);
9320
- data.person_seniority_score = calculateSeniorityScore(data);
9321
- data.person_pronouns = getPronounsFromGender(data.person_gender);
9350
+ // data.person_professional = {
9351
+ // title: data.person_title,
9352
+ // company: data.person_company,
9353
+ // industry: data.person_industry,
9354
+ // career_level: data.person_career_level,
9355
+ // years_experience: data.person_years_experience,
9356
+ // skills: data.person_skills,
9357
+ // expertise: data.person_expertise,
9358
+ // education: data.person_education,
9359
+ // };
9322
9360
 
9323
- // Extract insights from email context
9324
- if (emailContext && !data.person_context_insights) {
9325
- data.person_context_insights = extractContextInsights(emailContext);
9326
- }
9361
+ // data.person_online = {
9362
+ // linkedin: data.person_linkedin,
9363
+ // twitter: data.person_twitter,
9364
+ // github: data.person_github,
9365
+ // website: data.person_website,
9366
+ // other: data.person_social_other || [],
9367
+ // };
9327
9368
 
9328
- // Create structured objects for easier access
9329
- data.person_demographics = {
9330
- age: data.person_age,
9331
- age_range: data.person_age_range,
9332
- gender: data.person_gender,
9333
- pronouns: data.person_pronouns,
9334
- nationality: data.person_nationality,
9335
- nationality_name: countryCodeToName(data.person_nationality),
9336
- };
9369
+ // // Add context metadata
9370
+ // data.context_analysis = {
9371
+ // has_context: !!emailContext,
9372
+ // context_length: emailContext?.length || 0,
9373
+ // extracted_insights: data.person_context_insights || [],
9374
+ // context_useful: data.person_has_context_hints || false,
9375
+ // };
9337
9376
 
9338
- data.person_contact = {
9339
- email: data.person_email,
9340
- phone: data.person_phone,
9341
- location: {
9342
- city: data.person_location_city,
9343
- state: data.person_location_state,
9344
- country: data.person_location_country,
9345
- country_name: countryCodeToName(data.person_location_country),
9346
- full: data.person_location_full,
9347
- },
9348
- };
9377
+ // // Add timestamp
9378
+ // data.retrieved_at = new Date().toISOString();
9349
9379
 
9350
- data.person_professional = {
9351
- title: data.person_title,
9352
- company: data.person_company,
9353
- industry: data.person_industry,
9354
- career_level: data.person_career_level,
9355
- years_experience: data.person_years_experience,
9356
- skills: data.person_skills,
9357
- expertise: data.person_expertise,
9358
- education: data.person_education,
9359
- };
9380
+ // return data;
9381
+ // }
9360
9382
 
9361
- data.person_online = {
9362
- linkedin: data.person_linkedin,
9363
- twitter: data.person_twitter,
9364
- github: data.person_github,
9365
- website: data.person_website,
9366
- other: data.person_social_other || [],
9367
- };
9383
+ // function processAge(ageInput) {
9384
+ // if (!ageInput || ageInput === 'unknown' || ageInput === 'Not available') {
9385
+ // return 'unknown';
9386
+ // }
9368
9387
 
9369
- // Add context metadata
9370
- data.context_analysis = {
9371
- has_context: !!emailContext,
9372
- context_length: emailContext?.length || 0,
9373
- extracted_insights: data.person_context_insights || [],
9374
- context_useful: data.person_has_context_hints || false,
9375
- };
9388
+ // if (typeof ageInput === 'number') {
9389
+ // return Math.max(18, Math.min(100, ageInput));
9390
+ // }
9376
9391
 
9377
- // Add timestamp
9378
- data.retrieved_at = new Date().toISOString();
9392
+ // if (typeof ageInput === 'string') {
9393
+ // // Parse age ranges like "30-40", "35+", "mid-30s"
9394
+ // const rangeMatch = ageInput.match(/(\d+)-(\d+)/);
9395
+ // if (rangeMatch) {
9396
+ // const avg = Math.floor((parseInt(rangeMatch[1]) + parseInt(rangeMatch[2])) / 2);
9397
+ // return avg;
9398
+ // }
9379
9399
 
9380
- return data;
9381
- }
9400
+ // const numberMatch = ageInput.match(/\d+/);
9401
+ // if (numberMatch) {
9402
+ // const age = parseInt(numberMatch[0]);
9403
+ // if (age >= 18 && age <= 100) return age;
9404
+ // }
9382
9405
 
9383
- function processAge(ageInput) {
9384
- if (!ageInput || ageInput === 'unknown' || ageInput === 'Not available') {
9385
- return 'unknown';
9386
- }
9406
+ // // Handle textual age descriptions
9407
+ // const text = ageInput.toLowerCase();
9408
+ // if (text.includes('twent') || text.includes('20')) return 25;
9409
+ // if (text.includes('thirt') || text.includes('30')) return 35;
9410
+ // if (text.includes('fort') || text.includes('40')) return 45;
9411
+ // if (text.includes('fift') || text.includes('50')) return 55;
9412
+ // if (text.includes('sixt') || text.includes('60')) return 65;
9413
+ // }
9387
9414
 
9388
- if (typeof ageInput === 'number') {
9389
- return Math.max(18, Math.min(100, ageInput));
9390
- }
9415
+ // return 'unknown';
9416
+ // }
9391
9417
 
9392
- if (typeof ageInput === 'string') {
9393
- // Parse age ranges like "30-40", "35+", "mid-30s"
9394
- const rangeMatch = ageInput.match(/(\d+)-(\d+)/);
9395
- if (rangeMatch) {
9396
- const avg = Math.floor((parseInt(rangeMatch[1]) + parseInt(rangeMatch[2])) / 2);
9397
- return avg;
9398
- }
9418
+ // function calculateAgeRange(age) {
9419
+ // if (age === 'unknown' || typeof age !== 'number') return 'Unknown';
9399
9420
 
9400
- const numberMatch = ageInput.match(/\d+/);
9401
- if (numberMatch) {
9402
- const age = parseInt(numberMatch[0]);
9403
- if (age >= 18 && age <= 100) return age;
9404
- }
9421
+ // if (age < 25) return '18-24';
9422
+ // if (age < 30) return '25-29';
9423
+ // if (age < 35) return '30-34';
9424
+ // if (age < 40) return '35-39';
9425
+ // if (age < 50) return '40-49';
9426
+ // if (age < 60) return '50-59';
9427
+ // return '60+';
9428
+ // }
9405
9429
 
9406
- // Handle textual age descriptions
9407
- const text = ageInput.toLowerCase();
9408
- if (text.includes('twent') || text.includes('20')) return 25;
9409
- if (text.includes('thirt') || text.includes('30')) return 35;
9410
- if (text.includes('fort') || text.includes('40')) return 45;
9411
- if (text.includes('fift') || text.includes('50')) return 55;
9412
- if (text.includes('sixt') || text.includes('60')) return 65;
9413
- }
9430
+ // function processNationality(nationality) {
9431
+ // if (!nationality || nationality.toLowerCase() === 'unknown' || nationality.toLowerCase() === 'not available') {
9432
+ // return 'unknown';
9433
+ // }
9414
9434
 
9415
- return 'unknown';
9416
- }
9435
+ // // Convert to uppercase for country codes
9436
+ // const upper = nationality.toUpperCase();
9417
9437
 
9418
- function calculateAgeRange(age) {
9419
- if (age === 'unknown' || typeof age !== 'number') return 'Unknown';
9438
+ // // Map common variations to ISO codes
9439
+ // const countryMap = {
9440
+ // USA: 'US',
9441
+ // 'UNITED STATES': 'US',
9442
+ // AMERICA: 'US',
9443
+ // UK: 'GB',
9444
+ // 'UNITED KINGDOM': 'GB',
9445
+ // ENGLAND: 'GB',
9446
+ // SCOTLAND: 'GB',
9447
+ // CANADA: 'CA',
9448
+ // AUSTRALIA: 'AU',
9449
+ // INDIA: 'IN',
9450
+ // GERMANY: 'DE',
9451
+ // FRANCE: 'FR',
9452
+ // CHINA: 'CN',
9453
+ // JAPAN: 'JP',
9454
+ // BRAZIL: 'BR',
9455
+ // MEXICO: 'MX',
9456
+ // SPAIN: 'ES',
9457
+ // ITALY: 'IT',
9458
+ // NETHERLANDS: 'NL',
9459
+ // SWEDEN: 'SE',
9460
+ // SWITZERLAND: 'CH',
9461
+ // 'SOUTH KOREA': 'KR',
9462
+ // RUSSIA: 'RU',
9463
+ // };
9420
9464
 
9421
- if (age < 25) return '18-24';
9422
- if (age < 30) return '25-29';
9423
- if (age < 35) return '30-34';
9424
- if (age < 40) return '35-39';
9425
- if (age < 50) return '40-49';
9426
- if (age < 60) return '50-59';
9427
- return '60+';
9428
- }
9465
+ // if (countryMap[upper]) {
9466
+ // return countryMap[upper];
9467
+ // }
9429
9468
 
9430
- function processNationality(nationality) {
9431
- if (!nationality || nationality.toLowerCase() === 'unknown' || nationality.toLowerCase() === 'not available') {
9432
- return 'unknown';
9433
- }
9469
+ // // If it's already a 2-letter code, return it
9470
+ // if (/^[A-Z]{2}$/.test(upper)) {
9471
+ // return upper;
9472
+ // }
9434
9473
 
9435
- // Convert to uppercase for country codes
9436
- const upper = nationality.toUpperCase();
9474
+ // return 'unknown';
9475
+ // }
9437
9476
 
9438
- // Map common variations to ISO codes
9439
- const countryMap = {
9440
- USA: 'US',
9441
- 'UNITED STATES': 'US',
9442
- AMERICA: 'US',
9443
- UK: 'GB',
9444
- 'UNITED KINGDOM': 'GB',
9445
- ENGLAND: 'GB',
9446
- SCOTLAND: 'GB',
9447
- CANADA: 'CA',
9448
- AUSTRALIA: 'AU',
9449
- INDIA: 'IN',
9450
- GERMANY: 'DE',
9451
- FRANCE: 'FR',
9452
- CHINA: 'CN',
9453
- JAPAN: 'JP',
9454
- BRAZIL: 'BR',
9455
- MEXICO: 'MX',
9456
- SPAIN: 'ES',
9457
- ITALY: 'IT',
9458
- NETHERLANDS: 'NL',
9459
- SWEDEN: 'SE',
9460
- SWITZERLAND: 'CH',
9461
- 'SOUTH KOREA': 'KR',
9462
- RUSSIA: 'RU',
9463
- };
9477
+ // function processCountryCode(country) {
9478
+ // if (!country || country.toLowerCase() === 'not available') {
9479
+ // return 'unknown';
9480
+ // }
9464
9481
 
9465
- if (countryMap[upper]) {
9466
- return countryMap[upper];
9467
- }
9482
+ // return processNationality(country);
9483
+ // }
9468
9484
 
9469
- // If it's already a 2-letter code, return it
9470
- if (/^[A-Z]{2}$/.test(upper)) {
9471
- return upper;
9472
- }
9485
+ // function countryCodeToName(code) {
9486
+ // if (!code || code === 'unknown') return 'Unknown';
9473
9487
 
9474
- return 'unknown';
9475
- }
9488
+ // const countryNames = {
9489
+ // US: 'United States',
9490
+ // GB: 'United Kingdom',
9491
+ // CA: 'Canada',
9492
+ // AU: 'Australia',
9493
+ // DE: 'Germany',
9494
+ // FR: 'France',
9495
+ // JP: 'Japan',
9496
+ // CN: 'China',
9497
+ // IN: 'India',
9498
+ // BR: 'Brazil',
9499
+ // MX: 'Mexico',
9500
+ // ES: 'Spain',
9501
+ // IT: 'Italy',
9502
+ // NL: 'Netherlands',
9503
+ // SE: 'Sweden',
9504
+ // CH: 'Switzerland',
9505
+ // KR: 'South Korea',
9506
+ // RU: 'Russia',
9507
+ // };
9476
9508
 
9477
- function processCountryCode(country) {
9478
- if (!country || country.toLowerCase() === 'not available') {
9479
- return 'unknown';
9480
- }
9481
-
9482
- return processNationality(country);
9483
- }
9509
+ // return countryNames[code] || code;
9510
+ // }
9484
9511
 
9485
- function countryCodeToName(code) {
9486
- if (!code || code === 'unknown') return 'Unknown';
9512
+ // function getPronounsFromGender(gender) {
9513
+ // switch (gender) {
9514
+ // case 'male':
9515
+ // return ['he/him', 'his'];
9516
+ // case 'female':
9517
+ // return ['she/her', 'hers'];
9518
+ // case 'non-binary':
9519
+ // return ['they/them', 'theirs'];
9520
+ // default:
9521
+ // return ['unknown'];
9522
+ // }
9523
+ // }
9487
9524
 
9488
- const countryNames = {
9489
- US: 'United States',
9490
- GB: 'United Kingdom',
9491
- CA: 'Canada',
9492
- AU: 'Australia',
9493
- DE: 'Germany',
9494
- FR: 'France',
9495
- JP: 'Japan',
9496
- CN: 'China',
9497
- IN: 'India',
9498
- BR: 'Brazil',
9499
- MX: 'Mexico',
9500
- ES: 'Spain',
9501
- IT: 'Italy',
9502
- NL: 'Netherlands',
9503
- SE: 'Sweden',
9504
- CH: 'Switzerland',
9505
- KR: 'South Korea',
9506
- RU: 'Russia',
9507
- };
9525
+ // function extractContextInsights(emailContext) {
9526
+ // if (!emailContext) return [];
9508
9527
 
9509
- return countryNames[code] || code;
9510
- }
9528
+ // const insights = [];
9529
+ // const context = emailContext.toLowerCase();
9511
9530
 
9512
- function getPronounsFromGender(gender) {
9513
- switch (gender) {
9514
- case 'male':
9515
- return ['he/him', 'his'];
9516
- case 'female':
9517
- return ['she/her', 'hers'];
9518
- case 'non-binary':
9519
- return ['they/them', 'theirs'];
9520
- default:
9521
- return ['unknown'];
9522
- }
9523
- }
9531
+ // // Extract potential job role hints
9532
+ // const rolePatterns = {
9533
+ // manager: ['manage', 'supervise', 'team lead', 'department head'],
9534
+ // engineer: ['engineer', 'developer', 'programmer', 'software'],
9535
+ // sales: ['sales', 'account executive', 'business development'],
9536
+ // marketing: ['marketing', 'campaign', 'brand', 'social media'],
9537
+ // executive: ['ceo', 'cto', 'cfo', 'director', 'vp', 'vice president'],
9538
+ // };
9524
9539
 
9525
- function extractContextInsights(emailContext) {
9526
- if (!emailContext) return [];
9540
+ // for (const [role, patterns] of Object.entries(rolePatterns)) {
9541
+ // if (patterns.some((pattern) => context.includes(pattern))) {
9542
+ // insights.push(`Possible ${role} role indicated in email`);
9543
+ // }
9544
+ // }
9527
9545
 
9528
- const insights = [];
9529
- const context = emailContext.toLowerCase();
9546
+ // // Extract company hints
9547
+ // const companyMatch = context.match(/(?:at|from|of)\s+([A-Z][A-Za-z0-9\s&]+)(?:\s|$)/);
9548
+ // if (companyMatch && companyMatch[1].length > 2) {
9549
+ // insights.push(`Mentioned company/organization: ${companyMatch[1].trim()}`);
9550
+ // }
9530
9551
 
9531
- // Extract potential job role hints
9532
- const rolePatterns = {
9533
- manager: ['manage', 'supervise', 'team lead', 'department head'],
9534
- engineer: ['engineer', 'developer', 'programmer', 'software'],
9535
- sales: ['sales', 'account executive', 'business development'],
9536
- marketing: ['marketing', 'campaign', 'brand', 'social media'],
9537
- executive: ['ceo', 'cto', 'cfo', 'director', 'vp', 'vice president'],
9538
- };
9552
+ // // Extract project/technology hints
9553
+ // const techMatch = context.match(/(?:using|with|built\s+in)\s+([A-Za-z0-9\s+#]+)(?:\s|$)/);
9554
+ // if (techMatch) {
9555
+ // insights.push(`Technology mentioned: ${techMatch[1].trim()}`);
9556
+ // }
9539
9557
 
9540
- for (const [role, patterns] of Object.entries(rolePatterns)) {
9541
- if (patterns.some((pattern) => context.includes(pattern))) {
9542
- insights.push(`Possible ${role} role indicated in email`);
9543
- }
9544
- }
9558
+ // // Extract urgency/priority
9559
+ // if (context.includes('urgent') || context.includes('asap') || context.includes('immediately')) {
9560
+ // insights.push('Email suggests urgency or time sensitivity');
9561
+ // }
9545
9562
 
9546
- // Extract company hints
9547
- const companyMatch = context.match(/(?:at|from|of)\s+([A-Z][A-Za-z0-9\s&]+)(?:\s|$)/);
9548
- if (companyMatch && companyMatch[1].length > 2) {
9549
- insights.push(`Mentioned company/organization: ${companyMatch[1].trim()}`);
9550
- }
9563
+ // // Extract tone
9564
+ // if (context.includes('thank you') || context.includes('appreciate') || context.includes('grateful')) {
9565
+ // insights.push('Email shows appreciative/grateful tone');
9566
+ // }
9551
9567
 
9552
- // Extract project/technology hints
9553
- const techMatch = context.match(/(?:using|with|built\s+in)\s+([A-Za-z0-9\s+#]+)(?:\s|$)/);
9554
- if (techMatch) {
9555
- insights.push(`Technology mentioned: ${techMatch[1].trim()}`);
9556
- }
9568
+ // return insights.slice(0, 5); // Limit to 5 insights
9569
+ // }
9557
9570
 
9558
- // Extract urgency/priority
9559
- if (context.includes('urgent') || context.includes('asap') || context.includes('immediately')) {
9560
- insights.push('Email suggests urgency or time sensitivity');
9561
- }
9571
+ // // Existing helper functions (kept from previous version)
9572
+ // function validateURL(url) {
9573
+ // /* ... same as before ... */
9574
+ // }
9575
+ // function validatePhoneNumber(phone) {
9576
+ // /* ... same as before ... */
9577
+ // }
9578
+ // function cleanText(text) {
9579
+ // /* ... same as before ... */
9580
+ // }
9581
+ // function cleanStateProvince(state) {
9582
+ // /* ... same as before ... */
9583
+ // }
9584
+ // function ensureArray(value) {
9585
+ // /* ... same as before ... */
9586
+ // }
9587
+ // function getInitials(firstName, lastName) {
9588
+ // /* ... same as before ... */
9589
+ // }
9590
+ // function calculateSeniorityScore(data) {
9591
+ // /* ... same as before ... */
9592
+ // }
9562
9593
 
9563
- // Extract tone
9564
- if (context.includes('thank you') || context.includes('appreciate') || context.includes('grateful')) {
9565
- insights.push('Email shows appreciative/grateful tone');
9566
- }
9594
+ // function getDefaultPersonInfo(name, email, emailContext = '', error = null) {
9595
+ // const nameParts = name.trim().split(/\s+/);
9596
+ // const firstName = nameParts[0] || '';
9597
+ // const lastName = nameParts.slice(1).join(' ') || '';
9598
+ // const domain = email.includes('@') ? email.split('@')[1] : '';
9599
+ // const companyGuess = domain
9600
+ // .replace(/\..*$/, '')
9601
+ // .replace(/[^a-z]/gi, ' ')
9602
+ // .replace(/\b\w/g, (l) => l.toUpperCase());
9567
9603
 
9568
- return insights.slice(0, 5); // Limit to 5 insights
9569
- }
9604
+ // // Extract basic insights from email context
9605
+ // const contextInsights = emailContext ? extractContextInsights(emailContext) : [];
9570
9606
 
9571
- // Existing helper functions (kept from previous version)
9572
- function validateURL(url) {
9573
- /* ... same as before ... */
9574
- }
9575
- function validatePhoneNumber(phone) {
9576
- /* ... same as before ... */
9577
- }
9578
- function cleanText(text) {
9579
- /* ... same as before ... */
9580
- }
9581
- function cleanStateProvince(state) {
9582
- /* ... same as before ... */
9583
- }
9584
- function ensureArray(value) {
9585
- /* ... same as before ... */
9586
- }
9587
- function getInitials(firstName, lastName) {
9588
- /* ... same as before ... */
9589
- }
9590
- function calculateSeniorityScore(data) {
9591
- /* ... same as before ... */
9592
- }
9607
+ // return {
9608
+ // // Basic Information
9609
+ // person_full_name: name,
9610
+ // person_first_name: firstName,
9611
+ // person_last_name: lastName,
9612
+ // person_middle_name: '',
9613
+ // person_preferred_name: '',
9614
+
9615
+ // // Demographic Information
9616
+ // person_age: 'unknown',
9617
+ // person_gender: 'unknown',
9618
+ // person_nationality: 'unknown',
9619
+ // person_age_range: 'Unknown',
9620
+ // person_pronouns: ['unknown'],
9621
+
9622
+ // // Professional Information
9623
+ // person_title: 'Professional',
9624
+ // person_company: companyGuess || 'Unknown',
9625
+ // person_industry: 'Technology',
9626
+ // person_bio: 'Information not available',
9627
+
9628
+ // // Location
9629
+ // person_location_city: 'Not available',
9630
+ // person_location_state: 'Not available',
9631
+ // person_location_country: 'unknown',
9632
+ // person_location_full: 'Not available',
9633
+
9634
+ // // Contact Information
9635
+ // person_phone: 'Not available',
9636
+ // person_email: email,
9637
+ // person_email_alternate: '',
9638
+
9639
+ // // Education
9640
+ // person_education: [],
9641
+ // person_education_highest: 'Not available',
9642
+
9643
+ // // Professional Details
9644
+ // person_skills: [],
9645
+ // person_expertise: [],
9646
+ // person_career_level: 'unknown',
9647
+ // person_years_experience: 0,
9648
+
9649
+ // // Social & Online Presence
9650
+ // person_linkedin: 'Not available',
9651
+ // person_twitter: 'Not available',
9652
+ // person_github: 'Not available',
9653
+ // person_website: 'Not available',
9654
+ // person_social_other: [],
9655
+
9656
+ // // Additional Information
9657
+ // person_languages: ['English'],
9658
+ // person_interests: [],
9659
+ // person_achievements: [],
9660
+ // person_current_projects: [],
9661
+ // person_context_insights: contextInsights,
9593
9662
 
9594
- function getDefaultPersonInfo(name, email, emailContext = '', error = null) {
9595
- const nameParts = name.trim().split(/\s+/);
9596
- const firstName = nameParts[0] || '';
9597
- const lastName = nameParts.slice(1).join(' ') || '';
9598
- const domain = email.includes('@') ? email.split('@')[1] : '';
9599
- const companyGuess = domain
9600
- .replace(/\..*$/, '')
9601
- .replace(/[^a-z]/gi, ' ')
9602
- .replace(/\b\w/g, (l) => l.toUpperCase());
9663
+ // // Metadata
9664
+ // person_available_for_opportunities: false,
9665
+ // person_last_updated: new Date().toISOString(),
9666
+ // person_source_confidence: 0,
9667
+ // person_has_context_hints: contextInsights.length > 0,
9603
9668
 
9604
- // Extract basic insights from email context
9605
- const contextInsights = emailContext ? extractContextInsights(emailContext) : [];
9669
+ // // Processed fields
9670
+ // person_name_initials: getInitials(firstName, lastName),
9671
+ // person_seniority_score: 0,
9606
9672
 
9607
- return {
9608
- // Basic Information
9609
- person_full_name: name,
9610
- person_first_name: firstName,
9611
- person_last_name: lastName,
9612
- person_middle_name: '',
9613
- person_preferred_name: '',
9673
+ // // Structured objects
9674
+ // person_demographics: {
9675
+ // age: 'unknown',
9676
+ // age_range: 'Unknown',
9677
+ // gender: 'unknown',
9678
+ // pronouns: ['unknown'],
9679
+ // nationality: 'unknown',
9680
+ // nationality_name: 'Unknown',
9681
+ // },
9614
9682
 
9615
- // Demographic Information
9616
- person_age: 'unknown',
9617
- person_gender: 'unknown',
9618
- person_nationality: 'unknown',
9619
- person_age_range: 'Unknown',
9620
- person_pronouns: ['unknown'],
9683
+ // person_contact: {
9684
+ // email: email,
9685
+ // phone: 'Not available',
9686
+ // location: {
9687
+ // city: 'Not available',
9688
+ // state: 'Not available',
9689
+ // country: 'unknown',
9690
+ // country_name: 'Unknown',
9691
+ // full: 'Not available',
9692
+ // },
9693
+ // },
9621
9694
 
9622
- // Professional Information
9623
- person_title: 'Professional',
9624
- person_company: companyGuess || 'Unknown',
9625
- person_industry: 'Technology',
9626
- person_bio: 'Information not available',
9695
+ // person_professional: {
9696
+ // title: 'Professional',
9697
+ // company: companyGuess || 'Unknown',
9698
+ // industry: 'Technology',
9699
+ // career_level: 'unknown',
9700
+ // years_experience: 0,
9701
+ // skills: [],
9702
+ // expertise: [],
9703
+ // education: [],
9704
+ // },
9627
9705
 
9628
- // Location
9629
- person_location_city: 'Not available',
9630
- person_location_state: 'Not available',
9631
- person_location_country: 'unknown',
9632
- person_location_full: 'Not available',
9706
+ // person_online: {
9707
+ // linkedin: 'Not available',
9708
+ // twitter: 'Not available',
9709
+ // github: 'Not available',
9710
+ // website: 'Not available',
9711
+ // other: [],
9712
+ // },
9633
9713
 
9634
- // Contact Information
9635
- person_phone: 'Not available',
9636
- person_email: email,
9637
- person_email_alternate: '',
9714
+ // context_analysis: {
9715
+ // has_context: !!emailContext,
9716
+ // context_length: emailContext?.length || 0,
9717
+ // extracted_insights: contextInsights,
9718
+ // context_useful: contextInsights.length > 0,
9719
+ // },
9638
9720
 
9639
- // Education
9640
- person_education: [],
9641
- person_education_highest: 'Not available',
9721
+ // retrieved_at: new Date().toISOString(),
9722
+ // error: error || 'API call failed',
9723
+ // };
9724
+ // }
9642
9725
 
9643
- // Professional Details
9644
- person_skills: [],
9645
- person_expertise: [],
9646
- person_career_level: 'unknown',
9647
- person_years_experience: 0,
9726
+ // // Build context-aware prompt
9727
+ // let context_prompt = `Research the person: "${name}" (${email}).`;
9648
9728
 
9649
- // Social & Online Presence
9650
- person_linkedin: 'Not available',
9651
- person_twitter: 'Not available',
9652
- person_github: 'Not available',
9653
- person_website: 'Not available',
9654
- person_social_other: [],
9729
+ // if (email_context) {
9730
+ // context_prompt += `\n\nEMAIL CONTEXT PROVIDED:\n"${email_context.substring(0, 500)}${email_context.length > 500 ? '...' : ''}"`;
9731
+ // }
9655
9732
 
9656
- // Additional Information
9657
- person_languages: ['English'],
9658
- person_interests: [],
9659
- person_achievements: [],
9660
- person_current_projects: [],
9661
- person_context_insights: contextInsights,
9733
+ // context_prompt += `
9734
+
9735
+ // REQUIRED PERSON INFORMATION:
9736
+ // 1. Full name (first, middle, last)
9737
+ // 2. Professional title/role
9738
+ // 3. Company/organization they work for
9739
+ // 4. Industry/field they work in
9740
+ // 5. Location (city, state, country)
9741
+ // 6. Professional biography (1-2 sentences)
9742
+ // 7. Education background
9743
+ // 8. Professional skills/expertise
9744
+ // 9. Social media profiles (LinkedIn, Twitter, GitHub, etc.)
9745
+ // 10. Personal website or portfolio
9746
+
9747
+ // DEMOGRAPHIC INFORMATION:
9748
+ // 11. Estimated age (based on career, education, and public information)
9749
+ // 12. Gender (based on name, pronouns in content, or public profiles)
9750
+ // 13. Nationality/country of origin (if discernible) (based on name, pronouns in content, or public profiles)
9751
+
9752
+ // CONTACT INFORMATION:
9753
+ // 14. Phone number (if available)
9754
+ // 15. Alternate email addresses (if available)
9755
+ // 16. Physical address (if available and appropriate)
9756
+
9757
+ // ADDITIONAL INSIGHTS:
9758
+ // 17. Career level (entry, mid, senior, executive, etc.)
9759
+ // 18. Years of experience
9760
+ // 19. Notable achievements/awards
9761
+ // 20. Languages spoken
9762
+ // 21. Professional interests
9763
+ // 22. Current projects
9764
+ // 23. Availability for opportunities
9662
9765
 
9663
- // Metadata
9664
- person_available_for_opportunities: false,
9665
- person_last_updated: new Date().toISOString(),
9666
- person_source_confidence: 0,
9667
- person_has_context_hints: contextInsights.length > 0,
9766
+ // INSTRUCTIONS:
9767
+ // - Use web search to find accurate, public information
9768
+ // - Use email context provided to infer additional details
9769
+ // - Respect privacy - only include publicly available information
9770
+ // - For age: provide numeric estimate or range (e.g., 35, 30-40, unknown)
9771
+ // - For gender: use male/female/non-binary/unknown based on available cues
9772
+ // - For country: use 2-letter ISO code (e.g., US, IN, UK)
9773
+ // - If information is not available, use "Not available" or appropriate defaults
9774
+ // - For location, use standard formats (e.g., "San Francisco, CA, USA")
9775
+ // - For social media, provide full URLs when available
9776
+ // - Focus on professional information suitable for business networking
9777
+
9778
+ // Be thorough but respectful of privacy boundaries.`;
9779
+
9780
+ // const person_info_ret = await submit_chat_gpt_prompt({
9781
+ // uid,
9782
+ // prompt: context_prompt,
9783
+ // model: 'gpt-5-nano',
9784
+ // response_format: z.object({
9785
+ // // Basic Information
9786
+ // person_full_name: z.string().describe("Person's full name"),
9787
+ // person_first_name: z.string().describe('First name'),
9788
+ // person_last_name: z.string().describe('Last name'),
9789
+ // person_middle_name: z.string().optional().nullable().describe('Middle name if available'),
9790
+ // person_preferred_name: z.string().optional().nullable().describe('Preferred/nickname if different'),
9791
+
9792
+ // // Demographic Information
9793
+ // person_age: z.union([z.number().int().min(18).max(100), z.string().describe("Age range or 'unknown'")]).describe('find age base on the name Estimated age or range'),
9794
+ // person_gender: z.enum(['male', 'female']).describe('find Gender based on the name'),
9795
+ // person_nationality: z.string().describe('find nationality based on the name Country of origin/nationality (2-letter ISO code)'),
9796
+
9797
+ // // Professional Information
9798
+ // person_title: z.string().describe('Professional title/role'),
9799
+ // person_company: z.string().describe('Current company/organization'),
9800
+ // person_industry: z.string().describe('Primary industry/field'),
9801
+ // person_bio: z.string().describe('Professional biography (1-2 sentences)'),
9802
+
9803
+ // // Location
9804
+ // person_location_city: z.string().describe('City of residence/work'),
9805
+ // person_location_state: z.string().describe('State/province'),
9806
+ // person_location_country: z.string().describe('Country (2-letter ISO code)'),
9807
+ // person_location_full: z.string().describe('Full location string'),
9808
+
9809
+ // // Contact Information
9810
+ // person_phone: z.string().describe("Phone number or 'Not available'"),
9811
+ // person_email: z.string().describe('Primary email address'),
9812
+ // person_email_alternate: z.string().optional().nullable().describe('Alternate email if available'),
9813
+
9814
+ // // Education
9815
+ // person_education: z.array(z.string()).describe('Array of educational institutions/degrees'),
9816
+ // person_education_highest: z.string().describe('Highest degree obtained'),
9817
+
9818
+ // // Professional Details
9819
+ // person_skills: z.array(z.string()).describe('Array of professional skills'),
9820
+ // person_expertise: z.array(z.string()).describe('Areas of expertise'),
9821
+ // person_career_level: z.enum(['entry', 'mid', 'senior', 'lead', 'manager', 'director', 'executive', 'founder', 'unknown']),
9822
+ // person_years_experience: z.number().int().min(0).max(60).describe('Years of professional experience'),
9823
+
9824
+ // // Social & Online Presence
9825
+ // person_linkedin: z.string().describe("LinkedIn profile URL or 'Not available'"),
9826
+ // person_twitter: z.string().describe("Twitter/X profile URL or 'Not available'"),
9827
+ // person_github: z.string().describe("GitHub profile URL or 'Not available'"),
9828
+ // person_website: z.string().describe("Personal website/portfolio or 'Not available'"),
9829
+ // person_social_other: z.array(z.string()).optional().nullable().describe('Other social media profiles'),
9830
+
9831
+ // // Additional Information
9832
+ // person_languages: z.array(z.string()).describe('Languages spoken'),
9833
+ // person_interests: z.array(z.string()).describe('Professional/personal interests'),
9834
+ // person_achievements: z.array(z.string()).optional().nullable().describe('Notable achievements/awards'),
9835
+ // person_current_projects: z.array(z.string()).optional().nullable().describe('Current projects'),
9836
+
9837
+ // // Context Extracted Information
9838
+ // person_context_insights: z.array(z.string()).optional().nullable().describe('Insights extracted from provided email context'),
9668
9839
 
9669
- // Processed fields
9670
- person_name_initials: getInitials(firstName, lastName),
9671
- person_seniority_score: 0,
9840
+ // // Metadata
9841
+ // person_available_for_opportunities: z.boolean().describe('Open to new opportunities'),
9842
+ // person_last_updated: z.string().describe('Date information was last verified'),
9843
+ // person_source_confidence: z.number().min(0).max(100).describe('Confidence score for information accuracy'),
9844
+ // person_has_context_hints: z.boolean().describe('Whether email context provided useful hints'),
9845
+ // }),
9846
+ // metadata: {
9847
+ // func: 'get_person_info',
9848
+ // person_name: name,
9849
+ // person_email: email,
9850
+ // has_email_context: !!email_context,
9851
+ // email_context_length: email_context?.length || 0,
9852
+ // context_hash: email_context ? await hashString(email_context.substring(0, 200)) : null,
9853
+ // },
9854
+ // account_profile_info,
9855
+ // tools: [{ type: 'web_search_preview' }],
9856
+ // });
9672
9857
 
9673
- // Structured objects
9674
- person_demographics: {
9675
- age: 'unknown',
9676
- age_range: 'Unknown',
9677
- gender: 'unknown',
9678
- pronouns: ['unknown'],
9679
- nationality: 'unknown',
9680
- nationality_name: 'Unknown',
9681
- },
9858
+ // try {
9859
+ // if (person_info_ret.code > -1) {
9860
+ // const data = typeof person_info_ret.data === 'string' ? JSON.parse(person_info_ret.data) : person_info_ret.data;
9682
9861
 
9683
- person_contact: {
9684
- email: email,
9685
- phone: 'Not available',
9686
- location: {
9687
- city: 'Not available',
9688
- state: 'Not available',
9689
- country: 'unknown',
9690
- country_name: 'Unknown',
9691
- full: 'Not available',
9692
- },
9693
- },
9862
+ // // Process and enhance the data
9863
+ // const processedData = processPersonInfo(data, name, email, email_context);
9694
9864
 
9695
- person_professional: {
9696
- title: 'Professional',
9697
- company: companyGuess || 'Unknown',
9698
- industry: 'Technology',
9699
- career_level: 'unknown',
9700
- years_experience: 0,
9701
- skills: [],
9702
- expertise: [],
9703
- education: [],
9704
- },
9865
+ // return processedData;
9866
+ // } else {
9867
+ // console.error('Person info API call failed:', person_info_ret);
9868
+ // return getDefaultPersonInfo(name, email, email_context);
9869
+ // }
9870
+ // } catch (error) {
9871
+ // console.error('Error in get_person_info:', error);
9872
+ // return getDefaultPersonInfo(name, email, email_context, error.message);
9873
+ // }
9874
+ // };
9705
9875
 
9706
- person_online: {
9707
- linkedin: 'Not available',
9708
- twitter: 'Not available',
9709
- github: 'Not available',
9710
- website: 'Not available',
9711
- other: [],
9712
- },
9713
-
9714
- context_analysis: {
9715
- has_context: !!emailContext,
9716
- context_length: emailContext?.length || 0,
9717
- extracted_insights: contextInsights,
9718
- context_useful: contextInsights.length > 0,
9719
- },
9720
-
9721
- retrieved_at: new Date().toISOString(),
9722
- error: error || 'API call failed',
9723
- };
9724
- }
9725
-
9726
- // Build context-aware prompt
9727
- let context_prompt = `Research the person: "${name}" (${email}).`;
9728
-
9729
- if (email_context) {
9730
- context_prompt += `\n\nEMAIL CONTEXT PROVIDED:\n"${email_context.substring(0, 500)}${email_context.length > 500 ? '...' : ''}"`;
9731
- }
9732
-
9733
- context_prompt += `
9734
-
9735
- REQUIRED PERSON INFORMATION:
9736
- 1. Full name (first, middle, last)
9737
- 2. Professional title/role
9738
- 3. Company/organization they work for
9739
- 4. Industry/field they work in
9740
- 5. Location (city, state, country)
9741
- 6. Professional biography (1-2 sentences)
9742
- 7. Education background
9743
- 8. Professional skills/expertise
9744
- 9. Social media profiles (LinkedIn, Twitter, GitHub, etc.)
9745
- 10. Personal website or portfolio
9746
-
9747
- DEMOGRAPHIC INFORMATION:
9748
- 11. Estimated age (based on career, education, and public information)
9749
- 12. Gender (based on name, pronouns in content, or public profiles)
9750
- 13. Nationality/country of origin (if discernible) (based on name, pronouns in content, or public profiles)
9751
-
9752
- CONTACT INFORMATION:
9753
- 14. Phone number (if available)
9754
- 15. Alternate email addresses (if available)
9755
- 16. Physical address (if available and appropriate)
9756
-
9757
- ADDITIONAL INSIGHTS:
9758
- 17. Career level (entry, mid, senior, executive, etc.)
9759
- 18. Years of experience
9760
- 19. Notable achievements/awards
9761
- 20. Languages spoken
9762
- 21. Professional interests
9763
- 22. Current projects
9764
- 23. Availability for opportunities
9765
-
9766
- INSTRUCTIONS:
9767
- - Use web search to find accurate, public information
9768
- - Use email context provided to infer additional details
9769
- - Respect privacy - only include publicly available information
9770
- - For age: provide numeric estimate or range (e.g., 35, 30-40, unknown)
9771
- - For gender: use male/female/non-binary/unknown based on available cues
9772
- - For country: use 2-letter ISO code (e.g., US, IN, UK)
9773
- - If information is not available, use "Not available" or appropriate defaults
9774
- - For location, use standard formats (e.g., "San Francisco, CA, USA")
9775
- - For social media, provide full URLs when available
9776
- - Focus on professional information suitable for business networking
9777
-
9778
- Be thorough but respectful of privacy boundaries.`;
9779
-
9780
- const person_info_ret = await submit_chat_gpt_prompt({
9781
- uid,
9782
- prompt: context_prompt,
9783
- model: 'gpt-5-nano',
9784
- response_format: z.object({
9785
- // Basic Information
9786
- person_full_name: z.string().describe("Person's full name"),
9787
- person_first_name: z.string().describe('First name'),
9788
- person_last_name: z.string().describe('Last name'),
9789
- person_middle_name: z.string().optional().nullable().describe('Middle name if available'),
9790
- person_preferred_name: z.string().optional().nullable().describe('Preferred/nickname if different'),
9791
-
9792
- // Demographic Information
9793
- person_age: z.union([z.number().int().min(18).max(100), z.string().describe("Age range or 'unknown'")]).describe('find age base on the name Estimated age or range'),
9794
- person_gender: z.enum(['male', 'female']).describe('find Gender based on the name'),
9795
- person_nationality: z.string().describe('find nationality based on the name Country of origin/nationality (2-letter ISO code)'),
9796
-
9797
- // Professional Information
9798
- person_title: z.string().describe('Professional title/role'),
9799
- person_company: z.string().describe('Current company/organization'),
9800
- person_industry: z.string().describe('Primary industry/field'),
9801
- person_bio: z.string().describe('Professional biography (1-2 sentences)'),
9802
-
9803
- // Location
9804
- person_location_city: z.string().describe('City of residence/work'),
9805
- person_location_state: z.string().describe('State/province'),
9806
- person_location_country: z.string().describe('Country (2-letter ISO code)'),
9807
- person_location_full: z.string().describe('Full location string'),
9808
-
9809
- // Contact Information
9810
- person_phone: z.string().describe("Phone number or 'Not available'"),
9811
- person_email: z.string().describe('Primary email address'),
9812
- person_email_alternate: z.string().optional().nullable().describe('Alternate email if available'),
9813
-
9814
- // Education
9815
- person_education: z.array(z.string()).describe('Array of educational institutions/degrees'),
9816
- person_education_highest: z.string().describe('Highest degree obtained'),
9817
-
9818
- // Professional Details
9819
- person_skills: z.array(z.string()).describe('Array of professional skills'),
9820
- person_expertise: z.array(z.string()).describe('Areas of expertise'),
9821
- person_career_level: z.enum(['entry', 'mid', 'senior', 'lead', 'manager', 'director', 'executive', 'founder', 'unknown']),
9822
- person_years_experience: z.number().int().min(0).max(60).describe('Years of professional experience'),
9823
-
9824
- // Social & Online Presence
9825
- person_linkedin: z.string().describe("LinkedIn profile URL or 'Not available'"),
9826
- person_twitter: z.string().describe("Twitter/X profile URL or 'Not available'"),
9827
- person_github: z.string().describe("GitHub profile URL or 'Not available'"),
9828
- person_website: z.string().describe("Personal website/portfolio or 'Not available'"),
9829
- person_social_other: z.array(z.string()).optional().nullable().describe('Other social media profiles'),
9830
-
9831
- // Additional Information
9832
- person_languages: z.array(z.string()).describe('Languages spoken'),
9833
- person_interests: z.array(z.string()).describe('Professional/personal interests'),
9834
- person_achievements: z.array(z.string()).optional().nullable().describe('Notable achievements/awards'),
9835
- person_current_projects: z.array(z.string()).optional().nullable().describe('Current projects'),
9836
-
9837
- // Context Extracted Information
9838
- person_context_insights: z.array(z.string()).optional().nullable().describe('Insights extracted from provided email context'),
9839
-
9840
- // Metadata
9841
- person_available_for_opportunities: z.boolean().describe('Open to new opportunities'),
9842
- person_last_updated: z.string().describe('Date information was last verified'),
9843
- person_source_confidence: z.number().min(0).max(100).describe('Confidence score for information accuracy'),
9844
- person_has_context_hints: z.boolean().describe('Whether email context provided useful hints'),
9845
- }),
9846
- metadata: {
9847
- func: 'get_person_info',
9848
- person_name: name,
9849
- person_email: email,
9850
- has_email_context: !!email_context,
9851
- email_context_length: email_context?.length || 0,
9852
- context_hash: email_context ? await hashString(email_context.substring(0, 200)) : null,
9853
- },
9854
- account_profile_info,
9855
- tools: [{ type: 'web_search_preview' }],
9856
- });
9857
-
9858
- try {
9859
- if (person_info_ret.code > -1) {
9860
- const data = typeof person_info_ret.data === 'string' ? JSON.parse(person_info_ret.data) : person_info_ret.data;
9861
-
9862
- // Process and enhance the data
9863
- const processedData = processPersonInfo(data, name, email, email_context);
9864
-
9865
- return processedData;
9866
- } else {
9867
- console.error('Person info API call failed:', person_info_ret);
9868
- return getDefaultPersonInfo(name, email, email_context);
9869
- }
9870
- } catch (error) {
9871
- console.error('Error in get_person_info:', error);
9872
- return getDefaultPersonInfo(name, email, email_context, error.message);
9873
- }
9874
- };
9875
-
9876
- // export const analyze_email_account_type = async function (uid, name, email, account_profile_info, email_context = '') {
9877
- // // ========== HELPER FUNCTIONS ==========
9876
+ // export const analyze_email_account_type = async function (uid, name, email, account_profile_info, email_context = '') {
9877
+ // // ========== HELPER FUNCTIONS ==========
9878
9878
 
9879
9879
  // function enhanceEmailAnalysis(data, name, email, emailContext) {
9880
9880
  // const domain = email.split('@')[1] || '';
@@ -11911,3 +11911,2149 @@ export const get_business_info = async function (uid, name, email, account_profi
11911
11911
  return getDefaultBusinessInfo(name, email, error.message);
11912
11912
  }
11913
11913
  };
11914
+
11915
+ export const get_person_info = async function (uid, name, email, account_profile_info, email_context = '', options = {}) {
11916
+ // ========== HELPER FUNCTIONS ==========
11917
+
11918
+ function extractNameParts(fullName) {
11919
+ const nameParts = fullName.trim().split(/\s+/);
11920
+
11921
+ if (nameParts.length === 1) {
11922
+ return {
11923
+ firstName: nameParts[0],
11924
+ lastName: '',
11925
+ middleName: '',
11926
+ };
11927
+ }
11928
+
11929
+ if (nameParts.length === 2) {
11930
+ return {
11931
+ firstName: nameParts[0],
11932
+ lastName: nameParts[1],
11933
+ middleName: '',
11934
+ };
11935
+ }
11936
+
11937
+ // For names with 3+ parts, assume first is first name, last is last name, middle is middle name
11938
+ return {
11939
+ firstName: nameParts[0],
11940
+ lastName: nameParts[nameParts.length - 1],
11941
+ middleName: nameParts.slice(1, nameParts.length - 1).join(' '),
11942
+ };
11943
+ }
11944
+
11945
+ function inferGenderFromName(firstName) {
11946
+ if (!firstName) return 'unknown';
11947
+
11948
+ const firstNameLower = firstName.toLowerCase();
11949
+
11950
+ // Common male first names
11951
+ const maleNames = [
11952
+ 'john',
11953
+ 'james',
11954
+ 'robert',
11955
+ 'michael',
11956
+ 'william',
11957
+ 'david',
11958
+ 'richard',
11959
+ 'charles',
11960
+ 'joseph',
11961
+ 'thomas',
11962
+ 'chris',
11963
+ 'daniel',
11964
+ 'paul',
11965
+ 'mark',
11966
+ 'donald',
11967
+ 'george',
11968
+ 'kenneth',
11969
+ 'steven',
11970
+ 'edward',
11971
+ 'brian',
11972
+ 'ronald',
11973
+ 'anthony',
11974
+ 'kevin',
11975
+ 'jason',
11976
+ 'matthew',
11977
+ 'gary',
11978
+ 'timothy',
11979
+ 'jose',
11980
+ 'larry',
11981
+ 'jeffrey',
11982
+ 'frank',
11983
+ 'scott',
11984
+ 'eric',
11985
+ 'stephen',
11986
+ 'andrew',
11987
+ 'raymond',
11988
+ 'gregory',
11989
+ 'joshua',
11990
+ 'jerry',
11991
+ 'dennis',
11992
+ 'walter',
11993
+ 'patrick',
11994
+ 'peter',
11995
+ 'harold',
11996
+ 'douglas',
11997
+ 'henry',
11998
+ 'carl',
11999
+ 'arthur',
12000
+ 'ryan',
12001
+ 'roger',
12002
+ 'joe',
12003
+ 'juan',
12004
+ 'jack',
12005
+ 'albert',
12006
+ 'jonathan',
12007
+ 'justin',
12008
+ 'terry',
12009
+ 'gerald',
12010
+ 'keith',
12011
+ 'samuel',
12012
+ 'willie',
12013
+ 'ralph',
12014
+ 'lawrence',
12015
+ 'nicholas',
12016
+ 'roy',
12017
+ 'benjamin',
12018
+ 'bruce',
12019
+ 'brandon',
12020
+ 'adam',
12021
+ 'harry',
12022
+ 'fred',
12023
+ 'wayne',
12024
+ 'billy',
12025
+ 'steve',
12026
+ 'louis',
12027
+ 'jeremy',
12028
+ 'aaron',
12029
+ 'randy',
12030
+ 'howard',
12031
+ 'eugene',
12032
+ 'carlos',
12033
+ 'russell',
12034
+ 'bobby',
12035
+ 'victor',
12036
+ 'martin',
12037
+ 'ernest',
12038
+ 'phillip',
12039
+ 'todd',
12040
+ 'jesse',
12041
+ 'craig',
12042
+ 'alan',
12043
+ 'shawn',
12044
+ 'clarence',
12045
+ 'sean',
12046
+ 'philip',
12047
+ 'chris',
12048
+ 'johnny',
12049
+ 'earl',
12050
+ 'jimmy',
12051
+ 'antonio',
12052
+ 'danny',
12053
+ 'bryan',
12054
+ 'tony',
12055
+ 'luis',
12056
+ 'mike',
12057
+ 'stanley',
12058
+ 'leonard',
12059
+ 'nathan',
12060
+ 'dale',
12061
+ 'manuel',
12062
+ 'rodney',
12063
+ 'curtis',
12064
+ 'norman',
12065
+ 'allen',
12066
+ 'marvin',
12067
+ 'vincent',
12068
+ 'glenn',
12069
+ 'jeffery',
12070
+ 'travis',
12071
+ 'jeff',
12072
+ 'chad',
12073
+ 'jacob',
12074
+ 'lee',
12075
+ 'melvin',
12076
+ 'alfred',
12077
+ 'kyle',
12078
+ 'francis',
12079
+ 'bradley',
12080
+ 'jesus',
12081
+ 'herbert',
12082
+ 'frederick',
12083
+ 'ray',
12084
+ 'joel',
12085
+ 'edwin',
12086
+ 'don',
12087
+ 'eddie',
12088
+ 'ricky',
12089
+ 'troy',
12090
+ 'randall',
12091
+ 'barry',
12092
+ 'alexander',
12093
+ 'bernard',
12094
+ 'mario',
12095
+ 'leroy',
12096
+ 'francisco',
12097
+ 'marcus',
12098
+ 'micheal',
12099
+ 'theodore',
12100
+ 'clifford',
12101
+ 'miguel',
12102
+ 'oscar',
12103
+ 'jay',
12104
+ 'jim',
12105
+ 'tom',
12106
+ 'calvin',
12107
+ 'alex',
12108
+ 'jon',
12109
+ 'ronnie',
12110
+ 'bill',
12111
+ 'lloyd',
12112
+ 'tommy',
12113
+ 'leon',
12114
+ 'derek',
12115
+ 'warren',
12116
+ 'darrell',
12117
+ 'jerome',
12118
+ 'floyd',
12119
+ 'leo',
12120
+ 'alvin',
12121
+ 'tim',
12122
+ 'wesley',
12123
+ 'gordon',
12124
+ 'dean',
12125
+ 'greg',
12126
+ 'jorge',
12127
+ 'dustin',
12128
+ 'pedro',
12129
+ 'derrick',
12130
+ 'dan',
12131
+ 'lewis',
12132
+ 'zachary',
12133
+ 'corey',
12134
+ 'herman',
12135
+ 'maurice',
12136
+ 'vernon',
12137
+ 'roberto',
12138
+ 'clyde',
12139
+ 'glen',
12140
+ 'hector',
12141
+ 'shane',
12142
+ 'ricardo',
12143
+ 'sam',
12144
+ 'rick',
12145
+ 'lester',
12146
+ 'brent',
12147
+ 'ramon',
12148
+ 'charlie',
12149
+ 'tyler',
12150
+ 'gilbert',
12151
+ 'gene',
12152
+ 'marc',
12153
+ 'reginald',
12154
+ 'ruben',
12155
+ 'brett',
12156
+ 'angel',
12157
+ 'nathaniel',
12158
+ 'rafael',
12159
+ 'leslie',
12160
+ 'edgar',
12161
+ 'milton',
12162
+ 'raul',
12163
+ 'ben',
12164
+ 'chester',
12165
+ 'cecil',
12166
+ 'duane',
12167
+ 'franklin',
12168
+ 'andre',
12169
+ 'elmer',
12170
+ 'brad',
12171
+ 'gabriel',
12172
+ 'ron',
12173
+ 'mitchell',
12174
+ 'roland',
12175
+ 'arnold',
12176
+ 'harvey',
12177
+ 'jared',
12178
+ 'adrian',
12179
+ 'karl',
12180
+ 'cory',
12181
+ 'claude',
12182
+ 'erik',
12183
+ 'darryl',
12184
+ 'jamie',
12185
+ 'neil',
12186
+ 'jessie',
12187
+ 'christian',
12188
+ 'javier',
12189
+ 'fernando',
12190
+ 'clinton',
12191
+ 'ted',
12192
+ 'mathew',
12193
+ 'tyrone',
12194
+ 'darren',
12195
+ 'lonnie',
12196
+ 'lance',
12197
+ 'cody',
12198
+ 'julio',
12199
+ 'kelly',
12200
+ 'kurt',
12201
+ 'allan',
12202
+ 'nelson',
12203
+ 'guy',
12204
+ 'clayton',
12205
+ 'hugh',
12206
+ 'max',
12207
+ 'dwayne',
12208
+ 'dwight',
12209
+ 'armando',
12210
+ 'felix',
12211
+ 'jimmie',
12212
+ 'everett',
12213
+ 'jordan',
12214
+ 'ian',
12215
+ 'wallace',
12216
+ 'ken',
12217
+ 'bob',
12218
+ 'jaime',
12219
+ 'casey',
12220
+ 'alfredo',
12221
+ 'alberto',
12222
+ 'dave',
12223
+ 'ivan',
12224
+ 'johnnie',
12225
+ 'sidney',
12226
+ 'byron',
12227
+ 'julian',
12228
+ 'isaac',
12229
+ 'morris',
12230
+ 'clifton',
12231
+ 'willard',
12232
+ 'daryl',
12233
+ 'ross',
12234
+ 'virgil',
12235
+ 'andy',
12236
+ 'marshall',
12237
+ 'salvador',
12238
+ 'perry',
12239
+ 'kirk',
12240
+ 'sergio',
12241
+ 'marion',
12242
+ 'tracy',
12243
+ 'seth',
12244
+ 'kent',
12245
+ 'terrance',
12246
+ 'rene',
12247
+ 'eduardo',
12248
+ 'terrence',
12249
+ 'enrique',
12250
+ 'freddie',
12251
+ 'wade',
12252
+ 'austin',
12253
+ 'stuart',
12254
+ 'fredrick',
12255
+ 'arturo',
12256
+ 'alejandro',
12257
+ 'jackie',
12258
+ 'joey',
12259
+ 'nick',
12260
+ 'luther',
12261
+ 'wendell',
12262
+ 'jeremiah',
12263
+ 'evan',
12264
+ 'julius',
12265
+ 'dana',
12266
+ 'donnie',
12267
+ 'otis',
12268
+ 'shannon',
12269
+ 'trevor',
12270
+ 'oliver',
12271
+ 'luke',
12272
+ 'homer',
12273
+ 'gerard',
12274
+ 'doug',
12275
+ 'kenny',
12276
+ 'hubert',
12277
+ 'angelo',
12278
+ 'shaun',
12279
+ 'lyle',
12280
+ 'matt',
12281
+ 'lynn',
12282
+ 'alfonso',
12283
+ 'orlando',
12284
+ 'rex',
12285
+ 'carlton',
12286
+ 'ernesto',
12287
+ 'cameron',
12288
+ 'neal',
12289
+ 'pablo',
12290
+ 'lorenzo',
12291
+ 'omar',
12292
+ 'wilbur',
12293
+ 'blake',
12294
+ 'grant',
12295
+ 'horace',
12296
+ 'roderick',
12297
+ 'kerry',
12298
+ 'abraham',
12299
+ 'willis',
12300
+ 'rickey',
12301
+ 'jean',
12302
+ 'ira',
12303
+ 'andres',
12304
+ 'cesar',
12305
+ 'johnathan',
12306
+ 'malcolm',
12307
+ 'rudolph',
12308
+ 'damon',
12309
+ 'kelvin',
12310
+ 'rudy',
12311
+ 'preston',
12312
+ 'alton',
12313
+ 'archie',
12314
+ 'marco',
12315
+ 'wm',
12316
+ 'pete',
12317
+ 'randolph',
12318
+ 'garry',
12319
+ 'geoffrey',
12320
+ 'jonathon',
12321
+ 'felipe',
12322
+ 'bennie',
12323
+ 'gerardo',
12324
+ 'ed',
12325
+ 'dominique',
12326
+ 'robin',
12327
+ 'loren',
12328
+ 'delbert',
12329
+ 'colin',
12330
+ 'guillermo',
12331
+ 'earnest',
12332
+ 'lucas',
12333
+ 'benny',
12334
+ 'noel',
12335
+ 'spencer',
12336
+ 'rodolfo',
12337
+ 'myron',
12338
+ 'edmund',
12339
+ 'garrett',
12340
+ 'salvatore',
12341
+ 'cedric',
12342
+ 'lowell',
12343
+ 'gregg',
12344
+ 'sherman',
12345
+ 'wilson',
12346
+ 'devin',
12347
+ 'kristopher',
12348
+ 'sylvester',
12349
+ 'roosevelt',
12350
+ 'israel',
12351
+ 'jermaine',
12352
+ 'forrest',
12353
+ 'wilbert',
12354
+ 'leland',
12355
+ 'simon',
12356
+ 'guadalupe',
12357
+ 'clark',
12358
+ 'irving',
12359
+ 'carroll',
12360
+ 'bryant',
12361
+ 'owen',
12362
+ 'rufus',
12363
+ 'woodrow',
12364
+ 'sammy',
12365
+ 'kristopher',
12366
+ 'mack',
12367
+ 'levi',
12368
+ 'marcos',
12369
+ 'gustavo',
12370
+ 'jake',
12371
+ 'lionel',
12372
+ 'marty',
12373
+ 'taylor',
12374
+ 'ellis',
12375
+ 'dallas',
12376
+ 'gilberto',
12377
+ 'clint',
12378
+ 'nicolas',
12379
+ 'laurence',
12380
+ 'ismael',
12381
+ 'orville',
12382
+ 'drew',
12383
+ 'jody',
12384
+ 'ervin',
12385
+ 'dewey',
12386
+ 'al',
12387
+ 'wilfred',
12388
+ 'josh',
12389
+ 'hugo',
12390
+ 'ignacio',
12391
+ 'caleb',
12392
+ 'tomas',
12393
+ 'sheldon',
12394
+ 'erick',
12395
+ 'frankie',
12396
+ 'stewart',
12397
+ 'doyle',
12398
+ 'darrel',
12399
+ 'rogelio',
12400
+ 'terence',
12401
+ 'santiago',
12402
+ 'alonzo',
12403
+ 'elias',
12404
+ 'bert',
12405
+ 'elbert',
12406
+ 'ramiro',
12407
+ 'conrad',
12408
+ 'pat',
12409
+ 'noah',
12410
+ 'grady',
12411
+ 'phil',
12412
+ 'cornelius',
12413
+ 'lamar',
12414
+ 'rolando',
12415
+ 'clay',
12416
+ 'percy',
12417
+ 'dexter',
12418
+ 'bradford',
12419
+ 'merle',
12420
+ 'darin',
12421
+ 'amos',
12422
+ 'terrell',
12423
+ 'moses',
12424
+ 'irvin',
12425
+ 'saul',
12426
+ 'roman',
12427
+ 'darnell',
12428
+ 'randal',
12429
+ 'tommie',
12430
+ 'timmy',
12431
+ 'darrin',
12432
+ 'winston',
12433
+ 'brendan',
12434
+ 'toby',
12435
+ 'van',
12436
+ 'abel',
12437
+ 'dominick',
12438
+ 'boyd',
12439
+ 'courtney',
12440
+ 'jan',
12441
+ 'emilio',
12442
+ 'elijah',
12443
+ 'cary',
12444
+ 'domenic',
12445
+ 'emmanuel',
12446
+ 'stephon',
12447
+ 'lon',
12448
+ 'brice',
12449
+ 'judson',
12450
+ 'antoine',
12451
+ 'jerrod',
12452
+ 'foster',
12453
+ 'reynaldo',
12454
+ 'reinaldo',
12455
+ 'cristobal',
12456
+ 'demetrius',
12457
+ 'carlo',
12458
+ 'nolan',
12459
+ 'rod',
12460
+ 'quinton',
12461
+ 'hal',
12462
+ 'brain',
12463
+ 'rob',
12464
+ 'elwood',
12465
+ 'kendrick',
12466
+ 'darius',
12467
+ 'moises',
12468
+ 'son',
12469
+ 'marlin',
12470
+ 'fidel',
12471
+ 'thaddeus',
12472
+ 'cliff',
12473
+ 'marcel',
12474
+ 'ali',
12475
+ 'jackson',
12476
+ 'raphael',
12477
+ 'bryon',
12478
+ 'armand',
12479
+ 'alvaro',
12480
+ 'jeffry',
12481
+ 'dane',
12482
+ 'joesph',
12483
+ 'thurman',
12484
+ 'ned',
12485
+ 'sammie',
12486
+ 'rusty',
12487
+ 'michel',
12488
+ 'monty',
12489
+ 'rory',
12490
+ 'fabian',
12491
+ 'reggie',
12492
+ 'mason',
12493
+ 'graham',
12494
+ 'kris',
12495
+ 'isaiah',
12496
+ 'vaughn',
12497
+ 'gus',
12498
+ 'avery',
12499
+ 'loyd',
12500
+ 'diego',
12501
+ 'alexis',
12502
+ 'adolph',
12503
+ 'norris',
12504
+ 'millard',
12505
+ 'rocco',
12506
+ 'gonzalo',
12507
+ 'derick',
12508
+ 'rodrigo',
12509
+ 'gerry',
12510
+ 'stacey',
12511
+ 'trent',
12512
+ 'carmen',
12513
+ 'wiley',
12514
+ 'rigoberto',
12515
+ 'alphonso',
12516
+ 'ty',
12517
+ 'shelby',
12518
+ 'rickie',
12519
+ 'noe',
12520
+ 'vern',
12521
+ 'bobbie',
12522
+ 'reed',
12523
+ 'jefferson',
12524
+ 'elvis',
12525
+ 'bernardo',
12526
+ 'mauricio',
12527
+ 'hiram',
12528
+ 'donovan',
12529
+ 'basil',
12530
+ 'riley',
12531
+ 'ollie',
12532
+ 'nickolas',
12533
+ 'maynard',
12534
+ 'scotty',
12535
+ 'carson',
12536
+ 'rae',
12537
+ 'abdul',
12538
+ 'langston',
12539
+ 'dex',
12540
+ 'hassan',
12541
+ 'jamal',
12542
+ 'dwain',
12543
+ 'grover',
12544
+ 'porter',
12545
+ 'ervin',
12546
+ 'lenny',
12547
+ 'trenton',
12548
+ 'denny',
12549
+ 'fabian',
12550
+ 'michal',
12551
+ 'mikel',
12552
+ 'dorian',
12553
+ 'britt',
12554
+ 'bennet',
12555
+ 'sanford',
12556
+ 'chaim',
12557
+ 'otis',
12558
+ 'reuben',
12559
+ 'stewart',
12560
+ 'maxwell',
12561
+ 'dirk',
12562
+ 'nehemiah',
12563
+ 'levi',
12564
+ 'romeo',
12565
+ 'jarod',
12566
+ 'vance',
12567
+ 'damion',
12568
+ 'mohammad',
12569
+ 'zachery',
12570
+ 'tad',
12571
+ 'shon',
12572
+ 'michale',
12573
+ 'blair',
12574
+ 'tory',
12575
+ 'shad',
12576
+ 'cole',
12577
+ 'duncan',
12578
+ 'bertram',
12579
+ 'brant',
12580
+ 'wilfredo',
12581
+ 'hershel',
12582
+ 'eliseo',
12583
+ 'donny',
12584
+ 'dillon',
12585
+ 'robt',
12586
+ 'wilford',
12587
+ 'barton',
12588
+ 'stacy',
12589
+ 'elliott',
12590
+ 'darron',
12591
+ 'cortney',
12592
+ 'chance',
12593
+ 'denver',
12594
+ 'elvin',
12595
+ 'norbert',
12596
+ 'giovanni',
12597
+ 'stevie',
12598
+ 'barney',
12599
+ 'nestor',
12600
+ 'hollis',
12601
+ 'stefan',
12602
+ 'donn',
12603
+ 'linwood',
12604
+ 'lauren',
12605
+ 'tyree',
12606
+ 'jeramy',
12607
+ 'jed',
12608
+ 'efren',
12609
+ 'antony',
12610
+ 'ahmad',
12611
+ 'sung',
12612
+ 'harrison',
12613
+ 'antione',
12614
+ 'waldo',
12615
+ 'fredric',
12616
+ 'bradly',
12617
+ 'quincy',
12618
+ 'matt',
12619
+ 'hunter',
12620
+ ];
12621
+
12622
+ // Common female first names
12623
+ const femaleNames = [
12624
+ 'mary',
12625
+ 'patricia',
12626
+ 'linda',
12627
+ 'barbara',
12628
+ 'elizabeth',
12629
+ 'jennifer',
12630
+ 'maria',
12631
+ 'susan',
12632
+ 'margaret',
12633
+ 'dorothy',
12634
+ 'lisa',
12635
+ 'nancy',
12636
+ 'karen',
12637
+ 'betty',
12638
+ 'helen',
12639
+ 'sandra',
12640
+ 'donna',
12641
+ 'carol',
12642
+ 'ruth',
12643
+ 'sharon',
12644
+ 'michelle',
12645
+ 'laura',
12646
+ 'sarah',
12647
+ 'kimberly',
12648
+ 'deborah',
12649
+ 'jessica',
12650
+ 'shirley',
12651
+ 'cynthia',
12652
+ 'angela',
12653
+ 'melissa',
12654
+ 'brenda',
12655
+ 'amy',
12656
+ 'anna',
12657
+ 'rebecca',
12658
+ 'virginia',
12659
+ 'kathleen',
12660
+ 'pamela',
12661
+ 'martha',
12662
+ 'debra',
12663
+ 'amanda',
12664
+ 'stephanie',
12665
+ 'carolyn',
12666
+ 'christine',
12667
+ 'marie',
12668
+ 'janet',
12669
+ 'catherine',
12670
+ 'frances',
12671
+ 'ann',
12672
+ 'joyce',
12673
+ 'diane',
12674
+ 'alice',
12675
+ 'julie',
12676
+ 'heather',
12677
+ 'teresa',
12678
+ 'doris',
12679
+ 'gloria',
12680
+ 'evelyn',
12681
+ 'jean',
12682
+ 'cheryl',
12683
+ 'mildred',
12684
+ 'katherine',
12685
+ 'joan',
12686
+ 'ashley',
12687
+ 'judith',
12688
+ 'rose',
12689
+ 'janice',
12690
+ 'kelly',
12691
+ 'nicole',
12692
+ 'judy',
12693
+ 'christina',
12694
+ 'kathy',
12695
+ 'theresa',
12696
+ 'beverly',
12697
+ 'denise',
12698
+ 'tammy',
12699
+ 'irene',
12700
+ 'jane',
12701
+ 'lori',
12702
+ 'rachel',
12703
+ 'marilyn',
12704
+ 'andrea',
12705
+ 'kathryn',
12706
+ 'louise',
12707
+ 'sara',
12708
+ 'anne',
12709
+ 'jacqueline',
12710
+ 'wanda',
12711
+ 'bonnie',
12712
+ 'julia',
12713
+ 'ruby',
12714
+ 'lois',
12715
+ 'tina',
12716
+ 'phyllis',
12717
+ 'norma',
12718
+ 'paula',
12719
+ 'diana',
12720
+ 'annie',
12721
+ 'lillian',
12722
+ 'emily',
12723
+ 'robin',
12724
+ 'peggy',
12725
+ 'crystal',
12726
+ 'gladys',
12727
+ 'rita',
12728
+ 'dawn',
12729
+ 'connie',
12730
+ 'florence',
12731
+ 'tracy',
12732
+ 'edna',
12733
+ 'tiffany',
12734
+ 'carmen',
12735
+ 'rosa',
12736
+ 'cindy',
12737
+ 'grace',
12738
+ 'wendy',
12739
+ 'victoria',
12740
+ 'edith',
12741
+ 'kim',
12742
+ 'sherry',
12743
+ 'sylvia',
12744
+ 'josephine',
12745
+ 'thelma',
12746
+ 'shannon',
12747
+ 'sheila',
12748
+ 'ethel',
12749
+ 'ellen',
12750
+ 'elaine',
12751
+ 'marjorie',
12752
+ 'carrie',
12753
+ 'charlotte',
12754
+ 'monica',
12755
+ 'esther',
12756
+ 'pauline',
12757
+ 'emma',
12758
+ 'juanita',
12759
+ 'anita',
12760
+ 'rhonda',
12761
+ 'hazel',
12762
+ 'amber',
12763
+ 'eva',
12764
+ 'debbie',
12765
+ 'april',
12766
+ 'leslie',
12767
+ 'clara',
12768
+ 'lucille',
12769
+ 'jamie',
12770
+ 'joanne',
12771
+ 'eleanor',
12772
+ 'valerie',
12773
+ 'danielle',
12774
+ 'megan',
12775
+ 'alicia',
12776
+ 'suzanne',
12777
+ 'michele',
12778
+ 'gail',
12779
+ 'bertha',
12780
+ 'darlene',
12781
+ 'veronica',
12782
+ 'jill',
12783
+ 'erin',
12784
+ 'geraldine',
12785
+ 'lauren',
12786
+ 'cathy',
12787
+ 'joann',
12788
+ 'lorraine',
12789
+ 'lynn',
12790
+ 'sally',
12791
+ 'regina',
12792
+ 'erica',
12793
+ 'beatrice',
12794
+ 'dolores',
12795
+ 'bernice',
12796
+ 'audrey',
12797
+ 'yvonne',
12798
+ 'annette',
12799
+ 'june',
12800
+ 'samantha',
12801
+ 'marion',
12802
+ 'dana',
12803
+ 'stacy',
12804
+ 'ana',
12805
+ 'renee',
12806
+ 'ida',
12807
+ 'vivian',
12808
+ 'roberta',
12809
+ 'holly',
12810
+ 'brittany',
12811
+ 'melanie',
12812
+ 'loretta',
12813
+ 'yolanda',
12814
+ 'jeanette',
12815
+ 'laurie',
12816
+ 'katie',
12817
+ 'kristen',
12818
+ 'vanessa',
12819
+ 'alma',
12820
+ 'sue',
12821
+ 'elsie',
12822
+ 'beth',
12823
+ 'jeanne',
12824
+ 'vicki',
12825
+ 'carla',
12826
+ 'tara',
12827
+ 'rosemary',
12828
+ 'eileen',
12829
+ 'terri',
12830
+ 'gertrude',
12831
+ 'lucy',
12832
+ 'tonya',
12833
+ 'ella',
12834
+ 'stacey',
12835
+ 'wilma',
12836
+ 'gina',
12837
+ 'kristin',
12838
+ 'jessie',
12839
+ 'natalie',
12840
+ 'agnes',
12841
+ 'vera',
12842
+ 'willie',
12843
+ 'charlene',
12844
+ 'bessie',
12845
+ 'delores',
12846
+ 'melinda',
12847
+ 'pearl',
12848
+ 'arlene',
12849
+ 'maureen',
12850
+ 'colleen',
12851
+ 'allison',
12852
+ 'tamara',
12853
+ 'joy',
12854
+ 'georgia',
12855
+ 'constance',
12856
+ 'lillie',
12857
+ 'claudia',
12858
+ 'jackie',
12859
+ 'marcia',
12860
+ 'tanya',
12861
+ 'nellie',
12862
+ 'minnie',
12863
+ 'marlene',
12864
+ 'heidi',
12865
+ 'glenda',
12866
+ 'lydia',
12867
+ 'viola',
12868
+ 'courtney',
12869
+ 'marian',
12870
+ 'stella',
12871
+ 'caroline',
12872
+ 'dora',
12873
+ 'jo',
12874
+ 'vickie',
12875
+ 'mattie',
12876
+ 'terry',
12877
+ 'maxine',
12878
+ 'irma',
12879
+ 'mabel',
12880
+ 'marsha',
12881
+ 'myrtle',
12882
+ 'lena',
12883
+ 'christy',
12884
+ 'deanna',
12885
+ 'patsy',
12886
+ 'hilda',
12887
+ 'gwendolyn',
12888
+ 'jenny',
12889
+ 'nora',
12890
+ 'margie',
12891
+ 'nina',
12892
+ 'cassandra',
12893
+ 'leah',
12894
+ 'penny',
12895
+ 'kay',
12896
+ 'priscilla',
12897
+ 'naomi',
12898
+ 'carole',
12899
+ 'brandy',
12900
+ 'olga',
12901
+ 'billie',
12902
+ 'dianne',
12903
+ 'tracey',
12904
+ 'leona',
12905
+ 'jenny',
12906
+ 'felicia',
12907
+ 'sonia',
12908
+ 'miriam',
12909
+ 'velma',
12910
+ 'becky',
12911
+ 'bobbie',
12912
+ 'violet',
12913
+ 'kristina',
12914
+ 'toni',
12915
+ 'misty',
12916
+ 'mae',
12917
+ 'shelly',
12918
+ 'daisy',
12919
+ 'ramona',
12920
+ 'sherri',
12921
+ 'erika',
12922
+ 'katrina',
12923
+ 'claire',
12924
+ 'lindsey',
12925
+ 'lindsay',
12926
+ 'geneva',
12927
+ 'guadalupe',
12928
+ 'belinda',
12929
+ 'margarita',
12930
+ 'sheryl',
12931
+ 'cora',
12932
+ 'faye',
12933
+ 'ada',
12934
+ 'natasha',
12935
+ 'sabrina',
12936
+ 'isabel',
12937
+ 'marguerite',
12938
+ 'hattie',
12939
+ 'harriet',
12940
+ 'molly',
12941
+ 'cecilia',
12942
+ 'kristi',
12943
+ 'brandi',
12944
+ 'blanche',
12945
+ 'sandy',
12946
+ 'rosie',
12947
+ 'joanna',
12948
+ 'iris',
12949
+ 'eunice',
12950
+ 'angie',
12951
+ 'inez',
12952
+ 'lynda',
12953
+ 'madeline',
12954
+ 'amelia',
12955
+ 'alberta',
12956
+ 'genevieve',
12957
+ 'monique',
12958
+ 'jodi',
12959
+ 'janie',
12960
+ 'maggie',
12961
+ 'kayla',
12962
+ 'sonya',
12963
+ 'jan',
12964
+ 'lee',
12965
+ 'kristine',
12966
+ 'candace',
12967
+ 'fannie',
12968
+ 'maryann',
12969
+ 'opal',
12970
+ 'alison',
12971
+ 'yvette',
12972
+ 'melody',
12973
+ 'luz',
12974
+ 'susie',
12975
+ 'olivia',
12976
+ 'flora',
12977
+ 'shelley',
12978
+ 'kristy',
12979
+ 'mamie',
12980
+ 'lula',
12981
+ 'lola',
12982
+ 'verna',
12983
+ 'beulah',
12984
+ 'antoinette',
12985
+ 'candice',
12986
+ 'juana',
12987
+ 'jeannette',
12988
+ 'pam',
12989
+ 'kelli',
12990
+ 'hannah',
12991
+ 'whitney',
12992
+ 'bridget',
12993
+ 'karla',
12994
+ 'celia',
12995
+ 'latoya',
12996
+ 'patty',
12997
+ 'shelia',
12998
+ 'gayle',
12999
+ 'della',
13000
+ 'vicky',
13001
+ 'lynne',
13002
+ 'sheri',
13003
+ 'marianne',
13004
+ 'kara',
13005
+ 'jacquelyn',
13006
+ 'erma',
13007
+ 'blanca',
13008
+ 'myra',
13009
+ 'leticia',
13010
+ 'patrice',
13011
+ 'rona',
13012
+ 'kendall',
13013
+ 'wendi',
13014
+ 'sharron',
13015
+ 'jocelyn',
13016
+ 'angelica',
13017
+ 'leah',
13018
+ 'chrystal',
13019
+ 'tia',
13020
+ 'tricia',
13021
+ 'francine',
13022
+ 'leigh',
13023
+ 'betsy',
13024
+ 'elisa',
13025
+ 'rachelle',
13026
+ 'edwina',
13027
+ 'tabitha',
13028
+ 'janelle',
13029
+ 'lora',
13030
+ 'ciara',
13031
+ 'marla',
13032
+ 'tania',
13033
+ 'marcy',
13034
+ 'karin',
13035
+ 'bridgette',
13036
+ 'alexandria',
13037
+ 'daniela',
13038
+ 'kelsey',
13039
+ 'tameka',
13040
+ 'cathleen',
13041
+ 'lizzie',
13042
+ 'rochelle',
13043
+ 'charla',
13044
+ 'alisha',
13045
+ 'lorena',
13046
+ 'kaylee',
13047
+ 'celeste',
13048
+ 'brittney',
13049
+ 'mercedes',
13050
+ 'annabelle',
13051
+ 'noelle',
13052
+ 'sondra',
13053
+ 'marisol',
13054
+ 'karina',
13055
+ 'ronda',
13056
+ 'maritza',
13057
+ 'tessa',
13058
+ 'susana',
13059
+ 'elena',
13060
+ 'angelina',
13061
+ 'carmela',
13062
+ 'tiffani',
13063
+ 'mackenzie',
13064
+ 'kellie',
13065
+ 'genevieve',
13066
+ 'octavia',
13067
+ 'janine',
13068
+ 'carly',
13069
+ 'kasey',
13070
+ 'juliana',
13071
+ 'gabrielle',
13072
+ 'leanne',
13073
+ 'katelyn',
13074
+ 'dianna',
13075
+ 'jaime',
13076
+ 'roxanne',
13077
+ 'simone',
13078
+ 'julianne',
13079
+ 'lacie',
13080
+ 'latasha',
13081
+ 'leann',
13082
+ 'ashlee',
13083
+ 'cara',
13084
+ 'gretchen',
13085
+ 'mallory',
13086
+ 'shayla',
13087
+ 'lara',
13088
+ 'lindsey',
13089
+ 'kerri',
13090
+ 'desiree',
13091
+ 'cassidy',
13092
+ 'kaitlyn',
13093
+ 'deana',
13094
+ 'kira',
13095
+ 'cristina',
13096
+ 'sasha',
13097
+ 'eliza',
13098
+ 'melisa',
13099
+ 'aubrey',
13100
+ 'terrie',
13101
+ 'teri',
13102
+ 'lorene',
13103
+ 'katharine',
13104
+ 'jillian',
13105
+ 'doreen',
13106
+ 'hilary',
13107
+ 'kathrine',
13108
+ 'fay',
13109
+ 'adrienne',
13110
+ 'gwen',
13111
+ 'marisa',
13112
+ 'tasha',
13113
+ 'silvia',
13114
+ 'eula',
13115
+ 'maribel',
13116
+ 'susanna',
13117
+ 'leila',
13118
+ 'corinne',
13119
+ 'jami',
13120
+ 'alyce',
13121
+ 'dina',
13122
+ 'marcia',
13123
+ 'leigh',
13124
+ 'jenifer',
13125
+ 'corrine',
13126
+ 'hillary',
13127
+ 'marci',
13128
+ 'dixie',
13129
+ 'deidre',
13130
+ 'elise',
13131
+ 'ines',
13132
+ 'lily',
13133
+ 'lenora',
13134
+ 'marina',
13135
+ 'nikki',
13136
+ 'elsa',
13137
+ 'roxane',
13138
+ 'margo',
13139
+ 'rene',
13140
+ 'alyssa',
13141
+ 'sherrie',
13142
+ 'mollie',
13143
+ 'alycia',
13144
+ 'aurora',
13145
+ 'lorelei',
13146
+ 'joelle',
13147
+ 'kayleigh',
13148
+ 'jeanine',
13149
+ 'shana',
13150
+ 'sheree',
13151
+ 'elaina',
13152
+ 'elvira',
13153
+ 'chandra',
13154
+ 'marta',
13155
+ 'marianna',
13156
+ 'caitlin',
13157
+ 'leanne',
13158
+ 'jayne',
13159
+ 'rosalind',
13160
+ 'roxann',
13161
+ 'dee',
13162
+ 'marjory',
13163
+ 'rosalie',
13164
+ 'deann',
13165
+ 'shanna',
13166
+ 'angelia',
13167
+ 'darlene',
13168
+ 'juliet',
13169
+ 'misti',
13170
+ 'corina',
13171
+ 'larissa',
13172
+ 'noemi',
13173
+ 'marlena',
13174
+ 'tricia',
13175
+ 'shelby',
13176
+ 'carly',
13177
+ 'julieann',
13178
+ 'antonia',
13179
+ 'candi',
13180
+ 'luciana',
13181
+ 'peggy',
13182
+ 'georgina',
13183
+ 'jasmine',
13184
+ 'liza',
13185
+ 'marybeth',
13186
+ 'katarina',
13187
+ 'marisela',
13188
+ 'elva',
13189
+ 'shanda',
13190
+ 'terra',
13191
+ 'juliann',
13192
+ 'patrica',
13193
+ 'bonita',
13194
+ 'rosalinda',
13195
+ 'maricela',
13196
+ 'marla',
13197
+ 'krystle',
13198
+ 'kaylyn',
13199
+ 'karissa',
13200
+ 'shayna',
13201
+ 'maris',
13202
+ 'marcella',
13203
+ 'deirdre',
13204
+ 'darci',
13205
+ 'arlene',
13206
+ 'tabatha',
13207
+ 'annmarie',
13208
+ 'shauna',
13209
+ 'rebekah',
13210
+ 'deidra',
13211
+ 'shawna',
13212
+ 'paige',
13213
+ 'aimee',
13214
+ 'gabriela',
13215
+ 'jodi',
13216
+ 'kelli',
13217
+ 'bridgett',
13218
+ 'sadie',
13219
+ 'june',
13220
+ 'katy',
13221
+ 'krystal',
13222
+ 'tracie',
13223
+ 'angelique',
13224
+ ];
13225
+
13226
+ // Check for male names
13227
+ if (maleNames.includes(firstNameLower)) {
13228
+ return 'male';
13229
+ }
13230
+
13231
+ // Check for female names
13232
+ if (femaleNames.includes(firstNameLower)) {
13233
+ return 'female';
13234
+ }
13235
+
13236
+ // Check for unisex names that are commonly one gender
13237
+ const unisexMale = ['jordan', 'taylor', 'alex', 'jamie', 'casey', 'avery', 'riley', 'jesse', 'zane'];
13238
+ const unisexFemale = ['morgan', 'skyler', 'quinn', 'harper', 'payton', 'blake', 'reagan', 'sawyer', 'kendall'];
13239
+
13240
+ if (unisexMale.includes(firstNameLower)) {
13241
+ return 'male';
13242
+ }
13243
+
13244
+ if (unisexFemale.includes(firstNameLower)) {
13245
+ return 'female';
13246
+ }
13247
+
13248
+ return 'unknown';
13249
+ }
13250
+
13251
+ function inferNationalityFromName(firstName, lastName) {
13252
+ if (!firstName && !lastName) return 'unknown';
13253
+
13254
+ const fullName = `${firstName} ${lastName}`.toLowerCase();
13255
+
13256
+ // Common nationality patterns in names
13257
+ const nationalityPatterns = {
13258
+ // Asian names
13259
+ CN: ['li', 'wang', 'zhang', 'liu', 'chen', 'yang', 'zhao', 'huang', 'zhou', 'wu', 'xu', 'sun', 'ma', 'zhu', 'guo', 'lin', 'xie', 'he', 'gao'],
13260
+ JP: ['sato', 'suzuki', 'takahashi', 'tanaka', 'watanabe', 'ito', 'yamamoto', 'nakamura', 'kobayashi', 'kato', 'yoshida', 'yamada', 'sasaki', 'yamaguchi', 'matsumoto'],
13261
+ KR: ['kim', 'lee', 'park', 'choi', 'jung', 'kang', 'cho', 'yun', 'lim', 'jang', 'han', 'shin', 'seo', 'kwon', 'oh'],
13262
+ IN: ['patel', 'sharma', 'singh', 'kumar', 'gupta', 'reddy', 'mehta', 'rai', 'verma', 'jha', 'malik', 'yadav', 'thakur'],
13263
+
13264
+ // European names
13265
+ ES: ['garcia', 'rodriguez', 'gonzalez', 'fernandez', 'lopez', 'martinez', 'sanchez', 'perez', 'gomez', 'martin', 'jimenez', 'ruiz', 'hernandez', 'diaz', 'moreno'],
13266
+ FR: ['martin', 'bernard', 'dubois', 'thomas', 'robert', 'richard', 'petit', 'durand', 'leroy', 'moreau', 'simon', 'laurent', 'lefebvre', 'michel', 'garcia'],
13267
+ IT: ['rossi', 'russo', 'ferrari', 'esposito', 'bianchi', 'romano', 'colombo', 'ricci', 'marino', 'greco', 'bruno', 'gallo', 'conti', 'de luca', 'mancini'],
13268
+ DE: ['muller', 'schmidt', 'schneider', 'fischer', 'weber', 'meyer', 'wagner', 'becker', 'schulz', 'hoffmann', 'schäfer', 'koch', 'bauer', 'richter', 'klein'],
13269
+ GB: ['smith', 'jones', 'taylor', 'brown', 'williams', 'wilson', 'johnson', 'davis', 'robinson', 'wright', 'thompson', 'evans', 'walker', 'white', 'roberts'],
13270
+
13271
+ // Other regions
13272
+ BR: ['silva', 'santos', 'oliveira', 'souza', 'rodrigues', 'ferreira', 'alves', 'pereira', 'lima', 'gomes', 'costa', 'ribeiro', 'martins', 'carvalho', 'almeida'],
13273
+ RU: ['ivanov', 'smirnov', 'kuznetsov', 'popov', 'sokolov', 'lebedeva', 'kozlov', 'novikov', 'morozov', 'petrov', 'volkov', 'solovyov', 'vasilyev', 'zaitsev'],
13274
+ PL: ['nowak', 'kowalski', 'wisniewski', 'wojcik', 'kowalczyk', 'kaminski', 'lewandowski', 'zielinski', 'szymanski', 'wozniak', 'dabrowski', 'kozłowski', 'jankowski'],
13275
+ TR: ['yilmaz', 'kaya', 'demir', 'celik', 'sahin', 'yildiz', 'yildirim', 'ozturk', 'aydin', 'arslan', 'doğan', 'kural', 'aslan', 'çetin', 'kara'],
13276
+ };
13277
+
13278
+ // Check for patterns in last name
13279
+ for (const [countryCode, patterns] of Object.entries(nationalityPatterns)) {
13280
+ for (const pattern of patterns) {
13281
+ if (lastName.toLowerCase().includes(pattern.toLowerCase())) {
13282
+ return countryCode;
13283
+ }
13284
+ }
13285
+ }
13286
+
13287
+ // Check common first names by country
13288
+ const firstNamePatterns = {
13289
+ CN: ['wei', 'ming', 'li', 'jing', 'feng', 'yan', 'hua', 'xin', 'jun', 'lei'],
13290
+ JP: ['yuki', 'haru', 'aki', 'natsu', 'sora', 'hina', 'sakura', 'ren', 'yui', 'aoi'],
13291
+ KR: ['min', 'ji', 'seo', 'hyun', 'jung', 'soo', 'young', 'won', 'hae', 'jin'],
13292
+ IN: ['raj', 'dev', 'kumar', 'arun', 'vijay', 'suresh', 'anil', 'ram', 'hari', 'mohan'],
13293
+ ES: ['jose', 'juan', 'antonio', 'francisco', 'manuel', 'david', 'javier', 'daniel', 'carlos', 'alejandro'],
13294
+ FR: ['jean', 'pierre', 'michel', 'philippe', 'alain', 'nicolas', 'christophe', 'patrick', 'daniel', 'bernard'],
13295
+ IT: ['marco', 'andrea', 'alessandro', 'matteo', 'luca', 'giovanni', 'antonio', 'francesco', 'mario', 'luigi'],
13296
+ DE: ['hans', 'peter', 'wolfgang', 'michael', 'werner', 'klaus', 'thomas', 'helmut', 'gerhard', 'andreas'],
13297
+ RU: ['alexander', 'sergei', 'vladimir', 'dmitri', 'igor', 'nikolai', 'mikhail', 'yuri', 'oleg', 'pavel'],
13298
+ PL: ['jan', 'andrzej', 'piotr', 'krzysztof', 'stanislaw', 'tomasz', 'paweł', 'marek', 'michal', 'jerzy'],
13299
+ };
13300
+
13301
+ for (const [countryCode, patterns] of Object.entries(firstNamePatterns)) {
13302
+ for (const pattern of patterns) {
13303
+ if (firstName.toLowerCase().includes(pattern.toLowerCase())) {
13304
+ return countryCode;
13305
+ }
13306
+ }
13307
+ }
13308
+
13309
+ // Default to US for common English names
13310
+ const englishFirstNames = ['john', 'james', 'robert', 'michael', 'william', 'david', 'mary', 'patricia', 'linda', 'barbara'];
13311
+ if (englishFirstNames.includes(firstName.toLowerCase())) {
13312
+ return 'US';
13313
+ }
13314
+
13315
+ return 'unknown';
13316
+ }
13317
+
13318
+ async function hashString(str) {
13319
+ // Simple hash for context tracking
13320
+ const encoder = new TextEncoder();
13321
+ const data = encoder.encode(str);
13322
+ const hashBuffer = await crypto.subtle.digest('SHA-256', data);
13323
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
13324
+ return hashArray
13325
+ .map((b) => b.toString(16).padStart(2, '0'))
13326
+ .join('')
13327
+ .substring(0, 16);
13328
+ }
13329
+
13330
+ // ========== LIGHT MODE FUNCTION ==========
13331
+ const getLightPersonInfo = (name, email) => {
13332
+ const nameParts = extractNameParts(name);
13333
+ const gender = inferGenderFromName(nameParts.firstName);
13334
+ const nationality = inferNationalityFromName(nameParts.firstName, nameParts.lastName);
13335
+
13336
+ return {
13337
+ person_full_name: name,
13338
+ person_first_name: nameParts.firstName,
13339
+ person_last_name: nameParts.lastName,
13340
+ person_gender: gender,
13341
+ person_nationality: nationality,
13342
+ source: 'light_mode_inference',
13343
+ };
13344
+ };
13345
+
13346
+ // ========== MAIN FUNCTION LOGIC ==========
13347
+
13348
+ // Check if light mode is requested
13349
+ if (options.light === true) {
13350
+ console.log(`[Person Info] Running in light mode for: ${name} (${email})`);
13351
+ return getLightPersonInfo(name, email);
13352
+ }
13353
+
13354
+ // Original full mode with web search continues below...
13355
+ function processPersonInfo(data, originalName, originalEmail, emailContext) {
13356
+ // Ensure basic fields are populated
13357
+ if (!data.person_first_name || !data.person_last_name) {
13358
+ const nameParts = originalName.trim().split(/\s+/);
13359
+ data.person_first_name = data.person_first_name || nameParts[0] || '';
13360
+ data.person_last_name = data.person_last_name || nameParts.slice(1).join(' ') || '';
13361
+ }
13362
+
13363
+ // Set full name if not provided
13364
+ if (!data.person_full_name) {
13365
+ data.person_full_name = `${data.person_first_name} ${data.person_last_name}`.trim();
13366
+ }
13367
+
13368
+ // Ensure email is set
13369
+ data.person_email = data.person_email || originalEmail;
13370
+
13371
+ // Process demographic information
13372
+ data.person_age = processAge(data.person_age);
13373
+ data.person_gender = data.person_gender || 'unknown';
13374
+ data.person_nationality = processNationality(data.person_nationality);
13375
+
13376
+ // Standardize location fields
13377
+ data.person_location_city = cleanText(data.person_location_city);
13378
+ data.person_location_state = cleanStateProvince(data.person_location_state);
13379
+ data.person_location_country = processCountryCode(data.person_location_country);
13380
+
13381
+ // Format full location
13382
+ if (!data.person_location_full || data.person_location_full === 'Not available') {
13383
+ const locationParts = [];
13384
+ if (data.person_location_city && data.person_location_city !== 'Not available') {
13385
+ locationParts.push(data.person_location_city);
13386
+ }
13387
+ if (data.person_location_state && data.person_location_state !== 'Not available') {
13388
+ locationParts.push(data.person_location_state);
13389
+ }
13390
+ if (data.person_location_country && data.person_location_country !== 'Not available') {
13391
+ locationParts.push(countryCodeToName(data.person_location_country));
13392
+ }
13393
+ data.person_location_full = locationParts.length > 0 ? locationParts.join(', ') : 'Not available';
13394
+ }
13395
+
13396
+ // Clean and validate URLs
13397
+ data.person_linkedin = validateURL(data.person_linkedin);
13398
+ data.person_twitter = validateURL(data.person_twitter);
13399
+ data.person_github = validateURL(data.person_github);
13400
+ data.person_website = validateURL(data.person_website);
13401
+
13402
+ // Clean phone number
13403
+ data.person_phone = validatePhoneNumber(data.person_phone);
13404
+
13405
+ // Ensure arrays are properly formatted
13406
+ data.person_skills = ensureArray(data.person_skills);
13407
+ data.person_expertise = ensureArray(data.person_expertise);
13408
+ data.person_education = ensureArray(data.person_education);
13409
+ data.person_languages = ensureArray(data.person_languages);
13410
+ data.person_interests = ensureArray(data.person_interests);
13411
+ data.person_context_insights = ensureArray(data.person_context_insights);
13412
+
13413
+ // Calculate additional metadata
13414
+ data.person_name_initials = getInitials(data.person_first_name, data.person_last_name);
13415
+ data.person_age_range = calculateAgeRange(data.person_age);
13416
+ data.person_seniority_score = calculateSeniorityScore(data);
13417
+ data.person_pronouns = getPronounsFromGender(data.person_gender);
13418
+
13419
+ // Extract insights from email context
13420
+ if (emailContext && !data.person_context_insights) {
13421
+ data.person_context_insights = extractContextInsights(emailContext);
13422
+ }
13423
+
13424
+ // Create structured objects for easier access
13425
+ data.person_demographics = {
13426
+ age: data.person_age,
13427
+ age_range: data.person_age_range,
13428
+ gender: data.person_gender,
13429
+ pronouns: data.person_pronouns,
13430
+ nationality: data.person_nationality,
13431
+ nationality_name: countryCodeToName(data.person_nationality),
13432
+ };
13433
+
13434
+ data.person_contact = {
13435
+ email: data.person_email,
13436
+ phone: data.person_phone,
13437
+ location: {
13438
+ city: data.person_location_city,
13439
+ state: data.person_location_state,
13440
+ country: data.person_location_country,
13441
+ country_name: countryCodeToName(data.person_location_country),
13442
+ full: data.person_location_full,
13443
+ },
13444
+ };
13445
+
13446
+ data.person_professional = {
13447
+ title: data.person_title,
13448
+ company: data.person_company,
13449
+ industry: data.person_industry,
13450
+ career_level: data.person_career_level,
13451
+ years_experience: data.person_years_experience,
13452
+ skills: data.person_skills,
13453
+ expertise: data.person_expertise,
13454
+ education: data.person_education,
13455
+ };
13456
+
13457
+ data.person_online = {
13458
+ linkedin: data.person_linkedin,
13459
+ twitter: data.person_twitter,
13460
+ github: data.person_github,
13461
+ website: data.person_website,
13462
+ other: data.person_social_other || [],
13463
+ };
13464
+
13465
+ // Add context metadata
13466
+ data.context_analysis = {
13467
+ has_context: !!emailContext,
13468
+ context_length: emailContext?.length || 0,
13469
+ extracted_insights: data.person_context_insights || [],
13470
+ context_useful: data.person_has_context_hints || false,
13471
+ };
13472
+
13473
+ // Add timestamp
13474
+ data.retrieved_at = new Date().toISOString();
13475
+
13476
+ return data;
13477
+ }
13478
+
13479
+ function processAge(ageInput) {
13480
+ if (!ageInput || ageInput === 'unknown' || ageInput === 'Not available') {
13481
+ return 'unknown';
13482
+ }
13483
+
13484
+ if (typeof ageInput === 'number') {
13485
+ return Math.max(18, Math.min(100, ageInput));
13486
+ }
13487
+
13488
+ if (typeof ageInput === 'string') {
13489
+ // Parse age ranges like "30-40", "35+", "mid-30s"
13490
+ const rangeMatch = ageInput.match(/(\d+)-(\d+)/);
13491
+ if (rangeMatch) {
13492
+ const avg = Math.floor((parseInt(rangeMatch[1]) + parseInt(rangeMatch[2])) / 2);
13493
+ return avg;
13494
+ }
13495
+
13496
+ const numberMatch = ageInput.match(/\d+/);
13497
+ if (numberMatch) {
13498
+ const age = parseInt(numberMatch[0]);
13499
+ if (age >= 18 && age <= 100) return age;
13500
+ }
13501
+
13502
+ // Handle textual age descriptions
13503
+ const text = ageInput.toLowerCase();
13504
+ if (text.includes('twent') || text.includes('20')) return 25;
13505
+ if (text.includes('thirt') || text.includes('30')) return 35;
13506
+ if (text.includes('fort') || text.includes('40')) return 45;
13507
+ if (text.includes('fift') || text.includes('50')) return 55;
13508
+ if (text.includes('sixt') || text.includes('60')) return 65;
13509
+ }
13510
+
13511
+ return 'unknown';
13512
+ }
13513
+
13514
+ function calculateAgeRange(age) {
13515
+ if (age === 'unknown' || typeof age !== 'number') return 'Unknown';
13516
+
13517
+ if (age < 25) return '18-24';
13518
+ if (age < 30) return '25-29';
13519
+ if (age < 35) return '30-34';
13520
+ if (age < 40) return '35-39';
13521
+ if (age < 50) return '40-49';
13522
+ if (age < 60) return '50-59';
13523
+ return '60+';
13524
+ }
13525
+
13526
+ function processNationality(nationality) {
13527
+ if (!nationality || nationality.toLowerCase() === 'unknown' || nationality.toLowerCase() === 'not available') {
13528
+ return 'unknown';
13529
+ }
13530
+
13531
+ // Convert to uppercase for country codes
13532
+ const upper = nationality.toUpperCase();
13533
+
13534
+ // Map common variations to ISO codes
13535
+ const countryMap = {
13536
+ USA: 'US',
13537
+ 'UNITED STATES': 'US',
13538
+ AMERICA: 'US',
13539
+ UK: 'GB',
13540
+ 'UNITED KINGDOM': 'GB',
13541
+ ENGLAND: 'GB',
13542
+ SCOTLAND: 'GB',
13543
+ CANADA: 'CA',
13544
+ AUSTRALIA: 'AU',
13545
+ INDIA: 'IN',
13546
+ GERMANY: 'DE',
13547
+ FRANCE: 'FR',
13548
+ CHINA: 'CN',
13549
+ JAPAN: 'JP',
13550
+ BRAZIL: 'BR',
13551
+ MEXICO: 'MX',
13552
+ SPAIN: 'ES',
13553
+ ITALY: 'IT',
13554
+ NETHERLANDS: 'NL',
13555
+ SWEDEN: 'SE',
13556
+ SWITZERLAND: 'CH',
13557
+ 'SOUTH KOREA': 'KR',
13558
+ RUSSIA: 'RU',
13559
+ };
13560
+
13561
+ if (countryMap[upper]) {
13562
+ return countryMap[upper];
13563
+ }
13564
+
13565
+ // If it's already a 2-letter code, return it
13566
+ if (/^[A-Z]{2}$/.test(upper)) {
13567
+ return upper;
13568
+ }
13569
+
13570
+ return 'unknown';
13571
+ }
13572
+
13573
+ function processCountryCode(country) {
13574
+ if (!country || country.toLowerCase() === 'not available') {
13575
+ return 'unknown';
13576
+ }
13577
+
13578
+ return processNationality(country);
13579
+ }
13580
+
13581
+ function countryCodeToName(code) {
13582
+ if (!code || code === 'unknown') return 'Unknown';
13583
+
13584
+ const countryNames = {
13585
+ US: 'United States',
13586
+ GB: 'United Kingdom',
13587
+ CA: 'Canada',
13588
+ AU: 'Australia',
13589
+ DE: 'Germany',
13590
+ FR: 'France',
13591
+ JP: 'Japan',
13592
+ CN: 'China',
13593
+ IN: 'India',
13594
+ BR: 'Brazil',
13595
+ MX: 'Mexico',
13596
+ ES: 'Spain',
13597
+ IT: 'Italy',
13598
+ NL: 'Netherlands',
13599
+ SE: 'Sweden',
13600
+ CH: 'Switzerland',
13601
+ KR: 'South Korea',
13602
+ RU: 'Russia',
13603
+ };
13604
+
13605
+ return countryNames[code] || code;
13606
+ }
13607
+
13608
+ function getPronounsFromGender(gender) {
13609
+ switch (gender) {
13610
+ case 'male':
13611
+ return ['he/him', 'his'];
13612
+ case 'female':
13613
+ return ['she/her', 'hers'];
13614
+ case 'non-binary':
13615
+ return ['they/them', 'theirs'];
13616
+ default:
13617
+ return ['unknown'];
13618
+ }
13619
+ }
13620
+
13621
+ function extractContextInsights(emailContext) {
13622
+ if (!emailContext) return [];
13623
+
13624
+ const insights = [];
13625
+ const context = emailContext.toLowerCase();
13626
+
13627
+ // Extract potential job role hints
13628
+ const rolePatterns = {
13629
+ manager: ['manage', 'supervise', 'team lead', 'department head'],
13630
+ engineer: ['engineer', 'developer', 'programmer', 'software'],
13631
+ sales: ['sales', 'account executive', 'business development'],
13632
+ marketing: ['marketing', 'campaign', 'brand', 'social media'],
13633
+ executive: ['ceo', 'cto', 'cfo', 'director', 'vp', 'vice president'],
13634
+ };
13635
+
13636
+ for (const [role, patterns] of Object.entries(rolePatterns)) {
13637
+ if (patterns.some((pattern) => context.includes(pattern))) {
13638
+ insights.push(`Possible ${role} role indicated in email`);
13639
+ }
13640
+ }
13641
+
13642
+ // Extract company hints
13643
+ const companyMatch = context.match(/(?:at|from|of)\s+([A-Z][A-Za-z0-9\s&]+)(?:\s|$)/);
13644
+ if (companyMatch && companyMatch[1].length > 2) {
13645
+ insights.push(`Mentioned company/organization: ${companyMatch[1].trim()}`);
13646
+ }
13647
+
13648
+ // Extract project/technology hints
13649
+ const techMatch = context.match(/(?:using|with|built\s+in)\s+([A-Za-z0-9\s+#]+)(?:\s|$)/);
13650
+ if (techMatch) {
13651
+ insights.push(`Technology mentioned: ${techMatch[1].trim()}`);
13652
+ }
13653
+
13654
+ // Extract urgency/priority
13655
+ if (context.includes('urgent') || context.includes('asap') || context.includes('immediately')) {
13656
+ insights.push('Email suggests urgency or time sensitivity');
13657
+ }
13658
+
13659
+ // Extract tone
13660
+ if (context.includes('thank you') || context.includes('appreciate') || context.includes('grateful')) {
13661
+ insights.push('Email shows appreciative/grateful tone');
13662
+ }
13663
+
13664
+ return insights.slice(0, 5); // Limit to 5 insights
13665
+ }
13666
+
13667
+ // Existing helper functions (kept from previous version)
13668
+ function validateURL(url) {
13669
+ if (!url || url.toLowerCase() === 'not available' || url.trim() === '') {
13670
+ return 'Not available';
13671
+ }
13672
+
13673
+ const trimmed = url.trim();
13674
+
13675
+ // Ensure URL has proper protocol
13676
+ if (!trimmed.startsWith('http://') && !trimmed.startsWith('https://')) {
13677
+ // Check if it looks like a domain
13678
+ if (trimmed.includes('.') && !trimmed.includes(' ')) {
13679
+ return `https://${trimmed.replace(/^www\./, '')}`;
13680
+ }
13681
+ return 'Not available';
13682
+ }
13683
+
13684
+ return trimmed;
13685
+ }
13686
+
13687
+ function validatePhoneNumber(phone) {
13688
+ if (!phone || phone.toLowerCase() === 'not available' || phone.trim() === '') {
13689
+ return 'Not available';
13690
+ }
13691
+
13692
+ const cleaned = phone.replace(/[^\d\+\(\)\-\s]/g, '');
13693
+ const digitCount = (cleaned.match(/\d/g) || []).length;
13694
+
13695
+ // Require at least 7 digits for a valid phone number
13696
+ if (digitCount < 7) {
13697
+ return 'Not available';
13698
+ }
13699
+
13700
+ return cleaned.trim();
13701
+ }
13702
+
13703
+ function cleanText(text) {
13704
+ if (!text || text.toLowerCase() === 'not available' || text.trim() === '') {
13705
+ return 'Not available';
13706
+ }
13707
+
13708
+ return text.replace(/\s+/g, ' ').trim();
13709
+ }
13710
+
13711
+ function cleanStateProvince(state) {
13712
+ if (!state || state.toLowerCase() === 'not available' || state.trim() === '') {
13713
+ return 'Not available';
13714
+ }
13715
+
13716
+ return state.trim();
13717
+ }
13718
+
13719
+ function ensureArray(value) {
13720
+ if (!value) return [];
13721
+ if (Array.isArray(value)) return value;
13722
+ if (typeof value === 'string') {
13723
+ try {
13724
+ const parsed = JSON.parse(value);
13725
+ if (Array.isArray(parsed)) return parsed;
13726
+ } catch (e) {
13727
+ // If it's a comma-separated string, split it
13728
+ if (value.includes(',')) {
13729
+ return value
13730
+ .split(',')
13731
+ .map((item) => item.trim())
13732
+ .filter((item) => item);
13733
+ }
13734
+ // Otherwise, return as single item array
13735
+ return [value.trim()];
13736
+ }
13737
+ }
13738
+ return [String(value)];
13739
+ }
13740
+
13741
+ function getInitials(firstName, lastName) {
13742
+ const first = firstName?.charAt(0)?.toUpperCase() || '';
13743
+ const last = lastName?.charAt(0)?.toUpperCase() || '';
13744
+ return first + last || '?';
13745
+ }
13746
+
13747
+ function calculateSeniorityScore(data) {
13748
+ let score = 0;
13749
+
13750
+ // Add points for career level
13751
+ const levelScores = {
13752
+ executive: 90,
13753
+ director: 80,
13754
+ lead: 70,
13755
+ senior: 60,
13756
+ mid: 40,
13757
+ entry: 20,
13758
+ unknown: 30,
13759
+ };
13760
+
13761
+ score += levelScores[data.person_career_level?.toLowerCase()] || 30;
13762
+
13763
+ // Add points for years of experience
13764
+ if (data.person_years_experience) {
13765
+ const exp = parseInt(data.person_years_experience) || 0;
13766
+ score += Math.min(exp * 2, 40); // Max 40 points for 20+ years
13767
+ }
13768
+
13769
+ // Add points for title keywords
13770
+ const title = data.person_title?.toLowerCase() || '';
13771
+ if (title.includes('vp') || title.includes('vice president') || title.includes('chief')) score += 20;
13772
+ if (title.includes('director')) score += 15;
13773
+ if (title.includes('manager') || title.includes('lead') || title.includes('head')) score += 10;
13774
+ if (title.includes('senior')) score += 5;
13775
+
13776
+ return Math.min(100, score);
13777
+ }
13778
+
13779
+ function getDefaultPersonInfo(name, email, emailContext = '', error = null) {
13780
+ const nameParts = name.trim().split(/\s+/);
13781
+ const firstName = nameParts[0] || '';
13782
+ const lastName = nameParts.slice(1).join(' ') || '';
13783
+ const domain = email.includes('@') ? email.split('@')[1] : '';
13784
+ const companyGuess = domain
13785
+ .replace(/\..*$/, '')
13786
+ .replace(/[^a-z]/gi, ' ')
13787
+ .replace(/\b\w/g, (l) => l.toUpperCase());
13788
+
13789
+ // Extract basic insights from email context
13790
+ const contextInsights = emailContext ? extractContextInsights(emailContext) : [];
13791
+
13792
+ return {
13793
+ // Basic Information
13794
+ person_full_name: name,
13795
+ person_first_name: firstName,
13796
+ person_last_name: lastName,
13797
+ person_middle_name: '',
13798
+ person_preferred_name: '',
13799
+
13800
+ // Demographic Information
13801
+ person_age: 'unknown',
13802
+ person_gender: 'unknown',
13803
+ person_nationality: 'unknown',
13804
+ person_age_range: 'Unknown',
13805
+ person_pronouns: ['unknown'],
13806
+
13807
+ // Professional Information
13808
+ person_title: 'Professional',
13809
+ person_company: companyGuess || 'Unknown',
13810
+ person_industry: 'Technology',
13811
+ person_bio: 'Information not available',
13812
+
13813
+ // Location
13814
+ person_location_city: 'Not available',
13815
+ person_location_state: 'Not available',
13816
+ person_location_country: 'unknown',
13817
+ person_location_full: 'Not available',
13818
+
13819
+ // Contact Information
13820
+ person_phone: 'Not available',
13821
+ person_email: email,
13822
+ person_email_alternate: '',
13823
+
13824
+ // Education
13825
+ person_education: [],
13826
+ person_education_highest: 'Not available',
13827
+
13828
+ // Professional Details
13829
+ person_skills: [],
13830
+ person_expertise: [],
13831
+ person_career_level: 'unknown',
13832
+ person_years_experience: 0,
13833
+
13834
+ // Social & Online Presence
13835
+ person_linkedin: 'Not available',
13836
+ person_twitter: 'Not available',
13837
+ person_github: 'Not available',
13838
+ person_website: 'Not available',
13839
+ person_social_other: [],
13840
+
13841
+ // Additional Information
13842
+ person_languages: ['English'],
13843
+ person_interests: [],
13844
+ person_achievements: [],
13845
+ person_current_projects: [],
13846
+ person_context_insights: contextInsights,
13847
+
13848
+ // Metadata
13849
+ person_available_for_opportunities: false,
13850
+ person_last_updated: new Date().toISOString(),
13851
+ person_source_confidence: 0,
13852
+ person_has_context_hints: contextInsights.length > 0,
13853
+
13854
+ // Processed fields
13855
+ person_name_initials: getInitials(firstName, lastName),
13856
+ person_seniority_score: 0,
13857
+
13858
+ // Structured objects
13859
+ person_demographics: {
13860
+ age: 'unknown',
13861
+ age_range: 'Unknown',
13862
+ gender: 'unknown',
13863
+ pronouns: ['unknown'],
13864
+ nationality: 'unknown',
13865
+ nationality_name: 'Unknown',
13866
+ },
13867
+
13868
+ person_contact: {
13869
+ email: email,
13870
+ phone: 'Not available',
13871
+ location: {
13872
+ city: 'Not available',
13873
+ state: 'Not available',
13874
+ country: 'unknown',
13875
+ country_name: 'Unknown',
13876
+ full: 'Not available',
13877
+ },
13878
+ },
13879
+
13880
+ person_professional: {
13881
+ title: 'Professional',
13882
+ company: companyGuess || 'Unknown',
13883
+ industry: 'Technology',
13884
+ career_level: 'unknown',
13885
+ years_experience: 0,
13886
+ skills: [],
13887
+ expertise: [],
13888
+ education: [],
13889
+ },
13890
+
13891
+ person_online: {
13892
+ linkedin: 'Not available',
13893
+ twitter: 'Not available',
13894
+ github: 'Not available',
13895
+ website: 'Not available',
13896
+ other: [],
13897
+ },
13898
+
13899
+ context_analysis: {
13900
+ has_context: !!emailContext,
13901
+ context_length: emailContext?.length || 0,
13902
+ extracted_insights: contextInsights,
13903
+ context_useful: contextInsights.length > 0,
13904
+ },
13905
+
13906
+ retrieved_at: new Date().toISOString(),
13907
+ error: error || 'API call failed',
13908
+ };
13909
+ }
13910
+
13911
+ // Build context-aware prompt
13912
+ let context_prompt = `Research the person: "${name}" (${email}).`;
13913
+
13914
+ if (email_context) {
13915
+ context_prompt += `\n\nEMAIL CONTEXT PROVIDED:\n"${email_context.substring(0, 500)}${email_context.length > 500 ? '...' : ''}"`;
13916
+ }
13917
+
13918
+ context_prompt += `
13919
+
13920
+ REQUIRED PERSON INFORMATION:
13921
+ 1. Full name (first, middle, last)
13922
+ 2. Professional title/role
13923
+ 3. Company/organization they work for
13924
+ 4. Industry/field they work in
13925
+ 5. Location (city, state, country)
13926
+ 6. Professional biography (1-2 sentences)
13927
+ 7. Education background
13928
+ 8. Professional skills/expertise
13929
+ 9. Social media profiles (LinkedIn, Twitter, GitHub, etc.)
13930
+ 10. Personal website or portfolio
13931
+
13932
+ DEMOGRAPHIC INFORMATION:
13933
+ 11. Estimated age (based on career, education, and public information)
13934
+ 12. Gender (based on name, pronouns in content, or public profiles)
13935
+ 13. Nationality/country of origin (if discernible) (based on name, pronouns in content, or public profiles)
13936
+
13937
+ CONTACT INFORMATION:
13938
+ 14. Phone number (if available)
13939
+ 15. Alternate email addresses (if available)
13940
+ 16. Physical address (if available and appropriate)
13941
+
13942
+ ADDITIONAL INSIGHTS:
13943
+ 17. Career level (entry, mid, senior, executive, etc.)
13944
+ 18. Years of experience
13945
+ 19. Notable achievements/awards
13946
+ 20. Languages spoken
13947
+ 21. Professional interests
13948
+ 22. Current projects
13949
+ 23. Availability for opportunities
13950
+
13951
+ INSTRUCTIONS:
13952
+ - Use web search to find accurate, public information
13953
+ - Use email context provided to infer additional details
13954
+ - Respect privacy - only include publicly available information
13955
+ - For age: provide numeric estimate or range (e.g., 35, 30-40, unknown)
13956
+ - For gender: use male/female/non-binary/unknown based on available cues
13957
+ - For country: use 2-letter ISO code (e.g., US, IN, UK)
13958
+ - If information is not available, use "Not available" or appropriate defaults
13959
+ - For location, use standard formats (e.g., "San Francisco, CA, USA")
13960
+ - For social media, provide full URLs when available
13961
+ - Focus on professional information suitable for business networking
13962
+
13963
+ Be thorough but respectful of privacy boundaries.`;
13964
+
13965
+ const person_info_ret = await submit_chat_gpt_prompt({
13966
+ uid,
13967
+ prompt: context_prompt,
13968
+ model: 'gpt-5-nano',
13969
+ response_format: z.object({
13970
+ // Basic Information
13971
+ person_full_name: z.string().describe("Person's full name"),
13972
+ person_first_name: z.string().describe('First name'),
13973
+ person_last_name: z.string().describe('Last name'),
13974
+ person_middle_name: z.string().optional().nullable().describe('Middle name if available'),
13975
+ person_preferred_name: z.string().optional().nullable().describe('Preferred/nickname if different'),
13976
+
13977
+ // Demographic Information
13978
+ person_age: z.union([z.number().int().min(18).max(100), z.string().describe("Age range or 'unknown'")]).describe('find age base on the name Estimated age or range'),
13979
+ person_gender: z.enum(['male', 'female']).describe('find Gender based on the name'),
13980
+ person_nationality: z.string().describe('find nationality based on the name Country of origin/nationality (2-letter ISO code)'),
13981
+
13982
+ // Professional Information
13983
+ person_title: z.string().describe('Professional title/role'),
13984
+ person_company: z.string().describe('Current company/organization'),
13985
+ person_industry: z.string().describe('Primary industry/field'),
13986
+ person_bio: z.string().describe('Professional biography (1-2 sentences)'),
13987
+
13988
+ // Location
13989
+ person_location_city: z.string().describe('City of residence/work'),
13990
+ person_location_state: z.string().describe('State/province'),
13991
+ person_location_country: z.string().describe('Country (2-letter ISO code)'),
13992
+ person_location_full: z.string().describe('Full location string'),
13993
+
13994
+ // Contact Information
13995
+ person_phone: z.string().describe("Phone number or 'Not available'"),
13996
+ person_email: z.string().describe('Primary email address'),
13997
+ person_email_alternate: z.string().optional().nullable().describe('Alternate email if available'),
13998
+
13999
+ // Education
14000
+ person_education: z.array(z.string()).describe('Array of educational institutions/degrees'),
14001
+ person_education_highest: z.string().describe('Highest degree obtained'),
14002
+
14003
+ // Professional Details
14004
+ person_skills: z.array(z.string()).describe('Array of professional skills'),
14005
+ person_expertise: z.array(z.string()).describe('Areas of expertise'),
14006
+ person_career_level: z.enum(['entry', 'mid', 'senior', 'lead', 'manager', 'director', 'executive', 'founder', 'unknown']),
14007
+ person_years_experience: z.number().int().min(0).max(60).describe('Years of professional experience'),
14008
+
14009
+ // Social & Online Presence
14010
+ person_linkedin: z.string().describe("LinkedIn profile URL or 'Not available'"),
14011
+ person_twitter: z.string().describe("Twitter/X profile URL or 'Not available'"),
14012
+ person_github: z.string().describe("GitHub profile URL or 'Not available'"),
14013
+ person_website: z.string().describe("Personal website/portfolio or 'Not available'"),
14014
+ person_social_other: z.array(z.string()).optional().nullable().describe('Other social media profiles'),
14015
+
14016
+ // Additional Information
14017
+ person_languages: z.array(z.string()).describe('Languages spoken'),
14018
+ person_interests: z.array(z.string()).describe('Professional/personal interests'),
14019
+ person_achievements: z.array(z.string()).optional().nullable().describe('Notable achievements/awards'),
14020
+ person_current_projects: z.array(z.string()).optional().nullable().describe('Current projects'),
14021
+
14022
+ // Context Extracted Information
14023
+ person_context_insights: z.array(z.string()).optional().nullable().describe('Insights extracted from provided email context'),
14024
+
14025
+ // Metadata
14026
+ person_available_for_opportunities: z.boolean().describe('Open to new opportunities'),
14027
+ person_last_updated: z.string().describe('Date information was last verified'),
14028
+ person_source_confidence: z.number().min(0).max(100).describe('Confidence score for information accuracy'),
14029
+ person_has_context_hints: z.boolean().describe('Whether email context provided useful hints'),
14030
+ }),
14031
+ metadata: {
14032
+ func: 'get_person_info',
14033
+ person_name: name,
14034
+ person_email: email,
14035
+ has_email_context: !!email_context,
14036
+ email_context_length: email_context?.length || 0,
14037
+ context_hash: email_context ? await hashString(email_context.substring(0, 200)) : null,
14038
+ },
14039
+ account_profile_info,
14040
+ tools: [{ type: 'web_search_preview' }],
14041
+ });
14042
+
14043
+ try {
14044
+ if (person_info_ret.code > -1) {
14045
+ const data = typeof person_info_ret.data === 'string' ? JSON.parse(person_info_ret.data) : person_info_ret.data;
14046
+
14047
+ // Process and enhance the data
14048
+ const processedData = processPersonInfo(data, name, email, email_context);
14049
+
14050
+ return processedData;
14051
+ } else {
14052
+ console.error('Person info API call failed:', person_info_ret);
14053
+ return getDefaultPersonInfo(name, email, email_context);
14054
+ }
14055
+ } catch (error) {
14056
+ console.error('Error in get_person_info:', error);
14057
+ return getDefaultPersonInfo(name, email, email_context, error.message);
14058
+ }
14059
+ };