From 2c601c1693e76f0eaf27f0eb2bdd18badc25e9a9 Mon Sep 17 00:00:00 2001
From: pftom <1043269994@qq.com>
Date: Fri, 21 Feb 2025 10:44:51 +0800
Subject: [PATCH 1/6] style(canvas): update edge label styling with transparent
background
---
.../src/components/canvas/edges/custom-edge.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/ai-workspace-common/src/components/canvas/edges/custom-edge.tsx b/packages/ai-workspace-common/src/components/canvas/edges/custom-edge.tsx
index 5c5e99693..2af66223e 100644
--- a/packages/ai-workspace-common/src/components/canvas/edges/custom-edge.tsx
+++ b/packages/ai-workspace-common/src/components/canvas/edges/custom-edge.tsx
@@ -108,7 +108,7 @@ export const CustomEdge = memo(
/>
) : (
label && (
-
+
{label}
)
From b1ce9b294fb4495f766e522c8a51e608c40e75b9 Mon Sep 17 00:00:00 2001
From: pftom <1043269994@qq.com>
Date: Fri, 21 Feb 2025 17:45:06 +0800
Subject: [PATCH 2/6] feat(skills): enhance source data handling and chunking
for large search results
- Implement chunked source data emission in web search skill
- Add support for partial source data transmission in invoke action hook
- Update SSE post utility to improve error handling and logging
- Add translations for generate answer step in i18n
- Create mock sources for testing and development
---
.../src/hooks/canvas/use-invoke-action.ts | 33 +-
.../ai-workspace-common/src/utils/sse-post.ts | 5 +-
packages/i18n/src/en-US/skill-log.ts | 4 +
packages/i18n/src/zh-Hans/skill-log.ts | 4 +
packages/skill-template/src/base.ts | 70 +++
.../module/multiLingualSearch/index.ts | 2 +-
.../src/scheduler/utils/message.ts | 16 +-
.../skill-template/src/skills/mock-sources.ts | 529 ++++++++++++++++++
.../skill-template/src/skills/web-search.ts | 24 +-
9 files changed, 670 insertions(+), 17 deletions(-)
create mode 100644 packages/skill-template/src/skills/mock-sources.ts
diff --git a/packages/ai-workspace-common/src/hooks/canvas/use-invoke-action.ts b/packages/ai-workspace-common/src/hooks/canvas/use-invoke-action.ts
index 37b899c39..f4c643fe4 100644
--- a/packages/ai-workspace-common/src/hooks/canvas/use-invoke-action.ts
+++ b/packages/ai-workspace-common/src/hooks/canvas/use-invoke-action.ts
@@ -141,10 +141,35 @@ export const useInvokeAction = () => {
}
const updatedStep: ActionStep = findOrCreateStep(result.steps ?? [], step);
- updatedStep.structuredData = {
- ...updatedStep.structuredData,
- ...structuredData,
- };
+
+ // Handle chunked sources data
+ if (structuredData.sources && Array.isArray(structuredData.sources)) {
+ const existingData = updatedStep.structuredData || {};
+ const existingSources = (existingData.sources || []) as any[];
+
+ // If this is a chunk of sources, merge it with existing sources
+ if (structuredData.isPartial !== undefined) {
+ updatedStep.structuredData = {
+ ...existingData,
+ sources: [...existingSources, ...structuredData.sources],
+ isPartial: structuredData.isPartial,
+ chunkIndex: structuredData.chunkIndex,
+ totalChunks: structuredData.totalChunks,
+ };
+ } else {
+ // Handle non-chunked data as before
+ updatedStep.structuredData = {
+ ...existingData,
+ ...structuredData,
+ };
+ }
+ } else {
+ // Handle non-sources structured data
+ updatedStep.structuredData = {
+ ...updatedStep.structuredData,
+ ...structuredData,
+ };
+ }
const updatedResult = {
...result,
diff --git a/packages/ai-workspace-common/src/utils/sse-post.ts b/packages/ai-workspace-common/src/utils/sse-post.ts
index 6e2ed2143..117c43e01 100644
--- a/packages/ai-workspace-common/src/utils/sse-post.ts
+++ b/packages/ai-workspace-common/src/utils/sse-post.ts
@@ -68,7 +68,7 @@ export const ssePost = async ({
try {
const response = await makeSSERequest(payload, controller);
- const baseResp = await extractBaseResp(response);
+ const baseResp = await extractBaseResp(response, { success: true });
if (!baseResp.success) {
onSkillError?.({ error: baseResp, event: 'error' });
return;
@@ -107,7 +107,7 @@ export const ssePost = async ({
message: message.substring(6),
error: err,
});
- return;
+ // return;
}
if (skillEvent?.event === 'start') {
@@ -141,6 +141,7 @@ export const ssePost = async ({
bufferStr = lines[lines.length - 1];
} catch (err) {
+ console.log('actual err', err);
onSkillError(err);
onCompleted?.(true);
hasError = true;
diff --git a/packages/i18n/src/en-US/skill-log.ts b/packages/i18n/src/en-US/skill-log.ts
index f30ae3687..3e43c6441 100644
--- a/packages/i18n/src/en-US/skill-log.ts
+++ b/packages/i18n/src/en-US/skill-log.ts
@@ -27,6 +27,10 @@ const translations = {
title: 'Select Related Results',
description: 'Total of {{totalResults}} results, completed in {{duration}}ms',
},
+ generateAnswer: {
+ title: 'Generate Answer',
+ description: 'Start to generate answer...',
+ },
};
export default translations;
diff --git a/packages/i18n/src/zh-Hans/skill-log.ts b/packages/i18n/src/zh-Hans/skill-log.ts
index 5f492779b..183c840fe 100644
--- a/packages/i18n/src/zh-Hans/skill-log.ts
+++ b/packages/i18n/src/zh-Hans/skill-log.ts
@@ -27,6 +27,10 @@ const translations = {
title: '选择关联结果',
description: '总共 {{totalResults}} 个结果, 耗时 {{duration}} 毫秒',
},
+ generateAnswer: {
+ title: '生成答案',
+ description: '开始生成答案...',
+ },
};
export default translations;
diff --git a/packages/skill-template/src/base.ts b/packages/skill-template/src/base.ts
index 9c460f576..d00684ff9 100644
--- a/packages/skill-template/src/base.ts
+++ b/packages/skill-template/src/base.ts
@@ -87,6 +87,76 @@ export abstract class BaseSkill extends StructuredTool {
emitter.emit(eventData.event, eventData);
}
+ /**
+ * Emit large data in chunks with delay to prevent overwhelming the event system
+ * @param data The data to emit
+ * @param config The skill runnable config
+ * @param options Options for chunking and delay
+ */
+ async emitLargeDataEvent
(
+ data: {
+ event?: string;
+ data: T[];
+ buildEventData: (
+ chunk: T[],
+ meta: { isPartial: boolean; chunkIndex: number; totalChunks: number },
+ ) => Partial;
+ },
+ config: SkillRunnableConfig,
+ options: {
+ maxChunkSize?: number;
+ delayBetweenChunks?: number;
+ } = {},
+ ): Promise {
+ const { maxChunkSize = 500, delayBetweenChunks = 10 } = options;
+
+ // If no data or emitter, return early
+ if (!data.data?.length || !config?.configurable?.emitter) {
+ return;
+ }
+
+ // Split data into chunks based on size
+ const chunks: T[][] = [];
+ let currentChunk: T[] = [];
+ let currentSize = 0;
+
+ for (const item of data.data) {
+ const itemSize = JSON.stringify(item).length;
+
+ if (currentSize + itemSize > maxChunkSize && currentChunk.length > 0) {
+ chunks.push(currentChunk);
+ currentChunk = [];
+ currentSize = 0;
+ }
+
+ currentChunk.push(item);
+ currentSize += itemSize;
+ }
+
+ // Push the last chunk if not empty
+ if (currentChunk.length > 0) {
+ chunks.push(currentChunk);
+ }
+
+ // Emit chunks with delay
+ const emitPromises = chunks.map(
+ (chunk, i) =>
+ new Promise((resolve) => {
+ setTimeout(() => {
+ const eventData = data.buildEventData(chunk, {
+ isPartial: i < chunks.length - 1,
+ chunkIndex: i,
+ totalChunks: chunks.length,
+ });
+ this.emitEvent(eventData, config);
+ resolve();
+ }, i * delayBetweenChunks);
+ }),
+ );
+
+ await Promise.all(emitPromises);
+ }
+
async _call(
input: typeof this.graphState,
_runManager?: CallbackManagerForToolRun,
diff --git a/packages/skill-template/src/scheduler/module/multiLingualSearch/index.ts b/packages/skill-template/src/scheduler/module/multiLingualSearch/index.ts
index 1037350ec..7e22a6e97 100644
--- a/packages/skill-template/src/scheduler/module/multiLingualSearch/index.ts
+++ b/packages/skill-template/src/scheduler/module/multiLingualSearch/index.ts
@@ -335,7 +335,7 @@ export const callMultiLingualWebSearch = async (
// Keep original results if deduplication fails
}
- ctxThis.emitEvent({ structuredData: { multiLingualSearchResult: finalResults } }, config);
+ // ctxThis.emitEvent({ structuredData: { multiLingualSearchResult: finalResults } }, config);
// Return results with analysis
return {
diff --git a/packages/skill-template/src/scheduler/utils/message.ts b/packages/skill-template/src/scheduler/utils/message.ts
index 1964866d0..bbdd9bbb5 100644
--- a/packages/skill-template/src/scheduler/utils/message.ts
+++ b/packages/skill-template/src/scheduler/utils/message.ts
@@ -46,12 +46,16 @@ export const buildFinalRequestMessages = ({
...chatHistory,
...messages,
...contextMessages,
- new HumanMessage({
- content: [
- { type: 'text', text: userPrompt },
- ...(images?.map((image) => ({ type: 'image_url', image_url: { url: image } })) || []),
- ],
- }),
+ new HumanMessage(
+ images?.length
+ ? {
+ content: [
+ { type: 'text', text: userPrompt },
+ ...(images.map((image) => ({ type: 'image_url', image_url: { url: image } })) || []),
+ ],
+ }
+ : userPrompt,
+ ),
];
return requestMessages;
diff --git a/packages/skill-template/src/skills/mock-sources.ts b/packages/skill-template/src/skills/mock-sources.ts
new file mode 100644
index 000000000..047196b23
--- /dev/null
+++ b/packages/skill-template/src/skills/mock-sources.ts
@@ -0,0 +1,529 @@
+export const mockSources = [
+ {
+ url: 'https://www.threads.net/@yushengliin/post/DFOii6fTca-',
+ title: 'refly:高效智能的AI內容創作工具 - Threads',
+ pageContent:
+ 'refly 是一款集搜索、筆記、思維導圖與AI寫作為一體的超級工具,幫助用戶將想法高效轉化為文字,從靈感捕捉到內容創作,提供全方位的支持,讓創作過程更加流暢 ...',
+ metadata: {
+ originalLocale: 'en',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://www.threads.net/@yushengliin/post/DFOii6fTca-',
+ score: 0.8933094143867493,
+ source: 'https://www.threads.net/@yushengliin/post/DFOii6fTca-',
+ title: 'refly:高效智能的AI內容創作工具 - Threads',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://www.cnblogs.com/Agora/p/18671366',
+ title: 'Kyutai开源端侧模型Helium -1 preview;FoloToy内测「超级智能体」',
+ pageContent:
+ 'Refly 是一个基于自由画布的AI 原生创作引擎,旨在通过多线程对话、知识库集成、上下文记忆和智能搜索技术,帮助用户将创意转化为高质量内容。 该平台覆盖了 ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://www.cnblogs.com/Agora/p/18671366',
+ score: 0.8723474144935608,
+ source: 'https://www.cnblogs.com/Agora/p/18671366',
+ title: 'Kyutai开源端侧模型Helium -1 preview;FoloToy内测「超级智能体」',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://www.aisharenet.com/refly-zhengshikaifangben/',
+ title: 'Refly 正式开放注册,文字创作者的最佳工作平台 - 首席AI分享圈',
+ pageContent:
+ 'Refly 是一个基于「 自由画布 」理念构建的AI 原生内容创作平台,通过多线程对话、知识库整合、上下文记忆、智能搜索与可见即可得的AI 文档编辑器,为用户 ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://www.aisharenet.com/refly-zhengshikaifangben/',
+ score: 0.8714748620986938,
+ source: 'https://www.aisharenet.com/refly-zhengshikaifangben/',
+ title: 'Refly 正式开放注册,文字创作者的最佳工作平台 - 首席AI分享圈',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://www.163.com/dy/article/JMOURKU605567BLV.html',
+ title: 'Refly 是一个开源的AI..._手机网易网',
+ pageContent:
+ 'Refly是一个开源的AI原生创作引擎,提供了一个直观的自由格式画布界面,集成了多线程对话、AI知识库集成、上下文记忆、智能搜索、所见即所得的AI编辑器 ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://www.163.com/dy/article/JMOURKU605567BLV.html',
+ score: 0.8688268065452576,
+ source: 'https://www.163.com/dy/article/JMOURKU605567BLV.html',
+ title: 'Refly 是一个开源的AI..._手机网易网',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://top.aibase.com/tool/refly',
+ title: 'Refly使用入口地址Ai网站最新工具和软件app下载 - AIbase',
+ pageContent:
+ 'Refly是一个AI Native创作引擎,通过多线程对话、知识库整合、上下文记忆和智能搜索等技术,帮助用户将创意转化为优质内容。 它覆盖了学术研究、技术文档等20+专业场景模板, ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://top.aibase.com/tool/refly',
+ score: 0.8679338097572327,
+ source: 'https://top.aibase.com/tool/refly',
+ title: 'Refly使用入口地址Ai网站最新工具和软件app下载 - AIbase',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://c.m.163.com/news/a/JMOURKU605567BLV.html',
+ title: 'Refly 是一个开源的AI... - 网易新闻',
+ pageContent:
+ 'Refly 是一个开源的AI 原生创作引擎,提供了一个直观的自由格式画布界面,集成了多线程对话、AI 知识库集成、上下文记忆、智能搜索、所见即所得的AI ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://c.m.163.com/news/a/JMOURKU605567BLV.html',
+ score: 0.8652240633964539,
+ source: 'https://c.m.163.com/news/a/JMOURKU605567BLV.html',
+ title: 'Refly 是一个开源的AI... - 网易新闻',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://www.aibase.com/zh/tool/35128',
+ title: 'Refly-基于自由画布的创作平台 - AIbase',
+ pageContent:
+ 'Refly是一个AI Native创作引擎,通过多线程对话、知识库整合、上下文记忆和智能搜索等技术,帮助用户将创意转化为优质内容。它覆盖了学术研究、技术文档等20+专业场景 ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://www.aibase.com/zh/tool/35128',
+ score: 0.8643104434013367,
+ source: 'https://www.aibase.com/zh/tool/35128',
+ title: 'Refly-基于自由画布的创作平台 - AIbase',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://apps.shopify.com/refly?locale=zh-CN',
+ title: 'Refly: AI-Powered Multi-Channel Customer Service for Shopify',
+ pageContent:
+ 'Refly是一个AI驱动的工具,可以在多个渠道(包括Shopify、聊天小部件等)自动化客户服务。 适用于任何规模的企业,Refly简化了客户互动管理,自动化常规任务,并提供个性化 ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://apps.shopify.com/refly?locale=zh-CN',
+ score: 0.8558511734008789,
+ source: 'https://apps.shopify.com/refly?locale=zh-CN',
+ title: 'Refly: AI-Powered Multi-Channel Customer Service for Shopify',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://github.com/refly-ai/refly/blob/main/README_CN.md',
+ title: 'refly/README_CN.md at main · refly-ai/refly - GitHub',
+ pageContent:
+ 'Refly 是一个开源的AI 原生创作引擎。Refly 直观的自由画布界面集成了多线程对话、RAG 检索流程、上下文记忆、智能搜索和AI 文档编辑等功能,让您轻松地将创意转化为 ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://github.com/refly-ai/refly/blob/main/README_CN.md',
+ score: 0.854884684085846,
+ source: 'https://github.com/refly-ai/refly/blob/main/README_CN.md',
+ title: 'refly/README_CN.md at main · refly-ai/refly - GitHub',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://top.aibase.com/topic/%E6%99%BA%E8%83%BD%E6%90%9C%E7%B4%A2',
+ title: '最新Ai智能搜索网站工具和软件推荐_AiBase产品库',
+ pageContent:
+ 'Refly. 基于自由画布的创作平台,激发创作灵感。 Refly是一个AI Native创作引擎,通过多线程对话、知识库整合、上下文记忆和智能搜索等技术,帮助用户将创意转化为优质 ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://top.aibase.com/topic/%E6%99%BA%E8%83%BD%E6%90%9C%E7%B4%A2',
+ score: 0.8418256044387817,
+ source: 'https://top.aibase.com/topic/%E6%99%BA%E8%83%BD%E6%90%9C%E7%B4%A2',
+ title: '最新Ai智能搜索网站工具和软件推荐_AiBase产品库',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://www.cnblogs.com/wuhuacong/archive/2012/03/30/2426091.html',
+ title: '利用Refly和CodeDom实现代码的动态生成和动态编译- 伍华聪- 博客园',
+ pageContent:
+ 'Refly则是国外一个开发者对CodeDom进行封装,目的是使得Codedom的实现更加方便易懂,和CodeDom的使用对比,代码更加简洁优雅,不过要了解整体的东西 ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://www.cnblogs.com/wuhuacong/archive/2012/03/30/2426091.html',
+ score: 0.8198933005332947,
+ source: 'https://www.cnblogs.com/wuhuacong/archive/2012/03/30/2426091.html',
+ title: '利用Refly和CodeDom实现代码的动态生成和动态编译- 伍华聪- 博客园',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://dread.run/',
+ title: '帝阅- 专注信息摘要的智能侍读助理',
+ pageContent:
+ '1.Refly是一个AI原生创作平台,旨在简化创作流程。 2.平台基于LangGraph技术,提供强大的AI功能。 3.Refly开源,代码仓库在GitHub上可供访问 ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://dread.run/',
+ score: 0.8198933005332947,
+ source: 'https://dread.run/',
+ title: '帝阅- 专注信息摘要的智能侍读助理',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://developer.aliyun.com/article/326893',
+ title: '利用Refly和CodeDom实现代码的动态生成和动态编译',
+ pageContent:
+ 'Refly则是国外一个开发者对CodeDom进行封装,目的是使得Codedom的实现更加方便易懂,和CodeDom的使用对比,代码更加简洁优雅,不过要了解整体的东西,也需要对CodeDOM ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://developer.aliyun.com/article/326893',
+ score: 0.815232515335083,
+ source: 'https://developer.aliyun.com/article/326893',
+ title: '利用Refly和CodeDom实现代码的动态生成和动态编译',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://www.aihub.cn/tools/writing/refly/',
+ title: 'Refly.ai:AI驱动的专业内容创作引擎 - AIHub',
+ pageContent:
+ 'Refly.ai 是一款专为专业内容创作设计的AI驱动平台,通过多线程对话、知识整合、上下文记忆和智能搜索功能,帮助用户将创意快速转化为高质量内容。',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://www.aihub.cn/tools/writing/refly/',
+ score: 0.8068526387214661,
+ source: 'https://www.aihub.cn/tools/writing/refly/',
+ title: 'Refly.ai:AI驱动的专业内容创作引擎 - AIHub',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://www.threads.net/@yongshuai1013/post/DFPqDQURqkF',
+ title: '開源分享AI智慧創作工具:refly,它是一個集成了搜索、筆記軟體',
+ pageContent:
+ '開源分享AI智慧創作工具:refly,它是一個集成了搜索、筆記軟體、心智圖以及AI寫作的超級工具,用它可以更高效的把想法變成文字.',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://www.threads.net/@yongshuai1013/post/DFPqDQURqkF',
+ score: 0.7839884757995605,
+ source: 'https://www.threads.net/@yongshuai1013/post/DFPqDQURqkF',
+ title: '開源分享AI智慧創作工具:refly,它是一個集成了搜索、筆記軟體',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://t.cj.sina.com.cn/articles/view/2194035935/m82c654df03301e9hi?from=tech',
+ title: 'AI创作引擎项目Refly.AI刚正式开源了 - 新浪财经',
+ pageContent:
+ 'AI创作引擎项目Refly.AI刚正式开源了github.com/refly-ai/refly/Refly 是一个开源的AI 原生创作引擎。Refly 直观的自由画布界面集成了多线程对话、RAG ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://t.cj.sina.com.cn/articles/view/2194035935/m82c654df03301e9hi?from=tech',
+ score: 0.7520125508308411,
+ source: 'https://t.cj.sina.com.cn/articles/view/2194035935/m82c654df03301e9hi?from=tech',
+ title: 'AI创作引擎项目Refly.AI刚正式开源了 - 新浪财经',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://www.ftium4.com/ux-weekly-215.html',
+ title: '体验碎周报第215 期(2024.12.23) - 龙爪槐守望者',
+ pageContent:
+ 'Refly——自由画布笔记 ... 把笔记放在画布上,通过多线程对话、知识库整合、上下文记忆和智能搜索,帮助用户轻松将创意转化为高质量内容。该平台支持超过20 种 ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://www.ftium4.com/ux-weekly-215.html',
+ score: 0.7386690378189087,
+ source: 'https://www.ftium4.com/ux-weekly-215.html',
+ title: '体验碎周报第215 期(2024.12.23) - 龙爪槐守望者',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://x.com/aigclink/status/1882778663164461111',
+ title: 'AIGCLINK on X: "很棒的一个AI智能创作工具:refly,它是一个集成了 ...',
+ pageContent:
+ '很棒的一个AI智能创作工具:refly,它是一个集成了搜索、笔记软件、思维导图以及AI写作的超级工具,用它可以更高效的把想法变成文字自由画布界面设计, ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://x.com/aigclink/status/1882778663164461111',
+ score: 0.7371581792831421,
+ source: 'https://x.com/aigclink/status/1882778663164461111',
+ title: 'AIGCLINK on X: "很棒的一个AI智能创作工具:refly,它是一个集成了 ...',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://x.com/tuturetom/status/1868585642529284566',
+ title: 'Tom Huang on X: " 大家好! 很高兴向大家介绍我们的AI 写作产品 ...',
+ pageContent:
+ '大家好! 很高兴向大家介绍我们的AI 写作产品—— Refly,这是一款革新性的AI Native 内容创作引擎!⚡️ Refly 是基于「自由画布 」理念打造的AI Native ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://x.com/tuturetom/status/1868585642529284566',
+ score: 0.7256486415863037,
+ source: 'https://x.com/tuturetom/status/1868585642529284566',
+ title: 'Tom Huang on X: " 大家好! 很高兴向大家介绍我们的AI 写作产品 ...',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://zhidao.baidu.com/question/451012173.html',
+ title: 'refly是什么意思? - 百度知道',
+ pageContent: 'refly是"答复"的意思。 ...全文.',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://zhidao.baidu.com/question/451012173.html',
+ score: 0.7185943722724915,
+ source: 'https://zhidao.baidu.com/question/451012173.html',
+ title: 'refly是什么意思? - 百度知道',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://ainote.tw/refly-ai-creation-platform-explained/',
+ title: 'Refly AI 創作平臺全面解析 - 奕昇AI學習平台',
+ pageContent:
+ 'Refly 的核心概念為自由畫布,讓創作者可以在一個開放的環境中自由探索與 ... 如果您需要一款功能強大且靈活的AI 創作工具,Refly 無疑是最佳選擇!',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://ainote.tw/refly-ai-creation-platform-explained/',
+ score: 0.7000752091407776,
+ source: 'https://ainote.tw/refly-ai-creation-platform-explained/',
+ title: 'Refly AI 創作平臺全面解析 - 奕昇AI學習平台',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://www.waytoagi.com/zh/question/87173',
+ title: '与知识库对话- flowith 2.0与refly的区别具体在哪里? - WaytoAGI',
+ pageContent:
+ 'Flowith 2.0 与Refly 的区别主要体现在以下方面:. 功能定位:Refly 是一款国产应用,是全站式的文本创作工具,集成了“知识库+自由画布+AI 搜索+内容编辑”等功能,覆盖主题 ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://www.waytoagi.com/zh/question/87173',
+ score: 0.6388352513313293,
+ source: 'https://www.waytoagi.com/zh/question/87173',
+ title: '与知识库对话- flowith 2.0与refly的区别具体在哪里? - WaytoAGI',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://www.bgrdh.com/sites/49214.html',
+ title: 'Refly-基于“自由画布”理念的AI 原生内容创作引擎 - 办公人导航',
+ pageContent:
+ 'Refly 的适用范围非常广泛,涵盖了学术研究、技术文档编写、投资分析、法律文献 ... 用户可以根据自己的需求选择专业场景模板,并通过上下文记忆和智能搜索功能优化创作流程。',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://www.bgrdh.com/sites/49214.html',
+ score: 0.5404388308525085,
+ source: 'https://www.bgrdh.com/sites/49214.html',
+ title: 'Refly-基于“自由画布”理念的AI 原生内容创作引擎 - 办公人导航',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://www.youtube.com/watch?v=5efKJWEvR94',
+ title: '介绍AI自由画板写作工具Refly.ai 以及使用其逐步创建一个详细的游戏 ...',
+ pageContent:
+ '本片介绍一个AI自由画板写作工具——Refly.ai,并使用其逐步创建一个详细的游戏项目规则。视频只有3分多钟,很容易看完。稍后我会整理详细的各部分提示发 ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://www.youtube.com/watch?v=5efKJWEvR94',
+ score: 0.4929509162902832,
+ source: 'https://www.youtube.com/watch?v=5efKJWEvR94',
+ title: '介绍AI自由画板写作工具Refly.ai 以及使用其逐步创建一个详细的游戏 ...',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://ai-bot.cn/refly/',
+ title: 'Refly - AI原生内容创作平台,结合自由画布与多种AI 功能 - AI工具集',
+ pageContent:
+ 'Refly的应用场景. 学术研究:帮助研究人员整理思路、构建研究框架,快速生成文献 ... 相关文章. Manga Image Translator – 开源漫画图片文字翻译工具,多语言翻译无 ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://ai-bot.cn/refly/',
+ score: 0.4144248068332672,
+ source: 'https://ai-bot.cn/refly/',
+ title: 'Refly - AI原生内容创作平台,结合自由画布与多种AI 功能 - AI工具集',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://x.com/nishuang/status/1882819143860113520',
+ title: '倪爽- 上个月我推荐大家试用“自由画布”式AI 创作工具Refly - X',
+ pageContent:
+ '上个月我推荐大家试用“自由画布”式AI 创作工具Refly,当时我说了两个感受,没想到背后都有深意我说他们最慷慨,没想到今天他们把整个项目开源了 , ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://x.com/nishuang/status/1882819143860113520',
+ score: 0.39981165528297424,
+ source: 'https://x.com/nishuang/status/1882819143860113520',
+ title: '倪爽- 上个月我推荐大家试用“自由画布”式AI 创作工具Refly - X',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://aiguide.cc/sites/14379.html',
+ title: 'Refly | AI智库导航-aiguide.cc',
+ pageContent:
+ '主要应用场景. 内容创作:适用于各种需要快速生成高质量内容的创作者,如 ... 注册与登录:访问Refly的官方网站或相关应用平台,注册并登录账号。 创建画布:在 ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://aiguide.cc/sites/14379.html',
+ score: 0.38676342368125916,
+ source: 'https://aiguide.cc/sites/14379.html',
+ title: 'Refly | AI智库导航-aiguide.cc',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://www.yjpoo.com/site/5081.html',
+ title: 'Refly Ai:一个开源的AI原生创作引擎 - 映技派',
+ pageContent:
+ 'Refly AI特别适合需要深度思考和结构化组织的创作场景,如学术研究、技术文档编写等,Refly Ai现已集成最火的DeepSeek R1。 Refly Ai.webp. Refly Ai核心功能:. Refly AI 的 ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://www.yjpoo.com/site/5081.html',
+ score: 0.3656831681728363,
+ source: 'https://www.yjpoo.com/site/5081.html',
+ title: 'Refly Ai:一个开源的AI原生创作引擎 - 映技派',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'http://npses.ezsale.tw/upload/0402240001.ppt',
+ title: '[PPT] 資訊安全基本認知教育訓練',
+ pageContent:
+ '不論學校內部採用多強大的防火牆系統、防毒軟體、或其他資安技術軟硬體設施 ... http://www.refly.net/passwordchecker; http://www.microsoft.com/taiwan/athome ...',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'http://npses.ezsale.tw/upload/0402240001.ppt',
+ score: 0.25091278553009033,
+ source: 'http://npses.ezsale.tw/upload/0402240001.ppt',
+ title: '[PPT] 資訊安全基本認知教育訓練',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://docs.refly.ai/zh/guide/configuration',
+ title: '配置说明| Refly 文档',
+ pageContent:
+ 'MinIO Refly 需要两个MinIO 实例: 内部:用于存储画布、资源和文档数据,通常设置为私有可见性。 外部:用于存储上传的文件,通常设置为公开可见性。',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://docs.refly.ai/zh/guide/configuration',
+ score: 0.24508501589298248,
+ source: 'https://docs.refly.ai/zh/guide/configuration',
+ title: '配置说明| Refly 文档',
+ sourceType: 'webSearch',
+ },
+ },
+ {
+ url: 'https://chromewebstore.google.com/detail/refly-ai-native-%E5%88%9B%E4%BD%9C%E5%BC%95%E6%93%8E/lecbjbapfkinmikhadakbclblnemmjpd?hl=zh-CN',
+ title: 'Refly - AI Native 创作引擎- Chrome 应用商店',
+ pageContent:
+ '基于自由画布的创作平台,通过多线程对话、知识库整合、上下文记忆、智能搜索和AI 文档编辑器,轻松将想法转化为优质内容。',
+ metadata: {
+ originalLocale: 'zh-hans',
+ originalQuery: 'refly 的定义是什么?',
+ translatedQuery: undefined,
+ isTranslated: false,
+ url: 'https://chromewebstore.google.com/detail/refly-ai-native-%E5%88%9B%E4%BD%9C%E5%BC%95%E6%93%8E/lecbjbapfkinmikhadakbclblnemmjpd?hl=zh-CN',
+ score: 0.23091977834701538,
+ source:
+ 'https://chromewebstore.google.com/detail/refly-ai-native-%E5%88%9B%E4%BD%9C%E5%BC%95%E6%93%8E/lecbjbapfkinmikhadakbclblnemmjpd?hl=zh-CN',
+ title: 'Refly - AI Native 创作引擎- Chrome 应用商店',
+ sourceType: 'webSearch',
+ },
+ },
+];
diff --git a/packages/skill-template/src/skills/web-search.ts b/packages/skill-template/src/skills/web-search.ts
index 14d73d95f..3c6a3643e 100644
--- a/packages/skill-template/src/skills/web-search.ts
+++ b/packages/skill-template/src/skills/web-search.ts
@@ -13,7 +13,6 @@ import { safeStringifyJSON } from '@refly-packages/utils';
// utils
import { buildFinalRequestMessages } from '../scheduler/utils/message';
import { prepareContext } from '../scheduler/utils/context';
-
// prompts
import * as webSearch from '../scheduler/module/webSearch/index';
import { truncateSource } from '../scheduler/utils/truncator';
@@ -112,10 +111,27 @@ export class WebSearch extends BaseSkill {
this.engine.logger.log('Prepared context successfully!');
- if (sources.length > 0) {
- this.emitEvent({ structuredData: { sources: truncateSource(sources) } }, config);
+ if (sources?.length > 0) {
+ // Split sources into smaller chunks based on size and emit them separately
+ const truncatedSources = truncateSource(sources);
+ await this.emitLargeDataEvent(
+ {
+ data: truncatedSources,
+ buildEventData: (chunk, { isPartial, chunkIndex, totalChunks }) => ({
+ structuredData: {
+ // Build your event data here
+ sources: chunk,
+ isPartial,
+ chunkIndex,
+ totalChunks,
+ },
+ }),
+ },
+ config,
+ );
}
+ // Now proceed with building request messages after all chunks are sent
const requestMessages = buildFinalRequestMessages({
module,
locale,
@@ -128,7 +144,7 @@ export class WebSearch extends BaseSkill {
rewrittenQuery: optimizedQuery,
});
- this.engine.logger.log(`Request messages: ${safeStringifyJSON(requestMessages)}`);
+ // this.engine.logger.log(`Request messages: ${safeStringifyJSON(requestMessages)}`);
// Generate answer using the model
const model = this.engine.chatModel({ temperature: 0.1 });
From e62d842945c7cf4d9b6b01ee8ae05428df6da694 Mon Sep 17 00:00:00 2001
From: pftom <1043269994@qq.com>
Date: Fri, 21 Feb 2025 19:44:42 +0800
Subject: [PATCH 3/6] chore: remove debug logging in SSE post and web search
skill
---
packages/ai-workspace-common/src/utils/sse-post.ts | 1 -
packages/skill-template/src/skills/web-search.ts | 2 --
2 files changed, 3 deletions(-)
diff --git a/packages/ai-workspace-common/src/utils/sse-post.ts b/packages/ai-workspace-common/src/utils/sse-post.ts
index 117c43e01..b03d140ca 100644
--- a/packages/ai-workspace-common/src/utils/sse-post.ts
+++ b/packages/ai-workspace-common/src/utils/sse-post.ts
@@ -141,7 +141,6 @@ export const ssePost = async ({
bufferStr = lines[lines.length - 1];
} catch (err) {
- console.log('actual err', err);
onSkillError(err);
onCompleted?.(true);
hasError = true;
diff --git a/packages/skill-template/src/skills/web-search.ts b/packages/skill-template/src/skills/web-search.ts
index 3c6a3643e..aae4694e4 100644
--- a/packages/skill-template/src/skills/web-search.ts
+++ b/packages/skill-template/src/skills/web-search.ts
@@ -144,8 +144,6 @@ export class WebSearch extends BaseSkill {
rewrittenQuery: optimizedQuery,
});
- // this.engine.logger.log(`Request messages: ${safeStringifyJSON(requestMessages)}`);
-
// Generate answer using the model
const model = this.engine.chatModel({ temperature: 0.1 });
const responseMessage = await model.invoke(requestMessages, {
From 3417871e17fcc7fa6cabcf7838fc6b7c0fff36a3 Mon Sep 17 00:00:00 2001
From: pftom <1043269994@qq.com>
Date: Fri, 21 Feb 2025 20:10:12 +0800
Subject: [PATCH 4/6] feat(skills): implement large data event emission for
source truncation
- Replace direct event emission with chunked `emitLargeDataEvent` method
- Add support for partial source data transmission across multiple skills
- Enhance source data handling with chunk indexing and completeness tracking
---
.../module/multiLingualSearch/index.ts | 2 --
.../skill-template/src/skills/common-qna.ts | 17 +++++++++++++++-
.../skill-template/src/skills/edit-doc.ts | 18 ++++++++++++++++-
.../skill-template/src/skills/generate-doc.ts | 20 ++++++++++++++++++-
.../src/skills/library-search.ts | 18 ++++++++++++++++-
.../src/skills/recommend-questions.ts | 16 ++++++++++++++-
.../skill-template/src/skills/rewrite-doc.ts | 16 ++++++++++++++-
7 files changed, 99 insertions(+), 8 deletions(-)
diff --git a/packages/skill-template/src/scheduler/module/multiLingualSearch/index.ts b/packages/skill-template/src/scheduler/module/multiLingualSearch/index.ts
index 7e22a6e97..349339d66 100644
--- a/packages/skill-template/src/scheduler/module/multiLingualSearch/index.ts
+++ b/packages/skill-template/src/scheduler/module/multiLingualSearch/index.ts
@@ -335,8 +335,6 @@ export const callMultiLingualWebSearch = async (
// Keep original results if deduplication fails
}
- // ctxThis.emitEvent({ structuredData: { multiLingualSearchResult: finalResults } }, config);
-
// Return results with analysis
return {
sources: finalResults,
diff --git a/packages/skill-template/src/skills/common-qna.ts b/packages/skill-template/src/skills/common-qna.ts
index 21f1184cd..da204e71a 100644
--- a/packages/skill-template/src/skills/common-qna.ts
+++ b/packages/skill-template/src/skills/common-qna.ts
@@ -139,7 +139,22 @@ export class CommonQnA extends BaseSkill {
config.metadata.step = { name: 'answerQuestion' };
if (sources.length > 0) {
- this.emitEvent({ structuredData: { sources: truncateSource(sources) } }, config);
+ // Truncate sources before emitting
+ const truncatedSources = truncateSource(sources);
+ await this.emitLargeDataEvent(
+ {
+ data: truncatedSources,
+ buildEventData: (chunk, { isPartial, chunkIndex, totalChunks }) => ({
+ structuredData: {
+ sources: chunk,
+ isPartial,
+ chunkIndex,
+ totalChunks,
+ },
+ }),
+ },
+ config,
+ );
}
const model = this.engine.chatModel({ temperature: 0.1 });
diff --git a/packages/skill-template/src/skills/edit-doc.ts b/packages/skill-template/src/skills/edit-doc.ts
index 7f51d0bb5..d3afecdf9 100644
--- a/packages/skill-template/src/skills/edit-doc.ts
+++ b/packages/skill-template/src/skills/edit-doc.ts
@@ -166,7 +166,23 @@ export class EditDoc extends BaseSkill {
this.engine.logger.log(`context: ${safeStringifyJSON(context)}`);
if (sources.length > 0) {
- this.emitEvent({ structuredData: { sources: truncateSource(sources) } }, config);
+ // Split sources into smaller chunks based on size and emit them separately
+ const truncatedSources = truncateSource(sources);
+ await this.emitLargeDataEvent(
+ {
+ data: truncatedSources,
+ buildEventData: (chunk, { isPartial, chunkIndex, totalChunks }) => ({
+ structuredData: {
+ // Build your event data here
+ sources: chunk,
+ isPartial,
+ chunkIndex,
+ totalChunks,
+ },
+ }),
+ },
+ config,
+ );
}
}
diff --git a/packages/skill-template/src/skills/generate-doc.ts b/packages/skill-template/src/skills/generate-doc.ts
index 0c4fbd83c..1d3cf4818 100644
--- a/packages/skill-template/src/skills/generate-doc.ts
+++ b/packages/skill-template/src/skills/generate-doc.ts
@@ -222,7 +222,7 @@ ${recentHistory.map((msg) => `${(msg as HumanMessage)?.getType?.()}: ${msg.conte
buildContextUserPrompt: generateDocument.buildGenerateDocumentContextUserPrompt,
};
- const { optimizedQuery, requestMessages, context, usedChatHistory } =
+ const { optimizedQuery, requestMessages, context, sources, usedChatHistory } =
await this.commonPreprocess(state, config, module);
// Generate title first
@@ -263,6 +263,24 @@ ${recentHistory.map((msg) => `${(msg as HumanMessage)?.getType?.()}: ${msg.conte
config,
);
+ if (sources.length > 0) {
+ const truncatedSources = truncateSource(sources);
+ await this.emitLargeDataEvent(
+ {
+ data: truncatedSources,
+ buildEventData: (chunk, { isPartial, chunkIndex, totalChunks }) => ({
+ structuredData: {
+ sources: chunk,
+ isPartial,
+ chunkIndex,
+ totalChunks,
+ },
+ }),
+ },
+ config,
+ );
+ }
+
const responseMessage = await model.invoke(requestMessages, {
...config,
metadata: {
diff --git a/packages/skill-template/src/skills/library-search.ts b/packages/skill-template/src/skills/library-search.ts
index 7cee77f76..7519907bf 100644
--- a/packages/skill-template/src/skills/library-search.ts
+++ b/packages/skill-template/src/skills/library-search.ts
@@ -101,7 +101,23 @@ export class LibrarySearch extends BaseSkill {
};
if (sources.length > 0) {
- this.emitEvent({ structuredData: { sources: truncateSource(sources) } }, config);
+ // Split sources into smaller chunks based on size and emit them separately
+ const truncatedSources = truncateSource(sources);
+ await this.emitLargeDataEvent(
+ {
+ data: truncatedSources,
+ buildEventData: (chunk, { isPartial, chunkIndex, totalChunks }) => ({
+ structuredData: {
+ // Build your event data here
+ sources: chunk,
+ isPartial,
+ chunkIndex,
+ totalChunks,
+ },
+ }),
+ },
+ config,
+ );
}
const requestMessages = buildFinalRequestMessages({
diff --git a/packages/skill-template/src/skills/recommend-questions.ts b/packages/skill-template/src/skills/recommend-questions.ts
index c7db80a52..da21252f1 100644
--- a/packages/skill-template/src/skills/recommend-questions.ts
+++ b/packages/skill-template/src/skills/recommend-questions.ts
@@ -177,7 +177,21 @@ ${
Please generate relevant recommended questions in ${locale} language.`;
if (sources.length > 0) {
- this.emitEvent({ structuredData: { sources: truncateSource(sources) } }, config);
+ const truncatedSources = truncateSource(sources);
+ await this.emitLargeDataEvent(
+ {
+ data: truncatedSources,
+ buildEventData: (chunk, { isPartial, chunkIndex, totalChunks }) => ({
+ structuredData: {
+ sources: chunk,
+ isPartial,
+ chunkIndex,
+ totalChunks,
+ },
+ }),
+ },
+ config,
+ );
}
const result = await extractStructuredData(
diff --git a/packages/skill-template/src/skills/rewrite-doc.ts b/packages/skill-template/src/skills/rewrite-doc.ts
index 251489517..491bdabda 100644
--- a/packages/skill-template/src/skills/rewrite-doc.ts
+++ b/packages/skill-template/src/skills/rewrite-doc.ts
@@ -154,7 +154,21 @@ export class RewriteDoc extends BaseSkill {
this.engine.logger.log(`context: ${safeStringifyJSON(context)}`);
if (sources.length > 0) {
- this.emitEvent({ structuredData: { sources: truncateSource(sources) } }, config);
+ const truncatedSources = truncateSource(sources);
+ await this.emitLargeDataEvent(
+ {
+ data: truncatedSources,
+ buildEventData: (chunk, { isPartial, chunkIndex, totalChunks }) => ({
+ structuredData: {
+ sources: chunk,
+ isPartial,
+ chunkIndex,
+ totalChunks,
+ },
+ }),
+ },
+ config,
+ );
}
}
From 39ce90268a0b7d395151c7cbbf6a69e7508dee0e Mon Sep 17 00:00:00 2001
From: pftom <1043269994@qq.com>
Date: Fri, 21 Feb 2025 20:38:21 +0800
Subject: [PATCH 5/6] fix(result-aggregator): improve structured data merging
for partial events
- Handle partial structured data events with chunk tracking
- Merge sources across multiple partial events
- Preserve partial event metadata during aggregation
---
apps/api/src/utils/result.ts | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/apps/api/src/utils/result.ts b/apps/api/src/utils/result.ts
index 3dc467c06..cca770708 100644
--- a/apps/api/src/utils/result.ts
+++ b/apps/api/src/utils/result.ts
@@ -71,7 +71,24 @@ export class ResultAggregator {
break;
case 'structured_data':
if (event.structuredData) {
- step.structuredData = { ...step.structuredData, ...event.structuredData };
+ const structuredData = event.structuredData;
+ console.log('structuredData', structuredData?.isPartial);
+ if (structuredData?.isPartial !== undefined) {
+ const existingData = step.structuredData || {};
+ const existingSources = (existingData.sources || []) as any[];
+ step.structuredData = {
+ ...existingData,
+ sources: [
+ ...existingSources,
+ ...(Array.isArray(structuredData.sources) ? structuredData.sources : []),
+ ],
+ isPartial: structuredData.isPartial,
+ chunkIndex: structuredData.chunkIndex,
+ totalChunks: structuredData.totalChunks,
+ };
+ } else {
+ step.structuredData = { ...step.structuredData, ...event.structuredData };
+ }
}
break;
case 'log':
From 9f0dfbb24bac1d76ff6fb422e2942c6419474e7c Mon Sep 17 00:00:00 2001
From: pftom <1043269994@qq.com>
Date: Fri, 21 Feb 2025 20:40:01 +0800
Subject: [PATCH 6/6] chore(result-aggregator): remove debug logging for
structured data
---
apps/api/src/utils/result.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/apps/api/src/utils/result.ts b/apps/api/src/utils/result.ts
index cca770708..c341617cb 100644
--- a/apps/api/src/utils/result.ts
+++ b/apps/api/src/utils/result.ts
@@ -72,7 +72,6 @@ export class ResultAggregator {
case 'structured_data':
if (event.structuredData) {
const structuredData = event.structuredData;
- console.log('structuredData', structuredData?.isPartial);
if (structuredData?.isPartial !== undefined) {
const existingData = step.structuredData || {};
const existingSources = (existingData.sources || []) as any[];