Source code for nemo_evaluator.adapters.types

# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# 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.

from abc import ABC, abstractmethod
from dataclasses import dataclass
from pathlib import Path

import flask
import requests

# __all__ = ["AdapterGlobalContext", "AdapterRequestContext", "AdapterRequest", "AdapterResponse", "RequestInterceptor", "RequestToResponseInterceptor", "ResponseInterceptor"]


[docs] @dataclass class AdapterGlobalContext: """Global context passed to all interceptors containing server-level configuration. This context contains information that is available throughout the entire request-response cycle and is shared across all interceptors. """ output_dir: str # Directory for output files url: str # The upstream API URL to forward requests to model_name: str | None = None # Model name for logging context @property def metrics_path(self) -> Path: """Path to the eval_factory_metrics.json file.""" return Path(self.output_dir) / "eval_factory_metrics.json"
[docs] @dataclass class AdapterRequestContext: """This is passed with the _whole_ chain from request back to response. Add here any useful information. """ cache_hit: bool = False # Whether there was a cache hit cache_key: str | None = None # Cache key request_id: str | None = None # Request ID for identification
[docs] @dataclass class AdapterRequest: r: flask.Request rctx: AdapterRequestContext
[docs] @dataclass class AdapterResponse: r: requests.Response rctx: AdapterRequestContext latency_ms: float | None = None
[docs] class FatalErrorException(Exception): """Exception raised when an interceptor encounters a fatal error that should kill the process.""" pass
[docs] class RequestInterceptor(ABC): """Interface for providing interception of requests."""
[docs] @abstractmethod def intercept_request( self, req: AdapterRequest, context: AdapterGlobalContext, ) -> AdapterRequest: """Function that will be called by `AdapterServer` on the way upstream. This interceptor can modify the request but must return an AdapterRequest to continue the chain upstream. Args: req: The adapter request to intercept context: Global context containing server-level configuration Ex.: This is used for request preprocessing, logging, etc. """ pass
[docs] class RequestToResponseInterceptor(ABC): """Interface for interceptors that can either continue the request chain or return a response."""
[docs] @abstractmethod def intercept_request( self, req: AdapterRequest, context: AdapterGlobalContext, ) -> AdapterRequest | AdapterResponse: """Function that will be called by `AdapterServer` on the way upstream. If the return type is `AdapterRequest`, the chain will continue upstream. If the return type is `AdapterResponse`, the `AdapterServer` will consider the chain finished and start the reverse chain of responses. Args: req: The adapter request to intercept context: Global context containing server-level configuration Ex.: the latter case is e.g. how caching interceptor works. For cache miss it will continue the chain, passing request unchanged. For cache hit, it will go for the response. """ pass
[docs] class ResponseInterceptor(ABC): """Interface for providing interception of responses."""
[docs] @abstractmethod def intercept_response( self, resp: AdapterResponse, context: AdapterGlobalContext, ) -> AdapterResponse: """Function that will be called by `AdapterServer` on the way downstream. Args: resp: The adapter response to intercept context: Global context containing server-level configuration """ pass
[docs] class PostEvalHook(ABC): """Interface for post-evaluation hooks that run after the evaluation completes. Post-evaluation hooks are executed after the main evaluation has finished, allowing for cleanup, report generation, or other post-processing tasks. """
[docs] @abstractmethod def post_eval_hook(self, context: AdapterGlobalContext) -> None: """Function that will be called by the evaluation system after evaluation completes. Args: context: Global context containing server-level configuration and evaluation results Ex.: This is used for report generation, cleanup, metrics collection, etc. """ pass