[{"data":1,"prerenderedAt":58},["ShallowReactive",2],{"snippet-lambda-durable-ai-human-review":3},{"id":4,"title":5,"contributors":6,"description":8,"extension":9,"gitHub":10,"introBox":13,"languages":19,"meta":22,"resources":23,"services":28,"snippets":31,"stem":49,"tags":50,"type":56,"__hash__":57},"snippets\u002Fsnippets\u002Flambda-durable-ai-human-review.json","Human Review with AWS Lambda durable functions",[7],"content\u002Fcontributors\u002Fconnor-kirkpatrick.json","Pause AI workflow for human approval before continuing using AWS Lambda durable functions","json",{"template":11},{"repoURL":12},"https:\u002F\u002Fgithub.com\u002Faws-samples\u002Fsample-ai-workflows-in-aws-lambda-durable-functions",{"headline":14,"text":15},"How it works",[16,17,18],"This pattern demonstrates human-in-the-loop workflows with AWS Lambda durable functions. The workflow suspends execution while awaiting human review without consuming compute resources.","The example extracts fields from a document using an LLM, then waits for human approval before proceeding.","Benefits: Pay only for active compute time, not wait periods. Workflows can suspend for up to one year. No infrastructure to manage while waiting for human input.",[20,21],"Python","TypeScript",{},{"bullets":24},[25],{"text":26,"link":27},"AWS Lambda durable functions","https:\u002F\u002Faws.amazon.com\u002Flambda\u002Flambda-durable-functions\u002F",[29,30],"lambda-event-functions","bedrock",[32],{"title":33,"codeTabs":34},"Runtimes",[35,42],{"id":20,"title":36,"description":37,"snippets":38},"Usage Example with Python:","Human review workflow with Lambda durable functions using Python.",[39],{"language":40,"code":41},"py","import json\n\nimport boto3\nfrom aws_durable_execution_sdk_python import DurableContext, durable_execution\nfrom aws_durable_execution_sdk_python.config import Duration, WaitForCallbackConfig\nfrom pydantic import BaseModel\n\nMODEL_ID = \"us.amazon.nova-pro-v1:0\"\nbedrock = boto3.client(\"bedrock-runtime\")\n\n\ndef converse(model_id: str, prompt: str) -&#62; str:\n    response = bedrock.converse(\n        modelId=model_id,\n        messages=[{\"role\": \"user\", \"content\": [{\"text\": prompt}]}],\n    )\n    return response[\"output\"][\"message\"][\"content\"][0][\"text\"]\n\n\nclass ReviewResult(BaseModel):\n    approved: bool\n    notes: str | None = None\n\n\ndef send_for_review(callback_id: str, document: str, extracted_fields: str):\n    print(f\"Review needed for document. Callback ID: {callback_id}\")\n    print(f\"Extracted fields: {extracted_fields}\")\n\n\n@durable_execution\ndef handler(event: dict, context: DurableContext):\n    document = event.get(\"document\", \"Sample invoice with amount $1,234.56\")\n\n    extracted_fields = context.step(\n        lambda _: converse(\n            MODEL_ID,\n            f'Extract key fields from this document as JSON: \"{document}\"',\n        ),\n        \"extract fields\",\n    )\n\n    review_result_str = context.wait_for_callback(\n        lambda callback_id, _: send_for_review(callback_id, document, extracted_fields),\n        \"Await Human review\",\n        WaitForCallbackConfig(timeout=Duration.from_days(7)),\n    )\n\n    review_result = ReviewResult(**json.loads(review_result_str))\n\n    if not review_result.approved:\n        return {\n            \"status\": \"rejected\",\n            \"notes\": review_result.notes,\n            \"extractedFields\": extracted_fields,\n        }\n\n    return {\"status\": \"approved\", \"extractedFields\": extracted_fields}\n",{"id":21,"title":43,"description":44,"snippets":45},"Usage Example with TypeScript:","Human review workflow with Lambda durable functions using TypeScript.",[46],{"language":47,"code":48},"ts","import {\n  type DurableContext,\n  withDurableExecution,\n} from \"@aws\u002Fdurable-execution-sdk-js\";\nimport {\n  BedrockRuntimeClient,\n  ConverseCommand,\n} from \"@aws-sdk\u002Fclient-bedrock-runtime\";\nimport { z } from \"zod\";\n\nconst MODEL_ID = \"us.amazon.nova-pro-v1:0\";\nconst bedrock = new BedrockRuntimeClient({});\n\nasync function converse(modelId: string, prompt: string): Promise&#60;string&#62; {\n  const response = await bedrock.send(\n    new ConverseCommand({\n      modelId,\n      messages: [{ role: \"user\", content: [{ text: prompt }] }],\n    }),\n  );\n  return response.output?.message?.content?.[0].text ?? \"\";\n}\n\nconst ReviewResultSchema = z.object({\n  approved: z.boolean(),\n  notes: z.string().optional(),\n});\n\nconst sendForReview = (\n  callbackId: string,\n  document: string,\n  extractedFields: string\n) =&#62; {\n  console.log(`Review needed for document. Callback ID: ${callbackId}`);\n  console.log(`Extracted fields: ${extractedFields}`);\n};\n\nexport const handler = withDurableExecution(\n  async (event: { document?: string }, context: DurableContext) =&#62; {\n    const document = event.document ?? \"Sample invoice with amount $1,234.56\";\n\n    const extractedFields = await context.step(\"extract fields\", async () =&#62;\n      converse(MODEL_ID, `Extract key fields from this document as JSON: \"${document}\"`)\n    );\n\n    const reviewResultStr = await context.waitForCallback&#60;string&#62;(\n      \"Await Human review\",\n      async (callbackId) =&#62; {\n        sendForReview(callbackId, document, extractedFields);\n      },\n      { timeout: { days: 7 } }\n    );\n\n    const reviewResult = ReviewResultSchema.parse(JSON.parse(reviewResultStr));\n\n    if (!reviewResult.approved) {\n      return { status: \"rejected\", notes: reviewResult.notes, extractedFields };\n    }\n\n    return { status: \"approved\", extractedFields };\n  }\n);\n","snippets\u002Flambda-durable-ai-human-review",[51,52,30,53,54,55],"ai","durable-functions","genai","human-in-the-loop","approval","Integration","JaYoiK8nZ5ZwxwGeV4lAYrg4kfa4pAks7DLVroYWLm0",1792578864371]