Repository navigation
chore: allow cli to pick model from model list #1114
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from 1 commit
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,168 @@ | ||
| import tempfile | ||
| from unittest.mock import MagicMock, patch | ||
|
|
||
| import pytest | ||
| import yaml | ||
| from pydantic import SecretStr | ||
|
|
||
| from holmes.config import Config | ||
| from holmes.core.llm import LLMModelRegistry, ModelEntry | ||
| from holmes.core.supabase_dal import SupabaseDal | ||
|
|
||
|
|
||
| class TestLLMModelRegistry: | ||
| """Test cases for LLMModelRegistry class focusing on _init_models method.""" | ||
|
|
||
| def setup_method(self): | ||
| """Set up test fixtures.""" | ||
| self.mock_dal = MagicMock(spec=SupabaseDal) | ||
| self.mock_dal.enabled = True | ||
| self.mock_dal.account_id = "test-account" | ||
|
coderabbitai[bot] marked this conversation as resolved.
Resolved
|
||
|
|
||
| def create_temp_model_file(self, model_data: dict) -> str: | ||
| """Create a temporary model list file for testing.""" | ||
| temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) | ||
| yaml.dump(model_data, temp_file) | ||
| temp_file.flush() | ||
| return temp_file.name | ||
|
|
||
| def test_init_models_existing_model_with_api_key_overwrites(self): | ||
| """Test that existing model entry is overwritten when config.api_key is set.""" | ||
| # Create a model file with existing model | ||
| model_data = { | ||
| "test-model": { | ||
| "model": "gpt-4", | ||
| "api_key": "original-key", | ||
| "api_base": "https://original.api.com", | ||
| "api_version": "2023-01-01" | ||
| } | ||
| } | ||
| temp_file = self.create_temp_model_file(model_data) | ||
|
|
||
| with patch("holmes.core.llm.MODEL_LIST_FILE_LOCATION", temp_file), \ | ||
| patch.object(LLMModelRegistry, "_should_load_robusta_ai", return_value=False): | ||
|
|
||
| # Create config with api_key set - this should trigger overwrite | ||
| config = Config( | ||
| model="test-model", | ||
| api_key="new-key", | ||
| api_base="https://new.api.com", | ||
| api_version="2024-01-01" | ||
| ) | ||
|
|
||
| registry = LLMModelRegistry(config, self.mock_dal) | ||
|
|
||
| # Check that model entry was completely overwritten with new values | ||
| model_entry = registry.models["test-model"] | ||
| assert model_entry.model == "test-model" | ||
| assert model_entry.name == "test-model" | ||
| assert model_entry.api_key == SecretStr("new-key") | ||
| assert model_entry.base_url == "https://new.api.com" | ||
| assert model_entry.api_version == "2024-01-01" | ||
| assert model_entry.is_robusta_model is False | ||
|
|
||
| def test_init_models_existing_model_without_api_key_preserves(self): | ||
| """Test that existing model entry is preserved when config.api_key is not set.""" | ||
| # Create a model file with existing model | ||
| model_data = { | ||
| "test-model": { | ||
| "model": "gpt-4", | ||
| "api_key": "original-key", | ||
| "api_base": "https://original.api.com", | ||
| "api_version": "2023-01-01", | ||
| "is_robusta_model": True | ||
| } | ||
| } | ||
| temp_file = self.create_temp_model_file(model_data) | ||
|
|
||
| with patch("holmes.core.llm.MODEL_LIST_FILE_LOCATION", temp_file), \ | ||
| patch.object(LLMModelRegistry, "_should_load_robusta_ai", return_value=False): | ||
|
|
||
| # Create config without api_key - this should preserve existing model | ||
| config = Config( | ||
| model="test-model", | ||
| api_key=None, # No API key set | ||
| api_base="https://new.api.com", # These won't be used | ||
| api_version="2024-01-01" # These won't be used | ||
| ) | ||
|
|
||
| registry = LLMModelRegistry(config, self.mock_dal) | ||
|
|
||
| # Check that original model entry was preserved | ||
| model_entry = registry.models["test-model"] | ||
| assert model_entry.model == "gpt-4" # Original value | ||
| assert model_entry.api_key == SecretStr("original-key") # Original value | ||
| assert model_entry.api_base == "https://original.api.com" # Original value | ||
| assert model_entry.api_version == "2023-01-01" # Original value | ||
| assert model_entry.is_robusta_model is True # Original value | ||
|
|
||
| def test_init_models_nonexistent_model_creates_new_entry(self): | ||
| """Test that new model entry is created when model doesn't exist in file.""" | ||
| # Create a model file without the target model | ||
| model_data = { | ||
| "other-model": { | ||
| "model": "gpt-3.5", | ||
| "api_key": "other-key" | ||
| } | ||
| } | ||
| temp_file = self.create_temp_model_file(model_data) | ||
|
|
||
| with patch("holmes.core.llm.MODEL_LIST_FILE_LOCATION", temp_file), \ | ||
| patch.object(LLMModelRegistry, "_should_load_robusta_ai", return_value=False): | ||
|
|
||
| # Create config for a model that doesn't exist in file | ||
| config = Config( | ||
| model="new-model", | ||
| api_base="https://new.api.com", | ||
| api_key="new-key", | ||
| api_version="2024-01-01" | ||
| ) | ||
|
|
||
| registry = LLMModelRegistry(config, self.mock_dal) | ||
|
|
||
| # Check that new model entry was created | ||
| assert "new-model" in registry.models | ||
| assert "other-model" in registry.models # Original model should still exist | ||
|
|
||
| new_model_entry = registry.models["new-model"] | ||
| assert new_model_entry.model == "new-model" | ||
| assert new_model_entry.name == "new-model" | ||
| assert new_model_entry.base_url == "https://new.api.com" | ||
| assert new_model_entry.api_key == SecretStr("new-key") | ||
| assert new_model_entry.api_version == "2024-01-01" | ||
| assert new_model_entry.is_robusta_model is False | ||
|
|
||
| def test_init_models_nonexistent_model_without_api_key_still_creates(self): | ||
| """Test that new model entry is created even without api_key when model doesn't exist.""" | ||
| # Create a model file without the target model | ||
| model_data = { | ||
| "other-model": { | ||
| "model": "gpt-3.5", | ||
| "api_key": "other-key" | ||
| } | ||
| } | ||
| temp_file = self.create_temp_model_file(model_data) | ||
|
|
||
| with patch("holmes.core.llm.MODEL_LIST_FILE_LOCATION", temp_file), \ | ||
| patch.object(LLMModelRegistry, "_should_load_robusta_ai", return_value=False): | ||
|
|
||
| # Create config for nonexistent model without api_key | ||
| config = Config( | ||
| model="new-model", | ||
| api_base="https://new.api.com", | ||
| api_key=None, # No API key | ||
| api_version="2024-01-01" | ||
| ) | ||
|
|
||
| registry = LLMModelRegistry(config, self.mock_dal) | ||
|
|
||
| # Check that new model entry was still created (because not existing_model) | ||
| assert "new-model" in registry.models | ||
|
|
||
| new_model_entry = registry.models["new-model"] | ||
| assert new_model_entry.model == "new-model" | ||
| assert new_model_entry.name == "new-model" | ||
| assert new_model_entry.base_url == "https://new.api.com" | ||
| assert new_model_entry.api_key is None | ||
| assert new_model_entry.api_version == "2024-01-01" | ||
| assert new_model_entry.is_robusta_model is False | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking here loudly, I think that this is very confusing, in which cases the user wants to override the api key of the models.
Also, if he would like to override it he can't just set the value in the model list? we already have couple of places (toolset for example) that we are allowing those kind od override and it is complexing the code very much.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just want to make it safe when both model and api_key are specified in the configuration file(~/.homes/config), we pick the configuration file instead of from the model list.
I agree too much check makes the code complicated. I think we can allow the user either to specify the model config in either model list or configuration file, and in cli when model list is defined, he can specify which model to use through model flag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This merge the cli and server behavior when model list is defined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The thing that confuse me in that case is what happen if you set all the params like base_url in the model list and not in the config. It will override it?
I think it is very cumbersome. you either set all in the model list or set all in the config. In that case if the user set only the api key we will also overrider other params imlicitly and im not sure that this is the right behivour.
Are you sure that this is what we want to do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the user set all in the model list, how will he select which model to use through CLI?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks im missing something. In the suggested implementation we are overriding the model with the api_key, how this is connected to selecting the right model though cli?
@Avi-Robusta has an open pr that still need tests here: https://github.com/HolmesGPT/holmesgpt/pull/1096/files#diff-008177b02b41ee19dca44738e38891469986b5f51dfd58bc1b2ae22231471679R550-R556
In that pr you can select models from the model list and use them.
We still need to add tests + docs for this flow.
Can you help me understand what im missing in your suggested pr?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think #1096 addresses the same issue as I want to in this PR. Since the PR is opened for review, let's focus #1096 instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The user scenario is the user specify the model list and select the model from CLL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha, thanks @mainred !