automagik-cli
Version:
Automagik CLI - A powerful command-line interface for interacting with Automagik Hive multi-agent AI systems
383 lines (382 loc) ⢠16.7 kB
JavaScript
import { request as gaxiosRequest } from 'gaxios';
import { appConfig } from './settings.js';
// Essential events we care about - filter out the noise
const ESSENTIAL_EVENTS = new Set([
'TeamRunResponseContent',
'RunResponseContent',
'TeamRunStarted',
'RunStarted',
'TeamToolCallStarted',
'ToolCallStarted',
'ToolCallCompleted',
'TeamToolCallCompleted',
'TeamRunCompleted',
'RunCompleted',
'MemoryUpdateCompleted',
'RAGQueryCompleted',
'ThinkingStarted',
'ThinkingCompleted'
]);
export class OptimizedAPIClient {
baseUrl;
constructor() {
this.baseUrl = appConfig.apiBaseUrl;
}
getDefaultHeaders() {
const headers = {
'accept': 'application/json',
};
if (appConfig.apiKey) {
headers['x-api-key'] = appConfig.apiKey;
}
return headers;
}
createStreamParser() {
let buffer = '';
let stats = {
startTime: Date.now(),
agentCalls: 0,
toolCalls: 0,
memoryUpdates: 0,
ragQueries: 0,
toolMetrics: [],
eventCounts: {}
};
return {
stats,
parseChunk(chunk) {
buffer += chunk;
const events = [];
// Optimized parsing - find complete JSON objects
let start = 0;
let braceCount = 0;
let inString = false;
let escapeNext = false;
for (let i = 0; i < buffer.length; i++) {
const char = buffer[i];
if (!inString) {
if (char === '{') {
if (braceCount === 0)
start = i;
braceCount++;
}
else if (char === '}') {
braceCount--;
if (braceCount === 0) {
// Found complete object
const jsonStr = buffer.substring(start, i + 1);
try {
const event = JSON.parse(jsonStr);
// Update stats
stats.eventCounts[event.event] = (stats.eventCounts[event.event] || 0) + 1;
// Only process essential events
if (ESSENTIAL_EVENTS.has(event.event)) {
events.push(event);
// Collect metrics
if (event.event === 'RunStarted')
stats.agentCalls++;
if (event.event === 'ToolCallCompleted' || event.event === 'TeamToolCallCompleted') {
stats.toolCalls++;
if (event.tool?.metrics?.time) {
stats.toolMetrics.push({
name: event.tool.tool_name || event.tool_name || 'unknown',
duration: event.tool.metrics.time * 1000
});
}
}
if (event.event === 'MemoryUpdateCompleted')
stats.memoryUpdates++;
if (event.event === 'RAGQueryCompleted')
stats.ragQueries++;
// Capture final metrics
if (event.event === 'TeamRunCompleted' || event.event === 'RunCompleted') {
stats.endTime = Date.now();
stats.totalDuration = stats.endTime - stats.startTime;
stats.finalMetrics = event.metrics || event.run_metrics || {};
}
}
}
catch (e) {
// Invalid JSON, skip
}
}
}
else if (char === '"' && !escapeNext) {
inString = true;
}
}
else {
if (char === '"' && !escapeNext) {
inString = false;
}
escapeNext = char === '\\' && !escapeNext;
}
}
// Keep remaining buffer
if (braceCount === 0) {
buffer = '';
}
else {
buffer = buffer.substring(start);
}
return events;
}
};
}
async streamAgent(request, onMessage, onError, onComplete, abortSignal) {
try {
const formData = new FormData();
formData.append('message', request.message);
formData.append('stream', 'true');
formData.append('monitor', 'true');
if (request.session_id) {
formData.append('session_id', request.session_id);
}
if (request.user_id) {
formData.append('user_id', request.user_id);
}
const url = `${this.baseUrl}/playground/agents/${request.agent_id}/runs`;
const response = await gaxiosRequest({
url,
method: 'POST',
data: formData,
headers: this.getDefaultHeaders(),
responseType: 'stream',
timeout: 0,
signal: abortSignal,
});
const parser = this.createStreamParser();
let sessionId = request.session_id;
response.data.on('data', (chunk) => {
const events = parser.parseChunk(chunk.toString());
for (const event of events) {
// Extract session_id
if (event.session_id && !sessionId) {
sessionId = event.session_id;
}
// Handle content events - extract both content and thinking
if (event.event === 'TeamRunResponseContent' || event.event === 'RunResponseContent') {
const textContent = event.thinking || event.content || '';
if (textContent && event.content_type === 'str') {
onMessage({
content: textContent,
done: false,
session_id: sessionId,
metadata: { type: 'content' }
});
}
}
// Handle essential status events (reduced verbosity)
else if (event.event === 'TeamRunStarted') {
onMessage({
content: `šµ ${event.team_name || 'Team'}`,
done: false,
session_id: sessionId,
metadata: { type: 'team_start' }
});
}
else if (event.event === 'RunStarted') {
onMessage({
content: `š¤ ${event.agent_name || 'Agent'}`,
done: false,
session_id: sessionId,
metadata: { type: 'agent_start' }
});
}
else if (event.event === 'ToolCallStarted' || event.event === 'TeamToolCallStarted') {
// Skip showing tool starts to reduce noise
}
else if (event.event === 'ToolCallCompleted' || event.event === 'TeamToolCallCompleted') {
const toolName = event.tool?.tool_name || event.tool_name || 'unknown';
const duration = event.tool?.metrics?.time ? ` (${(event.tool.metrics.time * 1000).toFixed(0)}ms)` : '';
onMessage({
content: `ā
${toolName}${duration}`,
done: false,
session_id: sessionId,
metadata: { type: 'tool_complete' }
});
}
else if (event.event === 'TeamRunCompleted' || event.event === 'RunCompleted') {
// Extract final response content if available
if (event.content && event.content.length > 10) {
onMessage({
content: event.content,
done: false,
session_id: sessionId,
metadata: { type: 'content' }
});
}
onComplete(parser.stats);
return;
}
}
});
response.data.on('end', () => {
// Stats already handled in completion event
});
response.data.on('error', (error) => {
onError(error);
});
}
catch (error) {
onError(error instanceof Error ? error : new Error('Unknown streaming error'));
}
}
async streamTeam(request, onMessage, onError, onComplete, abortSignal) {
try {
const formData = new FormData();
formData.append('message', request.message);
formData.append('stream', 'true');
formData.append('monitor', 'true');
if (request.session_id) {
formData.append('session_id', request.session_id);
}
if (request.user_id) {
formData.append('user_id', request.user_id);
}
const url = `${this.baseUrl}/playground/teams/${request.team_id}/runs`;
const response = await gaxiosRequest({
url,
method: 'POST',
data: formData,
headers: this.getDefaultHeaders(),
responseType: 'stream',
timeout: 0,
signal: abortSignal,
});
const parser = this.createStreamParser();
let sessionId = request.session_id;
response.data.on('data', (chunk) => {
const events = parser.parseChunk(chunk.toString());
for (const event of events) {
// Extract session_id
if (event.session_id && !sessionId) {
sessionId = event.session_id;
}
// Handle content events - extract both content and thinking
if (event.event === 'TeamRunResponseContent' || event.event === 'RunResponseContent') {
const textContent = event.thinking || event.content || '';
if (textContent && event.content_type === 'str') {
onMessage({
content: textContent,
done: false,
session_id: sessionId,
metadata: { type: 'content' }
});
}
}
// Handle essential status events (reduced verbosity)
else if (event.event === 'TeamRunStarted') {
onMessage({
content: `šµ ${event.team_name || 'Team'}`,
done: false,
session_id: sessionId,
metadata: { type: 'team_start' }
});
}
else if (event.event === 'RunStarted') {
onMessage({
content: `š¤ ${event.agent_name || 'Agent'}`,
done: false,
session_id: sessionId,
metadata: { type: 'agent_start' }
});
}
else if (event.event === 'ToolCallCompleted' || event.event === 'TeamToolCallCompleted') {
const toolName = event.tool?.tool_name || event.tool_name || 'unknown';
const duration = event.tool?.metrics?.time ? ` (${(event.tool.metrics.time * 1000).toFixed(0)}ms)` : '';
onMessage({
content: `ā
${toolName}${duration}`,
done: false,
session_id: sessionId,
metadata: { type: 'tool_complete' }
});
}
else if (event.event === 'TeamRunCompleted') {
onComplete(parser.stats);
return;
}
}
});
response.data.on('end', () => {
// Stats already handled in completion event
});
response.data.on('error', (error) => {
onError(error);
});
}
catch (error) {
onError(error instanceof Error ? error : new Error('Unknown streaming error'));
}
}
// Use the original client methods for non-streaming operations
async listAgents() {
const url = `${this.baseUrl}/playground/agents`;
const response = await gaxiosRequest({
url,
method: 'GET',
headers: this.getDefaultHeaders(),
timeout: 5000, // Reduced timeout
});
return { data: response.data };
}
async listTeams() {
const url = `${this.baseUrl}/playground/teams`;
const response = await gaxiosRequest({
url,
method: 'GET',
headers: this.getDefaultHeaders(),
timeout: 5000,
});
return { data: response.data };
}
async listWorkflows() {
const url = `${this.baseUrl}/playground/workflows`;
const response = await gaxiosRequest({
url,
method: 'GET',
headers: this.getDefaultHeaders(),
timeout: 5000,
});
return { data: response.data };
}
async healthCheck() {
const url = `${this.baseUrl}/api/v1/health`;
const response = await gaxiosRequest({
url,
method: 'GET',
headers: this.getDefaultHeaders(),
timeout: 2000,
});
return { data: response.data };
}
}
export const optimizedAPIClient = new OptimizedAPIClient();
// Helper function to format stats
export function formatRunStats(stats) {
if (!stats)
return '';
const lines = [
'\nš Run Statistics:',
`ā±ļø Total Duration: ${stats.totalDuration ? (stats.totalDuration / 1000).toFixed(2) + 's' : 'N/A'}`,
`š¤ Agents Called: ${stats.agentCalls}`,
`š§ Tools Executed: ${stats.toolCalls}`,
`š§ Memory Updates: ${stats.memoryUpdates}`,
`š RAG Queries: ${stats.ragQueries}`,
];
if (stats.toolMetrics.length > 0) {
lines.push('\nā” Tool Performance:');
const sortedTools = stats.toolMetrics.sort((a, b) => b.duration - a.duration);
sortedTools.slice(0, 5).forEach(tool => {
lines.push(` ${tool.name}: ${tool.duration.toFixed(0)}ms`);
});
if (sortedTools.length > 5) {
lines.push(` ... and ${sortedTools.length - 5} more`);
}
}
if (stats.eventCounts && Object.keys(stats.eventCounts).length > 0) {
const totalEvents = Object.values(stats.eventCounts).reduce((sum, count) => sum + count, 0);
lines.push(`\nš Total Events Processed: ${totalEvents}`);
}
return lines.join('\n');
}