@xuda.io/ai_module 1.1.4673 → 1.1.4675

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 +1259 -131
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -7061,141 +7061,143 @@ const get_image_blob_from_downloaded_image = async function (url) {
7061
7061
  return images[0];
7062
7062
  };
7063
7063
 
7064
- export const get_business_info = async function (uid, name, email, account_profile_info) {
7065
- // Extract lists from your JSON categories array
7066
- const categories = [
7067
- {
7068
- id: 'ind_technology',
7069
- name: 'Technology',
7070
- sub: ['Software Development', 'AI & Machine Learning', 'Cybersecurity', 'Cloud Computing', 'IT Services', 'SaaS & Platforms', 'Hardware & Electronics'],
7071
- },
7072
- {
7073
- id: 'ind_marketing',
7074
- name: 'Marketing & Advertising',
7075
- sub: ['Digital Marketing', 'PPC & SEO', 'Content Creation', 'Branding', 'Social Media Management', 'Email Marketing', 'Market Research'],
7076
- },
7077
- {
7078
- id: 'ind_sales',
7079
- name: 'Sales',
7080
- sub: ['B2B Sales', 'B2C Sales', 'Real Estate Sales', 'Account Management', 'Lead Generation', 'Inside Sales', 'Field Sales'],
7081
- },
7082
- {
7083
- id: 'ind_finance',
7084
- name: 'Finance & Accounting',
7085
- sub: ['Banking', 'Investment', 'Financial Planning', 'Accounting', 'Bookkeeping', 'Insurance', 'FinTech'],
7086
- },
7087
- {
7088
- id: 'ind_healthcare',
7089
- name: 'Healthcare',
7090
- sub: ['Medical Clinics', 'Hospitals', 'Pharma', 'Mental Health', 'Healthcare IT', 'Home Care', 'Medical Research'],
7091
- },
7092
- {
7093
- id: 'ind_realestate',
7094
- name: 'Real Estate',
7095
- sub: ['Residential Real Estate', 'Commercial Real Estate', 'Property Management', 'Investment & BRRRR', 'Construction', 'Architecture', 'Interior Design'],
7096
- },
7097
- {
7098
- id: 'ind_ecommerce',
7099
- name: 'E-commerce',
7100
- sub: ['Online Store', 'Dropshipping', 'Amazon FBA', 'Marketplace Selling', 'Retail', 'Inventory Management', 'Fulfillment & Logistics'],
7101
- },
7102
- {
7103
- id: 'ind_education',
7104
- name: 'Education',
7105
- sub: ['Schools', 'Universities', 'Private Tutoring', 'Online Courses', 'EdTech', 'Training & Development'],
7106
- },
7107
- {
7108
- id: 'ind_hospitality',
7109
- name: 'Hospitality & Travel',
7110
- sub: ['Hotels', 'Restaurants', 'Cafes & Bars', 'Travel Agencies', 'Tourism', 'Events & Catering', 'Transportation'],
7111
- },
7112
- {
7113
- id: 'ind_professional',
7114
- name: 'Professional Services',
7115
- sub: ['Legal', 'Consulting', 'HR & Recruiting', 'Accounting Firms', 'Business Services', 'Management Consulting'],
7116
- },
7117
- {
7118
- id: 'ind_manufacturing',
7119
- name: 'Manufacturing',
7120
- sub: ['Automotive', 'Industrial', 'Textile', 'Electronics', 'Food Production', 'Machinery', 'Chemicals'],
7121
- },
7122
- {
7123
- id: 'ind_nonprofit',
7124
- name: 'Nonprofit & Government',
7125
- sub: ['NGOs', 'Municipality', 'Government Services', 'Public Health', 'Community Organizations', 'Education Programs'],
7126
- },
7127
- {
7128
- id: 'ind_media',
7129
- name: 'Media & Entertainment',
7130
- sub: ['Film & Video', 'Music', 'Publishing', 'Gaming', 'News & Journalism', 'Influencers', 'Production Studios'],
7131
- },
7132
- {
7133
- id: 'ind_logistics',
7134
- name: 'Logistics & Transportation',
7135
- sub: ['Shipping', 'Trucking', 'Last-Mile Delivery', 'Warehousing', 'Freight Forwarding', 'Route Optimization'],
7136
- },
7137
- {
7138
- id: 'ind_energy',
7139
- name: 'Energy & Utilities',
7140
- sub: ['Solar', 'Oil & Gas', 'Electricity Providers', 'Water Utilities', 'Environmental Services', 'Renewable Energy'],
7141
- },
7142
- {
7143
- id: 'ind_construction',
7144
- name: 'Construction & Trades',
7145
- sub: ['General Contracting', 'Roofing', 'Plumbing', 'HVAC', 'Electrical', 'Renovation', 'Engineering'],
7146
- },
7147
- {
7148
- id: 'ind_food',
7149
- name: 'Food & Beverage',
7150
- sub: ['Restaurants', 'Catering', 'Food Manufacturing', 'Bakeries', 'Bars & Nightlife', 'Food Delivery'],
7151
- },
7152
- {
7153
- id: 'ind_retail',
7154
- name: 'Retail',
7155
- sub: ['Physical Stores', 'Boutiques', 'Supermarkets', 'Consumer Goods', 'Fashion Retail', 'Home Goods'],
7156
- },
7157
- ];
7158
- const categoryNames = categories.map((c) => c.name);
7159
- const allSubCategories = categories.flatMap((c) => c.sub);
7064
+ // export const get_business_info = async function (uid, name, email, account_profile_info) {
7065
+ // // Extract lists from your JSON categories array
7066
+ // const categories = [
7067
+ // {
7068
+ // id: 'ind_technology',
7069
+ // name: 'Technology',
7070
+ // sub: ['Software Development', 'AI & Machine Learning', 'Cybersecurity', 'Cloud Computing', 'IT Services', 'SaaS & Platforms', 'Hardware & Electronics'],
7071
+ // },
7072
+ // {
7073
+ // id: 'ind_marketing',
7074
+ // name: 'Marketing & Advertising',
7075
+ // sub: ['Digital Marketing', 'PPC & SEO', 'Content Creation', 'Branding', 'Social Media Management', 'Email Marketing', 'Market Research'],
7076
+ // },
7077
+ // {
7078
+ // id: 'ind_sales',
7079
+ // name: 'Sales',
7080
+ // sub: ['B2B Sales', 'B2C Sales', 'Real Estate Sales', 'Account Management', 'Lead Generation', 'Inside Sales', 'Field Sales'],
7081
+ // },
7082
+ // {
7083
+ // id: 'ind_finance',
7084
+ // name: 'Finance & Accounting',
7085
+ // sub: ['Banking', 'Investment', 'Financial Planning', 'Accounting', 'Bookkeeping', 'Insurance', 'FinTech'],
7086
+ // },
7087
+ // {
7088
+ // id: 'ind_healthcare',
7089
+ // name: 'Healthcare',
7090
+ // sub: ['Medical Clinics', 'Hospitals', 'Pharma', 'Mental Health', 'Healthcare IT', 'Home Care', 'Medical Research'],
7091
+ // },
7092
+ // {
7093
+ // id: 'ind_realestate',
7094
+ // name: 'Real Estate',
7095
+ // sub: ['Residential Real Estate', 'Commercial Real Estate', 'Property Management', 'Investment & BRRRR', 'Construction', 'Architecture', 'Interior Design'],
7096
+ // },
7097
+ // {
7098
+ // id: 'ind_ecommerce',
7099
+ // name: 'E-commerce',
7100
+ // sub: ['Online Store', 'Dropshipping', 'Amazon FBA', 'Marketplace Selling', 'Retail', 'Inventory Management', 'Fulfillment & Logistics'],
7101
+ // },
7102
+ // {
7103
+ // id: 'ind_education',
7104
+ // name: 'Education',
7105
+ // sub: ['Schools', 'Universities', 'Private Tutoring', 'Online Courses', 'EdTech', 'Training & Development'],
7106
+ // },
7107
+ // {
7108
+ // id: 'ind_hospitality',
7109
+ // name: 'Hospitality & Travel',
7110
+ // sub: ['Hotels', 'Restaurants', 'Cafes & Bars', 'Travel Agencies', 'Tourism', 'Events & Catering', 'Transportation'],
7111
+ // },
7112
+ // {
7113
+ // id: 'ind_professional',
7114
+ // name: 'Professional Services',
7115
+ // sub: ['Legal', 'Consulting', 'HR & Recruiting', 'Accounting Firms', 'Business Services', 'Management Consulting'],
7116
+ // },
7117
+ // {
7118
+ // id: 'ind_manufacturing',
7119
+ // name: 'Manufacturing',
7120
+ // sub: ['Automotive', 'Industrial', 'Textile', 'Electronics', 'Food Production', 'Machinery', 'Chemicals'],
7121
+ // },
7122
+ // {
7123
+ // id: 'ind_nonprofit',
7124
+ // name: 'Nonprofit & Government',
7125
+ // sub: ['NGOs', 'Municipality', 'Government Services', 'Public Health', 'Community Organizations', 'Education Programs'],
7126
+ // },
7127
+ // {
7128
+ // id: 'ind_media',
7129
+ // name: 'Media & Entertainment',
7130
+ // sub: ['Film & Video', 'Music', 'Publishing', 'Gaming', 'News & Journalism', 'Influencers', 'Production Studios'],
7131
+ // },
7132
+ // {
7133
+ // id: 'ind_logistics',
7134
+ // name: 'Logistics & Transportation',
7135
+ // sub: ['Shipping', 'Trucking', 'Last-Mile Delivery', 'Warehousing', 'Freight Forwarding', 'Route Optimization'],
7136
+ // },
7137
+ // {
7138
+ // id: 'ind_energy',
7139
+ // name: 'Energy & Utilities',
7140
+ // sub: ['Solar', 'Oil & Gas', 'Electricity Providers', 'Water Utilities', 'Environmental Services', 'Renewable Energy'],
7141
+ // },
7142
+ // {
7143
+ // id: 'ind_construction',
7144
+ // name: 'Construction & Trades',
7145
+ // sub: ['General Contracting', 'Roofing', 'Plumbing', 'HVAC', 'Electrical', 'Renovation', 'Engineering'],
7146
+ // },
7147
+ // {
7148
+ // id: 'ind_food',
7149
+ // name: 'Food & Beverage',
7150
+ // sub: ['Restaurants', 'Catering', 'Food Manufacturing', 'Bakeries', 'Bars & Nightlife', 'Food Delivery'],
7151
+ // },
7152
+ // {
7153
+ // id: 'ind_retail',
7154
+ // name: 'Retail',
7155
+ // sub: ['Physical Stores', 'Boutiques', 'Supermarkets', 'Consumer Goods', 'Fashion Retail', 'Home Goods'],
7156
+ // },
7157
+ // ];
7158
+ // const categoryNames = categories.map((c) => c.name);
7159
+ // const allSubCategories = categories.flatMap((c) => c.sub);
7160
+
7161
+ // const business_info_ret = await submit_chat_gpt_prompt({
7162
+ // uid,
7163
+ // prompt: `Research the company: "${name}" (${email}).
7164
+ // 1. get the company name.
7165
+ // 2. get the domain name from the email.
7166
+ // 2. Provide a professional one-sentence biography of the company.
7167
+ // 3. Categorize them using the provided industry lists.
7168
+ // 4. Identify their primary country as a 2-letter ISO code (e.g., 'US').`,
7169
+ // model: 'gpt-5-nano',
7170
+ // response_format: z.object({
7171
+ // business_name: z.string().describe('the company name'),
7172
+ // business_domain: z.string().describe('the email domain name'),
7173
+ // business_bio: z.string().describe('A concise, professional one-sentence summary of what the business does'),
7174
+ // business_size: z.enum(['unknown', 'small', 'medium', 'large', 'enterprise']),
7175
+ // business_category: z.enum(categoryNames),
7176
+ // business_sub_category: z.enum(allSubCategories),
7177
+ // business_country: z.string().length(2).describe("2-letter ISO country code (e.g., 'US')"),
7178
+ // }),
7179
+ // metadata: { func: 'get_business_info' },
7180
+ // account_profile_info,
7181
+ // tools: [{ type: 'web_search_preview' }],
7182
+ // });
7160
7183
 
7161
- const business_info_ret = await submit_chat_gpt_prompt({
7162
- uid,
7163
- prompt: `Research the company: "${name}" (${email}).
7164
- 1. get the company name.
7165
- 2. Provide a professional one-sentence biography of the company.
7166
- 3. Categorize them using the provided industry lists.
7167
- 4. Identify their primary country as a 2-letter ISO code (e.g., 'US').`,
7168
- model: 'gpt-5-nano',
7169
- response_format: z.object({
7170
- business_name: z.string().describe('the company name'),
7171
- business_bio: z.string().describe('A concise, professional one-sentence summary of what the business does'),
7172
- business_size: z.enum(['unknown', 'small', 'medium', 'large', 'enterprise']),
7173
- business_category: z.enum(categoryNames),
7174
- business_sub_category: z.enum(allSubCategories),
7175
- business_country: z.string().length(2).describe("2-letter ISO country code (e.g., 'US')"),
7176
- }),
7177
- metadata: { func: 'get_business_info' },
7178
- account_profile_info,
7179
- tools: [{ type: 'web_search_preview' }],
7180
- });
7181
-
7182
- try {
7183
- if (business_info_ret.code > -1) {
7184
- const data = typeof business_info_ret.data === 'string' ? JSON.parse(business_info_ret.data) : business_info_ret.data;
7184
+ // try {
7185
+ // if (business_info_ret.code > -1) {
7186
+ // const data = typeof business_info_ret.data === 'string' ? JSON.parse(business_info_ret.data) : business_info_ret.data;
7185
7187
 
7186
- // Standardize country to uppercase
7187
- if (data.business_country) data.business_country = data.business_country.toUpperCase();
7188
+ // // Standardize country to uppercase
7189
+ // if (data.business_country) data.business_country = data.business_country.toUpperCase();
7188
7190
 
7189
- // Logic to attach the ID for your database
7190
- const matched = categories.find((c) => c.name === data.business_category);
7191
- data.business_category_id = matched ? matched.id : null;
7191
+ // // Logic to attach the ID for your database
7192
+ // const matched = categories.find((c) => c.name === data.business_category);
7193
+ // data.business_category_id = matched ? matched.id : null;
7192
7194
 
7193
- return data;
7194
- }
7195
- } catch (error) {
7196
- console.error('Error in get_business_info:', error);
7197
- }
7198
- };
7195
+ // return data;
7196
+ // }
7197
+ // } catch (error) {
7198
+ // console.error('Error in get_business_info:', error);
7199
+ // }
7200
+ // };
7199
7201
 
