Introduction#
- Create signature v4
- Send request to bedrock endpoint
- Parse response chunk by chunk
Here is the full script
bedrock-wo-sdk.js
// https://blog.alu.ai/signing-aws-api-requests-in-node-js-2023-version/// const { SignatureV4 } = require("@smithy/signature-v4");import { SignatureV4 } from '@smithy/signature-v4'import { Sha256 } from '@aws-crypto/sha256-js'import { HttpRequest } from '@aws-sdk/protocol-http'const decoder = new TextDecoder()// const ENDPOINT = "https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-v2/invoke";const ENDPOINT ='https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-v2/invoke-with-response-stream'async function signRequest() {// create a http request// path: /BUCKET-NAME/web-css/osaka-street.jpeg// host: s3.us-east-1.amazonaws.comconst request = new HttpRequest({path: new URL(ENDPOINT).pathname,hostname: new URL(ENDPOINT).hostname,protocol: 'https:',method: 'POST',headers: {host: new URL(ENDPOINT).hostname,Accept: 'application/vnd.amazon.eventstream','Content-Type': 'application/json'},body: JSON.stringify({prompt: '\n\nHuman: How to cook chicken soup? \n\nAssistant: ',max_tokens_to_sample: 128})})// create a signer using singnaturev4const signer = new SignatureV4({service: 'bedrock',region: 'us-east-1',credentials: {accessKeyId: '',secretAccessKey: '',sessionToken: ''},sha256: Sha256})const signedRequest = await signer.sign(request, {// signingRegion: "us-east-1",// signingService: "bedrock",// signingDate: new Date(),// date: new Date(),// expires: new Date(Date.now() + 15 * 60 * 1000), // 15 minutes// // list of headers used to compute signature// signedHeaders: "host;x-amz-date",})console.log(signedRequest)console.log(`https://${signedRequest.hostname}${signedRequest.path}`)const response = await fetch(`https://${signedRequest.hostname}${signedRequest.path}`,signedRequest)// const json = await response.json();// console.log(json);const reader = response.body.getReader()const decoder = new TextDecoder('utf-8')let answer = ''const regex = /\{(.*?)\}/glet matchwhile (true) {const { done, value } = await reader.read()if (done) {console.log('Done with stream')break}const x = decoder.decode(value)try {while ((match = regex.exec(x)) !== null) {const y = '{' + match[1] + '}'const z = atob(JSON.parse(y).bytes)console.log(JSON.parse(z).completion)answer += JSON.parse(z).completion}} catch (error) {console.log(error)answer += 'ERROR'}}console.log(answer)// console.log(response.data);// fs.writeFileSync("./hehe.jpg", response.data);// console.log(signedRequest.headers);// console.log(response.data);// pipe stream to file// response.data.pipe(fs.createWriteStream("./hehe.jpg"));// response.data event emitter data chunk// response.data.on("data", (chunk) => {// console.log(chunk.length);// });}// Example usageconst main = async () => {await signRequest()}main()
And here is package.json
package.json
{"name": "nodejs","version": "1.0.0","description": "","main": "index.js","type": "module","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"author": "","license": "ISC","dependencies": {"@aws-crypto/sha256-js": "^5.2.0","@aws-sdk/credential-provider-ini": "^3.529.1","@aws-sdk/protocol-http": "^3.374.0","@smithy/signature-v4": "^2.1.4","axios": "^1.6.7"}}
Create Request#
Let create a request
const ENDPOINT ='https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-v2/invoke-with-response-stream'const request = new HttpRequest({// path: /cdk-entest-videos/web-css/osaka-street.jpegpath: new URL(ENDPOINT).pathname,// host: s3.us-east-1.amazonaws.comhostname: new URL(ENDPOINT).hostname,protocol: 'https:',method: 'POST',headers: {host: new URL(ENDPOINT).hostname,Accept: 'application/vnd.amazon.eventstream','Content-Type': 'application/json'},body: JSON.stringify({prompt: '\n\nHuman: How to cook chicken soup? \n\nAssistant: ',max_tokens_to_sample: 128})})
Sign Request#
Let create a signature v4 and sign the request
// create a signer using singnaturev4const signer = new SignatureV4({service: 'bedrock',region: 'us-east-1',credentials: {accessKeyId: '',secretAccessKey: '',sessionToken: ''},sha256: Sha256})const signedRequest = await signer.sign(request, {// signingRegion: "us-east-1",// signingService: "bedrock",// signingDate: new Date(),// date: new Date(),// expires: new Date(Date.now() + 15 * 60 * 1000), // 15 minutes// // list of headers used to compute signature// signedHeaders: "host;x-amz-date",})console.log(signedRequest)
Parse Response#
In case of streaming response, let parse it chunk by chunk, use regex to detect the completion, the decode the based64.
- Parse chunk by chunk
- Regex to capture answer
- Base64 decode to string
const reader = response.body.getReader()const decoder = new TextDecoder('utf-8')let answer = ''const regex = /\{(.*?)\}/glet matchwhile (true) {const { done, value } = await reader.read()if (done) {console.log('Done with stream')break}const x = decoder.decode(value)try {while ((match = regex.exec(x)) !== null) {const y = '{' + match[1] + '}'const z = atob(JSON.parse(y).bytes)console.log(JSON.parse(z).completion)answer += JSON.parse(z).completion}} catch (error) {console.log(error)answer += 'ERROR'}}console.log(answer)
In case of normal response
const json = await response.json()console.log(json)