Skip to content

🧹 Refactor architecture: purify models layer and move validation to service layer - #89

Merged
chottokun merged 2 commits into
mainfrom
refactor/pure-ml-models-and-service-validation-3909149081493120938
Sep 12, 2026
Merged

chottokun merged 2 commits into
mainfrom
refactor/pure-ml-models-and-service-validation-3909149081493120938

Conversation

@chottokun

Copy link
Copy Markdown
Owner

What

  • Purified src/app/models.py by removing all fastapi imports (HTTPException) and dynamic reflection code (import app.main), turning it into a pure ML infrastructure layer.
  • Centralized model availability validation and HTTP 400 error handling in get_validated_model within src/app/services/base.py.
  • Updated EmbeddingService and RerankService to utilize get_validated_model with support for optional model_loader dependency injection.
  • Updated src/app/main.py to delegate _get_model_or_400 calls to get_validated_model, maintaining 100% backwards compatibility for existing unit tests.

Why

  • Fixes misplaced responsibilities where ML model cache/infrastructure layer was directly performing Web framework response validations.
  • Cleans up circular/dynamic imports and coupling between models.py and main.py.

Verification

  • Confirmed src/app/models.py has no references to fastapi or app.main.
  • Executed uv run ruff check src and uv run ruff format --check src with 0 errors.
  • Executed uv run pytest - all 149 test cases passed without modifying any test files.

PR created automatically by Jules for task 3909149081493120938 started by @chottokun

…d move validation to service layer

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@chottokun

Copy link
Copy Markdown
Owner Author

@jules
Thank you for this refactoring! Purifying by removing FastAPI and HTTP dependencies is a great architectural improvement.

However, looking at , dynamic reflection and circular dependency still remain:

    if loader is None:
        try:
            import app.main as main_mod

            fetch_func = getattr(main_mod, "get_model", get_model)
        except Exception:
            fetch_func = get_model
    else:
        fetch_func = loader

We can eliminate this reflection entirely by properly wiring Dependency Injection:

  1. In :
def get_validated_model(
    model_name: str,
    allowed_models: Collection[str],
    service_name: str,
    loader: Optional[Callable[[str], Any]] = None,
) -> Any:
    if model_name not in allowed_models:
        suffix = "s" if not service_name.endswith("s") else ""
        raise HTTPException(
            status_code=400,
            detail=f"Model '{model_name}' not found for {service_name}{suffix}.",
        )

    fetch_func = loader or get_model

    try:
        return fetch_func(model_name)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
  1. In :
    Pass in the DI providers and in :
def _get_model_or_400(model_name: str, model_type: str) -> Any:
    """
    Helper for backwards compatibility with legacy tests calling _get_model_or_400.
    """
    supported_models = EMBEDDING_MODELS if model_type == "embedding" else RERANK_MODELS
    return get_validated_model(
        model_name, supported_models, model_type, loader=get_model
    )


# Dependency Injection Providers
def get_embedding_service() -> BaseEmbeddingService:
    return EmbeddingService(proxy_to_tei_func=_proxy_to_tei, model_loader=get_model)


def get_rerank_service() -> BaseRerankService:
    return RerankService(proxy_to_tei_func=_proxy_to_tei, model_loader=get_model)

This cleanly satisfies all 149 existing unit tests (including tests patching app.main.get_model) while completely eliminating circular imports and dynamic import app.main in the service layer.

Could you please update the PR with these changes?

@chottokun

Copy link
Copy Markdown
Owner Author

@jules
Thank you for this refactoring! Purifying src/app/models.py by removing FastAPI and HTTP dependencies is a great architectural improvement.

However, in src/app/services/base.py, dynamic reflection and circular dependency still remain:

    if loader is None:
        try:
            import app.main as main_mod

            fetch_func = getattr(main_mod, "get_model", get_model)
        except Exception:
            fetch_func = get_model
    else:
        fetch_func = loader

We can eliminate this reflection entirely by properly wiring Dependency Injection:

  1. In src/app/services/base.py:
def get_validated_model(
    model_name: str,
    allowed_models: Collection[str],
    service_name: str,
    loader: Optional[Callable[[str], Any]] = None,
) -> Any:
    if model_name not in allowed_models:
        suffix = "s" if not service_name.endswith("s") else ""
        raise HTTPException(
            status_code=400,
            detail=f"Model '{model_name}' not found for {service_name}{suffix}.",
        )

    fetch_func = loader or get_model

    try:
        return fetch_func(model_name)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
  1. In src/app/main.py:
    Pass model_loader=get_model in the DI providers and loader=get_model in _get_model_or_400:
def _get_model_or_400(model_name: str, model_type: str) -> Any:
    """
    Helper for backwards compatibility with legacy tests calling _get_model_or_400.
    """
    supported_models = EMBEDDING_MODELS if model_type == "embedding" else RERANK_MODELS
    return get_validated_model(
        model_name, supported_models, model_type, loader=get_model
    )


# Dependency Injection Providers
def get_embedding_service() -> BaseEmbeddingService:
    return EmbeddingService(proxy_to_tei_func=_proxy_to_tei, model_loader=get_model)


def get_rerank_service() -> BaseRerankService:
    return RerankService(proxy_to_tei_func=_proxy_to_tei, model_loader=get_model)

This cleanly satisfies all 149 existing unit tests (including tests patching app.main.get_model) while completely eliminating circular imports and dynamic import app.main in the service layer.

Could you please update the PR with these changes?

@google-labs-jules

Copy link
Copy Markdown
Contributor

@jules
Thank you for this refactoring! Purifying src/app/models.py by removing FastAPI and HTTP dependencies is a great architectural improvement.

However, in src/app/services/base.py, dynamic reflection and circular dependency still remain:

    if loader is None:
        try:
            import app.main as main_mod

            fetch_func = getattr(main_mod, "get_model", get_model)
        except Exception:
            fetch_func = get_model
    else:
        fetch_func = loader

We can eliminate this reflection entirely by properly wiring Dependency Injection:

  1. In src/app/services/base.py:
def get_validated_model(
    model_name: str,
    allowed_models: Collection[str],
    service_name: str,
    loader: Optional[Callable[[str], Any]] = None,
) -> Any:
    if model_name not in allowed_models:
        suffix = "s" if not service_name.endswith("s") else ""
        raise HTTPException(
            status_code=400,
            detail=f"Model '{model_name}' not found for {service_name}{suffix}.",
        )

    fetch_func = loader or get_model

    try:
        return fetch_func(model_name)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
  1. In src/app/main.py:
    Pass model_loader=get_model in the DI providers and loader=get_model in _get_model_or_400:
def _get_model_or_400(model_name: str, model_type: str) -> Any:
    """
    Helper for backwards compatibility with legacy tests calling _get_model_or_400.
    """
    supported_models = EMBEDDING_MODELS if model_type == "embedding" else RERANK_MODELS
    return get_validated_model(
        model_name, supported_models, model_type, loader=get_model
    )


# Dependency Injection Providers
def get_embedding_service() -> BaseEmbeddingService:
    return EmbeddingService(proxy_to_tei_func=_proxy_to_tei, model_loader=get_model)


def get_rerank_service() -> BaseRerankService:
    return RerankService(proxy_to_tei_func=_proxy_to_tei, model_loader=get_model)

This cleanly satisfies all 149 existing unit tests (including tests patching app.main.get_model) while completely eliminating circular imports and dynamic import app.main in the service layer.

Could you please update the PR with these changes?

Thank you for the detailed feedback! I have updated get_validated_model in src/app/services/base.py to use fetch_func = loader or get_model, completely removing dynamic reflection (import app.main). In src/app/main.py, model_loader=get_model is now explicitly passed in DI providers (get_embedding_service and get_rerank_service) and _get_model_or_400. All 149 tests continue to pass.

@google-labs-jules

Copy link
Copy Markdown
Contributor

@jules
Thank you for this refactoring! Purifying by removing FastAPI and HTTP dependencies is a great architectural improvement.

However, looking at , dynamic reflection and circular dependency still remain:

    if loader is None:
        try:
            import app.main as main_mod

            fetch_func = getattr(main_mod, "get_model", get_model)
        except Exception:
            fetch_func = get_model
    else:
        fetch_func = loader

We can eliminate this reflection entirely by properly wiring Dependency Injection:

  1. In :
def get_validated_model(
    model_name: str,
    allowed_models: Collection[str],
    service_name: str,
    loader: Optional[Callable[[str], Any]] = None,
) -> Any:
    if model_name not in allowed_models:
        suffix = "s" if not service_name.endswith("s") else ""
        raise HTTPException(
            status_code=400,
            detail=f"Model '{model_name}' not found for {service_name}{suffix}.",
        )

    fetch_func = loader or get_model

    try:
        return fetch_func(model_name)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
  1. In :
    Pass in the DI providers and in :
def _get_model_or_400(model_name: str, model_type: str) -> Any:
    """
    Helper for backwards compatibility with legacy tests calling _get_model_or_400.
    """
    supported_models = EMBEDDING_MODELS if model_type == "embedding" else RERANK_MODELS
    return get_validated_model(
        model_name, supported_models, model_type, loader=get_model
    )


# Dependency Injection Providers
def get_embedding_service() -> BaseEmbeddingService:
    return EmbeddingService(proxy_to_tei_func=_proxy_to_tei, model_loader=get_model)


def get_rerank_service() -> BaseRerankService:
    return RerankService(proxy_to_tei_func=_proxy_to_tei, model_loader=get_model)

This cleanly satisfies all 149 existing unit tests (including tests patching app.main.get_model) while completely eliminating circular imports and dynamic import app.main in the service layer.

Could you please update the PR with these changes?

Thank you for the suggestion! I have removed import app.main from src/app/services/base.py and properly wired Dependency Injection by passing model_loader=get_model in src/app/main.py. All 149 unit tests pass cleanly.

…er via dependency injection

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
@chottokun

Copy link
Copy Markdown
Owner Author

LGTM! The dynamic reflection has been cleanly replaced with standard Dependency Injection, models layer is purified, and all tests and CI checks pass. Merging.

@chottokun
chottokun merged commit ed81425 into main Sep 12, 2026
1 check passed
@chottokun
chottokun deleted the refactor/pure-ml-models-and-service-validation-3909149081493120938 branch September 12, 2026 08:19
@google-labs-jules

Copy link
Copy Markdown
Contributor

LGTM! The dynamic reflection has been cleanly replaced with standard Dependency Injection, models layer is purified, and all tests and CI checks pass. Merging.

Thank you! Acknowledged and glad to hear the architectural refactoring met all requirements.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant