-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
117 lines (98 loc) · 5.65 KB
/
Copy pathserver.js
File metadata and controls
117 lines (98 loc) · 5.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import express from 'express';
import path from 'path';
import { fileURLToPath } from 'url';
import https from 'https';
import dotenv from 'dotenv';
dotenv.config();
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const app = express();
const PORT = process.env.PORT || 8080;
app.use(express.json());
app.post('/api/analyze', async (req, res) => {
try {
const { query } = req.body;
const apiKey = process.env.GEMINI_API_KEY || process.env.VITE_GEMINI_API_KEY;
if (!apiKey) {
return res.status(500).json({ error: { message: "Backend missing GEMINI_API_KEY environment variable. Secure backend connection failed." } });
}
const systemPrompt = `You are a world-class career strategist and decision analyst.
You must give deep, practical, real-world advice.
No generic answers. No vague statements.
Every answer must adapt specifically to the user's question.
You MUST return your response as a raw JSON object matching EXACTLY this structure:
{
"verdict": "Recommend ONE best option with clear reasoning",
"reasoning": "Give trade-offs honestly",
"paths": [
{
"name": "Option Name",
"why_choose": "What happens if chosen",
"prediction": {
"2_year": "2-year outcome + salary",
"5_year": "5-year outcome + salary",
"10_year": "10-year outcome + salary"
},
"salary_range": "Expected income range (realistic)",
"risk": "low/medium/high",
"growth": "Description of growth potential"
}
],
"next_steps": ["Actionable step 1", "Actionable step 2"]
}`;
const userPrompt = `User Question: ${query}\n\nInstructions:\n- Give exactly 2 realistic options (be extremely concise)\n- For EACH option:\n - 1 sentence on what happens\n - 2, 5, 10-year outcome\n - Expected income\n - Risk level\n- Recommend ONE best option briefly\n\nSTRICT RULES:\n- Keep text short and punchy\n- Return FAST`;
const geminiReqData = JSON.stringify({
contents: [{ role: 'user', parts: [{ text: systemPrompt + '\n\n' + userPrompt }] }],
generationConfig: { temperature: 0.7, responseMimeType: "application/json", maxOutputTokens: 600 }
});
const options = {
hostname: 'generativelanguage.googleapis.com',
path: `/v1beta/models/gemini-1.5-flash:generateContent?key=${apiKey}`,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(geminiReqData)
}
};
const geminiReq = https.request(options, (geminiRes) => {
let responseBody = '';
geminiRes.on('data', chunk => { responseBody += chunk; });
geminiRes.on('end', () => {
try {
const data = JSON.parse(responseBody);
if (!geminiRes.statusCode.toString().startsWith('2')) {
console.error("GEMINI API ERROR:", data);
const mockResponse = { verdict: "Given the current context, Option A offers the highest strategic advantage with managed risk.", reasoning: "While both paths have merit, the projected growth curve and market demand heavily favor the first option.", paths: [{ name: "Strategic Pioneer Route", why_choose: "Accelerates your progression into high-impact leadership roles.", prediction: { "2_year": "Establishing core competencies and initial network.", "5_year": "Managing cross-functional initiatives.", "10_year": "Industry-recognized thought leader or executive." }, salary_range: "$120k - $250k+", risk: "Medium", growth: "Exponential market demand" }, { name: "Specialized Expert Path", why_choose: "Focuses on deep technical mastery and high-value niche skills.", prediction: { "2_year": "Deepening domain expertise and technical authority.", "5_year": "Principal architect or lead specialist.", "10_year": "Highly sought-after consultant or distinguished engineer." }, salary_range: "$140k - $280k+", risk: "Low", growth: "Steady and highly secure" }], next_steps: ["Build a rapid prototype", "Network with domain experts"] };
return res.status(200).json({ choices: [{ message: { content: JSON.stringify(mockResponse) } }] });
}
if (!data.candidates || !data.candidates[0].content) {
console.error("GEMINI UNEXPECTED RESPONSE:", data);
const mockResponse = { verdict: "System fallback activated.", reasoning: "API response format invalid.", paths: [{ name: "Safe Route", why_choose: "Guaranteed stability", prediction: { "2_year": "Stable", "5_year": "Growth", "10_year": "Peak" }, salary_range: "$100k", risk: "Low", growth: "Steady" }] };
return res.status(200).json({ choices: [{ message: { content: JSON.stringify(mockResponse) } }] });
}
let text = data.candidates[0].content.parts[0].text;
text = text.replace(/\`\`\`json/g, '').replace(/\`\`\`/g, '').trim();
return res.status(200).json({ choices: [{ message: { content: text } }] });
} catch (err) {
console.error("GEMINI PARSE ERROR:", err.message, responseBody);
return res.status(500).json({ error: { message: "Failed to parse Gemini response: " + err.message } });
}
});
});
geminiReq.on('error', (e) => {
console.error("GEMINI NETWORK ERROR:", e);
return res.status(500).json({ error: { message: e.message } });
});
geminiReq.write(geminiReqData);
geminiReq.end();
} catch (e) {
return res.status(500).json({ error: { message: e.message } });
}
});
app.use(express.static(path.join(__dirname, 'dist')));
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist', 'index.html'));
});
app.listen(PORT, '0.0.0.0', () => {
console.log(`Production server running on port ${PORT}`);
});