Skip to content

Commit

Permalink
支持联网搜索能力
Browse files Browse the repository at this point in the history
  • Loading branch information
Vinlic committed Dec 10, 2024
1 parent d76160f commit 74e5bf4
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 10 deletions.
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
![](https://img.shields.io/github/forks/llm-red-team/deepseek-free-api.svg)
![](https://img.shields.io/docker/pulls/vinlic/deepseek-free-api.svg)

支持高速流式输出、支持多轮对话、支持R1深度思考和静默深度思考,零配置部署,多路token支持。
支持高速流式输出、支持多轮对话、支持联网搜索、支持R1深度思考和静默深度思考,零配置部署,多路token支持。

与ChatGPT接口完全兼容。

Expand Down Expand Up @@ -76,6 +76,10 @@ https://udify.app/chat/IWOnEupdZcfCN0y7

![多轮对话](./doc/example-2.png)

### 联网搜索Demo

![联网搜索](./doc/example-3.png)

## 接入准备

[DeepSeek](https://chat.deepseek.com/) 获取userToken value
Expand Down Expand Up @@ -241,7 +245,8 @@ Authorization: Bearer [userToken value]
// model名称
// 默认:deepseek
// 深度思考:deepseek-think 或 deepseek-r1
// 静默深度思考(不输出思考过程):deepseek-think-silent 或 deepseek-r1-silent
// 联网搜索:deepseek-search
// 静默模式(不输出思考过程或联网搜索结果):deepseek-think-silent 或 deepseek-r1-silent 或 deepseek-search-silent
// 深度思考但思考过程使用<details>可折叠标签包裹(需要页面支持显示):deepseek-think-fold 或 deepseek-r1-fold
"model": "deepseek",
// 默认多轮对话基于消息合并实现,某些场景可能导致能力下降且受单轮最大token数限制
Expand Down
Binary file added doc/example-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "deepseek-free-api",
"version": "0.0.9",
"version": "0.0.10",
"description": "DeepSeek Free API Server",
"type": "module",
"main": "dist/index.js",
Expand Down
49 changes: 42 additions & 7 deletions src/api/controllers/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ async function createCompletion(
// 请求流
const token = await acquireToken(refreshToken);

const isSearchModel = model.includes('search') || prompt.includes('联网搜索');
const isThinkingModel = model.includes('think') || model.includes('r1') || prompt.includes('深度思考');

if (isThinkingModel) {
Expand All @@ -219,6 +220,7 @@ async function createCompletion(
parent_message_id: refParentMsgId || null,
prompt,
ref_file_ids: [],
search_enabled: isSearchModel,
thinking_enabled: isThinkingModel
},
{
Expand Down Expand Up @@ -301,6 +303,7 @@ async function createCompletionStream(
// 解析引用对话ID
const [refSessionId, refParentMsgId] = refConvId?.split('@') || [];

const isSearchModel = model.includes('search') || prompt.includes('联网搜索');
const isThinkingModel = model.includes('think') || model.includes('r1') || prompt.includes('深度思考');

if (isThinkingModel) {
Expand All @@ -322,6 +325,7 @@ async function createCompletionStream(
parent_message_id: refParentMsgId || null,
prompt,
ref_file_ids: [],
search_enabled: isSearchModel,
thinking_enabled: isThinkingModel
},
{
Expand Down Expand Up @@ -461,10 +465,12 @@ function checkResult(result: AxiosResponse, refreshToken: string) {
*/
async function receiveStream(model: string, stream: any, refConvId?: string): Promise<any> {
let thinking = false;
const isSearchModel = model.includes('search');
const isThinkingModel = model.includes('think') || model.includes('r1');
const isSilentModel = model.includes('silent');
const isFoldModel = model.includes('fold');
logger.info(`模型: ${model}, 是否思考: ${isThinkingModel}, 是否静默思考: ${isSilentModel}, 是否折叠思考: ${isFoldModel}`);
logger.info(`模型: ${model}, 是否思考: ${isThinkingModel} 是否联网搜索: ${isSearchModel}, 是否静默思考: ${isSilentModel}, 是否折叠思考: ${isFoldModel}`);
let refContent = '';
return new Promise((resolve, reject) => {
// 消息初始化
const data = {
Expand All @@ -488,10 +494,15 @@ async function receiveStream(model: string, stream: any, refConvId?: string): Pr
const result = _.attempt(() => JSON.parse(event.data));
if (_.isError(result))
throw new Error(`Stream response invalid: ${event.data}`);
if (!result.choices || !result.choices[0] || !result.choices[0].delta || !result.choices[0].delta.content)
if (!result.choices || !result.choices[0] || !result.choices[0].delta)
return;
if (!data.id)
data.id = `${refConvId}@${result.message_id}`;
if(result.choices[0].delta.type === "search_result" && !isSilentModel) {
const searchResults = result.choices[0]?.delta?.search_results || [];
refContent += searchResults.map(item => `${item.title} - ${item.url}`).join('\n');
return;
}
if (result.choices[0].delta.type === "thinking") {
if (!thinking && isThinkingModel && !isSilentModel) {
thinking = true;
Expand All @@ -504,9 +515,10 @@ async function receiveStream(model: string, stream: any, refConvId?: string): Pr
thinking = false;
data.choices[0].message.content += isFoldModel ? "</pre></details>" : "[思考结束]";
}
data.choices[0].message.content += result.choices[0].delta.content;
if(result.choices[0].delta.content)
data.choices[0].message.content += result.choices[0].delta.content;
if (result.choices && result.choices[0] && result.choices[0].finish_reason === "stop") {
data.choices[0].message.content = data.choices[0].message.content.replace(/^\n+/, '');
data.choices[0].message.content = data.choices[0].message.content.replace(/^\n+/, '').replace(/\[citation:\d+\]/g, '') + (refContent ? `\n\n搜索结果来自:\n${refContent}` : '');
resolve(data);
}
} catch (err) {
Expand All @@ -532,10 +544,11 @@ async function receiveStream(model: string, stream: any, refConvId?: string): Pr
*/
function createTransStream(model: string, stream: any, refConvId: string, endCallback?: Function) {
let thinking = false;
const isSearchModel = model.includes('search');
const isThinkingModel = model.includes('think') || model.includes('r1');
const isSilentModel = model.includes('silent');
const isFoldModel = model.includes('fold');
logger.info(`模型: ${model}, 是否思考: ${isThinkingModel}, 是否静默思考: ${isSilentModel}, 是否折叠思考: ${isFoldModel}`);
logger.info(`模型: ${model}, 是否思考: ${isThinkingModel}, 是否联网搜索: ${isSearchModel}, 是否静默思考: ${isSilentModel}, 是否折叠思考: ${isFoldModel}`);
// 消息创建时间
const created = util.unixTimestamp();
// 创建转换流
Expand Down Expand Up @@ -563,9 +576,28 @@ function createTransStream(model: string, stream: any, refConvId: string, endCal
const result = _.attempt(() => JSON.parse(event.data));
if (_.isError(result))
throw new Error(`Stream response invalid: ${event.data}`);
if (!result.choices || !result.choices[0] || !result.choices[0].delta || !result.choices[0].delta.content)
if (!result.choices || !result.choices[0] || !result.choices[0].delta)
return;
result.model = model;
if (result.choices[0].delta.type === "search_result" && !isSilentModel) {
const searchResults = result.choices[0]?.delta?.search_results || [];
if (searchResults.length > 0) {
const refContent = searchResults.map(item => `检索 ${item.title} - ${item.url}`).join('\n') + '\n\n';
transStream.write(`data: ${JSON.stringify({
id: `${refConvId}@${result.message_id}`,
model: result.model,
object: "chat.completion.chunk",
choices: [
{
index: 0,
delta: { role: "assistant", content: refContent },
finish_reason: null,
},
],
})}\n\n`);
}
return;
}
if (result.choices[0].delta.type === "thinking") {
if (!thinking && isThinkingModel && !isSilentModel) {
thinking = true;
Expand Down Expand Up @@ -603,14 +635,17 @@ function createTransStream(model: string, stream: any, refConvId: string, endCal
})}\n\n`);
}

if(!result.choices[0].delta.content)
return;

transStream.write(`data: ${JSON.stringify({
id: `${refConvId}@${result.message_id}`,
model: result.model,
object: "chat.completion.chunk",
choices: [
{
index: 0,
delta: { role: "assistant", content: result.choices[0].delta.content },
delta: { role: "assistant", content: result.choices[0].delta.content.replace(/\[citation:\d+\]/g, '') },
finish_reason: null,
},
],
Expand Down

0 comments on commit 74e5bf4

Please sign in to comment.