diff --git a/custom_model_runner/CHANGELOG.md b/custom_model_runner/CHANGELOG.md index 4132fbd98..d07a9dffd 100644 --- a/custom_model_runner/CHANGELOG.md +++ b/custom_model_runner/CHANGELOG.md @@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +#### [1.16.23] - 2025-08-18 +##### Changed +- Add OTEL metrics and logs configuration. + +#### [1.16.22] - 2025-08-12 +##### Changed +- Add support for kwargs and headers to generative ai chat models +- Fix support for drum inline execution + #### [1.16.21] - 2025-07-16 ##### Removed - Removed PMML support diff --git a/custom_model_runner/datarobot_drum/drum/common.py b/custom_model_runner/datarobot_drum/drum/common.py index 24a2a040a..a549217f2 100644 --- a/custom_model_runner/datarobot_drum/drum/common.py +++ b/custom_model_runner/datarobot_drum/drum/common.py @@ -22,17 +22,24 @@ PayloadFormat, ) from datarobot_drum.drum.exceptions import DrumCommonException -from datarobot_drum.drum.lazy_loading.lazy_loading_handler import LazyLoadingHandler -from datarobot_drum.runtime_parameters.runtime_parameters import RuntimeParametersLoader -from opentelemetry import trace, context +from opentelemetry import trace, context, metrics +from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter +from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter + +from opentelemetry._logs import set_logger_provider +from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler +from opentelemetry.sdk._logs.export import BatchLogRecordProcessor +from opentelemetry.sdk._logs.export import SimpleLogRecordProcessor + +from opentelemetry.sdk.metrics import MeterProvider +from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader from opentelemetry.sdk.resources import Resource from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor from opentelemetry.sdk.trace.export import SimpleSpanProcessor from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator - ctx_request_id = ContextVar("request_id") @@ -139,7 +146,39 @@ def make_otel_endpoint(datarobot_endpoint): return result -def setup_tracer(runtime_parameters, options): +def _setup_otel_logging(resource, multiprocessing=False): + logger_provider = LoggerProvider(resource=resource) + set_logger_provider(logger_provider) + exporter = OTLPLogExporter() + if multiprocessing: + logger_provider.add_log_record_processor(SimpleLogRecordProcessor(exporter)) + else: + logger_provider.add_log_record_processor(BatchLogRecordProcessor(exporter)) + handler = LoggingHandler(level=logging.DEBUG, logger_provider=logger_provider) + logging.getLogger().addHandler(handler) + return logger_provider + + +def _setup_otel_metrics(resource): + metric_exporter = OTLPMetricExporter() + metric_reader = PeriodicExportingMetricReader(metric_exporter) + metric_provider = MeterProvider(metric_readers=[metric_reader], resource=resource) + metrics.set_meter_provider(metric_provider) + return metric_provider + + +def _setup_otel_tracing(resource, multiprocessing=False): + otlp_exporter = OTLPSpanExporter() + trace_provider = TracerProvider(resource=resource) + if multiprocessing: + trace_provider.add_span_processor(SimpleSpanProcessor(otlp_exporter)) + else: + trace_provider.add_span_processor(BatchSpanProcessor(otlp_exporter)) + trace.set_tracer_provider(trace_provider) + return trace_provider + + +def setup_otel(runtime_parameters, options): """Setups OTEL tracer. OTEL is configured by OTEL_EXPORTER_OTLP_ENDPOINT and @@ -153,25 +192,20 @@ def setup_tracer(runtime_parameters, options): command argumetns Returns ------- - TracerProvider + (TracerProvider, MetricProvider) """ - log = get_drum_logger("setup_tracer") + log = get_drum_logger("setup_otel") # Can be used to disable OTEL reporting from env var parameters # https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/ if runtime_parameters.has("OTEL_SDK_DISABLED") and os.environ.get("OTEL_SDK_DISABLED"): - log.info("Tracing explictly disabled") - return - - main_endpoint = os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT") - trace_endpoint = os.environ.get("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT") - if not main_endpoint and not trace_endpoint: - log.info("Tracing is not configured") - return + log.info("OTEL explictly disabled") + return (None, None, None) - resource = Resource.create() - otlp_exporter = OTLPSpanExporter() - provider = TracerProvider(resource=resource) + endpoint = os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT") + if not endpoint: + log.info("OTEL is not configured") + return (None, None, None) # In case of NIM flask server is configured to run in multiprocessing # mode that uses fork. Since BatchSpanProcessor start background thread @@ -180,16 +214,15 @@ def setup_tracer(runtime_parameters, options): # missing due to process exits before all data offloaded. In forking # case we use SimpleSpanProcessor (mostly NIMs) otherwise BatchSpanProcessor # (most frequent case) - if options.max_workers > 1: - provider.add_span_processor(SimpleSpanProcessor(otlp_exporter)) - else: - provider.add_span_processor(BatchSpanProcessor(otlp_exporter)) + multiprocessing = options.max_workers > 1 - trace.set_tracer_provider(provider) + resource = Resource.create() + trace_provider = _setup_otel_tracing(resource=resource, multiprocessing=multiprocessing) + logger_provider = _setup_otel_logging(resource=resource, multiprocessing=multiprocessing) + metric_provider = _setup_otel_metrics(resource=resource) - endpoint = main_endpoint or trace_endpoint - log.info(f"Tracing is configured with endpoint: {endpoint}") - return provider + log.info(f"OTEL is configured with endpoint: {endpoint}") + return trace_provider, metric_provider, logger_provider @contextmanager @@ -232,12 +265,3 @@ def extract_chat_response_attributes(response): # last completion wins attributes["gen_ai.completion"] = m.get("content") return attributes - - -def setup_required_environment_variables(options): - if "runtime_params_file" in options and options.runtime_params_file: - loader = RuntimeParametersLoader(options.runtime_params_file, options.code_dir) - loader.setup_environment_variables() - - if "lazy_loading_file" in options and options.lazy_loading_file: - LazyLoadingHandler.setup_environment_variables_from_values_file(options.lazy_loading_file) diff --git a/custom_model_runner/datarobot_drum/drum/description.py b/custom_model_runner/datarobot_drum/drum/description.py index 606c506a1..f3dfee031 100644 --- a/custom_model_runner/datarobot_drum/drum/description.py +++ b/custom_model_runner/datarobot_drum/drum/description.py @@ -4,6 +4,6 @@ This is proprietary source code of DataRobot, Inc. and its affiliates. Released under the terms of DataRobot Tool and Utility Agreement. """ -version = "1.16.20" +version = "1.16.23" __version__ = version project_name = "datarobot-drum" diff --git a/custom_model_runner/datarobot_drum/drum/main.py b/custom_model_runner/datarobot_drum/drum/main.py index d6384ec2f..94aa0e5b5 100644 --- a/custom_model_runner/datarobot_drum/drum/main.py +++ b/custom_model_runner/datarobot_drum/drum/main.py @@ -43,18 +43,13 @@ import signal import sys -from datarobot_drum.drum.args_parser import CMRunnerArgsRegistry -from datarobot_drum.drum.common import ( - config_logging, - setup_tracer, - setup_required_environment_variables, -) +from datarobot_drum.drum.common import config_logging, setup_otel +from datarobot_drum.drum.utils.setup import setup_options from datarobot_drum.drum.enum import RunMode from datarobot_drum.drum.enum import ExitCodes from datarobot_drum.drum.exceptions import DrumSchemaValidationException from datarobot_drum.drum.runtime import DrumRuntime from datarobot_drum.runtime_parameters.runtime_parameters import ( - RuntimeParametersLoader, RuntimeParameters, ) @@ -75,39 +70,24 @@ def signal_handler(sig, frame): # Let traceer offload accumulated spans before shutdown. if runtime.trace_provider is not None: runtime.trace_provider.shutdown() + if runtime.metric_provider is not None: + runtime.metric_provider.shutdown() + if runtime.log_provider is not None: + runtime.log_provider.shutdown() os._exit(130) - arg_parser = CMRunnerArgsRegistry.get_arg_parser() - try: - import argcomplete - except ImportError: - print( - "WARNING: autocompletion of arguments is not supported " - "as 'argcomplete' package is not found", - file=sys.stderr, - ) - else: - # argcomplete call should be as close to the beginning as possible - argcomplete.autocomplete(arg_parser) - - CMRunnerArgsRegistry.extend_sys_argv_with_env_vars() - - options = arg_parser.parse_args() - CMRunnerArgsRegistry.verify_options(options) - - try: - setup_required_environment_variables(options) + options = setup_options() + runtime.options = options except Exception as exc: print(str(exc)) exit(255) - if RuntimeParameters.has("CUSTOM_MODEL_WORKERS"): - options.max_workers = RuntimeParameters.get("CUSTOM_MODEL_WORKERS") - runtime.options = options - - runtime.trace_provider = setup_tracer(RuntimeParameters, options) + trace_provider, metric_provider, log_provider = setup_otel(RuntimeParameters, options) + runtime.trace_provider = trace_provider + runtime.metric_provider = metric_provider + runtime.log_provider = log_provider signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) diff --git a/custom_model_runner/datarobot_drum/drum/root_predictors/drum_inline_utils.py b/custom_model_runner/datarobot_drum/drum/root_predictors/drum_inline_utils.py index 2d94fce9c..19e9ed005 100644 --- a/custom_model_runner/datarobot_drum/drum/root_predictors/drum_inline_utils.py +++ b/custom_model_runner/datarobot_drum/drum/root_predictors/drum_inline_utils.py @@ -26,7 +26,8 @@ from typing import Generator, List from datarobot_drum.drum.args_parser import CMRunnerArgsRegistry -from datarobot_drum.drum.common import setup_required_environment_variables, setup_tracer +from datarobot_drum.drum.common import setup_otel +from datarobot_drum.drum.utils.setup import setup_options from datarobot_drum.drum.drum import CMRunner from datarobot_drum.drum.language_predictors.base_language_predictor import BaseLanguagePredictor from datarobot_drum.drum.runtime import DrumRuntime @@ -68,14 +69,23 @@ def drum_inline_predictor( target_type, *cmd_args, ] - options = arg_parser.parse_args(args) - CMRunnerArgsRegistry.verify_options(options) - setup_required_environment_variables(options) - runtime.options = options - setup_tracer(RuntimeParameters, options) + try: + options = setup_options(args) + runtime.options = options + except Exception as exc: + print(str(exc)) + exit(255) + + trace_provider, metric_provider, log_provider = setup_otel(RuntimeParameters, options) runtime.cm_runner = CMRunner(runtime) params = runtime.cm_runner.get_predictor_params() predictor = GenericPredictorComponent(params) yield predictor.predictor + if trace_provider is not None: + trace_provider.shutdown() + if metric_provider is not None: + metric_provider.shutdown() + if log_provider is not None: + log_provider.shutdown() diff --git a/custom_model_runner/datarobot_drum/drum/root_predictors/prediction_server.py b/custom_model_runner/datarobot_drum/drum/root_predictors/prediction_server.py index d2a6b3d5e..a6333fcb7 100644 --- a/custom_model_runner/datarobot_drum/drum/root_predictors/prediction_server.py +++ b/custom_model_runner/datarobot_drum/drum/root_predictors/prediction_server.py @@ -5,8 +5,11 @@ Released under the terms of DataRobot Tool and Utility Agreement. """ import logging +import os import sys +import time from pathlib import Path +from threading import Thread import requests from flask import Response, jsonify, request @@ -22,6 +25,7 @@ ModelInfoKeys, RunLanguage, TargetType, + URL_PREFIX_ENV_VAR_NAME, ) from datarobot_drum.drum.exceptions import DrumCommonException from datarobot_drum.drum.model_metadata import read_model_metadata_yaml @@ -71,6 +75,7 @@ def __init__(self, params: dict): "run_predictor_total", "finish", StatsOperation.SUB, "start" ) self._predictor = self._setup_predictor() + self._server_watchdog = None def _setup_predictor(self): if self._run_language == RunLanguage.PYTHON: @@ -301,10 +306,95 @@ def _run_flask_app(self, app): processes = self._params.get("processes") logger.info("Number of webserver processes: %s", processes) try: + if str(os.environ.get("USE_NIM_WATCHDOG", "false")).lower() in ["true", "1", "yes"]: + # Start the watchdog thread before running the app + self._server_watchdog = Thread( + target=self.watchdog, + args=(port,), # Pass host and port as arguments + daemon=True, + name="OpenAI Watchdog", + ) + self._server_watchdog.start() + app.run(host, port, threaded=False, processes=processes) except OSError as e: raise DrumCommonException("{}: host: {}; port: {}".format(e, host, port)) + def watchdog(self, port): + """ + Watchdog thread that periodically checks if the server is alive by making + GET requests to the /ping/ endpoint. Makes 3 attempts with quadratic backoff + before terminating the Flask app. + """ + + logger.info("Starting watchdog to monitor server health...") + + import os + + url_host = os.environ.get("TEST_URL_HOST", "localhost") + url_prefix = os.environ.get(URL_PREFIX_ENV_VAR_NAME, "") + health_url = f"http://{url_host}:{port}/{url_prefix}/info/" + + request_timeout = 120 + check_interval = 10 # seconds + max_attempts = 5 + + attempt = 0 + base_sleep_time = 2 + + while True: + try: + # Check if server is responding to health checks + logger.debug(f"Server health check") + response = requests.get(health_url, timeout=request_timeout) + logger.debug(f"Server health check status: {response.status_code}") + # Connection succeeded, reset attempts and wait for next check + attempt = 0 + time.sleep(check_interval) # Regular check interval + continue + + except Exception as e: + attempt += 1 + logger.error(f"health_url {health_url}") + logger.error( + f"Server health check failed (attempt {attempt}/{max_attempts}): {str(e)}" + ) + + if attempt >= max_attempts: + logger.error( + "All health check attempts failed. Forcefully killing all processes." + ) + + # First try clean termination + try: + self._terminate() + except Exception as e: + logger.error(f"Error during clean termination: {str(e)}") + + # Force kill all processes + import subprocess + + # Use more direct system commands to kill processes + try: + # Kill packedge jobs first (more aggressive approach) + logger.info("Killing Python package jobs") + # Run `busybox ps` and capture output + result = subprocess.run(["busybox", "ps"], capture_output=True, text=True) + # Parse lines, skip the header + lines = result.stdout.strip().split("\n")[1:] + # Extract the PID (first column) + pids = [int(line.split()[0]) for line in lines] + for pid in pids: + print("Killing pid:", pid) + subprocess.run(f"kill {pid}", shell=True) + except Exception as kill_error: + logger.error(f"Error during process killing: {str(kill_error)}") + + # Quadratic backoff + sleep_time = base_sleep_time * (attempt**2) + logger.info(f"Retrying in {sleep_time} seconds...") + time.sleep(sleep_time) + def terminate(self): terminate_op = getattr(self._predictor, "terminate", None) if callable(terminate_op): diff --git a/custom_model_runner/datarobot_drum/drum/runtime.py b/custom_model_runner/datarobot_drum/drum/runtime.py index ade7545ac..ea3f197b1 100644 --- a/custom_model_runner/datarobot_drum/drum/runtime.py +++ b/custom_model_runner/datarobot_drum/drum/runtime.py @@ -5,7 +5,6 @@ Released under the terms of DataRobot Tool and Utility Agreement. """ import logging -import traceback from datarobot_drum.drum.server import ( empty_api_blueprint, @@ -16,9 +15,7 @@ from datarobot_drum.drum.enum import LOGGER_NAME_PREFIX, RunMode from datarobot_drum.drum.exceptions import DrumCommonException -from datarobot_drum.drum.args_parser import ArgumentsOptions -from termcolor import colored logger = get_drum_logger(__name__) logger.setLevel(logging.ERROR) @@ -30,7 +27,10 @@ def __init__(self): self.initialization_succeeded = False self.options = None self.cm_runner = None + # OTEL services self.trace_provider = None + self.metric_provider = None + self.log_provider = None def __enter__(self): return self diff --git a/custom_model_runner/datarobot_drum/drum/utils/setup.py b/custom_model_runner/datarobot_drum/drum/utils/setup.py new file mode 100644 index 000000000..6bd80ba88 --- /dev/null +++ b/custom_model_runner/datarobot_drum/drum/utils/setup.py @@ -0,0 +1,68 @@ +""" +Copyright 2025 DataRobot, Inc. and its affiliates. +All rights reserved. +This is proprietary source code of DataRobot, Inc. and its affiliates. +Released under the terms of DataRobot Tool and Utility Agreement. +""" + +import sys + +from datarobot_drum import RuntimeParameters +from datarobot_drum.drum.args_parser import CMRunnerArgsRegistry +from datarobot_drum.drum.lazy_loading.lazy_loading_handler import LazyLoadingHandler +from datarobot_drum.runtime_parameters.runtime_parameters import RuntimeParametersLoader + + +def setup_options(args=None): + """ + Setup options for the Drum runtime. + This function is used to set up the command line arguments and options + for the Drum runtime, including environment variables and maximum workers. + + Parameters + ---------- + args : list, optional + List of command line arguments to parse. If None, uses sys.argv[1:]. + Defaults to None, which means it will use sys.argv[1:]. + + Returns + ------- + options : argparse.Namespace + Parsed command line options as an argparse.Namespace object. + """ + arg_parser = CMRunnerArgsRegistry.get_arg_parser() + + try: + import argcomplete + except ImportError: + print( + "WARNING: autocompletion of arguments is not supported " + "as 'argcomplete' package is not found", + file=sys.stderr, + ) + else: + # argcomplete call should be as close to the beginning as possible + argcomplete.autocomplete(arg_parser) + + CMRunnerArgsRegistry.extend_sys_argv_with_env_vars() + + options = arg_parser.parse_args(args) + + """Set max workers from runtime parameters if available.""" + if RuntimeParameters.has("CUSTOM_MODEL_WORKERS"): + options.max_workers = RuntimeParameters.get("CUSTOM_MODEL_WORKERS") + elif "max_workers" not in options or options.max_workers is None: + options.max_workers = 1 # Default to 1 worker if not specified + else: + options.max_workers = int(options.max_workers) + + CMRunnerArgsRegistry.verify_options(options) + + if "runtime_params_file" in options and options.runtime_params_file: + loader = RuntimeParametersLoader(options.runtime_params_file, options.code_dir) + loader.setup_environment_variables() + + if "lazy_loading_file" in options and options.lazy_loading_file: + LazyLoadingHandler.setup_environment_variables_from_values_file(options.lazy_loading_file) + + return options diff --git a/public_dropin_environments/java_codegen/env_info.json b/public_dropin_environments/java_codegen/env_info.json index add23d929..cbac232db 100644 --- a/public_dropin_environments/java_codegen/env_info.json +++ b/public_dropin_environments/java_codegen/env_info.json @@ -4,7 +4,7 @@ "description": "This template can be used as an environment for DataRobot generated scoring code or models that implement the either the IClassificationPredictor or IRegressionPredictor interface from the datarobot-prediction package and for H2O models exported as POJO or MOJO.", "programmingLanguage": "java", "label": "", - "environmentVersionId": "688c1c850012682da8000374", + "environmentVersionId": "689b9c1b0056193d330033b3", "environmentVersionDescription": "", "isPublic": true, "isDownloadable": true, @@ -14,8 +14,8 @@ "contextUrl": "https://github.com/datarobot/datarobot-user-models/tree/master/public_dropin_environments/java_codegen", "imageRepository": "env-java-codegen", "tags": [ - "v11.2.0-688c1c850012682da8000374", - "688c1c850012682da8000374", + "v11.2.0-689b9c1b0056193d330033b3", + "689b9c1b0056193d330033b3", "v11.2.0-latest" ] } diff --git a/public_dropin_environments/python311/env_info.json b/public_dropin_environments/python311/env_info.json index 5171546f8..ebfa292a9 100644 --- a/public_dropin_environments/python311/env_info.json +++ b/public_dropin_environments/python311/env_info.json @@ -4,7 +4,7 @@ "description": "This template environment can be used to create Python based custom models. User is responsible to provide requirements.txt with the model, to install all the required dependencies.", "programmingLanguage": "python", "label": "", - "environmentVersionId": "688c1c860072b67f71005108", + "environmentVersionId": "689b9c1c005f8052300021cc", "environmentVersionDescription": "", "isPublic": true, "isDownloadable": true, @@ -14,8 +14,8 @@ "contextUrl": "https://github.com/datarobot/datarobot-user-models/tree/master/public_dropin_environments/python311", "imageRepository": "env-python", "tags": [ - "v11.2.0-688c1c860072b67f71005108", - "688c1c860072b67f71005108", + "v11.2.0-689b9c1c005f8052300021cc", + "689b9c1c005f8052300021cc", "v11.2.0-latest" ] } diff --git a/public_dropin_environments/python311_genai_agents/env_info.json b/public_dropin_environments/python311_genai_agents/env_info.json index 90d5d9233..7acc25582 100644 --- a/public_dropin_environments/python311_genai_agents/env_info.json +++ b/public_dropin_environments/python311_genai_agents/env_info.json @@ -4,7 +4,7 @@ "description": "This template environment can be used to create GenAI-powered agents using CrewAI, LangGraph, or Llama-Index. Similar to other drop-in environments, you can either include a .pth artifact or any other code needed to deserialize your model, and optionally a custom.py file. You can also use this environment in codespaces.", "programmingLanguage": "python", "label": "", - "environmentVersionId": "6894fd315a5c1b1204e81aff", + "environmentVersionId": "689e166376dbcf1206bb5ce4", "environmentVersionDescription": "", "isPublic": true, "isDownloadable": true, @@ -15,8 +15,8 @@ "contextUrl": "https://github.com/datarobot/datarobot-user-models/tree/master/public_dropin_environments/python311_genai_agents", "imageRepository": "env-python-genai-agents", "tags": [ - "v11.2.0-6894fd315a5c1b1204e81aff", - "6894fd315a5c1b1204e81aff", + "v11.2.0-689e166376dbcf1206bb5ce4", + "689e166376dbcf1206bb5ce4", "v11.2.0-latest" ] } diff --git a/public_dropin_environments/python311_genai_agents/requirements.in b/public_dropin_environments/python311_genai_agents/requirements.in index 56951a1df..6b840b484 100644 --- a/public_dropin_environments/python311_genai_agents/requirements.in +++ b/public_dropin_environments/python311_genai_agents/requirements.in @@ -23,36 +23,40 @@ urllib3>=2.5.0 click~=8.1.8 crewai>=0.140.0 crewai-tools>=0.48.0 -datarobot-drum>=1.16.19 -datarobot-moderations>=11.1.23 +datarobot-drum>=1.16.22 +datarobot-moderations>=11.2.2 datarobot-mlops>=11.1.0 -datarobot>=3.7.0 +datarobot[auth]>=3.8.2 dotenv~=0.9.9 langchain-community~=0.3.23 +langchain-mcp-adapters~=0.1.9 langchain~=0.3.23 langgraph~=0.4.10 langgraph-prebuilt~=0.2.3 legacy-cgi~=2.6.3 litellm>=1.72.1 llama-index-core>=0.12.49 -llama-index-llms-langchain~=0.6.1 -llama-index-llms-litellm~=0.4.1 -llama-index-llms-openai~=0.3.38 +llama-index-llms-langchain>=0.6.1 +llama-index-llms-litellm>=0.4.1 +llama-index-llms-openai>=0.3.38 llama-index~=0.12.33 +mcp>=1.11.0 multidict~=6.5.0 onnxruntime~=1.22.0 -openai~=1.76.2 -opentelemetry-api~=1.33.0 -opentelemetry-instrumentation-aiohttp-client~=0.54b0 -opentelemetry-instrumentation-crewai~=0.40.5 -opentelemetry-instrumentation-httpx~=0.54b0 -opentelemetry-instrumentation-langchain~=0.40.5 -opentelemetry-instrumentation-llamaindex~=0.40.5 -opentelemetry-instrumentation-openai~=0.40.5 -opentelemetry-instrumentation-requests~=0.54b0 -opentelemetry-sdk~=1.33.0 +openai>=1.76.2 +opentelemetry-api>=1.33.0,<2.0.0 +opentelemetry-instrumentation-aiohttp-client>=0.54b0 +opentelemetry-instrumentation-crewai>=0.40.5 +opentelemetry-instrumentation-httpx>=0.54b0 +opentelemetry-instrumentation-langchain>=0.40.5 +opentelemetry-instrumentation-llamaindex>=0.40.5 +opentelemetry-instrumentation-openai>=0.40.5 +opentelemetry-instrumentation-requests>=0.54b0 +opentelemetry-sdk>=1.33.0 python-dotenv~=1.1.0 ragas @ git+https://github.com/explodinggradients/ragas@5d59549ad5ef511f621502c563bc55ac5aeb9188#subdirectory=ragas +# pyarrow==21.0.0 breaks the current ragas version https://github.com/apache/arrow/issues/47155 +pyarrow<21.0.0 requests~=2.32.4 traceloop-sdk~=0.40.2 uvicorn~=0.35.0 diff --git a/public_dropin_environments/python311_genai_agents/requirements.txt b/public_dropin_environments/python311_genai_agents/requirements.txt index 4468725aa..2db3a56ba 100644 --- a/public_dropin_environments/python311_genai_agents/requirements.txt +++ b/public_dropin_environments/python311_genai_agents/requirements.txt @@ -31,11 +31,9 @@ azure-core==1.34.0 azure-identity==1.22.0 azure-storage-blob==12.19.0 backoff==2.2.1 -backports-tarfile==1.2.0 banks==2.1.2 bcrypt==4.3.0 beautifulsoup4==4.13.4 -black==25.1.0 bleach[css]==6.2.0 blinker==1.9.0 boto3==1.37.3 @@ -52,21 +50,20 @@ cohere==5.15.0 colorama==0.4.6 coloredlogs==15.0.1 comm==0.2.2 -coverage==7.8.2 crewai==0.140.0 crewai-tools==0.48.0 cryptography==44.0.3 dataclasses-json==0.6.7 -datarobot==3.7.1 -datarobot-drum==1.16.19 +datarobot[auth]==3.8.2 +datarobot-drum==1.16.22 datarobot-mlops==11.1.0 -datarobot-moderations==11.2.0 +datarobot-moderations==11.2.2 datarobot-predict==1.13.2 datarobot-storage==2.2.0 datasets==3.6.0 debugpy==1.8.14 decorator==5.2.1 -deepeval==2.7.9 +deepeval==3.3.9 defusedxml==0.7.1 deprecated==1.2.18 deprecation==2.1.0 @@ -76,7 +73,6 @@ diskcache==5.6.3 distro==1.9.0 docker==7.1.0 docstring-parser==0.16 -docutils==0.21.2 dotenv==0.9.9 durationpy==0.9 ecs-logging==2.2.0 @@ -136,11 +132,7 @@ ipython==8.37.0 isodate==0.7.2 isoduration==20.11.0 itsdangerous==2.2.0 -jaraco-classes==3.4.0 -jaraco-context==6.0.1 -jaraco-functools==4.1.0 jedi==0.19.2 -jeepney==0.9.0 jinja2==3.1.6 jiter==0.8.2 jmespath==1.0.1 @@ -161,7 +153,6 @@ jupyter-kernel-gateway==3.0.1 jupyter-server==2.15.0 jupyter-server-terminals==0.5.3 jupyterlab-pygments==0.3.0 -keyring==25.6.0 kubernetes==32.0.1 lancedb==0.22.0 langchain==0.3.25 @@ -169,6 +160,7 @@ langchain-cohere==0.3.5 langchain-community==0.3.23 langchain-core==0.3.59 langchain-experimental==0.3.4 +langchain-mcp-adapters==0.1.9 langchain-nvidia-ai-endpoints==0.3.10 langchain-openai==0.2.14 langchain-text-splitters==0.3.8 @@ -182,24 +174,23 @@ legacy-cgi==2.6.3 litellm==1.72.6 llama-cloud==0.1.21 llama-cloud-services==0.6.15 -llama-index==0.12.35 -llama-index-agent-openai==0.4.7 -llama-index-cli==0.4.1 +llama-index==0.12.51 +llama-index-agent-openai==0.4.12 +llama-index-cli==0.4.4 llama-index-core==0.12.51 llama-index-embeddings-azure-openai==0.3.7 llama-index-embeddings-openai==0.3.1 llama-index-indices-managed-llama-cloud==0.6.11 llama-index-instrumentation==0.3.0 -llama-index-llms-azure-openai==0.3.2 +llama-index-llms-azure-openai==0.3.4 llama-index-llms-bedrock-converse==0.7.1 -llama-index-llms-fireworks==0.3.2 llama-index-llms-langchain==0.6.1 llama-index-llms-litellm==0.4.2 -llama-index-llms-openai==0.3.38 +llama-index-llms-openai==0.4.7 llama-index-llms-vertex==0.5.0 -llama-index-multi-modal-llms-openai==0.4.3 -llama-index-program-openai==0.3.1 -llama-index-question-gen-openai==0.3.0 +llama-index-multi-modal-llms-openai==0.5.3 +llama-index-program-openai==0.3.2 +llama-index-question-gen-openai==0.3.1 llama-index-readers-file==0.4.7 llama-index-readers-llama-parse==0.4.0 llama-index-workflows==1.1.0 @@ -210,13 +201,13 @@ markdown-it-py==3.0.0 markupsafe==3.0.2 marshmallow==3.26.1 matplotlib-inline==0.1.7 +mcp==1.11.0 mdurl==0.1.2 mem0ai==0.1.98 memory-profiler==0.61.0 mistune==3.1.3 mmh3==5.1.0 monotonic==1.6 -more-itertools==10.7.0 mpmath==1.3.0 msal==1.32.3 msal-extensions==1.3.1 @@ -229,14 +220,13 @@ nbformat==5.10.4 nemoguardrails==0.14.0 nest-asyncio==1.6.0 networkx==3.4.2 -nh3==0.2.21 nltk==3.9.1 nodeenv==1.9.1 numpy==2.2.5 oauthlib==3.2.2 ollama==0.5.1 onnxruntime==1.22.0 -openai==1.76.2 +openai==1.99.9 openpyxl==3.1.5 opentelemetry-api==1.33.0 opentelemetry-exporter-otlp-proto-common==1.33.0 @@ -292,12 +282,10 @@ packaging==24.2 pandas==2.2.3 pandocfilters==1.5.1 parso==0.8.4 -pathspec==0.12.1 pdfminer-six==20250327 pdfplumber==0.11.6 pexpect==4.9.0 pillow==11.3.0 -pkginfo==1.10.0 platformdirs==4.3.8 pluggy==1.6.0 portalocker==2.10.1 @@ -320,6 +308,7 @@ pycparser==2.22 pydantic==2.11.7 pydantic-core==2.33.2 pydantic-settings==2.9.1 +pyfiglet==1.0.3 pygments==2.19.1 pyjwt[crypto]==2.10.1 pypdf==5.4.0 @@ -336,6 +325,7 @@ pytest-xdist==3.7.0 python-dateutil==2.9.0.post0 python-dotenv==1.1.0 python-json-logger==3.3.0 +python-multipart==0.0.20 pytube==15.0.0 pytz==2024.2 pyvis==0.3.2 @@ -343,14 +333,12 @@ pyyaml==6.0.2 pyzmq==26.4.0 qdrant-client==1.14.2 ragas @ git+https://github.com/explodinggradients/ragas@5d59549ad5ef511f621502c563bc55ac5aeb9188#subdirectory=ragas -readme-renderer==44.0 referencing==0.36.2 regex==2024.11.6 requests==2.32.4 requests-oauthlib==2.0.0 requests-toolbelt==1.0.0 rfc3339-validator==0.1.4 -rfc3986==2.0.0 rfc3986-validator==0.1.1 rich==13.9.4 rouge-score==0.1.2 @@ -360,7 +348,6 @@ ruamel-yaml==0.17.4 s3transfer==0.11.3 schema==0.7.7 scipy==1.15.3 -secretstorage==3.3.3 send2trash==1.8.3 sentry-sdk==2.29.1 shapely==2.1.1 @@ -370,6 +357,7 @@ six==1.17.0 sniffio==1.3.1 soupsieve==2.7 sqlalchemy[asyncio]==2.0.40 +sse-starlette==3.0.2 stack-data==0.6.3 starlette==0.46.2 strenum==0.4.15 @@ -391,11 +379,10 @@ tqdm==4.67.1 traceloop-sdk==0.40.14 trafaret==2.1.1 traitlets==5.14.3 -twine==5.1.1 typer==0.15.3 types-python-dateutil==2.9.0.20241206 types-requests==2.32.0.20250328 -typing-extensions==4.13.2 +typing-extensions==4.14.1 typing-inspect==0.9.0 typing-inspection==0.4.0 tzdata==2025.2 diff --git a/public_dropin_environments/python3_keras/env_info.json b/public_dropin_environments/python3_keras/env_info.json index f4d93148f..f08ae463b 100644 --- a/public_dropin_environments/python3_keras/env_info.json +++ b/public_dropin_environments/python3_keras/env_info.json @@ -4,7 +4,7 @@ "description": "This template environment can be used to create artifact-only keras custom models. This environment contains keras backed by tensorflow and only requires your model artifact as a .h5 file and optionally a custom.py file.", "programmingLanguage": "python", "label": "", - "environmentVersionId": "688c1c86002072530100056c", + "environmentVersionId": "689cb69b9c627511efb5b741", "environmentVersionDescription": "", "isPublic": true, "isDownloadable": true, @@ -14,8 +14,8 @@ "contextUrl": "https://github.com/datarobot/datarobot-user-models/tree/master/public_dropin_environments/python3_keras", "imageRepository": "env-python-keras", "tags": [ - "v11.2.0-688c1c86002072530100056c", - "688c1c86002072530100056c", + "v11.2.0-689cb69b9c627511efb5b741", + "689cb69b9c627511efb5b741", "v11.2.0-latest" ] } diff --git a/public_dropin_environments/python3_keras/requirements.txt b/public_dropin_environments/python3_keras/requirements.txt index 72350ad75..f8a04dc25 100644 --- a/public_dropin_environments/python3_keras/requirements.txt +++ b/public_dropin_environments/python3_keras/requirements.txt @@ -4,80 +4,79 @@ # # pip-compile --index-url=https://pypi.org/simple --no-annotate --no-emit-index-url --no-emit-trusted-host --output-file=requirements.txt requirements.in # -absl-py==2.3.0 +absl-py==2.3.1 annotated-types==0.7.0 argcomplete==3.6.2 astunparse==1.6.3 -azure-core==1.34.0 -azure-identity==1.23.0 +azure-core==1.35.0 +azure-identity==1.24.0 azure-storage-blob==12.19.0 blinker==1.9.0 -boto3==1.38.23 -botocore==1.38.23 +boto3==1.40.8 +botocore==1.40.8 cachetools==5.5.2 -certifi==2025.4.26 +certifi==2025.8.3 cffi==1.17.1 -charset-normalizer==3.4.2 +charset-normalizer==3.4.3 click==8.2.1 -cryptography==45.0.3 -datarobot==3.7.1 -datarobot-drum==1.16.19 +cryptography==45.0.6 +datarobot==3.8.2 +datarobot-drum==1.16.20 datarobot-mlops==11.1.0 datarobot-storage==2.2.0 -deprecated==1.2.18 docker==7.1.0 filechunkio==1.8 flask==3.1.1 flatbuffers==25.2.10 gast==0.6.0 -google-api-core==2.25.0rc1 -google-auth==2.40.2 +google-api-core==2.25.1 +google-auth==2.40.3 google-cloud-core==2.4.3 google-cloud-storage==2.19.0 google-crc32c==1.7.1 google-pasta==0.2.0 google-resumable-media==2.7.2 googleapis-common-protos==1.70.0 -grpcio==1.71.0 -h5py==3.13.0 +grpcio==1.74.0 +h5py==3.14.0 idna==3.10 -importlib-metadata==8.6.1 +importlib-metadata==8.7.0 isodate==0.7.2 itsdangerous==2.2.0 jinja2==3.1.6 jmespath==1.0.1 joblib==1.5.1 julia==0.5.7 -keras==3.10.0 +keras==3.11.2 libclang==18.1.1 -markdown==3.8 -markdown-it-py==3.0.0 +markdown==3.8.2 +markdown-it-py==4.0.0 markupsafe==3.0.2 mdurl==0.1.2 memory-profiler==0.61.0 -ml-dtypes==0.5.1 -msal==1.32.3 +ml-dtypes==0.5.3 +msal==1.33.0 msal-extensions==1.3.1 mypy-extensions==1.1.0 namex==0.1.0 numpy==2.1.3 -opentelemetry-api==1.33.1 -opentelemetry-exporter-otlp-proto-common==1.33.1 -opentelemetry-exporter-otlp-proto-http==1.33.1 -opentelemetry-instrumentation==0.54b1 -opentelemetry-instrumentation-aiohttp-client==0.54b1 -opentelemetry-instrumentation-requests==0.54b1 -opentelemetry-proto==1.33.1 -opentelemetry-sdk==1.33.1 -opentelemetry-semantic-conventions==0.54b1 -opentelemetry-util-http==0.54b1 +opentelemetry-api==1.36.0 +opentelemetry-exporter-otlp-proto-common==1.36.0 +opentelemetry-exporter-otlp-proto-http==1.36.0 +opentelemetry-instrumentation==0.57b0 +opentelemetry-instrumentation-aiohttp-client==0.57b0 +opentelemetry-instrumentation-requests==0.57b0 +opentelemetry-proto==1.36.0 +opentelemetry-sdk==1.36.0 +opentelemetry-semantic-conventions==0.57b0 +opentelemetry-util-http==0.57b0 opt-einsum==3.4.0 -optree==0.15.0 -orjson==3.10.18 +optree==0.17.0 +orjson==3.11.2 packaging==25.0 -pandas==2.2.3 +pandas==2.3.1 pillow==11.3.0 -progress==1.6 +progress==1.6.1 proto-plus==1.26.1 protobuf==5.29.5 psutil==7.0.0 @@ -85,21 +84,21 @@ py4j==0.10.9.9 pyasn1==0.6.1 pyasn1-modules==0.4.2 pycparser==2.22 -pydantic==2.11.5 +pydantic==2.11.7 pydantic-core==2.33.2 -pygments==2.19.1 +pygments==2.19.2 pyjwt[crypto]==2.10.1 python-dateutil==2.9.0.post0 pytz==2025.2 pyyaml==6.0.2 requests==2.32.4 requests-toolbelt==1.0.0 -rich==14.0.0 +rich==14.1.0 rsa==4.9.1 ruamel-yaml==0.17.4 -s3transfer==0.13.0 +s3transfer==0.13.1 scikeras==0.13.0 -scikit-learn==1.6.1 +scikit-learn==1.7.1 scipy==1.15.3 six==1.17.0 strenum==0.4.15 @@ -112,14 +111,14 @@ termcolor==3.1.0 texttable==1.7.0 threadpoolctl==3.6.0 trafaret==2.1.1 -typing-extensions==4.13.2 +typing-extensions==4.14.1 typing-inspection==0.4.1 tzdata==2025.2 urllib3==2.5.0 werkzeug==3.1.3 wheel==0.45.1 -wrapt==1.17.2 -zipp==3.22.0 +wrapt==1.17.3 +zipp==3.23.0 # The following packages are considered to be unsafe in a requirements file: # setuptools diff --git a/public_dropin_environments/python3_onnx/env_info.json b/public_dropin_environments/python3_onnx/env_info.json index d7ed253c2..f55343d40 100644 --- a/public_dropin_environments/python3_onnx/env_info.json +++ b/public_dropin_environments/python3_onnx/env_info.json @@ -4,7 +4,7 @@ "description": "This template environment can be used to create artifact-only ONNX custom models. This environment contains ONNX runtime and only requires your model artifact as an .onnx file and optionally a custom.py file.", "programmingLanguage": "python", "label": "", - "environmentVersionId": "688c1c860026bb4aaf007fc9", + "environmentVersionId": "689b9c1c003a007bac004734", "environmentVersionDescription": "", "isPublic": true, "isDownloadable": true, @@ -14,8 +14,8 @@ "contextUrl": "https://github.com/datarobot/datarobot-user-models/tree/master/public_dropin_environments/python3_onnx", "imageRepository": "env-python-onnx", "tags": [ - "v11.2.0-688c1c860026bb4aaf007fc9", - "688c1c860026bb4aaf007fc9", + "v11.2.0-689b9c1c003a007bac004734", + "689b9c1c003a007bac004734", "v11.2.0-latest" ] } diff --git a/public_dropin_environments/python3_pytorch/env_info.json b/public_dropin_environments/python3_pytorch/env_info.json index 7b15c168b..12f121211 100644 --- a/public_dropin_environments/python3_pytorch/env_info.json +++ b/public_dropin_environments/python3_pytorch/env_info.json @@ -4,7 +4,7 @@ "description": "This template environment can be used to create artifact-only PyTorch custom models. This environment contains PyTorch and requires only your model artifact as a .pth file, any other code needed to deserialize your model, and optionally a custom.py file.", "programmingLanguage": "python", "label": "", - "environmentVersionId": "688c1c860071401a3a006f93", + "environmentVersionId": "689b9c1c00358f486400449a", "environmentVersionDescription": "", "isPublic": true, "isDownloadable": true, @@ -14,8 +14,8 @@ "contextUrl": "https://github.com/datarobot/datarobot-user-models/tree/master/public_dropin_environments/python3_pytorch", "imageRepository": "env-python-pytorch", "tags": [ - "v11.2.0-688c1c860071401a3a006f93", - "688c1c860071401a3a006f93", + "v11.2.0-689b9c1c00358f486400449a", + "689b9c1c00358f486400449a", "v11.2.0-latest" ] } diff --git a/public_dropin_environments/python3_sklearn/env_info.json b/public_dropin_environments/python3_sklearn/env_info.json index 08a06cce7..9d3dfe678 100644 --- a/public_dropin_environments/python3_sklearn/env_info.json +++ b/public_dropin_environments/python3_sklearn/env_info.json @@ -4,7 +4,7 @@ "description": "This template environment can be used to create artifact-only scikit-learn custom models. This environment contains scikit-learn and only requires your model artifact as a .pkl file and optionally a custom.py file.", "programmingLanguage": "python", "label": "", - "environmentVersionId": "688c1c860052032c27001851", + "environmentVersionId": "689b9c1c00391a04300027af", "environmentVersionDescription": "", "isPublic": true, "isDownloadable": true, @@ -14,8 +14,8 @@ "contextUrl": "https://github.com/datarobot/datarobot-user-models/tree/master/public_dropin_environments/python3_sklearn", "imageRepository": "env-python-sklearn", "tags": [ - "v11.2.0-688c1c860052032c27001851", - "688c1c860052032c27001851", + "v11.2.0-689b9c1c00391a04300027af", + "689b9c1c00391a04300027af", "v11.2.0-latest" ] } diff --git a/public_dropin_environments/python3_xgboost/env_info.json b/public_dropin_environments/python3_xgboost/env_info.json index 3431e045a..91b2468b4 100644 --- a/public_dropin_environments/python3_xgboost/env_info.json +++ b/public_dropin_environments/python3_xgboost/env_info.json @@ -4,7 +4,7 @@ "description": "This template environment can be used to create artifact-only xgboost custom models. This environment contains xgboost and only requires your model artifact as a .pkl file and optionally a custom.py file.", "programmingLanguage": "python", "label": "", - "environmentVersionId": "688c1c86005f9d5205004325", + "environmentVersionId": "689b9c1c003e816f3c004d3d", "environmentVersionDescription": "", "isPublic": true, "isDownloadable": true, @@ -14,8 +14,8 @@ "contextUrl": "https://github.com/datarobot/datarobot-user-models/tree/master/public_dropin_environments/python3_xgboost", "imageRepository": "env-python-xgboost", "tags": [ - "v11.2.0-688c1c86005f9d5205004325", - "688c1c86005f9d5205004325", + "v11.2.0-689b9c1c003e816f3c004d3d", + "689b9c1c003e816f3c004d3d", "v11.2.0-latest" ] } diff --git a/public_dropin_environments/r_lang/env_info.json b/public_dropin_environments/r_lang/env_info.json index 5b1d123a0..4da033388 100644 --- a/public_dropin_environments/r_lang/env_info.json +++ b/public_dropin_environments/r_lang/env_info.json @@ -4,7 +4,7 @@ "description": "This template environment can be used to create artifact-only R custom models that use the caret library. Your custom model archive need only contain your model artifacts if you use the environment correctly.", "programmingLanguage": "r", "label": "", - "environmentVersionId": "688c1c860078575bb2000c15", + "environmentVersionId": "689b9c1c00395609d2005e42", "environmentVersionDescription": "", "isPublic": true, "isDownloadable": true, @@ -14,8 +14,8 @@ "contextUrl": "https://github.com/datarobot/datarobot-user-models/tree/master/public_dropin_environments/r_lang", "imageRepository": "env-r-lang", "tags": [ - "v11.2.0-688c1c860078575bb2000c15", - "688c1c860078575bb2000c15", + "v11.2.0-689b9c1c00395609d2005e42", + "689b9c1c00395609d2005e42", "v11.2.0-latest" ] } diff --git a/public_dropin_nim_environments/nim_sidecar/Dockerfile b/public_dropin_nim_environments/nim_sidecar/Dockerfile index 75a314df3..11ab1bb33 100644 --- a/public_dropin_nim_environments/nim_sidecar/Dockerfile +++ b/public_dropin_nim_environments/nim_sidecar/Dockerfile @@ -1,6 +1,6 @@ # This is a private chain-guard development image that is stored in DataRobot's private registry. # Replace it with your own development chain-gaurd image if you build your own. -FROM datarobotdev/mirror_chainguard_datarobot.com_python-fips:3.11-dev as build +FROM datarobotdev/mirror_chainguard_datarobot.com_python-fips:3.11-dev AS build ENV VIRTUAL_ENV=/opt/venv USER root @@ -42,9 +42,13 @@ ENV PATH=${VIRTUAL_ENV}/bin:${PATH} ENV OPENAI_HOST=localhost ENV OPENAI_PORT=8000 ENV CODE_DIR=/opt/code +ENV USE_NIM_WATCHDOG=true ENV ADDRESS=0.0.0.0:8080 ENV WITH_ERROR_SERVER=1 +RUN mkdir -p ${CODE_DIR} \ + && chmod a+rwX ${CODE_DIR} + # This makes print statements show up in the logs API ENV PYTHONUNBUFFERED=1 diff --git a/public_dropin_nim_environments/nim_sidecar/env_info.json b/public_dropin_nim_environments/nim_sidecar/env_info.json index 5b7319fd1..a2a0d2ba5 100644 --- a/public_dropin_nim_environments/nim_sidecar/env_info.json +++ b/public_dropin_nim_environments/nim_sidecar/env_info.json @@ -4,17 +4,18 @@ "description": "", "programmingLanguage": "python", "label": "", - "environmentVersionId": "6848a84e0c7c49131250fdd4", + "environmentVersionId": "68a50ccae61f42cbb6297e9e", "environmentVersionDescription": "Run with 10 HTTP workers by default", "isPublic": true, + "isDownloadable": true, "useCases": [ "customModel" ], "contextUrl": "https://github.com/datarobot/datarobot-user-models/tree/master/public_dropin_nim_environments/nim_sidecar", "imageRepository": "env-nim-sidecar", "tags": [ - "v11.1.0-6848b6572081a81ac56c7a0b", - "6848b6572081a81ac56c7a0b", - "v11.1.0-latest" + "v11.2.0-68a50ccae61f42cbb6297e9e", + "68a50ccae61f42cbb6297e9e", + "v11.2.0-latest" ] } diff --git a/public_dropin_nim_environments/nim_sidecar/requirements.txt b/public_dropin_nim_environments/nim_sidecar/requirements.txt index 71133d457..073c51ff6 100644 --- a/public_dropin_nim_environments/nim_sidecar/requirements.txt +++ b/public_dropin_nim_environments/nim_sidecar/requirements.txt @@ -4,113 +4,113 @@ # # pip-compile --index-url=https://pypi.org/simple --no-annotate --no-emit-index-url --no-emit-trusted-host --output-file=requirements.txt requirements.in # -aiohappyeyeballs==2.4.6 -aiohttp==3.11.13 -aiosignal==1.3.2 +aiohappyeyeballs==2.6.1 +aiohttp==3.12.15 +aiosignal==1.4.0 annotated-types==0.7.0 -anyio==4.8.0 -argcomplete==3.5.3 +anyio==4.10.0 +argcomplete==3.6.2 async-timeout==5.0.1 -attrs==25.1.0 -azure-core==1.32.0 -azure-identity==1.20.0 +attrs==25.3.0 +azure-core==1.35.0 +azure-identity==1.24.0 azure-storage-blob==12.19.0 -backoff==2.2.1 blinker==1.9.0 -boto3==1.37.1 -botocore==1.37.1 -cachetools==4.2.4 -certifi==2025.1.31 +boto3==1.40.13 +botocore==1.40.13 +cachetools==5.5.2 +certifi==2025.8.3 cffi==1.17.1 -charset-normalizer==3.4.1 -click==8.1.8 -cryptography==44.0.1 -datarobot==3.6.3 -datarobot-drum==1.16.17 -datarobot-mlops==11.1.0a3 -datarobot-mlops-connected-client==11.1.0a3 -datarobot-storage==0.0.0 -deprecated==1.2.18 +charset-normalizer==3.4.3 +click==8.2.1 +cryptography==45.0.6 +datarobot==3.8.2 +datarobot-drum==1.16.23 +datarobot-mlops==11.1.0 +datarobot-mlops-connected-client==11.1.0 +datarobot-storage==2.2.0 distro==1.9.0 docker==7.1.0 -exceptiongroup==1.2.2 +exceptiongroup==1.3.0 filechunkio==1.8 -flask==3.1.0 -frozenlist==1.5.0 -google-api-core==1.34.0 -google-auth==1.28.1 -google-cloud-core==2.4.2 -google-cloud-storage==1.43.0 -google-crc32c==1.6.0 +flask==3.1.2 +frozenlist==1.7.0 +google-api-core==2.25.1 +google-auth==2.40.3 +google-cloud-core==2.4.3 +google-cloud-storage==2.19.0 +google-crc32c==1.7.1 google-resumable-media==2.7.2 -googleapis-common-protos==1.68.0 -h11==0.14.0 -httpcore==1.0.7 +googleapis-common-protos==1.70.0 +h11==0.16.0 +httpcore==1.0.9 httpx==0.28.1 idna==3.10 +importlib-metadata==8.7.0 isodate==0.7.2 itsdangerous==2.2.0 -jinja2==3.1.5 -jiter==0.8.2 +jinja2==3.1.6 +jiter==0.10.0 jmespath==1.0.1 julia==0.5.7 markupsafe==3.0.2 memory-profiler==0.61.0 -msal==1.31.1 -msal-extensions==1.2.0 -multidict==6.1.0 -mypy-extensions==1.0.0 -numpy==2.0.2 -openai==1.64.0 -opentelemetry-api==1.16.0 -opentelemetry-exporter-otlp-proto-http==1.16.0 -opentelemetry-instrumentation-aiohttp-client==0.37b0 -opentelemetry-instrumentation-openai==0.37b0 -opentelemetry-instrumentation-requests==0.37b0 -opentelemetry-instrumentation==0.37b0 -opentelemetry-proto==1.16.0 -opentelemetry-sdk==1.16.0 -opentelemetry-semantic-conventions==0.37b0 -opentelemetry-util-http==0.37b0 -orjson==3.10.15 -packaging==24.2 -pandas==2.2.3 -pillow==11.1.0 -portalocker==2.10.1 -progress==1.6 -propcache==0.3.0 -protobuf==3.20.3 +msal==1.33.0 +msal-extensions==1.3.1 +multidict==6.6.4 +mypy-extensions==1.1.0 +numpy==2.2.6 +openai==1.100.2 +opentelemetry-api==1.36.0 +opentelemetry-exporter-otlp-proto-common==1.36.0 +opentelemetry-exporter-otlp-proto-http==1.36.0 +opentelemetry-instrumentation==0.57b0 +opentelemetry-instrumentation-aiohttp-client==0.57b0 +opentelemetry-instrumentation-openai==0.45.6 +opentelemetry-instrumentation-requests==0.57b0 +opentelemetry-proto==1.36.0 +opentelemetry-sdk==1.36.0 +opentelemetry-semantic-conventions==0.57b0 +opentelemetry-semantic-conventions-ai==0.4.12 +opentelemetry-util-http==0.57b0 +orjson==3.11.2 +packaging==25.0 +pandas==2.3.1 +pillow==11.3.0 +progress==1.6.1 +propcache==0.3.2 +proto-plus==1.26.1 +protobuf==6.32.0 psutil==7.0.0 py4j==0.10.9.9 pyasn1==0.6.1 -pyasn1-modules==0.4.1 +pyasn1-modules==0.4.2 pycparser==2.22 -pydantic==2.10.6 -pydantic-core==2.27.2 +pydantic==2.11.7 +pydantic-core==2.33.2 pyjwt[crypto]==2.10.1 python-dateutil==2.9.0.post0 -pytz==2025.1 +pytz==2025.2 pyyaml==6.0.2 -requests==2.32.3 +requests==2.32.5 requests-toolbelt==1.0.0 -rsa==4.9 +rsa==4.9.1 ruamel-yaml==0.17.4 -s3transfer==0.11.2 -scipy==1.13.1 +s3transfer==0.13.1 +scipy==1.15.3 six==1.17.0 sniffio==1.3.1 strenum==0.4.15 strictyaml==1.4.2 -termcolor==2.5.0 +termcolor==3.1.0 texttable==1.7.0 tqdm==4.67.1 trafaret==2.1.1 -typing-extensions==4.12.2 -tzdata==2025.1 -urllib3==1.26.20 +typing-extensions==4.14.1 +typing-inspection==0.4.1 +tzdata==2025.2 +urllib3==2.5.0 werkzeug==3.1.3 -wrapt==1.17.2 -yarl==1.18.3 - -# The following packages are considered to be unsafe in a requirements file: -# setuptools +wrapt==1.17.3 +yarl==1.20.1 +zipp==3.23.0 diff --git a/public_dropin_notebook_environments/py311_notebook_dropin.tar.gz b/public_dropin_notebook_environments/py311_notebook_dropin.tar.gz index 80d65fcdd..5481eed2f 100644 Binary files a/public_dropin_notebook_environments/py311_notebook_dropin.tar.gz and b/public_dropin_notebook_environments/py311_notebook_dropin.tar.gz differ diff --git a/public_dropin_notebook_environments/python311_notebook_base/Dockerfile b/public_dropin_notebook_environments/python311_notebook_base/Dockerfile index 0e7211fc7..d43388b02 100644 --- a/public_dropin_notebook_environments/python311_notebook_base/Dockerfile +++ b/public_dropin_notebook_environments/python311_notebook_base/Dockerfile @@ -74,7 +74,7 @@ RUN python3.11 -m venv ${VENV_PATH} && pip3 install -U pip setuptools WORKDIR ${WORKDIR} # Install git helper binary used for private git authentication in Notebooks/Codepaces -RUN curl -L -o drgithelper https://github.com/datarobot-oss/drgithelper/releases/download/v0.0.13/drgithelper && chmod +x drgithelper +RUN curl -L -o drgithelper https://github.com/datarobot-oss/drgithelper/releases/download/v0.0.16/drgithelper && chmod +x drgithelper COPY ./agent/agent.py ./agent/cgroup_watchers.py ${AGENTDIR}/ COPY ./jupyter_kernel_gateway_config.py ./start_server.sh ${WORKDIR}/ diff --git a/public_dropin_notebook_environments/python311_notebook_base/env_info.json b/public_dropin_notebook_environments/python311_notebook_base/env_info.json index d6930ce78..d9dd0c4f9 100644 --- a/public_dropin_notebook_environments/python311_notebook_base/env_info.json +++ b/public_dropin_notebook_environments/python311_notebook_base/env_info.json @@ -4,7 +4,7 @@ "description": "This template environment can be used to create Python 3.11 notebook environments.", "programmingLanguage": "python", "label": "", - "environmentVersionId": "68827e0dfccc7df6685c1899", + "environmentVersionId": "689f735077c7c911fc10b5c1", "environmentVersionDescription": "", "isPublic": true, "isDownloadable": true, @@ -15,8 +15,8 @@ "contextUrl": "https://github.com/datarobot/datarobot-user-models/tree/master/public_dropin_notebook_environments/python311_notebook_base", "imageRepository": "env-notebook-python311-notebook-base", "tags": [ - "v11.2.0-68827e0dfccc7df6685c1899", - "68827e0dfccc7df6685c1899", + "v11.2.0-689f735077c7c911fc10b5c1", + "689f735077c7c911fc10b5c1", "v11.2.0-latest" ] } diff --git a/public_dropin_notebook_environments/python311_notebook_base/start_server.sh b/public_dropin_notebook_environments/python311_notebook_base/start_server.sh index 738f1ba41..569dc8775 100644 --- a/public_dropin_notebook_environments/python311_notebook_base/start_server.sh +++ b/public_dropin_notebook_environments/python311_notebook_base/start_server.sh @@ -29,7 +29,9 @@ cp -L /var/run/notebooks/ssh/authorized_keys/notebooks /etc/authorized_keys/ && mkdir /etc/ssh/keys && cp -L /var/run/notebooks/ssh/keys/ssh_host_* /etc/ssh/keys/ && chmod 600 /etc/ssh/keys/ssh_host_* nohup /usr/sbin/sshd -D & -# Initialize the git helper. Turn on/off features dependent on `GITHELPER_*` env vars +# Ensure proper permissions on the directory used by cache daemon to ensure it starts (create dir. if needed) +mkdir -p /home/notebooks/storage/.cache/git/credential && chmod 700 /home/notebooks/storage/.cache/git/credential +# Initialize the git helper. Features are turned on/off dependent on `GITHELPER_*` env vars /etc/system/kernel/drgithelper configs set # no trailing slash in the working dir path diff --git a/tests/unit/datarobot_drum/drum/test_main.py b/tests/unit/datarobot_drum/drum/test_main.py index b84320681..fd5c05058 100644 --- a/tests/unit/datarobot_drum/drum/test_main.py +++ b/tests/unit/datarobot_drum/drum/test_main.py @@ -13,12 +13,12 @@ @pytest.mark.parametrize("workers_param, expected_workers", [(None, 0), (1, 1), (10, 10)]) -@patch("datarobot_drum.drum.main.RuntimeParameters", autospec=True) -@patch("datarobot_drum.drum.main.RuntimeParametersLoader", autospec=True) @patch("datarobot_drum.drum.drum.CMRunner", autospec=True) -@patch("datarobot_drum.drum.main.CMRunnerArgsRegistry", autospec=True) +@patch("datarobot_drum.drum.utils.setup.RuntimeParameters", autospec=True) +@patch("datarobot_drum.drum.utils.setup.RuntimeParametersLoader", autospec=True) +@patch("datarobot_drum.drum.utils.setup.CMRunnerArgsRegistry", autospec=True) def test_custom_model_workers( - args_registry, cm_runner, runtime_params_loader, runtime_params, workers_param, expected_workers + args_registry, runtime_params_loader, runtime_params, cm_runner, workers_param, expected_workers ): options = argparse.Namespace() options.max_workers = 0 @@ -34,7 +34,10 @@ def test_custom_model_workers( else: runtime_params.has.return_value = False - main() + with patch("datarobot_drum.drum.main.setup_otel") as setup_otel_mock: + setup_otel_mock.return_value = (None, None, None) + main() + runtime_params.has.assert_any_call("CUSTOM_MODEL_WORKERS") if workers_param: runtime_params.get.assert_any_call("CUSTOM_MODEL_WORKERS") diff --git a/tests/unit/datarobot_drum/drum/utils/test_setup.py b/tests/unit/datarobot_drum/drum/utils/test_setup.py new file mode 100644 index 000000000..cb37cb9de --- /dev/null +++ b/tests/unit/datarobot_drum/drum/utils/test_setup.py @@ -0,0 +1,133 @@ +# +# Copyright 2025 DataRobot, Inc. and its affiliates. +# +# All rights reserved. +# This is proprietary source code of DataRobot, Inc. and its affiliates. +# Released under the terms of DataRobot Tool and Utility Agreement. +# +from argparse import Namespace +from unittest import mock + +# Import the function under test +from datarobot_drum.drum.utils.setup import setup_options + + +def test_setup_options_default(monkeypatch): + # Mock CMRunnerArgsRegistry and dependencies + mock_parser = mock.Mock() + mock_options = Namespace(max_workers=None, runtime_params_file=None, lazy_loading_file=None) + mock_parser.parse_args.return_value = mock_options + + with mock.patch( + "datarobot_drum.drum.args_parser.CMRunnerArgsRegistry.get_arg_parser", + return_value=mock_parser, + ), mock.patch( + "datarobot_drum.drum.args_parser.CMRunnerArgsRegistry.extend_sys_argv_with_env_vars" + ), mock.patch( + "datarobot_drum.drum.args_parser.CMRunnerArgsRegistry.verify_options" + ): + opts = setup_options([]) + assert hasattr(opts, "max_workers") + assert opts.max_workers == 1 # Default to 1 + + +def test_setup_options_with_max_workers(monkeypatch): + mock_parser = mock.Mock() + mock_options = Namespace(max_workers="3", runtime_params_file=None, lazy_loading_file=None) + mock_parser.parse_args.return_value = mock_options + + with mock.patch( + "datarobot_drum.drum.args_parser.CMRunnerArgsRegistry.get_arg_parser", + return_value=mock_parser, + ), mock.patch( + "datarobot_drum.drum.args_parser.CMRunnerArgsRegistry.extend_sys_argv_with_env_vars" + ), mock.patch( + "datarobot_drum.drum.args_parser.CMRunnerArgsRegistry.verify_options" + ): + opts = setup_options([]) + assert opts.max_workers == 3 or opts.max_workers == "3" or int(opts.max_workers) == 3 + + +def test_setup_options_runtime_parameters(monkeypatch): + mock_parser = mock.Mock() + mock_options = Namespace(max_workers=None, runtime_params_file=None, lazy_loading_file=None) + mock_parser.parse_args.return_value = mock_options + + with mock.patch( + "datarobot_drum.drum.args_parser.CMRunnerArgsRegistry.get_arg_parser", + return_value=mock_parser, + ), mock.patch( + "datarobot_drum.drum.args_parser.CMRunnerArgsRegistry.extend_sys_argv_with_env_vars" + ), mock.patch( + "datarobot_drum.drum.args_parser.CMRunnerArgsRegistry.verify_options" + ), mock.patch( + "datarobot_drum.RuntimeParameters.has", return_value=True + ), mock.patch( + "datarobot_drum.RuntimeParameters.get", return_value=5 + ): + opts = setup_options([]) + assert opts.max_workers == 5 + + +def test_setup_options_runtime_params_file(monkeypatch): + mock_parser = mock.Mock() + mock_options = Namespace( + max_workers=None, runtime_params_file="params.yaml", code_dir=".", lazy_loading_file=None + ) + mock_parser.parse_args.return_value = mock_options + + with mock.patch( + "datarobot_drum.drum.args_parser.CMRunnerArgsRegistry.get_arg_parser", + return_value=mock_parser, + ), mock.patch( + "datarobot_drum.drum.args_parser.CMRunnerArgsRegistry.extend_sys_argv_with_env_vars" + ), mock.patch( + "datarobot_drum.drum.args_parser.CMRunnerArgsRegistry.verify_options" + ), mock.patch( + "datarobot_drum.drum.utils.setup.RuntimeParametersLoader" + ) as mock_loader: + instance = mock_loader.return_value + instance.setup_environment_variables = mock.Mock() + opts = setup_options([]) + instance.setup_environment_variables.assert_called_once() + + +def test_setup_options_lazy_loading_file(monkeypatch): + mock_parser = mock.Mock() + mock_options = Namespace( + max_workers=None, runtime_params_file=None, lazy_loading_file="lazy.yaml" + ) + mock_parser.parse_args.return_value = mock_options + + with mock.patch( + "datarobot_drum.drum.args_parser.CMRunnerArgsRegistry.get_arg_parser", + return_value=mock_parser, + ), mock.patch( + "datarobot_drum.drum.args_parser.CMRunnerArgsRegistry.extend_sys_argv_with_env_vars" + ), mock.patch( + "datarobot_drum.drum.args_parser.CMRunnerArgsRegistry.verify_options" + ), mock.patch( + "datarobot_drum.drum.lazy_loading.lazy_loading_handler.LazyLoadingHandler.setup_environment_variables_from_values_file" + ) as mock_lazy: + opts = setup_options([]) + mock_lazy.assert_called_once_with("lazy.yaml") + + +def test_setup_options_argcomplete_missing(monkeypatch, capsys): + mock_parser = mock.Mock() + mock_options = Namespace(max_workers=None, runtime_params_file=None, lazy_loading_file=None) + mock_parser.parse_args.return_value = mock_options + + with mock.patch( + "datarobot_drum.drum.args_parser.CMRunnerArgsRegistry.get_arg_parser", + return_value=mock_parser, + ), mock.patch( + "datarobot_drum.drum.args_parser.CMRunnerArgsRegistry.extend_sys_argv_with_env_vars" + ), mock.patch( + "datarobot_drum.drum.args_parser.CMRunnerArgsRegistry.verify_options" + ), mock.patch.dict( + "sys.modules", {"argcomplete": None} + ): + opts = setup_options([]) + captured = capsys.readouterr() + assert "autocompletion of arguments is not supported" in captured.err