# cockpit_chat.dspy - Send message or list conversation # POST: action=send_message, iteration_id, message_text # GET: action=list_messages, iteration_id (optional), task_id (optional) action = (params_kw or {}).get('action', 'list_messages') dbname = get_module_dbname('pipeline-sdlc') if action == 'send_message': iteration_id = (params_kw or {}).get('iteration_id', '') task_id = (params_kw or {}).get('task_id', '') message_text = (params_kw or {}).get('message_text', '').strip() if not iteration_id or not message_text: return json.dumps({"error": "Missing iteration_id or message_text"}, ensure_ascii=False) uid = await get_user() async with DBPools().sqlorContext(dbname) as sor: # Save user message msg_id = getID() await sor.I('sd_conversations', { 'id': msg_id, 'iteration_id': iteration_id, 'task_id': task_id or '', 'step_name': '', 'role': 'user', 'content': message_text, 'attachments': '[]', 'msg_type': 'text', 'org_id': '0', 'created_by': uid }) # Generate agent response # 1. Check if there's an active pipeline task for this iteration if task_id: agent_reply = f"收到你的消息。当前任务 {task_id} 正在执行中,你的输入已记录。" else: # Check for existing iterations iters = await sor.sqlExe( "SELECT id, task_id, status FROM sd_iterations WHERE id=${iid}$", {"iid": iteration_id}) if iters: it = iters[0] if hasattr(it, 'task_id') and it.task_id: agent_reply = f"收到。迭代任务 {it.task_id} 状态: {it.status}。" else: agent_reply = f"收到你的需求:「{message_text[:50]}...」。请确认是否要为此迭代启动开发产线?" else: agent_reply = f"收到你的需求:「{message_text[:50]}...」。建议先在项目管理中创建项目和迭代。" # Save agent message agent_msg_id = getID() await sor.I('sd_conversations', { 'id': agent_msg_id, 'iteration_id': iteration_id, 'task_id': task_id or '', 'step_name': '', 'role': 'agent', 'content': agent_reply, 'attachments': '[]', 'msg_type': 'text', 'org_id': '0', 'created_by': 'system' }) return json.dumps({"success": True, "message_id": msg_id}, ensure_ascii=False) else: # list_messages - return messages as Bricks widget JSON iteration_id = (params_kw or {}).get('iteration_id', '') task_id = (params_kw or {}).get('task_id', '') msgs = [] if iteration_id or task_id: async with DBPools().sqlorContext(dbname) as sor: where = [] params = {} if task_id: where.append("task_id=${tid}$") params["tid"] = task_id if iteration_id: where.append("iteration_id=${iid}$") params["iid"] = iteration_id sql = f"SELECT role, content, msg_type, created_at FROM sd_conversations WHERE {' OR '.join(where)} ORDER BY created_at ASC LIMIT 50" msgs = await sor.sqlExe(sql, params) # Build chat messages widget msg_widgets = [] for m in msgs: role = m.role if hasattr(m, 'role') else '' content = m.content if hasattr(m, 'content') else '' if role == 'agent': bg = '#e8f0fe' align = 'flex-start' label = 'Agent' label_color = '#3b82f6' elif role == 'user': bg = '#dbeafe' align = 'flex-end' label = '你' label_color = '#2563eb' else: bg = '#f1f5f9' align = 'center' label = '系统' label_color = '#94a3b8' msg_widgets.append({ "widgettype": "VBox", "options": { "width": "85%", "alignSelf": align, "bgcolor": bg, "borderRadius": "12px", "padding": "12px 16px", "marginBottom": "10px", "gap": "4px" }, "subwidgets": [ {"widgettype": "Text", "options": { "text": label, "cfontsize": 0.75, "color": label_color, "fontWeight": "bold" }}, {"widgettype": "Text", "options": { "text": content, "cfontsize": 0.95, "color": "#1e293b", "whiteSpace": "pre-wrap" }} ] }) if not msg_widgets: msg_widgets.append({ "widgettype": "Text", "options": { "text": "暂无对话记录。选择一个迭代后,在下方输入框中开始对话。", "cfontsize": 0.9, "color": "#94a3b8", "padding": "20px" } }) return { "widgettype": "VBox", "options": {"width": "100%", "padding": "4px"}, "subwidgets": msg_widgets }