File size: 1,707 Bytes
aa2e6af
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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;