Spaces:
Runtime error
Runtime error
Tolga Kavukcu commited on
Commit ·
7b3dda5
1
Parent(s): e3122c4
Initial commit
Browse files- .env.example +13 -0
- .gitignore +350 -0
- DEPLOYMENT.md +62 -0
- _old_app.py +704 -0
- ai-agent-ui/index.html +221 -0
- ai-agent-ui/script.js +562 -0
- ai-agent-ui/style.css +561 -0
- app.py +297 -50
- get_tools_working.py +303 -0
- requirements.txt +10 -1
.env.example
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Environment variables for AI Lead Generation deployment
|
| 2 |
+
|
| 3 |
+
# SendGrid API key for email functionality
|
| 4 |
+
# Get this from your SendGrid account at https://app.sendgrid.com/settings/api_keys
|
| 5 |
+
SENDGRID_API_KEY=your_sendgrid_api_key_here
|
| 6 |
+
|
| 7 |
+
# Optional: AWS credentials for Bedrock (if not using IAM roles)
|
| 8 |
+
# AWS_ACCESS_KEY_ID=your_aws_access_key
|
| 9 |
+
# AWS_SECRET_ACCESS_KEY=your_aws_secret_key
|
| 10 |
+
# AWS_DEFAULT_REGION=us-west-2
|
| 11 |
+
|
| 12 |
+
# Optional: Other configuration
|
| 13 |
+
# BYPASS_TOOL_CONSENT=true
|
.gitignore
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Environment variables
|
| 2 |
+
.env
|
| 3 |
+
.env.local
|
| 4 |
+
.env.development.local
|
| 5 |
+
.env.test.local
|
| 6 |
+
.env.production.local
|
| 7 |
+
|
| 8 |
+
# Python
|
| 9 |
+
__pycache__/
|
| 10 |
+
*.py[cod]
|
| 11 |
+
*$py.class
|
| 12 |
+
*.so
|
| 13 |
+
.Python
|
| 14 |
+
build/
|
| 15 |
+
develop-eggs/
|
| 16 |
+
dist/
|
| 17 |
+
downloads/
|
| 18 |
+
eggs/
|
| 19 |
+
.eggs/
|
| 20 |
+
lib/
|
| 21 |
+
lib64/
|
| 22 |
+
parts/
|
| 23 |
+
sdist/
|
| 24 |
+
var/
|
| 25 |
+
wheels/
|
| 26 |
+
share/python-wheels/
|
| 27 |
+
*.egg-info/
|
| 28 |
+
.installed.cfg
|
| 29 |
+
*.egg
|
| 30 |
+
MANIFEST
|
| 31 |
+
|
| 32 |
+
# PyInstaller
|
| 33 |
+
*.manifest
|
| 34 |
+
*.spec
|
| 35 |
+
|
| 36 |
+
# Installer logs
|
| 37 |
+
pip-log.txt
|
| 38 |
+
pip-delete-this-directory.txt
|
| 39 |
+
|
| 40 |
+
# Unit test / coverage reports
|
| 41 |
+
htmlcov/
|
| 42 |
+
.tox/
|
| 43 |
+
.nox/
|
| 44 |
+
.coverage
|
| 45 |
+
.coverage.*
|
| 46 |
+
.cache
|
| 47 |
+
nosetests.xml
|
| 48 |
+
coverage.xml
|
| 49 |
+
*.cover
|
| 50 |
+
*.py,cover
|
| 51 |
+
.hypothesis/
|
| 52 |
+
.pytest_cache/
|
| 53 |
+
cover/
|
| 54 |
+
|
| 55 |
+
# Translations
|
| 56 |
+
*.mo
|
| 57 |
+
*.pot
|
| 58 |
+
|
| 59 |
+
# Django stuff:
|
| 60 |
+
*.log
|
| 61 |
+
local_settings.py
|
| 62 |
+
db.sqlite3
|
| 63 |
+
db.sqlite3-journal
|
| 64 |
+
|
| 65 |
+
# Flask stuff:
|
| 66 |
+
instance/
|
| 67 |
+
.webassets-cache
|
| 68 |
+
|
| 69 |
+
# Scrapy stuff:
|
| 70 |
+
.scrapy
|
| 71 |
+
|
| 72 |
+
# Sphinx documentation
|
| 73 |
+
docs/_build/
|
| 74 |
+
|
| 75 |
+
# PyBuilder
|
| 76 |
+
.pybuilder/
|
| 77 |
+
target/
|
| 78 |
+
|
| 79 |
+
# Jupyter Notebook
|
| 80 |
+
.ipynb_checkpoints
|
| 81 |
+
|
| 82 |
+
# IPython
|
| 83 |
+
profile_default/
|
| 84 |
+
ipython_config.py
|
| 85 |
+
|
| 86 |
+
# pyenv
|
| 87 |
+
.python-version
|
| 88 |
+
|
| 89 |
+
# pipenv
|
| 90 |
+
Pipfile.lock
|
| 91 |
+
|
| 92 |
+
# poetry
|
| 93 |
+
poetry.lock
|
| 94 |
+
|
| 95 |
+
# pdm
|
| 96 |
+
.pdm.toml
|
| 97 |
+
|
| 98 |
+
# PEP 582
|
| 99 |
+
__pypackages__/
|
| 100 |
+
|
| 101 |
+
# Celery stuff
|
| 102 |
+
celerybeat-schedule
|
| 103 |
+
celerybeat.pid
|
| 104 |
+
|
| 105 |
+
# SageMath parsed files
|
| 106 |
+
*.sage.py
|
| 107 |
+
|
| 108 |
+
# Environments
|
| 109 |
+
.env
|
| 110 |
+
.venv
|
| 111 |
+
env/
|
| 112 |
+
venv/
|
| 113 |
+
ENV/
|
| 114 |
+
env.bak/
|
| 115 |
+
venv.bak/
|
| 116 |
+
|
| 117 |
+
# Spyder project settings
|
| 118 |
+
.spyderproject
|
| 119 |
+
.spyproject
|
| 120 |
+
|
| 121 |
+
# Rope project settings
|
| 122 |
+
.ropeproject
|
| 123 |
+
|
| 124 |
+
# mkdocs documentation
|
| 125 |
+
/site
|
| 126 |
+
|
| 127 |
+
# mypy
|
| 128 |
+
.mypy_cache/
|
| 129 |
+
.dmypy.json
|
| 130 |
+
dmypy.json
|
| 131 |
+
|
| 132 |
+
# Pyre type checker
|
| 133 |
+
.pyre/
|
| 134 |
+
|
| 135 |
+
# pytype static type analyzer
|
| 136 |
+
.pytype/
|
| 137 |
+
|
| 138 |
+
# Cython debug symbols
|
| 139 |
+
cython_debug/
|
| 140 |
+
|
| 141 |
+
# PyCharm
|
| 142 |
+
.idea/
|
| 143 |
+
|
| 144 |
+
# VS Code
|
| 145 |
+
.vscode/
|
| 146 |
+
*.code-workspace
|
| 147 |
+
|
| 148 |
+
# Sublime Text
|
| 149 |
+
*.sublime-project
|
| 150 |
+
*.sublime-workspace
|
| 151 |
+
|
| 152 |
+
# Vim
|
| 153 |
+
*.swp
|
| 154 |
+
*.swo
|
| 155 |
+
*~
|
| 156 |
+
|
| 157 |
+
# Emacs
|
| 158 |
+
*~
|
| 159 |
+
\#*\#
|
| 160 |
+
/.emacs.desktop
|
| 161 |
+
/.emacs.desktop.lock
|
| 162 |
+
*.elc
|
| 163 |
+
auto-save-list
|
| 164 |
+
tramp
|
| 165 |
+
.\#*
|
| 166 |
+
|
| 167 |
+
# macOS
|
| 168 |
+
.DS_Store
|
| 169 |
+
.AppleDouble
|
| 170 |
+
.LSOverride
|
| 171 |
+
Icon?
|
| 172 |
+
._*
|
| 173 |
+
.DocumentRevisions-V100
|
| 174 |
+
.fseventsd
|
| 175 |
+
.Spotlight-V100
|
| 176 |
+
.TemporaryItems
|
| 177 |
+
.Trashes
|
| 178 |
+
.VolumeIcon.icns
|
| 179 |
+
.com.apple.timemachine.donotpresent
|
| 180 |
+
.AppleDB
|
| 181 |
+
.AppleDesktop
|
| 182 |
+
Network Trash Folder
|
| 183 |
+
Temporary Items
|
| 184 |
+
.apdisk
|
| 185 |
+
|
| 186 |
+
# Windows
|
| 187 |
+
Thumbs.db
|
| 188 |
+
Thumbs.db:encryptable
|
| 189 |
+
ehthumbs.db
|
| 190 |
+
ehthumbs_vista.db
|
| 191 |
+
*.tmp
|
| 192 |
+
*.temp
|
| 193 |
+
*.log
|
| 194 |
+
*.bak
|
| 195 |
+
*.swp
|
| 196 |
+
*.lnk
|
| 197 |
+
Desktop.ini
|
| 198 |
+
$RECYCLE.BIN/
|
| 199 |
+
*.cab
|
| 200 |
+
*.msi
|
| 201 |
+
*.msix
|
| 202 |
+
*.msm
|
| 203 |
+
*.msp
|
| 204 |
+
*.exe
|
| 205 |
+
|
| 206 |
+
# Linux
|
| 207 |
+
*~
|
| 208 |
+
.fuse_hidden*
|
| 209 |
+
.directory
|
| 210 |
+
.Trash-*
|
| 211 |
+
.nfs*
|
| 212 |
+
|
| 213 |
+
# Node.js (for any frontend dependencies)
|
| 214 |
+
node_modules/
|
| 215 |
+
npm-debug.log*
|
| 216 |
+
yarn-debug.log*
|
| 217 |
+
yarn-error.log*
|
| 218 |
+
|
| 219 |
+
# Logs
|
| 220 |
+
logs
|
| 221 |
+
*.log
|
| 222 |
+
npm-debug.log*
|
| 223 |
+
yarn-debug.log*
|
| 224 |
+
yarn-error.log*
|
| 225 |
+
lerna-debug.log*
|
| 226 |
+
.pnpm-debug.log*
|
| 227 |
+
|
| 228 |
+
# Runtime data
|
| 229 |
+
pids
|
| 230 |
+
*.pid
|
| 231 |
+
*.seed
|
| 232 |
+
*.pid.lock
|
| 233 |
+
|
| 234 |
+
# Directory for instrumented libs generated by jscoverage/JSCover
|
| 235 |
+
lib-cov
|
| 236 |
+
|
| 237 |
+
# Coverage directory used by tools like istanbul
|
| 238 |
+
coverage
|
| 239 |
+
*.lcov
|
| 240 |
+
|
| 241 |
+
# nyc test coverage
|
| 242 |
+
.nyc_output
|
| 243 |
+
|
| 244 |
+
# Dependency directories
|
| 245 |
+
node_modules/
|
| 246 |
+
jspm_packages/
|
| 247 |
+
|
| 248 |
+
# Optional npm cache directory
|
| 249 |
+
.npm
|
| 250 |
+
|
| 251 |
+
# Optional eslint cache
|
| 252 |
+
.eslintcache
|
| 253 |
+
|
| 254 |
+
# Microbundle cache
|
| 255 |
+
.rpt2_cache/
|
| 256 |
+
.rts2_cache_cjs/
|
| 257 |
+
.rts2_cache_es/
|
| 258 |
+
.rts2_cache_umd/
|
| 259 |
+
|
| 260 |
+
# Optional REPL history
|
| 261 |
+
.node_repl_history
|
| 262 |
+
|
| 263 |
+
# Output of 'npm pack'
|
| 264 |
+
*.tgz
|
| 265 |
+
|
| 266 |
+
# Yarn Integrity file
|
| 267 |
+
.yarn-integrity
|
| 268 |
+
|
| 269 |
+
# dotenv environment variables file
|
| 270 |
+
.env
|
| 271 |
+
.env.test
|
| 272 |
+
.env.production
|
| 273 |
+
|
| 274 |
+
# parcel-bundler cache (https://parceljs.org/)
|
| 275 |
+
.cache
|
| 276 |
+
.parcel-cache
|
| 277 |
+
|
| 278 |
+
# Next.js build output
|
| 279 |
+
.next
|
| 280 |
+
out
|
| 281 |
+
|
| 282 |
+
# Nuxt.js build / generate output
|
| 283 |
+
.nuxt
|
| 284 |
+
dist
|
| 285 |
+
|
| 286 |
+
# Gatsby files
|
| 287 |
+
.cache/
|
| 288 |
+
public
|
| 289 |
+
|
| 290 |
+
# Storybook build outputs
|
| 291 |
+
.out
|
| 292 |
+
.storybook-out
|
| 293 |
+
|
| 294 |
+
# Temporary folders
|
| 295 |
+
tmp/
|
| 296 |
+
temp/
|
| 297 |
+
|
| 298 |
+
# API keys and secrets (additional protection)
|
| 299 |
+
**/secrets/
|
| 300 |
+
**/secret/
|
| 301 |
+
**/*secret*
|
| 302 |
+
**/*key*
|
| 303 |
+
**/*token*
|
| 304 |
+
**/*password*
|
| 305 |
+
**/config/local.py
|
| 306 |
+
**/config/production.py
|
| 307 |
+
|
| 308 |
+
# Database files
|
| 309 |
+
*.db
|
| 310 |
+
*.sqlite
|
| 311 |
+
*.sqlite3
|
| 312 |
+
|
| 313 |
+
# AI/ML specific
|
| 314 |
+
*.model
|
| 315 |
+
*.pkl
|
| 316 |
+
*.pickle
|
| 317 |
+
model/
|
| 318 |
+
models/
|
| 319 |
+
checkpoints/
|
| 320 |
+
*.h5
|
| 321 |
+
*.hdf5
|
| 322 |
+
*.pt
|
| 323 |
+
*.pth
|
| 324 |
+
*.onnx
|
| 325 |
+
|
| 326 |
+
# Data files
|
| 327 |
+
data/
|
| 328 |
+
datasets/
|
| 329 |
+
*.csv
|
| 330 |
+
*.json
|
| 331 |
+
*.xml
|
| 332 |
+
*.xlsx
|
| 333 |
+
*.xls
|
| 334 |
+
!**/sample*.csv
|
| 335 |
+
!**/example*.json
|
| 336 |
+
|
| 337 |
+
# Backup files
|
| 338 |
+
*.backup
|
| 339 |
+
*.bak
|
| 340 |
+
*~
|
| 341 |
+
|
| 342 |
+
# Archive files
|
| 343 |
+
*.zip
|
| 344 |
+
*.tar
|
| 345 |
+
*.tar.gz
|
| 346 |
+
*.rar
|
| 347 |
+
*.7z
|
| 348 |
+
|
| 349 |
+
# Gradio
|
| 350 |
+
.gradio/
|
DEPLOYMENT.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Deployment Guide
|
| 2 |
+
|
| 3 |
+
## Hugging Face Spaces Deployment
|
| 4 |
+
|
| 5 |
+
### 1. Environment Variables
|
| 6 |
+
|
| 7 |
+
Set the following environment variables in your Hugging Face Space settings:
|
| 8 |
+
|
| 9 |
+
#### Required:
|
| 10 |
+
- `SENDGRID_API_KEY`: Your SendGrid API key for email functionality
|
| 11 |
+
- Get this from https://app.sendgrid.com/settings/api_keys
|
| 12 |
+
- Must have Mail Send permissions
|
| 13 |
+
|
| 14 |
+
#### Optional (for AWS Bedrock):
|
| 15 |
+
- `AWS_ACCESS_KEY_ID`: Your AWS access key
|
| 16 |
+
- `AWS_SECRET_ACCESS_KEY`: Your AWS secret key
|
| 17 |
+
- `AWS_DEFAULT_REGION`: AWS region (default: us-west-2)
|
| 18 |
+
|
| 19 |
+
### 2. Hugging Face Space Configuration
|
| 20 |
+
|
| 21 |
+
1. **Create a new Space** on Hugging Face
|
| 22 |
+
2. **Select Framework**: Gradio
|
| 23 |
+
3. **Add environment variables** in Space settings
|
| 24 |
+
4. **Upload your code** or connect via Git
|
| 25 |
+
|
| 26 |
+
### 3. Required Files
|
| 27 |
+
|
| 28 |
+
Ensure these files are in your repository:
|
| 29 |
+
- `app_api.py` - Main application entry point
|
| 30 |
+
- `get_tools_working.py` - Core agent logic
|
| 31 |
+
- `ai-agent-ui/` - Frontend UI files
|
| 32 |
+
- `requirements.txt` - Python dependencies
|
| 33 |
+
|
| 34 |
+
### 4. Entry Point
|
| 35 |
+
|
| 36 |
+
The main entry point is `app_api.py`. Hugging Face will automatically detect and run this file.
|
| 37 |
+
|
| 38 |
+
### 5. Access URLs
|
| 39 |
+
|
| 40 |
+
After deployment, your application will be available at:
|
| 41 |
+
- **Main API**: `https://your-space-name.hf.space/`
|
| 42 |
+
- **Frontend UI**: `https://your-space-name.hf.space/ai-agent-ui/`
|
| 43 |
+
|
| 44 |
+
### 6. Testing
|
| 45 |
+
|
| 46 |
+
1. Check that the server starts successfully
|
| 47 |
+
2. Test email functionality with a valid email address
|
| 48 |
+
3. Verify RSS feed processing works
|
| 49 |
+
4. Test the complete lead generation workflow
|
| 50 |
+
|
| 51 |
+
### 7. Troubleshooting
|
| 52 |
+
|
| 53 |
+
- **Email not sending**: Check SENDGRID_API_KEY is set correctly
|
| 54 |
+
- **AWS errors**: Verify AWS credentials and region
|
| 55 |
+
- **RSS errors**: Check network connectivity and feed URLs
|
| 56 |
+
- **UI not loading**: Ensure static files are properly served
|
| 57 |
+
|
| 58 |
+
### 8. Security Notes
|
| 59 |
+
|
| 60 |
+
- Never commit API keys to the repository
|
| 61 |
+
- Use environment variables for all sensitive configuration
|
| 62 |
+
- The application uses environment variables for secure deployment
|
_old_app.py
ADDED
|
@@ -0,0 +1,704 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
from get_tools_working import get_leadgen_agent_and_prompt
|
| 3 |
+
import re
|
| 4 |
+
|
| 5 |
+
# Initialize agent and default prompt
|
| 6 |
+
agent, default_prompt = get_leadgen_agent_and_prompt()
|
| 7 |
+
|
| 8 |
+
# Default RSS feeds
|
| 9 |
+
default_rss_feeds = """https://techfundingnews.com/feed
|
| 10 |
+
https://feeds.feedburner.com/vcnewsdaily
|
| 11 |
+
https://news.crunchbase.com/feed/"""
|
| 12 |
+
|
| 13 |
+
# Default user preferences
|
| 14 |
+
default_preferences = """Focus Areas:
|
| 15 |
+
- Tech startups and SaaS companies
|
| 16 |
+
- Series A and Series B funding rounds
|
| 17 |
+
- Companies in North America and Europe
|
| 18 |
+
|
| 19 |
+
Scoring Preferences:
|
| 20 |
+
- Prioritize companies with explicit hiring signals
|
| 21 |
+
- Higher scores for funding rounds above $10M
|
| 22 |
+
- Focus on B2B companies over B2C
|
| 23 |
+
- Prefer companies in high-growth sectors (AI, fintech, healthcare tech)"""
|
| 24 |
+
|
| 25 |
+
def validate_email(email):
|
| 26 |
+
"""Validate email format"""
|
| 27 |
+
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
|
| 28 |
+
return re.match(pattern, email) is not None
|
| 29 |
+
|
| 30 |
+
def parse_rss_urls(rss_text):
|
| 31 |
+
"""Parse RSS URLs from text input"""
|
| 32 |
+
urls = []
|
| 33 |
+
for line in rss_text.strip().split('\n'):
|
| 34 |
+
line = line.strip()
|
| 35 |
+
if line and (line.startswith('http://') or line.startswith('https://')):
|
| 36 |
+
urls.append(line)
|
| 37 |
+
return urls
|
| 38 |
+
|
| 39 |
+
def create_enhanced_prompt(system_prompt, rss_urls, preferences, email):
|
| 40 |
+
"""Create enhanced prompt with all user inputs"""
|
| 41 |
+
rss_url_list = parse_rss_urls(rss_urls)
|
| 42 |
+
rss_urls_str = ' '.join(rss_url_list)
|
| 43 |
+
|
| 44 |
+
enhanced_prompt = f"""RSS Feed URLs: {rss_urls_str}
|
| 45 |
+
|
| 46 |
+
{system_prompt}
|
| 47 |
+
|
| 48 |
+
User Preferences:
|
| 49 |
+
{preferences}
|
| 50 |
+
|
| 51 |
+
Send the report to: {email}"""
|
| 52 |
+
|
| 53 |
+
return enhanced_prompt
|
| 54 |
+
|
| 55 |
+
# Create custom professional theme using compatible properties
|
| 56 |
+
custom_theme = gr.themes.Soft(
|
| 57 |
+
primary_hue="blue",
|
| 58 |
+
secondary_hue="slate",
|
| 59 |
+
neutral_hue="slate",
|
| 60 |
+
text_size="sm",
|
| 61 |
+
spacing_size="md",
|
| 62 |
+
radius_size="md",
|
| 63 |
+
font=[
|
| 64 |
+
gr.themes.GoogleFont("Inter"),
|
| 65 |
+
"ui-sans-serif",
|
| 66 |
+
"system-ui",
|
| 67 |
+
"sans-serif"
|
| 68 |
+
],
|
| 69 |
+
font_mono=[
|
| 70 |
+
gr.themes.GoogleFont("JetBrains Mono"),
|
| 71 |
+
"ui-monospace",
|
| 72 |
+
"Consolas",
|
| 73 |
+
"monospace"
|
| 74 |
+
]
|
| 75 |
+
)
|
| 76 |
+
|
| 77 |
+
# Professional CSS with animations and modern styling
|
| 78 |
+
css = """
|
| 79 |
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
| 80 |
+
|
| 81 |
+
/* Global container styling */
|
| 82 |
+
.gradio-container {
|
| 83 |
+
max-width: 1400px !important;
|
| 84 |
+
margin: 0 auto !important;
|
| 85 |
+
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%) !important;
|
| 86 |
+
min-height: 100vh;
|
| 87 |
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
/* Animated header with professional styling */
|
| 91 |
+
.professional-header {
|
| 92 |
+
background: linear-gradient(135deg, #1e293b 0%, #334155 50%, #475569 100%);
|
| 93 |
+
padding: 48px 32px;
|
| 94 |
+
margin: -20px -20px 32px -20px;
|
| 95 |
+
border-radius: 0 0 24px 24px;
|
| 96 |
+
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
| 97 |
+
text-align: center;
|
| 98 |
+
position: relative;
|
| 99 |
+
overflow: hidden;
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
.professional-header::before {
|
| 103 |
+
content: '';
|
| 104 |
+
position: absolute;
|
| 105 |
+
top: 0;
|
| 106 |
+
left: 0;
|
| 107 |
+
right: 0;
|
| 108 |
+
bottom: 0;
|
| 109 |
+
background: radial-gradient(circle at 30% 20%, rgba(59, 130, 246, 0.3) 0%, transparent 50%),
|
| 110 |
+
radial-gradient(circle at 70% 80%, rgba(139, 92, 246, 0.2) 0%, transparent 50%);
|
| 111 |
+
animation: pulse-gradient 6s ease-in-out infinite;
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
@keyframes pulse-gradient {
|
| 115 |
+
0%, 100% { opacity: 0.5; }
|
| 116 |
+
50% { opacity: 0.8; }
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
.professional-header h1 {
|
| 120 |
+
color: white;
|
| 121 |
+
font-size: 3.5rem;
|
| 122 |
+
font-weight: 700;
|
| 123 |
+
margin: 0 0 16px 0;
|
| 124 |
+
text-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
| 125 |
+
position: relative;
|
| 126 |
+
z-index: 2;
|
| 127 |
+
letter-spacing: -0.025em;
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
.professional-header p {
|
| 131 |
+
color: rgba(255, 255, 255, 0.9);
|
| 132 |
+
font-size: 1.25rem;
|
| 133 |
+
margin: 0;
|
| 134 |
+
font-weight: 400;
|
| 135 |
+
position: relative;
|
| 136 |
+
z-index: 2;
|
| 137 |
+
line-height: 1.6;
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
/* Professional card styling */
|
| 141 |
+
.config-section {
|
| 142 |
+
background: white;
|
| 143 |
+
border-radius: 8px;
|
| 144 |
+
padding: 20px;
|
| 145 |
+
margin: 12px 0;
|
| 146 |
+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
| 147 |
+
border: 1px solid rgba(226, 232, 240, 0.8);
|
| 148 |
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
| 149 |
+
position: relative;
|
| 150 |
+
overflow: hidden;
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
.config-section::before {
|
| 154 |
+
content: '';
|
| 155 |
+
position: absolute;
|
| 156 |
+
top: 0;
|
| 157 |
+
left: 0;
|
| 158 |
+
right: 0;
|
| 159 |
+
height: 4px;
|
| 160 |
+
background: linear-gradient(90deg, #3b82f6, #8b5cf6);
|
| 161 |
+
border-radius: 8px 8px 0 0;
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
.config-section:hover {
|
| 165 |
+
transform: translateY(-2px);
|
| 166 |
+
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
.section-header {
|
| 170 |
+
display: flex;
|
| 171 |
+
align-items: center;
|
| 172 |
+
gap: 12px;
|
| 173 |
+
font-size: 1.25rem;
|
| 174 |
+
font-weight: 600;
|
| 175 |
+
color: #1e293b;
|
| 176 |
+
margin-bottom: 16px;
|
| 177 |
+
padding-bottom: 10px;
|
| 178 |
+
border-bottom: 2px solid #f1f5f9;
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
.section-header .emoji {
|
| 182 |
+
font-size: 1.5rem;
|
| 183 |
+
filter: none;
|
| 184 |
+
background: none;
|
| 185 |
+
-webkit-background-clip: unset;
|
| 186 |
+
-webkit-text-fill-color: unset;
|
| 187 |
+
background-clip: unset;
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
/* Info boxes with modern styling */
|
| 191 |
+
.info-card {
|
| 192 |
+
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
|
| 193 |
+
border: 1px solid #e2e8f0;
|
| 194 |
+
border-left: 4px solid #3b82f6;
|
| 195 |
+
padding: 16px 20px;
|
| 196 |
+
margin: 16px 0;
|
| 197 |
+
border-radius: 8px;
|
| 198 |
+
font-size: 0.875rem;
|
| 199 |
+
line-height: 1.6;
|
| 200 |
+
color: #475569;
|
| 201 |
+
position: relative;
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
.info-card::before {
|
| 205 |
+
content: '💡';
|
| 206 |
+
position: absolute;
|
| 207 |
+
top: 16px;
|
| 208 |
+
right: 20px;
|
| 209 |
+
font-size: 1.25rem;
|
| 210 |
+
opacity: 0.6;
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
/* Status indicators */
|
| 214 |
+
.status-indicator {
|
| 215 |
+
padding: 12px 20px;
|
| 216 |
+
border-radius: 12px;
|
| 217 |
+
margin: 16px 0;
|
| 218 |
+
font-weight: 500;
|
| 219 |
+
font-size: 0.9rem;
|
| 220 |
+
display: flex;
|
| 221 |
+
align-items: center;
|
| 222 |
+
gap: 8px;
|
| 223 |
+
transition: all 0.3s ease;
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
.status-success {
|
| 227 |
+
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
| 228 |
+
color: white;
|
| 229 |
+
box-shadow: 0 4px 14px 0 rgba(16, 185, 129, 0.4);
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
.status-error {
|
| 233 |
+
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
|
| 234 |
+
color: white;
|
| 235 |
+
box-shadow: 0 4px 14px 0 rgba(239, 68, 68, 0.4);
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
+
.status-info {
|
| 239 |
+
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
|
| 240 |
+
color: white;
|
| 241 |
+
box-shadow: 0 4px 14px 0 rgba(59, 130, 246, 0.4);
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
/* Enhanced button styling */
|
| 245 |
+
.primary-button {
|
| 246 |
+
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%) !important;
|
| 247 |
+
color: white !important;
|
| 248 |
+
font-weight: 600 !important;
|
| 249 |
+
padding: 10px 20px !important;
|
| 250 |
+
border-radius: 6px !important;
|
| 251 |
+
border: none !important;
|
| 252 |
+
font-size: 0.9rem !important;
|
| 253 |
+
box-shadow: 0 4px 14px 0 rgba(59, 130, 246, 0.4) !important;
|
| 254 |
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
|
| 255 |
+
position: relative !important;
|
| 256 |
+
overflow: hidden !important;
|
| 257 |
+
margin: 0 0 20px 0 !important;
|
| 258 |
+
display: block !important;
|
| 259 |
+
}
|
| 260 |
+
|
| 261 |
+
/* Fix button container alignment */
|
| 262 |
+
.primary-button {
|
| 263 |
+
width: 100% !important;
|
| 264 |
+
min-width: 0 !important;
|
| 265 |
+
flex-shrink: 1 !important;
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
.primary-button::before {
|
| 269 |
+
content: '';
|
| 270 |
+
position: absolute;
|
| 271 |
+
top: 0;
|
| 272 |
+
left: -100%;
|
| 273 |
+
width: 100%;
|
| 274 |
+
height: 100%;
|
| 275 |
+
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
| 276 |
+
transition: left 0.5s;
|
| 277 |
+
}
|
| 278 |
+
|
| 279 |
+
.primary-button:hover {
|
| 280 |
+
background: linear-gradient(135deg, #2563eb 0%, #1e40af 100%) !important;
|
| 281 |
+
transform: translateY(-2px) !important;
|
| 282 |
+
box-shadow: 0 8px 25px -5px rgba(59, 130, 246, 0.6) !important;
|
| 283 |
+
}
|
| 284 |
+
|
| 285 |
+
.primary-button:hover::before {
|
| 286 |
+
left: 100%;
|
| 287 |
+
}
|
| 288 |
+
|
| 289 |
+
/* Control panel styling */
|
| 290 |
+
.control-panel {
|
| 291 |
+
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
|
| 292 |
+
border-radius: 16px;
|
| 293 |
+
padding: 18px;
|
| 294 |
+
border: 1px solid #e2e8f0;
|
| 295 |
+
box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
|
| 296 |
+
}
|
| 297 |
+
|
| 298 |
+
/* Enhanced input styling */
|
| 299 |
+
.gradio-textbox, .gradio-dropdown {
|
| 300 |
+
border-radius: 8px !important;
|
| 301 |
+
border: 1px solid #d1d5db !important;
|
| 302 |
+
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1) !important;
|
| 303 |
+
transition: all 0.3s ease !important;
|
| 304 |
+
}
|
| 305 |
+
|
| 306 |
+
.gradio-textbox:focus, .gradio-dropdown:focus {
|
| 307 |
+
border-color: #3b82f6 !important;
|
| 308 |
+
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1) !important;
|
| 309 |
+
outline: none !important;
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
/* Progress indicators */
|
| 313 |
+
.progress-card {
|
| 314 |
+
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
|
| 315 |
+
border: 1px solid #e2e8f0;
|
| 316 |
+
border-left: 4px solid #3b82f6;
|
| 317 |
+
padding: 16px;
|
| 318 |
+
margin: 16px 0;
|
| 319 |
+
border-radius: 8px;
|
| 320 |
+
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.06);
|
| 321 |
+
}
|
| 322 |
+
|
| 323 |
+
/* Chatbot styling */
|
| 324 |
+
.chatbot-container {
|
| 325 |
+
background: white !important;
|
| 326 |
+
border-radius: 16px !important;
|
| 327 |
+
border: 1px solid #e2e8f0 !important;
|
| 328 |
+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1) !important;
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
/* Animation for loading states */
|
| 332 |
+
@keyframes shimmer {
|
| 333 |
+
0% { transform: translateX(-100%); }
|
| 334 |
+
100% { transform: translateX(100%); }
|
| 335 |
+
}
|
| 336 |
+
|
| 337 |
+
.loading-shimmer {
|
| 338 |
+
position: relative;
|
| 339 |
+
overflow: hidden;
|
| 340 |
+
}
|
| 341 |
+
|
| 342 |
+
.loading-shimmer::after {
|
| 343 |
+
content: '';
|
| 344 |
+
position: absolute;
|
| 345 |
+
top: 0;
|
| 346 |
+
right: 0;
|
| 347 |
+
bottom: 0;
|
| 348 |
+
left: 0;
|
| 349 |
+
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.6), transparent);
|
| 350 |
+
animation: shimmer 2s infinite;
|
| 351 |
+
}
|
| 352 |
+
|
| 353 |
+
/* Responsive design */
|
| 354 |
+
@media (max-width: 768px) {
|
| 355 |
+
.professional-header h1 {
|
| 356 |
+
font-size: 2.5rem;
|
| 357 |
+
}
|
| 358 |
+
|
| 359 |
+
.config-section {
|
| 360 |
+
padding: 20px;
|
| 361 |
+
margin: 12px 0;
|
| 362 |
+
}
|
| 363 |
+
}
|
| 364 |
+
|
| 365 |
+
/* Custom scrollbar */
|
| 366 |
+
::-webkit-scrollbar {
|
| 367 |
+
width: 8px;
|
| 368 |
+
}
|
| 369 |
+
|
| 370 |
+
::-webkit-scrollbar-track {
|
| 371 |
+
background: #f1f5f9;
|
| 372 |
+
border-radius: 4px;
|
| 373 |
+
}
|
| 374 |
+
|
| 375 |
+
::-webkit-scrollbar-thumb {
|
| 376 |
+
background: linear-gradient(135deg, #3b82f6, #8b5cf6);
|
| 377 |
+
border-radius: 4px;
|
| 378 |
+
}
|
| 379 |
+
|
| 380 |
+
::-webkit-scrollbar-thumb:hover {
|
| 381 |
+
background: linear-gradient(135deg, #2563eb, #7c3aed);
|
| 382 |
+
}
|
| 383 |
+
"""
|
| 384 |
+
|
| 385 |
+
# JavaScript for enhanced interactions
|
| 386 |
+
js = """
|
| 387 |
+
function addInteractiveEffects() {
|
| 388 |
+
// Add ripple effect to buttons
|
| 389 |
+
document.addEventListener('click', function(e) {
|
| 390 |
+
if (e.target.classList.contains('primary-button')) {
|
| 391 |
+
const ripple = document.createElement('span');
|
| 392 |
+
const rect = e.target.getBoundingClientRect();
|
| 393 |
+
const size = Math.max(rect.width, rect.height);
|
| 394 |
+
const x = e.clientX - rect.left - size / 2;
|
| 395 |
+
const y = e.clientY - rect.top - size / 2;
|
| 396 |
+
|
| 397 |
+
ripple.style.width = ripple.style.height = size + 'px';
|
| 398 |
+
ripple.style.left = x + 'px';
|
| 399 |
+
ripple.style.top = y + 'px';
|
| 400 |
+
ripple.classList.add('ripple');
|
| 401 |
+
|
| 402 |
+
e.target.appendChild(ripple);
|
| 403 |
+
|
| 404 |
+
setTimeout(() => {
|
| 405 |
+
ripple.remove();
|
| 406 |
+
}, 600);
|
| 407 |
+
}
|
| 408 |
+
});
|
| 409 |
+
|
| 410 |
+
// Add smooth scrolling for better UX
|
| 411 |
+
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
| 412 |
+
anchor.addEventListener('click', function (e) {
|
| 413 |
+
e.preventDefault();
|
| 414 |
+
const target = document.querySelector(this.getAttribute('href'));
|
| 415 |
+
if (target) {
|
| 416 |
+
target.scrollIntoView({
|
| 417 |
+
behavior: 'smooth',
|
| 418 |
+
block: 'start'
|
| 419 |
+
});
|
| 420 |
+
}
|
| 421 |
+
});
|
| 422 |
+
});
|
| 423 |
+
|
| 424 |
+
return "Interactive effects initialized";
|
| 425 |
+
}
|
| 426 |
+
"""
|
| 427 |
+
|
| 428 |
+
# Create the professional Gradio interface
|
| 429 |
+
with gr.Blocks(
|
| 430 |
+
css=css,
|
| 431 |
+
js=js,
|
| 432 |
+
title="🎯 AI Lead Generation Agent - Professional Dashboard",
|
| 433 |
+
theme=custom_theme
|
| 434 |
+
) as demo:
|
| 435 |
+
|
| 436 |
+
# Header
|
| 437 |
+
gr.HTML("""
|
| 438 |
+
<div class="professional-header">
|
| 439 |
+
<h1><span class="emoji">🎯</span> AI Lead Generation Agent</h1>
|
| 440 |
+
<p>Automated lead generation from funding announcements and hiring signals</p>
|
| 441 |
+
</div>
|
| 442 |
+
""")
|
| 443 |
+
|
| 444 |
+
with gr.Row():
|
| 445 |
+
with gr.Column(scale=2):
|
| 446 |
+
# Email Configuration Section
|
| 447 |
+
with gr.Group(elem_classes=["config-section"]):
|
| 448 |
+
gr.HTML('<div class="section-header"><span class="emoji">📧</span>Email Configuration</div>')
|
| 449 |
+
gr.HTML('<div class="info-card">Enter your email address to receive lead generation reports.</div>')
|
| 450 |
+
|
| 451 |
+
email_input = gr.Textbox(
|
| 452 |
+
label="Email Address",
|
| 453 |
+
placeholder="your.email@company.com",
|
| 454 |
+
info="Enter a valid email address for report delivery",
|
| 455 |
+
elem_id="email-input"
|
| 456 |
+
)
|
| 457 |
+
|
| 458 |
+
# RSS Feed Configuration Section
|
| 459 |
+
with gr.Group(elem_classes=["config-section"]):
|
| 460 |
+
gr.HTML('<div class="section-header"><span class="emoji">📡</span>Data Sources</div>')
|
| 461 |
+
gr.HTML('<div class="info-card">Configure RSS feed sources for monitoring funding announcements and hiring signals.</div>')
|
| 462 |
+
|
| 463 |
+
rss_feeds_input = gr.Textbox(
|
| 464 |
+
label="RSS Feed URLs",
|
| 465 |
+
value=default_rss_feeds,
|
| 466 |
+
lines=6,
|
| 467 |
+
placeholder="https://example.com/feed\nhttps://another-source.com/rss",
|
| 468 |
+
info="One URL per line"
|
| 469 |
+
)
|
| 470 |
+
|
| 471 |
+
# AI Configuration Section
|
| 472 |
+
with gr.Group(elem_classes=["config-section"]):
|
| 473 |
+
gr.HTML('<div class="section-header"><span class="emoji">🤖</span>AI Configuration</div>')
|
| 474 |
+
gr.HTML('<div class="info-card">Customize AI instructions for lead scoring and analysis. The default prompt is optimized for recruitment use cases.</div>')
|
| 475 |
+
|
| 476 |
+
system_prompt_input = gr.Textbox(
|
| 477 |
+
label="AI System Prompt",
|
| 478 |
+
value=default_prompt,
|
| 479 |
+
lines=12,
|
| 480 |
+
info="Modify AI behavior and scoring criteria"
|
| 481 |
+
)
|
| 482 |
+
|
| 483 |
+
# Lead Preferences Section
|
| 484 |
+
with gr.Group(elem_classes=["config-section"]):
|
| 485 |
+
gr.HTML('<div class="section-header"><span class="emoji">⚙️</span>Lead Targeting</div>')
|
| 486 |
+
gr.HTML('<div class="info-card">Define your ideal customer profile and lead scoring preferences for maximum relevance.</div>')
|
| 487 |
+
|
| 488 |
+
preferences_input = gr.Textbox(
|
| 489 |
+
label="Targeting Preferences",
|
| 490 |
+
value=default_preferences,
|
| 491 |
+
lines=8,
|
| 492 |
+
placeholder="Define your target criteria...",
|
| 493 |
+
info="Specify industries, funding stages, geographic regions, and other targeting criteria"
|
| 494 |
+
)
|
| 495 |
+
|
| 496 |
+
with gr.Column(scale=1):
|
| 497 |
+
# Control Panel
|
| 498 |
+
with gr.Group(elem_classes=["control-panel"]):
|
| 499 |
+
gr.HTML('<div class="section-header"><span class="emoji">🚀</span>Mission Control</div>')
|
| 500 |
+
|
| 501 |
+
# Status display
|
| 502 |
+
status_display = gr.HTML('<div class="info-card">Ready to generate leads. Configure your settings and launch the analysis.</div>')
|
| 503 |
+
|
| 504 |
+
# Generate button
|
| 505 |
+
generate_btn = gr.Button(
|
| 506 |
+
"🎯 Generate Report",
|
| 507 |
+
variant="primary",
|
| 508 |
+
size="md",
|
| 509 |
+
elem_classes=["primary-button"]
|
| 510 |
+
)
|
| 511 |
+
|
| 512 |
+
# Progress section
|
| 513 |
+
with gr.Group(elem_classes=["progress-card"]):
|
| 514 |
+
gr.HTML('<div class="section-header" style="margin-bottom: 12px;"><span class="emoji">📊</span>Progress Monitor</div>')
|
| 515 |
+
|
| 516 |
+
progress_display = gr.Textbox(
|
| 517 |
+
label="Status",
|
| 518 |
+
value="Awaiting launch command...",
|
| 519 |
+
interactive=False,
|
| 520 |
+
lines=2,
|
| 521 |
+
elem_id="progress-monitor"
|
| 522 |
+
)
|
| 523 |
+
|
| 524 |
+
# Intelligence Panel
|
| 525 |
+
gr.HTML('''
|
| 526 |
+
<div class="info-card" style="margin-top: 20px;">
|
| 527 |
+
<strong>🎯 Intelligence Capabilities:</strong><br/>
|
| 528 |
+
• Real-time RSS feed monitoring<br/>
|
| 529 |
+
• AI-powered company analysis<br/>
|
| 530 |
+
• Hiring signal detection<br/>
|
| 531 |
+
• Lead scoring & prioritization<br/>
|
| 532 |
+
• Professional report generation
|
| 533 |
+
</div>
|
| 534 |
+
''')
|
| 535 |
+
|
| 536 |
+
# Best Practices
|
| 537 |
+
gr.HTML('''
|
| 538 |
+
<div class="info-card">
|
| 539 |
+
<strong>📈 Best Practices:</strong><br/>
|
| 540 |
+
• Use industry-specific RSS feeds<br/>
|
| 541 |
+
• Update preferences regularly<br/>
|
| 542 |
+
• Monitor funding announcement timing<br/>
|
| 543 |
+
• Focus on growth-stage companies<br/>
|
| 544 |
+
• Track geographic preferences
|
| 545 |
+
</div>
|
| 546 |
+
''')
|
| 547 |
+
|
| 548 |
+
# Results Section
|
| 549 |
+
with gr.Group(elem_classes=["config-section"]):
|
| 550 |
+
gr.HTML('<div class="section-header"><span class="emoji">💬</span>AI Agent Activity & Intelligence Feed</div>')
|
| 551 |
+
gr.HTML('<div class="info-card">Monitor real-time AI agent activity, analysis progress, and detailed lead intelligence results.</div>')
|
| 552 |
+
|
| 553 |
+
chatbot = gr.Chatbot(
|
| 554 |
+
label="Lead Intelligence Stream",
|
| 555 |
+
type='messages',
|
| 556 |
+
height=450,
|
| 557 |
+
show_label=True,
|
| 558 |
+
elem_classes=["chatbot-container"],
|
| 559 |
+
placeholder="AI agent will display live analysis and results here..."
|
| 560 |
+
)
|
| 561 |
+
|
| 562 |
+
# State management
|
| 563 |
+
state = gr.State({"email": "", "active": False})
|
| 564 |
+
|
| 565 |
+
def validate_and_generate(email, rss_feeds, system_prompt, preferences, current_state):
|
| 566 |
+
"""Validate inputs and generate lead report with enhanced UX"""
|
| 567 |
+
|
| 568 |
+
# Reset chatbot
|
| 569 |
+
chat_history = []
|
| 570 |
+
|
| 571 |
+
# Enhanced validation with better feedback
|
| 572 |
+
if not email or not validate_email(email):
|
| 573 |
+
status = '<div class="status-indicator status-error">❌ Please enter a valid email address</div>'
|
| 574 |
+
progress = "❌ Validation Error: Invalid email format"
|
| 575 |
+
chat_history.append({
|
| 576 |
+
"role": "assistant",
|
| 577 |
+
"content": "❌ **Validation Error**\n\nPlease provide a valid email address to receive your lead intelligence report."
|
| 578 |
+
})
|
| 579 |
+
return status, progress, chat_history, current_state
|
| 580 |
+
|
| 581 |
+
rss_urls = parse_rss_urls(rss_feeds)
|
| 582 |
+
if not rss_urls:
|
| 583 |
+
status = '<div class="status-indicator status-error">❌ Please configure at least one RSS feed source</div>'
|
| 584 |
+
progress = "❌ Configuration Error: No RSS sources"
|
| 585 |
+
chat_history.append({
|
| 586 |
+
"role": "assistant",
|
| 587 |
+
"content": "❌ **Configuration Error**\n\nAt least one valid RSS feed URL is required for lead generation."
|
| 588 |
+
})
|
| 589 |
+
return status, progress, chat_history, current_state
|
| 590 |
+
|
| 591 |
+
if not system_prompt.strip():
|
| 592 |
+
status = '<div class="status-indicator status-error">❌ AI system prompt cannot be empty</div>'
|
| 593 |
+
progress = "❌ Configuration Error: Missing AI prompt"
|
| 594 |
+
chat_history.append({
|
| 595 |
+
"role": "assistant",
|
| 596 |
+
"content": "❌ **Configuration Error**\n\nAI system prompt is required for intelligent lead analysis."
|
| 597 |
+
})
|
| 598 |
+
return status, progress, chat_history, current_state
|
| 599 |
+
|
| 600 |
+
# Update state
|
| 601 |
+
current_state["email"] = email
|
| 602 |
+
current_state["active"] = True
|
| 603 |
+
|
| 604 |
+
# Enhanced progress feedback
|
| 605 |
+
status = '<div class="status-indicator status-success">✅ All systems validated. Initiating AI lead generation...</div>'
|
| 606 |
+
progress = f"🚀 Launching intelligence gathering from {len(rss_urls)} premium sources..."
|
| 607 |
+
|
| 608 |
+
chat_history.append({
|
| 609 |
+
"role": "assistant",
|
| 610 |
+
"content": f"""🚀 **Lead Generation Mission Initiated**
|
| 611 |
+
|
| 612 |
+
📧 **Target Email:** {email}
|
| 613 |
+
📡 **Data Sources:** {len(rss_urls)} RSS feeds configured
|
| 614 |
+
🎯 **Analysis Mode:** Advanced lead scoring enabled
|
| 615 |
+
🤖 **AI Status:** Ready for intelligent analysis
|
| 616 |
+
|
| 617 |
+
Initiating real-time monitoring and analysis..."""
|
| 618 |
+
})
|
| 619 |
+
|
| 620 |
+
# Create enhanced prompt
|
| 621 |
+
enhanced_prompt = create_enhanced_prompt(system_prompt, rss_feeds, preferences, email)
|
| 622 |
+
|
| 623 |
+
try:
|
| 624 |
+
# Run the agent with enhanced feedback
|
| 625 |
+
chat_history.append({
|
| 626 |
+
"role": "user",
|
| 627 |
+
"content": "🎯 Execute lead generation with current configuration"
|
| 628 |
+
})
|
| 629 |
+
|
| 630 |
+
progress = "🤖 AI Agent processing feeds • Analyzing companies • Scoring leads..."
|
| 631 |
+
|
| 632 |
+
result = agent(enhanced_prompt)
|
| 633 |
+
response = str(result)
|
| 634 |
+
|
| 635 |
+
chat_history.append({
|
| 636 |
+
"role": "assistant",
|
| 637 |
+
"content": f"""✅ **Lead Generation Mission Completed Successfully!**
|
| 638 |
+
|
| 639 |
+
🎯 **Results Summary:**
|
| 640 |
+
{response}
|
| 641 |
+
|
| 642 |
+
📧 **Report Status:** Professional lead intelligence report has been generated and sent to {email}
|
| 643 |
+
|
| 644 |
+
📊 **Next Steps:**
|
| 645 |
+
• Check your email for the detailed report
|
| 646 |
+
• Review lead scores and prioritize outreach
|
| 647 |
+
• Monitor for new funding announcements
|
| 648 |
+
• Update targeting criteria based on results"""
|
| 649 |
+
})
|
| 650 |
+
|
| 651 |
+
status = '<div class="status-indicator status-success">✅ Mission accomplished! Lead intelligence report delivered successfully.</div>'
|
| 652 |
+
progress = f"✅ Complete! Professional report sent to {email}"
|
| 653 |
+
|
| 654 |
+
except Exception as e:
|
| 655 |
+
error_msg = f"""❌ **Mission Error Encountered**
|
| 656 |
+
|
| 657 |
+
**Error Details:** {str(e)}
|
| 658 |
+
|
| 659 |
+
**Troubleshooting Steps:**
|
| 660 |
+
• Verify RSS feed accessibility
|
| 661 |
+
• Check email configuration
|
| 662 |
+
• Ensure stable internet connection
|
| 663 |
+
• Try again with simplified parameters"""
|
| 664 |
+
|
| 665 |
+
chat_history.append({"role": "assistant", "content": error_msg})
|
| 666 |
+
|
| 667 |
+
status = '<div class="status-indicator status-error">❌ Mission error - please review configuration and try again</div>'
|
| 668 |
+
progress = f"❌ Error: {str(e)}"
|
| 669 |
+
|
| 670 |
+
return status, progress, chat_history, current_state
|
| 671 |
+
|
| 672 |
+
# Event handlers with enhanced functionality
|
| 673 |
+
generate_btn.click(
|
| 674 |
+
validate_and_generate,
|
| 675 |
+
inputs=[email_input, rss_feeds_input, system_prompt_input, preferences_input, state],
|
| 676 |
+
outputs=[status_display, progress_display, chatbot, state],
|
| 677 |
+
queue=True
|
| 678 |
+
)
|
| 679 |
+
|
| 680 |
+
# Real-time email validation
|
| 681 |
+
def update_email_status(email):
|
| 682 |
+
if not email:
|
| 683 |
+
return '<div class="info-card">Enter your email address to receive lead reports.</div>'
|
| 684 |
+
elif validate_email(email):
|
| 685 |
+
return '<div class="status-indicator status-success">✅ Valid email address configured</div>'
|
| 686 |
+
else:
|
| 687 |
+
return '<div class="status-indicator status-error">❌ Invalid email format - please correct</div>'
|
| 688 |
+
|
| 689 |
+
email_input.change(
|
| 690 |
+
update_email_status,
|
| 691 |
+
inputs=[email_input],
|
| 692 |
+
outputs=[status_display]
|
| 693 |
+
)
|
| 694 |
+
|
| 695 |
+
if __name__ == "__main__":
|
| 696 |
+
print("🚀 Launching Professional AI Lead Generation Agent...")
|
| 697 |
+
demo.launch(
|
| 698 |
+
share=False,
|
| 699 |
+
server_name="0.0.0.0",
|
| 700 |
+
server_port=7860,
|
| 701 |
+
show_error=True,
|
| 702 |
+
favicon_path=None,
|
| 703 |
+
show_api=False
|
| 704 |
+
)
|
ai-agent-ui/index.html
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>🎯 AI Lead Generation Agent - Professional Dashboard</title>
|
| 7 |
+
|
| 8 |
+
<!-- Material UI CSS -->
|
| 9 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
| 10 |
+
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
| 11 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/material-design-lite/1.3.0/material.indigo-pink.min.css">
|
| 12 |
+
|
| 13 |
+
<!-- Custom CSS -->
|
| 14 |
+
<link rel="stylesheet" href="style.css">
|
| 15 |
+
</head>
|
| 16 |
+
<body>
|
| 17 |
+
<!-- Header Section -->
|
| 18 |
+
<header class="professional-header">
|
| 19 |
+
<div class="header-content">
|
| 20 |
+
<h1><span class="material-icons">track_changes</span> AI Lead Generation Agent</h1>
|
| 21 |
+
<p>Automated lead generation from funding announcements and hiring signals</p>
|
| 22 |
+
</div>
|
| 23 |
+
</header>
|
| 24 |
+
|
| 25 |
+
<!-- Main Content -->
|
| 26 |
+
<main class="main-container">
|
| 27 |
+
<div class="content-grid">
|
| 28 |
+
<!-- Left Column - Configuration -->
|
| 29 |
+
<div class="config-column">
|
| 30 |
+
<!-- Email Configuration -->
|
| 31 |
+
<div class="config-section">
|
| 32 |
+
<div class="section-header">
|
| 33 |
+
<span class="material-icons">email</span>
|
| 34 |
+
<h3>Email Configuration</h3>
|
| 35 |
+
</div>
|
| 36 |
+
<div class="info-card">
|
| 37 |
+
<span class="material-icons">info</span>
|
| 38 |
+
Enter your email address to receive lead generation reports.
|
| 39 |
+
</div>
|
| 40 |
+
<div class="input-group">
|
| 41 |
+
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
|
| 42 |
+
<input class="mdl-textfield__input" type="email" id="email-input" required>
|
| 43 |
+
<label class="mdl-textfield__label" for="email-input">Email Address</label>
|
| 44 |
+
<span class="mdl-textfield__error">Please enter a valid email address</span>
|
| 45 |
+
</div>
|
| 46 |
+
<div class="input-helper">Enter a valid email address for report delivery</div>
|
| 47 |
+
</div>
|
| 48 |
+
</div>
|
| 49 |
+
|
| 50 |
+
<!-- RSS Feed Configuration -->
|
| 51 |
+
<div class="config-section">
|
| 52 |
+
<div class="section-header">
|
| 53 |
+
<span class="material-icons">rss_feed</span>
|
| 54 |
+
<h3>Data Sources</h3>
|
| 55 |
+
</div>
|
| 56 |
+
<div class="info-card">
|
| 57 |
+
<span class="material-icons">info</span>
|
| 58 |
+
Configure RSS feed sources for monitoring funding announcements and hiring signals.
|
| 59 |
+
</div>
|
| 60 |
+
<div class="input-group">
|
| 61 |
+
<div class="mdl-textfield mdl-js-textfield">
|
| 62 |
+
<textarea class="mdl-textfield__input" type="text" rows="6" id="rss-feeds" required></textarea>
|
| 63 |
+
<label class="mdl-textfield__label" for="rss-feeds">RSS Feed URLs</label>
|
| 64 |
+
</div>
|
| 65 |
+
<div class="input-helper">One URL per line</div>
|
| 66 |
+
</div>
|
| 67 |
+
</div>
|
| 68 |
+
|
| 69 |
+
<!-- AI Configuration -->
|
| 70 |
+
<div class="config-section">
|
| 71 |
+
<div class="section-header">
|
| 72 |
+
<span class="material-icons">smart_toy</span>
|
| 73 |
+
<h3>AI Configuration</h3>
|
| 74 |
+
</div>
|
| 75 |
+
<div class="info-card">
|
| 76 |
+
<span class="material-icons">info</span>
|
| 77 |
+
Customize AI instructions for lead scoring and analysis. The default prompt is optimized for recruitment use cases.
|
| 78 |
+
</div>
|
| 79 |
+
<div class="input-group">
|
| 80 |
+
<div class="mdl-textfield mdl-js-textfield">
|
| 81 |
+
<textarea class="mdl-textfield__input" type="text" rows="12" id="system-prompt" required></textarea>
|
| 82 |
+
<label class="mdl-textfield__label" for="system-prompt">AI System Prompt</label>
|
| 83 |
+
</div>
|
| 84 |
+
<div class="input-helper">Modify AI behavior and scoring criteria</div>
|
| 85 |
+
</div>
|
| 86 |
+
</div>
|
| 87 |
+
|
| 88 |
+
<!-- Lead Preferences -->
|
| 89 |
+
<div class="config-section">
|
| 90 |
+
<div class="section-header">
|
| 91 |
+
<span class="material-icons">settings</span>
|
| 92 |
+
<h3>Lead Targeting</h3>
|
| 93 |
+
</div>
|
| 94 |
+
<div class="info-card">
|
| 95 |
+
<span class="material-icons">info</span>
|
| 96 |
+
Define your ideal customer profile and lead scoring preferences for maximum relevance.
|
| 97 |
+
</div>
|
| 98 |
+
<div class="input-group">
|
| 99 |
+
<div class="mdl-textfield mdl-js-textfield">
|
| 100 |
+
<textarea class="mdl-textfield__input" type="text" rows="8" id="preferences" required></textarea>
|
| 101 |
+
<label class="mdl-textfield__label" for="preferences">Targeting Preferences</label>
|
| 102 |
+
</div>
|
| 103 |
+
<div class="input-helper">Specify industries, funding stages, geographic regions, and other targeting criteria</div>
|
| 104 |
+
</div>
|
| 105 |
+
</div>
|
| 106 |
+
</div>
|
| 107 |
+
|
| 108 |
+
<!-- Right Column - Control Panel -->
|
| 109 |
+
<div class="control-column">
|
| 110 |
+
<!-- Mission Control -->
|
| 111 |
+
<div class="control-panel">
|
| 112 |
+
<div class="section-header">
|
| 113 |
+
<span class="material-icons">rocket_launch</span>
|
| 114 |
+
<h3>Mission Control</h3>
|
| 115 |
+
</div>
|
| 116 |
+
|
| 117 |
+
<!-- Status Display -->
|
| 118 |
+
<div class="status-display" id="status-display">
|
| 119 |
+
<div class="status-indicator status-info">
|
| 120 |
+
<span class="material-icons">info</span>
|
| 121 |
+
Ready to generate leads. Configure your settings and launch the analysis.
|
| 122 |
+
</div>
|
| 123 |
+
</div>
|
| 124 |
+
|
| 125 |
+
<!-- Generate Button -->
|
| 126 |
+
<button class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect generate-btn" id="generate-btn">
|
| 127 |
+
<span class="material-icons">track_changes</span>
|
| 128 |
+
Generate Report
|
| 129 |
+
</button>
|
| 130 |
+
|
| 131 |
+
<!-- Progress Monitor -->
|
| 132 |
+
<div class="progress-card">
|
| 133 |
+
<div class="section-header">
|
| 134 |
+
<span class="material-icons">analytics</span>
|
| 135 |
+
<h4>Progress Monitor</h4>
|
| 136 |
+
</div>
|
| 137 |
+
<div class="progress-display" id="progress-display">
|
| 138 |
+
<div class="progress-text">Awaiting launch command...</div>
|
| 139 |
+
<div class="mdl-progress mdl-js-progress" id="progress-bar" style="width: 100%; display: none;"></div>
|
| 140 |
+
</div>
|
| 141 |
+
</div>
|
| 142 |
+
|
| 143 |
+
<!-- Intelligence Capabilities -->
|
| 144 |
+
<div class="progress-card">
|
| 145 |
+
<div class="section-header">
|
| 146 |
+
<span class="material-icons">psychology</span>
|
| 147 |
+
<h4>Intelligence Capabilities</h4>
|
| 148 |
+
</div>
|
| 149 |
+
<ul class="capability-list">
|
| 150 |
+
<li><span class="material-icons">check_circle</span>Real-time RSS feed monitoring</li>
|
| 151 |
+
<li><span class="material-icons">check_circle</span>AI-powered company analysis</li>
|
| 152 |
+
<li><span class="material-icons">check_circle</span>Hiring signal detection</li>
|
| 153 |
+
<li><span class="material-icons">check_circle</span>Lead scoring & prioritization</li>
|
| 154 |
+
<li><span class="material-icons">check_circle</span>Professional report generation</li>
|
| 155 |
+
</ul>
|
| 156 |
+
</div>
|
| 157 |
+
|
| 158 |
+
<!-- Best Practices -->
|
| 159 |
+
<div class="progress-card">
|
| 160 |
+
<div class="section-header">
|
| 161 |
+
<span class="material-icons">trending_up</span>
|
| 162 |
+
<h4>Best Practices</h4>
|
| 163 |
+
</div>
|
| 164 |
+
<ul class="capability-list">
|
| 165 |
+
<li><span class="material-icons">check_circle</span>Use industry-specific RSS feeds</li>
|
| 166 |
+
<li><span class="material-icons">check_circle</span>Update preferences regularly</li>
|
| 167 |
+
<li><span class="material-icons">check_circle</span>Monitor funding announcement timing</li>
|
| 168 |
+
<li><span class="material-icons">check_circle</span>Focus on growth-stage companies</li>
|
| 169 |
+
<li><span class="material-icons">check_circle</span>Track geographic preferences</li>
|
| 170 |
+
</ul>
|
| 171 |
+
</div>
|
| 172 |
+
</div>
|
| 173 |
+
</div>
|
| 174 |
+
</div>
|
| 175 |
+
|
| 176 |
+
<!-- Results Section -->
|
| 177 |
+
<div class="results-section">
|
| 178 |
+
<div class="section-header">
|
| 179 |
+
<span class="material-icons">forum</span>
|
| 180 |
+
<h3>AI Agent Activity & Intelligence Feed</h3>
|
| 181 |
+
</div>
|
| 182 |
+
<div class="info-card">
|
| 183 |
+
<span class="material-icons">info</span>
|
| 184 |
+
Monitor real-time AI agent activity, analysis progress, and detailed lead intelligence results.
|
| 185 |
+
</div>
|
| 186 |
+
<div class="chat-container" id="chat-container">
|
| 187 |
+
<div class="chat-placeholder">
|
| 188 |
+
<span class="material-icons">smart_toy</span>
|
| 189 |
+
<p>AI agent will display live analysis and results here...</p>
|
| 190 |
+
</div>
|
| 191 |
+
</div>
|
| 192 |
+
</div>
|
| 193 |
+
</main>
|
| 194 |
+
|
| 195 |
+
<!-- Loading Overlay -->
|
| 196 |
+
<div class="loading-overlay" id="loading-overlay" style="display: none;">
|
| 197 |
+
<div class="loading-content">
|
| 198 |
+
<div class="mdl-spinner mdl-js-spinner is-active"></div>
|
| 199 |
+
<p>Processing your request...</p>
|
| 200 |
+
</div>
|
| 201 |
+
</div>
|
| 202 |
+
|
| 203 |
+
<!-- Snackbar for notifications -->
|
| 204 |
+
<div class="mdl-snackbar mdl-js-snackbar">
|
| 205 |
+
<div class="mdl-snackbar__text"></div>
|
| 206 |
+
<button class="mdl-snackbar__action" type="button"></button>
|
| 207 |
+
</div>
|
| 208 |
+
|
| 209 |
+
<!-- Material Design Lite JavaScript -->
|
| 210 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/material-design-lite/1.3.0/material.min.js"></script>
|
| 211 |
+
|
| 212 |
+
<!-- Gradio Client -->
|
| 213 |
+
<script type="module">
|
| 214 |
+
import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js";
|
| 215 |
+
window.GradioClient = Client;
|
| 216 |
+
</script>
|
| 217 |
+
|
| 218 |
+
<!-- Custom JavaScript -->
|
| 219 |
+
<script src="script.js"></script>
|
| 220 |
+
</body>
|
| 221 |
+
</html>
|
ai-agent-ui/script.js
ADDED
|
@@ -0,0 +1,562 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Application Configuration
|
| 2 |
+
const CONFIG = {
|
| 3 |
+
API_BASE_URL: 'http://localhost:7860',
|
| 4 |
+
DEFAULT_RSS_FEEDS: `https://techfundingnews.com/feed
|
| 5 |
+
https://feeds.feedburner.com/vcnewsdaily
|
| 6 |
+
https://news.crunchbase.com/feed/`,
|
| 7 |
+
DEFAULT_PREFERENCES: `Focus Areas:
|
| 8 |
+
- Tech startups and SaaS companies
|
| 9 |
+
- Series A and Series B funding rounds
|
| 10 |
+
- Companies in North America and Europe
|
| 11 |
+
|
| 12 |
+
Scoring Preferences:
|
| 13 |
+
- Prioritize companies with explicit hiring signals
|
| 14 |
+
- Higher scores for funding rounds above $10M
|
| 15 |
+
- Focus on B2B companies over B2C
|
| 16 |
+
- Prefer companies in high-growth sectors (AI, fintech, healthcare tech)`,
|
| 17 |
+
DEFAULT_PROMPT: `You are an AI lead generation agent specialized in analyzing funding announcements and hiring signals to identify potential leads for a recruitment services company.
|
| 18 |
+
|
| 19 |
+
Your task is to:
|
| 20 |
+
1. Monitor RSS feeds for funding announcements and hiring-related signals
|
| 21 |
+
2. Analyze companies that have recently received funding or are showing signs of growth
|
| 22 |
+
3. Score leads based on their likelihood to need recruitment services
|
| 23 |
+
4. Generate a professional report with actionable insights
|
| 24 |
+
|
| 25 |
+
Analysis Framework:
|
| 26 |
+
- Company funding stage and amount
|
| 27 |
+
- Hiring signals and job postings activity
|
| 28 |
+
- Industry and growth potential
|
| 29 |
+
- Geographic relevance
|
| 30 |
+
- Recent news and market position
|
| 31 |
+
|
| 32 |
+
Scoring Criteria (1-10 scale):
|
| 33 |
+
- 9-10: High priority leads with strong hiring signals and recent funding
|
| 34 |
+
- 7-8: Good prospects with moderate signals
|
| 35 |
+
- 5-6: Potential leads worth monitoring
|
| 36 |
+
- 1-4: Low priority or insufficient data
|
| 37 |
+
|
| 38 |
+
Output a comprehensive report with:
|
| 39 |
+
- Executive summary
|
| 40 |
+
- Top 10 prioritized leads with scores and rationale
|
| 41 |
+
- Market insights and trends
|
| 42 |
+
- Recommended next steps for outreach`
|
| 43 |
+
};
|
| 44 |
+
|
| 45 |
+
// State Management
|
| 46 |
+
class AppState {
|
| 47 |
+
constructor() {
|
| 48 |
+
this.isProcessing = false;
|
| 49 |
+
this.currentRequestId = null;
|
| 50 |
+
this.chatHistory = [];
|
| 51 |
+
this.formData = {
|
| 52 |
+
email: '',
|
| 53 |
+
rssFeeds: CONFIG.DEFAULT_RSS_FEEDS,
|
| 54 |
+
systemPrompt: CONFIG.DEFAULT_PROMPT,
|
| 55 |
+
preferences: CONFIG.DEFAULT_PREFERENCES
|
| 56 |
+
};
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
updateFormData(key, value) {
|
| 60 |
+
this.formData[key] = value;
|
| 61 |
+
this.validateForm();
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
validateForm() {
|
| 65 |
+
const emailValid = this.validateEmail(this.formData.email);
|
| 66 |
+
const rssValid = this.parseRSSUrls(this.formData.rssFeeds).length > 0;
|
| 67 |
+
const promptValid = this.formData.systemPrompt.trim().length > 0;
|
| 68 |
+
|
| 69 |
+
return emailValid && rssValid && promptValid;
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
validateEmail(email) {
|
| 73 |
+
const pattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
| 74 |
+
return pattern.test(email);
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
parseRSSUrls(rssText) {
|
| 78 |
+
const urls = [];
|
| 79 |
+
const lines = rssText.trim().split('\n');
|
| 80 |
+
lines.forEach(line => {
|
| 81 |
+
line = line.trim();
|
| 82 |
+
if (line && (line.startsWith('http://') || line.startsWith('https://'))) {
|
| 83 |
+
urls.push(line);
|
| 84 |
+
}
|
| 85 |
+
});
|
| 86 |
+
return urls;
|
| 87 |
+
}
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
// UI Controller
|
| 91 |
+
class UIController {
|
| 92 |
+
constructor(state) {
|
| 93 |
+
this.state = state;
|
| 94 |
+
this.initializeElements();
|
| 95 |
+
this.setupEventListeners();
|
| 96 |
+
this.populateDefaultValues();
|
| 97 |
+
this.initializeMDL();
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
initializeElements() {
|
| 101 |
+
this.elements = {
|
| 102 |
+
emailInput: document.getElementById('email-input'),
|
| 103 |
+
rssFeeds: document.getElementById('rss-feeds'),
|
| 104 |
+
systemPrompt: document.getElementById('system-prompt'),
|
| 105 |
+
preferences: document.getElementById('preferences'),
|
| 106 |
+
generateBtn: document.getElementById('generate-btn'),
|
| 107 |
+
statusDisplay: document.getElementById('status-display'),
|
| 108 |
+
progressDisplay: document.getElementById('progress-display'),
|
| 109 |
+
chatContainer: document.getElementById('chat-container'),
|
| 110 |
+
loadingOverlay: document.getElementById('loading-overlay'),
|
| 111 |
+
progressBar: document.getElementById('progress-bar')
|
| 112 |
+
};
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
initializeMDL() {
|
| 116 |
+
// Initialize Material Design Lite components
|
| 117 |
+
if (window.componentHandler) {
|
| 118 |
+
window.componentHandler.upgradeAllRegistered();
|
| 119 |
+
}
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
populateDefaultValues() {
|
| 123 |
+
// Clear email field to ensure fresh state
|
| 124 |
+
this.elements.emailInput.value = '';
|
| 125 |
+
this.state.formData.email = '';
|
| 126 |
+
|
| 127 |
+
// Set default values for other fields
|
| 128 |
+
this.elements.rssFeeds.value = CONFIG.DEFAULT_RSS_FEEDS;
|
| 129 |
+
this.elements.systemPrompt.value = CONFIG.DEFAULT_PROMPT;
|
| 130 |
+
this.elements.preferences.value = CONFIG.DEFAULT_PREFERENCES;
|
| 131 |
+
|
| 132 |
+
// Update state to match form values
|
| 133 |
+
this.state.formData.rssFeeds = CONFIG.DEFAULT_RSS_FEEDS;
|
| 134 |
+
this.state.formData.systemPrompt = CONFIG.DEFAULT_PROMPT;
|
| 135 |
+
this.state.formData.preferences = CONFIG.DEFAULT_PREFERENCES;
|
| 136 |
+
|
| 137 |
+
// Trigger MDL updates
|
| 138 |
+
this.elements.emailInput.parentElement.MaterialTextfield?.change();
|
| 139 |
+
this.elements.rssFeeds.parentElement.MaterialTextfield?.change();
|
| 140 |
+
this.elements.systemPrompt.parentElement.MaterialTextfield?.change();
|
| 141 |
+
this.elements.preferences.parentElement.MaterialTextfield?.change();
|
| 142 |
+
|
| 143 |
+
// Update email status after clearing
|
| 144 |
+
this.updateEmailStatus();
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
+
setupEventListeners() {
|
| 148 |
+
// Form input listeners
|
| 149 |
+
this.elements.emailInput.addEventListener('input', (e) => {
|
| 150 |
+
this.state.updateFormData('email', e.target.value);
|
| 151 |
+
this.updateEmailStatus();
|
| 152 |
+
});
|
| 153 |
+
|
| 154 |
+
this.elements.rssFeeds.addEventListener('input', (e) => {
|
| 155 |
+
this.state.updateFormData('rssFeeds', e.target.value);
|
| 156 |
+
});
|
| 157 |
+
|
| 158 |
+
this.elements.systemPrompt.addEventListener('input', (e) => {
|
| 159 |
+
this.state.updateFormData('systemPrompt', e.target.value);
|
| 160 |
+
});
|
| 161 |
+
|
| 162 |
+
this.elements.preferences.addEventListener('input', (e) => {
|
| 163 |
+
this.state.updateFormData('preferences', e.target.value);
|
| 164 |
+
});
|
| 165 |
+
|
| 166 |
+
// Generate button listener
|
| 167 |
+
this.elements.generateBtn.addEventListener('click', () => {
|
| 168 |
+
this.handleGenerate();
|
| 169 |
+
});
|
| 170 |
+
|
| 171 |
+
// Enter key handling
|
| 172 |
+
document.addEventListener('keydown', (e) => {
|
| 173 |
+
if (e.key === 'Enter' && e.ctrlKey) {
|
| 174 |
+
this.handleGenerate();
|
| 175 |
+
}
|
| 176 |
+
});
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
updateEmailStatus() {
|
| 180 |
+
const email = this.elements.emailInput.value;
|
| 181 |
+
let statusHtml = '';
|
| 182 |
+
|
| 183 |
+
if (!email) {
|
| 184 |
+
statusHtml = `
|
| 185 |
+
<div class="status-indicator status-info">
|
| 186 |
+
<span class="material-icons">info</span>
|
| 187 |
+
Ready to generate leads. Configure your settings and launch the analysis.
|
| 188 |
+
</div>
|
| 189 |
+
`;
|
| 190 |
+
} else if (this.state.validateEmail(email)) {
|
| 191 |
+
statusHtml = `
|
| 192 |
+
<div class="status-indicator status-success">
|
| 193 |
+
<span class="material-icons">check_circle</span>
|
| 194 |
+
Valid email address configured
|
| 195 |
+
</div>
|
| 196 |
+
`;
|
| 197 |
+
} else {
|
| 198 |
+
statusHtml = `
|
| 199 |
+
<div class="status-indicator status-error">
|
| 200 |
+
<span class="material-icons">error</span>
|
| 201 |
+
Invalid email format - please correct
|
| 202 |
+
</div>
|
| 203 |
+
`;
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
this.elements.statusDisplay.innerHTML = statusHtml;
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
updateProgress(message, showProgress = false) {
|
| 210 |
+
const progressText = this.elements.progressDisplay.querySelector('.progress-text');
|
| 211 |
+
progressText.textContent = message;
|
| 212 |
+
|
| 213 |
+
if (showProgress) {
|
| 214 |
+
this.elements.progressBar.style.display = 'block';
|
| 215 |
+
this.elements.progressBar.MaterialProgress?.setProgress(50);
|
| 216 |
+
} else {
|
| 217 |
+
this.elements.progressBar.style.display = 'none';
|
| 218 |
+
}
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
showNotification(message, type = 'info') {
|
| 222 |
+
const snackbar = document.querySelector('.mdl-snackbar');
|
| 223 |
+
if (snackbar && snackbar.MaterialSnackbar) {
|
| 224 |
+
snackbar.MaterialSnackbar.showSnackbar({
|
| 225 |
+
message: message,
|
| 226 |
+
timeout: 4000
|
| 227 |
+
});
|
| 228 |
+
}
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
showLoading(show = true) {
|
| 232 |
+
this.elements.loadingOverlay.style.display = show ? 'flex' : 'none';
|
| 233 |
+
this.elements.generateBtn.disabled = show;
|
| 234 |
+
|
| 235 |
+
if (show) {
|
| 236 |
+
this.elements.generateBtn.innerHTML = `
|
| 237 |
+
<div class="mdl-spinner mdl-js-spinner is-active" style="width: 20px; height: 20px; margin-right: 8px;"></div>
|
| 238 |
+
Processing...
|
| 239 |
+
`;
|
| 240 |
+
} else {
|
| 241 |
+
this.elements.generateBtn.innerHTML = `
|
| 242 |
+
<span class="material-icons">track_changes</span>
|
| 243 |
+
Generate Report
|
| 244 |
+
`;
|
| 245 |
+
}
|
| 246 |
+
|
| 247 |
+
// Re-initialize spinner
|
| 248 |
+
if (show && window.componentHandler) {
|
| 249 |
+
window.componentHandler.upgradeAllRegistered();
|
| 250 |
+
}
|
| 251 |
+
}
|
| 252 |
+
|
| 253 |
+
addChatMessage(content, role = 'assistant') {
|
| 254 |
+
// Remove placeholder if it exists
|
| 255 |
+
const placeholder = this.elements.chatContainer.querySelector('.chat-placeholder');
|
| 256 |
+
if (placeholder) {
|
| 257 |
+
placeholder.remove();
|
| 258 |
+
}
|
| 259 |
+
|
| 260 |
+
const messageDiv = document.createElement('div');
|
| 261 |
+
messageDiv.className = `chat-message ${role}`;
|
| 262 |
+
|
| 263 |
+
const avatarDiv = document.createElement('div');
|
| 264 |
+
avatarDiv.className = `chat-avatar ${role}`;
|
| 265 |
+
avatarDiv.innerHTML = `<span class="material-icons">${role === 'user' ? 'person' : 'smart_toy'}</span>`;
|
| 266 |
+
|
| 267 |
+
const contentDiv = document.createElement('div');
|
| 268 |
+
contentDiv.className = 'chat-content';
|
| 269 |
+
|
| 270 |
+
// Format content for better display
|
| 271 |
+
const formattedContent = this.formatChatContent(content);
|
| 272 |
+
contentDiv.innerHTML = formattedContent;
|
| 273 |
+
|
| 274 |
+
messageDiv.appendChild(avatarDiv);
|
| 275 |
+
messageDiv.appendChild(contentDiv);
|
| 276 |
+
|
| 277 |
+
this.elements.chatContainer.appendChild(messageDiv);
|
| 278 |
+
|
| 279 |
+
// Scroll to bottom
|
| 280 |
+
messageDiv.scrollIntoView({ behavior: 'smooth' });
|
| 281 |
+
|
| 282 |
+
// Store in state
|
| 283 |
+
this.state.chatHistory.push({ role, content });
|
| 284 |
+
}
|
| 285 |
+
|
| 286 |
+
formatChatContent(content) {
|
| 287 |
+
// Convert markdown-style formatting to HTML
|
| 288 |
+
let formatted = content
|
| 289 |
+
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
| 290 |
+
.replace(/\*(.*?)\*/g, '<em>$1</em>')
|
| 291 |
+
.replace(/`(.*?)`/g, '<code>$1</code>')
|
| 292 |
+
.replace(/\n/g, '<br>');
|
| 293 |
+
|
| 294 |
+
// Format lists
|
| 295 |
+
formatted = formatted.replace(/^- (.*?)(<br>|$)/gm, '<li>$1</li>');
|
| 296 |
+
if (formatted.includes('<li>')) {
|
| 297 |
+
formatted = formatted.replace(/(<li>.*<\/li>)/s, '<ul>$1</ul>');
|
| 298 |
+
}
|
| 299 |
+
|
| 300 |
+
return formatted;
|
| 301 |
+
}
|
| 302 |
+
|
| 303 |
+
clearChat() {
|
| 304 |
+
this.elements.chatContainer.innerHTML = `
|
| 305 |
+
<div class="chat-placeholder">
|
| 306 |
+
<span class="material-icons">smart_toy</span>
|
| 307 |
+
<p>AI agent will display live analysis and results here...</p>
|
| 308 |
+
</div>
|
| 309 |
+
`;
|
| 310 |
+
this.state.chatHistory = [];
|
| 311 |
+
}
|
| 312 |
+
|
| 313 |
+
async handleGenerate() {
|
| 314 |
+
if (this.state.isProcessing) {
|
| 315 |
+
return;
|
| 316 |
+
}
|
| 317 |
+
|
| 318 |
+
// Validate form
|
| 319 |
+
const email = this.elements.emailInput.value;
|
| 320 |
+
const rssFeeds = this.elements.rssFeeds.value;
|
| 321 |
+
const systemPrompt = this.elements.systemPrompt.value;
|
| 322 |
+
const preferences = this.elements.preferences.value;
|
| 323 |
+
|
| 324 |
+
if (!this.state.validateEmail(email)) {
|
| 325 |
+
this.updateStatus('error', '❌ Please enter a valid email address');
|
| 326 |
+
this.showNotification('Please enter a valid email address', 'error');
|
| 327 |
+
return;
|
| 328 |
+
}
|
| 329 |
+
|
| 330 |
+
const rssUrls = this.state.parseRSSUrls(rssFeeds);
|
| 331 |
+
if (rssUrls.length === 0) {
|
| 332 |
+
this.updateStatus('error', '❌ Please configure at least one RSS feed source');
|
| 333 |
+
this.showNotification('At least one RSS feed is required', 'error');
|
| 334 |
+
return;
|
| 335 |
+
}
|
| 336 |
+
|
| 337 |
+
if (!systemPrompt.trim()) {
|
| 338 |
+
this.updateStatus('error', '❌ AI system prompt cannot be empty');
|
| 339 |
+
this.showNotification('AI system prompt is required', 'error');
|
| 340 |
+
return;
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
// Start processing
|
| 344 |
+
this.state.isProcessing = true;
|
| 345 |
+
this.showLoading(true);
|
| 346 |
+
this.clearChat();
|
| 347 |
+
|
| 348 |
+
this.updateStatus('success', '✅ All systems validated. Initiating AI lead generation...');
|
| 349 |
+
this.updateProgress(`🚀 Launching intelligence gathering from ${rssUrls.length} premium sources...`, true);
|
| 350 |
+
|
| 351 |
+
// Add initial chat message
|
| 352 |
+
this.addChatMessage(`🚀 **Lead Generation Mission Initiated**
|
| 353 |
+
|
| 354 |
+
📧 **Target Email:** ${email}
|
| 355 |
+
📡 **Data Sources:** ${rssUrls.length} RSS feeds configured
|
| 356 |
+
🎯 **Analysis Mode:** Advanced lead scoring enabled
|
| 357 |
+
🤖 **AI Status:** Ready for intelligent analysis
|
| 358 |
+
|
| 359 |
+
Initiating real-time monitoring and analysis...`);
|
| 360 |
+
|
| 361 |
+
try {
|
| 362 |
+
// Create enhanced prompt
|
| 363 |
+
const enhancedPrompt = this.createEnhancedPrompt(systemPrompt, rssFeeds, preferences, email);
|
| 364 |
+
|
| 365 |
+
// Add user message to chat
|
| 366 |
+
this.addChatMessage('🎯 Execute lead generation with current configuration', 'user');
|
| 367 |
+
|
| 368 |
+
this.updateProgress('🤖 AI Agent processing feeds • Analyzing companies • Scoring leads...', true);
|
| 369 |
+
|
| 370 |
+
// Make API call
|
| 371 |
+
const result = await this.callGradioAPI(enhancedPrompt);
|
| 372 |
+
|
| 373 |
+
// Add success message
|
| 374 |
+
this.addChatMessage(`✅ **Lead Generation Mission Completed Successfully!**
|
| 375 |
+
|
| 376 |
+
🎯 **Results Summary:**
|
| 377 |
+
${result}
|
| 378 |
+
|
| 379 |
+
📧 **Report Status:** Professional lead intelligence report has been generated and sent to ${email}
|
| 380 |
+
|
| 381 |
+
📊 **Next Steps:**
|
| 382 |
+
• Check your email for the detailed report
|
| 383 |
+
• Review lead scores and prioritize outreach
|
| 384 |
+
• Monitor for new funding announcements
|
| 385 |
+
• Update targeting criteria based on results`);
|
| 386 |
+
|
| 387 |
+
this.updateStatus('success', '✅ Mission accomplished! Lead intelligence report delivered successfully.');
|
| 388 |
+
this.updateProgress(`✅ Complete! Professional report sent to ${email}`);
|
| 389 |
+
this.showNotification('Lead generation completed successfully!', 'success');
|
| 390 |
+
|
| 391 |
+
} catch (error) {
|
| 392 |
+
console.error('Generation error:', error);
|
| 393 |
+
|
| 394 |
+
const errorMessage = `❌ **Mission Error Encountered**
|
| 395 |
+
|
| 396 |
+
**Error Details:** ${error.message}
|
| 397 |
+
|
| 398 |
+
**Troubleshooting Steps:**
|
| 399 |
+
• Verify RSS feed accessibility
|
| 400 |
+
• Check email configuration
|
| 401 |
+
• Ensure stable internet connection
|
| 402 |
+
• Try again with simplified parameters`;
|
| 403 |
+
|
| 404 |
+
this.addChatMessage(errorMessage);
|
| 405 |
+
this.updateStatus('error', '❌ Mission error - please review configuration and try again');
|
| 406 |
+
this.updateProgress(`❌ Error: ${error.message}`);
|
| 407 |
+
this.showNotification('An error occurred. Please try again.', 'error');
|
| 408 |
+
} finally {
|
| 409 |
+
this.state.isProcessing = false;
|
| 410 |
+
this.showLoading(false);
|
| 411 |
+
}
|
| 412 |
+
}
|
| 413 |
+
|
| 414 |
+
updateStatus(type, message) {
|
| 415 |
+
const iconMap = {
|
| 416 |
+
success: 'check_circle',
|
| 417 |
+
error: 'error',
|
| 418 |
+
info: 'info'
|
| 419 |
+
};
|
| 420 |
+
|
| 421 |
+
const statusHtml = `
|
| 422 |
+
<div class="status-indicator status-${type}">
|
| 423 |
+
<span class="material-icons">${iconMap[type]}</span>
|
| 424 |
+
${message}
|
| 425 |
+
</div>
|
| 426 |
+
`;
|
| 427 |
+
|
| 428 |
+
this.elements.statusDisplay.innerHTML = statusHtml;
|
| 429 |
+
}
|
| 430 |
+
|
| 431 |
+
createEnhancedPrompt(systemPrompt, rssFeeds, preferences, email) {
|
| 432 |
+
const rssUrls = this.state.parseRSSUrls(rssFeeds);
|
| 433 |
+
const rssUrlsStr = rssUrls.join(' ');
|
| 434 |
+
|
| 435 |
+
// Always add the email instruction before making the call to backend
|
| 436 |
+
const emailInstruction = `
|
| 437 |
+
|
| 438 |
+
IMPORTANT: When you need to send an email with the report, use the email address provided in the user input. Do not use any hardcoded email addresses. The email should be sent to the address specified by the user.
|
| 439 |
+
|
| 440 |
+
Format the report professionally for business stakeholders and ensure the email is professionally formatted with proper HTML styling.`;
|
| 441 |
+
|
| 442 |
+
return `RSS Feed URLs: ${rssUrlsStr}
|
| 443 |
+
|
| 444 |
+
${systemPrompt}${emailInstruction}
|
| 445 |
+
|
| 446 |
+
User Preferences:
|
| 447 |
+
${preferences}
|
| 448 |
+
|
| 449 |
+
Send the report to: ${email}`;
|
| 450 |
+
}
|
| 451 |
+
|
| 452 |
+
async callGradioAPI(enhancedPrompt) {
|
| 453 |
+
try {
|
| 454 |
+
// Extract the parameters from the enhanced prompt
|
| 455 |
+
const email = this.state.formData.email;
|
| 456 |
+
const rssFeeds = this.state.formData.rssFeeds;
|
| 457 |
+
const systemPrompt = this.state.formData.systemPrompt;
|
| 458 |
+
const preferences = this.state.formData.preferences;
|
| 459 |
+
|
| 460 |
+
console.log('🔄 Making API call to Gradio server...');
|
| 461 |
+
console.log('📧 Email:', email);
|
| 462 |
+
console.log('📡 RSS count:', this.state.parseRSSUrls(rssFeeds).length);
|
| 463 |
+
|
| 464 |
+
// Wait for GradioClient to be available
|
| 465 |
+
if (!window.GradioClient) {
|
| 466 |
+
throw new Error('Gradio client not loaded. Please refresh the page.');
|
| 467 |
+
}
|
| 468 |
+
|
| 469 |
+
// Connect to Gradio server
|
| 470 |
+
const client = await window.GradioClient.connect(CONFIG.API_BASE_URL);
|
| 471 |
+
console.log('📡 Connected to Gradio server');
|
| 472 |
+
|
| 473 |
+
// Make prediction call
|
| 474 |
+
const result = await client.predict("/generate_lead_report", {
|
| 475 |
+
email: email,
|
| 476 |
+
rss_feeds: rssFeeds,
|
| 477 |
+
system_prompt: systemPrompt,
|
| 478 |
+
preferences: preferences
|
| 479 |
+
});
|
| 480 |
+
|
| 481 |
+
console.log('📦 API Response:', result);
|
| 482 |
+
|
| 483 |
+
// Check if the API call was successful
|
| 484 |
+
if (result && result.data && result.data.length > 0) {
|
| 485 |
+
const apiResponse = result.data[0];
|
| 486 |
+
console.log('✅ API Response data:', apiResponse);
|
| 487 |
+
|
| 488 |
+
if (typeof apiResponse === 'object' && apiResponse.success) {
|
| 489 |
+
return apiResponse.result;
|
| 490 |
+
} else if (typeof apiResponse === 'object' && apiResponse.success === false) {
|
| 491 |
+
throw new Error(apiResponse.message || apiResponse.error || 'API returned error');
|
| 492 |
+
} else if (typeof apiResponse === 'string') {
|
| 493 |
+
// If it's a string response, treat it as the result
|
| 494 |
+
return apiResponse;
|
| 495 |
+
} else {
|
| 496 |
+
throw new Error('Unexpected API response format');
|
| 497 |
+
}
|
| 498 |
+
} else {
|
| 499 |
+
throw new Error('Invalid API response format - no data returned');
|
| 500 |
+
}
|
| 501 |
+
|
| 502 |
+
} catch (error) {
|
| 503 |
+
console.error('❌ API call failed:', error);
|
| 504 |
+
|
| 505 |
+
// If API call fails, provide helpful error messages
|
| 506 |
+
if (error.message.includes('Failed to fetch') || error.message.includes('Network')) {
|
| 507 |
+
throw new Error('Unable to connect to API server. Please ensure the Gradio server is running on localhost:7860');
|
| 508 |
+
} else if (error.message.includes('CORS')) {
|
| 509 |
+
throw new Error('CORS issue detected. Please ensure the Gradio server is running with CORS enabled');
|
| 510 |
+
}
|
| 511 |
+
|
| 512 |
+
throw error;
|
| 513 |
+
}
|
| 514 |
+
}
|
| 515 |
+
}
|
| 516 |
+
|
| 517 |
+
// Initialize Application
|
| 518 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 519 |
+
// Initialize app state and UI controller
|
| 520 |
+
const appState = new AppState();
|
| 521 |
+
const uiController = new UIController(appState);
|
| 522 |
+
|
| 523 |
+
// Add some initial UI enhancements
|
| 524 |
+
setTimeout(() => {
|
| 525 |
+
// Add ripple effect to cards
|
| 526 |
+
document.querySelectorAll('.config-section').forEach(card => {
|
| 527 |
+
card.addEventListener('mouseenter', function() {
|
| 528 |
+
this.style.transform = 'translateY(-2px)';
|
| 529 |
+
});
|
| 530 |
+
|
| 531 |
+
card.addEventListener('mouseleave', function() {
|
| 532 |
+
this.style.transform = 'translateY(0)';
|
| 533 |
+
});
|
| 534 |
+
});
|
| 535 |
+
|
| 536 |
+
// Add keyboard shortcuts info
|
| 537 |
+
const shortcutInfo = document.createElement('div');
|
| 538 |
+
shortcutInfo.innerHTML = `
|
| 539 |
+
<div class="info-card" style="margin-top: 16px; font-size: 0.8rem;">
|
| 540 |
+
<span class="material-icons">keyboard</span>
|
| 541 |
+
<strong>Keyboard Shortcuts:</strong> Ctrl+Enter to generate report
|
| 542 |
+
</div>
|
| 543 |
+
`;
|
| 544 |
+
document.querySelector('.control-panel').appendChild(shortcutInfo);
|
| 545 |
+
|
| 546 |
+
}, 1000);
|
| 547 |
+
|
| 548 |
+
console.log('🎯 AI Lead Generation Agent - Professional Dashboard Initialized');
|
| 549 |
+
});
|
| 550 |
+
|
| 551 |
+
// Global error handling
|
| 552 |
+
window.addEventListener('error', (event) => {
|
| 553 |
+
console.error('Application error:', event.error);
|
| 554 |
+
});
|
| 555 |
+
|
| 556 |
+
// Service worker registration for offline support (optional)
|
| 557 |
+
if ('serviceWorker' in navigator) {
|
| 558 |
+
window.addEventListener('load', () => {
|
| 559 |
+
// Optional: Register service worker for offline support
|
| 560 |
+
// navigator.serviceWorker.register('/sw.js');
|
| 561 |
+
});
|
| 562 |
+
}
|
ai-agent-ui/style.css
ADDED
|
@@ -0,0 +1,561 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Global Styles */
|
| 2 |
+
* {
|
| 3 |
+
margin: 0;
|
| 4 |
+
padding: 0;
|
| 5 |
+
box-sizing: border-box;
|
| 6 |
+
}
|
| 7 |
+
|
| 8 |
+
body {
|
| 9 |
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
| 10 |
+
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
|
| 11 |
+
min-height: 100vh;
|
| 12 |
+
color: #1e293b;
|
| 13 |
+
line-height: 1.6;
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
/* Header Styling */
|
| 17 |
+
.professional-header {
|
| 18 |
+
background: linear-gradient(135deg, #1e293b 0%, #334155 50%, #475569 100%);
|
| 19 |
+
padding: 48px 32px;
|
| 20 |
+
text-align: center;
|
| 21 |
+
position: relative;
|
| 22 |
+
overflow: hidden;
|
| 23 |
+
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
.professional-header::before {
|
| 27 |
+
content: '';
|
| 28 |
+
position: absolute;
|
| 29 |
+
top: 0;
|
| 30 |
+
left: 0;
|
| 31 |
+
right: 0;
|
| 32 |
+
bottom: 0;
|
| 33 |
+
background: radial-gradient(circle at 30% 20%, rgba(59, 130, 246, 0.3) 0%, transparent 50%),
|
| 34 |
+
radial-gradient(circle at 70% 80%, rgba(139, 92, 246, 0.2) 0%, transparent 50%);
|
| 35 |
+
animation: pulse-gradient 6s ease-in-out infinite;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
@keyframes pulse-gradient {
|
| 39 |
+
0%, 100% { opacity: 0.5; }
|
| 40 |
+
50% { opacity: 0.8; }
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
.header-content {
|
| 44 |
+
position: relative;
|
| 45 |
+
z-index: 2;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
.professional-header h1 {
|
| 49 |
+
color: white;
|
| 50 |
+
font-size: 3rem;
|
| 51 |
+
font-weight: 700;
|
| 52 |
+
margin: 0 0 16px 0;
|
| 53 |
+
text-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
| 54 |
+
letter-spacing: -0.025em;
|
| 55 |
+
display: flex;
|
| 56 |
+
align-items: center;
|
| 57 |
+
justify-content: center;
|
| 58 |
+
gap: 16px;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
.professional-header h1 .material-icons {
|
| 62 |
+
font-size: 3rem;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
.professional-header p {
|
| 66 |
+
color: rgba(255, 255, 255, 0.9);
|
| 67 |
+
font-size: 1.25rem;
|
| 68 |
+
margin: 0;
|
| 69 |
+
font-weight: 400;
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
/* Main Container */
|
| 73 |
+
.main-container {
|
| 74 |
+
max-width: 1400px;
|
| 75 |
+
margin: 0 auto;
|
| 76 |
+
padding: 32px 20px;
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
.content-grid {
|
| 80 |
+
display: grid;
|
| 81 |
+
grid-template-columns: 2fr 1fr;
|
| 82 |
+
gap: 32px;
|
| 83 |
+
margin-bottom: 32px;
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
/* Configuration Sections */
|
| 87 |
+
.config-section {
|
| 88 |
+
background: white;
|
| 89 |
+
border-radius: 12px;
|
| 90 |
+
padding: 24px;
|
| 91 |
+
margin-bottom: 24px;
|
| 92 |
+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
| 93 |
+
border: 1px solid rgba(226, 232, 240, 0.8);
|
| 94 |
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
| 95 |
+
position: relative;
|
| 96 |
+
overflow: hidden;
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
.config-section::before {
|
| 100 |
+
content: '';
|
| 101 |
+
position: absolute;
|
| 102 |
+
top: 0;
|
| 103 |
+
left: 0;
|
| 104 |
+
right: 0;
|
| 105 |
+
height: 4px;
|
| 106 |
+
background: linear-gradient(90deg, #3b82f6, #8b5cf6);
|
| 107 |
+
border-radius: 12px 12px 0 0;
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
.config-section:hover {
|
| 111 |
+
transform: translateY(-2px);
|
| 112 |
+
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
.section-header {
|
| 116 |
+
display: flex;
|
| 117 |
+
align-items: center;
|
| 118 |
+
gap: 12px;
|
| 119 |
+
margin-bottom: 20px;
|
| 120 |
+
padding-bottom: 12px;
|
| 121 |
+
border-bottom: 2px solid #f1f5f9;
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
.section-header h3, .section-header h4 {
|
| 125 |
+
font-size: 1.25rem;
|
| 126 |
+
font-weight: 600;
|
| 127 |
+
color: #1e293b;
|
| 128 |
+
margin: 0;
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
.section-header h4 {
|
| 132 |
+
font-size: 1.1rem;
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
.section-header .material-icons {
|
| 136 |
+
font-size: 1.5rem;
|
| 137 |
+
color: #3b82f6;
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
/* Info Cards */
|
| 141 |
+
.info-card {
|
| 142 |
+
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
|
| 143 |
+
border: 1px solid #e2e8f0;
|
| 144 |
+
border-left: 4px solid #3b82f6;
|
| 145 |
+
padding: 16px 20px;
|
| 146 |
+
margin: 16px 0;
|
| 147 |
+
border-radius: 8px;
|
| 148 |
+
font-size: 0.9rem;
|
| 149 |
+
line-height: 1.6;
|
| 150 |
+
color: #475569;
|
| 151 |
+
display: flex;
|
| 152 |
+
align-items: flex-start;
|
| 153 |
+
gap: 12px;
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
.info-card .material-icons {
|
| 157 |
+
color: #3b82f6;
|
| 158 |
+
font-size: 1.2rem;
|
| 159 |
+
margin-top: 2px;
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
/* Input Groups */
|
| 163 |
+
.input-group {
|
| 164 |
+
margin: 20px 0;
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
.input-helper {
|
| 168 |
+
font-size: 0.875rem;
|
| 169 |
+
color: #6b7280;
|
| 170 |
+
margin-top: 8px;
|
| 171 |
+
padding-left: 4px;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
/* Material Design Overrides */
|
| 175 |
+
.mdl-textfield {
|
| 176 |
+
width: 100%;
|
| 177 |
+
margin: 8px 0;
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
.mdl-textfield__input {
|
| 181 |
+
border-radius: 8px !important;
|
| 182 |
+
font-family: 'Inter', sans-serif !important;
|
| 183 |
+
font-size: 0.9rem !important;
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
.mdl-textfield__label {
|
| 187 |
+
font-family: 'Inter', sans-serif !important;
|
| 188 |
+
font-weight: 500 !important;
|
| 189 |
+
color: #64748b !important;
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
.mdl-textfield--floating-label.is-focused .mdl-textfield__label,
|
| 193 |
+
.mdl-textfield--floating-label.is-dirty .mdl-textfield__label {
|
| 194 |
+
color: #3b82f6 !important;
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
.mdl-textfield__input:focus {
|
| 198 |
+
border-color: #3b82f6 !important;
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
/* Control Panel */
|
| 202 |
+
.control-panel {
|
| 203 |
+
background: white;
|
| 204 |
+
border-radius: 16px;
|
| 205 |
+
padding: 24px;
|
| 206 |
+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
| 207 |
+
border: 1px solid rgba(226, 232, 240, 0.8);
|
| 208 |
+
position: sticky;
|
| 209 |
+
top: 20px;
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
/* Status Indicators */
|
| 213 |
+
.status-indicator {
|
| 214 |
+
padding: 16px 20px;
|
| 215 |
+
border-radius: 12px;
|
| 216 |
+
margin: 16px 0;
|
| 217 |
+
font-weight: 500;
|
| 218 |
+
font-size: 0.9rem;
|
| 219 |
+
display: flex;
|
| 220 |
+
align-items: center;
|
| 221 |
+
gap: 12px;
|
| 222 |
+
transition: all 0.3s ease;
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
.status-success {
|
| 226 |
+
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
| 227 |
+
color: white;
|
| 228 |
+
box-shadow: 0 4px 14px 0 rgba(16, 185, 129, 0.4);
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
.status-error {
|
| 232 |
+
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
|
| 233 |
+
color: white;
|
| 234 |
+
box-shadow: 0 4px 14px 0 rgba(239, 68, 68, 0.4);
|
| 235 |
+
}
|
| 236 |
+
|
| 237 |
+
.status-info {
|
| 238 |
+
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
|
| 239 |
+
color: white;
|
| 240 |
+
box-shadow: 0 4px 14px 0 rgba(59, 130, 246, 0.4);
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
/* Generate Button */
|
| 244 |
+
.generate-btn {
|
| 245 |
+
width: 100% !important;
|
| 246 |
+
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%) !important;
|
| 247 |
+
color: white !important;
|
| 248 |
+
font-weight: 600 !important;
|
| 249 |
+
padding: 16px 24px !important;
|
| 250 |
+
border-radius: 12px !important;
|
| 251 |
+
border: none !important;
|
| 252 |
+
font-size: 1rem !important;
|
| 253 |
+
box-shadow: 0 4px 14px 0 rgba(59, 130, 246, 0.4) !important;
|
| 254 |
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
|
| 255 |
+
margin: 20px 0 !important;
|
| 256 |
+
display: flex !important;
|
| 257 |
+
align-items: center !important;
|
| 258 |
+
justify-content: center !important;
|
| 259 |
+
gap: 8px !important;
|
| 260 |
+
text-transform: none !important;
|
| 261 |
+
letter-spacing: 0.025em !important;
|
| 262 |
+
}
|
| 263 |
+
|
| 264 |
+
.generate-btn:hover {
|
| 265 |
+
background: linear-gradient(135deg, #2563eb 0%, #1e40af 100%) !important;
|
| 266 |
+
transform: translateY(-2px) !important;
|
| 267 |
+
box-shadow: 0 8px 25px -5px rgba(59, 130, 246, 0.6) !important;
|
| 268 |
+
}
|
| 269 |
+
|
| 270 |
+
/* Progress Card */
|
| 271 |
+
.progress-card {
|
| 272 |
+
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
|
| 273 |
+
border: 1px solid #e2e8f0;
|
| 274 |
+
border-left: 4px solid #3b82f6;
|
| 275 |
+
padding: 20px;
|
| 276 |
+
margin: 20px 0;
|
| 277 |
+
border-radius: 8px;
|
| 278 |
+
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.06);
|
| 279 |
+
}
|
| 280 |
+
|
| 281 |
+
.progress-text {
|
| 282 |
+
font-size: 0.9rem;
|
| 283 |
+
color: #475569;
|
| 284 |
+
margin-bottom: 12px;
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
/* Capabilities Lists */
|
| 288 |
+
.capabilities, .best-practices {
|
| 289 |
+
margin-top: 20px;
|
| 290 |
+
}
|
| 291 |
+
|
| 292 |
+
.capability-title {
|
| 293 |
+
text-align: center;
|
| 294 |
+
margin-bottom: 16px;
|
| 295 |
+
font-size: 1.1rem;
|
| 296 |
+
font-weight: 600;
|
| 297 |
+
color: #1e293b;
|
| 298 |
+
}
|
| 299 |
+
|
| 300 |
+
.capability-list {
|
| 301 |
+
list-style: none;
|
| 302 |
+
margin: 0;
|
| 303 |
+
padding: 0;
|
| 304 |
+
}
|
| 305 |
+
|
| 306 |
+
.capability-list li {
|
| 307 |
+
display: flex;
|
| 308 |
+
align-items: center;
|
| 309 |
+
gap: 12px;
|
| 310 |
+
padding: 8px 0;
|
| 311 |
+
font-size: 0.875rem;
|
| 312 |
+
color: #475569;
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
.capability-list li .material-icons {
|
| 316 |
+
color: #10b981;
|
| 317 |
+
font-size: 1rem;
|
| 318 |
+
}
|
| 319 |
+
|
| 320 |
+
/* Results Section */
|
| 321 |
+
.results-section {
|
| 322 |
+
background: white;
|
| 323 |
+
border-radius: 12px;
|
| 324 |
+
padding: 24px;
|
| 325 |
+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
| 326 |
+
border: 1px solid rgba(226, 232, 240, 0.8);
|
| 327 |
+
position: relative;
|
| 328 |
+
overflow: hidden;
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
.results-section::before {
|
| 332 |
+
content: '';
|
| 333 |
+
position: absolute;
|
| 334 |
+
top: 0;
|
| 335 |
+
left: 0;
|
| 336 |
+
right: 0;
|
| 337 |
+
height: 4px;
|
| 338 |
+
background: linear-gradient(90deg, #3b82f6, #8b5cf6);
|
| 339 |
+
border-radius: 12px 12px 0 0;
|
| 340 |
+
}
|
| 341 |
+
|
| 342 |
+
/* Chat Container */
|
| 343 |
+
.chat-container {
|
| 344 |
+
background: #f8fafc;
|
| 345 |
+
border-radius: 12px;
|
| 346 |
+
min-height: 450px;
|
| 347 |
+
padding: 24px;
|
| 348 |
+
border: 1px solid #e2e8f0;
|
| 349 |
+
margin-top: 16px;
|
| 350 |
+
position: relative;
|
| 351 |
+
}
|
| 352 |
+
|
| 353 |
+
.chat-placeholder {
|
| 354 |
+
display: flex;
|
| 355 |
+
flex-direction: column;
|
| 356 |
+
align-items: center;
|
| 357 |
+
justify-content: center;
|
| 358 |
+
height: 100%;
|
| 359 |
+
min-height: 400px;
|
| 360 |
+
color: #6b7280;
|
| 361 |
+
text-align: center;
|
| 362 |
+
}
|
| 363 |
+
|
| 364 |
+
.chat-placeholder .material-icons {
|
| 365 |
+
font-size: 4rem;
|
| 366 |
+
color: #d1d5db;
|
| 367 |
+
margin-bottom: 16px;
|
| 368 |
+
}
|
| 369 |
+
|
| 370 |
+
.chat-placeholder p {
|
| 371 |
+
font-size: 1.1rem;
|
| 372 |
+
margin: 0;
|
| 373 |
+
}
|
| 374 |
+
|
| 375 |
+
/* Chat Messages */
|
| 376 |
+
.chat-message {
|
| 377 |
+
margin: 16px 0;
|
| 378 |
+
display: flex;
|
| 379 |
+
gap: 12px;
|
| 380 |
+
animation: slideInUp 0.3s ease-out;
|
| 381 |
+
}
|
| 382 |
+
|
| 383 |
+
@keyframes slideInUp {
|
| 384 |
+
from {
|
| 385 |
+
opacity: 0;
|
| 386 |
+
transform: translateY(20px);
|
| 387 |
+
}
|
| 388 |
+
to {
|
| 389 |
+
opacity: 1;
|
| 390 |
+
transform: translateY(0);
|
| 391 |
+
}
|
| 392 |
+
}
|
| 393 |
+
|
| 394 |
+
.chat-message.user {
|
| 395 |
+
flex-direction: row-reverse;
|
| 396 |
+
}
|
| 397 |
+
|
| 398 |
+
.chat-avatar {
|
| 399 |
+
width: 40px;
|
| 400 |
+
height: 40px;
|
| 401 |
+
border-radius: 20px;
|
| 402 |
+
display: flex;
|
| 403 |
+
align-items: center;
|
| 404 |
+
justify-content: center;
|
| 405 |
+
flex-shrink: 0;
|
| 406 |
+
}
|
| 407 |
+
|
| 408 |
+
.chat-avatar.assistant {
|
| 409 |
+
background: linear-gradient(135deg, #3b82f6, #8b5cf6);
|
| 410 |
+
color: white;
|
| 411 |
+
}
|
| 412 |
+
|
| 413 |
+
.chat-avatar.user {
|
| 414 |
+
background: linear-gradient(135deg, #10b981, #059669);
|
| 415 |
+
color: white;
|
| 416 |
+
}
|
| 417 |
+
|
| 418 |
+
.chat-content {
|
| 419 |
+
background: white;
|
| 420 |
+
padding: 16px 20px;
|
| 421 |
+
border-radius: 16px;
|
| 422 |
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
| 423 |
+
border: 1px solid #e2e8f0;
|
| 424 |
+
max-width: 80%;
|
| 425 |
+
line-height: 1.6;
|
| 426 |
+
}
|
| 427 |
+
|
| 428 |
+
.chat-message.user .chat-content {
|
| 429 |
+
background: linear-gradient(135deg, #3b82f6, #2563eb);
|
| 430 |
+
color: white;
|
| 431 |
+
}
|
| 432 |
+
|
| 433 |
+
/* Loading Overlay */
|
| 434 |
+
.loading-overlay {
|
| 435 |
+
position: fixed;
|
| 436 |
+
top: 0;
|
| 437 |
+
left: 0;
|
| 438 |
+
width: 100%;
|
| 439 |
+
height: 100%;
|
| 440 |
+
background: rgba(0, 0, 0, 0.5);
|
| 441 |
+
display: flex;
|
| 442 |
+
align-items: center;
|
| 443 |
+
justify-content: center;
|
| 444 |
+
z-index: 1000;
|
| 445 |
+
backdrop-filter: blur(4px);
|
| 446 |
+
}
|
| 447 |
+
|
| 448 |
+
.loading-content {
|
| 449 |
+
background: white;
|
| 450 |
+
padding: 40px;
|
| 451 |
+
border-radius: 16px;
|
| 452 |
+
text-align: center;
|
| 453 |
+
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
|
| 454 |
+
max-width: 400px;
|
| 455 |
+
width: 90%;
|
| 456 |
+
}
|
| 457 |
+
|
| 458 |
+
.loading-content p {
|
| 459 |
+
margin-top: 20px;
|
| 460 |
+
font-size: 1.1rem;
|
| 461 |
+
color: #475569;
|
| 462 |
+
}
|
| 463 |
+
|
| 464 |
+
/* Responsive Design */
|
| 465 |
+
@media (max-width: 1024px) {
|
| 466 |
+
.content-grid {
|
| 467 |
+
grid-template-columns: 1fr;
|
| 468 |
+
gap: 24px;
|
| 469 |
+
}
|
| 470 |
+
|
| 471 |
+
.control-panel {
|
| 472 |
+
position: static;
|
| 473 |
+
}
|
| 474 |
+
}
|
| 475 |
+
|
| 476 |
+
@media (max-width: 768px) {
|
| 477 |
+
.main-container {
|
| 478 |
+
padding: 20px 16px;
|
| 479 |
+
}
|
| 480 |
+
|
| 481 |
+
.professional-header {
|
| 482 |
+
padding: 32px 20px;
|
| 483 |
+
}
|
| 484 |
+
|
| 485 |
+
.professional-header h1 {
|
| 486 |
+
font-size: 2.25rem;
|
| 487 |
+
flex-direction: column;
|
| 488 |
+
gap: 8px;
|
| 489 |
+
}
|
| 490 |
+
|
| 491 |
+
.config-section,
|
| 492 |
+
.control-panel,
|
| 493 |
+
.results-section {
|
| 494 |
+
padding: 20px;
|
| 495 |
+
}
|
| 496 |
+
|
| 497 |
+
.chat-content {
|
| 498 |
+
max-width: 90%;
|
| 499 |
+
}
|
| 500 |
+
}
|
| 501 |
+
|
| 502 |
+
@media (max-width: 480px) {
|
| 503 |
+
.professional-header h1 {
|
| 504 |
+
font-size: 1.875rem;
|
| 505 |
+
}
|
| 506 |
+
|
| 507 |
+
.professional-header p {
|
| 508 |
+
font-size: 1rem;
|
| 509 |
+
}
|
| 510 |
+
|
| 511 |
+
.section-header h3 {
|
| 512 |
+
font-size: 1.1rem;
|
| 513 |
+
}
|
| 514 |
+
|
| 515 |
+
.config-section,
|
| 516 |
+
.control-panel,
|
| 517 |
+
.results-section {
|
| 518 |
+
padding: 16px;
|
| 519 |
+
}
|
| 520 |
+
}
|
| 521 |
+
|
| 522 |
+
/* Custom Scrollbar */
|
| 523 |
+
::-webkit-scrollbar {
|
| 524 |
+
width: 8px;
|
| 525 |
+
}
|
| 526 |
+
|
| 527 |
+
::-webkit-scrollbar-track {
|
| 528 |
+
background: #f1f5f9;
|
| 529 |
+
border-radius: 4px;
|
| 530 |
+
}
|
| 531 |
+
|
| 532 |
+
::-webkit-scrollbar-thumb {
|
| 533 |
+
background: linear-gradient(135deg, #3b82f6, #8b5cf6);
|
| 534 |
+
border-radius: 4px;
|
| 535 |
+
}
|
| 536 |
+
|
| 537 |
+
::-webkit-scrollbar-thumb:hover {
|
| 538 |
+
background: linear-gradient(135deg, #2563eb, #7c3aed);
|
| 539 |
+
}
|
| 540 |
+
|
| 541 |
+
/* Material Design Lite Overrides */
|
| 542 |
+
.mdl-button--raised {
|
| 543 |
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) !important;
|
| 544 |
+
}
|
| 545 |
+
|
| 546 |
+
.mdl-button--raised:hover {
|
| 547 |
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15) !important;
|
| 548 |
+
}
|
| 549 |
+
|
| 550 |
+
/* Snackbar Styling */
|
| 551 |
+
.mdl-snackbar {
|
| 552 |
+
background: #1e293b !important;
|
| 553 |
+
color: white !important;
|
| 554 |
+
border-radius: 8px !important;
|
| 555 |
+
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.2) !important;
|
| 556 |
+
}
|
| 557 |
+
|
| 558 |
+
.mdl-snackbar__text {
|
| 559 |
+
font-family: 'Inter', sans-serif !important;
|
| 560 |
+
font-weight: 500 !important;
|
| 561 |
+
}
|
app.py
CHANGED
|
@@ -1,64 +1,311 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
-
from
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
"""
|
| 7 |
-
client = InferenceClient("HuggingFaceH4/zephyr-7b-beta")
|
| 8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
-
def
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
messages = [{"role": "system", "content": system_message}]
|
| 19 |
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
|
|
|
| 25 |
|
| 26 |
-
|
| 27 |
|
| 28 |
-
|
|
|
|
| 29 |
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
stream=True,
|
| 34 |
-
temperature=temperature,
|
| 35 |
-
top_p=top_p,
|
| 36 |
-
):
|
| 37 |
-
token = message.choices[0].delta.content
|
| 38 |
|
| 39 |
-
|
| 40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
],
|
| 60 |
-
)
|
| 61 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
|
| 63 |
if __name__ == "__main__":
|
| 64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
+
from fastapi import FastAPI
|
| 3 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 4 |
+
from fastapi.staticfiles import StaticFiles
|
| 5 |
+
from fastapi.responses import FileResponse
|
| 6 |
+
from get_tools_working import get_leadgen_agent_and_prompt
|
| 7 |
+
import re
|
| 8 |
+
import json
|
| 9 |
+
import os
|
| 10 |
|
| 11 |
+
# Server is generic - no defaults defined here
|
| 12 |
+
# All defaults are handled by the UI layer for better separation of concerns
|
|
|
|
|
|
|
| 13 |
|
| 14 |
+
def validate_email(email):
|
| 15 |
+
"""Validate email format"""
|
| 16 |
+
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
|
| 17 |
+
return re.match(pattern, email) is not None
|
| 18 |
|
| 19 |
+
def parse_rss_urls(rss_text):
|
| 20 |
+
"""Parse RSS URLs from text input"""
|
| 21 |
+
urls = []
|
| 22 |
+
for line in rss_text.strip().split('\n'):
|
| 23 |
+
line = line.strip()
|
| 24 |
+
if line and (line.startswith('http://') or line.startswith('https://')):
|
| 25 |
+
urls.append(line)
|
| 26 |
+
return urls
|
|
|
|
| 27 |
|
| 28 |
+
def create_enhanced_prompt(system_prompt, rss_urls, preferences, email):
|
| 29 |
+
"""Create enhanced prompt with all user inputs"""
|
| 30 |
+
rss_url_list = parse_rss_urls(rss_urls)
|
| 31 |
+
rss_urls_str = ' '.join(rss_url_list)
|
| 32 |
+
|
| 33 |
+
enhanced_prompt = f"""RSS Feed URLs: {rss_urls_str}
|
| 34 |
|
| 35 |
+
{system_prompt}
|
| 36 |
|
| 37 |
+
User Preferences:
|
| 38 |
+
{preferences}
|
| 39 |
|
| 40 |
+
Send the report to: {email}"""
|
| 41 |
+
|
| 42 |
+
return enhanced_prompt
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
|
| 44 |
+
def generate_lead_report(email, rss_feeds, system_prompt, preferences):
|
| 45 |
+
"""Main API function to generate lead report"""
|
| 46 |
+
try:
|
| 47 |
+
# Enhanced validation with better feedback
|
| 48 |
+
if not email or not validate_email(email):
|
| 49 |
+
return {
|
| 50 |
+
"success": False,
|
| 51 |
+
"error": "Invalid email address",
|
| 52 |
+
"message": "Please provide a valid email address to receive your lead intelligence report."
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
rss_urls = parse_rss_urls(rss_feeds)
|
| 56 |
+
if not rss_urls:
|
| 57 |
+
return {
|
| 58 |
+
"success": False,
|
| 59 |
+
"error": "No RSS sources",
|
| 60 |
+
"message": "At least one valid RSS feed URL is required for lead generation."
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
if not system_prompt.strip():
|
| 64 |
+
return {
|
| 65 |
+
"success": False,
|
| 66 |
+
"error": "Missing AI prompt",
|
| 67 |
+
"message": "AI system prompt is required for intelligent lead analysis."
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
# Create agent with user's email for this request
|
| 71 |
+
agent, prompt_template = get_leadgen_agent_and_prompt(user_email=email)
|
| 72 |
+
|
| 73 |
+
# Create enhanced prompt
|
| 74 |
+
enhanced_prompt = create_enhanced_prompt(system_prompt, rss_feeds, preferences, email)
|
| 75 |
+
|
| 76 |
+
# Run the agent
|
| 77 |
+
result = agent(enhanced_prompt)
|
| 78 |
+
response = str(result)
|
| 79 |
+
|
| 80 |
+
return {
|
| 81 |
+
"success": True,
|
| 82 |
+
"result": response,
|
| 83 |
+
"message": f"Lead intelligence report has been generated and sent to {email}",
|
| 84 |
+
"email": email,
|
| 85 |
+
"sources_count": len(rss_urls)
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
except Exception as e:
|
| 89 |
+
return {
|
| 90 |
+
"success": False,
|
| 91 |
+
"error": str(e),
|
| 92 |
+
"message": "An error occurred during lead generation. Please check your configuration and try again."
|
| 93 |
+
}
|
| 94 |
|
| 95 |
+
def get_server_info():
|
| 96 |
+
"""API function to get server information"""
|
| 97 |
+
return {
|
| 98 |
+
"success": True,
|
| 99 |
+
"server_info": {
|
| 100 |
+
"name": "AI Lead Generation API",
|
| 101 |
+
"version": "1.0.0",
|
| 102 |
+
"description": "Generic API server for lead generation - configuration handled by UI layer",
|
| 103 |
+
"endpoints": [
|
| 104 |
+
"/generate_lead_report",
|
| 105 |
+
"/validate_configuration"
|
| 106 |
+
]
|
| 107 |
+
}
|
| 108 |
+
}
|
| 109 |
|
| 110 |
+
def validate_configuration(email, rss_feeds, system_prompt, preferences):
|
| 111 |
+
"""API function to validate configuration without running the agent"""
|
| 112 |
+
validation_results = {
|
| 113 |
+
"email_valid": validate_email(email) if email else False,
|
| 114 |
+
"rss_valid": len(parse_rss_urls(rss_feeds)) > 0,
|
| 115 |
+
"prompt_valid": bool(system_prompt.strip()),
|
| 116 |
+
"preferences_valid": bool(preferences.strip())
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
validation_results["all_valid"] = all(validation_results.values())
|
| 120 |
+
|
| 121 |
+
return {
|
| 122 |
+
"success": True,
|
| 123 |
+
"validation": validation_results,
|
| 124 |
+
"rss_count": len(parse_rss_urls(rss_feeds))
|
| 125 |
+
}
|
|
|
|
|
|
|
| 126 |
|
| 127 |
+
# Create API-only Gradio interface
|
| 128 |
+
def create_api_interface():
|
| 129 |
+
"""Create Gradio interface for API endpoints only"""
|
| 130 |
+
|
| 131 |
+
with gr.Blocks(title="AI Lead Generation API") as api_demo:
|
| 132 |
+
gr.Markdown("# AI Lead Generation API Server")
|
| 133 |
+
gr.Markdown("This server provides generic API endpoints for lead generation. All configuration is handled by the UI layer.")
|
| 134 |
+
|
| 135 |
+
with gr.Tab("Generate Report"):
|
| 136 |
+
gr.Markdown("### Generate Lead Report API")
|
| 137 |
+
|
| 138 |
+
with gr.Row():
|
| 139 |
+
with gr.Column():
|
| 140 |
+
api_email = gr.Textbox(
|
| 141 |
+
label="Email Address",
|
| 142 |
+
placeholder="your.email@company.com"
|
| 143 |
+
)
|
| 144 |
+
api_rss_feeds = gr.Textbox(
|
| 145 |
+
label="RSS Feed URLs",
|
| 146 |
+
lines=6,
|
| 147 |
+
placeholder="One URL per line"
|
| 148 |
+
)
|
| 149 |
+
api_system_prompt = gr.Textbox(
|
| 150 |
+
label="AI System Prompt",
|
| 151 |
+
lines=10,
|
| 152 |
+
placeholder="Enter your AI system prompt"
|
| 153 |
+
)
|
| 154 |
+
api_preferences = gr.Textbox(
|
| 155 |
+
label="Targeting Preferences",
|
| 156 |
+
lines=6,
|
| 157 |
+
placeholder="Enter your targeting preferences"
|
| 158 |
+
)
|
| 159 |
+
|
| 160 |
+
with gr.Column():
|
| 161 |
+
api_generate_btn = gr.Button("Generate Report", variant="primary")
|
| 162 |
+
api_result = gr.JSON(label="API Response")
|
| 163 |
+
|
| 164 |
+
api_generate_btn.click(
|
| 165 |
+
generate_lead_report,
|
| 166 |
+
inputs=[api_email, api_rss_feeds, api_system_prompt, api_preferences],
|
| 167 |
+
outputs=[api_result]
|
| 168 |
+
)
|
| 169 |
+
|
| 170 |
+
with gr.Tab("Server Info"):
|
| 171 |
+
gr.Markdown("### Server Information")
|
| 172 |
+
|
| 173 |
+
with gr.Row():
|
| 174 |
+
with gr.Column():
|
| 175 |
+
info_btn = gr.Button("Get Server Info", variant="secondary")
|
| 176 |
+
info_result = gr.JSON(label="Server Information")
|
| 177 |
+
|
| 178 |
+
with gr.Column():
|
| 179 |
+
val_email = gr.Textbox(label="Email", placeholder="test@example.com")
|
| 180 |
+
val_rss = gr.Textbox(label="RSS Feeds", lines=3, placeholder="Enter RSS URLs")
|
| 181 |
+
val_prompt = gr.Textbox(label="System Prompt", lines=5, placeholder="Enter system prompt")
|
| 182 |
+
val_pref = gr.Textbox(label="Preferences", lines=3, placeholder="Enter preferences")
|
| 183 |
+
validate_btn = gr.Button("Validate Configuration", variant="secondary")
|
| 184 |
+
validation_result = gr.JSON(label="Validation Result")
|
| 185 |
+
|
| 186 |
+
info_btn.click(
|
| 187 |
+
get_server_info,
|
| 188 |
+
outputs=[info_result]
|
| 189 |
+
)
|
| 190 |
+
|
| 191 |
+
validate_btn.click(
|
| 192 |
+
validate_configuration,
|
| 193 |
+
inputs=[val_email, val_rss, val_prompt, val_pref],
|
| 194 |
+
outputs=[validation_result]
|
| 195 |
+
)
|
| 196 |
+
|
| 197 |
+
with gr.Tab("API Documentation"):
|
| 198 |
+
gr.Markdown("""
|
| 199 |
+
## API Endpoints
|
| 200 |
+
|
| 201 |
+
### 1. Generate Lead Report
|
| 202 |
+
**Endpoint:** `/api/v1/generate`
|
| 203 |
+
**Method:** POST
|
| 204 |
+
**Parameters:**
|
| 205 |
+
- `email`: Valid email address for report delivery
|
| 206 |
+
- `rss_feeds`: RSS feed URLs (one per line)
|
| 207 |
+
- `system_prompt`: AI system prompt for lead analysis
|
| 208 |
+
- `preferences`: User targeting preferences
|
| 209 |
+
|
| 210 |
+
**Response:**
|
| 211 |
+
```json
|
| 212 |
+
{
|
| 213 |
+
"success": true/false,
|
| 214 |
+
"result": "Generated report text",
|
| 215 |
+
"message": "Status message",
|
| 216 |
+
"email": "recipient@email.com",
|
| 217 |
+
"sources_count": 3,
|
| 218 |
+
"error": "Error message if failed"
|
| 219 |
+
}
|
| 220 |
+
```
|
| 221 |
+
|
| 222 |
+
### 2. Get Default Configuration
|
| 223 |
+
**Endpoint:** `/api/v1/config`
|
| 224 |
+
**Method:** GET
|
| 225 |
+
|
| 226 |
+
**Response:**
|
| 227 |
+
```json
|
| 228 |
+
{
|
| 229 |
+
"success": true,
|
| 230 |
+
"config": {
|
| 231 |
+
"default_rss_feeds": "RSS feeds string",
|
| 232 |
+
"default_preferences": "Preferences string",
|
| 233 |
+
"default_prompt": "System prompt"
|
| 234 |
+
}
|
| 235 |
+
}
|
| 236 |
+
```
|
| 237 |
+
|
| 238 |
+
### 3. Validate Configuration
|
| 239 |
+
**Endpoint:** `/api/v1/validate`
|
| 240 |
+
**Method:** POST
|
| 241 |
+
**Parameters:** Same as generate endpoint
|
| 242 |
+
|
| 243 |
+
**Response:**
|
| 244 |
+
```json
|
| 245 |
+
{
|
| 246 |
+
"success": true,
|
| 247 |
+
"validation": {
|
| 248 |
+
"email_valid": true/false,
|
| 249 |
+
"rss_valid": true/false,
|
| 250 |
+
"prompt_valid": true/false,
|
| 251 |
+
"preferences_valid": true/false,
|
| 252 |
+
"all_valid": true/false
|
| 253 |
+
},
|
| 254 |
+
"rss_count": 3
|
| 255 |
+
}
|
| 256 |
+
```
|
| 257 |
+
|
| 258 |
+
## Usage with Material UI Frontend
|
| 259 |
+
|
| 260 |
+
The Material UI frontend at `/ai-agent-ui/` will make requests to these endpoints:
|
| 261 |
+
|
| 262 |
+
1. **Load Configuration:** GET `/api/v1/config`
|
| 263 |
+
2. **Validate Form:** POST `/api/v1/validate`
|
| 264 |
+
3. **Generate Report:** POST `/api/v1/generate`
|
| 265 |
+
|
| 266 |
+
## CORS Headers
|
| 267 |
+
|
| 268 |
+
The API server includes CORS headers to allow requests from the Material UI frontend.
|
| 269 |
+
|
| 270 |
+
## Error Handling
|
| 271 |
+
|
| 272 |
+
All endpoints return consistent JSON responses with `success` boolean and appropriate error messages.
|
| 273 |
+
""")
|
| 274 |
+
|
| 275 |
+
return api_demo
|
| 276 |
|
| 277 |
if __name__ == "__main__":
|
| 278 |
+
import uvicorn
|
| 279 |
+
|
| 280 |
+
print("🚀 Launching AI Lead Generation API Server...")
|
| 281 |
+
|
| 282 |
+
# Create Gradio interface
|
| 283 |
+
demo = create_api_interface()
|
| 284 |
+
|
| 285 |
+
# Create FastAPI app
|
| 286 |
+
app = FastAPI(title="AI Lead Generation API", version="1.0.0")
|
| 287 |
+
|
| 288 |
+
# Add CORS middleware
|
| 289 |
+
print("📡 Configuring server with CORS support...")
|
| 290 |
+
app.add_middleware(
|
| 291 |
+
CORSMiddleware,
|
| 292 |
+
allow_origins=["*"],
|
| 293 |
+
allow_credentials=True, # Enable credentials for Gradio client
|
| 294 |
+
allow_methods=["*"],
|
| 295 |
+
allow_headers=["*"],
|
| 296 |
+
)
|
| 297 |
+
|
| 298 |
+
# Mount the frontend directory
|
| 299 |
+
app.mount("/ai-agent-ui", StaticFiles(directory="ai-agent-ui", html=True), name="frontend")
|
| 300 |
+
|
| 301 |
+
# Mount Gradio app to FastAPI
|
| 302 |
+
app = gr.mount_gradio_app(app, demo, path="/")
|
| 303 |
+
|
| 304 |
+
print("✅ CORS configured successfully")
|
| 305 |
+
print("🌐 Server will be available at: http://localhost:7860")
|
| 306 |
+
print("🎨 Frontend available at: http://localhost:7860/ai-agent-ui/index.html")
|
| 307 |
+
print("📋 API documentation at: http://localhost:7860/docs")
|
| 308 |
+
print("🎯 Starting server with: python app_api.py")
|
| 309 |
+
|
| 310 |
+
# Launch server directly from app_api.py
|
| 311 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|
get_tools_working.py
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Recruitment Lead Generation Agent
|
| 4 |
+
|
| 5 |
+
An AI-powered agent that generates high-quality leads for recruitment agencies by monitoring
|
| 6 |
+
funding announcements and other hiring signals using the strands framework.
|
| 7 |
+
|
| 8 |
+
This agent implements an ETL (Extract, Transform, Load) pipeline as a workflow:
|
| 9 |
+
1. Extract: Fetch RSS feeds from multiple sources
|
| 10 |
+
2. Transform: Use LLM to analyze articles and score leads
|
| 11 |
+
3. Load: Generate CSV report and log summary information
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
import os
|
| 15 |
+
import json
|
| 16 |
+
import pandas as pd
|
| 17 |
+
from datetime import datetime
|
| 18 |
+
import requests
|
| 19 |
+
import sys
|
| 20 |
+
import xml.etree.ElementTree as ET
|
| 21 |
+
from botocore.config import Config as BotocoreConfig
|
| 22 |
+
|
| 23 |
+
from strands import Agent, tool
|
| 24 |
+
from strands.models import BedrockModel
|
| 25 |
+
from strands_tools import workflow
|
| 26 |
+
|
| 27 |
+
from sendgrid import SendGridAPIClient
|
| 28 |
+
from sendgrid.helpers.mail import Mail
|
| 29 |
+
|
| 30 |
+
# Allow full tool use without prompting
|
| 31 |
+
os.environ.setdefault("BYPASS_TOOL_CONSENT", "true")
|
| 32 |
+
|
| 33 |
+
# Define tool functions first
|
| 34 |
+
|
| 35 |
+
def fetch_rss(url: str = "https://techfundingnews.com/feed") -> str:
|
| 36 |
+
"""
|
| 37 |
+
Extract: Load RSS feed as raw XML from the specified URL.
|
| 38 |
+
|
| 39 |
+
Args:
|
| 40 |
+
url: The URL of the RSS feed to fetch (default: techfundingnews.com/feed)
|
| 41 |
+
|
| 42 |
+
Returns:
|
| 43 |
+
The raw XML content of the RSS feed
|
| 44 |
+
"""
|
| 45 |
+
try:
|
| 46 |
+
print(f"Fetching RSS feed from {url}")
|
| 47 |
+
# Add headers to mimic a browser request
|
| 48 |
+
headers = {
|
| 49 |
+
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
| 50 |
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
|
| 51 |
+
'Accept-Language': 'en-US,en;q=0.9',
|
| 52 |
+
'Referer': 'https://techfundingnews.com/'
|
| 53 |
+
}
|
| 54 |
+
response = requests.get(url, headers=headers)
|
| 55 |
+
print(f"Response status code: {response.status_code}")
|
| 56 |
+
|
| 57 |
+
if response.status_code != 200:
|
| 58 |
+
print(f"Error fetching RSS feed: {response.status_code}")
|
| 59 |
+
print(f"Response content: {response.text[:500]}...") # Print first 500 chars of response
|
| 60 |
+
return ""
|
| 61 |
+
|
| 62 |
+
print(f"Successfully fetched RSS feed, content length: {len(response.text)} characters")
|
| 63 |
+
|
| 64 |
+
# Count the number of <item> tags to see how many entries are in the feed
|
| 65 |
+
item_count = response.text.count("<item>")
|
| 66 |
+
print(f"Number of <item> tags found in RSS feed: {item_count}")
|
| 67 |
+
|
| 68 |
+
return response.text
|
| 69 |
+
except Exception as e:
|
| 70 |
+
print(f"Exception fetching RSS feed: {str(e)}")
|
| 71 |
+
return ""
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def parse_rss(xml: str):
|
| 75 |
+
"""
|
| 76 |
+
Parse RSS feed XML using Python's built-in XML parser.
|
| 77 |
+
|
| 78 |
+
Args:
|
| 79 |
+
xml: The raw XML content of the RSS feed
|
| 80 |
+
|
| 81 |
+
Returns:
|
| 82 |
+
A list of dictionaries with title, link, pubDate, summary
|
| 83 |
+
"""
|
| 84 |
+
import re # Import re module here
|
| 85 |
+
|
| 86 |
+
if not xml:
|
| 87 |
+
print("No XML content to parse")
|
| 88 |
+
return []
|
| 89 |
+
|
| 90 |
+
print("Parsing RSS feed with Python XML parser")
|
| 91 |
+
try:
|
| 92 |
+
# Parse the XML
|
| 93 |
+
root = ET.fromstring(xml)
|
| 94 |
+
|
| 95 |
+
# Find all item elements
|
| 96 |
+
items = root.findall(".//item")
|
| 97 |
+
print(f"Found {len(items)} items in RSS feed")
|
| 98 |
+
|
| 99 |
+
entries = []
|
| 100 |
+
for item in items:
|
| 101 |
+
# Extract the title, link, pubDate, and description
|
| 102 |
+
title = item.find("title").text if item.find("title") is not None else ""
|
| 103 |
+
link = item.find("link").text if item.find("link") is not None else ""
|
| 104 |
+
pub_date = item.find("pubDate").text if item.find("pubDate") is not None else ""
|
| 105 |
+
|
| 106 |
+
# For the summary, try description first, then content:encoded
|
| 107 |
+
summary = ""
|
| 108 |
+
if item.find("description") is not None:
|
| 109 |
+
summary = item.find("description").text
|
| 110 |
+
elif item.find("{http://purl.org/rss/1.0/modules/content/}encoded") is not None:
|
| 111 |
+
summary = item.find("{http://purl.org/rss/1.0/modules/content/}encoded").text
|
| 112 |
+
|
| 113 |
+
# Clean up the summary (remove HTML tags)
|
| 114 |
+
summary = re.sub(r'<[^>]+>', '', summary) if summary else ""
|
| 115 |
+
summary = summary.strip()
|
| 116 |
+
|
| 117 |
+
# Create the entry
|
| 118 |
+
entry = {
|
| 119 |
+
"title": title,
|
| 120 |
+
"link": link,
|
| 121 |
+
"pubDate": pub_date,
|
| 122 |
+
"summary": summary[:500] # Limit summary length
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
entries.append(entry)
|
| 126 |
+
|
| 127 |
+
print(f"Successfully parsed {len(entries)} entries from RSS feed")
|
| 128 |
+
return entries
|
| 129 |
+
except Exception as e:
|
| 130 |
+
print(f"Error parsing RSS feed: {str(e)}")
|
| 131 |
+
print(f"Exception type: {type(e).__name__}")
|
| 132 |
+
print(f"Traceback: {sys.exc_info()}")
|
| 133 |
+
return []
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
@tool
|
| 137 |
+
def fetch_and_parse_feeds(feed_urls: list, max_entries_per_feed: int = 4):
|
| 138 |
+
"""Tool function to fetch and parse multiple RSS feeds. Use this tool to fetch RSS content for given list of feed urls.
|
| 139 |
+
|
| 140 |
+
Args:
|
| 141 |
+
feed_urls: List of RSS feed URLs to fetch and parse (required)
|
| 142 |
+
max_entries_per_feed: Maximum number of entries to return per feed (default: 4)
|
| 143 |
+
|
| 144 |
+
Returns:
|
| 145 |
+
A list of entries from all feeds
|
| 146 |
+
"""
|
| 147 |
+
# Ensure feed_urls is provided and is a list
|
| 148 |
+
if not feed_urls or not isinstance(feed_urls, list):
|
| 149 |
+
print("Error: feed_urls parameter is required and must be a list")
|
| 150 |
+
return []
|
| 151 |
+
|
| 152 |
+
# Process all feeds and collect entries
|
| 153 |
+
all_entries = []
|
| 154 |
+
for url in feed_urls:
|
| 155 |
+
# Extract domain name for logging
|
| 156 |
+
domain = url.split("//")[-1].split("/")[0]
|
| 157 |
+
print(f"\n--- Processing {domain} feed ---")
|
| 158 |
+
|
| 159 |
+
xml = fetch_rss(url)
|
| 160 |
+
if not xml:
|
| 161 |
+
print(f"Failed to fetch {domain} feed.")
|
| 162 |
+
continue
|
| 163 |
+
|
| 164 |
+
entries = parse_rss(xml)
|
| 165 |
+
if entries:
|
| 166 |
+
# Convert pubDate strings to datetime objects for sorting
|
| 167 |
+
from datetime import datetime
|
| 168 |
+
import email.utils
|
| 169 |
+
|
| 170 |
+
for entry in entries:
|
| 171 |
+
if 'pubDate' in entry and entry['pubDate']:
|
| 172 |
+
try:
|
| 173 |
+
# Parse the RFC 822 date format
|
| 174 |
+
time_tuple = email.utils.parsedate_tz(entry['pubDate'])
|
| 175 |
+
if time_tuple:
|
| 176 |
+
dt = datetime(*time_tuple[:6])
|
| 177 |
+
entry['_datetime'] = dt # Add datetime object for sorting
|
| 178 |
+
except Exception as e:
|
| 179 |
+
print(f"Error parsing date: {e}")
|
| 180 |
+
entry['_datetime'] = datetime(1970, 1, 1) # Default to epoch if parsing fails
|
| 181 |
+
else:
|
| 182 |
+
entry['_datetime'] = datetime(1970, 1, 1) # Default to epoch if no date
|
| 183 |
+
|
| 184 |
+
# Sort entries by date (newest first)
|
| 185 |
+
sorted_entries = sorted(entries, key=lambda x: x.get('_datetime', datetime(1970, 1, 1)), reverse=True)
|
| 186 |
+
|
| 187 |
+
# Remove the temporary _datetime field
|
| 188 |
+
for entry in sorted_entries:
|
| 189 |
+
if '_datetime' in entry:
|
| 190 |
+
del entry['_datetime']
|
| 191 |
+
|
| 192 |
+
# Limit the number of entries per feed
|
| 193 |
+
limited_entries = sorted_entries[:max_entries_per_feed]
|
| 194 |
+
|
| 195 |
+
# Add source information to each entry
|
| 196 |
+
for entry in limited_entries:
|
| 197 |
+
entry["source"] = url
|
| 198 |
+
entry["feed"] = domain
|
| 199 |
+
|
| 200 |
+
print(f"Found {len(entries)} entries from {domain}, using {len(limited_entries)} most recent")
|
| 201 |
+
all_entries.extend(limited_entries)
|
| 202 |
+
else:
|
| 203 |
+
print(f"No entries found in {domain} feed.")
|
| 204 |
+
|
| 205 |
+
print(f"\nTotal entries collected from all sources: {len(all_entries)}")
|
| 206 |
+
return all_entries
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
@tool
|
| 210 |
+
def send_email(to: str, html_content: str, subject: str) -> str:
|
| 211 |
+
"""Send an email to given address"""
|
| 212 |
+
|
| 213 |
+
print(f"Sending to {to} content {html_content} and subject {subject}.")
|
| 214 |
+
|
| 215 |
+
# Get SendGrid API key from environment variable
|
| 216 |
+
sendgrid_api_key = os.getenv("SENDGRID_API_KEY")
|
| 217 |
+
if not sendgrid_api_key:
|
| 218 |
+
raise ValueError("SENDGRID_API_KEY environment variable is not set")
|
| 219 |
+
|
| 220 |
+
message = Mail(
|
| 221 |
+
from_email='kavukcu.tolga@gmail.com',
|
| 222 |
+
to_emails=to,
|
| 223 |
+
subject=subject,
|
| 224 |
+
html_content=html_content
|
| 225 |
+
)
|
| 226 |
+
|
| 227 |
+
try:
|
| 228 |
+
sg = SendGridAPIClient(sendgrid_api_key)
|
| 229 |
+
# sg.set_sendgrid_data_residency("eu")
|
| 230 |
+
# uncomment the above line if you are sending mail using a regional EU subuser
|
| 231 |
+
response = sg.send(message)
|
| 232 |
+
print(response.status_code)
|
| 233 |
+
print(response.body)
|
| 234 |
+
print(response.headers)
|
| 235 |
+
return f"Mail is sent to {to}"
|
| 236 |
+
except Exception as e:
|
| 237 |
+
print(f"Error sending email: {str(e)}")
|
| 238 |
+
return f"Failed to send email to {to}: {str(e)}"
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
def get_leadgen_agent_and_prompt(user_email: str):
|
| 242 |
+
"""
|
| 243 |
+
Returns a tuple of (agent, default_prompt) for use in Gradio or other interfaces.
|
| 244 |
+
|
| 245 |
+
Args:
|
| 246 |
+
user_email: The email address to send the report to (required - no default)
|
| 247 |
+
"""
|
| 248 |
+
if not user_email:
|
| 249 |
+
raise ValueError("user_email is required and cannot be empty")
|
| 250 |
+
|
| 251 |
+
custom_config = BotocoreConfig(read_timeout=3600)
|
| 252 |
+
bedrock_model = BedrockModel(
|
| 253 |
+
model_id="us.amazon.nova-pro-v1:0",
|
| 254 |
+
temperature=0.2,
|
| 255 |
+
region_name="us-west-2",
|
| 256 |
+
boto_client_config=custom_config,
|
| 257 |
+
streaming=False,
|
| 258 |
+
)
|
| 259 |
+
agent = Agent(tools=[fetch_and_parse_feeds, send_email], model=bedrock_model)
|
| 260 |
+
|
| 261 |
+
default_prompt = (
|
| 262 |
+
"Analyze articles and score leads based on funding and hiring signals from feeds. "
|
| 263 |
+
"https://techfundingnews.com/feed https://feeds.feedburner.com/vcnewsdaily https://news.crunchbase.com/feed/\n"
|
| 264 |
+
"You are a lead scoring AI for a recruitment agency. Your task is to analyze funding news articles and score them based on their potential as recruitment leads.\n"
|
| 265 |
+
"\nFor each article, extract and return the following information:\n"
|
| 266 |
+
"- companyName: The name of the company that received funding\n"
|
| 267 |
+
"- fundingStage: The funding stage (e.g., Seed, Series A, Series B, etc.)\n"
|
| 268 |
+
"- fundingAmount: The funding amount as a number (in millions)\n"
|
| 269 |
+
"- hiringSignal: Boolean (true/false) indicating if there are signals the company is hiring\n"
|
| 270 |
+
"- News source, with link.\n"
|
| 271 |
+
"- score: A score from 0-100 based on the following criteria:\n"
|
| 272 |
+
" * Series A and B rounds score higher (70-90) than Seed rounds (40-60)\n"
|
| 273 |
+
" * Higher funding amounts increase the score\n"
|
| 274 |
+
" * Explicit mentions of hiring or team expansion increase the score\n"
|
| 275 |
+
" * Tech companies and startups score higher than other industries\n"
|
| 276 |
+
"- reasoning: A brief explanation of the score (1-2 sentences)\n"
|
| 277 |
+
"- status: 'Active Hiring' if hiringSignal is true, otherwise 'Pre-emptive Opportunity'\n\n"
|
| 278 |
+
f"When you have the data, send an email to {user_email}. Ensure the email is professionally formatted and presents a professional analysis. So apply styling and formatting to html email."
|
| 279 |
+
)
|
| 280 |
+
return agent, default_prompt
|
| 281 |
+
|
| 282 |
+
|
| 283 |
+
if __name__ == "__main__":
|
| 284 |
+
print("Initializing recruitment lead generation agent...")
|
| 285 |
+
|
| 286 |
+
# Print Python version
|
| 287 |
+
print(f"Python version: {sys.version}")
|
| 288 |
+
|
| 289 |
+
# Create the Bedrock model with Claude 4 Sonnet
|
| 290 |
+
print("Creating Bedrock model...")
|
| 291 |
+
# The model and prompt are now handled by get_leadgen_agent_and_prompt()
|
| 292 |
+
# custom_config = BotocoreConfig(read_timeout=3600)
|
| 293 |
+
# bedrock_model = BedrockModel(
|
| 294 |
+
# model_id="us.amazon.nova-pro-v1:0",
|
| 295 |
+
# temperature=0.2,
|
| 296 |
+
# region_name="us-west-2", # Explicitly set the region to us-west-2
|
| 297 |
+
# boto_client_config=custom_config,
|
| 298 |
+
# streaming=False,
|
| 299 |
+
# )
|
| 300 |
+
# print(f"Bedrock model type: {type(bedrock_model)}")
|
| 301 |
+
|
| 302 |
+
# Create the main agent
|
| 303 |
+
print("Creating agent...")
|
requirements.txt
CHANGED
|
@@ -1 +1,10 @@
|
|
| 1 |
-
huggingface_hub==0.25.2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
huggingface_hub==0.25.2
|
| 2 |
+
gradio
|
| 3 |
+
strands-agents
|
| 4 |
+
strands-agents-tools
|
| 5 |
+
botocore
|
| 6 |
+
pandas
|
| 7 |
+
requests
|
| 8 |
+
sendgrid
|
| 9 |
+
uvicorn
|
| 10 |
+
fastapi
|