NitishBorthakur commited on
Commit
4705b54
·
verified ·
1 Parent(s): a5be48d

Upload 10 files

Browse files
Files changed (10) hide show
  1. CONFIG.json +191 -0
  2. app.py +841 -0
  3. dockerignore +60 -0
  4. env.example +19 -0
  5. gitignore +48 -0
  6. logging.conf +69 -0
  7. packages.txt +7 -0
  8. pre-commit-config.yaml +23 -0
  9. pyproject.toml +34 -0
  10. requirements.txt +26 -0
CONFIG.json ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "app_settings": {
3
+ "title": "Multi-Source Research Assistant",
4
+ "description": "Comprehensive research analysis combining multiple data sources",
5
+ "version": "1.0.0",
6
+ "debug_mode": false
7
+ },
8
+ "rate_limits": {
9
+ "github_api": {
10
+ "calls_per_minute": 30,
11
+ "max_retries": 3,
12
+ "retry_delay": 3,
13
+ "search_rate_limit": 10,
14
+ "core_rate_limit": 5000,
15
+ "min_remaining_threshold": 5,
16
+ "wait_time_multiplier": 1.5
17
+ },
18
+ "arxiv_api": {
19
+ "calls_per_minute": 20,
20
+ "max_retries": 3,
21
+ "retry_delay": 10
22
+ },
23
+ "producthunt_api": {
24
+ "calls_per_minute": 25,
25
+ "max_retries": 3,
26
+ "retry_delay": 5
27
+ },
28
+ "reddit_api": {
29
+ "global_rate_limit": 60,
30
+ "model_rate_limit": 45,
31
+ "search_rate_limit": 45,
32
+ "content_rate_limit": 60,
33
+ "max_retries": 3,
34
+ "retry_delay": 5,
35
+ "backoff_multiplier": 2,
36
+ "max_backoff": 300,
37
+ "rate_limit_buffer": 0.1
38
+ }
39
+ },
40
+ "timeouts": {
41
+ "api_request": 300,
42
+ "analysis": 400,
43
+ "summary_generation": 300,
44
+ "total_operation": 800,
45
+ "github": {
46
+ "search": 30,
47
+ "repo_analysis": 60,
48
+ "batch_timeout": 180,
49
+ "query_preprocessing": 20
50
+ }
51
+ },
52
+ "batch_settings": {
53
+ "max_batch_size": 10,
54
+ "processing_delay": 1,
55
+ "max_items_per_request": 30,
56
+ "github": {
57
+ "max_search_results": 20,
58
+ "max_analyze_per_batch": 5,
59
+ "min_stars_threshold": 5,
60
+ "max_description_length": 1000,
61
+ "relevance_score_threshold": 0.6
62
+ }
63
+ },
64
+ "reddit_settings": {
65
+ "search": {
66
+ "max_results_per_subreddit": 5,
67
+ "posts_per_subreddit": 5,
68
+ "batch_size": 20,
69
+ "time_filter": "year",
70
+ "sort_options": ["relevance", "hot", "top", "new"],
71
+ "default_sort": "relevance",
72
+ "syntax": "cloudsearch",
73
+ "pagination_delay": 1
74
+ },
75
+ "resource_limits": {
76
+ "max_concurrent_searches": 4,
77
+ "max_retries_per_request": 3,
78
+ "request_timeout": 25
79
+ },
80
+ "content_processing": {
81
+ "max_title_length": 300,
82
+ "max_content_length": 40000,
83
+ "content_chunk_size": 2000
84
+ }
85
+ },
86
+ "producthunt_settings": {
87
+ "search": {
88
+ "max_results": 5,
89
+ "max_batch_size": 10,
90
+ "keyword_match_count": 20,
91
+ "weights": {
92
+ "name_weight": 1.0,
93
+ "tagline_weight": 3.0
94
+ }
95
+ },
96
+ "api": {
97
+ "max_retries": 3,
98
+ "retry_delay": 5,
99
+ "request_delay": 1
100
+ },
101
+ "semantic_analysis": {
102
+ "batch_size": 5,
103
+ "timeout": 60,
104
+ "min_score_threshold": 0.3
105
+ }
106
+ },
107
+ "cache": {
108
+ "enabled": true,
109
+ "max_size": 2000,
110
+ "ttl": 7200,
111
+ "cleanup_interval": 600
112
+ },
113
+ "memory_management": {
114
+ "gc_collect_frequency": 10,
115
+ "max_retries": 3,
116
+ "cleanup_on_error": true,
117
+ "batch_gc_threshold": 5
118
+ },
119
+ "github_settings": {
120
+ "search": {
121
+ "min_query_length": 3,
122
+ "max_query_length": 256,
123
+ "default_order": "desc",
124
+ "fallback_enabled": true,
125
+ "languages_priority": ["python", "javascript", "typescript", "java", "go"],
126
+ "exclude_archived": true,
127
+ "exclude_forks": true,
128
+ "initial_result_count": 20,
129
+ "relevance_scoring_count": 10,
130
+ "final_analysis_count": 5,
131
+ "default_sort": "best-match",
132
+ "preprocessing": {
133
+ "min_keyword_length": 3,
134
+ "max_keywords": 4,
135
+ "gemini_enhancement": {
136
+ "temperature": 0.1,
137
+ "top_k": 10,
138
+ "top_p": 0.5,
139
+ "max_retries": 3
140
+ },
141
+ "nlp_extraction": {
142
+ "min_relevance_score": 0.6,
143
+ "max_tokens_per_keyword": 20,
144
+ "pos_weights": {
145
+ "NOUN": 1.0,
146
+ "VERB": 0.8,
147
+ "ADJ": 0.6
148
+ }
149
+ }
150
+ }
151
+ },
152
+ "analysis": {
153
+ "relevance_weights": {
154
+ "stars": 2.0,
155
+ "recency": 1.5,
156
+ "description_similarity": 8.0,
157
+ "topics_match": 0.3
158
+ },
159
+ "max_analysis_retries": 2,
160
+ "max_concurrent_analyses": 3
161
+ }
162
+ },
163
+ "allowed_subreddits": [
164
+ "InternetIsBeautiful",
165
+ "Indiewebdev",
166
+ "SideProject",
167
+ "Productivity",
168
+ "HuggingFace",
169
+ "ArtificialInteligence",
170
+ "MachineLearning",
171
+ "deeplearning",
172
+ "webdev",
173
+ "Futurology",
174
+ "ProductHunters",
175
+ "coolgithubprojects",
176
+ "github"
177
+ ],
178
+ "error_handling": {
179
+ "max_retries": 3,
180
+ "retry_delay_base": 2,
181
+ "retry_delay_max": 10,
182
+ "fail_fast": false,
183
+ "retry_status_codes": [429, 500, 502, 503, 504]
184
+ },
185
+ "ui_settings": {
186
+ "theme": "default",
187
+ "max_input_length": 1000,
188
+ "refresh_interval": 5000,
189
+ "show_progress": true
190
+ }
191
+ }
app.py ADDED
@@ -0,0 +1,841 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ import sys
4
+ from sentence_transformers import SentenceTransformer
5
+ import torch
6
+ import torch.nn.functional as F
7
+ from functools import lru_cache
8
+ # Debug print: Check current working directory
9
+ import os
10
+ import subprocess
11
+ import sys
12
+ def install_private_repo():
13
+ github_agent_token = os.getenv('GITHUB_AGENT_TOKEN')
14
+ if not github_agent_token:
15
+ print("Error: GITHUB_AGENT_TOKEN environment variable is not set.")
16
+ return False
17
+
18
+ repo_url = f"git+https://{github_agent_token}@github.com/punekichikki/ideaLensAgent.git"
19
+
20
+ try:
21
+ #print(f"Attempting to install from private repo: {repo_url.replace(github_agent_token, '*******')}")
22
+
23
+ # Install with verbose output
24
+ result = subprocess.run(
25
+ [sys.executable, "-m", "pip", "install", "-v", repo_url],
26
+ capture_output=True,
27
+ text=True
28
+ )
29
+
30
+ if result.returncode != 0:
31
+ print(f"Installation failed with error code: {result.returncode}")
32
+ print(f"stdout: {result.stdout}")
33
+ print(f"stderr: {result.stderr}")
34
+ return False
35
+
36
+ #print("Installation output:")
37
+ #print(result.stdout)
38
+
39
+ # Check installed packages
40
+ print("\nInstalled packages:")
41
+ pip_list = subprocess.run(
42
+ [sys.executable, "-m", "pip", "list"],
43
+ capture_output=True,
44
+ text=True
45
+ )
46
+ #print(pip_list.stdout)
47
+
48
+ # Check Python path
49
+ #print("\nPython path:")
50
+ #print(sys.path)
51
+
52
+ # Try to find the package location
53
+ find_package = subprocess.run(
54
+ [sys.executable, "-c", "import idealens_agents; print(idealens_agents.__file__)"],
55
+ capture_output=True,
56
+ text=True
57
+ )
58
+ #print("\nPackage location attempt:")
59
+ #print("stdout:", find_package.stdout)
60
+ #print("stderr:", find_package.stderr)
61
+
62
+ return True
63
+
64
+ except Exception as e:
65
+ print(f"Error: An unexpected error occurred: {str(e)}")
66
+ traceback.print_exc()
67
+ return False
68
+
69
+ # Add debug information before installation
70
+ #print("Current environment variables:", {k: v for k, v in os.environ.items() if 'TOKEN' in k})
71
+ #print("Python executable:", sys.executable)
72
+ #print("Python version:", sys.version)
73
+ #print("Current working directory:", os.getcwd())
74
+ #print("Directory contents:", os.listdir())
75
+
76
+ # Install private repo
77
+ install_success = install_private_repo()
78
+
79
+ if not install_success:
80
+ print("Failed to install private repository. Exiting.")
81
+ sys.exit(1)
82
+ agents_dir = os.path.join(os.path.dirname(__file__), 'agents')
83
+ sys.path.append(agents_dir)
84
+ # Try importing with more detailed error handling
85
+ from agents import GitHubAgent
86
+ from agents import ArxivSearchAgent
87
+ from agents import ProductHuntAgent
88
+ from agents import RedditAgent
89
+ from vertexai.generative_models import GenerativeModel
90
+ import vertexai
91
+ import asyncio
92
+ import json
93
+ import logging.config
94
+ from typing import Dict, Any, Optional, Tuple
95
+ import traceback
96
+ from datetime import datetime
97
+ import gc
98
+ import jinja2
99
+
100
+ custom_css = """
101
+ .center-label {
102
+ display: flex;
103
+ flex-direction: column; /* Makes the label stack above the input*/
104
+ align-items: center; /* Horizontally center the contents*/
105
+ text-align: center;
106
+ }
107
+
108
+ .center-label .form {
109
+ display: flex;
110
+ flex-direction: column;
111
+ align-items: center;
112
+ width: 100%;
113
+ }
114
+
115
+
116
+ .center-label .label {
117
+ text-align: center;
118
+ width: 100%;
119
+ }
120
+
121
+ .equal-button {
122
+ flex: 1; /* Makes the buttons share available space equally */
123
+ margin: 5px;
124
+ background-color: #f0f0f0;
125
+ font-size: 12px;
126
+ }
127
+ .loading-text textarea {
128
+ text-align: center !important;
129
+ font-weight: bold !important;
130
+ color: #e67e22 !important;
131
+ background-color: #f7f7f7 !important;
132
+ }
133
+
134
+ """
135
+ intro_text = """
136
+ <div style="padding: 20px; border: 1px solid #e0e0e0; border-radius: 8px; margin-bottom: 20px;">
137
+ <h2 style="text-align:center; margin-bottom: 10px;">Meet IdeaLens: the AI Agent for your product ideas</h2>
138
+ <div style="font-family: Arial, sans-serif; line-height: 1.6; color: #333; margin: 10px;">
139
+ <div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 20px; position: relative;">
140
+ <div style="flex: 1; margin-right: 20px; padding-right: 20px; border-right: 1px solid #e0e0e0;">
141
+ <h2 style="margin: 0; font-size: 20px; font-weight: bold;">❓ Got an idea that could change the world? 🌍</h2>
142
+ <div style="margin-top: 10px; font-size: 13px;">
143
+ 💡 <span style="font-weight: bold; color: #0073e6;">IdeaLens</span> intelligently analyzes data to chart your idea's potential<br>
144
+ 🌟 It explores user perspectives and predicts market reactions<br>
145
+ 📊 It identifies competitors and uncovers technical resources to refine your concept<br>
146
+ 📚 By integrating cutting-edge academic insights, <span style="font-weight: bold; color: #0073e6;">IdeaLens</span> ensures thorough validation<br>
147
+ 🚀 With <span style="font-weight: bold; color: #0073e6;">IdeaLens</span> assisting you, success is just an idea away!
148
+ </div>
149
+ </div>
150
+ <div style="flex: 1; margin-left: 20px;">
151
+ <h2 style="margin: 0; font-size: 20px;">How IdeaLens Assists:</h2>
152
+ <div style="margin-top: 10px; font-size: 13px;">
153
+ 🧠 IdeaLens reveals hidden insights by connecting patterns across platforms like <span style="font-weight: bold; color: #0073e6;">Reddit</span>,
154
+ <span style="font-weight: bold; color: #0073e6;">ProductHunt</span>,
155
+ <span style="font-weight: bold; color: #0073e6;">GitHub</span>, and
156
+ <span style="font-weight: bold; color: #0073e6;">arXiv</span>.<br>
157
+ 📋 It synthesizes competitive insights and surface critical technical resources to strengthen your vision<br>
158
+ 🔍 Want to go deeper? IdeaLens provides comprehensive strategic intelligence from each platform<br>
159
+ ⏳ In just <span style="font-weight: bold; color: #0073e6;">5 minutes</span>, IdeaLens delivers focused, actionable guidance
160
+ </div>
161
+ </div>
162
+ </div>
163
+
164
+ <div style="font-weight: bold; margin-top: 20px;">
165
+ 🔥 Ready to let IdeaLens assist you?<br>
166
+ 👉 Enter your idea in the search prompt or try one of the curated examples below! 🎯✨
167
+ </div>
168
+ </div>
169
+ </div>
170
+
171
+ """
172
+ # Debug print: Initial imports complete
173
+ print("Debug: Initial imports complete")
174
+
175
+ # Create logs directory if it doesn't exist
176
+ os.makedirs(os.path.join(os.path.dirname(__file__), 'logs'), exist_ok=True)
177
+ print(f"Debug: Logs directory created or exists at {os.path.join(os.path.dirname(__file__), 'logs')}")
178
+
179
+
180
+
181
+ print("Debug: Agents imported")
182
+
183
+ # Load configuration
184
+ print("Debug: Loading configuration...")
185
+ with open('CONFIG.json') as f:
186
+ CONFIG = json.load(f)
187
+ print("Debug: Configuration loaded successfully")
188
+
189
+ # Set up logging
190
+ print("Debug: Setting up logging...")
191
+ logging.config.fileConfig('logging.conf')
192
+ logger = logging.getLogger('app')
193
+ print("Debug: Logging setup complete")
194
+
195
+
196
+ class QueryPreprocessor:
197
+ def __init__(self):
198
+ self._model = None
199
+ print("Debug: QueryPreprocessor initialized") # Add print statement here
200
+
201
+ @property
202
+ @lru_cache()
203
+ def model(self) -> SentenceTransformer:
204
+
205
+ if self._model is None:
206
+ print("Debug: Loading sentence transformer model...")
207
+ self._model = SentenceTransformer('all-MiniLM-L6-v2')
208
+ logger.info("Sentence transformer model initialized")
209
+ print("Debug: Sentence transformer model loaded successfully")
210
+ return self._model
211
+
212
+ async def extract_core_concepts(self, text: str) -> str:
213
+ """Extract core concepts from text, removing structural elements"""
214
+ markers = ["Market analysis request:", "Target sector:",
215
+ "Primary features:", "Business model category:",
216
+ "Technical requirements:"]
217
+ cleaned = text
218
+ for marker in markers:
219
+ cleaned = cleaned.replace(marker, "")
220
+ return cleaned.strip()
221
+
222
+ async def check_semantic_similarity(self, original: str, processed: str,
223
+ threshold: float = 0.7) -> bool:
224
+ try:
225
+ # Extract core concepts from processed text
226
+ processed_core = await self.extract_core_concepts(processed)
227
+
228
+ # Run embedding computation in thread pool
229
+ loop = asyncio.get_event_loop()
230
+ embeddings = await loop.run_in_executor(
231
+ None,
232
+ lambda: (
233
+ self.model.encode(original, convert_to_tensor=True),
234
+ self.model.encode(processed_core, convert_to_tensor=True)
235
+ )
236
+ )
237
+ original_embedding, processed_embedding = embeddings
238
+
239
+ # Calculate cosine similarity
240
+ similarity = F.cosine_similarity(
241
+ original_embedding.unsqueeze(0),
242
+ processed_embedding.unsqueeze(0)
243
+ ).item()
244
+
245
+ logger.info(f"Original query: {original}")
246
+ logger.info(f"Processed core concepts: {processed_core}")
247
+ logger.info(f"Semantic similarity: {similarity:.3f}")
248
+
249
+ return similarity > threshold
250
+
251
+ except Exception as e:
252
+ logger.error(f"Error in semantic similarity check: {str(e)}")
253
+ return False
254
+
255
+
256
+ def configure_environment():
257
+ start_time = datetime.now()
258
+ print("Debug: Starting configure_environment")
259
+ required_env_vars = [
260
+ 'GITHUB_TOKEN',
261
+ 'PRODUCT_HUNT_TOKEN',
262
+ 'REDDIT_CLIENT_ID',
263
+ 'REDDIT_CLIENT_SECRET',
264
+ 'REDDIT_USER_AGENT',
265
+ 'GOOGLE_APPLICATION_CREDENTIALS_JSON',
266
+ 'GITHUB_APPLICATION_CREDENTIALS_JSON',
267
+ 'ARXIV_APPLICATION_CREDENTIALS_JSON',
268
+ 'PRODUCT_HUNT_APPLICATION_CREDENTIALS_JSON',
269
+ 'REDDIT_APPLICATION_CREDENTIALS_JSON',
270
+ 'GITHUB_CLOUD_PROJECT',
271
+ 'ARXIV_CLOUD_PROJECT',
272
+ 'PRODUCTHUNT_CLOUD_PROJECT',
273
+ 'REDDIT_CLOUD_PROJECT'
274
+ ]
275
+ print(f"Debug: Required environment variables: {required_env_vars}")
276
+
277
+ missing_vars = [var for var in required_env_vars if not os.getenv(var)]
278
+ if missing_vars:
279
+ error_message = f"Missing required environment variables: {', '.join(missing_vars)}. " \
280
+ f"Please check .env.example for required variables."
281
+ print(f"Debug: Error - {error_message}")
282
+ raise EnvironmentError(error_message)
283
+ print("Debug: All required environment variables are present")
284
+
285
+ # Set up Google credentials from the JSON stored in env variable
286
+ if 'GOOGLE_APPLICATION_CREDENTIALS_JSON' in os.environ:
287
+ print("Debug: Found GOOGLE_APPLICATION_CREDENTIALS_JSON")
288
+ creds_json = os.environ['GOOGLE_APPLICATION_CREDENTIALS_JSON']
289
+ with open('/tmp/google_credentials.json', 'w') as f:
290
+ f.write(creds_json)
291
+ os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = '/tmp/google_credentials.json'
292
+ print("Debug: Google credentials file created and GOOGLE_APPLICATION_CREDENTIALS set")
293
+
294
+ # Set up Github credentials from the JSON stored in env variable
295
+ if 'GITHUB_APPLICATION_CREDENTIALS_JSON' in os.environ:
296
+ print("Debug: Found GITHUB_APPLICATION_CREDENTIALS_JSON")
297
+ creds_json = os.environ['GITHUB_APPLICATION_CREDENTIALS_JSON']
298
+ with open('/tmp/github_credentials.json', 'w') as f:
299
+ f.write(creds_json)
300
+ os.environ['GITHUB_APPLICATION_CREDENTIALS'] = '/tmp/github_credentials.json'
301
+ print("Debug: Github credentials file created and GITHUB_APPLICATION_CREDENTIALS set")
302
+
303
+ # Set up Arxiv credentials from the JSON stored in env variable
304
+ if 'ARXIV_APPLICATION_CREDENTIALS_JSON' in os.environ:
305
+ print("Debug: Found ARXIV_APPLICATION_CREDENTIALS_JSON")
306
+ creds_json = os.environ['ARXIV_APPLICATION_CREDENTIALS_JSON']
307
+ with open('/tmp/arxiv_credentials.json', 'w') as f:
308
+ f.write(creds_json)
309
+ os.environ['ARXIV_APPLICATION_CREDENTIALS'] = '/tmp/arxiv_credentials.json'
310
+ print("Debug: Arxiv credentials file created and ARXIV_APPLICATION_CREDENTIALS set")
311
+
312
+ # Set up Product Hunt credentials from the JSON stored in env variable
313
+ if 'PRODUCTHUNT_APPLICATION_CREDENTIALS_JSON' in os.environ:
314
+ print("Debug: Found PRODUCTHUNT_APPLICATION_CREDENTIALS_JSON")
315
+ creds_json = os.environ['PRODUCTHUNT_APPLICATION_CREDENTIALS_JSON']
316
+ with open('/tmp/producthunt_credentials.json', 'w') as f:
317
+ f.write(creds_json)
318
+ os.environ['PRODUCTHUNT_APPLICATION_CREDENTIALS'] = '/tmp/producthunt_credentials.json'
319
+ print("Debug: Product Hunt credentials file created and PRODUCTHUNT_APPLICATION_CREDENTIALS set")
320
+
321
+ # Set up Reddit credentials from the JSON stored in env variable
322
+ if 'REDDIT_APPLICATION_CREDENTIALS_JSON' in os.environ:
323
+ print("Debug: Found REDDIT_APPLICATION_CREDENTIALS_JSON")
324
+ creds_json = os.environ['REDDIT_APPLICATION_CREDENTIALS_JSON']
325
+ with open('/tmp/reddit_credentials.json', 'w') as f:
326
+ f.write(creds_json)
327
+ os.environ['REDDIT_APPLICATION_CREDENTIALS'] = '/tmp/reddit_credentials.json'
328
+ print("Debug: Reddit credentials file created and REDDIT_APPLICATION_CREDENTIALS set")
329
+ end_time = datetime.now()
330
+ print(f"Debug: Finished configure_environment, Time taken: {end_time - start_time}")
331
+
332
+
333
+ def initialize_agents() -> Optional[Dict[str, Any]]:
334
+ """Initialize all search agents with proper error handling."""
335
+ start_time = datetime.now()
336
+ print("Debug: Starting initialize_agents")
337
+ try:
338
+ print("Debug: Initializing Vertex AI...")
339
+ logger.info("Initializing agents...")
340
+ vertexai.init(project=None, location="us-central1")
341
+ print("Debug: Vertex AI initialized successfully")
342
+ summary_model = GenerativeModel("gemini-pro")
343
+ print("Debug: Summary model initialized")
344
+
345
+ # Initialize agents
346
+ print("Debug: Initializing agents...")
347
+ github_agent = GitHubAgent(
348
+ project_id=os.getenv('GITHUB_CLOUD_PROJECT'),
349
+ credentials_path='/tmp/github_credentials.json'
350
+ )
351
+ print("Debug: GitHub agent initialized")
352
+
353
+ arxiv_agent = ArxivSearchAgent(
354
+ project_id=os.getenv('ARXIV_CLOUD_PROJECT'),
355
+ credentials_path='/tmp/arxiv_credentials.json'
356
+ )
357
+ print("Debug: Arxiv agent initialized")
358
+
359
+ producthunt_agent = ProductHuntAgent(
360
+ project_id=os.getenv('PRODUCTHUNT_CLOUD_PROJECT'),
361
+ credentials_path='/tmp/producthunt_credentials.json'
362
+ )
363
+ print("Debug: Product Hunt agent initialized")
364
+
365
+ reddit_agent = RedditAgent(
366
+ project_id=os.getenv('REDDIT_CLOUD_PROJECT'),
367
+ credentials_path='/tmp/reddit_credentials.json'
368
+ )
369
+ print("Debug: Reddit agent initialized")
370
+
371
+ agents_dict = {
372
+ 'summary_model': summary_model,
373
+ 'github_agent': {'agent': github_agent, 'model': summary_model,'config': CONFIG.get('github_settings', {})},
374
+ 'arxiv_agent': {'agent': arxiv_agent, 'model': summary_model},
375
+ 'producthunt_agent': {
376
+ 'agent': producthunt_agent,
377
+ 'model': summary_model,
378
+ 'config': CONFIG.get('producthunt_settings', {}) # Pass ProductHunt specific config
379
+ },
380
+ 'reddit_agent': {'agent': reddit_agent, 'model': summary_model}
381
+ }
382
+ print("Debug: Agents initialized successfully.")
383
+ end_time = datetime.now()
384
+ print(f"Debug: Finished initialize_agents, Time taken: {end_time - start_time}")
385
+ return agents_dict
386
+ except Exception as e:
387
+ logger.error(f"Error initializing agents: {str(e)}")
388
+ print(f"Debug: Error initializing agents: {str(e)}")
389
+ logger.error(traceback.format_exc())
390
+ print(f"Debug: Traceback: {traceback.format_exc()}")
391
+ return None
392
+
393
+
394
+ async def fetch_with_timeout(coro: Any, timeout: int = 600) -> Any:
395
+ """Wrapper for async operations with timeout."""
396
+ start_time = datetime.now()
397
+ print(f"Debug: Starting fetch_with_timeout with timeout: {timeout}")
398
+ try:
399
+ result = await asyncio.wait_for(coro, timeout=timeout)
400
+ print("Debug: fetch_with_timeout completed successfully")
401
+ end_time = datetime.now()
402
+ print(f"Debug: Finished fetch_with_timeout, Time taken: {end_time - start_time}")
403
+ return result
404
+ except asyncio.TimeoutError:
405
+ logger.error(f"Task timed out: {coro}")
406
+ print(f"Debug: Task timed out: {coro}")
407
+ return "Task timed out"
408
+
409
+
410
+ async def process_prompt(prompt: str, agents: Dict[str, Any],
411
+ progress: Optional[gr.Progress] = None) -> Tuple[str, str, str, str, str]:
412
+ try:
413
+ print(f"\n=== APP.PY PROCESSING START ===")
414
+ print(f"Original prompt received: {prompt}")
415
+
416
+ if progress is not None:
417
+ progress(0, "Starting preprocessing")
418
+
419
+ # Initialize task tracking
420
+ tasks_completed = 0 # Initialize here
421
+ total_tasks = 4 # Total number of major tasks
422
+
423
+ preprocessing_prompt = f"""
424
+ Transform this business/app idea query into structured market research format.
425
+
426
+ Original query: {prompt}
427
+
428
+ Transform using these rules:
429
+ 1. Begin with "Market analysis request:"
430
+ 2. Include "Target sector:"
431
+ 3. Specify "Primary features:"
432
+ 4. Add "Business model category:"
433
+ 5. End with "Technical requirements:"
434
+
435
+ Format as a single paragraph without the rule headers. Use professional business language.
436
+ """
437
+ print(f"Generated preprocessing prompt: {preprocessing_prompt}")
438
+
439
+ # Initialize preprocessor
440
+ preprocessor = QueryPreprocessor()
441
+
442
+ try:
443
+ # First pass - structure the query
444
+ response = await fetch_with_timeout(
445
+ asyncio.to_thread(
446
+ lambda: agents['summary_model'].generate_content(preprocessing_prompt)
447
+ )
448
+ )
449
+ structured_prompt = response.text.strip()
450
+ print(f"After first pass structuring: {structured_prompt}")
451
+
452
+ # Second pass - format for API efficiency
453
+ format_prompt = f"""
454
+ Convert this market analysis into a concise, direct query suitable for API processing.
455
+ Keep all key details but remove unnecessary words.
456
+ Query: {structured_prompt}
457
+ """
458
+
459
+ format_response = await fetch_with_timeout(
460
+ asyncio.to_thread(
461
+ lambda: agents['summary_model'].generate_content(format_prompt)
462
+ )
463
+ )
464
+ processed_prompt = format_response.text.strip()
465
+ print(f"After second pass formatting: {processed_prompt}")
466
+
467
+ # Check semantic similarity with core concept extraction
468
+ is_similar = await preprocessor.check_semantic_similarity(prompt, processed_prompt)
469
+ print(f"Semantic similarity check result: {is_similar}")
470
+
471
+ if not processed_prompt or not is_similar:
472
+ logger.warning("Query preprocessing validation failed, using original query")
473
+ logger.info(f"Original: {prompt}")
474
+ logger.info(f"Processed: {processed_prompt}")
475
+ processed_prompt = prompt
476
+
477
+ except Exception as e:
478
+ logger.error(f"Query preprocessing failed: {str(e)}")
479
+ processed_prompt = prompt
480
+
481
+ logger.info(f"Original prompt: {prompt}")
482
+ logger.info(f"Processed prompt: {processed_prompt}")
483
+
484
+ # Sequential execution with individual error handling
485
+ if progress is not None:
486
+ progress(0.2, "Processing ProductHunt data")
487
+
488
+ try:
489
+ print("\n=== CALLING PRODUCTHUNT AGENT ===")
490
+ producthunt_result = await fetch_with_timeout(
491
+ agents['producthunt_agent']['agent'].process_search(
492
+ agents['producthunt_agent']['model'],
493
+ processed_prompt
494
+ ),
495
+ timeout=CONFIG['timeouts']['analysis']
496
+ )
497
+ print(f"ProductHunt result received: {'Empty' if not producthunt_result else 'Has content'}")
498
+ producthunt_result = str(producthunt_result)
499
+ tasks_completed += 1
500
+ await asyncio.sleep(5)
501
+ gc.collect()
502
+ except Exception as e:
503
+ producthunt_result = f"ProductHunt Error: {str(e)}"
504
+ logger.error(f"ProductHunt search failed: {str(e)}")
505
+ print(f"ProductHunt search failed: {str(e)}")
506
+
507
+ if progress is not None:
508
+ progress(0.4, "Processing GitHub data")
509
+ await asyncio.sleep(20)
510
+ try:
511
+ print("\n=== CALLING GITHUB AGENT ===")
512
+ github_result = await fetch_with_timeout(
513
+ agents['github_agent']['agent'].search_and_analyze(
514
+ processed_prompt,
515
+ CONFIG['github_settings']['search']['final_analysis_count'],
516
+ agents['github_agent']['model']
517
+ )
518
+ )
519
+ print(f"GitHub result received: {'Empty' if not github_result else 'Has content'}")
520
+ github_result = str(github_result)
521
+ tasks_completed += 1
522
+ except Exception as e:
523
+ github_result = f"GitHub Error: {str(e)}"
524
+ logger.error(f"GitHub search failed: {str(e)}")
525
+ print(f"GitHub search failed: {str(e)}")
526
+
527
+ if progress is not None:
528
+ progress(0.6, "Processing Arxiv data")
529
+
530
+ try:
531
+ arxiv_result = await fetch_with_timeout(
532
+ agents['arxiv_agent']['agent'].search_and_analyze(
533
+ processed_prompt, 5, agents['arxiv_agent']['model']
534
+ )
535
+ )
536
+ arxiv_result = str(arxiv_result)
537
+ tasks_completed += 1
538
+ except Exception as e:
539
+ arxiv_result = f"Arxiv Error: {str(e)}"
540
+ logger.error(f"Arxiv search failed: {str(e)}")
541
+ print(f"Arxiv search failed: {str(e)}")
542
+
543
+ if progress is not None:
544
+ progress(0.8, "Processing Reddit data")
545
+
546
+ try:
547
+ reddit_result = await fetch_with_timeout(
548
+ agents['reddit_agent']['agent'].search_and_analyze(
549
+ processed_prompt,
550
+ agents['reddit_agent']['model'],
551
+ num_posts=5
552
+ ),
553
+ timeout=CONFIG['timeouts']['analysis']
554
+ )
555
+ reddit_result = str(reddit_result)
556
+ tasks_completed += 1
557
+ except Exception as e:
558
+ reddit_result = f"Reddit Error: {str(e)}"
559
+ logger.error(f"Reddit search failed: {str(e)}")
560
+
561
+ if progress is not None:
562
+ progress(0.9, "Generating summary")
563
+
564
+ template_env = jinja2.Environment(
565
+ loader=jinja2.FileSystemLoader('templates')
566
+ )
567
+ template = template_env.get_template('summary_template.txt')
568
+
569
+ prompt_text = template.render(
570
+ prompt=processed_prompt,
571
+ github_data=github_result,
572
+ arxiv_data=arxiv_result,
573
+ producthunt_data=producthunt_result,
574
+ reddit_data=reddit_result
575
+ )
576
+
577
+ API_LIMIT_MESSAGE = """
578
+ IdeaLens has reached its API query limits but you may still be able to see results from individual platform sections below. Please try again in a few minutes.
579
+
580
+ This temporary pause helps us maintain service quality and ensure fair access for all users.
581
+ Thank you for your patience!
582
+ """
583
+
584
+ try:
585
+ max_retries = 3
586
+ retry_delay = 5
587
+ attempt = 0
588
+
589
+ while attempt < max_retries:
590
+ try:
591
+ loop = asyncio.get_event_loop()
592
+ response = await asyncio.wait_for(
593
+ loop.run_in_executor(
594
+ None,
595
+ lambda: agents['summary_model'].generate_content(prompt_text)
596
+ ),
597
+ timeout=CONFIG['timeouts']['analysis']
598
+ )
599
+ executive_summary = response.text
600
+ break
601
+
602
+ except asyncio.TimeoutError:
603
+ print("Summary generation timeout")
604
+ if attempt < max_retries - 1:
605
+ print(f"Retry attempt {attempt + 1}/{max_retries}")
606
+ await asyncio.sleep(retry_delay)
607
+ retry_delay *= 2
608
+ attempt += 1
609
+ else:
610
+ raise Exception("Summary generation timeout")
611
+
612
+ except Exception as e:
613
+ if "429" in str(e) or attempt < max_retries - 1:
614
+ print(f"Rate limit or error, attempt {attempt + 1}/{max_retries}. Waiting {retry_delay} seconds.")
615
+ await asyncio.sleep(retry_delay)
616
+ retry_delay *= 2
617
+ attempt += 1
618
+ else:
619
+ raise
620
+
621
+ else:
622
+ raise Exception("Max retries exceeded for summary generation")
623
+
624
+ except Exception as e:
625
+ error_type = str(e)
626
+ print(f"Summary generation failed: {error_type}")
627
+ logger.error(f"Summary generation failed: {error_type}")
628
+ executive_summary = API_LIMIT_MESSAGE
629
+
630
+ if progress is not None:
631
+ progress(1, "Completed")
632
+
633
+ return (executive_summary, arxiv_result, github_result, producthunt_result, reddit_result)
634
+
635
+ except Exception as e:
636
+ print(f"Process prompt error: {str(e)}")
637
+ logger.error(f"Process prompt error: {str(e)}")
638
+ API_LIMIT_MESSAGE = """
639
+ IdeaLens has reached its API query limits. Please try again in a few minutes.
640
+
641
+ This temporary pause helps us maintain service quality and ensure fair access for all users.
642
+ Thank you for your patience!
643
+ """
644
+ return (API_LIMIT_MESSAGE, "", "", "", "")
645
+ finally:
646
+ gc.collect()
647
+
648
+ def start_loading(prompt):
649
+ """Show loader when search starts"""
650
+ print("Start loading")
651
+ if not prompt.strip():
652
+ return gr.update(visible=False)
653
+ return gr.update(visible=True)
654
+
655
+ def create_interface(agents: Dict[str, Any]) -> gr.Blocks:
656
+ """Create and configure the Gradio interface."""
657
+ start_time = datetime.now()
658
+ print("Debug: Starting create_interface")
659
+ with gr.Blocks(css=custom_css) as interface:
660
+ gr.HTML(intro_text)
661
+ with gr.Row():
662
+ input_text = gr.Textbox(
663
+ lines=2,
664
+ placeholder="Enter your search prompt here",
665
+ label="Search Prompt",
666
+ elem_classes="center-label"
667
+ )
668
+ print("Debug: Input textbox created")
669
+
670
+ # Create the sample prompt buttons
671
+ with gr.Row(equal_height=True):
672
+ sample_prompts = [
673
+ "A crypto-backed decentralized marketplace for digital assets, enabling trustless peer-to-peer trading and licensing",
674
+ "A music app that adjusts soundscapes based on relaxation or focus detected via EEG",
675
+ "An AI app that turns real-world objects into interactive holographic tutorials using augmented reality and real-time object recognition",
676
+ ]
677
+ for prompt in sample_prompts:
678
+ gr.Button(prompt, elem_classes="equal-button").click(
679
+ lambda p=prompt: p,
680
+ outputs=input_text
681
+ )
682
+
683
+ with gr.Row():
684
+ start_button = gr.Button("Start Search", variant="primary")
685
+ print("Debug: Start button created")
686
+
687
+ with gr.Row():
688
+ error_box = gr.Textbox(
689
+ label="Status/Error Messages",
690
+ visible=False
691
+ )
692
+ print("Debug: Error box created")
693
+
694
+ loader = gr.Textbox(
695
+ value="Processing your request... Please wait...",
696
+ visible=False,
697
+ label="Status",
698
+ elem_classes="loading-text"
699
+ )
700
+
701
+ with gr.Row():
702
+ executive_summary_output = gr.Markdown(
703
+ label="Executive Summary",
704
+ show_copy_button=True
705
+ )
706
+ print("Debug: Executive summary textbox created")
707
+
708
+ with gr.Column(visible=False) as output_column:
709
+ outputs = {}
710
+ buttons = {}
711
+ for source in ['reddit', 'producthunt', 'github', 'arxiv']:
712
+ outputs[source] = gr.Markdown(
713
+ label=f"{source.title()} Results",
714
+ visible=False,
715
+ show_copy_button=True
716
+ )
717
+ if source == "reddit":
718
+ button_label = "Show User Perspectives (Reddit)"
719
+ elif source == "producthunt":
720
+ button_label = "Show Similar Products (Producthunt)"
721
+ elif source == "arxiv":
722
+ button_label = "Show Related Research (Arxiv)"
723
+ elif source == "github":
724
+ button_label = "Explore Related Code (Github)"
725
+ else:
726
+ button_label = f"Show Full {source.title()} Results"
727
+ buttons[source] = gr.Button(button_label)
728
+
729
+ # Configure button handlers
730
+ async def handle_search(prompt: str) -> Tuple[str, str, str, str, str, gr.Textbox, gr.Column]:
731
+ start_time = datetime.now()
732
+ print(f"Debug: Starting handle_search with prompt: {prompt}")
733
+
734
+ API_LIMIT_MESSAGE = """
735
+ IdeaLens has reached its API query limits. Please try again in a few minutes.
736
+
737
+ This temporary pause helps us maintain service quality and ensure fair access for all users.
738
+ Thank you for your patience!
739
+ """
740
+
741
+ try:
742
+ if not prompt.strip():
743
+ print("Debug: Prompt is empty")
744
+ return "Please enter a search query", "", "", "", "", gr.update(visible=False), gr.update(visible=True)
745
+
746
+ result = await process_prompt(prompt, agents, gr.Progress())
747
+ print("Debug: handle_search completed")
748
+
749
+ # Check if any of the results contain error messages
750
+ if any(isinstance(r, str) and "Error:" in r for r in result[1:]):
751
+ raise Exception("One or more data sources failed")
752
+
753
+ end_time = datetime.now()
754
+ print(f"Debug: Finished handle_search, Time taken: {end_time - start_time}")
755
+ return result[0], result[1], result[2], result[3], result[4], gr.update(visible=False), gr.update(visible=True)
756
+
757
+ except Exception as e:
758
+ print(f"Handle search error: {str(e)}")
759
+ logger.error(f"Handle search error: {str(e)}")
760
+ # Return API limit message for all error scenarios
761
+ return (
762
+ API_LIMIT_MESSAGE, # executive summary
763
+ "", # arxiv
764
+ "", # github
765
+ "", # producthunt
766
+ "", # reddit
767
+ gr.update(visible=False), # loader
768
+ gr.update(visible=True) # output column
769
+ )
770
+
771
+ # Wire up the start button handlers
772
+ start_button.click(
773
+ fn=start_loading,
774
+ inputs=[input_text],
775
+ outputs=[loader],
776
+ queue=False # Execute immediately
777
+ ).then(
778
+ fn=handle_search,
779
+ inputs=[input_text],
780
+ outputs=[
781
+ executive_summary_output,
782
+ outputs['arxiv'],
783
+ outputs['github'],
784
+ outputs['producthunt'],
785
+ outputs['reddit'],
786
+ loader,
787
+ output_column
788
+ ],
789
+ api_name="search",
790
+ queue=True # This will run after the loader is shown
791
+ )
792
+
793
+ # Configure view buttons
794
+ for source, button in buttons.items():
795
+ button.click(
796
+ lambda: gr.update(visible=True),
797
+ None,
798
+ outputs[source]
799
+ )
800
+ print(f"Debug: {source} button click handler configured")
801
+
802
+ return interface
803
+
804
+ def main() -> None:
805
+ """Main application entry point."""
806
+ start_time = datetime.now()
807
+ print("Debug: Starting main function")
808
+ try:
809
+ # Configure environment
810
+ print("Debug: Configuring environment...")
811
+ configure_environment()
812
+ print("Debug: Environment configured successfully")
813
+
814
+ # Initialize agents
815
+ print("Debug: Initializing agents...")
816
+ agents = initialize_agents()
817
+ if not agents:
818
+ print("Debug: Agent initialization failed")
819
+ raise RuntimeError("Failed to initialize one or more agents")
820
+ print("Debug: Agents initialized successfully")
821
+
822
+ # Create and launch interface
823
+ print("Debug: Creating interface...")
824
+ interface = create_interface(agents)
825
+ print("Debug: Interface created successfully")
826
+ interface.launch(debug=True)
827
+ print("Debug: Gradio interface launched")
828
+
829
+ except Exception as e:
830
+ logger.error(f"Application startup failed: {str(e)}")
831
+ print(f"Debug: Application startup failed: {str(e)}")
832
+ logger.error(traceback.format_exc())
833
+ print(f"Debug: Traceback: {traceback.format_exc()}")
834
+ raise
835
+ finally:
836
+ end_time = datetime.now()
837
+ print(f"Debug: Exiting main function, Time taken: {end_time - start_time}")
838
+
839
+
840
+ if __name__ == "__main__":
841
+ main()
dockerignore ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Version control
2
+ .git
3
+ .gitignore
4
+
5
+ # Python
6
+ __pycache__/
7
+ *.py[cod]
8
+ *$py.class
9
+ *.so
10
+ .Python
11
+ env/
12
+ build/
13
+ develop-eggs/
14
+ dist/
15
+ downloads/
16
+ eggs/
17
+ .eggs/
18
+ lib/
19
+ lib64/
20
+ parts/
21
+ sdist/
22
+ var/
23
+ *.egg-info/
24
+ .installed.cfg
25
+ *.egg
26
+
27
+ # Virtual Environment
28
+ venv/
29
+ ENV/
30
+
31
+ # Logs
32
+ *.log
33
+ logs/
34
+ log/
35
+
36
+ # Local development
37
+ .env
38
+ .env.local
39
+ .env.*.local
40
+
41
+ # IDE
42
+ .idea/
43
+ .vscode/
44
+ *.swp
45
+ *.swo
46
+
47
+ # Test files
48
+ tests/
49
+ test/
50
+ testing/
51
+
52
+ # Documentation
53
+ docs/
54
+ *.md
55
+
56
+ # Temporary files
57
+ *.tmp
58
+ *.temp
59
+ .DS_Store
60
+ Thumbs.db
env.example ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # GitHub Configuration
2
+ GITHUB_TOKEN=your_github_token_here
3
+
4
+ # ProductHunt Configuration
5
+ PRODUCT_HUNT_TOKEN=your_producthunt_token_here
6
+
7
+ # Reddit Configuration
8
+ REDDIT_CLIENT_ID=your_reddit_client_id_here
9
+ REDDIT_CLIENT_SECRET=your_reddit_client_secret_here
10
+ REDDIT_USER_AGENT=your_reddit_user_agent_here
11
+
12
+ # Google Cloud Configuration
13
+ GOOGLE_APPLICATION_CREDENTIALS_JSON=your_google_credentials_json_here
14
+
15
+ # Application Settings
16
+ DEBUG=false
17
+ LOG_LEVEL=INFO
18
+ MAX_BATCH_SIZE=10
19
+ REQUEST_TIMEOUT=30
gitignore ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ env/
8
+ build/
9
+ develop-eggs/
10
+ dist/
11
+ downloads/
12
+ eggs/
13
+ .eggs/
14
+ lib/
15
+ lib64/
16
+ parts/
17
+ sdist/
18
+ var/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual Environment
24
+ venv/
25
+ ENV/
26
+
27
+ # Logs
28
+ *.log
29
+ logs/
30
+ log/
31
+
32
+ # Local development
33
+ .env
34
+ .env.local
35
+ .env.*.local
36
+
37
+ # IDE
38
+ .idea/
39
+ .vscode/
40
+ *.swp
41
+ *.swo
42
+
43
+ # Operating System
44
+ .DS_Store
45
+ Thumbs.db
46
+
47
+ # Private folders
48
+ agents/
logging.conf ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [loggers]
2
+ keys=root,app,githubAgent,arxivAgent,producthuntAgent,redditAgent
3
+
4
+ [handlers]
5
+ keys=consoleHandler,fileHandler,errorFileHandler
6
+
7
+ [formatters]
8
+ keys=defaultFormatter,errorFormatter
9
+
10
+ [logger_root]
11
+ level=INFO
12
+ handlers=consoleHandler,fileHandler
13
+ qualname=root
14
+
15
+ [logger_app]
16
+ level=INFO
17
+ handlers=fileHandler,errorFileHandler
18
+ qualname=app
19
+ propagate=0
20
+
21
+ [logger_githubAgent]
22
+ level=INFO
23
+ handlers=fileHandler,errorFileHandler
24
+ qualname=githubAgent
25
+ propagate=0
26
+
27
+ [logger_arxivAgent]
28
+ level=INFO
29
+ handlers=fileHandler,errorFileHandler
30
+ qualname=arxivAgent
31
+ propagate=0
32
+
33
+ [logger_producthuntAgent]
34
+ level=INFO
35
+ handlers=fileHandler,errorFileHandler
36
+ qualname=producthuntAgent
37
+ propagate=0
38
+
39
+ [logger_redditAgent]
40
+ level=INFO
41
+ handlers=fileHandler,errorFileHandler
42
+ qualname=redditAgent
43
+ propagate=0
44
+
45
+ [handler_consoleHandler]
46
+ class=StreamHandler
47
+ level=INFO
48
+ formatter=defaultFormatter
49
+ args=(sys.stdout,)
50
+
51
+ [handler_fileHandler]
52
+ class=handlers.TimedRotatingFileHandler
53
+ level=INFO
54
+ formatter=defaultFormatter
55
+ args=('logs/app.log', 'midnight', 1, 30)
56
+
57
+ [handler_errorFileHandler]
58
+ class=handlers.TimedRotatingFileHandler
59
+ level=ERROR
60
+ formatter=errorFormatter
61
+ args=('logs/error.log', 'midnight', 1, 30)
62
+
63
+ [formatter_defaultFormatter]
64
+ format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
65
+ datefmt=%Y-%m-%d %H:%M:%S
66
+
67
+ [formatter_errorFormatter]
68
+ format=%(asctime)s - %(name)s - %(levelname)s - %(message)s - %(pathname)s:%(lineno)d
69
+ datefmt=%Y-%m-%d %H:%M:%S
packages.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ python3-dev
2
+ build-essential
3
+ git
4
+ wget
5
+ curl
6
+ software-properties-common
7
+ python3-pip
pre-commit-config.yaml ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v4.5.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ - id: check-yaml
8
+ - id: check-json
9
+ - id: debug-statements
10
+ - id: detect-private-key
11
+ - id: check-added-large-files
12
+
13
+ - repo: https://github.com/psf/black
14
+ rev: 23.11.0
15
+ hooks:
16
+ - id: black
17
+ language_version: python3.10
18
+
19
+ - repo: https://github.com/pycqa/flake8
20
+ rev: 6.1.0
21
+ hooks:
22
+ - id: flake8
23
+ additional_dependencies: [flake8-docstrings]
pyproject.toml ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [build-system]
2
+ requires = ["setuptools>=45", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "multi-source-research-assistant"
7
+ version = "1.0.0"
8
+ description = "A comprehensive research tool combining multiple data sources"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = {text = "MIT"}
12
+ authors = [
13
+ {name = "Your Name", email = "your.email@example.com"}
14
+ ]
15
+
16
+ [tool.black]
17
+ line-length = 88
18
+ target-version = ['py310']
19
+ include = '\.pyi?$'
20
+
21
+ [tool.flake8]
22
+ max-line-length = 88
23
+ extend-ignore = "E203"
24
+ exclude = [
25
+ ".git",
26
+ "__pycache__",
27
+ "build",
28
+ "dist"
29
+ ]
30
+
31
+ [tool.pytest]
32
+ testpaths = "tests"
33
+ python_files = "test_*.py"
34
+ addopts = "-v -s --cov=agents"
requirements.txt ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ gradio
2
+ praw
3
+ google-generativeai
4
+ vertexai
5
+ google-cloud-aiplatform
6
+ sentence-transformers
7
+ beautifulsoup4
8
+ pandas
9
+ datasets
10
+ google-auth
11
+ spacy
12
+ httpx
13
+ jinja2
14
+ tenacity
15
+ en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.7.0/en_core_web_sm-3.7.0.tar.gz
16
+ tqdm
17
+ numpy
18
+ scikit-learn
19
+ feedparser
20
+ requests
21
+ google.cloud.core
22
+ python-dotenv
23
+ torch
24
+ psutil
25
+ nltk
26
+ aiohttp