Commit 86758af5 by Archer Committed by GitHub

feishu stream (#6629)

parent 25d6bb7d
......@@ -6,6 +6,7 @@ description: 'FastGPT V4.14.10 更新说明'
## 🚀 新增内容
1. 飞书发布渠道,支持流输出。
## ⚙️ 优化
......
......@@ -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/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/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.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 @@
"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/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.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 @@
"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/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.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",
......
......@@ -28,6 +28,10 @@ export const getErrText = (err: any, def = ''): any => {
return replaceSensitiveText(msg);
};
export const getErrResponse = (err: any): any => {
return err?.response?.data || err?.response || err;
};
export class UserError extends Error {
constructor(message: string) {
super(message);
......
import _, { type AxiosRequestConfig } from 'axios';
import { ProxyAgent } from 'proxy-agent';
import { isDevEnv } from '@fastgpt/global/common/system/constants';
export function createProxyAxios(config?: AxiosRequestConfig) {
if (isDevEnv) {
return _.create(config);
}
const agent = new ProxyAgent();
return _.create({
......
......@@ -21,7 +21,7 @@ import { authOutLinkLimit } from './auth';
import { addOutLinkUsage } from '../../../support/outLink/tools';
import { getLogger, LogCategories } from '../../../common/logger';
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 { getChatSourceByPublishChannel } from '@fastgpt/global/core/chat/utils';
import { WORKFLOW_MAX_RUN_TIMES } from '../../../core/workflow/constants';
......@@ -75,7 +75,10 @@ export type outLinkInvokeChatProps<T extends OutlinkAppType> = {
res?: NextApiResponse;
messageId: 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;
};
......@@ -91,7 +94,8 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({
res,
messageId,
chatUserId,
replyCallback,
onReply,
onStreamChunk,
streamId
}: outLinkInvokeChatProps<T>) {
const streamResKey = `${STREAM_CACHE_KEY_PREFIX}${streamId}`;
......@@ -112,7 +116,7 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({
const userQuestion = query.find((item) => item.text)?.text?.content || '';
if (RESET_CHAT_INPUT[userQuestion]) {
await resetChat({ appId: outLinkConfig.appId, chatId });
await replyCallback?.(RESET_CHAT_REPLY);
await onReply?.(RESET_CHAT_REPLY);
if (streamId) {
await appendRedisCache(streamResKey, RESET_CHAT_REPLY, 60);
await appendRedisCache(streamResKey, STREAM_END_FLAG, 60);
......@@ -144,7 +148,9 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({
ip: chatId
});
const workflowStreamResponse = streamId
const enableStreaming = !!streamId || !!onStreamChunk;
const workflowStreamResponse = enableStreaming
? async ({
write,
event,
......@@ -158,7 +164,12 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({
try {
const text = data.choices?.[0]?.delta?.content;
if (text) {
await appendRedisCache(streamResKey, text, 60);
if (streamId) {
await appendRedisCache(streamResKey, text, 60);
}
if (onStreamChunk) {
await onStreamChunk(text);
}
}
} catch (error) {
logger.error('Outlink real-time streaming failed', {
......@@ -171,8 +182,10 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({
}
: undefined;
// Append first
await appendRedisCache(streamResKey, '', 120);
// Initialize Redis key only when needed
if (streamId) {
await appendRedisCache(streamResKey, '', 120);
}
// Merge global variables from database
const variables = chatDetail?.variables ?? {};
......@@ -202,7 +215,7 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({
histories,
query: query,
chatConfig,
stream: streamId ? true : false,
stream: enableStreaming,
workflowStreamResponse,
runtimeEdges: storeEdges2RuntimeEdges(edges),
runtimeNodes: storeNodes2RuntimeNodes(nodes, getWorkflowEntryNodeIds(nodes)),
......@@ -225,31 +238,13 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({
const replyResult = await (async () => {
try {
if (streamId) {
// if streamId is provided, do not reply
return;
// wecom model: Redis handles streaming, no reply needed
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
};
}
logger.debug('Outlink official account reply success', {
responseContent,
result
});
return {
success: true,
data: result
};
await onReply?.(responseContent);
return { success: true };
} catch (error) {
logger.error('Outlink reply callback failed', { error });
logger.error('Outlink reply callback failed', { error: getErrResponse(error) });
return {
success: false,
errmsg: getErrText(error)
......@@ -306,9 +301,10 @@ export async function outlinkInvokeChat<T extends OutlinkAppType>({
});
try {
await appendRedisCache(streamResKey, STREAM_END_FLAG, 60);
await replyCallback?.(`App run error: ${getErrText(error)}`);
if (streamId) {
await appendRedisCache(streamResKey, STREAM_END_FLAG, 60);
}
await onReply?.(`App run error: ${getErrText(error)}`);
} catch (error) {
logger.error('Outlink invoke chat fallback reply failed', {
shareId: outLinkConfig.shareId,
......
......@@ -126,13 +126,12 @@ async function processUserGroup(
query: [{ text: { content: group.text } }],
messageId: group.msgIds[group.msgIds.length - 1],
chatUserId: group.userId,
replyCallback: async (replyContent: string) => {
onReply: async (replyContent: string) => {
await client.sendMessage({
to_user_id: group.userId,
text: replyContent,
context_token: group.contextToken
});
return { errcode: 0 };
}
});
} 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