feat: Agent Marketplace categories synced through librechat.yaml#13309
feat: Agent Marketplace categories synced through librechat.yaml#13309Educg550 wants to merge 3 commits into
librechat.yaml#13309Conversation
There was a problem hiding this comment.
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
syncCategoriesutility 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.
| const formattedCategory = { | ||
| value: prepared.normalizedValue, | ||
| label: prepared.label, | ||
| description: prepared.description, | ||
| isActive: true, | ||
| custom: true, | ||
| order: prepared.order, | ||
| }; |
| 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; | ||
| }, []); |
| for (const cat of dbCategories.filter((c) => !c.custom)) { | ||
| logger.info(`${enableDefaultCategories ? 'Enabling' : 'Disabling'} category: ${cat.value}`); | ||
| await updateCategory(cat.value, { isActive: enableDefaultCategories }); | ||
| } |
| categories: | ||
| enableDefaultCategories: true | ||
| # list: | ||
| # - value: "Education" | ||
| # description: "Educational agents." | ||
| # - value: "Productivity" | ||
| # description: "Productivity agents." |
|
@codex review |
There was a problem hiding this comment.
💡 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); |
There was a problem hiding this comment.
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 👍 / 👎.
| 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; |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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 👍 / 👎.
Summary
Now, it is possible to use a new syntax in
librechat.yamlto add categories. They are ordered according to the list declared in the YAML, always prioritizing the default categories first if enabled:Closes #9287, further implementation details are described in #9604.
Documentation added in librechat.ai repo at LibreChat-AI/librechat.ai#587
Change Type
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:
Checklist