Skip to content

Commit e2e5501

Browse files
authored
Python: Use InvokeFlow for a turn by turn conversation. (#7210)
1 parent dd2f7bd commit e2e5501

File tree

4 files changed

+260
-1
lines changed

4 files changed

+260
-1
lines changed

.doc_gen/metadata/bedrock-agent-runtime_metadata.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,24 @@ bedrock-agent-runtime_InvokeFlow:
3232
- javascriptv3/example_code/bedrock-agent-runtime/actions/invoke-flow.js
3333
services:
3434
bedrock-agent-runtime: {InvokeFlow}
35+
36+
bedrock-agent-runtime_Scenario_ConverseWithFlow:
37+
title: Converse with an &BRlong; flow
38+
synopsis: use InvokeFlow to converse with an &BRlong; flow that includes an agent node.
39+
category: Basics
40+
guide_topic:
41+
title: Converse with an &BRlong; flow
42+
url: bedrock/latest/userguide/flows-multi-turn-invocation.html
43+
languages:
44+
Python:
45+
versions:
46+
- sdk_version: 3
47+
github: python/example_code/bedrock-agent-runtime
48+
sdkguide:
49+
excerpts:
50+
- description:
51+
snippet_tags:
52+
- python.example_code.bedrock-agent-runtime.flow_conversation.complete
53+
54+
services:
55+
bedrock-agent-runtime: {InvokeFlow}

python/example_code/bedrock-agent-runtime/README.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ python -m pip install -r requirements.txt
3434
<!--custom.prerequisites.start-->
3535
<!--custom.prerequisites.end-->
3636

37+
### Basics
38+
39+
Code examples that show you how to perform the essential operations within a service.
40+
41+
- [Learn the basics](flows/flow-conversation.py)
42+
43+
3744
### Single actions
3845

3946
Code excerpts that show you how to call individual service functions.
@@ -53,6 +60,24 @@ Code excerpts that show you how to call individual service functions.
5360
<!--custom.instructions.end-->
5461

5562

63+
#### Learn the basics
64+
65+
This example shows you how to use InvokeFlow to converse with an Amazon Bedrock flow that includes an agent node.
66+
67+
68+
<!--custom.basic_prereqs.bedrock-agent-runtime_Scenario_ConverseWithFlow.start-->
69+
<!--custom.basic_prereqs.bedrock-agent-runtime_Scenario_ConverseWithFlow.end-->
70+
71+
Start the example by running the following at a command prompt:
72+
73+
```
74+
python flows/flow-conversation.py
75+
```
76+
77+
78+
<!--custom.basics.bedrock-agent-runtime_Scenario_ConverseWithFlow.start-->
79+
<!--custom.basics.bedrock-agent-runtime_Scenario_ConverseWithFlow.end-->
80+
5681

5782
### Tests
5883

@@ -80,4 +105,4 @@ in the `python` folder.
80105

81106
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
82107

83-
SPDX-License-Identifier: Apache-2.0
108+
SPDX-License-Identifier: Apache-2.0
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# snippet-start:[python.example_code.bedrock-agent-runtime.flow_conversation.complete]
5+
6+
7+
"""
8+
Shows how to run an Amazon Bedrock flow with InvokeFlow and handle muli-turn interaction
9+
for a single conversation.
10+
For more information, see https://docs.aws.amazon.com/bedrock/latest/userguide/flows-multi-turn-invocation.html.
11+
12+
"""
13+
import logging
14+
import boto3
15+
import botocore
16+
17+
import botocore.exceptions
18+
19+
logging.basicConfig(level=logging.INFO)
20+
logger = logging.getLogger(__name__)
21+
22+
23+
def invoke_flow(client, flow_id, flow_alias_id, input_data, execution_id):
24+
"""
25+
Invoke an Amazon Bedrock flow and handle the response stream.
26+
27+
Args:
28+
client: Boto3 client for Amazon Bedrock agent runtime.
29+
flow_id: The ID of the flow to invoke.
30+
flow_alias_id: The alias ID of the flow.
31+
input_data: Input data for the flow.
32+
execution_id: Execution ID for continuing a flow. Use the value None on first run.
33+
34+
Returns:
35+
Dict containing flow_complete status, input_required info, and execution_id
36+
"""
37+
38+
response = None
39+
request_params = None
40+
41+
if execution_id is None:
42+
# Don't pass execution ID for first run.
43+
request_params = {
44+
"flowIdentifier": flow_id,
45+
"flowAliasIdentifier": flow_alias_id,
46+
"inputs": [input_data],
47+
"enableTrace": True
48+
}
49+
else:
50+
request_params = {
51+
"flowIdentifier": flow_id,
52+
"flowAliasIdentifier": flow_alias_id,
53+
"executionId": execution_id,
54+
"inputs": [input_data],
55+
"enableTrace": True
56+
}
57+
58+
response = client.invoke_flow(**request_params)
59+
60+
if "executionId" not in request_params:
61+
execution_id = response['executionId']
62+
63+
input_required = None
64+
flow_status = ""
65+
66+
# Process the streaming response
67+
for event in response['responseStream']:
68+
69+
# Check if flow is complete.
70+
if 'flowCompletionEvent' in event:
71+
flow_status = event['flowCompletionEvent']['completionReason']
72+
73+
# Check if more input us needed from user.
74+
elif 'flowMultiTurnInputRequestEvent' in event:
75+
input_required = event
76+
77+
# Print the model output.
78+
elif 'flowOutputEvent' in event:
79+
print(event['flowOutputEvent']['content']['document'])
80+
81+
# Log trace events.
82+
elif 'flowTraceEvent' in event:
83+
logger.info("Flow trace: %s", event['flowTraceEvent'])
84+
85+
return {
86+
"flow_status": flow_status,
87+
"input_required": input_required,
88+
"execution_id": execution_id
89+
}
90+
91+
92+
def converse_with_flow(bedrock_agent_client, flow_id, flow_alias_id):
93+
"""
94+
Run a conversation with the supplied flow.
95+
96+
Args:
97+
bedrock_agent_client: Boto3 client for Amazon Bedrock agent runtime.
98+
flow_id: The ID of the flow to run.
99+
flow_alias_id: The alias ID of the flow.
100+
101+
"""
102+
103+
flow_execution_id = None
104+
finished = False
105+
106+
# Get the intial prompt from the user.
107+
user_input = input("Enter input: ")
108+
109+
# Use prompt to create input data.
110+
flow_input_data = {
111+
"content": {
112+
"document": user_input
113+
},
114+
"nodeName": "FlowInputNode",
115+
"nodeOutputName": "document"
116+
}
117+
118+
try:
119+
while not finished:
120+
# Invoke the flow until successfully finished.
121+
122+
result = invoke_flow(
123+
bedrock_agent_client, flow_id, flow_alias_id, flow_input_data, flow_execution_id)
124+
125+
status = result['flow_status']
126+
flow_execution_id = result['execution_id']
127+
more_input = result['input_required']
128+
if status == "INPUT_REQUIRED":
129+
# The flow needs more information from the user.
130+
logger.info("The flow %s requires more input", flow_id)
131+
user_input = input(
132+
more_input['flowMultiTurnInputRequestEvent']['content']['document'] + ": ")
133+
flow_input_data = {
134+
"content": {
135+
"document": user_input
136+
},
137+
"nodeName": more_input['flowMultiTurnInputRequestEvent']['nodeName'],
138+
"nodeInputName": "agentInputText"
139+
140+
}
141+
elif status == "SUCCESS":
142+
# The flow completed successfully.
143+
finished = True
144+
logger.info("The flow %s successfully completed.", flow_id)
145+
146+
except botocore.exceptions.ClientError as e:
147+
print(f"Client error: {str(e)}")
148+
logger.error("Client error: %s", {str(e)})
149+
150+
except Exception as e:
151+
print(f"An error occurred: {str(e)}")
152+
logger.error("An error occurred: %s", {str(e)})
153+
logger.error("Error type: %s", {type(e)})
154+
155+
156+
def main():
157+
"""
158+
Main entry point for the script.
159+
"""
160+
161+
# Replace these with your actual flow ID and flow alias ID.
162+
FLOW_ID = 'YOUR_FLOW_ID'
163+
FLOW_ALIAS_ID = 'YOUR_FLOW_ALIAS_ID'
164+
165+
logger.info("Starting conversation with FLOW: %s ID: %s",
166+
FLOW_ID, FLOW_ALIAS_ID)
167+
168+
# Get the Bedrock agent runtime client.
169+
session = boto3.Session(profile_name='default')
170+
bedrock_agent_client = session.client('bedrock-agent-runtime')
171+
172+
# Start the conversation.
173+
converse_with_flow(bedrock_agent_client, FLOW_ID, FLOW_ALIAS_ID)
174+
175+
logger.info("Conversation with FLOW: %s ID: %s finished",
176+
FLOW_ID, FLOW_ALIAS_ID)
177+
178+
179+
if __name__ == "__main__":
180+
main()
181+
182+
# snippet-end:[python.example_code.bedrock-agent-runtime.flow_conversation.complete]
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
import pytest
5+
import subprocess
6+
import sys
7+
8+
files_under_test = [
9+
"flows/flow-conversation.py"
10+
]
11+
12+
@pytest.mark.integ
13+
@pytest.mark.parametrize("file", files_under_test)
14+
def test_flow_conversation(file):
15+
# Simulate user input - each string represents one input() call
16+
# If you're using the docs at https://docs.aws.amazon.com/bedrock/latest/userguide/flows-multi-turn-invocation.html,
17+
# "Create a playlist\n 3\n pop, castles\n" should work with Antropic Haiku.
18+
test_input = "Hello\n"
19+
20+
result = subprocess.run(
21+
[sys.executable, file],
22+
input=test_input,
23+
capture_output=True,
24+
text=True,
25+
)
26+
27+
print(f"STDOUT: {result.stdout}") # For debugging
28+
print(f"STDERR: {result.stderr}") # For debugging
29+
30+
assert result.stdout != ""
31+
assert result.returncode == 0

0 commit comments

Comments
 (0)