import json from unittest.mock import MagicMock, patch import pytest from photools3.lm_studio import LMStudioError, LMStudioModelManager def _http_response(payload: dict): response = MagicMock() response.__enter__.return_value = response response.read.return_value = json.dumps(payload).encode("utf-8") return response def test_reload_model_unloads_and_reloads_preserving_context_config(): model_response = { "models": [ { "key": "qwen/qwen3-vl-4b", "loaded_instances": [ { "id": "qwen/qwen3-vl-4b-instance", "config": { "context_length": 16384, "eval_batch_size": 256, "flash_attention": True, "offload_kv_cache_to_gpu": False, "parallel": 2, }, } ], } ] } with patch( "photools3.lm_studio.urlopen", side_effect=[ _http_response(model_response), _http_response({"instance_id": "qwen/qwen3-vl-4b-instance"}), _http_response({"instance_id": "qwen/qwen3-vl-4b-instance"}), ], ) as mocked_urlopen: manager = LMStudioModelManager( "http://localhost:1234/v1", "test-token", timeout=12 ) manager.reload_model("qwen/qwen3-vl-4b") requests = [call.args[0] for call in mocked_urlopen.call_args_list] assert requests[0].full_url == "http://localhost:1234/api/v1/models" assert requests[0].get_method() == "GET" assert requests[0].get_header("Authorization") == "Bearer test-token" assert requests[1].full_url.endswith("/models/unload") assert json.loads(requests[1].data) == {"instance_id": "qwen/qwen3-vl-4b-instance"} assert requests[2].full_url.endswith("/models/load") assert json.loads(requests[2].data) == { "model": "qwen/qwen3-vl-4b", "context_length": 16384, "eval_batch_size": 256, "flash_attention": True, "offload_kv_cache_to_gpu": False, } def test_reload_model_loads_by_key_if_no_instance_is_loaded(): model_response = {"models": [{"key": "qwen/model", "loaded_instances": []}]} with patch( "photools3.lm_studio.urlopen", side_effect=[ _http_response(model_response), _http_response({"status": "loaded"}), ], ) as mocked_urlopen: manager = LMStudioModelManager("http://localhost:1234/v1", "token") manager.reload_model("qwen/model") assert mocked_urlopen.call_count == 2 load_request = mocked_urlopen.call_args.args[0] assert load_request.full_url.endswith("/models/load") assert json.loads(load_request.data) == {"model": "qwen/model"} def test_reload_model_rejects_ambiguous_model_selection(): model_response = { "models": [ { "key": "qwen/model", "loaded_instances": [ {"id": "instance-1", "config": {}}, {"id": "instance-2", "config": {}}, ], } ] } with patch( "photools3.lm_studio.urlopen", return_value=_http_response(model_response) ): manager = LMStudioModelManager("http://localhost:1234/v1", "token") with pytest.raises(LMStudioError, match="Multiple loaded instances"): manager.reload_model("qwen/model")