mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-05-13 15:58:48 +00:00
fix: unset empty Langfuse agent config
This commit is contained in:
parent
ddb7090d64
commit
bc2c19d841
5 changed files with 80 additions and 7 deletions
|
|
@ -566,12 +566,20 @@ const updateAgentHandler = async (req, res) => {
|
|||
}
|
||||
|
||||
if (updateData.langfuse) {
|
||||
updateData.langfuse = await normalizeLangfuseConfig(
|
||||
const normalizedLangfuse = await normalizeLangfuseConfig(
|
||||
updateData.langfuse,
|
||||
existingAgent.langfuse,
|
||||
);
|
||||
if (!updateData.langfuse) {
|
||||
if (normalizedLangfuse) {
|
||||
updateData.langfuse = normalizedLangfuse;
|
||||
} else {
|
||||
delete updateData.langfuse;
|
||||
if (existingAgent.langfuse) {
|
||||
updateData.$unset = {
|
||||
...(updateData.$unset || {}),
|
||||
langfuse: 1,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -958,6 +958,36 @@ describe('Agent Controllers - Mass Assignment Protection', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('should remove Langfuse config when update clears the only stored field', async () => {
|
||||
const encryptedOriginal = await encryptStoredSecret('sk-original');
|
||||
await Agent.updateOne(
|
||||
{ id: existingAgentId },
|
||||
{
|
||||
langfuse: {
|
||||
secretKey: encryptedOriginal,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
mockReq.params.id = existingAgentId;
|
||||
mockReq.body = {
|
||||
langfuse: {
|
||||
secretKey: LANGFUSE_SECRET_CLEAR_VALUE,
|
||||
},
|
||||
};
|
||||
|
||||
await updateAgentHandler(mockReq, mockRes);
|
||||
|
||||
expect(mockRes.json).toHaveBeenCalled();
|
||||
const updatedAgent = mockRes.json.mock.calls[0][0];
|
||||
expect(updatedAgent.langfuse).toBeUndefined();
|
||||
|
||||
const agentInDb = await Agent.findOne({ id: existingAgentId }).lean();
|
||||
expect(agentInDb.langfuse).toBeUndefined();
|
||||
const latestVersion = agentInDb.versions[agentInDb.versions.length - 1];
|
||||
expect(latestVersion.langfuse).toBeUndefined();
|
||||
});
|
||||
|
||||
test('uploadAgentAvatarHandler should redact Langfuse secret in response', async () => {
|
||||
await Agent.updateOne(
|
||||
{ id: existingAgentId },
|
||||
|
|
|
|||
|
|
@ -59,6 +59,19 @@ describe('normalizeLangfuseConfig', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('returns undefined when clearing the only stored field', async () => {
|
||||
const result = await normalizeLangfuseConfig(
|
||||
{
|
||||
secretKey: LANGFUSE_SECRET_CLEAR_VALUE,
|
||||
},
|
||||
{
|
||||
secretKey: '0123456789abcdef0123456789abcdef:736b2d6167656e74',
|
||||
},
|
||||
);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('clears explicit blank non-secret fields while preserving absent fields', async () => {
|
||||
const result = await normalizeLangfuseConfig(
|
||||
{
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ export async function normalizeLangfuseConfig(
|
|||
|
||||
const incomingSecret = incoming.secretKey;
|
||||
if (incomingSecret === LANGFUSE_SECRET_CLEAR_VALUE) {
|
||||
return normalized;
|
||||
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
||||
}
|
||||
|
||||
if (isNonEmptyString(incomingSecret)) {
|
||||
|
|
|
|||
|
|
@ -57,6 +57,15 @@ function extractMCPServerNames(tools: string[] | undefined | null): string[] {
|
|||
return Array.from(serverNames);
|
||||
}
|
||||
|
||||
function removeUnsetFields(target: Record<string, unknown>, unsetUpdates: unknown): void {
|
||||
if (!unsetUpdates || typeof unsetUpdates !== 'object' || Array.isArray(unsetUpdates)) {
|
||||
return;
|
||||
}
|
||||
for (const key of Object.keys(unsetUpdates)) {
|
||||
delete target[key];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a version already exists in the versions array, excluding timestamp and author fields.
|
||||
*/
|
||||
|
|
@ -84,13 +93,20 @@ function isDuplicateVersion(
|
|||
'actionsHash',
|
||||
];
|
||||
|
||||
const { $push: _$push, $pull: _$pull, $addToSet: _$addToSet, ...directUpdates } = updateData;
|
||||
const {
|
||||
$push: _$push,
|
||||
$pull: _$pull,
|
||||
$addToSet: _$addToSet,
|
||||
$unset,
|
||||
...directUpdates
|
||||
} = updateData;
|
||||
|
||||
if (Object.keys(directUpdates).length === 0 && !actionsHash) {
|
||||
if (Object.keys(directUpdates).length === 0 && !$unset && !actionsHash) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const wouldBeVersion = { ...currentData, ...directUpdates } as Record<string, unknown>;
|
||||
removeUnsetFields(wouldBeVersion, $unset);
|
||||
const lastVersion = versions[versions.length - 1] as Record<string, unknown>;
|
||||
|
||||
if (actionsHash && lastVersion.actionsHash !== actionsHash) {
|
||||
|
|
@ -310,7 +326,7 @@ export function createAgentMethods(mongoose: typeof import('mongoose'), deps: Ag
|
|||
author: _author,
|
||||
...versionData
|
||||
} = currentAgent.toObject() as unknown as Record<string, unknown>;
|
||||
const { $push, $pull, $addToSet, ...directUpdates } = updateData;
|
||||
const { $push, $pull, $addToSet, $unset, ...directUpdates } = updateData;
|
||||
|
||||
// Sync mcpServerNames when tools are updated
|
||||
if ((directUpdates as Record<string, unknown>).tools !== undefined) {
|
||||
|
|
@ -348,7 +364,12 @@ export function createAgentMethods(mongoose: typeof import('mongoose'), deps: Ag
|
|||
|
||||
const shouldCreateVersion =
|
||||
!skipVersioning &&
|
||||
(forceVersion || Object.keys(directUpdates).length > 0 || $push || $pull || $addToSet);
|
||||
(forceVersion ||
|
||||
Object.keys(directUpdates).length > 0 ||
|
||||
$push ||
|
||||
$pull ||
|
||||
$addToSet ||
|
||||
$unset);
|
||||
|
||||
if (shouldCreateVersion) {
|
||||
const duplicateVersion = isDuplicateVersion(
|
||||
|
|
@ -372,6 +393,7 @@ export function createAgentMethods(mongoose: typeof import('mongoose'), deps: Ag
|
|||
...directUpdates,
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
removeUnsetFields(versionEntry, $unset);
|
||||
|
||||
if (actionsHash) {
|
||||
versionEntry.actionsHash = actionsHash;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue