44import com .yahoo .config .model .api .ConfigChangeAction ;
55import com .yahoo .config .model .deploy .DeployState ;
66import com .yahoo .config .model .deploy .TestDeployState ;
7- import com .yahoo .config .model .deploy .TestProperties ;
87import com .yahoo .text .Text ;
98import com .yahoo .vespa .model .VespaModel ;
109import com .yahoo .vespa .model .application .validation .ValidationTester ;
1413import java .util .List ;
1514
1615import static org .junit .jupiter .api .Assertions .assertEquals ;
16+ import static org .junit .jupiter .api .Assertions .assertFalse ;
1717import static org .junit .jupiter .api .Assertions .assertTrue ;
1818
1919/**
20- * @author lesters
20+ * @author Lester Solbakken
21+ * @author glebashnik
2122 */
2223public class RestartOnDeployForLocalLLMValidatorTest {
2324
@@ -26,38 +27,68 @@ public class RestartOnDeployForLocalLLMValidatorTest {
2627 @ Test
2728 void validate_no_restart_on_deploy () {
2829 VespaModel current = createModel ();
29- VespaModel next = createModel (withComponent ( LOCAL_LLM_COMPONENT ));
30+ VespaModel next = createModel (withLocalLLMComponent ( "models/model.gguf" , 1 ));
3031 List <ConfigChangeAction > result = validateModel (current , next );
3132 assertEquals (0 , result .size ());
3233 }
3334
3435 @ Test
35- void validate_restart_on_deploy () {
36- VespaModel current = createModel (withComponent ( LOCAL_LLM_COMPONENT ));
37- VespaModel next = createModel (withComponent ( LOCAL_LLM_COMPONENT ));
36+ void validate_restart_on_deploy_llm_config_unchanged () {
37+ VespaModel current = createModel (withLocalLLMComponent ( "models/model.gguf" , 1 ));
38+ VespaModel next = createModel (withLocalLLMComponent ( "models/model.gguf" , 1 ));
3839 List <ConfigChangeAction > result = validateModel (current , next );
3940 assertEquals (1 , result .size ());
4041 assertTrue (result .get (0 ).validationId ().isEmpty ());
41- assertEquals ("Need to restart services in cluster 'cluster1' due to use of local LLM" , result .get (0 ).getMessage ());
42+ assertEquals (
43+ "Need to restart services in cluster 'cluster1' due to use of local LLM" , result .get (0 ).getMessage ());
44+ assertTrue (
45+ result .get (0 ).ignoreForInternalRedeploy (),
46+ "Restart must be ignored for internal redeployment when LLM config is unchanged"
47+ );
48+ }
49+
50+ @ Test
51+ void validate_restart_on_deploy_llm_config_changed () {
52+ VespaModel current = createModel (withLocalLLMComponent ("models/model.gguf" , 1 ));
53+ VespaModel next = createModel (withLocalLLMComponent ("models/model.gguf" , 2 ));
54+ List <ConfigChangeAction > result = validateModel (current , next );
55+ assertEquals (1 , result .size ());
56+ assertFalse (
57+ result .get (0 ).ignoreForInternalRedeploy (),
58+ "Restart must not be ignored for internal redeployment when LLM config changed"
59+ );
60+ }
61+
62+ @ Test
63+ void validate_restart_on_deploy_model_path_changed () {
64+ VespaModel current = createModel (withLocalLLMComponent ("models/model-a.gguf" , 1 ));
65+ VespaModel next = createModel (withLocalLLMComponent ("models/model-b.gguf" , 1 ));
66+ List <ConfigChangeAction > result = validateModel (current , next );
67+ assertEquals (1 , result .size ());
68+ assertFalse (result .get (0 ).ignoreForInternalRedeploy (), "Restart must not be ignored when model path changed" );
4269 }
4370
4471 private static List <ConfigChangeAction > validateModel (VespaModel current , VespaModel next ) {
45- return ValidationTester .validateChanges (new RestartOnDeployForLocalLLMValidator (),
46- next ,
47- deployStateBuilder ().previousModel (current ).build ());
72+ return ValidationTester .validateChanges (
73+ new RestartOnDeployForLocalLLMValidator (),
74+ next ,
75+ deployStateBuilder ().previousModel (current ).build ()
76+ );
4877 }
4978
5079 private static VespaModel createModel (String component ) {
51- var xml = Text .format ("""
52- <services version='1.0'>
53- <container id='cluster1' version='1.0'>
54- <http>
55- <server id='server1' port='8080'/>
56- </http>
57- %s
58- </container>
59- </services>
60- """ , component );
80+ var xml = Text .format (
81+ """
82+ <services version='1.0'>
83+ <container id='cluster1' version='1.0'>
84+ <http>
85+ <server id='server1' port='8080'/>
86+ </http>
87+ %s
88+ </container>
89+ </services>
90+ """ , component
91+ );
6192 DeployState .Builder builder = deployStateBuilder ();
6293 return new VespaModelCreatorWithMockPkg (null , xml ).create (builder );
6394 }
@@ -66,16 +97,21 @@ private static VespaModel createModel() {
6697 return createModel ("" );
6798 }
6899
69- private static String withComponent (String componentClass ) {
70- return Text .format ("<component id='llm' class='%s' />" , componentClass );
100+ private static String withLocalLLMComponent (String modelPath , int parallelRequests ) {
101+ return Text .format (
102+ """
103+ <component id='llm' class='%s'>
104+ <config name="ai.vespa.llm.clients.llm-local-client">
105+ <model path="%s"/>
106+ <parallelRequests>%d</parallelRequests>
107+ </config>
108+ </component>""" , RestartOnDeployForLocalLLMValidatorTest .LOCAL_LLM_COMPONENT , modelPath ,
109+ parallelRequests
110+ );
71111 }
72112
73113 private static DeployState .Builder deployStateBuilder () {
74114 return TestDeployState .createBuilder ();
75115 }
76116
77- private static void assertStartsWith (String expected , List <ConfigChangeAction > result ) {
78- assertTrue (result .get (0 ).getMessage ().startsWith (expected ));
79- }
80-
81- }
117+ }
0 commit comments