diff --git a/operation/1.png b/operation/1.png new file mode 100644 index 0000000..9628eac Binary files /dev/null and b/operation/1.png differ diff --git a/operation/2.png b/operation/2.png new file mode 100644 index 0000000..9e2796c Binary files /dev/null and b/operation/2.png differ diff --git a/operation/3.png b/operation/3.png new file mode 100644 index 0000000..974be92 Binary files /dev/null and b/operation/3.png differ diff --git a/operation/4.png b/operation/4.png new file mode 100644 index 0000000..b2a2b1d Binary files /dev/null and b/operation/4.png differ diff --git a/src/assets/icons/a.svg b/src/assets/icons/a.svg new file mode 100644 index 0000000..27ce94d --- /dev/null +++ b/src/assets/icons/a.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/b.svg b/src/assets/icons/b.svg new file mode 100644 index 0000000..dad9420 --- /dev/null +++ b/src/assets/icons/b.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/bulb.svg b/src/assets/icons/bulb.svg new file mode 100644 index 0000000..18d1176 --- /dev/null +++ b/src/assets/icons/bulb.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/c.svg b/src/assets/icons/c.svg new file mode 100644 index 0000000..f0fdfa0 --- /dev/null +++ b/src/assets/icons/c.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/d.svg b/src/assets/icons/d.svg new file mode 100644 index 0000000..7821513 --- /dev/null +++ b/src/assets/icons/d.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/e.svg b/src/assets/icons/e.svg new file mode 100644 index 0000000..d6146cc --- /dev/null +++ b/src/assets/icons/e.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/eye.svg b/src/assets/icons/eye.svg new file mode 100644 index 0000000..4ebe497 --- /dev/null +++ b/src/assets/icons/eye.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/f.svg b/src/assets/icons/f.svg new file mode 100644 index 0000000..880bc1d --- /dev/null +++ b/src/assets/icons/f.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/rocket.svg b/src/assets/icons/rocket.svg new file mode 100644 index 0000000..049fc99 --- /dev/null +++ b/src/assets/icons/rocket.svg @@ -0,0 +1 @@ + diff --git a/src/assets/locale/zh/option.json b/src/assets/locale/zh/option.json index 1ce133c..0d41a2f 100644 --- a/src/assets/locale/zh/option.json +++ b/src/assets/locale/zh/option.json @@ -1,29 +1,30 @@ { - "newChat": "新聊天", - "selectAPrompt": "选择一个提示词", - "githubRepository": "GitHub 仓库", - "settings": "设置", - "metering": "计量", - "sidebarTitle": "聊天历史", - "error": "错误", - "somethingWentWrong": "出现了错误", - "validationSelectModel": "请选择一个模型以继续", - "deleteHistoryConfirmation": "你确定要删除这个历史记录吗?", - "editHistoryTitle": "输入一个新的标题", - "temporaryChat": "临时聊天", - "more": { - "copy": { - "group": "复制", - "asText": "复制为文本", - "asMarkdown": "复制为 Markdown", - "success": "已复制到剪贴板!" - }, - "download": { - "group": "下载", - "text": "文本文件 (.txt)", - "markdown": "Markdown 文件 (.md)", - "json": "JSON 文件 (.json)" - }, - "share": "分享" - } -} \ No newline at end of file + "projectTitle": "数联网科创智能体", + "newChat": "新对话", + "selectAPrompt": "选择一个提示词", + "githubRepository": "GitHub 仓库", + "settings": "设置", + "metering": "计量", + "sidebarTitle": "对话历史", + "error": "错误", + "somethingWentWrong": "出现了错误", + "validationSelectModel": "请选择一个模型以继续", + "deleteHistoryConfirmation": "你确定要删除这个历史记录吗?", + "editHistoryTitle": "输入一个新的标题", + "temporaryChat": "临时对话", + "more": { + "copy": { + "group": "复制", + "asText": "复制为文本", + "asMarkdown": "复制为 Markdown", + "success": "已复制到剪贴板!" + }, + "download": { + "group": "下载", + "text": "文本文件 (.txt)", + "markdown": "Markdown 文件 (.md)", + "json": "JSON 文件 (.json)" + }, + "share": "分享" + } +} diff --git a/src/components/Common/DataNavigation.tsx b/src/components/Common/DataNavigation.tsx new file mode 100644 index 0000000..7e1f48d --- /dev/null +++ b/src/components/Common/DataNavigation.tsx @@ -0,0 +1,38 @@ +import React from "react"; +import { Typography, Button } from "antd"; +import { AcademicCapIcon, ChevronRightIcon } from "@heroicons/react/24/outline"; + +const { Title } = Typography; + +type Props = { + Header: React.ReactNode; + showButton?: boolean; + onClick?: () => void; +}; + +export const DataNavigation: React.FC = ({ Header, showButton = true, onClick }) => { + return ( +
+ {/* 左侧部分 */} +
+ + {Header} + +
+ + {/* 右侧部分 */} + {showButton && ( + + )} +
+ ) +}; diff --git a/src/components/Common/Markdown.tsx b/src/components/Common/Markdown.tsx index d0eacd7..c497823 100644 --- a/src/components/Common/Markdown.tsx +++ b/src/components/Common/Markdown.tsx @@ -12,7 +12,7 @@ import { preprocessLaTeX } from "@/utils/latex" function Markdown({ message, - className = "prose break-words dark:prose-invert prose-p:leading-relaxed prose-pre:p-0 dark:prose-dark" + className = "prose-lg break-words dark:prose-invert prose-p:leading-relaxed prose-pre:p-0 dark:prose-dark" }: { message: string className?: string diff --git a/src/components/Common/Playground/Data.tsx b/src/components/Common/Playground/Data.tsx new file mode 100644 index 0000000..98e6ece --- /dev/null +++ b/src/components/Common/Playground/Data.tsx @@ -0,0 +1,175 @@ +import React from "react" +import { DataNavigation } from "@/components/Common/DataNavigation.tsx" +import { Card, Drawer, List } from "antd" +import { useCallback, useState } from "react" + +export const PlaygroundData = () => { + // 模拟数据 + const data: { + title: string + description: string + time: string + metadata?: string + }[] = [ + { + title: "2019-2024年黄海清浅海域中河湖代数生物物种数据集", + description: + "数字对象标识: CSTR:13452.11.01.11.2021.242 国家海洋科学数据中心", + time: "包括2019年8月,2021年8月和2024年6月", + metadata: "热 榜 第" + }, + { + title: "祁连山老虎沟大本营10米气象每日值数据集(V1.0)(2018-2023)", + description: + "中国科学院西北生态环境资源研究院,2021年8月3日发布,2021年8月3日20:48更新", + time: "包括2019年8月,2021年8月和2024年6月", + metadata: "热 榜 第" + }, + { + title: "李嘉图为研究老虎沟大本营2014-2018年...", + description: + "中国科学院西北生态环境资源研究院,2021年8月3日发布,2021年8月3日20:48更新", + time: "包括2019年8月,2021年8月和2024年6月", + metadata: "热 榜 第" + }, + { + title: "青海玉树B1区俄日矿勘探数据2017-2023", + description: + "数字中国集团,CSTR:3260.11.1528414774204895456,DT2023年地质勘探补充调查", + time: "包括2019年8月,2021年8月和2024年6月", + metadata: "热 榜 第" + } + ] + + for (let i = 0; i < 10; i++) { + data.push({ + title: "中国资源环境网", + description: "中国资源环境网,2021年8月3日发布,2021年8月3日20:48更新", + time: "包括2019年8月,2021年8月和2024年6月" + }) + } + + const [open, setOpen] = useState(false) + + const showDrawer = () => { + setOpen(true) + } + + const onClose = () => { + setOpen(false) + } + + return ( +
+ {/* 数据导航 */} + + + + + + + + + + 相关数据 +
+ } + onClick={showDrawer} + /> + + {/* 数据列表 */} +
+ {data.slice(0, 3).map((item, index) => ( + +
+

+ {item.title} +

+

+ {item.description} +

+

{item.time}

+ {item.metadata && ( +
+ + + + + + + {item.metadata} +
+ )} +
+
+ ))} +
+ + {/* 抽屉 */} + + ( + + + {item.title} + + } + description={ +
+

{item.description}

+

{item.time}

+ {item.metadata && ( +
+ + + + + + + {item.metadata} +
+ )} +
+ } + /> +
+ )} + /> +
+ + ) +} diff --git a/src/components/Common/Playground/History.tsx b/src/components/Common/Playground/History.tsx new file mode 100644 index 0000000..d1ace83 --- /dev/null +++ b/src/components/Common/Playground/History.tsx @@ -0,0 +1,84 @@ +import { Sidebar } from "@/components/Option/Sidebar.tsx" +import React, { useContext, useState } from "react" +import { useMessageOption } from "@/hooks/useMessageOption.tsx" +import { useStoreChatModelSettings } from "@/store/model.tsx" +import { Card, Tooltip } from "antd" +import { PageAssitDatabase } from "@/db" +import { EraserIcon } from "lucide-react" +import { useTranslation } from "react-i18next" +import { useQueryClient } from "@tanstack/react-query" +import { HistoryContext } from "@/components/Layouts/Layout.tsx" + +export const PlaygroundHistory = () => { + const { setSystemPrompt } = useStoreChatModelSettings() + + const { show, setShow } = useContext(HistoryContext) + + const { + setMessages, + setHistory, + setHistoryId, + historyId, + clearChat, + setSelectedModel, + temporaryChat, + setSelectedSystemPrompt + } = useMessageOption() + + const { t } = useTranslation(["option", "common", "settings"]) + + const queryClient = useQueryClient() + + return ( + :nth-child(2)]:flex-1 [&>:nth-child(2)]:overflow-y-auto w-[300px] h-full pt-16 pb-5 transition-all duration-300 ease-in-out transform ${ + show + ? 'opacity-100 translate-x-0' + : 'opacity-0 -translate-x-full absolute' + }`} + style={{ paddingTop: "4rem" }} + title={ +
+ {t("sidebarTitle")} + + + +
+ }> +
+ setShow(true)} + setMessages={setMessages} + setHistory={setHistory} + setHistoryId={setHistoryId} + setSelectedModel={setSelectedModel} + setSelectedSystemPrompt={setSelectedSystemPrompt} + clearChat={clearChat} + historyId={historyId} + setSystemPrompt={setSystemPrompt} + temporaryChat={temporaryChat} + history={history} + /> +
+
+ ) +} diff --git a/src/components/Common/Playground/IodRelevant.tsx b/src/components/Common/Playground/IodRelevant.tsx new file mode 100644 index 0000000..648e3a8 --- /dev/null +++ b/src/components/Common/Playground/IodRelevant.tsx @@ -0,0 +1,95 @@ +import React from 'react'; + +const SuccessIcon = () => { + return ( + + ) +} + +const LoadingIcon = () => { + return ( + + + + + + + ) +} + + +export const PlaygroundIodRelevant: React.FC = () => { + const data = [ + { + title: "已在29个科学数据中心的50万个科学数据集中进行搜索", + description: "已发现4个数据集", + status: "success" + }, + { + title: "已在100万篇论文、2800个科创场景中进行搜索", + description: "已发现4个数据集", + status: "success" + }, + { + title: "正在1000位智库专家、12万个创新机构中进行搜索", + status: "loading" + }, + ] + + for (let i = 0; i < 10; i++) { + data.push({ + title: "已在29个科学数据中心的50万个科学数据集中进行搜索" + i, + description: "已发现4个数据集", + status: "success" + }) + } + + + return ( +
+ {/* Header */} +
+

+ 数联网搜索相关内 +

+ {data.length}个结果 +
+ + {/* Content */} +
+ { + data.map((item, index) => ( +
+
+ {item.status === "success" ? : } +
+
+

+ {item.title} +

+ {item.description &&

{item.description}

} +
+
+ )) + } +
+
+ ) +} diff --git a/src/components/Common/Playground/Message.tsx b/src/components/Common/Playground/Message.tsx index 6e8a971..7592e83 100644 --- a/src/components/Common/Playground/Message.tsx +++ b/src/components/Common/Playground/Message.tsx @@ -58,7 +58,7 @@ export const PlaygroundMessage = (props: Props) => { const { t } = useTranslation("common") const { cancel, isSpeaking, speak } = useTTS() return ( -
+
{/*
*/}
@@ -138,7 +138,7 @@ export const PlaygroundMessage = (props: Props) => { ) : (

diff --git a/src/components/Common/Playground/Scene.tsx b/src/components/Common/Playground/Scene.tsx new file mode 100644 index 0000000..ca6a994 --- /dev/null +++ b/src/components/Common/Playground/Scene.tsx @@ -0,0 +1,142 @@ +import React, { useState } from "react" +import { DataNavigation } from "@/components/Common/DataNavigation.tsx" +import { Card, Drawer, List } from "antd" + +export const PlaygroundScene = () => { + // 模拟数据 + const data = [ + { + title: "绿色化工工艺项目", + description: + "基于生物基原料,采用repeal2.0可降解材料技术,开发新型环保材料。", + demander: "奥赛康药业 供方:美国Propella公司" + }, + { + title: "智能农业解决方案", + description: "利用物联网技术,实现精准农业管理,提高农作物产量。", + demander: "奥赛康药业 供方:美国Propella公司" + }, + { + title: "新能源汽车电池技术", + description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。", + demander: "奥赛康药业 供方:美国Propella公司" + }, + { + title: "碳捕集与封存技术", + description: "开发高效的碳捕集技术,减少工业排放,助力碳中和目标。", + demander: "奥赛康药业 供方:美国Propella公司" + } + ] + + for (let i = 0; i < 10; i++) { + data.push({ + title: "开发新型电池材料", + description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。", + demander: "奥赛康药业 供方:美国Propella公司" + }) + } + + const [open, setOpen] = useState(false) + + const showDrawer = () => { + setOpen(true) + } + + const onClose = () => { + setOpen(false) + } + + return ( +

+ {/* 数据导航 */} + + + + + + + + 相关场景 +
+ } + onClick={showDrawer} + /> + + {/* 场景列表 */} +
+ {data.slice(0,3).map((item, index) => ( + +
+

+ {item.title} +

+

+ + 技 术 应 + + + 制 造 + +

+

{item.demander}

+ + {item.description} + +
+
+ ))} +
+ + {/* 抽屉 */} + + ( + + + {item.title} + + } + description={ +
+

+ + 技 术 应 + + + 制 造 + +

+

+ {item.demander} +

+ + {item.description} + +
+ } + /> +
+ )} + /> +
+
+ ) +} diff --git a/src/components/Common/Playground/Team.tsx b/src/components/Common/Playground/Team.tsx new file mode 100644 index 0000000..c4fa3d8 --- /dev/null +++ b/src/components/Common/Playground/Team.tsx @@ -0,0 +1,136 @@ +import React, { useState } from "react" +import { DataNavigation } from "@/components/Common/DataNavigation.tsx" +import { Card, Drawer, List } from "antd" + +export const PlaygroundTeam = () => { + // 模拟数据 + const data = [ + { + title: "绿色化工工艺项目", + description: + "基于生物基原料,采用repeal2.0可降解材料技术,开发新型环保材料。", + demander: "奥赛康药业 供方:美国Propella公司" + }, + { + title: "智能农业解决方案", + description: "利用物联网技术,实现精准农业管理,提高农作物产量。", + demander: "奥赛康药业 供方:美国Propella公司" + }, + { + title: "新能源汽车电池技术", + description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。", + demander: "奥赛康药业 供方:美国Propella公司" + }, + { + title: "碳捕集与封存技术", + description: "开发高效的碳捕集技术,减少工业排放,助力碳中和目标。", + demander: "奥赛康药业 供方:美国Propella公司" + } + ] + + for (let i = 0; i < 10; i++) { + data.push({ + title: "开发新型电池材料", + description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。", + demander: "奥赛康药业 供方:美国Propella公司" + }) + } + + const [open, setOpen] = useState(false) + + const showDrawer = () => { + setOpen(true) + } + + const onClose = () => { + setOpen(false) + } + + return ( +
+ {/* 数据导航 */} + + + + + + + + 相关团队 +
+ } + onClick={showDrawer} + /> + + {/* 场景列表 */} +
+ {data.slice(0,2).map((item, index) => ( + +
+

+ {item.title} +

+

+ + 技 术 应 + + + 制 造 + +

+

{item.description}

+
+
+ ))} +
+ + {/* 抽屉 */} + + ( + + + {item.title} + + } + description={ +
+

+ + 技 术 应 + + + 制 造 + +

+

+ {item.description} +

+
+ } + /> +
+ )} + /> +
+
+ ) +} diff --git a/src/components/Common/Playground/TokenStatistics.tsx b/src/components/Common/Playground/TokenStatistics.tsx new file mode 100644 index 0000000..f974595 --- /dev/null +++ b/src/components/Common/Playground/TokenStatistics.tsx @@ -0,0 +1,46 @@ +import { DataNavigation } from "@/components/Common/DataNavigation.tsx" +import { Card, Descriptions, DescriptionsProps, Drawer, List, Spin } from "antd" +import { useCallback, useMemo, useState } from "react" +import { useMessageOption } from "@/hooks/useMessageOption.tsx" +import { useStoreMessageOption } from "@/store/option.tsx" + +export const PlaygroundTokenStatistics = () => { + const { currentMeteringEntry } = useStoreMessageOption() + + const items = useMemo(() => { + const { data } = currentMeteringEntry + return [ + // { + // key: "relatedDataCount", + // label: "关联数据个数", + // children: data.relatedDataCount + // }, + { + key: "iodTokenCount", + label: "数联网引用token总数", + children: data.iodTokenCount ?? 0 + }, + { + key: "modelInputTokenCount", + label: "大模型输入token数量", + children: data.modelInputTokenCount ?? 0 + }, + { + key: "modelOutputTokenCount", + label: "大模型输出token数量", + children: data.modelOutputTokenCount ?? 0 + } + ] + }, [currentMeteringEntry]) + + return ( + }> + + + + + ) +} diff --git a/src/components/Layouts/Header.tsx b/src/components/Layouts/Header.tsx index 62ad8a6..c1e5aba 100644 --- a/src/components/Layouts/Header.tsx +++ b/src/components/Layouts/Header.tsx @@ -24,18 +24,24 @@ import { ProviderIcons } from "../Common/ProviderIcon" import { NewChat } from "./NewChat" import { PageAssistSelect } from "../Select" import { MoreOptions } from "./MoreOptions" +import { useContext } from "react" +import { HistoryContext } from "@/components/Layouts/Layout.tsx" type Props = { - setSidebarOpen: (open: boolean) => void + sidebarOpen: boolean + setSidebarOpen: () => void setOpenModelSettings: (open: boolean) => void } export const Header: React.FC = ({ setOpenModelSettings, - setSidebarOpen + setSidebarOpen, + sidebarOpen }) => { const { t, i18n } = useTranslation(["option", "common"]) const isRTL = i18n?.dir() === "rtl" + + const [shareModeEnabled] = useStorage("shareMode", false) const [hideCurrentChatModelSettings] = useStorage( "hideCurrentChatModelSettings", @@ -109,10 +115,16 @@ export const Header: React.FC = ({
)} -
+
+

+ {t("projectTitle")} +

+
diff --git a/src/components/Layouts/Layout.tsx b/src/components/Layouts/Layout.tsx index a5ae02d..d98add0 100644 --- a/src/components/Layouts/Layout.tsx +++ b/src/components/Layouts/Layout.tsx @@ -1,103 +1,50 @@ -import React, { useState } from "react" - -import { Sidebar } from "../Option/Sidebar" -import { Drawer, Tooltip } from "antd" - -import { useTranslation } from "react-i18next" +import React, { useCallback, useEffect, useState } from "react" import { CurrentChatModelSettings } from "../Common/Settings/CurrentChatModelSettings" import { Header } from "./Header" -import { EraserIcon } from "lucide-react" -import { PageAssitDatabase } from "@/db" -import { useMessageOption } from "@/hooks/useMessageOption" -import { useQueryClient } from "@tanstack/react-query" -import { useStoreChatModelSettings } from "@/store/model" + +interface History { + show: boolean + setShow: (show: boolean) => void +} + +export const HistoryContext = React.createContext({ + show: true, + setShow: () => {} +}) export default function OptionLayout({ children }: { children: React.ReactNode }) { - const [sidebarOpen, setSidebarOpen] = useState(false) - const { t } = useTranslation(["option", "common", "settings"]) + const [showHistory, setShowHistory] = useState(true) const [openModelSettings, setOpenModelSettings] = useState(false) - const { - setMessages, - setHistory, - setHistoryId, - historyId, - clearChat, - setSelectedModel, - temporaryChat, - setSelectedSystemPrompt - } = useMessageOption() - const queryClient = useQueryClient() - const { setSystemPrompt } = useStoreChatModelSettings() + const historyContextValue = { + show: showHistory, + setShow: setShowHistory + } + + const useToggle = useCallback(() => { + setShowHistory(!showHistory) + }, [showHistory]) return (
{/*
*/} - {children} + + {children} + {/*
*/} - - {t("sidebarTitle")} - - - - -
- } - placement="left" - closeIcon={null} - onClose={() => setSidebarOpen(false)} - open={sidebarOpen}> - setSidebarOpen(false)} - setMessages={setMessages} - setHistory={setHistory} - setHistoryId={setHistoryId} - setSelectedModel={setSelectedModel} - setSelectedSystemPrompt={setSelectedSystemPrompt} - clearChat={clearChat} - historyId={historyId} - setSystemPrompt={setSystemPrompt} - temporaryChat={temporaryChat} - history={history} - /> - { const drop = React.useRef(null) @@ -132,26 +143,43 @@ export const Playground = () => { return (
-
- + +
+
+ +
+
+ {!isAtBottom && ( +
+ +
+ )} + +
-
- {!isAtBottom && ( -
- + {messages.length && ( +
+
+
- )} - -
+
+ + +
+
+ +
+
+ )}
) } diff --git a/src/components/Option/Playground/PlaygroundChat.tsx b/src/components/Option/Playground/PlaygroundChat.tsx index ca1c9e6..19d29bf 100644 --- a/src/components/Option/Playground/PlaygroundChat.tsx +++ b/src/components/Option/Playground/PlaygroundChat.tsx @@ -20,7 +20,7 @@ export const PlaygroundChat = () => { <>
{messages.length === 0 && ( -
+
)} diff --git a/src/components/Option/Playground/PlaygroundEmpty.tsx b/src/components/Option/Playground/PlaygroundEmpty.tsx index 0b772f6..28c4edf 100644 --- a/src/components/Option/Playground/PlaygroundEmpty.tsx +++ b/src/components/Option/Playground/PlaygroundEmpty.tsx @@ -1,130 +1,113 @@ -import { cleanUrl } from "@/libs/clean-url" -import { useStorage } from "@plasmohq/storage/hook" -import { useQuery } from "@tanstack/react-query" -import { RotateCcw } from "lucide-react" -import { useEffect, useState } from "react" -import { Trans, useTranslation } from "react-i18next" -import { - getOllamaURL, - isOllamaRunning, - setOllamaURL as saveOllamaURL -} from "~/services/ollama" +import { Card, Col, Row } from "antd" + +import RocketSvg from '@/assets/icons/rocket.svg' +import BulbSvg from '@/assets/icons/bulb.svg' +import EyeSvg from '@/assets/icons/eye.svg' +import ASvg from '@/assets/icons/a.svg' +import BSvg from '@/assets/icons/b.svg' +import CSvg from '@/assets/icons/c.svg' +import DSvg from '@/assets/icons/d.svg' +import ESvg from '@/assets/icons/e.svg' +import FSvg from '@/assets/icons/f.svg' +import { useMessageOption } from "@/hooks/useMessageOption.tsx" +import { useMutation, useQueryClient } from "@tanstack/react-query" + export const PlaygroundEmpty = () => { - const [ollamaURL, setOllamaURL] = useState("") - const { t } = useTranslation(["playground", "common"]) - - const [checkOllamaStatus] = useStorage("checkOllamaStatus", true) - const { - data: ollamaInfo, - status: ollamaStatus, - refetch, - isRefetching - } = useQuery({ - queryKey: ["ollamaStatus"], - queryFn: async () => { - const ollamaURL = await getOllamaURL() - const isOk = await isOllamaRunning() + onSubmit, + setMessages, + setHistory, + setHistoryId, + historyId, + clearChat, + setSelectedModel, + temporaryChat, + setSelectedSystemPrompt + } = useMessageOption() - if (ollamaURL) { - saveOllamaURL(ollamaURL) - } + const queryClient = useQueryClient() - return { - isOk, - ollamaURL - } + + const questions = [ + { + title: "最近一年大型语言模型的技术进展有哪些?", + icon: Rocket, }, - enabled: checkOllamaStatus + { + title: "生成式AI在企业中有哪些具体应用场景?", + icon: Rocket, + }, + { + title: "多模态学习技术的最新研究方向是什么?", + icon: Rocket, + }, + { + title: "当前AI芯片市场格局和未来三年发展趋势如何?", + icon: Rocket, + }, + { + title: "主流深度学习框架性能与易用性对比分析?", + icon: Rocket, + }, + { + title: "国内外AI伦理治理框架有哪些最佳实践?", + icon: Rocket, + }, + { + title: "大规模知识图谱构建与应用最新进展?", + icon: Rocket, + }, + { + title: "计算机视觉领域近期突破性技术有哪些?", + icon: Rocket, + }, + { + title: "量子计算对AI算法的影响与应用前景?", + icon: Rocket, + }, + ] + + const { mutateAsync: sendMessage } = useMutation({ + mutationFn: onSubmit, + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ["fetchChatHistory"] + }) + } }) - useEffect(() => { - if (ollamaInfo?.ollamaURL) { - setOllamaURL(ollamaInfo.ollamaURL) - } - }, [ollamaInfo]) - - if (!checkOllamaStatus) { - return ( -
-
-

- 👋 - - {t("welcome")} - -

-
-
- ) + function handleQuestion(message: string) { + void sendMessage({message, image: ''}) } + return ( -
-
- {(ollamaStatus === "pending" || isRefetching) && ( -
-
-

- {t("ollamaState.searching")} -

-
- )} - {!isRefetching && ollamaStatus === "success" ? ( - ollamaInfo.isOk ? ( -
-
-

- {t("ollamaState.running")} -

-
- ) : ( -
-
-
-

- {t("ollamaState.notRunning")} -

-
- - setOllamaURL(e.target.value)} - /> - - - - {ollamaURL && - cleanUrl(ollamaURL) !== "http://127.0.0.1:11434" && ( -

- - ) - }} - /> -

- )} -
- ) - ) : null} +
+ {/* 标题区域 */} +
+

数联网科创智能体

+

您好!请问有什么可以帮您?

+ + {/* 卡片网格布局 */} + + {questions.map((item, index) => ( + + handleQuestion(item.title)} + > +
+
{item.icon}
+
{item.title}
+
+
+ + ))} +
) } diff --git a/src/components/Option/Playground/PlaygroundForm.tsx b/src/components/Option/Playground/PlaygroundForm.tsx index 4a20e37..43b9818 100644 --- a/src/components/Option/Playground/PlaygroundForm.tsx +++ b/src/components/Option/Playground/PlaygroundForm.tsx @@ -207,11 +207,11 @@ export const PlaygroundForm = ({ dropedFile }: Props) => { } return ( -
+
-
+
{ setHistory, meteringEntries, setMeteringEntries, + setCurrentMeteringEntry, setStreaming, streaming, setIsFirstMessage, @@ -200,6 +201,11 @@ export const useMessageOption = () => { date: new Date().getTime() } as MeteringEntry + setCurrentMeteringEntry({ + loading: true, + data: meter + }) + if (!isRegenerate) { newMessage = [ ...messages, @@ -500,7 +506,7 @@ export const useMessageOption = () => { // Save metering entry const { cot, content } = responseResolver(fullText) - const _meteringEntries = [{ + const currentMeteringEntry = { ...meter, modelInputTokenCount: prompt.length, modelOutputTokenCount: fullText.length, @@ -511,9 +517,15 @@ export const useMessageOption = () => { cot, responseContent: content, modelResponseContent: fullText, - }, + } + const _meteringEntries = [ + currentMeteringEntry, ...meteringEntries, ] + setCurrentMeteringEntry({ + loading: false, + data: currentMeteringEntry, + }) setMeteringEntries(_meteringEntries) localStorage.setItem("meteringEntries", JSON.stringify(_meteringEntries)) } catch (e) { @@ -633,6 +645,16 @@ export const useMessageOption = () => { let newMessage: Message[] = [] let generateMessageId = generateID() + const meter: MeteringEntry = { + id: generateMessageId, + queryContent: message, + date: new Date().getTime() + } as MeteringEntry + + setCurrentMeteringEntry({ + loading: true, + data: meter, + }) if (!isRegenerate) { newMessage = [ @@ -759,6 +781,7 @@ export const useMessageOption = () => { let reasoningStartTime: Date | null = null let reasoningEndTime: Date | null = null let apiReasoning: boolean = false + const chatStartTime = new Date() for await (const chunk of chunks) { if (chunk?.additional_kwargs?.reasoning_content) { @@ -859,6 +882,31 @@ export const useMessageOption = () => { setStreaming(false) setIsProcessing(false) setStreaming(false) + + + // Save metering entry + const { cot, content } = responseResolver(fullText) + const currentMeteringEntry = { + ...meter, + modelInputTokenCount: prompt.length, + modelOutputTokenCount: fullText.length, + model: ollama.modelName ?? ollama.model, + relatedDataCount: 0, + timeTaken: new Date().getTime() - chatStartTime.getTime(), + date: chatStartTime.getTime(), + cot, + responseContent: content, + modelResponseContent: fullText, + } + const _meteringEntries = [ + currentMeteringEntry, + ...meteringEntries, + ] + setCurrentMeteringEntry({ + loading: false, + data: currentMeteringEntry, + }) + setMeteringEntries(_meteringEntries) } catch (e) { const errorSave = await saveMessageOnError({ e, diff --git a/src/store/option.tsx b/src/store/option.tsx index 9d524eb..79ec929 100644 --- a/src/store/option.tsx +++ b/src/store/option.tsx @@ -35,6 +35,8 @@ type State = { setMessages: (messages: Message[]) => void history: ChatHistory setHistory: (history: ChatHistory) => void + currentMeteringEntry: {data: MeteringEntry, loading: boolean} + setCurrentMeteringEntry: (meteringEntry: {data: MeteringEntry, loading: boolean}) => void meteringEntries: MeteringEntry[] setMeteringEntries: (meteringEntries: MeteringEntry[]) => void streaming: boolean @@ -120,6 +122,8 @@ export const useStoreMessageOption = create((set) => ({ setMessages: (messages) => set({ messages }), history: [], setHistory: (history) => set({ history }), + currentMeteringEntry: {data: {} as MeteringEntry, loading: false}, + setCurrentMeteringEntry: (currentMeteringEntry) => set({ currentMeteringEntry }), meteringEntries: JSON.parse(localStorage.getItem("meteringEntries") || JSON.stringify([])), setMeteringEntries: (meteringEntries) => set({ meteringEntries }), streaming: false, diff --git a/src/web/iod.ts b/src/web/iod.ts index bc5e64f..4a701aa 100644 --- a/src/web/iod.ts +++ b/src/web/iod.ts @@ -26,10 +26,10 @@ export const tokenizeInput = function (input: string): string[] { } //doipUrl = tcp://reg01.public.internetofdata.cn:21037 export const iodConfig = { - "gatewayUrl": "tcp://127.0.0.1:21036", - "registry":"bdware/Registry", - "localRepository":"bdtest.local/myrepo1", - "doBrowser":"http://127.0.0.1:21030/SCIDE/SCManager" + "gatewayUrl": "tcp://021.node.internetapi.cn:21052", + "registry":"data/Registry", + "localRepository":"data/Repository", + "doBrowser":"http://021.node.internetapi.cn:21030/SCIDE/SCManager" } function inGrepList(str: string){ return "什么|问题|需要|合适|设计|考虑|合作|精度|传感器|最新|研究|药物".indexOf(str)!=-1; @@ -88,7 +88,7 @@ export const makeDOIPParams = (doId:string, op:string, attributes:Object, reques attributes: attributes, body: requestBody } -}) +}) export const retrieveDoc = function(doId: string) : Promise { console.log("retriveDoc:"+doId) @@ -185,6 +185,7 @@ export async function localIodSearch( const abortController = new AbortController(); setTimeout(() => abortController.abort(), 10000); const params = makeRegSearchParams(TOTAL_SEARCH_RESULTS, keywords); + console.log('params------->',params) try { const response = await fetch(iodConfig.doBrowser, { method: "POST", @@ -194,13 +195,11 @@ export async function localIodSearch( const res = await response.json(); if (res.status !== "Success") { - console.log(res); return []; } const body = JSON.parse(res.result.body); if (body.code !== 0) { - console.log(body); return []; } @@ -363,7 +362,7 @@ export const searchIod = async (query: string, keywords: string[]) => { } } return searchResults - + /* const ollamaUrl = await getOllamaURL() diff --git a/tailwind.config.js b/tailwind.config.js index 8f13db3..0357344 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -5,6 +5,9 @@ module.exports = { content: ["./src/**/*.tsx"], theme: { extend: { + width: { + '1/10': '10%', + }, backgroundImage: { 'bottom-mask-light': 'linear-gradient(0deg, transparent 0, #ffffff 160px)', 'bottom-mask-dark': 'linear-gradient(0deg, transparent 0, #171717 160px)',