@lobehub/chat 1.136.11 → 1.136.13

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 (90) hide show
  1. package/.github/workflows/claude-translator.yml +13 -1
  2. package/CHANGELOG.md +60 -0
  3. package/changelog/v1.json +21 -0
  4. package/locales/ar/modelProvider.json +12 -0
  5. package/locales/ar/models.json +39 -24
  6. package/locales/bg-BG/modelProvider.json +12 -0
  7. package/locales/bg-BG/models.json +39 -24
  8. package/locales/de-DE/modelProvider.json +12 -0
  9. package/locales/de-DE/models.json +39 -24
  10. package/locales/en-US/modelProvider.json +12 -0
  11. package/locales/en-US/models.json +39 -24
  12. package/locales/es-ES/modelProvider.json +12 -0
  13. package/locales/es-ES/models.json +39 -24
  14. package/locales/fa-IR/modelProvider.json +12 -0
  15. package/locales/fa-IR/models.json +39 -24
  16. package/locales/fr-FR/modelProvider.json +12 -0
  17. package/locales/fr-FR/models.json +39 -24
  18. package/locales/it-IT/modelProvider.json +12 -0
  19. package/locales/it-IT/models.json +39 -24
  20. package/locales/ja-JP/modelProvider.json +12 -0
  21. package/locales/ja-JP/models.json +39 -24
  22. package/locales/ko-KR/modelProvider.json +12 -0
  23. package/locales/ko-KR/models.json +39 -24
  24. package/locales/nl-NL/modelProvider.json +12 -0
  25. package/locales/nl-NL/models.json +39 -24
  26. package/locales/pl-PL/modelProvider.json +12 -0
  27. package/locales/pl-PL/models.json +39 -24
  28. package/locales/pt-BR/modelProvider.json +12 -0
  29. package/locales/pt-BR/models.json +39 -24
  30. package/locales/ru-RU/modelProvider.json +12 -0
  31. package/locales/ru-RU/models.json +39 -24
  32. package/locales/tr-TR/modelProvider.json +12 -0
  33. package/locales/tr-TR/models.json +39 -24
  34. package/locales/vi-VN/modelProvider.json +12 -0
  35. package/locales/vi-VN/models.json +39 -24
  36. package/locales/zh-CN/modelProvider.json +12 -0
  37. package/locales/zh-CN/models.json +39 -24
  38. package/locales/zh-TW/modelProvider.json +12 -0
  39. package/locales/zh-TW/models.json +39 -24
  40. package/package.json +3 -3
  41. package/packages/const/src/settings/index.ts +1 -0
  42. package/packages/database/package.json +7 -5
  43. package/packages/electron-client-ipc/src/events/index.ts +2 -2
  44. package/packages/electron-client-ipc/src/events/{localFile.ts → localSystem.ts} +25 -6
  45. package/packages/electron-client-ipc/src/types/index.ts +1 -1
  46. package/packages/electron-client-ipc/src/types/{localFile.ts → localSystem.ts} +89 -4
  47. package/packages/file-loaders/package.json +1 -2
  48. package/packages/file-loaders/src/loadFile.ts +4 -1
  49. package/packages/file-loaders/src/loaders/doc/__snapshots__/index.test.ts.snap +46 -0
  50. package/packages/file-loaders/src/loaders/doc/index.test.ts +38 -0
  51. package/packages/file-loaders/src/loaders/doc/index.ts +57 -0
  52. package/packages/file-loaders/src/loaders/docx/index.ts +36 -45
  53. package/packages/file-loaders/src/loaders/index.ts +2 -0
  54. package/packages/file-loaders/src/types/word-extractor.d.ts +9 -0
  55. package/packages/file-loaders/src/types.ts +1 -1
  56. package/packages/model-bank/src/aiModels/infiniai.ts +465 -174
  57. package/packages/model-bank/src/aiModels/modelscope.ts +10 -20
  58. package/packages/model-bank/src/aiModels/novita.ts +2 -2
  59. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.test.ts +267 -38
  60. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.ts +45 -0
  61. package/packages/model-runtime/src/providerTestUtils.ts +0 -5
  62. package/packages/model-runtime/src/providers/anthropic/generateObject.test.ts +57 -44
  63. package/packages/model-runtime/src/providers/anthropic/generateObject.ts +28 -20
  64. package/packages/model-runtime/src/providers/deepseek/index.ts +5 -0
  65. package/packages/model-runtime/src/providers/infiniai/index.ts +8 -54
  66. package/packages/model-runtime/src/providers/openai/index.test.ts +0 -5
  67. package/packages/model-runtime/src/providers/openrouter/index.test.ts +3 -3
  68. package/packages/model-runtime/src/providers/openrouter/index.ts +32 -20
  69. package/packages/model-runtime/src/providers/openrouter/type.ts +25 -24
  70. package/packages/model-runtime/src/providers/zhipu/index.test.ts +0 -1
  71. package/packages/model-runtime/src/types/structureOutput.ts +13 -1
  72. package/packages/model-runtime/src/utils/handleOpenAIError.test.ts +0 -5
  73. package/packages/model-runtime/src/utils/handleOpenAIError.ts +2 -2
  74. package/packages/types/src/aiChat.ts +13 -1
  75. package/packages/types/src/index.ts +1 -0
  76. package/src/app/[variants]/(main)/settings/provider/detail/bedrock/index.tsx +36 -2
  77. package/src/config/modelProviders/infiniai.ts +2 -25
  78. package/src/config/modelProviders/modelscope.ts +1 -17
  79. package/src/features/ChatInput/InputEditor/index.tsx +39 -26
  80. package/src/features/Conversation/Messages/Assistant/Tool/Render/LoadingPlaceholder/index.tsx +1 -1
  81. package/src/server/routers/lambda/agent.ts +2 -3
  82. package/src/server/routers/lambda/aiChat.ts +33 -1
  83. package/src/server/routers/lambda/chunk.ts +2 -2
  84. package/src/services/electron/file.ts +1 -2
  85. package/src/services/electron/localFileService.ts +40 -0
  86. package/src/tools/local-system/Placeholder/ListFiles.tsx +23 -0
  87. package/src/tools/local-system/Placeholder/ReadLocalFile.tsx +9 -0
  88. package/src/tools/local-system/Placeholder/SearchFiles.tsx +55 -0
  89. package/src/tools/local-system/Placeholder/index.tsx +25 -0
  90. package/src/tools/placeholders.ts +3 -0
@@ -48,7 +48,6 @@ describe('handleOpenAIError', () => {
48
48
 
49
49
  expect(result.errorResult).toEqual({
50
50
  headers: { headers, status: 401 },
51
- stack: apiError.stack,
52
51
  status: 472,
53
52
  });
54
53
  expect(result.RuntimeError).toBeUndefined();
@@ -84,7 +83,6 @@ describe('handleOpenAIError', () => {
84
83
  cause: { details: 'Error details' },
85
84
  message: 'Generic error',
86
85
  name: 'Error',
87
- stack: error.stack,
88
86
  },
89
87
  });
90
88
  });
@@ -100,7 +98,6 @@ describe('handleOpenAIError', () => {
100
98
  cause: undefined,
101
99
  message: 'Simple error',
102
100
  name: 'Error',
103
- stack: error.stack,
104
101
  },
105
102
  });
106
103
  });
@@ -122,7 +119,6 @@ describe('handleOpenAIError', () => {
122
119
  cause: undefined,
123
120
  message: 'Custom error message',
124
121
  name: 'CustomError',
125
- stack: error.stack,
126
122
  },
127
123
  });
128
124
  });
@@ -141,7 +137,6 @@ describe('handleOpenAIError', () => {
141
137
  cause: undefined,
142
138
  message: 'Object error',
143
139
  name: undefined,
144
- stack: undefined,
145
140
  },
146
141
  });
147
142
  });
@@ -20,7 +20,7 @@ export const handleOpenAIError = (
20
20
  }
21
21
  // if there is no other request error, the error object is a Response like object
22
22
  else {
23
- errorResult = { headers: error.headers, stack: error.stack, status: error.status };
23
+ errorResult = { headers: error.headers, status: error.status };
24
24
  }
25
25
 
