muzakkirhussain011 Claude commited on
Commit
b2313f4
Β·
1 Parent(s): 3951741

Redesign UI to enterprise-grade Salesforce/Microsoft style

Browse files

Complete UI/UX overhaul for non-technical end users:

## Visual Design
- Professional blue gradient header
- Salesforce-inspired color palette
- Card-based layout with shadows and hover effects
- Clean typography with Inter font
- Responsive design for all screen sizes

## New Dashboard Tab
- Quick stats cards (Prospects, Emails, Contacts)
- Quick action tiles for common tasks
- Getting started guide for new users

## Improved Research Tab
- Simple company name input (no technical jargon)
- Radio buttons for task type selection
- Popular companies quick-fill buttons
- Clean results panel with markdown output
- User-friendly progress messages

## New Tabs
- Prospects: Pipeline view placeholder
- Emails: Drafted emails placeholder
- Help: FAQ and documentation

## User Experience
- Hides technical details (logs, tool names)
- Friendly step descriptions instead of tool calls
- Clear error messages with helpful suggestions
- Results summary table with metrics

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

Files changed (1) hide show
  1. app.py +880 -366
app.py CHANGED
@@ -1,14 +1,8 @@
1
  """
2
- CX AI Agent - Autonomous MCP Agent with HuggingFace Inference Providers
3
 
4
- Main Entry Point for HuggingFace Spaces Deployment
5
-
6
- This application demonstrates TRUE MCP (Model Context Protocol) usage where:
7
- - AI (Qwen2.5-72B via HuggingFace) autonomously decides which MCP tools to call
8
- - NO hardcoded workflow - AI uses native tool calling
9
- - HuggingFace Inference Providers (Nebius, Together, Sambanova)
10
- - Free tier available with HuggingFace account
11
- - Works on free HuggingFace Spaces (no local model loading needed!)
12
  """
13
 
14
  import os
@@ -17,104 +11,454 @@ import asyncio
17
  import logging
18
  from pathlib import Path
19
  from dotenv import load_dotenv
 
20
 
21
  # Load environment variables
22
  load_dotenv()
23
 
24
- # Set in-memory MCP mode for HF Spaces (no separate server processes)
25
  os.environ["USE_IN_MEMORY_MCP"] = "true"
26
 
27
  # Import MCP components
28
  from mcp.registry import get_mcp_registry
29
  from mcp.agents.autonomous_agent_hf import AutonomousMCPAgentHF
30
 
31
- # Setup logging with both console and string capture
32
  import io
33
  import sys
34
 
35
- # Create a string buffer to capture logs
36
  log_capture_string = io.StringIO()
37
-
38
- # Setup logging with multiple handlers
39
  logging.basicConfig(
40
  level=logging.INFO,
41
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
42
  handlers=[
43
- logging.StreamHandler(sys.stdout), # Console output
44
- logging.StreamHandler(log_capture_string) # Capture for display
45
  ]
46
  )
47
  logger = logging.getLogger(__name__)
48
 
49
- # ============================================================================
50
- # DIAGNOSTIC: Check API Keys on Startup
51
- # ============================================================================
52
  print("\n" + "="*80)
53
- print("πŸš€ CX AI AGENT - AUTONOMOUS MCP WITH HUGGINGFACE INFERENCE")
54
  print("="*80)
55
 
56
- # Check HF_TOKEN
57
  hf_token = os.getenv('HF_TOKEN') or os.getenv('HF_API_TOKEN')
58
  if hf_token:
59
- print(f"βœ… HF_TOKEN is loaded (length: {len(hf_token)} chars)")
60
  else:
61
- print("❌ HF_TOKEN NOT FOUND!")
62
- print(" This is REQUIRED for HuggingFace Inference Providers")
63
- print(" Get token at: https://huggingface.co/settings/tokens")
64
- print(" Set in: Settings -> Repository secrets -> HF_TOKEN")
65
-
66
- # Check HF_PROVIDER (optional, defaults to nscale)
67
- hf_provider = os.getenv('HF_PROVIDER', 'nscale')
68
- print(f"πŸ“‘ Inference Provider: {hf_provider}")
69
- print(" Available: nscale, nebius, together, sambanova, fireworks-ai, cerebras")
70
 
71
- # Check HF_MODEL (optional)
72
- hf_model = os.getenv('HF_MODEL', 'Qwen/Qwen3-4B-Instruct-2507')
73
- print(f"πŸ€– Model: {hf_model}")
74
-
75
- # Check SERPER_API_KEY (optional for web search)
76
  serper_key = os.getenv('SERPER_API_KEY')
77
  if serper_key:
78
- print(f"βœ… SERPER_API_KEY is loaded (length: {len(serper_key)} chars)")
79
  else:
80
- print("⚠️ SERPER_API_KEY NOT FOUND")
81
- print(" Web search will use fallback data")
82
- print(" Get free key at: https://serper.dev")
83
 
84
- # Check if running in HF Space
85
  space_id = os.getenv('SPACE_ID')
86
  if space_id:
87
- print(f"πŸ“ Running in HuggingFace Space: {space_id}")
88
- else:
89
- print("πŸ“ Running locally")
90
-
91
  print("="*80 + "\n")
92
 
93
- # Initialize MCP registry (in-memory mode for HF Spaces)
94
  try:
95
  mcp_registry = get_mcp_registry()
96
- print("βœ… MCP Registry initialized (in-memory mode)")
97
- print(" - Search MCP Server")
98
- print(" - Store MCP Server")
99
- print(" - Email MCP Server")
100
- print(" - Calendar MCP Server")
101
  except Exception as e:
102
- print(f"❌ MCP Registry initialization failed: {e}")
103
  raise
104
 
105
 
106
- async def run_autonomous_agent(task: str, progress=gr.Progress()):
107
- """
108
- Run the autonomous AI agent with MCP tool calling using HuggingFace Inference.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
- Args:
111
- task: The task for the AI to complete autonomously
112
 
113
- Yields:
114
- Progress updates from the agent with logs
115
- """
 
 
116
 
117
- # Helper function to get recent logs
118
  def get_recent_logs(last_position=0):
119
  log_capture_string.seek(last_position)
120
  new_logs = log_capture_string.read()
@@ -123,42 +467,44 @@ async def run_autonomous_agent(task: str, progress=gr.Progress()):
123
 
124
  log_position = 0
125
 
126
- if not task or not task.strip():
127
- yield "❌ Error: Please provide a task description"
128
  return
129
 
130
- # Check if HF token is available
131
  hf_token = os.getenv('HF_TOKEN') or os.getenv('HF_API_TOKEN')
132
  if not hf_token:
133
- yield """❌ Error: HuggingFace token not found!
134
-
135
- Please set your HuggingFace token:
136
-
137
- 1. Get a token from: https://huggingface.co/settings/tokens
138
- 2. In HF Space: Settings β†’ Repository secrets β†’ Add:
139
- Name: HF_TOKEN
140
- Value: hf_your_token_here
141
 
142
- 3. Restart the space
 
 
 
 
 
 
 
143
 
144
- For local development, set environment variable:
145
- export HF_TOKEN=hf_your_token_here
146
- """
147
- return
148
 
149
- # Get provider and model from environment
150
  provider = os.getenv('HF_PROVIDER', 'nscale')
151
  model = os.getenv('HF_MODEL', 'Qwen/Qwen3-4B-Instruct-2507')
152
 
153
- # Create autonomous agent
154
- try:
155
- output_text = f"⏳ Initializing HuggingFace Inference Agent...\n\n"
156
- output_text += f"Provider: {provider}\n"
157
- output_text += f"Model: {model}\n\n"
158
- output_text += "No local model loading needed - using cloud inference!\n\n"
159
- yield output_text
160
- progress(0.1, desc="Initializing HuggingFace agent...")
161
 
 
 
 
 
 
 
 
162
  agent = AutonomousMCPAgentHF(
163
  mcp_registry=mcp_registry,
164
  hf_token=hf_token,
@@ -166,338 +512,506 @@ export HF_TOKEN=hf_your_token_here
166
  model=model
167
  )
