capsule-ai-cli
Version:
The AI Model Orchestrator - Intelligent multi-model workflows with device-locked licensing
170 lines ⢠6.62 kB
JavaScript
import chalk from 'chalk';
import { v4 as uuidv4 } from 'uuid';
import { serviceLocator } from './service-locator.js';
export class ChatService {
messages = [];
currentModel = 'gpt-4o';
sessionId = uuidv4();
startTime = new Date();
async initialize() {
try {
const licenseService = serviceLocator.getLicense();
const { valid, license } = await licenseService.validate();
if (!valid) {
console.log(chalk.yellow('\nš Using Capsule CLI Free Edition'));
console.log(chalk.dim('Upgrade to Pro for advanced features: Model Fusion, Smart Router, and more!'));
console.log(chalk.dim('Learn more: capsule activate <license-key>\n'));
}
else {
console.log(chalk.green(`\n⨠Capsule CLI ${license.tier.toUpperCase()} Edition`));
}
const analytics = await serviceLocator.getAnalytics();
analytics.track({
event: 'session_start',
sessionId: this.sessionId,
timestamp: new Date(),
properties: {
edition: license?.tier || 'free'
}
});
const router = await serviceLocator.getModelRouter();
const models = await router.listAvailableModels();
if (models.length === 0) {
return {
success: false,
error: 'No AI providers configured. Set API keys for OpenAI, Anthropic, or other providers.'
};
}
return { success: true };
}
catch (error) {
return {
success: false,
error: `Failed to initialize: ${error.message}`
};
}
}
async sendMessage(content, options = {}) {
const startTime = Date.now();
this.messages.push({
role: 'user',
content
});
try {
const router = await serviceLocator.getModelRouter();
const analytics = await serviceLocator.getAnalytics();
const config = serviceLocator.getConfig();
const estimate = await router.estimateCost({
model: this.currentModel,
messages: this.messages,
stream: options.stream
});
if (config.get('ui.showCosts')) {
console.log(chalk.dim(`\nEstimated cost: ~$${estimate.estimatedCost.toFixed(4)}...`));
}
const response = await router.route({
model: this.currentModel,
messages: this.messages,
stream: options.stream
});
console.log(chalk.cyan('\nš¤ Assistant:'));
console.log(response.content);
console.log();
this.messages.push({
role: 'assistant',
content: response.content
});
const duration = Date.now() - startTime;
await analytics.track({
event: 'model_completion',
sessionId: this.sessionId,
timestamp: new Date(),
properties: {
model: this.currentModel,
provider: response.provider,
promptTokens: response.usage.promptTokens,
completionTokens: response.usage.completionTokens,
totalTokens: response.usage.totalTokens,
cost: estimate.estimatedCost,
duration
}
});
if (config.get('ui.showCosts')) {
console.log(chalk.gray(`Cost: $${estimate.estimatedCost.toFixed(4)} | ` +
`Tokens: ${response.usage.totalTokens} | ` +
`Time: ${(duration / 1000).toFixed(1)}s`));
}
}
catch (error) {
console.log(chalk.red('\nā Error:'), error.message);
const analytics = await serviceLocator.getAnalytics();
await analytics.track({
event: 'error',
sessionId: this.sessionId,
timestamp: new Date(),
properties: {
error: error.message,
model: this.currentModel
}
});
if (error.message.includes('401')) {
console.log(chalk.yellow('Invalid API key. Please check your API keys.'));
}
else if (error.message.includes('429')) {
console.log(chalk.yellow('Rate limit exceeded. Please try again later.'));
}
else if (error.message.includes('network')) {
console.log(chalk.yellow('Network error. Please check your connection.'));
}
}
}
clearHistory() {
this.messages = [];
console.log(chalk.green('ā Conversation history cleared'));
}
async showStats() {
const analytics = await serviceLocator.getAnalytics();
const insights = await analytics.getInsights('week');
console.log(chalk.cyan('\nš Weekly Usage Stats:'));
console.log(chalk.dim('ā'.repeat(40)));
console.log(`Sessions: ${insights.totalSessions}`);
console.log(`Queries: ${insights.totalQueries}`);
console.log(`Avg Response Time: ${(insights.averageResponseTime / 1000).toFixed(1)}s`);
console.log(chalk.dim('ā'.repeat(40)));
console.log('\nTop Models:');
Object.entries(insights.modelUsage)
.sort(([, a], [, b]) => b - a)
.slice(0, 3)
.forEach(([model, count]) => {
console.log(` ${model}: ${count} requests`);
});
console.log();
}
async cleanup() {
const analytics = await serviceLocator.getAnalytics();
await analytics.track({
event: 'session_end',
sessionId: this.sessionId,
timestamp: new Date(),
properties: {
duration: Date.now() - this.startTime.getTime(),
messageCount: this.messages.length
}
});
}
setModel(model) {
this.currentModel = model;
console.log(chalk.green(`ā Model set to: ${model}`));
}
getHistory() {
return this.messages;
}
addSystemMessage(content) {
this.messages.push({
role: 'system',
content
});
}
}
export const chatService = new ChatService();
//# sourceMappingURL=chat-service.js.map