muzakkirhussain011 commited on
Commit
dbbf1a3
·
1 Parent(s): 6a58058

Add application files

Browse files
app.py CHANGED
@@ -894,52 +894,34 @@ async def discover_prospects(num_prospects: int, progress=gr.Progress()):
894
  About {client_name}:
895
  {client_info}
896
 
897
- CRITICAL: Only save prospects that have verified contact details. A prospect without contacts is useless.
898
-
899
- WORKFLOW - Follow these steps:
900
-
901
- STEP 1: Search for potential prospect companies
902
- - Use search_web to find companies that would benefit from {client_name}'s services
903
- - Look for REAL, INDEPENDENT companies (not acquired/merged ones)
904
- - You may need to search for more companies than {num_prospects} to find ones with contacts
905
-
906
- STEP 2: For EACH potential company, FIRST find contacts:
907
- a. Use find_verified_contacts tool FIRST:
908
- {{"company_name": "CompanyName", "company_domain": "company.com", "target_titles": ["CEO", "Founder", "VP Sales", "CTO", "Head of Sales"], "max_contacts": 3}}
909
-
910
- This tool searches:
911
- - Company website (team/about pages)
912
- - LinkedIn profiles
913
- - Crunchbase and business directories
914
- - Press releases and news
915
- - Social media profiles
916
-
917
- b. CHECK THE RESULT:
918
- - If find_verified_contacts returns contacts (count > 0): PROCEED to save
919
- - If find_verified_contacts returns NO contacts: SKIP this company, try the next one
920
-
921
- c. ONLY IF contacts were found, save the prospect:
922
- {{"prospect_id": "prospect_1", "company_id": "company_1", "company_name": "CompanyName", "company_domain": "company.com", "fit_score": 85, "metadata": {{"industry": "...", "fit_reason": "..."}}}}
923
-
924
- d. Draft personalized email using send_email tool:
925
- - Use the REAL contact info returned by find_verified_contacts
926
- - to: actual verified email from the tool result
927
- - subject: Reference {client_name} AND the prospect's business
928
- - body: Personalized email with real facts about the company
929
- - prospect_id: the prospect_id
930
-
931
- IMPORTANT RULES:
932
- - CONTACTS FIRST: Always find contacts BEFORE saving a prospect
933
- - NO CONTACTS = NO SAVE: Skip companies where no verified contacts are found
934
- - NEVER invent contact names or emails
935
- - Keep searching until you find {num_prospects} companies WITH verified contacts
936
- - It's better to have fewer prospects with real contacts than many with none
937
-
938
- After processing, provide summary:
939
- - Prospects saved (only those with verified contacts)
940
  - Total contacts found
941
- - Emails drafted
942
- - Companies skipped (no contacts found)"""
943
 
944
  prospects_found = []
945
  contacts_found = []
@@ -1050,8 +1032,49 @@ After processing, provide summary:
1050
  else:
1051
  output += f" ✅ Contact saved\n"
1052
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1053
  elif tool == "find_verified_contacts":
1054
- # Handle verified contacts from the enhanced contact finder
1055
  if isinstance(result, dict):
1056
  status = result.get("status", "")
1057
  found_contacts = result.get("contacts", [])
@@ -1071,7 +1094,7 @@ After processing, provide summary:
1071
  contacts_found.append(contact_data)
1072
  output += f" • {contact_data['name']} ({contact_data['title']}) - {contact_data['email']}\n"
1073
  elif status == "no_contacts_found":
1074
- output += f" ℹ️ {message}\n"
1075
  else:
1076
  output += f" ⚠️ Contact search: {message}\n"
1077
 
 
894
  About {client_name}:
895
  {client_info}
896
 
897
+ USE THE discover_prospects_with_contacts TOOL - it handles everything automatically:
898
+ - Searches for potential prospect companies
899
+ - Finds verified contacts for each (LinkedIn, company websites, directories, etc.)
900
+ - ONLY saves prospects that have real verified contacts
901
+ - Keeps searching until target is met or max attempts reached
902
+ - Skips companies without contacts automatically
903
+
904
+ STEP 1: Call discover_prospects_with_contacts:
905
+ {{"client_company": "{client_name}", "client_industry": "Brief description of {client_name}'s services and target market", "target_prospects": {num_prospects}, "target_titles": ["CEO", "Founder", "VP Sales", "CTO", "Head of Sales"]}}
906
+
907
+ STEP 2: After discovery completes, for each prospect with contacts, draft personalized email:
908
+ - Use send_email tool with the REAL contact info returned
909
+ - to: actual verified email
910
+ - subject: Reference {client_name} AND the prospect's business
911
+ - body: Personalized email mentioning the contact by name and specific facts about their company
912
+ - prospect_id: the prospect_id from discovery results
913
+
914
+ IMPORTANT:
915
+ - The discover_prospects_with_contacts tool does ALL the hard work
916
+ - It will check multiple companies until it finds {num_prospects} with verified contacts
917
+ - Only prospects WITH contacts are saved (no useless data)
918
+ - NEVER invent contact names or emails - only use what the tool returns
919
+
920
+ After the tool completes, provide a summary of:
921
+ - Prospects saved (with verified contacts)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
922
  - Total contacts found
923
+ - Companies checked vs skipped
924
+ - Emails drafted"""
925
 
926
  prospects_found = []
927
  contacts_found = []
 
1032
  else:
1033
  output += f" ✅ Contact saved\n"
1034
 
1035
+ elif tool == "discover_prospects_with_contacts":
1036
+ # Handle the all-in-one prospect discovery tool
1037
+ if isinstance(result, dict):
1038
+ status = result.get("status", "")
1039
+ discovered_prospects = result.get("prospects", [])
1040
+ total_contacts = result.get("contacts_count", 0)
1041
+ companies_checked = result.get("companies_checked", 0)
1042
+ companies_skipped = result.get("companies_skipped", 0)
1043
+ message = result.get("message", "")
1044
+
1045
+ output += f"\n 📊 **Discovery Complete**\n"
1046
+ output += f" {message}\n\n"
1047
+
1048
+ if discovered_prospects:
1049
+ for p in discovered_prospects:
1050
+ # Add to prospects_found
1051
+ prospect_data = {
1052
+ "name": p.get("company_name", "Unknown"),
1053
+ "domain": p.get("domain", ""),
1054
+ "fit_score": 75,
1055
+ "summary": f"Found with {p.get('contact_count', 0)} verified contacts"
1056
+ }
1057
+ prospects_found.append(prospect_data)
1058
+
1059
+ output += f" ✅ **{p.get('company_name')}** ({p.get('domain')})\n"
1060
+
1061
+ # Add contacts
1062
+ for c in p.get("contacts", []):
1063
+ contact_data = {
1064
+ "name": c.get("name", "Unknown"),
1065
+ "email": c.get("email", ""),
1066
+ "title": c.get("title", ""),
1067
+ "company": p.get("company_name", ""),
1068
+ "verified": True,
1069
+ "source": c.get("source", "web_search")
1070
+ }
1071
+ contacts_found.append(contact_data)
1072
+ output += f" • {c.get('name')} ({c.get('title')}) - {c.get('email')}\n"
1073
+ else:
1074
+ output += f" ⚠️ No prospects with verified contacts found.\n"
1075
+
1076
  elif tool == "find_verified_contacts":
1077
+ # Handle verified contacts from the enhanced contact finder (single company)
1078
  if isinstance(result, dict):
1079
  status = result.get("status", "")
1080
  found_contacts = result.get("contacts", [])
 
1094
  contacts_found.append(contact_data)
1095
  output += f" • {contact_data['name']} ({contact_data['title']}) - {contact_data['email']}\n"
1096
  elif status == "no_contacts_found":
1097
+ output += f" ⏭️ {message}\n"
1098
  else:
1099
  output += f" ⚠️ Contact search: {message}\n"
1100
 
mcp/agents/autonomous_agent_hf.py CHANGED
@@ -577,7 +577,12 @@ After completing, summarize:
577
  return None
578
  keys = set(params.keys())
579
 
580
- # Check for find_verified_contacts patterns (PRIORITY - use this instead of save_contact)
 
 
 
 
 
581
  if "company_name" in keys and "company_domain" in keys and "target_titles" in keys:
582
  return "find_verified_contacts"
583
  if "company_name" in keys and "company_domain" in keys and "max_contacts" in keys:
@@ -730,7 +735,178 @@ After completing, summarize:
730
  "count": len(results[:max_results])
731
  }
732
 
733
- # ============ VERIFIED CONTACT FINDER ============
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
734
  elif tool_name == "find_verified_contacts":
735
  from services.enhanced_contact_finder import EnhancedContactFinder
736
 
@@ -741,7 +917,6 @@ After completing, summarize:
741
 
742
  logger.info(f"Finding verified contacts for {company_name} ({company_domain})")
743
 
744
- # Use the EnhancedContactFinder which does real web scraping and LinkedIn search
745
  contact_finder = EnhancedContactFinder(mcp_registry=self.mcp_registry)
746
 
747
  try:
@@ -752,7 +927,6 @@ After completing, summarize:
752
  max_contacts=max_contacts
753
  )
754
 
755
- # Convert contacts to serializable format
756
  contact_list = []
757
  for contact in contacts:
758
  contact_data = {
@@ -762,12 +936,11 @@ After completing, summarize:
762
  "title": contact.title,
763
  "company": company_name,
764
  "domain": company_domain,
765
- "verified": True, # These come from real web searches
766
  "source": "web_search_and_scraping"
767
  }
768
  contact_list.append(contact_data)
769
 
770
- # Also save to store automatically
771
  await self.mcp_registry.store.save_contact({
772
  "id": contact.id,
773
  "company_id": company_domain.replace(".", "_"),
@@ -782,14 +955,16 @@ After completing, summarize:
782
  "status": "success",
783
  "contacts": contact_list,
784
  "count": len(contact_list),
785
- "message": f"Found {len(contact_list)} verified contacts at {company_name}"
 
786
  }
787
  else:
788
  return {
789
  "status": "no_contacts_found",
790
  "contacts": [],
791
  "count": 0,
792
- "message": f"No verified contacts found for {company_name}. Leadership information may not be publicly available."
 
793
  }
794
 
795
  except Exception as e:
@@ -798,7 +973,8 @@ After completing, summarize:
798
  "status": "error",
799
  "contacts": [],
800
  "count": 0,
801
- "message": f"Error searching for contacts: {str(e)}"
 
802
  }
803
 
804
  # ============ STORE MCP SERVER ============
 
577
  return None
578
  keys = set(params.keys())
579
 
580
+ # Check for discover_prospects_with_contacts (HIGHEST PRIORITY - all-in-one tool)
581
+ if "client_company" in keys and "client_industry" in keys:
582
+ return "discover_prospects_with_contacts"
583
+ if "client_company" in keys and "target_prospects" in keys:
584
+ return "discover_prospects_with_contacts"
585
+ # Check for find_verified_contacts patterns (single company)
586
  if "company_name" in keys and "company_domain" in keys and "target_titles" in keys:
587
  return "find_verified_contacts"
588
  if "company_name" in keys and "company_domain" in keys and "max_contacts" in keys:
 
735
  "count": len(results[:max_results])
736
  }
737
 
