Skip to content

Amazon Bedrock Streaming API Response Contains Unexpected Control Characters & Event Metadata

0

Service: Amazon Bedrock Model ID: amazon.nova-pro-v1:0 Region: us-east-1 API Endpoint: https://bedrock-runtime.us-east-1.amazonaws.com/model/amazon.nova-pro-v1:0/converse-stream

Sample Code:

import requests
import json
from requests_aws4auth import AWS4Auth

# AWS Credentials
AWS_ACCESS_KEY = "Your_KEY"
AWS_SECRET_KEY = "Your secret Key"
REGION = "us-east-1"
SERVICE = "bedrock"
MODEL_ID = "amazon.nova-pro-v1:0"

# AWS Auth for Signature V4
aws_auth = AWS4Auth(AWS_ACCESS_KEY, AWS_SECRET_KEY, REGION, SERVICE)

# API Endpoint
url = f"https://bedrock-runtime.{REGION}.amazonaws.com/model/{MODEL_ID}/converse-stream"

# Request Payload
payload = {
    "inferenceConfig": {
        "maxTokens": 50,
        "temperature": 0.0
    },
    "messages": [
        {
            "content": [
                {
                    "text": "Tell me about artificial intelligence."
                }
            ],
            "role": "user"
        }
    ]
}

# Headers
headers = {
    "Content-Type": "application/json"
}

async def converse_stream():
    async with httpx.AsyncClient() as client:
        async with client.stream("POST", url, headers=headers, json=payload,auth=aws_auth) as response:
            async for chunk in response.aiter_text():
                print(chunk, end="")  # Print each chunk as it arrives

await converse_stream()

Observed Raw Response from AWS

:message-typeevent{"contentBlockIndex":0,"delta":{"text":")"},"p":"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW"}�W���:event-typecontentBlockDeltaentBlockDeltaypecontentBlockDelta
:message-typeevent{"contentBlockIndex":0,"delta":{"text":" creating"},"p":"abcdefghijklmnopqrstuvwxyzABCDEFG"}��0q�WH234567"}Ag�8�W>��5:event-typecontentBlockDelta
:message-typeevent{"contentBlockIndex":0,"delta":{"text":"."},"p":"abcdefghijklmnopqrstuvwxyzABCDEFGH"}|�{��WHvent-typecontentBlockDeltaa�@:event-typecontentBlockDeltaDeltaW���:event-typecontentBlockDelta
:message-typeevent{"metrics":{"latencyMs":672},"p":"abcdefghijklmnopqrst","usage":{"inputTokens":6,"outputTokens":50,"totalTokens":56}}���>kStop

Expected Response form AWS: https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ConverseStream.html#API_runtime_ConverseStream_ResponseSyntax

Please let me know where I am making a mistake and how I can correct it or provide me working code.

2 Answers
0

The issue you're experiencing is related to how you're processing the streaming response from Amazon Bedrock. The raw response you're seeing contains Server-Sent Events (SSE) format, which includes event metadata and control characters. This is expected behavior for streaming responses, but it requires proper parsing to extract the actual content.

To correctly handle this response, you need to parse the SSE format. Each event in the stream starts with "data:" and ends with two newline characters. The content between these markers is JSON-encoded data that you need to parse.

Here's how you can modify your code to properly handle the streaming response:

  1. Use a library that supports SSE parsing, such as sseclient-py.
  2. Parse each event and extract the JSON content.
  3. Process the JSON content based on the event type.

Here's an example of how you can modify your code:

import json
import sseclient
import requests
from requests_aws4auth import AWS4Auth

# AWS Credentials and setup (same as before)

# Function to handle the streaming response
def handle_sse_stream(url, headers, payload, auth):
    response = requests.post(url, stream=True, headers=headers, json=payload, auth=auth)
    client = sseclient.SSEClient(response)
    
    for event in client.events():
        if event.data:
            try:
                data = json.loads(event.data)
                if 'contentBlockIndex' in data and 'delta' in data:
                    print(data['delta'].get('text', ''), end='')
                elif 'metrics' in data:
                    print("\nMetrics:", data['metrics'])
                    print("Usage:", data['usage'])
            except json.JSONDecodeError:
                print("Error decoding JSON:", event.data)

# Make the request
handle_sse_stream(url, headers, payload, aws_auth)

This code will properly parse the SSE stream, extract the JSON content, and handle different types of events (content deltas and metrics). It will print the text content as it arrives and display the metrics at the end.

The unexpected control characters and event metadata you were seeing are part of the SSE format, which is used to structure the streaming response. By using a proper SSE client and parsing the events, you can extract the meaningful content and ignore the SSE-specific formatting.

Remember to install the sseclient-py library (pip install sseclient-py) before running this code.

This approach should resolve the issues you were experiencing and provide you with a clean output of the model's response.

Sources
Community | Process Amazon Bedrock's Response Stream with JavaScript
InvokeFlow - Amazon Bedrock

answered a year ago

  • Thanks for answer. I used the same code what you have mentioned and getting following error:

    UnicodeDecodeError Traceback (most recent call last) Cell In[8], line 59 56 print("Error decoding JSON:", event.data) 58 # Make the request ---> 59 handle_sse_stream(url, headers, payload, aws_auth)

    Cell In[8], line 46, in handle_sse_stream(url, headers, payload, auth) 43 response = requests.post(url, stream=True, headers=headers, json=payload, auth=auth) 44 client = sseclient.SSEClient(response) ---> 46 for event in client.events(): 47 if event.data: 48 try:

    File /opt/conda/envs/langchain/lib/python3.12/site-packages/sseclient/init.py:60, in SSEClient.events(self) 57 # Split before decoding so splitlines() only uses \r and \n 58 for line in chunk.splitlines(): 59 # Decode the line. ---> 60 line = line.decode(self._char_enc) 62 # Lines starting with a separator are comments and are to be 63 # ignored. 64 if not line.strip() or line.startswith(_FIELD_SEPARATOR):

    UnicodeDecodeError: 'utf-8' codec can't decode byte 0x90 in position 3: invalid start byte

0

Hello,

The error you're encountering is due to receiving raw encypted data from Amazon Bedrock's Streaming API. Your current code is printing this raw data directly, which includes control characters, event information, and other metadata alongside the actual response content without proper parsing of the raw data which comes in chunks in case of Bedrock's Nova model while using Converse Stream.

Here is a Code which is designed to handle streaming responses from Bedrock's Nova model, where the "extract_text" function searches for text between each received chunk of data (1024 bytes), then decodes and displays it in real-time while also accumulating the complete response.

import requests
import json
from requests_aws4auth import AWS4Auth
import struct

# AWS Credentials
AWS_ACCESS_KEY = "Your_Access_key"
AWS_SECRET_KEY = "Your_secter_key"
REGION = "us-east-1"
MODEL_ID = "amazon.nova-pro-v1:0"


# API Endpoint
url = f"https://bedrock-runtime.{REGION}.amazonaws.com/model/{MODEL_ID}/converse-stream"

# AWS4Auth for request signing
auth = AWS4Auth(AWS_ACCESS_KEY, AWS_SECRET_KEY, REGION, 'bedrock')

# Request Headers
headers = {
    "Content-Type": "application/json",
    "Accept": "application/json"
}

# Request Payload
payload = {
    "messages": [
        {
            "role": "user",
            "content": [
                {
                    "text": "Tell me about artificial intelligence."
                }
            ]
        }
    ],
    "system": [{"text": "You are an economist with access to lots of data"}],
    "inferenceConfig": {
        "maxTokens": 1000,
        "temperature": 0.5
    }
}

def extract_text(data):
    try:
        # Look for the text field in the JSON data
        json_start = data.find(b'"text":"')
        if json_start != -1:
            json_start += 8  # Length of '"text":"'
            json_end = data.find(b'"', json_start)
            if json_end != -1:
                return data[json_start:json_end].decode('utf-8', errors='ignore')
    except Exception as e:
        print(f"Error extracting text: {e}")
    return None

def converse_stream():
    try:
        print("Sending request...")
        response = requests.post(url, auth=auth, headers=headers, json=payload, stream=True)
        print(f"Response status code: {response.status_code}")
        
        if response.status_code != 200:
            print(f"Error response: {response.text}")
            return

        print("Streaming response:")
        full_response = ""
        print(vars(response))
        # print("above is response")

        for chunk in response.iter_content(chunk_size=1024):
            if chunk:
                text = extract_text(chunk)
                if text:
                    full_response += text
                    print(text, end='', flush=True)

        print("\n\nFull response:")
        print(full_response)

    except requests.exceptions.RequestException as e:
        print(f"An error occurred: {str(e)}")
        if hasattr(e, 'response') and e.response is not None:
            print(f"Status code: {e.response.status_code}")
            print(f"Response content: {e.response.text}")

if __name__ == "__main__":
    converse_stream()

Using this code, you'll be able to interact with Amazon Bedrock's Nova model through a streaming API connection, which enables real-time communication with the AI model. Instead of waiting for the entire response to be generated and delivered at once, the code receives and processes the response in chunks of 1024 bytes, immediately displaying each piece of text as it arrives. This creates a more dynamic and interactive experience. The code also maintains error handling and keeps track of the full response while providing this streaming functionality.

AWS
SUPPORT ENGINEER

answered a year ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.