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,361 @@
|
|
|
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
|
+
"""HTTP plumbing shared by every route: request ids, metrics, the body cap, SSE responses.
|
|
16
|
+
|
|
17
|
+
* `RequestContextMiddleware`: takes the caller's `X-Request-ID` (letters,
|
|
18
|
+
digits and `._:-`, at most 128) or makes one, returns it on the response,
|
|
19
|
+
binds it to every log record of the request, continues the caller's W3C
|
|
20
|
+
trace (`traceparent`, under OTLP tracing), and records the request metrics.
|
|
21
|
+
* `BodySizeLimitMiddleware`: refuses a body over `MAX_REQUEST_BYTES` with 413,
|
|
22
|
+
from `Content-Length` before reading anything, or while reading a chunked body.
|
|
23
|
+
* `max_message_chars()`: `MAX_MESSAGE_CHARS` (default 32000), the longest user
|
|
24
|
+
message a run accepts, on every surface that reaches the model (`/chat`
|
|
25
|
+
answers 422 above it, A2A an invalid-params error).
|
|
26
|
+
* `RunStreamingResponse`: the `/chat` SSE response. It watches for the client
|
|
27
|
+
going away and cancels the stream right then (the run is recorded as
|
|
28
|
+
`cancelled` and stops spending model tokens), and it always calls
|
|
29
|
+
`on_close` (which releases the thread's run lock), even when the stream
|
|
30
|
+
never started.
|
|
31
|
+
|
|
32
|
+
Under langgraph-server the server installs this app's middleware for every
|
|
33
|
+
route, its own native API included, and two more apply there only:
|
|
34
|
+
|
|
35
|
+
* `AuthErrorMiddleware`: the server answers an auth failure on its native API
|
|
36
|
+
with a bare 401 (the policy's `WWW-Authenticate` challenge dropped) and lets
|
|
37
|
+
any other status escape as an exception (a 500). This puts the challenge
|
|
38
|
+
back and answers an escaped auth error (for example the 503 of a
|
|
39
|
+
misconfigured policy or an unreachable token issuer) with its own status.
|
|
40
|
+
* `ThreadDeleteHookMiddleware`: after the server's own
|
|
41
|
+
`DELETE /threads/{thread_id}` succeeds, drops that thread's run records and
|
|
42
|
+
its A2A tasks.
|
|
43
|
+
|
|
44
|
+
Both rely on the server's default middleware order (this app's middleware
|
|
45
|
+
around the server's auth; no `"middleware_order": "auth_first"`).
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
from __future__ import annotations
|
|
49
|
+
|
|
50
|
+
import asyncio
|
|
51
|
+
import logging
|
|
52
|
+
import os
|
|
53
|
+
import re
|
|
54
|
+
import time
|
|
55
|
+
import uuid
|
|
56
|
+
from collections.abc import Awaitable, Callable
|
|
57
|
+
from typing import Any
|
|
58
|
+
|
|
59
|
+
from fastapi import HTTPException
|
|
60
|
+
from fastapi.responses import JSONResponse, StreamingResponse
|
|
61
|
+
from starlette.datastructures import MutableHeaders
|
|
62
|
+
from starlette.types import ASGIApp, Message, Receive, Scope, Send
|
|
63
|
+
|
|
64
|
+
from {{cookiecutter.agent_directory}}.app_utils.auth import AUTH_CHALLENGE_STATE_KEY
|
|
65
|
+
from {{cookiecutter.agent_directory}}.app_utils.limits import SettingsError, max_request_bytes
|
|
66
|
+
from {{cookiecutter.agent_directory}}.app_utils.metrics import observe_request, route_label
|
|
67
|
+
from {{cookiecutter.agent_directory}}.app_utils.telemetry import (
|
|
68
|
+
attach_trace_context,
|
|
69
|
+
bind_log_context,
|
|
70
|
+
detach_trace_context,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
logger = logging.getLogger(__name__)
|
|
74
|
+
|
|
75
|
+
MAX_MESSAGE_CHARS = ("MAX_MESSAGE_CHARS", 32_000)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def max_message_chars() -> int:
|
|
79
|
+
"""`MAX_MESSAGE_CHARS`: the longest user message (in characters) a run accepts.
|
|
80
|
+
|
|
81
|
+
One cap for every surface that reaches the model (`/chat` and A2A), so a
|
|
82
|
+
message refused on one is refused on the other. Anything but a whole
|
|
83
|
+
number >= 1 is a `SettingsError`, reported at startup with every other
|
|
84
|
+
bad setting.
|
|
85
|
+
"""
|
|
86
|
+
name, default = MAX_MESSAGE_CHARS
|
|
87
|
+
raw = (os.environ.get(name) or "").strip()
|
|
88
|
+
if not raw:
|
|
89
|
+
return default
|
|
90
|
+
try:
|
|
91
|
+
value = int(raw)
|
|
92
|
+
except ValueError:
|
|
93
|
+
raise SettingsError(f"{name}={raw!r} is not an integer.") from None
|
|
94
|
+
if value < 1:
|
|
95
|
+
raise SettingsError(f"{name}={value} must be >= 1.")
|
|
96
|
+
return value
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
REQUEST_ID_HEADER = "x-request-id"
|
|
100
|
+
_REQUEST_ID = re.compile(r"^[A-Za-z0-9._:-]{1,128}$")
|
|
101
|
+
# The server's native thread delete: `DELETE /threads/{thread_id}` (any mount prefix).
|
|
102
|
+
_THREAD_DELETE_PATH = re.compile(r"(?:^|/)threads/([^/]+)/?$")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _header(scope: Scope, name: bytes) -> str | None:
|
|
106
|
+
for key, value in scope.get("headers") or ():
|
|
107
|
+
if key.lower() == name:
|
|
108
|
+
return value.decode("latin-1")
|
|
109
|
+
return None
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class RequestContextMiddleware:
|
|
113
|
+
def __init__(self, app: ASGIApp) -> None:
|
|
114
|
+
self.app = app
|
|
115
|
+
|
|
116
|
+
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
|
117
|
+
if scope["type"] != "http":
|
|
118
|
+
await self.app(scope, receive, send)
|
|
119
|
+
return
|
|
120
|
+
incoming = _header(scope, REQUEST_ID_HEADER.encode())
|
|
121
|
+
if incoming and _REQUEST_ID.fullmatch(incoming):
|
|
122
|
+
request_id = incoming
|
|
123
|
+
else:
|
|
124
|
+
request_id = uuid.uuid4().hex
|
|
125
|
+
# Downstream code (LangGraph Server's own request-id middleware
|
|
126
|
+
# included) sees the same id.
|
|
127
|
+
scope["headers"] = [
|
|
128
|
+
(k, v) for k, v in scope.get("headers") or () if k.lower() != b"x-request-id"
|
|
129
|
+
] + [(b"x-request-id", request_id.encode())]
|
|
130
|
+
scope.setdefault("state", {})["request_id"] = request_id
|
|
131
|
+
bind_log_context(
|
|
132
|
+
request_id=request_id, run_id=None, thread_id=None, principal_hash=None, actor=None
|
|
133
|
+
)
|
|
134
|
+
status = 500
|
|
135
|
+
started = time.perf_counter()
|
|
136
|
+
|
|
137
|
+
async def send_with_id(message: Message) -> None:
|
|
138
|
+
nonlocal status
|
|
139
|
+
if message["type"] == "http.response.start":
|
|
140
|
+
status = int(message["status"])
|
|
141
|
+
MutableHeaders(scope=message)[REQUEST_ID_HEADER] = request_id
|
|
142
|
+
await send(message)
|
|
143
|
+
|
|
144
|
+
# A caller's W3C trace context (another agent's `traceparent`): this request's
|
|
145
|
+
# spans join its trace (OTLP tracing only; under PROPAGATE_TRACE_HEADERS=peers, the
|
|
146
|
+
# default, on the A2A routes only; see telemetry.attach_trace_context).
|
|
147
|
+
trace_token = attach_trace_context(scope.get("headers") or (), _app_path(scope))
|
|
148
|
+
try:
|
|
149
|
+
await self.app(scope, receive, send_with_id)
|
|
150
|
+
finally:
|
|
151
|
+
detach_trace_context(trace_token)
|
|
152
|
+
observe_request(
|
|
153
|
+
scope.get("method", "GET"),
|
|
154
|
+
route_label(scope),
|
|
155
|
+
status,
|
|
156
|
+
time.perf_counter() - started,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _app_path(scope: Scope) -> str:
|
|
161
|
+
"""The request's path within the app (`root_path` taken off when the path carries it)."""
|
|
162
|
+
path = str(scope.get("path") or "")
|
|
163
|
+
root = str(scope.get("root_path") or "").rstrip("/")
|
|
164
|
+
return path[len(root) :] if root and path.startswith(root + "/") else path
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
async def read_body(receive: Receive) -> bytes:
|
|
168
|
+
"""The whole request body, read from `receive` (pair it with `replay_body`)."""
|
|
169
|
+
chunks: list[bytes] = []
|
|
170
|
+
while True:
|
|
171
|
+
message = await receive()
|
|
172
|
+
if message["type"] != "http.request":
|
|
173
|
+
break # the client left
|
|
174
|
+
chunks.append(message.get("body") or b"")
|
|
175
|
+
if not message.get("more_body"):
|
|
176
|
+
break
|
|
177
|
+
return b"".join(chunks)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def replay_body(body: bytes, receive: Receive) -> Receive:
|
|
181
|
+
"""A `receive` that hands the app `body` (already read), then the connection's own messages."""
|
|
182
|
+
replayed = False
|
|
183
|
+
|
|
184
|
+
async def replay() -> Message:
|
|
185
|
+
nonlocal replayed
|
|
186
|
+
if not replayed:
|
|
187
|
+
replayed = True
|
|
188
|
+
return {"type": "http.request", "body": body, "more_body": False}
|
|
189
|
+
return await receive()
|
|
190
|
+
|
|
191
|
+
return replay
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class BodySizeLimitMiddleware:
|
|
195
|
+
def __init__(self, app: ASGIApp) -> None:
|
|
196
|
+
self.app = app
|
|
197
|
+
|
|
198
|
+
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
|
199
|
+
if scope["type"] != "http":
|
|
200
|
+
await self.app(scope, receive, send)
|
|
201
|
+
return
|
|
202
|
+
limit = max_request_bytes()
|
|
203
|
+
detail = f"Request body exceeds MAX_REQUEST_BYTES ({limit} bytes)."
|
|
204
|
+
declared = _header(scope, b"content-length")
|
|
205
|
+
if declared is not None:
|
|
206
|
+
try:
|
|
207
|
+
too_large = int(declared) > limit
|
|
208
|
+
except ValueError:
|
|
209
|
+
too_large = False # the server rejects a malformed length itself
|
|
210
|
+
if too_large:
|
|
211
|
+
await JSONResponse({"detail": detail}, status_code=413)(scope, receive, send)
|
|
212
|
+
return
|
|
213
|
+
received = 0
|
|
214
|
+
|
|
215
|
+
async def limited_receive() -> Message:
|
|
216
|
+
nonlocal received
|
|
217
|
+
message = await receive()
|
|
218
|
+
if message["type"] == "http.request":
|
|
219
|
+
received += len(message.get("body") or b"")
|
|
220
|
+
if received > limit:
|
|
221
|
+
# Raised inside the route's body read: FastAPI and Starlette
|
|
222
|
+
# both turn it into the 413 response.
|
|
223
|
+
raise HTTPException(status_code=413, detail=detail)
|
|
224
|
+
return message
|
|
225
|
+
|
|
226
|
+
await self.app(scope, limited_receive, send)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class RunStreamingResponse(StreamingResponse):
|
|
230
|
+
"""A streaming response that stops as soon as the client disconnects."""
|
|
231
|
+
|
|
232
|
+
def __init__(
|
|
233
|
+
self,
|
|
234
|
+
content: Any,
|
|
235
|
+
*,
|
|
236
|
+
on_close: Callable[[], Awaitable[None]] | None = None,
|
|
237
|
+
**kwargs: Any,
|
|
238
|
+
) -> None:
|
|
239
|
+
super().__init__(content, **kwargs)
|
|
240
|
+
self._on_close = on_close
|
|
241
|
+
|
|
242
|
+
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
|
243
|
+
task = asyncio.current_task()
|
|
244
|
+
disconnected = False
|
|
245
|
+
|
|
246
|
+
async def watch_disconnect() -> None:
|
|
247
|
+
nonlocal disconnected
|
|
248
|
+
while True:
|
|
249
|
+
message = await receive()
|
|
250
|
+
if message["type"] == "http.disconnect":
|
|
251
|
+
disconnected = True
|
|
252
|
+
if task is not None:
|
|
253
|
+
task.cancel()
|
|
254
|
+
return
|
|
255
|
+
|
|
256
|
+
watcher = asyncio.ensure_future(watch_disconnect())
|
|
257
|
+
try:
|
|
258
|
+
await self.stream_response(send)
|
|
259
|
+
except asyncio.CancelledError:
|
|
260
|
+
if not disconnected or task is None:
|
|
261
|
+
raise
|
|
262
|
+
task.uncancel() # the client left: a normal end for this request
|
|
263
|
+
except OSError:
|
|
264
|
+
pass # the client left while a chunk was being sent
|
|
265
|
+
finally:
|
|
266
|
+
watcher.cancel()
|
|
267
|
+
aclose = getattr(self.body_iterator, "aclose", None)
|
|
268
|
+
try:
|
|
269
|
+
if aclose is not None:
|
|
270
|
+
await aclose()
|
|
271
|
+
finally:
|
|
272
|
+
if self._on_close is not None:
|
|
273
|
+
await self._on_close()
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _sdk_auth_error(exc: BaseException) -> Any:
|
|
277
|
+
"""`exc` when it is a `langgraph_sdk` auth `HTTPException`, else None."""
|
|
278
|
+
try:
|
|
279
|
+
from langgraph_sdk.auth.exceptions import HTTPException as SdkHTTPException
|
|
280
|
+
except ImportError: # not under langgraph-server
|
|
281
|
+
return None
|
|
282
|
+
return exc if isinstance(exc, SdkHTTPException) else None
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
class AuthErrorMiddleware:
|
|
286
|
+
"""langgraph-server: the policy's own 401 challenge and error status on the native API."""
|
|
287
|
+
|
|
288
|
+
def __init__(self, app: ASGIApp) -> None:
|
|
289
|
+
self.app = app
|
|
290
|
+
|
|
291
|
+
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
|
292
|
+
if scope["type"] != "http":
|
|
293
|
+
await self.app(scope, receive, send)
|
|
294
|
+
return
|
|
295
|
+
# The auth handler leaves the challenge here (`auth._stash_challenge`).
|
|
296
|
+
state = scope.setdefault("state", {})
|
|
297
|
+
started = False
|
|
298
|
+
|
|
299
|
+
async def send_with_challenge(message: Message) -> None:
|
|
300
|
+
nonlocal started
|
|
301
|
+
if message["type"] == "http.response.start":
|
|
302
|
+
started = True
|
|
303
|
+
challenge = state.get(AUTH_CHALLENGE_STATE_KEY) if isinstance(state, dict) else None
|
|
304
|
+
if message["status"] == 401 and challenge:
|
|
305
|
+
headers = MutableHeaders(scope=message)
|
|
306
|
+
if "www-authenticate" not in headers:
|
|
307
|
+
headers["www-authenticate"] = challenge
|
|
308
|
+
await send(message)
|
|
309
|
+
|
|
310
|
+
try:
|
|
311
|
+
await self.app(scope, receive, send_with_challenge)
|
|
312
|
+
except Exception as exc:
|
|
313
|
+
error = _sdk_auth_error(exc)
|
|
314
|
+
if error is None or started:
|
|
315
|
+
raise
|
|
316
|
+
logger.warning("request refused by the auth policy with %s", error.status_code)
|
|
317
|
+
response = JSONResponse(
|
|
318
|
+
{"detail": str(error.detail)},
|
|
319
|
+
status_code=int(error.status_code),
|
|
320
|
+
headers=dict(error.headers or {}),
|
|
321
|
+
)
|
|
322
|
+
await response(scope, receive, send_with_challenge)
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
class ThreadDeleteHookMiddleware:
|
|
326
|
+
"""langgraph-server: `on_deleted(thread_id)` after the server's own thread delete succeeds.
|
|
327
|
+
|
|
328
|
+
The server deletes a thread's checkpoints but knows nothing of the run
|
|
329
|
+
records this app keeps (`agent_runs`) or of its A2A tasks; `on_deleted`
|
|
330
|
+
drops them. It runs only after a 2xx, so a refused delete (someone else's
|
|
331
|
+
thread, an unknown one, a busy one) never touches them.
|
|
332
|
+
"""
|
|
333
|
+
|
|
334
|
+
def __init__(self, app: ASGIApp, on_deleted: Callable[[str], Awaitable[None]]) -> None:
|
|
335
|
+
self.app = app
|
|
336
|
+
self.on_deleted = on_deleted
|
|
337
|
+
|
|
338
|
+
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
|
339
|
+
match = None
|
|
340
|
+
if scope["type"] == "http" and scope.get("method") == "DELETE":
|
|
341
|
+
match = _THREAD_DELETE_PATH.search(scope.get("path") or "")
|
|
342
|
+
if match is None:
|
|
343
|
+
await self.app(scope, receive, send)
|
|
344
|
+
return
|
|
345
|
+
status = 0
|
|
346
|
+
|
|
347
|
+
async def watch(message: Message) -> None:
|
|
348
|
+
nonlocal status
|
|
349
|
+
if message["type"] == "http.response.start":
|
|
350
|
+
status = int(message["status"])
|
|
351
|
+
await send(message)
|
|
352
|
+
|
|
353
|
+
await self.app(scope, receive, watch)
|
|
354
|
+
if 200 <= status < 300:
|
|
355
|
+
try:
|
|
356
|
+
await self.on_deleted(match.group(1))
|
|
357
|
+
except Exception:
|
|
358
|
+
logger.warning(
|
|
359
|
+
"could not remove the run records or A2A tasks of a deleted thread",
|
|
360
|
+
exc_info=True,
|
|
361
|
+
)
|