From ee8005644ff68c2a852573abc1501bf348f25cfe Mon Sep 17 00:00:00 2001 From: yumoqing Date: Sun, 2 Aug 2026 14:59:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20Phase=203=20=E2=80=94=20cockpit=5Fagent?= =?UTF-8?q?.dspy=20(start/status/deliverables/review)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wwwroot/api/cockpit_agent.dspy | 120 +++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 wwwroot/api/cockpit_agent.dspy diff --git a/wwwroot/api/cockpit_agent.dspy b/wwwroot/api/cockpit_agent.dspy new file mode 100644 index 0000000..339f446 --- /dev/null +++ b/wwwroot/api/cockpit_agent.dspy @@ -0,0 +1,120 @@ +# cockpit_agent.dspy — Agent control: start/stop, status, deliverables +# POST: action=start_agent | agent_status | list_deliverables | review_deliverable + +import aiohttp + +action = (params_kw or {}).get('action', 'agent_status') +dbname = get_module_dbname('pipeline-sdlc') +uid = await get_user() +org_id = await get_userorgid() or '0' + + +async def _get_context(sor, uid): + recs = await sor.sqlExe( + "SELECT current_project_id, current_iteration_id FROM pipeline_agent_settings WHERE user_id=${uid}$", + {"uid": uid}) + if recs: + return {'project_id': getattr(recs[0], 'current_project_id', '') or '', + 'iteration_id': getattr(recs[0], 'current_iteration_id', '') or ''} + return {'project_id': '', 'iteration_id': ''} + + +if action == 'start_agent': + async with DBPools().sqlorContext(dbname) as sor: + ctx = await _get_context(sor, uid) + pid = ctx['project_id'] + if not pid: + return json.dumps({"success": False, "error": "请先选择项目"}, ensure_ascii=False) + + model_id = (params_kw or {}).get('model_id', '') + role = (params_kw or {}).get('role', 'developer') + + # Ensure agent config exists + existing = await sor.sqlExe( + "SELECT id FROM pipeline_project_agents WHERE project_id=${pid}$ AND role_name=${role}$", + {"pid": pid, "role": role}) + if not existing: + await sor.C('pipeline_project_agents', { + 'id': getID(), 'project_id': pid, 'role_name': role, + 'model_name': model_id, 'status': 'running' + }) + else: + await sor.sqlExe( + "UPDATE pipeline_project_agents SET status='running', model_name=${mid}$ WHERE project_id=${pid}$ AND role_name=${role}$", + {"pid": pid, "role": role, "mid": model_id}) + + # Run one iteration synchronously + try: + result = await run_agent_loop(pid, role, model_id or None) + return json.dumps({"success": True, "result": result}, ensure_ascii=False) + except Exception as e: + return json.dumps({"success": False, "error": str(e)[:200]}, ensure_ascii=False) + + +elif action == 'agent_status': + async with DBPools().sqlorContext(dbname) as sor: + ctx = await _get_context(sor, uid) + pid = ctx['project_id'] + + agents = [] + if pid: + recs = await sor.sqlExe( + "SELECT role_name, status, model_name FROM pipeline_project_agents WHERE project_id=${pid}$", + {"pid": pid}) + agents = [{'role': getattr(r, 'role_name', ''), 'status': getattr(r, 'status', ''), + 'model': getattr(r, 'model_name', '')} for r in recs] + + # Recent deliverables + delivs = [] + if pid: + drecs = await sor.sqlExe( + "SELECT id, title, deliverable_type, quality_score, review_status, created_by, created_at " + "FROM pipeline_deliverables WHERE project_id=${pid}$ ORDER BY created_at DESC LIMIT 10", + {"pid": pid}) + delivs = [{'id': d.id, 'title': d.title, 'type': d.deliverable_type, + 'score': d.quality_score, 'review': d.review_status, + 'by': d.created_by, 'at': str(d.created_at)[:16]} for d in drecs] + + return json.dumps({"success": True, "agents": agents, "deliverables": delivs, + "project_name": ctx.get('project_name', '')}, ensure_ascii=False) + + +elif action == 'list_deliverables': + async with DBPools().sqlorContext(dbname) as sor: + ctx = await _get_context(sor, uid) + pid = ctx['project_id'] + if not pid: + return json.dumps([], ensure_ascii=False) + + drecs = await sor.sqlExe( + "SELECT id, task_id, title, deliverable_type, quality_score, review_status, " + "LEFT(content,200) as preview, created_by, created_at " + "FROM pipeline_deliverables WHERE project_id=${pid}$ ORDER BY created_at DESC LIMIT 50", + {"pid": pid}) + rows = [] + for d in drecs: + rows.append({ + 'id': d.id, 'task_id': d.task_id, 'title': d.title, + 'type': d.deliverable_type, 'score': d.quality_score, + 'review': d.review_status, 'preview': getattr(d, 'preview', ''), + 'by': d.created_by, 'at': str(d.created_at)[:16] + }) + return json.dumps(rows, ensure_ascii=False) + + +elif action == 'review_deliverable': + did = (params_kw or {}).get('id', '') + status = (params_kw or {}).get('status', 'approved') + comment = (params_kw or {}).get('comment', '') + if not did: + return json.dumps({"success": False, "error": "缺少id"}, ensure_ascii=False) + + async with DBPools().sqlorContext(dbname) as sor: + await sor.sqlExe( + "UPDATE pipeline_deliverables SET review_status=${st}$, reviewed_by=${uid}$, review_comment=${cm}$ WHERE id=${did}$", + {"st": status, "uid": uid, "cm": comment, "did": did}) + return json.dumps({"success": True}, ensure_ascii=False) + + +else: + return json.dumps({"success": False, "error": f"Unknown action: {action}"}, ensure_ascii=False)