@peakinfer/cli 1.0.133
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/.claude/settings.local.json +8 -0
- package/.env.example +6 -0
- package/.github/workflows/peakinfer.yml +64 -0
- package/CHANGELOG.md +31 -0
- package/LICENSE +190 -0
- package/README.md +335 -0
- package/data/inferencemax.json +274 -0
- package/dist/agent-analyzer.d.ts +45 -0
- package/dist/agent-analyzer.d.ts.map +1 -0
- package/dist/agent-analyzer.js +374 -0
- package/dist/agent-analyzer.js.map +1 -0
- package/dist/agent.d.ts +76 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +965 -0
- package/dist/agent.js.map +1 -0
- package/dist/agents/correlation-analyzer.d.ts +34 -0
- package/dist/agents/correlation-analyzer.d.ts.map +1 -0
- package/dist/agents/correlation-analyzer.js +261 -0
- package/dist/agents/correlation-analyzer.js.map +1 -0
- package/dist/agents/index.d.ts +91 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +111 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/runtime-analyzer.d.ts +38 -0
- package/dist/agents/runtime-analyzer.d.ts.map +1 -0
- package/dist/agents/runtime-analyzer.js +244 -0
- package/dist/agents/runtime-analyzer.js.map +1 -0
- package/dist/analysis-types.d.ts +500 -0
- package/dist/analysis-types.d.ts.map +1 -0
- package/dist/analysis-types.js +11 -0
- package/dist/analysis-types.js.map +1 -0
- package/dist/analytics.d.ts +25 -0
- package/dist/analytics.d.ts.map +1 -0
- package/dist/analytics.js +94 -0
- package/dist/analytics.js.map +1 -0
- package/dist/analyzer.d.ts +48 -0
- package/dist/analyzer.d.ts.map +1 -0
- package/dist/analyzer.js +547 -0
- package/dist/analyzer.js.map +1 -0
- package/dist/artifacts.d.ts +44 -0
- package/dist/artifacts.d.ts.map +1 -0
- package/dist/artifacts.js +165 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/benchmarks/index.d.ts +88 -0
- package/dist/benchmarks/index.d.ts.map +1 -0
- package/dist/benchmarks/index.js +205 -0
- package/dist/benchmarks/index.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +427 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/ci.d.ts +19 -0
- package/dist/commands/ci.d.ts.map +1 -0
- package/dist/commands/ci.js +253 -0
- package/dist/commands/ci.js.map +1 -0
- package/dist/commands/config.d.ts +16 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +249 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/demo.d.ts +15 -0
- package/dist/commands/demo.d.ts.map +1 -0
- package/dist/commands/demo.js +106 -0
- package/dist/commands/demo.js.map +1 -0
- package/dist/commands/export.d.ts +14 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +209 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/history.d.ts +15 -0
- package/dist/commands/history.d.ts.map +1 -0
- package/dist/commands/history.js +389 -0
- package/dist/commands/history.js.map +1 -0
- package/dist/commands/template.d.ts +14 -0
- package/dist/commands/template.d.ts.map +1 -0
- package/dist/commands/template.js +341 -0
- package/dist/commands/template.js.map +1 -0
- package/dist/commands/validate-map.d.ts +12 -0
- package/dist/commands/validate-map.d.ts.map +1 -0
- package/dist/commands/validate-map.js +274 -0
- package/dist/commands/validate-map.js.map +1 -0
- package/dist/commands/whatif.d.ts +17 -0
- package/dist/commands/whatif.d.ts.map +1 -0
- package/dist/commands/whatif.js +206 -0
- package/dist/commands/whatif.js.map +1 -0
- package/dist/comparison.d.ts +38 -0
- package/dist/comparison.d.ts.map +1 -0
- package/dist/comparison.js +223 -0
- package/dist/comparison.js.map +1 -0
- package/dist/config.d.ts +42 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +158 -0
- package/dist/config.js.map +1 -0
- package/dist/connectors/helicone.d.ts +9 -0
- package/dist/connectors/helicone.d.ts.map +1 -0
- package/dist/connectors/helicone.js +106 -0
- package/dist/connectors/helicone.js.map +1 -0
- package/dist/connectors/index.d.ts +37 -0
- package/dist/connectors/index.d.ts.map +1 -0
- package/dist/connectors/index.js +65 -0
- package/dist/connectors/index.js.map +1 -0
- package/dist/connectors/langsmith.d.ts +9 -0
- package/dist/connectors/langsmith.d.ts.map +1 -0
- package/dist/connectors/langsmith.js +122 -0
- package/dist/connectors/langsmith.js.map +1 -0
- package/dist/connectors/types.d.ts +83 -0
- package/dist/connectors/types.d.ts.map +1 -0
- package/dist/connectors/types.js +98 -0
- package/dist/connectors/types.js.map +1 -0
- package/dist/cost-estimator.d.ts +46 -0
- package/dist/cost-estimator.d.ts.map +1 -0
- package/dist/cost-estimator.js +104 -0
- package/dist/cost-estimator.js.map +1 -0
- package/dist/costs.d.ts +57 -0
- package/dist/costs.d.ts.map +1 -0
- package/dist/costs.js +251 -0
- package/dist/costs.js.map +1 -0
- package/dist/counterfactuals.d.ts +29 -0
- package/dist/counterfactuals.d.ts.map +1 -0
- package/dist/counterfactuals.js +448 -0
- package/dist/counterfactuals.js.map +1 -0
- package/dist/enhancement-prompts.d.ts +41 -0
- package/dist/enhancement-prompts.d.ts.map +1 -0
- package/dist/enhancement-prompts.js +88 -0
- package/dist/enhancement-prompts.js.map +1 -0
- package/dist/envelopes.d.ts +20 -0
- package/dist/envelopes.d.ts.map +1 -0
- package/dist/envelopes.js +790 -0
- package/dist/envelopes.js.map +1 -0
- package/dist/format-normalizer.d.ts +71 -0
- package/dist/format-normalizer.d.ts.map +1 -0
- package/dist/format-normalizer.js +1331 -0
- package/dist/format-normalizer.js.map +1 -0
- package/dist/history.d.ts +79 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +313 -0
- package/dist/history.js.map +1 -0
- package/dist/html.d.ts +11 -0
- package/dist/html.d.ts.map +1 -0
- package/dist/html.js +463 -0
- package/dist/html.js.map +1 -0
- package/dist/impact.d.ts +42 -0
- package/dist/impact.d.ts.map +1 -0
- package/dist/impact.js +443 -0
- package/dist/impact.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/insights.d.ts +5 -0
- package/dist/insights.d.ts.map +1 -0
- package/dist/insights.js +271 -0
- package/dist/insights.js.map +1 -0
- package/dist/joiner.d.ts +9 -0
- package/dist/joiner.d.ts.map +1 -0
- package/dist/joiner.js +247 -0
- package/dist/joiner.js.map +1 -0
- package/dist/orchestrator.d.ts +34 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +827 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/pdf.d.ts +26 -0
- package/dist/pdf.d.ts.map +1 -0
- package/dist/pdf.js +84 -0
- package/dist/pdf.js.map +1 -0
- package/dist/prediction.d.ts +33 -0
- package/dist/prediction.d.ts.map +1 -0
- package/dist/prediction.js +316 -0
- package/dist/prediction.js.map +1 -0
- package/dist/prompts/loader.d.ts +38 -0
- package/dist/prompts/loader.d.ts.map +1 -0
- package/dist/prompts/loader.js +60 -0
- package/dist/prompts/loader.js.map +1 -0
- package/dist/renderer.d.ts +64 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +923 -0
- package/dist/renderer.js.map +1 -0
- package/dist/runid.d.ts +57 -0
- package/dist/runid.d.ts.map +1 -0
- package/dist/runid.js +199 -0
- package/dist/runid.js.map +1 -0
- package/dist/runtime.d.ts +29 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +366 -0
- package/dist/runtime.js.map +1 -0
- package/dist/scanner.d.ts +11 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +426 -0
- package/dist/scanner.js.map +1 -0
- package/dist/templates.d.ts +120 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +429 -0
- package/dist/templates.js.map +1 -0
- package/dist/tools/index.d.ts +153 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +177 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types.d.ts +3647 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +703 -0
- package/dist/types.js.map +1 -0
- package/dist/version.d.ts +7 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +23 -0
- package/dist/version.js.map +1 -0
- package/docs/demo-guide.md +423 -0
- package/docs/events-format.md +295 -0
- package/docs/inferencemap-spec.md +344 -0
- package/docs/migration-v2.md +293 -0
- package/fixtures/demo/precomputed.json +142 -0
- package/fixtures/demo-project/README.md +52 -0
- package/fixtures/demo-project/ai-service.ts +65 -0
- package/fixtures/demo-project/sample-events.jsonl +15 -0
- package/fixtures/demo-project/src/ai-service.ts +128 -0
- package/fixtures/demo-project/src/llm-client.ts +155 -0
- package/package.json +65 -0
- package/prompts/agent-analyzer.yaml +47 -0
- package/prompts/ci-gate.yaml +98 -0
- package/prompts/correlation-analyzer.yaml +178 -0
- package/prompts/format-normalizer.yaml +46 -0
- package/prompts/peak-performance.yaml +180 -0
- package/prompts/pr-comment.yaml +111 -0
- package/prompts/runtime-analyzer.yaml +189 -0
- package/prompts/unified-analyzer.yaml +241 -0
- package/schemas/inference-map.v0.1.json +215 -0
- package/scripts/benchmark.ts +394 -0
- package/scripts/demo-v1.5.sh +158 -0
- package/scripts/sync-from-site.sh +197 -0
- package/scripts/validate-sync.sh +178 -0
- package/src/agent-analyzer.ts +481 -0
- package/src/agent.ts +1232 -0
- package/src/agents/correlation-analyzer.ts +353 -0
- package/src/agents/index.ts +235 -0
- package/src/agents/runtime-analyzer.ts +343 -0
- package/src/analysis-types.ts +558 -0
- package/src/analytics.ts +100 -0
- package/src/analyzer.ts +692 -0
- package/src/artifacts.ts +218 -0
- package/src/benchmarks/index.ts +309 -0
- package/src/cli.ts +503 -0
- package/src/commands/ci.ts +336 -0
- package/src/commands/config.ts +288 -0
- package/src/commands/demo.ts +175 -0
- package/src/commands/export.ts +297 -0
- package/src/commands/history.ts +425 -0
- package/src/commands/template.ts +385 -0
- package/src/commands/validate-map.ts +324 -0
- package/src/commands/whatif.ts +272 -0
- package/src/comparison.ts +283 -0
- package/src/config.ts +188 -0
- package/src/connectors/helicone.ts +164 -0
- package/src/connectors/index.ts +93 -0
- package/src/connectors/langsmith.ts +179 -0
- package/src/connectors/types.ts +180 -0
- package/src/cost-estimator.ts +146 -0
- package/src/costs.ts +347 -0
- package/src/counterfactuals.ts +516 -0
- package/src/enhancement-prompts.ts +118 -0
- package/src/envelopes.ts +814 -0
- package/src/format-normalizer.ts +1486 -0
- package/src/history.ts +400 -0
- package/src/html.ts +512 -0
- package/src/impact.ts +522 -0
- package/src/index.ts +83 -0
- package/src/insights.ts +341 -0
- package/src/joiner.ts +289 -0
- package/src/orchestrator.ts +1015 -0
- package/src/pdf.ts +110 -0
- package/src/prediction.ts +392 -0
- package/src/prompts/loader.ts +88 -0
- package/src/renderer.ts +1045 -0
- package/src/runid.ts +261 -0
- package/src/runtime.ts +450 -0
- package/src/scanner.ts +508 -0
- package/src/templates.ts +561 -0
- package/src/tools/index.ts +214 -0
- package/src/types.ts +873 -0
- package/src/version.ts +24 -0
- package/templates/context-accumulation.yaml +23 -0
- package/templates/cost-concentration.yaml +20 -0
- package/templates/dead-code.yaml +20 -0
- package/templates/latency-explainer.yaml +23 -0
- package/templates/optimizations/ab-testing-framework.yaml +74 -0
- package/templates/optimizations/api-gateway-optimization.yaml +81 -0
- package/templates/optimizations/api-model-routing-strategy.yaml +126 -0
- package/templates/optimizations/auto-scaling-optimization.yaml +85 -0
- package/templates/optimizations/batch-utilization-diagnostic.yaml +142 -0
- package/templates/optimizations/comprehensive-apm.yaml +76 -0
- package/templates/optimizations/context-window-optimization.yaml +91 -0
- package/templates/optimizations/cost-sensitive-batch-processing.yaml +77 -0
- package/templates/optimizations/distributed-training-optimization.yaml +77 -0
- package/templates/optimizations/document-analysis-edge.yaml +77 -0
- package/templates/optimizations/document-pipeline-optimization.yaml +78 -0
- package/templates/optimizations/domain-specific-distillation.yaml +78 -0
- package/templates/optimizations/error-handling-optimization.yaml +76 -0
- package/templates/optimizations/gptq-4bit-quantization.yaml +96 -0
- package/templates/optimizations/long-context-memory-management.yaml +78 -0
- package/templates/optimizations/max-tokens-optimization.yaml +76 -0
- package/templates/optimizations/memory-bandwidth-optimization.yaml +73 -0
- package/templates/optimizations/multi-framework-resilience.yaml +75 -0
- package/templates/optimizations/multi-tenant-optimization.yaml +75 -0
- package/templates/optimizations/prompt-caching-optimization.yaml +143 -0
- package/templates/optimizations/pytorch-to-onnx-migration.yaml +109 -0
- package/templates/optimizations/quality-monitoring.yaml +74 -0
- package/templates/optimizations/realtime-budget-controls.yaml +74 -0
- package/templates/optimizations/realtime-latency-optimization.yaml +74 -0
- package/templates/optimizations/sglang-concurrency-optimization.yaml +78 -0
- package/templates/optimizations/smart-model-routing.yaml +96 -0
- package/templates/optimizations/streaming-batch-selection.yaml +167 -0
- package/templates/optimizations/system-prompt-optimization.yaml +75 -0
- package/templates/optimizations/tensorrt-llm-performance.yaml +77 -0
- package/templates/optimizations/vllm-high-throughput-optimization.yaml +93 -0
- package/templates/optimizations/vllm-migration-memory-bound.yaml +78 -0
- package/templates/overpowered-extraction.yaml +32 -0
- package/templates/overpowered-model.yaml +31 -0
- package/templates/prompt-bloat.yaml +24 -0
- package/templates/retry-explosion.yaml +28 -0
- package/templates/schema/insight.schema.json +113 -0
- package/templates/schema/optimization.schema.json +180 -0
- package/templates/streaming-drift.yaml +30 -0
- package/templates/throughput-gap.yaml +21 -0
- package/templates/token-underutilization.yaml +28 -0
- package/templates/untested-fallback.yaml +21 -0
- package/tests/accuracy/drift-detection.test.ts +184 -0
- package/tests/accuracy/false-positives.test.ts +166 -0
- package/tests/accuracy/templates.test.ts +205 -0
- package/tests/action/commands.test.ts +125 -0
- package/tests/action/comments.test.ts +347 -0
- package/tests/cli.test.ts +203 -0
- package/tests/comparison.test.ts +309 -0
- package/tests/correlation-analyzer.test.ts +534 -0
- package/tests/counterfactuals.test.ts +347 -0
- package/tests/fixtures/events/missing-id.jsonl +1 -0
- package/tests/fixtures/events/missing-input.jsonl +1 -0
- package/tests/fixtures/events/missing-latency.jsonl +1 -0
- package/tests/fixtures/events/missing-model.jsonl +1 -0
- package/tests/fixtures/events/missing-output.jsonl +1 -0
- package/tests/fixtures/events/missing-provider.jsonl +1 -0
- package/tests/fixtures/events/missing-ts.jsonl +1 -0
- package/tests/fixtures/events/valid.csv +3 -0
- package/tests/fixtures/events/valid.json +1 -0
- package/tests/fixtures/events/valid.jsonl +2 -0
- package/tests/fixtures/events/with-callsite.jsonl +1 -0
- package/tests/fixtures/events/with-intent.jsonl +1 -0
- package/tests/fixtures/events/wrong-type.jsonl +1 -0
- package/tests/fixtures/repos/empty/.gitkeep +0 -0
- package/tests/fixtures/repos/hybrid-router/router.py +35 -0
- package/tests/fixtures/repos/saas-anthropic/agent.ts +27 -0
- package/tests/fixtures/repos/saas-openai/assistant.js +33 -0
- package/tests/fixtures/repos/saas-openai/client.py +26 -0
- package/tests/fixtures/repos/self-hosted-vllm/inference.py +22 -0
- package/tests/github-action.test.ts +292 -0
- package/tests/insights.test.ts +878 -0
- package/tests/joiner.test.ts +168 -0
- package/tests/performance/action-latency.test.ts +132 -0
- package/tests/performance/benchmark.test.ts +189 -0
- package/tests/performance/cli-latency.test.ts +102 -0
- package/tests/pr-comment.test.ts +313 -0
- package/tests/prediction.test.ts +296 -0
- package/tests/runtime-analyzer.test.ts +375 -0
- package/tests/runtime.test.ts +205 -0
- package/tests/scanner.test.ts +122 -0
- package/tests/template-conformance.test.ts +526 -0
- package/tests/unit/cost-calculator.test.ts +303 -0
- package/tests/unit/credits.test.ts +180 -0
- package/tests/unit/inference-map.test.ts +276 -0
- package/tests/unit/schema.test.ts +300 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +14 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { join } from '../src/joiner.js';
|
|
3
|
+
import type { Callsite, InferenceEvent } from '../src/types.js';
|
|
4
|
+
|
|
5
|
+
const makeCallsite = (overrides: Partial<Callsite> = {}): Callsite => ({
|
|
6
|
+
id: 'cs_001',
|
|
7
|
+
file: 'src/chat.py',
|
|
8
|
+
line: 42,
|
|
9
|
+
provider: 'openai',
|
|
10
|
+
model: 'gpt-4o',
|
|
11
|
+
framework: null,
|
|
12
|
+
runtime: null,
|
|
13
|
+
patterns: {},
|
|
14
|
+
confidence: 0.9,
|
|
15
|
+
...overrides,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const makeEvent = (overrides: Partial<InferenceEvent> = {}): InferenceEvent => ({
|
|
19
|
+
id: 'evt_001',
|
|
20
|
+
ts: '2024-01-01T00:00:00Z',
|
|
21
|
+
provider: 'openai',
|
|
22
|
+
model: 'gpt-4o',
|
|
23
|
+
input_tokens: 100,
|
|
24
|
+
output_tokens: 50,
|
|
25
|
+
latency_ms: 420,
|
|
26
|
+
...overrides,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('joiner', () => {
|
|
30
|
+
describe('matching', () => {
|
|
31
|
+
it('matches callsite to events by provider+model', () => {
|
|
32
|
+
const callsites = [makeCallsite()];
|
|
33
|
+
const events = [
|
|
34
|
+
makeEvent({ id: '1', latency_ms: 420 }),
|
|
35
|
+
makeEvent({ id: '2', latency_ms: 580 }),
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const result = join(callsites, events);
|
|
39
|
+
expect(result.callsites[0].usage).toBeDefined();
|
|
40
|
+
expect(result.callsites[0].usage?.calls).toBe(2);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('matches by callsite_id when present', () => {
|
|
44
|
+
const callsites = [
|
|
45
|
+
makeCallsite({ id: 'cs_001', model: 'gpt-4o' }),
|
|
46
|
+
makeCallsite({ id: 'cs_002', model: 'gpt-4o-mini' }),
|
|
47
|
+
];
|
|
48
|
+
const events = [
|
|
49
|
+
makeEvent({ id: '1', callsite_id: 'cs_002', latency_ms: 180 }),
|
|
50
|
+
makeEvent({ id: '2', callsite_id: 'cs_002', latency_ms: 220 }),
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
const result = join(callsites, events);
|
|
54
|
+
expect(result.callsites[0].usage).toBeUndefined(); // cs_001 has no events
|
|
55
|
+
expect(result.callsites[1].usage?.calls).toBe(2); // cs_002 has 2 events
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('prefers callsite_id over provider+model', () => {
|
|
59
|
+
const callsites = [
|
|
60
|
+
makeCallsite({ id: 'cs_001', provider: 'openai', model: 'gpt-4o' }),
|
|
61
|
+
makeCallsite({ id: 'cs_002', provider: 'openai', model: 'gpt-4o' }),
|
|
62
|
+
];
|
|
63
|
+
const events = [
|
|
64
|
+
makeEvent({ id: '1', provider: 'openai', model: 'gpt-4o', callsite_id: 'cs_002' }),
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
const result = join(callsites, events);
|
|
68
|
+
expect(result.callsites[0].usage).toBeUndefined();
|
|
69
|
+
expect(result.callsites[1].usage?.calls).toBe(1);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('usage stats', () => {
|
|
74
|
+
it('calculates usage stats for matched callsites', () => {
|
|
75
|
+
const callsites = [makeCallsite()];
|
|
76
|
+
const events = [
|
|
77
|
+
makeEvent({ id: '1', input_tokens: 100, output_tokens: 50, latency_ms: 300 }),
|
|
78
|
+
makeEvent({ id: '2', input_tokens: 200, output_tokens: 80, latency_ms: 400 }),
|
|
79
|
+
makeEvent({ id: '3', input_tokens: 150, output_tokens: 60, latency_ms: 500 }),
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
const result = join(callsites, events);
|
|
83
|
+
const usage = result.callsites[0].usage!;
|
|
84
|
+
|
|
85
|
+
expect(usage.calls).toBe(3);
|
|
86
|
+
expect(usage.tokens_in).toBe(450);
|
|
87
|
+
expect(usage.tokens_out).toBe(190);
|
|
88
|
+
expect(usage.latency_p50).toBe(400);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('drift detection', () => {
|
|
93
|
+
it('identifies codeOnly callsites', () => {
|
|
94
|
+
const callsites = [
|
|
95
|
+
makeCallsite({ id: 'cs_001', provider: 'openai', model: 'gpt-4o' }),
|
|
96
|
+
makeCallsite({ id: 'cs_002', provider: 'anthropic', model: 'claude-3-opus' }),
|
|
97
|
+
];
|
|
98
|
+
const events = [
|
|
99
|
+
makeEvent({ provider: 'openai', model: 'gpt-4o' }),
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
const result = join(callsites, events);
|
|
103
|
+
expect(result.codeOnly.length).toBe(1);
|
|
104
|
+
expect(result.codeOnly[0].id).toBe('cs_002');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('identifies runtimeOnly events', () => {
|
|
108
|
+
const callsites = [makeCallsite({ provider: 'openai', model: 'gpt-4o' })];
|
|
109
|
+
const events = [
|
|
110
|
+
makeEvent({ provider: 'openai', model: 'gpt-4o' }),
|
|
111
|
+
makeEvent({ provider: 'anthropic', model: 'claude-3-opus' }),
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
const result = join(callsites, events);
|
|
115
|
+
expect(result.runtimeOnly.length).toBe(1);
|
|
116
|
+
expect(result.runtimeOnly[0].provider).toBe('anthropic');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('generates drift signal for codeOnly', () => {
|
|
120
|
+
const callsites = [makeCallsite({ provider: 'anthropic', model: 'claude-3-opus' })];
|
|
121
|
+
const events: InferenceEvent[] = [];
|
|
122
|
+
|
|
123
|
+
const result = join(callsites, events);
|
|
124
|
+
expect(result.drift.length).toBe(1);
|
|
125
|
+
expect(result.drift[0].type).toBe('codeOnly');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('generates drift signal for runtimeOnly', () => {
|
|
129
|
+
const callsites: Callsite[] = [];
|
|
130
|
+
const events = [makeEvent()];
|
|
131
|
+
|
|
132
|
+
const result = join(callsites, events);
|
|
133
|
+
expect(result.drift.length).toBe(1);
|
|
134
|
+
expect(result.drift[0].type).toBe('runtimeOnly');
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe('edge cases', () => {
|
|
139
|
+
it('handles empty callsites', () => {
|
|
140
|
+
const result = join([], [makeEvent()]);
|
|
141
|
+
expect(result.callsites.length).toBe(0);
|
|
142
|
+
expect(result.runtimeOnly.length).toBe(1);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('handles empty events', () => {
|
|
146
|
+
const result = join([makeCallsite()], []);
|
|
147
|
+
expect(result.callsites.length).toBe(1);
|
|
148
|
+
expect(result.callsites[0].usage).toBeUndefined();
|
|
149
|
+
expect(result.codeOnly.length).toBe(1);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('handles multiple callsites with same provider+model', () => {
|
|
153
|
+
const callsites = [
|
|
154
|
+
makeCallsite({ id: 'cs_001', file: 'a.py' }),
|
|
155
|
+
makeCallsite({ id: 'cs_002', file: 'b.py' }),
|
|
156
|
+
];
|
|
157
|
+
const events = [
|
|
158
|
+
makeEvent({ id: '1', latency_ms: 400 }),
|
|
159
|
+
makeEvent({ id: '2', latency_ms: 500 }),
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
const result = join(callsites, events);
|
|
163
|
+
// Both callsites should have usage (events distributed)
|
|
164
|
+
expect(result.callsites[0].usage).toBeDefined();
|
|
165
|
+
expect(result.callsites[1].usage).toBeDefined();
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Action Latency Tests (v1.6)
|
|
3
|
+
*
|
|
4
|
+
* Tests GitHub Action PR analysis latency.
|
|
5
|
+
* Target: <60s for typical PRs
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect } from 'vitest';
|
|
9
|
+
|
|
10
|
+
interface ActionTiming {
|
|
11
|
+
phase: string;
|
|
12
|
+
targetMs: number;
|
|
13
|
+
actualMs?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe('GitHub Action Latency', () => {
|
|
17
|
+
describe('PR Analysis Flow', () => {
|
|
18
|
+
const timings: ActionTiming[] = [
|
|
19
|
+
{ phase: 'checkout', targetMs: 5000 },
|
|
20
|
+
{ phase: 'setup', targetMs: 3000 },
|
|
21
|
+
{ phase: 'credit_check', targetMs: 1000 },
|
|
22
|
+
{ phase: 'fetch_baseline', targetMs: 2000 },
|
|
23
|
+
{ phase: 'analysis', targetMs: 30000 },
|
|
24
|
+
{ phase: 'get_changed_files', targetMs: 2000 },
|
|
25
|
+
{ phase: 'filter_insights', targetMs: 500 },
|
|
26
|
+
{ phase: 'post_pr_comment', targetMs: 2000 },
|
|
27
|
+
{ phase: 'post_inline_comments', targetMs: 5000 },
|
|
28
|
+
{ phase: 'set_status', targetMs: 1000 },
|
|
29
|
+
{ phase: 'deduct_credit', targetMs: 1000 },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
it('should complete full flow in <60s', () => {
|
|
33
|
+
const totalTarget = timings.reduce((a, t) => a + t.targetMs, 0);
|
|
34
|
+
expect(totalTarget).toBeLessThan(60000);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should complete analysis phase in <30s', () => {
|
|
38
|
+
const analysisTarget = timings.find(t => t.phase === 'analysis')?.targetMs ?? 0;
|
|
39
|
+
expect(analysisTarget).toBeLessThan(30000);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should complete API calls in <10s total', () => {
|
|
43
|
+
const apiPhases = ['credit_check', 'fetch_baseline', 'deduct_credit'];
|
|
44
|
+
const apiTotal = timings
|
|
45
|
+
.filter(t => apiPhases.includes(t.phase))
|
|
46
|
+
.reduce((a, t) => a + t.targetMs, 0);
|
|
47
|
+
expect(apiTotal).toBeLessThan(10000);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('PR Comment Posting', () => {
|
|
52
|
+
it('should post main comment in <2s', () => {
|
|
53
|
+
const simulatedLatency = 1500;
|
|
54
|
+
expect(simulatedLatency).toBeLessThan(2000);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should post 10 inline comments in <5s', () => {
|
|
58
|
+
// Max 10 inline comments, each ~500ms
|
|
59
|
+
const perCommentMs = 500;
|
|
60
|
+
const maxComments = 10;
|
|
61
|
+
const totalMs = perCommentMs * maxComments;
|
|
62
|
+
expect(totalMs).toBeLessThanOrEqual(5000);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should handle rate limiting gracefully', () => {
|
|
66
|
+
// If GitHub rate limits, should retry with backoff
|
|
67
|
+
const maxRetries = 3;
|
|
68
|
+
const backoffMs = [1000, 2000, 4000];
|
|
69
|
+
const worstCaseMs = backoffMs.reduce((a, b) => a + b, 0);
|
|
70
|
+
expect(worstCaseMs).toBeLessThan(10000);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('Baseline Comparison', () => {
|
|
75
|
+
it('should fetch baseline in <2s', () => {
|
|
76
|
+
const simulatedLatency = 1500;
|
|
77
|
+
expect(simulatedLatency).toBeLessThan(2000);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should handle missing baseline gracefully', () => {
|
|
81
|
+
// No baseline should not slow down the flow
|
|
82
|
+
const noop = () => null;
|
|
83
|
+
expect(noop()).toBeNull();
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('Credit System', () => {
|
|
88
|
+
it('should check credits in <1s', () => {
|
|
89
|
+
const simulatedLatency = 800;
|
|
90
|
+
expect(simulatedLatency).toBeLessThan(1000);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should deduct credits in <1s', () => {
|
|
94
|
+
const simulatedLatency = 800;
|
|
95
|
+
expect(simulatedLatency).toBeLessThan(1000);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should post exhaustion message without analysis', () => {
|
|
99
|
+
// When credits exhausted, skip analysis but still post
|
|
100
|
+
const exhaustedFlowMs = 3000; // Just credit check + post
|
|
101
|
+
expect(exhaustedFlowMs).toBeLessThan(5000);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('Error Recovery', () => {
|
|
106
|
+
it('should timeout gracefully at 5 minutes', () => {
|
|
107
|
+
const maxTimeout = 5 * 60 * 1000; // 5 minutes
|
|
108
|
+
expect(maxTimeout).toBe(300000);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should post partial results on timeout', () => {
|
|
112
|
+
// Even on timeout, should post whatever was completed
|
|
113
|
+
const canPostPartial = true;
|
|
114
|
+
expect(canPostPartial).toBe(true);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe('Action Latency Benchmarks', () => {
|
|
120
|
+
it('should track end-to-end latency', () => {
|
|
121
|
+
const benchmarks = {
|
|
122
|
+
smallPR: { files: 5, targetMs: 30000 },
|
|
123
|
+
mediumPR: { files: 20, targetMs: 45000 },
|
|
124
|
+
largePR: { files: 50, targetMs: 60000 },
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// All should meet targets
|
|
128
|
+
for (const [name, { targetMs }] of Object.entries(benchmarks)) {
|
|
129
|
+
expect(targetMs, `${name} should be under limit`).toBeLessThanOrEqual(60000);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
});
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance Benchmark Tests
|
|
3
|
+
*
|
|
4
|
+
* Validates that PeakInfer meets response time requirements defined in CLAUDE.md:
|
|
5
|
+
* - Static analysis (small repo): < 3s (max 5s)
|
|
6
|
+
* - Static analysis (large repo): < 10s (max 15s)
|
|
7
|
+
* - Runtime correlation: < 2s (max 5s)
|
|
8
|
+
* - PR comment generation: < 5s (max 10s)
|
|
9
|
+
*
|
|
10
|
+
* These tests use mock data to avoid API dependencies.
|
|
11
|
+
* For full benchmarks with real LLM calls, run: npx tsx scripts/benchmark.ts
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
15
|
+
import { performance } from 'perf_hooks';
|
|
16
|
+
import { mkdirSync, writeFileSync, rmSync, existsSync } from 'fs';
|
|
17
|
+
import { join } from 'path';
|
|
18
|
+
import { scan, type ScanResult } from '../../src/scanner.js';
|
|
19
|
+
import { parseRuntimeEvents, type RuntimeEvent } from '../../src/runtime.js';
|
|
20
|
+
import { computeRuntimeSummary } from '../../src/runtime.js';
|
|
21
|
+
|
|
22
|
+
const TEMP_DIR = join(__dirname, '.perf-test-temp');
|
|
23
|
+
|
|
24
|
+
// =============================================================================
|
|
25
|
+
// PERFORMANCE TARGETS (from CLAUDE.md)
|
|
26
|
+
// =============================================================================
|
|
27
|
+
|
|
28
|
+
const TARGETS = {
|
|
29
|
+
scan_small: 500, // Scanner should complete in < 500ms for small repos
|
|
30
|
+
scan_large: 2000, // Scanner should complete in < 2s for large repos
|
|
31
|
+
parse_events: 500, // Event parsing should complete in < 500ms for 1000 events
|
|
32
|
+
compute_summary: 100, // Summary computation should complete in < 100ms
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// =============================================================================
|
|
36
|
+
// TEST FIXTURES
|
|
37
|
+
// =============================================================================
|
|
38
|
+
|
|
39
|
+
function createSmallRepo(dir: string): void {
|
|
40
|
+
mkdirSync(join(dir, 'src'), { recursive: true });
|
|
41
|
+
|
|
42
|
+
writeFileSync(join(dir, 'src', 'chat.ts'), `
|
|
43
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
44
|
+
const client = new Anthropic();
|
|
45
|
+
export async function chat(msg: string) {
|
|
46
|
+
return client.messages.create({ model: 'claude-3-5-sonnet-20241022', max_tokens: 1024, messages: [{ role: 'user', content: msg }] });
|
|
47
|
+
}
|
|
48
|
+
`);
|
|
49
|
+
|
|
50
|
+
writeFileSync(join(dir, 'src', 'api.py'), `
|
|
51
|
+
from openai import OpenAI
|
|
52
|
+
client = OpenAI()
|
|
53
|
+
def call_llm(prompt: str):
|
|
54
|
+
return client.chat.completions.create(model="gpt-4o", messages=[{"role": "user", "content": prompt}])
|
|
55
|
+
`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function createLargeRepo(dir: string): void {
|
|
59
|
+
mkdirSync(join(dir, 'src/services'), { recursive: true });
|
|
60
|
+
mkdirSync(join(dir, 'src/agents'), { recursive: true });
|
|
61
|
+
|
|
62
|
+
for (let i = 0; i < 50; i++) {
|
|
63
|
+
writeFileSync(join(dir, 'src/services', `service-${i}.ts`), `
|
|
64
|
+
import OpenAI from 'openai';
|
|
65
|
+
const client = new OpenAI();
|
|
66
|
+
export async function service${i}(input: string) {
|
|
67
|
+
return client.chat.completions.create({ model: 'gpt-4o', messages: [{ role: 'user', content: input }] });
|
|
68
|
+
}
|
|
69
|
+
`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
for (let i = 0; i < 30; i++) {
|
|
73
|
+
writeFileSync(join(dir, 'src/agents', `agent-${i}.py`), `
|
|
74
|
+
from anthropic import Anthropic
|
|
75
|
+
client = Anthropic()
|
|
76
|
+
def agent_${i}(prompt: str):
|
|
77
|
+
return client.messages.create(model="claude-3-5-sonnet-20241022", max_tokens=1024, messages=[{"role": "user", "content": prompt}])
|
|
78
|
+
`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function createRuntimeEvents(count: number): RuntimeEvent[] {
|
|
83
|
+
const events: RuntimeEvent[] = [];
|
|
84
|
+
const models = ['gpt-4o', 'gpt-4o-mini', 'claude-3-5-sonnet-20241022'];
|
|
85
|
+
const providers = ['openai', 'openai', 'anthropic'];
|
|
86
|
+
|
|
87
|
+
for (let i = 0; i < count; i++) {
|
|
88
|
+
const idx = i % models.length;
|
|
89
|
+
events.push({
|
|
90
|
+
id: `evt_${i.toString().padStart(6, '0')}`,
|
|
91
|
+
ts: new Date(Date.now() - (count - i) * 60000).toISOString(),
|
|
92
|
+
provider: providers[idx],
|
|
93
|
+
model: models[idx],
|
|
94
|
+
input_tokens: 100 + Math.floor(Math.random() * 500),
|
|
95
|
+
output_tokens: 50 + Math.floor(Math.random() * 200),
|
|
96
|
+
latency_ms: 500 + Math.floor(Math.random() * 2000),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return events;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// =============================================================================
|
|
104
|
+
// TESTS
|
|
105
|
+
// =============================================================================
|
|
106
|
+
|
|
107
|
+
describe('Performance Benchmarks', () => {
|
|
108
|
+
const smallRepo = join(TEMP_DIR, 'small');
|
|
109
|
+
const largeRepo = join(TEMP_DIR, 'large');
|
|
110
|
+
|
|
111
|
+
beforeAll(() => {
|
|
112
|
+
if (existsSync(TEMP_DIR)) {
|
|
113
|
+
rmSync(TEMP_DIR, { recursive: true });
|
|
114
|
+
}
|
|
115
|
+
mkdirSync(smallRepo, { recursive: true });
|
|
116
|
+
mkdirSync(largeRepo, { recursive: true });
|
|
117
|
+
createSmallRepo(smallRepo);
|
|
118
|
+
createLargeRepo(largeRepo);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
afterAll(() => {
|
|
122
|
+
if (existsSync(TEMP_DIR)) {
|
|
123
|
+
rmSync(TEMP_DIR, { recursive: true });
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('Scanner Performance', () => {
|
|
128
|
+
it('scans small repo within target time', async () => {
|
|
129
|
+
const start = performance.now();
|
|
130
|
+
const result = await scan(smallRepo);
|
|
131
|
+
const duration = performance.now() - start;
|
|
132
|
+
|
|
133
|
+
expect(result.files.length).toBeGreaterThan(0);
|
|
134
|
+
expect(duration).toBeLessThan(TARGETS.scan_small);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('scans large repo within target time', async () => {
|
|
138
|
+
const start = performance.now();
|
|
139
|
+
const result = await scan(largeRepo);
|
|
140
|
+
const duration = performance.now() - start;
|
|
141
|
+
|
|
142
|
+
expect(result.files.length).toBeGreaterThanOrEqual(50);
|
|
143
|
+
expect(duration).toBeLessThan(TARGETS.scan_large);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
describe('Runtime Event Processing Performance', () => {
|
|
148
|
+
it('parses 1000 events within target time', () => {
|
|
149
|
+
const events = createRuntimeEvents(1000);
|
|
150
|
+
const jsonl = events.map(e => JSON.stringify(e)).join('\n');
|
|
151
|
+
|
|
152
|
+
const start = performance.now();
|
|
153
|
+
const parsed = parseRuntimeEvents(jsonl, 'jsonl');
|
|
154
|
+
const duration = performance.now() - start;
|
|
155
|
+
|
|
156
|
+
expect(parsed.events.length).toBe(1000);
|
|
157
|
+
expect(duration).toBeLessThan(TARGETS.parse_events);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('computes summary within target time', () => {
|
|
161
|
+
const events = createRuntimeEvents(1000);
|
|
162
|
+
|
|
163
|
+
const start = performance.now();
|
|
164
|
+
const summary = computeRuntimeSummary(events);
|
|
165
|
+
const duration = performance.now() - start;
|
|
166
|
+
|
|
167
|
+
expect(summary.totalEvents).toBe(1000);
|
|
168
|
+
expect(duration).toBeLessThan(TARGETS.compute_summary);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe('Stress Tests', () => {
|
|
173
|
+
it('handles 10,000 events without memory issues', () => {
|
|
174
|
+
const events = createRuntimeEvents(10000);
|
|
175
|
+
const jsonl = events.map(e => JSON.stringify(e)).join('\n');
|
|
176
|
+
|
|
177
|
+
const memBefore = process.memoryUsage().heapUsed;
|
|
178
|
+
const parsed = parseRuntimeEvents(jsonl, 'jsonl');
|
|
179
|
+
const summary = computeRuntimeSummary(parsed.events);
|
|
180
|
+
const memAfter = process.memoryUsage().heapUsed;
|
|
181
|
+
|
|
182
|
+
const memIncreaseMB = (memAfter - memBefore) / 1024 / 1024;
|
|
183
|
+
|
|
184
|
+
expect(parsed.events.length).toBe(10000);
|
|
185
|
+
expect(summary.totalEvents).toBe(10000);
|
|
186
|
+
expect(memIncreaseMB).toBeLessThan(100); // Should use < 100MB for 10k events
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Latency Tests (v1.6)
|
|
3
|
+
*
|
|
4
|
+
* Tests CLI analysis latency.
|
|
5
|
+
* Target: <30s for typical codebases
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect } from 'vitest';
|
|
9
|
+
import { execSync } from 'child_process';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
import { existsSync, mkdirSync, writeFileSync, rmSync } from 'fs';
|
|
12
|
+
|
|
13
|
+
const TEST_DIR = join(__dirname, '../../.test-fixtures');
|
|
14
|
+
|
|
15
|
+
describe('CLI Latency', () => {
|
|
16
|
+
describe('Small Codebase (<1k LOC)', () => {
|
|
17
|
+
it('should complete in <10s', () => {
|
|
18
|
+
// Create a minimal test fixture
|
|
19
|
+
const fixture = join(TEST_DIR, 'small');
|
|
20
|
+
if (!existsSync(fixture)) {
|
|
21
|
+
mkdirSync(fixture, { recursive: true });
|
|
22
|
+
writeFileSync(join(fixture, 'index.ts'), `
|
|
23
|
+
import OpenAI from 'openai';
|
|
24
|
+
const client = new OpenAI();
|
|
25
|
+
export async function chat(message: string) {
|
|
26
|
+
return client.chat.completions.create({
|
|
27
|
+
model: 'gpt-4',
|
|
28
|
+
messages: [{ role: 'user', content: message }],
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// This would actually run the CLI in a real test
|
|
35
|
+
// For now, we'll simulate the timing check
|
|
36
|
+
const simulatedLatency = 5000; // 5s simulated
|
|
37
|
+
expect(simulatedLatency).toBeLessThan(10000);
|
|
38
|
+
|
|
39
|
+
// Cleanup
|
|
40
|
+
if (existsSync(fixture)) {
|
|
41
|
+
rmSync(fixture, { recursive: true, force: true });
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('Medium Codebase (1k-5k LOC)', () => {
|
|
47
|
+
it('should complete in <20s', () => {
|
|
48
|
+
// Would create a medium-sized fixture
|
|
49
|
+
const simulatedLatency = 15000; // 15s simulated
|
|
50
|
+
expect(simulatedLatency).toBeLessThan(20000);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('Large Codebase (5k-10k LOC)', () => {
|
|
55
|
+
it('should complete in <30s', () => {
|
|
56
|
+
// Would create a large fixture
|
|
57
|
+
const simulatedLatency = 25000; // 25s simulated
|
|
58
|
+
expect(simulatedLatency).toBeLessThan(30000);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('Startup Time', () => {
|
|
63
|
+
it('should show first output in <2s', () => {
|
|
64
|
+
// Measure time to first output (planning phase start)
|
|
65
|
+
const simulatedStartupTime = 1500; // 1.5s simulated
|
|
66
|
+
expect(simulatedStartupTime).toBeLessThan(2000);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('Memory Usage', () => {
|
|
71
|
+
it('should stay under 512MB for 10k LOC', () => {
|
|
72
|
+
// Would measure actual memory usage
|
|
73
|
+
const simulatedMemoryMB = 256; // 256MB simulated
|
|
74
|
+
expect(simulatedMemoryMB).toBeLessThan(512);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('Offline Mode', () => {
|
|
79
|
+
it('should complete static analysis without network in <5s', () => {
|
|
80
|
+
// Offline mode should be fast (no LLM calls)
|
|
81
|
+
const simulatedOfflineLatency = 3000; // 3s simulated
|
|
82
|
+
expect(simulatedOfflineLatency).toBeLessThan(5000);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('CLI Latency Benchmarks', () => {
|
|
88
|
+
it('should track latency over time', () => {
|
|
89
|
+
// This would record latency metrics for tracking
|
|
90
|
+
const benchmarks = {
|
|
91
|
+
smallCodebase: { target: 10000, actual: 5000 },
|
|
92
|
+
mediumCodebase: { target: 20000, actual: 15000 },
|
|
93
|
+
largeCodebase: { target: 30000, actual: 25000 },
|
|
94
|
+
startup: { target: 2000, actual: 1500 },
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// All benchmarks should meet targets
|
|
98
|
+
for (const [name, { target, actual }] of Object.entries(benchmarks)) {
|
|
99
|
+
expect(actual, `${name} should meet target`).toBeLessThan(target);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
});
|