mirror of
https://gitlab.rlp.net/proj-wise2526-video2document/video2document.git
synced 2026-06-15 18:01:52 +02:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 948ddb4310 | |||
| 889d455fbe | |||
| 58741f0057 | |||
| a8c284edd2 | |||
| 3a5808d97c | |||
| 4083d5a5cb | |||
| d655925031 | |||
| 405a32098c | |||
| 1683502aa1 | |||
| 789ecd3a31 | |||
| e72d03efbe | |||
| ec57411992 | |||
| 271fe78b7b | |||
| 1e38cc79f4 | |||
| 746fec05d4 | |||
| d647f53790 | |||
| 5ea8ec6a1a | |||
| 7cd334645f | |||
| 15e2e35bda | |||
| 53508b175a | |||
| 3dd8485140 | |||
| 68c1f0ed9f | |||
| 2b597add6c | |||
| 3af038d195 | |||
| b546c96238 | |||
| 425e24853e |
@@ -185,7 +185,7 @@ electron.ipcMain.on("file_submit", async (event, args) => {
|
||||
for (let i = 0; i < args.document.styles.length; i++) {
|
||||
console.log(`\n\n Running the LLM for Document Style ${i+1}`);
|
||||
|
||||
await mapFunctions.get("module-handler").function(args.document.module, {inputTranscriptPath: transcriptpath, documentTypePath: "./storage/documentType/meetingReport.json", language: "en"}).then(resp => {
|
||||
await mapFunctions.get("module-handler").function(args.document.module, {inputTranscriptPath: transcriptpath, documentTypePath: "./storage/documentType/standard_meeting_report.txt", language: "en"}).then(resp => {
|
||||
console.log(resp);
|
||||
transcriptpath = resp
|
||||
curstep++
|
||||
|
||||
Generated
+1407
-2
File diff suppressed because it is too large
Load Diff
+3
-1
@@ -8,7 +8,9 @@
|
||||
"express": "^5.1.0",
|
||||
"ffmpeg-static": "^5.2.0",
|
||||
"fluent-ffmpeg": "^2.1.3",
|
||||
"mocha": "^11.7.5"
|
||||
"html-to-docx": "^1.8.0",
|
||||
"mocha": "^11.7.5",
|
||||
"puppeteer": "^24.33.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cli-progress": "^3.11.6",
|
||||
|
||||
+3
-1
@@ -6,6 +6,9 @@ platform = process.platform
|
||||
mainDir = __dirname
|
||||
fs = require("fs")
|
||||
readline = require("readline")
|
||||
|
||||
puppeteer = require("puppeteer")
|
||||
htmltodocx = require("html-to-docx")
|
||||
config = require("./config/config")
|
||||
|
||||
ffmpegPath = require('ffmpeg-static');
|
||||
@@ -16,7 +19,6 @@ path = require('path');
|
||||
// { app, BrowserWindow, ipcMain, dialog } = require('electron');
|
||||
|
||||
electron = require('electron');
|
||||
genai = require("@google/genai");
|
||||
|
||||
axios = require("axios")
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
//node show-models.js, remember to set SAIA_API_KEY in your environment before running the script
|
||||
|
||||
const SAIA_API_KEY = process.env.SAIA_API_KEY;
|
||||
const SAIA_MODELS_URL = "https://chat-ai.academiccloud.de/v1/models";
|
||||
|
||||
// Script to list available models
|
||||
(async () => {
|
||||
if (!SAIA_API_KEY) {
|
||||
console.error("ERROR: SAIA_API_KEY environment variable is not set!");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log("Fetching available models from SAIA...\n");
|
||||
|
||||
try {
|
||||
const response = await fetch(SAIA_MODELS_URL, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": `Bearer ${SAIA_API_KEY}`,
|
||||
"Accept": "application/json"
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text();
|
||||
throw new Error(`SAIA API error (${response.status}): ${text}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
console.log("Available models:");
|
||||
console.log(JSON.stringify(data, null, 2));
|
||||
|
||||
if (data.data && Array.isArray(data.data)) {
|
||||
console.log("\n\nModel IDs:");
|
||||
data.data.forEach(model => {
|
||||
console.log(`- ${model.id}`);
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error fetching models:", error);
|
||||
}
|
||||
})();
|
||||
@@ -0,0 +1,194 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const puppeteer = require('puppeteer');
|
||||
const htmlToDocx = require('html-to-docx');
|
||||
const { execSync } = require('child_process');
|
||||
const os = require('os');
|
||||
|
||||
const outputDir = path.join(__dirname, "../../../storage/documents");
|
||||
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
}
|
||||
|
||||
async function showSaveDialog(defaultName, format) {
|
||||
const platform = os.platform();
|
||||
|
||||
if (platform === 'darwin') {
|
||||
// macOS
|
||||
const applescript = `
|
||||
set defaultName to "${defaultName}.${format}"
|
||||
set theFile to choose file name with prompt "Dokument speichern als:" default name defaultName
|
||||
POSIX path of theFile
|
||||
`;
|
||||
|
||||
try {
|
||||
const result = execSync(`osascript -e '${applescript}'`, { encoding: 'utf8' });
|
||||
return result.trim();
|
||||
} catch (err) {
|
||||
if (err.status === 1) return null; // User canceled
|
||||
throw err;
|
||||
}
|
||||
} else if (platform === 'win32') {
|
||||
// Windows
|
||||
const powershell = `
|
||||
Add-Type -AssemblyName System.Windows.Forms
|
||||
$dialog = New-Object System.Windows.Forms.SaveFileDialog
|
||||
$dialog.FileName = "${defaultName}.${format}"
|
||||
$dialog.Filter = "${format.toUpperCase()} Dateien (*.${format})|*.${format}|Alle Dateien (*.*)|*.*"
|
||||
$dialog.Title = "Dokument speichern als"
|
||||
$result = $dialog.ShowDialog()
|
||||
if ($result -eq 'OK') { $dialog.FileName }
|
||||
`;
|
||||
|
||||
try {
|
||||
const result = execSync(`powershell -Command "${powershell.replace(/\n/g, '; ')}"`, {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
return result.trim() || null;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
// Linux - zenity oder kdialog
|
||||
try {
|
||||
const result = execSync(
|
||||
`zenity --file-selection --save --confirm-overwrite --filename="${defaultName}.${format}"`,
|
||||
{ encoding: 'utf8' }
|
||||
);
|
||||
return result.trim();
|
||||
} catch (err) {
|
||||
try {
|
||||
const result = execSync(
|
||||
`kdialog --getsavefilename . "${defaultName}.${format}"`,
|
||||
{ encoding: 'utf8' }
|
||||
);
|
||||
return result.trim();
|
||||
} catch (err2) {
|
||||
// Fallback
|
||||
return path.join(os.homedir(), 'Downloads', `${defaultName}.${format}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const module_exports = {
|
||||
name: "htmlDocumentConverter",
|
||||
type: "converter",
|
||||
displayname: "HTML Document Converter",
|
||||
description: "Converts LLM-generated HTML to PDF, DOCX, TXT, or HTML",
|
||||
|
||||
/**
|
||||
* Main conversion function
|
||||
* @param {Object} options
|
||||
* @param {string} options.inputPath - Path to the HTML input
|
||||
* @param {string} options.format - 'pdf' | 'docx' | 'html' | 'txt'
|
||||
* @param {string} [options.outputName] - Optional output filename (without extension)
|
||||
* @param {boolean} [options.showDialog] - Show save dialog (default: false in module mode, true in CLI mode)
|
||||
*/
|
||||
async convert({ inputPath, format = 'pdf', outputName, showDialog = false }) {
|
||||
if (!fs.existsSync(inputPath)) {
|
||||
throw new Error(`Input file not found: ${inputPath}`);
|
||||
}
|
||||
|
||||
const ext = path.extname(inputPath).toLowerCase();
|
||||
const baseName = outputName || path.basename(inputPath, ext);
|
||||
|
||||
let outputFile;
|
||||
|
||||
if (showDialog) {
|
||||
// Zeige nativen Dialog
|
||||
outputFile = await showSaveDialog(baseName, format);
|
||||
if (!outputFile) {
|
||||
console.log('Speichervorgang abgebrochen.');
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// Nutze Standard-Ausgabeverzeichnis
|
||||
outputFile = path.join(outputDir, `${baseName}.${format.toLowerCase()}`);
|
||||
}
|
||||
|
||||
let htmlContent = fs.readFileSync(inputPath, 'utf8');
|
||||
|
||||
// Remove <think> tags if present
|
||||
htmlContent = htmlContent.replace(/<think>[\s\S]*?<\/think>/gi, '');
|
||||
|
||||
switch (format.toLowerCase()) {
|
||||
case 'html':
|
||||
fs.writeFileSync(outputFile, htmlContent, 'utf8');
|
||||
break;
|
||||
case 'pdf':
|
||||
await this.htmlToPDF(htmlContent, outputFile);
|
||||
break;
|
||||
case 'docx':
|
||||
await this.htmlToDOCX(htmlContent, outputFile);
|
||||
break;
|
||||
case 'txt':
|
||||
fs.writeFileSync(outputFile, this.htmlToTXT(htmlContent), 'utf8');
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unsupported format: ${format}`);
|
||||
}
|
||||
|
||||
console.log(`Erfolgreich gespeichert: ${outputFile}`);
|
||||
return outputFile;
|
||||
},
|
||||
|
||||
// HTML → PDF
|
||||
async htmlToPDF(html, outputPath) {
|
||||
const browser = await puppeteer.launch({
|
||||
headless: true,
|
||||
args: ['--no-sandbox', '--disable-setuid-sandbox']
|
||||
});
|
||||
const page = await browser.newPage();
|
||||
await page.setContent(html, { waitUntil: 'networkidle0' });
|
||||
await page.pdf({
|
||||
path: outputPath,
|
||||
format: 'A4',
|
||||
printBackground: true,
|
||||
margin: { top: '20mm', right: '20mm', bottom: '20mm', left: '20mm' }
|
||||
});
|
||||
await browser.close();
|
||||
},
|
||||
|
||||
// HTML → DOCX
|
||||
async htmlToDOCX(html, outputPath) {
|
||||
const buffer = await htmlToDocx(html);
|
||||
fs.writeFileSync(outputPath, buffer);
|
||||
},
|
||||
|
||||
// HTML → TXT (rudimentär)
|
||||
htmlToTXT(html) {
|
||||
return html.replace(/<[^>]*>/g, '').replace(/\s+\n/g, '\n').trim();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = module_exports;
|
||||
|
||||
// CLI usage mit Dialog
|
||||
if (require.main === module) {
|
||||
(async () => {
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length < 1) {
|
||||
console.log('Usage: node htmlDocumentConverter.js <input.html> [format]');
|
||||
console.log('Formats: pdf (default), docx, html, txt');
|
||||
console.log('');
|
||||
console.log('Ein nativer "Speichern unter" Dialog wird automatisch geöffnet.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const inputPath = args[0];
|
||||
const format = args[1] || 'pdf';
|
||||
|
||||
try {
|
||||
await module_exports.convert({
|
||||
inputPath,
|
||||
format,
|
||||
showDialog: true
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Konvertierung fehlgeschlagen:', err.message);
|
||||
process.exit(1);
|
||||
}
|
||||
})();
|
||||
}
|
||||
@@ -1,8 +1,131 @@
|
||||
module.exports = {
|
||||
name:"chatgpt", // Unique name for our function that will later be used to get the function from the map via "mapFunctions.get("example").function()"
|
||||
type:"llm", // value used to differentiate each module to order them in the UI
|
||||
displayname:"ChatGPT", // The displayname used within the UI
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const outputDir = path.join(__dirname, "../../../storage/documents"); // path for output directory
|
||||
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir, { recursive: true }); // Create output directory if it doesn't exist
|
||||
}
|
||||
|
||||
// Ensure SAIA API key is set in environment variables: export SAIA_API_KEY="your_api_key_here"
|
||||
const SAIA_API_KEY = process.env.SAIA_API_KEY; // Ensure SAIA API key is set in environment variables
|
||||
|
||||
const SAIA_URL = "https://chat-ai.academiccloud.de/v1/chat/completions"; //URL for the REST call, used model and action
|
||||
|
||||
const module_exports = {
|
||||
name: "llm-saia_openai_gpt",
|
||||
type: "llm",
|
||||
displayname: "GPT 120B",
|
||||
description: "Generates documents using OpenAI GPT OSS 120B via SAIA platform",
|
||||
|
||||
async function(parameter) {
|
||||
// TODO add code to actually send the transcript to ChatGPT and get a response back
|
||||
try {
|
||||
console.log("SAIA OpenAI GPT module invoked with parameters:", parameter);
|
||||
|
||||
await this.createDocumentFromTranscript( //Call the function to create document with transcript, document type and language
|
||||
parameter.inputTranscriptPath, // Path to input transcript file
|
||||
parameter.documentTypePath, // Path to document type file which is chosen in the front end by the user
|
||||
parameter.language // Language for the document which is chosen in the front end by the user
|
||||
);
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error in SAIA OpenAI GPT module:", error);
|
||||
}
|
||||
},
|
||||
|
||||
createDocumentFromTranscript: async function(transcriptPath, documentTypePath, language = "en") { // default language is English
|
||||
try {
|
||||
const transcript = await fs.promises.readFile(transcriptPath, "utf-8"); //read transcript file from Path
|
||||
const documentType = await fs.promises.readFile(documentTypePath, "utf-8"); //read document type from Path
|
||||
const promptText = `${documentType}, in language ${language}, transcript:\n\n${transcript}`; //combine doc type, language and transcript - Change prompt here if needed
|
||||
|
||||
// --- REST CALL ---
|
||||
const response = await fetch(SAIA_URL, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Authorization": `Bearer ${SAIA_API_KEY}`,
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: "openai-gpt-oss-120b",
|
||||
messages: [
|
||||
{ role: "system", content: "You are a helpful assistant that generates HTML documents from transcripts. Output only valid HTML content without any preamble, explanations, or markdown formatting." },
|
||||
{ role: "user", content: promptText }
|
||||
],
|
||||
temperature: 0
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) { //ok is true when a response was successful
|
||||
const text = await response.text();
|
||||
throw new Error(`SAIA API error (${response.status}): ${text}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Get generated text from response or default to empty string (if null)
|
||||
// SAIA uses OpenAI-compatible structure: data.choices[x].message.content
|
||||
const output = data.choices?.[0]?.message?.content || "";
|
||||
|
||||
let inputTranscriptName = path.basename(transcriptPath, path.extname(transcriptPath)); // Name for the output file
|
||||
console.log(inputTranscriptName);
|
||||
|
||||
const outPath = path.join(outputDir, `${inputTranscriptName}.html`); // Output file path & name to make naming dynamic. Pulled from input transcript name
|
||||
fs.writeFileSync(outPath, output, "utf8"); // Write output to file
|
||||
|
||||
console.log("Generated document written to:", outPath);
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error generating SAIA content:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = module_exports;
|
||||
|
||||
// CLI Mode: Allow direct execution
|
||||
if (require.main === module) {
|
||||
(async () => {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length < 2) {
|
||||
console.error("Usage: node llm-openai-gpt.js <transcriptPath> <documentTypePath> [language]");
|
||||
console.error("Example: node llm-openai-gpt.js ./transcript.json ./docType.txt de");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const [transcriptPath, documentTypePath, language] = args;
|
||||
|
||||
// Check if API key is set
|
||||
if (!SAIA_API_KEY) {
|
||||
console.error("ERROR: SAIA_API_KEY environment variable is not set!");
|
||||
console.error("Please set it with: export SAIA_API_KEY='your_api_key_here'");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Check if files exist
|
||||
if (!fs.existsSync(transcriptPath)) {
|
||||
console.error(`ERROR: Transcript file not found: ${transcriptPath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(documentTypePath)) {
|
||||
console.error(`ERROR: Document type file not found: ${documentTypePath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log("Starting document generation...");
|
||||
console.log(`Transcript: ${transcriptPath}`);
|
||||
console.log(`Document Type: ${documentTypePath}`);
|
||||
console.log(`Language: ${language || 'en (default)'}`);
|
||||
|
||||
await module_exports.createDocumentFromTranscript(
|
||||
transcriptPath,
|
||||
documentTypePath,
|
||||
language || 'en'
|
||||
);
|
||||
|
||||
console.log("Done!");
|
||||
})();
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const outputDir = path.join(__dirname, "../../../storage/documents"); // path for output directory
|
||||
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
@@ -5,11 +8,10 @@ if (!fs.existsSync(outputDir)) {
|
||||
}
|
||||
|
||||
// Ensure Google API key is set in environment variables: export GOOGLE_API_KEY="your_api_key_here"
|
||||
|
||||
const GEMINI_API_KEY = process.env.GOOGLE_API_KEY; // Ensure Google API key is set in environment variables: export GOOGLE_API_KEY="your_api_key_here"
|
||||
const GEMINI_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent"; // URL for the REST call, used model and action
|
||||
|
||||
module.exports = {
|
||||
const module_exports = {
|
||||
name: "llm-gemini",
|
||||
type: "llm",
|
||||
displayname: "Gemini LLM",
|
||||
@@ -82,3 +84,51 @@ module.exports = {
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = module_exports;
|
||||
|
||||
// CLI Mode: Allow direct execution
|
||||
if (require.main === module) {
|
||||
(async () => {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length < 2) {
|
||||
console.error("Usage: node llm-gemini.js <transcriptPath> <documentTypePath> [language]");
|
||||
console.error("Example: node llm-gemini.js ./transcript.json ./docType.txt de");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const [transcriptPath, documentTypePath, language] = args;
|
||||
|
||||
// Check if API key is set
|
||||
if (!GEMINI_API_KEY) {
|
||||
console.error("ERROR: GOOGLE_API_KEY environment variable is not set!");
|
||||
console.error("Please set it with: export GOOGLE_API_KEY='your_api_key_here'");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Check if files exist
|
||||
if (!fs.existsSync(transcriptPath)) {
|
||||
console.error(`ERROR: Transcript file not found: ${transcriptPath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(documentTypePath)) {
|
||||
console.error(`ERROR: Document type file not found: ${documentTypePath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log("Starting document generation...");
|
||||
console.log(`Transcript: ${transcriptPath}`);
|
||||
console.log(`Document Type: ${documentTypePath}`);
|
||||
console.log(`Language: ${language || 'en (default)'}`);
|
||||
|
||||
await module_exports.createDocumentFromTranscript(
|
||||
transcriptPath,
|
||||
documentTypePath,
|
||||
language || 'en'
|
||||
);
|
||||
|
||||
console.log("Done!");
|
||||
})();
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const outputDir = path.join(__dirname, "../../../storage/documents"); // path for output directory
|
||||
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir, { recursive: true }); // Create output directory if it doesn't exist
|
||||
}
|
||||
|
||||
// Ensure SAIA API key is set in environment variables: export SAIA_API_KEY="your_api_key_here"
|
||||
const SAIA_API_KEY = process.env.SAIA_API_KEY;
|
||||
const SAIA_URL = "https://chat-ai.academiccloud.de/v1/chat/completions"; // URL for the REST call, used model and action
|
||||
|
||||
const module_exports = {
|
||||
name: "qwen3-235b-a22b",
|
||||
type: "llm",
|
||||
displayname: "QWEN 3 235B",
|
||||
description: "Generates documents using QWEN 3 235B via SAIA platform",
|
||||
|
||||
async function(parameter) {
|
||||
try {
|
||||
console.log("SAIA QWEN 3 235B module invoked with parameters:", parameter);
|
||||
|
||||
await this.createDocumentFromTranscript( // Call the function to create document with transcript, document type and language
|
||||
parameter.inputTranscriptPath, // Path to input transcript file
|
||||
parameter.documentTypePath, // Path to document type file which is chosen in the front end by the user
|
||||
parameter.language // Language for the document which is chosen in the front end by the user
|
||||
);
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error in SAIA QWEN 3 235B module:", error);
|
||||
}
|
||||
},
|
||||
|
||||
createDocumentFromTranscript: async function(transcriptPath, documentTypePath, language = "en") { // default language is English
|
||||
try {
|
||||
const transcript = await fs.promises.readFile(transcriptPath, "utf-8"); // read transcript file from Path
|
||||
const documentType = await fs.promises.readFile(documentTypePath, "utf-8"); // read document type from Path
|
||||
const promptText = `${documentType}, in language ${language}, transcript:\n\n${transcript}`; // combine doc type, language and transcript - Change prompt here if needed
|
||||
|
||||
// --- REST CALL ---
|
||||
const response = await fetch(SAIA_URL, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Authorization": `Bearer ${SAIA_API_KEY}`,
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: "qwen3-235b-a22b",
|
||||
messages: [
|
||||
{ role: "system", content: "You are a helpful assistant that generates HTML documents from transcripts. Output only valid HTML content without any preamble, explanations, or markdown formatting." },
|
||||
{ role: "user", content: promptText }
|
||||
],
|
||||
temperature: 0
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) { // ok is true when a response was successful
|
||||
const text = await response.text();
|
||||
throw new Error(`SAIA API error (${response.status}): ${text}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Get generated text from response or default to empty string (if null)
|
||||
// SAIA uses OpenAI-compatible structure: data.choices[x].message.content
|
||||
const output = data.choices?.[0]?.message?.content || "";
|
||||
|
||||
let inputTranscriptName = path.basename(transcriptPath, path.extname(transcriptPath)); // Name for the output file
|
||||
console.log(inputTranscriptName);
|
||||
|
||||
const outPath = path.join(outputDir, `${inputTranscriptName}.html`); // Output file path & name to make naming dynamic. Pulled from input transcript name
|
||||
fs.writeFileSync(outPath, output, "utf8"); // Write output to file
|
||||
|
||||
console.log("Generated document written to:", outPath);
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error generating SAIA content:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = module_exports;
|
||||
|
||||
// CLI Mode: Allow direct execution
|
||||
if (require.main === module) {
|
||||
(async () => {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length < 2) {
|
||||
console.error("Usage: node qwen3.js <transcriptPath> <documentTypePath> [language]");
|
||||
console.error("Example: node qwen3.js ./transcript.json ./docType.txt de");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const [transcriptPath, documentTypePath, language] = args;
|
||||
|
||||
// Check if API key is set
|
||||
if (!SAIA_API_KEY) {
|
||||
console.error("ERROR: SAIA_API_KEY environment variable is not set!");
|
||||
console.error("Please set it with: export SAIA_API_KEY='your_api_key_here'");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Check if files exist
|
||||
if (!fs.existsSync(transcriptPath)) {
|
||||
console.error(`ERROR: Transcript file not found: ${transcriptPath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(documentTypePath)) {
|
||||
console.error(`ERROR: Document type file not found: ${documentTypePath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log("Starting document generation...");
|
||||
console.log(`Transcript: ${transcriptPath}`);
|
||||
console.log(`Document Type: ${documentTypePath}`);
|
||||
console.log(`Language: ${language || 'en (default)'}`);
|
||||
|
||||
await module_exports.createDocumentFromTranscript(
|
||||
transcriptPath,
|
||||
documentTypePath,
|
||||
language || 'en'
|
||||
);
|
||||
|
||||
console.log("Done!");
|
||||
})();
|
||||
}
|
||||
+1
Submodule services/modules/transcription-local/whisper.cpp added at 999a7e0cbf
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"FORMAT": "markdown",
|
||||
"GOAL":"Generate a structured meeting report (Markdown). **Output ONLY:** final .md. No meta.",
|
||||
"STRUCTURE": {
|
||||
"titlepage": ["title","date","start","end","duration","location","host","participants"],
|
||||
"toc": "[section](#anchor) — HH:MM:SS",
|
||||
"section": {
|
||||
"h2": "<topic> — HH:MM:SS",
|
||||
"summary": "1 sentence",
|
||||
"key_points": "<=5 bullets, quotes optional",
|
||||
"decisions": "list: text | owner | due",
|
||||
"actions": "table: id | task | owner | due | status"
|
||||
},
|
||||
"exec_summary": "3 short sentences",
|
||||
"consolidated": ["decisions", "actions"],
|
||||
"appendix": "optional"
|
||||
},
|
||||
|
||||
"STYLE": {
|
||||
"tone": "neutral, concise",
|
||||
"ts_format": "HH:MM:SS",
|
||||
"no_meta": true
|
||||
},
|
||||
|
||||
"PROCESS": {
|
||||
"timestamps": "use if present; else estimate minimal",
|
||||
"speakers": "use labels; else Speaker X",
|
||||
"long_transcripts": "chunk → summarize → merge",
|
||||
"unclear": "UNKLAR:<reason>"
|
||||
},
|
||||
|
||||
"JSON_OUTPUT_OPTIONAL": true,
|
||||
|
||||
"PROMPT_SNIPPET": "Generate meeting report in markdown using STRUCTURE and STYLE. Output only the report."
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
Generate a structured meeting report in HTML using STRUCTURE and STYLE.
|
||||
Output ONLY the final .md document — no meta comments, no explanations.
|
||||
|
||||
Follow exactly the STRUCTURE defined below.
|
||||
Follow exactly the STYLE rules.
|
||||
Use timestamps in HH:MM:SS format.
|
||||
If information is missing, use: Unclear:<reason>.
|
||||
|
||||
==================== STRUCTURE & RULES ====================
|
||||
|
||||
{
|
||||
"FORMAT": "HTML",
|
||||
|
||||
"STRUCTURE": {
|
||||
"titlepage": [
|
||||
"title",
|
||||
"date",
|
||||
"start",
|
||||
"end",
|
||||
"duration",
|
||||
"location",
|
||||
"host",
|
||||
"participants"
|
||||
],
|
||||
|
||||
"toc": "[section](#anchor) — HH:MM:SS",
|
||||
|
||||
"section": {
|
||||
"h2": "<topic> — HH:MM:SS",
|
||||
"summary": "exactly 1 concise sentence",
|
||||
"key_points": "maximum 5 bullet points; quotes optional",
|
||||
"decisions": "list items formatted as: decision text | owner | due date",
|
||||
"actions": "HTML table: id | task | owner | due | status"
|
||||
},
|
||||
|
||||
"exec_summary": "exactly 3 short sentences",
|
||||
|
||||
"consolidated": [
|
||||
"decisions",
|
||||
"actions"
|
||||
],
|
||||
|
||||
"appendix": "optional"
|
||||
},
|
||||
|
||||
"STYLE": {
|
||||
"tone": "neutral, concise, professional",
|
||||
"ts_format": "HH:MM:SS",
|
||||
"no_meta": true
|
||||
},
|
||||
|
||||
"PROCESS": {
|
||||
"timestamps": "use transcript timestamps if present; otherwise estimate minimal",
|
||||
"speakers": "use names if available; else Speaker X",
|
||||
"long_transcripts": "split → summarize → merge",
|
||||
"unclear": "Unclear:<reason>"
|
||||
},
|
||||
|
||||
"PROMPT_SNIPPET": "Generate meeting report in HTML using STRUCTURE and STYLE. Output only the report."
|
||||
}
|
||||
|
||||
============================================================
|
||||
|
||||
Insert all generated content into the following HTML TEMPLATE:
|
||||
|
||||
# {{title}}
|
||||
|
||||
**Date:** {{date}}
|
||||
**Start:** {{start}}
|
||||
**End:** {{end}}
|
||||
**Duration:** {{duration}}
|
||||
**Location:** {{location}}
|
||||
**Host:** {{host}}
|
||||
**Participants:** {{participants}}
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
{{toc}}
|
||||
|
||||
---
|
||||
Generate a structured meeting report in HTML using STRUCTURE and STYLE.
|
||||
Output ONLY the final .md document — no meta comments, no explanations.
|
||||
|
||||
Follow exactly the STRUCTURE defined below.
|
||||
Follow exactly the STYLE rules.
|
||||
Use timestamps in HH:MM:SS format.
|
||||
If information is missing, use: UNKLAR:<reason>.
|
||||
|
||||
==================== STRUCTURE & RULES ====================
|
||||
|
||||
{
|
||||
"FORMAT": "HTML",
|
||||
|
||||
"STRUCTURE": {
|
||||
"titlepage": [
|
||||
"title",
|
||||
"date",
|
||||
"start",
|
||||
"end",
|
||||
"duration",
|
||||
"location",
|
||||
"host",
|
||||
"participants"
|
||||
],
|
||||
|
||||
"toc": "[section](#anchor) — HH:MM:SS",
|
||||
|
||||
"section": {
|
||||
"h2": "<topic> — HH:MM:SS",
|
||||
"summary": "exactly 1 concise sentence",
|
||||
"key_points": "maximum 5 bullet points; quotes optional",
|
||||
"decisions": "list items formatted as: decision text | owner | due date",
|
||||
"actions": "HTML table: id | task | owner | due | status"
|
||||
},
|
||||
|
||||
"exec_summary": "exactly 3 short sentences",
|
||||
|
||||
"consolidated": [
|
||||
"decisions",
|
||||
"actions"
|
||||
],
|
||||
|
||||
"appendix": "optional"
|
||||
},
|
||||
|
||||
"STYLE": {
|
||||
"tone": "neutral, concise, professional",
|
||||
"ts_format": "HH:MM:SS",
|
||||
"no_meta": true
|
||||
},
|
||||
|
||||
"PROCESS": {
|
||||
"timestamps": "use transcript timestamps if present; otherwise estimate minimal",
|
||||
"speakers": "use names if available; else Speaker X",
|
||||
"long_transcripts": "split → summarize → merge",
|
||||
"unclear": "UNKLAR:<reason>"
|
||||
},
|
||||
|
||||
"PROMPT_SNIPPET": "Generate meeting report in HTML using STRUCTURE and STYLE. Output only the report."
|
||||
}
|
||||
|
||||
============================================================
|
||||
|
||||
Insert all generated content into the following HTML TEMPLATE:
|
||||
|
||||
# {{title}}
|
||||
|
||||
**Date:** {{date}}
|
||||
**Start:** {{start}}
|
||||
**End:** {{end}}
|
||||
**Duration:** {{duration}}
|
||||
**Location:** {{location}}
|
||||
**Host:** {{host}}
|
||||
**Participants:** {{participants}}
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
{{toc}}
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
{{exec_summary}}
|
||||
|
||||
---
|
||||
|
||||
## Sections
|
||||
{{sections}}
|
||||
|
||||
---
|
||||
|
||||
## Consolidated Decisions
|
||||
{{consolidated_decisions}}
|
||||
|
||||
---
|
||||
|
||||
## Consolidated Actions
|
||||
{{consolidated_actions}}
|
||||
|
||||
---
|
||||
|
||||
## Appendix
|
||||
{{appendix}}
|
||||
|
||||
============================================================
|
||||
|
||||
Final Requirement:
|
||||
Output ONLY the completed HTML meeting report.
|
||||
## Executive Summary
|
||||
{{exec_summary}}
|
||||
|
||||
---
|
||||
|
||||
## Sections
|
||||
{{sections}}
|
||||
|
||||
---
|
||||
|
||||
## Consolidated Decisions
|
||||
{{consolidated_decisions}}
|
||||
|
||||
---
|
||||
|
||||
## Consolidated Actions
|
||||
{{consolidated_actions}}
|
||||
|
||||
---
|
||||
|
||||
## Appendix
|
||||
{{appendix}}
|
||||
|
||||
============================================================
|
||||
|
||||
Final Requirement:
|
||||
Output ONLY the completed HTML meeting report.
|
||||
Reference in New Issue
Block a user