# 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' session_id = (params_kw or {}).get('session_id', '') async def _get_context(sor, uid, session_id=''): pid, iid = await get_session_context(sor, uid, session_id) return {'project_id': pid, 'iteration_id': iid} if action == 'start_agent': async with DBPools().sqlorContext(dbname) as sor: ctx = await _get_context(sor, uid, session_id) 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', 'develop') # model_id 兼容 id / vendor_model_id / 注册名 → 统一走模型治理模块解析 # (pipeline_llm.selection.resolve_model_name,唯一解析点,不再各模块自查表) # capabilities='chat':角色 agent 只接受对话能力(t2t/i2t/m2t,2026-09-06) if model_id: _resolved = await llm_resolve_model_name(model_id, org_id=org_id, capabilities='chat') if _resolved: model_id = _resolved # 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, session_id) 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, session_id) 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)