tabby-ai-assistant 1.0.5 → 1.0.6

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 (116) hide show
  1. package/dist/components/chat/ai-sidebar.component.d.ts +147 -0
  2. package/dist/components/chat/chat-interface.component.d.ts +38 -6
  3. package/dist/components/settings/general-settings.component.d.ts +6 -3
  4. package/dist/components/settings/provider-config.component.d.ts +25 -12
  5. package/dist/components/terminal/command-preview.component.d.ts +38 -0
  6. package/dist/index-full.d.ts +8 -0
  7. package/dist/index-minimal.d.ts +3 -0
  8. package/dist/index.d.ts +7 -3
  9. package/dist/index.js +1 -2
  10. package/dist/providers/tabby/ai-config.provider.d.ts +57 -5
  11. package/dist/providers/tabby/ai-hotkey.provider.d.ts +8 -14
  12. package/dist/providers/tabby/ai-toolbar-button.provider.d.ts +8 -9
  13. package/dist/services/chat/ai-sidebar.service.d.ts +89 -0
  14. package/dist/services/chat/chat-history.service.d.ts +78 -0
  15. package/dist/services/chat/chat-session.service.d.ts +57 -2
  16. package/dist/services/context/compaction.d.ts +90 -0
  17. package/dist/services/context/manager.d.ts +69 -0
  18. package/dist/services/context/memory.d.ts +116 -0
  19. package/dist/services/context/token-budget.d.ts +105 -0
  20. package/dist/services/core/ai-assistant.service.d.ts +40 -1
  21. package/dist/services/core/checkpoint.service.d.ts +130 -0
  22. package/dist/services/platform/escape-sequence.service.d.ts +132 -0
  23. package/dist/services/platform/platform-detection.service.d.ts +146 -0
  24. package/dist/services/providers/anthropic-provider.service.d.ts +5 -0
  25. package/dist/services/providers/base-provider.service.d.ts +6 -1
  26. package/dist/services/providers/glm-provider.service.d.ts +5 -0
  27. package/dist/services/providers/minimax-provider.service.d.ts +10 -1
  28. package/dist/services/providers/ollama-provider.service.d.ts +76 -0
  29. package/dist/services/providers/openai-compatible.service.d.ts +5 -0
  30. package/dist/services/providers/openai-provider.service.d.ts +5 -0
  31. package/dist/services/providers/vllm-provider.service.d.ts +82 -0
  32. package/dist/services/terminal/buffer-analyzer.service.d.ts +128 -0
  33. package/dist/services/terminal/terminal-manager.service.d.ts +185 -0
  34. package/dist/services/terminal/terminal-tools.service.d.ts +79 -0
  35. package/dist/types/ai.types.d.ts +92 -0
  36. package/dist/types/provider.types.d.ts +1 -1
  37. package/package.json +7 -10
  38. package/src/components/chat/ai-sidebar.component.ts +945 -0
  39. package/src/components/chat/chat-input.component.html +9 -24
  40. package/src/components/chat/chat-input.component.scss +3 -2
  41. package/src/components/chat/chat-interface.component.html +77 -69
  42. package/src/components/chat/chat-interface.component.scss +54 -4
  43. package/src/components/chat/chat-interface.component.ts +250 -34
  44. package/src/components/chat/chat-settings.component.scss +4 -4
  45. package/src/components/chat/chat-settings.component.ts +22 -11
  46. package/src/components/common/error-message.component.html +15 -0
  47. package/src/components/common/error-message.component.scss +77 -0
  48. package/src/components/common/error-message.component.ts +2 -96
  49. package/src/components/common/loading-spinner.component.html +4 -0
  50. package/src/components/common/loading-spinner.component.scss +57 -0
  51. package/src/components/common/loading-spinner.component.ts +2 -63
  52. package/src/components/security/consent-dialog.component.html +22 -0
  53. package/src/components/security/consent-dialog.component.scss +34 -0
  54. package/src/components/security/consent-dialog.component.ts +2 -55
  55. package/src/components/security/password-prompt.component.html +19 -0
  56. package/src/components/security/password-prompt.component.scss +30 -0
  57. package/src/components/security/password-prompt.component.ts +2 -54
  58. package/src/components/security/risk-confirm-dialog.component.html +8 -12
  59. package/src/components/security/risk-confirm-dialog.component.scss +8 -5
  60. package/src/components/security/risk-confirm-dialog.component.ts +6 -6
  61. package/src/components/settings/ai-settings-tab.component.html +16 -20
  62. package/src/components/settings/ai-settings-tab.component.scss +8 -5
  63. package/src/components/settings/ai-settings-tab.component.ts +12 -12
  64. package/src/components/settings/general-settings.component.html +8 -17
  65. package/src/components/settings/general-settings.component.scss +6 -3
  66. package/src/components/settings/general-settings.component.ts +62 -22
  67. package/src/components/settings/provider-config.component.html +19 -39
  68. package/src/components/settings/provider-config.component.scss +182 -39
  69. package/src/components/settings/provider-config.component.ts +119 -7
  70. package/src/components/settings/security-settings.component.scss +1 -1
  71. package/src/components/terminal/ai-toolbar-button.component.html +8 -0
  72. package/src/components/terminal/ai-toolbar-button.component.scss +20 -0
  73. package/src/components/terminal/ai-toolbar-button.component.ts +2 -30
  74. package/src/components/terminal/command-preview.component.html +61 -0
  75. package/src/components/terminal/command-preview.component.scss +72 -0
  76. package/src/components/terminal/command-preview.component.ts +127 -140
  77. package/src/components/terminal/command-suggestion.component.html +23 -0
  78. package/src/components/terminal/command-suggestion.component.scss +55 -0
  79. package/src/components/terminal/command-suggestion.component.ts +2 -77
  80. package/src/index-minimal.ts +32 -0
  81. package/src/index.ts +94 -11
  82. package/src/index.ts.backup +165 -0
  83. package/src/providers/tabby/ai-config.provider.ts +60 -51
  84. package/src/providers/tabby/ai-hotkey.provider.ts +23 -39
  85. package/src/providers/tabby/ai-settings-tab.provider.ts +2 -2
  86. package/src/providers/tabby/ai-toolbar-button.provider.ts +29 -24
  87. package/src/services/chat/ai-sidebar.service.ts +258 -0
  88. package/src/services/chat/chat-history.service.ts +308 -0
  89. package/src/services/chat/chat-history.service.ts.backup +239 -0
  90. package/src/services/chat/chat-session.service.ts +276 -3
  91. package/src/services/context/compaction.ts +483 -0
  92. package/src/services/context/manager.ts +442 -0
  93. package/src/services/context/memory.ts +519 -0
  94. package/src/services/context/token-budget.ts +422 -0
  95. package/src/services/core/ai-assistant.service.ts +280 -5
  96. package/src/services/core/ai-provider-manager.service.ts +2 -2
  97. package/src/services/core/checkpoint.service.ts +619 -0
  98. package/src/services/platform/escape-sequence.service.ts +499 -0
  99. package/src/services/platform/platform-detection.service.ts +494 -0
  100. package/src/services/providers/anthropic-provider.service.ts +28 -1
  101. package/src/services/providers/base-provider.service.ts +7 -1
  102. package/src/services/providers/glm-provider.service.ts +28 -1
  103. package/src/services/providers/minimax-provider.service.ts +209 -11
  104. package/src/services/providers/ollama-provider.service.ts +445 -0
  105. package/src/services/providers/openai-compatible.service.ts +9 -0
  106. package/src/services/providers/openai-provider.service.ts +9 -0
  107. package/src/services/providers/vllm-provider.service.ts +463 -0
  108. package/src/services/security/risk-assessment.service.ts +6 -2
  109. package/src/services/terminal/buffer-analyzer.service.ts +594 -0
  110. package/src/services/terminal/terminal-manager.service.ts +748 -0
  111. package/src/services/terminal/terminal-tools.service.ts +441 -0
  112. package/src/styles/ai-assistant.scss +78 -6
  113. package/src/types/ai.types.ts +144 -0
  114. package/src/types/provider.types.ts +1 -1
  115. package/tsconfig.json +9 -9
  116. package/webpack.config.js +28 -6
@@ -1,8 +1,9 @@
1
1
  import { Injectable } from '@angular/core';
2
+ import { Observable, Observer } from 'rxjs';
2
3
  import { Anthropic } from '@anthropic-ai/sdk';
3
4
  import { BaseAiProvider } from './base-provider.service';
4
5
  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 { ChatRequest, ChatResponse, CommandRequest, CommandResponse, ExplainRequest, ExplainResponse, AnalysisRequest, AnalysisResponse, MessageRole, StreamEvent } from '../../types/ai.types';
6
7
  import { LoggerService } from '../core/logger.service';
7
8
 
8
9
  /**
@@ -79,14 +80,23 @@ export class MinimaxProviderService extends BaseAiProvider {
79
80
 
80
81
  try {
81
82
  const response = await this.withRetry(async () => {
82
- const result = await this.client!.messages.create({
83
+ // 构建请求参数
84
+ const createParams: any = {
83
85
  model: this.config?.model || 'MiniMax-M2',
84
86
  max_tokens: request.maxTokens || 1000,
85
87
  system: request.systemPrompt || this.getDefaultSystemPrompt(),
86
88
  messages: this.transformMessages(request.messages),
87
89
  temperature: request.temperature || 1.0,
88
90
  stream: request.stream || false
89
- });
91
+ };
92
+
93
+ // 如果有工具定义,添加到请求中
94
+ if (request.tools && request.tools.length > 0) {
95
+ createParams.tools = request.tools;
96
+ this.logger.info('Adding tools to request', { toolCount: request.tools.length });
97
+ }
98
+
99
+ const result = await this.client!.messages.create(createParams);
90
100
 
91
101
  this.logResponse(result);
92
102
  return result;
@@ -100,6 +110,109 @@ export class MinimaxProviderService extends BaseAiProvider {
100
110
  }
101
111
  }
102
112
 
113
+ /**
114
+ * 流式聊天功能
115
+ */
116
+ chatStream(request: ChatRequest): Observable<StreamEvent> {
117
+ return new Observable<StreamEvent>((subscriber: Observer<StreamEvent>) => {
118
+ if (!this.client) {
119
+ subscriber.error(new Error('Minimax client not initialized'));
120
+ return;
121
+ }
122
+
123
+ this.logRequest(request);
124
+
125
+ const runStream = async () => {
126
+ try {
127
+ // 注意:SDK 类型定义可能不包含 tools,但 API 实际支持
128
+ // 使用 as any 绕过类型检查
129
+ const stream = (this.client!.messages.stream as any)({
130
+ model: this.config?.model || 'MiniMax-M2',
131
+ max_tokens: request.maxTokens || 1000,
132
+ system: request.systemPrompt || this.getDefaultSystemPrompt(),
133
+ messages: this.transformMessages(request.messages),
134
+ temperature: request.temperature || 1.0,
135
+ tools: request.tools // 流式 API 支持工具调用
136
+ });
137
+
138
+ // 累积工具调用数据
139
+ let currentToolId = '';
140
+ let currentToolName = '';
141
+ let currentToolInput = '';
142
+
143
+ for await (const event of stream) {
144
+ if (event.type === 'content_block_delta') {
145
+ const delta = event.delta as any;
146
+ // 文本增量
147
+ if (delta.type === 'text_delta') {
148
+ subscriber.next({
149
+ type: 'text_delta',
150
+ textDelta: delta.text
151
+ });
152
+ }
153
+ // 工具输入增量
154
+ else if (delta.type === 'input_json_delta') {
155
+ currentToolInput += delta.partial_json || '';
156
+ }
157
+ }
158
+ // 工具调用开始
159
+ else if (event.type === 'content_block_start') {
160
+ const block = event.content_block as any;
161
+ if (block.type === 'tool_use') {
162
+ currentToolId = block.id;
163
+ currentToolName = block.name;
164
+ currentToolInput = '';
165
+ subscriber.next({ type: 'tool_use_start' });
166
+ }
167
+ }
168
+ // 内容块结束
169
+ else if (event.type === 'content_block_stop') {
170
+ // 如果有工具调用,发送完整的工具调用
171
+ if (currentToolId && currentToolName) {
172
+ let parsedInput = {};
173
+ try {
174
+ parsedInput = JSON.parse(currentToolInput || '{}');
175
+ } catch (e) {
176
+ this.logger.warn('Failed to parse tool input', { input: currentToolInput });
177
+ }
178
+ subscriber.next({
179
+ type: 'tool_use_end',
180
+ toolCall: {
181
+ id: currentToolId,
182
+ name: currentToolName,
183
+ input: parsedInput
184
+ }
185
+ });
186
+ // 重置
187
+ currentToolId = '';
188
+ currentToolName = '';
189
+ currentToolInput = '';
190
+ }
191
+ }
192
+ }
193
+
194
+ // 获取最终消息
195
+ const finalMessage = await stream.finalMessage();
196
+ subscriber.next({
197
+ type: 'message_end',
198
+ message: this.transformChatResponse(finalMessage).message
199
+ });
200
+ subscriber.complete();
201
+ } catch (error) {
202
+ this.logError(error, { request });
203
+ subscriber.error(new Error(`Minimax stream failed: ${error instanceof Error ? error.message : String(error)}`));
204
+ }
205
+ };
206
+
207
+ runStream();
208
+
209
+ // 返回取消订阅的处理函数
210
+ return () => {
211
+ this.logger.debug('Stream subscription cancelled');
212
+ };
213
+ });
214
+ }
215
+
103
216
  /**
104
217
  * 生成命令
105
218
  */
@@ -231,11 +344,26 @@ export class MinimaxProviderService extends BaseAiProvider {
231
344
 
232
345
  /**
233
346
  * 转换消息格式
347
+ * Anthropic API 支持两种格式:
348
+ * 1. 简单字符串: { role: 'user', content: 'Hello' }
349
+ * 2. 内容块数组: { role: 'user', content: [{ type: 'text', text: 'Hello' }] }
350
+ * 使用简单字符串格式以确保兼容性
234
351
  */
235
352
  protected transformMessages(messages: any[]): any[] {
236
- return messages.map(msg => ({
237
- role: msg.role,
238
- content: [{ type: 'text', text: msg.content }]
353
+ // 过滤掉系统消息(system role 不应该在 messages 数组中)
354
+ const filteredMessages = messages.filter(msg =>
355
+ msg.role === 'user' || msg.role === 'assistant'
356
+ );
357
+
358
+ this.logger.info('Transforming messages', {
359
+ originalCount: messages.length,
360
+ filteredCount: filteredMessages.length,
361
+ roles: messages.map(m => m.role)
362
+ });
363
+
364
+ return filteredMessages.map(msg => ({
365
+ role: msg.role === 'user' ? 'user' : 'assistant',
366
+ content: String(msg.content || '')
239
367
  }));
240
368
  }
241
369
 
@@ -243,22 +371,74 @@ export class MinimaxProviderService extends BaseAiProvider {
243
371
  * 转换聊天响应
244
372
  */
245
373
  private transformChatResponse(response: any): ChatResponse {
246
- const content = response.content[0];
247
- const text = content.type === 'text' ? content.text : '';
374
+ this.logger.info('Transforming chat response', {
375
+ hasContent: !!response?.content,
376
+ contentLength: response?.content?.length,
377
+ responseKeys: Object.keys(response || {})
378
+ });
379
+
380
+ let text = '';
381
+ let toolCalls: any[] = [];
382
+
383
+ // 尝试多种方式提取响应文本和工具调用
384
+ if (response?.content) {
385
+ if (Array.isArray(response.content)) {
386
+ // Anthropic 格式: content 是数组
387
+ for (const block of response.content) {
388
+ if (block.type === 'text') {
389
+ text += block.text || '';
390
+ } else if (block.type === 'tool_use') {
391
+ // 提取工具调用
392
+ toolCalls.push({
393
+ id: block.id,
394
+ name: block.name,
395
+ input: block.input
396
+ });
397
+ }
398
+ }
399
+ } else if (typeof response.content === 'string') {
400
+ // 直接是字符串
401
+ text = response.content;
402
+ }
403
+ } else if (response?.message?.content) {
404
+ // 某些 API 可能使用 message.content
405
+ text = response.message.content;
406
+ } else if (response?.choices?.[0]?.message?.content) {
407
+ // OpenAI 兼容格式
408
+ text = response.choices[0].message.content;
409
+ }
248
410
 
249
- return {
411
+ this.logger.info('Extracted response', {
412
+ textLength: text.length,
413
+ textPreview: text.substring(0, 100),
414
+ toolCallCount: toolCalls.length
415
+ });
416
+
417
+ if (!text && toolCalls.length === 0) {
418
+ this.logger.warn('Empty response text and no tool calls, full response:', response);
419
+ }
420
+
421
+ const result: any = {
250
422
  message: {
251
423
  id: this.generateId(),
252
424
  role: MessageRole.ASSISTANT,
253
425
  content: text,
254
426
  timestamp: new Date()
255
427
  },
256
- usage: response.usage ? {
428
+ usage: response?.usage ? {
257
429
  promptTokens: response.usage.input_tokens || 0,
258
430
  completionTokens: response.usage.output_tokens || 0,
259
431
  totalTokens: (response.usage.input_tokens || 0) + (response.usage.output_tokens || 0)
260
432
  } : undefined
261
433
  };
434
+
435
+ // 如果有工具调用,添加到响应中
436
+ if (toolCalls.length > 0) {
437
+ result.toolCalls = toolCalls;
438
+ this.logger.info('Tool calls extracted', { toolCalls });
439
+ }
440
+
441
+ return result;
262
442
  }
263
443
 
264
444
  /**
@@ -422,6 +602,24 @@ export class MinimaxProviderService extends BaseAiProvider {
422
602
  * 获取默认系统提示
423
603
  */
424
604
  private getDefaultSystemPrompt(): string {
425
- return `你是一个专业的终端命令助手,擅长将自然语言转换为准确的命令,并提供详细的解释。`;
605
+ return `你是一个专业的终端命令助手,运行在 Tabby 终端中。
606
+
607
+ ## 核心能力
608
+ 你可以通过以下工具直接操作终端:
609
+ - write_to_terminal: 向终端写入并执行命令
610
+ - read_terminal_output: 读取终端输出
611
+ - get_terminal_list: 获取所有终端列表
612
+ - get_terminal_cwd: 获取当前工作目录
613
+
614
+ ## 重要规则
615
+ 1. 当用户请求执行命令(如"查看当前目录"、"列出文件"等),你必须使用 write_to_terminal 工具来执行
616
+ 2. 不要只是描述你"将要做什么",而是直接调用工具执行
617
+ 3. 执行命令后,使用 read_terminal_output 读取结果并报告给用户
618
+ 4. 如果不确定当前目录或终端状态,先使用 get_terminal_cwd 或 get_terminal_list 获取信息
619
+
620
+ ## 示例
621
+ 用户:"查看当前目录的文件"
622
+ 正确做法:调用 write_to_terminal 工具,参数 { "command": "dir", "execute": true }
623
+ 错误做法:仅回复文字"我将执行 dir 命令"`;
426
624
  }
427
625
  }