Skip to content
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

Single model - Single Term changes #1457

Open
wants to merge 18 commits into
base: mvp_demo
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions design/KruizeLocalAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -2238,6 +2238,65 @@ If no experiment type is specified, it will default to `container`.
```
</details>

**Request with `model_settings` and `term_settings` field**

Under `recommendation_settings`, the `model_settings` and `term_settings` field is optional
and can be used to specify model and term details. Currently, it supports configurations
for a single model and a single term. Model can be set to either cost or performance and
term can be set to short, medium or long term.

If mode is set to auto or recreate and the above settings are not mentioned then it will
default to `performance` and `short` term.

<details>
<summary><b>Example Request with custom model_settings and term_settings </b></summary>

### EXAMPLE REQUEST
```json
[
{
"version": "v2.0",
"experiment_name": "default|default|deployment|tfb-qrh-deployment",
"cluster_name": "default",
"performance_profile": "resource-optimization-openshift",
"mode": "monitor",
"target_cluster": "local",
"experiment_type": "container",
"kubernetes_objects": [
{
"type": "deployment",
"name": "tfb-qrh-deployment",
"namespace": "default",
"containers": [
{
"container_image_name": "kruize/tfb-db:1.15",
"container_name": "tfb-server-0"
},
{
"container_image_name": "kruize/tfb-qrh:1.13.2.F_et17",
"container_name": "tfb-server-1"
}
]
}
],
"trial_settings": {
"measurement_duration": "15min"
},
"recommendation_settings": {
"threshold": "0.1",
"model_settings": {
"models": ["cost"]
},
"term_settings": {
"terms": ["short"]
}
},
"datasource": "prometheus-1"
}
]
```
</details>

**Response**

<details>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*******************************************************************************
* Copyright (c) 2025 Red Hat, IBM Corporation and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.autotune.analyzer.exceptions;

public class InvalidModelException extends Throwable {
public InvalidModelException(String model) {
super(model);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*******************************************************************************
* Copyright (c) 2025 Red Hat, IBM Corporation and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.autotune.analyzer.exceptions;

public class InvalidTermException extends Throwable {
public InvalidTermException(String term) {
super(term);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
*******************************************************************************/
package com.autotune.analyzer.exceptions;

public class InvalidValueException extends Throwable
{
public class InvalidValueException extends Throwable {
public InvalidValueException(String namespace) {
super(namespace);
}
Expand Down
86 changes: 58 additions & 28 deletions src/main/java/com/autotune/analyzer/kruizeObject/KruizeObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
*******************************************************************************/
package com.autotune.analyzer.kruizeObject;

import com.autotune.analyzer.exceptions.InvalidTermException;
import com.autotune.analyzer.exceptions.InvalidValueException;
import com.autotune.analyzer.recommendations.term.Terms;
import com.autotune.analyzer.utils.AnalyzerConstants;
import com.autotune.analyzer.utils.AnalyzerErrorConstants;
import com.autotune.analyzer.utils.ExperimentTypeAware;
import com.autotune.analyzer.utils.ExperimentTypeUtil;
import com.autotune.common.data.ValidationOutputData;
Expand Down Expand Up @@ -133,26 +135,73 @@ public KruizeObject() {
@param terms A map to store the default terms with term name as the key and Terms object as the value.
@param kruizeObject The KruizeObject for which the default terms are being set.
*/

public static void setDefaultTerms(Map<String, Terms> terms, KruizeObject kruizeObject) {
// TODO: define term names like daily, weekly, fortnightly etc
// TODO: add CRD for terms
// for monitoring use case
terms.put(KruizeConstants.JSONKeys.SHORT_TERM, new Terms(KruizeConstants.JSONKeys.SHORT_TERM,
KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.SHORT_TERM_DURATION_DAYS,
KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.SHORT_TERM_DURATION_DAYS_THRESHOLD,
4, 0.25));
terms.put(KruizeConstants.JSONKeys.MEDIUM_TERM, new Terms(KruizeConstants.JSONKeys.MEDIUM_TERM,
KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.MEDIUM_TERM_DURATION_DAYS,
KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.MEDIUM_TERM_DURATION_DAYS_THRESHOLD,
7, 1));
terms.put(KruizeConstants.JSONKeys.LONG_TERM, new Terms(KruizeConstants.JSONKeys.LONG_TERM,
KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.LONG_TERM_DURATION_DAYS,
KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.LONG_TERM_DURATION_DAYS_THRESHOLD,
15, 1));
kruizeObject.setTerms(terms);

}

public static void setDefaultTermsForAutoAndRecreate(Map<String, Terms> terms, KruizeObject kruizeObject) {
// for auto and recreate mode
// Default is Short Term
terms.put(KruizeConstants.JSONKeys.SHORT_TERM, new Terms(KruizeConstants.JSONKeys.SHORT_TERM,
KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.SHORT_TERM_DURATION_DAYS,
getTermThresholdInDays(KruizeConstants.JSONKeys.SHORT_TERM, kruizeObject.getTrial_settings().getMeasurement_durationMinutes_inDouble()),
KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.SHORT_TERM_DURATION_DAYS_THRESHOLD,
4, 0.25));
terms.put(KruizeConstants.JSONKeys.MEDIUM_TERM, new Terms(KruizeConstants.JSONKeys.MEDIUM_TERM,
KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.MEDIUM_TERM_DURATION_DAYS,
getTermThresholdInDays(KruizeConstants.JSONKeys.MEDIUM_TERM, kruizeObject.getTrial_settings().getMeasurement_durationMinutes_inDouble()),
7, 1));
terms.put(KruizeConstants.JSONKeys.LONG_TERM, new Terms(KruizeConstants.JSONKeys.LONG_TERM,
KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.LONG_TERM_DURATION_DAYS,
getTermThresholdInDays(KruizeConstants.JSONKeys.LONG_TERM, kruizeObject.getTrial_settings().getMeasurement_durationMinutes_inDouble()),
15, 1));

kruizeObject.setTerms(terms);
}

public static void setCustomTerms(Map<String, Terms> terms, KruizeObject kruizeObject) throws InvalidTermException {
// TODO: define term names like daily, weekly, fortnightly etc
// TODO: add CRD for terms

if (kruizeObject.getRecommendation_settings() != null && kruizeObject.getRecommendation_settings().getTermSettings() != null) {

List<String> termList = kruizeObject.getRecommendation_settings().getTermSettings().getTerms();

for (String userInputTerm : termList) {
if (AnalyzerConstants.RecommendationSettings.SHORT.equalsIgnoreCase(userInputTerm)) {
terms.put(KruizeConstants.JSONKeys.SHORT_TERM, new Terms(KruizeConstants.JSONKeys.SHORT_TERM,
KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.SHORT_TERM_DURATION_DAYS,
KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.SHORT_TERM_DURATION_DAYS_THRESHOLD,
4, 0.25));
} else if (AnalyzerConstants.RecommendationSettings.MEDIUM.equalsIgnoreCase(userInputTerm)) {
terms.put(KruizeConstants.JSONKeys.MEDIUM_TERM, new Terms(KruizeConstants.JSONKeys.MEDIUM_TERM,
KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.MEDIUM_TERM_DURATION_DAYS,
KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.MEDIUM_TERM_DURATION_DAYS_THRESHOLD,
7, 1));
} else if (AnalyzerConstants.RecommendationSettings.LONG.equalsIgnoreCase(userInputTerm)) {
terms.put(KruizeConstants.JSONKeys.LONG_TERM, new Terms(KruizeConstants.JSONKeys.LONG_TERM,
KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.LONG_TERM_DURATION_DAYS,
KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.LONG_TERM_DURATION_DAYS_THRESHOLD,
15, 1));
} else {
throw new InvalidTermException(userInputTerm + AnalyzerErrorConstants.APIErrors.CreateExperimentAPI.INVALID_TERM_NAME);
}
}
kruizeObject.setTerms(terms);
} else {
// Handles the case where termSettings is null
throw new InvalidTermException(AnalyzerErrorConstants.APIErrors.CreateExperimentAPI.TERM_SETTINGS_UNDEFINED);
}
}

public String getExperimentName() {
return experimentName;
}
Expand Down Expand Up @@ -363,23 +412,4 @@ public boolean isNamespaceExperiment() {
public boolean isContainerExperiment() {
return ExperimentTypeUtil.isContainerExperiment(experimentType);
}

private static double getTermThresholdInDays(String term, Double measurement_duration) {
double minDataPoints = 2;

switch (term) {
case KruizeConstants.JSONKeys.SHORT_TERM:
minDataPoints = KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.SHORT_TERM_MIN_DATAPOINTS;
break;
case KruizeConstants.JSONKeys.MEDIUM_TERM:
minDataPoints = KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.MEDIUM_TERM_MIN_DATAPOINTS;
break;
case KruizeConstants.JSONKeys.LONG_TERM:
minDataPoints = KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.LONG_TERM_MIN_DATAPOINTS;
break;
}

return ((double) measurement_duration * minDataPoints
/ (KruizeConstants.TimeConv.NO_OF_HOURS_PER_DAY * KruizeConstants.TimeConv.NO_OF_MINUTES_PER_HOUR));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.autotune.analyzer.kruizeObject;

import java.util.List;

public class ModelSettings {

private List<String> models;

public ModelSettings() {}

public List<String> getModels() {
return models;
}

public void setModels(List<String> models) {
dinogun marked this conversation as resolved.
Show resolved Hide resolved
this.models = models;
}

@Override
public String toString() {
return "ModelSettings{" +
"models=" + models +
'}';
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,17 @@
*******************************************************************************/
package com.autotune.analyzer.kruizeObject;

import com.autotune.utils.KruizeConstants;
import com.google.gson.annotations.SerializedName;

public class RecommendationSettings {
private Double threshold;
@SerializedName(KruizeConstants.JSONKeys.MODEL_SETTINGS)
private ModelSettings modelSettings;
@SerializedName(KruizeConstants.JSONKeys.TERM_SETTINGS)
private TermSettings termSettings;

public RecommendationSettings(){}

public Double getThreshold() {
return threshold;
Expand All @@ -26,10 +35,28 @@ public void setThreshold(Double threshold) {
this.threshold = threshold;
}

public ModelSettings getModelSettings() {
return modelSettings;
}

public void setModelSettings(ModelSettings modelSettings) {
this.modelSettings = modelSettings;
}

public TermSettings getTermSettings() {
return termSettings;
}

public void setTermSettings(TermSettings termSettings) {
this.termSettings = termSettings;
}

@Override
public String toString() {
return "RecommendationSettings{" +
"threshold=" + threshold +
", modelSettings=" + modelSettings +
", termSettings=" + termSettings +
'}';
}
}
24 changes: 24 additions & 0 deletions src/main/java/com/autotune/analyzer/kruizeObject/TermSettings.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.autotune.analyzer.kruizeObject;

import java.util.List;

public class TermSettings {
private List<String> terms;

public TermSettings() {}

public List<String> getTerms() {
return terms;
}

public void setTerms(List<String> terms) {
this.terms = terms;
}

@Override
public String toString() {
return "TermSettings{" +
"terms=" + terms +
'}';
}
}
Loading
Loading