11import pytest
2-
2+ import os
33from tests .llama_stack .constants import LlamaStackProviders
4- from llama_stack_client import LlamaStackClient
5- from utilities . constants import MinIo , QWEN_MODEL_NAME
4+ from llama_stack_client import LlamaStackClient , NotFoundError
5+ from llama_stack_client . types import Model
66
77
88@pytest .mark .parametrize (
9- "model_namespace, minio_pod, minio_data_connection, llama_stack_server_config " ,
9+ "unprivileged_model_namespace " ,
1010 [
1111 pytest .param (
1212 {"name" : "test-llamastack-models" , "randomize_name" : True },
13- MinIo .PodConfig .QWEN_HAP_BPIV2_MINIO_CONFIG ,
14- {"bucket" : "llms" },
15- {
16- "vllm_url_fixture" : "qwen_isvc_url" ,
17- "inference_model" : QWEN_MODEL_NAME ,
18- "llama_stack_storage_size" : "2Gi" ,
19- },
20- )
13+ ),
2114 ],
2215 indirect = True ,
2316)
24- @pytest .mark .rawdeployment
25- @pytest .mark .smoke
2617@pytest .mark .llama_stack
27- @pytest .mark .skip_must_gather
28- @pytest .mark .usefixtures ("minio_pod" , "minio_data_connection" )
18+ @pytest .mark .smoke
2919class TestLlamaStackModels :
3020 """Test class for LlamaStack models API functionality.
3121
@@ -34,44 +24,171 @@ class TestLlamaStackModels:
3424 - https://github.com/openai/openai-python/blob/main/api.md#models
3525 """
3626
37- def test_models_list (self , llama_stack_client : LlamaStackClient ) -> None :
38- """Test the initial state of the LlamaStack server and available models."""
39- models = llama_stack_client .models .list ()
27+ def test_models_list (
28+ self ,
29+ unprivileged_llama_stack_client : LlamaStackClient ,
30+ ) -> None :
31+ """Test listing all available models.
32+
33+ Verifies that the models.list() method returns a non-empty list
34+ containing at least one LLM and one embedding model, compatible
35+ with OpenAI SDK structure.
36+ """
37+ models = unprivileged_llama_stack_client .models .list ()
4038 assert models is not None , "No models returned from LlamaStackClient"
39+ assert isinstance (models , list ), "models.list() should return a list"
40+ assert len (models ) > 0 , "At least one model should be available"
4141
4242 llm_model = next ((model for model in models if model .api_model_type == "llm" ), None )
4343 assert llm_model is not None , "No LLM model found in available models"
44- model_id = llm_model .identifier
45- assert model_id is not None , "No identifier set in LLM model"
44+ assert isinstance (llm_model , Model ), "LLM model should be a Model instance"
45+ assert llm_model .identifier is not None , "No identifier set in LLM model"
46+ assert len (llm_model .identifier ) > 0 , "LLM model identifier should not be empty"
4647
4748 embedding_model = next ((model for model in models if model .api_model_type == "embedding" ), None )
4849 assert embedding_model is not None , "No embedding model found in available models"
49- embedding_model_id = embedding_model .identifier
50- assert embedding_model_id is not None , "No embedding model returned from LlamaStackClient"
50+ assert isinstance (embedding_model , Model ), "Embedding model should be a Model instance"
51+ assert embedding_model .identifier is not None , "No identifier set in embedding model"
52+ assert len (embedding_model .identifier ) > 0 , "Embedding model identifier should not be empty"
5153 assert "embedding_dimension" in embedding_model .metadata , "embedding_dimension not found in model metadata"
5254 embedding_dimension = embedding_model .metadata ["embedding_dimension" ]
5355 assert embedding_dimension is not None , "No embedding_dimension set in embedding model"
56+ # API returns dimension as float (e.g., 768.0) though conceptually an integer
57+ assert isinstance (embedding_dimension , float ), "embedding_dimension should be a float"
58+ assert embedding_dimension > 0 , "embedding_dimension should be positive"
59+ assert embedding_dimension .is_integer (), "embedding_dimension should be a whole number"
60+
61+ def test_models_list_structure (
62+ self ,
63+ unprivileged_llama_stack_client : LlamaStackClient ,
64+ ) -> None :
65+ """Test that model list response structure matches OpenAI SDK compatibility.
66+
67+ Verifies that each model in the list has the required fields expected
68+ by OpenAI-compatible clients.
69+ """
70+ models = unprivileged_llama_stack_client .models .list ()
71+ assert models is not None , "No models returned from LlamaStackClient"
72+
73+ for model in models :
74+ assert hasattr (model , "identifier" ), "Model should have identifier attribute"
75+ assert hasattr (model , "api_model_type" ), "Model should have api_model_type attribute"
76+ assert model .identifier is not None , f"Model { model } should have a non-None identifier"
77+ assert model .api_model_type in ["llm" , "embedding" ], (
78+ f"Model { model .identifier } should have api_model_type 'llm' or 'embedding', "
79+ f"got '{ model .api_model_type } '"
80+ )
81+ assert hasattr (model , "metadata" ), "Model should have metadata attribute"
82+ assert isinstance (model .metadata , dict ), "Model metadata should be a dictionary"
83+
84+ def test_models_retrieve_existing (
85+ self ,
86+ unprivileged_llama_stack_client : LlamaStackClient ,
87+ ) -> None :
88+ """Test retrieving an existing model by ID.
89+
90+ Verifies that models.retrieve() returns the correct model when given
91+ a valid model identifier from the list.
92+ """
93+ models = unprivileged_llama_stack_client .models .list ()
94+ assert len (models ) > 0 , "At least one model should be available"
95+
96+ test_model = models [0 ]
97+ retrieved_model = unprivileged_llama_stack_client .models .retrieve (model_id = test_model .identifier )
5498
55- def test_models_register (self , llama_stack_client : LlamaStackClient ) -> None :
56- """Test model registration functionality."""
57- response = llama_stack_client .models .register (
58- provider_id = LlamaStackProviders .Inference .VLLM_INFERENCE , model_type = "llm" , model_id = QWEN_MODEL_NAME
99+ assert retrieved_model is not None , f"Model { test_model .identifier } should be retrievable"
100+ assert isinstance (retrieved_model , Model ), "Retrieved model should be a Model instance"
101+ assert retrieved_model .identifier == test_model .identifier , (
102+ f"Retrieved model identifier '{ retrieved_model .identifier } ' "
103+ f"should match requested '{ test_model .identifier } '"
59104 )
60- assert response
105+ assert retrieved_model .api_model_type == test_model .api_model_type , (
106+ f"Retrieved model type '{ retrieved_model .api_model_type } ' "
107+ f"should match original '{ test_model .api_model_type } '"
108+ )
109+
110+ def test_models_retrieve_nonexistent (
111+ self ,
112+ unprivileged_llama_stack_client : LlamaStackClient ,
113+ ) -> None :
114+ """Test retrieving a non-existent model raises NotFoundError.
115+
116+ Verifies that models.retrieve() raises NotFoundError when given
117+ an invalid model identifier, matching OpenAI SDK behavior.
118+ """
119+ nonexistent_model_id = "nonexistent-provider/nonexistent-model"
61120
62- def test_model_list (self , llama_stack_client : LlamaStackClient ) -> None :
63- """Test listing available models and verify their properties."""
64- models = llama_stack_client .models .list ()
121+ with pytest .raises (NotFoundError ):
122+ unprivileged_llama_stack_client .models .retrieve (model_id = nonexistent_model_id )
65123
66- # Find the registered LLM by identifier suffix
67- expected_id_suffix = f"/{ QWEN_MODEL_NAME } "
68- target = next (
69- (model for model in models if model .model_type == "llm" and model .identifier .endswith (expected_id_suffix )),
70- None ,
124+ def test_models_register (
125+ self ,
126+ unprivileged_llama_stack_client : LlamaStackClient ,
127+ ) -> None :
128+ """Test registering a new model.
129+
130+ Verifies that models.register() successfully registers a new model
131+ and it appears in the models list.
132+ """
133+ inference_model = os .getenv ("LLS_CORE_INFERENCE_MODEL" )
134+ assert inference_model , "LLS_CORE_INFERENCE_MODEL environment variable must be set"
135+ test_model_id = f"{ inference_model } -test-register"
136+
137+ response = unprivileged_llama_stack_client .models .register (
138+ model_id = test_model_id ,
139+ model_type = "llm" ,
140+ provider_id = LlamaStackProviders .Inference .VLLM_INFERENCE ,
71141 )
72- assert target is not None , (
73- f"LLM { QWEN_MODEL_NAME } not found in models: { [model .identifier for model in models ]} "
142+ assert response is not None , "Model registration should return a response"
143+
144+ registered_model_id = f"{ LlamaStackProviders .Inference .VLLM_INFERENCE .value } /{ test_model_id } "
145+ try :
146+ models = unprivileged_llama_stack_client .models .list ()
147+ registered_model_ids = [model .identifier for model in models ]
148+ assert registered_model_id in registered_model_ids , (
149+ f"Registered model { registered_model_id } should appear in models list"
150+ )
151+ finally :
152+ unprivileged_llama_stack_client .models .unregister (model_id = registered_model_id )
153+
154+ def test_models_register_retrieve_unregister (
155+ self ,
156+ unprivileged_llama_stack_client : LlamaStackClient ,
157+ ) -> None :
158+ """Test complete model lifecycle: register, retrieve, and unregister.
159+
160+ Verifies the full workflow of registering a model, retrieving it,
161+ verifying its properties, and then unregistering it.
162+ """
163+ inference_model = os .getenv ("LLS_CORE_INFERENCE_MODEL" )
164+ assert inference_model , "LLS_CORE_INFERENCE_MODEL environment variable must be set"
165+ test_model_id = f"{ inference_model } -test-lifecycle"
166+
167+ response = unprivileged_llama_stack_client .models .register (
168+ model_id = test_model_id ,
169+ model_type = "llm" ,
170+ provider_id = LlamaStackProviders .Inference .VLLM_INFERENCE ,
74171 )
75- assert target .identifier .endswith (expected_id_suffix )
76- assert target .model_type == "llm"
77- assert target .provider_id == LlamaStackProviders .Inference .VLLM_INFERENCE .value
172+ assert response is not None , "Model registration should return a response"
173+
174+ registered_model_id = f"{ LlamaStackProviders .Inference .VLLM_INFERENCE .value } /{ test_model_id } "
175+ try :
176+ registered_model = unprivileged_llama_stack_client .models .retrieve (model_id = registered_model_id )
177+ assert registered_model is not None , f"LLM { registered_model_id } not found using models.retrieve"
178+ assert isinstance (registered_model , Model ), "Retrieved model should be a Model instance"
179+ expected_id_suffix = f"/{ test_model_id } "
180+ assert registered_model .identifier .endswith (expected_id_suffix ), (
181+ f"Model identifier '{ registered_model .identifier } ' should end with '{ expected_id_suffix } '"
182+ )
183+ assert registered_model .api_model_type == "llm" , (
184+ f"Registered model should have api_model_type 'llm', got '{ registered_model .api_model_type } '"
185+ )
186+ assert registered_model .provider_id == LlamaStackProviders .Inference .VLLM_INFERENCE .value , (
187+ f"Registered model provider_id should be '{ LlamaStackProviders .Inference .VLLM_INFERENCE .value } ', "
188+ f"got '{ registered_model .provider_id } '"
189+ )
190+ finally :
191+ unprivileged_llama_stack_client .models .unregister (model_id = registered_model_id )
192+
193+ with pytest .raises (NotFoundError ):
194+ unprivileged_llama_stack_client .models .retrieve (model_id = registered_model_id )
0 commit comments