jarvis-ai-assistant 0.1.115__py3-none-any.whl → 0.1.117__py3-none-any.whl

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.

Potentially problematic release.


This version of jarvis-ai-assistant might be problematic. Click here for more details.

Files changed (51) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/{agent.py → jarvis_agent/__init__.py} +122 -203
  3. jarvis/jarvis_agent/output_handler.py +23 -0
  4. jarvis/jarvis_code_agent/code_agent.py +113 -100
  5. jarvis/jarvis_code_agent/file_select.py +28 -7
  6. jarvis/jarvis_code_agent/patch.py +80 -2
  7. jarvis/jarvis_code_agent/relevant_files.py +82 -42
  8. jarvis/jarvis_codebase/main.py +53 -25
  9. jarvis/jarvis_dev/main.py +719 -547
  10. jarvis/jarvis_lsp/cpp.py +1 -1
  11. jarvis/jarvis_lsp/go.py +1 -1
  12. jarvis/jarvis_lsp/registry.py +1 -1
  13. jarvis/jarvis_lsp/rust.py +1 -1
  14. jarvis/jarvis_multi_agent/__init__.py +170 -0
  15. jarvis/jarvis_platform/ai8.py +2 -2
  16. jarvis/jarvis_platform/base.py +14 -4
  17. jarvis/jarvis_platform/kimi.py +2 -2
  18. jarvis/jarvis_platform/ollama.py +1 -1
  19. jarvis/jarvis_platform/openai.py +1 -1
  20. jarvis/jarvis_platform/oyi.py +1 -1
  21. jarvis/jarvis_platform/registry.py +1 -1
  22. jarvis/jarvis_platform_manager/main.py +422 -6
  23. jarvis/jarvis_platform_manager/openai_test.py +139 -0
  24. jarvis/jarvis_rag/main.py +57 -20
  25. jarvis/jarvis_smart_shell/main.py +55 -29
  26. jarvis/jarvis_tools/ask_codebase.py +1 -1
  27. jarvis/jarvis_tools/ask_user.py +1 -1
  28. jarvis/jarvis_tools/chdir.py +1 -1
  29. jarvis/jarvis_tools/code_review.py +3 -3
  30. jarvis/jarvis_tools/create_code_agent.py +1 -1
  31. jarvis/jarvis_tools/create_sub_agent.py +2 -2
  32. jarvis/jarvis_tools/execute_shell.py +1 -1
  33. jarvis/jarvis_tools/file_operation.py +16 -14
  34. jarvis/jarvis_tools/git_commiter.py +2 -2
  35. jarvis/jarvis_tools/methodology.py +1 -1
  36. jarvis/jarvis_tools/rag.py +1 -1
  37. jarvis/jarvis_tools/read_code.py +19 -8
  38. jarvis/jarvis_tools/read_webpage.py +1 -1
  39. jarvis/jarvis_tools/registry.py +157 -31
  40. jarvis/jarvis_tools/search.py +1 -1
  41. jarvis/jarvis_tools/select_code_files.py +1 -1
  42. jarvis/{utils.py → jarvis_utils/__init__.py} +69 -53
  43. {jarvis_ai_assistant-0.1.115.dist-info → jarvis_ai_assistant-0.1.117.dist-info}/METADATA +1 -1
  44. jarvis_ai_assistant-0.1.117.dist-info/RECORD +65 -0
  45. {jarvis_ai_assistant-0.1.115.dist-info → jarvis_ai_assistant-0.1.117.dist-info}/WHEEL +1 -1
  46. {jarvis_ai_assistant-0.1.115.dist-info → jarvis_ai_assistant-0.1.117.dist-info}/entry_points.txt +1 -1
  47. jarvis/multi_agent.py +0 -76
  48. jarvis/utils/date_utils.py +0 -19
  49. jarvis_ai_assistant-0.1.115.dist-info/RECORD +0 -64
  50. {jarvis_ai_assistant-0.1.115.dist-info → jarvis_ai_assistant-0.1.117.dist-info}/LICENSE +0 -0
  51. {jarvis_ai_assistant-0.1.115.dist-info → jarvis_ai_assistant-0.1.117.dist-info}/top_level.txt +0 -0
jarvis/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Jarvis AI Assistant"""
2
2
 
3
- __version__ = "0.1.115"
3
+ __version__ = "0.1.117"
@@ -1,15 +1,16 @@
1
1
  import argparse
2
2
  import re
3
3
  import time
4
- from typing import Callable, Dict, List, Optional
4
+ from typing import Any, Callable, Dict, List, Optional, Tuple, Union
5
5
 
6
6
  from prompt_toolkit import prompt
7
7
  import yaml
8
8
 
9
+ from jarvis.jarvis_agent.output_handler import OutputHandler
9
10
  from jarvis.jarvis_platform.base import BasePlatform
10
11
  from jarvis.jarvis_platform.registry import PlatformRegistry
11
12
  from jarvis.jarvis_tools.registry import ToolRegistry
12
- from jarvis.utils import PrettyOutput, OutputType, get_context_token_count, is_auto_complete, is_execute_tool_confirm, is_need_summary, is_record_methodology, load_methodology, set_agent, delete_agent, get_max_token_count, get_multiline_input, init_env, is_use_methodology, make_agent_name, user_confirm
13
+ from jarvis.jarvis_utils import PrettyOutput, OutputType, get_context_token_count, is_auto_complete, is_execute_tool_confirm, is_need_summary, is_record_methodology, load_methodology, set_agent, delete_agent, get_max_token_count, get_multiline_input, init_env, is_use_methodology, make_agent_name, user_confirm
13
14
  import os
14
15
 
15
16
  class Agent:
@@ -25,54 +26,23 @@ class Agent:
25
26
  def __del__(self):
26
27
  delete_agent(self.name)
27
28
 
28
- def set_output_handler_before_tool(self, handler: List[Callable]):
29
- """Set handlers to process output before tool execution.
30
-
31
- Args:
32
- handler: List of callable functions to process output
33
- """
34
- self.output_handler_before_tool = handler
35
29
 
36
30
  def __init__(self,
37
31
  system_prompt: str,
38
32
  name: str = "Jarvis",
39
33
  description: str = "",
40
34
  is_sub_agent: bool = False,
41
- tool_registry: Optional[ToolRegistry|List[str]] = None,
42
- platform: Optional[BasePlatform]|Optional[str] = None,
35
+ platform: Union[Optional[BasePlatform], Optional[str]] = None,
43
36
  model_name: Optional[str] = None,
44
37
  summary_prompt: Optional[str] = None,
45
38
  auto_complete: Optional[bool] = None,
46
- output_handler_before_tool: Optional[List[Callable]] = None,
47
- output_handler_after_tool: Optional[List[Callable]] = None,
39
+ output_handler: List[OutputHandler] = [],
48
40
  input_handler: Optional[List[Callable]] = None,
49
41
  use_methodology: Optional[bool] = None,
50
42
  record_methodology: Optional[bool] = None,
51
43
  need_summary: Optional[bool] = None,
52
44
  max_context_length: Optional[int] = None,
53
- support_send_msg: bool = False,
54
45
  execute_tool_confirm: Optional[bool] = None):
55
- """Initialize an Agent instance.
56
-
57
- Args:
58
- system_prompt: The system prompt defining agent behavior
59
- name: Agent name, defaults to "Jarvis"
60
- description: Agent description, defaults to ""
61
- is_sub_agent: Whether this is a sub-agent
62
- tool_registry: Registry of available tools
63
- platform: AI platform to use
64
- summary_prompt: Template for generating summaries
65
- auto_complete: Whether to enable auto-completion
66
- output_handler_before_tool: Handlers to process output before tool execution
67
- output_handler_after_tool: Handlers to process output after tool execution
68
- use_methodology: Whether to use methodology
69
- record_methodology: Whether to record methodology
70
- need_summary: Whether to generate summaries
71
- max_context_length: Maximum context length
72
- support_send_msg: Whether to support sending messages
73
- execute_tool_confirm: Whether to confirm tool execution
74
- """
75
-
76
46
  self.name = make_agent_name(name)
77
47
  self.description = description
78
48
  # 初始化平台和模型
@@ -91,15 +61,7 @@ class Agent:
91
61
  self.model.set_model_name(model_name)
92
62
 
93
63
 
94
- # 初始化工具
95
- if tool_registry is not None:
96
- if isinstance(tool_registry, ToolRegistry):
97
- self.tool_registry = tool_registry
98
- elif isinstance(tool_registry, List):
99
- self.tool_registry = ToolRegistry()
100
- self.tool_registry.use_tools(tool_registry)
101
- else:
102
- self.tool_registry = ToolRegistry()
64
+ self.output_handler = output_handler
103
65
 
104
66
 
105
67
  self.record_methodology = record_methodology if record_methodology is not None else is_record_methodology()
@@ -111,10 +73,7 @@ class Agent:
111
73
  self.need_summary = need_summary if need_summary is not None else is_need_summary()
112
74
  self.input_handler = input_handler if input_handler is not None else []
113
75
  # Load configuration from environment variables
114
- self.output_handler_before_tool = output_handler_before_tool if output_handler_before_tool else []
115
- self.output_handler_after_tool = output_handler_after_tool if output_handler_after_tool else []
116
76
 
117
- self.support_send_msg = support_send_msg
118
77
 
119
78
  self.execute_tool_confirm = execute_tool_confirm if execute_tool_confirm is not None else is_execute_tool_confirm()
120
79
 
@@ -132,48 +91,38 @@ Please describe in concise bullet points, highlighting important information.
132
91
  self.max_token_count = max_context_length if max_context_length is not None else get_max_token_count()
133
92
  self.auto_complete = auto_complete if auto_complete is not None else is_auto_complete()
134
93
  welcome_message = f"{name} 初始化完成 - 使用 {self.model.name()} 模型"
135
- tools = self.tool_registry.get_all_tools()
136
- if tools:
137
- welcome_message += f"\n可用工具: \n{','.join([f'{tool['name']}' for tool in tools])}"
94
+
138
95
  PrettyOutput.print(welcome_message, OutputType.SYSTEM)
139
96
 
140
- tools_prompt = self.tool_registry.load_tools()
97
+ tool_prompt = """
98
+ # 🧰 Available Tools
99
+ The following tools are at your disposal:
100
+ """
141
101
 
142
- send_msg_prompt = ""
143
- if self.support_send_msg:
144
- send_msg_prompt = """
145
- ## Send Message Rules
146
-
147
- !!! CRITICAL ACTION RULES !!!
148
- You can ONLY perform ONE action per turn:
149
- - ENSURE USE ONLY ONE TOOL EVERY TURN (file_operation, ask_user, etc.)
150
- - OR SEND ONE MESSAGE TO ANOTHER AGENT
151
- - NEVER DO BOTH IN THE SAME TURN
152
-
153
- 2. Message Format:
154
- <SEND_MESSAGE>
155
- to: agent_name # Target agent name
156
- content: |
157
- message_content # Message content, multi-line must be separated by newlines
158
- </SEND_MESSAGE>
159
-
160
- 3. Message Handling:
161
- - After sending a message, WAIT for response
162
- - Process response before next action
163
- - Never send multiple messages at once
164
- - Never combine message with tool calls
165
-
166
- 4. If Multiple Actions Needed:
167
- a. Choose most important action first
168
- b. Wait for response/result
169
- c. Plan next action based on response
170
- d. Execute next action in new turn
171
-
172
- Remember:
173
- - First action will be executed
174
- - Additional actions will be IGNORED
175
- - Always process responses before new actions
176
- """
102
+ # 添加工具列表概览
103
+ tool_prompt += "\n## Tool List\n"
104
+ tool_prompt += ", ".join([handler.name() for handler in self.output_handler])
105
+
106
+ # 添加每个工具的详细说明
107
+ tool_prompt += "\n\n# 📝 Tool Details\n"
108
+ for handler in self.output_handler:
109
+ tool_prompt += f"\n## {handler.name()}\n"
110
+ # 获取工具的提示词并确保格式正确
111
+ handler_prompt = handler.prompt().strip()
112
+ # 调整缩进以保持层级结构
113
+ handler_prompt = "\n".join(" " + line if line.strip() else line
114
+ for line in handler_prompt.split("\n"))
115
+ tool_prompt += handler_prompt + "\n"
116
+
117
+ # 添加工具使用总结
118
+ tool_prompt += """
119
+ # ❗ Important Tool Usage Rules
120
+ 1. Use ONE tool at a time
121
+ 2. Follow each tool's format exactly
122
+ 3. Wait for tool results before next action
123
+ 4. Process results before new tool calls
124
+ 5. Request help if tool usage is unclear
125
+ """
177
126
 
178
127
  complete_prompt = ""
179
128
  if self.auto_complete:
@@ -183,69 +132,16 @@ Please describe in concise bullet points, highlighting important information.
183
132
  <!!!COMPLETE!!!>
184
133
  """
