diff --git a/packages/gen/codegen/.openapi-generator-ignore b/packages/gen/codegen/.openapi-generator-ignore new file mode 100644 index 0000000..dae0b3f --- /dev/null +++ b/packages/gen/codegen/.openapi-generator-ignore @@ -0,0 +1,15 @@ +README.md +git_push.sh +setup.py +setup.cfg +pyproject.toml +requirements.txt +test-requirements.txt +tox.ini +.gitignore +.gitlab-ci.yml +.travis.yml +docs/ +test/ +.github/ +.openapi-generator/ diff --git a/packages/gen/codegen/rpt_1_5_generate.sh b/packages/gen/codegen/rpt_1_5_generate.sh new file mode 100755 index 0000000..ad846a1 --- /dev/null +++ b/packages/gen/codegen/rpt_1_5_generate.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# Regenerates rpt_1_5/generated/ from the vendored OpenAPI spec. +# Run from packages/gen/ directory. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PKG_DIR="$(dirname "$SCRIPT_DIR")" +OUT_DIR="${PKG_DIR}/gen_ai_hub/proxy/native/rpt_1_5/generated" + +mkdir -p "${OUT_DIR}" +cp "${SCRIPT_DIR}/.openapi-generator-ignore" "${OUT_DIR}/.openapi-generator-ignore" + +docker run --rm \ + -v "${PKG_DIR}:/local" \ + openapitools/openapi-generator-cli generate \ + -i /local/openapi_specs/sap-rpt-1.5_openapi.json \ + -g python \ + --additional-properties=library=httpx,packageName=generated \ + -o /local/gen_ai_hub/proxy/native/rpt_1_5/generated diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/README.md b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/README.md new file mode 100644 index 0000000..322f812 --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/README.md @@ -0,0 +1,130 @@ +# RPT 1.5 Native Client + +Async Python client for the SAP RPT 1.5 prediction service. Models are auto-generated from +the OpenAPI spec; a thin hand-written wrapper wires SAP proxy authentication and deployment +URL resolution on top. + +## File Structure + +``` +packages/gen/ +├── openapi_specs/ +│ └── sap-rpt-1.5_openapi.json # vendored spec snapshot +├── codegen/ +│ ├── rpt_1_5_generate.sh # Docker regeneration command +│ └── .openapi-generator-ignore # excludes docs/tests from generator output +└── gen_ai_hub/proxy/native/ + ├── utils.py # shared proxy/auth utilities + └── rpt_1_5/ + ├── __init__.py # public surface re-exports + ├── client.py # RPT15Client + ├── models.py # readable aliases + factory functions + └── generated/ # openapi-generator output — DO NOT EDIT + ├── api/ + │ └── default_api.py + ├── models/ + ├── api_client.py + ├── configuration.py + └── rest.py +``` + +## Usage + +```python +from gen_ai_hub.proxy.native.rpt_1_5 import ( + RPT15Client, + PredictionConfig, + PredictionPlaceholder, + TargetColumnConfig, + rows_request, + columns_request, +) + +# Build a row-oriented request +request = rows_request( + prediction_config=PredictionConfig( + target_columns=[ + TargetColumnConfig( + name="SALESGROUP", + prediction_placeholder=PredictionPlaceholder("[PREDICT]"), + ) + ] + ), + index_column="__row_idx__", + rows=[ + { + "PRODUCT": "Laptop", + "PRICE": 999.99, + "SALESGROUP": "[PREDICT]", + "__row_idx__": "1", + }, + ], +) + +# Predict — deployment URL and auth are resolved automatically +async with RPT15Client(model_name="sap-rpt-1.5") as client: + response = await client.predict(request) + predictions = response["predictions"] +``` + +## Request formats + +### Row-oriented (`rows_request`) + +Each row is a plain `dict`. Columns with `"[PREDICT]"` as the value are prediction targets. + +```python +rows_request( + prediction_config=PredictionConfig(...), + rows=[{"COL_A": "value", "COL_B": 1.0}], + index_column="__row_idx__", # optional + parse_data_types=True, # optional, default True +) +``` + +### Column-oriented (`columns_request`) + +Each column is a list of values, one per row. + +```python +columns_request( + prediction_config=PredictionConfig(...), + columns={ + "PRODUCT": ["Laptop", "Chair"], + "PRICE": [999.99, 142.99], + }, +) +``` + +## Client + +```python +RPT15Client( + model_name: str, + model_version: str | None = None, # None → server default (latest) + proxy_client: GenAIHubProxyClient | None = None, # None → process default + timeout: float | None = None, +) +``` + +Methods: + +| Method | Description | +|---|---| +| `await client.predict(request)` | Run predictions; returns raw response dict | +| `await client.health()` | Check deployment health | +| `await client.close()` | Release the underlying HTTP connection pool | + +Supports use as an async context manager (`async with`). + +## Regenerating the generated code + +```bash +cd packages/gen +bash codegen/rpt_1_5_generate.sh +``` + +The script runs `openapi-generator` via Docker — no local Java installation required. +The source spec is at `openapi_specs/sap-rpt-1.5_openapi.json`. + +> **Do not edit files under `generated/` by hand.** Run the generator and commit the result. diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/__init__.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/__init__.py new file mode 100644 index 0000000..370f20c --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/__init__.py @@ -0,0 +1,34 @@ +"""RPT 1.5 native client — spec-generated with SAP auth wiring.""" +from gen_ai_hub.proxy.native.rpt_1_5.client import RPT15Client +from gen_ai_hub.proxy.native.rpt_1_5.models import ( + ColumnsRequest, + PredictionConfig, + PredictionPlaceholder, + PredictionResult, + PredictResponseMetadata, + PredictResponsePayload, + PredictResponseStatus, + RowsInnerValue, + RowsRequest, + SchemaFieldConfig, + TargetColumnConfig, + columns_request, + rows_request, +) + +__all__ = [ + "ColumnsRequest", + "PredictResponseMetadata", + "PredictResponsePayload", + "PredictResponseStatus", + "PredictionConfig", + "PredictionPlaceholder", + "PredictionResult", + "RPT15Client", + "RowsInnerValue", + "RowsRequest", + "SchemaFieldConfig", + "TargetColumnConfig", + "columns_request", + "rows_request", +] diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/client.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/client.py new file mode 100644 index 0000000..11ef308 --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/client.py @@ -0,0 +1,98 @@ +"""RPT 1.5 typed client with SAP proxy authentication.""" +from __future__ import annotations + +from typing import Self + +from generated.api.default_api import DefaultApi +from generated.api_client import ApiClient +from generated.configuration import Configuration +from generated.models.predict_request_payload import PredictRequestPayload +from generated.models.predict_request_payload_one_of import ( + PredictRequestPayloadOneOf as RowsRequest, +) +from generated.models.predict_request_payload_one_of1 import ( + PredictRequestPayloadOneOf1 as ColumnsRequest, +) +from generated.rest import RESTClientObject + +from gen_ai_hub import GenAIHubProxyClient +from gen_ai_hub.proxy.native.utils import ( + build_sap_api_client, + get_proxy_client_instance, + resolve_deployment_url, +) + + +class RPT15Client: + """Async client for the RPT 1.5 prediction service. + + Resolves the deployment URL from the proxy client credentials using + model_name and optional model_version. All requests are authenticated + automatically via the SAP proxy client. + + Usage:: + + async with RPT15Client(model_name="sap-rpt-1.5") as client: + response = await client.predict(request) + + # or without context manager + client = RPT15Client(model_name="sap-rpt-1.5") + response = await client.predict(request) + await client.close() + """ + + def __init__( + self, + model_name: str, + model_version: str | None = None, + proxy_client: GenAIHubProxyClient | None = None, + timeout: float | None = None, + ) -> None: + self._proxy = get_proxy_client_instance(proxy_client) + base_url = resolve_deployment_url(self._proxy, model_name, model_version) + self._api_client = build_sap_api_client( + base_url=base_url, + proxy_client=self._proxy, + api_client_class=ApiClient, + configuration_class=Configuration, + rest_client_class=RESTClientObject, + timeout=timeout, + ) + self._api = DefaultApi(self._api_client) + + async def close(self) -> None: + """Close the underlying HTTP client.""" + await self._api_client.close() + + async def __aenter__(self) -> Self: + return self + + async def __aexit__(self, *_: object) -> None: + await self.close() + + async def predict(self, request: RowsRequest | ColumnsRequest) -> object: + """Make predictions from JSON data. + + Returns the raw response dict. The generated PredictResponsePayload + deserializer cannot handle the spec's nested anyOf response structure, + so response_types_map is set to "object" to bypass it. + """ + payload = PredictRequestPayload(request) + _param = self._api._predict_serialize( # type: ignore[attr-defined] # pylint: disable=protected-access + predict_request_payload=payload, + content_encoding=None, + _request_auth=None, + _content_type=None, + _headers=None, + _host_index=0, + ) + response_data = await self._api_client.call_api(*_param) # type: ignore[arg-type] + await response_data.read() # type: ignore[misc] + return self._api_client.response_deserialize( # type: ignore[no-any-return] + response_data=response_data, + response_types_map={"200": "object"}, + ).data + + async def health(self) -> object: + """Check the health of the RPT deployment.""" + return await self._api.health() # type: ignore[no-any-return] diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/__init__.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/__init__.py new file mode 100644 index 0000000..932d98f --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/__init__.py @@ -0,0 +1,82 @@ +# coding: utf-8 + +# flake8: noqa + +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +__version__ = "1.0.0" + +# Define package exports +__all__ = [ + "DefaultApi", + "ApiResponse", + "ApiClient", + "Configuration", + "OpenApiException", + "ApiTypeError", + "ApiValueError", + "ApiKeyError", + "ApiAttributeError", + "ApiException", + "ColumnType", + "ExplanationConfig", + "ExplanationResult", + "PredictRequestPayload", + "PredictRequestPayloadOneOf", + "PredictRequestPayloadOneOf1", + "PredictResponseMetadata", + "PredictResponsePayload", + "PredictResponseStatus", + "Prediction", + "PredictionConfig", + "PredictionPlaceholder", + "PredictionResult", + "PredictionsInnerValue", + "RowsInnerValue", + "SchemaFieldConfig", + "TargetColumnConfig", +] + +# import apis into sdk package +from generated.api.default_api import DefaultApi as DefaultApi + +# import ApiClient +from generated.api_response import ApiResponse as ApiResponse +from generated.api_client import ApiClient as ApiClient +from generated.configuration import Configuration as Configuration +from generated.exceptions import OpenApiException as OpenApiException +from generated.exceptions import ApiTypeError as ApiTypeError +from generated.exceptions import ApiValueError as ApiValueError +from generated.exceptions import ApiKeyError as ApiKeyError +from generated.exceptions import ApiAttributeError as ApiAttributeError +from generated.exceptions import ApiException as ApiException + +# import models into sdk package +from generated.models.column_type import ColumnType as ColumnType +from generated.models.explanation_config import ExplanationConfig as ExplanationConfig +from generated.models.explanation_result import ExplanationResult as ExplanationResult +from generated.models.predict_request_payload import PredictRequestPayload as PredictRequestPayload +from generated.models.predict_request_payload_one_of import PredictRequestPayloadOneOf as PredictRequestPayloadOneOf +from generated.models.predict_request_payload_one_of1 import PredictRequestPayloadOneOf1 as PredictRequestPayloadOneOf1 +from generated.models.predict_response_metadata import PredictResponseMetadata as PredictResponseMetadata +from generated.models.predict_response_payload import PredictResponsePayload as PredictResponsePayload +from generated.models.predict_response_status import PredictResponseStatus as PredictResponseStatus +from generated.models.prediction import Prediction as Prediction +from generated.models.prediction_config import PredictionConfig as PredictionConfig +from generated.models.prediction_placeholder import PredictionPlaceholder as PredictionPlaceholder +from generated.models.prediction_result import PredictionResult as PredictionResult +from generated.models.predictions_inner_value import PredictionsInnerValue as PredictionsInnerValue +from generated.models.rows_inner_value import RowsInnerValue as RowsInnerValue +from generated.models.schema_field_config import SchemaFieldConfig as SchemaFieldConfig +from generated.models.target_column_config import TargetColumnConfig as TargetColumnConfig + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/api/__init__.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/api/__init__.py new file mode 100644 index 0000000..0994b13 --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/api/__init__.py @@ -0,0 +1,5 @@ +# flake8: noqa + +# import apis into api package +from generated.api.default_api import DefaultApi + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/api/default_api.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/api/default_api.py new file mode 100644 index 0000000..533bb83 --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/api/default_api.py @@ -0,0 +1,911 @@ +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +import warnings +from pydantic import validate_call, Field, StrictFloat, StrictStr, StrictInt +from typing import Any, Dict, List, Optional, Tuple, Union +from typing_extensions import Annotated + +from pydantic import Field, StrictBool, StrictStr, field_validator +from typing import Any, Optional +from typing_extensions import Annotated +from generated.models.predict_request_payload import PredictRequestPayload +from generated.models.predict_response_payload import PredictResponsePayload + +from generated.api_client import ApiClient, RequestSerialized +from generated.api_response import ApiResponse +from generated.rest import RESTResponseType + + +class DefaultApi: + """NOTE: This class is auto generated by OpenAPI Generator + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + def __init__(self, api_client=None) -> None: + if api_client is None: + api_client = ApiClient.get_default() + self.api_client = api_client + + + @validate_call + async def health( + self, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> object: + """Health Check + + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._health_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "object", + } + response_data = await self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + await response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + + @validate_call + async def health_with_http_info( + self, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[object]: + """Health Check + + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._health_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "object", + } + response_data = await self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + await response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + + @validate_call + async def health_without_preload_content( + self, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Health Check + + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._health_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "object", + } + response_data = await self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + + def _health_serialize( + self, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: Dict[str, str] = { + } + + _path_params: Dict[str, str] = {} + _query_params: List[Tuple[str, str]] = [] + _header_params: Dict[str, Optional[str]] = _headers or {} + _form_params: List[Tuple[str, str]] = [] + _files: Dict[ + str, Union[str, bytes, List[str], List[bytes], List[Tuple[str, bytes]]] + ] = {} + _body_params: Optional[bytes] = None + + # process the path parameters + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + + + # set the HTTP header `Accept` + if 'Accept' not in _header_params: + _header_params['Accept'] = self.api_client.select_header_accept( + [ + 'application/json' + ] + ) + + + # authentication setting + _auth_settings: List[str] = [ + ] + + return self.api_client.param_serialize( + method='GET', + resource_path='/health', + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + + + + @validate_call + async def predict( + self, + predict_request_payload: PredictRequestPayload, + content_encoding: Annotated[Optional[StrictStr], Field(description="Content encoding of the request body. Use 'gzip' for gzip-compressed payloads. Use compression level 1.")] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> PredictResponsePayload: + """Make predictions from JSON (optionally gzip-compressed). + + + :param predict_request_payload: (required) + :type predict_request_payload: PredictRequestPayload + :param content_encoding: Content encoding of the request body. Use 'gzip' for gzip-compressed payloads. Use compression level 1. + :type content_encoding: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._predict_serialize( + predict_request_payload=predict_request_payload, + content_encoding=content_encoding, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "PredictResponsePayload", + '400': None, + '413': None, + '422': None, + '500': None, + '503': None, + } + response_data = await self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + await response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + + @validate_call + async def predict_with_http_info( + self, + predict_request_payload: PredictRequestPayload, + content_encoding: Annotated[Optional[StrictStr], Field(description="Content encoding of the request body. Use 'gzip' for gzip-compressed payloads. Use compression level 1.")] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[PredictResponsePayload]: + """Make predictions from JSON (optionally gzip-compressed). + + + :param predict_request_payload: (required) + :type predict_request_payload: PredictRequestPayload + :param content_encoding: Content encoding of the request body. Use 'gzip' for gzip-compressed payloads. Use compression level 1. + :type content_encoding: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._predict_serialize( + predict_request_payload=predict_request_payload, + content_encoding=content_encoding, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "PredictResponsePayload", + '400': None, + '413': None, + '422': None, + '500': None, + '503': None, + } + response_data = await self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + await response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + + @validate_call + async def predict_without_preload_content( + self, + predict_request_payload: PredictRequestPayload, + content_encoding: Annotated[Optional[StrictStr], Field(description="Content encoding of the request body. Use 'gzip' for gzip-compressed payloads. Use compression level 1.")] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Make predictions from JSON (optionally gzip-compressed). + + + :param predict_request_payload: (required) + :type predict_request_payload: PredictRequestPayload + :param content_encoding: Content encoding of the request body. Use 'gzip' for gzip-compressed payloads. Use compression level 1. + :type content_encoding: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._predict_serialize( + predict_request_payload=predict_request_payload, + content_encoding=content_encoding, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "PredictResponsePayload", + '400': None, + '413': None, + '422': None, + '500': None, + '503': None, + } + response_data = await self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + + def _predict_serialize( + self, + predict_request_payload, + content_encoding, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: Dict[str, str] = { + } + + _path_params: Dict[str, str] = {} + _query_params: List[Tuple[str, str]] = [] + _header_params: Dict[str, Optional[str]] = _headers or {} + _form_params: List[Tuple[str, str]] = [] + _files: Dict[ + str, Union[str, bytes, List[str], List[bytes], List[Tuple[str, bytes]]] + ] = {} + _body_params: Optional[bytes] = None + + # process the path parameters + # process the query parameters + # process the header parameters + if content_encoding is not None: + _header_params['Content-Encoding'] = content_encoding + # process the form parameters + # process the body parameter + if predict_request_payload is not None: + _body_params = predict_request_payload + + + # set the HTTP header `Accept` + if 'Accept' not in _header_params: + _header_params['Accept'] = self.api_client.select_header_accept( + [ + 'application/json' + ] + ) + + # set the HTTP header `Content-Type` + if _content_type: + _header_params['Content-Type'] = _content_type + else: + _default_content_type = ( + self.api_client.select_header_content_type( + [ + 'application/json' + ] + ) + ) + if _default_content_type is not None: + _header_params['Content-Type'] = _default_content_type + + # authentication setting + _auth_settings: List[str] = [ + ] + + return self.api_client.param_serialize( + method='POST', + resource_path='/predict', + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + + + + @validate_call + async def predict_parquet( + self, + file: StrictStr, + prediction_config: Annotated[StrictStr, Field(description="JSON string containing the prediction configuration (see PredictionConfig schema).")], + index_column: Optional[StrictStr] = None, + parse_data_types: Optional[StrictBool] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> PredictResponsePayload: + """Make predictions from Parquet file + + + :param file: (required) + :type file: str + :param prediction_config: JSON string containing the prediction configuration (see PredictionConfig schema). (required) + :type prediction_config: str + :param index_column: + :type index_column: str + :param parse_data_types: + :type parse_data_types: bool + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._predict_parquet_serialize( + file=file, + prediction_config=prediction_config, + index_column=index_column, + parse_data_types=parse_data_types, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "PredictResponsePayload", + '400': None, + '413': None, + '422': None, + '500': None, + '503': None, + } + response_data = await self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + await response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + + @validate_call + async def predict_parquet_with_http_info( + self, + file: StrictStr, + prediction_config: Annotated[StrictStr, Field(description="JSON string containing the prediction configuration (see PredictionConfig schema).")], + index_column: Optional[StrictStr] = None, + parse_data_types: Optional[StrictBool] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[PredictResponsePayload]: + """Make predictions from Parquet file + + + :param file: (required) + :type file: str + :param prediction_config: JSON string containing the prediction configuration (see PredictionConfig schema). (required) + :type prediction_config: str + :param index_column: + :type index_column: str + :param parse_data_types: + :type parse_data_types: bool + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._predict_parquet_serialize( + file=file, + prediction_config=prediction_config, + index_column=index_column, + parse_data_types=parse_data_types, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "PredictResponsePayload", + '400': None, + '413': None, + '422': None, + '500': None, + '503': None, + } + response_data = await self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + await response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + + @validate_call + async def predict_parquet_without_preload_content( + self, + file: StrictStr, + prediction_config: Annotated[StrictStr, Field(description="JSON string containing the prediction configuration (see PredictionConfig schema).")], + index_column: Optional[StrictStr] = None, + parse_data_types: Optional[StrictBool] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Make predictions from Parquet file + + + :param file: (required) + :type file: str + :param prediction_config: JSON string containing the prediction configuration (see PredictionConfig schema). (required) + :type prediction_config: str + :param index_column: + :type index_column: str + :param parse_data_types: + :type parse_data_types: bool + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._predict_parquet_serialize( + file=file, + prediction_config=prediction_config, + index_column=index_column, + parse_data_types=parse_data_types, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "PredictResponsePayload", + '400': None, + '413': None, + '422': None, + '500': None, + '503': None, + } + response_data = await self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + + def _predict_parquet_serialize( + self, + file, + prediction_config, + index_column, + parse_data_types, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: Dict[str, str] = { + } + + _path_params: Dict[str, str] = {} + _query_params: List[Tuple[str, str]] = [] + _header_params: Dict[str, Optional[str]] = _headers or {} + _form_params: List[Tuple[str, str]] = [] + _files: Dict[ + str, Union[str, bytes, List[str], List[bytes], List[Tuple[str, bytes]]] + ] = {} + _body_params: Optional[bytes] = None + + # process the path parameters + # process the query parameters + # process the header parameters + # process the form parameters + if file is not None: + _form_params.append(('file', file)) + if prediction_config is not None: + _form_params.append(('prediction_config', prediction_config)) + if index_column is not None: + _form_params.append(('index_column', index_column)) + if parse_data_types is not None: + _form_params.append(('parse_data_types', parse_data_types)) + # process the body parameter + + + # set the HTTP header `Accept` + if 'Accept' not in _header_params: + _header_params['Accept'] = self.api_client.select_header_accept( + [ + 'application/json' + ] + ) + + # set the HTTP header `Content-Type` + if _content_type: + _header_params['Content-Type'] = _content_type + else: + _default_content_type = ( + self.api_client.select_header_content_type( + [ + 'multipart/form-data' + ] + ) + ) + if _default_content_type is not None: + _header_params['Content-Type'] = _default_content_type + + # authentication setting + _auth_settings: List[str] = [ + ] + + return self.api_client.param_serialize( + method='POST', + resource_path='/predict_parquet', + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/api_client.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/api_client.py new file mode 100644 index 0000000..1f9e298 --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/api_client.py @@ -0,0 +1,830 @@ +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + + +import datetime +from dateutil.parser import parse +from enum import Enum +import decimal +import json +import mimetypes +import os +import re +import tempfile +import uuid + +from urllib.parse import quote +from typing import Tuple, Optional, List, Dict, Union +from pydantic import SecretStr + +from generated.configuration import Configuration +from generated.api_response import ApiResponse, T as ApiResponseT +import generated.models +from generated import rest +from generated.exceptions import ( + ApiValueError, + ApiException, + BadRequestException, + UnauthorizedException, + ForbiddenException, + NotFoundException, + ServiceException +) + +RequestSerialized = Tuple[str, str, Dict[str, str], Optional[str], List[str]] + +class ApiClient: + """Generic API client for OpenAPI client library builds. + + OpenAPI generic API client. This client handles the client- + server communication, and is invariant across implementations. Specifics of + the methods and models for each application are generated from the OpenAPI + templates. + + :param configuration: .Configuration object for this client + :param header_name: a header to pass when making calls to the API. + :param header_value: a header value to pass when making calls to + the API. + :param cookie: a cookie to include in the header when making calls + to the API + """ + + PRIMITIVE_TYPES = (float, bool, bytes, str, int) + NATIVE_TYPES_MAPPING = { + 'int': int, + 'long': int, # TODO remove as only py3 is supported? + 'float': float, + 'str': str, + 'bool': bool, + 'date': datetime.date, + 'datetime': datetime.datetime, + 'decimal': decimal.Decimal, + 'UUID': uuid.UUID, + 'object': object, + } + _pool = None + + def __init__( + self, + configuration=None, + header_name=None, + header_value=None, + cookie=None + ) -> None: + # use default configuration if none is provided + if configuration is None: + configuration = Configuration.get_default() + self.configuration = configuration + + self.rest_client = rest.RESTClientObject(configuration) + self.default_headers = {} + if header_name is not None: + self.default_headers[header_name] = header_value + self.cookie = cookie + # Set default User-Agent. + self.user_agent = 'OpenAPI-Generator/1.0.0/python' + self.client_side_validation = configuration.client_side_validation + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc_value, traceback): + await self.close() + + async def close(self): + await self.rest_client.close() + + @property + def user_agent(self): + """User agent for this API client""" + return self.default_headers['User-Agent'] + + @user_agent.setter + def user_agent(self, value): + self.default_headers['User-Agent'] = value + + def set_default_header(self, header_name, header_value): + self.default_headers[header_name] = header_value + + + _default = None + + @classmethod + def get_default(cls): + """Return new instance of ApiClient. + + This method returns newly created, based on default constructor, + object of ApiClient class or returns a copy of default + ApiClient. + + :return: The ApiClient object. + """ + if cls._default is None: + cls._default = ApiClient() + return cls._default + + @classmethod + def set_default(cls, default): + """Set default instance of ApiClient. + + It stores default ApiClient. + + :param default: object of ApiClient. + """ + cls._default = default + + def param_serialize( + self, + method, + resource_path, + path_params=None, + query_params=None, + header_params=None, + body=None, + post_params=None, + files=None, auth_settings=None, + collection_formats=None, + _host=None, + _request_auth=None + ) -> RequestSerialized: + + """Builds the HTTP request params needed by the request. + :param method: Method to call. + :param resource_path: Path to method endpoint. + :param path_params: Path parameters in the url. + :param query_params: Query parameters in the url. + :param header_params: Header parameters to be + placed in the request header. + :param body: Request body. + :param post_params dict: Request post form parameters, + for `application/x-www-form-urlencoded`, `multipart/form-data`. + :param auth_settings list: Auth Settings names for the request. + :param files dict: key -> filename, value -> filepath, + for `multipart/form-data`. + :param collection_formats: dict of collection formats for path, query, + header, and post parameters. + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the authentication + in the spec for a single request. + :return: tuple of form (path, http_method, query_params, header_params, + body, post_params, files) + """ + + config = self.configuration + + # header parameters + header_params = header_params or {} + header_params.update(self.default_headers) + if self.cookie: + header_params['Cookie'] = self.cookie + if header_params: + header_params = self.sanitize_for_serialization(header_params) + header_params = dict( + self.parameters_to_tuples(header_params,collection_formats) + ) + + # path parameters + if path_params: + path_params = self.sanitize_for_serialization(path_params) + path_params = self.parameters_to_tuples( + path_params, + collection_formats + ) + for k, v in path_params: + # specified safe chars, encode everything + resource_path = resource_path.replace( + '{%s}' % k, + quote(str(v), safe=config.safe_chars_for_path_param) + ) + + # post parameters + if post_params or files: + post_params = post_params if post_params else [] + post_params = self.sanitize_for_serialization(post_params) + post_params = self.parameters_to_tuples( + post_params, + collection_formats + ) + if files: + post_params.extend(self.files_parameters(files)) + + # auth setting + self.update_params_for_auth( + header_params, + query_params, + auth_settings, + resource_path, + method, + body, + request_auth=_request_auth + ) + + # body + if body: + body = self.sanitize_for_serialization(body) + + # request url + if _host is None or self.configuration.ignore_operation_servers: + url = self.configuration.host + resource_path + else: + # use server/host defined in path or operation instead + url = _host + resource_path + + # query parameters + if query_params: + query_params = self.sanitize_for_serialization(query_params) + url_query = self.parameters_to_url_query( + query_params, + collection_formats + ) + url += "?" + url_query + + return method, url, header_params, body, post_params + + async def call_api( + self, + method, + url, + header_params=None, + body=None, + post_params=None, + _request_timeout=None + ) -> rest.RESTResponse: + """Makes the HTTP request (synchronous) + :param method: Method to call. + :param url: Path to method endpoint. + :param header_params: Header parameters to be + placed in the request header. + :param body: Request body. + :param post_params dict: Request post form parameters, + for `application/x-www-form-urlencoded`, `multipart/form-data`. + :param _request_timeout: timeout setting for this request. + :return: RESTResponse + """ + + try: + # perform request and return response + response_data = await self.rest_client.request( + method, url, + headers=header_params, + body=body, post_params=post_params, + _request_timeout=_request_timeout + ) + + except ApiException as e: + raise e + + return response_data + + def response_deserialize( + self, + response_data: rest.RESTResponse, + response_types_map: Optional[Dict[str, ApiResponseT]]=None + ) -> ApiResponse[ApiResponseT]: + """Deserializes response into an object. + :param response_data: RESTResponse object to be deserialized. + :param response_types_map: dict of response types. + :return: ApiResponse + """ + + msg = "RESTResponse.read() must be called before passing it to response_deserialize()" + assert response_data.data is not None, msg + + response_type = response_types_map.get(str(response_data.status), None) + if not response_type and isinstance(response_data.status, int) and 100 <= response_data.status <= 599: + # if not found, look for '1XX', '2XX', etc. + response_type = response_types_map.get(str(response_data.status)[0] + "XX", None) + + # If the response_type has not matched (eg. did not match the previous if statements) and the default response is available, use it. + if response_type is None and str(response_data.status) not in response_types_map \ + and (not isinstance(response_data.status, int) or not 100 <= response_data.status <= 599 or str(response_data.status)[0] + "XX" not in response_types_map) \ + and 'default' in response_types_map: + response_type = response_types_map['default'] + + # deserialize response data + response_text = None + return_data = None + try: + if response_type in ("bytearray", "bytes"): + return_data = response_data.data + elif response_type == "file": + return_data = self.__deserialize_file(response_data) + elif response_type is not None: + match = None + content_type = response_data.headers.get('content-type') + if content_type is not None: + match = re.search(r"charset=([a-zA-Z\-\d]+)[\s;]?", content_type) + encoding = match.group(1) if match else "utf-8" + response_text = response_data.data.decode(encoding) + return_data = self.deserialize(response_text, response_type, content_type) + finally: + if not 200 <= response_data.status <= 299: + raise ApiException.from_response( + http_resp=response_data, + body=response_text, + data=return_data, + ) + + return ApiResponse( + status_code = response_data.status, + data = return_data, + headers = response_data.headers, + raw_data = response_data.data + ) + + def sanitize_for_serialization(self, obj): + """Builds a JSON POST object. + + If obj is None, return None. + If obj is SecretStr, return obj.get_secret_value() + If obj is str, int, long, float, bool, return directly. + If obj is datetime.datetime, datetime.date + convert to string in iso8601 format. + If obj is decimal.Decimal return string representation. + If obj is list, sanitize each element in the list. + If obj is dict, return the dict. + If obj is OpenAPI model, return the properties dict. + + :param obj: The data to serialize. + :return: The serialized form of data. + """ + if obj is None: + return None + elif isinstance(obj, Enum): + return obj.value + elif isinstance(obj, SecretStr): + return obj.get_secret_value() + elif isinstance(obj, self.PRIMITIVE_TYPES): + return obj + elif isinstance(obj, uuid.UUID): + return str(obj) + elif isinstance(obj, list): + return [ + self.sanitize_for_serialization(sub_obj) for sub_obj in obj + ] + elif isinstance(obj, tuple): + return tuple( + self.sanitize_for_serialization(sub_obj) for sub_obj in obj + ) + elif isinstance(obj, (datetime.datetime, datetime.date)): + return obj.isoformat() + elif isinstance(obj, decimal.Decimal): + return str(obj) + elif isinstance(obj, dict): + return { + key: self.sanitize_for_serialization(val) + for key, val in obj.items() + } + + # Convert model obj to dict except + # attributes `openapi_types`, `attribute_map` + # and attributes which value is not None. + # Convert attribute name to json key in + # model definition for request. + if hasattr(obj, 'to_dict') and callable(getattr(obj, 'to_dict')): + obj_dict = obj.to_dict() + else: + obj_dict = obj.__dict__ + + return self.sanitize_for_serialization(obj_dict) + + + def deserialize(self, response_text: str, response_type: str, content_type: Optional[str]): + """Deserializes response into an object. + + :param response: RESTResponse object to be deserialized. + :param response_type: class literal for + deserialized object, or string of class name. + :param content_type: content type of response. + + :return: deserialized object. + """ + + # fetch data from response object + if content_type is None: + try: + data = json.loads(response_text) + except ValueError: + data = response_text + elif re.match(r'^application/(json|[\w!#$&.+\-^_]+\+json)\s*(;|$)', content_type, re.IGNORECASE): + if response_text == "": + data = "" + else: + data = json.loads(response_text) + elif re.match(r'^text\/[a-z.+-]+\s*(;|$)', content_type, re.IGNORECASE): + data = response_text + else: + raise ApiException( + status=0, + reason="Unsupported content type: {0}".format(content_type) + ) + + return self.__deserialize(data, response_type) + + def __deserialize(self, data, klass): + """Deserializes dict, list, str into an object. + + :param data: dict, list or str. + :param klass: class literal, or string of class name. + + :return: object. + """ + if data is None: + return None + + if isinstance(klass, str): + if klass.startswith('List['): + m = re.match(r'List\[(.*)]', klass) + assert m is not None, "Malformed List type definition" + sub_kls = m.group(1) + return [self.__deserialize(sub_data, sub_kls) + for sub_data in data] + + if klass.startswith('Dict['): + m = re.match(r'Dict\[([^,]*), (.*)]', klass) + assert m is not None, "Malformed Dict type definition" + sub_kls = m.group(2) + return {k: self.__deserialize(v, sub_kls) + for k, v in data.items()} + + # convert str to class + if klass in self.NATIVE_TYPES_MAPPING: + klass = self.NATIVE_TYPES_MAPPING[klass] + else: + klass = getattr(generated.models, klass) + + if klass in self.PRIMITIVE_TYPES: + return self.__deserialize_primitive(data, klass) + elif klass is object: + return self.__deserialize_object(data) + elif klass is datetime.date: + return self.__deserialize_date(data) + elif klass is datetime.datetime: + return self.__deserialize_datetime(data) + elif klass is decimal.Decimal: + return decimal.Decimal(data) + elif klass is uuid.UUID: + return uuid.UUID(data) + elif issubclass(klass, Enum): + return self.__deserialize_enum(data, klass) + else: + return self.__deserialize_model(data, klass) + + def parameters_to_tuples(self, params, collection_formats): + """Get parameters as list of tuples, formatting collections. + + :param params: Parameters as dict or list of two-tuples + :param dict collection_formats: Parameter collection formats + :return: Parameters as list of tuples, collections formatted + """ + new_params: List[Tuple[str, str]] = [] + if collection_formats is None: + collection_formats = {} + for k, v in params.items() if isinstance(params, dict) else params: + if isinstance(v, bool): + v = str(v).lower() + if k in collection_formats: + collection_format = collection_formats[k] + if collection_format == 'multi': + new_params.extend( + (k, str(value).lower() if isinstance(value, bool) else value) + for value in v + ) + else: + if collection_format == 'ssv': + delimiter = ' ' + elif collection_format == 'tsv': + delimiter = '\t' + elif collection_format == 'pipes': + delimiter = '|' + else: # csv is the default + delimiter = ',' + new_params.append( + (k, delimiter.join( + str(value).lower() if isinstance(value, bool) else str(value) + for value in v))) + else: + new_params.append((k, v)) + return new_params + + def parameters_to_url_query(self, params, collection_formats): + """Get parameters as list of tuples, formatting collections. + + :param params: Parameters as dict or list of two-tuples + :param dict collection_formats: Parameter collection formats + :return: URL query string (e.g. a=Hello%20World&b=123) + """ + new_params: List[Tuple[str, str]] = [] + if collection_formats is None: + collection_formats = {} + for k, v in params.items() if isinstance(params, dict) else params: + if isinstance(v, bool): + v = str(v).lower() + if isinstance(v, (int, float)): + v = str(v) + if isinstance(v, dict): + v = json.dumps(v) + + if k in collection_formats: + collection_format = collection_formats[k] + if collection_format == 'multi': + new_params.extend( + (k, quote(str(value).lower() if isinstance(value, bool) else str(value))) + for value in v + ) + else: + if collection_format == 'ssv': + delimiter = ' ' + elif collection_format == 'tsv': + delimiter = '\t' + elif collection_format == 'pipes': + delimiter = '|' + else: # csv is the default + delimiter = ',' + new_params.append( + (k, delimiter.join( + quote(str(value).lower() if isinstance(value, bool) else str(value)) + for value in v)) + ) + else: + new_params.append((k, quote(str(v)))) + + return "&".join(["=".join(map(str, item)) for item in new_params]) + + def files_parameters( + self, + files: Dict[str, Union[str, bytes, List[str], List[bytes], Tuple[str, bytes]]], + ): + """Builds form parameters. + + :param files: File parameters. + :return: Form parameters with files. + """ + params = [] + for k, v in files.items(): + if isinstance(v, str): + with open(v, 'rb') as f: + filename = os.path.basename(f.name) + filedata = f.read() + elif isinstance(v, bytes): + filename = k + filedata = v + elif isinstance(v, tuple): + filename, filedata = v + elif isinstance(v, list): + for file_param in v: + params.extend(self.files_parameters({k: file_param})) + continue + else: + raise ValueError("Unsupported file value") + mimetype = ( + mimetypes.guess_type(filename)[0] + or 'application/octet-stream' + ) + params.append( + tuple([k, tuple([filename, filedata, mimetype])]) + ) + return params + + def select_header_accept(self, accepts: List[str]) -> Optional[str]: + """Returns `Accept` based on an array of accepts provided. + + :param accepts: List of headers. + :return: Accept (e.g. application/json). + """ + if not accepts: + return None + + for accept in accepts: + if re.search('json', accept, re.IGNORECASE): + return accept + + return accepts[0] + + def select_header_content_type(self, content_types): + """Returns `Content-Type` based on an array of content_types provided. + + :param content_types: List of content-types. + :return: Content-Type (e.g. application/json). + """ + if not content_types: + return None + + for content_type in content_types: + if re.search('json', content_type, re.IGNORECASE): + return content_type + + return content_types[0] + + def update_params_for_auth( + self, + headers, + queries, + auth_settings, + resource_path, + method, + body, + request_auth=None + ) -> None: + """Updates header and query params based on authentication setting. + + :param headers: Header parameters dict to be updated. + :param queries: Query parameters tuple list to be updated. + :param auth_settings: Authentication setting identifiers list. + :resource_path: A string representation of the HTTP request resource path. + :method: A string representation of the HTTP request method. + :body: A object representing the body of the HTTP request. + The object type is the return value of sanitize_for_serialization(). + :param request_auth: if set, the provided settings will + override the token in the configuration. + """ + if not auth_settings: + return + + if request_auth: + self._apply_auth_params( + headers, + queries, + resource_path, + method, + body, + request_auth + ) + else: + for auth in auth_settings: + auth_setting = self.configuration.auth_settings().get(auth) + if auth_setting: + self._apply_auth_params( + headers, + queries, + resource_path, + method, + body, + auth_setting + ) + + def _apply_auth_params( + self, + headers, + queries, + resource_path, + method, + body, + auth_setting + ) -> None: + """Updates the request parameters based on a single auth_setting + + :param headers: Header parameters dict to be updated. + :param queries: Query parameters tuple list to be updated. + :resource_path: A string representation of the HTTP request resource path. + :method: A string representation of the HTTP request method. + :body: A object representing the body of the HTTP request. + The object type is the return value of sanitize_for_serialization(). + :param auth_setting: auth settings for the endpoint + """ + if auth_setting['in'] == 'cookie': + if not 'Cookie' in headers: + headers['Cookie'] = "" + else: + headers['Cookie'] += "; " + # Account for cookie value containing spaces and special characters, excluding base64 delimiters + cookie_value = quote(str(auth_setting['value']), safe="!#$%&'()*+-./:<=>?@[]^_`{|}~%+/=") + headers['Cookie'] += f"{auth_setting['key']}={cookie_value}" + elif auth_setting['in'] == 'header': + if auth_setting['type'] != 'http-signature': + headers[auth_setting['key']] = auth_setting['value'] + elif auth_setting['in'] == 'query': + queries.append((auth_setting['key'], auth_setting['value'])) + else: + raise ApiValueError( + 'Authentication token must be in `query` or `header`' + ) + + def __deserialize_file(self, response): + """Deserializes body to file + + Saves response body into a file in a temporary folder, + using the filename from the `Content-Disposition` header if provided. + + handle file downloading + save response body into a tmp file and return the instance + + :param response: RESTResponse. + :return: file path. + """ + fd, path = tempfile.mkstemp(dir=self.configuration.temp_folder_path) + os.close(fd) + os.remove(path) + + content_disposition = response.headers.get("Content-Disposition") + if content_disposition: + m = re.search( + r'filename=[\'"]?([^\'"\s]+)[\'"]?', + content_disposition + ) + assert m is not None, "Unexpected 'content-disposition' header value" + filename = os.path.basename(m.group(1)) # Strip any directory traversal + if filename in ("", ".", ".."): # fall back to tmp filename + filename = os.path.basename(path) + path = os.path.join(os.path.dirname(path), filename) + + with open(path, "wb") as f: + f.write(response.data) + + return path + + def __deserialize_primitive(self, data, klass): + """Deserializes string to primitive type. + + :param data: str. + :param klass: class literal. + + :return: int, long, float, str, bool. + """ + try: + return klass(data) + except UnicodeEncodeError: + return str(data) + except TypeError: + return data + + def __deserialize_object(self, value): + """Return an original value. + + :return: object. + """ + return value + + def __deserialize_date(self, string): + """Deserializes string to date. + + :param string: str. + :return: date. + """ + try: + return parse(string).date() + except ImportError: + return string + except ValueError: + raise rest.ApiException( + status=0, + reason="Failed to parse `{0}` as date object".format(string) + ) + + def __deserialize_datetime(self, string): + """Deserializes string to datetime. + + The string should be in iso8601 datetime format. + + :param string: str. + :return: datetime. + """ + try: + return parse(string) + except ImportError: + return string + except ValueError: + raise rest.ApiException( + status=0, + reason=( + "Failed to parse `{0}` as datetime object" + .format(string) + ) + ) + + def __deserialize_enum(self, data, klass): + """Deserializes primitive type to enum. + + :param data: primitive type. + :param klass: class literal. + :return: enum value. + """ + try: + return klass(data) + except ValueError: + raise rest.ApiException( + status=0, + reason=( + "Failed to parse `{0}` as `{1}`" + .format(data, klass) + ) + ) + + def __deserialize_model(self, data, klass): + """Deserializes list or dict to model. + + :param data: dict, list. + :param klass: class literal. + :return: model object. + """ + + return klass.from_dict(data) diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/api_response.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/api_response.py new file mode 100644 index 0000000..9bc7c11 --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/api_response.py @@ -0,0 +1,21 @@ +"""API response object.""" + +from __future__ import annotations +from typing import Optional, Generic, Mapping, TypeVar +from pydantic import Field, StrictInt, StrictBytes, BaseModel + +T = TypeVar("T") + +class ApiResponse(BaseModel, Generic[T]): + """ + API response object + """ + + status_code: StrictInt = Field(description="HTTP status code") + headers: Optional[Mapping[str, str]] = Field(None, description="HTTP headers") + data: T = Field(description="Deserialized data given the data type") + raw_data: StrictBytes = Field(description="Raw data (HTTP response body)") + + model_config = { + "arbitrary_types_allowed": True + } diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/configuration.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/configuration.py new file mode 100644 index 0000000..70a4c2d --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/configuration.py @@ -0,0 +1,595 @@ +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +import base64 +import copy +import http.client as httplib +import logging +from logging import FileHandler +import sys +from typing import Any, ClassVar, Dict, List, Literal, Optional, TypedDict, Union +from typing_extensions import NotRequired, Self + + + +JSON_SCHEMA_VALIDATION_KEYWORDS = { + 'multipleOf', 'maximum', 'exclusiveMaximum', + 'minimum', 'exclusiveMinimum', 'maxLength', + 'minLength', 'pattern', 'maxItems', 'minItems' +} + +ServerVariablesT = Dict[str, str] + +GenericAuthSetting = TypedDict( + "GenericAuthSetting", + { + "type": str, + "in": str, + "key": str, + "value": str, + }, +) + + +OAuth2AuthSetting = TypedDict( + "OAuth2AuthSetting", + { + "type": Literal["oauth2"], + "in": Literal["header"], + "key": Literal["Authorization"], + "value": str, + }, +) + + +APIKeyAuthSetting = TypedDict( + "APIKeyAuthSetting", + { + "type": Literal["api_key"], + "in": str, + "key": str, + "value": Optional[str], + }, +) + + +BasicAuthSetting = TypedDict( + "BasicAuthSetting", + { + "type": Literal["basic"], + "in": Literal["header"], + "key": Literal["Authorization"], + "value": Optional[str], + }, +) + + +BearerFormatAuthSetting = TypedDict( + "BearerFormatAuthSetting", + { + "type": Literal["bearer"], + "in": Literal["header"], + "format": Literal["JWT"], + "key": Literal["Authorization"], + "value": str, + }, +) + + +BearerAuthSetting = TypedDict( + "BearerAuthSetting", + { + "type": Literal["bearer"], + "in": Literal["header"], + "key": Literal["Authorization"], + "value": str, + }, +) + + +HTTPSignatureAuthSetting = TypedDict( + "HTTPSignatureAuthSetting", + { + "type": Literal["http-signature"], + "in": Literal["header"], + "key": Literal["Authorization"], + "value": None, + }, +) + + +AuthSettings = TypedDict( + "AuthSettings", + { + }, + total=False, +) + + +class HostSettingVariable(TypedDict): + description: str + default_value: str + enum_values: List[str] + + +class HostSetting(TypedDict): + url: str + description: str + variables: NotRequired[Dict[str, HostSettingVariable]] + + +class Configuration: + """This class contains various settings of the API client. + + :param host: Base url. + :param ignore_operation_servers + Boolean to ignore operation servers for the API client. + Config will use `host` as the base url regardless of the operation servers. + :param api_key: Dict to store API key(s). + Each entry in the dict specifies an API key. + The dict key is the name of the security scheme in the OAS specification. + The dict value is the API key secret. + :param api_key_prefix: Dict to store API prefix (e.g. Bearer). + The dict key is the name of the security scheme in the OAS specification. + The dict value is an API key prefix when generating the auth data. + :param username: Username for HTTP basic authentication. + :param password: Password for HTTP basic authentication. + :param access_token: Access token. + :param server_index: Index to servers configuration. + :param server_variables: Mapping with string values to replace variables in + templated server configuration. The validation of enums is performed for + variables with defined enum values before. + :param server_operation_index: Mapping from operation ID to an index to server + configuration. + :param server_operation_variables: Mapping from operation ID to a mapping with + string values to replace variables in templated server configuration. + The validation of enums is performed for variables with defined enum + values before. + :param verify_ssl: bool - Set this to false to skip verifying SSL certificate + when calling API from https server. + :param ssl_ca_cert: str - the path to a file of concatenated CA certificates + in PEM format. + :param retries: int - Retry configuration. + :param ca_cert_data: verify the peer using concatenated CA certificate data + in PEM (str) or DER (bytes) format. + :param cert_file: the path to a client certificate file, for mTLS. + :param key_file: the path to a client key file, for mTLS. + :param assert_hostname: Set this to True/False to enable/disable SSL hostname verification. + :param tls_server_name: SSL/TLS Server Name Indication (SNI). Set this to the SNI value expected by the server. + :param connection_pool_maxsize: Connection pool max size. None in the constructor is coerced to 100 for async and cpu_count * 5 for sync. + :param proxy: Proxy URL. + :param proxy_headers: Proxy headers. + :param safe_chars_for_path_param: Safe characters for path parameter encoding. + :param client_side_validation: Enable client-side validation. Default True. + :param socket_options: Options to pass down to the underlying urllib3 socket. + :param datetime_format: Datetime format string for serialization. + :param date_format: Date format string for serialization. + + """ + + _default: ClassVar[Optional[Self]] = None + + def __init__( + self, + host: Optional[str]=None, + api_key: Optional[Dict[str, str]]=None, + api_key_prefix: Optional[Dict[str, str]]=None, + username: Optional[str]=None, + password: Optional[str]=None, + access_token: Optional[str]=None, + server_index: Optional[int]=None, + server_variables: Optional[ServerVariablesT]=None, + server_operation_index: Optional[Dict[int, int]]=None, + server_operation_variables: Optional[Dict[int, ServerVariablesT]]=None, + ignore_operation_servers: bool=False, + ssl_ca_cert: Optional[str]=None, + retries: Optional[int] = None, + ca_cert_data: Optional[Union[str, bytes]] = None, + cert_file: Optional[str]=None, + key_file: Optional[str]=None, + verify_ssl: bool=True, + assert_hostname: Optional[bool]=None, + tls_server_name: Optional[str]=None, + connection_pool_maxsize: Optional[int]=None, + proxy: Optional[str]=None, + proxy_headers: Optional[Any]=None, + safe_chars_for_path_param: str='', + client_side_validation: bool=True, + socket_options: Optional[Any]=None, + datetime_format: str="%Y-%m-%dT%H:%M:%S.%f%z", + date_format: str="%Y-%m-%d", + *, + debug: Optional[bool] = None, + ) -> None: + """Constructor + """ + self._base_path = "http://localhost" if host is None else host + """Default Base url + """ + self.server_index = 0 if server_index is None and host is None else server_index + self.server_operation_index = server_operation_index or {} + """Default server index + """ + self.server_variables = server_variables or {} + self.server_operation_variables = server_operation_variables or {} + """Default server variables + """ + self.ignore_operation_servers = ignore_operation_servers + """Ignore operation servers + """ + self.temp_folder_path = None + """Temp file folder for downloading files + """ + # Authentication Settings + self.api_key = {} + if api_key: + self.api_key = api_key + """dict to store API key(s) + """ + self.api_key_prefix = {} + if api_key_prefix: + self.api_key_prefix = api_key_prefix + """dict to store API prefix (e.g. Bearer) + """ + self.refresh_api_key_hook = None + """function hook to refresh API key if expired + """ + self.username = username + """Username for HTTP basic authentication + """ + self.password = password + """Password for HTTP basic authentication + """ + self.access_token = access_token + """Access token + """ + self.logger = {} + """Logging Settings + """ + self.logger["package_logger"] = logging.getLogger("generated") + self.logger_format = '%(asctime)s %(levelname)s %(message)s' + """Log format + """ + self.logger_stream_handler = None + """Log stream handler + """ + self.logger_file_handler: Optional[FileHandler] = None + """Log file handler + """ + self.logger_file = None + """Debug file location + """ + if debug is not None: + self.debug = debug + else: + self.__debug = False + """Debug switch + """ + + self.verify_ssl = verify_ssl + """SSL/TLS verification + Set this to false to skip verifying SSL certificate when calling API + from https server. + """ + self.ssl_ca_cert = ssl_ca_cert + """Set this to customize the certificate file to verify the peer. + """ + self.ca_cert_data = ca_cert_data + """Set this to verify the peer using PEM (str) or DER (bytes) + certificate data. + """ + self.cert_file = cert_file + """client certificate file + """ + self.key_file = key_file + """client key file + """ + self.assert_hostname = assert_hostname + """Set this to True/False to enable/disable SSL hostname verification. + """ + self.tls_server_name = tls_server_name + """SSL/TLS Server Name Indication (SNI) + Set this to the SNI value expected by the server. + """ + + self.connection_pool_maxsize = connection_pool_maxsize if connection_pool_maxsize is not None else 100 + """This value is passed to the aiohttp to limit simultaneous connections. + None in the constructor is coerced to default 100. + """ + + self.proxy = proxy + """Proxy URL + """ + self.proxy_headers = proxy_headers + """Proxy headers + """ + self.safe_chars_for_path_param = safe_chars_for_path_param + """Safe chars for path_param + """ + self.retries = retries + """Retry configuration + """ + # Enable client side validation + self.client_side_validation = client_side_validation + + self.socket_options = socket_options + """Options to pass down to the underlying urllib3 socket + """ + + self.datetime_format = datetime_format + """datetime format + """ + + self.date_format = date_format + """date format + """ + + def __deepcopy__(self, memo: Dict[int, Any]) -> Self: + cls = self.__class__ + result = cls.__new__(cls) + memo[id(self)] = result + for k, v in self.__dict__.items(): + if k not in ('logger', 'logger_file_handler'): + setattr(result, k, copy.deepcopy(v, memo)) + # shallow copy of loggers + result.logger = copy.copy(self.logger) + # use setter to re-create the file handler (excluded from __dict__ copy) + result.logger_file = self.logger_file + + return result + + def __setattr__(self, name: str, value: Any) -> None: + object.__setattr__(self, name, value) + + @classmethod + def set_default(cls, default: Optional[Self]) -> None: + """Set default instance of configuration. + + It stores default configuration, which can be + returned by get_default_copy method. + + :param default: object of Configuration + """ + cls._default = default + + @classmethod + def get_default_copy(cls) -> Self: + """Deprecated. Please use `get_default` instead. + + Deprecated. Please use `get_default` instead. + + :return: The configuration object. + """ + return cls.get_default() + + @classmethod + def get_default(cls) -> Self: + """Return the default configuration. + + This method returns newly created, based on default constructor, + object of Configuration class or returns a copy of default + configuration. + + :return: The configuration object. + """ + if cls._default is None: + cls._default = cls() + return cls._default + + @property + def logger_file(self) -> Optional[str]: + """The logger file. + + If the logger_file is None, then add stream handler and remove file + handler. Otherwise, add file handler and remove stream handler. + + :param value: The logger_file path. + :type: str + """ + return self.__logger_file + + @logger_file.setter + def logger_file(self, value: Optional[str]) -> None: + """The logger file. + + If the logger_file is None, then add stream handler and remove file + handler. Otherwise, add file handler and remove stream handler. + + :param value: The logger_file path. + :type: str + """ + self.__logger_file = value + if self.__logger_file: + # If set logging file, + # then add file handler and remove stream handler. + self.logger_file_handler = logging.FileHandler(self.__logger_file) + self.logger_file_handler.setFormatter(self.logger_formatter) + for _, logger in self.logger.items(): + logger.addHandler(self.logger_file_handler) + + @property + def debug(self) -> bool: + """Debug status + + :param value: The debug status, True or False. + :type: bool + """ + return self.__debug + + @debug.setter + def debug(self, value: bool) -> None: + """Debug status + + :param value: The debug status, True or False. + :type: bool + """ + self.__debug = value + if self.__debug: + # if debug status is True, turn on debug logging + for _, logger in self.logger.items(): + logger.setLevel(logging.DEBUG) + # turn on httplib debug + httplib.HTTPConnection.debuglevel = 1 + else: + # if debug status is False, turn off debug logging, + # setting log level to default `logging.WARNING` + for _, logger in self.logger.items(): + logger.setLevel(logging.WARNING) + # turn off httplib debug + httplib.HTTPConnection.debuglevel = 0 + + @property + def logger_format(self) -> str: + """The logger format. + + The logger_formatter will be updated when sets logger_format. + + :param value: The format string. + :type: str + """ + return self.__logger_format + + @logger_format.setter + def logger_format(self, value: str) -> None: + """The logger format. + + The logger_formatter will be updated when sets logger_format. + + :param value: The format string. + :type: str + """ + self.__logger_format = value + self.logger_formatter = logging.Formatter(self.__logger_format) + + def get_api_key_with_prefix(self, identifier: str, alias: Optional[str]=None) -> Optional[str]: + """Gets API key (with prefix if set). + + :param identifier: The identifier of apiKey. + :param alias: The alternative identifier of apiKey. + :return: The token for api key authentication. + """ + if self.refresh_api_key_hook is not None: + self.refresh_api_key_hook(self) + key = self.api_key.get(identifier, self.api_key.get(alias) if alias is not None else None) + if key: + prefix = self.api_key_prefix.get( + identifier, self.api_key_prefix.get(alias) if alias is not None else None) + if prefix: + return "%s %s" % (prefix, key) + else: + return key + + return None + + def get_basic_auth_token(self) -> Optional[str]: + """Gets HTTP basic authentication header (string). + + :return: The token for basic HTTP authentication. + """ + username = "" + if self.username is not None: + username = self.username + password = "" + if self.password is not None: + password = self.password + + return "Basic " + base64.b64encode( + (username + ":" + password).encode('utf-8') + ).decode('utf-8') + + def auth_settings(self)-> AuthSettings: + """Gets Auth Settings dict for api client. + + :return: The Auth Settings information dict. + """ + auth: AuthSettings = {} + return auth + + def to_debug_report(self) -> str: + """Gets the essential information for debugging. + + :return: The report for debugging. + """ + return "Python SDK Debug Report:\n"\ + "OS: {env}\n"\ + "Python Version: {pyversion}\n"\ + "Version of the API: 1.5.0\n"\ + "SDK Package Version: 1.0.0".\ + format(env=sys.platform, pyversion=sys.version) + + def get_host_settings(self) -> List[HostSetting]: + """Gets an array of host settings + + :return: An array of host settings + """ + return [ + { + 'url': "", + 'description': "No description provided", + } + ] + + def get_host_from_settings( + self, + index: Optional[int], + variables: Optional[ServerVariablesT]=None, + servers: Optional[List[HostSetting]]=None, + ) -> str: + """Gets host URL based on the index and variables + :param index: array index of the host settings + :param variables: hash of variable and the corresponding value + :param servers: an array of host settings or None + :return: URL based on host settings + """ + if index is None: + return self._base_path + + variables = {} if variables is None else variables + servers = self.get_host_settings() if servers is None else servers + + try: + server = servers[index] + except IndexError: + raise ValueError( + "Invalid index {0} when selecting the host settings. " + "Must be less than {1}".format(index, len(servers))) + + url = server['url'] + + # go through variables and replace placeholders + for variable_name, variable in server.get('variables', {}).items(): + used_value = variables.get( + variable_name, variable['default_value']) + + if 'enum_values' in variable \ + and variable['enum_values'] \ + and used_value not in variable['enum_values']: + raise ValueError( + "The variable `{0}` in the host URL has invalid value " + "{1}. Must be {2}.".format( + variable_name, variables[variable_name], + variable['enum_values'])) + + url = url.replace("{" + variable_name + "}", used_value) + + return url + + @property + def host(self) -> str: + """Return generated host.""" + return self.get_host_from_settings(self.server_index, variables=self.server_variables) + + @host.setter + def host(self, value: str) -> None: + """Fix base path.""" + self._base_path = value + self.server_index = None diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/exceptions.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/exceptions.py new file mode 100644 index 0000000..9b4ab79 --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/exceptions.py @@ -0,0 +1,218 @@ +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from typing import Any, Optional +from typing_extensions import Self + +class OpenApiException(Exception): + """The base exception class for all OpenAPIExceptions""" + + +class ApiTypeError(OpenApiException, TypeError): + def __init__(self, msg, path_to_item=None, valid_classes=None, + key_type=None) -> None: + """ Raises an exception for TypeErrors + + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (list): a list of keys an indices to get to the + current_item + None if unset + valid_classes (tuple): the primitive classes that current item + should be an instance of + None if unset + key_type (bool): False if our value is a value in a dict + True if it is a key in a dict + False if our item is an item in a list + None if unset + """ + self.path_to_item = path_to_item + self.valid_classes = valid_classes + self.key_type = key_type + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiTypeError, self).__init__(full_msg) + + +class ApiValueError(OpenApiException, ValueError): + def __init__(self, msg, path_to_item=None) -> None: + """ + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (list) the path to the exception in the + received_data dict. None if unset + """ + + self.path_to_item = path_to_item + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiValueError, self).__init__(full_msg) + + +class ApiAttributeError(OpenApiException, AttributeError): + def __init__(self, msg, path_to_item=None) -> None: + """ + Raised when an attribute reference or assignment fails. + + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (None/list) the path to the exception in the + received_data dict + """ + self.path_to_item = path_to_item + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiAttributeError, self).__init__(full_msg) + + +class ApiKeyError(OpenApiException, KeyError): + def __init__(self, msg, path_to_item=None) -> None: + """ + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (None/list) the path to the exception in the + received_data dict + """ + self.path_to_item = path_to_item + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiKeyError, self).__init__(full_msg) + + +class ApiException(OpenApiException): + + def __init__( + self, + status=None, + reason=None, + http_resp=None, + *, + body: Optional[str] = None, + data: Optional[Any] = None, + ) -> None: + self.status = status + self.reason = reason + self.body = body + self.data = data + self.headers = None + + if http_resp: + if self.status is None: + self.status = http_resp.status + if self.reason is None: + self.reason = http_resp.reason + if self.body is None: + try: + self.body = http_resp.data.decode('utf-8') + except Exception: + pass + self.headers = http_resp.headers + + @classmethod + def from_response( + cls, + *, + http_resp, + body: Optional[str], + data: Optional[Any], + ) -> Self: + if http_resp.status == 400: + raise BadRequestException(http_resp=http_resp, body=body, data=data) + + if http_resp.status == 401: + raise UnauthorizedException(http_resp=http_resp, body=body, data=data) + + if http_resp.status == 403: + raise ForbiddenException(http_resp=http_resp, body=body, data=data) + + if http_resp.status == 404: + raise NotFoundException(http_resp=http_resp, body=body, data=data) + + # Added new conditions for 409 and 422 + if http_resp.status == 409: + raise ConflictException(http_resp=http_resp, body=body, data=data) + + if http_resp.status == 422: + raise UnprocessableEntityException(http_resp=http_resp, body=body, data=data) + + if 500 <= http_resp.status <= 599: + raise ServiceException(http_resp=http_resp, body=body, data=data) + raise ApiException(http_resp=http_resp, body=body, data=data) + + def __str__(self): + """Custom error messages for exception""" + error_message = "({0})\n"\ + "Reason: {1}\n".format(self.status, self.reason) + if self.headers: + error_message += "HTTP response headers: {0}\n".format( + self.headers) + + if self.body: + error_message += "HTTP response body: {0}\n".format(self.body) + + if self.data: + error_message += "HTTP response data: {0}\n".format(self.data) + + return error_message + + +class BadRequestException(ApiException): + pass + + +class NotFoundException(ApiException): + pass + + +class UnauthorizedException(ApiException): + pass + + +class ForbiddenException(ApiException): + pass + + +class ServiceException(ApiException): + pass + + +class ConflictException(ApiException): + """Exception for HTTP 409 Conflict.""" + pass + + +class UnprocessableEntityException(ApiException): + """Exception for HTTP 422 Unprocessable Entity.""" + pass + + +def render_path(path_to_item): + """Returns a string representation of a path""" + result = "" + for pth in path_to_item: + if isinstance(pth, int): + result += "[{0}]".format(pth) + else: + result += "['{0}']".format(pth) + return result diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/__init__.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/__init__.py new file mode 100644 index 0000000..e290214 --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/__init__.py @@ -0,0 +1,33 @@ +# coding: utf-8 + +# flake8: noqa +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + +# import models into model package +from generated.models.column_type import ColumnType +from generated.models.explanation_config import ExplanationConfig +from generated.models.explanation_result import ExplanationResult +from generated.models.predict_request_payload import PredictRequestPayload +from generated.models.predict_request_payload_one_of import PredictRequestPayloadOneOf +from generated.models.predict_request_payload_one_of1 import PredictRequestPayloadOneOf1 +from generated.models.predict_response_metadata import PredictResponseMetadata +from generated.models.predict_response_payload import PredictResponsePayload +from generated.models.predict_response_status import PredictResponseStatus +from generated.models.prediction import Prediction +from generated.models.prediction_config import PredictionConfig +from generated.models.prediction_placeholder import PredictionPlaceholder +from generated.models.prediction_result import PredictionResult +from generated.models.predictions_inner_value import PredictionsInnerValue +from generated.models.rows_inner_value import RowsInnerValue +from generated.models.schema_field_config import SchemaFieldConfig +from generated.models.target_column_config import TargetColumnConfig + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/column_type.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/column_type.py new file mode 100644 index 0000000..424dcd5 --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/column_type.py @@ -0,0 +1,51 @@ +# coding: utf-8 + +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import json +from enum import Enum +from typing_extensions import Self + + +class ColumnType(str, Enum): + """ + Supported column data types for the data schema. Includes base types (string, numeric, date) and additional types derived from SAP CDS (https://cap.cloud.sap/docs/cds/types#core-built-in-types). Additional types are mapped to the corresponding base type internally. All values are lowercase for case-insensitive matching. + """ + + """ + allowed enum values + """ + STRING = 'string' + NUMERIC = 'numeric' + DATE = 'date' + BOOLEAN = 'boolean' + LARGESTRING = 'largestring' + UUID = 'uuid' + INTEGER = 'integer' + INT16 = 'int16' + INT32 = 'int32' + INT64 = 'int64' + UINT8 = 'uint8' + DECIMAL = 'decimal' + DOUBLE = 'double' + TIME = 'time' + DATETIME = 'datetime' + TIMESTAMP = 'timestamp' + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ColumnType from a JSON string""" + return cls(json.loads(json_str)) + + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/explanation_config.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/explanation_config.py new file mode 100644 index 0000000..65b2f81 --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/explanation_config.py @@ -0,0 +1,91 @@ +# coding: utf-8 + +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field +from typing import Any, ClassVar, Dict, List, Optional +from typing_extensions import Annotated +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class ExplanationConfig(BaseModel): + """ + Configuration for explainability outputs. + """ # noqa: E501 + top_column_scores: Optional[Annotated[int, Field(le=20, strict=True, ge=0)]] = Field(default=0, description="For how many columns to output column scores (optional, default is 0). 0 by default (no explainability). Max value is 20.") + top_relevant_context_rows: Optional[Annotated[int, Field(le=20, strict=True, ge=0)]] = Field(default=0, description="For how many context rows to return indices per query row (optional, default is 0). 0 by default (no explainability). Max value is 20.") + __properties: ClassVar[List[str]] = ["top_column_scores", "top_relevant_context_rows"] + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of ExplanationConfig from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of ExplanationConfig from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "top_column_scores": obj.get("top_column_scores") if obj.get("top_column_scores") is not None else 0, + "top_relevant_context_rows": obj.get("top_relevant_context_rows") if obj.get("top_relevant_context_rows") is not None else 0 + }) + return _obj + + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/explanation_result.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/explanation_result.py new file mode 100644 index 0000000..eb8d5a8 --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/explanation_result.py @@ -0,0 +1,100 @@ +# coding: utf-8 + +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field, StrictFloat, StrictInt +from typing import Any, ClassVar, Dict, List, Optional, Union +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class ExplanationResult(BaseModel): + """ + Explanation data for predictions. + """ # noqa: E501 + top_column_scores: Optional[List[Dict[str, Union[StrictFloat, StrictInt]]]] = Field(default=None, description="Column scores per query row extracted from the model (higher means more weight was put on this column).") + top_relevant_context_rows: Optional[List[List[StrictInt]]] = Field(default=None, description="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.") + __properties: ClassVar[List[str]] = ["top_column_scores", "top_relevant_context_rows"] + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of ExplanationResult from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # set to None if top_column_scores (nullable) is None + # and model_fields_set contains the field + if self.top_column_scores is None and "top_column_scores" in self.model_fields_set: + _dict['top_column_scores'] = None + + # set to None if top_relevant_context_rows (nullable) is None + # and model_fields_set contains the field + if self.top_relevant_context_rows is None and "top_relevant_context_rows" in self.model_fields_set: + _dict['top_relevant_context_rows'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of ExplanationResult from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "top_column_scores": obj.get("top_column_scores"), + "top_relevant_context_rows": obj.get("top_relevant_context_rows") + }) + return _obj + + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predict_request_payload.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predict_request_payload.py new file mode 100644 index 0000000..b1d3643 --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predict_request_payload.py @@ -0,0 +1,137 @@ +# coding: utf-8 + +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import json +import pprint +from pydantic import BaseModel, ConfigDict, Field, StrictStr, ValidationError, field_validator +from typing import Any, List, Optional +from generated.models.predict_request_payload_one_of import PredictRequestPayloadOneOf +from generated.models.predict_request_payload_one_of1 import PredictRequestPayloadOneOf1 +from pydantic import StrictStr, Field +from typing import Union, List, Set, Optional, Dict +from typing_extensions import Literal, Self + +PREDICTREQUESTPAYLOAD_ONE_OF_SCHEMAS = ["PredictRequestPayloadOneOf", "PredictRequestPayloadOneOf1"] + +class PredictRequestPayload(BaseModel): + """ + Users need to specify a list of rows, which contains both the context rows and the rows for which to predict a label, and a mapping of column names to placeholder values. The model will predict the value for any column specified in `target_columns` for all rows that have the prediction placeholder in that column. Either \"rows\" or \"columns\" must be provided, but not both. + """ + # data type: PredictRequestPayloadOneOf + oneof_schema_1_validator: Optional[PredictRequestPayloadOneOf] = None + # data type: PredictRequestPayloadOneOf1 + oneof_schema_2_validator: Optional[PredictRequestPayloadOneOf1] = None + actual_instance: Optional[Union[PredictRequestPayloadOneOf, PredictRequestPayloadOneOf1]] = None + one_of_schemas: Set[str] = { "PredictRequestPayloadOneOf", "PredictRequestPayloadOneOf1" } + + model_config = ConfigDict( + validate_assignment=True, + protected_namespaces=(), + ) + + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + if kwargs: + raise ValueError("If a position argument is used, keyword arguments cannot be used.") + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator('actual_instance') + def actual_instance_must_validate_oneof(cls, v): + instance = PredictRequestPayload.model_construct() + error_messages = [] + match = 0 + # validate data type: PredictRequestPayloadOneOf + if not isinstance(v, PredictRequestPayloadOneOf): + error_messages.append(f"Error! Input type `{type(v)}` is not `PredictRequestPayloadOneOf`") + else: + match += 1 + # validate data type: PredictRequestPayloadOneOf1 + if not isinstance(v, PredictRequestPayloadOneOf1): + error_messages.append(f"Error! Input type `{type(v)}` is not `PredictRequestPayloadOneOf1`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when setting `actual_instance` in PredictRequestPayload with oneOf schemas: PredictRequestPayloadOneOf, PredictRequestPayloadOneOf1. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when setting `actual_instance` in PredictRequestPayload with oneOf schemas: PredictRequestPayloadOneOf, PredictRequestPayloadOneOf1. Details: " + ", ".join(error_messages)) + else: + return v + + @classmethod + def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into PredictRequestPayloadOneOf + try: + instance.actual_instance = PredictRequestPayloadOneOf.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into PredictRequestPayloadOneOf1 + try: + instance.actual_instance = PredictRequestPayloadOneOf1.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when deserializing the JSON string into PredictRequestPayload with oneOf schemas: PredictRequestPayloadOneOf, PredictRequestPayloadOneOf1. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when deserializing the JSON string into PredictRequestPayload with oneOf schemas: PredictRequestPayloadOneOf, PredictRequestPayloadOneOf1. Details: " + ", ".join(error_messages)) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Optional[Union[Dict[str, Any], PredictRequestPayloadOneOf, PredictRequestPayloadOneOf1]]: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predict_request_payload_one_of.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predict_request_payload_one_of.py new file mode 100644 index 0000000..ac29f4f --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predict_request_payload_one_of.py @@ -0,0 +1,134 @@ +# coding: utf-8 + +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictStr +from typing import Any, ClassVar, Dict, List, Optional +from generated.models.prediction_config import PredictionConfig +from generated.models.rows_inner_value import RowsInnerValue +from generated.models.schema_field_config import SchemaFieldConfig +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class PredictRequestPayloadOneOf(BaseModel): + """ + PredictRequestPayloadOneOf + """ # noqa: E501 + prediction_config: PredictionConfig = Field(description="Configuration of target columns and placeholder value.") + index_column: Optional[StrictStr] = Field(default=None, description="The name of the index column. If provided, the service will return this column's value in each prediction object to facilitate aligning the output predictions with the input rows on the client side. If not provided, the column will not be included in the output.") + parse_data_types: Optional[StrictBool] = Field(default=True, description="Whether to parse the data types of the columns. If set to True, numeric columns will be parsed to float or integer and dates in ISO format YYYY-MM-DD will be parsed.") + data_schema: Optional[Dict[str, SchemaFieldConfig]] = Field(default=None, description="Optional schema defining the data types of each column. If provided, this will override automatic data type parsing.") + rows: List[Dict[str, Optional[RowsInnerValue]]] = Field(description="Table rows, i.e. list of objects where each object is a mapping of column names to values. Either \"rows\" or \"columns\" must be provided.") + __properties: ClassVar[List[str]] = ["prediction_config", "index_column", "parse_data_types", "data_schema", "rows"] + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of PredictRequestPayloadOneOf from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of prediction_config + if self.prediction_config: + _dict['prediction_config'] = self.prediction_config.to_dict() + # override the default output from pydantic by calling `to_dict()` of each value in data_schema (dict) + _field_dict = {} + if self.data_schema: + for _key_data_schema in self.data_schema: + _field_dict[_key_data_schema] = self.data_schema[_key_data_schema].to_dict() if self.data_schema[_key_data_schema] is not None else None + _dict['data_schema'] = _field_dict + # override the default output from pydantic by calling `to_dict()` of each item in rows (list of dict) + _items = [] + if self.rows: + for _item_rows in self.rows: + _items.append( + {_inner_key: _inner_value.to_dict() if _inner_value is not None else None for _inner_key, _inner_value in _item_rows.items()} if _item_rows is not None else None + ) + _dict['rows'] = _items + # set to None if index_column (nullable) is None + # and model_fields_set contains the field + if self.index_column is None and "index_column" in self.model_fields_set: + _dict['index_column'] = None + + # set to None if data_schema (nullable) is None + # and model_fields_set contains the field + if self.data_schema is None and "data_schema" in self.model_fields_set: + _dict['data_schema'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of PredictRequestPayloadOneOf from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "prediction_config": PredictionConfig.from_dict(obj["prediction_config"]) if obj.get("prediction_config") is not None else None, + "index_column": obj.get("index_column"), + "parse_data_types": obj.get("parse_data_types") if obj.get("parse_data_types") is not None else True, + "data_schema": dict( + (_k, SchemaFieldConfig.from_dict(_v)) + for _k, _v in obj["data_schema"].items() + ) + if obj.get("data_schema") is not None + else None, + "rows": [ + {_inner_key: RowsInnerValue.from_dict(_inner_value) for _inner_key, _inner_value in _item.items()} if _item is not None else None + for _item in obj["rows"] + ] if obj.get("rows") is not None else None + }) + return _obj + + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predict_request_payload_one_of1.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predict_request_payload_one_of1.py new file mode 100644 index 0000000..2276166 --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predict_request_payload_one_of1.py @@ -0,0 +1,136 @@ +# coding: utf-8 + +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictStr +from typing import Any, ClassVar, Dict, List, Optional +from generated.models.prediction_config import PredictionConfig +from generated.models.rows_inner_value import RowsInnerValue +from generated.models.schema_field_config import SchemaFieldConfig +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class PredictRequestPayloadOneOf1(BaseModel): + """ + PredictRequestPayloadOneOf1 + """ # noqa: E501 + prediction_config: PredictionConfig = Field(description="Configuration of target columns and placeholder value.") + index_column: Optional[StrictStr] = Field(default=None, description="The name of the index column. If provided, the service will return this column's value in each prediction object to facilitate aligning the output predictions with the input rows on the client side. If not provided, the column will not be included in the output.") + parse_data_types: Optional[StrictBool] = Field(default=True, description="Whether to parse the data types of the columns. If set to True, numeric columns will be parsed to float or integer and dates in ISO format YYYY-MM-DD will be parsed.") + data_schema: Optional[Dict[str, SchemaFieldConfig]] = Field(default=None, description="Optional schema defining the data types of each column. If provided, this will override automatic data type parsing.") + columns: Dict[str, List[Optional[RowsInnerValue]]] = Field(description="Alternative to rows: columns of data where each key is a column name and the value is a list of all column values. Either \"rows\" or \"columns\" must be provided.") + __properties: ClassVar[List[str]] = ["prediction_config", "index_column", "parse_data_types", "data_schema", "columns"] + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of PredictRequestPayloadOneOf1 from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of prediction_config + if self.prediction_config: + _dict['prediction_config'] = self.prediction_config.to_dict() + # override the default output from pydantic by calling `to_dict()` of each value in data_schema (dict) + _field_dict = {} + if self.data_schema: + for _key_data_schema in self.data_schema: + _field_dict[_key_data_schema] = self.data_schema[_key_data_schema].to_dict() if self.data_schema[_key_data_schema] is not None else None + _dict['data_schema'] = _field_dict + # override the default output from pydantic by calling `to_dict()` of each value in columns (dict of array) + _field_dict_of_array = {} + if self.columns: + for _key_columns in self.columns: + _field_dict_of_array[_key_columns] = [ + _item.to_dict() if _item is not None else None for _item in self.columns[_key_columns] + ] if self.columns[_key_columns] is not None else None + _dict['columns'] = _field_dict_of_array + # set to None if index_column (nullable) is None + # and model_fields_set contains the field + if self.index_column is None and "index_column" in self.model_fields_set: + _dict['index_column'] = None + + # set to None if data_schema (nullable) is None + # and model_fields_set contains the field + if self.data_schema is None and "data_schema" in self.model_fields_set: + _dict['data_schema'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of PredictRequestPayloadOneOf1 from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "prediction_config": PredictionConfig.from_dict(obj["prediction_config"]) if obj.get("prediction_config") is not None else None, + "index_column": obj.get("index_column"), + "parse_data_types": obj.get("parse_data_types") if obj.get("parse_data_types") is not None else True, + "data_schema": dict( + (_k, SchemaFieldConfig.from_dict(_v)) + for _k, _v in obj["data_schema"].items() + ) + if obj.get("data_schema") is not None + else None, + "columns": { + _k: [RowsInnerValue.from_dict(_item) for _item in _v] if _v is not None else None + for _k, _v in obj["columns"].items() + } + if obj.get("columns") is not None + else None + }) + return _obj + + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predict_response_metadata.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predict_response_metadata.py new file mode 100644 index 0000000..7328f6a --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predict_response_metadata.py @@ -0,0 +1,94 @@ +# coding: utf-8 + +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field, StrictInt +from typing import Any, ClassVar, Dict, List +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class PredictResponseMetadata(BaseModel): + """ + Metadata about the prediction request. + """ # noqa: E501 + num_columns: StrictInt = Field(description="Number of columns in the input data.") + num_rows: StrictInt = Field(description="Number of rows in the input data.") + num_predictions: StrictInt = Field(description="Number of table cells containing the specified placeholder value.") + num_query_rows: StrictInt = Field(description="Number of rows for which a prediction was made.") + __properties: ClassVar[List[str]] = ["num_columns", "num_rows", "num_predictions", "num_query_rows"] + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of PredictResponseMetadata from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of PredictResponseMetadata from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "num_columns": obj.get("num_columns"), + "num_rows": obj.get("num_rows"), + "num_predictions": obj.get("num_predictions"), + "num_query_rows": obj.get("num_query_rows") + }) + return _obj + + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predict_response_payload.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predict_response_payload.py new file mode 100644 index 0000000..7c801ee --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predict_response_payload.py @@ -0,0 +1,125 @@ +# coding: utf-8 + +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field, StrictStr +from typing import Any, ClassVar, Dict, List, Optional +from generated.models.explanation_result import ExplanationResult +from generated.models.predict_response_metadata import PredictResponseMetadata +from generated.models.predict_response_status import PredictResponseStatus +from generated.models.predictions_inner_value import PredictionsInnerValue +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class PredictResponsePayload(BaseModel): + """ + Response payload for prediction requests. Contains a list of prediction results. + """ # noqa: E501 + id: StrictStr = Field(description="Unique ID for the request.") + status: PredictResponseStatus = Field(description="Status message that can indicate warnings (e.g. about suboptimal data).") + predictions: List[Dict[str, PredictionsInnerValue]] = Field(description="Mapping of column names to their list of prediction results or index column.") + explanations: Optional[ExplanationResult] = Field(default=None, description="Explanation data containing context row and column scores.") + metadata: PredictResponseMetadata + __properties: ClassVar[List[str]] = ["id", "status", "predictions", "explanations", "metadata"] + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of PredictResponsePayload from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of status + if self.status: + _dict['status'] = self.status.to_dict() + # override the default output from pydantic by calling `to_dict()` of each item in predictions (list of dict) + _items = [] + if self.predictions: + for _item_predictions in self.predictions: + _items.append( + {_inner_key: _inner_value.to_dict() if _inner_value is not None else None for _inner_key, _inner_value in _item_predictions.items()} if _item_predictions is not None else None + ) + _dict['predictions'] = _items + # override the default output from pydantic by calling `to_dict()` of explanations + if self.explanations: + _dict['explanations'] = self.explanations.to_dict() + # override the default output from pydantic by calling `to_dict()` of metadata + if self.metadata: + _dict['metadata'] = self.metadata.to_dict() + # set to None if explanations (nullable) is None + # and model_fields_set contains the field + if self.explanations is None and "explanations" in self.model_fields_set: + _dict['explanations'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of PredictResponsePayload from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "id": obj.get("id"), + "status": PredictResponseStatus.from_dict(obj["status"]) if obj.get("status") is not None else None, + "predictions": [ + {_inner_key: PredictionsInnerValue.from_dict(_inner_value) for _inner_key, _inner_value in _item.items()} if _item is not None else None + for _item in obj["predictions"] + ] if obj.get("predictions") is not None else None, + "explanations": ExplanationResult.from_dict(obj["explanations"]) if obj.get("explanations") is not None else None, + "metadata": PredictResponseMetadata.from_dict(obj["metadata"]) if obj.get("metadata") is not None else None + }) + return _obj + + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predict_response_status.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predict_response_status.py new file mode 100644 index 0000000..e97d144 --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predict_response_status.py @@ -0,0 +1,90 @@ +# coding: utf-8 + +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr +from typing import Any, ClassVar, Dict, List +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class PredictResponseStatus(BaseModel): + """ + Output status for prediction requests. + """ # noqa: E501 + code: StrictInt = Field(description="Status code (zero means success, other status codes indicate warnings or errors)") + message: StrictStr = Field(description="Status message, either \"ok\" or contains a warning / more information.") + __properties: ClassVar[List[str]] = ["code", "message"] + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of PredictResponseStatus from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of PredictResponseStatus from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "code": obj.get("code"), + "message": obj.get("message") + }) + return _obj + + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/prediction.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/prediction.py new file mode 100644 index 0000000..51e56c1 --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/prediction.py @@ -0,0 +1,138 @@ +# coding: utf-8 + +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +from inspect import getfullargspec +import json +import pprint +import re # noqa: F401 +from pydantic import BaseModel, ConfigDict, Field, StrictFloat, StrictInt, StrictStr, ValidationError, field_validator +from typing import Optional, Union +from typing import Union, Any, List, Set, TYPE_CHECKING, Optional, Dict +from typing_extensions import Literal, Self +from pydantic import Field + +PREDICTION_ANY_OF_SCHEMAS = ["float", "str"] + +class Prediction(BaseModel): + """ + The predicted value for the column (string for classification, number for regression). + """ + + # data type: str + anyof_schema_1_validator: Optional[StrictStr] = None + # data type: float + anyof_schema_2_validator: Optional[Union[StrictFloat, StrictInt]] = None + if TYPE_CHECKING: + actual_instance: Optional[Union[float, str]] = None + else: + actual_instance: Any = None + any_of_schemas: Set[str] = { "float", "str" } + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + if kwargs: + raise ValueError("If a position argument is used, keyword arguments cannot be used.") + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator('actual_instance') + def actual_instance_must_validate_anyof(cls, v): + instance = Prediction.model_construct() + error_messages = [] + # validate data type: str + try: + instance.anyof_schema_1_validator = v + return v + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: float + try: + instance.anyof_schema_2_validator = v + return v + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if error_messages: + # no match + raise ValueError("No match found when setting the actual_instance in Prediction with anyOf schemas: float, str. Details: " + ", ".join(error_messages)) + else: + return v + + @classmethod + def from_dict(cls, obj: Dict[str, Any]) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + # deserialize data into str + try: + # validation + instance.anyof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.anyof_schema_1_validator + return instance + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into float + try: + # validation + instance.anyof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.anyof_schema_2_validator + return instance + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if error_messages: + # no match + raise ValueError("No match found when deserializing the JSON string into Prediction with anyOf schemas: float, str. Details: " + ", ".join(error_messages)) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Optional[Union[Dict[str, Any], float, str]]: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): + return self.actual_instance.to_dict() + else: + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/prediction_config.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/prediction_config.py new file mode 100644 index 0000000..db9c39d --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/prediction_config.py @@ -0,0 +1,101 @@ +# coding: utf-8 + +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field +from typing import Any, ClassVar, Dict, List, Optional +from generated.models.explanation_config import ExplanationConfig +from generated.models.target_column_config import TargetColumnConfig +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class PredictionConfig(BaseModel): + """ + Configuration of the prediction model. + """ # noqa: E501 + target_columns: List[TargetColumnConfig] + explanations: Optional[ExplanationConfig] = Field(default=None, description="Optional configuration for explainability outputs (column scores and relevant context rows).") + __properties: ClassVar[List[str]] = ["target_columns", "explanations"] + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of PredictionConfig from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in target_columns (list) + _items = [] + if self.target_columns: + for _item_target_columns in self.target_columns: + _items.append(_item_target_columns.to_dict() if _item_target_columns is not None else None) + _dict['target_columns'] = _items + # override the default output from pydantic by calling `to_dict()` of explanations + if self.explanations: + _dict['explanations'] = self.explanations.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of PredictionConfig from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "target_columns": [TargetColumnConfig.from_dict(_item) for _item in obj["target_columns"]] if obj.get("target_columns") is not None else None, + "explanations": ExplanationConfig.from_dict(obj["explanations"]) if obj.get("explanations") is not None else None + }) + return _obj + + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/prediction_placeholder.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/prediction_placeholder.py new file mode 100644 index 0000000..7ba7911 --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/prediction_placeholder.py @@ -0,0 +1,144 @@ +# coding: utf-8 + +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +from inspect import getfullargspec +import json +import pprint +import re # noqa: F401 +from pydantic import BaseModel, ConfigDict, Field, StrictFloat, StrictInt, StrictStr, ValidationError, field_validator +from typing import Optional, Union +from typing import Union, Any, List, Set, TYPE_CHECKING, Optional, Dict +from typing_extensions import Literal, Self +from pydantic import Field + +PREDICTIONPLACEHOLDER_ANY_OF_SCHEMAS = ["float", "str"] + +class PredictionPlaceholder(BaseModel): + """ + The placeholder value in any column for which to predict a value. The model will predict a value for all table cells containing this value. + """ + + # data type: str + anyof_schema_1_validator: Optional[StrictStr] = None + # data type: float + anyof_schema_2_validator: Optional[Union[StrictFloat, StrictInt]] = None + if TYPE_CHECKING: + actual_instance: Optional[Union[float, str]] = None + else: + actual_instance: Any = None + any_of_schemas: Set[str] = { "float", "str" } + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + if kwargs: + raise ValueError("If a position argument is used, keyword arguments cannot be used.") + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator('actual_instance') + def actual_instance_must_validate_anyof(cls, v): + if v is None: + return v + + instance = PredictionPlaceholder.model_construct() + error_messages = [] + # validate data type: str + try: + instance.anyof_schema_1_validator = v + return v + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: float + try: + instance.anyof_schema_2_validator = v + return v + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if error_messages: + # no match + raise ValueError("No match found when setting the actual_instance in PredictionPlaceholder with anyOf schemas: float, str. Details: " + ", ".join(error_messages)) + else: + return v + + @classmethod + def from_dict(cls, obj: Dict[str, Any]) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + if json_str is None: + return instance + + error_messages = [] + # deserialize data into str + try: + # validation + instance.anyof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.anyof_schema_1_validator + return instance + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into float + try: + # validation + instance.anyof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.anyof_schema_2_validator + return instance + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if error_messages: + # no match + raise ValueError("No match found when deserializing the JSON string into PredictionPlaceholder with anyOf schemas: float, str. Details: " + ", ".join(error_messages)) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Optional[Union[Dict[str, Any], float, str]]: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): + return self.actual_instance.to_dict() + else: + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/prediction_result.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/prediction_result.py new file mode 100644 index 0000000..602f743 --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/prediction_result.py @@ -0,0 +1,120 @@ +# coding: utf-8 + +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field +from typing import Any, ClassVar, Dict, List, Optional, Union +from typing_extensions import Annotated +from generated.models.prediction import Prediction +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class PredictionResult(BaseModel): + """ + A single prediction result for a single column in a single row. + """ # noqa: E501 + prediction: Prediction + confidence: Optional[Union[Annotated[float, Field(le=1, strict=True, ge=0)], Annotated[int, Field(le=1, strict=True, ge=0)]]] = Field(default=None, description="The confidence of the prediction (null for regression predictions).") + confidence_interval: Optional[Annotated[List[Any], Field(min_length=2, max_length=2)]] = Field(default=None, description="Lower and upper bounds of the prediction confidence interval (null for classification predictions).") + additional_properties: Dict[str, Any] = {} + __properties: ClassVar[List[str]] = ["prediction", "confidence", "confidence_interval"] + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of PredictionResult from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + * Fields in `self.additional_properties` are added to the output dict. + """ + excluded_fields: Set[str] = set([ + "additional_properties", + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of prediction + if self.prediction: + _dict['prediction'] = self.prediction.to_dict() + # puts key-value pairs in additional_properties in the top level + if self.additional_properties is not None: + for _key, _value in self.additional_properties.items(): + _dict[_key] = _value + + # set to None if confidence (nullable) is None + # and model_fields_set contains the field + if self.confidence is None and "confidence" in self.model_fields_set: + _dict['confidence'] = None + + # set to None if confidence_interval (nullable) is None + # and model_fields_set contains the field + if self.confidence_interval is None and "confidence_interval" in self.model_fields_set: + _dict['confidence_interval'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of PredictionResult from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "prediction": Prediction.from_dict(obj["prediction"]) if obj.get("prediction") is not None else None, + "confidence": obj.get("confidence"), + "confidence_interval": obj.get("confidence_interval") + }) + # store additional fields in additional_properties + for _key in obj.keys(): + if _key not in cls.__properties: + _obj.additional_properties[_key] = obj.get(_key) + + return _obj + + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predictions_inner_value.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predictions_inner_value.py new file mode 100644 index 0000000..cc6f643 --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/predictions_inner_value.py @@ -0,0 +1,156 @@ +# coding: utf-8 + +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +from inspect import getfullargspec +import json +import pprint +import re # noqa: F401 +from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr, ValidationError, field_validator +from typing import List, Optional +from generated.models.prediction_result import PredictionResult +from typing import Union, Any, List, Set, TYPE_CHECKING, Optional, Dict +from typing_extensions import Literal, Self +from pydantic import Field + +PREDICTIONSINNERVALUE_ANY_OF_SCHEMAS = ["List[PredictionResult]", "int", "str"] + +class PredictionsInnerValue(BaseModel): + """ + PredictionsInnerValue + """ + + # data type: List[PredictionResult] + anyof_schema_1_validator: Optional[List[PredictionResult]] = None + # data type: str + anyof_schema_2_validator: Optional[StrictStr] = None + # data type: int + anyof_schema_3_validator: Optional[StrictInt] = None + if TYPE_CHECKING: + actual_instance: Optional[Union[List[PredictionResult], int, str]] = None + else: + actual_instance: Any = None + any_of_schemas: Set[str] = { "List[PredictionResult]", "int", "str" } + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + if kwargs: + raise ValueError("If a position argument is used, keyword arguments cannot be used.") + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator('actual_instance') + def actual_instance_must_validate_anyof(cls, v): + instance = PredictionsInnerValue.model_construct() + error_messages = [] + # validate data type: List[PredictionResult] + try: + instance.anyof_schema_1_validator = v + return v + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: str + try: + instance.anyof_schema_2_validator = v + return v + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: int + try: + instance.anyof_schema_3_validator = v + return v + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if error_messages: + # no match + raise ValueError("No match found when setting the actual_instance in PredictionsInnerValue with anyOf schemas: List[PredictionResult], int, str. Details: " + ", ".join(error_messages)) + else: + return v + + @classmethod + def from_dict(cls, obj: Dict[str, Any]) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + # deserialize data into List[PredictionResult] + try: + # validation + instance.anyof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.anyof_schema_1_validator + return instance + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into str + try: + # validation + instance.anyof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.anyof_schema_2_validator + return instance + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into int + try: + # validation + instance.anyof_schema_3_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.anyof_schema_3_validator + return instance + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if error_messages: + # no match + raise ValueError("No match found when deserializing the JSON string into PredictionsInnerValue with anyOf schemas: List[PredictionResult], int, str. Details: " + ", ".join(error_messages)) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Optional[Union[Dict[str, Any], List[PredictionResult], int, str]]: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): + return self.actual_instance.to_dict() + else: + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/rows_inner_value.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/rows_inner_value.py new file mode 100644 index 0000000..3441d7f --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/rows_inner_value.py @@ -0,0 +1,161 @@ +# coding: utf-8 + +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +from inspect import getfullargspec +import json +import pprint +import re # noqa: F401 +from pydantic import BaseModel, ConfigDict, Field, StrictFloat, StrictInt, StrictStr, ValidationError, field_validator +from typing import Optional, Union +from typing import Union, Any, List, Set, TYPE_CHECKING, Optional, Dict +from typing_extensions import Literal, Self +from pydantic import Field + +ROWSINNERVALUE_ANY_OF_SCHEMAS = ["float", "int", "str"] + +class RowsInnerValue(BaseModel): + """ + RowsInnerValue + """ + + # data type: str + anyof_schema_1_validator: Optional[StrictStr] = None + # data type: float + anyof_schema_2_validator: Optional[Union[StrictFloat, StrictInt]] = None + # data type: int + anyof_schema_3_validator: Optional[StrictInt] = None + if TYPE_CHECKING: + actual_instance: Optional[Union[float, int, str]] = None + else: + actual_instance: Any = None + any_of_schemas: Set[str] = { "float", "int", "str" } + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + if kwargs: + raise ValueError("If a position argument is used, keyword arguments cannot be used.") + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator('actual_instance') + def actual_instance_must_validate_anyof(cls, v): + if v is None: + return v + + instance = RowsInnerValue.model_construct() + error_messages = [] + # validate data type: str + try: + instance.anyof_schema_1_validator = v + return v + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: float + try: + instance.anyof_schema_2_validator = v + return v + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: int + try: + instance.anyof_schema_3_validator = v + return v + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if error_messages: + # no match + raise ValueError("No match found when setting the actual_instance in RowsInnerValue with anyOf schemas: float, int, str. Details: " + ", ".join(error_messages)) + else: + return v + + @classmethod + def from_dict(cls, obj: Dict[str, Any]) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + if json_str is None: + return instance + + error_messages = [] + # deserialize data into str + try: + # validation + instance.anyof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.anyof_schema_1_validator + return instance + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into float + try: + # validation + instance.anyof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.anyof_schema_2_validator + return instance + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into int + try: + # validation + instance.anyof_schema_3_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.anyof_schema_3_validator + return instance + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if error_messages: + # no match + raise ValueError("No match found when deserializing the JSON string into RowsInnerValue with anyOf schemas: float, int, str. Details: " + ", ".join(error_messages)) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Optional[Union[Dict[str, Any], float, int, str]]: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): + return self.actual_instance.to_dict() + else: + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/schema_field_config.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/schema_field_config.py new file mode 100644 index 0000000..6264b1e --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/schema_field_config.py @@ -0,0 +1,89 @@ +# coding: utf-8 + +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field +from typing import Any, ClassVar, Dict, List +from generated.models.column_type import ColumnType +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class SchemaFieldConfig(BaseModel): + """ + Configuration for a single field in the input data schema. + """ # noqa: E501 + dtype: ColumnType = Field(description="The data type of the column. Supports base types (string, numeric, date) and extended types (e.g., Boolean, Integer, Timestamp). Extended types are mapped to corresponding base types internally. Case-insensitive.") + __properties: ClassVar[List[str]] = ["dtype"] + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of SchemaFieldConfig from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of SchemaFieldConfig from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "dtype": obj.get("dtype") + }) + return _obj + + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/target_column_config.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/target_column_config.py new file mode 100644 index 0000000..e525f4d --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/models/target_column_config.py @@ -0,0 +1,136 @@ +# coding: utf-8 + +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr, field_validator +from typing import Any, ClassVar, Dict, List, Optional +from generated.models.prediction_placeholder import PredictionPlaceholder +from typing import Optional, Set +from typing_extensions import Self +from pydantic_core import to_jsonable_python + +class TargetColumnConfig(BaseModel): + """ + Configuration for a target column in the prediction model. + """ # noqa: E501 + name: StrictStr = Field(description="The name of the target column.") + prediction_placeholder: Optional[PredictionPlaceholder] + task_type: Optional[StrictStr] = Field(default=None, description="The type of prediction task for this column. If not provided, the model will infer the task type from the data.") + top_k: Optional[StrictInt] = Field(default=None, description="How many predictions to output for this classification column.If not provided, only a single prediction is returned. Only relevant for classification.") + additional_properties: Dict[str, Any] = {} + __properties: ClassVar[List[str]] = ["name", "prediction_placeholder", "task_type", "top_k"] + + @field_validator('task_type') + def task_type_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + if value not in set(['classification', 'regression']): + raise ValueError("must be one of enum values ('classification', 'regression')") + return value + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(to_jsonable_python(self.to_dict())) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of TargetColumnConfig from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + * Fields in `self.additional_properties` are added to the output dict. + """ + excluded_fields: Set[str] = set([ + "additional_properties", + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of prediction_placeholder + if self.prediction_placeholder: + _dict['prediction_placeholder'] = self.prediction_placeholder.to_dict() + # puts key-value pairs in additional_properties in the top level + if self.additional_properties is not None: + for _key, _value in self.additional_properties.items(): + _dict[_key] = _value + + # set to None if prediction_placeholder (nullable) is None + # and model_fields_set contains the field + if self.prediction_placeholder is None and "prediction_placeholder" in self.model_fields_set: + _dict['prediction_placeholder'] = None + + # set to None if task_type (nullable) is None + # and model_fields_set contains the field + if self.task_type is None and "task_type" in self.model_fields_set: + _dict['task_type'] = None + + # set to None if top_k (nullable) is None + # and model_fields_set contains the field + if self.top_k is None and "top_k" in self.model_fields_set: + _dict['top_k'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of TargetColumnConfig from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "name": obj.get("name"), + "prediction_placeholder": PredictionPlaceholder.from_dict(obj["prediction_placeholder"]) if obj.get("prediction_placeholder") is not None else None, + "task_type": obj.get("task_type"), + "top_k": obj.get("top_k") + }) + # store additional fields in additional_properties + for _key in obj.keys(): + if _key not in cls.__properties: + _obj.additional_properties[_key] = obj.get(_key) + + return _obj + + diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/py.typed b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/rest.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/rest.py new file mode 100644 index 0000000..b3e0473 --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated/rest.py @@ -0,0 +1,201 @@ +# coding: utf-8 + +""" + SAP RPT + + A REST API for in-context learning with SAP RPT models. + + The version of the OpenAPI document: 1.5.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +import io +import json +import re +import ssl +from typing import Optional, Union + +import httpx + +from generated.exceptions import ApiException, ApiValueError + +RESTResponseType = httpx.Response + +class RESTResponse(io.IOBase): + + def __init__(self, resp) -> None: + self.response = resp + self.status = resp.status_code + self.reason = resp.reason_phrase + self.data = None + + async def read(self): + if self.data is None: + self.data = await self.response.aread() + return self.data + + @property + def headers(self): + """Returns a CIMultiDictProxy of response headers.""" + return self.response.headers + + def getheaders(self): + """Returns a CIMultiDictProxy of the response headers; use ``headers`` instead.""" + return self.response.headers + + def getheader(self, name, default=None): + """Returns a given response header; use ``headers`` instead.""" + return self.response.headers.get(name, default) + + +class RESTClientObject: + + def __init__(self, configuration) -> None: + + # maxsize is number of requests to host that are allowed in parallel + self.maxsize = configuration.connection_pool_maxsize + + self.ssl_context = ssl.create_default_context( + cafile=configuration.ssl_ca_cert, + cadata=configuration.ca_cert_data, + ) + if configuration.cert_file: + self.ssl_context.load_cert_chain( + configuration.cert_file, keyfile=configuration.key_file + ) + + if not configuration.verify_ssl: + self.ssl_context.check_hostname = False + self.ssl_context.verify_mode = ssl.CERT_NONE + + self.proxy = configuration.proxy + self.proxy_headers = configuration.proxy_headers + + self.pool_manager: Optional[httpx.AsyncClient] = None + + async def close(self): + if self.pool_manager is not None: + await self.pool_manager.aclose() + + async def request( + self, + method, + url, + headers=None, + body=None, + post_params=None, + _request_timeout=None): + """Execute request + + :param method: http request method + :param url: http request url + :param headers: http request headers + :param body: request json body, for `application/json` + :param post_params: request post parameters, + `application/x-www-form-urlencoded` + and `multipart/form-data` + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + """ + method = method.upper() + assert method in [ + 'GET', + 'HEAD', + 'DELETE', + 'POST', + 'PUT', + 'PATCH', + 'OPTIONS' + ] + + if post_params and body: + raise ApiValueError( + "body parameter cannot be used with post_params parameter." + ) + + post_params = post_params or {} + headers = headers or {} + timeout = _request_timeout or 5 * 60 + + if 'Content-Type' not in headers: + headers['Content-Type'] = 'application/json' + + args = { + "method": method, + "url": url, + "timeout": timeout, + "headers": headers + } + + # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE` + if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']: + if re.search('json', headers['Content-Type'], re.IGNORECASE): + if body is not None: + args["json"] = body + if body is None and post_params: + args["json"] = dict(post_params) + elif headers['Content-Type'] == 'application/x-www-form-urlencoded': # noqa: E501 + args["data"] = dict(post_params) + elif headers['Content-Type'] == 'multipart/form-data': + # must del headers['Content-Type'], or the correct + # Content-Type which generated by httpx + del headers['Content-Type'] + + files = [] + data = {} + for param in post_params: + k, v = param + if isinstance(v, tuple) and len(v) == 3: + files.append((k, v)) + else: + # Ensures that dict objects are serialized + if isinstance(v, dict): + v = json.dumps(v) + elif isinstance(v, int): + v = str(v) + data[k] = v + + if files: + args["files"] = files + if data: + args["data"] = data + + # Pass a `bytes` parameter directly in the body to support + # other content types than Json when `body` argument is provided + # in serialized form + elif isinstance(body, str) or isinstance(body, bytes): + args["data"] = body + else: + # Cannot generate the request from given parameters + msg = """Cannot prepare a request message for provided + arguments. Please check that your arguments match + declared content type.""" + raise ApiException(status=0, reason=msg) + + if self.pool_manager is None: + self.pool_manager = self._create_pool_manager() + + r = await self.pool_manager.request(**args) + return RESTResponse(r) + + def _create_pool_manager(self) -> httpx.AsyncClient: + limits = httpx.Limits(max_connections=self.maxsize) + + proxy = None + if self.proxy: + proxy = httpx.Proxy( + url=self.proxy, + headers=self.proxy_headers + ) + + return httpx.AsyncClient( + limits=limits, + proxy=proxy, + verify=self.ssl_context, + trust_env=True + ) diff --git a/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/models.py b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/models.py new file mode 100644 index 0000000..ad7b91c --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/rpt_1_5/models.py @@ -0,0 +1,105 @@ +"""Readable type aliases for the RPT 1.5 request/response models. + +The OpenAPI spec defines ``PredictRequestPayload`` as a ``oneOf`` of two +concrete schemas that differ only in how the input data is provided. The +generator names them ``PredictRequestPayloadOneOf`` / ``PredictRequestPayloadOneOf1`` +which gives users no hint about when to use which. This module re-exports +them under descriptive names alongside all other public model types. +""" + +from collections.abc import Mapping, Sequence +from typing import Any + +from generated.models.predict_request_payload_one_of import ( + PredictRequestPayloadOneOf as RowsRequest, +) +from generated.models.predict_request_payload_one_of1 import ( + PredictRequestPayloadOneOf1 as ColumnsRequest, +) +from generated.models.predict_response_metadata import PredictResponseMetadata +from generated.models.predict_response_payload import PredictResponsePayload +from generated.models.predict_response_status import PredictResponseStatus +from generated.models.prediction_config import PredictionConfig +from generated.models.prediction_placeholder import PredictionPlaceholder +from generated.models.prediction_result import PredictionResult +from generated.models.rows_inner_value import RowsInnerValue +from generated.models.schema_field_config import SchemaFieldConfig +from generated.models.target_column_config import TargetColumnConfig + +CellValue = str | float | int | None + + +def rows_request( + prediction_config: PredictionConfig, + rows: Sequence[Mapping[str, Any]], + index_column: str | None = None, + parse_data_types: bool | None = True, +) -> RowsRequest: + """Build a :class:`RowsRequest` from plain dicts. + + Each row is a ``dict[column_name, value]`` with primitive values. + Define a ``TypedDict`` for your row shape to get key autocomplete:: + + class SalesRow(TypedDict): + PRODUCT: str + PRICE: float + SALESGROUP: str + + rows_request(prediction_config=..., rows=[SalesRow(...)]) + """ + return RowsRequest( + prediction_config=prediction_config, + index_column=index_column, + parse_data_types=parse_data_types, + rows=[ + {k: RowsInnerValue(v) for k, v in row.items()} + for row in rows + ], + ) + + +def columns_request( + prediction_config: PredictionConfig, + columns: dict[str, list[CellValue]], + index_column: str | None = None, + parse_data_types: bool | None = True, +) -> ColumnsRequest: + """Build a :class:`ColumnsRequest` from plain column lists. + + ``columns`` maps each column name to its list of values:: + + columns_request( + prediction_config=..., + columns={ + "PRODUCT": ["Laptop", "Chair"], + "PRICE": [999.99, 142.99], + }, + ) + """ + return ColumnsRequest( + prediction_config=prediction_config, + index_column=index_column, + parse_data_types=parse_data_types, + columns={ + col: [RowsInnerValue(v) for v in vals] + for col, vals in columns.items() + }, + ) + + +__all__ = [ + "CellValue", + "ColumnsRequest", + "PredictResponseMetadata", + "PredictResponsePayload", + "PredictResponseStatus", + "PredictionConfig", + "PredictionPlaceholder", + "PredictionResult", + "RowsInnerValue", + "RowsRequest", + "SchemaFieldConfig", + "TargetColumnConfig", + "columns_request", + "rows_request", +] diff --git a/packages/gen/gen_ai_hub/proxy/native/utils.py b/packages/gen/gen_ai_hub/proxy/native/utils.py new file mode 100644 index 0000000..14b9d47 --- /dev/null +++ b/packages/gen/gen_ai_hub/proxy/native/utils.py @@ -0,0 +1,90 @@ +"""Shared utilities for spec-generated native proxy clients.""" +from typing import Any, Union + +import httpx # pylint: disable=import-error + +from gen_ai_hub import GenAIHubProxyClient +from gen_ai_hub.proxy import get_proxy_client + + +def get_proxy_client_instance( + proxy_client: GenAIHubProxyClient | None = None, +) -> GenAIHubProxyClient: + """Return the provided proxy client, or the process-default one.""" + return proxy_client or get_proxy_client(proxy_version="gen-ai-hub") + + +def resolve_deployment_url( + proxy_client: GenAIHubProxyClient, + model_name: str, + model_version: str | None = None, +) -> str: + """Resolve a deployment base URL from model identity via the proxy client.""" + filters = {"model_name": model_name} + if model_version: + filters["model_version"] = model_version + try: + return proxy_client.select_deployment(**filters).url + except ValueError as exc: + raise ValueError( + f"No deployment found for the given parameters: {filters}." + ) from exc + + +def _make_auth_hook(proxy_client: GenAIHubProxyClient): + async def inject_auth(request: httpx.Request) -> None: + for key, value in proxy_client.request_header.items(): + request.headers[key] = value + return inject_auth + + +def build_sap_async_httpx_client( + proxy_client: GenAIHubProxyClient, + timeout: Union[float, "httpx.Timeout", None] = None, +) -> "httpx.AsyncClient": + """httpx.AsyncClient with SAP auth injected via event hook.""" + kwargs: dict[str, Any] = {"event_hooks": {"request": [_make_auth_hook(proxy_client)]}} + if timeout is not None: + kwargs["timeout"] = timeout + return httpx.AsyncClient(**kwargs) + + +def build_sap_api_client( # pylint: disable=too-many-arguments,too-many-positional-arguments + base_url: str, + proxy_client: GenAIHubProxyClient, + api_client_class: Any, + configuration_class: Any, + rest_client_class: Any, + timeout: float | None = None, +) -> Any: + """Build a generated ApiClient subclassed with SAP auth and deployment URL. + + Each generated package has its own ApiClient, Configuration, and RESTClientObject. + Pass those classes here so the SAP auth wiring can be applied generically. + + :param base_url: Deployment URL resolved from the proxy client. + :param proxy_client: Authenticated SAP proxy client. + :param api_client_class: The generated ApiClient class for this package. + :param configuration_class: The generated Configuration class for this package. + :param rest_client_class: The generated RESTClientObject class for this package. + :param timeout: Optional request timeout. + :return: Configured ApiClient instance with SAP auth. + """ + _build_sap_async_httpx_client = build_sap_async_httpx_client # capture for closure + + class _SapRESTClientObject(rest_client_class): # pylint: disable=too-few-public-methods + def __init__(self, configuration: Any) -> None: + super().__init__(configuration) + self._sap_proxy = proxy_client + self._sap_timeout = timeout + + def _create_pool_manager(self) -> Any: + return _build_sap_async_httpx_client(self._sap_proxy, self._sap_timeout) + + class _SapApiClient(api_client_class): # pylint: disable=too-few-public-methods + def __init__(self) -> None: + config = configuration_class(host=base_url) + super().__init__(configuration=config) + self.rest_client = _SapRESTClientObject(config) + + return _SapApiClient() diff --git a/packages/gen/openapi_specs/sap-rpt-1.5_openapi.json b/packages/gen/openapi_specs/sap-rpt-1.5_openapi.json new file mode 100644 index 0000000..e3df281 --- /dev/null +++ b/packages/gen/openapi_specs/sap-rpt-1.5_openapi.json @@ -0,0 +1,1144 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "SAP RPT", + "description": "A REST API for in-context learning with SAP RPT models.", + "version": "1.5.0" + }, + "servers": [ + { + "url": "/" + } + ], + "paths": { + "/health": { + "get": { + "summary": "Health Check", + "operationId": "health", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/predict": { + "post": { + "summary": "Make predictions from JSON (optionally gzip-compressed).", + "operationId": "predict", + "responses": { + "200": { + "description": "Successful Prediction", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PredictResponsePayload" + }, + "example": { + "id": "781bf15e-602a-4503-a8ff-dc32b20f804a", + "status": { + "code": 0, + "message": "ok" + }, + "predictions": [ + { + "COSTCENTER": [ + { + "prediction": "Office Furniture", + "confidence": 0.52 + } + ], + "PRICE": [ + { + "prediction": 195.09017944335938, + "confidence_interval": [ + 191.4201023, + 198.7602565 + ] + } + ], + "ID": "35" + }, + { + "COSTCENTER": [ + { + "prediction": "Data Infrastructure", + "confidence": 1.0 + } + ], + "PRICE": [ + { + "prediction": 209.38052368164062, + "confidence_interval": [ + 198.182501013, + 220.57854635 + ] + } + ], + "ID": "104" + } + ], + "explanations": { + "top_column_scores": [ + { + "PRODUCT": 0.08, + "ORDERDATE": 0.03 + }, + { + "PRODUCT": 0.07, + "ORDERDATE": 0.02 + } + ], + "top_relevant_context_rows": [ + [ + 3, + 4, + 1 + ], + [ + 2, + 1, + 4 + ] + ] + }, + "metadata": { + "num_columns": 5, + "num_rows": 2, + "num_predictions": 4, + "num_query_rows": 2 + } + } + } + } + }, + "400": { + "description": "Bad Request - Invalid input data", + "content": { + "application/json": { + "example": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "status": { + "code": 2, + "message": "Invalid input" + }, + "detail": [ + { + "loc": [ + "body", + "prediction_config" + ], + "msg": "Field required", + "type": "missing" + } + ] + } + } + } + }, + "413": { + "description": "Payload Too Large", + "content": { + "application/json": { + "example": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "status": { + "code": 2, + "message": "Invalid input" + }, + "detail": [ + { + "loc": [], + "msg": "Request body too large (>576716800 bytes)", + "type": "value_error" + } + ] + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "example": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "status": { + "code": 2, + "message": "Invalid input" + }, + "detail": [ + { + "loc": [ + "body", + "prediction_config" + ], + "msg": "Field required", + "type": "value_error" + } + ] + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "example": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "status": { + "code": 3, + "message": "Internal server error" + }, + "detail": [] + } + } + } + }, + "503": { + "description": "Service Unavailable", + "content": { + "application/json": { + "example": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "status": { + "code": 4, + "message": "Server under high load, please try again later" + }, + "detail": [] + } + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PredictRequestPayload" + }, + "examples": { + "classification_example": { + "summary": "Classification Example", + "description": "Predict product category using in-context learning", + "value": { + "index_column": "id", + "prediction_config": { + "target_columns": [ + { + "name": "category", + "prediction_placeholder": "?", + "task_type": "classification", + "top_k": 1 + } + ] + }, + "columns": { + "id": [ + 1, + 2, + 3, + 4 + ], + "product": [ + "Laptop", + "Mouse", + "Keyboard", + "Monitor" + ], + "price": [ + 899, + 25, + 75, + 350 + ], + "category": [ + "Electronics", + "Accessories", + "Accessories", + "?" + ], + "stock": [ + "150", + "500", + "320", + "200" + ] + }, + "data_schema": { + "id": { + "dtype": "numeric" + }, + "product": { + "dtype": "string" + }, + "price": { + "dtype": "numeric" + }, + "category": { + "dtype": "string" + }, + "stock": { + "dtype": "numeric" + } + } + } + }, + "regression_example": { + "summary": "Regression Example", + "description": "Predict multiple columns including regression using in-context learning (note that you can also use null or numeric values as placeholders)", + "value": { + "index_column": "ID", + "prediction_config": { + "target_columns": [ + { + "name": "PRICE", + "prediction_placeholder": "[?]", + "task_type": "regression" + }, + { + "name": "COSTCENTER", + "prediction_placeholder": "[PREDICT]", + "task_type": "classification" + } + ] + }, + "columns": { + "PRODUCT": [ + "Couch", + "Office Chair", + "Server Rack", + "Server Rack" + ], + "PRICE": [ + "[?]", + 150.8, + "210.0", + "[?]" + ], + "ORDERDATE": [ + "2025-11-28", + "2025-11-02", + "2025-11-01", + "2025-11-01" + ], + "ID": [ + "35", + "44", + "108", + "104" + ], + "COSTCENTER": [ + "[PREDICT]", + "Office Furniture", + "Data Infrastructure", + "[PREDICT]" + ] + }, + "data_schema": { + "PRODUCT": { + "dtype": "string" + }, + "PRICE": { + "dtype": "numeric" + }, + "ORDERDATE": { + "dtype": "date" + }, + "ID": { + "dtype": "string" + }, + "COSTCENTER": { + "dtype": "string" + } + } + } + } + } + } + } + }, + "parameters": [ + { + "name": "Content-Encoding", + "in": "header", + "description": "Content encoding of the request body. Use 'gzip' for gzip-compressed payloads. Use compression level 1.", + "required": false, + "schema": { + "type": "string", + "enum": [ + "gzip" + ] + } + } + ] + } + }, + "/predict_parquet": { + "post": { + "summary": "Make predictions from Parquet file", + "operationId": "predict_parquet", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_predict_parquet" + }, + "encoding": { + "file": { + "contentType": "application/vnd.apache.parquet" + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Prediction", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PredictResponsePayload" + }, + "example": { + "id": "781bf15e-602a-4503-a8ff-dc32b20f804a", + "status": { + "code": 0, + "message": "ok" + }, + "predictions": [ + { + "COSTCENTER": [ + { + "prediction": "Office Furniture", + "confidence": 0.52 + } + ], + "PRICE": [ + { + "prediction": 195.09017944335938, + "confidence_interval": [ + 191.4201023, + 198.7602565 + ] + } + ], + "ID": "35" + }, + { + "COSTCENTER": [ + { + "prediction": "Data Infrastructure", + "confidence": 1.0 + } + ], + "PRICE": [ + { + "prediction": 209.38052368164062, + "confidence_interval": [ + 198.182501013, + 220.57854635 + ] + } + ], + "ID": "104" + } + ], + "explanations": { + "top_column_scores": [ + { + "PRODUCT": 0.08, + "ORDERDATE": 0.03 + }, + { + "PRODUCT": 0.07, + "ORDERDATE": 0.02 + } + ], + "top_relevant_context_rows": [ + [ + 3, + 4, + 1 + ], + [ + 2, + 1, + 4 + ] + ] + }, + "metadata": { + "num_columns": 5, + "num_rows": 2, + "num_predictions": 4, + "num_query_rows": 2 + } + } + } + } + }, + "400": { + "description": "Bad Request - Invalid input data", + "content": { + "application/json": { + "example": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "status": { + "code": 2, + "message": "Invalid input" + }, + "detail": [ + { + "loc": [ + "body", + "prediction_config" + ], + "msg": "Field required", + "type": "missing" + } + ] + } + } + } + }, + "413": { + "description": "Payload Too Large", + "content": { + "application/json": { + "example": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "status": { + "code": 2, + "message": "Invalid input" + }, + "detail": [ + { + "loc": [], + "msg": "Request body too large (>10485760 bytes)", + "type": "value_error" + } + ] + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "example": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "status": { + "code": 2, + "message": "Invalid input" + }, + "detail": [ + { + "loc": [ + "body", + "prediction_config" + ], + "msg": "Field required", + "type": "value_error" + } + ] + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "example": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "status": { + "code": 3, + "message": "Internal server error" + }, + "detail": [] + } + } + } + }, + "503": { + "description": "Service Unavailable", + "content": { + "application/json": { + "example": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "status": { + "code": 4, + "message": "Server under high load, please try again later" + }, + "detail": [] + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Body_predict_parquet": { + "properties": { + "file": { + "type": "string", + "contentMediaType": "application/vnd.apache.parquet", + "title": "File" + }, + "prediction_config": { + "type": "string", + "title": "Prediction Config", + "description": "JSON string containing the prediction configuration (see PredictionConfig schema).", + "example": "{\"target_columns\":[{\"name\": \"PRICE\",\"prediction_placeholder\": null,\"task_type\": \"regression\"}]}", + "contentMediaType": "application/json", + "contentSchema": { + "$ref": "#/components/schemas/PredictionConfig" + } + }, + "index_column": { + "type": "string", + "title": "Index Column" + }, + "parse_data_types": { + "type": "boolean", + "title": "Parse Data Types", + "default": false + } + }, + "type": "object", + "required": [ + "file", + "prediction_config" + ], + "title": "Body_predict_parquet" + }, + "ExplanationResult": { + "properties": { + "top_column_scores": { + "anyOf": [ + { + "items": { + "additionalProperties": { + "type": "number" + }, + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Top Column Scores", + "description": "Column scores per query row extracted from the model (higher means more weight was put on this column)." + }, + "top_relevant_context_rows": { + "anyOf": [ + { + "items": { + "items": { + "type": "integer" + }, + "type": "array" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Top Relevant Context Rows", + "description": "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": "object", + "title": "ExplanationResult", + "description": "Explanation data for predictions." + }, + "PredictResponseMetadata": { + "properties": { + "num_columns": { + "type": "integer", + "title": "Num Columns", + "description": "Number of columns in the input data." + }, + "num_rows": { + "type": "integer", + "title": "Num Rows", + "description": "Number of rows in the input data." + }, + "num_predictions": { + "type": "integer", + "title": "Num Predictions", + "description": "Number of table cells containing the specified placeholder value." + }, + "num_query_rows": { + "type": "integer", + "title": "Num Query Rows", + "description": "Number of rows for which a prediction was made." + } + }, + "type": "object", + "required": [ + "num_columns", + "num_rows", + "num_predictions", + "num_query_rows" + ], + "title": "PredictResponseMetadata", + "description": "Metadata about the prediction request." + }, + "PredictResponsePayload": { + "properties": { + "id": { + "type": "string", + "title": "Id", + "description": "Unique ID for the request." + }, + "status": { + "$ref": "#/components/schemas/PredictResponseStatus", + "description": "Status message that can indicate warnings (e.g. about suboptimal data)." + }, + "predictions": { + "items": { + "additionalProperties": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/PredictionResult" + }, + "type": "array" + }, + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "type": "object" + }, + "type": "array", + "title": "Predictions", + "description": "Mapping of column names to their list of prediction results or index column." + }, + "explanations": { + "anyOf": [ + { + "$ref": "#/components/schemas/ExplanationResult" + }, + { + "type": "null" + } + ], + "description": "Explanation data containing context row and column scores." + }, + "metadata": { + "$ref": "#/components/schemas/PredictResponseMetadata" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "status", + "predictions", + "metadata" + ], + "title": "PredictResponsePayload", + "description": "Response payload for prediction requests.\nContains a list of prediction results." + }, + "PredictResponseStatus": { + "properties": { + "code": { + "type": "integer", + "title": "Code", + "description": "Status code (zero means success, other status codes indicate warnings or errors)" + }, + "message": { + "type": "string", + "title": "Message", + "description": "Status message, either \"ok\" or contains a warning / more information." + } + }, + "type": "object", + "required": [ + "code", + "message" + ], + "title": "PredictResponseStatus", + "description": "Output status for prediction requests." + }, + "PredictionResult": { + "properties": { + "prediction": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "title": "Prediction", + "description": "The predicted value for the column (string for classification, number for regression)." + }, + "confidence": { + "anyOf": [ + { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + { + "type": "null" + } + ], + "title": "Confidence", + "description": "The confidence of the prediction (null for regression predictions)." + }, + "confidence_interval": { + "anyOf": [ + { + "prefixItems": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array", + "maxItems": 2, + "minItems": 2 + }, + { + "type": "null" + } + ], + "title": "Confidence Interval", + "description": "Lower and upper bounds of the prediction confidence interval (null for classification predictions)." + } + }, + "additionalProperties": true, + "type": "object", + "required": [ + "prediction" + ], + "title": "PredictionResult", + "description": "A single prediction result for a single column in a single row." + }, + "ColumnType": { + "description": "Supported column data types for the data schema.\n\nIncludes base types (string, numeric, date) and additional types\nderived from SAP CDS (https://cap.cloud.sap/docs/cds/types#core-built-in-types).\nAdditional types are mapped to the corresponding base type internally.\nAll values are lowercase for case-insensitive matching.", + "enum": [ + "string", + "numeric", + "date", + "boolean", + "largestring", + "uuid", + "integer", + "int16", + "int32", + "int64", + "uint8", + "decimal", + "double", + "time", + "datetime", + "timestamp" + ], + "title": "ColumnType", + "type": "string" + }, + "ExplanationConfig": { + "additionalProperties": false, + "description": "Configuration for explainability outputs.", + "properties": { + "top_column_scores": { + "default": 0, + "description": "For how many columns to output column scores (optional, default is 0). 0 by default (no explainability). Max value is 20.", + "maximum": 20, + "minimum": 0, + "title": "Top Column Scores", + "type": "integer" + }, + "top_relevant_context_rows": { + "default": 0, + "description": "For how many context rows to return indices per query row (optional, default is 0). 0 by default (no explainability). Max value is 20.", + "maximum": 20, + "minimum": 0, + "title": "Top Relevant Context Rows", + "type": "integer" + } + }, + "title": "ExplanationConfig", + "type": "object" + }, + "PredictionConfig": { + "additionalProperties": false, + "description": "Configuration of the prediction model.", + "properties": { + "target_columns": { + "items": { + "$ref": "#/components/schemas/TargetColumnConfig" + }, + "title": "Target Columns", + "type": "array" + }, + "explanations": { + "$ref": "#/components/schemas/ExplanationConfig", + "description": "Optional configuration for explainability outputs (column scores and relevant context rows)." + } + }, + "required": [ + "target_columns" + ], + "title": "PredictionConfig", + "type": "object" + }, + "SchemaFieldConfig": { + "additionalProperties": false, + "description": "Configuration for a single field in the input data schema.", + "properties": { + "dtype": { + "$ref": "#/components/schemas/ColumnType", + "description": "The data type of the column. Supports base types (string, numeric, date) and extended types (e.g., Boolean, Integer, Timestamp). Extended types are mapped to corresponding base types internally. Case-insensitive." + } + }, + "required": [ + "dtype" + ], + "title": "SchemaFieldConfig", + "type": "object" + }, + "TargetColumnConfig": { + "additionalProperties": false, + "description": "Configuration for a target column in the prediction model.", + "properties": { + "name": { + "description": "The name of the target column.", + "title": "Name", + "type": "string" + }, + "prediction_placeholder": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "The placeholder value in any column for which to predict a value. The model will predict a value for all table cells containing this value.", + "title": "Prediction Placeholder" + }, + "task_type": { + "anyOf": [ + { + "enum": [ + "classification", + "regression" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The type of prediction task for this column. If not provided, the model will infer the task type from the data.", + "title": "Task Type" + }, + "top_k": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "How many predictions to output for this classification column.If not provided, only a single prediction is returned. Only relevant for classification.", + "title": "Top K" + } + }, + "required": [ + "name", + "prediction_placeholder" + ], + "title": "TargetColumnConfig", + "type": "object" + }, + "PredictRequestPayload": { + "oneOf": [ + { + "type": "object", + "properties": { + "prediction_config": { + "$ref": "#/components/schemas/PredictionConfig", + "description": "Configuration of target columns and placeholder value." + }, + "index_column": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The name of the index column. If provided, the service will return this column's value in each prediction object to facilitate aligning the output predictions with the input rows on the client side. If not provided, the column will not be included in the output.", + "title": "Index Column" + }, + "parse_data_types": { + "default": true, + "description": "Whether to parse the data types of the columns. If set to True, numeric columns will be parsed to float or integer and dates in ISO format YYYY-MM-DD will be parsed.", + "title": "Parse Data Types", + "type": "boolean" + }, + "data_schema": { + "anyOf": [ + { + "additionalProperties": { + "$ref": "#/components/schemas/SchemaFieldConfig" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional schema defining the data types of each column. If provided, this will override automatic data type parsing.", + "title": "Data Schema" + }, + "rows": { + "description": "Table rows, i.e. list of objects where each object is a mapping of column names to values. Either \"rows\" or \"columns\" must be provided.", + "items": { + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "type": "object" + }, + "title": "Rows", + "type": "array" + } + }, + "required": [ + "prediction_config", + "rows" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "prediction_config": { + "$ref": "#/components/schemas/PredictionConfig", + "description": "Configuration of target columns and placeholder value." + }, + "index_column": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The name of the index column. If provided, the service will return this column's value in each prediction object to facilitate aligning the output predictions with the input rows on the client side. If not provided, the column will not be included in the output.", + "title": "Index Column" + }, + "parse_data_types": { + "default": true, + "description": "Whether to parse the data types of the columns. If set to True, numeric columns will be parsed to float or integer and dates in ISO format YYYY-MM-DD will be parsed.", + "title": "Parse Data Types", + "type": "boolean" + }, + "data_schema": { + "anyOf": [ + { + "additionalProperties": { + "$ref": "#/components/schemas/SchemaFieldConfig" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional schema defining the data types of each column. If provided, this will override automatic data type parsing.", + "title": "Data Schema" + }, + "columns": { + "description": "Alternative to rows: columns of data where each key is a column name and the value is a list of all column values. Either \"rows\" or \"columns\" must be provided.", + "title": "Columns", + "additionalProperties": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "type": "array" + }, + "type": "object" + } + }, + "required": [ + "prediction_config", + "columns" + ], + "additionalProperties": false + } + ], + "description": "Users need to specify a list of rows, which contains both the context rows and the rows for which to predict a label, and a mapping of column names to placeholder values. The model will predict the value for any column specified in `target_columns` for all rows that have the prediction placeholder in that column. Either \"rows\" or \"columns\" must be provided, but not both." + } + } + } +} diff --git a/pyproject.toml b/pyproject.toml index 55a508d..649a819 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,7 @@ license = "Apache-2.0" [dependency-groups] dev = ["pip-licenses>=5.5.5", "commitizen>=4"] +sample = ["fastapi>=0.115", "uvicorn>=0.30", "python-dotenv>=1.0"] [tool.uv.sources] sap-ai-sdk-base = { workspace = true } @@ -26,3 +27,15 @@ ignore-packages = ["pylint", "astroid"] markers = [ "bedrock: mark a test as a bedrock test running in a different environment", ] + +[tool.pylint.main] +ignore-paths = ["packages/gen/gen_ai_hub/proxy/native/rpt_1_5/generated"] +init-hook = "import sys; sys.path.insert(0, 'packages/gen/gen_ai_hub/proxy/native/rpt_1_5'); sys.path.insert(0, 'packages/gen'); sys.path.insert(0, 'packages/core'); sys.path.insert(0, 'packages/base')" +extension-pkg-allow-list = ["httpx"] + +[tool.pylint.design] +max-args = 6 +max-positional-arguments = 6 + +[tool.pylint.similarities] +min-similarity-lines = 20 diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 0000000..74dff5f --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,12 @@ +{ + "pythonVersion": "3.11", + "venvPath": ".", + "venv": ".venv", + "extraPaths": [ + "packages/gen", + "packages/core", + "packages/base", + "sample_code", + "packages/gen/gen_ai_hub/proxy/native/rpt_1_5" + ] +} diff --git a/sample_code/rpt.py b/sample_code/rpt.py new file mode 100644 index 0000000..e22b408 --- /dev/null +++ b/sample_code/rpt.py @@ -0,0 +1,76 @@ +"""Service logic for RPT 1.5 predictions — mirrors sample-code/src/rpt.ts.""" + +import os +from typing import Any + +from gen_ai_hub import GenAIHubProxyClient +from gen_ai_hub.proxy.native.rpt_1_5 import ( + PredictionConfig, + PredictionPlaceholder, + RPT15Client, + TargetColumnConfig, + rows_request, +) + +MODEL_NAME = os.environ.get("RPT_MODEL_NAME", "sap-rpt-1.5") + +_REQUEST = rows_request( + prediction_config=PredictionConfig( + target_columns=[ + TargetColumnConfig( + name="SALESGROUP", + prediction_placeholder=PredictionPlaceholder("[PREDICT]"), + ) + ] + ), + index_column="__row_idx__", + rows=[ + { + "PRODUCT": "Laptop", + "PRICE": 999.99, + "PRODUCTION_DATE": "2025-01-15", + "__row_idx__": "35", + "SALESGROUP": "[PREDICT]", + }, + { + "PRODUCT": "Office Chair", + "PRICE": 142.99, + "PRODUCTION_DATE": "2025-07-13", + "__row_idx__": "571", + "SALESGROUP": "[PREDICT]", + }, + { + "PRODUCT": "Desktop Computer", + "PRICE": 921.50, + "PRODUCTION_DATE": "2024-12-02", + "__row_idx__": "42", + "SALESGROUP": "Electronics", + }, + { + "PRODUCT": "Macbook", + "PRICE": 1220.99, + "PRODUCTION_DATE": "2026-01-31", + "__row_idx__": "99", + "SALESGROUP": "Electronics", + }, + { + "PRODUCT": "Office Desk", + "PRICE": 750.50, + "PRODUCTION_DATE": "2024-12-05", + "__row_idx__": "689", + "SALESGROUP": "Furniture", + }, + ], +) + +async def predict_sales_group(proxy_client: GenAIHubProxyClient) -> Any: + """Predict the sales group of products.""" + client = RPT15Client(model_name=MODEL_NAME, proxy_client=proxy_client) + response = await client.predict(_REQUEST) + return response["predictions"] # type: ignore[index] + + +async def rpt_health(proxy_client: GenAIHubProxyClient) -> Any: + """Check the health of the RPT deployment.""" + client = RPT15Client(model_name=MODEL_NAME, proxy_client=proxy_client) + return await client.health() diff --git a/sample_code/server.py b/sample_code/server.py new file mode 100644 index 0000000..0f74a1d --- /dev/null +++ b/sample_code/server.py @@ -0,0 +1,98 @@ +""" +SAP AI SDK for Python — sample server. + +Credentials are read from environment variables (or VCAP_SERVICES on SAP BTP): + AICORE_BASE_URL e.g. https://api.ai.prodeu..... + AICORE_AUTH_URL e.g. https://.authentication.eu10.hana.ondemand.com + AICORE_CLIENT_ID + AICORE_CLIENT_SECRET + AICORE_RESOURCE_GROUP (optional, defaults to "default") + +Alternatively configure ~/.aicore/config.json — all methods are supported by the SDK. + +Run: + pip install fastapi uvicorn + uvicorn sample_code.server:app --reload +""" + +import json +import os +import sys +from pathlib import Path + +from dotenv import load_dotenv + +load_dotenv(Path(__file__).parent / ".env") + +# Make all SDK packages importable when running from the repo root. +_REPO_ROOT = os.path.dirname(os.path.dirname(__file__)) +for _pkg in ( + "packages/gen", + "packages/core", + "packages/base", + "packages/gen/gen_ai_hub/proxy/native/rpt_1_5", +): + _path = os.path.join(_REPO_ROOT, _pkg) + if _path not in sys.path: + sys.path.insert(0, _path) + +from contextlib import asynccontextmanager + +from fastapi import FastAPI, HTTPException +from fastapi.responses import JSONResponse + +from gen_ai_hub import GenAIHubProxyClient + +from sample_code.rpt import predict_sales_group, rpt_health + + +def _build_proxy_client() -> GenAIHubProxyClient: + # AICORE_SERVICE_KEY is the raw service key JSON from the SAP BTP service binding. + # The SDK reads VCAP_SERVICES, so wrap the key in the expected envelope. + # The entry needs "label": "aicore" so VCAPEnvironment can look it up by name. + service_key_json = os.environ.get("AICORE_SERVICE_KEY") + if service_key_json: + service_key = json.loads(service_key_json) + os.environ["VCAP_SERVICES"] = json.dumps( + {"aicore": [{"label": "aicore", "credentials": service_key}]} + ) + # GenAIHubProxyClient reads VCAP_SERVICES (or individual AICORE_* vars) via from_env(). + return GenAIHubProxyClient() + + +proxy_client = _build_proxy_client() + + +@asynccontextmanager +async def lifespan(app: FastAPI): + yield + + +app = FastAPI(title="SAP AI SDK Python Sample", lifespan=lifespan) + + +@app.get("/health") +async def server_health(): + return {"status": "ok"} + + +# --------------------------------------------------------------------------- +# RPT 1.5 +# --------------------------------------------------------------------------- + +@app.get("/rpt/predict") +async def rpt_predict(): + try: + predictions = await predict_sales_group(proxy_client) + return JSONResponse({"predictions": predictions}) + except Exception as exc: + raise HTTPException(status_code=500, detail=str(exc)) from exc + + +@app.get("/rpt/health") +async def rpt_health_check(): + try: + result = await rpt_health(proxy_client) + return JSONResponse({"status": result}) + except Exception as exc: + raise HTTPException(status_code=500, detail=str(exc)) from exc diff --git a/uv.lock b/uv.lock index 461ae24..23d7ac7 100644 --- a/uv.lock +++ b/uv.lock @@ -44,6 +44,11 @@ dev = [ { name = "commitizen" }, { name = "pip-licenses" }, ] +sample = [ + { name = "fastapi" }, + { name = "python-dotenv" }, + { name = "uvicorn" }, +] [package.metadata] @@ -52,6 +57,11 @@ dev = [ { name = "commitizen", specifier = ">=4" }, { name = "pip-licenses", specifier = ">=5.5.5" }, ] +sample = [ + { name = "fastapi", specifier = ">=0.115" }, + { name = "python-dotenv", specifier = ">=1.0" }, + { name = "uvicorn", specifier = ">=0.30" }, +] [[package]] name = "aiobotocore" @@ -249,6 +259,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929, upload-time = "2024-07-26T18:15:02.05Z" }, ] +[[package]] +name = "annotated-doc" +version = "0.0.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/8e/38aa427ed5402449e226975b649c5dc73ccadfefeb95e6aecb8f8ea4b6b6/annotated_doc-0.0.5.tar.gz", hash = "sha256:c7e58ce09192557605d8bbd92836d7e1d520ac9580096042c0bfd197efacf1bb", size = 10758, upload-time = "2026-07-28T13:50:58.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/30/e900b21425a860e195f32e37657aa1f7c7f2b1bfb26f03ca209b90933c06/annotated_doc-0.0.5-py3-none-any.whl", hash = "sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101", size = 5302, upload-time = "2026-07-28T13:50:57.239Z" }, +] + [[package]] name = "annotated-types" version = "0.8.0" @@ -924,6 +943,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, ] +[[package]] +name = "fastapi" +version = "0.141.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/02/91e3416a8fdd715abb903a952a6bec7cdd8d14eed55d415fc8595524c319/fastapi-0.141.1.tar.gz", hash = "sha256:e8822fc40db1e1858054d7a949a888695bc9bdce70139178e33bd2871a453ca1", size = 425799, upload-time = "2026-07-29T17:18:05.568Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/03/10388a42375ee7e4ac9b94eb2c5c569c8b5795e377e701c9ac3ad63de890/fastapi-0.141.1-py3-none-any.whl", hash = "sha256:bfb91aa2d334c61cb35ba9a116fc123b3d3df31640b801cf57a7a78ec3f603b3", size = 131954, upload-time = "2026-07-29T17:18:04.364Z" }, +] + [[package]] name = "fastjsonschema" version = "2.22.1" @@ -4480,6 +4515,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, ] +[[package]] +name = "starlette" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/e3/7c1dc7381d9f8ab7d854328ebfa884e62cb3f3d8549ddfd37c7814f42afa/starlette-1.3.1.tar.gz", hash = "sha256:05d0213193f2fbaae60e2ecb593b4add4262ad4e46536b54abe36f11a71724e0", size = 2703240, upload-time = "2026-06-12T09:23:11.602Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/bb/2799cc2ede3ed41131f8975621e7213dfc7ef4acbbaadfa440f32500c370/starlette-1.3.1-py3-none-any.whl", hash = "sha256:c7372aae11c3c3f26a42df7bd626cec2f47d03483d261d369516a615a53714c6", size = 73632, upload-time = "2026-06-12T09:23:10.017Z" }, +] + [[package]] name = "tabulate" version = "0.10.0" @@ -4809,6 +4857,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9d/8e/86ce2c03a1d9674530f6649e49067f7c69929600127077731de590d12132/uuid_utils-0.17.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2db386941cfdecdd0b5a8ceeed5cf7479c83d1730dcf64a48d43cfa018cc3310", size = 178681, upload-time = "2026-07-09T13:49:57.096Z" }, ] +[[package]] +name = "uvicorn" +version = "0.52.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/03/18/ccce41535dee1be77735592bd19965f3972c82e07ee703d324709496b716/uvicorn-0.52.1.tar.gz", hash = "sha256:112ec661814189acbccd3f7b86460147cc065fc92c0821afa78918780e4354dd", size = 100571, upload-time = "2026-08-01T18:19:30.732Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/d5/68e6e9bca63c0badf67002890a46d3784c958de45b65e1275ec583ca1f06/uvicorn-0.52.1-py3-none-any.whl", hash = "sha256:e4403f9d93188cf9d1088e9f40e3acd12630e2df8675316704379a7fc20fff6a", size = 79859, upload-time = "2026-08-01T18:19:29.294Z" }, +] + [[package]] name = "wcwidth" version = "0.8.2"