truss 0.10.9rc535__py3-none-any.whl → 0.10.10rc0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of truss might be problematic. Click here for more details.

Files changed (33) hide show
  1. truss/cli/logs/base_watcher.py +1 -1
  2. truss/cli/train/deploy_checkpoints/deploy_checkpoints.py +30 -22
  3. truss/cli/train/deploy_checkpoints/deploy_checkpoints_helpers.py +8 -2
  4. truss/cli/train/deploy_checkpoints/deploy_full_checkpoints.py +14 -7
  5. truss/cli/train/deploy_checkpoints/deploy_whisper_checkpoints.py +63 -0
  6. truss/cli/train/deploy_from_checkpoint_config_whisper.yml +17 -0
  7. truss/cli/train/metrics_watcher.py +170 -59
  8. truss/cli/train_commands.py +11 -3
  9. truss/contexts/image_builder/serving_image_builder.py +22 -39
  10. truss/remote/baseten/api.py +11 -0
  11. truss/remote/baseten/core.py +209 -1
  12. truss/remote/baseten/utils/time.py +15 -0
  13. truss/templates/base.Dockerfile.jinja +6 -23
  14. truss/templates/cache.Dockerfile.jinja +5 -5
  15. truss/templates/copy_cache_files.Dockerfile.jinja +1 -1
  16. truss/templates/docker_server/supervisord.conf.jinja +0 -1
  17. truss/templates/server/requirements.txt +1 -1
  18. truss/templates/server.Dockerfile.jinja +16 -33
  19. truss/tests/cli/train/test_deploy_checkpoints.py +446 -2
  20. truss/tests/cli/train/test_train_cli_core.py +96 -0
  21. truss/tests/remote/baseten/conftest.py +18 -0
  22. truss/tests/remote/baseten/test_api.py +49 -14
  23. truss/tests/remote/baseten/test_core.py +517 -1
  24. {truss-0.10.9rc535.dist-info → truss-0.10.10rc0.dist-info}/METADATA +2 -2
  25. {truss-0.10.9rc535.dist-info → truss-0.10.10rc0.dist-info}/RECORD +31 -29
  26. truss_train/definitions.py +6 -0
  27. truss_train/deployment.py +15 -2
  28. truss_train/loader.py +7 -20
  29. truss/tests/util/test_basetenpointer.py +0 -227
  30. truss/util/basetenpointer.py +0 -160
  31. {truss-0.10.9rc535.dist-info → truss-0.10.10rc0.dist-info}/WHEEL +0 -0
  32. {truss-0.10.9rc535.dist-info → truss-0.10.10rc0.dist-info}/entry_points.txt +0 -0
  33. {truss-0.10.9rc535.dist-info → truss-0.10.10rc0.dist-info}/licenses/LICENSE +0 -0
@@ -669,6 +669,17 @@ class BasetenApi:
669
669
  # NB(nikhil): reverse order so latest logs are at the end
670
670
  return resp_json["logs"][::-1]
671
671
 
672
+ def _fetch_log_batch(
673
+ self, project_id: str, job_id: str, query_params: Dict[str, Any]
674
+ ) -> List[Any]:
675
+ """
676
+ Fetch a single batch of logs from the API.
677
+ """
678
+ resp_json = self._rest_api_client.post(
679
+ f"v1/training_projects/{project_id}/jobs/{job_id}/logs", body=query_params
680
+ )
681
+ return resp_json["logs"]
682
+
672
683
  def get_training_job_checkpoint_presigned_url(
673
684
  self, project_id: str, job_id: str, page_size: int = 100
674
685
  ) -> List[Dict[str, str]]:
@@ -3,7 +3,9 @@ import json
3
3
  import logging
4
4
  import pathlib
5
5
  import textwrap
6
- from typing import IO, TYPE_CHECKING, List, NamedTuple, Optional, Tuple, Type
6
+ from typing import IO, TYPE_CHECKING, Any, Dict, List, NamedTuple, Optional, Tuple, Type
7
+
8
+ import requests
7
9
 
8
10
  from truss.base.errors import ValidationError
9
11
 
@@ -15,6 +17,7 @@ from truss.remote.baseten import custom_types as b10_types
15
17
  from truss.remote.baseten.api import BasetenApi
16
18
  from truss.remote.baseten.error import ApiError
17
19
  from truss.remote.baseten.utils.tar import create_tar_with_progress_bar
