@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.
- package/index.mjs +669 -36
- 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
|
+
};
|