tabby-ai-assistant 1.0.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/README.md +232 -0
- package/dist/components/chat/chat-input.component.d.ts +65 -0
- package/dist/components/chat/chat-interface.component.d.ts +71 -0
- package/dist/components/chat/chat-message.component.d.ts +53 -0
- package/dist/components/chat/chat-settings.component.d.ts +62 -0
- package/dist/components/common/error-message.component.d.ts +11 -0
- package/dist/components/common/loading-spinner.component.d.ts +4 -0
- package/dist/components/security/consent-dialog.component.d.ts +11 -0
- package/dist/components/security/password-prompt.component.d.ts +10 -0
- package/dist/components/security/risk-confirm-dialog.component.d.ts +36 -0
- package/dist/components/settings/ai-settings-tab.component.d.ts +72 -0
- package/dist/components/settings/general-settings.component.d.ts +60 -0
- package/dist/components/settings/provider-config.component.d.ts +182 -0
- package/dist/components/settings/security-settings.component.d.ts +23 -0
- package/dist/components/terminal/ai-toolbar-button.component.d.ts +10 -0
- package/dist/components/terminal/command-preview.component.d.ts +15 -0
- package/dist/components/terminal/command-suggestion.component.d.ts +16 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +2 -0
- package/dist/index.js.LICENSE.txt +18 -0
- package/dist/main.d.ts +8 -0
- package/dist/providers/tabby/ai-config.provider.d.ts +18 -0
- package/dist/providers/tabby/ai-hotkey.provider.d.ts +21 -0
- package/dist/providers/tabby/ai-settings-tab.provider.d.ts +11 -0
- package/dist/providers/tabby/ai-toolbar-button.provider.d.ts +17 -0
- package/dist/services/chat/chat-history.service.d.ts +67 -0
- package/dist/services/chat/chat-session.service.d.ts +58 -0
- package/dist/services/chat/command-generator.service.d.ts +49 -0
- package/dist/services/core/ai-assistant.service.d.ts +88 -0
- package/dist/services/core/ai-provider-manager.service.d.ts +119 -0
- package/dist/services/core/config-provider.service.d.ts +137 -0
- package/dist/services/core/logger.service.d.ts +21 -0
- package/dist/services/providers/anthropic-provider.service.d.ts +39 -0
- package/dist/services/providers/base-provider.service.d.ts +137 -0
- package/dist/services/providers/glm-provider.service.d.ts +91 -0
- package/dist/services/providers/minimax-provider.service.d.ts +93 -0
- package/dist/services/providers/openai-compatible.service.d.ts +39 -0
- package/dist/services/providers/openai-provider.service.d.ts +38 -0
- package/dist/services/security/consent-manager.service.d.ts +65 -0
- package/dist/services/security/password-manager.service.d.ts +67 -0
- package/dist/services/security/risk-assessment.service.d.ts +65 -0
- package/dist/services/security/security-validator.service.d.ts +36 -0
- package/dist/services/terminal/command-analyzer.service.d.ts +20 -0
- package/dist/services/terminal/context-menu.service.d.ts +24 -0
- package/dist/services/terminal/hotkey.service.d.ts +28 -0
- package/dist/services/terminal/terminal-context.service.d.ts +100 -0
- package/dist/types/ai.types.d.ts +107 -0
- package/dist/types/provider.types.d.ts +105 -0
- package/dist/types/security.types.d.ts +85 -0
- package/dist/types/terminal.types.d.ts +150 -0
- package/dist/utils/encryption.utils.d.ts +83 -0
- package/dist/utils/formatting.utils.d.ts +106 -0
- package/dist/utils/validation.utils.d.ts +83 -0
- package/integration-test-output.txt +50 -0
- package/integration-tests/api-integration.test.ts +183 -0
- package/jest.config.js +47 -0
- package/package.json +73 -0
- package/setup-jest.ts +37 -0
- package/src/components/chat/chat-input.component.html +61 -0
- package/src/components/chat/chat-input.component.scss +183 -0
- package/src/components/chat/chat-input.component.ts +149 -0
- package/src/components/chat/chat-interface.component.html +119 -0
- package/src/components/chat/chat-interface.component.scss +354 -0
- package/src/components/chat/chat-interface.component.ts +224 -0
- package/src/components/chat/chat-message.component.html +65 -0
- package/src/components/chat/chat-message.component.scss +178 -0
- package/src/components/chat/chat-message.component.ts +93 -0
- package/src/components/chat/chat-settings.component.html +132 -0
- package/src/components/chat/chat-settings.component.scss +172 -0
- package/src/components/chat/chat-settings.component.ts +168 -0
- package/src/components/common/error-message.component.ts +124 -0
- package/src/components/common/loading-spinner.component.ts +72 -0
- package/src/components/security/consent-dialog.component.ts +77 -0
- package/src/components/security/password-prompt.component.ts +79 -0
- package/src/components/security/risk-confirm-dialog.component.html +87 -0
- package/src/components/security/risk-confirm-dialog.component.scss +360 -0
- package/src/components/security/risk-confirm-dialog.component.ts +96 -0
- package/src/components/settings/ai-settings-tab.component.html +140 -0
- package/src/components/settings/ai-settings-tab.component.scss +371 -0
- package/src/components/settings/ai-settings-tab.component.ts +193 -0
- package/src/components/settings/general-settings.component.html +103 -0
- package/src/components/settings/general-settings.component.scss +285 -0
- package/src/components/settings/general-settings.component.ts +123 -0
- package/src/components/settings/provider-config.component.html +95 -0
- package/src/components/settings/provider-config.component.scss +60 -0
- package/src/components/settings/provider-config.component.ts +206 -0
- package/src/components/settings/security-settings.component.html +51 -0
- package/src/components/settings/security-settings.component.scss +66 -0
- package/src/components/settings/security-settings.component.ts +71 -0
- package/src/components/terminal/ai-toolbar-button.component.ts +49 -0
- package/src/components/terminal/command-preview.component.ts +185 -0
- package/src/components/terminal/command-suggestion.component.ts +128 -0
- package/src/index.ts +163 -0
- package/src/main.ts +16 -0
- package/src/providers/tabby/ai-config.provider.ts +70 -0
- package/src/providers/tabby/ai-hotkey.provider.ts +55 -0
- package/src/providers/tabby/ai-settings-tab.provider.ts +18 -0
- package/src/providers/tabby/ai-toolbar-button.provider.ts +49 -0
- package/src/services/chat/chat-history.service.ts +239 -0
- package/src/services/chat/chat-session.service.spec.ts +249 -0
- package/src/services/chat/chat-session.service.ts +180 -0
- package/src/services/chat/command-generator.service.ts +301 -0
- package/src/services/core/ai-assistant.service.ts +334 -0
- package/src/services/core/ai-provider-manager.service.ts +314 -0
- package/src/services/core/config-provider.service.ts +347 -0
- package/src/services/core/logger.service.ts +104 -0
- package/src/services/providers/anthropic-provider.service.ts +373 -0
- package/src/services/providers/base-provider.service.ts +369 -0
- package/src/services/providers/glm-provider.service.ts +467 -0
- package/src/services/providers/minimax-provider.service.ts +427 -0
- package/src/services/providers/openai-compatible.service.ts +394 -0
- package/src/services/providers/openai-provider.service.ts +376 -0
- package/src/services/security/consent-manager.service.ts +332 -0
- package/src/services/security/password-manager.service.ts +188 -0
- package/src/services/security/risk-assessment.service.ts +340 -0
- package/src/services/security/security-validator.service.ts +143 -0
- package/src/services/terminal/command-analyzer.service.ts +43 -0
- package/src/services/terminal/context-menu.service.ts +45 -0
- package/src/services/terminal/hotkey.service.ts +53 -0
- package/src/services/terminal/terminal-context.service.ts +317 -0
- package/src/styles/ai-assistant.scss +449 -0
- package/src/types/ai.types.ts +133 -0
- package/src/types/provider.types.ts +147 -0
- package/src/types/security.types.ts +103 -0
- package/src/types/terminal.types.ts +186 -0
- package/src/utils/encryption.utils.spec.ts +250 -0
- package/src/utils/encryption.utils.ts +271 -0
- package/src/utils/formatting.utils.ts +359 -0
- package/src/utils/validation.utils.spec.ts +225 -0
- package/src/utils/validation.utils.ts +314 -0
- package/tsconfig.json +45 -0
- package/webpack-docker.config.js +42 -0
- package/webpack.config.js +59 -0
- package/webpack.config.js.backup +57 -0
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import axios, { AxiosInstance } from 'axios';
|
|
3
|
+
import { BaseAiProvider } from './base-provider.service';
|
|
4
|
+
import { ProviderCapability, HealthStatus, ValidationResult } from '../../types/provider.types';
|
|
5
|
+
import { ChatRequest, ChatResponse, CommandRequest, CommandResponse, ExplainRequest, ExplainResponse, AnalysisRequest, AnalysisResponse, MessageRole } from '../../types/ai.types';
|
|
6
|
+
import { LoggerService } from '../core/logger.service';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* GLM (ChatGLM) AI提供商
|
|
10
|
+
* 基于Anthropic兼容API格式
|
|
11
|
+
*/
|
|
12
|
+
@Injectable()
|
|
13
|
+
export class GlmProviderService extends BaseAiProvider {
|
|
14
|
+
readonly name = 'glm';
|
|
15
|
+
readonly displayName = 'GLM (ChatGLM-4.6)';
|
|
16
|
+
readonly capabilities = [
|
|
17
|
+
ProviderCapability.CHAT,
|
|
18
|
+
ProviderCapability.COMMAND_GENERATION,
|
|
19
|
+
ProviderCapability.COMMAND_EXPLANATION,
|
|
20
|
+
ProviderCapability.FUNCTION_CALL,
|
|
21
|
+
ProviderCapability.STREAMING
|
|
22
|
+
];
|
|
23
|
+
readonly authConfig = {
|
|
24
|
+
type: 'bearer' as const,
|
|
25
|
+
credentials: {
|
|
26
|
+
apiKey: ''
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
private client: AxiosInstance | null = null;
|
|
31
|
+
|
|
32
|
+
constructor(logger: LoggerService) {
|
|
33
|
+
super(logger);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 配置提供商
|
|
38
|
+
*/
|
|
39
|
+
configure(config: any): void {
|
|
40
|
+
super.configure(config);
|
|
41
|
+
this.authConfig.credentials.apiKey = config.apiKey || '';
|
|
42
|
+
this.initializeClient();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 初始化HTTP客户端
|
|
47
|
+
*/
|
|
48
|
+
private initializeClient(): void {
|
|
49
|
+
if (!this.config?.apiKey) {
|
|
50
|
+
this.logger.warn('GLM API key not provided');
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
this.client = axios.create({
|
|
56
|
+
baseURL: this.getBaseURL(),
|
|
57
|
+
timeout: this.getTimeout(),
|
|
58
|
+
headers: {
|
|
59
|
+
'Authorization': `Bearer ${this.config.apiKey}`,
|
|
60
|
+
'Content-Type': 'application/json'
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// 添加请求拦截器
|
|
65
|
+
this.client.interceptors.request.use(
|
|
66
|
+
(config) => {
|
|
67
|
+
this.logger.debug('GLM API request', {
|
|
68
|
+
url: config.url,
|
|
69
|
+
method: config.method,
|
|
70
|
+
data: this.sanitizeRequest(config.data)
|
|
71
|
+
});
|
|
72
|
+
return config;
|
|
73
|
+
},
|
|
74
|
+
(error) => {
|
|
75
|
+
this.logger.error('GLM API request error', error);
|
|
76
|
+
return Promise.reject(error);
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// 添加响应拦截器
|
|
81
|
+
this.client.interceptors.response.use(
|
|
82
|
+
(response) => {
|
|
83
|
+
this.logger.debug('GLM API response', {
|
|
84
|
+
status: response.status,
|
|
85
|
+
data: this.sanitizeResponse(response.data)
|
|
86
|
+
});
|
|
87
|
+
return response;
|
|
88
|
+
},
|
|
89
|
+
(error) => {
|
|
90
|
+
this.logger.error('GLM API response error', error);
|
|
91
|
+
return Promise.reject(error);
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
this.logger.info('GLM client initialized', {
|
|
96
|
+
baseURL: this.getBaseURL(),
|
|
97
|
+
model: this.config.model || 'glm-4.6'
|
|
98
|
+
});
|
|
99
|
+
} catch (error) {
|
|
100
|
+
this.logger.error('Failed to initialize GLM client', error);
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 聊天功能
|
|
107
|
+
*/
|
|
108
|
+
async chat(request: ChatRequest): Promise<ChatResponse> {
|
|
109
|
+
if (!this.client) {
|
|
110
|
+
throw new Error('GLM client not initialized');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
this.logRequest(request);
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const response = await this.withRetry(async () => {
|
|
117
|
+
const result = await this.client!.post('/v1/messages', {
|
|
118
|
+
model: this.config?.model || 'glm-4.6',
|
|
119
|
+
max_tokens: request.maxTokens || 1000,
|
|
120
|
+
system: request.systemPrompt || this.getDefaultSystemPrompt(),
|
|
121
|
+
messages: this.transformMessages(request.messages),
|
|
122
|
+
temperature: request.temperature || 0.95,
|
|
123
|
+
stream: request.stream || false
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
this.logResponse(result.data);
|
|
127
|
+
return result.data;
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
return this.transformChatResponse(response);
|
|
131
|
+
|
|
132
|
+
} catch (error) {
|
|
133
|
+
this.logError(error, { request });
|
|
134
|
+
throw new Error(`GLM chat failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* 生成命令
|
|
140
|
+
*/
|
|
141
|
+
async generateCommand(request: CommandRequest): Promise<CommandResponse> {
|
|
142
|
+
const prompt = this.buildCommandPrompt(request);
|
|
143
|
+
|
|
144
|
+
const chatRequest: ChatRequest = {
|
|
145
|
+
messages: [
|
|
146
|
+
{
|
|
147
|
+
id: this.generateId(),
|
|
148
|
+
role: MessageRole.USER,
|
|
149
|
+
content: prompt,
|
|
150
|
+
timestamp: new Date()
|
|
151
|
+
}
|
|
152
|
+
],
|
|
153
|
+
maxTokens: 500,
|
|
154
|
+
temperature: 0.3
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const response = await this.chat(chatRequest);
|
|
158
|
+
return this.parseCommandResponse(response.message.content);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* 解释命令
|
|
163
|
+
*/
|
|
164
|
+
async explainCommand(request: ExplainRequest): Promise<ExplainResponse> {
|
|
165
|
+
const prompt = this.buildExplainPrompt(request);
|
|
166
|
+
|
|
167
|
+
const chatRequest: ChatRequest = {
|
|
168
|
+
messages: [
|
|
169
|
+
{
|
|
170
|
+
id: this.generateId(),
|
|
171
|
+
role: MessageRole.USER,
|
|
172
|
+
content: prompt,
|
|
173
|
+
timestamp: new Date()
|
|
174
|
+
}
|
|
175
|
+
],
|
|
176
|
+
maxTokens: 1000,
|
|
177
|
+
temperature: 0.5
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const response = await this.chat(chatRequest);
|
|
181
|
+
return this.parseExplainResponse(response.message.content);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* 分析结果
|
|
186
|
+
*/
|
|
187
|
+
async analyzeResult(request: AnalysisRequest): Promise<AnalysisResponse> {
|
|
188
|
+
const prompt = this.buildAnalysisPrompt(request);
|
|
189
|
+
|
|
190
|
+
const chatRequest: ChatRequest = {
|
|
191
|
+
messages: [
|
|
192
|
+
{
|
|
193
|
+
id: this.generateId(),
|
|
194
|
+
role: MessageRole.USER,
|
|
195
|
+
content: prompt,
|
|
196
|
+
timestamp: new Date()
|
|
197
|
+
}
|
|
198
|
+
],
|
|
199
|
+
maxTokens: 1000,
|
|
200
|
+
temperature: 0.7
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const response = await this.chat(chatRequest);
|
|
204
|
+
return this.parseAnalysisResponse(response.message.content);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* 健康检查
|
|
209
|
+
*/
|
|
210
|
+
async healthCheck(): Promise<HealthStatus> {
|
|
211
|
+
try {
|
|
212
|
+
if (!this.client) {
|
|
213
|
+
return HealthStatus.UNHEALTHY;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// 简单的测试请求
|
|
217
|
+
const response = await this.client.post('/v1/messages', {
|
|
218
|
+
model: this.config?.model || 'glm-4.6',
|
|
219
|
+
max_tokens: 1,
|
|
220
|
+
messages: [
|
|
221
|
+
{
|
|
222
|
+
role: 'user',
|
|
223
|
+
content: [{ type: 'text', text: 'Hi' }]
|
|
224
|
+
}
|
|
225
|
+
]
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
if (response.status === 200) {
|
|
229
|
+
this.lastHealthCheck = { status: HealthStatus.HEALTHY, timestamp: new Date() };
|
|
230
|
+
return HealthStatus.HEALTHY;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return HealthStatus.DEGRADED;
|
|
234
|
+
|
|
235
|
+
} catch (error) {
|
|
236
|
+
this.logger.error('GLM health check failed', error);
|
|
237
|
+
this.lastHealthCheck = { status: HealthStatus.UNHEALTHY, timestamp: new Date() };
|
|
238
|
+
return HealthStatus.UNHEALTHY;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* 验证配置
|
|
244
|
+
*/
|
|
245
|
+
validateConfig(): ValidationResult {
|
|
246
|
+
const result = super.validateConfig();
|
|
247
|
+
|
|
248
|
+
if (!this.config?.apiKey) {
|
|
249
|
+
return {
|
|
250
|
+
valid: false,
|
|
251
|
+
errors: [...(result.errors || []), 'GLM API key is required']
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// 验证支持的模型
|
|
256
|
+
const supportedModels = ['glm-4.6'];
|
|
257
|
+
if (this.config.model && !supportedModels.includes(this.config.model)) {
|
|
258
|
+
result.warnings = [
|
|
259
|
+
...(result.warnings || []),
|
|
260
|
+
`Model ${this.config.model} might not be supported. Supported models: ${supportedModels.join(', ')}`
|
|
261
|
+
];
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return result;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* 获取默认基础URL
|
|
269
|
+
* GLM提供与Anthropic兼容的API端点
|
|
270
|
+
*/
|
|
271
|
+
protected getDefaultBaseURL(): string {
|
|
272
|
+
return 'https://open.bigmodel.cn/api/anthropic';
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* 转换消息格式(Anthropic兼容)
|
|
277
|
+
*/
|
|
278
|
+
protected transformMessages(messages: any[]): any[] {
|
|
279
|
+
return messages.map(msg => ({
|
|
280
|
+
role: msg.role,
|
|
281
|
+
content: [{ type: 'text', text: msg.content }]
|
|
282
|
+
}));
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* 转换聊天响应
|
|
287
|
+
*/
|
|
288
|
+
private transformChatResponse(response: any): ChatResponse {
|
|
289
|
+
const content = response.content[0];
|
|
290
|
+
const text = content.type === 'text' ? content.text : '';
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
message: {
|
|
294
|
+
id: this.generateId(),
|
|
295
|
+
role: MessageRole.ASSISTANT,
|
|
296
|
+
content: text,
|
|
297
|
+
timestamp: new Date()
|
|
298
|
+
},
|
|
299
|
+
usage: response.usage ? {
|
|
300
|
+
promptTokens: response.usage.input_tokens || 0,
|
|
301
|
+
completionTokens: response.usage.output_tokens || 0,
|
|
302
|
+
totalTokens: (response.usage.input_tokens || 0) + (response.usage.output_tokens || 0)
|
|
303
|
+
} : undefined
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* 构建命令生成提示
|
|
309
|
+
*/
|
|
310
|
+
private buildCommandPrompt(request: CommandRequest): string {
|
|
311
|
+
let prompt = `请将以下自然语言描述转换为准确的终端命令:\n\n"${request.naturalLanguage}"\n\n`;
|
|
312
|
+
|
|
313
|
+
if (request.context) {
|
|
314
|
+
prompt += `当前环境:\n`;
|
|
315
|
+
if (request.context.currentDirectory) {
|
|
316
|
+
prompt += `- 当前目录:${request.context.currentDirectory}\n`;
|
|
317
|
+
}
|
|
318
|
+
if (request.context.operatingSystem) {
|
|
319
|
+
prompt += `- 操作系统:${request.context.operatingSystem}\n`;
|
|
320
|
+
}
|
|
321
|
+
if (request.context.shell) {
|
|
322
|
+
prompt += `- Shell:${request.context.shell}\n`;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
prompt += `\n请直接返回JSON格式:\n`;
|
|
327
|
+
prompt += `{\n`;
|
|
328
|
+
prompt += ` "command": "具体命令",\n`;
|
|
329
|
+
prompt += ` "explanation": "命令解释",\n`;
|
|
330
|
+
prompt += ` "confidence": 0.95\n`;
|
|
331
|
+
prompt += `}\n`;
|
|
332
|
+
|
|
333
|
+
return prompt;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* 构建命令解释提示
|
|
338
|
+
*/
|
|
339
|
+
private buildExplainPrompt(request: ExplainRequest): string {
|
|
340
|
+
let prompt = `请详细解释以下终端命令:\n\n\`${request.command}\`\n\n`;
|
|
341
|
+
|
|
342
|
+
if (request.context?.currentDirectory) {
|
|
343
|
+
prompt += `当前目录:${request.context.currentDirectory}\n`;
|
|
344
|
+
}
|
|
345
|
+
if (request.context?.operatingSystem) {
|
|
346
|
+
prompt += `操作系统:${request.context.operatingSystem}\n`;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
prompt += `\n请按以下JSON格式返回:\n`;
|
|
350
|
+
prompt += `{\n`;
|
|
351
|
+
prompt += ` "explanation": "整体解释",\n`;
|
|
352
|
+
prompt += ` "breakdown": [\n`;
|
|
353
|
+
prompt += ` {"part": "命令部分", "description": "说明"}\n`;
|
|
354
|
+
prompt += ` ],\n`;
|
|
355
|
+
prompt += ` "examples": ["使用示例"]\n`;
|
|
356
|
+
prompt += `}\n`;
|
|
357
|
+
|
|
358
|
+
return prompt;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* 构建结果分析提示
|
|
363
|
+
*/
|
|
364
|
+
private buildAnalysisPrompt(request: AnalysisRequest): string {
|
|
365
|
+
let prompt = `请分析以下命令执行结果:\n\n`;
|
|
366
|
+
prompt += `命令:${request.command}\n`;
|
|
367
|
+
prompt += `退出码:${request.exitCode}\n`;
|
|
368
|
+
prompt += `输出:\n${request.output}\n\n`;
|
|
369
|
+
|
|
370
|
+
if (request.context?.workingDirectory) {
|
|
371
|
+
prompt += `工作目录:${request.context.workingDirectory}\n`;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
prompt += `\n请按以下JSON格式返回:\n`;
|
|
375
|
+
prompt += `{\n`;
|
|
376
|
+
prompt += ` "summary": "结果总结",\n`;
|
|
377
|
+
prompt += ` "insights": ["洞察1", "洞察2"],\n`;
|
|
378
|
+
prompt += ` "success": true/false,\n`;
|
|
379
|
+
prompt += ` "issues": [\n`;
|
|
380
|
+
prompt += ` {"severity": "warning|error|info", "message": "问题描述", "suggestion": "建议"}\n`;
|
|
381
|
+
prompt += ` ]\n`;
|
|
382
|
+
prompt += `}\n`;
|
|
383
|
+
|
|
384
|
+
return prompt;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* 解析命令响应
|
|
389
|
+
*/
|
|
390
|
+
private parseCommandResponse(content: string): CommandResponse {
|
|
391
|
+
try {
|
|
392
|
+
const match = content.match(/\{[\s\S]*\}/);
|
|
393
|
+
if (match) {
|
|
394
|
+
const parsed = JSON.parse(match[0]);
|
|
395
|
+
return {
|
|
396
|
+
command: parsed.command || '',
|
|
397
|
+
explanation: parsed.explanation || '',
|
|
398
|
+
confidence: parsed.confidence || 0.5
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
} catch (error) {
|
|
402
|
+
this.logger.warn('Failed to parse command response as JSON', error);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// 备用解析
|
|
406
|
+
const lines = content.split('\n').map(l => l.trim()).filter(l => l);
|
|
407
|
+
return {
|
|
408
|
+
command: lines[0] || '',
|
|
409
|
+
explanation: lines.slice(1).join(' ') || 'AI生成的命令',
|
|
410
|
+
confidence: 0.5
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* 解析解释响应
|
|
416
|
+
*/
|
|
417
|
+
private parseExplainResponse(content: string): ExplainResponse {
|
|
418
|
+
try {
|
|
419
|
+
const match = content.match(/\{[\s\S]*\}/);
|
|
420
|
+
if (match) {
|
|
421
|
+
const parsed = JSON.parse(match[0]);
|
|
422
|
+
return {
|
|
423
|
+
explanation: parsed.explanation || '',
|
|
424
|
+
breakdown: parsed.breakdown || [],
|
|
425
|
+
examples: parsed.examples || []
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
} catch (error) {
|
|
429
|
+
this.logger.warn('Failed to parse explain response as JSON', error);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return {
|
|
433
|
+
explanation: content,
|
|
434
|
+
breakdown: []
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* 解析分析响应
|
|
440
|
+
*/
|
|
441
|
+
private parseAnalysisResponse(content: string): AnalysisResponse {
|
|
442
|
+
try {
|
|
443
|
+
const match = content.match(/\{[\s\S]*\}/);
|
|
444
|
+
if (match) {
|
|
445
|
+
const parsed = JSON.parse(match[0]);
|
|
446
|
+
return {
|
|
447
|
+
summary: parsed.summary || '',
|
|
448
|
+
insights: parsed.insights || [],
|
|
449
|
+
success: parsed.success !== false,
|
|
450
|
+
issues: parsed.issues || []
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
} catch (error) {
|
|
454
|
+
this.logger.warn('Failed to parse analysis response as JSON', error);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return {
|
|
458
|
+
summary: content,
|
|
459
|
+
insights: [],
|
|
460
|
+
success: true
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
private getDefaultSystemPrompt(): string {
|
|
465
|
+
return `你是一个专业的终端命令助手,擅长将自然语言转换为准确的命令,并提供详细的解释。`;
|
|
466
|
+
}
|
|
467
|
+
}
|