10xscale-agentflow-cli 0.3.2.3__tar.gz → 0.3.2.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/10xscale_agentflow_cli.egg-info/PKG-INFO +1 -1
  2. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/10xscale_agentflow_cli.egg-info/SOURCES.txt +5 -1
  3. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/PKG-INFO +1 -1
  4. 10xscale_agentflow_cli-0.3.2.4/agentflow_cli/cli/commands/eval.py +273 -0
  5. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/commands/init.py +1 -1
  6. 10xscale_agentflow_cli-0.3.2.4/agentflow_cli/cli/commands/test.py +87 -0
  7. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/core/config.py +22 -0
  8. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/main.py +99 -0
  9. 10xscale_agentflow_cli-0.3.2.4/agentflow_cli/cli/templates/dev/agentflow.json +12 -0
  10. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/agentflow.json +11 -0
  11. 10xscale_agentflow_cli-0.3.2.4/agentflow_cli/cli/templates/prod/evals/weather_agents_eval.py +41 -0
  12. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/tests/test_agent_eval.py +15 -25
  13. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/SKILL.md +4 -0
  14. 10xscale_agentflow_cli-0.3.2.4/agentflow_cli/cli/templates/skills/agent-skills/references/evaluation.md +500 -0
  15. 10xscale_agentflow_cli-0.3.2.4/agentflow_cli/cli/templates/skills/agent-skills/references/unit-testing.md +202 -0
  16. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/pyproject.toml +1 -1
  17. 10xscale_agentflow_cli-0.3.2.3/agentflow_cli/cli/templates/dev/agentflow.json +0 -6
  18. 10xscale_agentflow_cli-0.3.2.3/agentflow_cli/cli/templates/prod/evals/weather_agents.py +0 -119
  19. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/10xscale_agentflow_cli.egg-info/dependency_links.txt +0 -0
  20. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/10xscale_agentflow_cli.egg-info/entry_points.txt +0 -0
  21. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/10xscale_agentflow_cli.egg-info/not-zip-safe +0 -0
  22. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/10xscale_agentflow_cli.egg-info/requires.txt +0 -0
  23. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/10xscale_agentflow_cli.egg-info/top_level.txt +0 -0
  24. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/MANIFEST.in +0 -0
  25. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/README.md +0 -0
  26. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow.json +0 -0
  27. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/__init__.py +0 -0
  28. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/__init__.py +0 -0
  29. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/commands/__init__.py +0 -0
  30. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/commands/api.py +0 -0
  31. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/commands/build.py +0 -0
  32. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/commands/skills.py +0 -0
  33. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/commands/version.py +0 -0
  34. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/constants.py +0 -0
  35. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/core/__init__.py +0 -0
  36. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/core/output.py +0 -0
  37. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/core/validation.py +0 -0
  38. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/exceptions.py +0 -0
  39. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/logger.py +0 -0
  40. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/__init__.py +0 -0
  41. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/defaults.py +0 -0
  42. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/dev/graph/__init__.py +0 -0
  43. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/dev/graph/agent.py +0 -0
  44. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/.pre-commot-config.yaml +0 -0
  45. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/auth/__init__.py +0 -0
  46. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/auth/agent_auth.py +0 -0
  47. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/evals/__init__.py +0 -0
  48. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/__init__.py +0 -0
  49. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/agent.py +0 -0
  50. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/nodes/__init__.py +0 -0
  51. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/nodes/main_node.py +0 -0
  52. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/nodes/tool_node.py +0 -0
  53. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/state.py +0 -0
  54. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/thread_name_generator.py +0 -0
  55. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/tools/__init__.py +0 -0
  56. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/tools/weather_tool.py +0 -0
  57. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/utils/__init__.py +0 -0
  58. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/utils/tool_decision.py +0 -0
  59. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/validators/__init__.py +0 -0
  60. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/validators/lifecyle.py +0 -0
  61. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/validators/manager.py +0 -0
  62. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/validators/validators.py +0 -0
  63. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/tests/__init__.py +0 -0
  64. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/tests/conftest.py +0 -0
  65. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/tests/test_catalog_tools.py +0 -0
  66. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/tests/test_graph_nodes.py +0 -0
  67. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/agents-and-tools.md +0 -0
  68. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/api-client.md +0 -0
  69. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/api-configuration.md +0 -0
  70. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/api-settings-and-middleware.md +0 -0
  71. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/architecture.md +0 -0
  72. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/auth-and-authorization.md +0 -0
  73. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/callbacks-and-command.md +0 -0
  74. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/checkpointing-and-threads.md +0 -0
  75. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/cli-commands.md +0 -0
  76. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/client-auth-and-errors.md +0 -0
  77. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/client-messages-invoke-stream.md +0 -0
  78. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/client-threads-memory-files.md +0 -0
  79. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/context-id-background.md +0 -0
  80. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/dependency-injection.md +0 -0
  81. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/id-and-thread-name-generators.md +0 -0
  82. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/media-and-files.md +0 -0
  83. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/memory-and-store.md +0 -0
  84. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/prebuilt-agents-and-tools.md +0 -0
  85. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/production-runtime.md +0 -0
  86. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/providers-and-adapters.md +0 -0
  87. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/publishers-and-runtime-protocols.md +0 -0
  88. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/rate-limiting.md +0 -0
  89. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/remote-tools.md +0 -0
  90. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/rest-api-and-errors.md +0 -0
  91. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/security-and-validators.md +0 -0
  92. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/state-and-messages.md +0 -0
  93. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/state-graph.md +0 -0
  94. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/stream-emitter.md +0 -0
  95. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/streaming.md +0 -0
  96. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/testing-and-evaluation.md +0 -0
  97. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/copilot/agentflow.instructions.md +0 -0
  98. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/__init__.py +0 -0
  99. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/__init__.py +0 -0
  100. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/__init__.py +0 -0
  101. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/auth/__init__.py +0 -0
  102. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/auth/auth_backend.py +0 -0
  103. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/auth/authorization.py +0 -0
  104. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/auth/base_auth.py +0 -0
  105. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/auth/jwt_auth.py +0 -0
  106. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/auth/permissions.py +0 -0
  107. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/config/__init__.py +0 -0
  108. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/config/graph_config.py +0 -0
  109. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/config/media_settings.py +0 -0
  110. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/config/sentry_config.py +0 -0
  111. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/config/settings.py +0 -0
  112. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/config/setup_logs.py +0 -0
  113. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/config/setup_middleware.py +0 -0
  114. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/config/worker_middleware.py +0 -0
  115. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/exceptions/__init__.py +0 -0
  116. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/exceptions/general_exception.py +0 -0
  117. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/exceptions/handle_errors.py +0 -0
  118. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/exceptions/resources_exceptions.py +0 -0
  119. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/exceptions/user_exception.py +0 -0
  120. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/middleware/__init__.py +0 -0
  121. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/middleware/rate_limit/__init__.py +0 -0
  122. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/middleware/rate_limit/base.py +0 -0
  123. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/middleware/rate_limit/factory.py +0 -0
  124. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/middleware/rate_limit/memory.py +0 -0
  125. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/middleware/rate_limit/middleware.py +0 -0
  126. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/middleware/rate_limit/redis.py +0 -0
  127. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/middleware/request_limits.py +0 -0
  128. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/middleware/security_headers.py +0 -0
  129. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/utils/__init__.py +0 -0
  130. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/utils/log_sanitizer.py +0 -0
  131. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/loader.py +0 -0
  132. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/main.py +0 -0
  133. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/__init__.py +0 -0
  134. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/a2a.py +0 -0
  135. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/a2ui.py +0 -0
  136. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/checkpointer/__init__.py +0 -0
  137. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/checkpointer/router.py +0 -0
  138. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/checkpointer/schemas/__init__.py +0 -0
  139. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/checkpointer/schemas/checkpointer_schemas.py +0 -0
  140. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/checkpointer/services/__init__.py +0 -0
  141. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/checkpointer/services/checkpointer_service.py +0 -0
  142. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/graph/__init__.py +0 -0
  143. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/graph/router.py +0 -0
  144. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/graph/schemas/__init__.py +0 -0
  145. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/graph/schemas/graph_schemas.py +0 -0
  146. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/graph/services/__init__.py +0 -0
  147. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/graph/services/graph_service.py +0 -0
  148. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/graph/services/multimodal_preprocessor.py +0 -0
  149. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/media/__init__.py +0 -0
  150. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/media/router.py +0 -0
  151. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/media/schemas.py +0 -0
  152. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/ping/__init__.py +0 -0
  153. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/ping/router.py +0 -0
  154. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/setup_router.py +0 -0
  155. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/store/__init__.py +0 -0
  156. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/store/router.py +0 -0
  157. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/store/schemas/__init__.py +0 -0
  158. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/store/schemas/store_schemas.py +0 -0
  159. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/store/services/__init__.py +0 -0
  160. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/store/services/store_service.py +0 -0
  161. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/tasks/__init__.py +0 -0
  162. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/tasks/user_tasks.py +0 -0
  163. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/__init__.py +0 -0
  164. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/callable_helper.py +0 -0
  165. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/media/__init__.py +0 -0
  166. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/media/extractor.py +0 -0
  167. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/media/pipeline.py +0 -0
  168. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/parse_output.py +0 -0
  169. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/response_helper.py +0 -0
  170. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/schemas/__init__.py +0 -0
  171. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/schemas/output_schemas.py +0 -0
  172. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/schemas/user_schemas.py +0 -0
  173. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/snowflake_id_generator.py +0 -0
  174. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/swagger_helper.py +0 -0
  175. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/thread_name_generator.py +0 -0
  176. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/worker.py +0 -0
  177. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/graph/__init__.py +0 -0
  178. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/graph/react.py +0 -0
  179. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/requirements.txt +0 -0
  180. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/setup.cfg +0 -0
  181. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/tests/test_multimodal_sprint2_extraction.py +0 -0
  182. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/tests/test_sprint4_media_api.py +0 -0
  183. {10xscale_agentflow_cli-0.3.2.3 → 10xscale_agentflow_cli-0.3.2.4}/tests/test_utils_parse_and_callable.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: 10xscale-agentflow-cli
3
- Version: 0.3.2.3
3
+ Version: 0.3.2.4
4
4
  Summary: CLI and API for 10xscale AgentFlow
5
5
  Author-email: 10xscale <contact@10xscale.ai>
6
6
  Maintainer-email: Shudipto Trafder <shudiptotrafder@gmail.com>
@@ -19,8 +19,10 @@ agentflow_cli/cli/main.py
19
19
  agentflow_cli/cli/commands/__init__.py
20
20
  agentflow_cli/cli/commands/api.py
21
21
  agentflow_cli/cli/commands/build.py
22
+ agentflow_cli/cli/commands/eval.py
22
23
  agentflow_cli/cli/commands/init.py
23
24
  agentflow_cli/cli/commands/skills.py
25
+ agentflow_cli/cli/commands/test.py
24
26
  agentflow_cli/cli/commands/version.py
25
27
  agentflow_cli/cli/core/__init__.py
26
28
  agentflow_cli/cli/core/config.py
@@ -36,7 +38,7 @@ agentflow_cli/cli/templates/prod/agentflow.json
36
38
  agentflow_cli/cli/templates/prod/auth/__init__.py
37
39
  agentflow_cli/cli/templates/prod/auth/agent_auth.py
38
40
  agentflow_cli/cli/templates/prod/evals/__init__.py
