tabby-ai-assistant 1.0.10 → 1.0.12

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 (104) hide show
  1. package/dist/index.js +1 -1
  2. package/package.json +3 -3
  3. package/src/components/chat/ai-sidebar.component.scss +43 -0
  4. package/src/components/chat/ai-sidebar.component.ts +43 -1
  5. package/src/components/settings/ai-settings-tab.component.html +13 -5
  6. package/src/components/settings/ai-settings-tab.component.scss +164 -33
  7. package/src/components/settings/ai-settings-tab.component.ts +1 -0
  8. package/src/components/settings/context-settings.component.html +87 -0
  9. package/src/components/settings/context-settings.component.scss +133 -0
  10. package/src/components/settings/context-settings.component.ts +91 -0
  11. package/src/components/settings/provider-config.component.ts +12 -6
  12. package/src/i18n/translations/en-US.ts +27 -1
  13. package/src/i18n/translations/ja-JP.ts +27 -1
  14. package/src/i18n/translations/zh-CN.ts +27 -1
  15. package/src/i18n/types.ts +28 -0
  16. package/src/index.ts +6 -0
  17. package/src/services/chat/ai-sidebar.service.ts +1 -1
  18. package/src/services/chat/chat-session.service.ts +91 -2
  19. package/src/services/context/manager.ts +33 -2
  20. package/src/services/core/ai-assistant.service.ts +35 -11
  21. package/src/services/core/config-provider.service.ts +66 -0
  22. package/src/services/core/toast.service.ts +70 -0
  23. package/src/services/providers/anthropic-provider.service.ts +12 -4
  24. package/src/services/providers/glm-provider.service.ts +12 -4
  25. package/src/services/providers/minimax-provider.service.ts +12 -4
  26. package/src/styles/ai-assistant.scss +23 -0
  27. package/src/types/ai.types.ts +3 -3
  28. package/src/types/provider.types.ts +1 -0
  29. package/dist/components/chat/ai-sidebar.component.d.ts +0 -160
  30. package/dist/components/chat/chat-input.component.d.ts +0 -69
  31. package/dist/components/chat/chat-interface.component.d.ts +0 -124
  32. package/dist/components/chat/chat-message.component.d.ts +0 -53
  33. package/dist/components/chat/chat-settings.component.d.ts +0 -72
  34. package/dist/components/common/error-message.component.d.ts +0 -11
  35. package/dist/components/common/loading-spinner.component.d.ts +0 -4
  36. package/dist/components/security/consent-dialog.component.d.ts +0 -11
  37. package/dist/components/security/password-prompt.component.d.ts +0 -10
  38. package/dist/components/security/risk-confirm-dialog.component.d.ts +0 -36
  39. package/dist/components/settings/ai-settings-tab.component.d.ts +0 -82
  40. package/dist/components/settings/general-settings.component.d.ts +0 -94
  41. package/dist/components/settings/provider-config.component.d.ts +0 -300
  42. package/dist/components/settings/security-settings.component.d.ts +0 -33
  43. package/dist/components/terminal/ai-toolbar-button.component.d.ts +0 -10
  44. package/dist/components/terminal/command-preview.component.d.ts +0 -53
  45. package/dist/components/terminal/command-suggestion.component.d.ts +0 -16
  46. package/dist/i18n/index.d.ts +0 -48
  47. package/dist/i18n/translations/en-US.d.ts +0 -5
  48. package/dist/i18n/translations/ja-JP.d.ts +0 -5
  49. package/dist/i18n/translations/zh-CN.d.ts +0 -5
  50. package/dist/i18n/types.d.ts +0 -198
  51. package/dist/index-full.d.ts +0 -8
  52. package/dist/index-minimal.d.ts +0 -3
  53. package/dist/index.d.ts +0 -12
  54. package/dist/main.d.ts +0 -8
  55. package/dist/providers/tabby/ai-config.provider.d.ts +0 -70
  56. package/dist/providers/tabby/ai-hotkey.provider.d.ts +0 -15
  57. package/dist/providers/tabby/ai-settings-tab.provider.d.ts +0 -11
  58. package/dist/providers/tabby/ai-toolbar-button.provider.d.ts +0 -16
  59. package/dist/services/chat/ai-sidebar.service.d.ts +0 -111
  60. package/dist/services/chat/chat-history.service.d.ts +0 -145
  61. package/dist/services/chat/chat-session.service.d.ts +0 -113
  62. package/dist/services/chat/command-generator.service.d.ts +0 -49
  63. package/dist/services/context/compaction.d.ts +0 -90
  64. package/dist/services/context/manager.d.ts +0 -69
  65. package/dist/services/context/memory.d.ts +0 -116
  66. package/dist/services/context/token-budget.d.ts +0 -105
  67. package/dist/services/core/ai-assistant.service.d.ts +0 -127
  68. package/dist/services/core/ai-provider-manager.service.d.ts +0 -119
  69. package/dist/services/core/checkpoint.service.d.ts +0 -130
  70. package/dist/services/core/config-provider.service.d.ts +0 -137
  71. package/dist/services/core/logger.service.d.ts +0 -21
  72. package/dist/services/core/theme.service.d.ts +0 -53
  73. package/dist/services/core/toast.service.d.ts +0 -15
  74. package/dist/services/platform/escape-sequence.service.d.ts +0 -132
  75. package/dist/services/platform/platform-detection.service.d.ts +0 -146
  76. package/dist/services/providers/anthropic-provider.service.d.ts +0 -44
  77. package/dist/services/providers/base-provider.service.d.ts +0 -142
  78. package/dist/services/providers/glm-provider.service.d.ts +0 -96
  79. package/dist/services/providers/minimax-provider.service.d.ts +0 -102
  80. package/dist/services/providers/ollama-provider.service.d.ts +0 -76
  81. package/dist/services/providers/openai-compatible.service.d.ts +0 -44
  82. package/dist/services/providers/openai-provider.service.d.ts +0 -43
  83. package/dist/services/providers/vllm-provider.service.d.ts +0 -82
  84. package/dist/services/security/consent-manager.service.d.ts +0 -65
  85. package/dist/services/security/password-manager.service.d.ts +0 -67
  86. package/dist/services/security/risk-assessment.service.d.ts +0 -65
  87. package/dist/services/security/security-validator.service.d.ts +0 -36
  88. package/dist/services/terminal/buffer-analyzer.service.d.ts +0 -128
  89. package/dist/services/terminal/command-analyzer.service.d.ts +0 -20
  90. package/dist/services/terminal/context-menu.service.d.ts +0 -24
  91. package/dist/services/terminal/hotkey.service.d.ts +0 -28
  92. package/dist/services/terminal/terminal-context.service.d.ts +0 -100
  93. package/dist/services/terminal/terminal-manager.service.d.ts +0 -185
  94. package/dist/services/terminal/terminal-tools.service.d.ts +0 -79
  95. package/dist/types/ai.types.d.ts +0 -199
  96. package/dist/types/provider.types.d.ts +0 -105
  97. package/dist/types/security.types.d.ts +0 -85
  98. package/dist/types/terminal.types.d.ts +0 -150
  99. package/dist/utils/encryption.utils.d.ts +0 -83
  100. package/dist/utils/formatting.utils.d.ts +0 -106
  101. package/dist/utils/validation.utils.d.ts +0 -83
  102. package/jest.config.js +0 -47
  103. package/webpack-docker.config.js +0 -42
  104. package/webpack.config.js +0 -81
