mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-27 12:13:30 +00:00
✂️ fix(agents): warn when skill descriptions are truncated in the model catalog (#14878)
formatSkillCatalog caps every catalog entry at 250 characters and truncates silently, while the import validator allows descriptions up to 1024 chars. Skill authors therefore get no signal that the trigger phrases at the end of a long description will never reach the model, and the skill silently stops firing on the prompts it was written for. Log a warning per truncated skill so operators can see which descriptions need tightening, and pass the cap explicitly so the warning and the catalog stay in sync if @librechat/agents changes its default. Closes #14657 Co-authored-by: dymux <putramkti@users.noreply.github.com>
This commit is contained in:
parent
16e4d14191
commit
49083d0adf
2 changed files with 50 additions and 1 deletions
|
|
@ -897,6 +897,38 @@ describe('injectSkillCatalog', () => {
|
|||
expect(agent.additional_instructions).toContain('desc-my-skill');
|
||||
});
|
||||
|
||||
it('warns when a skill description exceeds the catalog entry cap', async () => {
|
||||
const { logger } = await import('@librechat/data-schemas');
|
||||
const warnSpy = jest.spyOn(logger, 'warn');
|
||||
const longDesc = 'x'.repeat(400);
|
||||
const longSkill: PageSkill = {
|
||||
...makeSkill('long-skill', userObjectId),
|
||||
description: longDesc,
|
||||
};
|
||||
const shortSkill = makeSkill('short-skill', userObjectId);
|
||||
const listSkillsByAccess = buildPager([[longSkill, shortSkill]]);
|
||||
const agent = makeAgent();
|
||||
await injectSkillCatalog(baseParams({ listSkillsByAccess, agent }));
|
||||
|
||||
const truncWarns = warnSpy.mock.calls
|
||||
.map((call) => String(call[0]))
|
||||
.filter((msg) => msg.includes('truncated to'));
|
||||
expect(truncWarns).toHaveLength(1);
|
||||
expect(truncWarns[0]).toContain('"long-skill"');
|
||||
expect(truncWarns[0]).toContain('was 400');
|
||||
/* Short description is not flagged. */
|
||||
expect(
|
||||
warnSpy.mock.calls
|
||||
.map((call) => String(call[0]))
|
||||
.filter((msg) => msg.includes('"short-skill"') && msg.includes('truncated')),
|
||||
).toHaveLength(0);
|
||||
|
||||
/* The catalog still reaches the model — the warning is additive. */
|
||||
expect(agent.additional_instructions).toContain('long-skill');
|
||||
expect(agent.additional_instructions).toContain('short-skill');
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('honors a configured maxCatalogSkills below the default hard limit', async () => {
|
||||
const first = makeSkill('first-skill', userObjectId);
|
||||
const second = makeSkill('second-skill', userObjectId);
|
||||
|
|
|
|||
|
|
@ -89,6 +89,13 @@ const MIN_SKILL_CATALOG_LIMIT = 1;
|
|||
const MAX_CATALOG_PAGES = 10;
|
||||
/** Page size used when paginating to fill the active-skill quota. */
|
||||
const CATALOG_PAGE_SIZE = 100;
|
||||
/**
|
||||
* Per-entry description cap applied by `formatSkillCatalog` before the
|
||||
* catalog is injected into agent context. `@librechat/agents` truncates
|
||||
* silently, so this mirrors the default so we can warn when authors' skill
|
||||
* descriptions will not reach the model verbatim.
|
||||
*/
|
||||
const SKILL_CATALOG_MAX_ENTRY_CHARS = 250;
|
||||
/** Hard ceiling on skill names a model spec can request by config. */
|
||||
const MAX_MODEL_SPEC_SKILLS = SKILL_CATALOG_LIMIT;
|
||||
/**
|
||||
|
|
@ -620,9 +627,19 @@ export async function injectSkillCatalog(
|
|||
* and those reads would otherwise be impossible.
|
||||
*/
|
||||
if (catalogVisibleSkills.length > 0) {
|
||||
for (const s of catalogVisibleSkills) {
|
||||
if (s.description.length > SKILL_CATALOG_MAX_ENTRY_CHARS) {
|
||||
logger.warn(
|
||||
`[injectSkillCatalog] skill "${s.name}" description truncated to ${SKILL_CATALOG_MAX_ENTRY_CHARS} chars for the model catalog (was ${s.description.length})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
const catalog = formatSkillCatalog(
|
||||
catalogVisibleSkills.map((s) => ({ name: s.name, description: s.description })),
|
||||
{ contextWindowTokens: contextWindowTokens || 200_000 },
|
||||
{
|
||||
contextWindowTokens: contextWindowTokens || 200_000,
|
||||
maxEntryChars: SKILL_CATALOG_MAX_ENTRY_CHARS,
|
||||
},
|
||||
);
|
||||
if (catalog) {
|
||||
agent.additional_instructions = agent.additional_instructions
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue