openai-mock-api
Version:
A mock OpenAI API server for testing LLM applications
108 lines • 4.98 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConfigLoader = void 0;
const fs_1 = require("fs");
const yaml_1 = __importDefault(require("yaml"));
class ConfigLoader {
constructor(logger) {
this.logger = logger;
}
async load(configPath) {
try {
let fileContent;
if (configPath === '-') {
this.logger.debug('Loading configuration from stdin');
fileContent = await this.readFromStdin();
}
else {
this.logger.debug(`Loading configuration from ${configPath}`);
fileContent = (0, fs_1.readFileSync)(configPath, 'utf-8');
}
const config = yaml_1.default.parse(fileContent);
this.validateConfig(config);
this.logger.debug('Configuration loaded successfully', {
responseCount: config.responses.length,
port: config.port,
});
return config;
}
catch (error) {
this.logger.error(`Failed to load configuration: ${error}`);
const source = configPath === '-' ? 'stdin' : configPath;
throw new Error(`Failed to load configuration from ${source}: ${error}`);
}
}
readFromStdin() {
return new Promise((resolve, reject) => {
let data = '';
process.stdin.setEncoding('utf8');
process.stdin.on('data', (chunk) => {
data += chunk;
});
process.stdin.on('end', () => {
resolve(data);
});
process.stdin.on('error', (err) => {
reject(err);
});
// Start reading
process.stdin.resume();
});
}
validateConfig(config) {
if (!config.apiKey) {
throw new Error('Configuration must include an apiKey');
}
if (!config.responses || !Array.isArray(config.responses)) {
throw new Error('Configuration must include a responses array');
}
if (config.responses.length === 0) {
throw new Error('Configuration must include at least one response');
}
for (const [index, response] of config.responses.entries()) {
if (!response.id) {
throw new Error(`Response at index ${index} must have an id`);
}
if (!response.messages || !Array.isArray(response.messages)) {
throw new Error(`Response ${response.id} must have a messages array`);
}
if (response.messages.length === 0) {
throw new Error(`Response ${response.id} must have at least one message`);
}
for (const [msgIndex, message] of response.messages.entries()) {
if (!['system', 'user', 'assistant', 'tool'].includes(message.role)) {
throw new Error(`Response ${response.id} message ${msgIndex} role must be 'system', 'user', 'assistant', or 'tool'`);
}
if (message.matcher &&
!['exact', 'fuzzy', 'regex', 'contains', 'any'].includes(message.matcher)) {
throw new Error(`Response ${response.id} message ${msgIndex} matcher type must be 'exact', 'fuzzy', 'regex', 'contains', or 'any'`);
}
if (message.matcher === 'fuzzy' && typeof message.threshold !== 'number') {
throw new Error(`Response ${response.id} message ${msgIndex} fuzzy matcher must have a threshold number`);
}
if (message.matcher === 'any' && message.content !== undefined) {
throw new Error(`Response ${response.id} message ${msgIndex} with 'any' matcher should not have content`);
}
if (message.matcher !== 'any' &&
message.role !== 'assistant' &&
!message.content &&
!message.tool_calls) {
throw new Error(`Response ${response.id} message ${msgIndex} must have content or tool_calls`);
}
if (message.role === 'tool' && !message.tool_call_id) {
throw new Error(`Response ${response.id} message ${msgIndex} with role 'tool' must have tool_call_id`);
}
}
// Validate that there's at least one assistant message to use as response
const assistantMessages = response.messages.filter((msg) => msg.role === 'assistant');
if (assistantMessages.length === 0) {
throw new Error(`Response ${response.id} must have at least one assistant message`);
}
}
}
}
exports.ConfigLoader = ConfigLoader;
//# sourceMappingURL=config.js.map