I can’t figure out the correct way to pass a system prompt to claude 3 haiku through AWS Bedrock InvokeModelWithResponseStreamCommand. When I try to add { role: "system", content: systemPrompt } to the messages array it breaks. I’ve scoured Anthropic’s and AWS Bedrock’s documentation but can’t find out whether system prompts are suppposed to be in a separate field from messages. According to anthropic docs it should be, but it does not appear that AWS Bedrock supports a separate system prompt field outside of messages in its JS SDK.
import { NextResponse } from "next/server";
import { BedrockRuntimeClient, InvokeModelWithResponseStreamCommand } from "@aws-sdk/client-bedrock-runtime";
const systemPrompt = "todo"
export async function POST(req) {
// Create a Bedrock Runtime client in the AWS Region you want to use.
const client = new BedrockRuntimeClient({ region: "us-east-1" });
const data = await req.json();
// Set the model ID, e.g., Claude 3 Haiku.
const modelId = "anthropic.claude-3-haiku-20240307-v1:0";
console.log(data)
const payload = {
anthropic_version: "bedrock-2023-05-31",
max_tokens: 10000,
messages: [
{"role": "user", "content": "Hello there."},
{"role": "assistant", "content": "Hi, I'm Claude. How can I help you?"},
{"role": "user", "content": "Can you explain LLMs in plain English?"},
],
}
// Create a command with the model ID, the message, and a basic configuration.
const command = new InvokeModelWithResponseStreamCommand({
contentType: "application/json",
body: JSON.stringify(payload),
modelId,
});
const stream = new ReadableStream({
async start(controller) {
try {
// Send the command to the model and wait for the response
const response = await client.send(command);
let completeMessage = "";
// Extract and print the streamed response text in real-time.
for await (const item of response.body) {
const chunk = JSON.parse(new TextDecoder().decode(item.chunk.bytes));
const chunk_type = chunk.type;
if (chunk_type === "content_block_delta") {
const text = chunk.delta.text;
completeMessage = completeMessage + text;
controller.enqueue(text);
process.stdout.write(text);
}
}
} catch (err) {
controller.error(err)
} finally {
controller.close()
}
}
})
return new NextResponse(stream);
}
The error when I try to add the system prompt to the messages array:
Error: failed to pipe response
...
[cause]: ValidationException: Malformed input request: #: subject must not be valid against schema {"required":["messages"]}#/messages/0/role: system is not a valid enum value, please reformat your input and try again.
at de_ValidationExceptionRes (webpack-internal:///(rsc)/./node_modules/@aws-sdk/client-bedrock-runtime/dist-es/protocols/Aws_restJson1.js:379:23)
at de_CommandError (webpack-internal:///(rsc)/./node_modules/@aws-sdk/client-bedrock-runtime/dist-es/protocols/Aws_restJson1.js:212:25)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async eval (webpack-internal:///(rsc)/./node_modules/@smithy/middleware-serde/dist-es/deserializerMiddleware.js:8:24)
at async eval (webpack-internal:///(rsc)/./node_modules/@smithy/core/dist-es/middleware-http-signing/httpSigningMiddleware.js:25:20)
at async eval (webpack-internal:///(rsc)/./node_modules/@smithy/middleware-retry/dist-es/retryMiddleware.js:41:46)
at async eval (webpack-internal:///(rsc)/./node_modules/@aws-sdk/middleware-logger/dist-es/loggerMiddleware.js:9:26)
at async Object.start (webpack-internal:///(rsc)/./app/api/chat/route.js:118:34) {
'$fault': 'client',
'$metadata': {
httpStatusCode: 400,
requestId: '5c49e0b0-d781-4c5a-bd7d-0823a50a763f',
extendedRequestId: undefined,
cfId: undefined,
attempts: 1,
totalRetryDelay: 0
}
}
}
POST /api/chat 500 in 928ms
I’ve tried to add { role: "system", content: systemPrompt } as an additional message in the messages array and as a separate ("system") field. Using the external system field approach appears to be the correct way according to Anthropic’s docs, but AWS Bedrock doesn’t appear to support an external system field in its InvokeModelWithResponseStreamCommand Bedrock Runtime API. I’m expecting a streaming response that takes in user prompts and responds within the constraints of the system prompt.
https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_InvokeModelWithResponseStream.html
https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/system-prompts
https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/javascript_bedrock-runtime_code_examples.html#anthropic_claude
2
Answers
Looks like max_tokens can't be more than 4096 and the correct way is to pass in the prompt as a separate system field: https://github.com/aws/aws-sdk-js-v3/issues/5853
https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages.html#model-parameters-anthropic-claude-messages-request-response
The code below works in my experience:
I had exactly this issue and finally managed to get it working by tweaking some rather cryptic parameters. A better solution, the one I settled on and is recommended by AWS is to use the
ConverseCommand
API rather than theInvokeModelCommand
API. The Converse API standardises the parameters between models.