Runtime_env/app/utils/utils.py (82 lines of code) (raw):

# Copyright 2025 Google LLC. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # pylint: disable=C0301, W0718 """Module that contains various utility functions across the runtime env""" import os import toml from vertexai import agent_engines import yaml def get_requirements_from_toml(pyproject_file="pyproject.toml"): """ Reads a pyproject.toml file and extracts dependencies to generate a requirements.txt-compatible list. Handles extras correctly. Args: pyproject_file (str): Path to the pyproject.toml file. This is from the route dir. Returns: list: A list of strings, where each string is a dependency in the format expected by requirements.txt (e.g., "fastapi==0.110.3"). Returns an empty list if the dependencies section is not found or if there's an error reading the file. """ try: with open(pyproject_file, "r", encoding="utf-8") as f: data = toml.load(f) dependencies = data.get("tool", {}).get("poetry", {}).get("dependencies", {}) if not dependencies: print("No dependencies section found in pyproject.toml.") return [] requirements = [] for package, version_info in dependencies.items(): if package == "python": # Skip python version specifier continue if isinstance(version_info, str): if "^" in version_info: requirements.append(f"{package}>={version_info.replace('^', '')}") else: requirements.append(f"{package}=={version_info}") elif isinstance(version_info, dict): version = version_info.get("version") extras = version_info.get("extras") if extras: extras_str = ",".join(extras) if "^" in version: requirements.append(f"{package}[{extras_str}]>={version.replace('^', '')}") else: requirements.append(f"{package}[{extras_str}]=={version}") else: print(f"Warning: Unexpected dependency format for {package}: {version_info}") return requirements except FileNotFoundError: print(f"Error: File not found: {pyproject_file}") return [] except toml.TomlDecodeError as e: print(f"Error decoding TOML: {e}") return [] except Exception as e: print(f"An unexpected error occurred: {e}") return [] def read_yaml_file(filepath: str) -> dict: """Reads a yaml and returns file contents as a dict. Defaults to utf-8 encoding. Args: filepath (str): Path to the yaml. Returns: dict: Contents of the yaml. Raises: Exception: If an error is encountered reading the file. """ try: with open(filepath, "r", encoding="utf-8") as file: file_dict = yaml.safe_load(file) file.close() except yaml.YAMLError as err: raise yaml.YAMLError(f"Error reading file. {err}") from err return file_dict def load_env_from_yaml(filepath: str): """ Loads environment variables from a YAML file and sets them in the os.environ. Args: filepath (str): The path to the YAML file containing the environment variables. Raises: FileNotFoundError: If the specified YAML file does not exist. yaml.YAMLError: If there is an error parsing the YAML file. """ try: env_vars = read_yaml_file(filepath) if env_vars is None: # Handle empty YAML files print(f"Warning: YAML file {filepath} is empty.") return if not isinstance(env_vars, dict): raise TypeError(f"YAML file {filepath} must contain a dictionary at the top level.") for key, value in env_vars.items(): if not isinstance(key, str): raise TypeError(f"Key '{key}' in YAML file must be a string.") if not isinstance(value, (str, int, float, bool, type(None))): raise TypeError(f"Value for key '{key}' in YAML file must be a string, number, boolean, or None. Found type: {type(value)}") # Convert value to string for setting in os.environ os.environ[key] = str(value) except FileNotFoundError as e: raise FileNotFoundError(f"YAML file not found: {filepath}") from e def deploy_agent_to_agent_engine( agent, user_agent: str, description: str ): """ Deploys the Vertex AI agent engine to a remote managed endpoint. Args: agent: The agent to be deployed to agent engine. Will be an .agent_executor in the case of langchain/langgraph user_agent: The name of the agent description: The description of the agent Returns: Remote Agent Engine agent. Exception: An error is encountered during deployment. """ try: remote_agent = agent_engines.create( agent, requirements=get_requirements_from_toml(), display_name=user_agent, description=description, extra_packages=["./app", "./deployment/config"], ) except Exception as e: raise RuntimeError(f"Error deploying Agent Engine Agent. {e}") from e return remote_agent