tabby-ai-assistant 1.0.9 → 1.0.11
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/dist/index.js +1 -1
- package/package.json +3 -3
- package/src/components/chat/ai-sidebar.component.scss +43 -0
- package/src/components/chat/ai-sidebar.component.ts +43 -1
- package/src/components/settings/ai-settings-tab.component.html +13 -5
- package/src/components/settings/ai-settings-tab.component.scss +164 -33
- package/src/components/settings/ai-settings-tab.component.ts +1 -0
- package/src/components/settings/context-settings.component.html +87 -0
- package/src/components/settings/context-settings.component.scss +133 -0
- package/src/components/settings/context-settings.component.ts +91 -0
- package/src/components/settings/provider-config.component.html +46 -12
- package/src/components/settings/provider-config.component.scss +59 -0
- package/src/components/settings/provider-config.component.ts +112 -15
- package/src/i18n/translations/en-US.ts +27 -1
- package/src/i18n/translations/ja-JP.ts +27 -1
- package/src/i18n/translations/zh-CN.ts +27 -1
- package/src/i18n/types.ts +28 -0
- package/src/index.ts +6 -0
- package/src/services/chat/chat-session.service.ts +91 -2
- package/src/services/context/manager.ts +33 -2
- package/src/services/core/ai-assistant.service.ts +35 -11
- package/src/services/core/config-provider.service.ts +66 -0
- package/src/services/core/toast.service.ts +36 -0
- package/src/services/providers/anthropic-provider.service.ts +12 -4
- package/src/services/providers/glm-provider.service.ts +12 -4
- package/src/services/providers/minimax-provider.service.ts +12 -4
- package/src/types/ai.types.ts +3 -3
- package/src/types/provider.types.ts +1 -0
- package/dist/components/chat/ai-sidebar.component.d.ts +0 -160
- package/dist/components/chat/chat-input.component.d.ts +0 -69
- package/dist/components/chat/chat-interface.component.d.ts +0 -124
- package/dist/components/chat/chat-message.component.d.ts +0 -53
- package/dist/components/chat/chat-settings.component.d.ts +0 -72
- package/dist/components/common/error-message.component.d.ts +0 -11
- package/dist/components/common/loading-spinner.component.d.ts +0 -4
- package/dist/components/security/consent-dialog.component.d.ts +0 -11
- package/dist/components/security/password-prompt.component.d.ts +0 -10
- package/dist/components/security/risk-confirm-dialog.component.d.ts +0 -36
- package/dist/components/settings/ai-settings-tab.component.d.ts +0 -82
- package/dist/components/settings/general-settings.component.d.ts +0 -94
- package/dist/components/settings/provider-config.component.d.ts +0 -273
- package/dist/components/settings/security-settings.component.d.ts +0 -33
- package/dist/components/terminal/ai-toolbar-button.component.d.ts +0 -10
- package/dist/components/terminal/command-preview.component.d.ts +0 -53
- package/dist/components/terminal/command-suggestion.component.d.ts +0 -16
- package/dist/i18n/index.d.ts +0 -48
- package/dist/i18n/translations/en-US.d.ts +0 -5
- package/dist/i18n/translations/ja-JP.d.ts +0 -5
- package/dist/i18n/translations/zh-CN.d.ts +0 -5
- package/dist/i18n/types.d.ts +0 -198
- package/dist/index-full.d.ts +0 -8
- package/dist/index-minimal.d.ts +0 -3
- package/dist/index.d.ts +0 -12
- package/dist/main.d.ts +0 -8
- package/dist/providers/tabby/ai-config.provider.d.ts +0 -70
- package/dist/providers/tabby/ai-hotkey.provider.d.ts +0 -15
- package/dist/providers/tabby/ai-settings-tab.provider.d.ts +0 -11
- package/dist/providers/tabby/ai-toolbar-button.provider.d.ts +0 -16
- package/dist/services/chat/ai-sidebar.service.d.ts +0 -111
- package/dist/services/chat/chat-history.service.d.ts +0 -145
- package/dist/services/chat/chat-session.service.d.ts +0 -113
- package/dist/services/chat/command-generator.service.d.ts +0 -49
- package/dist/services/context/compaction.d.ts +0 -90
- package/dist/services/context/manager.d.ts +0 -69
- package/dist/services/context/memory.d.ts +0 -116
- package/dist/services/context/token-budget.d.ts +0 -105
- package/dist/services/core/ai-assistant.service.d.ts +0 -127
- package/dist/services/core/ai-provider-manager.service.d.ts +0 -119
- package/dist/services/core/checkpoint.service.d.ts +0 -130
- package/dist/services/core/config-provider.service.d.ts +0 -137
- package/dist/services/core/logger.service.d.ts +0 -21
- package/dist/services/core/theme.service.d.ts +0 -53
- package/dist/services/platform/escape-sequence.service.d.ts +0 -132
- package/dist/services/platform/platform-detection.service.d.ts +0 -146
- package/dist/services/providers/anthropic-provider.service.d.ts +0 -44
- package/dist/services/providers/base-provider.service.d.ts +0 -142
- package/dist/services/providers/glm-provider.service.d.ts +0 -96
- package/dist/services/providers/minimax-provider.service.d.ts +0 -102
- package/dist/services/providers/ollama-provider.service.d.ts +0 -76
- package/dist/services/providers/openai-compatible.service.d.ts +0 -44
- package/dist/services/providers/openai-provider.service.d.ts +0 -43
- package/dist/services/providers/vllm-provider.service.d.ts +0 -82
- package/dist/services/security/consent-manager.service.d.ts +0 -65
- package/dist/services/security/password-manager.service.d.ts +0 -67
- package/dist/services/security/risk-assessment.service.d.ts +0 -65
- package/dist/services/security/security-validator.service.d.ts +0 -36
- package/dist/services/terminal/buffer-analyzer.service.d.ts +0 -128
- package/dist/services/terminal/command-analyzer.service.d.ts +0 -20
- package/dist/services/terminal/context-menu.service.d.ts +0 -24
- package/dist/services/terminal/hotkey.service.d.ts +0 -28
- package/dist/services/terminal/terminal-context.service.d.ts +0 -100
- package/dist/services/terminal/terminal-manager.service.d.ts +0 -185
- package/dist/services/terminal/terminal-tools.service.d.ts +0 -79
- package/dist/types/ai.types.d.ts +0 -199
- package/dist/types/provider.types.d.ts +0 -105
- package/dist/types/security.types.d.ts +0 -85
- package/dist/types/terminal.types.d.ts +0 -150
- package/dist/utils/encryption.utils.d.ts +0 -83
- package/dist/utils/formatting.utils.d.ts +0 -106
- package/dist/utils/validation.utils.d.ts +0 -83
- package/jest.config.js +0 -47
- package/webpack-docker.config.js +0 -42
- package/webpack.config.js +0 -81
package/src/i18n/types.ts
CHANGED
|
@@ -31,6 +31,7 @@ export interface SettingsTranslations {
|
|
|
31
31
|
title: string;
|
|
32
32
|
generalTab: string;
|
|
33
33
|
providersTab: string;
|
|
34
|
+
contextTab: string;
|
|
34
35
|
securityTab: string;
|
|
35
36
|
chatTab: string;
|
|
36
37
|
advancedTab: string;
|
|
@@ -130,6 +131,8 @@ export interface ProvidersTranslations {
|
|
|
130
131
|
apiKey: string;
|
|
131
132
|
baseURL: string;
|
|
132
133
|
model: string;
|
|
134
|
+
contextWindow: string;
|
|
135
|
+
contextWindowDesc: string;
|
|
133
136
|
saveConfig: string;
|
|
134
137
|
testConnection: string;
|
|
135
138
|
detectService: string;
|
|
@@ -199,6 +202,30 @@ export interface AdvancedSettingsTranslations {
|
|
|
199
202
|
currentProvider: string;
|
|
200
203
|
}
|
|
201
204
|
|
|
205
|
+
// 上下文设置
|
|
206
|
+
export interface ContextSettingsTranslations {
|
|
207
|
+
title: string;
|
|
208
|
+
autoCompact: string;
|
|
209
|
+
enableAutoCompact: string;
|
|
210
|
+
autoCompactDesc: string;
|
|
211
|
+
autoCompactEnabled: string;
|
|
212
|
+
autoCompactDisabled: string;
|
|
213
|
+
tokenConfig: string;
|
|
214
|
+
currentProviderLimit: string;
|
|
215
|
+
maxContextTokens: string;
|
|
216
|
+
maxContextTokensDesc: string;
|
|
217
|
+
reservedOutputTokens: string;
|
|
218
|
+
reservedOutputTokensDesc: string;
|
|
219
|
+
thresholdConfig: string;
|
|
220
|
+
pruneThreshold: string;
|
|
221
|
+
pruneThresholdDesc: string;
|
|
222
|
+
compactThreshold: string;
|
|
223
|
+
compactThresholdDesc: string;
|
|
224
|
+
messagesToKeep: string;
|
|
225
|
+
messagesToKeepDesc: string;
|
|
226
|
+
configSaved: string;
|
|
227
|
+
}
|
|
228
|
+
|
|
202
229
|
// 主翻译类型
|
|
203
230
|
export interface TranslationKeys {
|
|
204
231
|
common: CommonTranslations;
|
|
@@ -211,6 +238,7 @@ export interface TranslationKeys {
|
|
|
211
238
|
chatInterface: ChatInterfaceTranslations;
|
|
212
239
|
riskLevel: RiskLevelTranslations;
|
|
213
240
|
advancedSettings: AdvancedSettingsTranslations;
|
|
241
|
+
contextSettings: ContextSettingsTranslations;
|
|
214
242
|
}
|
|
215
243
|
|
|
216
244
|
// 支持的语言类型
|
package/src/index.ts
CHANGED
|
@@ -59,6 +59,7 @@ import { PlatformDetectionService } from './services/platform/platform-detection
|
|
|
59
59
|
|
|
60
60
|
// Core Services
|
|
61
61
|
import { CheckpointManager } from './services/core/checkpoint.service';
|
|
62
|
+
import { ToastService } from './services/core/toast.service';
|
|
62
63
|
|
|
63
64
|
// Enhanced Terminal Services
|
|
64
65
|
import { BufferAnalyzerService } from './services/terminal/buffer-analyzer.service';
|
|
@@ -76,6 +77,7 @@ import { AiSettingsTabComponent } from './components/settings/ai-settings-tab.co
|
|
|
76
77
|
import { ProviderConfigComponent } from './components/settings/provider-config.component';
|
|
77
78
|
import { SecuritySettingsComponent } from './components/settings/security-settings.component';
|
|
78
79
|
import { GeneralSettingsComponent } from './components/settings/general-settings.component';
|
|
80
|
+
import { ContextSettingsComponent } from './components/settings/context-settings.component';
|
|
79
81
|
|
|
80
82
|
import { RiskConfirmDialogComponent } from './components/security/risk-confirm-dialog.component';
|
|
81
83
|
import { PasswordPromptComponent } from './components/security/password-prompt.component';
|
|
@@ -151,6 +153,9 @@ import { AiHotkeyProvider } from './providers/tabby/ai-hotkey.provider';
|
|
|
151
153
|
// Core Services
|
|
152
154
|
CheckpointManager,
|
|
153
155
|
|
|
156
|
+
// Toast Service
|
|
157
|
+
ToastService,
|
|
158
|
+
|
|
154
159
|
// Enhanced Terminal Services
|
|
155
160
|
BufferAnalyzerService,
|
|
156
161
|
|
|
@@ -173,6 +178,7 @@ import { AiHotkeyProvider } from './providers/tabby/ai-hotkey.provider';
|
|
|
173
178
|
ProviderConfigComponent,
|
|
174
179
|
SecuritySettingsComponent,
|
|
175
180
|
GeneralSettingsComponent,
|
|
181
|
+
ContextSettingsComponent,
|
|
176
182
|
|
|
177
183
|
// Security Components
|
|
178
184
|
RiskConfirmDialogComponent,
|
|
@@ -3,7 +3,9 @@ import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
|
|
3
3
|
import { ChatMessage, ChatRequest, ChatResponse, MessageRole, Checkpoint, ApiMessage } from '../../types/ai.types';
|
|
4
4
|
import { AiAssistantService } from '../core/ai-assistant.service';
|
|
5
5
|
import { LoggerService } from '../core/logger.service';
|
|
6
|
+
import { ConfigProviderService } from '../core/config-provider.service';
|
|
6
7
|
import { ChatHistoryService } from './chat-history.service';
|
|
8
|
+
import { ContextManager } from '../context/manager';
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* 聊天会话服务
|
|
@@ -27,7 +29,9 @@ export class ChatSessionService {
|
|
|
27
29
|
constructor(
|
|
28
30
|
private aiService: AiAssistantService,
|
|
29
31
|
private logger: LoggerService,
|
|
30
|
-
private chatHistoryService: ChatHistoryService
|
|
32
|
+
private chatHistoryService: ChatHistoryService,
|
|
33
|
+
private contextManager: ContextManager,
|
|
34
|
+
private configService: ConfigProviderService
|
|
31
35
|
) { }
|
|
32
36
|
|
|
33
37
|
/**
|
|
@@ -56,8 +60,10 @@ export class ChatSessionService {
|
|
|
56
60
|
if (!this.currentSessionId) {
|
|
57
61
|
this.createSession();
|
|
58
62
|
}
|
|
59
|
-
|
|
60
63
|
try {
|
|
64
|
+
// === 发送前检查并管理上下文 ===
|
|
65
|
+
await this.checkAndManageContext();
|
|
66
|
+
|
|
61
67
|
// 添加用户消息
|
|
62
68
|
const userMessage: ChatMessage = {
|
|
63
69
|
id: this.generateMessageId(),
|
|
@@ -450,4 +456,87 @@ export class ChatSessionService {
|
|
|
450
456
|
return sum + Math.ceil(content.length / 4);
|
|
451
457
|
}, 0);
|
|
452
458
|
}
|
|
459
|
+
|
|
460
|
+
// ==================== 上下文自动管理 ====================
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* 检查并管理上下文(自动压缩)
|
|
464
|
+
*/
|
|
465
|
+
private async checkAndManageContext(): Promise<void> {
|
|
466
|
+
if (!this.currentSessionId) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
// 检查自动压缩开关是否启用
|
|
470
|
+
if (!this.configService.isAutoCompactEnabled()) {
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
// 同步当前消息到 ChatHistoryService
|
|
474
|
+
this.syncMessagesToHistory();
|
|
475
|
+
// 检查是否需要管理上下文
|
|
476
|
+
if (this.contextManager.shouldManageContext(this.currentSessionId)) {
|
|
477
|
+
this.logger.info('Context management triggered', {
|
|
478
|
+
sessionId: this.currentSessionId
|
|
479
|
+
});
|
|
480
|
+
try {
|
|
481
|
+
const results = await this.contextManager.manageContext(this.currentSessionId);
|
|
482
|
+
// 记录压缩结果
|
|
483
|
+
if (results.compactionResult) {
|
|
484
|
+
this.logger.info('Context compacted', {
|
|
485
|
+
tokensSaved: results.compactionResult.tokensSaved,
|
|
486
|
+
summary: results.compactionResult.summary?.substring(0, 100)
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
if (results.pruneResult && results.pruneResult.pruned) {
|
|
490
|
+
this.logger.info('Context pruned', {
|
|
491
|
+
tokensSaved: results.pruneResult.tokensSaved,
|
|
492
|
+
partsCompacted: results.pruneResult.partsCompacted
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
if (results.truncationResult) {
|
|
496
|
+
this.logger.info('Context truncated', {
|
|
497
|
+
messagesRemoved: results.truncationResult.messagesRemoved
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
// 重新加载压缩后的消息
|
|
501
|
+
this.reloadMessagesFromHistory();
|
|
502
|
+
} catch (error) {
|
|
503
|
+
this.logger.error('Context management failed', {
|
|
504
|
+
error,
|
|
505
|
+
sessionId: this.currentSessionId
|
|
506
|
+
});
|
|
507
|
+
// 即使压缩失败,也继续发送消息
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* 同步当前消息到历史存储
|
|
514
|
+
*/
|
|
515
|
+
private syncMessagesToHistory(): void {
|
|
516
|
+
if (!this.currentSessionId) return;
|
|
517
|
+
const currentMessages = this.messagesSubject.value;
|
|
518
|
+
// 将当前会话保存到历史
|
|
519
|
+
this.chatHistoryService.saveSession(this.currentSessionId, currentMessages);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* 从历史存储重新加载消息(压缩后)
|
|
524
|
+
*/
|
|
525
|
+
private reloadMessagesFromHistory(): void {
|
|
526
|
+
if (!this.currentSessionId) return;
|
|
527
|
+
// 获取有效历史(已被压缩过滤)
|
|
528
|
+
const effectiveMessages = this.contextManager.getEffectiveHistory(this.currentSessionId);
|
|
529
|
+
// 转换为 ChatMessage 格式
|
|
530
|
+
const chatMessages: ChatMessage[] = effectiveMessages.map(msg => ({
|
|
531
|
+
id: this.generateMessageId(),
|
|
532
|
+
role: msg.role as any,
|
|
533
|
+
content: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content),
|
|
534
|
+
timestamp: new Date(msg.ts)
|
|
535
|
+
}));
|
|
536
|
+
this.messagesSubject.next(chatMessages);
|
|
537
|
+
this.logger.info('Messages reloaded after context management', {
|
|
538
|
+
messageCount: chatMessages.length,
|
|
539
|
+
sessionId: this.currentSessionId
|
|
540
|
+
});
|
|
541
|
+
}
|
|
453
542
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Injectable } from '@angular/core';
|
|
2
2
|
import { LoggerService } from '../core/logger.service';
|
|
3
|
+
import { ConfigProviderService } from '../core/config-provider.service';
|
|
3
4
|
import { ChatHistoryService, SavedSession } from '../chat/chat-history.service';
|
|
4
5
|
import {
|
|
5
6
|
ApiMessage,
|
|
@@ -23,12 +24,28 @@ export class ContextManager {
|
|
|
23
24
|
|
|
24
25
|
constructor(
|
|
25
26
|
private logger: LoggerService,
|
|
26
|
-
private chatHistoryService: ChatHistoryService
|
|
27
|
+
private chatHistoryService: ChatHistoryService,
|
|
28
|
+
private configService: ConfigProviderService
|
|
27
29
|
) {
|
|
28
30
|
this.config = { ...DEFAULT_CONTEXT_CONFIG };
|
|
31
|
+
// 动态获取当前供应商的上下文限制
|
|
32
|
+
this.updateContextLimit();
|
|
29
33
|
this.logger.info('ContextManager initialized', { config: this.config });
|
|
30
34
|
}
|
|
31
35
|
|
|
36
|
+
/**
|
|
37
|
+
* 更新上下文限制(根据当前供应商)
|
|
38
|
+
*/
|
|
39
|
+
updateContextLimit(): void {
|
|
40
|
+
const providerContextWindow = this.configService.getActiveProviderContextWindow();
|
|
41
|
+
if (providerContextWindow !== this.config.maxContextTokens) {
|
|
42
|
+
this.config.maxContextTokens = providerContextWindow;
|
|
43
|
+
this.logger.info('Context limit updated', {
|
|
44
|
+
newLimit: providerContextWindow
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
32
49
|
/**
|
|
33
50
|
* 计算Token使用量
|
|
34
51
|
*/
|
|
@@ -71,10 +88,24 @@ export class ContextManager {
|
|
|
71
88
|
* 判断是否需要管理上下文
|
|
72
89
|
*/
|
|
73
90
|
shouldManageContext(sessionId: string): boolean {
|
|
91
|
+
// 动态更新上下文限制
|
|
92
|
+
this.updateContextLimit();
|
|
93
|
+
|
|
74
94
|
const session = this.chatHistoryService.loadSession(sessionId);
|
|
75
|
-
if (!session
|
|
95
|
+
if (!session) {
|
|
76
96
|
return false;
|
|
77
97
|
}
|
|
98
|
+
if (!session.messages || session.messages.length < 2) {
|
|
99
|
+
// 消息太少,不需要管理
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
if (!session.contextInfo?.tokenUsage) {
|
|
103
|
+
// 没有 token 使用统计,检查消息数量
|
|
104
|
+
const messages = session.messages as unknown as ApiMessage[];
|
|
105
|
+
const tokenUsage = this.calculateTokenUsage(messages);
|
|
106
|
+
const usageRate = this.calculateUsageRate(tokenUsage);
|
|
107
|
+
return usageRate >= this.config.pruneThreshold;
|
|
108
|
+
}
|
|
78
109
|
|
|
79
110
|
const usageRate = this.calculateUsageRate(session.contextInfo.tokenUsage);
|
|
80
111
|
|
|
@@ -172,18 +172,16 @@ export class AiAssistantService {
|
|
|
172
172
|
// 处理工具调用(如果响应包含工具调用)
|
|
173
173
|
response = await this.handleToolCalls(request, response, activeProvider);
|
|
174
174
|
|
|
175
|
-
//
|
|
175
|
+
// 检测幻觉:AI声称执行了操作但未调用工具
|
|
176
176
|
const toolCalls = (response as any).toolCalls;
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
response.message.content += '\n\n⚠️ 注意:命令未实际执行。请明确告诉我需要执行的命令。';
|
|
186
|
-
}
|
|
177
|
+
const hallucinationDetected = this.detectHallucination({
|
|
178
|
+
text: response.message.content,
|
|
179
|
+
toolCallCount: toolCalls?.length || 0
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
if (hallucinationDetected) {
|
|
183
|
+
// 附加警告消息,提醒用户
|
|
184
|
+
response.message.content += '\n\n⚠️ **检测到可能的幻觉**:AI声称执行了操作但未实际调用工具。\n实际执行的命令可能为空。请重新描述您的需求。';
|
|
187
185
|
}
|
|
188
186
|
|
|
189
187
|
this.logger.info('Chat request completed successfully');
|
|
@@ -606,4 +604,30 @@ export class AiAssistantService {
|
|
|
606
604
|
return null;
|
|
607
605
|
}
|
|
608
606
|
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* 检测AI幻觉
|
|
610
|
+
* 当AI声称执行了操作(如切换终端、执行命令)但未调用相应工具时触发
|
|
611
|
+
*/
|
|
612
|
+
private detectHallucination(response: { text: string; toolCallCount: number }): boolean {
|
|
613
|
+
const actionKeywords = [
|
|
614
|
+
'已切换', '已执行', '已完成', '已写入', '已读取',
|
|
615
|
+
'切换成功', '执行成功', '写入成功', '读取成功',
|
|
616
|
+
'现在切换', '现在执行', '已经为您切换', '已经为您执行',
|
|
617
|
+
'我将切换', '我会切换', '已经切换到', '已经执行了',
|
|
618
|
+
'终端已切换', '命令已执行', '操作已完成'
|
|
619
|
+
];
|
|
620
|
+
|
|
621
|
+
const hasActionClaim = actionKeywords.some(keyword => response.text.includes(keyword));
|
|
622
|
+
|
|
623
|
+
if (hasActionClaim && response.toolCallCount === 0) {
|
|
624
|
+
this.logger.warn('AI Hallucination detected', {
|
|
625
|
+
textPreview: response.text.substring(0, 100),
|
|
626
|
+
toolCallCount: response.toolCallCount
|
|
627
|
+
});
|
|
628
|
+
return true;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
return false;
|
|
632
|
+
}
|
|
609
633
|
}
|
|
@@ -3,6 +3,7 @@ import { Subject, Observable } from 'rxjs';
|
|
|
3
3
|
import { LoggerService } from './logger.service';
|
|
4
4
|
import { SecurityConfig } from '../../types/security.types';
|
|
5
5
|
import { ProviderConfig } from '../../types/provider.types';
|
|
6
|
+
import { ContextConfig } from '../../types/ai.types';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* AI助手配置
|
|
@@ -203,6 +204,32 @@ export class ConfigProviderService {
|
|
|
203
204
|
return { ...this.config.providers };
|
|
204
205
|
}
|
|
205
206
|
|
|
207
|
+
/**
|
|
208
|
+
* 获取活跃供应商的上下文窗口大小
|
|
209
|
+
*/
|
|
210
|
+
getActiveProviderContextWindow(): number {
|
|
211
|
+
const activeProvider = this.config.defaultProvider;
|
|
212
|
+
if (!activeProvider) {
|
|
213
|
+
return 200000; // 默认值
|
|
214
|
+
}
|
|
215
|
+
const providerConfig = this.getProviderConfig(activeProvider);
|
|
216
|
+
const contextWindow = providerConfig?.contextWindow;
|
|
217
|
+
if (contextWindow && contextWindow > 0) {
|
|
218
|
+
return contextWindow;
|
|
219
|
+
}
|
|
220
|
+
// 返回供应商默认值
|
|
221
|
+
const defaults: { [key: string]: number } = {
|
|
222
|
+
'openai': 128000,
|
|
223
|
+
'anthropic': 200000,
|
|
224
|
+
'minimax': 128000,
|
|
225
|
+
'glm': 128000,
|
|
226
|
+
'ollama': 8192,
|
|
227
|
+
'vllm': 8192,
|
|
228
|
+
'openai-compatible': 128000
|
|
229
|
+
};
|
|
230
|
+
return defaults[activeProvider] || 200000;
|
|
231
|
+
}
|
|
232
|
+
|
|
206
233
|
/**
|
|
207
234
|
* 获取默认提供商
|
|
208
235
|
*/
|
|
@@ -344,4 +371,43 @@ export class ConfigProviderService {
|
|
|
344
371
|
errors
|
|
345
372
|
};
|
|
346
373
|
}
|
|
374
|
+
|
|
375
|
+
// ==================== 上下文配置 ====================
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* 获取上下文配置
|
|
379
|
+
*/
|
|
380
|
+
getContextConfig(): ContextConfig | null {
|
|
381
|
+
try {
|
|
382
|
+
const key = 'tabby-ai-assistant-context-config';
|
|
383
|
+
const stored = localStorage.getItem(key);
|
|
384
|
+
return stored ? JSON.parse(stored) : null;
|
|
385
|
+
} catch {
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* 设置上下文配置
|
|
392
|
+
*/
|
|
393
|
+
setContextConfig(config: ContextConfig): void {
|
|
394
|
+
const key = 'tabby-ai-assistant-context-config';
|
|
395
|
+
localStorage.setItem(key, JSON.stringify(config));
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* 获取自动压缩开关状态
|
|
400
|
+
*/
|
|
401
|
+
isAutoCompactEnabled(): boolean {
|
|
402
|
+
const key = 'tabby-ai-assistant-auto-compact';
|
|
403
|
+
return localStorage.getItem(key) !== 'false';
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* 设置自动压缩开关状态
|
|
408
|
+
*/
|
|
409
|
+
setAutoCompactEnabled(enabled: boolean): void {
|
|
410
|
+
const key = 'tabby-ai-assistant-auto-compact';
|
|
411
|
+
localStorage.setItem(key, String(enabled));
|
|
412
|
+
}
|
|
347
413
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { Subject } from 'rxjs';
|
|
3
|
+
|
|
4
|
+
export interface ToastMessage {
|
|
5
|
+
id: string;
|
|
6
|
+
type: 'success' | 'error' | 'warning' | 'info';
|
|
7
|
+
message: string;
|
|
8
|
+
duration?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
@Injectable({ providedIn: 'root' })
|
|
12
|
+
export class ToastService {
|
|
13
|
+
private toastSubject = new Subject<ToastMessage>();
|
|
14
|
+
toast$ = this.toastSubject.asObservable();
|
|
15
|
+
|
|
16
|
+
show(type: ToastMessage['type'], message: string, duration = 3000): void {
|
|
17
|
+
const id = `toast-${Date.now()}`;
|
|
18
|
+
this.toastSubject.next({ id, type, message, duration });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
success(message: string, duration = 3000): void {
|
|
22
|
+
this.show('success', message, duration);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
error(message: string, duration = 5000): void {
|
|
26
|
+
this.show('error', message, duration);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
warning(message: string, duration = 4000): void {
|
|
30
|
+
this.show('warning', message, duration);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
info(message: string, duration = 3000): void {
|
|
34
|
+
this.show('info', message, duration);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -385,16 +385,24 @@ export class AnthropicProviderService extends BaseAiProvider {
|
|
|
385
385
|
- read_terminal_output: 读取终端输出
|
|
386
386
|
- get_terminal_list: 获取所有终端列表
|
|
387
387
|
- get_terminal_cwd: 获取当前工作目录
|
|
388
|
+
- focus_terminal: 切换到指定索引的终端(需要参数 terminal_index)
|
|
389
|
+
- get_terminal_selection: 获取终端中选中的文本
|
|
388
390
|
|
|
389
391
|
## 重要规则
|
|
390
392
|
1. 当用户请求执行命令(如"查看当前目录"、"列出文件"等),你必须使用 write_to_terminal 工具来执行
|
|
391
|
-
2.
|
|
392
|
-
3.
|
|
393
|
-
4.
|
|
393
|
+
2. **当用户请求切换终端(如"切换到终端0"、"打开终端4"等),你必须使用 focus_terminal 工具**
|
|
394
|
+
3. 不要只是描述你"将要做什么",而是直接调用工具执行
|
|
395
|
+
4. 执行命令后,使用 read_terminal_output 读取结果并报告给用户
|
|
396
|
+
5. 如果不确定当前目录或终端状态,先使用 get_terminal_cwd 或 get_terminal_list 获取信息
|
|
397
|
+
6. **永远不要假装执行了操作,必须真正调用工具**
|
|
394
398
|
|
|
395
399
|
## 示例
|
|
396
400
|
用户:"查看当前目录的文件"
|
|
397
401
|
正确做法:调用 write_to_terminal 工具,参数 { "command": "dir", "execute": true }
|
|
398
|
-
错误做法:仅回复文字"我将执行 dir 命令"
|
|
402
|
+
错误做法:仅回复文字"我将执行 dir 命令"
|
|
403
|
+
|
|
404
|
+
用户:"切换到终端4"
|
|
405
|
+
正确做法:调用 focus_terminal 工具,参数 { "terminal_index": 4 }
|
|
406
|
+
错误做法:仅回复文字"已切换到终端4"(不调用工具)`;
|
|
399
407
|
}
|
|
400
408
|
}
|
|
@@ -479,16 +479,24 @@ export class GlmProviderService extends BaseAiProvider {
|
|
|
479
479
|
- read_terminal_output: 读取终端输出
|
|
480
480
|
- get_terminal_list: 获取所有终端列表
|
|
481
481
|
- get_terminal_cwd: 获取当前工作目录
|
|
482
|
+
- focus_terminal: 切换到指定索引的终端(需要参数 terminal_index)
|
|
483
|
+
- get_terminal_selection: 获取终端中选中的文本
|
|
482
484
|
|
|
483
485
|
## 重要规则
|
|
484
486
|
1. 当用户请求执行命令(如"查看当前目录"、"列出文件"等),你必须使用 write_to_terminal 工具来执行
|
|
485
|
-
2.
|
|
486
|
-
3.
|
|
487
|
-
4.
|
|
487
|
+
2. **当用户请求切换终端(如"切换到终端0"、"打开终端4"等),你必须使用 focus_terminal 工具**
|
|
488
|
+
3. 不要只是描述你"将要做什么",而是直接调用工具执行
|
|
489
|
+
4. 执行命令后,使用 read_terminal_output 读取结果并报告给用户
|
|
490
|
+
5. 如果不确定当前目录或终端状态,先使用 get_terminal_cwd 或 get_terminal_list 获取信息
|
|
491
|
+
6. **永远不要假装执行了操作,必须真正调用工具**
|
|
488
492
|
|
|
489
493
|
## 示例
|
|
490
494
|
用户:"查看当前目录的文件"
|
|
491
495
|
正确做法:调用 write_to_terminal 工具,参数 { "command": "dir", "execute": true }
|
|
492
|
-
错误做法:仅回复文字"我将执行 dir 命令"
|
|
496
|
+
错误做法:仅回复文字"我将执行 dir 命令"
|
|
497
|
+
|
|
498
|
+
用户:"切换到终端4"
|
|
499
|
+
正确做法:调用 focus_terminal 工具,参数 { "terminal_index": 4 }
|
|
500
|
+
错误做法:仅回复文字"已切换到终端4"(不调用工具)`;
|
|
493
501
|
}
|
|
494
502
|
}
|
|
@@ -610,16 +610,24 @@ export class MinimaxProviderService extends BaseAiProvider {
|
|
|
610
610
|
- read_terminal_output: 读取终端输出
|
|
611
611
|
- get_terminal_list: 获取所有终端列表
|
|
612
612
|
- get_terminal_cwd: 获取当前工作目录
|
|
613
|
+
- focus_terminal: 切换到指定索引的终端(需要参数 terminal_index)
|
|
614
|
+
- get_terminal_selection: 获取终端中选中的文本
|
|
613
615
|
|
|
614
616
|
## 重要规则
|
|
615
617
|
1. 当用户请求执行命令(如"查看当前目录"、"列出文件"等),你必须使用 write_to_terminal 工具来执行
|
|
616
|
-
2.
|
|
617
|
-
3.
|
|
618
|
-
4.
|
|
618
|
+
2. **当用户请求切换终端(如"切换到终端0"、"打开终端4"等),你必须使用 focus_terminal 工具**
|
|
619
|
+
3. 不要只是描述你"将要做什么",而是直接调用工具执行
|
|
620
|
+
4. 执行命令后,使用 read_terminal_output 读取结果并报告给用户
|
|
621
|
+
5. 如果不确定当前目录或终端状态,先使用 get_terminal_cwd 或 get_terminal_list 获取信息
|
|
622
|
+
6. **永远不要假装执行了操作,必须真正调用工具**
|
|
619
623
|
|
|
620
624
|
## 示例
|
|
621
625
|
用户:"查看当前目录的文件"
|
|
622
626
|
正确做法:调用 write_to_terminal 工具,参数 { "command": "dir", "execute": true }
|
|
623
|
-
错误做法:仅回复文字"我将执行 dir 命令"
|
|
627
|
+
错误做法:仅回复文字"我将执行 dir 命令"
|
|
628
|
+
|
|
629
|
+
用户:"切换到终端4"
|
|
630
|
+
正确做法:调用 focus_terminal 工具,参数 { "terminal_index": 4 }
|
|
631
|
+
错误做法:仅回复文字"已切换到终端4"(不调用工具)`;
|
|
624
632
|
}
|
|
625
633
|
}
|
package/src/types/ai.types.ts
CHANGED
|
@@ -192,6 +192,7 @@ export interface ContextConfig {
|
|
|
192
192
|
pruneThreshold: number; // 裁剪触发阈值 (0-1)
|
|
193
193
|
messagesToKeep: number; // 保留的最近消息数
|
|
194
194
|
bufferPercentage: number; // 安全缓冲区百分比
|
|
195
|
+
summaryPrompt?: string; // 自定义摘要提示词
|
|
195
196
|
}
|
|
196
197
|
|
|
197
198
|
// 默认配置
|
|
@@ -201,10 +202,9 @@ export const DEFAULT_CONTEXT_CONFIG: ContextConfig = {
|
|
|
201
202
|
compactThreshold: 0.85,
|
|
202
203
|
pruneThreshold: 0.70,
|
|
203
204
|
messagesToKeep: 3,
|
|
204
|
-
bufferPercentage: 0.10
|
|
205
|
+
bufferPercentage: 0.10,
|
|
206
|
+
summaryPrompt: '请用一句话总结以下对话的要点,保留关键信息和用户意图:'
|
|
205
207
|
};
|
|
206
|
-
|
|
207
|
-
// 压缩结果
|
|
208
208
|
export interface CompactionResult {
|
|
209
209
|
success: boolean;
|
|
210
210
|
messages: ApiMessage[];
|