mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 17:00:15 +01:00
* ✨ feat: Implement Show Thinking feature; refactor: testing thinking render optimizations * ✨ feat: Refactor Thinking component styles and enhance Markdown rendering * chore: add back removed code, revert type changes * chore: Add back resetCounter effect to Markdown component for improved code block indexing * chore: bump @librechat/agents and google langchain packages * WIP: reasoning type updates * WIP: first pass, reasoning content blocks * chore: revert code * chore: bump @librechat/agents * refactor: optimize reasoning tag handling * style: ul indent padding * feat: add Reasoning component to handle reasoning display * feat: first pass, content reasoning part styling * refactor: add content placeholder for endpoints using new stream handler * refactor: only cache messages when requesting stream audio * fix: circular dep. * fix: add default param * refactor: tts, only request after message stream, fix chrome autoplay * style: update label for submitting state and add localization for 'Thinking...' * fix: improve global audio pause logic and reset active run ID * fix: handle artifact edge cases * fix: remove unnecessary console log from artifact update test * feat: add support for continued message handling with new streaming method --------- Co-authored-by: Marco Beretta <81851188+berry-13@users.noreply.github.com>
109 lines
3.5 KiB
JavaScript
109 lines
3.5 KiB
JavaScript
const ARTIFACT_START = ':::artifact';
|
|
const ARTIFACT_END = ':::';
|
|
|
|
/**
|
|
* Find all artifact boundaries in the message
|
|
* @param {TMessage} message
|
|
* @returns {Array<{start: number, end: number, source: 'content'|'text', partIndex?: number}>}
|
|
*/
|
|
const findAllArtifacts = (message) => {
|
|
const artifacts = [];
|
|
|
|
// Check content parts first
|
|
if (message.content?.length) {
|
|
message.content.forEach((part, partIndex) => {
|
|
if (part.type === 'text' && typeof part.text === 'string') {
|
|
let currentIndex = 0;
|
|
let start = part.text.indexOf(ARTIFACT_START, currentIndex);
|
|
|
|
while (start !== -1) {
|
|
const end = part.text.indexOf(ARTIFACT_END, start + ARTIFACT_START.length);
|
|
artifacts.push({
|
|
start,
|
|
end: end !== -1 ? end + ARTIFACT_END.length : part.text.length,
|
|
source: 'content',
|
|
partIndex,
|
|
text: part.text,
|
|
});
|
|
|
|
currentIndex = end !== -1 ? end + ARTIFACT_END.length : part.text.length;
|
|
start = part.text.indexOf(ARTIFACT_START, currentIndex);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Check message.text if no content parts
|
|
if (!artifacts.length && message.text) {
|
|
let currentIndex = 0;
|
|
let start = message.text.indexOf(ARTIFACT_START, currentIndex);
|
|
|
|
while (start !== -1) {
|
|
const end = message.text.indexOf(ARTIFACT_END, start + ARTIFACT_START.length);
|
|
artifacts.push({
|
|
start,
|
|
end: end !== -1 ? end + ARTIFACT_END.length : message.text.length,
|
|
source: 'text',
|
|
text: message.text,
|
|
});
|
|
|
|
currentIndex = end !== -1 ? end + ARTIFACT_END.length : message.text.length;
|
|
start = message.text.indexOf(ARTIFACT_START, currentIndex);
|
|
}
|
|
}
|
|
|
|
return artifacts;
|
|
};
|
|
|
|
const replaceArtifactContent = (originalText, artifact, original, updated) => {
|
|
const artifactContent = artifact.text.substring(artifact.start, artifact.end);
|
|
|
|
// Find boundaries between ARTIFACT_START and ARTIFACT_END
|
|
const contentStart = artifactContent.indexOf('\n', artifactContent.indexOf(ARTIFACT_START)) + 1;
|
|
const contentEnd = artifactContent.lastIndexOf(ARTIFACT_END);
|
|
|
|
if (contentStart === -1 || contentEnd === -1) {
|
|
return null;
|
|
}
|
|
|
|
// Check if there are code blocks
|
|
const codeBlockStart = artifactContent.indexOf('```\n', contentStart);
|
|
const codeBlockEnd = artifactContent.lastIndexOf('\n```', contentEnd);
|
|
|
|
// Determine where to look for the original content
|
|
let searchStart, searchEnd;
|
|
if (codeBlockStart !== -1 && codeBlockEnd !== -1) {
|
|
// If code blocks exist, search between them
|
|
searchStart = codeBlockStart + 4; // after ```\n
|
|
searchEnd = codeBlockEnd;
|
|
} else {
|
|
// Otherwise search in the whole artifact content
|
|
searchStart = contentStart;
|
|
searchEnd = contentEnd;
|
|
}
|
|
|
|
const innerContent = artifactContent.substring(searchStart, searchEnd);
|
|
// Remove trailing newline from original for comparison
|
|
const originalTrimmed = original.replace(/\n$/, '');
|
|
const relativeIndex = innerContent.indexOf(originalTrimmed);
|
|
|
|
if (relativeIndex === -1) {
|
|
return null;
|
|
}
|
|
|
|
const absoluteIndex = artifact.start + searchStart + relativeIndex;
|
|
const endText = originalText.substring(absoluteIndex + originalTrimmed.length);
|
|
const hasTrailingNewline = endText.startsWith('\n');
|
|
|
|
const updatedText =
|
|
originalText.substring(0, absoluteIndex) + updated + (hasTrailingNewline ? '' : '\n') + endText;
|
|
|
|
return updatedText.replace(/\n+(?=```\n:::)/g, '\n');
|
|
};
|
|
|
|
module.exports = {
|
|
ARTIFACT_START,
|
|
ARTIFACT_END,
|
|
findAllArtifacts,
|
|
replaceArtifactContent,
|
|
};
|