mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-20 10:20:15 +01:00
refactor(addImages): use in functions agent response and assure generated images are included in the response (#1120)
This commit is contained in:
parent
5c1e44eff7
commit
af69763103
3 changed files with 137 additions and 7 deletions
|
|
@ -357,6 +357,7 @@ If your reverse proxy is compatible to OpenAI specs in every other way, it may s
|
||||||
const trimmedPartial = opts.getPartialText().replaceAll(':::plugin:::\n', '');
|
const trimmedPartial = opts.getPartialText().replaceAll(':::plugin:::\n', '');
|
||||||
responseMessage.text =
|
responseMessage.text =
|
||||||
trimmedPartial.length === 0 ? `${partialText}${this.result.output}` : partialText;
|
trimmedPartial.length === 0 ? `${partialText}${this.result.output}` : partialText;
|
||||||
|
addImages(this.result.intermediateSteps, responseMessage);
|
||||||
await this.generateTextStream(this.result.output, opts.onProgress, { delay: 5 });
|
await this.generateTextStream(this.result.output, opts.onProgress, { delay: 5 });
|
||||||
return await this.handleResponseMessage(responseMessage, saveOptions, user);
|
return await this.handleResponseMessage(responseMessage, saveOptions, user);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,68 @@
|
||||||
|
/**
|
||||||
|
* The `addImages` function corrects any erroneous image URLs in the `responseMessage.text`
|
||||||
|
* and appends image observations from `intermediateSteps` if they are not already present.
|
||||||
|
*
|
||||||
|
* @function
|
||||||
|
* @module addImages
|
||||||
|
*
|
||||||
|
* @param {Array.<Object>} intermediateSteps - An array of objects, each containing an observation.
|
||||||
|
* @param {Object} responseMessage - An object containing the text property which might have image URLs.
|
||||||
|
*
|
||||||
|
* @property {string} intermediateSteps[].observation - The observation string which might contain an image markdown.
|
||||||
|
* @property {string} responseMessage.text - The text which might contain image URLs.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* const intermediateSteps = [
|
||||||
|
* { observation: '' }
|
||||||
|
* ];
|
||||||
|
* const responseMessage = { text: 'Some text with ' };
|
||||||
|
*
|
||||||
|
* addImages(intermediateSteps, responseMessage);
|
||||||
|
*
|
||||||
|
* console.log(responseMessage.text);
|
||||||
|
* // Outputs: 'Some text with \n'
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
function addImages(intermediateSteps, responseMessage) {
|
function addImages(intermediateSteps, responseMessage) {
|
||||||
if (!intermediateSteps || !responseMessage) {
|
if (!intermediateSteps || !responseMessage) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Correct any erroneous URLs in the responseMessage.text first
|
||||||
intermediateSteps.forEach((step) => {
|
intermediateSteps.forEach((step) => {
|
||||||
const { observation } = step;
|
const { observation } = step;
|
||||||
if (!observation || !observation.includes('![')) {
|
if (!observation || !observation.includes('![')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the image file path from the observation
|
const match = observation.match(/\/images\/.*\.\w*/);
|
||||||
const observedImagePath = observation.match(/\(\/images\/.*\.\w*\)/g)[0];
|
if (!match) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const essentialImagePath = match[0];
|
||||||
|
|
||||||
// Check if the responseMessage already includes the image file path
|
const regex = /!\[.*?\]\((.*?)\)/g;
|
||||||
if (!responseMessage.text.includes(observedImagePath)) {
|
let matchErroneous;
|
||||||
// If the image file path is not found, append the whole observation
|
while ((matchErroneous = regex.exec(responseMessage.text)) !== null) {
|
||||||
|
if (matchErroneous[1] && !matchErroneous[1].startsWith('/images/')) {
|
||||||
|
responseMessage.text = responseMessage.text.replace(matchErroneous[1], essentialImagePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Now, check if the responseMessage already includes the correct image file path and append if not
|
||||||
|
intermediateSteps.forEach((step) => {
|
||||||
|
const { observation } = step;
|
||||||
|
if (!observation || !observation.includes('![')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const observedImagePath = observation.match(/\(\/images\/.*\.\w*\)/g);
|
||||||
|
if (observedImagePath && !responseMessage.text.includes(observedImagePath[0])) {
|
||||||
responseMessage.text += '\n' + observation;
|
responseMessage.text += '\n' + observation;
|
||||||
if (this.options.debug) {
|
if (process.env.DEBUG_PLUGINS) {
|
||||||
console.debug('added image from intermediateSteps');
|
console.debug('[addImages] added image from intermediateSteps');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
84
api/app/clients/output_parsers/addImages.spec.js
Normal file
84
api/app/clients/output_parsers/addImages.spec.js
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
let addImages = require('./addImages');
|
||||||
|
|
||||||
|
describe('addImages', () => {
|
||||||
|
let intermediateSteps;
|
||||||
|
let responseMessage;
|
||||||
|
let options;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
intermediateSteps = [];
|
||||||
|
responseMessage = { text: '' };
|
||||||
|
options = { debug: false };
|
||||||
|
this.options = options;
|
||||||
|
addImages = addImages.bind(this);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle null or undefined parameters', () => {
|
||||||
|
addImages(null, responseMessage);
|
||||||
|
expect(responseMessage.text).toBe('');
|
||||||
|
|
||||||
|
addImages(intermediateSteps, null);
|
||||||
|
expect(responseMessage.text).toBe('');
|
||||||
|
|
||||||
|
addImages(null, null);
|
||||||
|
expect(responseMessage.text).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should append correct image markdown if not present in responseMessage', () => {
|
||||||
|
intermediateSteps.push({ observation: '' });
|
||||||
|
addImages(intermediateSteps, responseMessage);
|
||||||
|
expect(responseMessage.text).toBe('\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not append image markdown if already present in responseMessage', () => {
|
||||||
|
responseMessage.text = '';
|
||||||
|
intermediateSteps.push({ observation: '' });
|
||||||
|
addImages(intermediateSteps, responseMessage);
|
||||||
|
expect(responseMessage.text).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correct and append image markdown with erroneous URL', () => {
|
||||||
|
responseMessage.text = '';
|
||||||
|
intermediateSteps.push({ observation: '' });
|
||||||
|
addImages(intermediateSteps, responseMessage);
|
||||||
|
expect(responseMessage.text).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correct multiple erroneous URLs in responseMessage', () => {
|
||||||
|
responseMessage.text =
|
||||||
|
' ';
|
||||||
|
intermediateSteps.push({ observation: '' });
|
||||||
|
intermediateSteps.push({ observation: '' });
|
||||||
|
addImages(intermediateSteps, responseMessage);
|
||||||
|
expect(responseMessage.text).toBe(' ');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not append non-image markdown observations', () => {
|
||||||
|
intermediateSteps.push({ observation: '[desc](/images/test.png)' });
|
||||||
|
addImages(intermediateSteps, responseMessage);
|
||||||
|
expect(responseMessage.text).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle multiple observations', () => {
|
||||||
|
intermediateSteps.push({ observation: '' });
|
||||||
|
intermediateSteps.push({ observation: '' });
|
||||||
|
addImages(intermediateSteps, responseMessage);
|
||||||
|
expect(responseMessage.text).toBe('\n\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not append if observation does not contain image markdown', () => {
|
||||||
|
intermediateSteps.push({ observation: 'This is a test observation without image markdown.' });
|
||||||
|
addImages(intermediateSteps, responseMessage);
|
||||||
|
expect(responseMessage.text).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should append correctly from a real scenario', () => {
|
||||||
|
responseMessage.text =
|
||||||
|
'Here is the generated image based on your request. It depicts a surreal landscape filled with floating musical notes. The style is impressionistic, with vibrant sunset hues dominating the scene. At the center, there\'s a silhouette of a grand piano, adding a dreamy emotion to the overall image. This could serve as a unique and creative music album cover. Would you like to make any changes or generate another image?';
|
||||||
|
const originalText = responseMessage.text;
|
||||||
|
const imageMarkdown = '';
|
||||||
|
intermediateSteps.push({ observation: imageMarkdown });
|
||||||
|
addImages(intermediateSteps, responseMessage);
|
||||||
|
expect(responseMessage.text).toBe(`${originalText}\n${imageMarkdown}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue