auto-coder 0.1.267__py3-none-any.whl → 0.1.269__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 auto-coder might be problematic. Click here for more details.

@@ -0,0 +1,2635 @@
1
+ from itertools import product
2
+ from prompt_toolkit.formatted_text import HTML
3
+ from prompt_toolkit.shortcuts import radiolist_dialog
4
+ from prompt_toolkit import prompt
5
+ import os
6
+ import yaml
7
+ import json
8
+ import sys
9
+ import io
10
+ import uuid
11
+ import glob
12
+ import time
13
+ import hashlib
14
+ from contextlib import contextmanager
15
+ from typing import List, Dict, Any, Optional
16
+ from autocoder.common import AutoCoderArgs
17
+ from pydantic import BaseModel
18
+ from autocoder.common.result_manager import ResultManager
19
+ from autocoder.version import __version__
20
+ from autocoder.auto_coder import main as auto_coder_main
21
+ from autocoder.utils import get_last_yaml_file
22
+ from autocoder.index.symbols_utils import (
23
+ extract_symbols,
24
+ SymbolType,
25
+ )
26
+ import platform
27
+ import subprocess
28
+ from rich.console import Console
29
+ from rich.panel import Panel
30
+ from rich.table import Table
31
+ from rich.live import Live
32
+ from rich.text import Text
33
+ from rich.live import Live
34
+ from rich.markdown import Markdown
35
+ from byzerllm.utils.nontext import Image
36
+ import git
37
+ from autocoder.common import git_utils
38
+ from autocoder.chat_auto_coder_lang import get_message
39
+ from autocoder.agent.auto_guess_query import AutoGuessQuery
40
+ from autocoder.common.mcp_server import get_mcp_server, McpRequest, McpInstallRequest, McpRemoveRequest, McpListRequest, McpListRunningRequest, McpRefreshRequest
41
+ import byzerllm
42
+ from byzerllm.utils import format_str_jinja2
43
+ from autocoder.common.memory_manager import get_global_memory_file_paths
44
+ from autocoder import models as models_module
45
+ import shlex
46
+ from autocoder.utils.llms import get_single_llm
47
+ import pkg_resources
48
+ from autocoder.common.printer import Printer
49
+ from autocoder.utils.thread_utils import run_in_raw_thread
50
+ from autocoder.common.command_completer import CommandCompleter,FileSystemModel as CCFileSystemModel,MemoryConfig as CCMemoryModel
51
+ from autocoder.common.conf_validator import ConfigValidator
52
+
53
+ class SymbolItem(BaseModel):
54
+ symbol_name: str
55
+ symbol_type: SymbolType
56
+ file_name: str
57
+
58
+ class InitializeSystemRequest(BaseModel):
59
+ product_mode: str
60
+ skip_provider_selection: bool
61
+ debug: bool
62
+ quick: bool
63
+ lite: bool
64
+ pro: bool
65
+
66
+
67
+ if platform.system() == "Windows":
68
+ from colorama import init
69
+
70
+ init()
71
+
72
+
73
+ memory = {
74
+ "conversation": [],
75
+ "current_files": {"files": [], "groups": {}},
76
+ "conf": {},
77
+ "exclude_dirs": [],
78
+ "mode": "auto_detect", # 新增mode字段,默认为 auto_detect 模式
79
+ }
80
+
81
+ project_root = os.getcwd()
82
+
83
+ base_persist_dir = os.path.join(".auto-coder", "plugins", "chat-auto-coder")
84
+
85
+ defaut_exclude_dirs = [".git", "node_modules", "dist", "build", "__pycache__"]
86
+
87
+ commands = [
88
+ "/add_files",
89
+ "/remove_files",
90
+ "/list_files",
91
+ "/conf",
92
+ "/coding",
93
+ "/chat",
94
+ "/ask",
95
+ "/commit",
96
+ "/revert",
97
+ "/index/query",
98
+ "/index/build",
99
+ "/index/export",
100
+ "/index/import",
101
+ "/exclude_files",
102
+ "/help",
103
+ "/shell",
104
+ "/voice_input",
105
+ "/exit",
106
+ "/summon",
107
+ "/mode",
108
+ "/lib",
109
+ "/design",
110
+ "/mcp",
111
+ "/models",
112
+ "/auto",
113
+ "/conf/export",
114
+ "/conf/import",
115
+ "/exclude_dirs",
116
+ ]
117
+
118
+ def load_tokenizer():
119
+ from autocoder.rag.variable_holder import VariableHolder
120
+ from tokenizers import Tokenizer
121
+ try:
122
+ tokenizer_path = pkg_resources.resource_filename(
123
+ "autocoder", "data/tokenizer.json"
124
+ )
125
+ VariableHolder.TOKENIZER_PATH = tokenizer_path
126
+ VariableHolder.TOKENIZER_MODEL = Tokenizer.from_file(tokenizer_path)
127
+ except FileNotFoundError:
128
+ tokenizer_path = None
129
+
130
+
131
+ def configure_project_type():
132
+ from prompt_toolkit.lexers import PygmentsLexer
133
+ from pygments.lexers.markup import MarkdownLexer
134
+ from prompt_toolkit.formatted_text import HTML
135
+ from prompt_toolkit.shortcuts import print_formatted_text
136
+ from prompt_toolkit.styles import Style
137
+ from html import escape
138
+
139
+ style = Style.from_dict(
140
+ {
141
+ "info": "#ansicyan",
142
+ "warning": "#ansiyellow",
143
+ "input-area": "#ansigreen",
144
+ "header": "#ansibrightyellow bold",
145
+ }
146
+ )
147
+
148
+ def print_info(text):
149
+ print_formatted_text(HTML(f"<info>{escape(text)}</info>"), style=style)
150
+
151
+ def print_warning(text):
152
+ print_formatted_text(
153
+ HTML(f"<warning>{escape(text)}</warning>"), style=style)
154
+
155
+ def print_header(text):
156
+ print_formatted_text(
157
+ HTML(f"<header>{escape(text)}</header>"), style=style)
158
+
159
+ print_header(f"\n=== {get_message('project_type_config')} ===\n")
160
+ print_info(get_message("project_type_supports"))
161
+ print_info(get_message("language_suffixes"))
162
+ print_info(get_message("predefined_types"))
163
+ print_info(get_message("mixed_projects"))
164
+ print_info(get_message("examples"))
165
+
166
+ print_warning(f"{get_message('default_type')}\n")
167
+
168
+ project_type = prompt(
169
+ get_message("enter_project_type"), default="py", style=style
170
+ ).strip()
171
+
172
+ if project_type:
173
+ configure(f"project_type:{project_type}", skip_print=True)
174
+ configure("skip_build_index:false", skip_print=True)
175
+ print_info(f"\n{get_message('project_type_set')} {project_type}")
176
+ else:
177
+ print_info(f"\n{get_message('using_default_type')}")
178
+
179
+ print_warning(f"\n{get_message('change_setting_later')}:")
180
+ print_warning("/conf project_type:<new_type>\n")
181
+
182
+ return project_type
183
+
184
+
185
+ def initialize_system(args:InitializeSystemRequest):
186
+ from autocoder.utils.model_provider_selector import ModelProviderSelector
187
+ from autocoder import models as models_module
188
+ print(f"\n\033[1;34m{get_message('initializing')}\033[0m")
189
+
190
+ first_time = [False]
191
+ configure_success = [False]
192
+
193
+ def print_status(message, status):
194
+ if status == "success":
195
+ print(f"\033[32m✓ {message}\033[0m")
196
+ elif status == "warning":
197
+ print(f"\033[33m! {message}\033[0m")
198
+ elif status == "error":
199
+ print(f"\033[31m✗ {message}\033[0m")
200
+ else:
201
+ print(f" {message}")
202
+
203
+ def init_project():
204
+ if not os.path.exists(".auto-coder"):
205
+ first_time[0] = True
206
+ print_status(get_message("not_initialized"), "warning")
207
+ init_choice = input(
208
+ f" {get_message('init_prompt')}").strip().lower()
209
+ if init_choice == "y":
210
+ try:
211
+ subprocess.run(
212
+ ["auto-coder", "init", "--source_dir", "."], check=True
213
+ )
214
+ print_status(get_message("init_success"), "success")
215
+ except subprocess.CalledProcessError:
216
+ print_status(get_message("init_fail"), "error")
217
+ print_status(get_message("init_manual"), "warning")
218
+ exit(1)
219
+ else:
220
+ print_status(get_message("exit_no_init"), "warning")
221
+ exit(1)
222
+
223
+ if not os.path.exists(base_persist_dir):
224
+ os.makedirs(base_persist_dir, exist_ok=True)
225
+ print_status(get_message("created_dir").format(
226
+ base_persist_dir), "success")
227
+
228
+ if first_time[0]:
229
+ configure_project_type()
230
+ configure_success[0] = True
231
+
232
+ print_status(get_message("init_complete"), "success")
233
+
234
+ init_project()
235
+
236
+ if not args.skip_provider_selection and first_time[0]:
237
+ if args.product_mode == "lite":
238
+ ## 如果已经是配置过的项目,就无需再选择
239
+ if first_time[0]:
240
+ if not models_module.check_model_exists("v3_chat") or not models_module.check_model_exists("r1_chat"):
241
+ model_provider_selector = ModelProviderSelector()
242
+ model_provider_info = model_provider_selector.select_provider()
243
+ if model_provider_info is not None:
244
+ models_json_list = model_provider_selector.to_models_json(model_provider_info)
245
+ models_module.add_and_activate_models(models_json_list)
246
+
247
+ if args.product_mode == "pro":
248
+ # Check if Ray is running
249
+ print_status(get_message("checking_ray"), "")
250
+ ray_status = subprocess.run(
251
+ ["ray", "status"], capture_output=True, text=True)
252
+ if ray_status.returncode != 0:
253
+ print_status(get_message("ray_not_running"), "warning")
254
+ try:
255
+ subprocess.run(["ray", "start", "--head"], check=True)
256
+ print_status(get_message("ray_start_success"), "success")
257
+ except subprocess.CalledProcessError:
258
+ print_status(get_message("ray_start_fail"), "error")
259
+ return
260
+ else:
261
+ print_status(get_message("ray_running"), "success")
262
+
263
+ # Check if deepseek_chat model is available
264
+ print_status(get_message("checking_model"), "")
265
+ try:
266
+ result = subprocess.run(
267
+ ["easy-byzerllm", "chat", "v3_chat", "你好"],
268
+ capture_output=True,
269
+ text=True,
270
+ timeout=30,
271
+ )
272
+ if result.returncode == 0:
273
+ print_status(get_message("model_available"), "success")
274
+ init_project()
275
+ print_status(get_message("init_complete_final"), "success")
276
+ return
277
+ except subprocess.TimeoutExpired:
278
+ print_status(get_message("model_timeout"), "error")
279
+ except subprocess.CalledProcessError:
280
+ print_status(get_message("model_error"), "error")
281
+
282
+ # If deepseek_chat is not available
283
+ print_status(get_message("model_not_available"), "warning")
284
+ api_key = prompt(HTML(f"<b>{get_message('enter_api_key')} </b>"))
285
+
286
+ print_status(get_message("deploying_model").format("Deepseek官方"), "")
287
+ deploy_cmd = [
288
+ "byzerllm",
289
+ "deploy",
290
+ "--pretrained_model_type",
291
+ "saas/openai",
292
+ "--cpus_per_worker",
293
+ "0.001",
294
+ "--gpus_per_worker",
295
+ "0",
296
+ "--worker_concurrency",
297
+ "1000",
298
+ "--num_workers",
299
+ "1",
300
+ "--infer_params",
301
+ f"saas.base_url=https://api.deepseek.com/v1 saas.api_key={api_key} saas.model=deepseek-chat",
302
+ "--model",
303
+ "v3_chat",
304
+ ]
305
+
306
+ try:
307
+ subprocess.run(deploy_cmd, check=True)
308
+ print_status(get_message("deploy_complete"), "success")
309
+ except subprocess.CalledProcessError:
310
+ print_status(get_message("deploy_fail"), "error")
311
+ return
312
+
313
+
314
+ deploy_cmd = [
315
+ "byzerllm",
316
+ "deploy",
317
+ "--pretrained_model_type",
318
+ "saas/reasoning_openai",
319
+ "--cpus_per_worker",
320
+ "0.001",
321
+ "--gpus_per_worker",
322
+ "0",
323
+ "--worker_concurrency",
324
+ "1000",
325
+ "--num_workers",
326
+ "1",
327
+ "--infer_params",
328
+ f"saas.base_url=https://api.deepseek.com/v1 saas.api_key={api_key} saas.model=deepseek-reasoner",
329
+ "--model",
330
+ "r1_chat",
331
+ ]
332
+
333
+ try:
334
+ subprocess.run(deploy_cmd, check=True)
335
+ print_status(get_message("deploy_complete"), "success")
336
+ except subprocess.CalledProcessError:
337
+ print_status(get_message("deploy_fail"), "error")
338
+ return
339
+
340
+ # Validate the deployment
341
+ print_status(get_message("validating_deploy"), "")
342
+ try:
343
+ validation_result = subprocess.run(
344
+ ["easy-byzerllm", "chat", "v3_chat", "你好"],
345
+ capture_output=True,
346
+ text=True,
347
+ timeout=30,
348
+ check=True,
349
+ )
350
+ print_status(get_message("validation_success"), "success")
351
+ except (subprocess.TimeoutExpired, subprocess.CalledProcessError):
352
+ print_status(get_message("validation_fail"), "error")
353
+ print_status(get_message("manual_start"), "warning")
354
+ print_status("easy-byzerllm chat v3_chat 你好", "")
355
+
356
+ print_status(get_message("init_complete_final"), "success")
357
+ configure_success[0] = True
358
+
359
+ if first_time[0] and args.product_mode == "pro" and configure_success[0]:
360
+ configure(f"model:v3_chat", skip_print=True)
361
+ configure(f"chat_model:r1_chat", skip_print=True)
362
+ configure(f"generate_rerank_model:r1_chat", skip_print=True)
363
+ configure(f"code_model:v3_chat", skip_print=True)
364
+ configure(f"index_filter_model:r1_chat", skip_print=True)
365
+
366
+ if first_time[0] and args.product_mode == "lite" and models_module.check_model_exists("v3_chat"):
367
+ configure(f"model:v3_chat", skip_print=True)
368
+ configure(f"chat_model:r1_chat", skip_print=True)
369
+ configure(f"generate_rerank_model:r1_chat", skip_print=True)
370
+ configure(f"code_model:v3_chat", skip_print=True)
371
+ configure(f"index_filter_model:r1_chat", skip_print=True)
372
+
373
+
374
+ def convert_yaml_config_to_str(yaml_config):
375
+ yaml_content = yaml.safe_dump(
376
+ yaml_config,
377
+ allow_unicode=True,
378
+ default_flow_style=False,
379
+ default_style=None,
380
+ )
381
+ return yaml_content
382
+
383
+
384
+ def get_all_file_names_in_project() -> List[str]:
385
+
386
+ file_names = []
387
+ final_exclude_dirs = defaut_exclude_dirs + memory.get("exclude_dirs", [])
388
+ for root, dirs, files in os.walk(project_root, followlinks=True):
389
+ dirs[:] = [d for d in dirs if d not in final_exclude_dirs]
390
+ file_names.extend(files)
391
+ return file_names
392
+
393
+
394
+ def get_all_file_in_project() -> List[str]:
395
+
396
+ file_names = []
397
+ final_exclude_dirs = defaut_exclude_dirs + memory.get("exclude_dirs", [])
398
+ for root, dirs, files in os.walk(project_root, followlinks=True):
399
+ dirs[:] = [d for d in dirs if d not in final_exclude_dirs]
400
+ for file in files:
401
+ file_names.append(os.path.join(root, file))
402
+ return file_names
403
+
404
+
405
+ def get_all_file_in_project_with_dot() -> List[str]:
406
+ file_names = []
407
+ final_exclude_dirs = defaut_exclude_dirs + memory.get("exclude_dirs", [])
408
+ for root, dirs, files in os.walk(project_root, followlinks=True):
409
+ dirs[:] = [d for d in dirs if d not in final_exclude_dirs]
410
+ for file in files:
411
+ file_names.append(os.path.join(
412
+ root, file).replace(project_root, "."))
413
+ return file_names
414
+
415
+
416
+ def get_all_dir_names_in_project() -> List[str]:
417
+ dir_names = []
418
+ final_exclude_dirs = defaut_exclude_dirs + memory.get("exclude_dirs", [])
419
+ for root, dirs, files in os.walk(project_root, followlinks=True):
420
+ dirs[:] = [d for d in dirs if d not in final_exclude_dirs]
421
+ for dir in dirs:
422
+ dir_names.append(dir)
423
+ return dir_names
424
+
425
+
426
+ def find_files_in_project(patterns: List[str]) -> List[str]:
427
+ matched_files = []
428
+ final_exclude_dirs = defaut_exclude_dirs + memory.get("exclude_dirs", [])
429
+
430
+ for pattern in patterns:
431
+ if "*" in pattern or "?" in pattern:
432
+ for file_path in glob.glob(pattern, recursive=True):
433
+ if os.path.isfile(file_path):
434
+ abs_path = os.path.abspath(file_path)
435
+ if not any(
436
+ exclude_dir in abs_path.split(os.sep)
437
+ for exclude_dir in final_exclude_dirs
438
+ ):
439
+ matched_files.append(abs_path)
440
+ else:
441
+ is_added = False
442
+ # add files belongs to project
443
+ for root, dirs, files in os.walk(project_root, followlinks=True):
444
+ dirs[:] = [d for d in dirs if d not in final_exclude_dirs]
445
+ if pattern in files:
446
+ matched_files.append(os.path.join(root, pattern))
447
+ is_added = True
448
+ else:
449
+ for file in files:
450
+ _pattern = os.path.abspath(pattern)
451
+ if _pattern in os.path.join(root, file):
452
+ matched_files.append(os.path.join(root, file))
453
+ is_added = True
454
+ # add files not belongs to project
455
+ if not is_added:
456
+ matched_files.append(pattern)
457
+
458
+ return list(set(matched_files))
459
+
460
+
461
+ def convert_config_value(key, value):
462
+ field_info = AutoCoderArgs.model_fields.get(key)
463
+ if field_info:
464
+ if value.lower() in ["true", "false"]:
465
+ return value.lower() == "true"
466
+ elif "int" in str(field_info.annotation):
467
+ return int(value)
468
+ elif "float" in str(field_info.annotation):
469
+ return float(value)
470
+ else:
471
+ return value
472
+ else:
473
+ print(f"Invalid configuration key: {key}")
474
+ return None
475
+
476
+
477
+ @contextmanager
478
+ def redirect_stdout():
479
+ original_stdout = sys.stdout
480
+ sys.stdout = f = io.StringIO()
481
+ try:
482
+ yield f
483
+ finally:
484
+ sys.stdout = original_stdout
485
+
486
+
487
+ def configure(conf: str, skip_print=False):
488
+ printer = Printer()
489
+ parts = conf.split(None, 1)
490
+ if len(parts) == 2 and parts[0] in ["/drop", "/unset", "/remove"]:
491
+ key = parts[1].strip()
492
+ if key in memory["conf"]:
493
+ del memory["conf"][key]
494
+ save_memory()
495
+ printer.print_in_terminal("config_delete_success", style="green", key=key)
496
+ else:
497
+ printer.print_in_terminal("config_not_found", style="yellow", key=key)
498
+ else:
499
+ parts = conf.split(":", 1)
500
+ if len(parts) != 2:
501
+ printer.print_in_terminal("config_invalid_format", style="red")
502
+ return
503
+ key, value = parts
504
+ key = key.strip()
505
+ value = value.strip()
506
+ if not value:
507
+ printer.print_in_terminal("config_value_empty", style="red")
508
+ return
509
+ product_mode = memory["conf"].get("product_mode",None)
510
+ if product_mode:
511
+ ConfigValidator.validate(key, value, product_mode)
512
+ memory["conf"][key] = value
513
+ save_memory()
514
+ if not skip_print:
515
+ printer.print_in_terminal("config_set_success", style="green", key=key, value=value)
516
+
517
+ # word_completer = WordCompleter(commands)
518
+
519
+
520
+ def get_symbol_list() -> List[SymbolItem]:
521
+ list_of_symbols = []
522
+ index_file = os.path.join(".auto-coder", "index.json")
523
+
524
+ if os.path.exists(index_file):
525
+ with open(index_file, "r",encoding="utf-8") as file:
526
+ index_data = json.load(file)
527
+ else:
528
+ index_data = {}
529
+
530
+ for item in index_data.values():
531
+ symbols_str = item["symbols"]
532
+ module_name = item["module_name"]
533
+ info1 = extract_symbols(symbols_str)
534
+ for name in info1.classes:
535
+ list_of_symbols.append(
536
+ SymbolItem(
537
+ symbol_name=name,
538
+ symbol_type=SymbolType.CLASSES,
539
+ file_name=module_name,
540
+ )
541
+ )
542
+ for name in info1.functions:
543
+ list_of_symbols.append(
544
+ SymbolItem(
545
+ symbol_name=name,
546
+ symbol_type=SymbolType.FUNCTIONS,
547
+ file_name=module_name,
548
+ )
549
+ )
550
+ for name in info1.variables:
551
+ list_of_symbols.append(
552
+ SymbolItem(
553
+ symbol_name=name,
554
+ symbol_type=SymbolType.VARIABLES,
555
+ file_name=module_name,
556
+ )
557
+ )
558
+ return list_of_symbols
559
+
560
+
561
+ def save_memory():
562
+ with open(os.path.join(base_persist_dir, "memory.json"), "w",encoding="utf-8") as f:
563
+ json.dump(memory, f, indent=2, ensure_ascii=False)
564
+ load_memory()
565
+
566
+
567
+ def load_memory():
568
+ global memory
569
+ memory_path = os.path.join(base_persist_dir, "memory.json")
570
+ if os.path.exists(memory_path):
571
+ with open(memory_path, "r", encoding="utf-8") as f:
572
+ memory = json.load(f)
573
+ completer.update_current_files(memory["current_files"]["files"])
574
+
575
+ def get_memory():
576
+ global memory
577
+ return memory
578
+
579
+
580
+ completer = CommandCompleter(commands,
581
+ file_system_model=CCFileSystemModel(project_root=project_root,
582
+ defaut_exclude_dirs=defaut_exclude_dirs,
583
+ get_all_file_names_in_project=get_all_file_names_in_project,
584
+ get_all_file_in_project=get_all_file_in_project,
585
+ get_all_dir_names_in_project=get_all_dir_names_in_project,
586
+ get_all_file_in_project_with_dot=get_all_file_in_project_with_dot,
587
+ get_symbol_list=get_symbol_list
588
+ ),
589
+ memory_model=CCMemoryModel(memory=memory,
590
+ save_memory_func=save_memory))
591
+
592
+
593
+
594
+
595
+ def print_conf(content:Dict[str,Any]):
596
+ """Display configuration dictionary in a Rich table format with enhanced visual styling.
597
+
598
+ Args:
599
+ conf (Dict[str, Any]): Configuration dictionary to display
600
+ """
601
+ console = Console()
602
+
603
+ # Create a styled table with rounded borders
604
+ table = Table(
605
+ show_header=True,
606
+ header_style="bold magenta",
607
+ title=get_message("conf_title"),
608
+ title_style="bold blue",
609
+ border_style="blue",
610
+ show_lines=True
611
+ )
612
+
613
+ # Add columns with explicit width and alignment
614
+ table.add_column(get_message("conf_key"), style="cyan", justify="right", width=30, no_wrap=False)
615
+ table.add_column(get_message("conf_value"), style="green", justify="left", width=50, no_wrap=False)
616
+
617
+ # Sort keys for consistent display
618
+ for key in sorted(content.keys()):
619
+ value = content[key]
620
+ # Format value based on type
621
+ if isinstance(value, (dict, list)):
622
+ formatted_value = Text(json.dumps(value, indent=2), style="yellow")
623
+ elif isinstance(value, bool):
624
+ formatted_value = Text(str(value), style="bright_green" if value else "red")
625
+ elif isinstance(value, (int, float)):
626
+ formatted_value = Text(str(value), style="bright_cyan")
627
+ else:
628
+ formatted_value = Text(str(value), style="green")
629
+
630
+ table.add_row(str(key), formatted_value)
631
+
632
+ # Add padding and print with a panel
633
+ console.print(Panel(
634
+ table,
635
+ padding=(1, 2),
636
+ subtitle=f"[italic]{get_message('conf_subtitle')}[/italic]",
637
+ border_style="blue"
638
+ ))
639
+
640
+ def revert():
641
+ result_manager = ResultManager()
642
+ last_yaml_file = get_last_yaml_file("actions")
643
+ if last_yaml_file:
644
+ file_path = os.path.join("actions", last_yaml_file)
645
+
646
+ with redirect_stdout() as output:
647
+ auto_coder_main(["revert", "--file", file_path])
648
+ s = output.getvalue()
649
+ print(s, flush=True)
650
+ if "Successfully reverted changes" in s:
651
+ result_manager.append(content=s, meta={"action": "revert","success":False, "input":{
652
+ }})
653
+ print(
654
+ "Reverted the last chat action successfully. Remove the yaml file {file_path}"
655
+ )
656
+ os.remove(file_path)
657
+ else:
658
+ result_manager.append(content=s, meta={"action": "revert","success":False, "input":{
659
+ }})
660
+ else:
661
+ result_manager.append(content="No previous chat action found to revert.", meta={"action": "revert","success":False, "input":{
662
+ }})
663
+ print("No previous chat action found to revert.")
664
+
665
+
666
+ def add_files(args: List[str]):
667
+
668
+ result_manager = ResultManager()
669
+ if "groups" not in memory["current_files"]:
670
+ memory["current_files"]["groups"] = {}
671
+ if "groups_info" not in memory["current_files"]:
672
+ memory["current_files"]["groups_info"] = {}
673
+ if "current_groups" not in memory["current_files"]:
674
+ memory["current_files"]["current_groups"] = []
675
+ groups = memory["current_files"]["groups"]
676
+ groups_info = memory["current_files"]["groups_info"]
677
+
678
+ console = Console()
679
+ printer = Printer()
680
+
681
+ if not args:
682
+ printer.print_in_terminal("add_files_no_args", style="red")
683
+ result_manager.append(content=printer.get_message_from_key("add_files_no_args"),
684
+ meta={"action": "add_files","success":False, "input":{ "args": args}})
685
+ return
686
+
687
+ if args[0] == "/refresh":
688
+ completer.refresh_files()
689
+ load_memory()
690
+ console.print(
691
+ Panel("Refreshed file list.",
692
+ title="Files Refreshed", border_style="green")
693
+ )
694
+ result_manager.append(content="Files refreshed.",
695
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
696
+ return
697
+
698
+ if args[0] == "/group":
699
+ if len(args) == 1 or (len(args) == 2 and args[1] == "list"):
700
+ if not groups:
701
+ console.print(
702
+ Panel("No groups defined.", title="Groups",
703
+ border_style="yellow")
704
+ )
705
+ result_manager.append(content="No groups defined.",
706
+ meta={"action": "add_files","success":False, "input":{ "args": args}})
707
+ else:
708
+ table = Table(
709
+ title="Defined Groups",
710
+ show_header=True,
711
+ header_style="bold magenta",
712
+ show_lines=True,
713
+ )
714
+ table.add_column("Group Name", style="cyan", no_wrap=True)
715
+ table.add_column("Files", style="green")
716
+ table.add_column("Query Prefix", style="yellow")
717
+ table.add_column("Active", style="magenta")
718
+
719
+ for i, (group_name, files) in enumerate(groups.items()):
720
+ query_prefix = groups_info.get(group_name, {}).get(
721
+ "query_prefix", ""
722
+ )
723
+ is_active = (
724
+ "✓"
725
+ if group_name in memory["current_files"]["current_groups"]
726
+ else ""
727
+ )
728
+ table.add_row(
729
+ group_name,
730
+ "\n".join([os.path.relpath(f, project_root)
731
+ for f in files]),
732
+ query_prefix,
733
+ is_active,
734
+ end_section=(i == len(groups) - 1),
735
+ )
736
+ console.print(Panel(table, border_style="blue"))
737
+ result_manager.append(content="Defined groups.",
738
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
739
+ elif len(args) >= 2 and args[1] == "/reset":
740
+ memory["current_files"]["current_groups"] = []
741
+ console.print(
742
+ Panel(
743
+ "Active group names have been reset. If you want to clear the active files, you should use the command /remove_files /all.",
744
+ title="Groups Reset",
745
+ border_style="green",
746
+ )
747
+ )
748
+ result_manager.append(content="Active group names have been reset. If you want to clear the active files, you should use the command /remove_files /all.",
749
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
750
+ elif len(args) >= 3 and args[1] == "/add":
751
+ group_name = args[2]
752
+ groups[group_name] = memory["current_files"]["files"].copy()
753
+ console.print(
754
+ Panel(
755
+ f"Added group '{group_name}' with current files.",
756
+ title="Group Added",
757
+ border_style="green",
758
+ )
759
+ )
760
+ result_manager.append(content=f"Added group '{group_name}' with current files.",
761
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
762
+
763
+ elif len(args) >= 3 and args[1] == "/drop":
764
+ group_name = args[2]
765
+ if group_name in groups:
766
+ del memory["current_files"]["groups"][group_name]
767
+ if group_name in groups_info:
768
+ del memory["current_files"]["groups_info"][group_name]
769
+ if group_name in memory["current_files"]["current_groups"]:
770
+ memory["current_files"]["current_groups"].remove(
771
+ group_name)
772
+ console.print(
773
+ Panel(
774
+ f"Dropped group '{group_name}'.",
775
+ title="Group Dropped",
776
+ border_style="green",
777
+ )
778
+ )
779
+ result_manager.append(content=f"Dropped group '{group_name}'.",
780
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
781
+ else:
782
+ console.print(
783
+ Panel(
784
+ f"Group '{group_name}' not found.",
785
+ title="Error",
786
+ border_style="red",
787
+ )
788
+ )
789
+ result_manager.append(content=f"Group '{group_name}' not found.",
790
+ meta={"action": "add_files","success":False, "input":{ "args": args}})
791
+ elif len(args) == 3 and args[1] == "/set":
792
+ group_name = args[2]
793
+
794
+ def multiline_edit():
795
+ from prompt_toolkit.lexers import PygmentsLexer
796
+ from pygments.lexers.markup import MarkdownLexer
797
+ from prompt_toolkit.formatted_text import HTML
798
+ from prompt_toolkit.shortcuts import print_formatted_text
799
+
800
+ style = Style.from_dict(
801
+ {
802
+ "dialog": "bg:#88ff88",
803
+ "dialog frame.label": "bg:#ffffff #000000",
804
+ "dialog.body": "bg:#000000 #00ff00",
805
+ "dialog shadow": "bg:#00aa00",
806
+ }
807
+ )
808
+
809
+ print_formatted_text(
810
+ HTML(
811
+ "<b>Type Atom Group Desc (Prese [Esc] + [Enter] to finish.)</b><br/>"
812
+ )
813
+ )
814
+ text = prompt(
815
+ HTML("<ansicyan>║</ansicyan> "),
816
+ multiline=True,
817
+ lexer=PygmentsLexer(MarkdownLexer),
818
+ style=style,
819
+ wrap_lines=True,
820
+ prompt_continuation=HTML("<ansicyan>║</ansicyan> "),
821
+ rprompt=HTML("<ansicyan>║</ansicyan>"),
822
+ )
823
+ return text
824
+
825
+ query_prefix = multiline_edit()
826
+ if group_name in groups:
827
+ groups_info[group_name] = {"query_prefix": query_prefix}
828
+ console.print(
829
+ Panel(
830
+ f"Set Atom Group Desc for group '{group_name}'.",
831
+ title="Group Info Updated",
832
+ border_style="green",
833
+ )
834
+ )
835
+ else:
836
+ console.print(
837
+ Panel(
838
+ f"Group '{group_name}' not found.",
839
+ title="Error",
840
+ border_style="red",
841
+ )
842
+ )
843
+ elif len(args) >= 2:
844
+ # 支持多个组的合并,允许组名之间使用逗号或空格分隔
845
+ group_names = " ".join(args[1:]).replace(",", " ").split()
846
+ merged_files = set()
847
+ missing_groups = []
848
+ for group_name in group_names:
849
+ if group_name in groups:
850
+ merged_files.update(groups[group_name])
851
+ else:
852
+ missing_groups.append(group_name)
853
+
854
+ if missing_groups:
855
+ console.print(
856
+ Panel(
857
+ f"Group(s) not found: {', '.join(missing_groups)}",
858
+ title="Error",
859
+ border_style="red",
860
+ )
861
+ )
862
+ result_manager.append(content=f"Group(s) not found: {', '.join(missing_groups)}",
863
+ meta={"action": "add_files","success":False, "input":{ "args": args}})
864
+
865
+ if merged_files:
866
+ memory["current_files"]["files"] = list(merged_files)
867
+ memory["current_files"]["current_groups"] = [
868
+ name for name in group_names if name in groups
869
+ ]
870
+ console.print(
871
+ Panel(
872
+ f"Merged files from groups: {', '.join(group_names)}",
873
+ title="Files Merged",
874
+ border_style="green",
875
+ )
876
+ )
877
+ table = Table(
878
+ title="Current Files",
879
+ show_header=True,
880
+ header_style="bold magenta",
881
+ show_lines=True, # 这会在每行之间添加分割线
882
+ )
883
+ table.add_column("File", style="green")
884
+ for i, f in enumerate(memory["current_files"]["files"]):
885
+ table.add_row(
886
+ os.path.relpath(f, project_root),
887
+ end_section=(
888
+ i == len(memory["current_files"]["files"]) - 1
889
+ ), # 在最后一行之后不添加分割线
890
+ )
891
+ console.print(Panel(table, border_style="blue"))
892
+ console.print(
893
+ Panel(
894
+ f"Active groups: {', '.join(memory['current_files']['current_groups'])}",
895
+ title="Active Groups",
896
+ border_style="green",
897
+ )
898
+ )
899
+ result_manager.append(content=f"Active groups: {', '.join(memory['current_files']['current_groups'])}",
900
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
901
+ elif not missing_groups:
902
+ console.print(
903
+ Panel(
904
+ "No files in the specified groups.",
905
+ title="No Files Added",
906
+ border_style="yellow",
907
+ )
908
+ )
909
+ result_manager.append(content="No files in the specified groups.",
910
+ meta={"action": "add_files","success":False, "input":{ "args": args}})
911
+ else:
912
+ existing_files = memory["current_files"]["files"]
913
+ matched_files = find_files_in_project(args)
914
+
915
+ files_to_add = [f for f in matched_files if f not in existing_files]
916
+ if files_to_add:
917
+ memory["current_files"]["files"].extend(files_to_add)
918
+ table = Table(
919
+ title=get_message("add_files_added_files"),
920
+ show_header=True,
921
+ header_style="bold magenta",
922
+ show_lines=True, # 这会在每行之间添加分割线
923
+ )
924
+ table.add_column("File", style="green")
925
+ for i, f in enumerate(files_to_add):
926
+ table.add_row(
927
+ os.path.relpath(f, project_root),
928
+ end_section=(
929
+ i == len(files_to_add) - 1
930
+ ), # 在最后一行之后不添加分割线
931
+ )
932
+ console.print(Panel(table, border_style="green"))
933
+ result_manager.append(content=f"Added files: {', '.join(files_to_add)}",
934
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
935
+ else:
936
+ printer.print_in_terminal("add_files_matched", style="yellow")
937
+ result_manager.append(content=f"No files matched.",
938
+ meta={"action": "add_files","success":False, "input":{ "args": args}})
939
+
940
+ completer.update_current_files(memory["current_files"]["files"])
941
+ save_memory()
942
+
943
+
944
+ def remove_files(file_names: List[str]):
945
+ project_root = os.getcwd()
946
+ printer = Printer()
947
+ result_manager = ResultManager()
948
+
949
+ if "/all" in file_names:
950
+ memory["current_files"]["files"] = []
951
+ memory["current_files"]["current_groups"] = []
952
+ printer.print_in_terminal("remove_files_all", style="green")
953
+ result_manager.append(content="All files removed.",
954
+ meta={"action": "remove_files","success":True, "input":{ "file_names": file_names}})
955
+ else:
956
+ removed_files = []
957
+ for file in memory["current_files"]["files"]:
958
+ if os.path.basename(file) in file_names:
959
+ removed_files.append(file)
960
+ elif file in file_names:
961
+ removed_files.append(file)
962
+ for file in removed_files:
963
+ memory["current_files"]["files"].remove(file)
964
+
965
+ if removed_files:
966
+ table = Table(
967
+ show_header=True,
968
+ header_style="bold magenta"
969
+ )
970
+ table.add_column("File", style="green")
971
+ for f in removed_files:
972
+ table.add_row(os.path.relpath(f, project_root))
973
+
974
+ console = Console()
975
+ console.print(
976
+ Panel(table, border_style="green",
977
+ title=printer.get_message_from_key("files_removed")))
978
+ result_manager.append(content=f"Removed files: {', '.join(removed_files)}",
979
+ meta={"action": "remove_files","success":True, "input":{ "file_names": file_names}})
980
+ else:
981
+ printer.print_in_terminal("remove_files_none", style="yellow")
982
+ result_manager.append(content=printer.get_message_from_key("remove_files_none"),
983
+ meta={"action": "remove_files","success":False, "input":{ "file_names": file_names}})
984
+
985
+ completer.update_current_files(memory["current_files"]["files"])
986
+ save_memory()
987
+
988
+ @run_in_raw_thread()
989
+ def ask(query: str):
990
+ conf = memory.get("conf", {})
991
+ yaml_config = {
992
+ "include_file": ["./base/base.yml"],
993
+ }
994
+ yaml_config["query"] = query
995
+
996
+ if "project_type" in conf:
997
+ yaml_config["project_type"] = conf["project_type"]
998
+
999
+ if "model" in conf:
1000
+ yaml_config["model"] = conf["model"]
1001
+
1002
+ if "index_model" in conf:
1003
+ yaml_config["index_model"] = conf["index_model"]
1004
+
1005
+ if "vl_model" in conf:
1006
+ yaml_config["vl_model"] = conf["vl_model"]
1007
+
1008
+ if "code_model" in conf:
1009
+ yaml_config["code_model"] = conf["code_model"]
1010
+
1011
+ if "product_mode" in conf:
1012
+ yaml_config["product_mode"] = conf["product_mode"]
1013
+
1014
+ yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
1015
+
1016
+ execute_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
1017
+
1018
+ with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1019
+ f.write(yaml_content)
1020
+
1021
+ def execute_ask():
1022
+ auto_coder_main(["agent", "project_reader", "--file", execute_file])
1023
+
1024
+ try:
1025
+ execute_ask()
1026
+ finally:
1027
+ os.remove(execute_file)
1028
+
1029
+
1030
+ def get_llm_friendly_package_docs(
1031
+ package_name: Optional[str] = None, return_paths: bool = False
1032
+ ) -> List[str]:
1033
+ lib_dir = os.path.join(".auto-coder", "libs")
1034
+ llm_friendly_packages_dir = os.path.join(lib_dir, "llm_friendly_packages")
1035
+ docs = []
1036
+
1037
+ if not os.path.exists(llm_friendly_packages_dir):
1038
+ return docs
1039
+
1040
+ libs = list(memory.get("libs", {}).keys())
1041
+
1042
+ for domain in os.listdir(llm_friendly_packages_dir):
1043
+ domain_path = os.path.join(llm_friendly_packages_dir, domain)
1044
+ if os.path.isdir(domain_path):
1045
+ for username in os.listdir(domain_path):
1046
+ username_path = os.path.join(domain_path, username)
1047
+ if os.path.isdir(username_path):
1048
+ for lib_name in os.listdir(username_path):
1049
+ lib_path = os.path.join(username_path, lib_name)
1050
+ if (
1051
+ os.path.isdir(lib_path)
1052
+ and (
1053
+ package_name is None
1054
+ or lib_name == package_name
1055
+ or package_name == os.path.join(username, lib_name)
1056
+ )
1057
+ and lib_name in libs
1058
+ ):
1059
+ for root, _, files in os.walk(lib_path):
1060
+ for file in files:
1061
+ if file.endswith(".md"):
1062
+ file_path = os.path.join(root, file)
1063
+ if return_paths:
1064
+ docs.append(file_path)
1065
+ else:
1066
+ with open(file_path, "r",encoding="utf-8") as f:
1067
+ docs.append(f.read())
1068
+
1069
+ return docs
1070
+
1071
+
1072
+ def convert_yaml_to_config(yaml_file: str):
1073
+ from autocoder.auto_coder import AutoCoderArgs, load_include_files, Template
1074
+
1075
+ args = AutoCoderArgs()
1076
+ with open(yaml_file, "r",encoding="utf-8") as f:
1077
+ config = yaml.safe_load(f)
1078
+ config = load_include_files(config, yaml_file)
1079
+ for key, value in config.items():
1080
+ if key != "file": # 排除 --file 参数本身
1081
+ # key: ENV {{VARIABLE_NAME}}
1082
+ if isinstance(value, str) and value.startswith("ENV"):
1083
+ template = Template(value.removeprefix("ENV").strip())
1084
+ value = template.render(os.environ)
1085
+ setattr(args, key, value)
1086
+ return args
1087
+
1088
+ @run_in_raw_thread()
1089
+ def mcp(query: str):
1090
+ query = query.strip()
1091
+ mcp_server = get_mcp_server()
1092
+ printer = Printer()
1093
+
1094
+ # Handle remove command
1095
+ if query.startswith("/remove"):
1096
+ server_name = query.replace("/remove", "", 1).strip()
1097
+ response = mcp_server.send_request(
1098
+ McpRemoveRequest(server_name=server_name))
1099
+ if response.error:
1100
+ printer.print_in_terminal("mcp_remove_error", style="red", error=response.error)
1101
+ else:
1102
+ printer.print_in_terminal("mcp_remove_success", style="green", result=response.result)
1103
+ return
1104
+
1105
+ # Handle list command
1106
+ if query.startswith("/list_running"):
1107
+ response = mcp_server.send_request(McpListRunningRequest())
1108
+ if response.error:
1109
+ printer.print_in_terminal("mcp_list_running_error", style="red", error=response.error)
1110
+ else:
1111
+ printer.print_in_terminal("mcp_list_running_title")
1112
+ printer.print_str_in_terminal(response.result)
1113
+ return
1114
+
1115
+ # Handle list command
1116
+ if query.startswith("/list"):
1117
+ response = mcp_server.send_request(McpListRequest())
1118
+ if response.error:
1119
+ printer.print_in_terminal("mcp_list_builtin_error", style="red", error=response.error)
1120
+ else:
1121
+ printer.print_in_terminal("mcp_list_builtin_title")
1122
+ printer.print_str_in_terminal(response.result)
1123
+ return
1124
+
1125
+ # Handle refresh command
1126
+ if query.startswith("/refresh"):
1127
+ server_name = query.replace("/refresh", "", 1).strip()
1128
+ response = mcp_server.send_request(McpRefreshRequest(name=server_name or None))
1129
+ if response.error:
1130
+ printer.print_in_terminal("mcp_refresh_error", style="red", error=response.error)
1131
+ else:
1132
+ printer.print_in_terminal("mcp_refresh_success", style="green")
1133
+ return
1134
+
1135
+ # Handle add command
1136
+ if query.startswith("/add"):
1137
+ query = query.replace("/add", "", 1).strip()
1138
+ request = McpInstallRequest(server_name_or_config=query)
1139
+ response = mcp_server.send_request(request)
1140
+
1141
+ if response.error:
1142
+ printer.print_in_terminal("mcp_install_error", style="red", error=response.error)
1143
+ else:
1144
+ printer.print_in_terminal("mcp_install_success", style="green", result=response.result)
1145
+ return
1146
+
1147
+ # Handle default query
1148
+ conf = memory.get("conf", {})
1149
+ yaml_config = {
1150
+ "include_file": ["./base/base.yml"],
1151
+ "auto_merge": conf.get("auto_merge", "editblock"),
1152
+ "human_as_model": conf.get("human_as_model", "false") == "true",
1153
+ "skip_build_index": conf.get("skip_build_index", "true") == "true",
1154
+ "skip_confirm": conf.get("skip_confirm", "true") == "true",
1155
+ "silence": conf.get("silence", "true") == "true",
1156
+ "include_project_structure": conf.get("include_project_structure", "true")
1157
+ == "true",
1158
+ }
1159
+ for key, value in conf.items():
1160
+ converted_value = convert_config_value(key, value)
1161
+ if converted_value is not None:
1162
+ yaml_config[key] = converted_value
1163
+
1164
+ temp_yaml = os.path.join("actions", f"{uuid.uuid4()}.yml")
1165
+ try:
1166
+ with open(temp_yaml, "w",encoding="utf-8") as f:
1167
+ f.write(convert_yaml_config_to_str(yaml_config=yaml_config))
1168
+ args = convert_yaml_to_config(temp_yaml)
1169
+ finally:
1170
+ if os.path.exists(temp_yaml):
1171
+ os.remove(temp_yaml)
1172
+
1173
+ mcp_server = get_mcp_server()
1174
+ response = mcp_server.send_request(
1175
+ McpRequest(
1176
+ query=query,
1177
+ model=args.inference_model or args.model,
1178
+ product_mode=args.product_mode
1179
+ )
1180
+ )
1181
+
1182
+ if response.error:
1183
+ printer.print_panel(
1184
+ f"Error from MCP server: {response.error}",
1185
+ text_options={"justify": "left"},
1186
+ panel_options={
1187
+ "title": printer.get_message_from_key("mcp_error_title"),
1188
+ "border_style": "red"
1189
+ }
1190
+ )
1191
+ else:
1192
+ # Save conversation
1193
+ mcp_dir = os.path.join(".auto-coder", "mcp", "conversations")
1194
+ os.makedirs(mcp_dir, exist_ok=True)
1195
+ timestamp = str(int(time.time()))
1196
+ file_path = os.path.join(mcp_dir, f"{timestamp}.md")
1197
+
1198
+ # Format response as markdown
1199
+ markdown_content = response.result
1200
+
1201
+ # Save to file
1202
+ with open(file_path, "w", encoding="utf-8") as f:
1203
+ f.write(markdown_content)
1204
+
1205
+ console = Console()
1206
+ console.print(
1207
+ Panel(
1208
+ Markdown(markdown_content, justify="left"),
1209
+ title=printer.get_message_from_key('mcp_response_title'),
1210
+ border_style="green"
1211
+ )
1212
+ )
1213
+
1214
+
1215
+ @run_in_raw_thread()
1216
+ def code_next(query: str):
1217
+ conf = memory.get("conf", {})
1218
+ yaml_config = {
1219
+ "include_file": ["./base/base.yml"],
1220
+ "auto_merge": conf.get("auto_merge", "editblock"),
1221
+ "human_as_model": conf.get("human_as_model", "false") == "true",
1222
+ "skip_build_index": conf.get("skip_build_index", "true") == "true",
1223
+ "skip_confirm": conf.get("skip_confirm", "true") == "true",
1224
+ "silence": conf.get("silence", "true") == "true",
1225
+ "include_project_structure": conf.get("include_project_structure", "true")
1226
+ == "true",
1227
+ }
1228
+ for key, value in conf.items():
1229
+ converted_value = convert_config_value(key, value)
1230
+ if converted_value is not None:
1231
+ yaml_config[key] = converted_value
1232
+
1233
+ temp_yaml = os.path.join("actions", f"{uuid.uuid4()}.yml")
1234
+ try:
1235
+ with open(temp_yaml, "w",encoding="utf-8") as f:
1236
+ f.write(convert_yaml_config_to_str(yaml_config=yaml_config))
1237
+ args = convert_yaml_to_config(temp_yaml)
1238
+ finally:
1239
+ if os.path.exists(temp_yaml):
1240
+ os.remove(temp_yaml)
1241
+
1242
+ product_mode = conf.get("product_mode", "lite")
1243
+ llm = get_single_llm(args.chat_model or args.model, product_mode=product_mode)
1244
+
1245
+ auto_guesser = AutoGuessQuery(
1246
+ llm=llm, project_dir=os.getcwd(), skip_diff=True)
1247
+
1248
+ predicted_tasks = auto_guesser.predict_next_tasks(
1249
+ 5, is_human_as_model=args.human_as_model
1250
+ )
1251
+
1252
+ if not predicted_tasks:
1253
+ console = Console()
1254
+ console.print(Panel("No task predictions available", style="yellow"))
1255
+ return
1256
+
1257
+ console = Console()
1258
+
1259
+ # Create main panel for all predicted tasks
1260
+ table = Table(show_header=True,
1261
+ header_style="bold magenta", show_lines=True)
1262
+ table.add_column("Priority", style="cyan", width=8)
1263
+ table.add_column("Task Description", style="green",
1264
+ width=40, overflow="fold")
1265
+ table.add_column("Files", style="yellow", width=30, overflow="fold")
1266
+ table.add_column("Reason", style="blue", width=30, overflow="fold")
1267
+ table.add_column("Dependencies", style="magenta",
1268
+ width=30, overflow="fold")
1269
+
1270
+ for task in predicted_tasks:
1271
+ # Format file paths to be more readable
1272
+ file_list = "\n".join([os.path.relpath(f, os.getcwd())
1273
+ for f in task.urls])
1274
+
1275
+ # Format dependencies to be more readable
1276
+ dependencies = (
1277
+ "\n".join(
1278
+ task.dependency_queries) if task.dependency_queries else "None"
1279
+ )
1280
+
1281
+ table.add_row(
1282
+ str(task.priority), task.query, file_list, task.reason, dependencies
1283
+ )
1284
+
1285
+ console.print(
1286
+ Panel(
1287
+ table,
1288
+ title="[bold]Predicted Next Tasks[/bold]",
1289
+ border_style="blue",
1290
+ padding=(1, 2), # Add more horizontal padding
1291
+ )
1292
+ )
1293
+
1294
+
1295
+ @run_in_raw_thread()
1296
+ def commit(query: str):
1297
+ conf = memory.get("conf", {})
1298
+ product_mode = conf.get("product_mode", "lite")
1299
+ def prepare_commit_yaml():
1300
+ auto_coder_main(["next", "chat_action"])
1301
+
1302
+ prepare_commit_yaml()
1303
+
1304
+ # no_diff = query.strip().startswith("/no_diff")
1305
+ # if no_diff:
1306
+ # query = query.replace("/no_diff", "", 1).strip()
1307
+
1308
+ latest_yaml_file = get_last_yaml_file("actions")
1309
+
1310
+ conf = memory.get("conf", {})
1311
+ current_files = memory["current_files"]["files"]
1312
+ execute_file = None
1313
+
1314
+ if latest_yaml_file:
1315
+ try:
1316
+ execute_file = os.path.join("actions", latest_yaml_file)
1317
+ yaml_config = {
1318
+ "include_file": ["./base/base.yml"],
1319
+ "auto_merge": conf.get("auto_merge", "editblock"),
1320
+ "human_as_model": conf.get("human_as_model", "false") == "true",
1321
+ "skip_build_index": conf.get("skip_build_index", "true") == "true",
1322
+ "skip_confirm": conf.get("skip_confirm", "true") == "true",
1323
+ "silence": conf.get("silence", "true") == "true",
1324
+ "include_project_structure": conf.get("include_project_structure", "true")
1325
+ == "true",
1326
+ }
1327
+ for key, value in conf.items():
1328
+ converted_value = convert_config_value(key, value)
1329
+ if converted_value is not None:
1330
+ yaml_config[key] = converted_value
1331
+
1332
+ yaml_config["urls"] = current_files + get_llm_friendly_package_docs(
1333
+ return_paths=True
1334
+ )
1335
+
1336
+ if conf.get("enable_global_memory", "true") in ["true", "True",True]:
1337
+ yaml_config["urls"] += get_global_memory_file_paths()
1338
+
1339
+ # 临时保存yaml文件,然后读取yaml文件,转换为args
1340
+ temp_yaml = os.path.join("actions", f"{uuid.uuid4()}.yml")
1341
+ try:
1342
+ with open(temp_yaml, "w",encoding="utf-8") as f:
1343
+ f.write(convert_yaml_config_to_str(
1344
+ yaml_config=yaml_config))
1345
+ args = convert_yaml_to_config(temp_yaml)
1346
+ finally:
1347
+ if os.path.exists(temp_yaml):
1348
+ os.remove(temp_yaml)
1349
+
1350
+ target_model = args.commit_model or args.model
1351
+ llm = get_single_llm(target_model, product_mode)
1352
+ printer = Printer()
1353
+ printer.print_in_terminal("commit_generating", style="yellow", model_name=target_model)
1354
+ commit_message = ""
1355
+
1356
+ try:
1357
+ uncommitted_changes = git_utils.get_uncommitted_changes(".")
1358
+ commit_message = git_utils.generate_commit_message.with_llm(llm).run(
1359
+ uncommitted_changes
1360
+ )
1361
+ memory["conversation"].append(
1362
+ {"role": "user", "content": commit_message})
1363
+ except Exception as e:
1364
+ printer.print_in_terminal("commit_failed", style="red", error=str(e), model_name=target_model)
1365
+ return
1366
+
1367
+ yaml_config["query"] = commit_message
1368
+ yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
1369
+ with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1370
+ f.write(yaml_content)
1371
+
1372
+ file_content = open(execute_file).read()
1373
+ md5 = hashlib.md5(file_content.encode("utf-8")).hexdigest()
1374
+ file_name = os.path.basename(execute_file)
1375
+ commit_result = git_utils.commit_changes(
1376
+ ".", f"auto_coder_{file_name}_{md5}\n{commit_message}"
1377
+ )
1378
+ git_utils.print_commit_info(commit_result=commit_result)
1379
+ if commit_message:
1380
+ printer.print_in_terminal("commit_message", style="green", model_name=target_model, message=commit_message)
1381
+ except Exception as e:
1382
+ import traceback
1383
+ traceback.print_exc()
1384
+ print(f"Failed to commit: {e}")
1385
+ if execute_file:
1386
+ os.remove(execute_file)
1387
+
1388
+
1389
+ @run_in_raw_thread()
1390
+ def coding(query: str):
1391
+ console = Console()
1392
+ is_apply = query.strip().startswith("/apply")
1393
+ if is_apply:
1394
+ query = query.replace("/apply", "", 1).strip()
1395
+
1396
+ is_next = query.strip().startswith("/next")
1397
+ if is_next:
1398
+ query = query.replace("/next", "", 1).strip()
1399
+
1400
+ if is_next:
1401
+ code_next(query)
1402
+ return
1403
+
1404
+ memory["conversation"].append({"role": "user", "content": query})
1405
+ conf = memory.get("conf", {})
1406
+
1407
+ current_files = memory["current_files"]["files"]
1408
+ current_groups = memory["current_files"].get("current_groups", [])
1409
+ groups = memory["current_files"].get("groups", {})
1410
+ groups_info = memory["current_files"].get("groups_info", {})
1411
+
1412
+ def prepare_chat_yaml():
1413
+ auto_coder_main(["next", "chat_action"])
1414
+
1415
+ prepare_chat_yaml()
1416
+
1417
+ latest_yaml_file = get_last_yaml_file("actions")
1418
+
1419
+ if latest_yaml_file:
1420
+ yaml_config = {
1421
+ "include_file": ["./base/base.yml"],
1422
+ "auto_merge": conf.get("auto_merge", "editblock"),
1423
+ "human_as_model": conf.get("human_as_model", "false") == "true",
1424
+ "skip_build_index": conf.get("skip_build_index", "true") == "true",
1425
+ "skip_confirm": conf.get("skip_confirm", "true") == "true",
1426
+ "silence": conf.get("silence", "true") == "true",
1427
+ "include_project_structure": conf.get("include_project_structure", "true")
1428
+ == "true",
1429
+ "exclude_files": memory.get("exclude_files", []),
1430
+ }
1431
+
1432
+ yaml_config["context"] = ""
1433
+ yaml_config["in_code_apply"] = is_apply
1434
+
1435
+ for key, value in conf.items():
1436
+ converted_value = convert_config_value(key, value)
1437
+ if converted_value is not None:
1438
+ yaml_config[key] = converted_value
1439
+
1440
+ yaml_config["urls"] = current_files + get_llm_friendly_package_docs(
1441
+ return_paths=True
1442
+ )
1443
+
1444
+ if conf.get("enable_global_memory", "true") in ["true", "True",True]:
1445
+ yaml_config["urls"] += get_global_memory_file_paths()
1446
+
1447
+ # handle image
1448
+ v = Image.convert_image_paths_from(query)
1449
+ yaml_config["query"] = v
1450
+
1451
+ # Add context for active groups and their query prefixes
1452
+ if current_groups:
1453
+ active_groups_context = "下面是对上面文件按分组给到的一些描述,当用户的需求正好匹配描述的时候,参考描述来做修改:\n"
1454
+ for group in current_groups:
1455
+ group_files = groups.get(group, [])
1456
+ query_prefix = groups_info.get(
1457
+ group, {}).get("query_prefix", "")
1458
+ active_groups_context += f"组名: {group}\n"
1459
+ active_groups_context += f"文件列表:\n"
1460
+ for file in group_files:
1461
+ active_groups_context += f"- {file}\n"
1462
+ active_groups_context += f"组描述: {query_prefix}\n\n"
1463
+
1464
+ yaml_config["context"] = active_groups_context + "\n"
1465
+
1466
+ if is_apply:
1467
+ memory_dir = os.path.join(".auto-coder", "memory")
1468
+ os.makedirs(memory_dir, exist_ok=True)
1469
+ memory_file = os.path.join(memory_dir, "chat_history.json")
1470
+
1471
+ def error_message():
1472
+ console.print(
1473
+ Panel(
1474
+ "No chat history found to apply.",
1475
+ title="Chat History",
1476
+ expand=False,
1477
+ border_style="yellow",
1478
+ )
1479
+ )
1480
+
1481
+ if not os.path.exists(memory_file):
1482
+ error_message()
1483
+ return
1484
+
1485
+ with open(memory_file, "r",encoding="utf-8") as f:
1486
+ chat_history = json.load(f)
1487
+
1488
+ if not chat_history["ask_conversation"]:
1489
+ error_message()
1490
+ return
1491
+
1492
+ conversations = chat_history["ask_conversation"]
1493
+
1494
+ yaml_config[
1495
+ "context"
1496
+ ] += f"下面是我们的历史对话,参考我们的历史对话从而更好的理解需求和修改代码: \n\n<history>\n"
1497
+ for conv in conversations:
1498
+ if conv["role"] == "user":
1499
+ yaml_config["context"] += f"用户: {conv['content']}\n"
1500
+ elif conv["role"] == "assistant":
1501
+ yaml_config["context"] += f"你: {conv['content']}\n"
1502
+ yaml_config["context"] += "</history>\n"
1503
+
1504
+ yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
1505
+
1506
+ md5 = hashlib.md5(yaml_content.encode("utf-8")).hexdigest()
1507
+
1508
+ execute_file = os.path.join("actions", latest_yaml_file)
1509
+ with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1510
+ f.write(yaml_content)
1511
+
1512
+ def execute_chat():
1513
+ cmd = ["--file", execute_file]
1514
+ auto_coder_main(cmd)
1515
+ result_manager = ResultManager()
1516
+ result_manager.append(content="", meta={"commit_message": f"auto_coder_{latest_yaml_file}_{md5}","action": "coding", "input":{
1517
+ "query": query
1518
+ }})
1519
+
1520
+ execute_chat()
1521
+ else:
1522
+ print("Failed to create new YAML file.")
1523
+
1524
+ save_memory()
1525
+ completer.refresh_files()
1526
+
1527
+
1528
+ @byzerllm.prompt()
1529
+ def code_review(query: str) -> str:
1530
+ """
1531
+ 掐面提供了上下文,对代码进行review,参考如下检查点。
1532
+ 1. 有没有调用不符合方法,类的签名的调用,包括对第三方类,模块,方法的检查(如果上下文提供了这些信息)
1533
+ 2. 有没有未声明直接使用的变量,方法,类
1534
+ 3. 有没有明显的语法错误
1535
+ 4. 如果是python代码,检查有没有缩进方面的错误
1536
+ 5. 如果是python代码,检查是否 try 后面缺少 except 或者 finally
1537
+ {% if query %}
1538
+ 6. 用户的额外的检查需求:{{ query }}
1539
+ {% endif %}
1540
+
1541
+ 如果用户的需求包含了@一个文件名 或者 @@符号, 那么重点关注这些文件或者符号(函数,类)进行上述的review。
1542
+ review 过程中严格遵循上述的检查点,不要遗漏,没有发现异常的点直接跳过,只对发现的异常点,给出具体的修改后的代码。
1543
+ """
1544
+
1545
+
1546
+ @run_in_raw_thread()
1547
+ def chat(query: str):
1548
+ conf = memory.get("conf", {})
1549
+
1550
+ yaml_config = {
1551
+ "include_file": ["./base/base.yml"],
1552
+ "include_project_structure": conf.get("include_project_structure", "true")
1553
+ in ["true", "True"],
1554
+ "human_as_model": conf.get("human_as_model", "false") == "true",
1555
+ "skip_build_index": conf.get("skip_build_index", "true") == "true",
1556
+ "skip_confirm": conf.get("skip_confirm", "true") == "true",
1557
+ "silence": conf.get("silence", "true") == "true",
1558
+ "exclude_files": memory.get("exclude_files", []),
1559
+ }
1560
+
1561
+ current_files = memory["current_files"]["files"] + get_llm_friendly_package_docs(
1562
+ return_paths=True
1563
+ )
1564
+
1565
+ if conf.get("enable_global_memory", "true") in ["true", "True",True]:
1566
+ current_files += get_global_memory_file_paths()
1567
+
1568
+ yaml_config["urls"] = current_files
1569
+
1570
+ if "emb_model" in conf:
1571
+ yaml_config["emb_model"] = conf["emb_model"]
1572
+
1573
+ is_new = "/new" in query
1574
+ if is_new:
1575
+ query = query.replace("/new", "", 1).strip()
1576
+
1577
+ yaml_config["action"] = []
1578
+
1579
+ if "/mcp " in query:
1580
+ yaml_config["action"].append("mcp")
1581
+ query = query.replace("/mcp ", "", 1).strip()
1582
+
1583
+ if "/rag " in query:
1584
+ yaml_config["action"].append("rag")
1585
+ query = query.replace("/rag ", "", 1).strip()
1586
+
1587
+ if "/copy" in query:
1588
+ yaml_config["action"].append("copy")
1589
+ query = query.replace("/copy", "", 1).strip()
1590
+
1591
+ if "/save" in query:
1592
+ yaml_config["action"].append("save")
1593
+ query = query.replace("/save", "", 1).strip()
1594
+
1595
+ if "/review" in query and "/commit" in query:
1596
+ yaml_config["action"].append("review_commit")
1597
+ query = query.replace("/review", "", 1).replace("/commit", "", 1).strip()
1598
+ else:
1599
+ is_review = query.strip().startswith("/review")
1600
+ if is_review:
1601
+ query = query.replace("/review", "", 1).strip()
1602
+ if "prompt_review" in conf:
1603
+ query = format_str_jinja2(conf["prompt_review"], query=query)
1604
+ else:
1605
+ query = code_review.prompt(query)
1606
+
1607
+ is_no_context = "/no_context" in query.strip()
1608
+ if is_no_context:
1609
+ query = query.replace("/no_context", "", 1).strip()
1610
+ yaml_config["action"].append("no_context")
1611
+
1612
+ for key, value in conf.items():
1613
+ converted_value = convert_config_value(key, value)
1614
+ if converted_value is not None:
1615
+ yaml_config[key] = converted_value
1616
+
1617
+ query = Image.convert_image_paths_from(query)
1618
+
1619
+ yaml_config["query"] = query
1620
+
1621
+ yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
1622
+
1623
+ execute_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
1624
+
1625
+ with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1626
+ f.write(yaml_content)
1627
+
1628
+ def execute_ask():
1629
+ cmd = ["agent", "chat", "--file", execute_file]
1630
+ if is_new:
1631
+ cmd.append("--new_session")
1632
+ auto_coder_main(cmd)
1633
+
1634
+ try:
1635
+ execute_ask()
1636
+ finally:
1637
+ os.remove(execute_file)
1638
+
1639
+
1640
+ @run_in_raw_thread()
1641
+ def summon(query: str):
1642
+ conf = memory.get("conf", {})
1643
+ current_files = memory["current_files"]["files"]
1644
+
1645
+ file_contents = []
1646
+ for file in current_files:
1647
+ if os.path.exists(file):
1648
+ try:
1649
+ with open(file, "r",encoding="utf-8") as f:
1650
+ content = f.read()
1651
+ s = f"##File: {file}\n{content}\n\n"
1652
+ file_contents.append(s)
1653
+ except Exception as e:
1654
+ print(f"Failed to read file: {file}. Error: {str(e)}")
1655
+
1656
+ all_file_content = "".join(file_contents)
1657
+
1658
+ yaml_config = {
1659
+ "include_file": ["./base/base.yml"],
1660
+ }
1661
+ yaml_config["query"] = query
1662
+ yaml_config["context"] = json.dumps(
1663
+ {"file_content": all_file_content}, ensure_ascii=False
1664
+ )
1665
+
1666
+ if "emb_model" in conf:
1667
+ yaml_config["emb_model"] = conf["emb_model"]
1668
+
1669
+ if "vl_model" in conf:
1670
+ yaml_config["vl_model"] = conf["vl_model"]
1671
+
1672
+ if "code_model" in conf:
1673
+ yaml_config["code_model"] = conf["code_model"]
1674
+
1675
+ if "model" in conf:
1676
+ yaml_config["model"] = conf["model"]
1677
+
1678
+ if "product_mode" in conf:
1679
+ yaml_config["product_mode"] = conf["product_mode"]
1680
+
1681
+ yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
1682
+
1683
+ execute_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
1684
+
1685
+ with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1686
+ f.write(yaml_content)
1687
+
1688
+ def execute_summon():
1689
+ auto_coder_main(["agent", "auto_tool", "--file", execute_file])
1690
+
1691
+ try:
1692
+ execute_summon()
1693
+ finally:
1694
+ os.remove(execute_file)
1695
+
1696
+
1697
+ @run_in_raw_thread()
1698
+ def design(query: str):
1699
+
1700
+ conf = memory.get("conf", {})
1701
+ yaml_config = {
1702
+ "include_file": ["./base/base.yml"],
1703
+ }
1704
+
1705
+ if query.strip().startswith("/svg"):
1706
+ query = query.replace("/svg", "", 1).strip()
1707
+ yaml_config["agent_designer_mode"] = "svg"
1708
+ elif query.strip().startswith("/sd"):
1709
+ query = query.replace("/svg", "", 1).strip()
1710
+ yaml_config["agent_designer_mode"] = "sd"
1711
+ elif query.strip().startswith("/logo"):
1712
+ query = query.replace("/logo", "", 1).strip()
1713
+ yaml_config["agent_designer_mode"] = "logo"
1714
+ else:
1715
+ yaml_config["agent_designer_mode"] = "svg"
1716
+
1717
+ yaml_config["query"] = query
1718
+
1719
+ if "model" in conf:
1720
+ yaml_config["model"] = conf["model"]
1721
+
1722
+ if "designer_model" in conf:
1723
+ yaml_config["designer_model"] = conf["designer_model"]
1724
+
1725
+ if "sd_model" in conf:
1726
+ yaml_config["sd_model"] = conf["sd_model"]
1727
+
1728
+ yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
1729
+
1730
+ execute_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
1731
+
1732
+ with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1733
+ f.write(yaml_content)
1734
+
1735
+ def execute_design():
1736
+ auto_coder_main(["agent", "designer", "--file", execute_file])
1737
+
1738
+ try:
1739
+ execute_design()
1740
+ finally:
1741
+ os.remove(execute_file)
1742
+
1743
+
1744
+ def voice_input():
1745
+ conf = memory.get("conf", {})
1746
+ yaml_config = {
1747
+ "include_file": ["./base/base.yml"],
1748
+ }
1749
+
1750
+ if "voice2text_model" not in conf:
1751
+ print(
1752
+ "Please set voice2text_model in configuration. /conf voice2text_model:<model>"
1753
+ )
1754
+ return
1755
+
1756
+ yaml_config["voice2text_model"] = conf["voice2text_model"]
1757
+ yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
1758
+
1759
+ execute_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
1760
+
1761
+ with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1762
+ f.write(yaml_content)
1763
+
1764
+ def execute_voice2text_command():
1765
+ auto_coder_main(["agent", "voice2text", "--file", execute_file])
1766
+
1767
+ try:
1768
+ execute_voice2text_command()
1769
+ with open(os.path.join(".auto-coder", "exchange.txt"), "r",encoding="utf-8") as f:
1770
+ return f.read()
1771
+ finally:
1772
+ os.remove(execute_file)
1773
+
1774
+
1775
+ @run_in_raw_thread()
1776
+ def generate_shell_command(input_text):
1777
+ conf = memory.get("conf", {})
1778
+ yaml_config = {
1779
+ "include_file": ["./base/base.yml"],
1780
+ }
1781
+
1782
+ if "model" in conf:
1783
+ yaml_config["model"] = conf["model"]
1784
+
1785
+ yaml_config["query"] = input_text
1786
+
1787
+ yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
1788
+
1789
+ execute_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
1790
+
1791
+ with open(os.path.join(execute_file), "w",encoding="utf-8") as f:
1792
+ f.write(yaml_content)
1793
+
1794
+ try:
1795
+ auto_coder_main(["agent", "generate_command", "--file", execute_file])
1796
+ with open(os.path.join(".auto-coder", "exchange.txt"), "r",encoding="utf-8") as f:
1797
+ shell_script = f.read()
1798
+ result_manager = ResultManager()
1799
+ result_manager.add_result(content=shell_script,meta={
1800
+ "action": "generate_shell_command",
1801
+ "input": {
1802
+ "query": input_text
1803
+ }
1804
+ })
1805
+ return shell_script
1806
+ finally:
1807
+ os.remove(execute_file)
1808
+
1809
+ def manage_models(query: str):
1810
+ """
1811
+ Handle /models subcommands:
1812
+ /models /list - List all models (default + custom)
1813
+ /models /add <name> <api_key> - Add model with simplified params
1814
+ /models /add_model name=xxx base_url=xxx ... - Add model with custom params
1815
+ /models /remove <name> - Remove model by name
1816
+ """
1817
+ printer = Printer()
1818
+ console = Console()
1819
+
1820
+ product_mode = memory.get("product_mode", "lite")
1821
+ if product_mode != "lite":
1822
+ printer.print_in_terminal("models_lite_only", style="red")
1823
+ return
1824
+
1825
+ models_data = models_module.load_models()
1826
+ subcmd = ""
1827
+ if "/list" in query:
1828
+ subcmd = "/list"
1829
+ query = query.replace("/list", "", 1).strip()
1830
+
1831
+ if "/add_model" in query:
1832
+ subcmd = "/add_model"
1833
+ query = query.replace("/add_model", "", 1).strip()
1834
+
1835
+ if "/add" in query:
1836
+ subcmd = "/add"
1837
+ query = query.replace("/add", "", 1).strip()
1838
+
1839
+ # alias to /add
1840
+ if "/activate" in query:
1841
+ subcmd = "/add"
1842
+ query = query.replace("/activate", "", 1).strip()
1843
+
1844
+ if "/remove" in query:
1845
+ subcmd = "/remove"
1846
+ query = query.replace("/remove", "", 1).strip()
1847
+
1848
+ if "/speed-test" in query:
1849
+ subcmd = "/speed-test"
1850
+ query = query.replace("/speed-test", "", 1).strip()
1851
+
1852
+ if "/speed_test" in query:
1853
+ subcmd = "/speed-test"
1854
+ query = query.replace("/speed_test", "", 1).strip()
1855
+
1856
+ if "input_price" in query:
1857
+ subcmd = "/input_price"
1858
+ query = query.replace("/input_price", "", 1).strip()
1859
+
1860
+ if "output_price" in query:
1861
+ subcmd = "/output_price"
1862
+ query = query.replace("/output_price", "", 1).strip()
1863
+
1864
+ if "/speed" in query:
1865
+ subcmd = "/speed"
1866
+ query = query.replace("/speed", "", 1).strip()
1867
+
1868
+
1869
+
1870
+ if not subcmd:
1871
+ printer.print_in_terminal("models_usage")
1872
+
1873
+ result_manager = ResultManager()
1874
+ if subcmd == "/list":
1875
+ if models_data:
1876
+ # Sort models by speed (average_speed)
1877
+ sorted_models = sorted(models_data, key=lambda x: float(x.get('average_speed', 0)))
1878
+ sorted_models.reverse()
1879
+
1880
+ table = Table(
1881
+ title=printer.get_message_from_key("models_title"),
1882
+ expand=True,
1883
+ show_lines=True
1884
+ )
1885
+ table.add_column("Name", style="cyan", width=30, overflow="fold", no_wrap=False)
1886
+ table.add_column("Model Name", style="magenta", width=30, overflow="fold", no_wrap=False)
1887
+ table.add_column("Base URL", style="white", width=40, overflow="fold", no_wrap=False)
1888
+ table.add_column("Input Price (M)", style="magenta", width=15, overflow="fold", no_wrap=False)
1889
+ table.add_column("Output Price (M)", style="magenta", width=15, overflow="fold", no_wrap=False)
1890
+ table.add_column("Speed (s/req)", style="blue", width=15, overflow="fold", no_wrap=False)
1891
+ for m in sorted_models:
1892
+ # Check if api_key_path exists and file exists
1893
+ is_api_key_set = "api_key" in m
1894
+ name = m.get("name", "")
1895
+ if is_api_key_set:
1896
+ api_key = m.get("api_key", "").strip()
1897
+ if not api_key:
1898
+ printer.print_in_terminal("models_api_key_empty", style="yellow", name=name)
1899
+ name = f"{name} *"
1900
+
1901
+ table.add_row(
1902
+ name,
1903
+ m.get("model_name", ""),
1904
+ m.get("base_url", ""),
1905
+ f"{m.get('input_price', 0.0):.2f}",
1906
+ f"{m.get('output_price', 0.0):.2f}",
1907
+ f"{m.get('average_speed', 0.0):.3f}"
1908
+ )
1909
+ console.print(table)
1910
+ result_manager.add_result(content=json.dumps(sorted_models,ensure_ascii=False),meta={
1911
+ "action": "models",
1912
+ "input": {
1913
+ "query": query
1914
+ }
1915
+ })
1916
+
1917
+ else:
1918
+ printer.print_in_terminal("models_no_models", style="yellow")
1919
+ result_manager.add_result(content="No models found",meta={
1920
+ "action": "models",
1921
+ "input": {
1922
+ "query": query
1923
+ }
1924
+ })
1925
+
1926
+ elif subcmd == "/input_price":
1927
+ args = query.strip().split()
1928
+ if len(args) >= 2:
1929
+ name = args[0]
1930
+ try:
1931
+ price = float(args[1])
1932
+ if models_module.update_model_input_price(name, price):
1933
+ printer.print_in_terminal("models_input_price_updated", style="green", name=name, price=price)
1934
+ result_manager.add_result(content=f"models_input_price_updated: {name} {price}",meta={
1935
+ "action": "models",
1936
+ "input": {
1937
+ "query": query
1938
+ }
1939
+ })
1940
+ else:
1941
+ printer.print_in_terminal("models_not_found", style="red", name=name)
1942
+ result_manager.add_result(content=f"models_not_found: {name}",meta={
1943
+ "action": "models",
1944
+ "input": {
1945
+ "query": query
1946
+ }
1947
+ })
1948
+ except ValueError as e:
1949
+ result_manager.add_result(content=f"models_invalid_price: {str(e)}",meta={
1950
+ "action": "models",
1951
+ "input": {
1952
+ "query": query
1953
+ }
1954
+ })
1955
+ printer.print_in_terminal("models_invalid_price", style="red", error=str(e))
1956
+ else:
1957
+ result_manager.add_result(content=printer.get_message_from_key("models_input_price_usage"),meta={
1958
+ "action": "models",
1959
+ "input": {
1960
+ "query": query
1961
+ }
1962
+ })
1963
+ printer.print_in_terminal("models_input_price_usage", style="red")
1964
+
1965
+ elif subcmd == "/output_price":
1966
+ args = query.strip().split()
1967
+ if len(args) >= 2:
1968
+ name = args[0]
1969
+ try:
1970
+ price = float(args[1])
1971
+ if models_module.update_model_output_price(name, price):
1972
+ printer.print_in_terminal("models_output_price_updated", style="green", name=name, price=price)
1973
+ result_manager.add_result(content=f"models_output_price_updated: {name} {price}",meta={
1974
+ "action": "models",
1975
+ "input": {
1976
+ "query": query
1977
+ }
1978
+ })
1979
+ else:
1980
+ printer.print_in_terminal("models_not_found", style="red", name=name)
1981
+ result_manager.add_result(content=f"models_not_found: {name}",meta={
1982
+ "action": "models",
1983
+ "input": {
1984
+ "query": query
1985
+ }
1986
+ })
1987
+ except ValueError as e:
1988
+ printer.print_in_terminal("models_invalid_price", style="red", error=str(e))
1989
+ result_manager.add_result(content=f"models_invalid_price: {str(e)}",meta={
1990
+ "action": "models",
1991
+ "input": {
1992
+ "query": query
1993
+ }
1994
+ })
1995
+ else:
1996
+ result_manager.add_result(content=printer.get_message_from_key("models_output_price_usage"),meta={
1997
+ "action": "models",
1998
+ "input": {
1999
+ "query": query
2000
+ }
2001
+ })
2002
+ printer.print_in_terminal("models_output_price_usage", style="red")
2003
+
2004
+ elif subcmd == "/speed":
2005
+ args = query.strip().split()
2006
+ if len(args) >= 2:
2007
+ name = args[0]
2008
+ try:
2009
+ speed = float(args[1])
2010
+ if models_module.update_model_speed(name, speed):
2011
+ printer.print_in_terminal("models_speed_updated", style="green", name=name, speed=speed)
2012
+ result_manager.add_result(content=f"models_speed_updated: {name} {speed}",meta={
2013
+ "action": "models",
2014
+ "input": {
2015
+ "query": query
2016
+ }
2017
+ })
2018
+ else:
2019
+ printer.print_in_terminal("models_not_found", style="red", name=name)
2020
+ result_manager.add_result(content=f"models_not_found: {name}",meta={
2021
+ "action": "models",
2022
+ "input": {
2023
+ "query": query
2024
+ }
2025
+ })
2026
+ except ValueError as e:
2027
+ printer.print_in_terminal("models_invalid_speed", style="red", error=str(e))
2028
+ result_manager.add_result(content=f"models_invalid_speed: {str(e)}",meta={
2029
+ "action": "models",
2030
+ "input": {
2031
+ "query": query
2032
+ }
2033
+ })
2034
+ else:
2035
+ result_manager.add_result(content=printer.get_message_from_key("models_speed_usage"),meta={
2036
+ "action": "models",
2037
+ "input": {
2038
+ "query": query
2039
+ }
2040
+ })
2041
+ printer.print_in_terminal("models_speed_usage", style="red")
2042
+
2043
+ elif subcmd == "/speed-test":
2044
+ from autocoder.common.model_speed_test import render_speed_test_in_terminal
2045
+ test_rounds = 1 # 默认测试轮数
2046
+
2047
+ enable_long_context = False
2048
+ if "/long_context" in query:
2049
+ enable_long_context = True
2050
+ query = query.replace("/long_context", "", 1).strip()
2051
+
2052
+ if "/long-context" in query:
2053
+ enable_long_context = True
2054
+ query = query.replace("/long-context", "", 1).strip()
2055
+
2056
+ # 解析可选的测试轮数参数
2057
+ args = query.strip().split()
2058
+ if args and args[0].isdigit():
2059
+ test_rounds = int(args[0])
2060
+
2061
+ render_speed_test_in_terminal(product_mode, test_rounds,enable_long_context=enable_long_context)
2062
+ ## 等待优化,获取明细数据
2063
+ result_manager.add_result(content="models test success",meta={
2064
+ "action": "models",
2065
+ "input": {
2066
+ "query": query
2067
+ }
2068
+ })
2069
+
2070
+ elif subcmd == "/add":
2071
+ # Support both simplified and legacy formats
2072
+ args = query.strip().split(" ")
2073
+ if len(args) == 2:
2074
+ # Simplified: /models /add <name> <api_key>
2075
+ name, api_key = args[0], args[1]
2076
+ result = models_module.update_model_with_api_key(name, api_key)
2077
+ if result:
2078
+ result_manager.add_result(content=f"models_added: {name}",meta={
2079
+ "action": "models",
2080
+ "input": {
2081
+ "query": query
2082
+ }
2083
+ })
2084
+ printer.print_in_terminal("models_added", style="green", name=name)
2085
+ else:
2086
+ result_manager.add_result(content=f"models_add_failed: {name}",meta={
2087
+ "action": "models",
2088
+ "input": {
2089
+ "query": query
2090
+ }
2091
+ })
2092
+ printer.print_in_terminal("models_add_failed", style="red", name=name)
2093
+ else:
2094
+ printer.print_in_terminal("models_add_usage", style="red")
2095
+ result_manager.add_result(content=printer.get_message_from_key("models_add_usage"),meta={
2096
+ "action": "models",
2097
+ "input": {
2098
+ "query": query
2099
+ }
2100
+ })
2101
+
2102
+ elif subcmd == "/add_model":
2103
+ # Parse key=value pairs: /models /add_model name=abc base_url=http://xx ...
2104
+ # Collect key=value pairs
2105
+ kv_pairs = shlex.split(query)
2106
+ data_dict = {}
2107
+ for pair in kv_pairs:
2108
+ if '=' not in pair:
2109
+ printer.print_in_terminal("models_add_model_params", style="red")
2110
+ continue
2111
+ k, v = pair.split('=', 1)
2112
+ data_dict[k.strip()] = v.strip()
2113
+
2114
+ # Name is required
2115
+ if "name" not in data_dict:
2116
+ printer.print_in_terminal("models_add_model_name_required", style="red")
2117
+ return
2118
+
2119
+ # Check duplication
2120
+ if any(m["name"] == data_dict["name"] for m in models_data):
2121
+ printer.print_in_terminal("models_add_model_exists", style="yellow", name=data_dict["name"])
2122
+ result_manager.add_result(content=printer.get_message_from_key("models_add_model_exists",name=data_dict["name"]),meta={
2123
+ "action": "models",
2124
+ "input": {
2125
+ "query": query
2126
+ }
2127
+ })
2128
+ return
2129
+
2130
+ # Create model with defaults
2131
+ final_model = {
2132
+ "name": data_dict["name"],
2133
+ "model_type": data_dict.get("model_type", "saas/openai"),
2134
+ "model_name": data_dict.get("model_name", data_dict["name"]),
2135
+ "base_url": data_dict.get("base_url", "https://api.openai.com/v1"),
2136
+ "api_key_path": data_dict.get("api_key_path", "api.openai.com"),
2137
+ "description": data_dict.get("description", ""),
2138
+ "is_reasoning": data_dict.get("is_reasoning", "false") in ["true", "True", "TRUE", "1"]
2139
+ }
2140
+
2141
+ models_data.append(final_model)
2142
+ models_module.save_models(models_data)
2143
+ printer.print_in_terminal("models_add_model_success", style="green", name=data_dict["name"])
2144
+ result_manager.add_result(content=f"models_add_model_success: {data_dict['name']}",meta={
2145
+ "action": "models",
2146
+ "input": {
2147
+ "query": query
2148
+ }
2149
+ })
2150
+
2151
+ elif subcmd == "/remove":
2152
+ args = query.strip().split(" ")
2153
+ if len(args) < 1:
2154
+ printer.print_in_terminal("models_add_usage", style="red")
2155
+ result_manager.add_result(content=printer.get_message_from_key("models_add_usage"),meta={
2156
+ "action": "models",
2157
+ "input": {
2158
+ "query": query
2159
+ }
2160
+ })
2161
+ return
2162
+ name = args[0]
2163
+ filtered_models = [m for m in models_data if m["name"] != name]
2164
+ if len(filtered_models) == len(models_data):
2165
+ printer.print_in_terminal("models_add_model_remove", style="yellow", name=name)
2166
+ result_manager.add_result(content=printer.get_message_from_key("models_add_model_remove",name=name),meta={
2167
+ "action": "models",
2168
+ "input": {
2169
+ "query": query
2170
+ }
2171
+ })
2172
+ return
2173
+ models_module.save_models(filtered_models)
2174
+ printer.print_in_terminal("models_add_model_removed", style="green", name=name)
2175
+ result_manager.add_result(content=printer.get_message_from_key("models_add_model_removed",name=name),meta={
2176
+ "action": "models",
2177
+ "input": {
2178
+ "query": query
2179
+ }
2180
+ })
2181
+ else:
2182
+ printer.print_in_terminal("models_unknown_subcmd", style="yellow", subcmd=subcmd)
2183
+ result_manager.add_result(content=printer.get_message_from_key("models_unknown_subcmd",subcmd=subcmd),meta={
2184
+ "action": "models",
2185
+ "input": {
2186
+ "query": query
2187
+ }
2188
+ })
2189
+
2190
+ def exclude_dirs(dir_names: List[str]):
2191
+ new_dirs = dir_names
2192
+ existing_dirs = memory.get("exclude_dirs", [])
2193
+ dirs_to_add = [d for d in new_dirs if d not in existing_dirs]
2194
+
2195
+ if dirs_to_add:
2196
+ existing_dirs.extend(dirs_to_add)
2197
+ if "exclude_dirs" not in memory:
2198
+ memory["exclude_dirs"] = existing_dirs
2199
+ print(f"Added exclude dirs: {dirs_to_add}")
2200
+ exclude_files([f"regex://.*/{d}/*." for d in dirs_to_add])
2201
+ else:
2202
+ print("All specified dirs are already in the exclude list.")
2203
+ save_memory()
2204
+ completer.refresh_files()
2205
+
2206
+ def exclude_files(query: str):
2207
+ result_manager = ResultManager()
2208
+ printer = Printer()
2209
+ if "/list" in query:
2210
+ query = query.replace("/list", "", 1).strip()
2211
+ existing_file_patterns = memory.get("exclude_files", [])
2212
+ console = Console()
2213
+ # 打印表格
2214
+ table = Table(title="Exclude Files")
2215
+ table.add_column("File Pattern")
2216
+ for file_pattern in existing_file_patterns:
2217
+ table.add_row(file_pattern)
2218
+ console.print(table)
2219
+ result_manager.add_result(content=f"Exclude files: {existing_file_patterns}",meta={
2220
+ "action": "exclude_files",
2221
+ "input": {
2222
+ "query": query
2223
+ }
2224
+ })
2225
+ return
2226
+
2227
+ if "/drop" in query:
2228
+ query = query.replace("/drop", "", 1).strip()
2229
+ existing_file_patterns = memory.get("exclude_files", [])
2230
+ existing_file_patterns.remove(query.strip())
2231
+ memory["exclude_files"] = existing_file_patterns
2232
+ save_memory()
2233
+ completer.refresh_files()
2234
+ result_manager.add_result(content=f"Dropped exclude files: {query}",meta={
2235
+ "action": "exclude_files",
2236
+ "input": {
2237
+ "query": query
2238
+ }
2239
+ })
2240
+ return
2241
+
2242
+ new_file_patterns = query.strip().split(",")
2243
+
2244
+ existing_file_patterns = memory.get("exclude_files", [])
2245
+ file_patterns_to_add = [f for f in new_file_patterns if f not in existing_file_patterns]
2246
+
2247
+ for file_pattern in file_patterns_to_add:
2248
+ if not file_pattern.startswith("regex://"):
2249
+ result_manager.add_result(content=printer.get_message_from_key_with_format("invalid_file_pattern", file_pattern=file_pattern),meta={
2250
+ "action": "exclude_files",
2251
+ "input": {
2252
+ "query": file_pattern
2253
+ }
2254
+ })
2255
+ raise ValueError(printer.get_message_from_key_with_format("invalid_file_pattern", file_pattern=file_pattern))
2256
+
2257
+ if file_patterns_to_add:
2258
+ existing_file_patterns.extend(file_patterns_to_add)
2259
+ if "exclude_files" not in memory:
2260
+ memory["exclude_files"] = existing_file_patterns
2261
+
2262
+ result_manager.add_result(content=f"Added exclude files: {file_patterns_to_add}",meta={
2263
+ "action": "exclude_files",
2264
+ "input": {
2265
+ "query": file_patterns_to_add
2266
+ }
2267
+ })
2268
+ save_memory()
2269
+ print(f"Added exclude files: {file_patterns_to_add}")
2270
+ else:
2271
+ result_manager.add_result(content=f"All specified files are already in the exclude list.",meta={
2272
+ "action": "exclude_files",
2273
+ "input": {
2274
+ "query": file_patterns_to_add
2275
+ }
2276
+ })
2277
+ print("All specified files are already in the exclude list.")
2278
+
2279
+
2280
+
2281
+ @run_in_raw_thread()
2282
+ def index_build():
2283
+ conf = memory.get("conf", {})
2284
+ yaml_config = {
2285
+ "include_file": ["./base/base.yml"],
2286
+ }
2287
+
2288
+ for key, value in conf.items():
2289
+ converted_value = convert_config_value(key, value)
2290
+ if converted_value is not None:
2291
+ yaml_config[key] = converted_value
2292
+
2293
+ yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
2294
+ yaml_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
2295
+
2296
+ with open(yaml_file, "w",encoding="utf-8") as f:
2297
+ f.write(yaml_content)
2298
+ try:
2299
+ with redirect_stdout() as output:
2300
+ auto_coder_main(["index", "--file", yaml_file])
2301
+ print(output.getvalue(), flush=True)
2302
+ completer.refresh_files()
2303
+ finally:
2304
+ os.remove(yaml_file)
2305
+
2306
+
2307
+ def get_final_config()->AutoCoderArgs:
2308
+ conf = memory.get("conf", {})
2309
+ yaml_config = {
2310
+ "include_file": ["./base/base.yml"],
2311
+ "auto_merge": conf.get("auto_merge", "editblock"),
2312
+ "human_as_model": conf.get("human_as_model", "false") == "true",
2313
+ "skip_build_index": conf.get("skip_build_index", "true") == "true",
2314
+ "skip_confirm": conf.get("skip_confirm", "true") == "true",
2315
+ "silence": conf.get("silence", "true") == "true",
2316
+ "include_project_structure": conf.get("include_project_structure", "true")
2317
+ == "true",
2318
+ }
2319
+ for key, value in conf.items():
2320
+ converted_value = convert_config_value(key, value)
2321
+ if converted_value is not None:
2322
+ yaml_config[key] = converted_value
2323
+
2324
+ temp_yaml = os.path.join("actions", f"{uuid.uuid4()}.yml")
2325
+ try:
2326
+ with open(temp_yaml, "w",encoding="utf-8") as f:
2327
+ f.write(convert_yaml_config_to_str(yaml_config=yaml_config))
2328
+ args = convert_yaml_to_config(temp_yaml)
2329
+ finally:
2330
+ if os.path.exists(temp_yaml):
2331
+ os.remove(temp_yaml)
2332
+ return args
2333
+
2334
+ def help(query: str):
2335
+ from autocoder.common.auto_configure import ConfigAutoTuner,MemoryConfig,AutoConfigRequest
2336
+ args = get_final_config()
2337
+ product_mode = memory.get("product_mode", "lite")
2338
+ llm = get_single_llm(args.chat_model or args.model, product_mode=product_mode)
2339
+ auto_config_tuner = ConfigAutoTuner(args=args, llm=llm, memory_config=MemoryConfig(memory=memory, save_memory_func=save_memory))
2340
+ auto_config_tuner.tune(AutoConfigRequest(query=query))
2341
+
2342
+ @run_in_raw_thread()
2343
+ def index_export(path: str):
2344
+ from autocoder.common.index_import_export import export_index
2345
+ from autocoder.common.printer import Printer
2346
+ printer = Printer()
2347
+ project_root = os.getcwd()
2348
+ if export_index(project_root, path):
2349
+ printer.print_in_terminal("index_export_success", path=path)
2350
+ else:
2351
+ printer.print_in_terminal("index_export_fail", path=path)
2352
+
2353
+ @run_in_raw_thread()
2354
+ def index_import(path: str):
2355
+ from autocoder.common.index_import_export import import_index
2356
+ from autocoder.common.printer import Printer
2357
+ printer = Printer()
2358
+ project_root = os.getcwd()
2359
+ if import_index(project_root, path):
2360
+ printer.print_in_terminal("index_import_success", path=path)
2361
+ else:
2362
+ printer.print_in_terminal("index_import_fail", path=path)
2363
+
2364
+ @run_in_raw_thread()
2365
+ def index_query(query: str):
2366
+ conf = memory.get("conf", {})
2367
+ yaml_config = {
2368
+ "include_file": ["./base/base.yml"],
2369
+ }
2370
+
2371
+ for key, value in conf.items():
2372
+ converted_value = convert_config_value(key, value)
2373
+ if converted_value is not None:
2374
+ yaml_config[key] = converted_value
2375
+
2376
+ yaml_config["query"] = query
2377
+
2378
+ yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
2379
+ yaml_file = os.path.join("actions", f"{uuid.uuid4()}.yml")
2380
+
2381
+ with open(yaml_file, "w",encoding="utf-8") as f:
2382
+ f.write(yaml_content)
2383
+ try:
2384
+ with redirect_stdout() as output:
2385
+ auto_coder_main(["index-query", "--file", yaml_file])
2386
+ print(output.getvalue(), flush=True)
2387
+ finally:
2388
+ os.remove(yaml_file)
2389
+
2390
+
2391
+ def list_files():
2392
+ console = Console()
2393
+ project_root = os.getcwd()
2394
+ current_files = memory["current_files"]["files"]
2395
+
2396
+ if current_files:
2397
+ table = Table(
2398
+ title="Current Files", show_header=True, header_style="bold magenta"
2399
+ )
2400
+ table.add_column("File", style="green")
2401
+ for file in current_files:
2402
+ table.add_row(os.path.relpath(file, project_root))
2403
+ console.print(Panel(table, border_style="blue"))
2404
+ else:
2405
+ console.print(
2406
+ Panel(
2407
+ "No files in the current session.",
2408
+ title="Current Files",
2409
+ border_style="yellow",
2410
+ )
2411
+ )
2412
+
2413
+
2414
+ def gen_and_exec_shell_command(query: str):
2415
+ from rich.prompt import Confirm
2416
+ from autocoder.common.printer import Printer
2417
+ from rich.console import Console
2418
+
2419
+ printer = Printer()
2420
+ console = Console()
2421
+ # Generate the shell script
2422
+ shell_script = generate_shell_command(query)
2423
+
2424
+ # Ask for confirmation using rich
2425
+ if Confirm.ask(
2426
+ printer.get_message_from_key("confirm_execute_shell_script"),
2427
+ default=False
2428
+ ):
2429
+ execute_shell_command(shell_script)
2430
+ else:
2431
+ console.print(
2432
+ Panel(
2433
+ printer.get_message_from_key("shell_script_not_executed"),
2434
+ border_style="yellow"
2435
+ )
2436
+ )
2437
+
2438
+
2439
+ def lib_command(args: List[str]):
2440
+ console = Console()
2441
+ lib_dir = os.path.join(".auto-coder", "libs")
2442
+ llm_friendly_packages_dir = os.path.join(lib_dir, "llm_friendly_packages")
2443
+
2444
+ if not os.path.exists(lib_dir):
2445
+ os.makedirs(lib_dir)
2446
+
2447
+ if "libs" not in memory:
2448
+ memory["libs"] = {}
2449
+
2450
+ if not args:
2451
+ console.print(
2452
+ "Please specify a subcommand: /add, /remove, /list, /set-proxy, /refresh, or /get"
2453
+ )
2454
+ return
2455
+
2456
+ subcommand = args[0]
2457
+
2458
+ if subcommand == "/add":
2459
+ if len(args) < 2:
2460
+ console.print("Please specify a library name to add")
2461
+ return
2462
+ lib_name = args[1].strip()
2463
+
2464
+ # Clone the repository if it doesn't exist
2465
+ if not os.path.exists(llm_friendly_packages_dir):
2466
+ console.print("Cloning llm_friendly_packages repository...")
2467
+ try:
2468
+ proxy_url = memory.get(
2469
+ "lib-proxy", "https://github.com/allwefantasy/llm_friendly_packages"
2470
+ )
2471
+ git.Repo.clone_from(
2472
+ proxy_url,
2473
+ llm_friendly_packages_dir,
2474
+ )
2475
+ console.print(
2476
+ "Successfully cloned llm_friendly_packages repository")
2477
+ except git.exc.GitCommandError as e:
2478
+ console.print(f"Error cloning repository: {e}")
2479
+
2480
+ if lib_name in memory["libs"]:
2481
+ console.print(f"Library {lib_name} is already added")
2482
+ else:
2483
+ memory["libs"][lib_name] = {}
2484
+ console.print(f"Added library: {lib_name}")
2485
+
2486
+ save_memory()
2487
+
2488
+ elif subcommand == "/remove":
2489
+ if len(args) < 2:
2490
+ console.print("Please specify a library name to remove")
2491
+ return
2492
+ lib_name = args[1].strip()
2493
+ if lib_name in memory["libs"]:
2494
+ del memory["libs"][lib_name]
2495
+ console.print(f"Removed library: {lib_name}")
2496
+ save_memory()
2497
+ else:
2498
+ console.print(f"Library {lib_name} is not in the list")
2499
+
2500
+ elif subcommand == "/list":
2501
+ if memory["libs"]:
2502
+ table = Table(title="Added Libraries")
2503
+ table.add_column("Library Name", style="cyan")
2504
+ for lib_name in memory["libs"]:
2505
+ table.add_row(lib_name)
2506
+ console.print(table)
2507
+ else:
2508
+ console.print("No libraries added yet")
2509
+
2510
+ elif subcommand == "/set-proxy":
2511
+ if len(args) == 1:
2512
+ current_proxy = memory.get("lib-proxy", "No proxy set")
2513
+ console.print(f"Current proxy: {current_proxy}")
2514
+ elif len(args) == 2:
2515
+ proxy_url = args[1]
2516
+ memory["lib-proxy"] = proxy_url
2517
+ console.print(f"Set proxy to: {proxy_url}")
2518
+ save_memory()
2519
+ else:
2520
+ console.print("Invalid number of arguments for /set-proxy")
2521
+
2522
+ elif subcommand == "/refresh":
2523
+ if os.path.exists(llm_friendly_packages_dir):
2524
+ try:
2525
+ repo = git.Repo(llm_friendly_packages_dir)
2526
+ origin = repo.remotes.origin
2527
+ proxy_url = memory.get("lib-proxy")
2528
+
2529
+ current_url = origin.url
2530
+
2531
+ if proxy_url and proxy_url != current_url:
2532
+ new_url = proxy_url
2533
+ origin.set_url(new_url)
2534
+ console.print(f"Updated remote URL to: {new_url}")
2535
+
2536
+ origin.pull()
2537
+ console.print(
2538
+ "Successfully updated llm_friendly_packages repository")
2539
+
2540
+ except git.exc.GitCommandError as e:
2541
+ console.print(f"Error updating repository: {e}")
2542
+ else:
2543
+ console.print(
2544
+ "llm_friendly_packages repository does not exist. Please run /lib /add <library_name> command first to clone it."
2545
+ )
2546
+
2547
+ elif subcommand == "/get":
2548
+ if len(args) < 2:
2549
+ console.print("Please specify a package name to get")
2550
+ return
2551
+ package_name = args[1].strip()
2552
+ docs = get_llm_friendly_package_docs(package_name, return_paths=True)
2553
+ if docs:
2554
+ table = Table(title=f"Markdown Files for {package_name}")
2555
+ table.add_column("File Path", style="cyan")
2556
+ for doc in docs:
2557
+ table.add_row(doc)
2558
+ console.print(table)
2559
+ else:
2560
+ console.print(
2561
+ f"No markdown files found for package: {package_name}")
2562
+
2563
+ else:
2564
+ console.print(f"Unknown subcommand: {subcommand}")
2565
+
2566
+
2567
+ def execute_shell_command(command: str):
2568
+ from autocoder.common.shells import execute_shell_command as shell_exec
2569
+ shell_exec(command)
2570
+
2571
+
2572
+ def conf_export(path: str):
2573
+ from autocoder.common.conf_import_export import export_conf
2574
+ export_conf(os.getcwd(), path)
2575
+
2576
+ def conf_import(path: str):
2577
+ from autocoder.common.conf_import_export import import_conf
2578
+ import_conf(os.getcwd(), path)
2579
+
2580
+ @run_in_raw_thread()
2581
+ def auto_command(params,query: str):
2582
+ """处理/auto指令"""
2583
+ from autocoder.commands.auto_command import CommandAutoTuner, AutoCommandRequest, CommandConfig, MemoryConfig
2584
+ args = get_final_config()
2585
+ # help(query)
2586
+
2587
+ # 准备请求参数
2588
+ request = AutoCommandRequest(
2589
+ user_input=query
2590
+ )
2591
+
2592
+ # 初始化调优器
2593
+ llm = get_single_llm(args.chat_model or args.model,product_mode=args.product_mode)
2594
+ tuner = CommandAutoTuner(llm,
2595
+ args=args,
2596
+ memory_config=MemoryConfig(memory=memory, save_memory_func=save_memory),
2597
+ command_config=CommandConfig(
2598
+ add_files=add_files,
2599
+ remove_files=remove_files,
2600
+ list_files=list_files,
2601
+ conf=configure,
2602
+ revert=revert,
2603
+ commit=commit,
2604
+ help=help,
2605
+ exclude_dirs=exclude_dirs,
2606
+ exclude_files=exclude_files,
2607
+ ask=ask,
2608
+ chat=chat,
2609
+ coding=coding,
2610
+ design=design,
2611
+ summon=summon,
2612
+ lib=lib_command,
2613
+ mcp=mcp,
2614
+ models=manage_models,
2615
+ index_build=index_build,
2616
+ index_query=index_query,
2617
+ execute_shell_command=execute_shell_command,
2618
+ generate_shell_command=generate_shell_command,
2619
+ conf_export=conf_export,
2620
+ conf_import=conf_import,
2621
+ index_export=index_export,
2622
+ index_import=index_import
2623
+ ))
2624
+
2625
+ # 生成建议
2626
+ response = tuner.analyze(request)
2627
+ printer = Printer()
2628
+ # 显示建议
2629
+ console = Console()
2630
+ console.print(Panel(
2631
+ Markdown(response.reasoning or ""),
2632
+ title=printer.get_message_from_key_with_format("auto_command_reasoning_title"),
2633
+ border_style="blue",
2634
+ padding=(1, 2)
2635
+ ))