20
+ from truss.remote.baseten.utils.time import iso_to_millis
18
21
  from truss.remote.baseten.utils.transfer import multipart_upload_boto3
19
22
  from truss.util.path import load_trussignore_patterns_from_truss_dir
20
23
 
@@ -27,6 +30,16 @@ NO_ENVIRONMENTS_EXIST_ERROR_MESSAGING = (
27
30
  "Model hasn't been deployed yet. No environments exist."
28
31
  )
29
32
 
33
+ # Maximum number of iterations to prevent infinite loops when paginating logs
34
+ MAX_ITERATIONS = 10_000
35
+ MIN_BATCH_SIZE = 100
36
+
37
+ # LIMIT for the number of logs to fetch per request defined by the server
38
+ MAX_BATCH_SIZE = 1000
39
+
40
+ NANOSECONDS_PER_MILLISECOND = 1_000_000
41
+ MILLISECONDS_PER_HOUR = 60 * 60 * 1000
42
+
30
43
 
31
44
  class ModelIdentifier:
32
45
  value: str
@@ -465,3 +478,198 @@ def validate_truss_config_against_backend(api: BasetenApi, config: str):
465
478
  raise ValidationError(
466
479
  f"Validation failed with the following errors:\n{error_messages}"
467
480
  )
481
+
482
+
483
+ def _build_log_query_params(
484
+ start_time: Optional[int], end_time: Optional[int], batch_size: int
485
+ ) -> Dict[str, Any]:
486
+ """
487
+ Build query parameters for log fetching request.
488
+
489
+ Args:
490
+ start_time: Start time in milliseconds since epoch
491
+ end_time: End time in milliseconds since epoch
492
+ batch_size: Number of logs to fetch per request
493
+
494
+ Returns:
495
+ Dictionary of query parameters with None values removed
496
+ """
497
+ query_body = {
498
+ "start_epoch_millis": start_time,
499
+ "end_epoch_millis": end_time,
500
+ "limit": batch_size,
501
+ "direction": "asc",
502
+ }
503
+
504
+ return {k: v for k, v in query_body.items() if v is not None}
505
+
506
+
507
+ def _handle_server_error_backoff(
508
+ error: requests.HTTPError, job_id: str, iteration: int, batch_size: int
509
+ ) -> int:
510
+ """
511
+ Slash the batch size in half and return the new batch size
512
+ """
513
+ old_batch_size = batch_size
514
+ new_batch_size = max(batch_size // 2, MIN_BATCH_SIZE)
515
+
516
+ logging.warning(
517
+ f"Server error (HTTP {error.response.status_code}) for job {job_id} at iteration {iteration}. "
518
+ f"Reducing batch size from {old_batch_size} to {new_batch_size}. Retrying..."
519
+ )
520
+
521
+ return new_batch_size
522
+
523
+
524
+ def _process_batch_logs(
525
+ batch_logs: List[Any], job_id: str, iteration: int, batch_size: int
526
+ ) -> Tuple[bool, Optional[int], Optional[int]]:
527
+ """
528
+ Process a batch of logs and determine if pagination should continue.
529
+
530
+ Args:
531
+ batch_logs: List of logs from the current batch
532
+ job_id: The job ID for logging
533
+ iteration: Current iteration number for logging
534
+ batch_size: Expected batch size
535
+
536
+ Returns:
537
+ Tuple of (should_continue, next_start_time, next_end_time)
538
+ """
539
+
540
+ # If no logs returned, we're done
541
+ if not batch_logs:
542
+ logging.info(f"No logs returned for job {job_id} at iteration {iteration}")
543
+ return False, None, None
544
+
545
+ # If we got fewer logs than the batch size, we've reached the end
546
+ if len(batch_logs) == 0:
547
+ logging.info(f"Reached end of logs for job {job_id} at iteration {iteration}")
548
+ return False, None, None
549
+
550
+ # Timestamp returned in nanoseconds for the last log in this batch converted
551
+ # to milliseconds to use as start for next iteration
552
+ last_log_timestamp = int(batch_logs[-1]["timestamp"]) // NANOSECONDS_PER_MILLISECOND
553
+
554
+ # Update start time for next iteration (add 1ms to avoid overlap)
555
+ next_start_time_ms = last_log_timestamp + 1
556
+
557
+ # Set end time to 2 hours from next start time, maximum time delta allowed by the API
558
+ next_end_time_ms = next_start_time_ms + 2 * MILLISECONDS_PER_HOUR
559
+
560
+ return True, next_start_time_ms, next_end_time_ms
561
+
562
+
563
+ class BatchedTrainingLogsFetcher:
564
+ """
565
+ Iterator for fetching training job logs in batches using time-based pagination.
566
+
567
+ This iterator handles the complexity of paginating through training job logs,
568
+ including error handling, batch size adjustment, and time window management.
569
+ """
570
+
571
+ def __init__(
572
+ self,
573
+ api: BasetenApi,
574
+ project_id: str,
575
+ job_id: str,
576
+ batch_size: int = MAX_BATCH_SIZE,
577
+ ):
578
+ self.api = api
579
+ self.project_id = project_id
580
+ self.job_id = job_id
581
+ self.batch_size = batch_size
582
+ self.iteration = 0
583
+ self.current_start_time = None
584
+ self.current_end_time = None
585
+ self._initialize_time_window()
586
+
587
+ def _initialize_time_window(self):
588
+ training_job = self.api.get_training_job(self.project_id, self.job_id)
589
+ self.current_start_time = iso_to_millis(
590
+ training_job["training_job"]["created_at"]
591
+ )
592
+ self.current_end_time = self.current_start_time + 2 * MILLISECONDS_PER_HOUR
593
+
594
+ def __iter__(self):
595
+ return self
596
+
597
+ def __next__(self) -> List[Any]:
598
+ if self.iteration >= MAX_ITERATIONS:
599
+ logging.warning(
600
+ f"Reached maximum iteration limit ({MAX_ITERATIONS}) while paginating "
601
+ f"training job logs for project_id={self.project_id}, job_id={self.job_id}."
602
+ )
603
+ raise StopIteration
604
+
605
+ query_params = _build_log_query_params(
606
+ self.current_start_time, self.current_end_time, self.batch_size
607
+ )
608
+
609
+ try:
610
+ batch_logs = self.api._fetch_log_batch(
611
+ self.project_id, self.job_id, query_params
612
+ )
613
+
614
+ should_continue, next_start_time, next_end_time = _process_batch_logs(
615
+ batch_logs, self.job_id, self.iteration, self.batch_size
616
+ )
617
+
618
+ if not should_continue:
619
+ logging.info(
620
+ f"Completed pagination for job {self.job_id}. Total iterations: {self.iteration + 1}"
621
+ )
622
+ raise StopIteration
623
+
624
+ self.current_start_time = next_start_time # type: ignore[assignment]
625
+ self.current_end_time = next_end_time # type: ignore[assignment]
626
+ self.iteration += 1
627
+
628
+ return batch_logs
629
+
630
+ except requests.HTTPError as e:
631
+ if 500 <= e.response.status_code < 600:
632
+ if self.batch_size == MIN_BATCH_SIZE:
633
+ logging.error(
634
+ "Failed to fetch all training job logs due to persistent server errors. "
635
+ "Please try again later or contact support if the issue persists."
636
+ )
637
+ raise StopIteration
638
+ self.batch_size = _handle_server_error_backoff(
639
+ e, self.job_id, self.iteration, self.batch_size
640
+ )
641
+ # Retry the same iteration with reduced batch size
642
+ return self.__next__()
643
+ else:
644
+ logging.error(
645
+ f"HTTP error fetching logs for job {self.job_id} at iteration {self.iteration}: {e}"
646
+ )
647
+ raise StopIteration
648
+ except Exception as e:
649
+ logging.error(
650
+ f"Error fetching logs for job {self.job_id} at iteration {self.iteration}: {e}"
651
+ )
652
+ raise StopIteration
653
+
654
+
655
+ def get_training_job_logs_with_pagination(
656
+ api: BasetenApi, project_id: str, job_id: str, batch_size: int = MAX_BATCH_SIZE
657
+ ) -> List[Any]:
658
+ """
659
+ This method implements forward time-based pagination by starting from the earliest
660
+ available log and working forward in time. It uses the timestamp of the newest log in
661
+ each batch as the start time for the next request.
662
+
663
+ Returns:
664
+ List of all logs in chronological order (oldest first)
665
+ """
666
+ all_logs = []
667
+
668
+ logs_iterator = BatchedTrainingLogsFetcher(api, project_id, job_id, batch_size)
669
+
670
+ for batch_logs in logs_iterator:
671
+ all_logs.extend(batch_logs)
672
+
673
+ logging.info(f"Completed pagination for job {job_id}. Total logs: {len(all_logs)}")
674
+
675
+ return all_logs
@@ -0,0 +1,15 @@
1
+ from dateutil import parser
2
+
3
+
4
+ def iso_to_millis(ts: str) -> int:
5
+ """
6
+ Convert ISO 8601 timestamp string to milliseconds since epoch.
7
+
8
+ Args:
9
+ ts: ISO 8601 timestamp string (handles Zulu/UTC (Z) automatically)
10
+
11
+ Returns:
12
+ Milliseconds since epoch as integer
13
+ """
14
+ dt = parser.isoparse(ts) # handles Zulu/UTC (Z) automatically
15
+ return int(dt.timestamp() * 1000)
@@ -8,25 +8,6 @@ FROM {{ base_image_name_and_tag }} AS truss_server
8
8
  {%- set python_executable = config.base_image.python_executable_path or 'python3' %}
9
9
  ENV PYTHON_EXECUTABLE="{{ python_executable }}"
10
10
 
11
- # The non-root user's home directory.
12
- # uv will use $HOME to install packages.
13
- ENV HOME=/home/{{ app_username }}
14
- # Directory containing inference server code.
15
- ENV APP_HOME=/{{ app_username }}
16
- RUN mkdir -p ${APP_HOME} {{ control_server_dir }}
17
- # Create a non-root user to run model containers.
18
- RUN useradd -u {{ app_user_uid }} -ms /bin/bash {{ app_username }}
19
- # Add the user's local bin to the path, used by uv.
20
- ENV PATH=${PATH}:${HOME}/.local/bin
21
-
22
- {%- if non_root_user %}
23
- RUN apt update -y && apt install -y sudo
24
- RUN echo "{{ app_username }} ALL=(root) NOPASSWD: /usr/bin/apt install *, /usr/bin/apt update *" > /etc/sudoers.d/app-packages
25
- RUN chmod 0440 /etc/sudoers.d/app-packages
26
- {#- optional but good practice: check if the sudoers file is valid #}
27
- RUN visudo -c
28
- {%- endif %}
29
-
30
11
  {%- set UV_VERSION = "0.7.19" %}
31
12
  {#
32
13
  NB(nikhil): We use a semi-complex uv installation command across the board:
@@ -58,6 +39,7 @@ RUN if ! command -v uv >/dev/null 2>&1; then \
58
39
  command -v curl >/dev/null 2>&1 || (apt update && apt install -y curl) && \
59
40
  curl -LsSf --retry 5 --retry-delay 5 https://astral.sh/uv/{{ UV_VERSION }}/install.sh | sh; \
60
41
  fi
42
+ ENV PATH="/root/.local/bin:$PATH"
61
43
  {% endblock %}
62
44
 
63
45
  {% block base_image_patch %}
@@ -75,7 +57,7 @@ RUN {{ sys_pip_install_command }} install mkl
75
57
 
76
58
  {% block install_system_requirements %}
77
59
  {%- if should_install_system_requirements %}
78
- COPY --chown={{ default_owner }} ./{{ system_packages_filename }} {{ system_packages_filename }}
60
+ COPY ./{{ system_packages_filename }} {{ system_packages_filename }}
79
61
  RUN apt-get update && apt-get install --yes --no-install-recommends $(cat {{ system_packages_filename }}) \
80
62
  && apt-get autoremove -y \
81
63
  && apt-get clean -y \
@@ -86,11 +68,11 @@ RUN apt-get update && apt-get install --yes --no-install-recommends $(cat {{ sys
86
68
 
87
69
  {% block install_requirements %}
88
70
  {%- if should_install_user_requirements_file %}
89
- COPY --chown={{ default_owner }} ./{{ user_supplied_requirements_filename }} {{ user_supplied_requirements_filename }}
71
+ COPY ./{{ user_supplied_requirements_filename }} {{ user_supplied_requirements_filename }}
90
72
  RUN {{ sys_pip_install_command }} -r {{ user_supplied_requirements_filename }} --no-cache-dir
91
73
  {%- endif %}
92
74
  {%- if should_install_requirements %}
93
- COPY --chown={{ default_owner }} ./{{ config_requirements_filename }} {{ config_requirements_filename }}
75
+ COPY ./{{ config_requirements_filename }} {{ config_requirements_filename }}
94
76
  RUN {{ sys_pip_install_command }} -r {{ config_requirements_filename }} --no-cache-dir
95
77
  {%- endif %}
96
78
  {% endblock %}
@@ -98,6 +80,7 @@ RUN {{ sys_pip_install_command }} -r {{ config_requirements_filename }} --no-cac
98
80
 
99
81
 
100
82
  {%- if not config.docker_server %}
83
+ ENV APP_HOME="/app"
101
84
  WORKDIR $APP_HOME
102
85
  {%- endif %}
103
86
 
@@ -107,7 +90,7 @@ WORKDIR $APP_HOME
107
90
 
108
91
  {% block bundled_packages_copy %}
109
92
  {%- if bundled_packages_dir_exists %}
110
- COPY --chown={{ default_owner }} ./{{ config.bundled_packages_dir }} /packages
93
+ COPY ./{{ config.bundled_packages_dir }} /packages
111
94
  {%- endif %}
112
95
  {% endblock %}
113
96
 
@@ -1,7 +1,7 @@
1
1
  FROM python:3.11-slim AS cache_warmer
2
2
 
3
- RUN mkdir -p ${APP_HOME}/model_cache
4
- WORKDIR ${APP_HOME}
3
+ RUN mkdir -p /app/model_cache
4
+ WORKDIR /app
5
5
 
6
6
  {% if hf_access_token %}
7
7
  ENV HUGGING_FACE_HUB_TOKEN="{{hf_access_token}}"
@@ -9,12 +9,12 @@ ENV HUGGING_FACE_HUB_TOKEN="{{hf_access_token}}"
9
9
 
10
10
  RUN apt-get -y update; apt-get -y install curl; curl -s https://baseten-public.s3.us-west-2.amazonaws.com/bin/b10cp-5fe8dc7da-linux-amd64 -o /app/b10cp; chmod +x /app/b10cp
11
11
  ENV B10CP_PATH_TRUSS="/app/b10cp"
12
- COPY --chown={{ default_owner }} ./cache_requirements.txt ${APP_HOME}/cache_requirements.txt
12
+ COPY ./cache_requirements.txt /app/cache_requirements.txt
13
13
  RUN pip install -r /app/cache_requirements.txt --no-cache-dir && rm -rf /root/.cache/pip
14
- COPY --chown={{ default_owner }} ./cache_warmer.py /cache_warmer.py
14
+ COPY ./cache_warmer.py /cache_warmer.py
15
15
 
16
16
  {% for credential in credentials_to_cache %}
17
- COPY ./{{credential}} ${APP_HOME}/{{credential}}
17
+ COPY ./{{credential}} /app/{{credential}}
18
18
  {% endfor %}
19
19
 
20
20
  {% for repo, hf_dir in models.items() %}
@@ -1,3 +1,3 @@
1
1
  {% for file in cached_files %}
2
- COPY --chown={{ default_owner }} --from=cache_warmer {{file.source}} {{file.dst}}
2
+ COPY --from=cache_warmer {{file.source}} {{file.dst}}
3
3
  {% endfor %}
@@ -1,5 +1,4 @@
1
1
  [supervisord]
2
- pidfile=/tmp/supervisord.pid ; Set PID file location to /tmp to be writable by the non-root user
3
2
  nodaemon=true ; Run supervisord in the foreground (useful for containers)
4
3
  logfile=/dev/null ; Disable logging to file (send logs to /dev/null)
5
4
  logfile_maxbytes=0 ; No size limit on logfile (since logging is disabled)
@@ -18,7 +18,7 @@ psutil>=5.9.4
18
18
  python-json-logger>=2.0.2
19
19
  pyyaml>=6.0.0
20
20
  requests>=2.31.0
21
- truss-transfer==0.0.27
21
+ truss-transfer==0.0.28
22
22
  uvicorn>=0.24.0
23
23
  uvloop>=0.19.0
24
24
  websockets>=10.0
@@ -15,12 +15,12 @@ ENV DEBIAN_FRONTEND="noninteractive"
15
15
 
16
16
  {# Install common dependencies #}
17
17
  RUN apt update && \
18
- apt install -y bash build-essential git curl ca-certificates sudo \
18
+ apt install -y bash build-essential git curl ca-certificates \
19
19
  && apt-get autoremove -y \
20
20
  && apt-get clean -y \
21
21
  && rm -rf /var/lib/apt/lists/*
22
22
 
23
- COPY --chown={{ default_owner }} ./{{ base_server_requirements_filename }} {{ base_server_requirements_filename }}
23
+ COPY ./{{ base_server_requirements_filename }} {{ base_server_requirements_filename }}
24
24
  RUN {{ sys_pip_install_command }} -r {{ base_server_requirements_filename }} --no-cache-dir
25
25
  {%- endif %} {#- endif not config.docker_server #}
26
26
 
@@ -38,7 +38,7 @@ RUN ln -sf {{ config.base_image.python_executable_path }} /usr/local/bin/python
38
38
 
39
39
  {% block install_requirements %}
40
40
  {%- if should_install_server_requirements %}
41
- COPY --chown={{ default_owner }} ./{{ server_requirements_filename }} {{ server_requirements_filename }}
41
+ COPY ./{{ server_requirements_filename }} {{ server_requirements_filename }}
42
42
  RUN {{ sys_pip_install_command }} -r {{ server_requirements_filename }} --no-cache-dir
43
43
  {%- endif %} {#- endif should_install_server_requirements #}
44
44
  {{ super() }}
@@ -65,47 +65,47 @@ RUN {% for secret,path in config.build.secret_to_path_mapping.items() %} --mount
65
65
 
66
66
  {# Copy data before code for better caching #}
67
67
  {%- if data_dir_exists %}
68
- COPY --chown={{ default_owner }} ./{{ config.data_dir }} ${APP_HOME}/data
68
+ COPY ./{{ config.data_dir }} /app/data
69
69
  {%- endif %} {#- endif data_dir_exists #}
70
70
 
71
71
  {%- if model_cache_v2 %}
72
72
  # v0.0.9, keep synced with server_requirements.txt
73
- RUN curl -sSL --fail --retry 5 --retry-delay 2 -o /usr/local/bin/truss-transfer-cli https://github.com/basetenlabs/truss/releases/download/v0.10.9rc0/truss-transfer-cli-v0.10.9rc0-linux-x86_64-unknown-linux-musl
73
+ RUN curl -sSL --fail --retry 5 --retry-delay 2 -o /usr/local/bin/truss-transfer-cli https://github.com/basetenlabs/truss/releases/download/v0.10.9rc30/truss-transfer-cli-v0.10.9rc30-linux-x86_64-unknown-linux-musl
74
74
  RUN chmod +x /usr/local/bin/truss-transfer-cli
75
75
  RUN mkdir /static-bptr
76
76
  RUN echo "hash {{model_cache_hash}}"
77
- COPY --chown={{ default_owner }} ./bptr-manifest /static-bptr/static-bptr-manifest.json
77
+ COPY ./bptr-manifest /static-bptr/static-bptr-manifest.json
78
78
  {%- endif %} {#- endif model_cache_v2 #}
79
79
 
80
80
  {%- if not config.docker_server %}
81
- COPY --chown={{ default_owner }} ./server ${APP_HOME}
81
+ COPY ./server /app
82
82
  {%- endif %} {#- endif not config.docker_server #}
83
83
 
84
84
  {%- if use_local_src %}
85
85
  {# This path takes precedence over site-packages. #}
86
- COPY --chown={{ default_owner }} ./truss_chains ${APP_HOME}/truss_chains
87
- COPY --chown={{ default_owner }} ./truss ${APP_HOME}/truss
86
+ COPY ./truss_chains /app/truss_chains
87
+ COPY ./truss /app/truss
88
88
  {%- endif %} {#- endif use_local_src #}
89
89
 
90
- COPY --chown={{ default_owner }} ./config.yaml ${APP_HOME}/config.yaml
90
+ COPY ./config.yaml /app/config.yaml
91
91
  {%- if requires_live_reload %}
92
92
  RUN uv python install {{ control_python_version }}
93
93
  RUN uv venv /control/.env --python {{ control_python_version }}
94
94
 
95
- COPY --chown={{ default_owner }} ./control /control
95
+ COPY ./control /control
96
96
  RUN uv pip install -r /control/requirements.txt --python /control/.env/bin/python --no-cache-dir
97
97
  {%- endif %} {#- endif requires_live_reload #}
98
98
 
99
99
  {%- if model_dir_exists %}
100
- COPY --chown={{ default_owner }} ./{{ config.model_module_dir }} ${APP_HOME}/model
100
+ COPY ./{{ config.model_module_dir }} /app/model
101
101
  {%- endif %} {#- endif model_dir_exists #}
102
102
  {% endblock %} {#- endblock app_copy #}
103
103
 
104
104
  {% block run %}
105
105
  {%- if config.docker_server %}
106
- RUN apt-get update -y && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
106
+ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
107
107
  curl nginx && rm -rf /var/lib/apt/lists/*
108
- COPY --chown={{ default_owner }} ./docker_server_requirements.txt ${APP_HOME}/docker_server_requirements.txt
108
+ COPY ./docker_server_requirements.txt /app/docker_server_requirements.txt
109
109
 
110
110
  {# NB(nikhil): Use the same python version for custom server proxy as the control server, for consistency. #}
111
111
  RUN uv python install {{ control_python_version }}
@@ -115,38 +115,21 @@ RUN uv pip install --python /docker_server/.venv/bin/python -r /app/docker_serve
115
115
  {% set supervisor_config_path = "/etc/supervisor/supervisord.conf" %}
116
116
  {% set supervisor_log_dir = "/var/log/supervisor" %}
117
117
  {% set supervisor_server_url = "http://localhost:8080" %}
118
- COPY --chown={{ default_owner }} ./proxy.conf {{ proxy_config_path }}
118
+ COPY ./proxy.conf {{ proxy_config_path }}
119
119
  RUN mkdir -p {{ supervisor_log_dir }}
120
- COPY --chown={{ default_owner }} ./supervisord.conf {{ supervisor_config_path }}
120
+ COPY supervisord.conf {{ supervisor_config_path }}
121
121
  ENV SUPERVISOR_SERVER_URL="{{ supervisor_server_url }}"
122
122
  ENV SERVER_START_CMD="/docker_server/.venv/bin/supervisord -c {{ supervisor_config_path }}"
123
- {#- default configuration uses port 80, which requires root privileges, so we remove it #}
124
- RUN rm -f /etc/nginx/sites-enabled/default
125
- {%- if non_root_user %}
126
- {#- nginx writes to /var/lib/nginx, /var/log/nginx, and /run directories #}
127
- {% set nginx_dirs = ["/var/lib/nginx", "/var/log/nginx", "/run"] %}
128
- RUN chown -R {{ app_username }}:{{ app_username }} {{ nginx_dirs | join(" ") }}
129
- RUN chown -R {{ app_username }}:{{ app_username }} ${HOME} ${APP_HOME}
130
- USER {{ app_username }}
131
- {%- endif %} {#- endif non_root_user #}
132
123
  ENTRYPOINT ["/docker_server/.venv/bin/supervisord", "-c", "{{ supervisor_config_path }}"]
133
124
  {%- elif requires_live_reload %} {#- elif requires_live_reload #}
134
125
  ENV HASH_TRUSS="{{ truss_hash }}"
135
126
  ENV CONTROL_SERVER_PORT="8080"
136
127
  ENV INFERENCE_SERVER_PORT="8090"
137
128
  ENV SERVER_START_CMD="/control/.env/bin/python /control/control/server.py"
138
- {%- if non_root_user %}
139
- RUN chown -R {{ app_username }}:{{ app_username }} ${HOME} ${APP_HOME}
140
- USER {{ app_username }}
141
- {%- endif %} {#- endif non_root_user #}
142
129
  ENTRYPOINT ["/control/.env/bin/python", "/control/control/server.py"]
143
130
  {%- else %} {#- else (default inference server) #}
144
131
  ENV INFERENCE_SERVER_PORT="8080"
145
132
  ENV SERVER_START_CMD="{{ python_executable }} /app/main.py"
146
- {%- if non_root_user %}
147
- RUN chown -R {{ app_username }}:{{ app_username }} ${HOME} ${APP_HOME}
148
- USER {{ app_username }}
149
- {%- endif %} {#- endif non_root_user #}
150
133
  ENTRYPOINT ["{{ python_executable }}", "/app/main.py"]
151
134
  {%- endif %} {#- endif config.docker_server / live_reload #}
152
135
  {% endblock %} {#- endblock run #}