jzou19950715 commited on
Commit
ed879c6
·
verified ·
1 Parent(s): 7c80102

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +500 -250
app.py CHANGED
@@ -1,293 +1,543 @@
1
- import base64
2
- import io
3
- import os
4
- from dataclasses import dataclass
5
- from typing import Any, Callable, Dict, List, Optional
6
 
 
7
  import gradio as gr
8
- import matplotlib.pyplot as plt
9
  import numpy as np
10
- import pandas as pd
11
- import seaborn as sns
12
- from litellm import completion
 
 
 
 
13
 
 
 
 
14
 
15
- # Code Execution Environment
16
- class CodeEnvironment:
17
- """Safe environment for executing code with data analysis capabilities"""
18
-
 
 
 
 
 
 
 
 
19
  def __init__(self):
20
- self.globals = {
21
- 'pd': pd,
22
- 'np': np,
23
- 'plt': plt,
24
- 'sns': sns,
 
 
 
25
  }
26
- self.locals = {}
27
 
28
- def execute(self, code: str, df: pd.DataFrame = None) -> Dict[str, Any]:
29
- """Execute code and capture outputs"""
30
- if df is not None:
31
- self.globals['df'] = df
32
-
33
- # Capture output
34
- output_buffer = io.StringIO()
35
- result = {'output': '', 'figures': [], 'error': None}
36
-
37
- try:
38
- # Execute code
39
- exec(code, self.globals, self.locals)
40
-
41
- # Capture figures
42
- for i in plt.get_fignums():
43
- fig = plt.figure(i)
44
- buf = io.BytesIO()
45
- fig.savefig(buf, format='png')
46
- buf.seek(0)
47
- img_str = base64.b64encode(buf.read()).decode()
48
- result['figures'].append(f"data:image/png;base64,{img_str}")
49
- plt.close(fig)
50
-
51
- # Get printed output
52
- result['output'] = output_buffer.getvalue()
53
-
54
- except Exception as e:
55
- result['error'] = str(e)
56
-
57
- finally:
58
- output_buffer.close()
59
-
60
- return result
61
 
62
- @dataclass
63
- class Tool:
64
- """Tool for data analysis"""
65
- name: str
66
- description: str
67
- func: Callable
68
 
69
- class AnalysisAgent:
70
- """Agent that can analyze data and execute code"""
71
-
72
- def __init__(
73
- self,
74
- model_id: str = "gpt-4o-mini",
75
- temperature: float = 0.7,
76
- ):
77
- self.model_id = model_id
78
- self.temperature = temperature
79
- self.tools: List[Tool] = []
80
- self.code_env = CodeEnvironment()
81
 
82
- def add_tool(self, name: str, description: str, func: Callable) -> None:
83
- """Add a tool to the agent"""
84
- self.tools.append(Tool(name=name, description=description, func=func))
 
 
 
 
 
 
85
 
86
- def run(self, prompt: str, df: pd.DataFrame = None) -> str:
87
- """Run analysis with code execution"""
88
- messages = [
89
- {"role": "system", "content": self._get_system_prompt()},
90
- {"role": "user", "content": prompt}
91
- ]
92
 
93
- try:
94
- # Get response from model
95
- response = completion(
96
- model=self.model_id,
97
- messages=messages,
98
- temperature=self.temperature,
99
  )
100
- analysis = response.choices[0].message.content
101
-
102
- # Extract code blocks
103
- code_blocks = self._extract_code(analysis)
104
-
105
- # Execute code and capture results
106
- results = []
107
- for code in code_blocks:
108
- result = self.code_env.execute(code, df)
109
- if result['error']:
110
- results.append(f"Error executing code: {result['error']}")
111
- else:
112
- # Add output and figures
113
- if result['output']:
114
- results.append(result['output'])
115
- for fig in result['figures']:
116
- results.append(f"![Figure]({fig})")
117
-
118
- # Combine analysis and results
119
- return analysis + "\n\n" + "\n".join(results)
120
-
121
- except Exception as e:
122
- return f"Error: {str(e)}"
123
-
124
- def _get_system_prompt(self) -> str:
125
- """Get system prompt with tools and capabilities"""
126
- tools_desc = "\n".join([
127
- f"- {tool.name}: {tool.description}"
128
- for tool in self.tools
129
- ])
130
 
