# agent_menus.dspy - 返回当前产线的功能菜单(AgentIO 上方菜单/按钮) # 由 core 通用 agent 的 index.ui 通过 urlwidget 加载,动态渲染产线固化的联机功能入口 import json as _json uid = await get_user() if not uid: uid = 'user-01' # 1. 解析当前产线:用户当前项目 → sd_projects.pipeline_id pipeline_id = '' pid = '' try: dbname = get_module_dbname('pipeline_core') async with DBPools().sqlorContext(dbname) as sor: recs = await sor.sqlExe( "SELECT current_project_id FROM pipeline_agent_settings WHERE user_id=${u}$", {"u": uid}) pid = getattr(recs[0], 'current_project_id', '') if recs else '' if pid: precs = await sor.sqlExe( "SELECT pipeline_id FROM sd_projects WHERE id=${p}$", {"p": pid}) if precs: pipeline_id = getattr(precs[0], 'pipeline_id', '') or '' except Exception: pass if not pipeline_id: try: from pipeline_core import DEFAULT_ABILITY_ID pipeline_id = DEFAULT_ABILITY_ID except Exception: pipeline_id = 'sdlc_general' # 2. 读产线功能菜单 try: from pipeline_core.ability import get_ability_menus menus = get_ability_menus(pipeline_id) except Exception: menus = [] # 3. 构建菜单按钮(actiontype=urlwidget 打开通用弹窗 agent_menu_open,控制弹窗大小) # 注意:这里不预先 urlencode 查询参数。前端 bricks_fetch 的 url_parse 会把 url 里的 # 查询参数提取后再 encodeURIComponent 编码一次,若此处已 urlencode 会导致双重编码 # (title 的 emoji/中文会变成 %F0%9F... 明文显示)。 buttons = [] # 无当前项目时,点击「需要项目」的按钮弹报错(不打开页面) _no_proj_script = "var m=new bricks.Message({title:'提示',message:'请先在会话中切换项目'});m.open();" def _bind(url, require_project): if require_project and not pid: return { "wid": "self", "event": "click", "actiontype": "script", "target": "self", "script": _no_proj_script, } return { "wid": "self", "event": "click", "actiontype": "urlwidget", "target": "self", "options": {"url": url}, } # 固定「工作空间」按钮(通用能力,所有 agent 都有;依赖当前项目) buttons.append({ "widgettype": "Button", "options": {"label": "🗂 工作空间", "css": "small", "id": "menu_btn_ws"}, "binds": [_bind(entire_url("/pipeline-sdlc/api/workspace_popup.dspy"), True)], }) for i, m in enumerate(menus): label = str(m.get("label", "") or "") url = str(m.get("url", "") or "") if not url: continue require_project = bool(m.get("require_project", False)) width = str(m.get("width", "85%") or "85%") height = str(m.get("height", "80%") or "80%") open_url = entire_url("/pipeline_core/api/agent_menu_open.dspy") + "?url=" + url + "&title=" + label + "&width=" + width + "&height=" + height buttons.append({ "widgettype": "Button", "options": {"label": label, "css": "small", "id": "menu_btn_%d" % i}, "binds": [_bind(open_url, require_project)], }) return _json.dumps({ "widgettype": "HBox", "options": {"width": "100%", "padding": "0 24px 8px 24px", "gap": "8px", "alignItems": "center"}, "subwidgets": buttons, }, ensure_ascii=False)