From 746fec05d41378c51833e52ab84337f5dad387bf Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Sun, 14 Dec 2025 16:28:48 +0100 Subject: [PATCH 1/5] Changed model from llama to quen3 because llama was timing out due to token limit --- .../{llm-llama/llm-llama.js => quen3/quen3.js} | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) rename services/modules/{llm-llama/llm-llama.js => quen3/quen3.js} (86%) diff --git a/services/modules/llm-llama/llm-llama.js b/services/modules/quen3/quen3.js similarity index 86% rename from services/modules/llm-llama/llm-llama.js rename to services/modules/quen3/quen3.js index 4e6d920..ff942de 100644 --- a/services/modules/llm-llama/llm-llama.js +++ b/services/modules/quen3/quen3.js @@ -11,14 +11,14 @@ const SAIA_API_KEY = process.env.SAIA_API_KEY; const SAIA_URL = "https://chat-ai.academiccloud.de/v1/chat/completions"; const module_exports = { - name: "llm-saia_llama_3.3", + name: "qwen3-235b-a22b", type: "llm", - displayname: "LLAMA", - description: "Generates documents using Llama 3.3 70B Instruct via SAIA platform", + displayname: "QWEN 3 235B", + description: "Generates documents using QWEN 3 235B via SAIA platform", async function(parameter) { try { - console.log("SAIA Llama 3.3 70B module invoked with parameters:", parameter); + console.log("SAIA QWEN 3 235B module invoked with parameters:", parameter); await this.createDocumentFromTranscript( parameter.inputTranscriptPath, @@ -27,7 +27,7 @@ const module_exports = { ); } catch (error) { - console.error("Error in SAIA Llama 3.3 70B module:", error); + console.error("Error in SAIA QWEN 3 235B module:", error); } }, @@ -46,7 +46,7 @@ const module_exports = { "Content-Type": "application/json" }, body: JSON.stringify({ - model: "llama-3.3-70b-instruct", // Korrekter Modellname! + model: "qwen3-235b-a22b", // Korrekter Modellname! messages: [ { role: "system", content: "You are a helpful assistant that generates documents from transcripts." }, { role: "user", content: promptText } @@ -84,8 +84,8 @@ if (require.main === module) { const args = process.argv.slice(2); if (args.length < 2) { - console.error("Usage: node llm-llama-3.3.js [language]"); - console.error("Example: node llm-llama-3.3.js ./transcript.json ./docType.json de"); + console.error("Usage: node quen3.js [language]"); + console.error("Example: node quen3.js ./transcript.json ./docType.json de"); process.exit(1); } From 1e38cc79f44a099c6aad2c82a5698d0de7c49106 Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Sun, 14 Dec 2025 16:53:35 +0100 Subject: [PATCH 2/5] Extended the Role description --- services/modules/quen3/quen3.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/modules/quen3/quen3.js b/services/modules/quen3/quen3.js index ff942de..8272c52 100644 --- a/services/modules/quen3/quen3.js +++ b/services/modules/quen3/quen3.js @@ -46,9 +46,9 @@ const module_exports = { "Content-Type": "application/json" }, body: JSON.stringify({ - model: "qwen3-235b-a22b", // Korrekter Modellname! + model: "qwen3-235b-a22b", messages: [ - { role: "system", content: "You are a helpful assistant that generates documents from transcripts." }, + { role: "system", content: "You are a helpful assistant that generates documents from transcripts. Do NOT include any , reasoning, chain-of-thought, or internal analysis. Only output the final document content." }, { role: "user", content: promptText } ], temperature: 0 From ec574119922a34b908251362a9195a0161f90bc8 Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Mon, 15 Dec 2025 13:53:14 +0100 Subject: [PATCH 3/5] Refactor document generation to output HTML format and update system instructions for clarity --- main.js | 2 +- services/modules/llm-chat_gpt/chatgpt.js | 6 +- services/modules/llm-gemini/gemini.js | 75 ++++++++++++++++++++---- services/modules/quen3/quen3.js | 43 ++++++++------ 4 files changed, 93 insertions(+), 33 deletions(-) diff --git a/main.js b/main.js index 39c408d..4f71d81 100644 --- a/main.js +++ b/main.js @@ -183,7 +183,7 @@ electron.ipcMain.on("file_submit", async (event, args) => { // TODO implement documentation module // This code handles the Text to Document processing module call for (let i = 0; i < args.document.styles.length; i++) { - await mapFunctions.get("module-handler").function(args.document.module, {prompt: args.document.styles[i].prompt, transcript: transcriptpath}).then(resp => { + await mapFunctions.get("module-handler").function(args.document.module, {inputTranscriptPath: transcriptpath, documentTypePath: "./storage/documentType/standard_meeting_report.txt", language: "en"}).then(resp => { // TODO add language handling from frontend console.log(resp); transcriptpath = resp curstep++ diff --git a/services/modules/llm-chat_gpt/chatgpt.js b/services/modules/llm-chat_gpt/chatgpt.js index 76fa3a3..d07a422 100644 --- a/services/modules/llm-chat_gpt/chatgpt.js +++ b/services/modules/llm-chat_gpt/chatgpt.js @@ -50,7 +50,7 @@ const module_exports = { body: JSON.stringify({ model: "openai-gpt-oss-120b", messages: [ - { role: "system", content: "You are a helpful assistant that generates documents from transcripts." }, + { 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 @@ -71,7 +71,7 @@ const module_exports = { let inputTranscriptName = path.basename(transcriptPath, path.extname(transcriptPath)); // Name for the output file console.log(inputTranscriptName); - const outPath = path.join(outputDir, `${inputTranscriptName}.md`); // Output file path & name to make naming dynamic. Pulled from input transcript name + 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); @@ -91,7 +91,7 @@ if (require.main === module) { if (args.length < 2) { console.error("Usage: node llm-openai-gpt.js [language]"); - console.error("Example: node llm-openai-gpt.js ./transcript.json ./docType.json de"); + console.error("Example: node llm-openai-gpt.js ./transcript.json ./docType.txt de"); process.exit(1); } diff --git a/services/modules/llm-gemini/gemini.js b/services/modules/llm-gemini/gemini.js index 6bd88a6..892560e 100644 --- a/services/modules/llm-gemini/gemini.js +++ b/services/modules/llm-gemini/gemini.js @@ -1,15 +1,17 @@ -const outputDir = path.join(__dirname, "../../../storage/documents"); // path for output directory +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 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 +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", @@ -19,7 +21,7 @@ module.exports = { try { console.log("Gemini LLM module invoked with parameters:", parameter); - await this.createDocumentFromTranscript( //Call the function to create document with transcript, document type and language + 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 @@ -32,12 +34,15 @@ module.exports = { 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 + 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 + + // Add system instruction for HTML output + const systemInstruction = "You are a helpful assistant that generates HTML documents from transcripts. Output only valid HTML content without any preamble, explanations, or markdown formatting."; + const promptText = `${systemInstruction}\n\n${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(`${GEMINI_URL}?key=${GEMINI_API_KEY}`, { //safe model response in variable + const response = await fetch(`${GEMINI_URL}?key=${GEMINI_API_KEY}`, { // safe model response in variable method: "POST", headers: { "Content-Type": "application/json" @@ -64,7 +69,7 @@ module.exports = { const output = data?.candidates?.[0]?.content?.parts?.[0]?.text || ""; let inputTranscriptName = path.basename(transcriptPath, path.extname(transcriptPath)); // Name for the output file console.log(inputTranscriptName); - const outPath = path.join(outputDir, `${inputTranscriptName}.md`); // Output file path & name to make naming dynamic. Pulled from input transcript name + 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); @@ -73,4 +78,52 @@ module.exports = { console.error("Error generating Gemini content:", error); } } -}; \ No newline at end of file +}; + +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 [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!"); + })(); +} \ No newline at end of file diff --git a/services/modules/quen3/quen3.js b/services/modules/quen3/quen3.js index 8272c52..fd93737 100644 --- a/services/modules/quen3/quen3.js +++ b/services/modules/quen3/quen3.js @@ -1,14 +1,15 @@ const fs = require('fs'); const path = require('path'); -const outputDir = path.join(__dirname, "../../../storage/documents"); +const outputDir = path.join(__dirname, "../../../storage/documents"); // path for output directory if (!fs.existsSync(outputDir)) { - fs.mkdirSync(outputDir, { recursive: true }); + 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"; +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", @@ -20,10 +21,10 @@ const module_exports = { try { console.log("SAIA QWEN 3 235B module invoked with parameters:", parameter); - await this.createDocumentFromTranscript( - parameter.inputTranscriptPath, - parameter.documentTypePath, - parameter.language + 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) { @@ -31,11 +32,11 @@ const module_exports = { } }, - createDocumentFromTranscript: async function(transcriptPath, documentTypePath, language = "en") { + createDocumentFromTranscript: async function(transcriptPath, documentTypePath, language = "en") { // default language is English try { - const transcript = await fs.promises.readFile(transcriptPath, "utf-8"); - const documentType = await fs.promises.readFile(documentTypePath, "utf-8"); - const promptText = `${documentType}, in language ${language}, transcript:\n\n${transcript}`; + 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, { @@ -48,26 +49,29 @@ const module_exports = { body: JSON.stringify({ model: "qwen3-235b-a22b", messages: [ - { role: "system", content: "You are a helpful assistant that generates documents from transcripts. Do NOT include any , reasoning, chain-of-thought, or internal analysis. Only output the final document content." }, + { 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) { + 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)); + let inputTranscriptName = path.basename(transcriptPath, path.extname(transcriptPath)); // Name for the output file console.log(inputTranscriptName); - const outPath = path.join(outputDir, `${inputTranscriptName}.md`); - fs.writeFileSync(outPath, output, "utf8"); + 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); @@ -79,24 +83,27 @@ const 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 quen3.js [language]"); - console.error("Example: node quen3.js ./transcript.json ./docType.json de"); + console.error("Usage: node qwen3.js [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); From e72d03efbeb7ae66653497b79b2f8e7cc944c1fa Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Mon, 15 Dec 2025 13:55:40 +0100 Subject: [PATCH 4/5] Added Pre's Document in the correct Folder --- .../documentType/standard_meeting_report.txt | 217 ++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 storage/documentType/standard_meeting_report.txt diff --git a/storage/documentType/standard_meeting_report.txt b/storage/documentType/standard_meeting_report.txt new file mode 100644 index 0000000..7a202f1 --- /dev/null +++ b/storage/documentType/standard_meeting_report.txt @@ -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:. + +==================== STRUCTURE & RULES ==================== + +{ + "FORMAT": "HTML", + + "STRUCTURE": { + "titlepage": [ + "title", + "date", + "start", + "end", + "duration", + "location", + "host", + "participants" + ], + + "toc": "[section](#anchor) — HH:MM:SS", + + "section": { + "h2": " — 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:" + }, + + "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:. + +==================== STRUCTURE & RULES ==================== + +{ + "FORMAT": "HTML", + + "STRUCTURE": { + "titlepage": [ + "title", + "date", + "start", + "end", + "duration", + "location", + "host", + "participants" + ], + + "toc": "[section](#anchor) — HH:MM:SS", + + "section": { + "h2": " — 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:" + }, + + "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. \ No newline at end of file From 789ecd3a3178221b6cca9540decd13f543200795 Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Mon, 15 Dec 2025 14:00:26 +0100 Subject: [PATCH 5/5] Update document processing to use prompts from styles and add structured meeting report template --- main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.js b/main.js index 4f71d81..39c408d 100644 --- a/main.js +++ b/main.js @@ -183,7 +183,7 @@ electron.ipcMain.on("file_submit", async (event, args) => { // TODO implement documentation module // This code handles the Text to Document processing module call for (let i = 0; i < args.document.styles.length; i++) { - await mapFunctions.get("module-handler").function(args.document.module, {inputTranscriptPath: transcriptpath, documentTypePath: "./storage/documentType/standard_meeting_report.txt", language: "en"}).then(resp => { // TODO add language handling from frontend + await mapFunctions.get("module-handler").function(args.document.module, {prompt: args.document.styles[i].prompt, transcript: transcriptpath}).then(resp => { console.log(resp); transcriptpath = resp curstep++