131
- return f"""You are a data analysis assistant.
 
 
 
 
 
132
 
133
- Available tools:
134
- {tools_desc}
135
-
136
- Capabilities:
137
- - Data analysis (pandas, numpy)
138
- - Visualization (matplotlib, seaborn)
139
- - Statistical analysis (scipy)
140
- - Machine learning (sklearn)
141
-
142
- When writing code:
143
- - Use markdown code blocks
144
- - Create clear visualizations
145
- - Include explanations
146
- - Handle errors gracefully
147
- """
148
-
149
- @staticmethod
150
- def _extract_code(text: str) -> List[str]:
151
- """Extract Python code blocks from markdown"""
152
- import re
153
- pattern = r'```python\n(.*?)```'
154
- return re.findall(pattern, text, re.DOTALL)
155
-
156
- def process_file(file: gr.File) -> Optional[pd.DataFrame]:
157
- """Process uploaded file into DataFrame"""
158
- if not file:
159
- return None
160
 
161
- try:
162
- if file.name.endswith('.csv'):
163
- return pd.read_csv(file.name)
164
- elif file.name.endswith(('.xlsx', '.xls')):
165
- return pd.read_excel(file.name)
166
- except Exception as e:
167
- print(f"Error reading file: {str(e)}")
168
- return None
169
-
170
- def analyze_data(
171
- file: gr.File,
172
- query: str,
173
- api_key: str,
174
- temperature: float = 0.7,
175
- ) -> str:
176
- """Process user request and generate analysis"""
177
-
178
- if not api_key:
179
- return "Error: Please provide an API key."
180
 
181
- if not file:
182
- return "Error: Please upload a file."
 
183
 
184
- try:
185
- # Set up environment
186
- os.environ["OPENAI_API_KEY"] = api_key
187
 
188
- # Create agent
189
- agent = AnalysisAgent(
190
- model_id="gpt-4o-mini",
191
- temperature=temperature
192
- )
193
 
194
- # Process file
195
- df = process_file(file)
196
- if df is None:
197
- return "Error: Could not process file."
198
-
199
- # Build context
200
- file_info = f"""
201
- File: {file.name}
202
- Shape: {df.shape}
203
- Columns: {', '.join(df.columns)}
204
 
205
- Column Types:
206
- {chr(10).join([f'- {col}: {dtype}' for col, dtype in df.dtypes.items()])}
207
- """
208
 
209
- # Run analysis
210
- prompt = f"""
211
- {file_info}
 
 
 
212
 
213
- The data is loaded in a pandas DataFrame called 'df'.
 
214
 
215
- User request: {query}
 
 
216
 
217
- Please analyze the data and provide:
218
-
219
- 1. Key insights and findings
220
- 2. Whenever the user request is unclear, proactively interpret them such that it becomes analyzable.
221
- """
 
222
 
223
- return agent.run(prompt, df=df)
 
224
 
225
- except Exception as e:
226
- return f"Error occurred: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
 
228
  def create_interface():
229
- """Create Gradio interface"""
230
-
231
- with gr.Blocks(title="AI Data Analysis Assistant") as interface:
 
 
 
232
  gr.Markdown("""
233
- # AI Data Analysis Assistant
234
-
235
- Upload your data file and get AI-powered analysis with visualizations.
236
-
237
- **Features:**
238
- - Data analysis and visualization
239
- - Statistical analysis
240
- - Machine learning capabilities
241
 
242
- **Note**: Requires your own OpenAi API key.
 
243
  """)
244
-
 
 
 
 
 
 
 
 
 
245
  with gr.Row():
246
- with gr.Column():
247
- file = gr.File(
248
- label="Upload Data File",
249
- file_types=[".csv", ".xlsx", ".xls"]
 
 
 
250
  )
251
- query = gr.Textbox(
252
- label="What would you like to analyze?",
253
- placeholder="e.g., Create visualizations showing relationships between variables",
254
- lines=3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  )
256
- api_key = gr.Textbox(
257
- label="API Key (Required)",
258
- placeholder="Your API key",
259
- type="password"
260
  )
261
- temperature = gr.Slider(
262
- label="Temperature",
263
- minimum=0.0,
264
- maximum=1.0,
265
- value=0.7,
266
- step=0.1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267
  )
268
- analyze_btn = gr.Button("Analyze")
269
-
270
- with gr.Column():
271
- output = gr.Markdown(label="Output")
272
-
273
- analyze_btn.click(
274
- analyze_data,
275
- inputs=[file, query, api_key, temperature],
276
- outputs=output
 
277
  )
278
-
279
- gr.Examples(
280
- examples=[
281
- [None, "Show the distribution of values and key statistics"],
282
- [None, "Create a correlation analysis with heatmap"],
283
- [None, "Identify and visualize any outliers in the data"],
284
- [None, "Generate summary plots for the main variables"],
285
- ],
286
- inputs=[file, query]
287
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
 
289
- return interface
 
 
 
 
 
290
 
291
  if __name__ == "__main__":
292
- interface = create_interface()
293
- interface.launch()
 
1
+ # ==============================================
2
+ # Monte Carlo Salary Prediction Application
3
+ # ==============================================
 
 
4
 
5
+ # Required imports
6
  import gradio as gr
 
7
  import numpy as np
8
+ import matplotlib.pyplot as plt
9
+ import base64
10
+ import io
11
+ import json
12
+ import requests
13
+ from typing import Dict, List, Tuple, Any
14
+ import logging
15
 
16
+ # Configure logging
17
+ logging.basicConfig(level=logging.INFO)
18
+ logger = logging.getLogger(__name__)
19
 
20
+ # ==============================================
21
+ # System Prompts (Unchanged)
22
+ # ==============================================
23
+
24
+ CONVERSATION_PROMPT = """...""" # (Keep your existing prompt)
25
+ EXTRACTION_PROMPT = """...""" # (Keep your existing prompt)
26
+
27
+ # ==============================================
28
+ # Monte Carlo Simulation Class (Unchanged)
29
+ # ==============================================
30
+
31
+ class SalarySimulator:
32
  def __init__(self):
33
+ """Initialize growth and premium calculators."""
34
+ # Growth factors
35
+ self.growth_factors = {
36
+ "base_growth": lambda score: (0.02 + (score * 0.03), 0.04 + (score * 0.04)),
37
+ "skill_premium": lambda score: (0.01 + (score * 0.02), 0.02 + (score * 0.03)),
38
+ "experience_premium": lambda score: (0.01 + (score * 0.02), 0.02 + (score * 0.03)),
39
+ "education_premium": lambda score: (0.005 + (score * 0.015), 0.01 + (score * 0.02)),
40
+ "location_premium": lambda score: (0.0 + (score * 0.02), 0.01 + (score * 0.03))
41
  }
 
42
 
43
+ # Risk factors
44
+ self.risk_factors = {
45
+ "volatility": lambda score: (0.02 + (score * 0.02), 0.03 + (score * 0.03)),
46
+ "disruption": lambda score: (0.05 + (score * 0.15), 0.1 + (score * 0.2))
47
+ }
48
+
49
+ def validate_scores(self, scores: Dict[str, float]) -> None:
50
+ """Validate all required scores are present and valid."""
51
+ required = [
52
+ "industry_score", "experience_score", "education_score",
53
+ "skills_score", "location_score", "current_salary"
54
+ ]
55
+ for key in required:
56
+ if key not in scores:
57
+ raise ValueError(f"Missing required score: {key}")
58
+ if key == "current_salary":
59
+ if not isinstance(scores[key], (int, float)) or scores[key] <= 0:
60
+ raise ValueError("Invalid salary value")
61
+ else:
62
+ if not 0 <= scores[key] <= 1:
63
+ raise ValueError(f"Invalid {key}: must be between 0 and 1")
 
 
 
 
 
 
 
 
 
 
 
 
64
 
65
+ def calculate_factor(self, name: str, score: float, factor_type: str) -> float:
66
+ """Calculate growth or risk factor."""
67
+ factors = self.growth_factors if factor_type == "growth" else self.risk_factors
68
+ min_val, max_val = factors[name](score)
69
+ return np.random.uniform(min_val, max_val)
 
70
 
71
+ def run_simulation(self, scores: Dict[str, float]) -> Tuple[np.ndarray, Dict[str, float]]:
72
+ """Run Monte Carlo simulation."""
73
+ self.validate_scores(scores)
 
 
 
 
 
 
 
 
 
74
 
75
+ # Calculate factors
76
+ factors = {}
77
+ score_mapping = {
78
+ "base_growth": "industry_score",
79
+ "skill_premium": "skills_score",
80
+ "experience_premium": "experience_score",
81
+ "education_premium": "education_score",
82
+ "location_premium": "location_score"
83
+ }
84
 
85
+ # Calculate growth factors
86
+ for factor_name, score_key in score_mapping.items():
87
+ factors[factor_name] = self.calculate_factor(factor_name, scores[score_key], "growth")
 
 
 
88
 
89
+ # Calculate risk factors using industry score
90
+ for factor_name in ["volatility", "disruption"]:
91
+ factors[factor_name] = self.calculate_factor(
92
+ factor_name, scores["industry_score"], "risk"
 
 
93
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
 
95
+ # Run simulation
96
+ years = 5
97
+ num_paths = 10000
98
+ paths = np.zeros((num_paths, years + 1))
99
+ initial_salary = float(scores["current_salary"])
100
+ paths[:, 0] = initial_salary
101
 
102
+ for path in range(num_paths):
103
+ salary = initial_salary
104
+ for year in range(1, years + 1):
105
+ # Calculate base growth
106
+ growth = sum(factors[f] for f in score_mapping.keys())
107
+
108
+ # Add market volatility
109
+ growth += np.random.normal(0, factors["volatility"])
110
+
111
+ # Add potential disruption
112
+ if np.random.random() < 0.1: # 10% chance each year
113
+ disruption = factors["disruption"] * np.random.random()
114
+ if np.random.random() < 0.7: # 70% positive disruption
115
+ growth += disruption
116
+ else:
117
+ growth -= disruption
118
+
119
+ # Apply growth bounds
120
+ growth = min(max(growth, -0.1), 0.25) # -10% to +25%
121
+
122
+ # Update salary
123
+ salary *= (1 + growth)
124
+ paths[path, year] = salary
 
 
 
 
125
 
126
+ return paths, factors
127
+
128
+ def create_plots(self, paths: np.ndarray) -> str:
129
+ """Create visualization using matplotlib and return as base64 string."""
130
+ plt.style.use('dark_background')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
 
132
+ # Create figure
133
+ fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 12), height_ratios=[2, 1])
134
+ fig.tight_layout(pad=4)
135
 
136
+ # Plot 1: Salary Projection
137
+ years = list(range(paths.shape[1]))
 
138
 
139
+ # Add confidence intervals
140
+ percentiles = [(5, 95), (10, 90), (25, 75)]
141
+ alphas = [0.1, 0.2, 0.3]
 
 
142
 
143
+ for (lower, upper), alpha in zip(percentiles, alphas):
144
+ lower_bound = np.percentile(paths, lower, axis=0)
145
+ upper_bound = np.percentile(paths, upper, axis=0)
146
+ ax1.fill_between(years, lower_bound, upper_bound, alpha=alpha, color='blue')
 
 
 
 
 
 
147
 
148
+ # Add median line
149
+ median = np.percentile(paths, 50, axis=0)
150
+ ax1.plot(years, median, color='white', linewidth=2, label='Expected Path')
151
 
152
+ # Customize first plot
153
+ ax1.set_title('Salary Projection', pad=20)
154
+ ax1.set_xlabel('Years')
155
+ ax1.set_ylabel('Salary ($)')
156
+ ax1.grid(True, alpha=0.2)
157
+ ax1.legend()
158
 
159
+ # Format y-axis as currency
160
+ ax1.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, p: f'${x:,.0f}'))
161
 
162
+ # Customize x-axis
163
+ ax1.set_xticks(years)
164
+ ax1.set_xticklabels(['Current'] + [f'Year {i+1}' for i in range(len(years)-1)])
165
 
166
+ # Plot 2: Distribution
167
+ ax2.hist(paths[:, -1], bins=50, color='blue', alpha=0.7)
168
+ ax2.set_title('Final Salary Distribution', pad=20)
169
+ ax2.set_xlabel('Salary ($)')
170
+ ax2.set_ylabel('Count')
171
+ ax2.grid(True, alpha=0.2)
172
 
173
+ # Format x-axis as currency
174
+ ax2.xaxis.set_major_formatter(plt.FuncFormatter(lambda x, p: f'${x:,.0f}'))
175
 
176
+ # Convert to base64
177
+ buf = io.BytesIO()
178
+ plt.savefig(buf, format='png', dpi=100, bbox_inches='tight')
179
+ buf.seek(0)
180
+ img_str = base64.b64encode(buf.read()).decode()
181
+ plt.close()
182
+
183
+ return img_str # Return raw base64 string
184
+
185
+ def generate_report(
186
+ self,
187
+ scores: Dict[str, float],
188
+ paths: np.ndarray,
189
+ factors: Dict[str, float]
190
+ ) -> str:
191
+ """Generate analysis report."""
192
+ final_salaries = paths[:, -1]
193
+ initial_salary = paths[0, 0]
194
+
195
+ metrics = {
196
+ "p25": np.percentile(final_salaries, 25),
197
+ "p50": np.percentile(final_salaries, 50),
198
+ "p75": np.percentile(final_salaries, 75),
199
+ "cagr": (np.median(final_salaries) / initial_salary) ** (1/5) - 1
200
+ }
201
+
202
+ report = f"""
203
+ Monte Carlo Salary Projection Analysis
204
+ ====================================
205
+
206
+ Profile Scores (0-1 scale):
207
+ --------------------------
208
+ • Industry Score: {scores['industry_score']:.2f}
209
+ • Experience Score: {scores['experience_score']:.2f}
210
+ • Education Score: {scores['education_score']:.2f}
211
+ • Skills Score: {scores['skills_score']:.2f}
212
+ • Location Score: {scores['location_score']:.2f}
213
+ • Current Salary: ${scores['current_salary']:,.2f}
214
+
215
+ Growth Factors (Annual):
216
+ -----------------------
217
+ • Base Growth: {factors['base_growth']*100:.1f}%
218
+ • Skill Premium: {factors['skill_premium']*100:.1f}%
219
+ • Experience Premium: {factors['experience_premium']*100:.1f}%
220
+ • Education Premium: {factors['education_premium']*100:.1f}%
221
+ • Location Premium: {factors['location_premium']*100:.1f}%
222
+ • Market Volatility: {factors['volatility']*100:.1f}%
223
+ • Potential Disruption: {factors['disruption']*100:.1f}%
224
+
225
+ 5-Year Projection Results:
226
+ -------------------------
227
+ • Conservative Estimate (25th percentile): ${metrics['p25']:,.2f}
228
+ • Most Likely Outcome (Median): ${metrics['p50']:,.2f}
229
+ • Optimistic Estimate (75th percentile): ${metrics['p75']:,.2f}
230
+ • Expected Annual Growth Rate: {metrics['cagr']*100:.1f}%
231
+
232
+ Analysis Insights:
233
+ -----------------
234
+ • Career profile suggests {metrics['cagr']*100:.1f}% annual growth potential
235
+ • Market volatility could lead to {factors['volatility']*100:.1f}% annual variation
236
+ • Industry position provides {factors['base_growth']*100:.1f}% base growth
237
+ • Personal factors add {(factors['skill_premium'] + factors['experience_premium'] + factors['education_premium'])*100:.1f}% potential premium
238
+ • Location impact contributes {factors['location_premium']*100:.1f}% to growth
239
+
240
+ Key Considerations:
241
+ ------------------
242
+ • Projections based on {paths.shape[0]:,} simulated career paths
243
+ • Accounts for both regular growth and market disruptions
244
+ • Considers personal development and market factors
245
+ • Results show range of potential outcomes
246
+ • Actual results may vary based on economic conditions
247
+ """
248
+ return report
249
+
250
+ # ==============================================
251
+ # Career Advisor Bot (Unchanged)
252
+ # ==============================================
253
+
254
+ class CareerAdvisor:
255
+ def __init__(self):
256
+ """Initialize career advisor."""
257
+ self.chat_history = [] # List of dicts with 'role' and 'content'
258
+ self.simulator = SalarySimulator()
259
+
260
+ def process_message(self, message: str, api_key: str) -> Dict[str, str]:
261
+ """Process user message and generate response."""
262
+ try:
263
+ if not api_key.strip().startswith("sk-"):
264
+ return {"error": "Invalid API key format"}
265
+
266
+ # Prepare conversation history
267
+ messages = [
268
+ {"role": "system", "content": CONVERSATION_PROMPT}
269
+ ]
270
+
271
+ # Add chat history in correct format
272
+ messages.extend(self.chat_history)
273
+
274
+ # Add current message
275
+ messages.append({"role": "user", "content": message})
276
+
277
+ # Call API
278
+ response = requests.post(
279
+ "https://api.openai.com/v1/chat/completions",
280
+ headers={
281
+ "Authorization": f"Bearer {api_key}",
282
+ "Content-Type": "application/json"
283
+ },
284
+ json={
285
+ "model": "gpt-4",
286
+ "messages": messages,
287
+ "temperature": 0.7
288
+ }
289
+ )
290
+
291
+ if response.status_code == 200:
292
+ assistant_message = response.json()["choices"][0]["message"]["content"].strip()
293
+
294
+ # Store messages in correct format
295
+ self.chat_history.append({"role": "user", "content": message})
296
+ self.chat_history.append({"role": "assistant", "content": assistant_message})
297
+
298
+ return {"response": assistant_message}
299
+ else:
300
+ return {"error": f"API error: {response.status_code}"}
301
+
302
+ except Exception as e:
303
+ logger.error(f"Message processing error: {str(e)}")
304
+ return {"error": str(e)}
305
+
306
+ def extract_profile(self, api_key: str) -> Dict[str, float]:
307
+ """Extract numerical profile from conversation."""
308
+ try:
309
+ # Prepare conversation for extraction
310
+ conversation = "\n".join([
311
+ f"{msg['role'].title()}: {msg['content']}"
312
+ for msg in self.chat_history
313
+ ])
314
+
315
+ # Call API for extraction
316
+ response = requests.post(
317
+ "https://api.openai.com/v1/chat/completions",
318
+ headers={
319
+ "Authorization": f"Bearer {api_key}",
320
+ "Content-Type": "application/json"
321
+ },
322
+ json={
323
+ "model": "gpt-4",
324
+ "messages": [
325
+ {
326
+ "role": "system",
327
+ "content": EXTRACTION_PROMPT
328
+ },
329
+ {
330
+ "role": "user",
331
+ "content": f"Extract profile from:\n\n{conversation}"
332
+ }
333
+ ],
334
+ "temperature": 0.3
335
+ }
336
+ )
337
+
338
+ if response.status_code == 200:
339
+ profile_data = json.loads(
340
+ response.json()["choices"][0]["message"]["content"].strip()
341
+ )
342
+ return profile_data
343
+ else:
344
+ raise Exception(f"API error: {response.status_code}")
345
+
346
+ except Exception as e:
347
+ logger.error(f"Profile extraction error: {str(e)}")
348
+ return {
349
+ "industry_score": 0.6,
350
+ "experience_score": 0.6,
351
+ "education_score": 0.6,
352
+ "skills_score": 0.6,
353
+ "location_score": 0.6,
354
+ "current_salary": 85000
355
+ }
356
+
357
+ def generate_analysis(self, api_key: str) -> Dict[str, Any]:
358
+ """Generate complete salary analysis."""
359
+ try:
360
+ # Extract profile
361
+ profile_data = self.extract_profile(api_key)
362
+
363
+ # Run simulation
364
+ paths, factors = self.simulator.run_simulation(profile_data)
365
+
366
+ # Generate plots
367
+ plots_image = self.simulator.create_plots(paths)
368
+
369
+ # Generate report
370
+ report = self.simulator.generate_report(
371
+ profile_data,
372
+ paths,
373
+ factors
374
+ )
375
+
376
+ return {
377
+ "status": "success",
378
+ "report": report,
379
+ "plots": plots_image # Raw base64 string
380
+ }
381
+
382
+ except Exception as e:
383
+ logger.error(f"Analysis generation error: {str(e)}")
384
+ return {"error": str(e)}
385
+
386
+ # ==============================================
387
+ # Gradio Interface (Updated)
388
+ # ==============================================
389
 
390
  def create_interface():
391
+ """Create the Gradio interface."""
392
+ advisor = CareerAdvisor()
393
+
394
+ # Create Gradio blocks
395
+ with gr.Blocks(title="Monte Carlo Simulation of Salary Prediction") as demo:
396
+ # Title and description
397
  gr.Markdown("""
398
+ # 💰 Monte Carlo Simulation of Salary Prediction
 
 
 
 
 
 
 
399
 
400
+ Chat with me about your career, and I'll generate detailed salary projections
401
+ using Monte Carlo simulation with machine learning.
402
  """)
403
+
404
+ # API Key input
405
+ with gr.Row():
406
+ api_key = gr.Textbox(
407
+ label="OpenAI API Key",
408
+ placeholder="Enter your API key",
409
+ type="password"
410
+ )
411
+
412
+ # Main content area
413
  with gr.Row():
414
+ # Left column: Chat interface
415
+ with gr.Column(scale=2):
416
+ chatbot = gr.Chatbot(
417
+ label="Career Conversation",
418
+ height=400,
419
+ show_copy_button=True,
420
+ type="messages" # Using OpenAI message format
421
  )
422
+
423
+ # Message input and send button
424
+ with gr.Row():
425
+ message = gr.Textbox(
426
+ label="Your message",
427
+ placeholder="Tell me about your career...",
428
+ lines=2,
429
+ scale=4
430
+ )
431
+ send_btn = gr.Button(
432
+ "Send Message",
433
+ scale=1
434
+ )
435
+
436
+ # Right column: Analysis output
437
+ with gr.Column(scale=3):
438
+ status = gr.Textbox(label="Status")
439
+ report = gr.TextArea(
440
+ label="Analysis Report",
441
+ lines=20,
442
+ max_lines=30
443
  )
