tabby-ai-assistant 1.0.12 → 1.0.15

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 (47) hide show
  1. package/.editorconfig +18 -0
  2. package/README.md +113 -55
  3. package/dist/index.js +1 -1
  4. package/package.json +6 -4
  5. package/src/components/chat/ai-sidebar.component.scss +220 -9
  6. package/src/components/chat/ai-sidebar.component.ts +364 -29
  7. package/src/components/chat/chat-input.component.ts +36 -4
  8. package/src/components/chat/chat-interface.component.ts +225 -5
  9. package/src/components/chat/chat-message.component.ts +6 -1
  10. package/src/components/settings/context-settings.component.ts +91 -91
  11. package/src/components/terminal/ai-toolbar-button.component.ts +4 -2
  12. package/src/components/terminal/command-suggestion.component.ts +148 -6
  13. package/src/index.ts +0 -6
  14. package/src/providers/tabby/ai-toolbar-button.provider.ts +7 -3
  15. package/src/services/chat/ai-sidebar.service.ts +414 -410
  16. package/src/services/chat/chat-session.service.ts +36 -12
  17. package/src/services/context/compaction.ts +110 -134
  18. package/src/services/context/manager.ts +27 -7
  19. package/src/services/context/memory.ts +17 -33
  20. package/src/services/context/summary.service.ts +136 -0
  21. package/src/services/core/ai-assistant.service.ts +1060 -37
  22. package/src/services/core/ai-provider-manager.service.ts +154 -25
  23. package/src/services/core/checkpoint.service.ts +218 -18
  24. package/src/services/core/config-provider.service.ts +4 -12
  25. package/src/services/core/toast.service.ts +106 -106
  26. package/src/services/providers/anthropic-provider.service.ts +126 -202
  27. package/src/services/providers/base-provider.service.ts +315 -21
  28. package/src/services/providers/glm-provider.service.ts +151 -233
  29. package/src/services/providers/minimax-provider.service.ts +55 -238
  30. package/src/services/providers/ollama-provider.service.ts +117 -188
  31. package/src/services/providers/openai-compatible.service.ts +165 -177
  32. package/src/services/providers/openai-provider.service.ts +170 -177
  33. package/src/services/providers/vllm-provider.service.ts +116 -188
  34. package/src/services/terminal/terminal-context.service.ts +265 -5
  35. package/src/services/terminal/terminal-manager.service.ts +748 -748
  36. package/src/services/terminal/terminal-tools.service.ts +612 -441
  37. package/src/types/ai.types.ts +156 -3
  38. package/src/types/provider.types.ts +206 -75
  39. package/src/utils/cost.utils.ts +249 -0
  40. package/src/utils/validation.utils.ts +306 -2
  41. package/dist/index.js.LICENSE.txt +0 -18
  42. package/src/index.ts.backup +0 -165
  43. package/src/services/chat/chat-history.service.ts.backup +0 -239
  44. package/src/services/terminal/command-analyzer.service.ts +0 -43
  45. package/src/services/terminal/context-menu.service.ts +0 -45
  46. package/src/services/terminal/hotkey.service.ts +0 -53
  47. package/webpack.config.js.backup +0 -57
@@ -6,7 +6,8 @@
6
6
  export enum MessageRole {
7
7
  USER = 'user',
8
8
  ASSISTANT = 'assistant',
9
- SYSTEM = 'system'
9
+ SYSTEM = 'system',
10
+ TOOL = 'tool' // 工具结果角色(部分 AI 需要)
10
11
  }
11
12
 
12
13
  // 聊天消息
