13 Commits

Author SHA1 Message Date
Heiko Joerg Schick 023a8a202b Fixed chunked encoding not sent in realtime with revers proxy software 2023-12-07 21:15:15 +01:00
Heiko Joerg Schick 82b77b0baa Updated Alpine version 2023-11-08 09:52:55 +01:00
Heiko Joerg Schick f65aca0221 Added GPT-4-Turbo 2023-11-08 09:50:56 +01:00
schihei fd8b183f51 Merge branch 'feature/export-chat-to-screenshot' into 'wip/h3132'
Added functionality to export chats to screenshot

See merge request schihei/chatbot-ui!4
2023-08-13 08:34:41 +00:00
Heiko Joerg Schick db4375ca8a Added functionality to export chats to screenshot 2023-08-13 10:33:24 +02:00
Heiko Joerg Schick accab8e0cd Merge branch 'wip/h3132' of ssh://gitlab.h3132.de:30002/schihei/chatbot-ui into wip/h3132 2023-08-12 23:26:12 +02:00
Heiko Joerg Schick 6c39256008 Minor bugfixes 2023-08-12 23:25:56 +02:00
schihei e48fda0a2a Merge branch 'feature/export-chat-to-markdown' into 'wip/h3132'
Added functionallity to export chat to markdown

See merge request schihei/chatbot-ui!3
2023-08-12 21:18:52 +00:00
Heiko Joerg Schick 1f94effb43 Added functionallity to export chat to markdown 2023-08-12 22:50:15 +02:00
schihei b14c052dcd Merge branch 'feature/GPT-3.5-turbo-16k-model' into 'wip/h3132'
Added GPT-3.5-turbo-16k model

See merge request schihei/chatbot-ui!1
2023-08-12 20:41:57 +00:00
schihei c067490a78 Merge branch 'feature/export-chat-to-pdf' into 'wip/h3132'
Added functionallity to export chat to PDF

