Spaces:
Sleeping
Sleeping
Commit ·
2f2874d
1
Parent(s): 217d6b6
Uploaded
Browse files
server.js
CHANGED
|
@@ -50,18 +50,15 @@ async function updateSheetCache() {
|
|
| 50 |
console.log('No data found in spreadsheet.');
|
| 51 |
return;
|
| 52 |
}
|
| 53 |
-
|
| 54 |
rawSheetCache = rows;
|
| 55 |
const headers = rows[0];
|
| 56 |
const dataRows = rows.slice(1);
|
| 57 |
let currentLocation = '';
|
| 58 |
let currentOfficeExt = 'N/A';
|
| 59 |
const allStaff = [];
|
| 60 |
-
|
| 61 |
dataRows.forEach((row, rowIndex) => {
|
| 62 |
const rowData = {};
|
| 63 |
headers.forEach((header, index) => rowData[header] = row[index] || '');
|
| 64 |
-
|
| 65 |
if (isValidName(rowData.Location)) {
|
| 66 |
currentLocation = rowData.Location;
|
| 67 |
if (currentLocation.toLowerCase().includes('liveoak')) {
|
|
@@ -73,27 +70,16 @@ async function updateSheetCache() {
|
|
| 73 |
}
|
| 74 |
rowData.Location = currentLocation;
|
| 75 |
rowData.OfficeExtension = currentOfficeExt;
|
| 76 |
-
|
| 77 |
const baseId = `r${rowIndex}`;
|
| 78 |
-
|
| 79 |
-
if (isValidName(rowData.
|
| 80 |
-
|
| 81 |
-
}
|
| 82 |
-
if (isValidName(rowData.MA)) {
|
| 83 |
-
allStaff.push({ ...rowData, id: `${baseId}-ma`, role: 'Medical Assistant', Extension: rowData.Extension });
|
| 84 |
-
}
|
| 85 |
-
if (isValidName(rowData.VA)) {
|
| 86 |
-
allStaff.push({ ...rowData, id: `${baseId}-va`, role: 'Virtual Assistant', Extension: rowData.Ext });
|
| 87 |
-
}
|
| 88 |
-
if (isValidName(rowData['Team Leads/Manager'])) {
|
| 89 |
-
allStaff.push({ ...rowData, id: `${baseId}-tl`, role: 'Team Lead / Manager', Extension: rowData['Ext/Phone#'] });
|
| 90 |
-
}
|
| 91 |
if (isValidName(rowData['Ext/Phone#']) && /^[a-zA-Z]+-\s*\d+$/.test(rowData['Ext/Phone#'])) {
|
| 92 |
const [name, ext] = rowData['Ext/Phone#'].split('-');
|
| 93 |
allStaff.push({ ...rowData, id: `${baseId}-other`, role: 'Other Staff', OtherStaffName: name.trim(), Extension: ext.trim() });
|
| 94 |
}
|
| 95 |
});
|
| 96 |
-
|
| 97 |
processedStaffCache = allStaff;
|
| 98 |
console.log('Cache updated. Processed records:', processedStaffCache.length);
|
| 99 |
} catch (error) {
|
|
@@ -120,21 +106,15 @@ async function generateWithRetry(model, prompt, maxRetries = 3) {
|
|
| 120 |
}
|
| 121 |
}
|
| 122 |
|
| 123 |
-
app.get('/', (req, res) =>
|
| 124 |
-
res.sendFile(path.join(__dirname, 'index.html'));
|
| 125 |
-
});
|
| 126 |
|
| 127 |
app.get('/api/data', (req, res) => {
|
| 128 |
-
if (!processedStaffCache) {
|
| 129 |
-
return res.status(503).json({ message: 'Data is being fetched.' });
|
| 130 |
-
}
|
| 131 |
res.json(processedStaffCache);
|
| 132 |
});
|
| 133 |
|
| 134 |
app.get('/api/raw-data', (req, res) => {
|
| 135 |
-
if (!rawSheetCache) {
|
| 136 |
-
return res.status(503).json({ message: 'Data is being fetched.' });
|
| 137 |
-
}
|
| 138 |
res.json(rawSheetCache);
|
| 139 |
});
|
| 140 |
|
|
@@ -147,6 +127,7 @@ app.get('/api/ask-luis', async (req, res) => {
|
|
| 147 |
res.setHeader('Cache-Control', 'no-cache');
|
| 148 |
res.setHeader('Connection', 'keep-alive');
|
| 149 |
res.flushHeaders();
|
|
|
|
| 150 |
const prompt = `
|
| 151 |
You are "Luis", an AI data analyst for the Hillside Medical Group staff directory.
|
| 152 |
Your primary function is to answer questions about staff based ONLY on the JSON data provided.
|
|
@@ -165,18 +146,28 @@ app.get('/api/ask-luis', async (req, res) => {
|
|
| 165 |
**User's Question:**
|
| 166 |
"${question}"
|
| 167 |
`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
try {
|
| 169 |
-
const
|
|
|
|
|
|
|
| 170 |
for await (const chunk of result.stream) {
|
| 171 |
if (chunk.text()) {
|
| 172 |
res.write(`data: ${JSON.stringify({ chunk: chunk.text() })}\n\n`);
|
| 173 |
}
|
| 174 |
}
|
| 175 |
} catch (error) {
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
|
|
|
|
|
|
| 180 |
res.write(`data: ${JSON.stringify({ error: errorMessage })}\n\n`);
|
| 181 |
} finally {
|
| 182 |
res.write('data: [DONE]\n\n');
|
|
|
|
| 50 |
console.log('No data found in spreadsheet.');
|
| 51 |
return;
|
| 52 |
}
|
|
|
|
| 53 |
rawSheetCache = rows;
|
| 54 |
const headers = rows[0];
|
| 55 |
const dataRows = rows.slice(1);
|
| 56 |
let currentLocation = '';
|
| 57 |
let currentOfficeExt = 'N/A';
|
| 58 |
const allStaff = [];
|
|
|
|
| 59 |
dataRows.forEach((row, rowIndex) => {
|
| 60 |
const rowData = {};
|
| 61 |
headers.forEach((header, index) => rowData[header] = row[index] || '');
|
|
|
|
| 62 |
if (isValidName(rowData.Location)) {
|
| 63 |
currentLocation = rowData.Location;
|
| 64 |
if (currentLocation.toLowerCase().includes('liveoak')) {
|
|
|
|
| 70 |
}
|
| 71 |
rowData.Location = currentLocation;
|
| 72 |
rowData.OfficeExtension = currentOfficeExt;
|
|
|
|
| 73 |
const baseId = `r${rowIndex}`;
|
| 74 |
+
if (isValidName(rowData.Provider)) allStaff.push({ ...rowData, id: `${baseId}-p`, role: 'Provider' });
|
| 75 |
+
if (isValidName(rowData.MA)) allStaff.push({ ...rowData, id: `${baseId}-ma`, role: 'Medical Assistant', Extension: rowData.Extension });
|
| 76 |
+
if (isValidName(rowData.VA)) allStaff.push({ ...rowData, id: `${baseId}-va`, role: 'Virtual Assistant', Extension: rowData.Ext });
|
| 77 |
+
if (isValidName(rowData['Team Leads/Manager'])) allStaff.push({ ...rowData, id: `${baseId}-tl`, role: 'Team Lead / Manager', Extension: rowData['Ext/Phone#'] });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
if (isValidName(rowData['Ext/Phone#']) && /^[a-zA-Z]+-\s*\d+$/.test(rowData['Ext/Phone#'])) {
|
| 79 |
const [name, ext] = rowData['Ext/Phone#'].split('-');
|
| 80 |
allStaff.push({ ...rowData, id: `${baseId}-other`, role: 'Other Staff', OtherStaffName: name.trim(), Extension: ext.trim() });
|
| 81 |
}
|
| 82 |
});
|
|
|
|
| 83 |
processedStaffCache = allStaff;
|
| 84 |
console.log('Cache updated. Processed records:', processedStaffCache.length);
|
| 85 |
} catch (error) {
|
|
|
|
| 106 |
}
|
| 107 |
}
|
| 108 |
|
| 109 |
+
app.get('/', (req, res) => res.sendFile(path.join(__dirname, 'index.html')));
|
|
|
|
|
|
|
| 110 |
|
| 111 |
app.get('/api/data', (req, res) => {
|
| 112 |
+
if (!processedStaffCache) return res.status(503).json({ message: 'Data is being fetched.' });
|
|
|
|
|
|
|
| 113 |
res.json(processedStaffCache);
|
| 114 |
});
|
| 115 |
|
| 116 |
app.get('/api/raw-data', (req, res) => {
|
| 117 |
+
if (!rawSheetCache) return res.status(503).json({ message: 'Data is being fetched.' });
|
|
|
|
|
|
|
| 118 |
res.json(rawSheetCache);
|
| 119 |
});
|
| 120 |
|
|
|
|
| 127 |
res.setHeader('Cache-Control', 'no-cache');
|
| 128 |
res.setHeader('Connection', 'keep-alive');
|
| 129 |
res.flushHeaders();
|
| 130 |
+
|
| 131 |
const prompt = `
|
| 132 |
You are "Luis", an AI data analyst for the Hillside Medical Group staff directory.
|
| 133 |
Your primary function is to answer questions about staff based ONLY on the JSON data provided.
|
|
|
|
| 146 |
**User's Question:**
|
| 147 |
"${question}"
|
| 148 |
`;
|
| 149 |
+
|
| 150 |
+
const AI_TIMEOUT = 20000; // 20 seconds
|
| 151 |
+
const timeoutPromise = new Promise((_, reject) =>
|
| 152 |
+
setTimeout(() => reject(new Error('AI_TIMEOUT')), AI_TIMEOUT)
|
| 153 |
+
);
|
| 154 |
+
|
| 155 |
try {
|
| 156 |
+
const resultPromise = generateWithRetry(model, prompt);
|
| 157 |
+
const result = await Promise.race([resultPromise, timeoutPromise]);
|
| 158 |
+
|
| 159 |
for await (const chunk of result.stream) {
|
| 160 |
if (chunk.text()) {
|
| 161 |
res.write(`data: ${JSON.stringify({ chunk: chunk.text() })}\n\n`);
|
| 162 |
}
|
| 163 |
}
|
| 164 |
} catch (error) {
|
| 165 |
+
let errorMessage = "I'm having a bit of trouble connecting to my brain right now. Please try again in a moment.";
|
| 166 |
+
if (error.message === 'AI_TIMEOUT') {
|
| 167 |
+
errorMessage = "Sorry, my connection to the AI service timed out. The service may be busy. Please try your question again in a few moments.";
|
| 168 |
+
} else if (error.status === 503) {
|
| 169 |
+
errorMessage = "The directory AI service is currently busy. Please try again.";
|
| 170 |
+
}
|
| 171 |
res.write(`data: ${JSON.stringify({ error: errorMessage })}\n\n`);
|
| 172 |
} finally {
|
| 173 |
res.write('data: [DONE]\n\n');
|