-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.ts
More file actions
164 lines (147 loc) · 5.72 KB
/
Copy pathserver.ts
File metadata and controls
164 lines (147 loc) · 5.72 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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import express from "express";
import path from "path";
import { createServer as createViteServer } from "vite";
import { GoogleGenAI, Type } from "@google/genai";
import dotenv from "dotenv";
dotenv.config();
// Initialize Express
const app = express();
app.use(express.json());
const PORT = 3000;
// Lazy initialize Gemini API client
let aiClient: GoogleGenAI | null = null;
function getAiClient(): GoogleGenAI {
if (!aiClient) {
const apiKey = process.env.GEMINI_API_KEY;
if (!apiKey) {
throw new Error("GEMINI_API_KEY environment variable is not set.");
}
aiClient = new GoogleGenAI({
apiKey,
httpOptions: {
headers: {
"User-Agent": "aistudio-build",
},
},
});
}
return aiClient;
}
// AI Issue Triage Endpoint
app.post("/api/triage", async (req, res) => {
try {
const { complaint, assetName, assetCategory, assetLocation, assetCondition, recentHistory } = req.body;
if (!complaint) {
return res.status(400).json({ error: "Complaint text is required" });
}
let ai;
try {
ai = getAiClient();
} catch (e: any) {
// API key is missing or not configured
console.warn("Gemini Client initialization failed, returning fallback data:", e.message);
return res.json({
title: "Triage Alert: " + (complaint.length > 30 ? complaint.substring(0, 30) + "..." : complaint),
category: assetCategory || "General",
priority: "High",
possibleCauses: ["Requires technician diagnostic due to manual submission"],
initialChecks: ["Advise visual inspection only", "Ensure area is secure before technician arrival"],
recurringPatternWarning: recentHistory && recentHistory.length > 0 ? "Warning: Asset has a history of reported issues." : "",
isFallback: true,
errorMessage: "GEMINI_API_KEY is not configured in Secrets."
});
}
const prompt = `
Please triage this maintenance issue.
Asset Name: ${assetName || "Unknown Asset"}
Asset Category: ${assetCategory || "Unknown Category"}
Asset Location: ${assetLocation || "Unknown Location"}
Asset Condition: ${assetCondition || "Unknown"}
Recent History: ${recentHistory && recentHistory.length > 0 ? JSON.stringify(recentHistory) : "No recent issues reported"}
User Complaint: "${complaint}"
`;
const systemInstruction = `
You are MaintainIQ's expert AI maintenance engineer. Convert the user's natural-language complaint and asset context into a structured JSON triage report.
Ensure you do NOT provide unsafe instructions for electrical, mechanical, fire, medical, or industrial hazards. Always clearly recommend qualified technicians for critical or safety-related issues.
Analyse the recent history (if any) to check for repeated patterns (e.g. if the AC had the same leak 2 weeks ago, warn about a recurring drain blockage).
`;
const response = await ai.models.generateContent({
model: "gemini-3.5-flash",
contents: prompt,
config: {
systemInstruction,
responseMimeType: "application/json",
responseSchema: {
type: Type.OBJECT,
properties: {
title: {
type: Type.STRING,
description: "A concise professional title describing the issue."
},
category: {
type: Type.STRING,
description: "The suggested category of the issue (e.g. Electrical, Plumbing, HVAC, Mechanical, IT Hardware, Structural, etc.)."
},
priority: {
type: Type.STRING,
description: "Suggested priority: Low, Medium, High, or Critical."
},
possibleCauses: {
type: Type.ARRAY,
items: { type: Type.STRING },
description: "A list of 2-4 possible root causes."
},
initialChecks: {
type: Type.ARRAY,
items: { type: Type.STRING },
description: "A list of 2-4 safe initial checks for the user or technician. For any hazardous system, advise calling a qualified professional."
},
recurringPatternWarning: {
type: Type.STRING,
description: "A warning message summarizing any repeated patterns found in history, or an empty string if none."
}
},
required: ["title", "category", "priority", "possibleCauses", "initialChecks", "recurringPatternWarning"]
}
}
});
const text = response.text;
if (!text) {
throw new Error("No response text from Gemini API.");
}
const parsedResult = JSON.parse(text.trim());
return res.json(parsedResult);
} catch (err: any) {
console.error("AI Triage Error:", err);
return res.json({
title: "Diagnostics Required",
category: "General",
priority: "Medium",
possibleCauses: ["Requires manual investigation", "No AI triage results available"],
initialChecks: ["Safely secure the asset", "Report details to supervisor"],
recurringPatternWarning: "",
isFallback: true,
errorMessage: err.message
});
}
});
// Setup Vite or serve static production build
async function startServer() {
if (process.env.NODE_ENV !== "production") {
const vite = await createViteServer({
server: { middlewareMode: true },
appType: "spa",
});
app.use(vite.middlewares);
} else {
const distPath = path.join(process.cwd(), "dist");
app.use(express.static(distPath));
app.get("*", (req, res) => {
res.sendFile(path.join(distPath, "index.html"));
});
}
app.listen(PORT, "0.0.0.0", () => {
console.log(`Server running on http://localhost:${PORT}`);
});
}
startServer();