container_model_acceptance.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import asyncio
  2. import json
  3. from pathlib import Path
  4. from uuid import UUID, uuid4
  5. import httpx
  6. from sqlalchemy import func, select
  7. from test_after_sales import scenario # noqa: F401
  8. from zhifu.modules.after_sales.models import Approval, Ticket
  9. from zhifu.modules.agent_runtime.models import AgentRun, ModelAttempt
  10. from zhifu.modules.integrations.models import ExternalOperation
  11. async def test_container_agent_uses_real_model_and_tools(scenario): # noqa: F811
  12. async with httpx.AsyncClient(
  13. base_url="http://127.0.0.1:28000",
  14. headers=scenario["headers"]["customer"],
  15. timeout=10,
  16. trust_env=False,
  17. ) as client:
  18. response = await client.post(
  19. "/openapi/v1/agent/runs",
  20. headers={"Idempotency-Key": uuid4().hex},
  21. json={
  22. "store_id": str(scenario["store"]),
  23. "message": (
  24. f"请查询订单 {scenario['order_id']},商品 keyboard-1 的键盘按键失灵。"
  25. "请创建数量为 1 的换货方案,并告知下一步操作。"
  26. ),
  27. },
  28. )
  29. assert response.status_code == 202, response.text
  30. run_id = UUID(response.json()["run_id"])
  31. async with asyncio.timeout(150):
  32. while True:
  33. response = await client.get(f"/openapi/v1/agent/runs/{run_id}")
  34. assert response.status_code == 200, response.text
  35. if response.json()["status"] not in {"QUEUED", "RUNNING"}:
  36. break
  37. await asyncio.sleep(1)
  38. assert response.json()["status"] == "COMPLETED", response.text
  39. async with scenario["app"].state.sessions() as session:
  40. run = await session.get(AgentRun, run_id.bytes)
  41. calls = {
  42. call["name"]
  43. for message in run.messages
  44. for call in message["data"].get("tool_calls", [])
  45. }
  46. assert {"get_order", "create_exchange_proposal"} <= calls
  47. attempts = (
  48. await session.scalars(select(ModelAttempt).where(ModelAttempt.run_id == run_id.bytes))
  49. ).all()
  50. assert attempts and all(value.status == "RECEIVED" and value.usage for value in attempts)
  51. tickets = (
  52. await session.scalars(
  53. select(Ticket).where(Ticket.tenant_id == scenario["tenant"].bytes)
  54. )
  55. ).all()
  56. assert len(tickets) == 1 and tickets[0].status == "DRAFT"
  57. for model in (Approval, ExternalOperation):
  58. assert (
  59. await session.scalar(
  60. select(func.count())
  61. .select_from(model)
  62. .where(model.tenant_id == scenario["tenant"].bytes)
  63. )
  64. == 0
  65. )
  66. report = {
  67. "status": response.json()["status"],
  68. "tools": sorted(calls),
  69. "model_calls": len(attempts),
  70. "total_tokens": sum(value.usage["total_tokens"] for value in attempts),
  71. "ticket_status": tickets[0].status,
  72. }
  73. await asyncio.to_thread(
  74. Path("docs/verification/container-model.json").write_text,
  75. json.dumps(report, indent=2),
  76. encoding="utf-8",
  77. )