168
 
169
- # Get logs from initialization
170
- new_logs, log_position = get_recent_logs(log_position)
 
171
 
172
- output_text += "βœ… Agent initialized successfully!\n\nStarting autonomous task...\n\n"
173
- if new_logs.strip():
174
- output_text += "\n" + "="*70 + "\n"
175
- output_text += "πŸ“‹ LOGS:\n"
176
- output_text += "="*70 + "\n"
177
- output_text += new_logs + "\n"
178
- output_text += "="*70 + "\n\n"
179
-
180
- yield output_text
181
- progress(0.2, desc="Agent ready, starting task...")
182
- logger.info(f"Agent initialized for task: {task}")
183
  except Exception as e:
184
- error_msg = f"❌ Error initializing agent: {str(e)}\n\nPlease check:\n1. HF_TOKEN is valid\n2. Network connection available\n3. Provider '{provider}' supports model '{model}'\n\n"
185
-
186
- # Include error logs
187
- new_logs, log_position = get_recent_logs(log_position)
188
- error_msg += "\n" + "="*70 + "\n"
189
- error_msg += "πŸ“‹ ERROR LOGS:\n"
190
- error_msg += "="*70 + "\n"
191
- error_msg += new_logs + "\n"
192
-
193
- yield error_msg
194
  return
195
 
196
- # Run agent autonomously
197
- output_text = ""
198
- iteration_count = 0
199
- show_logs_every = 2 # Show logs every N iterations
 
 
 
 
200
 
201
  try:
 
202
  async for event in agent.run(task, max_iterations=15):
203
  event_type = event.get("type")
204
- message = event.get("message", "")
205
-
206
- # Update progress
207
- if event_type == "iteration_start":
208
- iteration_count = event.get("iteration", 0)
209
- progress((iteration_count / 15), desc=f"Iteration {iteration_count}/15")
210
-
211
- # Periodically show logs
212
- if iteration_count % show_logs_every == 0:
213
- new_logs, log_position = get_recent_logs(log_position)
214
- if new_logs.strip():
215
- output_text += f"\n{'─'*70}\n"
216
- output_text += f"πŸ“‹ Logs (Iteration {iteration_count}):\n"
217
- output_text += f"{'─'*70}\n"
218
- output_text += new_logs
219
- output_text += f"{'─'*70}\n\n"
220
-
221
- # Format the message based on event type
222
- if event_type == "agent_start":
223
- output_text += f"\n{'='*70}\n"
224
- output_text += f"πŸ€– {message}\n"
225
- output_text += f"Task: {event.get('task', '')}\n"
226
- output_text += f"Model: {event.get('model', 'Granite 4')}\n"
227
- output_text += f"{'='*70}\n\n"
228
-
229
- elif event_type == "iteration_start":
230
- output_text += f"\n--- {message} ---\n"
231
-
232
- elif event_type == "thought":
233
- thought = event.get("thought", "")
234
- output_text += f"\nπŸ’­ AI Reasoning:\n"
235
- output_text += f" {thought}\n"
236
-
237
- elif event_type == "tool_call":
238
- tool = event.get("tool")
239
  tool_input = event.get("input", {})
240
- output_text += f"\nπŸ”§ {message}\n"
241
- output_text += f" Parameters: {tool_input}\n"
 
 
 
 
 
 
242
 
243
  elif event_type == "tool_result":
244
- tool = event.get("tool")
245
  result = event.get("result", {})
246
- output_text += f"βœ… {message}\n"
247
-
248
- # Show some result details
249
- if isinstance(result, dict):
250
- if "count" in result:
251
- output_text += f" β†’ Returned {result['count']} items\n"
252
- elif "status" in result:
253
- output_text += f" β†’ Status: {result['status']}\n"
254
- elif "prospect_id" in result:
255
- output_text += f" β†’ Prospect ID: {result['prospect_id']}\n"
256
- elif "company_id" in result:
257
- output_text += f" β†’ Company ID: {result['company_id']}\n"
258
 
259
- elif event_type == "tool_error":
260
- tool = event.get("tool")
261
- error = event.get("error")
262
- output_text += f"\n❌ {message}\n"
263
- output_text += f" Error: {error}\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
 
265
- elif event_type == "parse_error":
266
- output_text += f"\n⚠️ {message}\n"
 
267
 
268
  elif event_type == "agent_complete":
269
  final_answer = event.get("final_answer", "")
270
- iterations = event.get("iterations", 0)
271
- output_text += f"\n{'='*70}\n"
272
- output_text += f"βœ… {message}\n"
273
- output_text += f"Completed in {iterations} iterations\n"
274
- output_text += f"{'='*70}\n\n"
275
- output_text += f"πŸ“‹ **Final Answer:**\n\n{final_answer}\n"
276
-
277
- # Show final logs
278
- new_logs, log_position = get_recent_logs(log_position)
279
- if new_logs.strip():
280
- output_text += f"\n\n{'='*70}\n"
281
- output_text += f"πŸ“‹ FINAL LOGS:\n"
282
- output_text += f"{'='*70}\n"
283
- output_text += new_logs + "\n"
284
 
285
  elif event_type == "agent_error":
286
- error = event.get("error")
287
- output_text += f"\n❌ {message}\n"
288
- output_text += f"Error: {error}\n"
289
-
290
- # Show error logs
291
- new_logs, log_position = get_recent_logs(log_position)
292
- if new_logs.strip():
293
- output_text += f"\n{'─'*70}\n"
294
- output_text += f"πŸ“‹ Error Logs:\n"
295
- output_text += f"{'─'*70}\n"
296
- output_text += new_logs + "\n"
297
 
298
  elif event_type == "agent_max_iterations":
299
- iterations = event.get("iterations", 0)
300
- output_text += f"\n⚠️ {message}\n"
301
- output_text += f"The task may be too complex. Try breaking it into smaller tasks.\n"
302
-
303
- yield output_text
304
 
305
  except Exception as e:
306
- output_text += f"\n\n❌ Agent execution failed: {str(e)}\n"
307
- logger.error(f"Agent execution error: {e}", exc_info=True)
308
-
309
- # Show exception logs
310
- new_logs, log_position = get_recent_logs(log_position)
311
- if new_logs.strip():
312
- output_text += f"\n{'='*70}\n"
313
- output_text += f"πŸ“‹ EXCEPTION LOGS:\n"
314
- output_text += f"{'='*70}\n"
315
- output_text += new_logs + "\n"
316
- output_text += f"{'='*70}\n"
317
-
318
- yield output_text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
319
 
320
 
321
- def create_demo():
322
- """Create Gradio demo interface"""
 
 
 
323
 
324
  with gr.Blocks(
325
- title="CX AI Agent - Autonomous MCP with HuggingFace",
326
- theme=gr.themes.Soft(),
327
- css="""
328
- .center-text { text-align: center; }
329
- .highlight { background-color: #f0f7ff; padding: 15px; border-radius: 5px; }
330
- """
 
 
331
  ) as demo:
332
 
333
- gr.Markdown("""
334
- # πŸ€– CX AI Agent - Autonomous MCP Demo
335
- ### Powered by HuggingFace Inference Providers + Model Context Protocol
336
-
337
- This demo shows **TRUE AI-driven MCP usage** where the AI:
338
- - βœ… **Autonomously decides** which MCP tools to call (no hardcoded workflow!)
339
- - βœ… **Native tool calling** via HuggingFace Inference Providers
340
- - βœ… **Qwen3-4B-Instruct-2507** - Latest Qwen3 with strong tool calling
341
- - βœ… **Calls MCP servers** (Search, Store, Email, Calendar)
342
- - βœ… **Adapts to any task** in B2B sales automation
343
- - βœ… **Cloud inference** - No local model loading needed!
344
-
345
- ---
346
  """)
347
 
348
- with gr.Row():
349
- with gr.Column(scale=1):
350
- gr.Markdown("""
351
- ### 🎯 Available MCP Tools:
352
-
353
- **πŸ” Search**
354
- - Web search
355
- - News search
356
 
357
- **πŸ’Ύ Store**
358
- - Save/get prospects
359
- - Save/get companies
360
- - Save facts & contacts
361
- - Check suppressions
362
-
363
- **πŸ“§ Email**
364
- - Send emails
365
- - Track threads
366
-
367
- **πŸ“… Calendar**
368
- - Meeting slots
369
- - Calendar invites
370
  """)
371
 
372
- with gr.Column(scale=2):
373
- task_input = gr.Textbox(
374
- label="πŸ“ Task for AI Agent (be specific!)",
375
- placeholder="Example: Research Shopify and create a prospect profile with company info and recent news facts",
376
- lines=4,
377
- info="The AI will autonomously decide which MCP tools to use"
378
- )
379
-
380
- # Example tasks dropdown - covers all MCP modules
381
- example_tasks = gr.Dropdown(
382
- label="πŸ’‘ Example Tasks (click to use)",
383
- choices=[
384
- # === SEARCH + COMPANY/PROSPECT TASKS ===
385
- "Research Shopify and create a prospect profile with recent news",
386
- "Find information about Stripe and save company details and facts",
387
- "Research HubSpot and save company profile with industry insights",
388
-
389
- # === CONTACT FINDING TASKS ===
390
- "Find decision makers at Salesforce (CEO, CTO, VP Sales) and save their contact info",
391
- "Research Atlassian leadership team and save contacts with their roles and LinkedIn profiles",
392
- "Find key contacts at Zendesk including executives and department heads",
393
-
394
- # === EMAIL GENERATION TASKS ===
395
- "Research Slack, find their VP of Sales, and draft a personalized outreach email",
396
- "Create a cold email campaign: Research Monday.com, find contacts, and generate personalized emails",
397
- "Research Asana, save company info, find marketing director, and compose an introduction email",
398
-
399
- # === FULL PIPELINE TASKS ===
400
- "Complete B2B pipeline for Notion: research company, find CEO contact, save all data, and draft outreach email",
401
- "Full prospect workflow for Airtable: company research, decision maker contacts, facts, and email generation",
402
-
403
- # === CALENDAR/MEETING TASKS ===
404
- "Research Zoom and suggest meeting slots for a product demo with their team",
405
- "Find Calendly company info, save prospect, and generate a meeting invite for next week",
406
-
407
- # === MULTI-PROSPECT TASKS ===
408
- "Research 3 CRM companies (HubSpot, Pipedrive, Freshsales) and create prospect profiles for each",
409
- "Find and compare 2 project management tools (Jira, Linear) - save company details and key facts",
410
- ],
411
- interactive=True
412
- )
413
 
414
- def use_example(example):
415
- return example
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
416
 
417
- example_tasks.change(fn=use_example, inputs=[example_tasks], outputs=[task_input])
 
 
 
 
 
 
 
418
 
419
- run_btn = gr.Button(
420
- "πŸš€ Run Autonomous Agent",
421
- variant="primary",
422
- size="lg"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
423
  )
424
 
425
- with gr.Row():
426
- output = gr.Textbox(
427
- label="πŸ”„ Agent Progress & Results",
428
- lines=30,
429
- max_lines=50,
430
- show_copy_button=True,
431
- interactive=False
432
- )
433
-
434
- run_btn.click(
435
- fn=run_autonomous_agent,
436
- inputs=[task_input],
437
- outputs=[output]
438
- )
439
-
440
- gr.Markdown("""
441
- ---
442
-
443
- ## πŸŽ“ How It Works (Native Tool Calling)
444
-
445
- The AI uses **native function calling** to autonomously complete tasks:
446
-
447
- 1. **Analyze** - AI understands the task
448
- 2. **Tool Call** - AI calls MCP tools with proper parameters
449
- 3. **Process Results** - AI receives and processes tool results
450
- 4. **Iterate** - AI continues calling tools as needed
451
- 5. **Summary** - AI provides final answer
452
-
453
- **No hardcoded workflow!** The AI decides everything based on the task.
454
-
455
- ## πŸ† True MCP Implementation
456
-
457
- This demonstrates proper Model Context Protocol usage:
458
- - βœ… **AI autonomous tool calling** (not manual invocation)
459
- - βœ… **15 MCP tools** with proper schemas
460
- - βœ… **4 MCP servers** (Search, Store, Email, Calendar)
461
- - βœ… **Adapts to any B2B task** (not fixed pipeline)
462
- - βœ… **HuggingFace Inference** (cloud-based, no local loading)
463
-
464
- ## πŸ’‘ Tips for Best Results
465
-
466
- - **Be specific** about what you want
467
- - **Include details** like company names
468
- - **Multi-step tasks** show AI decision-making best
469
- - **Watch the tool calls** to see AI's actions
470
-
471
- ## βš™οΈ Configuration
472
 
473
- **Required:**
474
- - `HF_TOKEN` - HuggingFace API token
 
 
 
 
 
 
 
 
 
 
475
 
476
- **Optional:**
477
- - `HF_PROVIDER` - Inference provider (default: nscale)
478
- - `HF_MODEL` - Model to use (default: Qwen/Qwen3-4B-Instruct-2507)
479
- - `SERPER_API_KEY` - For real web search (free at serper.dev)
 
 
 
 
480
 
481
- Set in HF Space: **Settings β†’ Repository secrets**
 
 
 
 
 
 
 
 
 
 
 
482
 
483
- ---
 
 
 
 
 
 
 
484
 
485
- **Provider:** HuggingFace Inference Providers (Nscale, Nebius, etc.)
486
- **Model:** Qwen/Qwen3-4B-Instruct-2507 (configurable)
487
- **Protocol:** Model Context Protocol (MCP)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
488
  """)
489
 
490
  return demo
491
 
492
 
493
  if __name__ == "__main__":
494
- # Create and launch demo
495
- demo = create_demo()
496
-
497
- # Launch settings
498
  demo.launch(
499
  server_name="0.0.0.0",
500
  server_port=7860,
501
  show_error=True,
502
- share=False # Don't create public link by default
503
  )
 
1
  """
2
+ CX AI Agent - Enterprise B2B Sales Intelligence Platform
3
 
4
+ A modern, user-friendly interface for AI-powered sales automation.
5
+ Designed for sales teams, not developers.
 
 
 
 
 
 
6
  """
7
 
8
  import os
 
11
  import logging
12
  from pathlib import Path
13
  from dotenv import load_dotenv
14
+ from datetime import datetime
15
 
16
  # Load environment variables
17
  load_dotenv()
18
 
19
+ # Set in-memory MCP mode for HF Spaces
20
  os.environ["USE_IN_MEMORY_MCP"] = "true"
21
 
22
  # Import MCP components
23
  from mcp.registry import get_mcp_registry
24
  from mcp.agents.autonomous_agent_hf import AutonomousMCPAgentHF
25
 
26
+ # Setup logging
27
  import io
28
  import sys
29
 
 
30
  log_capture_string = io.StringIO()
 
 
31
  logging.basicConfig(
32
  level=logging.INFO,
33
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
34
  handlers=[
35
+ logging.StreamHandler(sys.stdout),
36
+ logging.StreamHandler(log_capture_string)
37
  ]
38
  )
39
  logger = logging.getLogger(__name__)
40
 
41
+ # Startup diagnostics
 
 
42
  print("\n" + "="*80)
43
+ print("πŸš€ CX AI AGENT - ENTERPRISE B2B SALES INTELLIGENCE")
44
  print("="*80)