185
134
 
186
- additional_prompt = ""
187
- if not tools_prompt and send_msg_prompt:
188
- additional_prompt = """YOU MUST CALL ONE TOOL EVERY TURN, UNLESS YOU THINK THE TASK IS COMPLETED!!!"""
189
- if not send_msg_prompt and tools_prompt:
190
- additional_prompt = """YOU MUST SEND ONE MESSAGE EVERY TURN, UNLESS YOU THINK THE TASK IS COMPLETED!!!"""
191
- if send_msg_prompt and tools_prompt:
192
- additional_prompt = """YOU MUST CALL ONE TOOL OR SEND ONE MESSAGE EVERY TURN, UNLESS YOU THINK THE TASK IS COMPLETED!!!"""
193
-
194
135
  self.model.set_system_message(f"""
195
136
  {self.system_prompt}
196
137
 
197
- {tools_prompt}
198
-
199
- {send_msg_prompt}
138
+ {tool_prompt}
200
139
 
201
140
  {complete_prompt}
202
-
203
- {additional_prompt}
204
141
  """)
205
142
  self.first = True
206
143
 
207
- @staticmethod
208
- def _extract_send_msg(content: str) -> List[Dict]:
209
- """Extract send message from content.
210
-
211
- Args:
212
- content: The content containing send message
213
- """
214
- data = re.findall(r'<SEND_MESSAGE>(.*?)</SEND_MESSAGE>', content, re.DOTALL)
215
- ret = []
216
- for item in data:
217
- try:
218
- msg = yaml.safe_load(item)
219
- if 'to' in msg and 'content' in msg:
220
- ret.append(msg)
221
- except Exception as e:
222
- continue
223
- return ret
224
144
 
