123 lines
5.3 KiB
Plaintext
123 lines
5.3 KiB
Plaintext
# cockpit_chat_v2.dspy - SDLC Agent v2 (AgentExecutor 驱动)
|
||
# 替代 cockpit_chat.dspy,使用 pipeline-core/pipeline-service v2 架构
|
||
|
||
import aiohttp
|
||
|
||
action = (params_kw or {}).get('action', 'send_message')
|
||
msg = ''
|
||
if action != 'send_message':
|
||
p = params_kw or {}
|
||
msg = (p.get('message_text') or '').strip()
|
||
if not msg:
|
||
inner = p.get('params', {})
|
||
if isinstance(inner, dict):
|
||
msg = (inner.get('prompt') or inner.get('message_text') or '').strip()
|
||
if msg:
|
||
action = 'send_message'
|
||
|
||
dbname = get_module_dbname('pipeline-sdlc')
|
||
|
||
# ── Widget helpers ──
|
||
def _w_text(t, css='agent-text'):
|
||
return {"widgettype":"Text","options":{"text":t,"halign":"left","css":css}}
|
||
def _w_progress(t):
|
||
return {"widgettype":"Text","options":{"text":t,"halign":"left","style":{"color":"#f0a040","fontStyle":"italic"}}}
|
||
def _w_md(t):
|
||
return {"widgettype":"MdWidget","options":{"text":t}}
|
||
def _w_card(title, body, kind="info"):
|
||
colors = {"success":"#4caf50","error":"#f44336","info":"#2196f3","warn":"#ff9800"}
|
||
color = colors.get(kind, colors["info"])
|
||
title_w = {"widgettype":"Text","options":{"text":title,"halign":"left","style":{"fontWeight":"bold","color":color,"marginBottom":"4px"}}}
|
||
body_w = body if isinstance(body, dict) else _w_text(str(body))
|
||
return {"widgettype":"VBox","subwidgets":[title_w, body_w],
|
||
"options":{"css":"agent-card","style":{"borderLeft":"3px solid "+color,"paddingLeft":"8px","marginBottom":"8px"}}}
|
||
|
||
if action == 'send_message':
|
||
prompt = msg
|
||
|
||
# ── 意图识别:停止类指令 ──
|
||
stop_keywords = ['停止', '取消', '停下', '终止', '停', 'stop', 'cancel', 'abort']
|
||
if any(kw == prompt.strip().lower() or kw in prompt.strip().lower() for kw in stop_keywords):
|
||
async def _stop_stream():
|
||
yield json.dumps(_w_md("已停止当前任务。"), ensure_ascii=False) + '\n'
|
||
return await stream_response(request, _stop_stream, 'text/plain; charset=utf-8')
|
||
|
||
uid = await get_user()
|
||
if not uid:
|
||
uid = 'user-01' # 测试兼容
|
||
|
||
# ── 加载上下文 ──
|
||
ctx = {'pid': '', 'name': ''}
|
||
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})
|
||
if recs:
|
||
ctx['pid'] = getattr(recs[0], 'current_project_id', '') or ''
|
||
if ctx['pid']:
|
||
proj_recs = await sor.sqlExe(
|
||
"SELECT name FROM sd_projects WHERE id=${p}$", {"p": ctx['pid']})
|
||
if proj_recs:
|
||
ctx['name'] = getattr(proj_recs[0], 'name', '')
|
||
|
||
# ── 加载 v2 引擎 ──
|
||
from pipeline_core.agent_config import load_agent_config
|
||
from pipeline_service.agent_loop_v2 import AgentExecutor
|
||
|
||
config = await load_agent_config(project_id=ctx['pid'])
|
||
executor = AgentExecutor(
|
||
config=config,
|
||
project_id=ctx['pid'],
|
||
user_id=uid,
|
||
)
|
||
|
||
async def agent_stream():
|
||
# 项目上下文
|
||
if ctx['name']:
|
||
d = json.dumps(_w_progress("项目: " + ctx['name']), ensure_ascii=False) + '\n'
|
||
yield d
|
||
|
||
# 运行 AgentExecutor
|
||
async for chunk in executor.run(prompt):
|
||
data = json.loads(chunk)
|
||
t = data.get('type', '')
|
||
|
||
if t == 'progress':
|
||
d = json.dumps(_w_progress(data.get('message', '')), ensure_ascii=False) + '\n'
|
||
elif t == 'auto_tool':
|
||
d = json.dumps(_w_progress("🔄 " + data.get('message', '')), ensure_ascii=False) + '\n'
|
||
elif t == 'debug':
|
||
d = '' # skip debug in UI
|
||
continue
|
||
elif t == 'tool_call':
|
||
tool = data.get('tool', '')
|
||
d = json.dumps(_w_card("调用: " + tool, "参数: " + json.dumps(data.get('params', {}), ensure_ascii=False), "info"), ensure_ascii=False) + '\n'
|
||
elif t == 'tool_result':
|
||
tool = data.get('tool', '')
|
||
result = data.get('result', '')
|
||
good = not result.startswith('ERROR') and not result.startswith('未知工具')
|
||
d = json.dumps(_w_card(tool + " 结果", result[:600], "success" if good else "error"), ensure_ascii=False) + '\n'
|
||
elif t == 'reply':
|
||
d = json.dumps(_w_md(data.get('message', '')), ensure_ascii=False) + '\n'
|
||
elif t == 'error':
|
||
d = json.dumps(_w_card("错误", data.get('message', ''), "error"), ensure_ascii=False) + '\n'
|
||
else:
|
||
d = chunk # 透传
|
||
|
||
yield d
|
||
|
||
return await stream_response(request, agent_stream, 'text/plain; charset=utf-8')
|
||
|
||
elif action == 'list_messages':
|
||
uid = await get_user()
|
||
if not uid:
|
||
return json.dumps({"error":"请先登录"}, ensure_ascii=False)
|
||
async with DBPools().sqlorContext(dbname) as sor:
|
||
ms = await sor.sqlExe(
|
||
"SELECT role,content,created_at FROM pipeline_conversations ORDER BY created_at ASC LIMIT 100", {})
|
||
result = [{"role":getattr(m,'role',''),"content":getattr(m,'content','')} for m in (ms or [])]
|
||
return json.dumps({"success":True,"messages":result}, ensure_ascii=False, default=str)
|
||
|
||
else:
|
||
return json.dumps({"error":"Unknown: " + str(action)}, ensure_ascii=False)
|