Merge branch 'develop' into 'feature/35-backend-llm-chat-gpt-integration-s4-10'

# Conflicts:
#   services/modules/llm-gemini/gemini.js
This commit is contained in:
Hughes, Mike
2025-12-15 14:07:32 +01:00
20 changed files with 829 additions and 160 deletions
+5 -22
View File
@@ -24,17 +24,11 @@ module.exports = {
outputType: String // Audio file output format
}
*/
let progressBar = new cliProgress.SingleBar({
format: 'Processing |{bar}| {percentage}% | {timemark}',
barCompleteChar: '\u2588',
barIncompleteChar: '\u2591',
hideCursor: true
});
try {
return new Promise((resolve, reject) => {
this.extractAudioFromVideo(parameter.inputVideoPath, progressBar, parameter.outputType)
this.extractAudioFromVideo(parameter.inputVideoPath, parameter.outputType)
.then((resp) => resolve(resp))
.catch((err) => console.error(err));
.catch((err) => {reject(err)});
})
} catch (error) {
console.log(parameter.outputType);
@@ -52,7 +46,7 @@ module.exports = {
* - Shows CLI progress bar
* - Handles errors gracefully (without errors)
*/
extractAudioFromVideo: async function (videoFilePath, progressBar, outputType){
extractAudioFromVideo: async function (videoFilePath, outputType){
let inputVideoName = path.basename(videoFilePath, path.extname(videoFilePath));
let outputAudioPath = path.join(outputDir, `${inputVideoName}.${outputType}`);
@@ -63,28 +57,17 @@ module.exports = {
// .audioCodec('pcm_s16le')
.audioChannels(1)
.audioFrequency(16000)
// .setFfmpegPath("./ffmpeg.exe")
.on('progress', (progress) => {
if (!progressBar.isActive) progressBar.start(100, 0, { timemark: '00:00:00' });
if (progress.percent) {
progressBar.update(progress.percent, { timemark: progress.timemark });
}
})
.on('end', () => {
progressBar.update(100, { timemark: 'done' });
progressBar.stop();
console.log(`Extraction completed: ${outputAudioPath}`);
resolve(outputAudioPath);
})
.on('error', (err) => {
progressBar.stop();
console.error(`failed_audio_extraction on type ${outputType}: ${err.message}`);
// console.error(`failed_audio_extraction on type ${outputType}: ${err.message}`);
reject(err);
})
.save(outputAudioPath);
} catch (error) {
console.log();
// console.log(error);
}
});
}
@@ -21,30 +21,36 @@ module.exports = {
const raw = fs.readFileSync(args.jsonPath, "utf-8");
inputJson = JSON.parse(raw);
} catch (e) {
console.error("Failed to load JSON from file:", e);
return { error: "Could not read JSON from file path." };
// console.error("Failed to load JSON from file:", e);
reject(e)
return
}
}
// JSON parsen
if (typeof args === "string") {
try {
await new Promise((res) => {
await new Promise((res, rej) => {
fs.readFile(args, 'utf8', function (err, data) {
if (err) throw err;
if (err){
rej(err)
return
}
inputJson = JSON.parse(data);
res()
});
})
} catch (e) {
console.log("Invalid JSON in summarize-transcription");
console.log(e)
return { error: "Invalid JSON" };
// console.log("Invalid JSON in summarize-transcription");
// console.log(e)
reject(e)
return
}
}
const words = inputJson.words;
if (!Array.isArray(words)) {
return { error: "No words Array found" };
reject("No words Array found")
return
}
const ENDINGS = [".", "!", "?"]; // '...' auch als Satzende ?
@@ -136,11 +142,11 @@ module.exports = {
const txtPath = path.join(outputDir, "transcription_result.txt");
fs.writeFileSync(txtPath, output.join("\n"), "utf-8");
console.log(`Summary successfully saved:\n- ${jsonPath}\n- ${txtPath}`);
// console.log(`Summary successfully saved:\n- ${jsonPath}\n- ${txtPath}`);
resolve(jsonPath);
} catch (err) {
console.error("Error saving Summary:", err);
// console.error("Error saving Summary:", err);
reject(err);
}
})
@@ -32,29 +32,35 @@ module.exports = {
inputJson = JSON.parse(raw);
} catch (e) {
console.error("Failed to load JSON from file:", e);
return { error: "Could not read JSON from file path." };
reject("Could not read JSON from file path.")
return
}
}
// JSON parsen
if (typeof args === "string") {
try {
await new Promise((res) => {
fs.readFile(args, 'utf8', function (err, data) {
if (err) throw err;
inputJson = JSON.parse(data);
res()
});
})
} catch (e) {
console.log("Invalid JSON in summarize-transcription");
console.log(e)
return { error: "Invalid JSON" };
}
if (typeof args === "string") {
try {
await new Promise((res, rej) => {
fs.readFile(args, 'utf8', function (err, data) {
if (err){
rej(err)
return
}
inputJson = JSON.parse(data);
res()
});
})
} catch (e) {
// console.log("Invalid JSON in summarize-transcription");
// console.log(e)
reject(e)
return
}
}
const words = inputJson.words;
if (!Array.isArray(words)) {
return { error: "No words Array found" };
reject("No words Array found")
return;
}
const ENDINGS = [".", "!", "?"]; // '...' auch als Satzende ?
@@ -70,7 +76,7 @@ module.exports = {
if (!currentSpeaker) currentSpeaker = w.speaker;
if (startTime === null) startTime = w.start;
endTime = w.end;
//speaker changing
if (currentSpeaker !== w.speaker && currentSentence) {
result.push({
@@ -132,10 +138,10 @@ module.exports = {
const txtPath = path.join(outputDir, `${filename}-${new Date().getTime()}.txt`);
fs.writeFileSync(txtPath, output.join("\n"), "utf-8");
console.log(`Summary successfully saved:\n- ${jsonPath}\n- ${txtPath}`);
// console.log(`Summary successfully saved:\n- ${jsonPath}\n- ${txtPath}`);
resolve(jsonPath);
} catch (err) {
console.error("Error saving Summary:", err);
// console.error("Error saving Summary:", err);
reject(err);
}
})
+56 -51
View File
@@ -18,65 +18,70 @@ const module_exports = {
description: "Generates documents using Google Gemini LLM",
async function(parameter) {
try {
console.log("Gemini LLM module invoked with parameters:", parameter);
return new Promise(async (resolve, reject) => {
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
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
);
resolve(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 Gemini LLM module:", error);
reject(error)
}
})
} catch (error) {
console.error("Error in Gemini LLM 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
// 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
return new Promise(async(resolve, reject) => {
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(`${GEMINI_URL}?key=${GEMINI_API_KEY}`, { // safe model response in variable
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
contents: [
{
parts: [
{ text: promptText } // Input prompt for content generation
]
}
]
})
});
// --- REST CALL ---
const response = await fetch(`${GEMINI_URL}?key=${GEMINI_API_KEY}`, { //safe model response in variable
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
contents: [
{
parts: [
{ text: promptText } // Input prompt for content generation
]
}
]
})
});
if (!response.ok) { //ok is true when a responce was successfull
const text = await response.text();
throw new Error(`Gemini API error (${response.status}): ${text}`);
if (!response.ok) { //ok is true when a responce was successfull
const text = await response.text();
throw new Error(`Gemini API error (${response.status}): ${text}`);
}
const data = await response.json();
// Get generated text from response or default to empty string (if null)
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
fs.writeFileSync(outPath, output, "utf8"); // Write output to file
// console.log("Generated document written to:", outPath);
resolve(outPath)
} catch (error) {
// console.error("Error generating Gemini content:", error);
reject(error)
}
const data = await response.json();
// Get generated text from response or default to empty string (if null)
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}.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 Gemini content:", error);
}
})
}
};
@@ -78,7 +78,7 @@ function saveTranscript(transcript, sessionId) {
const outputPath = path.join(outputDir, `${sessionId}.json`);
fs.writeFileSync(outputPath, JSON.stringify(transcript, null, 2));
console.log(`Transcript saved: ${outputPath}`);
// console.log(`Transcript saved: ${outputPath}`);
return outputPath;
}
@@ -89,6 +89,7 @@ module.exports = {
name: 'assembly',
type: 'transcription',
displayname: 'AssemblyAI',
audioformat: "mp3",
async function(audioFileName) {
return new Promise(async (resolve, reject) => {
@@ -115,8 +116,9 @@ module.exports = {
resolve(saveTranscript(transcript, sessionId));
} catch (error) {
console.error('Transcription error:', error.message);
// console.error('Transcription error:', error.message);
reject(error);
return
}
})
}
+1
View File
@@ -1,3 +1,4 @@
module.exports = {
name:"Startup_function",
async function(){