@artemiskit/reports 0.1.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.
@@ -0,0 +1,373 @@
1
+ /**
2
+ * Stress Test HTML Report Generator
3
+ */
4
+
5
+ import type { StressManifest } from '@artemiskit/core';
6
+ import Handlebars from 'handlebars';
7
+
8
+ const HTML_TEMPLATE = `
9
+ <!DOCTYPE html>
10
+ <html lang="en">
11
+ <head>
12
+ <meta charset="UTF-8">
13
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
14
+ <title>Artemis Stress Test Report - {{manifest.config.scenario}}</title>
15
+ <style>
16
+ * { margin: 0; padding: 0; box-sizing: border-box; }
17
+ body {
18
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
19
+ line-height: 1.6;
20
+ color: #333;
21
+ background: #f5f5f5;
22
+ padding: 2rem;
23
+ }
24
+ .container { max-width: 1200px; margin: 0 auto; }
25
+ h1 { margin-bottom: 0.5rem; color: #1a1a1a; }
26
+ h2 { margin: 2rem 0 1rem; color: #333; border-bottom: 2px solid #e0e0e0; padding-bottom: 0.5rem; }
27
+ .meta { color: #666; margin-bottom: 2rem; }
28
+ .badge {
29
+ display: inline-block;
30
+ padding: 0.25rem 0.75rem;
31
+ border-radius: 4px;
32
+ font-size: 0.875rem;
33
+ font-weight: 600;
34
+ margin-right: 0.5rem;
35
+ }
36
+ .badge.stress { background: #dbeafe; color: #1e40af; }
37
+ .summary {
38
+ display: grid;
39
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
40
+ gap: 1rem;
41
+ margin-bottom: 2rem;
42
+ }
43
+ .card {
44
+ background: white;
45
+ padding: 1.5rem;
46
+ border-radius: 8px;
47
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
48
+ }
49
+ .card h3 { font-size: 0.875rem; color: #666; margin-bottom: 0.5rem; }
50
+ .card .value { font-size: 2rem; font-weight: bold; }
51
+ .card .value.success { color: #22c55e; }
52
+ .card .value.warning { color: #f59e0b; }
53
+ .card .value.error { color: #ef4444; }
54
+ .card .value.info { color: #3b82f6; }
55
+ .card .unit { font-size: 1rem; font-weight: normal; color: #666; }
56
+ .latency-grid {
57
+ display: grid;
58
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
59
+ gap: 1rem;
60
+ margin-top: 1rem;
61
+ }
62
+ .latency-item {
63
+ text-align: center;
64
+ padding: 1rem;
65
+ background: #f9fafb;
66
+ border-radius: 8px;
67
+ }
68
+ .latency-item .label { font-size: 0.75rem; color: #666; text-transform: uppercase; }
69
+ .latency-item .value { font-size: 1.5rem; font-weight: bold; color: #333; }
70
+ .histogram {
71
+ margin-top: 1rem;
72
+ padding: 1rem;
73
+ background: #f9fafb;
74
+ border-radius: 8px;
75
+ }
76
+ .histogram-bar {
77
+ display: flex;
78
+ align-items: center;
79
+ margin-bottom: 0.5rem;
80
+ }
81
+ .histogram-label {
82
+ width: 100px;
83
+ font-size: 0.75rem;
84
+ color: #666;
85
+ text-align: right;
86
+ padding-right: 1rem;
87
+ }
88
+ .histogram-track {
89
+ flex: 1;
90
+ height: 20px;
91
+ background: #e5e7eb;
92
+ border-radius: 4px;
93
+ overflow: hidden;
94
+ }
95
+ .histogram-fill {
96
+ height: 100%;
97
+ background: linear-gradient(90deg, #3b82f6, #1d4ed8);
98
+ border-radius: 4px;
99
+ }
100
+ .histogram-count {
101
+ width: 60px;
102
+ font-size: 0.75rem;
103
+ color: #666;
104
+ padding-left: 0.5rem;
105
+ }
106
+ .success-meter {
107
+ width: 100%;
108
+ height: 24px;
109
+ background: #e5e7eb;
110
+ border-radius: 12px;
111
+ overflow: hidden;
112
+ margin-top: 1rem;
113
+ }
114
+ .success-meter-fill {
115
+ height: 100%;
116
+ display: flex;
117
+ align-items: center;
118
+ justify-content: flex-end;
119
+ padding-right: 0.5rem;
120
+ color: white;
121
+ font-weight: 600;
122
+ font-size: 0.875rem;
123
+ }
124
+ .success-meter-fill.high { background: linear-gradient(90deg, #22c55e, #16a34a); }
125
+ .success-meter-fill.medium { background: linear-gradient(90deg, #f59e0b, #d97706); }
126
+ .success-meter-fill.low { background: linear-gradient(90deg, #ef4444, #dc2626); }
127
+ table { width: 100%; border-collapse: collapse; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
128
+ th, td { padding: 0.75rem 1rem; text-align: left; border-bottom: 1px solid #e0e0e0; }
129
+ th { background: #f9fafb; font-weight: 600; }
130
+ tr:last-child td { border-bottom: none; }
131
+ .source-badge {
132
+ display: inline-block;
133
+ padding: 0.125rem 0.5rem;
134
+ border-radius: 4px;
135
+ font-size: 0.75rem;
136
+ font-weight: 500;
137
+ background: #e0e7ff;
138
+ color: #3730a3;
139
+ margin-left: 0.5rem;
140
+ }
141
+ .redaction-banner {
142
+ background: #fef3c7;
143
+ border: 1px solid #f59e0b;
144
+ border-radius: 8px;
145
+ padding: 1rem;
146
+ margin-bottom: 1.5rem;
147
+ display: flex;
148
+ align-items: center;
149
+ gap: 0.75rem;
150
+ }
151
+ .redaction-banner .icon { font-size: 1.5rem; }
152
+ .redaction-banner .content { flex: 1; }
153
+ .redaction-banner .title { font-weight: 600; color: #92400e; }
154
+ .redaction-banner .details { font-size: 0.875rem; color: #a16207; margin-top: 0.25rem; }
155
+ footer { margin-top: 3rem; text-align: center; color: #666; font-size: 0.875rem; }
156
+ </style>
157
+ </head>
158
+ <body>
159
+ <div class="container">
160
+ <h1>
161
+ <span class="badge stress">STRESS TEST</span>
162
+ {{manifest.config.scenario}}
163
+ </h1>
164
+ <p class="meta">
165
+ Run ID: {{manifest.run_id}} |
166
+ Provider: {{manifest.config.provider}} |
167
+ {{#if manifest.config.model}}Model: {{manifest.config.model}} |{{/if}}
168
+ {{formatDate manifest.start_time}}
169
+ </p>
170
+
171
+ {{#if manifest.redaction.enabled}}
172
+ <div class="redaction-banner">
173
+ <div class="icon">🔒</div>
174
+ <div class="content">
175
+ <div class="title">Data Redaction Configured</div>
176
+ <div class="details">
177
+ Redaction was enabled for this stress test run.
178
+ Patterns: {{join manifest.redaction.patternsUsed ', '}}
179
+ </div>
180
+ </div>
181
+ </div>
182
+ {{/if}}
183
+
184
+ <div class="summary">
185
+ <div class="card">
186
+ <h3>Success Rate</h3>
187
+ <div class="value {{successRateClass manifest.metrics.success_rate}}">
188
+ {{formatPercent manifest.metrics.success_rate}}
189
+ </div>
190
+ <div class="success-meter">
191
+ <div class="success-meter-fill {{successRateClass manifest.metrics.success_rate}}" style="width: {{formatPercentRaw manifest.metrics.success_rate}}">
192
+ {{manifest.metrics.successful_requests}}/{{manifest.metrics.total_requests}}
193
+ </div>
194
+ </div>
195
+ </div>
196
+ <div class="card">
197
+ <h3>Requests/Second</h3>
198
+ <div class="value info">{{formatDecimal manifest.metrics.requests_per_second}}</div>
199
+ </div>
200
+ <div class="card">
201
+ <h3>Total Requests</h3>
202
+ <div class="value">{{formatNumber manifest.metrics.total_requests}}</div>
203
+ </div>
204
+ <div class="card">
205
+ <h3>Duration</h3>
206
+ <div class="value">{{formatDuration manifest.duration_ms}}<span class="unit">s</span></div>
207
+ </div>
208
+ </div>
209
+
210
+ <h2>Latency Percentiles</h2>
211
+ <div class="card">
212
+ <div class="latency-grid">
213
+ <div class="latency-item">
214
+ <div class="label">Min</div>
215
+ <div class="value">{{manifest.metrics.min_latency_ms}}ms</div>
216
+ </div>
217
+ <div class="latency-item">
218
+ <div class="label">P50</div>
219
+ <div class="value">{{manifest.metrics.p50_latency_ms}}ms</div>
220
+ </div>
221
+ <div class="latency-item">
222
+ <div class="label">P90</div>
223
+ <div class="value">{{manifest.metrics.p90_latency_ms}}ms</div>
224
+ </div>
225
+ <div class="latency-item">
226
+ <div class="label">P95</div>
227
+ <div class="value">{{manifest.metrics.p95_latency_ms}}ms</div>
228
+ </div>
229
+ <div class="latency-item">
230
+ <div class="label">P99</div>
231
+ <div class="value">{{manifest.metrics.p99_latency_ms}}ms</div>
232
+ </div>
233
+ <div class="latency-item">
234
+ <div class="label">Max</div>
235
+ <div class="value">{{manifest.metrics.max_latency_ms}}ms</div>
236
+ </div>
237
+ </div>
238
+ </div>
239
+
240
+ <h2>Configuration</h2>
241
+ <div class="card">
242
+ <table>
243
+ <tr>
244
+ <th>Parameter</th>
245
+ <th>Value</th>
246
+ </tr>
247
+ <tr>
248
+ <td>Concurrency</td>
249
+ <td>{{manifest.config.concurrency}}</td>
250
+ </tr>
251
+ <tr>
252
+ <td>Duration</td>
253
+ <td>{{manifest.config.duration_seconds}} seconds</td>
254
+ </tr>
255
+ <tr>
256
+ <td>Ramp-up Time</td>
257
+ <td>{{manifest.config.ramp_up_seconds}} seconds</td>
258
+ </tr>
259
+ {{#if manifest.config.max_requests}}
260
+ <tr>
261
+ <td>Max Requests</td>
262
+ <td>{{manifest.config.max_requests}}</td>
263
+ </tr>
264
+ {{/if}}
265
+ </table>
266
+ </div>
267
+
268
+ <h2>Results Summary</h2>
269
+ <div class="card">
270
+ <table>
271
+ <tr>
272
+ <th>Metric</th>
273
+ <th>Value</th>
274
+ </tr>
275
+ <tr>
276
+ <td>Total Requests</td>
277
+ <td>{{formatNumber manifest.metrics.total_requests}}</td>
278
+ </tr>
279
+ <tr>
280
+ <td>Successful</td>
281
+ <td style="color: #22c55e;">{{formatNumber manifest.metrics.successful_requests}}</td>
282
+ </tr>
283
+ <tr>
284
+ <td>Failed</td>
285
+ <td style="color: {{#if manifest.metrics.failed_requests}}#ef4444{{else}}inherit{{/if}};">{{formatNumber manifest.metrics.failed_requests}}</td>
286
+ </tr>
287
+ <tr>
288
+ <td>Average Latency</td>
289
+ <td>{{formatDecimal manifest.metrics.avg_latency_ms}}ms</td>
290
+ </tr>
291
+ </table>
292
+ </div>
293
+
294
+ {{#if manifest.resolved_config}}
295
+ <h2>Resolved Configuration</h2>
296
+ <div class="card">
297
+ <p><strong>Provider:</strong> {{manifest.resolved_config.provider}} <span class="source-badge">{{manifest.resolved_config.source.provider}}</span></p>
298
+ {{#if manifest.resolved_config.model}}
299
+ <p><strong>Model:</strong> {{manifest.resolved_config.model}} <span class="source-badge">{{manifest.resolved_config.source.model}}</span></p>
300
+ {{/if}}
301
+ {{#if manifest.resolved_config.deployment_name}}
302
+ <p><strong>Deployment:</strong> {{manifest.resolved_config.deployment_name}} <span class="source-badge">{{manifest.resolved_config.source.deployment_name}}</span></p>
303
+ {{/if}}
304
+ {{#if manifest.resolved_config.resource_name}}
305
+ <p><strong>Resource:</strong> {{manifest.resolved_config.resource_name}} <span class="source-badge">{{manifest.resolved_config.source.resource_name}}</span></p>
306
+ {{/if}}
307
+ {{#if manifest.resolved_config.api_version}}
308
+ <p><strong>API Version:</strong> {{manifest.resolved_config.api_version}} <span class="source-badge">{{manifest.resolved_config.source.api_version}}</span></p>
309
+ {{/if}}
310
+ {{#if manifest.resolved_config.base_url}}
311
+ <p><strong>Base URL:</strong> {{manifest.resolved_config.base_url}} <span class="source-badge">{{manifest.resolved_config.source.base_url}}</span></p>
312
+ {{/if}}
313
+ {{#if manifest.resolved_config.temperature}}
314
+ <p><strong>Temperature:</strong> {{manifest.resolved_config.temperature}} <span class="source-badge">{{manifest.resolved_config.source.temperature}}</span></p>
315
+ {{/if}}
316
+ </div>
317
+ {{/if}}
318
+
319
+ <h2>Provenance</h2>
320
+ <div class="card">
321
+ <p><strong>Git Commit:</strong> {{manifest.git.commit}}</p>
322
+ <p><strong>Git Branch:</strong> {{manifest.git.branch}}</p>
323
+ <p><strong>Run By:</strong> {{manifest.provenance.run_by}}</p>
324
+ <p><strong>Platform:</strong> {{manifest.environment.platform}} ({{manifest.environment.arch}})</p>
325
+ </div>
326
+
327
+ <footer>
328
+ Generated by Artemis Agent Reliability Toolkit - Stress Test Module
329
+ </footer>
330
+ </div>
331
+ </body>
332
+ </html>
333
+ `;
334
+
335
+ export function generateStressHTMLReport(manifest: StressManifest): string {
336
+ // Register helpers
337
+ Handlebars.registerHelper('formatPercent', (value: number) => {
338
+ return `${(value * 100).toFixed(1)}%`;
339
+ });
340
+
341
+ Handlebars.registerHelper('formatPercentRaw', (value: number) => {
342
+ return `${(value * 100).toFixed(0)}%`;
343
+ });
344
+
345
+ Handlebars.registerHelper('formatDate', (value: string) => {
346
+ return new Date(value).toLocaleString();
347
+ });
348
+
349
+ Handlebars.registerHelper('formatNumber', (value: number) => {
350
+ return value.toLocaleString();
351
+ });
352
+
353
+ Handlebars.registerHelper('formatDecimal', (value: number) => {
354
+ return value.toFixed(2);
355
+ });
356
+
357
+ Handlebars.registerHelper('formatDuration', (ms: number) => {
358
+ return (ms / 1000).toFixed(1);
359
+ });
360
+
361
+ Handlebars.registerHelper('successRateClass', (value: number) => {
362
+ if (value >= 0.99) return 'high';
363
+ if (value >= 0.95) return 'medium';
364
+ return 'low';
365
+ });
366
+
367
+ Handlebars.registerHelper('join', (arr: string[], separator: string) => {
368
+ return arr?.join(separator) || '';
369
+ });
370
+
371
+ const template = Handlebars.compile(HTML_TEMPLATE);
372
+ return template({ manifest });
373
+ }
package/src/index.ts ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @artemiskit/reports
3
+ * Report generation for Artemis Agent Reliability Toolkit
4
+ */
5
+
6
+ // Standard run reports
7
+ export { generateHTMLReport } from './html/generator';
8
+ export { generateJSONReport, type JSONReportOptions } from './json/generator';
9
+
10
+ // Red team reports
11
+ export { generateRedTeamHTMLReport } from './html/redteam-generator';
12
+
13
+ // Stress test reports
14
+ export { generateStressHTMLReport } from './html/stress-generator';
@@ -0,0 +1,18 @@
1
+ /**
2
+ * JSON Report Generator
3
+ */
4
+
5
+ import type { AnyManifest, RedTeamManifest, RunManifest, StressManifest } from '@artemiskit/core';
6
+
7
+ export interface JSONReportOptions {
8
+ pretty?: boolean;
9
+ includeRaw?: boolean;
10
+ }
11
+
12
+ export function generateJSONReport(manifest: RunManifest, options?: JSONReportOptions): string;
13
+ export function generateJSONReport(manifest: RedTeamManifest, options?: JSONReportOptions): string;
14
+ export function generateJSONReport(manifest: StressManifest, options?: JSONReportOptions): string;
15
+ export function generateJSONReport(manifest: AnyManifest, options: JSONReportOptions = {}): string {
16
+ const { pretty = true } = options;
17
+ return pretty ? JSON.stringify(manifest, null, 2) : JSON.stringify(manifest);
18
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src",
6
+ "noEmit": false,
7
+ "declaration": true,
8
+ "declarationMap": true,
9
+ "emitDeclarationOnly": true
10
+ },
11
+ "include": ["src/**/*"],
12
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
13
+ }