225
- @staticmethod
226
- def _extract_tool_calls(content: str) -> List[Dict]:
227
- """Extract tool calls from content.
228
-
229
- Args:
230
- content: The content containing tool calls
231
-
232
- Returns:
233
- List[Dict]: List of extracted tool calls with name and arguments
234
-
235
- Raises:
236
- Exception: If tool call is missing necessary fields
237
- """
238
- # Split content into lines
239
- data = re.findall(r'<TOOL_CALL>(.*?)</TOOL_CALL>', content, re.DOTALL)
240
- ret = []
241
- for item in data:
242
- try:
243
- msg = yaml.safe_load(item)
244
- if 'name' in msg and 'arguments' in msg:
245
- ret.append(msg)
246
- except Exception as e:
247
- continue
248
- return ret
249
145
 
250
146
  def _call_model(self, message: str) -> str:
251
147
  """Call the AI model with retry logic.
@@ -322,6 +218,21 @@ Please continue the task based on the above information.
322
218
  except Exception as e:
323
219
  PrettyOutput.print(f"总结对话历史失败: {str(e)}", OutputType.ERROR)
324
220
 
221
+ def _call_tools(self, response: str) -> Tuple[bool, Any]:
222
+ tool_list = []
223
+ for handler in self.output_handler:
224
+ if handler.can_handle(response):
225
+ tool_list.append(handler)
226
+ if len(tool_list) > 1:
227
+ PrettyOutput.print(f"操作失败:检测到多个操作。一次只能执行一个操作。尝试执行的操作:{', '.join([handler.name() for handler in tool_list])}", OutputType.ERROR)
228
+ return False, f"Action failed: Multiple actions detected. Please only perform one action at a time. Actions attempted: {', '.join([handler.name() for handler in tool_list])}"
229
+ if len(tool_list) == 0:
230
+ return False, ""
231
+ if not self.execute_tool_confirm or user_confirm(f"需要执行{tool_list[0].name()}确认执行?", True):
232
+ return tool_list[0].handle(response)
233
+ return False, ""
234
+
235
+
325
236
  def _complete_task(self) -> str:
326
237
  """Complete the current task and generate summary if needed.
327
238
 
@@ -349,13 +260,7 @@ Please continue the task based on the above information.
349
260
  self.prompt = analysis_prompt
350
261
  response = self._call_model(self.prompt)
351
262
 
352
- # 检查是否包含工具调用
353
- try:
354
- tool_calls = Agent._extract_tool_calls(response)
355
- if tool_calls:
356
- self.tool_registry.handle_tool_calls(tool_calls[0])
357
- except Exception as e:
358
- PrettyOutput.print(f"处理方法论生成失败: {str(e)}", OutputType.ERROR)
263
+ self._call_tools(response)
359
264
 
360
265
  except Exception as e:
361
266
  PrettyOutput.print(f"生成方法论失败: {str(e)}", OutputType.ERROR)
@@ -369,7 +274,7 @@ Please continue the task based on the above information.
369
274
  return "任务完成"
370
275
 
371
276
 
372
- def run(self, user_input: str, file_list: Optional[List[str]] = None) -> str|Dict:
277
+ def run(self, user_input: str, file_list: Optional[List[str]] = None) -> Any:
373
278
  """Process user input and execute the task.
374
279
 
375
280
  Args:
@@ -412,38 +317,10 @@ Please continue the task based on the above information.
412
317
  self.prompt = ""
413
318
  self.conversation_length += get_context_token_count(current_response)
414
319
 
415
- # Check for message first
416
- send_msg = Agent._extract_send_msg(current_response)
417
- tool_calls = Agent._extract_tool_calls(current_response)
418
-
419
- if len(send_msg) > 1:
420
- PrettyOutput.print(f"收到多个消息,违反规则,重试...", OutputType.WARNING)
421
- self.prompt += f"Only one message can be sent, but {len(send_msg)} messages were received. Please retry."
422
- continue
423
- if len(tool_calls) > 1:
424
- PrettyOutput.print(f"收到多个工具调用,违反规则,重试...", OutputType.WARNING)
425
- self.prompt += f"Only one tool call can be executed, but {len(tool_calls)} tool calls were received. Please retry."
426
- continue
427
- if send_msg and tool_calls:
428
- PrettyOutput.print(f"收到消息和工具调用,违反规则,重试...", OutputType.WARNING)
429
- self.prompt += f"Only one action can be performed, but both a message and a tool call were received. Please retry."
430
- continue
431
-
432
- if send_msg:
433
- return send_msg[0] # Return the first message
320
+ need_return, self.prompt = self._call_tools(current_response)
434
321
 
435
- # If no message, process tool calls
436
- for handler in self.output_handler_before_tool:
437
- self.prompt += handler(current_response)
438
-
439
- if tool_calls:
440
- if not self.execute_tool_confirm or user_confirm(f"执行工具调用: {tool_calls[0]['name']}?"):
441
- PrettyOutput.print("正在执行工具调用...", OutputType.PROGRESS)
442
- tool_result = self.tool_registry.handle_tool_calls(tool_calls[0])
443
- self.prompt += tool_result
444
-
445
- for handler in self.output_handler_after_tool:
446
- self.prompt += handler(current_response)
322
+ if need_return:
323
+ return self.prompt
447
324
 
448
325
  if self.prompt:
449
326
  continue
@@ -575,29 +452,69 @@ def _select_task(tasks: dict) -> str:
575
452
  PrettyOutput.print(f"选择任务失败: {str(e)}", OutputType.ERROR)
576
453
  continue
577
454
 
578
- origin_agent_system_prompt = """You are Jarvis, an AI assistant with powerful problem-solving capabilities.
579
-
580
- When users need to execute tasks, you will strictly follow these steps to handle problems:
581
- 1. Problem Restatement: Confirm understanding of the problem
582
- 2. Root Cause Analysis (only if needed for problem analysis tasks)
583
- 3. Set Objectives: Define achievable and verifiable goals
584
- 4. Generate Solutions: Create one or more actionable solutions
585
- 5. Evaluate Solutions: Select the optimal solution from multiple options
586
- 6. Create Action Plan: Based on available tools, create an action plan using PlantUML format for clear execution flow
587
- 7. Execute Action Plan: Execute one step at a time, **use at most one tool** (wait for tool execution results before proceeding)
588
- 8. Monitor and Adjust: If execution results don't match expectations, reflect and adjust the action plan, iterate previous steps
589
- 9. Methodology: If the current task has general applicability and valuable experience is gained, use methodology tools to record it for future similar problems
590
- 10. Auto check the task goal completion status: If the task goal is completed, use the task completion command to end the task
591
- 11. Task Completion: End the task using task completion command when finished
592
-
593
- Tip: Chat in user's language
594
-
595
- Methodology Template:
596
- 1. Problem Restatement
597
- 2. Optimal Solution
598
- 3. Optimal Solution Steps (exclude failed actions)
599
-
600
- -------------------------------------------------------------"""
455
+ origin_agent_system_prompt = """
456
+ # 🤖 Role Definition
457
+ You are Jarvis, an AI assistant with powerful problem-solving capabilities. You communicate in the user's language (if user speaks Chinese, respond in Chinese).
458
+
459
+ # 🎯 Core Responsibilities
460
+ - Process and analyze problems systematically
461
+ - Generate and execute actionable solutions
462
+ - Document methodologies for future reference
463
+ - Monitor and adjust execution plans
464
+ - Ensure task completion
465
+
466
+ # 🔄 Problem-Solving Workflow
467
+ 1. Problem Analysis
468
+ - Restate the problem to confirm understanding
469
+ - Analyze root causes (for problem analysis tasks)
470
+ - Define clear, achievable objectives
471
+
472
+ 2. Solution Design
473
+ - Generate multiple actionable solutions
474
+ - Evaluate and select optimal solution
475
+ - Create detailed action plan using PlantUML
476
+
477
+ 3. Execution
478
+ - Execute one step at a time
479
+ - Use only ONE tool per step
480
+ - Wait for tool results before proceeding
481
+ - Monitor results and adjust as needed
482
+
483
+ 4. Task Completion
484
+ - Verify goal completion
485
+ - Document methodology if valuable
486
+ - Use completion command to end task
487
+
488
+ # 📑 Methodology Template
489
+ ```markdown
490
+ # [Problem Title]
491
+ ## Problem Restatement
492
+ [Clear problem definition]
493
+
494
+ ## Optimal Solution
495
+ [Selected solution approach]
496
+
497
+ ## Solution Steps
498
+ 1. [Step 1]
499
+ 2. [Step 2]
500
+ 3. [Step 3]
501
+ ...
502
+ ```
503
+
504
+ # ⚖️ Operating Principles
505
+ - ONE tool per action
506
+ - Wait for results before next step
507
+ - Adjust plans based on feedback
508
+ - Document reusable solutions
509
+ - Use completion command to end tasks
510
+
511
+ # ❗ Important Rules
512
+ 1. Always use only ONE tool per action
513
+ 2. Always wait for tool execution results
514
+ 3. Always verify task completion
515
+ 4. Always communicate in user's language
516
+ 5. Always document valuable methodologies
517
+ """
601
518
 
602
519
  def main():
603
520
  """Jarvis main entry point"""
@@ -605,11 +522,13 @@ def main():
605
522
  init_env()
606
523
  parser = argparse.ArgumentParser(description='Jarvis AI assistant')
607
524
  parser.add_argument('-f', '--files', nargs='*', help='List of files to process')
525
+ parser.add_argument('-p', '--platform', type=str, help='Platform to use')
526
+ parser.add_argument('-m', '--model', type=str, help='Model to use')
608
527
  args = parser.parse_args()
609
528
 
610
529
  try:
611
530
  # 获取全局模型实例
612
- agent = Agent(system_prompt=origin_agent_system_prompt)
531
+ agent = Agent(system_prompt=origin_agent_system_prompt, platform=args.platform, model_name=args.model, output_handler=[ToolRegistry()])
613
532
 
614
533
  # 加载预定义任务
615
534
  tasks = _load_tasks()
@@ -0,0 +1,23 @@
1
+
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import Any, Tuple
5
+
6
+
7
+
8
+ class OutputHandler(ABC):
9
+ @abstractmethod
10
+ def handle(self, response: str) -> Tuple[bool, Any]:
11
+ pass
12
+
13
+ @abstractmethod
14
+ def can_handle(self, response: str) -> bool:
15
+ pass
16
+
17
+ @abstractmethod
18
+ def prompt(self) -> str:
19
+ pass
20
+
21
+ @abstractmethod
22
+ def name(self) -> str:
23
+ pass