Skip to content

Commit

Permalink
Merge pull request #813 from basetenlabs/bump-version-0.8.1
Browse files Browse the repository at this point in the history
Release 0.8.1
  • Loading branch information
helenlyang authored Feb 1, 2024
2 parents 76e7d7a + eea593b commit 7cdbcd7
Show file tree
Hide file tree
Showing 20 changed files with 242 additions and 87 deletions.
11 changes: 10 additions & 1 deletion docs/_snippets/config-params.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,18 @@ model_metadata:

This is also where display metdata can be stored

### `requirements_file`
Path the requirements file with the required Python dependencies.

We strongly recommend pinning versions in your requirements.

```yaml
requirements_file: ./requirements.txt
```

### `requirements`

List the Python dependencies that the model depends on. The requirements should be provided in the [pip requirements file format](https://pip.pypa.io/en/stable/reference/requirements-file-format/), but as a yaml list.
List the Python dependencies that the model depends on. The requirements should be provided in the [pip requirements file format](https://pip.pypa.io/en/stable/reference/requirements-file-format/), but as a yaml list. These requirements are installed after the ones from `requirements_file`.

We strongly recommend pinning versions in your requirements.

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "truss"
version = "0.8.0rc1"
version = "0.8.1"
description = "A seamless bridge from model development to model delivery"
license = "MIT"
readme = "README.md"
Expand Down
3 changes: 1 addition & 2 deletions truss/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import rich
import rich_click as click
import truss
from truss.cli.console import console
from truss.cli.create import ask_name, select_server_backend
from truss.remote.baseten.core import (
ModelId,
Expand Down Expand Up @@ -44,8 +45,6 @@
]
}

console = rich.console.Console()


def echo_output(f: Callable[..., object]):
@wraps(f)
Expand Down
5 changes: 5 additions & 0 deletions truss/cli/console.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import rich

console = rich.console.Console()

error_console = rich.console.Console(stderr=True, style="bold red")
10 changes: 10 additions & 0 deletions truss/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,21 @@
SERVING_DIR: pathlib.Path = TEMPLATES_DIR

REQUIREMENTS_TXT_FILENAME = "requirements.txt"
USER_SUPPLIED_REQUIREMENTS_TXT_FILENAME = "user_requirements.txt"
BASE_SERVER_REQUIREMENTS_TXT_FILENAME = "base_server_requirements.txt"
SERVER_REQUIREMENTS_TXT_FILENAME = "server_requirements.txt"
TRAINING_REQUIREMENTS_TXT_FILENAME = "training_requirements.txt"
SYSTEM_PACKAGES_TXT_FILENAME = "system_packages.txt"

FILENAME_CONSTANTS_MAP = {
"config_requirements_filename": REQUIREMENTS_TXT_FILENAME,
"user_supplied_requirements_filename": USER_SUPPLIED_REQUIREMENTS_TXT_FILENAME,
"base_server_requirements_filename": BASE_SERVER_REQUIREMENTS_TXT_FILENAME,
"server_requirements_filename": SERVER_REQUIREMENTS_TXT_FILENAME,
"training_requirements_filename": TRAINING_REQUIREMENTS_TXT_FILENAME,
"system_packages_filename": SYSTEM_PACKAGES_TXT_FILENAME,
}

SERVER_DOCKERFILE_TEMPLATE_NAME = "server.Dockerfile.jinja"
TRAINING_DOCKERFILE_TEMPLATE_NAME = "training.Dockerfile.jinja"
MODEL_DOCKERFILE_NAME = "Dockerfile"
Expand Down
12 changes: 12 additions & 0 deletions truss/contexts/image_builder/serving_image_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from truss.constants import (
BASE_SERVER_REQUIREMENTS_TXT_FILENAME,
CONTROL_SERVER_CODE_DIR,
FILENAME_CONSTANTS_MAP,
MODEL_DOCKERFILE_NAME,
REQUIREMENTS_TXT_FILENAME,
SERVER_CODE_DIR,
Expand All @@ -25,6 +26,7 @@
SYSTEM_PACKAGES_TXT_FILENAME,
TEMPLATES_DIR,
TRITON_SERVER_CODE_DIR,
USER_SUPPLIED_REQUIREMENTS_TXT_FILENAME,
)
from truss.contexts.image_builder.cache_warmer import (
AWSCredentials,
Expand Down Expand Up @@ -579,6 +581,11 @@ def copy_into_build_dir(from_path: Path, path_in_build_dir: str):
if spec.requirements
else ""
)
if spec.requirements_file is not None:
copy_into_build_dir(
truss_dir / spec.requirements_file,
USER_SUPPLIED_REQUIREMENTS_TXT_FILENAME,
)
(build_dir / REQUIREMENTS_TXT_FILENAME).write_text(
user_provided_python_requirements
)
Expand Down Expand Up @@ -626,13 +633,17 @@ def _render_dockerfile(
should_install_python_requirements = file_is_not_empty(
build_dir / REQUIREMENTS_TXT_FILENAME
)
should_install_user_requirements_file = file_is_not_empty(
build_dir / USER_SUPPLIED_REQUIREMENTS_TXT_FILENAME
)

hf_access_token = config.secrets.get(HF_ACCESS_TOKEN_SECRET_NAME)
dockerfile_contents = dockerfile_template.render(
should_install_server_requirements=should_install_server_requirements,
base_image_name_and_tag=base_image_name_and_tag,
should_install_system_requirements=should_install_system_requirements,
should_install_requirements=should_install_python_requirements,
should_install_user_requirements_file=should_install_user_requirements_file,
config=config,
python_version=python_version,
live_reload=config.live_reload,
Expand All @@ -649,6 +660,7 @@ def _render_dockerfile(
hf_access_token=hf_access_token,
hf_access_token_file_name=HF_ACCESS_TOKEN_FILE_NAME,
external_data_files=external_data_files,
**FILENAME_CONSTANTS_MAP,
)
docker_file_path = build_dir / MODEL_DOCKERFILE_NAME
docker_file_path.write_text(dockerfile_contents)
2 changes: 2 additions & 0 deletions truss/contexts/image_builder/training_image_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from jinja2 import Environment, FileSystemLoader
from truss.constants import (
FILENAME_CONSTANTS_MAP,
REQUIREMENTS_TXT_FILENAME,
SHARED_SERVING_AND_TRAINING_CODE_DIR,
SHARED_SERVING_AND_TRAINING_CODE_DIR_NAME,
Expand Down Expand Up @@ -117,6 +118,7 @@ def prepare_image_build_dir(self, build_dir: Optional[Path] = None):
bundled_packages_dir_exists=bundled_packages_dir_exists,
should_install_system_requirements=should_install_system_requirements,
should_install_requirements=should_install_requirements,
**FILENAME_CONSTANTS_MAP,
)
docker_file_path = build_dir / TRAINING_DOCKERFILE_NAME
with docker_file_path.open("w") as docker_file:
Expand Down
41 changes: 0 additions & 41 deletions truss/contexts/local_loader/truss_file_syncer.py

This file was deleted.

67 changes: 39 additions & 28 deletions truss/remote/baseten/remote.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import logging
import re
from pathlib import Path
import time
from typing import List, Optional, Tuple

import click
import rich
import yaml
from requests import ReadTimeout
from truss.contexts.local_loader.truss_file_syncer import TrussFilesSyncer
from truss.local.local_config_handler import LocalConfigHandler
from truss.remote.baseten.api import BasetenApi
from truss.remote.baseten.auth import AuthService
Expand All @@ -31,6 +30,8 @@
from truss.remote.truss_remote import TrussRemote, TrussService
from truss.truss_config import ModelServer
from truss.truss_handle import TrussHandle
from truss.util.path import is_ignored, load_trussignore_patterns
from watchfiles import watch


class BasetenRemote(TrussRemote):
Expand Down Expand Up @@ -213,44 +214,50 @@ def sync_truss_to_dev_version_by_name(
"No development model found. Run `truss push` then try again."
)

# TODO(helen): refactor this such that truss watch runs on the main thread.
TrussFilesSyncer(
Path(target_directory),
self,
).start()
watch_path = Path(target_directory)
trussignore_patterns = load_trussignore_patterns()

# Since the `TrussFilesSyncer` runs a daemon thread, we run this infinite loop on the main
# thread to keep it alive. When this loop is interrupted by the user, then the whole process
# can shutdown gracefully.
while True:
time.sleep(100)
def watch_filter(_, path):
return not is_ignored(
Path(path),
trussignore_patterns,
)

# disable watchfiles logger
logging.getLogger("watchfiles.main").disabled = True

rich.print(f"🚰 Attempting to sync truss at '{watch_path}' with remote")
self.patch(watch_path)

rich.print(f"👀 Watching for changes to truss at '{watch_path}' ...")
for _ in watch(watch_path, watch_filter=watch_filter, raise_interrupt=False):
self.patch(watch_path)

def patch(
self,
watch_path: Path,
logger: logging.Logger,
):
from truss.cli.console import console, error_console

try:
truss_handle = TrussHandle(watch_path)
except yaml.parser.ParserError:
logger.error("Unable to parse config file")
error_console.print("Unable to parse config file")
return
except ValueError:
logger.error(
f"Error when reading truss from directory {watch_path}", exc_info=True
)
error_console.print(f"Error when reading truss from directory {watch_path}")
return
model_name = truss_handle.spec.config.model_name
dev_version = get_dev_version(self._api, model_name) # type: ignore
if not dev_version:
logger.error(
error_console.print(
f"No development deployment found with model name: {model_name}"
)
return
truss_hash = dev_version.get("truss_hash", None)
truss_signature = dev_version.get("truss_signature", None)
if not (truss_hash and truss_signature):
logger.error(
error_console.print(
"""Failed to inspect a running remote deployment to watch for changes.
Ensure that there exists a running remote deployment before attempting to watch for changes
"""
Expand All @@ -260,39 +267,43 @@ def patch(
try:
patch_request = truss_handle.calc_patch(truss_hash)
except Exception:
logger.error("Failed to calculate patch, bailing on patching")
error_console.print("Failed to calculate patch, bailing on patching")
return
if patch_request:
if (
patch_request.prev_hash == patch_request.next_hash
or len(patch_request.patch_ops) == 0
):
logger.info("No changes observed, skipping deployment")
console.print("No changes observed, skipping patching")
return
try:
resp = self._api.patch_draft_truss(model_name, patch_request)
with console.status("Applying patch..."):
resp = self._api.patch_draft_truss(model_name, patch_request)
except ReadTimeout:
logger.error(
error_console.print(
"Read Timeout when attempting to connect to remote. Bailing on patching"
)
return
except Exception:
logger.error("Failed to patch draft deployment, bailing on patching")
error_console.print(
"Failed to patch draft deployment, bailing on patching"
)
return
if not resp["succeeded"]:
needs_full_deploy = resp.get("needs_full_deploy", None)
if needs_full_deploy:
logger.info(
error_console.print(
f"Model {model_name} is not able to be patched, use `truss push` to deploy"
)
else:
logger.error(
error_console.print(
f"Failed to patch: `{resp['error']}`. Model left in original state"
)
else:
logger.info(
console.print(
resp.get(
"success_message",
f"Model {model_name} patched successfully",
)
),
style="green",
)
12 changes: 8 additions & 4 deletions truss/templates/base.Dockerfile.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ RUN pip install mkl

{% block install_system_requirements %}
{%- if should_install_system_requirements %}
COPY ./system_packages.txt system_packages.txt
RUN apt-get update && apt-get install --yes --no-install-recommends $(cat system_packages.txt) \
COPY ./{{system_packages_filename}} {{system_packages_filename}}
RUN apt-get update && apt-get install --yes --no-install-recommends $(cat {{system_packages_filename}}) \
&& apt-get autoremove -y \
&& apt-get clean -y \
&& rm -rf /var/lib/apt/lists/*
Expand All @@ -37,9 +37,13 @@ RUN apt-get update && apt-get install --yes --no-install-recommends $(cat system


{% block install_requirements %}
{%- if should_install_user_requirements_file %}
COPY ./{{user_supplied_requirements_filename}} {{user_supplied_requirements_filename}}
RUN pip install -r {{user_supplied_requirements_filename}} --no-cache-dir && rm -rf /root/.cache/pip
{%- endif %}
{%- if should_install_requirements %}
COPY ./requirements.txt requirements.txt
RUN pip install -r requirements.txt --no-cache-dir && rm -rf /root/.cache/pip
COPY ./{{config_requirements_filename}} {{config_requirements_filename}}
RUN pip install -r {{config_requirements_filename}} --no-cache-dir && rm -rf /root/.cache/pip
{%- endif %}
{% endblock %}

Expand Down
11 changes: 4 additions & 7 deletions truss/templates/server.Dockerfile.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ RUN apt update && \
&& apt-get clean -y \
&& rm -rf /var/lib/apt/lists/*

COPY ./base_server_requirements.txt base_server_requirements.txt
RUN pip install -r base_server_requirements.txt --no-cache-dir && rm -rf /root/.cache/pip
COPY ./{{base_server_requirements_filename}} {{base_server_requirements_filename}}
RUN pip install -r {{base_server_requirements_filename}} --no-cache-dir && rm -rf /root/.cache/pip

{%- if config.live_reload %}
RUN $PYTHON_EXECUTABLE -m venv -h >/dev/null \
Expand All @@ -43,8 +43,8 @@ RUN ln -sf {{config.base_image.python_executable_path}} /usr/local/bin/python

{% block install_requirements %}
{%- if should_install_server_requirements %}
COPY ./server_requirements.txt server_requirements.txt
RUN pip install -r server_requirements.txt --no-cache-dir && rm -rf /root/.cache/pip
COPY ./{{server_requirements_filename}} {{server_requirements_filename}}
RUN pip install -r {{server_requirements_filename}} --no-cache-dir && rm -rf /root/.cache/pip
{%- endif %}
{{ super() }}
{% endblock %}
Expand Down Expand Up @@ -75,9 +75,6 @@ COPY ./control /control
RUN python3 -m venv /control/.env \
&& /control/.env/bin/pip3 install -r /control/requirements.txt
{%- endif %}



{% endblock %}

{% block run %}
Expand Down
Loading

0 comments on commit 7cdbcd7

Please sign in to comment.