feat: add metering data
This commit is contained in:
		
							parent
							
								
									c50bb49b37
								
							
						
					
					
						commit
						7b8879a7a8
					
				| @ -88,4 +88,4 @@ | ||||
|   "resolutions": { | ||||
|     "@langchain/core": "0.1.45" | ||||
|   } | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -9,139 +9,128 @@ import { | ||||
|   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" | ||||
|   } | ||||
| ] | ||||
| import { NavLink, useParams } from "react-router-dom" | ||||
| import { useStoreMessageOption } from "@/store/option.tsx" | ||||
| import { useMemo } from "react" | ||||
| 
 | ||||
| interface DataType { | ||||
|   key: string | ||||
|   name: string | ||||
|   age: number | ||||
|   address: string | ||||
|   tags: number | ||||
|   doId: number | ||||
|   data_space: string | ||||
|   content: string | ||||
|   tokenCount: number | ||||
| } | ||||
| 
 | ||||
| const columns: TableProps<DataType>["columns"] = [ | ||||
|   { | ||||
|     title: "序号", | ||||
|     dataIndex: "key", | ||||
|     key: "name", | ||||
|     render: (text) => <a>{text}</a> | ||||
|     title: '序号', | ||||
|     key: 'index', | ||||
|     width: 100, | ||||
|     render: (_text, _record, index) => index + 1, // 索引从0开始,+1后从1显示
 | ||||
|   }, | ||||
|   { | ||||
|     title: "标识", | ||||
|     dataIndex: "age", | ||||
|     key: "age" | ||||
|     dataIndex: "doId", | ||||
|     key: "doId" | ||||
|   }, | ||||
|   { | ||||
|     title: "提供方", | ||||
|     dataIndex: "address", | ||||
|     key: "address" | ||||
|     dataIndex: "data_space", | ||||
|     key: "data_space" | ||||
|   }, | ||||
|   { | ||||
|     title: "token数量", | ||||
|     key: "tags", | ||||
|     dataIndex: "tags" | ||||
|     key: "tokenCount", | ||||
|     dataIndex: "tokenCount", | ||||
|     width: 100 | ||||
|   }, | ||||
|   { | ||||
|     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: "内容" | ||||
|     dataIndex: "content", | ||||
|     ellipsis: { | ||||
|       showTitle: false | ||||
|     }, | ||||
|     render: (content) => ( | ||||
|       <Tooltip placement="topLeft" title={content}> | ||||
|         {content} | ||||
|       </Tooltip> | ||||
|     ) | ||||
|   } | ||||
| ] | ||||
| 
 | ||||
| export const ListDetail = () => { | ||||
|   const { chatMessages } = useStoreMessageOption() | ||||
|   const { id } = useParams() | ||||
|   const record = useMemo( | ||||
|     () => chatMessages.find((item) => item.id === id), | ||||
|     [chatMessages] | ||||
|   ) | ||||
| 
 | ||||
|   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={data} | ||||
|         dataSource={modelData} | ||||
|         renderItem={(item) => ( | ||||
|           <List.Item> | ||||
|             <Card title={item.key}>{item.value}</Card> | ||||
| @ -150,43 +139,65 @@ export const ListDetail = () => { | ||||
|         style={{ marginBottom: "2rem" }} | ||||
|       /> | ||||
| 
 | ||||
|       <div> | ||||
|       <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.Text mark className="mr-1"> | ||||
|               <Typography.Paragraph  style={{ marginBottom: 0 }} className="mr-1"> | ||||
|                 {item.key} | ||||
|               </Typography.Text> | ||||
|               <Tooltip placement="topLeft" title={item.value}> | ||||
|               </Typography.Paragraph> | ||||
|               <Tooltip placement="topLeft" style={{ marginBottom: 0 }} title={item.value}> | ||||
|                 {item.value} | ||||
|               </Tooltip> | ||||
|             </List.Item> | ||||
|           )} | ||||
|           style={{ marginBottom: "1rem" }} | ||||
|         /> | ||||
|         <Table<DataType> columns={columns} dataSource={data1} /> | ||||
|       </div> | ||||
|         <Card title="数联网引用数据"> | ||||
|           <Table<DataType> columns={columns} dataSource={record.iodData} /> | ||||
|         </Card> | ||||
|       </Space> | ||||
| 
 | ||||
|       <div> | ||||
|       <Space direction="vertical" size={10}> | ||||
|         <Divider orientation="left">输出token详情</Divider> | ||||
|         <List | ||||
|           bordered | ||||
|           dataSource={outputTokenData} | ||||
|           dataSource={keywordsData} | ||||
|           header={<div>数联网搜索关键词</div>} | ||||
|           renderItem={(item) => ( | ||||
|             <List.Item style={{ justifyContent: "flex-start" }}> | ||||
|               <Typography.Text mark className="mr-1"> | ||||
|                 {item.key} | ||||
|               </Typography.Text> | ||||
|               <Tooltip placement="topLeft" title={item.value}> | ||||
|               <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> | ||||
|           )} | ||||
|         /> | ||||
|       </div> | ||||
|         <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> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| @ -1,24 +1,9 @@ | ||||
| import React from "react" | ||||
| import React, { useMemo } from "react" | ||||
| import { ChatMessage, useStoreMessageOption } from "@/store/option" | ||||
| import { Card, List, Table, Tag, Space, TableProps, Tooltip } from "antd" | ||||
| import { NavLink } from "react-router-dom" | ||||
| import { formatDate } from "@/utils/date" | ||||
| 
 | ||||
| const data = [ | ||||
|   { | ||||
|     key: "对话数量", | ||||
|     value: 2 | ||||
|   }, | ||||
|   { | ||||
|     key: "输出token数", | ||||
|     value: 2 | ||||
|   }, | ||||
|   { | ||||
|     key: "输入token数", | ||||
|     value: 2 | ||||
|   } | ||||
| ] | ||||
| 
 | ||||
| const columns: TableProps<ChatMessage>["columns"] = [ | ||||
|   { | ||||
|     title: "id", | ||||
| @ -49,6 +34,14 @@ const columns: TableProps<ChatMessage>["columns"] = [ | ||||
|     title: "思维链", | ||||
|     key: "thinkingChain", | ||||
|     dataIndex: "thinkingChain", | ||||
|     ellipsis: { | ||||
|       showTitle: false | ||||
|     }, | ||||
|     render: (responseContent) => ( | ||||
|       <Tooltip placement="topLeft" title={responseContent}> | ||||
|         {responseContent} | ||||
|       </Tooltip> | ||||
|     ), | ||||
|     width: "10%" | ||||
|   }, | ||||
| 
 | ||||
| @ -73,9 +66,8 @@ const columns: TableProps<ChatMessage>["columns"] = [ | ||||
|   }, | ||||
|   { | ||||
|     title: "数联网token", | ||||
|     dataIndex: "iodOutputToken", | ||||
|     key: "iodOutputToken", | ||||
|     render: (iodOutputToken) => <div>{iodOutputToken?.length}</div> | ||||
|     dataIndex: "iodDataTokenCount", | ||||
|     key: "iodDataTokenCount" | ||||
|   }, | ||||
|   { | ||||
|     title: "大模型token", | ||||
| @ -83,9 +75,7 @@ const columns: TableProps<ChatMessage>["columns"] = [ | ||||
|     dataIndex: "largeModelToken", | ||||
|     render: (_, record) => { | ||||
|       return ( | ||||
|         <div> | ||||
|           {record.iodInputToken?.length + record.iodOutputToken?.length} | ||||
|         </div> | ||||
|         <div>{record.modelInputTokenCount + record.modelOutputTokenCount}</div> | ||||
|       ) | ||||
|     } | ||||
|   }, | ||||
| @ -119,13 +109,49 @@ const columns: TableProps<ChatMessage>["columns"] = [ | ||||
| 
 | ||||
| export const MeteringDetail = () => { | ||||
|   const { chatMessages } = useStoreMessageOption() | ||||
|   console.log(chatMessages, "opppp") | ||||
| 
 | ||||
|   const data = useMemo( | ||||
|     () => [ | ||||
|       { | ||||
|         key: "对话数量", | ||||
|         value: chatMessages.length | ||||
|       }, | ||||
|       { | ||||
|         key: "数联网输入token数", | ||||
|         value: chatMessages.reduce((acc, cur) => { | ||||
|           for (const item of cur.iodKeywords) { | ||||
|             acc += item.length | ||||
|           } | ||||
|           return acc | ||||
|         }, 0) | ||||
|       }, | ||||
|       { | ||||
|         key: "数联网输出token数", | ||||
|         value: chatMessages.reduce((acc, cur) => acc + cur.iodDataTokenCount, 0) | ||||
|       }, | ||||
|       { | ||||
|         key: "大模型输入token数", | ||||
|         value: chatMessages.reduce( | ||||
|           (acc, cur) => acc + cur.modelInputTokenCount, | ||||
|           0 | ||||
|         ) | ||||
|       }, | ||||
|       { | ||||
|         key: "大模型输出token数", | ||||
|         value: chatMessages.reduce( | ||||
|           (acc, cur) => acc + cur.modelOutputTokenCount, | ||||
|           0 | ||||
|         ) | ||||
|       } | ||||
|     ], | ||||
|     [chatMessages] | ||||
|   ) | ||||
|   return ( | ||||
|     <div className="pt-[4rem]"> | ||||
|     <div className="p-4  pt-[4rem]"> | ||||
|       <List | ||||
|         grid={{ gutter: 16, column: 3 }} | ||||
|         grid={{ gutter: 16, column: 5 }} | ||||
|         dataSource={data} | ||||
|         split={false} | ||||
|         renderItem={(item) => ( | ||||
|           <List.Item> | ||||
|             <Card title={item.key}>{item.value}</Card> | ||||
|  | ||||
| @ -316,15 +316,14 @@ export const useMessageOption = () => { | ||||
|           .map((k) => k.trim()) | ||||
|       } | ||||
| 
 | ||||
|       const { prompt, webSources, iodSources } = await getSystemPromptForWeb( | ||||
|         query, | ||||
|         keywords, | ||||
|         webSearch, | ||||
|         iodSearch | ||||
|       ) | ||||
|       const { prompt, webSources, iodSources, iodData, iodDataTokenCount } = | ||||
|         await getSystemPromptForWeb(query, keywords, webSearch, iodSearch) | ||||
|       console.log("prompt:\n" + prompt) | ||||
|       setIsSearchingInternet(false) | ||||
|       chatMessage.prompt = prompt | ||||
|       chatMessage.iodKeywords = keywords | ||||
|       chatMessage.iodData = iodData | ||||
|       chatMessage.iodDataTokenCount = iodDataTokenCount | ||||
| 
 | ||||
|       //  message = message.trim().replaceAll("\n", " ")
 | ||||
| 
 | ||||
| @ -485,12 +484,16 @@ export const useMessageOption = () => { | ||||
|       setIsProcessing(false) | ||||
|       setStreaming(false) | ||||
| 
 | ||||
|       chatMessage.relatedDataCount = keywords.length | ||||
|       chatMessage.modelInputTokenCount = generationInfo?.prompt_eval_count ?? 0 | ||||
|       chatMessage.modelOutputTokenCount = generationInfo?.eval_count ?? 0 | ||||
|       chatMessage.model = generationInfo?.model ?? "" | ||||
|       chatMessage.relatedDataCount = iodData?.length ?? 0 | ||||
|       chatMessage.timeTaken = timetaken | ||||
|       chatMessage.date = reasoningStartTime | ||||
|       const { think, content } = responseResolver(fullText) | ||||
|       chatMessage.thinkingChain = think | ||||
|       chatMessage.responseContent = content | ||||
|       chatMessage.modelResponseContent = fullText | ||||
|       setChatMessages([...chatMessages, chatMessage]) | ||||
|     } catch (e) { | ||||
|       const errorSave = await saveMessageOnError({ | ||||
|  | ||||
| @ -46,14 +46,24 @@ export type ChatMessage = { | ||||
|   iodInputToken: string | ||||
|   // 数联网输出token
 | ||||
|   iodOutputToken: string | ||||
|   // 大模型输入token
 | ||||
|   modelInputToken: string | ||||
|   // 大模型输出token
 | ||||
|   modelOutputToken: string | ||||
|   // 大模型输入token数量
 | ||||
|   modelInputTokenCount: number | ||||
|   // 大模型输出token数量
 | ||||
|   modelOutputTokenCount: number | ||||
|   // 日期
 | ||||
|   date: Date | ||||
|   // 耗时
 | ||||
|   timeTaken: number | ||||
|   // 大模型回答的全部内容
 | ||||
|   modelResponseContent: string | ||||
|   // iod的全部内容的token数量
 | ||||
|   iodDataTokenCount: number | ||||
|   // iod返回的数据
 | ||||
|   iodData: any[] | ||||
|   // iod keywords
 | ||||
|   iodKeywords: string[] | ||||
|   // 模型
 | ||||
|   model: string | ||||
| } | ||||
| 
 | ||||
| type State = { | ||||
|  | ||||
| @ -5,4 +5,5 @@ export type IodRegistryEntry = { | ||||
|   pdf_url?: string | ||||
|   description: string | ||||
|   content?: string | ||||
|   data_space?: string | ||||
| } | ||||
|  | ||||
| @ -91,7 +91,13 @@ export async function localIodSearch( | ||||
|     ) | ||||
|   ).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\// | ||||
|  | ||||
| @ -95,13 +95,17 @@ export const getSystemPromptForWeb = async ( | ||||
|       //   )
 | ||||
|       //   .join("\n")
 | ||||
|     } | ||||
|     const iod_search_results = iodSearchResults | ||||
|       .map((res) => ({ | ||||
|         doId: res.doId, | ||||
|         name: res.name, | ||||
|         url: res.url, | ||||
|         content: res.content || res.description | ||||
|       })) | ||||
|     const _iodSearchResults = iodSearchResults | ||||
|         .map((res) => ({ | ||||
|           doId: res.doId, | ||||
|           name: res.name, | ||||
|           url: res.url, | ||||
|           data_space: res.data_space, | ||||
|           tokenCount: (res.content || res.description)?.length ?? 0, | ||||
|           content: res.content || res.description | ||||
|         })) | ||||
| 
 | ||||
|     const iod_search_results = _iodSearchResults | ||||
|       .map( | ||||
|         (result, idx) => | ||||
|           `<result doId="${result.doId}" name="${result.name}" source="${result.url}" id="${idx + 1}">${result.content}</result>` | ||||
| @ -135,7 +139,9 @@ export const getSystemPromptForWeb = async ( | ||||
|           type: "url" | ||||
|         } | ||||
|       }), | ||||
|       iodSources: iodSearchResults | ||||
|       iodSources: iodSearchResults, | ||||
|       iodData: _iodSearchResults, | ||||
|       iodDataTokenCount: _iodSearchResults.reduce((acc, cur) => (acc + cur.content.length), 0) | ||||
|     } | ||||
|   } catch (e) { | ||||
|     console.error(e) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user