mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-05-13 07:46:47 +00:00
Merge a4b4abac60 into 6b5596ec36
This commit is contained in:
commit
c12aecae9c
2 changed files with 117 additions and 0 deletions
|
|
@ -159,6 +159,88 @@ describe('replaceSpecialVars', () => {
|
|||
expect(result).toContain('2024-04-29T16:34:56.000Z'); // iso_datetime
|
||||
expect(result).toContain('Test User'); // current_user
|
||||
});
|
||||
|
||||
describe('LIBRECHAT_USER_* placeholders', () => {
|
||||
const fullUser = {
|
||||
id: 'user-abc-123',
|
||||
name: 'Test User',
|
||||
username: 'tuser',
|
||||
email: 'tuser@example.com',
|
||||
role: 'USER',
|
||||
provider: 'ldap',
|
||||
} as TUser;
|
||||
|
||||
test('expands LIBRECHAT_USER_USERNAME and LIBRECHAT_USER_ID', () => {
|
||||
const result = replaceSpecialVars({
|
||||
text: 'USERNAME={{LIBRECHAT_USER_USERNAME}} USER_ID={{LIBRECHAT_USER_ID}}',
|
||||
user: fullUser,
|
||||
});
|
||||
expect(result).toBe('USERNAME=tuser USER_ID=user-abc-123');
|
||||
});
|
||||
|
||||
test('alongside {{current_date}} (modelSpecs.promptPrefix scenario from #12686)', () => {
|
||||
const result = replaceSpecialVars({
|
||||
text: [
|
||||
'DATE={{current_date}}',
|
||||
'USERNAME={{LIBRECHAT_USER_USERNAME}}',
|
||||
'USER_ID={{LIBRECHAT_USER_ID}}',
|
||||
].join('\n'),
|
||||
user: fullUser,
|
||||
});
|
||||
expect(result).toBe(
|
||||
['DATE=2024-04-29 (Monday)', 'USERNAME=tuser', 'USER_ID=user-abc-123'].join('\n'),
|
||||
);
|
||||
});
|
||||
|
||||
test('expands email, role, provider, and name fields', () => {
|
||||
const result = replaceSpecialVars({
|
||||
text:
|
||||
'email={{LIBRECHAT_USER_EMAIL}} role={{LIBRECHAT_USER_ROLE}} ' +
|
||||
'provider={{LIBRECHAT_USER_PROVIDER}} name={{LIBRECHAT_USER_NAME}}',
|
||||
user: fullUser,
|
||||
});
|
||||
expect(result).toBe(
|
||||
'email=tuser@example.com role=USER provider=ldap name=Test User',
|
||||
);
|
||||
});
|
||||
|
||||
test('is case-insensitive and tolerates surrounding whitespace', () => {
|
||||
const result = replaceSpecialVars({
|
||||
text: '{{ librechat_user_username }} / {{LIBRECHAT_USER_username}}',
|
||||
user: fullUser,
|
||||
});
|
||||
expect(result).toBe('tuser / tuser');
|
||||
});
|
||||
|
||||
test('leaves placeholder intact when the user field is missing', () => {
|
||||
const partialUser = { id: 'u1', name: 'Only Name' } as TUser;
|
||||
const result = replaceSpecialVars({
|
||||
text: 'u={{LIBRECHAT_USER_USERNAME}} id={{LIBRECHAT_USER_ID}}',
|
||||
user: partialUser,
|
||||
});
|
||||
expect(result).toBe('u={{LIBRECHAT_USER_USERNAME}} id=u1');
|
||||
});
|
||||
|
||||
test('leaves placeholders intact when no user is supplied', () => {
|
||||
const result = replaceSpecialVars({
|
||||
text: '{{LIBRECHAT_USER_USERNAME}} {{LIBRECHAT_USER_ID}}',
|
||||
});
|
||||
expect(result).toBe('{{LIBRECHAT_USER_USERNAME}} {{LIBRECHAT_USER_ID}}');
|
||||
});
|
||||
|
||||
test('does not expand fields outside the allowlist', () => {
|
||||
const userWithExtras = {
|
||||
id: 'u1',
|
||||
name: 'N',
|
||||
avatar: 'https://example.com/a.png',
|
||||
} as TUser;
|
||||
const result = replaceSpecialVars({
|
||||
text: '{{LIBRECHAT_USER_AVATAR}} {{LIBRECHAT_USER_PASSWORD}}',
|
||||
user: userWithExtras,
|
||||
});
|
||||
expect(result).toBe('{{LIBRECHAT_USER_AVATAR}} {{LIBRECHAT_USER_PASSWORD}}');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseCompactConvo', () => {
|
||||
|
|
|
|||
|
|
@ -424,6 +424,27 @@ export function findLastSeparatorIndex(text: string, separators = SEPARATORS): n
|
|||
return lastIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fields on the user object that may be expanded via
|
||||
* `{{LIBRECHAT_USER_<FIELD>}}` placeholders inside a prompt template
|
||||
* (e.g. modelSpecs `promptPrefix`, agent instructions, prompts).
|
||||
*
|
||||
* The set is intentionally limited to non-sensitive identity fields and
|
||||
* mirrors the allowlist used by MCP/header placeholder resolution in
|
||||
* `@librechat/api`. Placeholders for fields that are missing on the user
|
||||
* object are left intact (matching the existing `{{current_user}}` contract).
|
||||
*/
|
||||
export const librechatUserFields = [
|
||||
'id',
|
||||
'name',
|
||||
'username',
|
||||
'email',
|
||||
'provider',
|
||||
'role',
|
||||
] as const;
|
||||
|
||||
export type LibreChatUserField = (typeof librechatUserFields)[number];
|
||||
|
||||
export function replaceSpecialVars({
|
||||
text,
|
||||
user,
|
||||
|
|
@ -454,6 +475,20 @@ export function replaceSpecialVars({
|
|||
result = result.replace(/{{\s*current_user\s*}}/gi, user.name);
|
||||
}
|
||||
|
||||
if (user) {
|
||||
for (const field of librechatUserFields) {
|
||||
const fieldValue = (user as Record<string, unknown>)[field];
|
||||
if (fieldValue == null || fieldValue === '') {
|
||||
continue;
|
||||
}
|
||||
const pattern = new RegExp(
|
||||
`{{\\s*LIBRECHAT_USER_${field.toUpperCase()}\\s*}}`,
|
||||
'gi',
|
||||
);
|
||||
result = result.replace(pattern, String(fieldValue));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue