From 425e24853e87af36edd4a5e4971f156278dda808 Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Thu, 4 Dec 2025 10:58:50 +0100 Subject: [PATCH 1/5] Similar to the Gemini LLM we now have a ChatGPT REST call. The API Key is still missing --- services/modules/llm-chat_gpt/chatgpt.js | 82 ++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 6 deletions(-) diff --git a/services/modules/llm-chat_gpt/chatgpt.js b/services/modules/llm-chat_gpt/chatgpt.js index 8446af5..b163b27 100644 --- a/services/modules/llm-chat_gpt/chatgpt.js +++ b/services/modules/llm-chat_gpt/chatgpt.js @@ -1,8 +1,78 @@ +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 Chat GPT API key is set in environment variables: export CHAT_GPT_API_KEY="your_api_key_here" +// NOTE: Using standard OPENAI_API_KEY instead because the API expects this env variable +const OPENAI_API_KEY = process.env.OPENAI_API_KEY; // Ensure Chat GPT API key is set in environment variables: export OPENAI_API_KEY="your_api_key_here" + +const CHAT_GPT_URL = "https://api.openai.com/v1/chat/completions"; //URL for the REST call, used model and action + 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 - async function(parameter){ - // TODO add code to actually send the transcript to ChatGPT and get a response back + name: "llm-chat_gpt", + type: "llm", + displayname: "Chat GPT", + description: "Generates documents using Chat GPT", + + async function(parameter) { + try { + console.log("Chat 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 Chat 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(CHAT_GPT_URL, { + method: "POST", + headers: { + "Authorization": `Bearer ${OPENAI_API_KEY}`, + "Content-Type": "application/json" + }, + body: JSON.stringify({ + model: "gpt-4.1", + messages: [ + { role: "user", content: promptText } + ] + }) + }); + + if (!response.ok) { //ok is true when a responce was successfull + const text = await response.text(); + throw new Error(`Chat GPT API error (${response.status}): ${text}`); + } + + const data = await response.json(); + + // Get generated text from response or default to empty string (if null) + // FIX: Chat Completions API uses 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}.md`); // 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 Chat GPT content:", error); + } } -} \ No newline at end of file +}; From 3af038d1956d2a9ff42b3d6b8b07ad565b58253a Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Thu, 11 Dec 2025 12:41:11 +0100 Subject: [PATCH 2/5] Multiple AI models implemented - chatgpt, llama --- scripts/show-models.js | 47 +++++++ services/modules/llm-chat_gpt/chatgpt.js | 89 ++++++++++--- services/modules/llm-llama/llm-llama.js | 123 ++++++++++++++++++ .../modules/transcription-local/whisper.cpp | 1 + 4 files changed, 242 insertions(+), 18 deletions(-) create mode 100644 scripts/show-models.js create mode 100644 services/modules/llm-llama/llm-llama.js create mode 160000 services/modules/transcription-local/whisper.cpp diff --git a/scripts/show-models.js b/scripts/show-models.js new file mode 100644 index 0000000..9aae541 --- /dev/null +++ b/scripts/show-models.js @@ -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); + } +})(); \ No newline at end of file diff --git a/services/modules/llm-chat_gpt/chatgpt.js b/services/modules/llm-chat_gpt/chatgpt.js index b163b27..e3e8cc8 100644 --- a/services/modules/llm-chat_gpt/chatgpt.js +++ b/services/modules/llm-chat_gpt/chatgpt.js @@ -1,24 +1,26 @@ +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 Chat GPT API key is set in environment variables: export CHAT_GPT_API_KEY="your_api_key_here" -// NOTE: Using standard OPENAI_API_KEY instead because the API expects this env variable -const OPENAI_API_KEY = process.env.OPENAI_API_KEY; // Ensure Chat GPT API key is set in environment variables: export OPENAI_API_KEY="your_api_key_here" +// 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 CHAT_GPT_URL = "https://api.openai.com/v1/chat/completions"; //URL for the REST call, used model and action +const SAIA_URL = "https://chat-ai.academiccloud.de/v1/chat/completions"; //URL for the REST call, used model and action -module.exports = { - name: "llm-chat_gpt", +const module_exports = { + name: "llm-saia_openai_gpt", type: "llm", - displayname: "Chat GPT", - description: "Generates documents using Chat GPT", + displayname: "SAIA OpenAI GPT OSS 120B", + description: "Generates documents using OpenAI GPT OSS 120B via SAIA platform", async function(parameter) { try { - console.log("Chat GPT module invoked with parameters:", parameter); + 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 @@ -27,7 +29,7 @@ module.exports = { ); } catch (error) { - console.error("Error in Chat GPT module:", error); + console.error("Error in SAIA OpenAI GPT module:", error); } }, @@ -38,29 +40,32 @@ module.exports = { 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(CHAT_GPT_URL, { + const response = await fetch(SAIA_URL, { method: "POST", headers: { - "Authorization": `Bearer ${OPENAI_API_KEY}`, + "Authorization": `Bearer ${SAIA_API_KEY}`, + "Accept": "application/json", "Content-Type": "application/json" }, body: JSON.stringify({ - model: "gpt-4.1", + model: "openai-gpt-oss-120b", messages: [ + { role: "system", content: "You are a helpful assistant that generates documents from transcripts." }, { role: "user", content: promptText } - ] + ], + temperature: 0 }) }); - if (!response.ok) { //ok is true when a responce was successfull + if (!response.ok) { //ok is true when a response was successful const text = await response.text(); - throw new Error(`Chat GPT API error (${response.status}): ${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) - // FIX: Chat Completions API uses data.choices[x].message.content + // 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 @@ -72,7 +77,55 @@ module.exports = { console.log("Generated document written to:", outPath); } catch (error) { - console.error("Error generating Chat GPT content:", 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 [language]"); + console.error("Example: node llm-openai-gpt.js ./transcript.json ./docType.json 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!"); + })(); +} \ No newline at end of file diff --git a/services/modules/llm-llama/llm-llama.js b/services/modules/llm-llama/llm-llama.js new file mode 100644 index 0000000..0cf4fb8 --- /dev/null +++ b/services/modules/llm-llama/llm-llama.js @@ -0,0 +1,123 @@ +const fs = require('fs'); +const path = require('path'); + +const outputDir = path.join(__dirname, "../../../storage/documents"); + +if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { recursive: true }); +} + +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", + type: "llm", + displayname: "SAIA Llama 3.3 70B", + description: "Generates documents using Llama 3.3 70B Instruct via SAIA platform", + + async function(parameter) { + try { + console.log("SAIA Llama 3.3 70B module invoked with parameters:", parameter); + + await this.createDocumentFromTranscript( + parameter.inputTranscriptPath, + parameter.documentTypePath, + parameter.language + ); + + } catch (error) { + console.error("Error in SAIA Llama 3.3 70B module:", error); + } + }, + + createDocumentFromTranscript: async function(transcriptPath, documentTypePath, language = "en") { + 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}`; + + // --- 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: "llama-3.3-70b-instruct", // Korrekter Modellname! + messages: [ + { role: "system", content: "You are a helpful assistant that generates documents from transcripts." }, + { role: "user", content: promptText } + ], + temperature: 0 + }) + }); + + if (!response.ok) { + const text = await response.text(); + throw new Error(`SAIA API error (${response.status}): ${text}`); + } + + const data = await response.json(); + const output = data.choices?.[0]?.message?.content || ""; + + let inputTranscriptName = path.basename(transcriptPath, path.extname(transcriptPath)); + console.log(inputTranscriptName); + + const outPath = path.join(outputDir, `${inputTranscriptName}.md`); + fs.writeFileSync(outPath, output, "utf8"); + + console.log("Generated document written to:", outPath); + + } catch (error) { + console.error("Error generating SAIA content:", error); + } + } +}; + +module.exports = module_exports; + +if (require.main === module) { + (async () => { + 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"); + process.exit(1); + } + + const [transcriptPath, documentTypePath, language] = args; + + 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); + } + + 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/transcription-local/whisper.cpp b/services/modules/transcription-local/whisper.cpp new file mode 160000 index 0000000..999a7e0 --- /dev/null +++ b/services/modules/transcription-local/whisper.cpp @@ -0,0 +1 @@ +Subproject commit 999a7e0cbf8484dc2cea1e9f855d6b39f34f7ae9 From 68c1f0ed9fc4dd42e0b36ff7ea5e08b493e39df3 Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Thu, 11 Dec 2025 13:20:54 +0100 Subject: [PATCH 3/5] removed google from requires --- requires.js | 1 - 1 file changed, 1 deletion(-) diff --git a/requires.js b/requires.js index 6036867..fa410f3 100644 --- a/requires.js +++ b/requires.js @@ -16,7 +16,6 @@ cliProgress = require('cli-progress'); // { app, BrowserWindow, ipcMain, dialog } = require('electron'); electron = require('electron'); -genai = require("@google/genai"); axios = require("axios") From 3dd8485140c90c67c6fc47147078b9d0ba86addf Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Thu, 11 Dec 2025 13:23:22 +0100 Subject: [PATCH 4/5] audit --- package-lock.json | 54 ++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4de511e..bf45f52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -451,22 +451,27 @@ } }, "node_modules/body-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", - "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", + "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", + "license": "MIT", "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", - "debug": "^4.4.0", + "debug": "^4.4.3", "http-errors": "^2.0.0", - "iconv-lite": "^0.6.3", + "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.0", - "raw-body": "^3.0.0", - "type-is": "^2.0.0" + "raw-body": "^3.0.1", + "type-is": "^2.0.1" }, "engines": { "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/boolean": { @@ -1612,14 +1617,19 @@ } }, "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/inherits": { @@ -1708,11 +1718,12 @@ } }, "node_modules/jws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", - "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", "dependencies": { - "jwa": "^2.0.0", + "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, @@ -2077,21 +2088,6 @@ "node": ">= 0.10" } }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", - "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", From 53508b175a2bc102f76e7f267951f64ef165e328 Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Thu, 11 Dec 2025 13:27:00 +0100 Subject: [PATCH 5/5] changed Display name --- services/modules/llm-chat_gpt/chatgpt.js | 2 +- services/modules/llm-llama/llm-llama.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/services/modules/llm-chat_gpt/chatgpt.js b/services/modules/llm-chat_gpt/chatgpt.js index e3e8cc8..76fa3a3 100644 --- a/services/modules/llm-chat_gpt/chatgpt.js +++ b/services/modules/llm-chat_gpt/chatgpt.js @@ -15,7 +15,7 @@ const SAIA_URL = "https://chat-ai.academiccloud.de/v1/chat/completions"; //URL f const module_exports = { name: "llm-saia_openai_gpt", type: "llm", - displayname: "SAIA OpenAI GPT OSS 120B", + displayname: "GPT 120B", description: "Generates documents using OpenAI GPT OSS 120B via SAIA platform", async function(parameter) { diff --git a/services/modules/llm-llama/llm-llama.js b/services/modules/llm-llama/llm-llama.js index 0cf4fb8..4e6d920 100644 --- a/services/modules/llm-llama/llm-llama.js +++ b/services/modules/llm-llama/llm-llama.js @@ -13,7 +13,7 @@ const SAIA_URL = "https://chat-ai.academiccloud.de/v1/chat/completions"; const module_exports = { name: "llm-saia_llama_3.3", type: "llm", - displayname: "SAIA Llama 3.3 70B", + displayname: "LLAMA", description: "Generates documents using Llama 3.3 70B Instruct via SAIA platform", async function(parameter) {