39
- agentflow_cli/cli/templates/prod/evals/weather_agents.py
41
+ agentflow_cli/cli/templates/prod/evals/weather_agents_eval.py
40
42
  agentflow_cli/cli/templates/prod/graph/__init__.py
41
43
  agentflow_cli/cli/templates/prod/graph/agent.py
42
44
  agentflow_cli/cli/templates/prod/graph/state.py
@@ -72,6 +74,7 @@ agentflow_cli/cli/templates/skills/agent-skills/references/client-messages-invok
72
74
  agentflow_cli/cli/templates/skills/agent-skills/references/client-threads-memory-files.md
73
75
  agentflow_cli/cli/templates/skills/agent-skills/references/context-id-background.md
74
76
  agentflow_cli/cli/templates/skills/agent-skills/references/dependency-injection.md
77
+ agentflow_cli/cli/templates/skills/agent-skills/references/evaluation.md
75
78
  agentflow_cli/cli/templates/skills/agent-skills/references/id-and-thread-name-generators.md
76
79
  agentflow_cli/cli/templates/skills/agent-skills/references/media-and-files.md
77
80
  agentflow_cli/cli/templates/skills/agent-skills/references/memory-and-store.md
@@ -88,6 +91,7 @@ agentflow_cli/cli/templates/skills/agent-skills/references/state-graph.md
88
91
  agentflow_cli/cli/templates/skills/agent-skills/references/stream-emitter.md
89
92
  agentflow_cli/cli/templates/skills/agent-skills/references/streaming.md
90
93
  agentflow_cli/cli/templates/skills/agent-skills/references/testing-and-evaluation.md
94
+ agentflow_cli/cli/templates/skills/agent-skills/references/unit-testing.md
91
95
  agentflow_cli/cli/templates/skills/copilot/agentflow.instructions.md
92
96
  agentflow_cli/src/__init__.py
93
97
  agentflow_cli/src/app/__init__.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: 10xscale-agentflow-cli
3
- Version: 0.3.2.3
3
+ Version: 0.3.2.4
4
4
  Summary: CLI and API for 10xscale AgentFlow
5
5
  Author-email: 10xscale <contact@10xscale.ai>
6
6
  Maintainer-email: Shudipto Trafder <shudiptotrafder@gmail.com>
@@ -0,0 +1,273 @@
1
+ """Eval command — discover and run agentflow evaluations, always generating reports."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import importlib
7
+ import importlib.util
8
+ import inspect
9
+ import sys
10
+ import webbrowser
11
+ from pathlib import Path
12
+ from typing import TYPE_CHECKING, Any
13
+
14
+ from agentflow.qa.evaluation import CriterionConfig, EvalConfig, MatchType
15
+ from agentflow.qa.evaluation.collectors.trajectory_collector import TrajectoryCollector
16
+ from agentflow.qa.evaluation.config.eval_config import ReporterConfig
17
+ from agentflow.qa.evaluation.eval_result import EvalReport as ER
18
+ from agentflow.qa.evaluation.evaluator import AgentEvaluator
19
+ from agentflow.qa.evaluation.reporters.manager import ReporterManager
20
+
21
+ from agentflow_cli.cli.commands import BaseCommand
22
+ from agentflow_cli.cli.core.config import ConfigManager
23
+
24
+
25
+ if TYPE_CHECKING:
26
+ from agentflow.qa.evaluation.eval_result import EvalReport
27
+
28
+
29
+ class EvalCommand(BaseCommand):
30
+ """Discover and run agent evaluations; always write HTML + JSON reports."""
31
+
32
+ # ------------------------------------------------------------------
33
+ # Discovery
34
+ # ------------------------------------------------------------------
35
+
36
+ def _discover(self, target: Path) -> list[Path]:
37
+ """Return eval files under target. If target is a file, return it directly."""
38
+ if target.is_file():
39
+ return [target]
40
+
41
+ seen: dict[Path, None] = {}
42
+ for pattern in ("*_eval.py", "eval_*.py"):
43
+ for p in sorted(target.rglob(pattern)):
44
+ seen[p] = None
45
+ return list(seen)
46
+
47
+ # ------------------------------------------------------------------
48
+ # Module loading
49
+ # ------------------------------------------------------------------
50
+
51
+ def _load_module(self, path: Path) -> Any:
52
+ project_root = str(Path.cwd())
53
+ if project_root not in sys.path:
54
+ sys.path.insert(0, project_root)
55
+
56
+ spec = importlib.util.spec_from_file_location("_agentflow_eval", path)
57
+ mod = importlib.util.module_from_spec(spec) # type: ignore[arg-type]
58
+ spec.loader.exec_module(mod) # type: ignore[union-attr]
59
+ return mod
60
+
61
+ # ------------------------------------------------------------------
62
+ # Agent loading from agentflow.json
63
+ # ------------------------------------------------------------------
64
+
65
+ def _load_agent_from_config(self) -> Any:
66
+ config_manager = ConfigManager()
67
+ discovered = config_manager.auto_discover_config()
68
+ if not discovered:
69
+ raise RuntimeError("No agentflow.json found — cannot auto-load agent.")
70
+ config_manager.load_config(str(discovered))
71
+ agent_spec: str = config_manager.get_config_value("agent", default="")
72
+ if not agent_spec or ":" not in agent_spec:
73
+ raise RuntimeError(f"Invalid 'agent' field in agentflow.json: {agent_spec!r}")
74
+ module_path, attr = agent_spec.rsplit(":", 1)
75
+ mod = importlib.import_module(module_path)
76
+ return getattr(mod, attr)
77
+
78
+ # ------------------------------------------------------------------
79
+ # Per-file runner (sync wrapper — avoids nested asyncio.run() issues)
80
+ # ------------------------------------------------------------------
81
+
82
+ def _default_config(self) -> Any:
83
+ return EvalConfig(
84
+ criteria={
85
+ "response_match": CriterionConfig(threshold=0.6, match_type=MatchType.ANY_ORDER),
86
+ "tool_name_match_score": CriterionConfig(
87
+ threshold=1.0, match_type=MatchType.EXACT, check_args=False
88
+ ),
89
+ "node_order": CriterionConfig(threshold=0.8, match_type=MatchType.IN_ORDER),
90
+ },
91
+ )
92
+
93
+ def _run_file_sync(self, path: Path) -> EvalReport | None:
94
+ """Load and run a single eval file. Returns EvalReport or None if skipped."""
95
+ self.output.info(f"Running: {path.name}", emoji=False)
96
+ mod = self._load_module(path)
97
+
98
+ # Primary protocol: run() — full control when the standard pipeline isn't enough.
99
+ # Must return an EvalReport.
100
+ if hasattr(mod, "run"):
101
+ result = mod.run()
102
+ if inspect.isawaitable(result):
103
+ return asyncio.run(result) # type: ignore[arg-type]
104
+ return result # type: ignore[return-value]
105
+
106
+ # Fallback protocol: get_eval_set() — CLI handles graph loading, evaluation, and reports.
107
+ # Config is optional; omit it and the CLI default is used.
108
+ if hasattr(mod, "get_eval_set"):
109
+ if hasattr(mod, "get_eval_config"):
110
+ config = mod.get_eval_config()
111
+ elif hasattr(mod, "EVAL_CONFIG"):
112
+ config = mod.EVAL_CONFIG
113
+ else:
114
+ config = self._default_config()
115
+ return self._run_with_evaluator(mod, mod.get_eval_set(), config)
116
+
117
+ self.output.warning(f"Skipping {path.name} — no get_eval_set() or run() found.")
118
+ return None
119
+
120
+ def _run_with_evaluator(self, mod: Any, eval_set: Any, config: Any) -> EvalReport:
121
+ # Prefer the graph already imported in the module (most common pattern)
122
+ graph = getattr(mod, "app", None) or self._load_agent_from_config()
123
+ collector = TrajectoryCollector(capture_all_events=True)
124
+ evaluator = AgentEvaluator(graph, collector, config=config)
125
+ return asyncio.run(evaluator.evaluate(eval_set))
126
+
127
+ # ------------------------------------------------------------------
128
+ # Report merging
129
+ # ------------------------------------------------------------------
130
+
131
+ def _merge_reports(self, reports: list[EvalReport]) -> EvalReport:
132
+ if len(reports) == 1:
133
+ return reports[0]
134
+
135
+ all_results = []
136
+ for r in reports:
137
+ all_results.extend(r.results)
138
+ return ER.create(
139
+ eval_set_id="combined_eval",
140
+ eval_set_name="Combined Evaluation",
141
+ results=all_results,
142
+ )
143
+
144
+ # ------------------------------------------------------------------
145
+ # Eval directory from agentflow.json
146
+ # ------------------------------------------------------------------
147
+
148
+ def _resolve_eval_dir(self) -> Path:
149
+ config_manager = ConfigManager()
150
+ discovered = config_manager.auto_discover_config()
151
+ directory = "evals"
152
+ if discovered:
153
+ try:
154
+ config_manager.load_config(str(discovered))
155
+ eval_cfg = config_manager.get_evaluation_config()
156
+ directory = eval_cfg.get("directory", "evals")
157
+ except Exception:
158
+ self.logger.warning(
159
+ "Failed to load eval directory from config; using default 'evals/'"
160
+ )
161
+ return Path.cwd() / directory
162
+
163
+ # ------------------------------------------------------------------
164
+ # Main entry point
165
+ # ------------------------------------------------------------------
166
+
167
+ def execute( # noqa: PLR0912, PLR0915
168
+ self,
169
+ target: str | None = None,
170
+ output_dir: str = "eval_reports",
171
+ no_report: bool = False,
172
+ threshold: float | None = None,
173
+ open_report: bool = False,
174
+ verbose: bool = False,
175
+ quiet: bool = False,
176
+ **kwargs: Any,
177
+ ) -> int:
178
+ # Load optional overrides from agentflow.json
179
+ config_manager = ConfigManager()
180
+ discovered = config_manager.auto_discover_config()
181
+ if discovered:
182
+ try:
183
+ config_manager.load_config(str(discovered))
184
+ eval_cfg = config_manager.get_evaluation_config()
185
+ if output_dir == "eval_reports":
186
+ output_dir = eval_cfg.get("output_dir", output_dir)
187
+ if threshold is None:
188
+ threshold = eval_cfg.get("threshold")
189
+ except Exception:
190
+ self.logger.warning(
191
+ "Failed to load eval config from agentflow.json; using defaults"
192
+ )
193
+
194
+ # Resolve target path
195
+ if target:
196
+ target_path = Path(target)
197
+ if not target_path.exists():
198
+ self.output.error(f"Path not found: {target}")
199
+ return 1
200
+ else:
201
+ target_path = self._resolve_eval_dir()
202
+ if not target_path.exists():
203
+ self.output.error(
204
+ f"Eval directory '{target_path}' not found. "
205
+ "Create an evals/ directory or pass a file/folder path."
206
+ )
207
+ return 1
208
+
209
+ files = self._discover(target_path)
210
+ if not files:
211
+ self.output.error(f"No eval files found in {target_path}")
212
+ return 1
213
+
214
+ self.output.print_banner("Eval", f"Found {len(files)} eval file(s) in {target_path}")
215
+
216
+ # Run each file
217
+ reports: list[EvalReport] = []
218
+ for f in files:
219
+ try:
220
+ report = self._run_file_sync(f)
221
+ if report is not None:
222
+ reports.append(report)
223
+ except Exception as exc:
224
+ self.output.error(f"Error in {f.name}: {exc}")
225
+ self.logger.exception("Eval file failed: %s", f)
226
+
227
+ if not reports:
228
+ self.output.error("No reports produced. Ensure eval files expose run().")
229
+ return 1
230
+
231
+ merged = self._merge_reports(reports)
232
+
233
+ # Determine exit code
234
+ if threshold is not None and merged.summary.pass_rate < threshold:
235
+ self.output.error(
236
+ f"Pass rate {merged.summary.pass_rate:.1%} is below threshold {threshold:.1%}"
237
+ )
238
+ return_code = 1
239
+ else:
240
+ return_code = 0 if merged.summary.pass_rate == 1.0 else 1
241
+
242
+ # Always generate file reports unless --no-report
243
+ if not no_report:
244
+ manager = ReporterManager(
245
+ ReporterConfig(
246
+ output_dir=output_dir,
247
+ html=True,
248
+ json_report=True,
249
+ console=False, # console output already handled by run() modules
250
+ timestamp_files=True,
251
+ )
252
+ )
253
+ result = manager.run_all(merged)
254
+
255
+ if result.html_path:
256
+ self.output.success(f"HTML report: {result.html_path}")
257
+ if result.json_path:
258
+ self.output.info(f"JSON report: {result.json_path}", emoji=False)
259
+ if result.has_errors:
260
+ for name, err in result.errors:
261
+ self.output.warning(f"Reporter error [{name}]: {err}")
262
+
263
+ if open_report and result.html_path:
264
+ webbrowser.open(Path(result.html_path).as_uri())
265
+
266
+ summary = merged.summary
267
+ self.output.info(
268
+ f"Results: {summary.passed_cases}/{summary.total_cases} passed "
269
+ f"({summary.pass_rate:.1%})",
270
+ emoji=False,
271
+ )
272
+
273
+ return return_code
@@ -16,7 +16,7 @@ from agentflow_cli.cli.exceptions import FileOperationError
16
16
 
17
17
 
18
18
  _TEMPLATES_DIR = Path(__file__).parent.parent / "templates"
19
- _SKIP_DIRS = {"__pycache__"}
19
+ _SKIP_DIRS = {"__pycache__", ".ruff_cache"}
20
20
  _DIVIDER = Colors.colorize(" " + "─" * 46, "cyan")
21
21
 
22
22
  # Directories inside prod/ that are only included based on user choices
@@ -0,0 +1,87 @@
1
+ """Test command implementation — thin pytest wrapper."""
2
+
3
+ import subprocess # nosec: B404
4
+ import sys
5
+ import webbrowser
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from agentflow_cli.cli.commands import BaseCommand
10
+ from agentflow_cli.cli.core.config import ConfigManager
11
+
12
+
13
+ class TestCommand(BaseCommand):
14
+ """Run the project's test suite via pytest."""
15
+
16
+ def execute(
17
+ self,
18
+ path: str | None = None,
19
+ coverage: bool = False,
20
+ html: bool = False,
21
+ keyword: str | None = None,
22
+ verbose: bool = False,
23
+ quiet: bool = False,
24
+ extra_args: tuple[str, ...] = (),
25
+ **kwargs: Any,
26
+ ) -> int:
27
+ project_root = Path.cwd()
28
+
29
+ # Load optional overrides from agentflow.json
30
+ cfg: dict[str, Any] = {}
31
+ config_manager = ConfigManager()
32
+ discovered = config_manager.auto_discover_config()
33
+ if discovered:
34
+ try:
35
+ config_manager.load_config(str(discovered))
36
+ cfg = config_manager.get_test_config()
37
+ except Exception: # nosec: B110
38
+ self.logger.warning("Failed to load test configuration from %s", discovered)
39
+
40
+ # Explicit CLI path wins; fall back to agentflow.json; None = pytest auto-discovery
41
+ resolved_path: str | None = path or cfg.get("path") or None
42
+ resolved_coverage = coverage or cfg.get("coverage", False)
43
+ coverage_threshold: int | None = cfg.get("coverage_threshold")
44
+
45
+ location = str(project_root / resolved_path) if resolved_path else str(project_root)
46
+ self.output.print_banner("Test", f"Running tests in {location}")
47
+
48
+ cmd = [sys.executable, "-m", "pytest"]
49
+ if resolved_path:
50
+ cmd.append(resolved_path)
51
+
52
+ if verbose:
53
+ cmd.append("-v")
54
+ elif quiet:
55
+ cmd.append("-q")
56
+ else:
57
+ cmd.append("-v")
58
+
59
+ if resolved_coverage:
60
+ cmd += [
61
+ "--cov=.",
62
+ "--cov-report=term-missing",
63
+ "--cov-report=html:htmlcov",
64
+ ]
65
+ if coverage_threshold is not None:
66
+ cmd.append(f"--cov-fail-under={coverage_threshold}")
67
+
68
+ if keyword:
69
+ cmd += ["-k", keyword]
70
+
71
+ cmd += list(extra_args)
72
+
73
+ self.logger.info("Running: %s", " ".join(cmd))
74
+
75
+ result = subprocess.run(cmd, cwd=project_root, check=False) # nosec: B603 # noqa: S603
76
+
77
+ if result.returncode == 0:
78
+ self.output.success("All tests passed.")
79
+ else:
80
+ self.output.error(f"Tests finished with exit code {result.returncode}.")
81
+
82
+ if html and resolved_coverage:
83
+ report_path = (project_root / "htmlcov" / "index.html").as_uri()
84
+ self.output.info(f"Opening coverage report: {report_path}", emoji=False)
85
+ webbrowser.open(report_path)
86
+
87
+ return result.returncode
@@ -214,6 +214,28 @@ class ConfigManager:
214
214
 
