@yasserkhanorg/e2e-agents 0.3.2
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/LICENSE +168 -0
- package/README.md +620 -0
- package/dist/agent/analysis.d.ts +62 -0
- package/dist/agent/analysis.d.ts.map +1 -0
- package/dist/agent/analysis.js +292 -0
- package/dist/agent/blast_radius.d.ts +4 -0
- package/dist/agent/blast_radius.d.ts.map +1 -0
- package/dist/agent/blast_radius.js +37 -0
- package/dist/agent/cache_utils.d.ts +38 -0
- package/dist/agent/cache_utils.d.ts.map +1 -0
- package/dist/agent/cache_utils.js +67 -0
- package/dist/agent/config.d.ts +148 -0
- package/dist/agent/config.d.ts.map +1 -0
- package/dist/agent/config.js +640 -0
- package/dist/agent/dependency_graph.d.ts +14 -0
- package/dist/agent/dependency_graph.d.ts.map +1 -0
- package/dist/agent/dependency_graph.js +227 -0
- package/dist/agent/feedback.d.ts +55 -0
- package/dist/agent/feedback.d.ts.map +1 -0
- package/dist/agent/feedback.js +257 -0
- package/dist/agent/flags.d.ts +23 -0
- package/dist/agent/flags.d.ts.map +1 -0
- package/dist/agent/flags.js +171 -0
- package/dist/agent/flow_catalog.d.ts +25 -0
- package/dist/agent/flow_catalog.d.ts.map +1 -0
- package/dist/agent/flow_catalog.js +106 -0
- package/dist/agent/flow_mapping.d.ts +10 -0
- package/dist/agent/flow_mapping.d.ts.map +1 -0
- package/dist/agent/flow_mapping.js +84 -0
- package/dist/agent/framework.d.ts +13 -0
- package/dist/agent/framework.d.ts.map +1 -0
- package/dist/agent/framework.js +149 -0
- package/dist/agent/gap_suggestions.d.ts +14 -0
- package/dist/agent/gap_suggestions.d.ts.map +1 -0
- package/dist/agent/gap_suggestions.js +101 -0
- package/dist/agent/generator.d.ts +10 -0
- package/dist/agent/generator.d.ts.map +1 -0
- package/dist/agent/generator.js +115 -0
- package/dist/agent/git.d.ts +11 -0
- package/dist/agent/git.d.ts.map +1 -0
- package/dist/agent/git.js +90 -0
- package/dist/agent/handoff.d.ts +22 -0
- package/dist/agent/handoff.d.ts.map +1 -0
- package/dist/agent/handoff.js +180 -0
- package/dist/agent/impact-analyzer.d.ts +114 -0
- package/dist/agent/impact-analyzer.d.ts.map +1 -0
- package/dist/agent/impact-analyzer.js +557 -0
- package/dist/agent/index.d.ts +21 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +38 -0
- package/dist/agent/model-router.d.ts +57 -0
- package/dist/agent/model-router.d.ts.map +1 -0
- package/dist/agent/model-router.js +154 -0
- package/dist/agent/operational_insights.d.ts +41 -0
- package/dist/agent/operational_insights.d.ts.map +1 -0
- package/dist/agent/operational_insights.js +126 -0
- package/dist/agent/pipeline.d.ts +23 -0
- package/dist/agent/pipeline.d.ts.map +1 -0
- package/dist/agent/pipeline.js +609 -0
- package/dist/agent/plan.d.ts +91 -0
- package/dist/agent/plan.d.ts.map +1 -0
- package/dist/agent/plan.js +331 -0
- package/dist/agent/playwright_report.d.ts +8 -0
- package/dist/agent/playwright_report.d.ts.map +1 -0
- package/dist/agent/playwright_report.js +126 -0
- package/dist/agent/report-generator.d.ts +24 -0
- package/dist/agent/report-generator.d.ts.map +1 -0
- package/dist/agent/report-generator.js +250 -0
- package/dist/agent/report.d.ts +81 -0
- package/dist/agent/report.d.ts.map +1 -0
- package/dist/agent/report.js +147 -0
- package/dist/agent/runner.d.ts +7 -0
- package/dist/agent/runner.d.ts.map +1 -0
- package/dist/agent/runner.js +576 -0
- package/dist/agent/selectors.d.ts +10 -0
- package/dist/agent/selectors.d.ts.map +1 -0
- package/dist/agent/selectors.js +75 -0
- package/dist/agent/spec-bridge.d.ts +101 -0
- package/dist/agent/spec-bridge.d.ts.map +1 -0
- package/dist/agent/spec-bridge.js +273 -0
- package/dist/agent/spec-builder.d.ts +102 -0
- package/dist/agent/spec-builder.d.ts.map +1 -0
- package/dist/agent/spec-builder.js +273 -0
- package/dist/agent/subsystem_risk.d.ts +23 -0
- package/dist/agent/subsystem_risk.d.ts.map +1 -0
- package/dist/agent/subsystem_risk.js +207 -0
- package/dist/agent/telemetry.d.ts +84 -0
- package/dist/agent/telemetry.d.ts.map +1 -0
- package/dist/agent/telemetry.js +220 -0
- package/dist/agent/test_path.d.ts +2 -0
- package/dist/agent/test_path.d.ts.map +1 -0
- package/dist/agent/test_path.js +23 -0
- package/dist/agent/tests.d.ts +18 -0
- package/dist/agent/tests.d.ts.map +1 -0
- package/dist/agent/tests.js +106 -0
- package/dist/agent/traceability.d.ts +22 -0
- package/dist/agent/traceability.d.ts.map +1 -0
- package/dist/agent/traceability.js +183 -0
- package/dist/agent/traceability_capture.d.ts +18 -0
- package/dist/agent/traceability_capture.d.ts.map +1 -0
- package/dist/agent/traceability_capture.js +313 -0
- package/dist/agent/traceability_ingest.d.ts +21 -0
- package/dist/agent/traceability_ingest.d.ts.map +1 -0
- package/dist/agent/traceability_ingest.js +237 -0
- package/dist/agent/utils.d.ts +13 -0
- package/dist/agent/utils.d.ts.map +1 -0
- package/dist/agent/utils.js +152 -0
- package/dist/agent/validators/selector-validator.d.ts +74 -0
- package/dist/agent/validators/selector-validator.d.ts.map +1 -0
- package/dist/agent/validators/selector-validator.js +165 -0
- package/dist/anthropic_provider.d.ts +65 -0
- package/dist/anthropic_provider.d.ts.map +1 -0
- package/dist/anthropic_provider.js +332 -0
- package/dist/api.d.ts +48 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +113 -0
- package/dist/base_provider.d.ts +53 -0
- package/dist/base_provider.d.ts.map +1 -0
- package/dist/base_provider.js +81 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +843 -0
- package/dist/custom_provider.d.ts +20 -0
- package/dist/custom_provider.d.ts.map +1 -0
- package/dist/custom_provider.js +276 -0
- package/dist/e2e-test-gen/index.d.ts +51 -0
- package/dist/e2e-test-gen/index.d.ts.map +1 -0
- package/dist/e2e-test-gen/index.js +57 -0
- package/dist/e2e-test-gen/spec_parser.d.ts +142 -0
- package/dist/e2e-test-gen/spec_parser.d.ts.map +1 -0
- package/dist/e2e-test-gen/spec_parser.js +786 -0
- package/dist/e2e-test-gen/types.d.ts +185 -0
- package/dist/e2e-test-gen/types.d.ts.map +1 -0
- package/dist/e2e-test-gen/types.js +4 -0
- package/dist/esm/agent/analysis.js +287 -0
- package/dist/esm/agent/blast_radius.js +34 -0
- package/dist/esm/agent/cache_utils.js +63 -0
- package/dist/esm/agent/config.js +637 -0
- package/dist/esm/agent/dependency_graph.js +224 -0
- package/dist/esm/agent/feedback.js +253 -0
- package/dist/esm/agent/flags.js +160 -0
- package/dist/esm/agent/flow_catalog.js +103 -0
- package/dist/esm/agent/flow_mapping.js +81 -0
- package/dist/esm/agent/framework.js +145 -0
- package/dist/esm/agent/gap_suggestions.js +98 -0
- package/dist/esm/agent/generator.js +112 -0
- package/dist/esm/agent/git.js +87 -0
- package/dist/esm/agent/handoff.js +177 -0
- package/dist/esm/agent/impact-analyzer.js +548 -0
- package/dist/esm/agent/index.js +22 -0
- package/dist/esm/agent/model-router.js +150 -0
- package/dist/esm/agent/operational_insights.js +123 -0
- package/dist/esm/agent/pipeline.js +605 -0
- package/dist/esm/agent/plan.js +324 -0
- package/dist/esm/agent/playwright_report.js +123 -0
- package/dist/esm/agent/report-generator.js +247 -0
- package/dist/esm/agent/report.js +144 -0
- package/dist/esm/agent/runner.js +572 -0
- package/dist/esm/agent/selectors.js +71 -0
- package/dist/esm/agent/spec-bridge.js +267 -0
- package/dist/esm/agent/spec-builder.js +267 -0
- package/dist/esm/agent/subsystem_risk.js +204 -0
- package/dist/esm/agent/telemetry.js +216 -0
- package/dist/esm/agent/test_path.js +20 -0
- package/dist/esm/agent/tests.js +101 -0
- package/dist/esm/agent/traceability.js +180 -0
- package/dist/esm/agent/traceability_capture.js +310 -0
- package/dist/esm/agent/traceability_ingest.js +234 -0
- package/dist/esm/agent/utils.js +138 -0
- package/dist/esm/agent/validators/selector-validator.js +160 -0
- package/dist/esm/anthropic_provider.js +324 -0
- package/dist/esm/api.js +105 -0
- package/dist/esm/base_provider.js +77 -0
- package/dist/esm/cli.js +841 -0
- package/dist/esm/custom_provider.js +272 -0
- package/dist/esm/e2e-test-gen/index.js +50 -0
- package/dist/esm/e2e-test-gen/spec_parser.js +782 -0
- package/dist/esm/e2e-test-gen/types.js +3 -0
- package/dist/esm/index.js +16 -0
- package/dist/esm/logger.js +89 -0
- package/dist/esm/mcp-server.js +465 -0
- package/dist/esm/ollama_provider.js +300 -0
- package/dist/esm/openai_provider.js +242 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/plan-and-test-constants.js +126 -0
- package/dist/esm/provider_factory.js +336 -0
- package/dist/esm/provider_interface.js +23 -0
- package/dist/esm/provider_utils.js +96 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/logger.d.ts +23 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +93 -0
- package/dist/mcp-server.d.ts +35 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +469 -0
- package/dist/ollama_provider.d.ts +65 -0
- package/dist/ollama_provider.d.ts.map +1 -0
- package/dist/ollama_provider.js +308 -0
- package/dist/openai_provider.d.ts +23 -0
- package/dist/openai_provider.d.ts.map +1 -0
- package/dist/openai_provider.js +250 -0
- package/dist/plan-and-test-constants.d.ts +110 -0
- package/dist/plan-and-test-constants.d.ts.map +1 -0
- package/dist/plan-and-test-constants.js +132 -0
- package/dist/provider_factory.d.ts +99 -0
- package/dist/provider_factory.d.ts.map +1 -0
- package/dist/provider_factory.js +341 -0
- package/dist/provider_interface.d.ts +358 -0
- package/dist/provider_interface.d.ts.map +1 -0
- package/dist/provider_interface.js +28 -0
- package/dist/provider_utils.d.ts +39 -0
- package/dist/provider_utils.d.ts.map +1 -0
- package/dist/provider_utils.js +103 -0
- package/package.json +101 -0
- package/schemas/gap.schema.json +18 -0
- package/schemas/impact.schema.json +418 -0
- package/schemas/plan.schema.json +285 -0
- package/schemas/subsystem-risk-map.schema.json +62 -0
- package/schemas/traceability-input.schema.json +122 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
6
|
+
};
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.AnthropicProvider = void 0;
|
|
9
|
+
exports.checkAnthropicSetup = checkAnthropicSetup;
|
|
10
|
+
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
11
|
+
const provider_interface_js_1 = require("./provider_interface.js");
|
|
12
|
+
const provider_utils_js_1 = require("./provider_utils.js");
|
|
13
|
+
const base_provider_js_1 = require("./base_provider.js");
|
|
14
|
+
const logger_js_1 = require("./logger.js");
|
|
15
|
+
/**
|
|
16
|
+
* Anthropic Provider - Claude AI models
|
|
17
|
+
*
|
|
18
|
+
* Features:
|
|
19
|
+
* - Highest quality AI (98% accuracy in testing)
|
|
20
|
+
* - Vision support (analyze screenshots, compare UI)
|
|
21
|
+
* - Fast response times (<1 second)
|
|
22
|
+
* - 200K token context window
|
|
23
|
+
* - Prompt caching (reduces costs by 90% on repeated prompts)
|
|
24
|
+
*
|
|
25
|
+
* Costs (Claude Sonnet 4.5):
|
|
26
|
+
* - Input: $3 per 1M tokens
|
|
27
|
+
* - Output: $15 per 1M tokens
|
|
28
|
+
* - Cached input: $0.30 per 1M tokens
|
|
29
|
+
* - Estimated: ~$30-80/month for autonomous testing
|
|
30
|
+
*
|
|
31
|
+
* Use cases:
|
|
32
|
+
* - Vision tasks (screenshot comparison)
|
|
33
|
+
* - Complex failure diagnosis
|
|
34
|
+
* - High-stakes production testing
|
|
35
|
+
* - When quality is paramount
|
|
36
|
+
*
|
|
37
|
+
* Models:
|
|
38
|
+
* - claude-sonnet-4-5-20250929 (recommended - best balance)
|
|
39
|
+
* - claude-opus-4-5-20251101 (highest quality, slower, more expensive)
|
|
40
|
+
* - claude-haiku-4-0-20250430 (fastest, cheapest, lower quality)
|
|
41
|
+
*/
|
|
42
|
+
class AnthropicProvider extends base_provider_js_1.BaseProvider {
|
|
43
|
+
constructor(config) {
|
|
44
|
+
super();
|
|
45
|
+
this.name = 'anthropic';
|
|
46
|
+
this.capabilities = {
|
|
47
|
+
vision: true, // Full vision support
|
|
48
|
+
streaming: true,
|
|
49
|
+
maxTokens: 200000, // 200K context window
|
|
50
|
+
costPer1MInputTokens: 3, // $3 per 1M input tokens
|
|
51
|
+
costPer1MOutputTokens: 15, // $15 per 1M output tokens
|
|
52
|
+
supportsTools: true, // Function calling support
|
|
53
|
+
supportsPromptCaching: true, // Reduces costs by 90%
|
|
54
|
+
typicalResponseTimeMs: 800, // ~0.8 seconds
|
|
55
|
+
};
|
|
56
|
+
// SECURITY: Validate API key format
|
|
57
|
+
if (!provider_utils_js_1.API_KEY_PATTERNS.anthropic.test(config.apiKey)) {
|
|
58
|
+
throw new Error('Invalid API key format. Expected sk-ant-* format.');
|
|
59
|
+
}
|
|
60
|
+
// SECURITY: Validate and enforce HTTPS for remote connections
|
|
61
|
+
if (config.baseUrl) {
|
|
62
|
+
const validation = (0, provider_utils_js_1.validateAndSanitizeUrl)(config.baseUrl);
|
|
63
|
+
if (!validation.valid) {
|
|
64
|
+
throw new Error(`Invalid base URL: ${validation.warning}`);
|
|
65
|
+
}
|
|
66
|
+
if (validation.warning) {
|
|
67
|
+
logger_js_1.logger.warn(`HTTPS required for remote URLs: ${validation.warning}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
this.client = new sdk_1.default({
|
|
71
|
+
apiKey: config.apiKey,
|
|
72
|
+
baseURL: config.baseUrl,
|
|
73
|
+
});
|
|
74
|
+
this.model = config.model || 'claude-sonnet-4-5-20250929';
|
|
75
|
+
}
|
|
76
|
+
async generateText(prompt, options) {
|
|
77
|
+
const startTime = Date.now();
|
|
78
|
+
try {
|
|
79
|
+
// SECURITY: Validate prompt length to prevent resource exhaustion
|
|
80
|
+
if (prompt.length > 10 * 1024 * 1024) {
|
|
81
|
+
throw new Error('Prompt exceeds maximum size (10MB)');
|
|
82
|
+
}
|
|
83
|
+
const response = await (0, provider_utils_js_1.withTimeout)(this.client.messages.create({
|
|
84
|
+
model: this.model,
|
|
85
|
+
max_tokens: options?.maxTokens || 4000,
|
|
86
|
+
temperature: options?.temperature,
|
|
87
|
+
top_p: options?.topP,
|
|
88
|
+
stop_sequences: options?.stopSequences,
|
|
89
|
+
system: options?.systemPrompt,
|
|
90
|
+
messages: [
|
|
91
|
+
{
|
|
92
|
+
role: 'user',
|
|
93
|
+
content: prompt,
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
}), options?.timeout, 'generateText');
|
|
97
|
+
const responseTime = Date.now() - startTime;
|
|
98
|
+
const text = this.extractTextFromResponse(response);
|
|
99
|
+
// SECURITY: Type-safe usage extraction
|
|
100
|
+
const usage = this.extractUsageFromResponse(response.usage);
|
|
101
|
+
const cost = this.calculateCost(usage, this.capabilities.costPer1MInputTokens, this.capabilities.costPer1MOutputTokens);
|
|
102
|
+
// Update stats
|
|
103
|
+
this.updateStats(usage, responseTime, cost);
|
|
104
|
+
return {
|
|
105
|
+
text,
|
|
106
|
+
usage,
|
|
107
|
+
cost,
|
|
108
|
+
metadata: {
|
|
109
|
+
model: this.model,
|
|
110
|
+
responseTimeMs: responseTime,
|
|
111
|
+
stopReason: response.stop_reason,
|
|
112
|
+
stopSequence: response.stop_sequence,
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
this.stats.failedRequests++;
|
|
118
|
+
throw new provider_interface_js_1.LLMProviderError((0, provider_utils_js_1.sanitizeErrorMessage)(error, 'generateText'), this.name, this.extractStatusCode(error), error);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async analyzeImage(images, prompt, options) {
|
|
122
|
+
const startTime = Date.now();
|
|
123
|
+
try {
|
|
124
|
+
// SECURITY: Validate image array size
|
|
125
|
+
if (images.length === 0 || images.length > 20) {
|
|
126
|
+
throw new Error('Image count must be between 1 and 20');
|
|
127
|
+
}
|
|
128
|
+
// SECURITY: Validate prompt length
|
|
129
|
+
if (prompt.length > 10 * 1024 * 1024) {
|
|
130
|
+
throw new Error('Prompt exceeds maximum size (10MB)');
|
|
131
|
+
}
|
|
132
|
+
// Build content array with text and images
|
|
133
|
+
const content = [];
|
|
134
|
+
// Add prompt text first
|
|
135
|
+
content.push({
|
|
136
|
+
type: 'text',
|
|
137
|
+
text: prompt,
|
|
138
|
+
});
|
|
139
|
+
// Add each image
|
|
140
|
+
for (const image of images) {
|
|
141
|
+
// Validate media type
|
|
142
|
+
const mediaType = (image.mimeType || image.mediaType || 'image/png');
|
|
143
|
+
if (!['image/png', 'image/jpeg', 'image/webp', 'image/gif'].includes(mediaType)) {
|
|
144
|
+
throw new Error(`Unsupported image type: ${mediaType}`);
|
|
145
|
+
}
|
|
146
|
+
const data = image.data || image.base64 || '';
|
|
147
|
+
// SECURITY: Validate base64 data size (limit to 20MB per image)
|
|
148
|
+
if (data.length > 20 * 1024 * 1024) {
|
|
149
|
+
throw new Error('Image data exceeds maximum size (20MB)');
|
|
150
|
+
}
|
|
151
|
+
content.push({
|
|
152
|
+
type: 'image',
|
|
153
|
+
source: {
|
|
154
|
+
type: 'base64',
|
|
155
|
+
media_type: mediaType,
|
|
156
|
+
data: data,
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
// Add description if provided
|
|
160
|
+
if (image.description) {
|
|
161
|
+
content.push({
|
|
162
|
+
type: 'text',
|
|
163
|
+
text: `[Image: ${image.description}]`,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const response = await (0, provider_utils_js_1.withTimeout)(this.client.messages.create({
|
|
168
|
+
model: this.model,
|
|
169
|
+
max_tokens: options?.maxTokens || 4000,
|
|
170
|
+
temperature: options?.temperature,
|
|
171
|
+
top_p: options?.topP,
|
|
172
|
+
stop_sequences: options?.stopSequences,
|
|
173
|
+
system: options?.systemPrompt,
|
|
174
|
+
messages: [
|
|
175
|
+
{
|
|
176
|
+
role: 'user',
|
|
177
|
+
content,
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
}), options?.timeout, 'analyzeImage');
|
|
181
|
+
const responseTime = Date.now() - startTime;
|
|
182
|
+
const text = this.extractTextFromResponse(response);
|
|
183
|
+
// SECURITY: Type-safe usage extraction
|
|
184
|
+
const usage = this.extractUsageFromResponse(response.usage);
|
|
185
|
+
const cost = this.calculateCost(usage, this.capabilities.costPer1MInputTokens, this.capabilities.costPer1MOutputTokens);
|
|
186
|
+
// Update stats
|
|
187
|
+
this.updateStats(usage, responseTime, cost);
|
|
188
|
+
return {
|
|
189
|
+
text,
|
|
190
|
+
usage,
|
|
191
|
+
cost,
|
|
192
|
+
metadata: {
|
|
193
|
+
model: this.model,
|
|
194
|
+
responseTimeMs: responseTime,
|
|
195
|
+
stopReason: response.stop_reason,
|
|
196
|
+
imageCount: images.length,
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
this.stats.failedRequests++;
|
|
202
|
+
throw new provider_interface_js_1.LLMProviderError((0, provider_utils_js_1.sanitizeErrorMessage)(error, 'analyzeImage'), this.name, this.extractStatusCode(error), error);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
async *streamText(prompt, options) {
|
|
206
|
+
try {
|
|
207
|
+
// SECURITY: Validate prompt length
|
|
208
|
+
if (prompt.length > 10 * 1024 * 1024) {
|
|
209
|
+
throw new Error('Prompt exceeds maximum size (10MB)');
|
|
210
|
+
}
|
|
211
|
+
const stream = await (0, provider_utils_js_1.withTimeout)(this.client.messages.create({
|
|
212
|
+
model: this.model,
|
|
213
|
+
max_tokens: options?.maxTokens || 4000,
|
|
214
|
+
temperature: options?.temperature,
|
|
215
|
+
top_p: options?.topP,
|
|
216
|
+
stop_sequences: options?.stopSequences,
|
|
217
|
+
system: options?.systemPrompt,
|
|
218
|
+
messages: [
|
|
219
|
+
{
|
|
220
|
+
role: 'user',
|
|
221
|
+
content: prompt,
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
stream: true,
|
|
225
|
+
}), options?.timeout, 'streamText');
|
|
226
|
+
for await (const event of stream) {
|
|
227
|
+
if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
|
|
228
|
+
yield event.delta.text;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Note: Streaming doesn't provide detailed usage stats
|
|
232
|
+
// We increment request count but can't track exact tokens/cost
|
|
233
|
+
this.stats.requestCount++;
|
|
234
|
+
this.stats.lastUpdated = new Date();
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
this.stats.failedRequests++;
|
|
238
|
+
throw new provider_interface_js_1.LLMProviderError((0, provider_utils_js_1.sanitizeErrorMessage)(error, 'streamText'), this.name, this.extractStatusCode(error), error);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
extractTextFromResponse(response) {
|
|
242
|
+
const textBlocks = response.content.filter((block) => block.type === 'text');
|
|
243
|
+
return textBlocks.map((block) => {
|
|
244
|
+
if (block.type === 'text') {
|
|
245
|
+
return block.text;
|
|
246
|
+
}
|
|
247
|
+
return '';
|
|
248
|
+
}).join('\n');
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* SECURITY: Type-safe usage extraction
|
|
252
|
+
* Avoids unsafe `as any` casts
|
|
253
|
+
*/
|
|
254
|
+
extractUsageFromResponse(usage) {
|
|
255
|
+
return {
|
|
256
|
+
inputTokens: usage.input_tokens || 0,
|
|
257
|
+
outputTokens: usage.output_tokens || 0,
|
|
258
|
+
totalTokens: (usage.input_tokens || 0) + (usage.output_tokens || 0),
|
|
259
|
+
cachedTokens: usage.cache_read_input_tokens ?? undefined,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* SECURITY: Extract status code safely
|
|
264
|
+
*/
|
|
265
|
+
extractStatusCode(error) {
|
|
266
|
+
if (error && typeof error === 'object') {
|
|
267
|
+
const err = error;
|
|
268
|
+
const status = err.status;
|
|
269
|
+
if (typeof status === 'number') {
|
|
270
|
+
return status;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return undefined;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Check if API key is valid and service is accessible
|
|
277
|
+
*/
|
|
278
|
+
async checkHealth() {
|
|
279
|
+
try {
|
|
280
|
+
// Try a minimal request to verify API key
|
|
281
|
+
await (0, provider_utils_js_1.withTimeout)(this.client.messages.create({
|
|
282
|
+
model: this.model,
|
|
283
|
+
max_tokens: 10,
|
|
284
|
+
messages: [
|
|
285
|
+
{
|
|
286
|
+
role: 'user',
|
|
287
|
+
content: 'Hi',
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
}), 5000, 'health check');
|
|
291
|
+
return {
|
|
292
|
+
healthy: true,
|
|
293
|
+
message: `Anthropic API is accessible`,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
return {
|
|
298
|
+
healthy: false,
|
|
299
|
+
message: `Anthropic API error: ${(0, provider_utils_js_1.sanitizeErrorMessage)(error, 'health check')}`,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
exports.AnthropicProvider = AnthropicProvider;
|
|
305
|
+
/**
|
|
306
|
+
* Helper to check Anthropic setup
|
|
307
|
+
*/
|
|
308
|
+
async function checkAnthropicSetup(apiKey) {
|
|
309
|
+
if (!apiKey) {
|
|
310
|
+
return {
|
|
311
|
+
valid: false,
|
|
312
|
+
message: 'No API key provided',
|
|
313
|
+
estimatedMonthlyCost: 'N/A',
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
try {
|
|
317
|
+
const provider = new AnthropicProvider({ apiKey });
|
|
318
|
+
const health = await provider.checkHealth();
|
|
319
|
+
return {
|
|
320
|
+
valid: health.healthy,
|
|
321
|
+
message: health.message,
|
|
322
|
+
estimatedMonthlyCost: '$30-80 for autonomous testing (24 cycles/day)',
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
catch (error) {
|
|
326
|
+
return {
|
|
327
|
+
valid: false,
|
|
328
|
+
message: `Setup check failed: ${(0, provider_utils_js_1.sanitizeErrorMessage)(error, 'setup check')}`,
|
|
329
|
+
estimatedMonthlyCost: 'N/A',
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
}
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type ConfigOverrides } from './agent/config.js';
|
|
2
|
+
import { type PlanReport } from './agent/plan.js';
|
|
3
|
+
import type { ReportData } from './agent/report.js';
|
|
4
|
+
import { type FinalizeGeneratedTestsOptions, type FinalizeGeneratedTestsResult } from './agent/handoff.js';
|
|
5
|
+
import { type TraceabilityIngestOptions, type TraceabilityIngestResult } from './agent/traceability_ingest.js';
|
|
6
|
+
import { type TraceabilityCaptureResult } from './agent/traceability_capture.js';
|
|
7
|
+
export interface AgentApiOptions extends Omit<ConfigOverrides, 'mode'> {
|
|
8
|
+
cwd?: string;
|
|
9
|
+
configPath?: string;
|
|
10
|
+
apply?: boolean;
|
|
11
|
+
allowFallback?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface AnalyzeResult {
|
|
14
|
+
report: ReportData;
|
|
15
|
+
reportPath: string;
|
|
16
|
+
}
|
|
17
|
+
export interface RecommendTestsResult extends AnalyzeResult {
|
|
18
|
+
plan: PlanReport;
|
|
19
|
+
planPath: string;
|
|
20
|
+
ciSummaryMarkdown: string;
|
|
21
|
+
ciSummaryPath: string;
|
|
22
|
+
}
|
|
23
|
+
export interface TraceabilityIngestApiOptions {
|
|
24
|
+
cwd?: string;
|
|
25
|
+
configPath?: string;
|
|
26
|
+
path?: string;
|
|
27
|
+
testsRoot?: string;
|
|
28
|
+
payload: unknown;
|
|
29
|
+
options?: TraceabilityIngestOptions;
|
|
30
|
+
}
|
|
31
|
+
export interface TraceabilityCaptureApiOptions {
|
|
32
|
+
cwd?: string;
|
|
33
|
+
configPath?: string;
|
|
34
|
+
path?: string;
|
|
35
|
+
testsRoot?: string;
|
|
36
|
+
reportPath: string;
|
|
37
|
+
sinceRef?: string;
|
|
38
|
+
outputPath?: string;
|
|
39
|
+
coverageMapPath?: string;
|
|
40
|
+
changedFilesPath?: string;
|
|
41
|
+
}
|
|
42
|
+
export declare function analyzeImpact(options?: AgentApiOptions): Promise<AnalyzeResult>;
|
|
43
|
+
export declare function findGaps(options?: AgentApiOptions): Promise<AnalyzeResult>;
|
|
44
|
+
export declare function recommendTests(options?: AgentApiOptions): Promise<RecommendTestsResult>;
|
|
45
|
+
export declare function handoffGeneratedTests(options: FinalizeGeneratedTestsOptions): FinalizeGeneratedTestsResult;
|
|
46
|
+
export declare function ingestTraceability(options: TraceabilityIngestApiOptions): TraceabilityIngestResult;
|
|
47
|
+
export declare function captureTraceability(options: TraceabilityCaptureApiOptions): TraceabilityCaptureResult;
|
|
48
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAKA,OAAO,EAAgB,KAAK,eAAe,EAAC,MAAM,mBAAmB,CAAC;AAEtE,OAAO,EAMH,KAAK,UAAU,EAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,mBAAmB,CAAC;AAElD,OAAO,EAAyB,KAAK,6BAA6B,EAAE,KAAK,4BAA4B,EAAC,MAAM,oBAAoB,CAAC;AACjI,OAAO,EAEH,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAChC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAGH,KAAK,yBAAyB,EACjC,MAAM,iCAAiC,CAAC;AAEzC,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC;IAClE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,UAAU,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAqB,SAAQ,aAAa;IACvD,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,4BAA4B;IACzC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,yBAAyB,CAAC;CACvC;AAED,MAAM,WAAW,6BAA6B;IAC1C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC7B;AA0BD,wBAAsB,aAAa,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,aAAa,CAAC,CAOzF;AAED,wBAAsB,QAAQ,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,aAAa,CAAC,CAOpF;AAED,wBAAsB,cAAc,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAwBjG;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,6BAA6B,GAAG,4BAA4B,CAE1G;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,4BAA4B,GAAG,wBAAwB,CASlG;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,6BAA6B,GAAG,yBAAyB,CAkBrG"}
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.analyzeImpact = analyzeImpact;
|
|
6
|
+
exports.findGaps = findGaps;
|
|
7
|
+
exports.recommendTests = recommendTests;
|
|
8
|
+
exports.handoffGeneratedTests = handoffGeneratedTests;
|
|
9
|
+
exports.ingestTraceability = ingestTraceability;
|
|
10
|
+
exports.captureTraceability = captureTraceability;
|
|
11
|
+
const fs_1 = require("fs");
|
|
12
|
+
const path_1 = require("path");
|
|
13
|
+
const config_js_1 = require("./agent/config.js");
|
|
14
|
+
const runner_js_1 = require("./agent/runner.js");
|
|
15
|
+
const plan_js_1 = require("./agent/plan.js");
|
|
16
|
+
const operational_insights_js_1 = require("./agent/operational_insights.js");
|
|
17
|
+
const handoff_js_1 = require("./agent/handoff.js");
|
|
18
|
+
const traceability_ingest_js_1 = require("./agent/traceability_ingest.js");
|
|
19
|
+
const traceability_capture_js_1 = require("./agent/traceability_capture.js");
|
|
20
|
+
function readReportJson(reportPath) {
|
|
21
|
+
if (!(0, fs_1.existsSync)(reportPath)) {
|
|
22
|
+
throw new Error(`Expected report not found: ${reportPath}`);
|
|
23
|
+
}
|
|
24
|
+
const raw = (0, fs_1.readFileSync)(reportPath, 'utf-8');
|
|
25
|
+
return JSON.parse(raw);
|
|
26
|
+
}
|
|
27
|
+
function resolveAgent(options, mode) {
|
|
28
|
+
const cwd = options.cwd || process.cwd();
|
|
29
|
+
const { config } = (0, config_js_1.resolveConfig)(cwd, options.configPath, {
|
|
30
|
+
...options,
|
|
31
|
+
mode,
|
|
32
|
+
});
|
|
33
|
+
if (options.allowFallback) {
|
|
34
|
+
config.impact.allowFallback = true;
|
|
35
|
+
}
|
|
36
|
+
return config;
|
|
37
|
+
}
|
|
38
|
+
function reportPathFor(configPath, mode) {
|
|
39
|
+
return (0, path_1.join)(configPath, '.e2e-ai-agents', mode === 'impact' ? 'impact.json' : 'gap.json');
|
|
40
|
+
}
|
|
41
|
+
async function analyzeImpact(options = {}) {
|
|
42
|
+
const config = resolveAgent(options, 'impact');
|
|
43
|
+
await (0, runner_js_1.runImpact)(config, { apply: options.apply ?? false });
|
|
44
|
+
const reportRoot = config.testsRoot || config.path;
|
|
45
|
+
const reportPath = reportPathFor(reportRoot, 'impact');
|
|
46
|
+
const report = readReportJson(reportPath);
|
|
47
|
+
return { report, reportPath };
|
|
48
|
+
}
|
|
49
|
+
async function findGaps(options = {}) {
|
|
50
|
+
const config = resolveAgent(options, 'gap');
|
|
51
|
+
await (0, runner_js_1.runGap)(config, { apply: options.apply ?? false });
|
|
52
|
+
const reportRoot = config.testsRoot || config.path;
|
|
53
|
+
const reportPath = reportPathFor(reportRoot, 'gap');
|
|
54
|
+
const report = readReportJson(reportPath);
|
|
55
|
+
return { report, reportPath };
|
|
56
|
+
}
|
|
57
|
+
async function recommendTests(options = {}) {
|
|
58
|
+
const config = resolveAgent(options, 'impact');
|
|
59
|
+
await (0, runner_js_1.runImpact)(config, { apply: options.apply ?? false });
|
|
60
|
+
const reportRoot = config.testsRoot || config.path;
|
|
61
|
+
const impactPath = reportPathFor(reportRoot, 'impact');
|
|
62
|
+
const report = readReportJson(impactPath);
|
|
63
|
+
const basePlan = (0, plan_js_1.buildPlanFromImpactReport)(report, config.policy);
|
|
64
|
+
const withActions = (0, plan_js_1.attachDeveloperActions)(basePlan, {
|
|
65
|
+
appPath: config.path,
|
|
66
|
+
testsRoot: reportRoot,
|
|
67
|
+
sinceRef: config.git.since,
|
|
68
|
+
});
|
|
69
|
+
const plan = (0, operational_insights_js_1.applyOperationalInsights)(withActions, reportRoot);
|
|
70
|
+
const planPath = (0, plan_js_1.writePlanReport)(reportRoot, plan);
|
|
71
|
+
const ciSummaryMarkdown = (0, plan_js_1.renderCiSummaryMarkdown)(plan);
|
|
72
|
+
const ciSummaryPath = (0, plan_js_1.writeCiSummary)(reportRoot, ciSummaryMarkdown);
|
|
73
|
+
return {
|
|
74
|
+
report,
|
|
75
|
+
reportPath: impactPath,
|
|
76
|
+
plan,
|
|
77
|
+
planPath,
|
|
78
|
+
ciSummaryMarkdown,
|
|
79
|
+
ciSummaryPath,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function handoffGeneratedTests(options) {
|
|
83
|
+
return (0, handoff_js_1.finalizeGeneratedTests)(options);
|
|
84
|
+
}
|
|
85
|
+
function ingestTraceability(options) {
|
|
86
|
+
const cwd = options.cwd || process.cwd();
|
|
87
|
+
const { config } = (0, config_js_1.resolveConfig)(cwd, options.configPath, {
|
|
88
|
+
path: options.path,
|
|
89
|
+
testsRoot: options.testsRoot,
|
|
90
|
+
mode: 'impact',
|
|
91
|
+
});
|
|
92
|
+
const reportRoot = config.testsRoot || config.path;
|
|
93
|
+
return (0, traceability_ingest_js_1.ingestTraceabilityInput)(reportRoot, config.impact.traceability, options.payload, options.options);
|
|
94
|
+
}
|
|
95
|
+
function captureTraceability(options) {
|
|
96
|
+
const cwd = options.cwd || process.cwd();
|
|
97
|
+
const { config } = (0, config_js_1.resolveConfig)(cwd, options.configPath, {
|
|
98
|
+
path: options.path,
|
|
99
|
+
testsRoot: options.testsRoot,
|
|
100
|
+
mode: 'impact',
|
|
101
|
+
});
|
|
102
|
+
const reportRoot = config.testsRoot || config.path;
|
|
103
|
+
const captureOptions = {
|
|
104
|
+
appPath: config.path,
|
|
105
|
+
testsRoot: reportRoot,
|
|
106
|
+
reportPath: options.reportPath,
|
|
107
|
+
sinceRef: options.sinceRef || config.git.since,
|
|
108
|
+
outputPath: options.outputPath,
|
|
109
|
+
coverageMapPath: options.coverageMapPath,
|
|
110
|
+
changedFilesPath: options.changedFilesPath,
|
|
111
|
+
};
|
|
112
|
+
return (0, traceability_capture_js_1.captureTraceabilityInput)(captureOptions);
|
|
113
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { GenerateOptions, ImageInput, LLMProvider, LLMResponse, ProviderCapabilities, ProviderUsageStats } from './provider_interface.js';
|
|
2
|
+
/**
|
|
3
|
+
* Abstract base class for all LLM providers
|
|
4
|
+
* Eliminates 240+ lines of duplicate stats management code
|
|
5
|
+
* Provides common functionality for token tracking, cost calculation, and stats management
|
|
6
|
+
*/
|
|
7
|
+
export declare abstract class BaseProvider implements LLMProvider {
|
|
8
|
+
abstract name: string;
|
|
9
|
+
abstract capabilities: ProviderCapabilities;
|
|
10
|
+
protected stats: ProviderUsageStats;
|
|
11
|
+
constructor();
|
|
12
|
+
/**
|
|
13
|
+
* Initialize stats object with default values
|
|
14
|
+
*/
|
|
15
|
+
protected initializeStats(): void;
|
|
16
|
+
/**
|
|
17
|
+
* Update stats with new usage data
|
|
18
|
+
* Maintains rolling average for response time
|
|
19
|
+
*/
|
|
20
|
+
protected updateStats(usage: {
|
|
21
|
+
inputTokens: number;
|
|
22
|
+
outputTokens: number;
|
|
23
|
+
totalTokens: number;
|
|
24
|
+
}, responseTime: number, cost: number): void;
|
|
25
|
+
/**
|
|
26
|
+
* Get a copy of current usage stats
|
|
27
|
+
*/
|
|
28
|
+
getUsageStats(): ProviderUsageStats;
|
|
29
|
+
/**
|
|
30
|
+
* Reset all usage stats to initial state
|
|
31
|
+
*/
|
|
32
|
+
resetUsageStats(): void;
|
|
33
|
+
/**
|
|
34
|
+
* Abstract methods that must be implemented by subclasses
|
|
35
|
+
*/
|
|
36
|
+
abstract generateText(prompt: string, options?: GenerateOptions): Promise<LLMResponse>;
|
|
37
|
+
abstract analyzeImage(images: ImageInput[], prompt: string, options?: GenerateOptions): Promise<LLMResponse>;
|
|
38
|
+
abstract streamText(prompt: string, options?: GenerateOptions): AsyncGenerator<string, void, unknown>;
|
|
39
|
+
abstract checkHealth(): Promise<{
|
|
40
|
+
healthy: boolean;
|
|
41
|
+
message: string;
|
|
42
|
+
}>;
|
|
43
|
+
/**
|
|
44
|
+
* Calculate cost for token usage, accounting for prompt caching discounts
|
|
45
|
+
* Cached tokens cost 90% less than regular tokens
|
|
46
|
+
*/
|
|
47
|
+
protected calculateCost(usage: {
|
|
48
|
+
inputTokens: number;
|
|
49
|
+
outputTokens: number;
|
|
50
|
+
cachedTokens?: number;
|
|
51
|
+
}, costPer1MInputTokens: number, costPer1MOutputTokens: number): number;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=base_provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base_provider.d.ts","sourceRoot":"","sources":["../src/base_provider.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACR,eAAe,EACf,UAAU,EACV,WAAW,EACX,WAAW,EACX,oBAAoB,EACpB,kBAAkB,EACrB,MAAM,yBAAyB,CAAC;AAEjC;;;;GAIG;AACH,8BAAsB,YAAa,YAAW,WAAW;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,oBAAoB,CAAC;IAE5C,SAAS,CAAC,KAAK,EAAG,kBAAkB,CAAC;;IAMrC;;OAEG;IACH,SAAS,CAAC,eAAe,IAAI,IAAI;IAcjC;;;OAGG;IACH,SAAS,CAAC,WAAW,CACjB,KAAK,EAAE;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,EACvE,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,GACb,IAAI;IAeP;;OAEG;IACH,aAAa,IAAI,kBAAkB;IAInC;;OAEG;IACH,eAAe,IAAI,IAAI;IAIvB;;OAEG;IACH,QAAQ,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC;IACtF,QAAQ,CAAC,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC;IAC5G,QAAQ,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC;IACrG,QAAQ,CAAC,WAAW,IAAI,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAC,CAAC;IAEpE;;;OAGG;IACH,SAAS,CAAC,aAAa,CACnB,KAAK,EAAE;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAC,EACzE,oBAAoB,EAAE,MAAM,EAC5B,qBAAqB,EAAE,MAAM,GAC9B,MAAM;CAmBZ"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.BaseProvider = void 0;
|
|
6
|
+
/**
|
|
7
|
+
* Abstract base class for all LLM providers
|
|
8
|
+
* Eliminates 240+ lines of duplicate stats management code
|
|
9
|
+
* Provides common functionality for token tracking, cost calculation, and stats management
|
|
10
|
+
*/
|
|
11
|
+
class BaseProvider {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.initializeStats();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Initialize stats object with default values
|
|
17
|
+
*/
|
|
18
|
+
initializeStats() {
|
|
19
|
+
this.stats = {
|
|
20
|
+
requestCount: 0,
|
|
21
|
+
totalInputTokens: 0,
|
|
22
|
+
totalOutputTokens: 0,
|
|
23
|
+
totalTokens: 0,
|
|
24
|
+
totalCost: 0,
|
|
25
|
+
averageResponseTimeMs: 0,
|
|
26
|
+
failedRequests: 0,
|
|
27
|
+
startTime: new Date(),
|
|
28
|
+
lastUpdated: new Date(),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Update stats with new usage data
|
|
33
|
+
* Maintains rolling average for response time
|
|
34
|
+
*/
|
|
35
|
+
updateStats(usage, responseTime, cost) {
|
|
36
|
+
this.stats.requestCount++;
|
|
37
|
+
this.stats.totalInputTokens += usage.inputTokens;
|
|
38
|
+
this.stats.totalOutputTokens += usage.outputTokens;
|
|
39
|
+
this.stats.totalTokens += usage.totalTokens;
|
|
40
|
+
this.stats.totalCost += cost;
|
|
41
|
+
// Update rolling average response time
|
|
42
|
+
const totalRequests = this.stats.requestCount;
|
|
43
|
+
this.stats.averageResponseTimeMs =
|
|
44
|
+
(this.stats.averageResponseTimeMs * (totalRequests - 1) + responseTime) / totalRequests;
|
|
45
|
+
this.stats.lastUpdated = new Date();
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get a copy of current usage stats
|
|
49
|
+
*/
|
|
50
|
+
getUsageStats() {
|
|
51
|
+
return { ...this.stats };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Reset all usage stats to initial state
|
|
55
|
+
*/
|
|
56
|
+
resetUsageStats() {
|
|
57
|
+
this.initializeStats();
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Calculate cost for token usage, accounting for prompt caching discounts
|
|
61
|
+
* Cached tokens cost 90% less than regular tokens
|
|
62
|
+
*/
|
|
63
|
+
calculateCost(usage, costPer1MInputTokens, costPer1MOutputTokens) {
|
|
64
|
+
// Calculate input token cost
|
|
65
|
+
let inputCost = 0;
|
|
66
|
+
// Cached tokens cost 90% less
|
|
67
|
+
if (usage.cachedTokens) {
|
|
68
|
+
const cachedCost = (usage.cachedTokens / 1000000) * (costPer1MInputTokens * 0.1);
|
|
69
|
+
const uncachedInputTokens = usage.inputTokens - usage.cachedTokens;
|
|
70
|
+
const uncachedCost = (uncachedInputTokens / 1000000) * costPer1MInputTokens;
|
|
71
|
+
inputCost = cachedCost + uncachedCost;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
inputCost = (usage.inputTokens / 1000000) * costPer1MInputTokens;
|
|
75
|
+
}
|
|
76
|
+
// Calculate output token cost
|
|
77
|
+
const outputCost = (usage.outputTokens / 1000000) * costPer1MOutputTokens;
|
|
78
|
+
return inputCost + outputCost;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.BaseProvider = BaseProvider;
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|