234 lines
6.5 KiB
JavaScript
234 lines
6.5 KiB
JavaScript
#!/usr/bin/env node
|
||
|
||
const fs = require("node:fs");
|
||
const path = require("node:path");
|
||
const request = require("supertest");
|
||
|
||
function parseArgs(argv) {
|
||
const args = {
|
||
questionsFile: "",
|
||
runDir: "",
|
||
rawFileName: "chat20_wave13_raw.json",
|
||
chatFileName: "Chat20.txt",
|
||
chatRuFileName: "Чат20.txt",
|
||
promptsFileName: path.join("prompt_dialogs", "chat20_prompts.md"),
|
||
useMock: true,
|
||
promptVersion: "normalizer_v2_0_2",
|
||
sessionId: "",
|
||
casePrefix: "q"
|
||
};
|
||
|
||
for (let i = 0; i < argv.length; i += 1) {
|
||
const token = argv[i];
|
||
if (token === "--questions-file") {
|
||
args.questionsFile = String(argv[i + 1] ?? "");
|
||
i += 1;
|
||
continue;
|
||
}
|
||
if (token === "--run-dir") {
|
||
args.runDir = String(argv[i + 1] ?? "");
|
||
i += 1;
|
||
continue;
|
||
}
|
||
if (token === "--raw-file") {
|
||
args.rawFileName = String(argv[i + 1] ?? args.rawFileName);
|
||
i += 1;
|
||
continue;
|
||
}
|
||
if (token === "--chat-file") {
|
||
args.chatFileName = String(argv[i + 1] ?? args.chatFileName);
|
||
i += 1;
|
||
continue;
|
||
}
|
||
if (token === "--chat-ru-file") {
|
||
args.chatRuFileName = String(argv[i + 1] ?? args.chatRuFileName);
|
||
i += 1;
|
||
continue;
|
||
}
|
||
if (token === "--prompts-file") {
|
||
args.promptsFileName = String(argv[i + 1] ?? args.promptsFileName);
|
||
i += 1;
|
||
continue;
|
||
}
|
||
if (token === "--use-mock") {
|
||
const value = String(argv[i + 1] ?? "true").toLowerCase();
|
||
args.useMock = value !== "0" && value !== "false" && value !== "no";
|
||
i += 1;
|
||
continue;
|
||
}
|
||
if (token === "--prompt-version") {
|
||
args.promptVersion = String(argv[i + 1] ?? args.promptVersion);
|
||
i += 1;
|
||
continue;
|
||
}
|
||
if (token === "--session-id") {
|
||
args.sessionId = String(argv[i + 1] ?? "");
|
||
i += 1;
|
||
continue;
|
||
}
|
||
if (token === "--case-prefix") {
|
||
args.casePrefix = String(argv[i + 1] ?? args.casePrefix);
|
||
i += 1;
|
||
}
|
||
}
|
||
|
||
return args;
|
||
}
|
||
|
||
function ensureDir(dirPath) {
|
||
fs.mkdirSync(dirPath, { recursive: true });
|
||
}
|
||
|
||
function readJson(filePath) {
|
||
const raw = fs.readFileSync(filePath, "utf8").replace(/^\uFEFF/, "");
|
||
return JSON.parse(raw);
|
||
}
|
||
|
||
function writeUtf8Bom(filePath, content) {
|
||
ensureDir(path.dirname(filePath));
|
||
fs.writeFileSync(filePath, `\uFEFF${content}`, "utf8");
|
||
}
|
||
|
||
function asText(value) {
|
||
return value == null ? "" : String(value);
|
||
}
|
||
|
||
function makeCaseId(prefix, index) {
|
||
return `${prefix}${String(index + 1).padStart(2, "0")}`;
|
||
}
|
||
|
||
function buildPromptsMarkdown(questions) {
|
||
const lines = [];
|
||
for (let i = 0; i < questions.length; i += 1) {
|
||
lines.push(`${i + 1}. ${questions[i]}`);
|
||
lines.push("");
|
||
}
|
||
return `${lines.join("\n").trim()}\n`;
|
||
}
|
||
|
||
function buildChatTxt(sessionId, exportedAt, rows) {
|
||
const lines = [];
|
||
lines.push("# Assistant conversation export");
|
||
lines.push(`session_id: ${sessionId}`);
|
||
lines.push(`exported_at: ${exportedAt}`);
|
||
lines.push("");
|
||
|
||
let messageCounter = 1;
|
||
for (const row of rows) {
|
||
lines.push(`## ${messageCounter}. user`);
|
||
lines.push("message_id: pending");
|
||
lines.push("created_at: pending");
|
||
lines.push("reply_type: n/a");
|
||
lines.push("");
|
||
lines.push(asText(row.user_message));
|
||
lines.push("");
|
||
messageCounter += 1;
|
||
|
||
lines.push(`## ${messageCounter}. assistant`);
|
||
lines.push(`message_id: ${asText(row.message_id) || "n/a"}`);
|
||
lines.push(`created_at: ${asText(row.created_at) || "n/a"}`);
|
||
lines.push(`reply_type: ${asText(row.reply_type) || "n/a"}`);
|
||
if (row.trace_id) {
|
||
lines.push(`trace_id: ${asText(row.trace_id)}`);
|
||
}
|
||
lines.push("");
|
||
lines.push(asText(row.assistant_reply));
|
||
lines.push("");
|
||
messageCounter += 1;
|
||
}
|
||
|
||
return `${lines.join("\n").trim()}\n`;
|
||
}
|
||
|
||
async function main() {
|
||
const args = parseArgs(process.argv.slice(2));
|
||
if (!args.questionsFile) {
|
||
throw new Error("Missing required argument --questions-file");
|
||
}
|
||
if (!args.runDir) {
|
||
throw new Error("Missing required argument --run-dir");
|
||
}
|
||
|
||
const questionsPath = path.resolve(args.questionsFile);
|
||
const runDir = path.resolve(args.runDir);
|
||
const backendRoot = path.resolve(__dirname, "..");
|
||
|
||
const questions = readJson(questionsPath);
|
||
if (!Array.isArray(questions) || questions.length === 0) {
|
||
throw new Error("Questions JSON must be a non-empty array of strings.");
|
||
}
|
||
|
||
const { createApp } = require(path.join(backendRoot, "dist", "server.js"));
|
||
const app = createApp();
|
||
|
||
ensureDir(runDir);
|
||
ensureDir(path.join(runDir, "prompt_dialogs"));
|
||
|
||
const rows = [];
|
||
let sessionId = args.sessionId || `wave13-chat20-${Date.now()}`;
|
||
|
||
for (let i = 0; i < questions.length; i += 1) {
|
||
const userMessage = asText(questions[i]);
|
||
const response = await request(app).post("/api/assistant/message").send({
|
||
useMock: args.useMock,
|
||
promptVersion: args.promptVersion,
|
||
session_id: sessionId,
|
||
user_message: userMessage
|
||
});
|
||
|
||
const body = response.body || {};
|
||
sessionId = asText(body.session_id) || sessionId;
|
||
const item = body.conversation_item || {};
|
||
rows.push({
|
||
case_id: makeCaseId(args.casePrefix, i),
|
||
user_message: userMessage,
|
||
assistant_reply: asText(body.assistant_reply),
|
||
reply_type: asText(body.reply_type),
|
||
message_id: asText(item.message_id),
|
||
created_at: asText(item.created_at),
|
||
trace_id: asText(item.trace_id || body.debug?.trace_id),
|
||
http_status: response.status,
|
||
debug: body.debug || {}
|
||
});
|
||
}
|
||
|
||
const exportedAt = new Date().toISOString();
|
||
const rawPayload = {
|
||
session_id: sessionId,
|
||
exported_at: exportedAt,
|
||
cases_total: rows.length,
|
||
rows
|
||
};
|
||
|
||
const rawPath = path.join(runDir, args.rawFileName);
|
||
const chatPath = path.join(runDir, args.chatFileName);
|
||
const chatRuPath = path.join(runDir, args.chatRuFileName);
|
||
const promptsPath = path.join(runDir, args.promptsFileName);
|
||
|
||
writeUtf8Bom(rawPath, `${JSON.stringify(rawPayload, null, 2)}\n`);
|
||
const chatBody = buildChatTxt(sessionId, exportedAt, rows);
|
||
writeUtf8Bom(chatPath, chatBody);
|
||
if (args.chatRuFileName) {
|
||
writeUtf8Bom(chatRuPath, chatBody);
|
||
}
|
||
writeUtf8Bom(promptsPath, buildPromptsMarkdown(questions));
|
||
|
||
process.stdout.write(
|
||
[
|
||
`run_dir=${runDir}`,
|
||
`session_id=${sessionId}`,
|
||
`cases_total=${rows.length}`,
|
||
`raw=${rawPath}`,
|
||
`chat=${chatPath}`,
|
||
`chat_ru=${chatRuPath}`,
|
||
`prompts=${promptsPath}`
|
||
].join("\n")
|
||
);
|
||
}
|
||
|
||
main().catch((error) => {
|
||
process.stderr.write(`${error?.stack || error}\n`);
|
||
process.exitCode = 1;
|
||
});
|
||
|