Skip to content

Commit 23bb3f2

Browse files
nx-plugin-for-awsnx-plugin-for-awsgithub-actions[bot]
authored
fix(py#strands-agent): extract session ID from header instead of request body (#557)
* fix(py#strands-agent): extract session ID from header instead of request body Align the Python strands agent with the TypeScript implementation by reading the session ID from the `x-amzn-bedrock-agentcore-runtime-session-id` HTTP header (the Bedrock AgentCore standard) instead of requiring it in the POST request body. A random UUID is generated as fallback when the header is absent. Also renames the input field from `prompt` to `message` for consistency with the TypeScript agent's input schema. * docs: update translations * fix(py#strands-agent): use Request to extract session ID header Use FastAPI's Request object instead of Header() dependency to extract the session ID from the x-amzn-bedrock-agentcore-runtime-session-id header. This avoids the header appearing in the OpenAPI spec, which caused @hey-api/openapi-ts to generate invalid TypeScript types for the long hyphenated header name. * docs: update translations --------- Co-authored-by: nx-plugin-for-aws <nx-plugin@amazon.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent e9cdfb3 commit 23bb3f2

22 files changed

Lines changed: 208 additions & 138 deletions

File tree

docs/src/content/docs/en/get_started/tutorials/dungeon-game/1.mdx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -454,27 +454,31 @@ This creates an example Strands agent and defines an addition tool.
454454

455455
```python
456456
# agent/main.py
457+
import uuid
458+
457459
import uvicorn
458460
from bedrock_agentcore.runtime.models import PingStatus
461+
from fastapi import Request
459462
from pydantic import BaseModel
460463

461464
from .agent import get_agent
462465
from .init import JsonStreamingResponse, app
463466

467+
SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
468+
464469

465470
class InvokeInput(BaseModel):
466-
prompt: str
467-
session_id: str
471+
message: str
468472

469473

470474
class StreamChunk(BaseModel):
471475
content: str
472476

473477

474-
async def handle_invoke(input: InvokeInput):
478+
async def handle_invoke(input: InvokeInput, session_id: str):
475479
"""Streaming handler for agent invocation"""
476-
with get_agent(session_id=input.session_id) as agent:
477-
stream = agent.stream_async(input.prompt)
480+
with get_agent(session_id=session_id) as agent:
481+
stream = agent.stream_async(input.message)
478482
async for event in stream:
479483
print(event)
480484
text = event.get("event", {}).get("contentBlockDelta", {}).get("delta", {}).get("text")
@@ -489,9 +493,10 @@ async def handle_invoke(input: InvokeInput):
489493
response_class=JsonStreamingResponse,
490494
responses={200: JsonStreamingResponse.openapi_response(StreamChunk, "Stream of agent response chunks")},
491495
)
492-
async def invoke(input: InvokeInput) -> JsonStreamingResponse:
496+
async def invoke(input: InvokeInput, request: Request) -> JsonStreamingResponse:
493497
"""Entry point for agent invocation"""
494-
return JsonStreamingResponse(handle_invoke(input))
498+
session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
499+
return JsonStreamingResponse(handle_invoke(input, session_id))
495500

496501

497502
@app.get("/ping")

docs/src/content/docs/en/guides/py-strands-agent.mdx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,18 +191,19 @@ The agent's invocation endpoint uses [Pydantic](https://docs.pydantic.dev/) mode
191191

192192
#### Defining Input Models
193193

194-
The default `InvokeInput` model accepts a prompt and session ID.
194+
The default `InvokeInput` model accepts a message.
195195

196196
```python
197197
from pydantic import BaseModel
198198

199199
class InvokeInput(BaseModel):
200-
prompt: str
201-
session_id: str
200+
message: str
202201
```
203202

204203
You can extend this model to include any additional fields your agent needs.
205204

205+
The session ID is extracted from the `x-amzn-bedrock-agentcore-runtime-session-id` HTTP header, consistent with the [Bedrock AgentCore Runtime session contract](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-service-contract.html). If the header is not provided, a random UUID is generated as a fallback.
206+
206207
:::caution[Code Organization]
207208
You will likely want to abstract some or all of the session ID from the caller if users are to invoke your agent directly. For example, you may use the authenticated user ID as part of the session ID.
208209

@@ -302,7 +303,7 @@ To invoke an Agent running locally via the `<your-agent-name>-serve` target, you
302303

303304
```bash
304305
curl -N -X POST http://localhost:8081/invocations \
305-
-d '{"prompt": "what is 3 + 5?", "session_id": "abcdefghijklmnopqrstuvwxyz0123456789"}' \
306+
-d '{"message": "what is 3 + 5?"}' \
306307
-H "Content-Type: application/json"
307308
```
308309

@@ -337,7 +338,7 @@ For Cognito Authentication, pass the Cognito Access Token in the `Authorization`
337338
```bash
338339
curl -N -X POST 'https://bedrock
339340
-agentcore.<region>.amazonaws.com/runtimes/<url-encoded-arn>/invocations' \
340-
-d '{"prompt": "what is 3 + 5?", "session_id": "abcdefghijklmnopqrstuvwxyz0123456789"}' \
341+
-d '{"message": "what is 3 + 5?"}' \
341342
-H "Content-Type: application/json" \
342343
-H "Authorization: Bearer <access-token>"
343344
```

docs/src/content/docs/es/get_started/tutorials/dungeon-game/1.mdx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -454,27 +454,31 @@ Esto crea un agente Strands de ejemplo y define una herramienta de suma.
454454

455455
```python
456456
# agent/main.py
457+
import uuid
458+
457459
import uvicorn
458460
from bedrock_agentcore.runtime.models import PingStatus
461+
from fastapi import Request
459462
from pydantic import BaseModel
460463

461464
from .agent import get_agent
462465
from .init import JsonStreamingResponse, app
463466

467+
SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
468+
464469

465470
class InvokeInput(BaseModel):
466-
prompt: str
467-
session_id: str
471+
message: str
468472

469473

470474
class StreamChunk(BaseModel):
471475
content: str
472476

473477

474-
async def handle_invoke(input: InvokeInput):
478+
async def handle_invoke(input: InvokeInput, session_id: str):
475479
"""Manejador de streaming para invocación del agente"""
476-
with get_agent(session_id=input.session_id) as agent:
477-
stream = agent.stream_async(input.prompt)
480+
with get_agent(session_id=session_id) as agent:
481+
stream = agent.stream_async(input.message)
478482
async for event in stream:
479483
print(event)
480484
text = event.get("event", {}).get("contentBlockDelta", {}).get("delta", {}).get("text")
@@ -489,9 +493,10 @@ async def handle_invoke(input: InvokeInput):
489493
response_class=JsonStreamingResponse,
490494
responses={200: JsonStreamingResponse.openapi_response(StreamChunk, "Stream of agent response chunks")},
491495
)
492-
async def invoke(input: InvokeInput) -> JsonStreamingResponse:
496+
async def invoke(input: InvokeInput, request: Request) -> JsonStreamingResponse:
493497
"""Punto de entrada para invocación del agente"""
494-
return JsonStreamingResponse(handle_invoke(input))
498+
session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
499+
return JsonStreamingResponse(handle_invoke(input, session_id))
495500

496501

497502
@app.get("/ping")

docs/src/content/docs/es/guides/py-strands-agent.mdx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,18 +191,19 @@ El endpoint de invocación del agente usa modelos [Pydantic](https://docs.pydant
191191

192192
#### Definiendo Modelos de Entrada
193193

194-
El modelo `InvokeInput` por defecto acepta un prompt y un ID de sesión.
194+
El modelo `InvokeInput` por defecto acepta un mensaje.
195195

196196
```python
197197
from pydantic import BaseModel
198198

199199
class InvokeInput(BaseModel):
200-
prompt: str
201-
session_id: str
200+
message: str
202201
```
203202

204203
Puedes extender este modelo para incluir cualquier campo adicional que tu agente necesite.
205204

205+
El ID de sesión se extrae del header HTTP `x-amzn-bedrock-agentcore-runtime-session-id`, de acuerdo con el [contrato de sesión de Bedrock AgentCore Runtime](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-service-contract.html). Si el header no se proporciona, se genera un UUID aleatorio como alternativa.
206+
206207
:::caution[Organización del código]
207208
Probablemente querrás abstraer parte o todo el ID de sesión del llamador si los usuarios van a invocar tu agente directamente. Por ejemplo, puedes usar el ID de usuario autenticado como parte del ID de sesión.
208209

@@ -300,7 +301,7 @@ Para invocar un Agente ejecutándose localmente mediante el target `<your-agent-
300301

301302
```bash
302303
curl -N -X POST http://localhost:8081/invocations \
303-
-d '{"prompt": "what is 3 + 5?", "session_id": "abcdefghijklmnopqrstuvwxyz0123456789"}' \
304+
-d '{"message": "what is 3 + 5?"}' \
304305
-H "Content-Type: application/json"
305306
```
306307

@@ -335,7 +336,7 @@ Para autenticación Cognito, pasa el Access Token de Cognito en el header `Autho
335336
```bash
336337
curl -N -X POST 'https://bedrock
337338
-agentcore.<region>.amazonaws.com/runtimes/<url-encoded-arn>/invocations' \
338-
-d '{"prompt": "what is 3 + 5?", "session_id": "abcdefghijklmnopqrstuvwxyz0123456789"}' \
339+
-d '{"message": "what is 3 + 5?"}' \
339340
-H "Content-Type: application/json" \
340341
-H "Authorization: Bearer <access-token>"
341342
```

docs/src/content/docs/fr/get_started/tutorials/dungeon-game/1.mdx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -455,27 +455,31 @@ Cela crée un exemple d'agent Strands et définit un outil d'addition.
455455

456456
```python
457457
# agent/main.py
458+
import uuid
459+
458460
import uvicorn
459461
from bedrock_agentcore.runtime.models import PingStatus
462+
from fastapi import Request
460463
from pydantic import BaseModel
461464

462465
from .agent import get_agent
463466
from .init import JsonStreamingResponse, app
464467

468+
SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
469+
465470

466471
class InvokeInput(BaseModel):
467-
prompt: str
468-
session_id: str
472+
message: str
469473

470474

471475
class StreamChunk(BaseModel):
472476
content: str
473477

474478

475-
async def handle_invoke(input: InvokeInput):
479+
async def handle_invoke(input: InvokeInput, session_id: str):
476480
"""Handler de streaming pour l'invocation de l'agent"""
477-
with get_agent(session_id=input.session_id) as agent:
478-
stream = agent.stream_async(input.prompt)
481+
with get_agent(session_id=session_id) as agent:
482+
stream = agent.stream_async(input.message)
479483
async for event in stream:
480484
print(event)
481485
text = event.get("event", {}).get("contentBlockDelta", {}).get("delta", {}).get("text")
@@ -490,9 +494,10 @@ async def handle_invoke(input: InvokeInput):
490494
response_class=JsonStreamingResponse,
491495
responses={200: JsonStreamingResponse.openapi_response(StreamChunk, "Stream of agent response chunks")},
492496
)
493-
async def invoke(input: InvokeInput) -> JsonStreamingResponse:
497+
async def invoke(input: InvokeInput, request: Request) -> JsonStreamingResponse:
494498
"""Point d'entrée pour l'invocation de l'agent"""
495-
return JsonStreamingResponse(handle_invoke(input))
499+
session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
500+
return JsonStreamingResponse(handle_invoke(input, session_id))
496501

497502

498503
@app.get("/ping")

docs/src/content/docs/fr/guides/py-strands-agent.mdx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,18 +191,19 @@ Le point de terminaison d'invocation de l'agent utilise des modèles [Pydantic](
191191

192192
#### Définition des modèles d'entrée
193193

194-
Le modèle `InvokeInput` par défaut accepte un prompt et un ID de session.
194+
Le modèle `InvokeInput` par défaut accepte un message.
195195

196196
```python
197197
from pydantic import BaseModel
198198

199199
class InvokeInput(BaseModel):
200-
prompt: str
201-
session_id: str
200+
message: str
202201
```
203202

204203
Vous pouvez étendre ce modèle pour inclure tous les champs supplémentaires dont votre agent a besoin.
205204

205+
L'ID de session est extrait de l'en-tête HTTP `x-amzn-bedrock-agentcore-runtime-session-id`, conformément au [contrat de session Bedrock AgentCore Runtime](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-service-contract.html). Si l'en-tête n'est pas fourni, un UUID aléatoire est généré par défaut.
206+
206207
:::caution[Organisation du code]
207208
Vous voudrez probablement abstraire tout ou partie de l'ID de session de l'appelant si les utilisateurs doivent invoquer votre agent directement. Par exemple, vous pouvez utiliser l'ID utilisateur authentifié comme partie de l'ID de session.
208209

@@ -302,7 +303,7 @@ Pour invoquer un agent exécuté localement via la cible `<your-agent-name>-serv
302303

303304
```bash
304305
curl -N -X POST http://localhost:8081/invocations \
305-
-d '{"prompt": "what is 3 + 5?", "session_id": "abcdefghijklmnopqrstuvwxyz0123456789"}' \
306+
-d '{"message": "what is 3 + 5?"}' \
306307
-H "Content-Type: application/json"
307308
```
308309

@@ -337,7 +338,7 @@ Pour l'authentification Cognito, passez le jeton d'accès Cognito dans l'en-têt
337338
```bash
338339
curl -N -X POST 'https://bedrock
339340
-agentcore.<region>.amazonaws.com/runtimes/<url-encoded-arn>/invocations' \
340-
-d '{"prompt": "what is 3 + 5?", "session_id": "abcdefghijklmnopqrstuvwxyz0123456789"}' \
341+
-d '{"message": "what is 3 + 5?"}' \
341342
-H "Content-Type: application/json" \
342343
-H "Authorization: Bearer <access-token>"
343344
```

docs/src/content/docs/it/get_started/tutorials/dungeon-game/1.mdx

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Questo configurerà un monorepo NX all'interno della directory `dungeon-adventur
5454

5555
Ora possiamo iniziare a creare i diversi sotto-progetti utilizzando `@aws/nx-plugin`.
5656

57-
<Aside type="tip">È una best practice assicurarsi che tutti i file non staged siano committati in Git prima di eseguire qualsiasi generatore. Questo permette di visualizzare le modifiche dopo l'esecuzione tramite `git diff`.</Aside>
57+
<Aside type="tip" title="Committa spesso">È una best practice assicurarsi che tutti i file non staged siano committati in Git prima di eseguire qualsiasi generatore. Questo permette di visualizzare le modifiche dopo l'esecuzione tramite `git diff`.</Aside>
5858

5959
## Task 2: Creare una Game API
6060

@@ -454,27 +454,31 @@ Questo crea un agente Strands di esempio e definisce uno strumento di addizione.
454454

455455
```python
456456
# agent/main.py
457+
import uuid
458+
457459
import uvicorn
458460
from bedrock_agentcore.runtime.models import PingStatus
461+
from fastapi import Request
459462
from pydantic import BaseModel
460463

461464
from .agent import get_agent
462465
from .init import JsonStreamingResponse, app
463466

467+
SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
468+
464469

465470
class InvokeInput(BaseModel):
466-
prompt: str
467-
session_id: str
471+
message: str
468472

469473

470474
class StreamChunk(BaseModel):
471475
content: str
472476

473477

474-
async def handle_invoke(input: InvokeInput):
478+
async def handle_invoke(input: InvokeInput, session_id: str):
475479
"""Handler di streaming per l'invocazione dell'agente"""
476-
with get_agent(session_id=input.session_id) as agent:
477-
stream = agent.stream_async(input.prompt)
480+
with get_agent(session_id=session_id) as agent:
481+
stream = agent.stream_async(input.message)
478482
async for event in stream:
479483
print(event)
480484
text = event.get("event", {}).get("contentBlockDelta", {}).get("delta", {}).get("text")
@@ -489,9 +493,10 @@ async def handle_invoke(input: InvokeInput):
489493
response_class=JsonStreamingResponse,
490494
responses={200: JsonStreamingResponse.openapi_response(StreamChunk, "Stream of agent response chunks")},
491495
)
492-
async def invoke(input: InvokeInput) -> JsonStreamingResponse:
496+
async def invoke(input: InvokeInput, request: Request) -> JsonStreamingResponse:
493497
"""Entry point per l'invocazione dell'agente"""
494-
return JsonStreamingResponse(handle_invoke(input))
498+
session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
499+
return JsonStreamingResponse(handle_invoke(input, session_id))
495500

496501

497502
@app.get("/ping")
@@ -1096,7 +1101,7 @@ new ApplicationStage(app, 'dungeon-adventure-infra-sandbox', {
10961101
app.synth();
10971102
```
10981103

1099-
<Aside type="tip">Se vedi un errore di import nel tuo IDE, è perché il progetto infrastruttura non ha ancora un riferimento TypeScript configurato nel `tsconfig.json`. Nx è stato [configurato](https://nx.dev/nx-api/js/generators/typescript-sync) per creare questi riferimenti *dinamicamente* ogni volta che viene eseguita una build/compilazione o se esegui manualmente il comando `nx sync`. Per maggiori informazioni consulta la <Link path="guides/typescript-project#importing-your-library-code-in-other-projects">guida TypeScript</Link>.</Aside>
1104+
<Aside type="tip" title="Correggere errori di import">Se vedi un errore di import nel tuo IDE, è perché il progetto infrastruttura non ha ancora un riferimento TypeScript configurato nel `tsconfig.json`. Nx è stato [configurato](https://nx.dev/nx-api/js/generators/typescript-sync) per creare questi riferimenti *dinamicamente* ogni volta che viene eseguita una build/compilazione o se esegui manualmente il comando `nx sync`. Per maggiori informazioni consulta la <Link path="guides/typescript-project#importing-your-library-code-in-other-projects">guida TypeScript</Link>.</Aside>
11001105

11011106
Questo è l'entry point per l'applicazione CDK.
11021107

docs/src/content/docs/it/guides/py-strands-agent.mdx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,18 +191,19 @@ L'endpoint di invocazione dell'agente utilizza modelli [Pydantic](https://docs.p
191191

192192
#### Definire Modelli di Input
193193

194-
Il modello `InvokeInput` predefinito accetta un prompt e un ID di sessione.
194+
Il modello `InvokeInput` predefinito accetta un messaggio.
195195

196196
```python
197197
from pydantic import BaseModel
198198

199199
class InvokeInput(BaseModel):
200-
prompt: str
201-
session_id: str
200+
message: str
202201
```
203202

204203
Puoi estendere questo modello per includere qualsiasi campo aggiuntivo di cui il tuo agente ha bisogno.
205204

205+
L'ID di sessione viene estratto dall'header HTTP `x-amzn-bedrock-agentcore-runtime-session-id`, coerentemente con il [contratto di sessione di Bedrock AgentCore Runtime](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-service-contract.html). Se l'header non viene fornito, viene generato un UUID casuale come fallback.
206+
206207
:::caution[Organizzazione del codice]
207208
Probabilmente vorrai astrarre parte o tutto l'ID di sessione dal chiamante se gli utenti devono invocare il tuo agente direttamente. Ad esempio, potresti usare l'ID utente autenticato come parte dell'ID di sessione.
208209

@@ -302,7 +303,7 @@ Per invocare un Agente in esecuzione localmente tramite il target `<your-agent-n
302303

303304
```bash
304305
curl -N -X POST http://localhost:8081/invocations \
305-
-d '{"prompt": "what is 3 + 5?", "session_id": "abcdefghijklmnopqrstuvwxyz0123456789"}' \
306+
-d '{"message": "what is 3 + 5?"}' \
306307
-H "Content-Type: application/json"
307308
```
308309

@@ -337,7 +338,7 @@ Per l'Autenticazione Cognito, passa il Token di Accesso Cognito nell'header `Aut
337338
```bash
338339
curl -N -X POST 'https://bedrock
339340
-agentcore.<region>.amazonaws.com/runtimes/<url-encoded-arn>/invocations' \
340-
-d '{"prompt": "what is 3 + 5?", "session_id": "abcdefghijklmnopqrstuvwxyz0123456789"}' \
341+
-d '{"message": "what is 3 + 5?"}' \
341342
-H "Content-Type: application/json" \
342343
-H "Authorization: Bearer <access-token>"
343344
```

0 commit comments

Comments
 (0)