@opensumi/ide-ai-native 3.7.1-next-1737628160.0 → 3.7.1-next-1737703128.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.
Files changed (165) hide show
  1. package/lib/browser/ai-core.contribution.d.ts +3 -0
  2. package/lib/browser/ai-core.contribution.d.ts.map +1 -1
  3. package/lib/browser/ai-core.contribution.js +31 -4
  4. package/lib/browser/ai-core.contribution.js.map +1 -1
  5. package/lib/browser/chat/chat-model.d.ts +2 -2
  6. package/lib/browser/chat/chat-model.d.ts.map +1 -1
  7. package/lib/browser/chat/chat-model.js +21 -9
  8. package/lib/browser/chat/chat-model.js.map +1 -1
  9. package/lib/browser/chat/chat-proxy.service.d.ts +1 -0
  10. package/lib/browser/chat/chat-proxy.service.d.ts.map +1 -1
  11. package/lib/browser/chat/chat-proxy.service.js +11 -0
  12. package/lib/browser/chat/chat-proxy.service.js.map +1 -1
  13. package/lib/browser/components/ChatEditor.d.ts +8 -0
  14. package/lib/browser/components/ChatEditor.d.ts.map +1 -1
  15. package/lib/browser/components/ChatEditor.js +8 -7
  16. package/lib/browser/components/ChatEditor.js.map +1 -1
  17. package/lib/browser/components/ChatReply.d.ts.map +1 -1
  18. package/lib/browser/components/ChatReply.js +33 -4
  19. package/lib/browser/components/ChatReply.js.map +1 -1
  20. package/lib/browser/components/ChatToolRender.d.ts +6 -0
  21. package/lib/browser/components/ChatToolRender.d.ts.map +1 -0
  22. package/lib/browser/components/ChatToolRender.js +24 -0
  23. package/lib/browser/components/ChatToolRender.js.map +1 -0
  24. package/lib/browser/components/components.module.less +32 -31
  25. package/lib/browser/contrib/intelligent-completions/index.d.ts +1 -5
  26. package/lib/browser/contrib/intelligent-completions/index.d.ts.map +1 -1
  27. package/lib/browser/contrib/intelligent-completions/index.js.map +1 -1
  28. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.d.ts.map +1 -1
  29. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.js +1 -2
  30. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.js.map +1 -1
  31. package/lib/browser/contrib/intelligent-completions/source/base.d.ts +2 -1
  32. package/lib/browser/contrib/intelligent-completions/source/base.d.ts.map +1 -1
  33. package/lib/browser/contrib/intelligent-completions/source/base.js.map +1 -1
  34. package/lib/browser/contrib/intelligent-completions/source/line-change.source.d.ts +3 -2
  35. package/lib/browser/contrib/intelligent-completions/source/line-change.source.d.ts.map +1 -1
  36. package/lib/browser/contrib/intelligent-completions/source/line-change.source.js +21 -51
  37. package/lib/browser/contrib/intelligent-completions/source/line-change.source.js.map +1 -1
  38. package/lib/browser/index.d.ts +11 -3
  39. package/lib/browser/index.d.ts.map +1 -1
  40. package/lib/browser/index.js +39 -3
  41. package/lib/browser/index.js.map +1 -1
  42. package/lib/browser/mcp/mcp-server-proxy.service.d.ts +17 -0
  43. package/lib/browser/mcp/mcp-server-proxy.service.d.ts.map +1 -0
  44. package/lib/browser/mcp/mcp-server-proxy.service.js +36 -0
  45. package/lib/browser/mcp/mcp-server-proxy.service.js.map +1 -0
  46. package/lib/browser/mcp/mcp-server.feature.registry.d.ts +16 -0
  47. package/lib/browser/mcp/mcp-server.feature.registry.d.ts.map +1 -0
  48. package/lib/browser/mcp/mcp-server.feature.registry.js +45 -0
  49. package/lib/browser/mcp/mcp-server.feature.registry.js.map +1 -0
  50. package/lib/browser/mcp/tools/createNewFileWithText.d.ts +9 -0
  51. package/lib/browser/mcp/tools/createNewFileWithText.d.ts.map +1 -0
  52. package/lib/browser/mcp/tools/createNewFileWithText.js +84 -0
  53. package/lib/browser/mcp/tools/createNewFileWithText.js.map +1 -0
  54. package/lib/browser/mcp/tools/findFilesByNameSubstring.d.ts +9 -0
  55. package/lib/browser/mcp/tools/findFilesByNameSubstring.d.ts.map +1 -0
  56. package/lib/browser/mcp/tools/findFilesByNameSubstring.js +92 -0
  57. package/lib/browser/mcp/tools/findFilesByNameSubstring.js.map +1 -0
  58. package/lib/browser/mcp/tools/getCurrentFilePath.d.ts +8 -0
  59. package/lib/browser/mcp/tools/getCurrentFilePath.d.ts.map +1 -0
  60. package/lib/browser/mcp/tools/getCurrentFilePath.js +49 -0
  61. package/lib/browser/mcp/tools/getCurrentFilePath.js.map +1 -0
  62. package/lib/browser/mcp/tools/getDiagnosticsByPath.d.ts +10 -0
  63. package/lib/browser/mcp/tools/getDiagnosticsByPath.d.ts.map +1 -0
  64. package/lib/browser/mcp/tools/getDiagnosticsByPath.js +119 -0
  65. package/lib/browser/mcp/tools/getDiagnosticsByPath.js.map +1 -0
  66. package/lib/browser/mcp/tools/getFileTextByPath.d.ts +9 -0
  67. package/lib/browser/mcp/tools/getFileTextByPath.d.ts.map +1 -0
  68. package/lib/browser/mcp/tools/getFileTextByPath.js +97 -0
  69. package/lib/browser/mcp/tools/getFileTextByPath.js.map +1 -0
  70. package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.d.ts +11 -0
  71. package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.d.ts.map +1 -0
  72. package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.js +119 -0
  73. package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.js.map +1 -0
  74. package/lib/browser/mcp/tools/getOpenEditorFileText.d.ts +8 -0
  75. package/lib/browser/mcp/tools/getOpenEditorFileText.d.ts.map +1 -0
  76. package/lib/browser/mcp/tools/getOpenEditorFileText.js +50 -0
  77. package/lib/browser/mcp/tools/getOpenEditorFileText.js.map +1 -0
  78. package/lib/browser/mcp/tools/getSelectedText.d.ts +8 -0
  79. package/lib/browser/mcp/tools/getSelectedText.d.ts.map +1 -0
  80. package/lib/browser/mcp/tools/getSelectedText.js +57 -0
  81. package/lib/browser/mcp/tools/getSelectedText.js.map +1 -0
  82. package/lib/browser/preferences/schema.d.ts.map +1 -1
  83. package/lib/browser/preferences/schema.js +0 -4
  84. package/lib/browser/preferences/schema.js.map +1 -1
  85. package/lib/browser/types.d.ts +31 -0
  86. package/lib/browser/types.d.ts.map +1 -1
  87. package/lib/browser/types.js +4 -1
  88. package/lib/browser/types.js.map +1 -1
  89. package/lib/common/index.d.ts +5 -0
  90. package/lib/common/index.d.ts.map +1 -1
  91. package/lib/common/index.js +4 -1
  92. package/lib/common/index.js.map +1 -1
  93. package/lib/common/mcp-server-manager.d.ts +39 -0
  94. package/lib/common/mcp-server-manager.d.ts.map +1 -0
  95. package/lib/common/mcp-server-manager.js +6 -0
  96. package/lib/common/mcp-server-manager.js.map +1 -0
  97. package/lib/common/tool-invocation-registry.d.ts +67 -0
  98. package/lib/common/tool-invocation-registry.d.ts.map +1 -0
  99. package/lib/common/tool-invocation-registry.js +68 -0
  100. package/lib/common/tool-invocation-registry.js.map +1 -0
  101. package/lib/common/types.d.ts +14 -0
  102. package/lib/common/types.d.ts.map +1 -1
  103. package/lib/node/anthropic/anthropic-language-model.d.ts +13 -0
  104. package/lib/node/anthropic/anthropic-language-model.d.ts.map +1 -0
  105. package/lib/node/anthropic/anthropic-language-model.js +79 -0
  106. package/lib/node/anthropic/anthropic-language-model.js.map +1 -0
  107. package/lib/node/index.d.ts.map +1 -1
  108. package/lib/node/index.js +21 -0
  109. package/lib/node/index.js.map +1 -1
  110. package/lib/node/mcp/sumi-mcp-server.d.ts +84 -0
  111. package/lib/node/mcp/sumi-mcp-server.d.ts.map +1 -0
  112. package/lib/node/mcp/sumi-mcp-server.js +134 -0
  113. package/lib/node/mcp/sumi-mcp-server.js.map +1 -0
  114. package/lib/node/mcp-server-manager-impl.d.ts +20 -0
  115. package/lib/node/mcp-server-manager-impl.d.ts.map +1 -0
  116. package/lib/node/mcp-server-manager-impl.js +123 -0
  117. package/lib/node/mcp-server-manager-impl.js.map +1 -0
  118. package/lib/node/mcp-server.d.ts +205 -0
  119. package/lib/node/mcp-server.d.ts.map +1 -0
  120. package/lib/node/mcp-server.js +86 -0
  121. package/lib/node/mcp-server.js.map +1 -0
  122. package/lib/node/openai/openai-language-model.d.ts +12 -0
  123. package/lib/node/openai/openai-language-model.d.ts.map +1 -0
  124. package/lib/node/openai/openai-language-model.js +136 -0
  125. package/lib/node/openai/openai-language-model.js.map +1 -0
  126. package/package.json +26 -21
  127. package/src/browser/ai-core.contribution.ts +47 -4
  128. package/src/browser/chat/chat-model.ts +21 -9
  129. package/src/browser/chat/chat-proxy.service.ts +12 -0
  130. package/src/browser/components/ChatEditor.tsx +6 -4
  131. package/src/browser/components/ChatReply.tsx +40 -4
  132. package/src/browser/components/ChatToolRender.tsx +27 -0
  133. package/src/browser/components/components.module.less +32 -31
  134. package/src/browser/contrib/intelligent-completions/index.ts +2 -3
  135. package/src/browser/contrib/intelligent-completions/intelligent-completions.controller.ts +1 -2
  136. package/src/browser/contrib/intelligent-completions/source/base.ts +2 -0
  137. package/src/browser/contrib/intelligent-completions/source/line-change.source.ts +24 -79
  138. package/src/browser/index.ts +50 -4
  139. package/src/browser/mcp/mcp-server-proxy.service.ts +32 -0
  140. package/src/browser/mcp/mcp-server.feature.registry.ts +47 -0
  141. package/src/browser/mcp/tools/createNewFileWithText.ts +85 -0
  142. package/src/browser/mcp/tools/findFilesByNameSubstring.ts +93 -0
  143. package/src/browser/mcp/tools/getCurrentFilePath.ts +49 -0
  144. package/src/browser/mcp/tools/getDiagnosticsByPath.ts +123 -0
  145. package/src/browser/mcp/tools/getFileTextByPath.ts +97 -0
  146. package/src/browser/mcp/tools/getOpenEditorFileDiagnostics.ts +121 -0
  147. package/src/browser/mcp/tools/getOpenEditorFileText.ts +50 -0
  148. package/src/browser/mcp/tools/getSelectedText.ts +57 -0
  149. package/src/browser/preferences/schema.ts +0 -4
  150. package/src/browser/types.ts +39 -0
  151. package/src/common/index.ts +9 -0
  152. package/src/common/mcp-server-manager.ts +45 -0
  153. package/src/common/tool-invocation-registry.ts +124 -0
  154. package/src/common/types.ts +18 -0
  155. package/src/node/anthropic/anthropic-language-model.ts +96 -0
  156. package/src/node/index.ts +24 -0
  157. package/src/node/mcp/sumi-mcp-server.ts +161 -0
  158. package/src/node/mcp-server-manager-impl.ts +130 -0
  159. package/src/node/mcp-server.ts +118 -0
  160. package/src/node/openai/openai-language-model.ts +152 -0
  161. package/lib/browser/contrib/intelligent-completions/source/typing.source.d.ts +0 -9
  162. package/lib/browser/contrib/intelligent-completions/source/typing.source.d.ts.map +0 -1
  163. package/lib/browser/contrib/intelligent-completions/source/typing.source.js +0 -38
  164. package/lib/browser/contrib/intelligent-completions/source/typing.source.js.map +0 -1
  165. package/src/browser/contrib/intelligent-completions/source/typing.source.ts +0 -36
@@ -0,0 +1,161 @@
1
+ // 想要通过 MCP 的方式暴露 Opensumi 的 IDE 能力,就需要 Node.js 层打通 MCP 的通信
2
+ // 因为大部分 MCP 功能的实现在前端,因此需要再这里做前后端通信
3
+
4
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
5
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
6
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
7
+
8
+ import { Autowired, Injectable } from '@opensumi/di';
9
+ import { RPCService } from '@opensumi/ide-connection';
10
+
11
+ import { MCPServerManager } from '../../common/mcp-server-manager';
12
+ import { IMCPServerProxyService } from '../../common/types';
13
+ import { IMCPServer } from '../mcp-server';
14
+ import { MCPServerManagerImpl } from '../mcp-server-manager-impl';
15
+
16
+ // 每个 BrowserTab 都对应了一个 SumiMCPServerBackend 实例
17
+ // SumiMCPServerBackend 需要做的事情:
18
+ // 维护 Browser 端工具的注册和调用
19
+ // 处理第三方 MCP Server 的注册和调用
20
+
21
+ @Injectable({ multiple: true })
22
+ export class SumiMCPServerBackend extends RPCService<IMCPServerProxyService> {
23
+
24
+ // 这里需要考虑不同的 BrowserTab 的区分问题,目前的 POC 所有的 Tab 都会注册到 tools 中
25
+ // 后续需要区分不同的 Tab 对应的实例
26
+ @Autowired(MCPServerManager)
27
+ private readonly mcpServerManager: MCPServerManagerImpl;
28
+
29
+ private server: Server | undefined;
30
+
31
+ async getMCPTools() {
32
+ if (!this.client) {
33
+ throw new Error('SUMI MCP RPC Client not initialized');
34
+ }
35
+ // 获取 MCP 工具
36
+ const tools = await this.client.$getMCPTools();
37
+ console.log('[Node backend] SUMI MCP tools', tools);
38
+ return tools;
39
+ }
40
+
41
+ async callMCPTool(name: string, args: any) {
42
+ if (!this.client) {
43
+ throw new Error('SUMI MCP RPC Client not initialized');
44
+ }
45
+ return await this.client.$callMCPTool(name, args);
46
+ }
47
+
48
+ getServer() {
49
+ return this.server;
50
+ }
51
+
52
+ initBuiltinMCPServer() {
53
+ const builtinMCPServer = new BuiltinMCPServer(this);
54
+ this.mcpServerManager.initBuiltinServer(builtinMCPServer);
55
+ }
56
+
57
+ async initExposedMCPServer() {
58
+ // 初始化 MCP Server
59
+ this.server = new Server(
60
+ {
61
+ name: 'sumi-ide-mcp-server',
62
+ version: '0.2.0',
63
+ },
64
+ {
65
+ capabilities: {
66
+ tools: {},
67
+ },
68
+ },
69
+ );
70
+
71
+ // 设置工具列表请求处理器
72
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => {
73
+ const tools = await this.getMCPTools();
74
+ return { tools };
75
+ });
76
+
77
+ // 设置工具调用请求处理器
78
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
79
+ try {
80
+ const { name, arguments: args } = request.params;
81
+ return await this.callMCPTool(name, args);
82
+ } catch (error) {
83
+ const errorMessage = error instanceof Error ? error.message : String(error);
84
+ return {
85
+ content: [{ type: 'text', text: `Error: ${errorMessage}` }],
86
+ isError: true,
87
+ };
88
+ }
89
+ });
90
+
91
+ return this.server;
92
+ }
93
+ }
94
+
95
+
96
+ export const TokenBuiltinMCPServer = Symbol('TokenBuiltinMCPServer');
97
+
98
+ export class BuiltinMCPServer implements IMCPServer {
99
+
100
+ constructor(
101
+ private readonly sumiMCPServer: SumiMCPServerBackend,
102
+ ) {}
103
+
104
+ private started: boolean = true;
105
+
106
+ isStarted(): boolean {
107
+ return this.started;
108
+ }
109
+
110
+ getServerName(): string {
111
+ return 'sumi-builtin';
112
+ }
113
+
114
+ async start(): Promise<void> {
115
+ if (this.started) {
116
+ return;
117
+ }
118
+ // TODO 考虑 MCP Server 的对外暴露
119
+ // await this.sumiMCPServer.initMCPServer();
120
+ this.started = true;
121
+ }
122
+
123
+ async callTool(toolName: string, arg_string: string): Promise<any> {
124
+ if (!this.started) {
125
+ throw new Error('MCP Server not started');
126
+ }
127
+ let args;
128
+ try {
129
+ args = JSON.parse(arg_string);
130
+ } catch (error) {
131
+ console.error(
132
+ `Failed to parse arguments for calling tool "${toolName}" in Builtin MCP server.
133
+ Invalid JSON: ${arg_string}`,
134
+ error,
135
+ );
136
+ throw error;
137
+ }
138
+ return this.sumiMCPServer.callMCPTool(toolName, args);
139
+ }
140
+
141
+ async getTools(): ReturnType<Client['listTools']> {
142
+ if (!this.started) {
143
+ throw new Error('MCP Server not started');
144
+ }
145
+ const tools = await this.sumiMCPServer.getMCPTools();
146
+ return { tools };
147
+ }
148
+
149
+ update(_command: string, _args?: string[], _env?: { [key: string]: string }): void {
150
+ // No-op for builtin server as it doesn't need command/args/env updates
151
+ }
152
+
153
+ stop(): void {
154
+ if (!this.started) {
155
+ return;
156
+ }
157
+ // No explicit cleanup needed for in-memory server
158
+ this.started = false;
159
+ }
160
+ }
161
+
@@ -0,0 +1,130 @@
1
+ import { Autowired, Injectable } from '@opensumi/di';
2
+
3
+ import { MCPServerDescription, MCPServerManager, MCPTool } from '../common/mcp-server-manager';
4
+ import { ToolInvocationRegistry, ToolInvocationRegistryImpl, ToolRequest } from '../common/tool-invocation-registry';
5
+
6
+ import { BuiltinMCPServer } from './mcp/sumi-mcp-server';
7
+ import { IMCPServer, MCPServerImpl } from './mcp-server';
8
+
9
+ @Injectable()
10
+ export class MCPServerManagerImpl implements MCPServerManager {
11
+ @Autowired(ToolInvocationRegistry)
12
+ private readonly toolInvocationRegistry: ToolInvocationRegistryImpl;
13
+
14
+ protected servers: Map<string, IMCPServer> = new Map();
15
+
16
+ async stopServer(serverName: string): Promise<void> {
17
+ const server = this.servers.get(serverName);
18
+ if (!server) {
19
+ throw new Error(`MCP server "${serverName}" not found.`);
20
+ }
21
+ server.stop();
22
+ console.log(`MCP server "${serverName}" stopped.`);
23
+ }
24
+
25
+ async getStartedServers(): Promise<string[]> {
26
+ const startedServers: string[] = [];
27
+ for (const [name, server] of this.servers.entries()) {
28
+ if (server.isStarted()) {
29
+ startedServers.push(name);
30
+ }
31
+ }
32
+ return startedServers;
33
+ }
34
+
35
+ callTool(serverName: string, toolName: string, arg_string: string): ReturnType<IMCPServer['callTool']> {
36
+ const server = this.servers.get(serverName);
37
+ if (!server) {
38
+ throw new Error(`MCP server "${toolName}" not found.`);
39
+ }
40
+ return server.callTool(toolName, arg_string);
41
+ }
42
+
43
+ async startServer(serverName: string): Promise<void> {
44
+ const server = this.servers.get(serverName);
45
+ if (!server) {
46
+ throw new Error(`MCP server "${serverName}" not found.`);
47
+ }
48
+ await server.start();
49
+ }
50
+
51
+ async getServerNames(): Promise<string[]> {
52
+ return Array.from(this.servers.keys());
53
+ }
54
+
55
+ private convertToToolRequest(tool: MCPTool, serverName: string): ToolRequest {
56
+ const id = `mcp_${serverName}_${tool.name}`;
57
+
58
+ return {
59
+ id,
60
+ name: id,
61
+ providerName: `mcp_${serverName}`,
62
+ parameters: tool.inputSchema,
63
+ description: tool.description,
64
+ handler: async (arg_string: string) => {
65
+ try {
66
+ const res = await this.callTool(serverName, tool.name, arg_string);
67
+ console.log(`[MCP: ${serverName}] ${tool.name} called with ${arg_string}`);
68
+ console.log(res);
69
+ return JSON.stringify(res);
70
+ } catch (error) {
71
+ console.error(`Error in tool handler for ${tool.name} on MCP server ${serverName}:`, error);
72
+ throw error;
73
+ }
74
+ },
75
+ };
76
+ }
77
+
78
+ public async collectTools(serverName: string): Promise<void> {
79
+ const server = this.servers.get(serverName);
80
+ if (!server) {
81
+ throw new Error(`MCP server "${serverName}" not found.`);
82
+ }
83
+
84
+ const { tools } = await server.getTools();
85
+ const toolRequests: ToolRequest[] = tools.map((tool) => this.convertToToolRequest(tool, serverName));
86
+
87
+ for (const toolRequest of toolRequests) {
88
+ this.toolInvocationRegistry.registerTool(toolRequest);
89
+ }
90
+ }
91
+
92
+ public async getTools(serverName: string): ReturnType<IMCPServer['getTools']> {
93
+ const server = this.servers.get(serverName);
94
+ if (!server) {
95
+ throw new Error(`MCP server "${serverName}" not found.`);
96
+ }
97
+ return server.getTools();
98
+ }
99
+
100
+ addOrUpdateServer(description: MCPServerDescription): void {
101
+ const { name, command, args, env } = description;
102
+ const existingServer = this.servers.get(name);
103
+
104
+ if (existingServer) {
105
+ existingServer.update(command, args, env);
106
+ } else {
107
+ const newServer = new MCPServerImpl(name, command, args, env);
108
+ this.servers.set(name, newServer);
109
+ }
110
+ }
111
+
112
+ addOrUpdateServerDirectly(server: IMCPServer): void {
113
+ this.servers.set(server.getServerName(), server);
114
+ }
115
+
116
+ initBuiltinServer(builtinMCPServer: BuiltinMCPServer): void {
117
+ this.addOrUpdateServerDirectly(builtinMCPServer);
118
+ this.collectTools(builtinMCPServer.getServerName());
119
+ }
120
+
121
+ removeServer(name: string): void {
122
+ const server = this.servers.get(name);
123
+ if (server) {
124
+ server.stop();
125
+ this.servers.delete(name);
126
+ } else {
127
+ console.warn(`MCP server "${name}" not found.`);
128
+ }
129
+ }
130
+ }
@@ -0,0 +1,118 @@
1
+ // have to import with extension since the exports map is ./* -> ./dist/cjs/*
2
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
3
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
4
+
5
+ export interface IMCPServer {
6
+ isStarted(): boolean;
7
+ start(): Promise<void>;
8
+ getServerName(): string;
9
+ callTool(toolName: string, arg_string: string): ReturnType<Client['callTool']>;
10
+ getTools(): ReturnType<Client['listTools']>;
11
+ update(command: string, args?: string[], env?: { [key: string]: string }): void;
12
+ stop(): void;
13
+ }
14
+
15
+ export class MCPServerImpl implements IMCPServer {
16
+ private name: string;
17
+ private command: string;
18
+ private args?: string[];
19
+ private client: Client;
20
+ private env?: { [key: string]: string };
21
+ private started: boolean = false;
22
+
23
+ constructor(name: string, command: string, args?: string[], env?: Record<string, string>) {
24
+ this.name = name;
25
+ this.command = command;
26
+ this.args = args;
27
+ this.env = env;
28
+ }
29
+
30
+ isStarted(): boolean {
31
+ return this.started;
32
+ }
33
+
34
+ getServerName(): string {
35
+ return this.name;
36
+ }
37
+
38
+ async start(): Promise<void> {
39
+ if (this.started) {
40
+ return;
41
+ }
42
+ console.log(
43
+ `Starting server "${this.name}" with command: ${this.command} and args: ${this.args?.join(
44
+ ' ',
45
+ )} and env: ${JSON.stringify(this.env)}`,
46
+ );
47
+ // Filter process.env to exclude undefined values
48
+ const sanitizedEnv: Record<string, string> = Object.fromEntries(
49
+ Object.entries(process.env).filter((entry): entry is [string, string] => entry[1] !== undefined),
50
+ );
51
+
52
+ const mergedEnv: Record<string, string> = {
53
+ ...sanitizedEnv,
54
+ ...(this.env || {}),
55
+ };
56
+ const transport = new StdioClientTransport({
57
+ command: this.command,
58
+ args: this.args,
59
+ env: mergedEnv,
60
+ });
61
+ transport.onerror = (error) => {
62
+ console.error('Error: ' + error);
63
+ };
64
+
65
+ this.client = new Client(
66
+ {
67
+ name: 'opensumi-mcp-client',
68
+ version: '1.0.0',
69
+ },
70
+ {
71
+ capabilities: {},
72
+ },
73
+ );
74
+ this.client.onerror = (error) => {
75
+ console.error('Error in MCP client: ' + error);
76
+ };
77
+
78
+ await this.client.connect(transport);
79
+ this.started = true;
80
+ }
81
+
82
+ async callTool(toolName: string, arg_string: string) {
83
+ let args;
84
+ try {
85
+ args = JSON.parse(arg_string);
86
+ } catch (error) {
87
+ console.error(
88
+ `Failed to parse arguments for calling tool "${toolName}" in MCP server "${this.name}" with command "${this.command}".
89
+ Invalid JSON: ${arg_string}`,
90
+ error,
91
+ );
92
+ }
93
+ const params = {
94
+ name: toolName,
95
+ arguments: args,
96
+ };
97
+ return this.client.callTool(params);
98
+ }
99
+
100
+ async getTools() {
101
+ return await this.client.listTools();
102
+ }
103
+
104
+ update(command: string, args?: string[], env?: { [key: string]: string }): void {
105
+ this.command = command;
106
+ this.args = args;
107
+ this.env = env;
108
+ }
109
+
110
+ stop(): void {
111
+ if (!this.started || !this.client) {
112
+ return;
113
+ }
114
+ console.log(`Stopping MCP server "${this.name}"`);
115
+ this.client.close();
116
+ this.started = false;
117
+ }
118
+ }
@@ -0,0 +1,152 @@
1
+ import OpenAI from 'openai';
2
+ import { RunnableToolFunctionWithoutParse } from 'openai/lib/RunnableFunction';
3
+
4
+ import { Autowired, Injectable } from '@opensumi/di';
5
+ import { ChatReadableStream } from '@opensumi/ide-core-node';
6
+ import { CancellationToken } from '@opensumi/ide-utils';
7
+
8
+ import { ToolInvocationRegistry, ToolInvocationRegistryImpl, ToolRequest } from '../../common/tool-invocation-registry';
9
+
10
+ export const OpenAiModelIdentifier = Symbol('OpenAiModelIdentifier');
11
+
12
+ const apiKey = '';
13
+
14
+ @Injectable()
15
+ export class OpenAIModel {
16
+ @Autowired(ToolInvocationRegistry)
17
+ private readonly toolInvocationRegistry: ToolInvocationRegistryImpl;
18
+
19
+ protected initializeOpenAi(): OpenAI {
20
+ if (!apiKey) {
21
+ throw new Error('Please provide ANTHROPIC_API_KEY in preferences or via environment variable');
22
+ }
23
+
24
+ return new OpenAI({ apiKey: apiKey ?? 'no-key', baseURL: 'https://api.deepseek.com' });
25
+ }
26
+
27
+ async request(request: string, cancellationToken?: CancellationToken): Promise<any> {
28
+ return this.handleStreamingRequest(request, cancellationToken);
29
+ }
30
+
31
+ private createTool(tools: ToolRequest[]): RunnableToolFunctionWithoutParse[] {
32
+ return tools?.map(
33
+ (tool) =>
34
+ ({
35
+ type: 'function',
36
+ function: {
37
+ name: tool.name,
38
+ description: tool.description,
39
+ parameters: tool.parameters,
40
+ function: (args_string: string) => tool.handler(args_string),
41
+ },
42
+ } as RunnableToolFunctionWithoutParse),
43
+ );
44
+ }
45
+
46
+ private getCompletionContent(message: OpenAI.Chat.Completions.ChatCompletionToolMessageParam): string {
47
+ if (Array.isArray(message.content)) {
48
+ return message.content.join('');
49
+ }
50
+ return message.content;
51
+ }
52
+
53
+ protected async handleStreamingRequest(request: string, cancellationToken?: CancellationToken): Promise<any> {
54
+ const chatReadableStream = new ChatReadableStream();
55
+
56
+ const openai = this.initializeOpenAi();
57
+
58
+ const allFunctions = this.toolInvocationRegistry.getAllFunctions();
59
+
60
+ const tools = this.createTool(allFunctions);
61
+
62
+ const params = {
63
+ model: 'deepseek-chat',
64
+ messages: [{ role: 'user', content: request }],
65
+ stream: true,
66
+ tools,
67
+ tool_choice: 'auto',
68
+ } as any;
69
+ console.log('🚀 ~ OpenAIModel ~ params:', JSON.stringify(params, null, 2));
70
+
71
+ const runner = openai.beta.chat.completions.runTools(params) as any;
72
+
73
+ cancellationToken?.onCancellationRequested(() => {
74
+ runner.abort();
75
+ });
76
+
77
+ const runnerEnd = false;
78
+
79
+ // runner.on('error', error => {
80
+ // console.error('Error in OpenAI chat completion stream:', error);
81
+ // runnerEnd = true;
82
+ // resolve({ content: error.message });
83
+ // });
84
+ // // we need to also listen for the emitted errors, as otherwise any error actually thrown by the API will not be caught
85
+ // runner.emitted('error').then(error => {
86
+ // console.error('Error in OpenAI chat completion stream:', error);
87
+ // runnerEnd = true;
88
+ // resolve({ content: error.message });
89
+ // });
90
+ // runner.emitted('abort').then(() => {
91
+ // // do nothing, as the abort event is only emitted when the runner is aborted by us
92
+ // });
93
+ // runner.on('message', message => {
94
+ // if (message.tool_calls) {
95
+ // resolve({
96
+ // tool_calls: message.tool_calls.map((tool) => (
97
+ // {
98
+ // id: tool.id,
99
+ // type: tool.type,
100
+ // function: tool.function
101
+ // }
102
+ // ))
103
+ // });
104
+ // }
105
+ // });
106
+ runner.once('end', () => {
107
+ // runnerEnd = true;
108
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
109
+ // resolve(runner.finalChatCompletion as any);
110
+ chatReadableStream.end();
111
+ });
112
+
113
+ runner.on('chunk', (chunk) => {
114
+ if (chunk.choices[0]?.delta) {
115
+ const chunkData = { ...chunk.choices[0]?.delta };
116
+ // resolve(chunkData);
117
+
118
+ console.log('🚀 ~ OpenAIModel ~ chunkData:', chunkData);
119
+ if (chunkData.tool_calls) {
120
+ chatReadableStream.emitData({ kind: 'toolCall', content: chunkData.tool_calls[0] });
121
+ } else if (chunkData.content) {
122
+ chatReadableStream.emitData({ kind: 'content', content: chunkData.content });
123
+ }
124
+ }
125
+ });
126
+
127
+ // const asyncIterator = {
128
+ // async *[Symbol.asyncIterator](): AsyncIterator<any> {
129
+ // runner.on('chunk', chunk => {
130
+ // if (chunk.choices[0]?.delta) {
131
+ // const chunkData = { ...chunk.choices[0]?.delta };
132
+ // resolve(chunkData);
133
+
134
+ // if (chunkData.tool_calls) {
135
+ // chatReadableStream.emitData({ kind: 'toolCall', content: chunkData.tool_calls[0] });
136
+ // } else if (chunkData.content) {
137
+ // chatReadableStream.emitData({ kind: 'content', content: chunkData.content });
138
+ // }
139
+ // }
140
+ // });
141
+ // while (!runnerEnd) {
142
+ // const promise = new Promise<any>((res, rej) => {
143
+ // resolve = res;
144
+ // });
145
+ // yield promise;
146
+ // }
147
+ // }
148
+ // };
149
+ // return { stream: asyncIterator };
150
+ return chatReadableStream;
151
+ }
152
+ }
@@ -1,9 +0,0 @@
1
- import { IDisposable } from '@opensumi/ide-core-common';
2
- import { IModelContentChangedEvent, Position } from '@opensumi/ide-monaco';
3
- import { BaseCodeEditsSource } from './base';
4
- export declare class TypingCodeEditsSource extends BaseCodeEditsSource {
5
- priority: number;
6
- mount(): IDisposable;
7
- protected doTrigger(position: Position, data: IModelContentChangedEvent): Promise<void>;
8
- }
9
- //# sourceMappingURL=typing.source.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"typing.source.d.ts","sourceRoot":"","sources":["../../../../../src/browser/contrib/intelligent-completions/source/typing.source.ts"],"names":[],"mappings":"AACA,OAAO,EAAqD,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC3G,OAAO,EAAE,yBAAyB,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAE3E,OAAO,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAE7C,qBACa,qBAAsB,SAAQ,mBAAmB;IACrD,QAAQ,SAAK;IAEb,KAAK,IAAI,WAAW;cAYX,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,yBAAyB;CAa9E"}
@@ -1,38 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TypingCodeEditsSource = void 0;
4
- const tslib_1 = require("tslib");
5
- const di_1 = require("@opensumi/di");
6
- const ide_core_common_1 = require("@opensumi/ide-core-common");
7
- const base_1 = require("./base");
8
- let TypingCodeEditsSource = class TypingCodeEditsSource extends base_1.BaseCodeEditsSource {
9
- constructor() {
10
- super(...arguments);
11
- this.priority = 0;
12
- }
13
- mount() {
14
- this.addDispose(this.monacoEditor.onDidChangeModelContent((event) => {
15
- const position = this.monacoEditor.getPosition();
16
- if (position) {
17
- this.doTrigger(position, event);
18
- }
19
- }));
20
- return this;
21
- }
22
- async doTrigger(position, data) {
23
- const isTypingEnabled = this.preferenceService.getValid(ide_core_common_1.AINativeSettingSectionsId.CodeEditsTyping, false);
24
- if (!isTypingEnabled || !this.model) {
25
- return;
26
- }
27
- this.setBean({
28
- typing: ide_core_common_1.ECodeEditsSourceTyping.Typing,
29
- position,
30
- data,
31
- });
32
- }
33
- };
34
- exports.TypingCodeEditsSource = TypingCodeEditsSource;
35
- exports.TypingCodeEditsSource = TypingCodeEditsSource = tslib_1.__decorate([
36
- (0, di_1.Injectable)({ multiple: true })
37
- ], TypingCodeEditsSource);
38
- //# sourceMappingURL=typing.source.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"typing.source.js","sourceRoot":"","sources":["../../../../../src/browser/contrib/intelligent-completions/source/typing.source.ts"],"names":[],"mappings":";;;;AAAA,qCAA0C;AAC1C,+DAA2G;AAG3G,iCAA6C;AAGtC,IAAM,qBAAqB,GAA3B,MAAM,qBAAsB,SAAQ,0BAAmB;IAAvD;;QACE,aAAQ,GAAG,CAAC,CAAC;IA2BtB,CAAC;IAzBQ,KAAK;QACV,IAAI,CAAC,UAAU,CACb,IAAI,CAAC,YAAY,CAAC,uBAAuB,CAAC,CAAC,KAAgC,EAAE,EAAE;YAC7E,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC;YACjD,IAAI,QAAQ,EAAE,CAAC;gBACb,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YAClC,CAAC;QACH,CAAC,CAAC,CACH,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IAES,KAAK,CAAC,SAAS,CAAC,QAAkB,EAAE,IAA+B;QAC3E,MAAM,eAAe,GAAG,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,2CAAyB,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;QAE1G,IAAI,CAAC,eAAe,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YACpC,OAAO;QACT,CAAC;QAED,IAAI,CAAC,OAAO,CAAC;YACX,MAAM,EAAE,wCAAsB,CAAC,MAAM;YACrC,QAAQ;YACR,IAAI;SACL,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AA5BY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,eAAU,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;GAClB,qBAAqB,CA4BjC"}
@@ -1,36 +0,0 @@
1
- import { Injectable } from '@opensumi/di';
2
- import { AINativeSettingSectionsId, ECodeEditsSourceTyping, IDisposable } from '@opensumi/ide-core-common';
3
- import { IModelContentChangedEvent, Position } from '@opensumi/ide-monaco';
4
-
5
- import { BaseCodeEditsSource } from './base';
6
-
7
- @Injectable({ multiple: true })
8
- export class TypingCodeEditsSource extends BaseCodeEditsSource {
9
- public priority = 0;
10
-
11
- public mount(): IDisposable {
12
- this.addDispose(
13
- this.monacoEditor.onDidChangeModelContent((event: IModelContentChangedEvent) => {
14
- const position = this.monacoEditor.getPosition();
15
- if (position) {
16
- this.doTrigger(position, event);
17
- }
18
- }),
19
- );
20
- return this;
21
- }
22
-
23
- protected async doTrigger(position: Position, data: IModelContentChangedEvent) {
24
- const isTypingEnabled = this.preferenceService.getValid(AINativeSettingSectionsId.CodeEditsTyping, false);
25
-
26
- if (!isTypingEnabled || !this.model) {
27
- return;
28
- }
29
-
30
- this.setBean({
31
- typing: ECodeEditsSourceTyping.Typing,
32
- position,
33
- data,
34
- });
35
- }
36
- }