Commit 86758af5 by Archer Committed by GitHub

feishu stream (#6629)

parent 25d6bb7d
...@@ -6,6 +6,7 @@ description: 'FastGPT V4.14.10 更新说明' ...@@ -6,6 +6,7 @@ description: 'FastGPT V4.14.10 更新说明'
## 🚀 新增内容 ## 🚀 新增内容
1. 飞书发布渠道,支持流输出。
## ⚙️ 优化 ## ⚙️ 优化
......
...@@ -220,6 +220,7 @@ ...@@ -220,6 +220,7 @@
"document/content/docs/self-host/upgrading/4-14/4140.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/4-14/4140.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/4-14/4141.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/4-14/4141.en.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/4-14/4141.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/4-14/4141.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/4-14/41410.mdx": "2026-03-24T09:59:30+08:00",
"document/content/docs/self-host/upgrading/4-14/4142.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/4-14/4142.en.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/4-14/4142.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/4-14/4142.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/4-14/4143.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/4-14/4143.en.mdx": "2026-03-03T17:39:47+08:00",
...@@ -239,7 +240,7 @@ ...@@ -239,7 +240,7 @@
"document/content/docs/self-host/upgrading/4-14/41481.en.mdx": "2026-03-09T12:02:02+08:00", "document/content/docs/self-host/upgrading/4-14/41481.en.mdx": "2026-03-09T12:02:02+08:00",
"document/content/docs/self-host/upgrading/4-14/41481.mdx": "2026-03-09T17:39:53+08:00", "document/content/docs/self-host/upgrading/4-14/41481.mdx": "2026-03-09T17:39:53+08:00",
"document/content/docs/self-host/upgrading/4-14/4149.en.mdx": "2026-03-23T12:17:04+08:00", "document/content/docs/self-host/upgrading/4-14/4149.en.mdx": "2026-03-23T12:17:04+08:00",
"document/content/docs/self-host/upgrading/4-14/4149.mdx": "2026-03-23T23:36:27+08:00", "document/content/docs/self-host/upgrading/4-14/4149.mdx": "2026-03-23T23:37:12+08:00",
"document/content/docs/self-host/upgrading/outdated/40.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/40.en.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/outdated/40.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/40.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/outdated/41.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/41.en.mdx": "2026-03-03T17:39:47+08:00",
...@@ -381,7 +382,7 @@ ...@@ -381,7 +382,7 @@
"document/content/docs/self-host/upgrading/upgrade-intruction.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/upgrade-intruction.en.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/upgrade-intruction.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/upgrade-intruction.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/toc.en.mdx": "2026-03-23T12:10:44+08:00", "document/content/docs/toc.en.mdx": "2026-03-23T12:10:44+08:00",
"document/content/docs/toc.mdx": "2026-03-23T11:57:05+08:00", "document/content/docs/toc.mdx": "2026-03-24T09:59:30+08:00",
"document/content/docs/use-cases/app-cases/dalle3.en.mdx": "2026-02-26T22:14:30+08:00", "document/content/docs/use-cases/app-cases/dalle3.en.mdx": "2026-02-26T22:14:30+08:00",
"document/content/docs/use-cases/app-cases/dalle3.mdx": "2025-07-23T21:35:03+08:00", "document/content/docs/use-cases/app-cases/dalle3.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/use-cases/app-cases/english_essay_correction_bot.en.mdx": "2026-02-26T22:14:30+08:00", "document/content/docs/use-cases/app-cases/english_essay_correction_bot.en.mdx": "2026-02-26T22:14:30+08:00",
......
...@@ -28,6 +28,10 @@ export const getErrText = (err: any, def = ''): any => { ...@@ -28,6 +28,10 @@ export const getErrText = (err: any, def = ''): any => {
return replaceSensitiveText(msg); return replaceSensitiveText(msg);
}; };
export const getErrResponse = (err: any): any => {
return err?.response?.data || err?.response || err;
};
export class UserError extends Error { export class UserError extends Error {
constructor(message: string) { constructor(message: string) {
super(message); super(message);
......
import _, { type AxiosRequestConfig } from 'axios'; import _, { type AxiosRequestConfig } from 'axios';
import { ProxyAgent } from 'proxy-agent'; import { ProxyAgent } from 'proxy-agent';
import { isDevEnv } from '@fastgpt/global/common/system/constants';
export function createProxyAxios(config?: AxiosRequestConfig) { export function createProxyAxios(config?: AxiosRequestConfig) {
if (isDevEnv) {
return _.create(config);
}
const agent = new ProxyAgent(); const agent = new ProxyAgent();
return _.create({ return _.create({
......
...@@ -21,7 +21,7 @@ import { authOutLinkLimit } from './auth'; ...@@ -21,7 +21,7 @@ import { authOutLinkLimit } from './auth';
import { addOutLinkUsage } from '../../../support/outLink/tools'; import { addOutLinkUsage } from '../../../support/outLink/tools';
import { getLogger, LogCategories } from '../../../common/logger'; import { getLogger, LogCategories } from '../../../common/logger';
import { appendRedisCache } from '../../../common/redis/cache'; import { appendRedisCache } from '../../../common/redis/cache';
import { getErrText } from '@fastgpt/global/common/error/utils'; import { getErrResponse, getErrText } from '@fastgpt/global/common/error/utils';
import { getUsageSourceByPublishChannel } from '@fastgpt/global/support/wallet/usage/tools'; import { getUsageSourceByPublishChannel } from '@fastgpt/global/support/wallet/usage/tools';
import { getChatSourceByPublishChannel } from '@fastgpt/global/core/chat/utils'; import { getChatSourceByPublishChannel } from '@fastgpt/global/core/chat/utils';
import { WORKFLOW_MAX_RUN_TIMES } from '../../../core/workflow/constants'; import { WORKFLOW_MAX_RUN_TIMES } from '../../../core/workflow/constants';
...@@ -75,7 +75,10 @@ export type outLinkInvokeChatProps<T extends OutlinkAppType> = { ...@@ -75,7 +75,10 @@ export type outLinkInvokeChatProps<T extends OutlinkAppType> = {
res?: NextApiResponse; res?: NextApiResponse;
messageId: string; messageId: string;
chatUserId: string; chatUserId: string;
replyCallback?: (replyContent: string) => Promise<any>; // Called once with complete response content (all channels except wecom)
onReply?: (replyContent: string) => Promise<void>;
// Called for each streaming chunk (feishu and other push channels)
onStreamChunk?: (text: string) => Promise<void>;
streamId?: string; streamId?: string;
}; };
...@@ -91,7 +94,8 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({ ...@@ -91,7 +94,8 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({
res, res,
messageId, messageId,
chatUserId, chatUserId,
replyCallback, onReply,
onStreamChunk,
streamId streamId
}: outLinkInvokeChatProps<T>) { }: outLinkInvokeChatProps<T>) {
const streamResKey = `${STREAM_CACHE_KEY_PREFIX}${streamId}`; const streamResKey = `${STREAM_CACHE_KEY_PREFIX}${streamId}`;
...@@ -112,7 +116,7 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({ ...@@ -112,7 +116,7 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({
const userQuestion = query.find((item) => item.text)?.text?.content || ''; const userQuestion = query.find((item) => item.text)?.text?.content || '';
if (RESET_CHAT_INPUT[userQuestion]) { if (RESET_CHAT_INPUT[userQuestion]) {
await resetChat({ appId: outLinkConfig.appId, chatId }); await resetChat({ appId: outLinkConfig.appId, chatId });
await replyCallback?.(RESET_CHAT_REPLY); await onReply?.(RESET_CHAT_REPLY);
if (streamId) { if (streamId) {
await appendRedisCache(streamResKey, RESET_CHAT_REPLY, 60); await appendRedisCache(streamResKey, RESET_CHAT_REPLY, 60);
await appendRedisCache(streamResKey, STREAM_END_FLAG, 60); await appendRedisCache(streamResKey, STREAM_END_FLAG, 60);
...@@ -144,7 +148,9 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({ ...@@ -144,7 +148,9 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({
ip: chatId ip: chatId
}); });
const workflowStreamResponse = streamId const enableStreaming = !!streamId || !!onStreamChunk;
const workflowStreamResponse = enableStreaming
? async ({ ? async ({
write, write,
event, event,
...@@ -158,8 +164,13 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({ ...@@ -158,8 +164,13 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({
try { try {
const text = data.choices?.[0]?.delta?.content; const text = data.choices?.[0]?.delta?.content;
if (text) { if (text) {
if (streamId) {
await appendRedisCache(streamResKey, text, 60); await appendRedisCache(streamResKey, text, 60);
} }
if (onStreamChunk) {
await onStreamChunk(text);
}
}
} catch (error) { } catch (error) {
logger.error('Outlink real-time streaming failed', { logger.error('Outlink real-time streaming failed', {
streamId, streamId,
...@@ -171,8 +182,10 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({ ...@@ -171,8 +182,10 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({
} }
: undefined; : undefined;
// Append first // Initialize Redis key only when needed
if (streamId) {
await appendRedisCache(streamResKey, '', 120); await appendRedisCache(streamResKey, '', 120);
}
// Merge global variables from database // Merge global variables from database
const variables = chatDetail?.variables ?? {}; const variables = chatDetail?.variables ?? {};
...@@ -202,7 +215,7 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({ ...@@ -202,7 +215,7 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({
histories, histories,
query: query, query: query,
chatConfig, chatConfig,
stream: streamId ? true : false, stream: enableStreaming,
workflowStreamResponse, workflowStreamResponse,
runtimeEdges: storeEdges2RuntimeEdges(edges), runtimeEdges: storeEdges2RuntimeEdges(edges),
runtimeNodes: storeNodes2RuntimeNodes(nodes, getWorkflowEntryNodeIds(nodes)), runtimeNodes: storeNodes2RuntimeNodes(nodes, getWorkflowEntryNodeIds(nodes)),
...@@ -225,31 +238,13 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({ ...@@ -225,31 +238,13 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({
const replyResult = await (async () => { const replyResult = await (async () => {
try { try {
if (streamId) { if (streamId) {
// if streamId is provided, do not reply // wecom model: Redis handles streaming, no reply needed
return; return { success: true };
}
const result = await replyCallback?.(responseContent);
if (result.errcode !== 0) {
logger.error('Outlink official account reply failed', {
errmsg: result.errmsg
});
return {
success: false,
errmsg: result.errmsg
};
} }
await onReply?.(responseContent);
logger.debug('Outlink official account reply success', { return { success: true };
responseContent,
result
});
return {
success: true,
data: result
};
} catch (error) { } catch (error) {
logger.error('Outlink reply callback failed', { error }); logger.error('Outlink reply callback failed', { error: getErrResponse(error) });
return { return {
success: false, success: false,
errmsg: getErrText(error) errmsg: getErrText(error)
...@@ -306,9 +301,10 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({ ...@@ -306,9 +301,10 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({
}); });
try { try {
if (streamId) {
await appendRedisCache(streamResKey, STREAM_END_FLAG, 60); await appendRedisCache(streamResKey, STREAM_END_FLAG, 60);
}
await replyCallback?.(`App run error: ${getErrText(error)}`); await onReply?.(`App run error: ${getErrText(error)}`);
} catch (error) { } catch (error) {
logger.error('Outlink invoke chat fallback reply failed', { logger.error('Outlink invoke chat fallback reply failed', {
shareId: outLinkConfig.shareId, shareId: outLinkConfig.shareId,
......
...@@ -126,13 +126,12 @@ async function processUserGroup( ...@@ -126,13 +126,12 @@ async function processUserGroup(
query: [{ text: { content: group.text } }], query: [{ text: { content: group.text } }],
messageId: group.msgIds[group.msgIds.length - 1], messageId: group.msgIds[group.msgIds.length - 1],
chatUserId: group.userId, chatUserId: group.userId,
replyCallback: async (replyContent: string) => { onReply: async (replyContent: string) => {
await client.sendMessage({ await client.sendMessage({
to_user_id: group.userId, to_user_id: group.userId,
text: replyContent, text: replyContent,
context_token: group.contextToken context_token: group.contextToken
}); });
return { errcode: 0 };
} }
}); });
} catch (error) { } catch (error) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment