Skip to content

feat: Add LanguageModelMixin and update LanguageModelComponent to use it#10858

Closed
ogabrielluiz wants to merge 3 commits into
mainfrom
feat/llm-mixin
Closed

feat: Add LanguageModelMixin and update LanguageModelComponent to use it#10858
ogabrielluiz wants to merge 3 commits into
mainfrom
feat/llm-mixin

Conversation

@ogabrielluiz
Copy link
Copy Markdown
Contributor

@ogabrielluiz ogabrielluiz commented Dec 3, 2025

Introduce LanguageModelMixin to facilitate dynamic integration of LLM providers, simplify the LanguageModelComponent by removing unused code, and add unit tests to ensure functionality.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added language model mixin enabling dynamic provider and model configuration across supported LLM services
    • Implemented dynamic field visibility for provider-specific settings
    • Enhanced model fetching and validation with improved error handling
  • Tests

    • Comprehensive test coverage for provider configuration, model building, and dynamic UI behavior across all supported providers

✏️ Tip: You can customize this high-level summary in your review settings.

@github-actions github-actions Bot added the community Pull Request from an external contributor label Dec 3, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 3, 2025

Walkthrough

This PR extracts language model provider handling logic (input generation, model fetching, LLM instantiation, dynamic configuration) from LanguageModelComponent into a new reusable LanguageModelMixin, consolidating multi-provider support (OpenAI, Anthropic, Google, IBM watsonx.ai, Ollama) while maintaining functional equivalence.

Changes

Cohort / File(s) Summary
Base Models Exports
src/lfx/src/lfx/base/models/__init__.py
Added import and public export of LanguageModelMixin alongside LCModelComponent.
Language Model Mixin Implementation
src/lfx/src/lfx/base/models/language_model_mixin.py
New mixin class providing provider-aware LLM input generation (get_llm_inputs), model fetching for IBM watsonx (fetch_ibm_models), provider-specific LLM instantiation (build_llm with per-provider validation and configuration for OpenAI, Anthropic, Google, IBM, Ollama), and dynamic UI field management (update_llm_provider_config) with Ollama URL normalization and runtime field visibility toggling.
Component Refactoring
src/lfx/src/lfx/components/models_and_agents/language_model.py
Refactored LanguageModelComponent to inherit from LanguageModelMixin alongside LCModelComponent, replacing inline provider-specific input definitions, model-fetching logic, and build/configuration methods with mixin delegations; removed explicit fetch_ibm_models, provider constants, and provider-branching code paths.
Comprehensive Test Suite
src/lfx/tests/unit/base/models/test_language_model_mixin.py
New test module with four test classes covering LLM input assembly (default and optional fields), provider/model dropdown configurations, IBM watsonx and Ollama field visibility rules, mixin integration with Component, provider-specific error handling (missing credentials per provider), provider switching behavior, o1-model system_message visibility, and LanguageModelComponent integration validation. Includes helper functions to detect installed LangChain backends.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Multi-provider branching logic: Mixin contains provider-specific validation, credential handling, and LLM instantiation paths across five providers (OpenAI, Anthropic, Google, IBM, Ollama) that require careful verification for correctness and error messaging.
  • Dynamic UI field management: The update_llm_provider_config async method adjusts field visibility and fetches model options conditionally per provider; verify state transitions and side-effect ordering.
  • Lazy imports and dependency minimization: Review guarded imports to confirm they do not introduce circular dependencies or unexpected import-time side effects.
  • Credential and API key handling: Ensure each provider's credential validation, URL normalization (especially Ollama OpenAI-compatibility suffix), and error fallbacks are sound.
  • Test coverage depth: Verify test assertions correctly validate provider-specific error conditions, dynamic field toggles, and integration with LanguageModelComponent's delegation to build_llm.

Possibly related PRs

Suggested labels

enhancement, lgtm

Suggested reviewers

  • lucaseduoli
  • erichare
  • HzaRashid

Pre-merge checks and finishing touches

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 3 warnings)
Check name Status Explanation Resolution
Test Coverage For New Implementations ❌ Error Test assertion assert comp.build_model == comp.build_llm or callable(comp.build_model) is always True and does not verify delegation; fetch_ibm_models() lacks unit test coverage. Use unittest.mock.patch to verify build_model() calls build_llm() and add dedicated unit test for fetch_ibm_models() static method.
Docstring Coverage ⚠️ Warning Docstring coverage is 79.41% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Test Quality And Coverage ⚠️ Warning Test suite lacks coverage for fetch_ibm_models static method, async method testing patterns, and weak assertions for delegation verification. Add tests for fetch_ibm_models with mocked HTTP responses, use @pytest.mark.asyncio for async methods, replace weak assertions with proper mock-based behavior verification, and expand error handling coverage for all provider-specific validation paths.
Test File Naming And Structure ⚠️ Warning Test file follows proper backend naming (test_*.py), pytest structure, has 4 well-organized test classes with 21 descriptive methods covering positive/negative scenarios and edge cases with proper fixtures. Fix weak assertion at line 366 in test_language_model_component_uses_mixin by using unittest.mock.patch to verify delegation or implement explicit runtime verification that build_model properly calls build_llm.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: introducing LanguageModelMixin and updating LanguageModelComponent to use it, which aligns with the core objective of the PR.
Excessive Mock Usage Warning ✅ Passed Test suite demonstrates exemplary design with zero mock usage. All 21 test methods instantiate real LanguageModelMixin objects and Component subclasses, validating actual behavior rather than mocking interactions.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/llm-mixin

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Dec 3, 2025
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/lfx/src/lfx/base/models/language_model_mixin.py (2)

85-98: Synchronous HTTP call may block when invoked from async context.

fetch_ibm_models uses synchronous requests.get, but it's called from the async update_llm_provider_config method (line 454). While the fallback behavior is resilient, the blocking call could delay other async tasks.

Consider using httpx.AsyncClient (already used for Ollama in model_utils.py) or wrapping in asyncio.to_thread for consistency with the async pattern.


298-299: Redundant SecretStr wrapping and unwrapping.

The expression SecretStr(api_key).get_secret_value() wraps api_key in a SecretStr only to immediately extract the secret value. Since api_key is already a string at this point, you can pass it directly:

             return ChatWatsonx(
-                apikey=SecretStr(api_key).get_secret_value(),
+                apikey=api_key,
                 url=base_url_ibm,
src/lfx/tests/unit/base/models/test_language_model_mixin.py (1)

96-139: Consider consolidating import-check helpers.

The five _has_langchain_* functions follow an identical pattern. While the current approach is explicit, you could reduce duplication:

def _has_package(package_name: str) -> bool:
    import importlib
    try:
        importlib.import_module(package_name)
        return True
    except ImportError:
        return False

# Usage: pytest.mark.skipif(not _has_package("langchain_openai"), ...)

This is a minor nit—the current explicit approach works fine.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 613f682 and f855564.

📒 Files selected for processing (4)
  • src/lfx/src/lfx/base/models/__init__.py (1 hunks)
  • src/lfx/src/lfx/base/models/language_model_mixin.py (1 hunks)
  • src/lfx/src/lfx/components/models_and_agents/language_model.py (2 hunks)
  • src/lfx/tests/unit/base/models/test_language_model_mixin.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/test_*.py

📄 CodeRabbit inference engine (Custom checks)

**/test_*.py: Review test files for excessive use of mocks that may indicate poor test design - check if tests have too many mock objects that obscure what's actually being tested
Warn when mocks are used instead of testing real behavior and interactions, and suggest using real objects or test doubles when mocks become excessive
Ensure mocks are used appropriately for external dependencies only, not for core logic
Backend test files should follow the naming convention test_*.py with proper pytest structure
Test files should have descriptive test function names that explain what is being tested
Tests should be organized logically with proper setup and teardown
Consider including edge cases and error conditions for comprehensive test coverage
Verify tests cover both positive and negative scenarios where appropriate
For async functions in backend tests, ensure proper async testing patterns are used with pytest
For API endpoints, verify both success and error response testing

Files:

  • src/lfx/tests/unit/base/models/test_language_model_mixin.py
🧠 Learnings (5)
📚 Learning: 2025-11-24T19:46:09.104Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/backend_development.mdc:0-0
Timestamp: 2025-11-24T19:46:09.104Z
Learning: Applies to src/backend/base/langflow/components/**/__init__.py : Update `__init__.py` with alphabetically sorted imports when adding new components

Applied to files:

  • src/lfx/src/lfx/base/models/__init__.py
📚 Learning: 2025-11-24T19:46:09.104Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/backend_development.mdc:0-0
Timestamp: 2025-11-24T19:46:09.104Z
Learning: Applies to src/backend/base/langflow/components/**/*.py : Add new components to the appropriate subdirectory under `src/backend/base/langflow/components/` (agents/, data/, embeddings/, input_output/, models/, processing/, prompts/, tools/, or vectorstores/)

Applied to files:

  • src/lfx/src/lfx/components/models_and_agents/language_model.py
  • src/lfx/tests/unit/base/models/test_language_model_mixin.py
📚 Learning: 2025-11-24T19:47:28.997Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.997Z
Learning: Applies to src/backend/tests/**/*.py : Use `pytest.mark.api_key_required` and `pytest.mark.no_blockbuster` markers for components that need external APIs; use `MockLanguageModel` from `tests.unit.mock_language_model` for testing without external API keys

Applied to files:

  • src/lfx/tests/unit/base/models/test_language_model_mixin.py
📚 Learning: 2025-11-24T19:47:28.997Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.997Z
Learning: Applies to src/backend/tests/**/*.py : Test component versioning and backward compatibility using `file_names_mapping` fixture with `VersionComponentMapping` objects mapping component files across Langflow versions

Applied to files:

  • src/lfx/tests/unit/base/models/test_language_model_mixin.py
📚 Learning: 2025-08-05T22:51:27.961Z
Learnt from: edwinjosechittilappilly
Repo: langflow-ai/langflow PR: 0
File: :0-0
Timestamp: 2025-08-05T22:51:27.961Z
Learning: The TestComposioComponentAuth test in src/backend/tests/unit/components/bundles/composio/test_base_composio.py demonstrates proper integration testing patterns for external API components, including real API calls with mocking for OAuth completion, comprehensive resource cleanup, and proper environment variable handling with pytest.skip() fallbacks.

Applied to files:

  • src/lfx/tests/unit/base/models/test_language_model_mixin.py
🧬 Code graph analysis (3)
src/lfx/src/lfx/base/models/__init__.py (1)
src/lfx/src/lfx/base/models/language_model_mixin.py (1)
  • LanguageModelMixin (76-519)
src/lfx/src/lfx/base/models/language_model_mixin.py (2)
src/lfx/src/lfx/base/models/model_utils.py (1)
  • get_ollama_models (39-108)
src/lfx/src/lfx/utils/util.py (1)
  • transform_localhost_url (119-161)
src/lfx/tests/unit/base/models/test_language_model_mixin.py (1)
src/lfx/src/lfx/components/models_and_agents/language_model.py (1)
  • LanguageModelComponent (10-44)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Update Starter Projects
  • GitHub Check: Run Ruff Check and Format
🔇 Additional comments (9)
src/lfx/src/lfx/base/models/__init__.py (1)

1-4: LGTM!

Imports are alphabetically sorted and the new LanguageModelMixin is properly exported alongside LCModelComponent.

src/lfx/src/lfx/base/models/language_model_mixin.py (4)

1-46: Well-documented module with clear usage examples.

The docstring provides helpful guidance on how to use the mixin, including code examples for input composition and update_build_config integration.


47-74: LGTM!

Constants are well-organized and properly grouped by provider.


100-218: LGTM!

Clean API design with keyword-only arguments and sensible defaults. The conditional inclusion of optional inputs provides good flexibility for different use cases.


343-518: LGTM!

The dynamic configuration logic handles provider switching comprehensively with appropriate fallback behavior when model fetching fails. The o1 model system_message visibility toggle is a nice touch for OpenAI's reasoning model constraints.

src/lfx/src/lfx/components/models_and_agents/language_model.py (1)

10-44: LGTM!

Clean refactoring that delegates provider logic to the mixin while maintaining explicit control over the input_value and system_message field placement. The separation of concerns is well-executed.

src/lfx/tests/unit/base/models/test_language_model_mixin.py (3)

15-94: LGTM!

Tests thoroughly validate input configuration defaults and optional field inclusion without relying on mocks.


141-261: LGTM!

Comprehensive coverage of validation error cases for each provider. The use of pytest.mark.skipif for optional dependencies follows best practices from the testing guidelines.


375-385: LGTM!

Good integration test verifying input ordering expectations for the component.

Comment on lines +365 to +366
# Check that build_model delegates to build_llm
assert comp.build_model == comp.build_llm or callable(comp.build_model)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Assertion does not verify delegation.

The condition comp.build_model == comp.build_llm or callable(comp.build_model) is always true because any method is callable. This doesn't actually verify that build_model delegates to build_llm.

Consider testing the actual delegation behavior:

         # Check that build_model delegates to build_llm
-        assert comp.build_model == comp.build_llm or callable(comp.build_model)
+        # Verify they're both methods (minimal check since actual call requires API keys)
+        assert callable(comp.build_model)
+        assert callable(comp.build_llm)
+        # The implementation of build_model should call build_llm
+        # This is verified by code inspection: build_model returns self.build_llm()

Alternatively, you could use unittest.mock.patch to verify the call if stronger verification is needed.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Check that build_model delegates to build_llm
assert comp.build_model == comp.build_llm or callable(comp.build_model)
# Check that build_model delegates to build_llm
# Verify they're both methods (minimal check since actual call requires API keys)
assert callable(comp.build_model)
assert callable(comp.build_llm)
# The implementation of build_model should call build_llm
# This is verified by code inspection: build_model returns self.build_llm()
🤖 Prompt for AI Agents
In src/lfx/tests/unit/base/models/test_language_model_mixin.py around lines
365-366, the assertion `comp.build_model == comp.build_llm or
callable(comp.build_model)` is ineffective because methods are always callable
and doesn't verify delegation; replace it with a test that patches/mocks
comp.build_llm (e.g., with unittest.mock.patch.object or a monkeypatch) to
assert that calling comp.build_model(...) invokes comp.build_llm(...) with the
same arguments and returns its result (or alternatively directly assert
comp.build_model is comp.build_llm when delegation is implemented by aliasing);
ensure the mock verifies the call count and arguments to confirm actual
delegation.

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

Labels

community Pull Request from an external contributor enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant