diff --git a/pyproject.toml b/pyproject.toml index 040a351b..d3ce2421 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -94,6 +94,7 @@ select = [ "G", # logging format "I", # isort "LOG", # logging + "UP", # pyupgrade ] [tool.ruff.lint.per-file-ignores] diff --git a/scripts/bump-version.py b/scripts/bump-version.py index 60a0c5c6..64dc1065 100755 --- a/scripts/bump-version.py +++ b/scripts/bump-version.py @@ -7,7 +7,6 @@ import time from datetime import datetime from pathlib import Path -from typing import Optional, Tuple import requests @@ -21,7 +20,7 @@ def get_current_version() -> str: return match.group(1) -def get_sdk_dependency_version() -> Optional[str]: +def get_sdk_dependency_version() -> str | None: """Get current SDK dependency version.""" content = Path("pyproject.toml").read_text() match = re.search(r'bedrock-agentcore>=([^"]+)', content) @@ -60,7 +59,7 @@ def update_sdk_dependency(new_sdk_version: str): print(f"✓ Updated SDK dependency to >={new_sdk_version}") -def parse_version(version: str) -> Tuple[int, int, int, Optional[str]]: +def parse_version(version: str) -> tuple[int, int, int, str | None]: """Parse semantic version string.""" match = re.match(r"(\d+)\.(\d+)\.(\d+)(?:-(.+))?", version) if not match: @@ -122,7 +121,7 @@ def update_all_versions(old_version: str, new_version: str): print(f"✓ Updated {init_file}") -def get_git_log(since_tag: Optional[str] = None) -> str: +def get_git_log(since_tag: str | None = None) -> str: """Get git commit messages since last tag.""" cmd = ["git", "log", "--pretty=format:- %s (%h)"] if since_tag: @@ -220,7 +219,7 @@ def main(): print("\nNext steps:") print("1. Review changes: git diff") - print("2. Commit: git add -A && git commit -m 'chore: bump version to {}'".format(new)) + print(f"2. Commit: git add -A && git commit -m 'chore: bump version to {new}'") print("3. Create PR or push to trigger release workflow") except Exception as e: diff --git a/scripts/prepare-release.py b/scripts/prepare-release.py index 3bf44af9..02b3722f 100755 --- a/scripts/prepare-release.py +++ b/scripts/prepare-release.py @@ -5,7 +5,7 @@ print("Preparing pyproject.toml for release...") -with open("pyproject.toml", "r") as f: +with open("pyproject.toml") as f: content = f.read() # Remove [tool.uv.sources] section diff --git a/scripts/validate-release.py b/scripts/validate-release.py index db6dd147..fe71a2e8 100755 --- a/scripts/validate-release.py +++ b/scripts/validate-release.py @@ -9,7 +9,6 @@ import sys import zipfile from pathlib import Path -from typing import List, Tuple class Colors: @@ -37,7 +36,7 @@ def print_status(message: str, status: str = "info"): print(f" {message}") -def run_command(cmd: List[str], capture=True) -> Tuple[int, str, str]: +def run_command(cmd: list[str], capture=True) -> tuple[int, str, str]: """Run a command and return exit code, stdout, and stderr.""" result = subprocess.run(cmd, capture_output=capture, text=True) return result.returncode, result.stdout, result.stderr diff --git a/src/bedrock_agentcore_starter_toolkit/cli/cli_ui.py b/src/bedrock_agentcore_starter_toolkit/cli/cli_ui.py index fa6ceea7..bf3901cd 100644 --- a/src/bedrock_agentcore_starter_toolkit/cli/cli_ui.py +++ b/src/bedrock_agentcore_starter_toolkit/cli/cli_ui.py @@ -2,7 +2,6 @@ import re import time -from typing import Optional from prompt_toolkit.application import Application from prompt_toolkit.filters import Condition @@ -433,7 +432,7 @@ def sandwich_text_ui(style: str, text: str) -> None: _pause_and_new_line_on_finish() -def show_invalid_aws_creds(ok: bool, msg: Optional[str], optional_header: Optional[str] = None) -> bool: +def show_invalid_aws_creds(ok: bool, msg: str | None, optional_header: str | None = None) -> bool: """Standard UI messaging for AWS credential validation. Returns True if creds are valid, False otherwise. diff --git a/src/bedrock_agentcore_starter_toolkit/cli/common.py b/src/bedrock_agentcore_starter_toolkit/cli/common.py index e815aa08..731d9846 100644 --- a/src/bedrock_agentcore_starter_toolkit/cli/common.py +++ b/src/bedrock_agentcore_starter_toolkit/cli/common.py @@ -1,7 +1,7 @@ """Common utilities for BedrockAgentCore CLI.""" import functools -from typing import NoReturn, Optional +from typing import NoReturn import typer from prompt_toolkit import prompt @@ -32,7 +32,7 @@ def assert_valid_aws_creds_or_exit(failure_message=None): raise typer.Exit(code=1) -def _handle_error(message: str, exception: Optional[Exception] = None) -> NoReturn: +def _handle_error(message: str, exception: Exception | None = None) -> NoReturn: """Handle errors with consistent formatting and exit.""" console.print(f"[red]❌ {message}[/red]") if exception: @@ -51,7 +51,7 @@ def _print_success(message: str) -> None: console.print(f"[green]✓[/green] {message}") -def _prompt_with_default(question: str, default_value: Optional[str] = "") -> str: +def _prompt_with_default(question: str, default_value: str | None = "") -> str: """Prompt user with AWS CLI style [default] format and empty input field.""" prompt_text = question if default_value: diff --git a/src/bedrock_agentcore_starter_toolkit/cli/create/commands.py b/src/bedrock_agentcore_starter_toolkit/cli/create/commands.py index fc70234c..f34656c5 100644 --- a/src/bedrock_agentcore_starter_toolkit/cli/create/commands.py +++ b/src/bedrock_agentcore_starter_toolkit/cli/create/commands.py @@ -3,7 +3,6 @@ import re from contextlib import contextmanager from pathlib import Path -from typing import Optional, Tuple import typer @@ -79,13 +78,13 @@ @create_app.callback(invoke_without_command=True) def create( ctx: typer.Context, - project_name: Optional[str] = project_name_option, - template: Optional[CreateTemplateDisplay] = template_option, + project_name: str | None = project_name_option, + template: CreateTemplateDisplay | None = template_option, sdk: CreateSDKProvider = sdk_option, model_provider: CreateModelProvider = model_provider_option, - provider_api_key: Optional[str] = model_provider_api_key_option, - iac: Optional[CreateIACProvider] = iac_option, - non_interactive_flag: Optional[bool] = non_interactive_flag_opt, + provider_api_key: str | None = model_provider_api_key_option, + iac: CreateIACProvider | None = iac_option, + non_interactive_flag: bool | None = non_interactive_flag_opt, venv_option: bool = venv_option, ): """CLI Implementation for Create Command.""" @@ -170,11 +169,11 @@ def create( def _apply_non_interactive_defaults( - template: Optional[CreateTemplateDisplay], - sdk: Optional[CreateSDKProvider], - model_provider: Optional[CreateModelProvider], - iac: Optional[CreateIACProvider], -) -> Tuple[CreateTemplateDisplay, CreateSDKProvider, CreateModelProvider, Optional[CreateIACProvider]]: + template: CreateTemplateDisplay | None, + sdk: CreateSDKProvider | None, + model_provider: CreateModelProvider | None, + iac: CreateIACProvider | None, +) -> tuple[CreateTemplateDisplay, CreateSDKProvider, CreateModelProvider, CreateIACProvider | None]: """Applies defaults for non-interactive mode. Assumes non-interactive mode is already active. @@ -214,9 +213,9 @@ def _apply_non_interactive_defaults( def _handle_basic_runtime_flow( sdk: CreateSDKProvider, model_provider: CreateModelProvider, - provider_api_key: Optional[str], + provider_api_key: str | None, non_interactive_flag: bool, -) -> Tuple[CreateSDKProvider, CreateModelProvider, Optional[str], bool]: +) -> tuple[CreateSDKProvider, CreateModelProvider, str | None, bool]: """Handles prompt logic for Runtime-only mode.""" if not sdk: sdk = prompt_sdk_provider(is_direct_code_deploy=True) @@ -259,9 +258,9 @@ def _handle_basic_runtime_flow( def _handle_monorepo_flow( sdk: CreateSDKProvider, model_provider: CreateModelProvider, - iac: Optional[CreateIACProvider], + iac: CreateIACProvider | None, non_interactive_flag: bool, -) -> Tuple[CreateSDKProvider, CreateModelProvider, Optional[CreateIACProvider], Optional[BedrockAgentCoreAgentSchema]]: +) -> tuple[CreateSDKProvider, CreateModelProvider, CreateIACProvider | None, BedrockAgentCoreAgentSchema | None]: """Handles prompt logic for Monorepo mode.""" agent_config = None configure_yaml = Path.cwd() / ".bedrock_agentcore.yaml" diff --git a/src/bedrock_agentcore_starter_toolkit/cli/evaluation/commands.py b/src/bedrock_agentcore_starter_toolkit/cli/evaluation/commands.py index e71b9c76..bb4e5efb 100644 --- a/src/bedrock_agentcore_starter_toolkit/cli/evaluation/commands.py +++ b/src/bedrock_agentcore_starter_toolkit/cli/evaluation/commands.py @@ -3,7 +3,6 @@ import json import logging from pathlib import Path -from typing import List, Optional import typer from botocore.exceptions import ClientError @@ -38,7 +37,7 @@ evaluation_app.add_typer(online_app, name="online") -def _get_agent_config_from_file(agent_name: Optional[str] = None) -> Optional[dict]: +def _get_agent_config_from_file(agent_name: str | None = None) -> dict | None: """Get agent configuration from .bedrock_agentcore.yaml file. Args: @@ -76,25 +75,25 @@ def _get_agent_config_from_file(agent_name: Optional[str] = None) -> Optional[di @evaluation_app.command("run") def run_evaluation( - agent: Optional[str] = typer.Option( + agent: str | None = typer.Option( None, "--agent", "-a", help="Agent name (use 'agentcore configure list' to see available agents)", ), - session_id: Optional[str] = typer.Option(None, "--session-id", "-s", help="Override session ID from config"), - agent_id: Optional[str] = typer.Option(None, "--agent-id", help="Override agent ID from config"), - trace_id: Optional[str] = typer.Option( + session_id: str | None = typer.Option(None, "--session-id", "-s", help="Override session ID from config"), + agent_id: str | None = typer.Option(None, "--agent-id", help="Override agent ID from config"), + trace_id: str | None = typer.Option( None, "--trace-id", "-t", help="Evaluate only this trace (includes spans from all previous traces for context)", ), - evaluators: List[str] = typer.Option( # noqa: B008 + evaluators: list[str] = typer.Option( # noqa: B008 [], "--evaluator", "-e", help="Evaluator(s) to use (can specify multiple times)" ), days: int = typer.Option(7, "--days", "-d", help="Number of days to look back for session data (default: 7)"), - output: Optional[str] = typer.Option(None, "--output", "-o", help="Save results to JSON file"), + output: str | None = typer.Option(None, "--output", "-o", help="Save results to JSON file"), ): """Run evaluation on a session. @@ -262,7 +261,7 @@ def get_evaluator( evaluator_id: str = typer.Option( ..., "--evaluator-id", help="Evaluator ID (e.g., Builtin.Helpfulness or custom-id)" ), - output: Optional[str] = typer.Option(None, "--output", "-o", help="Save to JSON file"), + output: str | None = typer.Option(None, "--output", "-o", help="Save to JSON file"), ): """Get detailed information about an evaluator. @@ -354,10 +353,10 @@ def _interactive_create_evaluator(client: EvaluationControlPlaneClient) -> tuple @evaluator_app.command("create") def create_evaluator( - name: Optional[str] = typer.Option(None, "--name", help="Evaluator name"), - config: Optional[str] = typer.Option(None, "--config", help="Path to evaluator config JSON file or inline JSON"), - level: Optional[str] = typer.Option(None, "--level", help="Evaluation level (SESSION, TRACE, TOOL_CALL)"), - description: Optional[str] = typer.Option(None, "--description", help="Evaluator description"), + name: str | None = typer.Option(None, "--name", help="Evaluator name"), + config: str | None = typer.Option(None, "--config", help="Path to evaluator config JSON file or inline JSON"), + level: str | None = typer.Option(None, "--level", help="Evaluation level (SESSION, TRACE, TOOL_CALL)"), + description: str | None = typer.Option(None, "--description", help="Evaluator description"), ): r"""Create a custom evaluator. @@ -435,8 +434,8 @@ def create_evaluator( @evaluator_app.command("update") def update_evaluator( evaluator_id: str = typer.Option(..., "--evaluator-id", help="Evaluator ID to update"), - description: Optional[str] = typer.Option(None, "--description", help="New description"), - config: Optional[str] = typer.Option(None, "--config", help="Path to new config JSON file"), + description: str | None = typer.Option(None, "--description", help="New description"), + config: str | None = typer.Option(None, "--config", help="Path to new config JSON file"), ): r"""Update a custom evaluator. @@ -531,23 +530,21 @@ def delete_evaluator( @online_app.command("create") def create_online_config( - agent_id: Optional[str] = typer.Option(None, "--agent-id", help="Agent ID (uses config file if not provided)"), - config_name: Optional[str] = typer.Option( - None, "--name", "-n", help="Name for the online evaluation configuration" - ), - agent: Optional[str] = typer.Option(None, "--agent", "-a", help="Agent name from config file"), + agent_id: str | None = typer.Option(None, "--agent-id", help="Agent ID (uses config file if not provided)"), + config_name: str | None = typer.Option(None, "--name", "-n", help="Name for the online evaluation configuration"), + agent: str | None = typer.Option(None, "--agent", "-a", help="Agent name from config file"), endpoint: str = typer.Option("DEFAULT", "--endpoint", help="Agent endpoint (DEFAULT, DRAFT, or alias ARN)"), sampling_rate: float = typer.Option(1.0, "--sampling-rate", "-s", help="Sampling rate percentage (0-100)"), - evaluators: List[str] = typer.Option( # noqa: B008 + evaluators: list[str] = typer.Option( # noqa: B008 [], "--evaluator", "-e", help="Evaluator ID(s) to use (can specify multiple times)" ), - description: Optional[str] = typer.Option(None, "--description", "-d", help="Config description"), - execution_role: Optional[str] = typer.Option( + description: str | None = typer.Option(None, "--description", "-d", help="Config description"), + execution_role: str | None = typer.Option( None, "--execution-role", help="IAM role ARN (auto-creates if not provided)" ), no_auto_create_role: bool = typer.Option(False, "--no-auto-create-role", help="Disable automatic role creation"), disabled: bool = typer.Option(False, "--disabled", help="Create config in disabled state"), - output: Optional[str] = typer.Option(None, "--output", "-o", help="Save config details to JSON file"), + output: str | None = typer.Option(None, "--output", "-o", help="Save config details to JSON file"), ): r"""Create online evaluation configuration for continuous agent evaluation. @@ -653,7 +650,7 @@ def create_online_config( @online_app.command("get") def get_online_config( config_id: str = typer.Option(..., "--config-id", help="Online evaluation config ID"), - output: Optional[str] = typer.Option(None, "--output", "-o", help="Save config details to JSON file"), + output: str | None = typer.Option(None, "--output", "-o", help="Save config details to JSON file"), ): """Get online evaluation configuration details. @@ -713,10 +710,10 @@ def get_online_config( @online_app.command("list") def list_online_configs( - agent_id: Optional[str] = typer.Option(None, "--agent-id", help="Filter by agent ID"), - agent: Optional[str] = typer.Option(None, "--agent", "-a", help="Filter by agent name from config file"), + agent_id: str | None = typer.Option(None, "--agent-id", help="Filter by agent ID"), + agent: str | None = typer.Option(None, "--agent", "-a", help="Filter by agent name from config file"), max_results: int = typer.Option(50, "--max-results", help="Maximum number of configs to return"), - output: Optional[str] = typer.Option(None, "--output", "-o", help="Save configs list to JSON file"), + output: str | None = typer.Option(None, "--output", "-o", help="Save configs list to JSON file"), ): """List online evaluation configurations. @@ -795,13 +792,13 @@ def list_online_configs( @online_app.command("update") def update_online_config( config_id: str = typer.Option(..., "--config-id", help="Online evaluation config ID to update"), - status: Optional[str] = typer.Option(None, "--status", help="New status (ENABLED or DISABLED)"), - sampling_rate: Optional[float] = typer.Option(None, "--sampling-rate", "-s", help="New sampling rate (0-100)"), - evaluators: Optional[List[str]] = typer.Option( # noqa: B008 + status: str | None = typer.Option(None, "--status", help="New status (ENABLED or DISABLED)"), + sampling_rate: float | None = typer.Option(None, "--sampling-rate", "-s", help="New sampling rate (0-100)"), + evaluators: list[str] | None = typer.Option( # noqa: B008 None, "--evaluator", "-e", help="New evaluator list (replaces existing, can specify multiple)" ), - description: Optional[str] = typer.Option(None, "--description", "-d", help="New description"), - output: Optional[str] = typer.Option(None, "--output", "-o", help="Save updated config to JSON file"), + description: str | None = typer.Option(None, "--description", "-d", help="New description"), + output: str | None = typer.Option(None, "--output", "-o", help="Save updated config to JSON file"), ): r"""Update online evaluation configuration. @@ -876,9 +873,7 @@ def update_online_config( def delete_online_config( config_id: str = typer.Option(..., "--config-id", help="Online evaluation config ID to delete"), force: bool = typer.Option(False, "--force", "-f", help="Skip all confirmation prompts"), - delete_role: Optional[bool] = typer.Option( - None, "--delete-role/--no-delete-role", help="Delete IAM execution role" - ), + delete_role: bool | None = typer.Option(None, "--delete-role/--no-delete-role", help="Delete IAM execution role"), ): """Delete online evaluation configuration. diff --git a/src/bedrock_agentcore_starter_toolkit/cli/gateway/commands.py b/src/bedrock_agentcore_starter_toolkit/cli/gateway/commands.py index cd4889b3..9c1604c0 100644 --- a/src/bedrock_agentcore_starter_toolkit/cli/gateway/commands.py +++ b/src/bedrock_agentcore_starter_toolkit/cli/gateway/commands.py @@ -1,7 +1,6 @@ """Bedrock AgentCore CLI - Command line interface for Bedrock AgentCore.""" import json -from typing import Optional import typer @@ -15,14 +14,12 @@ @gateway_app.command() def create_mcp_gateway( region: str = typer.Option(None, help="AWS region to use (defaults to us-west-2)"), - name: Optional[str] = typer.Option(None, help="Name of the gateway (defaults to TestGateway)"), - role_arn: Optional[str] = typer.Option( - None, "--role-arn", help="IAM role ARN to use (creates one if not provided)" - ), - authorizer_config: Optional[str] = typer.Option( + name: str | None = typer.Option(None, help="Name of the gateway (defaults to TestGateway)"), + role_arn: str | None = typer.Option(None, "--role-arn", help="IAM role ARN to use (creates one if not provided)"), + authorizer_config: str | None = typer.Option( None, "--authorizer-config", help="Serialized authorizer config JSON (creates one if not provided)" ), - enable_semantic_search: Optional[bool] = typer.Option( + enable_semantic_search: bool | None = typer.Option( True, "--enable_semantic_search", "-sem", help="Enable semantic search tool" ), ) -> None: @@ -49,16 +46,16 @@ def create_mcp_gateway_target( gateway_url: str = typer.Option(None, "--gateway-url", help="URL of the created gateway (required)"), role_arn: str = typer.Option(None, "--role-arn", help="IAM role ARN of the created gateway (required)"), region: str = typer.Option(None, help="AWS region to use (defaults to us-west-2)"), - name: Optional[str] = typer.Option(None, help="Name of the target (defaults to TestGatewayTarget)"), - target_type: Optional[str] = typer.Option( + name: str | None = typer.Option(None, help="Name of the target (defaults to TestGatewayTarget)"), + target_type: str | None = typer.Option( None, "--target-type", help="Type of target: 'lambda', 'openApiSchema', 'mcpServer', or 'smithyModel' (defaults to 'lambda')", ), - target_payload: Optional[str] = typer.Option( + target_payload: str | None = typer.Option( None, "--target-payload", help="Target specification JSON (required for openApiSchema targets)" ), - credentials: Optional[str] = typer.Option( + credentials: str | None = typer.Option( None, help="Credentials JSON for target access (API key or OAuth2, for openApiSchema targets)" ), ) -> None: @@ -101,9 +98,9 @@ def create_mcp_gateway_target( @gateway_app.command(name="delete-mcp-gateway") def delete_mcp_gateway( region: str = typer.Option(None, help="AWS region to use (defaults to us-west-2)"), - gateway_identifier: Optional[str] = typer.Option(None, "--id", help="Gateway ID to delete"), - name: Optional[str] = typer.Option(None, help="Gateway name to delete"), - gateway_arn: Optional[str] = typer.Option(None, "--arn", help="Gateway ARN to delete"), + gateway_identifier: str | None = typer.Option(None, "--id", help="Gateway ID to delete"), + name: str | None = typer.Option(None, help="Gateway name to delete"), + gateway_arn: str | None = typer.Option(None, "--arn", help="Gateway ARN to delete"), force: bool = typer.Option(False, "--force", help="Delete all targets before deleting the gateway"), ) -> None: """Deletes an MCP Gateway. @@ -136,11 +133,11 @@ def delete_mcp_gateway( @gateway_app.command(name="delete-mcp-gateway-target") def delete_mcp_gateway_target( region: str = typer.Option(None, help="AWS region to use (defaults to us-west-2)"), - gateway_identifier: Optional[str] = typer.Option(None, "--id", help="Gateway ID"), - name: Optional[str] = typer.Option(None, help="Gateway name"), - gateway_arn: Optional[str] = typer.Option(None, "--arn", help="Gateway ARN"), - target_id: Optional[str] = typer.Option(None, "--target-id", help="Target ID to delete"), - target_name: Optional[str] = typer.Option(None, "--target-name", help="Target name to delete"), + gateway_identifier: str | None = typer.Option(None, "--id", help="Gateway ID"), + name: str | None = typer.Option(None, help="Gateway name"), + gateway_arn: str | None = typer.Option(None, "--arn", help="Gateway ARN"), + target_id: str | None = typer.Option(None, "--target-id", help="Target ID to delete"), + target_name: str | None = typer.Option(None, "--target-name", help="Target name to delete"), ) -> None: """Deletes an MCP Gateway Target. @@ -169,7 +166,7 @@ def delete_mcp_gateway_target( @gateway_app.command(name="list-mcp-gateways") def list_mcp_gateways( region: str = typer.Option(None, help="AWS region to use"), - name: Optional[str] = typer.Option(None, help="Filter by gateway name"), + name: str | None = typer.Option(None, help="Filter by gateway name"), max_results: int = typer.Option(50, "--max-results", "-m", min=1, max=1000, help="Maximum number of results"), ) -> None: """Lists MCP Gateways. @@ -189,9 +186,9 @@ def list_mcp_gateways( @gateway_app.command(name="get-mcp-gateway") def get_mcp_gateway( region: str = typer.Option(None, help="AWS region to use"), - gateway_identifier: Optional[str] = typer.Option(None, "--id", help="Gateway ID"), - name: Optional[str] = typer.Option(None, help="Gateway name"), - gateway_arn: Optional[str] = typer.Option(None, "--arn", help="Gateway ARN"), + gateway_identifier: str | None = typer.Option(None, "--id", help="Gateway ID"), + name: str | None = typer.Option(None, help="Gateway name"), + gateway_arn: str | None = typer.Option(None, "--arn", help="Gateway ARN"), ) -> None: """Gets details for a specific MCP Gateway. @@ -215,9 +212,9 @@ def get_mcp_gateway( @gateway_app.command(name="list-mcp-gateway-targets") def list_mcp_gateway_targets( region: str = typer.Option(None, help="AWS region to use"), - gateway_identifier: Optional[str] = typer.Option(None, "--id", help="Gateway ID"), - name: Optional[str] = typer.Option(None, help="Gateway name"), - gateway_arn: Optional[str] = typer.Option(None, "--arn", help="Gateway ARN"), + gateway_identifier: str | None = typer.Option(None, "--id", help="Gateway ID"), + name: str | None = typer.Option(None, help="Gateway name"), + gateway_arn: str | None = typer.Option(None, "--arn", help="Gateway ARN"), max_results: int = typer.Option( 50, "--max-results", "-m", min=1, max=1000, help="Maximum number of results to return" ), @@ -246,11 +243,11 @@ def list_mcp_gateway_targets( @gateway_app.command(name="get-mcp-gateway-target") def get_mcp_gateway_target( region: str = typer.Option(None, help="AWS region to use"), - gateway_identifier: Optional[str] = typer.Option(None, "--id", help="Gateway ID"), - name: Optional[str] = typer.Option(None, help="Gateway name "), - gateway_arn: Optional[str] = typer.Option(None, "--arn", help="Gateway ARN"), - target_id: Optional[str] = typer.Option(None, "--target-id", help="Target ID"), - target_name: Optional[str] = typer.Option(None, "--target-name", help="Target name"), + gateway_identifier: str | None = typer.Option(None, "--id", help="Gateway ID"), + name: str | None = typer.Option(None, help="Gateway name "), + gateway_arn: str | None = typer.Option(None, "--arn", help="Gateway ARN"), + target_id: str | None = typer.Option(None, "--target-id", help="Target ID"), + target_name: str | None = typer.Option(None, "--target-name", help="Target name"), ) -> None: """Gets details for a specific Gateway Target. @@ -279,11 +276,11 @@ def get_mcp_gateway_target( @gateway_app.command(name="update-gateway") def update_gateway( region: str = typer.Option(None, help="AWS region to use (defaults to us-west-2)"), - gateway_identifier: Optional[str] = typer.Option(None, "--id", help="Gateway ID to update"), - gateway_arn: Optional[str] = typer.Option(None, "--arn", help="Gateway ARN to update"), - description: Optional[str] = typer.Option(None, "--description", help="New gateway description"), - policy_engine_arn: Optional[str] = typer.Option(None, "--policy-engine-arn", help="Policy engine ARN to attach"), - policy_engine_mode: Optional[str] = typer.Option( + gateway_identifier: str | None = typer.Option(None, "--id", help="Gateway ID to update"), + gateway_arn: str | None = typer.Option(None, "--arn", help="Gateway ARN to update"), + description: str | None = typer.Option(None, "--description", help="New gateway description"), + policy_engine_arn: str | None = typer.Option(None, "--policy-engine-arn", help="Policy engine ARN to attach"), + policy_engine_mode: str | None = typer.Option( None, "--policy-engine-mode", help="Policy engine mode: LOG_ONLY or ENFORCE" ), ) -> None: diff --git a/src/bedrock_agentcore_starter_toolkit/cli/identity/commands.py b/src/bedrock_agentcore_starter_toolkit/cli/identity/commands.py index f76ab436..6aacab52 100644 --- a/src/bedrock_agentcore_starter_toolkit/cli/identity/commands.py +++ b/src/bedrock_agentcore_starter_toolkit/cli/identity/commands.py @@ -4,7 +4,6 @@ import logging import os from pathlib import Path -from typing import List, Optional import typer from rich.panel import Panel @@ -35,13 +34,13 @@ def create_credential_provider( provider_type: str = typer.Option(..., "--type", "-t", help="Provider type: cognito, github, google, salesforce"), client_id: str = typer.Option(..., "--client-id", help="OAuth client ID"), client_secret: str = typer.Option(..., "--client-secret", help="OAuth client secret"), - discovery_url: Optional[str] = typer.Option( + discovery_url: str | None = typer.Option( None, "--discovery-url", help="OAuth discovery URL (required for cognito)" ), - cognito_pool_id: Optional[str] = typer.Option( + cognito_pool_id: str | None = typer.Option( None, "--cognito-pool-id", help="Cognito pool ID (for auto-updating callback URLs)" ), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region"), ): r"""Create an OAuth2 credential provider for outbound authentication (3LO support). @@ -146,13 +145,13 @@ def create_credential_provider( @identity_app.command("create-workload-identity") def create_workload_identity( - name: Optional[str] = typer.Option(None, "--name", "-n", help="Workload identity name (auto-generated if empty)"), - return_urls: Optional[str] = typer.Option( + name: str | None = typer.Option(None, "--name", "-n", help="Workload identity name (auto-generated if empty)"), + return_urls: str | None = typer.Option( None, "--return-urls", help="Optional: OAuth return URLs for enhanced session binding security. Not required for basic OAuth flows.", ), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region"), ): """Create a workload identity for your agent. @@ -223,11 +222,11 @@ def create_workload_identity( @identity_app.command("update-workload-identity") def update_workload_identity( name: str = typer.Option(..., "--name", "-n", help="Workload identity name"), - add_return_urls: Optional[str] = typer.Option(None, "--add-return-urls", help="Comma-separated return URLs to ADD"), - set_return_urls: Optional[str] = typer.Option( + add_return_urls: str | None = typer.Option(None, "--add-return-urls", help="Comma-separated return URLs to ADD"), + set_return_urls: str | None = typer.Option( None, "--set-return-urls", help="Comma-separated return URLs to SET (replaces existing)" ), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region"), ): r"""Update workload identity callback URLs. @@ -290,22 +289,22 @@ def get_cognito_inbound_token( auth_flow: str = typer.Option( "user", "--auth-flow", help="OAuth flow type: 'user' (USER_FEDERATION) or 'm2m' (M2M)" ), - pool_id: Optional[str] = typer.Option( + pool_id: str | None = typer.Option( None, "--pool-id", help="Cognito User Pool ID (auto-loads from RUNTIME_POOL_ID env var)" ), - client_id: Optional[str] = typer.Option( + client_id: str | None = typer.Option( None, "--client-id", help="Cognito App Client ID (auto-loads from RUNTIME_CLIENT_ID env var)" ), - client_secret: Optional[str] = typer.Option( + client_secret: str | None = typer.Option( None, "--client-secret", help="Client secret (auto-loads from RUNTIME_CLIENT_SECRET env var, required for m2m)" ), - username: Optional[str] = typer.Option( + username: str | None = typer.Option( None, "--username", "-u", help="Username (auto-loads from RUNTIME_USERNAME env var, required for user flow)" ), - password: Optional[str] = typer.Option( + password: str | None = typer.Option( None, "--password", "-p", help="Password (auto-loads from RUNTIME_PASSWORD env var, required for user flow)" ), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region"), ): """Get an access token from Cognito for Runtime inbound authentication. @@ -468,7 +467,7 @@ def list_credential_providers(): def _build_provider_config( - provider_type: str, name: str, client_id: str, client_secret: str, discovery_url: Optional[str] + provider_type: str, name: str, client_id: str, client_secret: str, discovery_url: str | None ) -> dict: """Build provider configuration based on type.""" if provider_type == "cognito": @@ -545,7 +544,7 @@ def _save_provider_config(name: str, arn: str, provider_type: str, callback_url: _handle_warn(".bedrock_agentcore.yaml not found. Provider created but not saved to config.") -def _save_workload_config(name: str, arn: str, return_urls: List[str]): +def _save_workload_config(name: str, arn: str, return_urls: list[str]): """Save workload identity configuration to .bedrock_agentcore.yaml.""" config_path = Path.cwd() / ".bedrock_agentcore.yaml" if config_path.exists(): @@ -565,7 +564,7 @@ def _save_workload_config(name: str, arn: str, return_urls: List[str]): @identity_app.command("setup-cognito") def setup_cognito( - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region (defaults to configured region)"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region (defaults to configured region)"), auth_flow: str = typer.Option( "user", "--auth-flow", help="Identity pool OAuth flow type: user (USER_FEDERATION) or m2m (client_credentials)" ), @@ -716,7 +715,7 @@ def setup_aws_jwt( help="Signing algorithm: ES384 (default) or RS256", ), duration_seconds: int = typer.Option(300, "--duration", "-d", help="Default token duration in seconds (60-3600)"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region"), ): """Set up AWS IAM JWT federation for M2M authentication without secrets. @@ -892,7 +891,7 @@ def list_aws_jwt(): @identity_app.command("cleanup") def cleanup_identity( - agent: Optional[str] = typer.Option(None, "--agent", "-a", help="Agent name to clean up Identity resources for"), + agent: str | None = typer.Option(None, "--agent", "-a", help="Agent name to clean up Identity resources for"), force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation prompts"), ): """Clean up Identity resources for an agent. diff --git a/src/bedrock_agentcore_starter_toolkit/cli/memory/browser.py b/src/bedrock_agentcore_starter_toolkit/cli/memory/browser.py index cfb924a6..b314188f 100644 --- a/src/bedrock_agentcore_starter_toolkit/cli/memory/browser.py +++ b/src/bedrock_agentcore_starter_toolkit/cli/memory/browser.py @@ -3,7 +3,7 @@ import json import logging from dataclasses import dataclass, field, replace -from typing import Any, Dict, List, Optional +from typing import Any from botocore.exceptions import BotoCoreError, ClientError from prompt_toolkit import Application @@ -24,13 +24,13 @@ class NavigationState: """State for navigation through memory hierarchy.""" - memory_id: Optional[str] = None - actor_id: Optional[str] = None - session_id: Optional[str] = None - namespace: Optional[str] = None - namespace_template: Optional[str] = None - event_index: Optional[int] = None - record_index: Optional[int] = None + memory_id: str | None = None + actor_id: str | None = None + session_id: str | None = None + namespace: str | None = None + namespace_template: str | None = None + event_index: int | None = None + record_index: int | None = None view: str = "memory" cursor: int = 0 @@ -39,12 +39,12 @@ class NavigationState: class BrowserData: """Cached data for the browser.""" - memory: Optional[Dict[str, Any]] = None - actors: List[Dict[str, Any]] = field(default_factory=list) - sessions: List[Dict[str, Any]] = field(default_factory=list) - events: List[Dict[str, Any]] = field(default_factory=list) - namespaces: List[Dict[str, Any]] = field(default_factory=list) - records: List[Dict[str, Any]] = field(default_factory=list) + memory: dict[str, Any] | None = None + actors: list[dict[str, Any]] = field(default_factory=list) + sessions: list[dict[str, Any]] = field(default_factory=list) + events: list[dict[str, Any]] = field(default_factory=list) + namespaces: list[dict[str, Any]] = field(default_factory=list) + records: list[dict[str, Any]] = field(default_factory=list) class MemoryBrowser: @@ -54,25 +54,25 @@ def __init__( self, manager: MemoryManager, memory_id: str, - visualizer: Optional[MemoryVisualizer] = None, - initial_memory: Optional[Dict[str, Any]] = None, + visualizer: MemoryVisualizer | None = None, + initial_memory: dict[str, Any] | None = None, ) -> None: """Initialize the memory browser.""" self.manager = manager self.console = Console() self.visualizer = visualizer or MemoryVisualizer(self.console) - self.nav_stack: List[NavigationState] = [] + self.nav_stack: list[NavigationState] = [] self.current = NavigationState(memory_id=memory_id, view="memory") self.data = BrowserData() if initial_memory: self.data.memory = initial_memory self.verbose = False self.cursor = 0 - self.items: List[Any] = [] - self.actors_next_token: Optional[str] = None - self.sessions_next_token: Optional[str] = None - self.events_next_token: Optional[str] = None - self.records_next_token: Optional[str] = None + self.items: list[Any] = [] + self.actors_next_token: str | None = None + self.sessions_next_token: str | None = None + self.events_next_token: str | None = None + self.records_next_token: str | None = None def run(self) -> None: """Run the interactive browser.""" @@ -669,7 +669,7 @@ def _select_record(self) -> None: self.current.record_index = self.cursor self.current.view = "record_detail" - def _extract_role(self, event: Dict[str, Any]) -> str: + def _extract_role(self, event: dict[str, Any]) -> str: """Extract role from event.""" payload = event.get("payload", {}) if isinstance(payload, dict): @@ -680,7 +680,7 @@ def _extract_role(self, event: Dict[str, Any]) -> str: return item["role"] return "" - def _extract_text(self, event: Dict[str, Any]) -> str: + def _extract_text(self, event: dict[str, Any]) -> str: """Extract text from event.""" payload = event.get("payload", {}) if isinstance(payload, dict): @@ -691,7 +691,7 @@ def _extract_text(self, event: Dict[str, Any]) -> str: return item["text"] return "" - def _extract_payload_snippet(self, event: Dict[str, Any]) -> str: + def _extract_payload_snippet(self, event: dict[str, Any]) -> str: """Extract a snippet from raw payload for preview.""" payload = event.get("payload") if not payload: @@ -701,7 +701,7 @@ def _extract_payload_snippet(self, event: Dict[str, Any]) -> str: return f"{raw[:60]}…" return raw - def _extract_record_text(self, record: Dict[str, Any]) -> str: + def _extract_record_text(self, record: dict[str, Any]) -> str: """Extract text from record.""" content = record.get("content", {}) if isinstance(content, dict): diff --git a/src/bedrock_agentcore_starter_toolkit/cli/memory/commands.py b/src/bedrock_agentcore_starter_toolkit/cli/memory/commands.py index 2f204419..2a4657fd 100644 --- a/src/bedrock_agentcore_starter_toolkit/cli/memory/commands.py +++ b/src/bedrock_agentcore_starter_toolkit/cli/memory/commands.py @@ -4,7 +4,7 @@ import logging from dataclasses import dataclass from pathlib import Path -from typing import Any, Dict, List, Optional +from typing import Any import typer from rich.panel import Panel @@ -34,20 +34,20 @@ class ResolvedMemoryConfig: """Resolved memory configuration from explicit params or config file.""" memory_id: str - region: Optional[str] + region: str | None @dataclass class _ConfigLookupResult: """Result of looking up memory config from file.""" - memory_id: Optional[str] = None - region: Optional[str] = None + memory_id: str | None = None + region: str | None = None config_exists: bool = False - agent_name: Optional[str] = None # The resolved agent name (could be default) + agent_name: str | None = None # The resolved agent name (could be default) -def _get_memory_config_from_file(agent_name: Optional[str] = None) -> _ConfigLookupResult: +def _get_memory_config_from_file(agent_name: str | None = None) -> _ConfigLookupResult: """Load memory config from .bedrock_agentcore.yaml if it exists. Returns _ConfigLookupResult with details about what was found. @@ -78,9 +78,9 @@ def _get_memory_config_from_file(agent_name: Optional[str] = None) -> _ConfigLoo def _resolve_memory_config( - agent: Optional[str] = None, - memory_id: Optional[str] = None, - region: Optional[str] = None, + agent: str | None = None, + memory_id: str | None = None, + region: str | None = None, show_hint: bool = True, ) -> ResolvedMemoryConfig: """Resolve memory configuration from explicit params or config file. @@ -99,7 +99,7 @@ def _resolve_memory_config( """ final_memory_id = memory_id final_region = region - config_result: Optional[_ConfigLookupResult] = None + config_result: _ConfigLookupResult | None = None if not final_memory_id: config_result = _get_memory_config_from_file(agent) @@ -144,8 +144,8 @@ def _resolve_memory_config( def _validate_events_options( all_events: bool, last: int, - session_id: Optional[str], - actor_id: Optional[str], + session_id: str | None, + actor_id: str | None, list_sessions: bool, ) -> None: """Validate mutually exclusive options for events command.""" @@ -162,8 +162,8 @@ def _validate_events_options( def _validate_records_options( all_records: bool, last: int, - namespace: Optional[str], - query: Optional[str], + namespace: str | None, + query: str | None, ) -> None: """Validate mutually exclusive options for records command.""" if all_records and last != 1: @@ -179,7 +179,7 @@ def _validate_records_options( # ==================== Data Collection Utilities ==================== -def _collect_all_events(manager: MemoryManager, memory_id: str) -> List[Dict[str, Any]]: +def _collect_all_events(manager: MemoryManager, memory_id: str) -> list[dict[str, Any]]: """Collect all events across all actors/sessions in a memory.""" all_events = [] actors = manager.list_actors(memory_id) @@ -203,11 +203,11 @@ def _collect_all_events(manager: MemoryManager, memory_id: str) -> List[Dict[str def _collect_all_records( manager: MemoryManager, memory_id: str, - namespace: Optional[str], + namespace: str | None, max_results: int, -) -> List[Dict[str, Any]]: +) -> list[dict[str, Any]]: """Collect records from specified namespace or all namespaces.""" - all_records: List[Dict[str, Any]] = [] + all_records: list[dict[str, Any]] = [] if namespace: # Single namespace @@ -232,7 +232,7 @@ def _collect_records_from_namespace_template( memory_id: str, ns_template: str, max_results: int, - all_records: List[Dict[str, Any]], + all_records: list[dict[str, Any]], ) -> None: """Collect records from a namespace template, resolving placeholders.""" if "{actorId}" not in ns_template and "{sessionId}" not in ns_template: @@ -264,7 +264,7 @@ def _try_collect_records( memory_id: str, namespace: str, max_results: int, - all_records: List[Dict[str, Any]], + all_records: list[dict[str, Any]], ) -> None: """Try to collect records from a namespace, ignoring errors.""" try: @@ -282,19 +282,17 @@ def _try_collect_records( @memory_app.command() def create( name: str = typer.Argument(..., help="Name for the memory resource"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region (default: session region)"), - description: Optional[str] = typer.Option(None, "--description", "-d", help="Description for the memory"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region (default: session region)"), + description: str | None = typer.Option(None, "--description", "-d", help="Description for the memory"), event_expiry_days: int = typer.Option(90, "--event-expiry-days", "-e", help="Event retention in days"), - strategies: Optional[str] = typer.Option( + strategies: str | None = typer.Option( None, "--strategies", "-s", help='JSON string of memory strategies (e.g., \'[{"semanticMemoryStrategy": {"name": "Facts"}}]\')', ), - memory_execution_role_arn: Optional[str] = typer.Option( - None, "--role-arn", help="IAM role ARN for memory execution" - ), - encryption_key_arn: Optional[str] = typer.Option(None, "--encryption-key-arn", help="KMS key ARN for encryption"), + memory_execution_role_arn: str | None = typer.Option(None, "--role-arn", help="IAM role ARN for memory execution"), + encryption_key_arn: str | None = typer.Option(None, "--encryption-key-arn", help="KMS key ARN for encryption"), wait: bool = typer.Option(True, "--wait/--no-wait", help="Wait for memory to become ACTIVE"), max_wait: int = typer.Option(300, "--max-wait", help="Maximum wait time in seconds"), ) -> None: @@ -352,18 +350,18 @@ def create( @memory_app.command() def show( - agent: Optional[str] = typer.Option( + agent: str | None = typer.Option( None, "--agent", "-a", help="Agent name (use 'agentcore configure list' to see available agents)", ), - memory_id: Optional[str] = typer.Option(None, "--memory-id", "-m", help="Memory ID (overrides config)"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region"), + memory_id: str | None = typer.Option(None, "--memory-id", "-m", help="Memory ID (overrides config)"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region"), all_events: bool = typer.Option(False, "--all", help="Show all events in memory"), verbose: bool = typer.Option(False, "--verbose", "-v", help="Show full configuration and event content"), max_events: int = typer.Option(10, "--max-events", "-n", help="Max events per session (with --all)"), - output: Optional[str] = typer.Option(None, "--output", "-o", help="Export to JSON file"), + output: str | None = typer.Option(None, "--output", "-o", help="Export to JSON file"), ) -> None: """Show memory details and events. @@ -428,7 +426,7 @@ def show( @memory_app.command() def get( memory_id: str = typer.Argument(..., help="Memory resource ID"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region"), ) -> None: """Get details of a memory resource. @@ -457,7 +455,7 @@ def get( @memory_app.command(name="list") def list_memories_cmd( - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region"), max_results: int = typer.Option(100, "--max-results", "-n", help="Maximum number of results"), ) -> None: """List all memory resources. @@ -479,7 +477,7 @@ def list_memories_cmd( @memory_app.command() def delete( memory_id: str = typer.Argument(..., help="Memory resource ID to delete"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region"), wait: bool = typer.Option(False, "--wait", help="Wait for deletion to complete"), max_wait: int = typer.Option(300, "--max-wait", help="Maximum wait time in seconds"), ) -> None: @@ -507,7 +505,7 @@ def delete( @memory_app.command() def status( memory_id: str = typer.Argument(..., help="Memory resource ID"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region"), ) -> None: """Get memory provisioning status. @@ -531,11 +529,11 @@ def status( @show_app.callback() def show_callback( ctx: typer.Context, - agent: Optional[str] = typer.Option(None, "--agent", "-a", help="Agent name from config"), - memory_id: Optional[str] = typer.Option(None, "--memory-id", "-m", help="Memory resource ID"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region"), + agent: str | None = typer.Option(None, "--agent", "-a", help="Agent name from config"), + memory_id: str | None = typer.Option(None, "--memory-id", "-m", help="Memory resource ID"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region"), verbose: bool = typer.Option(False, "--verbose", "-v", help="Show full details"), - output: Optional[str] = typer.Option(None, "--output", "-o", help="Export to JSON file"), + output: str | None = typer.Option(None, "--output", "-o", help="Export to JSON file"), ) -> None: """Show memory details from config or explicit memory ID. @@ -579,18 +577,18 @@ def show_callback( @show_app.command(name="events") def show_events( - agent: Optional[str] = typer.Option(None, "--agent", help="Agent name from config"), - memory_id: Optional[str] = typer.Option(None, "--memory-id", "-m", help="Memory resource ID"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region"), + agent: str | None = typer.Option(None, "--agent", help="Agent name from config"), + memory_id: str | None = typer.Option(None, "--memory-id", "-m", help="Memory resource ID"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region"), all_events: bool = typer.Option(False, "--all", help="Show events tree"), - actor_id: Optional[str] = typer.Option(None, "--actor-id", "-a", help="Filter to specific actor"), - session_id: Optional[str] = typer.Option(None, "--session-id", "-s", help="Filter to specific session"), + actor_id: str | None = typer.Option(None, "--actor-id", "-a", help="Filter to specific actor"), + session_id: str | None = typer.Option(None, "--session-id", "-s", help="Filter to specific session"), last: int = typer.Option(1, "--last", "-l", help="Show Nth most recent event (default: 1=latest)"), list_actors: bool = typer.Option(False, "--list-actors", help="List all actor IDs"), list_sessions: bool = typer.Option(False, "--list-sessions", help="List all session IDs for actor"), verbose: bool = typer.Option(False, "--verbose", "-v", help="Show full content"), max_events: int = typer.Option(10, "--max-events", help="Max events per session used with --all"), - output: Optional[str] = typer.Option(None, "--output", "-o", help="Export to JSON file"), + output: str | None = typer.Option(None, "--output", "-o", help="Export to JSON file"), ) -> None: """Show memory events. @@ -665,7 +663,7 @@ def _handle_list_actors(manager: MemoryManager, memory_id: str) -> None: console.print(f"\n[dim]{len(actors)} actors[/dim]") -def _handle_list_sessions(manager: MemoryManager, memory_id: str, actor_id: Optional[str]) -> None: +def _handle_list_sessions(manager: MemoryManager, memory_id: str, actor_id: str | None) -> None: """Handle --list-sessions mode.""" if not actor_id: _handle_error("--list-sessions requires --actor-id") @@ -684,7 +682,7 @@ def _handle_show_nth_event( memory_id: str, last: int, verbose: bool, - output: Optional[str], + output: str | None, ) -> None: """Handle showing the Nth most recent event.""" console.print(f"[dim]Fetching events for {memory_id}...[/dim]") @@ -712,16 +710,16 @@ def _handle_show_nth_event( @show_app.command(name="records") def show_records( - agent: Optional[str] = typer.Option(None, "--agent", help="Agent name from config"), - memory_id: Optional[str] = typer.Option(None, "--memory-id", "-m", help="Memory resource ID"), - namespace: Optional[str] = typer.Option(None, "--namespace", "-n", help="Namespace to list records from"), - query: Optional[str] = typer.Option(None, "--query", "-q", help="Semantic search query"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region"), + agent: str | None = typer.Option(None, "--agent", help="Agent name from config"), + memory_id: str | None = typer.Option(None, "--memory-id", "-m", help="Memory resource ID"), + namespace: str | None = typer.Option(None, "--namespace", "-n", help="Namespace to list records from"), + query: str | None = typer.Option(None, "--query", "-q", help="Semantic search query"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region"), all_records: bool = typer.Option(False, "--all", help="Show all records across all namespaces"), last: int = typer.Option(1, "--last", "-l", help="Show Nth most recent record (default: 1=latest)"), verbose: bool = typer.Option(False, "--verbose", "-v", help="Show full record content"), max_results: int = typer.Option(10, "--max-results", help="Max records to return"), - output: Optional[str] = typer.Option(None, "--output", "-o", help="Export to JSON file"), + output: str | None = typer.Option(None, "--output", "-o", help="Export to JSON file"), ) -> None: """Show memory records (long-term memory). @@ -781,7 +779,7 @@ def _handle_semantic_search( manager: MemoryManager, visualizer: MemoryVisualizer, memory_id: str, - namespace: Optional[str], + namespace: str | None, query: str, max_results: int, verbose: bool, @@ -801,11 +799,11 @@ def _handle_show_nth_record( manager: MemoryManager, visualizer: MemoryVisualizer, memory_id: str, - namespace: Optional[str], + namespace: str | None, last: int, verbose: bool, max_results: int, - output: Optional[str], + output: str | None, ) -> None: """Handle showing the Nth most recent record.""" console.print(f"[dim]Fetching records for {memory_id}...[/dim]") @@ -837,9 +835,9 @@ def _handle_show_nth_record( @memory_app.command() def browse( - memory_id: Optional[str] = typer.Option(None, "--memory-id", "-m", help="Memory ID to browse"), - agent: Optional[str] = typer.Option(None, "--agent", "-a", help="Agent name from config"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region"), + memory_id: str | None = typer.Option(None, "--memory-id", "-m", help="Memory ID to browse"), + agent: str | None = typer.Option(None, "--agent", "-a", help="Agent name from config"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region"), ) -> None: """Interactive TUI browser for exploring memory content. diff --git a/src/bedrock_agentcore_starter_toolkit/cli/observability/commands.py b/src/bedrock_agentcore_starter_toolkit/cli/observability/commands.py index a6b839ef..2c047448 100644 --- a/src/bedrock_agentcore_starter_toolkit/cli/observability/commands.py +++ b/src/bedrock_agentcore_starter_toolkit/cli/observability/commands.py @@ -4,7 +4,6 @@ import logging from datetime import datetime, timedelta from pathlib import Path -from typing import Optional import typer from rich.text import Text @@ -34,7 +33,7 @@ def _get_default_time_range(days: int = DEFAULT_LOOKBACK_DAYS) -> tuple[int, int return int(start_time.timestamp() * 1000), int(end_time.timestamp() * 1000) -def _get_agent_config_from_file(agent_name: Optional[str] = None) -> Optional[dict]: +def _get_agent_config_from_file(agent_name: str | None = None) -> dict | None: """Load agent configuration from .bedrock_agentcore.yaml.""" config_path = Path.cwd() / ".bedrock_agentcore.yaml" config = load_config_if_exists(config_path) @@ -65,10 +64,10 @@ def _get_agent_config_from_file(agent_name: Optional[str] = None) -> Optional[di def _create_observability_client( - agent_id: Optional[str], - agent: Optional[str] = None, - region: Optional[str] = None, - runtime_suffix: Optional[str] = None, + agent_id: str | None, + agent: str | None = None, + region: str | None = None, + runtime_suffix: str | None = None, ) -> tuple[ObservabilityClient, str, str]: """Create stateless ObservabilityClient and return agent context. @@ -241,15 +240,15 @@ def _export_trace_data_to_json(trace_data: TraceData, output_path: str, data_typ @observability_app.command("show") def show( - agent: Optional[str] = typer.Option( + agent: str | None = typer.Option( None, "--agent", "-a", help="Agent name (use 'agentcore configure list' to see available agents)", ), - trace_id: Optional[str] = typer.Option(None, "--trace-id", "-t", help="Trace ID to visualize"), - session_id: Optional[str] = typer.Option(None, "--session-id", "-s", help="Session ID to visualize"), - agent_id: Optional[str] = typer.Option(None, "--agent-id", help="Override agent ID from config"), + trace_id: str | None = typer.Option(None, "--trace-id", "-t", help="Trace ID to visualize"), + session_id: str | None = typer.Option(None, "--session-id", "-s", help="Session ID to visualize"), + agent_id: str | None = typer.Option(None, "--agent-id", help="Override agent ID from config"), days: int = typer.Option( DEFAULT_LOOKBACK_DAYS, "--days", "-d", help=f"Number of days to look back (default: {DEFAULT_LOOKBACK_DAYS})" ), @@ -258,7 +257,7 @@ def show( verbose: bool = typer.Option( False, "--verbose", "-v", help="Show full event payloads and detailed metadata without truncation" ), - output: Optional[str] = typer.Option(None, "--output", "-o", help="Export to JSON file"), + output: str | None = typer.Option(None, "--output", "-o", help="Export to JSON file"), last: int = typer.Option(1, "--last", "-n", help="[Session only] Show Nth most recent trace (default: 1 = latest)"), ) -> None: """Show trace details with full visualization. @@ -407,7 +406,7 @@ def _show_trace_view( start_time_ms: int, end_time_ms: int, verbose: bool, - output: Optional[str], + output: str | None, agent_id: str, endpoint_name: str = "DEFAULT", ) -> None: @@ -449,7 +448,7 @@ def _show_session_view( end_time_ms: int, verbose: bool, errors_only: bool, - output: Optional[str], + output: str | None, agent_id: str, endpoint_name: str = "DEFAULT", show_all: bool = True, @@ -551,16 +550,16 @@ def get_latest_time(spans_list): @observability_app.command("list") def list_traces( - agent: Optional[str] = typer.Option( + agent: str | None = typer.Option( None, "--agent", "-a", help="Agent name (use 'agentcore configure list' to see available agents)", ), - session_id: Optional[str] = typer.Option( + session_id: str | None = typer.Option( None, "--session-id", "-s", help="Session ID to list traces from. Omit to use config." ), - agent_id: Optional[str] = typer.Option(None, "--agent-id", help="Override agent ID from config"), + agent_id: str | None = typer.Option(None, "--agent-id", help="Override agent ID from config"), days: int = typer.Option( DEFAULT_LOOKBACK_DAYS, "--days", "-d", help=f"Number of days to look back (default: {DEFAULT_LOOKBACK_DAYS})" ), diff --git a/src/bedrock_agentcore_starter_toolkit/cli/policy/commands.py b/src/bedrock_agentcore_starter_toolkit/cli/policy/commands.py index aa1e2a2d..a12e79b2 100644 --- a/src/bedrock_agentcore_starter_toolkit/cli/policy/commands.py +++ b/src/bedrock_agentcore_starter_toolkit/cli/policy/commands.py @@ -1,7 +1,6 @@ """Bedrock AgentCore Policy CLI commands.""" import json -from typing import Optional import typer @@ -19,8 +18,8 @@ @requires_aws_creds def create_policy_engine( name: str = typer.Option(..., "--name", "-n", help="Name of the policy engine"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), - description: Optional[str] = typer.Option(None, "--description", "-d", help="Policy engine description"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), + description: str | None = typer.Option(None, "--description", "-d", help="Policy engine description"), ) -> None: """Create a new policy engine.""" client = PolicyClient(region_name=region) @@ -41,7 +40,7 @@ def create_policy_engine( @requires_aws_creds def get_policy_engine( policy_engine_id: str = typer.Option(..., "--policy-engine-id", "-e", help="Policy engine ID"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), ) -> None: """Get policy engine details.""" client = PolicyClient(region_name=region) @@ -63,8 +62,8 @@ def get_policy_engine( @requires_aws_creds def update_policy_engine( policy_engine_id: str = typer.Option(..., "--policy-engine-id", "-e", help="Policy engine ID"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), - description: Optional[str] = typer.Option(None, "--description", "-d", help="Updated description"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), + description: str | None = typer.Option(None, "--description", "-d", help="Updated description"), ) -> None: """Update a policy engine.""" client = PolicyClient(region_name=region) @@ -82,9 +81,9 @@ def update_policy_engine( @policy_app.command("list-policy-engines") @requires_aws_creds def list_policy_engines( - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), - max_results: Optional[int] = typer.Option(None, "--max-results", help="Maximum number of results"), - next_token: Optional[str] = typer.Option(None, "--next-token", help="Token for pagination"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), + max_results: int | None = typer.Option(None, "--max-results", help="Maximum number of results"), + next_token: str | None = typer.Option(None, "--next-token", help="Token for pagination"), ) -> None: """List policy engines.""" from rich.table import Table @@ -122,7 +121,7 @@ def list_policy_engines( @requires_aws_creds def delete_policy_engine( policy_engine_id: str = typer.Option(..., "--policy-engine-id", "-e", help="Policy engine ID"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), ) -> None: """Delete a policy engine.""" client = PolicyClient(region_name=region) @@ -147,9 +146,9 @@ def create_policy( "-def", help='Policy definition JSON (e.g., \'{"cedar":{"statement":"permit(...);"}}\')', ), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), - description: Optional[str] = typer.Option(None, "--description", "-d", help="Policy description"), - validation_mode: Optional[str] = typer.Option( + region: str | None = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), + description: str | None = typer.Option(None, "--description", "-d", help="Policy description"), + validation_mode: str | None = typer.Option( None, "--validation-mode", help="Validation mode (FAIL_ON_ANY_FINDINGS, IGNORE_ALL_FINDINGS)" ), ) -> None: @@ -184,7 +183,7 @@ def create_policy( def get_policy( policy_engine_id: str = typer.Option(..., "--policy-engine-id", "-e", help="Policy engine ID"), policy_id: str = typer.Option(..., "--policy-id", "-p", help="Policy ID"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), ) -> None: """Get policy details.""" client = PolicyClient(region_name=region) @@ -211,9 +210,9 @@ def update_policy( policy_engine_id: str = typer.Option(..., "--policy-engine-id", "-e", help="Policy engine ID"), policy_id: str = typer.Option(..., "--policy-id", "-p", help="Policy ID"), definition: str = typer.Option(..., "--definition", "-def", help="Updated policy definition JSON"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), - description: Optional[str] = typer.Option(None, "--description", "-d", help="Updated description"), - validation_mode: Optional[str] = typer.Option( + region: str | None = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), + description: str | None = typer.Option(None, "--description", "-d", help="Updated description"), + validation_mode: str | None = typer.Option( None, "--validation-mode", help="Validation mode (FAIL_ON_ANY_FINDINGS, IGNORE_ALL_FINDINGS)" ), ) -> None: @@ -245,10 +244,10 @@ def update_policy( @requires_aws_creds def list_policies( policy_engine_id: str = typer.Option(..., "--policy-engine-id", "-e", help="Policy engine ID"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), - target_resource_scope: Optional[str] = typer.Option(None, "--target-resource-scope", help="Filter by resource ARN"), - max_results: Optional[int] = typer.Option(None, "--max-results", help="Maximum number of results"), - next_token: Optional[str] = typer.Option(None, "--next-token", help="Token for pagination"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), + target_resource_scope: str | None = typer.Option(None, "--target-resource-scope", help="Filter by resource ARN"), + max_results: int | None = typer.Option(None, "--max-results", help="Maximum number of results"), + next_token: str | None = typer.Option(None, "--next-token", help="Token for pagination"), ) -> None: """List policies.""" from rich.table import Table @@ -292,7 +291,7 @@ def list_policies( def delete_policy( policy_engine_id: str = typer.Option(..., "--policy-engine-id", "-e", help="Policy engine ID"), policy_id: str = typer.Option(..., "--policy-id", "-p", help="Policy ID"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), ) -> None: """Delete a policy.""" client = PolicyClient(region_name=region) @@ -318,7 +317,7 @@ def start_policy_generation( "-c", help="Natural language policy description", ), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), ) -> None: r"""Start a policy generation workflow. @@ -354,7 +353,7 @@ def start_policy_generation( def get_policy_generation( policy_engine_id: str = typer.Option(..., "--policy-engine-id", "-e", help="Policy engine ID"), generation_id: str = typer.Option(..., "--generation-id", "-g", help="Generation ID"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), ) -> None: """Get policy generation details.""" client = PolicyClient(region_name=region) @@ -376,9 +375,9 @@ def get_policy_generation( def list_policy_generation_assets( policy_engine_id: str = typer.Option(..., "--policy-engine-id", "-e", help="Policy engine ID"), generation_id: str = typer.Option(..., "--generation-id", "-g", help="Generation ID"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), - max_results: Optional[int] = typer.Option(None, "--max-results", help="Maximum number of results"), - next_token: Optional[str] = typer.Option(None, "--next-token", help="Token for pagination"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), + max_results: int | None = typer.Option(None, "--max-results", help="Maximum number of results"), + next_token: str | None = typer.Option(None, "--next-token", help="Token for pagination"), ) -> None: """List policy generation assets (generated policies).""" client = PolicyClient(region_name=region) @@ -396,9 +395,9 @@ def list_policy_generation_assets( @requires_aws_creds def list_policy_generations( policy_engine_id: str = typer.Option(..., "--policy-engine-id", "-e", help="Policy engine ID"), - region: Optional[str] = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), - max_results: Optional[int] = typer.Option(None, "--max-results", help="Maximum number of results"), - next_token: Optional[str] = typer.Option(None, "--next-token", help="Token for pagination"), + region: str | None = typer.Option(None, "--region", "-r", help="AWS region (default: us-east-1)"), + max_results: int | None = typer.Option(None, "--max-results", help="Maximum number of results"), + next_token: str | None = typer.Option(None, "--next-token", help="Token for pagination"), ) -> None: """List policy generations.""" from rich.table import Table diff --git a/src/bedrock_agentcore_starter_toolkit/cli/runtime/_configure_impl.py b/src/bedrock_agentcore_starter_toolkit/cli/runtime/_configure_impl.py index fb8f6348..2dc157b0 100644 --- a/src/bedrock_agentcore_starter_toolkit/cli/runtime/_configure_impl.py +++ b/src/bedrock_agentcore_starter_toolkit/cli/runtime/_configure_impl.py @@ -1,6 +1,5 @@ import json from pathlib import Path -from typing import Optional from prompt_toolkit import prompt from prompt_toolkit.completion import PathCompleter @@ -716,7 +715,7 @@ def _validate_requirements_file(file_path: str) -> str: _handle_error(str(e), e) -def _prompt_for_requirements_file(prompt_text: str, source_path: str, default: str = "") -> Optional[str]: +def _prompt_for_requirements_file(prompt_text: str, source_path: str, default: str = "") -> str | None: """Prompt user for requirements file path with validation. Args: @@ -752,8 +751,8 @@ def _prompt_for_requirements_file(prompt_text: str, source_path: str, default: s def _handle_requirements_file_display( - requirements_file: Optional[str], non_interactive: bool = False, source_path: Optional[str] = None -) -> Optional[str]: + requirements_file: str | None, non_interactive: bool = False, source_path: str | None = None +) -> str | None: """Handle requirements file with display logic for CLI. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/cli/runtime/commands.py b/src/bedrock_agentcore_starter_toolkit/cli/runtime/commands.py index 52fcaf7a..242fd4d4 100644 --- a/src/bedrock_agentcore_starter_toolkit/cli/runtime/commands.py +++ b/src/bedrock_agentcore_starter_toolkit/cli/runtime/commands.py @@ -10,7 +10,6 @@ import os from pathlib import Path from threading import Thread -from typing import List, Optional import requests import typer @@ -107,27 +106,25 @@ def configure( ctx: typer.Context, *, create: bool = typer.Option(False, "--create", "-c"), - entrypoint: Optional[str] = typer.Option( + entrypoint: str | None = typer.Option( None, "--entrypoint", "-e", help="Entry point: file path (e.g., agent.py) or directory path (auto-detects main.py, agent.py, app.py)", ), - agent_name: Optional[str] = typer.Option(None, "--name", "-n"), - execution_role: Optional[str] = typer.Option(None, "--execution-role", "-er"), - code_build_execution_role: Optional[str] = typer.Option(None, "--code-build-execution-role", "-cber"), - ecr_repository: Optional[str] = typer.Option(None, "--ecr", "-ecr"), - s3_bucket: Optional[str] = typer.Option(None, "--s3", "-s3", help="S3 bucket for direct_code_deploy deployment"), - container_runtime: Optional[str] = typer.Option(None, "--container-runtime", "-ctr"), - requirements_file: Optional[str] = typer.Option( - None, "--requirements-file", "-rf", help="Path to requirements file" - ), + agent_name: str | None = typer.Option(None, "--name", "-n"), + execution_role: str | None = typer.Option(None, "--execution-role", "-er"), + code_build_execution_role: str | None = typer.Option(None, "--code-build-execution-role", "-cber"), + ecr_repository: str | None = typer.Option(None, "--ecr", "-ecr"), + s3_bucket: str | None = typer.Option(None, "--s3", "-s3", help="S3 bucket for direct_code_deploy deployment"), + container_runtime: str | None = typer.Option(None, "--container-runtime", "-ctr"), + requirements_file: str | None = typer.Option(None, "--requirements-file", "-rf", help="Path to requirements file"), disable_otel: bool = typer.Option(False, "--disable-otel", "-do", help="Disable OpenTelemetry"), disable_memory: bool = typer.Option(False, "--disable-memory", "-dm", help="Disable memory"), - authorizer_config: Optional[str] = typer.Option( + authorizer_config: str | None = typer.Option( None, "--authorizer-config", "-ac", help="OAuth authorizer configuration as JSON string" ), - request_header_allowlist: Optional[str] = typer.Option( + request_header_allowlist: str | None = typer.Option( None, "--request-header-allowlist", "-rha", @@ -137,24 +134,24 @@ def configure( vpc: bool = typer.Option( False, "--vpc", help="Enable VPC networking mode (requires --subnets and --security-groups)" ), - subnets: Optional[str] = typer.Option( + subnets: str | None = typer.Option( None, "--subnets", help="Comma-separated list of subnet IDs (e.g., subnet-abc123,subnet-def456). Required with --vpc.", ), - security_groups: Optional[str] = typer.Option( + security_groups: str | None = typer.Option( None, "--security-groups", help="Comma-separated list of security group IDs (e.g., sg-xyz789). Required with --vpc.", ), - idle_timeout: Optional[int] = typer.Option( + idle_timeout: int | None = typer.Option( None, "--idle-timeout", help="Idle runtime session timeout in seconds (60-28800, default: 900)", min=60, max=28800, ), - max_lifetime: Optional[int] = typer.Option( + max_lifetime: int | None = typer.Option( None, "--max-lifetime", help="Maximum instance lifetime in seconds (60-28800, default: 28800)", @@ -162,18 +159,18 @@ def configure( max=28800, ), verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose output"), - region: Optional[str] = typer.Option(None, "--region", "-r"), - protocol: Optional[str] = typer.Option(None, "--protocol", "-p", help="Server protocol (HTTP or MCP or A2A)"), + region: str | None = typer.Option(None, "--region", "-r"), + protocol: str | None = typer.Option(None, "--protocol", "-p", help="Server protocol (HTTP or MCP or A2A)"), non_interactive: bool = typer.Option( False, "--non-interactive", "-ni", help="Skip prompts; use defaults unless overridden" ), - deployment_type: Optional[str] = typer.Option( + deployment_type: str | None = typer.Option( None, "--deployment-type", "-dt", help="Deployment type (container or direct_code_deploy)" ), - runtime: Optional[str] = typer.Option( + runtime: str | None = typer.Option( None, "--runtime", "-rt", help="Python runtime version for direct_code_deploy (e.g., PYTHON_3_10, PYTHON_3_11)" ), - language: Optional[str] = typer.Option( + language: str | None = typer.Option( None, "--language", "-lang", help="Project language (python or typescript). Auto-detected if not specified." ), ): @@ -217,7 +214,7 @@ def configure( @requires_aws_creds def deploy( - agent: Optional[str] = typer.Option( + agent: str | None = typer.Option( None, "--agent", "-a", help="Agent name (use 'agentcore configure list' to see available agents)" ), local: bool = typer.Option(False, "--local", "-l", help="Run locally for development and testing"), @@ -227,7 +224,7 @@ def deploy( "-lb", help="Build locally and deploy to cloud (container deployment only)", ), - image_tag: Optional[str] = typer.Option( + image_tag: str | None = typer.Option( None, "--image-tag", "-t", @@ -246,7 +243,7 @@ def deploy( "-frd", help="Force rebuild of dependencies even if cached (direct_code_deploy deployments only)", ), - envs: List[str] = typer.Option( # noqa: B008 + envs: list[str] = typer.Option( # noqa: B008 None, "--env", "-env", help="Environment variables for agent (format: KEY=VALUE)" ), code_build: bool = typer.Option( @@ -713,18 +710,18 @@ def _parse_custom_headers(headers_str: str) -> dict: def invoke( payload: str = typer.Argument(..., help="JSON payload to send"), - agent: Optional[str] = typer.Option( + agent: str | None = typer.Option( None, "--agent", "-a", help="Agent name (use 'bedrock_agentcore configure list' to see available)" ), - session_id: Optional[str] = typer.Option(None, "--session-id", "-s"), - bearer_token: Optional[str] = typer.Option( + session_id: str | None = typer.Option(None, "--session-id", "-s"), + bearer_token: str | None = typer.Option( None, "--bearer-token", "-bt", help="Bearer token for OAuth authentication" ), - local_mode: Optional[bool] = typer.Option(False, "--local", "-l", help="Send request to a running local container"), - dev_mode: Optional[bool] = typer.Option(False, "--dev", "-d", help="Send request to local development server"), - port: Optional[int] = typer.Option(8080, "--port", help="Port for local development server"), - user_id: Optional[str] = typer.Option(None, "--user-id", "-u", help="User id for authorization flows"), - headers: Optional[str] = typer.Option( + local_mode: bool | None = typer.Option(False, "--local", "-l", help="Send request to a running local container"), + dev_mode: bool | None = typer.Option(False, "--dev", "-d", help="Send request to local development server"), + port: int | None = typer.Option(8080, "--port", help="Port for local development server"), + user_id: str | None = typer.Option(None, "--user-id", "-u", help="User id for authorization flows"), + headers: str | None = typer.Option( None, "--headers", help="Custom headers (format: 'Header1:value,Header2:value2'). " @@ -875,10 +872,10 @@ def invoke( def status( - agent: Optional[str] = typer.Option( + agent: str | None = typer.Option( None, "--agent", "-a", help="Agent name (use 'bedrock_agentcore configure list' to see available)" ), - verbose: Optional[bool] = typer.Option( + verbose: bool | None = typer.Option( None, "--verbose", "-v", help="Verbose json output of config, agent and endpoint status" ), ): @@ -1096,13 +1093,13 @@ def status( def stop_session( - session_id: Optional[str] = typer.Option( + session_id: str | None = typer.Option( None, "--session-id", "-s", help="Runtime session ID to stop. If not provided, stops the last active session from invoke.", ), - agent: Optional[str] = typer.Option( + agent: str | None = typer.Option( None, "--agent", "-a", @@ -1199,7 +1196,7 @@ def stop_session( def destroy( - agent: Optional[str] = typer.Option( + agent: str | None = typer.Option( None, "--agent", "-a", help="Agent name (use 'agentcore configure list' to see available agents)" ), dry_run: bool = typer.Option( diff --git a/src/bedrock_agentcore_starter_toolkit/cli/runtime/configuration_manager.py b/src/bedrock_agentcore_starter_toolkit/cli/runtime/configuration_manager.py index 45d247e7..3a7c98bc 100644 --- a/src/bedrock_agentcore_starter_toolkit/cli/runtime/configuration_manager.py +++ b/src/bedrock_agentcore_starter_toolkit/cli/runtime/configuration_manager.py @@ -3,7 +3,6 @@ import json import os from pathlib import Path -from typing import Dict, Optional, Tuple from ..common import _handle_error, _print_success, _prompt_with_default, console @@ -11,7 +10,7 @@ class ConfigurationManager: """Manages interactive configuration prompts with existing configuration defaults.""" - def __init__(self, config_path: Path, non_interactive: bool = False, region: Optional[str] = None): + def __init__(self, config_path: Path, non_interactive: bool = False, region: str | None = None): """Initialize the ConfigPrompt with a configuration path. Args: @@ -49,7 +48,7 @@ def prompt_agent_name(self, suggested_name: str) -> str: _print_success(f"Using agent name: [cyan]{agent_name}[/cyan]") return agent_name - def prompt_execution_role(self) -> Optional[str]: + def prompt_execution_role(self) -> str | None: """Prompt for execution role. Returns role name/ARN or None for auto-creation.""" if self.non_interactive: _print_success("Will auto-create execution role") @@ -69,7 +68,7 @@ def prompt_execution_role(self) -> Optional[str]: _print_success("Will auto-create execution role") return None - def prompt_ecr_repository(self) -> tuple[Optional[str], bool]: + def prompt_ecr_repository(self) -> tuple[str | None, bool]: """Prompt for ECR repository. Returns (repository, auto_create_flag).""" if self.non_interactive: _print_success("Will auto-create ECR repository") @@ -89,7 +88,7 @@ def prompt_ecr_repository(self) -> tuple[Optional[str], bool]: _print_success("Will auto-create ECR repository") return None, True - def prompt_s3_bucket(self) -> tuple[Optional[str], bool]: + def prompt_s3_bucket(self) -> tuple[str | None, bool]: """Prompt for S3 bucket. Returns (bucket_uri, auto_create_flag).""" if self.non_interactive: _print_success("Will auto-create S3 bucket") @@ -144,7 +143,7 @@ def _validate_s3_bucket(self, s3_input: str) -> bool: except Exception: return False - def prompt_oauth_config(self) -> Optional[dict]: + def prompt_oauth_config(self) -> dict | None: """Prompt for OAuth configuration. Returns OAuth config dict or None.""" if self.non_interactive: _print_success("Using default IAM authorization") @@ -205,7 +204,7 @@ def _configure_oauth(self) -> dict: json.loads(custom_claim.strip()) for custom_claim in custom_claims_input.split(", ") if custom_claim.strip() ] - config: Dict = { + config: dict = { "customJWTAuthorizer": { "discoveryUrl": discovery_url, } @@ -226,7 +225,7 @@ def _configure_oauth(self) -> dict: _print_success("OAuth authorizer configuration created") return config - def prompt_request_header_allowlist(self) -> Optional[dict]: + def prompt_request_header_allowlist(self) -> dict | None: """Prompt for request header allowlist configuration. Returns allowlist config dict or None.""" if self.non_interactive: _print_success("Using default request header configuration") @@ -313,7 +312,7 @@ def prompt_memory_type(self) -> tuple[bool, bool]: return enable_memory, enable_ltm - def prompt_memory_selection(self) -> Tuple[str, str]: + def prompt_memory_selection(self) -> tuple[str, str]: """Prompt user to select existing memory or create new (no skip option). Returns: @@ -406,7 +405,7 @@ def prompt_memory_selection(self) -> Tuple[str, str]: # Fall back to creating new memory return self._prompt_new_memory_config() - def _prompt_new_memory_config(self) -> Tuple[str, str]: + def _prompt_new_memory_config(self) -> tuple[str, str]: """Prompt for new memory configuration - LTM yes/no only.""" console.print("[green]✓ Short-term memory will be enabled (default)[/green]") console.print(" • Stores conversations within sessions") diff --git a/src/bedrock_agentcore_starter_toolkit/cli/runtime/dev_command.py b/src/bedrock_agentcore_starter_toolkit/cli/runtime/dev_command.py index a8c906ad..3f9e260c 100644 --- a/src/bedrock_agentcore_starter_toolkit/cli/runtime/dev_command.py +++ b/src/bedrock_agentcore_starter_toolkit/cli/runtime/dev_command.py @@ -5,7 +5,6 @@ import socket import subprocess # nosec B404 - subprocess required for running uvicorn dev server from pathlib import Path -from typing import Dict, List, Optional, Tuple import typer @@ -23,8 +22,8 @@ def dev( - port: Optional[int] = typer.Option(None, "--port", "-p", help="Port for development server (default: 8080)"), - envs: List[str] = typer.Option( # noqa: B008 + port: int | None = typer.Option(None, "--port", "-p", help="Port for development server (default: 8080)"), + envs: list[str] = typer.Option( # noqa: B008 None, "--env", "-env", help="Environment variables for agent (format: KEY=VALUE)" ), ): @@ -126,7 +125,7 @@ def _has_dev_script(project_dir: Path) -> bool: return False -def _build_typescript_command(config_path: Path, port: str) -> List[str]: +def _build_typescript_command(config_path: Path, port: str) -> list[str]: """Build command for TypeScript dev server.""" project_dir = Path.cwd() if _has_dev_script(project_dir): @@ -168,7 +167,7 @@ def _get_module_path_and_agent_name(config_path: Path) -> tuple[str, str]: return DEFAULT_MODULE_PATH, "default" -def _get_env_vars(config_path: Path) -> Dict[str, str]: +def _get_env_vars(config_path: Path) -> dict[str, str]: env_vars = dict() if not config_path.exists(): return env_vars @@ -186,7 +185,7 @@ def _get_env_vars(config_path: Path) -> Dict[str, str]: return env_vars -def _ensure_config(config_path: Path) -> Tuple[bool, bool]: +def _ensure_config(config_path: Path) -> tuple[bool, bool]: """Ensure that project configuration and entrypoint file are defined.""" has_config = config_path.exists() has_default_entrypoint = Path("src/main.py").exists() @@ -220,7 +219,7 @@ def _get_module_path_from_config(config_path: Path, agent_config) -> str: return f"{entrypoint_path.stem}:app" -def _setup_dev_environment(envs: List[str], port: Optional[int], config_path: Path) -> tuple[dict, bool, int]: +def _setup_dev_environment(envs: list[str], port: int | None, config_path: Path) -> tuple[dict, bool, int]: """Parse environment variables and setup development environment with port handling. Environment variable precedence (lowest to highest): diff --git a/src/bedrock_agentcore_starter_toolkit/create/configure/resolve.py b/src/bedrock_agentcore_starter_toolkit/create/configure/resolve.py index 10271bb6..28ec2de5 100644 --- a/src/bedrock_agentcore_starter_toolkit/create/configure/resolve.py +++ b/src/bedrock_agentcore_starter_toolkit/create/configure/resolve.py @@ -1,7 +1,5 @@ """Implementation for create command to be compatible with the outputs from the configure command.""" -from typing import Optional - from ...cli.common import _handle_error, _handle_warn from ...utils.runtime.schema import ( AWSConfig, @@ -46,7 +44,7 @@ def resolve_agent_config_with_project_context(ctx: ProjectContext, agent_config: ctx.memory_name = memory_config.memory_name # custom authorizer - authorizer_config: Optional[dict[str, any]] = agent_config.authorizer_configuration + authorizer_config: dict[str, any] | None = agent_config.authorizer_configuration if authorizer_config: ctx.custom_authorizer_enabled = True authorizer_config_values = authorizer_config["customJWTAuthorizer"] diff --git a/src/bedrock_agentcore_starter_toolkit/create/features/__init__.py b/src/bedrock_agentcore_starter_toolkit/create/features/__init__.py index 0f1dc8bf..5e0ca9dd 100644 --- a/src/bedrock_agentcore_starter_toolkit/create/features/__init__.py +++ b/src/bedrock_agentcore_starter_toolkit/create/features/__init__.py @@ -1,7 +1,5 @@ """Implements code generation for supported SDK and IaC providers.""" -from typing import Type - from ..constants import IACProvider, SDKProvider from ..types import CreateIACProvider, CreateSDKProvider from .autogen.feature import AutogenFeature @@ -14,7 +12,7 @@ from .strands.feature import StrandsFeature from .terraform.feature import TerraformFeature -sdk_feature_registry: dict[CreateSDKProvider, Type[Feature]] = { +sdk_feature_registry: dict[CreateSDKProvider, type[Feature]] = { SDKProvider.STRANDS: StrandsFeature, SDKProvider.LANG_CHAIN_LANG_GRAPH: LangChainLangGraphFeature, SDKProvider.GOOGLE_ADK: GoogleADKFeature, @@ -23,7 +21,7 @@ SDKProvider.AUTOGEN: AutogenFeature, } -iac_feature_registry: dict[CreateIACProvider, Type[Feature]] = { +iac_feature_registry: dict[CreateIACProvider, type[Feature]] = { IACProvider.CDK: CDKFeature, IACProvider.TERRAFORM: TerraformFeature, } diff --git a/src/bedrock_agentcore_starter_toolkit/create/features/base_feature.py b/src/bedrock_agentcore_starter_toolkit/create/features/base_feature.py index d5ea9cd4..51e88047 100644 --- a/src/bedrock_agentcore_starter_toolkit/create/features/base_feature.py +++ b/src/bedrock_agentcore_starter_toolkit/create/features/base_feature.py @@ -4,7 +4,6 @@ from abc import ABC, abstractmethod from pathlib import Path -from typing import Optional from jinja2 import Environment, FileSystemLoader @@ -15,18 +14,18 @@ class Feature(ABC): """Base feature class for applying Jinja2-based templates to a target directory.""" - feature_dir_name: Optional[str] + feature_dir_name: str | None python_dependencies: list[str] = [] - template_override_dir: Optional[Path] = None + template_override_dir: Path | None = None render_common_dir: bool = False def __init__(self) -> None: """Initialize the base feature.""" if not (self.template_override_dir or self.feature_dir_name): raise Exception("Without template_override_parent_dir, feature_dir_name must be defined") - self.template_dir: Optional[Path] = None - self.base_path: Optional[Path] = None - self.model_provider_name: Optional[str] = None + self.template_dir: Path | None = None + self.base_path: Path | None = None + self.model_provider_name: str | None = None def _resolve_template_dir(self, context: ProjectContext) -> Path: """Determine which template directory to use.""" diff --git a/src/bedrock_agentcore_starter_toolkit/create/types.py b/src/bedrock_agentcore_starter_toolkit/create/types.py index bdd1e9a8..aa49366c 100644 --- a/src/bedrock_agentcore_starter_toolkit/create/types.py +++ b/src/bedrock_agentcore_starter_toolkit/create/types.py @@ -2,7 +2,7 @@ from dataclasses import asdict, dataclass from pathlib import Path -from typing import List, Literal, Optional, get_args +from typing import Literal, get_args CreateSDKProvider = Literal["Strands", "LangChain_LangGraph", "GoogleADK", "OpenAIAgents", "AutoGen", "CrewAI"] SupportedSDKProviders = list(get_args(CreateSDKProvider)) @@ -34,36 +34,36 @@ class ProjectContext: output_dir: Path src_dir: Path entrypoint_path: Path - sdk_provider: Optional[CreateSDKProvider] - iac_provider: Optional[CreateIACProvider] + sdk_provider: CreateSDKProvider | None + iac_provider: CreateIACProvider | None model_provider: CreateModelProvider template_dir_selection: CreateTemplateDirSelection runtime_protocol: CreateRuntimeProtocol deployment_type: CreateDeploymentType - python_dependencies: List[str] - iac_dir: Optional[Path] = None + python_dependencies: list[str] + iac_dir: Path | None = None # below properties are related to consuming the yaml from configure - agent_name: Optional[str] = None + agent_name: str | None = None # memory memory_enabled: bool = False - memory_name: Optional[str] = None - memory_event_expiry_days: Optional[int] = None - memory_is_long_term: Optional[bool] = None + memory_name: str | None = None + memory_event_expiry_days: int | None = None + memory_is_long_term: bool | None = None # custom jwt custom_authorizer_enabled: bool = False - custom_authorizer_url: Optional[str] = None - custom_authorizer_allowed_clients: Optional[list[str]] = None - custom_authorizer_allowed_audience: Optional[list[str]] = None + custom_authorizer_url: str | None = None + custom_authorizer_allowed_clients: list[str] | None = None + custom_authorizer_allowed_audience: list[str] | None = None # vpc vpc_enabled: bool = False - vpc_subnets: Optional[list[str]] = None - vpc_security_groups: Optional[list[str]] = None + vpc_subnets: list[str] | None = None + vpc_security_groups: list[str] | None = None # request headers - request_header_allowlist: Optional[list[str]] = None + request_header_allowlist: list[str] | None = None # observability (use opentelemetry-instrument at Docker entry CMD) observability_enabled: bool = True # api key authentication - api_key_env_var_name: Optional[str] = False + api_key_env_var_name: str | None = False def dict(self): """Return dataclass as dictionary.""" diff --git a/src/bedrock_agentcore_starter_toolkit/notebook/evaluation/client.py b/src/bedrock_agentcore_starter_toolkit/notebook/evaluation/client.py index 1473d832..a74a87c8 100644 --- a/src/bedrock_agentcore_starter_toolkit/notebook/evaluation/client.py +++ b/src/bedrock_agentcore_starter_toolkit/notebook/evaluation/client.py @@ -1,7 +1,6 @@ """User-friendly Evaluation client for Python scripts and notebooks.""" from pathlib import Path -from typing import Dict, List, Optional from rich.console import Console @@ -42,8 +41,8 @@ class Evaluation: def __init__( self, - region: Optional[str] = None, - endpoint_url: Optional[str] = None, + region: str | None = None, + endpoint_url: str | None = None, ): """Initialize Evaluation client. @@ -83,8 +82,8 @@ def __init__( @classmethod def from_config( - cls, config_path: Optional[Path] = None, agent_name: Optional[str] = None - ) -> tuple["Evaluation", str, Optional[str]]: + cls, config_path: Path | None = None, agent_name: str | None = None + ) -> tuple["Evaluation", str, str | None]: """Create Evaluation client from config file. Args: @@ -120,7 +119,7 @@ def from_config( agent_config.bedrock_agentcore.agent_session_id, ) - def get_latest_session(self, agent_id: str) -> Optional[str]: + def get_latest_session(self, agent_id: str) -> str | None: """Get the latest session ID for the specified agent. Args: @@ -156,10 +155,10 @@ def get_latest_session(self, agent_id: str) -> Optional[str]: def run( self, agent_id: str, - session_id: Optional[str] = None, - evaluators: Optional[List[str]] = None, - trace_id: Optional[str] = None, - output: Optional[str] = None, + session_id: str | None = None, + evaluators: list[str] | None = None, + trace_id: str | None = None, + output: str | None = None, ) -> EvaluationResults: """Run evaluation on a session (mirrors: agentcore eval run). @@ -251,7 +250,7 @@ def run( # Evaluator Management Methods # =========================== - def list_evaluators(self, max_results: int = 50) -> Dict: + def list_evaluators(self, max_results: int = 50) -> dict: """List all evaluators (mirrors: agentcore eval evaluator list). Args: @@ -272,7 +271,7 @@ def list_evaluators(self, max_results: int = 50) -> Dict: display_evaluator_list(evaluators, self.console) return response - def get_evaluator(self, evaluator_id: str, output: Optional[str] = None) -> Dict: + def get_evaluator(self, evaluator_id: str, output: str | None = None) -> dict: """Get detailed information about an evaluator (mirrors: agentcore eval evaluator get). Args: @@ -302,8 +301,8 @@ def duplicate_evaluator( self, source_evaluator_id: str, new_name: str, - description: Optional[str] = None, - ) -> Dict: + description: str | None = None, + ) -> dict: """Duplicate a custom evaluator (mirrors: agentcore eval evaluator create interactive). Args: @@ -341,10 +340,10 @@ def duplicate_evaluator( def create_evaluator( self, name: str, - config: Dict, + config: dict, level: str = "TRACE", - description: Optional[str] = None, - ) -> Dict: + description: str | None = None, + ) -> dict: """Create a custom evaluator (mirrors: agentcore eval evaluator create). Args: @@ -393,9 +392,9 @@ def create_evaluator( def update_evaluator( self, evaluator_id: str, - description: Optional[str] = None, - config: Optional[Dict] = None, - ) -> Dict: + description: str | None = None, + config: dict | None = None, + ) -> dict: """Update a custom evaluator (mirrors: agentcore eval evaluator update). Args: @@ -444,15 +443,15 @@ def delete_evaluator(self, evaluator_id: str) -> None: def create_online_config( self, config_name: str, - agent_id: Optional[str] = None, + agent_id: str | None = None, agent_endpoint: str = "DEFAULT", - config_description: Optional[str] = None, + config_description: str | None = None, sampling_rate: float = 1.0, - evaluator_list: Optional[List[str]] = None, - execution_role: Optional[str] = None, + evaluator_list: list[str] | None = None, + execution_role: str | None = None, auto_create_execution_role: bool = True, enable_on_create: bool = True, - ) -> Dict: + ) -> dict: """Create online evaluation configuration (mirrors: agentcore eval online create). Enables continuous automatic evaluation of agent interactions by monitoring @@ -508,7 +507,7 @@ def create_online_config( return response - def get_online_config(self, config_id: str) -> Dict: + def get_online_config(self, config_id: str) -> dict: """Get online evaluation configuration details (mirrors: agentcore eval online get). Args: @@ -529,7 +528,7 @@ def get_online_config(self, config_id: str) -> Dict: return response - def list_online_configs(self, agent_id: Optional[str] = None, max_results: int = 50) -> Dict: + def list_online_configs(self, agent_id: str | None = None, max_results: int = 50) -> dict: """List online evaluation configurations (mirrors: agentcore eval online list). Args: @@ -562,11 +561,11 @@ def list_online_configs(self, agent_id: Optional[str] = None, max_results: int = def update_online_config( self, config_id: str, - status: Optional[str] = None, - sampling_rate: Optional[float] = None, - evaluator_list: Optional[List[str]] = None, - description: Optional[str] = None, - ) -> Dict: + status: str | None = None, + sampling_rate: float | None = None, + evaluator_list: list[str] | None = None, + description: str | None = None, + ) -> dict: """Update online evaluation configuration (mirrors: agentcore eval online update). Args: diff --git a/src/bedrock_agentcore_starter_toolkit/notebook/memory/memory.py b/src/bedrock_agentcore_starter_toolkit/notebook/memory/memory.py index dfc65212..b06795a3 100644 --- a/src/bedrock_agentcore_starter_toolkit/notebook/memory/memory.py +++ b/src/bedrock_agentcore_starter_toolkit/notebook/memory/memory.py @@ -1,6 +1,6 @@ """Notebook interface for memory - thin wrappers over CLI operations.""" -from typing import Any, Dict, List, Optional +from typing import Any from rich.console import Console from rich.tree import Tree @@ -10,9 +10,9 @@ def _resolve_memory_config( - agent_name: Optional[str] = None, - memory_id: Optional[str] = None, - region: Optional[str] = None, + agent_name: str | None = None, + memory_id: str | None = None, + region: str | None = None, ) -> tuple: """Resolve memory_id and region from args or config.""" import boto3 @@ -57,15 +57,15 @@ class Memory: def __init__( self, - memory_id: Optional[str] = None, - agent_name: Optional[str] = None, - region: Optional[str] = None, + memory_id: str | None = None, + agent_name: str | None = None, + region: str | None = None, ): """Initialize Memory interface.""" self.memory_id, self.region, self.manager, self.console = _resolve_memory_config(agent_name, memory_id, region) self.visualizer = MemoryVisualizer(self.console) - def show(self, verbose: bool = False) -> Dict[str, Any]: + def show(self, verbose: bool = False) -> dict[str, Any]: """Show memory details (equivalent to `agentcore memory show`).""" memory = self.manager.get_memory(self.memory_id) self.visualizer.visualize_memory(memory, verbose=verbose) @@ -74,14 +74,14 @@ def show(self, verbose: bool = False) -> Dict[str, Any]: def show_events( self, all: bool = False, - actor_id: Optional[str] = None, - session_id: Optional[str] = None, + actor_id: str | None = None, + session_id: str | None = None, last: int = 1, list_actors: bool = False, list_sessions: bool = False, verbose: bool = False, max_events: int = 10, - ) -> List[Dict[str, Any]]: + ) -> list[dict[str, Any]]: """Show memory events (equivalent to `agentcore memory show events`).""" from ...cli.memory.commands import _collect_all_events @@ -138,12 +138,12 @@ def show_events( def show_records( self, all: bool = False, - namespace: Optional[str] = None, - query: Optional[str] = None, + namespace: str | None = None, + query: str | None = None, last: int = 1, verbose: bool = False, max_results: int = 10, - ) -> List[Dict[str, Any]]: + ) -> list[dict[str, Any]]: """Show memory records (equivalent to `agentcore memory show records`).""" from ...cli.memory.commands import _collect_all_records diff --git a/src/bedrock_agentcore_starter_toolkit/notebook/observability/observability.py b/src/bedrock_agentcore_starter_toolkit/notebook/observability/observability.py index 358c3ffa..880cd058 100644 --- a/src/bedrock_agentcore_starter_toolkit/notebook/observability/observability.py +++ b/src/bedrock_agentcore_starter_toolkit/notebook/observability/observability.py @@ -1,7 +1,6 @@ """Notebook interface for observability - thin wrappers over operations.""" import logging -from typing import Optional from rich.console import Console @@ -32,9 +31,9 @@ class Observability: def __init__( self, - agent_id: Optional[str] = None, - agent_name: Optional[str] = None, - region: Optional[str] = None, + agent_id: str | None = None, + agent_name: str | None = None, + region: str | None = None, runtime_suffix: str = DEFAULT_RUNTIME_SUFFIX, ): """Initialize observability interface. @@ -67,7 +66,7 @@ def __init__( def list( self, - session_id: Optional[str] = None, + session_id: str | None = None, days: int = DEFAULT_LOOKBACK_DAYS, errors: bool = False, ) -> TraceData: @@ -135,14 +134,14 @@ def list( def show( self, - trace_id: Optional[str] = None, - session_id: Optional[str] = None, + trace_id: str | None = None, + session_id: str | None = None, days: int = DEFAULT_LOOKBACK_DAYS, all: bool = False, last: int = 1, errors: bool = False, verbose: bool = False, - output: Optional[str] = None, + output: str | None = None, ) -> TraceData: """Show traces (equivalent to `agentcore obs show`). diff --git a/src/bedrock_agentcore_starter_toolkit/notebook/runtime/bedrock_agentcore.py b/src/bedrock_agentcore_starter_toolkit/notebook/runtime/bedrock_agentcore.py index e2f799e0..7c1d296a 100644 --- a/src/bedrock_agentcore_starter_toolkit/notebook/runtime/bedrock_agentcore.py +++ b/src/bedrock_agentcore_starter_toolkit/notebook/runtime/bedrock_agentcore.py @@ -2,7 +2,7 @@ import logging from pathlib import Path -from typing import Any, Dict, List, Literal, Optional +from typing import Any, Literal from ...operations.runtime import ( configure_bedrock_agentcore, @@ -31,37 +31,37 @@ class Runtime: def __init__(self): """Initialize Bedrock AgentCore notebook interface.""" - self._config_path: Optional[Path] = None + self._config_path: Path | None = None self.name = None def configure( self, entrypoint: str, - execution_role: Optional[str] = None, - code_build_execution_role: Optional[str] = None, - agent_name: Optional[str] = None, - requirements: Optional[List[str]] = None, - requirements_file: Optional[str] = None, - ecr_repository: Optional[str] = None, - container_runtime: Optional[str] = None, + execution_role: str | None = None, + code_build_execution_role: str | None = None, + agent_name: str | None = None, + requirements: list[str] | None = None, + requirements_file: str | None = None, + ecr_repository: str | None = None, + container_runtime: str | None = None, auto_create_ecr: bool = True, auto_create_execution_role: bool = False, - s3_path: Optional[str] = None, + s3_path: str | None = None, auto_create_s3: bool = False, - authorizer_configuration: Optional[Dict[str, Any]] = None, - request_header_configuration: Optional[Dict[str, Any]] = None, - region: Optional[str] = None, - protocol: Optional[Literal["HTTP", "MCP", "A2A"]] = None, + authorizer_configuration: dict[str, Any] | None = None, + request_header_configuration: dict[str, Any] | None = None, + region: str | None = None, + protocol: Literal["HTTP", "MCP", "A2A"] | None = None, disable_otel: bool = False, memory_mode: Literal["NO_MEMORY", "STM_ONLY", "STM_AND_LTM"] = "NO_MEMORY", non_interactive: bool = True, vpc_enabled: bool = False, - vpc_subnets: Optional[List[str]] = None, - vpc_security_groups: Optional[List[str]] = None, - idle_timeout: Optional[int] = None, - max_lifetime: Optional[int] = None, + vpc_subnets: list[str] | None = None, + vpc_security_groups: list[str] | None = None, + idle_timeout: int | None = None, + max_lifetime: int | None = None, deployment_type: Literal["direct_code_deploy", "container"] = "container", - runtime_type: Optional[str] = None, + runtime_type: str | None = None, ) -> ConfigureResult: """Configure Bedrock AgentCore from notebook using an entrypoint file. @@ -259,7 +259,7 @@ def launch( local: bool = False, local_build: bool = False, auto_update_on_conflict: bool = False, - env_vars: Optional[Dict] = None, + env_vars: dict | None = None, ) -> LaunchResult: """Launch Bedrock AgentCore from notebook. @@ -393,12 +393,12 @@ def launch( def invoke( self, - payload: Dict[str, Any], - session_id: Optional[str] = None, - bearer_token: Optional[str] = None, - local: Optional[bool] = False, - user_id: Optional[str] = None, - ) -> Dict[str, Any]: + payload: dict[str, Any], + session_id: str | None = None, + bearer_token: str | None = None, + local: bool | None = False, + user_id: str | None = None, + ) -> dict[str, Any]: """Invoke deployed Bedrock AgentCore endpoint. Args: @@ -430,7 +430,7 @@ def invoke( ) return result.response - def stop_session(self, session_id: Optional[str] = None) -> Dict[str, Any]: + def stop_session(self, session_id: str | None = None) -> dict[str, Any]: """Stop an active runtime session. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/operations/evaluation/control_plane_client.py b/src/bedrock_agentcore_starter_toolkit/operations/evaluation/control_plane_client.py index 42183c7d..8d173ede 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/evaluation/control_plane_client.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/evaluation/control_plane_client.py @@ -5,7 +5,7 @@ import logging import os -from typing import Any, Dict, List, Optional +from typing import Any import boto3 @@ -38,7 +38,7 @@ class EvaluationControlPlaneClient: NO business logic - that belongs in EvaluationProcessor or formatters. """ - def __init__(self, region_name: str, endpoint_url: Optional[str] = None, boto_client: Optional[Any] = None): + def __init__(self, region_name: str, endpoint_url: str | None = None, boto_client: Any | None = None): """Initialize Control Plane client. Args: @@ -67,7 +67,7 @@ def __init__(self, region_name: str, endpoint_url: Optional[str] = None, boto_cl endpoint_url=self.endpoint_url, ) - def list_evaluators(self, max_results: int = 50) -> Dict[str, Any]: + def list_evaluators(self, max_results: int = 50) -> dict[str, Any]: """List all evaluators (builtin and custom). Returns evaluators with level and description for display. @@ -93,7 +93,7 @@ def list_evaluators(self, max_results: int = 50) -> Dict[str, Any]: """ return self.client.list_evaluators(maxResults=max_results) - def get_evaluator(self, evaluator_id: str) -> Dict[str, Any]: + def get_evaluator(self, evaluator_id: str) -> dict[str, Any]: """Get evaluator details. Args: @@ -105,8 +105,8 @@ def get_evaluator(self, evaluator_id: str) -> Dict[str, Any]: return self.client.get_evaluator(evaluatorId=evaluator_id) def create_evaluator( - self, name: str, config: Dict[str, Any], level: str = "TRACE", description: Optional[str] = None - ) -> Dict[str, Any]: + self, name: str, config: dict[str, Any], level: str = "TRACE", description: str | None = None + ) -> dict[str, Any]: """Create custom evaluator. Args: @@ -125,8 +125,8 @@ def create_evaluator( return self.client.create_evaluator(**params) def update_evaluator( - self, evaluator_id: str, description: Optional[str] = None, config: Optional[Dict[str, Any]] = None - ) -> Dict[str, Any]: + self, evaluator_id: str, description: str | None = None, config: dict[str, Any] | None = None + ) -> dict[str, Any]: """Update custom evaluator. Args: @@ -177,13 +177,13 @@ def create_online_evaluation_config( config_name: str, agent_id: str, agent_endpoint: str = "DEFAULT", - config_description: Optional[str] = None, + config_description: str | None = None, sampling_rate: float = 1.0, - evaluator_list: Optional[List[str]] = None, - execution_role: Optional[str] = None, + evaluator_list: list[str] | None = None, + execution_role: str | None = None, auto_create_execution_role: bool = True, enable_on_create: bool = True, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Create online evaluation configuration. Enables continuous automatic evaluation of agent interactions by monitoring @@ -266,7 +266,7 @@ def create_online_evaluation_config( logger.info("✓ Online evaluation config created: %s", response.get("onlineEvaluationConfigId")) return response - def get_online_evaluation_config(self, config_id: str) -> Dict[str, Any]: + def get_online_evaluation_config(self, config_id: str) -> dict[str, Any]: """Get online evaluation configuration details. Args: @@ -282,7 +282,7 @@ def get_online_evaluation_config(self, config_id: str) -> Dict[str, Any]: """ return self.client.get_online_evaluation_config(onlineEvaluationConfigId=config_id) - def list_online_evaluation_configs(self, agent_id: Optional[str] = None, max_results: int = 50) -> Dict[str, Any]: + def list_online_evaluation_configs(self, agent_id: str | None = None, max_results: int = 50) -> dict[str, Any]: """List online evaluation configurations. Args: @@ -312,11 +312,11 @@ def list_online_evaluation_configs(self, agent_id: Optional[str] = None, max_res def update_online_evaluation_config( self, config_id: str, - status: Optional[str] = None, - sampling_rate: Optional[float] = None, - evaluator_list: Optional[List[str]] = None, - description: Optional[str] = None, - ) -> Dict[str, Any]: + status: str | None = None, + sampling_rate: float | None = None, + evaluator_list: list[str] | None = None, + description: str | None = None, + ) -> dict[str, Any]: """Update online evaluation configuration. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/operations/evaluation/create_role.py b/src/bedrock_agentcore_starter_toolkit/operations/evaluation/create_role.py index b58f9719..1f1dba60 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/evaluation/create_role.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/evaluation/create_role.py @@ -4,7 +4,6 @@ import json import logging import time -from typing import Optional from boto3 import Session from botocore.client import BaseClient @@ -36,7 +35,7 @@ def get_or_create_evaluation_execution_role( region: str, account_id: str, config_name: str, - role_name: Optional[str] = None, + role_name: str | None = None, ) -> str: """Get existing evaluation execution role or create a new one (idempotent). diff --git a/src/bedrock_agentcore_starter_toolkit/operations/evaluation/data_plane_client.py b/src/bedrock_agentcore_starter_toolkit/operations/evaluation/data_plane_client.py index 9fa5f287..3e101ec1 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/evaluation/data_plane_client.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/evaluation/data_plane_client.py @@ -5,7 +5,7 @@ import logging import os -from typing import Any, Dict, List, Optional +from typing import Any import boto3 from botocore.config import Config @@ -26,7 +26,7 @@ class EvaluationDataPlaneClient: NO business logic - that belongs in EvaluationProcessor. """ - def __init__(self, region_name: str, endpoint_url: Optional[str] = None, boto_client: Optional[Any] = None): + def __init__(self, region_name: str, endpoint_url: str | None = None, boto_client: Any | None = None): """Initialize evaluation data plane client. Args: @@ -55,8 +55,8 @@ def __init__(self, region_name: str, endpoint_url: Optional[str] = None, boto_cl ) def evaluate( - self, evaluator_id: str, session_spans: List[Dict[str, Any]], evaluation_target: Optional[Dict[str, Any]] = None - ) -> Dict[str, Any]: + self, evaluator_id: str, session_spans: list[dict[str, Any]], evaluation_target: dict[str, Any] | None = None + ) -> dict[str, Any]: """Call evaluation API with transformed spans. Note: API accepts ONE evaluator per call via URI path. diff --git a/src/bedrock_agentcore_starter_toolkit/operations/evaluation/evaluator_processor.py b/src/bedrock_agentcore_starter_toolkit/operations/evaluation/evaluator_processor.py index 60e9fc80..b8ee20f5 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/evaluation/evaluator_processor.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/evaluation/evaluator_processor.py @@ -4,7 +4,7 @@ separated from UI/display concerns. """ -from typing import Any, Dict, List, Optional, Tuple +from typing import Any from .control_plane_client import EvaluationControlPlaneClient @@ -13,7 +13,7 @@ # ============================================================================= -def filter_custom_evaluators(evaluators: List[Dict[str, Any]]) -> List[Dict[str, Any]]: +def filter_custom_evaluators(evaluators: list[dict[str, Any]]) -> list[dict[str, Any]]: """Filter list to only custom evaluators. Args: @@ -37,7 +37,7 @@ def is_builtin_evaluator(evaluator_id: str) -> bool: return evaluator_id.startswith("Builtin.") -def validate_evaluator_config(config_data: Dict[str, Any]) -> None: +def validate_evaluator_config(config_data: dict[str, Any]) -> None: """Validate evaluator configuration structure. Args: @@ -57,7 +57,7 @@ def validate_evaluator_config(config_data: Dict[str, Any]) -> None: def get_evaluator_for_duplication( client: EvaluationControlPlaneClient, evaluator_id: str -) -> Tuple[Dict[str, Any], str, str]: +) -> tuple[dict[str, Any], str, str]: """Get evaluator details and prepare for duplication. Args: @@ -96,10 +96,10 @@ def get_evaluator_for_duplication( def create_evaluator( client: EvaluationControlPlaneClient, name: str, - config: Dict[str, Any], + config: dict[str, Any], level: str = "TRACE", - description: Optional[str] = None, -) -> Dict[str, Any]: + description: str | None = None, +) -> dict[str, Any]: """Create a new evaluator. Args: @@ -120,8 +120,8 @@ def create_evaluator( def duplicate_evaluator( - client: EvaluationControlPlaneClient, source_evaluator_id: str, new_name: str, new_description: Optional[str] = None -) -> Dict[str, Any]: + client: EvaluationControlPlaneClient, source_evaluator_id: str, new_name: str, new_description: str | None = None +) -> dict[str, Any]: """Duplicate an existing custom evaluator. Args: @@ -154,9 +154,9 @@ def duplicate_evaluator( def update_evaluator( client: EvaluationControlPlaneClient, evaluator_id: str, - description: Optional[str] = None, - config: Optional[Dict[str, Any]] = None, -) -> Dict[str, Any]: + description: str | None = None, + config: dict[str, Any] | None = None, +) -> dict[str, Any]: """Update an existing evaluator. Args: @@ -185,7 +185,7 @@ def update_evaluator( def update_evaluator_instructions( client: EvaluationControlPlaneClient, evaluator_id: str, new_instructions: str -) -> Dict[str, Any]: +) -> dict[str, Any]: """Update only the instructions of an evaluator. Args: @@ -239,7 +239,7 @@ def delete_evaluator(client: EvaluationControlPlaneClient, evaluator_id: str) -> # ============================================================================= -def list_evaluators(client: EvaluationControlPlaneClient, max_results: int = 50) -> Dict[str, Any]: +def list_evaluators(client: EvaluationControlPlaneClient, max_results: int = 50) -> dict[str, Any]: """List all evaluators. Args: @@ -252,7 +252,7 @@ def list_evaluators(client: EvaluationControlPlaneClient, max_results: int = 50) return client.list_evaluators(max_results=max_results) -def get_evaluator(client: EvaluationControlPlaneClient, evaluator_id: str) -> Dict[str, Any]: +def get_evaluator(client: EvaluationControlPlaneClient, evaluator_id: str) -> dict[str, Any]: """Get evaluator details. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/operations/evaluation/formatters.py b/src/bedrock_agentcore_starter_toolkit/operations/evaluation/formatters.py index ec578087..b7d76aef 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/evaluation/formatters.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/evaluation/formatters.py @@ -6,7 +6,7 @@ import json from pathlib import Path -from typing import Any, Dict, List +from typing import Any from rich.console import Console from rich.panel import Panel @@ -16,7 +16,7 @@ from .models import EvaluationResults -def display_evaluator_list(evaluators: List[Dict[str, Any]], console: Console) -> None: +def display_evaluator_list(evaluators: list[dict[str, Any]], console: Console) -> None: """Display formatted list of evaluators. Args: @@ -71,7 +71,7 @@ def display_evaluator_list(evaluators: List[Dict[str, Any]], console: Console) - console.print(f"\n[dim]Total: {len(evaluators)} ({len(builtin)} builtin, {len(custom)} custom)[/dim]") -def display_evaluator_details(details: Dict[str, Any], console: Console) -> None: +def display_evaluator_details(details: dict[str, Any], console: Console) -> None: """Display detailed evaluator information. Args: @@ -244,7 +244,7 @@ def save_evaluation_results(results: EvaluationResults, output_file: str, consol console.print(f"[green]✓[/green] Input data saved to: {input_path}") -def save_json_output(data: Dict[str, Any], output_file: str, console: Console) -> None: +def save_json_output(data: dict[str, Any], output_file: str, console: Console) -> None: """Save JSON data to file. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/operations/evaluation/models.py b/src/bedrock_agentcore_starter_toolkit/operations/evaluation/models.py index a5a6337c..c0833fbb 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/evaluation/models.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/evaluation/models.py @@ -1,7 +1,7 @@ """Data models for evaluation requests and results.""" from dataclasses import dataclass, field -from typing import Any, Dict, List, Optional +from typing import Any @dataclass @@ -12,10 +12,10 @@ class EvaluationRequest: """ evaluator_id: str - session_spans: List[Dict[str, Any]] - evaluation_target: Optional[Dict[str, Any]] = None + session_spans: list[dict[str, Any]] + evaluation_target: dict[str, Any] | None = None - def to_api_request(self) -> tuple[str, Dict[str, Any]]: + def to_api_request(self) -> tuple[str, dict[str, Any]]: """Convert to API request format. Returns: @@ -37,14 +37,14 @@ class EvaluationResult: evaluator_name: str evaluator_arn: str explanation: str - context: Dict[str, Any] # Contains spanContext union from API - value: Optional[float] = None - label: Optional[str] = None - token_usage: Optional[Dict[str, int]] = None - error: Optional[str] = None + context: dict[str, Any] # Contains spanContext union from API + value: float | None = None + label: str | None = None + token_usage: dict[str, int] | None = None + error: str | None = None @classmethod - def from_api_response(cls, result: Dict[str, Any]) -> "EvaluationResult": + def from_api_response(cls, result: dict[str, Any]) -> "EvaluationResult": """Create from API response. Args: @@ -87,10 +87,10 @@ def has_error(self) -> bool: class EvaluationResults: """Container for multiple evaluation results.""" - session_id: Optional[str] = None - trace_id: Optional[str] = None - results: List[EvaluationResult] = field(default_factory=list) - input_data: Optional[Dict[str, Any]] = None # Store OTel spans sent to API + session_id: str | None = None + trace_id: str | None = None + results: list[EvaluationResult] = field(default_factory=list) + input_data: dict[str, Any] | None = None # Store OTel spans sent to API def add_result(self, result: EvaluationResult) -> None: """Add a result to the collection.""" @@ -100,15 +100,15 @@ def has_errors(self) -> bool: """Check if any evaluation failed.""" return any(r.has_error() for r in self.results) - def get_successful_results(self) -> List[EvaluationResult]: + def get_successful_results(self) -> list[EvaluationResult]: """Get only successful evaluations.""" return [r for r in self.results if not r.has_error()] - def get_failed_results(self) -> List[EvaluationResult]: + def get_failed_results(self) -> list[EvaluationResult]: """Get only failed evaluations.""" return [r for r in self.results if r.has_error()] - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: """Convert to dictionary for serialization.""" result = { "session_id": self.session_id, @@ -152,19 +152,19 @@ class OnlineEvaluationConfig: agent_name: str log_group_name: str sampling_rate: float - evaluator_list: List[str] + evaluator_list: list[str] execution_role: str status: str # ENABLED or DISABLED - config_arn: Optional[str] = None - agent_endpoint: Optional[str] = None - description: Optional[str] = None - created_at: Optional[str] = None - updated_at: Optional[str] = None - cloudwatch_logs_url: Optional[str] = None - dashboard_url: Optional[str] = None + config_arn: str | None = None + agent_endpoint: str | None = None + description: str | None = None + created_at: str | None = None + updated_at: str | None = None + cloudwatch_logs_url: str | None = None + dashboard_url: str | None = None @classmethod - def from_api_response(cls, response: Dict[str, Any]) -> "OnlineEvaluationConfig": + def from_api_response(cls, response: dict[str, Any]) -> "OnlineEvaluationConfig": """Create from API response. Args: @@ -212,7 +212,7 @@ def from_api_response(cls, response: Dict[str, Any]) -> "OnlineEvaluationConfig" dashboard_url=response.get("dashboard_url"), ) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: """Convert to dictionary for serialization.""" return { "config_id": self.config_id, diff --git a/src/bedrock_agentcore_starter_toolkit/operations/evaluation/on_demand_processor.py b/src/bedrock_agentcore_starter_toolkit/operations/evaluation/on_demand_processor.py index 9e579649..1252abec 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/evaluation/on_demand_processor.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/evaluation/on_demand_processor.py @@ -5,7 +5,7 @@ import logging from datetime import datetime, timedelta -from typing import Any, Dict, List, Optional +from typing import Any from botocore.exceptions import ClientError @@ -43,7 +43,7 @@ def __init__(self, data_plane_client, control_plane_client=None): self.data_plane_client = data_plane_client self.control_plane_client = control_plane_client - def get_latest_session(self, agent_id: str, region: str) -> Optional[str]: + def get_latest_session(self, agent_id: str, region: str) -> str | None: """Get the latest session ID for an agent. Args: @@ -148,7 +148,7 @@ def fetch_session_data( except (ClientError, ValueError, KeyError, TypeError) as e: raise RuntimeError(f"Failed to fetch session data: {e}") from e - def extract_raw_spans(self, trace_data: TraceData) -> List[Dict[str, Any]]: + def extract_raw_spans(self, trace_data: TraceData) -> list[dict[str, Any]]: """Extract raw span documents from TraceData. Args: @@ -171,7 +171,7 @@ def extract_raw_spans(self, trace_data: TraceData) -> List[Dict[str, Any]]: return raw_spans - def filter_relevant_spans(self, raw_spans: List[Dict[str, Any]]) -> List[Dict[str, Any]]: + def filter_relevant_spans(self, raw_spans: list[dict[str, Any]]) -> list[dict[str, Any]]: """Filter to only high-signal spans for evaluation. Keeps only: @@ -246,7 +246,7 @@ def filter_traces_up_to(self, trace_data: TraceData, target_trace_id: str) -> Tr def get_most_recent_spans( self, trace_data: TraceData, max_items: int = DEFAULT_MAX_EVALUATION_ITEMS - ) -> List[Dict[str, Any]]: + ) -> list[dict[str, Any]]: """Get most recent relevant spans across all traces in session. Collects spans from known instrumentation scopes and log events with conversation data, @@ -278,7 +278,7 @@ def get_timestamp(span_doc): # Return most recent max_items return relevant_spans[:max_items] - def count_span_types(self, raw_spans: List[Dict[str, Any]]) -> tuple: + def count_span_types(self, raw_spans: list[dict[str, Any]]) -> tuple: """Count spans, logs, and scoped spans. Args: @@ -304,9 +304,9 @@ def determine_spans_for_evaluator( self, evaluator_level: str, trace_data: TraceData, - trace_id: Optional[str] = None, + trace_id: str | None = None, max_items: int = DEFAULT_MAX_EVALUATION_ITEMS, - ) -> tuple[List[Dict[str, Any]], Optional[Dict[str, Any]]]: + ) -> tuple[list[dict[str, Any]], dict[str, Any] | None]: """Determine which spans to send based on evaluator level. Args: @@ -341,11 +341,11 @@ def determine_spans_for_evaluator( def execute_evaluators( self, - evaluators: List[str], - otel_spans: List[Dict[str, Any]], + evaluators: list[str], + otel_spans: list[dict[str, Any]], session_id: str, - evaluation_target: Optional[Dict[str, Any]] = None, - ) -> List[EvaluationResult]: + evaluation_target: dict[str, Any] | None = None, + ) -> list[EvaluationResult]: """Execute evaluators and return results. Calls data plane API once per evaluator. @@ -396,10 +396,10 @@ def execute_evaluators( def evaluate_session( self, session_id: str, - evaluators: List[str], + evaluators: list[str], agent_id: str, region: str, - trace_id: Optional[str] = None, + trace_id: str | None = None, days: int = DEFAULT_LOOKBACK_DAYS, ) -> EvaluationResults: """Evaluate a session using multiple evaluators. @@ -486,7 +486,7 @@ def evaluate_session( return results - def _group_evaluators_by_level(self, evaluators: List[str]) -> Dict[str, List[str]]: + def _group_evaluators_by_level(self, evaluators: list[str]) -> dict[str, list[str]]: """Group evaluators by their level (SESSION or TRACE). Note: TOOL_CALL and other levels are treated as TRACE for evaluation purposes. diff --git a/src/bedrock_agentcore_starter_toolkit/operations/evaluation/online_processor.py b/src/bedrock_agentcore_starter_toolkit/operations/evaluation/online_processor.py index df95bf85..49b6d8ac 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/evaluation/online_processor.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/evaluation/online_processor.py @@ -6,7 +6,7 @@ """ import logging -from typing import Any, Dict, List, Optional +from typing import Any import boto3 from botocore.exceptions import ClientError @@ -21,13 +21,13 @@ def create_online_evaluation_config( config_name: str, agent_id: str, agent_endpoint: str = "DEFAULT", - config_description: Optional[str] = None, + config_description: str | None = None, sampling_rate: float = 1.0, - evaluator_list: Optional[List[str]] = None, - execution_role: Optional[str] = None, + evaluator_list: list[str] | None = None, + execution_role: str | None = None, auto_create_execution_role: bool = True, enable_on_create: bool = True, -) -> Dict[str, Any]: +) -> dict[str, Any]: """Create online evaluation configuration with validation. Args: @@ -90,7 +90,7 @@ def create_online_evaluation_config( def get_online_evaluation_config( client: EvaluationControlPlaneClient, config_id: str, -) -> Dict[str, Any]: +) -> dict[str, Any]: """Get online evaluation configuration. Args: @@ -112,9 +112,9 @@ def get_online_evaluation_config( def list_online_evaluation_configs( client: EvaluationControlPlaneClient, - agent_id: Optional[str] = None, + agent_id: str | None = None, max_results: int = 50, -) -> Dict[str, Any]: +) -> dict[str, Any]: """List online evaluation configurations. Args: @@ -144,11 +144,11 @@ def list_online_evaluation_configs( def update_online_evaluation_config( client: EvaluationControlPlaneClient, config_id: str, - status: Optional[str] = None, - sampling_rate: Optional[float] = None, - evaluator_list: Optional[List[str]] = None, - description: Optional[str] = None, -) -> Dict[str, Any]: + status: str | None = None, + sampling_rate: float | None = None, + evaluator_list: list[str] | None = None, + description: str | None = None, +) -> dict[str, Any]: """Update online evaluation configuration with validation. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/operations/gateway/client.py b/src/bedrock_agentcore_starter_toolkit/operations/gateway/client.py index f5e913be..7dc2a8ce 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/gateway/client.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/gateway/client.py @@ -5,7 +5,7 @@ import time import urllib.parse import uuid -from typing import Any, Dict, Optional +from typing import Any import boto3 import urllib3 @@ -25,7 +25,7 @@ class GatewayClient: """High-level client for Bedrock AgentCore Gateway operations.""" - def __init__(self, region_name: Optional[str] = None, endpoint_url: Optional[str] = None): + def __init__(self, region_name: str | None = None, endpoint_url: str | None = None): """Initialize the Gateway client. Args: @@ -267,9 +267,9 @@ def fix_iam_permissions(self, gateway: dict) -> None: def delete_gateway( self, - gateway_identifier: Optional[str] = None, - name: Optional[str] = None, - gateway_arn: Optional[str] = None, + gateway_identifier: str | None = None, + name: str | None = None, + gateway_arn: str | None = None, skip_resource_in_use: bool = False, ) -> dict: """Delete a gateway resource. @@ -280,7 +280,7 @@ def delete_gateway( :param skip_resource_in_use: If True, delete all targets before deleting the gateway (default: False) :return: Result dict with status and details """ - resolved_id: Optional[str] = None + resolved_id: str | None = None # Resolve gateway ID from different input types if gateway_identifier: @@ -342,11 +342,11 @@ def delete_gateway( def delete_gateway_target( self, - gateway_identifier: Optional[str] = None, - name: Optional[str] = None, - gateway_arn: Optional[str] = None, - target_id: Optional[str] = None, - target_name: Optional[str] = None, + gateway_identifier: str | None = None, + name: str | None = None, + gateway_arn: str | None = None, + target_id: str | None = None, + target_name: str | None = None, ) -> dict: """Delete a gateway target. @@ -357,7 +357,7 @@ def delete_gateway_target( :param target_name: Target name to delete (will look up ID) :return: Result dict with status and details """ - resolved_id: Optional[str] = None + resolved_id: str | None = None # Resolve gateway ID if gateway_identifier: @@ -404,7 +404,7 @@ def delete_gateway_target( self.logger.error("Error deleting gateway target: %s", str(e)) return {"status": "error", "message": f"Error deleting gateway target: {str(e)}"} - def _get_gateway_id_by_name(self, name: str) -> Optional[str]: + def _get_gateway_id_by_name(self, name: str) -> str | None: """Get gateway ID by name. :param name: Gateway name to look up @@ -413,7 +413,7 @@ def _get_gateway_id_by_name(self, name: str) -> Optional[str]: try: next_token = None while True: - kwargs: Dict[str, Any] = {"maxResults": 1000} + kwargs: dict[str, Any] = {"maxResults": 1000} if next_token: kwargs["nextToken"] = next_token resp = self.client.list_gateways(**kwargs) @@ -430,7 +430,7 @@ def _get_gateway_id_by_name(self, name: str) -> Optional[str]: def list_gateways( self, - name: Optional[str] = None, + name: str | None = None, max_results: int = 50, ) -> dict: """List all gateways. @@ -443,7 +443,7 @@ def list_gateways( next_token = None items = [] while True: - kwargs: Dict[str, Any] = {"maxResults": min(max_results - len(items), 1000)} + kwargs: dict[str, Any] = {"maxResults": min(max_results - len(items), 1000)} if next_token: kwargs["nextToken"] = next_token resp = self.client.list_gateways(**kwargs) @@ -466,9 +466,9 @@ def list_gateways( def get_gateway( self, - gateway_identifier: Optional[str] = None, - name: Optional[str] = None, - gateway_arn: Optional[str] = None, + gateway_identifier: str | None = None, + name: str | None = None, + gateway_arn: str | None = None, ) -> dict: """Get gateway details. @@ -477,7 +477,7 @@ def get_gateway( :param gateway_arn: Gateway ARN (will extract ID) :return: Result dict with status and gateway details """ - resolved_id: Optional[str] = None + resolved_id: str | None = None # Resolve gateway ID if gateway_identifier: @@ -503,9 +503,9 @@ def get_gateway( def list_gateway_targets( self, - gateway_identifier: Optional[str] = None, - name: Optional[str] = None, - gateway_arn: Optional[str] = None, + gateway_identifier: str | None = None, + name: str | None = None, + gateway_arn: str | None = None, max_results: int = 50, ) -> dict: """List gateway targets. @@ -516,7 +516,7 @@ def list_gateway_targets( :param max_results: Maximum number of results to return (default: 50) :return: Result dict with status and list of targets """ - resolved_id: Optional[str] = None + resolved_id: str | None = None # Resolve gateway ID if gateway_identifier: @@ -536,7 +536,7 @@ def list_gateway_targets( next_token = None items = [] while True: - kwargs: Dict[str, Any] = { + kwargs: dict[str, Any] = { "gatewayIdentifier": resolved_id, "maxResults": min(max_results - len(items), 1000), } @@ -560,11 +560,11 @@ def list_gateway_targets( def get_gateway_target( self, - gateway_identifier: Optional[str] = None, - name: Optional[str] = None, - gateway_arn: Optional[str] = None, - target_id: Optional[str] = None, - target_name: Optional[str] = None, + gateway_identifier: str | None = None, + name: str | None = None, + gateway_arn: str | None = None, + target_id: str | None = None, + target_name: str | None = None, ) -> dict: """Get gateway target details. @@ -575,7 +575,7 @@ def get_gateway_target( :param target_name: Target name (will look up ID) :return: Result dict with status and target details """ - resolved_id: Optional[str] = None + resolved_id: str | None = None # Resolve gateway ID if gateway_identifier: @@ -619,7 +619,7 @@ def get_gateway_target( self.logger.error("Error getting gateway target: %s", str(e)) return {"status": "error", "message": f"Error getting gateway target: {str(e)}"} - def cleanup_gateway(self, gateway_id: str, client_info: Optional[Dict] = None) -> None: + def cleanup_gateway(self, gateway_id: str, client_info: dict | None = None) -> None: """Remove all resources associated with a gateway. :param gateway_id: the ID of the gateway to clean up @@ -696,7 +696,7 @@ def cleanup_gateway(self, gateway_id: str, client_info: Optional[Dict] = None) - self.logger.info("✅ Cleanup complete") - def __handle_lambda_target_creation(self, role_arn: str) -> Dict[str, Any]: + def __handle_lambda_target_creation(self, role_arn: str) -> dict[str, Any]: """Create a test lambda. :return: the targetConfiguration for the Lambda. @@ -708,8 +708,8 @@ def __handle_lambda_target_creation(self, role_arn: str) -> Dict[str, Any]: } def __handle_openapi_target_credential_provider_creation( - self, name: str, credentials: Dict[str, Any] - ) -> Dict[str, Any]: + self, name: str, credentials: dict[str, Any] + ) -> dict[str, Any]: """Generate the credential provider config for open api target. :param name: the name of the target. @@ -794,7 +794,7 @@ def generate_random_id(): """Generate a random ID for Cognito resources.""" return str(uuid.uuid4())[:8] - def create_oauth_authorizer_with_cognito(self, gateway_name: str) -> Dict[str, Any]: + def create_oauth_authorizer_with_cognito(self, gateway_name: str) -> dict[str, Any]: """Creates Cognito OAuth authorization server. Note: This implementation uses AdminCreateUserOnly mode where only administrators @@ -926,8 +926,8 @@ def create_oauth_authorizer_with_cognito(self, gateway_name: str) -> Dict[str, A def update_gateway( self, gateway_identifier: str, - description: Optional[str] = None, - policy_engine_config: Optional[Dict] = None, + description: str | None = None, + policy_engine_config: dict | None = None, ) -> dict: """Update gateway configuration. @@ -1027,7 +1027,7 @@ def update_gateway_policy_engine( }, ) - def get_access_token_for_cognito(self, client_info: Dict[str, Any]) -> str: + def get_access_token_for_cognito(self, client_info: dict[str, Any]) -> str: """Get OAuth token using client credentials flow. :param client_info: credentials and context needed to get the access token @@ -1109,10 +1109,10 @@ def _enable_observability_for_gateway(self, gateway: dict) -> None: def enable_observability( self, gateway_id: str, - gateway_arn: Optional[str] = None, + gateway_arn: str | None = None, enable_logs: bool = True, enable_traces: bool = True, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Enable CloudWatch observability for an existing gateway resource.""" delivery_manager = ObservabilityDeliveryManager( region_name=self.region, @@ -1137,7 +1137,7 @@ def disable_observability( self, gateway_id: str, delete_log_group: bool = False, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Disable CloudWatch observability for a gateway resource.""" delivery_manager = ObservabilityDeliveryManager(region_name=self.region) result = delivery_manager.disable_for_gateway( diff --git a/src/bedrock_agentcore_starter_toolkit/operations/gateway/create_role.py b/src/bedrock_agentcore_starter_toolkit/operations/gateway/create_role.py index af5ea9e8..7fcface0 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/gateway/create_role.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/gateway/create_role.py @@ -2,7 +2,6 @@ import json import logging -from typing import Optional from boto3 import Session from botocore.client import BaseClient @@ -60,9 +59,9 @@ def create_gateway_execution_role( def _attach_policy( iam_client: BaseClient, role_name: str, - policy_arn: Optional[str] = None, - policy_document: Optional[str] = None, - policy_name: Optional[str] = None, + policy_arn: str | None = None, + policy_document: str | None = None, + policy_name: str | None = None, ) -> None: """Attach a policy to an IAM role. diff --git a/src/bedrock_agentcore_starter_toolkit/operations/identity/helpers.py b/src/bedrock_agentcore_starter_toolkit/operations/identity/helpers.py index a59aecb6..83d71495 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/identity/helpers.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/identity/helpers.py @@ -6,7 +6,7 @@ import string import time import uuid -from typing import Any, Dict, List, Optional, Tuple +from typing import Any import boto3 from botocore.exceptions import ClientError @@ -16,9 +16,9 @@ def create_cognito_oauth_pool( base_name: str = "AgentCoreTest", region: str = "us-west-2", create_test_user: bool = True, - agentcore_callback_url: Optional[str] = None, + agentcore_callback_url: str | None = None, use_for_runtime_auth: bool = False, -) -> Dict: +) -> dict: """Create a Cognito user pool configured for OAuth 2.0 flows. Args: @@ -149,7 +149,7 @@ def get_cognito_access_token( username: str, password: str, region: str = "us-west-2", - client_secret: Optional[str] = None, + client_secret: str | None = None, ) -> str: """Retrieve an access token from Cognito using username/password. @@ -192,7 +192,7 @@ def get_cognito_m2m_token( client_id: str, client_secret: str, region: str = "us-west-2", - scopes: Optional[List[str]] = None, + scopes: list[str] | None = None, ) -> str: """Retrieve an access token from Cognito using M2M client credentials flow. @@ -357,7 +357,7 @@ def ensure_identity_permissions(role_arn: str, provider_arns: list, region: str, raise -def setup_aws_jwt_federation(region: str, logger: Optional[logging.Logger] = None) -> Tuple[bool, str]: +def setup_aws_jwt_federation(region: str, logger: logging.Logger | None = None) -> tuple[bool, str]: """Enable AWS IAM Outbound Federation and return the issuer URL. This is idempotent - if already enabled, just returns the issuer URL. @@ -421,7 +421,7 @@ def setup_aws_jwt_federation(region: str, logger: Optional[logging.Logger] = Non raise -def get_aws_jwt_federation_info(region: str, logger: Optional[logging.Logger] = None) -> Optional[Dict[str, Any]]: +def get_aws_jwt_federation_info(region: str, logger: logging.Logger | None = None) -> dict[str, Any] | None: """Get AWS IAM JWT federation info if enabled. Args: @@ -453,12 +453,12 @@ def get_aws_jwt_federation_info(region: str, logger: Optional[logging.Logger] = def ensure_aws_jwt_permissions( role_arn: str, - audiences: List[str], + audiences: list[str], region: str, account_id: str, signing_algorithm: str = "ES384", max_duration_seconds: int = 3600, - logger: Optional[logging.Logger] = None, + logger: logging.Logger | None = None, ) -> None: """Ensure execution role has STS:GetWebIdentityToken permissions. @@ -543,7 +543,7 @@ def generate_random_id() -> str: """Generate a random ID for Cognito resources using cryptographically secure random.""" return str(uuid.uuid4())[:8] - def create_dual_pool_setup(self) -> Dict[str, Any]: + def create_dual_pool_setup(self) -> dict[str, Any]: """Create complete Cognito setup for Identity. Creates two user pools: @@ -576,7 +576,7 @@ def create_dual_pool_setup(self) -> Dict[str, Any]: self.logger.error("Failed to create Cognito pools: %s", str(e)) raise - def _create_runtime_pool(self) -> Dict[str, Any]: + def _create_runtime_pool(self) -> dict[str, Any]: """Create Runtime User Pool for agent inbound authentication. Returns: @@ -628,7 +628,7 @@ def _create_runtime_pool(self) -> Dict[str, Any]: "password": password, } - def _create_identity_pool(self) -> Dict[str, Any]: + def _create_identity_pool(self) -> dict[str, Any]: """Create Identity User Pool for external service authentication. Returns: @@ -779,7 +779,7 @@ def _delete_user_pool(self, pool_id: str, pool_type: str) -> None: else: self.logger.warning(" ⚠️ Error deleting %s pool: %s", pool_type, str(e)) - def _create_identity_pool_m2m(self) -> Dict[str, Any]: + def _create_identity_pool_m2m(self) -> dict[str, Any]: """Create Identity User Pool for M2M (client credentials) flows. Returns: @@ -832,7 +832,7 @@ def _create_identity_pool_m2m(self) -> Dict[str, Any]: "flow_type": "client_credentials", } - def create_user_federation_pools(self) -> Dict[str, Any]: + def create_user_federation_pools(self) -> dict[str, Any]: """Create pools for USER_FEDERATION flow (user consent required). Returns: @@ -848,7 +848,7 @@ def create_user_federation_pools(self) -> Dict[str, Any]: return {"runtime": runtime_config, "identity": identity_config, "flow_type": "user"} - def create_m2m_pools(self) -> Dict[str, Any]: + def create_m2m_pools(self) -> dict[str, Any]: """Create pools for M2M CLIENT_CREDENTIALS flow (no user required). Returns: diff --git a/src/bedrock_agentcore_starter_toolkit/operations/memory/constants.py b/src/bedrock_agentcore_starter_toolkit/operations/memory/constants.py index 05627b7b..6b00dcee 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/memory/constants.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/memory/constants.py @@ -1,7 +1,6 @@ """Constants for Bedrock AgentCore Memory SDK.""" from enum import Enum -from typing import Optional class StrategyType(Enum): @@ -12,7 +11,7 @@ class StrategyType(Enum): USER_PREFERENCE = "userPreferenceMemoryStrategy" CUSTOM = "customMemoryStrategy" - def extraction_wrapper_key(self) -> Optional[str]: + def extraction_wrapper_key(self) -> str | None: """Get the extraction wrapper key for this strategy type.""" extraction_keys = { StrategyType.SEMANTIC: "semanticExtractionConfiguration", @@ -20,7 +19,7 @@ def extraction_wrapper_key(self) -> Optional[str]: } return extraction_keys.get(self) - def consolidation_wrapper_key(self) -> Optional[str]: + def consolidation_wrapper_key(self) -> str | None: """Get the consolidation wrapper key for this strategy type.""" # Only SUMMARY strategy has a consolidation wrapper key if self == StrategyType.SUMMARY: @@ -37,7 +36,7 @@ def get_memory_strategy(self) -> str: } return strategy_mapping[self] - def get_override_type(self) -> Optional[str]: + def get_override_type(self) -> str | None: """Get the override type for custom strategies.""" # This method is primarily for CUSTOM strategy type # The actual override type would be determined by context @@ -53,7 +52,7 @@ class OverrideType(Enum): SUMMARY_OVERRIDE = "SUMMARY_OVERRIDE" USER_PREFERENCE_OVERRIDE = "USER_PREFERENCE_OVERRIDE" - def extraction_wrapper_key(self) -> Optional[str]: + def extraction_wrapper_key(self) -> str | None: """Get the extraction wrapper key for this override type.""" extraction_keys = { OverrideType.SEMANTIC_OVERRIDE: "semanticExtractionOverride", @@ -61,7 +60,7 @@ def extraction_wrapper_key(self) -> Optional[str]: } return extraction_keys.get(self) - def consolidation_wrapper_key(self) -> Optional[str]: + def consolidation_wrapper_key(self) -> str | None: """Get the consolidation wrapper key for this override type.""" consolidation_keys = { OverrideType.SEMANTIC_OVERRIDE: "semanticConsolidationOverride", diff --git a/src/bedrock_agentcore_starter_toolkit/operations/memory/manager.py b/src/bedrock_agentcore_starter_toolkit/operations/memory/manager.py index 19e969f1..1d8b439b 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/memory/manager.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/memory/manager.py @@ -4,7 +4,8 @@ import logging import time import uuid -from typing import Any, Callable, Dict, List, Optional, Union +from collections.abc import Callable +from typing import Any import boto3 from botocore.config import Config as BotocoreConfig @@ -32,10 +33,10 @@ class MemoryManager: def __init__( self, - region_name: Optional[str] = None, - boto3_session: Optional[boto3.Session] = None, - boto_client_config: Optional[BotocoreConfig] = None, - console: Optional[Console] = None, + region_name: str | None = None, + boto3_session: boto3.Session | None = None, + boto_client_config: BotocoreConfig | None = None, + console: Console | None = None, ): """Initialize MemoryManager with AWS region. @@ -139,7 +140,7 @@ def _validate_namespace(self, namespace: str) -> bool: return True - def _validate_strategy_config(self, strategy: Dict[str, Any], strategy_type: str) -> None: + def _validate_strategy_config(self, strategy: dict[str, Any], strategy_type: str) -> None: """Validate strategy configuration parameters.""" strategy_config = strategy[strategy_type] @@ -148,8 +149,8 @@ def _validate_strategy_config(self, strategy: Dict[str, Any], strategy_type: str self._validate_namespace(namespace) def _wrap_configuration( - self, config: Dict[str, Any], strategy_type: str, override_type: Optional[str] = None - ) -> Dict[str, Any]: + self, config: dict[str, Any], strategy_type: str, override_type: str | None = None + ) -> dict[str, Any]: """Wrap configuration based on strategy type using new enum methods.""" wrapped_config = {} @@ -199,11 +200,11 @@ def _wrap_configuration( def _create_memory( self, name: str, - strategies: Optional[List[Dict[str, Any]]] = None, - description: Optional[str] = None, + strategies: list[dict[str, Any]] | None = None, + description: str | None = None, event_expiry_days: int = 90, - memory_execution_role_arn: Optional[str] = None, - encryption_key_arn: Optional[str] = None, + memory_execution_role_arn: str | None = None, + encryption_key_arn: str | None = None, ) -> Memory: """Create a memory resource and return the raw response. @@ -245,13 +246,13 @@ def _create_memory( def _create_memory_and_wait( self, name: str, - strategies: Optional[List[Dict[str, Any]]], - description: Optional[str] = None, + strategies: list[dict[str, Any]] | None, + description: str | None = None, event_expiry_days: int = 90, - memory_execution_role_arn: Optional[str] = None, + memory_execution_role_arn: str | None = None, max_wait: int = 300, poll_interval: int = 10, - encryption_key_arn: Optional[str] = None, + encryption_key_arn: str | None = None, enable_observability: bool = True, ) -> Memory: """Create a memory and wait for it to become ACTIVE. @@ -304,11 +305,11 @@ def _create_memory_and_wait( def create_memory_and_wait( self, name: str, - strategies: Optional[List[Union[BaseStrategy, Dict[str, Any]]]] = None, - description: Optional[str] = None, + strategies: list[BaseStrategy | dict[str, Any]] | None = None, + description: str | None = None, event_expiry_days: int = 90, - memory_execution_role_arn: Optional[str] = None, - encryption_key_arn: Optional[str] = None, + memory_execution_role_arn: str | None = None, + encryption_key_arn: str | None = None, max_wait: int = 300, poll_interval: int = 10, enable_observability: bool = True, # NEW PARAMETER - defaults to True @@ -364,11 +365,11 @@ def create_memory_and_wait( def get_or_create_memory( self, name: str, - strategies: Optional[List[Union[BaseStrategy, Dict[str, Any]]]] = None, - description: Optional[str] = None, + strategies: list[BaseStrategy | dict[str, Any]] | None = None, + description: str | None = None, event_expiry_days: int = 90, - memory_execution_role_arn: Optional[str] = None, - encryption_key_arn: Optional[str] = None, + memory_execution_role_arn: str | None = None, + encryption_key_arn: str | None = None, ) -> Memory: """Fetch an existing memory resource or create the memory. @@ -455,7 +456,7 @@ def get_memory_status(self, memory_id: str) -> str: logger.error(" ❌ Error retrieving memory status: %s", e) raise - def get_memory_strategies(self, memory_id: str) -> List[MemoryStrategy]: + def get_memory_strategies(self, memory_id: str) -> list[MemoryStrategy]: """Get all strategies for a memory.""" try: response = self._control_plane_client.get_memory(memoryId=memory_id) @@ -505,7 +506,7 @@ def list_memories(self, max_results: int = 100) -> list[MemorySummary]: logger.error(" ❌ Error listing memories: %s", e) raise - def delete_memory(self, memory_id: str) -> Dict[str, Any]: + def delete_memory(self, memory_id: str) -> dict[str, Any]: """Delete a memory resource. Maps to: bedrock-agentcore-control.delete_memory. @@ -518,7 +519,7 @@ def delete_memory(self, memory_id: str) -> Dict[str, Any]: logger.error(" ❌ Error deleting memory: %s", e) raise - def delete_memory_and_wait(self, memory_id: str, max_wait: int = 300, poll_interval: int = 10) -> Dict[str, Any]: + def delete_memory_and_wait(self, memory_id: str, max_wait: int = 300, poll_interval: int = 10) -> dict[str, Any]: """Delete a memory and wait for deletion to complete. This method deletes a memory and polls until it's fully deleted, @@ -558,18 +559,18 @@ def delete_memory_and_wait(self, memory_id: str, max_wait: int = 300, poll_inter time.sleep(poll_interval) - raise TimeoutError("Memory %s was not deleted within %d seconds" % (memory_id, max_wait)) + raise TimeoutError(f"Memory {memory_id} was not deleted within {max_wait} seconds") # ==================== DATA PLANE METHODS ==================== def _paginated_list( self, - api_method: Callable[..., Dict[str, Any]], + api_method: Callable[..., dict[str, Any]], response_key: str, - base_kwargs: Dict[str, Any], - max_results: Optional[int] = None, - next_token: Optional[str] = None, - ) -> tuple[List[Dict[str, Any]], Optional[str]]: + base_kwargs: dict[str, Any], + max_results: int | None = None, + next_token: str | None = None, + ) -> tuple[list[dict[str, Any]], str | None]: """Generic paginated list helper for data plane list operations. Args: @@ -612,12 +613,12 @@ def _paginated_list( def _paginated_list_page( self, - api_method: Callable[..., Dict[str, Any]], + api_method: Callable[..., dict[str, Any]], response_key: str, - base_kwargs: Dict[str, Any], - max_results: Optional[int] = None, - next_token: Optional[str] = None, - ) -> tuple[List[Dict[str, Any]], Optional[str]]: + base_kwargs: dict[str, Any], + max_results: int | None = None, + next_token: str | None = None, + ) -> tuple[list[dict[str, Any]], str | None]: """Fetch a single page of results for browser pagination. Args: @@ -641,8 +642,8 @@ def _paginated_list_page( def list_actors( self, memory_id: str, - max_results: Optional[int] = None, - ) -> List[Dict[str, Any]]: + max_results: int | None = None, + ) -> list[dict[str, Any]]: """List all actors who have events in a memory. Maps to: bedrock-agentcore.list_actors. @@ -675,8 +676,8 @@ def list_sessions( self, memory_id: str, actor_id: str, - max_results: Optional[int] = None, - ) -> List[Dict[str, Any]]: + max_results: int | None = None, + ) -> list[dict[str, Any]]: """List all sessions for an actor. Maps to: bedrock-agentcore.list_sessions. @@ -711,8 +712,8 @@ def list_events( memory_id: str, actor_id: str, session_id: str, - max_results: Optional[int] = None, - ) -> List[Dict[str, Any]]: + max_results: int | None = None, + ) -> list[dict[str, Any]]: """List events in a session. Maps to: bedrock-agentcore.list_events. @@ -743,7 +744,7 @@ def list_events( logger.error("Error listing events: %s", e) raise - def get_event(self, memory_id: str, event_id: str) -> Dict[str, Any]: + def get_event(self, memory_id: str, event_id: str) -> dict[str, Any]: """Get a specific event by ID. Maps to: bedrock-agentcore.get_event. @@ -771,8 +772,8 @@ def list_records( self, memory_id: str, namespace: str, - max_results: Optional[int] = None, - ) -> List[Dict[str, Any]]: + max_results: int | None = None, + ) -> list[dict[str, Any]]: """List memory records in a namespace. Maps to: bedrock-agentcore.list_memory_records. @@ -802,7 +803,7 @@ def list_records( logger.error("Error listing records: %s", e) raise - def get_record(self, memory_id: str, record_id: str) -> Dict[str, Any]: + def get_record(self, memory_id: str, record_id: str) -> dict[str, Any]: """Get a specific memory record by ID. Maps to: bedrock-agentcore.get_memory_record. @@ -832,7 +833,7 @@ def search_records( namespace: str, query: str, max_results: int = 10, - ) -> List[Dict[str, Any]]: + ) -> list[dict[str, Any]]: """Semantic search for memory records. Maps to: bedrock-agentcore.retrieve_memory_records. @@ -870,14 +871,14 @@ def add_semantic_strategy( self, memory_id: str, name: str, - description: Optional[str] = None, - namespaces: Optional[List[str]] = None, + description: str | None = None, + namespaces: list[str] | None = None, ) -> Memory: """Add a semantic memory strategy. Note: Configuration is no longer provided for built-in strategies as per API changes. """ - strategy: Dict = { + strategy: dict = { StrategyType.SEMANTIC.value: { "name": name, } @@ -894,8 +895,8 @@ def add_semantic_strategy_and_wait( self, memory_id: str, name: str, - description: Optional[str] = None, - namespaces: Optional[List[str]] = None, + description: str | None = None, + namespaces: list[str] | None = None, max_wait: int = 300, poll_interval: int = 10, ) -> Memory: @@ -914,14 +915,14 @@ def add_summary_strategy( self, memory_id: str, name: str, - description: Optional[str] = None, - namespaces: Optional[List[str]] = None, + description: str | None = None, + namespaces: list[str] | None = None, ) -> Memory: """Add a summary memory strategy. Note: Configuration is no longer provided for built-in strategies as per API changes. """ - strategy: Dict = { + strategy: dict = { StrategyType.SUMMARY.value: { "name": name, } @@ -938,8 +939,8 @@ def add_summary_strategy_and_wait( self, memory_id: str, name: str, - description: Optional[str] = None, - namespaces: Optional[List[str]] = None, + description: str | None = None, + namespaces: list[str] | None = None, max_wait: int = 300, poll_interval: int = 10, ) -> Memory: @@ -951,14 +952,14 @@ def add_user_preference_strategy( self, memory_id: str, name: str, - description: Optional[str] = None, - namespaces: Optional[List[str]] = None, + description: str | None = None, + namespaces: list[str] | None = None, ) -> Memory: """Add a user preference memory strategy. Note: Configuration is no longer provided for built-in strategies as per API changes. """ - strategy: Dict = { + strategy: dict = { StrategyType.USER_PREFERENCE.value: { "name": name, } @@ -975,8 +976,8 @@ def add_user_preference_strategy_and_wait( self, memory_id: str, name: str, - description: Optional[str] = None, - namespaces: Optional[List[str]] = None, + description: str | None = None, + namespaces: list[str] | None = None, max_wait: int = 300, poll_interval: int = 10, ) -> Memory: @@ -988,10 +989,10 @@ def add_custom_semantic_strategy( self, memory_id: str, name: str, - extraction_config: Dict[str, Any], - consolidation_config: Dict[str, Any], - description: Optional[str] = None, - namespaces: Optional[List[str]] = None, + extraction_config: dict[str, Any], + consolidation_config: dict[str, Any], + description: str | None = None, + namespaces: list[str] | None = None, ) -> Memory: """Add a custom semantic strategy with prompts. @@ -1034,10 +1035,10 @@ def add_custom_semantic_strategy_and_wait( self, memory_id: str, name: str, - extraction_config: Dict[str, Any], - consolidation_config: Dict[str, Any], - description: Optional[str] = None, - namespaces: Optional[List[str]] = None, + extraction_config: dict[str, Any], + consolidation_config: dict[str, Any], + description: str | None = None, + namespaces: list[str] | None = None, max_wait: int = 300, poll_interval: int = 10, ) -> Memory: @@ -1051,12 +1052,12 @@ def modify_strategy( self, memory_id: str, strategy_id: str, - description: Optional[str] = None, - namespaces: Optional[List[str]] = None, - configuration: Optional[Dict[str, Any]] = None, + description: str | None = None, + namespaces: list[str] | None = None, + configuration: dict[str, Any] | None = None, ) -> Memory: """Modify a strategy with full control over configuration.""" - modify_config: Dict = {"strategyId": strategy_id} + modify_config: dict = {"strategyId": strategy_id} if description is not None: modify_config["description"] = description @@ -1074,9 +1075,9 @@ def delete_strategy(self, memory_id: str, strategy_id: str) -> Memory: def update_memory_strategies( self, memory_id: str, - add_strategies: Optional[List[Union[BaseStrategy, Dict[str, Any]]]] = None, - modify_strategies: Optional[List[Dict[str, Any]]] = None, - delete_strategy_ids: Optional[List[str]] = None, + add_strategies: list[BaseStrategy | dict[str, Any]] | None = None, + modify_strategies: list[dict[str, Any]] | None = None, + delete_strategy_ids: list[str] | None = None, ) -> Memory: """Update memory strategies - add, modify, or delete. @@ -1120,7 +1121,7 @@ def update_memory_strategies( strategy_info = strategy_map.get(strategy_id) if not strategy_info: - raise ValueError("Strategy %s not found in memory %s" % (strategy_id, memory_id)) + raise ValueError(f"Strategy {strategy_id} not found in memory {memory_id}") # Handle field name variations for strategy type strategy_type = strategy_info.get("type", strategy_info.get("memoryStrategyType", "SEMANTIC")) @@ -1161,9 +1162,9 @@ def update_memory_strategies( def update_memory_strategies_and_wait( self, memory_id: str, - add_strategies: Optional[List[Union[BaseStrategy, Dict[str, Any]]]] = None, - modify_strategies: Optional[List[Dict[str, Any]]] = None, - delete_strategy_ids: Optional[List[str]] = None, + add_strategies: list[BaseStrategy | dict[str, Any]] | None = None, + modify_strategies: list[dict[str, Any]] | None = None, + delete_strategy_ids: list[str] | None = None, max_wait: int = 300, poll_interval: int = 10, ) -> Memory: @@ -1199,7 +1200,7 @@ def update_memory_strategies_and_wait( # Wait for memory to return to ACTIVE return self._wait_for_memory_active(memory_id, max_wait, poll_interval) - def add_strategy(self, memory_id: str, strategy: Union[BaseStrategy, Dict[str, Any]]) -> Memory: + def add_strategy(self, memory_id: str, strategy: BaseStrategy | dict[str, Any]) -> Memory: """Add a strategy to a memory (without waiting). WARNING: After adding a strategy, the memory enters CREATING state temporarily. @@ -1228,7 +1229,7 @@ def add_strategy(self, memory_id: str, strategy: Union[BaseStrategy, Dict[str, A def add_strategy_and_wait( self, memory_id: str, - strategy: Union[BaseStrategy, Dict[str, Any]], + strategy: BaseStrategy | dict[str, Any], max_wait: int = 300, poll_interval: int = 10, ) -> Memory: @@ -1270,7 +1271,7 @@ def add_strategy_and_wait( memory_id=memory_id, add_strategies=[strategy], max_wait=max_wait, poll_interval=poll_interval ) - def _check_strategies_terminal_state(self, strategies: List[Dict[str, Any]]) -> tuple[bool, List[str], List[str]]: + def _check_strategies_terminal_state(self, strategies: list[dict[str, Any]]) -> tuple[bool, list[str], list[str]]: """Check if all strategies are in terminal states. Args: @@ -1318,7 +1319,7 @@ def _wait_for_memory_active(self, memory_id: str, max_wait: int, poll_interval: # Check if memory itself has failed if memory_status == MemoryStatus.FAILED.value: failure_reason = memory.get("failureReason", "Unknown") - raise RuntimeError("Memory update failed: %s" % failure_reason) + raise RuntimeError(f"Memory update failed: {failure_reason}") # Get strategies and check their statuses strategies = memory.get("strategies", memory.get("memoryStrategies", [])) @@ -1343,7 +1344,7 @@ def _wait_for_memory_active(self, memory_id: str, max_wait: int, poll_interval: if memory_status == MemoryStatus.ACTIVE.value and all_strategies_terminal: # Check if any strategy failed if failed_strategy_names: - raise RuntimeError("Memory strategy(ies) failed: %s" % ", ".join(failed_strategy_names)) + raise RuntimeError("Memory strategy(ies) failed: {}".format(", ".join(failed_strategy_names))) logger.info( "Memory %s is ACTIVE and all strategies are in terminal states (took %d seconds)", @@ -1361,8 +1362,8 @@ def _wait_for_memory_active(self, memory_id: str, max_wait: int, poll_interval: raise raise TimeoutError( - "Memory %s did not return to ACTIVE state with all strategies in terminal states within %d seconds" - % (memory_id, max_wait) + f"Memory {memory_id} did not return to ACTIVE state with all strategies " + f"in terminal states within {max_wait} seconds" ) def _validate_namespace(self, namespace: str) -> bool: @@ -1375,7 +1376,7 @@ def _validate_namespace(self, namespace: str) -> bool: return True - def _validate_strategy_config(self, strategy: Dict[str, Any], strategy_type: str) -> None: + def _validate_strategy_config(self, strategy: dict[str, Any], strategy_type: str) -> None: """Validate strategy configuration parameters.""" strategy_config = strategy[strategy_type] @@ -1394,10 +1395,10 @@ def _enable_observability_for_memory(self, memory: Memory) -> None: def enable_observability( self, memory_id: str, - memory_arn: Optional[str] = None, + memory_arn: str | None = None, enable_logs: bool = True, enable_traces: bool = True, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Enable CloudWatch observability for an existing memory resource.""" delivery_manager = ObservabilityDeliveryManager(region_name=self.region_name) result = delivery_manager.enable_for_memory( @@ -1419,7 +1420,7 @@ def disable_observability( self, memory_id: str, delete_log_group: bool = False, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Disable CloudWatch observability for a memory resource.""" delivery_manager = ObservabilityDeliveryManager(region_name=self.region_name) result = delivery_manager.disable_for_memory( diff --git a/src/bedrock_agentcore_starter_toolkit/operations/memory/memory_formatters.py b/src/bedrock_agentcore_starter_toolkit/operations/memory/memory_formatters.py index 7e560d42..8580a369 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/memory/memory_formatters.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/memory/memory_formatters.py @@ -2,7 +2,7 @@ import json from dataclasses import dataclass -from typing import Any, Dict, Optional, Union +from typing import Any from rich.panel import Panel @@ -91,7 +91,7 @@ class DisplayConfig: # ==================== Content Extraction ==================== -def extract_record_text(record: Dict[str, Any]) -> str: +def extract_record_text(record: dict[str, Any]) -> str: """Extract text content from a record. Args: @@ -106,7 +106,7 @@ def extract_record_text(record: Dict[str, Any]) -> str: return str(content) -def extract_event_text(event: Dict[str, Any]) -> Optional[str]: +def extract_event_text(event: dict[str, Any]) -> str | None: """Extract text content from event payload. Args: @@ -139,7 +139,7 @@ def extract_event_text(event: Dict[str, Any]) -> Optional[str]: return None -def extract_event_role(event: Dict[str, Any]) -> Optional[str]: +def extract_event_role(event: dict[str, Any]) -> str | None: """Extract role from event payload. Args: @@ -156,7 +156,7 @@ def extract_event_role(event: Dict[str, Any]) -> Optional[str]: return None -def extract_event_type(event: Dict[str, Any]) -> Optional[str]: +def extract_event_type(event: dict[str, Any]) -> str | None: """Extract event type from payload. Args: @@ -209,7 +209,7 @@ def format_content_preview(text: str, verbose: bool = False) -> str: return truncate_text(preview, max_len, verbose=False) -def render_content_panel(text: str, verbose: bool = False) -> Union[Panel, str]: +def render_content_panel(text: str, verbose: bool = False) -> Panel | str: """Render content as panel (verbose) or truncated string. Args: @@ -224,7 +224,7 @@ def render_content_panel(text: str, verbose: bool = False) -> Union[Panel, str]: return format_content_preview(text) -def format_payload_snippet(event: Dict[str, Any], max_len: int = 60) -> str: +def format_payload_snippet(event: dict[str, Any], max_len: int = 60) -> str: """Format raw payload as truncated JSON snippet. Args: @@ -261,7 +261,7 @@ def format_truncation_hint(shown: int, total: int) -> str: return f"[dim]... {remaining} more[/dim]" -def format_role_icon(role: Optional[str]) -> str: +def format_role_icon(role: str | None) -> str: """Format role as colored icon string. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/operations/memory/memory_visualizer.py b/src/bedrock_agentcore_starter_toolkit/operations/memory/memory_visualizer.py index 3d1880f3..385f380d 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/memory/memory_visualizer.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/memory/memory_visualizer.py @@ -3,7 +3,7 @@ import json import logging from pathlib import Path -from typing import Any, Dict, List, Optional +from typing import Any from rich.box import ROUNDED from rich.console import Console @@ -38,15 +38,13 @@ class MemoryVisualizer: """Visualizer for displaying memory resources in human-readable format.""" - def __init__(self, console: Optional[Console] = None): + def __init__(self, console: Console | None = None): """Initialize the memory visualizer.""" self.console = console or Console() # ==================== Build Methods (return renderables) ==================== - def build_memory_tree( - self, memory: Dict[str, Any], verbose: bool = False, actor_count: Optional[int] = None - ) -> Tree: + def build_memory_tree(self, memory: dict[str, Any], verbose: bool = False, actor_count: int | None = None) -> Tree: """Build a memory tree renderable. Args: @@ -67,7 +65,7 @@ def build_memory_tree( self._add_memory_strategies(tree, data, verbose) return tree - def build_actors_table(self, actors: List[Dict[str, Any]], memory_id: str) -> Table: + def build_actors_table(self, actors: list[dict[str, Any]], memory_id: str) -> Table: """Build an actors table renderable. Args: @@ -85,7 +83,7 @@ def build_actors_table(self, actors: List[Dict[str, Any]], memory_id: str) -> Ta table.add_row(str(idx), actor.get("actorId", "N/A")) return table - def build_sessions_table(self, sessions: List[Dict[str, Any]], actor_id: str) -> Table: + def build_sessions_table(self, sessions: list[dict[str, Any]], actor_id: str) -> Table: """Build a sessions table renderable. Args: @@ -103,7 +101,7 @@ def build_sessions_table(self, sessions: List[Dict[str, Any]], actor_id: str) -> table.add_row(str(idx), session.get("sessionId", "N/A")) return table - def build_events_table(self, events: List[Dict[str, Any]], session_id: str, verbose: bool = False) -> Table: + def build_events_table(self, events: list[dict[str, Any]], session_id: str, verbose: bool = False) -> Table: """Build an events table renderable. Args: @@ -128,7 +126,7 @@ def build_events_table(self, events: List[Dict[str, Any]], session_id: str, verb table.add_row(str(idx), timestamp, format_role_icon(role), content) return table - def build_event_detail(self, event: Dict[str, Any], verbose: bool = False) -> Panel: + def build_event_detail(self, event: dict[str, Any], verbose: bool = False) -> Panel: """Build an event detail panel renderable. Args: @@ -172,7 +170,7 @@ def build_event_detail(self, event: Dict[str, Any], verbose: bool = False) -> Pa return Panel("\n".join(lines), title="Event Detail", border_style="cyan") - def build_namespaces_table(self, strategies: List[Dict[str, Any]], memory_id: str) -> Table: + def build_namespaces_table(self, strategies: list[dict[str, Any]], memory_id: str) -> Table: """Build a namespaces table renderable. Args: @@ -197,7 +195,7 @@ def build_namespaces_table(self, strategies: List[Dict[str, Any]], memory_id: st idx += 1 return table - def build_records_table(self, records: List[Dict[str, Any]], namespace: str, verbose: bool = False) -> Table: + def build_records_table(self, records: list[dict[str, Any]], namespace: str, verbose: bool = False) -> Table: """Build a records table renderable. Args: @@ -222,9 +220,7 @@ def build_records_table(self, records: List[Dict[str, Any]], namespace: str, ver table.add_row(str(idx), record_id, created, content) return table - def build_record_detail( - self, record: Dict[str, Any], verbose: bool = False, namespace: Optional[str] = None - ) -> Panel: + def build_record_detail(self, record: dict[str, Any], verbose: bool = False, namespace: str | None = None) -> Panel: """Build a record detail panel renderable. Args: @@ -250,14 +246,12 @@ def build_record_detail( # ==================== Memory Details ==================== - def visualize_memory( - self, memory: Dict[str, Any], verbose: bool = False, actor_count: Optional[int] = None - ) -> None: + def visualize_memory(self, memory: dict[str, Any], verbose: bool = False, actor_count: int | None = None) -> None: """Visualize a memory resource as a hierarchical tree.""" tree = self.build_memory_tree(memory, verbose, actor_count) self.console.print(tree) - def _extract_memory_data(self, memory: Any) -> Dict[str, Any]: + def _extract_memory_data(self, memory: Any) -> dict[str, Any]: """Extract data dict from memory object.""" if hasattr(memory, "get"): return memory @@ -274,7 +268,7 @@ def _format_memory_header(self, memory_id: str, name: str, status: str) -> Text: header.append(f" ({icon}{status})", style=style) return header - def _add_memory_info(self, tree: Tree, data: Dict[str, Any], verbose: bool, actor_count: Optional[int]) -> None: + def _add_memory_info(self, tree: Tree, data: dict[str, Any], verbose: bool, actor_count: int | None) -> None: """Add info section to memory tree.""" info_branch = tree.add("📋 [bold]Info[/bold]") @@ -299,7 +293,7 @@ def _add_memory_info(self, tree: Tree, data: Dict[str, Any], verbose: bool, acto if actor_count is not None: info_branch.add(f"[dim]Actors:[/dim] {actor_count}") - def _add_memory_strategies(self, tree: Tree, data: Dict[str, Any], verbose: bool) -> None: + def _add_memory_strategies(self, tree: Tree, data: dict[str, Any], verbose: bool) -> None: """Add strategies section to memory tree.""" strategies = data.get("strategies") or data.get("memoryStrategies") or [] @@ -311,7 +305,7 @@ def _add_memory_strategies(self, tree: Tree, data: Dict[str, Any], verbose: bool for strategy in strategies: self._add_strategy_node(strategies_branch, strategy, verbose) - def _add_strategy_node(self, parent: Tree, strategy: Dict[str, Any], verbose: bool) -> None: + def _add_strategy_node(self, parent: Tree, strategy: dict[str, Any], verbose: bool) -> None: """Add a strategy node to the tree.""" strategy_name = strategy.get("name", "Unnamed") strategy_type = strategy.get("type") or strategy.get("memoryStrategyType", "UNKNOWN") @@ -351,7 +345,7 @@ def _format_strategy_header(self, name: str, strategy_type: str, status: str) -> header.append(f" ({status_icon}{status})", style=status_style) return header - def _add_config_tree(self, parent: Tree, config: Dict[str, Any]) -> None: + def _add_config_tree(self, parent: Tree, config: dict[str, Any]) -> None: """Add configuration subtree.""" config_branch = parent.add("[dim]Configuration:[/dim]") for key, value in config.items(): @@ -363,7 +357,7 @@ def _add_config_tree(self, parent: Tree, config: Dict[str, Any]) -> None: # ==================== Memory List ==================== - def display_memory_list(self, memories: List[Dict[str, Any]], manager: Any = None) -> None: + def display_memory_list(self, memories: list[dict[str, Any]], manager: Any = None) -> None: """Display memories in a table format.""" if not memories: self.console.print("[yellow]No memories found.[/yellow]") @@ -423,9 +417,9 @@ def display_events_tree( max_actors: int = 10, max_sessions: int = 10, max_events: int = 10, - actor_id: Optional[str] = None, - session_id: Optional[str] = None, - output: Optional[str] = None, + actor_id: str | None = None, + session_id: str | None = None, + output: str | None = None, verbose: bool = False, ) -> None: """Display events as a tree: memory -> actors -> sessions -> events.""" @@ -450,7 +444,7 @@ def display_events_tree( self._output_or_print(root, export_data, output, "events") - def _get_actors(self, manager: Any, memory_id: str, actor_id: Optional[str], max_actors: int) -> tuple: + def _get_actors(self, manager: Any, memory_id: str, actor_id: str | None, max_actors: int) -> tuple: """Get actors list (filtered or all).""" if actor_id: return [{"actorId": actor_id}], 1 @@ -462,12 +456,12 @@ def _build_actor_subtree( root: Tree, manager: Any, memory_id: str, - actor: Dict[str, Any], + actor: dict[str, Any], max_sessions: int, max_events: int, - session_id: Optional[str], + session_id: str | None, verbose: bool, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Build actor subtree with sessions and events.""" aid = actor.get("actorId", "N/A") actor_data = {"actorId": aid, "sessions": []} @@ -491,7 +485,7 @@ def _build_actor_subtree( return actor_data def _get_sessions( - self, manager: Any, memory_id: str, actor_id: str, session_id: Optional[str], max_sessions: int + self, manager: Any, memory_id: str, actor_id: str, session_id: str | None, max_sessions: int ) -> tuple: """Get sessions list (filtered or all).""" if session_id: @@ -505,10 +499,10 @@ def _build_session_subtree( manager: Any, memory_id: str, actor_id: str, - session: Dict[str, Any], + session: dict[str, Any], max_events: int, verbose: bool, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Build session subtree with events.""" sid = session.get("sessionId", "N/A") session_data = {"sessionId": sid, "events": []} @@ -533,9 +527,9 @@ def _build_session_subtree( return session_data - def _group_events_by_branch(self, events: List[Dict[str, Any]]) -> Dict[str, List[Dict[str, Any]]]: + def _group_events_by_branch(self, events: list[dict[str, Any]]) -> dict[str, list[dict[str, Any]]]: """Group events by branch name.""" - branches: Dict[str, List[Dict[str, Any]]] = {} + branches: dict[str, list[dict[str, Any]]] = {} for event in events: branch_name = event.get("branch", {}).get("name", "main") if branch_name not in branches: @@ -543,7 +537,7 @@ def _group_events_by_branch(self, events: List[Dict[str, Any]]) -> Dict[str, Lis branches[branch_name].append(event) return branches - def _add_event_node(self, branch_tree: Tree, event: Dict[str, Any], verbose: bool) -> None: + def _add_event_node(self, branch_tree: Tree, event: dict[str, Any], verbose: bool) -> None: """Add a single event node to the tree.""" timestamp = str(event.get("eventTimestamp", ""))[:19] text_content = extract_event_text(event) @@ -566,7 +560,7 @@ def _add_event_node(self, branch_tree: Tree, event: Dict[str, Any], verbose: boo # ==================== Single Event/Record Display ==================== - def display_single_event(self, event: Dict[str, Any], nth: int, total: int, verbose: bool) -> None: + def display_single_event(self, event: dict[str, Any], nth: int, total: int, verbose: bool) -> None: """Display a single event with details.""" self.console.print(f"[bold]Event[/bold] ({self._format_position_label(nth, total)})\n") @@ -588,7 +582,7 @@ def display_single_event(self, event: Dict[str, Any], nth: int, total: int, verb self.console.print() self._print_content_panel(text_content, verbose) - def display_single_record(self, record: Dict[str, Any], nth: int, total: int, verbose: bool) -> None: + def display_single_record(self, record: dict[str, Any], nth: int, total: int, verbose: bool) -> None: """Display a single record with details.""" self.console.print(f"[bold]Record[/bold] ({self._format_position_label(nth, total)})\n") @@ -622,7 +616,7 @@ def display_namespace_records( namespace: str, verbose: bool, max_results: int, - output: Optional[str] = None, + output: str | None = None, ) -> None: """Display records for a specific namespace.""" root = Tree(f"🧠 [bold cyan]{memory_id}[/bold cyan]") @@ -645,7 +639,7 @@ def display_records_tree( memory_id: str, verbose: bool, max_results: int, - output: Optional[str] = None, + output: str | None = None, ) -> None: """Display records as a tree by namespace.""" memory = manager.get_memory(memory_id) @@ -664,10 +658,10 @@ def _add_strategy_records( root: Tree, manager: Any, memory_id: str, - strategy: Dict[str, Any], + strategy: dict[str, Any], verbose: bool, max_results: int, - export_data: Dict[str, Any], + export_data: dict[str, Any], ) -> None: """Add strategy records subtree.""" strategy_name = strategy.get("name", "Unknown") @@ -693,9 +687,9 @@ def _add_records_to_tree( self, parent: Tree, namespace: str, - records: List[Dict[str, Any]], + records: list[dict[str, Any]], verbose: bool, - export_list: List[Dict[str, Any]], + export_list: list[dict[str, Any]], ) -> None: """Add records to a tree branch.""" records.sort(key=lambda r: r.get("createdAt", "")) @@ -714,7 +708,7 @@ def _add_records_to_tree( if hint: ns_branch.add(hint) - def _resolve_namespace(self, manager: Any, memory_id: str, ns_template: str) -> List[str]: + def _resolve_namespace(self, manager: Any, memory_id: str, ns_template: str) -> list[str]: """Resolve namespace template to actual namespaces.""" if "{actorId}" not in ns_template and "{sessionId}" not in ns_template: return [ns_template] @@ -738,7 +732,7 @@ def _resolve_namespace(self, manager: Any, memory_id: str, ns_template: str) -> return resolved - def display_search_results(self, records: List[Dict[str, Any]], query: str, verbose: bool) -> None: + def display_search_results(self, records: list[dict[str, Any]], query: str, verbose: bool) -> None: """Display semantic search results.""" self.console.print(f'[bold]Search Results[/bold] for "{query}" ({len(records)} found)\n') @@ -757,7 +751,7 @@ def display_search_results(self, records: List[Dict[str, Any]], query: str, verb # ==================== Utility Methods ==================== - def _output_or_print(self, tree: Tree, data: Dict[str, Any], output: Optional[str], label: str) -> None: + def _output_or_print(self, tree: Tree, data: dict[str, Any], output: str | None, label: str) -> None: """Output to file or print to console.""" if output: path = Path(output) diff --git a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/DictWrapper.py b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/DictWrapper.py index f29652cd..b9724e39 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/DictWrapper.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/DictWrapper.py @@ -1,12 +1,12 @@ """Base wrapper class for dictionary-like data structures.""" -from typing import Any, Dict +from typing import Any class DictWrapper: """A wrapper class that provides both attribute and dictionary-style access to data.""" - def __init__(self, data: Dict[str, Any]): + def __init__(self, data: dict[str, Any]): """Initialize the wrapper with dictionary data. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/Memory.py b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/Memory.py index f0139f7d..bd97fd96 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/Memory.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/Memory.py @@ -1,6 +1,6 @@ """Memory model class for AgentCore Memory resources.""" -from typing import Any, Dict +from typing import Any from .DictWrapper import DictWrapper @@ -8,7 +8,7 @@ class Memory(DictWrapper): """A class representing a memory resource.""" - def __init__(self, memory: Dict[str, Any]): + def __init__(self, memory: dict[str, Any]): """Initialize Memory with memory data. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/MemoryStrategy.py b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/MemoryStrategy.py index 942f6aa0..429115f2 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/MemoryStrategy.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/MemoryStrategy.py @@ -1,6 +1,6 @@ """Memory strategy model class for AgentCore Memory resources.""" -from typing import Any, Dict +from typing import Any from .DictWrapper import DictWrapper @@ -8,7 +8,7 @@ class MemoryStrategy(DictWrapper): """A class representing a memory strategy.""" - def __init__(self, memory_strategy: Dict[str, Any]): + def __init__(self, memory_strategy: dict[str, Any]): """Initialize MemoryStrategy with strategy data. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/MemorySummary.py b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/MemorySummary.py index b4a4dcc7..017f77e2 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/MemorySummary.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/MemorySummary.py @@ -1,6 +1,6 @@ """Memory summary model class for AgentCore Memory resources.""" -from typing import Any, Dict +from typing import Any from .DictWrapper import DictWrapper @@ -8,7 +8,7 @@ class MemorySummary(DictWrapper): """A class representing a memory summary.""" - def __init__(self, memory_summary: Dict[str, Any]): + def __init__(self, memory_summary: dict[str, Any]): """Initialize MemorySummary with summary data. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/__init__.py b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/__init__.py index a707ce05..f9d989a1 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/__init__.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/__init__.py @@ -34,7 +34,7 @@ """ # Memory resource models -from typing import Any, Dict, List +from typing import Any from .Memory import Memory from .MemoryStrategy import MemoryStrategy @@ -53,7 +53,7 @@ ) -def convert_strategies_to_dicts(strategies: List[StrategyType]) -> List[Dict[str, Any]]: +def convert_strategies_to_dicts(strategies: list[StrategyType]) -> list[dict[str, Any]]: """Convert mixed strategy types to dictionary format for API calls. This function handles both new typed strategies and legacy dictionary diff --git a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/base.py b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/base.py index b2adc8ee..2441a451 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/base.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/base.py @@ -1,7 +1,7 @@ """Base classes and types for memory strategies.""" from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union +from typing import TYPE_CHECKING, Any, Union from pydantic import BaseModel, ConfigDict, Field @@ -21,8 +21,8 @@ class ExtractionConfig(BaseModel): model_id: Model identifier for extraction operations """ - append_to_prompt: Optional[str] = Field(None, description="Additional prompt text for extraction") - model_id: Optional[str] = Field(None, description="Model identifier for extraction operations") + append_to_prompt: str | None = Field(None, description="Additional prompt text for extraction") + model_id: str | None = Field(None, description="Model identifier for extraction operations") model_config = ConfigDict(validate_by_name=True) @@ -35,8 +35,8 @@ class ConsolidationConfig(BaseModel): model_id: Model identifier for consolidation operations """ - append_to_prompt: Optional[str] = Field(None, description="Additional prompt text for consolidation") - model_id: Optional[str] = Field(None, description="Model identifier for consolidation operations") + append_to_prompt: str | None = Field(None, description="Additional prompt text for consolidation") + model_id: str | None = Field(None, description="Model identifier for consolidation operations") model_config = ConfigDict(validate_by_name=True) @@ -51,11 +51,11 @@ class BaseStrategy(BaseModel, ABC): """ name: str = Field(..., description="Strategy name") - description: Optional[str] = Field(None, description="Strategy description") - namespaces: Optional[List[str]] = Field(None, description="Strategy namespaces") + description: str | None = Field(None, description="Strategy description") + namespaces: list[str] | None = Field(None, description="Strategy namespaces") @abstractmethod - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: """Convert strategy to dictionary format for API calls. Returns: @@ -75,5 +75,5 @@ def to_dict(self) -> Dict[str, Any]: "CustomUserPreferenceStrategy", "UserPreferenceStrategy", "SelfManagedStrategy", - Dict[str, Any], # Backward compatibility with dict-based strategies + dict[str, Any], # Backward compatibility with dict-based strategies ] diff --git a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/custom.py b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/custom.py index a59992d4..1900498c 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/custom.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/custom.py @@ -1,6 +1,6 @@ """Custom memory strategy implementation.""" -from typing import Any, Dict +from typing import Any from pydantic import Field @@ -36,7 +36,7 @@ class CustomSemanticStrategy(BaseStrategy): extraction_config: ExtractionConfig = Field(..., description="Extraction configuration") consolidation_config: ConsolidationConfig = Field(..., description="Consolidation configuration") - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: """Convert to dictionary format for API calls.""" config = { "name": self.name, @@ -56,7 +56,7 @@ def to_dict(self) -> Dict[str, Any]: return {"customMemoryStrategy": config} - def _convert_extraction_config(self) -> Dict[str, Any]: + def _convert_extraction_config(self) -> dict[str, Any]: """Convert extraction config to API format.""" config = {} if self.extraction_config.append_to_prompt is not None: @@ -65,7 +65,7 @@ def _convert_extraction_config(self) -> Dict[str, Any]: config["modelId"] = self.extraction_config.model_id return config - def _convert_consolidation_config(self) -> Dict[str, Any]: + def _convert_consolidation_config(self) -> dict[str, Any]: """Convert consolidation config to API format.""" config = {} if self.consolidation_config.append_to_prompt is not None: @@ -97,7 +97,7 @@ class CustomSummaryStrategy(BaseStrategy): consolidation_config: ConsolidationConfig = Field(..., description="Consolidation configuration") - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: """Convert to dictionary format for API calls.""" config = { "name": self.name, @@ -116,7 +116,7 @@ def to_dict(self) -> Dict[str, Any]: return {"customMemoryStrategy": config} - def _convert_consolidation_config(self) -> Dict[str, Any]: + def _convert_consolidation_config(self) -> dict[str, Any]: """Convert consolidation config to API format.""" config = {} if self.consolidation_config.append_to_prompt is not None: @@ -155,7 +155,7 @@ class CustomUserPreferenceStrategy(BaseStrategy): extraction_config: ExtractionConfig = Field(..., description="Extraction configuration") consolidation_config: ConsolidationConfig = Field(..., description="Consolidation configuration") - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: """Convert to dictionary format for API calls.""" config = { "name": self.name, @@ -175,7 +175,7 @@ def to_dict(self) -> Dict[str, Any]: return {"customMemoryStrategy": config} - def _convert_extraction_config(self) -> Dict[str, Any]: + def _convert_extraction_config(self) -> dict[str, Any]: """Convert extraction config to API format.""" config = {} if self.extraction_config.append_to_prompt is not None: @@ -184,7 +184,7 @@ def _convert_extraction_config(self) -> Dict[str, Any]: config["modelId"] = self.extraction_config.model_id return config - def _convert_consolidation_config(self) -> Dict[str, Any]: + def _convert_consolidation_config(self) -> dict[str, Any]: """Convert consolidation config to API format.""" config = {} if self.consolidation_config.append_to_prompt is not None: diff --git a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/self_managed.py b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/self_managed.py index d299e964..2c50d539 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/self_managed.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/self_managed.py @@ -1,6 +1,6 @@ """Self managed memory strategy implementation.""" -from typing import Any, Dict, List, Union +from typing import Any from pydantic import BaseModel, Field @@ -61,15 +61,13 @@ class SelfManagedStrategy(BaseStrategy): ) """ - trigger_conditions: List[Union[MessageBasedTrigger, TokenBasedTrigger, TimeBasedTrigger]] = Field( - default_factory=list - ) + trigger_conditions: list[MessageBasedTrigger | TokenBasedTrigger | TimeBasedTrigger] = Field(default_factory=list) invocation_config: InvocationConfig historical_context_window_size: int = Field( default=4, description="Number of historical messages to include in processing context." ) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: """Convert to dictionary format for API calls.""" config = { "name": self.name, @@ -87,7 +85,7 @@ def to_dict(self) -> Dict[str, Any]: return {"customMemoryStrategy": config} - def _convert_trigger_conditions(self) -> List[Dict[str, Any]]: + def _convert_trigger_conditions(self) -> list[dict[str, Any]]: """Convert trigger conditions to API format.""" conditions = [] for condition in self.trigger_conditions: @@ -99,7 +97,7 @@ def _convert_trigger_conditions(self) -> List[Dict[str, Any]]: conditions.append({"timeBasedTrigger": {"idleSessionTimeout": condition.idle_session_timeout}}) return conditions - def _convert_invocation_config(self) -> Dict[str, Any]: + def _convert_invocation_config(self) -> dict[str, Any]: """Convert invocation config to API format.""" return { "topicArn": self.invocation_config.topic_arn, diff --git a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/semantic.py b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/semantic.py index 908df6d8..0b30b93d 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/semantic.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/semantic.py @@ -1,6 +1,6 @@ """Semantic memory strategy implementation.""" -from typing import Any, Dict +from typing import Any from .base import BaseStrategy @@ -20,7 +20,7 @@ class SemanticStrategy(BaseStrategy): ) """ - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: """Convert to dictionary format for API calls.""" config = { "name": self.name, diff --git a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/summary.py b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/summary.py index 34c3dc41..49a216aa 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/summary.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/summary.py @@ -1,6 +1,6 @@ """Summary memory strategy implementation.""" -from typing import Any, Dict +from typing import Any from .base import BaseStrategy @@ -20,7 +20,7 @@ class SummaryStrategy(BaseStrategy): ) """ - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: """Convert to dictionary format for API calls.""" config = { "name": self.name, diff --git a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/user_preference.py b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/user_preference.py index 6537a095..8946e35f 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/user_preference.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/user_preference.py @@ -1,6 +1,6 @@ """User preference memory strategy implementation.""" -from typing import Any, Dict +from typing import Any from .base import BaseStrategy @@ -19,7 +19,7 @@ class UserPreferenceStrategy(BaseStrategy): ) """ - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: """Convert to dictionary format for API calls.""" config = { "name": self.name, diff --git a/src/bedrock_agentcore_starter_toolkit/operations/memory/strategy_validator.py b/src/bedrock_agentcore_starter_toolkit/operations/memory/strategy_validator.py index 1b301f06..27ad231a 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/memory/strategy_validator.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/memory/strategy_validator.py @@ -2,7 +2,7 @@ import logging import re -from typing import Any, Dict, List, Union +from typing import Any from .constants import StrategyType from .models import convert_strategies_to_dicts @@ -36,7 +36,7 @@ def normalize_field_names(data: Any) -> Any: return data @staticmethod - def deep_compare(dict1: Dict[str, Any], dict2: Dict[str, Any], path: str = "") -> tuple[bool, str]: + def deep_compare(dict1: dict[str, Any], dict2: dict[str, Any], path: str = "") -> tuple[bool, str]: """Deep compare two dictionaries with detailed error reporting.""" # Normalize both dictionaries norm1 = UniversalComparator.normalize_field_names(dict1) @@ -127,7 +127,7 @@ class StrategyComparator: """Utility class for comparing memory strategies in detail.""" @staticmethod - def normalize_strategy(strategy: Union[Dict[str, Any], Dict[str, Dict[str, Any]]]) -> Dict[str, Any]: + def normalize_strategy(strategy: dict[str, Any] | dict[str, dict[str, Any]]) -> dict[str, Any]: """Normalize a strategy to a standard format with universal field normalization. Args: @@ -144,7 +144,7 @@ def normalize_strategy(strategy: Union[Dict[str, Any], Dict[str, Dict[str, Any]] return StrategyComparator._normalize_request_strategy(strategy) @staticmethod - def _normalize_memory_strategy(strategy: Dict[str, Any]) -> Dict[str, Any]: + def _normalize_memory_strategy(strategy: dict[str, Any]) -> dict[str, Any]: """Normalize a strategy from memory response, including only fields relevant for comparison.""" # Handle different field name variations strategy_type = strategy.get("type", strategy.get("memoryStrategyType")) @@ -169,7 +169,7 @@ def _normalize_memory_strategy(strategy: Dict[str, Any]) -> Dict[str, Any]: return normalized @staticmethod - def _transform_memory_configuration(config: Dict[str, Any], strategy_type: str) -> Dict[str, Any]: + def _transform_memory_configuration(config: dict[str, Any], strategy_type: str) -> dict[str, Any]: """Transform memory configuration from stored format to match requested format. This handles the structural differences between how configurations are stored @@ -266,7 +266,7 @@ def _transform_memory_configuration(config: Dict[str, Any], strategy_type: str) return config @staticmethod - def _normalize_request_strategy(strategy_dict: Dict[str, Any]) -> Dict[str, Any]: + def _normalize_request_strategy(strategy_dict: dict[str, Any]) -> dict[str, Any]: """Normalize a strategy from request format.""" # Find the strategy type key in the dictionary strategy_type = None @@ -321,7 +321,7 @@ def _normalize_request_strategy(strategy_dict: Dict[str, Any]) -> Dict[str, Any] @staticmethod def compare_strategies( - existing_strategies: List[Dict[str, Any]], requested_strategies: List[Union[BaseStrategy, Dict[str, Any]]] + existing_strategies: list[dict[str, Any]], requested_strategies: list[BaseStrategy | dict[str, Any]] ) -> tuple[bool, str]: """Compare existing memory strategies with requested strategies using universal comparison. @@ -377,8 +377,8 @@ def compare_strategies( def validate_existing_memory_strategies( - memory_strategies: List[Dict[str, Any]], - requested_strategies: List[Union[BaseStrategy, Dict[str, Any]]], + memory_strategies: list[dict[str, Any]], + requested_strategies: list[BaseStrategy | dict[str, Any]], memory_name: str, ) -> None: """Validate that existing memory strategies match the requested strategies using universal comparison. diff --git a/src/bedrock_agentcore_starter_toolkit/operations/observability/builders.py b/src/bedrock_agentcore_starter_toolkit/operations/observability/builders.py index eb4dff4a..6c832430 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/observability/builders.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/observability/builders.py @@ -1,7 +1,7 @@ """Builders for constructing telemetry models from CloudWatch Logs Insights results.""" import json -from typing import Any, Optional +from typing import Any from .telemetry import RuntimeLog, Span @@ -37,12 +37,12 @@ def parse_json_field(field_name: str) -> Any: return value return value - def get_float(field_name: str) -> Optional[float]: + def get_float(field_name: str) -> float | None: """Get field as float. CloudWatch returns numeric fields as strings.""" value = get_field(field_name) return float(value) if value is not None else None - def get_int(field_name: str) -> Optional[int]: + def get_int(field_name: str) -> int | None: """Get field as int. CloudWatch returns numeric fields as strings.""" value = get_field(field_name) return int(value) if value is not None else None diff --git a/src/bedrock_agentcore_starter_toolkit/operations/observability/client.py b/src/bedrock_agentcore_starter_toolkit/operations/observability/client.py index 0c12a74f..161d48cd 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/observability/client.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/observability/client.py @@ -2,7 +2,6 @@ import logging import time -from typing import Dict, List, Optional import boto3 @@ -47,7 +46,7 @@ def query_spans_by_session( start_time_ms: int, end_time_ms: int, agent_id: str, - ) -> List[Span]: + ) -> list[Span]: """Query all spans for a session from aws/spans log group. Args: @@ -82,7 +81,7 @@ def query_spans_by_trace( start_time_ms: int, end_time_ms: int, agent_id: str, - ) -> List[Span]: + ) -> list[Span]: """Query all spans for a trace from aws/spans log group. Args: @@ -113,12 +112,12 @@ def query_spans_by_trace( def query_runtime_logs_by_traces( self, - trace_ids: List[str], + trace_ids: list[str], start_time_ms: int, end_time_ms: int, agent_id: str, endpoint_name: str = "DEFAULT", - ) -> List[RuntimeLog]: + ) -> list[RuntimeLog]: """Query runtime logs for multiple traces from agent-specific log group. Optimized to use a single batch query instead of one query per trace. @@ -165,12 +164,12 @@ def query_runtime_logs_by_traces( def _query_runtime_logs_individually( self, - trace_ids: List[str], + trace_ids: list[str], start_time_ms: int, end_time_ms: int, agent_id: str, endpoint_name: str = "DEFAULT", - ) -> List[RuntimeLog]: + ) -> list[RuntimeLog]: """Fallback method to query runtime logs one trace at a time. Args: @@ -214,7 +213,7 @@ def get_latest_session_id( start_time_ms: int, end_time_ms: int, agent_id: str, - ) -> Optional[str]: + ) -> str | None: """Get the most recent session ID for an agent. Args: @@ -260,7 +259,7 @@ def _execute_cloudwatch_query( log_group_name: str, start_time: int, end_time: int, - ) -> List[Dict]: + ) -> list[dict]: """Execute a CloudWatch Logs Insights query and wait for results. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/operations/observability/delivery.py b/src/bedrock_agentcore_starter_toolkit/operations/observability/delivery.py index 3db91488..4ccc27ae 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/observability/delivery.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/observability/delivery.py @@ -19,7 +19,7 @@ """ import logging -from typing import Any, Dict, Optional +from typing import Any import boto3 from botocore.exceptions import ClientError @@ -65,8 +65,8 @@ class ObservabilityDeliveryManager: def __init__( self, - region_name: Optional[str] = None, - boto3_session: Optional[boto3.Session] = None, + region_name: str | None = None, + boto3_session: boto3.Session | None = None, ): """Initialize the ObservabilityDeliveryManager. @@ -101,12 +101,12 @@ def account_id(self) -> str: def enable_observability_for_resource( self, resource_arn: str, - resource_id: Optional[str] = None, - resource_type: Optional[str] = None, + resource_id: str | None = None, + resource_type: str | None = None, enable_logs: bool = True, enable_traces: bool = True, - custom_log_group: Optional[str] = None, - ) -> Dict[str, Any]: + custom_log_group: str | None = None, + ) -> dict[str, Any]: """Enable CloudWatch observability for an AgentCore resource. This configures CloudWatch delivery sources and destinations to capture @@ -159,7 +159,7 @@ def enable_observability_for_resource( f"Unsupported resource_type: '{resource_type}'. Must be one of: {self.SUPPORTED_RESOURCE_TYPES}" ) - results: Dict[str, Any] = { + results: dict[str, Any] = { "resource_id": resource_id, "resource_type": resource_type, "resource_arn": resource_arn, @@ -242,7 +242,7 @@ def enable_traces_for_runtime( self, runtime_arn: str, runtime_id: str, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Enable TRACES delivery for a Runtime resource. This is a convenience method for Runtime resources where: @@ -284,7 +284,7 @@ def _setup_logs_delivery( resource_arn: str, resource_id: str, log_group_arn: str, - ) -> Dict[str, str]: + ) -> dict[str, str]: """Set up APPLICATION_LOGS delivery to CloudWatch Logs. This creates: @@ -360,7 +360,7 @@ def _setup_traces_delivery( self, resource_arn: str, resource_id: str, - ) -> Dict[str, str]: + ) -> dict[str, str]: """Set up TRACES delivery to X-Ray. This creates: @@ -427,7 +427,7 @@ def disable_observability_for_resource( self, resource_id: str, delete_log_group: bool = False, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Disable CloudWatch observability for a resource. This removes the delivery sources, destinations, and deliveries. @@ -441,7 +441,7 @@ def disable_observability_for_resource( Returns: Dict with status and list of deleted resources """ - results: Dict[str, Any] = { + results: dict[str, Any] = { "resource_id": resource_id, "deleted": [], "errors": [], @@ -493,7 +493,7 @@ def disable_observability_for_resource( def get_observability_status( self, resource_id: str, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Check the observability configuration status for a resource. Args: @@ -502,7 +502,7 @@ def get_observability_status( Returns: Dict with status information for logs and traces delivery """ - status: Dict[str, Any] = { + status: dict[str, Any] = { "resource_id": resource_id, "logs": {"configured": False}, "traces": {"configured": False}, @@ -531,10 +531,10 @@ def get_observability_status( def enable_for_memory( self, memory_id: str, - memory_arn: Optional[str] = None, + memory_arn: str | None = None, enable_logs: bool = True, enable_traces: bool = True, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Enable observability for a memory resource. Convenience method that handles ARN construction if not provided. @@ -553,10 +553,10 @@ def enable_for_memory( def enable_for_gateway( self, gateway_id: str, - gateway_arn: Optional[str] = None, + gateway_arn: str | None = None, enable_logs: bool = True, enable_traces: bool = True, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Enable observability for a gateway resource. Convenience method that handles ARN construction if not provided. @@ -576,7 +576,7 @@ def disable_for_memory( self, memory_id: str, delete_log_group: bool = False, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Disable observability for a memory resource.""" return self.disable_observability_for_resource( resource_id=memory_id, @@ -587,7 +587,7 @@ def disable_for_gateway( self, gateway_id: str, delete_log_group: bool = False, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Disable observability for a gateway resource.""" return self.disable_observability_for_resource( resource_id=gateway_id, @@ -603,7 +603,7 @@ def enable_observability_for_resource( region: str = "us-east-1", enable_logs: bool = True, enable_traces: bool = True, -) -> Dict[str, Any]: +) -> dict[str, Any]: """Enable observability for a Bedrock AgentCore resource. This is a convenience function that matches the signature from AWS documentation. diff --git a/src/bedrock_agentcore_starter_toolkit/operations/observability/formatters.py b/src/bedrock_agentcore_starter_toolkit/operations/observability/formatters.py index 3fb16487..eb2fa910 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/observability/formatters.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/observability/formatters.py @@ -1,6 +1,6 @@ """Formatting utilities for observability data display.""" -from typing import Any, Dict, Optional +from typing import Any from ..constants import GenAIAttributes, LLMAttributes, TruncationConfig @@ -223,7 +223,7 @@ def format_status_display(has_errors: bool) -> tuple[str, str]: # Attribute extraction helpers -def get_span_attribute(attributes: Dict[str, Any], *attr_names: str) -> Optional[Any]: +def get_span_attribute(attributes: dict[str, Any], *attr_names: str) -> Any | None: """Get first available attribute from a list of attribute names. Args: @@ -249,7 +249,7 @@ def get_span_attribute(attributes: Dict[str, Any], *attr_names: str) -> Optional return None -def extract_prompt(attributes: Dict[str, Any]) -> Optional[str]: +def extract_prompt(attributes: dict[str, Any]) -> str | None: """Extract prompt/user message from span attributes. Checks GenAI and LLM attribute patterns in priority order. @@ -274,7 +274,7 @@ def extract_prompt(attributes: Dict[str, Any]) -> Optional[str]: return str(value) if value is not None else None -def extract_completion(attributes: Dict[str, Any]) -> Optional[str]: +def extract_completion(attributes: dict[str, Any]) -> str | None: """Extract completion/assistant response from span attributes. Checks GenAI and LLM attribute patterns in priority order. @@ -299,7 +299,7 @@ def extract_completion(attributes: Dict[str, Any]) -> Optional[str]: return str(value) if value is not None else None -def extract_invocation_payload(attributes: Dict[str, Any]) -> Optional[str]: +def extract_invocation_payload(attributes: dict[str, Any]) -> str | None: """Extract invocation request payload from span attributes. Checks multiple GenAI attribute patterns for invocation data. @@ -324,7 +324,7 @@ def extract_invocation_payload(attributes: Dict[str, Any]) -> Optional[str]: return str(value) if value is not None else None -def extract_input_data(attributes: Dict[str, Any]) -> Optional[str]: +def extract_input_data(attributes: dict[str, Any]) -> str | None: """Extract input data from span attributes. Checks multiple GenAI attribute patterns for input data. @@ -348,7 +348,7 @@ def extract_input_data(attributes: Dict[str, Any]) -> Optional[str]: return str(value) if value is not None else None -def extract_output_data(attributes: Dict[str, Any]) -> Optional[str]: +def extract_output_data(attributes: dict[str, Any]) -> str | None: """Extract output data from span attributes. Checks multiple GenAI attribute patterns for output data. @@ -398,7 +398,7 @@ def truncate_for_display(text: str, verbose: bool = False, is_tool_use: bool = F return TruncationConfig.truncate(text, is_tool_use=is_tool_use) -def has_llm_attributes(attributes: Dict[str, Any]) -> bool: +def has_llm_attributes(attributes: dict[str, Any]) -> bool: """Check if span has any LLM-related attributes. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/operations/observability/message_parser.py b/src/bedrock_agentcore_starter_toolkit/operations/observability/message_parser.py index f6086fa2..0c9f0aaf 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/observability/message_parser.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/observability/message_parser.py @@ -9,7 +9,7 @@ """ import json -from typing import Any, Dict, List, Optional +from typing import Any from ..constants import InstrumentationScopes @@ -17,7 +17,7 @@ class UnifiedLogParser: """OpenTelemetry-based parser for runtime logs.""" - def parse(self, raw_message: Optional[Dict[str, Any]], timestamp: str) -> List[Dict[str, Any]]: + def parse(self, raw_message: dict[str, Any] | None, timestamp: str) -> list[dict[str, Any]]: """Parse structured data from an OpenTelemetry runtime log. Returns a list of items, each with a 'type' field: @@ -42,7 +42,7 @@ def parse(self, raw_message: Optional[Dict[str, Any]], timestamp: str) -> List[D # 2. Extract messages (conversations) return self._extract_messages(raw_message, timestamp) - def _extract_exception(self, raw_message: Dict[str, Any], timestamp: str) -> Optional[Dict[str, Any]]: + def _extract_exception(self, raw_message: dict[str, Any], timestamp: str) -> dict[str, Any] | None: """Extract exception from OTEL attributes. OTEL format: attributes.exception.type, attributes.exception.message, attributes.exception.stacktrace @@ -64,7 +64,7 @@ def _extract_exception(self, raw_message: Dict[str, Any], timestamp: str) -> Opt return None - def _extract_messages(self, raw_message: Dict[str, Any], timestamp: str) -> List[Dict[str, Any]]: + def _extract_messages(self, raw_message: dict[str, Any], timestamp: str) -> list[dict[str, Any]]: """Extract conversation messages using scope-based routing. Routes to appropriate extractor based on scope.name: @@ -90,7 +90,7 @@ def _extract_messages(self, raw_message: Dict[str, Any], timestamp: str) -> List # Fallback: Generic OTEL extraction return self._extract_generic_otel(raw_message, body, timestamp) - def _get_role_from_event_name(self, event_name: str) -> Optional[str]: + def _get_role_from_event_name(self, event_name: str) -> str | None: """Infer message role from OTEL gen_ai event name. OTEL convention: gen_ai.{role}.message @@ -109,7 +109,7 @@ def _get_role_from_event_name(self, event_name: str) -> Optional[str]: return None - def _extract_content(self, body: Dict[str, Any]) -> Optional[str]: + def _extract_content(self, body: dict[str, Any]) -> str | None: """Extract text content from body. OTEL GenAI format: body.content (string or array of content parts) @@ -139,8 +139,8 @@ def _extract_content(self, body: Dict[str, Any]) -> Optional[str]: return None def _extract_generic_otel( - self, raw_message: Dict[str, Any], body: Dict[str, Any], timestamp: str - ) -> List[Dict[str, Any]]: + self, raw_message: dict[str, Any], body: dict[str, Any], timestamp: str + ) -> list[dict[str, Any]]: """Extract from generic OTEL format (gen_ai events or input/output structure).""" attributes = raw_message.get("attributes", {}) event_name = attributes.get("event.name", "") if isinstance(attributes, dict) else "" @@ -164,11 +164,11 @@ def _extract_generic_otel( return [] - def _extract_from_strands(self, body: Dict[str, Any], timestamp: str) -> List[Dict[str, Any]]: + def _extract_from_strands(self, body: dict[str, Any], timestamp: str) -> list[dict[str, Any]]: """Extract from Strands instrumentation (uses standard input/output structure).""" return self._extract_from_input_output(body, timestamp) - def _extract_from_input_output(self, body: Dict[str, Any], timestamp: str) -> List[Dict[str, Any]]: + def _extract_from_input_output(self, body: dict[str, Any], timestamp: str) -> list[dict[str, Any]]: """Extract from input/output structure. Format: {"input": {"messages": [...]}, "output": {"messages": [...]}} @@ -204,7 +204,7 @@ def _extract_from_input_output(self, body: Dict[str, Any], timestamp: str) -> Li return messages - def _extract_from_langchain(self, body: Dict[str, Any], timestamp: str) -> List[Dict[str, Any]]: + def _extract_from_langchain(self, body: dict[str, Any], timestamp: str) -> list[dict[str, Any]]: """Extract from LangChain/LangGraph - parse JSON string and extract content.""" messages = [] @@ -220,7 +220,7 @@ def _extract_from_langchain(self, body: Dict[str, Any], timestamp: str) -> List[ return messages - def _parse_langchain_input(self, body: Dict[str, Any]) -> Optional[str]: + def _parse_langchain_input(self, body: dict[str, Any]) -> str | None: """Parse LangChain input message.""" try: input_data = body.get("input", {}).get("messages", []) @@ -237,7 +237,7 @@ def _parse_langchain_input(self, body: Dict[str, Any]) -> Optional[str]: except (json.JSONDecodeError, KeyError, IndexError, AttributeError): return None - def _parse_langchain_output(self, body: Dict[str, Any]) -> Optional[str]: + def _parse_langchain_output(self, body: dict[str, Any]) -> str | None: """Parse LangChain output message with tool calls.""" try: output_data = body.get("output", {}).get("messages", []) @@ -270,7 +270,7 @@ def _parse_langchain_output(self, body: Dict[str, Any]) -> Optional[str]: except (json.JSONDecodeError, KeyError, IndexError, AttributeError): return None - def _format_langchain_content(self, content: Any, tool_calls: list) -> Optional[str]: + def _format_langchain_content(self, content: Any, tool_calls: list) -> str | None: """Format LangChain content (string or list) with tool calls.""" parts = [] @@ -291,7 +291,7 @@ def _format_langchain_content(self, content: Any, tool_calls: list) -> Optional[ return "\n".join(parts) if parts else None - def _extract_text_from_array(self, content: list) -> Optional[str]: + def _extract_text_from_array(self, content: list) -> str | None: """Extract text from array of content parts (OTEL multimodal format).""" text_parts = [] for item in content: diff --git a/src/bedrock_agentcore_starter_toolkit/operations/observability/telemetry.py b/src/bedrock_agentcore_starter_toolkit/operations/observability/telemetry.py index a451555d..8d4f2c48 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/observability/telemetry.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/observability/telemetry.py @@ -4,7 +4,7 @@ """ from dataclasses import dataclass, field -from typing import Any, Dict, List, Optional +from typing import Any @dataclass @@ -14,23 +14,23 @@ class Span: trace_id: str span_id: str span_name: str - session_id: Optional[str] = None - start_time_unix_nano: Optional[int] = None - end_time_unix_nano: Optional[int] = None - duration_ms: Optional[float] = None - status_code: Optional[str] = None - status_message: Optional[str] = None - parent_span_id: Optional[str] = None - kind: Optional[str] = None - events: List[Dict[str, Any]] = field(default_factory=list) - attributes: Dict[str, Any] = field(default_factory=dict) - resource_attributes: Dict[str, Any] = field(default_factory=dict) - service_name: Optional[str] = None - resource_id: Optional[str] = None - service_type: Optional[str] = None - timestamp: Optional[str] = None - raw_message: Optional[Dict[str, Any]] = None - children: List["Span"] = field(default_factory=list, repr=False) + session_id: str | None = None + start_time_unix_nano: int | None = None + end_time_unix_nano: int | None = None + duration_ms: float | None = None + status_code: str | None = None + status_message: str | None = None + parent_span_id: str | None = None + kind: str | None = None + events: list[dict[str, Any]] = field(default_factory=list) + attributes: dict[str, Any] = field(default_factory=dict) + resource_attributes: dict[str, Any] = field(default_factory=dict) + service_name: str | None = None + resource_id: str | None = None + service_type: str | None = None + timestamp: str | None = None + raw_message: dict[str, Any] | None = None + children: list["Span"] = field(default_factory=list, repr=False) @dataclass @@ -39,20 +39,20 @@ class RuntimeLog: timestamp: str message: str - span_id: Optional[str] = None - trace_id: Optional[str] = None - log_stream: Optional[str] = None - raw_message: Optional[Dict[str, Any]] = None + span_id: str | None = None + trace_id: str | None = None + log_stream: str | None = None + raw_message: dict[str, Any] | None = None @dataclass class TraceData: """Complete trace/session data including spans and runtime logs.""" - session_id: Optional[str] = None - agent_id: Optional[str] = None - spans: List[Span] = field(default_factory=list) - runtime_logs: List[RuntimeLog] = field(default_factory=list) - traces: Dict[str, List[Span]] = field(default_factory=dict) - start_time: Optional[int] = None - end_time: Optional[int] = None + session_id: str | None = None + agent_id: str | None = None + spans: list[Span] = field(default_factory=list) + runtime_logs: list[RuntimeLog] = field(default_factory=list) + traces: dict[str, list[Span]] = field(default_factory=dict) + start_time: int | None = None + end_time: int | None = None diff --git a/src/bedrock_agentcore_starter_toolkit/operations/observability/trace_processor.py b/src/bedrock_agentcore_starter_toolkit/operations/observability/trace_processor.py index 2ee2c376..ff7949e5 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/observability/trace_processor.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/observability/trace_processor.py @@ -3,7 +3,7 @@ This module contains all business logic for processing TraceData, Spans, and RuntimeLogs. """ -from typing import Any, Dict, List +from typing import Any from .message_parser import UnifiedLogParser from .telemetry import RuntimeLog, Span, TraceData @@ -29,7 +29,7 @@ def group_spans_by_trace(trace_data: TraceData) -> None: trace_data.traces[trace_id].sort(key=lambda s: s.start_time_unix_nano or 0) @staticmethod - def build_span_hierarchy(trace_data: TraceData, trace_id: str) -> List[Span]: + def build_span_hierarchy(trace_data: TraceData, trace_id: str) -> list[Span]: """Build hierarchical structure of spans for a trace. Args: @@ -46,8 +46,8 @@ def build_span_hierarchy(trace_data: TraceData, trace_id: str) -> List[Span]: span_map = {span.span_id: span for span in trace_data.traces[trace_id]} # Build children map and root spans list - children_map: Dict[str, List[Span]] = {} - root_spans: List[Span] = [] + children_map: dict[str, list[Span]] = {} + root_spans: list[Span] = [] for span in trace_data.traces[trace_id]: parent_id = span.parent_span_id @@ -66,14 +66,14 @@ def build_span_hierarchy(trace_data: TraceData, trace_id: str) -> List[Span]: return root_spans @staticmethod - def get_messages_by_span(trace_data: TraceData) -> Dict[str, List[Dict[str, Any]]]: + def get_messages_by_span(trace_data: TraceData) -> dict[str, list[dict[str, Any]]]: """Extract messages and exceptions from runtime logs grouped by span ID. Returns: Dictionary mapping span_id to list of items (messages/exceptions) """ parser = UnifiedLogParser() - items_by_span: Dict[str, List[Dict[str, Any]]] = {} + items_by_span: dict[str, list[dict[str, Any]]] = {} for log in trace_data.runtime_logs: if not log.span_id: @@ -91,7 +91,7 @@ def get_messages_by_span(trace_data: TraceData) -> Dict[str, List[Dict[str, Any] return items_by_span @staticmethod - def calculate_trace_duration(spans: List[Span]) -> float: + def calculate_trace_duration(spans: list[Span]) -> float: """Calculate trace duration from earliest start to latest end time. Args: @@ -112,7 +112,7 @@ def calculate_trace_duration(spans: List[Span]) -> float: return sum(s.duration_ms or 0 for s in root_spans) @staticmethod - def count_error_spans(spans: List[Span]) -> int: + def count_error_spans(spans: list[Span]) -> int: """Count number of spans with ERROR status. Args: @@ -124,7 +124,7 @@ def count_error_spans(spans: List[Span]) -> int: return sum(1 for span in spans if span.status_code == "ERROR") @staticmethod - def get_trace_ids(trace_data: TraceData) -> List[str]: + def get_trace_ids(trace_data: TraceData) -> list[str]: """Get all unique trace IDs from spans. Args: @@ -136,7 +136,7 @@ def get_trace_ids(trace_data: TraceData) -> List[str]: return list(set(span.trace_id for span in trace_data.spans if span.trace_id)) @staticmethod - def filter_error_traces(trace_data: TraceData) -> Dict[str, List[Span]]: + def filter_error_traces(trace_data: TraceData) -> dict[str, list[Span]]: """Filter traces to only those containing errors. Args: @@ -201,7 +201,7 @@ def get_trace_messages(trace_data: TraceData, trace_id: str) -> tuple[str, str]: return input_text, output_text @staticmethod - def to_dict(trace_data: TraceData) -> Dict[str, Any]: + def to_dict(trace_data: TraceData) -> dict[str, Any]: """Export complete trace data to dictionary for JSON serialization. Args: @@ -212,7 +212,7 @@ def to_dict(trace_data: TraceData) -> Dict[str, Any]: """ parser = UnifiedLogParser() - def span_to_dict(span: Span) -> Dict[str, Any]: + def span_to_dict(span: Span) -> dict[str, Any]: """Convert span to dictionary recursively.""" return { "trace_id": span.trace_id, @@ -236,7 +236,7 @@ def span_to_dict(span: Span) -> Dict[str, Any]: "children": [span_to_dict(child) for child in span.children], } - def log_to_dict(log: RuntimeLog) -> Dict[str, Any]: + def log_to_dict(log: RuntimeLog) -> dict[str, Any]: """Convert log to dictionary with parsed content.""" result = { "timestamp": log.timestamp, diff --git a/src/bedrock_agentcore_starter_toolkit/operations/observability/trace_visualizer.py b/src/bedrock_agentcore_starter_toolkit/operations/observability/trace_visualizer.py index 8453670b..1463be5a 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/observability/trace_visualizer.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/observability/trace_visualizer.py @@ -1,6 +1,6 @@ """Trace visualization with hierarchical tree views.""" -from typing import Any, Dict, List, Optional +from typing import Any from rich.console import Console from rich.text import Text @@ -26,7 +26,7 @@ class TraceVisualizer: """Visualizer for displaying traces in an intuitive hierarchical format.""" - def __init__(self, console: Optional[Console] = None): + def __init__(self, console: Console | None = None): """Initialize the trace visualizer. Args: @@ -113,7 +113,7 @@ def visualize_all_traces( self.visualize_trace(trace_data, trace_id, show_details, show_messages, verbose) self.console.print() # Empty line between traces - def _format_trace_header(self, trace_id: str, spans: List[Span]) -> Text: + def _format_trace_header(self, trace_id: str, spans: list[Span]) -> Text: """Format the trace header with summary information. Args: @@ -143,7 +143,7 @@ def _has_meaningful_data( self, span: Span, show_messages: bool, - messages_by_span: Dict[str, List[Dict[str, Any]]], + messages_by_span: dict[str, list[dict[str, Any]]], ) -> bool: """Check if a span has meaningful data worth showing in non-verbose mode. @@ -211,7 +211,7 @@ def _add_span_to_tree( span: Span, show_details: bool, show_messages: bool, - messages_by_span: Dict[str, List[Dict[str, Any]]], + messages_by_span: dict[str, list[dict[str, Any]]], seen_messages: set, verbose: bool, ) -> None: @@ -251,7 +251,7 @@ def _format_span( span: Span, show_details: bool, show_messages: bool, - messages_by_span: Dict[str, List[Dict[str, Any]]], + messages_by_span: dict[str, list[dict[str, Any]]], seen_messages: set, verbose: bool = False, ) -> Text: @@ -409,7 +409,7 @@ def _format_span( return text - def _get_message_id(self, item: Dict[str, Any]) -> str: + def _get_message_id(self, item: dict[str, Any]) -> str: """Create a unique identifier for a message/event/exception for deduplication. Args: @@ -438,7 +438,7 @@ def _get_message_id(self, item: Dict[str, Any]) -> str: return f"{item_type}_{timestamp}_{hash(str(item))}" - def _is_generic_wrapper_event(self, event_name: str, payload: Dict[str, Any]) -> bool: + def _is_generic_wrapper_event(self, event_name: str, payload: dict[str, Any]) -> bool: """Check if an event is a generic wrapper that doesn't add new information. Args: @@ -468,7 +468,7 @@ def _is_generic_wrapper_event(self, event_name: str, payload: Dict[str, Any]) -> return False - def _format_event_payload_display(self, text: Text, payload: Dict[str, Any], verbose: bool = False) -> None: + def _format_event_payload_display(self, text: Text, payload: dict[str, Any], verbose: bool = False) -> None: """Format event payload for display in a more readable way. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/operations/policy/client.py b/src/bedrock_agentcore_starter_toolkit/operations/policy/client.py index fd4d700c..ecb772f8 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/policy/client.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/policy/client.py @@ -2,7 +2,7 @@ import logging import time -from typing import Any, Dict, Optional +from typing import Any import boto3 @@ -28,7 +28,7 @@ class PolicyClient: and policy generation operations. """ - def __init__(self, region_name: Optional[str] = None): + def __init__(self, region_name: str | None = None): """Initialize the Policy client. Args: @@ -54,9 +54,9 @@ def __init__(self, region_name: Optional[str] = None): def create_policy_engine( self, name: str, - description: Optional[str] = None, - client_token: Optional[str] = None, - ) -> Dict[str, Any]: + description: str | None = None, + client_token: str | None = None, + ) -> dict[str, Any]: """Create a new policy engine. Args: @@ -86,9 +86,9 @@ def create_policy_engine( def create_or_get_policy_engine( self, name: str, - description: Optional[str] = None, - client_token: Optional[str] = None, - ) -> Dict[str, Any]: + description: str | None = None, + client_token: str | None = None, + ) -> dict[str, Any]: """Create a new policy engine or get existing one with the same name. This method is idempotent - it will reuse existing policy engines with @@ -181,7 +181,7 @@ def create_or_get_policy_engine( raise raise - def get_policy_engine(self, policy_engine_id: str) -> Dict[str, Any]: + def get_policy_engine(self, policy_engine_id: str) -> dict[str, Any]: """Get policy engine details. Args: @@ -201,8 +201,8 @@ def get_policy_engine(self, policy_engine_id: str) -> Dict[str, Any]: def update_policy_engine( self, policy_engine_id: str, - description: Optional[str] = None, - ) -> Dict[str, Any]: + description: str | None = None, + ) -> dict[str, Any]: """Update a policy engine. Args: @@ -230,9 +230,9 @@ def update_policy_engine( def list_policy_engines( self, - max_results: Optional[int] = None, - next_token: Optional[str] = None, - ) -> Dict[str, Any]: + max_results: int | None = None, + next_token: str | None = None, + ) -> dict[str, Any]: """List policy engines. Args: @@ -254,7 +254,7 @@ def list_policy_engines( except Exception as e: raise PolicySetupException(f"Failed to list policy engines: {e}") from e - def delete_policy_engine(self, policy_engine_id: str) -> Dict[str, Any]: + def delete_policy_engine(self, policy_engine_id: str) -> dict[str, Any]: """Delete a policy engine. Args: @@ -280,11 +280,11 @@ def create_policy( self, policy_engine_id: str, name: str, - definition: Dict[str, Any], - description: Optional[str] = None, - validation_mode: Optional[str] = None, - client_token: Optional[str] = None, - ) -> Dict[str, Any]: + definition: dict[str, Any], + description: str | None = None, + validation_mode: str | None = None, + client_token: str | None = None, + ) -> dict[str, Any]: """Create a new policy. Args: @@ -326,11 +326,11 @@ def create_or_get_policy( self, policy_engine_id: str, name: str, - definition: Dict[str, Any], - description: Optional[str] = None, - validation_mode: Optional[str] = None, - client_token: Optional[str] = None, - ) -> Dict[str, Any]: + definition: dict[str, Any], + description: str | None = None, + validation_mode: str | None = None, + client_token: str | None = None, + ) -> dict[str, Any]: """Create a new policy or get existing one with the same name. This method is idempotent - it will reuse existing policies with @@ -433,7 +433,7 @@ def create_or_get_policy( raise raise - def get_policy(self, policy_engine_id: str, policy_id: str) -> Dict[str, Any]: + def get_policy(self, policy_engine_id: str, policy_id: str) -> dict[str, Any]: """Get policy details. Args: @@ -458,10 +458,10 @@ def update_policy( self, policy_engine_id: str, policy_id: str, - definition: Dict[str, Any], - description: Optional[str] = None, - validation_mode: Optional[str] = None, - ) -> Dict[str, Any]: + definition: dict[str, Any], + description: str | None = None, + validation_mode: str | None = None, + ) -> dict[str, Any]: """Update a policy. Args: @@ -499,10 +499,10 @@ def update_policy( def list_policies( self, policy_engine_id: str, - target_resource_scope: Optional[str] = None, - max_results: Optional[int] = None, - next_token: Optional[str] = None, - ) -> Dict[str, Any]: + target_resource_scope: str | None = None, + max_results: int | None = None, + next_token: str | None = None, + ) -> dict[str, Any]: """List policies. Args: @@ -531,7 +531,7 @@ def list_policies( except Exception as e: raise PolicySetupException(f"Failed to list policies: {e}") from e - def delete_policy(self, policy_engine_id: str, policy_id: str) -> Dict[str, Any]: + def delete_policy(self, policy_engine_id: str, policy_id: str) -> dict[str, Any]: """Delete a policy. Args: @@ -561,10 +561,10 @@ def start_policy_generation( self, policy_engine_id: str, name: str, - resource: Dict[str, Any], - content: Dict[str, Any], - client_token: Optional[str] = None, - ) -> Dict[str, Any]: + resource: dict[str, Any], + content: dict[str, Any], + client_token: str | None = None, + ) -> dict[str, Any]: """Start a policy generation. Args: @@ -602,7 +602,7 @@ def get_policy_generation( self, policy_engine_id: str, policy_generation_id: str, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Get policy generation details. Args: @@ -627,9 +627,9 @@ def list_policy_generation_assets( self, policy_engine_id: str, policy_generation_id: str, - max_results: Optional[int] = None, - next_token: Optional[str] = None, - ) -> Dict[str, Any]: + max_results: int | None = None, + next_token: str | None = None, + ) -> dict[str, Any]: """Get policy generation assets (generated policies). Args: @@ -662,9 +662,9 @@ def list_policy_generation_assets( def list_policy_generations( self, policy_engine_id: str, - max_results: Optional[int] = None, - next_token: Optional[str] = None, - ) -> Dict[str, Any]: + max_results: int | None = None, + next_token: str | None = None, + ) -> dict[str, Any]: """List policy generations. Args: @@ -694,13 +694,13 @@ def generate_policy( self, policy_engine_id: str, name: str, - resource: Dict[str, Any], - content: Dict[str, Any], - client_token: Optional[str] = None, + resource: dict[str, Any], + content: dict[str, Any], + client_token: str | None = None, max_attempts: int = DEFAULT_MAX_ATTEMPTS, delay: int = DEFAULT_POLL_DELAY, fetch_assets: bool = False, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Generate Cedar policies from natural language and wait for completion. This is a convenience method that combines start_policy_generation() @@ -787,7 +787,7 @@ def _wait_for_policy_engine_active( policy_engine_id: str, max_attempts: int = DEFAULT_MAX_ATTEMPTS, delay: int = DEFAULT_POLL_DELAY, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Wait for a policy engine to become active. Args: @@ -822,7 +822,7 @@ def _wait_for_policy_active( policy_id: str, max_attempts: int = DEFAULT_MAX_ATTEMPTS, delay: int = DEFAULT_POLL_DELAY, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Wait for a policy to become active. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/operations/runtime/configure.py b/src/bedrock_agentcore_starter_toolkit/operations/runtime/configure.py index bae04741..c4b38680 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/runtime/configure.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/runtime/configure.py @@ -4,7 +4,7 @@ import os import re from pathlib import Path -from typing import Any, Dict, List, Literal, Optional, Tuple +from typing import Any, Literal import boto3 @@ -31,7 +31,7 @@ log = logging.getLogger(__name__) -def get_relative_path(path: Path, base: Optional[Path] = None) -> str: +def get_relative_path(path: Path, base: Path | None = None) -> str: """Convert path to relative format with OS-native separators. Args: @@ -61,7 +61,7 @@ def get_relative_path(path: Path, base: Optional[Path] = None) -> str: return str(path_obj) -def detect_entrypoint(source_path: Path) -> List[Path]: +def detect_entrypoint(source_path: Path) -> list[Path]: """Detect entrypoint files in source directory. Args: @@ -109,7 +109,7 @@ def detect_requirements(source_path: Path): return deps -def infer_agent_name(entrypoint_path: Path, base: Optional[Path] = None) -> str: +def infer_agent_name(entrypoint_path: Path, base: Path | None = None) -> str: """Infer agent name from entrypoint path. Args: @@ -138,34 +138,34 @@ def configure_bedrock_agentcore( agent_name: str, entrypoint_path: Path, create_mode_enabled: bool = False, - execution_role: Optional[str] = None, - code_build_execution_role: Optional[str] = None, - ecr_repository: Optional[str] = None, - s3_path: Optional[str] = None, - container_runtime: Optional[str] = None, + execution_role: str | None = None, + code_build_execution_role: str | None = None, + ecr_repository: str | None = None, + s3_path: str | None = None, + container_runtime: str | None = None, auto_create_ecr: bool = True, auto_create_s3: bool = True, auto_create_execution_role: bool = True, enable_observability: bool = True, memory_mode: Literal["NO_MEMORY", "STM_ONLY", "STM_AND_LTM"] = "NO_MEMORY", - requirements_file: Optional[str] = None, - authorizer_configuration: Optional[Dict[str, Any]] = None, - request_header_configuration: Optional[Dict[str, Any]] = None, + requirements_file: str | None = None, + authorizer_configuration: dict[str, Any] | None = None, + request_header_configuration: dict[str, Any] | None = None, verbose: bool = False, - region: Optional[str] = None, - protocol: Optional[str] = None, + region: str | None = None, + protocol: str | None = None, non_interactive: bool = False, - source_path: Optional[str] = None, + source_path: str | None = None, vpc_enabled: bool = False, - vpc_subnets: Optional[List[str]] = None, - vpc_security_groups: Optional[List[str]] = None, - idle_timeout: Optional[int] = None, - max_lifetime: Optional[int] = None, + vpc_subnets: list[str] | None = None, + vpc_security_groups: list[str] | None = None, + idle_timeout: int | None = None, + max_lifetime: int | None = None, deployment_type: str = "direct_code_deploy", - runtime_type: Optional[str] = None, + runtime_type: str | None = None, is_generated_by_agentcore_create: bool = False, language: str = "python", - node_version: Optional[str] = None, + node_version: str | None = None, ) -> ConfigureResult: """Configure Bedrock AgentCore application with deployment settings. @@ -626,7 +626,7 @@ def configure_bedrock_agentcore( ) -def validate_agent_name(name: str) -> Tuple[bool, str]: +def validate_agent_name(name: str) -> tuple[bool, str]: """Check if name matches the pattern [a-zA-Z][a-zA-Z0-9_]{0,47}. This pattern requires: diff --git a/src/bedrock_agentcore_starter_toolkit/operations/runtime/create_role.py b/src/bedrock_agentcore_starter_toolkit/operations/runtime/create_role.py index 35980843..451ed90b 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/runtime/create_role.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/runtime/create_role.py @@ -3,7 +3,7 @@ import hashlib import json import logging -from typing import Any, Optional +from typing import Any from boto3 import Session from botocore.client import BaseClient @@ -16,7 +16,7 @@ ) -def _extract_ecr_repository_name(ecr_uri: Optional[str]) -> Optional[str]: +def _extract_ecr_repository_name(ecr_uri: str | None) -> str | None: """Extract repository name from ECR URI. Args: @@ -61,8 +61,8 @@ def get_or_create_runtime_execution_role( region: str, account_id: str, agent_name: str, - role_name: Optional[str] = None, - agent_config: Optional[Any] = None, + role_name: str | None = None, + agent_config: Any | None = None, ) -> str: """Get existing execution role or create a new one (idempotent). diff --git a/src/bedrock_agentcore_starter_toolkit/operations/runtime/destroy.py b/src/bedrock_agentcore_starter_toolkit/operations/runtime/destroy.py index 9faf4cb4..28c72267 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/runtime/destroy.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/runtime/destroy.py @@ -2,7 +2,6 @@ import logging from pathlib import Path -from typing import Optional import boto3 from botocore.exceptions import ClientError @@ -19,7 +18,7 @@ def destroy_bedrock_agentcore( config_path: Path, - agent_name: Optional[str] = None, + agent_name: str | None = None, dry_run: bool = False, force: bool = False, delete_ecr_repo: bool = False, diff --git a/src/bedrock_agentcore_starter_toolkit/operations/runtime/exceptions.py b/src/bedrock_agentcore_starter_toolkit/operations/runtime/exceptions.py index 9c57de91..5791e94a 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/runtime/exceptions.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/runtime/exceptions.py @@ -1,7 +1,5 @@ """Exceptions for the Bedrock AgentCore Runtime module.""" -from typing import List, Optional - class RuntimeException(Exception): """Base exception for all Runtime SDK errors.""" @@ -12,7 +10,7 @@ class RuntimeException(Exception): class RuntimeToolkitException(RuntimeException): """Raised when runtime operations fail with resource tracking.""" - def __init__(self, message: str, created_resources: Optional[List[str]] = None): + def __init__(self, message: str, created_resources: list[str] | None = None): """Initialize RuntimeToolkitException with optional resource tracking. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/operations/runtime/invoke.py b/src/bedrock_agentcore_starter_toolkit/operations/runtime/invoke.py index 0b12e345..9553aebc 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/runtime/invoke.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/runtime/invoke.py @@ -3,7 +3,7 @@ import json import logging from pathlib import Path -from typing import Any, Optional +from typing import Any from bedrock_agentcore.services.identity import IdentityClient @@ -25,12 +25,12 @@ def invoke_bedrock_agentcore( config_path: Path, payload: Any, - agent_name: Optional[str] = None, - session_id: Optional[str] = None, - bearer_token: Optional[str] = None, - user_id: Optional[str] = None, - local_mode: Optional[bool] = False, - custom_headers: Optional[dict] = None, + agent_name: str | None = None, + session_id: str | None = None, + bearer_token: str | None = None, + user_id: str | None = None, + local_mode: bool | None = False, + custom_headers: dict | None = None, ) -> InvokeResult: """Invoke deployed Bedrock AgentCore endpoint.""" # Load project configuration diff --git a/src/bedrock_agentcore_starter_toolkit/operations/runtime/launch.py b/src/bedrock_agentcore_starter_toolkit/operations/runtime/launch.py index abaffdd0..dc4d9f23 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/runtime/launch.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/runtime/launch.py @@ -5,7 +5,6 @@ import time import urllib.parse from pathlib import Path -from typing import List, Optional import boto3 from botocore.exceptions import ClientError @@ -177,7 +176,7 @@ def _ensure_identity_permissions( agent_config: BedrockAgentCoreAgentSchema, region: str, account_id: str, - console: Optional[Console] = None, + console: Console | None = None, ) -> None: """Add Identity service permissions to execution role if credential providers configured.""" if not agent_config.identity or not agent_config.identity.is_enabled: @@ -223,7 +222,7 @@ def _ensure_aws_jwt_permissions( agent_config: BedrockAgentCoreAgentSchema, region: str, account_id: str, - console: Optional[Console] = None, + console: Console | None = None, ) -> None: """Add AWS IAM JWT (STS:GetWebIdentityToken) permissions to execution role if configured.""" # Check if AWS JWT is configured @@ -348,8 +347,8 @@ def _ensure_memory_for_agent( project_config: BedrockAgentCoreConfigSchema, config_path: Path, agent_name: str, - console: Optional[Console] = None, -) -> Optional[str]: + console: Console | None = None, +) -> str | None: """Ensure memory resource exists for agent. Returns memory_id or None. This function is idempotent - it creates memory if needed or reuses existing. @@ -520,7 +519,7 @@ def _deploy_to_bedrock_agentcore( ecr_uri: str, region: str, account_id: str, - env_vars: Optional[dict] = None, + env_vars: dict | None = None, auto_update_on_conflict: bool = False, ): """Deploy agent to Bedrock AgentCore with retry logic for role validation.""" @@ -655,7 +654,7 @@ def _deploy_to_bedrock_agentcore( return agent_id, agent_arn -def _check_vpc_deployment(session: boto3.Session, agent_id: str, vpc_subnets: List[str], region: str) -> None: +def _check_vpc_deployment(session: boto3.Session, agent_id: str, vpc_subnets: list[str], region: str) -> None: """Verify VPC deployment created ENIs in the specified subnets.""" ec2_client = session.client("ec2", region_name=region) @@ -688,7 +687,7 @@ def _check_vpc_deployment(session: boto3.Session, agent_id: str, vpc_subnets: Li def _launch_direct_code_deploy_local( agent_config: BedrockAgentCoreAgentSchema, - env_vars: Optional[dict], + env_vars: dict | None, ) -> LaunchResult: """Prepare for local direct_code_deploy execution using uv python.""" import shutil @@ -744,14 +743,14 @@ def _launch_direct_code_deploy_local( def launch_bedrock_agentcore( config_path: Path, - agent_name: Optional[str] = None, + agent_name: str | None = None, local: bool = False, use_codebuild: bool = True, - env_vars: Optional[dict] = None, + env_vars: dict | None = None, auto_update_on_conflict: bool = False, - console: Optional[Console] = None, + console: Console | None = None, force_rebuild_deps: bool = False, - image_tag: Optional[str] = None, + image_tag: str | None = None, ) -> LaunchResult: """Launch Bedrock AgentCore locally or to cloud. @@ -995,8 +994,8 @@ def _execute_codebuild_workflow( project_config, ecr_only: bool = False, auto_update_on_conflict: bool = False, - env_vars: Optional[dict] = None, - image_tag: Optional[str] = None, + env_vars: dict | None = None, + image_tag: str | None = None, ) -> LaunchResult: """Launch using CodeBuild for ARM64 builds.""" log.info( @@ -1123,9 +1122,9 @@ def _launch_with_codebuild( agent_config, project_config, auto_update_on_conflict: bool = False, - env_vars: Optional[dict] = None, - console: Optional[Console] = None, - image_tag: Optional[str] = None, + env_vars: dict | None = None, + console: Console | None = None, + image_tag: str | None = None, ) -> LaunchResult: """Launch using CodeBuild for ARM64 builds.""" if console is None: @@ -1175,7 +1174,7 @@ def _launch_with_direct_code_deploy( agent_config: BedrockAgentCoreAgentSchema, project_config: BedrockAgentCoreConfigSchema, auto_update_on_conflict: bool, - env_vars: Optional[dict], + env_vars: dict | None, force_rebuild_deps: bool = False, ) -> LaunchResult: """Deploy using code zip artifact (Lambda-style deployment). diff --git a/src/bedrock_agentcore_starter_toolkit/operations/runtime/models.py b/src/bedrock_agentcore_starter_toolkit/operations/runtime/models.py index b7606c36..381b2f7b 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/runtime/models.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/runtime/models.py @@ -1,7 +1,7 @@ """Pydantic models for operation requests and responses.""" from pathlib import Path -from typing import Any, Dict, List, Optional +from typing import Any from pydantic import BaseModel, ConfigDict, Field @@ -13,22 +13,22 @@ class ConfigureResult(BaseModel): """Result of configure operation.""" config_path: Path = Field(..., description="Path to configuration file") - dockerfile_path: Optional[Path] = Field(None, description="Path to generated Dockerfile") - dockerignore_path: Optional[Path] = Field(None, description="Path to generated .dockerignore") - runtime: Optional[str] = Field(None, description="Container runtime name") - runtime_type: Optional[str] = Field(None, description="Python runtime version for direct_code_deploy") + dockerfile_path: Path | None = Field(None, description="Path to generated Dockerfile") + dockerignore_path: Path | None = Field(None, description="Path to generated .dockerignore") + runtime: str | None = Field(None, description="Container runtime name") + runtime_type: str | None = Field(None, description="Python runtime version for direct_code_deploy") region: str = Field(..., description="AWS region") account_id: str = Field(..., description="AWS account ID") - execution_role: Optional[str] = Field(None, description="AWS execution role ARN") - ecr_repository: Optional[str] = Field(None, description="ECR repository URI") + execution_role: str | None = Field(None, description="AWS execution role ARN") + ecr_repository: str | None = Field(None, description="ECR repository URI") auto_create_ecr: bool = Field(False, description="Whether ECR will be auto-created") - s3_path: Optional[str] = Field(None, description="S3 URI") + s3_path: str | None = Field(None, description="S3 URI") auto_create_s3: bool = Field(False, description="Whether S3 bucket will be auto-created") - memory_id: Optional[str] = Field(default=None, description="Memory resource ID if created") - network_mode: Optional[str] = Field(None, description="Network mode (PUBLIC or VPC)") - network_subnets: Optional[List[str]] = Field(None, description="VPC subnet IDs") - network_security_groups: Optional[List[str]] = Field(None, description="VPC security group IDs") - network_vpc_id: Optional[str] = Field(None, description="VPC ID") + memory_id: str | None = Field(default=None, description="Memory resource ID if created") + network_mode: str | None = Field(None, description="Network mode (PUBLIC or VPC)") + network_subnets: list[str] | None = Field(None, description="VPC subnet IDs") + network_security_groups: list[str] | None = Field(None, description="VPC security group IDs") + network_vpc_id: str | None = Field(None, description="VPC ID") # Launch operation models @@ -36,27 +36,25 @@ class LaunchResult(BaseModel): """Result of launch operation.""" mode: str = Field(..., description="Launch mode: local, cloud, or codebuild") - tag: Optional[str] = Field( + tag: str | None = Field( default=None, description="Versioned Docker image tag (e.g., 20260108-120435-123 or custom tag)" ) - env_vars: Optional[Dict[str, str]] = Field(default=None, description="Environment variables for local deployment") + env_vars: dict[str, str] | None = Field(default=None, description="Environment variables for local deployment") # Local mode fields - port: Optional[int] = Field(default=None, description="Port for local deployment") - runtime: Optional[ContainerRuntime] = Field(default=None, description="Container runtime instance") + port: int | None = Field(default=None, description="Port for local deployment") + runtime: ContainerRuntime | None = Field(default=None, description="Container runtime instance") # Cloud mode fields - ecr_uri: Optional[str] = Field( - default=None, description="Versioned ECR image URI (e.g., {repo}:20260108-120435-123)" - ) - agent_id: Optional[str] = Field(default=None, description="BedrockAgentCore agent ID") - agent_arn: Optional[str] = Field(default=None, description="BedrockAgentCore agent ARN") + ecr_uri: str | None = Field(default=None, description="Versioned ECR image URI (e.g., {repo}:20260108-120435-123)") + agent_id: str | None = Field(default=None, description="BedrockAgentCore agent ID") + agent_arn: str | None = Field(default=None, description="BedrockAgentCore agent ARN") # CodeBuild mode fields - codebuild_id: Optional[str] = Field(default=None, description="CodeBuild build ID for ARM64 builds") + codebuild_id: str | None = Field(default=None, description="CodeBuild build ID for ARM64 builds") # Build output (optional) - build_output: Optional[List[str]] = Field(default=None, description="Docker build output") + build_output: list[str] | None = Field(default=None, description="Docker build output") model_config = ConfigDict(arbitrary_types_allowed=True) # For runtime field @@ -64,9 +62,9 @@ class LaunchResult(BaseModel): class InvokeResult(BaseModel): """Result of invoke operation.""" - response: Dict[str, Any] = Field(..., description="Response from Bedrock AgentCore endpoint") + response: dict[str, Any] = Field(..., description="Response from Bedrock AgentCore endpoint") session_id: str = Field(..., description="Session ID used for invocation") - agent_arn: Optional[str] = Field(default=None, description="BedrockAgentCore agent ARN") + agent_arn: str | None = Field(default=None, description="BedrockAgentCore agent ARN") # Status operation models @@ -75,41 +73,41 @@ class StatusConfigInfo(BaseModel): name: str = Field(..., description="Bedrock AgentCore application name") entrypoint: str = Field(..., description="Entrypoint file path") - region: Optional[str] = Field(None, description="AWS region") - account: Optional[str] = Field(None, description="AWS account ID") - execution_role: Optional[str] = Field(None, description="AWS execution role ARN") - ecr_repository: Optional[str] = Field(None, description="ECR repository URI") - agent_id: Optional[str] = Field(None, description="BedrockAgentCore agent ID") - agent_arn: Optional[str] = Field(None, description="BedrockAgentCore agent ARN") - network_mode: Optional[str] = None - network_subnets: Optional[List[str]] = None - network_security_groups: Optional[List[str]] = None - network_vpc_id: Optional[str] = None - memory_id: Optional[str] = Field(None, description="Memory resource ID") - memory_status: Optional[str] = Field(None, description="Memory provisioning status (CREATING/ACTIVE/FAILED)") - memory_type: Optional[str] = Field(None, description="Memory type (STM or STM+LTM)") - memory_enabled: Optional[bool] = Field(None, description="Whether memory is enabled") - memory_strategies: Optional[List[str]] = Field(None, description="Active memory strategies") - memory_details: Optional[Dict[str, Any]] = Field(None, description="Detailed memory resource information") - idle_timeout: Optional[int] = Field(None, description="Idle runtime session timeout in seconds") - max_lifetime: Optional[int] = Field(None, description="Maximum instance lifetime in seconds") + region: str | None = Field(None, description="AWS region") + account: str | None = Field(None, description="AWS account ID") + execution_role: str | None = Field(None, description="AWS execution role ARN") + ecr_repository: str | None = Field(None, description="ECR repository URI") + agent_id: str | None = Field(None, description="BedrockAgentCore agent ID") + agent_arn: str | None = Field(None, description="BedrockAgentCore agent ARN") + network_mode: str | None = None + network_subnets: list[str] | None = None + network_security_groups: list[str] | None = None + network_vpc_id: str | None = None + memory_id: str | None = Field(None, description="Memory resource ID") + memory_status: str | None = Field(None, description="Memory provisioning status (CREATING/ACTIVE/FAILED)") + memory_type: str | None = Field(None, description="Memory type (STM or STM+LTM)") + memory_enabled: bool | None = Field(None, description="Whether memory is enabled") + memory_strategies: list[str] | None = Field(None, description="Active memory strategies") + memory_details: dict[str, Any] | None = Field(None, description="Detailed memory resource information") + idle_timeout: int | None = Field(None, description="Idle runtime session timeout in seconds") + max_lifetime: int | None = Field(None, description="Maximum instance lifetime in seconds") class StatusResult(BaseModel): """Result of status operation.""" config: StatusConfigInfo = Field(..., description="Configuration information") - agent: Optional[Dict[str, Any]] = Field(None, description="Agent runtime details or error") - endpoint: Optional[Dict[str, Any]] = Field(None, description="Endpoint details or error") + agent: dict[str, Any] | None = Field(None, description="Agent runtime details or error") + endpoint: dict[str, Any] | None = Field(None, description="Endpoint details or error") class DestroyResult(BaseModel): """Result of destroy operation.""" agent_name: str = Field(..., description="Name of the destroyed agent") - resources_removed: List[str] = Field(default_factory=list, description="List of removed AWS resources") - warnings: List[str] = Field(default_factory=list, description="List of warnings during destruction") - errors: List[str] = Field(default_factory=list, description="List of errors during destruction") + resources_removed: list[str] = Field(default_factory=list, description="List of removed AWS resources") + warnings: list[str] = Field(default_factory=list, description="List of warnings during destruction") + errors: list[str] = Field(default_factory=list, description="List of errors during destruction") dry_run: bool = Field(default=False, description="Whether this was a dry run") diff --git a/src/bedrock_agentcore_starter_toolkit/operations/runtime/status.py b/src/bedrock_agentcore_starter_toolkit/operations/runtime/status.py index ed74112e..bf4d4c21 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/runtime/status.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/runtime/status.py @@ -1,7 +1,6 @@ """Status operations for Bedrock AgentCore SDK.""" from pathlib import Path -from typing import Optional from ...services.runtime import BedrockAgentCoreClient from ...utils.runtime.config import load_config @@ -9,7 +8,7 @@ from .models import StatusConfigInfo, StatusResult -def get_status(config_path: Path, agent_name: Optional[str] = None) -> StatusResult: +def get_status(config_path: Path, agent_name: str | None = None) -> StatusResult: """Get Bedrock AgentCore status including config and runtime details. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/operations/runtime/stop_session.py b/src/bedrock_agentcore_starter_toolkit/operations/runtime/stop_session.py index f3dc9292..d4d0c5b0 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/runtime/stop_session.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/runtime/stop_session.py @@ -2,7 +2,6 @@ import logging from pathlib import Path -from typing import Optional from botocore.exceptions import ClientError @@ -16,8 +15,8 @@ def stop_runtime_session( config_path: Path, - session_id: Optional[str] = None, - agent_name: Optional[str] = None, + session_id: str | None = None, + agent_name: str | None = None, ) -> StopSessionResult: """Stop an active runtime session. diff --git a/src/bedrock_agentcore_starter_toolkit/operations/runtime/vpc_validation.py b/src/bedrock_agentcore_starter_toolkit/operations/runtime/vpc_validation.py index 910effa7..859c3cc3 100644 --- a/src/bedrock_agentcore_starter_toolkit/operations/runtime/vpc_validation.py +++ b/src/bedrock_agentcore_starter_toolkit/operations/runtime/vpc_validation.py @@ -1,7 +1,6 @@ """VPC networking validation utilities for AgentCore Runtime.""" import logging -from typing import List, Optional, Tuple import boto3 from botocore.exceptions import ClientError @@ -11,10 +10,10 @@ def validate_vpc_configuration( region: str, - subnets: List[str], - security_groups: List[str], - session: Optional[boto3.Session] = None, -) -> Tuple[str, List[str]]: + subnets: list[str], + security_groups: list[str], + session: boto3.Session | None = None, +) -> tuple[str, list[str]]: """Validate VPC configuration and return VPC ID and any warnings. Args: @@ -44,7 +43,7 @@ def validate_vpc_configuration( return vpc_id, warnings -def _validate_subnets(ec2_client, subnets: List[str], warnings: List[str]) -> str: +def _validate_subnets(ec2_client, subnets: list[str], warnings: list[str]) -> str: """Validate subnets and return VPC ID.""" try: response = ec2_client.describe_subnets(SubnetIds=subnets) @@ -83,7 +82,7 @@ def _validate_subnets(ec2_client, subnets: List[str], warnings: List[str]) -> st def _validate_security_groups( - ec2_client, security_groups: List[str], expected_vpc_id: str, warnings: List[str] + ec2_client, security_groups: list[str], expected_vpc_id: str, warnings: list[str] ) -> None: """Validate security groups are in the expected VPC.""" try: @@ -124,12 +123,12 @@ def _validate_security_groups( def check_network_immutability( existing_network_mode: str, - existing_subnets: Optional[List[str]], - existing_security_groups: Optional[List[str]], + existing_subnets: list[str] | None, + existing_security_groups: list[str] | None, new_network_mode: str, - new_subnets: Optional[List[str]], - new_security_groups: Optional[List[str]], -) -> Optional[str]: + new_subnets: list[str] | None, + new_security_groups: list[str] | None, +) -> str | None: """Check if network configuration is being changed (not allowed). Returns: @@ -165,7 +164,7 @@ def check_network_immutability( return None -def verify_subnet_azs(ec2_client, subnets: List[str], region: str) -> List[str]: +def verify_subnet_azs(ec2_client, subnets: list[str], region: str) -> list[str]: """Verify subnets are in supported AZs and return any issues.""" # Supported AZ IDs for us-west-2 SUPPORTED_AZS = { diff --git a/src/bedrock_agentcore_starter_toolkit/services/codebuild.py b/src/bedrock_agentcore_starter_toolkit/services/codebuild.py index 0bf4825f..a48390e7 100644 --- a/src/bedrock_agentcore_starter_toolkit/services/codebuild.py +++ b/src/bedrock_agentcore_starter_toolkit/services/codebuild.py @@ -8,7 +8,6 @@ import zipfile from importlib.resources import files from pathlib import Path -from typing import List, Optional import boto3 from botocore.exceptions import ClientError @@ -72,7 +71,7 @@ def ensure_source_bucket(self, account_id: str) -> str: return bucket_name - def upload_source(self, agent_name: str, source_dir: str = ".", dockerfile_dir: Optional[str] = None) -> str: + def upload_source(self, agent_name: str, source_dir: str = ".", dockerfile_dir: str | None = None) -> str: """Upload source directory to S3, respecting .dockerignore patterns. Args: @@ -162,7 +161,7 @@ def create_or_update_project( ecr_repository_uri: str, execution_role: str, source_location: str, - image_tag: Optional[str] = None, + image_tag: str | None = None, ) -> str: """Create or update CodeBuild project for ARM64 builds.""" # Generate tag if not provided @@ -307,7 +306,7 @@ def _get_arm64_buildspec(self, ecr_repository_uri: str, image_tag: str) -> str: - echo "Build completed at $(date)" """ - def _parse_dockerignore(self) -> List[str]: + def _parse_dockerignore(self) -> list[str]: """Parse .dockerignore patterns from template for consistent filtering. Always uses the dockerignore.template to ensure consistent file filtering @@ -345,7 +344,7 @@ def _parse_dockerignore(self) -> List[str]: ".bedrock_agentcore.yaml", # Always exclude config ] - def _should_ignore(self, path: str, patterns: List[str], is_dir: bool = False) -> bool: + def _should_ignore(self, path: str, patterns: list[str], is_dir: bool = False) -> bool: """Check if path should be ignored based on dockerignore patterns.""" # Normalize path if path.startswith("./"): diff --git a/src/bedrock_agentcore_starter_toolkit/services/ecr.py b/src/bedrock_agentcore_starter_toolkit/services/ecr.py index 3edefcd0..46700b2a 100644 --- a/src/bedrock_agentcore_starter_toolkit/services/ecr.py +++ b/src/bedrock_agentcore_starter_toolkit/services/ecr.py @@ -3,7 +3,6 @@ import base64 import re from datetime import datetime -from typing import Optional import boto3 @@ -111,7 +110,7 @@ def deploy_to_ecr( repo_name: str, region: str, container_runtime: ContainerRuntime, - image_tag: Optional[str] = None, + image_tag: str | None = None, ) -> str: """Build and push image to ECR with versioned tagging.""" ecr = boto3.client("ecr", region_name=region) diff --git a/src/bedrock_agentcore_starter_toolkit/services/import_agent/assets/memory_manager_template.py b/src/bedrock_agentcore_starter_toolkit/services/import_agent/assets/memory_manager_template.py index e93ae381..82c485d2 100644 --- a/src/bedrock_agentcore_starter_toolkit/services/import_agent/assets/memory_manager_template.py +++ b/src/bedrock_agentcore_starter_toolkit/services/import_agent/assets/memory_manager_template.py @@ -10,14 +10,14 @@ import os import weakref from datetime import datetime -from typing import Any, Dict, List, Set +from typing import Any class LongTermMemoryManager: """Custom Memory Manager to have equivalent functionality with Bedrock Agents Long Term Memory and Sessions.""" # Class variable to keep track of all instances - _instances: Set[weakref.ref] = set() + _instances: set[weakref.ref] = set() def __init__( self, @@ -49,11 +49,11 @@ def _cleanup_reference(ref): """Callback for when a weak reference is removed.""" LongTermMemoryManager._instances.discard(ref) - def _load_session_summaries(self) -> List[Dict[str, Any]]: + def _load_session_summaries(self) -> list[dict[str, Any]]: """Load all stored session summaries.""" summary_file = self.storage_path if os.path.exists(summary_file): - with open(summary_file, "r") as f: + with open(summary_file) as f: return json.load(f) return [] @@ -64,7 +64,7 @@ def _save_session_summaries(self): json.dump(self.session_summaries, f) self._last_memory_update_time = datetime.now().timestamp() - def add_message(self, message: Dict[str, str]): + def add_message(self, message: dict[str, str]): """Add a message to the current session.""" self.current_session_messages.append(message) diff --git a/src/bedrock_agentcore_starter_toolkit/services/import_agent/scripts/base_bedrock_translate.py b/src/bedrock_agentcore_starter_toolkit/services/import_agent/scripts/base_bedrock_translate.py index 2b841b89..772814e0 100644 --- a/src/bedrock_agentcore_starter_toolkit/services/import_agent/scripts/base_bedrock_translate.py +++ b/src/bedrock_agentcore_starter_toolkit/services/import_agent/scripts/base_bedrock_translate.py @@ -12,7 +12,6 @@ import time import uuid import zipfile -from typing import Dict, Tuple import autopep8 import boto3 @@ -164,7 +163,7 @@ def __init__(self, agent_config, debug: bool, output_dir: str, enabled_primitive self.agent_setup_code = "" self.usage_code = "" - def _clean_fixtures_and_prompt(self, base_template, fixtures) -> Tuple[str, Dict]: + def _clean_fixtures_and_prompt(self, base_template, fixtures) -> tuple[str, dict]: """Clean up the base template and fixtures by removing unused keys. Args: @@ -207,7 +206,7 @@ def _clean_fixtures_and_prompt(self, base_template, fixtures) -> Tuple[str, Dict return base_template, fixtures - def generate_prompt(self, config: Dict): + def generate_prompt(self, config: dict): """Generate prompt code based on the configuration.""" prompt_type = config.get("promptType", "") self.enabled_prompts.append(prompt_type) @@ -329,7 +328,6 @@ def generate_memory_configuration(self, memory_saver: str) -> str: open(memory_manager_path, "a", encoding="utf-8") as target, open( os.path.join(get_base_dir(__file__), "assets", "memory_manager_template.py"), - "r", encoding="utf-8", ) as template, ): @@ -465,7 +463,7 @@ def user_input_tool(user_targeted_question: str): return tool_code - def generate_openapi_ag_code(self, ag: Dict, platform: str) -> Tuple[list, str]: + def generate_openapi_ag_code(self, ag: dict, platform: str) -> tuple[list, str]: """Generate code for OpenAPI Action Groups.""" tool_code = "" tool_instances = [] @@ -637,7 +635,7 @@ def {tool_name}(input_data) -> str: return tool_instances, tool_code - def generate_structured_ag_code(self, ag: Dict, platform: str) -> Tuple[list, str]: + def generate_structured_ag_code(self, ag: dict, platform: str) -> tuple[list, str]: """Generate code for Structured Function Action Groups.""" tool_code = "" tool_instances = [] @@ -1136,7 +1134,7 @@ def translate(self, output_path: str, code_sections: list, platform: str): requirements_path = os.path.join(get_base_dir(__file__), "assets", f"requirements_{platform}.j2") if os.path.exists(requirements_path): with ( - open(requirements_path, "r", encoding="utf-8") as src_file, + open(requirements_path, encoding="utf-8") as src_file, open(os.path.join(self.output_dir, "requirements.txt"), "w", encoding="utf-8") as dest_file, ): dest_file.truncate(0) diff --git a/src/bedrock_agentcore_starter_toolkit/services/import_agent/scripts/bedrock_to_strands.py b/src/bedrock_agentcore_starter_toolkit/services/import_agent/scripts/bedrock_to_strands.py index 917fe7b3..b6e33d81 100644 --- a/src/bedrock_agentcore_starter_toolkit/services/import_agent/scripts/bedrock_to_strands.py +++ b/src/bedrock_agentcore_starter_toolkit/services/import_agent/scripts/bedrock_to_strands.py @@ -59,7 +59,7 @@ def generate_model_configurations(self) -> str: model_configs = [] for i, config in enumerate(self.prompt_configs): - prompt_type = config.get("promptType", "CUSTOM_{}".format(i)) + prompt_type = config.get("promptType", f"CUSTOM_{i}") if prompt_type == "KNOWLEDGE_BASE_RESPONSE_GENERATION" and not self.knowledge_bases: continue inference_config = config.get("inferenceConfiguration", {}) diff --git a/src/bedrock_agentcore_starter_toolkit/services/import_agent/utils.py b/src/bedrock_agentcore_starter_toolkit/services/import_agent/utils.py index b1addaa4..4262bf0d 100644 --- a/src/bedrock_agentcore_starter_toolkit/services/import_agent/utils.py +++ b/src/bedrock_agentcore_starter_toolkit/services/import_agent/utils.py @@ -5,7 +5,7 @@ import re import secrets import textwrap -from typing import Any, Dict, List, Union +from typing import Any def json_to_obj_fixed(json_string: str): @@ -91,7 +91,7 @@ def unindent_by_one(input_code, spaces_per_indent=4): def generate_pydantic_models( - schema_input: Union[Dict[str, Any], List[Dict[str, Any]], str], + schema_input: dict[str, Any] | list[dict[str, Any]] | str, root_model_name: str = "RequestModel", content_type_annotation: str = "", ) -> str: @@ -128,7 +128,7 @@ def clean_class_name(name: str) -> str: # Convert to CamelCase return "".join(word.capitalize() for word in cleaned.split("_")) - def process_schema(schema_obj: Dict[str, Any], name: str) -> str: + def process_schema(schema_obj: dict[str, Any], name: str) -> str: """Process a schema object and return the model class name.""" # Handle schema wrapper if "schema" in schema_obj: @@ -200,7 +200,7 @@ def process_schema(schema_obj: Dict[str, Any], name: str) -> str: else: return get_python_type(obj_type) - def get_type_hint(prop_schema: Dict[str, Any], name: str) -> str: + def get_type_hint(prop_schema: dict[str, Any], name: str) -> str: """Get the Python type hint for a property schema.""" if "$ref" in prop_schema: ref_name = prop_schema["$ref"].split("/")[-1] @@ -234,7 +234,7 @@ def get_python_type(openapi_type: str) -> str: } return type_mapping.get(openapi_type, "Any") - def process_parameter_list(params: List[Dict[str, Any]], name: str) -> str: + def process_parameter_list(params: list[dict[str, Any]], name: str) -> str: """Process OpenAPI parameter array and create a model.""" class_name = clean_class_name(name) if class_name in models: @@ -297,7 +297,7 @@ def process_parameter_list(params: List[Dict[str, Any]], name: str) -> str: models[class_name] = class_def return class_name - def process_parameter_dict(params: Dict[str, Dict[str, Any]], name: str) -> str: + def process_parameter_dict(params: dict[str, dict[str, Any]], name: str) -> str: """Process a dictionary of named parameters.""" class_name = clean_class_name(name) if class_name in models: @@ -391,7 +391,7 @@ def get_template_fixtures(field: str = "orchestrationBasePrompts", group: str = """ project_root = os.path.dirname(os.path.abspath(__file__)) file_path = os.path.join(project_root, "assets", "template_fixtures_merged.json") - with open(file_path, "r", encoding="utf-8") as f: + with open(file_path, encoding="utf-8") as f: data = json.load(f) if field not in data: diff --git a/src/bedrock_agentcore_starter_toolkit/services/runtime.py b/src/bedrock_agentcore_starter_toolkit/services/runtime.py index 75e409e9..ce370ef0 100644 --- a/src/bedrock_agentcore_starter_toolkit/services/runtime.py +++ b/src/bedrock_agentcore_starter_toolkit/services/runtime.py @@ -6,7 +6,7 @@ import urllib.parse import uuid from importlib.metadata import version -from typing import Any, Dict, Optional +from typing import Any import boto3 import requests @@ -38,7 +38,7 @@ def generate_session_id() -> str: return str(uuid.uuid4()) -def _validate_runtime_type(runtime_type: Optional[str]) -> str: +def _validate_runtime_type(runtime_type: str | None) -> str: """Validate runtime type format. Args: @@ -101,7 +101,7 @@ def _handle_aws_response(response) -> dict: return response -def _handle_streaming_response(response) -> Dict[str, Any]: +def _handle_streaming_response(response) -> dict[str, Any]: complete_text = "" for line in response.iter_lines(chunk_size=1): if line: @@ -166,22 +166,22 @@ def create_agent( execution_role_arn: str, # Code zip parameters (for direct_code_deploy deployment) deployment_type: str = "direct_code_deploy", - code_s3_bucket: Optional[str] = None, - code_s3_key: Optional[str] = None, - runtime_type: Optional[str] = None, - entrypoint_array: Optional[list] = None, - entrypoint_handler: Optional[str] = None, + code_s3_bucket: str | None = None, + code_s3_key: str | None = None, + runtime_type: str | None = None, + entrypoint_array: list | None = None, + entrypoint_handler: str | None = None, # Container parameters (for container deployment) - image_uri: Optional[str] = None, + image_uri: str | None = None, # Common parameters - network_config: Optional[Dict] = None, - authorizer_config: Optional[Dict] = None, - request_header_config: Optional[Dict] = None, - protocol_config: Optional[Dict] = None, - env_vars: Optional[Dict] = None, + network_config: dict | None = None, + authorizer_config: dict | None = None, + request_header_config: dict | None = None, + protocol_config: dict | None = None, + env_vars: dict | None = None, auto_update_on_conflict: bool = False, - lifecycle_config: Optional[Dict] = None, - ) -> Dict[str, str]: + lifecycle_config: dict | None = None, + ) -> dict[str, str]: """Create new agent with either direct_code_deploy or container deployment. Args: @@ -325,21 +325,21 @@ def update_agent( execution_role_arn: str, # Code zip parameters (for direct_code_deploy deployment) deployment_type: str = "direct_code_deploy", - code_s3_bucket: Optional[str] = None, - code_s3_key: Optional[str] = None, - runtime_type: Optional[str] = None, - entrypoint_array: Optional[list] = None, - entrypoint_handler: Optional[str] = None, + code_s3_bucket: str | None = None, + code_s3_key: str | None = None, + runtime_type: str | None = None, + entrypoint_array: list | None = None, + entrypoint_handler: str | None = None, # Container parameters (for container deployment) - image_uri: Optional[str] = None, + image_uri: str | None = None, # Common parameters - network_config: Optional[Dict] = None, - authorizer_config: Optional[Dict] = None, - request_header_config: Optional[Dict] = None, - protocol_config: Optional[Dict] = None, - env_vars: Optional[Dict] = None, - lifecycle_config: Optional[Dict] = None, - ) -> Dict[str, str]: + network_config: dict | None = None, + authorizer_config: dict | None = None, + request_header_config: dict | None = None, + protocol_config: dict | None = None, + env_vars: dict | None = None, + lifecycle_config: dict | None = None, + ) -> dict[str, str]: """Update existing agent with either direct_code_deploy or container deployment. Args: @@ -440,7 +440,7 @@ def list_agents(self, max_results: int = 100) -> list: self.logger.error("Failed to list agents: %s", str(e)) raise - def find_agent_by_name(self, agent_name: str) -> Optional[Dict]: + def find_agent_by_name(self, agent_name: str) -> dict | None: """Find an agent by name, reusing list_agents method.""" try: # Get all agents using the existing method @@ -458,27 +458,27 @@ def find_agent_by_name(self, agent_name: str) -> Optional[Dict]: def create_or_update_agent( self, - agent_id: Optional[str], + agent_id: str | None, agent_name: str, execution_role_arn: str, # Code zip parameters deployment_type: str = "direct_code_deploy", - code_s3_bucket: Optional[str] = None, - code_s3_key: Optional[str] = None, - runtime_type: Optional[str] = None, - entrypoint_array: Optional[list] = None, - entrypoint_handler: Optional[str] = None, + code_s3_bucket: str | None = None, + code_s3_key: str | None = None, + runtime_type: str | None = None, + entrypoint_array: list | None = None, + entrypoint_handler: str | None = None, # Container parameters - image_uri: Optional[str] = None, + image_uri: str | None = None, # Common parameters - network_config: Optional[Dict] = None, - authorizer_config: Optional[Dict] = None, - request_header_config: Optional[Dict] = None, - protocol_config: Optional[Dict] = None, - env_vars: Optional[Dict] = None, + network_config: dict | None = None, + authorizer_config: dict | None = None, + request_header_config: dict | None = None, + protocol_config: dict | None = None, + env_vars: dict | None = None, auto_update_on_conflict: bool = False, - lifecycle_config: Optional[Dict] = None, - ) -> Dict[str, str]: + lifecycle_config: dict | None = None, + ) -> dict[str, str]: """Create or update agent with either direct_code_deploy or container deployment.""" if agent_id: return self.update_agent( @@ -557,7 +557,7 @@ def wait_for_agent_endpoint_ready(self, agent_id: str, endpoint_name: str = "DEF f"please check status and try to invoke after some time" ) - def get_agent_runtime(self, agent_id: str) -> Dict: + def get_agent_runtime(self, agent_id: str) -> dict: """Get agent runtime details. Args: @@ -568,7 +568,7 @@ def get_agent_runtime(self, agent_id: str) -> Dict: """ return self.client.get_agent_runtime(agentRuntimeId=agent_id) - def get_agent_runtime_endpoint(self, agent_id: str, endpoint_name: str = "DEFAULT") -> Dict: + def get_agent_runtime_endpoint(self, agent_id: str, endpoint_name: str = "DEFAULT") -> dict: """Get agent runtime endpoint details. Args: @@ -583,7 +583,7 @@ def get_agent_runtime_endpoint(self, agent_id: str, endpoint_name: str = "DEFAUL endpointName=endpoint_name, ) - def delete_agent_runtime_endpoint(self, agent_id: str, endpoint_name: str = "DEFAULT") -> Dict: + def delete_agent_runtime_endpoint(self, agent_id: str, endpoint_name: str = "DEFAULT") -> dict: """Delete agent runtime endpoint. Args: @@ -615,9 +615,9 @@ def invoke_endpoint( payload: str, session_id: str, endpoint_name: str = "DEFAULT", - user_id: Optional[str] = None, - custom_headers: Optional[dict] = None, - ) -> Dict: + user_id: str | None = None, + custom_headers: dict | None = None, + ) -> dict: """Invoke agent endpoint. Args: @@ -675,7 +675,7 @@ def stop_runtime_session( agent_arn: str, session_id: str, endpoint_name: str = "DEFAULT", - ) -> Dict: + ) -> dict: """Stop a runtime session. Args: @@ -703,7 +703,7 @@ def stop_runtime_session( def _update_api_key_credential_provider( self, api_key_credential_provider_name: str, api_key: str, agent_name: str - ) -> Dict[Any, Any]: + ) -> dict[Any, Any]: try: response = self.client.update_api_key_credential_provider( name=api_key_credential_provider_name, apiKey=api_key @@ -725,7 +725,7 @@ def _update_api_key_credential_provider( def _create_api_key_credential_provider( self, api_key_credential_provider_name: str, api_key: str, agent_name: str - ) -> Dict[Any, Any]: + ) -> dict[Any, Any]: try: response = self.client.create_api_key_credential_provider( name=api_key_credential_provider_name, apiKey=api_key @@ -746,8 +746,8 @@ def _create_api_key_credential_provider( raise def create_or_update_api_key_credential_provider( - self, api_key_credential_provider_name: Optional[str], api_key: str, agent_name: str, key_name: str - ) -> Dict[Any, Any]: + self, api_key_credential_provider_name: str | None, api_key: str, agent_name: str, key_name: str + ) -> dict[Any, Any]: """Get or Create an API Key Credential provider for an agent.""" if api_key_credential_provider_name: return self._update_api_key_credential_provider(api_key_credential_provider_name, api_key, agent_name) @@ -755,7 +755,7 @@ def create_or_update_api_key_credential_provider( return self._create_api_key_credential_provider(api_key_credential_provider_name, api_key, agent_name) - def delete_api_key_credential_provider(self, api_key_credential_provider_name: str) -> Dict[Any, Any]: + def delete_api_key_credential_provider(self, api_key_credential_provider_name: str) -> dict[Any, Any]: """Delete an API Key Credential provider.""" return self.client.delete_api_key_credential_provider(name=api_key_credential_provider_name) @@ -781,11 +781,11 @@ def invoke_endpoint( agent_arn: str, payload, session_id: str, - bearer_token: Optional[str], - user_id: Optional[str] = None, + bearer_token: str | None, + user_id: str | None = None, endpoint_name: str = "DEFAULT", - custom_headers: Optional[dict] = None, - ) -> Dict: + custom_headers: dict | None = None, + ) -> dict: """Invoke agent endpoint using HTTP request with bearer token. Args: @@ -866,7 +866,7 @@ def invoke_endpoint( payload: str, workload_access_token: str, oauth2_callback_url: str, - custom_headers: Optional[dict] = None, + custom_headers: dict | None = None, ): """Invoke the endpoint with the given parameters.""" from bedrock_agentcore.runtime.models import ACCESS_TOKEN_HEADER, OAUTH2_CALLBACK_URL_HEADER, SESSION_HEADER diff --git a/src/bedrock_agentcore_starter_toolkit/utils/aws.py b/src/bedrock_agentcore_starter_toolkit/utils/aws.py index b3e43d10..f84813dc 100644 --- a/src/bedrock_agentcore_starter_toolkit/utils/aws.py +++ b/src/bedrock_agentcore_starter_toolkit/utils/aws.py @@ -1,7 +1,5 @@ """Generic aws utilities.""" -from typing import Optional - import boto3 from botocore.exceptions import ( ClientError, @@ -43,7 +41,7 @@ def get_region() -> str: return boto3.Session().region_name or DEFAULT_REGION -def ensure_valid_aws_creds() -> tuple[bool, Optional[str]]: +def ensure_valid_aws_creds() -> tuple[bool, str | None]: """Try to make an sts call and return a resourceful message if it fails.""" try: get_account_id() diff --git a/src/bedrock_agentcore_starter_toolkit/utils/lambda_utils.py b/src/bedrock_agentcore_starter_toolkit/utils/lambda_utils.py index 85c9f392..3c669f81 100644 --- a/src/bedrock_agentcore_starter_toolkit/utils/lambda_utils.py +++ b/src/bedrock_agentcore_starter_toolkit/utils/lambda_utils.py @@ -4,7 +4,6 @@ import json import logging import zipfile -from typing import Optional from boto3 import Session @@ -19,7 +18,7 @@ def create_lambda_function( runtime: str, handler: str, gateway_role_arn: str, - description: Optional[str] = None, + description: str | None = None, ) -> str: """Create a Lambda function with the specified code. diff --git a/src/bedrock_agentcore_starter_toolkit/utils/runtime/agentcore_identity.py b/src/bedrock_agentcore_starter_toolkit/utils/runtime/agentcore_identity.py index f860602d..4673801c 100644 --- a/src/bedrock_agentcore_starter_toolkit/utils/runtime/agentcore_identity.py +++ b/src/bedrock_agentcore_starter_toolkit/utils/runtime/agentcore_identity.py @@ -2,14 +2,13 @@ import logging from pathlib import Path -from typing import Dict, Optional from .schema import BedrockAgentCoreAgentSchema log = logging.getLogger(__name__) -def _parse_env_file(env_file_path: Path) -> Dict[str, str]: +def _parse_env_file(env_file_path: Path) -> dict[str, str]: """Parse a .env file and return a dictionary of environment variables. Args: @@ -53,7 +52,7 @@ def _parse_env_file(env_file_path: Path) -> Dict[str, str]: def _load_api_key_from_env_if_configured( agent_config: BedrockAgentCoreAgentSchema, project_dir: Path, -) -> Optional[str]: +) -> str | None: """Load API key from .env file if api_key_env_var_name is configured. This function checks if the agent is configured to use API key-based authentication diff --git a/src/bedrock_agentcore_starter_toolkit/utils/runtime/config.py b/src/bedrock_agentcore_starter_toolkit/utils/runtime/config.py index f3c909f8..7ae2929f 100644 --- a/src/bedrock_agentcore_starter_toolkit/utils/runtime/config.py +++ b/src/bedrock_agentcore_starter_toolkit/utils/runtime/config.py @@ -2,7 +2,6 @@ import logging from pathlib import Path -from typing import Optional import yaml from pydantic import ValidationError @@ -30,7 +29,7 @@ def is_project_config_format(config_path: Path) -> bool: """Check if config file uses project format (has 'agents' key).""" if not config_path.exists(): return False - with open(config_path, "r") as f: + with open(config_path) as f: data = yaml.safe_load(f) or {} return isinstance(data, dict) and "agents" in data @@ -74,7 +73,7 @@ def load_config(config_path: Path, autofill_missing_aws=True) -> BedrockAgentCor if not config_path.exists(): raise FileNotFoundError(f"Configuration not found: {config_path}") - with open(config_path, "r") as f: + with open(config_path) as f: data = yaml.safe_load(f) or {} # Auto-detect and transform legacy format @@ -147,7 +146,7 @@ def save_config(config: BedrockAgentCoreConfigSchema, config_path: Path): ) -def load_config_if_exists(config_path: Path, autofill_missing_aws=True) -> Optional[BedrockAgentCoreConfigSchema]: +def load_config_if_exists(config_path: Path, autofill_missing_aws=True) -> BedrockAgentCoreConfigSchema | None: """Load configuration if file exists, otherwise return None. Args: @@ -225,7 +224,7 @@ def merge_agent_config( return config -def get_agentcore_directory(project_root: Path, agent_name: str, source_path: Optional[str] = None) -> Path: +def get_agentcore_directory(project_root: Path, agent_name: str, source_path: str | None = None) -> Path: """Get the agentcore directory for an agent's build artifacts. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/utils/runtime/container.py b/src/bedrock_agentcore_starter_toolkit/utils/runtime/container.py index edafaf05..425bddaa 100644 --- a/src/bedrock_agentcore_starter_toolkit/utils/runtime/container.py +++ b/src/bedrock_agentcore_starter_toolkit/utils/runtime/container.py @@ -5,7 +5,6 @@ import subprocess # nosec B404 - Required for container runtime operations import time from pathlib import Path -from typing import List, Optional, Tuple from jinja2 import Template from rich.console import Console @@ -25,7 +24,7 @@ class ContainerRuntime: DEFAULT_RUNTIME = "auto" DEFAULT_PLATFORM = "linux/arm64" - def __init__(self, runtime_type: Optional[str] = None, print_logs=True): + def __init__(self, runtime_type: str | None = None, print_logs=True): """Initialize container runtime. Args: @@ -128,14 +127,14 @@ def generate_dockerfile( agent_path: Path, output_dir: Path, agent_name: str, - aws_region: Optional[str] = None, + aws_region: str | None = None, enable_observability: bool = True, - requirements_file: Optional[str] = None, - memory_id: Optional[str] = None, - memory_name: Optional[str] = None, - source_path: Optional[str] = None, - protocol: Optional[str] = None, - explicit_requirements_file: Optional[Path] = None, + requirements_file: str | None = None, + memory_id: str | None = None, + memory_name: str | None = None, + source_path: str | None = None, + protocol: str | None = None, + explicit_requirements_file: Path | None = None, silence_warn=False, language: str = "python", node_version: str = "20", @@ -388,9 +387,9 @@ def build( self, build_context: Path, tag: str, - dockerfile_path: Optional[Path] = None, - platform: Optional[str] = None, - ) -> Tuple[bool, List[str]]: + dockerfile_path: Path | None = None, + platform: str | None = None, + ) -> tuple[bool, list[str]]: """Build container image. Args: @@ -433,7 +432,7 @@ def build( return self._execute_command(cmd) - def run_local(self, tag: str, port: int = 8080, env_vars: Optional[dict] = None) -> subprocess.CompletedProcess: + def run_local(self, tag: str, port: int = 8080, env_vars: dict | None = None) -> subprocess.CompletedProcess: """Run container locally. Args: @@ -519,7 +518,7 @@ def push(self, tag: str) -> bool: log.error("Failed to push image") return False - def _execute_command(self, cmd: List[str]) -> Tuple[bool, List[str]]: + def _execute_command(self, cmd: list[str]) -> tuple[bool, list[str]]: """Execute command and capture output.""" try: process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1) # nosec B603 diff --git a/src/bedrock_agentcore_starter_toolkit/utils/runtime/create_with_iam_eventual_consistency.py b/src/bedrock_agentcore_starter_toolkit/utils/runtime/create_with_iam_eventual_consistency.py index 4509acb7..f90466c6 100644 --- a/src/bedrock_agentcore_starter_toolkit/utils/runtime/create_with_iam_eventual_consistency.py +++ b/src/bedrock_agentcore_starter_toolkit/utils/runtime/create_with_iam_eventual_consistency.py @@ -2,7 +2,8 @@ import logging import time -from typing import Any, Callable +from collections.abc import Callable +from typing import Any from botocore.exceptions import ClientError diff --git a/src/bedrock_agentcore_starter_toolkit/utils/runtime/entrypoint.py b/src/bedrock_agentcore_starter_toolkit/utils/runtime/entrypoint.py index 5eb98b49..ee7d6654 100644 --- a/src/bedrock_agentcore_starter_toolkit/utils/runtime/entrypoint.py +++ b/src/bedrock_agentcore_starter_toolkit/utils/runtime/entrypoint.py @@ -7,7 +7,7 @@ import sys from dataclasses import dataclass from pathlib import Path -from typing import List, Literal, Optional, Tuple +from typing import Literal, Optional log = logging.getLogger(__name__) @@ -25,7 +25,7 @@ ] -def detect_entrypoint_by_language(source_dir: Path, language: str) -> List[Path]: +def detect_entrypoint_by_language(source_dir: Path, language: str) -> list[Path]: """Detect entrypoint files based on project language. Args: @@ -55,7 +55,7 @@ def detect_entrypoint_by_language(source_dir: Path, language: str) -> List[Path] return found_files -def detect_language(project_dir: Path, entrypoint: Optional[str] = None) -> Literal["python", "typescript"]: +def detect_language(project_dir: Path, entrypoint: str | None = None) -> Literal["python", "typescript"]: """Auto-detect project language based on entrypoint extension or dependency files. Args: @@ -118,7 +118,7 @@ def detect_typescript_project(project_dir: Path) -> Optional["TypeScriptProjectI ) -def parse_entrypoint(entrypoint: str) -> Tuple[Path, str]: +def parse_entrypoint(entrypoint: str) -> tuple[Path, str]: """Parse entrypoint into file path and name. Args: @@ -145,10 +145,10 @@ def parse_entrypoint(entrypoint: str) -> Tuple[Path, str]: class DependencyInfo: """Information about project dependencies.""" - file: Optional[str] # Relative path for Docker context + file: str | None # Relative path for Docker context type: str # "requirements", "pyproject", or "notfound" - resolved_path: Optional[str] = None # Absolute path for validation - install_path: Optional[str] = None # Path for pip install command + resolved_path: str | None = None # Absolute path for validation + install_path: str | None = None # Path for pip install command @property def found(self) -> bool: @@ -175,7 +175,7 @@ def is_root_package(self) -> bool: class TypeScriptProjectInfo: """Information about a TypeScript project extracted from package.json.""" - package_json_path: Optional[str] = None + package_json_path: str | None = None node_version: str = "20" has_build_script: bool = False @@ -185,7 +185,7 @@ def found(self) -> bool: return self.package_json_path is not None -def detect_dependencies(package_dir: Path, explicit_file: Optional[str] = None) -> DependencyInfo: +def detect_dependencies(package_dir: Path, explicit_file: str | None = None) -> DependencyInfo: """Detect dependency file, with optional explicit override.""" if explicit_file: return _handle_explicit_file(package_dir, explicit_file) @@ -309,7 +309,7 @@ class RuntimeEntrypointInfo: handler_name: str # Handler function name (e.g., "app") -def parse_entrypoint_for_runtime(entrypoint: str, source_dir: Optional[Path] = None) -> RuntimeEntrypointInfo: +def parse_entrypoint_for_runtime(entrypoint: str, source_dir: Path | None = None) -> RuntimeEntrypointInfo: """Parse entrypoint for Runtime codeConfiguration API. Supported formats: @@ -365,7 +365,7 @@ def parse_entrypoint_for_runtime(entrypoint: str, source_dir: Optional[Path] = N return RuntimeEntrypointInfo(file_path=file_path, module_name=module, handler_name=handler) -def build_entrypoint_array(entrypoint_path: str, has_otel_distro: bool, observability_enabled: bool) -> List[str]: +def build_entrypoint_array(entrypoint_path: str, has_otel_distro: bool, observability_enabled: bool) -> list[str]: """Build entrypoint array for Runtime codeConfiguration API. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/utils/runtime/logs.py b/src/bedrock_agentcore_starter_toolkit/utils/runtime/logs.py index 928bf84c..620cb361 100644 --- a/src/bedrock_agentcore_starter_toolkit/utils/runtime/logs.py +++ b/src/bedrock_agentcore_starter_toolkit/utils/runtime/logs.py @@ -1,10 +1,9 @@ """Utility functions for agent log information.""" from datetime import datetime, timezone -from typing import Optional, Tuple -def get_agent_runtime_log_group(agent_id: str, endpoint_name: Optional[str] = None) -> str: +def get_agent_runtime_log_group(agent_id: str, endpoint_name: str | None = None) -> str: """Get the CloudWatch log group name for agent runtime logs. This is used by observability and evaluation features to reference agent logs. @@ -34,10 +33,10 @@ def get_genai_observability_url(region: str) -> str: def get_agent_log_paths( agent_id: str, - endpoint_name: Optional[str] = None, - deployment_type: Optional[str] = None, - session_id: Optional[str] = None, -) -> Tuple[str, str]: + endpoint_name: str | None = None, + deployment_type: str | None = None, + session_id: str | None = None, +) -> tuple[str, str]: """Get CloudWatch log group paths for an agent. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/utils/runtime/package.py b/src/bedrock_agentcore_starter_toolkit/utils/runtime/package.py index 66d1d362..9eaeca85 100644 --- a/src/bedrock_agentcore_starter_toolkit/utils/runtime/package.py +++ b/src/bedrock_agentcore_starter_toolkit/utils/runtime/package.py @@ -9,7 +9,6 @@ import tempfile import zipfile from pathlib import Path -from typing import List, Optional import boto3 @@ -41,9 +40,9 @@ def dependencies_hash(self) -> Path: def should_rebuild_dependencies( self, requirements_file: Path, - user_lock_file: Optional[Path], + user_lock_file: Path | None, force: bool, - runtime_version: Optional[str] = None, + runtime_version: str | None = None, ) -> bool: """Determine if dependencies need rebuilding using multi-signal detection. @@ -84,7 +83,7 @@ def should_rebuild_dependencies( return False def save_dependencies_hash( - self, requirements_file: Path, user_lock_file: Optional[Path], runtime_version: Optional[str] = None + self, requirements_file: Path, user_lock_file: Path | None, runtime_version: str | None = None ) -> None: """Save combined hash of requirements file, uv.lock, and runtime version for future comparisons. @@ -109,7 +108,7 @@ def _compute_file_hash(file_path: Path) -> str: return hashlib.sha256(file_path.read_bytes()).hexdigest() def _compute_combined_hash( - self, requirements_file: Path, user_lock_file: Optional[Path], runtime_version: Optional[str] = None + self, requirements_file: Path, user_lock_file: Path | None, runtime_version: str | None = None ) -> str: """Compute combined hash of requirements file, uv.lock, and runtime version. @@ -155,7 +154,7 @@ def create_deployment_package( agent_name: str, cache_dir: Path, runtime_version: str, - requirements_file: Optional[Path] = None, + requirements_file: Path | None = None, force_rebuild_deps: bool = False, ) -> tuple[Path, bool]: """Create deployment.zip with smart dependency caching. @@ -264,7 +263,7 @@ def _build_dependencies_zip(self, requirements_file: Path, output_zip: Path, run arcname = file_path.relative_to(package_dir) zipf.write(file_path, arcname) - def _check_otel_distro(self, requirements_file: Optional[Path]) -> bool: + def _check_otel_distro(self, requirements_file: Path | None) -> bool: """Check if aws-opentelemetry-distro is in requirements. Args: @@ -381,9 +380,7 @@ def _install_dependencies( except subprocess.CalledProcessError as e: raise RuntimeError(f"Failed to install dependencies with uv: {e.stderr}") from e - def _build_uv_command( - self, requirements: Path, target: Path, py_version: str, platform: Optional[str] - ) -> List[str]: + def _build_uv_command(self, requirements: Path, target: Path, py_version: str, platform: str | None) -> list[str]: """Build uv pip install command. Args: @@ -462,7 +459,7 @@ def _build_direct_code_deploy(self, source_dir: Path, output_zip: Path) -> None: zipf.write(Path(root) / file, file_rel) - def _merge_zips(self, dependencies_zip: Optional[Path], direct_code_deploy: Path, output_zip: Path) -> None: + def _merge_zips(self, dependencies_zip: Path | None, direct_code_deploy: Path, output_zip: Path) -> None: """Merge dependencies and code layers into deployment.zip. Args: @@ -486,7 +483,7 @@ def _merge_zips(self, dependencies_zip: Optional[Path], direct_code_deploy: Path original_info = code.getinfo(item) out.writestr(original_info, code.read(item)) - def _get_ignore_patterns(self) -> List[str]: + def _get_ignore_patterns(self) -> list[str]: """Get ignore patterns from dockerignore.template (matches CodeBuild logic). Returns: @@ -525,7 +522,7 @@ def _get_ignore_patterns(self) -> List[str]: ".bedrock_agentcore", ] - def _should_ignore(self, path: str, patterns: List[str], is_dir: bool) -> bool: + def _should_ignore(self, path: str, patterns: list[str], is_dir: bool) -> bool: """Check if path should be ignored based on dockerignore patterns. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/utils/runtime/policy_template.py b/src/bedrock_agentcore_starter_toolkit/utils/runtime/policy_template.py index 2ab5c05a..dd6bc388 100644 --- a/src/bedrock_agentcore_starter_toolkit/utils/runtime/policy_template.py +++ b/src/bedrock_agentcore_starter_toolkit/utils/runtime/policy_template.py @@ -3,7 +3,6 @@ import json import re from pathlib import Path -from typing import Dict, Optional from jinja2 import Environment, FileSystemLoader @@ -13,7 +12,7 @@ def _get_template_dir() -> Path: return Path(__file__).parent / "templates" -def _render_template(template_name: str, variables: Dict[str, str]) -> str: +def _render_template(template_name: str, variables: dict[str, str]) -> str: """Render a Jinja2 template with the provided variables.""" template_dir = _get_template_dir() env = Environment(loader=FileSystemLoader(template_dir), autoescape=True) @@ -43,9 +42,9 @@ def render_execution_policy_template( account_id: str, agent_name: str, deployment_type: str = "direct_code_deploy", - protocol: Optional[str] = None, - memory_id: Optional[str] = None, - ecr_repository_name: Optional[str] = None, + protocol: str | None = None, + memory_id: str | None = None, + ecr_repository_name: str | None = None, ) -> str: """Render the execution policy template with provided values. @@ -84,7 +83,7 @@ def render_execution_policy_template( return cleaned -def validate_rendered_policy(policy_json: str) -> Dict: +def validate_rendered_policy(policy_json: str) -> dict: """Validate that the rendered policy is valid JSON. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/utils/runtime/schema.py b/src/bedrock_agentcore_starter_toolkit/utils/runtime/schema.py index 733622c5..c82fb8fc 100644 --- a/src/bedrock_agentcore_starter_toolkit/utils/runtime/schema.py +++ b/src/bedrock_agentcore_starter_toolkit/utils/runtime/schema.py @@ -1,6 +1,6 @@ """Typed configuration schema for Bedrock AgentCore SDK.""" -from typing import Dict, List, Literal, Optional +from typing import Literal from pydantic import BaseModel, Field, field_validator, model_validator @@ -8,8 +8,8 @@ class NetworkModeConfig(BaseModel): """Network mode configuration for VPC deployments.""" - security_groups: List[str] = Field(default_factory=list, description="List of security group IDs") - subnets: List[str] = Field(default_factory=list, description="List of subnet IDs") + security_groups: list[str] = Field(default_factory=list, description="List of security group IDs") + subnets: list[str] = Field(default_factory=list, description="List of subnet IDs") class MemoryConfig(BaseModel): @@ -18,9 +18,9 @@ class MemoryConfig(BaseModel): mode: Literal["STM_ONLY", "STM_AND_LTM", "NO_MEMORY"] = Field( default="NO_MEMORY", description="Memory mode - opt-in feature" ) - memory_id: Optional[str] = Field(default=None, description="Memory resource ID") - memory_arn: Optional[str] = Field(default=None, description="Memory resource ARN") - memory_name: Optional[str] = Field(default=None, description="Memory name") + memory_id: str | None = Field(default=None, description="Memory resource ID") + memory_arn: str | None = Field(default=None, description="Memory resource ARN") + memory_name: str | None = Field(default=None, description="Memory name") event_expiry_days: int = Field(default=30, description="Event expiry duration in days") first_invoke_memory_check_done: bool = Field( default=False, description="Whether first invoke memory check has been performed" @@ -54,7 +54,7 @@ class WorkloadIdentityInfo(BaseModel): name: str = Field(..., description="Workload identity name") arn: str = Field(..., description="Workload identity ARN") - return_urls: List[str] = Field( + return_urls: list[str] = Field( default_factory=list, description="Application return URLs where AgentCore redirects users for session binding verification", ) @@ -64,11 +64,11 @@ class AwsJwtConfig(BaseModel): """AWS IAM JWT federation configuration for outbound authentication without secrets.""" enabled: bool = Field(default=False, description="Whether AWS IAM JWT federation is enabled for this account") - audiences: List[str] = Field( + audiences: list[str] = Field( default_factory=list, description="List of allowed audiences for STS:GetWebIdentityToken IAM policy" ) signing_algorithm: str = Field(default="ES384", description="Default signing algorithm (ES384 or RS256)") - issuer_url: Optional[str] = Field( + issuer_url: str | None = Field( default=None, description="Account's AWS STS issuer URL (populated after enabling federation)" ) duration_seconds: int = Field( @@ -91,10 +91,10 @@ def validate_signing_algorithm(cls, v: str) -> str: class IdentityConfig(BaseModel): """Identity service configuration for outbound authentication.""" - credential_providers: List[CredentialProviderInfo] = Field( + credential_providers: list[CredentialProviderInfo] = Field( default_factory=list, description="List of configured OAuth2 credential providers" ) - workload: Optional[WorkloadIdentityInfo] = Field(None, description="Workload identity configuration") + workload: WorkloadIdentityInfo | None = Field(None, description="Workload identity configuration") @property def is_enabled(self) -> bool: @@ -107,7 +107,7 @@ def has_oauth_providers(self) -> bool: return len(self.credential_providers) > 0 @property - def provider_names(self) -> List[str]: + def provider_names(self) -> list[str]: """Get list of OAuth provider names.""" return [p.name for p in self.credential_providers] @@ -116,7 +116,7 @@ class NetworkConfiguration(BaseModel): """Network configuration for BedrockAgentCore deployment.""" network_mode: str = Field(default="PUBLIC", description="Network mode for deployment") - network_mode_config: Optional[NetworkModeConfig] = Field( + network_mode_config: NetworkModeConfig | None = Field( default=None, description="Network mode configuration (required for VPC mode)" ) @@ -131,7 +131,7 @@ def validate_network_mode(cls, v: str) -> str: @field_validator("network_mode_config") @classmethod - def validate_network_mode_config(cls, v: Optional[NetworkModeConfig], info) -> Optional[NetworkModeConfig]: + def validate_network_mode_config(cls, v: NetworkModeConfig | None, info) -> NetworkModeConfig | None: """Validate that network_mode_config is provided when network_mode is VPC.""" if info.data.get("network_mode") == "VPC" and v is None: raise ValueError("network_mode_config is required when network_mode is VPC") @@ -174,19 +174,19 @@ def to_aws_dict(self) -> dict: class LifecycleConfiguration(BaseModel): """Lifecycle configuration for runtime sessions.""" - idle_runtime_session_timeout: Optional[int] = Field( + idle_runtime_session_timeout: int | None = Field( default=None, description="Timeout in seconds for idle runtime sessions (60-28800)", ge=60, le=28800, ) - max_lifetime: Optional[int] = Field( + max_lifetime: int | None = Field( default=None, description="Maximum lifetime for the instance in seconds (60-28800)", ge=60, le=28800 ) @field_validator("max_lifetime") @classmethod - def validate_lifecycle_relationship(cls, v: Optional[int], info) -> Optional[int]: + def validate_lifecycle_relationship(cls, v: int | None, info) -> int | None: """Validate that max_lifetime >= idle_timeout if both are set.""" if v is None: return v @@ -222,13 +222,13 @@ class ObservabilityConfig(BaseModel): class AWSConfig(BaseModel): """AWS-specific configuration.""" - execution_role: Optional[str] = Field(default=None, description="AWS IAM execution role ARN") + execution_role: str | None = Field(default=None, description="AWS IAM execution role ARN") execution_role_auto_create: bool = Field(default=False, description="Whether to auto-create execution role") - account: Optional[str] = Field(default=None, description="AWS account ID") - region: Optional[str] = Field(default=None, description="AWS region") - ecr_repository: Optional[str] = Field(default=None, description="ECR repository URI") + account: str | None = Field(default=None, description="AWS account ID") + region: str | None = Field(default=None, description="AWS region") + ecr_repository: str | None = Field(default=None, description="ECR repository URI") ecr_auto_create: bool = Field(default=False, description="Whether to auto-create ECR repository") - s3_path: Optional[str] = Field(default=None, description="S3 URI for code deployment") + s3_path: str | None = Field(default=None, description="S3 URI for code deployment") s3_auto_create: bool = Field(default=False, description="Whether to auto-create S3 bucket") network_configuration: NetworkConfiguration = Field(default_factory=NetworkConfiguration) protocol_configuration: ProtocolConfiguration = Field(default_factory=ProtocolConfiguration) @@ -237,7 +237,7 @@ class AWSConfig(BaseModel): @field_validator("account") @classmethod - def validate_account(cls, v: Optional[str]) -> Optional[str]: + def validate_account(cls, v: str | None) -> str | None: """Validate AWS account ID.""" if v is not None: if not v.isdigit() or len(v) != 12: @@ -248,17 +248,17 @@ def validate_account(cls, v: Optional[str]) -> Optional[str]: class CodeBuildConfig(BaseModel): """CodeBuild deployment information.""" - project_name: Optional[str] = Field(default=None, description="CodeBuild project name") - execution_role: Optional[str] = Field(default=None, description="CodeBuild execution role ARN") - source_bucket: Optional[str] = Field(default=None, description="S3 source bucket name") + project_name: str | None = Field(default=None, description="CodeBuild project name") + execution_role: str | None = Field(default=None, description="CodeBuild execution role ARN") + source_bucket: str | None = Field(default=None, description="S3 source bucket name") class BedrockAgentCoreDeploymentInfo(BaseModel): """BedrockAgentCore deployment information.""" - agent_id: Optional[str] = Field(default=None, description="BedrockAgentCore agent ID") - agent_arn: Optional[str] = Field(default=None, description="BedrockAgentCore agent ARN") - agent_session_id: Optional[str] = Field(default=None, description="Session ID for invocations") + agent_id: str | None = Field(default=None, description="BedrockAgentCore agent ID") + agent_arn: str | None = Field(default=None, description="BedrockAgentCore agent ARN") + agent_session_id: str | None = Field(default=None, description="Session ID for invocations") class BedrockAgentCoreAgentSchema(BaseModel): @@ -266,38 +266,38 @@ class BedrockAgentCoreAgentSchema(BaseModel): name: str = Field(..., description="Name of the Bedrock AgentCore application") language: Literal["python", "typescript"] = Field(default="python", description="Programming language of the agent") - node_version: Optional[str] = Field( + node_version: str | None = Field( default=None, description="Node.js major version for TypeScript agents (e.g., '20', '22')" ) entrypoint: str = Field(..., description="Entrypoint file path (e.g., 'agent.py' or 'agent.py:handler')") deployment_type: Literal["container", "direct_code_deploy"] = Field( default="container", description="Deployment artifact type: container (Docker) or direct_code_deploy" ) - runtime_type: Optional[str] = Field( + runtime_type: str | None = Field( default=None, description="Managed runtime version for direct_code_deploy (e.g., 'PYTHON_3_10', 'PYTHON_3_11')" ) platform: str = Field(default="linux/amd64", description="Target platform (for container deployments)") - container_runtime: Optional[str] = Field( + container_runtime: str | None = Field( default=None, description="Container runtime to use (for container deployments)" ) - source_path: Optional[str] = Field(default=None, description="Directory containing agent source code") + source_path: str | None = Field(default=None, description="Directory containing agent source code") aws: AWSConfig = Field(default_factory=AWSConfig) bedrock_agentcore: BedrockAgentCoreDeploymentInfo = Field(default_factory=BedrockAgentCoreDeploymentInfo) codebuild: CodeBuildConfig = Field(default_factory=CodeBuildConfig) memory: MemoryConfig = Field(default_factory=MemoryConfig) identity: IdentityConfig = Field(default_factory=IdentityConfig) aws_jwt: AwsJwtConfig = Field(default_factory=AwsJwtConfig) - authorizer_configuration: Optional[dict] = Field(default=None, description="JWT authorizer configuration") - request_header_configuration: Optional[dict] = Field(default=None, description="Request header configuration") - oauth_configuration: Optional[dict] = Field(default=None, description="Oauth configuration") - api_key_env_var_name: Optional[str] = Field( + authorizer_configuration: dict | None = Field(default=None, description="JWT authorizer configuration") + request_header_configuration: dict | None = Field(default=None, description="Request header configuration") + oauth_configuration: dict | None = Field(default=None, description="Oauth configuration") + api_key_env_var_name: str | None = Field( default=None, description="Environment variable name for API key (e.g., 'OPENAI_API_KEY' for non-Bedrock providers)", ) - api_key_credential_provider_name: Optional[str] = Field( + api_key_credential_provider_name: str | None = Field( default=None, description="Name of the API Key Credential Provider created in AgentCore Identity" ) - is_generated_by_agentcore_create: Optional[bool] = Field( + is_generated_by_agentcore_create: bool | None = Field( default=False, description="True if the agent is created with agentcore create" ) @@ -308,11 +308,11 @@ def validate_typescript_deployment(self) -> "BedrockAgentCoreAgentSchema": raise ValueError("TypeScript agents require container deployment (direct_code_deploy not supported)") return self - def get_authorizer_configuration(self) -> Optional[dict]: + def get_authorizer_configuration(self) -> dict | None: """Get the authorizer configuration.""" return self.authorizer_configuration - def validate(self, for_local: bool = False) -> List[str]: + def validate(self, for_local: bool = False) -> list[str]: """Validate configuration and return list of errors. Args: @@ -349,15 +349,15 @@ class BedrockAgentCoreConfigSchema(BaseModel): Operations use --agent parameter to select which agent to work with. """ - default_agent: Optional[str] = Field(default=None, description="Default agent name for operations") - is_agentcore_create_with_iac: Optional[bool] = Field( + default_agent: str | None = Field(default=None, description="Default agent name for operations") + is_agentcore_create_with_iac: bool | None = Field( default=False ) # will only be provided by projects created from agentcore create - agents: Dict[str, BedrockAgentCoreAgentSchema] = Field( + agents: dict[str, BedrockAgentCoreAgentSchema] = Field( default_factory=dict, description="Named agent configurations" ) - def get_agent_config(self, agent_name: Optional[str] = None) -> BedrockAgentCoreAgentSchema: + def get_agent_config(self, agent_name: str | None = None) -> BedrockAgentCoreAgentSchema: """Get agent config by name or default. Args: diff --git a/src/bedrock_agentcore_starter_toolkit/utils/server_addresses.py b/src/bedrock_agentcore_starter_toolkit/utils/server_addresses.py index 39f5b1c7..38a5e8a2 100644 --- a/src/bedrock_agentcore_starter_toolkit/utils/server_addresses.py +++ b/src/bedrock_agentcore_starter_toolkit/utils/server_addresses.py @@ -3,15 +3,14 @@ from __future__ import annotations import socket -from typing import List, Optional, Tuple -ServerUrl = Tuple[str, str] +ServerUrl = tuple[str, str] -def build_server_urls(port: int, *, path_suffix: str = "", protocol: str = "http") -> List[ServerUrl]: +def build_server_urls(port: int, *, path_suffix: str = "", protocol: str = "http") -> list[ServerUrl]: """Return URLs that are reachable when binding to 0.0.0.0.""" suffix = _normalize_path_suffix(path_suffix) - urls: List[ServerUrl] = [ + urls: list[ServerUrl] = [ ("Localhost", f"{protocol}://localhost:{port}{suffix}"), ("127.0.0.1", f"{protocol}://127.0.0.1:{port}{suffix}"), ] @@ -29,7 +28,7 @@ def _normalize_path_suffix(path_suffix: str) -> str: return path_suffix if path_suffix.startswith("/") else f"/{path_suffix}" -def _detect_local_network_ip() -> Optional[str]: +def _detect_local_network_ip() -> str | None: """Best-effort detection of an externally reachable LAN IP.""" try: with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock: diff --git a/tests/cli/observability/test_commands.py b/tests/cli/observability/test_commands.py index 964266a8..d44a969a 100644 --- a/tests/cli/observability/test_commands.py +++ b/tests/cli/observability/test_commands.py @@ -747,7 +747,7 @@ def test_show_output_handles_export_error(self, mock_path, mock_config, mock_cli mock_client_class.return_value = mock_client # Mock file operation to raise error - mock_path.return_value.open.side_effect = IOError("Cannot write file") + mock_path.return_value.open.side_effect = OSError("Cannot write file") result = runner.invoke(observability_app, ["show", "--trace-id", "error-export", "--output", "bad-path.json"]) diff --git a/tests/create/test_helper/run_create_with_config.py b/tests/create/test_helper/run_create_with_config.py index bb2eeaf5..0b13ce0c 100644 --- a/tests/create/test_helper/run_create_with_config.py +++ b/tests/create/test_helper/run_create_with_config.py @@ -1,6 +1,5 @@ import shutil from pathlib import Path -from typing import Optional from typer.testing import CliRunner @@ -15,7 +14,7 @@ test_runner = CliRunner() -def run_create_with_config(tmp_path, monkeypatch, scenario, iac: Optional[IACProvider]) -> tuple[Path, ScenarioConfig]: +def run_create_with_config(tmp_path, monkeypatch, scenario, iac: IACProvider | None) -> tuple[Path, ScenarioConfig]: """Runs the CLI generator and returns the project directory and the ScenarioConfig used""" scenario_config = IAC_WITH_CONFIG_SCENARIOS[scenario] sdk = scenario_config.sdk diff --git a/tests/services/import_agent/test_import_agent.py b/tests/services/import_agent/test_import_agent.py index 2b741694..4dacb87b 100644 --- a/tests/services/import_agent/test_import_agent.py +++ b/tests/services/import_agent/test_import_agent.py @@ -338,7 +338,7 @@ def test_bedrock_to_strands(self, enhanced_mock_boto3_clients): base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__))) agent_config = json.load( - open(os.path.join(base_dir, "data", "bedrock_config_multi_agent.json"), "r", encoding="utf-8") + open(os.path.join(base_dir, "data", "bedrock_config_multi_agent.json"), encoding="utf-8") ) output_dir = os.path.join(base_dir, "output", "strands") os.makedirs(output_dir, exist_ok=True) @@ -352,7 +352,7 @@ def test_bedrock_to_langchain(self, enhanced_mock_boto3_clients): base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__))) agent_config = json.load( - open(os.path.join(base_dir, "data", "bedrock_config_multi_agent.json"), "r", encoding="utf-8") + open(os.path.join(base_dir, "data", "bedrock_config_multi_agent.json"), encoding="utf-8") ) output_dir = os.path.join(base_dir, "output", "langchain") os.makedirs(output_dir, exist_ok=True) @@ -444,7 +444,7 @@ def mock_client_side_effect(service_name, **kwargs): mock_gateway_client.get_access_token_for_cognito.return_value = "test-access-token" base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__))) - agent_config = json.load(open(os.path.join(base_dir, "data", "bedrock_config.json"), "r", encoding="utf-8")) + agent_config = json.load(open(os.path.join(base_dir, "data", "bedrock_config.json"), encoding="utf-8")) output_dir = os.path.join(base_dir, "output", "langchain_with_primitives") os.makedirs(output_dir, exist_ok=True) @@ -550,7 +550,7 @@ def mock_client_side_effect(service_name, **kwargs): mock_gateway_client.get_access_token_for_cognito.return_value = "test-access-token" base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__))) - agent_config = json.load(open(os.path.join(base_dir, "data", "bedrock_config.json"), "r", encoding="utf-8")) + agent_config = json.load(open(os.path.join(base_dir, "data", "bedrock_config.json"), encoding="utf-8")) output_dir = os.path.join(base_dir, "output", "strands_with_primitives") os.makedirs(output_dir, exist_ok=True) @@ -576,7 +576,7 @@ def mock_client_side_effect(service_name, **kwargs): def test_bedrock_to_langchain_with_function_schema_no_gateway(self, enhanced_mock_boto3_clients): """Test Bedrock to LangChain import with function schema action groups but no gateway.""" base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__))) - agent_config = json.load(open(os.path.join(base_dir, "data", "bedrock_config.json"), "r", encoding="utf-8")) + agent_config = json.load(open(os.path.join(base_dir, "data", "bedrock_config.json"), encoding="utf-8")) output_dir = os.path.join(base_dir, "output", "langchain_function_schema") os.makedirs(output_dir, exist_ok=True) @@ -592,7 +592,7 @@ def test_bedrock_to_langchain_with_function_schema_no_gateway(self, enhanced_moc def test_bedrock_to_strands_with_function_schema_no_gateway(self, enhanced_mock_boto3_clients): """Test Bedrock to Strands import with function schema action groups but no gateway.""" base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__))) - agent_config = json.load(open(os.path.join(base_dir, "data", "bedrock_config.json"), "r", encoding="utf-8")) + agent_config = json.load(open(os.path.join(base_dir, "data", "bedrock_config.json"), encoding="utf-8")) output_dir = os.path.join(base_dir, "output", "strands_function_schema") os.makedirs(output_dir, exist_ok=True) @@ -609,7 +609,7 @@ def test_bedrock_to_langchain_with_no_schema_action_group(self, enhanced_mock_bo """Test Bedrock to LangChain import with action group that has no schema (to cover branch coverage).""" base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__))) agent_config = json.load( - open(os.path.join(base_dir, "data", "bedrock_config_no_schema.json"), "r", encoding="utf-8") + open(os.path.join(base_dir, "data", "bedrock_config_no_schema.json"), encoding="utf-8") ) output_dir = os.path.join(base_dir, "output", "langchain_no_schema") os.makedirs(output_dir, exist_ok=True) diff --git a/tests_integ/cli/identity/test_identity_aws_jwt.py b/tests_integ/cli/identity/test_identity_aws_jwt.py index fc4fbab2..1137464a 100644 --- a/tests_integ/cli/identity/test_identity_aws_jwt.py +++ b/tests_integ/cli/identity/test_identity_aws_jwt.py @@ -2,7 +2,6 @@ import os import re from pathlib import Path -from typing import List from unittest.mock import patch from click.testing import Result @@ -29,7 +28,7 @@ def setup(self): self.audience = "https://api.example.com" self.issuer_url = None - def get_command_invocations(self) -> List[CommandInvocation]: + def get_command_invocations(self) -> list[CommandInvocation]: """Test AWS JWT-specific commands.""" return [ # Step 1: Configure agent first @@ -249,7 +248,7 @@ def setup(self): """Setup for validation tests.""" pass - def get_command_invocations(self) -> List[CommandInvocation]: + def get_command_invocations(self) -> list[CommandInvocation]: """Test validation error cases.""" return [ # Test invalid signing algorithm diff --git a/tests_integ/cli/identity/test_identity_flow.py b/tests_integ/cli/identity/test_identity_flow.py index 79ca7a97..fa242d3f 100644 --- a/tests_integ/cli/identity/test_identity_flow.py +++ b/tests_integ/cli/identity/test_identity_flow.py @@ -4,7 +4,6 @@ import re import textwrap import uuid -from typing import List import pytest from click.testing import Result @@ -57,7 +56,7 @@ async def invoke(payload, context): with open(self.requirements_file, "w") as file: file.write("bedrock-agentcore\nboto3\n") - def get_command_invocations(self) -> List[CommandInvocation]: + def get_command_invocations(self) -> list[CommandInvocation]: """Define the sequence of commands to test Identity flow (config only).""" return [ # Step 1: Setup Cognito pools @@ -172,7 +171,7 @@ def _load_cognito_config(self): return json.load(f) return None - def _build_jwt_config_command(self) -> List[str]: + def _build_jwt_config_command(self) -> list[str]: """Build configure command with JWT authorizer.""" cognito_config = self._load_cognito_config() if not cognito_config: @@ -201,7 +200,7 @@ def _build_jwt_config_command(self) -> List[str]: "--non-interactive", ] - def _build_create_provider_command(self) -> List[str]: + def _build_create_provider_command(self) -> list[str]: """Build create-credential-provider command.""" cognito_config = self._load_cognito_config() if not cognito_config: diff --git a/tests_integ/cli/identity/test_identity_m2m.py b/tests_integ/cli/identity/test_identity_m2m.py index a230e740..18b5dc2e 100644 --- a/tests_integ/cli/identity/test_identity_m2m.py +++ b/tests_integ/cli/identity/test_identity_m2m.py @@ -2,7 +2,6 @@ import logging import os import re -from typing import List from click.testing import Result @@ -29,7 +28,7 @@ def setup(self): self.runtime_pool_id = None self.identity_pool_id = None - def get_command_invocations(self) -> List[CommandInvocation]: + def get_command_invocations(self) -> list[CommandInvocation]: """Test M2M-specific commands.""" return [ # Step 1: Setup Cognito with M2M flow diff --git a/tests_integ/cli/runtime/base_test.py b/tests_integ/cli/runtime/base_test.py index ae8d6ca1..d46ce638 100644 --- a/tests_integ/cli/runtime/base_test.py +++ b/tests_integ/cli/runtime/base_test.py @@ -1,8 +1,9 @@ import logging import os from abc import ABC, abstractmethod +from collections.abc import Callable from dataclasses import dataclass -from typing import Any, Callable, List +from typing import Any from click.testing import Result from prompt_toolkit.application import create_app_session @@ -16,8 +17,8 @@ @dataclass class CommandInvocation: - command: List[str] - user_input: List[str] + command: list[str] + user_input: list[str] validator: Callable[[Result], Any] @@ -63,7 +64,7 @@ def setup(self) -> None: return @abstractmethod - def get_command_invocations(self) -> List[CommandInvocation]: + def get_command_invocations(self) -> list[CommandInvocation]: """ Get the commands to be tested. This method should be implemented by subclasses to return the specific commands. diff --git a/tests_integ/cli/runtime/test_simple_agent.py b/tests_integ/cli/runtime/test_simple_agent.py index 5d8df28b..5ed44afe 100644 --- a/tests_integ/cli/runtime/test_simple_agent.py +++ b/tests_integ/cli/runtime/test_simple_agent.py @@ -1,7 +1,6 @@ import json import logging import textwrap -from typing import List import boto3 from click.testing import Result @@ -109,7 +108,7 @@ def _setup_role_trust_policy(self): logger.error("Error updating role trust policy: %s", str(e)) raise - def get_command_invocations(self) -> List[CommandInvocation]: + def get_command_invocations(self) -> list[CommandInvocation]: configure_invocation = CommandInvocation( command=[ "configure", diff --git a/tests_integ/policy/policy_gateway_quickstart.ipynb b/tests_integ/policy/policy_gateway_quickstart.ipynb index ebb2399d..d7c83aae 100644 --- a/tests_integ/policy/policy_gateway_quickstart.ipynb +++ b/tests_integ/policy/policy_gateway_quickstart.ipynb @@ -167,7 +167,7 @@ "metadata": {}, "outputs": [], "source": [ - "with open(\"config.json\", \"r\") as f:\n", + "with open(\"config.json\") as f:\n", " config = json.load(f)\n", "\n", "gateway_client = GatewayClient(region_name=config[\"region\"])\n", @@ -389,7 +389,7 @@ ")\n", "\n", "# Update config to include second gateway\n", - "with open(\"config.json\", \"r\") as f:\n", + "with open(\"config.json\") as f:\n", " config = json.load(f)\n", "\n", "config[\"gateway2_url\"] = gateway2[\"gatewayUrl\"]\n", @@ -418,7 +418,7 @@ "metadata": {}, "outputs": [], "source": [ - "with open(\"config.json\", \"r\") as f:\n", + "with open(\"config.json\") as f:\n", " config = json.load(f)\n", "\n", "policy_client = PolicyClient(region_name=config[\"region\"])\n",