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
|
@@ -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,,
|
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
|