Skip to content

Commit

Permalink
Add composite image CLI test and refine image output (#905)
Browse files Browse the repository at this point in the history
# Description

Please add an informative description that covers that changes made by
the pull request and link all relevant issues.

# All Promptflow Contribution checklist:
- [ ] **The pull request does not introduce [breaking changes].**
- [ ] **CHANGELOG is updated for new features, bug fixes or other
significant changes.**
- [ ] **I have read the [contribution guidelines](../CONTRIBUTING.md).**
- [ ] **Create an issue and link to the pull request to get dedicated
review from promptflow team. Learn more: [suggested
workflow](../CONTRIBUTING.md#suggested-workflow).**

## General Guidelines and Best Practices
- [ ] Title of the pull request is clear and informative.
- [ ] There are a small number of commits, each of which have an
informative message. This means that previously merged commits do not
appear in the history of the PR. For more information on cleaning up the
commits in your PR, [see this
page](https://github.com/Azure/azure-powershell/blob/master/documentation/development-docs/cleaning-up-commits.md).

### Testing Guidelines
- [ ] Pull request includes test coverage for the included changes.

---------

Co-authored-by: Ying Chen <[email protected]>
  • Loading branch information
2 people authored and brynn-code committed Oct 26, 2023
1 parent 9a1f541 commit 02aed9e
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 187 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,6 @@ Exported files & its dependencies are located in the same folder. The structure
### A template script of the entry file
PyInstaller reads a spec file or Python script written by you. It analyzes your code to discover every other module and library your script needs in order to execute. Then it collects copies of all those files, including the active Python interpreter, and puts them with your script in a single folder, or optionally in a single executable file.

::::{tab-set}
:::{tab-item} app.py
:sync: app.py
We provide a Python entry script named `app.py` as the entry point for the bundled app, which enables you to serve a flow folder as an endpoint.

```python
Expand Down Expand Up @@ -80,87 +77,7 @@ if __name__ == "__main__":
st_cli.main(prog_name="streamlit")

```
:::

:::{tab-item} main.py
:sync: main.py
The `main.py` file will start streamlit service and be called by the entry file.

```python
import json
import os
import streamlit as st
from pathlib import Path

from promptflow._sdk._utils import print_yellow_warning
from promptflow._sdk._serving.flow_invoker import FlowInvoker


invoker = None


def start():
def clear_chat() -> None:
st.session_state.messages = []

def show_conversation() -> None:
if "messages" not in st.session_state:
st.session_state.messages = []
if st.session_state.messages:
for role, message in st.session_state.messages:
st.chat_message(role).write(message)


def submit(**kwargs) -> None:
container.chat_message("user").write(json.dumps(kwargs))
st.session_state.messages.append(("user", json.dumps(kwargs)))
response = run_flow(kwargs)
container.chat_message("assistant").write(response)
st.session_state.messages.append(("assistant", response))


def run_flow(data: dict) -> dict:
global invoker
if not invoker:
flow = Path(__file__).parent / "flow"
os.chdir(flow)
invoker = FlowInvoker(flow, connection_provider="local")
result = invoker.invoke(data)
print_yellow_warning(f"Result: {result}")
return result


st.title("web-classification APP")
st.chat_message("assistant").write("Hello, please input following flow inputs and connection keys.")
container = st.container()
with container:
show_conversation()

with st.form(key='input_form', clear_on_submit=True):
with open(os.path.join(os.path.dirname(__file__), "settings.json"), "r") as file:
json_data = json.load(file)
environment_variables = list(json_data.keys())
for environment_variable in environment_variables:
secret_input = st.text_input(label=environment_variable, type="password", placeholder=f"Please input {environment_variable} here. If you input before, you can leave it blank.")
if secret_input != "":
os.environ[environment_variable] = secret_input

url = st.text_input(label='url', placeholder='https://play.google.com/store/apps/details?id=com.twitter.android')
cols = st.columns(7)
submit_bt = cols[0].form_submit_button(label='Submit')
clear_bt = cols[1].form_submit_button(label='Clear')

if submit_bt:
submit(url=url)

if clear_bt:
clear_chat()

if __name__ == "__main__":
start()
```
:::
::::

### A template script of the spec file
The spec file tells PyInstaller how to process your script. It encodes the script names and most of the options you give to the pyinstaller command. The spec file is actually executable Python code. PyInstaller builds the app by executing the contents of the spec file.
Expand All @@ -177,6 +94,7 @@ datas += collect_data_files('streamlit')
datas += copy_metadata('streamlit')
datas += collect_data_files('keyrings.alt', include_py_files=True)
datas += copy_metadata('keyrings.alt')
datas += collect_data_files('streamlit_quill')

block_cipher = None

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@ Exported files & its dependencies are located in the same folder. The structure
### A template script of the entry file
PyInstaller reads a spec file or Python script written by you. It analyzes your code to discover every other module and library your script needs in order to execute. Then it collects copies of all those files, including the active Python interpreter, and puts them with your script in a single folder, or optionally in a single executable file.

::::{tab-set}
:::{tab-item} app.py
:sync: app.py
We provide a Python entry script named `app.py` as the entry point for the bundled app, which enables you to serve a flow folder as an endpoint.

```python
Expand Down Expand Up @@ -73,85 +70,6 @@ if __name__ == "__main__":
```
:::

:::{tab-item} main.py
:sync: main.py
The `main.py` file will start Streamlit service and be called by the entry file.

```python
import json
import os
import streamlit as st
from pathlib import Path

from promptflow._sdk._utils import print_yellow_warning
from promptflow._sdk._serving.flow_invoker import FlowInvoker


invoker = None


def start():
def clear_chat() -> None:
st.session_state.messages = []

def show_conversation() -> None:
if "messages" not in st.session_state:
st.session_state.messages = []
if st.session_state.messages:
for role, message in st.session_state.messages:
st.chat_message(role).write(message)


def submit(**kwargs) -> None:
container.chat_message("user").write(json.dumps(kwargs))
st.session_state.messages.append(("user", json.dumps(kwargs)))
response = run_flow(kwargs)
container.chat_message("assistant").write(response)
st.session_state.messages.append(("assistant", response))


def run_flow(data: dict) -> dict:
global invoker
if not invoker:
flow = Path(__file__).parent / "flow"
os.chdir(flow)
invoker = FlowInvoker(flow, connection_provider="local")
result = invoker.invoke(data)
print_yellow_warning(f"Result: {result}")
return result


st.title("web-classification APP")
st.chat_message("assistant").write("Hello, please input following flow inputs and connection keys.")
container = st.container()
with container:
show_conversation()

with st.form(key='input_form', clear_on_submit=True):
with open(os.path.join(os.path.dirname(__file__), "settings.json"), "r") as file:
json_data = json.load(file)
environment_variables = list(json_data.keys())
for environment_variable in environment_variables:
secret_input = st.text_input(label=environment_variable, type="password", placeholder=f"Please input {environment_variable} here. If you input before, you can leave it blank.")
if secret_input != "":
os.environ[environment_variable] = secret_input

url = st.text_input(label='url', placeholder='https://play.google.com/store/apps/details?id=com.twitter.android')
cols = st.columns(7)
submit_bt = cols[0].form_submit_button(label='Submit')
clear_bt = cols[1].form_submit_button(label='Clear')

if submit_bt:
submit(url=url)

if clear_bt:
clear_chat()

if __name__ == "__main__":
start()
```
:::
::::

### A template script of the spec file
The spec file tells PyInstaller how to process your script. It encodes the script names and most of the options you give to the pyinstaller command. The spec file is actually executable Python code. PyInstaller builds the app by executing the contents of the spec file.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.hooks import collect_data_files
from PyInstaller.utils.hooks import copy_metadata

datas = [('connections', 'connections'), ('flow', 'flow'), ('settings.json', '.'), ('main.py', '.'), ('{{streamlit_runtime_interpreter_path}}', './streamlit/runtime')]
datas += collect_data_files('streamlit')
datas += copy_metadata('streamlit')
datas += collect_data_files('keyrings.alt', include_py_files=True)
datas += copy_metadata('keyrings.alt')
datas += collect_data_files('streamlit_quill')

block_cipher = None


a = Analysis(
['app.py', 'main.py'],
pathex=[],
binaries=[],
datas=datas,
hiddenimports=['bs4'],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='app',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
Loading

0 comments on commit 02aed9e

Please sign in to comment.