mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 08:50:15 +01:00
commit
30a7a80bfc
15 changed files with 1530 additions and 216 deletions
39
api/.eslintrc.js
Normal file
39
api/.eslintrc.js
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
es2021: true,
|
||||||
|
node: true
|
||||||
|
},
|
||||||
|
extends: ['eslint:recommended'],
|
||||||
|
overrides: [],
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module'
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
indent: ['error', 2, { SwitchCase: 1 }],
|
||||||
|
'max-len': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
code: 150,
|
||||||
|
ignoreStrings: true,
|
||||||
|
ignoreTemplateLiterals: true,
|
||||||
|
ignoreComments: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'linebreak-style': 0,
|
||||||
|
'arrow-parens': [2, 'as-needed', { requireForBlockBody: true }],
|
||||||
|
// 'no-plusplus': ['error', { allowForLoopAfterthoughts: true }],
|
||||||
|
'no-console': 'off',
|
||||||
|
'import/extensions': 'off',
|
||||||
|
'no-use-before-define': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
functions: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'no-promise-executor-return': 'off',
|
||||||
|
'no-param-reassign': 'off',
|
||||||
|
'no-continue': 'off',
|
||||||
|
'no-restricted-syntax': 'off'
|
||||||
|
}
|
||||||
|
};
|
||||||
1483
api/package-lock.json
generated
1483
api/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -27,6 +27,7 @@
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"crypto": "^1.0.1",
|
"crypto": "^1.0.1",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
|
"eslint": "^8.36.0",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"express-session": "^1.17.3",
|
"express-session": "^1.17.3",
|
||||||
"html": "^1.0.0",
|
"html": "^1.0.0",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const citationRegex = /\[\^\d+?\^]/g;
|
const citationRegex = /\[\^\d+?\^]/g;
|
||||||
const { getCitations, citeText, detectCode } = require('../../app/');
|
const backtick = /(?<!`)[`](?!`)/g;
|
||||||
|
// const singleBacktick = /(?<!`)[`](?!`)/;
|
||||||
|
const cursorDefault = '<span class="result-streaming">█</span>';
|
||||||
|
const { getCitations, citeText } = require('../../app/');
|
||||||
|
|
||||||
const handleError = (res, message) => {
|
const handleError = (res, message) => {
|
||||||
res.write(`event: error\ndata: ${JSON.stringify(message)}\n\n`);
|
res.write(`event: error\ndata: ${JSON.stringify(message)}\n\n`);
|
||||||
|
|
@ -16,12 +19,43 @@ const sendMessage = (res, message) => {
|
||||||
|
|
||||||
const createOnProgress = () => {
|
const createOnProgress = () => {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
let code = '';
|
||||||
let tokens = '';
|
let tokens = '';
|
||||||
|
let precode = '';
|
||||||
|
let blockCount = 0;
|
||||||
|
let codeBlock = false;
|
||||||
|
let cursor = cursorDefault;
|
||||||
|
|
||||||
const progressCallback = async (partial, { res, text, bing = false, ...rest }) => {
|
const progressCallback = async (partial, { res, text, bing = false, ...rest }) => {
|
||||||
tokens += partial === text ? '' : partial;
|
let chunk = partial === text ? '' : partial;
|
||||||
|
tokens += chunk;
|
||||||
|
precode += chunk;
|
||||||
tokens = tokens.replaceAll('[DONE]', '');
|
tokens = tokens.replaceAll('[DONE]', '');
|
||||||
|
|
||||||
|
if (codeBlock) {
|
||||||
|
code += chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (precode.includes('```') && codeBlock) {
|
||||||
|
codeBlock = false;
|
||||||
|
cursor = cursorDefault;
|
||||||
|
precode = precode.replace(/```/g, '');
|
||||||
|
code = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (precode.includes('```') && code === '') {
|
||||||
|
precode = precode.replace(/```/g, '');
|
||||||
|
codeBlock = true;
|
||||||
|
blockCount++;
|
||||||
|
cursor = blockCount > 1 ? '█\n\n```' : '█\n\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
const backticks = precode.match(backtick);
|
||||||
|
if (backticks && !codeBlock && cursor === cursorDefault) {
|
||||||
|
precode = precode.replace(backtick, '');
|
||||||
|
cursor = '█';
|
||||||
|
}
|
||||||
|
|
||||||
if (tokens.match(/^\n/)) {
|
if (tokens.match(/^\n/)) {
|
||||||
tokens = tokens.replace(/^\n/, '');
|
tokens = tokens.replace(/^\n/, '');
|
||||||
}
|
}
|
||||||
|
|
@ -30,7 +64,7 @@ const createOnProgress = () => {
|
||||||
tokens = citeText(tokens, true);
|
tokens = citeText(tokens, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessage(res, { text: tokens + '█', message: true, initial: i === 0, ...rest });
|
sendMessage(res, { text: tokens + cursor, message: true, initial: i === 0, ...rest });
|
||||||
i++;
|
i++;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,5 +25,6 @@ module.exports = {
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
'react/prop-types': ['off'],
|
'react/prop-types': ['off'],
|
||||||
|
'react/display-name': ['off'],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
19
client/package-lock.json
generated
19
client/package-lock.json
generated
|
|
@ -6456,19 +6456,6 @@
|
||||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/fsevents": {
|
|
||||||
"version": "2.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
|
||||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/function-bind": {
|
"node_modules/function-bind": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||||
|
|
@ -17793,12 +17780,6 @@
|
||||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"fsevents": {
|
|
||||||
"version": "2.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
|
||||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"function-bind": {
|
"function-bind": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useRef, useState } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
import Clipboard from '../svg/Clipboard';
|
import Clipboard from '~/components/svg/Clipboard';
|
||||||
import CheckMark from '../svg/CheckMark';
|
import CheckMark from '~/components/svg/CheckMark';
|
||||||
|
|
||||||
const CodeBlock = ({ lang, codeChildren }) => {
|
const CodeBlock = ({ lang, codeChildren }) => {
|
||||||
const codeRef = useRef(null);
|
const codeRef = useRef(null);
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import Clipboard from '../svg/Clipboard';
|
import Clipboard from '~/components/svg/Clipboard';
|
||||||
import CheckMark from '../svg/CheckMark';
|
import CheckMark from '~/components/svg/CheckMark';
|
||||||
|
|
||||||
const Embed = React.memo(({ children, lang = '', code, matched }) => {
|
const Embed = React.memo(({ children, lang = '', code, matched }) => {
|
||||||
const [buttonText, setButtonText] = useState('Copy code');
|
const [buttonText, setButtonText] = useState('Copy code');
|
||||||
|
|
@ -1,153 +0,0 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import ReactMarkdown from 'react-markdown';
|
|
||||||
import rehypeKatex from 'rehype-katex';
|
|
||||||
import rehypeHighlight from 'rehype-highlight';
|
|
||||||
import remarkMath from 'remark-math';
|
|
||||||
import remarkGfm from 'remark-gfm';
|
|
||||||
import TabLink from './TabLink';
|
|
||||||
import Markdown from 'markdown-to-jsx';
|
|
||||||
import Highlight from './Highlight';
|
|
||||||
import CodeBlock from './CodeBlock';
|
|
||||||
import Embed from './Embed';
|
|
||||||
// import { langSubset } from '~/utils/languages';
|
|
||||||
|
|
||||||
const mdOptions = {
|
|
||||||
wrapper: React.Fragment,
|
|
||||||
forceWrapper: true,
|
|
||||||
overrides: {
|
|
||||||
a: {
|
|
||||||
component: TabLink
|
|
||||||
// props: {
|
|
||||||
// className: 'foo'
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
pre: code,
|
|
||||||
// code: {
|
|
||||||
// component: code
|
|
||||||
// },
|
|
||||||
// pre: {
|
|
||||||
// component: PreBlock
|
|
||||||
// },
|
|
||||||
p: {
|
|
||||||
component: p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const Content = ({ content }) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{/* <ReactMarkdown
|
|
||||||
remarkPlugins={[remarkGfm, [remarkMath, { singleDollarTextMath: false }]]}
|
|
||||||
rehypePlugins={[
|
|
||||||
[rehypeKatex, { output: 'mathml' }],
|
|
||||||
[
|
|
||||||
rehypeHighlight,
|
|
||||||
{
|
|
||||||
detect: true,
|
|
||||||
ignoreMissing: true,
|
|
||||||
subset: langSubset
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]}
|
|
||||||
linkTarget="_new"
|
|
||||||
components={{
|
|
||||||
code,
|
|
||||||
p
|
|
||||||
// li,
|
|
||||||
// ul,
|
|
||||||
// ol
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{content}
|
|
||||||
</ReactMarkdown> */}
|
|
||||||
<Markdown
|
|
||||||
options={mdOptions}
|
|
||||||
>
|
|
||||||
{content}
|
|
||||||
</Markdown>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const PreBlock = ({children, ...rest}) => {
|
|
||||||
console.log('pre', children);
|
|
||||||
if ('type' in children && children ['type'] === 'code') {
|
|
||||||
return code(children['props']);
|
|
||||||
}
|
|
||||||
return <pre {...rest}>{children}</pre>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const code = (props) => {
|
|
||||||
const { inline, className, children, ...rest } = props;
|
|
||||||
|
|
||||||
if ('type' in children && children ['type'] === 'code') {
|
|
||||||
// return code(children['props']);
|
|
||||||
const match = /language-(\w+)/.exec(className || '');
|
|
||||||
const lang = match && match[1];
|
|
||||||
console.log('code', lang, children);
|
|
||||||
|
|
||||||
if (inline) {
|
|
||||||
return <code className={className}>{children}</code>;
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<Embed
|
|
||||||
language={lang}
|
|
||||||
code={children}
|
|
||||||
// matched={matched}
|
|
||||||
>
|
|
||||||
<Highlight
|
|
||||||
language={lang}
|
|
||||||
code={children}
|
|
||||||
/>
|
|
||||||
</Embed>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return <pre {...rest}>{children}</pre>;
|
|
||||||
|
|
||||||
const match = /language-(\w+)/.exec(className || '');
|
|
||||||
const lang = match && match[1];
|
|
||||||
console.log('code', lang, children);
|
|
||||||
|
|
||||||
if (inline) {
|
|
||||||
return <code className={className}>{children}</code>;
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<Embed
|
|
||||||
language={lang}
|
|
||||||
code={children}
|
|
||||||
// matched={matched}
|
|
||||||
>
|
|
||||||
<Highlight
|
|
||||||
language={lang}
|
|
||||||
code={children}
|
|
||||||
/>
|
|
||||||
</Embed>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const p = (props) => {
|
|
||||||
const regex = /^█$/;
|
|
||||||
const match = regex.exec(props?.children || '');
|
|
||||||
// if (match) {
|
|
||||||
// return (
|
|
||||||
// <p className="whitespace-pre-wrap ">
|
|
||||||
// {props?.children.slice(0, -1)}
|
|
||||||
// <span className="result-streaming">{'█'}</span>
|
|
||||||
// </p>
|
|
||||||
// );
|
|
||||||
|
|
||||||
if (match) {
|
|
||||||
return (
|
|
||||||
<p className="whitespace-pre-wrap ">
|
|
||||||
<span className="result-streaming">{'█'}</span>
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <p className="whitespace-pre-wrap ">{props?.children}</p>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Content;
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||||
import Wrapper from './Wrapper';
|
import Wrapper from './Content/Wrapper';
|
||||||
import MultiMessage from './MultiMessage';
|
import MultiMessage from './MultiMessage';
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
import HoverButtons from './HoverButtons';
|
import HoverButtons from './HoverButtons';
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue