bugfix: Enhance Agent and AgentCategory schemas with new fields for category, support contact, and promotion status

This commit is contained in:
“Praneeth 2025-06-11 22:55:07 +05:30 committed by Danny Avila
parent f55cdc9b7f
commit 348ee5821e
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
5 changed files with 57 additions and 100 deletions

View file

@ -5,7 +5,6 @@ const { SystemRoles, Tools, actionDelimiter } = require('librechat-data-provider
const { GLOBAL_PROJECT_NAME, EPHEMERAL_AGENT_ID, mcp_delimiter } = const { GLOBAL_PROJECT_NAME, EPHEMERAL_AGENT_ID, mcp_delimiter } =
require('librechat-data-provider').Constants; require('librechat-data-provider').Constants;
// Default category value for new agents // Default category value for new agents
const AgentCategory = require('./AgentCategory');
const { const {
getProjectByName, getProjectByName,
addAgentIdsToProject, addAgentIdsToProject,
@ -15,80 +14,7 @@ const {
const { getCachedTools } = require('~/server/services/Config'); const { getCachedTools } = require('~/server/services/Config');
// Category values are now imported from shared constants // Category values are now imported from shared constants
// Schema fields (category, support_contact, is_promoted) are defined in @librechat/data-schemas
// Add category field to the Agent schema if it doesn't already exist
if (!agentSchema.paths.category) {
agentSchema.add({
category: {
type: String,
trim: true,
validate: {
validator: async function (value) {
if (!value) return true; // Allow empty values (will use default)
// Check if category exists in database
const validCategories = await AgentCategory.getValidCategoryValues();
return validCategories.includes(value);
},
message: function (props) {
return `"${props.value}" is not a valid agent category. Please check available categories.`;
},
},
index: true,
default: 'general',
},
});
}
// Add support_contact field to the Agent schema if it doesn't already exist
if (!agentSchema.paths.support_contact) {
agentSchema.add({
support_contact: {
type: Object,
default: {},
name: {
type: String,
minlength: [3, 'Support contact name must be at least 3 characters.'],
trim: true,
},
email: {
type: String,
match: [
/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/,
'Please enter a valid email address.',
],
trim: true,
},
},
});
}
// Add promotion field to the Agent schema if it doesn't already exist
if (!agentSchema.paths.is_promoted) {
agentSchema.add({
is_promoted: {
type: Boolean,
default: false,
index: true, // Index for efficient promoted agent queries
},
});
}
// Add additional indexes for marketplace functionality
agentSchema.index({ projectIds: 1, is_promoted: 1, updatedAt: -1 }); // Optimize promoted agents query
agentSchema.index({ category: 1, projectIds: 1, updatedAt: -1 }); // Optimize category filtering
agentSchema.index({ projectIds: 1, category: 1 }); // Optimize aggregation pipeline
// Text indexes for search functionality
agentSchema.index(
{ name: 'text', description: 'text' },
{
weights: {
name: 3, // Name matches are 3x more important than description matches
description: 1,
},
},
);
const { getActions } = require('./Action'); const { getActions } = require('./Action');
const { Agent } = require('~/db/models'); const { Agent } = require('~/db/models');

View file

@ -46,7 +46,7 @@ const agentCategorySchema = new mongoose.Schema(
}, },
{ {
timestamps: true, timestamps: true,
} },
); );
// Indexes for performance // Indexes for performance
@ -56,18 +56,16 @@ agentCategorySchema.index({ isActive: 1, order: 1 });
* Get all active categories sorted by order * Get all active categories sorted by order
* @returns {Promise<AgentCategory[]>} Array of active categories * @returns {Promise<AgentCategory[]>} Array of active categories
*/ */
agentCategorySchema.statics.getActiveCategories = function() { agentCategorySchema.statics.getActiveCategories = function () {
return this.find({ isActive: true }) return this.find({ isActive: true }).sort({ order: 1, label: 1 }).lean();
.sort({ order: 1, label: 1 })
.lean();
}; };
/** /**
* Get categories with agent counts * Get categories with agent counts
* @returns {Promise<AgentCategory[]>} Categories with agent counts * @returns {Promise<AgentCategory[]>} Categories with agent counts
*/ */
agentCategorySchema.statics.getCategoriesWithCounts = async function() { agentCategorySchema.statics.getCategoriesWithCounts = async function () {
const Agent = mongoose.model('agent'); const Agent = mongoose.model('Agent');
// Aggregate to get agent counts per category // Aggregate to get agent counts per category
const categoryCounts = await Agent.aggregate([ const categoryCounts = await Agent.aggregate([
@ -76,12 +74,12 @@ agentCategorySchema.statics.getCategoriesWithCounts = async function() {
]); ]);
// Create a map for quick lookup // Create a map for quick lookup
const countMap = new Map(categoryCounts.map(c => [c._id, c.count])); const countMap = new Map(categoryCounts.map((c) => [c._id, c.count]));
// Get all active categories and add counts // Get all active categories and add counts
const categories = await this.getActiveCategories(); const categories = await this.getActiveCategories();
return categories.map(category => ({ return categories.map((category) => ({
...category, ...category,
agentCount: countMap.get(category.value) || 0, agentCount: countMap.get(category.value) || 0,
})); }));
@ -91,16 +89,14 @@ agentCategorySchema.statics.getCategoriesWithCounts = async function() {
* Get valid category values for Agent model validation * Get valid category values for Agent model validation
* @returns {Promise<string[]>} Array of valid category values * @returns {Promise<string[]>} Array of valid category values
*/ */
agentCategorySchema.statics.getValidCategoryValues = function() { agentCategorySchema.statics.getValidCategoryValues = function () {
return this.find({ isActive: true }) return this.find({ isActive: true }).distinct('value').lean();
.distinct('value')
.lean();
}; };
/** /**
* Seed initial categories from existing constants * Seed initial categories from existing constants
*/ */
agentCategorySchema.statics.seedCategories = async function(categories) { agentCategorySchema.statics.seedCategories = async function (categories) {
const operations = categories.map((category, index) => ({ const operations = categories.map((category, index) => ({
updateOne: { updateOne: {
filter: { value: category.value }, filter: { value: category.value },

View file

@ -3,7 +3,7 @@ const mongoose = require('mongoose');
const { logger } = require('~/config'); const { logger } = require('~/config');
// Get the Agent model // Get the Agent model
const Agent = mongoose.model('agent'); const Agent = mongoose.model('Agent');
// Default page size for agent browsing // Default page size for agent browsing
const DEFAULT_PAGE_SIZE = 6; const DEFAULT_PAGE_SIZE = 6;

View file

@ -92,6 +92,35 @@ const agentSchema = new Schema<IAgent>(
type: [Schema.Types.Mixed], type: [Schema.Types.Mixed],
default: [], default: [],
}, },
category: {
type: String,
trim: true,
index: true,
default: 'general',
},
support_contact: {
type: {
name: {
type: String,
minlength: [3, 'Support contact name must be at least 3 characters.'],
trim: true,
},
email: {
type: String,
match: [
/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/,
'Please enter a valid email address.',
],
trim: true,
},
},
default: {},
},
is_promoted: {
type: Boolean,
default: false,
index: true,
},
}, },
{ {
timestamps: true, timestamps: true,

View file

@ -36,4 +36,10 @@ export interface IAgent extends Omit<Document, 'model'> {
versions?: Omit<IAgent, 'versions'>[]; versions?: Omit<IAgent, 'versions'>[];
category: string; category: string;
support_contact?: ISupportContact; support_contact?: ISupportContact;
category: string;
support_contact?: {
name?: string;
email?: string;
};
is_promoted?: boolean;
} }