diff --git a/src/assets/locale/da/common.json b/src/assets/locale/da/common.json index 4d0de2d..881dab7 100644 --- a/src/assets/locale/da/common.json +++ b/src/assets/locale/da/common.json @@ -112,5 +112,6 @@ "older": "Ældre" }, "pin": "Fastgør", - "unpin": "Frigør" + "unpin": "Frigør", + "generationInfo": "Genererings Info" } \ No newline at end of file diff --git a/src/assets/locale/de/common.json b/src/assets/locale/de/common.json index 739e0dd..9a69b22 100644 --- a/src/assets/locale/de/common.json +++ b/src/assets/locale/de/common.json @@ -112,5 +112,6 @@ "older": "Älter" }, "pin": "Anheften", - "unpin": "Losheften" + "unpin": "Losheften", + "generationInfo": "Generierungsinformationen" } \ No newline at end of file diff --git a/src/assets/locale/en/common.json b/src/assets/locale/en/common.json index f5b9e39..a7a3ecc 100644 --- a/src/assets/locale/en/common.json +++ b/src/assets/locale/en/common.json @@ -116,5 +116,6 @@ "older": "Older" }, "pin": "Pin", - "unpin": "Unpin" + "unpin": "Unpin", + "generationInfo": "Generation Info" } diff --git a/src/assets/locale/es/common.json b/src/assets/locale/es/common.json index 65590aa..cf5ccf8 100644 --- a/src/assets/locale/es/common.json +++ b/src/assets/locale/es/common.json @@ -111,5 +111,6 @@ "older": "Más antiguo" }, "pin": "Fijar", - "unpin": "Desfijar" + "unpin": "Desfijar", + "generationInfo": "Información de Generación" } \ No newline at end of file diff --git a/src/assets/locale/fa/common.json b/src/assets/locale/fa/common.json index 4adb307..6abb70a 100644 --- a/src/assets/locale/fa/common.json +++ b/src/assets/locale/fa/common.json @@ -105,5 +105,6 @@ "older": "قدیمی‌تر" }, "pin": "پین کردن", - "unpin": "حذف پین" + "unpin": "حذف پین", + "generationInfo": "اطلاعات تولید" } \ No newline at end of file diff --git a/src/assets/locale/fr/common.json b/src/assets/locale/fr/common.json index 8dbe609..0775303 100644 --- a/src/assets/locale/fr/common.json +++ b/src/assets/locale/fr/common.json @@ -111,5 +111,6 @@ "older": "Plus ancien" }, "pin": "Épingler", - "unpin": "Désépingler" + "unpin": "Désépingler", + "generationInfo": "Informations de génération" } \ No newline at end of file diff --git a/src/assets/locale/it/common.json b/src/assets/locale/it/common.json index 80ede68..102b6c8 100644 --- a/src/assets/locale/it/common.json +++ b/src/assets/locale/it/common.json @@ -111,5 +111,6 @@ "older": "Più Vecchi" }, "pin": "Fissa", - "unpin": "Rimuovi" + "unpin": "Rimuovi", + "generationInfo": "Informazioni sulla Generazione" } \ No newline at end of file diff --git a/src/assets/locale/ja-JP/common.json b/src/assets/locale/ja-JP/common.json index df20028..6f38578 100644 --- a/src/assets/locale/ja-JP/common.json +++ b/src/assets/locale/ja-JP/common.json @@ -111,5 +111,6 @@ "older": "それ以前" }, "pin": "固定", - "unpin": "固定解除" + "unpin": "固定解除", + "generationInfo": "生成情報" } \ No newline at end of file diff --git a/src/assets/locale/ko/common.json b/src/assets/locale/ko/common.json index 908bb0d..e59e785 100644 --- a/src/assets/locale/ko/common.json +++ b/src/assets/locale/ko/common.json @@ -111,5 +111,6 @@ "older": "그 이전" }, "pin": "고정", - "unpin": "고정 해제" + "unpin": "고정 해제", + "generationInfo": "생성 정보" } diff --git a/src/assets/locale/ml/common.json b/src/assets/locale/ml/common.json index bb1149a..ed6988a 100644 --- a/src/assets/locale/ml/common.json +++ b/src/assets/locale/ml/common.json @@ -110,5 +110,7 @@ "older": "പഴയത്" }, "pin": "പിൻ ചെയ്യുക", - "unpin": "അൺപിൻ ചെയ്യുക" + "unpin": "അൺപിൻ ചെയ്യുക", + "generationInfo": "ജനറേഷൻ വിവരങ്ങൾ" + } \ No newline at end of file diff --git a/src/assets/locale/no/common.json b/src/assets/locale/no/common.json index 6665280..b95ffce 100644 --- a/src/assets/locale/no/common.json +++ b/src/assets/locale/no/common.json @@ -112,5 +112,6 @@ "older": "Eldre" }, "pin": "Fest", - "unpin": "Løsne" + "unpin": "Løsne", + "generationInfo": "Generasjonsinformasjon" } \ No newline at end of file diff --git a/src/assets/locale/pt-BR/common.json b/src/assets/locale/pt-BR/common.json index ba4f252..a2f1a41 100644 --- a/src/assets/locale/pt-BR/common.json +++ b/src/assets/locale/pt-BR/common.json @@ -111,5 +111,6 @@ "older": "Mais Antigos" }, "pin": "Fixar", - "unpin": "Desafixar" + "unpin": "Desafixar", + "generationInfo": "Informações de Geração" } \ No newline at end of file diff --git a/src/assets/locale/ru/common.json b/src/assets/locale/ru/common.json index b619def..04b54de 100644 --- a/src/assets/locale/ru/common.json +++ b/src/assets/locale/ru/common.json @@ -111,5 +111,6 @@ "older": "Ранее" }, "pin": "Закрепить", - "unpin": "Открепить" + "unpin": "Открепить", + "generationInfo": "Информация о генерации" } \ No newline at end of file diff --git a/src/assets/locale/sv/common.json b/src/assets/locale/sv/common.json index 3127267..b0df981 100644 --- a/src/assets/locale/sv/common.json +++ b/src/assets/locale/sv/common.json @@ -116,5 +116,6 @@ "older": "Äldre" }, "pin": "Fäst", - "unpin": "Ta bort fäst" + "unpin": "Ta bort fäst", + "generationInfo": "Generationsinformation" } diff --git a/src/assets/locale/zh/common.json b/src/assets/locale/zh/common.json index cd58e05..7fb9f9d 100644 --- a/src/assets/locale/zh/common.json +++ b/src/assets/locale/zh/common.json @@ -111,5 +111,6 @@ "older": "更早" }, "pin": "置顶", - "unpin": "取消置顶" + "unpin": "取消置顶", + "generationInfo": "生成信息" } \ No newline at end of file diff --git a/src/components/Common/Playground/GenerationInfo.tsx b/src/components/Common/Playground/GenerationInfo.tsx new file mode 100644 index 0000000..4e34710 --- /dev/null +++ b/src/components/Common/Playground/GenerationInfo.tsx @@ -0,0 +1,65 @@ +type GenerationMetrics = { + total_duration?: number + load_duration?: number + prompt_eval_count?: number + prompt_eval_duration?: number + eval_count?: number + eval_duration?: number + context?: string + response?: string +} + +type Props = { + generationInfo: GenerationMetrics +} + +export const GenerationInfo = ({ generationInfo }: Props) => { + if (!generationInfo) return null + + const calculateTokensPerSecond = ( + evalCount?: number, + evalDuration?: number + ) => { + if (!evalCount || !evalDuration) return 0 + return (evalCount / evalDuration) * 1e9 + } + + const formatDuration = (nanoseconds?: number) => { + if (!nanoseconds) return "0ms" + const ms = nanoseconds / 1e6 + if (ms < 1) return `${ms.toFixed(3)}ms` + if (ms < 1000) return `${Math.round(ms)}ms` + return `${(ms / 1000).toFixed(2)}s` + } + + const metricsToDisplay = { + ...generationInfo, + ...(generationInfo?.eval_count && generationInfo?.eval_duration + ? { + tokens_per_second: calculateTokensPerSecond( + generationInfo.eval_count, + generationInfo.eval_duration + ).toFixed(2) + } + : {}) + } + + return ( +
+
+ {Object.entries(metricsToDisplay) + .filter(([key]) => key !== "model") + .map(([key, value]) => ( +
+
{key}
+
+ {key.includes("duration") + ? formatDuration(value as number) + : String(value)} +
+
+ ))} +
+
+ ) +} diff --git a/src/components/Common/Playground/Message.tsx b/src/components/Common/Playground/Message.tsx index 0c6299c..925fe4c 100644 --- a/src/components/Common/Playground/Message.tsx +++ b/src/components/Common/Playground/Message.tsx @@ -1,10 +1,11 @@ import Markdown from "../../Common/Markdown" import React from "react" -import { Tag, Image, Tooltip, Collapse } from "antd" +import { Tag, Image, Tooltip, Collapse, Popover } from "antd" import { WebSearch } from "./WebSearch" import { CheckIcon, ClipboardIcon, + InfoIcon, Pen, PlayIcon, RotateCcw, @@ -16,6 +17,7 @@ import { MessageSource } from "./MessageSource" import { useTTS } from "@/hooks/useTTS" import { tagColors } from "@/utils/color" import { removeModelSuffix } from "@/db/models" +import { GenerationInfo } from "./GenerationInfo" type Props = { message: string @@ -37,6 +39,7 @@ type Props = { hideEditAndRegenerate?: boolean onSourceClick?: (source: any) => void isTTSEnabled?: boolean + generationInfo?: any } export const PlaygroundMessage = (props: Props) => { @@ -206,6 +209,18 @@ export const PlaygroundMessage = (props: Props) => { )} + {props.generationInfo && ( + + } + title={t("generationInfo")}> + + + )} + {!props.hideEditAndRegenerate && props.currentMessageIndex === props.totalMessages - 1 && ( diff --git a/src/components/Option/Playground/PlaygroundChat.tsx b/src/components/Option/Playground/PlaygroundChat.tsx index 5f2adfe..aaf3462 100644 --- a/src/components/Option/Playground/PlaygroundChat.tsx +++ b/src/components/Option/Playground/PlaygroundChat.tsx @@ -54,6 +54,7 @@ export const PlaygroundChat = () => { setIsSourceOpen(true) }} isTTSEnabled={ttsEnabled} + generationInfo={message?.generationInfo} /> ))} {messages.length > 0 && ( diff --git a/src/components/Sidepanel/Chat/body.tsx b/src/components/Sidepanel/Chat/body.tsx index 61d9071..f9f1407 100644 --- a/src/components/Sidepanel/Chat/body.tsx +++ b/src/components/Sidepanel/Chat/body.tsx @@ -47,6 +47,7 @@ export const SidePanelBody = () => { setIsSourceOpen(true) }} isTTSEnabled={ttsEnabled} + generationInfo={message?.generationInfo} /> ))}
diff --git a/src/db/index.ts b/src/db/index.ts index 4a77553..68b3b02 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -33,6 +33,7 @@ type Message = { search?: WebSearch createdAt: number messageType?: string + generationInfo?: any } type Webshare = { @@ -254,7 +255,8 @@ export const saveMessage = async ( images: string[], source?: any[], time?: number, - message_type?: string + message_type?: string, + generationInfo?: any ) => { const id = generateID() let createdAt = Date.now() @@ -270,7 +272,8 @@ export const saveMessage = async ( images, createdAt, sources: source, - messageType: message_type + messageType: message_type, + generationInfo: generationInfo } const db = new PageAssitDatabase() await db.addMessage(message) diff --git a/src/hooks/chat-helper/index.ts b/src/hooks/chat-helper/index.ts index 18f89be..3f725c0 100644 --- a/src/hooks/chat-helper/index.ts +++ b/src/hooks/chat-helper/index.ts @@ -118,7 +118,7 @@ export const saveMessageOnSuccess = async ({ fullText, source, message_source = "web-ui", - message_type + message_type, generationInfo }: { historyId: string | null setHistoryId: (historyId: string) => void @@ -130,6 +130,7 @@ export const saveMessageOnSuccess = async ({ source: any[] message_source?: "copilot" | "web-ui", message_type?: string + generationInfo?: any }) => { if (historyId) { if (!isRegenerate) { @@ -141,7 +142,8 @@ export const saveMessageOnSuccess = async ({ [image], [], 1, - message_type + message_type, + generationInfo ) } await saveMessage( @@ -152,7 +154,8 @@ export const saveMessageOnSuccess = async ({ [], source, 2, - message_type + message_type, + generationInfo ) await setLastUsedChatModel(historyId, selectedModel!) } else { @@ -166,7 +169,8 @@ export const saveMessageOnSuccess = async ({ [image], [], 1, - message_type + message_type, + generationInfo ) await saveMessage( newHistoryId.id, @@ -176,7 +180,8 @@ export const saveMessageOnSuccess = async ({ [], source, 2, - message_type + message_type, + generationInfo ) setHistoryId(newHistoryId.id) await setLastUsedChatModel(newHistoryId.id, selectedModel!) diff --git a/src/hooks/useMessage.tsx b/src/hooks/useMessage.tsx index c85324a..d9a17ac 100644 --- a/src/hooks/useMessage.tsx +++ b/src/hooks/useMessage.tsx @@ -328,10 +328,25 @@ export const useMessage = () => { const applicationChatHistory = generateHistory(history, selectedModel) + let generationInfo: any | undefined = undefined + const chunks = await ollama.stream( [...applicationChatHistory, humanMessage], { - signal: signal + signal: signal, + callbacks: [ + { + handleLLMEnd( + output: any, + ): any { + try { + generationInfo = output?.generations?.[0][0]?.generationInfo + } catch (e) { + console.log("handleLLMEnd error", e) + } + } + } + ] } ) let count = 0 @@ -361,7 +376,8 @@ export const useMessage = () => { return { ...message, message: fullText, - sources: source + sources: source, + generationInfo } } return message @@ -390,7 +406,8 @@ export const useMessage = () => { image, fullText, source, - message_source: "copilot" + message_source: "copilot", + generationInfo }) setIsProcessing(false) @@ -544,10 +561,25 @@ export const useMessage = () => { ) } + let generationInfo: any | undefined = undefined + const chunks = await ollama.stream( [...applicationChatHistory, humanMessage], { - signal: signal + signal: signal, + callbacks: [ + { + handleLLMEnd( + output: any, + ): any { + try { + generationInfo = output?.generations?.[0][0]?.generationInfo + } catch (e) { + console.log("handleLLMEnd error", e) + } + } + } + ] } ) let count = 0 @@ -576,7 +608,8 @@ export const useMessage = () => { if (message.id === generateMessageId) { return { ...message, - message: fullText + message: fullText, + generationInfo } } return message @@ -605,7 +638,8 @@ export const useMessage = () => { image, fullText, source: [], - message_source: "copilot" + message_source: "copilot", + generationInfo }) setIsProcessing(false) @@ -789,10 +823,24 @@ export const useMessage = () => { ) } + let generationInfo: any | undefined = undefined const chunks = await ollama.stream( [...applicationChatHistory, humanMessage], { - signal: signal + signal: signal, + callbacks: [ + { + handleLLMEnd( + output: any, + ): any { + try { + generationInfo = output?.generations?.[0][0]?.generationInfo + } catch (e) { + console.log("handleLLMEnd error", e) + } + } + } + ] } ) let count = 0 @@ -822,7 +870,8 @@ export const useMessage = () => { return { ...message, message: fullText, - sources: source + sources: source, + generationInfo } } return message @@ -850,7 +899,8 @@ export const useMessage = () => { message, image, fullText, - source + source, + generationInfo }) setIsProcessing(false) @@ -982,8 +1032,23 @@ export const useMessage = () => { }) } + let generationInfo: any | undefined = undefined + const chunks = await ollama.stream([humanMessage], { - signal: signal + signal: signal, + callbacks: [ + { + handleLLMEnd( + output: any, + ): any { + try { + generationInfo = output?.generations?.[0][0]?.generationInfo + } catch (e) { + console.log("handleLLMEnd error", e) + } + } + } + ] }) let count = 0 for await (const chunk of chunks) { @@ -1011,7 +1076,8 @@ export const useMessage = () => { if (message.id === generateMessageId) { return { ...message, - message: fullText + message: fullText, + generationInfo } } return message @@ -1042,7 +1108,8 @@ export const useMessage = () => { fullText, source: [], message_source: "copilot", - message_type: messageType + message_type: messageType, + generationInfo }) setIsProcessing(false) diff --git a/src/hooks/useMessageOption.tsx b/src/hooks/useMessageOption.tsx index 58d56bf..1cccb62 100644 --- a/src/hooks/useMessageOption.tsx +++ b/src/hooks/useMessageOption.tsx @@ -243,10 +243,23 @@ export const useMessageOption = () => { ) } + let generationInfo: any | undefined = undefined + const chunks = await ollama.stream( [...applicationChatHistory, humanMessage], { - signal: signal + signal: signal, + callbacks: [ + { + handleLLMEnd(output: any): any { + try { + generationInfo = output?.generations?.[0][0]?.generationInfo + } catch (e) { + console.log("handleLLMEnd error", e) + } + } + } + ] } ) let count = 0 @@ -276,7 +289,8 @@ export const useMessageOption = () => { return { ...message, message: fullText, - sources: source + sources: source, + generationInfo } } return message @@ -304,7 +318,8 @@ export const useMessageOption = () => { message, image, fullText, - source + source, + generationInfo }) setIsProcessing(false) @@ -465,10 +480,23 @@ export const useMessageOption = () => { ) } + let generationInfo: any | undefined = undefined + const chunks = await ollama.stream( [...applicationChatHistory, humanMessage], { - signal: signal + signal: signal, + callbacks: [ + { + handleLLMEnd(output: any): any { + try { + generationInfo = output?.generations?.[0][0]?.generationInfo + } catch (e) { + console.log("handleLLMEnd error", e) + } + } + } + ], } ) @@ -498,7 +526,8 @@ export const useMessageOption = () => { if (message.id === generateMessageId) { return { ...message, - message: fullText + message: fullText, + generationInfo } } return message @@ -526,7 +555,8 @@ export const useMessageOption = () => { message, image, fullText, - source: [] + source: [], + generationInfo }) setIsProcessing(false) @@ -711,10 +741,23 @@ export const useMessageOption = () => { const applicationChatHistory = generateHistory(history, selectedModel) + let generationInfo: any | undefined = undefined + const chunks = await ollama.stream( [...applicationChatHistory, humanMessage], { - signal: signal + signal: signal, + callbacks: [ + { + handleLLMEnd(output: any): any { + try { + generationInfo = output?.generations?.[0][0]?.generationInfo + } catch (e) { + console.log("handleLLMEnd error", e) + } + } + } + ] } ) let count = 0 @@ -744,7 +787,8 @@ export const useMessageOption = () => { return { ...message, message: fullText, - sources: source + sources: source, + generationInfo } } return message @@ -772,7 +816,8 @@ export const useMessageOption = () => { message, image, fullText, - source + source, + generationInfo }) setIsProcessing(false) diff --git a/src/models/index.ts b/src/models/index.ts index d459e66..02f2ce8 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -49,7 +49,7 @@ export const pageAssistModel = async ({ configuration: { apiKey: providerInfo.apiKey || "temp", baseURL: providerInfo.baseUrl || "", - } + }, }) as any } diff --git a/src/types/message.ts b/src/types/message.ts index 3be7cdc..1ec1d5a 100644 --- a/src/types/message.ts +++ b/src/types/message.ts @@ -1,19 +1,20 @@ type WebSearch = { - search_engine: string - search_url: string - search_query: string - search_results: { - title: string - link: string - }[] - } - export type Message = { - isBot: boolean - name: string - message: string - sources: any[] - images?: string[] - search?: WebSearch - messageType?: string - id?: string - } \ No newline at end of file + search_engine: string + search_url: string + search_query: string + search_results: { + title: string + link: string + }[] +} +export type Message = { + isBot: boolean + name: string + message: string + sources: any[] + images?: string[] + search?: WebSearch + messageType?: string + id?: string + generationInfo?: any +} \ No newline at end of file diff --git a/wxt.config.ts b/wxt.config.ts index c870e8e..288a555 100644 --- a/wxt.config.ts +++ b/wxt.config.ts @@ -50,7 +50,7 @@ export default defineConfig({ outDir: "build", manifest: { - version: "1.3.3", + version: "1.3.4", name: process.env.TARGET === "firefox" ? "Page Assist - A Web UI for Local AI Models"