Update: this post used an older version of Prompt Flow at the time. It had some issues with building and running the Docker image without issues. In version 1.5.0, it should work fine because the Dockerfile now also installs gcc.
In the previous post, we created a flow with Prompt Flow in Visual Studio Code. The Prompt Flow extension for VS Code has a visual flow editor to test the flow. You simply provide the input and click the Run button. When the flow is finished, the result can be seen in the Outputs node, including a trace of the flow:

Now it’s time to deploy the flow. One of the options is creating a container image with Docker.
Before we start, we will first convert this flow into a chat flow. Chat does not make much sense for this flow. However, the Docker container includes a UI to run your flow via a chat interface. You will also be able to test your flow locally in a web app.
Convert the flow to a chat flow
To convert the flow to a chat flow, enable chat mode and add chat_history to the Inputs node:

To include the chat history in your conversations, modify the .jinja2
template in the LLM node:
system:
You return the url to an image that best matches the user's question. Use the provided context to select the image. Only return the url. When no
matching url is found, simply return NO_IMAGE
{% for item in chat_history %}
user:
{{item.inputs.description}}
assistant:
{{item.outputs.url}}
{% endfor %}
user:
{{description}}
context : {{search_results}}
Enabling chat history allows you to loop over its content and reconstruct the user/assistant interactions before adding the most recent description. When you run the flow, you get:

The third option will give you a GUI to test your flow:

As you can probably tell, this requires Streamlit. The first time you run this flow, check the terminal for instructions about the packages to install. When you are finished, press CTRL-C in the terminal.
Now that we know the chat flow works, we can create the Docker image.
⚠️ Important: a chat flow is not required to build the Docker image; we only add it here to illustrate the user interface that the Docker image can present to the user; you can always call your flow using a HTTP endpoint, chat flow or not
Generating the Docker image
Before creating the Docker image, ensure your Python requirements.txt
file in your flow’s folder has the following content:
promptflow
promptflow-tools
azure-search-documents
We need promptflow-tools
to support tools like the embedding tool in the container. We also need azure-search-documents
to use in the custom Python tool.
To build the flow as a Docker image, you should be able to use the build icon and select Build as Docker:

However, in my case, that did not result in any output to build a Docker image. This is a temporary issue from the 1.6 version of the extension and will be fixed. For now, I recommend building the image with the command line tool:
pf flow build --source --output --format docker
I ran the following command in my flow folder:
pf flow build --source . --output ./docker --format docker
That resulted in a docker
folder like below:

pf flow build
Note that this copies your flow’s files to a flow
folder under the docker
folder. Ensure that requirements.txt
in the docker/flow
folder matches requirements.txt
in your original flow folder (it should).
You can now cd
into the Docker folder and run the following command. Don’t forget the . at the end:
docker build -t YOURTAG .
In my case, I used:
docker build -t gbaeke/pfimage .
After running the above command, you might get an error. I got: ERROR: failed to solve...
I fixed that by modifying the Docker file. Move the RUN apt-get
line above the RUN conda create
line and add gcc
:
# syntax=docker/dockerfile:1
FROM docker.io/continuumio/miniconda3:latest
WORKDIR /
COPY ./flow /flow
RUN apt-get update && apt-get install -y runit gcc
# create conda environment
RUN conda create -n promptflow-serve python=3.9.16 pip=23.0.1 -q -y && \
conda run -n promptflow-serve \
.......
After this modification, the docker build
command ran successfully.
Running the image
The image contains the connections you created. Remember we created an Azure OpenAI connection and a custom connection. Connections contain both config and secrets. Although the config is available in the image, the secrets are not. You need to provide the secrets as environment variables.
You can find the names of the environment variables in the settings.json
file:
{
"OPEN_AI_CONNECTION_API_KEY": "",
"AZURE_AI_SEARCH_CONNECTION_KEY": ""
}
Run the container as shown below and replace OPENAIKEY and AISEARCHKEY with the key to your Azure OpenAI resource and Azure AI Search resource. In the container, the code listens on port 8080 so we map that port to port 8080 on the host:
docker run -itp 8080:8080 -e OPEN_AI_CONNECTION_API_KEY=OPENAIKEY \
AZURE_AI_SEARCH_CONNECTION_KEY=AISEARCHKEY
When you run the above command, you get the following output (some parts removed):
finish run supervise
Azure_AI_Search_Connection.yaml open_ai_connection.yaml
{
"name": "open_ai_connection",
"module": "promptflow.connections",
......
"api_type": "azure",
"api_version": "2023-07-01-preview"
}
{
"name": "Azure AI Search Connection",
"module": "promptflow.connections",
....
},
"secrets": {
"key": "******"
}
}
start promptflow serving with worker_num: 8, worker_threads: 1
[2023-12-14 12:55:09 +0000] [51] [INFO] Starting gunicorn 20.1.0
[2023-12-14 12:55:09 +0000] [51] [INFO] Listening at: http://0.0.0.0:8080 (51)
[2023-12-14 12:55:09 +0000] [51] [INFO] Using worker: sync
...
You should now be able to send requests to the score endpoint. The screenshot below shows a .http file with the call config and result:

When you browse to http://localhost:8080, you get a chat interface like the one below:

In my case, the chat UI did not work. Although I could enter a description and press ENTER, I did not see the response. In the background, the flow was triggered, just the response was missing. Remember that these features, and Prompt Flow on your local machine are still experimental at the time of writing (December 2023). They will probably change quite a lot in the future or have changed by the time you read this.
Conclusion
Although you can create a flow in the cloud and deploy that flow to an online endpoint, you might want more control over the deployment. Developing the flow locally and building a container image gives you that control. Once the image is built and pushed to a container registry, you can deploy to your environment of choice. That could be Kubernetes, Azure Container Apps or any other environment that supports containers.