738
+ # ============ OPTIMIZED PROSPECT DISCOVERY WITH CONTACTS ============
739
+ elif tool_name == "discover_prospects_with_contacts":
740
+ from services.enhanced_contact_finder import EnhancedContactFinder
741
+ from urllib.parse import urlparse
742
+
743
+ client_company = tool_input["client_company"]
744
+ client_industry = tool_input["client_industry"]
745
+ target_prospects = tool_input.get("target_prospects", 3)
746
+ target_titles = tool_input.get("target_titles", ["CEO", "Founder", "VP Sales", "CTO", "Head of Sales"])
747
+
748
+ logger.info(f"Discovering {target_prospects} prospects with contacts for {client_company}")
749
+ print(f"\n[PROSPECT DISCOVERY] ========================================")
750
+ print(f"[PROSPECT DISCOVERY] Finding {target_prospects} prospects WITH verified contacts")
751
+ print(f"[PROSPECT DISCOVERY] Client: {client_company}")
752
+ print(f"[PROSPECT DISCOVERY] ========================================")
753
+
754
+ contact_finder = EnhancedContactFinder(mcp_registry=self.mcp_registry)
755
+
756
+ saved_prospects = []
757
+ all_contacts = []
758
+ skipped_companies = []
759
+ companies_checked = 0
760
+ max_companies_to_check = target_prospects * 5 # Check up to 5x to find enough with contacts
761
+
762
+ # Search queries to find potential companies
763
+ search_queries = [
764
+ f"companies that need {client_industry} services",
765
+ f"businesses looking for {client_industry}",
766
+ f"startups {client_industry} customers",
767
+ f"enterprise companies {client_industry}",
768
+ f"growing companies need {client_industry}",
769
+ ]
770
+
771
+ seen_domains = set()
772
+
773
+ for query in search_queries:
774
+ if len(saved_prospects) >= target_prospects:
775
+ break
776
+
777
+ try:
778
+ print(f"\n[PROSPECT DISCOVERY] Searching: {query}")
779
+ results = await self.mcp_registry.search.query(query, max_results=10)
780
+
781
+ for result in results:
782
+ if len(saved_prospects) >= target_prospects:
783
+ break
784
+ if companies_checked >= max_companies_to_check:
785
+ break
786
+
787
+ url = result.get('url', '')
788
+ title = result.get('title', '')
789
+
790
+ # Extract domain from URL
791
+ try:
792
+ parsed = urlparse(url)
793
+ domain = parsed.netloc.replace('www.', '')
794
+ if not domain or domain in seen_domains:
795
+ continue
796
+ seen_domains.add(domain)
797
+ except:
798
+ continue
799
+
800
+ # Extract company name from title
801
+ company_name = title.split(' - ')[0].split(' | ')[0].strip()
802
+ if not company_name or len(company_name) < 3:
803
+ continue
804
+
805
+ # Skip if it's a directory/aggregator site
806
+ skip_domains = ['linkedin.com', 'facebook.com', 'twitter.com', 'wikipedia.org',
807
+ 'crunchbase.com', 'zoominfo.com', 'yelp.com', 'glassdoor.com']
808
+ if any(skip in domain for skip in skip_domains):
809
+ continue
810
+
811
+ companies_checked += 1
812
+ print(f"\n[PROSPECT DISCOVERY] Checking ({companies_checked}/{max_companies_to_check}): {company_name} ({domain})")
813
+
814
+ # Find contacts for this company
815
+ try:
816
+ contacts = await contact_finder.find_real_contacts(
817
+ company_name=company_name,
818
+ domain=domain,
819
+ target_titles=target_titles,
820
+ max_contacts=3
821
+ )
822
+
823
+ if contacts and len(contacts) > 0:
824
+ # Save prospect
825
+ prospect_id = f"prospect_{len(saved_prospects) + 1}"
826
+ company_id = domain.replace(".", "_")
827
+
828
+ prospect_data = {
829
+ "id": prospect_id,
830
+ "company": {
831
+ "id": company_id,
832
+ "name": company_name,
833
+ "domain": domain
834
+ },
835
+ "fit_score": 75,
836
+ "status": "new",
837
+ "metadata": {"source": "automated_discovery"}
838
+ }
839
+
840
+ await self.mcp_registry.store.save_prospect(prospect_data)
841
+
842
+ # Save contacts
843
+ contact_list = []
844
+ for contact in contacts:
845
+ contact_data = {
846
+ "id": contact.id,
847
+ "name": contact.name,
848
+ "email": contact.email,
849
+ "title": contact.title,
850
+ "company": company_name,
851
+ "domain": domain,
852
+ "verified": True,
853
+ "source": "web_search_and_scraping"
854
+ }
855
+ contact_list.append(contact_data)
856
+ all_contacts.append(contact_data)
857
+
858
+ await self.mcp_registry.store.save_contact({
859
+ "id": contact.id,
860
+ "company_id": company_id,
861
+ "email": contact.email,
862
+ "first_name": contact.name.split()[0] if contact.name else "",
863
+ "last_name": contact.name.split()[-1] if len(contact.name.split()) > 1 else "",
864
+ "title": contact.title
865
+ })
866
+
867
+ saved_prospects.append({
868
+ "prospect_id": prospect_id,
869
+ "company_name": company_name,
870
+ "domain": domain,
871
+ "contacts": contact_list,
872
+ "contact_count": len(contact_list)
873
+ })
874
+
875
+ print(f"[PROSPECT DISCOVERY] ✅ SAVED: {company_name} with {len(contacts)} contacts")
876
+ else:
877
+ skipped_companies.append({"name": company_name, "domain": domain, "reason": "no_contacts"})
878
+ print(f"[PROSPECT DISCOVERY] ⏭️ SKIPPED: {company_name} (no verified contacts)")
879
+
880
+ except Exception as e:
881
+ logger.debug(f"Error checking {company_name}: {str(e)}")
882
+ skipped_companies.append({"name": company_name, "domain": domain, "reason": str(e)})
883
+ continue
884
+
885
+ except Exception as e:
886
+ logger.debug(f"Search error: {str(e)}")
887
+ continue
888
+
889
+ print(f"\n[PROSPECT DISCOVERY] ========================================")
890
+ print(f"[PROSPECT DISCOVERY] DISCOVERY COMPLETE")
891
+ print(f"[PROSPECT DISCOVERY] ========================================")
892
+ print(f"[PROSPECT DISCOVERY] Prospects saved: {len(saved_prospects)}/{target_prospects}")
893
+ print(f"[PROSPECT DISCOVERY] Total contacts: {len(all_contacts)}")
894
+ print(f"[PROSPECT DISCOVERY] Companies checked: {companies_checked}")
895
+ print(f"[PROSPECT DISCOVERY] Companies skipped: {len(skipped_companies)}")
896
+ print(f"[PROSPECT DISCOVERY] ========================================\n")
897
+
898
+ return {
899
+ "status": "success" if len(saved_prospects) > 0 else "no_prospects_found",
900
+ "prospects": saved_prospects,
901
+ "prospects_count": len(saved_prospects),
902
+ "contacts_count": len(all_contacts),
903
+ "companies_checked": companies_checked,
904
+ "companies_skipped": len(skipped_companies),
905
+ "target_met": len(saved_prospects) >= target_prospects,
906
+ "message": f"Found {len(saved_prospects)} prospects with {len(all_contacts)} verified contacts. Checked {companies_checked} companies, skipped {len(skipped_companies)} (no contacts)."
907
+ }
908
+
909
+ # ============ VERIFIED CONTACT FINDER (Single Company) ============
910
  elif tool_name == "find_verified_contacts":
911
  from services.enhanced_contact_finder import EnhancedContactFinder
912
 
 
917
 
918
  logger.info(f"Finding verified contacts for {company_name} ({company_domain})")
919
 
 
920
  contact_finder = EnhancedContactFinder(mcp_registry=self.mcp_registry)
921
 
922
  try:
 
927
  max_contacts=max_contacts
928
  )
929
 
 
930
  contact_list = []
931
  for contact in contacts:
932
  contact_data = {
 
936
  "title": contact.title,
937
  "company": company_name,
938
  "domain": company_domain,
939
+ "verified": True,
940
  "source": "web_search_and_scraping"
941
  }
942
  contact_list.append(contact_data)
943
 
 
944
  await self.mcp_registry.store.save_contact({
945
  "id": contact.id,
946
  "company_id": company_domain.replace(".", "_"),
 
955
  "status": "success",
956
  "contacts": contact_list,
957
  "count": len(contact_list),
958
+ "message": f"Found {len(contact_list)} verified contacts at {company_name}",
959
+ "should_save_prospect": True
960
  }
961
  else:
962
  return {
963
  "status": "no_contacts_found",
964
  "contacts": [],
965
  "count": 0,
966
+ "message": f"No verified contacts found for {company_name}. Skip this prospect.",
967
+ "should_save_prospect": False
968
  }
969
 
970
  except Exception as e:
 
973
  "status": "error",
974
  "contacts": [],
975
  "count": 0,
976
+ "message": f"Error searching for contacts: {str(e)}",
977
+ "should_save_prospect": False
978
  }
979
 
980
  # ============ STORE MCP SERVER ============
mcp/tools/definitions.py CHANGED
@@ -201,9 +201,37 @@ MCP_TOOLS: List[Dict[str, Any]] = [
201
  "required": ["fact_id", "company_id", "fact_type", "content"]
202
  }
203
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  {
205
  "name": "find_verified_contacts",
206
- "description": "Find REAL verified decision-makers at a company using LinkedIn search, team page scraping, and web research. This tool searches the web to find actual executives - DO NOT make up contact names. Use this INSTEAD of save_contact to get real contacts.",
207
  "input_schema": {
208
  "type": "object",
209
  "properties": {
 
201
  "required": ["fact_id", "company_id", "fact_type", "content"]
202
  }
203
  },
204
+ {
205
+ "name": "discover_prospects_with_contacts",
206
+ "description": "Find prospect companies WITH verified decision-maker contacts. This tool searches for companies, finds their contacts, and ONLY saves prospects that have real verified contacts. It keeps searching until the target number of valid prospects is found. Use this as your PRIMARY tool for prospect discovery.",
207
+ "input_schema": {
208
+ "type": "object",
209
+ "properties": {
210
+ "client_company": {
211
+ "type": "string",
212
+ "description": "Your client company name (who you're finding prospects for)"
213
+ },
214
+ "client_industry": {
215
+ "type": "string",
216
+ "description": "Brief description of what the client does and their target market"
217
+ },
218
+ "target_prospects": {
219
+ "type": "integer",
220
+ "description": "Number of prospects WITH contacts to find (default: 3)",
221
+ "default": 3
222
+ },
223
+ "target_titles": {
224
+ "type": "array",
225
+ "items": {"type": "string"},
226
+ "description": "Job titles to search for (default: ['CEO', 'Founder', 'VP Sales', 'CTO'])"
227
+ }
228
+ },
229
+ "required": ["client_company", "client_industry"]
230
+ }
231
+ },
232
  {
233
  "name": "find_verified_contacts",
234
+ "description": "Find REAL verified decision-makers at a specific company. Searches LinkedIn, company websites, directories, press releases, and social media. Only returns contacts with verified email addresses found on the web.",
235
  "input_schema": {
236
  "type": "object",
237
  "properties": {