10xscale-agentflow-cli 0.3.2.2__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.
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/10xscale_agentflow_cli.egg-info/PKG-INFO +1 -1
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/10xscale_agentflow_cli.egg-info/SOURCES.txt +5 -1
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/PKG-INFO +1 -1
- 10xscale_agentflow_cli-0.3.2.4/agentflow_cli/cli/commands/eval.py +273 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/commands/init.py +1 -1
- 10xscale_agentflow_cli-0.3.2.4/agentflow_cli/cli/commands/test.py +87 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/core/config.py +22 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/main.py +99 -0
- 10xscale_agentflow_cli-0.3.2.4/agentflow_cli/cli/templates/dev/agentflow.json +12 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/agentflow.json +11 -0
- 10xscale_agentflow_cli-0.3.2.4/agentflow_cli/cli/templates/prod/evals/weather_agents_eval.py +41 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/tests/test_agent_eval.py +15 -25
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/SKILL.md +11 -7
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/agents-and-tools.md +6 -6
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/api-client.md +9 -14
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/api-configuration.md +4 -5
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/api-settings-and-middleware.md +10 -11
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/architecture.md +12 -14
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/auth-and-authorization.md +6 -7
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/callbacks-and-command.md +10 -10
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/checkpointing-and-threads.md +5 -5
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/cli-commands.md +8 -10
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/client-auth-and-errors.md +5 -6
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/client-messages-invoke-stream.md +7 -9
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/client-threads-memory-files.md +5 -7
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/context-id-background.md +5 -7
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/dependency-injection.md +4 -4
- 10xscale_agentflow_cli-0.3.2.4/agentflow_cli/cli/templates/skills/agent-skills/references/evaluation.md +500 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/id-and-thread-name-generators.md +6 -8
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/media-and-files.md +3 -3
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/memory-and-store.md +4 -4
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/prebuilt-agents-and-tools.md +5 -6
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/production-runtime.md +5 -5
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/providers-and-adapters.md +8 -10
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/publishers-and-runtime-protocols.md +5 -6
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/rate-limiting.md +6 -6
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/remote-tools.md +14 -14
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/rest-api-and-errors.md +9 -9
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/security-and-validators.md +4 -5
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/state-and-messages.md +5 -5
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/state-graph.md +4 -4
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/streaming.md +4 -4
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/testing-and-evaluation.md +4 -6
- 10xscale_agentflow_cli-0.3.2.4/agentflow_cli/cli/templates/skills/agent-skills/references/unit-testing.md +202 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/pyproject.toml +1 -1
- 10xscale_agentflow_cli-0.3.2.2/agentflow_cli/cli/templates/dev/agentflow.json +0 -6
- 10xscale_agentflow_cli-0.3.2.2/agentflow_cli/cli/templates/prod/evals/weather_agents.py +0 -119
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/10xscale_agentflow_cli.egg-info/dependency_links.txt +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/10xscale_agentflow_cli.egg-info/entry_points.txt +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/10xscale_agentflow_cli.egg-info/not-zip-safe +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/10xscale_agentflow_cli.egg-info/requires.txt +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/10xscale_agentflow_cli.egg-info/top_level.txt +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/MANIFEST.in +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/README.md +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow.json +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/commands/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/commands/api.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/commands/build.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/commands/skills.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/commands/version.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/constants.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/core/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/core/output.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/core/validation.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/exceptions.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/logger.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/defaults.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/dev/graph/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/dev/graph/agent.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/.pre-commot-config.yaml +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/auth/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/auth/agent_auth.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/evals/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/agent.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/nodes/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/nodes/main_node.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/nodes/tool_node.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/state.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/thread_name_generator.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/tools/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/tools/weather_tool.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/utils/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/utils/tool_decision.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/validators/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/validators/lifecyle.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/validators/manager.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/graph/validators/validators.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/tests/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/tests/conftest.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/tests/test_catalog_tools.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/prod/tests/test_graph_nodes.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/agent-skills/references/stream-emitter.md +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/templates/skills/copilot/agentflow.instructions.md +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/auth/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/auth/auth_backend.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/auth/authorization.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/auth/base_auth.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/auth/jwt_auth.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/auth/permissions.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/config/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/config/graph_config.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/config/media_settings.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/config/sentry_config.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/config/settings.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/config/setup_logs.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/config/setup_middleware.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/config/worker_middleware.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/exceptions/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/exceptions/general_exception.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/exceptions/handle_errors.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/exceptions/resources_exceptions.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/exceptions/user_exception.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/middleware/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/middleware/rate_limit/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/middleware/rate_limit/base.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/middleware/rate_limit/factory.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/middleware/rate_limit/memory.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/middleware/rate_limit/middleware.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/middleware/rate_limit/redis.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/middleware/request_limits.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/middleware/security_headers.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/utils/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/core/utils/log_sanitizer.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/loader.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/main.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/a2a.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/a2ui.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/checkpointer/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/checkpointer/router.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/checkpointer/schemas/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/checkpointer/schemas/checkpointer_schemas.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/checkpointer/services/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/checkpointer/services/checkpointer_service.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/graph/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/graph/router.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/graph/schemas/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/graph/schemas/graph_schemas.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/graph/services/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/graph/services/graph_service.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/graph/services/multimodal_preprocessor.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/media/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/media/router.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/media/schemas.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/ping/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/ping/router.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/setup_router.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/store/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/store/router.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/store/schemas/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/store/schemas/store_schemas.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/store/services/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/routers/store/services/store_service.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/tasks/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/tasks/user_tasks.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/callable_helper.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/media/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/media/extractor.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/media/pipeline.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/parse_output.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/response_helper.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/schemas/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/schemas/output_schemas.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/schemas/user_schemas.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/snowflake_id_generator.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/swagger_helper.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/utils/thread_name_generator.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/src/app/worker.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/graph/__init__.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/graph/react.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/requirements.txt +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/setup.cfg +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/tests/test_multimodal_sprint2_extraction.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/tests/test_sprint4_media_api.py +0 -0
- {10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/tests/test_utils_parse_and_callable.py +0 -0
|
@@ -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/
|
|
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
|
|
@@ -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
|
{10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/commands/init.py
RENAMED
|
@@ -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
|
{10xscale_agentflow_cli-0.3.2.2 → 10xscale_agentflow_cli-0.3.2.4}/agentflow_cli/cli/core/config.py
RENAMED
|
@@ -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:
|
|
@@ -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
|
+
)
|