Skip to content

Commit 22e0bfa

Browse files
committed
feat: add web search logic in backend
Signed-off-by: Bob Du <[email protected]>
1 parent 925c8c3 commit 22e0bfa

File tree

5 files changed

+105
-9
lines changed

5 files changed

+105
-9
lines changed

service/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@
2828
"common:cleanup": "rimraf node_modules && rimraf pnpm-lock.yaml"
2929
},
3030
"dependencies": {
31+
"@tavily/core": "^0.5.3",
3132
"axios": "^1.8.4",
32-
"dayjs": "^1.11.7",
33+
"dayjs": "^1.11.13",
3334
"dotenv": "^16.0.3",
3435
"express": "^5.1.0",
3536
"express-rate-limit": "^6.7.0",

service/pnpm-lock.yaml

Lines changed: 28 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

service/src/chatgpt/index.ts

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import * as dotenv from 'dotenv'
22
import OpenAI from 'openai'
33
import { HttpsProxyAgent } from 'https-proxy-agent'
4+
import { tavily } from '@tavily/core'
5+
import dayjs from 'dayjs'
46
import type { AuditConfig, KeyConfig, UserInfo } from '../storage/model'
57
import { Status, UsageResponse } from '../storage/model'
68
import { convertImageUrl } from '../utils/image'
@@ -10,7 +12,7 @@ import { getCacheApiKeys, getCacheConfig, getOriginConfig } from '../storage/con
1012
import { sendResponse } from '../utils'
1113
import { hasAnyRole, isNotEmptyString } from '../utils/is'
1214
import type { ModelConfig } from '../types'
13-
import { getChatByMessageId, updateRoomChatModel } from '../storage/mongo'
15+
import { getChatByMessageId, updateChatSearchQuery, updateChatSearchResult } from '../storage/mongo'
1416
import type { ChatMessage, RequestOptions } from './types'
1517

1618
dotenv.config()
@@ -49,17 +51,16 @@ export async function initApi(key: KeyConfig) {
4951
const processThreads: { userId: string; abort: AbortController; messageId: string }[] = []
5052

5153
async function chatReplyProcess(options: RequestOptions) {
54+
const globalConfig = await getCacheConfig()
5255
const model = options.room.chatModel
56+
const searchEnabled = options.room.searchEnabled
5357
const key = await getRandomApiKey(options.user, model)
5458
const userId = options.user._id.toString()
5559
const maxContextCount = options.user.advanced.maxContextCount ?? 20
5660
const messageId = options.messageId
5761
if (key == null || key === undefined)
5862
throw new Error('没有对应的apikeys配置。请再试一次 | No available apikeys configuration. Please try again.')
5963

60-
// Add Chat Record
61-
updateRoomChatModel(userId, options.room.roomId, model)
62-
6364
const { message, uploadFileKeys, parentMessageId, process, systemMessage, temperature, top_p } = options
6465

6566
try {
@@ -93,6 +94,52 @@ async function chatReplyProcess(options: RequestOptions) {
9394
content,
9495
})
9596

97+
const searchConfig = globalConfig.searchConfig
98+
if (searchConfig.enabled && searchConfig?.options?.apiKey && searchEnabled) {
99+
messages[0].content = `Before you formally answer the question, you have the option to search the web to get more context from the web.
100+
Please judge whether you need to search the Internet.
101+
If you need to search, please return the query word to be submitted to the search engine.
102+
If you do not need to search, the result will be empty.
103+
Please do not actually answer the question.
104+
Just wrap the result in <search_query></search_query>> and return it in plain text, such as <search_query>example search query</search_query> or <search_query></search_query>`
105+
const completion = await openai.chat.completions.create({
106+
model,
107+
messages,
108+
})
109+
let searchQuery: string = completion.choices[0].message.content
110+
const match = searchQuery.match(/<search_query>([\s\S]*)<\/search_query>/i)
111+
if (match)
112+
searchQuery = match[1].trim()
113+
else
114+
searchQuery = ''
115+
116+
if (searchQuery) {
117+
await updateChatSearchQuery(messageId, searchQuery)
118+
119+
const tvly = tavily({ apiKey: searchConfig.options?.apiKey })
120+
const response = await tvly.search(
121+
searchQuery,
122+
{
123+
includeRawContent: true,
124+
timeout: 300,
125+
},
126+
)
127+
128+
const searchResult = JSON.stringify(response)
129+
await updateChatSearchResult(messageId, searchResult)
130+
131+
messages.push({
132+
role: 'user',
133+
content: `Additional information from web searche engine.
134+
search query: <search_query>${searchQuery}</search_query>
135+
search result: <search_result>${searchResult}</search_result>
136+
current time: <date>${dayjs().format('YYYY-MM-DD HH:mm:ss')}</date>`,
137+
})
138+
}
139+
}
140+
141+
messages[0].content = systemMessage
142+
96143
// Create the chat completion with streaming
97144
const stream = await openai.chat.completions.create({
98145
model,

service/src/storage/model.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ export class ChatInfo {
120120
dateTime: number
121121
prompt: string
122122
images?: string[]
123+
searchQuery?: string
124+
searchResult?: string
123125
reasoning?: string
124126
response?: string
125127
status: Status = Status.Normal

service/src/storage/mongo.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,28 @@ export async function updateChat(chatId: string, reasoning: string, response: st
115115
await chatCol.updateOne(query, update)
116116
}
117117

118+
export async function updateChatSearchQuery(chatId: string, searchQuery: string) {
119+
const query = { _id: new ObjectId(chatId) }
120+
const update = {
121+
$set: {
122+
searchQuery,
123+
},
124+
}
125+
const result = await chatCol.updateOne(query, update)
126+
return result.modifiedCount > 0
127+
}
128+
129+
export async function updateChatSearchResult(chatId: string, searchResult: string) {
130+
const query = { _id: new ObjectId(chatId) }
131+
const update = {
132+
$set: {
133+
searchResult,
134+
},
135+
}
136+
const result = await chatCol.updateOne(query, update)
137+
return result.modifiedCount > 0
138+
}
139+
118140
export async function insertChatUsage(userId: ObjectId,
119141
roomId: number,
120142
chatId: ObjectId,

0 commit comments

Comments
 (0)