215
215
  return value
216
216
 
217
+ def get_test_config(self) -> dict[str, Any]:
218
+ """Return the optional 'test' section from agentflow.json.
219
+
220
+ Returns a dict with keys: path, coverage, coverage_threshold.
221
+ All fields are optional; callers should use .get() with their own defaults.
222
+ """
223
+ raw = self.get_config_value("test", default={})
224
+ if not isinstance(raw, dict):
225
+ return {}
226
+ return raw
227
+
228
+ def get_evaluation_config(self) -> dict[str, Any]:
229
+ """Return the optional 'evaluation' section from agentflow.json.
230
+
231
+ Returns a dict with keys: directory, output_dir, threshold, timestamp_files.
232
+ All fields are optional; callers should use .get() with their own defaults.
233
+ """
234
+ raw = self.get_config_value("evaluation", default={})
235
+ if not isinstance(raw, dict):
236
+ return {}
237
+ return raw
238
+
217
239
  def resolve_env_file(self) -> Path | None:
218
240
  """Resolve environment file path from configuration.
219
241
 
@@ -7,8 +7,10 @@ from dotenv import load_dotenv
7
7
 
8
8
  from agentflow_cli.cli.commands.api import APICommand
9
9
  from agentflow_cli.cli.commands.build import BuildCommand
10
+ from agentflow_cli.cli.commands.eval import EvalCommand
10
11
  from agentflow_cli.cli.commands.init import InitCommand
11
12
  from agentflow_cli.cli.commands.skills import SkillsCommand
13
+ from agentflow_cli.cli.commands.test import TestCommand
12
14
  from agentflow_cli.cli.commands.version import VersionCommand
13
15
  from agentflow_cli.cli.constants import (
14
16
  DEFAULT_CONFIG_FILE,
@@ -362,6 +364,103 @@ def skills(
362
364
  sys.exit(handle_exception(e))
363
365
 
364
366
 
367
+ @app.command(
368
+ context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
369
+ )
370
+ def test(
371
+ ctx: typer.Context,
372
+ path: str | None = typer.Argument(
373
+ None, help="Path to tests directory or file (omit to let pytest auto-discover)"
374
+ ),
375
+ coverage: bool = typer.Option(False, "--coverage", "-C", help="Run with coverage"),
376
+ html: bool = typer.Option(
377
+ False, "--html", help="Open HTML coverage report after run (requires --coverage)"
378
+ ),
379
+ keyword: str | None = typer.Option(None, "-k", help="Only run tests matching this expression"),
380
+ verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose output"),
381
+ quiet: bool = typer.Option(False, "--quiet", "-q", help="Suppress all output except errors"),
382
+ ) -> None:
383
+ """Run project tests with pytest.
384
+
385
+ Any arguments after -- are forwarded verbatim to pytest.
386
+ """
387
+ setup_cli_logging(verbose=verbose, quiet=quiet)
388
+
389
+ try:
390
+ command = TestCommand(output)
391
+ exit_code = command.execute(
392
+ path=path,
393
+ coverage=coverage,
394
+ html=html,
395
+ keyword=keyword,
396
+ verbose=verbose,
397
+ quiet=quiet,
398
+ extra_args=tuple(ctx.args),
399
+ )
400
+ sys.exit(exit_code)
401
+ except Exception as e:
402
+ sys.exit(handle_exception(e))
403
+
404
+
405
+ @app.command(name="eval")
406
+ def eval_cmd(
407
+ target: str | None = typer.Argument(
408
+ None,
409
+ help="File or directory to evaluate (default: evals/ from agentflow.json or cwd)",
410
+ ),
411
+ output_dir: str = typer.Option(
412
+ "eval_reports",
413
+ "--output",
414
+ "-o",
415
+ help="Directory for generated report files",
416
+ ),
417
+ no_report: bool = typer.Option(
418
+ False,
419
+ "--no-report",
420
+ help="Skip file report generation (console summary only)",
421
+ ),
422
+ threshold: float | None = typer.Option(
423
+ None,
424
+ "--threshold",
425
+ "-t",
426
+ help="Fail if overall pass rate is below this value (0.0-1.0)",
427
+ ),
428
+ open_report: bool = typer.Option(
429
+ False,
430
+ "--open",
431
+ help="Open the HTML report in the default browser after the run",
432
+ ),
433
+ verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose output"),
434
+ quiet: bool = typer.Option(False, "--quiet", "-q", help="Suppress all output except errors"),
435
+ ) -> None:
436
+ """Run agent evaluations.
437
+
438
+ Discovers *_eval.py / eval_*.py files in the target directory (default: evals/).
439
+ Always generates HTML + JSON reports in eval_reports/ unless --no-report is set.
440
+
441
+ Each eval file must expose one of:
442
+ run() # full control, returns EvalReport
443
+ get_eval_set() + get_eval_config() # CLI loads agent from agentflow.json
444
+ EVAL_CONFIG + get_eval_set() # same, config as a constant
445
+ """
446
+ setup_cli_logging(verbose=verbose, quiet=quiet)
447
+
448
+ try:
449
+ command = EvalCommand(output)
450
+ exit_code = command.execute(
451
+ target=target,
452
+ output_dir=output_dir,
453
+ no_report=no_report,
454
+ threshold=threshold,
455
+ open_report=open_report,
456
+ verbose=verbose,
457
+ quiet=quiet,
458
+ )
459
+ sys.exit(exit_code)
460
+ except Exception as e:
461
+ sys.exit(handle_exception(e))
462
+
463
+
365
464
  def main() -> None:
366
465
  """Main CLI entry point."""
367
466
  try:
@@ -0,0 +1,12 @@
1
+ {
2
+ "agent": "graph.agent:app",
3
+ "env": ".env",
4
+ "auth": null,
5
+ "thread_name_generator": null,
6
+ "evaluation": {
7
+ "directory": "evals",
8
+ "output_dir": "eval_reports",
9
+ "threshold": 0.75,
10
+ "timestamp_files": true
11
+ }
12
+ }
@@ -15,5 +15,16 @@
15
15
  "by": "ip",
16
16
  "trusted_proxy_headers": false,
17
17
  "exclude_paths": ["/health", "/docs", "/redoc", "/openapi.json"]
18
+ },
19
+ "test": {
20
+ "path": "tests",
21
+ "coverage": true,
22
+ "coverage_threshold": 70
23
+ },
24
+ "evaluation": {
25
+ "directory": "evals",
26
+ "output_dir": "eval_reports",
27
+ "threshold": 0.75,
28
+ "timestamp_files": true
18
29
  }
19
30
  }
@@ -0,0 +1,41 @@
1
+ from agentflow.qa.evaluation import EvalConfig, EvalSet, EvalSetBuilder
2
+
3
+
4
+ def get_eval_config() -> EvalConfig:
5
+ """Return the default eval configuration for CLI auto-discovery."""
6
+ return EvalConfig()
7
+
8
+
9
+ def get_eval_set() -> EvalSet:
10
+ return (
11
+ EvalSetBuilder(name="weather-agent-regression")
12
+ .add_case(
13
+ query="Hi",
14
+ expected="assistant",
15
+ expected_node_order=["MAIN"],
16
+ name="greeting_response",
17
+ description="The agent responds to a greeting.",
18
+ )
19
+ .add_tool_test(
20
+ query="What is the weather in London?",
21
+ tool_name="get_weather",
22
+ tool_args={"location": "London"},
23
+ expected_response="London",
24
+ case_id="weather_london",
25
+ )
26
+ .add_tool_test(
27
+ query="Tell me the current weather in New York",
28
+ tool_name="get_weather",
29
+ tool_args={"location": "New York"},
30
+ expected_response="New York",
31
+ case_id="weather_new_york",
32
+ )
33
+ .add_tool_test(
34
+ query="How is the weather in Tokyo today?",
35
+ tool_name="get_weather",
36
+ tool_args={"location": "Tokyo"},
37
+ expected_response="Tokyo",
38
+ case_id="weather_tokyo",
39
+ )
40
+ .build()
41
+ )