9 Commits

Author SHA1 Message Date
zhaoweijie
c5fa739a95 feat: change token get 2025-02-24 10:17:05 +08:00
CaiHQ
70d1f40333 fix: file name lower case 2025-02-24 10:05:06 +08:00
CaiHQ
2866bcc7af feat: mock 4 button 2025-02-24 10:02:12 +08:00
CaiHQ
2a57034c9d feat: change filename 2025-02-24 10:02:12 +08:00
Nex Zhu
79a03ab6fc fix: file name case 2025-02-24 09:23:19 +08:00
Nex Zhu
50f9e4354f fix 2025-02-24 08:36:42 +08:00
Nex Zhu
8f27ca2e4e fix: fix no meteringEntry date when no cot, and style 2025-02-24 08:30:37 +08:00
Nex Zhu
ce333714b7 style: revert locale json format 2025-02-23 22:22:43 +08:00
zhaoweijie
7b8879a7a8 feat: add metering data 2025-02-23 13:02:32 +08:00
17 changed files with 537 additions and 360 deletions

BIN
bun.lockb Normal file → Executable file

Binary file not shown.

View File

@@ -9,7 +9,12 @@ import {
Pen, Pen,
PlayIcon, PlayIcon,
RotateCcw, RotateCcw,
Square Square,
Star,
ThumbsUp,
ThumbsDown,
MessageSquareShare,
ArrowUpSquare
} from "lucide-react" } from "lucide-react"
import { EditMessageForm } from "./EditMessageForm" import { EditMessageForm } from "./EditMessageForm"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
@@ -316,6 +321,51 @@ export const PlaygroundMessage = (props: Props) => {
</button> </button>
</Tooltip> </Tooltip>
)} )}
{ (
<Tooltip title="收藏">
<button
aria-label="收藏"
className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
<Star className="w-3 h-3 text-gray-400 group-hover:text-gray-500" />
</button>
</Tooltip>
)}
{ (
<Tooltip title="发布语用">
<button
aria-label="发布语用"
className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
<ArrowUpSquare className="w-3 h-3 text-gray-400 group-hover:text-gray-500" />
</button>
</Tooltip>
)}
{ (
<Tooltip title="发布对话">
<button
aria-label="发布对话"
className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
<MessageSquareShare className="w-3 h-3 text-gray-400 group-hover:text-gray-500" />
</button>
</Tooltip>
)}
{ (
<Tooltip title="点赞">
<button
aria-label="点赞"
className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
<ThumbsUp className="w-3 h-3 text-gray-400 group-hover:text-gray-500" />
</button>
</Tooltip>
)}
{ (
<Tooltip title="点踩">
<button
aria-label="点踩"
className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
<ThumbsDown className="w-3 h-3 text-gray-400 group-hover:text-gray-500" />
</button>
</Tooltip>
)}
</div> </div>
) : ( ) : (
// add invisible div to prevent layout shift // add invisible div to prevent layout shift

View File

@@ -5,7 +5,7 @@ import {
ChevronRight, ChevronRight,
CogIcon, CogIcon,
ComputerIcon, ComputerIcon,
Slice, GaugeCircle,
GithubIcon, GithubIcon,
PanelLeftIcon, PanelLeftIcon,
ZapIcon ZapIcon
@@ -245,7 +245,7 @@ export const Header: React.FC<Props> = ({
<NavLink <NavLink
to="/metering" to="/metering"
className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors"> className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
<Slice className="w-6 h-6" /> <GaugeCircle className="w-6 h-6" />
</NavLink> </NavLink>
</Tooltip> </Tooltip>
</div> </div>

View File

@@ -1,192 +0,0 @@
import {
Card,
List,
Table,
Tag,
Space,
TableProps,
Divider,
Typography,
Tooltip
} from "antd"
import { NavLink } from "react-router-dom"
const data = [
{
key: "输出token数",
value: 2
},
{
key: "输入token数",
value: 2
},
{
key: "模型",
value: "xxx"
}
]
const inputTokenData = [
{
key: "关键词提示",
value: "xxx"
},
{
key: "问题",
value: "xxx"
},
{
key: "数联网引用数据",
value: "xxx"
},
{
key: "提供方",
value: "xxx"
},
{
key: "token数量",
value: 2
},
{
key: "内容",
value: "xxx"
}
]
const outputTokenData = [
{
key: "类型",
value: "xxx"
},
{
key: "来源",
value: "xxx"
},
{
key: "token数量",
value: 2
},
{
key: "内容",
value: "xxx"
}
]
interface DataType {
key: string
name: string
age: number
address: string
tags: number
content: string
}
const columns: TableProps<DataType>["columns"] = [
{
title: "序号",
dataIndex: "key",
key: "name",
render: (text) => <a>{text}</a>
},
{
title: "标识",
dataIndex: "age",
key: "age"
},
{
title: "提供方",
dataIndex: "address",
key: "address"
},
{
title: "token数量",
key: "tags",
dataIndex: "tags"
},
{
title: "内容",
key: "content",
dataIndex: "content"
}
]
const data1: DataType[] = [
{
key: "1",
name: "John Brown",
age: 32,
address: "New York No. 1 Lake Park",
tags: 2,
content: "内容"
},
{
key: "2",
name: "Jim Green",
age: 42,
address: "London No. 1 Lake Park",
tags: 3,
content: "内容"
},
{
key: "3",
name: "Joe Black",
age: 32,
address: "Sydney No. 1 Lake Park",
tags: 3,
content: "内容"
}
]
export const ListDetail = () => {
return (
<div className="p-[1rem] pt-[4rem]">
<List
grid={{ gutter: 16, column: 3 }}
dataSource={data}
renderItem={(item) => (
<List.Item>
<Card title={item.key}>{item.value}</Card>
</List.Item>
)}
style={{ marginBottom: "2rem" }}
/>
<div>
<Divider orientation="left">token详情</Divider>
<List
bordered
dataSource={inputTokenData}
renderItem={(item) => (
<List.Item style={{ justifyContent: "flex-start" }}>
<Typography.Text mark className="mr-1">
{item.key}
</Typography.Text>
<Tooltip placement="topLeft" title={item.value}>
{item.value}
</Tooltip>
</List.Item>
)}
style={{ marginBottom: "1rem" }}
/>
<Table<DataType> columns={columns} dataSource={data1} />
</div>
<div>
<Divider orientation="left">token详情</Divider>
<List
bordered
dataSource={outputTokenData}
renderItem={(item) => (
<List.Item style={{ justifyContent: "flex-start" }}>
<Typography.Text mark className="mr-1">
{item.key}
</Typography.Text>
<Tooltip placement="topLeft" title={item.value}>
{item.value}
</Tooltip>
</List.Item>
)}
/>
</div>
</div>
)
}

View File

@@ -1,30 +1,15 @@
import React from "react" import React, { useMemo } from "react"
import { ChatMessage, useStoreMessageOption } from "@/store/option" import { MeteringEntry, useStoreMessageOption } from "@/store/option"
import { Card, List, Table, Tag, Space, TableProps, Tooltip } from "antd" import { Card, List, Table, Tag, Space, TableProps, Tooltip } from "antd"
import { NavLink } from "react-router-dom" import { NavLink } from "react-router-dom"
import { formatDate } from "@/utils/date" import { formatDate } from "@/utils/date"
const data = [ const columns: TableProps<MeteringEntry>["columns"] = [
{ {
key: "对话数量", title: '序号',
value: 2 key: 'index',
}, width: 100,
{ render: (_text, _record, index) => index + 1, // 索引从0开始+1后从1显示
key: "输出token数",
value: 2
},
{
key: "输入token数",
value: 2
}
]
const columns: TableProps<ChatMessage>["columns"] = [
{
title: "id",
dataIndex: "id",
key: "id",
width: "13%"
}, },
{ {
title: "问题", title: "问题",
@@ -47,8 +32,16 @@ const columns: TableProps<ChatMessage>["columns"] = [
}, },
{ {
title: "思维链", title: "思维链",
key: "thinkingChain", key: "cot",
dataIndex: "thinkingChain", dataIndex: "cot",
ellipsis: {
showTitle: false
},
render: (responseContent) => (
<Tooltip placement="topLeft" title={responseContent}>
{responseContent}
</Tooltip>
),
width: "10%" width: "10%"
}, },
@@ -73,9 +66,8 @@ const columns: TableProps<ChatMessage>["columns"] = [
}, },
{ {
title: "数联网token", title: "数联网token",
dataIndex: "iodOutputToken", dataIndex: "iodTokenCount",
key: "iodOutputToken", key: "iodTokenCount"
render: (iodOutputToken) => <div>{iodOutputToken?.length}</div>
}, },
{ {
title: "大模型token", title: "大模型token",
@@ -83,9 +75,7 @@ const columns: TableProps<ChatMessage>["columns"] = [
dataIndex: "largeModelToken", dataIndex: "largeModelToken",
render: (_, record) => { render: (_, record) => {
return ( return (
<div> <div>{record.modelInputTokenCount + record.modelOutputTokenCount}</div>
{record.iodInputToken?.length + record.iodOutputToken?.length}
</div>
) )
} }
}, },
@@ -94,7 +84,7 @@ const columns: TableProps<ChatMessage>["columns"] = [
dataIndex: "date", dataIndex: "date",
key: "date", key: "date",
render: (date) => { render: (date) => {
return <div>{formatDate(date)}</div> return <div>{formatDate(date ?? new Date())}</div>
} }
}, },
{ {
@@ -118,14 +108,50 @@ const columns: TableProps<ChatMessage>["columns"] = [
] ]
export const MeteringDetail = () => { export const MeteringDetail = () => {
const { chatMessages } = useStoreMessageOption() const { meteringEntries } = useStoreMessageOption()
console.log(chatMessages, "opppp")
const data = useMemo(
() => [
{
key: "对话数量",
value: meteringEntries.length
},
{
key: "数联网输入token数",
value: meteringEntries.reduce((acc, cur) => {
for (const item of cur.iodKeywords) {
acc += item.length
}
return acc
}, 0)
},
{
key: "数联网输出token数",
value: meteringEntries.reduce((acc, cur) => acc + cur.iodTokenCount, 0)
},
{
key: "大模型输入token数",
value: meteringEntries.reduce(
(acc, cur) => acc + cur.modelInputTokenCount,
0
)
},
{
key: "大模型输出token数",
value: meteringEntries.reduce(
(acc, cur) => acc + cur.modelOutputTokenCount,
0
)
}
],
[meteringEntries]
)
return ( return (
<div className="pt-[4rem]"> <div className="p-4 pt-[4rem]">
<List <List
grid={{ gutter: 16, column: 3 }} grid={{ gutter: 16, column: 5 }}
dataSource={data} dataSource={data}
split={false}
renderItem={(item) => ( renderItem={(item) => (
<List.Item> <List.Item>
<Card title={item.key}>{item.value}</Card> <Card title={item.key}>{item.value}</Card>
@@ -133,7 +159,7 @@ export const MeteringDetail = () => {
)} )}
/> />
<Table<ChatMessage> columns={columns} dataSource={chatMessages} /> <Table<MeteringEntry> columns={columns} dataSource={meteringEntries} />
</div> </div>
) )
} }

View File

@@ -0,0 +1,203 @@
import {
Card,
List,
Table,
Tag,
Space,
TableProps,
Divider,
Typography,
Tooltip
} from "antd"
import { NavLink, useParams } from "react-router-dom"
import { useStoreMessageOption } from "@/store/option.tsx"
import { useMemo } from "react"
interface DataType {
key: string
name: string
doId: number
data_space: string
content: string
tokenCount: number
}
const columns: TableProps<DataType>["columns"] = [
{
title: '序号',
key: 'index',
width: 100,
render: (_text, _record, index) => index + 1, // 索引从0开始+1后从1显示
},
{
title: "标识",
dataIndex: "doId",
key: "doId"
},
{
title: "提供方",
dataIndex: "data_space",
key: "data_space"
},
{
title: "token数量",
key: "tokenCount",
dataIndex: "tokenCount",
width: 100
},
{
title: "内容",
key: "content",
dataIndex: "content",
ellipsis: {
showTitle: false
},
render: (content) => (
<Tooltip placement="topLeft" title={content}>
{content}
</Tooltip>
)
}
]
export const ListDetail = () => {
const { meteringEntries } = useStoreMessageOption()
const { id } = useParams()
const record = useMemo(
() => meteringEntries.find((item) => item.id === id),
[meteringEntries]
)
const modelData = useMemo(
() => [
{
key: "大模型输入token数",
value: record.modelInputTokenCount
},
{
key: "大模型输出token数",
value: record.modelOutputTokenCount
},
{
key: "模型",
value: record.model
}
],
[record]
)
const inputTokenData = useMemo(
() => [
{
key: "内容:",
value: record.queryContent
},
{
key: "token数量:",
value: record.queryContent.length
}
],
[record]
)
const keywordsData = useMemo(
() => [
{
key: "token数量:",
value: record.iodKeywords.reduce((acc, cur) => acc + cur.length, 0)
},
{
key: "内容:",
value: record.iodKeywords.join(", ")
}
],
[record]
)
const responseContent = useMemo(
() => [
{
key: "token数量:",
value: record.modelResponseContent.length
},
{
key: "内容:",
value: record.modelResponseContent
}
],
[record]
)
return (
<div className="p-[1rem] pt-[4rem]">
<List
grid={{ gutter: 16, column: 3 }}
dataSource={modelData}
renderItem={(item) => (
<List.Item>
<Card title={item.key}>{item.value}</Card>
</List.Item>
)}
style={{ marginBottom: "2rem" }}
/>
<Space direction="vertical" size={10}>
<Divider orientation="left">token详情</Divider>
<List
bordered
header={<div></div>}
dataSource={inputTokenData}
renderItem={(item) => (
<List.Item style={{ justifyContent: "flex-start" }}>
<Typography.Paragraph style={{ marginBottom: 0 }} className="mr-1">
{item.key}
</Typography.Paragraph>
<Tooltip placement="topLeft" style={{ marginBottom: 0 }} title={item.value}>
{item.value}
</Tooltip>
</List.Item>
)}
style={{ marginBottom: "1rem" }}
/>
<Card title="数联网引用数据">
<Table<DataType> columns={columns} dataSource={record.iodData} />
</Card>
</Space>
<Space direction="vertical" size={10}>
<Divider orientation="left">token详情</Divider>
<List
bordered
dataSource={keywordsData}
header={<div></div>}
renderItem={(item) => (
<List.Item style={{ justifyContent: "flex-start" }}>
<Typography.Text className="mr-1" style={{ marginBottom: 0 }}>{item.key}</Typography.Text>
<Tooltip style={{ marginBottom: 0 }} placement="topLeft" title={item.value}>
{item.value}
</Tooltip>
</List.Item>
)}
/>
<List
bordered
dataSource={responseContent}
header={<div></div>}
renderItem={(item) => (
<List.Item
style={{ justifyContent: "flex-start", alignItems: "center" }}>
<Typography.Text
className="mt-0 mr-1 w-20"
style={{ marginBottom: 0 }}>
{item.key}
</Typography.Text>
<Typography.Paragraph
style={{ marginBottom: 0 }}
ellipsis={{ tooltip: item.value, rows: 2, expandable: true }}>
{item.value}
</Typography.Paragraph>
</List.Item>
)}
/>
</Space>
</div>
)
}

View File

@@ -8,7 +8,7 @@ import {
promptForRag, promptForRag,
systemPromptForNonRagOption systemPromptForNonRagOption
} from "~/services/ollama" } from "~/services/ollama"
import { type ChatHistory, ChatMessage, type Message } from "~/store/option" import type { ChatHistory, Message, MeteringEntry } from "~/store/option"
import { SystemMessage } from "@langchain/core/messages" import { SystemMessage } from "@langchain/core/messages"
import { useStoreMessageOption } from "~/store/option" import { useStoreMessageOption } from "~/store/option"
import { import {
@@ -55,8 +55,8 @@ export const useMessageOption = () => {
const { const {
history, history,
setHistory, setHistory,
chatMessages, meteringEntries,
setChatMessages, setMeteringEntries,
setStreaming, setStreaming,
streaming, streaming,
setIsFirstMessage, setIsFirstMessage,
@@ -114,23 +114,24 @@ export const useMessageOption = () => {
setWebSearch(true) setWebSearch(true)
} }
} }
// 从最后的结果中解析出 思维链 和 结果
// 从最后的结果中解析出 思维链 (Chain-of-Thought) 和 结果
const responseResolver = (msg: string) => { const responseResolver = (msg: string) => {
const thinkStart = msg.indexOf("<think>") const cotStart = msg.indexOf("<think>")
const thinkEnd = msg.indexOf("</think>") const cotEnd = msg.indexOf("</think>")
let think = "" let cot = ""
let content = "" let content = ""
if (thinkStart > -1 && thinkEnd > -1) { if (cotStart > -1 && cotEnd > -1) {
think = msg.substring(thinkStart + 7, thinkEnd) cot = msg.substring(cotStart + 7, cotEnd)
content = msg.substring(thinkEnd + 8) content = msg.substring(cotEnd + 8)
} else { } else {
content = msg content = msg
} }
// 去掉换行符 // 去掉换行符
think = think.replace(/\n/g, "") cot = cot.replace(/\n/g, "")
content = content.replace(/\n/g, "") content = content.replace(/\n/g, "")
return { return {
think, cot: cot,
content content
} }
} }
@@ -190,10 +191,11 @@ export const useMessageOption = () => {
}) })
let newMessage: Message[] = [] let newMessage: Message[] = []
let generateMessageId = generateID() let generateMessageId = generateID()
const chatMessage: ChatMessage = { const meter: MeteringEntry = {
id: generateMessageId, id: generateMessageId,
queryContent: message queryContent: message,
} as ChatMessage date: new Date()
} as MeteringEntry
if (!isRegenerate) { if (!isRegenerate) {
newMessage = [ newMessage = [
@@ -316,15 +318,14 @@ export const useMessageOption = () => {
.map((k) => k.trim()) .map((k) => k.trim())
} }
const { prompt, webSources, iodSources } = await getSystemPromptForWeb( const { prompt, webSources, iodSources, iodSearchResults: iodData, iodTokenCount } =
query, await getSystemPromptForWeb(query, keywords, webSearch, iodSearch)
keywords,
webSearch,
iodSearch
)
console.log("prompt:\n" + prompt) console.log("prompt:\n" + prompt)
setIsSearchingInternet(false) setIsSearchingInternet(false)
chatMessage.prompt = prompt meter.prompt = prompt
meter.iodKeywords = keywords
meter.iodData = iodData
meter.iodTokenCount = iodTokenCount
// message = message.trim().replaceAll("\n", " ") // message = message.trim().replaceAll("\n", " ")
@@ -385,6 +386,7 @@ export const useMessageOption = () => {
} }
) )
let count = 0 let count = 0
const chatStartTime = new Date()
let reasoningStartTime: Date | undefined = undefined let reasoningStartTime: Date | undefined = undefined
let reasoningEndTime: Date | undefined = undefined let reasoningEndTime: Date | undefined = undefined
let apiReasoning = false let apiReasoning = false
@@ -485,13 +487,22 @@ export const useMessageOption = () => {
setIsProcessing(false) setIsProcessing(false)
setStreaming(false) setStreaming(false)
chatMessage.relatedDataCount = keywords.length // Save metering entry
chatMessage.timeTaken = timetaken const { cot, content } = responseResolver(fullText)
chatMessage.date = reasoningStartTime setMeteringEntries([ {
const { think, content } = responseResolver(fullText) ...meter,
chatMessage.thinkingChain = think modelInputTokenCount: prompt.length,
chatMessage.responseContent = content modelOutputTokenCount: fullText.length,
setChatMessages([...chatMessages, chatMessage]) model: ollama.modelName,
relatedDataCount: iodData?.length ?? 0,
timeTaken: new Date().getTime() - meter.date.getTime(),
date: chatStartTime,
cot,
responseContent: content,
modelResponseContent: fullText,
},
...meteringEntries,
])
} catch (e) { } catch (e) {
const errorSave = await saveMessageOnError({ const errorSave = await saveMessageOnError({
e, e,

View File

@@ -72,7 +72,7 @@ export const pageAssistModel = async ({
configuration: { configuration: {
apiKey: providerInfo.apiKey || "temp", apiKey: providerInfo.apiKey || "temp",
baseURL: providerInfo.baseUrl || "" baseURL: providerInfo.baseUrl || ""
} },
}) as any }) as any
} }
@@ -85,7 +85,7 @@ export const pageAssistModel = async ({
configuration: { configuration: {
apiKey: providerInfo.apiKey || "temp", apiKey: providerInfo.apiKey || "temp",
baseURL: providerInfo.baseUrl || "" baseURL: providerInfo.baseUrl || ""
} },
}) as any }) as any
} }
return new ChatOllama({ return new ChatOllama({

View File

@@ -30,39 +30,13 @@ export type ChatHistory = {
messageType?: string messageType?: string
}[] }[]
export type ChatMessage = {
id: string
// 问题
queryContent: string
// 提示词全文
prompt: string
// 思维链(只有深度思考时有)
thinkingChain?: string
// 回答
responseContent: string
// 关联数据个数
relatedDataCount: number
// 数联网输入token
iodInputToken: string
// 数联网输出token
iodOutputToken: string
// 大模型输入token
modelInputToken: string
// 大模型输出token
modelOutputToken: string
// 日期
date: Date
// 耗时
timeTaken: number
}
type State = { type State = {
messages: Message[] messages: Message[]
setMessages: (messages: Message[]) => void setMessages: (messages: Message[]) => void
history: ChatHistory history: ChatHistory
setHistory: (history: ChatHistory) => void setHistory: (history: ChatHistory) => void
chatMessages: ChatMessage[] meteringEntries: MeteringEntry[]
setChatMessages: (chatMessages: ChatMessage[]) => void setMeteringEntries: (meteringEntries: MeteringEntry[]) => void
streaming: boolean streaming: boolean
setStreaming: (streaming: boolean) => void setStreaming: (streaming: boolean) => void
isFirstMessage: boolean isFirstMessage: boolean
@@ -105,13 +79,49 @@ type State = {
setUseOCR: (useOCR: boolean) => void setUseOCR: (useOCR: boolean) => void
} }
export type MeteringEntry = {
id: string
// 问题
queryContent: string
// 提示词全文
prompt: string
// 思维链(只有深度思考时有)
cot?: string
// 回答
responseContent: string
// 关联数据个数
relatedDataCount: number
// 数联网输入token
iodInputToken: string
// 数联网输出token
iodOutputToken: string
// 大模型输入token数量
modelInputTokenCount: number
// 大模型输出token数量
modelOutputTokenCount: number
// 日期
date: Date
// 耗时
timeTaken: number
// 大模型回答的全部内容
modelResponseContent: string
// iod的全部内容的token数量
iodTokenCount: number
// iod返回的数据
iodData: any[]
// iod keywords
iodKeywords: string[]
// 模型
model: string
}
export const useStoreMessageOption = create<State>((set) => ({ export const useStoreMessageOption = create<State>((set) => ({
messages: [], messages: [],
setMessages: (messages) => set({ messages }), setMessages: (messages) => set({ messages }),
history: [], history: [],
setHistory: (history) => set({ history }), setHistory: (history) => set({ history }),
chatMessages: [], meteringEntries: [],
setChatMessages: (chatMessages) => set({ chatMessages }), setMeteringEntries: (meteringEntries) => set({ meteringEntries }),
streaming: false, streaming: false,
setStreaming: (streaming) => set({ streaming }), setStreaming: (streaming) => set({ streaming }),
isFirstMessage: true, isFirstMessage: true,

View File

@@ -5,4 +5,5 @@ export type IodRegistryEntry = {
pdf_url?: string pdf_url?: string
description: string description: string
content?: string content?: string
data_space?: string
} }

21
src/web/1.json Normal file
View File

@@ -0,0 +1,21 @@
{
"action": "executeContract",
"contractID": "BDBrowser",
"operation": "sendRequestDirectly",
"arg": {
"id": "670E241C9937B3537047C87053E3AA36",
"doipUrl": "tcp://reg01.public.internetofdata.cn:21037",
"op": "Search",
"attributes": {
"offset": 2100,
"count": 5,
"bodyBase64Encoded": false,
"searchMode": [
{ "key": "data_type", "type": "MUST", "value": "paper" },
{ "key": "title", "type": "MUST", "value": "Number_1" },
{ "key": "description", "type": "MUST", "value": "Number_1" }
]
},
"body": ""
}
}

33
src/web/2.ts Normal file
View File

@@ -0,0 +1,33 @@
const ollama = await pageAssistModel({
model: selectedModel!,
baseUrl: cleanUrl(url),
keepAlive:
currentChatModelSettings?.keepAlive ?? userDefaultModelSettings?.keepAlive,
temperature:
currentChatModelSettings?.temperature ??
userDefaultModelSettings?.temperature,
topK: currentChatModelSettings?.topK ?? userDefaultModelSettings?.topK,
topP: currentChatModelSettings?.topP ?? userDefaultModelSettings?.topP,
numCtx: currentChatModelSettings?.numCtx ?? userDefaultModelSettings?.numCtx,
seed: currentChatModelSettings?.seed,
numGpu: currentChatModelSettings?.numGpu ?? userDefaultModelSettings?.numGpu,
numPredict:
currentChatModelSettings?.numPredict ??
userDefaultModelSettings?.numPredict,
useMMap:
currentChatModelSettings?.useMMap ?? userDefaultModelSettings?.useMMap,
minP: currentChatModelSettings?.minP ?? userDefaultModelSettings?.minP,
repeatLastN:
currentChatModelSettings?.repeatLastN ??
userDefaultModelSettings?.repeatLastN,
repeatPenalty:
currentChatModelSettings?.repeatPenalty ??
userDefaultModelSettings?.repeatPenalty,
tfsZ: currentChatModelSettings?.tfsZ ?? userDefaultModelSettings?.tfsZ,
numKeep:
currentChatModelSettings?.numKeep ?? userDefaultModelSettings?.numKeep,
numThread:
currentChatModelSettings?.numThread ?? userDefaultModelSettings?.numThread,
useMlock:
currentChatModelSettings?.useMlock ?? userDefaultModelSettings?.useMlock
})

View File

@@ -91,7 +91,13 @@ export async function localIodSearch(
) )
).flat() ).flat()
return results // results 根据 doId 去重
const map = new Map<string, IodRegistryEntry>()
for (const r of results) {
map.set(r.doId, r)
}
return Array.from(map.values())
} }
const ARXIV_URL_PATTERN = /^https?:\/\/arxiv\.org\// const ARXIV_URL_PATTERN = /^https?:\/\/arxiv\.org\//

View File

@@ -95,13 +95,17 @@ export const getSystemPromptForWeb = async (
// ) // )
// .join("\n") // .join("\n")
} }
const iod_search_results = iodSearchResults const _iodSearchResults = iodSearchResults
.map((res) => ({ .map((res) => ({
doId: res.doId, doId: res.doId,
name: res.name, name: res.name,
url: res.url, url: res.url,
content: res.content || res.description data_space: res.data_space,
content: res.content || res.description,
tokenCount: (res.content || res.description)?.length ?? 0,
})) }))
const iod_search_results = _iodSearchResults
.map( .map(
(result, idx) => (result, idx) =>
`<result doId="${result.doId}" name="${result.name}" source="${result.url}" id="${idx + 1}">${result.content}</result>` `<result doId="${result.doId}" name="${result.name}" source="${result.url}" id="${idx + 1}">${result.content}</result>`
@@ -135,14 +139,18 @@ export const getSystemPromptForWeb = async (
type: "url" type: "url"
} }
}), }),
iodSources: iodSearchResults iodSources: iodSearchResults,
iodSearchResults: _iodSearchResults,
iodTokenCount: _iodSearchResults.reduce((acc, cur) => (acc + cur.content.length), 0)
} }
} catch (e) { } catch (e) {
console.error(e) console.error(e)
return { return {
prompt: "", prompt: "",
webSources: [], webSources: [],
iodSources: [] iodSources: [],
iodSearchResults: [],
iodTokenCount: 0,
} }
} }
} }