mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-16 04:25:59 +00:00
💄 style: support web_search tool for MiniMax & Zhipu (#7980)
* 💄 style: support web_search for MiniMax * ♻️ refactor: refactor zhipu `web_search` tools * 🐛 fix: fix live search citations * ♻️ refactor: refactor Minimax citations * 🐛 fix: fix ci error * 🐛 fix: fix ci error * 💄 style: support OpenAI Search model citations --------- Co-authored-by: Arvin Xu <arvinx@foxmail.com>
This commit is contained in:
@@ -4,6 +4,7 @@ const minimaxChatModels: AIChatModelCard[] = [
|
||||
{
|
||||
abilities: {
|
||||
functionCall: true,
|
||||
search: true,
|
||||
vision: true,
|
||||
},
|
||||
contextWindowTokens: 1_000_192,
|
||||
@@ -19,11 +20,15 @@ const minimaxChatModels: AIChatModelCard[] = [
|
||||
output: 8,
|
||||
},
|
||||
releasedAt: '2025-01-15',
|
||||
settings: {
|
||||
searchImpl: 'params',
|
||||
},
|
||||
type: 'chat',
|
||||
},
|
||||
{
|
||||
abilities: {
|
||||
functionCall: true,
|
||||
search: true,
|
||||
vision: true,
|
||||
},
|
||||
contextWindowTokens: 245_760,
|
||||
@@ -37,6 +42,9 @@ const minimaxChatModels: AIChatModelCard[] = [
|
||||
input: 1,
|
||||
output: 1,
|
||||
},
|
||||
settings: {
|
||||
searchImpl: 'params',
|
||||
},
|
||||
type: 'chat',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -238,20 +238,22 @@ export const openaiChatModels: AIChatModelCard[] = [
|
||||
},
|
||||
{
|
||||
abilities: {
|
||||
functionCall: true,
|
||||
vision: true,
|
||||
search: true,
|
||||
},
|
||||
contextWindowTokens: 128_000,
|
||||
description:
|
||||
'ChatGPT-4o 是一款动态模型,实时更新以保持当前最新版本。它结合了强大的语言理解与生成能力,适合于大规模应用场景,包括客户服务、教育和技术支持。',
|
||||
displayName: 'GPT-4o 1120',
|
||||
id: 'gpt-4o-2024-11-20',
|
||||
'GPT-4o mini 搜索预览版是一个专门训练用于理解和执行网页搜索查询的模型,使用的是 Chat Completions API。除了令牌费用之外,网页搜索查询还会按每次工具调用收取费用。',
|
||||
displayName: 'GPT-4o mini Search Preview',
|
||||
id: 'gpt-4o-mini-search-preview',
|
||||
maxOutput: 16_384,
|
||||
pricing: {
|
||||
cachedInput: 1.25,
|
||||
input: 2.5,
|
||||
output: 10,
|
||||
input: 0.15,
|
||||
output: 0.6,
|
||||
},
|
||||
releasedAt: '2025-03-11',
|
||||
settings: {
|
||||
searchImpl: 'internal',
|
||||
},
|
||||
releasedAt: '2024-11-20',
|
||||
type: 'chat',
|
||||
},
|
||||
{
|
||||
@@ -272,6 +274,44 @@ export const openaiChatModels: AIChatModelCard[] = [
|
||||
releasedAt: '2024-05-13',
|
||||
type: 'chat',
|
||||
},
|
||||
{
|
||||
abilities: {
|
||||
search: true,
|
||||
},
|
||||
contextWindowTokens: 128_000,
|
||||
description:
|
||||
'GPT-4o 搜索预览版是一个专门训练用于理解和执行网页搜索查询的模型,使用的是 Chat Completions API。除了令牌费用之外,网页搜索查询还会按每次工具调用收取费用。',
|
||||
displayName: 'GPT-4o Search Preview',
|
||||
id: 'gpt-4o-search-preview',
|
||||
maxOutput: 16_384,
|
||||
pricing: {
|
||||
input: 2.5,
|
||||
output: 10,
|
||||
},
|
||||
releasedAt: '2025-03-11',
|
||||
settings: {
|
||||
searchImpl: 'internal',
|
||||
},
|
||||
type: 'chat',
|
||||
},
|
||||
{
|
||||
abilities: {
|
||||
functionCall: true,
|
||||
vision: true,
|
||||
},
|
||||
contextWindowTokens: 128_000,
|
||||
description:
|
||||
'ChatGPT-4o 是一款动态模型,实时更新以保持当前最新版本。它结合了强大的语言理解与生成能力,适合于大规模应用场景,包括客户服务、教育和技术支持。',
|
||||
displayName: 'GPT-4o 1120',
|
||||
id: 'gpt-4o-2024-11-20',
|
||||
pricing: {
|
||||
cachedInput: 1.25,
|
||||
input: 2.5,
|
||||
output: 10,
|
||||
},
|
||||
releasedAt: '2024-11-20',
|
||||
type: 'chat',
|
||||
},
|
||||
{
|
||||
abilities: {
|
||||
functionCall: true,
|
||||
|
||||
@@ -12,7 +12,16 @@ export const LobeMinimaxAI = LobeOpenAICompatibleFactory({
|
||||
baseURL: 'https://api.minimax.chat/v1',
|
||||
chatCompletion: {
|
||||
handlePayload: (payload) => {
|
||||
const { max_tokens, temperature, top_p, ...params } = payload;
|
||||
const { enabledSearch, max_tokens, temperature, tools, top_p, ...params } = payload;
|
||||
|
||||
const minimaxTools = enabledSearch
|
||||
? [
|
||||
...(tools || []),
|
||||
{
|
||||
type: 'web_search',
|
||||
},
|
||||
]
|
||||
: tools;
|
||||
|
||||
return {
|
||||
...params,
|
||||
@@ -20,6 +29,7 @@ export const LobeMinimaxAI = LobeOpenAICompatibleFactory({
|
||||
max_tokens: max_tokens !== undefined ? max_tokens : getMinimaxMaxOutputs(payload.model),
|
||||
presence_penalty: undefined,
|
||||
temperature: temperature === undefined || temperature <= 0 ? undefined : temperature / 2,
|
||||
tools: minimaxTools,
|
||||
top_p: top_p !== undefined && top_p > 0 && top_p <= 1 ? top_p : undefined,
|
||||
} as any;
|
||||
},
|
||||
|
||||
@@ -21,6 +21,8 @@ export const LobeOpenAI = LobeOpenAICompatibleFactory({
|
||||
}
|
||||
|
||||
if (model.includes('-search-')) {
|
||||
const oaiSearchContextSize = process.env.OPENAI_SEARCH_CONTEXT_SIZE; // low, medium, high
|
||||
|
||||
return {
|
||||
...payload,
|
||||
frequency_penalty: undefined,
|
||||
@@ -28,7 +30,12 @@ export const LobeOpenAI = LobeOpenAICompatibleFactory({
|
||||
stream: payload.stream ?? true,
|
||||
temperature: undefined,
|
||||
top_p: undefined,
|
||||
};
|
||||
...(oaiSearchContextSize && {
|
||||
web_search_options: {
|
||||
search_context_size: oaiSearchContextSize,
|
||||
},
|
||||
}),
|
||||
} as any;
|
||||
}
|
||||
|
||||
return { ...payload, stream: payload.stream ?? true };
|
||||
|
||||
@@ -99,16 +99,87 @@ export const transformOpenAIStream = (
|
||||
if (item.finish_reason) {
|
||||
// one-api 的流式接口,会出现既有 finish_reason ,也有 content 的情况
|
||||
// {"id":"demo","model":"deepl-en","choices":[{"index":0,"delta":{"role":"assistant","content":"Introduce yourself."},"finish_reason":"stop"}]}
|
||||
|
||||
if (typeof item.delta?.content === 'string' && !!item.delta.content) {
|
||||
// MiniMax 内建搜索功能会在第一个 tools 流中 content 返回引用源,需要忽略
|
||||
// {"id":"0483748a25071c611e2f48d2982fbe96","choices":[{"finish_reason":"stop","index":0,"delta":{"content":"[{\"no\":1,\"url\":\"https://www.xiaohongshu.com/discovery/item/66d8de3c000000001f01e752\",\"title\":\"郑钦文为国而战,没有理由不坚持🏅\",\"content\":\"·2024年08月03日\\n中国队选手郑钦文夺得巴黎奥运会网球女单比赛金牌(巴黎奥运第16金)\\n#巴黎奥运会[话题]# #郑钦文[话题]# #人物素材积累[话题]# #作文素材积累[话题]# #申论素材[话题]#\",\"web_icon\":\"https://www.xiaohongshu.com/favicon.ico\"}]","role":"tool","tool_call_id":"call_function_6696730535"}}],"created":1748255114,"model":"abab6.5s-chat","object":"chat.completion.chunk","usage":{"total_tokens":0,"total_characters":0},"input_sensitive":false,"output_sensitive":false,"input_sensitive_type":0,"output_sensitive_type":0,"output_sensitive_int":0}
|
||||
if (typeof item.delta?.role === 'string' && item.delta.role === 'tool') {
|
||||
return { data: null, id: chunk.id, type: 'text' };
|
||||
}
|
||||
|
||||
return { data: item.delta.content, id: chunk.id, type: 'text' };
|
||||
}
|
||||
|
||||
// OpenAI Search Preview 模型返回引用源
|
||||
// {"id":"chatcmpl-18037d13-243c-4941-8b05-9530b352cf17","object":"chat.completion.chunk","created":1748351805,"model":"gpt-4o-mini-search-preview-2025-03-11","choices":[{"index":0,"delta":{"annotations":[{"type":"url_citation","url_citation":{"url":"https://zh.wikipedia.org/wiki/%E4%B8%8A%E6%B5%B7%E4%B9%90%E9%AB%98%E4%B9%90%E5%9B%AD?utm_source=openai","title":"上海乐高乐园","start_index":75,"end_index":199}}]},"finish_reason":"stop"}],"service_tier":"default"}
|
||||
if ((item as any).delta?.annotations && (item as any).delta.annotations.length > 0) {
|
||||
const citations = (item as any).delta.annotations;
|
||||
|
||||
return [
|
||||
{
|
||||
data: {
|
||||
citations: citations.map(
|
||||
(item: any) =>
|
||||
({
|
||||
title: item.url_citation.title,
|
||||
url: item.url_citation.url,
|
||||
}) as CitationItem,
|
||||
),
|
||||
},
|
||||
id: chunk.id,
|
||||
type: 'grounding',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// MiniMax 内建搜索功能会在最后一个流中的 message 数组中返回 4 个 Object,其中最后一个为 annotations
|
||||
// {"id":"0483bf14ba55225a66de2342a21b4003","choices":[{"finish_reason":"tool_calls","index":0,"messages":[{"content":"","role":"user","reasoning_content":""},{"content":"","role":"assistant","tool_calls":[{"id":"call_function_0872338692","type":"web_search","function":{"name":"get_search_result","arguments":"{\"query_tag\":[\"天气\"],\"query_list\":[\"上海 2025年5月26日 天气\"]}"}}],"reasoning_content":""},{"content":"","role":"tool","tool_call_id":"call_function_0872338692","reasoning_content":""},{"content":"","role":"assistant","name":"海螺AI","annotations":[{"text":"【5†source】","url":"https://mtianqi.eastday.com/tianqi/shanghai/20250526.html","quote":"上海天气预报提供上海2025年05月26日天气"}],"audio_content":"","reasoning_content":""}]}],"created":1748274196,"model":"MiniMax-Text-01","object":"chat.completion","usage":{"total_tokens":13110,"total_characters":0,"prompt_tokens":12938,"completion_tokens":172},"base_resp":{"status_code":0,"status_msg":"Invalid parameters detected, json: unknown field \"user\""}}
|
||||
if ((item as any).messages && (item as any).messages.length > 0) {
|
||||
const citations = (item as any).messages.at(-1).annotations;
|
||||
|
||||
return [
|
||||
{
|
||||
data: {
|
||||
citations: citations.map(
|
||||
(item: any) =>
|
||||
({
|
||||
title: item.url,
|
||||
url: item.url,
|
||||
}) as CitationItem,
|
||||
),
|
||||
},
|
||||
id: chunk.id,
|
||||
type: 'grounding',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (chunk.usage) {
|
||||
const usage = chunk.usage;
|
||||
return { data: convertUsage(usage), id: chunk.id, type: 'usage' };
|
||||
}
|
||||
|
||||
// xAI Live Search 功能返回引用源
|
||||
// {"id":"8721eebb-6465-4c47-ba2e-8e2ec0f97055","object":"chat.completion.chunk","created":1747809109,"model":"grok-3","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":"stop"}],"system_fingerprint":"fp_1affcf9872","citations":["https://world.huanqiu.com/"]}
|
||||
if ((chunk as any).citations) {
|
||||
const citations = (chunk as any).citations;
|
||||
|
||||
return [
|
||||
{
|
||||
data: {
|
||||
citations: citations.map(
|
||||
(item: any) =>
|
||||
({
|
||||
title: item,
|
||||
url: item,
|
||||
}) as CitationItem,
|
||||
),
|
||||
},
|
||||
id: chunk.id,
|
||||
type: 'grounding',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return { data: item.finish_reason, id: chunk.id, type: 'stop' };
|
||||
}
|
||||
|
||||
@@ -146,7 +217,9 @@ export const transformOpenAIStream = (
|
||||
// in Hunyuan api, the citation is in every chunk
|
||||
('search_info' in chunk && (chunk.search_info as any)?.search_results) ||
|
||||
// in Wenxin api, the citation is in the first and last chunk
|
||||
('search_results' in chunk && chunk.search_results);
|
||||
('search_results' in chunk && chunk.search_results) ||
|
||||
// in Zhipu api, the citation is in the first chunk
|
||||
('web_search' in chunk && chunk.web_search);
|
||||
|
||||
if (citations) {
|
||||
streamContext.returnedCitation = true;
|
||||
@@ -154,13 +227,10 @@ export const transformOpenAIStream = (
|
||||
return [
|
||||
{
|
||||
data: {
|
||||
citations: (citations as any[]).map(
|
||||
(item) =>
|
||||
({
|
||||
title: typeof item === 'string' ? item : item.title,
|
||||
url: typeof item === 'string' ? item : item.url,
|
||||
}) as CitationItem,
|
||||
),
|
||||
citations: (citations as any[]).map((item) => ({
|
||||
title: typeof item === 'string' ? item : item.title,
|
||||
url: typeof item === 'string' ? item : item.url || item.link,
|
||||
})).filter(c => c.title && c.url), // Zhipu 内建搜索工具有时会返回空 link 引发程序崩溃
|
||||
},
|
||||
id: chunk.id,
|
||||
type: 'grounding',
|
||||
|
||||
@@ -22,6 +22,9 @@ export const LobeZhipuAI = LobeOpenAICompatibleFactory({
|
||||
type: 'web_search',
|
||||
web_search: {
|
||||
enable: true,
|
||||
result_sequence: 'before', // 将搜索结果返回顺序更改为 before 适配最小化 OpenAIStream 改动
|
||||
search_engine: process.env.ZHIPU_SEARCH_ENGINE || 'search_std', // search_std, search_pro
|
||||
search_result: true,
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user