7200
7202
  const get_chat_picture_cache = async function (uid, docType, key) {
7201
7203
  if (!key || typeof key !== 'string') return null;
@@ -8598,3 +8600,1129 @@ Category:`;
8598
8600
  };
8599
8601
  }
8600
8602
  };
8603
+
8604
+ export const get_business_info = async function (uid, name, email, account_profile_info) {
8605
+ // ========== HELPER FUNCTIONS ==========
8606
+
8607
+ function validatePhoneNumber(phone) {
8608
+ if (!phone || phone.toLowerCase() === 'not available' || phone.trim() === '') {
8609
+ return 'Not available';
8610
+ }
8611
+
8612
+ const cleaned = phone.replace(/[^\d\+\(\)\-\s]/g, '');
8613
+ const digitCount = (cleaned.match(/\d/g) || []).length;
8614
+
8615
+ // Require at least 7 digits for a valid phone number
8616
+ if (digitCount < 7) {
8617
+ return 'Not available';
8618
+ }
8619
+
8620
+ return cleaned.trim();
8621
+ }
8622
+
8623
+ function validateWebsite(website) {
8624
+ if (!website || website.toLowerCase() === 'not available' || website.trim() === '') {
8625
+ return 'Not available';
8626
+ }
8627
+
8628
+ const trimmed = website.trim();
8629
+
8630
+ // Ensure URL has proper protocol
8631
+ if (!trimmed.startsWith('http://') && !trimmed.startsWith('https://')) {
8632
+ // Check if it looks like a domain
8633
+ if (trimmed.includes('.') && !trimmed.includes(' ')) {
8634
+ return `https://${trimmed.replace(/^www\./, '')}`;
8635
+ }
8636
+ return 'Not available';
8637
+ }
8638
+
8639
+ return trimmed;
8640
+ }
8641
+
8642
+ function cleanStreetAddress(street) {
8643
+ if (!street || street.toLowerCase() === 'not available' || street.trim() === '') {
8644
+ return 'Not available';
8645
+ }
8646
+
8647
+ return street
8648
+ .replace(/\s+/g, ' ')
8649
+ .replace(/,\s*$/g, '') // Remove trailing commas
8650
+ .trim();
8651
+ }
8652
+
8653
+ function cleanText(text) {
8654
+ if (!text || text.toLowerCase() === 'not available' || text.trim() === '') {
8655
+ return 'Not available';
8656
+ }
8657
+
8658
+ return text.replace(/\s+/g, ' ').trim();
8659
+ }
8660
+
8661
+ function cleanZipCode(zip, country) {
8662
+ if (!zip || zip.toLowerCase() === 'not available' || zip.trim() === '') {
8663
+ return 'Not available';
8664
+ }
8665
+
8666
+ const cleaned = zip.trim();
8667
+
8668
+ // Country-specific formatting
8669
+ switch (country) {
8670
+ case 'US':
8671
+ // US ZIP: 5 digits or 5-4 format
8672
+ return cleaned.replace(/\D/g, '').replace(/^(\d{5})(\d{4})$/, '$1-$2');
8673
+ case 'CA':
8674
+ // Canada: A1A 1A1 format
8675
+ return cleaned.toUpperCase().replace(/\s+/g, '');
8676
+ case 'UK':
8677
+ // UK: various formats, preserve as is but uppercase
8678
+ return cleaned.toUpperCase();
8679
+ case 'AU':
8680
+ // Australia: 4 digits
8681
+ return cleaned.replace(/\D/g, '');
8682
+ default:
8683
+ return cleaned;
8684
+ }
8685
+ }
8686
+
8687
+ function cleanStateProvince(state, country) {
8688
+ if (!state || state.toLowerCase() === 'not available' || state.trim() === '') {
8689
+ return 'Not available';
8690
+ }
8691
+
8692
+ const cleaned = state.trim();
8693
+
8694
+ // Standardize US state abbreviations
8695
+ if (country === 'US') {
8696
+ const stateMap = {
8697
+ alabama: 'AL',
8698
+ alaska: 'AK',
8699
+ arizona: 'AZ',
8700
+ arkansas: 'AR',
8701
+ california: 'CA',
8702
+ colorado: 'CO',
8703
+ connecticut: 'CT',
8704
+ delaware: 'DE',
8705
+ florida: 'FL',
8706
+ georgia: 'GA',
8707
+ hawaii: 'HI',
8708
+ idaho: 'ID',
8709
+ illinois: 'IL',
8710
+ indiana: 'IN',
8711
+ iowa: 'IA',
8712
+ kansas: 'KS',
8713
+ kentucky: 'KY',
8714
+ louisiana: 'LA',
8715
+ maine: 'ME',
8716
+ maryland: 'MD',
8717
+ massachusetts: 'MA',
8718
+ michigan: 'MI',
8719
+ minnesota: 'MN',
8720
+ mississippi: 'MS',
8721
+ missouri: 'MO',
8722
+ montana: 'MT',
8723
+ nebraska: 'NE',
8724
+ nevada: 'NV',
8725
+ 'new hampshire': 'NH',
8726
+ 'new jersey': 'NJ',
8727
+ 'new mexico': 'NM',
8728
+ 'new york': 'NY',
8729
+ 'north carolina': 'NC',
8730
+ 'north dakota': 'ND',
8731
+ ohio: 'OH',
8732
+ oklahoma: 'OK',
8733
+ oregon: 'OR',
8734
+ pennsylvania: 'PA',
8735
+ 'rhode island': 'RI',
8736
+ 'south carolina': 'SC',
8737
+ 'south dakota': 'SD',
8738
+ tennessee: 'TN',
8739
+ texas: 'TX',
8740
+ utah: 'UT',
8741
+ vermont: 'VT',
8742
+ virginia: 'VA',
8743
+ washington: 'WA',
8744
+ 'west virginia': 'WV',
8745
+ wisconsin: 'WI',
8746
+ wyoming: 'WY',
8747
+ 'district of columbia': 'DC',
8748
+ };
8749
+
8750
+ const lowerState = cleaned.toLowerCase();
8751
+ if (stateMap[lowerState]) {
8752
+ return stateMap[lowerState];
8753
+ }
8754
+
8755
+ // If already 2-letter code, uppercase it
8756
+ if (/^[A-Z]{2}$/i.test(cleaned)) {
8757
+ return cleaned.toUpperCase();
8758
+ }
8759
+ }
8760
+
8761
+ return cleaned;
8762
+ }
8763
+
8764
+ function cleanCountry(country) {
8765
+ if (!country || country.toLowerCase() === 'not available' || country.trim() === '') {
8766
+ return 'Not available';
8767
+ }
8768
+
8769
+ // Convert 2-letter codes to full country names for display
8770
+ const countryMap = {
8771
+ US: 'United States',
8772
+ CA: 'Canada',
8773
+ UK: 'United Kingdom',
8774
+ GB: 'United Kingdom',
8775
+ AU: 'Australia',
8776
+ DE: 'Germany',
8777
+ FR: 'France',
8778
+ JP: 'Japan',
8779
+ CN: 'China',
8780
+ IN: 'India',
8781
+ BR: 'Brazil',
8782
+ MX: 'Mexico',
8783
+ };
8784
+
8785
+ const upperCountry = country.toUpperCase();
8786
+ return countryMap[upperCountry] || upperCountry;
8787
+ }
8788
+
8789
+ function formatFullAddress(data) {
8790
+ const parts = [];
8791
+
8792
+ if (data.business_address_street && data.business_address_street !== 'Not available') {
8793
+ parts.push(data.business_address_street);
8794
+ }
8795
+
8796
+ const cityStateZip = [];
8797
+ if (data.business_address_city && data.business_address_city !== 'Not available') {
8798
+ cityStateZip.push(data.business_address_city);
8799
+ }
8800
+
8801
+ if (data.business_address_state && data.business_address_state !== 'Not available') {
8802
+ cityStateZip.push(data.business_address_state);
8803
+ }
8804
+
8805
+ if (data.business_address_zip && data.business_address_zip !== 'Not available') {
8806
+ cityStateZip.push(data.business_address_zip);
8807
+ }
8808
+
8809
+ if (cityStateZip.length > 0) {
8810
+ parts.push(cityStateZip.join(', '));
8811
+ }
8812
+
8813
+ if (data.business_address_country && data.business_address_country !== 'Not available') {
8814
+ parts.push(data.business_address_country);
8815
+ }
8816
+
8817
+ return parts.length > 0 ? parts.join(', ') : 'Not available';
8818
+ }
8819
+
8820
+ function getRegionFromState(state, country) {
8821
+ if (country !== 'US' || state === 'Not available') return null;
8822
+
8823
+ const regions = {
8824
+ Northeast: ['CT', 'ME', 'MA', 'NH', 'RI', 'VT', 'NJ', 'NY', 'PA'],
8825
+ Midwest: ['IL', 'IN', 'MI', 'OH', 'WI', 'IA', 'KS', 'MN', 'MO', 'NE', 'ND', 'SD'],
8826
+ South: ['DE', 'FL', 'GA', 'MD', 'NC', 'SC', 'VA', 'DC', 'WV', 'AL', 'KY', 'MS', 'TN', 'AR', 'LA', 'OK', 'TX'],
8827
+ West: ['AZ', 'CO', 'ID', 'MT', 'NV', 'NM', 'UT', 'WY', 'AK', 'CA', 'HI', 'OR', 'WA'],
8828
+ };
8829
+
8830
+ for (const [region, states] of Object.entries(regions)) {
8831
+ if (states.includes(state)) {
8832
+ return region;
8833
+ }
8834
+ }
8835
+
8836
+ return null;
8837
+ }
8838
+
8839
+ function getDefaultBusinessInfo(name, email, error = null) {
8840
+ const domain = email.includes('@') ? email.split('@')[1] : '';
8841
+
8842
+ return {
8843
+ business_name: name,
8844
+ business_domain: domain,
8845
+ business_bio: 'Information not available',
8846
+ business_size: 'unknown',
8847
+ business_category: 'Professional Services',
8848
+ business_sub_category: 'Business Services',
8849
+ business_country: 'US',
8850
+ business_phone: 'Not available',
8851
+ business_website: 'Not available',
8852
+
8853
+ // Address components
8854
+ business_address_street: 'Not available',
8855
+ business_address_city: 'Not available',
8856
+ business_address_state: 'Not available',
8857
+ business_address_zip: 'Not available',
8858
+ business_address_country: 'Not available',
8859
+ business_address_full: 'Not available',
8860
+
8861
+ // Structured objects
8862
+ business_category_id: null,
8863
+ business_address: {
8864
+ street: 'Not available',
8865
+ city: 'Not available',
8866
+ state: 'Not available',
8867
+ zip: 'Not available',
8868
+ country: 'Not available',
8869
+ full: 'Not available',
8870
+ coordinates: null,
8871
+ timezone: null,
8872
+ },
8873
+ business_location: {
8874
+ region: null,
8875
+ metro_area: null,
8876
+ is_headquarters: null,
8877
+ },
8878
+ error: error || 'API call failed',
8879
+ };
8880
+ }
8881
+
8882
+ // Extract lists from your JSON categories array
8883
+ const categories = [
8884
+ {
8885
+ id: 'ind_technology',
8886
+ name: 'Technology',
8887
+ sub: ['Software Development', 'AI & Machine Learning', 'Cybersecurity', 'Cloud Computing', 'IT Services', 'SaaS & Platforms', 'Hardware & Electronics'],
8888
+ },
8889
+ {
8890
+ id: 'ind_marketing',
8891
+ name: 'Marketing & Advertising',
8892
+ sub: ['Digital Marketing', 'PPC & SEO', 'Content Creation', 'Branding', 'Social Media Management', 'Email Marketing', 'Market Research'],
8893
+ },
8894
+ {
8895
+ id: 'ind_sales',
8896
+ name: 'Sales',
8897
+ sub: ['B2B Sales', 'B2C Sales', 'Real Estate Sales', 'Account Management', 'Lead Generation', 'Inside Sales', 'Field Sales'],
8898
+ },
8899
+ {
8900
+ id: 'ind_finance',
8901
+ name: 'Finance & Accounting',
8902
+ sub: ['Banking', 'Investment', 'Financial Planning', 'Accounting', 'Bookkeeping', 'Insurance', 'FinTech'],
8903
+ },
8904
+ {
8905
+ id: 'ind_healthcare',
8906
+ name: 'Healthcare',
8907
+ sub: ['Medical Clinics', 'Hospitals', 'Pharma', 'Mental Health', 'Healthcare IT', 'Home Care', 'Medical Research'],
8908
+ },
8909
+ {
8910
+ id: 'ind_realestate',
8911
+ name: 'Real Estate',
8912
+ sub: ['Residential Real Estate', 'Commercial Real Estate', 'Property Management', 'Investment & BRRRR', 'Construction', 'Architecture', 'Interior Design'],
8913
+ },
8914
+ {
8915
+ id: 'ind_ecommerce',
8916
+ name: 'E-commerce',
8917
+ sub: ['Online Store', 'Dropshipping', 'Amazon FBA', 'Marketplace Selling', 'Retail', 'Inventory Management', 'Fulfillment & Logistics'],
8918
+ },
8919
+ {
8920
+ id: 'ind_education',
8921
+ name: 'Education',
8922
+ sub: ['Schools', 'Universities', 'Private Tutoring', 'Online Courses', 'EdTech', 'Training & Development'],
8923
+ },
8924
+ {
8925
+ id: 'ind_hospitality',
8926
+ name: 'Hospitality & Travel',
8927
+ sub: ['Hotels', 'Restaurants', 'Cafes & Bars', 'Travel Agencies', 'Tourism', 'Events & Catering', 'Transportation'],
8928
+ },
8929
+ {
8930
+ id: 'ind_professional',
8931
+ name: 'Professional Services',
8932
+ sub: ['Legal', 'Consulting', 'HR & Recruiting', 'Accounting Firms', 'Business Services', 'Management Consulting'],
8933
+ },
8934
+ {
8935
+ id: 'ind_manufacturing',
8936
+ name: 'Manufacturing',
8937
+ sub: ['Automotive', 'Industrial', 'Textile', 'Electronics', 'Food Production', 'Machinery', 'Chemicals'],
8938
+ },
8939
+ {
8940
+ id: 'ind_nonprofit',
8941
+ name: 'Nonprofit & Government',
8942
+ sub: ['NGOs', 'Municipality', 'Government Services', 'Public Health', 'Community Organizations', 'Education Programs'],
8943
+ },
8944
+ {
8945
+ id: 'ind_media',
8946
+ name: 'Media & Entertainment',
8947
+ sub: ['Film & Video', 'Music', 'Publishing', 'Gaming', 'News & Journalism', 'Influencers', 'Production Studios'],
8948
+ },
8949
+ {
8950
+ id: 'ind_logistics',
8951
+ name: 'Logistics & Transportation',
8952
+ sub: ['Shipping', 'Trucking', 'Last-Mile Delivery', 'Warehousing', 'Freight Forwarding', 'Route Optimization'],
8953
+ },
8954
+ {
8955
+ id: 'ind_energy',
8956
+ name: 'Energy & Utilities',
8957
+ sub: ['Solar', 'Oil & Gas', 'Electricity Providers', 'Water Utilities', 'Environmental Services', 'Renewable Energy'],
8958
+ },
8959
+ {
8960
+ id: 'ind_construction',
8961
+ name: 'Construction & Trades',
8962
+ sub: ['General Contracting', 'Roofing', 'Plumbing', 'HVAC', 'Electrical', 'Renovation', 'Engineering'],
8963
+ },
8964
+ {
8965
+ id: 'ind_food',
8966
+ name: 'Food & Beverage',
8967
+ sub: ['Restaurants', 'Catering', 'Food Manufacturing', 'Bakeries', 'Bars & Nightlife', 'Food Delivery'],
8968
+ },
8969
+ {
8970
+ id: 'ind_retail',
8971
+ name: 'Retail',
8972
+ sub: ['Physical Stores', 'Boutiques', 'Supermarkets', 'Consumer Goods', 'Fashion Retail', 'Home Goods'],
8973
+ },
8974
+ ];
8975
+ const categoryNames = categories.map((c) => c.name);
8976
+ const allSubCategories = categories.flatMap((c) => c.sub);
8977
+
8978
+ const business_info_ret = await submit_chat_gpt_prompt({
8979
+ uid,
8980
+ prompt: `Research the company: "${name}" (${email}).
8981
+
8982
+ REQUIRED INFORMATION:
8983
+ 1. Company name (official name)
8984
+ 2. Email domain name
8985
+ 3. Professional one-sentence biography
8986
+ 4. Industry category and sub-category
8987
+ 5. Primary country (2-letter ISO code)
8988
+ 6. Telephone number
8989
+ 7. Website URL
8990
+ 8. COMPLETE ADDRESS DETAILS:
8991
+ - Full street address (street number, street name, suite/apt if applicable)
8992
+ - City
8993
+ - State/Province (use abbreviation where standard)
8994
+ - Zip/Postal code
8995
+ - Country
8996
+
8997
+ ADDRESS FORMATTING EXAMPLES:
8998
+ - Full street: "1600 Amphitheatre Parkway"
8999
+ - City: "Mountain View"
9000
+ - State: "CA"
9001
+ - Zip: "94043"
9002
+ - Country: "US"
9003
+ - Full address: "1600 Amphitheatre Parkway, Mountain View, CA 94043, USA"
9004
+
9005
+ INSTRUCTIONS:
9006
+ - Use web search to find accurate information
9007
+ - If not found online, infer from typical patterns
9008
+ - For unknown info, use "Not available"
9009
+ - For US states, use 2-letter abbreviations (CA, NY, TX, etc.)
9010
+ - For other countries, use standard regional names
9011
+
9012
+ Be as precise and accurate as possible.`,
9013
+ model: 'gpt-4o-mini',
9014
+ response_format: z.object({
9015
+ business_name: z.string().describe('Official company name'),
9016
+ business_domain: z.string().describe('Domain name from email'),
9017
+ business_bio: z.string().describe('One-sentence professional summary'),
9018
+ business_size: z.enum(['unknown', 'small', 'medium', 'large', 'enterprise']),
9019
+ business_category: z.enum(categoryNames),
9020
+ business_sub_category: z.enum(allSubCategories),
9021
+ business_country: z.string().length(2).describe('2-letter ISO country code'),
9022
+ business_phone: z.string().describe("Telephone number or 'Not available'"),
9023
+ business_website: z.string().describe("Website URL or 'Not available'"),
9024
+
9025
+ // Complete address components
9026
+ business_address_street: z.string().describe('Full street address (number, street, suite)'),
9027
+ business_address_city: z.string().describe('City name'),
9028
+ business_address_state: z.string().describe('State/Province (abbreviation if standard)'),
9029
+ business_address_zip: z.string().describe('Zip/Postal code'),
9030
+ business_address_country: z.string().describe('Country name'),
9031
+ business_address_full: z.string().describe('Complete formatted address'),
9032
+ }),
9033
+ metadata: {
9034
+ func: 'get_business_info',
9035
+ company_name: name,
9036
+ company_email: email,
9037
+ },
9038
+ account_profile_info,
9039
+ tools: [{ type: 'web_search_preview' }],
9040
+ });
9041
+
9042
+ try {
9043
+ if (business_info_ret.code > -1) {
9044
+ const data = typeof business_info_ret.data === 'string' ? JSON.parse(business_info_ret.data) : business_info_ret.data;
9045
+
9046
+ // Standardize country codes to uppercase
9047
+ if (data.business_country) data.business_country = data.business_country.toUpperCase();
9048
+ if (data.business_address_country) {
9049
+ data.business_address_country = data.business_address_country.toUpperCase();
9050
+ }
9051
+
9052
+ // Logic to attach the ID for your database
9053
+ const matched = categories.find((c) => c.name === data.business_category);
9054
+ data.business_category_id = matched ? matched.id : null;
9055
+
9056
+ // Validate and clean all contact information
9057
+ data.business_phone = validatePhoneNumber(data.business_phone);
9058
+ data.business_website = validateWebsite(data.business_website);
9059
+
9060
+ // Clean and standardize all address components
9061
+ data.business_address_street = cleanStreetAddress(data.business_address_street);
9062
+ data.business_address_city = cleanText(data.business_address_city);
9063
+ data.business_address_zip = cleanZipCode(data.business_address_zip, data.business_country);
9064
+ data.business_address_state = cleanStateProvince(data.business_address_state, data.business_country);
9065
+ data.business_address_country = cleanCountry(data.business_address_country || data.business_country);
9066
+ data.business_address_full = formatFullAddress(data);
9067
+
9068
+ // Create a comprehensive structured address object
9069
+ data.business_address = {
9070
+ street: data.business_address_street,
9071
+ city: data.business_address_city,
9072
+ state: data.business_address_state,
9073
+ zip: data.business_address_zip,
9074
+ country: data.business_address_country,
9075
+ full: data.business_address_full,
9076
+ coordinates: null, // Can be populated later with geocoding
9077
+ timezone: null, // Can be populated later
9078
+ };
9079
+
9080
+ // Generate additional useful fields
9081
+ data.business_location = {
9082
+ region: getRegionFromState(data.business_address_state, data.business_country),
9083
+ metro_area: null, // Can be populated later
9084
+ is_headquarters: null, // Can be determined from address pattern
9085
+ };
9086
+
9087
+ return data;
9088
+ } else {
9089
+ console.error('Business info API call failed:', business_info_ret);
9090
+ return getDefaultBusinessInfo(name, email);
9091
+ }
9092
+ } catch (error) {
9093
+ console.error('Error in get_business_info:', error);
9094
+ return getDefaultBusinessInfo(name, email, error.message);
9095
+ }
9096
+ };
9097
+
9098
+ export const get_person_info = async function (uid, name, email, account_profile_info, email_context = '') {
9099
+ // ========== HELPER FUNCTIONS ==========
9100
+
9101
+ async function hashString(str) {
9102
+ // Simple hash for context tracking
9103
+ const encoder = new TextEncoder();
9104
+ const data = encoder.encode(str);
9105
+ const hashBuffer = await crypto.subtle.digest('SHA-256', data);
9106
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
9107
+ return hashArray
9108
+ .map((b) => b.toString(16).padStart(2, '0'))
9109
+ .join('')
9110
+ .substring(0, 16);
9111
+ }
9112
+
9113
+ function processPersonInfo(data, originalName, originalEmail, emailContext) {
9114
+ // Ensure basic fields are populated
9115
+ if (!data.person_first_name || !data.person_last_name) {
9116
+ const nameParts = originalName.trim().split(/\s+/);
9117
+ data.person_first_name = data.person_first_name || nameParts[0] || '';
9118
+ data.person_last_name = data.person_last_name || nameParts.slice(1).join(' ') || '';
9119
+ }
9120
+
9121
+ // Set full name if not provided
9122
+ if (!data.person_full_name) {
9123
+ data.person_full_name = `${data.person_first_name} ${data.person_last_name}`.trim();
9124
+ }
9125
+
9126
+ // Ensure email is set
9127
+ data.person_email = data.person_email || originalEmail;
9128
+
9129
+ // Process demographic information
9130
+ data.person_age = processAge(data.person_age);
9131
+ data.person_gender = data.person_gender || 'unknown';
9132
+ data.person_nationality = processNationality(data.person_nationality);
9133
+
9134
+ // Standardize location fields
9135
+ data.person_location_city = cleanText(data.person_location_city);
9136
+ data.person_location_state = cleanStateProvince(data.person_location_state);
9137
+ data.person_location_country = processCountryCode(data.person_location_country);
9138
+
9139
+ // Format full location
9140
+ if (!data.person_location_full || data.person_location_full === 'Not available') {
9141
+ const locationParts = [];
9142
+ if (data.person_location_city && data.person_location_city !== 'Not available') {
9143
+ locationParts.push(data.person_location_city);
9144
+ }
9145
+ if (data.person_location_state && data.person_location_state !== 'Not available') {
9146
+ locationParts.push(data.person_location_state);
9147
+ }
9148
+ if (data.person_location_country && data.person_location_country !== 'Not available') {
9149
+ locationParts.push(countryCodeToName(data.person_location_country));
9150
+ }
9151
+ data.person_location_full = locationParts.length > 0 ? locationParts.join(', ') : 'Not available';
9152
+ }
9153
+
9154
+ // Clean and validate URLs
9155
+ data.person_linkedin = validateURL(data.person_linkedin);
9156
+ data.person_twitter = validateURL(data.person_twitter);
9157
+ data.person_github = validateURL(data.person_github);
9158
+ data.person_website = validateURL(data.person_website);
9159
+
9160
+ // Clean phone number
9161
+ data.person_phone = validatePhoneNumber(data.person_phone);
9162
+
9163
+ // Ensure arrays are properly formatted
9164
+ data.person_skills = ensureArray(data.person_skills);
9165
+ data.person_expertise = ensureArray(data.person_expertise);
9166
+ data.person_education = ensureArray(data.person_education);
9167
+ data.person_languages = ensureArray(data.person_languages);
9168
+ data.person_interests = ensureArray(data.person_interests);
9169
+ data.person_context_insights = ensureArray(data.person_context_insights);
9170
+
9171
+ // Calculate additional metadata
9172
+ data.person_name_initials = getInitials(data.person_first_name, data.person_last_name);
9173
+ data.person_age_range = calculateAgeRange(data.person_age);
9174
+ data.person_seniority_score = calculateSeniorityScore(data);
9175
+ data.person_pronouns = getPronounsFromGender(data.person_gender);
9176
+
9177
+ // Extract insights from email context
9178
+ if (emailContext && !data.person_context_insights) {
9179
+ data.person_context_insights = extractContextInsights(emailContext);
9180
+ }
9181
+
9182
+ // Create structured objects for easier access
9183
+ data.person_demographics = {
9184
+ age: data.person_age,
9185
+ age_range: data.person_age_range,
9186
+ gender: data.person_gender,
9187
+ pronouns: data.person_pronouns,
9188
+ nationality: data.person_nationality,
9189
+ nationality_name: countryCodeToName(data.person_nationality),
9190
+ };
9191
+
9192
+ data.person_contact = {
9193
+ email: data.person_email,
9194
+ phone: data.person_phone,
9195
+ location: {
9196
+ city: data.person_location_city,
9197
+ state: data.person_location_state,
9198
+ country: data.person_location_country,
9199
+ country_name: countryCodeToName(data.person_location_country),
9200
+ full: data.person_location_full,
9201
+ },
9202
+ };
9203
+
9204
+ data.person_professional = {
9205
+ title: data.person_title,
9206
+ company: data.person_company,
9207
+ industry: data.person_industry,
9208
+ career_level: data.person_career_level,
9209
+ years_experience: data.person_years_experience,
9210
+ skills: data.person_skills,
9211
+ expertise: data.person_expertise,
9212
+ education: data.person_education,
9213
+ };
9214
+
9215
+ data.person_online = {
9216
+ linkedin: data.person_linkedin,
9217
+ twitter: data.person_twitter,
9218
+ github: data.person_github,
9219
+ website: data.person_website,
9220
+ other: data.person_social_other || [],
9221
+ };
9222
+
9223
+ // Add context metadata
9224
+ data.context_analysis = {
9225
+ has_context: !!emailContext,
9226
+ context_length: emailContext?.length || 0,
9227
+ extracted_insights: data.person_context_insights || [],
9228
+ context_useful: data.person_has_context_hints || false,
9229
+ };
9230
+
9231
+ // Add timestamp
9232
+ data.retrieved_at = new Date().toISOString();
9233
+
9234
+ return data;
9235
+ }
9236
+
9237
+ function processAge(ageInput) {
9238
+ if (!ageInput || ageInput === 'unknown' || ageInput === 'Not available') {
9239
+ return 'unknown';
9240
+ }
9241
+
9242
+ if (typeof ageInput === 'number') {
9243
+ return Math.max(18, Math.min(100, ageInput));
9244
+ }
9245
+
9246
+ if (typeof ageInput === 'string') {
9247
+ // Parse age ranges like "30-40", "35+", "mid-30s"
9248
+ const rangeMatch = ageInput.match(/(\d+)-(\d+)/);
9249
+ if (rangeMatch) {
9250
+ const avg = Math.floor((parseInt(rangeMatch[1]) + parseInt(rangeMatch[2])) / 2);
9251
+ return avg;
9252
+ }
9253
+
9254
+ const numberMatch = ageInput.match(/\d+/);
9255
+ if (numberMatch) {
9256
+ const age = parseInt(numberMatch[0]);
9257
+ if (age >= 18 && age <= 100) return age;
9258
+ }
9259
+
9260
+ // Handle textual age descriptions
9261
+ const text = ageInput.toLowerCase();
9262
+ if (text.includes('twent') || text.includes('20')) return 25;
9263
+ if (text.includes('thirt') || text.includes('30')) return 35;
9264
+ if (text.includes('fort') || text.includes('40')) return 45;
9265
+ if (text.includes('fift') || text.includes('50')) return 55;
9266
+ if (text.includes('sixt') || text.includes('60')) return 65;
9267
+ }
9268
+
9269
+ return 'unknown';
9270
+ }
9271
+
9272
+ function calculateAgeRange(age) {
9273
+ if (age === 'unknown' || typeof age !== 'number') return 'Unknown';
9274
+
9275
+ if (age < 25) return '18-24';
9276
+ if (age < 30) return '25-29';
9277
+ if (age < 35) return '30-34';
9278
+ if (age < 40) return '35-39';
9279
+ if (age < 50) return '40-49';
9280
+ if (age < 60) return '50-59';
9281
+ return '60+';
9282
+ }
9283
+
9284
+ function processNationality(nationality) {
9285
+ if (!nationality || nationality.toLowerCase() === 'unknown' || nationality.toLowerCase() === 'not available') {
9286
+ return 'unknown';
9287
+ }
9288
+
9289
+ // Convert to uppercase for country codes
9290
+ const upper = nationality.toUpperCase();
9291
+
9292
+ // Map common variations to ISO codes
9293
+ const countryMap = {
9294
+ USA: 'US',
9295
+ 'UNITED STATES': 'US',
9296
+ AMERICA: 'US',
9297
+ UK: 'GB',
9298
+ 'UNITED KINGDOM': 'GB',
9299
+ ENGLAND: 'GB',
9300
+ SCOTLAND: 'GB',
9301
+ CANADA: 'CA',
9302
+ AUSTRALIA: 'AU',
9303
+ INDIA: 'IN',
9304
+ GERMANY: 'DE',
9305
+ FRANCE: 'FR',
9306
+ CHINA: 'CN',
9307
+ JAPAN: 'JP',
9308
+ BRAZIL: 'BR',
9309
+ MEXICO: 'MX',
9310
+ SPAIN: 'ES',
9311
+ ITALY: 'IT',
9312
+ NETHERLANDS: 'NL',
9313
+ SWEDEN: 'SE',
9314
+ SWITZERLAND: 'CH',
9315
+ 'SOUTH KOREA': 'KR',
9316
+ RUSSIA: 'RU',
9317
+ };
9318
+
9319
+ if (countryMap[upper]) {
9320
+ return countryMap[upper];
9321
+ }
9322
+
9323
+ // If it's already a 2-letter code, return it
9324
+ if (/^[A-Z]{2}$/.test(upper)) {
9325
+ return upper;
9326
+ }
9327
+
9328
+ return 'unknown';
9329
+ }
9330
+
9331
+ function processCountryCode(country) {
9332
+ if (!country || country.toLowerCase() === 'not available') {
9333
+ return 'unknown';
9334
+ }
9335
+
9336
+ return processNationality(country);
9337
+ }
9338
+
9339
+ function countryCodeToName(code) {
9340
+ if (!code || code === 'unknown') return 'Unknown';
9341
+
9342
+ const countryNames = {
9343
+ US: 'United States',
9344
+ GB: 'United Kingdom',
9345
+ CA: 'Canada',
9346
+ AU: 'Australia',
9347
+ DE: 'Germany',
9348
+ FR: 'France',
9349
+ JP: 'Japan',
9350
+ CN: 'China',
9351
+ IN: 'India',
9352
+ BR: 'Brazil',
9353
+ MX: 'Mexico',
9354
+ ES: 'Spain',
9355
+ IT: 'Italy',
9356
+ NL: 'Netherlands',
9357
+ SE: 'Sweden',
9358
+ CH: 'Switzerland',
9359
+ KR: 'South Korea',
9360
+ RU: 'Russia',
9361
+ };
9362
+
9363
+ return countryNames[code] || code;
9364
+ }
9365
+
9366
+ function getPronounsFromGender(gender) {
9367
+ switch (gender) {
9368
+ case 'male':
9369
+ return ['he/him', 'his'];
9370
+ case 'female':
9371
+ return ['she/her', 'hers'];
9372
+ case 'non-binary':
9373
+ return ['they/them', 'theirs'];
9374
+ default:
9375
+ return ['unknown'];
9376
+ }
9377
+ }
9378
+
9379
+ function extractContextInsights(emailContext) {
9380
+ if (!emailContext) return [];
9381
+
9382
+ const insights = [];
9383
+ const context = emailContext.toLowerCase();
9384
+
9385
+ // Extract potential job role hints
9386
+ const rolePatterns = {
9387
+ manager: ['manage', 'supervise', 'team lead', 'department head'],
9388
+ engineer: ['engineer', 'developer', 'programmer', 'software'],
9389
+ sales: ['sales', 'account executive', 'business development'],
9390
+ marketing: ['marketing', 'campaign', 'brand', 'social media'],
9391
+ executive: ['ceo', 'cto', 'cfo', 'director', 'vp', 'vice president'],
9392
+ };
9393
+
9394
+ for (const [role, patterns] of Object.entries(rolePatterns)) {
9395
+ if (patterns.some((pattern) => context.includes(pattern))) {
9396
+ insights.push(`Possible ${role} role indicated in email`);
9397
+ }
9398
+ }
9399
+
9400
+ // Extract company hints
9401
+ const companyMatch = context.match(/(?:at|from|of)\s+([A-Z][A-Za-z0-9\s&]+)(?:\s|$)/);
9402
+ if (companyMatch && companyMatch[1].length > 2) {
9403
+ insights.push(`Mentioned company/organization: ${companyMatch[1].trim()}`);
9404
+ }
9405
+
9406
+ // Extract project/technology hints
9407
+ const techMatch = context.match(/(?:using|with|built\s+in)\s+([A-Za-z0-9\s+#]+)(?:\s|$)/);
9408
+ if (techMatch) {
9409
+ insights.push(`Technology mentioned: ${techMatch[1].trim()}`);
9410
+ }
9411
+
9412
+ // Extract urgency/priority
9413
+ if (context.includes('urgent') || context.includes('asap') || context.includes('immediately')) {
9414
+ insights.push('Email suggests urgency or time sensitivity');
9415
+ }
9416
+
9417
+ // Extract tone
9418
+ if (context.includes('thank you') || context.includes('appreciate') || context.includes('grateful')) {
9419
+ insights.push('Email shows appreciative/grateful tone');
9420
+ }
9421
+
9422
+ return insights.slice(0, 5); // Limit to 5 insights
9423
+ }
9424
+
9425
+ // Existing helper functions (kept from previous version)
9426
+ function validateURL(url) {
9427
+ /* ... same as before ... */
9428
+ }
9429
+ function validatePhoneNumber(phone) {
9430
+ /* ... same as before ... */
9431
+ }
9432
+ function cleanText(text) {
9433
+ /* ... same as before ... */
9434
+ }
9435
+ function cleanStateProvince(state) {
9436
+ /* ... same as before ... */
9437
+ }
9438
+ function ensureArray(value) {
9439
+ /* ... same as before ... */
9440
+ }
9441
+ function getInitials(firstName, lastName) {
9442
+ /* ... same as before ... */
9443
+ }
9444
+ function calculateSeniorityScore(data) {
9445
+ /* ... same as before ... */
9446
+ }
9447
+
9448
+ function getDefaultPersonInfo(name, email, emailContext = '', error = null) {
9449
+ const nameParts = name.trim().split(/\s+/);
9450
+ const firstName = nameParts[0] || '';
9451
+ const lastName = nameParts.slice(1).join(' ') || '';
9452
+ const domain = email.includes('@') ? email.split('@')[1] : '';
9453
+ const companyGuess = domain
9454
+ .replace(/\..*$/, '')
9455
+ .replace(/[^a-z]/gi, ' ')
9456
+ .replace(/\b\w/g, (l) => l.toUpperCase());
9457
+
9458
+ // Extract basic insights from email context
9459
+ const contextInsights = emailContext ? extractContextInsights(emailContext) : [];
9460
+
9461
+ return {
9462
+ // Basic Information
9463
+ person_full_name: name,
9464
+ person_first_name: firstName,
9465
+ person_last_name: lastName,
9466
+ person_middle_name: '',
9467
+ person_preferred_name: '',
9468
+
9469
+ // Demographic Information
9470
+ person_age: 'unknown',
9471
+ person_gender: 'unknown',
9472
+ person_nationality: 'unknown',
9473
+ person_age_range: 'Unknown',
9474
+ person_pronouns: ['unknown'],
9475
+
9476
+ // Professional Information
9477
+ person_title: 'Professional',
9478
+ person_company: companyGuess || 'Unknown',
9479
+ person_industry: 'Technology',
9480
+ person_bio: 'Information not available',
9481
+
9482
+ // Location
9483
+ person_location_city: 'Not available',
9484
+ person_location_state: 'Not available',
9485
+ person_location_country: 'unknown',
9486
+ person_location_full: 'Not available',
9487
+
9488
+ // Contact Information
9489
+ person_phone: 'Not available',
9490
+ person_email: email,
9491
+ person_email_alternate: '',
9492
+
9493
+ // Education
9494
+ person_education: [],
9495
+ person_education_highest: 'Not available',
9496
+
9497
+ // Professional Details
9498
+ person_skills: [],
9499
+ person_expertise: [],
9500
+ person_career_level: 'unknown',
9501
+ person_years_experience: 0,
9502
+
9503
+ // Social & Online Presence
9504
+ person_linkedin: 'Not available',
9505
+ person_twitter: 'Not available',
9506
+ person_github: 'Not available',
9507
+ person_website: 'Not available',
9508
+ person_social_other: [],
9509
+
9510
+ // Additional Information
9511
+ person_languages: ['English'],
9512
+ person_interests: [],
9513
+ person_achievements: [],
9514
+ person_current_projects: [],
9515
+ person_context_insights: contextInsights,
9516
+
9517
+ // Metadata
9518
+ person_available_for_opportunities: false,
9519
+ person_last_updated: new Date().toISOString(),
9520
+ person_source_confidence: 0,
9521
+ person_has_context_hints: contextInsights.length > 0,
9522
+
9523
+ // Processed fields
9524
+ person_name_initials: getInitials(firstName, lastName),
9525
+ person_seniority_score: 0,
9526
+
9527
+ // Structured objects
9528
+ person_demographics: {
9529
+ age: 'unknown',
9530
+ age_range: 'Unknown',
9531
+ gender: 'unknown',
9532
+ pronouns: ['unknown'],
9533
+ nationality: 'unknown',
9534
+ nationality_name: 'Unknown',
9535
+ },
9536
+
9537
+ person_contact: {
9538
+ email: email,
9539
+ phone: 'Not available',
9540
+ location: {
9541
+ city: 'Not available',
9542
+ state: 'Not available',
9543
+ country: 'unknown',
9544
+ country_name: 'Unknown',
9545
+ full: 'Not available',
9546
+ },
9547
+ },
9548
+
9549
+ person_professional: {
9550
+ title: 'Professional',
9551
+ company: companyGuess || 'Unknown',
9552
+ industry: 'Technology',
9553
+ career_level: 'unknown',
9554
+ years_experience: 0,
9555
+ skills: [],
9556
+ expertise: [],
9557
+ education: [],
9558
+ },
9559
+
9560
+ person_online: {
9561
+ linkedin: 'Not available',
9562
+ twitter: 'Not available',
9563
+ github: 'Not available',
9564
+ website: 'Not available',
9565
+ other: [],
9566
+ },
9567
+
9568
+ context_analysis: {
9569
+ has_context: !!emailContext,
9570
+ context_length: emailContext?.length || 0,
9571
+ extracted_insights: contextInsights,
9572
+ context_useful: contextInsights.length > 0,
9573
+ },
9574
+
9575
+ retrieved_at: new Date().toISOString(),
9576
+ error: error || 'API call failed',
9577
+ };
9578
+ }
9579
+
9580
+ // Build context-aware prompt
9581
+ let context_prompt = `Research the person: "${name}" (${email}).`;
9582
+
9583
+ if (email_context) {
9584
+ context_prompt += `\n\nEMAIL CONTEXT PROVIDED:\n"${email_context.substring(0, 500)}${email_context.length > 500 ? '...' : ''}"`;
9585
+ }
9586
+
9587
+ context_prompt += `
9588
+
9589
+ REQUIRED PERSON INFORMATION:
9590
+ 1. Full name (first, middle, last)
9591
+ 2. Professional title/role
9592
+ 3. Company/organization they work for
9593
+ 4. Industry/field they work in
9594
+ 5. Location (city, state, country)
9595
+ 6. Professional biography (1-2 sentences)
9596
+ 7. Education background
9597
+ 8. Professional skills/expertise
9598
+ 9. Social media profiles (LinkedIn, Twitter, GitHub, etc.)
9599
+ 10. Personal website or portfolio
9600
+
9601
+ DEMOGRAPHIC INFORMATION:
9602
+ 11. Estimated age (based on career, education, and public information)
9603
+ 12. Gender (based on name, pronouns in content, or public profiles)
9604
+ 13. Nationality/country of origin (if discernible)
9605
+
9606
+ CONTACT INFORMATION:
9607
+ 14. Phone number (if available)
9608
+ 15. Alternate email addresses (if available)
9609
+ 16. Physical address (if available and appropriate)
9610
+
9611
+ ADDITIONAL INSIGHTS:
9612
+ 17. Career level (entry, mid, senior, executive, etc.)
9613
+ 18. Years of experience
9614
+ 19. Notable achievements/awards
9615
+ 20. Languages spoken
9616
+ 21. Professional interests
9617
+ 22. Current projects
9618
+ 23. Availability for opportunities
9619
+
9620
+ INSTRUCTIONS:
9621
+ - Use web search to find accurate, public information
9622
+ - Use email context provided to infer additional details
9623
+ - Respect privacy - only include publicly available information
9624
+ - For age: provide numeric estimate or range (e.g., 35, 30-40, unknown)
9625
+ - For gender: use male/female/non-binary/unknown based on available cues
9626
+ - For country: use 2-letter ISO code (e.g., US, IN, UK)
9627
+ - If information is not available, use "Not available" or appropriate defaults
9628
+ - For location, use standard formats (e.g., "San Francisco, CA, USA")
9629
+ - For social media, provide full URLs when available
9630
+ - Focus on professional information suitable for business networking
9631
+
9632
+ Be thorough but respectful of privacy boundaries.`;
9633
+
9634
+ const person_info_ret = await submit_chat_gpt_prompt({
9635
+ uid,
9636
+ prompt: context_prompt,
9637
+ model: 'gpt-4o-mini',
9638
+ response_format: z.object({
9639
+ // Basic Information
9640
+ person_full_name: z.string().describe("Person's full name"),
9641
+ person_first_name: z.string().describe('First name'),
9642
+ person_last_name: z.string().describe('Last name'),
9643
+ person_middle_name: z.string().optional().describe('Middle name if available'),
9644
+ person_preferred_name: z.string().optional().describe('Preferred/nickname if different'),
9645
+
9646
+ // Demographic Information
9647
+ person_age: z.union([z.number().int().min(18).max(100), z.string().describe("Age range or 'unknown'")]).describe('Estimated age or range'),
9648
+ person_gender: z.enum(['male', 'female', 'non-binary', 'unknown']).describe('Gender based on available information'),
9649
+ person_nationality: z.string().describe('Country of origin/nationality (2-letter ISO code)'),
9650
+
9651
+ // Professional Information
9652
+ person_title: z.string().describe('Professional title/role'),
9653
+ person_company: z.string().describe('Current company/organization'),
9654
+ person_industry: z.string().describe('Primary industry/field'),
9655
+ person_bio: z.string().describe('Professional biography (1-2 sentences)'),
9656
+
9657
+ // Location
9658
+ person_location_city: z.string().describe('City of residence/work'),
9659
+ person_location_state: z.string().describe('State/province'),
9660
+ person_location_country: z.string().describe('Country (2-letter ISO code)'),
9661
+ person_location_full: z.string().describe('Full location string'),
9662
+
9663
+ // Contact Information
9664
+ person_phone: z.string().describe("Phone number or 'Not available'"),
9665
+ person_email: z.string().describe('Primary email address'),
9666
+ person_email_alternate: z.string().optional().describe('Alternate email if available'),
9667
+
9668
+ // Education
9669
+ person_education: z.array(z.string()).describe('Array of educational institutions/degrees'),
9670
+ person_education_highest: z.string().describe('Highest degree obtained'),
9671
+
9672
+ // Professional Details
9673
+ person_skills: z.array(z.string()).describe('Array of professional skills'),
9674
+ person_expertise: z.array(z.string()).describe('Areas of expertise'),
9675
+ person_career_level: z.enum(['entry', 'mid', 'senior', 'lead', 'manager', 'director', 'executive', 'founder', 'unknown']),
9676
+ person_years_experience: z.number().int().min(0).max(60).describe('Years of professional experience'),
9677
+
9678
+ // Social & Online Presence
9679
+ person_linkedin: z.string().describe("LinkedIn profile URL or 'Not available'"),
9680
+ person_twitter: z.string().describe("Twitter/X profile URL or 'Not available'"),
9681
+ person_github: z.string().describe("GitHub profile URL or 'Not available'"),
9682
+ person_website: z.string().describe("Personal website/portfolio or 'Not available'"),
9683
+ person_social_other: z.array(z.string()).optional().describe('Other social media profiles'),
9684
+
9685
+ // Additional Information
9686
+ person_languages: z.array(z.string()).describe('Languages spoken'),
9687
+ person_interests: z.array(z.string()).describe('Professional/personal interests'),
9688
+ person_achievements: z.array(z.string()).optional().describe('Notable achievements/awards'),
9689
+ person_current_projects: z.array(z.string()).optional().describe('Current projects'),
9690
+
9691
+ // Context Extracted Information
9692
+ person_context_insights: z.array(z.string()).optional().describe('Insights extracted from provided email context'),
9693
+
9694
+ // Metadata
9695
+ person_available_for_opportunities: z.boolean().describe('Open to new opportunities'),
9696
+ person_last_updated: z.string().describe('Date information was last verified'),
9697
+ person_source_confidence: z.number().min(0).max(100).describe('Confidence score for information accuracy'),
9698
+ person_has_context_hints: z.boolean().describe('Whether email context provided useful hints'),
9699
+ }),
9700
+ metadata: {
9701
+ func: 'get_person_info',
9702
+ person_name: name,
9703
+ person_email: email,
9704
+ has_email_context: !!email_context,
9705
+ email_context_length: email_context?.length || 0,
9706
+ context_hash: email_context ? await hashString(email_context.substring(0, 200)) : null,
9707
+ },
9708
+ account_profile_info,
9709
+ tools: [{ type: 'web_search_preview' }],
9710
+ });
9711
+
9712
+ try {
9713
+ if (person_info_ret.code > -1) {
9714
+ const data = typeof person_info_ret.data === 'string' ? JSON.parse(person_info_ret.data) : person_info_ret.data;
9715
+
9716
+ // Process and enhance the data
9717
+ const processedData = processPersonInfo(data, name, email, email_context);
9718
+
9719
+ return processedData;
9720
+ } else {
9721
+ console.error('Person info API call failed:', person_info_ret);
9722
+ return getDefaultPersonInfo(name, email, email_context);
9723
+ }
9724
+ } catch (error) {
9725
+ console.error('Error in get_person_info:', error);
9726
+ return getDefaultPersonInfo(name, email, email_context, error.message);
9727
+ }
9728
+ };