See merge request schihei/chatbot-ui!2
2023-08-12 20:40:35 +00:00
Heiko Joerg Schick 6a49f664db Added functionallity to export chat to PDF 2023-08-12 22:26:24 +02:00
Heiko Joerg Schick 10733fab7d Added GPT-3.5-turbo-16k model 2023-08-12 22:13:34 +02:00
5 changed files with 129 additions and 4 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
# ---- Base Node ----
FROM node:19-alpine AS base
FROM node:20-alpine AS base
WORKDIR /app
COPY package*.json ./
@@ -13,7 +13,7 @@ COPY . .
RUN npm run build
# ---- Production ----
FROM node:19-alpine AS production
FROM node:20-alpine AS production
WORKDIR /app
COPY --from=dependencies /app/node_modules ./node_modules
COPY --from=build /app/.next ./.next
+103 -1
View File
@@ -1,4 +1,10 @@
import { IconClearAll, IconSettings } from '@tabler/icons-react';
import {
IconClearAll,
IconSettings,
IconMarkdown,
IconPdf,
IconScreenshot,
} from '@tabler/icons-react';
import {
MutableRefObject,
memo,
@@ -34,6 +40,11 @@ import { SystemPrompt } from './SystemPrompt';
import { TemperatureSlider } from './Temperature';
import { MemoizedChatMessage } from './MemoizedChatMessage';
import {jsPDF} from "jspdf";
import html2canvas from "html2canvas";
import { toPng } from 'html-to-image';
interface Props {
stopConversationRef: MutableRefObject<boolean>;
}
@@ -300,6 +311,79 @@ export const Chat = memo(({ stopConversationRef }: Props) => {
}
};
const onMarkdown = () => {
if (!selectedConversation){
return '';
}
let markdownContent = '';
selectedConversation.messages.forEach(obj => {
markdownContent += `## ${obj.role === "user" ? t('You') : t("AI")}\n\n${obj.content}\n\n`;
});
const date = new Date().toLocaleString("default", { year: "numeric", month: "long", day: "numeric" })
const time = new Date().toLocaleTimeString("default", {hour12: true, hour: "numeric", minute: "numeric"})
markdownContent += `---\n`
markdownContent += `${t("Exported on")} ` + date + ` ${t("at")} ` + time + ".";
const markdownFile = new Blob([markdownContent], { type: 'text/markdown' });
const downloadLink = document.createElement('a');
downloadLink.href = URL.createObjectURL(markdownFile);
downloadLink.download = `${selectedConversation?.name || 'conversation'}.md`;
downloadLink.click();
}
const onPdf = () => {
if (chatContainerRef.current === null) {
return;
}
else {
chatContainerRef.current.classList.remove('max-h-full')
html2canvas(chatContainerRef.current).then((canvas) => {
if (chatContainerRef.current) {
chatContainerRef.current.classList.add('max-h-full')
}
const imgData = canvas.toDataURL('image/png');
const orientation = canvas.width > canvas.height ? "l" : "p";
const pixelRatio = window.devicePixelRatio > 2 ? window.devicePixelRatio : 2
const pdf = new jsPDF(
orientation,
"pt",
[canvas.width / pixelRatio, canvas.height / pixelRatio],
true,
);
var pdfWidth = pdf.internal.pageSize.getWidth();
var pdfHeight = pdf.internal.pageSize.getHeight();
pdf.addImage(imgData, "PNG", 0, 0, pdfWidth, pdfHeight, "", "FAST");
const title = `${selectedConversation?.name || 'conversation'}.pdf`
pdf.save(title)
})
}
};
const onScreenshot = () => {
if (chatContainerRef.current === null) {
return;
}
chatContainerRef.current.classList.remove('max-h-full');
toPng(chatContainerRef.current, { cacheBust: true })
.then((dataUrl) => {
const link = document.createElement('a');
link.download = `${selectedConversation?.name || 'conversation'}.png`;
link.href = dataUrl;
link.click();
if (chatContainerRef.current) {
chatContainerRef.current.classList.add('max-h-full');
}
})
.catch((err) => {
console.log(err);
});
};
const scrollDown = () => {
if (autoScrollEnabled) {
messagesEndRef.current?.scrollIntoView(true);
@@ -454,6 +538,24 @@ export const Chat = memo(({ stopConversationRef }: Props) => {
>
<IconClearAll size={18} />
</button>
<button
className="ml-2 cursor-pointer hover:opacity-50"
onClick={onMarkdown}
>
<IconMarkdown size={18} />
</button>
<button
className="ml-2 cursor-pointer hover:opacity-50"
onClick={onPdf}
>
<IconPdf size={18} />
</button>
<button
className="ml-2 cursor-pointer hover:opacity-50"
onClick={onScreenshot}
>
<IconScreenshot size={18} />
</button>
</div>
{showSettings && (
<div className="flex flex-col space-y-10 md:mx-auto md:max-w-xl md:gap-6 md:py-3 md:pt-6 lg:max-w-2xl lg:px-0 xl:max-w-3xl">
+3
View File
@@ -15,7 +15,10 @@
"@dqbd/tiktoken": "^1.0.2",
"@tabler/icons-react": "^2.9.0",
"eventsource-parser": "^0.1.0",
"html2canvas": "^1.4.1",
"html-to-image": "^1.11.11",
"i18next": "^22.4.13",
"jspdf": "^2.5.1",
"next": "13.2.4",
"next-i18next": "^13.2.2",
"openai": "^3.2.1",
+7 -1
View File
@@ -54,7 +54,13 @@ const handler = async (req: Request): Promise<Response> => {
const stream = await OpenAIStream(model, promptToSend, temperatureToUse, key, messagesToSend);
return new Response(stream);
var resp = new Response(stream);
// let proxy services like nginx or argo tunnel know about pass the chunk immediately
// similar to nginx option `proxy_buffering off;`
resp.headers.set('Content-Type', 'text/event-stream');
return resp;
} catch (error) {
console.error(error);
if (error instanceof OpenAIError) {
+14
View File
@@ -10,8 +10,10 @@ export interface OpenAIModel {
export enum OpenAIModelID {
GPT_3_5 = 'gpt-3.5-turbo',
GPT_3_5_AZ = 'gpt-35-turbo',
GPT_3_5_16K = 'gpt-3.5-turbo-16k',
GPT_4 = 'gpt-4',
GPT_4_32K = 'gpt-4-32k',
GPT_4_TURBO = 'gpt-4-1106-preview'
}
// in case the `DEFAULT_MODEL` environment variable is not set or set to an unsupported model
@@ -30,6 +32,12 @@ export const OpenAIModels: Record<OpenAIModelID, OpenAIModel> = {
maxLength: 12000,
tokenLimit: 4000,
},
[OpenAIModelID.GPT_3_5_16K]: {
id: OpenAIModelID.GPT_3_5_16K,
name: 'GPT-3.5-16K',
maxLength: 96000,
tokenLimit: 32000,
},
[OpenAIModelID.GPT_4]: {
id: OpenAIModelID.GPT_4,
name: 'GPT-4',
@@ -42,4 +50,10 @@ export const OpenAIModels: Record<OpenAIModelID, OpenAIModel> = {
maxLength: 96000,
tokenLimit: 32000,
},
[OpenAIModelID.GPT_4_TURBO]: {
id: OpenAIModelID.GPT_4_TURBO,
name: 'GPT-4-TURBO',
maxLength: 380000,
tokenLimit: 128000,
},
};