Skip to content

Commit

Permalink
👮 fix(enforceModelSpec): handle nested objects (#2681)
Browse files Browse the repository at this point in the history
  • Loading branch information
danny-avila authored May 12, 2024
1 parent c83d9d6 commit 8989916
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 4 deletions.
9 changes: 9 additions & 0 deletions api/server/middleware/buildEndpointOption.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ async function buildEndpointOption(req, res, next) {
return handleError(res, { text: 'Model spec mismatch' });
}

if (
currentModelSpec.preset.endpoint !== EModelEndpoint.gptPlugins &&
currentModelSpec.preset.tools
) {
return handleError(res, {
text: `Only the "${EModelEndpoint.gptPlugins}" endpoint can have tools defined in the preset`,
});
}

const isValidModelSpec = enforceModelSpec(currentModelSpec, parsedBody);
if (!isValidModelSpec) {
return handleError(res, { text: 'Model spec mismatch' });
Expand Down
19 changes: 15 additions & 4 deletions api/server/middleware/enforceModelSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,32 @@ const enforceModelSpec = (modelSpec, parsedBody) => {

/**
* Checks if there is a match for the given key and value in the parsed body
* or any of its interchangeable keys.
* or any of its interchangeable keys, including deep comparison for objects and arrays.
* @param {string} key
* @param {any} value
* @param {TConversation} parsedBody
* @param {object} parsedBody
* @returns {boolean}
*/
const checkMatch = (key, value, parsedBody) => {
if (parsedBody[key] === value) {
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) => parsedBody[interchangeableKey] === value);
.some((interchangeableKey) => isEqual(parsedBody[interchangeableKey], value));
}

return false;
Expand Down
47 changes: 47 additions & 0 deletions api/server/middleware/enforceModelSpec.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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);
});
});

0 comments on commit 8989916

Please sign in to comment.