Skip to content

Commit

Permalink
Integrate pfs in pf command (#2413)
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
YingChen1996 and Ying Chen authored Apr 1, 2024
1 parent 7757f80 commit 7f83ed4
Show file tree
Hide file tree
Showing 18 changed files with 159 additions and 313 deletions.
3 changes: 0 additions & 3 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@
"**/setup.py",
"scripts/installer/curl_install_pypi/**",
"scripts/installer/windows/**",
"src/promptflow/promptflow/_sdk/_service/pfsvc.py",
"src/promptflow-devkit/promptflow/_sdk/_service/pfsvc.py",
".github/workflows/**",
".github/actions/**",
".github/pipelines/**"
Expand Down Expand Up @@ -94,7 +92,6 @@
"nunit",
"astext",
"Likert",
"pfsvc",
"geval",
"Summ",
"Bhavik",
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/promptflow-sdk-cli-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -179,4 +179,4 @@ jobs:
osVersion: ubuntu-latest
pythonVersion: 3.9
coverageThreshold: 40
context: test/sdk_cli
context: test/sdk_cli
5 changes: 1 addition & 4 deletions scripts/installer/curl_install_pypi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,12 @@ Uninstall the promptflow by directly deleting the files from the location chosen
```bash
# The default install/executable location is the user's home directory ($HOME).
rm -r $HOME/lib/promptflow
rm $HOME/bin/pf
rm $HOME/bin/pfs
rm $HOME/bin/pfazure
```

2. Modify your `$HOME/.bash_profile` or `$HOME/.bashrc` file to remove the following line:

```text
export PATH=$PATH:$HOME/bin
export PATH=$PATH:$HOME/lib/promptflow/bin
```

3. If using `bash` or `zsh`, reload your shell's command cache.
Expand Down
130 changes: 16 additions & 114 deletions scripts/installer/curl_install_pypi/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,15 @@
import os
import sys
import platform
import stat
import tempfile
import shutil
import subprocess
import hashlib


PF_DISPATCH_TEMPLATE = """#!/usr/bin/env bash
export PF_INSTALLER=Script
{install_dir}/bin/python -m promptflow._cli._pf.entry "$@"
"""

PFAZURE_DISPATCH_TEMPLATE = """#!/usr/bin/env bash
{install_dir}/bin/python -m promptflow.azure._cli.entry "$@"
"""

PFS_DISPATCH_TEMPLATE = """#!/usr/bin/env bash
{install_dir}/bin/python -m promptflow._sdk._service.entry "$@"
"""

DEFAULT_INSTALL_DIR = os.path.expanduser(os.path.join('~', 'lib', 'promptflow'))
DEFAULT_EXEC_DIR = os.path.expanduser(os.path.join('~', 'bin'))
PF_EXECUTABLE_NAME = 'pf'
PFAZURE_EXECUTABLE_NAME = 'pfazure'
PFS_EXECUTABLE_NAME = 'pfs'


USER_BASH_RC = os.path.expanduser(os.path.join('~', '.bashrc'))
Expand Down Expand Up @@ -96,46 +80,22 @@ def create_dir(dir):
os.makedirs(dir)


def is_valid_sha256sum(a_file, expected_sum):
sha256 = hashlib.sha256()
with open(a_file, 'rb') as f:
sha256.update(f.read())
computed_hash = sha256.hexdigest()
return expected_sum == computed_hash


def create_virtualenv(install_dir):
cmd = [sys.executable, '-m', 'venv', install_dir]
exec_command(cmd)


def install_cli(install_dir, tmp_dir):
path_to_pip = os.path.join(install_dir, 'bin', 'pip')
cmd = [path_to_pip, 'install', '--cache-dir', tmp_dir, 'promptflow[azure,executable,azureml-serving]',
'--upgrade']
cmd = [path_to_pip, 'install', '--cache-dir', tmp_dir,
'promptflow[azure,executable,azureml-serving,executor-service]', '--upgrade']
exec_command(cmd)
cmd = [path_to_pip, 'install', '--cache-dir', tmp_dir, 'promptflow-tools', '--upgrade']
exec_command(cmd)
cmd = [path_to_pip, 'install', '--cache-dir', tmp_dir, 'keyrings.alt', '--upgrade']
exec_command(cmd)


def create_executable(exec_dir, install_dir):
create_dir(exec_dir)
exec_filepaths = []
for filename, template in [(PF_EXECUTABLE_NAME, PF_DISPATCH_TEMPLATE),
(PFAZURE_EXECUTABLE_NAME, PFAZURE_DISPATCH_TEMPLATE),
(PFS_EXECUTABLE_NAME, PFS_DISPATCH_TEMPLATE)]:
exec_filepath = os.path.join(exec_dir, filename)
with open(exec_filepath, 'w') as exec_file:
exec_file.write(template.format(install_dir=install_dir))
cur_stat = os.stat(exec_filepath)
os.chmod(exec_filepath, cur_stat.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
print_status("The executable is available at '{}'.".format(exec_filepath))
exec_filepaths.append(exec_filepath)
return exec_filepaths


def get_install_dir():
install_dir = None
while not install_dir:
Expand All @@ -161,21 +121,6 @@ def get_install_dir():
return install_dir


def get_exec_dir():
exec_dir = None
while not exec_dir:
prompt_message = (f"In what directory would you like to place the "
f"'{PFS_EXECUTABLE_NAME}/{PFS_EXECUTABLE_NAME}/{PFAZURE_EXECUTABLE_NAME}' executable?")
exec_dir = prompt_input_with_default(prompt_message, DEFAULT_EXEC_DIR)
exec_dir = os.path.realpath(os.path.expanduser(exec_dir))
if ' ' in exec_dir:
print_status("The executable directory '{}' cannot contain spaces.".format(exec_dir))
exec_dir = None
create_dir(exec_dir)
print_status("The executable will be in '{}'.".format(exec_dir))
return exec_dir


def _backup_rc(rc_file):
try:
shutil.copyfile(rc_file, rc_file+'.backup')
Expand Down Expand Up @@ -240,28 +185,28 @@ def warn_other_azs_on_path(exec_dir, exec_filepath):
conflicting_paths = []
if env_path:
for p in env_path.split(':'):
for file in [PF_EXECUTABLE_NAME, PFAZURE_EXECUTABLE_NAME, PFS_EXECUTABLE_NAME]:
for file in [PF_EXECUTABLE_NAME, PFAZURE_EXECUTABLE_NAME]:
p_to_pf = os.path.join(p, file)
if p != exec_dir and os.path.isfile(p_to_pf):
conflicting_paths.append(p_to_pf)
if conflicting_paths:
print_status()
print_status(f"** WARNING: Other '{PFS_EXECUTABLE_NAME}/{PFS_EXECUTABLE_NAME}/{PFAZURE_EXECUTABLE_NAME}' "
print_status(f"** WARNING: Other '{PF_EXECUTABLE_NAME}/{PFAZURE_EXECUTABLE_NAME}' "
f"executables are on your $PATH. **")
print_status("Conflicting paths: {}".format(', '.join(conflicting_paths)))
print_status("You can run this installation of the promptflow with '{}'.".format(exec_filepath))


def handle_path_and_tab_completion(exec_filepath, exec_dir):
def handle_path_and_tab_completion(exec_filepath, install_dir):
ans_yes = prompt_y_n('Modify profile to update your $PATH now?', 'y')
if ans_yes:
rc_file_path = get_rc_file_path()
if not rc_file_path:
raise CLIInstallError('No suitable profile file found.')
_backup_rc(rc_file_path)
line_to_add = "export PATH=$PATH:{}".format(exec_dir)
line_to_add = "export PATH=$PATH:{}".format(os.path.join(install_dir, "bin"))
_modify_rc(rc_file_path, line_to_add)
warn_other_azs_on_path(exec_dir, exec_filepath)
warn_other_azs_on_path(install_dir, exec_filepath)
print_status()
print_status('** Run `exec -l $SHELL` to restart your shell. **')
print_status()
Expand All @@ -280,59 +225,16 @@ def verify_python_version():
print_status('Python version {}.{}.{} okay.'.format(v.major, v.minor, v.micro))


def _native_dependencies_for_dist(verify_cmd_args, install_cmd_args, dep_list):
try:
print_status("Executing: '{} {}'".format(' '.join(verify_cmd_args), ' '.join(dep_list)))
subprocess.check_output(verify_cmd_args + dep_list, stderr=subprocess.STDOUT)
print_status('Native dependencies okay.')
except subprocess.CalledProcessError:
err_msg = 'One or more of the following native dependencies are not currently installed and may be required.\n'
err_msg += '"{}"'.format(' '.join(install_cmd_args + dep_list))
print_status(err_msg)
ans_yes = prompt_y_n('Missing native dependencies. Attempt to continue anyway?', 'n')
if not ans_yes:
raise CLIInstallError('Please install the native dependencies and try again.')


def _get_linux_distro():
if platform.system() != 'Linux':
return None, None

try:
with open('/etc/os-release') as lines:
tokens = [line.strip() for line in lines]
except Exception:
return None, None

release_info = {}
for token in tokens:
if '=' in token:
k, v = token.split('=', 1)
release_info[k.lower()] = v.strip('"')

return release_info.get('name', None), release_info.get('version_id', None)


def verify_install_dir_exec_path_conflict(install_dir, exec_dir):
for exec_name in [PF_EXECUTABLE_NAME, PFAZURE_EXECUTABLE_NAME, PFS_EXECUTABLE_NAME]:
exec_path = os.path.join(exec_dir, exec_name)
if install_dir == exec_path:
raise CLIInstallError("The executable file '{}' would clash with the install directory of '{}'. Choose "
"either a different install directory or directory to place the "
"executable.".format(exec_path, install_dir))


def main():
verify_python_version()
tmp_dir = create_tmp_dir()
install_dir = get_install_dir()
exec_dir = get_exec_dir()
verify_install_dir_exec_path_conflict(install_dir, exec_dir)
create_virtualenv(install_dir)
install_cli(install_dir, tmp_dir)
exec_filepath = create_executable(exec_dir, install_dir)
exec_filepath = [os.path.join(install_dir, "bin", PF_EXECUTABLE_NAME),
os.path.join(install_dir, "bin", PFAZURE_EXECUTABLE_NAME)]
try:
handle_path_and_tab_completion(exec_filepath, exec_dir)
handle_path_and_tab_completion(exec_filepath, install_dir)
except Exception as e:
print_status("Unable to set up PATH. ERROR: {}".format(str(e)))
shutil.rmtree(tmp_dir)
Expand All @@ -351,10 +253,10 @@ def main():
sys.exit(1)

# SIG # Begin signature block
# Z1F07ShfIJ7kejST2NXwW1QcFPEya4xaO2xZz6vLT847zaMzbc/PaEa1RKFlD881
# 4J+i6Au2wtbHzOXDisyH6WeLQ3gh0X2gxFRa4EzW7Nzjcvwm4+WogiTcnPVVxlk3
# qafM/oyVqs3695K7W5XttOiq2guv/yedsf/TW2BKSEKruFQh9IwDfIiBoi9Zv3wa
# iuzQulRR8KyrCtjEPDV0t4WnZVB/edQea6xJZeTlMG+uLR/miBTbPhUb/VZkVjBf
# qHBv623oLXICzoTNuaPTln9OWvL2NZpisGYvNzebKO7/Ho6AOWZNs5XOVnjs0Ax2
# aeXvlwBzIQyfyxd25487/Q==
# Op0/tmmNDX4QgQefj28K91e/ClVKWeYaA1w1kb5Hi8ALJZtmvyhwvxYlCRZ9eWT+
# wFBfSvpUIzAWYxEMfVqqWy7g9AzqHGa5vE37zQ7uGkIyR0OsmO0bkauOv5FxuCWX
# U0u9d9sir6sRTb2nrEj2O1EXAcP2xNaW77w1fcOtMX9W6ytHXx/+v5p387+/HBnQ
# y00GvJrrcIJeRj4MboBdDdv0VgGJAAlTJp0hxO0lt5ZQUMJvi/sM4e1cPUTcx7uB
# djCmvSZzZrJO2ZIIWiyiP2XNYWJv4A9klJWMbWKukeZOSjxYPS0pO2mTftkKoL5U
# sOJu9RtRWIIx/kLG/Axliw==
# SIG # End signature block
15 changes: 14 additions & 1 deletion scripts/installer/windows/scripts/pf.bat
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,17 @@ setlocal

SET PF_INSTALLER=MSI
set MAIN_EXE=%~dp0.\pfcli.exe
"%MAIN_EXE%" pf %*
REM Check if the first argument is 'start'
if "%~1"=="service" (
REM Check if the second argument is 'start'
if "%~2"=="start" (
cscript //nologo %~dp0.\start_pfs.vbs """%MAIN_EXE%"" pf %*"
REM since we won't wait for vbs to finish, we need to wait for the output file to be flushed to disk
timeout /t 5 >nul
type "%~dp0output.txt"
) else (
"%MAIN_EXE%" pf %*
)
) else (
"%MAIN_EXE%" pf %*
)
8 changes: 1 addition & 7 deletions scripts/installer/windows/scripts/pfcli.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,5 @@
elif command == 'pfazure':
from promptflow.azure._cli.entry import main as pfazure_main
pfazure_main()
elif command == 'pfs':
from promptflow._sdk._service.entry import main as pfs_main
pfs_main()
elif command == 'pfsvc':
from promptflow._sdk._service.pfsvc import init as pfsvc_init
pfsvc_init()
else:
print(f"Invalid command {sys.argv}. Please use 'pf', 'pfazure', 'pfs' or 'pfsvc'.")
print(f"Invalid command {sys.argv}. Please use 'pf', 'pfazure'.")
13 changes: 0 additions & 13 deletions scripts/installer/windows/scripts/pfs.bat

This file was deleted.

5 changes: 0 additions & 5 deletions scripts/installer/windows/scripts/pfsvc.bat

This file was deleted.

2 changes: 1 addition & 1 deletion scripts/installer/windows/scripts/promptflow.spec.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ from PyInstaller.utils.hooks import collect_data_files, collect_all, copy_metada
datas = [('../resources/CLI_LICENSE.rtf', '.'), ('../../../../src/promptflow/NOTICE.txt', '.'),
('../../../../src/promptflow/promptflow/_sdk/data/executable/', './promptflow/_sdk/data/executable/'),
('../../../../src/promptflow-tools/promptflow/tools/', './promptflow/tools/'),
('./pf.bat', '.'), ('./pfs.bat', '.'), ('./pfazure.bat', '.'), ('./pfsvc.bat', '.'), ('./start_pfs.vbs', '.')]
('./pf.bat', '.'), ('./pfazure.bat', '.'), ('./start_pfs.vbs', '.')]


all_packages = {{all_packages}}
Expand Down
2 changes: 1 addition & 1 deletion scripts/installer/windows/scripts/promptflow_service.vbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
DIM objshell
set objshell = wscript.createobject("wscript.shell")
iReturn = objshell.run("pfcli.exe pfs start --force", 0, true)
iReturn = objshell.run("pfcli.exe pf service start --force", 0, true)
9 changes: 5 additions & 4 deletions src/promptflow-devkit/pf
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#!/usr/bin/env python

import sys
import os
import sys


def main():
if os.environ.get('PF_INSTALLER') is None:
os.environ['PF_INSTALLER'] = 'PIP'
if os.environ.get("PF_INSTALLER") is None:
os.environ["PF_INSTALLER"] = "PIP"

os.execl(sys.executable, sys.executable, '-m', 'promptflow._cli._pf.entry', *sys.argv[1:])
os.execl(sys.executable, sys.executable, "-m", "promptflow._cli._pf.entry", *sys.argv[1:])
Loading

0 comments on commit 7f83ed4

Please sign in to comment.