diff --git a/README.md b/README.md index 502a6486..cfd649eb 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,7 @@ Please note that `sam local` starts a Lambda Runtime Interface Emulator on port - [FastAPI](examples/fastapi) - [FastAPI with Response Streaming](examples/fastapi-response-streaming) +- [FastAPI with Response Streaming in Zip](examples/fastapi-response-streaming-zip) - [FastAPI in Zip](examples/fastapi-zip) - [Flask](examples/flask) - [Flask in Zip](examples/flask-zip) diff --git a/examples/fastapi-response-streaming-zip/.gitignore b/examples/fastapi-response-streaming-zip/.gitignore new file mode 100644 index 00000000..4808264d --- /dev/null +++ b/examples/fastapi-response-streaming-zip/.gitignore @@ -0,0 +1,244 @@ + +# Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Ruby plugin and RubyMine +/.rakeTasks + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +### PyCharm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +.idea/sonarlint + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +.pytest_cache/ +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule.* + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Build folder + +*/build/* + +# End of https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode \ No newline at end of file diff --git a/examples/fastapi-response-streaming-zip/README.md b/examples/fastapi-response-streaming-zip/README.md new file mode 100644 index 00000000..da030274 --- /dev/null +++ b/examples/fastapi-response-streaming-zip/README.md @@ -0,0 +1,52 @@ +# FastAPI Response Streaming + +This example shows how to use Lambda Web Adapter to run a FastAPI application with response streaming via a Function URL. + +### How does it work? + +We add Lambda Web Adapter layer to the function and configure wrapper script. + +1. attach Lambda Adapter layer to your function. This layer containers Lambda Adapter binary and a wrapper script. + 1. x86_64: `arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerX86:17` + 2. arm64: `arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerArm64:17` +2. configure Lambda environment variable `AWS_LAMBDA_EXEC_WRAPPER` to `/opt/bootstrap`. This is a wrapper script included in the layer. +3. set function handler to a startup command: `run.sh`. The wrapper script will execute this command to boot up your application. + +To get more information of Wrapper script, please read Lambda documentation [here](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-modify.html#runtime-wrapper). + +This is the resource for Lambda function. The function urls's invoke mode is configured as "RESPONSE_STREAM", and Lambda environment variable "AWS_LWA_INVOKE_MODE" is set to "response_stream". + +```yaml + FastAPIFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: app/ + Handler: run.sh + Runtime: python3.9 + MemorySize: 256 + Environment: + Variables: + AWS_LAMBDA_EXEC_WRAPPER: /opt/bootstrap + AWS_LWA_INVOKE_MODE: response_stream + PORT: 8000 + Layers: + - !Sub arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerX86:17 + FunctionUrlConfig: + AuthType: NONE + InvokeMode: RESPONSE_STREAM +``` + +### Build and Deploy + +Run the following commands to build and deploy the application to lambda. + +```bash +sam build --use-container +sam deploy --guided +``` +When the deployment completes, take note of FastAPI's Value. It is the API Gateway endpoint URL. + +### Verify it works + +Open FastAPI's URL in a browser, you should see "This is streaming from Lambda" streams back 10 times. + diff --git a/examples/fastapi-response-streaming/__init__.py b/examples/fastapi-response-streaming-zip/__init__.py similarity index 100% rename from examples/fastapi-response-streaming/__init__.py rename to examples/fastapi-response-streaming-zip/__init__.py diff --git a/examples/fastapi-response-streaming/app/__init__.py b/examples/fastapi-response-streaming-zip/app/__init__.py similarity index 100% rename from examples/fastapi-response-streaming/app/__init__.py rename to examples/fastapi-response-streaming-zip/app/__init__.py diff --git a/examples/fastapi-response-streaming-zip/app/main.py b/examples/fastapi-response-streaming-zip/app/main.py new file mode 100644 index 00000000..7ece3366 --- /dev/null +++ b/examples/fastapi-response-streaming-zip/app/main.py @@ -0,0 +1,16 @@ +from fastapi import FastAPI +from fastapi.responses import StreamingResponse +import asyncio + +app = FastAPI() + + +async def streamer(): + for i in range(10): + await asyncio.sleep(1) + yield b"This is streaming from Lambda \n" + + +@app.get("/") +async def index(): + return StreamingResponse(streamer(), media_type="text/plain; charset=utf-8") diff --git a/examples/fastapi-response-streaming-zip/app/requirements.txt b/examples/fastapi-response-streaming-zip/app/requirements.txt new file mode 100644 index 00000000..70d378fa --- /dev/null +++ b/examples/fastapi-response-streaming-zip/app/requirements.txt @@ -0,0 +1,12 @@ +anyio==3.6.2 +click==8.1.3 +fastapi==0.95.2 +h11==0.14.0 +idna==3.4 +importlib-metadata==6.0.0 +pydantic==1.10.5 +sniffio==1.3.0 +starlette==0.27.0 +typing_extensions==4.5.0 +uvicorn==0.20.0 +zipp==3.13.0 diff --git a/examples/fastapi-response-streaming/app/run.sh b/examples/fastapi-response-streaming-zip/app/run.sh similarity index 100% rename from examples/fastapi-response-streaming/app/run.sh rename to examples/fastapi-response-streaming-zip/app/run.sh diff --git a/examples/fastapi-response-streaming-zip/template.yaml b/examples/fastapi-response-streaming-zip/template.yaml new file mode 100644 index 00000000..5f826ca3 --- /dev/null +++ b/examples/fastapi-response-streaming-zip/template.yaml @@ -0,0 +1,36 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + fastapi response streaming + +# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Timeout: 60 + +Resources: + FastAPIFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: app/ + Handler: run.sh + Runtime: python3.9 + MemorySize: 256 + Environment: + Variables: + AWS_LAMBDA_EXEC_WRAPPER: /opt/bootstrap + AWS_LWA_INVOKE_MODE: response_stream + PORT: 8000 + Layers: + - !Sub arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerX86:17 + FunctionUrlConfig: + AuthType: NONE + InvokeMode: RESPONSE_STREAM + +Outputs: + FastAPIFunctionUrl: + Description: "Function URL for FastAPI function" + Value: !GetAtt FastAPIFunctionUrl.FunctionUrl + FastAPIFunction: + Description: "FastAPI Lambda Function ARN" + Value: !GetAtt FastAPIFunction.Arn diff --git a/examples/fastapi-response-streaming/.gitignore b/examples/fastapi-response-streaming/.gitignore index 4808264d..f117fb68 100644 --- a/examples/fastapi-response-streaming/.gitignore +++ b/examples/fastapi-response-streaming/.gitignore @@ -241,4 +241,8 @@ $RECYCLE.BIN/ */build/* -# End of https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode \ No newline at end of file +# End of https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode + +.aws-sam +.vscode +samconfig.toml \ No newline at end of file diff --git a/examples/fastapi-response-streaming/README.md b/examples/fastapi-response-streaming/README.md index da030274..28bcd177 100644 --- a/examples/fastapi-response-streaming/README.md +++ b/examples/fastapi-response-streaming/README.md @@ -1,52 +1,71 @@ -# FastAPI Response Streaming +# Serverless Bedtime Storyteller -This example shows how to use Lambda Web Adapter to run a FastAPI application with response streaming via a Function URL. +This example shows streaming response from Amazon Bedrock with FastAPI on AWS Lambda. -### How does it work? -We add Lambda Web Adapter layer to the function and configure wrapper script. +![Architecture](imgs/serverless-storyteller-architecture.png) -1. attach Lambda Adapter layer to your function. This layer containers Lambda Adapter binary and a wrapper script. - 1. x86_64: `arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerX86:17` - 2. arm64: `arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerArm64:17` -2. configure Lambda environment variable `AWS_LAMBDA_EXEC_WRAPPER` to `/opt/bootstrap`. This is a wrapper script included in the layer. -3. set function handler to a startup command: `run.sh`. The wrapper script will execute this command to boot up your application. -To get more information of Wrapper script, please read Lambda documentation [here](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-modify.html#runtime-wrapper). +## How does it work? -This is the resource for Lambda function. The function urls's invoke mode is configured as "RESPONSE_STREAM", and Lambda environment variable "AWS_LWA_INVOKE_MODE" is set to "response_stream". +This example uses Anthropic Claude v2 model to generate bedtime stories. FastAPI provides the static web frontend and an inference API. The inference API endpoint invokes Bedrock using Boto3, and streams the response. Both Lambda Web Adapter and function URL have response streaming mode enabled. So the response from Bedrock are streamed all the way back to the client. + +This function is packaged as a Docker image. Here is the content of the Dockerfile. + +```dockerfile +FROM public.ecr.aws/docker/library/python:3.12.0-slim-bullseye +COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.7.1 /lambda-adapter /opt/extensions/lambda-adapter + +WORKDIR /app +ADD . . +RUN pip install -r requirements.txt + +CMD ["python", "main.py"] +``` + +Notice that we only need to add the second line to install Lambda Web Adapter. + +```dockerfile +COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.7.1 /lambda-adapter /opt/extensions/ +``` + +In the SAM template, we use an environment variable `AWS_LWA_INVOKE_MODE: RESPONSE_STREAM` to configure Lambda Web Adapter in response streaming mode. And adding a function url with `InvokeMode: RESPONSE_STREAM`. ```yaml FastAPIFunction: Type: AWS::Serverless::Function Properties: - CodeUri: app/ - Handler: run.sh - Runtime: python3.9 - MemorySize: 256 + PackageType: Image + MemorySize: 512 Environment: Variables: - AWS_LAMBDA_EXEC_WRAPPER: /opt/bootstrap - AWS_LWA_INVOKE_MODE: response_stream - PORT: 8000 - Layers: - - !Sub arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerX86:17 + AWS_LWA_INVOKE_MODE: RESPONSE_STREAM FunctionUrlConfig: AuthType: NONE InvokeMode: RESPONSE_STREAM -``` + Policies: + - Statement: + - Sid: BedrockInvokePolicy + Effect: Allow + Action: + - bedrock:InvokeModelWithResponseStream + Resource: '*' +``` -### Build and Deploy -Run the following commands to build and deploy the application to lambda. +## Build and deploy + +Run the following commends to build and deploy this example. ```bash sam build --use-container sam deploy --guided ``` -When the deployment completes, take note of FastAPI's Value. It is the API Gateway endpoint URL. -### Verify it works -Open FastAPI's URL in a browser, you should see "This is streaming from Lambda" streams back 10 times. +## Test the example + +After the deployment completes, open the `FastAPIFunctionUrl` shown in the output messages. You should see a simple web page. Here is a demo. + +![Demo](imgs/demo.gif) \ No newline at end of file diff --git a/examples/fastapi-response-streaming/app/Dockerfile b/examples/fastapi-response-streaming/app/Dockerfile new file mode 100644 index 00000000..9f4e9c2b --- /dev/null +++ b/examples/fastapi-response-streaming/app/Dockerfile @@ -0,0 +1,8 @@ +FROM public.ecr.aws/docker/library/python:3.12.0-slim-bullseye +COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.7.1 /lambda-adapter /opt/extensions/lambda-adapter + +WORKDIR /app +ADD . . +RUN pip install -r requirements.txt + +CMD ["python", "main.py"] \ No newline at end of file diff --git a/examples/fastapi-response-streaming/app/main.py b/examples/fastapi-response-streaming/app/main.py index 7ece3366..14699bc3 100644 --- a/examples/fastapi-response-streaming/app/main.py +++ b/examples/fastapi-response-streaming/app/main.py @@ -1,16 +1,60 @@ +import boto3 +import json +import os +import uvicorn from fastapi import FastAPI -from fastapi.responses import StreamingResponse -import asyncio +from fastapi.staticfiles import StaticFiles +from fastapi.responses import RedirectResponse, StreamingResponse +from pydantic import BaseModel +from typing import Optional + app = FastAPI() +app.mount("/demo", StaticFiles(directory="static", html=True)) -async def streamer(): - for i in range(10): - await asyncio.sleep(1) - yield b"This is streaming from Lambda \n" +@app.get("/") +async def root(): + return RedirectResponse(url='/demo/') +class Story(BaseModel): + topic: Optional[str] = None -@app.get("/") -async def index(): - return StreamingResponse(streamer(), media_type="text/plain; charset=utf-8") +@app.post("/api/story") +def api_story(story: Story): + if story.topic == None or story.topic == "": + return None + + return StreamingResponse(bedrock_stream(story.topic), media_type="text/html") + + +bedrock = boto3.client('bedrock-runtime') + +async def bedrock_stream(topic: str): + instruction = f""" + You are a world class writer. Please write a sweet bedtime story about {topic}. + """ + + body = json.dumps({ + 'prompt': f'Human:{instruction}\n\nAssistant:', + 'max_tokens_to_sample': 1028, + 'temperature': 1, + 'top_k': 250, + 'top_p': 0.999, + 'stop_sequences': ['\n\nHuman:'] + }) + response = bedrock.invoke_model_with_response_stream( + modelId='anthropic.claude-v2', + body=body + ) + + stream = response.get('body') + if stream: + for event in stream: + chunk = event.get('chunk') + if chunk: + yield json.loads(chunk.get('bytes').decode())['completion'] + + +if __name__ == "__main__": + uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "8080"))) diff --git a/examples/fastapi-response-streaming/app/requirements.txt b/examples/fastapi-response-streaming/app/requirements.txt index 70d378fa..ff663fa1 100644 --- a/examples/fastapi-response-streaming/app/requirements.txt +++ b/examples/fastapi-response-streaming/app/requirements.txt @@ -1,12 +1,20 @@ -anyio==3.6.2 -click==8.1.3 -fastapi==0.95.2 +annotated-types==0.5.0 +anyio==3.7.1 +boto3==1.28.61 +botocore==1.31.61 +click==8.1.7 +exceptiongroup==1.1.3 +fastapi==0.103.2 h11==0.14.0 idna==3.4 -importlib-metadata==6.0.0 -pydantic==1.10.5 +jmespath==1.0.1 +pydantic==2.4.2 +pydantic_core==2.10.1 +python-dateutil==2.8.2 +s3transfer==0.7.0 +six==1.16.0 sniffio==1.3.0 starlette==0.27.0 -typing_extensions==4.5.0 -uvicorn==0.20.0 -zipp==3.13.0 +typing_extensions==4.8.0 +urllib3==1.26.17 +uvicorn==0.23.2 diff --git a/examples/fastapi-response-streaming/app/static/index.html b/examples/fastapi-response-streaming/app/static/index.html new file mode 100644 index 00000000..ad1cc50d --- /dev/null +++ b/examples/fastapi-response-streaming/app/static/index.html @@ -0,0 +1,25 @@ + + + + + Storyteller + + + + + + + +
+
+

