@crashsense/ai 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +251 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +245 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +50 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { AIConfig, CrashEvent, AIResponse, AIPayload, ClassificationResult, CrashAnalysis } from '@crashsense/types';
|
|
2
|
+
export { AIConfig, AIPayload, AIResponse, CrashAnalysis } from '@crashsense/types';
|
|
3
|
+
|
|
4
|
+
declare function createAIClient(config: AIConfig): {
|
|
5
|
+
analyze(event: CrashEvent): Promise<AIResponse | null>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
declare function buildAIPayload(event: CrashEvent): AIPayload;
|
|
9
|
+
|
|
10
|
+
declare function mergeAnalysis(heuristic: ClassificationResult, aiResponse: AIResponse | null): CrashAnalysis;
|
|
11
|
+
|
|
12
|
+
declare function parseAIResponse(raw: string): AIResponse | null;
|
|
13
|
+
|
|
14
|
+
declare const SYSTEM_PROMPT = "You are CrashSense AI, a specialized web application crash diagnostician. You analyze structured crash reports from JavaScript web applications (React, Vue, vanilla JS) running in browsers and mobile WebViews.\n\nYour task: Given a crash report, provide:\n1. ROOT CAUSE: The specific technical root cause (1-2 sentences)\n2. EXPLANATION: Why this happened, including the chain of events (3-5 sentences)\n3. FIX: Working code example that fixes the issue\n4. PREVENTION: How to prevent this class of bug in the future (2-3 bullet points)\n5. CONFIDENCE: Your confidence in this diagnosis (0.0-1.0)\n\nRules:\n- Be specific. \"Check your code\" is not acceptable. Name the exact issue.\n- Fix code must be syntactically correct and production-ready.\n- If you are not confident (< 0.6), say so and list the top 2-3 possible causes.\n- Reference the specific component, file, or line from the stack trace.\n- Consider the system state (memory, CPU, network) as contributing factors.\n- Consider the device context (mobile, low memory, slow network).\n\nOutput ONLY valid JSON matching this schema:\n{\n \"rootCause\": \"string\",\n \"explanation\": \"string\",\n \"fix\": {\n \"description\": \"string\",\n \"code\": \"string\",\n \"filename\": \"string\"\n },\n \"prevention\": [\"string\"],\n \"confidence\": number,\n \"alternativeCauses\": [{\"cause\": \"string\", \"likelihood\": number}]\n}";
|
|
15
|
+
|
|
16
|
+
export { SYSTEM_PROMPT, buildAIPayload, createAIClient, mergeAnalysis, parseAIResponse };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { AIConfig, CrashEvent, AIResponse, AIPayload, ClassificationResult, CrashAnalysis } from '@crashsense/types';
|
|
2
|
+
export { AIConfig, AIPayload, AIResponse, CrashAnalysis } from '@crashsense/types';
|
|
3
|
+
|
|
4
|
+
declare function createAIClient(config: AIConfig): {
|
|
5
|
+
analyze(event: CrashEvent): Promise<AIResponse | null>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
declare function buildAIPayload(event: CrashEvent): AIPayload;
|
|
9
|
+
|
|
10
|
+
declare function mergeAnalysis(heuristic: ClassificationResult, aiResponse: AIResponse | null): CrashAnalysis;
|
|
11
|
+
|
|
12
|
+
declare function parseAIResponse(raw: string): AIResponse | null;
|
|
13
|
+
|
|
14
|
+
declare const SYSTEM_PROMPT = "You are CrashSense AI, a specialized web application crash diagnostician. You analyze structured crash reports from JavaScript web applications (React, Vue, vanilla JS) running in browsers and mobile WebViews.\n\nYour task: Given a crash report, provide:\n1. ROOT CAUSE: The specific technical root cause (1-2 sentences)\n2. EXPLANATION: Why this happened, including the chain of events (3-5 sentences)\n3. FIX: Working code example that fixes the issue\n4. PREVENTION: How to prevent this class of bug in the future (2-3 bullet points)\n5. CONFIDENCE: Your confidence in this diagnosis (0.0-1.0)\n\nRules:\n- Be specific. \"Check your code\" is not acceptable. Name the exact issue.\n- Fix code must be syntactically correct and production-ready.\n- If you are not confident (< 0.6), say so and list the top 2-3 possible causes.\n- Reference the specific component, file, or line from the stack trace.\n- Consider the system state (memory, CPU, network) as contributing factors.\n- Consider the device context (mobile, low memory, slow network).\n\nOutput ONLY valid JSON matching this schema:\n{\n \"rootCause\": \"string\",\n \"explanation\": \"string\",\n \"fix\": {\n \"description\": \"string\",\n \"code\": \"string\",\n \"filename\": \"string\"\n },\n \"prevention\": [\"string\"],\n \"confidence\": number,\n \"alternativeCauses\": [{\"cause\": \"string\", \"likelihood\": number}]\n}";
|
|
15
|
+
|
|
16
|
+
export { SYSTEM_PROMPT, buildAIPayload, createAIClient, mergeAnalysis, parseAIResponse };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/payload-builder.ts
|
|
4
|
+
function truncate(str, maxLen) {
|
|
5
|
+
return str.length > maxLen ? str.slice(0, maxLen) + "..." : str;
|
|
6
|
+
}
|
|
7
|
+
function formatRelativeTime(eventTimestamp, referenceTimestamp) {
|
|
8
|
+
const delta = (eventTimestamp - referenceTimestamp) / 1e3;
|
|
9
|
+
if (delta >= 0) return "0s";
|
|
10
|
+
return `${delta.toFixed(1)}s`;
|
|
11
|
+
}
|
|
12
|
+
function buildAIPayload(event) {
|
|
13
|
+
const stackTop5 = event.error.stack.slice(0, 5).map(
|
|
14
|
+
(f) => `at ${f.function} (${f.filename}:${f.lineno}:${f.colno})`
|
|
15
|
+
);
|
|
16
|
+
const userCodeFrames = event.error.stack.filter((f) => f.inApp).slice(0, 5).map((f) => `at ${f.function} (${f.filename}:${f.lineno}:${f.colno})`);
|
|
17
|
+
const memUtil = event.system.memory.utilizationPercent;
|
|
18
|
+
const memUtilStr = memUtil !== null ? `${memUtil}%` : "N/A";
|
|
19
|
+
const last5Breadcrumbs = event.breadcrumbs.slice(-5).map((bc) => ({
|
|
20
|
+
type: bc.type,
|
|
21
|
+
message: truncate(bc.message, 100),
|
|
22
|
+
time: formatRelativeTime(bc.timestamp, event.timestamp)
|
|
23
|
+
}));
|
|
24
|
+
return {
|
|
25
|
+
crash_summary: {
|
|
26
|
+
category: event.category,
|
|
27
|
+
subcategory: event.subcategory,
|
|
28
|
+
heuristic_confidence: event.confidence,
|
|
29
|
+
severity: event.severity
|
|
30
|
+
},
|
|
31
|
+
error: {
|
|
32
|
+
type: event.error.type,
|
|
33
|
+
message: truncate(event.error.message, 500),
|
|
34
|
+
stack_top_5: stackTop5,
|
|
35
|
+
user_code_frames: userCodeFrames
|
|
36
|
+
},
|
|
37
|
+
system_state: {
|
|
38
|
+
memory_utilization: memUtilStr,
|
|
39
|
+
memory_trend: event.system.memory.trend,
|
|
40
|
+
long_tasks_last_30s: event.system.cpu.longTasksLast30s,
|
|
41
|
+
fps_at_crash: event.system.eventLoop.fps,
|
|
42
|
+
pending_network_requests: event.system.network.pendingRequests,
|
|
43
|
+
failed_requests_last_60s: event.system.network.failedRequestsLast60s
|
|
44
|
+
},
|
|
45
|
+
device: {
|
|
46
|
+
platform: truncate(event.device.userAgent, 80),
|
|
47
|
+
memory: event.device.deviceMemory !== null ? `${event.device.deviceMemory}GB` : "N/A",
|
|
48
|
+
viewport: `${event.device.viewport.width}x${event.device.viewport.height}`,
|
|
49
|
+
connection: event.system.network.connectionType ?? "unknown"
|
|
50
|
+
},
|
|
51
|
+
framework: {
|
|
52
|
+
name: event.framework.name,
|
|
53
|
+
version: event.framework.version,
|
|
54
|
+
lifecycle_stage: event.framework.lifecycleStage ?? "unknown",
|
|
55
|
+
component_path: event.framework.componentTree ?? [],
|
|
56
|
+
render_count_since_nav: event.framework.renderCount ?? 0
|
|
57
|
+
},
|
|
58
|
+
breadcrumbs_last_5: last5Breadcrumbs,
|
|
59
|
+
contributing_factors: event.contributingFactors.map((f) => ({
|
|
60
|
+
factor: f.factor,
|
|
61
|
+
weight: f.weight,
|
|
62
|
+
evidence: truncate(f.evidence, 150)
|
|
63
|
+
}))
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/prompt.ts
|
|
68
|
+
var SYSTEM_PROMPT = `You are CrashSense AI, a specialized web application crash diagnostician. You analyze structured crash reports from JavaScript web applications (React, Vue, vanilla JS) running in browsers and mobile WebViews.
|
|
69
|
+
|
|
70
|
+
Your task: Given a crash report, provide:
|
|
71
|
+
1. ROOT CAUSE: The specific technical root cause (1-2 sentences)
|
|
72
|
+
2. EXPLANATION: Why this happened, including the chain of events (3-5 sentences)
|
|
73
|
+
3. FIX: Working code example that fixes the issue
|
|
74
|
+
4. PREVENTION: How to prevent this class of bug in the future (2-3 bullet points)
|
|
75
|
+
5. CONFIDENCE: Your confidence in this diagnosis (0.0-1.0)
|
|
76
|
+
|
|
77
|
+
Rules:
|
|
78
|
+
- Be specific. "Check your code" is not acceptable. Name the exact issue.
|
|
79
|
+
- Fix code must be syntactically correct and production-ready.
|
|
80
|
+
- If you are not confident (< 0.6), say so and list the top 2-3 possible causes.
|
|
81
|
+
- Reference the specific component, file, or line from the stack trace.
|
|
82
|
+
- Consider the system state (memory, CPU, network) as contributing factors.
|
|
83
|
+
- Consider the device context (mobile, low memory, slow network).
|
|
84
|
+
|
|
85
|
+
Output ONLY valid JSON matching this schema:
|
|
86
|
+
{
|
|
87
|
+
"rootCause": "string",
|
|
88
|
+
"explanation": "string",
|
|
89
|
+
"fix": {
|
|
90
|
+
"description": "string",
|
|
91
|
+
"code": "string",
|
|
92
|
+
"filename": "string"
|
|
93
|
+
},
|
|
94
|
+
"prevention": ["string"],
|
|
95
|
+
"confidence": number,
|
|
96
|
+
"alternativeCauses": [{"cause": "string", "likelihood": number}]
|
|
97
|
+
}`;
|
|
98
|
+
function buildMessages(payload) {
|
|
99
|
+
return [
|
|
100
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
101
|
+
{ role: "user", content: JSON.stringify(payload, null, 2) }
|
|
102
|
+
];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/response-parser.ts
|
|
106
|
+
function extractJsonFromMarkdown(raw) {
|
|
107
|
+
const codeBlockMatch = raw.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
|
|
108
|
+
if (codeBlockMatch) return codeBlockMatch[1].trim();
|
|
109
|
+
return raw.trim();
|
|
110
|
+
}
|
|
111
|
+
function isValidAIResponse(obj) {
|
|
112
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
113
|
+
const o = obj;
|
|
114
|
+
if (typeof o.rootCause !== "string") return false;
|
|
115
|
+
if (typeof o.explanation !== "string") return false;
|
|
116
|
+
if (typeof o.confidence !== "number" || o.confidence < 0 || o.confidence > 1) return false;
|
|
117
|
+
if (!Array.isArray(o.prevention)) return false;
|
|
118
|
+
if (o.fix !== null && o.fix !== void 0) {
|
|
119
|
+
const fix = o.fix;
|
|
120
|
+
if (typeof fix.description !== "string") return false;
|
|
121
|
+
if (typeof fix.code !== "string") return false;
|
|
122
|
+
if (typeof fix.filename !== "string") return false;
|
|
123
|
+
}
|
|
124
|
+
if (!Array.isArray(o.alternativeCauses)) return false;
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
function parseAIResponse(raw) {
|
|
128
|
+
try {
|
|
129
|
+
const jsonStr = extractJsonFromMarkdown(raw);
|
|
130
|
+
const parsed = JSON.parse(jsonStr);
|
|
131
|
+
if (!isValidAIResponse(parsed)) return null;
|
|
132
|
+
return {
|
|
133
|
+
rootCause: parsed.rootCause,
|
|
134
|
+
explanation: parsed.explanation,
|
|
135
|
+
fix: parsed.fix ?? { description: "", code: "", filename: "" },
|
|
136
|
+
prevention: parsed.prevention,
|
|
137
|
+
confidence: Math.min(1, Math.max(0, parsed.confidence)),
|
|
138
|
+
alternativeCauses: parsed.alternativeCauses ?? []
|
|
139
|
+
};
|
|
140
|
+
} catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/client.ts
|
|
146
|
+
function sleep(ms) {
|
|
147
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
148
|
+
}
|
|
149
|
+
function createAIClient(config) {
|
|
150
|
+
const timeout = config.timeout ?? 3e4;
|
|
151
|
+
const maxRetries = config.retries ?? 2;
|
|
152
|
+
async function sendRequest(event) {
|
|
153
|
+
const payload = buildAIPayload(event);
|
|
154
|
+
const messages = buildMessages(payload);
|
|
155
|
+
const body = {
|
|
156
|
+
messages,
|
|
157
|
+
temperature: config.temperature ?? 0.1,
|
|
158
|
+
max_tokens: config.maxTokens ?? 2e3
|
|
159
|
+
};
|
|
160
|
+
if (config.model) {
|
|
161
|
+
body.model = config.model;
|
|
162
|
+
}
|
|
163
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
164
|
+
const controller = new AbortController();
|
|
165
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
166
|
+
try {
|
|
167
|
+
const response = await fetch(config.endpoint, {
|
|
168
|
+
method: "POST",
|
|
169
|
+
headers: {
|
|
170
|
+
"Content-Type": "application/json",
|
|
171
|
+
"Authorization": `Bearer ${config.apiKey}`
|
|
172
|
+
},
|
|
173
|
+
body: JSON.stringify(body),
|
|
174
|
+
signal: controller.signal
|
|
175
|
+
});
|
|
176
|
+
clearTimeout(timeoutId);
|
|
177
|
+
if (!response.ok) {
|
|
178
|
+
if (attempt < maxRetries) {
|
|
179
|
+
await sleep(1e3 * (attempt + 1));
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
const data = await response.json();
|
|
185
|
+
return data.choices?.[0]?.message?.content ?? null;
|
|
186
|
+
} catch {
|
|
187
|
+
clearTimeout(timeoutId);
|
|
188
|
+
if (attempt < maxRetries) {
|
|
189
|
+
await sleep(1e3 * (attempt + 1));
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
async analyze(event) {
|
|
199
|
+
const rawResponse = await sendRequest(event);
|
|
200
|
+
if (!rawResponse) return null;
|
|
201
|
+
return parseAIResponse(rawResponse);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// src/hybrid-merger.ts
|
|
207
|
+
function generateHeuristicExplanation(classification) {
|
|
208
|
+
const { category, subcategory, confidence } = classification;
|
|
209
|
+
return `Heuristic analysis identified this as a ${category} issue (${subcategory}) with ${(confidence * 100).toFixed(0)}% confidence. ` + classification.contributingFactors.map((f) => f.evidence).join(" ");
|
|
210
|
+
}
|
|
211
|
+
function mergeAnalysis(heuristic, aiResponse) {
|
|
212
|
+
if (!aiResponse) {
|
|
213
|
+
return {
|
|
214
|
+
rootCause: `${heuristic.category}: ${heuristic.subcategory}`,
|
|
215
|
+
explanation: generateHeuristicExplanation(heuristic),
|
|
216
|
+
fix: null,
|
|
217
|
+
prevention: [],
|
|
218
|
+
confidence: heuristic.confidence,
|
|
219
|
+
alternativeCauses: [],
|
|
220
|
+
source: "heuristic"
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
if (aiResponse.confidence > 0.8) {
|
|
224
|
+
return {
|
|
225
|
+
rootCause: aiResponse.rootCause,
|
|
226
|
+
explanation: aiResponse.explanation,
|
|
227
|
+
fix: aiResponse.fix,
|
|
228
|
+
prevention: aiResponse.prevention,
|
|
229
|
+
confidence: heuristic.confidence * 0.4 + aiResponse.confidence * 0.6,
|
|
230
|
+
alternativeCauses: aiResponse.alternativeCauses,
|
|
231
|
+
source: "ai"
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
return {
|
|
235
|
+
rootCause: aiResponse.rootCause || `${heuristic.category}: ${heuristic.subcategory}`,
|
|
236
|
+
explanation: aiResponse.explanation || generateHeuristicExplanation(heuristic),
|
|
237
|
+
fix: aiResponse.fix,
|
|
238
|
+
prevention: aiResponse.prevention,
|
|
239
|
+
confidence: heuristic.confidence * 0.4 + aiResponse.confidence * 0.6,
|
|
240
|
+
alternativeCauses: aiResponse.alternativeCauses,
|
|
241
|
+
source: "hybrid"
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
exports.SYSTEM_PROMPT = SYSTEM_PROMPT;
|
|
246
|
+
exports.buildAIPayload = buildAIPayload;
|
|
247
|
+
exports.createAIClient = createAIClient;
|
|
248
|
+
exports.mergeAnalysis = mergeAnalysis;
|
|
249
|
+
exports.parseAIResponse = parseAIResponse;
|
|
250
|
+
//# sourceMappingURL=index.js.map
|
|
251
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/payload-builder.ts","../src/prompt.ts","../src/response-parser.ts","../src/client.ts","../src/hybrid-merger.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { CrashEvent, AIPayload } from '@crashsense/types';\n\nfunction truncate(str: string, maxLen: number): string {\n return str.length > maxLen ? str.slice(0, maxLen) + '...' : str;\n}\n\nfunction formatRelativeTime(eventTimestamp: number, referenceTimestamp: number): string {\n const delta = (eventTimestamp - referenceTimestamp) / 1000;\n if (delta >= 0) return '0s';\n return `${delta.toFixed(1)}s`;\n}\n\nexport function buildAIPayload(event: CrashEvent): AIPayload {\n const stackTop5 = event.error.stack.slice(0, 5).map(\n (f) => `at ${f.function} (${f.filename}:${f.lineno}:${f.colno})`,\n );\n\n const userCodeFrames = event.error.stack\n .filter((f) => f.inApp)\n .slice(0, 5)\n .map((f) => `at ${f.function} (${f.filename}:${f.lineno}:${f.colno})`);\n\n const memUtil = event.system.memory.utilizationPercent;\n const memUtilStr = memUtil !== null ? `${memUtil}%` : 'N/A';\n\n const last5Breadcrumbs = event.breadcrumbs.slice(-5).map((bc) => ({\n type: bc.type,\n message: truncate(bc.message, 100),\n time: formatRelativeTime(bc.timestamp, event.timestamp),\n }));\n\n return {\n crash_summary: {\n category: event.category,\n subcategory: event.subcategory,\n heuristic_confidence: event.confidence,\n severity: event.severity,\n },\n error: {\n type: event.error.type,\n message: truncate(event.error.message, 500),\n stack_top_5: stackTop5,\n user_code_frames: userCodeFrames,\n },\n system_state: {\n memory_utilization: memUtilStr,\n memory_trend: event.system.memory.trend,\n long_tasks_last_30s: event.system.cpu.longTasksLast30s,\n fps_at_crash: event.system.eventLoop.fps,\n pending_network_requests: event.system.network.pendingRequests,\n failed_requests_last_60s: event.system.network.failedRequestsLast60s,\n },\n device: {\n platform: truncate(event.device.userAgent, 80),\n memory: event.device.deviceMemory !== null ? `${event.device.deviceMemory}GB` : 'N/A',\n viewport: `${event.device.viewport.width}x${event.device.viewport.height}`,\n connection: event.system.network.connectionType ?? 'unknown',\n },\n framework: {\n name: event.framework.name,\n version: event.framework.version,\n lifecycle_stage: event.framework.lifecycleStage ?? 'unknown',\n component_path: event.framework.componentTree ?? [],\n render_count_since_nav: event.framework.renderCount ?? 0,\n },\n breadcrumbs_last_5: last5Breadcrumbs,\n contributing_factors: event.contributingFactors.map((f) => ({\n factor: f.factor,\n weight: f.weight,\n evidence: truncate(f.evidence, 150),\n })),\n };\n}\n","import type { AIPayload } from '@crashsense/types';\n\nexport const SYSTEM_PROMPT = `You are CrashSense AI, a specialized web application crash diagnostician. You analyze structured crash reports from JavaScript web applications (React, Vue, vanilla JS) running in browsers and mobile WebViews.\n\nYour task: Given a crash report, provide:\n1. ROOT CAUSE: The specific technical root cause (1-2 sentences)\n2. EXPLANATION: Why this happened, including the chain of events (3-5 sentences)\n3. FIX: Working code example that fixes the issue\n4. PREVENTION: How to prevent this class of bug in the future (2-3 bullet points)\n5. CONFIDENCE: Your confidence in this diagnosis (0.0-1.0)\n\nRules:\n- Be specific. \"Check your code\" is not acceptable. Name the exact issue.\n- Fix code must be syntactically correct and production-ready.\n- If you are not confident (< 0.6), say so and list the top 2-3 possible causes.\n- Reference the specific component, file, or line from the stack trace.\n- Consider the system state (memory, CPU, network) as contributing factors.\n- Consider the device context (mobile, low memory, slow network).\n\nOutput ONLY valid JSON matching this schema:\n{\n \"rootCause\": \"string\",\n \"explanation\": \"string\",\n \"fix\": {\n \"description\": \"string\",\n \"code\": \"string\",\n \"filename\": \"string\"\n },\n \"prevention\": [\"string\"],\n \"confidence\": number,\n \"alternativeCauses\": [{\"cause\": \"string\", \"likelihood\": number}]\n}`;\n\nexport function buildMessages(\n payload: AIPayload,\n): Array<{ role: string; content: string }> {\n return [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: JSON.stringify(payload, null, 2) },\n ];\n}\n","import type { AIResponse } from '@crashsense/types';\n\nfunction extractJsonFromMarkdown(raw: string): string {\n const codeBlockMatch = raw.match(/```(?:json)?\\s*\\n?([\\s\\S]*?)\\n?```/);\n if (codeBlockMatch) return codeBlockMatch[1].trim();\n return raw.trim();\n}\n\nfunction isValidAIResponse(obj: unknown): obj is AIResponse {\n if (typeof obj !== 'object' || obj === null) return false;\n const o = obj as Record<string, unknown>;\n\n if (typeof o.rootCause !== 'string') return false;\n if (typeof o.explanation !== 'string') return false;\n if (typeof o.confidence !== 'number' || o.confidence < 0 || o.confidence > 1) return false;\n if (!Array.isArray(o.prevention)) return false;\n\n if (o.fix !== null && o.fix !== undefined) {\n const fix = o.fix as Record<string, unknown>;\n if (typeof fix.description !== 'string') return false;\n if (typeof fix.code !== 'string') return false;\n if (typeof fix.filename !== 'string') return false;\n }\n\n if (!Array.isArray(o.alternativeCauses)) return false;\n\n return true;\n}\n\nexport function parseAIResponse(raw: string): AIResponse | null {\n try {\n const jsonStr = extractJsonFromMarkdown(raw);\n const parsed = JSON.parse(jsonStr);\n\n if (!isValidAIResponse(parsed)) return null;\n\n return {\n rootCause: parsed.rootCause,\n explanation: parsed.explanation,\n fix: parsed.fix ?? { description: '', code: '', filename: '' },\n prevention: parsed.prevention,\n confidence: Math.min(1, Math.max(0, parsed.confidence)),\n alternativeCauses: parsed.alternativeCauses ?? [],\n };\n } catch {\n return null;\n }\n}\n","import type { AIConfig, AIResponse, CrashEvent } from '@crashsense/types';\nimport { buildAIPayload } from './payload-builder';\nimport { buildMessages } from './prompt';\nimport { parseAIResponse } from './response-parser';\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function createAIClient(config: AIConfig) {\n const timeout = config.timeout ?? 30000;\n const maxRetries = config.retries ?? 2;\n\n async function sendRequest(event: CrashEvent): Promise<string | null> {\n const payload = buildAIPayload(event);\n const messages = buildMessages(payload);\n\n const body: Record<string, unknown> = {\n messages,\n temperature: config.temperature ?? 0.1,\n max_tokens: config.maxTokens ?? 2000,\n };\n\n if (config.model) {\n body.model = config.model;\n }\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(config.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n if (attempt < maxRetries) {\n await sleep(1000 * (attempt + 1));\n continue;\n }\n return null;\n }\n\n const data = await response.json() as {\n choices?: Array<{ message?: { content?: string } }>;\n };\n\n return data.choices?.[0]?.message?.content ?? null;\n } catch {\n clearTimeout(timeoutId);\n if (attempt < maxRetries) {\n await sleep(1000 * (attempt + 1));\n continue;\n }\n return null;\n }\n }\n\n return null;\n }\n\n return {\n async analyze(event: CrashEvent): Promise<AIResponse | null> {\n const rawResponse = await sendRequest(event);\n if (!rawResponse) return null;\n return parseAIResponse(rawResponse);\n },\n };\n}\n","import type { ClassificationResult, AIResponse, CrashAnalysis } from '@crashsense/types';\n\nfunction generateHeuristicExplanation(classification: ClassificationResult): string {\n const { category, subcategory, confidence } = classification;\n return `Heuristic analysis identified this as a ${category} issue (${subcategory}) ` +\n `with ${(confidence * 100).toFixed(0)}% confidence. ` +\n classification.contributingFactors\n .map((f) => f.evidence)\n .join(' ');\n}\n\nexport function mergeAnalysis(\n heuristic: ClassificationResult,\n aiResponse: AIResponse | null,\n): CrashAnalysis {\n if (!aiResponse) {\n return {\n rootCause: `${heuristic.category}: ${heuristic.subcategory}`,\n explanation: generateHeuristicExplanation(heuristic),\n fix: null,\n prevention: [],\n confidence: heuristic.confidence,\n alternativeCauses: [],\n source: 'heuristic',\n };\n }\n\n if (aiResponse.confidence > 0.8) {\n return {\n rootCause: aiResponse.rootCause,\n explanation: aiResponse.explanation,\n fix: aiResponse.fix,\n prevention: aiResponse.prevention,\n confidence: heuristic.confidence * 0.4 + aiResponse.confidence * 0.6,\n alternativeCauses: aiResponse.alternativeCauses,\n source: 'ai',\n };\n }\n\n return {\n rootCause: aiResponse.rootCause || `${heuristic.category}: ${heuristic.subcategory}`,\n explanation: aiResponse.explanation || generateHeuristicExplanation(heuristic),\n fix: aiResponse.fix,\n prevention: aiResponse.prevention,\n confidence: heuristic.confidence * 0.4 + aiResponse.confidence * 0.6,\n alternativeCauses: aiResponse.alternativeCauses,\n source: 'hybrid',\n };\n}\n"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
// src/payload-builder.ts
|
|
2
|
+
function truncate(str, maxLen) {
|
|
3
|
+
return str.length > maxLen ? str.slice(0, maxLen) + "..." : str;
|
|
4
|
+
}
|
|
5
|
+
function formatRelativeTime(eventTimestamp, referenceTimestamp) {
|
|
6
|
+
const delta = (eventTimestamp - referenceTimestamp) / 1e3;
|
|
7
|
+
if (delta >= 0) return "0s";
|
|
8
|
+
return `${delta.toFixed(1)}s`;
|
|
9
|
+
}
|
|
10
|
+
function buildAIPayload(event) {
|
|
11
|
+
const stackTop5 = event.error.stack.slice(0, 5).map(
|
|
12
|
+
(f) => `at ${f.function} (${f.filename}:${f.lineno}:${f.colno})`
|
|
13
|
+
);
|
|
14
|
+
const userCodeFrames = event.error.stack.filter((f) => f.inApp).slice(0, 5).map((f) => `at ${f.function} (${f.filename}:${f.lineno}:${f.colno})`);
|
|
15
|
+
const memUtil = event.system.memory.utilizationPercent;
|
|
16
|
+
const memUtilStr = memUtil !== null ? `${memUtil}%` : "N/A";
|
|
17
|
+
const last5Breadcrumbs = event.breadcrumbs.slice(-5).map((bc) => ({
|
|
18
|
+
type: bc.type,
|
|
19
|
+
message: truncate(bc.message, 100),
|
|
20
|
+
time: formatRelativeTime(bc.timestamp, event.timestamp)
|
|
21
|
+
}));
|
|
22
|
+
return {
|
|
23
|
+
crash_summary: {
|
|
24
|
+
category: event.category,
|
|
25
|
+
subcategory: event.subcategory,
|
|
26
|
+
heuristic_confidence: event.confidence,
|
|
27
|
+
severity: event.severity
|
|
28
|
+
},
|
|
29
|
+
error: {
|
|
30
|
+
type: event.error.type,
|
|
31
|
+
message: truncate(event.error.message, 500),
|
|
32
|
+
stack_top_5: stackTop5,
|
|
33
|
+
user_code_frames: userCodeFrames
|
|
34
|
+
},
|
|
35
|
+
system_state: {
|
|
36
|
+
memory_utilization: memUtilStr,
|
|
37
|
+
memory_trend: event.system.memory.trend,
|
|
38
|
+
long_tasks_last_30s: event.system.cpu.longTasksLast30s,
|
|
39
|
+
fps_at_crash: event.system.eventLoop.fps,
|
|
40
|
+
pending_network_requests: event.system.network.pendingRequests,
|
|
41
|
+
failed_requests_last_60s: event.system.network.failedRequestsLast60s
|
|
42
|
+
},
|
|
43
|
+
device: {
|
|
44
|
+
platform: truncate(event.device.userAgent, 80),
|
|
45
|
+
memory: event.device.deviceMemory !== null ? `${event.device.deviceMemory}GB` : "N/A",
|
|
46
|
+
viewport: `${event.device.viewport.width}x${event.device.viewport.height}`,
|
|
47
|
+
connection: event.system.network.connectionType ?? "unknown"
|
|
48
|
+
},
|
|
49
|
+
framework: {
|
|
50
|
+
name: event.framework.name,
|
|
51
|
+
version: event.framework.version,
|
|
52
|
+
lifecycle_stage: event.framework.lifecycleStage ?? "unknown",
|
|
53
|
+
component_path: event.framework.componentTree ?? [],
|
|
54
|
+
render_count_since_nav: event.framework.renderCount ?? 0
|
|
55
|
+
},
|
|
56
|
+
breadcrumbs_last_5: last5Breadcrumbs,
|
|
57
|
+
contributing_factors: event.contributingFactors.map((f) => ({
|
|
58
|
+
factor: f.factor,
|
|
59
|
+
weight: f.weight,
|
|
60
|
+
evidence: truncate(f.evidence, 150)
|
|
61
|
+
}))
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/prompt.ts
|
|
66
|
+
var SYSTEM_PROMPT = `You are CrashSense AI, a specialized web application crash diagnostician. You analyze structured crash reports from JavaScript web applications (React, Vue, vanilla JS) running in browsers and mobile WebViews.
|
|
67
|
+
|
|
68
|
+
Your task: Given a crash report, provide:
|
|
69
|
+
1. ROOT CAUSE: The specific technical root cause (1-2 sentences)
|
|
70
|
+
2. EXPLANATION: Why this happened, including the chain of events (3-5 sentences)
|
|
71
|
+
3. FIX: Working code example that fixes the issue
|
|
72
|
+
4. PREVENTION: How to prevent this class of bug in the future (2-3 bullet points)
|
|
73
|
+
5. CONFIDENCE: Your confidence in this diagnosis (0.0-1.0)
|
|
74
|
+
|
|
75
|
+
Rules:
|
|
76
|
+
- Be specific. "Check your code" is not acceptable. Name the exact issue.
|
|
77
|
+
- Fix code must be syntactically correct and production-ready.
|
|
78
|
+
- If you are not confident (< 0.6), say so and list the top 2-3 possible causes.
|
|
79
|
+
- Reference the specific component, file, or line from the stack trace.
|
|
80
|
+
- Consider the system state (memory, CPU, network) as contributing factors.
|
|
81
|
+
- Consider the device context (mobile, low memory, slow network).
|
|
82
|
+
|
|
83
|
+
Output ONLY valid JSON matching this schema:
|
|
84
|
+
{
|
|
85
|
+
"rootCause": "string",
|
|
86
|
+
"explanation": "string",
|
|
87
|
+
"fix": {
|
|
88
|
+
"description": "string",
|
|
89
|
+
"code": "string",
|
|
90
|
+
"filename": "string"
|
|
91
|
+
},
|
|
92
|
+
"prevention": ["string"],
|
|
93
|
+
"confidence": number,
|
|
94
|
+
"alternativeCauses": [{"cause": "string", "likelihood": number}]
|
|
95
|
+
}`;
|
|
96
|
+
function buildMessages(payload) {
|
|
97
|
+
return [
|
|
98
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
99
|
+
{ role: "user", content: JSON.stringify(payload, null, 2) }
|
|
100
|
+
];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/response-parser.ts
|
|
104
|
+
function extractJsonFromMarkdown(raw) {
|
|
105
|
+
const codeBlockMatch = raw.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
|
|
106
|
+
if (codeBlockMatch) return codeBlockMatch[1].trim();
|
|
107
|
+
return raw.trim();
|
|
108
|
+
}
|
|
109
|
+
function isValidAIResponse(obj) {
|
|
110
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
111
|
+
const o = obj;
|
|
112
|
+
if (typeof o.rootCause !== "string") return false;
|
|
113
|
+
if (typeof o.explanation !== "string") return false;
|
|
114
|
+
if (typeof o.confidence !== "number" || o.confidence < 0 || o.confidence > 1) return false;
|
|
115
|
+
if (!Array.isArray(o.prevention)) return false;
|
|
116
|
+
if (o.fix !== null && o.fix !== void 0) {
|
|
117
|
+
const fix = o.fix;
|
|
118
|
+
if (typeof fix.description !== "string") return false;
|
|
119
|
+
if (typeof fix.code !== "string") return false;
|
|
120
|
+
if (typeof fix.filename !== "string") return false;
|
|
121
|
+
}
|
|
122
|
+
if (!Array.isArray(o.alternativeCauses)) return false;
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
function parseAIResponse(raw) {
|
|
126
|
+
try {
|
|
127
|
+
const jsonStr = extractJsonFromMarkdown(raw);
|
|
128
|
+
const parsed = JSON.parse(jsonStr);
|
|
129
|
+
if (!isValidAIResponse(parsed)) return null;
|
|
130
|
+
return {
|
|
131
|
+
rootCause: parsed.rootCause,
|
|
132
|
+
explanation: parsed.explanation,
|
|
133
|
+
fix: parsed.fix ?? { description: "", code: "", filename: "" },
|
|
134
|
+
prevention: parsed.prevention,
|
|
135
|
+
confidence: Math.min(1, Math.max(0, parsed.confidence)),
|
|
136
|
+
alternativeCauses: parsed.alternativeCauses ?? []
|
|
137
|
+
};
|
|
138
|
+
} catch {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/client.ts
|
|
144
|
+
function sleep(ms) {
|
|
145
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
146
|
+
}
|
|
147
|
+
function createAIClient(config) {
|
|
148
|
+
const timeout = config.timeout ?? 3e4;
|
|
149
|
+
const maxRetries = config.retries ?? 2;
|
|
150
|
+
async function sendRequest(event) {
|
|
151
|
+
const payload = buildAIPayload(event);
|
|
152
|
+
const messages = buildMessages(payload);
|
|
153
|
+
const body = {
|
|
154
|
+
messages,
|
|
155
|
+
temperature: config.temperature ?? 0.1,
|
|
156
|
+
max_tokens: config.maxTokens ?? 2e3
|
|
157
|
+
};
|
|
158
|
+
if (config.model) {
|
|
159
|
+
body.model = config.model;
|
|
160
|
+
}
|
|
161
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
162
|
+
const controller = new AbortController();
|
|
163
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
164
|
+
try {
|
|
165
|
+
const response = await fetch(config.endpoint, {
|
|
166
|
+
method: "POST",
|
|
167
|
+
headers: {
|
|
168
|
+
"Content-Type": "application/json",
|
|
169
|
+
"Authorization": `Bearer ${config.apiKey}`
|
|
170
|
+
},
|
|
171
|
+
body: JSON.stringify(body),
|
|
172
|
+
signal: controller.signal
|
|
173
|
+
});
|
|
174
|
+
clearTimeout(timeoutId);
|
|
175
|
+
if (!response.ok) {
|
|
176
|
+
if (attempt < maxRetries) {
|
|
177
|
+
await sleep(1e3 * (attempt + 1));
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
const data = await response.json();
|
|
183
|
+
return data.choices?.[0]?.message?.content ?? null;
|
|
184
|
+
} catch {
|
|
185
|
+
clearTimeout(timeoutId);
|
|
186
|
+
if (attempt < maxRetries) {
|
|
187
|
+
await sleep(1e3 * (attempt + 1));
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
async analyze(event) {
|
|
197
|
+
const rawResponse = await sendRequest(event);
|
|
198
|
+
if (!rawResponse) return null;
|
|
199
|
+
return parseAIResponse(rawResponse);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// src/hybrid-merger.ts
|
|
205
|
+
function generateHeuristicExplanation(classification) {
|
|
206
|
+
const { category, subcategory, confidence } = classification;
|
|
207
|
+
return `Heuristic analysis identified this as a ${category} issue (${subcategory}) with ${(confidence * 100).toFixed(0)}% confidence. ` + classification.contributingFactors.map((f) => f.evidence).join(" ");
|
|
208
|
+
}
|
|
209
|
+
function mergeAnalysis(heuristic, aiResponse) {
|
|
210
|
+
if (!aiResponse) {
|
|
211
|
+
return {
|
|
212
|
+
rootCause: `${heuristic.category}: ${heuristic.subcategory}`,
|
|
213
|
+
explanation: generateHeuristicExplanation(heuristic),
|
|
214
|
+
fix: null,
|
|
215
|
+
prevention: [],
|
|
216
|
+
confidence: heuristic.confidence,
|
|
217
|
+
alternativeCauses: [],
|
|
218
|
+
source: "heuristic"
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
if (aiResponse.confidence > 0.8) {
|
|
222
|
+
return {
|
|
223
|
+
rootCause: aiResponse.rootCause,
|
|
224
|
+
explanation: aiResponse.explanation,
|
|
225
|
+
fix: aiResponse.fix,
|
|
226
|
+
prevention: aiResponse.prevention,
|
|
227
|
+
confidence: heuristic.confidence * 0.4 + aiResponse.confidence * 0.6,
|
|
228
|
+
alternativeCauses: aiResponse.alternativeCauses,
|
|
229
|
+
source: "ai"
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
rootCause: aiResponse.rootCause || `${heuristic.category}: ${heuristic.subcategory}`,
|
|
234
|
+
explanation: aiResponse.explanation || generateHeuristicExplanation(heuristic),
|
|
235
|
+
fix: aiResponse.fix,
|
|
236
|
+
prevention: aiResponse.prevention,
|
|
237
|
+
confidence: heuristic.confidence * 0.4 + aiResponse.confidence * 0.6,
|
|
238
|
+
alternativeCauses: aiResponse.alternativeCauses,
|
|
239
|
+
source: "hybrid"
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export { SYSTEM_PROMPT, buildAIPayload, createAIClient, mergeAnalysis, parseAIResponse };
|
|
244
|
+
//# sourceMappingURL=index.mjs.map
|
|
245
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/payload-builder.ts","../src/prompt.ts","../src/response-parser.ts","../src/client.ts","../src/hybrid-merger.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type { CrashEvent, AIPayload } from '@crashsense/types';\n\nfunction truncate(str: string, maxLen: number): string {\n return str.length > maxLen ? str.slice(0, maxLen) + '...' : str;\n}\n\nfunction formatRelativeTime(eventTimestamp: number, referenceTimestamp: number): string {\n const delta = (eventTimestamp - referenceTimestamp) / 1000;\n if (delta >= 0) return '0s';\n return `${delta.toFixed(1)}s`;\n}\n\nexport function buildAIPayload(event: CrashEvent): AIPayload {\n const stackTop5 = event.error.stack.slice(0, 5).map(\n (f) => `at ${f.function} (${f.filename}:${f.lineno}:${f.colno})`,\n );\n\n const userCodeFrames = event.error.stack\n .filter((f) => f.inApp)\n .slice(0, 5)\n .map((f) => `at ${f.function} (${f.filename}:${f.lineno}:${f.colno})`);\n\n const memUtil = event.system.memory.utilizationPercent;\n const memUtilStr = memUtil !== null ? `${memUtil}%` : 'N/A';\n\n const last5Breadcrumbs = event.breadcrumbs.slice(-5).map((bc) => ({\n type: bc.type,\n message: truncate(bc.message, 100),\n time: formatRelativeTime(bc.timestamp, event.timestamp),\n }));\n\n return {\n crash_summary: {\n category: event.category,\n subcategory: event.subcategory,\n heuristic_confidence: event.confidence,\n severity: event.severity,\n },\n error: {\n type: event.error.type,\n message: truncate(event.error.message, 500),\n stack_top_5: stackTop5,\n user_code_frames: userCodeFrames,\n },\n system_state: {\n memory_utilization: memUtilStr,\n memory_trend: event.system.memory.trend,\n long_tasks_last_30s: event.system.cpu.longTasksLast30s,\n fps_at_crash: event.system.eventLoop.fps,\n pending_network_requests: event.system.network.pendingRequests,\n failed_requests_last_60s: event.system.network.failedRequestsLast60s,\n },\n device: {\n platform: truncate(event.device.userAgent, 80),\n memory: event.device.deviceMemory !== null ? `${event.device.deviceMemory}GB` : 'N/A',\n viewport: `${event.device.viewport.width}x${event.device.viewport.height}`,\n connection: event.system.network.connectionType ?? 'unknown',\n },\n framework: {\n name: event.framework.name,\n version: event.framework.version,\n lifecycle_stage: event.framework.lifecycleStage ?? 'unknown',\n component_path: event.framework.componentTree ?? [],\n render_count_since_nav: event.framework.renderCount ?? 0,\n },\n breadcrumbs_last_5: last5Breadcrumbs,\n contributing_factors: event.contributingFactors.map((f) => ({\n factor: f.factor,\n weight: f.weight,\n evidence: truncate(f.evidence, 150),\n })),\n };\n}\n","import type { AIPayload } from '@crashsense/types';\n\nexport const SYSTEM_PROMPT = `You are CrashSense AI, a specialized web application crash diagnostician. You analyze structured crash reports from JavaScript web applications (React, Vue, vanilla JS) running in browsers and mobile WebViews.\n\nYour task: Given a crash report, provide:\n1. ROOT CAUSE: The specific technical root cause (1-2 sentences)\n2. EXPLANATION: Why this happened, including the chain of events (3-5 sentences)\n3. FIX: Working code example that fixes the issue\n4. PREVENTION: How to prevent this class of bug in the future (2-3 bullet points)\n5. CONFIDENCE: Your confidence in this diagnosis (0.0-1.0)\n\nRules:\n- Be specific. \"Check your code\" is not acceptable. Name the exact issue.\n- Fix code must be syntactically correct and production-ready.\n- If you are not confident (< 0.6), say so and list the top 2-3 possible causes.\n- Reference the specific component, file, or line from the stack trace.\n- Consider the system state (memory, CPU, network) as contributing factors.\n- Consider the device context (mobile, low memory, slow network).\n\nOutput ONLY valid JSON matching this schema:\n{\n \"rootCause\": \"string\",\n \"explanation\": \"string\",\n \"fix\": {\n \"description\": \"string\",\n \"code\": \"string\",\n \"filename\": \"string\"\n },\n \"prevention\": [\"string\"],\n \"confidence\": number,\n \"alternativeCauses\": [{\"cause\": \"string\", \"likelihood\": number}]\n}`;\n\nexport function buildMessages(\n payload: AIPayload,\n): Array<{ role: string; content: string }> {\n return [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: JSON.stringify(payload, null, 2) },\n ];\n}\n","import type { AIResponse } from '@crashsense/types';\n\nfunction extractJsonFromMarkdown(raw: string): string {\n const codeBlockMatch = raw.match(/```(?:json)?\\s*\\n?([\\s\\S]*?)\\n?```/);\n if (codeBlockMatch) return codeBlockMatch[1].trim();\n return raw.trim();\n}\n\nfunction isValidAIResponse(obj: unknown): obj is AIResponse {\n if (typeof obj !== 'object' || obj === null) return false;\n const o = obj as Record<string, unknown>;\n\n if (typeof o.rootCause !== 'string') return false;\n if (typeof o.explanation !== 'string') return false;\n if (typeof o.confidence !== 'number' || o.confidence < 0 || o.confidence > 1) return false;\n if (!Array.isArray(o.prevention)) return false;\n\n if (o.fix !== null && o.fix !== undefined) {\n const fix = o.fix as Record<string, unknown>;\n if (typeof fix.description !== 'string') return false;\n if (typeof fix.code !== 'string') return false;\n if (typeof fix.filename !== 'string') return false;\n }\n\n if (!Array.isArray(o.alternativeCauses)) return false;\n\n return true;\n}\n\nexport function parseAIResponse(raw: string): AIResponse | null {\n try {\n const jsonStr = extractJsonFromMarkdown(raw);\n const parsed = JSON.parse(jsonStr);\n\n if (!isValidAIResponse(parsed)) return null;\n\n return {\n rootCause: parsed.rootCause,\n explanation: parsed.explanation,\n fix: parsed.fix ?? { description: '', code: '', filename: '' },\n prevention: parsed.prevention,\n confidence: Math.min(1, Math.max(0, parsed.confidence)),\n alternativeCauses: parsed.alternativeCauses ?? [],\n };\n } catch {\n return null;\n }\n}\n","import type { AIConfig, AIResponse, CrashEvent } from '@crashsense/types';\nimport { buildAIPayload } from './payload-builder';\nimport { buildMessages } from './prompt';\nimport { parseAIResponse } from './response-parser';\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function createAIClient(config: AIConfig) {\n const timeout = config.timeout ?? 30000;\n const maxRetries = config.retries ?? 2;\n\n async function sendRequest(event: CrashEvent): Promise<string | null> {\n const payload = buildAIPayload(event);\n const messages = buildMessages(payload);\n\n const body: Record<string, unknown> = {\n messages,\n temperature: config.temperature ?? 0.1,\n max_tokens: config.maxTokens ?? 2000,\n };\n\n if (config.model) {\n body.model = config.model;\n }\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(config.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n if (attempt < maxRetries) {\n await sleep(1000 * (attempt + 1));\n continue;\n }\n return null;\n }\n\n const data = await response.json() as {\n choices?: Array<{ message?: { content?: string } }>;\n };\n\n return data.choices?.[0]?.message?.content ?? null;\n } catch {\n clearTimeout(timeoutId);\n if (attempt < maxRetries) {\n await sleep(1000 * (attempt + 1));\n continue;\n }\n return null;\n }\n }\n\n return null;\n }\n\n return {\n async analyze(event: CrashEvent): Promise<AIResponse | null> {\n const rawResponse = await sendRequest(event);\n if (!rawResponse) return null;\n return parseAIResponse(rawResponse);\n },\n };\n}\n","import type { ClassificationResult, AIResponse, CrashAnalysis } from '@crashsense/types';\n\nfunction generateHeuristicExplanation(classification: ClassificationResult): string {\n const { category, subcategory, confidence } = classification;\n return `Heuristic analysis identified this as a ${category} issue (${subcategory}) ` +\n `with ${(confidence * 100).toFixed(0)}% confidence. ` +\n classification.contributingFactors\n .map((f) => f.evidence)\n .join(' ');\n}\n\nexport function mergeAnalysis(\n heuristic: ClassificationResult,\n aiResponse: AIResponse | null,\n): CrashAnalysis {\n if (!aiResponse) {\n return {\n rootCause: `${heuristic.category}: ${heuristic.subcategory}`,\n explanation: generateHeuristicExplanation(heuristic),\n fix: null,\n prevention: [],\n confidence: heuristic.confidence,\n alternativeCauses: [],\n source: 'heuristic',\n };\n }\n\n if (aiResponse.confidence > 0.8) {\n return {\n rootCause: aiResponse.rootCause,\n explanation: aiResponse.explanation,\n fix: aiResponse.fix,\n prevention: aiResponse.prevention,\n confidence: heuristic.confidence * 0.4 + aiResponse.confidence * 0.6,\n alternativeCauses: aiResponse.alternativeCauses,\n source: 'ai',\n };\n }\n\n return {\n rootCause: aiResponse.rootCause || `${heuristic.category}: ${heuristic.subcategory}`,\n explanation: aiResponse.explanation || generateHeuristicExplanation(heuristic),\n fix: aiResponse.fix,\n prevention: aiResponse.prevention,\n confidence: heuristic.confidence * 0.4 + aiResponse.confidence * 0.6,\n alternativeCauses: aiResponse.alternativeCauses,\n source: 'hybrid',\n };\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@crashsense/ai",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CrashSense AI integration — LLM client, crash payload builder, response parser",
|
|
5
|
+
"author": "hoainho <nhoxtvt@gmail.com>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/hoainho/crashsense.git",
|
|
10
|
+
"directory": "packages/ai"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/hoainho/crashsense/tree/main/packages/ai",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/hoainho/crashsense/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"crashsense",
|
|
18
|
+
"ai",
|
|
19
|
+
"llm",
|
|
20
|
+
"crash-analysis",
|
|
21
|
+
"root-cause-analysis",
|
|
22
|
+
"openai",
|
|
23
|
+
"anthropic"
|
|
24
|
+
],
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"main": "./dist/index.js",
|
|
29
|
+
"module": "./dist/index.mjs",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"import": "./dist/index.mjs",
|
|
35
|
+
"require": "./dist/index.js"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist"
|
|
40
|
+
],
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsup",
|
|
43
|
+
"dev": "tsup --watch",
|
|
44
|
+
"test": "vitest run"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@crashsense/types": "*"
|
|
48
|
+
},
|
|
49
|
+
"sideEffects": false
|
|
50
|
+
}
|