Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/config/aiModels/minimax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const minimaxChatModels: AIChatModelCard[] = [
{
abilities: {
functionCall: true,
search: true,
vision: true,
},
contextWindowTokens: 1_000_192,
Expand All @@ -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,
Expand All @@ -37,6 +42,9 @@ const minimaxChatModels: AIChatModelCard[] = [
input: 1,
output: 1,
},
settings: {
searchImpl: 'params',
},
type: 'chat',
},
{
Expand Down
12 changes: 11 additions & 1 deletion src/libs/model-runtime/minimax/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,24 @@ 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,
frequency_penalty: undefined,
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;
},
Expand Down
66 changes: 57 additions & 9 deletions src/libs/model-runtime/utils/streams/openai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,60 @@ 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' };
}

// 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;
Comment on lines +136 to +137
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Potential null reference error if annotations is undefined in the last message

Suggested change
if ((item as any).messages && (item as any).messages.length > 0) {
const citations = (item as any).messages.at(-1).annotations;
if ((item as any).messages && (item as any).messages.length > 0) {
const lastMessage = (item as any).messages.at(-1);
const citations = lastMessage?.annotations;
if (!citations) return [];


return [
{
data: {
citations: citations.map(
(item: any) =>
({
title: item.url,
url: item.url,
Comment on lines +145 to +146
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Using URL as both title and URL may be confusing for users - consider using item.text for title

}) as CitationItem,
),
},
id: chunk.id,
type: 'grounding',
},
];
}

// 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',
},
];
}

if (chunk.usage) {
const usage = chunk.usage;
return { data: convertUsage(usage), id: chunk.id, type: 'usage' };
Expand Down Expand Up @@ -146,21 +195,20 @@ 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;

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',
Expand Down
3 changes: 3 additions & 0 deletions src/libs/model-runtime/zhipu/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
},
]
Expand Down
Loading