flakewatch-core 0.1.2 → 0.2.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/config.d.ts +9 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +53 -28
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/llm.d.ts +1 -0
- package/dist/llm.d.ts.map +1 -1
- package/dist/llm.js +111 -37
- package/dist/llm.js.map +1 -1
- package/dist/triage.d.ts +12 -3
- package/dist/triage.d.ts.map +1 -1
- package/dist/triage.js +29 -10
- package/dist/triage.js.map +1 -1
- package/package.json +1 -1
package/dist/config.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
export type LLMProviderType = 'anthropic' | 'openai';
|
|
1
2
|
export interface LLMConfig {
|
|
2
|
-
provider:
|
|
3
|
+
provider: LLMProviderType;
|
|
3
4
|
model: string;
|
|
4
5
|
apiKey?: string;
|
|
6
|
+
/** Base URL override (useful for OpenAI-compatible APIs like Azure, Ollama, etc.) */
|
|
7
|
+
baseURL?: string;
|
|
5
8
|
}
|
|
6
9
|
export interface BrowserConfig {
|
|
7
10
|
method: 'mcp';
|
|
@@ -28,7 +31,7 @@ export interface OutputConfig {
|
|
|
28
31
|
format: 'markdown' | 'json' | 'github-comment';
|
|
29
32
|
dir: string;
|
|
30
33
|
}
|
|
31
|
-
export interface
|
|
34
|
+
export interface FlakewatchConfig {
|
|
32
35
|
llm: LLMConfig;
|
|
33
36
|
browser: BrowserConfig;
|
|
34
37
|
investigation: InvestigationConfig;
|
|
@@ -36,6 +39,8 @@ export interface SmartRetryConfig {
|
|
|
36
39
|
context: ContextConfig;
|
|
37
40
|
output: OutputConfig;
|
|
38
41
|
}
|
|
39
|
-
|
|
40
|
-
export
|
|
42
|
+
/** @deprecated Use FlakewatchConfig instead */
|
|
43
|
+
export type SmartRetryConfig = FlakewatchConfig;
|
|
44
|
+
export declare function loadConfig(configPath?: string, overrides?: Partial<FlakewatchConfig>): Promise<FlakewatchConfig>;
|
|
45
|
+
export declare function validateConfig(config: FlakewatchConfig): string[];
|
|
41
46
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,QAAQ,CAAC;AAErD,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,eAAe,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,KAAK,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG,MAAM,CAAC;AAEtD,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,iBAAiB,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;IACxB,cAAc,EAAE,OAAO,CAAC;IACxB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,gBAAgB,CAAC;IAC/C,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,SAAS,CAAC;IACf,OAAO,EAAE,aAAa,CAAC;IACvB,aAAa,EAAE,mBAAmB,CAAC;IACnC,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,+CAA+C;AAC/C,MAAM,MAAM,gBAAgB,GAAG,gBAAgB,CAAC;AAoDhD,wBAAsB,UAAU,CAC9B,UAAU,CAAC,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GACpC,OAAO,CAAC,gBAAgB,CAAC,CA6B3B;AA+BD,wBAAgB,cAAc,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,EAAE,CA0BjE"}
|
package/dist/config.js
CHANGED
|
@@ -1,32 +1,52 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
function detectProvider() {
|
|
3
|
+
if (process.env['ANTHROPIC_API_KEY']) {
|
|
4
|
+
return {
|
|
5
|
+
provider: 'anthropic',
|
|
6
|
+
model: 'claude-sonnet-4-20250514',
|
|
7
|
+
apiKey: process.env['ANTHROPIC_API_KEY'],
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
if (process.env['OPENAI_API_KEY']) {
|
|
11
|
+
return {
|
|
12
|
+
provider: 'openai',
|
|
13
|
+
model: 'gpt-4o',
|
|
14
|
+
apiKey: process.env['OPENAI_API_KEY'],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
4
18
|
provider: 'anthropic',
|
|
5
19
|
model: 'claude-sonnet-4-20250514',
|
|
6
|
-
apiKey:
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
20
|
+
apiKey: undefined,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function buildDefaultConfig() {
|
|
24
|
+
const llm = detectProvider();
|
|
25
|
+
return {
|
|
26
|
+
llm,
|
|
27
|
+
browser: {
|
|
28
|
+
method: 'mcp',
|
|
29
|
+
mcpServerCommand: 'npx chrome-devtools-mcp --headless --isolated',
|
|
30
|
+
},
|
|
31
|
+
investigation: {
|
|
32
|
+
mode: 'screenshot',
|
|
33
|
+
maxConcurrent: 3,
|
|
34
|
+
maxTotal: 10,
|
|
35
|
+
massFailureThreshold: 0.5,
|
|
36
|
+
},
|
|
37
|
+
auth: {},
|
|
38
|
+
context: {
|
|
39
|
+
includeTrace: true,
|
|
40
|
+
includeConsole: true,
|
|
41
|
+
includeNetwork: true,
|
|
42
|
+
maxTestSourceTokens: 6000,
|
|
43
|
+
},
|
|
44
|
+
output: {
|
|
45
|
+
format: 'markdown',
|
|
46
|
+
dir: './flakewatch-reports',
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
30
50
|
export async function loadConfig(configPath, overrides) {
|
|
31
51
|
let fileConfig = {};
|
|
32
52
|
if (configPath) {
|
|
@@ -52,7 +72,7 @@ export async function loadConfig(configPath, overrides) {
|
|
|
52
72
|
}
|
|
53
73
|
}
|
|
54
74
|
}
|
|
55
|
-
return deepMerge(
|
|
75
|
+
return deepMerge(buildDefaultConfig(), fileConfig, (overrides ?? {}));
|
|
56
76
|
}
|
|
57
77
|
function deepMerge(...sources) {
|
|
58
78
|
const result = {};
|
|
@@ -71,10 +91,15 @@ function deepMerge(...sources) {
|
|
|
71
91
|
}
|
|
72
92
|
return result;
|
|
73
93
|
}
|
|
94
|
+
const ENV_KEY_NAMES = {
|
|
95
|
+
anthropic: 'ANTHROPIC_API_KEY',
|
|
96
|
+
openai: 'OPENAI_API_KEY',
|
|
97
|
+
};
|
|
74
98
|
export function validateConfig(config) {
|
|
75
99
|
const errors = [];
|
|
76
100
|
if (!config.llm.apiKey) {
|
|
77
|
-
|
|
101
|
+
const envVar = ENV_KEY_NAMES[config.llm.provider] ?? 'ANTHROPIC_API_KEY';
|
|
102
|
+
errors.push(`Missing API key for ${config.llm.provider}. Set the ${envVar} environment variable or configure llm.apiKey in flakewatch.config.ts.`);
|
|
78
103
|
}
|
|
79
104
|
if (config.investigation.maxConcurrent < 1) {
|
|
80
105
|
errors.push('investigation.maxConcurrent must be >= 1');
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
export { loadConfig, validateConfig } from './config.js';
|
|
2
|
-
export type { SmartRetryConfig, LLMConfig, BrowserConfig, InvestigationConfig, InvestigationMode, AuthConfig, ContextConfig, OutputConfig } from './config.js';
|
|
2
|
+
export type { FlakewatchConfig, SmartRetryConfig, LLMConfig, LLMProviderType, BrowserConfig, InvestigationConfig, InvestigationMode, AuthConfig, ContextConfig, OutputConfig } from './config.js';
|
|
3
3
|
export { packageFailureContext, formatContextForLLM } from './context.js';
|
|
4
4
|
export type { TestFailure, FailureContext, FailingLine, TraceAction, NetworkRequest } from './context.js';
|
|
5
5
|
export { groupFailures, selectGroupsForInvestigation } from './grouping.js';
|
|
6
6
|
export type { FailureGroup } from './grouping.js';
|
|
7
|
-
export { createLLMProvider } from './llm.js';
|
|
7
|
+
export { createLLMProvider, parseVerdictResponse } from './llm.js';
|
|
8
8
|
export type { LLMProvider, LLMAnalysisResult } from './llm.js';
|
|
9
9
|
export type { Verdict, VerdictType, VerdictEvidence, ProposedFix, ProposedComment, InvestigationResult, TriageReport, FailureGroupSummary } from './verdicts.js';
|
|
10
10
|
export { formatMarkdownReport, formatGitHubComment, formatJsonReport, writeReport } from './report.js';
|
|
11
11
|
export { parsePlaywrightJsonReport } from './playwright-json.js';
|
|
12
12
|
export { triageFailures } from './triage.js';
|
|
13
|
-
export type { TriageOptions, BrowserInvestigatorFn } from './triage.js';
|
|
13
|
+
export type { TriageOptions, BrowserInvestigatorFn, TriageProgress } from './triage.js';
|
|
14
14
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACzD,YAAY,EAAE,gBAAgB,EAAE,SAAS,EAAE,aAAa,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACzD,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,SAAS,EAAE,eAAe,EAAE,aAAa,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAElM,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAC1E,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE1G,OAAO,EAAE,aAAa,EAAE,4BAA4B,EAAE,MAAM,eAAe,CAAC;AAC5E,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AACnE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE/D,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,mBAAmB,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEjK,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEvG,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAEjE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { loadConfig, validateConfig } from './config.js';
|
|
2
2
|
export { packageFailureContext, formatContextForLLM } from './context.js';
|
|
3
3
|
export { groupFailures, selectGroupsForInvestigation } from './grouping.js';
|
|
4
|
-
export { createLLMProvider } from './llm.js';
|
|
4
|
+
export { createLLMProvider, parseVerdictResponse } from './llm.js';
|
|
5
5
|
export { formatMarkdownReport, formatGitHubComment, formatJsonReport, writeReport } from './report.js';
|
|
6
6
|
export { parsePlaywrightJsonReport } from './playwright-json.js';
|
|
7
7
|
export { triageFailures } from './triage.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGzD,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAG1E,OAAO,EAAE,aAAa,EAAE,4BAA4B,EAAE,MAAM,eAAe,CAAC;AAG5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGzD,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAG1E,OAAO,EAAE,aAAa,EAAE,4BAA4B,EAAE,MAAM,eAAe,CAAC;AAG5E,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAKnE,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEvG,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAEjE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/llm.d.ts
CHANGED
package/dist/llm.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../src/llm.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../src/llm.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,OAAO,EAAe,MAAM,eAAe,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAGnD,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,OAAO,EAAE,cAAc,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACzF;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AA8CD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,WAAW,CAWhE;AAyMD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CA0B1D"}
|
package/dist/llm.js
CHANGED
|
@@ -28,38 +28,51 @@ IMPORTANT: You MUST respond with ONLY valid JSON and nothing else. No markdown,
|
|
|
28
28
|
"summary": string,
|
|
29
29
|
"evidence": {
|
|
30
30
|
"reasoning": string,
|
|
31
|
-
"networkErrors": [{ "url": string, "status": number, "method": string }] |
|
|
32
|
-
"consoleErrors": [string] |
|
|
31
|
+
"networkErrors": [{ "url": string, "status": number, "method": string }] | null,
|
|
32
|
+
"consoleErrors": [string] | null
|
|
33
33
|
},
|
|
34
34
|
"proposedFix": {
|
|
35
35
|
"filePath": string,
|
|
36
36
|
"originalCode": string,
|
|
37
37
|
"fixedCode": string,
|
|
38
38
|
"explanation": string
|
|
39
|
-
} |
|
|
39
|
+
} | null,
|
|
40
40
|
"proposedComments": [{
|
|
41
41
|
"filePath": string,
|
|
42
42
|
"line": number,
|
|
43
43
|
"comment": string
|
|
44
|
-
}] |
|
|
44
|
+
}] | null
|
|
45
45
|
}`;
|
|
46
46
|
export function createLLMProvider(config) {
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
switch (config.provider) {
|
|
48
|
+
case 'anthropic':
|
|
49
|
+
return new AnthropicProvider(config);
|
|
50
|
+
case 'openai':
|
|
51
|
+
return new OpenAICompatibleProvider(config);
|
|
52
|
+
default:
|
|
53
|
+
throw new Error(`Unsupported LLM provider: "${config.provider}". Supported providers: anthropic, openai`);
|
|
49
54
|
}
|
|
50
|
-
throw new Error(`Unsupported LLM provider: ${config.provider}`);
|
|
51
55
|
}
|
|
52
|
-
|
|
56
|
+
// ── Anthropic Provider ───────────────────────────────────────────────
|
|
57
|
+
async function callWithRetry(callFn, providerName, maxRetries = 3) {
|
|
53
58
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
54
59
|
try {
|
|
55
|
-
return await
|
|
60
|
+
return await callFn();
|
|
56
61
|
}
|
|
57
62
|
catch (e) {
|
|
63
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
58
64
|
const isRateLimit = e instanceof Anthropic.RateLimitError ||
|
|
59
|
-
|
|
65
|
+
msg.includes('rate_limit') ||
|
|
66
|
+
msg.includes('429');
|
|
67
|
+
const isAuth = e instanceof Anthropic.AuthenticationError ||
|
|
68
|
+
msg.includes('401') ||
|
|
69
|
+
msg.includes('invalid') && msg.includes('key');
|
|
70
|
+
if (isAuth) {
|
|
71
|
+
throw new Error(`Authentication failed for ${providerName}. Check that your API key is valid and has sufficient credits.`);
|
|
72
|
+
}
|
|
60
73
|
if (isRateLimit && attempt < maxRetries) {
|
|
61
74
|
const waitMs = Math.min(30_000, (attempt + 1) * 15_000);
|
|
62
|
-
|
|
75
|
+
process.stderr.write(` Rate limited by ${providerName}, retrying in ${waitMs / 1000}s...\n`);
|
|
63
76
|
await new Promise((r) => setTimeout(r, waitMs));
|
|
64
77
|
continue;
|
|
65
78
|
}
|
|
@@ -72,17 +85,18 @@ class AnthropicProvider {
|
|
|
72
85
|
client;
|
|
73
86
|
model;
|
|
74
87
|
constructor(config) {
|
|
75
|
-
this.client = new Anthropic({
|
|
88
|
+
this.client = new Anthropic({
|
|
89
|
+
apiKey: config.apiKey,
|
|
90
|
+
...(config.baseURL ? { baseURL: config.baseURL } : {}),
|
|
91
|
+
});
|
|
76
92
|
this.model = config.model;
|
|
77
93
|
}
|
|
78
94
|
async analyze(context, screenshotBase64) {
|
|
79
95
|
const userContent = [];
|
|
80
|
-
// Add the failure context as text
|
|
81
96
|
userContent.push({
|
|
82
97
|
type: 'text',
|
|
83
98
|
text: formatContextForLLM(context),
|
|
84
99
|
});
|
|
85
|
-
// Add screenshot if available
|
|
86
100
|
if (screenshotBase64) {
|
|
87
101
|
userContent.push({
|
|
88
102
|
type: 'image',
|
|
@@ -101,22 +115,92 @@ class AnthropicProvider {
|
|
|
101
115
|
type: 'text',
|
|
102
116
|
text: 'Analyze this test failure and respond with the JSON verdict.',
|
|
103
117
|
});
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
118
|
+
const result = await callWithRetry(async () => {
|
|
119
|
+
const response = await this.client.messages.create({
|
|
120
|
+
model: this.model,
|
|
121
|
+
max_tokens: 2048,
|
|
122
|
+
system: SYSTEM_PROMPT,
|
|
123
|
+
messages: [{ role: 'user', content: userContent }],
|
|
124
|
+
});
|
|
125
|
+
const textBlock = response.content.find((block) => block.type === 'text');
|
|
126
|
+
return {
|
|
127
|
+
text: textBlock?.text ?? '',
|
|
128
|
+
tokensUsed: (response.usage.input_tokens ?? 0) +
|
|
129
|
+
(response.usage.output_tokens ?? 0),
|
|
130
|
+
};
|
|
131
|
+
}, 'Anthropic');
|
|
132
|
+
if (!result.text) {
|
|
133
|
+
throw new Error('No text response from Anthropic');
|
|
134
|
+
}
|
|
135
|
+
const verdict = parseVerdictResponse(result.text);
|
|
136
|
+
return { verdict, tokensUsed: result.tokensUsed };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// ── OpenAI-Compatible Provider ───────────────────────────────────────
|
|
140
|
+
class OpenAICompatibleProvider {
|
|
141
|
+
apiKey;
|
|
142
|
+
model;
|
|
143
|
+
baseURL;
|
|
144
|
+
constructor(config) {
|
|
145
|
+
this.apiKey = config.apiKey ?? '';
|
|
146
|
+
this.model = config.model;
|
|
147
|
+
this.baseURL = config.baseURL ?? 'https://api.openai.com/v1';
|
|
148
|
+
}
|
|
149
|
+
async analyze(context, screenshotBase64) {
|
|
150
|
+
const userContent = [];
|
|
151
|
+
userContent.push({
|
|
152
|
+
type: 'text',
|
|
153
|
+
text: formatContextForLLM(context),
|
|
109
154
|
});
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
155
|
+
if (screenshotBase64) {
|
|
156
|
+
userContent.push({
|
|
157
|
+
type: 'image_url',
|
|
158
|
+
image_url: { url: `data:image/png;base64,${screenshotBase64}` },
|
|
159
|
+
});
|
|
160
|
+
userContent.push({
|
|
161
|
+
type: 'text',
|
|
162
|
+
text: 'Above is the screenshot captured at the moment of failure.',
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
userContent.push({
|
|
166
|
+
type: 'text',
|
|
167
|
+
text: 'Analyze this test failure and respond with the JSON verdict.',
|
|
168
|
+
});
|
|
169
|
+
const result = await callWithRetry(async () => {
|
|
170
|
+
const response = await fetch(`${this.baseURL}/chat/completions`, {
|
|
171
|
+
method: 'POST',
|
|
172
|
+
headers: {
|
|
173
|
+
'Content-Type': 'application/json',
|
|
174
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
175
|
+
},
|
|
176
|
+
body: JSON.stringify({
|
|
177
|
+
model: this.model,
|
|
178
|
+
max_tokens: 2048,
|
|
179
|
+
messages: [
|
|
180
|
+
{ role: 'system', content: SYSTEM_PROMPT },
|
|
181
|
+
{ role: 'user', content: userContent },
|
|
182
|
+
],
|
|
183
|
+
}),
|
|
184
|
+
});
|
|
185
|
+
if (!response.ok) {
|
|
186
|
+
const body = await response.text();
|
|
187
|
+
throw new Error(`${response.status} ${body}`);
|
|
188
|
+
}
|
|
189
|
+
const data = (await response.json());
|
|
190
|
+
const text = data.choices?.[0]?.message?.content ?? '';
|
|
191
|
+
const tokensUsed = (data.usage?.prompt_tokens ?? 0) +
|
|
192
|
+
(data.usage?.completion_tokens ?? 0);
|
|
193
|
+
return { text, tokensUsed };
|
|
194
|
+
}, 'OpenAI');
|
|
195
|
+
if (!result.text) {
|
|
196
|
+
throw new Error('No text response from OpenAI');
|
|
113
197
|
}
|
|
114
|
-
const verdict = parseVerdictResponse(
|
|
115
|
-
|
|
116
|
-
return { verdict, tokensUsed };
|
|
198
|
+
const verdict = parseVerdictResponse(result.text);
|
|
199
|
+
return { verdict, tokensUsed: result.tokensUsed };
|
|
117
200
|
}
|
|
118
201
|
}
|
|
119
|
-
|
|
202
|
+
// ── Verdict Parsing ──────────────────────────────────────────────────
|
|
203
|
+
export function parseVerdictResponse(text) {
|
|
120
204
|
// Extract JSON from the response (may be wrapped in markdown code block)
|
|
121
205
|
const jsonMatch = text.match(/```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/) ?? [
|
|
122
206
|
null,
|
|
@@ -142,17 +226,13 @@ function parseVerdictResponse(text) {
|
|
|
142
226
|
}
|
|
143
227
|
}
|
|
144
228
|
}
|
|
145
|
-
/**
|
|
146
|
-
* When the LLM responds with natural language instead of JSON,
|
|
147
|
-
* try to infer a verdict from keywords in the response.
|
|
148
|
-
*/
|
|
149
229
|
function inferVerdictFromText(text) {
|
|
150
230
|
const lower = text.toLowerCase();
|
|
151
231
|
let type;
|
|
152
232
|
if (lower.includes('environment variable') || lower.includes('missing data') || lower.includes('test data')) {
|
|
153
233
|
type = 'data_issue';
|
|
154
234
|
}
|
|
155
|
-
else if (lower.includes('stale') || lower.includes('selector') && lower.includes('renamed') || lower.includes('outdated')) {
|
|
235
|
+
else if (lower.includes('stale') || (lower.includes('selector') && lower.includes('renamed')) || lower.includes('outdated')) {
|
|
156
236
|
type = 'stale_test';
|
|
157
237
|
}
|
|
158
238
|
else if (lower.includes('timeout') || lower.includes('flaky') || lower.includes('race condition') || lower.includes('intermittent')) {
|
|
@@ -167,7 +247,6 @@ function inferVerdictFromText(text) {
|
|
|
167
247
|
else {
|
|
168
248
|
return null;
|
|
169
249
|
}
|
|
170
|
-
// Extract a summary from the first ~200 chars
|
|
171
250
|
const summary = text.replace(/```[\s\S]*?```/g, '').trim().slice(0, 300);
|
|
172
251
|
return {
|
|
173
252
|
type,
|
|
@@ -176,11 +255,6 @@ function inferVerdictFromText(text) {
|
|
|
176
255
|
evidence: { reasoning: text.slice(0, 1000) },
|
|
177
256
|
};
|
|
178
257
|
}
|
|
179
|
-
/**
|
|
180
|
-
* Fix common issues in LLM-generated JSON:
|
|
181
|
-
* - Replace bare `undefined` with `null`
|
|
182
|
-
* - Strip trailing commas before } or ]
|
|
183
|
-
*/
|
|
184
258
|
function sanitizeJsonString(str) {
|
|
185
259
|
return str
|
|
186
260
|
.replace(/:\s*undefined\b/g, ': null')
|
package/dist/llm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm.js","sourceRoot":"","sources":["../src/llm.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAI1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAWnD,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0CpB,CAAC;AAEH,MAAM,UAAU,iBAAiB,CAAC,MAAiB;IACjD,
|
|
1
|
+
{"version":3,"file":"llm.js","sourceRoot":"","sources":["../src/llm.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAI1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAWnD,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0CpB,CAAC;AAEH,MAAM,UAAU,iBAAiB,CAAC,MAAiB;IACjD,QAAQ,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxB,KAAK,WAAW;YACd,OAAO,IAAI,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACvC,KAAK,QAAQ;YACX,OAAO,IAAI,wBAAwB,CAAC,MAAM,CAAC,CAAC;QAC9C;YACE,MAAM,IAAI,KAAK,CACb,8BAA8B,MAAM,CAAC,QAAQ,2CAA2C,CACzF,CAAC;IACN,CAAC;AACH,CAAC;AAED,wEAAwE;AAExE,KAAK,UAAU,aAAa,CAC1B,MAA2D,EAC3D,YAAoB,EACpB,UAAU,GAAG,CAAC;IAEd,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;QACvD,IAAI,CAAC;YACH,OAAO,MAAM,MAAM,EAAE,CAAC;QACxB,CAAC;QAAC,OAAO,CAAU,EAAE,CAAC;YACpB,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACvD,MAAM,WAAW,GACf,CAAC,YAAY,SAAS,CAAC,cAAc;gBACrC,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAC;gBAC1B,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YACtB,MAAM,MAAM,GACV,CAAC,YAAY,SAAS,CAAC,mBAAmB;gBAC1C,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC;gBACnB,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAEjD,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,IAAI,KAAK,CACb,6BAA6B,YAAY,gEAAgE,CAC1G,CAAC;YACJ,CAAC;YAED,IAAI,WAAW,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;gBACxC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;gBACxD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,qBAAqB,YAAY,iBAAiB,MAAM,GAAG,IAAI,QAAQ,CACxE,CAAC;gBACF,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;gBAChD,SAAS;YACX,CAAC;YACD,MAAM,CAAC,CAAC;QACV,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,iBAAiB;IACb,MAAM,CAAY;IAClB,KAAK,CAAS;IAEtB,YAAY,MAAiB;QAC3B,IAAI,CAAC,MAAM,GAAG,IAAI,SAAS,CAAC;YAC1B,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACvD,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,OAAO,CACX,OAAuB,EACvB,gBAAyB;QAEzB,MAAM,WAAW,GAA2C,EAAE,CAAC;QAE/D,WAAW,CAAC,IAAI,CAAC;YACf,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,mBAAmB,CAAC,OAAO,CAAC;SACnC,CAAC,CAAC;QAEH,IAAI,gBAAgB,EAAE,CAAC;YACrB,WAAW,CAAC,IAAI,CAAC;gBACf,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE;oBACN,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE,WAAW;oBACvB,IAAI,EAAE,gBAAgB;iBACvB;aACF,CAAC,CAAC;YACH,WAAW,CAAC,IAAI,CAAC;gBACf,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,0IAA0I;aACjJ,CAAC,CAAC;QACL,CAAC;QAED,WAAW,CAAC,IAAI,CAAC;YACf,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,8DAA8D;SACrE,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,KAAK,IAAI,EAAE;YAC5C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;gBACjD,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,UAAU,EAAE,IAAI;gBAChB,MAAM,EAAE,aAAa;gBACrB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;aACnD,CAAC,CAAC;YAEH,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CACrC,CAAC,KAAK,EAAyC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,CACxE,CAAC;YAEF,OAAO;gBACL,IAAI,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE;gBAC3B,UAAU,EACR,CAAC,QAAQ,CAAC,KAAK,CAAC,YAAY,IAAI,CAAC,CAAC;oBAClC,CAAC,QAAQ,CAAC,KAAK,CAAC,aAAa,IAAI,CAAC,CAAC;aACtC,CAAC;QACJ,CAAC,EAAE,WAAW,CAAC,CAAC;QAEhB,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,OAAO,GAAG,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAClD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC;IACpD,CAAC;CACF;AAED,wEAAwE;AAExE,MAAM,wBAAwB;IACpB,MAAM,CAAS;IACf,KAAK,CAAS;IACd,OAAO,CAAS;IAExB,YAAY,MAAiB;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;QAClC,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QAC1B,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,2BAA2B,CAAC;IAC/D,CAAC;IAED,KAAK,CAAC,OAAO,CACX,OAAuB,EACvB,gBAAyB;QAEzB,MAAM,WAAW,GAAwE,EAAE,CAAC;QAE5F,WAAW,CAAC,IAAI,CAAC;YACf,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,mBAAmB,CAAC,OAAO,CAAC;SACnC,CAAC,CAAC;QAEH,IAAI,gBAAgB,EAAE,CAAC;YACrB,WAAW,CAAC,IAAI,CAAC;gBACf,IAAI,EAAE,WAAW;gBACjB,SAAS,EAAE,EAAE,GAAG,EAAE,yBAAyB,gBAAgB,EAAE,EAAE;aAChE,CAAC,CAAC;YACH,WAAW,CAAC,IAAI,CAAC;gBACf,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,4DAA4D;aACnE,CAAC,CAAC;QACL,CAAC;QAED,WAAW,CAAC,IAAI,CAAC;YACf,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,8DAA8D;SACrE,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,KAAK,IAAI,EAAE;YAC5C,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,mBAAmB,EAAE;gBAC/D,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,aAAa,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE;iBACvC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,UAAU,EAAE,IAAI;oBAChB,QAAQ,EAAE;wBACR,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE;wBAC1C,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE;qBACvC;iBACF,CAAC;aACH,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACnC,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;YAChD,CAAC;YAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAGlC,CAAC;YAEF,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE,CAAC;YACvD,MAAM,UAAU,GACd,CAAC,IAAI,CAAC,KAAK,EAAE,aAAa,IAAI,CAAC,CAAC;gBAChC,CAAC,IAAI,CAAC,KAAK,EAAE,iBAAiB,IAAI,CAAC,CAAC,CAAC;YAEvC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QAC9B,CAAC,EAAE,QAAQ,CAAC,CAAC;QAEb,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAClD,CAAC;QAED,MAAM,OAAO,GAAG,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAClD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC;IACpD,CAAC;CACF;AAED,wEAAwE;AAExE,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,yEAAyE;IACzE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,uCAAuC,CAAC,IAAI;QACvE,IAAI;QACJ,IAAI;KACL,CAAC;IACF,MAAM,OAAO,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAExD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACnC,OAAO,eAAe,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,uCAAuC;QACvC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YAC3D,OAAO,eAAe,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,yEAAyE;YACzE,MAAM,QAAQ,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;YAC5C,IAAI,QAAQ;gBAAE,OAAO,QAAQ,CAAC;YAE9B,MAAM,IAAI,KAAK,CACb,yCAA0C,CAAW,CAAC,OAAO,eAAe,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CACjG,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAY;IACxC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAEjC,IAAI,IAAiB,CAAC;IACtB,IAAI,KAAK,CAAC,QAAQ,CAAC,sBAAsB,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QAC5G,IAAI,GAAG,YAAY,CAAC;IACtB,CAAC;SAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9H,IAAI,GAAG,YAAY,CAAC;IACtB,CAAC;SAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;QACtI,IAAI,GAAG,OAAO,CAAC;IACjB,CAAC;SAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzH,IAAI,GAAG,YAAY,CAAC;IACtB,CAAC;SAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACpE,IAAI,GAAG,gBAAgB,CAAC;IAC1B,CAAC;SAAM,CAAC;QACN,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAEzE,OAAO;QACL,IAAI;QACJ,UAAU,EAAE,GAAG;QACf,OAAO;QACP,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE;KAC7C,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAW;IACrC,OAAO,GAAG;SACP,OAAO,CAAC,kBAAkB,EAAE,QAAQ,CAAC;SACrC,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,cAAc,GAAqB,IAAI,GAAG,CAAC;IAC/C,YAAY;IACZ,YAAY;IACZ,gBAAgB;IAChB,YAAY;IACZ,OAAO;CACR,CAAC,CAAC;AAEH,SAAS,eAAe,CAAC,MAAe;IACtC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,GAAG,GAAG,MAAiC,CAAC;IAE9C,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAgB,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,yBAAyB,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;IAC7C,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,CAAC,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,uBAAuB,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,IAAI,OAAO,GAAG,CAAC,SAAS,CAAC,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,MAAM,CAAgB;QAChC,UAAU;QACV,OAAO,EAAE,GAAG,CAAC,SAAS,CAAC;QACvB,QAAQ,EAAE;YACR,SAAS,EACP,OAAQ,GAAG,CAAC,UAAU,CAA6B,EAAE,CAAC,WAAW,CAAC,KAAK,QAAQ;gBAC7E,CAAC,CAAE,GAAG,CAAC,UAAU,CAA6B,CAAC,WAAW,CAAW;gBACrE,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC;YACpB,aAAa,EAAG,GAAG,CAAC,UAAU,CAA6B,EAAE,CAAC,eAAe,CAAyC;YACtH,aAAa,EAAG,GAAG,CAAC,UAAU,CAA6B,EAAE,CAAC,eAAe,CAAyC;SACvH;QACD,WAAW,EAAE,GAAG,CAAC,aAAa,CAA2B;QACzD,gBAAgB,EAAE,GAAG,CAAC,kBAAkB,CAAgC;KACzE,CAAC;AACJ,CAAC"}
|
package/dist/triage.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FlakewatchConfig } from './config.js';
|
|
2
2
|
import type { TestFailure, FailureContext } from './context.js';
|
|
3
|
-
import type { TriageReport, Verdict } from './verdicts.js';
|
|
3
|
+
import type { TriageReport, Verdict, VerdictType } from './verdicts.js';
|
|
4
4
|
/**
|
|
5
5
|
* Browser investigator interface — allows browser-agent to plug in
|
|
6
6
|
* without core depending on it.
|
|
@@ -14,9 +14,18 @@ export interface BrowserInvestigatorFn {
|
|
|
14
14
|
export interface TriageOptions {
|
|
15
15
|
failures: TestFailure[];
|
|
16
16
|
totalTests: number;
|
|
17
|
-
config:
|
|
17
|
+
config: FlakewatchConfig;
|
|
18
18
|
/** Optional browser investigator for 'full' mode */
|
|
19
19
|
browserInvestigator?: BrowserInvestigatorFn;
|
|
20
|
+
/** Called with progress updates during investigation */
|
|
21
|
+
onProgress?: (progress: TriageProgress) => void;
|
|
22
|
+
}
|
|
23
|
+
export interface TriageProgress {
|
|
24
|
+
phase: 'grouping' | 'investigating' | 'reporting';
|
|
25
|
+
current: number;
|
|
26
|
+
total: number;
|
|
27
|
+
testTitle?: string;
|
|
28
|
+
verdict?: VerdictType;
|
|
20
29
|
}
|
|
21
30
|
/**
|
|
22
31
|
* Main triage pipeline: group failures, select samples, investigate, report.
|
package/dist/triage.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"triage.d.ts","sourceRoot":"","sources":["../src/triage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAIhE,OAAO,KAAK,EACV,YAAY,EAEZ,OAAO,
|
|
1
|
+
{"version":3,"file":"triage.d.ts","sourceRoot":"","sources":["../src/triage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAIhE,OAAO,KAAK,EACV,YAAY,EAEZ,OAAO,EACP,WAAW,EACZ,MAAM,eAAe,CAAC;AAGvB;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAClD,OAAO,EAAE,OAAO,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,gBAAgB,CAAC;IACzB,oDAAoD;IACpD,mBAAmB,CAAC,EAAE,qBAAqB,CAAC;IAC5C,wDAAwD;IACxD,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,IAAI,CAAC;CACjD;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,UAAU,GAAG,eAAe,GAAG,WAAW,CAAC;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,YAAY,CAAC,CAmDvB"}
|
package/dist/triage.js
CHANGED
|
@@ -7,15 +7,20 @@ import { writeReport } from './report.js';
|
|
|
7
7
|
* Main triage pipeline: group failures, select samples, investigate, report.
|
|
8
8
|
*/
|
|
9
9
|
export async function triageFailures(options) {
|
|
10
|
-
const { failures, totalTests, config } = options;
|
|
10
|
+
const { failures, totalTests, config, onProgress } = options;
|
|
11
11
|
// 1. Group failures by error signature
|
|
12
|
+
onProgress?.({ phase: 'grouping', current: 0, total: failures.length });
|
|
12
13
|
const groups = groupFailures(failures);
|
|
13
14
|
// 2. Select groups to investigate (respecting limits + circuit breaker)
|
|
14
15
|
const { selected, circuitBroken } = selectGroupsForInvestigation(groups, totalTests, config.investigation);
|
|
15
|
-
|
|
16
|
+
if (circuitBroken) {
|
|
17
|
+
process.stderr.write(` Circuit breaker: ${failures.length}/${totalTests} tests failed (>${Math.round(config.investigation.massFailureThreshold * 100)}%). Investigating top ${selected.length} groups only.\n`);
|
|
18
|
+
}
|
|
19
|
+
// 3. Investigate samples
|
|
16
20
|
const llm = createLLMProvider(config.llm);
|
|
17
|
-
const results = await investigateGroups(selected, config, llm, options.browserInvestigator);
|
|
21
|
+
const results = await investigateGroups(selected, config, llm, options.browserInvestigator, onProgress);
|
|
18
22
|
// 4. Build report
|
|
23
|
+
onProgress?.({ phase: 'reporting', current: 0, total: 1 });
|
|
19
24
|
const report = {
|
|
20
25
|
timestamp: new Date().toISOString(),
|
|
21
26
|
totalFailures: failures.length,
|
|
@@ -34,13 +39,30 @@ export async function triageFailures(options) {
|
|
|
34
39
|
await writeReport(report, config.output);
|
|
35
40
|
return report;
|
|
36
41
|
}
|
|
37
|
-
async function investigateGroups(groups, config, llm, browserInvestigator) {
|
|
42
|
+
async function investigateGroups(groups, config, llm, browserInvestigator, onProgress) {
|
|
38
43
|
const results = [];
|
|
39
44
|
const maxConcurrent = config.investigation.maxConcurrent;
|
|
40
|
-
|
|
45
|
+
let completed = 0;
|
|
41
46
|
for (let i = 0; i < groups.length; i += maxConcurrent) {
|
|
42
47
|
const batch = groups.slice(i, i + maxConcurrent);
|
|
43
|
-
const batchResults = await Promise.all(batch.map((group) =>
|
|
48
|
+
const batchResults = await Promise.all(batch.map(async (group) => {
|
|
49
|
+
onProgress?.({
|
|
50
|
+
phase: 'investigating',
|
|
51
|
+
current: completed + 1,
|
|
52
|
+
total: groups.length,
|
|
53
|
+
testTitle: group.sample.testTitle,
|
|
54
|
+
});
|
|
55
|
+
const result = await investigateSample(group, config, llm, browserInvestigator);
|
|
56
|
+
completed++;
|
|
57
|
+
onProgress?.({
|
|
58
|
+
phase: 'investigating',
|
|
59
|
+
current: completed,
|
|
60
|
+
total: groups.length,
|
|
61
|
+
testTitle: group.sample.testTitle,
|
|
62
|
+
verdict: result.verdict.type,
|
|
63
|
+
});
|
|
64
|
+
return result;
|
|
65
|
+
}));
|
|
44
66
|
results.push(...batchResults);
|
|
45
67
|
}
|
|
46
68
|
return results;
|
|
@@ -48,13 +70,10 @@ async function investigateGroups(groups, config, llm, browserInvestigator) {
|
|
|
48
70
|
async function investigateSample(group, config, llm, browserInvestigator) {
|
|
49
71
|
const start = Date.now();
|
|
50
72
|
const sample = group.sample;
|
|
51
|
-
// Package context
|
|
52
73
|
const context = await packageFailureContext(sample, config.context);
|
|
53
|
-
// Choose investigation mode
|
|
54
74
|
const mode = config.investigation.mode;
|
|
55
75
|
const baseURL = config.investigation.baseURL;
|
|
56
76
|
if (mode === 'full' && browserInvestigator && baseURL) {
|
|
57
|
-
// Full browser investigation
|
|
58
77
|
const { verdict, tokensUsed } = await browserInvestigator(context, baseURL);
|
|
59
78
|
return {
|
|
60
79
|
testId: sample.testId,
|
|
@@ -74,7 +93,7 @@ async function investigateSample(group, config, llm, browserInvestigator) {
|
|
|
74
93
|
screenshotBase64 = buffer.toString('base64');
|
|
75
94
|
}
|
|
76
95
|
catch {
|
|
77
|
-
// Screenshot not available
|
|
96
|
+
// Screenshot not available
|
|
78
97
|
}
|
|
79
98
|
}
|
|
80
99
|
const { verdict, tokensUsed } = await llm.analyze(context, screenshotBase64);
|
package/dist/triage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"triage.js","sourceRoot":"","sources":["../src/triage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,4BAA4B,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAoB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"triage.js","sourceRoot":"","sources":["../src/triage.ts"],"names":[],"mappings":"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"}
|