agentcore-cli 0.31.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.
- agentcore/__init__.py +1 -0
- agentcore/agent_inspector/NOTICE +9 -0
- agentcore/agent_inspector/favicon.svg +60 -0
- agentcore/agent_inspector/index.css +1 -0
- agentcore/agent_inspector/index.html +14 -0
- agentcore/agent_inspector/index.js +289 -0
- agentcore/assets/AGENTS.md +60 -0
- agentcore/assets/README.md +108 -0
- agentcore/assets/agents/AGENTS.md +218 -0
- agentcore/assets/cdk/.prettierrc +8 -0
- agentcore/assets/cdk/README.md +26 -0
- agentcore/assets/cdk/bin/cdk.ts +216 -0
- agentcore/assets/cdk/cdk.json +88 -0
- agentcore/assets/cdk/gitignore.template +9 -0
- agentcore/assets/cdk/jest.config.js +9 -0
- agentcore/assets/cdk/lib/cdk-stack.ts +291 -0
- agentcore/assets/cdk/npmignore.template +6 -0
- agentcore/assets/cdk/package.json +30 -0
- agentcore/assets/cdk/test/cdk.test.ts +31 -0
- agentcore/assets/cdk/tsconfig.json +28 -0
- agentcore/assets/container/python/Dockerfile +47 -0
- agentcore/assets/container/python/dockerignore.template +27 -0
- agentcore/assets/container/typescript/Dockerfile +25 -0
- agentcore/assets/container/typescript/dockerignore.template +24 -0
- agentcore/assets/datasets/predefined-v1.jsonl +3 -0
- agentcore/assets/datasets/simulated-v1.jsonl +3 -0
- agentcore/assets/evaluators/autoevals-lambda/execution-role-policy.json +15 -0
- agentcore/assets/evaluators/autoevals-lambda/lambda_function.py +37 -0
- agentcore/assets/evaluators/autoevals-lambda/pyproject.toml +22 -0
- agentcore/assets/evaluators/deepeval-lambda/execution-role-policy.json +15 -0
- agentcore/assets/evaluators/deepeval-lambda/lambda_function.py +29 -0
- agentcore/assets/evaluators/deepeval-lambda/pyproject.toml +19 -0
- agentcore/assets/evaluators/python-lambda/execution-role-policy.json +10 -0
- agentcore/assets/evaluators/python-lambda/lambda_function.py +19 -0
- agentcore/assets/evaluators/python-lambda/pyproject.toml +15 -0
- agentcore/assets/harness/invoke.py.template +74 -0
- agentcore/assets/mcp/python/README.md +27 -0
- agentcore/assets/mcp/python/pyproject.toml +22 -0
- agentcore/assets/mcp/python/server.py +117 -0
- agentcore/assets/mcp/python-lambda/README.md +22 -0
- agentcore/assets/mcp/python-lambda/handler.py +144 -0
- agentcore/assets/mcp/python-lambda/pyproject.toml +15 -0
- agentcore/assets/python/a2a/googleadk/base/README.md +22 -0
- agentcore/assets/python/a2a/googleadk/base/gitignore.template +41 -0
- agentcore/assets/python/a2a/googleadk/base/main.py +124 -0
- agentcore/assets/python/a2a/googleadk/base/model/__init__.py +1 -0
- agentcore/assets/python/a2a/googleadk/base/model/load.py +41 -0
- agentcore/assets/python/a2a/googleadk/base/pyproject.toml +22 -0
- agentcore/assets/python/a2a/langchain_langgraph/base/README.md +22 -0
- agentcore/assets/python/a2a/langchain_langgraph/base/gitignore.template +41 -0
- agentcore/assets/python/a2a/langchain_langgraph/base/main.py +144 -0
- agentcore/assets/python/a2a/langchain_langgraph/base/model/__init__.py +1 -0
- agentcore/assets/python/a2a/langchain_langgraph/base/model/load.py +123 -0
- agentcore/assets/python/a2a/langchain_langgraph/base/pyproject.toml +26 -0
- agentcore/assets/python/a2a/strands/base/README.md +22 -0
- agentcore/assets/python/a2a/strands/base/gitignore.template +41 -0
- agentcore/assets/python/a2a/strands/base/main.py +113 -0
- agentcore/assets/python/a2a/strands/base/model/__init__.py +1 -0
- agentcore/assets/python/a2a/strands/base/model/load.py +123 -0
- agentcore/assets/python/a2a/strands/base/pyproject.toml +23 -0
- agentcore/assets/python/a2a/strands/capabilities/memory/__init__.py +1 -0
- agentcore/assets/python/a2a/strands/capabilities/memory/session.py +46 -0
- agentcore/assets/python/agui/googleadk/base/README.md +30 -0
- agentcore/assets/python/agui/googleadk/base/gitignore.template +41 -0
- agentcore/assets/python/agui/googleadk/base/main.py +91 -0
- agentcore/assets/python/agui/googleadk/base/model/__init__.py +1 -0
- agentcore/assets/python/agui/googleadk/base/model/load.py +41 -0
- agentcore/assets/python/agui/googleadk/base/pyproject.toml +25 -0
- agentcore/assets/python/agui/langchain_langgraph/base/README.md +22 -0
- agentcore/assets/python/agui/langchain_langgraph/base/gitignore.template +41 -0
- agentcore/assets/python/agui/langchain_langgraph/base/main.py +130 -0
- agentcore/assets/python/agui/langchain_langgraph/base/model/__init__.py +1 -0
- agentcore/assets/python/agui/langchain_langgraph/base/model/load.py +123 -0
- agentcore/assets/python/agui/langchain_langgraph/base/pyproject.toml +31 -0
- agentcore/assets/python/agui/strands/base/README.md +22 -0
- agentcore/assets/python/agui/strands/base/gitignore.template +41 -0
- agentcore/assets/python/agui/strands/base/main.py +105 -0
- agentcore/assets/python/agui/strands/base/model/__init__.py +1 -0
- agentcore/assets/python/agui/strands/base/model/load.py +123 -0
- agentcore/assets/python/agui/strands/base/pyproject.toml +27 -0
- agentcore/assets/python/agui/strands/capabilities/memory/__init__.py +1 -0
- agentcore/assets/python/agui/strands/capabilities/memory/session.py +46 -0
- agentcore/assets/python/http/autogen/base/README.md +45 -0
- agentcore/assets/python/http/autogen/base/gitignore.template +41 -0
- agentcore/assets/python/http/autogen/base/main.py +145 -0
- agentcore/assets/python/http/autogen/base/mcp_client/__init__.py +1 -0
- agentcore/assets/python/http/autogen/base/mcp_client/client.py +28 -0
- agentcore/assets/python/http/autogen/base/model/__init__.py +1 -0
- agentcore/assets/python/http/autogen/base/model/load.py +136 -0
- agentcore/assets/python/http/autogen/base/pyproject.toml +34 -0
- agentcore/assets/python/http/bma/base/Dockerfile +41 -0
- agentcore/assets/python/http/bma/base/README.md +178 -0
- agentcore/assets/python/http/bma/base/client.py +200 -0
- agentcore/assets/python/http/bma/base/lifecycle/server.py +641 -0
- agentcore/assets/python/http/bma/base/otel/collector.yaml +69 -0
- agentcore/assets/python/http/bma/base/plugins/acr-report/.codex-plugin/plugin.json +6 -0
- agentcore/assets/python/http/bma/base/plugins/acr-report/skills/acr-report/SKILL.md +16 -0
- agentcore/assets/python/http/bma/base/policies/bma-acr-policy.json +11 -0
- agentcore/assets/python/http/bma/base/policies/bma-session-policy.json +35 -0
- agentcore/assets/python/http/bma/base/policies/bma-session-trust.json +20 -0
- agentcore/assets/python/http/bma/base/pyproject.toml +21 -0
- agentcore/assets/python/http/googleadk/base/README.md +45 -0
- agentcore/assets/python/http/googleadk/base/gitignore.template +41 -0
- agentcore/assets/python/http/googleadk/base/main.py +207 -0
- agentcore/assets/python/http/googleadk/base/mcp_client/__init__.py +1 -0
- agentcore/assets/python/http/googleadk/base/mcp_client/client.py +75 -0
- agentcore/assets/python/http/googleadk/base/model/__init__.py +1 -0
- agentcore/assets/python/http/googleadk/base/model/load.py +41 -0
- agentcore/assets/python/http/googleadk/base/pyproject.toml +24 -0
- agentcore/assets/python/http/langchain_langgraph/base/README.md +45 -0
- agentcore/assets/python/http/langchain_langgraph/base/gitignore.template +41 -0
- agentcore/assets/python/http/langchain_langgraph/base/main.py +227 -0
- agentcore/assets/python/http/langchain_langgraph/base/mcp_client/__init__.py +1 -0
- agentcore/assets/python/http/langchain_langgraph/base/mcp_client/client.py +79 -0
- agentcore/assets/python/http/langchain_langgraph/base/model/__init__.py +1 -0
- agentcore/assets/python/http/langchain_langgraph/base/model/load.py +123 -0
- agentcore/assets/python/http/langchain_langgraph/base/pyproject.toml +38 -0
- agentcore/assets/python/http/openaiagents/base/README.md +45 -0
- agentcore/assets/python/http/openaiagents/base/gitignore.template +41 -0
- agentcore/assets/python/http/openaiagents/base/main.py +200 -0
- agentcore/assets/python/http/openaiagents/base/mcp_client/__init__.py +1 -0
- agentcore/assets/python/http/openaiagents/base/mcp_client/client.py +74 -0
- agentcore/assets/python/http/openaiagents/base/model/__init__.py +1 -0
- agentcore/assets/python/http/openaiagents/base/model/load.py +37 -0
- agentcore/assets/python/http/openaiagents/base/pyproject.toml +21 -0
- agentcore/assets/python/http/strands/base/README.md +46 -0
- agentcore/assets/python/http/strands/base/gitignore.template +41 -0
- agentcore/assets/python/http/strands/base/main.py +714 -0
- agentcore/assets/python/http/strands/base/mcp_client/__init__.py +1 -0
- agentcore/assets/python/http/strands/base/mcp_client/client.py +116 -0
- agentcore/assets/python/http/strands/base/model/__init__.py +1 -0
- agentcore/assets/python/http/strands/base/model/load.py +239 -0
- agentcore/assets/python/http/strands/base/model/mantle_compat.py +21 -0
- agentcore/assets/python/http/strands/base/pyproject.toml +31 -0
- agentcore/assets/python/http/strands/base/skills/fetcher.py +279 -0
- agentcore/assets/python/http/strands/capabilities/execution-limits/hooks/execution_limits.py +54 -0
- agentcore/assets/python/http/strands/capabilities/memory/__init__.py +1 -0
- agentcore/assets/python/http/strands/capabilities/memory/session.py +47 -0
- agentcore/assets/python/http/strands/capabilities/payments/__init__.py +4 -0
- agentcore/assets/python/http/strands/capabilities/payments/payments.py +142 -0
- agentcore/assets/python/mcp/standalone/base/README.md +36 -0
- agentcore/assets/python/mcp/standalone/base/gitignore.template +41 -0
- agentcore/assets/python/mcp/standalone/base/main.py +83 -0
- agentcore/assets/python/mcp/standalone/base/pyproject.toml +16 -0
- agentcore/assets/typescript/.gitkeep +0 -0
- agentcore/assets/typescript/http/strands/base/README.md +42 -0
- agentcore/assets/typescript/http/strands/base/gitignore.template +22 -0
- agentcore/assets/typescript/http/strands/base/main.ts +149 -0
- agentcore/assets/typescript/http/strands/base/mcp_client/client.ts +11 -0
- agentcore/assets/typescript/http/strands/base/model/load.ts +102 -0
- agentcore/assets/typescript/http/strands/base/package.json +38 -0
- agentcore/assets/typescript/http/strands/base/tsconfig.json +19 -0
- agentcore/assets/typescript/http/strands/capabilities/memory/memory.ts +52 -0
- agentcore/assets/typescript/http/vercelai/base/README.md +42 -0
- agentcore/assets/typescript/http/vercelai/base/gitignore.template +22 -0
- agentcore/assets/typescript/http/vercelai/base/main.ts +70 -0
- agentcore/assets/typescript/http/vercelai/base/model/load.ts +121 -0
- agentcore/assets/typescript/http/vercelai/base/package.json +35 -0
- agentcore/assets/typescript/http/vercelai/base/tsconfig.json +19 -0
- agentcore/aws/__init__.py +263 -0
- agentcore/aws/_aws_errors.py +72 -0
- agentcore/aws/_http.py +176 -0
- agentcore/aws/_util.py +37 -0
- agentcore/aws/account.py +165 -0
- agentcore/aws/agentcore.py +971 -0
- agentcore/aws/agentcore_ab_tests.py +230 -0
- agentcore/aws/agentcore_batch_evaluation.py +227 -0
- agentcore/aws/agentcore_config_bundles.py +220 -0
- agentcore/aws/agentcore_control.py +994 -0
- agentcore/aws/agentcore_datasets.py +222 -0
- agentcore/aws/agentcore_harness.py +330 -0
- agentcore/aws/agentcore_payments.py +430 -0
- agentcore/aws/agentcore_recommendation.py +140 -0
- agentcore/aws/agui_parser.py +131 -0
- agentcore/aws/agui_types.py +342 -0
- agentcore/aws/api_client.py +87 -0
- agentcore/aws/arn.py +18 -0
- agentcore/aws/aws_context.py +23 -0
- agentcore/aws/bedrock.py +44 -0
- agentcore/aws/bedrock_agent.py +129 -0
- agentcore/aws/bedrock_import.py +252 -0
- agentcore/aws/bedrock_import_types.py +111 -0
- agentcore/aws/cloudwatch.py +136 -0
- agentcore/aws/connect_shell.py +266 -0
- agentcore/aws/iam.py +41 -0
- agentcore/aws/partition.py +58 -0
- agentcore/aws/policy_generation.py +116 -0
- agentcore/aws/region.py +54 -0
- agentcore/aws/retry.py +27 -0
- agentcore/aws/shell_framer.py +136 -0
- agentcore/aws/stage_endpoint.py +39 -0
- agentcore/aws/target_region.py +54 -0
- agentcore/aws/transaction_search.py +97 -0
- agentcore/cdk/__init__.py +10 -0
- agentcore/cdk/constants.py +13 -0
- agentcore/cdk/local_cdk_project.py +48 -0
- agentcore/cdk/toolkit.py +706 -0
- agentcore/cli/__init__.py +0 -0
- agentcore/cli/commands/__init__.py +0 -0
- agentcore/cli/commands/_jobs_common.py +39 -0
- agentcore/cli/commands/_run_wizards.py +639 -0
- agentcore/cli/commands/add/__init__.py +37 -0
- agentcore/cli/commands/add/_commander.py +52 -0
- agentcore/cli/commands/add/_common.py +47 -0
- agentcore/cli/commands/add/agent_wizard.py +528 -0
- agentcore/cli/commands/add/auth_options.py +154 -0
- agentcore/cli/commands/add/skill_action.py +105 -0
- agentcore/cli/commands/add/skill_command.py +87 -0
- agentcore/cli/commands/add/tool_action.py +160 -0
- agentcore/cli/commands/add/tool_command.py +117 -0
- agentcore/cli/commands/add/validate.py +333 -0
- agentcore/cli/commands/capacity_provider/__init__.py +86 -0
- agentcore/cli/commands/capacity_provider/action.py +114 -0
- agentcore/cli/commands/capacity_provider/constants.py +22 -0
- agentcore/cli/commands/config.py +91 -0
- agentcore/cli/commands/config_bundle.py +251 -0
- agentcore/cli/commands/create/__init__.py +643 -0
- agentcore/cli/commands/create/action.py +406 -0
- agentcore/cli/commands/create/dependencies.py +91 -0
- agentcore/cli/commands/create/harness_validate.py +82 -0
- agentcore/cli/commands/create/validate.py +195 -0
- agentcore/cli/commands/create/wizard.py +127 -0
- agentcore/cli/commands/dataset.py +119 -0
- agentcore/cli/commands/deploy/__init__.py +31 -0
- agentcore/cli/commands/deploy/_guards.py +25 -0
- agentcore/cli/commands/deploy/actions.py +840 -0
- agentcore/cli/commands/deploy/command.py +349 -0
- agentcore/cli/commands/deploy/progress.py +115 -0
- agentcore/cli/commands/deploy/types.py +68 -0
- agentcore/cli/commands/deploy/utils.py +43 -0
- agentcore/cli/commands/deploy/validate.py +21 -0
- agentcore/cli/commands/dev/__init__.py +5 -0
- agentcore/cli/commands/dev/_deps.py +50 -0
- agentcore/cli/commands/dev/browser_mode.py +270 -0
- agentcore/cli/commands/dev/command.py +450 -0
- agentcore/cli/commands/dev/terminal_mode.py +379 -0
- agentcore/cli/commands/exec/__init__.py +29 -0
- agentcore/cli/commands/exec/action.py +442 -0
- agentcore/cli/commands/exec/command.py +206 -0
- agentcore/cli/commands/exec/types.py +35 -0
- agentcore/cli/commands/export/__init__.py +114 -0
- agentcore/cli/commands/export/constants.py +21 -0
- agentcore/cli/commands/export/fetch_harness_spec.py +234 -0
- agentcore/cli/commands/export/harness_action.py +385 -0
- agentcore/cli/commands/export/harness_mapper.py +868 -0
- agentcore/cli/commands/export/harness_resolver.py +95 -0
- agentcore/cli/commands/export/types.py +90 -0
- agentcore/cli/commands/export/wizard.py +97 -0
- agentcore/cli/commands/feedback/__init__.py +69 -0
- agentcore/cli/commands/feedback/action.py +30 -0
- agentcore/cli/commands/feedback/consent_prompt.py +35 -0
- agentcore/cli/commands/feedback/wizard.py +49 -0
- agentcore/cli/commands/fetch/__init__.py +69 -0
- agentcore/cli/commands/fetch/action.py +62 -0
- agentcore/cli/commands/help.py +65 -0
- agentcore/cli/commands/import_/__init__.py +167 -0
- agentcore/cli/commands/import_/_js.py +72 -0
- agentcore/cli/commands/import_/actions.py +457 -0
- agentcore/cli/commands/import_/constants.py +45 -0
- agentcore/cli/commands/import_/import_evaluator.py +140 -0
- agentcore/cli/commands/import_/import_gateway.py +520 -0
- agentcore/cli/commands/import_/import_memory.py +128 -0
- agentcore/cli/commands/import_/import_online_eval.py +165 -0
- agentcore/cli/commands/import_/import_pipeline.py +145 -0
- agentcore/cli/commands/import_/import_runtime.py +214 -0
- agentcore/cli/commands/import_/import_utils.py +410 -0
- agentcore/cli/commands/import_/phase1_update.py +110 -0
- agentcore/cli/commands/import_/phase2_import.py +206 -0
- agentcore/cli/commands/import_/resource_import.py +228 -0
- agentcore/cli/commands/import_/template_utils.py +148 -0
- agentcore/cli/commands/import_/types.py +150 -0
- agentcore/cli/commands/import_/yaml_parser.py +265 -0
- agentcore/cli/commands/init.py +196 -0
- agentcore/cli/commands/invoke/__init__.py +32 -0
- agentcore/cli/commands/invoke/_deps.py +73 -0
- agentcore/cli/commands/invoke/action.py +840 -0
- agentcore/cli/commands/invoke/command.py +490 -0
- agentcore/cli/commands/invoke/constants.py +40 -0
- agentcore/cli/commands/invoke/guards.py +23 -0
- agentcore/cli/commands/invoke/interactive.py +604 -0
- agentcore/cli/commands/invoke/resolve.py +149 -0
- agentcore/cli/commands/invoke/resolve_prompt.py +60 -0
- agentcore/cli/commands/invoke/types.py +61 -0
- agentcore/cli/commands/invoke/validate.py +33 -0
- agentcore/cli/commands/job_actions.py +239 -0
- agentcore/cli/commands/job_history.py +380 -0
- agentcore/cli/commands/logs/__init__.py +53 -0
- agentcore/cli/commands/logs/action.py +159 -0
- agentcore/cli/commands/logs/filter_pattern.py +17 -0
- agentcore/cli/commands/logs/guards.py +51 -0
- agentcore/cli/commands/logs_evals.py +42 -0
- agentcore/cli/commands/package.py +118 -0
- agentcore/cli/commands/remove/__init__.py +172 -0
- agentcore/cli/commands/remove/skill_command.py +131 -0
- agentcore/cli/commands/remove/tool_command.py +59 -0
- agentcore/cli/commands/run.py +808 -0
- agentcore/cli/commands/run_ingest.py +97 -0
- agentcore/cli/commands/shared/__init__.py +0 -0
- agentcore/cli/commands/shared/arn_utils.py +9 -0
- agentcore/cli/commands/shared/filesystem_utils.py +421 -0
- agentcore/cli/commands/shared/header_utils.py +87 -0
- agentcore/cli/commands/shared/indexed_key_parser.py +76 -0
- agentcore/cli/commands/shared/lifecycle_utils.py +72 -0
- agentcore/cli/commands/shared/region_utils.py +20 -0
- agentcore/cli/commands/shared/vpc_utils.py +99 -0
- agentcore/cli/commands/status/__init__.py +30 -0
- agentcore/cli/commands/status/_guards.py +25 -0
- agentcore/cli/commands/status/action.py +741 -0
- agentcore/cli/commands/status/command.py +321 -0
- agentcore/cli/commands/status/constants.py +36 -0
- agentcore/cli/commands/status/format_knowledge_base.py +111 -0
- agentcore/cli/commands/telemetry.py +79 -0
- agentcore/cli/commands/traces/__init__.py +155 -0
- agentcore/cli/commands/traces/action.py +123 -0
- agentcore/cli/commands/update/__init__.py +63 -0
- agentcore/cli/commands/update/action.py +164 -0
- agentcore/cli/commands/validate.py +165 -0
- agentcore/cli/constants.py +96 -0
- agentcore/cli/errors.py +118 -0
- agentcore/cli/external_requirements/__init__.py +67 -0
- agentcore/cli/external_requirements/checks.py +280 -0
- agentcore/cli/external_requirements/detect.py +55 -0
- agentcore/cli/external_requirements/versions.py +57 -0
- agentcore/cli/groups.py +23 -0
- agentcore/cli/interactive.py +85 -0
- agentcore/cli/main.py +137 -0
- agentcore/cli/naming.py +11 -0
- agentcore/cli/notices.py +42 -0
- agentcore/cli/project.py +34 -0
- agentcore/cli/run_command.py +27 -0
- agentcore/cli/tree.py +135 -0
- agentcore/cli/update_notifier.py +123 -0
- agentcore/cli/variadic.py +50 -0
- agentcore/cloudformation/__init__.py +84 -0
- agentcore/cloudformation/bootstrap.py +64 -0
- agentcore/cloudformation/logical_ids.py +37 -0
- agentcore/cloudformation/outputs.py +566 -0
- agentcore/cloudformation/stack_discovery.py +75 -0
- agentcore/cloudformation/stack_events.py +107 -0
- agentcore/cloudformation/stack_status.py +98 -0
- agentcore/cloudformation/types.py +44 -0
- agentcore/lib/__init__.py +68 -0
- agentcore/lib/constants.py +77 -0
- agentcore/lib/dependency_management/__init__.py +23 -0
- agentcore/lib/dependency_management/messages.py +92 -0
- agentcore/lib/dependency_management/plan.py +98 -0
- agentcore/lib/dependency_management/semver.py +137 -0
- agentcore/lib/dependency_management/sync.py +139 -0
- agentcore/lib/dependency_management/types.py +56 -0
- agentcore/lib/errors/__init__.py +5 -0
- agentcore/lib/errors/types.py +352 -0
- agentcore/lib/errors/validation.py +121 -0
- agentcore/lib/packaging/__init__.py +81 -0
- agentcore/lib/packaging/build_args.py +23 -0
- agentcore/lib/packaging/build_context.py +26 -0
- agentcore/lib/packaging/build_context_dockerignore.py +45 -0
- agentcore/lib/packaging/container.py +95 -0
- agentcore/lib/packaging/helpers.py +289 -0
- agentcore/lib/packaging/node.py +153 -0
- agentcore/lib/packaging/python.py +135 -0
- agentcore/lib/packaging/types.py +69 -0
- agentcore/lib/packaging/uv.py +45 -0
- agentcore/lib/result.py +104 -0
- agentcore/lib/schemas/__init__.py +1 -0
- agentcore/lib/schemas/io/__init__.py +27 -0
- agentcore/lib/schemas/io/config_io.py +458 -0
- agentcore/lib/schemas/io/global_config.py +239 -0
- agentcore/lib/schemas/io/path_resolver.py +132 -0
- agentcore/lib/secrets/__init__.py +18 -0
- agentcore/lib/secrets/cipher.py +70 -0
- agentcore/lib/secrets/key_provider.py +163 -0
- agentcore/lib/secrets/sensitive_keys.py +55 -0
- agentcore/lib/time_constants.py +4 -0
- agentcore/lib/utils/__init__.py +47 -0
- agentcore/lib/utils/aws_account.py +15 -0
- agentcore/lib/utils/credentials.py +68 -0
- agentcore/lib/utils/dotenv.py +32 -0
- agentcore/lib/utils/env.py +105 -0
- agentcore/lib/utils/json_format.py +29 -0
- agentcore/lib/utils/json_rpc.py +22 -0
- agentcore/lib/utils/platform.py +33 -0
- agentcore/lib/utils/polling.py +86 -0
- agentcore/lib/utils/subprocess.py +135 -0
- agentcore/lib/utils/time_parser.py +48 -0
- agentcore/lib/utils/validation.py +106 -0
- agentcore/logging/__init__.py +10 -0
- agentcore/logging/_common.py +77 -0
- agentcore/logging/cdk_io_host.py +36 -0
- agentcore/logging/create_logger.py +107 -0
- agentcore/logging/dev_logger.py +63 -0
- agentcore/logging/exec_logger.py +159 -0
- agentcore/logging/invoke_logger.py +154 -0
- agentcore/logging/remove_logger.py +95 -0
- agentcore/operations/__init__.py +0 -0
- agentcore/operations/agent/__init__.py +0 -0
- agentcore/operations/agent/config_bundle_defaults.py +31 -0
- agentcore/operations/agent/generate/__init__.py +28 -0
- agentcore/operations/agent/generate/schema_mapper.py +319 -0
- agentcore/operations/agent/generate/types.py +172 -0
- agentcore/operations/agent/generate/write_agent_to_project.py +67 -0
- agentcore/operations/agent/import_/__init__.py +31 -0
- agentcore/operations/agent/import_/base_translator.py +449 -0
- agentcore/operations/agent/import_/constants.py +23 -0
- agentcore/operations/agent/import_/execute.py +127 -0
- agentcore/operations/agent/import_/langgraph_translator.py +294 -0
- agentcore/operations/agent/import_/pyproject_generator.py +73 -0
- agentcore/operations/agent/import_/strands_translator.py +289 -0
- agentcore/operations/config_bundle/__init__.py +1 -0
- agentcore/operations/config_bundle/bundle_name_variants.py +9 -0
- agentcore/operations/config_bundle/diff_versions.py +52 -0
- agentcore/operations/config_bundle/resolve_bundle.py +65 -0
- agentcore/operations/connectors/__init__.py +3 -0
- agentcore/operations/connectors/translators.py +63 -0
- agentcore/operations/dataset/__init__.py +21 -0
- agentcore/operations/dataset/delete.py +7 -0
- agentcore/operations/dataset/publish.py +16 -0
- agentcore/operations/dataset/pull.py +21 -0
- agentcore/operations/dataset/push.py +201 -0
- agentcore/operations/dataset/resolve_dataset.py +65 -0
- agentcore/operations/dataset/status.py +18 -0
- agentcore/operations/dataset/wait.py +25 -0
- agentcore/operations/deploy/__init__.py +152 -0
- agentcore/operations/deploy/backfill_vpc_id.py +90 -0
- agentcore/operations/deploy/change_detection.py +84 -0
- agentcore/operations/deploy/constants.py +58 -0
- agentcore/operations/deploy/dependency_sync.py +105 -0
- agentcore/operations/deploy/ensure_target.py +30 -0
- agentcore/operations/deploy/gateway_status.py +44 -0
- agentcore/operations/deploy/managed_memory_notice.py +36 -0
- agentcore/operations/deploy/payment_connector_authorization.py +106 -0
- agentcore/operations/deploy/post_deploy_datasets.py +96 -0
- agentcore/operations/deploy/post_deploy_knowledge_bases.py +104 -0
- agentcore/operations/deploy/post_deploy_observability.py +28 -0
- agentcore/operations/deploy/post_deploy_online_evals.py +60 -0
- agentcore/operations/deploy/pre_deploy_identity.py +505 -0
- agentcore/operations/deploy/preflight.py +385 -0
- agentcore/operations/deploy/region_override.py +45 -0
- agentcore/operations/deploy/teardown.py +153 -0
- agentcore/operations/dev/__init__.py +61 -0
- agentcore/operations/dev/_http.py +79 -0
- agentcore/operations/dev/_server.py +165 -0
- agentcore/operations/dev/codezip_dev_server.py +151 -0
- agentcore/operations/dev/config.py +128 -0
- agentcore/operations/dev/constants.py +9 -0
- agentcore/operations/dev/container_dev_server.py +237 -0
- agentcore/operations/dev/container_runtime.py +37 -0
- agentcore/operations/dev/dev_server.py +244 -0
- agentcore/operations/dev/gateway_env.py +25 -0
- agentcore/operations/dev/invoke.py +214 -0
- agentcore/operations/dev/invoke_a2a.py +204 -0
- agentcore/operations/dev/invoke_agui.py +88 -0
- agentcore/operations/dev/invoke_mcp.py +145 -0
- agentcore/operations/dev/invoke_types.py +30 -0
- agentcore/operations/dev/load_dev_env.py +26 -0
- agentcore/operations/dev/memory_env.py +19 -0
- agentcore/operations/dev/otel/__init__.py +5 -0
- agentcore/operations/dev/otel/collector.py +234 -0
- agentcore/operations/dev/otel/transforms.py +331 -0
- agentcore/operations/dev/payment_env.py +50 -0
- agentcore/operations/dev/server.py +28 -0
- agentcore/operations/dev/sse_transform.py +30 -0
- agentcore/operations/dev/utils.py +152 -0
- agentcore/operations/dev/web_ui/__init__.py +31 -0
- agentcore/operations/dev/web_ui/constants.py +25 -0
- agentcore/operations/dev/web_ui/handlers/__init__.py +36 -0
- agentcore/operations/dev/web_ui/handlers/_common.py +36 -0
- agentcore/operations/dev/web_ui/handlers/_deployed.py +37 -0
- agentcore/operations/dev/web_ui/handlers/a2a_proxy.py +37 -0
- agentcore/operations/dev/web_ui/handlers/cloudwatch_traces.py +71 -0
- agentcore/operations/dev/web_ui/handlers/harness_invocation.py +89 -0
- agentcore/operations/dev/web_ui/handlers/harness_utils.py +95 -0
- agentcore/operations/dev/web_ui/handlers/invocations.py +327 -0
- agentcore/operations/dev/web_ui/handlers/mcp_proxy.py +119 -0
- agentcore/operations/dev/web_ui/handlers/memory.py +84 -0
- agentcore/operations/dev/web_ui/handlers/resources.py +351 -0
- agentcore/operations/dev/web_ui/handlers/start.py +182 -0
- agentcore/operations/dev/web_ui/handlers/status.py +25 -0
- agentcore/operations/dev/web_ui/handlers/traces.py +50 -0
- agentcore/operations/dev/web_ui/run_web_ui.py +60 -0
- agentcore/operations/dev/web_ui/web_server.py +297 -0
- agentcore/operations/eval/__init__.py +8 -0
- agentcore/operations/eval/list_eval_runs.py +18 -0
- agentcore/operations/eval/logs_eval.py +119 -0
- agentcore/operations/eval/pause_resume.py +90 -0
- agentcore/operations/eval/run_eval.py +354 -0
- agentcore/operations/eval/shared/__init__.py +0 -0
- agentcore/operations/eval/shared/dataset_loader.py +68 -0
- agentcore/operations/eval/shared/dataset_session_provider.py +122 -0
- agentcore/operations/eval/shared/evaluator_runner.py +89 -0
- agentcore/operations/eval/shared/scenario_executor.py +71 -0
- agentcore/operations/eval/shared/span_collector.py +255 -0
- agentcore/operations/eval/storage.py +67 -0
- agentcore/operations/feedback/__init__.py +21 -0
- agentcore/operations/feedback/aperture_client.py +98 -0
- agentcore/operations/feedback/build_payload.py +97 -0
- agentcore/operations/feedback/constants.py +26 -0
- agentcore/operations/feedback/submit_feedback.py +150 -0
- agentcore/operations/fetch_access/__init__.py +36 -0
- agentcore/operations/fetch_access/_target.py +17 -0
- agentcore/operations/fetch_access/fetch_gateway_token.py +75 -0
- agentcore/operations/fetch_access/fetch_harness_token.py +72 -0
- agentcore/operations/fetch_access/fetch_runtime_token.py +76 -0
- agentcore/operations/fetch_access/list_agents.py +18 -0
- agentcore/operations/fetch_access/list_gateways.py +26 -0
- agentcore/operations/fetch_access/list_harnesses.py +23 -0
- agentcore/operations/fetch_access/oauth_token.py +162 -0
- agentcore/operations/fetch_access/types.py +34 -0
- agentcore/operations/harness/__init__.py +1 -0
- agentcore/operations/harness/orphan.py +84 -0
- agentcore/operations/harness/skill_utils.py +56 -0
- agentcore/operations/identity/__init__.py +33 -0
- agentcore/operations/identity/api_key_credential_provider.py +68 -0
- agentcore/operations/identity/oauth2_credential_provider.py +95 -0
- agentcore/operations/ingest/__init__.py +184 -0
- agentcore/operations/init/__init__.py +3 -0
- agentcore/operations/init/files.py +59 -0
- agentcore/operations/insights/__init__.py +21 -0
- agentcore/operations/insights/insights_storage.py +67 -0
- agentcore/operations/insights/run_insights.py +148 -0
- agentcore/operations/invoke/__init__.py +5 -0
- agentcore/operations/invoke/resolve_agent_context.py +85 -0
- agentcore/operations/jobs/__init__.py +38 -0
- agentcore/operations/jobs/ab_test/__init__.py +0 -0
- agentcore/operations/jobs/ab_test/build_options.py +126 -0
- agentcore/operations/jobs/ab_test/format.py +121 -0
- agentcore/operations/jobs/ab_test/handler.py +412 -0
- agentcore/operations/jobs/ab_test/promote.py +142 -0
- agentcore/operations/jobs/ab_test/resolve.py +221 -0
- agentcore/operations/jobs/batch_evaluation/__init__.py +0 -0
- agentcore/operations/jobs/batch_evaluation/build_source.py +68 -0
- agentcore/operations/jobs/batch_evaluation/dataset_phase1.py +95 -0
- agentcore/operations/jobs/batch_evaluation/format.py +58 -0
- agentcore/operations/jobs/batch_evaluation/handler.py +203 -0
- agentcore/operations/jobs/insights/__init__.py +0 -0
- agentcore/operations/jobs/insights/format.py +65 -0
- agentcore/operations/jobs/insights/handler.py +178 -0
- agentcore/operations/jobs/recommendation/__init__.py +0 -0
- agentcore/operations/jobs/recommendation/apply_to_bundle.py +76 -0
- agentcore/operations/jobs/recommendation/build_config.py +271 -0
- agentcore/operations/jobs/recommendation/fetch_session_spans.py +93 -0
- agentcore/operations/jobs/recommendation/format.py +77 -0
- agentcore/operations/jobs/recommendation/handler.py +331 -0
- agentcore/operations/jobs/shared/__init__.py +0 -0
- agentcore/operations/jobs/shared/constants.py +85 -0
- agentcore/operations/jobs/shared/engine.py +187 -0
- agentcore/operations/jobs/shared/format.py +104 -0
- agentcore/operations/jobs/shared/region.py +34 -0
- agentcore/operations/jobs/shared/resolve_agent_state.py +30 -0
- agentcore/operations/jobs/shared/storage.py +117 -0
- agentcore/operations/jobs/shared/wait.py +30 -0
- agentcore/operations/knowledge_base/__init__.py +33 -0
- agentcore/operations/knowledge_base/connector_config.py +105 -0
- agentcore/operations/knowledge_base/hydrate_data_sources.py +59 -0
- agentcore/operations/mcp/__init__.py +4 -0
- agentcore/operations/memory/__init__.py +13 -0
- agentcore/operations/memory/_records.py +72 -0
- agentcore/operations/memory/list_memory_records.py +45 -0
- agentcore/operations/memory/retrieve_memory_records.py +52 -0
- agentcore/operations/node/__init__.py +15 -0
- agentcore/operations/node/setup.py +36 -0
- agentcore/operations/python/__init__.py +17 -0
- agentcore/operations/python/setup.py +47 -0
- agentcore/operations/resolve_agent.py +159 -0
- agentcore/operations/session/__init__.py +86 -0
- agentcore/operations/traces/__init__.py +29 -0
- agentcore/operations/traces/get_trace.py +202 -0
- agentcore/operations/traces/insights_query.py +74 -0
- agentcore/operations/traces/list_traces.py +46 -0
- agentcore/operations/traces/trace_url.py +25 -0
- agentcore/operations/traces/types.py +55 -0
- agentcore/primitives/__init__.py +0 -0
- agentcore/primitives/_gateway_target_wizard.py +293 -0
- agentcore/primitives/_gateway_validation.py +442 -0
- agentcore/primitives/_harness_cli.py +557 -0
- agentcore/primitives/_harness_validation.py +163 -0
- agentcore/primitives/_harness_wizard.py +726 -0
- agentcore/primitives/_knowledge_base_wizard.py +259 -0
- agentcore/primitives/_payment_wizard.py +331 -0
- agentcore/primitives/_policy_form.py +160 -0
- agentcore/primitives/agent.py +817 -0
- agentcore/primitives/auth_utils.py +77 -0
- agentcore/primitives/base.py +178 -0
- agentcore/primitives/capacity_provider.py +501 -0
- agentcore/primitives/config_bundle.py +329 -0
- agentcore/primitives/credential.py +387 -0
- agentcore/primitives/credential_utils.py +45 -0
- agentcore/primitives/dataset.py +211 -0
- agentcore/primitives/deployed_gateways.py +34 -0
- agentcore/primitives/evaluator.py +909 -0
- agentcore/primitives/gateway.py +496 -0
- agentcore/primitives/gateway_target.py +988 -0
- agentcore/primitives/harness.py +706 -0
- agentcore/primitives/knowledge_base.py +477 -0
- agentcore/primitives/memory.py +387 -0
- agentcore/primitives/online_eval_config.py +365 -0
- agentcore/primitives/online_insights.py +275 -0
- agentcore/primitives/payment_connector.py +427 -0
- agentcore/primitives/payment_eligible.py +20 -0
- agentcore/primitives/payment_manager.py +605 -0
- agentcore/primitives/payment_validation.py +68 -0
- agentcore/primitives/policy.py +622 -0
- agentcore/primitives/policy_engine.py +358 -0
- agentcore/primitives/registry.py +56 -0
- agentcore/primitives/runtime_endpoint.py +361 -0
- agentcore/primitives/secret_flag_warning.py +17 -0
- agentcore/schema/__init__.py +14 -0
- agentcore/schema/_zod.py +395 -0
- agentcore/schema/constants.py +306 -0
- agentcore/schema/schemas/__init__.py +40 -0
- agentcore/schema/schemas/agent_env.py +545 -0
- agentcore/schema/schemas/agentcore_project.py +530 -0
- agentcore/schema/schemas/auth.py +286 -0
- agentcore/schema/schemas/aws_targets.py +72 -0
- agentcore/schema/schemas/connections.py +187 -0
- agentcore/schema/schemas/deployed_state.py +322 -0
- agentcore/schema/schemas/mcp.py +650 -0
- agentcore/schema/schemas/mcp_defs.py +54 -0
- agentcore/schema/schemas/primitives/__init__.py +52 -0
- agentcore/schema/schemas/primitives/ab_test.py +162 -0
- agentcore/schema/schemas/primitives/capacity_provider.py +214 -0
- agentcore/schema/schemas/primitives/config_bundle.py +48 -0
- agentcore/schema/schemas/primitives/dataset.py +55 -0
- agentcore/schema/schemas/primitives/evaluator.py +171 -0
- agentcore/schema/schemas/primitives/harness.py +710 -0
- agentcore/schema/schemas/primitives/knowledge_base.py +90 -0
- agentcore/schema/schemas/primitives/memory.py +113 -0
- agentcore/schema/schemas/primitives/online_eval_config.py +77 -0
- agentcore/schema/schemas/primitives/payment.py +114 -0
- agentcore/schema/schemas/primitives/policy.py +83 -0
- agentcore/schema/schemas/primitives/tags.py +50 -0
- agentcore/schema/schemas/zod_util.py +5 -0
- agentcore/schema/types/__init__.py +3 -0
- agentcore/schema/types/path.py +16 -0
- agentcore/templates/__init__.py +115 -0
- agentcore/templates/_handlebars.py +782 -0
- agentcore/templates/_js.py +164 -0
- agentcore/templates/base_renderer.py +107 -0
- agentcore/templates/cdk_renderer.py +129 -0
- agentcore/templates/evaluator_renderer.py +39 -0
- agentcore/templates/gateway_target_renderer.py +69 -0
- agentcore/templates/llm_context/README.md +16 -0
- agentcore/templates/llm_context/agentcore.ts +685 -0
- agentcore/templates/llm_context/aws-targets.ts +51 -0
- agentcore/templates/profiles.py +151 -0
- agentcore/templates/render.py +92 -0
- agentcore/templates/renderers.py +83 -0
- agentcore/templates/schema_assets.py +23 -0
- agentcore/templates/template_root.py +28 -0
- agentcore/templates/types.py +192 -0
- agentcore_cli-0.31.1.dist-info/METADATA +84 -0
- agentcore_cli-0.31.1.dist-info/RECORD +655 -0
- agentcore_cli-0.31.1.dist-info/WHEEL +4 -0
- agentcore_cli-0.31.1.dist-info/entry_points.txt +2 -0
- agentcore_cli-0.31.1.dist-info/licenses/LICENSE +175 -0
- agentcore_cli-0.31.1.dist-info/licenses/NOTICE +88 -0
|
@@ -0,0 +1,971 @@
|
|
|
1
|
+
"""AgentCore data-plane runtime operations (port of ``agentcore.ts``).
|
|
2
|
+
|
|
3
|
+
Covers HTTP/SSE invocation, MCP and A2A JSON-RPC over ``InvokeAgentRuntime``, AG-UI event
|
|
4
|
+
streaming, evaluation, session control, and ``InvokeAgentRuntimeCommand`` (exec). SigV4 calls go
|
|
5
|
+
through boto3's ``bedrock-agentcore`` client; ``CUSTOM_JWT`` runtimes are called with a bearer
|
|
6
|
+
token over plain HTTPS, as upstream does with ``fetch``.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import codecs
|
|
12
|
+
import itertools
|
|
13
|
+
import json
|
|
14
|
+
import time
|
|
15
|
+
from collections.abc import Iterable, Iterator, Mapping
|
|
16
|
+
from dataclasses import dataclass, field, replace
|
|
17
|
+
from typing import Any, Literal, NamedTuple, NotRequired, Protocol, TypedDict
|
|
18
|
+
|
|
19
|
+
from botocore.client import BaseClient
|
|
20
|
+
from botocore.config import Config
|
|
21
|
+
|
|
22
|
+
from agentcore.aws._http import encode_uri_component, http_request, iter_stream_chunks
|
|
23
|
+
from agentcore.aws._util import drop_none, js_string, js_template
|
|
24
|
+
from agentcore.aws.account import create_aws_client
|
|
25
|
+
from agentcore.aws.agui_parser import parse_agui_sse_stream
|
|
26
|
+
from agentcore.aws.agui_types import AguiEvent, AguiRunInput
|
|
27
|
+
from agentcore.aws.partition import service_endpoint
|
|
28
|
+
from agentcore.aws.stage_endpoint import data_plane_endpoint
|
|
29
|
+
from agentcore.lib.utils.json_format import to_json_string
|
|
30
|
+
from agentcore.lib.utils.json_rpc import parse_json_rpc_response
|
|
31
|
+
|
|
32
|
+
DEFAULT_RUNTIME_USER_ID = "default-user"
|
|
33
|
+
"""Default user ID sent with invocations. Container agents need it to obtain workload access tokens."""
|
|
34
|
+
|
|
35
|
+
MCP_PROTOCOL_VERSION = "2025-03-26"
|
|
36
|
+
SESSION_ID_HEADER = "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id"
|
|
37
|
+
USER_ID_HEADER = "X-Amzn-Bedrock-AgentCore-Runtime-User-Id"
|
|
38
|
+
_ACCEPT_JSON_OR_SSE = "application/json, text/event-stream"
|
|
39
|
+
|
|
40
|
+
# Runtime invocations can stream for many minutes; botocore's 60s default read timeout is too short.
|
|
41
|
+
_INVOKE_READ_TIMEOUT_S = 900
|
|
42
|
+
|
|
43
|
+
# Indirection point for tests.
|
|
44
|
+
_sleep = time.sleep
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class SSELogger(Protocol):
|
|
48
|
+
def log_sse_event(self, raw_line: str) -> None: ...
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class EvaluationReferenceInput(TypedDict):
|
|
52
|
+
context: dict[str, Any]
|
|
53
|
+
"""``{"spanContext": {"sessionId": str, "traceId"?: str}}``"""
|
|
54
|
+
expectedTrajectory: NotRequired[dict[str, list[str]]]
|
|
55
|
+
assertions: NotRequired[list[dict[str, str]]]
|
|
56
|
+
expectedResponse: NotRequired[dict[str, str]]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _create_agentcore_client(region: str, headers: Mapping[str, str] | None = None) -> BaseClient:
|
|
60
|
+
"""``bedrock-agentcore`` client on the stage-aware data-plane endpoint, injecting ``headers`` pre-signing."""
|
|
61
|
+
client = create_aws_client(
|
|
62
|
+
"bedrock-agentcore",
|
|
63
|
+
region,
|
|
64
|
+
endpoint_url=data_plane_endpoint(region),
|
|
65
|
+
config=Config(read_timeout=_INVOKE_READ_TIMEOUT_S),
|
|
66
|
+
)
|
|
67
|
+
if headers:
|
|
68
|
+
extra = dict(headers)
|
|
69
|
+
|
|
70
|
+
def add_custom_headers(request: Any, **_: Any) -> None:
|
|
71
|
+
for name, value in extra.items():
|
|
72
|
+
request.headers[name] = value
|
|
73
|
+
|
|
74
|
+
client.meta.events.register("before-sign.bedrock-agentcore", add_custom_headers)
|
|
75
|
+
return client
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# ---------------------------------------------------------------------------
|
|
79
|
+
# Options / results
|
|
80
|
+
# ---------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass(slots=True)
|
|
84
|
+
class InvokeAgentRuntimeOptions:
|
|
85
|
+
region: str
|
|
86
|
+
runtime_arn: str
|
|
87
|
+
payload: str
|
|
88
|
+
session_id: str | None = None
|
|
89
|
+
user_id: str | None = None
|
|
90
|
+
"""Runtime user ID header (defaults to ``default-user``); scopes OAuth tokens for Container agents."""
|
|
91
|
+
logger: SSELogger | None = None
|
|
92
|
+
headers: dict[str, str] | None = None
|
|
93
|
+
"""Custom headers forwarded to the agent runtime."""
|
|
94
|
+
bearer_token: str | None = None
|
|
95
|
+
"""CUSTOM_JWT token; when set, raw HTTPS with ``Authorization: Bearer`` replaces SigV4."""
|
|
96
|
+
baggage: str | None = None
|
|
97
|
+
"""W3C baggage header value (e.g. a config bundle ref)."""
|
|
98
|
+
endpoint: str | None = None
|
|
99
|
+
"""Runtime endpoint qualifier (defaults to ``DEFAULT``)."""
|
|
100
|
+
payment_instrument_id: str | None = None
|
|
101
|
+
payment_session_id: str | None = None
|
|
102
|
+
payment_user_id: str | None = None
|
|
103
|
+
"""Payments end-user identity, sent in the body as ``user_id`` (distinct from ``user_id``)."""
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@dataclass(slots=True)
|
|
107
|
+
class InvokeAgentRuntimeResult:
|
|
108
|
+
content: str
|
|
109
|
+
session_id: str | None = None
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@dataclass(slots=True)
|
|
113
|
+
class StreamingInvokeResult:
|
|
114
|
+
stream: Iterator[str]
|
|
115
|
+
session_id: str | None
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class StopRuntimeSessionResult(TypedDict):
|
|
119
|
+
sessionId: str | None
|
|
120
|
+
statusCode: int | None
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class SSELineResult(NamedTuple):
|
|
124
|
+
content: str | None
|
|
125
|
+
error: str | None
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# ---------------------------------------------------------------------------
|
|
129
|
+
# SSE / response parsing
|
|
130
|
+
# ---------------------------------------------------------------------------
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def parse_sse_line(line: str) -> SSELineResult:
|
|
134
|
+
"""Content or error carried by one SSE ``data:`` line (both ``None`` for other lines)."""
|
|
135
|
+
if not line.startswith("data: "):
|
|
136
|
+
return SSELineResult(None, None)
|
|
137
|
+
raw = line[6:]
|
|
138
|
+
try:
|
|
139
|
+
parsed = json.loads(raw)
|
|
140
|
+
except ValueError:
|
|
141
|
+
return SSELineResult(raw, None)
|
|
142
|
+
if isinstance(parsed, str):
|
|
143
|
+
return SSELineResult(parsed, None)
|
|
144
|
+
if isinstance(parsed, dict) and "error" in parsed:
|
|
145
|
+
return SSELineResult(None, js_string(parsed["error"]))
|
|
146
|
+
# ConverseStream-shaped event: extract the text delta.
|
|
147
|
+
if isinstance(parsed, dict):
|
|
148
|
+
event = parsed.get("event")
|
|
149
|
+
delta = event.get("contentBlockDelta") if isinstance(event, dict) else None
|
|
150
|
+
delta = delta.get("delta") if isinstance(delta, dict) else None
|
|
151
|
+
text = delta.get("text") if isinstance(delta, dict) else None
|
|
152
|
+
if isinstance(text, str):
|
|
153
|
+
return SSELineResult(text, None)
|
|
154
|
+
return SSELineResult(None, None)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def parse_sse(text: str) -> str:
|
|
158
|
+
"""Concatenate the content of an SSE body; stops at the first error (``"Error: ..."``)."""
|
|
159
|
+
parts: list[str] = []
|
|
160
|
+
for line in text.split("\n"):
|
|
161
|
+
content, error = parse_sse_line(line)
|
|
162
|
+
if error:
|
|
163
|
+
return f"Error: {error}"
|
|
164
|
+
if content:
|
|
165
|
+
parts.append(content)
|
|
166
|
+
return "".join(parts)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def extract_result(text: str) -> str:
|
|
170
|
+
"""``{"result": ...}`` payload text, a JSON string's value, pretty JSON, or the raw text."""
|
|
171
|
+
try:
|
|
172
|
+
parsed = json.loads(text)
|
|
173
|
+
except ValueError:
|
|
174
|
+
return text
|
|
175
|
+
if isinstance(parsed, dict) and "result" in parsed:
|
|
176
|
+
result = parsed["result"]
|
|
177
|
+
return result if isinstance(result, str) else to_json_string(result)
|
|
178
|
+
return parsed if isinstance(parsed, str) else to_json_string(parsed)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def build_invoke_payload(options: InvokeAgentRuntimeOptions) -> str:
|
|
182
|
+
"""JSON invoke body; payment fields are included only when set."""
|
|
183
|
+
body: dict[str, str] = {"prompt": options.payload}
|
|
184
|
+
# The agent reads payload.user_id to scope the payment wallet/budget and applies its own
|
|
185
|
+
# "default-user" fallback, so it is never baked into the wire when unset.
|
|
186
|
+
if options.payment_user_id:
|
|
187
|
+
body["user_id"] = options.payment_user_id
|
|
188
|
+
if options.payment_instrument_id:
|
|
189
|
+
body["payment_instrument_id"] = options.payment_instrument_id
|
|
190
|
+
if options.payment_session_id:
|
|
191
|
+
body["payment_session_id"] = options.payment_session_id
|
|
192
|
+
return to_json_string(body, indent=None)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _stream_sse_text(chunks: Iterable[bytes], logger: SSELogger | None) -> Iterator[str]:
|
|
196
|
+
"""Incrementally parse an SSE byte stream into text content, falling back to plain JSON."""
|
|
197
|
+
decoder = codecs.getincrementaldecoder("utf-8")(errors="replace")
|
|
198
|
+
buffer = ""
|
|
199
|
+
full_response = ""
|
|
200
|
+
yielded_content = False
|
|
201
|
+
source = iter(chunks)
|
|
202
|
+
try:
|
|
203
|
+
for chunk in source:
|
|
204
|
+
decoded = decoder.decode(chunk)
|
|
205
|
+
buffer += decoded
|
|
206
|
+
full_response += decoded
|
|
207
|
+
lines = buffer.split("\n")
|
|
208
|
+
buffer = lines.pop()
|
|
209
|
+
for line in lines:
|
|
210
|
+
if logger is not None and line.strip():
|
|
211
|
+
logger.log_sse_event(line)
|
|
212
|
+
content, error = parse_sse_line(line)
|
|
213
|
+
if error:
|
|
214
|
+
yield f"Error: {error}"
|
|
215
|
+
return
|
|
216
|
+
if content:
|
|
217
|
+
yield content
|
|
218
|
+
yielded_content = True
|
|
219
|
+
|
|
220
|
+
if buffer:
|
|
221
|
+
if logger is not None and buffer.strip():
|
|
222
|
+
logger.log_sse_event(buffer)
|
|
223
|
+
content, error = parse_sse_line(buffer)
|
|
224
|
+
if error:
|
|
225
|
+
yield f"Error: {error}"
|
|
226
|
+
elif content:
|
|
227
|
+
yield content
|
|
228
|
+
yielded_content = True
|
|
229
|
+
|
|
230
|
+
# Plain JSON (non-SSE) response.
|
|
231
|
+
if not yielded_content and full_response.strip():
|
|
232
|
+
yield extract_result(full_response.strip())
|
|
233
|
+
finally:
|
|
234
|
+
close = getattr(source, "close", None)
|
|
235
|
+
if close is not None:
|
|
236
|
+
close()
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
# ---------------------------------------------------------------------------
|
|
240
|
+
# Bearer token (CUSTOM_JWT) thin HTTP client
|
|
241
|
+
# ---------------------------------------------------------------------------
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _build_invoke_url(region: str, runtime_arn: str, endpoint: str | None = None) -> str:
|
|
245
|
+
qualifier = endpoint if endpoint is not None else "DEFAULT"
|
|
246
|
+
host = service_endpoint("bedrock-agentcore", region)
|
|
247
|
+
return f"https://{host}/runtimes/{encode_uri_component(runtime_arn)}/invocations?qualifier={qualifier}"
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def build_bearer_invoke_headers(options: Any, accept: str) -> dict[str, str]:
|
|
251
|
+
"""Headers for bearer-token invocations (``options``: ``bearer_token``/``session_id``/``user_id``/``headers``/``baggage``)."""
|
|
252
|
+
headers = {
|
|
253
|
+
"Authorization": f"Bearer {options.bearer_token}",
|
|
254
|
+
"Content-Type": "application/json",
|
|
255
|
+
"Accept": accept,
|
|
256
|
+
}
|
|
257
|
+
if options.session_id:
|
|
258
|
+
headers[SESSION_ID_HEADER] = options.session_id
|
|
259
|
+
headers[USER_ID_HEADER] = options.user_id if options.user_id is not None else DEFAULT_RUNTIME_USER_ID
|
|
260
|
+
baggage = getattr(options, "baggage", None)
|
|
261
|
+
if baggage:
|
|
262
|
+
headers["baggage"] = baggage
|
|
263
|
+
if options.headers:
|
|
264
|
+
headers.update(options.headers)
|
|
265
|
+
return headers
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def _raise_invoke_failed(response: Any, prefix: str = "Invoke failed") -> None:
|
|
269
|
+
try:
|
|
270
|
+
body = response.text()
|
|
271
|
+
except Exception: # noqa: BLE001
|
|
272
|
+
body = ""
|
|
273
|
+
raise RuntimeError(f"{prefix} ({response.status}): {body or response.reason}")
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _invoke_with_bearer_token_streaming(options: InvokeAgentRuntimeOptions) -> StreamingInvokeResult:
|
|
277
|
+
url = _build_invoke_url(options.region, options.runtime_arn, options.endpoint)
|
|
278
|
+
headers = build_bearer_invoke_headers(options, _ACCEPT_JSON_OR_SSE)
|
|
279
|
+
response = http_request("POST", url, headers=headers, body=build_invoke_payload(options), stream=True)
|
|
280
|
+
if not response.ok:
|
|
281
|
+
_raise_invoke_failed(response)
|
|
282
|
+
session_id = response.header(SESSION_ID_HEADER)
|
|
283
|
+
return StreamingInvokeResult(stream=_stream_sse_text(response.iter_bytes(), options.logger), session_id=session_id)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def _invoke_with_bearer_token(options: InvokeAgentRuntimeOptions) -> InvokeAgentRuntimeResult:
|
|
287
|
+
url = _build_invoke_url(options.region, options.runtime_arn, options.endpoint)
|
|
288
|
+
headers = build_bearer_invoke_headers(options, "application/json")
|
|
289
|
+
response = http_request("POST", url, headers=headers, body=build_invoke_payload(options))
|
|
290
|
+
if not response.ok:
|
|
291
|
+
_raise_invoke_failed(response)
|
|
292
|
+
session_id = response.header(SESSION_ID_HEADER)
|
|
293
|
+
text = response.text()
|
|
294
|
+
content = parse_sse(text) if "data: " in text else extract_result(text)
|
|
295
|
+
return InvokeAgentRuntimeResult(content=content, session_id=session_id)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
# ---------------------------------------------------------------------------
|
|
299
|
+
# SDK-based invoke (SigV4)
|
|
300
|
+
# ---------------------------------------------------------------------------
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _invoke_params(options: InvokeAgentRuntimeOptions) -> dict[str, Any]:
|
|
304
|
+
params: dict[str, Any] = {
|
|
305
|
+
"agentRuntimeArn": options.runtime_arn,
|
|
306
|
+
"payload": build_invoke_payload(options).encode("utf-8"),
|
|
307
|
+
"contentType": "application/json",
|
|
308
|
+
"accept": _ACCEPT_JSON_OR_SSE,
|
|
309
|
+
"runtimeUserId": options.user_id if options.user_id is not None else DEFAULT_RUNTIME_USER_ID,
|
|
310
|
+
}
|
|
311
|
+
if options.session_id is not None:
|
|
312
|
+
params["runtimeSessionId"] = options.session_id
|
|
313
|
+
if options.baggage:
|
|
314
|
+
params["baggage"] = options.baggage
|
|
315
|
+
return params
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def invoke_agent_runtime_streaming(options: InvokeAgentRuntimeOptions) -> StreamingInvokeResult:
|
|
319
|
+
"""Invoke a runtime and stream text chunks as they arrive."""
|
|
320
|
+
if options.bearer_token:
|
|
321
|
+
return _invoke_with_bearer_token_streaming(options)
|
|
322
|
+
|
|
323
|
+
client = _create_agentcore_client(options.region, options.headers)
|
|
324
|
+
response = client.invoke_agent_runtime(**_invoke_params(options))
|
|
325
|
+
body = response.get("response")
|
|
326
|
+
if body is None:
|
|
327
|
+
raise RuntimeError("No response from AgentCore Runtime")
|
|
328
|
+
return StreamingInvokeResult(
|
|
329
|
+
stream=_stream_sse_text(iter_stream_chunks(body), options.logger),
|
|
330
|
+
session_id=response.get("runtimeSessionId"),
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def invoke_agent_runtime(options: InvokeAgentRuntimeOptions) -> InvokeAgentRuntimeResult:
|
|
335
|
+
"""Invoke a runtime and return the full response text."""
|
|
336
|
+
if options.bearer_token:
|
|
337
|
+
return _invoke_with_bearer_token(options)
|
|
338
|
+
|
|
339
|
+
client = _create_agentcore_client(options.region, options.headers)
|
|
340
|
+
response = client.invoke_agent_runtime(**_invoke_params(options))
|
|
341
|
+
body = response.get("response")
|
|
342
|
+
if body is None:
|
|
343
|
+
raise RuntimeError("No response from AgentCore Runtime")
|
|
344
|
+
text = body.read().decode("utf-8", errors="replace")
|
|
345
|
+
content = parse_sse(text) if "data: " in text else extract_result(text)
|
|
346
|
+
return InvokeAgentRuntimeResult(content=content, session_id=response.get("runtimeSessionId"))
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
# ============================================================================
|
|
350
|
+
# Evaluate
|
|
351
|
+
# ============================================================================
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
class EvaluationResultContext(TypedDict):
|
|
355
|
+
sessionId: str | None
|
|
356
|
+
traceId: str | None
|
|
357
|
+
spanId: str | None
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
class EvaluationResultTokenUsage(TypedDict):
|
|
361
|
+
inputTokens: int
|
|
362
|
+
outputTokens: int
|
|
363
|
+
totalTokens: int
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
class EvaluationResult(TypedDict):
|
|
367
|
+
evaluatorArn: str | None
|
|
368
|
+
evaluatorId: str | None
|
|
369
|
+
evaluatorName: str | None
|
|
370
|
+
explanation: str | None
|
|
371
|
+
value: float | None
|
|
372
|
+
label: str | None
|
|
373
|
+
errorMessage: str | None
|
|
374
|
+
errorCode: str | None
|
|
375
|
+
context: EvaluationResultContext | None
|
|
376
|
+
tokenUsage: EvaluationResultTokenUsage | None
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
class EvaluateResult(TypedDict):
|
|
380
|
+
evaluationResults: list[EvaluationResult]
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def evaluate(
|
|
384
|
+
*,
|
|
385
|
+
region: str,
|
|
386
|
+
evaluator_id: str,
|
|
387
|
+
session_spans: list[Any],
|
|
388
|
+
target_span_ids: list[str] | None = None,
|
|
389
|
+
target_trace_ids: list[str] | None = None,
|
|
390
|
+
evaluation_reference_inputs: list[EvaluationReferenceInput] | None = None,
|
|
391
|
+
) -> EvaluateResult:
|
|
392
|
+
"""Run an on-demand evaluation of agent spans with one evaluator."""
|
|
393
|
+
client = create_aws_client("bedrock-agentcore", region)
|
|
394
|
+
|
|
395
|
+
params: dict[str, Any] = {"evaluatorId": evaluator_id, "evaluationInput": {"sessionSpans": session_spans}}
|
|
396
|
+
if target_span_ids is not None:
|
|
397
|
+
params["evaluationTarget"] = {"spanIds": target_span_ids}
|
|
398
|
+
elif target_trace_ids is not None:
|
|
399
|
+
params["evaluationTarget"] = {"traceIds": target_trace_ids}
|
|
400
|
+
if evaluation_reference_inputs is not None:
|
|
401
|
+
params["evaluationReferenceInputs"] = evaluation_reference_inputs
|
|
402
|
+
|
|
403
|
+
response = client.evaluate(**params)
|
|
404
|
+
results = response.get("evaluationResults")
|
|
405
|
+
if results is None:
|
|
406
|
+
raise RuntimeError("No evaluation results returned")
|
|
407
|
+
|
|
408
|
+
def map_result(r: dict[str, Any]) -> EvaluationResult:
|
|
409
|
+
ctx = r.get("context")
|
|
410
|
+
span_context = ctx.get("spanContext") if isinstance(ctx, dict) else None
|
|
411
|
+
usage = r.get("tokenUsage")
|
|
412
|
+
return {
|
|
413
|
+
"evaluatorArn": r.get("evaluatorArn"),
|
|
414
|
+
"evaluatorId": r.get("evaluatorId"),
|
|
415
|
+
"evaluatorName": r.get("evaluatorName"),
|
|
416
|
+
"explanation": r.get("explanation"),
|
|
417
|
+
"value": r.get("value"),
|
|
418
|
+
"label": r.get("label"),
|
|
419
|
+
"errorMessage": r.get("errorMessage"),
|
|
420
|
+
"errorCode": r.get("errorCode"),
|
|
421
|
+
"context": {
|
|
422
|
+
"sessionId": span_context.get("sessionId"),
|
|
423
|
+
"traceId": span_context.get("traceId"),
|
|
424
|
+
"spanId": span_context.get("spanId"),
|
|
425
|
+
}
|
|
426
|
+
if span_context
|
|
427
|
+
else None,
|
|
428
|
+
"tokenUsage": {
|
|
429
|
+
"inputTokens": usage.get("inputTokens") or 0,
|
|
430
|
+
"outputTokens": usage.get("outputTokens") or 0,
|
|
431
|
+
"totalTokens": usage.get("totalTokens") or 0,
|
|
432
|
+
}
|
|
433
|
+
if usage
|
|
434
|
+
else None,
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return {"evaluationResults": [map_result(r) for r in results]}
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
# ---------------------------------------------------------------------------
|
|
441
|
+
# MCP: JSON-RPC over InvokeAgentRuntime
|
|
442
|
+
# ---------------------------------------------------------------------------
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
@dataclass(slots=True)
|
|
446
|
+
class McpInvokeOptions:
|
|
447
|
+
region: str
|
|
448
|
+
runtime_arn: str
|
|
449
|
+
user_id: str | None = None
|
|
450
|
+
mcp_session_id: str | None = None
|
|
451
|
+
logger: SSELogger | None = None
|
|
452
|
+
headers: dict[str, str] | None = None
|
|
453
|
+
bearer_token: str | None = None
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
class McpToolDef(TypedDict):
|
|
457
|
+
name: str
|
|
458
|
+
description: NotRequired[str]
|
|
459
|
+
inputSchema: NotRequired[dict[str, Any]]
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
@dataclass(slots=True)
|
|
463
|
+
class McpListToolsResult:
|
|
464
|
+
tools: list[McpToolDef]
|
|
465
|
+
mcp_session_id: str | None = None
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
@dataclass(slots=True)
|
|
469
|
+
class _McpRpcResult:
|
|
470
|
+
result: dict[str, Any]
|
|
471
|
+
mcp_session_id: str | None = None
|
|
472
|
+
error: dict[str, Any] | None = None
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
_mcp_request_ids = itertools.count(1)
|
|
476
|
+
_a2a_request_ids = itertools.count(1)
|
|
477
|
+
|
|
478
|
+
_TRANSIENT_STATUS_CODES = frozenset({429, 500, 502, 503, 504})
|
|
479
|
+
_MAX_FETCH_RETRIES = 3
|
|
480
|
+
_FETCH_RETRY_DELAY_S = 1.0
|
|
481
|
+
_COLD_START_RETRIES = 3
|
|
482
|
+
_COLD_START_DELAY_S = 2.0
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def _log(logger: SSELogger | None, line: str) -> None:
|
|
486
|
+
if logger is not None:
|
|
487
|
+
logger.log_sse_event(line)
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def _fetch_with_retry(url: str, headers: Mapping[str, str], body: str, logger: SSELogger | None) -> Any:
|
|
491
|
+
"""POST with retries on transient failures (5xx, 429, network errors)."""
|
|
492
|
+
for attempt in range(_MAX_FETCH_RETRIES):
|
|
493
|
+
try:
|
|
494
|
+
response = http_request("POST", url, headers=headers, body=body)
|
|
495
|
+
if response.ok or response.status not in _TRANSIENT_STATUS_CODES or attempt == _MAX_FETCH_RETRIES - 1:
|
|
496
|
+
return response
|
|
497
|
+
_log(logger, f"Transient failure ({response.status}), retrying ({attempt + 1}/{_MAX_FETCH_RETRIES})...")
|
|
498
|
+
except Exception:
|
|
499
|
+
if attempt == _MAX_FETCH_RETRIES - 1:
|
|
500
|
+
raise
|
|
501
|
+
_log(logger, f"Network error, retrying ({attempt + 1}/{_MAX_FETCH_RETRIES})...")
|
|
502
|
+
_sleep(_FETCH_RETRY_DELAY_S)
|
|
503
|
+
raise RuntimeError("fetchWithRetry: exhausted retries")
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
def _build_mcp_bearer_headers(options: McpInvokeOptions) -> dict[str, str]:
|
|
507
|
+
headers = {
|
|
508
|
+
"Authorization": f"Bearer {options.bearer_token}",
|
|
509
|
+
"Content-Type": "application/json",
|
|
510
|
+
"Accept": _ACCEPT_JSON_OR_SSE,
|
|
511
|
+
"Mcp-Protocol-Version": MCP_PROTOCOL_VERSION,
|
|
512
|
+
USER_ID_HEADER: options.user_id if options.user_id is not None else DEFAULT_RUNTIME_USER_ID,
|
|
513
|
+
}
|
|
514
|
+
if options.mcp_session_id:
|
|
515
|
+
headers["Mcp-Session-Id"] = options.mcp_session_id
|
|
516
|
+
if options.headers:
|
|
517
|
+
headers.update(options.headers)
|
|
518
|
+
return headers
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
def _rpc_result(parsed: dict[str, Any], session_id: str | None) -> _McpRpcResult:
|
|
522
|
+
result = parsed.get("result")
|
|
523
|
+
return _McpRpcResult(
|
|
524
|
+
result=result if result is not None else {},
|
|
525
|
+
mcp_session_id=session_id,
|
|
526
|
+
error=parsed.get("error"),
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
def _mcp_rpc_call_with_bearer(options: McpInvokeOptions, body: dict[str, Any]) -> _McpRpcResult:
|
|
531
|
+
url = _build_invoke_url(options.region, options.runtime_arn)
|
|
532
|
+
headers = _build_mcp_bearer_headers(options)
|
|
533
|
+
serialized = to_json_string(body, indent=None)
|
|
534
|
+
_log(options.logger, f"MCP request: {serialized}")
|
|
535
|
+
|
|
536
|
+
response = _fetch_with_retry(url, headers, serialized, options.logger)
|
|
537
|
+
if not response.ok:
|
|
538
|
+
_raise_invoke_failed(response, "MCP call failed")
|
|
539
|
+
|
|
540
|
+
text = response.text()
|
|
541
|
+
_log(options.logger, f"MCP response: {text}")
|
|
542
|
+
return _rpc_result(parse_json_rpc_response(text), response.header("Mcp-Session-Id"))
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
def _mcp_rpc_notify_with_bearer(options: McpInvokeOptions, body: dict[str, Any]) -> None:
|
|
546
|
+
url = _build_invoke_url(options.region, options.runtime_arn)
|
|
547
|
+
headers = _build_mcp_bearer_headers(options)
|
|
548
|
+
response = _fetch_with_retry(url, headers, to_json_string(body, indent=None), options.logger)
|
|
549
|
+
if not response.ok:
|
|
550
|
+
_raise_invoke_failed(response, "MCP notification failed")
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
def _mcp_invoke_params(options: McpInvokeOptions, body: dict[str, Any]) -> dict[str, Any]:
|
|
554
|
+
params: dict[str, Any] = {
|
|
555
|
+
"agentRuntimeArn": options.runtime_arn,
|
|
556
|
+
"payload": to_json_string(body, indent=None).encode("utf-8"),
|
|
557
|
+
"contentType": "application/json",
|
|
558
|
+
"accept": _ACCEPT_JSON_OR_SSE,
|
|
559
|
+
"mcpProtocolVersion": MCP_PROTOCOL_VERSION,
|
|
560
|
+
"runtimeUserId": options.user_id if options.user_id is not None else DEFAULT_RUNTIME_USER_ID,
|
|
561
|
+
}
|
|
562
|
+
if options.mcp_session_id is not None:
|
|
563
|
+
params["mcpSessionId"] = options.mcp_session_id
|
|
564
|
+
return params
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def _mcp_rpc_call(options: McpInvokeOptions, body: dict[str, Any]) -> _McpRpcResult:
|
|
568
|
+
"""Send a JSON-RPC request through InvokeAgentRuntime and parse the response."""
|
|
569
|
+
if options.bearer_token:
|
|
570
|
+
return _mcp_rpc_call_with_bearer(options, body)
|
|
571
|
+
|
|
572
|
+
client = _create_agentcore_client(options.region, options.headers)
|
|
573
|
+
_log(options.logger, f"MCP request: {to_json_string(body, indent=None)}")
|
|
574
|
+
response = client.invoke_agent_runtime(**_mcp_invoke_params(options, body))
|
|
575
|
+
stream = response.get("response")
|
|
576
|
+
if stream is None:
|
|
577
|
+
raise RuntimeError("No response from AgentCore Runtime")
|
|
578
|
+
text = stream.read().decode("utf-8", errors="replace")
|
|
579
|
+
_log(options.logger, f"MCP response: {text}")
|
|
580
|
+
return _rpc_result(parse_json_rpc_response(text), response.get("mcpSessionId"))
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
def _mcp_rpc_call_strict(options: McpInvokeOptions, body: dict[str, Any]) -> _McpRpcResult:
|
|
584
|
+
""":func:`_mcp_rpc_call`, raising on JSON-RPC errors."""
|
|
585
|
+
result = _mcp_rpc_call(options, body)
|
|
586
|
+
if result.error:
|
|
587
|
+
message = result.error.get("message")
|
|
588
|
+
raise RuntimeError(message if message is not None else f"MCP error (code {result.error.get('code')})")
|
|
589
|
+
return result
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
def _mcp_rpc_notify(options: McpInvokeOptions, body: dict[str, Any]) -> None:
|
|
593
|
+
"""Send a JSON-RPC notification (no id, response ignored)."""
|
|
594
|
+
if options.bearer_token:
|
|
595
|
+
_mcp_rpc_notify_with_bearer(options, body)
|
|
596
|
+
return
|
|
597
|
+
client = _create_agentcore_client(options.region, options.headers)
|
|
598
|
+
response = client.invoke_agent_runtime(**_mcp_invoke_params(options, body))
|
|
599
|
+
stream = response.get("response")
|
|
600
|
+
if stream is not None:
|
|
601
|
+
stream.close()
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
def _initialize_request() -> dict[str, Any]:
|
|
605
|
+
return {
|
|
606
|
+
"jsonrpc": "2.0",
|
|
607
|
+
"id": next(_mcp_request_ids),
|
|
608
|
+
"method": "initialize",
|
|
609
|
+
"params": {
|
|
610
|
+
"protocolVersion": MCP_PROTOCOL_VERSION,
|
|
611
|
+
"capabilities": {},
|
|
612
|
+
"clientInfo": {"name": "agentcore-cli", "version": "1.0.0"},
|
|
613
|
+
},
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
def _is_cold_start(err: BaseException) -> bool:
|
|
618
|
+
msg = str(err)
|
|
619
|
+
return "initialization time exceeded" in msg or "initialization" in msg
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
def mcp_list_tools(options: McpInvokeOptions) -> McpListToolsResult:
|
|
623
|
+
"""Initialize an MCP session and list tools; retries cold-start initialization timeouts."""
|
|
624
|
+
for attempt in range(_COLD_START_RETRIES):
|
|
625
|
+
try:
|
|
626
|
+
return _mcp_list_tools_once(options)
|
|
627
|
+
except Exception as exc:
|
|
628
|
+
if _is_cold_start(exc) and attempt < _COLD_START_RETRIES - 1:
|
|
629
|
+
_log(options.logger, f"MCP cold start (attempt {attempt + 1}/{_COLD_START_RETRIES}), retrying...")
|
|
630
|
+
_sleep(_COLD_START_DELAY_S)
|
|
631
|
+
continue
|
|
632
|
+
raise
|
|
633
|
+
raise RuntimeError("Failed to list MCP tools after retries")
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
def _mcp_list_tools_once(options: McpInvokeOptions) -> McpListToolsResult:
|
|
637
|
+
# Stateless servers may reject initialize but still return a session ID, so errors are tolerated.
|
|
638
|
+
init_result = _mcp_rpc_call(options, _initialize_request())
|
|
639
|
+
if init_result.error:
|
|
640
|
+
_log(
|
|
641
|
+
options.logger,
|
|
642
|
+
"MCP initialize returned error (expected for stateless servers): "
|
|
643
|
+
f"{js_template(init_result.error.get('message'))}",
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
session_id = init_result.mcp_session_id
|
|
647
|
+
with_session = replace(options, mcp_session_id=session_id)
|
|
648
|
+
_mcp_rpc_notify(with_session, {"jsonrpc": "2.0", "method": "notifications/initialized"})
|
|
649
|
+
|
|
650
|
+
list_result = _mcp_rpc_call_strict(
|
|
651
|
+
with_session, {"jsonrpc": "2.0", "id": next(_mcp_request_ids), "method": "tools/list", "params": {}}
|
|
652
|
+
)
|
|
653
|
+
tools = list_result.result.get("tools") or []
|
|
654
|
+
mapped: list[McpToolDef] = [
|
|
655
|
+
drop_none({"name": t.get("name"), "description": t.get("description"), "inputSchema": t.get("inputSchema")}) # type: ignore[misc]
|
|
656
|
+
for t in tools
|
|
657
|
+
]
|
|
658
|
+
return McpListToolsResult(tools=mapped, mcp_session_id=session_id)
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
def mcp_init_session(options: McpInvokeOptions) -> str | None:
|
|
662
|
+
"""Initialize an MCP session (without listing tools); returns its session ID."""
|
|
663
|
+
init_result = _mcp_rpc_call(options, _initialize_request())
|
|
664
|
+
session_id = init_result.mcp_session_id
|
|
665
|
+
_mcp_rpc_notify(
|
|
666
|
+
replace(options, mcp_session_id=session_id), {"jsonrpc": "2.0", "method": "notifications/initialized"}
|
|
667
|
+
)
|
|
668
|
+
return session_id
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
def mcp_call_tool(options: McpInvokeOptions, tool_name: str, args: dict[str, Any]) -> str:
|
|
672
|
+
"""Call an MCP tool; returns its concatenated text content (or pretty JSON of the result)."""
|
|
673
|
+
for attempt in range(_COLD_START_RETRIES):
|
|
674
|
+
try:
|
|
675
|
+
rpc = _mcp_rpc_call_strict(
|
|
676
|
+
options,
|
|
677
|
+
{
|
|
678
|
+
"jsonrpc": "2.0",
|
|
679
|
+
"id": next(_mcp_request_ids),
|
|
680
|
+
"method": "tools/call",
|
|
681
|
+
"params": {"name": tool_name, "arguments": args},
|
|
682
|
+
},
|
|
683
|
+
)
|
|
684
|
+
content = rpc.result.get("content")
|
|
685
|
+
if content:
|
|
686
|
+
texts = [item["text"] for item in content if isinstance(item, dict) and item.get("text") is not None]
|
|
687
|
+
if texts:
|
|
688
|
+
return "".join(texts)
|
|
689
|
+
return to_json_string(rpc.result)
|
|
690
|
+
except Exception as exc:
|
|
691
|
+
if _is_cold_start(exc) and attempt < _COLD_START_RETRIES - 1:
|
|
692
|
+
_log(options.logger, f"MCP cold start (attempt {attempt + 1}/{_COLD_START_RETRIES}), retrying...")
|
|
693
|
+
_sleep(_COLD_START_DELAY_S)
|
|
694
|
+
continue
|
|
695
|
+
raise
|
|
696
|
+
raise RuntimeError("Failed to call MCP tool after retries")
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
# ---------------------------------------------------------------------------
|
|
700
|
+
# A2A: JSON-RPC message/send over InvokeAgentRuntime
|
|
701
|
+
# ---------------------------------------------------------------------------
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
@dataclass(slots=True)
|
|
705
|
+
class A2AInvokeOptions:
|
|
706
|
+
region: str
|
|
707
|
+
runtime_arn: str
|
|
708
|
+
user_id: str | None = None
|
|
709
|
+
session_id: str | None = None
|
|
710
|
+
logger: SSELogger | None = None
|
|
711
|
+
headers: dict[str, str] | None = None
|
|
712
|
+
bearer_token: str | None = None
|
|
713
|
+
|
|
714
|
+
|
|
715
|
+
def _single_value_stream(value: str) -> Iterator[str]:
|
|
716
|
+
yield value
|
|
717
|
+
|
|
718
|
+
|
|
719
|
+
def invoke_a2a_runtime(options: A2AInvokeOptions, message: str) -> StreamingInvokeResult:
|
|
720
|
+
"""Send an A2A ``message/send`` request and return the agent's text as a one-item stream."""
|
|
721
|
+
body = {
|
|
722
|
+
"jsonrpc": "2.0",
|
|
723
|
+
"id": next(_a2a_request_ids),
|
|
724
|
+
"method": "message/send",
|
|
725
|
+
"params": {
|
|
726
|
+
"message": {
|
|
727
|
+
"role": "user",
|
|
728
|
+
"parts": [{"kind": "text", "text": message}],
|
|
729
|
+
"messageId": f"msg-{int(time.time() * 1000)}",
|
|
730
|
+
}
|
|
731
|
+
},
|
|
732
|
+
}
|
|
733
|
+
serialized = to_json_string(body, indent=None)
|
|
734
|
+
_log(options.logger, f"A2A request: {serialized}")
|
|
735
|
+
|
|
736
|
+
if options.bearer_token:
|
|
737
|
+
url = _build_invoke_url(options.region, options.runtime_arn)
|
|
738
|
+
headers = build_bearer_invoke_headers(options, _ACCEPT_JSON_OR_SSE)
|
|
739
|
+
response = http_request("POST", url, headers=headers, body=serialized)
|
|
740
|
+
if not response.ok:
|
|
741
|
+
_raise_invoke_failed(response)
|
|
742
|
+
text = response.text()
|
|
743
|
+
_log(options.logger, f"A2A response: {text}")
|
|
744
|
+
return StreamingInvokeResult(
|
|
745
|
+
stream=_single_value_stream(parse_a2a_response(text)), session_id=response.header(SESSION_ID_HEADER)
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
client = _create_agentcore_client(options.region, options.headers)
|
|
749
|
+
params: dict[str, Any] = {
|
|
750
|
+
"agentRuntimeArn": options.runtime_arn,
|
|
751
|
+
"payload": serialized.encode("utf-8"),
|
|
752
|
+
"contentType": "application/json",
|
|
753
|
+
"accept": _ACCEPT_JSON_OR_SSE,
|
|
754
|
+
"runtimeUserId": options.user_id if options.user_id is not None else DEFAULT_RUNTIME_USER_ID,
|
|
755
|
+
}
|
|
756
|
+
if options.session_id:
|
|
757
|
+
params["runtimeSessionId"] = options.session_id
|
|
758
|
+
response = client.invoke_agent_runtime(**params)
|
|
759
|
+
stream = response.get("response")
|
|
760
|
+
if stream is None:
|
|
761
|
+
raise RuntimeError("No response from AgentCore Runtime")
|
|
762
|
+
text = stream.read().decode("utf-8", errors="replace")
|
|
763
|
+
_log(options.logger, f"A2A response: {text}")
|
|
764
|
+
# Upstream does not surface the session ID on the SigV4 A2A path.
|
|
765
|
+
return StreamingInvokeResult(stream=_single_value_stream(parse_a2a_response(text)), session_id=None)
|
|
766
|
+
|
|
767
|
+
|
|
768
|
+
def _text_parts(parts: Any) -> list[str]:
|
|
769
|
+
if not isinstance(parts, list):
|
|
770
|
+
return []
|
|
771
|
+
return [
|
|
772
|
+
js_string(p["text"])
|
|
773
|
+
for p in parts
|
|
774
|
+
if isinstance(p, dict) and (p.get("kind") == "text" or p.get("type") == "text") and p.get("text") is not None
|
|
775
|
+
]
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
def parse_a2a_response(text: str) -> str:
|
|
779
|
+
"""Text of an A2A JSON-RPC response (``kind: 'text'`` and ``type: 'text'`` parts)."""
|
|
780
|
+
try:
|
|
781
|
+
parsed = json.loads(text)
|
|
782
|
+
if not isinstance(parsed, dict):
|
|
783
|
+
return text
|
|
784
|
+
|
|
785
|
+
error = parsed.get("error")
|
|
786
|
+
if isinstance(error, (dict, list)):
|
|
787
|
+
message = error.get("message") if isinstance(error, dict) else None
|
|
788
|
+
return f"Error: {js_string(message) if message is not None else to_json_string(error, indent=None)}"
|
|
789
|
+
|
|
790
|
+
result = parsed.get("result")
|
|
791
|
+
if not result:
|
|
792
|
+
return text
|
|
793
|
+
|
|
794
|
+
artifacts = result.get("artifacts") if isinstance(result, dict) else None
|
|
795
|
+
if artifacts:
|
|
796
|
+
texts = [
|
|
797
|
+
t for artifact in artifacts if isinstance(artifact, dict) for t in _text_parts(artifact.get("parts"))
|
|
798
|
+
]
|
|
799
|
+
if texts:
|
|
800
|
+
return "".join(texts)
|
|
801
|
+
|
|
802
|
+
# Fallback: last agent message in the history.
|
|
803
|
+
history = result.get("history") if isinstance(result, dict) else None
|
|
804
|
+
if history:
|
|
805
|
+
for msg in reversed(history):
|
|
806
|
+
if isinstance(msg, dict) and msg.get("role") == "agent" and msg.get("parts"):
|
|
807
|
+
agent_texts = _text_parts(msg.get("parts"))
|
|
808
|
+
if agent_texts:
|
|
809
|
+
return "".join(agent_texts)
|
|
810
|
+
|
|
811
|
+
return to_json_string(result)
|
|
812
|
+
except Exception: # noqa: BLE001 - any malformed shape falls back to the raw text
|
|
813
|
+
return text
|
|
814
|
+
|
|
815
|
+
|
|
816
|
+
# ---------------------------------------------------------------------------
|
|
817
|
+
# AGUI: structured event streaming over InvokeAgentRuntime
|
|
818
|
+
# ---------------------------------------------------------------------------
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
@dataclass(slots=True)
|
|
822
|
+
class AguiInvokeOptions:
|
|
823
|
+
region: str
|
|
824
|
+
runtime_arn: str
|
|
825
|
+
session_id: str | None = None
|
|
826
|
+
user_id: str | None = None
|
|
827
|
+
logger: SSELogger | None = None
|
|
828
|
+
headers: dict[str, str] | None = None
|
|
829
|
+
bearer_token: str | None = None
|
|
830
|
+
"""Not yet supported for AG-UI; raises if provided."""
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
@dataclass(slots=True)
|
|
834
|
+
class AguiStreamingInvokeResult:
|
|
835
|
+
stream: Iterator[AguiEvent]
|
|
836
|
+
"""All AG-UI events, for rich rendering."""
|
|
837
|
+
text_stream: Iterator[str]
|
|
838
|
+
"""Only TEXT_MESSAGE_CONTENT deltas (and RUN_ERROR messages)."""
|
|
839
|
+
session_id: str | None
|
|
840
|
+
|
|
841
|
+
|
|
842
|
+
def invoke_agui_runtime(options: AguiInvokeOptions, input: AguiRunInput) -> AguiStreamingInvokeResult:
|
|
843
|
+
"""Invoke an AG-UI runtime and stream its structured events."""
|
|
844
|
+
if options.bearer_token:
|
|
845
|
+
raise RuntimeError("Bearer token auth is not yet supported for AGUI. Use SigV4 credentials.")
|
|
846
|
+
|
|
847
|
+
client = _create_agentcore_client(options.region, options.headers)
|
|
848
|
+
params: dict[str, Any] = {
|
|
849
|
+
"agentRuntimeArn": options.runtime_arn,
|
|
850
|
+
"payload": to_json_string(input, indent=None).encode("utf-8"),
|
|
851
|
+
"contentType": "application/json",
|
|
852
|
+
"accept": "text/event-stream",
|
|
853
|
+
"runtimeUserId": options.user_id if options.user_id is not None else DEFAULT_RUNTIME_USER_ID,
|
|
854
|
+
}
|
|
855
|
+
if options.session_id is not None:
|
|
856
|
+
params["runtimeSessionId"] = options.session_id
|
|
857
|
+
response = client.invoke_agent_runtime(**params)
|
|
858
|
+
body = response.get("response")
|
|
859
|
+
if body is None:
|
|
860
|
+
raise RuntimeError("No response from AgentCore Runtime")
|
|
861
|
+
|
|
862
|
+
streams = parse_agui_sse_stream(iter_stream_chunks(body), logger=options.logger)
|
|
863
|
+
if streams.text_stream is None:
|
|
864
|
+
raise RuntimeError("AGUI parser created in single-consumer mode — textStream unavailable")
|
|
865
|
+
return AguiStreamingInvokeResult(
|
|
866
|
+
stream=streams.event_stream, text_stream=streams.text_stream, session_id=response.get("runtimeSessionId")
|
|
867
|
+
)
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
def stop_runtime_session(*, region: str, runtime_arn: str, session_id: str) -> StopRuntimeSessionResult:
|
|
871
|
+
"""Stop a runtime session."""
|
|
872
|
+
client = create_aws_client("bedrock-agentcore", region)
|
|
873
|
+
response = client.stop_runtime_session(agentRuntimeArn=runtime_arn, runtimeSessionId=session_id)
|
|
874
|
+
return {"sessionId": response.get("runtimeSessionId"), "statusCode": response.get("statusCode")}
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
# ---------------------------------------------------------------------------
|
|
878
|
+
# Delete capacity provider session (data plane)
|
|
879
|
+
# ---------------------------------------------------------------------------
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
class DeleteCapacityProviderSessionResult(TypedDict, total=False):
|
|
883
|
+
capacityProviderArn: str
|
|
884
|
+
sessionId: str
|
|
885
|
+
status: str
|
|
886
|
+
"""Session status after the asynchronous delete, typically ``Deprovisioning``."""
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
def delete_capacity_provider_session(
|
|
890
|
+
*, region: str, capacity_provider_id: str, session_id: str
|
|
891
|
+
) -> DeleteCapacityProviderSessionResult:
|
|
892
|
+
"""Deprovision one capacity provider session (idempotent, asynchronous; deletes attached EBS volumes).
|
|
893
|
+
|
|
894
|
+
``capacity_provider_id`` is the ``{cpName}-{suffix}`` id, not the ARN.
|
|
895
|
+
"""
|
|
896
|
+
client = _create_agentcore_client(region)
|
|
897
|
+
response = client.delete_capacity_provider_session(capacityProviderId=capacity_provider_id, sessionId=session_id)
|
|
898
|
+
return drop_none( # type: ignore[return-value]
|
|
899
|
+
{
|
|
900
|
+
"capacityProviderArn": response.get("capacityProviderArn"),
|
|
901
|
+
"sessionId": response.get("sessionId"),
|
|
902
|
+
"status": response.get("status"),
|
|
903
|
+
}
|
|
904
|
+
)
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
# ---------------------------------------------------------------------------
|
|
908
|
+
# Execute bash: run shell commands in runtime containers
|
|
909
|
+
# ---------------------------------------------------------------------------
|
|
910
|
+
|
|
911
|
+
|
|
912
|
+
@dataclass(slots=True)
|
|
913
|
+
class ExecuteBashOptions:
|
|
914
|
+
region: str
|
|
915
|
+
runtime_arn: str
|
|
916
|
+
command: str
|
|
917
|
+
session_id: str | None = None
|
|
918
|
+
timeout: int | None = None
|
|
919
|
+
headers: dict[str, str] | None = None
|
|
920
|
+
bearer_token: str | None = None
|
|
921
|
+
"""Not yet supported for exec; raises if provided."""
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
class ExecuteBashStreamEvent(TypedDict):
|
|
925
|
+
type: Literal["start", "stdout", "stderr", "stop"]
|
|
926
|
+
data: NotRequired[str]
|
|
927
|
+
exitCode: NotRequired[int | None]
|
|
928
|
+
status: NotRequired[str | None]
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
@dataclass(slots=True)
|
|
932
|
+
class ExecuteBashResult:
|
|
933
|
+
stream: Iterator[ExecuteBashStreamEvent]
|
|
934
|
+
session_id: str | None = field(default=None)
|
|
935
|
+
|
|
936
|
+
|
|
937
|
+
def execute_bash_command(options: ExecuteBashOptions) -> ExecuteBashResult:
|
|
938
|
+
"""Run a shell command in a running runtime container, streaming stdout/stderr and the exit code."""
|
|
939
|
+
if options.bearer_token:
|
|
940
|
+
raise RuntimeError("Bearer token auth for exec is not yet supported. Use SigV4 credentials.")
|
|
941
|
+
|
|
942
|
+
client = _create_agentcore_client(options.region, options.headers)
|
|
943
|
+
body: dict[str, Any] = {"command": options.command}
|
|
944
|
+
if options.timeout is not None:
|
|
945
|
+
body["timeout"] = options.timeout
|
|
946
|
+
params: dict[str, Any] = {"agentRuntimeArn": options.runtime_arn, "body": body}
|
|
947
|
+
if options.session_id is not None:
|
|
948
|
+
params["runtimeSessionId"] = options.session_id
|
|
949
|
+
response = client.invoke_agent_runtime_command(**params)
|
|
950
|
+
|
|
951
|
+
def stream_events() -> Iterator[ExecuteBashStreamEvent]:
|
|
952
|
+
event_stream = response.get("stream")
|
|
953
|
+
if event_stream is None:
|
|
954
|
+
raise RuntimeError("No stream in response from AgentCore Runtime")
|
|
955
|
+
for event in event_stream:
|
|
956
|
+
chunk = event.get("chunk") if isinstance(event, dict) else None
|
|
957
|
+
if not isinstance(chunk, dict):
|
|
958
|
+
continue
|
|
959
|
+
if chunk.get("contentStart") is not None:
|
|
960
|
+
yield {"type": "start"}
|
|
961
|
+
delta = chunk.get("contentDelta")
|
|
962
|
+
if delta:
|
|
963
|
+
if delta.get("stdout"):
|
|
964
|
+
yield {"type": "stdout", "data": delta["stdout"]}
|
|
965
|
+
if delta.get("stderr"):
|
|
966
|
+
yield {"type": "stderr", "data": delta["stderr"]}
|
|
967
|
+
stop = chunk.get("contentStop")
|
|
968
|
+
if stop is not None:
|
|
969
|
+
yield {"type": "stop", "exitCode": stop.get("exitCode"), "status": stop.get("status")}
|
|
970
|
+
|
|
971
|
+
return ExecuteBashResult(stream=stream_events(), session_id=response.get("runtimeSessionId"))
|