feat: multi-repo workspace — org settings, project repos, deliverable types
- sd_projects: add workspace_dir field - sd_project_repos: new table for multi-repo per project - sd_org_settings: org-level workspace_root + skills_dir - pipeline_deliverables: add repo_name, target_path, deliverable_type enum - cockpit_chat.dspy: load repos+org settings, pass to LLM prompt
This commit is contained in:
parent
95bd140393
commit
f86cce02f4
17
json/sd_org_setting_list.json
Normal file
17
json/sd_org_setting_list.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"tblname": "sd_org_settings",
|
||||
"title": "组织SDLC设置",
|
||||
"params": {
|
||||
"sortby": ["created_at desc"],
|
||||
"logined_userorgid": "org_id",
|
||||
"browserfields": {
|
||||
"exclouded": []
|
||||
},
|
||||
"editexclouded": ["id", "created_at", "updated_at", "org_id"],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/sd_org_setting_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/sd_org_setting_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/sd_org_setting_delete.dspy')}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -68,6 +68,12 @@
|
||||
"title": "迭代列表",
|
||||
"url": "{{entire_url('../sd_iteration_list?project_id=${id}')}}",
|
||||
"subtable": "sd_iterations"
|
||||
},
|
||||
{
|
||||
"field": "project_id",
|
||||
"title": "代码仓库",
|
||||
"url": "{{entire_url('../sd_project_repo_list?project_id=${id}')}}",
|
||||
"subtable": "sd_project_repos"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
17
json/sd_project_repo_list.json
Normal file
17
json/sd_project_repo_list.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"tblname": "sd_project_repos",
|
||||
"title": "代码仓库",
|
||||
"params": {
|
||||
"sortby": ["created_at desc"],
|
||||
"logined_userorgid": "org_id",
|
||||
"browserfields": {
|
||||
"exclouded": ["org_id"]
|
||||
},
|
||||
"editexclouded": ["id", "created_at", "org_id"],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/sd_project_repo_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/sd_project_repo_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/sd_project_repo_delete.dspy')}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
36
models/pipeline_deliverables.json
Normal file
36
models/pipeline_deliverables.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"summary": [
|
||||
{
|
||||
"name": "pipeline_deliverables",
|
||||
"title": "任务交付件",
|
||||
"primary": ["id"],
|
||||
"catelog": "entity"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{"name": "id", "title": "主键ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "project_id", "title": "项目ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "task_id", "title": "任务ID", "type": "str", "length": 32},
|
||||
{"name": "title", "title": "交付件标题", "type": "str", "length": 200, "nullable": "no"},
|
||||
{"name": "deliverable_type", "title": "交付件类型", "type": "str", "length": 32, "nullable": "no", "default": "'code'"},
|
||||
{"name": "repo_name", "title": "目标仓库别名", "type": "str", "length": 100},
|
||||
{"name": "target_path", "title": "仓库内相对路径", "type": "str", "length": 500},
|
||||
{"name": "file_path", "title": "本地存储路径", "type": "str", "length": 500},
|
||||
{"name": "content", "title": "交付件内容", "type": "longtext"},
|
||||
{"name": "quality_score", "title": "质量评分(0-100)", "type": "int", "nullable": "no", "default": "80"},
|
||||
{"name": "review_status", "title": "评审状态", "type": "str", "length": 20, "nullable": "no", "default": "'pending'"},
|
||||
{"name": "review_comment", "title": "评审意见", "type": "text"},
|
||||
{"name": "reviewed_by", "title": "评审人", "type": "str", "length": 32},
|
||||
{"name": "created_by", "title": "创建人", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "created_at", "title": "创建时间", "type": "timestamp", "nullable": "no"}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_pd_project", "idxtype": "index", "idxfields": ["project_id"]},
|
||||
{"name": "idx_pd_task", "idxtype": "index", "idxfields": ["task_id"]},
|
||||
{"name": "idx_pd_review", "idxtype": "index", "idxfields": ["review_status"]}
|
||||
],
|
||||
"codes": [
|
||||
{"field": "deliverable_type", "table": "appcodes_kv", "valuefield": "k", "textfield": "v", "cond": "parentid='deliverable_type'"},
|
||||
{"field": "review_status", "table": "appcodes_kv", "valuefield": "k", "textfield": "v", "cond": "parentid='review_status'"}
|
||||
]
|
||||
}
|
||||
21
models/sd_org_settings.json
Normal file
21
models/sd_org_settings.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"summary": [
|
||||
{
|
||||
"name": "sd_org_settings",
|
||||
"title": "组织SDLC设置",
|
||||
"primary": ["id"],
|
||||
"catelog": "entity"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{"name": "id", "title": "主键ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "org_id", "title": "组织ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "workspace_root", "title": "工作空间根路径", "type": "str", "length": 500, "nullable": "no"},
|
||||
{"name": "skills_dir", "title": "企业Skills目录", "type": "str", "length": 500},
|
||||
{"name": "created_at", "title": "创建时间", "type": "timestamp", "nullable": "no"},
|
||||
{"name": "updated_at", "title": "更新时间", "type": "timestamp"}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_sos_org", "idxtype": "unique", "idxfields": ["org_id"]}
|
||||
]
|
||||
}
|
||||
24
models/sd_project_repos.json
Normal file
24
models/sd_project_repos.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"summary": [
|
||||
{
|
||||
"name": "sd_project_repos",
|
||||
"title": "项目代码仓库",
|
||||
"primary": ["id"],
|
||||
"catelog": "entity"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{"name": "id", "title": "主键ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "project_id", "title": "项目ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "repo_name", "title": "仓库别名", "type": "str", "length": 100, "nullable": "no"},
|
||||
{"name": "repo_url", "title": "Git地址", "type": "str", "length": 500, "nullable": "no"},
|
||||
{"name": "default_branch", "title": "默认分支", "type": "str", "length": 100, "nullable": "no", "default": "'main'"},
|
||||
{"name": "local_path", "title": "本地相对路径", "type": "str", "length": 200},
|
||||
{"name": "org_id", "title": "组织ID", "type": "str", "length": 32, "nullable": "no", "default": "'0'"},
|
||||
{"name": "created_at", "title": "创建时间", "type": "timestamp", "nullable": "no"}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_spr_project", "idxtype": "index", "idxfields": ["project_id"]},
|
||||
{"name": "idx_spr_org", "idxtype": "index", "idxfields": ["org_id"]}
|
||||
]
|
||||
}
|
||||
@ -13,7 +13,8 @@
|
||||
{"name": "description", "title": "项目描述", "type": "text"},
|
||||
{"name": "project_type", "title": "项目类型", "type": "str", "length": 50, "nullable": "no", "default": "'web_app'"},
|
||||
{"name": "tech_stack", "title": "技术栈配置(JSON)", "type": "text"},
|
||||
{"name": "repo_url", "title": "代码仓库地址", "type": "str", "length": 500},
|
||||
{"name": "repo_url", "title": "主仓库地址", "type": "str", "length": 500},
|
||||
{"name": "workspace_dir", "title": "项目工作目录", "type": "str", "length": 500},
|
||||
{"name": "pipeline_id", "title": "关联Pipeline定义ID", "type": "str", "length": 32},
|
||||
{"name": "status", "title": "项目状态", "type": "str", "length": 20, "nullable": "no", "default": "'draft'"},
|
||||
{"name": "org_id", "title": "组织ID", "type": "str", "length": 32, "nullable": "no", "default": "'0'"},
|
||||
|
||||
@ -53,18 +53,34 @@ async def _load_context(sor, uid):
|
||||
"SELECT current_project_id, current_iteration_id FROM pipeline_agent_settings WHERE user_id=${uid}$",
|
||||
{"uid": uid}
|
||||
)
|
||||
ctx = {'project_id': '', 'iteration_id': '', 'project_name': '', 'iteration_name': ''}
|
||||
ctx = {'project_id': '', 'iteration_id': '', 'project_name': '', 'iteration_name': '',
|
||||
'workspace_dir': '', 'workspace_root': '', 'skills_dir': '', 'repos': []}
|
||||
if recs:
|
||||
r = recs[0]
|
||||
ctx['project_id'] = getattr(r, 'current_project_id', '') or ''
|
||||
ctx['iteration_id'] = getattr(r, 'current_iteration_id', '') or ''
|
||||
if ctx['project_id']:
|
||||
projs = await sor.sqlExe(
|
||||
"SELECT name, workspace_dir FROM sd_projects WHERE id=${pid}$", {"pid": ctx['project_id']}
|
||||
"SELECT name, workspace_dir, org_id FROM sd_projects WHERE id=${pid}$", {"pid": ctx['project_id']}
|
||||
)
|
||||
if projs:
|
||||
ctx['project_name'] = getattr(projs[0], 'name', '')
|
||||
ctx['workspace_dir'] = getattr(projs[0], 'workspace_dir', '') or ''
|
||||
org_id = getattr(projs[0], 'org_id', '') or '0'
|
||||
# Load org settings
|
||||
orgs = await sor.sqlExe(
|
||||
"SELECT workspace_root, skills_dir FROM sd_org_settings WHERE org_id=${oid}$", {"oid": org_id}
|
||||
)
|
||||
if orgs:
|
||||
ctx['workspace_root'] = getattr(orgs[0], 'workspace_root', '') or ''
|
||||
ctx['skills_dir'] = getattr(orgs[0], 'skills_dir', '') or ''
|
||||
# Load repos
|
||||
repos = await sor.sqlExe(
|
||||
"SELECT repo_name, repo_url, default_branch, local_path FROM sd_project_repos WHERE project_id=${pid}$",
|
||||
{"pid": ctx['project_id']}
|
||||
)
|
||||
ctx['repos'] = [{'name': r.repo_name, 'url': r.repo_url,
|
||||
'branch': r.default_branch, 'path': r.local_path or ''} for r in (repos or [])]
|
||||
if ctx['iteration_id']:
|
||||
iters = await sor.sqlExe(
|
||||
"SELECT iteration_name FROM sd_iterations WHERE id=${iid}$", {"iid": ctx['iteration_id']}
|
||||
@ -407,20 +423,40 @@ if action == 'send_message':
|
||||
if not tasks:
|
||||
agent_reply = f"项目「{ctx['project_name']}」暂无待执行任务。\n\n请先提交开发任务,例如:设计用户表结构"
|
||||
else:
|
||||
# Execute each pending task
|
||||
results = []
|
||||
ws = ctx.get('workspace_dir', '')
|
||||
results = [f"📁 工作目录:{ws}" if ws else "📁 工作目录:未配置"]
|
||||
ws_root = ctx.get('workspace_root', '')
|
||||
repos = ctx.get('repos', [])
|
||||
|
||||
# Build repo info for prompt
|
||||
repo_lines = [f"工作空间根路径:{ws_root}" if ws_root else "工作空间根路径:未配置"]
|
||||
repo_lines.append(f"项目本地路径:{ws}" if ws else "项目本地路径:未配置")
|
||||
skills_dir = ctx.get('skills_dir', '')
|
||||
if skills_dir:
|
||||
repo_lines.append(f"企业Skills目录:{skills_dir}")
|
||||
if repos:
|
||||
repo_lines.append("关联代码仓库:")
|
||||
for rp in repos:
|
||||
repo_lines.append(f" - {rp['name']}: {rp['url']} (分支:{rp['branch']}, 本地:{rp['path']})")
|
||||
results.append('\\n'.join(repo_lines))
|
||||
|
||||
for t in tasks:
|
||||
try:
|
||||
task_msgs = [{"role": "system", "content": settings['system_prompt']}]
|
||||
task_msgs.append({"role": "user", "content": f"请完成:{t.title}\n工作目录:{ws}"})
|
||||
prompt_parts = [f"请完成:{t.title}"]
|
||||
prompt_parts.append('\\n'.join(repo_lines))
|
||||
prompt_parts.append("请在关联仓库中直接修改代码文件,完成后提供变更摘要。")
|
||||
task_msgs.append({"role": "user", "content": '\\n'.join(prompt_parts)})
|
||||
result = await _call_llm(model_info, task_msgs, settings['temperature'])
|
||||
did = getID()
|
||||
role_dir = f"{ws}/deliverables/agent"
|
||||
role_dir = f\"{ws}/deliverables/agent\" if ws else \"deliverables/agent\"
|
||||
# Detect primary repo for deliverable
|
||||
primary_repo = repos[0]['name'] if repos else ''
|
||||
await sor.C('pipeline_deliverables', {
|
||||
'id': did, 'project_id': pid, 'task_id': t.id,
|
||||
'deliverable_type': 'code', 'title': t.title, 'content': result,
|
||||
'file_path': f"{role_dir}/{t.id}.md",
|
||||
'repo_name': primary_repo, 'target_path': '',
|
||||
'file_path': f\"{role_dir}/{t.id}.md\",
|
||||
'quality_score': 80, 'review_status': 'pending', 'created_by': 'agent'
|
||||
})
|
||||
await sor.sqlExe("UPDATE pipeline_tasks SET state='completed' WHERE id=${tid}$", {"tid": t.id})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user