Spaces:
Runtime error
Runtime error
from typing import Optional, Dict, Any, Union, List | |
from pydantic import field_validator, Field | |
from pydantic_core.core_schema import FieldValidationInfo | |
from phi.base import PhiBase | |
from phi.app.context import ContainerContext | |
from phi.resource.base import ResourceBase | |
from phi.utils.log import logger | |
class AppBase(PhiBase): | |
# -*- App Name (required) | |
name: str | |
# -*- Image Configuration | |
# Image can be provided as a DockerImage object | |
image: Optional[Any] = None | |
# OR as image_name:image_tag str | |
image_str: Optional[str] = None | |
# OR as image_name and image_tag | |
image_name: Optional[str] = None | |
image_tag: Optional[str] = None | |
# Entrypoint for the container | |
entrypoint: Optional[Union[str, List[str]]] = None | |
# Command for the container | |
command: Optional[Union[str, List[str]]] = None | |
# -*- Python Configuration | |
# Install python dependencies using a requirements.txt file | |
install_requirements: bool = False | |
# Path to the requirements.txt file relative to the workspace_root | |
requirements_file: str = "requirements.txt" | |
# Set the PYTHONPATH env var | |
set_python_path: bool = True | |
# Manually provide the PYTHONPATH. | |
# If None, PYTHONPATH is set to workspace_root | |
python_path: Optional[str] = None | |
# Add paths to the PYTHONPATH env var | |
# If python_path is provided, this value is ignored | |
add_python_paths: Optional[List[str]] = None | |
# -*- App Ports | |
# Open a container port if open_port=True | |
open_port: bool = False | |
# If open_port=True, port_number is used to set the | |
# container_port if container_port is None and host_port if host_port is None | |
port_number: int = 80 | |
# Port number on the Container to open | |
# Preferred over port_number if both are set | |
container_port: Optional[int] = Field(None, validate_default=True) | |
# Port name for the opened port | |
container_port_name: str = "http" | |
# Port number on the Host to map to the Container port | |
# Preferred over port_number if both are set | |
host_port: Optional[int] = Field(None, validate_default=True) | |
# -*- Extra Resources created "before" the App resources | |
resources: Optional[List[ResourceBase]] = None | |
# -*- Other args | |
print_env_on_load: bool = False | |
# -*- App specific args. Not to be set by the user. | |
# Container Environment that can be set by subclasses | |
# which is used as a starting point for building the container_env | |
# Any variables set in container_env will be overriden by values | |
# in the env_vars dict or env_file | |
container_env: Optional[Dict[str, Any]] = None | |
# Variable used to cache the container context | |
container_context: Optional[ContainerContext] = None | |
# -*- Cached Data | |
cached_resources: Optional[List[Any]] = None | |
def set_container_port(cls, v, info: FieldValidationInfo): | |
port_number = info.data.get("port_number") | |
if v is None and port_number is not None: | |
v = port_number | |
return v | |
def set_host_port(cls, v, info: FieldValidationInfo): | |
port_number = info.data.get("port_number") | |
if v is None and port_number is not None: | |
v = port_number | |
return v | |
def get_app_name(self) -> str: | |
return self.name | |
def get_image_str(self) -> str: | |
if self.image: | |
return f"{self.image.name}:{self.image.tag}" | |
elif self.image_str: | |
return self.image_str | |
elif self.image_name and self.image_tag: | |
return f"{self.image_name}:{self.image_tag}" | |
elif self.image_name: | |
return f"{self.image_name}:latest" | |
else: | |
return "" | |
def build_resources(self, build_context: Any) -> Optional[Any]: | |
logger.debug(f"@build_resource_group not defined for {self.get_app_name()}") | |
return None | |
def get_dependencies(self) -> Optional[List[ResourceBase]]: | |
return ( | |
[dep for dep in self.depends_on if isinstance(dep, ResourceBase)] if self.depends_on is not None else None | |
) | |
def add_app_properties_to_resources(self, resources: List[ResourceBase]) -> List[ResourceBase]: | |
updated_resources = [] | |
app_properties = self.model_dump(exclude_defaults=True) | |
app_group = self.get_group_name() | |
app_output_dir = self.get_app_name() | |
app_skip_create = app_properties.get("skip_create", None) | |
app_skip_read = app_properties.get("skip_read", None) | |
app_skip_update = app_properties.get("skip_update", None) | |
app_skip_delete = app_properties.get("skip_delete", None) | |
app_recreate_on_update = app_properties.get("recreate_on_update", None) | |
app_use_cache = app_properties.get("use_cache", None) | |
app_force = app_properties.get("force", None) | |
app_debug_mode = app_properties.get("debug_mode", None) | |
app_wait_for_create = app_properties.get("wait_for_create", None) | |
app_wait_for_update = app_properties.get("wait_for_update", None) | |
app_wait_for_delete = app_properties.get("wait_for_delete", None) | |
app_save_output = app_properties.get("save_output", None) | |
for resource in resources: | |
resource_properties = resource.model_dump(exclude_defaults=True) | |
resource_skip_create = resource_properties.get("skip_create", None) | |
resource_skip_read = resource_properties.get("skip_read", None) | |
resource_skip_update = resource_properties.get("skip_update", None) | |
resource_skip_delete = resource_properties.get("skip_delete", None) | |
resource_recreate_on_update = resource_properties.get("recreate_on_update", None) | |
resource_use_cache = resource_properties.get("use_cache", None) | |
resource_force = resource_properties.get("force", None) | |
resource_debug_mode = resource_properties.get("debug_mode", None) | |
resource_wait_for_create = resource_properties.get("wait_for_create", None) | |
resource_wait_for_update = resource_properties.get("wait_for_update", None) | |
resource_wait_for_delete = resource_properties.get("wait_for_delete", None) | |
resource_save_output = resource_properties.get("save_output", None) | |
# If skip_create on resource is not set, use app level skip_create (if set on app) | |
if resource_skip_create is None and app_skip_create is not None: | |
resource.skip_create = app_skip_create | |
# If skip_read on resource is not set, use app level skip_read (if set on app) | |
if resource_skip_read is None and app_skip_read is not None: | |
resource.skip_read = app_skip_read | |
# If skip_update on resource is not set, use app level skip_update (if set on app) | |
if resource_skip_update is None and app_skip_update is not None: | |
resource.skip_update = app_skip_update | |
# If skip_delete on resource is not set, use app level skip_delete (if set on app) | |
if resource_skip_delete is None and app_skip_delete is not None: | |
resource.skip_delete = app_skip_delete | |
# If recreate_on_update on resource is not set, use app level recreate_on_update (if set on app) | |
if resource_recreate_on_update is None and app_recreate_on_update is not None: | |
resource.recreate_on_update = app_recreate_on_update | |
# If use_cache on resource is not set, use app level use_cache (if set on app) | |
if resource_use_cache is None and app_use_cache is not None: | |
resource.use_cache = app_use_cache | |
# If force on resource is not set, use app level force (if set on app) | |
if resource_force is None and app_force is not None: | |
resource.force = app_force | |
# If debug_mode on resource is not set, use app level debug_mode (if set on app) | |
if resource_debug_mode is None and app_debug_mode is not None: | |
resource.debug_mode = app_debug_mode | |
# If wait_for_create on resource is not set, use app level wait_for_create (if set on app) | |
if resource_wait_for_create is None and app_wait_for_create is not None: | |
resource.wait_for_create = app_wait_for_create | |
# If wait_for_update on resource is not set, use app level wait_for_update (if set on app) | |
if resource_wait_for_update is None and app_wait_for_update is not None: | |
resource.wait_for_update = app_wait_for_update | |
# If wait_for_delete on resource is not set, use app level wait_for_delete (if set on app) | |
if resource_wait_for_delete is None and app_wait_for_delete is not None: | |
resource.wait_for_delete = app_wait_for_delete | |
# If save_output on resource is not set, use app level save_output (if set on app) | |
if resource_save_output is None and app_save_output is not None: | |
resource.save_output = app_save_output | |
# If workspace_settings on resource is not set, use app level workspace_settings (if set on app) | |
if resource.workspace_settings is None and self.workspace_settings is not None: | |
resource.set_workspace_settings(self.workspace_settings) | |
# If group on resource is not set, use app level group (if set on app) | |
if resource.group is None and app_group is not None: | |
resource.group = app_group | |
# Always set output_dir on resource to app level output_dir | |
resource.output_dir = app_output_dir | |
app_dependencies = self.get_dependencies() | |
if app_dependencies is not None: | |
if resource.depends_on is None: | |
resource.depends_on = app_dependencies | |
else: | |
resource.depends_on.extend(app_dependencies) | |
updated_resources.append(resource) | |
return updated_resources | |
def get_resources(self, build_context: Any) -> List[ResourceBase]: | |
if self.cached_resources is not None and len(self.cached_resources) > 0: | |
return self.cached_resources | |
base_resources = self.resources or [] | |
app_resources = self.build_resources(build_context) | |
if app_resources is not None: | |
base_resources.extend(app_resources) | |
self.cached_resources = self.add_app_properties_to_resources(base_resources) | |
# logger.debug(f"Resources: {self.cached_resources}") | |
return self.cached_resources | |
def matches_filters(self, group_filter: Optional[str] = None) -> bool: | |
if group_filter is not None: | |
group_name = self.get_group_name() | |
logger.debug(f"{self.get_app_name()}: Checking {group_filter} in {group_name}") | |
if group_name is None or group_filter not in group_name: | |
return False | |
return True | |
def should_create(self, group_filter: Optional[str] = None) -> bool: | |
if not self.enabled or self.skip_create: | |
return False | |
return self.matches_filters(group_filter) | |
def should_delete(self, group_filter: Optional[str] = None) -> bool: | |
if not self.enabled or self.skip_delete: | |
return False | |
return self.matches_filters(group_filter) | |
def should_update(self, group_filter: Optional[str] = None) -> bool: | |
if not self.enabled or self.skip_update: | |
return False | |
return self.matches_filters(group_filter) | |