45
 
 
46
  hf_token = os.getenv('HF_TOKEN') or os.getenv('HF_API_TOKEN')
47
  if hf_token:
48
+ print(f"βœ… HF_TOKEN loaded")
49
  else:
50
+ print("❌ HF_TOKEN NOT FOUND - Required for AI features")
 
 
 
 
 
 
 
 
51
 
 
 
 
 
 
52
  serper_key = os.getenv('SERPER_API_KEY')
53
  if serper_key:
54
+ print(f"βœ… SERPER_API_KEY loaded")
55
  else:
56
+ print("⚠️ SERPER_API_KEY not found - Web search limited")
 
 
57
 
 
58
  space_id = os.getenv('SPACE_ID')
59
  if space_id:
60
+ print(f"πŸ“ Running in: {space_id}")
 
 
 
61
  print("="*80 + "\n")
62
 
63
+ # Initialize MCP registry
64
  try:
65
  mcp_registry = get_mcp_registry()
66
+ print("βœ… AI Services initialized")
 
 
 
 
67
  except Exception as e:
68
+ print(f"❌ Initialization failed: {e}")
69
  raise
70
 
71
 
72
+ # ============================================================================
73
+ # ENTERPRISE CSS THEME
74
+ # ============================================================================
75
+ ENTERPRISE_CSS = """
76
+ /* ===== ENTERPRISE THEME - SALESFORCE/MICROSOFT INSPIRED ===== */
77
+
78
+ /* Root variables */
79
+ :root {
80
+ --primary-blue: #0176D3;
81
+ --primary-dark: #014486;
82
+ --success-green: #2E844A;
83
+ --warning-orange: #DD7A01;
84
+ --error-red: #EA001E;
85
+ --neutral-dark: #181818;
86
+ --neutral-gray: #706E6B;
87
+ --neutral-light: #F3F3F3;
88
+ --white: #FFFFFF;
89
+ --card-shadow: 0 2px 8px rgba(0,0,0,0.08);
90
+ --card-shadow-hover: 0 4px 16px rgba(0,0,0,0.12);
91
+ }
92
+
93
+ /* Main container */
94
+ .gradio-container {
95
+ max-width: 1400px !important;
96
+ margin: 0 auto !important;
97
+ font-family: 'Salesforce Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
98
+ }
99
+
100
+ /* Header styling */
101
+ .main-header {
102
+ background: linear-gradient(135deg, #0176D3 0%, #014486 100%);
103
+ color: white;
104
+ padding: 24px 32px;
105
+ border-radius: 12px;
106
+ margin-bottom: 24px;
107
+ box-shadow: var(--card-shadow);
108
+ }
109
+
110
+ .main-header h1 {
111
+ margin: 0 0 8px 0;
112
+ font-size: 28px;
113
+ font-weight: 700;
114
+ }
115
+
116
+ .main-header p {
117
+ margin: 0;
118
+ opacity: 0.9;
119
+ font-size: 16px;
120
+ }
121
+
122
+ /* Stats cards */
123
+ .stats-row {
124
+ display: flex;
125
+ gap: 16px;
126
+ margin-bottom: 24px;
127
+ }
128
+
129
+ .stat-card {
130
+ background: white;
131
+ border-radius: 12px;
132
+ padding: 20px 24px;
133
+ flex: 1;
134
+ box-shadow: var(--card-shadow);
135
+ border-left: 4px solid var(--primary-blue);
136
+ transition: all 0.2s ease;
137
+ }
138
+
139
+ .stat-card:hover {
140
+ box-shadow: var(--card-shadow-hover);
141
+ transform: translateY(-2px);
142
+ }
143
+
144
+ .stat-card .stat-value {
145
+ font-size: 32px;
146
+ font-weight: 700;
147
+ color: var(--neutral-dark);
148
+ margin-bottom: 4px;
149
+ }
150
+
151
+ .stat-card .stat-label {
152
+ font-size: 14px;
153
+ color: var(--neutral-gray);
154
+ text-transform: uppercase;
155
+ letter-spacing: 0.5px;
156
+ }
157
+
158
+ /* Action cards */
159
+ .action-card {
160
+ background: white;
161
+ border-radius: 12px;
162
+ padding: 24px;
163
+ box-shadow: var(--card-shadow);
164
+ margin-bottom: 16px;
165
+ border: 1px solid #E5E5E5;
166
+ }
167
+
168
+ .action-card:hover {
169
+ border-color: var(--primary-blue);
170
+ box-shadow: var(--card-shadow-hover);
171
+ }
172
+
173
+ .action-card h3 {
174
+ margin: 0 0 12px 0;
175
+ color: var(--neutral-dark);
176
+ font-size: 18px;
177
+ font-weight: 600;
178
+ }
179
+
180
+ .action-card p {
181
+ margin: 0 0 16px 0;
182
+ color: var(--neutral-gray);
183
+ font-size: 14px;
184
+ line-height: 1.5;
185
+ }
186
+
187
+ /* Primary button */
188
+ .primary-btn {
189
+ background: linear-gradient(135deg, #0176D3 0%, #014486 100%) !important;
190
+ color: white !important;
191
+ border: none !important;
192
+ border-radius: 8px !important;
193
+ padding: 12px 24px !important;
194
+ font-size: 16px !important;
195
+ font-weight: 600 !important;
196
+ cursor: pointer !important;
197
+ transition: all 0.2s ease !important;
198
+ box-shadow: 0 2px 4px rgba(1, 118, 211, 0.3) !important;
199
+ }
200
+
201
+ .primary-btn:hover {
202
+ transform: translateY(-1px) !important;
203
+ box-shadow: 0 4px 12px rgba(1, 118, 211, 0.4) !important;
204
+ }
205
+
206
+ /* Secondary button */
207
+ .secondary-btn {
208
+ background: white !important;
209
+ color: var(--primary-blue) !important;
210
+ border: 2px solid var(--primary-blue) !important;
211
+ border-radius: 8px !important;
212
+ padding: 10px 20px !important;
213
+ font-weight: 600 !important;
214
+ }
215
+
216
+ /* Input fields */
217
+ .input-field textarea, .input-field input {
218
+ border: 2px solid #E5E5E5 !important;
219
+ border-radius: 8px !important;
220
+ padding: 12px 16px !important;
221
+ font-size: 15px !important;
222
+ transition: all 0.2s ease !important;
223
+ }
224
+
225
+ .input-field textarea:focus, .input-field input:focus {
226
+ border-color: var(--primary-blue) !important;
227
+ box-shadow: 0 0 0 3px rgba(1, 118, 211, 0.1) !important;
228
+ }
229
+
230
+ /* Results panel */
231
+ .results-panel {
232
+ background: #FAFBFC;
233
+ border-radius: 12px;
234
+ padding: 24px;
235
+ border: 1px solid #E5E5E5;
236
+ }
237
+
238
+ .results-panel h3 {
239
+ margin: 0 0 16px 0;
240
+ color: var(--neutral-dark);
241
+ font-size: 16px;
242
+ font-weight: 600;
243
+ display: flex;
244
+ align-items: center;
245
+ gap: 8px;
246
+ }
247
+
248
+ /* Progress indicator */
249
+ .progress-bar {
250
+ height: 4px;
251
+ background: #E5E5E5;
252
+ border-radius: 2px;
253
+ overflow: hidden;
254
+ margin-bottom: 16px;
255
+ }
256
+
257
+ .progress-bar-fill {
258
+ height: 100%;
259
+ background: linear-gradient(90deg, #0176D3, #2E844A);
260
+ border-radius: 2px;
261
+ transition: width 0.3s ease;
262
+ }
263
+
264
+ /* Status badges */
265
+ .badge {
266
+ display: inline-flex;
267
+ align-items: center;
268
+ padding: 4px 12px;
269
+ border-radius: 16px;
270
+ font-size: 12px;
271
+ font-weight: 600;
272
+ text-transform: uppercase;
273
+ letter-spacing: 0.5px;
274
+ }
275
+
276
+ .badge-success {
277
+ background: #E6F4EA;
278
+ color: #2E844A;
279
+ }
280
+
281
+ .badge-warning {
282
+ background: #FEF3E2;
283
+ color: #DD7A01;
284
+ }
285
+
286
+ .badge-info {
287
+ background: #E5F3FE;
288
+ color: #0176D3;
289
+ }
290
+
291
+ .badge-error {
292
+ background: #FDE7E9;
293
+ color: #EA001E;
294
+ }
295
+
296
+ /* Tabs styling */
297
+ .tabs {
298
+ border: none !important;
299
+ background: transparent !important;
300
+ }
301
+
302
+ .tab-nav {
303
+ background: white !important;
304
+ border-radius: 12px 12px 0 0 !important;
305
+ padding: 8px 8px 0 8px !important;
306
+ border-bottom: 2px solid #E5E5E5 !important;
307
+ }
308
+
309
+ .tab-nav button {
310
+ border: none !important;
311
+ background: transparent !important;
312
+ padding: 12px 24px !important;
313
+ font-weight: 600 !important;
314
+ color: var(--neutral-gray) !important;
315
+ border-radius: 8px 8px 0 0 !important;
316
+ margin-right: 4px !important;
317
+ }
318
+
319
+ .tab-nav button.selected {
320
+ background: var(--primary-blue) !important;
321
+ color: white !important;
322
+ }
323
+
324
+ /* Quick action tiles */
325
+ .quick-action {
326
+ background: white;
327
+ border: 2px solid #E5E5E5;
328
+ border-radius: 12px;
329
+ padding: 20px;
330
+ text-align: center;
331
+ cursor: pointer;
332
+ transition: all 0.2s ease;
333
+ }
334
+
335
+ .quick-action:hover {
336
+ border-color: var(--primary-blue);
337
+ background: #F8FBFE;
338
+ transform: translateY(-2px);
339
+ }
340
+
341
+ .quick-action-icon {
342
+ font-size: 32px;
343
+ margin-bottom: 12px;
344
+ }
345
+
346
+ .quick-action-title {
347
+ font-size: 14px;
348
+ font-weight: 600;
349
+ color: var(--neutral-dark);
350
+ margin-bottom: 4px;
351
+ }
352
+
353
+ .quick-action-desc {
354
+ font-size: 12px;
355
+ color: var(--neutral-gray);
356
+ }
357
+
358
+ /* Prospect card */
359
+ .prospect-card {
360
+ background: white;
361
+ border-radius: 12px;
362
+ padding: 20px;
363
+ margin-bottom: 16px;
364
+ border: 1px solid #E5E5E5;
365
+ box-shadow: var(--card-shadow);
366
+ }
367
+
368
+ .prospect-card-header {
369
+ display: flex;
370
+ justify-content: space-between;
371
+ align-items: center;
372
+ margin-bottom: 16px;
373
+ padding-bottom: 16px;
374
+ border-bottom: 1px solid #E5E5E5;
375
+ }
376
+
377
+ .prospect-name {
378
+ font-size: 18px;
379
+ font-weight: 700;
380
+ color: var(--neutral-dark);
381
+ }
382
+
383
+ .fit-score {
384
+ display: flex;
385
+ align-items: center;
386
+ gap: 8px;
387
+ }
388
+
389
+ .fit-score-value {
390
+ font-size: 24px;
391
+ font-weight: 700;
392
+ }
393
+
394
+ .fit-score-high { color: #2E844A; }
395
+ .fit-score-medium { color: #DD7A01; }
396
+ .fit-score-low { color: #EA001E; }
397
+
398
+ /* Hide technical elements for users */
399
+ .technical-details {
400
+ display: none;
401
+ }
402
+
403
+ /* Responsive */
404
+ @media (max-width: 768px) {
405
+ .stats-row {
406
+ flex-direction: column;
407
+ }
408
+
409
+ .main-header {
410
+ padding: 16px 20px;
411
+ }
412
+
413
+ .main-header h1 {
414
+ font-size: 22px;
415
+ }
416
+ }
417
+
418
+ /* Loading animation */
419
+ .loading-spinner {
420
+ display: inline-block;
421
+ width: 20px;
422
+ height: 20px;
423
+ border: 2px solid #E5E5E5;
424
+ border-top-color: var(--primary-blue);
425
+ border-radius: 50%;
426
+ animation: spin 0.8s linear infinite;
427
+ }
428
+
429
+ @keyframes spin {
430
+ to { transform: rotate(360deg); }
431
+ }
432
+
433
+ /* Output formatting */
434
+ .output-section {
435
+ background: white;
436
+ border-radius: 8px;
437
+ padding: 16px;
438
+ margin-bottom: 12px;
439
+ border-left: 4px solid var(--primary-blue);
440
+ }
441
+
442
+ .output-section.success {
443
+ border-left-color: var(--success-green);
444
+ }
445
+
446
+ .output-section.warning {
447
+ border-left-color: var(--warning-orange);
448
+ }
449
+
450
+ .output-section.error {
451
+ border-left-color: var(--error-red);
452
+ }
453
+ """
454
 
 
 
