补齐平台模板与场景 DSL、预算控制、执行看板和企业场景脚本,增强 Windows 启动/迁移与前端代理和聊天会话记忆,修复执行创建阶段 500 与异步链路排障体验。 Made-with: Cursor
125 lines
4.6 KiB
Python
125 lines
4.6 KiB
Python
"""E2E:知你客服16号 单轮「你好」(需 API + Celery + LLM)。
|
||
|
||
环境变量:
|
||
USE_TESTCLIENT=1 走 FastAPI TestClient(不经 HTTP),仅校验创建执行 201。
|
||
API_BASE 默认 http://127.0.0.1:8037;若该端口上 /health 无 checks,可改用本仓库单独起的实例,例如 http://127.0.0.1:8040。
|
||
E2E_AGENT_NAME / E2E_QUERY
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
import os
|
||
import sys
|
||
import time
|
||
import uuid
|
||
|
||
BACKEND_DIR = __file__.rsplit("scripts", 1)[0]
|
||
API_BASE = os.environ.get("API_BASE", "http://127.0.0.1:8037")
|
||
AGENT_NAME = os.environ.get("E2E_AGENT_NAME", "知你客服16号")
|
||
QUERY = os.environ.get("E2E_QUERY", "你好")
|
||
|
||
|
||
def main() -> int:
|
||
os.chdir(BACKEND_DIR)
|
||
sys.path.insert(0, BACKEND_DIR)
|
||
|
||
if os.environ.get("USE_TESTCLIENT") == "1":
|
||
from fastapi.testclient import TestClient
|
||
from app.main import app
|
||
from app.core.database import SessionLocal
|
||
from app.core.security import create_access_token
|
||
from app.models.agent import Agent
|
||
from app.models.user import User
|
||
|
||
db = SessionLocal()
|
||
try:
|
||
agent = db.query(Agent).filter(Agent.name == AGENT_NAME).first()
|
||
if not agent:
|
||
print(f"数据库中未找到「{AGENT_NAME}」", file=sys.stderr)
|
||
return 1
|
||
owner = db.query(User).filter(User.id == agent.user_id).first()
|
||
user = owner or db.query(User).first()
|
||
if not user:
|
||
print("无可用用户", file=sys.stderr)
|
||
return 1
|
||
token = create_access_token(data={"sub": user.id, "username": user.username})
|
||
h = {"Authorization": f"Bearer {token}"}
|
||
finally:
|
||
db.close()
|
||
|
||
c = TestClient(app)
|
||
er = c.post(
|
||
"/api/v1/executions",
|
||
headers=h,
|
||
json={
|
||
"agent_id": str(agent.id),
|
||
"input_data": {"query": QUERY, "user_id": f"e2e_z16_tc_{uuid.uuid4().hex[:8]}"},
|
||
},
|
||
)
|
||
print("TestClient POST /executions:", er.status_code, er.text[:800])
|
||
if er.status_code not in (200, 201):
|
||
return 2
|
||
print("OK — 本仓库 API 创建执行成功(异步需 Celery Worker 才会跑完)")
|
||
return 0
|
||
|
||
import httpx
|
||
from app.core.database import SessionLocal
|
||
from app.core.security import create_access_token
|
||
from app.models.agent import Agent
|
||
from app.models.user import User
|
||
|
||
db = SessionLocal()
|
||
try:
|
||
agent = db.query(Agent).filter(Agent.name == AGENT_NAME).first()
|
||
if not agent:
|
||
print(f"数据库中未找到「{AGENT_NAME}」", file=sys.stderr)
|
||
return 1
|
||
owner = db.query(User).filter(User.id == agent.user_id).first()
|
||
user = owner or db.query(User).first()
|
||
if not user:
|
||
print("无可用用户", file=sys.stderr)
|
||
return 1
|
||
token = create_access_token(data={"sub": user.id, "username": user.username})
|
||
headers = {"Authorization": f"Bearer {token}"}
|
||
uid = f"e2e_z16_{uuid.uuid4().hex[:10]}"
|
||
print(f"agent_id={agent.id} name={agent.name} Q={QUERY!r} user_id={uid}\n")
|
||
|
||
def poll(client: httpx.Client, execution_id: str, timeout: float = 300.0) -> dict:
|
||
t0 = time.time()
|
||
while time.time() - t0 < timeout:
|
||
r = client.get(f"/api/v1/executions/{execution_id}", headers=headers)
|
||
r.raise_for_status()
|
||
data = r.json()
|
||
st = data.get("status")
|
||
if st == "completed":
|
||
return data
|
||
if st == "failed":
|
||
print("error:", data.get("error_message"), file=sys.stderr)
|
||
raise RuntimeError("执行失败")
|
||
time.sleep(1)
|
||
raise TimeoutError("超时")
|
||
|
||
with httpx.Client(base_url=API_BASE, timeout=300.0) as client:
|
||
r = client.post(
|
||
"/api/v1/executions",
|
||
json={"agent_id": str(agent.id), "input_data": {"query": QUERY, "user_id": uid}},
|
||
headers=headers,
|
||
)
|
||
if r.status_code >= 400:
|
||
print(r.text, file=sys.stderr)
|
||
r.raise_for_status()
|
||
eid = r.json()["id"]
|
||
out = poll(client, eid)
|
||
od = out.get("output_data") or {}
|
||
rtxt = od.get("result") if isinstance(od, dict) else None
|
||
print("--- 回复 ---")
|
||
print((rtxt or json.dumps(od, ensure_ascii=False))[:4000])
|
||
finally:
|
||
db.close()
|
||
print("\n完成")
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|