Files
sqlmon/summarize_sql.js
2025-09-07 05:20:25 +02:00

146 lines
4.8 KiB
JavaScript

// summarize_sql.js
// Usage: node summarize_sql.js <inputfile.sql>
// Requires: OPENAI_API_KEY in environment
require('dotenv').config();
const fs = require('fs');
const path = require('path');
const { OpenAI } = require('openai');
if (!process.env.OPENAI_API_KEY) {
console.error('Error: OPENAI_API_KEY environment variable is required.');
console.error('Please create a .env file with your OpenAI API key or set the environment variable.');
process.exit(1);
}
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
if (process.argv.length < 3) {
console.error('Usage: node summarize_sql.js <inputfile.sql>');
process.exit(1);
}
const inputFile = process.argv[2];
const inputDir = path.dirname(inputFile);
const baseName = path.basename(inputFile, '.sql');
const summaryFile = path.join(inputDir, baseName + '.summary.txt');
function findTableOrViewFile(schemaDotName) {
// e.g. "Amazon.tVcsLiteUploadQueue" => tables/Amazon.tVcsLiteUploadQueue.sql
const tablesDir = path.join(__dirname, 'tables');
const file = path.join(tablesDir, schemaDotName + '.sql');
if (fs.existsSync(file)) {
return file;
}
return null;
}
function readTableOrView(schemaDotName) {
const file = findTableOrViewFile(schemaDotName);
if (!file) return `-- Definition for ${schemaDotName} not found.`;
return fs.readFileSync(file, 'utf8');
}
const SYSTEM_PROMPT = `
You are a SQL expert assistant. Your task is to help the user understand the definition and logic of a given SQL stored procedure or view.
- Focus on explaining the purpose, main logic, and important details of the procedure/view.
- If you need to look up the definition of a table or view referenced in the code, you can call the function read_table_or_view(schemaDotName) (e.g. read_table_or_view('Amazon.tVcsLiteUploadQueue')) and you will receive the full CREATE statement for that object.
- If you need more than one table/view definition, call read_table_or_view multiple times.
- Be concise but thorough. Output your summary in clear, readable language.
`;
async function main() {
const sqlText = fs.readFileSync(inputFile, 'utf8');
let messages = [
{ role: 'system', content: SYSTEM_PROMPT },
{ role: 'user', content: `Please summarize the following SQL stored procedure or view:\n\n${sqlText}` }
];
// Function tool definition for OpenAI function-calling
const functions = [
{
name: "read_table_or_view",
description: "Get the CREATE statement for a table or view by schema.name",
parameters: {
type: "object",
properties: {
schemaDotName: {
type: "string",
description: "The schema and name, e.g. 'Amazon.tVcsLiteUploadQueue'"
}
},
required: ["schemaDotName"]
}
}
];
let summary = null;
let maxToolCalls = 5; // Prevent infinite loops
while (maxToolCalls-- > 0) {
// Log model call
console.log('\n--- Model Call ---');
console.log('Model:', "o4-mini");
console.log('Messages:', JSON.stringify(messages, null, 2));
console.log('Functions:', JSON.stringify(functions, null, 2));
const response = await openai.chat.completions.create({
model: "o4-mini", // or "gpt-3.5-turbo-1106" if you don't have access to gpt-4o
messages,
service_tier: "flex",
functions,
function_call: "auto"
});
const msg = response.choices[0].message;
if (msg.content) {
summary = msg.content;
break;
} else if (msg.function_call) {
// LLM is calling our tool
const { name, arguments: argsJson } = msg.function_call;
console.log('\n--- Tool Call ---');
console.log('Function:', name);
console.log('Arguments:', argsJson);
if (name === "read_table_or_view") {
let args;
try {
args = JSON.parse(argsJson);
} catch (e) {
messages.push({ role: 'assistant', content: "Error: Invalid function call arguments." });
continue;
}
const def = readTableOrView(args.schemaDotName);
// Log tool result (truncate if large)
const defPreview = def.length > 500 ? def.slice(0, 500) + '...[truncated]' : def;
console.log('Result:', defPreview);
messages.push({
role: 'function',
name: 'read_table_or_view',
content: def
});
} else {
messages.push({ role: 'assistant', content: `Error: Unknown function ${name}` });
}
} else {
messages.push({ role: 'assistant', content: "Error: No content or function call in response." });
}
}
if (!summary) {
summary = "Error: Could not generate summary after several tool calls.";
}
fs.writeFileSync(summaryFile, summary, 'utf8');
console.log(`Summary written to ${summaryFile}`);
}
main().catch(e => {
console.error(e);
process.exit(1);
});
hi