siada-cli 0.0.1__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.
- agent_config.yaml +34 -0
- siada/__init__.py +13 -0
- siada/agent_hub/__init__.py +10 -0
- siada/agent_hub/coder/bug_fix_agent.py +398 -0
- siada/agent_hub/coder/code_gen_agent.py +235 -0
- siada/agent_hub/coder/fe_gen_agent.py +37 -0
- siada/agent_hub/coder/issue_review_agent.py +68 -0
- siada/agent_hub/coder/prompt/__init__.py +0 -0
- siada/agent_hub/coder/prompt/base/__init__.py +0 -0
- siada/agent_hub/coder/prompt/base/capabilities.py +18 -0
- siada/agent_hub/coder/prompt/base/non_interactive.py +14 -0
- siada/agent_hub/coder/prompt/base/prompt_builder.py +33 -0
- siada/agent_hub/coder/prompt/base/rules.py +40 -0
- siada/agent_hub/coder/prompt/base/tool_use.py +14 -0
- siada/agent_hub/coder/prompt/bug_prompt/bug_fix_prompt.py +51 -0
- siada/agent_hub/coder/prompt/bug_prompt/rules.py +53 -0
- siada/agent_hub/coder/prompt/code_gen_prompt.py +45 -0
- siada/agent_hub/coder/prompt/fe_gen_prompt.py +45 -0
- siada/agent_hub/coder/prompt/issue_review_prompt.py +121 -0
- siada/agent_hub/coder/tracing/__init__.py +22 -0
- siada/agent_hub/coder/tracing/logger_tracing_processor.py +458 -0
- siada/agent_hub/hooks/__init__.py +0 -0
- siada/agent_hub/hooks/io_hooks.py +30 -0
- siada/agent_hub/siada_agent.py +214 -0
- siada/config/__init__.py +0 -0
- siada/config/config_loader.py +67 -0
- siada/entrypoint/__init__.py +3 -0
- siada/entrypoint/args_parser/__init__.py +0 -0
- siada/entrypoint/args_parser/args.py +195 -0
- siada/entrypoint/interaction/__init__.py +0 -0
- siada/entrypoint/interaction/base_turn.py +107 -0
- siada/entrypoint/interaction/config.py +30 -0
- siada/entrypoint/interaction/controller.py +122 -0
- siada/entrypoint/interaction/nointeractive_controller.py +27 -0
- siada/entrypoint/interaction/turn/interface.py +116 -0
- siada/entrypoint/interaction/turn/models.py +34 -0
- siada/entrypoint/interaction/turn/run_turn.py +641 -0
- siada/entrypoint/interaction/turn_factory.py +35 -0
- siada/entrypoint/interaction/turn_models.py +28 -0
- siada/entrypoint/siadahub.py +413 -0
- siada/foundation/__init__.py +0 -0
- siada/foundation/code_agent_context.py +48 -0
- siada/foundation/config.py +59 -0
- siada/foundation/constants.py +2 -0
- siada/foundation/context.py +76 -0
- siada/foundation/logging.py +341 -0
- siada/foundation/tools/__init__.py +0 -0
- siada/foundation/tools/get_git_diff.py +105 -0
- siada/io/__init__.py +0 -0
- siada/io/banner.py +148 -0
- siada/io/color_settings.py +207 -0
- siada/io/color_utils.py +16 -0
- siada/io/components/__init__.py +0 -0
- siada/io/components/mdstream.py +204 -0
- siada/io/console_printer.py +68 -0
- siada/io/io.py +661 -0
- siada/io/key_bindings.py +129 -0
- siada/io/notification_command.py +40 -0
- siada/io/test_banner.py +64 -0
- siada/models/__init__.py +0 -0
- siada/models/agent.py +47 -0
- siada/models/coder/__init__.py +0 -0
- siada/models/converter.py +27 -0
- siada/models/model_base_config.py +121 -0
- siada/models/model_run_config.py +153 -0
- siada/provider/__init__.py +0 -0
- siada/provider/client_factory.py +112 -0
- siada/provider/llm_client.py +11 -0
- siada/provider/openrouter/__init__.py +0 -0
- siada/provider/openrouter/coverter.py +17 -0
- siada/provider/openrouter/openroute_provider.py +33 -0
- siada/provider/provider_factory.py +53 -0
- siada/queries/tree-sitter-language-pack/README.md +7 -0
- siada/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
- siada/queries/tree-sitter-language-pack/c-tags.scm +9 -0
- siada/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
- siada/queries/tree-sitter-language-pack/commonlisp-tags.scm +122 -0
- siada/queries/tree-sitter-language-pack/cpp-tags.scm +15 -0
- siada/queries/tree-sitter-language-pack/csharp-tags.scm +26 -0
- siada/queries/tree-sitter-language-pack/d-tags.scm +26 -0
- siada/queries/tree-sitter-language-pack/dart-tags.scm +92 -0
- siada/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
- siada/queries/tree-sitter-language-pack/elixir-tags.scm +54 -0
- siada/queries/tree-sitter-language-pack/elm-tags.scm +19 -0
- siada/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
- siada/queries/tree-sitter-language-pack/go-tags.scm +42 -0
- siada/queries/tree-sitter-language-pack/java-tags.scm +20 -0
- siada/queries/tree-sitter-language-pack/javascript-tags.scm +88 -0
- siada/queries/tree-sitter-language-pack/lua-tags.scm +34 -0
- siada/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
- siada/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +98 -0
- siada/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
- siada/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
- siada/queries/tree-sitter-language-pack/python-tags.scm +14 -0
- siada/queries/tree-sitter-language-pack/r-tags.scm +21 -0
- siada/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
- siada/queries/tree-sitter-language-pack/ruby-tags.scm +64 -0
- siada/queries/tree-sitter-language-pack/rust-tags.scm +60 -0
- siada/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
- siada/queries/tree-sitter-language-pack/swift-tags.scm +51 -0
- siada/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
- siada/queries/tree-sitter-languages/README.md +23 -0
- siada/queries/tree-sitter-languages/c-tags.scm +9 -0
- siada/queries/tree-sitter-languages/c_sharp-tags.scm +46 -0
- siada/queries/tree-sitter-languages/cpp-tags.scm +15 -0
- siada/queries/tree-sitter-languages/dart-tags.scm +91 -0
- siada/queries/tree-sitter-languages/elisp-tags.scm +8 -0
- siada/queries/tree-sitter-languages/elixir-tags.scm +54 -0
- siada/queries/tree-sitter-languages/elm-tags.scm +19 -0
- siada/queries/tree-sitter-languages/go-tags.scm +30 -0
- siada/queries/tree-sitter-languages/hcl-tags.scm +77 -0
- siada/queries/tree-sitter-languages/java-tags.scm +20 -0
- siada/queries/tree-sitter-languages/javascript-tags.scm +88 -0
- siada/queries/tree-sitter-languages/kotlin-tags.scm +27 -0
- siada/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
- siada/queries/tree-sitter-languages/ocaml_interface-tags.scm +98 -0
- siada/queries/tree-sitter-languages/php-tags.scm +26 -0
- siada/queries/tree-sitter-languages/python-tags.scm +12 -0
- siada/queries/tree-sitter-languages/ql-tags.scm +26 -0
- siada/queries/tree-sitter-languages/ruby-tags.scm +64 -0
- siada/queries/tree-sitter-languages/rust-tags.scm +60 -0
- siada/queries/tree-sitter-languages/scala-tags.scm +65 -0
- siada/queries/tree-sitter-languages/typescript-tags.scm +41 -0
- siada/services/__init__.py +30 -0
- siada/services/at_file_recommendation_design_document.md +1213 -0
- siada/services/code_context_manager.py +136 -0
- siada/services/enhanced_fix_result_check.py +546 -0
- siada/services/execution_trace_collector.py +207 -0
- siada/services/file_recommendation/__init__.py +136 -0
- siada/services/file_recommendation/core/__init__.py +17 -0
- siada/services/file_recommendation/core/completion_engine.py +351 -0
- siada/services/file_recommendation/core/config.py +62 -0
- siada/services/file_recommendation/core/file_discovery.py +353 -0
- siada/services/file_recommendation/core/suggestion.py +110 -0
- siada/services/file_recommendation/utils/__init__.py +22 -0
- siada/services/file_recommendation/utils/path_utils.py +138 -0
- siada/services/file_recommendation/utils/text_utils.py +253 -0
- siada/services/fix_result_check.py +377 -0
- siada/services/handle_at_command/README.md +247 -0
- siada/services/handle_at_command/__init__.py +115 -0
- siada/services/handle_at_command/exceptions.py +46 -0
- siada/services/handle_at_command/models.py +92 -0
- siada/services/handle_at_command/parser.py +189 -0
- siada/services/handle_at_command/processor.py +321 -0
- siada/services/handle_at_command/resolver.py +318 -0
- siada/services/handle_at_command/utils.py +345 -0
- siada/services/model_info_service.py +103 -0
- siada/services/siada_runner.py +170 -0
- siada/session/__init__.py +29 -0
- siada/session/session_manager.py +66 -0
- siada/session/session_models.py +43 -0
- siada/support/__init__.py +0 -0
- siada/support/completer.py +204 -0
- siada/support/editor.py +146 -0
- siada/support/envprocessor.py +54 -0
- siada/support/repo.py +51 -0
- siada/support/slash_commands.py +410 -0
- siada/support/spinner.py +320 -0
- siada/tools/__init__.py +0 -0
- siada/tools/ast/README.md +345 -0
- siada/tools/ast/ast_tool.py +341 -0
- siada/tools/ast/models.py +11 -0
- siada/tools/coder/__init__.py +0 -0
- siada/tools/coder/ask_followup_question.py +70 -0
- siada/tools/coder/cmd_runner.py +132 -0
- siada/tools/coder/file_operator.py +284 -0
- siada/tools/coder/file_search/README.md +223 -0
- siada/tools/coder/file_search/__init__.py +10 -0
- siada/tools/coder/file_search/bin/rg-linux-arm64 +0 -0
- siada/tools/coder/file_search/bin/rg-linux-x64 +0 -0
- siada/tools/coder/file_search/bin/rg-macos-arm64 +0 -0
- siada/tools/coder/file_search/bin/rg-macos-x64 +0 -0
- siada/tools/coder/file_search/bin/rg.exe +0 -0
- siada/tools/coder/file_search/search.py +437 -0
- siada/tools/coder/file_search/setup_package.py +150 -0
- siada/tools/coder/file_search/test_search.py +142 -0
- siada/tools/coder/files.py +147 -0
- siada/tools/coder/fix_attempt_completion.py +58 -0
- siada/tools/coder/issue_review_completion.py +29 -0
- siada/tools/coder/observation/__init__.py +0 -0
- siada/tools/coder/observation/error.py +22 -0
- siada/tools/coder/observation/file_observation.py +216 -0
- siada/tools/coder/observation/observation.py +71 -0
- siada/tools/coder/repo_map/__init__.py +157 -0
- siada/tools/coder/repo_map/dump.py +29 -0
- siada/tools/coder/repo_map/io.py +226 -0
- siada/tools/coder/repo_map/repo_map.py +881 -0
- siada/tools/coder/repo_map/special.py +203 -0
- siada/tools/coder/repo_map/token_counter.py +154 -0
- siada/tools/coder/repo_map/waiting.py +221 -0
- siada/tools/coder/reproduce_completion.py +39 -0
- siada/tools/coder/run_cmd.py +60 -0
- siada/tools/coder/test_completion.py +25 -0
- siada/tools/coder/tool_docs.py +44 -0
- siada/tools/compression_tool.py +200 -0
- siada/tools/read_many_files/README.md +220 -0
- siada/tools/read_many_files/__init__.py +25 -0
- siada/tools/read_many_files/file_processor.py +486 -0
- siada/tools/read_many_files/filters.py +326 -0
- siada/tools/read_many_files/formatters.py +204 -0
- siada/tools/read_many_files/models.py +189 -0
- siada/tools/read_many_files_tool.py +233 -0
- siada/tools/tool_call_format/__init__.py +55 -0
- siada/tools/tool_call_format/file_to_language.py +231 -0
- siada/tools/tool_call_format/formatter_factory.py +80 -0
- siada/tools/tool_call_format/formatters.py +282 -0
- siada/tools/tool_call_format/tool_call_formatter.py +54 -0
- siada/tools/web/__init__.py +0 -0
- siada/tools/web/web_crawl.py +208 -0
- siada/utils.py +194 -0
- siada_cli-0.0.1.dist-info/LICENSE +201 -0
- siada_cli-0.0.1.dist-info/METADATA +457 -0
- siada_cli-0.0.1.dist-info/RECORD +215 -0
- siada_cli-0.0.1.dist-info/WHEEL +4 -0
- siada_cli-0.0.1.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Repo Map模块 - 代码仓库地图生成工具
|
|
3
|
+
|
|
4
|
+
该模块提供了生成代码仓库地图的完整功能,包括:
|
|
5
|
+
- 代码文件分析和标签提取
|
|
6
|
+
- 基于PageRank算法的文件重要性排序
|
|
7
|
+
- 智能的代码结构展示
|
|
8
|
+
- 高性能的token计算
|
|
9
|
+
- 灵活的IO处理
|
|
10
|
+
|
|
11
|
+
主要组件:
|
|
12
|
+
- RepoMap: 核心仓库地图生成器
|
|
13
|
+
- IO: 标准输入输出处理器
|
|
14
|
+
- TokenCounterModel: token计算模型
|
|
15
|
+
- Tag: 代码标签数据结构
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from .repo_map import RepoMap, Tag
|
|
19
|
+
from .io import IO, SilentIO, FileIO
|
|
20
|
+
from .token_counter import TokenCounterModel, OptimizedTokenCounterModel
|
|
21
|
+
from .dump import dump
|
|
22
|
+
from .special import filter_important_files, is_important
|
|
23
|
+
from .waiting import Spinner, WaitingSpinner
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
# 核心类
|
|
27
|
+
'RepoMap',
|
|
28
|
+
'Tag',
|
|
29
|
+
|
|
30
|
+
# IO类
|
|
31
|
+
'IO',
|
|
32
|
+
'SilentIO',
|
|
33
|
+
'FileIO',
|
|
34
|
+
|
|
35
|
+
# Token计算类
|
|
36
|
+
'TokenCounterModel',
|
|
37
|
+
'OptimizedTokenCounterModel',
|
|
38
|
+
|
|
39
|
+
# 工具函数
|
|
40
|
+
'dump',
|
|
41
|
+
'filter_important_files',
|
|
42
|
+
'is_important',
|
|
43
|
+
|
|
44
|
+
# 等待/进度指示器
|
|
45
|
+
'Spinner',
|
|
46
|
+
'WaitingSpinner',
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
# 版本信息
|
|
50
|
+
__version__ = '1.0.0'
|
|
51
|
+
|
|
52
|
+
# 模块级别的便捷函数
|
|
53
|
+
def create_repo_map(
|
|
54
|
+
root_path: str,
|
|
55
|
+
model_name: str = "claude-3-5-sonnet-20241022",
|
|
56
|
+
verbose: bool = False,
|
|
57
|
+
map_tokens: int = 1024,
|
|
58
|
+
**kwargs
|
|
59
|
+
) -> RepoMap:
|
|
60
|
+
"""
|
|
61
|
+
创建RepoMap实例的便捷函数
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
root_path (str): 仓库根目录路径
|
|
65
|
+
model_name (str): 语言模型名称,默认为Claude 3.5 Sonnet
|
|
66
|
+
verbose (bool): 是否启用详细输出
|
|
67
|
+
map_tokens (int): 地图最大token数量
|
|
68
|
+
**kwargs: 其他RepoMap参数
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
RepoMap: 配置好的RepoMap实例
|
|
72
|
+
|
|
73
|
+
Example:
|
|
74
|
+
>>> repo_map = create_repo_map("/path/to/repo", verbose=True)
|
|
75
|
+
>>> result = repo_map.get_repo_map(chat_files=[], other_files=python_files)
|
|
76
|
+
"""
|
|
77
|
+
io = IO(verbose=verbose)
|
|
78
|
+
model = TokenCounterModel(model_name)
|
|
79
|
+
|
|
80
|
+
return RepoMap(
|
|
81
|
+
root=root_path,
|
|
82
|
+
main_model=model,
|
|
83
|
+
io=io,
|
|
84
|
+
verbose=verbose,
|
|
85
|
+
map_tokens=map_tokens,
|
|
86
|
+
**kwargs
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def create_optimized_repo_map(
|
|
91
|
+
root_path: str,
|
|
92
|
+
model_name: str = "claude-3-5-sonnet-20241022",
|
|
93
|
+
verbose: bool = False,
|
|
94
|
+
map_tokens: int = 8192,
|
|
95
|
+
sampling_threshold: int = 10000,
|
|
96
|
+
**kwargs
|
|
97
|
+
) -> RepoMap:
|
|
98
|
+
"""
|
|
99
|
+
创建优化版RepoMap实例的便捷函数
|
|
100
|
+
|
|
101
|
+
适用于大型代码仓库,使用优化的token计算器
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
root_path (str): 仓库根目录路径
|
|
105
|
+
model_name (str): 语言模型名称
|
|
106
|
+
verbose (bool): 是否启用详细输出
|
|
107
|
+
map_tokens (int): 地图最大token数量,默认8192
|
|
108
|
+
sampling_threshold (int): 采样阈值
|
|
109
|
+
**kwargs: 其他RepoMap参数
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
RepoMap: 配置好的优化版RepoMap实例
|
|
113
|
+
"""
|
|
114
|
+
io = IO(verbose=verbose)
|
|
115
|
+
model = OptimizedTokenCounterModel(model_name, sampling_threshold)
|
|
116
|
+
|
|
117
|
+
return RepoMap(
|
|
118
|
+
root=root_path,
|
|
119
|
+
main_model=model,
|
|
120
|
+
io=io,
|
|
121
|
+
verbose=verbose,
|
|
122
|
+
map_tokens=map_tokens,
|
|
123
|
+
**kwargs
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def create_silent_repo_map(
|
|
128
|
+
root_path: str,
|
|
129
|
+
model_name: str = "claude-3-5-sonnet-20241022",
|
|
130
|
+
map_tokens: int = 1024,
|
|
131
|
+
**kwargs
|
|
132
|
+
) -> RepoMap:
|
|
133
|
+
"""
|
|
134
|
+
创建静默版RepoMap实例的便捷函数
|
|
135
|
+
|
|
136
|
+
适用于测试或需要静默运行的场景
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
root_path (str): 仓库根目录路径
|
|
140
|
+
model_name (str): 语言模型名称
|
|
141
|
+
map_tokens (int): 地图最大token数量
|
|
142
|
+
**kwargs: 其他RepoMap参数
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
RepoMap: 配置好的静默版RepoMap实例
|
|
146
|
+
"""
|
|
147
|
+
io = SilentIO()
|
|
148
|
+
model = TokenCounterModel(model_name)
|
|
149
|
+
|
|
150
|
+
return RepoMap(
|
|
151
|
+
root=root_path,
|
|
152
|
+
main_model=model,
|
|
153
|
+
io=io,
|
|
154
|
+
verbose=False,
|
|
155
|
+
map_tokens=map_tokens,
|
|
156
|
+
**kwargs
|
|
157
|
+
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import traceback
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def cvt(s):
|
|
6
|
+
if isinstance(s, str):
|
|
7
|
+
return s
|
|
8
|
+
try:
|
|
9
|
+
return json.dumps(s, indent=4)
|
|
10
|
+
except TypeError:
|
|
11
|
+
return str(s)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def dump(*vals):
|
|
15
|
+
# http://docs.python.org/library/traceback.html
|
|
16
|
+
stack = traceback.extract_stack()
|
|
17
|
+
vars = stack[-2][3]
|
|
18
|
+
|
|
19
|
+
# strip away the call to dump()
|
|
20
|
+
vars = "(".join(vars.split("(")[1:])
|
|
21
|
+
vars = ")".join(vars.split(")")[:-1])
|
|
22
|
+
|
|
23
|
+
vals = [cvt(v) for v in vals]
|
|
24
|
+
has_newline = sum(1 for v in vals if "\n" in v)
|
|
25
|
+
if has_newline:
|
|
26
|
+
print("%s:" % vars)
|
|
27
|
+
print(", ".join(vals))
|
|
28
|
+
else:
|
|
29
|
+
print("%s:" % vars, ", ".join(vals))
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"""
|
|
2
|
+
IO类 - 提供标准的输入输出接口
|
|
3
|
+
|
|
4
|
+
该模块提供了RepoMap使用的标准IO接口,包括:
|
|
5
|
+
- 不同级别的日志输出(info, warning, error)
|
|
6
|
+
- 文件读取功能
|
|
7
|
+
- 可配置的输出选项
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
from typing import Optional, TextIO
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class IO:
|
|
17
|
+
"""
|
|
18
|
+
标准IO类,提供文件读取和日志输出功能
|
|
19
|
+
|
|
20
|
+
该类为RepoMap提供统一的IO接口,支持:
|
|
21
|
+
- 多级别日志输出
|
|
22
|
+
- 文件内容读取
|
|
23
|
+
- 可配置的输出目标
|
|
24
|
+
- 详细模式控制
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
verbose: bool = False,
|
|
30
|
+
output_stream: Optional[TextIO] = None,
|
|
31
|
+
error_stream: Optional[TextIO] = None
|
|
32
|
+
):
|
|
33
|
+
"""
|
|
34
|
+
初始化IO实例
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
verbose (bool): 是否启用详细输出模式
|
|
38
|
+
output_stream (TextIO, optional): 标准输出流,默认为sys.stdout
|
|
39
|
+
error_stream (TextIO, optional): 错误输出流,默认为sys.stderr
|
|
40
|
+
"""
|
|
41
|
+
self.verbose = verbose
|
|
42
|
+
self.output_stream = output_stream or sys.stdout
|
|
43
|
+
self.error_stream = error_stream or sys.stderr
|
|
44
|
+
|
|
45
|
+
# 统计信息
|
|
46
|
+
self.outputs = []
|
|
47
|
+
self.warnings = []
|
|
48
|
+
self.errors = []
|
|
49
|
+
|
|
50
|
+
def tool_output(self, message: str) -> None:
|
|
51
|
+
"""
|
|
52
|
+
输出信息消息
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
message (str): 要输出的消息
|
|
56
|
+
"""
|
|
57
|
+
self.outputs.append(message)
|
|
58
|
+
if self.verbose:
|
|
59
|
+
print(f"[INFO] {message}", file=self.output_stream)
|
|
60
|
+
|
|
61
|
+
def tool_warning(self, message: str) -> None:
|
|
62
|
+
"""
|
|
63
|
+
输出警告消息
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
message (str): 要输出的警告消息
|
|
67
|
+
"""
|
|
68
|
+
self.warnings.append(message)
|
|
69
|
+
print(f"[WARNING] {message}", file=self.error_stream)
|
|
70
|
+
|
|
71
|
+
def tool_error(self, message: str) -> None:
|
|
72
|
+
"""
|
|
73
|
+
输出错误消息
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
message (str): 要输出的错误消息
|
|
77
|
+
"""
|
|
78
|
+
self.errors.append(message)
|
|
79
|
+
print(f"[ERROR] {message}", file=self.error_stream)
|
|
80
|
+
|
|
81
|
+
def read_text(self, filepath: str) -> str:
|
|
82
|
+
"""
|
|
83
|
+
读取文件内容
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
filepath (str): 文件路径
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
str: 文件内容,如果读取失败则返回空字符串
|
|
90
|
+
"""
|
|
91
|
+
try:
|
|
92
|
+
# 确保路径是绝对路径或相对于当前工作目录的路径
|
|
93
|
+
path = Path(filepath)
|
|
94
|
+
|
|
95
|
+
# 尝试不同的编码方式
|
|
96
|
+
encodings = ['utf-8', 'utf-8-sig', 'gbk', 'gb2312', 'latin1']
|
|
97
|
+
|
|
98
|
+
for encoding in encodings:
|
|
99
|
+
try:
|
|
100
|
+
with open(path, 'r', encoding=encoding) as f:
|
|
101
|
+
content = f.read()
|
|
102
|
+
return content
|
|
103
|
+
except UnicodeDecodeError:
|
|
104
|
+
continue
|
|
105
|
+
|
|
106
|
+
# 如果所有编码都失败,尝试二进制模式读取并忽略错误
|
|
107
|
+
with open(path, 'r', encoding='utf-8', errors='ignore') as f:
|
|
108
|
+
content = f.read()
|
|
109
|
+
|
|
110
|
+
if self.verbose:
|
|
111
|
+
self.tool_warning(f"文件 {filepath} 使用fallback编码读取")
|
|
112
|
+
|
|
113
|
+
return content
|
|
114
|
+
|
|
115
|
+
except FileNotFoundError:
|
|
116
|
+
self.tool_error(f"文件不存在: {filepath}")
|
|
117
|
+
return ""
|
|
118
|
+
except PermissionError:
|
|
119
|
+
self.tool_error(f"没有权限读取文件: {filepath}")
|
|
120
|
+
return ""
|
|
121
|
+
except IsADirectoryError:
|
|
122
|
+
self.tool_error(f"路径是目录而不是文件: {filepath}")
|
|
123
|
+
return ""
|
|
124
|
+
except Exception as e:
|
|
125
|
+
self.tool_error(f"读取文件失败 {filepath}: {str(e)}")
|
|
126
|
+
return ""
|
|
127
|
+
|
|
128
|
+
def clear_stats(self) -> None:
|
|
129
|
+
"""清除统计信息"""
|
|
130
|
+
self.outputs.clear()
|
|
131
|
+
self.warnings.clear()
|
|
132
|
+
self.errors.clear()
|
|
133
|
+
|
|
134
|
+
def get_stats(self) -> dict:
|
|
135
|
+
"""
|
|
136
|
+
获取统计信息
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
dict: 包含输出、警告、错误数量的字典
|
|
140
|
+
"""
|
|
141
|
+
return {
|
|
142
|
+
'outputs': len(self.outputs),
|
|
143
|
+
'warnings': len(self.warnings),
|
|
144
|
+
'errors': len(self.errors)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
def set_verbose(self, verbose: bool) -> None:
|
|
148
|
+
"""
|
|
149
|
+
设置详细模式
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
verbose (bool): 是否启用详细输出
|
|
153
|
+
"""
|
|
154
|
+
self.verbose = verbose
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class SilentIO(IO):
|
|
158
|
+
"""
|
|
159
|
+
静默IO类,不输出任何信息到控制台
|
|
160
|
+
|
|
161
|
+
适用于测试或需要静默运行的场景
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
def __init__(self):
|
|
165
|
+
"""初始化静默IO实例"""
|
|
166
|
+
super().__init__(verbose=False)
|
|
167
|
+
|
|
168
|
+
def tool_output(self, message: str) -> None:
|
|
169
|
+
"""静默记录输出消息"""
|
|
170
|
+
self.outputs.append(message)
|
|
171
|
+
|
|
172
|
+
def tool_warning(self, message: str) -> None:
|
|
173
|
+
"""静默记录警告消息"""
|
|
174
|
+
self.warnings.append(message)
|
|
175
|
+
|
|
176
|
+
def tool_error(self, message: str) -> None:
|
|
177
|
+
"""静默记录错误消息"""
|
|
178
|
+
self.errors.append(message)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class FileIO(IO):
|
|
182
|
+
"""
|
|
183
|
+
文件IO类,将输出重定向到文件
|
|
184
|
+
|
|
185
|
+
适用于需要将日志保存到文件的场景
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
def __init__(
|
|
189
|
+
self,
|
|
190
|
+
log_file: str,
|
|
191
|
+
verbose: bool = True,
|
|
192
|
+
append: bool = True
|
|
193
|
+
):
|
|
194
|
+
"""
|
|
195
|
+
初始化文件IO实例
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
log_file (str): 日志文件路径
|
|
199
|
+
verbose (bool): 是否启用详细输出
|
|
200
|
+
append (bool): 是否追加到现有文件
|
|
201
|
+
"""
|
|
202
|
+
self.log_file = Path(log_file)
|
|
203
|
+
self.append = append
|
|
204
|
+
|
|
205
|
+
# 确保日志目录存在
|
|
206
|
+
self.log_file.parent.mkdir(parents=True, exist_ok=True)
|
|
207
|
+
|
|
208
|
+
# 打开日志文件
|
|
209
|
+
mode = 'a' if append else 'w'
|
|
210
|
+
self.log_stream = open(self.log_file, mode, encoding='utf-8')
|
|
211
|
+
|
|
212
|
+
super().__init__(
|
|
213
|
+
verbose=verbose,
|
|
214
|
+
output_stream=self.log_stream,
|
|
215
|
+
error_stream=self.log_stream
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
def __del__(self):
|
|
219
|
+
"""析构函数,确保文件被正确关闭"""
|
|
220
|
+
if hasattr(self, 'log_stream') and not self.log_stream.closed:
|
|
221
|
+
self.log_stream.close()
|
|
222
|
+
|
|
223
|
+
def close(self):
|
|
224
|
+
"""手动关闭日志文件"""
|
|
225
|
+
if hasattr(self, 'log_stream') and not self.log_stream.closed:
|
|
226
|
+
self.log_stream.close()
|