| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- import json
- import threading
- import time
- from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
- from unittest.mock import MagicMock, patch
- from hello_agents import SimpleAgent
- from app.agent.agent import ModeratorAgent, ParticipantAgent, run_simple_agent
- from demo_helloagents import run_demo
- def _configure_llm(monkeypatch):
- monkeypatch.setenv("API_KEY", "test-key")
- monkeypatch.setenv("MODEL_NAME", "test-model")
- monkeypatch.setenv("BASE_URL", "https://example.test/v1/")
- def test_one_shot_task_is_driven_by_helloagents(monkeypatch):
- _configure_llm(monkeypatch)
- framework_agent = MagicMock()
- framework_agent.run.return_value = "framework response"
- with patch("app.agent.agent.HelloAgentsLLM") as llm_class, patch(
- "app.agent.agent.SimpleAgent", return_value=framework_agent
- ) as agent_class:
- response = run_simple_agent("TestAgent", "system", "hello")
- llm_class.assert_called_once()
- agent_class.assert_called_once()
- framework_agent.run.assert_called_once_with("hello")
- assert response == "framework response"
- def test_participant_reuses_helloagents_agent_for_multiple_turns(monkeypatch):
- _configure_llm(monkeypatch)
- framework_agent = MagicMock()
- framework_agent.run.return_value = '{"decision":"LISTEN","inner_monologue":"观察"}'
- framework_agent.stream_run.return_value = iter(["第一段", "第二段"])
- persona = {
- "name": "测试嘉宾",
- "bio": "测试生平",
- "title": "研究者",
- "theories": ["测试理论"],
- "stance": "审慎",
- "system_prompt": "保持审慎。",
- }
- with patch("app.agent.agent.HelloAgentsLLM"):
- participant = ParticipantAgent("测试嘉宾", persona, 2, "测试议题")
- with patch.object(participant, "run", return_value=framework_agent.run.return_value), patch.object(
- participant, "stream_run", return_value=framework_agent.stream_run.return_value
- ):
- thought = participant.think("当前讨论")
- chunks = list(participant.speak(thought, "当前讨论"))
- assert isinstance(participant, SimpleAgent)
- assert thought["action"] == "listen"
- assert chunks == ["第一段", "第二段"]
- def test_end_to_end_discussion_uses_helloagents_agents(monkeypatch):
- _configure_llm(monkeypatch)
- streams = iter([iter(["主持人开场"]), iter(["嘉宾发言"]), iter(["阶段总结"]), iter(["主持人闭幕"])])
- with patch("app.agent.agent.HelloAgentsLLM"), patch.object(
- SimpleAgent, "run", return_value='{"decision":"APPLY_SPEAK","inner_monologue":"回应议题"}'
- ), patch.object(SimpleAgent, "stream_run", side_effect=lambda *args, **kwargs: next(streams)):
- transcript = run_demo("测试议题")
- assert transcript["opening"] == "主持人开场"
- assert transcript["thought"]["action"] == "apply_to_speak"
- assert transcript["speech"] == "嘉宾发言"
- assert transcript["summary"] == "阶段总结"
- assert transcript["closing"] == "主持人闭幕"
- def test_madf_agents_are_native_helloagents_subclasses(monkeypatch):
- _configure_llm(monkeypatch)
- persona = {"system_prompt": "persona", "name": "P"}
- with patch("app.agent.agent.HelloAgentsLLM"):
- moderator = ModeratorAgent("topic")
- participant = ParticipantAgent("P", persona, 1, "topic")
- assert isinstance(moderator, SimpleAgent)
- assert isinstance(participant, SimpleAgent)
- assert not hasattr(participant, "_hello_agent")
- def test_end_to_end_discussion_through_real_helloagents_runtime(monkeypatch):
- responses = iter(
- [
- "真实框架开场",
- '{"decision":"APPLY_SPEAK","inner_monologue":"真实框架思考"}',
- "真实框架发言",
- "真实框架总结",
- "真实框架闭幕",
- ]
- )
- class Handler(BaseHTTPRequestHandler):
- def log_message(self, format, *args):
- return
- def do_POST(self):
- size = int(self.headers.get("Content-Length", "0"))
- request = json.loads(self.rfile.read(size))
- content = next(responses)
- if request.get("stream"):
- payload = {
- "id": "chatcmpl-madf",
- "object": "chat.completion.chunk",
- "created": int(time.time()),
- "model": "test-model",
- "choices": [{"index": 0, "delta": {"content": content}, "finish_reason": None}],
- }
- body = f"data: {json.dumps(payload, ensure_ascii=False)}\n\ndata: [DONE]\n\n".encode()
- self.send_response(200)
- self.send_header("Content-Type", "text/event-stream")
- self.send_header("Content-Length", str(len(body)))
- self.end_headers()
- self.wfile.write(body)
- return
- body = json.dumps(
- {
- "id": "chatcmpl-madf",
- "object": "chat.completion",
- "created": int(time.time()),
- "model": "test-model",
- "choices": [
- {
- "index": 0,
- "message": {"role": "assistant", "content": content},
- "finish_reason": "stop",
- }
- ],
- "usage": {"prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2},
- },
- ensure_ascii=False,
- ).encode()
- self.send_response(200)
- self.send_header("Content-Type", "application/json")
- self.send_header("Content-Length", str(len(body)))
- self.end_headers()
- self.wfile.write(body)
- server = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
- thread = threading.Thread(target=server.serve_forever, daemon=True)
- thread.start()
- monkeypatch.setenv("API_KEY", "test-key")
- monkeypatch.setenv("MODEL_NAME", "test-model")
- monkeypatch.setenv("BASE_URL", f"http://127.0.0.1:{server.server_port}/v1")
- try:
- transcript = run_demo("真实 HelloAgents 链路测试")
- finally:
- server.shutdown()
- thread.join(timeout=5)
- assert transcript["opening"] == "真实框架开场"
- assert transcript["thought"]["action"] == "apply_to_speak"
- assert transcript["speech"] == "真实框架发言"
- assert transcript["summary"] == "真实框架总结"
- assert transcript["closing"] == "真实框架闭幕"
|