455
 
456
+ # ============================================================================
457
+ # AGENT EXECUTION
458
+ # ============================================================================
459
+ async def run_sales_agent(company_name: str, task_type: str, progress=gr.Progress()):
460
+ """Run the AI agent with user-friendly output formatting."""
461
 
 
462
  def get_recent_logs(last_position=0):
463
  log_capture_string.seek(last_position)
464
  new_logs = log_capture_string.read()
 
467
 
468
  log_position = 0
469
 
470
+ if not company_name or not company_name.strip():
471
+ yield format_error("Please enter a company name to research.")
472
  return
473
 
 
474
  hf_token = os.getenv('HF_TOKEN') or os.getenv('HF_API_TOKEN')
475
  if not hf_token:
476
+ yield format_error("AI services not configured. Please contact your administrator.")
477
+ return
 
 
 
 
 
 
478
 
479
+ # Build task based on type
480
+ task_templates = {
481
+ "full_research": f"Research {company_name} thoroughly: find company information, identify 3-5 key decision makers (CEO, VP Sales, CTO), save company profile and contacts, then draft a personalized outreach email to the primary contact.",
482
+ "company_profile": f"Research {company_name} and create a detailed company profile including industry, size, recent news, and key business insights. Save all information to the database.",
483
+ "find_contacts": f"Find decision makers at {company_name} including CEO, VP of Sales, CTO, and other executives. Save their contact information with names, titles, and any available LinkedIn profiles.",
484
+ "draft_email": f"Research {company_name}, understand their business challenges, and draft a personalized B2B sales outreach email that addresses their specific pain points.",
485
+ "competitive_intel": f"Research {company_name} and gather competitive intelligence including their products, market position, recent news, funding, and potential pain points we could address."
486
+ }
487
 
488
+ task = task_templates.get(task_type, task_templates["full_research"])
 
 
 
489
 
 
490
  provider = os.getenv('HF_PROVIDER', 'nscale')
491
  model = os.getenv('HF_MODEL', 'Qwen/Qwen3-4B-Instruct-2507')
492
 
493
+ # User-friendly output
494
+ output = f"""
495
+ ## πŸ” Researching: {company_name}
496
+
497
+ **Task:** {get_task_label(task_type)}
498
+
499
+ ---
 
500
 
501
+ ### ⏳ Progress
502
+
503
+ """
504
+ yield output
505
+ progress(0.1, desc="Starting research...")
506
+
507
+ try:
508
  agent = AutonomousMCPAgentHF(
509
  mcp_registry=mcp_registry,
510
  hf_token=hf_token,
 
512
  model=model
513
  )
514
 
515
+ output += "βœ… AI Agent initialized\n\n"
516
+ yield output
517
+ progress(0.2, desc="Agent ready...")
518
 
 
 
 
 
 
 
 
 
 
 
 
519
  except Exception as e:
520
+ yield format_error(f"Could not start AI agent. Please try again later.")
 
 
 
 
 
 
 
 
 
521
  return
522
 
523
+ # Track results for summary
524
+ results = {
525
+ "company_saved": False,
526
+ "contacts_found": 0,
527
+ "facts_gathered": 0,
528
+ "email_drafted": False,
529
+ "steps_completed": []
530
+ }
531
 
532
  try:
533
+ iteration = 0
534
  async for event in agent.run(task, max_iterations=15):
535
  event_type = event.get("type")
536
+ iteration += 1
537
+
538
+ progress_pct = min(0.2 + (iteration * 0.05), 0.9)
539
+
540
+ if event_type == "tool_call":
541
+ tool = event.get("tool", "")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
542
  tool_input = event.get("input", {})
543
+
544
+ # User-friendly tool descriptions
545
+ step_desc = get_friendly_step(tool, tool_input, company_name)
546
+ if step_desc:
547
+ output += f"πŸ”„ {step_desc}\n"
548
+ results["steps_completed"].append(step_desc)
549
+ yield output
550
+ progress(progress_pct, desc=step_desc[:50])
551
 
552
  elif event_type == "tool_result":
553
+ tool = event.get("tool", "")
554
  result = event.get("result", {})
 
 
 
 
 
 
 
 
 
 
 
 
555
 
556
+ # Track results
557
+ if tool == "save_company":
558
+ results["company_saved"] = True
559
+ output += " βœ… Company profile saved\n"
560
+ elif tool == "save_contact":
561
+ results["contacts_found"] += 1
562
+ output += f" βœ… Contact #{results['contacts_found']} saved\n"
563
+ elif tool == "save_fact":
564
+ results["facts_gathered"] += 1
565
+ elif tool == "save_prospect":
566
+ output += " βœ… Prospect record created\n"
567
+ elif tool in ["search_web", "search_news"]:
568
+ count = result.get("count", 0) if isinstance(result, dict) else 0
569
+ output += f" βœ… Found {count} results\n"
570
+ elif tool == "send_email":
571
+ results["email_drafted"] = True
572
+ output += " βœ… Email drafted\n"
573
+
574
+ yield output
575
 
576
+ elif event_type == "tool_error":
577
+ # Don't show errors to users, just continue
578
+ pass
579
 
580
  elif event_type == "agent_complete":
581
  final_answer = event.get("final_answer", "")
582
+
583
+ # Format final summary
584
+ output += "\n---\n\n"
585
+ output += "## βœ… Research Complete!\n\n"
586
+ output += format_results_summary(results, company_name)
587
+ output += "\n### πŸ“‹ Detailed Findings\n\n"
588
+ output += final_answer
589
+
590
+ yield output
591
+ progress(1.0, desc="Complete!")
 
 
 
 
592
 
593
  elif event_type == "agent_error":
594
+ output += f"\n⚠️ Encountered an issue, but partial results may be available.\n"
595
+ yield output
 
 
 
 
 
 
 
 
 
596
 
597
  elif event_type == "agent_max_iterations":
598
+ output += "\n---\n\n"
599
+ output += "## ⏱️ Research Summary\n\n"
600
+ output += format_results_summary(results, company_name)
601
+ output += "\n*Research time limit reached. Results above are partial.*\n"
602
+ yield output
603
 
604
  except Exception as e:
605
+ output += f"\n\n⚠️ Research interrupted. Partial results may be available above.\n"
606
+ yield output
607
+
608
+
609
+ def get_task_label(task_type: str) -> str:
610
+ """Get user-friendly task label."""
611
+ labels = {
612
+ "full_research": "Complete prospect research with contacts and email",
613
+ "company_profile": "Company profile and insights",
614
+ "find_contacts": "Find decision makers",
615
+ "draft_email": "Draft personalized outreach email",
616
+ "competitive_intel": "Competitive intelligence gathering"
617
+ }
618
+ return labels.get(task_type, "Research task")
619
+
620
+
621
+ def get_friendly_step(tool: str, tool_input: dict, company: str) -> str:
622
+ """Convert technical tool names to user-friendly descriptions."""
623
+ friendly = {
624
+ "search_web": f"Searching for {company} information...",
625
+ "search_news": f"Finding recent news about {company}...",
626
+ "save_company": f"Saving {company} company profile...",
627
+ "save_prospect": f"Creating prospect record for {company}...",
628
+ "save_contact": f"Saving contact: {tool_input.get('name', 'decision maker')}...",
629
+ "save_fact": "Recording business insight...",
630
+ "get_company": f"Retrieving {company} data...",
631
+ "get_prospect": "Loading prospect information...",
632
+ "list_prospects": "Checking existing prospects...",
633
+ "send_email": "Drafting outreach email...",
634
+ "get_thread": "Loading email thread...",
635
+ "suggest_slots": "Finding available meeting times...",
636
+ "generate_ics": "Creating calendar invite..."
637
+ }
638
+ return friendly.get(tool, "")
639
+
640
+
641
+ def format_results_summary(results: dict, company: str) -> str:
642
+ """Format a user-friendly results summary."""
643
+ summary = f"### πŸ“Š Results for {company}\n\n"
644
+ summary += "| Metric | Status |\n"
645
+ summary += "|--------|--------|\n"
646
+ summary += f"| Company Profile | {'βœ… Saved' if results['company_saved'] else '⏳ Pending'} |\n"
647
+ summary += f"| Contacts Found | {results['contacts_found']} decision makers |\n"
648
+ summary += f"| Business Insights | {results['facts_gathered']} facts gathered |\n"
649
+ summary += f"| Outreach Email | {'βœ… Drafted' if results['email_drafted'] else '⏳ Pending'} |\n\n"
650
+ return summary
651
+
652
+
653
+ def format_error(message: str) -> str:
654
+ """Format user-friendly error message."""
655
+ return f"""
656
+ ## ⚠️ Unable to Complete Request
657
+
658
+ {message}
659
+
660
+ **Need help?** Try:
661
+ - Checking your internet connection
662
+ - Using a different company name
663
+ - Refreshing the page
664
+
665
+ If the problem persists, please try again in a few minutes.
666
+ """
667
 
668
 
669
+ # ============================================================================
670
+ # GRADIO UI - ENTERPRISE DESIGN
671
+ # ============================================================================
672
+ def create_enterprise_ui():
673
+ """Create enterprise-grade Gradio interface."""
674
 
675
  with gr.Blocks(
676
+ title="CX AI Agent - B2B Sales Intelligence",
677
+ theme=gr.themes.Soft(
678
+ primary_hue="blue",
679
+ secondary_hue="slate",
680
+ neutral_hue="slate",
681
+ font=gr.themes.GoogleFont("Inter")
682
+ ),
683
+ css=ENTERPRISE_CSS
684
  ) as demo:
685
 
686
+ # ===== HEADER =====
687
+ gr.HTML("""
688
+ <div class="main-header">
689
+ <h1>πŸš€ CX AI Agent</h1>
690
+ <p>Enterprise B2B Sales Intelligence Platform β€” AI-Powered Prospect Research & Outreach</p>
691
+ </div>
 
 
 
 
 
 
 
692
  """)
693
 
694
+ # ===== MAIN TABS =====
695
+ with gr.Tabs() as tabs:
 
 
 
 
 
 
696
 
697
+ # ===== TAB 1: DASHBOARD =====
698
+ with gr.Tab("πŸ“Š Dashboard", id="dashboard"):
699
+ gr.HTML("""
700
+ <div style="padding: 20px 0;">
701
+ <h2 style="margin: 0 0 20px 0; color: #181818;">Welcome to Your Sales Command Center</h2>
702
+ </div>
 
 
 
 
 
 
 
703
  """)
704
 
705
+ # Quick Stats Row
706
+ with gr.Row():
707
+ with gr.Column(scale=1):
708
+ gr.HTML("""
709
+ <div class="stat-card">
710
+ <div class="stat-value">β€”</div>
711
+ <div class="stat-label">Prospects Today</div>
712
+ </div>
713
+ """)
714
+ with gr.Column(scale=1):
715
+ gr.HTML("""
716
+ <div class="stat-card" style="border-left-color: #2E844A;">
717
+ <div class="stat-value">β€”</div>
718
+ <div class="stat-label">Emails Drafted</div>
719
+ </div>
720
+ """)
721
+ with gr.Column(scale=1):
722
+ gr.HTML("""
723
+ <div class="stat-card" style="border-left-color: #DD7A01;">
724
+ <div class="stat-value">β€”</div>
725
+ <div class="stat-label">Contacts Found</div>
726
+ </div>
727
+ """)
728
+ with gr.Column(scale=1):
729
+ gr.HTML("""
730
+ <div class="stat-card" style="border-left-color: #9050E9;">
731
+ <div class="stat-value">AI</div>
732
+ <div class="stat-label">Powered By</div>
733
+ </div>
734
+ """)
735
+
736
+ gr.HTML("<div style='height: 24px;'></div>")
737
+
738
+ # Quick Actions
739
+ gr.HTML("""
740
+ <h3 style="margin: 0 0 16px 0; color: #181818; font-size: 18px;">⚑ Quick Actions</h3>
741
+ """)
 
 
 
 
742
 
743
+ with gr.Row():
744
+ with gr.Column(scale=1):
745
+ gr.HTML("""
746
+ <div class="quick-action" onclick="document.querySelector('[data-testid=tab-research]')?.click()">
747
+ <div class="quick-action-icon">πŸ”</div>
748
+ <div class="quick-action-title">Research Company</div>
749
+ <div class="quick-action-desc">Deep-dive into any prospect</div>
750
+ </div>
751
+ """)
752
+ with gr.Column(scale=1):
753
+ gr.HTML("""
754
+ <div class="quick-action">
755
+ <div class="quick-action-icon">πŸ‘₯</div>
756
+ <div class="quick-action-title">Find Contacts</div>
757
+ <div class="quick-action-desc">Discover decision makers</div>
758
+ </div>
759
+ """)
760
+ with gr.Column(scale=1):
761
+ gr.HTML("""
762
+ <div class="quick-action">
763
+ <div class="quick-action-icon">βœ‰οΈ</div>
764
+ <div class="quick-action-title">Draft Email</div>
765
+ <div class="quick-action-desc">AI-written outreach</div>
766
+ </div>
767
+ """)
768
+ with gr.Column(scale=1):
769
+ gr.HTML("""
770
+ <div class="quick-action">
771
+ <div class="quick-action-icon">πŸ“ˆ</div>
772
+ <div class="quick-action-title">View Pipeline</div>
773
+ <div class="quick-action-desc">Track all prospects</div>
774
+ </div>
775
+ """)
776
+
777
+ gr.HTML("<div style='height: 24px;'></div>")
778
+
779
+ # Getting Started
780
+ gr.HTML("""
781
+ <div class="action-card">
782
+ <h3>🎯 Getting Started</h3>
783
+ <p>CX AI Agent helps your sales team research prospects, find decision makers, and draft personalized outreach emails β€” all powered by AI.</p>
784
+ <ol style="color: #706E6B; margin: 0; padding-left: 20px;">
785
+ <li style="margin-bottom: 8px;"><strong>Research a Company</strong> β€” Enter any company name to gather intelligence</li>
786
+ <li style="margin-bottom: 8px;"><strong>Find Decision Makers</strong> β€” AI identifies key contacts (CEO, VP Sales, etc.)</li>
787
+ <li style="margin-bottom: 8px;"><strong>Draft Personalized Email</strong> β€” Get AI-written outreach based on research</li>
788
+ <li><strong>Export & Send</strong> β€” Use the generated content in your sales workflow</li>
789
+ </ol>
790
+ </div>
791
+ """)
792
 
793
+ # ===== TAB 2: RESEARCH =====
794
+ with gr.Tab("πŸ” Research", id="research"):
795
+ gr.HTML("""
796
+ <div style="padding: 20px 0 10px 0;">
797
+ <h2 style="margin: 0 0 8px 0; color: #181818;">Prospect Research</h2>
798
+ <p style="margin: 0; color: #706E6B;">Enter a company name to start AI-powered research</p>
799
+ </div>
800
+ """)
801
 
802
+ with gr.Row():
803
+ # Left Panel - Input
804
+ with gr.Column(scale=1):
805
+ gr.HTML("""
806
+ <div class="action-card">
807
+ <h3>🏒 Company Information</h3>
808
+ """)
809
+
810
+ company_input = gr.Textbox(
811
+ label="Company Name",
812
+ placeholder="e.g., Salesforce, HubSpot, Stripe...",
813
+ lines=1,
814
+ max_lines=1,
815
+ elem_classes=["input-field"]
816
+ )
817
+
818
+ task_type = gr.Radio(
819
+ label="What would you like to do?",
820
+ choices=[
821
+ ("πŸ” Full Research (Recommended)", "full_research"),
822
+ ("🏒 Company Profile Only", "company_profile"),
823
+ ("πŸ‘₯ Find Decision Makers", "find_contacts"),
824
+ ("βœ‰οΈ Draft Outreach Email", "draft_email"),
825
+ ("πŸ“Š Competitive Intelligence", "competitive_intel")
826
+ ],
827
+ value="full_research"
828
+ )
829
+
830
+ gr.HTML("<div style='height: 16px;'></div>")
831
+
832
+ research_btn = gr.Button(
833
+ "πŸš€ Start Research",
834
+ variant="primary",
835
+ size="lg",
836
+ elem_classes=["primary-btn"]
837
+ )
838
+
839
+ gr.HTML("</div>") # Close action-card
840
+
841
+ # Popular Companies
842
+ gr.HTML("""
843
+ <div class="action-card" style="margin-top: 16px;">
844
+ <h3>πŸ’‘ Popular Companies</h3>
845
+ <p>Click to quick-fill:</p>
846
+ </div>
847
+ """)
848
+
849
+ with gr.Row():
850
+ ex1 = gr.Button("Salesforce", size="sm", variant="secondary")
851
+ ex2 = gr.Button("HubSpot", size="sm", variant="secondary")
852
+ ex3 = gr.Button("Stripe", size="sm", variant="secondary")
853
+ with gr.Row():
854
+ ex4 = gr.Button("Shopify", size="sm", variant="secondary")
855
+ ex5 = gr.Button("Zendesk", size="sm", variant="secondary")
856
+ ex6 = gr.Button("Slack", size="sm", variant="secondary")
857
+
858
+ # Quick fill handlers
859
+ ex1.click(lambda: "Salesforce", outputs=company_input)
860
+ ex2.click(lambda: "HubSpot", outputs=company_input)
861
+ ex3.click(lambda: "Stripe", outputs=company_input)
862
+ ex4.click(lambda: "Shopify", outputs=company_input)
863
+ ex5.click(lambda: "Zendesk", outputs=company_input)
864
+ ex6.click(lambda: "Slack", outputs=company_input)
865
+
866
+ # Right Panel - Results
867
+ with gr.Column(scale=2):
868
+ gr.HTML("""
869
+ <div class="results-panel">
870
+ <h3>πŸ“‹ Research Results</h3>
871
+ """)
872
+
873
+ research_output = gr.Markdown(
874
+ value="*Enter a company name and click 'Start Research' to begin.*",
875
+ elem_classes=["results-content"]
876
+ )
877
+
878
+ gr.HTML("</div>")
879
+
880
+ # Connect button to function
881
+ research_btn.click(
882
+ fn=run_sales_agent,
883
+ inputs=[company_input, task_type],
884
+ outputs=[research_output]
885
  )