444
+ plots = gr.Image(
445
+ label="Salary Projections",
446
+ show_download_button=True
 
447
  )
448
+
449
+ # Analysis button
450
+ analyze_btn = gr.Button(
451
+ "Generate Analysis",
452
+ variant="primary",
453
+ size="lg"
454
+ )
455
+
456
+ # Message handling function
457
+ def handle_message(
458
+ message: str,
459
+ history: List[Dict[str, str]],
460
+ key: str
461
+ ) -> Tuple[str, List[Dict[str, str]], str]:
462
+ """Process chat messages."""
463
+ try:
464
+ result = advisor.process_message(message, key)
465
+
466
+ if "error" in result:
467
+ return "", history, f"Error: {result['error']}"
468
+
469
+ # Format messages in OpenAI style
470
+ new_history = history + [
471
+ {"role": "user", "content": message},
472
+ {"role": "assistant", "content": result["response"]}
473
+ ]
474
+ return "", new_history, ""
475
+
476
+ except Exception as e:
477
+ return "", history, f"Error: {str(e)}"
478
+
479
+ # Analysis generation function
480
+ def generate_analysis(key: str) -> Tuple[str, str, str]:
481
+ """Generate salary analysis."""
482
+ try:
483
+ result = advisor.generate_analysis(key)
484
+
485
+ if "error" in result:
486
+ return f"Error: {result['error']}", "", None
487
+
488
+ # Decode base64 image for Gradio
489
+ plots_image = f"data:image/png;base64,{result['plots']}"
490
+
491
+ return (
492
+ "Analysis completed successfully!",
493
+ result["report"],
494
+ plots_image
495
  )
496
+
497
+ except Exception as e:
498
+ return f"Error: {str(e)}", "", None
499
+
500
+ # Wire up the interface
501
+ message.submit(
502
+ handle_message,
503
+ inputs=[message, chatbot, api_key],
504
+ outputs=[message, chatbot, status],
505
+ queue=False # Immediate response for better UX
506
  )
507
+
508
+ send_btn.click(
509
+ handle_message,
510
+ inputs=[message, chatbot, api_key],
511
+ outputs=[message, chatbot, status],
512
+ queue=False # Immediate response for better UX
 
 
 
513
  )
514
+
515
+ analyze_btn.click(
516
+ generate_analysis,
517
+ inputs=[api_key],
518
+ outputs=[status, report, plots]
519
+ )
520
+
521
+ return demo
522
+
523
+ # ==============================================
524
+ # Main Entry Point
525
+ # ==============================================
526
+
527
+ def main():
528
+ """Launch the application."""
529
+ # Create interface
530
+ demo = create_interface()
531
+
532
+ # Enable queue for concurrent processing
533
+ demo.queue()
534
 
535
+ # Launch the application
536
+ demo.launch(
537
+ server_name="0.0.0.0", # Required for HuggingFace Spaces
538
+ server_port=7860, # Standard port for HuggingFace Spaces
539
+ share=True # Enable sharing
540
+ )
541
 
542
  if __name__ == "__main__":
543
+ main()