mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-18 01:10:14 +01:00
🔄 refactor: Apply Config Preset for Model Spec Enforcement (#4214)
This commit is contained in:
parent
17e59349ff
commit
321260e3c7
3 changed files with 6 additions and 111 deletions
|
|
@ -10,7 +10,6 @@ const openAI = require('~/server/services/Endpoints/openAI');
|
||||||
const agents = require('~/server/services/Endpoints/agents');
|
const agents = require('~/server/services/Endpoints/agents');
|
||||||
const custom = require('~/server/services/Endpoints/custom');
|
const custom = require('~/server/services/Endpoints/custom');
|
||||||
const google = require('~/server/services/Endpoints/google');
|
const google = require('~/server/services/Endpoints/google');
|
||||||
const enforceModelSpec = require('./enforceModelSpec');
|
|
||||||
const { handleError } = require('~/server/utils');
|
const { handleError } = require('~/server/utils');
|
||||||
|
|
||||||
const buildFunction = {
|
const buildFunction = {
|
||||||
|
|
@ -28,7 +27,7 @@ const buildFunction = {
|
||||||
|
|
||||||
async function buildEndpointOption(req, res, next) {
|
async function buildEndpointOption(req, res, next) {
|
||||||
const { endpoint, endpointType } = req.body;
|
const { endpoint, endpointType } = req.body;
|
||||||
const parsedBody = parseCompactConvo({ endpoint, endpointType, conversation: req.body });
|
let parsedBody = parseCompactConvo({ endpoint, endpointType, conversation: req.body });
|
||||||
|
|
||||||
if (req.app.locals.modelSpecs?.list && req.app.locals.modelSpecs?.enforce) {
|
if (req.app.locals.modelSpecs?.list && req.app.locals.modelSpecs?.enforce) {
|
||||||
/** @type {{ list: TModelSpec[] }}*/
|
/** @type {{ list: TModelSpec[] }}*/
|
||||||
|
|
@ -57,10 +56,11 @@ async function buildEndpointOption(req, res, next) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const isValidModelSpec = enforceModelSpec(currentModelSpec, parsedBody);
|
parsedBody = parseCompactConvo({
|
||||||
if (!isValidModelSpec) {
|
endpoint,
|
||||||
return handleError(res, { text: 'Model spec mismatch' });
|
endpointType,
|
||||||
}
|
conversation: currentModelSpec.preset,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const endpointFn = buildFunction[endpointType ?? endpoint];
|
const endpointFn = buildFunction[endpointType ?? endpoint];
|
||||||
|
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
const interchangeableKeys = new Map([
|
|
||||||
['chatGptLabel', ['modelLabel']],
|
|
||||||
['modelLabel', ['chatGptLabel']],
|
|
||||||
]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Middleware to enforce the model spec for a conversation
|
|
||||||
* @param {TModelSpec} modelSpec - The model spec to enforce
|
|
||||||
* @param {TConversation} parsedBody - The parsed body of the conversation
|
|
||||||
* @returns {boolean} - Whether the model spec is enforced
|
|
||||||
*/
|
|
||||||
const enforceModelSpec = (modelSpec, parsedBody) => {
|
|
||||||
for (const [key, value] of Object.entries(modelSpec.preset)) {
|
|
||||||
if (key === 'endpoint') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!checkMatch(key, value, parsedBody)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if there is a match for the given key and value in the parsed body
|
|
||||||
* or any of its interchangeable keys, including deep comparison for objects and arrays.
|
|
||||||
* @param {string} key
|
|
||||||
* @param {any} value
|
|
||||||
* @param {object} parsedBody
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
const checkMatch = (key, value, parsedBody) => {
|
|
||||||
const isEqual = (a, b) => {
|
|
||||||
if (Array.isArray(a) && Array.isArray(b)) {
|
|
||||||
return a.length === b.length && a.every((val, index) => isEqual(val, b[index]));
|
|
||||||
} else if (typeof a === 'object' && typeof b === 'object' && a !== null && b !== null) {
|
|
||||||
const keysA = Object.keys(a);
|
|
||||||
const keysB = Object.keys(b);
|
|
||||||
return keysA.length === keysB.length && keysA.every((k) => isEqual(a[k], b[k]));
|
|
||||||
}
|
|
||||||
return a === b;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isEqual(parsedBody[key], value)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interchangeableKeys.has(key)) {
|
|
||||||
return interchangeableKeys
|
|
||||||
.get(key)
|
|
||||||
.some((interchangeableKey) => isEqual(parsedBody[interchangeableKey], value));
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = enforceModelSpec;
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
// enforceModelSpec.test.js
|
|
||||||
|
|
||||||
const enforceModelSpec = require('./enforceModelSpec');
|
|
||||||
|
|
||||||
describe('enforceModelSpec function', () => {
|
|
||||||
test('returns true when all model specs match parsed body directly', () => {
|
|
||||||
const modelSpec = { preset: { title: 'Dialog', status: 'Active' } };
|
|
||||||
const parsedBody = { title: 'Dialog', status: 'Active' };
|
|
||||||
expect(enforceModelSpec(modelSpec, parsedBody)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('returns true when model specs match via interchangeable keys', () => {
|
|
||||||
const modelSpec = { preset: { chatGptLabel: 'GPT-4' } };
|
|
||||||
const parsedBody = { modelLabel: 'GPT-4' };
|
|
||||||
expect(enforceModelSpec(modelSpec, parsedBody)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('returns false if any key value does not match', () => {
|
|
||||||
const modelSpec = { preset: { language: 'English', level: 'Advanced' } };
|
|
||||||
const parsedBody = { language: 'Spanish', level: 'Advanced' };
|
|
||||||
expect(enforceModelSpec(modelSpec, parsedBody)).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('ignores the \'endpoint\' key in model spec', () => {
|
|
||||||
const modelSpec = { preset: { endpoint: 'ignored', feature: 'Special' } };
|
|
||||||
const parsedBody = { feature: 'Special' };
|
|
||||||
expect(enforceModelSpec(modelSpec, parsedBody)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('handles nested objects correctly', () => {
|
|
||||||
const modelSpec = { preset: { details: { time: 'noon', location: 'park' } } };
|
|
||||||
const parsedBody = { details: { time: 'noon', location: 'park' } };
|
|
||||||
expect(enforceModelSpec(modelSpec, parsedBody)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('handles arrays within objects', () => {
|
|
||||||
const modelSpec = { preset: { tags: ['urgent', 'important'] } };
|
|
||||||
const parsedBody = { tags: ['urgent', 'important'] };
|
|
||||||
expect(enforceModelSpec(modelSpec, parsedBody)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('fails when arrays in objects do not match', () => {
|
|
||||||
const modelSpec = { preset: { tags: ['urgent', 'important'] } };
|
|
||||||
const parsedBody = { tags: ['important', 'urgent'] }; // Different order
|
|
||||||
expect(enforceModelSpec(modelSpec, parsedBody)).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue