@xuda.io/ai_module 1.1.4674 → 1.1.4676

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 +669 -36
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -4309,6 +4309,43 @@ export const create_conversation = async function (req, job_id, headers) {
4309
4309
  // }
4310
4310
  // }
4311
4311
  // }
4312
+
4313
+ if (conversation_doc.reference_type === 'contacts') {
4314
+ if (recipient_uid) {
4315
+ // valid for chat conversation
4316
+ // write conversation to host after thumbnail update
4317
+ // get sender conversation
4318
+
4319
+ const conversation_doc = await db_module.get_app_couch_doc_native(account_profile_info.app_id, conversation_doc._id);
4320
+
4321
+ const recipient_account_profile_info = await account_module.get_active_account_profile_info(recipient_uid);
4322
+ let recipient_conversation_doc = await db_module.get_app_couch_doc_native(recipient_account_profile_info.app_id, conversation_doc._id);
4323
+ recipient_conversation_doc.title = conversation_doc.title;
4324
+
4325
+ recipient_conversation_doc.thumbnail_request_ts = conversation_doc.thumbnail_request_ts;
4326
+ recipient_conversation_doc.chat_image = conversation_doc.chat_image;
4327
+ await db_module.save_app_couch_doc_native(recipient_account_profile_info.app_id, recipient_conversation_doc);
4328
+
4329
+ await update_conversation_mood_level(
4330
+ uid,
4331
+ [
4332
+ { uid: account_profile_info.uid, contact_id: reference_id },
4333
+ { uid: recipient_uid, contact_id: recipient_contact_id },
4334
+ ],
4335
+ conversation_doc._id,
4336
+ prompt,
4337
+ uid,
4338
+ recipient_uid,
4339
+ true,
4340
+ account_profile_info,
4341
+ );
4342
+ } else {
4343
+ // other cases rather then chat
4344
+ if (perform_ai_execution) {
4345
+ await update_conversation_mood_level(uid, [{ uid: account_profile_info.uid, contact_id: reference_id }], conversation_doc._id, prompt, null, reference_id, conversation_doc.reference_type === 'contacts', account_profile_info);
4346
+ }
4347
+ }
4348
+ }
4312
4349
  }, 1000);
4313
4350
 
4314
4351
  return save_ret;
@@ -4371,42 +4408,6 @@ const process_conversation = async function (uid, conversation_id, account_profi
4371
4408
  if (category_info.category) {
4372
4409
  await update_thumbnail('conversation_category', conversation_doc, account_profile_info.app_id, uid, job_id, headers, null, null, account_profile_info);
4373
4410
  }
4374
- if (conversation_doc.reference_type === 'contacts') {
4375
- if (recipient_uid) {
4376
- // valid for chat conversation
4377
- // write conversation to host after thumbnail update
4378
- // get sender conversation
4379
-
4380
- const conversation_doc = await db_module.get_app_couch_doc_native(account_profile_info.app_id, conversation_doc._id);
4381
-
4382
- const recipient_account_profile_info = await account_module.get_active_account_profile_info(recipient_uid);
4383
- let recipient_conversation_doc = await db_module.get_app_couch_doc_native(recipient_account_profile_info.app_id, conversation_doc._id);
4384
- recipient_conversation_doc.title = conversation_doc.title;
4385
-
4386
- recipient_conversation_doc.thumbnail_request_ts = conversation_doc.thumbnail_request_ts;
4387
- recipient_conversation_doc.chat_image = conversation_doc.chat_image;
4388
- await db_module.save_app_couch_doc_native(recipient_account_profile_info.app_id, recipient_conversation_doc);
4389
-
4390
- await update_conversation_mood_level(
4391
- uid,
4392
- [
4393
- { uid: account_profile_info.uid, contact_id: reference_id },
4394
- { uid: recipient_uid, contact_id: recipient_contact_id },
4395
- ],
4396
- conversation_doc._id,
4397
- prompt,
4398
- uid,
4399
- recipient_uid,
4400
- true,
4401
- account_profile_info,
4402
- );
4403
- } else {
4404
- // other cases rather then chat
4405
- if (perform_ai_execution) {
4406
- await update_conversation_mood_level(uid, [{ uid: account_profile_info.uid, contact_id: reference_id }], conversation_doc._id, prompt, null, reference_id, conversation_doc.reference_type === 'contacts', account_profile_info);
4407
- }
4408
- }
4409
- }
4410
4411
  };
4411
4412
 
4412
4413
  const contactGuardrailAgent = new Agent({
@@ -9094,3 +9095,635 @@ export const get_business_info = async function (uid, name, email, account_profi
9094
9095
  return getDefaultBusinessInfo(name, email, error.message);
9095
9096
  }
9096
9097
  };
9098
+
9099
+ export const get_person_info = async function (uid, name, email, account_profile_info, email_context = '') {
9100
+ // ========== HELPER FUNCTIONS ==========
9101
+
9102
+ async function hashString(str) {
9103
+ // Simple hash for context tracking
9104
+ const encoder = new TextEncoder();
9105
+ const data = encoder.encode(str);
9106
+ const hashBuffer = await crypto.subtle.digest('SHA-256', data);
9107
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
9108
+ return hashArray
9109
+ .map((b) => b.toString(16).padStart(2, '0'))
9110
+ .join('')
9111
+ .substring(0, 16);
9112
+ }
9113
+
9114
+ function processPersonInfo(data, originalName, originalEmail, emailContext) {
9115
+ // Ensure basic fields are populated
9116
+ if (!data.person_first_name || !data.person_last_name) {
9117
+ const nameParts = originalName.trim().split(/\s+/);
9118
+ data.person_first_name = data.person_first_name || nameParts[0] || '';
9119
+ data.person_last_name = data.person_last_name || nameParts.slice(1).join(' ') || '';
9120
+ }
9121
+
9122
+ // Set full name if not provided
9123
+ if (!data.person_full_name) {
9124
+ data.person_full_name = `${data.person_first_name} ${data.person_last_name}`.trim();
9125
+ }
9126
+
9127
+ // Ensure email is set
9128
+ data.person_email = data.person_email || originalEmail;
9129
+
9130
+ // Process demographic information
9131
+ data.person_age = processAge(data.person_age);
9132
+ data.person_gender = data.person_gender || 'unknown';
9133
+ data.person_nationality = processNationality(data.person_nationality);
9134
+
9135
+ // Standardize location fields
9136
+ data.person_location_city = cleanText(data.person_location_city);
9137
+ data.person_location_state = cleanStateProvince(data.person_location_state);
9138
+ data.person_location_country = processCountryCode(data.person_location_country);
9139
+
9140
+ // Format full location
9141
+ if (!data.person_location_full || data.person_location_full === 'Not available') {
9142
+ const locationParts = [];
9143
+ if (data.person_location_city && data.person_location_city !== 'Not available') {
9144
+ locationParts.push(data.person_location_city);
9145
+ }
9146
+ if (data.person_location_state && data.person_location_state !== 'Not available') {
9147
+ locationParts.push(data.person_location_state);
9148
+ }
9149
+ if (data.person_location_country && data.person_location_country !== 'Not available') {
9150
+ locationParts.push(countryCodeToName(data.person_location_country));
9151
+ }
9152
+ data.person_location_full = locationParts.length > 0 ? locationParts.join(', ') : 'Not available';
9153
+ }
9154
+
9155
+ // Clean and validate URLs
9156
+ data.person_linkedin = validateURL(data.person_linkedin);
9157
+ data.person_twitter = validateURL(data.person_twitter);
9158
+ data.person_github = validateURL(data.person_github);
9159
+ data.person_website = validateURL(data.person_website);
9160
+
9161
+ // Clean phone number
9162
+ data.person_phone = validatePhoneNumber(data.person_phone);
9163
+
9164
+ // Ensure arrays are properly formatted
9165
+ data.person_skills = ensureArray(data.person_skills);
9166
+ data.person_expertise = ensureArray(data.person_expertise);
9167
+ data.person_education = ensureArray(data.person_education);
9168
+ data.person_languages = ensureArray(data.person_languages);
9169
+ data.person_interests = ensureArray(data.person_interests);
9170
+ data.person_context_insights = ensureArray(data.person_context_insights);
9171
+
9172
+ // Calculate additional metadata
9173
+ data.person_name_initials = getInitials(data.person_first_name, data.person_last_name);
9174
+ data.person_age_range = calculateAgeRange(data.person_age);
9175
+ data.person_seniority_score = calculateSeniorityScore(data);
9176
+ data.person_pronouns = getPronounsFromGender(data.person_gender);
9177
+
9178
+ // Extract insights from email context
9179
+ if (emailContext && !data.person_context_insights) {
9180
+ data.person_context_insights = extractContextInsights(emailContext);
9181
+ }
9182
+
9183
+ // Create structured objects for easier access
9184
+ data.person_demographics = {
9185
+ age: data.person_age,
9186
+ age_range: data.person_age_range,
9187
+ gender: data.person_gender,
9188
+ pronouns: data.person_pronouns,
9189
+ nationality: data.person_nationality,
9190
+ nationality_name: countryCodeToName(data.person_nationality),
9191
+ };
9192
+
9193
+ data.person_contact = {
9194
+ email: data.person_email,
9195
+ phone: data.person_phone,
9196
+ location: {
9197
+ city: data.person_location_city,
9198
+ state: data.person_location_state,
9199
+ country: data.person_location_country,
9200
+ country_name: countryCodeToName(data.person_location_country),
9201
+ full: data.person_location_full,
9202
+ },
9203
+ };
9204
+
9205
+ data.person_professional = {
9206
+ title: data.person_title,
9207
+ company: data.person_company,
9208
+ industry: data.person_industry,
9209
+ career_level: data.person_career_level,
9210
+ years_experience: data.person_years_experience,
9211
+ skills: data.person_skills,
9212
+ expertise: data.person_expertise,
9213
+ education: data.person_education,
9214
+ };
9215
+
9216
+ data.person_online = {
9217
+ linkedin: data.person_linkedin,
9218
+ twitter: data.person_twitter,
9219
+ github: data.person_github,
9220
+ website: data.person_website,
9221
+ other: data.person_social_other || [],
9222
+ };
9223
+
9224
+ // Add context metadata
9225
+ data.context_analysis = {
9226
+ has_context: !!emailContext,
9227
+ context_length: emailContext?.length || 0,
9228
+ extracted_insights: data.person_context_insights || [],
9229
+ context_useful: data.person_has_context_hints || false,
9230
+ };
9231
+
9232
+ // Add timestamp
9233
+ data.retrieved_at = new Date().toISOString();
9234
+
9235
+ return data;
9236
+ }
9237
+
9238
+ function processAge(ageInput) {
9239
+ if (!ageInput || ageInput === 'unknown' || ageInput === 'Not available') {
9240
+ return 'unknown';
9241
+ }
9242
+
9243
+ if (typeof ageInput === 'number') {
9244
+ return Math.max(18, Math.min(100, ageInput));
9245
+ }
9246
+
9247
+ if (typeof ageInput === 'string') {
9248
+ // Parse age ranges like "30-40", "35+", "mid-30s"
9249
+ const rangeMatch = ageInput.match(/(\d+)-(\d+)/);
9250
+ if (rangeMatch) {
9251
+ const avg = Math.floor((parseInt(rangeMatch[1]) + parseInt(rangeMatch[2])) / 2);
9252
+ return avg;
9253
+ }
9254
+
9255
+ const numberMatch = ageInput.match(/\d+/);
9256
+ if (numberMatch) {
9257
+ const age = parseInt(numberMatch[0]);
9258
+ if (age >= 18 && age <= 100) return age;
9259
+ }
9260
+
9261
+ // Handle textual age descriptions
9262
+ const text = ageInput.toLowerCase();
9263
+ if (text.includes('twent') || text.includes('20')) return 25;
9264
+ if (text.includes('thirt') || text.includes('30')) return 35;
9265
+ if (text.includes('fort') || text.includes('40')) return 45;
9266
+ if (text.includes('fift') || text.includes('50')) return 55;
9267
+ if (text.includes('sixt') || text.includes('60')) return 65;
9268
+ }
9269
+
9270
+ return 'unknown';
9271
+ }
9272
+
9273
+ function calculateAgeRange(age) {
9274
+ if (age === 'unknown' || typeof age !== 'number') return 'Unknown';
9275
+
9276
+ if (age < 25) return '18-24';
9277
+ if (age < 30) return '25-29';
9278
+ if (age < 35) return '30-34';
9279
+ if (age < 40) return '35-39';
9280
+ if (age < 50) return '40-49';
9281
+ if (age < 60) return '50-59';
9282
+ return '60+';
9283
+ }
9284
+
9285
+ function processNationality(nationality) {
9286
+ if (!nationality || nationality.toLowerCase() === 'unknown' || nationality.toLowerCase() === 'not available') {
9287
+ return 'unknown';
9288
+ }
9289
+
9290
+ // Convert to uppercase for country codes
9291
+ const upper = nationality.toUpperCase();
9292
+
9293
+ // Map common variations to ISO codes
9294
+ const countryMap = {
9295
+ USA: 'US',
9296
+ 'UNITED STATES': 'US',
9297
+ AMERICA: 'US',
9298
+ UK: 'GB',
9299
+ 'UNITED KINGDOM': 'GB',
9300
+ ENGLAND: 'GB',
9301
+ SCOTLAND: 'GB',
9302
+ CANADA: 'CA',
9303
+ AUSTRALIA: 'AU',
9304
+ INDIA: 'IN',
9305
+ GERMANY: 'DE',
9306
+ FRANCE: 'FR',
9307
+ CHINA: 'CN',
9308
+ JAPAN: 'JP',
9309
+ BRAZIL: 'BR',
9310
+ MEXICO: 'MX',
9311
+ SPAIN: 'ES',
9312
+ ITALY: 'IT',
9313
+ NETHERLANDS: 'NL',
9314
+ SWEDEN: 'SE',
9315
+ SWITZERLAND: 'CH',
9316
+ 'SOUTH KOREA': 'KR',
9317
+ RUSSIA: 'RU',
9318
+ };
9319
+
9320
+ if (countryMap[upper]) {
9321
+ return countryMap[upper];
9322
+ }
9323
+
9324
+ // If it's already a 2-letter code, return it
9325
+ if (/^[A-Z]{2}$/.test(upper)) {
9326
+ return upper;
9327
+ }
9328
+
9329
+ return 'unknown';
9330
+ }
9331
+
9332
+ function processCountryCode(country) {
9333
+ if (!country || country.toLowerCase() === 'not available') {
9334
+ return 'unknown';
9335
+ }
9336
+
9337
+ return processNationality(country);
9338
+ }
9339
+
9340
+ function countryCodeToName(code) {
9341
+ if (!code || code === 'unknown') return 'Unknown';
9342
+
9343
+ const countryNames = {
9344
+ US: 'United States',
9345
+ GB: 'United Kingdom',
9346
+ CA: 'Canada',
9347
+ AU: 'Australia',
9348
+ DE: 'Germany',
9349
+ FR: 'France',
9350
+ JP: 'Japan',
9351
+ CN: 'China',
9352
+ IN: 'India',
9353
+ BR: 'Brazil',
9354
+ MX: 'Mexico',
9355
+ ES: 'Spain',
9356
+ IT: 'Italy',
9357
+ NL: 'Netherlands',
9358
+ SE: 'Sweden',
9359
+ CH: 'Switzerland',
9360
+ KR: 'South Korea',
9361
+ RU: 'Russia',
9362
+ };
9363
+
9364
+ return countryNames[code] || code;
9365
+ }
9366
+
9367
+ function getPronounsFromGender(gender) {
9368
+ switch (gender) {
9369
+ case 'male':
9370
+ return ['he/him', 'his'];
9371
+ case 'female':
9372
+ return ['she/her', 'hers'];
9373
+ case 'non-binary':
9374
+ return ['they/them', 'theirs'];
9375
+ default:
9376
+ return ['unknown'];
9377
+ }
9378
+ }
9379
+
9380
+ function extractContextInsights(emailContext) {
9381
+ if (!emailContext) return [];
9382
+
9383
+ const insights = [];
9384
+ const context = emailContext.toLowerCase();
9385
+
9386
+ // Extract potential job role hints
9387
+ const rolePatterns = {
9388
+ manager: ['manage', 'supervise', 'team lead', 'department head'],
9389
+ engineer: ['engineer', 'developer', 'programmer', 'software'],
9390
+ sales: ['sales', 'account executive', 'business development'],
9391
+ marketing: ['marketing', 'campaign', 'brand', 'social media'],
9392
+ executive: ['ceo', 'cto', 'cfo', 'director', 'vp', 'vice president'],
9393
+ };
9394
+
9395
+ for (const [role, patterns] of Object.entries(rolePatterns)) {
9396
+ if (patterns.some((pattern) => context.includes(pattern))) {
9397
+ insights.push(`Possible ${role} role indicated in email`);
9398
+ }
9399
+ }
9400
+
9401
+ // Extract company hints
9402
+ const companyMatch = context.match(/(?:at|from|of)\s+([A-Z][A-Za-z0-9\s&]+)(?:\s|$)/);
9403
+ if (companyMatch && companyMatch[1].length > 2) {
9404
+ insights.push(`Mentioned company/organization: ${companyMatch[1].trim()}`);
9405
+ }
9406
+
9407
+ // Extract project/technology hints
9408
+ const techMatch = context.match(/(?:using|with|built\s+in)\s+([A-Za-z0-9\s+#]+)(?:\s|$)/);
9409
+ if (techMatch) {
9410
+ insights.push(`Technology mentioned: ${techMatch[1].trim()}`);
9411
+ }
9412
+
9413
+ // Extract urgency/priority
9414
+ if (context.includes('urgent') || context.includes('asap') || context.includes('immediately')) {
9415
+ insights.push('Email suggests urgency or time sensitivity');
9416
+ }
9417
+
9418
+ // Extract tone
9419
+ if (context.includes('thank you') || context.includes('appreciate') || context.includes('grateful')) {
9420
+ insights.push('Email shows appreciative/grateful tone');
9421
+ }
9422
+
9423
+ return insights.slice(0, 5); // Limit to 5 insights
9424
+ }
9425
+
9426
+ // Existing helper functions (kept from previous version)
9427
+ function validateURL(url) {
9428
+ /* ... same as before ... */
9429
+ }
9430
+ function validatePhoneNumber(phone) {
9431
+ /* ... same as before ... */
9432
+ }
9433
+ function cleanText(text) {
9434
+ /* ... same as before ... */
9435
+ }
9436
+ function cleanStateProvince(state) {
9437
+ /* ... same as before ... */
9438
+ }
9439
+ function ensureArray(value) {
9440
+ /* ... same as before ... */
9441
+ }
9442
+ function getInitials(firstName, lastName) {
9443
+ /* ... same as before ... */
9444
+ }
9445
+ function calculateSeniorityScore(data) {
9446
+ /* ... same as before ... */
9447
+ }
9448
+
9449
+ function getDefaultPersonInfo(name, email, emailContext = '', error = null) {
9450
+ const nameParts = name.trim().split(/\s+/);
9451
+ const firstName = nameParts[0] || '';
9452
+ const lastName = nameParts.slice(1).join(' ') || '';
9453
+ const domain = email.includes('@') ? email.split('@')[1] : '';
9454
+ const companyGuess = domain
9455
+ .replace(/\..*$/, '')
9456
+ .replace(/[^a-z]/gi, ' ')
9457
+ .replace(/\b\w/g, (l) => l.toUpperCase());
9458
+
9459
+ // Extract basic insights from email context
9460
+ const contextInsights = emailContext ? extractContextInsights(emailContext) : [];
9461
+
9462
+ return {
9463
+ // Basic Information
9464
+ person_full_name: name,
9465
+ person_first_name: firstName,
9466
+ person_last_name: lastName,
9467
+ person_middle_name: '',
9468
+ person_preferred_name: '',
9469
+
9470
+ // Demographic Information
9471
+ person_age: 'unknown',
9472
+ person_gender: 'unknown',
9473
+ person_nationality: 'unknown',
9474
+ person_age_range: 'Unknown',
9475
+ person_pronouns: ['unknown'],
9476
+
9477
+ // Professional Information
9478
+ person_title: 'Professional',
9479
+ person_company: companyGuess || 'Unknown',
9480
+ person_industry: 'Technology',
9481
+ person_bio: 'Information not available',
9482
+
9483
+ // Location
9484
+ person_location_city: 'Not available',
9485
+ person_location_state: 'Not available',
9486
+ person_location_country: 'unknown',
9487
+ person_location_full: 'Not available',
9488
+
9489
+ // Contact Information
9490
+ person_phone: 'Not available',
9491
+ person_email: email,
9492
+ person_email_alternate: '',
9493
+
9494
+ // Education
9495
+ person_education: [],
9496
+ person_education_highest: 'Not available',
9497
+
9498
+ // Professional Details
9499
+ person_skills: [],
9500
+ person_expertise: [],
9501
+ person_career_level: 'unknown',
9502
+ person_years_experience: 0,
9503
+
9504
+ // Social & Online Presence
9505
+ person_linkedin: 'Not available',
9506
+ person_twitter: 'Not available',
9507
+ person_github: 'Not available',
9508
+ person_website: 'Not available',
9509
+ person_social_other: [],
9510
+
9511
+ // Additional Information
9512
+ person_languages: ['English'],
9513
+ person_interests: [],
9514
+ person_achievements: [],
9515
+ person_current_projects: [],
9516
+ person_context_insights: contextInsights,
9517
+
9518
+ // Metadata
9519
+ person_available_for_opportunities: false,
9520
+ person_last_updated: new Date().toISOString(),
9521
+ person_source_confidence: 0,
9522
+ person_has_context_hints: contextInsights.length > 0,
9523
+
9524
+ // Processed fields
9525
+ person_name_initials: getInitials(firstName, lastName),
9526
+ person_seniority_score: 0,
9527
+
9528
+ // Structured objects
9529
+ person_demographics: {
9530
+ age: 'unknown',
9531
+ age_range: 'Unknown',
9532
+ gender: 'unknown',
9533
+ pronouns: ['unknown'],
9534
+ nationality: 'unknown',
9535
+ nationality_name: 'Unknown',
9536
+ },
9537
+
9538
+ person_contact: {
9539
+ email: email,
9540
+ phone: 'Not available',
9541
+ location: {
9542
+ city: 'Not available',
9543
+ state: 'Not available',
9544
+ country: 'unknown',
9545
+ country_name: 'Unknown',
9546
+ full: 'Not available',
9547
+ },
9548
+ },
9549
+
9550
+ person_professional: {
9551
+ title: 'Professional',
9552
+ company: companyGuess || 'Unknown',
9553
+ industry: 'Technology',
9554
+ career_level: 'unknown',
9555
+ years_experience: 0,
9556
+ skills: [],
9557
+ expertise: [],
9558
+ education: [],
9559
+ },
9560
+
9561
+ person_online: {
9562
+ linkedin: 'Not available',
9563
+ twitter: 'Not available',
9564
+ github: 'Not available',
9565
+ website: 'Not available',
9566
+ other: [],
9567
+ },
9568
+
9569
+ context_analysis: {
9570
+ has_context: !!emailContext,
9571
+ context_length: emailContext?.length || 0,
9572
+ extracted_insights: contextInsights,
9573
+ context_useful: contextInsights.length > 0,
9574
+ },
9575
+
9576
+ retrieved_at: new Date().toISOString(),
9577
+ error: error || 'API call failed',
9578
+ };
9579
+ }
9580
+
9581
+ // Build context-aware prompt
9582
+ let context_prompt = `Research the person: "${name}" (${email}).`;
9583
+
9584
+ if (email_context) {
9585
+ context_prompt += `\n\nEMAIL CONTEXT PROVIDED:\n"${email_context.substring(0, 500)}${email_context.length > 500 ? '...' : ''}"`;
9586
+ }
9587
+
9588
+ context_prompt += `
9589
+
9590
+ REQUIRED PERSON INFORMATION:
9591
+ 1. Full name (first, middle, last)
9592
+ 2. Professional title/role
9593
+ 3. Company/organization they work for
9594
+ 4. Industry/field they work in
9595
+ 5. Location (city, state, country)
9596
+ 6. Professional biography (1-2 sentences)
9597
+ 7. Education background
9598
+ 8. Professional skills/expertise
9599
+ 9. Social media profiles (LinkedIn, Twitter, GitHub, etc.)
9600
+ 10. Personal website or portfolio
9601
+
9602
+ DEMOGRAPHIC INFORMATION:
9603
+ 11. Estimated age (based on career, education, and public information)
9604
+ 12. Gender (based on name, pronouns in content, or public profiles)
9605
+ 13. Nationality/country of origin (if discernible)
9606
+
9607
+ CONTACT INFORMATION:
9608
+ 14. Phone number (if available)
9609
+ 15. Alternate email addresses (if available)
9610
+ 16. Physical address (if available and appropriate)
9611
+
9612
+ ADDITIONAL INSIGHTS:
9613
+ 17. Career level (entry, mid, senior, executive, etc.)
9614
+ 18. Years of experience
9615
+ 19. Notable achievements/awards
9616
+ 20. Languages spoken
9617
+ 21. Professional interests
9618
+ 22. Current projects
9619
+ 23. Availability for opportunities
9620
+
9621
+ INSTRUCTIONS:
9622
+ - Use web search to find accurate, public information
9623
+ - Use email context provided to infer additional details
9624
+ - Respect privacy - only include publicly available information
9625
+ - For age: provide numeric estimate or range (e.g., 35, 30-40, unknown)
9626
+ - For gender: use male/female/non-binary/unknown based on available cues
9627
+ - For country: use 2-letter ISO code (e.g., US, IN, UK)
9628
+ - If information is not available, use "Not available" or appropriate defaults
9629
+ - For location, use standard formats (e.g., "San Francisco, CA, USA")
9630
+ - For social media, provide full URLs when available
9631
+ - Focus on professional information suitable for business networking
9632
+
9633
+ Be thorough but respectful of privacy boundaries.`;
9634
+
9635
+ const person_info_ret = await submit_chat_gpt_prompt({
9636
+ uid,
9637
+ prompt: context_prompt,
9638
+ model: 'gpt-4o-mini',
9639
+ response_format: z.object({
9640
+ // Basic Information
9641
+ person_full_name: z.string().describe("Person's full name"),
9642
+ person_first_name: z.string().describe('First name'),
9643
+ person_last_name: z.string().describe('Last name'),
9644
+ person_middle_name: z.string().optional().describe('Middle name if available'),
9645
+ person_preferred_name: z.string().optional().describe('Preferred/nickname if different'),
9646
+
9647
+ // Demographic Information
9648
+ person_age: z.union([z.number().int().min(18).max(100), z.string().describe("Age range or 'unknown'")]).describe('Estimated age or range'),
9649
+ person_gender: z.enum(['male', 'female', 'non-binary', 'unknown']).describe('Gender based on available information'),
9650
+ person_nationality: z.string().describe('Country of origin/nationality (2-letter ISO code)'),
9651
+
9652
+ // Professional Information
9653
+ person_title: z.string().describe('Professional title/role'),
9654
+ person_company: z.string().describe('Current company/organization'),
9655
+ person_industry: z.string().describe('Primary industry/field'),
9656
+ person_bio: z.string().describe('Professional biography (1-2 sentences)'),
9657
+
9658
+ // Location
9659
+ person_location_city: z.string().describe('City of residence/work'),
9660
+ person_location_state: z.string().describe('State/province'),
9661
+ person_location_country: z.string().describe('Country (2-letter ISO code)'),
9662
+ person_location_full: z.string().describe('Full location string'),
9663
+
9664
+ // Contact Information
9665
+ person_phone: z.string().describe("Phone number or 'Not available'"),
9666
+ person_email: z.string().describe('Primary email address'),
9667
+ person_email_alternate: z.string().optional().describe('Alternate email if available'),
9668
+
9669
+ // Education
9670
+ person_education: z.array(z.string()).describe('Array of educational institutions/degrees'),
9671
+ person_education_highest: z.string().describe('Highest degree obtained'),
9672
+
9673
+ // Professional Details
9674
+ person_skills: z.array(z.string()).describe('Array of professional skills'),
9675
+ person_expertise: z.array(z.string()).describe('Areas of expertise'),
9676
+ person_career_level: z.enum(['entry', 'mid', 'senior', 'lead', 'manager', 'director', 'executive', 'founder', 'unknown']),
9677
+ person_years_experience: z.number().int().min(0).max(60).describe('Years of professional experience'),
9678
+
9679
+ // Social & Online Presence
9680
+ person_linkedin: z.string().describe("LinkedIn profile URL or 'Not available'"),
9681
+ person_twitter: z.string().describe("Twitter/X profile URL or 'Not available'"),
9682
+ person_github: z.string().describe("GitHub profile URL or 'Not available'"),
9683
+ person_website: z.string().describe("Personal website/portfolio or 'Not available'"),
9684
+ person_social_other: z.array(z.string()).optional().describe('Other social media profiles'),
9685
+
9686
+ // Additional Information
9687
+ person_languages: z.array(z.string()).describe('Languages spoken'),
9688
+ person_interests: z.array(z.string()).describe('Professional/personal interests'),
9689
+ person_achievements: z.array(z.string()).optional().describe('Notable achievements/awards'),
9690
+ person_current_projects: z.array(z.string()).optional().describe('Current projects'),
9691
+
9692
+ // Context Extracted Information
9693
+ person_context_insights: z.array(z.string()).optional().describe('Insights extracted from provided email context'),
9694
+
9695
+ // Metadata
9696
+ person_available_for_opportunities: z.boolean().describe('Open to new opportunities'),
9697
+ person_last_updated: z.string().describe('Date information was last verified'),
9698
+ person_source_confidence: z.number().min(0).max(100).describe('Confidence score for information accuracy'),
9699
+ person_has_context_hints: z.boolean().describe('Whether email context provided useful hints'),
9700
+ }),
9701
+ metadata: {
9702
+ func: 'get_person_info',
9703
+ person_name: name,
9704
+ person_email: email,
9705
+ has_email_context: !!email_context,
9706
+ email_context_length: email_context?.length || 0,
9707
+ context_hash: email_context ? await hashString(email_context.substring(0, 200)) : null,
9708
+ },
9709
+ account_profile_info,
9710
+ tools: [{ type: 'web_search_preview' }],
9711
+ });
9712
+
9713
+ try {
9714
+ if (person_info_ret.code > -1) {
9715
+ const data = typeof person_info_ret.data === 'string' ? JSON.parse(person_info_ret.data) : person_info_ret.data;
9716
+
9717
+ // Process and enhance the data
9718
+ const processedData = processPersonInfo(data, name, email, email_context);
9719
+
9720
+ return processedData;
9721
+ } else {
9722
+ console.error('Person info API call failed:', person_info_ret);
9723
+ return getDefaultPersonInfo(name, email, email_context);
9724
+ }
9725
+ } catch (error) {
9726
+ console.error('Error in get_person_info:', error);
9727
+ return getDefaultPersonInfo(name, email, email_context, error.message);
9728
+ }
9729
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xuda.io/ai_module",
3
- "version": "1.1.4674",
3
+ "version": "1.1.4676",
4
4
  "description": "Xuda AI Module",
5
5
  "main": "index.mjs",
6
6
  "type": "module",