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.
@@ -0,0 +1,31 @@
1
+ agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ agents/audit_agent.py,sha256=4kcUpK-fSoCdKHKbjmSi0D-9DS6auFLReGmBizWD--4,7059
3
+ agents/backend_agent.py,sha256=lQak0WjlIuIpzK0nFPbWKgCsEBj-q8ApMCkRXmx2DBA,5472
4
+ agents/base_agent.py,sha256=HCmTz1whO8-IYpyWYRuN3HQTZmc7Xw7a0NiEojv_ovQ,12454
5
+ agents/frontend_agent.py,sha256=hafysLpHS1WOnPp4Mxe_d4MKbyZu9yu1a64yCdHefV4,4524
6
+ agents/test_agent.py,sha256=0nviTiSEJQUf-MrnGJ6YqABDvELTo6IYeouS1KhyHBM,4380
7
+ coordinator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ coordinator/coordinator.py,sha256=m52zM5D5v9zeJ0OVUNF1Gs8zTvcXQm-ZbbzsDyFB08o,16385
9
+ coordinator/dag.py,sha256=hSy9_uCyL3_bxN9cZTmhno4tOLLHTB_fGlZNFXo--EU,4818
10
+ executor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ executor/cb_integration.py,sha256=MZdEnCWXdB9pvDa_r8-WEsCqeRd8hp3HRyN0gkXlKns,4224
12
+ full_stack_coding_assistant_agent/__init__.py,sha256=EoE-_WS5X4mGr52rjGsO3FTIEC7Ui9cVAc2g777LmNY,114
13
+ full_stack_coding_assistant_agent/cli.py,sha256=BRoGBOQVrTF0_Q4kJF1guFNVbkrI7_ENxoutZIgL-bs,142
14
+ full_stack_coding_assistant_agent/main.py,sha256=Bhj_b-EYVPEkDuK3EUT1n_tx5rmrOcdoKHpj80pBfA8,21772
15
+ model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ model/config.py,sha256=QpXv3X3IyumOi2kzm_7bUi8r73991pUi_ABV6XB0HlE,1814
17
+ model/model_router.py,sha256=inyUbcjIItyY138zSus3ak9rtgyPvGzS1wuE6sPzZBU,5059
18
+ storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ storage/context_db.py,sha256=4nJ4tyjUeSidFB547056ajnJdCqq1acJKEtyHmraQHc,8818
20
+ utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ utils/agent_selector.py,sha256=lKdcQM28jMZX-PM4jnAhaRf5tztH2tHRdDtAB2PCG-c,8447
22
+ utils/config_validator.py,sha256=7_ht_CFyMo5uWxe9VURyUvEBodgkkHreK6KXth3CDZE,4110
23
+ utils/logger.py,sha256=weInUPgk8cVYqOGo1LY31wDWSI7yjRfbWHoqA4bJHcE,2390
24
+ utils/output_manager.py,sha256=C-OhNPkuxhUsxwteRc2NVGlndDazOsHTalw249r8gEU,56878
25
+ utils/pdf_reader.py,sha256=Zusotq6Rcb7mCEQfpLREsUQeygb0wFxtnQyTT8j2zYk,3575
26
+ utils/version.py,sha256=Qgv710glSRpnC3c4hDgkGL7MGU5pVmYbUhtLDIXmdrA,5423
27
+ full_stack_coding_assistant_agent-0.1.0.dist-info/METADATA,sha256=WsOEKwvuSbhsmzxEw3O_Uujs6GFFQIyPbGMRS2JQPc0,26780
28
+ full_stack_coding_assistant_agent-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
29
+ full_stack_coding_assistant_agent-0.1.0.dist-info/entry_points.txt,sha256=aQQuGx1UwVXvNCMee2CfKndddXpOWFLp5KQsA7puXAQ,69
30
+ full_stack_coding_assistant_agent-0.1.0.dist-info/top_level.txt,sha256=sKrNm57RWmXu0BlnT_LN0PVRdTwmbokTSgdpeNY4goQ,82
31
+ full_stack_coding_assistant_agent-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ fscaa = full_stack_coding_assistant_agent.cli:main
@@ -0,0 +1,7 @@
1
+ agents
2
+ coordinator
3
+ executor
4
+ full_stack_coding_assistant_agent
5
+ model
6
+ storage
7
+ utils
model/__init__.py ADDED
File without changes
model/config.py ADDED
@@ -0,0 +1,62 @@
1
+ """
2
+ 模型配置文件
3
+ 定义各 Agent 使用的模型及 Fallback 策略
4
+ 支持从环境变量读取敏感信息
5
+ """
6
+
7
+ import os
8
+
9
+ from dotenv import load_dotenv
10
+
11
+ # 加载 .env 文件中的环境变量
12
+ load_dotenv(override=True) # 允许 .env 覆盖系统环境变量
13
+
14
+
15
+ def get_env(key: str, default: str = "") -> str:
16
+ """安全获取环境变量"""
17
+ return os.getenv(key, default)
18
+
19
+
20
+ # 从环境变量读取敏感配置
21
+ TENCENT_API_KEY = get_env("TENCENT_API_KEY", "YOUR_TENCENT_API_KEY")
22
+ TENCENT_API_BASE = get_env(
23
+ "TENCENT_API_BASE", "https://api.hunyuan.cloud.tencent.com/v1"
24
+ )
25
+ SQLITE_DB_PATH = get_env("SQLITE_DB_PATH", "./context.db")
26
+ CODEBUDDY_CLI_PATH = get_env("CODEBUDDY_CLI_PATH", "codebuddy")
27
+ CODEBUDDY_TIMEOUT = int(get_env("CODEBUDDY_TIMEOUT", "60"))
28
+
29
+ MODEL_CONFIG = {
30
+ # 默认模型(腾讯混元,OpenAI 兼容 API)
31
+ "default": "openai/hunyuan-lite",
32
+ # 各 Agent 专用模型配置
33
+ "agents": {
34
+ "frontend": "openai/hunyuan-lite", # 前端 Agent 使用轻量模型
35
+ "backend": "openai/hunyuan-standard", # 后端 Agent 使用标准模型
36
+ "test": "openai/hunyuan-lite", # 测试 Agent 使用轻量模型
37
+ "audit": "openai/hunyuan-pro", # 审计 Agent 使用专业模型
38
+ },
39
+ # 腾讯混元 API 配置(从环境变量读取)
40
+ "tencent": {
41
+ "api_base": TENCENT_API_BASE,
42
+ "api_key": TENCENT_API_KEY,
43
+ },
44
+ # Fallback 链式降级策略
45
+ "fallback_chain": ["openai/hunyuan-lite", "openai/hunyuan-standard"],
46
+ # LiteLLM 通用参数
47
+ "litellm_settings": {
48
+ "timeout": 60,
49
+ "max_retries": 2,
50
+ },
51
+ }
52
+
53
+ # 数据库配置
54
+ DATABASE_CONFIG = {
55
+ "path": SQLITE_DB_PATH,
56
+ }
57
+
58
+ # CodeBuddy CLI 配置
59
+ CODEBUDDY_CONFIG = {
60
+ "cli_path": CODEBUDDY_CLI_PATH,
61
+ "timeout": CODEBUDDY_TIMEOUT,
62
+ }
model/model_router.py ADDED
@@ -0,0 +1,150 @@
1
+ """
2
+ 模型路由器 - 基于 LiteLLM 的统一模型接口
3
+ 支持多模型切换、Fallback、流式输出、Token 用量追踪
4
+ """
5
+
6
+ import time
7
+ from typing import Dict, Generator, List, Optional
8
+
9
+ import litellm
10
+
11
+ from model.config import MODEL_CONFIG
12
+
13
+
14
+ def _extract_usage(response) -> Dict:
15
+ """安全提取 LiteLLM response 中的 usage 信息"""
16
+ try:
17
+ usage = response.usage
18
+ if usage is None:
19
+ return {}
20
+ return {
21
+ "prompt_tokens": getattr(usage, "prompt_tokens", 0) or 0,
22
+ "completion_tokens": getattr(usage, "completion_tokens", 0) or 0,
23
+ "total_tokens": getattr(usage, "total_tokens", 0) or 0,
24
+ }
25
+ except Exception:
26
+ return {}
27
+
28
+
29
+ class ModelRouter:
30
+ """LiteLLM 封装,提供统一的模型调用接口"""
31
+
32
+ def __init__(self):
33
+ self.config = MODEL_CONFIG
34
+ self.default_model = self.config["default"]
35
+ self.fallback_chain = self.config["fallback_chain"]
36
+
37
+ # 配置 LiteLLM
38
+ litellm.api_key = self.config["tencent"]["api_key"]
39
+ litellm.api_base = self.config["tencent"]["api_base"]
40
+
41
+ def chat(
42
+ self,
43
+ messages: List[Dict],
44
+ model: Optional[str] = None,
45
+ temperature: float = 0.7,
46
+ max_tokens: int = 4096,
47
+ ) -> Dict:
48
+ """
49
+ 统一聊天接口(非流式)
50
+
51
+ Args:
52
+ messages: OpenAI 格式的消息列表
53
+ model: 指定模型,None 则使用默认模型
54
+ temperature: 温度参数
55
+ max_tokens: 最大 token 数
56
+
57
+ Returns:
58
+ 字典 {"content": str, "usage": dict, "model": str, "latency_ms": float}
59
+ usage 包含 prompt_tokens / completion_tokens / total_tokens
60
+ """
61
+ model = model or self.default_model
62
+
63
+ def _make_empty_result(content: str) -> Dict:
64
+ return {
65
+ "content": content,
66
+ "usage": {},
67
+ "model": model,
68
+ "latency_ms": 0,
69
+ }
70
+
71
+ try:
72
+ t0 = time.time()
73
+ response = litellm.completion(
74
+ model=model,
75
+ messages=messages,
76
+ temperature=temperature,
77
+ max_tokens=max_tokens,
78
+ **self.config["litellm_settings"],
79
+ )
80
+ latency = (time.time() - t0) * 1000
81
+ return {
82
+ "content": response.choices[0].message.content or "",
83
+ "usage": _extract_usage(response),
84
+ "model": model,
85
+ "latency_ms": round(latency, 1),
86
+ }
87
+ except Exception as e:
88
+ import logging
89
+
90
+ logger = logging.getLogger("coding_agent")
91
+ logger.error(f"LLM 调用失败 (model={model}): {e}")
92
+ # Fallback 策略
93
+ for fallback_model in self.fallback_chain:
94
+ if fallback_model != model:
95
+ try:
96
+ logger.warning(f"尝试 Fallback 模型: {fallback_model}")
97
+ t0 = time.time()
98
+ response = litellm.completion(
99
+ model=fallback_model,
100
+ messages=messages,
101
+ temperature=temperature,
102
+ max_tokens=max_tokens,
103
+ )
104
+ latency = (time.time() - t0) * 1000
105
+ return {
106
+ "content": response.choices[0].message.content or "",
107
+ "usage": _extract_usage(response),
108
+ "model": fallback_model,
109
+ "latency_ms": round(latency, 1),
110
+ }
111
+ except Exception as fe:
112
+ logger.warning(f"Fallback 模型 {fallback_model} 也失败: {fe}")
113
+ continue
114
+ raise RuntimeError(
115
+ f"所有模型调用失败 (model={model}, fallback={self.fallback_chain}): {str(e)}"
116
+ )
117
+
118
+ def stream_chat(
119
+ self,
120
+ messages: List[Dict],
121
+ model: Optional[str] = None,
122
+ temperature: float = 0.7,
123
+ max_tokens: int = 4096,
124
+ ) -> Generator[str, None, None]:
125
+ """
126
+ 流式聊天接口
127
+
128
+ Yields:
129
+ 流式输出的文本片段
130
+ """
131
+ model = model or self.default_model
132
+
133
+ try:
134
+ response = litellm.completion(
135
+ model=model,
136
+ messages=messages,
137
+ temperature=temperature,
138
+ max_tokens=max_tokens,
139
+ stream=True,
140
+ )
141
+ for chunk in response:
142
+ content = chunk.choices[0].delta.content
143
+ if content:
144
+ yield content
145
+ except Exception as e:
146
+ yield f"[ERROR] 模型调用失败: {str(e)}"
147
+
148
+ def get_model_for_agent(self, agent_type: str) -> str:
149
+ """获取指定 Agent 类型的模型"""
150
+ return self.config["agents"].get(agent_type, self.default_model)
storage/__init__.py ADDED
File without changes
storage/context_db.py ADDED
@@ -0,0 +1,274 @@
1
+ """
2
+ SQLite 上下文数据库操作封装
3
+ 零部署方案 - 整个数据库是一个文件
4
+ 支持 Python 3.13+ 的最新 SQLite 特性
5
+ """
6
+
7
+ import json
8
+ import sqlite3
9
+ from typing import Dict, List, Optional
10
+
11
+ from model.config import DATABASE_CONFIG
12
+
13
+
14
+ class ContextDB:
15
+ """SQLite 上下文数据库操作类 (Python 3.13+ 优化版)"""
16
+
17
+ def __init__(self, db_path: Optional[str] = None):
18
+ """
19
+ 初始化数据库
20
+
21
+ Args:
22
+ db_path: 数据库路径,None 则使用配置文件中的默认值
23
+ """
24
+ self.db_path = db_path or DATABASE_CONFIG["path"]
25
+ self._init_db()
26
+
27
+ def _get_connection(self):
28
+ """获取数据库连接 (Python 3.13+ 优化)"""
29
+ conn = sqlite3.connect(self.db_path)
30
+ # Python 3.13+ 支持更多 SQLite pragma 设置
31
+ conn.execute("PRAGMA journal_mode=WAL") # 提升并发性能
32
+ conn.execute("PRAGMA foreign_keys=ON") # 启用外键约束
33
+ conn.execute("PRAGMA busy_timeout=5000") # 5秒忙等待
34
+ return conn
35
+
36
+ def _init_db(self):
37
+ """初始化数据库,执行 schema.sql"""
38
+ conn = self._get_connection()
39
+ cursor = conn.cursor()
40
+
41
+ # 创建 tables(如果不存在)
42
+ cursor.executescript(
43
+ """
44
+ CREATE TABLE IF NOT EXISTS tasks (
45
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
46
+ task_id TEXT UNIQUE NOT NULL,
47
+ description TEXT NOT NULL,
48
+ status TEXT NOT NULL DEFAULT 'pending',
49
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
50
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
51
+ result TEXT,
52
+ error TEXT
53
+ );
54
+
55
+ CREATE TABLE IF NOT EXISTS code_changes (
56
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
57
+ task_id TEXT NOT NULL,
58
+ agent_type TEXT NOT NULL,
59
+ file_path TEXT NOT NULL,
60
+ change_type TEXT NOT NULL,
61
+ diff TEXT,
62
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
63
+ FOREIGN KEY (task_id) REFERENCES tasks(task_id)
64
+ );
65
+
66
+ CREATE TABLE IF NOT EXISTS api_contracts (
67
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
68
+ task_id TEXT NOT NULL,
69
+ endpoint TEXT NOT NULL,
70
+ method TEXT NOT NULL,
71
+ request_schema TEXT,
72
+ response_schema TEXT,
73
+ status TEXT DEFAULT 'draft',
74
+ created_by TEXT NOT NULL,
75
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
76
+ FOREIGN KEY (task_id) REFERENCES tasks(task_id)
77
+ );
78
+
79
+ CREATE TABLE IF NOT EXISTS audit_reports (
80
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
81
+ task_id TEXT NOT NULL,
82
+ agent_type TEXT NOT NULL,
83
+ severity TEXT NOT NULL,
84
+ message TEXT NOT NULL,
85
+ file_path TEXT,
86
+ line_number INTEGER,
87
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
88
+ FOREIGN KEY (task_id) REFERENCES tasks(task_id)
89
+ );
90
+
91
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
92
+ CREATE INDEX IF NOT EXISTS idx_code_changes_task_id ON code_changes(task_id);
93
+ CREATE INDEX IF NOT EXISTS idx_api_contracts_task_id ON api_contracts(task_id);
94
+ CREATE INDEX IF NOT EXISTS idx_audit_reports_task_id ON audit_reports(task_id);
95
+ """
96
+ )
97
+
98
+ conn.commit()
99
+ conn.close()
100
+
101
+ # ==================== Task 操作 ====================
102
+
103
+ def create_task(self, task_id: str, description: str) -> int:
104
+ """创建新任务"""
105
+ conn = self._get_connection()
106
+ cursor = conn.cursor()
107
+ cursor.execute(
108
+ "INSERT INTO tasks (task_id, description) VALUES (?, ?)",
109
+ (task_id, description),
110
+ )
111
+ task_pk = cursor.lastrowid
112
+ conn.commit()
113
+ conn.close()
114
+ return task_pk
115
+
116
+ def update_task_status(
117
+ self,
118
+ task_id: str,
119
+ status: str,
120
+ result: Optional[str] = None,
121
+ error: Optional[str] = None,
122
+ ):
123
+ """更新任务状态"""
124
+ conn = self._get_connection()
125
+ cursor = conn.cursor()
126
+ cursor.execute(
127
+ """UPDATE tasks
128
+ SET status = ?, result = ?, error = ?, updated_at = CURRENT_TIMESTAMP
129
+ WHERE task_id = ?""",
130
+ (status, result, error, task_id),
131
+ )
132
+ conn.commit()
133
+ conn.close()
134
+
135
+ def get_task(self, task_id: str) -> Optional[Dict]:
136
+ """获取任务详情"""
137
+ conn = self._get_connection()
138
+ cursor = conn.cursor()
139
+ cursor.execute("SELECT * FROM tasks WHERE task_id = ?", (task_id,))
140
+ row = cursor.fetchone()
141
+ conn.close()
142
+
143
+ if row:
144
+ columns = [
145
+ "id",
146
+ "task_id",
147
+ "description",
148
+ "status",
149
+ "created_at",
150
+ "updated_at",
151
+ "result",
152
+ "error",
153
+ ]
154
+ return dict(zip(columns, row))
155
+ return None
156
+
157
+ # ==================== Code Changes 操作 ====================
158
+
159
+ def log_code_change(
160
+ self,
161
+ task_id: str,
162
+ agent_type: str,
163
+ file_path: str,
164
+ change_type: str,
165
+ diff: Optional[str] = None,
166
+ ):
167
+ """记录代码变更"""
168
+ conn = self._get_connection()
169
+ cursor = conn.cursor()
170
+ cursor.execute(
171
+ """INSERT INTO code_changes
172
+ (task_id, agent_type, file_path, change_type, diff)
173
+ VALUES (?, ?, ?, ?, ?)""",
174
+ (task_id, agent_type, file_path, change_type, diff),
175
+ )
176
+ conn.commit()
177
+ conn.close()
178
+
179
+ # ==================== API Contracts 操作 ====================
180
+
181
+ def save_api_contract(
182
+ self,
183
+ task_id: str,
184
+ endpoint: str,
185
+ method: str,
186
+ request_schema: Optional[Dict] = None,
187
+ response_schema: Optional[Dict] = None,
188
+ created_by: str = "backend",
189
+ ):
190
+ """保存前后端接口契约"""
191
+ conn = self._get_connection()
192
+ cursor = conn.cursor()
193
+ cursor.execute(
194
+ """INSERT INTO api_contracts
195
+ (task_id, endpoint, method, request_schema, response_schema, created_by)
196
+ VALUES (?, ?, ?, ?, ?, ?)""",
197
+ (
198
+ task_id,
199
+ endpoint,
200
+ method,
201
+ json.dumps(request_schema) if request_schema else None,
202
+ json.dumps(response_schema) if response_schema else None,
203
+ created_by,
204
+ ),
205
+ )
206
+ conn.commit()
207
+ conn.close()
208
+
209
+ def get_api_contracts(self, task_id: str) -> List[Dict]:
210
+ """获取任务的接口契约列表"""
211
+ conn = self._get_connection()
212
+ cursor = conn.cursor()
213
+ cursor.execute("SELECT * FROM api_contracts WHERE task_id = ?", (task_id,))
214
+ rows = cursor.fetchall()
215
+ conn.close()
216
+
217
+ columns = [
218
+ "id",
219
+ "task_id",
220
+ "endpoint",
221
+ "method",
222
+ "request_schema",
223
+ "response_schema",
224
+ "status",
225
+ "created_by",
226
+ "created_at",
227
+ ]
228
+ return [dict(zip(columns, row)) for row in rows]
229
+
230
+ # ==================== Audit Reports 操作 ====================
231
+
232
+ def add_audit_report(
233
+ self,
234
+ task_id: str,
235
+ agent_type: str,
236
+ severity: str,
237
+ message: str,
238
+ file_path: Optional[str] = None,
239
+ line_number: Optional[int] = None,
240
+ ):
241
+ """添加审计意见"""
242
+ conn = self._get_connection()
243
+ cursor = conn.cursor()
244
+ cursor.execute(
245
+ """INSERT INTO audit_reports
246
+ (task_id, agent_type, severity, message, file_path, line_number)
247
+ VALUES (?, ?, ?, ?, ?, ?)""",
248
+ (task_id, agent_type, severity, message, file_path, line_number),
249
+ )
250
+ conn.commit()
251
+ conn.close()
252
+
253
+ def get_audit_reports(self, task_id: str) -> List[Dict]:
254
+ """获取任务的审计报告"""
255
+ conn = self._get_connection()
256
+ cursor = conn.cursor()
257
+ cursor.execute(
258
+ "SELECT * FROM audit_reports WHERE task_id = ? ORDER BY severity",
259
+ (task_id,),
260
+ )
261
+ rows = cursor.fetchall()
262
+ conn.close()
263
+
264
+ columns = [
265
+ "id",
266
+ "task_id",
267
+ "agent_type",
268
+ "severity",
269
+ "message",
270
+ "file_path",
271
+ "line_number",
272
+ "created_at",
273
+ ]
274
+ return [dict(zip(columns, row)) for row in rows]
utils/__init__.py ADDED
File without changes