Serverless Bedtime Storyteller

+

Enter a topic and let AI writes a bedtime story for you.

+ + +
+
+
+ + + + \ No newline at end of file diff --git a/examples/fastapi-response-streaming/app/static/script.js b/examples/fastapi-response-streaming/app/static/script.js new file mode 100644 index 00000000..86aadc05 --- /dev/null +++ b/examples/fastapi-response-streaming/app/static/script.js @@ -0,0 +1,48 @@ +async function tellStory() { + const story = document.getElementById("topic").value; + + if (story.trim().length === 0) { + return; + } + + const storyOutput = document.getElementById("story-output"); + storyOutput.innerText = "Thinking..."; + + try { + // Use Fetch API to send a POST request for response streaming. See https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API + const response = await fetch("/api/story", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ "topic": story }) + }); + + storyOutput.innerText = ""; + + // Response Body is a ReadableStream. See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + + // Process the chunks from the stream + while (true) { + const { done, value } = await reader.read(); + if (done) { + break; + } + const text = decoder.decode(value); + storyOutput.innerText += text; + } + + } catch (error) { + storyOutput.innerText = `Sorry, an error happened. Please try again later. \n\n ${error}`; + } + +} + +document.getElementById("tell-story").addEventListener("click", tellStory); +document.getElementById('topic').addEventListener('keydown', function (e) { + if (e.code === 'Enter') { + tellStory(); + } +}); diff --git a/examples/fastapi-response-streaming/app/static/style.css b/examples/fastapi-response-streaming/app/static/style.css new file mode 100644 index 00000000..c7762059 --- /dev/null +++ b/examples/fastapi-response-streaming/app/static/style.css @@ -0,0 +1,38 @@ +body { + font-family: sans-serif; + margin: 0; + padding: 0; + } + + #container { + justify-content: center + } + + h1 { + text-align: center; + } + + p { + margin-bottom: 10px; + } + + input { + width: 100%; + height: 20px; + border: 1px solid black; + margin-bottom: 10px; + } + + button { + height: 20px; + background-color: #000; + color: #fff; + border: none; + cursor: pointer; + } + + #story-output { + width: 100%; + overflow: auto; + } + \ No newline at end of file diff --git a/examples/fastapi-response-streaming/imgs/demo.gif b/examples/fastapi-response-streaming/imgs/demo.gif new file mode 100644 index 00000000..03225f4c Binary files /dev/null and b/examples/fastapi-response-streaming/imgs/demo.gif differ diff --git a/examples/fastapi-response-streaming/imgs/serverless-storyteller-architecture.png b/examples/fastapi-response-streaming/imgs/serverless-storyteller-architecture.png new file mode 100644 index 00000000..170ab055 Binary files /dev/null and b/examples/fastapi-response-streaming/imgs/serverless-storyteller-architecture.png differ diff --git a/examples/fastapi-response-streaming/template.yaml b/examples/fastapi-response-streaming/template.yaml index 5f826ca3..0ce24fa8 100644 --- a/examples/fastapi-response-streaming/template.yaml +++ b/examples/fastapi-response-streaming/template.yaml @@ -1,31 +1,37 @@ AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: > - fastapi response streaming + Streaming Bedrock Response with FastAPI on AWS Lambda # More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst Globals: Function: - Timeout: 60 + Timeout: 300 Resources: FastAPIFunction: Type: AWS::Serverless::Function Properties: - CodeUri: app/ - Handler: run.sh - Runtime: python3.9 - MemorySize: 256 + PackageType: Image + MemorySize: 512 Environment: Variables: - AWS_LAMBDA_EXEC_WRAPPER: /opt/bootstrap - AWS_LWA_INVOKE_MODE: response_stream - PORT: 8000 - Layers: - - !Sub arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerX86:17 + AWS_LWA_INVOKE_MODE: RESPONSE_STREAM FunctionUrlConfig: AuthType: NONE InvokeMode: RESPONSE_STREAM + Policies: + - Statement: + - Sid: BedrockInvokePolicy + Effect: Allow + Action: + - bedrock:InvokeModelWithResponseStream + Resource: '*' + Tracing: Active + Metadata: + Dockerfile: Dockerfile + DockerContext: ./app + DockerTag: v1 Outputs: FastAPIFunctionUrl: