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,104 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
@Injectable({ providedIn: 'root' })
|
|
4
|
+
export class LoggerService {
|
|
5
|
+
private logLevel: 'debug' | 'info' | 'warn' | 'error' = 'info';
|
|
6
|
+
private logs: Array<{ level: string; message: string; timestamp: Date; data?: any }> = [];
|
|
7
|
+
|
|
8
|
+
constructor() {
|
|
9
|
+
this.loadLogLevel();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
private loadLogLevel(): void {
|
|
13
|
+
// 从配置中加载日志级别
|
|
14
|
+
try {
|
|
15
|
+
const config = localStorage.getItem('ai-assistant-config');
|
|
16
|
+
if (config) {
|
|
17
|
+
const parsed = JSON.parse(config);
|
|
18
|
+
this.logLevel = parsed.logLevel || 'info';
|
|
19
|
+
}
|
|
20
|
+
} catch (e) {
|
|
21
|
+
// 忽略错误,使用默认级别
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
debug(message: string, data?: any): void {
|
|
26
|
+
if (this.shouldLog('debug')) {
|
|
27
|
+
this.log('DEBUG', message, data);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
info(message: string, data?: any): void {
|
|
32
|
+
if (this.shouldLog('info')) {
|
|
33
|
+
this.log('INFO', message, data);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
warn(message: string, data?: any): void {
|
|
38
|
+
if (this.shouldLog('warn')) {
|
|
39
|
+
this.log('WARN', message, data);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
error(message: string, error?: any): void {
|
|
44
|
+
if (this.shouldLog('error')) {
|
|
45
|
+
this.log('ERROR', message, error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private shouldLog(level: string): boolean {
|
|
50
|
+
const levels = ['debug', 'info', 'warn', 'error'];
|
|
51
|
+
const currentIndex = levels.indexOf(this.logLevel);
|
|
52
|
+
const messageIndex = levels.indexOf(level);
|
|
53
|
+
return messageIndex >= currentIndex;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private log(level: string, message: string, data?: any): void {
|
|
57
|
+
const entry = {
|
|
58
|
+
level,
|
|
59
|
+
message,
|
|
60
|
+
timestamp: new Date(),
|
|
61
|
+
data
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
this.logs.push(entry);
|
|
65
|
+
|
|
66
|
+
// 限制日志数量,避免内存溢出
|
|
67
|
+
if (this.logs.length > 1000) {
|
|
68
|
+
this.logs = this.logs.slice(-500);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 输出到控制台
|
|
72
|
+
switch (level) {
|
|
73
|
+
case 'DEBUG':
|
|
74
|
+
console.debug(`[AI Assistant] ${message}`, data);
|
|
75
|
+
break;
|
|
76
|
+
case 'INFO':
|
|
77
|
+
console.info(`[AI Assistant] ${message}`, data);
|
|
78
|
+
break;
|
|
79
|
+
case 'WARN':
|
|
80
|
+
console.warn(`[AI Assistant] ${message}`, data);
|
|
81
|
+
break;
|
|
82
|
+
case 'ERROR':
|
|
83
|
+
console.error(`[AI Assistant] ${message}`, data);
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
getLogs(): Array<{ level: string; message: string; timestamp: Date; data?: any }> {
|
|
89
|
+
return [...this.logs];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
clearLogs(): void {
|
|
93
|
+
this.logs = [];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
setLogLevel(level: 'debug' | 'info' | 'warn' | 'error'): void {
|
|
97
|
+
this.logLevel = level;
|
|
98
|
+
localStorage.setItem('ai-assistant-log-level', level);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
getLogLevel(): string {
|
|
102
|
+
return this.logLevel;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { Anthropic } from '@anthropic-ai/sdk';
|
|
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
|
+
* Anthropic Claude AI提供商
|
|
10
|
+
* 基于Anthropic Claude API
|
|
11
|
+
*/
|
|
12
|
+
@Injectable()
|
|
13
|
+
export class AnthropicProviderService extends BaseAiProvider {
|
|
14
|
+
readonly name = 'anthropic';
|
|
15
|
+
readonly displayName = 'Anthropic Claude';
|
|
16
|
+
readonly capabilities = [
|
|
17
|
+
ProviderCapability.CHAT,
|
|
18
|
+
ProviderCapability.COMMAND_GENERATION,
|
|
19
|
+
ProviderCapability.COMMAND_EXPLANATION,
|
|
20
|
+
ProviderCapability.REASONING,
|
|
21
|
+
ProviderCapability.STREAMING
|
|
22
|
+
];
|
|
23
|
+
readonly authConfig = {
|
|
24
|
+
type: 'bearer' as const,
|
|
25
|
+
credentials: {
|
|
26
|
+
apiKey: ''
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
private client: Anthropic | null = null;
|
|
31
|
+
|
|
32
|
+
constructor(logger: LoggerService) {
|
|
33
|
+
super(logger);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
configure(config: any): void {
|
|
37
|
+
super.configure(config);
|
|
38
|
+
this.authConfig.credentials.apiKey = config.apiKey || '';
|
|
39
|
+
this.initializeClient();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private initializeClient(): void {
|
|
43
|
+
if (!this.config?.apiKey) {
|
|
44
|
+
this.logger.warn('Anthropic API key not provided');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
this.client = new Anthropic({
|
|
50
|
+
apiKey: this.config.apiKey,
|
|
51
|
+
baseURL: this.getBaseURL()
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
this.logger.info('Anthropic client initialized', {
|
|
55
|
+
baseURL: this.getBaseURL(),
|
|
56
|
+
model: this.config.model || 'claude-3-sonnet'
|
|
57
|
+
});
|
|
58
|
+
} catch (error) {
|
|
59
|
+
this.logger.error('Failed to initialize Anthropic client', error);
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async chat(request: ChatRequest): Promise<ChatResponse> {
|
|
65
|
+
if (!this.client) {
|
|
66
|
+
throw new Error('Anthropic client not initialized');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
this.logRequest(request);
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const response = await this.withRetry(async () => {
|
|
73
|
+
const result = await this.client!.messages.create({
|
|
74
|
+
model: this.config?.model || 'claude-3-sonnet',
|
|
75
|
+
max_tokens: request.maxTokens || 1000,
|
|
76
|
+
system: request.systemPrompt || this.getDefaultSystemPrompt(),
|
|
77
|
+
messages: this.transformMessages(request.messages),
|
|
78
|
+
temperature: request.temperature || 1.0,
|
|
79
|
+
stream: request.stream || false
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
this.logResponse(result);
|
|
83
|
+
return result;
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return this.transformChatResponse(response);
|
|
87
|
+
|
|
88
|
+
} catch (error) {
|
|
89
|
+
this.logError(error, { request });
|
|
90
|
+
throw new Error(`Anthropic chat failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async generateCommand(request: CommandRequest): Promise<CommandResponse> {
|
|
95
|
+
const prompt = this.buildCommandPrompt(request);
|
|
96
|
+
|
|
97
|
+
const chatRequest: ChatRequest = {
|
|
98
|
+
messages: [
|
|
99
|
+
{
|
|
100
|
+
id: this.generateId(),
|
|
101
|
+
role: MessageRole.USER,
|
|
102
|
+
content: prompt,
|
|
103
|
+
timestamp: new Date()
|
|
104
|
+
}
|
|
105
|
+
],
|
|
106
|
+
maxTokens: 500,
|
|
107
|
+
temperature: 0.3
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const response = await this.chat(chatRequest);
|
|
111
|
+
return this.parseCommandResponse(response.message.content);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async explainCommand(request: ExplainRequest): Promise<ExplainResponse> {
|
|
115
|
+
const prompt = this.buildExplainPrompt(request);
|
|
116
|
+
|
|
117
|
+
const chatRequest: ChatRequest = {
|
|
118
|
+
messages: [
|
|
119
|
+
{
|
|
120
|
+
id: this.generateId(),
|
|
121
|
+
role: MessageRole.USER,
|
|
122
|
+
content: prompt,
|
|
123
|
+
timestamp: new Date()
|
|
124
|
+
}
|
|
125
|
+
],
|
|
126
|
+
maxTokens: 1000,
|
|
127
|
+
temperature: 0.5
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const response = await this.chat(chatRequest);
|
|
131
|
+
return this.parseExplainResponse(response.message.content);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async analyzeResult(request: AnalysisRequest): Promise<AnalysisResponse> {
|
|
135
|
+
const prompt = this.buildAnalysisPrompt(request);
|
|
136
|
+
|
|
137
|
+
const chatRequest: ChatRequest = {
|
|
138
|
+
messages: [
|
|
139
|
+
{
|
|
140
|
+
id: this.generateId(),
|
|
141
|
+
role: MessageRole.USER,
|
|
142
|
+
content: prompt,
|
|
143
|
+
timestamp: new Date()
|
|
144
|
+
}
|
|
145
|
+
],
|
|
146
|
+
maxTokens: 1000,
|
|
147
|
+
temperature: 0.7
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const response = await this.chat(chatRequest);
|
|
151
|
+
return this.parseAnalysisResponse(response.message.content);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async healthCheck(): Promise<HealthStatus> {
|
|
155
|
+
try {
|
|
156
|
+
if (!this.client) {
|
|
157
|
+
return HealthStatus.UNHEALTHY;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const response = await this.client.messages.create({
|
|
161
|
+
model: this.config?.model || 'claude-3-sonnet',
|
|
162
|
+
max_tokens: 1,
|
|
163
|
+
messages: [
|
|
164
|
+
{
|
|
165
|
+
role: 'user',
|
|
166
|
+
content: 'Hi'
|
|
167
|
+
}
|
|
168
|
+
]
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
this.lastHealthCheck = { status: HealthStatus.HEALTHY, timestamp: new Date() };
|
|
172
|
+
return HealthStatus.HEALTHY;
|
|
173
|
+
|
|
174
|
+
} catch (error) {
|
|
175
|
+
this.logger.error('Anthropic health check failed', error);
|
|
176
|
+
this.lastHealthCheck = { status: HealthStatus.UNHEALTHY, timestamp: new Date() };
|
|
177
|
+
return HealthStatus.UNHEALTHY;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
validateConfig(): ValidationResult {
|
|
182
|
+
const result = super.validateConfig();
|
|
183
|
+
|
|
184
|
+
if (!this.config?.apiKey) {
|
|
185
|
+
return {
|
|
186
|
+
valid: false,
|
|
187
|
+
errors: [...(result.errors || []), 'Anthropic API key is required']
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const supportedModels = ['claude-3-opus', 'claude-3-sonnet', 'claude-3-haiku'];
|
|
192
|
+
if (this.config.model && !supportedModels.includes(this.config.model)) {
|
|
193
|
+
result.warnings = [
|
|
194
|
+
...(result.warnings || []),
|
|
195
|
+
`Model ${this.config.model} might not be supported. Supported models: ${supportedModels.join(', ')}`
|
|
196
|
+
];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
protected getDefaultBaseURL(): string {
|
|
203
|
+
return 'https://api.anthropic.com';
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
protected transformMessages(messages: any[]): any[] {
|
|
207
|
+
return messages.map(msg => ({
|
|
208
|
+
role: msg.role,
|
|
209
|
+
content: [{ type: 'text', text: msg.content }]
|
|
210
|
+
}));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private transformChatResponse(response: any): ChatResponse {
|
|
214
|
+
const content = response.content[0];
|
|
215
|
+
const text = content.type === 'text' ? content.text : '';
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
message: {
|
|
219
|
+
id: this.generateId(),
|
|
220
|
+
role: MessageRole.ASSISTANT,
|
|
221
|
+
content: text,
|
|
222
|
+
timestamp: new Date()
|
|
223
|
+
},
|
|
224
|
+
usage: response.usage ? {
|
|
225
|
+
promptTokens: response.usage.input_tokens || 0,
|
|
226
|
+
completionTokens: response.usage.output_tokens || 0,
|
|
227
|
+
totalTokens: (response.usage.input_tokens || 0) + (response.usage.output_tokens || 0)
|
|
228
|
+
} : undefined
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private buildCommandPrompt(request: CommandRequest): string {
|
|
233
|
+
let prompt = `请将以下自然语言描述转换为准确的终端命令:\n\n"${request.naturalLanguage}"\n\n`;
|
|
234
|
+
|
|
235
|
+
if (request.context) {
|
|
236
|
+
prompt += `当前环境:\n`;
|
|
237
|
+
if (request.context.currentDirectory) {
|
|
238
|
+
prompt += `- 当前目录:${request.context.currentDirectory}\n`;
|
|
239
|
+
}
|
|
240
|
+
if (request.context.operatingSystem) {
|
|
241
|
+
prompt += `- 操作系统:${request.context.operatingSystem}\n`;
|
|
242
|
+
}
|
|
243
|
+
if (request.context.shell) {
|
|
244
|
+
prompt += `- Shell:${request.context.shell}\n`;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
prompt += `\n请直接返回JSON格式:\n`;
|
|
249
|
+
prompt += `{\n`;
|
|
250
|
+
prompt += ` "command": "具体命令",\n`;
|
|
251
|
+
prompt += ` "explanation": "命令解释",\n`;
|
|
252
|
+
prompt += ` "confidence": 0.95\n`;
|
|
253
|
+
prompt += `}\n`;
|
|
254
|
+
|
|
255
|
+
return prompt;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
private buildExplainPrompt(request: ExplainRequest): string {
|
|
259
|
+
let prompt = `请详细解释以下终端命令:\n\n\`${request.command}\`\n\n`;
|
|
260
|
+
|
|
261
|
+
if (request.context?.currentDirectory) {
|
|
262
|
+
prompt += `当前目录:${request.context.currentDirectory}\n`;
|
|
263
|
+
}
|
|
264
|
+
if (request.context?.operatingSystem) {
|
|
265
|
+
prompt += `操作系统:${request.context.operatingSystem}\n`;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
prompt += `\n请按以下JSON格式返回:\n`;
|
|
269
|
+
prompt += `{\n`;
|
|
270
|
+
prompt += ` "explanation": "整体解释",\n`;
|
|
271
|
+
prompt += ` "breakdown": [\n`;
|
|
272
|
+
prompt += ` {"part": "命令部分", "description": "说明"}\n`;
|
|
273
|
+
prompt += ` ],\n`;
|
|
274
|
+
prompt += ` "examples": ["使用示例"]\n`;
|
|
275
|
+
prompt += `}\n`;
|
|
276
|
+
|
|
277
|
+
return prompt;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
private buildAnalysisPrompt(request: AnalysisRequest): string {
|
|
281
|
+
let prompt = `请分析以下命令执行结果:\n\n`;
|
|
282
|
+
prompt += `命令:${request.command}\n`;
|
|
283
|
+
prompt += `退出码:${request.exitCode}\n`;
|
|
284
|
+
prompt += `输出:\n${request.output}\n\n`;
|
|
285
|
+
|
|
286
|
+
if (request.context?.workingDirectory) {
|
|
287
|
+
prompt += `工作目录:${request.context.workingDirectory}\n`;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
prompt += `\n请按以下JSON格式返回:\n`;
|
|
291
|
+
prompt += `{\n`;
|
|
292
|
+
prompt += ` "summary": "结果总结",\n`;
|
|
293
|
+
prompt += ` "insights": ["洞察1", "洞察2"],\n`;
|
|
294
|
+
prompt += ` "success": true/false,\n`;
|
|
295
|
+
prompt += ` "issues": [\n`;
|
|
296
|
+
prompt += ` {"severity": "warning|error|info", "message": "问题描述", "suggestion": "建议"}\n`;
|
|
297
|
+
prompt += ` ]\n`;
|
|
298
|
+
prompt += `}\n`;
|
|
299
|
+
|
|
300
|
+
return prompt;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
private parseCommandResponse(content: string): CommandResponse {
|
|
304
|
+
try {
|
|
305
|
+
const match = content.match(/\{[\s\S]*\}/);
|
|
306
|
+
if (match) {
|
|
307
|
+
const parsed = JSON.parse(match[0]);
|
|
308
|
+
return {
|
|
309
|
+
command: parsed.command || '',
|
|
310
|
+
explanation: parsed.explanation || '',
|
|
311
|
+
confidence: parsed.confidence || 0.5
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
} catch (error) {
|
|
315
|
+
this.logger.warn('Failed to parse command response as JSON', error);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const lines = content.split('\n').map(l => l.trim()).filter(l => l);
|
|
319
|
+
return {
|
|
320
|
+
command: lines[0] || '',
|
|
321
|
+
explanation: lines.slice(1).join(' ') || 'AI生成的命令',
|
|
322
|
+
confidence: 0.5
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
private parseExplainResponse(content: string): ExplainResponse {
|
|
327
|
+
try {
|
|
328
|
+
const match = content.match(/\{[\s\S]*\}/);
|
|
329
|
+
if (match) {
|
|
330
|
+
const parsed = JSON.parse(match[0]);
|
|
331
|
+
return {
|
|
332
|
+
explanation: parsed.explanation || '',
|
|
333
|
+
breakdown: parsed.breakdown || [],
|
|
334
|
+
examples: parsed.examples || []
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
} catch (error) {
|
|
338
|
+
this.logger.warn('Failed to parse explain response as JSON', error);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return {
|
|
342
|
+
explanation: content,
|
|
343
|
+
breakdown: []
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
private parseAnalysisResponse(content: string): AnalysisResponse {
|
|
348
|
+
try {
|
|
349
|
+
const match = content.match(/\{[\s\S]*\}/);
|
|
350
|
+
if (match) {
|
|
351
|
+
const parsed = JSON.parse(match[0]);
|
|
352
|
+
return {
|
|
353
|
+
summary: parsed.summary || '',
|
|
354
|
+
insights: parsed.insights || [],
|
|
355
|
+
success: parsed.success !== false,
|
|
356
|
+
issues: parsed.issues || []
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
} catch (error) {
|
|
360
|
+
this.logger.warn('Failed to parse analysis response as JSON', error);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return {
|
|
364
|
+
summary: content,
|
|
365
|
+
insights: [],
|
|
366
|
+
success: true
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
private getDefaultSystemPrompt(): string {
|
|
371
|
+
return `你是一个专业的终端命令助手,擅长将自然语言转换为准确的命令,并提供详细的解释。`;
|
|
372
|
+
}
|
|
373
|
+
}
|