@opensumi/ide-ai-native 3.8.3-next-1741940731.0 → 3.8.3-next-1741942697.0
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/lib/browser/ai-core.contribution.d.ts +1 -0
- package/lib/browser/ai-core.contribution.d.ts.map +1 -1
- package/lib/browser/ai-core.contribution.js +7 -1
- package/lib/browser/ai-core.contribution.js.map +1 -1
- package/lib/browser/chat/chat-manager.service.d.ts +1 -1
- package/lib/browser/chat/chat-manager.service.d.ts.map +1 -1
- package/lib/browser/chat/chat-manager.service.js +3 -2
- package/lib/browser/chat/chat-manager.service.js.map +1 -1
- package/lib/browser/chat/chat-proxy.service.d.ts.map +1 -1
- package/lib/browser/chat/chat-proxy.service.js +1 -0
- package/lib/browser/chat/chat-proxy.service.js.map +1 -1
- package/lib/browser/chat/chat.internal.service.d.ts +1 -1
- package/lib/browser/chat/chat.internal.service.d.ts.map +1 -1
- package/lib/browser/chat/chat.internal.service.js +2 -2
- package/lib/browser/chat/chat.internal.service.js.map +1 -1
- package/lib/browser/chat/chat.view.d.ts.map +1 -1
- package/lib/browser/chat/chat.view.js +10 -7
- package/lib/browser/chat/chat.view.js.map +1 -1
- package/lib/browser/components/ChatEditor.d.ts +2 -1
- package/lib/browser/components/ChatEditor.d.ts.map +1 -1
- package/lib/browser/components/ChatEditor.js +5 -2
- package/lib/browser/components/ChatEditor.js.map +1 -1
- package/lib/browser/components/ChatInput.d.ts +1 -1
- package/lib/browser/components/ChatInput.d.ts.map +1 -1
- package/lib/browser/components/ChatInput.js +1 -1
- package/lib/browser/components/ChatInput.js.map +1 -1
- package/lib/browser/components/ChatMentionInput.d.ts +3 -1
- package/lib/browser/components/ChatMentionInput.d.ts.map +1 -1
- package/lib/browser/components/ChatMentionInput.js +51 -3
- package/lib/browser/components/ChatMentionInput.js.map +1 -1
- package/lib/browser/components/components.module.less +49 -0
- package/lib/browser/components/mention-input/mention-input.d.ts.map +1 -1
- package/lib/browser/components/mention-input/mention-input.js +17 -3
- package/lib/browser/components/mention-input/mention-input.js.map +1 -1
- package/lib/browser/components/mention-input/mention-input.module.less +1 -0
- package/lib/browser/components/mention-input/types.d.ts +3 -1
- package/lib/browser/components/mention-input/types.d.ts.map +1 -1
- package/lib/browser/components/mention-input/types.js.map +1 -1
- package/lib/browser/context/llm-context.service.d.ts.map +1 -1
- package/lib/browser/context/llm-context.service.js.map +1 -1
- package/lib/browser/contrib/image-upload/image-upload.feature.registry.d.ts +8 -0
- package/lib/browser/contrib/image-upload/image-upload.feature.registry.d.ts.map +1 -0
- package/lib/browser/contrib/image-upload/image-upload.feature.registry.js +19 -0
- package/lib/browser/contrib/image-upload/image-upload.feature.registry.js.map +1 -0
- package/lib/browser/index.d.ts.map +1 -1
- package/lib/browser/index.js +5 -0
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/mcp/config/components/mcp-config.view.d.ts.map +1 -1
- package/lib/browser/mcp/config/components/mcp-config.view.js +1 -11
- package/lib/browser/mcp/config/components/mcp-config.view.js.map +1 -1
- package/lib/browser/mcp/mcp-server-proxy.service.d.ts +0 -1
- package/lib/browser/mcp/mcp-server-proxy.service.d.ts.map +1 -1
- package/lib/browser/model/msg-history-manager.d.ts +1 -1
- package/lib/browser/model/msg-history-manager.d.ts.map +1 -1
- package/lib/browser/model/msg-history-manager.js.map +1 -1
- package/lib/browser/types.d.ts +14 -1
- package/lib/browser/types.d.ts.map +1 -1
- package/lib/browser/types.js.map +1 -1
- package/lib/common/index.d.ts +6 -1
- package/lib/common/index.d.ts.map +1 -1
- package/lib/common/index.js.map +1 -1
- package/lib/common/llm-context.d.ts.map +1 -1
- package/lib/common/llm-context.js.map +1 -1
- package/lib/node/base-language-model.d.ts +1 -1
- package/lib/node/base-language-model.d.ts.map +1 -1
- package/lib/node/base-language-model.js +15 -4
- package/lib/node/base-language-model.js.map +1 -1
- package/package.json +23 -23
- package/src/browser/ai-core.contribution.ts +6 -0
- package/src/browser/chat/chat-manager.service.ts +3 -2
- package/src/browser/chat/chat-proxy.service.ts +1 -0
- package/src/browser/chat/chat.internal.service.ts +2 -2
- package/src/browser/chat/chat.view.tsx +18 -8
- package/src/browser/components/ChatEditor.tsx +8 -0
- package/src/browser/components/ChatInput.tsx +2 -2
- package/src/browser/components/ChatMentionInput.tsx +92 -4
- package/src/browser/components/components.module.less +49 -0
- package/src/browser/components/mention-input/mention-input.module.less +1 -0
- package/src/browser/components/mention-input/mention-input.tsx +18 -1
- package/src/browser/components/mention-input/types.ts +3 -1
- package/src/browser/context/llm-context.service.ts +2 -0
- package/src/browser/contrib/image-upload/image-upload.feature.registry.ts +18 -0
- package/src/browser/index.ts +8 -0
- package/src/browser/mcp/config/components/mcp-config.view.tsx +1 -11
- package/src/browser/model/msg-history-manager.ts +1 -1
- package/src/browser/types.ts +17 -1
- package/src/common/index.ts +7 -8
- package/src/common/llm-context.ts +2 -0
- package/src/node/base-language-model.ts +25 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opensumi/ide-ai-native",
|
|
3
|
-
"version": "3.8.3-next-
|
|
3
|
+
"version": "3.8.3-next-1741942697.0",
|
|
4
4
|
"files": [
|
|
5
5
|
"lib",
|
|
6
6
|
"src"
|
|
@@ -24,26 +24,26 @@
|
|
|
24
24
|
"@ai-sdk/openai": "^1.1.9",
|
|
25
25
|
"@ai-sdk/openai-compatible": "^0.1.11",
|
|
26
26
|
"@modelcontextprotocol/sdk": "^1.3.1",
|
|
27
|
-
"@opensumi/ide-addons": "3.8.3-next-
|
|
28
|
-
"@opensumi/ide-components": "3.8.3-next-
|
|
29
|
-
"@opensumi/ide-connection": "3.8.3-next-
|
|
30
|
-
"@opensumi/ide-core-common": "3.8.3-next-
|
|
31
|
-
"@opensumi/ide-core-node": "3.8.3-next-
|
|
32
|
-
"@opensumi/ide-debug": "3.8.3-next-
|
|
33
|
-
"@opensumi/ide-design": "3.8.3-next-
|
|
34
|
-
"@opensumi/ide-editor": "3.8.3-next-
|
|
35
|
-
"@opensumi/ide-file-search": "3.8.3-next-
|
|
36
|
-
"@opensumi/ide-file-service": "3.8.3-next-
|
|
37
|
-
"@opensumi/ide-main-layout": "3.8.3-next-
|
|
38
|
-
"@opensumi/ide-markers": "3.8.3-next-
|
|
39
|
-
"@opensumi/ide-monaco": "3.8.3-next-
|
|
40
|
-
"@opensumi/ide-overlay": "3.8.3-next-
|
|
41
|
-
"@opensumi/ide-preferences": "3.8.3-next-
|
|
42
|
-
"@opensumi/ide-search": "3.8.3-next-
|
|
43
|
-
"@opensumi/ide-terminal-next": "3.8.3-next-
|
|
44
|
-
"@opensumi/ide-theme": "3.8.3-next-
|
|
45
|
-
"@opensumi/ide-utils": "3.8.3-next-
|
|
46
|
-
"@opensumi/ide-workspace": "3.8.3-next-
|
|
27
|
+
"@opensumi/ide-addons": "3.8.3-next-1741942697.0",
|
|
28
|
+
"@opensumi/ide-components": "3.8.3-next-1741942697.0",
|
|
29
|
+
"@opensumi/ide-connection": "3.8.3-next-1741942697.0",
|
|
30
|
+
"@opensumi/ide-core-common": "3.8.3-next-1741942697.0",
|
|
31
|
+
"@opensumi/ide-core-node": "3.8.3-next-1741942697.0",
|
|
32
|
+
"@opensumi/ide-debug": "3.8.3-next-1741942697.0",
|
|
33
|
+
"@opensumi/ide-design": "3.8.3-next-1741942697.0",
|
|
34
|
+
"@opensumi/ide-editor": "3.8.3-next-1741942697.0",
|
|
35
|
+
"@opensumi/ide-file-search": "3.8.3-next-1741942697.0",
|
|
36
|
+
"@opensumi/ide-file-service": "3.8.3-next-1741942697.0",
|
|
37
|
+
"@opensumi/ide-main-layout": "3.8.3-next-1741942697.0",
|
|
38
|
+
"@opensumi/ide-markers": "3.8.3-next-1741942697.0",
|
|
39
|
+
"@opensumi/ide-monaco": "3.8.3-next-1741942697.0",
|
|
40
|
+
"@opensumi/ide-overlay": "3.8.3-next-1741942697.0",
|
|
41
|
+
"@opensumi/ide-preferences": "3.8.3-next-1741942697.0",
|
|
42
|
+
"@opensumi/ide-search": "3.8.3-next-1741942697.0",
|
|
43
|
+
"@opensumi/ide-terminal-next": "3.8.3-next-1741942697.0",
|
|
44
|
+
"@opensumi/ide-theme": "3.8.3-next-1741942697.0",
|
|
45
|
+
"@opensumi/ide-utils": "3.8.3-next-1741942697.0",
|
|
46
|
+
"@opensumi/ide-workspace": "3.8.3-next-1741942697.0",
|
|
47
47
|
"@xterm/xterm": "5.5.0",
|
|
48
48
|
"ai": "^4.1.45",
|
|
49
49
|
"ansi-regex": "^2.0.0",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"zod-to-json-schema": "^3.24.1"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
|
-
"@opensumi/ide-core-browser": "3.8.3-next-
|
|
63
|
+
"@opensumi/ide-core-browser": "3.8.3-next-1741942697.0"
|
|
64
64
|
},
|
|
65
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "3111e67ecae517aa86590e8cb6c58c549e090fff"
|
|
66
66
|
}
|
|
@@ -95,6 +95,7 @@ import { ChatProxyService } from './chat/chat-proxy.service';
|
|
|
95
95
|
import { ChatInternalService } from './chat/chat.internal.service';
|
|
96
96
|
import { AIChatView } from './chat/chat.view';
|
|
97
97
|
import { CodeActionSingleHandler } from './contrib/code-action/code-action.handler';
|
|
98
|
+
import { ImageUploadProviderRegistryToken } from './contrib/image-upload/image-upload.feature.registry';
|
|
98
99
|
import { AIInlineCompletionsProvider } from './contrib/inline-completions/completeProvider';
|
|
99
100
|
import { InlineCompletionsController } from './contrib/inline-completions/inline-completions.controller';
|
|
100
101
|
import { AICompletionsService } from './contrib/inline-completions/service/ai-completions.service';
|
|
@@ -114,6 +115,7 @@ import {
|
|
|
114
115
|
AINativeCoreContribution,
|
|
115
116
|
IChatFeatureRegistry,
|
|
116
117
|
IChatRenderRegistry,
|
|
118
|
+
IImageUploadProviderRegistry,
|
|
117
119
|
IIntelligentCompletionsRegistry,
|
|
118
120
|
IMCPServerRegistry,
|
|
119
121
|
IProblemFixProviderRegistry,
|
|
@@ -196,6 +198,9 @@ export class AINativeBrowserContribution
|
|
|
196
198
|
@Autowired(IntelligentCompletionsRegistryToken)
|
|
197
199
|
private readonly intelligentCompletionsRegistry: IIntelligentCompletionsRegistry;
|
|
198
200
|
|
|
201
|
+
@Autowired(ImageUploadProviderRegistryToken)
|
|
202
|
+
private readonly imageUploadProviderRegistry: IImageUploadProviderRegistry;
|
|
203
|
+
|
|
199
204
|
@Autowired(ProblemFixRegistryToken)
|
|
200
205
|
private readonly problemFixProviderRegistry: IProblemFixProviderRegistry;
|
|
201
206
|
|
|
@@ -453,6 +458,7 @@ export class AINativeBrowserContribution
|
|
|
453
458
|
contribution.registerIntelligentCompletionFeature?.(this.intelligentCompletionsRegistry);
|
|
454
459
|
contribution.registerProblemFixFeature?.(this.problemFixProviderRegistry);
|
|
455
460
|
contribution.registerChatAgentPromptProvider?.();
|
|
461
|
+
contribution.registerImageUploadProvider?.(this.imageUploadProviderRegistry);
|
|
456
462
|
});
|
|
457
463
|
|
|
458
464
|
// 注册 Opensumi 框架提供的 MCP Server Tools 能力 (此时的 Opensumi 作为 MCP Server)
|
|
@@ -150,7 +150,7 @@ export class ChatManagerService extends Disposable {
|
|
|
150
150
|
this.saveSessions();
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
createRequest(sessionId: string, message: string, agentId: string, command?: string) {
|
|
153
|
+
createRequest(sessionId: string, message: string, agentId: string, command?: string, images?: string[]) {
|
|
154
154
|
const model = this.getSession(sessionId);
|
|
155
155
|
if (!model) {
|
|
156
156
|
throw new Error(`Unknown session: ${sessionId}`);
|
|
@@ -160,7 +160,7 @@ export class ChatManagerService extends Disposable {
|
|
|
160
160
|
return;
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
return model.addRequest({ prompt: message, agentId, command });
|
|
163
|
+
return model.addRequest({ prompt: message, agentId, command, images });
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
async sendRequest(sessionId: string, request: ChatRequestModel, regenerate: boolean) {
|
|
@@ -191,6 +191,7 @@ export class ChatManagerService extends Disposable {
|
|
|
191
191
|
requestId: request.requestId,
|
|
192
192
|
message: request.message.prompt,
|
|
193
193
|
command: request.message.command,
|
|
194
|
+
images: request.message.images,
|
|
194
195
|
regenerate,
|
|
195
196
|
};
|
|
196
197
|
const result = await this.chatAgentService.invokeAgent(
|
|
@@ -62,8 +62,8 @@ export class ChatInternalService extends Disposable {
|
|
|
62
62
|
this._onChangeRequestId.fire(id);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
createRequest(input: string, agentId: string, command?: string) {
|
|
66
|
-
return this.chatManagerService.createRequest(this.#sessionModel.sessionId, input, agentId, command);
|
|
65
|
+
createRequest(input: string, agentId: string, images?: string[], command?: string) {
|
|
66
|
+
return this.chatManagerService.createRequest(this.#sessionModel.sessionId, input, agentId, command, images);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
sendRequest(request: ChatRequestModel, regenerate = false) {
|
|
@@ -281,7 +281,7 @@ export const AIChatView = () => {
|
|
|
281
281
|
if (loading) {
|
|
282
282
|
return;
|
|
283
283
|
}
|
|
284
|
-
await handleSend(message.message, message.agentId, message.command);
|
|
284
|
+
await handleSend(message.message, message.images, message.agentId, message.command);
|
|
285
285
|
} else {
|
|
286
286
|
if (message.agentId) {
|
|
287
287
|
setAgentId(message.agentId);
|
|
@@ -462,10 +462,16 @@ export const AIChatView = () => {
|
|
|
462
462
|
);
|
|
463
463
|
|
|
464
464
|
const renderUserMessage = React.useCallback(
|
|
465
|
-
async (renderModel: {
|
|
465
|
+
async (renderModel: {
|
|
466
|
+
message: string;
|
|
467
|
+
images?: string[];
|
|
468
|
+
agentId?: string;
|
|
469
|
+
relationId: string;
|
|
470
|
+
command?: string;
|
|
471
|
+
}) => {
|
|
466
472
|
const ChatUserRoleRender = chatRenderRegistry.chatUserRoleRender;
|
|
467
473
|
|
|
468
|
-
const { message, agentId, relationId, command } = renderModel;
|
|
474
|
+
const { message, images, agentId, relationId, command } = renderModel;
|
|
469
475
|
|
|
470
476
|
const visibleAgentId = agentId === ChatProxyService.AGENT_ID ? '' : agentId;
|
|
471
477
|
|
|
@@ -474,12 +480,13 @@ export const AIChatView = () => {
|
|
|
474
480
|
id: uuid(6),
|
|
475
481
|
relationId,
|
|
476
482
|
text: ChatUserRoleRender ? (
|
|
477
|
-
<ChatUserRoleRender content={message} agentId={visibleAgentId} command={command} />
|
|
483
|
+
<ChatUserRoleRender content={message} images={images} agentId={visibleAgentId} command={command} />
|
|
478
484
|
) : (
|
|
479
485
|
<CodeBlockWrapperInput
|
|
480
486
|
labelService={labelService}
|
|
481
487
|
relationId={relationId}
|
|
482
488
|
text={message}
|
|
489
|
+
images={images}
|
|
483
490
|
agentId={visibleAgentId}
|
|
484
491
|
command={command}
|
|
485
492
|
workspaceService={workspaceService}
|
|
@@ -598,10 +605,10 @@ export const AIChatView = () => {
|
|
|
598
605
|
|
|
599
606
|
const handleAgentReply = React.useCallback(
|
|
600
607
|
async (value: IChatMessageStructure) => {
|
|
601
|
-
const { message, agentId, command, reportExtra } = value;
|
|
608
|
+
const { message, images, agentId, command, reportExtra } = value;
|
|
602
609
|
const { actionType, actionSource } = reportExtra || {};
|
|
603
610
|
|
|
604
|
-
const request = aiChatService.createRequest(message, agentId!, command);
|
|
611
|
+
const request = aiChatService.createRequest(message, agentId!, images, command);
|
|
605
612
|
if (!request) {
|
|
606
613
|
return;
|
|
607
614
|
}
|
|
@@ -627,6 +634,7 @@ export const AIChatView = () => {
|
|
|
627
634
|
|
|
628
635
|
msgHistoryManager.addUserMessage({
|
|
629
636
|
content: message,
|
|
637
|
+
images: images || [],
|
|
630
638
|
agentId: agentId!,
|
|
631
639
|
agentCommand: command!,
|
|
632
640
|
relationId,
|
|
@@ -635,6 +643,7 @@ export const AIChatView = () => {
|
|
|
635
643
|
await renderUserMessage({
|
|
636
644
|
relationId,
|
|
637
645
|
message,
|
|
646
|
+
images,
|
|
638
647
|
command,
|
|
639
648
|
agentId,
|
|
640
649
|
});
|
|
@@ -668,7 +677,7 @@ export const AIChatView = () => {
|
|
|
668
677
|
);
|
|
669
678
|
|
|
670
679
|
const handleSend = React.useCallback(
|
|
671
|
-
async (message: string, agentId?: string, command?: string) => {
|
|
680
|
+
async (message: string, images?: string[], agentId?: string, command?: string) => {
|
|
672
681
|
const reportExtra = {
|
|
673
682
|
actionSource: ActionSourceEnum.Chat,
|
|
674
683
|
actionType: ActionTypeEnum.Send,
|
|
@@ -707,7 +716,7 @@ export const AIChatView = () => {
|
|
|
707
716
|
processedContent = processedContent.replace(match, `\`<attached_folder>${relativePath}\``);
|
|
708
717
|
}
|
|
709
718
|
}
|
|
710
|
-
return handleAgentReply({ message: processedContent, agentId, command, reportExtra });
|
|
719
|
+
return handleAgentReply({ message: processedContent, images, agentId, command, reportExtra });
|
|
711
720
|
},
|
|
712
721
|
[handleAgentReply],
|
|
713
722
|
);
|
|
@@ -750,6 +759,7 @@ export const AIChatView = () => {
|
|
|
750
759
|
message: msg.content,
|
|
751
760
|
agentId: msg.agentId,
|
|
752
761
|
command: msg.agentCommand,
|
|
762
|
+
images: msg.images,
|
|
753
763
|
});
|
|
754
764
|
} else if (msg.role === ChatMessageRole.Assistant && msg.requestId) {
|
|
755
765
|
const request = aiChatService.sessionModel.getRequest(msg.requestId)!;
|
|
@@ -2,6 +2,7 @@ import capitalize from 'lodash/capitalize';
|
|
|
2
2
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
3
|
import Highlight from 'react-highlight';
|
|
4
4
|
|
|
5
|
+
import { Image } from '@opensumi/ide-components/lib/image';
|
|
5
6
|
import {
|
|
6
7
|
EDITOR_COMMANDS,
|
|
7
8
|
FILE_COMMANDS,
|
|
@@ -336,6 +337,7 @@ export const CodeBlockWrapper = ({
|
|
|
336
337
|
|
|
337
338
|
export const CodeBlockWrapperInput = ({
|
|
338
339
|
text,
|
|
340
|
+
images,
|
|
339
341
|
relationId,
|
|
340
342
|
agentId,
|
|
341
343
|
command,
|
|
@@ -344,6 +346,7 @@ export const CodeBlockWrapperInput = ({
|
|
|
344
346
|
commandService,
|
|
345
347
|
}: {
|
|
346
348
|
text: string;
|
|
349
|
+
images?: string[];
|
|
347
350
|
relationId: string;
|
|
348
351
|
agentId?: string;
|
|
349
352
|
command?: string;
|
|
@@ -370,6 +373,11 @@ export const CodeBlockWrapperInput = ({
|
|
|
370
373
|
|
|
371
374
|
return (
|
|
372
375
|
<div className={styles.ai_chat_code_wrapper}>
|
|
376
|
+
{images?.map((image) => (
|
|
377
|
+
<div className={styles.image_wrapper}>
|
|
378
|
+
<Image src={image} />
|
|
379
|
+
</div>
|
|
380
|
+
))}
|
|
373
381
|
<div className={styles.render_text}>
|
|
374
382
|
{tag && (
|
|
375
383
|
<div className={styles.tag_wrapper}>
|
|
@@ -162,7 +162,7 @@ const AgentWidget = ({ agentId, command }) => (
|
|
|
162
162
|
);
|
|
163
163
|
|
|
164
164
|
export interface IChatInputProps {
|
|
165
|
-
onSend: (value: string, agentId?: string, command?: string) => void;
|
|
165
|
+
onSend: (value: string, images?: string[], agentId?: string, command?: string) => void;
|
|
166
166
|
onValueChange?: (value: string) => void;
|
|
167
167
|
onExpand?: (value: boolean) => void;
|
|
168
168
|
placeholder?: string;
|
|
@@ -341,7 +341,7 @@ export const ChatInput = React.forwardRef((props: IChatInputProps, ref) => {
|
|
|
341
341
|
}
|
|
342
342
|
|
|
343
343
|
const handleSendLogic = (newValue: string = value) => {
|
|
344
|
-
onSend(newValue, agentId, command);
|
|
344
|
+
onSend(newValue, [], agentId, command);
|
|
345
345
|
setValue('');
|
|
346
346
|
setTheme('');
|
|
347
347
|
setAgentId('');
|
|
@@ -1,23 +1,34 @@
|
|
|
1
|
+
import { DataContent } from 'ai';
|
|
1
2
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
3
|
|
|
4
|
+
import { Image } from '@opensumi/ide-components/lib/image';
|
|
3
5
|
import { LabelService, RecentFilesManager, useInjectable } from '@opensumi/ide-core-browser';
|
|
4
|
-
import { getIcon } from '@opensumi/ide-core-browser/lib/components';
|
|
6
|
+
import { Icon, getIcon } from '@opensumi/ide-core-browser/lib/components';
|
|
5
7
|
import { URI, localize } from '@opensumi/ide-core-common';
|
|
6
8
|
import { CommandService } from '@opensumi/ide-core-common/lib/command';
|
|
7
9
|
import { WorkbenchEditorService } from '@opensumi/ide-editor';
|
|
8
10
|
import { FileSearchServicePath, IFileSearchService } from '@opensumi/ide-file-search';
|
|
11
|
+
import { IMessageService } from '@opensumi/ide-overlay';
|
|
9
12
|
import { IWorkspaceService } from '@opensumi/ide-workspace';
|
|
10
13
|
|
|
11
14
|
import { IChatInternalService } from '../../common';
|
|
12
15
|
import { ChatInternalService } from '../chat/chat.internal.service';
|
|
16
|
+
import { ImageUploadProviderRegistryToken } from '../contrib/image-upload/image-upload.feature.registry';
|
|
13
17
|
import { OPEN_MCP_CONFIG_COMMAND } from '../mcp/config/mcp-config.commands';
|
|
18
|
+
import { IImageUploadProviderRegistry } from '../types';
|
|
14
19
|
|
|
15
20
|
import styles from './components.module.less';
|
|
16
21
|
import { MentionInput } from './mention-input/mention-input';
|
|
17
22
|
import { FooterButtonPosition, FooterConfig, MentionItem, MentionType } from './mention-input/types';
|
|
18
23
|
|
|
19
24
|
export interface IChatMentionInputProps {
|
|
20
|
-
onSend: (
|
|
25
|
+
onSend: (
|
|
26
|
+
value: string,
|
|
27
|
+
images?: string[],
|
|
28
|
+
agentId?: string,
|
|
29
|
+
command?: string,
|
|
30
|
+
option?: { model: string; [key: string]: any },
|
|
31
|
+
) => void;
|
|
21
32
|
onValueChange?: (value: string) => void;
|
|
22
33
|
onExpand?: (value: boolean) => void;
|
|
23
34
|
placeholder?: string;
|
|
@@ -26,6 +37,7 @@ export interface IChatMentionInputProps {
|
|
|
26
37
|
sendBtnClassName?: string;
|
|
27
38
|
defaultHeight?: number;
|
|
28
39
|
value?: string;
|
|
40
|
+
images?: Array<DataContent | URL>;
|
|
29
41
|
autoFocus?: boolean;
|
|
30
42
|
theme?: string | null;
|
|
31
43
|
setTheme: (theme: string | null) => void;
|
|
@@ -41,6 +53,7 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
|
|
|
41
53
|
const { onSend, disabled = false } = props;
|
|
42
54
|
|
|
43
55
|
const [value, setValue] = useState(props.value || '');
|
|
56
|
+
const [images, setImages] = useState(props.images || []);
|
|
44
57
|
const aiChatService = useInjectable<ChatInternalService>(IChatInternalService);
|
|
45
58
|
const commandService = useInjectable<CommandService>(CommandService);
|
|
46
59
|
const searchService = useInjectable<IFileSearchService>(FileSearchServicePath);
|
|
@@ -48,6 +61,8 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
|
|
|
48
61
|
const workspaceService = useInjectable<IWorkspaceService>(IWorkspaceService);
|
|
49
62
|
const editorService = useInjectable<WorkbenchEditorService>(WorkbenchEditorService);
|
|
50
63
|
const labelService = useInjectable<LabelService>(LabelService);
|
|
64
|
+
const messageService = useInjectable<IMessageService>(IMessageService);
|
|
65
|
+
const imageUploadProviderRegistry = useInjectable<IImageUploadProviderRegistry>(ImageUploadProviderRegistryToken);
|
|
51
66
|
|
|
52
67
|
const handleShowMCPConfig = React.useCallback(() => {
|
|
53
68
|
commandService.executeCommand(OPEN_MCP_CONFIG_COMMAND.id);
|
|
@@ -239,6 +254,24 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
|
|
|
239
254
|
onClick: handleShowMCPConfig,
|
|
240
255
|
position: FooterButtonPosition.LEFT,
|
|
241
256
|
},
|
|
257
|
+
{
|
|
258
|
+
id: 'upload-image',
|
|
259
|
+
iconClass: 'codicon codicon-file-media',
|
|
260
|
+
title: 'Upload Image',
|
|
261
|
+
onClick: () => {
|
|
262
|
+
const input = document.createElement('input');
|
|
263
|
+
input.type = 'file';
|
|
264
|
+
input.accept = 'image/*';
|
|
265
|
+
input.onchange = (e) => {
|
|
266
|
+
const file = (e.target as HTMLInputElement).files?.[0];
|
|
267
|
+
if (file) {
|
|
268
|
+
handleImageUpload(file);
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
input.click();
|
|
272
|
+
},
|
|
273
|
+
position: FooterButtonPosition.LEFT,
|
|
274
|
+
},
|
|
242
275
|
],
|
|
243
276
|
showModelSelector: true,
|
|
244
277
|
}),
|
|
@@ -254,13 +287,46 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
|
|
|
254
287
|
if (disabled) {
|
|
255
288
|
return;
|
|
256
289
|
}
|
|
257
|
-
onSend(
|
|
290
|
+
onSend(
|
|
291
|
+
content,
|
|
292
|
+
images.map((image) => image.toString()),
|
|
293
|
+
undefined,
|
|
294
|
+
undefined,
|
|
295
|
+
option,
|
|
296
|
+
);
|
|
258
297
|
},
|
|
259
|
-
[onSend,
|
|
298
|
+
[onSend, images, disabled],
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
const handleImageUpload = useCallback(
|
|
302
|
+
async (file: File) => {
|
|
303
|
+
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/gif'];
|
|
304
|
+
if (!allowedTypes.includes(file.type)) {
|
|
305
|
+
messageService.error('Only JPG, PNG, WebP and GIF images are supported');
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const imageUploadProvider = imageUploadProviderRegistry.getImageUploadProvider();
|
|
310
|
+
if (!imageUploadProvider) {
|
|
311
|
+
messageService.error('No image upload provider found');
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const data = await imageUploadProvider.imageUpload(file);
|
|
315
|
+
setImages([...images, data]);
|
|
316
|
+
},
|
|
317
|
+
[images],
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
const handleDeleteImage = useCallback(
|
|
321
|
+
(index: number) => {
|
|
322
|
+
setImages(images.filter((_, i) => i !== index));
|
|
323
|
+
},
|
|
324
|
+
[images],
|
|
260
325
|
);
|
|
261
326
|
|
|
262
327
|
return (
|
|
263
328
|
<div className={styles.chat_input_container}>
|
|
329
|
+
{images.length > 0 && <ImagePreviewer images={images} onDelete={handleDeleteImage} />}
|
|
264
330
|
<MentionInput
|
|
265
331
|
mentionItems={defaultMenuItems}
|
|
266
332
|
onSend={handleSend}
|
|
@@ -270,7 +336,29 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
|
|
|
270
336
|
workspaceService={workspaceService}
|
|
271
337
|
placeholder={localize('aiNative.chat.input.placeholder.default')}
|
|
272
338
|
footerConfig={defaultMentionInputFooterOptions}
|
|
339
|
+
onImageUpload={handleImageUpload}
|
|
273
340
|
/>
|
|
274
341
|
</div>
|
|
275
342
|
);
|
|
276
343
|
};
|
|
344
|
+
|
|
345
|
+
const ImagePreviewer = ({
|
|
346
|
+
images,
|
|
347
|
+
onDelete,
|
|
348
|
+
}: {
|
|
349
|
+
images: Array<DataContent | URL>;
|
|
350
|
+
onDelete: (index: number) => void;
|
|
351
|
+
}) => (
|
|
352
|
+
<div>
|
|
353
|
+
<div className={styles.thumbnail_container}>
|
|
354
|
+
{images.map((image, index) => (
|
|
355
|
+
<div key={index} className={styles.thumbnail}>
|
|
356
|
+
<Image src={image.toString()} />
|
|
357
|
+
<button onClick={() => onDelete(index)} className={styles.delete_button}>
|
|
358
|
+
<Icon iconClass='codicon codicon-close' />
|
|
359
|
+
</button>
|
|
360
|
+
</div>
|
|
361
|
+
))}
|
|
362
|
+
</div>
|
|
363
|
+
</div>
|
|
364
|
+
);
|
|
@@ -601,3 +601,52 @@
|
|
|
601
601
|
vertical-align: middle;
|
|
602
602
|
font-size: 12px;
|
|
603
603
|
}
|
|
604
|
+
|
|
605
|
+
.thumbnail_container {
|
|
606
|
+
display: flex;
|
|
607
|
+
gap: 4px;
|
|
608
|
+
padding: 4px 12px;
|
|
609
|
+
.thumbnail {
|
|
610
|
+
width: 36px;
|
|
611
|
+
height: 36px;
|
|
612
|
+
border-radius: 4px;
|
|
613
|
+
position: relative;
|
|
614
|
+
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.3);
|
|
615
|
+
padding: 2px;
|
|
616
|
+
display: inline-flex;
|
|
617
|
+
align-items: center;
|
|
618
|
+
img {
|
|
619
|
+
width: 100%;
|
|
620
|
+
height: 100%;
|
|
621
|
+
object-fit: cover;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
.delete_button {
|
|
625
|
+
position: absolute;
|
|
626
|
+
top: -5px;
|
|
627
|
+
right: -5px;
|
|
628
|
+
font-size: 12px;
|
|
629
|
+
padding: 2px;
|
|
630
|
+
border: 0;
|
|
631
|
+
border-radius: 50%;
|
|
632
|
+
transition: transform 0.2s ease-in-out;
|
|
633
|
+
width: 16px;
|
|
634
|
+
height: 16px;
|
|
635
|
+
display: flex;
|
|
636
|
+
justify-content: center;
|
|
637
|
+
align-items: center;
|
|
638
|
+
background-color: var(--badge-background);
|
|
639
|
+
&:hover {
|
|
640
|
+
cursor: pointer;
|
|
641
|
+
transform: scale(1.2);
|
|
642
|
+
}
|
|
643
|
+
:global(.codicon) {
|
|
644
|
+
font-size: 12px;
|
|
645
|
+
color: var(--badge-foreground);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
.image_wrapper {
|
|
651
|
+
display: inline-block;
|
|
652
|
+
}
|
|
@@ -18,6 +18,7 @@ export const MentionInput: React.FC<MentionInputProps> = ({
|
|
|
18
18
|
loading = false,
|
|
19
19
|
mentionKeyword = MENTION_KEYWORD,
|
|
20
20
|
onSelectionChange,
|
|
21
|
+
onImageUpload,
|
|
21
22
|
labelService,
|
|
22
23
|
workspaceService,
|
|
23
24
|
placeholder = 'Ask anything, @ to mention',
|
|
@@ -275,6 +276,21 @@ export const MentionInput: React.FC<MentionInputProps> = ({
|
|
|
275
276
|
}
|
|
276
277
|
};
|
|
277
278
|
|
|
279
|
+
// 处理图片粘贴事件
|
|
280
|
+
const handlePaste = (e: React.ClipboardEvent<HTMLDivElement>) => {
|
|
281
|
+
const items = e.clipboardData.items;
|
|
282
|
+
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
|
283
|
+
for (let i = 0; i < items.length; i++) {
|
|
284
|
+
if (items[i].kind === 'file' && items[i].type.startsWith('image/')) {
|
|
285
|
+
const file = items[i].getAsFile();
|
|
286
|
+
if (file && onImageUpload) {
|
|
287
|
+
e.preventDefault();
|
|
288
|
+
onImageUpload(file);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
|
|
278
294
|
// 处理键盘事件
|
|
279
295
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
|
280
296
|
// 如果按下ESC键且提及面板处于活动状态或内联搜索处于活动状态
|
|
@@ -867,7 +883,7 @@ export const MentionInput: React.FC<MentionInputProps> = ({
|
|
|
867
883
|
title={button.title}
|
|
868
884
|
>
|
|
869
885
|
<EnhanceIcon
|
|
870
|
-
className={cls(getIcon(button.icon), styles[`${button.id}_logo`])}
|
|
886
|
+
className={cls(button.icon ? getIcon(button.icon) : button.iconClass, styles[`${button.id}_logo`])}
|
|
871
887
|
tabIndex={0}
|
|
872
888
|
role='button'
|
|
873
889
|
ariaLabel={button.title}
|
|
@@ -901,6 +917,7 @@ export const MentionInput: React.FC<MentionInputProps> = ({
|
|
|
901
917
|
contentEditable={true}
|
|
902
918
|
onInput={handleInput}
|
|
903
919
|
onKeyDown={handleKeyDown}
|
|
920
|
+
onPaste={handlePaste}
|
|
904
921
|
onCompositionEnd={handleCompositionEnd}
|
|
905
922
|
/>
|
|
906
923
|
</div>
|
|
@@ -55,7 +55,8 @@ export enum MentionType {
|
|
|
55
55
|
|
|
56
56
|
interface FooterButton {
|
|
57
57
|
id: string;
|
|
58
|
-
icon
|
|
58
|
+
icon?: string;
|
|
59
|
+
iconClass?: string;
|
|
59
60
|
title: string;
|
|
60
61
|
onClick?: () => void;
|
|
61
62
|
position: FooterButtonPosition;
|
|
@@ -75,6 +76,7 @@ export interface MentionInputProps {
|
|
|
75
76
|
placeholder?: string;
|
|
76
77
|
loading?: boolean;
|
|
77
78
|
onSelectionChange?: (value: string) => void;
|
|
79
|
+
onImageUpload?: (file: File) => void;
|
|
78
80
|
footerConfig?: FooterConfig; // 新增配置项
|
|
79
81
|
mentionKeyword?: string;
|
|
80
82
|
labelService?: LabelService;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { DataContent } from 'ai';
|
|
2
|
+
|
|
1
3
|
import { Autowired, Injectable } from '@opensumi/di';
|
|
2
4
|
import { AppConfig } from '@opensumi/ide-core-browser/lib/react-providers/config-provider';
|
|
3
5
|
import { WithEventBus } from '@opensumi/ide-core-common/lib/event-bus/event-decorator';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Injectable } from '@opensumi/di';
|
|
2
|
+
|
|
3
|
+
import { IImageUploadProvider, IImageUploadProviderRegistry } from '../../types';
|
|
4
|
+
|
|
5
|
+
@Injectable()
|
|
6
|
+
export class ImageUploadProviderRegistry implements IImageUploadProviderRegistry {
|
|
7
|
+
private imageUploadProvider: IImageUploadProvider | undefined;
|
|
8
|
+
|
|
9
|
+
registerImageUploadProvider(provider: IImageUploadProvider): void {
|
|
10
|
+
this.imageUploadProvider = provider;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
getImageUploadProvider(): IImageUploadProvider | undefined {
|
|
14
|
+
return this.imageUploadProvider;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const ImageUploadProviderRegistryToken = Symbol('ImageUploadProviderRegistry');
|
package/src/browser/index.ts
CHANGED
|
@@ -46,6 +46,10 @@ import { ChatRenderRegistry } from './chat/chat.render.registry';
|
|
|
46
46
|
import { LlmContextContribution } from './context/llm-context.contribution';
|
|
47
47
|
import { LLMContextServiceImpl } from './context/llm-context.service';
|
|
48
48
|
import { AICodeActionContribution } from './contrib/code-action/code-action.contribution';
|
|
49
|
+
import {
|
|
50
|
+
ImageUploadProviderRegistry,
|
|
51
|
+
ImageUploadProviderRegistryToken,
|
|
52
|
+
} from './contrib/image-upload/image-upload.feature.registry';
|
|
49
53
|
import { AIInlineCompletionsProvider } from './contrib/inline-completions/completeProvider';
|
|
50
54
|
import { IntelligentCompletionsContribution } from './contrib/intelligent-completions/intelligent-completions.contribution';
|
|
51
55
|
import { IntelligentCompletionsRegistry } from './contrib/intelligent-completions/intelligent-completions.feature.registry';
|
|
@@ -186,6 +190,10 @@ export class AINativeModule extends BrowserModule {
|
|
|
186
190
|
token: TerminalRegistryToken,
|
|
187
191
|
useClass: TerminalFeatureRegistry,
|
|
188
192
|
},
|
|
193
|
+
{
|
|
194
|
+
token: ImageUploadProviderRegistryToken,
|
|
195
|
+
useClass: ImageUploadProviderRegistry,
|
|
196
|
+
},
|
|
189
197
|
{
|
|
190
198
|
token: LanguageParserService,
|
|
191
199
|
useClass: LanguageParserService,
|
|
@@ -26,17 +26,7 @@ export const MCPConfigView: React.FC = () => {
|
|
|
26
26
|
const [editingServer, setEditingServer] = React.useState<MCPServerFormData | undefined>();
|
|
27
27
|
const [loadingServer, setLoadingServer] = React.useState<string | undefined>();
|
|
28
28
|
const loadServers = useCallback(async () => {
|
|
29
|
-
const
|
|
30
|
-
const runningServers = await mcpServerProxyService.$getServers();
|
|
31
|
-
const allServers = userServers.map((server) => {
|
|
32
|
-
const runningServer = runningServers.find((s) => s.name === server.name);
|
|
33
|
-
return {
|
|
34
|
-
...server,
|
|
35
|
-
name: server.name,
|
|
36
|
-
isStarted: !!runningServer,
|
|
37
|
-
tools: runningServer?.tools,
|
|
38
|
-
};
|
|
39
|
-
});
|
|
29
|
+
const allServers = await mcpServerProxyService.$getServers();
|
|
40
30
|
setServers(allServers);
|
|
41
31
|
}, [mcpServerProxyService]);
|
|
42
32
|
|
|
@@ -62,7 +62,7 @@ export class MsgHistoryManager extends Disposable {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
public addUserMessage(
|
|
65
|
-
message: Required<Pick<IExcludeMessage, 'agentId' | 'agentCommand' | 'content' | 'relationId'>>,
|
|
65
|
+
message: Required<Pick<IExcludeMessage, 'agentId' | 'agentCommand' | 'content' | 'relationId' | 'images'>>,
|
|
66
66
|
): string {
|
|
67
67
|
return this.doAddMessage({
|
|
68
68
|
...message,
|