File size: 4,049 Bytes
21ad36a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import type { EngineChatMessage, EngineChatTool } from '../engine';

export const RELEASE_PROMPT_MATRIX_PATH = 'fixtures/release-prompts.json';
export const RELEASE_PROMPT_MATRIX_MIN_CASES = 6;
export const RELEASE_PROMPT_MATRIX_MAX_TOKENS = 64;

export interface ReleasePromptCase {
  id: string;
  messages: EngineChatMessage[];
  tools: EngineChatTool[];
}

function record(value: unknown, field: string): Record<string, unknown> {
  if (typeof value !== 'object' || value === null || Array.isArray(value)) {
    throw new Error(`${field} must be an object.`);
  }
  return value as Record<string, unknown>;
}

function nonEmptyString(value: unknown, field: string): string {
  if (typeof value !== 'string' || value.trim() === '') throw new Error(`${field} must be a non-empty string.`);
  return value;
}

function parseMessage(value: unknown, field: string): EngineChatMessage {
  const message = record(value, field);
  const role = nonEmptyString(message.role, `${field}.role`);
  const content = nonEmptyString(message.content, `${field}.content`);
  if (role !== 'system' && role !== 'user' && role !== 'assistant') {
    throw new Error(`${field}.role must be system, user, or assistant.`);
  }
  return { role, content };
}

function parseTool(value: unknown, field: string): EngineChatTool {
  const tool = record(value, field);
  if (tool.type !== 'function') throw new Error(`${field}.type must be function.`);
  const fn = record(tool.function, `${field}.function`);
  const parameters = record(fn.parameters, `${field}.function.parameters`);
  if (parameters.type !== 'object') throw new Error(`${field}.function.parameters.type must be object.`);
  const properties = record(parameters.properties, `${field}.function.parameters.properties`);
  return {
    type: 'function',
    function: {
      name: nonEmptyString(fn.name, `${field}.function.name`),
      ...(typeof fn.description === 'string' ? { description: fn.description } : {}),
      parameters: {
        type: 'object',
        properties: properties as EngineChatTool['function']['parameters']['properties'],
        ...(Array.isArray(parameters.required)
          ? { required: parameters.required.map((entry, index) => nonEmptyString(entry, `${field}.function.parameters.required[${index}]`)) }
          : {}),
      },
    },
  };
}

export function parseReleasePromptMatrix(value: unknown): ReleasePromptCase[] {
  if (!Array.isArray(value) || value.length < RELEASE_PROMPT_MATRIX_MIN_CASES) {
    throw new Error(`Release prompt matrix requires at least ${RELEASE_PROMPT_MATRIX_MIN_CASES} cases.`);
  }
  const ids = new Set<string>();
  const cases = value.map((entry, caseIndex) => {
    const item = record(entry, `cases[${caseIndex}]`);
    const id = nonEmptyString(item.id, `cases[${caseIndex}].id`);
    if (ids.has(id)) throw new Error(`Duplicate release prompt id: ${id}.`);
    ids.add(id);
    if (!Array.isArray(item.messages) || item.messages.length === 0) {
      throw new Error(`cases[${caseIndex}].messages must be a non-empty array.`);
    }
    const messages = item.messages.map((message, messageIndex) => (
      parseMessage(message, `cases[${caseIndex}].messages[${messageIndex}]`)
    ));
    const tools = item.tools === undefined
      ? []
      : Array.isArray(item.tools)
        ? item.tools.map((tool, toolIndex) => parseTool(tool, `cases[${caseIndex}].tools[${toolIndex}]`))
        : (() => { throw new Error(`cases[${caseIndex}].tools must be an array.`); })();
    return { id, messages, tools };
  });

  const multiturn = cases.find((entry) => entry.id === 'multiturn');
  if (!multiturn || multiturn.messages.length < 4 || !multiturn.messages.some((message) => message.role === 'assistant')) {
    throw new Error('Release prompt matrix requires the locked multi-turn assistant-history case.');
  }
  const toolCall = cases.find((entry) => entry.id === 'tool_call');
  if (!toolCall || toolCall.tools.length === 0) {
    throw new Error('Release prompt matrix requires the locked tool-call case.');
  }
  return cases;
}