adam-community 0.0.4__tar.gz
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.
- adam_community-0.0.4/PKG-INFO +53 -0
- adam_community-0.0.4/README.md +27 -0
- adam_community-0.0.4/adam_community/__init__.py +4 -0
- adam_community-0.0.4/adam_community/cli/__init__.py +0 -0
- adam_community-0.0.4/adam_community/cli/build.py +269 -0
- adam_community-0.0.4/adam_community/cli/cli.py +48 -0
- adam_community-0.0.4/adam_community/cli/init.py +154 -0
- adam_community-0.0.4/adam_community/cli/parser.py +326 -0
- adam_community-0.0.4/adam_community/cli/templates/__init__.py +1 -0
- adam_community-0.0.4/adam_community/tool.py +143 -0
- adam_community-0.0.4/adam_community/util.py +345 -0
- adam_community-0.0.4/adam_community.egg-info/PKG-INFO +53 -0
- adam_community-0.0.4/adam_community.egg-info/SOURCES.txt +18 -0
- adam_community-0.0.4/adam_community.egg-info/dependency_links.txt +1 -0
- adam_community-0.0.4/adam_community.egg-info/entry_points.txt +2 -0
- adam_community-0.0.4/adam_community.egg-info/requires.txt +4 -0
- adam_community-0.0.4/adam_community.egg-info/top_level.txt +1 -0
- adam_community-0.0.4/setup.cfg +4 -0
- adam_community-0.0.4/setup.py +31 -0
- adam_community-0.0.4/test/test_util_tool.py +143 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: adam_community
|
|
3
|
+
Version: 0.0.4
|
|
4
|
+
Summary: Adam Community Tools and Utilities
|
|
5
|
+
Home-page: https://github.com/yourusername/adam-community
|
|
6
|
+
Author: Adam Community
|
|
7
|
+
Author-email: admin@sidereus-ai.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: requests>=2.31.0
|
|
14
|
+
Requires-Dist: click>=8.0.0
|
|
15
|
+
Requires-Dist: docstring-parser>=0.15
|
|
16
|
+
Requires-Dist: rich>=13.0.0
|
|
17
|
+
Dynamic: author
|
|
18
|
+
Dynamic: author-email
|
|
19
|
+
Dynamic: classifier
|
|
20
|
+
Dynamic: description
|
|
21
|
+
Dynamic: description-content-type
|
|
22
|
+
Dynamic: home-page
|
|
23
|
+
Dynamic: requires-dist
|
|
24
|
+
Dynamic: requires-python
|
|
25
|
+
Dynamic: summary
|
|
26
|
+
|
|
27
|
+
# Adam Community
|
|
28
|
+
|
|
29
|
+
Adam Community 是一个 Python 工具包,提供了一系列实用工具和功能,用于与 Adam 社区相关的开发工作。
|
|
30
|
+
|
|
31
|
+
安装依赖:
|
|
32
|
+
```bash
|
|
33
|
+
make install
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
## 使用方法
|
|
38
|
+
|
|
39
|
+
安装完成后,您可以在 Python 代码中导入并使用该包:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from adam_community import tool
|
|
43
|
+
from adam_community import util
|
|
44
|
+
|
|
45
|
+
# 使用相关功能
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 开发
|
|
49
|
+
|
|
50
|
+
运行测试:
|
|
51
|
+
```bash
|
|
52
|
+
make test
|
|
53
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Adam Community
|
|
2
|
+
|
|
3
|
+
Adam Community 是一个 Python 工具包,提供了一系列实用工具和功能,用于与 Adam 社区相关的开发工作。
|
|
4
|
+
|
|
5
|
+
安装依赖:
|
|
6
|
+
```bash
|
|
7
|
+
make install
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## 使用方法
|
|
12
|
+
|
|
13
|
+
安装完成后,您可以在 Python 代码中导入并使用该包:
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from adam_community import tool
|
|
17
|
+
from adam_community import util
|
|
18
|
+
|
|
19
|
+
# 使用相关功能
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 开发
|
|
23
|
+
|
|
24
|
+
运行测试:
|
|
25
|
+
```bash
|
|
26
|
+
make test
|
|
27
|
+
```
|
|
File without changes
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import zipfile
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Tuple, List
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.panel import Panel
|
|
7
|
+
from rich.tree import Tree
|
|
8
|
+
from .parser import parse_directory
|
|
9
|
+
|
|
10
|
+
console = Console()
|
|
11
|
+
|
|
12
|
+
def check_python_files(directory: Path) -> Tuple[bool, List[str]]:
|
|
13
|
+
"""检查所有 Python 文件是否都有参数定义"""
|
|
14
|
+
tree = Tree("📦 Python 文件检查")
|
|
15
|
+
errors = []
|
|
16
|
+
warnings = []
|
|
17
|
+
functions = parse_directory(directory)
|
|
18
|
+
|
|
19
|
+
tree.add(f"找到 {len(functions)} 个类定义")
|
|
20
|
+
for func in functions:
|
|
21
|
+
func_info = func["function"]
|
|
22
|
+
|
|
23
|
+
# 检查描述长度
|
|
24
|
+
description = func_info.get("description")
|
|
25
|
+
if description is None or not description:
|
|
26
|
+
warning_msg = f"⚠️ {func_info['file']}: {func_info['name']} 没有描述"
|
|
27
|
+
tree.add(warning_msg)
|
|
28
|
+
warnings.append(warning_msg)
|
|
29
|
+
elif len(description) > 1024:
|
|
30
|
+
error_msg = f"❌ {func_info['file']}: {func_info['name']} 描述长度超过1024字符 ({len(description)})"
|
|
31
|
+
tree.add(error_msg)
|
|
32
|
+
errors.append(error_msg)
|
|
33
|
+
else:
|
|
34
|
+
tree.add(f"✓ {func_info['file']}: {func_info['name']} 描述长度: {len(description)}")
|
|
35
|
+
|
|
36
|
+
# 检查参数定义
|
|
37
|
+
if not func_info["parameters"]["properties"]:
|
|
38
|
+
warning_msg = f"⚠️ {func_info['file']}: {func_info['name']} 没有参数定义"
|
|
39
|
+
tree.add(warning_msg)
|
|
40
|
+
warnings.append(warning_msg)
|
|
41
|
+
else:
|
|
42
|
+
# 检查参数类型是否都是有效的JSON Schema类型
|
|
43
|
+
param_errors = []
|
|
44
|
+
for param_name, param_info in func_info["parameters"]["properties"].items():
|
|
45
|
+
if "type" not in param_info:
|
|
46
|
+
param_errors.append(f"参数 '{param_name}' 缺少类型定义")
|
|
47
|
+
elif param_info["type"] not in ["string", "integer", "number", "boolean", "array", "object", "null"]:
|
|
48
|
+
param_errors.append(f"参数 '{param_name}' 类型 '{param_info['type']}' 不是有效的JSON Schema类型")
|
|
49
|
+
|
|
50
|
+
if param_errors:
|
|
51
|
+
for param_error in param_errors:
|
|
52
|
+
error_msg = f"❌ {func_info['file']}: {func_info['name']} - {param_error}"
|
|
53
|
+
tree.add(error_msg)
|
|
54
|
+
errors.append(error_msg)
|
|
55
|
+
else:
|
|
56
|
+
tree.add(f"✓ {func_info['file']}: {func_info['name']} 参数类型验证通过")
|
|
57
|
+
|
|
58
|
+
if errors:
|
|
59
|
+
tree.add("❌ 检查未通过")
|
|
60
|
+
else:
|
|
61
|
+
tree.add("✅ 检查通过")
|
|
62
|
+
|
|
63
|
+
# 显示警告信息
|
|
64
|
+
if warnings:
|
|
65
|
+
tree.add(f"⚠️ 发现 {len(warnings)} 个警告(不影响构建)")
|
|
66
|
+
|
|
67
|
+
console.print(tree)
|
|
68
|
+
return len(errors) == 0, errors
|
|
69
|
+
|
|
70
|
+
def check_configuration(directory: Path) -> Tuple[bool, List[str]]:
|
|
71
|
+
"""检查 configure.json 文件"""
|
|
72
|
+
tree = Tree("📄 配置文件检查")
|
|
73
|
+
errors = []
|
|
74
|
+
config_path = directory / "config" / "configure.json"
|
|
75
|
+
|
|
76
|
+
if not config_path.exists():
|
|
77
|
+
tree.add("❌ 未找到 configure.json 文件")
|
|
78
|
+
console.print(tree)
|
|
79
|
+
return False, ["configure.json 文件不存在"]
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
with open(config_path, 'r', encoding='utf-8') as f:
|
|
83
|
+
config = json.load(f)
|
|
84
|
+
|
|
85
|
+
required_fields = ["name", "version", "display_name"]
|
|
86
|
+
for field in required_fields:
|
|
87
|
+
if field not in config:
|
|
88
|
+
errors.append(f"configure.json 缺少必要字段: {field}")
|
|
89
|
+
tree.add(f"❌ 缺少字段: {field}")
|
|
90
|
+
else:
|
|
91
|
+
tree.add(f"✓ {field}: {config[field]}")
|
|
92
|
+
|
|
93
|
+
if errors:
|
|
94
|
+
tree.add("❌ 检查未通过")
|
|
95
|
+
else:
|
|
96
|
+
tree.add("✅ 检查通过")
|
|
97
|
+
|
|
98
|
+
console.print(tree)
|
|
99
|
+
return len(errors) == 0, errors
|
|
100
|
+
except json.JSONDecodeError:
|
|
101
|
+
tree.add("❌ 配置文件格式错误")
|
|
102
|
+
console.print(tree)
|
|
103
|
+
return False, ["configure.json 文件格式错误"]
|
|
104
|
+
|
|
105
|
+
def check_markdown_files(directory: Path) -> Tuple[bool, List[str]]:
|
|
106
|
+
"""检查必要的 Markdown 文件"""
|
|
107
|
+
tree = Tree("📑 Markdown 文件检查")
|
|
108
|
+
errors = []
|
|
109
|
+
|
|
110
|
+
# 先读取配置文件中的 type 字段
|
|
111
|
+
config_path = directory / "config" / "configure.json"
|
|
112
|
+
config_type = "agent" # 默认值
|
|
113
|
+
|
|
114
|
+
if config_path.exists():
|
|
115
|
+
try:
|
|
116
|
+
with open(config_path, 'r', encoding='utf-8') as f:
|
|
117
|
+
config = json.load(f)
|
|
118
|
+
config_type = config.get("type", "agent")
|
|
119
|
+
except json.JSONDecodeError:
|
|
120
|
+
pass # 配置文件格式错误时使用默认值
|
|
121
|
+
|
|
122
|
+
# 根据 type 设置不同的 required_files
|
|
123
|
+
if config_type == "kit":
|
|
124
|
+
required_files = [
|
|
125
|
+
"configure.json",
|
|
126
|
+
"long_description.md",
|
|
127
|
+
"input.json"
|
|
128
|
+
]
|
|
129
|
+
else: # type=agent 或空值
|
|
130
|
+
required_files = [
|
|
131
|
+
"initial_assistant_message.md",
|
|
132
|
+
"initial_system_prompt.md",
|
|
133
|
+
"long_description.md"
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
tree.add(f"检查类型: {config_type}")
|
|
137
|
+
|
|
138
|
+
for file in required_files:
|
|
139
|
+
file_path = directory / "config" / file
|
|
140
|
+
if not file_path.exists():
|
|
141
|
+
errors.append(f"缺少必要文件: {file}")
|
|
142
|
+
tree.add(f"❌ {file}")
|
|
143
|
+
else:
|
|
144
|
+
# 对于 input.json,额外检查 JSON 格式
|
|
145
|
+
if file == "input.json":
|
|
146
|
+
try:
|
|
147
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
148
|
+
json.load(f)
|
|
149
|
+
tree.add(f"✓ {file} (JSON 格式正确)")
|
|
150
|
+
except json.JSONDecodeError:
|
|
151
|
+
errors.append(f"{file} JSON 格式错误")
|
|
152
|
+
tree.add(f"❌ {file} (JSON 格式错误)")
|
|
153
|
+
else:
|
|
154
|
+
tree.add(f"✓ {file}")
|
|
155
|
+
|
|
156
|
+
if errors:
|
|
157
|
+
tree.add("❌ 检查未通过")
|
|
158
|
+
else:
|
|
159
|
+
tree.add("✅ 检查通过")
|
|
160
|
+
|
|
161
|
+
console.print(tree)
|
|
162
|
+
return len(errors) == 0, errors
|
|
163
|
+
|
|
164
|
+
def create_zip_package(directory: Path) -> str:
|
|
165
|
+
"""创建 zip 包"""
|
|
166
|
+
tree = Tree("📦 创建压缩包")
|
|
167
|
+
with open(directory / "config" / "configure.json", 'r', encoding='utf-8') as f:
|
|
168
|
+
config = json.load(f)
|
|
169
|
+
|
|
170
|
+
zip_name = f"{config['name']}_{config['version']}.zip"
|
|
171
|
+
zip_path = directory / zip_name
|
|
172
|
+
|
|
173
|
+
tree.add(f"包名: {zip_name}")
|
|
174
|
+
|
|
175
|
+
# 获取配置类型
|
|
176
|
+
config_type = config.get("type", "agent")
|
|
177
|
+
|
|
178
|
+
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
|
179
|
+
# 添加所有 Python 文件
|
|
180
|
+
py_files = list(directory.rglob('*.py'))
|
|
181
|
+
py_tree = tree.add("Python 文件")
|
|
182
|
+
for py_file in py_files:
|
|
183
|
+
if not py_file.name.startswith('_'):
|
|
184
|
+
zipf.write(py_file, py_file.relative_to(directory))
|
|
185
|
+
py_tree.add(f"+ {py_file.relative_to(directory)}")
|
|
186
|
+
|
|
187
|
+
# 添加配置文件
|
|
188
|
+
config_tree = tree.add("配置文件")
|
|
189
|
+
zipf.write(directory / "config" / "configure.json", "config/configure.json")
|
|
190
|
+
config_tree.add("+ config/configure.json")
|
|
191
|
+
|
|
192
|
+
# 添加 demos 目录
|
|
193
|
+
demos_tree = tree.add("Demos 文件")
|
|
194
|
+
demos_dir = directory / "demos"
|
|
195
|
+
if demos_dir.exists() and demos_dir.is_dir():
|
|
196
|
+
for demos_file in demos_dir.rglob('*'):
|
|
197
|
+
if demos_file.is_file():
|
|
198
|
+
zipf.write(demos_file, demos_file.relative_to(directory))
|
|
199
|
+
demos_tree.add(f"+ {demos_file.relative_to(directory)}")
|
|
200
|
+
|
|
201
|
+
# 根据类型添加不同的文件
|
|
202
|
+
md_tree = tree.add("其他文件")
|
|
203
|
+
if config_type == "kit":
|
|
204
|
+
# kit 类型需要添加的文件
|
|
205
|
+
other_files = ["long_description.md", "input.json"]
|
|
206
|
+
else:
|
|
207
|
+
# agent 类型需要添加的文件
|
|
208
|
+
other_files = ["initial_assistant_message.md", "initial_system_prompt.md", "long_description.md"]
|
|
209
|
+
|
|
210
|
+
for file in other_files:
|
|
211
|
+
file_path = directory / "config" / file
|
|
212
|
+
if file_path.exists():
|
|
213
|
+
zipf.write(file_path, f"config/{file}")
|
|
214
|
+
md_tree.add(f"+ config/{file}")
|
|
215
|
+
|
|
216
|
+
tree.add("✅ 压缩包创建完成")
|
|
217
|
+
console.print(tree)
|
|
218
|
+
return zip_name
|
|
219
|
+
|
|
220
|
+
def build_package(directory: Path) -> Tuple[bool, List[str], str]:
|
|
221
|
+
"""构建项目包
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Tuple[bool, List[str], str]: (是否成功, 错误信息列表, zip包名称)
|
|
225
|
+
"""
|
|
226
|
+
console.print(Panel.fit(
|
|
227
|
+
"[bold blue]🚀 开始构建项目包[/bold blue]",
|
|
228
|
+
border_style="blue"
|
|
229
|
+
))
|
|
230
|
+
|
|
231
|
+
all_passed = True
|
|
232
|
+
all_errors = []
|
|
233
|
+
|
|
234
|
+
# 1. 检查 Python 文件
|
|
235
|
+
py_passed, py_errors = check_python_files(directory)
|
|
236
|
+
if not py_passed:
|
|
237
|
+
all_passed = False
|
|
238
|
+
all_errors.extend(py_errors)
|
|
239
|
+
|
|
240
|
+
# 2. 检查配置文件
|
|
241
|
+
config_passed, config_errors = check_configuration(directory)
|
|
242
|
+
if not config_passed:
|
|
243
|
+
all_passed = False
|
|
244
|
+
all_errors.extend(config_errors)
|
|
245
|
+
|
|
246
|
+
# 3. 检查 Markdown 文件
|
|
247
|
+
md_passed, md_errors = check_markdown_files(directory)
|
|
248
|
+
if not md_passed:
|
|
249
|
+
all_passed = False
|
|
250
|
+
all_errors.extend(md_errors)
|
|
251
|
+
|
|
252
|
+
# 如果所有检查都通过,创建 zip 包
|
|
253
|
+
zip_name = ""
|
|
254
|
+
if all_passed:
|
|
255
|
+
zip_name = create_zip_package(directory)
|
|
256
|
+
|
|
257
|
+
if all_passed:
|
|
258
|
+
console.print(Panel.fit(
|
|
259
|
+
f"[bold green]✅ 构建成功![/bold green]\n"
|
|
260
|
+
f"压缩包: {zip_name}",
|
|
261
|
+
border_style="green"
|
|
262
|
+
))
|
|
263
|
+
else:
|
|
264
|
+
console.print(Panel.fit(
|
|
265
|
+
"[bold red]❌ 构建失败![/bold red]\n" + "\n".join(all_errors),
|
|
266
|
+
border_style="red"
|
|
267
|
+
))
|
|
268
|
+
|
|
269
|
+
return all_passed, all_errors, zip_name
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import json
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from .parser import parse_directory
|
|
5
|
+
from .build import build_package
|
|
6
|
+
from .init import init
|
|
7
|
+
|
|
8
|
+
@click.group()
|
|
9
|
+
def cli():
|
|
10
|
+
"""Adam Community CLI 工具"""
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
@cli.command()
|
|
14
|
+
@click.argument('directory', type=click.Path(exists=True, file_okay=False, dir_okay=True), default='.')
|
|
15
|
+
def parse(directory):
|
|
16
|
+
"""解析指定目录下的所有 Python 文件并生成 functions.json"""
|
|
17
|
+
directory_path = Path(directory)
|
|
18
|
+
all_functions = parse_directory(directory_path)
|
|
19
|
+
|
|
20
|
+
# 将结果写入 functions.json
|
|
21
|
+
output_file = directory_path / 'functions.json'
|
|
22
|
+
with open(output_file, 'w', encoding='utf-8') as f:
|
|
23
|
+
json.dump(all_functions, f, indent=2, ensure_ascii=False)
|
|
24
|
+
|
|
25
|
+
click.echo(f"已成功解析 {len(all_functions)} 个类,结果保存在 {output_file}")
|
|
26
|
+
|
|
27
|
+
@cli.command()
|
|
28
|
+
@click.argument('directory', type=click.Path(exists=True, file_okay=False, dir_okay=True), default='.')
|
|
29
|
+
def build(directory):
|
|
30
|
+
"""构建项目包"""
|
|
31
|
+
directory_path = Path(directory)
|
|
32
|
+
|
|
33
|
+
# 执行构建
|
|
34
|
+
success, errors, zip_name = build_package(directory_path)
|
|
35
|
+
|
|
36
|
+
if success:
|
|
37
|
+
click.echo(f"包创建成功: {zip_name}")
|
|
38
|
+
else:
|
|
39
|
+
click.echo("检查未通过,发现以下问题:")
|
|
40
|
+
for error in errors:
|
|
41
|
+
click.echo(f"- {error}")
|
|
42
|
+
raise click.Abort()
|
|
43
|
+
|
|
44
|
+
# 添加 init 命令
|
|
45
|
+
cli.add_command(init)
|
|
46
|
+
|
|
47
|
+
if __name__ == '__main__':
|
|
48
|
+
cli()
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import re
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from jinja2 import Environment, FileSystemLoader
|
|
5
|
+
from typing import Dict, Any
|
|
6
|
+
|
|
7
|
+
@click.command()
|
|
8
|
+
@click.option('--name', prompt='项目名称(仅支持字母、数字、连字符,不能有空格)', help='项目的英文名称(用于文件夹和配置)')
|
|
9
|
+
@click.option('--display-name', prompt='显示名称', help='项目的中文显示名称')
|
|
10
|
+
@click.option('--description', prompt='项目描述', help='项目的简短描述')
|
|
11
|
+
@click.option('--version', default='1.0.0', prompt='版本号', help='项目版本号')
|
|
12
|
+
@click.option('--author', prompt='作者', help='项目作者')
|
|
13
|
+
@click.option('--type',
|
|
14
|
+
type=click.Choice(['kit', 'toolbox']),
|
|
15
|
+
prompt='项目类型',
|
|
16
|
+
help='选择项目类型: kit(表单工具) 或 toolbox(智能体工具)')
|
|
17
|
+
@click.argument('directory', type=click.Path(exists=True, file_okay=False, dir_okay=True), default='.')
|
|
18
|
+
def init(name: str, display_name: str, description: str, version: str, author: str, type: str, directory: str):
|
|
19
|
+
"""初始化一个新的 Adam 工具项目"""
|
|
20
|
+
|
|
21
|
+
# 验证项目名称格式
|
|
22
|
+
if not validate_project_name(name):
|
|
23
|
+
click.echo(f"错误: 项目名称 '{name}' 格式不正确")
|
|
24
|
+
click.echo("项目名称只能包含字母、数字和连字符(-),不能包含空格或其他特殊字符")
|
|
25
|
+
click.echo("示例: my-tool, data-processor, image-analyzer")
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
directory_path = Path(directory)
|
|
29
|
+
project_path = directory_path / name
|
|
30
|
+
|
|
31
|
+
# 检查目录是否已存在
|
|
32
|
+
if project_path.exists():
|
|
33
|
+
click.echo(f"错误: 目录 '{name}' 已存在")
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
# 创建项目目录
|
|
37
|
+
project_path.mkdir(parents=True, exist_ok=True)
|
|
38
|
+
click.echo(f"创建项目目录: {project_path}")
|
|
39
|
+
|
|
40
|
+
# 创建config目录
|
|
41
|
+
config_path = project_path / "config"
|
|
42
|
+
config_path.mkdir(parents=True, exist_ok=True)
|
|
43
|
+
|
|
44
|
+
# 设置Jinja2环境
|
|
45
|
+
template_dir = Path(__file__).parent / "templates"
|
|
46
|
+
env = Environment(loader=FileSystemLoader(template_dir))
|
|
47
|
+
|
|
48
|
+
# 模板变量
|
|
49
|
+
template_vars = {
|
|
50
|
+
'name': name,
|
|
51
|
+
'display_name': display_name,
|
|
52
|
+
'description': description,
|
|
53
|
+
'version': version,
|
|
54
|
+
'author': author,
|
|
55
|
+
'project_type': type
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# 生成配置文件
|
|
59
|
+
render_and_save(env, 'configure.json.j2', config_path / "configure.json", template_vars)
|
|
60
|
+
click.echo(f"生成配置文件: config/configure.json")
|
|
61
|
+
|
|
62
|
+
# 生成描述文件
|
|
63
|
+
render_and_save(env, 'long_description.md.j2', config_path / "long_description.md", template_vars)
|
|
64
|
+
click.echo(f"生成描述文件: config/long_description.md")
|
|
65
|
+
|
|
66
|
+
if type == 'kit':
|
|
67
|
+
generate_kit_files(env, project_path, config_path, template_vars)
|
|
68
|
+
else:
|
|
69
|
+
generate_toolbox_files(env, project_path, config_path, template_vars)
|
|
70
|
+
|
|
71
|
+
# 生成 Makefile
|
|
72
|
+
render_and_save(env, 'Makefile.j2', project_path / "Makefile", template_vars)
|
|
73
|
+
click.echo(f"生成构建脚本: Makefile")
|
|
74
|
+
|
|
75
|
+
# 生成 README 文件
|
|
76
|
+
if type == 'kit':
|
|
77
|
+
render_and_save(env, 'README_kit.md.j2', project_path / "README.md", template_vars)
|
|
78
|
+
else:
|
|
79
|
+
render_and_save(env, 'README_toolbox.md.j2', project_path / "README.md", template_vars)
|
|
80
|
+
click.echo(f"生成项目文档: README.md")
|
|
81
|
+
|
|
82
|
+
click.echo(f"\n✅ 项目 '{name}' 初始化完成!")
|
|
83
|
+
click.echo(f"📁 项目路径: {project_path}")
|
|
84
|
+
click.echo("\n📋 后续步骤:")
|
|
85
|
+
click.echo("1. 📖 阅读 README.md 了解详细的开发指南")
|
|
86
|
+
click.echo("2. 🔧 根据需要修改 Python 代码实现")
|
|
87
|
+
if type == 'kit':
|
|
88
|
+
click.echo("3. 📝 自定义 config/input.json 表单配置")
|
|
89
|
+
else:
|
|
90
|
+
click.echo("3. 🤖 自定义 config/initial_system_prompt.md 和 config/initial_assistant_message.md")
|
|
91
|
+
click.echo("4. 📄 完善 config/long_description.md 描述文档")
|
|
92
|
+
click.echo("5. ⚙️ 运行 'make parse' 生成 functions.json")
|
|
93
|
+
click.echo("6. 📦 运行 'make build' 打包项目")
|
|
94
|
+
click.echo(f"\n💡 详细的开发指南请查看: {project_path}/README.md")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def render_and_save(env: Environment, template_name: str, output_path: Path, template_vars: Dict[str, Any]):
|
|
98
|
+
"""渲染模板并保存到文件"""
|
|
99
|
+
template = env.get_template(template_name)
|
|
100
|
+
content = template.render(**template_vars)
|
|
101
|
+
with open(output_path, 'w', encoding='utf-8') as f:
|
|
102
|
+
f.write(content)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def generate_kit_files(env: Environment, project_path: Path, config_path: Path, template_vars: Dict[str, Any]):
|
|
106
|
+
"""生成 kit 项目的特定文件"""
|
|
107
|
+
|
|
108
|
+
# 生成 input.json 表单配置
|
|
109
|
+
render_and_save(env, 'input.json.j2', config_path / "input.json", template_vars)
|
|
110
|
+
click.echo(f"生成表单配置: config/input.json")
|
|
111
|
+
|
|
112
|
+
# 生成主要的Python实现文件
|
|
113
|
+
python_filename = f"{template_vars['name'].replace('-', '_')}.py"
|
|
114
|
+
render_and_save(env, 'kit_python.py.j2', project_path / python_filename, template_vars)
|
|
115
|
+
click.echo(f"生成主要实现文件: {python_filename}")
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def generate_toolbox_files(env: Environment, project_path: Path, config_path: Path, template_vars: Dict[str, Any]):
|
|
119
|
+
"""生成 toolbox 项目的特定文件"""
|
|
120
|
+
|
|
121
|
+
# 生成 initial_system_prompt.md
|
|
122
|
+
render_and_save(env, 'initial_system_prompt.md.j2', config_path / "initial_system_prompt.md", template_vars)
|
|
123
|
+
click.echo(f"生成系统提示文件: config/initial_system_prompt.md")
|
|
124
|
+
|
|
125
|
+
# 生成 initial_assistant_message.md
|
|
126
|
+
render_and_save(env, 'initial_assistant_message.md.j2', config_path / "initial_assistant_message.md", template_vars)
|
|
127
|
+
click.echo(f"生成助手消息文件: config/initial_assistant_message.md")
|
|
128
|
+
|
|
129
|
+
# 生成主要的Python实现文件
|
|
130
|
+
python_filename = f"{template_vars['name'].replace('-', '_')}.py"
|
|
131
|
+
render_and_save(env, 'toolbox_python.py.j2', project_path / python_filename, template_vars)
|
|
132
|
+
click.echo(f"生成主要实现文件: {python_filename}")
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def validate_project_name(name: str) -> bool:
|
|
136
|
+
"""验证项目名称格式"""
|
|
137
|
+
# 项目名称只能包含字母、数字和连字符,不能有空格
|
|
138
|
+
pattern = r'^[a-zA-Z0-9-]+$'
|
|
139
|
+
if not re.match(pattern, name):
|
|
140
|
+
return False
|
|
141
|
+
|
|
142
|
+
# 不能以连字符开始或结束
|
|
143
|
+
if name.startswith('-') or name.endswith('-'):
|
|
144
|
+
return False
|
|
145
|
+
|
|
146
|
+
# 不能有连续的连字符
|
|
147
|
+
if '--' in name:
|
|
148
|
+
return False
|
|
149
|
+
|
|
150
|
+
# 长度限制
|
|
151
|
+
if len(name) < 1 or len(name) > 50:
|
|
152
|
+
return False
|
|
153
|
+
|
|
154
|
+
return True
|