graph-agents-cli 0.3.1__py3-none-any.whl
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.
- graph_agents_cli/__init__.py +26 -0
- graph_agents_cli/_api_policy.py +2145 -0
- graph_agents_cli/_approvals.py +400 -0
- graph_agents_cli/_build.py +186 -0
- graph_agents_cli/_build_info.json +7 -0
- graph_agents_cli/_chat_client.py +462 -0
- graph_agents_cli/_click.py +157 -0
- graph_agents_cli/_defaults.py +139 -0
- graph_agents_cli/_experiments.py +64 -0
- graph_agents_cli/_http.py +192 -0
- graph_agents_cli/_output.py +83 -0
- graph_agents_cli/_project.py +462 -0
- graph_agents_cli/_remote.py +220 -0
- graph_agents_cli/_response_schema.py +264 -0
- graph_agents_cli/_runner.py +319 -0
- graph_agents_cli/_skills_check.py +274 -0
- graph_agents_cli/_tools.py +189 -0
- graph_agents_cli/_trust.py +66 -0
- graph_agents_cli/api/__init__.py +15 -0
- graph_agents_cli/api/_changes.py +506 -0
- graph_agents_cli/api/_files.py +658 -0
- graph_agents_cli/api/cmd_api.py +2480 -0
- graph_agents_cli/deploy/__init__.py +15 -0
- graph_agents_cli/deploy/_config.py +171 -0
- graph_agents_cli/deploy/_image.py +128 -0
- graph_agents_cli/deploy/_kube.py +286 -0
- graph_agents_cli/deploy/_modes.py +234 -0
- graph_agents_cli/deploy/_preflight.py +370 -0
- graph_agents_cli/deploy/_values.py +168 -0
- graph_agents_cli/deploy/cmd_deploy.py +1866 -0
- graph_agents_cli/deploy/gitops.py +562 -0
- graph_agents_cli/deploy/local_load.py +273 -0
- graph_agents_cli/dev/__init__.py +13 -0
- graph_agents_cli/dev/cmd_build.py +131 -0
- graph_agents_cli/dev/cmd_install.py +78 -0
- graph_agents_cli/dev/cmd_lint.py +119 -0
- graph_agents_cli/dev/cmd_playground.py +297 -0
- graph_agents_cli/dev/policy_check.py +1287 -0
- graph_agents_cli/eval/__init__.py +22 -0
- graph_agents_cli/eval/_client.py +670 -0
- graph_agents_cli/eval/_common.py +177 -0
- graph_agents_cli/eval/_judge.py +168 -0
- graph_agents_cli/eval/_judge_runner.py +238 -0
- graph_agents_cli/eval/_paths.py +212 -0
- graph_agents_cli/eval/checks.py +581 -0
- graph_agents_cli/eval/cmd_analyze.py +278 -0
- graph_agents_cli/eval/cmd_compare.py +284 -0
- graph_agents_cli/eval/cmd_eval_group.py +80 -0
- graph_agents_cli/eval/cmd_generate.py +558 -0
- graph_agents_cli/eval/cmd_grade.py +466 -0
- graph_agents_cli/eval/cmd_metric.py +156 -0
- graph_agents_cli/eval/cmd_run.py +370 -0
- graph_agents_cli/eval/cmd_submit.py +400 -0
- graph_agents_cli/eval/config.py +435 -0
- graph_agents_cli/eval/dataset.py +350 -0
- graph_agents_cli/eval/gate.py +420 -0
- graph_agents_cli/eval/transcript.py +192 -0
- graph_agents_cli/extension/__init__.py +13 -0
- graph_agents_cli/extension/_compat.py +86 -0
- graph_agents_cli/extension/_loader.py +293 -0
- graph_agents_cli/extension/_manifest.py +135 -0
- graph_agents_cli/extension/_overrides.py +195 -0
- graph_agents_cli/extension/_paths.py +91 -0
- graph_agents_cli/extension/_refs.py +193 -0
- graph_agents_cli/extension/_resolver.py +453 -0
- graph_agents_cli/extension/_schema.py +106 -0
- graph_agents_cli/extension/_spec.py +253 -0
- graph_agents_cli/extension/_sync.py +102 -0
- graph_agents_cli/extension/_trust.py +58 -0
- graph_agents_cli/extension/cmd_extension_add.py +259 -0
- graph_agents_cli/extension/cmd_extension_group.py +57 -0
- graph_agents_cli/extension/cmd_extension_list.py +56 -0
- graph_agents_cli/extension/cmd_extension_remove.py +61 -0
- graph_agents_cli/extension/cmd_extension_update.py +195 -0
- graph_agents_cli/info/__init__.py +13 -0
- graph_agents_cli/info/cmd_info.py +222 -0
- graph_agents_cli/infra/__init__.py +15 -0
- graph_agents_cli/infra/checks.py +1169 -0
- graph_agents_cli/infra/cmd_infra.py +103 -0
- graph_agents_cli/main.py +591 -0
- graph_agents_cli/peer/__init__.py +15 -0
- graph_agents_cli/peer/_generate.py +254 -0
- graph_agents_cli/peer/cmd_peer.py +1151 -0
- graph_agents_cli/run/__init__.py +13 -0
- graph_agents_cli/run/_local_server.py +1157 -0
- graph_agents_cli/run/_signals.py +141 -0
- graph_agents_cli/run/cmd_approvals.py +530 -0
- graph_agents_cli/run/cmd_run.py +1421 -0
- graph_agents_cli/scaffold/__init__.py +19 -0
- graph_agents_cli/scaffold/agents/README.md +24 -0
- graph_agents_cli/scaffold/agents/empty_py/.template/templateconfig.yaml +22 -0
- graph_agents_cli/scaffold/agents/langgraph/.env.example +292 -0
- graph_agents_cli/scaffold/agents/langgraph/.template/templateconfig.yaml +28 -0
- graph_agents_cli/scaffold/agents/langgraph/Dockerfile +59 -0
- graph_agents_cli/scaffold/agents/langgraph/Dockerfile.langgraph-server +59 -0
- graph_agents_cli/scaffold/agents/langgraph/README.md +571 -0
- graph_agents_cli/scaffold/agents/langgraph/api-policy.yaml +60 -0
- graph_agents_cli/scaffold/agents/langgraph/app/__init__.py +20 -0
- graph_agents_cli/scaffold/agents/langgraph/app/agent.py +174 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/__init__.py +15 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/a2a.py +2162 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/a2a_client.py +1167 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/api_client.py +4220 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/approvals.py +1349 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/auth.py +1986 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/chat.py +2962 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/checkpointer.py +432 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/content.py +569 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/db.py +580 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/limits.py +203 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/metrics.py +231 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/middleware.py +361 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/model.py +611 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/playground.py +230 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/run_locks.py +459 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/structured.py +755 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/telemetry.py +681 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/threads.py +493 -0
- graph_agents_cli/scaffold/agents/langgraph/app/app_utils/token_exchange.py +959 -0
- graph_agents_cli/scaffold/agents/langgraph/app/fast_api_app.py +770 -0
- graph_agents_cli/scaffold/agents/langgraph/app/policies/__init__.py +55 -0
- graph_agents_cli/scaffold/agents/langgraph/app/policies/custom.py +97 -0
- graph_agents_cli/scaffold/agents/langgraph/app/tools/__init__.py +46 -0
- graph_agents_cli/scaffold/agents/langgraph/app/tools/example_api.py +92 -0
- graph_agents_cli/scaffold/agents/langgraph/app/tools/weather.py +33 -0
- graph_agents_cli/scaffold/agents/langgraph/langgraph.json +14 -0
- graph_agents_cli/scaffold/agents/langgraph/pyproject.toml +78 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/conftest.py +376 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/eval/datasets/basic-dataset.json +53 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/eval/eval_config.yaml +32 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/approval_graph.py +137 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/fake_issuer.py +216 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/fake_openai.py +357 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_a2a_outcomes.py +569 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_a2a_relay.py +479 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_api_surface.py +812 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_approvals.py +1367 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_approvals_server.py +794 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_cross_actor.py +497 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_cross_actor_server.py +247 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_history_repair.py +278 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_model_apis.py +242 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_postgres.py +637 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_resilience_postgres.py +770 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_runtime_guardrails.py +854 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_server_e2e.py +340 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_server_runtime.py +989 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_structured_answers.py +584 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_structured_server.py +222 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_token_exchange_issuer.py +650 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/load_test/.results/.placeholder +0 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/load_test/README.md +22 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/load_test/conftest.py +21 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/load_test/load_test.py +81 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_a2a_client.py +824 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_a2a_scoping.py +724 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_api_client.py +1214 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_api_client_hardening.py +716 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_api_policy_rpc.py +767 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_approval_ledger.py +1536 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_fake_model.py +115 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_jwt_policy.py +991 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_limits.py +310 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_logging.py +148 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_logging_hardening.py +271 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_policy.py +378 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_resilience.py +610 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_server_auth.py +702 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_structured.py +673 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_telemetry.py +404 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_thread_listing.py +255 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_threads.py +268 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_token_exchange.py +1320 -0
- graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_untrusted_content.py +393 -0
- graph_agents_cli/scaffold/agents/langgraph/uv-fastapi.lock +2084 -0
- graph_agents_cli/scaffold/agents/langgraph/uv-langgraph-server.lock +2106 -0
- graph_agents_cli/scaffold/agents/langgraph/{{cookiecutter.agent_guidance_filename}} +129 -0
- graph_agents_cli/scaffold/base_templates/_shared/graph-agents-cli-manifest.yaml +36 -0
- graph_agents_cli/scaffold/base_templates/python/.dockerignore +32 -0
- graph_agents_cli/scaffold/base_templates/python/.github/CODEOWNERS +30 -0
- graph_agents_cli/scaffold/base_templates/python/.github/agent.env +7 -0
- graph_agents_cli/scaffold/base_templates/python/.github/workflows/pr_checks.yaml +214 -0
- graph_agents_cli/scaffold/base_templates/python/.gitignore +209 -0
- graph_agents_cli/scaffold/base_templates/python/tests/unit/test_dummy.py +23 -0
- graph_agents_cli/scaffold/base_templates/python/{{cookiecutter.agent_guidance_filename}} +35 -0
- graph_agents_cli/scaffold/cmd_scaffold_group.py +49 -0
- graph_agents_cli/scaffold/commands/__init__.py +13 -0
- graph_agents_cli/scaffold/commands/create.py +1424 -0
- graph_agents_cli/scaffold/commands/enhance.py +1652 -0
- graph_agents_cli/scaffold/commands/upgrade.py +570 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/.github/agent.env +12 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/.github/workflows/promote-to-prod.yaml +371 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/.github/workflows/staging.yaml +450 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/argocd/application-dev.yaml +43 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/argocd/application-prod.yaml +41 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/argocd/application-staging.yaml +43 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/.helmignore +14 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/Chart.yaml +21 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/examples/networkpolicy.yaml +103 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/NOTES.txt +48 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/_helpers.tpl +189 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/certificate.yaml +15 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/configmap.yaml +10 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/deployment.yaml +199 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/hpa.yaml +22 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/httproute.yaml +30 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/ingress.yaml +39 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/networkpolicy.yaml +48 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/pdb.yaml +13 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/postgresql-secret.yaml +37 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/service.yaml +15 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/serviceaccount.yaml +13 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/servicemonitor.yaml +42 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/values-dev.yaml +22 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/values-prod.yaml +45 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/values-staging.yaml +29 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/values.yaml +396 -0
- graph_agents_cli/scaffold/deployment_targets/kubernetes/python/tests/integration/test_chart.py +269 -0
- graph_agents_cli/scaffold/deployment_targets/none/README.md +5 -0
- graph_agents_cli/scaffold/deployment_targets/none/python/README.md +6 -0
- graph_agents_cli/scaffold/utils/__init__.py +13 -0
- graph_agents_cli/scaffold/utils/backup.py +212 -0
- graph_agents_cli/scaffold/utils/build_record.py +257 -0
- graph_agents_cli/scaffold/utils/cli_options.py +184 -0
- graph_agents_cli/scaffold/utils/fs.py +83 -0
- graph_agents_cli/scaffold/utils/generate_locks.py +214 -0
- graph_agents_cli/scaffold/utils/generation_metadata.py +88 -0
- graph_agents_cli/scaffold/utils/keyedit.py +768 -0
- graph_agents_cli/scaffold/utils/keymerge.py +537 -0
- graph_agents_cli/scaffold/utils/language.py +138 -0
- graph_agents_cli/scaffold/utils/lock_utils.py +94 -0
- graph_agents_cli/scaffold/utils/logging.py +77 -0
- graph_agents_cli/scaffold/utils/manifest.py +292 -0
- graph_agents_cli/scaffold/utils/merge.py +970 -0
- graph_agents_cli/scaffold/utils/merge3.py +216 -0
- graph_agents_cli/scaffold/utils/openapi_seed.py +199 -0
- graph_agents_cli/scaffold/utils/remote_template.py +376 -0
- graph_agents_cli/scaffold/utils/template.py +1352 -0
- graph_agents_cli/scaffold/utils/upgrade.py +894 -0
- graph_agents_cli/scaffold/utils/version.py +438 -0
- graph_agents_cli/secrets/__init__.py +15 -0
- graph_agents_cli/secrets/_apply.py +954 -0
- graph_agents_cli/secrets/_required.py +188 -0
- graph_agents_cli/secrets/cmd_secrets.py +211 -0
- graph_agents_cli/setup/__init__.py +13 -0
- graph_agents_cli/setup/_antigravity.py +221 -0
- graph_agents_cli/setup/cmd_auth.py +1030 -0
- graph_agents_cli/setup/cmd_dev_token.py +513 -0
- graph_agents_cli/setup/cmd_setup.py +428 -0
- graph_agents_cli/setup/cmd_update.py +140 -0
- graph_agents_cli/skills/__init__.py +13 -0
- graph_agents_cli/skills/_bundle.py +65 -0
- graph_agents_cli/skills/data/README.md +19 -0
- graph_agents_cli/skills/data/graph-agents-cli-deploy/SKILL.md +357 -0
- graph_agents_cli/skills/data/graph-agents-cli-deploy/references/github-settings.md +113 -0
- graph_agents_cli/skills/data/graph-agents-cli-deploy/references/gitops.md +137 -0
- graph_agents_cli/skills/data/graph-agents-cli-deploy/references/kubernetes.md +315 -0
- graph_agents_cli/skills/data/graph-agents-cli-deploy/references/secrets.md +160 -0
- graph_agents_cli/skills/data/graph-agents-cli-eval/SKILL.md +303 -0
- graph_agents_cli/skills/data/graph-agents-cli-eval/references/dataset_schema.md +282 -0
- graph_agents_cli/skills/data/graph-agents-cli-eval/references/metrics-guide.md +143 -0
- graph_agents_cli/skills/data/graph-agents-cli-langgraph-code/SKILL.md +659 -0
- graph_agents_cli/skills/data/graph-agents-cli-langgraph-code/references/langchain-models.md +124 -0
- graph_agents_cli/skills/data/graph-agents-cli-langgraph-code/references/langgraph.md +235 -0
- graph_agents_cli/skills/data/graph-agents-cli-langgraph-code/references/template-contract.md +477 -0
- graph_agents_cli/skills/data/graph-agents-cli-observability/SKILL.md +231 -0
- graph_agents_cli/skills/data/graph-agents-cli-observability/references/langsmith.md +46 -0
- graph_agents_cli/skills/data/graph-agents-cli-observability/references/otel.md +59 -0
- graph_agents_cli/skills/data/graph-agents-cli-scaffold/SKILL.md +414 -0
- graph_agents_cli/skills/data/graph-agents-cli-scaffold/references/flags.md +134 -0
- graph_agents_cli/skills/data/graph-agents-cli-workflow/SKILL.md +478 -0
- graph_agents_cli/skills/data/graph-agents-cli-workflow/references/brainstorming.md +118 -0
- graph_agents_cli/skills/data/graph-agents-cli-workflow/references/commands.md +419 -0
- graph_agents_cli/skills/data/graph-agents-cli-workflow/references/extension.md +156 -0
- graph_agents_cli/skills/data/graph-agents-cli-workflow/references/internals.md +67 -0
- graph_agents_cli/skills/data/graph-agents-cli-workflow/references/spec-template.md +56 -0
- graph_agents_cli/skills/data/graph-agents-cli-workflow/references/terminology.md +119 -0
- graph_agents_cli/system/__init__.py +15 -0
- graph_agents_cli/system/_apply.py +519 -0
- graph_agents_cli/system/_checks.py +1023 -0
- graph_agents_cli/system/_deploy.py +215 -0
- graph_agents_cli/system/_model.py +363 -0
- graph_agents_cli/system/_system.py +664 -0
- graph_agents_cli/system/_views.py +208 -0
- graph_agents_cli/system/cmd_system.py +423 -0
- graph_agents_cli-0.3.1.dist-info/METADATA +162 -0
- graph_agents_cli-0.3.1.dist-info/RECORD +291 -0
- graph_agents_cli-0.3.1.dist-info/WHEEL +4 -0
- graph_agents_cli-0.3.1.dist-info/entry_points.txt +2 -0
- graph_agents_cli-0.3.1.dist-info/licenses/LICENSE +201 -0
- graph_agents_cli-0.3.1.dist-info/licenses/NOTICE +19 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
# Copyright 2026 graph-agents-cli contributors
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Eval dataset loading and validation (``tests/eval/datasets/*.json``).
|
|
16
|
+
|
|
17
|
+
Dataset shape::
|
|
18
|
+
|
|
19
|
+
{"cases": [{"id": "greeting",
|
|
20
|
+
"messages": [{"role": "user", "content": "hi"}],
|
|
21
|
+
"approvals": [{"decision": "approve", "match": {"operation_id": "cancelOrder"}}],
|
|
22
|
+
"expect": {...}, "judge": {"response_quality": {"threshold": 4}},
|
|
23
|
+
"reference": "...", "context": "...", "metadata": {}}]}
|
|
24
|
+
|
|
25
|
+
``approvals`` tells ``eval generate`` how to decide each call the agent's
|
|
26
|
+
policy gates (an API's ``approval`` block): the first instruction whose
|
|
27
|
+
``match`` names the paused call decides it. A gate no instruction matches is
|
|
28
|
+
a case error: a case must say what a human would do.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
from dataclasses import dataclass, field
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
from typing import Any
|
|
36
|
+
|
|
37
|
+
from graph_agents_cli._approvals import match_problem
|
|
38
|
+
from graph_agents_cli.eval._common import EvalConfigError, canonical_hash, load_json_file
|
|
39
|
+
|
|
40
|
+
EXPECT_KEYS: tuple[str, ...] = (
|
|
41
|
+
"contains",
|
|
42
|
+
"not_contains",
|
|
43
|
+
"regex",
|
|
44
|
+
"json_schema",
|
|
45
|
+
"tool_calls",
|
|
46
|
+
"ordered",
|
|
47
|
+
"no_tool_calls",
|
|
48
|
+
"max_latency_ms",
|
|
49
|
+
"max_tokens",
|
|
50
|
+
"case_insensitive",
|
|
51
|
+
"scope",
|
|
52
|
+
"approvals",
|
|
53
|
+
"no_approvals",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# A case's `approvals` instructions: how generate decides a gated call.
|
|
57
|
+
APPROVAL_DECISIONS: tuple[str, ...] = ("approve", "reject")
|
|
58
|
+
APPROVAL_INSTRUCTION_KEYS: tuple[str, ...] = ("decision", "match")
|
|
59
|
+
# `expect.approvals` items: which gate the run hit, and how it ended.
|
|
60
|
+
APPROVAL_OUTCOMES: tuple[str, ...] = ("gated", "approved", "rejected")
|
|
61
|
+
APPROVAL_EXPECT_KEYS: tuple[str, ...] = ("match", "status")
|
|
62
|
+
|
|
63
|
+
# Which turns of a multi-turn case the expect checks read: the final turn (the
|
|
64
|
+
# reply being graded) or every turn. A single-turn case is the same either way.
|
|
65
|
+
SCOPE_FINAL_TURN = "final_turn"
|
|
66
|
+
SCOPE_ALL_TURNS = "all_turns"
|
|
67
|
+
EXPECT_SCOPES: tuple[str, ...] = (SCOPE_FINAL_TURN, SCOPE_ALL_TURNS)
|
|
68
|
+
|
|
69
|
+
EXPECT_DEFAULTS: dict[str, Any] = {
|
|
70
|
+
"contains": [],
|
|
71
|
+
"not_contains": [],
|
|
72
|
+
"regex": None,
|
|
73
|
+
"json_schema": None,
|
|
74
|
+
"tool_calls": None,
|
|
75
|
+
"ordered": False,
|
|
76
|
+
"no_tool_calls": False,
|
|
77
|
+
"max_latency_ms": None,
|
|
78
|
+
"max_tokens": None,
|
|
79
|
+
# contains / not_contains compare case-insensitively unless this is false:
|
|
80
|
+
# a refusal check such as not_contains ["deleted"] must also catch "Deleted".
|
|
81
|
+
"case_insensitive": True,
|
|
82
|
+
"scope": SCOPE_FINAL_TURN,
|
|
83
|
+
"approvals": None,
|
|
84
|
+
"no_approvals": False,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
MESSAGE_ROLES: tuple[str, ...] = ("user", "assistant", "system")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@dataclass
|
|
91
|
+
class EvalCase:
|
|
92
|
+
id: str
|
|
93
|
+
messages: list[dict[str, Any]]
|
|
94
|
+
expect: dict[str, Any] = field(default_factory=lambda: dict(EXPECT_DEFAULTS))
|
|
95
|
+
judge: dict[str, dict[str, Any]] = field(default_factory=dict)
|
|
96
|
+
reference: str | None = None
|
|
97
|
+
context: str | None = None
|
|
98
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
99
|
+
raw: dict[str, Any] = field(default_factory=dict)
|
|
100
|
+
# How generate decides the gated calls this case reaches ({decision, match}).
|
|
101
|
+
approvals: list[dict[str, Any]] = field(default_factory=list)
|
|
102
|
+
|
|
103
|
+
def user_messages(self) -> list[str]:
|
|
104
|
+
return [str(m.get("content", "")) for m in self.messages if m.get("role") == "user"]
|
|
105
|
+
|
|
106
|
+
def conversation_text(self) -> str:
|
|
107
|
+
return "\n".join(f"{m.get('role', '?')}: {m.get('content', '')}" for m in self.messages)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@dataclass
|
|
111
|
+
class Dataset:
|
|
112
|
+
cases: list[EvalCase]
|
|
113
|
+
hash: str
|
|
114
|
+
sources: list[Path] = field(default_factory=list)
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def case_ids(self) -> list[str]:
|
|
118
|
+
return [c.id for c in self.cases]
|
|
119
|
+
|
|
120
|
+
def by_id(self) -> dict[str, EvalCase]:
|
|
121
|
+
return {c.id: c for c in self.cases}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _where(source: Path | None, index: int) -> str:
|
|
125
|
+
return f"{source}: cases[{index}]" if source else f"cases[{index}]"
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def parse_case(raw: Any, index: int = 0, source: Path | None = None) -> EvalCase:
|
|
129
|
+
"""Validate one raw case dict into an :class:`EvalCase`.
|
|
130
|
+
|
|
131
|
+
Raises :class:`EvalConfigError` (exit 3) for a malformed case.
|
|
132
|
+
"""
|
|
133
|
+
where = _where(source, index)
|
|
134
|
+
if not isinstance(raw, dict):
|
|
135
|
+
raise EvalConfigError(f"{where}: a case must be an object")
|
|
136
|
+
case_id = raw.get("id")
|
|
137
|
+
if not isinstance(case_id, str) or not case_id.strip():
|
|
138
|
+
raise EvalConfigError(f"{where}: 'id' must be a non-empty string")
|
|
139
|
+
|
|
140
|
+
messages = raw.get("messages")
|
|
141
|
+
if not isinstance(messages, list) or not messages:
|
|
142
|
+
raise EvalConfigError(f"{where} ({case_id}): 'messages' must be a non-empty list")
|
|
143
|
+
for i, message in enumerate(messages):
|
|
144
|
+
if not isinstance(message, dict):
|
|
145
|
+
raise EvalConfigError(f"{where} ({case_id}): messages[{i}] must be an object")
|
|
146
|
+
role = message.get("role")
|
|
147
|
+
if role not in MESSAGE_ROLES:
|
|
148
|
+
raise EvalConfigError(
|
|
149
|
+
f"{where} ({case_id}): messages[{i}].role must be one of {', '.join(MESSAGE_ROLES)}"
|
|
150
|
+
)
|
|
151
|
+
if not isinstance(message.get("content"), str):
|
|
152
|
+
raise EvalConfigError(f"{where} ({case_id}): messages[{i}].content must be a string")
|
|
153
|
+
if not any(m.get("role") == "user" for m in messages):
|
|
154
|
+
raise EvalConfigError(f"{where} ({case_id}): 'messages' must contain a user message")
|
|
155
|
+
|
|
156
|
+
expect_raw = raw.get("expect")
|
|
157
|
+
expect = dict(EXPECT_DEFAULTS)
|
|
158
|
+
if expect_raw is not None:
|
|
159
|
+
if not isinstance(expect_raw, dict):
|
|
160
|
+
raise EvalConfigError(f"{where} ({case_id}): 'expect' must be an object")
|
|
161
|
+
unknown = sorted(set(expect_raw) - set(EXPECT_KEYS))
|
|
162
|
+
if unknown:
|
|
163
|
+
raise EvalConfigError(
|
|
164
|
+
f"{where} ({case_id}): unknown expect key(s) {', '.join(unknown)}; "
|
|
165
|
+
f"known: {', '.join(EXPECT_KEYS)}"
|
|
166
|
+
)
|
|
167
|
+
for key, value in expect_raw.items():
|
|
168
|
+
if value is not None:
|
|
169
|
+
expect[key] = value
|
|
170
|
+
_validate_expect(expect, f"{where} ({case_id})")
|
|
171
|
+
|
|
172
|
+
judge_raw = raw.get("judge")
|
|
173
|
+
judge: dict[str, dict[str, Any]] = {}
|
|
174
|
+
if judge_raw is not None:
|
|
175
|
+
if not isinstance(judge_raw, dict):
|
|
176
|
+
raise EvalConfigError(f"{where} ({case_id}): 'judge' must be an object")
|
|
177
|
+
for name, spec in judge_raw.items():
|
|
178
|
+
if spec is None:
|
|
179
|
+
spec = {}
|
|
180
|
+
elif isinstance(spec, int | float) and not isinstance(spec, bool):
|
|
181
|
+
spec = {"threshold": spec}
|
|
182
|
+
if not isinstance(spec, dict):
|
|
183
|
+
raise EvalConfigError(
|
|
184
|
+
f"{where} ({case_id}): judge.{name} must be an object like {{'threshold': 4}}"
|
|
185
|
+
)
|
|
186
|
+
threshold = spec.get("threshold")
|
|
187
|
+
if threshold is not None and (
|
|
188
|
+
isinstance(threshold, bool) or not isinstance(threshold, int | float)
|
|
189
|
+
):
|
|
190
|
+
raise EvalConfigError(
|
|
191
|
+
f"{where} ({case_id}): judge.{name}.threshold must be a number"
|
|
192
|
+
)
|
|
193
|
+
judge[str(name)] = dict(spec)
|
|
194
|
+
|
|
195
|
+
for text_key in ("reference", "context"):
|
|
196
|
+
value = raw.get(text_key)
|
|
197
|
+
if value is not None and not isinstance(value, str):
|
|
198
|
+
raise EvalConfigError(f"{where} ({case_id}): '{text_key}' must be a string")
|
|
199
|
+
|
|
200
|
+
metadata = raw.get("metadata") or {}
|
|
201
|
+
if not isinstance(metadata, dict):
|
|
202
|
+
raise EvalConfigError(f"{where} ({case_id}): 'metadata' must be an object")
|
|
203
|
+
|
|
204
|
+
return EvalCase(
|
|
205
|
+
id=case_id,
|
|
206
|
+
messages=messages,
|
|
207
|
+
expect=expect,
|
|
208
|
+
judge=judge,
|
|
209
|
+
reference=raw.get("reference"),
|
|
210
|
+
context=raw.get("context"),
|
|
211
|
+
metadata=metadata,
|
|
212
|
+
raw=raw,
|
|
213
|
+
approvals=_parse_approval_instructions(raw.get("approvals"), f"{where} ({case_id})"),
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _match_error(match: Any, where: str) -> None:
|
|
218
|
+
problem = match_problem(match)
|
|
219
|
+
if problem:
|
|
220
|
+
raise EvalConfigError(f"{where}.match: {problem}")
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _parse_approval_instructions(value: Any, where: str) -> list[dict[str, Any]]:
|
|
224
|
+
"""A case's ``approvals``: ``[{"decision": "approve"|"reject", "match": {...}}]``."""
|
|
225
|
+
if value is None:
|
|
226
|
+
return []
|
|
227
|
+
if not isinstance(value, list):
|
|
228
|
+
raise EvalConfigError(f"{where}: 'approvals' must be a list of {{decision, match}}")
|
|
229
|
+
instructions = []
|
|
230
|
+
for index, item in enumerate(value):
|
|
231
|
+
at = f"{where}: approvals[{index}]"
|
|
232
|
+
if not isinstance(item, dict):
|
|
233
|
+
raise EvalConfigError(f"{at} must be an object with decision and match")
|
|
234
|
+
unknown = sorted(set(item) - set(APPROVAL_INSTRUCTION_KEYS), key=str)
|
|
235
|
+
if unknown:
|
|
236
|
+
raise EvalConfigError(
|
|
237
|
+
f"{at}: unknown key(s) {', '.join(map(str, unknown))} "
|
|
238
|
+
f"(known: {', '.join(APPROVAL_INSTRUCTION_KEYS)})"
|
|
239
|
+
)
|
|
240
|
+
if item.get("decision") not in APPROVAL_DECISIONS:
|
|
241
|
+
raise EvalConfigError(f"{at}.decision must be one of {', '.join(APPROVAL_DECISIONS)}")
|
|
242
|
+
_match_error(item.get("match"), at)
|
|
243
|
+
instructions.append({"decision": item["decision"], "match": dict(item["match"])})
|
|
244
|
+
return instructions
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _validate_expect(expect: dict[str, Any], where: str) -> None:
|
|
248
|
+
for key in ("contains", "not_contains"):
|
|
249
|
+
value = expect[key]
|
|
250
|
+
if isinstance(value, str):
|
|
251
|
+
expect[key] = [value]
|
|
252
|
+
elif not isinstance(value, list) or not all(isinstance(v, str) for v in value):
|
|
253
|
+
raise EvalConfigError(f"{where}: expect.{key} must be a list of strings")
|
|
254
|
+
if expect["regex"] is not None and not isinstance(expect["regex"], str):
|
|
255
|
+
raise EvalConfigError(f"{where}: expect.regex must be a string")
|
|
256
|
+
if expect["json_schema"] is not None and not isinstance(expect["json_schema"], dict | bool):
|
|
257
|
+
raise EvalConfigError(f"{where}: expect.json_schema must be a JSON schema object")
|
|
258
|
+
tool_calls = expect["tool_calls"]
|
|
259
|
+
if tool_calls is not None:
|
|
260
|
+
if not isinstance(tool_calls, list):
|
|
261
|
+
raise EvalConfigError(f"{where}: expect.tool_calls must be a list")
|
|
262
|
+
for i, call in enumerate(tool_calls):
|
|
263
|
+
if isinstance(call, str):
|
|
264
|
+
tool_calls[i] = {"name": call}
|
|
265
|
+
continue
|
|
266
|
+
if not isinstance(call, dict) or not isinstance(call.get("name"), str):
|
|
267
|
+
raise EvalConfigError(
|
|
268
|
+
f"{where}: expect.tool_calls[{i}] must be {{'name': ..., 'args_subset': {{...}}}}"
|
|
269
|
+
)
|
|
270
|
+
if call.get("args_subset") is not None and not isinstance(call["args_subset"], dict):
|
|
271
|
+
raise EvalConfigError(
|
|
272
|
+
f"{where}: expect.tool_calls[{i}].args_subset must be an object"
|
|
273
|
+
)
|
|
274
|
+
for key in ("ordered", "no_tool_calls", "case_insensitive"):
|
|
275
|
+
if not isinstance(expect[key], bool):
|
|
276
|
+
raise EvalConfigError(f"{where}: expect.{key} must be true or false")
|
|
277
|
+
if expect["scope"] not in EXPECT_SCOPES:
|
|
278
|
+
raise EvalConfigError(
|
|
279
|
+
f"{where}: expect.scope must be one of {', '.join(EXPECT_SCOPES)} "
|
|
280
|
+
f"(got {expect['scope']!r})"
|
|
281
|
+
)
|
|
282
|
+
for key in ("max_latency_ms", "max_tokens"):
|
|
283
|
+
value = expect[key]
|
|
284
|
+
if value is not None and (isinstance(value, bool) or not isinstance(value, int | float)):
|
|
285
|
+
raise EvalConfigError(f"{where}: expect.{key} must be a number")
|
|
286
|
+
if expect["no_tool_calls"] and expect["tool_calls"]:
|
|
287
|
+
raise EvalConfigError(f"{where}: expect.no_tool_calls and expect.tool_calls conflict")
|
|
288
|
+
if not isinstance(expect["no_approvals"], bool):
|
|
289
|
+
raise EvalConfigError(f"{where}: expect.no_approvals must be true or false")
|
|
290
|
+
approvals = expect["approvals"]
|
|
291
|
+
if approvals is not None:
|
|
292
|
+
if not isinstance(approvals, list):
|
|
293
|
+
raise EvalConfigError(f"{where}: expect.approvals must be a list of {{match, status}}")
|
|
294
|
+
if not approvals:
|
|
295
|
+
# An empty list would check nothing and always pass.
|
|
296
|
+
raise EvalConfigError(
|
|
297
|
+
f"{where}: expect.approvals is empty (it would check nothing); list the gated "
|
|
298
|
+
"calls, or use expect.no_approvals: true"
|
|
299
|
+
)
|
|
300
|
+
for i, item in enumerate(approvals):
|
|
301
|
+
at = f"{where}: expect.approvals[{i}]"
|
|
302
|
+
if not isinstance(item, dict):
|
|
303
|
+
raise EvalConfigError(f"{at} must be an object with match (and status)")
|
|
304
|
+
unknown = sorted(set(item) - set(APPROVAL_EXPECT_KEYS), key=str)
|
|
305
|
+
if unknown:
|
|
306
|
+
raise EvalConfigError(
|
|
307
|
+
f"{at}: unknown key(s) {', '.join(map(str, unknown))} "
|
|
308
|
+
f"(known: {', '.join(APPROVAL_EXPECT_KEYS)})"
|
|
309
|
+
)
|
|
310
|
+
if item.get("status", "gated") not in APPROVAL_OUTCOMES:
|
|
311
|
+
raise EvalConfigError(f"{at}.status must be one of {', '.join(APPROVAL_OUTCOMES)}")
|
|
312
|
+
_match_error(item.get("match"), at)
|
|
313
|
+
if expect["no_approvals"] and expect["approvals"]:
|
|
314
|
+
raise EvalConfigError(f"{where}: expect.no_approvals and expect.approvals conflict")
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def dataset_hash(raw_cases: list[dict[str, Any]]) -> str:
|
|
318
|
+
"""The dataset hash recorded in traces and results: over the canonical cases list."""
|
|
319
|
+
return canonical_hash({"cases": raw_cases})
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def load_dataset(paths: list[Path]) -> Dataset:
|
|
323
|
+
"""Load and merge one or more dataset files; ids must be unique across them."""
|
|
324
|
+
if not paths:
|
|
325
|
+
raise EvalConfigError("no dataset files given")
|
|
326
|
+
cases: list[EvalCase] = []
|
|
327
|
+
raw_cases: list[dict[str, Any]] = []
|
|
328
|
+
seen: dict[str, Path] = {}
|
|
329
|
+
for path in paths:
|
|
330
|
+
data = load_json_file(path, "dataset")
|
|
331
|
+
if not isinstance(data, dict) or not isinstance(data.get("cases"), list):
|
|
332
|
+
raise EvalConfigError(f"dataset {path} must be an object with a 'cases' list")
|
|
333
|
+
if not data["cases"]:
|
|
334
|
+
raise EvalConfigError(f"dataset {path} has no cases")
|
|
335
|
+
for index, raw in enumerate(data["cases"]):
|
|
336
|
+
case = parse_case(raw, index, path)
|
|
337
|
+
if case.id in seen:
|
|
338
|
+
raise EvalConfigError(
|
|
339
|
+
f"duplicate case id {case.id!r} in {path} (first seen in {seen[case.id]})"
|
|
340
|
+
)
|
|
341
|
+
seen[case.id] = path
|
|
342
|
+
cases.append(case)
|
|
343
|
+
raw_cases.append(raw)
|
|
344
|
+
return Dataset(cases=cases, hash=dataset_hash(raw_cases), sources=list(paths))
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def cases_from_raw(raw_cases: list[dict[str, Any]]) -> Dataset:
|
|
348
|
+
"""Build a :class:`Dataset` from case dicts embedded in a trace file."""
|
|
349
|
+
cases = [parse_case(raw, i) for i, raw in enumerate(raw_cases)]
|
|
350
|
+
return Dataset(cases=cases, hash=dataset_hash(raw_cases))
|