feat(iod): 重构数联网搜索功能
- 新增数联网设置页面 - 优化数联网搜索结果展示 - 添加数据集、科创场景和科技企业等不同类型的搜索结果 - 重构搜索结果卡片组件,支持加载状态和不同展示模式 - 更新数联网搜索相关的国际化文案
This commit is contained in:
parent
efbf2a3eff
commit
17020e8755
@ -316,6 +316,10 @@
|
||||
"title": "管理知识",
|
||||
"heading": "配置知识库"
|
||||
},
|
||||
"iodSettings": {
|
||||
"title": "数联网 设置",
|
||||
"heading": "配置数联网"
|
||||
},
|
||||
"rag": {
|
||||
"title": "RAG 设置",
|
||||
"ragSettings": {
|
||||
|
@ -11,6 +11,11 @@ export const PageAssistProvider = ({
|
||||
const [controller, setController] = React.useState<AbortController | null>(
|
||||
null
|
||||
)
|
||||
|
||||
const [iodLoading, setIodLoading] = React.useState<boolean>(false)
|
||||
const [currentMessageId, setCurrentMessageId] = React.useState<string>('')
|
||||
|
||||
|
||||
const [embeddingController, setEmbeddingController] =
|
||||
React.useState<AbortController | null>(null)
|
||||
|
||||
@ -20,6 +25,12 @@ export const PageAssistProvider = ({
|
||||
messages,
|
||||
setMessages,
|
||||
|
||||
iodLoading,
|
||||
setIodLoading,
|
||||
|
||||
currentMessageId,
|
||||
setCurrentMessageId,
|
||||
|
||||
controller,
|
||||
setController,
|
||||
|
||||
|
@ -1,57 +1,99 @@
|
||||
import React from "react"
|
||||
import React, { useMemo, useState } from "react"
|
||||
import { DataNavigation } from "@/components/Common/DataNavigation.tsx"
|
||||
import { Card, Drawer, List } from "antd"
|
||||
import { useCallback, useState } from "react"
|
||||
import { Card, Drawer, Skeleton } from "antd"
|
||||
import { useMessageOption } from "@/hooks/useMessageOption.tsx"
|
||||
import { IodRegistryEntry } from "@/types/iod.ts"
|
||||
|
||||
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: "热 榜 第2"
|
||||
},
|
||||
{
|
||||
title: "祁连山老虎沟大本营10米气象每日值数据集(V1.0)(2018-2023)",
|
||||
description:
|
||||
"中国科学院西北生态环境资源研究院,2021年8月3日发布,2021年8月3日20:48更新",
|
||||
time: "包括2019年8月,2021年8月和2024年6月",
|
||||
metadata: "热 榜 第2"
|
||||
},
|
||||
{
|
||||
title: "李嘉图为研究老虎沟大本营2014-2018年...",
|
||||
description:
|
||||
"中国科学院西北生态环境资源研究院,2021年8月3日发布,2021年8月3日20:48更新",
|
||||
time: "包括2019年8月,2021年8月和2024年6月",
|
||||
metadata: "热 榜 第2"
|
||||
},
|
||||
{
|
||||
title: "青海玉树B1区俄日矿勘探数据2017-2023",
|
||||
description:
|
||||
"数字中国集团,CSTR:3260.11.1528414774204895456,DT2023年地质勘探补充调查",
|
||||
time: "包括2019年8月,2021年8月和2024年6月",
|
||||
metadata: "热 榜 第2"
|
||||
}
|
||||
]
|
||||
|
||||
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 defaultData: IodRegistryEntry[] = [
|
||||
{
|
||||
name: "2019-2024年黄海清浅海域中河湖代数生物物种数据集",
|
||||
doId: "CSTR:13452.11.01.11.2021.242",
|
||||
description:
|
||||
"数字对象标识: CSTR:13452.11.01.11.2021.242 国家海洋科学数据中心"
|
||||
},
|
||||
{
|
||||
name: "祁连山老虎沟大本营10米气象每日值数据集(V1.0)(2018-2023)",
|
||||
doId: "CSTR:13452.11.01.11.2021.343",
|
||||
description: "黄海清浅海域中河湖代数生物物种数据集"
|
||||
},
|
||||
{
|
||||
name: "李嘉图为研究老虎沟大本营2014-2018年",
|
||||
doId: "CSTR:3260.11.1528414789920489545",
|
||||
description:
|
||||
"中国科学院西北生态环境资源研究院,2021年8月3日发布,2021年8月3日20:48更新"
|
||||
},
|
||||
{
|
||||
name: "青海玉树B1区俄日矿勘探数据2017-2023",
|
||||
doId: "CSTR:3260.11.152841477420489545",
|
||||
description:
|
||||
"数字中国集团,CSTR:3260.11.1528414774204895456,DT2023年地质勘探补充调查"
|
||||
}
|
||||
]
|
||||
|
||||
type ShowCardProps = {
|
||||
loading: boolean
|
||||
record: IodRegistryEntry
|
||||
truncate?: boolean
|
||||
}
|
||||
|
||||
const ShowCard: React.FC<ShowCardProps> = ({
|
||||
loading,
|
||||
record,
|
||||
truncate = true
|
||||
}) => (
|
||||
<Card className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]">
|
||||
{loading ? (
|
||||
<Skeleton title={false} active />
|
||||
) : (
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3
|
||||
className={`text-base font-medium mb-1 text-[#222222] break-all ${truncate ? "line-clamp-2" : ""}`}
|
||||
title={record.name}>
|
||||
{record.name}
|
||||
</h3>
|
||||
<p
|
||||
className={`text-sm text-[#383838] break-all ${truncate ? "line-clamp-2" : ""}`}
|
||||
title={record.doId}>
|
||||
数字对象标识:{record.doId}
|
||||
</p>
|
||||
<p
|
||||
className={`text-[#828282] text-xs break-all ${truncate ? "truncate" : ""}`}
|
||||
title={record.description}>
|
||||
{record.description}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
)
|
||||
export const PlaygroundData = () => {
|
||||
const { messages, iodLoading, currentMessageId, iodSearch } = useMessageOption()
|
||||
|
||||
const data = useMemo<IodRegistryEntry[]>(() => {
|
||||
// 确保loading状态时数据大于3
|
||||
if (iodLoading) {
|
||||
return defaultData
|
||||
}
|
||||
|
||||
if (messages.length && iodSearch) {
|
||||
const currentMessage = messages?.find(
|
||||
(message) => message.id === currentMessageId
|
||||
)
|
||||
return currentMessage?.iodSources.data.data ?? []
|
||||
}
|
||||
|
||||
return defaultData
|
||||
}, [currentMessageId, messages, iodLoading, iodSearch])
|
||||
|
||||
const title = useMemo(() => {
|
||||
return messages.length > 0 ? "推荐数据" : "热点数据"
|
||||
}, [messages])
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const showDrawer = () => {
|
||||
if (iodLoading) {
|
||||
return
|
||||
}
|
||||
setOpen(true)
|
||||
}
|
||||
|
||||
@ -67,7 +109,7 @@ export const PlaygroundData = () => {
|
||||
{/* 数据导航 */}
|
||||
<DataNavigation
|
||||
Header={
|
||||
<div className="flex items-center gap-0.5 text-[#3480e3]">
|
||||
<div className="flex items-center gap-0.5 text-[#3581e3]">
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
@ -79,9 +121,9 @@ export const PlaygroundData = () => {
|
||||
<path
|
||||
d="M877.714286 54.857143H754.285714V9.142857c0-5.028571-4.114286-9.142857-9.142857-9.142857h-64c-5.028571 0-9.142857 4.114286-9.142857 9.142857v45.714286H498.285714V9.142857c0-5.028571-4.114286-9.142857-9.142857-9.142857h-64c-5.028571 0-9.142857 4.114286-9.142857 9.142857v45.714286H292.571429c-20.228571 0-36.571429 16.342857-36.571429 36.571428v137.142858h-109.714286c-20.228571 0-36.571429 16.342857-36.571428 36.571428v722.285714c0 20.228571 16.342857 36.571429 36.571428 36.571429h585.142857c20.228571 0 36.571429-16.342857 36.571429-36.571429v-109.714285h109.714286c20.228571 0 36.571429-16.342857 36.571428-36.571429V91.428571c0-20.228571-16.342857-36.571429-36.571428-36.571428zM685.714286 941.714286H192V310.857143h249.142857v198.857143c0 25.257143 20.457143 45.714286 45.714286 45.714285h198.857143v386.285715z m0-459.428572H514.285714V310.857143h0.228572L685.714286 482.057143v0.228571z m146.285714 313.142857h-64V448L548.571429 228.571429H338.285714v-91.428572h77.714286v36.571429c0 5.028571 4.114286 9.142857 9.142857 9.142857h64c5.028571 0 9.142857-4.114286 9.142857-9.142857v-36.571429h173.714286v36.571429c0 5.028571 4.114286 9.142857 9.142857 9.142857h64c5.028571 0 9.142857-4.114286 9.142857-9.142857v-36.571429h77.714286v658.285714z"
|
||||
p-id="3573"
|
||||
fill="#3480e3"></path>
|
||||
fill="#3581e3"></path>
|
||||
</svg>
|
||||
相关数据
|
||||
{title}
|
||||
</div>
|
||||
}
|
||||
onClick={showDrawer}
|
||||
@ -89,59 +131,23 @@ export const PlaygroundData = () => {
|
||||
|
||||
{/* 数据列表 */}
|
||||
<div className="space-y-1.5 flex-1 overflow-y-auto">
|
||||
{data.slice(0, 3).map((item, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3 className="text-base font-medium mb-1 text-[#222222] line-clamp-2">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="text-sm text-[#383838] line-clamp-2">
|
||||
{item.description}
|
||||
</p>
|
||||
<p className="text-[#828282] text-xs truncate">{item.time}</p>
|
||||
{item.metadata && (
|
||||
<div>
|
||||
<span className="inline-block text-[#D90000] bg-[#eb1c1c30] h-5 leading-5 mt-1 text-xs rounded-full px-2.5">
|
||||
{item.metadata}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
{data.slice(0, 3).map((item, index) => {
|
||||
return (
|
||||
<ShowCard key={item.doId} loading={iodLoading} record={item} />
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 抽屉 */}
|
||||
<Drawer
|
||||
title="相关数据"
|
||||
title={title}
|
||||
closable={{ "aria-label": "Close Button" }}
|
||||
onClose={onClose}
|
||||
open={open}
|
||||
width="33.33%">
|
||||
<div className="grid grid-cols-1 gap-3 overflow-y-auto">
|
||||
{data.slice(0, 3).map((item, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
hoverable
|
||||
className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3 className="text-base font-medium mb-1 text-[#222222]">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="text-sm text-[#383838]">{item.description}</p>
|
||||
<p className="text-[#828282] text-xs">{item.time}</p>
|
||||
{item.metadata && (
|
||||
<div>
|
||||
<span className="inline-block text-[#D90000] bg-[#eb1c1c30] h-5 leading-5 mt-1 text-xs rounded-full px-2.5">
|
||||
{item.metadata}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
{data.map((item, index) => (
|
||||
<ShowCard key={item.doId} loading={iodLoading} record={item} truncate={false} />
|
||||
))}
|
||||
</div>
|
||||
</Drawer>
|
||||
|
@ -67,7 +67,7 @@ export const PlaygroundHistory = () => {
|
||||
return [
|
||||
{
|
||||
key: "qaPrompt",
|
||||
label: "热门搜索",
|
||||
label: "热点问题",
|
||||
type: "group" as const,
|
||||
children: qaPrompt.map((item) => {
|
||||
return {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React from "react"
|
||||
import { Button, Card } from "antd"
|
||||
import React, { useMemo } from "react"
|
||||
import { Card } from "antd"
|
||||
|
||||
// 使用 CSS-in-JS 方式
|
||||
import styled, { keyframes } from 'styled-components'
|
||||
import styled, { keyframes } from "styled-components"
|
||||
|
||||
const rotate = keyframes`
|
||||
0% {
|
||||
@ -25,20 +25,22 @@ const breathe = keyframes`
|
||||
}
|
||||
`
|
||||
|
||||
const CircleElement = styled.div<{ delay: number }>`
|
||||
const CircleElement = styled.div<{ delay: number; playing: boolean }>`
|
||||
position: absolute;
|
||||
width: 300px;
|
||||
height: 160px;
|
||||
background: #3b82f6; // blue-500
|
||||
background: #3b82f6; // blue-500
|
||||
opacity: 0.2;
|
||||
border-radius: 50%;
|
||||
top: 55%;
|
||||
left: 50%;
|
||||
animation: ${rotate} 6s linear infinite, ${breathe} 2s infinite alternate;
|
||||
animation-delay: ${props => props.delay}s;
|
||||
animation:
|
||||
${rotate} 6s linear infinite,
|
||||
${breathe} 2s infinite alternate;
|
||||
animation-delay: ${(props) => props.delay}s;
|
||||
animation-play-state: ${(props) => (props.playing ? "running" : "paused")};
|
||||
`
|
||||
|
||||
|
||||
const SuccessIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
@ -64,13 +66,13 @@ const LoadingIcon = () => {
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="8408"
|
||||
p-id="29588"
|
||||
width="18"
|
||||
height="18">
|
||||
<path
|
||||
d="M512 128C299.936 128 128 296.672 128 504.736c0 130.784 67.904 245.984 170.976 313.536l35.52-52.256C248.576 709.696 192 613.696 192 504.736c0-173.376 143.264-313.92 320-313.92s320 140.544 320 313.92c0 98.112-45.856 185.696-117.696 243.296l-73.792-72.416V864h192l-72.768-71.36C843.072 723.52 896 620.16 896 504.704 896 296.672 724.064 128 512 128z"
|
||||
fill="#52c41a"
|
||||
p-id="8409"></path>
|
||||
d="M483.712 888.064a52.437333 52.437333 0 1 1 52.48 52.352 52.394667 52.394667 0 0 1-52.48-52.352z m-235.434667-53.76a65.578667 65.578667 0 1 1 46.421334 19.242667 65.962667 65.962667 0 0 1-46.378667-19.242667z m499.584-16.597333a41.984 41.984 0 0 1 59.264-59.434667 42.282667 42.282667 0 0 1 0 59.434667 41.941333 41.941333 0 0 1-59.264 0zM112.853333 546.602667a81.92 81.92 0 1 1 81.92 81.92 81.834667 81.834667 0 0 1-81.92-81.877334z m731.008 0a33.536 33.536 0 1 1 33.493334 33.578666 33.578667 33.578667 0 0 1-33.450667-33.536zM222.208 377.6a102.4 102.4 0 1 1 72.533333 29.866667 102.869333 102.869333 0 0 1-72.533333-29.824z m536.32-53.504a26.666667 26.666667 0 1 1 18.816 7.936 26.368 26.368 0 0 1-18.773333-7.893333zM414.378667 205.184a121.642667 121.642667 0 1 1 121.813333 121.6A121.728 121.728 0 0 1 414.378667 205.226667z"
|
||||
p-id="29589"
|
||||
fill="#4284f6"></path>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@ -94,60 +96,110 @@ const SearchIcon = () => {
|
||||
}
|
||||
|
||||
export const PlaygroundIodRelevant: React.FC = () => {
|
||||
const data = [
|
||||
{
|
||||
title: <p>已在<span className="text-[#9d0000]">29个科学数据中心</span>的<span className="text-[#9d0000]">50万个科学数据集</span>中进行搜索</p>,
|
||||
description: <p>已发现<span className="text-green-700"> 4个 </span>数据集</p>,
|
||||
status: "success"
|
||||
},
|
||||
{
|
||||
title: <p>已在<span className="text-[#9d0000]">100万篇论文</span>、<span className="text-[#9d0000]">2800个科创场景</span>中进行搜索</p>,
|
||||
description: "已发现4个数据集",
|
||||
status: "success"
|
||||
},
|
||||
{
|
||||
title: <p>正在<span className="text-[#9d0000]">1000位智库专家</span>、<span className="text-[#9d0000]">12万个创新机构</span>中进行搜索</p>,
|
||||
status: "loading"
|
||||
}
|
||||
]
|
||||
const { messages, iodLoading, currentMessageId, iodSearch } =
|
||||
useMessageOption()
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
data.push({
|
||||
title: <p>正在<span className="text-[#9d0000]">1000位智库专家</span>、<span className="text-[#9d0000]">12万个创新机构</span>中进行搜索{i}</p>,
|
||||
description: "已发现4个数据集",
|
||||
status: "success"
|
||||
})
|
||||
}
|
||||
const showDescription = useMemo(() => {
|
||||
return iodSearch && messages.length > 0 && !iodLoading
|
||||
}, [iodSearch, messages, iodLoading])
|
||||
|
||||
const data = useMemo(() => {
|
||||
const currentMessage = messages?.find(
|
||||
(message) => message.id === currentMessageId
|
||||
)
|
||||
return [
|
||||
{
|
||||
title: (
|
||||
<p className="font-extrabold">
|
||||
已连接<span className="text-[#f00000]"> 11家 </span>
|
||||
国家科学数据中心的
|
||||
<span className="text-[#f00000]"> 500000+个 </span>科学数据集中
|
||||
</p>
|
||||
),
|
||||
description: showDescription ? (
|
||||
<p>
|
||||
已发现
|
||||
<span className="text-green-700">
|
||||
{" "}
|
||||
{currentMessage?.iodSources.data.total}个{" "}
|
||||
</span>
|
||||
数据集
|
||||
</p>
|
||||
) : (
|
||||
""
|
||||
)
|
||||
},
|
||||
{
|
||||
title: (
|
||||
<p className="font-extrabold">
|
||||
已连接<span className="text-[#f00000]"> 1000000+篇 </span>论文和
|
||||
<span className="text-[#f00000]"> 50000+个 </span>科创场景
|
||||
</p>
|
||||
),
|
||||
description: showDescription ? (
|
||||
<p>
|
||||
已发现
|
||||
<span className="text-green-700">
|
||||
{" "}
|
||||
{currentMessage?.iodSources.scenario.total}个{" "}
|
||||
</span>
|
||||
科创场景
|
||||
</p>
|
||||
) : (
|
||||
""
|
||||
)
|
||||
},
|
||||
{
|
||||
title: (
|
||||
<p className="font-extrabold">
|
||||
已连接<span className="text-[#f00000]"> 1000+位 </span>智库专家
|
||||
<span className="text-[#f00000]"> 763家 </span>高校和科研机构和
|
||||
<span className="text-[#f00000]"> 21000+家 </span>科技企业
|
||||
</p>
|
||||
),
|
||||
description: showDescription ? (
|
||||
<p>
|
||||
已发现
|
||||
<span className="text-green-700">
|
||||
{" "}
|
||||
{currentMessage?.iodSources.organization.total}个{" "}
|
||||
</span>
|
||||
科技企业
|
||||
</p>
|
||||
) : (
|
||||
""
|
||||
)
|
||||
}
|
||||
]
|
||||
}, [messages, iodLoading])
|
||||
|
||||
return (
|
||||
<Card
|
||||
hoverable
|
||||
variant="outlined"
|
||||
className="flex flex-col h-full [&_.ant-card-body]:h-full [&_.ant-card-body]:!p-[20px] translate-y-[-2px] !bg-[#f0f9ff]"
|
||||
>
|
||||
className="flex flex-col h-full [&_.ant-card-body]:h-full [&_.ant-card-body]:!p-[20px] translate-y-[-2px] !bg-[#f0f9ff]">
|
||||
<div className="h-full flex flex-col relative">
|
||||
{/* 花瓣效果 */}
|
||||
<div className="absolute inset-0 pointer-events-none z-0 overflow-hidden">
|
||||
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-64 h-64">
|
||||
<CircleElement delay={0} />
|
||||
<CircleElement delay={1} />
|
||||
<CircleElement delay={2} />
|
||||
<CircleElement delay={0} playing={true} />
|
||||
<CircleElement delay={1} playing={true} />
|
||||
<CircleElement delay={2} playing={true} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Header */}
|
||||
<div className="p-3">
|
||||
<h2 className="text-xl font-semibold text-[#08307f] flex justify-between items-center">
|
||||
<div className='flex items-center gap-2'>
|
||||
<h2 className="text-xl font-semibold text-[#1a3c87] flex justify-center items-center">
|
||||
<div className="flex items-center gap-2">
|
||||
<SearchIcon />
|
||||
数联网搜索相关内容
|
||||
</div>
|
||||
<button className="bg-[#2563eb1a] text-[#08307f] font-medium py-1 px-3 rounded-full text-sm hover:bg-[#2563eb1a] transition-colors float-right">
|
||||
{data.length}个结果
|
||||
</button>
|
||||
{/*<button className="bg-[#2563eb1a] text-[#08307f] font-medium py-1 px-3 rounded-full text-sm hover:bg-[#2563eb1a] transition-colors float-right">*/}
|
||||
{/* {data.length}个结果*/}
|
||||
{/*</button>*/}
|
||||
</h2>
|
||||
<p className="text-sm text-[#08307f] mt-1 align-middle">
|
||||
<p className="text-sm text-[#1a3c87] mt-1 text-center">
|
||||
下面是在数联网上进行深度搜索的相关内容
|
||||
</p>
|
||||
</div>
|
||||
@ -156,25 +208,30 @@ export const PlaygroundIodRelevant: React.FC = () => {
|
||||
<div className="space-y-2 flex-1 overflow-y-auto">
|
||||
{data.map((item, index) => (
|
||||
<Card
|
||||
className="[&>*:first-child]:!p-3 shadow-md"
|
||||
key={index}
|
||||
>
|
||||
<div className="flex items-start gap-2">
|
||||
<div className="w-5 h-5 mt-1 flex-shrink-0">
|
||||
{item.status === "success" ? (
|
||||
<SuccessIcon />
|
||||
) : (
|
||||
<LoadingIcon />
|
||||
)}
|
||||
className="[&_.ant-card-body]:!p-3 [&_.ant-card-body]:h-full shadow-md h-[88px]"
|
||||
key={index}>
|
||||
<div
|
||||
className={`flex flex-col gap-2 h-full items-start ${showDescription ? "justify-start" : "justify-center"}`}>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className={`${showDescription ? "w-5 h-5" : "w-6 h-6"}`}>
|
||||
{iodSearch && iodLoading ? (
|
||||
<LoadingIcon />
|
||||
) : (
|
||||
<SuccessIcon />
|
||||
)}
|
||||
</div>
|
||||
<p
|
||||
className={`text-gray-700 ${showDescription ? "text-sm" : "text-lg"}`}>
|
||||
{item.title}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm text-gray-700">{item.title}</p>
|
||||
{item.description && (
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
{item.description && (
|
||||
<div className="flex-1">
|
||||
<p className="text-xs text-gray-500 mt-1 pl-7">
|
||||
{item.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
|
@ -1,30 +1,31 @@
|
||||
import Markdown from "../../Common/Markdown"
|
||||
import React from "react"
|
||||
import { Tag, Image, Tooltip, Collapse, Popover } from "antd"
|
||||
import { Collapse, Image, Popover, Tag, Tooltip } from "antd"
|
||||
import { WebSearch } from "./WebSearch"
|
||||
import {
|
||||
ArrowUpSquare,
|
||||
CheckIcon,
|
||||
ClipboardIcon,
|
||||
InfoIcon,
|
||||
MessageSquareShare,
|
||||
Pen,
|
||||
PlayIcon,
|
||||
RotateCcw,
|
||||
Square,
|
||||
Star,
|
||||
ThumbsUp,
|
||||
ThumbsDown,
|
||||
MessageSquareShare,
|
||||
ArrowUpSquare
|
||||
ThumbsUp
|
||||
} from "lucide-react"
|
||||
import { EditMessageForm } from "./EditMessageForm"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { MessageSource } from "./MessageSource"
|
||||
import { useTTS } from "@/hooks/useTTS"
|
||||
import { tagColors } from "@/utils/color"
|
||||
import { removeModelSuffix } from "@/db/models"
|
||||
import { GenerationInfo } from "./GenerationInfo"
|
||||
import { parseReasoning } from "@/libs/reasoning"
|
||||
import { humanizeMilliseconds } from "@/utils/humanize-milliseconds"
|
||||
import { AllIodRegistryEntry } from "@/types/iod.ts"
|
||||
|
||||
type Props = {
|
||||
message: string
|
||||
message_type?: string
|
||||
@ -42,7 +43,7 @@ type Props = {
|
||||
webSearch?: {}
|
||||
isSearchingInternet?: boolean
|
||||
webSources?: any[]
|
||||
iodSources?: any[]
|
||||
iodSources?: AllIodRegistryEntry
|
||||
hideEditAndRegenerate?: boolean
|
||||
onSourceClick?: (source: any) => void
|
||||
isTTSEnabled?: boolean
|
||||
@ -60,7 +61,8 @@ export const PlaygroundMessage = (props: Props) => {
|
||||
return (
|
||||
<div className="group relative flex w-full flex-col items-end justify-center pb-2 md:px-4 text-gray-800 dark:text-gray-100">
|
||||
{/* <div className="text-base md:max-w-2xl lg:max-w-xl xl:max-w-3xl flex lg:px-0 m-auto w-full"> */}
|
||||
<div className={`flex flex-row gap-1 md:gap-1 my-2 m-auto w-full ${props.isBot ? "" : "flex-row-reverse"}`}>
|
||||
<div
|
||||
className={`flex flex-row gap-1 md:gap-1 my-2 m-auto w-full ${props.isBot ? "" : "flex-row-reverse"}`}>
|
||||
<div className="w-8 flex flex-col relative items-center justify-center bottom-[8px]">
|
||||
<div className="relative h-7 w-7 p-1 rounded-sm text-white flex items-center justify-center text-opacity-100r">
|
||||
{props.isBot ? (
|
||||
@ -77,8 +79,7 @@ export const PlaygroundMessage = (props: Props) => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-[calc(100%-50px)] flex-col gap-2 lg:w-[calc(100%-115px)]">
|
||||
<span className="text-xs font-bold text-gray-800 dark:text-white">
|
||||
</span>
|
||||
<span className="text-xs font-bold text-gray-800 dark:text-white"></span>
|
||||
|
||||
{props.isBot &&
|
||||
props.isSearchingInternet &&
|
||||
@ -101,6 +102,7 @@ export const PlaygroundMessage = (props: Props) => {
|
||||
return (
|
||||
<Collapse
|
||||
key={i}
|
||||
defaultActiveKey={['reasoning']}
|
||||
className="border-none !mb-3"
|
||||
items={[
|
||||
{
|
||||
@ -132,8 +134,7 @@ export const PlaygroundMessage = (props: Props) => {
|
||||
) : (
|
||||
<p
|
||||
className={`prose-lg dark:prose-invert whitespace-pre-line prose-p:leading-relaxed prose-pre:p-0 dark:prose-dark ${
|
||||
props.message_type &&
|
||||
"italic dark:text-gray-400"
|
||||
props.message_type && "italic dark:text-gray-400"
|
||||
} flex flex-row-reverse`}>
|
||||
{props.message}
|
||||
{/*<span className="bg-[#0000000a] inline-block py-[9px] text-[#000000d9] rounded-xl px-4 font-light text-sm">{props.message}</span>*/}
|
||||
@ -170,9 +171,10 @@ export const PlaygroundMessage = (props: Props) => {
|
||||
<Collapse
|
||||
className="mt-6"
|
||||
ghost
|
||||
// defaultActiveKey={['webSources']}
|
||||
items={[
|
||||
{
|
||||
key: "1",
|
||||
key: "webSources",
|
||||
label: (
|
||||
<div className="italic text-gray-500 dark:text-gray-400">
|
||||
{t("webCitations")}
|
||||
@ -194,13 +196,14 @@ export const PlaygroundMessage = (props: Props) => {
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
{props.isBot && props?.iodSources && props?.iodSources.length > 0 && (
|
||||
{props.isBot && props?.iodSources && Object.values(props?.iodSources).map(item => item.data).flat().length > 0 && (
|
||||
<Collapse
|
||||
className="mt-6"
|
||||
ghost
|
||||
// defaultActiveKey={['iod']}
|
||||
items={[
|
||||
{
|
||||
key: "1",
|
||||
key: "iod",
|
||||
label: (
|
||||
<div className="italic text-gray-500 dark:text-gray-400">
|
||||
{t("iodCitations")}
|
||||
@ -208,7 +211,7 @@ export const PlaygroundMessage = (props: Props) => {
|
||||
),
|
||||
children: (
|
||||
<div className="mb-3 flex flex-wrap gap-2">
|
||||
{props?.iodSources?.map((source, index) => (
|
||||
{Object.values(props?.iodSources).map(item => item.data).flat()?.map((source, index) => (
|
||||
<MessageSource
|
||||
onSourceClick={props.onSourceClick}
|
||||
key={index}
|
||||
@ -315,7 +318,7 @@ export const PlaygroundMessage = (props: Props) => {
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
{ (
|
||||
{
|
||||
<Tooltip title="收藏">
|
||||
<button
|
||||
aria-label="收藏"
|
||||
@ -323,8 +326,8 @@ export const PlaygroundMessage = (props: Props) => {
|
||||
<Star className="w-3 h-3 text-gray-400 group-hover:text-gray-500" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
{ (
|
||||
}
|
||||
{
|
||||
<Tooltip title="发布语用">
|
||||
<button
|
||||
aria-label="发布语用"
|
||||
@ -332,8 +335,8 @@ export const PlaygroundMessage = (props: Props) => {
|
||||
<ArrowUpSquare className="w-3 h-3 text-gray-400 group-hover:text-gray-500" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
{ (
|
||||
}
|
||||
{
|
||||
<Tooltip title="发布对话">
|
||||
<button
|
||||
aria-label="发布对话"
|
||||
@ -341,8 +344,8 @@ export const PlaygroundMessage = (props: Props) => {
|
||||
<MessageSquareShare className="w-3 h-3 text-gray-400 group-hover:text-gray-500" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
{ (
|
||||
}
|
||||
{
|
||||
<Tooltip title="点赞">
|
||||
<button
|
||||
aria-label="点赞"
|
||||
@ -350,8 +353,8 @@ export const PlaygroundMessage = (props: Props) => {
|
||||
<ThumbsUp className="w-3 h-3 text-gray-400 group-hover:text-gray-500" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
{ (
|
||||
}
|
||||
{
|
||||
<Tooltip title="点踩">
|
||||
<button
|
||||
aria-label="点踩"
|
||||
@ -359,7 +362,7 @@ export const PlaygroundMessage = (props: Props) => {
|
||||
<ThumbsDown className="w-3 h-3 text-gray-400 group-hover:text-gray-500" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
}
|
||||
</div>
|
||||
) : (
|
||||
// add invisible div to prevent layout shift
|
||||
|
@ -1,44 +1,98 @@
|
||||
import React, { useState } from "react"
|
||||
import React, { useMemo, useState } from "react"
|
||||
import { DataNavigation } from "@/components/Common/DataNavigation.tsx"
|
||||
import { Card, Drawer, List } from "antd"
|
||||
import { Card, Drawer, Skeleton } from "antd"
|
||||
import { IodRegistryEntry } from "@/types/iod.ts"
|
||||
|
||||
const defaultData: IodRegistryEntry[] = [
|
||||
{
|
||||
name: "绿色化工工艺项目",
|
||||
description:
|
||||
"基于生物基原料,采用repeal2.0可降解材料技术,开发新型环保材料。",
|
||||
doId: "CSTR:13552.11.01.61.2021.742"
|
||||
},
|
||||
{
|
||||
name: "智能农业解决方案",
|
||||
description: "利用物联网技术,实现精准农业管理,提高农作物产量。",
|
||||
doId: "CSTR:14542.11.01.61.2031.528"
|
||||
},
|
||||
{
|
||||
name: "新能源汽车电池技术",
|
||||
description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。",
|
||||
doId: "CSTR:147842.11.04.91.2031.680"
|
||||
},
|
||||
{
|
||||
name: "碳捕集与封存技术",
|
||||
description: "开发高效的碳捕集技术,减少工业排放,助力碳中和目标。",
|
||||
doId: "CSTR:14242.19.11.61.2131.428"
|
||||
}
|
||||
]
|
||||
|
||||
type ShowCardProps = {
|
||||
loading: boolean
|
||||
record: IodRegistryEntry
|
||||
truncate?: boolean
|
||||
}
|
||||
|
||||
const ShowCard: React.FC<ShowCardProps> = ({
|
||||
loading,
|
||||
record,
|
||||
truncate = true
|
||||
}) => (
|
||||
<Card className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]">
|
||||
{loading ? (
|
||||
<Skeleton title={false} active />
|
||||
) : (
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3
|
||||
className={`text-base font-medium mb-1 text-[#222222] break-all ${truncate ? "line-clamp-2" : ""}`}
|
||||
title={record.name}>
|
||||
{record.name}
|
||||
</h3>
|
||||
<p
|
||||
className={`text-sm text-[#383838] break-all ${truncate ? "line-clamp-2" : ""}`}
|
||||
title={record.doId}>
|
||||
数字对象标识:{record.doId}
|
||||
</p>
|
||||
<p
|
||||
className={`text-[#828282] text-xs break-all ${truncate ? "truncate" : ""}`}
|
||||
title={record.description}>
|
||||
{record.description}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
)
|
||||
|
||||
export const PlaygroundScene = () => {
|
||||
// 模拟数据
|
||||
const data = [
|
||||
{
|
||||
title: "绿色化工工艺项目",
|
||||
description:
|
||||
"基于生物基原料,采用repeal2.0可降解材料技术,开发新型环保材料。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
},
|
||||
{
|
||||
title: "智能农业解决方案",
|
||||
description: "利用物联网技术,实现精准农业管理,提高农作物产量。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
},
|
||||
{
|
||||
title: "新能源汽车电池技术",
|
||||
description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
},
|
||||
{
|
||||
title: "碳捕集与封存技术",
|
||||
description: "开发高效的碳捕集技术,减少工业排放,助力碳中和目标。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
}
|
||||
]
|
||||
const { messages, iodLoading, currentMessageId, iodSearch } =
|
||||
useMessageOption()
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
data.push({
|
||||
title: "开发新型电池材料",
|
||||
description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
})
|
||||
}
|
||||
const data = useMemo<IodRegistryEntry[]>(() => {
|
||||
// 确保loading状态时数据大于3
|
||||
if (iodLoading) {
|
||||
return defaultData
|
||||
}
|
||||
|
||||
if (messages.length && iodSearch) {
|
||||
const currentMessage = messages?.find(
|
||||
(message) => message.id === currentMessageId
|
||||
)
|
||||
return currentMessage?.iodSources.scenario.data ?? []
|
||||
}
|
||||
|
||||
return defaultData
|
||||
}, [currentMessageId, messages, iodLoading])
|
||||
|
||||
const title = useMemo(() => {
|
||||
return messages.length > 0 ? "推荐场景" : "热点场景"
|
||||
}, [messages])
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const showDrawer = () => {
|
||||
if (iodLoading) {
|
||||
return
|
||||
}
|
||||
setOpen(true)
|
||||
}
|
||||
|
||||
@ -54,7 +108,7 @@ export const PlaygroundScene = () => {
|
||||
{/* 数据导航 */}
|
||||
<DataNavigation
|
||||
Header={
|
||||
<div className="flex items-center text-[#52c41a] gap-1">
|
||||
<div className="flex items-center text-[#54c41d] gap-1">
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1025 1024"
|
||||
@ -65,76 +119,39 @@ export const PlaygroundScene = () => {
|
||||
height="18">
|
||||
<path
|
||||
d="M980.34571 1.143792c-4.850903 0-9.824354 0.888481-14.797806 2.930966L229.773215 299.724504H20.428686c-11.233669 0-20.424853 9.446494-20.424853 21.180572V702.584302c0 11.74429 9.191184 21.180572 20.424853 21.180573h129.820365c-4.728353 14.808018-7.271248 30.51473-7.271248 46.46654 0 84.119757 68.678568 152.543014 153.176184 152.543014 70.721053 0 130.330986-47.998404 147.93721-112.847312l521.569043 209.59984c4.983664 1.919936 9.957116 2.930966 14.808019 2.930967 21.568645 0 40.839493-18.127057 40.839493-42.371358V43.525362C1021.195415 19.270849 1002.047116 1.143792 980.34571 1.143792zM296.153987 831.250663c-33.833769 0-61.274559-27.308028-61.274558-61.009035 0-14.297397 4.983664-27.951411 14.042086-38.807221l108.374269 43.525362c-2.553107 31.403211-28.972654 56.290895-61.141797 56.290894z m633.12959 74.550713L263.984844 638.501326l-16.462431-6.638077H91.915671V391.626129h155.606742l16.462431-6.638077 665.298733-267.30005v788.113374z m0 0"
|
||||
fill="#52c41a"
|
||||
fill="#54c41d"
|
||||
p-id="6236"></path>
|
||||
</svg>
|
||||
相关场景
|
||||
{title}
|
||||
</div>
|
||||
}
|
||||
onClick={showDrawer}
|
||||
/>
|
||||
|
||||
{/* 场景列表 */}
|
||||
{/* 数据列表 */}
|
||||
<div className="space-y-1.5 flex-1 overflow-y-auto">
|
||||
{data.slice(0, 3).map((item, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3 className="text-base font-medium mb-1 text-[#222222] line-clamp-2">
|
||||
{item.title}
|
||||
</h3>
|
||||
<span className="text-sm text-[#383838] line-clamp-2">
|
||||
{item.description}
|
||||
</span>
|
||||
<p className="text-[#828282] text-xs truncate">
|
||||
{item.demander}
|
||||
</p>
|
||||
<p className="flex items-center gap-1.5">
|
||||
<span className="inline-block text-[#003AD4] bg-[#003ad430] h-5 leading-5 mt-1 text-xs rounded-full px-2.5">
|
||||
技术应用
|
||||
</span>
|
||||
<span className="inline-block text-[#00BF68] bg-[#00bf6830] h-5 leading-5 mt-1 text-xs rounded-full px-2.5">
|
||||
制造业
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
{data.slice(0, 3).map((item, index) => {
|
||||
return (
|
||||
<ShowCard key={item.doId} loading={iodLoading} record={item} />
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 抽屉 */}
|
||||
<Drawer
|
||||
title="相关场景"
|
||||
title={title}
|
||||
closable={{ "aria-label": "Close Button" }}
|
||||
onClose={onClose}
|
||||
open={open}
|
||||
width="33.33%">
|
||||
<div className="grid grid-cols-1 gap-3 overflow-y-auto">
|
||||
{data.slice(0, 3).map((item, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
hoverable
|
||||
className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3 className="text-base font-medium mb-1 text-[#222222]">
|
||||
{item.title}
|
||||
</h3>
|
||||
<span className="text-sm text-[#383838]">
|
||||
{item.description}
|
||||
</span>
|
||||
<p className="text-[#828282] text-xs">{item.demander}</p>
|
||||
<p className="flex items-center gap-1.5">
|
||||
<span className="inline-block text-[#003AD4] bg-[#003ad430] h-5 leading-5 mt-1 text-xs rounded-full px-2.5">
|
||||
技术应用
|
||||
</span>
|
||||
<span className="inline-block text-[#00BF68] bg-[#00bf6830] h-5 leading-5 mt-1 text-xs rounded-full px-2.5">
|
||||
制造业
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
{data.map((item, index) => (
|
||||
<ShowCard
|
||||
key={item.doId}
|
||||
loading={iodLoading}
|
||||
record={item}
|
||||
truncate={false}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Drawer>
|
||||
|
@ -1,40 +1,85 @@
|
||||
import React, { useState } from "react"
|
||||
import React, { useMemo, useState } from "react"
|
||||
import { DataNavigation } from "@/components/Common/DataNavigation.tsx"
|
||||
import { Card, Drawer, List } from "antd"
|
||||
import { Card, Drawer, Skeleton } from "antd"
|
||||
import { IodRegistryEntry } from "@/types/iod.ts"
|
||||
|
||||
type ShowCardProps = {
|
||||
loading: boolean
|
||||
record: IodRegistryEntry
|
||||
truncate?: boolean
|
||||
}
|
||||
|
||||
const ShowCard: React.FC<ShowCardProps> = ({
|
||||
loading,
|
||||
record,
|
||||
truncate = true
|
||||
}) => (
|
||||
<Card className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]">
|
||||
{loading ? (
|
||||
<Skeleton title={false} active />
|
||||
) : (
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3
|
||||
className={`text-base font-medium mb-1 text-[#222222] break-all ${truncate ? "line-clamp-2" : ""}`}
|
||||
title={record.name}>
|
||||
{record.name}
|
||||
</h3>
|
||||
<p
|
||||
className={`text-sm text-[#383838] break-all ${truncate ? "line-clamp-2" : ""}`}
|
||||
title={record.doId}>
|
||||
数字对象标识:{record.doId}
|
||||
</p>
|
||||
<p
|
||||
className={`text-[#828282] text-xs break-all ${truncate ? "truncate" : ""}`}
|
||||
title={record.description}>
|
||||
{record.description}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
)
|
||||
|
||||
const defaultData:IodRegistryEntry[] = [
|
||||
{
|
||||
name: "上海芯飞睿科技有限公司",
|
||||
description:
|
||||
"上海芯飞睿科技有限公司专业从事集成化激光材料与微型化激光器件的研发",
|
||||
doId: "CSTR:15552.13.05.61.2022.783"
|
||||
},
|
||||
{
|
||||
name: "长三角先进材料研究院",
|
||||
description: "由江苏省人民政府联合中国科学院、中国钢研科技集团和中国",
|
||||
doId: "CSTR:15552.12.01.11.2021.528"
|
||||
},
|
||||
{
|
||||
name: "清华大学智能系统实验室",
|
||||
description: "清华大学",
|
||||
doId: "CSTR:15552.13.04.91.2021.614",
|
||||
},
|
||||
]
|
||||
|
||||
export const PlaygroundTeam = () => {
|
||||
// 模拟数据
|
||||
const data = [
|
||||
{
|
||||
title: "绿色化工工艺项目",
|
||||
description:
|
||||
"基于生物基原料,采用repeal2.0可降解材料技术,开发新型环保材料。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
},
|
||||
{
|
||||
title: "智能农业解决方案",
|
||||
description: "利用物联网技术,实现精准农业管理,提高农作物产量。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
},
|
||||
{
|
||||
title: "新能源汽车电池技术",
|
||||
description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
},
|
||||
{
|
||||
title: "碳捕集与封存技术",
|
||||
description: "开发高效的碳捕集技术,减少工业排放,助力碳中和目标。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
}
|
||||
]
|
||||
const { messages, iodLoading, currentMessageId, iodSearch } = useMessageOption()
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
data.push({
|
||||
title: "开发新型电池材料",
|
||||
description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
})
|
||||
}
|
||||
const data = useMemo<IodRegistryEntry[]>(() => {
|
||||
// 确保loading状态时数据大于3
|
||||
if (iodLoading) {
|
||||
return defaultData
|
||||
}
|
||||
|
||||
if (messages.length && iodSearch) {
|
||||
const currentMessage = messages?.find(
|
||||
(message) => message.id === currentMessageId
|
||||
)
|
||||
return currentMessage?.iodSources.organization.data ?? []
|
||||
}
|
||||
|
||||
return defaultData
|
||||
}, [currentMessageId, messages, iodLoading])
|
||||
|
||||
const title = useMemo(() => {
|
||||
return messages.length > 0 ? "推荐团队" : "热点团队"
|
||||
}, [messages])
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
@ -72,7 +117,7 @@ export const PlaygroundTeam = () => {
|
||||
p-id="7274"
|
||||
fill="#BE0BAC"></path>
|
||||
</svg>
|
||||
相关团队
|
||||
{title}
|
||||
</div>
|
||||
}
|
||||
onClick={showDrawer}
|
||||
@ -80,59 +125,27 @@ export const PlaygroundTeam = () => {
|
||||
|
||||
{/* 场景列表 */}
|
||||
<div className="grid grid-cols-3 gap-3 flex-1 overflow-y-auto">
|
||||
{data.slice(0, 5).map((item, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3 className="text-base font-medium mb-1 text-[#222222] line-clamp-2">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="text-[#828282] text-xs truncate">
|
||||
{item.description}
|
||||
</p>
|
||||
<p className="flex items-center gap-1.5">
|
||||
<span className="inline-block text-[#BE0BAC] bg-[#be0bac30] h-5 leading-5 mt-1 text-xs rounded-full px-2.5">
|
||||
晶体材料
|
||||
</span>
|
||||
<span className="inline-block text-[#EB1C1C] bg-[#eb1c1c30] h-5 leading-5 mt-1 text-xs rounded-full px-2.5">
|
||||
中国
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
{data.slice(0, 3).map((item, index) => (
|
||||
<ShowCard key={item.doId} loading={iodLoading} record={item} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 抽屉 */}
|
||||
<Drawer
|
||||
title="相关团队"
|
||||
title={title}
|
||||
closable={{ "aria-label": "Close Button" }}
|
||||
onClose={onClose}
|
||||
open={open}
|
||||
width="33.33%">
|
||||
<div className="grid grid-cols-1 gap-3 overflow-y-auto">
|
||||
{data.slice(0, 5).map((item, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
hoverable
|
||||
className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3 className="text-base font-medium mb-1 text-[#222222]">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="text-[#828282] text-xs">{item.description}</p>
|
||||
<p className="flex items-center gap-1.5">
|
||||
<span className="inline-block text-[#BE0BAC] bg-[#be0bac30] h-5 leading-5 mt-1 text-xs rounded-full px-2.5">
|
||||
晶体材料
|
||||
</span>
|
||||
<span className="inline-block text-[#EB1C1C] bg-[#eb1c1c30] h-5 leading-5 mt-1 text-xs rounded-full px-2.5">
|
||||
中国
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
{data.map((item, index) => (
|
||||
<ShowCard
|
||||
key={item.doId}
|
||||
loading={iodLoading}
|
||||
record={item}
|
||||
truncate={false}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Drawer>
|
||||
|
33
src/components/Icons/Iod.tsx
Normal file
33
src/components/Icons/Iod.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import React from "react"
|
||||
|
||||
export const IodIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1088 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
p-id="32289"
|
||||
{...props}
|
||||
ref={ref}
|
||||
>
|
||||
<path
|
||||
d="M853.333333 458.666667h21.333334v21.333333h-21.333334zM680.533333 217.6h21.333334v21.333333h-21.333334zM740.266667 264.533333h21.333333v21.333334h-21.333333zM629.333333 177.066667h21.333334v21.333333h-21.333334zM398.933333 177.066667h21.333334v21.333333h-21.333334zM343.466667 217.6h21.333333v21.333333h-21.333333zM285.866667 264.533333h21.333333v21.333334h-21.333333zM174.933333 458.666667h21.333334v21.333333h-21.333334zM174.933333 520.533333h21.333334v21.333334h-21.333334zM174.933333 576h21.333334v21.333333h-21.333334zM292.266667 759.466667h21.333333v21.333333h-21.333333zM347.733333 800h21.333334v21.333333h-21.333334zM403.2 846.933333h21.333333v21.333334h-21.333333zM629.333333 846.933333h21.333334v21.333334h-21.333334zM454.4 514.133333h21.333333v21.333334h-21.333333zM512 514.133333h21.333333v21.333334h-21.333333zM567.466667 514.133333h21.333333v21.333334h-21.333333zM680.533333 800h21.333334v21.333333h-21.333334zM740.266667 759.466667h21.333333v21.333333h-21.333333zM853.333333 520.533333h21.333334v21.333334h-21.333334zM853.333333 576h21.333334v21.333333h-21.333334zM509.866667 189.866667h21.333333v147.2h-21.333333zM509.866667 708.266667h21.333333v147.2h-21.333333zM225.92 672.170667l127.488-73.6 10.666667 18.474666-127.488 73.6zM223.509333 375.125333l10.666667-18.474666 127.466667 73.6-10.666667 18.474666zM677.312 422.186667l132.309333-64.554667 9.344 19.2-132.309333 64.512zM684.544 618.517333l10.986667-18.282666 126.186666 75.797333-10.986666 18.304z"
|
||||
p-id="32290"></path>
|
||||
<path
|
||||
d="M520.533333 590.933333c-93.866667 0-189.866667-23.466667-189.866666-66.133333s96-66.133333 189.866666-66.133333 189.866667 23.466667 189.866667 66.133333-93.866667 66.133333-189.866667 66.133333z m0-110.933333c-102.4 0-168.533333 27.733333-168.533333 44.8s66.133333 44.8 168.533333 44.8c102.4 0 168.533333-27.733333 168.533334-44.8s-64-44.8-168.533334-44.8z"
|
||||
p-id="32291"></path>
|
||||
<path
|
||||
d="M520.533333 714.666667c-36.266667 0-57.6-68.266667-64-130.133334l21.333334-2.133333c8.533333 76.8 29.866667 110.933333 42.666666 110.933333 12.8 0 34.133333-34.133333 42.666667-113.066666l21.333333 2.133333c-6.4 64-25.6 132.266667-64 132.266667z m44.8-243.2c-8.533333-78.933333-29.866667-115.2-42.666666-115.2-12.8 0-34.133333 36.266667-42.666667 113.066666l-21.333333-2.133333c6.4-64 27.733333-132.266667 64-132.266667 38.4 0 57.6 70.4 64 134.4l-21.333334 2.133334zM520.533333 209.066667c-36.266667 0-66.133333-29.866667-66.133333-66.133334 0-36.266667 29.866667-66.133333 66.133333-66.133333 36.266667 0 66.133333 29.866667 66.133334 66.133333 2.133333 36.266667-27.733333 66.133333-66.133334 66.133334z m0-113.066667c-25.6 0-44.8 21.333333-44.8 44.8 0 25.6 21.333333 44.8 44.8 44.8 25.6 0 44.8-21.333333 44.8-44.8 2.133333-23.466667-19.2-44.8-44.8-44.8zM857.6 407.466667c-36.266667 0-66.133333-29.866667-66.133333-66.133334 0-36.266667 29.866667-66.133333 66.133333-66.133333s66.133333 29.866667 66.133333 66.133333c2.133333 36.266667-27.733333 66.133333-66.133333 66.133334z m0-113.066667c-25.6 0-44.8 21.333333-44.8 44.8 0 25.6 21.333333 44.8 44.8 44.8s44.8-21.333333 44.8-44.8c2.133333-23.466667-19.2-44.8-44.8-44.8zM857.6 776.533333c-36.266667 0-66.133333-29.866667-66.133333-66.133333 0-36.266667 29.866667-66.133333 66.133333-66.133333s66.133333 29.866667 66.133333 66.133333c2.133333 34.133333-27.733333 66.133333-66.133333 66.133333z m0-113.066666c-25.6 0-44.8 21.333333-44.8 44.8s21.333333 44.8 44.8 44.8 44.8-21.333333 44.8-44.8-19.2-44.8-44.8-44.8zM520.533333 974.933333c-36.266667 0-66.133333-29.866667-66.133333-66.133333 0-36.266667 29.866667-66.133333 66.133333-66.133333 36.266667 0 66.133333 29.866667 66.133334 66.133333 2.133333 36.266667-27.733333 66.133333-66.133334 66.133333z m0-113.066666c-25.6 0-44.8 21.333333-44.8 44.8s21.333333 44.8 44.8 44.8c25.6 0 44.8-21.333333 44.8-44.8s-19.2-44.8-44.8-44.8zM183.466667 407.466667c-36.266667 0-66.133333-29.866667-66.133334-66.133334 0-36.266667 29.866667-66.133333 66.133334-66.133333 36.266667 0 66.133333 29.866667 66.133333 66.133333 2.133333 36.266667-27.733333 66.133333-66.133333 66.133334z m0-113.066667c-25.6 0-44.8 21.333333-44.8 44.8 0 25.6 21.333333 44.8 44.8 44.8 25.6 0 44.8-21.333333 44.8-44.8 2.133333-23.466667-19.2-44.8-44.8-44.8zM183.466667 776.533333c-36.266667 0-66.133333-29.866667-66.133334-66.133333 0-36.266667 29.866667-66.133333 66.133334-66.133333 36.266667 0 66.133333 29.866667 66.133333 66.133333 2.133333 34.133333-27.733333 66.133333-66.133333 66.133333z m0-113.066666c-25.6 0-44.8 21.333333-44.8 44.8s21.333333 44.8 44.8 44.8c25.6 0 44.8-21.333333 44.8-44.8s-19.2-44.8-44.8-44.8z"
|
||||
p-id="32292"></path>
|
||||
<path
|
||||
d="M514.133333 731.733333c-117.333333 0-215.466667-96-215.466666-215.466666 0-117.333333 96-215.466667 215.466666-215.466667 117.333333 0 215.466667 96 215.466667 215.466667s-96 215.466667-215.466667 215.466666z m0-386.133333c-93.866667 0-172.8 76.8-172.8 172.8s76.8 172.8 172.8 172.8c93.866667 0 172.8-76.8 172.8-172.8s-76.8-172.8-172.8-172.8z"
|
||||
p-id="32293"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
@ -1,22 +1,58 @@
|
||||
import React, { useContext } from "react"
|
||||
import React, { useContext, useMemo } from "react"
|
||||
import { HistoryContext } from "@/components/Layouts/Layout.tsx"
|
||||
import { PanelLeftIcon } from "lucide-react"
|
||||
import { Button } from "antd"
|
||||
import { Button, Tooltip } from "antd"
|
||||
import { PlusOutlined } from "@ant-design/icons"
|
||||
import { useMessageOption } from "@/hooks/useMessageOption.tsx"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { NavLink, useLocation } from "react-router-dom"
|
||||
|
||||
interface SettingIconProps {}
|
||||
const SettingIcon: React.FC<SettingIconProps> = () => {
|
||||
return (
|
||||
<svg
|
||||
// @ts-ignore
|
||||
t="1755767517454"
|
||||
className="icon"
|
||||
viewBox="0 0 1084 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="10420"
|
||||
width="20"
|
||||
height="20">
|
||||
<path
|
||||
d="M1072.147851 406.226367c-6.331285-33.456782-26.762037-55.073399-52.047135-55.073399-0.323417 0-0.651455 0.003081-0.830105 0.009241l-4.655674 0c-73.124722 0-132.618162-59.491899-132.618162-132.618162 0-23.731152 11.447443-50.336101 11.546009-50.565574 13.104573-29.498767 3.023185-65.672257-23.427755-84.127081l-1.601687-1.127342-134.400039-74.661726-1.700252-0.745401c-8.753836-3.805547-18.334698-5.735272-28.479231-5.735272-20.789593 0-41.235746 8.344174-54.683758 22.306575-14.741683 15.216028-65.622973 58.649474-104.721083 58.649474-39.450789 0-90.633935-44.286652-105.438762-59.784516-13.518857-14.247316-34.128258-22.753199-55.127302-22.753199-9.945862 0-19.354234 1.861961-27.958682 5.531982l-1.746455 0.74078-139.141957 76.431283-1.643269 1.139662c-26.537186 18.437884-36.675557 54.579032-23.584845 84.062398 0.115506 0.264895 11.579891 26.725075 11.579891 50.634877 0 73.126262-59.491899 132.618162-132.618162 132.618162l-4.581749 0c-0.318797-0.00616-0.636055-0.01078-0.951772-0.01078-25.260456 0-45.672728 21.618157-52.002472 55.0811-0.462025 2.453354-11.313456 60.622322-11.313456 106.117939 0 45.494078 10.85143 103.659965 11.314996 106.119479 6.334365 33.458322 26.758957 55.076479 52.036353 55.076479 0.320337 0 0.651455-0.00616 0.842426-0.012321l4.655674 0c73.126262 0 132.618162 59.491899 132.618162 132.616622 0 23.760413-11.444363 50.333021-11.546009 50.565574-13.093793 29.474125-3.041666 65.646075 23.395414 84.151722l1.569346 1.093459 131.838879 73.726895 1.675611 0.7377c8.750757 3.84251 18.305437 5.790715 28.397607 5.790715 21.082208 0 41.676209-8.706094 55.0888-23.290689 18.724339-20.347588 69.527086-62.362616 107.04815-62.362616 40.625872 0 92.72537 47.100385 107.759669 63.583903 13.441852 14.831008 34.176001 23.689571 55.470741 23.695731l0.00616 0c9.895039 0 19.27877-1.883523 27.893999-5.598205l1.711034-0.73924 136.659342-75.531873 1.617088-1.128882c26.492523-18.456365 36.601633-54.600594 23.538642-84.016195-0.115506-0.267974-11.595291-27.082374-11.595291-50.67646 0-73.124722 59.49344-132.616622 132.618162-132.616622l4.517066-0.00154c0.300316 0.00616 0.599092 0.009241 0.899409 0.009241 25.331299-0.00154 45.785153-21.619697 52.107197-55.054918 0.112426-0.589852 11.325776-59.507301 11.325776-106.14104C1083.464388 466.640776 1072.609877 408.67356 1072.147851 406.226367zM377.486862 945.656142l-115.32764-64.487932c5.082277-13.052211 15.437801-43.51815 15.437801-75.017486 0-109.382917-84.176364-199.816642-192.587488-208.134635-2.647404-15.427021-8.873963-54.967133-8.873963-85.667166 0-30.65691 6.223479-70.232445 8.869343-85.671786 108.415744-8.311832 192.592108-98.745557 192.592108-208.134635 0-31.416171-10.300081-61.797405-15.371577-74.854236l122.721583-67.40331c0.003081 0 0.00462 0.00154 0.007701 0.00154 4.423121 4.518606 22.121764 22.080182 46.558275 39.493911 39.929754 28.46229 77.952885 42.894416 113.014434 42.894416 34.716571 0 72.437845-14.151831 112.115025-42.06431 24.282503-17.07953 41.896442-34.302288 46.308782-38.74543 0.009241-0.00154 0.018481-0.00462 0.026182-0.00616l118.301542 65.726159c-5.077657 13.055291-15.416239 43.499669-15.416239 74.958962 0 109.389077 84.174824 199.822802 192.590568 208.134635 2.645865 15.462442 8.872423 55.107281 8.872423 85.671786 0 30.687711-6.223479 70.241685-8.869343 85.673326C890.042174 606.334084 805.86427 696.767809 805.86427 806.158426c0 31.450053 10.317022 61.851309 15.393138 74.903519l-119.783103 66.198965c-5.168521-5.490399-22.603811-23.363073-46.740005-41.288109-40.701336-30.224145-79.662378-45.549521-115.800446-45.549521-35.79155 0-74.458435 15.038919-114.927219 44.694774C400.22004 922.554885 382.666163 940.255068 377.486862 945.656142zM731.271848 511.646647c0-105.803762-86.081448-191.88059-191.888289-191.88059-105.803762 0-191.88059 86.076827-191.88059 191.88059 0 105.803762 86.076827 191.882129 191.88059 191.882129C645.19194 703.528777 731.271848 617.450409 731.271848 511.646647zM539.383558 395.903184c63.825696 0 115.751164 51.922387 115.751164 115.743463 0 63.825696-51.925468 115.751164-115.751164 115.751164-63.821076 0-115.743463-51.925468-115.743463-115.751164C423.640095 447.824031 475.562482 395.903184 539.383558 395.903184z"
|
||||
fill="#272636"
|
||||
p-id="10421"></path>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
type Props = {
|
||||
setOpenModelSettings: (open: boolean) => void
|
||||
}
|
||||
|
||||
export const Header: React.FC<Props> = ({ setOpenModelSettings }) => {
|
||||
const location = useLocation()
|
||||
|
||||
export const Header = () => {
|
||||
const { show, setShow } = useContext(HistoryContext)
|
||||
|
||||
const showLeft = useMemo<boolean>(() => {
|
||||
console.log(location.pathname)
|
||||
if (location.pathname.includes("/settings")) {
|
||||
return true
|
||||
}
|
||||
return show
|
||||
}, [location.pathname, show])
|
||||
|
||||
const { t } = useTranslation(["option", "common", "settings"])
|
||||
|
||||
const { clearChat } = useMessageOption()
|
||||
return (
|
||||
<div
|
||||
className={`w-full h-[60px] absolute inset-0 pl-5 z-10 flex items-center transition-all duration-300 ease-in-out ${show ? "left-[300px]" : ""}`}>
|
||||
className={`h-[60px] absolute inset-0 pl-5 z-10 flex items-center transition-all duration-300 ease-in-out ${show && !location.pathname.includes("/settings") ? "left-[300px]" : ""}`}>
|
||||
{/*控制侧边栏显示隐藏与新建对话*/}
|
||||
{!show && (
|
||||
{!showLeft && (
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
className="text-gray-500 dark:text-gray-400"
|
||||
@ -47,21 +83,54 @@ export const Header = () => {
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{location.pathname.includes("/settings") && (
|
||||
<h2 className="text-xl font-bold text-zinc-700 dark:text-zinc-300 mr-3">
|
||||
<NavLink
|
||||
to="/"
|
||||
className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
||||
<span className="text-[#d30100]">数联网</span>科创智能体
|
||||
</NavLink>
|
||||
</h2>
|
||||
)}
|
||||
{/* 项目标题 */}
|
||||
<div
|
||||
className={`
|
||||
absolute left-1/2 transform -translate-x-1/2
|
||||
w-[600px] h-[55px] bg-white dark:bg-black
|
||||
w-[600px] h-[60px] dark:bg-black
|
||||
flex items-center justify-center
|
||||
shadow-[0px_0px_5px_rgba(0,0,0,0.2)]
|
||||
rounded-b-[15px]
|
||||
transition-[top]
|
||||
${show ? "-top-[56px]" : "-top-[1px] delay-200"}
|
||||
transition-[top] drop-shadow
|
||||
${show ? "-top-[60px]" : "-top-[2px] delay-200"}
|
||||
`}>
|
||||
<h2 className="text-xl font-bold text-zinc-700 dark:text-zinc-300 mr-3">
|
||||
<svg
|
||||
// @ts-ignore
|
||||
t="1755653236428"
|
||||
className="icon"
|
||||
viewBox="0 0 8960 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="9634"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="100%"
|
||||
height="55">
|
||||
<path
|
||||
d="M8960 0c-451.52 181.184-171.2 1024-992 1024H992C171.232 1024 451.392 181.184 0 0h8960z"
|
||||
fill="#ffffff"
|
||||
p-id="9635"></path>
|
||||
</svg>
|
||||
<h2 className="text-xl font-bold text-zinc-700 dark:text-zinc-300 mr-3 absolute left-1/2 transform -translate-x-1/2">
|
||||
<span className="text-[#d30100]">数联网</span>科创智能体
|
||||
</h2>
|
||||
</div>
|
||||
{/*设置框*/}
|
||||
<div className="flex items-center ml-auto pr-5">
|
||||
<Tooltip title={t("settings")}>
|
||||
<NavLink
|
||||
to="/settings"
|
||||
className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
||||
<SettingIcon />
|
||||
</NavLink>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ export default function OptionLayout({
|
||||
{/*</div>*/}
|
||||
{/* <div className="relative flex h-full flex-col items-center"> */}
|
||||
<HistoryContext.Provider value={historyContextValue}>
|
||||
<Header />
|
||||
<Header setOpenModelSettings={setOpenModelSettings} />
|
||||
{children}
|
||||
</HistoryContext.Provider>
|
||||
{/* </div> */}
|
||||
|
@ -1,17 +1,18 @@
|
||||
import {
|
||||
BlocksIcon,
|
||||
BookIcon,
|
||||
BrainCircuitIcon,
|
||||
OrbitIcon,
|
||||
ShareIcon,
|
||||
BlocksIcon,
|
||||
InfoIcon,
|
||||
CombineIcon,
|
||||
ChromeIcon,
|
||||
CpuIcon
|
||||
CombineIcon,
|
||||
CpuIcon,
|
||||
InfoIcon,
|
||||
OrbitIcon,
|
||||
ShareIcon
|
||||
} from "lucide-react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { Link, useLocation } from "react-router-dom"
|
||||
import { OllamaIcon } from "../Icons/Ollama"
|
||||
import { IodIcon } from "../Icons/Iod.tsx"
|
||||
import { BetaTag } from "../Common/Beta"
|
||||
|
||||
function classNames(...classes: string[]) {
|
||||
@ -82,6 +83,12 @@ export const SettingsLayout = ({ children }: { children: React.ReactNode }) => {
|
||||
icon={OllamaIcon}
|
||||
current={location.pathname}
|
||||
/>
|
||||
<LinkComponent
|
||||
href="/settings/iod"
|
||||
name={t("iodSettings.title")}
|
||||
icon={IodIcon}
|
||||
current={location.pathname}
|
||||
/>
|
||||
{import.meta.env.BROWSER === "chrome" && (
|
||||
<LinkComponent
|
||||
href="/settings/chrome"
|
||||
|
@ -6,7 +6,7 @@ import { PlaygroundForm } from "./PlaygroundForm"
|
||||
import { PlaygroundChat } from "./PlaygroundChat"
|
||||
import { useMessageOption } from "@/hooks/useMessageOption"
|
||||
import { webUIResumeLastChat } from "@/services/app"
|
||||
import { PlaygroundData } from '@/components/Common/Playground/Data.tsx'
|
||||
import { PlaygroundData } from "@/components/Common/Playground/Data.tsx"
|
||||
import { PlaygroundScene } from "@/components/Common/Playground/Scene.tsx"
|
||||
|
||||
import {
|
||||
@ -23,7 +23,6 @@ import { PlaygroundTeam } from "@/components/Common/Playground/Team.tsx"
|
||||
import { PlaygroundHistory } from "@/components/Common/Playground/History.tsx"
|
||||
import { PlaygroundIodRelevant } from "@/components/Common/Playground/IodRelevant.tsx"
|
||||
|
||||
|
||||
export const Playground = () => {
|
||||
const drop = React.useRef<HTMLDivElement>(null)
|
||||
const [dropedFile, setDropedFile] = React.useState<File | undefined>()
|
||||
@ -146,13 +145,14 @@ export const Playground = () => {
|
||||
dropState === "dragging" ? "bg-gray-100 dark:bg-gray-800" : ""
|
||||
} bg-white dark:bg-[#171717]`}>
|
||||
<PlaygroundHistory />
|
||||
<div className="relative h-full flex-1 prose-lg flex flex-col items-center [&>*]:max-w-[848px] pt-[60px]">
|
||||
<div className="h-full flex-1 overflow-x-hidden prose-lg flex flex-col items-center [&>*]:max-w-[848px] pt-[60px]">
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="custom-scrollbar flex h-auto w-full flex-col items-center overflow-x-hidden overflow-y-auto px-5">
|
||||
className="custom-scrollbar flex h-auto w-full flex-col items-center px-5">
|
||||
<PlaygroundChat />
|
||||
</div>
|
||||
<div className="relative bottom-0 w-full">
|
||||
<div
|
||||
className={`${messages.length ? "absolute" : "relative"} bottom-0 w-full`}>
|
||||
{!isAtBottom && (
|
||||
<div className="absolute bottom-36 z-20 left-0 right-0 flex justify-center">
|
||||
<button
|
||||
@ -166,22 +166,20 @@ export const Playground = () => {
|
||||
</div>
|
||||
</div>
|
||||
{/*auto_530px_165px*/}
|
||||
{messages.length && (
|
||||
<div
|
||||
className="w-4/12 h-full grid grid-rows-10 gap-3 pt-16 pr-5 pb-0"
|
||||
style={{ paddingTop: "4rem" }}>
|
||||
<div className="w-full row-span-4">
|
||||
<PlaygroundIodRelevant />
|
||||
</div>
|
||||
<div className="w-full row-span-4 grid grid-cols-2 gap-3 custom-scrollbar">
|
||||
<PlaygroundData />
|
||||
<PlaygroundScene />
|
||||
</div>
|
||||
<div className="w-full row-span-2 pb-3">
|
||||
<PlaygroundTeam />
|
||||
</div>
|
||||
<div
|
||||
className="w-4/12 h-full grid grid-rows-12 gap-3 pt-16 pr-5 pb-0"
|
||||
style={{ paddingTop: "4rem" }}>
|
||||
<div className="w-full row-span-5">
|
||||
<PlaygroundIodRelevant />
|
||||
</div>
|
||||
)}
|
||||
<div className="w-full row-span-4 grid grid-cols-2 gap-3 custom-scrollbar">
|
||||
<PlaygroundData />
|
||||
<PlaygroundScene />
|
||||
</div>
|
||||
<div className="w-full row-span-3 pb-3">
|
||||
<PlaygroundTeam />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ export const PlaygroundChat = () => {
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{/*<div className="w-full pb-[0px]"></div>*/}
|
||||
{messages.length !== 0 && <div className="w-full pb-[157px]"></div>}
|
||||
<MessageSourcePopup
|
||||
open={isSourceOpen}
|
||||
setOpen={setIsSourceOpen}
|
||||
|
@ -19,11 +19,11 @@ export const PlaygroundEmpty = () => {
|
||||
})
|
||||
|
||||
function handleQuestion(message: string) {
|
||||
void sendMessage({ message, image: "" })
|
||||
void sendMessage({ message, image: "", isRegenerate: true })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full p-4">
|
||||
<div className="w-full pb-4 pt-[20%]">
|
||||
{/* 标题区域 */}
|
||||
<div className="mb-4">
|
||||
<h2
|
||||
|
@ -4,8 +4,7 @@ import React from "react"
|
||||
import useDynamicTextareaSize from "~/hooks/useDynamicTextareaSize"
|
||||
import { toBase64 } from "~/libs/to-base64"
|
||||
import { useMessageOption } from "~/hooks/useMessageOption"
|
||||
import { Checkbox, Dropdown, Switch, Tooltip } from "antd"
|
||||
import { Image } from "antd"
|
||||
import { Checkbox, Dropdown, Image, Switch, Tooltip } from "antd"
|
||||
import { useWebUI } from "~/store/webui"
|
||||
import { defaultEmbeddingModelForRag } from "~/services/ollama"
|
||||
import { ImageIcon, MicIcon, StopCircleIcon, X } from "lucide-react"
|
||||
@ -234,8 +233,7 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
className={`flex bg-transparent `}>
|
||||
<div className={`flex bg-transparent `}>
|
||||
<form
|
||||
onSubmit={form.onSubmit(async (value) => {
|
||||
stopListening()
|
||||
@ -304,33 +302,38 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
<div className="flex">
|
||||
{!selectedKnowledge && (
|
||||
<div>
|
||||
<Tooltip title={t("tooltip.searchInternet")}>
|
||||
<div className="inline-flex items-center gap-2">
|
||||
<PiGlobe
|
||||
className={`h-5 w-5 dark:text-gray-300 `}
|
||||
/>
|
||||
<Switch
|
||||
value={webSearch}
|
||||
onChange={(e) => setWebSearch(e)}
|
||||
checkedChildren={t("form.webSearch.on")}
|
||||
unCheckedChildren={t("form.webSearch.off")}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip title={t("tooltip.searchIod")} className="ml-3">
|
||||
<div className="inline-flex items-center gap-2">
|
||||
<PiNetwork
|
||||
className={`h-5 w-5 dark:text-gray-300 `}
|
||||
/>
|
||||
<Switch
|
||||
value={iodSearch}
|
||||
onChange={(e) => setIodSearch(e)}
|
||||
checkedChildren={t("form.webSearch.on")}
|
||||
unCheckedChildren={t("form.webSearch.off")}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{/* 展示隐藏深度搜索*/}
|
||||
<Tooltip
|
||||
title={t("tooltip.searchInternet")}
|
||||
className="hidden">
|
||||
<div className="inline-flex items-center gap-2">
|
||||
<PiGlobe
|
||||
className={`h-5 w-5 dark:text-gray-300 `}
|
||||
/>
|
||||
<Switch
|
||||
value={webSearch}
|
||||
onChange={(e) => setWebSearch(e)}
|
||||
checkedChildren={t("form.webSearch.on")}
|
||||
unCheckedChildren={t("form.webSearch.off")}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
title={t("tooltip.searchIod")}
|
||||
className="ml-3">
|
||||
<div className="inline-flex items-center gap-2">
|
||||
<PiNetwork
|
||||
className={`h-5 w-5 dark:text-gray-300 `}
|
||||
/>
|
||||
<Switch
|
||||
value={iodSearch}
|
||||
onChange={(e) => setIodSearch(e)}
|
||||
checkedChildren={t("form.webSearch.on")}
|
||||
unCheckedChildren={t("form.webSearch.off")}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex !justify-end gap-3">
|
||||
@ -357,7 +360,10 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
if (isListening) {
|
||||
stopSpeechRecognition()
|
||||
} else {
|
||||
console.log("开始语音识别,语言:", speechToTextLanguage);
|
||||
console.log(
|
||||
"开始语音识别,语言:",
|
||||
speechToTextLanguage
|
||||
)
|
||||
resetTranscript()
|
||||
startListening({
|
||||
continuous: true,
|
||||
|
54
src/components/Option/Settings/iod.tsx
Normal file
54
src/components/Option/Settings/iod.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
import { useTranslation } from "react-i18next"
|
||||
import TextArea from "antd/es/input/TextArea"
|
||||
import { useEffect, useState } from "react"
|
||||
|
||||
|
||||
export const IodApp = () => {
|
||||
const { t } = useTranslation("settings")
|
||||
|
||||
|
||||
const [connectVal, setConnectVal] = useState<string>('')
|
||||
|
||||
const setConnectValWrap = (val: string) => {
|
||||
localStorage.setItem("iod-connect", val)
|
||||
setConnectVal(val)
|
||||
}
|
||||
useEffect(() => {
|
||||
const val = localStorage.getItem("iod-connect")
|
||||
const defaultVal = {
|
||||
gatewayUrl: "tcp://reg01.public.internetofdata.cn:21037",
|
||||
registry: "data/Registry",
|
||||
localRepository: "data/Repository",
|
||||
doBrowser: "http://021.node.internetapi.cn:21030/SCIDE/SCManager"
|
||||
}
|
||||
if (!val) {
|
||||
localStorage.setItem(
|
||||
"iod-connect",
|
||||
JSON.stringify(defaultVal)
|
||||
)
|
||||
setConnectVal(JSON.stringify(defaultVal, null, 2))
|
||||
return
|
||||
}
|
||||
try {
|
||||
const val = localStorage.getItem("iod-connect")
|
||||
setConnectVal(JSON.stringify(JSON.parse(val), null, 2))
|
||||
} catch (e) {
|
||||
setConnectVal(JSON.stringify(defaultVal, null, 2))
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<dl className="flex flex-col space-y-6 text-sm">
|
||||
<div>
|
||||
<h2 className="text-base font-semibold leading-7 text-gray-900 dark:text-white">
|
||||
{t("iodSettings.heading")}
|
||||
</h2>
|
||||
<div className="border border-b border-gray-200 dark:border-gray-600 mt-3"></div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
<span className="text-gray-700 dark:text-neutral-50">连接配置</span>
|
||||
<TextArea rows={6} placeholder="请输入数联网连接配置" value={connectVal} onChange={(e) => setConnectValWrap(e.target.value)} />
|
||||
</div>
|
||||
</dl>
|
||||
)
|
||||
}
|
@ -5,6 +5,12 @@ interface PageAssistContext {
|
||||
messages: Message[]
|
||||
setMessages: Dispatch<SetStateAction<Message[]>>
|
||||
|
||||
currentMessageId: string
|
||||
setCurrentMessageId: Dispatch<SetStateAction<string>>
|
||||
|
||||
iodLoading: boolean
|
||||
setIodLoading: Dispatch<SetStateAction<boolean>>
|
||||
|
||||
controller: AbortController | null
|
||||
setController: Dispatch<SetStateAction<AbortController>>
|
||||
|
||||
@ -16,6 +22,12 @@ export const PageAssistContext = createContext<PageAssistContext>({
|
||||
messages: [],
|
||||
setMessages: () => {},
|
||||
|
||||
currentMessageId: "",
|
||||
setCurrentMessageId: () => {},
|
||||
|
||||
iodLoading: false,
|
||||
setIodLoading: () => {},
|
||||
|
||||
controller: null,
|
||||
setController: () => {},
|
||||
|
||||
|
@ -2,6 +2,7 @@ import {
|
||||
type ChatHistory as ChatHistoryType,
|
||||
type Message as MessageType
|
||||
} from "~/store/option"
|
||||
import { AllIodRegistryEntry } from "@/types/iod.ts"
|
||||
|
||||
type HistoryInfo = {
|
||||
id: string
|
||||
@ -30,7 +31,7 @@ type Message = {
|
||||
content: string
|
||||
images?: string[]
|
||||
webSources?: string[]
|
||||
iodSources?: string[]
|
||||
iodSources?: AllIodRegistryEntry
|
||||
search?: WebSearch
|
||||
createdAt: number
|
||||
reasoning_time_taken?: number
|
||||
@ -256,7 +257,7 @@ export const saveMessage = async (
|
||||
content: string,
|
||||
images: string[],
|
||||
webSources?: any[],
|
||||
iodSources?: any[],
|
||||
iodSources?: AllIodRegistryEntry,
|
||||
time?: number,
|
||||
message_type?: string,
|
||||
generationInfo?: any,
|
||||
@ -307,7 +308,7 @@ export const formatToMessage = (messages: MessageHistory): MessageType[] => {
|
||||
message: message.content,
|
||||
name: message.name,
|
||||
webSources: message?.webSources || [],
|
||||
iodSources: message?.iodSources || [],
|
||||
iodSources: message?.iodSources || { data: [], scenario: [], organization: []},
|
||||
images: message.images || [],
|
||||
generationInfo: message?.generationInfo,
|
||||
reasoning_time_taken: message?.reasoning_time_taken
|
||||
|
@ -1,8 +1,14 @@
|
||||
import { saveHistory, saveMessage } from "@/db"
|
||||
import { setLastUsedChatModel, setLastUsedChatSystemPrompt } from "@/services/model-settings"
|
||||
import {
|
||||
setLastUsedChatModel,
|
||||
setLastUsedChatSystemPrompt
|
||||
} from "@/services/model-settings"
|
||||
import { generateTitle } from "@/services/title"
|
||||
import { ChatHistory } from "@/store/option"
|
||||
import { updateDialog } from "@/web/iod"
|
||||
import { AllIodRegistryEntry } from "@/types/iod.ts"
|
||||
import { getDefaultIodSources } from "@/libs/iod.ts"
|
||||
|
||||
export const saveMessageOnError = async ({
|
||||
e,
|
||||
history,
|
||||
@ -62,7 +68,7 @@ export const saveMessageOnError = async ({
|
||||
userMessage,
|
||||
[image],
|
||||
[],
|
||||
[],
|
||||
getDefaultIodSources(),
|
||||
1,
|
||||
message_type
|
||||
)
|
||||
@ -74,13 +80,16 @@ export const saveMessageOnError = async ({
|
||||
botMessage,
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
getDefaultIodSources(),
|
||||
2,
|
||||
message_type
|
||||
)
|
||||
await setLastUsedChatModel(historyId, selectedModel)
|
||||
if (prompt_id || prompt_content) {
|
||||
await setLastUsedChatSystemPrompt(historyId, { prompt_content, prompt_id })
|
||||
await setLastUsedChatSystemPrompt(historyId, {
|
||||
prompt_content,
|
||||
prompt_id
|
||||
})
|
||||
}
|
||||
} else {
|
||||
const title = await generateTitle(selectedModel, userMessage, userMessage)
|
||||
@ -93,7 +102,7 @@ export const saveMessageOnError = async ({
|
||||
userMessage,
|
||||
[image],
|
||||
[],
|
||||
[],
|
||||
getDefaultIodSources(),
|
||||
1,
|
||||
message_type
|
||||
)
|
||||
@ -105,14 +114,17 @@ export const saveMessageOnError = async ({
|
||||
botMessage,
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
getDefaultIodSources(),
|
||||
2,
|
||||
message_type
|
||||
)
|
||||
setHistoryId(newHistoryId.id)
|
||||
await setLastUsedChatModel(newHistoryId.id, selectedModel)
|
||||
if (prompt_id || prompt_content) {
|
||||
await setLastUsedChatSystemPrompt(newHistoryId.id, { prompt_content, prompt_id })
|
||||
await setLastUsedChatSystemPrompt(newHistoryId.id, {
|
||||
prompt_content,
|
||||
prompt_id
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,7 +145,8 @@ export const saveMessageOnSuccess = async ({
|
||||
webSources,
|
||||
iodSources,
|
||||
message_source = "web-ui",
|
||||
message_type, generationInfo,
|
||||
message_type,
|
||||
generationInfo,
|
||||
prompt_id,
|
||||
prompt_content,
|
||||
reasoning_time_taken = 0
|
||||
@ -146,15 +159,15 @@ export const saveMessageOnSuccess = async ({
|
||||
image: string
|
||||
fullText: string
|
||||
webSources: any[]
|
||||
iodSources: any[]
|
||||
message_source?: "copilot" | "web-ui",
|
||||
iodSources: AllIodRegistryEntry
|
||||
message_source?: "copilot" | "web-ui"
|
||||
message_type?: string
|
||||
generationInfo?: any
|
||||
prompt_id?: string
|
||||
prompt_content?: string
|
||||
reasoning_time_taken?: number
|
||||
}) => {
|
||||
var botMessage;
|
||||
var botMessage
|
||||
if (historyId) {
|
||||
if (!isRegenerate) {
|
||||
await saveMessage(
|
||||
@ -164,7 +177,7 @@ export const saveMessageOnSuccess = async ({
|
||||
message,
|
||||
[image],
|
||||
[],
|
||||
[],
|
||||
getDefaultIodSources(),
|
||||
1,
|
||||
message_type,
|
||||
generationInfo,
|
||||
@ -187,7 +200,10 @@ export const saveMessageOnSuccess = async ({
|
||||
updateDialog(historyId, botMessage)
|
||||
await setLastUsedChatModel(historyId, selectedModel!)
|
||||
if (prompt_id || prompt_content) {
|
||||
await setLastUsedChatSystemPrompt(historyId, { prompt_content, prompt_id })
|
||||
await setLastUsedChatSystemPrompt(historyId, {
|
||||
prompt_content,
|
||||
prompt_id
|
||||
})
|
||||
}
|
||||
} else {
|
||||
const title = await generateTitle(selectedModel, message, message)
|
||||
@ -199,7 +215,7 @@ export const saveMessageOnSuccess = async ({
|
||||
message,
|
||||
[image],
|
||||
[],
|
||||
[],
|
||||
getDefaultIodSources(),
|
||||
1,
|
||||
message_type,
|
||||
generationInfo,
|
||||
@ -222,7 +238,10 @@ export const saveMessageOnSuccess = async ({
|
||||
setHistoryId(newHistoryId.id)
|
||||
await setLastUsedChatModel(newHistoryId.id, selectedModel!)
|
||||
if (prompt_id || prompt_content) {
|
||||
await setLastUsedChatSystemPrompt(newHistoryId.id, { prompt_content, prompt_id })
|
||||
await setLastUsedChatSystemPrompt(newHistoryId.id, {
|
||||
prompt_content,
|
||||
prompt_id
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import { useStoreMessageOption, type Message } from "~/store/option"
|
||||
import { useStoreMessage } from "~/store"
|
||||
import { SystemMessage } from "@langchain/core/messages"
|
||||
import { getDataFromCurrentTab } from "~/libs/get-html"
|
||||
import { MemoryVectorStore } from "langchain/vectorstores/memory"
|
||||
import { memoryEmbedding } from "@/utils/memory-embeddings"
|
||||
import { ChatHistory } from "@/store/option"
|
||||
import {
|
||||
@ -42,6 +41,8 @@ import {
|
||||
mergeReasoningContent,
|
||||
removeReasoning
|
||||
} from "@/libs/reasoning"
|
||||
import { AllIodRegistryEntry } from "@/types/iod.ts"
|
||||
import { getDefaultIodSources } from "@/libs/iod.ts"
|
||||
|
||||
export const useMessage = () => {
|
||||
const {
|
||||
@ -188,15 +189,15 @@ export const useMessage = () => {
|
||||
name: "You",
|
||||
message,
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
images: []
|
||||
},
|
||||
{
|
||||
isBot: true,
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
message: "",
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -208,7 +209,7 @@ export const useMessage = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -242,6 +243,7 @@ export const useMessage = () => {
|
||||
}
|
||||
isAlreadyExistEmbedding = keepTrackOfEmbedding[websiteUrl]
|
||||
}
|
||||
|
||||
setMessages(newMessage)
|
||||
const ollamaUrl = await getOllamaURL()
|
||||
const embeddingModle = await defaultEmbeddingModelForRag()
|
||||
@ -348,14 +350,7 @@ export const useMessage = () => {
|
||||
metadata: Record<string, any>
|
||||
}[] = []
|
||||
// TODO: update type
|
||||
let iodSources: {
|
||||
name: any
|
||||
type: any
|
||||
mode: string
|
||||
url: string
|
||||
pageContent: string
|
||||
metadata: Record<string, any>
|
||||
}[] = []
|
||||
let iodSources: AllIodRegistryEntry = getDefaultIodSources()
|
||||
|
||||
if (chatWithWebsiteEmbedding) {
|
||||
const docs = await vectorstore.similaritySearch(query, 4)
|
||||
@ -623,7 +618,7 @@ export const useMessage = () => {
|
||||
name: "You",
|
||||
message,
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
images: []
|
||||
},
|
||||
{
|
||||
@ -631,7 +626,7 @@ export const useMessage = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -643,7 +638,7 @@ export const useMessage = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -807,7 +802,7 @@ export const useMessage = () => {
|
||||
image,
|
||||
fullText,
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
message_source: "copilot",
|
||||
generationInfo,
|
||||
reasoning_time_taken: timetaken
|
||||
@ -912,7 +907,7 @@ export const useMessage = () => {
|
||||
name: "You",
|
||||
message,
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
images: [image]
|
||||
},
|
||||
{
|
||||
@ -920,7 +915,7 @@ export const useMessage = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -932,7 +927,7 @@ export const useMessage = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -1101,7 +1096,7 @@ export const useMessage = () => {
|
||||
image,
|
||||
fullText,
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
message_source: "copilot",
|
||||
generationInfo,
|
||||
reasoning_time_taken: timetaken
|
||||
@ -1145,7 +1140,7 @@ export const useMessage = () => {
|
||||
isRegenerate: boolean,
|
||||
messages: Message[],
|
||||
history: ChatHistory,
|
||||
signal: AbortSignal,
|
||||
signal: AbortSignal
|
||||
) => {
|
||||
const url = await getOllamaURL()
|
||||
setStreaming(true)
|
||||
@ -1203,7 +1198,7 @@ export const useMessage = () => {
|
||||
name: "You",
|
||||
message,
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
images: [image]
|
||||
},
|
||||
{
|
||||
@ -1211,7 +1206,7 @@ export const useMessage = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -1223,7 +1218,7 @@ export const useMessage = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -1300,8 +1295,12 @@ export const useMessage = () => {
|
||||
query = removeReasoning(query)
|
||||
}
|
||||
|
||||
const { prompt, webSources, iodSources } =
|
||||
await getSystemPromptForWeb(query, [], webSearch, iodSearch)
|
||||
const { prompt, webSources, iodSources } = await getSystemPromptForWeb(
|
||||
query,
|
||||
[],
|
||||
webSearch,
|
||||
iodSearch
|
||||
)
|
||||
setIsSearchingInternet(false)
|
||||
|
||||
// message = message.trim().replaceAll("\n", " ")
|
||||
@ -1556,7 +1555,7 @@ export const useMessage = () => {
|
||||
name: "You",
|
||||
message,
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
images: [image],
|
||||
messageType: messageType
|
||||
},
|
||||
@ -1565,7 +1564,7 @@ export const useMessage = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -1577,7 +1576,7 @@ export const useMessage = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -1724,7 +1723,7 @@ export const useMessage = () => {
|
||||
image,
|
||||
fullText,
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
message_source: "copilot",
|
||||
message_type: messageType,
|
||||
generationInfo,
|
||||
@ -1811,7 +1810,7 @@ export const useMessage = () => {
|
||||
isRegenerate || false,
|
||||
messages,
|
||||
memory || history,
|
||||
signal,
|
||||
signal
|
||||
)
|
||||
} else {
|
||||
await normalChatMode(
|
||||
|
@ -2,15 +2,14 @@ import React from "react"
|
||||
import { cleanUrl } from "~/libs/clean-url"
|
||||
import {
|
||||
defaultEmbeddingModelForRag,
|
||||
geWebSearchFollowUpPrompt,
|
||||
geWebSearchKeywordsPrompt,
|
||||
getOllamaURL,
|
||||
geWebSearchFollowUpPrompt,
|
||||
promptForRag,
|
||||
systemPromptForNonRagOption
|
||||
} from "~/services/ollama"
|
||||
import type { ChatHistory, Message, MeteringEntry } from "~/store/option"
|
||||
import { SystemMessage } from "@langchain/core/messages"
|
||||
import { useStoreMessageOption } from "~/store/option"
|
||||
import { SystemMessage } from "@langchain/core/messages"
|
||||
import {
|
||||
deleteChatForEdit,
|
||||
generateID,
|
||||
@ -47,14 +46,20 @@ import {
|
||||
mergeReasoningContent,
|
||||
removeReasoning
|
||||
} from "@/libs/reasoning"
|
||||
import { getDefaultIodSources } from "@/libs/iod.ts"
|
||||
|
||||
export const useMessageOption = () => {
|
||||
const {
|
||||
controller: abortController,
|
||||
setController: setAbortController,
|
||||
iodLoading,
|
||||
setIodLoading,
|
||||
currentMessageId,
|
||||
setCurrentMessageId,
|
||||
messages,
|
||||
setMessages
|
||||
setMessages,
|
||||
} = usePageAssist()
|
||||
|
||||
const {
|
||||
history,
|
||||
setHistory,
|
||||
@ -113,6 +118,8 @@ export const useMessageOption = () => {
|
||||
setIsProcessing(false)
|
||||
setStreaming(false)
|
||||
currentChatModelSettings.reset()
|
||||
setIodLoading(false)
|
||||
setCurrentMessageId("")
|
||||
textareaRef?.current?.focus()
|
||||
if (defaultInternetSearchOn) {
|
||||
setWebSearch(true)
|
||||
@ -195,6 +202,7 @@ export const useMessageOption = () => {
|
||||
})
|
||||
let newMessage: Message[] = []
|
||||
let generateMessageId = generateID()
|
||||
setCurrentMessageId(generateMessageId)
|
||||
const meter: MeteringEntry = {
|
||||
id: generateMessageId,
|
||||
queryContent: message,
|
||||
@ -214,15 +222,15 @@ export const useMessageOption = () => {
|
||||
name: "You",
|
||||
message,
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
images: [image]
|
||||
},
|
||||
{
|
||||
isBot: true,
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
message: "",
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -234,7 +242,7 @@ export const useMessageOption = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -316,7 +324,9 @@ export const useMessageOption = () => {
|
||||
// Currently only IoD search use keywords
|
||||
if (iodSearch) {
|
||||
// Extract keywords
|
||||
console.log("query:"+query+" --> "+JSON.stringify(tokenizeInput(query)));
|
||||
console.log(
|
||||
"query:" + query + " --> " + JSON.stringify(tokenizeInput(query))
|
||||
)
|
||||
keywords = tokenizeInput(query)
|
||||
/*
|
||||
const questionPrompt = await geWebSearchKeywordsPrompt()
|
||||
@ -335,15 +345,35 @@ export const useMessageOption = () => {
|
||||
*/
|
||||
}
|
||||
|
||||
const { prompt, webSources, iodSources, iodSearchResults: iodData, iodTokenCount } =
|
||||
await getSystemPromptForWeb(query, keywords, webSearch, iodSearch)
|
||||
const {
|
||||
prompt,
|
||||
webSources,
|
||||
iodSources,
|
||||
iodSearchResults: iodData,
|
||||
iodTokenCount
|
||||
} = await getSystemPromptForWeb(query, keywords, webSearch, iodSearch)
|
||||
setIodLoading(false)
|
||||
console.log("prompt:\n" + prompt)
|
||||
setIsSearchingInternet(false)
|
||||
meter.prompt = prompt
|
||||
meter.iodKeywords = keywords
|
||||
meter.iodData = iodData
|
||||
meter.iodData = Object.values(iodData).flat()
|
||||
meter.iodTokenCount = iodTokenCount
|
||||
|
||||
|
||||
setMessages((prev) => {
|
||||
return prev.map((message) => {
|
||||
if (message.id === generateMessageId) {
|
||||
return {
|
||||
...message,
|
||||
webSources,
|
||||
iodSources,
|
||||
}
|
||||
}
|
||||
return message
|
||||
})
|
||||
})
|
||||
|
||||
// message = message.trim().replaceAll("\n", " ")
|
||||
|
||||
let humanMessage = await humanMessageFormatter({
|
||||
@ -511,20 +541,17 @@ export const useMessageOption = () => {
|
||||
modelInputTokenCount: prompt.length,
|
||||
modelOutputTokenCount: fullText.length,
|
||||
model: ollama.modelName ?? ollama.model,
|
||||
relatedDataCount: iodData?.length ?? 0,
|
||||
relatedDataCount: Object.values(iodData).flat()?.length ?? 0,
|
||||
timeTaken: new Date().getTime() - chatStartTime.getTime(),
|
||||
date: chatStartTime.getTime(),
|
||||
cot,
|
||||
responseContent: content,
|
||||
modelResponseContent: fullText,
|
||||
modelResponseContent: fullText
|
||||
}
|
||||
const _meteringEntries = [
|
||||
currentMeteringEntry,
|
||||
...meteringEntries,
|
||||
]
|
||||
const _meteringEntries = [currentMeteringEntry, ...meteringEntries]
|
||||
setCurrentMeteringEntry({
|
||||
loading: false,
|
||||
data: currentMeteringEntry,
|
||||
data: currentMeteringEntry
|
||||
})
|
||||
setMeteringEntries(_meteringEntries)
|
||||
localStorage.setItem("meteringEntries", JSON.stringify(_meteringEntries))
|
||||
@ -653,7 +680,7 @@ export const useMessageOption = () => {
|
||||
|
||||
setCurrentMeteringEntry({
|
||||
loading: true,
|
||||
data: meter,
|
||||
data: meter
|
||||
})
|
||||
|
||||
if (!isRegenerate) {
|
||||
@ -664,7 +691,7 @@ export const useMessageOption = () => {
|
||||
name: "You",
|
||||
message,
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
images: [image]
|
||||
},
|
||||
{
|
||||
@ -672,7 +699,7 @@ export const useMessageOption = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -684,7 +711,7 @@ export const useMessageOption = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -883,7 +910,6 @@ export const useMessageOption = () => {
|
||||
setIsProcessing(false)
|
||||
setStreaming(false)
|
||||
|
||||
|
||||
// Save metering entry
|
||||
const { cot, content } = responseResolver(fullText)
|
||||
const currentMeteringEntry = {
|
||||
@ -891,20 +917,17 @@ export const useMessageOption = () => {
|
||||
modelInputTokenCount: prompt.length,
|
||||
modelOutputTokenCount: fullText.length,
|
||||
model: ollama.modelName ?? ollama.model,
|
||||
relatedDataCount: 0,
|
||||
relatedDataCount: 0,
|
||||
timeTaken: new Date().getTime() - chatStartTime.getTime(),
|
||||
date: chatStartTime.getTime(),
|
||||
cot,
|
||||
responseContent: content,
|
||||
modelResponseContent: fullText,
|
||||
modelResponseContent: fullText
|
||||
}
|
||||
const _meteringEntries = [
|
||||
currentMeteringEntry,
|
||||
...meteringEntries,
|
||||
]
|
||||
const _meteringEntries = [currentMeteringEntry, ...meteringEntries]
|
||||
setCurrentMeteringEntry({
|
||||
loading: false,
|
||||
data: currentMeteringEntry,
|
||||
data: currentMeteringEntry
|
||||
})
|
||||
setMeteringEntries(_meteringEntries)
|
||||
} catch (e) {
|
||||
@ -996,7 +1019,7 @@ export const useMessageOption = () => {
|
||||
name: "You",
|
||||
message,
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
images: []
|
||||
},
|
||||
{
|
||||
@ -1004,7 +1027,7 @@ export const useMessageOption = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -1016,7 +1039,7 @@ export const useMessageOption = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -1319,6 +1342,7 @@ export const useMessageOption = () => {
|
||||
)
|
||||
} else {
|
||||
if (webSearch || iodSearch) {
|
||||
setIodLoading(iodSearch)
|
||||
await searchChatMode(
|
||||
webSearch,
|
||||
iodSearch,
|
||||
@ -1435,6 +1459,10 @@ export const useMessageOption = () => {
|
||||
editMessage,
|
||||
messages,
|
||||
setMessages,
|
||||
iodLoading,
|
||||
currentMessageId,
|
||||
setIodLoading,
|
||||
setCurrentMessageId,
|
||||
onSubmit,
|
||||
setStreaming,
|
||||
streaming,
|
||||
|
18
src/libs/iod.ts
Normal file
18
src/libs/iod.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { AllIodRegistryEntry } from "@/types/iod.ts"
|
||||
|
||||
export const getDefaultIodSources = (): AllIodRegistryEntry => {
|
||||
return {
|
||||
data: {
|
||||
data: [],
|
||||
total: 0
|
||||
},
|
||||
scenario: {
|
||||
data: [],
|
||||
total: 0
|
||||
},
|
||||
organization: {
|
||||
data: [],
|
||||
total: 0
|
||||
}
|
||||
}
|
||||
}
|
@ -8,43 +8,43 @@ import DSvg from "@/assets/icons/d.svg"
|
||||
import ESvg from "@/assets/icons/e.svg"
|
||||
import FSvg from "@/assets/icons/f.svg"
|
||||
|
||||
export const qaPrompt = [
|
||||
export const qaPrompt =[
|
||||
{
|
||||
title: "最近一年大型语言模型的技术进展有哪些?",
|
||||
icon: <img src={RocketSvg} alt="Rocket" className="w-full my-0" />,
|
||||
title: "如何开发一个适合超大型城市的碳普惠方法学?",
|
||||
icon: <img src={RocketSvg} alt="Rocket" className="w-10 my-0" />,
|
||||
},
|
||||
{
|
||||
title: "生成式AI在企业中有哪些具体应用场景?",
|
||||
icon: <img src={BulbSvg} alt="Rocket" className="w-full my-0" />,
|
||||
title: "如何开发一个零碳园区的数字化评价系统?",
|
||||
icon: <img src={BulbSvg} alt="Rocket" className="w-10 my-0" />,
|
||||
},
|
||||
{
|
||||
title: "多模态学习技术的最新研究方向是什么?",
|
||||
icon: <img src={EyeSvg} alt="Rocket" className="w-full my-0" />,
|
||||
title: "如何开发一个碳定价预测系统?",
|
||||
icon: <img src={EyeSvg} alt="Rocket" className="w-10 my-0" />,
|
||||
},
|
||||
{
|
||||
title: "当前AI芯片市场格局和未来三年发展趋势如何?",
|
||||
icon: <img src={ASvg} alt="Rocket" className="w-full my-0" />,
|
||||
title: "新药临床研究如何提升实验安全性?",
|
||||
icon: <img src={ASvg} alt="Rocket" className="w-10 my-0" />,
|
||||
},
|
||||
{
|
||||
title: "主流深度学习框架性能与易用性对比分析?",
|
||||
icon: <img src={BSvg} alt="Rocket" className="w-full my-0" />,
|
||||
title: "如何加速新药申报和审批?",
|
||||
icon: <img src={BSvg} alt="Rocket" className="w-10 my-0" />,
|
||||
},
|
||||
{
|
||||
title: "国内外AI伦理治理框架有哪些最佳实践?",
|
||||
icon: <img src={CSvg} alt="Rocket" className="w-full my-0" />,
|
||||
},
|
||||
{
|
||||
title: "大规模知识图谱构建与应用最新进展?",
|
||||
icon: <img src={DSvg} alt="Rocket" className="w-full my-0" />,
|
||||
},
|
||||
{
|
||||
title: "计算机视觉领域近期突破性技术有哪些?",
|
||||
icon: <img src={ESvg} alt="Rocket" className="w-full my-0" />,
|
||||
},
|
||||
{
|
||||
title: "量子计算对AI算法的影响与应用前景?",
|
||||
icon: <img src={FSvg} alt="Rocket" className="w-full my-0" />,
|
||||
title: "如何研制与司美格鲁肽相似的新药?",
|
||||
icon: <img src={CSvg} alt="Rocket" className="w-10 my-0" />,
|
||||
},
|
||||
// {
|
||||
// title: "如何解决固态电池的成本和寿命难题?",
|
||||
// icon: <img src={DSvg} alt="Rocket" className="w-10 my-0" />,
|
||||
// },
|
||||
// {
|
||||
// title: "如何解决船舶制造中的材料腐蚀难题?",
|
||||
// icon: <img src={ESvg} alt="Rocket" className="w-10 my-0" />,
|
||||
// },
|
||||
// {
|
||||
// title: "如何解决船舶制造中流体模拟和建模优化难题?",
|
||||
// icon: <img src={FSvg} alt="Rocket" className="w-10 my-0" />,
|
||||
// },
|
||||
].map((item, index) => ({
|
||||
...item,
|
||||
id: index.toString(),
|
||||
|
@ -7,6 +7,7 @@ import OptionOllamaSettings from "./options-settings-ollama"
|
||||
import OptionShare from "./option-settings-share"
|
||||
import OptionKnowledgeBase from "./option-settings-knowledge"
|
||||
import OptionAbout from "./option-settings-about"
|
||||
import OptionIodSettings from "./option-settings-iod"
|
||||
import SidepanelChat from "./sidepanel-chat"
|
||||
import SidepanelSettings from "./sidepanel-settings"
|
||||
import OptionRagSettings from "./option-rag"
|
||||
@ -28,6 +29,7 @@ export const OptionRoutingChrome = () => {
|
||||
<Route path="/settings/share" element={<OptionShare />} />
|
||||
<Route path="/settings/knowledge" element={<OptionKnowledgeBase />} />
|
||||
<Route path="/settings/rag" element={<OptionRagSettings />} />
|
||||
<Route path="/settings/iod" element={<OptionIodSettings />} />
|
||||
<Route path="/settings/about" element={<OptionAbout />} />
|
||||
<Route path="/metering" element={<OptionMetering />} />
|
||||
<Route path="/metering/list/:id" element={<MeteringListDetail />} />
|
||||
|
@ -15,6 +15,7 @@ const MeteringListDetail = lazy(() => import("./metering-list-detail"))
|
||||
const OptionShare = lazy(() => import("./option-settings-share"))
|
||||
const OptionKnowledgeBase = lazy(() => import("./option-settings-knowledge"))
|
||||
const OptionAbout = lazy(() => import("./option-settings-about"))
|
||||
const OptionIodSettings = lazy(() => import("./option-settings-iod"))
|
||||
const OptionRagSettings = lazy(() => import("./option-rag"))
|
||||
const OptionOpenAI = lazy(() => import("./option-settings-openai"))
|
||||
|
||||
@ -31,6 +32,7 @@ export const OptionRoutingFirefox = () => {
|
||||
<Route path="/settings/knowledge" element={<OptionKnowledgeBase />} />
|
||||
<Route path="/settings/about" element={<OptionAbout />} />
|
||||
<Route path="/settings/rag" element={<OptionRagSettings />} />
|
||||
<Route path="/settings/iod" element={<OptionIodSettings />} />
|
||||
<Route path="/metering" element={<OptionMetering />} />
|
||||
<Route path="/metering/list/:id" element={<MeteringListDetail />} />
|
||||
</Routes>
|
||||
|
15
src/routes/option-settings-iod.tsx
Normal file
15
src/routes/option-settings-iod.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import { SettingsLayout } from "~/components/Layouts/SettingsOptionLayout"
|
||||
import OptionLayout from "~/components/Layouts/Layout"
|
||||
import { IodApp } from "@/components/Option/Settings/iod"
|
||||
|
||||
const OptionAbout = () => {
|
||||
return (
|
||||
<OptionLayout>
|
||||
<SettingsLayout>
|
||||
<IodApp />
|
||||
</SettingsLayout>
|
||||
</OptionLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export default OptionAbout
|
@ -18,6 +18,10 @@ export type ChatHistory = {
|
||||
type State = {
|
||||
messages: Message[]
|
||||
setMessages: (messages: Message[]) => void
|
||||
currentMessageId: string
|
||||
setCurrentMessageId: (messageId: string) => void
|
||||
iodLoading: boolean
|
||||
setIodLoading: (iodLoading: boolean) => void
|
||||
history: ChatHistory
|
||||
setHistory: (history: ChatHistory) => void
|
||||
streaming: boolean
|
||||
@ -53,6 +57,10 @@ type State = {
|
||||
export const useStoreMessage = create<State>((set) => ({
|
||||
messages: [],
|
||||
setMessages: (messages) => set({ messages }),
|
||||
currentMessageId: "",
|
||||
setCurrentMessageId: (currentMessageId) => set({ currentMessageId }),
|
||||
iodLoading: false,
|
||||
setIodLoading: (iodLoading) => set({ iodLoading }),
|
||||
history: [],
|
||||
setHistory: (history) => set({ history }),
|
||||
streaming: false,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Knowledge } from "@/db/knowledge"
|
||||
import { create } from "zustand"
|
||||
import { AllIodRegistryEntry } from "@/types/iod.ts"
|
||||
|
||||
type WebSearch = {
|
||||
search_engine: string
|
||||
@ -15,7 +16,7 @@ export type Message = {
|
||||
name: string
|
||||
message: string
|
||||
webSources: any[]
|
||||
iodSources: any[]
|
||||
iodSources: AllIodRegistryEntry
|
||||
images?: string[]
|
||||
search?: WebSearch
|
||||
reasoning_time_taken?: number
|
||||
@ -147,7 +148,7 @@ export const useStoreMessageOption = create<State>((set) => ({
|
||||
setIsEmbedding: (isEmbedding) => set({ isEmbedding }),
|
||||
webSearch: false,
|
||||
setWebSearch: (webSearch) => set({ webSearch }),
|
||||
iodSearch: false,
|
||||
iodSearch: true,
|
||||
setIodSearch: (iodSearch) => set({ iodSearch }),
|
||||
isSearchingInternet: false,
|
||||
setIsSearchingInternet: (isSearchingInternet) => set({ isSearchingInternet }),
|
||||
|
@ -8,4 +8,20 @@ export type IodRegistryEntry = {
|
||||
data_space?: string
|
||||
data_type?:string
|
||||
traceId?:string
|
||||
fromRepo?: string
|
||||
}
|
||||
|
||||
export type AllIodRegistryEntry = {
|
||||
data: {
|
||||
data: IodRegistryEntry[]
|
||||
total: number
|
||||
}
|
||||
scenario: {
|
||||
data: IodRegistryEntry[]
|
||||
total: number
|
||||
}
|
||||
organization: {
|
||||
data: IodRegistryEntry[]
|
||||
total: number
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { AllIodRegistryEntry } from "@/types/iod.ts"
|
||||
|
||||
type WebSearch = {
|
||||
search_engine: string
|
||||
search_url: string
|
||||
@ -12,11 +14,11 @@ export type Message = {
|
||||
name: string
|
||||
message: string
|
||||
webSources: any[]
|
||||
iodSources: any[]
|
||||
iodSources: AllIodRegistryEntry
|
||||
images?: string[]
|
||||
search?: WebSearch
|
||||
messageType?: string
|
||||
id?: string
|
||||
generationInfo?: any
|
||||
reasoning_time_taken?: number
|
||||
}
|
||||
}
|
||||
|
487
src/web/iod.ts
487
src/web/iod.ts
@ -1,296 +1,401 @@
|
||||
import { cleanUrl } from "@/libs/clean-url"
|
||||
import { PageAssistHtmlLoader } from "@/loader/html"
|
||||
import { PageAssistPDFUrlLoader } from "@/loader/pdf-url"
|
||||
import { pageAssistEmbeddingModel } from "@/models/embedding"
|
||||
import { defaultEmbeddingModelForRag, getOllamaURL } from "@/services/ollama"
|
||||
import { getOllamaURL } from "@/services/ollama"
|
||||
|
||||
import {
|
||||
getIsSimpleInternetSearch,
|
||||
totalSearchResults
|
||||
} from "@/services/search"
|
||||
import { getPageAssistTextSplitter } from "@/utils/text-splitter"
|
||||
import { Document } from "@langchain/core/documents"
|
||||
import { MemoryVectorStore } from "langchain/vectorstores/memory"
|
||||
import type { IodRegistryEntry } from "~/types/iod"
|
||||
|
||||
import { Document } from "@langchain/core/documents"
|
||||
import { AllIodRegistryEntry, IodRegistryEntry } from "~/types/iod"
|
||||
|
||||
import { PageAssitDatabase } from "@/db"
|
||||
import exp from "constants"
|
||||
|
||||
import { Segment, useDefault, cnPOSTag, enPOSTag} from 'segmentit';
|
||||
const segment = useDefault(new Segment());
|
||||
import { enPOSTag, Segment, useDefault } from "segmentit"
|
||||
import { getDefaultIodSources } from "@/libs/iod.ts"
|
||||
|
||||
const segment = useDefault(new Segment())
|
||||
export const tokenizeInput = function (input: string): string[] {
|
||||
const words = segment.doSegment(input, { simple: false });
|
||||
console.log(words.map(function(word){return {w:word.w, p:enPOSTag(word.p)}}) );
|
||||
return words.filter(word =>( word.w.length > 1)).map(word=>word.w);
|
||||
const words = segment.doSegment(input, { simple: false })
|
||||
console.log(
|
||||
words.map(function (word) {
|
||||
return { w: word.w, p: enPOSTag(word.p) }
|
||||
})
|
||||
)
|
||||
return words.filter((word) => word.w.length > 1).map((word) => word.w)
|
||||
}
|
||||
//doipUrl = tcp://reg01.public.internetofdata.cn:21037
|
||||
export const iodConfig = {
|
||||
"gatewayUrl": "tcp://reg01.public.internetofdata.cn:21037",
|
||||
"registry":"data/Registry",
|
||||
"localRepository":"data/Repository",
|
||||
"doBrowser":"http://021.node.internetapi.cn:21030/SCIDE/SCManager"
|
||||
export const _iodConfig = {
|
||||
gatewayUrl: "tcp://reg01.public.internetofdata.cn:21037",
|
||||
registry: "data/Registry",
|
||||
localRepository: "data/Repository",
|
||||
doBrowser: "http://021.node.internetapi.cn:21030/SCIDE/SCManager"
|
||||
}
|
||||
|
||||
function getIodConfig() {
|
||||
const val = localStorage.getItem("iod-connect")
|
||||
if (!val) {
|
||||
return _iodConfig
|
||||
}
|
||||
try {
|
||||
return JSON.parse(val)
|
||||
} catch {
|
||||
return _iodConfig
|
||||
}
|
||||
}
|
||||
export const iodConfigLocal = {
|
||||
"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://127.0.0.1:21036",
|
||||
registry: "bdware/Registry",
|
||||
localRepository: "bdtest.local/myrepo1",
|
||||
doBrowser: "http://127.0.0.1:21030/SCIDE/SCManager"
|
||||
}
|
||||
function inGrepList(str: string){
|
||||
return "什么|问题|需要|合适|设计|考虑|合作|精度|传感器|最新|研究|药物".indexOf(str)!=-1;
|
||||
function inGrepList(str: string) {
|
||||
return (
|
||||
"什么|问题|需要|合适|设计|考虑|合作|精度|传感器|最新|研究|药物".indexOf(
|
||||
str
|
||||
) != -1
|
||||
)
|
||||
}
|
||||
|
||||
export const makeSearchParamsWithDataType = function(count: number, keyword: string| string[], dataType: string){
|
||||
const searchMode = [];
|
||||
searchMode.push({"key":"data_type", "type":"MUST", "value":dataType})
|
||||
if (typeof keyword === 'string') {
|
||||
export const makeSearchParamsWithDataType = function (
|
||||
count: number,
|
||||
keyword: string | string[],
|
||||
dataType: string
|
||||
) {
|
||||
const iodConfig = getIodConfig()
|
||||
const searchMode = []
|
||||
searchMode.push({ key: "data_type", type: "MUST", value: dataType })
|
||||
if (typeof keyword === "string") {
|
||||
// 如果 keyword 是字符串,则直接添加一个 searchMode 条目
|
||||
searchMode.push({
|
||||
key: "description",
|
||||
type: "MUST",
|
||||
value: keyword
|
||||
});
|
||||
})
|
||||
} else if (Array.isArray(keyword)) {
|
||||
// 如果 keyword 是数组,则为每个元素添加一个 searchMode 条目
|
||||
keyword.forEach(str => {
|
||||
keyword.forEach((str) => {
|
||||
if (!inGrepList(str))
|
||||
searchMode.push({
|
||||
key: "description",
|
||||
type: "SHOULD",
|
||||
value: str
|
||||
});
|
||||
});
|
||||
searchMode.push({
|
||||
key: "description",
|
||||
type: "SHOULD",
|
||||
value: str
|
||||
})
|
||||
})
|
||||
}
|
||||
return {
|
||||
action: "executeContract",
|
||||
contractID: "BDBrowser",
|
||||
operation: "sendRequestDirectly",
|
||||
arg: {
|
||||
id: iodConfig.registry,
|
||||
id: iodConfig.registry,
|
||||
//doipUrl:"tcp://127.0.0.1:21039",
|
||||
doipUrl: iodConfig.gatewayUrl,
|
||||
op: "Search",
|
||||
vars:{
|
||||
timeout:15000
|
||||
vars: {
|
||||
timeout: 15000
|
||||
},
|
||||
attributes: {
|
||||
offset: 0,
|
||||
count,
|
||||
bodyBase64Encoded: false,
|
||||
searchMode:searchMode
|
||||
searchMode: searchMode
|
||||
},
|
||||
body: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const makeRegSearchParams = function(count: number, keyword: string| string[]){
|
||||
const searchMode = [];
|
||||
if (typeof keyword === 'string') {
|
||||
export const makeRegSearchParams = function (
|
||||
count: number,
|
||||
keyword: string | string[]
|
||||
) {
|
||||
const searchMode = []
|
||||
const iodConfig = getIodConfig()
|
||||
if (typeof keyword === "string") {
|
||||
// 如果 keyword 是字符串,则直接添加一个 searchMode 条目
|
||||
searchMode.push({
|
||||
key: "description",
|
||||
type: "MUST",
|
||||
value: keyword
|
||||
});
|
||||
})
|
||||
} else if (Array.isArray(keyword)) {
|
||||
// 如果 keyword 是数组,则为每个元素添加一个 searchMode 条目
|
||||
keyword.forEach(str => {
|
||||
keyword.forEach((str) => {
|
||||
if (!inGrepList(str))
|
||||
searchMode.push({
|
||||
key: "description",
|
||||
type: "SHOULD",
|
||||
value: str
|
||||
});
|
||||
});
|
||||
searchMode.push({
|
||||
key: "description",
|
||||
type: "SHOULD",
|
||||
value: str
|
||||
})
|
||||
})
|
||||
}
|
||||
return {
|
||||
action: "executeContract",
|
||||
contractID: "BDBrowser",
|
||||
operation: "sendRequestDirectly",
|
||||
arg: {
|
||||
id: iodConfig.registry,
|
||||
id: iodConfig.registry,
|
||||
//doipUrl:"tcp://127.0.0.1:21039",
|
||||
doipUrl: iodConfig.gatewayUrl,
|
||||
op: "Search",
|
||||
vars:{
|
||||
timeout:15000
|
||||
vars: {
|
||||
timeout: 15000
|
||||
},
|
||||
attributes: {
|
||||
offset: 0,
|
||||
count,
|
||||
bodyBase64Encoded: false,
|
||||
searchMode:searchMode
|
||||
searchMode: searchMode
|
||||
},
|
||||
body: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const makeDOIPParams = (doId:string, op:string, attributes:Object, requestBody: string) => ({
|
||||
action: "executeContract",
|
||||
contractID: "BDBrowser",
|
||||
operation: "sendRequestDirectly",
|
||||
arg: {
|
||||
id: doId,
|
||||
doipUrl: iodConfig.gatewayUrl,
|
||||
op: op,
|
||||
attributes: attributes,
|
||||
body: requestBody
|
||||
export const makeDOIPParams = (
|
||||
doId: string,
|
||||
op: string,
|
||||
attributes: Object,
|
||||
requestBody: string
|
||||
) => {
|
||||
const iodConfig = getIodConfig()
|
||||
return {
|
||||
action: "executeContract",
|
||||
contractID: "BDBrowser",
|
||||
operation: "sendRequestDirectly",
|
||||
arg: {
|
||||
id: doId,
|
||||
doipUrl: iodConfig.gatewayUrl,
|
||||
op: op,
|
||||
attributes: attributes,
|
||||
body: requestBody
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export const retrieveDoc = function(doId: string) : Promise<Document> {
|
||||
console.log("retriveDoc:"+doId)
|
||||
const params = makeDOIPParams(doId,"Retrieve",{
|
||||
bodyBase64Encoded: false
|
||||
}, "");
|
||||
const abortController = new AbortController()
|
||||
setTimeout(() => abortController.abort(), 10000)
|
||||
return fetch(iodConfig.doBrowser, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(params),
|
||||
signal: abortController.signal
|
||||
}).then((response) => {
|
||||
console.log("responseIn retrieveDoc:");
|
||||
console.log(response);
|
||||
return response.json()})
|
||||
.then((res) => {
|
||||
console.log("res:");
|
||||
console.log(res.result.body);
|
||||
//TODO
|
||||
return {
|
||||
metadata:{traceId:res.result.header.attributes?.traceId},
|
||||
pageContent:res.result.body
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const updateInLocalRepo = function(historyId: string, requestBody: Object) : Promise<string> {
|
||||
const params = makeDOIPParams(iodConfig.localRepository,"Update",{
|
||||
"aiDialogID": historyId,
|
||||
bodyBase64Encoded: false
|
||||
}, JSON.stringify(requestBody));
|
||||
export const retrieveDoc = function (doId: string): Promise<Document> {
|
||||
const iodConfig = getIodConfig()
|
||||
console.log("retriveDoc:" + doId)
|
||||
const params = makeDOIPParams(
|
||||
doId,
|
||||
"Retrieve",
|
||||
{
|
||||
bodyBase64Encoded: false
|
||||
},
|
||||
""
|
||||
)
|
||||
const abortController = new AbortController()
|
||||
setTimeout(() => abortController.abort(), 10000)
|
||||
return fetch(iodConfig.doBrowser, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(params),
|
||||
signal: abortController.signal
|
||||
}).then((response) => response.json())
|
||||
.then((res) => {
|
||||
console.log("update dialog:"+JSON.stringify(res))
|
||||
return res.body;
|
||||
})
|
||||
.then((response) => {
|
||||
console.log("responseIn retrieveDoc:")
|
||||
console.log(response)
|
||||
return response.json()
|
||||
})
|
||||
.then((res) => {
|
||||
console.log("res:")
|
||||
console.log(res.result.body)
|
||||
//TODO
|
||||
return {
|
||||
metadata: { traceId: res.result.header.attributes?.traceId },
|
||||
pageContent: res.result.body
|
||||
}
|
||||
})
|
||||
}
|
||||
export const updateDialog = async function(histroyId : string, botMessage: any): Promise<string> {
|
||||
|
||||
export const updateInLocalRepo = function (
|
||||
historyId: string,
|
||||
requestBody: Object
|
||||
): Promise<string> {
|
||||
const iodConfig = getIodConfig()
|
||||
const params = makeDOIPParams(
|
||||
iodConfig.localRepository,
|
||||
"Update",
|
||||
{
|
||||
aiDialogID: historyId,
|
||||
bodyBase64Encoded: false
|
||||
},
|
||||
JSON.stringify(requestBody)
|
||||
)
|
||||
const abortController = new AbortController()
|
||||
setTimeout(() => abortController.abort(), 10000)
|
||||
return fetch(iodConfig.doBrowser, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(params),
|
||||
signal: abortController.signal
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((res) => {
|
||||
console.log("update dialog:" + JSON.stringify(res))
|
||||
return res.body
|
||||
})
|
||||
}
|
||||
export const updateDialog = async function (
|
||||
histroyId: string,
|
||||
botMessage: any
|
||||
): Promise<string> {
|
||||
//TODO @Nex confused by Message/MessageType in ./db/index.ts!
|
||||
const db = new PageAssitDatabase()
|
||||
const chatHistory = await db.getChatHistory(histroyId)
|
||||
var userMessage = null;
|
||||
for (var i=0;i<chatHistory.length;i++){
|
||||
userMessage = chatHistory[i];
|
||||
if (userMessage.role=='user') break;
|
||||
var userMessage = null
|
||||
for (var i = 0; i < chatHistory.length; i++) {
|
||||
userMessage = chatHistory[i]
|
||||
if (userMessage.role == "user") break
|
||||
}
|
||||
let updateBody:any = {};
|
||||
let updateBody: any = {}
|
||||
// !!!IMPORTANT!!! traceId = histroyId+"/"+userMessage.id;
|
||||
// Update traceId in retrieveDoc!
|
||||
updateBody.traceId = histroyId+"/"+userMessage.id;
|
||||
updateBody.traceId = histroyId + "/" + userMessage.id
|
||||
updateBody.question = {
|
||||
"id": histroyId+"/"+userMessage.id,
|
||||
"content": userMessage.content,
|
||||
"tokenCount": userMessage.content.length
|
||||
id: histroyId + "/" + userMessage.id,
|
||||
content: userMessage.content,
|
||||
tokenCount: userMessage.content.length
|
||||
}
|
||||
updateBody.answer = {
|
||||
"id": histroyId+"/"+botMessage.id,
|
||||
"content": botMessage.content,
|
||||
"tokenCount": botMessage.content.length
|
||||
id: histroyId + "/" + botMessage.id,
|
||||
content: botMessage.content,
|
||||
tokenCount: botMessage.content.length
|
||||
}
|
||||
//TODO set a correct model ID
|
||||
updateBody.model = {"id":"bdware.ollama/" + userMessage.name}
|
||||
updateBody.model = { id: "bdware.ollama/" + userMessage.name }
|
||||
|
||||
//TODO incorrect tokenCount calculated!!
|
||||
updateBody.webSources = botMessage.webSources?.map((r) => ({
|
||||
url: r.url,
|
||||
tokenCount: r.url.length,
|
||||
content: r.url,
|
||||
traceId: r?.traceId
|
||||
})) ?? [];
|
||||
updateBody.IoDSources = botMessage.iodSources?.map((r) => ({
|
||||
id: r.doId,
|
||||
tokenCount: (r.content || r.description)?calculateTokenCount((r.content || r.description)):0,
|
||||
content: r.content || r.description,
|
||||
traceId: r?.traceId
|
||||
})) ?? [];
|
||||
console.log("updateBody:");
|
||||
updateBody.webSources =
|
||||
botMessage.webSources?.map((r) => ({
|
||||
url: r.url,
|
||||
tokenCount: r.url.length,
|
||||
content: r.url,
|
||||
traceId: r?.traceId
|
||||
})) ?? []
|
||||
updateBody.IoDSources =
|
||||
botMessage.iodSources?.map((r) => ({
|
||||
id: r.doId,
|
||||
tokenCount:
|
||||
r.content || r.description
|
||||
? calculateTokenCount(r.content || r.description)
|
||||
: 0,
|
||||
content: r.content || r.description,
|
||||
traceId: r?.traceId
|
||||
})) ?? []
|
||||
console.log("updateBody:")
|
||||
console.log(updateBody)
|
||||
return updateInLocalRepo(histroyId,updateBody)
|
||||
return updateInLocalRepo(histroyId, updateBody)
|
||||
}
|
||||
|
||||
export async function localIodSearch(
|
||||
query: string,
|
||||
keywords: string[]
|
||||
): Promise<IodRegistryEntry[]> {
|
||||
): Promise<AllIodRegistryEntry> {
|
||||
const iodConfig = getIodConfig()
|
||||
const TOTAL_SEARCH_RESULTS = await totalSearchResults()
|
||||
const abortController = new AbortController();
|
||||
setTimeout(() => abortController.abort(), 10000);
|
||||
const params = makeRegSearchParams(TOTAL_SEARCH_RESULTS, keywords);
|
||||
const dataParams = makeSearchParamsWithDataType(TOTAL_SEARCH_RESULTS,keywords, "data");
|
||||
const scenarioParams = makeSearchParamsWithDataType(TOTAL_SEARCH_RESULTS,keywords, "scenario");
|
||||
const orgParams = makeSearchParamsWithDataType(TOTAL_SEARCH_RESULTS,keywords, "organization");
|
||||
const abortController = new AbortController()
|
||||
setTimeout(() => abortController.abort(), 10000)
|
||||
const params = makeRegSearchParams(TOTAL_SEARCH_RESULTS, keywords)
|
||||
const dataParams = makeSearchParamsWithDataType(
|
||||
TOTAL_SEARCH_RESULTS,
|
||||
keywords,
|
||||
"data"
|
||||
)
|
||||
const scenarioParams = makeSearchParamsWithDataType(
|
||||
TOTAL_SEARCH_RESULTS,
|
||||
keywords,
|
||||
"scenario"
|
||||
)
|
||||
const orgParams = makeSearchParamsWithDataType(
|
||||
TOTAL_SEARCH_RESULTS,
|
||||
keywords,
|
||||
"organization"
|
||||
)
|
||||
|
||||
try {
|
||||
console.log('params------->',params)
|
||||
console.log("params------->", params)
|
||||
const requests = [
|
||||
fetch(iodConfig.doBrowser,{method: "POST", body: JSON.stringify(dataParams),signal: abortController.signal}),
|
||||
fetch(iodConfig.doBrowser,{method: "POST", body: JSON.stringify(scenarioParams),signal: abortController.signal}),
|
||||
fetch(iodConfig.doBrowser,{method: "POST", body: JSON.stringify(orgParams),signal: abortController.signal})
|
||||
];
|
||||
fetch(iodConfig.doBrowser, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(dataParams),
|
||||
signal: abortController.signal
|
||||
}),
|
||||
fetch(iodConfig.doBrowser, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(scenarioParams),
|
||||
signal: abortController.signal
|
||||
}),
|
||||
fetch(iodConfig.doBrowser, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(orgParams),
|
||||
signal: abortController.signal
|
||||
})
|
||||
]
|
||||
//TODO @Zhaoweijie, 这三类分别是数据、场景、团队的搜索请求。
|
||||
const responses = await Promise.all(requests);
|
||||
const results = await Promise.all(responses.map(res => res.json()));
|
||||
const allResults: IodRegistryEntry[] = [];
|
||||
const responses = await Promise.all(requests)
|
||||
const results = await Promise.all(responses.map((res) => res.json()))
|
||||
|
||||
const allResults: AllIodRegistryEntry = getDefaultIodSources()
|
||||
let i = 0
|
||||
for (const res of results) {
|
||||
// 检查顶层状态
|
||||
if (res.status !== "Success") {
|
||||
continue; // 跳过失败的请求
|
||||
continue // 跳过失败的请求
|
||||
}
|
||||
let body;
|
||||
let body
|
||||
try {
|
||||
body = JSON.parse(res.result.body);
|
||||
body = JSON.parse(res.result.body)
|
||||
} catch (e) {
|
||||
console.warn("Failed to parse result.body as JSON", e);
|
||||
continue;
|
||||
console.warn("Failed to parse result.body as JSON", e)
|
||||
continue
|
||||
}
|
||||
|
||||
if (body.code !== 0) {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
const entries: IodRegistryEntry[] = body.data?.results || [];
|
||||
// 数据清洗:补全 url 和 doId
|
||||
const entries: IodRegistryEntry[] = body.data?.results || []
|
||||
const prunedEntries: IodRegistryEntry[] = []
|
||||
const seenDoIds = new Set<string>()
|
||||
// 数据清洗:补全 url 和 doId
|
||||
for (const r of entries) {
|
||||
r.url = r.url || r.pdf_url;
|
||||
r.doId = r.doId || r.doid;
|
||||
r.url = r.url || r.pdf_url
|
||||
// @ts-ignore
|
||||
r.doId = r.doId || r.doid
|
||||
if (seenDoIds.has(r.doId)) {
|
||||
continue
|
||||
}
|
||||
prunedEntries.push(r)
|
||||
}
|
||||
// 合并到总结果
|
||||
allResults.push(...entries);
|
||||
}
|
||||
const seenDoIds = new Set<string>();
|
||||
const prunedResults: IodRegistryEntry[] = [];
|
||||
for (const r of allResults) {
|
||||
if (r.doId && !seenDoIds.has(r.doId)) {
|
||||
seenDoIds.add(r.doId);
|
||||
prunedResults.push(r);
|
||||
|
||||
// 数据
|
||||
if (i === 0) {
|
||||
allResults.data = {
|
||||
data: prunedEntries,
|
||||
total: body.data?.total ?? 0
|
||||
}
|
||||
}
|
||||
// 场景
|
||||
if (i === 1) {
|
||||
allResults.scenario = {
|
||||
data: prunedEntries,
|
||||
total: body.data?.total ?? 0
|
||||
}
|
||||
}
|
||||
// 团队
|
||||
if (i === 2) {
|
||||
allResults.organization = {
|
||||
data: prunedEntries,
|
||||
total: body.data?.total ?? 0
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
return prunedResults;
|
||||
|
||||
return allResults
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return [];
|
||||
console.log(e)
|
||||
return getDefaultIodSources()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
const ARXIV_URL_PATTERN = /^https?:\/\/arxiv\.org\//
|
||||
@ -300,28 +405,41 @@ export const searchIod = async (query: string, keywords: string[]) => {
|
||||
const searchResults = await localIodSearch(query, keywords)
|
||||
|
||||
const isSimpleMode = await getIsSimpleInternetSearch()
|
||||
console.log("searchMode:"+isSimpleMode+"\n kw:"+JSON.stringify(keywords)+"\n"+" ->searchResult:\n"+JSON.stringify(searchResults))
|
||||
console.log("pruned Search Result:"+JSON.stringify(searchResults.map(r=>r.doId+" "+r.name)))
|
||||
console.log(
|
||||
"searchMode:" +
|
||||
isSimpleMode +
|
||||
"\n kw:" +
|
||||
JSON.stringify(keywords) +
|
||||
"\n" +
|
||||
" ->searchResult:\n" +
|
||||
JSON.stringify(searchResults)
|
||||
)
|
||||
if (isSimpleMode) {
|
||||
await getOllamaURL()
|
||||
return searchResults
|
||||
}
|
||||
|
||||
const docs: Document<Record<string, any>>[] = []
|
||||
const resMap = new Map<string, IodRegistryEntry>()
|
||||
for (const result of searchResults) {
|
||||
for (const result of Object.values(searchResults)
|
||||
.map((item) => item.data)
|
||||
.flat()) {
|
||||
const url = result.url
|
||||
if (result.doId){
|
||||
//TODO !!!!@Nex traceId should be the id of history/question!
|
||||
let docFromRetrieve = await retrieveDoc(result.doId);
|
||||
console.log("doc from Retrieve:"+result.doId+" -->"+JSON.stringify(docFromRetrieve))
|
||||
docs.push(docFromRetrieve)
|
||||
result.description = docFromRetrieve.pageContent;
|
||||
result.traceId = docFromRetrieve.metadata?.traceId;
|
||||
continue;
|
||||
if (result.doId) {
|
||||
//TODO !!!!@Nex traceId should be the id of history/question!
|
||||
let docFromRetrieve = await retrieveDoc(result.doId)
|
||||
console.log(
|
||||
"doc from Retrieve:" +
|
||||
result.doId +
|
||||
" -->" +
|
||||
JSON.stringify(docFromRetrieve)
|
||||
)
|
||||
docs.push(docFromRetrieve)
|
||||
result.description = docFromRetrieve.pageContent
|
||||
result.traceId = docFromRetrieve.metadata?.traceId
|
||||
continue
|
||||
}
|
||||
if (!url) {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
|
||||
let htmlUrl = ""
|
||||
@ -419,8 +537,7 @@ export const searchIod = async (query: string, keywords: string[]) => {
|
||||
*/
|
||||
}
|
||||
|
||||
export const calculateTokenCount = function(str:string){
|
||||
const byteArray = new TextEncoder().encode(str);
|
||||
return byteArray.length;
|
||||
export const calculateTokenCount = function (str: string) {
|
||||
const byteArray = new TextEncoder().encode(str)
|
||||
return byteArray.length
|
||||
}
|
||||
|
||||
|
144
src/web/web.ts
144
src/web/web.ts
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user