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 onthreadId
andcreatedAt
for 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.,
MyPostgresStorage
orMyMongooseStorage
).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.parse
for metadata stored as text in SQL, or ensure BSON compatibility for MongoDB).Implement error handling and potentially throw
StorageError
for 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.,
pg
for PostgreSQL,mysql2
for MySQL,sqlite3
for 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
mongodb
Node.js driver.Connect to your MongoDB instance and get a
Db
object.Obtain
Collection
objects 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.,
threadId
in the messages collection,userId
in threads,status
in agent runs,createdAt
for sorting/filtering).Transactions (SQL): For operations that modify multiple tables (like
deleteThread
which also deletes messages), use database transactions to ensure atomicity.Error Handling: Catch database-specific errors and wrap them in
StorageError
orValidationError
as appropriate.Data Mapping: Carefully map between the
IThread
/IMessage
/IAgentRun
interface structures and your database schema, especially forDate
objects and JSON/BSONmetadata
orconfig
fields.Query Options: Pay close attention to implementing the
IMessageQueryOptions
(limit
,before
,after
,order
) correctly in yourgetMessages
method.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