autocoder-nano 0.1.35__py3-none-any.whl → 0.1.37__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.
- autocoder_nano/auto_coder_nano.py +25 -218
- autocoder_nano/chat/__init__.py +64 -0
- autocoder_nano/index/__init__.py +93 -0
- autocoder_nano/project/__init__.py +15 -1
- autocoder_nano/utils/config_utils.py +131 -0
- autocoder_nano/version.py +1 -1
- {autocoder_nano-0.1.35.dist-info → autocoder_nano-0.1.37.dist-info}/METADATA +1 -1
- {autocoder_nano-0.1.35.dist-info → autocoder_nano-0.1.37.dist-info}/RECORD +12 -10
- {autocoder_nano-0.1.35.dist-info → autocoder_nano-0.1.37.dist-info}/LICENSE +0 -0
- {autocoder_nano-0.1.35.dist-info → autocoder_nano-0.1.37.dist-info}/WHEEL +0 -0
- {autocoder_nano-0.1.35.dist-info → autocoder_nano-0.1.37.dist-info}/entry_points.txt +0 -0
- {autocoder_nano-0.1.35.dist-info → autocoder_nano-0.1.37.dist-info}/top_level.txt +0 -0
@@ -10,11 +10,15 @@ import uuid
|
|
10
10
|
|
11
11
|
from autocoder_nano.agent.agentic_edit import AgenticEdit
|
12
12
|
from autocoder_nano.agent.agentic_edit_types import AgenticEditRequest
|
13
|
+
from autocoder_nano.chat import stream_chat_display
|
13
14
|
from autocoder_nano.edit import Dispacher
|
14
15
|
from autocoder_nano.helper import show_help
|
15
|
-
from autocoder_nano.
|
16
|
-
from autocoder_nano.index
|
17
|
-
|
16
|
+
from autocoder_nano.project import project_source
|
17
|
+
from autocoder_nano.index import (index_export, index_import, index_build,
|
18
|
+
index_build_and_filter, extract_symbols)
|
19
|
+
# from autocoder_nano.index.entry import build_index_and_filter_files
|
20
|
+
# from autocoder_nano.index.index_manager import IndexManager
|
21
|
+
# from autocoder_nano.index.symbols_utils import extract_symbols
|
18
22
|
from autocoder_nano.llm_client import AutoLLM
|
19
23
|
from autocoder_nano.rules.rules_learn import AutoRulesLearn
|
20
24
|
from autocoder_nano.utils.completer_utils import CommandCompleter
|
@@ -25,7 +29,7 @@ from autocoder_nano.templates import create_actions
|
|
25
29
|
from autocoder_nano.git_utils import (repo_init, commit_changes, revert_changes,
|
26
30
|
get_uncommitted_changes, generate_commit_message)
|
27
31
|
from autocoder_nano.sys_utils import default_exclude_dirs, detect_env
|
28
|
-
from autocoder_nano.project import PyProject, SuffixProject
|
32
|
+
# from autocoder_nano.project import PyProject, SuffixProject, project_source
|
29
33
|
from autocoder_nano.utils.printer_utils import Printer
|
30
34
|
|
31
35
|
import yaml
|
@@ -316,119 +320,13 @@ def exclude_files(query: str):
|
|
316
320
|
|
317
321
|
def index_command(llm):
|
318
322
|
args = get_final_config(query="", delete_execute_file=True)
|
319
|
-
|
320
|
-
|
321
|
-
printer.print_text(f"开始对目录 {source_dir} 中的源代码进行索引", style="green")
|
322
|
-
if args.project_type == "py":
|
323
|
-
pp = PyProject(llm=llm, args=args)
|
324
|
-
else:
|
325
|
-
pp = SuffixProject(llm=llm, args=args)
|
326
|
-
pp.run()
|
327
|
-
_sources = pp.sources
|
328
|
-
index_manager = IndexManager(args=args, source_codes=_sources, llm=llm)
|
329
|
-
index_manager.build_index()
|
330
|
-
|
331
|
-
|
332
|
-
def index_export(export_path: str) -> bool:
|
333
|
-
try:
|
334
|
-
index_path = os.path.join(project_root, ".auto-coder", "index.json")
|
335
|
-
if not os.path.exists(index_path):
|
336
|
-
printer.print_text(Text(f"索引文件不存在. ", style="bold red"))
|
337
|
-
return False
|
338
|
-
|
339
|
-
with open(index_path, "r", encoding="utf-8") as f:
|
340
|
-
index_data = json.load(f)
|
341
|
-
|
342
|
-
converted_data = {}
|
343
|
-
for abs_path, data in index_data.items():
|
344
|
-
try:
|
345
|
-
rel_path = os.path.relpath(abs_path, project_root)
|
346
|
-
data["module_name"] = rel_path
|
347
|
-
converted_data[rel_path] = data
|
348
|
-
except ValueError:
|
349
|
-
printer.print_text(Text(f"索引转换路径失败. ", style="dim yellow"))
|
350
|
-
converted_data[abs_path] = data
|
351
|
-
|
352
|
-
export_file = os.path.join(export_path, "index.json")
|
353
|
-
with open(export_file, "w", encoding="utf-8") as f:
|
354
|
-
json.dump(converted_data, f, indent=2)
|
355
|
-
printer.print_text(Text(f"索引文件导出成功. ", style="bold green"))
|
356
|
-
return True
|
357
|
-
except Exception as err:
|
358
|
-
printer.print_text(Text(f"索引文件导出失败: {err}", style="bold red"))
|
359
|
-
return False
|
360
|
-
|
361
|
-
|
362
|
-
def index_import(import_path: str):
|
363
|
-
try:
|
364
|
-
import_file = os.path.join(import_path, "index.json")
|
365
|
-
if not os.path.exists(import_file):
|
366
|
-
printer.print_text(Text(f"导入索引文件不存在", style="bold red"))
|
367
|
-
return False
|
368
|
-
with open(import_file, "r", encoding="utf-8") as f:
|
369
|
-
index_data = json.load(f)
|
370
|
-
converted_data = {}
|
371
|
-
for rel_path, data in index_data.items():
|
372
|
-
try:
|
373
|
-
abs_path = os.path.join(project_root, rel_path)
|
374
|
-
data["module_name"] = abs_path
|
375
|
-
converted_data[abs_path] = data
|
376
|
-
except Exception as err:
|
377
|
-
printer.print_text(Text(f"{rel_path} 索引转换路径失败: {err}", style="dim yellow"))
|
378
|
-
converted_data[rel_path] = data
|
379
|
-
# Backup existing index
|
380
|
-
index_path = os.path.join(project_root, ".auto-coder", "index.json")
|
381
|
-
if os.path.exists(index_path):
|
382
|
-
printer.print_text(Text(f"原索引文件不存在", style="bold yellow"))
|
383
|
-
backup_path = index_path + ".bak"
|
384
|
-
shutil.copy2(index_path, backup_path)
|
385
|
-
|
386
|
-
# Write new index
|
387
|
-
with open(index_path, "w", encoding="utf-8") as f:
|
388
|
-
json.dump(converted_data, f, indent=2)
|
389
|
-
return True
|
390
|
-
except Exception as err:
|
391
|
-
printer.print_text(Text(f"索引文件导入失败: {err}", style="bold red"))
|
392
|
-
return False
|
323
|
+
index_build(llm=llm, args=args, sources_codes=project_source(source_llm=llm, args=args))
|
324
|
+
return
|
393
325
|
|
394
326
|
|
395
327
|
def index_query_command(query: str, llm: AutoLLM):
|
396
328
|
args = get_final_config(query=query, delete_execute_file=True)
|
397
|
-
|
398
|
-
# args.query = query
|
399
|
-
if args.project_type == "py":
|
400
|
-
pp = PyProject(llm=llm, args=args)
|
401
|
-
else:
|
402
|
-
pp = SuffixProject(llm=llm, args=args)
|
403
|
-
pp.run()
|
404
|
-
_sources = pp.sources
|
405
|
-
|
406
|
-
final_files = []
|
407
|
-
index_manager = IndexManager(args=args, source_codes=_sources, llm=llm)
|
408
|
-
target_files = index_manager.get_target_files_by_query(query)
|
409
|
-
|
410
|
-
if target_files:
|
411
|
-
final_files.extend(target_files.file_list)
|
412
|
-
|
413
|
-
if target_files and args.index_filter_level >= 2:
|
414
|
-
|
415
|
-
related_fiels = index_manager.get_related_files([file.file_path for file in target_files.file_list])
|
416
|
-
|
417
|
-
if related_fiels is not None:
|
418
|
-
final_files.extend(related_fiels.file_list)
|
419
|
-
|
420
|
-
all_results = list({file.file_path: file for file in final_files}.values())
|
421
|
-
printer.print_key_value(
|
422
|
-
{"索引过滤级别": f"{args.index_filter_level}", "查询条件": f"{args.query}", "过滤后的文件数": f"{len(all_results)}"},
|
423
|
-
panel=True
|
424
|
-
)
|
425
|
-
|
426
|
-
printer.print_table_compact(
|
427
|
-
headers=["文件路径", "原因"],
|
428
|
-
data=[[_target_file.file_path, _target_file.reason] for _target_file in all_results],
|
429
|
-
title="Index Query 结果",
|
430
|
-
show_lines=True,
|
431
|
-
)
|
329
|
+
index_build_and_filter(llm=llm, args=args, sources_codes=project_source(source_llm=llm, args=args))
|
432
330
|
return
|
433
331
|
|
434
332
|
|
@@ -481,42 +379,6 @@ def convert_config_value(key, value):
|
|
481
379
|
return None
|
482
380
|
|
483
381
|
|
484
|
-
# def update_config_to_args(query, delete_execute_file: bool = False):
|
485
|
-
# conf = memory.get("conf", {})
|
486
|
-
#
|
487
|
-
# # 默认 chat 配置
|
488
|
-
# yaml_config = {
|
489
|
-
# "include_file": ["./base/base.yml"],
|
490
|
-
# "skip_build_index": conf.get("skip_build_index", "true") == "true",
|
491
|
-
# "skip_confirm": conf.get("skip_confirm", "true") == "true",
|
492
|
-
# "chat_model": conf.get("chat_model", ""),
|
493
|
-
# "code_model": conf.get("code_model", ""),
|
494
|
-
# "auto_merge": conf.get("auto_merge", "editblock"),
|
495
|
-
# "exclude_files": memory.get("exclude_files", [])
|
496
|
-
# }
|
497
|
-
# current_files = memory["current_files"]["files"]
|
498
|
-
# yaml_config["urls"] = current_files
|
499
|
-
# yaml_config["query"] = query
|
500
|
-
#
|
501
|
-
# # 如果 conf 中有设置, 则以 conf 配置为主
|
502
|
-
# for key, value in conf.items():
|
503
|
-
# converted_value = convert_config_value(key, value)
|
504
|
-
# if converted_value is not None:
|
505
|
-
# yaml_config[key] = converted_value
|
506
|
-
#
|
507
|
-
# yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
|
508
|
-
# execute_file = os.path.join(args.source_dir, "actions", f"{uuid.uuid4()}.yml")
|
509
|
-
#
|
510
|
-
# with open(os.path.join(execute_file), "w") as f: # 保存此次查询的细节
|
511
|
-
# f.write(yaml_content)
|
512
|
-
#
|
513
|
-
# convert_yaml_to_config(execute_file) # 更新到args
|
514
|
-
#
|
515
|
-
# if delete_execute_file:
|
516
|
-
# if os.path.exists(execute_file):
|
517
|
-
# os.remove(execute_file)
|
518
|
-
|
519
|
-
|
520
382
|
def print_chat_history(history, max_entries=5):
|
521
383
|
recent_history = history[-max_entries:]
|
522
384
|
data_list = []
|
@@ -574,10 +436,8 @@ def chat(query: str, llm: AutoLLM):
|
|
574
436
|
old_chat_history = json.load(f)
|
575
437
|
if "conversation_history" not in old_chat_history:
|
576
438
|
old_chat_history["conversation_history"] = []
|
577
|
-
old_chat_history["conversation_history"].append(
|
578
|
-
|
579
|
-
chat_history = {"ask_conversation": [
|
580
|
-
], "conversation_history": old_chat_history["conversation_history"]}
|
439
|
+
old_chat_history["conversation_history"].append(old_chat_history.get("ask_conversation", []))
|
440
|
+
chat_history = {"ask_conversation": [], "conversation_history": old_chat_history["conversation_history"]}
|
581
441
|
else:
|
582
442
|
chat_history = {"ask_conversation": [],
|
583
443
|
"conversation_history": []}
|
@@ -604,8 +464,6 @@ def chat(query: str, llm: AutoLLM):
|
|
604
464
|
|
605
465
|
if is_history:
|
606
466
|
show_chat = []
|
607
|
-
# if "conversation_history" in chat_history:
|
608
|
-
# show_chat.extend(chat_history["conversation_history"])
|
609
467
|
if "ask_conversation" in chat_history:
|
610
468
|
show_chat.extend(chat_history["ask_conversation"])
|
611
469
|
print_chat_history(show_chat)
|
@@ -618,13 +476,15 @@ def chat(query: str, llm: AutoLLM):
|
|
618
476
|
chat_llm = llm
|
619
477
|
pre_conversations = []
|
620
478
|
|
621
|
-
if args.project_type == "py":
|
622
|
-
|
623
|
-
else:
|
624
|
-
|
625
|
-
pp.run()
|
626
|
-
_sources = pp.sources
|
627
|
-
|
479
|
+
# if args.project_type == "py":
|
480
|
+
# pp = PyProject(llm=llm, args=args)
|
481
|
+
# else:
|
482
|
+
# pp = SuffixProject(llm=llm, args=args)
|
483
|
+
# pp.run()
|
484
|
+
# _sources = pp.sources
|
485
|
+
# _sources = project_source(source_llm=llm, args=args)
|
486
|
+
# s = build_index_and_filter_files(args=args, llm=llm, sources=_sources)
|
487
|
+
s = index_build_and_filter(llm=llm, args=args, sources_codes=project_source(source_llm=llm, args=args))
|
628
488
|
if s:
|
629
489
|
pre_conversations.append(
|
630
490
|
{
|
@@ -639,50 +499,7 @@ def chat(query: str, llm: AutoLLM):
|
|
639
499
|
|
640
500
|
loaded_conversations = pre_conversations + chat_history["ask_conversation"]
|
641
501
|
|
642
|
-
|
643
|
-
|
644
|
-
MAX_HISTORY_LINES = 15 # 最大保留历史行数
|
645
|
-
lines_buffer = []
|
646
|
-
current_line = ""
|
647
|
-
assistant_response = ""
|
648
|
-
|
649
|
-
try:
|
650
|
-
with Live(Panel("", title="Response", style="cyan"), refresh_per_second=12) as live:
|
651
|
-
for chunk in v:
|
652
|
-
if chunk.choices and chunk.choices[0].delta.content:
|
653
|
-
content = chunk.choices[0].delta.content
|
654
|
-
assistant_response += content
|
655
|
-
|
656
|
-
# 处理换行符分割
|
657
|
-
parts = (current_line + content).split('\n')
|
658
|
-
|
659
|
-
# 最后一部分是未完成的新行
|
660
|
-
if len(parts) > 1:
|
661
|
-
# 将完整行加入缓冲区
|
662
|
-
lines_buffer.extend(parts[:-1])
|
663
|
-
# 保留最近N行历史
|
664
|
-
if len(lines_buffer) > MAX_HISTORY_LINES:
|
665
|
-
del lines_buffer[0: len(lines_buffer) - MAX_HISTORY_LINES]
|
666
|
-
# 更新当前行(最后未完成的部分)
|
667
|
-
current_line = parts[-1]
|
668
|
-
# 构建显示内容 = 历史行 + 当前行
|
669
|
-
display_content = '\n'.join(lines_buffer[-MAX_HISTORY_LINES:] + [current_line])
|
670
|
-
|
671
|
-
live.update(
|
672
|
-
Panel(Markdown(display_content), title="模型返回", border_style="cyan",
|
673
|
-
height=min(25, live.console.height - 4))
|
674
|
-
)
|
675
|
-
|
676
|
-
# 处理最后未换行的内容
|
677
|
-
if current_line:
|
678
|
-
lines_buffer.append(current_line)
|
679
|
-
|
680
|
-
# 最终完整渲染
|
681
|
-
live.update(
|
682
|
-
Panel(Markdown(assistant_response), title="模型返回", border_style="dim blue")
|
683
|
-
)
|
684
|
-
except Exception as e:
|
685
|
-
printer.print_panel(Text(f"{str(e)}", style="red"), title="模型返回", center=True)
|
502
|
+
assistant_response = stream_chat_display(chat_llm=llm, args=args, conversations=loaded_conversations)
|
686
503
|
|
687
504
|
chat_history["ask_conversation"].append({"role": "assistant", "content": assistant_response})
|
688
505
|
|
@@ -1841,20 +1658,10 @@ def main():
|
|
1841
1658
|
memory["mode"] = "normal"
|
1842
1659
|
event.app.invalidate()
|
1843
1660
|
|
1844
|
-
# def _update_bottom_toolbar(toolbar_arg):
|
1845
|
-
# if toolbar_arg in memory['conf']:
|
1846
|
-
# return memory['conf'][toolbar_arg]
|
1847
|
-
# return args.model_dump()[toolbar_arg]
|
1848
|
-
|
1849
1661
|
def get_bottom_toolbar():
|
1850
1662
|
if "mode" not in memory:
|
1851
1663
|
memory["mode"] = "normal"
|
1852
1664
|
mode = memory["mode"]
|
1853
|
-
# skip_build_toolbar = _update_bottom_toolbar('skip_build_index')
|
1854
|
-
# skip_filter_toolbar = _update_bottom_toolbar('skip_filter_index')
|
1855
|
-
# index_filter_toolbar = _update_bottom_toolbar('index_filter_level')
|
1856
|
-
# return (f" 当前模式: {MODES[mode]} (ctl+k 切换模式) | 跳过索引: {skip_build_toolbar} "
|
1857
|
-
# f"| 跳过过滤: {skip_filter_toolbar} | 过滤等级: {index_filter_toolbar}")
|
1858
1665
|
return f" 当前模式: {MODES[mode]} (ctl+k 切换模式) | 当前项目: {project_root}"
|
1859
1666
|
|
1860
1667
|
session = PromptSession(
|
@@ -1924,10 +1731,10 @@ def main():
|
|
1924
1731
|
index_query_command(query=query, llm=auto_llm)
|
1925
1732
|
elif user_input.startswith("/index/export"):
|
1926
1733
|
export_path = user_input[len("/index/export"):].strip()
|
1927
|
-
index_export(export_path)
|
1734
|
+
index_export(project_root, export_path)
|
1928
1735
|
elif user_input.startswith("/index/import"):
|
1929
1736
|
import_path = user_input[len("/index/import"):].strip()
|
1930
|
-
index_import(import_path)
|
1737
|
+
index_import(project_root, import_path)
|
1931
1738
|
elif user_input.startswith("/list_files"):
|
1932
1739
|
list_files()
|
1933
1740
|
elif user_input.startswith("/conf"):
|
@@ -0,0 +1,64 @@
|
|
1
|
+
from rich.live import Live
|
2
|
+
from rich.panel import Panel
|
3
|
+
from rich.markdown import Markdown
|
4
|
+
from rich.text import Text
|
5
|
+
|
6
|
+
from autocoder_nano.llm_client import AutoLLM
|
7
|
+
from autocoder_nano.llm_types import AutoCoderArgs
|
8
|
+
from autocoder_nano.utils.printer_utils import Printer
|
9
|
+
|
10
|
+
|
11
|
+
printer = Printer
|
12
|
+
|
13
|
+
|
14
|
+
def stream_chat_display(
|
15
|
+
chat_llm: AutoLLM, args: AutoCoderArgs, conversations: list[dict], max_history_lines: int = 15, max_height: int = 25
|
16
|
+
) -> str:
|
17
|
+
v = chat_llm.stream_chat_ai(conversations=conversations, model=args.chat_model)
|
18
|
+
|
19
|
+
lines_buffer = []
|
20
|
+
assistant_response = ""
|
21
|
+
current_line = ""
|
22
|
+
|
23
|
+
try:
|
24
|
+
with Live(Panel("", title="Response", style="cyan"), refresh_per_second=12) as live:
|
25
|
+
for chunk in v:
|
26
|
+
if chunk.choices and chunk.choices[0].delta.content:
|
27
|
+
content = chunk.choices[0].delta.content
|
28
|
+
assistant_response += content
|
29
|
+
|
30
|
+
# 处理换行符分割
|
31
|
+
parts = (current_line + content).split('\n')
|
32
|
+
|
33
|
+
# 最后一部分是未完成的新行
|
34
|
+
if len(parts) > 1:
|
35
|
+
# 将完整行加入缓冲区
|
36
|
+
lines_buffer.extend(parts[:-1])
|
37
|
+
# 保留最近N行历史
|
38
|
+
if len(lines_buffer) > max_history_lines:
|
39
|
+
del lines_buffer[0: len(lines_buffer) - max_history_lines]
|
40
|
+
# 更新当前行(最后未完成的部分)
|
41
|
+
current_line = parts[-1]
|
42
|
+
# 构建显示内容 = 历史行 + 当前行
|
43
|
+
display_content = '\n'.join(lines_buffer[-max_history_lines:] + [current_line])
|
44
|
+
|
45
|
+
live.update(
|
46
|
+
Panel(Markdown(display_content), title="模型返回", border_style="cyan",
|
47
|
+
height=min(max_height, live.console.height - 4))
|
48
|
+
)
|
49
|
+
|
50
|
+
# 处理最后未换行的内容
|
51
|
+
if current_line:
|
52
|
+
lines_buffer.append(current_line)
|
53
|
+
|
54
|
+
# 最终完整渲染
|
55
|
+
live.update(
|
56
|
+
Panel(Markdown(assistant_response), title="模型返回", border_style="dim blue")
|
57
|
+
)
|
58
|
+
except Exception as e:
|
59
|
+
printer.print_panel(Text(f"{str(e)}", style="red"), title="模型返回", center=True)
|
60
|
+
|
61
|
+
return assistant_response
|
62
|
+
|
63
|
+
|
64
|
+
__all__ = ["stream_chat_display"]
|
autocoder_nano/index/__init__.py
CHANGED
@@ -0,0 +1,93 @@
|
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
import shutil
|
4
|
+
|
5
|
+
from autocoder_nano.index.entry import build_index_and_filter_files
|
6
|
+
from autocoder_nano.index.index_manager import IndexManager
|
7
|
+
from autocoder_nano.index.symbols_utils import extract_symbols
|
8
|
+
from autocoder_nano.llm_client import AutoLLM
|
9
|
+
from autocoder_nano.llm_types import AutoCoderArgs, SourceCode
|
10
|
+
from autocoder_nano.project import project_source
|
11
|
+
from autocoder_nano.utils.printer_utils import Printer
|
12
|
+
|
13
|
+
|
14
|
+
printer = Printer()
|
15
|
+
|
16
|
+
|
17
|
+
def index_build(llm: AutoLLM, args: AutoCoderArgs, sources_codes: list[SourceCode] = None):
|
18
|
+
if not sources_codes:
|
19
|
+
sources_codes = project_source(source_llm=llm, args=args)
|
20
|
+
index = IndexManager(args=args, source_codes=sources_codes, llm=llm)
|
21
|
+
index.build_index()
|
22
|
+
|
23
|
+
|
24
|
+
def index_build_and_filter(llm: AutoLLM, args: AutoCoderArgs, sources_codes: list[SourceCode] = None) -> str:
|
25
|
+
if not sources_codes:
|
26
|
+
sources_codes = project_source(source_llm=llm, args=args)
|
27
|
+
return build_index_and_filter_files(args=args, llm=llm, sources=sources_codes)
|
28
|
+
|
29
|
+
|
30
|
+
def index_export(project_root: str, export_path: str) -> bool:
|
31
|
+
try:
|
32
|
+
index_path = os.path.join(project_root, ".auto-coder", "index.json")
|
33
|
+
if not os.path.exists(index_path):
|
34
|
+
printer.print_text(f"索引文件不存在. ", style="red")
|
35
|
+
return False
|
36
|
+
|
37
|
+
with open(index_path, "r", encoding="utf-8") as f:
|
38
|
+
index_data = json.load(f)
|
39
|
+
|
40
|
+
converted_data = {}
|
41
|
+
for abs_path, data in index_data.items():
|
42
|
+
try:
|
43
|
+
rel_path = os.path.relpath(abs_path, project_root)
|
44
|
+
data["module_name"] = rel_path
|
45
|
+
converted_data[rel_path] = data
|
46
|
+
except ValueError:
|
47
|
+
printer.print_text(f"索引转换路径失败. ", style="yellow")
|
48
|
+
converted_data[abs_path] = data
|
49
|
+
|
50
|
+
export_file = os.path.join(export_path, "index.json")
|
51
|
+
with open(export_file, "w", encoding="utf-8") as f:
|
52
|
+
json.dump(converted_data, f, indent=2)
|
53
|
+
printer.print_text(f"索引文件导出成功.", style="green")
|
54
|
+
return True
|
55
|
+
except Exception as err:
|
56
|
+
printer.print_text(f"索引文件导出失败: {err}", style="red")
|
57
|
+
return False
|
58
|
+
|
59
|
+
|
60
|
+
def index_import(project_root: str, import_path: str):
|
61
|
+
try:
|
62
|
+
import_file = os.path.join(import_path, "index.json")
|
63
|
+
if not os.path.exists(import_file):
|
64
|
+
printer.print_text(f"导入索引文件不存在. ", style="red")
|
65
|
+
return False
|
66
|
+
with open(import_file, "r", encoding="utf-8") as f:
|
67
|
+
index_data = json.load(f)
|
68
|
+
converted_data = {}
|
69
|
+
for rel_path, data in index_data.items():
|
70
|
+
try:
|
71
|
+
abs_path = os.path.join(project_root, rel_path)
|
72
|
+
data["module_name"] = abs_path
|
73
|
+
converted_data[abs_path] = data
|
74
|
+
except Exception as err:
|
75
|
+
printer.print_text(f"{rel_path} 索引转换路径失败: {err}", style="yellow")
|
76
|
+
converted_data[rel_path] = data
|
77
|
+
# Backup existing index
|
78
|
+
index_path = os.path.join(project_root, ".auto-coder", "index.json")
|
79
|
+
if os.path.exists(index_path):
|
80
|
+
printer.print_text(f"原索引文件不存在", style="yellow")
|
81
|
+
backup_path = index_path + ".bak"
|
82
|
+
shutil.copy2(index_path, backup_path)
|
83
|
+
|
84
|
+
# Write new index
|
85
|
+
with open(index_path, "w", encoding="utf-8") as f:
|
86
|
+
json.dump(converted_data, f, indent=2)
|
87
|
+
return True
|
88
|
+
except Exception as err:
|
89
|
+
printer.print_text(f"索引文件导入失败: {err}", style="red")
|
90
|
+
return False
|
91
|
+
|
92
|
+
|
93
|
+
__all__ = ["index_build", "index_export", "index_import", "index_build_and_filter", "extract_symbols", "IndexManager"]
|
@@ -1,5 +1,19 @@
|
|
1
|
+
from autocoder_nano.llm_client import AutoLLM
|
2
|
+
from autocoder_nano.llm_types import AutoCoderArgs, SourceCode
|
1
3
|
from autocoder_nano.project.pyproject import PyProject
|
2
4
|
from autocoder_nano.project.suffixproject import SuffixProject
|
3
5
|
from autocoder_nano.project.tsproject import TSProject
|
4
6
|
|
5
|
-
|
7
|
+
|
8
|
+
def project_source(source_llm: AutoLLM, args: AutoCoderArgs) -> list[SourceCode]:
|
9
|
+
if args.project_type == "py":
|
10
|
+
pp = PyProject(llm=source_llm, args=args)
|
11
|
+
elif args.project_type == "ts":
|
12
|
+
pp = TSProject(llm=source_llm, args=args)
|
13
|
+
else:
|
14
|
+
pp = SuffixProject(llm=source_llm, args=args)
|
15
|
+
pp.run()
|
16
|
+
return pp.sources
|
17
|
+
|
18
|
+
|
19
|
+
__all__ = ["PyProject", "SuffixProject", "TSProject", "project_source"]
|
@@ -0,0 +1,131 @@
|
|
1
|
+
import os
|
2
|
+
import uuid
|
3
|
+
|
4
|
+
import yaml
|
5
|
+
from jinja2 import Template
|
6
|
+
|
7
|
+
from autocoder_nano.llm_types import AutoCoderArgs
|
8
|
+
from autocoder_nano.utils.printer_utils import Printer
|
9
|
+
|
10
|
+
|
11
|
+
printer = Printer()
|
12
|
+
|
13
|
+
|
14
|
+
def convert_yaml_config_to_str(yaml_config):
|
15
|
+
yaml_content = yaml.safe_dump(
|
16
|
+
yaml_config,
|
17
|
+
allow_unicode=True,
|
18
|
+
default_flow_style=False,
|
19
|
+
default_style=None,
|
20
|
+
)
|
21
|
+
return yaml_content
|
22
|
+
|
23
|
+
|
24
|
+
def convert_config_value(key, value):
|
25
|
+
field_info = AutoCoderArgs.model_fields.get(key)
|
26
|
+
if field_info:
|
27
|
+
if value.lower() in ["true", "false"]:
|
28
|
+
return value.lower() == "true"
|
29
|
+
elif "int" in str(field_info.annotation):
|
30
|
+
return int(value)
|
31
|
+
elif "float" in str(field_info.annotation):
|
32
|
+
return float(value)
|
33
|
+
else:
|
34
|
+
return value
|
35
|
+
else:
|
36
|
+
printer.print_text(f"无效的配置项: {key}", style="red")
|
37
|
+
return None
|
38
|
+
|
39
|
+
|
40
|
+
def resolve_include_path(base_path, include_path):
|
41
|
+
if include_path.startswith(".") or include_path.startswith(".."):
|
42
|
+
full_base_path = os.path.abspath(base_path)
|
43
|
+
parent_dir = os.path.dirname(full_base_path)
|
44
|
+
return os.path.abspath(os.path.join(parent_dir, include_path))
|
45
|
+
else:
|
46
|
+
return include_path
|
47
|
+
|
48
|
+
|
49
|
+
def load_include_files(config, base_path, max_depth=10, current_depth=0):
|
50
|
+
if current_depth >= max_depth:
|
51
|
+
raise ValueError(
|
52
|
+
f"Exceeded maximum include depth of {max_depth},you may have a circular dependency in your include files."
|
53
|
+
)
|
54
|
+
if "include_file" in config:
|
55
|
+
include_files = config["include_file"]
|
56
|
+
if not isinstance(include_files, list):
|
57
|
+
include_files = [include_files]
|
58
|
+
|
59
|
+
for include_file in include_files:
|
60
|
+
abs_include_path = resolve_include_path(base_path, include_file)
|
61
|
+
# printer.print_text(f"正在加载 Include file: {abs_include_path}", style="green")
|
62
|
+
with open(abs_include_path, "r") as f:
|
63
|
+
include_config = yaml.safe_load(f)
|
64
|
+
if not include_config:
|
65
|
+
printer.print_text(f"Include file {abs_include_path} 为空,跳过处理.", style="green")
|
66
|
+
continue
|
67
|
+
config.update(
|
68
|
+
{
|
69
|
+
**load_include_files(include_config, abs_include_path, max_depth, current_depth + 1),
|
70
|
+
**config,
|
71
|
+
}
|
72
|
+
)
|
73
|
+
del config["include_file"]
|
74
|
+
return config
|
75
|
+
|
76
|
+
|
77
|
+
def convert_yaml_to_config(yaml_file: str | dict | AutoCoderArgs):
|
78
|
+
# global args
|
79
|
+
args = AutoCoderArgs()
|
80
|
+
config = {}
|
81
|
+
if isinstance(yaml_file, str):
|
82
|
+
args.file = yaml_file
|
83
|
+
with open(yaml_file, "r") as f:
|
84
|
+
config = yaml.safe_load(f)
|
85
|
+
config = load_include_files(config, yaml_file)
|
86
|
+
if isinstance(yaml_file, dict):
|
87
|
+
config = yaml_file
|
88
|
+
if isinstance(yaml_file, AutoCoderArgs):
|
89
|
+
config = yaml_file.model_dump()
|
90
|
+
for key, value in config.items():
|
91
|
+
if key != "file": # 排除 --file 参数本身
|
92
|
+
# key: ENV {{VARIABLE_NAME}}
|
93
|
+
if isinstance(value, str) and value.startswith("ENV"):
|
94
|
+
template = Template(value.removeprefix("ENV").strip())
|
95
|
+
value = template.render(os.environ)
|
96
|
+
setattr(args, key, value)
|
97
|
+
return args
|
98
|
+
|
99
|
+
|
100
|
+
def get_final_config(project_root: str, memory: dict, query: str, delete_execute_file: bool = False) -> AutoCoderArgs:
|
101
|
+
conf = memory.get("conf", {})
|
102
|
+
yaml_config = {
|
103
|
+
"include_file": ["./base/base.yml"],
|
104
|
+
"skip_build_index": conf.get("skip_build_index", "true") == "true",
|
105
|
+
"skip_confirm": conf.get("skip_confirm", "true") == "true",
|
106
|
+
"chat_model": conf.get("chat_model", ""),
|
107
|
+
"code_model": conf.get("code_model", ""),
|
108
|
+
"auto_merge": conf.get("auto_merge", "editblock"),
|
109
|
+
"exclude_files": memory.get("exclude_files", [])
|
110
|
+
}
|
111
|
+
current_files = memory["current_files"]["files"]
|
112
|
+
yaml_config["urls"] = current_files
|
113
|
+
yaml_config["query"] = query
|
114
|
+
|
115
|
+
# 如果 conf 中有设置, 则以 conf 配置为主
|
116
|
+
for key, value in conf.items():
|
117
|
+
converted_value = convert_config_value(key, value)
|
118
|
+
if converted_value is not None:
|
119
|
+
yaml_config[key] = converted_value
|
120
|
+
|
121
|
+
execute_file = os.path.join(project_root, "actions", f"{uuid.uuid4()}.yml")
|
122
|
+
try:
|
123
|
+
yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
|
124
|
+
with open(os.path.join(execute_file), "w") as f: # 保存此次查询的细节
|
125
|
+
f.write(yaml_content)
|
126
|
+
args = convert_yaml_to_config(execute_file) # 更新到args
|
127
|
+
finally:
|
128
|
+
if delete_execute_file:
|
129
|
+
if os.path.exists(execute_file):
|
130
|
+
os.remove(execute_file)
|
131
|
+
return args
|
autocoder_nano/version.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
autocoder_nano/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
autocoder_nano/auto_coder_nano.py,sha256
|
2
|
+
autocoder_nano/auto_coder_nano.py,sha256=AMJiNqQyEKaU5KqEOo9EzQwQDi5K3lr4YTeuMqdqWSA,74671
|
3
3
|
autocoder_nano/auto_coder_nano_rag.py,sha256=9BtNZ6nC5D5SPTIuziXZOfouCBLOMNzvJMTdDPQEgO8,10436
|
4
4
|
autocoder_nano/auto_coder_nano_ui.py,sha256=ZBskcIJMeTJY7_JipGJaee58G9fUJaOv3LV4hptLc6c,12669
|
5
5
|
autocoder_nano/file_utils.py,sha256=iGbkbQ191nKL4aNufdexYYYQSDM1XrDC9Uxp_PIbawY,661
|
@@ -10,7 +10,7 @@ autocoder_nano/llm_prompt.py,sha256=ViWUfCZp0gDESAAPHBhZc2WhHiFUHIxK6a2xbFu0sjU,
|
|
10
10
|
autocoder_nano/llm_types.py,sha256=T0ugeWdwejy6BJaQrAlk8Pk5qweW2xbggxzHaSpTBOg,11588
|
11
11
|
autocoder_nano/sys_utils.py,sha256=Sn6kr5diaEkVWbYDBrtenr9zw32jVIWvsAReY7_uEd0,1638
|
12
12
|
autocoder_nano/templates.py,sha256=fqlRtnx6HvPE4CbdnPcnLBB-flPwufwcGRpsFD3aW2c,4271
|
13
|
-
autocoder_nano/version.py,sha256=
|
13
|
+
autocoder_nano/version.py,sha256=zGimidx6OporaPNCY3Rm74IgiKpb8FGFXs-gXvOb2SY,79
|
14
14
|
autocoder_nano/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
15
|
autocoder_nano/agent/agent_base.py,sha256=O5Hq6VnoqrXnBE_oXZHXlbmSRdOEe28H65bJ1WhAQjg,16377
|
16
16
|
autocoder_nano/agent/agentic_edit.py,sha256=I1HjRhMabDmtfxcCOKawUJV0wU1GNzKtof19_GNgAjU,88749
|
@@ -48,6 +48,7 @@ autocoder_nano/app/templates/partials/examples.html,sha256=_i7TfpcRqW-IvI69vVXYe
|
|
48
48
|
autocoder_nano/app/templates/partials/header.html,sha256=txCMUmFFWSEDz5xxQwt8oBko8Y_b1bSsVASVOMCsILo,300
|
49
49
|
autocoder_nano/app/templates/partials/input.html,sha256=8CY3JcHaA4nPZ2Vu4ragdYZzzodvF0isQiOGHtdQs6k,1956
|
50
50
|
autocoder_nano/app/templates/partials/message.html,sha256=HWEh_j_yJAbP7zFs6jt88BDzkP7dG6VgPUbS2MT5Ax4,1548
|
51
|
+
autocoder_nano/chat/__init__.py,sha256=FuXp0tcnegngct9Jp8HbgwFkwnhxMirwNFHtoa_vACw,2441
|
51
52
|
autocoder_nano/data/tokenizer.json,sha256=7Lb5_DaYlDRvBRH0B0ynXO5c1fOwbQLxujX805-OEh0,7847602
|
52
53
|
autocoder_nano/edit/__init__.py,sha256=QPMuW7tBTUe0Q00gUPJEmdxWqvunqko9_dsim0ncr7c,623
|
53
54
|
autocoder_nano/edit/actions.py,sha256=N4qzSIE7Ifm7r6Sk-HbgWmbDqMP6jfrpByfpV7rbEo8,6480
|
@@ -56,11 +57,11 @@ autocoder_nano/edit/code/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
|
|
56
57
|
autocoder_nano/edit/code/generate_editblock.py,sha256=s-VTZK0G1OhjEyZXqyjj4sY48fOo02EvHhaxTIw4ytY,13110
|
57
58
|
autocoder_nano/edit/code/merge_editblock.py,sha256=Vk-FOVvaEzKcSRDMyMyR_M77kqj-s5-zejChn4QwLAY,17557
|
58
59
|
autocoder_nano/edit/code/modification_ranker.py,sha256=hnF1acqAzPYKm9hEFxobJHfGGDdM-GclZLxvtt83lGA,3431
|
59
|
-
autocoder_nano/index/__init__.py,sha256=
|
60
|
+
autocoder_nano/index/__init__.py,sha256=fYrXsjRMrL2cjHjH37Bcl51Uqr17_aqlTH-c_2WQcok,3767
|
60
61
|
autocoder_nano/index/entry.py,sha256=S71dfnYC201eQLXwqNCo_Y83ImI1ZxuJ0_m2hz5nCJc,7729
|
61
62
|
autocoder_nano/index/index_manager.py,sha256=ek7AqU8M-Snl5qZYhO_U0SEK3-y1u5OOxD9z-LdDesE,15619
|
62
63
|
autocoder_nano/index/symbols_utils.py,sha256=z_16X6BozTfmric1uU-r2GqzDabJ5ChfAOB4lo7i-_8,1450
|
63
|
-
autocoder_nano/project/__init__.py,sha256=
|
64
|
+
autocoder_nano/project/__init__.py,sha256=KfSsvUVi-MCUJ-AITl8jQpOTPMIW55VmwPzSUP6LnlI,708
|
64
65
|
autocoder_nano/project/pyproject.py,sha256=UZqHBrUmsCW73YkG8shjeFSEYGB_zFDH1ezoPP_f33Q,4478
|
65
66
|
autocoder_nano/project/suffixproject.py,sha256=190GCS25qYi4kPav0Hpk2qgSFalkvJk7VM_pchnfurY,4717
|
66
67
|
autocoder_nano/project/tsproject.py,sha256=3gBS-2aup2W5ehSbhD7Bdr-9v9uL7MgY_7TkLHShh9I,5565
|
@@ -86,12 +87,13 @@ autocoder_nano/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
|
|
86
87
|
autocoder_nano/tools/http_tools.py,sha256=04Tmg8BTwfsw7_-fKBDHv787XU4yQ5UtQSDj0zJBIUc,3189
|
87
88
|
autocoder_nano/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
88
89
|
autocoder_nano/utils/completer_utils.py,sha256=MGA3r5pAvDhp1vNGGCyjHWDtqXnd-CF4zPw7uawSzNM,25556
|
90
|
+
autocoder_nano/utils/config_utils.py,sha256=r5n0De4mz5sL_nj-CeT_F5TxtgWQIN5vv0Z5FiP8GXA,4800
|
89
91
|
autocoder_nano/utils/formatted_log_utils.py,sha256=1d3xvZ1Bo3-I1wQOMdXpwsMX5cl2FWkmpgHGHvTPEvI,5457
|
90
92
|
autocoder_nano/utils/printer_utils.py,sha256=6rGHihCh8DDESWs6qWqwsf3B6qaeM_CNx6crzkl9UCk,15303
|
91
93
|
autocoder_nano/utils/shell_utils.py,sha256=llVTrOrmS1RH2ws7W69tofVtf53Kq04uh-sURphejrU,2477
|
92
|
-
autocoder_nano-0.1.
|
93
|
-
autocoder_nano-0.1.
|
94
|
-
autocoder_nano-0.1.
|
95
|
-
autocoder_nano-0.1.
|
96
|
-
autocoder_nano-0.1.
|
97
|
-
autocoder_nano-0.1.
|
94
|
+
autocoder_nano-0.1.37.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
95
|
+
autocoder_nano-0.1.37.dist-info/METADATA,sha256=hHgyCckA57oWf1OHZlguJgB9IMxqiolJp-6jyQ3yxvY,13591
|
96
|
+
autocoder_nano-0.1.37.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
97
|
+
autocoder_nano-0.1.37.dist-info/entry_points.txt,sha256=Dj8gGZ_AgLy8ANqr2do_DJjpsR3JMh-ztsrUXo4Vn5Q,194
|
98
|
+
autocoder_nano-0.1.37.dist-info/top_level.txt,sha256=D7s34cwIs1F4EAjRRDvO_zTHtUz1Z7UVccFUNlJn7HI,15
|
99
|
+
autocoder_nano-0.1.37.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|