@lobehub/lobehub 2.0.0-next.321 → 2.0.0-next.323

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.
Files changed (111) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/apps/desktop/src/main/core/infrastructure/UpdaterManager.ts +9 -76
  3. package/apps/desktop/src/main/core/infrastructure/__tests__/UpdaterManager.test.ts +0 -1
  4. package/apps/desktop/src/main/modules/updater/configs.ts +0 -4
  5. package/changelog/v1.json +20 -0
  6. package/e2e/src/mocks/llm/index.ts +3 -3
  7. package/locales/ar/common.json +5 -0
  8. package/locales/ar/error.json +10 -1
  9. package/locales/bg-BG/common.json +5 -0
  10. package/locales/bg-BG/error.json +10 -1
  11. package/locales/de-DE/common.json +5 -0
  12. package/locales/de-DE/error.json +10 -1
  13. package/locales/en-US/common.json +5 -0
  14. package/locales/es-ES/common.json +5 -0
  15. package/locales/es-ES/error.json +10 -1
  16. package/locales/fa-IR/common.json +5 -0
  17. package/locales/fa-IR/error.json +10 -1
  18. package/locales/fr-FR/common.json +5 -0
  19. package/locales/fr-FR/error.json +10 -1
  20. package/locales/it-IT/common.json +5 -0
  21. package/locales/it-IT/error.json +10 -1
  22. package/locales/ja-JP/common.json +5 -0
  23. package/locales/ja-JP/error.json +10 -1
  24. package/locales/ko-KR/common.json +5 -0
  25. package/locales/ko-KR/error.json +10 -1
  26. package/locales/nl-NL/common.json +5 -0
  27. package/locales/nl-NL/error.json +10 -1
  28. package/locales/pl-PL/common.json +5 -0
  29. package/locales/pl-PL/error.json +10 -1
  30. package/locales/pt-BR/common.json +5 -0
  31. package/locales/pt-BR/error.json +10 -1
  32. package/locales/ru-RU/common.json +5 -0
  33. package/locales/ru-RU/error.json +10 -1
  34. package/locales/tr-TR/common.json +5 -0
  35. package/locales/tr-TR/error.json +10 -1
  36. package/locales/vi-VN/common.json +5 -0
  37. package/locales/vi-VN/error.json +10 -1
  38. package/locales/zh-CN/common.json +5 -0
  39. package/locales/zh-TW/common.json +5 -0
  40. package/locales/zh-TW/error.json +10 -1
  41. package/package.json +2 -2
  42. package/packages/business/const/src/branding.ts +1 -0
  43. package/packages/business/const/src/llm.ts +2 -1
  44. package/packages/const/src/settings/llm.ts +2 -1
  45. package/packages/const/src/settings/systemAgent.ts +12 -7
  46. package/packages/database/src/models/agent.ts +18 -1
  47. package/packages/database/src/models/chatGroup.ts +18 -1
  48. package/packages/database/src/types/chatGroup.ts +1 -0
  49. package/packages/model-bank/package.json +1 -1
  50. package/packages/model-bank/src/aiModels/index.ts +2 -2
  51. package/packages/model-bank/src/aiModels/lobehub/chat/anthropic.ts +256 -0
  52. package/packages/model-bank/src/aiModels/lobehub/chat/deepseek.ts +45 -0
  53. package/packages/model-bank/src/aiModels/lobehub/chat/google.ts +267 -0
  54. package/packages/model-bank/src/aiModels/lobehub/chat/index.ts +26 -0
  55. package/packages/model-bank/src/aiModels/lobehub/chat/minimax.ts +75 -0
  56. package/packages/model-bank/src/aiModels/lobehub/chat/moonshot.ts +28 -0
  57. package/packages/model-bank/src/aiModels/lobehub/chat/openai.ts +345 -0
  58. package/packages/model-bank/src/aiModels/lobehub/chat/xai.ts +32 -0
  59. package/packages/model-bank/src/aiModels/lobehub/image.ts +240 -0
  60. package/packages/model-bank/src/aiModels/lobehub/index.ts +10 -0
  61. package/packages/model-bank/src/aiModels/lobehub/utils.ts +58 -0
  62. package/packages/model-bank/src/modelProviders/index.ts +10 -10
  63. package/packages/model-runtime/src/core/streams/qwen.test.ts +320 -0
  64. package/packages/model-runtime/src/core/streams/qwen.ts +19 -10
  65. package/packages/types/package.json +1 -1
  66. package/packages/types/src/agentGroup/index.ts +2 -0
  67. package/packages/types/src/discover/assistants.ts +9 -0
  68. package/packages/types/src/discover/fork.ts +163 -0
  69. package/packages/types/src/discover/groupAgents.ts +13 -4
  70. package/packages/types/src/discover/index.ts +9 -0
  71. package/src/app/[variants]/(auth)/_layout/index.tsx +2 -1
  72. package/src/app/[variants]/(auth)/auth-error/page.tsx +5 -5
  73. package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/List/Item/index.tsx +1 -2
  74. package/src/app/[variants]/(main)/agent/profile/index.tsx +15 -2
  75. package/src/app/[variants]/(main)/community/(detail)/agent/features/Header.tsx +37 -0
  76. package/src/app/[variants]/(main)/community/(detail)/agent/features/Sidebar/ActionButton/ForkAndChat.tsx +133 -0
  77. package/src/app/[variants]/(main)/community/(detail)/agent/features/Sidebar/ActionButton/index.tsx +2 -2
  78. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Details/index.tsx +7 -10
  79. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/ActionButton/ForkGroupAndChat.tsx +208 -0
  80. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/ActionButton/index.tsx +2 -2
  81. package/src/app/[variants]/(main)/community/(detail)/user/features/DetailProvider.tsx +2 -0
  82. package/src/app/[variants]/(main)/community/(detail)/user/features/UserContent.tsx +7 -0
  83. package/src/app/[variants]/(main)/community/(detail)/user/features/UserForkedAgentGroups.tsx +63 -0
  84. package/src/app/[variants]/(main)/community/(detail)/user/features/UserForkedAgents.tsx +61 -0
  85. package/src/app/[variants]/(main)/community/(detail)/user/index.tsx +3 -1
  86. package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/List/Item/index.tsx +1 -2
  87. package/src/app/[variants]/(main)/settings/profile/index.tsx +92 -68
  88. package/src/app/[variants]/(mobile)/chat/features/Topic/index.tsx +2 -1
  89. package/src/features/CommandMenu/AskAgentCommands.tsx +105 -0
  90. package/src/features/CommandMenu/CommandMenuContext.tsx +57 -38
  91. package/src/features/CommandMenu/components/CommandInput.tsx +43 -9
  92. package/src/features/CommandMenu/index.tsx +89 -27
  93. package/src/features/CommandMenu/types.ts +6 -0
  94. package/src/features/CommandMenu/useCommandMenu.ts +62 -39
  95. package/src/features/PageEditor/PageEditor.tsx +20 -8
  96. package/src/locales/default/common.ts +5 -0
  97. package/src/locales/default/discover.ts +371 -0
  98. package/src/server/globalConfig/parseMemoryExtractionConfig.ts +7 -8
  99. package/src/server/routers/lambda/agent.ts +14 -0
  100. package/src/server/routers/lambda/agentGroup.ts +19 -3
  101. package/src/server/routers/lambda/market/agent.ts +234 -26
  102. package/src/server/routers/lambda/market/agentGroup.ts +204 -1
  103. package/src/server/services/discover/index.ts +52 -2
  104. package/src/server/services/memory/userMemory/__tests__/extract.runtime.test.ts +12 -2
  105. package/src/server/services/memory/userMemory/extract.ts +11 -2
  106. package/src/services/agent.ts +8 -0
  107. package/src/services/chatGroup/index.ts +8 -0
  108. package/src/services/marketApi.ts +78 -0
  109. package/src/store/user/slices/settings/selectors/__snapshots__/settings.test.ts.snap +12 -12
  110. package/src/utils/styles.ts +10 -0
  111. package/packages/model-bank/src/aiModels/lobehub.ts +0 -1315