26
26
  return {
@@ -29,7 +29,7 @@ export const handleOpenAIError = (
29
29
  } else {
30
30
  const err = error as Error;
31
31
 
32
- errorResult = { cause: err.cause, message: err.message, name: err.name, stack: err.stack };
32
+ errorResult = { cause: err.cause, message: err.message, name: err.name };
33
33
 
34
34
  return {
35
35
  RuntimeError: AgentRuntimeErrorType.AgentRuntimeError,
@@ -54,12 +54,24 @@ export interface SendMessageServerResponse {
54
54
  userMessageId: string;
55
55
  }
56
56
 
57
+ export const StructureSchema = z.object({
58
+ description: z.string().optional(),
59
+ name: z.string(),
60
+ schema: z.object({
61
+ additionalProperties: z.boolean().optional(),
62
+ properties: z.record(z.string(), z.any()),
63
+ required: z.array(z.string()).optional(),
64
+ type: z.literal('object'),
65
+ }),
66
+ strict: z.boolean().optional(),
67
+ });
68
+
57
69
  export const StructureOutputSchema = z.object({
58
70
  keyVaultsPayload: z.string(),
59
71
  messages: z.array(z.any()),
60
72
  model: z.string(),
61
73
  provider: z.string(),
62
- schema: z.any(),
74
+ schema: StructureSchema,
63
75
  });
64
76
 
65
77
  export interface StructureOutputParams {
@@ -10,6 +10,7 @@ export * from './clientDB';
10
10
  export * from './discover';
11
11
  export * from './eval';
12
12
  export * from './fetch';
13
+ export * from './files';
13
14
  export * from './hotkey';
14
15
  export * from './knowledgeBase';
15
16
  export * from './llm';
@@ -15,6 +15,40 @@ import ProviderDetail from '../default';
15
15
 
16
16
  const providerKey: GlobalLLMProviderKey = 'bedrock';
17
17
 
18
+ const AWS_REGIONS: string[] = [
19
+ 'us-east-1',
20
+ 'us-east-2',
21
+ 'us-west-1',
22
+ 'us-west-2',
23
+ 'ca-central-1',
24
+ 'us-gov-east-1',
25
+ 'us-gov-west-1',
26
+ 'sa-east-1',
27
+ 'eu-north-1',
28
+ 'eu-west-1',
29
+ 'eu-west-2',
30
+ 'eu-west-3',
31
+ 'eu-central-1',
32
+ 'eu-central-2',
33
+ 'eu-south-1',
34
+ 'eu-south-2',
35
+ 'me-south-1',
36
+ 'me-central-1',
37
+ 'af-south-1',
38
+ 'ap-south-1',
39
+ 'ap-south-2',
40
+ 'ap-east-1',
41
+ 'ap-southeast-1',
42
+ 'ap-southeast-2',
43
+ 'ap-southeast-3',
44
+ 'ap-southeast-4',
45
+ 'ap-northeast-1',
46
+ 'ap-northeast-2',
47
+ 'ap-northeast-3',
48
+ 'cn-north-1',
49
+ 'cn-northwest-1',
50
+ ];
51
+
18
52
  const useBedrockCard = (): ProviderItem => {
19
53
  const { t } = useTranslation('modelProvider');
20
54
 
@@ -68,11 +102,11 @@ const useBedrockCard = (): ProviderItem => {
68
102
  ) : (
69
103
  <Select
70
104
  allowClear
71
- options={['us-east-1', 'us-west-2', 'ap-southeast-1', 'eu-central-1'].map((i) => ({
105
+ options={AWS_REGIONS.map((i) => ({
72
106
  label: i,
73
107
  value: i,
74
108
  }))}
75
- placeholder={'us-east-1'}
109
+ placeholder={AWS_REGIONS[0]}
76
110
  />
77
111
  ),
78
112
  desc: t(`${providerKey}.region.desc`),
@@ -76,32 +76,8 @@ const InfiniAI: ModelProviderCard = {
76
76
  enabled: true,
77
77
  id: 'qwen2.5-7b-instruct',
78
78
  },
79
- {
80
- contextWindowTokens: 32_768,
81
- description:
82
- 'Qwen2 是 Qwen 团队推出的新一代大型语言模型系列。它基于 Transformer 架构,并采用 SwiGLU 激活函数、注意力 QKV 偏置(attention QKV bias)、群组查询注意力(group query attention)、滑动窗口注意力(mixture of sliding window attention)与全注意力的混合等技术。此外,Qwen 团队还改进了适应多种自然语言和代码的分词器。',
83
- displayName: 'Qwen 2 72B Instruct',
84
- enabled: true,
85
- id: 'qwen2-72b-instruct',
86
- },
87
- {
88
- contextWindowTokens: 32_768,
89
- description:
90
- 'Qwen2 是 Qwen 团队推出的新一代大型语言模型系列。它基于 Transformer 架构,并采用 SwiGLU 激活函数、注意力 QKV 偏置(attention QKV bias)、群组查询注意力(group query attention)、滑动窗口注意力(mixture of sliding window attention)与全注意力的混合等技术。此外,Qwen 团队还改进了适应多种自然语言和代码的分词器。',
91
- displayName: 'Qwen 2 7B Instruct',
92
- enabled: true,
93
- id: 'qwen2-7b-instruct',
94
- },
95
- {
96
- contextWindowTokens: 4096,
97
- description:
98
- 'Yi-1.5 是 Yi 的升级版本。 它使用 500B Tokens 的高质量语料库在 Yi 上持续进行预训练,并在 3M 个多样化的微调样本上进行微调。',
99
- displayName: 'Yi-1.5 34B Chat',
100
- enabled: true,
101
- id: 'yi-1.5-34b-chat',
102
- },
103
79
  ],
104
- checkModel: 'qwen2.5-7b-instruct',
80
+ checkModel: 'qwen3-8b',
105
81
  description:
106
82
  '为应用开发者提供高性能、易上手、安全可靠的大模型服务,覆盖从大模型开发到大模型服务化部署的全流程。',
107
83
  id: 'infiniai',
@@ -109,6 +85,7 @@ const InfiniAI: ModelProviderCard = {
109
85
  modelsUrl: 'https://cloud.infini-ai.com/genstudio/model',
110
86
  name: 'InfiniAI',
111
87
  settings: {
88
+ disableBrowserRequest: true,
112
89
  proxyUrl: {
113
90
  placeholder: 'https://cloud.infini-ai.com/maas/v1',
114
91
  },
@@ -12,22 +12,6 @@ const ModelScope: ModelProviderCard = {
12
12
  functionCall: true,
13
13
  id: 'deepseek-ai/DeepSeek-R1-0528',
14
14
  },
15
- {
16
- contextWindowTokens: 131_072,
17
- description: 'DeepSeek-V3是DeepSeek第三代模型的最新版本,具有强大的推理和对话能力。',
18
- displayName: 'DeepSeek-V3',
19
- enabled: true,
20
- functionCall: true,
21
- id: 'deepseek-ai/DeepSeek-V3',
22
- },
23
- {
24
- contextWindowTokens: 131_072,
25
- description: 'DeepSeek-R1是DeepSeek最新的推理模型,专注于复杂推理任务。',
26
- displayName: 'DeepSeek-R1',
27
- enabled: true,
28
- functionCall: true,
29
- id: 'deepseek-ai/DeepSeek-R1',
30
- },
31
15
  {
32
16
  contextWindowTokens: 131_072,
33
17
  description: 'Qwen3-235B-A22B是通义千问3代超大规模模型,提供顶级的AI能力。',
@@ -45,7 +29,7 @@ const ModelScope: ModelProviderCard = {
45
29
  id: 'Qwen/Qwen3-32B',
46
30
  },
47
31
  ],
48
- checkModel: 'Qwen/Qwen3-32B',
32
+ checkModel: 'Qwen/Qwen3-4B',
49
33
  description: 'ModelScope是阿里云推出的模型即服务平台,提供丰富的AI模型和推理服务。',
50
34
  id: 'modelscope',
51
35
  modelList: { showModelFetcher: true },
@@ -62,30 +62,45 @@ const InputEditor = memo<{ defaultRows?: number }>(() => {
62
62
  };
63
63
  }, [state.isEmpty]);
64
64
 
65
- const enableMarkdown = useUserStore(preferenceSelectors.inputMarkdownRender);
66
- const plugins = useMemo(
65
+ const enableRichRender = useUserStore(preferenceSelectors.inputMarkdownRender);
66
+
67
+ const richRenderProps = useMemo(
67
68
  () =>
68
- !enableMarkdown
69
- ? undefined
70
- : [
71
- ReactListPlugin,
72
- ReactLinkPlugin,
73
- ReactCodePlugin,
74
- ReactCodeblockPlugin,
75
- ReactHRPlugin,
76
- ReactTablePlugin,
77
- Editor.withProps(ReactMathPlugin, {
78
- renderComp: expand
79
- ? undefined
80
- : (props) => (
81
- <FloatMenu
82
- {...props}
83
- getPopupContainer={() => (slashMenuRef as any)?.current}
84
- />
85
- ),
86
- }),
87
- ],
88
- [enableMarkdown],
69
+ !enableRichRender
70
+ ? {
71
+ enablePasteMarkdown: false,
72
+ markdownOption: {
73
+ bold: false,
74
+ code: false,
75
+ header: false,
76
+ italic: false,
77
+ quote: false,
78
+ strikethrough: false,
79
+ underline: false,
80
+ underlineStrikethrough: false,
81
+ },
82
+ }
83
+ : {
84
+ plugins: [
85
+ ReactListPlugin,
86
+ ReactLinkPlugin,
87
+ ReactCodePlugin,
88
+ ReactCodeblockPlugin,
89
+ ReactHRPlugin,
90
+ ReactTablePlugin,
91
+ Editor.withProps(ReactMathPlugin, {
92
+ renderComp: expand
93
+ ? undefined
94
+ : (props) => (
95
+ <FloatMenu
96
+ {...props}
97
+ getPopupContainer={() => (slashMenuRef as any)?.current}
98
+ />
99
+ ),
100
+ }),
101
+ ],
102
+ },
103
+ [enableRichRender],
89
104
  );
90
105
 
91
106
  return (
@@ -94,8 +109,7 @@ const InputEditor = memo<{ defaultRows?: number }>(() => {
94
109
  className={className}
95
110
  content={''}
96
111
  editor={editor}
97
- enablePasteMarkdown={enableMarkdown}
98
- markdownOption={enableMarkdown}
112
+ {...richRenderProps}
99
113
  onBlur={() => {
100
114
  disableScope(HotkeyEnum.AddUserMessage);
101
115
  }}
@@ -145,7 +159,6 @@ const InputEditor = memo<{ defaultRows?: number }>(() => {
145
159
  }
146
160
  }}
147
161
  placeholder={<Placeholder />}
148
- plugins={plugins}
149
162
  slashOption={{
150
163
  items: slashItems,
151
164
  renderComp: expand
@@ -16,7 +16,7 @@ const LoadingPlaceholder = memo<LoadingPlaceholderProps>(
16
16
  ({ identifier, requestArgs, apiName, loading }) => {
17
17
  const Render = BuiltinToolPlaceholders[identifier || ''];
18
18
 
19
- if (identifier) {
19
+ if (identifier && Render) {
20
20
  return (
21
21
  <Render apiName={apiName} args={safeParseJSON(requestArgs) || {}} identifier={identifier} />
22
22
  );
@@ -1,7 +1,7 @@
1
+ import { DEFAULT_AGENT_CONFIG, INBOX_SESSION_ID } from '@lobechat/const';
2
+ import { KnowledgeItem, KnowledgeType } from '@lobechat/types';
1
3
  import { z } from 'zod';
2
4
 
3
- import { INBOX_SESSION_ID } from '@/const/session';
4
- import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
5
5
  import { AgentModel } from '@/database/models/agent';
6
6
  import { FileModel } from '@/database/models/file';
7
7
  import { KnowledgeBaseModel } from '@/database/models/knowledgeBase';
@@ -11,7 +11,6 @@ import { pino } from '@/libs/logger';
11
11
  import { authedProcedure, router } from '@/libs/trpc/lambda';
12
12
  import { serverDatabase } from '@/libs/trpc/lambda/middleware';
13
13
  import { AgentService } from '@/server/services/agent';
14
- import { KnowledgeItem, KnowledgeType } from '@/types/knowledgeBase';
15
14
 
16
15
  const agentProcedure = authedProcedure.use(serverDatabase).use(async (opts) => {
17
16
  const { ctx } = opts;
@@ -4,6 +4,7 @@ import {
4
4
  StructureOutputSchema,
5
5
  } from '@lobechat/types';
6
6
  import { TRPCError } from '@trpc/server';
7
+ import debug from 'debug';
7
8
 
8
9
  import { LOADING_FLAT } from '@/const/message';
9
10
  import { MessageModel } from '@/database/models/message';
@@ -15,6 +16,8 @@ import { AiChatService } from '@/server/services/aiChat';
15
16
  import { FileService } from '@/server/services/file';
16
17
  import { getXorPayload } from '@/utils/server';
17
18
 
19
+ const log = debug('lobe-lambda-router:ai-chat');
20
+
18
21
  const aiChatProcedure = authedProcedure.use(serverDatabase).use(async (opts) => {
19
22
  const { ctx } = opts;
20
23
 
@@ -30,30 +33,45 @@ const aiChatProcedure = authedProcedure.use(serverDatabase).use(async (opts) =>
30
33
 
31
34
  export const aiChatRouter = router({
32
35
  outputJSON: aiChatProcedure.input(StructureOutputSchema).mutation(async ({ input }) => {
36
+ log('outputJSON called with provider: %s, model: %s', input.provider, input.model);
37
+ log('messages count: %d', input.messages.length);
38
+ log('schema: %O', input.schema);
39
+
33
40
  let payload: object | undefined;
34
41
 
35
42
  try {
36
43
  payload = getXorPayload(input.keyVaultsPayload);
44
+ log('payload parsed successfully');
37
45
  } catch (e) {
46
+ log('payload parse error: %O', e);
38
47
  console.warn('user payload parse error', e);
39
48
  }
40
49
 
41
50
  if (!payload) {
51
+ log('payload is empty, throwing error');
42
52
  throw new TRPCError({ code: 'BAD_REQUEST', message: 'keyVaultsPayload is not correct' });
43
53
  }
44
54
 
55
+ log('initializing model runtime with provider: %s', input.provider);
45
56
  const modelRuntime = initModelRuntimeWithUserPayload(input.provider, payload);
46
57
 
47
- return modelRuntime.generateObject({
58
+ log('calling generateObject');
59
+ const result = await modelRuntime.generateObject({
48
60
  messages: input.messages,
49
61
  model: input.model,
50
62
  schema: input.schema,
51
63
  });
64
+
65
+ log('generateObject completed, result: %O', result);
66
+ return result;
52
67
  }),
53
68
 
54
69
  sendMessageInServer: aiChatProcedure
55
70
  .input(AiSendMessageServerSchema)
56
71
  .mutation(async ({ input, ctx }) => {
72
+ log('sendMessageInServer called for sessionId: %s', input.sessionId);
73
+ log('topicId: %s, newTopic: %O', input.topicId, input.newTopic);
74
+
57
75
  let messageId: string;
58
76
  let topicId = input.topicId!;
59
77
 
@@ -61,6 +79,7 @@ export const aiChatRouter = router({
61
79
 
62
80
  // create topic if there should be a new topic
63
81
  if (input.newTopic) {
82
+ log('creating new topic with title: %s', input.newTopic.title);
64
83
  const topicItem = await ctx.topicModel.create({
65
84
  messages: input.newTopic.topicMessageIds,
66
85
  sessionId: input.sessionId,
@@ -68,9 +87,11 @@ export const aiChatRouter = router({
68
87
  });
69
88
  topicId = topicItem.id;
70
89
  isCreatNewTopic = true;
90
+ log('new topic created with id: %s', topicId);
71
91
  }
72
92
 
73
93
  // create user message
94
+ log('creating user message with content length: %d', input.newUserMessage.content.length);
74
95
  const userMessageItem = await ctx.messageModel.create({
75
96
  content: input.newUserMessage.content,
76
97
  files: input.newUserMessage.files,
@@ -80,7 +101,14 @@ export const aiChatRouter = router({
80
101
  });
81
102
 
82
103
  messageId = userMessageItem.id;
104
+ log('user message created with id: %s', messageId);
105
+
83
106
  // create assistant message
107
+ log(
108
+ 'creating assistant message with model: %s, provider: %s',
109
+ input.newAssistantMessage.model,
110
+ input.newAssistantMessage.provider,
111
+ );
84
112
  const assistantMessageItem = await ctx.messageModel.create({
85
113
  content: LOADING_FLAT,
86
114
  fromModel: input.newAssistantMessage.model,
@@ -90,14 +118,18 @@ export const aiChatRouter = router({
90
118
  sessionId: input.sessionId!,
91
119
  topicId,
92
120
  });
121
+ log('assistant message created with id: %s', assistantMessageItem.id);
93
122
 
94
123
  // retrieve latest messages and topic with
124
+ log('retrieving messages and topics');
95
125
  const { messages, topics } = await ctx.aiChatService.getMessagesAndTopics({
96
126
  includeTopic: isCreatNewTopic,
97
127
  sessionId: input.sessionId,
98
128
  topicId,
99
129
  });
100
130
 
131
+ log('retrieved %d messages, %d topics', messages.length, topics?.length ?? 0);
132
+
101
133
  return {
102
134
  assistantMessageId: assistantMessageItem.id,
103
135
  isCreatNewTopic,
@@ -1,8 +1,9 @@
1
+ import { DEFAULT_FILE_EMBEDDING_MODEL_ITEM } from '@lobechat/const';
2
+ import { SemanticSearchSchema } from '@lobechat/types';
1
3
  import { TRPCError } from '@trpc/server';
2
4
  import { inArray } from 'drizzle-orm';
3
5
  import { z } from 'zod';
4
6
 
5
- import { DEFAULT_FILE_EMBEDDING_MODEL_ITEM } from '@/const/settings/knowledge';
6
7
  import { AsyncTaskModel } from '@/database/models/asyncTask';
7
8
  import { ChunkModel } from '@/database/models/chunk';
8
9
  import { EmbeddingModel } from '@/database/models/embedding';
@@ -14,7 +15,6 @@ import { keyVaults, serverDatabase } from '@/libs/trpc/lambda/middleware';
14
15
  import { getServerDefaultFilesConfig } from '@/server/globalConfig';
15
16
  import { initModelRuntimeWithUserPayload } from '@/server/modules/ModelRuntime';
16
17
  import { ChunkService } from '@/server/services/chunk';
17
- import { SemanticSearchSchema } from '@/types/rag';
18
18
 
19
19
  const chunkProcedure = authedProcedure
20
20
  .use(serverDatabase)
@@ -1,6 +1,5 @@
1
1
  import { dispatch } from '@lobechat/electron-client-ipc';
2
-
3
- import { FileMetadata } from '@/types/files';
2
+ import { FileMetadata } from '@lobechat/types';
4
3
 
5
4
  /**
6
5
  * 桌面应用文件API客户端服务
@@ -1,4 +1,14 @@
1
1
  import {
2
+ EditLocalFileParams,
3
+ EditLocalFileResult,
4
+ GetCommandOutputParams,
5
+ GetCommandOutputResult,
6
+ GlobFilesParams,
7
+ GlobFilesResult,
8
+ GrepContentParams,
9
+ GrepContentResult,
10
+ KillCommandParams,
11
+ KillCommandResult,
2
12
  ListLocalFileParams,
3
13
  LocalFileItem,
4
14
  LocalMoveFilesResultItem,
@@ -10,11 +20,14 @@ import {
10
20
  OpenLocalFileParams,
11
21
  OpenLocalFolderParams,
12
22
  RenameLocalFileParams,
23
+ RunCommandParams,
24
+ RunCommandResult,
13
25
  WriteLocalFileParams,
14
26
  dispatch,
15
27
  } from '@lobechat/electron-client-ipc';
16
28
 
17
29
  class LocalFileService {
30
+ // File Operations
18
31
  async listLocalFiles(params: ListLocalFileParams): Promise<LocalFileItem[]> {
19
32
  return dispatch('listLocalFiles', params);
20
33
  }
@@ -51,6 +64,33 @@ class LocalFileService {
51
64
  return dispatch('writeLocalFile', params);
52
65
  }
53
66
 
67
+ async editLocalFile(params: EditLocalFileParams): Promise<EditLocalFileResult> {
68
+ return dispatch('editLocalFile', params);
69
+ }
70
+
71
+ // Shell Commands
72
+ async runCommand(params: RunCommandParams): Promise<RunCommandResult> {
73
+ return dispatch('runCommand', params);
74
+ }
75
+
76
+ async getCommandOutput(params: GetCommandOutputParams): Promise<GetCommandOutputResult> {
77
+ return dispatch('getCommandOutput', params);
78
+ }
79
+
80
+ async killCommand(params: KillCommandParams): Promise<KillCommandResult> {
81
+ return dispatch('killCommand', params);
82
+ }
83
+
84
+ // Search & Find
85
+ async grepContent(params: GrepContentParams): Promise<GrepContentResult> {
86
+ return dispatch('grepContent', params);
87
+ }
88
+
89
+ async globFiles(params: GlobFilesParams): Promise<GlobFilesResult> {
90
+ return dispatch('globLocalFiles', params);
91
+ }
92
+
93
+ // Helper methods
54
94
  async openLocalFileOrFolder(path: string, isDirectory: boolean) {
55
95
  if (isDirectory) {
56
96
  return this.openLocalFolder({ isDirectory, path });
@@ -0,0 +1,23 @@
1
+ import { ListLocalFileParams } from '@lobechat/electron-client-ipc';
2
+ import { Skeleton } from 'antd';
3
+ import React, { memo } from 'react';
4
+ import { Flexbox } from 'react-layout-kit';
5
+
6
+ import { LocalFolder } from '@/features/LocalFile';
7
+
8
+ interface ListFilesProps {
9
+ args: ListLocalFileParams;
10
+ }
11
+ export const ListFiles = memo<ListFilesProps>(({ args }) => {
12
+ return (
13
+ <Flexbox gap={8}>
14
+ <LocalFolder path={args.path} />
15
+ <Flexbox gap={4}>
16
+ <Skeleton.Button active block style={{ height: 16 }} />
17
+ <Skeleton.Button active block style={{ height: 16 }} />
18
+ <Skeleton.Button active block style={{ height: 16 }} />
19
+ <Skeleton.Button active block style={{ height: 16 }} />
20
+ </Flexbox>
21
+ </Flexbox>
22
+ );
23
+ });
@@ -0,0 +1,9 @@
1
+ 'use client';
2
+
3
+ import { memo } from 'react';
4
+
5
+ import Skeleton from '../Render/ReadLocalFile/ReadFileSkeleton';
6
+
7
+ const ReadLocalFile = memo(() => <Skeleton />);
8
+
9
+ export default ReadLocalFile;
@@ -0,0 +1,55 @@
1
+ import { LocalSearchFilesParams } from '@lobechat/electron-client-ipc';
2
+ import { Icon } from '@lobehub/ui';
3
+ import { Skeleton } from 'antd';
4
+ import { createStyles } from 'antd-style';
5
+ import { SearchIcon } from 'lucide-react';
6
+ import { memo } from 'react';
7
+ import { Flexbox } from 'react-layout-kit';
8
+
9
+ const useStyles = createStyles(({ css, token, cx }) => ({
10
+ query: cx(css`
11
+ padding-block: 4px;
12
+ padding-inline: 8px;
13
+ border-radius: 8px;
14
+
15
+ font-size: 12px;
16
+ color: ${token.colorTextSecondary};
17
+
18
+ &:hover {
19
+ background: ${token.colorFillTertiary};
20
+ }
21
+ `),
22
+ }));
23
+
24
+ interface SearchFilesProps {
25
+ args: LocalSearchFilesParams;
26
+ }
27
+
28
+ const SearchFiles = memo<SearchFilesProps>(({ args }) => {
29
+ const { styles } = useStyles();
30
+
31
+ return (
32
+ <Flexbox gap={8}>
33
+ <Flexbox align={'center'} distribution={'space-between'} gap={40} height={32} horizontal>
34
+ <Flexbox align={'center'} className={styles.query} gap={8} horizontal>
35
+ <Icon icon={SearchIcon} />
36
+ {args.keywords ? (
37
+ args.keywords
38
+ ) : (
39
+ <Skeleton.Node active style={{ height: 20, width: 40 }} />
40
+ )}
41
+ </Flexbox>
42
+
43
+ <Skeleton.Node active style={{ height: 20, width: 40 }} />
44
+ </Flexbox>
45
+ <Flexbox gap={4}>
46
+ <Skeleton.Button active block style={{ height: 16 }} />
47
+ <Skeleton.Button active block style={{ height: 16 }} />
48
+ <Skeleton.Button active block style={{ height: 16 }} />
49
+ <Skeleton.Button active block style={{ height: 16 }} />
50
+ </Flexbox>
51
+ </Flexbox>
52
+ );
53
+ });
54
+
55
+ export default SearchFiles;
@@ -0,0 +1,25 @@
1
+ import { BuiltinPlaceholderProps } from '@lobechat/types';
2
+ import { memo } from 'react';
3
+
4
+ import { LocalSystemApiName } from '@/tools/local-system';
5
+
6
+ import { ListFiles } from './ListFiles';
7
+ import ReadLocalFile from './ReadLocalFile';
8
+ import SearchFiles from './SearchFiles';
9
+
10
+ const RenderMap = {
11
+ [LocalSystemApiName.searchLocalFiles]: SearchFiles,
12
+ [LocalSystemApiName.listLocalFiles]: ListFiles,
13
+ [LocalSystemApiName.readLocalFile]: ReadLocalFile,
14
+ // [LocalSystemApiName.renameLocalFile]: RenameLocalFile,
15
+ // [LocalSystemApiName.writeLocalFile]: WriteFile,
16
+ };
17
+ const Placeholder = memo<BuiltinPlaceholderProps>(({ apiName, args }) => {
18
+ const Render = RenderMap[apiName as any];
19
+
20
+ if (!Render) return;
21
+
22
+ return <Render args={(args || {}) as any} />;
23
+ });
24
+
25
+ export default Placeholder;
@@ -1,8 +1,11 @@
1
1
  import { BuiltinPlaceholder } from '@lobechat/types';
2
2
 
3
+ import { LocalSystemManifest } from './local-system';
4
+ import LocalSystem from './local-system/Placeholder';
3
5
  import { WebBrowsingManifest } from './web-browsing';
4
6
  import WebBrowsing from './web-browsing/Placeholder';
5
7
 
6
8
  export const BuiltinToolPlaceholders: Record<string, BuiltinPlaceholder> = {
7
9
  [WebBrowsingManifest.identifier]: WebBrowsing as BuiltinPlaceholder,
10
+ [LocalSystemManifest.identifier]: LocalSystem as BuiltinPlaceholder,
8
11
  };