@@ -0,0 +1,91 @@
1
+ import { Component, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core';
2
+ import { Subject } from 'rxjs';
3
+ import { takeUntil } from 'rxjs/operators';
4
+ import { ConfigProviderService } from '../../services/core/config-provider.service';
5
+ import { ContextManager } from '../../services/context/manager';
6
+ import { ToastService } from '../../services/core/toast.service';
7
+ import { TranslateService } from '../../i18n';
8
+ import { ContextConfig, DEFAULT_CONTEXT_CONFIG } from '../../types/ai.types';
9
+
10
+ @Component({
11
+ selector: 'app-context-settings',
12
+ templateUrl: './context-settings.component.html',
13
+ styleUrls: ['./context-settings.component.scss'],
14
+ encapsulation: ViewEncapsulation.None
15
+ })
16
+ export class ContextSettingsComponent implements OnInit, OnDestroy {
17
+ // 配置项
18
+ config: ContextConfig = { ...DEFAULT_CONTEXT_CONFIG };
19
+ autoCompactEnabled = true;
20
+
21
+ // 当前供应商的上下文限制
22
+ activeProviderContextWindow: number = 200000;
23
+
24
+ // 翻译对象
25
+ t: any;
26
+
27
+ private destroy$ = new Subject<void>();
28
+
29
+ constructor(
30
+ private configService: ConfigProviderService,
31
+ private contextManager: ContextManager,
32
+ private toast: ToastService,
33
+ private translate: TranslateService
34
+ ) {
35
+ this.t = this.translate.t;
36
+ }
37
+
38
+ ngOnInit(): void {
39
+ this.translate.translation$.pipe(
40
+ takeUntil(this.destroy$)
41
+ ).subscribe(translation => {
42
+ this.t = translation;
43
+ });
44
+
45
+ this.loadConfig();
46
+ }
47
+
48
+ ngOnDestroy(): void {
49
+ this.destroy$.next();
50
+ this.destroy$.complete();
51
+ }
52
+
53
+ loadConfig(): void {
54
+ const savedConfig = this.configService.getContextConfig();
55
+ if (savedConfig) {
56
+ this.config = { ...DEFAULT_CONTEXT_CONFIG, ...savedConfig };
57
+ }
58
+ this.autoCompactEnabled = this.configService.isAutoCompactEnabled();
59
+
60
+ // 获取当前供应商的上下文限制
61
+ this.activeProviderContextWindow = this.configService.getActiveProviderContextWindow();
62
+
63
+ // 确保配置的 maxContextTokens 不超过供应商限制
64
+ if (this.config.maxContextTokens > this.activeProviderContextWindow) {
65
+ this.config.maxContextTokens = this.activeProviderContextWindow;
66
+ }
67
+ }
68
+
69
+ saveConfig(): void {
70
+ this.configService.setContextConfig(this.config);
71
+ this.contextManager.updateConfig(this.config);
72
+ this.toast.success(this.t?.contextSettings?.configSaved || '上下文配置已保存');
73
+ }
74
+
75
+ toggleAutoCompact(): void {
76
+ this.autoCompactEnabled = !this.autoCompactEnabled;
77
+ this.configService.setAutoCompactEnabled(this.autoCompactEnabled);
78
+ this.toast.info(
79
+ this.autoCompactEnabled
80
+ ? (this.t?.contextSettings?.autoCompactEnabled || '自动压缩已启用')
81
+ : (this.t?.contextSettings?.autoCompactDisabled || '自动压缩已禁用')
82
+ );
83
+ }
84
+
85
+ resetToDefaults(): void {
86
+ this.config = { ...DEFAULT_CONTEXT_CONFIG };
87
+ this.autoCompactEnabled = true;
88
+ this.saveConfig();
89
+ this.toast.info(this.t?.common?.resetToDefault || '已重置为默认配置');
90
+ }
91
+ }
@@ -48,7 +48,8 @@ export class ProviderConfigComponent implements OnInit, OnDestroy {
48
48
  fields: [
49
49
  { key: 'apiKey', label: 'API Key', type: 'password', required: true },
50
50
  { key: 'baseURL', label: 'Base URL', type: 'text', default: 'https://api.openai.com/v1', required: false },
51
- { key: 'model', label: 'Model', type: 'text', default: 'gpt-4', required: false, placeholder: '例如: gpt-4, gpt-4-turbo, gpt-3.5-turbo' }
51
+ { key: 'model', label: 'Model', type: 'text', default: 'gpt-4', required: false, placeholder: '例如: gpt-4, gpt-4-turbo, gpt-3.5-turbo' },
52
+ { key: 'contextWindow', label: '上下文限制', type: 'number', default: 128000, required: false, placeholder: 'GPT-4: 128000, GPT-3.5: 16385' }
52
53
  ]
53
54
  },
54
55
  'anthropic': {
@@ -58,7 +59,8 @@ export class ProviderConfigComponent implements OnInit, OnDestroy {
58
59
  fields: [
59
60
  { key: 'apiKey', label: 'API Key', type: 'password', required: true },
60
61
  { key: 'baseURL', label: 'Base URL', type: 'text', default: 'https://api.anthropic.com', required: false },
61
- { key: 'model', label: 'Model', type: 'text', default: 'claude-3-sonnet-20240229', required: false, placeholder: '例如: claude-3-opus, claude-3-sonnet' }
62
+ { key: 'model', label: 'Model', type: 'text', default: 'claude-3-sonnet-20240229', required: false, placeholder: '例如: claude-3-opus, claude-3-sonnet' },
63
+ { key: 'contextWindow', label: '上下文限制', type: 'number', default: 200000, required: false, placeholder: 'Claude 3: 200000' }
62
64
  ]
63
65
  },
64
66
  'minimax': {
@@ -68,7 +70,8 @@ export class ProviderConfigComponent implements OnInit, OnDestroy {
68
70
  fields: [
69
71
  { key: 'apiKey', label: 'API Key', type: 'password', required: true },
70
72
  { key: 'baseURL', label: 'Base URL', type: 'text', default: 'https://api.minimaxi.com/anthropic', required: false },
71
- { key: 'model', label: 'Model', type: 'text', default: 'MiniMax-M2', required: false, placeholder: '例如: MiniMax-M2, MiniMax-M2.1' }
73
+ { key: 'model', label: 'Model', type: 'text', default: 'MiniMax-M2', required: false, placeholder: '例如: MiniMax-M2, MiniMax-M2.1' },
74
+ { key: 'contextWindow', label: '上下文限制', type: 'number', default: 128000, required: false, placeholder: 'MiniMax-M2: 128000' }
72
75
  ]
73
76
  },
74
77
  'glm': {
@@ -78,7 +81,8 @@ export class ProviderConfigComponent implements OnInit, OnDestroy {
78
81
  fields: [
79
82
  { key: 'apiKey', label: 'API Key', type: 'password', required: true },
80
83
  { key: 'baseURL', label: 'Base URL', type: 'text', default: 'https://open.bigmodel.cn/api/paas/v4', required: false },
81
- { key: 'model', label: 'Model', type: 'text', default: 'glm-4', required: false, placeholder: '例如: glm-4, glm-4-air, glm-4-flash' }
84
+ { key: 'model', label: 'Model', type: 'text', default: 'glm-4', required: false, placeholder: '例如: glm-4, glm-4-air, glm-4-flash' },
85
+ { key: 'contextWindow', label: '上下文限制', type: 'number', default: 128000, required: false, placeholder: 'GLM-4: 128000' }
82
86
  ]
83
87
  }
84
88
  };
@@ -92,7 +96,8 @@ export class ProviderConfigComponent implements OnInit, OnDestroy {
92
96
  defaultURL: 'http://localhost:11434/v1',
93
97
  fields: [
94
98
  { key: 'baseURL', label: 'Base URL', type: 'text', default: 'http://localhost:11434/v1', required: true, placeholder: '例如: http://localhost:11434/v1' },
95
- { key: 'model', label: 'Model', type: 'text', default: 'llama3.1', required: false, placeholder: '例如: llama3.1, qwen2.5, mistral' }
99
+ { key: 'model', label: 'Model', type: 'text', default: 'llama3.1', required: false, placeholder: '例如: llama3.1, qwen2.5, mistral' },
100
+ { key: 'contextWindow', label: '上下文限制', type: 'number', default: 8192, required: false, placeholder: 'Llama 3.1: 8192' }
96
101
  ]
97
102
  },
98
103
  'vllm': {
@@ -103,7 +108,8 @@ export class ProviderConfigComponent implements OnInit, OnDestroy {
103
108
  fields: [
104
109
  { key: 'baseURL', label: 'Base URL', type: 'text', default: 'http://localhost:8000/v1', required: true, placeholder: '例如: http://localhost:8000/v1' },
105
110
  { key: 'apiKey', label: 'API Key (可选)', type: 'password', required: false },
106
- { key: 'model', label: 'Model', type: 'text', default: 'meta-llama/Llama-3.1-8B', required: false, placeholder: 'HuggingFace 模型路径' }
111
+ { key: 'model', label: 'Model', type: 'text', default: 'meta-llama/Llama-3.1-8B', required: false, placeholder: 'HuggingFace 模型路径' },
112
+ { key: 'contextWindow', label: '上下文限制', type: 'number', default: 8192, required: false, placeholder: '根据模型实际配置设置' }
107
113
  ]
108
114
  }
109
115
  };
@@ -31,6 +31,7 @@ export const enUS: TranslationKeys = {
31
31
  title: 'Settings',
32
32
  generalTab: 'General',
33
33
  providersTab: 'AI Providers',
34
+ contextTab: 'Context',
34
35
  securityTab: 'Security',
35
36
  chatTab: 'Chat',
36
37
  advancedTab: 'Advanced'
@@ -135,7 +136,9 @@ export const enUS: TranslationKeys = {
135
136
  testFail: 'Connection test failed',
136
137
  testError: 'Cannot connect to service, please ensure it is running',
137
138
  configSaved: 'Configuration saved',
138
- configDeleted: 'Configuration deleted'
139
+ configDeleted: 'Configuration deleted',
140
+ contextWindow: 'Context Window',
141
+ contextWindowDesc: 'Maximum context tokens for this model'
139
142
  },
140
143
 
141
144
  providerNames: {
@@ -189,5 +192,28 @@ export const enUS: TranslationKeys = {
189
192
  pluginVersion: 'Plugin Version',
190
193
  supportedProviders: 'Supported Providers',
191
194
  currentProvider: 'Current Provider'
195
+ },
196
+
197
+ contextSettings: {
198
+ title: 'Context Management',
199
+ autoCompact: 'Auto Compact',
200
+ enableAutoCompact: 'Enable Auto Compact',
201
+ autoCompactDesc: 'Automatically compress history when context exceeds threshold',
202
+ autoCompactEnabled: 'Auto Compact Enabled',
203
+ autoCompactDisabled: 'Auto Compact Disabled',
204
+ tokenConfig: 'Token Configuration',
205
+ currentProviderLimit: 'Current Provider Context Limit',
206
+ maxContextTokens: 'Max Context Tokens',
207
+ maxContextTokensDesc: 'Cannot exceed current provider context limit',
208
+ reservedOutputTokens: 'Reserved Output Tokens',
209
+ reservedOutputTokensDesc: 'Token count reserved for AI response',
210
+ thresholdConfig: 'Compression Thresholds',
211
+ pruneThreshold: 'Prune Threshold',
212
+ pruneThresholdDesc: 'Prune redundant content when usage exceeds this (default: 70%)',
213
+ compactThreshold: 'Compact Threshold',
214
+ compactThresholdDesc: 'Generate summary when usage exceeds this (default: 85%)',
215
+ messagesToKeep: 'Messages to Keep',
216
+ messagesToKeepDesc: 'Recent messages to always preserve (default: 3)',
217
+ configSaved: 'Context config saved'
192
218
  }
193
219
  };
@@ -31,6 +31,7 @@ export const jaJP: TranslationKeys = {
31
31
  title: '設定',
32
32
  generalTab: '基本設定',
33
33
  providersTab: 'AIプロバイダー',
34
+ contextTab: 'コンテキスト',
34
35
  securityTab: 'セキュリティ',
35
36
  chatTab: 'チャット',
36
37
  advancedTab: '詳細設定'
@@ -135,7 +136,9 @@ export const jaJP: TranslationKeys = {
135
136
  testFail: '接続テスト失敗',
136
137
  testError: 'サービスに接続できません。サービスが起動していることを確認してください。',
137
138
  configSaved: '設定が保存されました',
138
- configDeleted: '設定が削除されました'
139
+ configDeleted: '設定が削除されました',
140
+ contextWindow: 'コンテキストウィンドウ',
141
+ contextWindowDesc: 'モデルの最大コンテキストトークン数'
139
142
  },
140
143
 
141
144
  providerNames: {
@@ -189,5 +192,28 @@ export const jaJP: TranslationKeys = {
189
192
  pluginVersion: 'プラグインVersion',
190
193
  supportedProviders: 'サポートされているプロバイダー',
191
194
  currentProvider: '現在のリプロバイダー'
195
+ },
196
+
197
+ contextSettings: {
198
+ title: 'コンテキスト管理',
199
+ autoCompact: '自動圧縮',
200
+ enableAutoCompact: '自動圧縮を有効にする',
201
+ autoCompactDesc: 'コンテキストがしきい値を超えた場合に履歴を自動的に圧縮',
202
+ autoCompactEnabled: '自動圧縮が有効',
203
+ autoCompactDisabled: '自動圧縮が無効',
204
+ tokenConfig: 'トークン設定',
205
+ currentProviderLimit: '現在のリプロバイダーのコンテキスト制限',
206
+ maxContextTokens: '最大コンテキストトークン数',
207
+ maxContextTokensDesc: '現在のリプロバイダーのコンテキスト制限を超えないこと',
208
+ reservedOutputTokens: '出力予約トークン数',
209
+ reservedOutputTokensDesc: 'AI応答に予約するトークン数',
210
+ thresholdConfig: '圧縮しきい値',
211
+ pruneThreshold: 'プルーンしきい値',
212
+ pruneThresholdDesc: '使用率がこれを超えた場合に冗長コンテンツを削除(デフォルト:70%)',
213
+ compactThreshold: '圧縮しきい値',
214
+ compactThresholdDesc: '使用率がこれを超えた場合にサマリーを生成(デフォルト:85%)',
215
+ messagesToKeep: '保持するメッセージ数',
216
+ messagesToKeepDesc: '圧縮時に常に保持する最新メッセージ数(デフォルト:3)',
217
+ configSaved: 'コンテキスト設定が保存されました'
192
218
  }
193
219
  };
@@ -31,6 +31,7 @@ export const zhCN: TranslationKeys = {
31
31
  title: '设置',
32
32
  generalTab: '基本设置',
33
33
  providersTab: 'AI提供商',
34
+ contextTab: '上下文设置',
34
35
  securityTab: '安全设置',
35
36
  chatTab: '聊天设置',
36
37
  advancedTab: '高级设置'
@@ -135,7 +136,9 @@ export const zhCN: TranslationKeys = {
135
136
  testFail: '连接测试失败',
136
137
  testError: '无法连接到服务,请确保服务已启动',
137
138
  configSaved: '配置已保存',
138
- configDeleted: '配置已删除'
139
+ configDeleted: '配置已删除',
140
+ contextWindow: '上下文限制',
141
+ contextWindowDesc: '模型的最大上下文Token数'
139
142
  },
140
143
 
141
144
  providerNames: {
@@ -189,5 +192,28 @@ export const zhCN: TranslationKeys = {
189
192
  pluginVersion: '插件版本',
190
193
  supportedProviders: '支持的提供商',
191
194
  currentProvider: '当前提供商'
195
+ },
196
+
197
+ contextSettings: {
198
+ title: '上下文管理',
199
+ autoCompact: '自动压缩',
200
+ enableAutoCompact: '启用自动压缩',
201
+ autoCompactDesc: '当上下文超过阈值时自动压缩历史消息',
202
+ autoCompactEnabled: '自动压缩已启用',
203
+ autoCompactDisabled: '自动压缩已禁用',
204
+ tokenConfig: 'Token 配置',
205
+ currentProviderLimit: '当前供应商上下文限制',
206
+ maxContextTokens: '最大上下文 Token 数',
207
+ maxContextTokensDesc: '不能超过当前供应商的上下文限制',
208
+ reservedOutputTokens: '输出预留 Token 数',
209
+ reservedOutputTokensDesc: '为 AI 回复预留的 Token 数量',
210
+ thresholdConfig: '压缩阈值',
211
+ pruneThreshold: '裁剪阈值',
212
+ pruneThresholdDesc: '使用率超过此阈值时裁剪冗余内容(默认:70%)',
213
+ compactThreshold: '压缩阈值',
214
+ compactThresholdDesc: '使用率超过此阈值时生成摘要压缩(默认:85%)',
215
+ messagesToKeep: '保留消息数',
216
+ messagesToKeepDesc: '压缩时始终保留的最近消息数量(默认:3)',
217
+ configSaved: '上下文配置已保存'
192
218
  }
193
219
  };
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,
@@ -16,7 +16,7 @@ export interface AiSidebarConfig {
16
16
 
17
17
  /**
18
18
  * AI Sidebar 服务 - 管理 AI 聊天侧边栏的生命周期
19
- *
19
+ *
20
20
  * 采用 Flexbox 布局方式,将 sidebar 插入到 app-root 作为第一个子元素,
21
21
  * app-root 变为水平 flex 容器,sidebar 在左侧
22
22
  */
@@ -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 || !session.contextInfo?.tokenUsage) {
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
- if (!toolCalls || toolCalls.length === 0) {
178
- const content = response.message.content.toLowerCase();
179
- const intentPatterns = ['我将向', '我会执行', '正在执行', '向终端写入'];
180
- const hasExecutionIntent = intentPatterns.some(p => content.includes(p));
181
-
182
- if (hasExecutionIntent) {
183
- this.logger.warn('AI stated execution intent but did not call tools');
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
  }