Skip to content

feat: Agent Marketplace categories synced through librechat.yaml#13309

Open
Educg550 wants to merge 3 commits into
danny-avila:mainfrom
Educg550:feat/agent-marketplace-categories-yaml-sync
Open

feat: Agent Marketplace categories synced through librechat.yaml#13309
Educg550 wants to merge 3 commits into
danny-avila:mainfrom
Educg550:feat/agent-marketplace-categories-yaml-sync

Conversation

@Educg550
Copy link
Copy Markdown
Contributor

@Educg550 Educg550 commented May 25, 2026

Summary

Now, it is possible to use a new syntax in librechat.yaml to add categories. They are ordered according to the list declared in the YAML, always prioritizing the default categories first if enabled:

interface:
  # ...
  marketplace:
    use: true 
    categories:
      enableDefaultCategories: true # Default categories will always come first in order if enabled here
      list: # The order of the categories matter and will be synced to the database as declared in here
        - value: "Education"
          description: "Educational agents."
        - value: "Productivity"
          description: "Productivity agents."

Sync behavior note: categories.list is opt-in for custom-category reconciliation. If list: is omitted, the sync only toggles default categories on/off and leaves existing custom categories in the database untouched. To explicitly remove all custom categories, set list: [].

Closes #9287, further implementation details are described in #9604.
Documentation added in librechat.ai repo at LibreChat-AI/librechat.ai#587

Change Type

  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update

Testing

Please describe your test process and include instructions so that we can reproduce your test. If there are any important variables for your testing configuration, list them here.

Test Configuration:

  • Backend tests:
api tests
  • Frontend tests:
frontend tests

Checklist

  • My code adheres to this project's style guidelines
  • I have performed a self-review of my own code
  • My changes do not introduce new warnings
  • Local unit API tests pass with my changes
  • A pull request for updating the documentation has been submitted.

Copilot AI review requested due to automatic review settings May 25, 2026 18:57
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds support for configuring and syncing Marketplace agent categories from the YAML config into the database.

Changes:

  • Extends the example config to include Marketplace category settings.
  • Adds a new syncCategories utility to upsert/delete custom categories and toggle default categories.
  • Triggers category sync during custom config load when Marketplace is enabled.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

File Description
librechat.example.yaml Documents new Marketplace category configuration options.
api/server/utils/agentCategory.js Implements DB synchronization for default/custom agent categories.
api/server/services/Config/loadCustomConfig.js Calls category sync during config loading when Marketplace is enabled.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread librechat.example.yaml Outdated
Comment thread api/server/utils/agentCategory.js Outdated
Comment thread api/server/utils/agentCategory.js Outdated
Comment thread api/server/utils/agentCategory.js Outdated
Comment thread api/server/services/Config/loadCustomConfig.js
@Educg550 Educg550 requested a review from Copilot May 26, 2026 17:19
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

Comment on lines +77 to +84
const formattedCategory = {
value: prepared.normalizedValue,
label: prepared.label,
description: prepared.description,
isActive: true,
custom: true,
order: prepared.order,
};
Comment on lines +39 to +54
const preparedCategories = customCategoriesList.reduce((acc, cat, index) => {
const normalizedValue = normalizeValue(cat?.value);
if (!normalizedValue) {
logger.warn(
`Skipping invalid custom category entry at index ${index}: missing or empty value.`,
);
return acc;
}
acc.push({
normalizedValue,
label: cat.value.toString().trim(),
description: typeof cat.description === 'string' ? cat.description : '',
order: initialOrder + acc.length,
});
return acc;
}, []);
Comment on lines +22 to +25
for (const cat of dbCategories.filter((c) => !c.custom)) {
logger.info(`${enableDefaultCategories ? 'Enabling' : 'Disabling'} category: ${cat.value}`);
await updateCategory(cat.value, { isActive: enableDefaultCategories });
}
Comment thread librechat.example.yaml
Comment on lines +140 to +146
categories:
enableDefaultCategories: true
# list:
# - value: "Education"
# description: "Educational agents."
# - value: "Productivity"
# description: "Productivity agents."
@danny-avila
Copy link
Copy Markdown
Owner

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4da3289b22

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

'No custom categories `list` provided; only default-category toggling will run.',
);
}
await syncCategories(customCategoriesList, enableDefaultCategories);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Run category sync under system tenant context

When TENANT_ISOLATION_STRICT=true and librechat.yaml contains interface.marketplace.use plus categories, this call runs while startServer builds getAppConfig({ baseOnly: true }) outside the surrounding runAsSystem startup blocks (api/server/index.js:93). syncCategories immediately calls getAllCategories(), and AgentCategory uses applyTenantIsolation, whose find pre-hook throws without a tenant context in strict mode. This makes the documented marketplace category config brick startup for strict-tenant deployments; run the sync under system context or move it into an existing system-scoped startup path.

Useful? React with 👍 / 👎.

Comment on lines +171 to +178
if (marketplaceConfig.categories) {
logger.info('Marketplace categories configuration found.');
// enableDefaultCategories should be set as a boolean, defaulting to true if not specified
const enableDefaultCategories =
typeof marketplaceConfig.categories.enableDefaultCategories === 'boolean'
? marketplaceConfig.categories.enableDefaultCategories
: true;
const customCategoriesList = marketplaceConfig.categories.list;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate categories before syncing

This consumes marketplace.categories from the original config even though configSchema still only defines interface.marketplace.use, so malformed category config passes validation and reaches the destructive sync path. For example, a typo like list: [{ name: Education }] is accepted, preparedCategories becomes empty, and syncCategories deletes every existing custom category because none are considered configured; add the new categories shape to config validation before running the database reconciliation.

Useful? React with 👍 / 👎.

}

const formattedCategory = {
value: prepared.normalizedValue,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Constrain persisted category values to URL-safe slugs

When a configured category value contains a path separator such as R&D / QA, this persists the normalized value with the slash intact. The marketplace uses category values directly in navigate(/agents/${tabValue}) while the only route is agents/:category (client/src/components/Agents/Marketplace.tsx:129, client/src/routes/index.tsx:161), so selecting that tab creates extra path segments and the category page no longer matches or filters correctly. Either encode route segments everywhere or store a URL-safe slug as the category value.

Useful? React with 👍 / 👎.

'No custom categories `list` provided; only default-category toggling will run.',
);
}
await syncCategories(customCategoriesList, enableDefaultCategories);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Defer database sync until config parsing finishes

This database reconciliation runs before the remaining config normalization/validation below it, including parseCustomParams on lines 192-194. If a deployment adds a valid categories.list but also has a malformed custom endpoint parameter, startup fails after categories have already been created, updated, or deleted, leaving the DB changed by a config that was never accepted; run the sync only after all config parsing that can throw has completed.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Enhancement]: Custom Agent Categories

3 participants