@lobehub/lobehub 2.0.0-next.211 → 2.0.0-next.213
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/auto-i18n.yml +1 -1
- package/.github/workflows/bundle-analyzer.yml +1 -1
- package/.github/workflows/claude-auto-testing.yml +1 -1
- package/.github/workflows/claude-dedupe-issues.yml +1 -1
- package/.github/workflows/claude-issue-triage.yml +1 -1
- package/.github/workflows/claude-translate-comments.yml +1 -1
- package/.github/workflows/claude-translator.yml +1 -1
- package/.github/workflows/claude.yml +1 -1
- package/.github/workflows/desktop-build-electron.yml +2 -2
- package/.github/workflows/e2e.yml +1 -1
- package/.github/workflows/issue-auto-close-duplicates.yml +1 -1
- package/.github/workflows/lighthouse.yml +2 -2
- package/.github/workflows/lock-closed-issues.yml +1 -1
- package/.github/workflows/manual-build-desktop.yml +6 -6
- package/.github/workflows/pr-build-desktop.yml +5 -5
- package/.github/workflows/pr-build-docker.yml +2 -2
- package/.github/workflows/release-desktop-beta.yml +4 -4
- package/.github/workflows/release-docker.yml +2 -2
- package/.github/workflows/release.yml +1 -1
- package/.github/workflows/sync-database-schema.yml +1 -1
- package/.github/workflows/sync.yml +1 -1
- package/.github/workflows/test.yml +5 -5
- package/.github/workflows/verify-desktop-patch.yml +1 -1
- package/CHANGELOG.md +58 -0
- package/changelog/v1.json +14 -0
- package/locales/ar/models.json +35 -4
- package/locales/ar/providers.json +1 -0
- package/locales/bg-BG/models.json +24 -1
- package/locales/bg-BG/providers.json +1 -0
- package/locales/de-DE/models.json +30 -1
- package/locales/de-DE/providers.json +1 -0
- package/locales/en-US/models.json +1 -0
- package/locales/en-US/providers.json +1 -0
- package/locales/es-ES/models.json +32 -1
- package/locales/es-ES/providers.json +1 -0
- package/locales/fa-IR/models.json +48 -1
- package/locales/fa-IR/providers.json +1 -0
- package/locales/fr-FR/models.json +47 -1
- package/locales/fr-FR/providers.json +1 -0
- package/locales/it-IT/models.json +32 -1
- package/locales/it-IT/providers.json +1 -0
- package/locales/ja-JP/models.json +2 -1
- package/locales/ja-JP/providers.json +1 -0
- package/locales/ko-KR/models.json +24 -1
- package/locales/ko-KR/providers.json +1 -0
- package/locales/nl-NL/models.json +46 -1
- package/locales/nl-NL/providers.json +1 -0
- package/locales/pl-PL/models.json +41 -1
- package/locales/pl-PL/providers.json +1 -0
- package/locales/pt-BR/models.json +32 -1
- package/locales/pt-BR/providers.json +1 -0
- package/locales/ru-RU/models.json +54 -2
- package/locales/ru-RU/providers.json +1 -0
- package/locales/tr-TR/models.json +32 -1
- package/locales/tr-TR/providers.json +1 -0
- package/locales/vi-VN/models.json +37 -1
- package/locales/vi-VN/providers.json +1 -0
- package/locales/zh-CN/models.json +24 -3
- package/locales/zh-CN/providers.json +1 -0
- package/locales/zh-TW/models.json +11 -1
- package/locales/zh-TW/providers.json +1 -0
- package/package.json +1 -1
- package/packages/context-engine/src/engine/messages/types.ts +1 -1
- package/packages/model-runtime/src/core/BaseAI.ts +1 -1
- package/packages/model-runtime/src/core/streams/qwen.test.ts +140 -0
- package/packages/model-runtime/src/core/streams/qwen.ts +17 -5
- package/packages/model-runtime/src/types/chat.ts +12 -12
- package/packages/model-runtime/src/types/error.ts +1 -1
- package/packages/model-runtime/src/types/image.ts +1 -1
- package/src/app/(backend)/f/[id]/route.ts +2 -2
- package/src/app/[variants]/(main)/chat/features/Conversation/Header/index.tsx +2 -1
- package/src/app/[variants]/(main)/resource/library/_layout/Header/LibraryHead.tsx +28 -18
- package/src/features/ResourceManager/components/Explorer/ListView/index.tsx +68 -3
- package/src/features/ResourceManager/components/Explorer/MasonryView/MasonryFileItem/MasonryItemWrapper.tsx +0 -2
- package/src/features/ResourceManager/components/Explorer/MasonryView/index.tsx +114 -86
- package/src/features/ResourceManager/components/Explorer/ToolBar/BatchActionsDropdown.tsx +72 -32
- package/src/features/ResourceManager/components/Explorer/index.tsx +1 -14
- package/src/libs/better-auth/define-config.ts +1 -4
- package/src/libs/redis/upstash.ts +4 -1
- package/src/server/services/comfyui/config/constants.ts +7 -7
- package/src/server/services/comfyui/config/promptToolConst.ts +26 -26
- package/src/server/services/comfyui/utils/promptSplitter.ts +23 -23
- package/src/server/services/comfyui/utils/weightDType.ts +4 -5
|
@@ -279,11 +279,9 @@
|
|
|
279
279
|
"claude-3-opus-20240229.description": "Claude 3 Opus 是 Anthropic 最强大的模型,适用于高度复杂的任务,在性能、智能、流畅性和理解力方面表现卓越。",
|
|
280
280
|
"claude-3-sonnet-20240229.description": "Claude 3 Sonnet 在智能与速度之间取得平衡,适用于企业级工作负载,提供高效能与低成本的可靠部署。",
|
|
281
281
|
"claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 是 Anthropic 推出的最快、最智能的 Haiku 模型,具备闪电般的响应速度和增强的推理能力。",
|
|
282
|
-
"claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 是 Anthropic 推出的最快、最智能的 Haiku 模型,具备闪电般的响应速度和增强的推理能力。",
|
|
283
282
|
"claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking 是一款高级变体,能够展示其推理过程。",
|
|
284
283
|
"claude-opus-4-1-20250805.description": "Claude Opus 4.1 是 Anthropic 最新、最强大的模型,适用于高度复杂的任务,在性能、智能、流畅性和理解力方面表现卓越。",
|
|
285
284
|
"claude-opus-4-20250514.description": "Claude Opus 4 是 Anthropic 最强大的模型,专为处理高度复杂任务而设计,在性能、智能、流畅性和理解力方面表现卓越。",
|
|
286
|
-
"claude-opus-4-20250514.description": "Claude Opus 4 是 Anthropic 最强大的模型,专为处理高度复杂任务而设计,在性能、智能、流畅性和理解力方面表现卓越。",
|
|
287
285
|
"claude-opus-4-5-20251101.description": "Claude Opus 4.5 是 Anthropic 的旗舰模型,结合卓越智能与可扩展性能,适用于需要最高质量响应与推理的复杂任务。",
|
|
288
286
|
"claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking 可生成近乎即时的响应或可视化的逐步推理过程。",
|
|
289
287
|
"claude-sonnet-4-20250514.description": "Claude Sonnet 4 能够生成几乎即时的响应,或展示可视化的逐步思考过程。",
|
|
@@ -392,6 +390,29 @@
|
|
|
392
390
|
"deepseek-v3.2-think.description": "DeepSeek V3.2 Think 是一款完整的深度思考模型,具备更强的长链推理能力。",
|
|
393
391
|
"deepseek-v3.2.description": "DeepSeek-V3.2 是深度求索推出的首个将思考融入工具使用的混合推理模型,采用高效架构节省算力,结合大规模强化学习提升能力与大规模合成任务数据增强泛化能力,三者融合使其性能媲美 GPT-5-High,输出长度大幅降低,显著减少计算开销与用户等待时间。",
|
|
394
392
|
"deepseek-v3.description": "DeepSeek-V3 是一款强大的 MoE 模型,总参数量为 671B,每个 token 激活参数为 37B。",
|
|
393
|
+
"deepseek-vl2-small.description": "DeepSeek VL2 Small 是一款轻量级多模态模型,适用于资源受限和高并发场景。",
|
|
394
|
+
"deepseek-vl2.description": "DeepSeek VL2 是一款多模态模型,专注于图文理解和细粒度视觉问答。",
|
|
395
|
+
"deepseek/deepseek-chat-v3-0324.description": "DeepSeek V3 是一款拥有 685B 参数的 MoE 模型,是 DeepSeek 旗舰聊天系列的最新版本。\n\n该模型基于 [DeepSeek V3](/deepseek/deepseek-chat-v3) 构建,在多项任务中表现出色。",
|
|
396
|
+
"deepseek/deepseek-chat-v3-0324:free.description": "DeepSeek V3 是一款拥有 685B 参数的 MoE 模型,是 DeepSeek 旗舰聊天系列的最新版本。\n\n该模型基于 [DeepSeek V3](/deepseek/deepseek-chat-v3) 构建,在多项任务中表现出色。",
|
|
397
|
+
"deepseek/deepseek-chat-v3.1.description": "DeepSeek-V3.1 是 DeepSeek 推出的长上下文混合推理模型,支持思考/非思考模式切换及工具集成。",
|
|
398
|
+
"deepseek/deepseek-chat.description": "DeepSeek-V3 是 DeepSeek 面向复杂任务和工具集成的高性能混合推理模型。",
|
|
399
|
+
"deepseek/deepseek-r1-0528.description": "DeepSeek R1 0528 是一款更新版本,专注于开放可用性和更深层次的推理能力。",
|
|
400
|
+
"deepseek/deepseek-r1-0528:free.description": "DeepSeek-R1 在仅需极少标注数据的情况下显著提升推理能力,并在最终答案前输出思维链以提高准确性。",
|
|
401
|
+
"deepseek/deepseek-r1-distill-llama-70b.description": "DeepSeek R1 Distill Llama 70B 是基于 Llama 3.3 70B 蒸馏而成的大语言模型,使用 DeepSeek R1 输出进行微调,在性能上可媲美大型前沿模型。",
|
|
402
|
+
"deepseek/deepseek-r1-distill-llama-8b.description": "DeepSeek R1 Distill Llama 8B 是基于 Llama-3.1-8B-Instruct 蒸馏而成的大语言模型,使用 DeepSeek R1 输出进行训练。",
|
|
403
|
+
"deepseek/deepseek-r1-distill-qwen-14b.description": "DeepSeek R1 Distill Qwen 14B 是基于 Qwen 2.5 14B 蒸馏而成的大语言模型,使用 DeepSeek R1 输出进行训练。在多个基准测试中超越 OpenAI o1-mini,在密集模型中达到 SOTA 水平。基准亮点:\nAIME 2024 pass@1: 69.7\nMATH-500 pass@1: 93.9\nCodeForces Rating: 1481\n基于 DeepSeek R1 输出的微调实现了与更大前沿模型的竞争性能。",
|
|
404
|
+
"deepseek/deepseek-r1-distill-qwen-32b.description": "DeepSeek R1 Distill Qwen 32B 是基于 Qwen 2.5 32B 蒸馏而成的大语言模型,使用 DeepSeek R1 输出进行训练。在多个基准测试中超越 OpenAI o1-mini,在密集模型中达到 SOTA 水平。基准亮点:\nAIME 2024 pass@1: 72.6\nMATH-500 pass@1: 94.3\nCodeForces Rating: 1691\n基于 DeepSeek R1 输出的微调实现了与更大前沿模型的竞争性能。",
|
|
405
|
+
"deepseek/deepseek-r1.description": "DeepSeek R1 已更新为 DeepSeek-R1-0528。通过更强的计算资源和后训练算法优化,显著提升了推理深度与能力。在数学、编程和通用逻辑基准测试中表现优异,接近 o3 和 Gemini 2.5 Pro 等领先模型。",
|
|
406
|
+
"deepseek/deepseek-r1/community.description": "DeepSeek R1 是 DeepSeek 团队最新开源模型,在数学、编程和推理任务中表现出色,性能可与 OpenAI o1 相媲美。",
|
|
407
|
+
"deepseek/deepseek-r1:free.description": "DeepSeek-R1 在仅需极少标注数据的情况下显著提升推理能力,并在最终答案前输出思维链以提高准确性。",
|
|
408
|
+
"deepseek/deepseek-reasoner.description": "DeepSeek-V3 Thinking(reasoner)是 DeepSeek 的实验性推理模型,适用于高复杂度推理任务。",
|
|
409
|
+
"deepseek/deepseek-v3.1-base.description": "DeepSeek V3.1 Base 是 DeepSeek V3 模型的改进版本。",
|
|
410
|
+
"deepseek/deepseek-v3.description": "一款快速的通用大语言模型,具备增强的推理能力。",
|
|
411
|
+
"deepseek/deepseek-v3/community.description": "DeepSeek-V3 在推理速度方面相较前代实现重大突破,在开源模型中排名第一,并可媲美最先进的闭源模型。DeepSeek-V3 采用了在 DeepSeek-V2 中验证的多头潜在注意力(MLA)和 DeepSeekMoE 架构,并引入了无损辅助策略以实现负载均衡,以及多 token 预测训练目标以增强性能。",
|
|
412
|
+
"deepseek_r1.description": "DeepSeek-R1 是一款基于强化学习的推理模型,解决了重复性和可读性问题。在强化学习前,使用冷启动数据进一步提升推理能力。在数学、编程和推理任务中表现与 OpenAI-o1 相当,训练过程精心设计以提升整体效果。",
|
|
413
|
+
"deepseek_r1_distill_llama_70b.description": "DeepSeek-R1-Distill-Llama-70B 是基于 Llama-3.3-70B-Instruct 蒸馏而成。作为 DeepSeek-R1 系列的一部分,使用 DeepSeek-R1 生成的样本进行微调,在数学、编程和推理方面表现出色。",
|
|
414
|
+
"deepseek_r1_distill_qwen_14b.description": "DeepSeek-R1-Distill-Qwen-14B 是基于 Qwen2.5-14B 蒸馏而成,并使用 DeepSeek-R1 生成的 80 万高质量样本进行微调,具备强大的推理能力。",
|
|
415
|
+
"deepseek_r1_distill_qwen_32b.description": "DeepSeek-R1-Distill-Qwen-32B 是基于 Qwen2.5-32B 蒸馏而成,并使用 DeepSeek-R1 生成的 80 万高质量样本进行微调,在数学、编程和推理方面表现卓越。",
|
|
395
416
|
"gemini-flash-latest.description": "Latest release of Gemini Flash",
|
|
396
417
|
"gemini-flash-lite-latest.description": "Latest release of Gemini Flash-Lite",
|
|
397
418
|
"gemini-pro-latest.description": "Latest release of Gemini Pro",
|
|
@@ -644,4 +665,4 @@
|
|
|
644
665
|
"zai/glm-4.5.description": "GLM-4.5 系列专为智能体设计,旗舰版 GLM-4.5 结合推理、编程和智能体能力,总参数 355B(激活 32B),提供双模式混合推理系统。",
|
|
645
666
|
"zai/glm-4.5v.description": "GLM-4.5V 基于 GLM-4.5-Air 构建,继承 GLM-4.1V-Thinking 的成熟技术,采用强大的 106B 参数 MoE 架构扩展能力。",
|
|
646
667
|
"zenmux/auto.description": "ZenMux 自动路由根据请求自动选择性价比最高、性能最优的支持模型。"
|
|
647
|
-
}
|
|
668
|
+
}
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"volcengine.description": "字节跳动的模型服务平台,提供安全、功能丰富、具备价格优势的模型访问服务,并支持数据、微调、推理与评估的端到端工具链。",
|
|
64
64
|
"wenxin.description": "文心是一个面向企业的基础模型与 AI 原生应用开发一体化平台,提供生成式 AI 模型与应用工作流的端到端工具支持。",
|
|
65
65
|
"xai.description": "xAI 致力于构建加速科学发现的 AI,使命是加深人类对宇宙的理解。",
|
|
66
|
+
"xiaomimimo.description": "小米 MiMo 提供兼容 OpenAI API 的对话模型服务。mimo-v2-flash 模型支持深度推理、流式输出、函数调用、256K 上下文窗口,以及最多 128K 的输出。",
|
|
66
67
|
"xinference.description": "Xorbits Inference(Xinference)是一个开源平台,简化 AI 模型的运行与集成,支持在本地或云端运行开源大模型、向量模型与多模态模型,构建强大的 AI 应用。",
|
|
67
68
|
"zenmux.description": "ZenMux 是一个统一的 AI 聚合平台,支持 OpenAI、Anthropic、Google VertexAI 等,具备灵活路由能力,便于模型切换与管理。",
|
|
68
69
|
"zeroone.description": "01.AI 推动以人为本的 AI 2.0 革命,利用大模型创造经济与社会价值,构建新型 AI 生态与商业模式。",
|
|
@@ -481,6 +481,16 @@
|
|
|
481
481
|
"fal-ai/nano-banana.description": "Nano Banana 是 Google 最新、最快且最高效的原生多模態模型,支援透過對話進行圖像生成與編輯。",
|
|
482
482
|
"fal-ai/qwen-image-edit.description": "來自 Qwen 團隊的專業圖像編輯模型,支援語義與外觀編輯,能精準處理中英文文字,並實現風格轉換、物體旋轉等高品質編輯。",
|
|
483
483
|
"fal-ai/qwen-image.description": "來自 Qwen 團隊的強大圖像生成模型,具備優秀的中文文字渲染與多樣化視覺風格。",
|
|
484
|
+
"flux-1-schnell.description": "來自黑森林實驗室的 12B 參數文字轉圖像模型,透過潛在對抗擴散蒸餾技術,在 1 至 4 步內生成高品質圖像。其表現媲美封閉式替代方案,並以 Apache-2.0 授權釋出,供個人、研究與商業用途。",
|
|
485
|
+
"flux-dev.description": "FLUX.1 [dev] 是一款開放權重的蒸餾模型,僅限非商業用途。它保有接近專業水準的圖像品質與指令遵循能力,同時運行更高效,資源使用優於同等大小的標準模型。",
|
|
486
|
+
"flux-kontext-max.description": "最先進的語境圖像生成與編輯技術,結合文字與圖像輸入,實現精準且一致的結果。",
|
|
487
|
+
"flux-kontext-pro.description": "最先進的語境圖像生成與編輯技術,結合文字與圖像輸入,實現精準且一致的結果。",
|
|
488
|
+
"flux-merged.description": "FLUX.1-merged 結合了「DEV」版本的深層特徵與「Schnell」版本的高速優勢,拓展性能極限並擴大應用範圍。",
|
|
489
|
+
"flux-pro-1.1-ultra.description": "支援 4MP 輸出的超高解析度圖像生成,10 秒內產出清晰圖像。",
|
|
490
|
+
"flux-pro-1.1.description": "升級版專業級圖像生成模型,具備卓越圖像品質與精準提示遵循能力。",
|
|
491
|
+
"flux-pro.description": "頂級商業圖像生成模型,擁有無與倫比的圖像品質與多樣化輸出能力。",
|
|
492
|
+
"flux-schnell.description": "FLUX.1 [schnell] 是最先進的開源少步驟模型,超越同類競品,甚至優於如 Midjourney v6.0 與 DALL-E 3(HD)等強大非蒸餾模型。其精細調校保留預訓練多樣性,顯著提升視覺品質、指令遵循、尺寸與比例變化、字體處理與輸出多樣性。",
|
|
493
|
+
"flux.1-schnell.description": "FLUX.1-schnell 是一款高效能圖像生成模型,支援快速多風格輸出。",
|
|
484
494
|
"gemini-flash-latest.description": "Gemini Flash 最新版本",
|
|
485
495
|
"gemini-flash-lite-latest.description": "Gemini Flash-Lite 最新版本",
|
|
486
496
|
"gemini-pro-latest.description": "Gemini Pro 最新版本",
|
|
@@ -746,4 +756,4 @@
|
|
|
746
756
|
"zai/glm-4.5.description": "GLM-4.5 系列專為代理設計。旗艦版 GLM-4.5 結合推理、編碼與代理能力,總參數 355B(啟用 32B),提供混合推理系統的雙模式運行。",
|
|
747
757
|
"zai/glm-4.5v.description": "GLM-4.5V 建構於 GLM-4.5-Air 基礎上,延續 GLM-4.1V-Thinking 技術,並以強大的 106B MoE 架構擴展能力。",
|
|
748
758
|
"zenmux/auto.description": "ZenMux 自動路由會根據您的請求,從支援的選項中選擇性價比最高、效能最佳的模型。"
|
|
749
|
-
}
|
|
759
|
+
}
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"volcengine.description": "字節跳動的模型服務平台,提供安全、功能豐富且具成本效益的模型存取,並支援資料、微調、推理與評估的端到端工具鏈。",
|
|
64
64
|
"wenxin.description": "文心是一個企業級的基礎模型與 AI 原生應用開發平台,提供生成式 AI 模型與應用流程的端到端工具。",
|
|
65
65
|
"xai.description": "xAI 致力於加速科學發現,目標是深化人類對宇宙的理解。",
|
|
66
|
+
"xiaomimimo.description": "小米 MiMo 提供一項支援 OpenAI 相容 API 的對話模型服務。mimo-v2-flash 模型支援深度推理、串流輸出、函式呼叫、256K 上下文視窗,以及最多 128K 的輸出。",
|
|
66
67
|
"xinference.description": "Xorbits Inference(Xinference)是一個開源平台,簡化 AI 模型的運行與整合,支援在本地或雲端運行開源 LLM、嵌入模型與多模態模型,打造強大的 AI 應用。",
|
|
67
68
|
"zenmux.description": "ZenMux 是一個統一的 AI 聚合平台,支援 OpenAI、Anthropic、Google VertexAI 等,具備靈活路由功能,便於模型切換與管理。",
|
|
68
69
|
"zeroone.description": "01.AI 推動以人為本的 AI 2.0 革命,透過大型語言模型創造經濟與社會價值,構建全新 AI 生態與商業模式。",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.213",
|
|
4
4
|
"description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -90,7 +90,7 @@ export interface UserMemoryIdentityItem {
|
|
|
90
90
|
description?: string | null;
|
|
91
91
|
id?: string;
|
|
92
92
|
role?: string | null;
|
|
93
|
-
/** Identity type: personal (
|
|
93
|
+
/** Identity type: personal (role), professional (occupation), demographic (attribute) */
|
|
94
94
|
type?: 'demographic' | 'personal' | 'professional' | string | null;
|
|
95
95
|
[key: string]: unknown;
|
|
96
96
|
}
|
|
@@ -36,7 +36,7 @@ export interface LobeRuntimeAI {
|
|
|
36
36
|
options?: TextToSpeechOptions,
|
|
37
37
|
) => Promise<ArrayBuffer>;
|
|
38
38
|
|
|
39
|
-
//
|
|
39
|
+
// Model management related interface
|
|
40
40
|
pullModel?(params: PullModelParams, options?: ModelRequestOptions): Promise<Response>;
|
|
41
41
|
}
|
|
42
42
|
/* eslint-enabled */
|
|
@@ -479,6 +479,146 @@ describe('QwenAIStream', () => {
|
|
|
479
479
|
`data: [{"function":{"arguments":"","name":"get_weather"},"id":"call_123","index":0,"type":"function"}]\n\n`,
|
|
480
480
|
]);
|
|
481
481
|
});
|
|
482
|
+
|
|
483
|
+
it('should handle mixed text content followed by streaming tool calls (DeepSeek style)', async () => {
|
|
484
|
+
// This test simulates the stream pattern from DeepSeek models via Qwen API
|
|
485
|
+
// where text content is streamed first, followed by incremental tool call chunks
|
|
486
|
+
const mockOpenAIStream = new ReadableStream({
|
|
487
|
+
start(controller) {
|
|
488
|
+
// Text content chunks with role in first chunk
|
|
489
|
+
controller.enqueue({
|
|
490
|
+
choices: [
|
|
491
|
+
{
|
|
492
|
+
delta: { content: '看来', role: 'assistant' },
|
|
493
|
+
finish_reason: null,
|
|
494
|
+
index: 0,
|
|
495
|
+
},
|
|
496
|
+
],
|
|
497
|
+
id: 'chatcmpl-4f901cb2-91bc-9763-a2c8-3ed58e9f4075',
|
|
498
|
+
model: 'deepseek-v3',
|
|
499
|
+
object: 'chat.completion.chunk',
|
|
500
|
+
created: 1767574524,
|
|
501
|
+
});
|
|
502
|
+
controller.enqueue({
|
|
503
|
+
choices: [
|
|
504
|
+
{
|
|
505
|
+
delta: { content: '我的' },
|
|
506
|
+
finish_reason: null,
|
|
507
|
+
index: 0,
|
|
508
|
+
},
|
|
509
|
+
],
|
|
510
|
+
id: 'chatcmpl-4f901cb2-91bc-9763-a2c8-3ed58e9f4075',
|
|
511
|
+
model: 'deepseek-v3',
|
|
512
|
+
object: 'chat.completion.chunk',
|
|
513
|
+
created: 1767574524,
|
|
514
|
+
});
|
|
515
|
+
controller.enqueue({
|
|
516
|
+
choices: [
|
|
517
|
+
{
|
|
518
|
+
delta: { content: '函数调用格式有误。' },
|
|
519
|
+
finish_reason: null,
|
|
520
|
+
index: 0,
|
|
521
|
+
},
|
|
522
|
+
],
|
|
523
|
+
id: 'chatcmpl-4f901cb2-91bc-9763-a2c8-3ed58e9f4075',
|
|
524
|
+
model: 'deepseek-v3',
|
|
525
|
+
object: 'chat.completion.chunk',
|
|
526
|
+
created: 1767574524,
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// First tool call chunk with id, name, and partial arguments
|
|
530
|
+
controller.enqueue({
|
|
531
|
+
choices: [
|
|
532
|
+
{
|
|
533
|
+
delta: {
|
|
534
|
+
tool_calls: [
|
|
535
|
+
{
|
|
536
|
+
id: 'call_ff00c42325d74b979990cb',
|
|
537
|
+
type: 'function',
|
|
538
|
+
function: {
|
|
539
|
+
name: 'modelscope-time____get_current_time____mcp',
|
|
540
|
+
arguments: '{"',
|
|
541
|
+
},
|
|
542
|
+
index: 0,
|
|
543
|
+
},
|
|
544
|
+
],
|
|
545
|
+
},
|
|
546
|
+
finish_reason: null,
|
|
547
|
+
index: 0,
|
|
548
|
+
},
|
|
549
|
+
],
|
|
550
|
+
id: 'chatcmpl-4f901cb2-91bc-9763-a2c8-3ed58e9f4075',
|
|
551
|
+
model: 'deepseek-v3',
|
|
552
|
+
object: 'chat.completion.chunk',
|
|
553
|
+
created: 1767574524,
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
// Subsequent tool call chunk with only incremental arguments (no id)
|
|
557
|
+
controller.enqueue({
|
|
558
|
+
choices: [
|
|
559
|
+
{
|
|
560
|
+
delta: {
|
|
561
|
+
tool_calls: [
|
|
562
|
+
{
|
|
563
|
+
type: 'function',
|
|
564
|
+
function: {
|
|
565
|
+
arguments: 'timezone":"America/New_York"}',
|
|
566
|
+
},
|
|
567
|
+
index: 0,
|
|
568
|
+
},
|
|
569
|
+
],
|
|
570
|
+
},
|
|
571
|
+
finish_reason: null,
|
|
572
|
+
index: 0,
|
|
573
|
+
},
|
|
574
|
+
],
|
|
575
|
+
id: 'chatcmpl-4f901cb2-91bc-9763-a2c8-3ed58e9f4075',
|
|
576
|
+
model: 'deepseek-v3',
|
|
577
|
+
object: 'chat.completion.chunk',
|
|
578
|
+
created: 1767574524,
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
controller.close();
|
|
582
|
+
},
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
const onTextMock = vi.fn();
|
|
586
|
+
const onToolCallMock = vi.fn();
|
|
587
|
+
|
|
588
|
+
const protocolStream = QwenAIStream(mockOpenAIStream, {
|
|
589
|
+
callbacks: {
|
|
590
|
+
onText: onTextMock,
|
|
591
|
+
onToolsCalling: onToolCallMock,
|
|
592
|
+
},
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
const decoder = new TextDecoder();
|
|
596
|
+
const chunks = [];
|
|
597
|
+
|
|
598
|
+
// @ts-ignore
|
|
599
|
+
for await (const chunk of protocolStream) {
|
|
600
|
+
chunks.push(decoder.decode(chunk, { stream: true }));
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// Verify complete chunks array
|
|
604
|
+
expect(chunks).toEqual([
|
|
605
|
+
'id: chatcmpl-4f901cb2-91bc-9763-a2c8-3ed58e9f4075\n',
|
|
606
|
+
'event: text\n',
|
|
607
|
+
'data: "看来"\n\n',
|
|
608
|
+
'id: chatcmpl-4f901cb2-91bc-9763-a2c8-3ed58e9f4075\n',
|
|
609
|
+
'event: text\n',
|
|
610
|
+
'data: "我的"\n\n',
|
|
611
|
+
'id: chatcmpl-4f901cb2-91bc-9763-a2c8-3ed58e9f4075\n',
|
|
612
|
+
'event: text\n',
|
|
613
|
+
'data: "函数调用格式有误。"\n\n',
|
|
614
|
+
'id: chatcmpl-4f901cb2-91bc-9763-a2c8-3ed58e9f4075\n',
|
|
615
|
+
'event: tool_calls\n',
|
|
616
|
+
'data: [{"function":{"arguments":"{\\"","name":"modelscope-time____get_current_time____mcp"},"id":"call_ff00c42325d74b979990cb","index":0,"type":"function"}]\n\n',
|
|
617
|
+
'id: chatcmpl-4f901cb2-91bc-9763-a2c8-3ed58e9f4075\n',
|
|
618
|
+
'event: tool_calls\n',
|
|
619
|
+
'data: [{"function":{"arguments":"timezone\\":\\"America/New_York\\"}","name":null},"id":"call_ff00c42325d74b979990cb","index":0,"type":"function"}]\n\n',
|
|
620
|
+
]);
|
|
621
|
+
});
|
|
482
622
|
});
|
|
483
623
|
|
|
484
624
|
describe('transformQwenStream', () => {
|
|
@@ -70,8 +70,18 @@ export const transformQwenStream = (
|
|
|
70
70
|
|
|
71
71
|
if (item.delta?.tool_calls) {
|
|
72
72
|
return {
|
|
73
|
-
data: item.delta.tool_calls.map(
|
|
74
|
-
|
|
73
|
+
data: item.delta.tool_calls.map((value, index): StreamToolCallChunkData => {
|
|
74
|
+
// Store first tool call's info in streamContext for subsequent chunks
|
|
75
|
+
// (similar pattern to OpenAI stream handling)
|
|
76
|
+
if (streamContext && !streamContext.tool && value.id && value.function?.name) {
|
|
77
|
+
streamContext.tool = {
|
|
78
|
+
id: value.id,
|
|
79
|
+
index: typeof value.index !== 'undefined' ? value.index : index,
|
|
80
|
+
name: value.function.name,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {
|
|
75
85
|
// Qwen models may send tool_calls in two separate chunks:
|
|
76
86
|
// 1. First chunk: {id, name} without arguments
|
|
77
87
|
// 2. Second chunk: {id, arguments} without name
|
|
@@ -81,11 +91,13 @@ export const transformQwenStream = (
|
|
|
81
91
|
arguments: value.function?.arguments ?? '',
|
|
82
92
|
name: value.function?.name ?? null,
|
|
83
93
|
},
|
|
84
|
-
id
|
|
94
|
+
// For incremental chunks without id, use the stored tool id from streamContext
|
|
95
|
+
id:
|
|
96
|
+
value.id || streamContext?.tool?.id || generateToolCallId(index, value.function?.name),
|
|
85
97
|
index: typeof value.index !== 'undefined' ? value.index : index,
|
|
86
98
|
type: value.type || 'function',
|
|
87
|
-
}
|
|
88
|
-
),
|
|
99
|
+
};
|
|
100
|
+
}),
|
|
89
101
|
id: chunk.id,
|
|
90
102
|
type: 'tool_calls',
|
|
91
103
|
} as StreamProtocolToolCallChunk;
|
|
@@ -62,15 +62,15 @@ export interface OpenAIChatMessage {
|
|
|
62
62
|
export interface ChatStreamPayload {
|
|
63
63
|
apiMode?: 'chatCompletion' | 'responses';
|
|
64
64
|
/**
|
|
65
|
-
*
|
|
65
|
+
* Enable context caching
|
|
66
66
|
*/
|
|
67
67
|
enabledContextCaching?: boolean;
|
|
68
68
|
/**
|
|
69
|
-
*
|
|
69
|
+
* Whether to enable search
|
|
70
70
|
*/
|
|
71
71
|
enabledSearch?: boolean;
|
|
72
72
|
/**
|
|
73
|
-
* @title
|
|
73
|
+
* @title Penalty coefficient for reducing repetitiveness in generated text
|
|
74
74
|
* @default 0
|
|
75
75
|
*/
|
|
76
76
|
frequency_penalty?: number;
|
|
@@ -83,23 +83,23 @@ export interface ChatStreamPayload {
|
|
|
83
83
|
*/
|
|
84
84
|
imageResolution?: '1K' | '2K' | '4K';
|
|
85
85
|
/**
|
|
86
|
-
* @title
|
|
86
|
+
* @title Maximum length of generated text
|
|
87
87
|
*/
|
|
88
88
|
max_tokens?: number;
|
|
89
89
|
/**
|
|
90
|
-
* @title
|
|
90
|
+
* @title List of chat messages
|
|
91
91
|
*/
|
|
92
92
|
messages: OpenAIChatMessage[];
|
|
93
93
|
/**
|
|
94
|
-
* @title
|
|
94
|
+
* @title Model name
|
|
95
95
|
*/
|
|
96
96
|
model: string;
|
|
97
97
|
/**
|
|
98
|
-
* @title
|
|
98
|
+
* @title Number of text responses to return
|
|
99
99
|
*/
|
|
100
100
|
n?: number;
|
|
101
101
|
/**
|
|
102
|
-
* @title
|
|
102
|
+
* @title Penalty coefficient for reducing topic variation in generated text
|
|
103
103
|
* @default 0
|
|
104
104
|
*/
|
|
105
105
|
presence_penalty?: number;
|
|
@@ -112,12 +112,12 @@ export interface ChatStreamPayload {
|
|
|
112
112
|
responseMode?: 'stream' | 'json';
|
|
113
113
|
response_format?: ChatResponseFormat;
|
|
114
114
|
/**
|
|
115
|
-
* @title
|
|
115
|
+
* @title Whether to enable streaming requests
|
|
116
116
|
* @default true
|
|
117
117
|
*/
|
|
118
118
|
stream?: boolean;
|
|
119
119
|
/**
|
|
120
|
-
* @title
|
|
120
|
+
* @title Randomness measure for generated text, controls creativity and diversity
|
|
121
121
|
* @default 1
|
|
122
122
|
*/
|
|
123
123
|
temperature?: number;
|
|
@@ -139,13 +139,13 @@ export interface ChatStreamPayload {
|
|
|
139
139
|
tool_choice?: string;
|
|
140
140
|
tools?: ChatCompletionTool[];
|
|
141
141
|
/**
|
|
142
|
-
* @title
|
|
142
|
+
* @title Controls the highest probability single token in generated text
|
|
143
143
|
* @default 1
|
|
144
144
|
*/
|
|
145
145
|
top_p?: number;
|
|
146
146
|
truncation?: 'auto' | 'disabled';
|
|
147
147
|
/**
|
|
148
|
-
* @title Gemini URL
|
|
148
|
+
* @title Gemini URL context fetching tool toggle
|
|
149
149
|
*/
|
|
150
150
|
urlContext?: boolean;
|
|
151
151
|
verbosity?: 'low' | 'medium' | 'high';
|
|
@@ -34,7 +34,7 @@ export type CreateImageResponse = {
|
|
|
34
34
|
modelUsage?: ModelUsage;
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
//
|
|
37
|
+
// New: Runtime interface for authenticated image download support
|
|
38
38
|
export interface AuthenticatedImageRuntime {
|
|
39
39
|
/**
|
|
40
40
|
* Get authentication headers for image download
|
|
@@ -43,8 +43,8 @@ export const GET = async (_req: Request, segmentData: { params: Params }) => {
|
|
|
43
43
|
|
|
44
44
|
const cacheKey = buildCacheKey(id);
|
|
45
45
|
if (redisClient) {
|
|
46
|
-
|
|
47
|
-
const cached = (
|
|
46
|
+
const cachedStr = await redisClient.get(cacheKey);
|
|
47
|
+
const cached = cachedStr ? (JSON.parse(cachedStr) as CachedFileData) : null;
|
|
48
48
|
if (cached?.redirectUrl) {
|
|
49
49
|
log('Cache hit for file: %s', id);
|
|
50
50
|
return Response.redirect(cached.redirectUrl, 302);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { isDesktop } from '@lobechat/const';
|
|
3
4
|
import { Flexbox } from '@lobehub/ui';
|
|
4
5
|
import { cssVar } from 'antd-style';
|
|
5
6
|
import { memo } from 'react';
|
|
@@ -22,7 +23,7 @@ const Header = memo(() => {
|
|
|
22
23
|
}
|
|
23
24
|
right={
|
|
24
25
|
<Flexbox align={'center'} horizontal style={{ backgroundColor: cssVar.colorBgContainer }}>
|
|
25
|
-
<WorkingDirectory />
|
|
26
|
+
{isDesktop && <WorkingDirectory />}
|
|
26
27
|
<NotebookButton />
|
|
27
28
|
<ShareButton />
|
|
28
29
|
<HeaderActions />
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useDroppable } from '@dnd-kit/core';
|
|
4
3
|
import { Center, type DropdownItem, DropdownMenu, Flexbox, Skeleton, Text } from '@lobehub/ui';
|
|
5
4
|
import { createStaticStyles, cx } from 'antd-style';
|
|
6
5
|
import { ChevronsUpDown } from 'lucide-react';
|
|
7
|
-
import { memo, useCallback, useMemo } from 'react';
|
|
6
|
+
import { type DragEvent, memo, useCallback, useMemo, useState } from 'react';
|
|
8
7
|
import { useNavigate } from 'react-router-dom';
|
|
9
8
|
|
|
10
9
|
import { useDragActive } from '@/app/[variants]/(main)/resource/features/DndContextWrapper';
|
|
@@ -48,24 +47,11 @@ const Head = memo<{ id: string }>(({ id }) => {
|
|
|
48
47
|
const name = useKnowledgeBaseStore(knowledgeBaseSelectors.getKnowledgeBaseNameById(id));
|
|
49
48
|
const setMode = useResourceManagerStore((s) => s.setMode);
|
|
50
49
|
const isDragActive = useDragActive();
|
|
50
|
+
const [isDropZoneActive, setIsDropZoneActive] = useState(false);
|
|
51
51
|
|
|
52
52
|
const useFetchKnowledgeBaseList = useKnowledgeBaseStore((s) => s.useFetchKnowledgeBaseList);
|
|
53
53
|
const { data: libraries } = useFetchKnowledgeBaseList();
|
|
54
54
|
|
|
55
|
-
// Special droppable ID for root folder - matches the pattern expected by DndContextWrapper
|
|
56
|
-
const ROOT_DROP_ID = `__root__:${id}`;
|
|
57
|
-
|
|
58
|
-
const { setNodeRef, isOver } = useDroppable({
|
|
59
|
-
data: {
|
|
60
|
-
fileType: 'custom/folder',
|
|
61
|
-
isFolder: true,
|
|
62
|
-
name: 'Root',
|
|
63
|
-
targetId: null,
|
|
64
|
-
},
|
|
65
|
-
disabled: !isDragActive,
|
|
66
|
-
id: ROOT_DROP_ID,
|
|
67
|
-
});
|
|
68
|
-
|
|
69
55
|
const handleClick = useCallback(() => {
|
|
70
56
|
navigate(`/resource/library/${id}`);
|
|
71
57
|
setMode('explorer');
|
|
@@ -79,6 +65,25 @@ const Head = memo<{ id: string }>(({ id }) => {
|
|
|
79
65
|
[navigate, setMode],
|
|
80
66
|
);
|
|
81
67
|
|
|
68
|
+
// Native HTML5 drag-and-drop handlers for root directory drop
|
|
69
|
+
const handleDragOver = useCallback(
|
|
70
|
+
(e: DragEvent<HTMLDivElement>) => {
|
|
71
|
+
if (!isDragActive) return;
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
e.stopPropagation();
|
|
74
|
+
setIsDropZoneActive(true);
|
|
75
|
+
},
|
|
76
|
+
[isDragActive],
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const handleDragLeave = useCallback(() => {
|
|
80
|
+
setIsDropZoneActive(false);
|
|
81
|
+
}, []);
|
|
82
|
+
|
|
83
|
+
const handleDrop = useCallback(() => {
|
|
84
|
+
setIsDropZoneActive(false);
|
|
85
|
+
}, []);
|
|
86
|
+
|
|
82
87
|
const menuItems = useMemo<DropdownItem[]>(() => {
|
|
83
88
|
if (!libraries) return [];
|
|
84
89
|
|
|
@@ -98,12 +103,17 @@ const Head = memo<{ id: string }>(({ id }) => {
|
|
|
98
103
|
return (
|
|
99
104
|
<Flexbox
|
|
100
105
|
align={'center'}
|
|
101
|
-
className={cx(styles.clickableHeader,
|
|
106
|
+
className={cx(styles.clickableHeader, isDropZoneActive && styles.dropZoneActive)}
|
|
107
|
+
data-drop-target-id="root"
|
|
108
|
+
data-is-folder="true"
|
|
109
|
+
data-root-drop="true"
|
|
102
110
|
gap={8}
|
|
103
111
|
horizontal
|
|
112
|
+
onDragLeave={handleDragLeave}
|
|
113
|
+
onDragOver={handleDragOver}
|
|
114
|
+
onDrop={handleDrop}
|
|
104
115
|
paddingBlock={6}
|
|
105
116
|
paddingInline={'12px 14px'}
|
|
106
|
-
ref={setNodeRef}
|
|
107
117
|
>
|
|
108
118
|
<Center style={{ minWidth: 24 }} width={24}>
|
|
109
119
|
<RepoIcon />
|
|
@@ -78,6 +78,9 @@ const ListView = memo(() => {
|
|
|
78
78
|
const [lastSelectedIndex, setLastSelectedIndex] = useState<number | null>(null);
|
|
79
79
|
const isDragActive = useDragActive();
|
|
80
80
|
const [isDropZoneActive, setIsDropZoneActive] = useState(false);
|
|
81
|
+
const scrollTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
82
|
+
const autoScrollIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
83
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
81
84
|
|
|
82
85
|
const { currentFolderSlug } = useFolderPath();
|
|
83
86
|
const { data: folderBreadcrumb } = useResourceManagerFetchFolderBreadcrumb(currentFolderSlug);
|
|
@@ -174,6 +177,18 @@ const ListView = memo(() => {
|
|
|
174
177
|
}
|
|
175
178
|
}, [fileListHasMore, loadMoreKnowledgeItems, isLoadingMore]);
|
|
176
179
|
|
|
180
|
+
// Clear auto-scroll timers
|
|
181
|
+
const clearScrollTimers = useCallback(() => {
|
|
182
|
+
if (scrollTimerRef.current) {
|
|
183
|
+
clearTimeout(scrollTimerRef.current);
|
|
184
|
+
scrollTimerRef.current = null;
|
|
185
|
+
}
|
|
186
|
+
if (autoScrollIntervalRef.current) {
|
|
187
|
+
clearInterval(autoScrollIntervalRef.current);
|
|
188
|
+
autoScrollIntervalRef.current = null;
|
|
189
|
+
}
|
|
190
|
+
}, []);
|
|
191
|
+
|
|
177
192
|
// Drop zone handlers for dragging to blank space
|
|
178
193
|
const handleDropZoneDragOver = useCallback(
|
|
179
194
|
(e: DragEvent) => {
|
|
@@ -187,11 +202,57 @@ const ListView = memo(() => {
|
|
|
187
202
|
|
|
188
203
|
const handleDropZoneDragLeave = useCallback(() => {
|
|
189
204
|
setIsDropZoneActive(false);
|
|
190
|
-
|
|
205
|
+
clearScrollTimers();
|
|
206
|
+
}, [clearScrollTimers]);
|
|
191
207
|
|
|
192
208
|
const handleDropZoneDrop = useCallback(() => {
|
|
193
209
|
setIsDropZoneActive(false);
|
|
194
|
-
|
|
210
|
+
clearScrollTimers();
|
|
211
|
+
}, [clearScrollTimers]);
|
|
212
|
+
|
|
213
|
+
// Handle auto-scroll during drag
|
|
214
|
+
const handleDragMove = useCallback(
|
|
215
|
+
(e: DragEvent<HTMLDivElement>) => {
|
|
216
|
+
if (!isDragActive || !containerRef.current) return;
|
|
217
|
+
|
|
218
|
+
const container = containerRef.current;
|
|
219
|
+
const rect = container.getBoundingClientRect();
|
|
220
|
+
const mouseY = e.clientY;
|
|
221
|
+
const bottomThreshold = 200; // pixels from bottom edge
|
|
222
|
+
const distanceFromBottom = rect.bottom - mouseY;
|
|
223
|
+
|
|
224
|
+
// Check if mouse is near the bottom edge
|
|
225
|
+
if (distanceFromBottom > 0 && distanceFromBottom <= bottomThreshold) {
|
|
226
|
+
// If not already started, start the 2-second timer
|
|
227
|
+
if (!scrollTimerRef.current && !autoScrollIntervalRef.current) {
|
|
228
|
+
scrollTimerRef.current = setTimeout(() => {
|
|
229
|
+
// After 2 seconds, start auto-scrolling
|
|
230
|
+
autoScrollIntervalRef.current = setInterval(() => {
|
|
231
|
+
virtuosoRef.current?.scrollBy({ top: 50 });
|
|
232
|
+
}, 100); // Scroll every 100ms for smooth scrolling
|
|
233
|
+
scrollTimerRef.current = null;
|
|
234
|
+
}, 2000);
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
// Mouse moved away from bottom edge, clear timers
|
|
238
|
+
clearScrollTimers();
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
[isDragActive, clearScrollTimers],
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
// Clean up timers when drag ends or component unmounts
|
|
245
|
+
useEffect(() => {
|
|
246
|
+
if (!isDragActive) {
|
|
247
|
+
clearScrollTimers();
|
|
248
|
+
}
|
|
249
|
+
}, [isDragActive, clearScrollTimers]);
|
|
250
|
+
|
|
251
|
+
useEffect(() => {
|
|
252
|
+
return () => {
|
|
253
|
+
clearScrollTimers();
|
|
254
|
+
};
|
|
255
|
+
}, [clearScrollTimers]);
|
|
195
256
|
|
|
196
257
|
return (
|
|
197
258
|
<Flexbox height={'100%'}>
|
|
@@ -227,8 +288,12 @@ const ListView = memo(() => {
|
|
|
227
288
|
data-drop-target-id={currentFolderId || undefined}
|
|
228
289
|
data-is-folder="true"
|
|
229
290
|
onDragLeave={handleDropZoneDragLeave}
|
|
230
|
-
onDragOver={
|
|
291
|
+
onDragOver={(e) => {
|
|
292
|
+
handleDropZoneDragOver(e);
|
|
293
|
+
handleDragMove(e);
|
|
294
|
+
}}
|
|
231
295
|
onDrop={handleDropZoneDrop}
|
|
296
|
+
ref={containerRef}
|
|
232
297
|
style={{ flex: 1, overflow: 'hidden', position: 'relative' }}
|
|
233
298
|
>
|
|
234
299
|
<Virtuoso
|
|
@@ -7,7 +7,6 @@ import MasonryFileItem from '.';
|
|
|
7
7
|
interface MasonryItemWrapperProps {
|
|
8
8
|
context: {
|
|
9
9
|
knowledgeBaseId?: string;
|
|
10
|
-
openFile?: (id: string) => void;
|
|
11
10
|
selectFileIds: string[];
|
|
12
11
|
setSelectedFileIds: (ids: string[]) => void;
|
|
13
12
|
};
|
|
@@ -25,7 +24,6 @@ const MasonryItemWrapper = memo<MasonryItemWrapperProps>(({ data: item, context
|
|
|
25
24
|
<div style={{ padding: '8px 4px' }}>
|
|
26
25
|
<MasonryFileItem
|
|
27
26
|
knowledgeBaseId={context.knowledgeBaseId}
|
|
28
|
-
onOpen={context.openFile}
|
|
29
27
|
onSelectedChange={(id, checked) => {
|
|
30
28
|
if (checked) {
|
|
31
29
|
context.setSelectedFileIds([...context.selectFileIds, id]);
|