886
 
887
+ # ===== TAB 3: PROSPECTS =====
888
+ with gr.Tab("πŸ‘₯ Prospects", id="prospects"):
889
+ gr.HTML("""
890
+ <div style="padding: 20px 0 10px 0;">
891
+ <h2 style="margin: 0 0 8px 0; color: #181818;">Prospect Pipeline</h2>
892
+ <p style="margin: 0; color: #706E6B;">View and manage your researched prospects</p>
893
+ </div>
894
+ """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
895
 
896
+ gr.HTML("""
897
+ <div class="action-card">
898
+ <h3>πŸ“‹ Your Prospects</h3>
899
+ <p>Prospects you research will appear here. Start by researching a company in the Research tab.</p>
900
+
901
+ <div style="text-align: center; padding: 40px 20px; color: #706E6B;">
902
+ <div style="font-size: 48px; margin-bottom: 16px;">πŸ“</div>
903
+ <div style="font-size: 16px; margin-bottom: 8px;">No prospects yet</div>
904
+ <div style="font-size: 14px;">Research your first company to get started</div>
905
+ </div>
906
+ </div>
907
+ """)
908
 
909
+ # ===== TAB 4: EMAILS =====
910
+ with gr.Tab("βœ‰οΈ Emails", id="emails"):
911
+ gr.HTML("""
912
+ <div style="padding: 20px 0 10px 0;">
913
+ <h2 style="margin: 0 0 8px 0; color: #181818;">Email Drafts</h2>
914
+ <p style="margin: 0; color: #706E6B;">AI-generated outreach emails ready for review</p>
915
+ </div>
916
+ """)
917
 
918
+ gr.HTML("""
919
+ <div class="action-card">
920
+ <h3>πŸ“§ Drafted Emails</h3>
921
+ <p>When you research prospects with the "Draft Email" option, your AI-generated emails will appear here.</p>
922
+
923
+ <div style="text-align: center; padding: 40px 20px; color: #706E6B;">
924
+ <div style="font-size: 48px; margin-bottom: 16px;">βœ‰οΈ</div>
925
+ <div style="font-size: 16px; margin-bottom: 8px;">No emails drafted yet</div>
926
+ <div style="font-size: 14px;">Research a company with email drafting to get started</div>
927
+ </div>
928
+ </div>
929
+ """)
930
 
931
+ # ===== TAB 5: HELP =====
932
+ with gr.Tab("❓ Help", id="help"):
933
+ gr.HTML("""
934
+ <div style="padding: 20px 0 10px 0;">
935
+ <h2 style="margin: 0 0 8px 0; color: #181818;">Help & Documentation</h2>
936
+ <p style="margin: 0; color: #706E6B;">Learn how to use CX AI Agent effectively</p>
937
+ </div>
938
+ """)
939
 
940
+ with gr.Row():
941
+ with gr.Column():
942
+ gr.HTML("""
943
+ <div class="action-card">
944
+ <h3>🎯 What is CX AI Agent?</h3>
945
+ <p>CX AI Agent is an AI-powered B2B sales intelligence platform that helps your team:</p>
946
+ <ul style="color: #706E6B; margin: 16px 0; padding-left: 20px;">
947
+ <li style="margin-bottom: 8px;"><strong>Research Companies</strong> β€” Automatically gather company information, news, and insights</li>
948
+ <li style="margin-bottom: 8px;"><strong>Find Decision Makers</strong> β€” Identify CEOs, VPs, and other key contacts</li>
949
+ <li style="margin-bottom: 8px;"><strong>Draft Personalized Emails</strong> β€” Generate outreach that addresses specific pain points</li>
950
+ <li><strong>Save Time</strong> β€” What takes hours manually is done in minutes</li>
951
+ </ul>
952
+ </div>
953
+
954
+ <div class="action-card">
955
+ <h3>πŸš€ How to Research a Company</h3>
956
+ <ol style="color: #706E6B; margin: 16px 0; padding-left: 20px;">
957
+ <li style="margin-bottom: 8px;">Go to the <strong>Research</strong> tab</li>
958
+ <li style="margin-bottom: 8px;">Enter a company name (e.g., "Salesforce")</li>
959
+ <li style="margin-bottom: 8px;">Select what you want to do (Full Research is recommended)</li>
960
+ <li style="margin-bottom: 8px;">Click <strong>Start Research</strong></li>
961
+ <li>Wait 1-2 minutes for AI to complete the research</li>
962
+ </ol>
963
+ </div>
964
+ """)
965
+
966
+ with gr.Column():
967
+ gr.HTML("""
968
+ <div class="action-card">
969
+ <h3>πŸ’‘ Tips for Best Results</h3>
970
+ <ul style="color: #706E6B; margin: 16px 0; padding-left: 20px;">
971
+ <li style="margin-bottom: 8px;"><strong>Use official company names</strong> β€” "Salesforce" works better than "SF"</li>
972
+ <li style="margin-bottom: 8px;"><strong>Start with Full Research</strong> β€” Gets you the most comprehensive results</li>
973
+ <li style="margin-bottom: 8px;"><strong>Be patient</strong> β€” AI research takes 1-2 minutes to complete</li>
974
+ <li><strong>Review AI outputs</strong> β€” Always verify before sending emails</li>
975
+ </ul>
976
+ </div>
977
+
978
+ <div class="action-card">
979
+ <h3>❓ Frequently Asked Questions</h3>
980
+ <details style="margin-bottom: 12px;">
981
+ <summary style="cursor: pointer; font-weight: 600; color: #181818;">How accurate is the research?</summary>
982
+ <p style="margin: 8px 0 0 0; color: #706E6B;">AI searches the web in real-time for current information. However, always verify critical details before using in outreach.</p>
983
+ </details>
984
+ <details style="margin-bottom: 12px;">
985
+ <summary style="cursor: pointer; font-weight: 600; color: #181818;">How long does research take?</summary>
986
+ <p style="margin: 8px 0 0 0; color: #706E6B;">Most research completes in 1-2 minutes depending on complexity.</p>
987
+ </details>
988
+ <details style="margin-bottom: 12px;">
989
+ <summary style="cursor: pointer; font-weight: 600; color: #181818;">Can I research multiple companies?</summary>
990
+ <p style="margin: 8px 0 0 0; color: #706E6B;">Currently, research one company at a time for best results.</p>
991
+ </details>
992
+ <details>
993
+ <summary style="cursor: pointer; font-weight: 600; color: #181818;">Is my data secure?</summary>
994
+ <p style="margin: 8px 0 0 0; color: #706E6B;">Yes, all data is processed securely and not shared with third parties.</p>
995
+ </details>
996
+ </div>
997
+ """)
998
+
999
+ # ===== FOOTER =====
1000
+ gr.HTML("""
1001
+ <div style="text-align: center; padding: 24px; color: #706E6B; border-top: 1px solid #E5E5E5; margin-top: 24px;">
1002
+ <p style="margin: 0 0 8px 0; font-size: 14px;">CX AI Agent β€” Enterprise B2B Sales Intelligence</p>
1003
+ <p style="margin: 0; font-size: 12px;">Powered by AI β€’ Built for Sales Teams</p>
1004
+ </div>
1005
  """)
1006
 
1007
  return demo
1008
 
1009
 
1010
  if __name__ == "__main__":
1011
+ demo = create_enterprise_ui()
 
 
 
1012
  demo.launch(
1013
  server_name="0.0.0.0",
1014
  server_port=7860,
1015
  show_error=True,
1016
+ share=False
1017
  )