From e075eae053a704061a598c5f28553ad931fc2ac7 Mon Sep 17 00:00:00 2001 From: Yamac Ay Date: Fri, 2 Oct 2026 08:34:53 +0200 Subject: [PATCH 1/9] rpt client updated to 1.6 --- .../gen/gen_ai_hub/proxy/native/sap/models.py | 70 ++++++++++++++++--- .../gen/tests/proxy/native/test_sap_rpt.py | 40 +++++++++-- 2 files changed, 94 insertions(+), 16 deletions(-) diff --git a/packages/gen/gen_ai_hub/proxy/native/sap/models.py b/packages/gen/gen_ai_hub/proxy/native/sap/models.py index 7770adce..1fa65ba2 100644 --- a/packages/gen/gen_ai_hub/proxy/native/sap/models.py +++ b/packages/gen/gen_ai_hub/proxy/native/sap/models.py @@ -1,5 +1,5 @@ from typing import Optional, Literal, Union, Any -from pydantic import BaseModel, RootModel, model_validator +from pydantic import BaseModel, Field, RootModel, model_validator class TargetColumn(BaseModel): @@ -7,17 +7,47 @@ class TargetColumn(BaseModel): :param name: Name of the target column. :type name: str - :param prediction_placeholder: Placeholder string denoting where predictions will be inserted. - Defaults to ``"[PREDICT]"``. - :type prediction_placeholder: str + :param prediction_placeholder: The prediction placeholder in any column for which to predict a value. The model will predict a value for all table cells containing this value. + :type prediction_placeholder: Optional[Union[str, int, float]] :param task_type: Task type of the target column. One of ``"classification"`` or ``"regression"``. Defaults to ``None``. :type task_type: Optional[Literal["classification", "regression"]] + :param top_k: How many predictions to output for this classification column.If not provided, only a single prediction is returned. Only relevant for classification. + :type top_k: Optional[int] """ name: str - prediction_placeholder: str = "[PREDICT]" + prediction_placeholder: Optional[Union[str, int, float]] task_type: Optional[Literal["classification", "regression"]] = None + top_k: Optional[int] = None + + +class ExplanationConfig(BaseModel): + """ + Configuration for explainability outputs. + + :param top_column_scores: For how many columns to output column scores (optional, default is 0). 0 by default (no explainability). Max value is 20. + :type top_column_scores: int + :param top_relevant_context_rows: For how many context rows to return indices per query row (optional, default is 0). 0 by default (no explainability). Max value is 20. + :type top_relevant_context_rows: int + """ + + top_column_scores: int = 0 + top_relevant_context_rows: int = 0 + + +class ExplanationResult(BaseModel): + """ + Explanation data for predictions. + + :param top_column_scores: Column scores per query row extracted from the model (higher means more weight was put on this column). + :type top_column_scores: Optional[list[dict[str, Union[int, float]]]] + :param top_relevant_context_rows: 2D array where each subarray contains indices of most relevant context rows for that query row. The first dimension indexes query rows, the second dimension indexes all rows as a sequential integer index. + :type top_relevant_context_rows: Optional[list[list[int]]] + """ + + top_column_scores: Optional[list[dict[str, Union[int, float]]]] + top_relevant_context_rows: Optional[list[list[int]]] class PredictionConfig(BaseModel): @@ -26,19 +56,32 @@ class PredictionConfig(BaseModel): :param target_columns: List of target columns to predict. :type target_columns: list[TargetColumn] + :param explanations: Optional configuration for explainability outputs (column scores and relevant context rows). + :type explanations: Optional[ExplanationConfig] + :param context_mode: Context mode for predictions. Set it to \"default\" for the best balance between accuracy and latency/cost. Set it to \"deep\" for higher accuracy with >8k context rows at increased latency and cost (only for \"sap-rpt-1.6-large\"). + :type context_mode: Literal['default', 'deep'] """ target_columns: list[TargetColumn] + explanations: Optional[ExplanationConfig] = None + context_mode: Literal['default', 'deep'] = 'default' class DataType(BaseModel): """Schema definition for a column. :param dtype: The data type of the column. - :type dtype: Literal["string", "numeric", "date"] + :type dtype: Literal['string', 'numeric', 'date', + 'time', 'boolean', 'largestring', 'uuid', 'integer', 'int16', + 'int32', 'int64', 'uint8', 'decimal', + 'double', 'datetime', 'timestamp'] """ - dtype: Literal["string", "numeric", "date"] + dtype: Literal['string', 'numeric', 'date', + 'time', 'boolean', 'largestring', + 'uuid', 'integer', 'int16', + 'int32', 'int64', 'uint8', 'decimal', + 'double', 'datetime', 'timestamp'] class RPTRequest(BaseModel): @@ -109,12 +152,15 @@ class ResponseMetadata(BaseModel): :type num_predictions: int :param num_query_rows: Number of query rows for which a prediction was made. :type num_query_rows: int + :param context_mode: The context mode used for this prediction request. + :type context_mode: Literal['default', 'deep'] """ num_rows: int num_columns: int num_predictions: int num_query_rows: int + context_mode: Literal['default', 'deep'] = 'default' class ResponseStatus(BaseModel): @@ -135,12 +181,15 @@ class PredictionItem(BaseModel): :param prediction: The predicted value. :type prediction: Union[str, float] - :param confidence: Confidence score for classification tasks. Defaults to ``None``. + :param confidence: Confidence score for classification tasks, in the range ``[0.0, 1.0]``. Defaults to ``None``. :type confidence: Optional[float] + :param confidence_interval: Lower and upper bounds of the confidence interval. Null for classification predictions. + :type confidence_interval: Optional[tuple[float, float]] """ prediction: Union[str, float] - confidence: Optional[float] = None + confidence: Optional[float] = Field(default=None, ge=0.0, le=1.0) + confidence_interval: Optional[tuple[float, float]] = None class Prediction(RootModel[dict[str, Union[list[PredictionItem], Any]]]): @@ -166,6 +215,8 @@ class RPTResponse(BaseModel): :type status: ResponseStatus :param predictions: Prediction data returned by the service. :type predictions: list[Prediction] + :param explanations: Explanation data containing context row and column scores. + :type explanations: Optional[ExplanationResult] :param metadata: Metadata about the request/response. :type metadata: ResponseMetadata """ @@ -173,6 +224,7 @@ class RPTResponse(BaseModel): id: str status: ResponseStatus predictions: list[Prediction] + explanations: Optional[ExplanationResult] = None metadata: ResponseMetadata diff --git a/packages/gen/tests/proxy/native/test_sap_rpt.py b/packages/gen/tests/proxy/native/test_sap_rpt.py index 5e4184b9..f28c266a 100644 --- a/packages/gen/tests/proxy/native/test_sap_rpt.py +++ b/packages/gen/tests/proxy/native/test_sap_rpt.py @@ -1,7 +1,7 @@ import unittest from unittest.mock import patch -from gen_ai_hub.proxy.native.sap.models import RPTRequest, PredictionConfig, TargetColumn, RPTResponse, RPTException +from gen_ai_hub.proxy.native.sap.models import RPTRequest, PredictionConfig, TargetColumn, RPTResponse, RPTException, PredictionItem from gen_ai_hub.proxy.native.sap.client import RPTClient from tests.mock import get_mocked_ai_core_client, sap_rpt_moke_response_code_0, sap_rpt_moke_response_code_2 @@ -101,12 +101,16 @@ class RPTRequestModels(unittest.TestCase): def test_prediction_config(self): expected_dict = { - "target_columns": [ - { - "name": "COSTCENTER", - "prediction_placeholder": "[PREDICT]", - "task_type": "classification" - }] + "target_columns": [ + { + "name": "COSTCENTER", + "prediction_placeholder": "[PREDICT]", + "task_type": "classification", + "top_k": None + } + ], + "explanations": None, + "context_mode": "default" } prediction_config = PredictionConfig(target_columns=[ TargetColumn(name="COSTCENTER", prediction_placeholder="[PREDICT]", task_type="classification") @@ -127,6 +131,28 @@ def test_rpt_request_by_columns_from_dict(self): assert request.columns["COSTCENTER"][0] == "[PREDICT]" assert "rows" not in request.model_dump() + def test_prediction_item_confidence_valid_boundaries(self): + item_low = PredictionItem(prediction="cat", confidence=0.0) + item_high = PredictionItem(prediction="cat", confidence=1.0) + self.assertEqual(item_low.confidence, 0.0) + self.assertEqual(item_high.confidence, 1.0) + + def test_prediction_item_confidence_valid_midrange(self): + item = PredictionItem(prediction="cat", confidence=0.85) + self.assertEqual(item.confidence, 0.85) + + def test_prediction_item_confidence_none(self): + item = PredictionItem(prediction=3.14) + self.assertIsNone(item.confidence) + + def test_prediction_item_confidence_above_max(self): + with self.assertRaises(ValueError): + PredictionItem(prediction="cat", confidence=1.1) + + def test_prediction_item_confidence_below_min(self): + with self.assertRaises(ValueError): + PredictionItem(prediction="cat", confidence=-0.1) + def test_rpt_request_columns_and_rows_provided(self): with self.assertRaises(ValueError) as err: RPTRequest( From 143a9a302c856d95ca3c1f38371a70221404e90f Mon Sep 17 00:00:00 2001 From: Yamac Ay Date: Fri, 2 Oct 2026 09:58:31 +0200 Subject: [PATCH 2/9] prediction_payload tests updated --- packages/gen/integration_tests/native_clients/test_sap_rpt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/gen/integration_tests/native_clients/test_sap_rpt.py b/packages/gen/integration_tests/native_clients/test_sap_rpt.py index 3513fa27..86bc47b3 100644 --- a/packages/gen/integration_tests/native_clients/test_sap_rpt.py +++ b/packages/gen/integration_tests/native_clients/test_sap_rpt.py @@ -187,7 +187,7 @@ def test_regression_prediction(self): body = RPTRequest( prediction_config=PredictionConfig( target_columns=[ - TargetColumn(name="DISCOUNT_RATE", task_type="regression") + TargetColumn(name="DISCOUNT_RATE", task_type="regression", prediction_placeholder="[PREDICT]") ]), rows=rows_regression ) @@ -201,7 +201,7 @@ def test_timeout_error(self): body = RPTRequest( prediction_config=PredictionConfig( target_columns=[ - TargetColumn(name="DISCOUNT_RATE", task_type="regression") + TargetColumn(name="DISCOUNT_RATE", task_type="regression", prediction_placeholder="[PREDICT]") ]), rows=rows_regression ) From c162a26981ced75e63b2489086864505b0f675ff Mon Sep 17 00:00:00 2001 From: Yamac Ay Date: Fri, 2 Oct 2026 10:17:58 +0200 Subject: [PATCH 3/9] mock tests added + integration tests extended --- .../native_clients/test_sap_rpt.py | 91 +++++++++++++++++++ packages/gen/tests/mock.py | 48 +++++++++- .../gen/tests/proxy/native/test_sap_rpt.py | 87 +++++++++++++++++- 3 files changed, 222 insertions(+), 4 deletions(-) diff --git a/packages/gen/integration_tests/native_clients/test_sap_rpt.py b/packages/gen/integration_tests/native_clients/test_sap_rpt.py index 86bc47b3..23ec949a 100644 --- a/packages/gen/integration_tests/native_clients/test_sap_rpt.py +++ b/packages/gen/integration_tests/native_clients/test_sap_rpt.py @@ -210,6 +210,74 @@ def test_timeout_error(self): self.client.predict(body=body, model_name=SAP_RPT_1_SMALL_TEST_MODEL, timeout=0.001) + + def test_predict_classification_with_top_k(self): + body = RPTRequest(**{ + **request_by_columns_dict, + 'prediction_config': { + 'target_columns': [ + { + 'name': 'COSTCENTER', + 'prediction_placeholder': '[PREDICT]', + 'task_type': 'classification', + 'top_k': 2 + } + ] + } + }) + response = self.client.predict(body=body, model_name=SAP_RPT_1_SMALL_TEST_MODEL) + self.assertIsInstance(response, RPTResponse) + self.assertEqual(response.status.code, 0) + costcenter_predictions = response.predictions[0]['COSTCENTER'] + self.assertGreaterEqual(len(costcenter_predictions), 1) + + def test_predict_response_includes_context_mode(self): + body = RPTRequest(**request_by_columns_dict) + response = self.client.predict(body=body, model_name=SAP_RPT_1_SMALL_TEST_MODEL) + self.assertIsInstance(response, RPTResponse) + self.assertIn(response.metadata.context_mode, ['default', 'deep']) + + def test_regression_prediction_includes_confidence_interval(self): + body = RPTRequest( + prediction_config=PredictionConfig( + target_columns=[ + TargetColumn(name='DISCOUNT_RATE', task_type='regression', prediction_placeholder='[PREDICT]') + ]), + rows=rows_regression + ) + response = self.client.predict(body=body, model_name=SAP_RPT_1_SMALL_TEST_MODEL) + self.assertIsInstance(response, RPTResponse) + self.assertEqual(response.status.code, 0) + discount_predictions = response.predictions[0]['DISCOUNT_RATE'] + self.assertIsNotNone(discount_predictions[0].confidence_interval) + self.assertEqual(len(discount_predictions[0].confidence_interval), 2) + self.assertIsNone(discount_predictions[0].confidence) + + def test_predict_with_explanations(self): + body = RPTRequest(**{ + **request_by_columns_dict, + 'prediction_config': { + 'target_columns': [ + { + 'name': 'COSTCENTER', + 'prediction_placeholder': '[PREDICT]', + 'task_type': 'classification' + } + ], + 'explanations': { + 'top_column_scores': 3, + 'top_relevant_context_rows': 2 + } + } + }) + response = self.client.predict(body=body, model_name=SAP_RPT_1_SMALL_TEST_MODEL) + self.assertIsInstance(response, RPTResponse) + self.assertEqual(response.status.code, 0) + self.assertIsNotNone(response.explanations) + self.assertIsNotNone(response.explanations.top_column_scores) + self.assertIsNotNone(response.explanations.top_relevant_context_rows) + + class AsyncRPTClientTests(TestCaseStandardSetupMixin, unittest.IsolatedAsyncioTestCase): def setUp(self) -> None: @@ -234,3 +302,26 @@ async def test_apredict_by_columns(self): self.assertEqual(response.metadata.num_predictions, 1) self.assertIn("COSTCENTER", response.predictions[0].model_dump()) self.assertNotIn("ID", response.predictions[0].model_dump()) + + async def test_apredict_response_includes_context_mode(self): + body = RPTRequest(**request_by_columns_dict) + response = await self.client.apredict(body=body, model_name=SAP_RPT_1_SMALL_TEST_MODEL) + self.assertIsInstance(response, RPTResponse) + self.assertIn(response.metadata.context_mode, ['default', 'deep']) + + async def test_apredict_regression_includes_confidence_interval(self): + body = RPTRequest( + prediction_config=PredictionConfig( + target_columns=[ + TargetColumn(name='DISCOUNT_RATE', task_type='regression', prediction_placeholder='[PREDICT]') + ]), + rows=rows_regression + ) + response = await self.client.apredict(body=body, model_name=SAP_RPT_1_SMALL_TEST_MODEL) + self.assertIsInstance(response, RPTResponse) + self.assertEqual(response.status.code, 0) + discount_predictions = response.predictions[0]['DISCOUNT_RATE'] + self.assertIsNotNone(discount_predictions[0].confidence_interval) + self.assertEqual(len(discount_predictions[0].confidence_interval), 2) + self.assertIsNone(discount_predictions[0].confidence) + diff --git a/packages/gen/tests/mock.py b/packages/gen/tests/mock.py index 5633d326..bf977e21 100644 --- a/packages/gen/tests/mock.py +++ b/packages/gen/tests/mock.py @@ -1330,17 +1330,55 @@ def openai_completion_mocker(deployment_url): "COSTCENTER": [ { "prediction": "Office Furniture", - "confidence": 0.96 + "confidence": 0.96, + "confidence_interval": None } ], "ID": "35" } ], + "explanations": None, "metadata": { "num_columns": 5, "num_rows": 2, "num_predictions": 1, - "num_query_rows": 1 + "num_query_rows": 1, + "context_mode": "default" + } +} + +RPT_RESPONSE_CODE_0_WITH_EXPLANATIONS = { + "id": "d445a965-1e81-5d8a-ce84-0bd692e9dcbe", + "status": { + "code": 0, + "message": "ok" + }, + "predictions": [ + { + "DISCOUNT_RATE": [ + { + "prediction": 0.15, + "confidence": None, + "confidence_interval": [0.12, 0.18] + } + ], + "ID": "35" + } + ], + "explanations": { + "top_column_scores": [ + {"PRODUCT": 0.08, "ORDERDATE": 0.03} + ], + "top_relevant_context_rows": [ + [3, 4, 1] + ] + }, + "metadata": { + "num_columns": 5, + "num_rows": 3, + "num_predictions": 1, + "num_query_rows": 1, + "context_mode": "default" } } @@ -1666,6 +1704,12 @@ def sap_rpt_moke_response_code_0(url: str): respx.post(f"{url}/predict").mock(return_value=Response(200, json=RPT_RESPONSE_CODE_0)) yield +@contextmanager +def sap_rpt_moke_response_code_0_with_explanations(url: str): + with respx.mock: + respx.post(f"{url}/predict").mock(return_value=Response(200, json=RPT_RESPONSE_CODE_0_WITH_EXPLANATIONS)) + yield + RPT_RESPONSE_CODE_2 = { "status": { "code": 2, diff --git a/packages/gen/tests/proxy/native/test_sap_rpt.py b/packages/gen/tests/proxy/native/test_sap_rpt.py index f28c266a..fcfc0051 100644 --- a/packages/gen/tests/proxy/native/test_sap_rpt.py +++ b/packages/gen/tests/proxy/native/test_sap_rpt.py @@ -1,9 +1,17 @@ import unittest from unittest.mock import patch -from gen_ai_hub.proxy.native.sap.models import RPTRequest, PredictionConfig, TargetColumn, RPTResponse, RPTException, PredictionItem +from gen_ai_hub.proxy.native.sap.models import ( + RPTRequest, PredictionConfig, TargetColumn, RPTResponse, RPTException, + PredictionItem, ExplanationConfig, ExplanationResult +) from gen_ai_hub.proxy.native.sap.client import RPTClient -from tests.mock import get_mocked_ai_core_client, sap_rpt_moke_response_code_0, sap_rpt_moke_response_code_2 +from tests.mock import ( + get_mocked_ai_core_client, + sap_rpt_moke_response_code_0, + sap_rpt_moke_response_code_0_with_explanations, + sap_rpt_moke_response_code_2, +) mock_url = "https://mock-rpt-deployment" @@ -153,6 +161,81 @@ def test_prediction_item_confidence_below_min(self): with self.assertRaises(ValueError): PredictionItem(prediction="cat", confidence=-0.1) + + def test_target_column_top_k(self): + tc = TargetColumn(name="CATEGORY", prediction_placeholder="[PREDICT]", task_type="classification", top_k=3) + self.assertEqual(tc.top_k, 3) + self.assertEqual(tc.model_dump()["top_k"], 3) + + def test_target_column_top_k_default_none(self): + tc = TargetColumn(name="CATEGORY", prediction_placeholder="[PREDICT]", task_type="classification") + self.assertIsNone(tc.top_k) + + def test_prediction_item_confidence_interval_regression(self): + item = PredictionItem(prediction=195.09, confidence_interval=(191.42, 198.76)) + self.assertEqual(item.confidence_interval, (191.42, 198.76)) + self.assertIsNone(item.confidence) + + def test_prediction_item_confidence_interval_none_for_classification(self): + item = PredictionItem(prediction="Office Furniture", confidence=0.96, confidence_interval=None) + self.assertIsNone(item.confidence_interval) + self.assertEqual(item.confidence, 0.96) + + def test_explanation_config_defaults(self): + config = ExplanationConfig() + self.assertEqual(config.top_column_scores, 0) + self.assertEqual(config.top_relevant_context_rows, 0) + + def test_explanation_config_custom_values(self): + config = ExplanationConfig(top_column_scores=5, top_relevant_context_rows=3) + self.assertEqual(config.top_column_scores, 5) + self.assertEqual(config.top_relevant_context_rows, 3) + + def test_explanation_result_deserialization(self): + data = { + "top_column_scores": [{"PRODUCT": 0.08, "ORDERDATE": 0.03}], + "top_relevant_context_rows": [[3, 4, 1]] + } + result = ExplanationResult(**data) + self.assertEqual(result.top_column_scores[0]["PRODUCT"], 0.08) + self.assertEqual(result.top_relevant_context_rows[0], [3, 4, 1]) + + def test_explanation_result_nullable_fields(self): + result = ExplanationResult(top_column_scores=None, top_relevant_context_rows=None) + self.assertIsNone(result.top_column_scores) + self.assertIsNone(result.top_relevant_context_rows) + + def test_response_metadata_includes_context_mode(self): + from tests.mock import RPT_RESPONSE_CODE_0 + response = RPTResponse(**RPT_RESPONSE_CODE_0) + self.assertEqual(response.metadata.context_mode, "default") + + def test_response_explanations_none_by_default(self): + from tests.mock import RPT_RESPONSE_CODE_0 + response = RPTResponse(**RPT_RESPONSE_CODE_0) + self.assertIsNone(response.explanations) + + def test_response_with_explanations(self): + from tests.mock import RPT_RESPONSE_CODE_0_WITH_EXPLANATIONS + response = RPTResponse(**RPT_RESPONSE_CODE_0_WITH_EXPLANATIONS) + self.assertIsNotNone(response.explanations) + self.assertEqual(response.explanations.top_column_scores[0]["PRODUCT"], 0.08) + self.assertEqual(response.explanations.top_relevant_context_rows[0], [3, 4, 1]) + + def test_response_confidence_interval_in_regression_prediction(self): + from tests.mock import RPT_RESPONSE_CODE_0_WITH_EXPLANATIONS + response = RPTResponse(**RPT_RESPONSE_CODE_0_WITH_EXPLANATIONS) + discount_rate_predictions = response.predictions[0]["DISCOUNT_RATE"] + self.assertEqual(discount_rate_predictions[0]["confidence_interval"], [0.12, 0.18]) + self.assertIsNone(discount_rate_predictions[0]["confidence"]) + + def test_response_confidence_interval_none_for_classification(self): + from tests.mock import RPT_RESPONSE_CODE_0 + response = RPTResponse(**RPT_RESPONSE_CODE_0) + costcenter_predictions = response.predictions[0]["COSTCENTER"] + self.assertIsNone(costcenter_predictions[0].confidence_interval) + self.assertEqual(costcenter_predictions[0].confidence, 0.96) + def test_rpt_request_columns_and_rows_provided(self): with self.assertRaises(ValueError) as err: RPTRequest( From 5e60f6c82aed5b487e7052ebfd1bee78dc35a95e Mon Sep 17 00:00:00 2001 From: Yamac Ay Date: Fri, 2 Oct 2026 11:35:31 +0200 Subject: [PATCH 4/9] update to rpt 1.6 client for tests --- packages/gen/integration_tests/constants.py | 2 +- .../native_clients/test_sap_rpt.py | 34 +++++++++---------- .../gen/integration_tests/setup_aicore.py | 2 +- .../gen/tests/proxy/native/test_sap_rpt.py | 10 +++--- sample-code/README.md | 2 +- sample-code/sample_code/sap_rpt.py | 6 ++-- 6 files changed, 28 insertions(+), 28 deletions(-) diff --git a/packages/gen/integration_tests/constants.py b/packages/gen/integration_tests/constants.py index 053ece96..4d89e073 100644 --- a/packages/gen/integration_tests/constants.py +++ b/packages/gen/integration_tests/constants.py @@ -23,4 +23,4 @@ PERPLEXITY_TEST_MODEL = "sonar" PERPLEXITY_SONAR_DEEP_RESEARCH_TEST_MODEL = "sonar-deep-research" COHERE_COMMAND_A_TEST_MODEL = "cohere--command-a-reasoning" -SAP_RPT_1_SMALL_TEST_MODEL = "sap-rpt-1-small" +SAP_RPT_1_6_SMALL_TEST_MODEL = "sap-rpt-1.6-small" diff --git a/packages/gen/integration_tests/native_clients/test_sap_rpt.py b/packages/gen/integration_tests/native_clients/test_sap_rpt.py index 23ec949a..f852ee4e 100644 --- a/packages/gen/integration_tests/native_clients/test_sap_rpt.py +++ b/packages/gen/integration_tests/native_clients/test_sap_rpt.py @@ -1,6 +1,6 @@ import unittest -from integration_tests.constants import SAP_RPT_1_SMALL_TEST_MODEL +from integration_tests.constants import SAP_RPT_1_6_SMALL_TEST_MODEL from integration_tests.setup_aicore import TestCaseStandardSetupMixin from gen_ai_hub.proxy.native.sap.client import RPTClient from gen_ai_hub.proxy.native.sap.models import RPTRequest, RPTResponse, PredictionConfig, TargetColumn @@ -138,15 +138,15 @@ def setUp(self) -> None: self.client = RPTClient(proxy_client=self.proxy_client) def test_client_find_url_by_model_name(self): - url = self.client._get_url(model_name=SAP_RPT_1_SMALL_TEST_MODEL) + url = self.client._get_url(model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) self.assertIsNotNone(url) def test_client_find_url_by_model_name_and_version(self): - url = self.client._get_url(model_name=SAP_RPT_1_SMALL_TEST_MODEL, model_version="latest") + url = self.client._get_url(model_name=SAP_RPT_1_6_SMALL_TEST_MODEL, model_version="latest") self.assertIsNotNone(url) def test_client_find_url_by_config_name(self): - url = self.client._get_url(config_name="sap-rpt-1-small-latest") + url = self.client._get_url(config_name="sap-rpt-1.6-small-latest") self.assertIsNotNone(url) def test_client_find_url_with_invalid_model_name(self): @@ -155,7 +155,7 @@ def test_client_find_url_with_invalid_model_name(self): def test_predict_by_row(self): body = RPTRequest(**request_by_row_dict) - response = self.client.predict(body=body, model_name=SAP_RPT_1_SMALL_TEST_MODEL) + response = self.client.predict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) self.assertEqual(response.predictions[0]["ID"], "35") @@ -165,7 +165,7 @@ def test_predict_by_row(self): def test_predict_by_columns(self): body = RPTRequest(**request_by_columns_dict) - response = self.client.predict(body=body, model_name=SAP_RPT_1_SMALL_TEST_MODEL) + response = self.client.predict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) self.assertEqual(response.metadata.num_columns, 5) @@ -175,7 +175,7 @@ def test_predict_by_columns(self): def test_predict_with_api_url(self): body = RPTRequest(**request_by_columns_dict) - deployment_url = self.client._get_url(model_name=SAP_RPT_1_SMALL_TEST_MODEL) + deployment_url = self.client._get_url(model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) response = self.client.predict(body=body, deployment_url=deployment_url) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) @@ -191,7 +191,7 @@ def test_regression_prediction(self): ]), rows=rows_regression ) - response = self.client.predict(body=body, model_name=SAP_RPT_1_SMALL_TEST_MODEL) + response = self.client.predict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) self.assertEqual(response.metadata.num_predictions, 2) @@ -207,7 +207,7 @@ def test_timeout_error(self): ) with self.assertRaises(Exception): - self.client.predict(body=body, model_name=SAP_RPT_1_SMALL_TEST_MODEL, timeout=0.001) + self.client.predict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL, timeout=0.001) @@ -225,7 +225,7 @@ def test_predict_classification_with_top_k(self): ] } }) - response = self.client.predict(body=body, model_name=SAP_RPT_1_SMALL_TEST_MODEL) + response = self.client.predict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) costcenter_predictions = response.predictions[0]['COSTCENTER'] @@ -233,7 +233,7 @@ def test_predict_classification_with_top_k(self): def test_predict_response_includes_context_mode(self): body = RPTRequest(**request_by_columns_dict) - response = self.client.predict(body=body, model_name=SAP_RPT_1_SMALL_TEST_MODEL) + response = self.client.predict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertIn(response.metadata.context_mode, ['default', 'deep']) @@ -245,7 +245,7 @@ def test_regression_prediction_includes_confidence_interval(self): ]), rows=rows_regression ) - response = self.client.predict(body=body, model_name=SAP_RPT_1_SMALL_TEST_MODEL) + response = self.client.predict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) discount_predictions = response.predictions[0]['DISCOUNT_RATE'] @@ -270,7 +270,7 @@ def test_predict_with_explanations(self): } } }) - response = self.client.predict(body=body, model_name=SAP_RPT_1_SMALL_TEST_MODEL) + response = self.client.predict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) self.assertIsNotNone(response.explanations) @@ -285,7 +285,7 @@ def setUp(self) -> None: async def test_apredict_by_row(self): body = RPTRequest(**request_by_row_dict) - response = await self.client.apredict(body=body, model_name=SAP_RPT_1_SMALL_TEST_MODEL) + response = await self.client.apredict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) self.assertEqual(response.predictions[0]["ID"], "35") @@ -295,7 +295,7 @@ async def test_apredict_by_row(self): async def test_apredict_by_columns(self): body = RPTRequest(**request_by_columns_dict) - response = await self.client.apredict(body=body, model_name=SAP_RPT_1_SMALL_TEST_MODEL) + response = await self.client.apredict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) self.assertEqual(response.metadata.num_columns, 5) @@ -305,7 +305,7 @@ async def test_apredict_by_columns(self): async def test_apredict_response_includes_context_mode(self): body = RPTRequest(**request_by_columns_dict) - response = await self.client.apredict(body=body, model_name=SAP_RPT_1_SMALL_TEST_MODEL) + response = await self.client.apredict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertIn(response.metadata.context_mode, ['default', 'deep']) @@ -317,7 +317,7 @@ async def test_apredict_regression_includes_confidence_interval(self): ]), rows=rows_regression ) - response = await self.client.apredict(body=body, model_name=SAP_RPT_1_SMALL_TEST_MODEL) + response = await self.client.apredict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) discount_predictions = response.predictions[0]['DISCOUNT_RATE'] diff --git a/packages/gen/integration_tests/setup_aicore.py b/packages/gen/integration_tests/setup_aicore.py index e522ff22..8ae33a58 100644 --- a/packages/gen/integration_tests/setup_aicore.py +++ b/packages/gen/integration_tests/setup_aicore.py @@ -175,7 +175,7 @@ def get_standard_models(): ("sonar", "latest"), ("sonar-deep-research", "latest"), ("cohere--command-a-reasoning", "latest"), - ("sap-rpt-1-small","latest"), + ("sap-rpt-1.6-small","latest"), ] diff --git a/packages/gen/tests/proxy/native/test_sap_rpt.py b/packages/gen/tests/proxy/native/test_sap_rpt.py index fcfc0051..57534fa7 100644 --- a/packages/gen/tests/proxy/native/test_sap_rpt.py +++ b/packages/gen/tests/proxy/native/test_sap_rpt.py @@ -254,7 +254,7 @@ def setUp(self): def test_request_with_response_code_0(self): with patch.object(RPTClient, "_get_url", return_value=mock_url) as url_mock: with sap_rpt_moke_response_code_0(url_mock.return_value): - response = self.client.predict(body=request_by_row_dict, model_name="sap-rpt-1-small") + response = self.client.predict(body=request_by_row_dict, model_name="sap-rpt-1.6-small") self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) self.assertEqual(response.predictions[0]["COSTCENTER"][0].prediction, "Office Furniture") @@ -273,13 +273,13 @@ def test_request_with_response_code_2(self): with patch.object(RPTClient, "_get_url", return_value=mock_url) as url_mock: with sap_rpt_moke_response_code_2(url_mock.return_value): with self.assertRaises(RPTException) as err: - self.client.predict(body=request_by_row_dict, model_name="sap-rpt-1-small") + self.client.predict(body=request_by_row_dict, model_name="sap-rpt-1.6-small") self.assertEqual(err.exception.status.code, 2) self.assertIsNotNone(err.exception.detail) def test_request_with_invalid_body(self): with self.assertRaises(ValueError): - self.client.predict(body={}, model_name="sap-rpt-1-small") + self.client.predict(body={}, model_name="sap-rpt-1.6-small") def test_request_without_model_name_api_url_and_kwargs(self): with self.assertRaises(ValueError): @@ -297,7 +297,7 @@ def setUp(self): async def test_async_request_with_response_code_0(self): with patch.object(RPTClient, "_get_url", return_value=mock_url) as url_mock: with sap_rpt_moke_response_code_0(url_mock.return_value): - response = await self.client.apredict(body=request_by_row_dict, model_name="sap-rpt-1-small") + response = await self.client.apredict(body=request_by_row_dict, model_name="sap-rpt-1.6-small") self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) self.assertEqual(response.predictions[0]["COSTCENTER"][0].prediction, "Office Furniture") @@ -308,7 +308,7 @@ async def test_async_request_with_response_code_2(self): with patch.object(RPTClient, "_get_url", return_value=mock_url) as url_mock: with sap_rpt_moke_response_code_2(url_mock.return_value): with self.assertRaises(RPTException) as err: - await self.client.apredict(body=request_by_row_dict, model_name="sap-rpt-1-small") + await self.client.apredict(body=request_by_row_dict, model_name="sap-rpt-1.6-small") self.assertEqual(err.exception.status.code, 2) self.assertIsNotNone(err.exception.detail) diff --git a/sample-code/README.md b/sample-code/README.md index fc34764f..058371fa 100644 --- a/sample-code/README.md +++ b/sample-code/README.md @@ -14,7 +14,7 @@ Before running the application, ensure the following prerequisites are met: - `text-embedding-3-small` - `anthropic--claude-4.6-sonnet` - `gemini-3.5-flash` - - `sap-rpt-1-small` + - `sap-rpt-1.6-small` ## Local Deployment diff --git a/sample-code/sample_code/sap_rpt.py b/sample-code/sample_code/sap_rpt.py index 6e47db0d..785d9d65 100644 --- a/sample-code/sample_code/sap_rpt.py +++ b/sample-code/sample_code/sap_rpt.py @@ -65,7 +65,7 @@ def predict_by_rows(): rows=CLASSIFICATION_ROWS, data_schema=CLASSIFICATION_SCHEMA, ) - return client.predict(body=body, model_name="sap-rpt-1-small") + return client.predict(body=body, model_name="sap-rpt-1.6-small") def predict_by_columns(): @@ -91,7 +91,7 @@ def predict_by_columns(): columns=CLASSIFICATION_COLUMNS, data_schema=CLASSIFICATION_SCHEMA, ) - return client.predict(body=body, model_name="sap-rpt-1-small") + return client.predict(body=body, model_name="sap-rpt-1.6-small") def regression(): @@ -112,4 +112,4 @@ def regression(): rows=REGRESSION_ROWS, data_schema=REGRESSION_SCHEMA, ) - return client.predict(body=body, model_name="sap-rpt-1-small") \ No newline at end of file + return client.predict(body=body, model_name="sap-rpt-1.6-small") \ No newline at end of file From a75d563b2e2d21305b2ed6db9c0eaa70d08a200b Mon Sep 17 00:00:00 2001 From: Yamac Ay Date: Fri, 2 Oct 2026 16:00:20 +0200 Subject: [PATCH 5/9] fix: rename rpt model --- packages/gen/integration_tests/constants.py | 2 +- .../integration_tests/native_clients/test_sap_rpt.py | 2 +- packages/gen/integration_tests/setup_aicore.py | 2 +- packages/gen/tests/proxy/native/test_sap_rpt.py | 10 +++++----- sample-code/README.md | 2 +- sample-code/sample_code/sap_rpt.py | 6 +++--- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/gen/integration_tests/constants.py b/packages/gen/integration_tests/constants.py index 4d89e073..af58466f 100644 --- a/packages/gen/integration_tests/constants.py +++ b/packages/gen/integration_tests/constants.py @@ -23,4 +23,4 @@ PERPLEXITY_TEST_MODEL = "sonar" PERPLEXITY_SONAR_DEEP_RESEARCH_TEST_MODEL = "sonar-deep-research" COHERE_COMMAND_A_TEST_MODEL = "cohere--command-a-reasoning" -SAP_RPT_1_6_SMALL_TEST_MODEL = "sap-rpt-1.6-small" +SAP_RPT_1_6_SMALL_TEST_MODEL = "sap-rpt-1.6" diff --git a/packages/gen/integration_tests/native_clients/test_sap_rpt.py b/packages/gen/integration_tests/native_clients/test_sap_rpt.py index f852ee4e..9fe48049 100644 --- a/packages/gen/integration_tests/native_clients/test_sap_rpt.py +++ b/packages/gen/integration_tests/native_clients/test_sap_rpt.py @@ -146,7 +146,7 @@ def test_client_find_url_by_model_name_and_version(self): self.assertIsNotNone(url) def test_client_find_url_by_config_name(self): - url = self.client._get_url(config_name="sap-rpt-1.6-small-latest") + url = self.client._get_url(config_name="sap-rpt-1.6-latest") self.assertIsNotNone(url) def test_client_find_url_with_invalid_model_name(self): diff --git a/packages/gen/integration_tests/setup_aicore.py b/packages/gen/integration_tests/setup_aicore.py index 8ae33a58..2f819b5b 100644 --- a/packages/gen/integration_tests/setup_aicore.py +++ b/packages/gen/integration_tests/setup_aicore.py @@ -175,7 +175,7 @@ def get_standard_models(): ("sonar", "latest"), ("sonar-deep-research", "latest"), ("cohere--command-a-reasoning", "latest"), - ("sap-rpt-1.6-small","latest"), + ("sap-rpt-1.6","latest"), ] diff --git a/packages/gen/tests/proxy/native/test_sap_rpt.py b/packages/gen/tests/proxy/native/test_sap_rpt.py index 57534fa7..2887824b 100644 --- a/packages/gen/tests/proxy/native/test_sap_rpt.py +++ b/packages/gen/tests/proxy/native/test_sap_rpt.py @@ -254,7 +254,7 @@ def setUp(self): def test_request_with_response_code_0(self): with patch.object(RPTClient, "_get_url", return_value=mock_url) as url_mock: with sap_rpt_moke_response_code_0(url_mock.return_value): - response = self.client.predict(body=request_by_row_dict, model_name="sap-rpt-1.6-small") + response = self.client.predict(body=request_by_row_dict, model_name="sap-rpt-1.6") self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) self.assertEqual(response.predictions[0]["COSTCENTER"][0].prediction, "Office Furniture") @@ -273,13 +273,13 @@ def test_request_with_response_code_2(self): with patch.object(RPTClient, "_get_url", return_value=mock_url) as url_mock: with sap_rpt_moke_response_code_2(url_mock.return_value): with self.assertRaises(RPTException) as err: - self.client.predict(body=request_by_row_dict, model_name="sap-rpt-1.6-small") + self.client.predict(body=request_by_row_dict, model_name="sap-rpt-1.6") self.assertEqual(err.exception.status.code, 2) self.assertIsNotNone(err.exception.detail) def test_request_with_invalid_body(self): with self.assertRaises(ValueError): - self.client.predict(body={}, model_name="sap-rpt-1.6-small") + self.client.predict(body={}, model_name="sap-rpt-1.6") def test_request_without_model_name_api_url_and_kwargs(self): with self.assertRaises(ValueError): @@ -297,7 +297,7 @@ def setUp(self): async def test_async_request_with_response_code_0(self): with patch.object(RPTClient, "_get_url", return_value=mock_url) as url_mock: with sap_rpt_moke_response_code_0(url_mock.return_value): - response = await self.client.apredict(body=request_by_row_dict, model_name="sap-rpt-1.6-small") + response = await self.client.apredict(body=request_by_row_dict, model_name="sap-rpt-1.6") self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) self.assertEqual(response.predictions[0]["COSTCENTER"][0].prediction, "Office Furniture") @@ -308,7 +308,7 @@ async def test_async_request_with_response_code_2(self): with patch.object(RPTClient, "_get_url", return_value=mock_url) as url_mock: with sap_rpt_moke_response_code_2(url_mock.return_value): with self.assertRaises(RPTException) as err: - await self.client.apredict(body=request_by_row_dict, model_name="sap-rpt-1.6-small") + await self.client.apredict(body=request_by_row_dict, model_name="sap-rpt-1.6") self.assertEqual(err.exception.status.code, 2) self.assertIsNotNone(err.exception.detail) diff --git a/sample-code/README.md b/sample-code/README.md index 058371fa..4b513e42 100644 --- a/sample-code/README.md +++ b/sample-code/README.md @@ -14,7 +14,7 @@ Before running the application, ensure the following prerequisites are met: - `text-embedding-3-small` - `anthropic--claude-4.6-sonnet` - `gemini-3.5-flash` - - `sap-rpt-1.6-small` + - `sap-rpt-1.6` ## Local Deployment diff --git a/sample-code/sample_code/sap_rpt.py b/sample-code/sample_code/sap_rpt.py index 785d9d65..55e18430 100644 --- a/sample-code/sample_code/sap_rpt.py +++ b/sample-code/sample_code/sap_rpt.py @@ -65,7 +65,7 @@ def predict_by_rows(): rows=CLASSIFICATION_ROWS, data_schema=CLASSIFICATION_SCHEMA, ) - return client.predict(body=body, model_name="sap-rpt-1.6-small") + return client.predict(body=body, model_name="sap-rpt-1.6") def predict_by_columns(): @@ -91,7 +91,7 @@ def predict_by_columns(): columns=CLASSIFICATION_COLUMNS, data_schema=CLASSIFICATION_SCHEMA, ) - return client.predict(body=body, model_name="sap-rpt-1.6-small") + return client.predict(body=body, model_name="sap-rpt-1.6") def regression(): @@ -112,4 +112,4 @@ def regression(): rows=REGRESSION_ROWS, data_schema=REGRESSION_SCHEMA, ) - return client.predict(body=body, model_name="sap-rpt-1.6-small") \ No newline at end of file + return client.predict(body=body, model_name="sap-rpt-1.6") \ No newline at end of file From a096f14d2a3d4f2ca4721930245e15a44938d397 Mon Sep 17 00:00:00 2001 From: Yamac Ay Date: Mon, 5 Oct 2026 09:52:28 +0200 Subject: [PATCH 6/9] update confidence interval + mock tests + happy path testing --- .../gen/gen_ai_hub/proxy/native/sap/models.py | 4 +- packages/gen/tests/mock.py | 6 +-- .../gen/tests/proxy/native/test_sap_rpt.py | 43 ++++++++++++------- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/packages/gen/gen_ai_hub/proxy/native/sap/models.py b/packages/gen/gen_ai_hub/proxy/native/sap/models.py index 1fa65ba2..db24ed68 100644 --- a/packages/gen/gen_ai_hub/proxy/native/sap/models.py +++ b/packages/gen/gen_ai_hub/proxy/native/sap/models.py @@ -1,4 +1,4 @@ -from typing import Optional, Literal, Union, Any +from typing import Annotated, Optional, Literal, Union, Any from pydantic import BaseModel, Field, RootModel, model_validator @@ -189,7 +189,7 @@ class PredictionItem(BaseModel): prediction: Union[str, float] confidence: Optional[float] = Field(default=None, ge=0.0, le=1.0) - confidence_interval: Optional[tuple[float, float]] = None + confidence_interval: Optional[Annotated[list[float], Field(min_length=2, max_length=2)]] = None class Prediction(RootModel[dict[str, Union[list[PredictionItem], Any]]]): diff --git a/packages/gen/tests/mock.py b/packages/gen/tests/mock.py index bf977e21..e555975b 100644 --- a/packages/gen/tests/mock.py +++ b/packages/gen/tests/mock.py @@ -1699,13 +1699,13 @@ def openai_responses_structured_outputs_mocker(deployment_url): yield @contextmanager -def sap_rpt_moke_response_code_0(url: str): +def sap_rpt_mock_response_code_0(url: str): with respx.mock: respx.post(f"{url}/predict").mock(return_value=Response(200, json=RPT_RESPONSE_CODE_0)) yield @contextmanager -def sap_rpt_moke_response_code_0_with_explanations(url: str): +def sap_rpt_mock_response_code_0_with_explanations(url: str): with respx.mock: respx.post(f"{url}/predict").mock(return_value=Response(200, json=RPT_RESPONSE_CODE_0_WITH_EXPLANATIONS)) yield @@ -1730,7 +1730,7 @@ def sap_rpt_moke_response_code_0_with_explanations(url: str): } @contextmanager -def sap_rpt_moke_response_code_2(url: str): +def sap_rpt_mock_response_code_2(url: str): with respx.mock: respx.post(f"{url}/predict").mock(return_value=Response(422, json=RPT_RESPONSE_CODE_2)) yield diff --git a/packages/gen/tests/proxy/native/test_sap_rpt.py b/packages/gen/tests/proxy/native/test_sap_rpt.py index 2887824b..a4decc27 100644 --- a/packages/gen/tests/proxy/native/test_sap_rpt.py +++ b/packages/gen/tests/proxy/native/test_sap_rpt.py @@ -8,9 +8,11 @@ from gen_ai_hub.proxy.native.sap.client import RPTClient from tests.mock import ( get_mocked_ai_core_client, - sap_rpt_moke_response_code_0, - sap_rpt_moke_response_code_0_with_explanations, - sap_rpt_moke_response_code_2, + sap_rpt_mock_response_code_0, + sap_rpt_mock_response_code_0_with_explanations, + sap_rpt_mock_response_code_2, + RPT_RESPONSE_CODE_0, + RPT_RESPONSE_CODE_0_WITH_EXPLANATIONS, ) mock_url = "https://mock-rpt-deployment" @@ -173,7 +175,7 @@ def test_target_column_top_k_default_none(self): def test_prediction_item_confidence_interval_regression(self): item = PredictionItem(prediction=195.09, confidence_interval=(191.42, 198.76)) - self.assertEqual(item.confidence_interval, (191.42, 198.76)) + self.assertEqual(item.confidence_interval, [191.42, 198.76]) self.assertIsNone(item.confidence) def test_prediction_item_confidence_interval_none_for_classification(self): @@ -206,31 +208,26 @@ def test_explanation_result_nullable_fields(self): self.assertIsNone(result.top_relevant_context_rows) def test_response_metadata_includes_context_mode(self): - from tests.mock import RPT_RESPONSE_CODE_0 response = RPTResponse(**RPT_RESPONSE_CODE_0) self.assertEqual(response.metadata.context_mode, "default") def test_response_explanations_none_by_default(self): - from tests.mock import RPT_RESPONSE_CODE_0 response = RPTResponse(**RPT_RESPONSE_CODE_0) self.assertIsNone(response.explanations) def test_response_with_explanations(self): - from tests.mock import RPT_RESPONSE_CODE_0_WITH_EXPLANATIONS response = RPTResponse(**RPT_RESPONSE_CODE_0_WITH_EXPLANATIONS) self.assertIsNotNone(response.explanations) self.assertEqual(response.explanations.top_column_scores[0]["PRODUCT"], 0.08) self.assertEqual(response.explanations.top_relevant_context_rows[0], [3, 4, 1]) def test_response_confidence_interval_in_regression_prediction(self): - from tests.mock import RPT_RESPONSE_CODE_0_WITH_EXPLANATIONS response = RPTResponse(**RPT_RESPONSE_CODE_0_WITH_EXPLANATIONS) discount_rate_predictions = response.predictions[0]["DISCOUNT_RATE"] - self.assertEqual(discount_rate_predictions[0]["confidence_interval"], [0.12, 0.18]) - self.assertIsNone(discount_rate_predictions[0]["confidence"]) + self.assertEqual(discount_rate_predictions[0].confidence_interval, [0.12, 0.18]) + self.assertIsNone(discount_rate_predictions[0].confidence) def test_response_confidence_interval_none_for_classification(self): - from tests.mock import RPT_RESPONSE_CODE_0 response = RPTResponse(**RPT_RESPONSE_CODE_0) costcenter_predictions = response.predictions[0]["COSTCENTER"] self.assertIsNone(costcenter_predictions[0].confidence_interval) @@ -253,7 +250,7 @@ def setUp(self): def test_request_with_response_code_0(self): with patch.object(RPTClient, "_get_url", return_value=mock_url) as url_mock: - with sap_rpt_moke_response_code_0(url_mock.return_value): + with sap_rpt_mock_response_code_0(url_mock.return_value): response = self.client.predict(body=request_by_row_dict, model_name="sap-rpt-1.6") self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) @@ -262,7 +259,7 @@ def test_request_with_response_code_0(self): self.assertEqual(response.metadata.num_predictions,1) def test_request_with_response_code_0_request_by_api_url(self): - with sap_rpt_moke_response_code_0(mock_url): + with sap_rpt_mock_response_code_0(mock_url): response = self.client.predict(body=request_by_row_dict, deployment_url=mock_url) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) @@ -271,7 +268,7 @@ def test_request_with_response_code_0_request_by_api_url(self): def test_request_with_response_code_2(self): with patch.object(RPTClient, "_get_url", return_value=mock_url) as url_mock: - with sap_rpt_moke_response_code_2(url_mock.return_value): + with sap_rpt_mock_response_code_2(url_mock.return_value): with self.assertRaises(RPTException) as err: self.client.predict(body=request_by_row_dict, model_name="sap-rpt-1.6") self.assertEqual(err.exception.status.code, 2) @@ -285,6 +282,20 @@ def test_request_without_model_name_api_url_and_kwargs(self): with self.assertRaises(ValueError): self.client.predict(body=request_by_row_dict) + def test_request_happy_path_rpt_1_0(self): + with patch.object(RPTClient, "_get_url", return_value=mock_url) as url_mock: + with sap_rpt_mock_response_code_0(url_mock.return_value): + response = self.client.predict(body=request_by_row_dict, model_name="sap-rpt-1-small") + self.assertIsInstance(response, RPTResponse) + self.assertEqual(response.status.code, 0) + + def test_request_happy_path_rpt_1_5(self): + with patch.object(RPTClient, "_get_url", return_value=mock_url) as url_mock: + with sap_rpt_mock_response_code_0(url_mock.return_value): + response = self.client.predict(body=request_by_row_dict, model_name="sap-rpt-1.5") + self.assertIsInstance(response, RPTResponse) + self.assertEqual(response.status.code, 0) + def test_timeout_determination(self): self.assertEqual(self.client._determine_timeout(10), 10) @@ -296,7 +307,7 @@ def setUp(self): async def test_async_request_with_response_code_0(self): with patch.object(RPTClient, "_get_url", return_value=mock_url) as url_mock: - with sap_rpt_moke_response_code_0(url_mock.return_value): + with sap_rpt_mock_response_code_0(url_mock.return_value): response = await self.client.apredict(body=request_by_row_dict, model_name="sap-rpt-1.6") self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) @@ -306,7 +317,7 @@ async def test_async_request_with_response_code_0(self): async def test_async_request_with_response_code_2(self): with patch.object(RPTClient, "_get_url", return_value=mock_url) as url_mock: - with sap_rpt_moke_response_code_2(url_mock.return_value): + with sap_rpt_mock_response_code_2(url_mock.return_value): with self.assertRaises(RPTException) as err: await self.client.apredict(body=request_by_row_dict, model_name="sap-rpt-1.6") self.assertEqual(err.exception.status.code, 2) From 35b9c6ed567353e080f4a7d4e1f1be5e9bae9e63 Mon Sep 17 00:00:00 2001 From: Yamac Ay Date: Mon, 5 Oct 2026 13:03:46 +0200 Subject: [PATCH 7/9] add missing sap models to the list --- sample-code/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sample-code/README.md b/sample-code/README.md index 091699db..e0137895 100644 --- a/sample-code/README.md +++ b/sample-code/README.md @@ -14,6 +14,8 @@ Before running the application, ensure the following prerequisites are met: - `text-embedding-3-small` - `anthropic--claude-4.6-sonnet` - `gemini-3.5-flash` + - `sap-rpt-1-small` + - `sap-rpt-1.5` - `sap-rpt-1.6` ## Local Deployment From 04e6dd83a15d0a5de51cb315e68eb5adf2f6e585 Mon Sep 17 00:00:00 2001 From: Yamac Ay Date: Tue, 6 Oct 2026 12:16:45 +0200 Subject: [PATCH 8/9] var renames and rm unused models --- packages/gen/integration_tests/constants.py | 2 +- .../native_clients/test_sap_rpt.py | 32 +++++++++---------- sample-code/README.md | 2 -- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/packages/gen/integration_tests/constants.py b/packages/gen/integration_tests/constants.py index af58466f..21c74867 100644 --- a/packages/gen/integration_tests/constants.py +++ b/packages/gen/integration_tests/constants.py @@ -23,4 +23,4 @@ PERPLEXITY_TEST_MODEL = "sonar" PERPLEXITY_SONAR_DEEP_RESEARCH_TEST_MODEL = "sonar-deep-research" COHERE_COMMAND_A_TEST_MODEL = "cohere--command-a-reasoning" -SAP_RPT_1_6_SMALL_TEST_MODEL = "sap-rpt-1.6" +SAP_RPT_1_6_TEST_MODEL = "sap-rpt-1.6" diff --git a/packages/gen/integration_tests/native_clients/test_sap_rpt.py b/packages/gen/integration_tests/native_clients/test_sap_rpt.py index 9fe48049..77c02787 100644 --- a/packages/gen/integration_tests/native_clients/test_sap_rpt.py +++ b/packages/gen/integration_tests/native_clients/test_sap_rpt.py @@ -1,6 +1,6 @@ import unittest -from integration_tests.constants import SAP_RPT_1_6_SMALL_TEST_MODEL +from integration_tests.constants import SAP_RPT_1_6_TEST_MODEL from integration_tests.setup_aicore import TestCaseStandardSetupMixin from gen_ai_hub.proxy.native.sap.client import RPTClient from gen_ai_hub.proxy.native.sap.models import RPTRequest, RPTResponse, PredictionConfig, TargetColumn @@ -138,11 +138,11 @@ def setUp(self) -> None: self.client = RPTClient(proxy_client=self.proxy_client) def test_client_find_url_by_model_name(self): - url = self.client._get_url(model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) + url = self.client._get_url(model_name=SAP_RPT_1_6_TEST_MODEL) self.assertIsNotNone(url) def test_client_find_url_by_model_name_and_version(self): - url = self.client._get_url(model_name=SAP_RPT_1_6_SMALL_TEST_MODEL, model_version="latest") + url = self.client._get_url(model_name=SAP_RPT_1_6_TEST_MODEL, model_version="latest") self.assertIsNotNone(url) def test_client_find_url_by_config_name(self): @@ -155,7 +155,7 @@ def test_client_find_url_with_invalid_model_name(self): def test_predict_by_row(self): body = RPTRequest(**request_by_row_dict) - response = self.client.predict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) + response = self.client.predict(body=body, model_name=SAP_RPT_1_6_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) self.assertEqual(response.predictions[0]["ID"], "35") @@ -165,7 +165,7 @@ def test_predict_by_row(self): def test_predict_by_columns(self): body = RPTRequest(**request_by_columns_dict) - response = self.client.predict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) + response = self.client.predict(body=body, model_name=SAP_RPT_1_6_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) self.assertEqual(response.metadata.num_columns, 5) @@ -175,7 +175,7 @@ def test_predict_by_columns(self): def test_predict_with_api_url(self): body = RPTRequest(**request_by_columns_dict) - deployment_url = self.client._get_url(model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) + deployment_url = self.client._get_url(model_name=SAP_RPT_1_6_TEST_MODEL) response = self.client.predict(body=body, deployment_url=deployment_url) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) @@ -191,7 +191,7 @@ def test_regression_prediction(self): ]), rows=rows_regression ) - response = self.client.predict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) + response = self.client.predict(body=body, model_name=SAP_RPT_1_6_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) self.assertEqual(response.metadata.num_predictions, 2) @@ -207,7 +207,7 @@ def test_timeout_error(self): ) with self.assertRaises(Exception): - self.client.predict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL, timeout=0.001) + self.client.predict(body=body, model_name=SAP_RPT_1_6_TEST_MODEL, timeout=0.001) @@ -225,7 +225,7 @@ def test_predict_classification_with_top_k(self): ] } }) - response = self.client.predict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) + response = self.client.predict(body=body, model_name=SAP_RPT_1_6_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) costcenter_predictions = response.predictions[0]['COSTCENTER'] @@ -233,7 +233,7 @@ def test_predict_classification_with_top_k(self): def test_predict_response_includes_context_mode(self): body = RPTRequest(**request_by_columns_dict) - response = self.client.predict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) + response = self.client.predict(body=body, model_name=SAP_RPT_1_6_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertIn(response.metadata.context_mode, ['default', 'deep']) @@ -245,7 +245,7 @@ def test_regression_prediction_includes_confidence_interval(self): ]), rows=rows_regression ) - response = self.client.predict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) + response = self.client.predict(body=body, model_name=SAP_RPT_1_6_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) discount_predictions = response.predictions[0]['DISCOUNT_RATE'] @@ -270,7 +270,7 @@ def test_predict_with_explanations(self): } } }) - response = self.client.predict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) + response = self.client.predict(body=body, model_name=SAP_RPT_1_6_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) self.assertIsNotNone(response.explanations) @@ -285,7 +285,7 @@ def setUp(self) -> None: async def test_apredict_by_row(self): body = RPTRequest(**request_by_row_dict) - response = await self.client.apredict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) + response = await self.client.apredict(body=body, model_name=SAP_RPT_1_6_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) self.assertEqual(response.predictions[0]["ID"], "35") @@ -295,7 +295,7 @@ async def test_apredict_by_row(self): async def test_apredict_by_columns(self): body = RPTRequest(**request_by_columns_dict) - response = await self.client.apredict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) + response = await self.client.apredict(body=body, model_name=SAP_RPT_1_6_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) self.assertEqual(response.metadata.num_columns, 5) @@ -305,7 +305,7 @@ async def test_apredict_by_columns(self): async def test_apredict_response_includes_context_mode(self): body = RPTRequest(**request_by_columns_dict) - response = await self.client.apredict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) + response = await self.client.apredict(body=body, model_name=SAP_RPT_1_6_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertIn(response.metadata.context_mode, ['default', 'deep']) @@ -317,7 +317,7 @@ async def test_apredict_regression_includes_confidence_interval(self): ]), rows=rows_regression ) - response = await self.client.apredict(body=body, model_name=SAP_RPT_1_6_SMALL_TEST_MODEL) + response = await self.client.apredict(body=body, model_name=SAP_RPT_1_6_TEST_MODEL) self.assertIsInstance(response, RPTResponse) self.assertEqual(response.status.code, 0) discount_predictions = response.predictions[0]['DISCOUNT_RATE'] diff --git a/sample-code/README.md b/sample-code/README.md index e0137895..091699db 100644 --- a/sample-code/README.md +++ b/sample-code/README.md @@ -14,8 +14,6 @@ Before running the application, ensure the following prerequisites are met: - `text-embedding-3-small` - `anthropic--claude-4.6-sonnet` - `gemini-3.5-flash` - - `sap-rpt-1-small` - - `sap-rpt-1.5` - `sap-rpt-1.6` ## Local Deployment From 11dbec1ac5598b13fcdaa8b1615296a884241a0c Mon Sep 17 00:00:00 2001 From: Yamac Ay Date: Tue, 6 Oct 2026 12:21:51 +0200 Subject: [PATCH 9/9] add type validation --- packages/gen/gen_ai_hub/proxy/native/sap/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/gen/gen_ai_hub/proxy/native/sap/models.py b/packages/gen/gen_ai_hub/proxy/native/sap/models.py index db24ed68..85315060 100644 --- a/packages/gen/gen_ai_hub/proxy/native/sap/models.py +++ b/packages/gen/gen_ai_hub/proxy/native/sap/models.py @@ -32,8 +32,8 @@ class ExplanationConfig(BaseModel): :type top_relevant_context_rows: int """ - top_column_scores: int = 0 - top_relevant_context_rows: int = 0 + top_column_scores: int = Field(default=0, ge=0, le=20) + top_relevant_context_rows: int = Field(default=0, ge=0, le=20) class ExplanationResult(BaseModel):