Skip to content

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

Open
Educg550 wants to merge 7 commits into
LibreChat-AI:devfrom
Educg550:feat/agent-marketplace-categories-yaml-sync
Open

Educg550 wants to merge 7 commits into
LibreChat-AI:devfrom
Educg550:feat/agent-marketplace-categories-yaml-sync

Conversation

@Educg550

@Educg550 Educg550 commented May 25, 2026 •

Copy link
Copy Markdown
Contributor

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/docs#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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 thread api/server/utils/agentCategory.js
Comment thread api/server/utils/agentCategory.js
Comment thread api/server/utils/agentCategory.js
Comment thread librechat.example.yaml Outdated
@danny-avila

Copy link
Copy Markdown
Collaborator

@codex review

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

Copy link
Copy Markdown

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".

Comment thread api/server/services/Config/loadCustomConfig.js Outdated
Comment thread api/server/services/Config/loadCustomConfig.js
Comment thread api/server/utils/agentCategory.js
Comment thread api/server/services/Config/loadCustomConfig.js Outdated
@nbhadauria

Copy link
Copy Markdown

I could use this. do we have any plan for this?

@Educg550
Educg550 force-pushed the feat/agent-marketplace-categories-yaml-sync branch from b90fc09 to 30c56e1 Compare June 23, 2026 18:44
@duan-levan

Copy link
Copy Markdown

@Educg550
Thanks for working on this — being able to declare marketplace categories in librechat.yaml would be really valuable for us (self-hosted, multi-locale internal deployment). We evaluated the branch against our setup and ran into two things that would affect us, plus one minor note.

  1. Custom categories can't be localized

syncCategories derives the label from the YAML value:

label: cat.value.toString().trim(),
But the client already supports translation keys for category labels — getCategoryDisplayName in client/src/components/Agents/CategoryTabs.tsx#L48 and getCategoryLabel in client/src/components/SidePanel/Agents/AgentCategorySelector.tsx#L48 call localize() when the label starts with com_, and client/src/components/Agents/Marketplace.tsx#L282-L287 does the same for both label and description.

So YAML-declared categories always render in whatever language the admin typed, even for users on a different UI locale — while the DB-seeded defaults localize correctly. For a deployment where users switch between Japanese, English,...., that's a blocker.

Would an optional label field be in scope? Something like:

list:

  • value: "education"
    label: "com_agents_category_education" # optional, falls back to value
    description: "com_agents_category_education_description"
    Looks like it would only need a small addition to preparedCategories and the zod schema, with current behaviour preserved as the default.
  1. enableDefaultCategories: false leaves general as an unreachable default

general is hardcoded as the fallback category in three places:

packages/data-schemas/src/schema/agent.ts#L104 — category: { default: 'general' }
packages/data-schemas/src/methods/agent.ts#L485 — category: agentData.category || 'general'
client/src/components/SidePanel/Agents/AgentCategorySelector.tsx#L26,L61,L71
With enableDefaultCategories: false, general becomes isActive: false so getActiveCategories() drops it — but newly created agents still get category: 'general'. Those agents then show up under no tab except "All", silently. And since a custom entry colliding with a default value is skipped, an admin can't re-declare general either — so replacing the default set entirely leads straight into this state.

Keeping general always active, or letting custom entries override defaults, would avoid it.

  1. Minor — the collision guard added in response to this review also means a default category's label/description can't be customized at all (e.g. renaming "After Sales"). Combined with point 2, that makes fully replacing the default set the only option, which is the path that runs into the general issue. Allowing overrides on custom: false rows — or flipping them to custom: true when overridden — would cover both.

This feature is exactly what we need — our current category set doesn't match how our internal teams actually use agents, and the alternative for us is patching the hardcoded defaults in our fork, which we'd rather avoid. We're holding off on that in the hope this lands.

Also +1 to @nbhadauria 's question above — is there anything blocking this from moving forward? Happy to test any changes against our deployment if that helps.

@Educg550

Educg550 commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@duan-levan hey, thks for the reply. Here my considerations:

1. Localizable labels. Yep, syncCategories hardcodes the label off the value. An
optional label is one field in the zod schema plus one fallback in preparedCategories,
with the old behaviour as the default. I'll add it, it's legitimaly a free improvement.

3. Overriding defaults. Doing it, but I'll keep overridden rows at custom: false and
just apply label/description/isActive instead of flipping them to custom: true. The
delete pass wipes any custom: true row that isn't in the config, so a flipped default
becomes deletable: pull it from your YAML later and the category vanishes until the next boot
recreates it. Staying custom: false avoids all of that. And because seedDatabase() runs
before loadCustomConfig() in api/server/index.js, ensureDefaultCategories' localization
reset lands first and your override gets reapplied after, so even a plain non-com_ label
survives restarts. I'll leave order alone on default rows so nobody's tab order shuffles.

2. general. Confirmed at all three sites, and the failure mode is exactly what you
describe: getActiveCategories() filters on isActive: true so there's no General tab, but
the schema still stamps category: 'general' on every new agent. Those agents sit in "All"
and nowhere else, and the collision guard stops you from fixing it yourself.

I'd like to split it, though.

Now, in this PR: general can't be deactivated by the default toggle, and with 3. you can
re-declare it to change its label and description. That's a guard in syncCategories and
nothing more.

Later, as its own issue: making the fallback category configurable, which means pulling the
hardcoded 'general' out of the schema default, createAgent, and the three client spots.
That's the part that actually lets you drop the default set entirely, but it raises questions
I don't want to answer unilaterally in a config PR: what happens to agents already stamped
general, whether the fallback reads from config or just takes the first active category, and
whether the schema default can change without a migration. I'd rather ship the guard now and
open a focused issue for the rest.

@Educg550
Educg550 force-pushed the feat/agent-marketplace-categories-yaml-sync branch from 253aa83 to 0236907 Compare August 6, 2026 19:57
@duan-levan

Copy link
Copy Markdown

@Educg550
Thanks, this all sounds right to me — and the split makes sense.

On 3: I independently verified the ordering you describe. await runAsSystem(seedDatabase) is at api/server/index.js:92 and getAppConfig({ baseOnly: true }) at :99, so ensureDefaultCategories does land before syncCategories on every boot. Keeping overridden rows at custom: false also avoids the delete-pass trap you point out. Agreed on leaving order untouched.

On 2: agreed, and the interim guard covers my case — I want to keep general as the fallback anyway, I just need to relabel it. Making the fallback configurable really is a separate question with migration implications; a focused issue is the right home for it.

One thing I noticed while checking the above, which may or may not be in scope:

syncCategories runs inside loadCustomConfig, which is the cache-miss path of getAppConfig (api/server/services/Config/app.js → loadBaseConfig), not only the boot path. So after clearAppConfigCache() — which invalidateConfigCaches() calls on every admin config mutation — or a cache TTL expiry, the sync re-runs while serving a request. Two implications worth a thought:

The delete pass (deleteCategory for every custom: true row not in config) becomes a request-time destructive operation rather than a startup-only one.
If two requests miss the cache concurrently, two syncCategories runs can interleave across the delete and create passes. With the unique index on { value, tenantId } that could surface as a duplicate-key error, or as a brief window where a category is absent from getActiveCategories().
Guarding the sync so it only runs once per process, or moving it to a startup-only path, would sidestep both. This also relates to the unnecessary-writes point Copilot raised — skipping no-op updateCategory calls would make repeat runs cheap and mostly harmless.

Happy to test the branch against our deployment once label and the override guard land.

@Educg550
Educg550 force-pushed the feat/agent-marketplace-categories-yaml-sync branch from 0236907 to e983953 Compare September 29, 2026 19:47
@github-actions
github-actions Bot changed the base branch from main to dev September 29, 2026 19:47
@github-actions

Copy link
Copy Markdown
Contributor

👋 Thanks for the contribution! LibreChat merges all changes into dev first — main only moves at release time — so this pull request's base branch was switched from main to dev automatically.

Nothing is needed from you; your commits, reviews and discussion are unchanged. If the diff now shows files you did not touch, rebase onto dev:

git remote add upstream https://github.com/LibreChat-AI/LibreChat.git
git fetch upstream dev
git rebase upstream/dev
git push --force-with-lease

Maintainers: apply the target: main label and restore the base branch if this one genuinely belongs on main.

@Educg550
Educg550 force-pushed the feat/agent-marketplace-categories-yaml-sync branch from e983953 to 2a620e5 Compare October 1, 2026 16:49
@codegraph-librechat codegraph-librechat Bot added 🗺️ Backend Platform codegraph: the taxonomy area this belongs to (classifier, confidence ≥ 0.9) 🗺️ Backend Infra codegraph: the taxonomy area this belongs to (classifier, confidence ≥ 0.9) labels Oct 1, 2026

This branch has not been deployed

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

Labels

🗺️ Backend Infra codegraph: the taxonomy area this belongs to (classifier, confidence ≥ 0.9) 🗺️ Backend Platform codegraph: the taxonomy area this belongs to (classifier, confidence ≥ 0.9)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Enhancement]: Custom Agent Categories

5 participants