@@ -140,7 +141,7 @@ export interface ValidationResult {
140
141
 
141
142
  // API消息接口(支持压缩标记)
142
143
  export interface ApiMessage {
143
- role: 'user' | 'assistant' | 'system';
144
+ role: 'user' | 'assistant' | 'system' | 'tool' | 'function';
144
145
  content: string | ContentBlock[];
145
146
  ts: number; // 时间戳(毫秒)
146
147
 
@@ -148,6 +149,11 @@ export interface ApiMessage {
148
149
  isSummary?: boolean; // 是否为摘要消息
149
150
  condenseId?: string; // 摘要ID
150
151
  condenseParent?: string; // 被哪个摘要压缩
152
+ summaryMeta?: { // 摘要元数据
153
+ originalMessageCount: number;
154
+ tokensCost: number;
155
+ compressionRatio: number;
156
+ };
151
157
 
152
158
  // 截断相关元数据
153
159
  isTruncationMarker?: boolean; // 是否为截断标记
@@ -245,6 +251,16 @@ export interface ExtendedChatMessage extends ChatMessage {
245
251
  tokenUsage?: TokenUsage;
246
252
  }
247
253
 
254
+ // 压缩后的检查点数据接口
255
+ export interface CompressedCheckpointData {
256
+ compressed: boolean;
257
+ compressionRatio: number;
258
+ originalSize: number;
259
+ compressedSize: number;
260
+ messages?: ApiMessage[]; // 可选,用于即时访问
261
+ messagesJson: string; // 压缩后的JSON字符串
262
+ }
263
+
248
264
  // 检查点接口
249
265
  export interface Checkpoint {
250
266
  id: string;
@@ -253,6 +269,11 @@ export interface Checkpoint {
253
269
  summary: string;
254
270
  createdAt: number; // 时间戳(毫秒)
255
271
  tokenUsage: TokenUsage;
272
+ compressedData?: CompressedCheckpointData; // 压缩数据(可选)
273
+
274
+ // 新增字段
275
+ tags?: string[]; // 标签列表
276
+ isArchived?: boolean; // 是否已归档
256
277
  }
257
278
 
258
279
  // ============================================================================
@@ -261,7 +282,7 @@ export interface Checkpoint {
261
282
 
262
283
  // 流式事件类型
263
284
  export interface StreamEvent {
264
- type: 'text_delta' | 'tool_use_start' | 'tool_use_delta' | 'tool_use_end' | 'message_end' | 'error';
285
+ type: 'text_delta' | 'tool_use_start' | 'tool_use_delta' | 'tool_use_end' | 'tool_result' | 'tool_error' | 'message_end' | 'error';
265
286
  // 文本增量
266
287
  textDelta?: string;
267
288
  // 工具调用(完整时才有)
@@ -270,8 +291,140 @@ export interface StreamEvent {
270
291
  name: string;
271
292
  input: any;
272
293
  };
294
+ // 工具结果(tool_result 事件)
295
+ result?: {
296
+ tool_use_id: string;
297
+ content: string;
298
+ is_error?: boolean;
299
+ };
273
300
  // 错误信息
274
301
  error?: string;
275
302
  // 最终消息(message_end 时)
276
303
  message?: ChatMessage;
277
304
  }
305
+
306
+ // ============================================================================
307
+ // Agent 循环相关类型定义
308
+ // ============================================================================
309
+
310
+ // 工具调用接口
311
+ export interface ToolCall {
312
+ id: string;
313
+ name: string;
314
+ input: any;
315
+ }
316
+
317
+ // 工具结果接口
318
+ export interface ToolResult {
319
+ tool_use_id: string;
320
+ name?: string; // 工具名称
321
+ content: string;
322
+ is_error?: boolean;
323
+ }
324
+
325
+ // Agent 事件类型
326
+ export type AgentEventType =
327
+ | 'text_delta' // 文本增量
328
+ | 'tool_use_start' // 工具开始
329
+ | 'tool_use_end' // 工具调用结束(收集参数)
330
+ | 'tool_executing' // 工具正在执行
331
+ | 'tool_executed' // 工具执行完成(带结果)
332
+ | 'tool_error' // 工具执行错误
333
+ | 'round_start' // 新一轮开始
334
+ | 'round_end' // 一轮结束
335
+ | 'agent_complete' // Agent 循环完成
336
+ | 'error'; // 错误
337
+
338
+ // Agent 流式事件
339
+ export interface AgentStreamEvent {
340
+ type: AgentEventType;
341
+
342
+ // text_delta 事件
343
+ textDelta?: string;
344
+
345
+ // 工具相关事件
346
+ toolCall?: {
347
+ id: string;
348
+ name: string;
349
+ input: any;
350
+ };
351
+
352
+ // tool_executed/tool_error 事件
353
+ toolResult?: {
354
+ tool_use_id: string;
355
+ content: string;
356
+ is_error?: boolean;
357
+ duration?: number;
358
+ };
359
+
360
+ // round_start/round_end 事件
361
+ round?: number;
362
+
363
+ // agent_complete 事件
364
+ reason?: TerminationReason;
365
+ totalRounds?: number;
366
+ terminationMessage?: string; // 可选的终止详情消息
367
+
368
+ // error 事件
369
+ error?: string;
370
+
371
+ // message_end 保留
372
+ message?: ChatMessage;
373
+ }
374
+
375
+ // Agent 循环配置
376
+ export interface AgentLoopConfig {
377
+ maxRounds?: number; // 最大轮数,默认 15
378
+ timeoutMs?: number; // 默认 120000 (2分钟)
379
+ repeatThreshold?: number; // 默认 3 次
380
+ failureThreshold?: number; // 默认 2 次
381
+ enableTaskComplete?: boolean; // 默认 true
382
+ onRoundStart?: (round: number) => void;
383
+ onRoundEnd?: (round: number) => void;
384
+ onAgentComplete?: (reason: string, totalRounds: number) => void;
385
+ }
386
+
387
+ // ============================================================================
388
+ // 智能 Agent 终止相关类型定义
389
+ // ============================================================================
390
+
391
+ // 终止原因枚举
392
+ export type TerminationReason =
393
+ | 'task_complete' // AI 主动调用 task_complete 工具
394
+ | 'no_tools' // 本轮无工具调用
395
+ | 'summarizing' // 检测到 AI 正在总结
396
+ | 'repeated_tool' // 重复调用相同工具
397
+ | 'high_failure_rate' // 连续失败率过高
398
+ | 'timeout' // 总时间超时
399
+ | 'max_rounds' // 达到最大轮数(安全保底)
400
+ | 'user_cancel'; // 用户取消
401
+
402
+ // Agent 状态追踪
403
+ export interface AgentState {
404
+ currentRound: number;
405
+ startTime: number;
406
+ toolCallHistory: ToolCallRecord[];
407
+ lastAiResponse: string;
408
+ isActive: boolean;
409
+ }
410
+
411
+ // 工具调用记录
412
+ export interface ToolCallRecord {
413
+ name: string;
414
+ input: any;
415
+ inputHash: string; // 用于快速比较
416
+ success: boolean;
417
+ timestamp: number;
418
+ }
419
+
420
+ // 终止检测结果
421
+ export interface TerminationResult {
422
+ shouldTerminate: boolean;
423
+ reason: TerminationReason;
424
+ message?: string;
425
+ }
426
+
427
+ // 扩展 ToolResult 添加任务完成标记
428
+ export interface ExtendedToolResult extends ToolResult {
429
+ isTaskComplete?: boolean; // 特殊标记:task_complete 工具调用
430
+ }
@@ -2,6 +2,7 @@
2
2
  * AI提供商相关类型定义
3
3
  */
4
4
 
5
+ import { Observable } from 'rxjs';
5
6
  import { ProviderCapability, HealthStatus, ValidationResult } from './ai.types';
6
7
  import { ChatRequest, ChatResponse, CommandRequest, CommandResponse, ExplainRequest, ExplainResponse, AnalysisRequest, AnalysisResponse } from './ai.types';
7
8
 
@@ -9,14 +10,18 @@ import { ChatRequest, ChatResponse, CommandRequest, CommandResponse, ExplainRequ
9
10
  export { ProviderCapability, HealthStatus, ValidationResult };
10
11
  export { ChatRequest, ChatResponse, CommandRequest, CommandResponse, ExplainRequest, ExplainResponse, AnalysisRequest, AnalysisResponse };
11
12
 
12
- // 认证配置
13
+ // ==================== 认证配置 ====================
14
+
15
+ export type AuthType = 'apiKey' | 'bearer' | 'basic' | 'oauth' | 'none';
16
+
13
17
  export interface AuthConfig {
14
- type: 'apiKey' | 'bearer' | 'basic' | 'oauth' | 'none';
18
+ type: AuthType;
15
19
  credentials: Record<string, string>;
16
20
  requiresEncryption?: boolean;
17
21
  }
18
22
 
19
- // 提供商配置
23
+ // ==================== 提供商配置 ====================
24
+
20
25
  export interface ProviderConfig {
21
26
  name: string;
22
27
  displayName: string;
@@ -32,111 +37,234 @@ export interface ProviderConfig {
32
37
  contextWindow?: number; // 供应商上下文窗口限制
33
38
  }
34
39
 
35
- // 提供商信息
36
- export interface ProviderInfo {
37
- name: string;
38
- displayName: string;
39
- version: string;
40
- description: string;
41
- capabilities: ProviderCapability[];
40
+ // 提供商默认配置
41
+ export interface ProviderDefaults {
42
+ baseURL: string;
43
+ model: string;
44
+ maxTokens: number;
45
+ temperature: number;
46
+ timeout: number;
47
+ retries: number;
48
+ contextWindow: number;
42
49
  authConfig: AuthConfig;
43
- supportedModels: string[];
44
- pricing?: {
45
- type: 'free' | 'paid' | 'freemium';
46
- currency: string;
47
- unit: string;
48
- costPerUnit?: number;
49
- };
50
- documentation?: string;
51
50
  }
52
51
 
53
- // 基础AI提供商接口
54
- export abstract class BaseAiProvider {
55
- abstract readonly name: string;
56
- abstract readonly displayName: string;
57
- abstract readonly capabilities: ProviderCapability[];
58
- abstract readonly authConfig: AuthConfig;
59
-
60
- protected config: ProviderConfig | null = null;
61
-
62
- /**
63
- * 配置提供商
64
- */
65
- abstract configure(config: ProviderConfig): void;
66
-
67
- /**
68
- * 聊天功能
69
- */
70
- abstract chat(request: ChatRequest): Promise<ChatResponse>;
71
-
72
- /**
73
- * 生成命令
74
- */
75
- abstract generateCommand(request: CommandRequest): Promise<CommandResponse>;
52
+ // 所有已知提供商及其默认配置
53
+ export const PROVIDER_DEFAULTS: Record<string, ProviderDefaults> = {
54
+ openai: {
55
+ baseURL: 'https://api.openai.com/v1',
56
+ model: 'gpt-4',
57
+ maxTokens: 1000,
58
+ temperature: 0.7,
59
+ timeout: 30000,
60
+ retries: 3,
61
+ contextWindow: 128000,
62
+ authConfig: { type: 'bearer', credentials: {} }
63
+ },
64
+ anthropic: {
65
+ baseURL: 'https://api.anthropic.com',
66
+ model: 'claude-3-sonnet',
67
+ maxTokens: 1000,
68
+ temperature: 1.0,
69
+ timeout: 30000,
70
+ retries: 3,
71
+ contextWindow: 200000,
72
+ authConfig: { type: 'bearer', credentials: {} }
73
+ },
74
+ minimax: {
75
+ baseURL: 'https://api.minimaxi.com/anthropic',
76
+ model: 'MiniMax-M2',
77
+ maxTokens: 1000,
78
+ temperature: 1.0,
79
+ timeout: 30000,
80
+ retries: 3,
81
+ contextWindow: 128000,
82
+ authConfig: { type: 'bearer', credentials: {} }
83
+ },
84
+ glm: {
85
+ baseURL: 'https://open.bigmodel.cn/api/anthropic',
86
+ model: 'glm-4.6',
87
+ maxTokens: 1000,
88
+ temperature: 0.95,
89
+ timeout: 30000,
90
+ retries: 3,
91
+ contextWindow: 128000,
92
+ authConfig: { type: 'bearer', credentials: {} }
93
+ },
94
+ ollama: {
95
+ baseURL: 'http://localhost:11434/v1',
96
+ model: 'llama3.1',
97
+ maxTokens: 1000,
98
+ temperature: 0.7,
99
+ timeout: 30000,
100
+ retries: 3,
101
+ contextWindow: 8192,
102
+ authConfig: { type: 'none', credentials: {} }
103
+ },
104
+ vllm: {
105
+ baseURL: 'http://localhost:8000/v1',
106
+ model: 'meta-llama/Llama-3.1-8B',
107
+ maxTokens: 1000,
108
+ temperature: 0.7,
109
+ timeout: 30000,
110
+ retries: 3,
111
+ contextWindow: 8192,
112
+ authConfig: { type: 'bearer', credentials: {} }
113
+ },
114
+ 'openai-compatible': {
115
+ baseURL: 'http://localhost:11434/v1',
116
+ model: 'gpt-3.5-turbo',
117
+ maxTokens: 1000,
118
+ temperature: 0.7,
119
+ timeout: 30000,
120
+ retries: 3,
121
+ contextWindow: 128000,
122
+ authConfig: { type: 'bearer', credentials: {} }
123
+ }
124
+ };
76
125
 
126
+ // 配置工具函数
127
+ export namespace ProviderConfigUtils {
77
128
  /**
78
- * 解释命令
129
+ * 使用默认值填充配置
79
130
  */
80
- abstract explainCommand(request: ExplainRequest): Promise<ExplainResponse>;
131
+ export function fillDefaults(config: Partial<ProviderConfig>, providerName: string): ProviderConfig {
132
+ const defaults = PROVIDER_DEFAULTS[providerName];
133
+ if (!defaults) {
134
+ throw new Error(`Unknown provider: ${providerName}`);
135
+ }
81
136
 
82
- /**
83
- * 分析结果
84
- */
85
- abstract analyzeResult(request: AnalysisRequest): Promise<AnalysisResponse>;
137
+ return {
138
+ name: config.name || providerName,
139
+ displayName: config.displayName || providerName,
140
+ apiKey: config.apiKey,
141
+ baseURL: config.baseURL || defaults.baseURL,
142
+ model: config.model || defaults.model,
143
+ maxTokens: config.maxTokens ?? defaults.maxTokens,
144
+ temperature: config.temperature ?? defaults.temperature,
145
+ timeout: config.timeout ?? defaults.timeout,
146
+ retries: config.retries ?? defaults.retries,
147
+ authConfig: config.authConfig || defaults.authConfig,
148
+ enabled: config.enabled ?? true,
149
+ contextWindow: config.contextWindow ?? defaults.contextWindow
150
+ };
151
+ }
86
152
 
87
153
  /**
88
- * 健康检查
154
+ * 检查配置是否完整(可用于API调用)
89
155
  */
90
- abstract healthCheck(): Promise<HealthStatus>;
156
+ export function isConfigComplete(config: ProviderConfig): boolean {
157
+ return !!(
158
+ config.name &&
159
+ config.displayName &&
160
+ // API key 不是必需的(如本地服务)
161
+ (config.apiKey || config.authConfig?.type === 'none') &&
162
+ config.baseURL
163
+ );
164
+ }
91
165
 
92
166
  /**
93
- * 验证配置
167
+ * 克隆配置(深拷贝,移除敏感信息)
94
168
  */
95
- abstract validateConfig(): ValidationResult;
169
+ export function cloneConfig(config: ProviderConfig, maskApiKey = true): ProviderConfig {
170
+ const clone = { ...config };
171
+ if (maskApiKey && clone.apiKey) {
172
+ clone.apiKey = '***MASKED***';
173
+ }
174
+ return clone;
175
+ }
96
176
 
97
177
  /**
98
- * 获取提供商信息
178
+ * 获取提供商默认配置
99
179
  */
100
- getInfo(): ProviderInfo {
101
- return {
102
- name: this.name,
103
- displayName: this.displayName,
104
- version: '1.0.0',
105
- description: `${this.displayName} AI Provider`,
106
- capabilities: this.capabilities,
107
- authConfig: this.authConfig,
108
- supportedModels: this.config?.model ? [this.config.model] : []
109
- };
180
+ export function getDefaults(providerName: string): ProviderDefaults | undefined {
181
+ return PROVIDER_DEFAULTS[providerName];
110
182
  }
111
183
 
112
184
  /**
113
- * 检查是否支持指定能力
185
+ * 获取所有已知提供商名称
114
186
  */
115
- supportsCapability(capability: ProviderCapability): boolean {
116
- return this.capabilities.includes(capability);
187
+ export function getKnownProviders(): string[] {
188
+ return Object.keys(PROVIDER_DEFAULTS);
117
189
  }
118
190
 
119
191
  /**
120
- * 获取当前配置
192
+ * 检查是否为已知提供商
121
193
  */
122
- getConfig(): ProviderConfig | null {
123
- return this.config;
194
+ export function isKnownProvider(name: string): boolean {
195
+ return name in PROVIDER_DEFAULTS;
124
196
  }
125
197
  }
126
198
 
127
- // 提供商管理器
199
+ // ==================== 提供商信息 ====================
200
+
201
+ export interface ProviderPricing {
202
+ type: 'free' | 'paid' | 'freemium';
203
+ currency: string;
204
+ unit: string;
205
+ costPerUnit?: number;
206
+ }
207
+
208
+ export interface ProviderInfo {
209
+ name: string;
210
+ displayName: string;
211
+ version: string;
212
+ description: string;
213
+ capabilities: ProviderCapability[];
214
+ authConfig: AuthConfig;
215
+ supportedModels: string[];
216
+ configured?: boolean;
217
+ lastHealthCheck?: { status: HealthStatus; timestamp: Date };
218
+ pricing?: ProviderPricing;
219
+ documentation?: string;
220
+ defaults?: ProviderDefaults;
221
+ }
222
+
223
+ // ==================== 基础AI提供商接口 ====================
224
+
225
+ export interface IBaseAiProvider {
226
+ readonly name: string;
227
+ readonly displayName: string;
228
+ readonly capabilities: ProviderCapability[];
229
+ readonly authConfig: AuthConfig;
230
+
231
+ // 配置与状态
232
+ configure(config: ProviderConfig): void;
233
+ getConfig(): ProviderConfig | null;
234
+ isConfigured(): boolean;
235
+ isEnabled(): boolean;
236
+
237
+ // 核心功能
238
+ chat(request: ChatRequest): Promise<ChatResponse>;
239
+ chatStream(request: ChatRequest): Observable<any>;
240
+ generateCommand(request: CommandRequest): Promise<CommandResponse>;
241
+ explainCommand(request: ExplainRequest): Promise<ExplainResponse>;
242
+ analyzeResult(request: AnalysisRequest): Promise<AnalysisResponse>;
243
+
244
+ // 健康与验证
245
+ healthCheck(): Promise<HealthStatus>;
246
+ validateConfig(): ValidationResult;
247
+
248
+ // 信息查询
249
+ getInfo(): ProviderInfo;
250
+ supportsCapability(capability: ProviderCapability): boolean;
251
+ }
252
+
253
+ // ==================== 提供商管理器 ====================
254
+
128
255
  export interface ProviderManager {
129
- registerProvider(provider: BaseAiProvider): void;
256
+ registerProvider(provider: IBaseAiProvider): void;
130
257
  unregisterProvider(name: string): void;
131
- getProvider(name: string): BaseAiProvider | undefined;
132
- getAllProviders(): BaseAiProvider[];
133
- getActiveProvider(): BaseAiProvider | undefined;
258
+ getProvider(name: string): IBaseAiProvider | undefined;
259
+ getAllProviders(): IBaseAiProvider[];
260
+ getActiveProvider(): IBaseAiProvider | undefined;
134
261
  setActiveProvider(name: string): boolean;
135
262
  getProviderInfo(name: string): ProviderInfo | undefined;
136
263
  getAllProviderInfo(): ProviderInfo[];
137
264
  }
138
265
 
139
- // 提供商事件
266
+ // ==================== 提供商事件 ====================
267
+
140
268
  export interface ProviderEvent {
141
269
  type: 'connected' | 'disconnected' | 'error' | 'config_changed' | 'health_changed';
142
270
  provider: string;
@@ -144,5 +272,8 @@ export interface ProviderEvent {
144
272
  data?: any;
145
273
  }
146
274
 
147
- // 提供商事件监听器
148
275
  export type ProviderEventListener = (event: ProviderEvent) => void;
276
+
277
+ // ==================== 便利类型 ====================
278
+
279
+ export type BaseAiProvider = IBaseAiProvider;