@@ -131,23 +131,23 @@ export const LOBE_DEFAULT_MODEL_LIST: ChatModelCard[] = [
131
131
 
132
132
  export const DEFAULT_MODEL_PROVIDER_LIST = [
133
133
  ...(ENABLE_BUSINESS_FEATURES ? [LobeHubProvider] : []),
134
- OpenAIProvider,
135
- { ...AzureProvider, chatModels: [] },
136
- AzureAIProvider,
137
- OllamaProvider,
138
- OllamaCloudProvider,
139
- VLLMProvider,
140
- ComfyUIProvider,
141
- XinferenceProvider,
142
134
  AnthropicProvider,
143
- BedrockProvider,
144
135
  GoogleProvider,
145
- VertexAIProvider,
136
+ OpenAIProvider,
146
137
  DeepSeekProvider,
138
+ XinferenceProvider,
147
139
  MoonshotProvider,
140
+ BedrockProvider,
141
+ VertexAIProvider,
142
+ { ...AzureProvider, chatModels: [] },
143
+ AzureAIProvider,
148
144
  AiHubMixProvider,
149
145
  OpenRouterProvider,
150
146
  FalProvider,
147
+ OllamaProvider,
148
+ OllamaCloudProvider,
149
+ VLLMProvider,
150
+ ComfyUIProvider,
151
151
  HuggingFaceProvider,
152
152
  CloudflareProvider,
153
153
  GithubProvider,
@@ -621,6 +621,326 @@ describe('QwenAIStream', () => {
621
621
  });
622
622
  });
623
623
 
624
+ // Test case for parallel tool calls bug (LOBE-3903)
625
+ // This test reproduces the issue where Qwen model returns 3 parallel tool calls
626
+ // for querying time in Beijing, Shanghai, and Nanjing simultaneously.
627
+ // The bug causes arguments from different tool calls to be incorrectly merged.
628
+ describe('parallel tool calls streaming bug', () => {
629
+ it('should handle 3 parallel tool calls with incremental arguments (Qwen qwen3-max behavior)', async () => {
630
+ // This test simulates the exact stream pattern from the bug report:
631
+ // User asks: "查一下北京、上海、南京的时间,同时调用3次mcp"
632
+ // Model returns 3 parallel tool calls with index 0, 1, 2
633
+ // Subsequent chunks contain arguments without id field, only index
634
+ const streamId = 'chatcmpl-23f324a2-059f-9ab4-b7b3-f47bcba5ebf7';
635
+
636
+ // Define all chunks as an array for clarity and maintainability
637
+ const chunks = [
638
+ // Chunk 0: First tool call starts (index=0)
639
+ {
640
+ id: streamId,
641
+ object: 'chat.completion.chunk',
642
+ created: 1768906556,
643
+ model: 'qwen3-max',
644
+ choices: [
645
+ {
646
+ index: 0,
647
+ delta: {
648
+ role: 'assistant',
649
+ tool_calls: [
650
+ {
651
+ id: 'call_c7d8b4984a4d4f54a4956bca',
652
+ type: 'function',
653
+ function: { name: 'time____get_time____mcp', arguments: '' },
654
+ index: 0,
655
+ },
656
+ ],
657
+ },
658
+ finish_reason: null,
659
+ },
660
+ ],
661
+ },
662
+ // Chunk 1: First tool call continues with empty arguments
663
+ {
664
+ id: streamId,
665
+ object: 'chat.completion.chunk',
666
+ created: 1768906556,
667
+ model: 'qwen3-max',
668
+ choices: [
669
+ {
670
+ index: 0,
671
+ delta: {
672
+ tool_calls: [
673
+ {
674
+ id: 'call_c7d8b4984a4d4f54a4956bca',
675
+ type: 'function',
676
+ function: { arguments: '' },
677
+ index: 0,
678
+ },
679
+ ],
680
+ },
681
+ finish_reason: null,
682
+ },
683
+ ],
684
+ },
685
+ // Chunk 2: First tool call arguments part 1 (北京)
686
+ {
687
+ id: streamId,
688
+ object: 'chat.completion.chunk',
689
+ created: 1768906556,
690
+ model: 'qwen3-max',
691
+ choices: [
692
+ {
693
+ index: 0,
694
+ delta: {
695
+ tool_calls: [
696
+ { type: 'function', function: { arguments: '{"location": "北京' }, index: 0 },
697
+ ],
698
+ },
699
+ finish_reason: null,
700
+ },
701
+ ],
702
+ },
703
+ // Chunk 3: First tool call arguments part 2
704
+ {
705
+ id: streamId,
706
+ object: 'chat.completion.chunk',
707
+ created: 1768906556,
708
+ model: 'qwen3-max',
709
+ choices: [
710
+ {
711
+ index: 0,
712
+ delta: { tool_calls: [{ type: 'function', function: { arguments: '"}' }, index: 0 }] },
713
+ finish_reason: null,
714
+ },
715
+ ],
716
+ },
717
+ // Chunk 4: Empty arguments for first tool call
718
+ {
719
+ id: streamId,
720
+ object: 'chat.completion.chunk',
721
+ created: 1768906556,
722
+ model: 'qwen3-max',
723
+ choices: [
724
+ {
725
+ index: 0,
726
+ delta: { tool_calls: [{ type: 'function', function: { arguments: '' }, index: 0 }] },
727
+ finish_reason: null,
728
+ },
729
+ ],
730
+ },
731
+ // Chunk 5: Second tool call starts (index=1) - 上海
732
+ {
733
+ id: streamId,
734
+ object: 'chat.completion.chunk',
735
+ created: 1768906556,
736
+ model: 'qwen3-max',
737
+ choices: [
738
+ {
739
+ index: 0,
740
+ delta: {
741
+ tool_calls: [
742
+ {
743
+ id: 'call_f564785a14534d9a8c5ee641',
744
+ type: 'function',
745
+ function: { name: 'time____get_time____mcp', arguments: '' },
746
+ index: 1,
747
+ },
748
+ ],
749
+ },
750
+ finish_reason: null,
751
+ },
752
+ ],
753
+ },
754
+ // Chunk 6: Second tool call arguments (上海)
755
+ {
756
+ id: streamId,
757
+ object: 'chat.completion.chunk',
758
+ created: 1768906556,
759
+ model: 'qwen3-max',
760
+ choices: [
761
+ {
762
+ index: 0,
763
+ delta: {
764
+ tool_calls: [
765
+ { type: 'function', function: { arguments: '{"location": "上海' }, index: 1 },
766
+ ],
767
+ },
768
+ finish_reason: null,
769
+ },
770
+ ],
771
+ },
772
+ // Chunk 7: Second tool call arguments part 2
773
+ {
774
+ id: streamId,
775
+ object: 'chat.completion.chunk',
776
+ created: 1768906556,
777
+ model: 'qwen3-max',
778
+ choices: [
779
+ {
780
+ index: 0,
781
+ delta: { tool_calls: [{ type: 'function', function: { arguments: '"}' }, index: 1 }] },
782
+ finish_reason: null,
783
+ },
784
+ ],
785
+ },
786
+ // Chunk 8: Third tool call starts (index=2) - 南京
787
+ {
788
+ id: streamId,
789
+ object: 'chat.completion.chunk',
790
+ created: 1768906556,
791
+ model: 'qwen3-max',
792
+ choices: [
793
+ {
794
+ index: 0,
795
+ delta: {
796
+ tool_calls: [
797
+ {
798
+ id: 'call_19693813aebd434aab821f06',
799
+ type: 'function',
800
+ function: { name: 'time____get_time____mcp', arguments: '{"location": "' },
801
+ index: 2,
802
+ },
803
+ ],
804
+ },
805
+ finish_reason: null,
806
+ },
807
+ ],
808
+ },
809
+ // Chunk 9: Third tool call arguments (南京)
810
+ {
811
+ id: streamId,
812
+ object: 'chat.completion.chunk',
813
+ created: 1768906556,
814
+ model: 'qwen3-max',
815
+ choices: [
816
+ {
817
+ index: 0,
818
+ delta: {
819
+ tool_calls: [{ type: 'function', function: { arguments: '南京"' }, index: 2 }],
820
+ },
821
+ finish_reason: null,
822
+ },
823
+ ],
824
+ },
825
+ // Chunk 10: Third tool call arguments final
826
+ {
827
+ id: streamId,
828
+ object: 'chat.completion.chunk',
829
+ created: 1768906556,
830
+ model: 'qwen3-max',
831
+ choices: [
832
+ {
833
+ index: 0,
834
+ delta: { tool_calls: [{ type: 'function', function: { arguments: '}' }, index: 2 }] },
835
+ finish_reason: null,
836
+ },
837
+ ],
838
+ },
839
+ // Chunk 11: Finish
840
+ {
841
+ id: streamId,
842
+ object: 'chat.completion.chunk',
843
+ created: 1768906556,
844
+ model: 'qwen3-max',
845
+ choices: [{ index: 0, delta: {}, finish_reason: 'tool_calls' }],
846
+ },
847
+ ];
848
+
849
+ const mockOpenAIStream = new ReadableStream({
850
+ start(controller) {
851
+ chunks.forEach((chunk) => controller.enqueue(chunk));
852
+ controller.close();
853
+ },
854
+ });
855
+
856
+ let aggregatedToolCalls: any[] = [];
857
+
858
+ const protocolStream = QwenAIStream(mockOpenAIStream, {
859
+ callbacks: {
860
+ onToolsCalling: ({ toolsCalling }) => {
861
+ aggregatedToolCalls = toolsCalling;
862
+ },
863
+ },
864
+ });
865
+
866
+ const decoder = new TextDecoder();
867
+ const outputChunks: string[] = [];
868
+
869
+ // @ts-ignore
870
+ for await (const chunk of protocolStream) {
871
+ outputChunks.push(decoder.decode(chunk, { stream: true }));
872
+ }
873
+
874
+ // Verify streaming chunks output format (SSE protocol)
875
+ // Each tool call chunk should have correct id based on its index
876
+ expect(outputChunks).toEqual([
877
+ // Chunk 0: First tool call starts (index=0)
878
+ `id: ${streamId}\n`,
879
+ 'event: tool_calls\n',
880
+ 'data: [{"function":{"arguments":"","name":"time____get_time____mcp"},"id":"call_c7d8b4984a4d4f54a4956bca","index":0,"type":"function"}]\n\n',
881
+ // Chunk 1: First tool call continues
882
+ `id: ${streamId}\n`,
883
+ 'event: tool_calls\n',
884
+ 'data: [{"function":{"arguments":"","name":null},"id":"call_c7d8b4984a4d4f54a4956bca","index":0,"type":"function"}]\n\n',
885
+ // Chunk 2: First tool call arguments part 1
886
+ `id: ${streamId}\n`,
887
+ 'event: tool_calls\n',
888
+ 'data: [{"function":{"arguments":"{\\"location\\": \\"北京","name":null},"id":"call_c7d8b4984a4d4f54a4956bca","index":0,"type":"function"}]\n\n',
889
+ // Chunk 3: First tool call arguments part 2
890
+ `id: ${streamId}\n`,
891
+ 'event: tool_calls\n',
892
+ 'data: [{"function":{"arguments":"\\"}","name":null},"id":"call_c7d8b4984a4d4f54a4956bca","index":0,"type":"function"}]\n\n',
893
+ // Chunk 4: Empty arguments
894
+ `id: ${streamId}\n`,
895
+ 'event: tool_calls\n',
896
+ 'data: [{"function":{"arguments":"","name":null},"id":"call_c7d8b4984a4d4f54a4956bca","index":0,"type":"function"}]\n\n',
897
+ // Chunk 5: Second tool call starts (index=1) - should have its own id
898
+ `id: ${streamId}\n`,
899
+ 'event: tool_calls\n',
900
+ 'data: [{"function":{"arguments":"","name":"time____get_time____mcp"},"id":"call_f564785a14534d9a8c5ee641","index":1,"type":"function"}]\n\n',
901
+ // Chunk 6: Second tool call arguments - should use index=1's stored id
902
+ `id: ${streamId}\n`,
903
+ 'event: tool_calls\n',
904
+ 'data: [{"function":{"arguments":"{\\"location\\": \\"上海","name":null},"id":"call_f564785a14534d9a8c5ee641","index":1,"type":"function"}]\n\n',
905
+ // Chunk 7: Second tool call arguments part 2
906
+ `id: ${streamId}\n`,
907
+ 'event: tool_calls\n',
908
+ 'data: [{"function":{"arguments":"\\"}","name":null},"id":"call_f564785a14534d9a8c5ee641","index":1,"type":"function"}]\n\n',
909
+ // Chunk 8: Third tool call starts (index=2)
910
+ `id: ${streamId}\n`,
911
+ 'event: tool_calls\n',
912
+ 'data: [{"function":{"arguments":"{\\"location\\": \\"","name":"time____get_time____mcp"},"id":"call_19693813aebd434aab821f06","index":2,"type":"function"}]\n\n',
913
+ // Chunk 9: Third tool call arguments - should use index=2's stored id
914
+ `id: ${streamId}\n`,
915
+ 'event: tool_calls\n',
916
+ 'data: [{"function":{"arguments":"南京\\"","name":null},"id":"call_19693813aebd434aab821f06","index":2,"type":"function"}]\n\n',
917
+ // Chunk 10: Third tool call arguments final
918
+ `id: ${streamId}\n`,
919
+ 'event: tool_calls\n',
920
+ 'data: [{"function":{"arguments":"}","name":null},"id":"call_19693813aebd434aab821f06","index":2,"type":"function"}]\n\n',
921
+ // Chunk 11: Finish
922
+ `id: ${streamId}\n`,
923
+ 'event: stop\n',
924
+ 'data: "tool_calls"\n\n',
925
+ ]);
926
+
927
+ // Verify aggregated tool calls have correct arguments (not merged incorrectly)
928
+ expect(aggregatedToolCalls).toHaveLength(3);
929
+ expect(aggregatedToolCalls[0]).toMatchObject({
930
+ id: 'call_c7d8b4984a4d4f54a4956bca',
931
+ function: { name: 'time____get_time____mcp', arguments: '{"location": "北京"}' },
932
+ });
933
+ expect(aggregatedToolCalls[1]).toMatchObject({
934
+ id: 'call_f564785a14534d9a8c5ee641',
935
+ function: { name: 'time____get_time____mcp', arguments: '{"location": "上海"}' },
936
+ });
937
+ expect(aggregatedToolCalls[2]).toMatchObject({
938
+ id: 'call_19693813aebd434aab821f06',
939
+ function: { name: 'time____get_time____mcp', arguments: '{"location": "南京"}' },
940
+ });
941
+ });
942
+ });
943
+
624
944
  describe('transformQwenStream', () => {
625
945
  it('should handle usage chunk', () => {
626
946
  const mockChunk: OpenAI.ChatCompletionChunk = {
@@ -70,17 +70,26 @@ export const transformQwenStream = (
70
70
 
71
71
  if (item.delta?.tool_calls) {
72
72
  return {
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 = {
73
+ data: item.delta.tool_calls.map((value, arrayIndex): StreamToolCallChunkData => {
74
+ // Get the actual tool index from the chunk, fallback to array position
75
+ const toolIndex = typeof value.index !== 'undefined' ? value.index : arrayIndex;
76
+
77
+ // Store tool call info in streamContext.tools map by index for parallel tool calls
78
+ // This allows us to correctly track multiple tools being called in parallel
79
+ if (streamContext && value.id && value.function?.name) {
80
+ if (!streamContext.tools) {
81
+ streamContext.tools = {};
82
+ }
83
+ streamContext.tools[toolIndex] = {
78
84
  id: value.id,
79
- index: typeof value.index !== 'undefined' ? value.index : index,
85
+ index: toolIndex,
80
86
  name: value.function.name,
81
87
  };
82
88
  }
83
89
 
90
+ // Get stored tool info for this index (for incremental chunks without id)
91
+ const storedTool = streamContext?.tools?.[toolIndex];
92
+
84
93
  return {
85
94
  // Qwen models may send tool_calls in two separate chunks:
86
95
  // 1. First chunk: {id, name} without arguments
@@ -91,10 +100,10 @@ export const transformQwenStream = (
91
100
  arguments: value.function?.arguments ?? '',
92
101
  name: value.function?.name ?? null,
93
102
  },
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),
97
- index: typeof value.index !== 'undefined' ? value.index : index,
103
+ // For incremental chunks without id, use the stored tool id from streamContext.tools
104
+ // based on the tool index to correctly associate arguments with the right tool call
105
+ id: value.id || storedTool?.id || generateToolCallId(toolIndex, value.function?.name),
106
+ index: toolIndex,
98
107
  type: value.type || 'function',
99
108
  };
100
109
  }),
@@ -8,7 +8,7 @@
8
8
  "@lobechat/python-interpreter": "workspace:*",
9
9
  "@lobechat/web-crawler": "workspace:*",
10
10
  "@lobehub/chat-plugin-sdk": "^1.32.4",
11
- "@lobehub/market-sdk": "0.29.0",
11
+ "@lobehub/market-sdk": "0.29.1",
12
12
  "@lobehub/market-types": "^1.12.3",
13
13
  "model-bank": "workspace:*",
14
14
  "type-fest": "^4.41.0",
@@ -13,6 +13,7 @@ export interface LobeChatGroupMetaConfig {
13
13
 
14
14
  export interface LobeChatGroupChatConfig {
15
15
  allowDM?: boolean;
16
+ forkedFromIdentifier?: string;
16
17
  openingMessage?: string;
17
18
  openingQuestions?: string[];
18
19
  revealDM?: boolean;
@@ -25,6 +26,7 @@ export type LobeChatGroupConfig = LobeChatGroupChatConfig;
25
26
  // Zod schema for ChatGroupConfig (database insert)
26
27
  export const ChatGroupConfigSchema = z.object({
27
28
  allowDM: z.boolean().optional(),
29
+ forkedFromIdentifier: z.string().optional(),
28
30
  openingMessage: z.string().optional(),
29
31
  openingQuestions: z.array(z.string()).optional(),
30
32
  revealDM: z.boolean().optional(),
@@ -48,6 +48,15 @@ export interface DiscoverAssistantItem extends Omit<LobeAgentSettings, 'meta'>,
48
48
  author: string;
49
49
  category?: AssistantCategory;
50
50
  createdAt: string;
51
+ /**
52
+ * Fork count - number of times this agent has been forked
53
+ */
54
+ forkCount?: number;
55
+ /**
56
+ * Forked from agent ID - ID of the source agent if this is a fork
57
+ * null means this is an original agent
58
+ */
59
+ forkedFromAgentId?: number | null;
51
60
  homepage: string;
52
61
  identifier: string;
53
62
  installCount?: number;
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Fork-related types for agents and agent groups
3
+ */
4
+
5
+ /**
6
+ * Fork request parameters for Agent
7
+ */
8
+ export interface AgentForkRequest {
9
+ /** New agent identifier (required, must be globally unique) */
10
+ identifier: string;
11
+ /** New agent name (optional, defaults to "{original name} (Fork)") */
12
+ name?: string;
13
+ /** Status (optional, defaults to 'published') */
14
+ status?: 'published' | 'unpublished' | 'archived' | 'deprecated';
15
+ /** Version number to fork (optional, defaults to current version) */
16
+ versionNumber?: number;
17
+ /** Visibility (optional, defaults to 'public') */
18
+ visibility?: 'public' | 'private' | 'internal';
19
+ }
20
+
21
+ /**
22
+ * Fork response for Agent
23
+ */
24
+ export interface AgentForkResponse {
25
+ /** Newly created agent */
26
+ agent: {
27
+ createdAt: string;
28
+ forkedFromAgentId: number;
29
+ id: number;
30
+ identifier: string;
31
+ name: string;
32
+ ownerId: number;
33
+ updatedAt: string;
34
+ };
35
+ /** Source agent information */
36
+ source: {
37
+ agentId: number;
38
+ identifier: string;
39
+ versionNumber: number;
40
+ };
41
+ /** Newly created version */
42
+ version: {
43
+ agentId: number;
44
+ createdAt: string;
45
+ id: number;
46
+ versionNumber: number;
47
+ };
48
+ }
49
+
50
+ /**
51
+ * Fork item for list display
52
+ */
53
+ export interface AgentForkItem {
54
+ createdAt: string;
55
+ forkCount: number;
56
+ id: number;
57
+ identifier: string;
58
+ name: string;
59
+ ownerId: number;
60
+ ownerName?: string;
61
+ status?: string;
62
+ visibility?: string;
63
+ }
64
+
65
+ /**
66
+ * Forks list response
67
+ */
68
+ export interface AgentForksResponse {
69
+ forks: AgentForkItem[];
70
+ totalCount: number;
71
+ }
72
+
73
+ /**
74
+ * Fork source response
75
+ */
76
+ export interface AgentForkSourceResponse {
77
+ source: AgentForkItem | null;
78
+ }
79
+
80
+ /**
81
+ * Fork request parameters for Agent Group
82
+ */
83
+ export interface AgentGroupForkRequest {
84
+ /** New group identifier (required, must be globally unique) */
85
+ identifier: string;
86
+ /** New group name (optional, defaults to "{original name} (Fork)") */
87
+ name?: string;
88
+ /** Status (optional, defaults to 'published') */
89
+ status?: 'published' | 'unpublished' | 'archived' | 'deprecated';
90
+ /** Version number to fork (optional, defaults to current version) */
91
+ versionNumber?: number;
92
+ /** Visibility (optional, defaults to 'public') */
93
+ visibility?: 'public' | 'private' | 'internal';
94
+ }
95
+
96
+ /**
97
+ * Fork response for Agent Group
98
+ */
99
+ export interface AgentGroupForkResponse {
100
+ /** Newly created agent group */
101
+ group: {
102
+ createdAt: string;
103
+ forkedFromGroupId: number;
104
+ id: number;
105
+ identifier: string;
106
+ name: string;
107
+ ownerId: number;
108
+ updatedAt: string;
109
+ };
110
+ /** Newly created group version */
111
+ groupVersion: {
112
+ agentGroupId: number;
113
+ createdAt: string;
114
+ id: number;
115
+ versionNumber: number;
116
+ };
117
+ /** Copied member agents */
118
+ memberAgents: Array<{
119
+ id: number;
120
+ identifier: string;
121
+ name: string;
122
+ role: string;
123
+ }>;
124
+ /** Copied member versions */
125
+ memberVersions: Array<{
126
+ agentForGroupId: number;
127
+ id: number;
128
+ versionNumber: number;
129
+ }>;
130
+ /** Source group information */
131
+ source: {
132
+ groupId: number;
133
+ identifier: string;
134
+ versionNumber: number;
135
+ };
136
+ }
137
+
138
+ /**
139
+ * Fork item for Agent Group list display
140
+ */
141
+ export interface AgentGroupForkItem {
142
+ createdAt: string;
143
+ forkCount: number;
144
+ id: number;
145
+ identifier: string;
146
+ name: string;
147
+ ownerId: number;
148
+ }
149
+
150
+ /**
151
+ * Agent Group forks list response
152
+ */
153
+ export interface AgentGroupForksResponse {
154
+ forks: AgentGroupForkItem[];
155
+ totalCount: number;
156
+ }
157
+
158
+ /**
159
+ * Agent Group fork source response
160
+ */
161
+ export interface AgentGroupForkSourceResponse {
162
+ source: AgentGroupForkItem | null;
163
+ }
@@ -42,6 +42,10 @@ export type GroupAgentCategory =
42
42
  * Group Agent Config - similar to LobeAgentConfig but for groups
43
43
  */
44
44
  export interface GroupAgentConfig {
45
+ /**
46
+ * Additional configuration
47
+ */
48
+ [key: string]: any;
45
49
  /**
46
50
  * Opening message when starting a conversation with the group
47
51
  */
@@ -54,10 +58,6 @@ export interface GroupAgentConfig {
54
58
  * System role/prompt for the group
55
59
  */
56
60
  systemRole?: string;
57
- /**
58
- * Additional configuration
59
- */
60
- [key: string]: any;
61
61
  }
62
62
 
63
63
  /**
@@ -71,6 +71,15 @@ export interface DiscoverGroupAgentItem extends MetaData {
71
71
  config?: GroupAgentConfig;
72
72
  createdAt: string;
73
73
  description?: string;
74
+ /**
75
+ * Fork count - number of times this agent group has been forked
76
+ */
77
+ forkCount?: number;
78
+ /**
79
+ * Forked from group ID - ID of the source agent group if this is a fork
80
+ * null means this is an original agent group
81
+ */
82
+ forkedFromGroupId?: number | null;
74
83
  homepage?: string;
75
84
  identifier: string;
76
85
  installCount?: number;
@@ -2,6 +2,7 @@ import { DiscoverAssistantItem } from './assistants';
2
2
  import { DiscoverGroupAgentItem } from './groupAgents';
3
3
 
4
4
  export * from './assistants';
5
+ export * from './fork';
5
6
  export * from './groupAgents';
6
7
  export * from './mcp';
7
8
  export * from './models';
@@ -66,5 +67,13 @@ export interface DiscoverUserInfo {
66
67
  export interface DiscoverUserProfile {
67
68
  agentGroups?: DiscoverGroupAgentItem[];
68
69
  agents: DiscoverAssistantItem[];
70
+ /**
71
+ * Agent groups forked by the user
72
+ */
73
+ forkedAgentGroups?: DiscoverGroupAgentItem[];
74
+ /**
75
+ * Agents forked by the user
76
+ */
77
+ forkedAgents?: DiscoverAssistantItem[];
69
78
  user: DiscoverUserInfo;
70
79
  }
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
 
3
+ import { COPYRIGHT_FULL } from '@lobechat/business-const';
3
4
  import { Center, Flexbox, Text } from '@lobehub/ui';
4
5
  import { Divider } from 'antd';
5
6
  import { cx } from 'antd-style';
@@ -41,7 +42,7 @@ const AuthContainer: FC<PropsWithChildren> = ({ children }) => {
41
42
  </Center>
42
43
  <Center padding={24}>
43
44
  <Text align={'center'} type={'secondary'}>
44
- © 2025 LobeHub. All rights reserved.
45
+ {COPYRIGHT_FULL}
45
46
  </Text>
46
47
  </Center>
47
48
  </Flexbox>