full-stack-coding-assistant-agent 0.1.0__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.
- agents/__init__.py +0 -0
- agents/audit_agent.py +223 -0
- agents/backend_agent.py +179 -0
- agents/base_agent.py +406 -0
- agents/frontend_agent.py +148 -0
- agents/test_agent.py +155 -0
- coordinator/__init__.py +0 -0
- coordinator/coordinator.py +452 -0
- coordinator/dag.py +147 -0
- executor/__init__.py +0 -0
- executor/cb_integration.py +160 -0
- full_stack_coding_assistant_agent/__init__.py +6 -0
- full_stack_coding_assistant_agent/cli.py +10 -0
- full_stack_coding_assistant_agent/main.py +686 -0
- full_stack_coding_assistant_agent-0.1.0.dist-info/METADATA +849 -0
- full_stack_coding_assistant_agent-0.1.0.dist-info/RECORD +31 -0
- full_stack_coding_assistant_agent-0.1.0.dist-info/WHEEL +5 -0
- full_stack_coding_assistant_agent-0.1.0.dist-info/entry_points.txt +2 -0
- full_stack_coding_assistant_agent-0.1.0.dist-info/top_level.txt +7 -0
- model/__init__.py +0 -0
- model/config.py +62 -0
- model/model_router.py +150 -0
- storage/__init__.py +0 -0
- storage/context_db.py +274 -0
- utils/__init__.py +0 -0
- utils/agent_selector.py +243 -0
- utils/config_validator.py +143 -0
- utils/logger.py +95 -0
- utils/output_manager.py +1572 -0
- utils/pdf_reader.py +122 -0
- utils/version.py +188 -0
utils/pdf_reader.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PDF 文档读取器 - 从 PDF 文件中提取文本内容作为项目描述
|
|
3
|
+
|
|
4
|
+
支持 PyPDF2 纯 Python 解析,零系统依赖。
|
|
5
|
+
设计为可扩展,后续可增加 docx/txt 等格式。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Tuple
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PDFReader:
|
|
14
|
+
"""PDF 文件文本提取器"""
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def extract_text(file_path: str) -> str:
|
|
18
|
+
"""
|
|
19
|
+
从 PDF 文件中提取全部文本
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
file_path: PDF 文件路径
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
提取的文本内容
|
|
26
|
+
|
|
27
|
+
Raises:
|
|
28
|
+
FileNotFoundError: 文件不存在
|
|
29
|
+
ValueError: 文件不是 PDF 或无法读取
|
|
30
|
+
"""
|
|
31
|
+
path = Path(file_path)
|
|
32
|
+
if not path.exists():
|
|
33
|
+
raise FileNotFoundError(f"文件不存在: {file_path}")
|
|
34
|
+
|
|
35
|
+
if path.suffix.lower() != ".pdf":
|
|
36
|
+
raise ValueError(f"文件不是 PDF 格式: {file_path}")
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
from PyPDF2 import PdfReader
|
|
40
|
+
except ImportError:
|
|
41
|
+
raise ImportError("需要安装 PyPDF2: pip install PyPDF2")
|
|
42
|
+
|
|
43
|
+
reader = PdfReader(str(path))
|
|
44
|
+
pages = []
|
|
45
|
+
for page in reader.pages:
|
|
46
|
+
text = page.extract_text()
|
|
47
|
+
if text:
|
|
48
|
+
pages.append(text)
|
|
49
|
+
|
|
50
|
+
if not pages:
|
|
51
|
+
raise ValueError(f"PDF 文件中没有可提取的文本: {file_path}")
|
|
52
|
+
|
|
53
|
+
full_text = "\n\n".join(pages)
|
|
54
|
+
return PDFReader._clean_text(full_text)
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def extract_summary(
|
|
58
|
+
file_path: str,
|
|
59
|
+
max_chars: int = 100,
|
|
60
|
+
) -> Tuple[str, str]:
|
|
61
|
+
"""
|
|
62
|
+
从 PDF 提取文本,同时返回摘要和完整内容
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
file_path: PDF 文件路径
|
|
66
|
+
max_chars: 摘要最大字符数
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
(summary, full_text) 元组
|
|
70
|
+
"""
|
|
71
|
+
full_text = PDFReader.extract_text(file_path)
|
|
72
|
+
# 取第一段非空内容作为摘要
|
|
73
|
+
first_line = full_text.strip().split("\n")[0].strip() if full_text else ""
|
|
74
|
+
if len(first_line) > max_chars:
|
|
75
|
+
summary = first_line[:max_chars] + "..."
|
|
76
|
+
else:
|
|
77
|
+
summary = first_line[:max_chars]
|
|
78
|
+
return summary, full_text
|
|
79
|
+
|
|
80
|
+
@staticmethod
|
|
81
|
+
def _clean_text(text: str) -> str:
|
|
82
|
+
"""
|
|
83
|
+
清洗 PDF 提取的文本:
|
|
84
|
+
- 合并多余空白行
|
|
85
|
+
- 修复断行(PDF 常见的换行符问题)
|
|
86
|
+
- 移除页码等噪音
|
|
87
|
+
"""
|
|
88
|
+
# 移除多余空白行(3个以上连续空行合并为2个)
|
|
89
|
+
text = re.sub(r"\n{3,}", "\n\n", text)
|
|
90
|
+
# 移除行尾多余空格
|
|
91
|
+
text = re.sub(r"[ \t]+$", "", text, flags=re.MULTILINE)
|
|
92
|
+
# 移除页码模式(如 "Page 1 of 10" 或 "- 1 -")
|
|
93
|
+
text = re.sub(
|
|
94
|
+
r"\n\s*(?:Page\s+\d+\s+of\s+\d+|[-\s]*\d+[-\s]*)\s*\n", "\n", text
|
|
95
|
+
)
|
|
96
|
+
return text.strip()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def read_document(file_path: str) -> Tuple[str, str]:
|
|
100
|
+
"""
|
|
101
|
+
通用文档读取入口,根据扩展名自动选择解析器
|
|
102
|
+
|
|
103
|
+
当前支持: .pdf
|
|
104
|
+
后续扩展: .docx, .txt, .md
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
file_path: 文档路径
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
(summary, full_text) 元组
|
|
111
|
+
"""
|
|
112
|
+
path = Path(file_path)
|
|
113
|
+
suffix = path.suffix.lower()
|
|
114
|
+
|
|
115
|
+
if suffix == ".pdf":
|
|
116
|
+
return PDFReader.extract_summary(file_path)
|
|
117
|
+
elif suffix in (".txt", ".md"):
|
|
118
|
+
content = path.read_text(encoding="utf-8")
|
|
119
|
+
summary = content[:100].replace("\n", " ").strip()
|
|
120
|
+
return summary, content
|
|
121
|
+
else:
|
|
122
|
+
raise ValueError(f"不支持的文件格式: {suffix},当前支持 .pdf / .txt / .md")
|
utils/version.py
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""
|
|
2
|
+
版本管理工具模块
|
|
3
|
+
|
|
4
|
+
提供版本号读取、校验和升级接口,版本号统一从项目根目录的 VERSION 文件读取。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Literal, Optional
|
|
10
|
+
|
|
11
|
+
# 项目根目录(向上查找 VERSION 文件)
|
|
12
|
+
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
|
13
|
+
VERSION_FILE = PROJECT_ROOT / "VERSION"
|
|
14
|
+
|
|
15
|
+
# 语义化版本正则表达式(支持预发布标签和构建元数据)
|
|
16
|
+
SEMVER_PATTERN = re.compile(
|
|
17
|
+
r"^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)"
|
|
18
|
+
r"(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?"
|
|
19
|
+
r"(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_version() -> str:
|
|
24
|
+
"""
|
|
25
|
+
从 VERSION 文件读取当前版本号
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
版本号字符串(如 "0.1.0")
|
|
29
|
+
|
|
30
|
+
Raises:
|
|
31
|
+
FileNotFoundError: VERSION 文件不存在
|
|
32
|
+
ValueError: VERSION 文件格式错误
|
|
33
|
+
"""
|
|
34
|
+
if not VERSION_FILE.exists():
|
|
35
|
+
raise FileNotFoundError(f"VERSION 文件不存在: {VERSION_FILE}")
|
|
36
|
+
|
|
37
|
+
version = VERSION_FILE.read_text(encoding="utf-8").strip()
|
|
38
|
+
|
|
39
|
+
if not version:
|
|
40
|
+
raise ValueError("VERSION 文件为空")
|
|
41
|
+
|
|
42
|
+
# 校验版本号格式
|
|
43
|
+
if not validate_version(version):
|
|
44
|
+
raise ValueError(f"VERSION 文件中的版本号格式错误: {version}")
|
|
45
|
+
|
|
46
|
+
return version
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def validate_version(version: str) -> bool:
|
|
50
|
+
"""
|
|
51
|
+
校验版本号是否符合语义化版本规范(SemVer 2.0.0)
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
version: 版本号字符串
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
True 如果格式正确,否则 False
|
|
58
|
+
"""
|
|
59
|
+
return SEMVER_PATTERN.match(version) is not None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def bump_version(
|
|
63
|
+
part: Literal["major", "minor", "patch"] = "patch",
|
|
64
|
+
prerelease: Optional[str] = None,
|
|
65
|
+
dry_run: bool = False,
|
|
66
|
+
) -> str:
|
|
67
|
+
"""
|
|
68
|
+
升级版本号并写回 VERSION 文件
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
part: 升级部分(major/minor/patch)
|
|
72
|
+
prerelease: 预发布标签(如 "alpha.1"、"beta.1")
|
|
73
|
+
dry_run: 如果为 True,只计算新版本号但不写入文件
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
新版本号字符串
|
|
77
|
+
|
|
78
|
+
Examples:
|
|
79
|
+
>>> bump_version("patch") # 0.1.0 -> 0.1.1
|
|
80
|
+
>>> bump_version("minor") # 0.1.0 -> 0.2.0
|
|
81
|
+
>>> bump_version("major") # 0.1.0 -> 1.0.0
|
|
82
|
+
>>> bump_version("patch", prerelease="alpha.1") # 0.1.0 -> 0.1.1-alpha.1
|
|
83
|
+
"""
|
|
84
|
+
current = get_version()
|
|
85
|
+
match = SEMVER_PATTERN.match(current)
|
|
86
|
+
|
|
87
|
+
if match is None:
|
|
88
|
+
raise ValueError(f"当前版本号格式错误: {current}")
|
|
89
|
+
|
|
90
|
+
major = int(match.group("major"))
|
|
91
|
+
minor = int(match.group("minor"))
|
|
92
|
+
patch = int(match.group("patch"))
|
|
93
|
+
|
|
94
|
+
# 升级对应部分
|
|
95
|
+
if part == "major":
|
|
96
|
+
major += 1
|
|
97
|
+
minor = 0
|
|
98
|
+
patch = 0
|
|
99
|
+
elif part == "minor":
|
|
100
|
+
minor += 1
|
|
101
|
+
patch = 0
|
|
102
|
+
elif part == "patch":
|
|
103
|
+
patch += 1
|
|
104
|
+
else:
|
|
105
|
+
raise ValueError(f"无效的升级部分: {part},支持 major/minor/patch")
|
|
106
|
+
|
|
107
|
+
# 构建新版本号
|
|
108
|
+
new_version = f"{major}.{minor}.{patch}"
|
|
109
|
+
if prerelease:
|
|
110
|
+
new_version += f"-{prerelease}"
|
|
111
|
+
|
|
112
|
+
if not dry_run:
|
|
113
|
+
write_version(new_version)
|
|
114
|
+
|
|
115
|
+
return new_version
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def write_version(version: str) -> None:
|
|
119
|
+
"""
|
|
120
|
+
将版本号写入 VERSION 文件
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
version: 版本号字符串
|
|
124
|
+
|
|
125
|
+
Raises:
|
|
126
|
+
ValueError: 版本号格式错误
|
|
127
|
+
"""
|
|
128
|
+
if not validate_version(version):
|
|
129
|
+
raise ValueError(f"版本号格式错误: {version}")
|
|
130
|
+
|
|
131
|
+
VERSION_FILE.write_text(f"{version}\n", encoding="utf-8")
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def get_version_info() -> dict:
|
|
135
|
+
"""
|
|
136
|
+
获取版本号详细信息
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
包含版本号各部分的字典,如:
|
|
140
|
+
{
|
|
141
|
+
"version": "0.1.0",
|
|
142
|
+
"major": 0,
|
|
143
|
+
"minor": 1,
|
|
144
|
+
"patch": 0,
|
|
145
|
+
"prerelease": None,
|
|
146
|
+
"buildmetadata": None,
|
|
147
|
+
}
|
|
148
|
+
"""
|
|
149
|
+
version = get_version()
|
|
150
|
+
match = SEMVER_PATTERN.match(version)
|
|
151
|
+
|
|
152
|
+
if match is None:
|
|
153
|
+
raise ValueError(f"版本号格式错误: {version}")
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
"version": version,
|
|
157
|
+
"major": int(match.group("major")),
|
|
158
|
+
"minor": int(match.group("minor")),
|
|
159
|
+
"patch": int(match.group("patch")),
|
|
160
|
+
"prerelease": match.group("prerelease"),
|
|
161
|
+
"buildmetadata": match.group("buildmetadata"),
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
if __name__ == "__main__":
|
|
166
|
+
# 命令行使用示例
|
|
167
|
+
import argparse
|
|
168
|
+
|
|
169
|
+
parser = argparse.ArgumentParser(description="版本管理工具")
|
|
170
|
+
parser.add_argument("--get", action="store_true", help="获取当前版本号")
|
|
171
|
+
parser.add_argument("--validate", type=str, help="校验版本号格式")
|
|
172
|
+
parser.add_argument("--bump", type=str, choices=["major", "minor", "patch"], help="升级版本号")
|
|
173
|
+
parser.add_argument("--prerelease", type=str, help="预发布标签")
|
|
174
|
+
parser.add_argument("--dry-run", action="store_true", help="只计算不写入")
|
|
175
|
+
args = parser.parse_args()
|
|
176
|
+
|
|
177
|
+
if args.get:
|
|
178
|
+
print(get_version())
|
|
179
|
+
elif args.validate is not None:
|
|
180
|
+
if validate_version(args.validate):
|
|
181
|
+
print(f"✅ 版本号格式正确: {args.validate}")
|
|
182
|
+
else:
|
|
183
|
+
print(f"❌ 版本号格式错误: {args.validate}")
|
|
184
|
+
elif args.bump:
|
|
185
|
+
new_version = bump_version(args.bump, prerelease=args.prerelease, dry_run=args.dry_run)
|
|
186
|
+
print(f"新版本号: {new_version}")
|
|
187
|
+
else:
|
|
188
|
+
print(get_version_info())
|