SQL & MongoDB (Conceptual)
For production applications requiring persistent storage of conversations and agent run data, you'll need to implement storage adapters for a database system like SQL (e.g., PostgreSQL, MySQL, SQLite) or a NoSQL database like MongoDB.
AgentB provides the interfaces (IThreadStorage, IMessageStorage, IAgentRunStorage) and conceptual stubs (SqlStorage, MongoDbStorage) to guide these implementations. These stubs showcase the methods you need to implement but do not contain actual database interaction logic.
General Approach to Implementing a Persistent Adapter
Choose Your Database: Select the database system that best fits your application's requirements (scalability, relational needs, document structure, etc.).
Design Your Schema:
Threads Table/Collection: Typically needs columns/fields for
id(primary key),createdAt,updatedAt,title(optional),userId(optional),metadata(often stored as JSON/JSONB or a BSON object),summary(optional).Messages Table/Collection:
id(primary key),threadId(foreign key/indexed),role,content(can be text, or JSON/BSON for structured content),createdAt,updatedAt,metadata(JSON/JSONB/BSON, for tool calls, etc.). Consider indexes onthreadIdandcreatedAtfor efficient querying.Agent Runs Table/Collection:
id(primary key),threadId(indexed),agentType,status(indexed),createdAt,startedAt(optional),completedAt(optional),expiresAt(optional),lastError(JSON/JSONB/BSON),config(JSON/JSONB/BSON),metadata(JSON/JSONB/BSON).
Select a Database Client/ORM:
SQL:
Query Builders: Knex.js
ORMs: Sequelize, TypeORM, Prisma
MongoDB:
Official MongoDB Node.js Driver
ODMs: Mongoose
Implement the Interfaces:
Create a class (e.g.,
MyPostgresStorageorMyMongooseStorage).Have this class implement
IThreadStorage,IMessageStorage, andIAgentRunStorage.In each method (e.g.,
createThread,addMessage), write the database-specific code (SQL queries, MongoDB driver commands) to perform the required CRUD operations.Handle data serialization/deserialization (e.g.,
JSON.stringify/JSON.parsefor metadata stored as text in SQL, or ensure BSON compatibility for MongoDB).Implement error handling and potentially throw
StorageErrorfor database-related issues.
SqlStorage (Conceptual Stub - src/threads/storage/sql-storage.ts)
SqlStorage (Conceptual Stub - src/threads/storage/sql-storage.ts)The SqlStorage class in AgentB (@ulifeai/agentb/dist/threads/storage/sql-storage.js) is a non-functional stub. It outlines the methods but contains comments like // TODO: Implement SQL INSERT operation.
To make it functional, you would:
Install a SQL client library (e.g.,
pgfor PostgreSQL,mysql2for MySQL,sqlite3for SQLite) and a query builder/ORM likeknex.Configure the database connection.
Replace the
// TODO:comments with actual SQL query logic using your chosen client/ORM.
Example Snippet (Conceptual for createThread with Knex):
// Inside your functional SqlStorage class
// Assuming 'dbClient' is an initialized Knex instance
async createThread(threadData?: Partial<Omit<IThread, 'id' | 'createdAt'>>): Promise<IThread> {
if (!this.dbClient) throw new StorageError('Database client not configured.');
const threadId = uuidv4();
const now = new Date();
const newThread: IThread = { /* ... as in IThread ... */ };
await this.dbClient('threads').insert({
id: newThread.id,
created_at: newThread.createdAt,
updated_at: newThread.updatedAt,
title: newThread.title,
user_id: newThread.userId,
metadata: JSON.stringify(newThread.metadata || {}), // Store as JSON string
summary: newThread.summary,
});
return newThread;
}MongoDbStorage (Conceptual Stub - src/threads/storage/mongodb-storage.ts)
MongoDbStorage (Conceptual Stub - src/threads/storage/mongodb-storage.ts)Similarly, MongoDbStorage (@ulifeai/agentb/dist/threads/storage/mongodb-storage.js) is a non-functional stub.
To make it functional, you would:
Install the
mongodbNode.js driver.Connect to your MongoDB instance and get a
Dbobject.Obtain
Collectionobjects for threads, messages, and agent runs.Replace
// TODO:comments with MongoDB driver operations (e.g.,insertOne,findOne,updateOne,deleteMany).
Example Snippet (Conceptual for addMessage with MongoDB Driver):
// Inside your functional MongoDbStorage class
// Assuming 'threadsCollection' and 'messagesCollection' are initialized MongoDB Collection instances
async addMessage(
messageData: Omit<IMessage, 'id' | 'createdAt' | 'updatedAt'> & Partial<Pick<IMessage, 'id' | 'createdAt' | 'updatedAt'>>
): Promise<IMessage> {
if (!this.messagesCollection || !this.threadsCollection) throw new StorageError('MongoDB collections not configured.');
if (!messageData.threadId) throw new ValidationError('threadId is required.');
const threadExistsCount = await this.threadsCollection.countDocuments({ id: messageData.threadId });
if (threadExistsCount === 0) {
throw new StorageError(`Thread with ID "${messageData.threadId}" not found. Cannot add message.`);
}
const messageId = messageData.id || uuidv4();
const now = new Date();
const newMessageDoc: IMessage & { _id?: string } = {
_id: messageId, // Use custom ID as _id for easier querying by 'id' field
id: messageId,
// ... other IMessage fields ...
createdAt: messageData.createdAt || now,
updatedAt: messageData.updatedAt || now,
};
await this.messagesCollection.insertOne(newMessageDoc);
await this.threadsCollection.updateOne(
{ id: messageData.threadId },
{ $set: { updatedAt: now } }
);
const { _id, ...messageToReturn } = newMessageDoc;
return messageToReturn as IMessage;
}Key Considerations for Implementation
Indexing: Ensure proper database indexes are created on frequently queried fields (e.g.,
threadIdin the messages collection,userIdin threads,statusin agent runs,createdAtfor sorting/filtering).Transactions (SQL): For operations that modify multiple tables (like
deleteThreadwhich also deletes messages), use database transactions to ensure atomicity.Error Handling: Catch database-specific errors and wrap them in
StorageErrororValidationErroras appropriate.Data Mapping: Carefully map between the
IThread/IMessage/IAgentRuninterface structures and your database schema, especially forDateobjects and JSON/BSONmetadataorconfigfields.Query Options: Pay close attention to implementing the
IMessageQueryOptions(limit,before,after,order) correctly in yourgetMessagesmethod.Scalability: Design your schema and queries with scalability in mind if you anticipate a large volume of data.
By implementing these interfaces with your chosen database, you can make AgentB's conversational state and agent run history persistent and robust for production environments.
Last updated