10xscale-agentflow-cli 0.3.2.4__tar.gz → 0.3.2.5__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.4 → 10xscale_agentflow_cli-0.3.2.5}/10xscale_agentflow_cli.egg-info/PKG-INFO +1 -1
  2. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/10xscale_agentflow_cli.egg-info/SOURCES.txt +1 -0
  3. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/PKG-INFO +1 -1
  4. 10xscale_agentflow_cli-0.3.2.5/agentflow_cli/cli/commands/eval.py +622 -0
  5. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/main.py +20 -4
  6. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/agentflow.json +2 -0
  7. 10xscale_agentflow_cli-0.3.2.5/agentflow_cli/cli/templates/prod/evals/user_simulator_eval.py +76 -0
  8. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/evals/weather_agents_eval.py +14 -2
  9. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/pyproject.toml +1 -1
  10. 10xscale_agentflow_cli-0.3.2.4/agentflow_cli/cli/commands/eval.py +0 -273
  11. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/10xscale_agentflow_cli.egg-info/dependency_links.txt +0 -0
  12. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/10xscale_agentflow_cli.egg-info/entry_points.txt +0 -0
  13. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/10xscale_agentflow_cli.egg-info/not-zip-safe +0 -0
  14. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/10xscale_agentflow_cli.egg-info/requires.txt +0 -0
  15. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/10xscale_agentflow_cli.egg-info/top_level.txt +0 -0
  16. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/MANIFEST.in +0 -0
  17. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/README.md +0 -0
  18. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow.json +0 -0
  19. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/__init__.py +0 -0
  20. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/__init__.py +0 -0
  21. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/commands/__init__.py +0 -0
  22. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/commands/api.py +0 -0
  23. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/commands/build.py +0 -0
  24. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/commands/init.py +0 -0
  25. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/commands/skills.py +0 -0
  26. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/commands/test.py +0 -0
  27. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/commands/version.py +0 -0
  28. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/constants.py +0 -0
  29. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/core/__init__.py +0 -0
  30. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/core/config.py +0 -0
  31. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/core/output.py +0 -0
  32. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/core/validation.py +0 -0
  33. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/exceptions.py +0 -0
  34. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/logger.py +0 -0
  35. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/__init__.py +0 -0
  36. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/defaults.py +0 -0
  37. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/dev/agentflow.json +0 -0
  38. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/dev/graph/__init__.py +0 -0
  39. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/dev/graph/agent.py +0 -0
  40. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/.pre-commot-config.yaml +0 -0
  41. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/auth/__init__.py +0 -0
  42. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/auth/agent_auth.py +0 -0
  43. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/evals/__init__.py +0 -0
  44. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/graph/__init__.py +0 -0
  45. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/graph/agent.py +0 -0
  46. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/graph/nodes/__init__.py +0 -0
  47. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/graph/nodes/main_node.py +0 -0
  48. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/graph/nodes/tool_node.py +0 -0
  49. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/graph/state.py +0 -0
  50. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/graph/thread_name_generator.py +0 -0
  51. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/graph/tools/__init__.py +0 -0
  52. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/graph/tools/weather_tool.py +0 -0
  53. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/graph/utils/__init__.py +0 -0
  54. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/graph/utils/tool_decision.py +0 -0
  55. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/graph/validators/__init__.py +0 -0
  56. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/graph/validators/lifecyle.py +0 -0
  57. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/graph/validators/manager.py +0 -0
  58. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/graph/validators/validators.py +0 -0
  59. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/tests/__init__.py +0 -0
  60. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/tests/conftest.py +0 -0
  61. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/tests/test_agent_eval.py +0 -0
  62. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/tests/test_catalog_tools.py +0 -0
  63. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/prod/tests/test_graph_nodes.py +0 -0
  64. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/SKILL.md +0 -0
  65. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/agents-and-tools.md +0 -0
  66. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/api-client.md +0 -0
  67. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/api-configuration.md +0 -0
  68. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/api-settings-and-middleware.md +0 -0
  69. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/architecture.md +0 -0
  70. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/auth-and-authorization.md +0 -0
  71. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/callbacks-and-command.md +0 -0
  72. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/checkpointing-and-threads.md +0 -0
  73. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/cli-commands.md +0 -0
  74. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/client-auth-and-errors.md +0 -0
  75. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/client-messages-invoke-stream.md +0 -0
  76. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/client-threads-memory-files.md +0 -0
  77. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/context-id-background.md +0 -0
  78. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/dependency-injection.md +0 -0
  79. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/evaluation.md +0 -0
  80. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/id-and-thread-name-generators.md +0 -0
  81. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/media-and-files.md +0 -0
  82. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/memory-and-store.md +0 -0
  83. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/prebuilt-agents-and-tools.md +0 -0
  84. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/production-runtime.md +0 -0
  85. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/providers-and-adapters.md +0 -0
  86. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/publishers-and-runtime-protocols.md +0 -0
  87. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/rate-limiting.md +0 -0
  88. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/remote-tools.md +0 -0
  89. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/rest-api-and-errors.md +0 -0
  90. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/security-and-validators.md +0 -0
  91. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/state-and-messages.md +0 -0
  92. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/state-graph.md +0 -0
  93. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/stream-emitter.md +0 -0
  94. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/streaming.md +0 -0
  95. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/testing-and-evaluation.md +0 -0
  96. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/agent-skills/references/unit-testing.md +0 -0
  97. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/cli/templates/skills/copilot/agentflow.instructions.md +0 -0
  98. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/__init__.py +0 -0
  99. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/__init__.py +0 -0
  100. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/__init__.py +0 -0
  101. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/auth/__init__.py +0 -0
  102. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/auth/auth_backend.py +0 -0
  103. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/auth/authorization.py +0 -0
  104. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/auth/base_auth.py +0 -0
  105. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/auth/jwt_auth.py +0 -0
  106. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/auth/permissions.py +0 -0
  107. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/config/__init__.py +0 -0
  108. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/config/graph_config.py +0 -0
  109. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/config/media_settings.py +0 -0
  110. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/config/sentry_config.py +0 -0
  111. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/config/settings.py +0 -0
  112. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/config/setup_logs.py +0 -0
  113. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/config/setup_middleware.py +0 -0
  114. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/config/worker_middleware.py +0 -0
  115. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/exceptions/__init__.py +0 -0
  116. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/exceptions/general_exception.py +0 -0
  117. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/exceptions/handle_errors.py +0 -0
  118. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/exceptions/resources_exceptions.py +0 -0
  119. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/exceptions/user_exception.py +0 -0
  120. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/middleware/__init__.py +0 -0
  121. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/middleware/rate_limit/__init__.py +0 -0
  122. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/middleware/rate_limit/base.py +0 -0
  123. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/middleware/rate_limit/factory.py +0 -0
  124. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/middleware/rate_limit/memory.py +0 -0
  125. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/middleware/rate_limit/middleware.py +0 -0
  126. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/middleware/rate_limit/redis.py +0 -0
  127. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/middleware/request_limits.py +0 -0
  128. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/middleware/security_headers.py +0 -0
  129. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/utils/__init__.py +0 -0
  130. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/core/utils/log_sanitizer.py +0 -0
  131. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/loader.py +0 -0
  132. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/main.py +0 -0
  133. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/__init__.py +0 -0
  134. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/a2a.py +0 -0
  135. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/a2ui.py +0 -0
  136. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/checkpointer/__init__.py +0 -0
  137. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/checkpointer/router.py +0 -0
  138. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/checkpointer/schemas/__init__.py +0 -0
  139. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/checkpointer/schemas/checkpointer_schemas.py +0 -0
  140. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/checkpointer/services/__init__.py +0 -0
  141. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/checkpointer/services/checkpointer_service.py +0 -0
  142. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/graph/__init__.py +0 -0
  143. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/graph/router.py +0 -0
  144. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/graph/schemas/__init__.py +0 -0
  145. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/graph/schemas/graph_schemas.py +0 -0
  146. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/graph/services/__init__.py +0 -0
  147. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/graph/services/graph_service.py +0 -0
  148. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/graph/services/multimodal_preprocessor.py +0 -0
  149. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/media/__init__.py +0 -0
  150. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/media/router.py +0 -0
  151. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/media/schemas.py +0 -0
  152. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/ping/__init__.py +0 -0
  153. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/ping/router.py +0 -0
  154. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/setup_router.py +0 -0
  155. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/store/__init__.py +0 -0
  156. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/store/router.py +0 -0
  157. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/store/schemas/__init__.py +0 -0
  158. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/store/schemas/store_schemas.py +0 -0
  159. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/store/services/__init__.py +0 -0
  160. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/routers/store/services/store_service.py +0 -0
  161. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/tasks/__init__.py +0 -0
  162. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/tasks/user_tasks.py +0 -0
  163. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/utils/__init__.py +0 -0
  164. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/utils/callable_helper.py +0 -0
  165. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/utils/media/__init__.py +0 -0
  166. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/utils/media/extractor.py +0 -0
  167. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/utils/media/pipeline.py +0 -0
  168. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/utils/parse_output.py +0 -0
  169. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/utils/response_helper.py +0 -0
  170. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/utils/schemas/__init__.py +0 -0
  171. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/utils/schemas/output_schemas.py +0 -0
  172. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/utils/schemas/user_schemas.py +0 -0
  173. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/utils/snowflake_id_generator.py +0 -0
  174. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/utils/swagger_helper.py +0 -0
  175. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/utils/thread_name_generator.py +0 -0
  176. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/agentflow_cli/src/app/worker.py +0 -0
  177. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/graph/__init__.py +0 -0
  178. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/graph/react.py +0 -0
  179. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/requirements.txt +0 -0
  180. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/setup.cfg +0 -0
  181. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/tests/test_multimodal_sprint2_extraction.py +0 -0
  182. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/tests/test_sprint4_media_api.py +0 -0
  183. {10xscale_agentflow_cli-0.3.2.4 → 10xscale_agentflow_cli-0.3.2.5}/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.4
3
+ Version: 0.3.2.5
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>
@@ -38,6 +38,7 @@ agentflow_cli/cli/templates/prod/agentflow.json
38
38
  agentflow_cli/cli/templates/prod/auth/__init__.py
39
39
  agentflow_cli/cli/templates/prod/auth/agent_auth.py
40
40
  agentflow_cli/cli/templates/prod/evals/__init__.py
41
+ agentflow_cli/cli/templates/prod/evals/user_simulator_eval.py
41
42
  agentflow_cli/cli/templates/prod/evals/weather_agents_eval.py
42
43
  agentflow_cli/cli/templates/prod/graph/__init__.py
43
44
  agentflow_cli/cli/templates/prod/graph/agent.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: 10xscale-agentflow-cli
3
- Version: 0.3.2.4
3
+ Version: 0.3.2.5
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,622 @@
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 typing
11
+ import webbrowser
12
+ from collections import defaultdict
13
+ from dataclasses import dataclass
14
+ from pathlib import Path
15
+ from typing import TYPE_CHECKING, Any
16
+
17
+ from agentflow.qa.evaluation import CriterionConfig, EvalConfig, MatchType
18
+ from agentflow.qa.evaluation.collectors.trajectory_collector import TrajectoryCollector
19
+ from agentflow.qa.evaluation.config.eval_config import ReporterConfig
20
+ from agentflow.qa.evaluation.eval_result import EvalReport as ER, EvalCaseResult
21
+ from agentflow.qa.evaluation.evaluator import AgentEvaluator
22
+ from agentflow.qa.evaluation.reporters.manager import ReporterManager
23
+
24
+ from agentflow_cli.cli.commands import BaseCommand
25
+ from agentflow_cli.cli.core.config import ConfigManager
26
+
27
+
28
+ if TYPE_CHECKING:
29
+ from agentflow.qa.evaluation.eval_result import EvalReport
30
+
31
+
32
+ @dataclass
33
+ class _PendingCase:
34
+ case: Any # EvalCase
35
+ evaluator: AgentEvaluator
36
+ file_name: str
37
+ eval_set_id: str
38
+ eval_set_name: str
39
+
40
+
41
+ @dataclass
42
+ class _PendingSimulation:
43
+ scenario: Any # ConversationScenario
44
+ graph: Any
45
+ simulator: Any # UserSimulator
46
+ file_name: str
47
+ eval_set_id: str
48
+ eval_set_name: str
49
+
50
+
51
+ class EvalCommand(BaseCommand):
52
+ """Discover and run agent evaluations; always write HTML + JSON reports."""
53
+
54
+ # ------------------------------------------------------------------
55
+ # Discovery
56
+ # ------------------------------------------------------------------
57
+
58
+ def _discover(self, target: Path) -> list[Path]:
59
+ """Return eval files under target. If target is a file, return it directly."""
60
+ if target.is_file():
61
+ return [target]
62
+
63
+ seen: dict[Path, None] = {}
64
+ for pattern in ("*_eval.py", "eval_*.py"):
65
+ for p in sorted(target.rglob(pattern)):
66
+ seen[p] = None
67
+ return list(seen)
68
+
69
+ # ------------------------------------------------------------------
70
+ # Module loading
71
+ # ------------------------------------------------------------------
72
+
73
+ def _load_module(self, path: Path) -> Any:
74
+ project_root = str(Path.cwd())
75
+ if project_root not in sys.path:
76
+ sys.path.insert(0, project_root)
77
+
78
+ spec = importlib.util.spec_from_file_location("_agentflow_eval", path)
79
+ mod = importlib.util.module_from_spec(spec) # type: ignore[arg-type]
80
+ spec.loader.exec_module(mod) # type: ignore[union-attr]
81
+ return mod
82
+
83
+ # ------------------------------------------------------------------
84
+ # Agent loading from agentflow.json
85
+ # ------------------------------------------------------------------
86
+
87
+ def _load_agent_from_config(self) -> Any:
88
+ config_manager = ConfigManager()
89
+ discovered = config_manager.auto_discover_config()
90
+ if not discovered:
91
+ raise RuntimeError("No agentflow.json found — cannot auto-load agent.")
92
+ config_manager.load_config(str(discovered))
93
+ agent_spec: str = config_manager.get_config_value("agent", default="")
94
+ if not agent_spec or ":" not in agent_spec:
95
+ raise RuntimeError(f"Invalid 'agent' field in agentflow.json: {agent_spec!r}")
96
+ module_path, attr = agent_spec.rsplit(":", 1)
97
+ mod = importlib.import_module(module_path)
98
+ return getattr(mod, attr)
99
+
100
+ # ------------------------------------------------------------------
101
+ # Default config
102
+ # ------------------------------------------------------------------
103
+
104
+ def _default_config(self) -> EvalConfig:
105
+ return EvalConfig(
106
+ criteria={
107
+ "response_match": CriterionConfig(threshold=0.6, match_type=MatchType.ANY_ORDER),
108
+ "tool_name_match_score": CriterionConfig(
109
+ threshold=0.6, match_type=MatchType.ANY_ORDER, check_args=False
110
+ ),
111
+ "node_order": CriterionConfig(threshold=0.6, match_type=MatchType.IN_ORDER),
112
+ },
113
+ )
114
+
115
+ def _collect_eval_functions(self, mod: Any) -> tuple[list[tuple[str, Any]], Any]:
116
+ """Pytest-style discovery: functions annotated -> EvalSet are evals, -> EvalConfig is config."""
117
+ from agentflow.qa.evaluation import EvalConfig, EvalSet
118
+
119
+ eval_pairs: list[tuple[str, Any]] = []
120
+ config: Any = None
121
+
122
+ for name, obj in inspect.getmembers(mod, inspect.isfunction):
123
+ if name.startswith("_"):
124
+ continue
125
+ if getattr(obj, "__module__", None) != getattr(mod, "__name__", None):
126
+ continue
127
+ hints: dict[str, Any] = {}
128
+ try:
129
+ hints = typing.get_type_hints(obj)
130
+ except Exception:
131
+ hints = getattr(obj, "__annotations__", {})
132
+
133
+ ret = hints.get("return")
134
+ if ret is None:
135
+ continue
136
+
137
+ try:
138
+ if inspect.isclass(ret) and issubclass(ret, EvalSet):
139
+ eval_pairs.append((name, obj()))
140
+ continue
141
+ if inspect.isclass(ret) and issubclass(ret, EvalConfig) and config is None:
142
+ config = obj()
143
+ except Exception as exc:
144
+ self.logger.warning("Could not call %s(): %s", name, exc)
145
+
146
+ return eval_pairs, config
147
+
148
+ # ------------------------------------------------------------------
149
+ # Flat pool: collect all pending cases from a file
150
+ # ------------------------------------------------------------------
151
+
152
+ def _collect_from_file(
153
+ self, path: Path, global_config: EvalConfig
154
+ ) -> list[_PendingCase | _PendingSimulation]:
155
+ """Load a module and return pending work for every eval case or simulation scenario.
156
+
157
+ Returns _PendingSimulation items when the file exposes get_scenarios() or SCENARIOS.
158
+ Returns _PendingCase items for the standard get_eval_set() / pytest-style protocols.
159
+ """
160
+ mod = self._load_module(path)
161
+ file_name = path.name
162
+
163
+ # Simulator protocol — get_scenarios() or SCENARIOS constant
164
+ scenarios = None
165
+ if hasattr(mod, "get_scenarios"):
166
+ try:
167
+ scenarios = mod.get_scenarios()
168
+ except Exception as exc:
169
+ self.logger.warning("Could not call get_scenarios() in %s: %s", file_name, exc)
170
+ elif hasattr(mod, "SCENARIOS"):
171
+ scenarios = mod.SCENARIOS
172
+
173
+ if scenarios is not None:
174
+ return self._collect_simulations(mod, scenarios, file_name)
175
+
176
+ # get_eval_set() protocol
177
+ if hasattr(mod, "get_eval_set"):
178
+ if hasattr(mod, "get_eval_config"):
179
+ file_config = mod.get_eval_config()
180
+ elif hasattr(mod, "EVAL_CONFIG"):
181
+ file_config = mod.EVAL_CONFIG
182
+ else:
183
+ file_config = global_config
184
+ config = global_config if global_config.criteria else file_config
185
+ return self._make_pending(mod, mod.get_eval_set(), config, file_name)
186
+
187
+ # pytest-style discovery
188
+ eval_pairs, discovered_config = self._collect_eval_functions(mod)
189
+ if eval_pairs:
190
+ file_config = discovered_config or (
191
+ mod.get_eval_config()
192
+ if hasattr(mod, "get_eval_config")
193
+ else mod.EVAL_CONFIG
194
+ if hasattr(mod, "EVAL_CONFIG")
195
+ else global_config
196
+ )
197
+ config = global_config if global_config.criteria else file_config
198
+ pending: list[_PendingCase] = []
199
+ for _, es in eval_pairs:
200
+ pending.extend(self._make_pending(mod, es, config, file_name))
201
+ return pending
202
+
203
+ self.output.warning(f"Skipping {file_name} — no eval entry point found.")
204
+ return []
205
+
206
+ def _collect_simulations(
207
+ self, mod: Any, scenarios: list[Any], file_name: str
208
+ ) -> list[_PendingSimulation]:
209
+ """Build _PendingSimulation items for each scenario in the file."""
210
+ from agentflow.qa.evaluation import (
211
+ CriterionConfig,
212
+ SimulationGoalsCriterion,
213
+ UserSimulator,
214
+ UserSimulatorConfig,
215
+ )
216
+
217
+ graph = getattr(mod, "app", None) or self._load_agent_from_config()
218
+
219
+ # Per-file simulator config via SIMULATOR_CONFIG constant or default
220
+ sim_cfg: UserSimulatorConfig | None = getattr(mod, "SIMULATOR_CONFIG", None)
221
+ goal_threshold: float = 0.7
222
+ if sim_cfg is not None and hasattr(sim_cfg, "goal_threshold"):
223
+ goal_threshold = sim_cfg.goal_threshold # type: ignore[attr-defined]
224
+
225
+ judge = SimulationGoalsCriterion(
226
+ config=CriterionConfig(threshold=goal_threshold, num_samples=1)
227
+ )
228
+ simulator = UserSimulator(
229
+ config=sim_cfg or UserSimulatorConfig(),
230
+ criteria=[judge],
231
+ )
232
+
233
+ eval_set_id = f"{Path(file_name).stem}_simulations"
234
+ eval_set_name = f"{Path(file_name).stem} (user simulator)"
235
+
236
+ return [
237
+ _PendingSimulation(
238
+ scenario=sc,
239
+ graph=graph,
240
+ simulator=simulator,
241
+ file_name=file_name,
242
+ eval_set_id=eval_set_id,
243
+ eval_set_name=eval_set_name,
244
+ )
245
+ for sc in scenarios
246
+ ]
247
+
248
+ def _make_pending(
249
+ self, mod: Any, eval_set: Any, config: EvalConfig, file_name: str
250
+ ) -> list[_PendingCase]:
251
+ graph = getattr(mod, "app", None) or self._load_agent_from_config()
252
+ collector = TrajectoryCollector(capture_all_events=True)
253
+ evaluator = AgentEvaluator(graph, collector, config=config)
254
+ return [
255
+ _PendingCase(
256
+ case=c,
257
+ evaluator=evaluator,
258
+ file_name=file_name,
259
+ eval_set_id=eval_set.eval_set_id,
260
+ eval_set_name=eval_set.name,
261
+ )
262
+ for c in eval_set.eval_cases
263
+ ]
264
+
265
+ # ------------------------------------------------------------------
266
+ # Progress printing
267
+ # ------------------------------------------------------------------
268
+
269
+ def _print_case_progress(
270
+ self,
271
+ file_name: str,
272
+ case_name: str,
273
+ result: EvalCaseResult,
274
+ index: int,
275
+ total: int,
276
+ ) -> None:
277
+ status = "PASSED" if result.passed else ("ERROR" if result.is_error else "FAILED")
278
+ duration = f"{result.duration_seconds:.2f}s"
279
+ label = f"{file_name}::{case_name}"
280
+ status_colored = (
281
+ f"\033[32m{status}\033[0m" if result.passed else f"\033[31m{status}\033[0m"
282
+ )
283
+ print(f"[{index:3d}/{total}] {label} {status_colored} {duration}", flush=True)
284
+
285
+ # ------------------------------------------------------------------
286
+ # Flat pool execution — single asyncio event loop for all cases
287
+ # ------------------------------------------------------------------
288
+
289
+ async def _run_flat_pool(
290
+ self,
291
+ pending: list[_PendingCase | _PendingSimulation],
292
+ max_concurrency: int,
293
+ parallel: bool,
294
+ ) -> list[tuple[str, str, str, EvalCaseResult]]:
295
+ """Run all cases and simulations under a single event loop.
296
+
297
+ Returns list of (file_name, eval_set_id, eval_set_name, EvalCaseResult).
298
+ """
299
+ total = len(pending)
300
+ completed = 0
301
+
302
+ async def _run_case(pc: _PendingCase) -> tuple[str, str, str, EvalCaseResult]:
303
+ local_collector = TrajectoryCollector(
304
+ capture_all_events=pc.evaluator.collector.capture_all_events,
305
+ )
306
+ try:
307
+ result = await pc.evaluator._evaluate_case(
308
+ pc.case, collector_override=local_collector
309
+ )
310
+ except Exception as exc:
311
+ result = EvalCaseResult.failure(
312
+ eval_id=pc.case.eval_id,
313
+ error=str(exc),
314
+ name=pc.case.name,
315
+ )
316
+ return (pc.file_name, pc.eval_set_id, pc.eval_set_name, result)
317
+
318
+ async def _run_simulation(ps: _PendingSimulation) -> tuple[str, str, str, EvalCaseResult]:
319
+ from agentflow.qa.evaluation.eval_result import CriterionResult
320
+
321
+ try:
322
+ sim_result = await ps.simulator.run(ps.graph, ps.scenario)
323
+ criterion_results = [
324
+ CriterionResult.success(
325
+ criterion=name,
326
+ score=score,
327
+ threshold=ps.simulator.criteria[0].threshold
328
+ if ps.simulator.criteria
329
+ else 0.7,
330
+ details=sim_result.criterion_details.get(name, {}),
331
+ )
332
+ for name, score in sim_result.criterion_scores.items()
333
+ ]
334
+ # If no criterion ran (no goals defined), fall back to completion flag
335
+ if not criterion_results:
336
+ score = 1.0 if sim_result.completed else 0.0
337
+ criterion_results = [
338
+ CriterionResult.success(
339
+ criterion="simulation_completed",
340
+ score=score,
341
+ threshold=0.5,
342
+ )
343
+ ]
344
+ conversation_text = "\n".join(
345
+ f"{m['role'].upper()}: {m['content']}"
346
+ for m in sim_result.conversation
347
+ )
348
+ result = EvalCaseResult.success(
349
+ eval_id=ps.scenario.scenario_id,
350
+ name=ps.scenario.description or ps.scenario.scenario_id,
351
+ criterion_results=criterion_results,
352
+ actual_response=conversation_text,
353
+ metadata={
354
+ "turns": sim_result.turns,
355
+ "goals_achieved": sim_result.goals_achieved,
356
+ "completed": sim_result.completed,
357
+ },
358
+ )
359
+ except Exception as exc:
360
+ result = EvalCaseResult.failure(
361
+ eval_id=ps.scenario.scenario_id,
362
+ error=str(exc),
363
+ name=ps.scenario.description or ps.scenario.scenario_id,
364
+ )
365
+ return (ps.file_name, ps.eval_set_id, ps.eval_set_name, result)
366
+
367
+ async def _dispatch(
368
+ item: _PendingCase | _PendingSimulation,
369
+ ) -> tuple[str, str, str, EvalCaseResult]:
370
+ if isinstance(item, _PendingSimulation):
371
+ return await _run_simulation(item)
372
+ return await _run_case(item)
373
+
374
+ if not parallel:
375
+ results: list[tuple[str, str, str, EvalCaseResult]] = []
376
+ for item in pending:
377
+ quad = await _dispatch(item)
378
+ completed += 1
379
+ file_name, _, _, result = quad
380
+ self._print_case_progress(
381
+ file_name, result.name or result.eval_id, result, completed, total
382
+ )
383
+ results.append(quad)
384
+ return results
385
+
386
+ semaphore = asyncio.Semaphore(max_concurrency)
387
+
388
+ async def _run_one(
389
+ item: _PendingCase | _PendingSimulation,
390
+ ) -> tuple[str, str, str, EvalCaseResult]:
391
+ async with semaphore:
392
+ return await _dispatch(item)
393
+
394
+ output_results: list[tuple[str, str, str, EvalCaseResult]] = []
395
+ tasks = [asyncio.create_task(_run_one(item)) for item in pending]
396
+ for coro in asyncio.as_completed(tasks):
397
+ quad = await coro
398
+ completed += 1
399
+ file_name, _, _, result = quad
400
+ self._print_case_progress(
401
+ file_name, result.name or result.eval_id, result, completed, total
402
+ )
403
+ output_results.append(quad)
404
+
405
+ return output_results
406
+
407
+ # ------------------------------------------------------------------
408
+ # Report merging
409
+ # ------------------------------------------------------------------
410
+
411
+ def _merge_reports(self, reports: list[EvalReport]) -> EvalReport:
412
+ if len(reports) == 1:
413
+ return reports[0]
414
+
415
+ all_results = []
416
+ for r in reports:
417
+ all_results.extend(r.results)
418
+ return ER.create(
419
+ eval_set_id="combined_eval",
420
+ eval_set_name="Combined Evaluation",
421
+ results=all_results,
422
+ )
423
+
424
+ # ------------------------------------------------------------------
425
+ # Eval directory from agentflow.json
426
+ # ------------------------------------------------------------------
427
+
428
+ def _resolve_eval_dir(self) -> Path:
429
+ config_manager = ConfigManager()
430
+ discovered = config_manager.auto_discover_config()
431
+ directory = "evals"
432
+ if discovered:
433
+ try:
434
+ config_manager.load_config(str(discovered))
435
+ eval_cfg = config_manager.get_evaluation_config()
436
+ directory = eval_cfg.get("directory", "evals")
437
+ except Exception:
438
+ self.logger.warning(
439
+ "Failed to load eval directory from config; using default 'evals/'"
440
+ )
441
+ return Path.cwd() / directory
442
+
443
+ # ------------------------------------------------------------------
444
+ # Main entry point
445
+ # ------------------------------------------------------------------
446
+
447
+ def execute( # noqa: PLR0912, PLR0915
448
+ self,
449
+ target: str | None = None,
450
+ output_dir: str = "eval_reports",
451
+ no_report: bool = False,
452
+ threshold: float | None = None,
453
+ open_report: bool = False,
454
+ parallel: bool = False,
455
+ max_concurrency: int = 4,
456
+ verbose: bool = False,
457
+ quiet: bool = False,
458
+ **kwargs: Any,
459
+ ) -> int:
460
+ # 1. Load global config from agentflow.json
461
+ global_eval_cfg: dict[str, Any] = {}
462
+ config_manager = ConfigManager()
463
+ discovered = config_manager.auto_discover_config()
464
+ if discovered:
465
+ try:
466
+ config_manager.load_config(str(discovered))
467
+ global_eval_cfg = config_manager.get_evaluation_config()
468
+ if output_dir == "eval_reports":
469
+ output_dir = global_eval_cfg.get("output_dir", output_dir)
470
+ if threshold is None:
471
+ threshold = global_eval_cfg.get("threshold")
472
+ except Exception:
473
+ self.logger.warning(
474
+ "Failed to load eval config from agentflow.json; using defaults"
475
+ )
476
+
477
+ # 2. Build typed EvalConfig; CLI flags override everything.
478
+ # If agentflow.json has an evaluation section but no criteria key, inject
479
+ # the built-in defaults so at least one criterion always runs.
480
+ try:
481
+ global_config = (
482
+ EvalConfig.model_validate(global_eval_cfg)
483
+ if global_eval_cfg
484
+ else self._default_config()
485
+ )
486
+ except Exception:
487
+ global_config = self._default_config()
488
+
489
+ if not global_config.criteria:
490
+ global_config.criteria = self._default_config().criteria
491
+
492
+ if parallel:
493
+ global_config.parallel = True
494
+ if max_concurrency != 4:
495
+ global_config.max_concurrency = max_concurrency
496
+
497
+ effective_parallel = global_config.parallel
498
+ effective_concurrency = global_config.max_concurrency
499
+
500
+ # 3. Resolve target path
501
+ if target:
502
+ target_path = Path(target)
503
+ if not target_path.exists():
504
+ self.output.error(f"Path not found: {target}")
505
+ return 1
506
+ else:
507
+ target_path = self._resolve_eval_dir()
508
+ if not target_path.exists():
509
+ self.output.error(
510
+ f"Eval directory '{target_path}' not found. "
511
+ "Create an evals/ directory or pass a file/folder path."
512
+ )
513
+ return 1
514
+
515
+ # 4. Discover files
516
+ files = self._discover(target_path)
517
+ if not files:
518
+ self.output.error(f"No eval files found in {target_path}")
519
+ return 1
520
+
521
+ # 5. Collect all pending cases across every file
522
+ pending: list[_PendingCase] = []
523
+ for f in files:
524
+ try:
525
+ cases = self._collect_from_file(f, global_config)
526
+ pending.extend(cases)
527
+ except Exception as exc:
528
+ self.output.error(f"Error loading {f.name}: {exc}")
529
+ self.logger.exception("Failed to load eval file: %s", f)
530
+
531
+ if not pending:
532
+ self.output.error(
533
+ "No eval cases found. Ensure eval files expose get_eval_set() "
534
+ "or functions annotated with -> EvalSet."
535
+ )
536
+ return 1
537
+
538
+ n_files = len({pc.file_name for pc in pending})
539
+ n_sims = sum(1 for pc in pending if isinstance(pc, _PendingSimulation))
540
+ n_cases = len(pending) - n_sims
541
+ parts = []
542
+ if n_cases:
543
+ parts.append(f"{n_cases} eval case(s)")
544
+ if n_sims:
545
+ parts.append(f"{n_sims} simulation scenario(s)")
546
+ self.output.print_banner(
547
+ "Eval",
548
+ f"Found {', '.join(parts)} across {n_files} file(s) in {target_path}",
549
+ )
550
+
551
+ # 6. Run all cases under a single asyncio event loop
552
+ quads = asyncio.run(
553
+ self._run_flat_pool(pending, effective_concurrency, effective_parallel)
554
+ )
555
+
556
+ if not quads:
557
+ self.output.error("No results produced.")
558
+ return 1
559
+
560
+ # 7. Group by eval_set_id → one EvalReport per set
561
+ groups: dict[str, tuple[str, list[EvalCaseResult]]] = defaultdict(
562
+ lambda: ("", [])
563
+ )
564
+ for file_name, eval_set_id, eval_set_name, result in quads:
565
+ name, results_list = groups[eval_set_id]
566
+ groups[eval_set_id] = (eval_set_name or name, results_list + [result])
567
+
568
+ reports: list[EvalReport] = []
569
+ for eval_set_id, (eval_set_name, results) in groups.items():
570
+ reports.append(
571
+ ER.create(
572
+ eval_set_id=eval_set_id,
573
+ eval_set_name=eval_set_name,
574
+ results=results,
575
+ config_used=global_config.model_dump(),
576
+ )
577
+ )
578
+
579
+ # 8. Merge into a single report
580
+ merged = self._merge_reports(reports)
581
+
582
+ # 9. Determine exit code
583
+ if threshold is not None and merged.summary.pass_rate < threshold:
584
+ self.output.error(
585
+ f"Pass rate {merged.summary.pass_rate:.1%} is below threshold {threshold:.1%}"
586
+ )
587
+ return_code = 1
588
+ else:
589
+ return_code = 0 if merged.summary.pass_rate == 1.0 else 1
590
+
591
+ # 10. Generate reports
592
+ if not no_report:
593
+ manager = ReporterManager(
594
+ ReporterConfig(
595
+ output_dir=output_dir,
596
+ html=True,
597
+ json_report=True,
598
+ console=False,
599
+ timestamp_files=True,
600
+ )
601
+ )
602
+ report_result = manager.run_all(merged)
603
+
604
+ if report_result.html_path:
605
+ self.output.success(f"HTML report: {report_result.html_path}")
606
+ if report_result.json_path:
607
+ self.output.info(f"JSON report: {report_result.json_path}", emoji=False)
608
+ if report_result.has_errors:
609
+ for name, err in report_result.errors:
610
+ self.output.warning(f"Reporter error [{name}]: {err}")
611
+
612
+ if open_report and report_result.html_path:
613
+ webbrowser.open(Path(report_result.html_path).as_uri())
614
+
615
+ summary = merged.summary
616
+ self.output.info(
617
+ f"Results: {summary.passed_cases}/{summary.total_cases} passed "
618
+ f"({summary.pass_rate:.1%})",
619
+ emoji=False,
620
+ )
621
+
622
+ return return_code
@@ -430,18 +430,32 @@ def eval_cmd(
430
430
  "--open",
431
431
  help="Open the HTML report in the default browser after the run",
432
432
  ),
433
+ parallel: bool = typer.Option(
434
+ False,
435
+ "--parallel",
436
+ "-p",
437
+ help="Collect all cases from all files into a flat pool and run them concurrently",
438
+ ),
439
+ max_concurrency: int = typer.Option(
440
+ 4,
441
+ "--max-concurrency",
442
+ "-c",
443
+ help="Max cases running concurrently when --parallel is set (global semaphore)",
444
+ ),
433
445
  verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose output"),
434
446
  quiet: bool = typer.Option(False, "--quiet", "-q", help="Suppress all output except errors"),
435
447
  ) -> None:
436
448
  """Run agent evaluations.
437
449
 
438
450
  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.
451
+ Collects all cases from all files into a flat pool, then runs them under a single
452
+ event loop throttled by --max-concurrency. Always generates HTML + JSON reports
453
+ in eval_reports/ unless --no-report is set.
440
454
 
441
455
  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
456
+ get_eval_set() + get_eval_config() # CLI loads agent from agentflow.json
457
+ EVAL_CONFIG + get_eval_set() # same, config as a constant
458
+ any function returning EvalSet # auto-discovered, pytest-style
445
459
  """
446
460
  setup_cli_logging(verbose=verbose, quiet=quiet)
447
461
 
@@ -453,6 +467,8 @@ def eval_cmd(
453
467
  no_report=no_report,
454
468
  threshold=threshold,
455
469
  open_report=open_report,
470
+ parallel=parallel,
471
+ max_concurrency=max_concurrency,
456
472
  verbose=verbose,
457
473
  quiet=quiet,
458
474
  )