universal-ai-brain
Version:
🧠UNIVERSAL AI BRAIN 3.3 - The world's most advanced cognitive architecture with 24 specialized systems, MongoDB 8.1 $rankFusion hybrid search, latest Voyage 3.5 embeddings, and framework-agnostic design. Works with Mastra, Vercel AI, LangChain, OpenAI A
594 lines (515 loc) • 16.2 kB
text/typescript
/**
* @file ToolCollection - MongoDB collection operations for agent tools
*
* This class provides CRUD operations and specialized queries for agent tools,
* implementing tool execution tracking, rate limiting, and cost monitoring.
*/
import { Collection, Db, ObjectId } from 'mongodb';
import { AgentTool, ToolExecution, ToolStatus } from '../types/index';
import { BaseCollection } from './BaseCollection';
export interface ToolFilter {
agentId?: string | ObjectId;
status?: ToolStatus;
category?: string;
tags?: string[];
createdAfter?: Date;
createdBefore?: Date;
lastUsedAfter?: Date;
lastUsedBefore?: Date;
}
export interface ToolUpdateData {
name?: string;
description?: string;
status?: ToolStatus;
configuration?: Record<string, any>;
rateLimits?: {
maxCallsPerMinute?: number;
maxCallsPerHour?: number;
maxCallsPerDay?: number;
};
costTracking?: {
costPerCall?: number;
currency?: string;
};
tags?: string[];
metadata?: Record<string, any>;
lastUsedAt?: Date;
}
export interface ToolExecutionFilter {
toolId?: string | ObjectId;
agentId?: string | ObjectId;
status?: 'pending' | 'running' | 'completed' | 'failed';
executedAfter?: Date;
executedBefore?: Date;
}
/**
* ToolCollection - Complete CRUD operations for agent tools
*
* Features:
* - Tool lifecycle management
* - Execution tracking and monitoring
* - Rate limiting enforcement
* - Cost tracking and analysis
* - Performance metrics
*/
export class ToolCollection extends BaseCollection<AgentTool> {
protected collectionName = 'agent_tools';
private executionCollection: Collection<ToolExecution>;
constructor(db: Db) {
super(db);
this.initializeCollection();
this.executionCollection = db.collection<ToolExecution>('tool_executions');
}
/**
* Create a new tool
*/
async createTool(toolData: Omit<AgentTool, '_id' | 'createdAt' | 'updatedAt'>): Promise<AgentTool> {
const now = new Date();
const tool: AgentTool = {
...toolData,
_id: new ObjectId(),
createdAt: now,
updatedAt: now,
status: toolData.status || ToolStatus.ACTIVE,
executionCount: 0,
totalCost: 0,
metadata: toolData.metadata || {}
};
await this.validateDocument(tool);
const result = await this.collection.insertOne(tool);
if (!result.acknowledged) {
throw new Error('Failed to create tool');
}
return tool;
}
/**
* Get tool by ID
*/
async getTool(toolId: string | ObjectId): Promise<AgentTool | null> {
const objectId = typeof toolId === 'string' ? new ObjectId(toolId) : toolId;
return await this.collection.findOne({ _id: objectId });
}
/**
* Get tool by name and agent
*/
async getToolByName(name: string, agentId?: string | ObjectId): Promise<AgentTool | null> {
const filter: any = { name };
if (agentId) {
const objectId = typeof agentId === 'string' ? new ObjectId(agentId) : agentId;
filter.agentId = objectId;
}
return await this.collection.findOne(filter);
}
/**
* Update tool
*/
async updateTool(toolId: string | ObjectId, updateData: ToolUpdateData): Promise<AgentTool | null> {
const objectId = typeof toolId === 'string' ? new ObjectId(toolId) : toolId;
const now = new Date();
const updateDoc = {
...updateData,
updatedAt: now
};
const result = await this.collection.findOneAndUpdate(
{ _id: objectId },
{ $set: updateDoc },
{ returnDocument: 'after' }
);
return result.value;
}
/**
* Update tool status
*/
async updateToolStatus(toolId: string | ObjectId, status: ToolStatus): Promise<boolean> {
const objectId = typeof toolId === 'string' ? new ObjectId(toolId) : toolId;
const result = await this.collection.updateOne(
{ _id: objectId },
{
$set: {
status,
updatedAt: new Date()
}
}
);
return result.modifiedCount > 0;
}
/**
* Delete tool
*/
async deleteTool(toolId: string | ObjectId): Promise<boolean> {
const objectId = typeof toolId === 'string' ? new ObjectId(toolId) : toolId;
const result = await this.collection.deleteOne({ _id: objectId });
return result.deletedCount > 0;
}
/**
* List tools with filtering and pagination
*/
async listTools(
filter: ToolFilter = {},
options: {
limit?: number;
skip?: number;
sort?: Record<string, 1 | -1>;
} = {}
): Promise<{ tools: AgentTool[]; total: number }> {
const mongoFilter = this.buildMongoFilter(filter);
const { limit = 50, skip = 0, sort = { createdAt: -1 } } = options;
const [tools, total] = await Promise.all([
this.collection
.find(mongoFilter)
.sort(sort)
.skip(skip)
.limit(limit)
.toArray(),
this.collection.countDocuments(mongoFilter)
]);
return { tools, total };
}
/**
* Get tools by agent
*/
async getAgentTools(agentId: string | ObjectId, status?: ToolStatus): Promise<AgentTool[]> {
const objectId = typeof agentId === 'string' ? new ObjectId(agentId) : agentId;
const filter: any = { agentId: objectId };
if (status) {
filter.status = status;
}
return await this.collection
.find(filter)
.sort({ name: 1 })
.toArray();
}
/**
* Get tools by category
*/
async getToolsByCategory(category: string): Promise<AgentTool[]> {
return await this.collection
.find({ category })
.sort({ name: 1 })
.toArray();
}
/**
* Search tools
*/
async searchTools(query: string, limit: number = 20): Promise<AgentTool[]> {
const searchFilter = {
$or: [
{ name: { $regex: query, $options: 'i' } },
{ description: { $regex: query, $options: 'i' } },
{ category: { $regex: query, $options: 'i' } }
]
};
return await this.collection
.find(searchFilter)
.limit(limit)
.toArray();
}
/**
* Record tool execution
*/
async recordExecution(executionData: Omit<ToolExecution, '_id' | 'createdAt'>): Promise<ToolExecution> {
const now = new Date();
const execution: ToolExecution = {
...executionData,
_id: new ObjectId(),
createdAt: now
};
const result = await this.executionCollection.insertOne(execution);
if (!result.acknowledged) {
throw new Error('Failed to record tool execution');
}
// Update tool statistics
await this.updateToolStats(execution.toolId, execution.cost || 0);
return execution;
}
/**
* Update tool statistics after execution
*/
async updateToolStats(toolId: string | ObjectId, cost: number = 0): Promise<boolean> {
const objectId = typeof toolId === 'string' ? new ObjectId(toolId) : toolId;
const result = await this.collection.updateOne(
{ _id: objectId },
{
$inc: {
executionCount: 1,
totalCost: cost
},
$set: {
lastUsedAt: new Date(),
updatedAt: new Date()
}
}
);
return result.modifiedCount > 0;
}
/**
* Check rate limits for tool
*/
async checkRateLimit(toolId: string | ObjectId): Promise<{
allowed: boolean;
limits: {
perMinute: { current: number; max: number; allowed: boolean };
perHour: { current: number; max: number; allowed: boolean };
perDay: { current: number; max: number; allowed: boolean };
};
}> {
const objectId = typeof toolId === 'string' ? new ObjectId(toolId) : toolId;
// Get tool rate limits
const tool = await this.collection.findOne(
{ _id: objectId },
{ projection: { rateLimits: 1 } }
);
if (!tool?.rateLimits) {
return {
allowed: true,
limits: {
perMinute: { current: 0, max: Infinity, allowed: true },
perHour: { current: 0, max: Infinity, allowed: true },
perDay: { current: 0, max: Infinity, allowed: true }
}
};
}
const now = new Date();
const oneMinuteAgo = new Date(now.getTime() - 60 * 1000);
const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);
const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000);
// Count executions in different time windows
const [perMinute, perHour, perDay] = await Promise.all([
this.executionCollection.countDocuments({
toolId: objectId.toString(),
createdAt: { $gte: oneMinuteAgo }
}),
this.executionCollection.countDocuments({
toolId: objectId.toString(),
createdAt: { $gte: oneHourAgo }
}),
this.executionCollection.countDocuments({
toolId: objectId.toString(),
createdAt: { $gte: oneDayAgo }
})
]);
const limits = {
perMinute: {
current: perMinute,
max: tool.rateLimits.maxCallsPerMinute || Infinity,
allowed: perMinute < (tool.rateLimits.maxCallsPerMinute || Infinity)
},
perHour: {
current: perHour,
max: tool.rateLimits.maxCallsPerHour || Infinity,
allowed: perHour < (tool.rateLimits.maxCallsPerHour || Infinity)
},
perDay: {
current: perDay,
max: tool.rateLimits.maxCallsPerDay || Infinity,
allowed: perDay < (tool.rateLimits.maxCallsPerDay || Infinity)
}
};
const allowed = limits.perMinute.allowed && limits.perHour.allowed && limits.perDay.allowed;
return { allowed, limits };
}
/**
* Get tool executions
*/
async getToolExecutions(
filter: ToolExecutionFilter = {},
options: {
limit?: number;
skip?: number;
sort?: Record<string, 1 | -1>;
} = {}
): Promise<{ executions: ToolExecution[]; total: number }> {
const mongoFilter = this.buildExecutionFilter(filter);
const { limit = 50, skip = 0, sort = { createdAt: -1 } } = options;
const [executions, total] = await Promise.all([
this.executionCollection
.find(mongoFilter)
.sort(sort)
.skip(skip)
.limit(limit)
.toArray(),
this.executionCollection.countDocuments(mongoFilter)
]);
return { executions, total };
}
/**
* Get tool statistics
*/
async getToolStats(toolId?: string | ObjectId): Promise<{
total: number;
byStatus: Record<ToolStatus, number>;
byCategory: Record<string, number>;
totalExecutions: number;
totalCost: number;
averageCostPerExecution: number;
mostUsedTools: Array<{ name: string; executionCount: number }>;
}> {
const matchStage = toolId
? { $match: { _id: typeof toolId === 'string' ? new ObjectId(toolId) : toolId } }
: { $match: {} };
const pipeline = [
matchStage,
{
$facet: {
total: [{ $count: 'count' }],
byStatus: [
{ $group: { _id: '$status', count: { $sum: 1 } } }
],
byCategory: [
{ $group: { _id: '$category', count: { $sum: 1 } } }
],
totals: [
{
$group: {
_id: null,
totalExecutions: { $sum: '$executionCount' },
totalCost: { $sum: '$totalCost' }
}
}
],
mostUsed: [
{ $sort: { executionCount: -1 } },
{ $limit: 10 },
{
$project: {
name: 1,
executionCount: 1
}
}
]
}
}
];
const [result] = await this.collection.aggregate(pipeline).toArray();
const totals = result.totals[0] || { totalExecutions: 0, totalCost: 0 };
return {
total: result.total[0]?.count || 0,
byStatus: result.byStatus.reduce((acc, item) => {
acc[item._id] = item.count;
return acc;
}, {}),
byCategory: result.byCategory.reduce((acc, item) => {
acc[item._id] = item.count;
return acc;
}, {}),
totalExecutions: totals.totalExecutions,
totalCost: totals.totalCost,
averageCostPerExecution: totals.totalExecutions > 0
? totals.totalCost / totals.totalExecutions
: 0,
mostUsedTools: result.mostUsed
};
}
/**
* Cleanup old executions
*/
async cleanupOldExecutions(olderThanDays: number = 30): Promise<number> {
const cutoffDate = new Date(Date.now() - olderThanDays * 24 * 60 * 60 * 1000);
const result = await this.executionCollection.deleteMany({
createdAt: { $lt: cutoffDate }
});
return result.deletedCount;
}
/**
* Build MongoDB filter from ToolFilter
*/
private buildMongoFilter(filter: ToolFilter): any {
const mongoFilter: any = {};
if (filter.agentId) {
const objectId = typeof filter.agentId === 'string' ? new ObjectId(filter.agentId) : filter.agentId;
mongoFilter.agentId = objectId;
}
if (filter.status) {
mongoFilter.status = filter.status;
}
if (filter.category) {
mongoFilter.category = filter.category;
}
if (filter.tags && filter.tags.length > 0) {
mongoFilter.tags = { $in: filter.tags };
}
if (filter.createdAfter || filter.createdBefore) {
mongoFilter.createdAt = {};
if (filter.createdAfter) {
mongoFilter.createdAt.$gte = filter.createdAfter;
}
if (filter.createdBefore) {
mongoFilter.createdAt.$lte = filter.createdBefore;
}
}
if (filter.lastUsedAfter || filter.lastUsedBefore) {
mongoFilter.lastUsedAt = {};
if (filter.lastUsedAfter) {
mongoFilter.lastUsedAt.$gte = filter.lastUsedAfter;
}
if (filter.lastUsedBefore) {
mongoFilter.lastUsedAt.$lte = filter.lastUsedBefore;
}
}
return mongoFilter;
}
/**
* Build MongoDB filter from ToolExecutionFilter
*/
private buildExecutionFilter(filter: ToolExecutionFilter): any {
const mongoFilter: any = {};
if (filter.toolId) {
const objectId = typeof filter.toolId === 'string' ? new ObjectId(filter.toolId) : filter.toolId;
mongoFilter.toolId = objectId;
}
if (filter.agentId) {
const objectId = typeof filter.agentId === 'string' ? new ObjectId(filter.agentId) : filter.agentId;
mongoFilter.agentId = objectId;
}
if (filter.status) {
mongoFilter.status = filter.status;
}
if (filter.executedAfter || filter.executedBefore) {
mongoFilter.createdAt = {};
if (filter.executedAfter) {
mongoFilter.createdAt.$gte = filter.executedAfter;
}
if (filter.executedBefore) {
mongoFilter.createdAt.$lte = filter.executedBefore;
}
}
return mongoFilter;
}
/**
* Create indexes for optimal performance
*/
async createIndexes(): Promise<void> {
// Tool collection indexes
await Promise.all([
// Primary indexes
this.collection.createIndex({ agentId: 1, name: 1 }, { unique: true }),
this.collection.createIndex({ status: 1 }),
this.collection.createIndex({ category: 1 }),
this.collection.createIndex({ createdAt: -1 }),
this.collection.createIndex({ lastUsedAt: -1 }),
this.collection.createIndex({ executionCount: -1 }),
// Compound indexes
this.collection.createIndex({ agentId: 1, status: 1 }),
this.collection.createIndex({ category: 1, status: 1 }),
// Text search index
this.collection.createIndex({
name: 'text',
description: 'text',
category: 'text'
}, {
name: 'tool_text_search'
}),
// Tag index
this.collection.createIndex({ tags: 1 })
]);
// Tool execution collection indexes
await Promise.all([
this.executionCollection.createIndex({ toolId: 1, createdAt: -1 }),
this.executionCollection.createIndex({ agentId: 1, createdAt: -1 }),
this.executionCollection.createIndex({ status: 1 }),
this.executionCollection.createIndex({ createdAt: -1 }),
// Compound indexes for rate limiting
this.executionCollection.createIndex({ toolId: 1, createdAt: 1 })
]);
}
}