From e1b678d266565523e476bd4968961990d905b7f9 Mon Sep 17 00:00:00 2001 From: Antigravity Agent Date: Tue, 27 Jan 2026 20:10:58 +0000 Subject: [PATCH 1/3] fix(settings): allow extra env vars in settings and env config --- src/regression_model_template/io/osvariables.py | 1 + src/regression_model_template/settings.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/regression_model_template/io/osvariables.py b/src/regression_model_template/io/osvariables.py index 25358be..db8e688 100644 --- a/src/regression_model_template/io/osvariables.py +++ b/src/regression_model_template/io/osvariables.py @@ -23,3 +23,4 @@ class Config: case_sensitive: bool = False # Optional: make env var lookup case-insensitive env_file: str = ".env" # Enable reading from .env file env_file_encoding: str = "utf-8" + extra = "ignore" diff --git a/src/regression_model_template/settings.py b/src/regression_model_template/settings.py index 2f80c10..9d731c6 100644 --- a/src/regression_model_template/settings.py +++ b/src/regression_model_template/settings.py @@ -10,7 +10,7 @@ # %% SETTINGS -class Settings(pdts.BaseSettings, strict=True, frozen=True, extra="forbid"): +class Settings(pdts.BaseSettings, strict=True, frozen=True, extra="ignore"): """Base class for application settings. Use settings to provide high-level preferences. From 4c1f841fb60e6659412a91a163fbe97e780f2eb2 Mon Sep 17 00:00:00 2001 From: Antigravity Agent Date: Tue, 27 Jan 2026 20:22:25 +0000 Subject: [PATCH 2/3] fix(mypy): resolve errors and remove exclusions --- pyproject.toml | 4 +--- src/regression_model_template/io/registries.py | 10 +++++----- src/regression_model_template/jobs/evaluations.py | 4 ++-- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5fe1ab3..a649c6d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -100,9 +100,7 @@ python_version = "3.12" check_untyped_defs = false ignore_missing_imports = true plugins = ["pandera.mypy", "pydantic.mypy"] -exclude = ["src/regression_model_template/jobs/evaluations.py", -"src/regression_model_template/jobs/training.py", -"src/regression_model_template/io/registries.py"] +exclude = [] [tool.pytest.ini_options] addopts = "--verbosity=2" diff --git a/src/regression_model_template/io/registries.py b/src/regression_model_template/io/registries.py index 9bf2dd3..a5aa44b 100644 --- a/src/regression_model_template/io/registries.py +++ b/src/regression_model_template/io/registries.py @@ -102,7 +102,7 @@ class CustomSaver(Saver): KIND: T.Literal["CustomSaver"] = "CustomSaver" - class Adapter(mlflow.pyfunc.PythonModel): # type: ignore[misc] + class Adapter(mlflow.pyfunc.PythonModel): # type: ignore[misc, name-defined] """Adapt a custom model to the Mlflow PyFunc flavor for saving operations. https://mlflow.org/docs/latest/python_api/mlflow.pyfunc.html?#mlflow.pyfunc.PythonModel @@ -118,7 +118,7 @@ def __init__(self, model: models.Model): def predict( self, - context: mlflow.pyfunc.PythonModelContext, + context: mlflow.pyfunc.PythonModelContext, # type: ignore[name-defined] model_input: schemas.Inputs, params: dict[str, T.Any] | None = None, ) -> schemas.Outputs: @@ -163,7 +163,7 @@ def save( model: models.Model, signature: signers.Signature, input_example: schemas.Inputs | None = None, - ) -> mlflow.entities.model_registry.ModelVersion: + ) -> Info: builtin_model = model.get_internal_model() module = getattr(mlflow, self.flavor) return module.log_model( @@ -222,7 +222,7 @@ class CustomLoader(Loader): class Adapter(Loader.Adapter): """Adapt a custom model for the project inference.""" - def __init__(self, model: mlflow.pyfunc.PyFuncModel) -> None: + def __init__(self, model: mlflow.pyfunc.PyFuncModel) -> None: # type: ignore[name-defined] """Initialize the adapter from an mlflow pyfunc model. Args: @@ -254,7 +254,7 @@ class BuiltinLoader(Loader): class Adapter(Loader.Adapter): """Adapt a builtin model for the project inference.""" - def __init__(self, model: mlflow.pyfunc.PyFuncModel) -> None: + def __init__(self, model: mlflow.pyfunc.PyFuncModel) -> None: # type: ignore[name-defined] """Initialize the adapter from an mlflow pyfunc model. Args: diff --git a/src/regression_model_template/jobs/evaluations.py b/src/regression_model_template/jobs/evaluations.py index 510adce..eedf062 100644 --- a/src/regression_model_template/jobs/evaluations.py +++ b/src/regression_model_template/jobs/evaluations.py @@ -81,10 +81,10 @@ def run(self) -> base.Locals: logger.debug("- Targets lineage: {}", targets_lineage.to_dict()) # dataset logger.info("Create dataset: inputs & targets") - dataset = mlflow.data.from_pandas( + dataset = mlflow.data.from_pandas( # type: ignore[attr-defined] df=pd.concat([inputs, targets], axis="columns"), name="evaluation", - source=f"{inputs_lineage.source.uri} & {targets_lineage.source.uri}", + source=f"{inputs_lineage.source.uri} & {targets_lineage.source.uri}", # type: ignore[attr-defined] targets=schemas.TargetsSchema.cnt, ) logger.debug("- Dataset: {}", dataset.to_dict()) From 8d507da5c1d5a62deef3ceeae9dd7369921dd0c2 Mon Sep 17 00:00:00 2001 From: Antigravity Agent Date: Tue, 27 Jan 2026 20:54:39 +0000 Subject: [PATCH 3/3] fix(typing): achieve full strict mypy compliance and resolve naming conflicts --- pyproject.toml | 4 ++-- .../controller/kafka_app.py | 18 +++++++++--------- src/regression_model_template/core/metrics.py | 8 ++++++-- src/regression_model_template/io/registries.py | 4 ++-- .../jobs/evaluations.py | 2 +- 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a649c6d..47eda53 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -95,9 +95,9 @@ omit = ["__main__.py"] [tool.mypy] pretty = true -strict = false +strict = true python_version = "3.12" -check_untyped_defs = false +check_untyped_defs = true ignore_missing_imports = true plugins = ["pandera.mypy", "pydantic.mypy"] exclude = [] diff --git a/src/regression_model_template/controller/kafka_app.py b/src/regression_model_template/controller/kafka_app.py index 80fe705..0f17f58 100644 --- a/src/regression_model_template/controller/kafka_app.py +++ b/src/regression_model_template/controller/kafka_app.py @@ -13,7 +13,7 @@ from fastapi import FastAPI, HTTPException from pydantic import BaseModel -from confluent_kafka import Producer, Consumer, KafkaError +from confluent_kafka import Producer, Consumer, KafkaError, Message from regression_model_template.core.schemas import InputsSchema, Outputs from regression_model_template.io import services, registries @@ -65,9 +65,9 @@ class PredictionRequest(BaseModel): "registered": [0, 50, 100, 150], } - def model_validate(self): + def validate_schema(self) -> pd.DataFrame: """Validates the input data against InputsSchema.""" - return InputsSchema.validate(pd.DataFrame([self.input_data])) + return InputsSchema.validate(pd.DataFrame([self.input_data])) # type: ignore[return-value] class PredictionResponse(BaseModel): @@ -96,7 +96,7 @@ def __init__( self.producer: Producer | None = None self.consumer: Consumer | None = None - def delivery_report(self, err, msg): + def delivery_report(self, err: KafkaError | None, msg: Message) -> None: """Called once for each message produced to indicate delivery result.""" if err is not None: logger.error(f"Message delivery failed: {err}") @@ -155,7 +155,7 @@ def _consume_messages(self) -> None: self._process_message(msg) self._close_consumer() - def _poll_message(self): + def _poll_message(self) -> Message | None: """Poll message from Kafka consumer.""" if self.consumer: return self.consumer.poll(1.0) @@ -163,7 +163,7 @@ def _poll_message(self): logger.error("Kafka consumer is not initialized.") return None - def _handle_message_error(self, msg) -> bool: + def _handle_message_error(self, msg: Message) -> bool: """Handle errors in polled messages.""" if msg.error().code() == KafkaError._PARTITION_EOF: logger.debug("Reached end of partition.") @@ -172,7 +172,7 @@ def _handle_message_error(self, msg) -> bool: logger.error(f"Consumer error: {msg.error()}") return False - def _process_message(self, msg) -> None: + def _process_message(self, msg: Message) -> None: """Process a valid Kafka message.""" predictionresponse: PredictionResponse = PredictionResponse() try: @@ -251,12 +251,12 @@ async def predict(request: PredictionRequest) -> PredictionResponse: # Use glob @app.get("/health", summary="Health Check", tags=["System"]) -async def health_check(): +async def health_check() -> dict[str, str]: """Simple health check endpoint to verify that the service is running.""" return {"status": "healthy"} -def main(): +def main() -> None: global fastapi_kafka_service # Configuration alias_or_version: str | int = "Champion" diff --git a/src/regression_model_template/core/metrics.py b/src/regression_model_template/core/metrics.py index 31c6c09..c5fcca6 100644 --- a/src/regression_model_template/core/metrics.py +++ b/src/regression_model_template/core/metrics.py @@ -90,7 +90,9 @@ def eval_fn(predictions: pd.Series[int], targets: pd.Series[int]) -> MlflowMetri score = self.score(targets=score_targets, outputs=score_outputs) return MlflowMetric(aggregate_results={self.name: score * sign}) - return mlflow.metrics.make_metric(eval_fn=eval_fn, name=self.name, greater_is_better=self.greater_is_better) + return mlflow.metrics.make_metric( # type: ignore[no-any-return] + eval_fn=eval_fn, name=self.name, greater_is_better=self.greater_is_better + ) class SklearnMetric(Metric): @@ -141,4 +143,6 @@ def to_mlflow(self) -> MlflowThreshold: Returns: MlflowThreshold: the mlflow threshold. """ - return MlflowThreshold(threshold=self.threshold, greater_is_better=self.greater_is_better) + return MlflowThreshold( # type: ignore[no-untyped-call] + threshold=self.threshold, greater_is_better=self.greater_is_better + ) diff --git a/src/regression_model_template/io/registries.py b/src/regression_model_template/io/registries.py index a5aa44b..bc3fcc0 100644 --- a/src/regression_model_template/io/registries.py +++ b/src/regression_model_template/io/registries.py @@ -137,7 +137,7 @@ def predict( def save(self, model: models.Model, signature: signers.Signature, input_example: schemas.Inputs) -> Info: adapter = CustomSaver.Adapter(model=model) - return mlflow.pyfunc.log_model( + return mlflow.pyfunc.log_model( # type: ignore[no-any-return] python_model=adapter, signature=signature, artifact_path=self.path, @@ -166,7 +166,7 @@ def save( ) -> Info: builtin_model = model.get_internal_model() module = getattr(mlflow, self.flavor) - return module.log_model( + return module.log_model( # type: ignore[no-any-return] builtin_model, artifact_path=self.path, signature=signature, input_example=input_example ) diff --git a/src/regression_model_template/jobs/evaluations.py b/src/regression_model_template/jobs/evaluations.py index eedf062..8423486 100644 --- a/src/regression_model_template/jobs/evaluations.py +++ b/src/regression_model_template/jobs/evaluations.py @@ -104,7 +104,7 @@ def run(self) -> base.Locals: logger.debug("- Validation thresholds: {}", validation_thresholds) # evaluations logger.info("Compute evaluations: {}", self.model_type) - evaluations = mlflow.evaluate( + evaluations = mlflow.evaluate( # type: ignore[no-untyped-call] data=dataset, model=model_uri, model_type=self.model_type,