test_e2e.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. """End-to-end test: load config, call LLM, run a tool, get final answer."""
  2. import asyncio
  3. import io
  4. import sys
  5. from pathlib import Path
  6. # Fix Windows GBK encoding for emoji output
  7. sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
  8. sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace")
  9. async def main():
  10. # 1. Load config
  11. from toyclaw.config import load_config
  12. cfg = load_config()
  13. print(f"[config] model={cfg.model}, api_base={cfg.api_base}")
  14. print(f"[config] workspace={cfg.workspace}")
  15. # 2. Test provider directly — simple chat (no tools)
  16. from toyclaw.provider import OpenAIProvider
  17. provider = OpenAIProvider(
  18. api_key=cfg.api_key,
  19. api_base=cfg.api_base,
  20. default_model=cfg.model,
  21. )
  22. print("\n--- Test 1: Simple chat (no tools) ---")
  23. resp = await provider.chat(
  24. messages=[
  25. {"role": "system", "content": "You are a helpful assistant. Reply concisely."},
  26. {"role": "user", "content": "What is 2+3? Just the number."},
  27. ],
  28. )
  29. print(f"[LLM] content={resp.content!r}")
  30. print(f"[LLM] finish_reason={resp.finish_reason}, tool_calls={len(resp.tool_calls)}")
  31. assert resp.content and "5" in resp.content, f"Expected '5' in response, got: {resp.content}"
  32. print("PASS: Simple chat works!\n")
  33. # 3. Test with tools — LLM should call a tool
  34. print("--- Test 2: Chat with tools (expect tool call) ---")
  35. from toyclaw.tools.base import ToolRegistry
  36. from toyclaw.tools.builtin import ReadFileTool, WriteFileTool, ListDirTool, ShellTool
  37. ws = cfg.workspace
  38. reg = ToolRegistry()
  39. reg.register(ReadFileTool(workspace=ws))
  40. reg.register(WriteFileTool(workspace=ws))
  41. reg.register(ListDirTool(workspace=ws))
  42. reg.register(ShellTool(workspace=ws))
  43. schemas = reg.get_definitions()
  44. print(f"[tools] {len(schemas)} tools registered: {reg.names}")
  45. resp2 = await provider.chat(
  46. messages=[
  47. {"role": "system", "content": "You are an AI assistant with tools. Use them when needed."},
  48. {"role": "user", "content": "Please list the files in the current workspace directory."},
  49. ],
  50. tools=schemas,
  51. )
  52. print(f"[LLM] content={resp2.content!r}")
  53. print(f"[LLM] tool_calls={[(tc.name, tc.arguments) for tc in resp2.tool_calls]}")
  54. if resp2.has_tool_calls:
  55. print("PASS: LLM generated tool calls!")
  56. # Execute the tool call
  57. for tc in resp2.tool_calls:
  58. result = await reg.execute(tc.name, tc.arguments)
  59. print(f"[tool:{tc.name}] result={result[:200]!r}")
  60. else:
  61. print("INFO: LLM responded directly without tool calls (acceptable)")
  62. # 4. Full agent loop test (via agent.py)
  63. print("\n--- Test 3: Full Agent.process() ---")
  64. from toyclaw.agent import Agent
  65. from toyclaw.session import SessionManager
  66. sess_mgr = SessionManager(ws)
  67. agent = Agent(
  68. provider=provider,
  69. tools=reg,
  70. workspace=ws,
  71. session_mgr=sess_mgr,
  72. max_iterations=cfg.max_iterations,
  73. )
  74. # Simple question — no tool needed
  75. answer = await agent.process(
  76. content="Hi! What's the capital of France? One word answer.",
  77. session_key="e2e_test",
  78. )
  79. print(f"[agent] answer={answer!r}")
  80. assert answer and len(answer) > 0, "Agent returned empty answer"
  81. print("PASS: Agent.process() works!\n")
  82. # 5. Agent with tool usage — ask it to create a file
  83. print("--- Test 4: Agent creates a file via tool ---")
  84. answer2 = await agent.process(
  85. content="Create a file called 'e2e_test.txt' in the workspace with content 'Hello from toyclaw!'. Then confirm what you wrote.",
  86. session_key="e2e_test",
  87. )
  88. print(f"[agent] answer={answer2[:300]!r}")
  89. test_file = ws / "e2e_test.txt"
  90. if test_file.exists():
  91. content = test_file.read_text(encoding="utf-8")
  92. print(f"[file] {test_file} content={content!r}")
  93. assert "Hello from toyclaw" in content
  94. print("PASS: Agent successfully created file via tool!")
  95. test_file.unlink() # cleanup
  96. else:
  97. print("WARN: File not created (agent may have used different path)")
  98. print("\n" + "=" * 50)
  99. print("All end-to-end tests completed!")
  100. if __name__ == "__main__":
  101. asyncio.run(main())