tachibot-mcp 2.0.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/.env.example +260 -0
- package/CHANGELOG.md +54 -0
- package/CODE_OF_CONDUCT.md +56 -0
- package/CONTRIBUTING.md +54 -0
- package/Dockerfile +36 -0
- package/LICENSE +644 -0
- package/README.md +201 -0
- package/SECURITY.md +95 -0
- package/dist/personality/komaai-expressions.js +12 -0
- package/dist/profiles/balanced.json +33 -0
- package/dist/profiles/code_focus.json +33 -0
- package/dist/profiles/full.json +33 -0
- package/dist/profiles/minimal.json +33 -0
- package/dist/profiles/research_power.json +33 -0
- package/dist/scripts/build-profiles.js +46 -0
- package/dist/src/application/services/focus/FocusModeRegistry.js +46 -0
- package/dist/src/application/services/focus/FocusTool.service.js +109 -0
- package/dist/src/application/services/focus/ModeRegistry.js +46 -0
- package/dist/src/application/services/focus/modes/focus-deep.mode.js +27 -0
- package/dist/src/application/services/focus/modes/status.mode.js +50 -0
- package/dist/src/application/services/focus/modes/tachibot-status.mode.js +50 -0
- package/dist/src/collaborative-orchestrator.js +391 -0
- package/dist/src/config/model-constants.js +188 -0
- package/dist/src/config/model-defaults.js +57 -0
- package/dist/src/config/model-preferences.js +382 -0
- package/dist/src/config/timeout-config.js +130 -0
- package/dist/src/config.js +173 -0
- package/dist/src/domain/interfaces/IFocusMode.js +5 -0
- package/dist/src/domain/interfaces/IProvider.js +6 -0
- package/dist/src/domain/interfaces/ITool.js +5 -0
- package/dist/src/focus-deep.js +245 -0
- package/dist/src/infrastructure/ascii/art/robots.ascii.js +16 -0
- package/dist/src/mcp-client.js +90 -0
- package/dist/src/memory/index.js +17 -0
- package/dist/src/memory/memory-config.js +135 -0
- package/dist/src/memory/memory-interface.js +174 -0
- package/dist/src/memory/memory-manager.js +383 -0
- package/dist/src/memory/providers/devlog-provider.js +385 -0
- package/dist/src/memory/providers/hybrid-provider.js +399 -0
- package/dist/src/memory/providers/local-provider.js +388 -0
- package/dist/src/memory/providers/mem0-provider.js +337 -0
- package/dist/src/modes/architect.js +477 -0
- package/dist/src/modes/auditor.js +362 -0
- package/dist/src/modes/challenger.js +841 -0
- package/dist/src/modes/code-reviewer.js +382 -0
- package/dist/src/modes/commit-guardian.js +424 -0
- package/dist/src/modes/documentation-writer.js +572 -0
- package/dist/src/modes/scout.js +587 -0
- package/dist/src/modes/shared/helpers/challenger-helpers.js +454 -0
- package/dist/src/modes/shared/helpers/index.js +17 -0
- package/dist/src/modes/shared/helpers/scout-helpers.js +270 -0
- package/dist/src/modes/shared/helpers/verifier-helpers.js +332 -0
- package/dist/src/modes/test-architect.js +767 -0
- package/dist/src/modes/verifier.js +378 -0
- package/dist/src/monitoring/performance-monitor.js +435 -0
- package/dist/src/optimization/batch-executor.js +121 -0
- package/dist/src/optimization/context-pruner.js +196 -0
- package/dist/src/optimization/cost-monitor.js +338 -0
- package/dist/src/optimization/index.js +65 -0
- package/dist/src/optimization/model-router.js +264 -0
- package/dist/src/optimization/result-cache.js +114 -0
- package/dist/src/optimization/token-optimizer.js +257 -0
- package/dist/src/optimization/token-tracker.js +118 -0
- package/dist/src/orchestrator-instructions.js +128 -0
- package/dist/src/orchestrator-lite.js +139 -0
- package/dist/src/orchestrator.js +191 -0
- package/dist/src/orchestrators/collaborative/interfaces/IToolExecutionEngine.js +1 -0
- package/dist/src/orchestrators/collaborative/interfaces/IToolExecutionStrategy.js +5 -0
- package/dist/src/orchestrators/collaborative/interfaces/IVisualizationRenderer.js +1 -0
- package/dist/src/orchestrators/collaborative/registries/ModelProviderRegistry.js +95 -0
- package/dist/src/orchestrators/collaborative/registries/ToolAdapterRegistry.js +64 -0
- package/dist/src/orchestrators/collaborative/services/tool-execution/ToolExecutionService.js +502 -0
- package/dist/src/orchestrators/collaborative/services/visualization/VisualizationService.js +206 -0
- package/dist/src/orchestrators/collaborative/types/session-types.js +5 -0
- package/dist/src/profiles/balanced.js +37 -0
- package/dist/src/profiles/code_focus.js +37 -0
- package/dist/src/profiles/debug_intensive.js +59 -0
- package/dist/src/profiles/full.js +37 -0
- package/dist/src/profiles/minimal.js +37 -0
- package/dist/src/profiles/research_code.js +59 -0
- package/dist/src/profiles/research_power.js +37 -0
- package/dist/src/profiles/types.js +5 -0
- package/dist/src/profiles/workflow_builder.js +53 -0
- package/dist/src/prompt-engineer-lite.js +78 -0
- package/dist/src/prompt-engineer.js +399 -0
- package/dist/src/reasoning-chain.js +508 -0
- package/dist/src/sequential-thinking.js +291 -0
- package/dist/src/server-diagnostic.js +74 -0
- package/dist/src/server-raw.js +158 -0
- package/dist/src/server-simple.js +58 -0
- package/dist/src/server.js +514 -0
- package/dist/src/session/session-logger.js +617 -0
- package/dist/src/session/session-manager.js +571 -0
- package/dist/src/session/session-tools.js +400 -0
- package/dist/src/tools/advanced-modes.js +200 -0
- package/dist/src/tools/claude-integration.js +356 -0
- package/dist/src/tools/consolidated/ai-router.js +174 -0
- package/dist/src/tools/consolidated/ai-tool.js +48 -0
- package/dist/src/tools/consolidated/brainstorm-tool.js +87 -0
- package/dist/src/tools/consolidated/environment-detector.js +80 -0
- package/dist/src/tools/consolidated/index.js +50 -0
- package/dist/src/tools/consolidated/search-tool.js +110 -0
- package/dist/src/tools/consolidated/workflow-tool.js +238 -0
- package/dist/src/tools/gemini-tools.js +329 -0
- package/dist/src/tools/grok-enhanced.js +376 -0
- package/dist/src/tools/grok-tools.js +299 -0
- package/dist/src/tools/lmstudio-tools.js +223 -0
- package/dist/src/tools/openai-tools.js +498 -0
- package/dist/src/tools/openrouter-tools.js +317 -0
- package/dist/src/tools/optimized-wrapper.js +204 -0
- package/dist/src/tools/perplexity-tools.js +294 -0
- package/dist/src/tools/pingpong-tool.js +343 -0
- package/dist/src/tools/qwen-wrapper.js +74 -0
- package/dist/src/tools/tool-router.js +444 -0
- package/dist/src/tools/unified-ai-provider.js +260 -0
- package/dist/src/tools/workflow-runner.js +425 -0
- package/dist/src/tools/workflow-validator-tool.js +107 -0
- package/dist/src/types.js +23 -0
- package/dist/src/utils/input-validator.js +130 -0
- package/dist/src/utils/model-router.js +91 -0
- package/dist/src/utils/progress-stream.js +255 -0
- package/dist/src/utils/provider-router.js +88 -0
- package/dist/src/utils/smart-api-client.js +146 -0
- package/dist/src/utils/table-builder.js +218 -0
- package/dist/src/utils/timestamp-formatter.js +134 -0
- package/dist/src/utils/tool-compressor.js +257 -0
- package/dist/src/utils/tool-config.js +201 -0
- package/dist/src/validators/dependency-graph-validator.js +147 -0
- package/dist/src/validators/interpolation-validator.js +222 -0
- package/dist/src/validators/output-usage-validator.js +151 -0
- package/dist/src/validators/syntax-validator.js +102 -0
- package/dist/src/validators/tool-registry-validator.js +123 -0
- package/dist/src/validators/tool-types.js +97 -0
- package/dist/src/validators/types.js +8 -0
- package/dist/src/validators/workflow-validator.js +134 -0
- package/dist/src/visualizer-lite.js +42 -0
- package/dist/src/visualizer.js +179 -0
- package/dist/src/workflows/circuit-breaker.js +199 -0
- package/dist/src/workflows/custom-workflows.js +451 -0
- package/dist/src/workflows/engine/AutoSynthesizer.js +97 -0
- package/dist/src/workflows/engine/StepParameterResolver.js +74 -0
- package/dist/src/workflows/engine/VariableInterpolator.js +123 -0
- package/dist/src/workflows/engine/WorkflowDiscovery.js +125 -0
- package/dist/src/workflows/engine/WorkflowExecutionEngine.js +485 -0
- package/dist/src/workflows/engine/WorkflowExecutor.js +113 -0
- package/dist/src/workflows/engine/WorkflowFileManager.js +244 -0
- package/dist/src/workflows/engine/WorkflowHelpers.js +114 -0
- package/dist/src/workflows/engine/WorkflowOutputFormatter.js +83 -0
- package/dist/src/workflows/engine/events/WorkflowEventBus.js +132 -0
- package/dist/src/workflows/engine/events/interfaces/IEventBus.js +5 -0
- package/dist/src/workflows/engine/handlers/ErrorRecoveryHandler.js +162 -0
- package/dist/src/workflows/engine/handlers/PromptEnhancementHandler.js +115 -0
- package/dist/src/workflows/engine/handlers/SessionPersistenceHandler.js +167 -0
- package/dist/src/workflows/engine/handlers/StepExecutionHandler.js +231 -0
- package/dist/src/workflows/engine/handlers/ToolInvocationHandler.js +46 -0
- package/dist/src/workflows/engine/interfaces/IAutoSynthesizer.js +5 -0
- package/dist/src/workflows/engine/interfaces/IStepParameterResolver.js +5 -0
- package/dist/src/workflows/engine/interfaces/IVariableInterpolator.js +5 -0
- package/dist/src/workflows/engine/interfaces/IWorkflowDiscovery.js +4 -0
- package/dist/src/workflows/engine/interfaces/IWorkflowFileManager.js +5 -0
- package/dist/src/workflows/engine/interfaces/IWorkflowOutputFormatter.js +5 -0
- package/dist/src/workflows/engine/state/WorkflowStateMachine.js +194 -0
- package/dist/src/workflows/engine/state/interfaces/IStateMachine.js +17 -0
- package/dist/src/workflows/fallback-strategies.js +373 -0
- package/dist/src/workflows/message-queue.js +455 -0
- package/dist/src/workflows/model-router.js +189 -0
- package/dist/src/workflows/orchestrator-examples.js +174 -0
- package/dist/src/workflows/orchestrator-integration.js +200 -0
- package/dist/src/workflows/self-healing.js +524 -0
- package/dist/src/workflows/tool-mapper.js +407 -0
- package/dist/src/workflows/tool-orchestrator.js +796 -0
- package/dist/src/workflows/workflow-engine.js +573 -0
- package/dist/src/workflows/workflow-parser.js +283 -0
- package/dist/src/workflows/workflow-types.js +95 -0
- package/dist/src/workflows.js +568 -0
- package/dist/test-workflow-file-output.js +93 -0
- package/docs/API_KEYS.md +570 -0
- package/docs/CLAUDE_CODE_SETUP.md +181 -0
- package/docs/CLAUDE_DESKTOP_MANUAL.md +127 -0
- package/docs/CONFIGURATION.md +745 -0
- package/docs/FOCUS_MODES.md +240 -0
- package/docs/INSTALLATION_BOTH.md +145 -0
- package/docs/TERMS.md +352 -0
- package/docs/TOOLS_REFERENCE.md +1622 -0
- package/docs/TOOL_PARAMETERS.md +496 -0
- package/docs/TOOL_PROFILES.md +236 -0
- package/docs/WORKFLOWS.md +987 -0
- package/docs/WORKFLOW_OUTPUT.md +198 -0
- package/docs/WORKFLOW_PROGRESS_TRACKING.md +305 -0
- package/docs/workflows/design-brainstorm.md +335 -0
- package/package.json +97 -0
- package/profiles/balanced.json +37 -0
- package/profiles/code_focus.json +37 -0
- package/profiles/debug_intensive.json +34 -0
- package/profiles/full.json +37 -0
- package/profiles/minimal.json +37 -0
- package/profiles/research_power.json +37 -0
- package/profiles/workflow_builder.json +37 -0
- package/smithery.yaml +66 -0
- package/start.sh +8 -0
- package/tools.config.json +81 -0
- package/tsconfig.json +18 -0
- package/workflows/accessibility-code-audit.yaml +92 -0
- package/workflows/code-architecture-review.yaml +202 -0
- package/workflows/code-review.yaml +142 -0
- package/workflows/core/iterative-problem-solver.yaml +283 -0
- package/workflows/creative-brainstorm-yaml.yaml +215 -0
- package/workflows/pingpong.yaml +141 -0
- package/workflows/system/README.md +412 -0
- package/workflows/system/challenger.yaml +175 -0
- package/workflows/system/scout.yaml +164 -0
- package/workflows/system/verifier.yaml +133 -0
- package/workflows/ultra-creative-brainstorm.yaml +318 -0
- package/workflows/ux-research-flow.yaml +92 -0
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
/**
|
|
3
|
+
* Performance monitoring for workflow execution
|
|
4
|
+
*/
|
|
5
|
+
export class PerformanceMonitor extends EventEmitter {
|
|
6
|
+
constructor() {
|
|
7
|
+
super();
|
|
8
|
+
this.metrics = [];
|
|
9
|
+
this.systemMetrics = [];
|
|
10
|
+
this.costModels = new Map();
|
|
11
|
+
this.activeRequests = new Map();
|
|
12
|
+
this.metricsRetention = 3600000; // 1 hour default
|
|
13
|
+
this.initializeCostModels();
|
|
14
|
+
this.startAggregation();
|
|
15
|
+
this.startSystemMonitoring();
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Initialize cost models for different AI providers
|
|
19
|
+
*/
|
|
20
|
+
initializeCostModels() {
|
|
21
|
+
// GPT-5 flagship
|
|
22
|
+
this.costModels.set('gpt5', {
|
|
23
|
+
model: 'gpt5',
|
|
24
|
+
inputTokenCost: 0.00125,
|
|
25
|
+
outputTokenCost: 0.01
|
|
26
|
+
});
|
|
27
|
+
// Gemini 2.5 Pro
|
|
28
|
+
this.costModels.set('gemini_25_pro', {
|
|
29
|
+
model: 'gemini_25_pro',
|
|
30
|
+
inputTokenCost: 0.00125,
|
|
31
|
+
outputTokenCost: 0.005
|
|
32
|
+
});
|
|
33
|
+
// GPT-5-mini (cost-effective, balanced)
|
|
34
|
+
this.costModels.set('gpt5_mini', {
|
|
35
|
+
model: 'gpt5_mini',
|
|
36
|
+
inputTokenCost: 0.00075,
|
|
37
|
+
outputTokenCost: 0.003
|
|
38
|
+
});
|
|
39
|
+
// Perplexity
|
|
40
|
+
this.costModels.set('perplexity', {
|
|
41
|
+
model: 'perplexity',
|
|
42
|
+
inputTokenCost: 0.001,
|
|
43
|
+
outputTokenCost: 0.001
|
|
44
|
+
});
|
|
45
|
+
// Focus deep reasoning (expensive)
|
|
46
|
+
this.costModels.set('focus', {
|
|
47
|
+
model: 'focus',
|
|
48
|
+
inputTokenCost: 0.005,
|
|
49
|
+
outputTokenCost: 0.02,
|
|
50
|
+
baseCost: 0.01
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Start tracking a request
|
|
55
|
+
*/
|
|
56
|
+
startRequest(requestId, tool) {
|
|
57
|
+
this.activeRequests.set(requestId, {
|
|
58
|
+
start: Date.now(),
|
|
59
|
+
tool
|
|
60
|
+
});
|
|
61
|
+
this.emit('request-started', { requestId, tool });
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Complete tracking a request
|
|
65
|
+
*/
|
|
66
|
+
completeRequest(requestId, success, tokensUsed, error, metadata) {
|
|
67
|
+
const request = this.activeRequests.get(requestId);
|
|
68
|
+
if (!request)
|
|
69
|
+
return;
|
|
70
|
+
const duration = Date.now() - request.start;
|
|
71
|
+
const cost = this.calculateCost(request.tool, tokensUsed);
|
|
72
|
+
const metric = {
|
|
73
|
+
timestamp: Date.now(),
|
|
74
|
+
requestId,
|
|
75
|
+
tool: request.tool,
|
|
76
|
+
duration,
|
|
77
|
+
tokensUsed: {
|
|
78
|
+
input: tokensUsed?.input || 0,
|
|
79
|
+
output: tokensUsed?.output || 0,
|
|
80
|
+
total: (tokensUsed?.input || 0) + (tokensUsed?.output || 0)
|
|
81
|
+
},
|
|
82
|
+
cost,
|
|
83
|
+
success,
|
|
84
|
+
error,
|
|
85
|
+
metadata
|
|
86
|
+
};
|
|
87
|
+
this.metrics.push(metric);
|
|
88
|
+
this.activeRequests.delete(requestId);
|
|
89
|
+
// Clean old metrics
|
|
90
|
+
this.cleanOldMetrics();
|
|
91
|
+
// Emit events
|
|
92
|
+
this.emit('request-completed', metric);
|
|
93
|
+
// Check for performance issues
|
|
94
|
+
this.checkPerformanceAlerts(metric);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Calculate cost based on token usage
|
|
98
|
+
*/
|
|
99
|
+
calculateCost(tool, tokensUsed) {
|
|
100
|
+
if (!tokensUsed)
|
|
101
|
+
return 0;
|
|
102
|
+
const costModel = this.costModels.get(tool);
|
|
103
|
+
if (!costModel)
|
|
104
|
+
return 0;
|
|
105
|
+
const inputCost = (tokensUsed.input / 1000) * costModel.inputTokenCost;
|
|
106
|
+
const outputCost = (tokensUsed.output / 1000) * costModel.outputTokenCost;
|
|
107
|
+
const baseCost = costModel.baseCost || 0;
|
|
108
|
+
return inputCost + outputCost + baseCost;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Check for performance alerts
|
|
112
|
+
*/
|
|
113
|
+
checkPerformanceAlerts(metric) {
|
|
114
|
+
// Slow response alert
|
|
115
|
+
if (metric.duration > 30000) {
|
|
116
|
+
this.emit('slow-response-alert', {
|
|
117
|
+
tool: metric.tool,
|
|
118
|
+
duration: metric.duration,
|
|
119
|
+
requestId: metric.requestId
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
// High cost alert
|
|
123
|
+
if (metric.cost > 0.1) {
|
|
124
|
+
this.emit('high-cost-alert', {
|
|
125
|
+
tool: metric.tool,
|
|
126
|
+
cost: metric.cost,
|
|
127
|
+
tokens: metric.tokensUsed.total,
|
|
128
|
+
requestId: metric.requestId
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
// High token usage alert (potential 25k limit issue)
|
|
132
|
+
if (metric.tokensUsed.total > 20000) {
|
|
133
|
+
this.emit('high-token-alert', {
|
|
134
|
+
tool: metric.tool,
|
|
135
|
+
tokens: metric.tokensUsed.total,
|
|
136
|
+
requestId: metric.requestId,
|
|
137
|
+
warning: 'Approaching 25k token limit'
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
// Error rate alert
|
|
141
|
+
if (!metric.success) {
|
|
142
|
+
const recentErrors = this.getRecentErrorRate();
|
|
143
|
+
if (recentErrors > 0.3) {
|
|
144
|
+
this.emit('high-error-rate', {
|
|
145
|
+
errorRate: recentErrors,
|
|
146
|
+
tool: metric.tool
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Get recent error rate
|
|
153
|
+
*/
|
|
154
|
+
getRecentErrorRate() {
|
|
155
|
+
const recent = this.getRecentMetrics(5); // Last 5 minutes
|
|
156
|
+
if (recent.length === 0)
|
|
157
|
+
return 0;
|
|
158
|
+
const errors = recent.filter(m => !m.success).length;
|
|
159
|
+
return errors / recent.length;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get recent metrics
|
|
163
|
+
*/
|
|
164
|
+
getRecentMetrics(minutes) {
|
|
165
|
+
const cutoff = Date.now() - (minutes * 60 * 1000);
|
|
166
|
+
return this.metrics.filter(m => m.timestamp > cutoff);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Start aggregation interval
|
|
170
|
+
*/
|
|
171
|
+
startAggregation() {
|
|
172
|
+
this.aggregationInterval = setInterval(() => {
|
|
173
|
+
const aggregated = this.aggregateMetrics();
|
|
174
|
+
this.emit('metrics-aggregated', aggregated);
|
|
175
|
+
}, 60000); // Every minute
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Start system monitoring
|
|
179
|
+
*/
|
|
180
|
+
startSystemMonitoring() {
|
|
181
|
+
this.systemMonitorInterval = setInterval(() => {
|
|
182
|
+
const metrics = this.collectSystemMetrics();
|
|
183
|
+
this.systemMetrics.push(metrics);
|
|
184
|
+
this.emit('system-metrics', metrics);
|
|
185
|
+
// Clean old system metrics
|
|
186
|
+
const cutoff = Date.now() - this.metricsRetention;
|
|
187
|
+
this.systemMetrics = this.systemMetrics.filter(m => m.timestamp > cutoff);
|
|
188
|
+
}, 10000); // Every 10 seconds
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Collect system metrics
|
|
192
|
+
*/
|
|
193
|
+
collectSystemMetrics() {
|
|
194
|
+
const memUsage = process.memoryUsage();
|
|
195
|
+
const cpuUsage = process.cpuUsage();
|
|
196
|
+
return {
|
|
197
|
+
timestamp: Date.now(),
|
|
198
|
+
activeTasks: this.activeRequests.size,
|
|
199
|
+
queuedTasks: 0, // Would come from message queue
|
|
200
|
+
memoryUsage: memUsage.heapUsed / 1024 / 1024, // MB
|
|
201
|
+
cpuUsage: (cpuUsage.user + cpuUsage.system) / 1000000, // seconds
|
|
202
|
+
networkLatency: 0, // Would need actual measurement
|
|
203
|
+
errorRate: this.getRecentErrorRate(),
|
|
204
|
+
systemHealth: this.calculateSystemHealth()
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Calculate system health score
|
|
209
|
+
*/
|
|
210
|
+
calculateSystemHealth() {
|
|
211
|
+
let health = 1.0;
|
|
212
|
+
// Deduct for high error rate
|
|
213
|
+
const errorRate = this.getRecentErrorRate();
|
|
214
|
+
if (errorRate > 0.1)
|
|
215
|
+
health -= 0.3;
|
|
216
|
+
if (errorRate > 0.3)
|
|
217
|
+
health -= 0.3;
|
|
218
|
+
// Deduct for slow responses
|
|
219
|
+
const recent = this.getRecentMetrics(5);
|
|
220
|
+
const avgDuration = recent.length > 0
|
|
221
|
+
? recent.reduce((sum, m) => sum + m.duration, 0) / recent.length
|
|
222
|
+
: 0;
|
|
223
|
+
if (avgDuration > 10000)
|
|
224
|
+
health -= 0.2;
|
|
225
|
+
if (avgDuration > 30000)
|
|
226
|
+
health -= 0.2;
|
|
227
|
+
// Deduct for high memory usage
|
|
228
|
+
const memUsage = process.memoryUsage().heapUsed / 1024 / 1024;
|
|
229
|
+
if (memUsage > 500)
|
|
230
|
+
health -= 0.1;
|
|
231
|
+
if (memUsage > 1000)
|
|
232
|
+
health -= 0.2;
|
|
233
|
+
return Math.max(health, 0);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Aggregate metrics by tool
|
|
237
|
+
*/
|
|
238
|
+
aggregateMetrics() {
|
|
239
|
+
const aggregated = new Map();
|
|
240
|
+
const recent = this.getRecentMetrics(60); // Last hour
|
|
241
|
+
// Group by tool
|
|
242
|
+
const byTool = new Map();
|
|
243
|
+
for (const metric of recent) {
|
|
244
|
+
if (!byTool.has(metric.tool)) {
|
|
245
|
+
byTool.set(metric.tool, []);
|
|
246
|
+
}
|
|
247
|
+
byTool.get(metric.tool).push(metric);
|
|
248
|
+
}
|
|
249
|
+
// Aggregate each tool
|
|
250
|
+
for (const [tool, metrics] of byTool.entries()) {
|
|
251
|
+
const durations = metrics.map(m => m.duration).sort((a, b) => a - b);
|
|
252
|
+
const successful = metrics.filter(m => m.success).length;
|
|
253
|
+
const agg = {
|
|
254
|
+
tool,
|
|
255
|
+
totalRequests: metrics.length,
|
|
256
|
+
successRate: successful / metrics.length,
|
|
257
|
+
avgDuration: durations.reduce((a, b) => a + b, 0) / durations.length,
|
|
258
|
+
p50Duration: this.percentile(durations, 0.5),
|
|
259
|
+
p95Duration: this.percentile(durations, 0.95),
|
|
260
|
+
p99Duration: this.percentile(durations, 0.99),
|
|
261
|
+
totalTokens: metrics.reduce((sum, m) => sum + m.tokensUsed.total, 0),
|
|
262
|
+
totalCost: metrics.reduce((sum, m) => sum + m.cost, 0),
|
|
263
|
+
avgTokensPerRequest: 0,
|
|
264
|
+
avgCostPerRequest: 0,
|
|
265
|
+
throughput: 0
|
|
266
|
+
};
|
|
267
|
+
agg.avgTokensPerRequest = agg.totalTokens / metrics.length;
|
|
268
|
+
agg.avgCostPerRequest = agg.totalCost / metrics.length;
|
|
269
|
+
// Calculate throughput (requests per second)
|
|
270
|
+
if (metrics.length > 1) {
|
|
271
|
+
const timeSpan = metrics[metrics.length - 1].timestamp - metrics[0].timestamp;
|
|
272
|
+
agg.throughput = (metrics.length / timeSpan) * 1000;
|
|
273
|
+
}
|
|
274
|
+
aggregated.set(tool, agg);
|
|
275
|
+
}
|
|
276
|
+
return aggregated;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Calculate percentile
|
|
280
|
+
*/
|
|
281
|
+
percentile(sorted, p) {
|
|
282
|
+
if (sorted.length === 0)
|
|
283
|
+
return 0;
|
|
284
|
+
const index = Math.ceil(sorted.length * p) - 1;
|
|
285
|
+
return sorted[Math.max(0, Math.min(index, sorted.length - 1))];
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Clean old metrics
|
|
289
|
+
*/
|
|
290
|
+
cleanOldMetrics() {
|
|
291
|
+
const cutoff = Date.now() - this.metricsRetention;
|
|
292
|
+
this.metrics = this.metrics.filter(m => m.timestamp > cutoff);
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Get dashboard data
|
|
296
|
+
*/
|
|
297
|
+
getDashboardData() {
|
|
298
|
+
const aggregated = this.aggregateMetrics();
|
|
299
|
+
const recent = this.getRecentMetrics(5);
|
|
300
|
+
const latestSystem = this.systemMetrics[this.systemMetrics.length - 1] || null;
|
|
301
|
+
// Calculate cost breakdown
|
|
302
|
+
const costBreakdown = [];
|
|
303
|
+
let totalCost = 0;
|
|
304
|
+
for (const [tool, metrics] of aggregated.entries()) {
|
|
305
|
+
totalCost += metrics.totalCost;
|
|
306
|
+
costBreakdown.push({
|
|
307
|
+
tool,
|
|
308
|
+
cost: metrics.totalCost,
|
|
309
|
+
percentage: 0
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
// Calculate percentages
|
|
313
|
+
for (const item of costBreakdown) {
|
|
314
|
+
item.percentage = totalCost > 0 ? (item.cost / totalCost) * 100 : 0;
|
|
315
|
+
}
|
|
316
|
+
// Sort by cost
|
|
317
|
+
costBreakdown.sort((a, b) => b.cost - a.cost);
|
|
318
|
+
// Generate alerts
|
|
319
|
+
const alerts = [];
|
|
320
|
+
if (latestSystem) {
|
|
321
|
+
if (latestSystem.errorRate > 0.3) {
|
|
322
|
+
alerts.push(`High error rate: ${(latestSystem.errorRate * 100).toFixed(1)}%`);
|
|
323
|
+
}
|
|
324
|
+
if (latestSystem.systemHealth < 0.5) {
|
|
325
|
+
alerts.push(`Low system health: ${(latestSystem.systemHealth * 100).toFixed(0)}%`);
|
|
326
|
+
}
|
|
327
|
+
if (latestSystem.memoryUsage > 1000) {
|
|
328
|
+
alerts.push(`High memory usage: ${latestSystem.memoryUsage.toFixed(0)}MB`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
// Check for expensive tools
|
|
332
|
+
for (const [tool, metrics] of aggregated.entries()) {
|
|
333
|
+
if (metrics.avgCostPerRequest > 0.05) {
|
|
334
|
+
alerts.push(`High cost for ${tool}: $${metrics.avgCostPerRequest.toFixed(3)}/request`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return {
|
|
338
|
+
currentRequests: this.activeRequests.size,
|
|
339
|
+
recentMetrics: recent,
|
|
340
|
+
aggregatedMetrics: aggregated,
|
|
341
|
+
systemMetrics: latestSystem,
|
|
342
|
+
costBreakdown,
|
|
343
|
+
performanceAlerts: alerts
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Get cost optimization recommendations
|
|
348
|
+
*/
|
|
349
|
+
getCostOptimizationRecommendations() {
|
|
350
|
+
const recommendations = [];
|
|
351
|
+
const aggregated = this.aggregateMetrics();
|
|
352
|
+
for (const [tool, metrics] of aggregated.entries()) {
|
|
353
|
+
// Check for high token usage
|
|
354
|
+
if (metrics.avgTokensPerRequest > 10000) {
|
|
355
|
+
recommendations.push(`${tool}: High token usage (${metrics.avgTokensPerRequest.toFixed(0)} avg). Consider chunking or summarization.`);
|
|
356
|
+
}
|
|
357
|
+
// Check for low success rate
|
|
358
|
+
if (metrics.successRate < 0.8) {
|
|
359
|
+
recommendations.push(`${tool}: Low success rate (${(metrics.successRate * 100).toFixed(1)}%). Consider fallback strategies.`);
|
|
360
|
+
}
|
|
361
|
+
// Check for slow performance
|
|
362
|
+
if (metrics.avgDuration > 20000) {
|
|
363
|
+
recommendations.push(`${tool}: Slow performance (${(metrics.avgDuration / 1000).toFixed(1)}s avg). Consider timeout or alternative.`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
// General recommendations
|
|
367
|
+
if (this.getRecentErrorRate() > 0.2) {
|
|
368
|
+
recommendations.push('High overall error rate. Enable circuit breakers and fallback strategies.');
|
|
369
|
+
}
|
|
370
|
+
const totalCost = Array.from(aggregated.values())
|
|
371
|
+
.reduce((sum, m) => sum + m.totalCost, 0);
|
|
372
|
+
if (totalCost > 10) {
|
|
373
|
+
recommendations.push(`High cost in last hour: $${totalCost.toFixed(2)}. Review workflow efficiency.`);
|
|
374
|
+
}
|
|
375
|
+
return recommendations;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Export metrics for analysis
|
|
379
|
+
*/
|
|
380
|
+
exportMetrics(format = 'json') {
|
|
381
|
+
if (format === 'json') {
|
|
382
|
+
return JSON.stringify({
|
|
383
|
+
metrics: this.metrics,
|
|
384
|
+
systemMetrics: this.systemMetrics,
|
|
385
|
+
aggregated: Object.fromEntries(this.aggregateMetrics()),
|
|
386
|
+
exported: new Date().toISOString()
|
|
387
|
+
}, null, 2);
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
// CSV format
|
|
391
|
+
const headers = [
|
|
392
|
+
'timestamp', 'requestId', 'tool', 'duration',
|
|
393
|
+
'inputTokens', 'outputTokens', 'totalTokens',
|
|
394
|
+
'cost', 'success', 'error'
|
|
395
|
+
].join(',');
|
|
396
|
+
const rows = this.metrics.map(m => [
|
|
397
|
+
m.timestamp,
|
|
398
|
+
m.requestId,
|
|
399
|
+
m.tool,
|
|
400
|
+
m.duration,
|
|
401
|
+
m.tokensUsed.input,
|
|
402
|
+
m.tokensUsed.output,
|
|
403
|
+
m.tokensUsed.total,
|
|
404
|
+
m.cost.toFixed(4),
|
|
405
|
+
m.success,
|
|
406
|
+
m.error || ''
|
|
407
|
+
].join(','));
|
|
408
|
+
return [headers, ...rows].join('\n');
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Set metrics retention period
|
|
413
|
+
*/
|
|
414
|
+
setRetention(milliseconds) {
|
|
415
|
+
this.metricsRetention = milliseconds;
|
|
416
|
+
this.cleanOldMetrics();
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Cleanup
|
|
420
|
+
*/
|
|
421
|
+
cleanup() {
|
|
422
|
+
if (this.aggregationInterval) {
|
|
423
|
+
clearInterval(this.aggregationInterval);
|
|
424
|
+
}
|
|
425
|
+
if (this.systemMonitorInterval) {
|
|
426
|
+
clearInterval(this.systemMonitorInterval);
|
|
427
|
+
}
|
|
428
|
+
this.metrics = [];
|
|
429
|
+
this.systemMetrics = [];
|
|
430
|
+
this.activeRequests.clear();
|
|
431
|
+
this.removeAllListeners();
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
// Export singleton instance
|
|
435
|
+
export const performanceMonitor = new PerformanceMonitor();
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
export class BatchExecutor {
|
|
2
|
+
constructor(maxConcurrency, defaultTimeout) {
|
|
3
|
+
this.maxConcurrency = 5;
|
|
4
|
+
this.defaultTimeout = 30000;
|
|
5
|
+
if (maxConcurrency)
|
|
6
|
+
this.maxConcurrency = maxConcurrency;
|
|
7
|
+
if (defaultTimeout)
|
|
8
|
+
this.defaultTimeout = defaultTimeout;
|
|
9
|
+
}
|
|
10
|
+
async execute(tasks) {
|
|
11
|
+
const groups = this.groupTasksByDependencies(tasks);
|
|
12
|
+
const results = [];
|
|
13
|
+
for (const group of groups) {
|
|
14
|
+
const groupResults = await this.executeGroup(group);
|
|
15
|
+
results.push(...groupResults);
|
|
16
|
+
}
|
|
17
|
+
return results;
|
|
18
|
+
}
|
|
19
|
+
async executeParallel(tasks, maxConcurrency) {
|
|
20
|
+
const concurrency = maxConcurrency || this.maxConcurrency;
|
|
21
|
+
const results = [];
|
|
22
|
+
const queue = [...tasks].sort((a, b) => (b.priority || 0) - (a.priority || 0));
|
|
23
|
+
while (queue.length > 0) {
|
|
24
|
+
const batch = queue.splice(0, concurrency);
|
|
25
|
+
const batchResults = await Promise.all(batch.map(task => this.executeTask(task)));
|
|
26
|
+
results.push(...batchResults);
|
|
27
|
+
}
|
|
28
|
+
return results;
|
|
29
|
+
}
|
|
30
|
+
async executeGroup(tasks) {
|
|
31
|
+
const promises = tasks.map(task => this.executeTask(task));
|
|
32
|
+
return Promise.all(promises);
|
|
33
|
+
}
|
|
34
|
+
async executeTask(task) {
|
|
35
|
+
const startTime = Date.now();
|
|
36
|
+
const timeout = task.timeout || this.defaultTimeout;
|
|
37
|
+
try {
|
|
38
|
+
const result = await Promise.race([
|
|
39
|
+
task.fn(),
|
|
40
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), timeout))
|
|
41
|
+
]);
|
|
42
|
+
return {
|
|
43
|
+
id: task.id,
|
|
44
|
+
result,
|
|
45
|
+
duration: Date.now() - startTime,
|
|
46
|
+
status: 'success'
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const isTimeout = error instanceof Error && error.message === 'Timeout';
|
|
51
|
+
return {
|
|
52
|
+
id: task.id,
|
|
53
|
+
error: error,
|
|
54
|
+
duration: Date.now() - startTime,
|
|
55
|
+
status: isTimeout ? 'timeout' : 'error'
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
groupTasksByDependencies(tasks) {
|
|
60
|
+
const groups = [];
|
|
61
|
+
const completed = new Set();
|
|
62
|
+
const remaining = [...tasks];
|
|
63
|
+
while (remaining.length > 0) {
|
|
64
|
+
const group = [];
|
|
65
|
+
for (let i = remaining.length - 1; i >= 0; i--) {
|
|
66
|
+
const task = remaining[i];
|
|
67
|
+
const ready = !task.dependencies ||
|
|
68
|
+
task.dependencies.every(dep => completed.has(dep));
|
|
69
|
+
if (ready) {
|
|
70
|
+
group.push(task);
|
|
71
|
+
remaining.splice(i, 1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (group.length === 0 && remaining.length > 0) {
|
|
75
|
+
throw new Error('Circular dependency detected in batch tasks');
|
|
76
|
+
}
|
|
77
|
+
groups.push(group);
|
|
78
|
+
group.forEach(task => completed.add(task.id));
|
|
79
|
+
}
|
|
80
|
+
return groups;
|
|
81
|
+
}
|
|
82
|
+
generateReport(results) {
|
|
83
|
+
const successful = results.filter(r => r.status === 'success').length;
|
|
84
|
+
const failed = results.filter(r => r.status === 'error').length;
|
|
85
|
+
const timeouts = results.filter(r => r.status === 'timeout').length;
|
|
86
|
+
const totalDuration = Math.max(...results.map(r => r.duration));
|
|
87
|
+
return {
|
|
88
|
+
totalTasks: results.length,
|
|
89
|
+
successful,
|
|
90
|
+
failed,
|
|
91
|
+
timeouts,
|
|
92
|
+
totalDuration,
|
|
93
|
+
parallelGroups: Math.ceil(results.length / this.maxConcurrency)
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
async executeWithRetry(task, maxRetries = 3, backoff = 1000) {
|
|
97
|
+
let lastError;
|
|
98
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
99
|
+
if (attempt > 0) {
|
|
100
|
+
await new Promise(resolve => setTimeout(resolve, backoff * attempt));
|
|
101
|
+
}
|
|
102
|
+
const result = await this.executeTask(task);
|
|
103
|
+
if (result.status === 'success') {
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
lastError = result.error;
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
id: task.id,
|
|
110
|
+
error: lastError || new Error('Max retries exceeded'),
|
|
111
|
+
duration: 0,
|
|
112
|
+
status: 'error'
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
setMaxConcurrency(max) {
|
|
116
|
+
this.maxConcurrency = max;
|
|
117
|
+
}
|
|
118
|
+
setDefaultTimeout(timeout) {
|
|
119
|
+
this.defaultTimeout = timeout;
|
|
120
|
+
}
|
|
121
|
+
}
|