Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,4 @@ local_settings.py
.bedrock_agentcore.yaml
.dockerignore
Dockerfile
CLAUDE.md
53 changes: 26 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
<div align="center">
<div>
<a href="https://aws.amazon.com/bedrock/agentcore/">
<img width="150" height="150" alt="image" src="https://github.com/user-attachments/assets/b8b9456d-c9e2-45e1-ac5b-760f21f1ac18" />
</a>
</div>

<h1>
Bedrock AgentCore SDK
</h1>
Expand All @@ -17,13 +23,19 @@
</div>

<p>
<a href="https://github.com/aws/bedrock-agentcore-sdk-python">Python SDK</a>
β—† <a href="https://github.com/aws/bedrock-agentcore-starter-toolkit">Starter Toolkit</a>
<a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/what-is-bedrock-agentcore.html">Documentation</a>
β—† <a href="https://github.com/awslabs/amazon-bedrock-agentcore-samples">Samples</a>
β—† <a href="https://discord.gg/bedrockagentcore-preview">Discord</a>
β—† <a href="https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/bedrock-agentcore-control.html">Boto3 Python SDK</a>
β—† <a href="https://github.com/aws/bedrock-agentcore-sdk-python">Runtime Python SDK</a>
β—† <a href="https://github.com/aws/bedrock-agentcore-starter-toolkit">Starter Toolkit</a>

</p>
</div>

## Overview
Amazon Bedrock AgentCore enables you to deploy and operate highly effective agents securely, at scale using any framework and model. With Amazon Bedrock AgentCore, developers can accelerate AI agents into production with the scale, reliability, and security, critical to real-world deployment. AgentCore provides tools and capabilities to make agents more effective and capable, purpose-built infrastructure to securely scale agents, and controls to operate trustworthy agents. Amazon Bedrock AgentCore services are composable and work with popular open-source frameworks and any model, so you don’t have to choose between open-source flexibility and enterprise-grade security and reliability.

## πŸš€ From Local Development to Bedrock AgentCore

```python
Expand Down Expand Up @@ -52,38 +64,25 @@ app.run() # Ready to run on Bedrock AgentCore
- βœ… **Enterprise-grade platform** - Built-in auth, memory, observability, security
- βœ… **Production-ready deployment** - Reliable, scalable, compliant hosting

## ⚠️ Preview Status

Bedrock AgentCore SDK is currently in public preview. APIs may change as we refine the SDK.

## πŸ› οΈ Built for AI Developers

**Real-time Health Monitoring**
```python
@app.async_task # Automatically tracks background work
async def process_documents(files):
# Long-running AI processing
return results

@app.ping # Custom health status
def health_check():
return "HEALTHY" if all_services_up() else "HEALTHY_BUSY"
```

**Enterprise Platform Services**
- 🧠 **Memory** - Persistent knowledge across sessions
- πŸ”— **Gateway** - Transform APIs into MCP tools
- πŸ’» **Code Interpreter** - Secure sandboxed execution
- 🌐 **Browser** - Cloud-based web automation
- πŸ“Š **Observability** - OpenTelemetry tracing
- πŸ” **Identity** - AWS & third-party auth
## Amazon Bedrock AgentCore services
- πŸš€ **Runtime** - Secure and session isolated compute: **[Runtime Quick Start](https://aws.github.io/bedrock-agentcore-starter-toolkit/user-guide/runtime/quickstart.html)**
- 🧠 **Memory** - Persistent knowledge across sessions: **[Memory Quick Start](https://aws.github.io/bedrock-agentcore-starter-toolkit/user-guide/memory/quickstart.html)**
- πŸ”— **Gateway** - Transform APIs into MCP tools: **[Gateway Quick Start](https://aws.github.io/bedrock-agentcore-starter-toolkit/user-guide/gateway/quickstart.html)**
- πŸ’» **Code Interpreter** - Secure sandboxed execution: **[Code Interpreter Quick Start](https://aws.github.io/bedrock-agentcore-starter-toolkit/user-guide/builtin-tools/quickstart-code-interpreter.html)**
- 🌐 **Browser** - Cloud-based web automation: **[Browser Quick Start](https://aws.github.io/bedrock-agentcore-starter-toolkit/user-guide/builtin-tools/quickstart-browser.html)**
- πŸ“Š **Observability** - OpenTelemetry tracing: **[Observability Quick Start](https://aws.github.io/bedrock-agentcore-starter-toolkit/user-guide/observability/quickstart.html)**
- πŸ” **Identity** - AWS & third-party auth:

## πŸ—οΈ Deployment

**Quick Start:** Use the [Bedrock AgentCore Starter Toolkit](https://github.com/aws/bedrock-agentcore-starter-toolkit) for rapid prototyping.

**Production:** [AWS CDK](https://aws.amazon.com/cdk/) - coming soon.

## ⚠️ Preview Status

Bedrock AgentCore SDK is currently in public preview. APIs may change as we refine the SDK.

## πŸ“ License & Contributing

- **License:** Apache 2.0 - see [LICENSE.txt](LICENSE.txt)
Expand Down
57 changes: 35 additions & 22 deletions src/bedrock_agentcore/runtime/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
from starlette.applications import Starlette
from starlette.responses import JSONResponse, Response, StreamingResponse
from starlette.routing import Route
from starlette.types import Lifespan

from .context import BedrockAgentCoreContext, RequestContext
from .models import (
ACCESS_TOKEN_HEADER,
REQUEST_ID_HEADER,
SESSION_HEADER,
TASK_ACTION_CLEAR_FORCED_STATUS,
TASK_ACTION_FORCE_BUSY,
Expand All @@ -30,31 +32,34 @@
)
from .utils import convert_complex_objects

# Request context for logging
request_id_context: contextvars.ContextVar[Optional[str]] = contextvars.ContextVar("request_id", default=None)


class RequestContextFormatter(logging.Formatter):
"""Custom formatter that includes request ID in log messages."""
"""Formatter including request and session IDs."""

def format(self, record):
"""Format log record with request ID context."""
request_id = request_id_context.get()
"""Format log record with request and session ID context."""
request_id = BedrockAgentCoreContext.get_request_id()
session_id = BedrockAgentCoreContext.get_session_id()

parts = []
if request_id:
record.request_id = f"[{request_id}] "
else:
record.request_id = ""
parts.append(f"[rid:{request_id}]")
if session_id:
parts.append(f"[sid:{session_id}]")

record.request_id = " ".join(parts) + " " if parts else ""
return super().format(record)


class BedrockAgentCoreApp(Starlette):
"""Bedrock AgentCore application class that extends Starlette for AI agent deployment."""

def __init__(self, debug: bool = False):
def __init__(self, debug: bool = False, lifespan: Optional[Lifespan] = None):
"""Initialize Bedrock AgentCore application.

Args:
debug: Enable debug actions for task management (default: False)
lifespan: Optional lifespan context manager for startup/shutdown
"""
self.handlers: Dict[str, Callable] = {}
self._ping_handler: Optional[Callable] = None
Expand All @@ -67,7 +72,7 @@ def __init__(self, debug: bool = False):
Route("/invocations", self._handle_invocation, methods=["POST"]),
Route("/ping", self._handle_ping, methods=["GET"]),
]
super().__init__(routes=routes)
super().__init__(routes=routes, lifespan=lifespan)
self.debug = debug # Set after super().__init__ to avoid override

self.logger = logging.getLogger("bedrock_agentcore.app")
Expand All @@ -76,7 +81,7 @@ def __init__(self, debug: bool = False):
formatter = RequestContextFormatter("%(asctime)s - %(name)s - %(levelname)s - %(request_id)s%(message)s")
handler.setFormatter(formatter)
self.logger.addHandler(handler)
self.logger.setLevel(logging.INFO)
self.logger.setLevel(logging.DEBUG if self.debug else logging.INFO)

def entrypoint(self, func: Callable) -> Callable:
"""Decorator to register a function as the main entrypoint.
Expand Down Expand Up @@ -246,17 +251,25 @@ def complete_async_task(self, task_id: int) -> bool:
return False

def _build_request_context(self, request) -> RequestContext:
"""Build request context and setup auth if present."""
"""Build request context and setup all context variables."""
try:
agent_identity_token = request.headers.get(ACCESS_TOKEN_HEADER) or request.headers.get(
ACCESS_TOKEN_HEADER.lower()
)
headers = request.headers
request_id = headers.get(REQUEST_ID_HEADER)
if not request_id:
request_id = str(uuid.uuid4())

session_id = headers.get(SESSION_HEADER)
BedrockAgentCoreContext.set_request_context(request_id, session_id)

agent_identity_token = headers.get(ACCESS_TOKEN_HEADER)
if agent_identity_token:
BedrockAgentCoreContext.set_workload_access_token(agent_identity_token)
session_id = request.headers.get(SESSION_HEADER) or request.headers.get(SESSION_HEADER.lower())

return RequestContext(session_id=session_id)
except Exception as e:
self.logger.warning("Failed to build request context: %s: %s", type(e).__name__, e)
request_id = str(uuid.uuid4())
BedrockAgentCoreContext.set_request_context(request_id, None)
return RequestContext(session_id=None)

def _takes_context(self, handler: Callable) -> bool:
Expand All @@ -267,8 +280,8 @@ def _takes_context(self, handler: Callable) -> bool:
return False

async def _handle_invocation(self, request):
request_id = str(uuid.uuid4())[:8]
request_id_context.set(request_id)
request_context = self._build_request_context(request)

start_time = time.time()

try:
Expand All @@ -287,7 +300,6 @@ async def _handle_invocation(self, request):
self.logger.error("No entrypoint defined")
return JSONResponse({"error": "No entrypoint defined"}, status_code=500)

request_context = self._build_request_context(request)
takes_context = self._takes_context(handler)

handler_name = handler.__name__ if hasattr(handler, "__name__") else "unknown"
Expand Down Expand Up @@ -341,7 +353,7 @@ def run(self, port: int = 8080, host: Optional[str] = None):
host = "0.0.0.0" # nosec B104 - Docker needs this to expose the port
else:
host = "127.0.0.1"
uvicorn.run(self, host=host, port=port)
uvicorn.run(self, host=host, port=port, access_log=self.debug, log_level="info" if self.debug else "warning")

async def _invoke_handler(self, handler, request_context, takes_context, payload):
try:
Expand All @@ -351,7 +363,8 @@ async def _invoke_handler(self, handler, request_context, takes_context, payload
return await handler(*args)
else:
loop = asyncio.get_event_loop()
return await loop.run_in_executor(None, handler, *args)
ctx = contextvars.copy_context()
return await loop.run_in_executor(None, ctx.run, handler, *args)
except Exception as e:
handler_name = getattr(handler, "__name__", "unknown")
self.logger.error("Handler '%s' execution failed: %s: %s", handler_name, type(e).__name__, e)
Expand Down
28 changes: 26 additions & 2 deletions src/bedrock_agentcore/runtime/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ class RequestContext(BaseModel):


class BedrockAgentCoreContext:
"""Context manager for Bedrock AgentCore."""
"""Unified context manager for Bedrock AgentCore."""

_workload_access_token: ContextVar[str] = ContextVar("workload_access_token")
_workload_access_token: ContextVar[Optional[str]] = ContextVar("workload_access_token")
_request_id: ContextVar[Optional[str]] = ContextVar("request_id")
_session_id: ContextVar[Optional[str]] = ContextVar("session_id")

@classmethod
def set_workload_access_token(cls, token: str):
Expand All @@ -32,3 +34,25 @@ def get_workload_access_token(cls) -> Optional[str]:
return cls._workload_access_token.get()
except LookupError:
return None

@classmethod
def set_request_context(cls, request_id: str, session_id: Optional[str] = None):
"""Set request-scoped identifiers."""
cls._request_id.set(request_id)
cls._session_id.set(session_id)

@classmethod
def get_request_id(cls) -> Optional[str]:
"""Get current request ID."""
try:
return cls._request_id.get()
except LookupError:
return None

@classmethod
def get_session_id(cls) -> Optional[str]:
"""Get current session ID."""
try:
return cls._session_id.get()
except LookupError:
return None
1 change: 1 addition & 0 deletions src/bedrock_agentcore/runtime/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class PingStatus(str, Enum):

# Header constants
SESSION_HEADER = "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id"
REQUEST_ID_HEADER = "X-Amzn-Bedrock-AgentCore-Runtime-Request-Id"
ACCESS_TOKEN_HEADER = "WorkloadAccessToken" # nosec

# Task action constants
Expand Down
Loading
Loading