diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/AIAPI/APICreateAIAPI.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/AIAPI/APICreateAIAPI.jsx index a7547ac59af..0f3df1df12d 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/AIAPI/APICreateAIAPI.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/AIAPI/APICreateAIAPI.jsx @@ -32,6 +32,8 @@ import Alert from 'AppComponents/Shared/Alert'; import CircularProgress from '@mui/material/CircularProgress'; import DefaultAPIForm from 'AppComponents/Apis/Create/Components/DefaultAPIForm'; import APICreateBase from 'AppComponents/Apis/Create/Components/APICreateBase'; +import { API_SECURITY_API_KEY } + from 'AppComponents/Apis/Details/Configuration/components/APISecurity/components/apiSecurityConstants'; import ProvideAIOpenAPI from './Steps/ProvideAIOpenAPI'; @@ -143,6 +145,7 @@ export default function ApiCreateAIAPI(props) { llmProviderName, llmProviderApiVersion, }, + securityScheme: [API_SECURITY_API_KEY] }; if (endpoint) { additionalProperties.endpointConfig = { diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/RuntimeConfiguration.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/RuntimeConfiguration.jsx index e07572396c5..ec0a8a245e0 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/RuntimeConfiguration.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/RuntimeConfiguration.jsx @@ -388,9 +388,20 @@ export default function RuntimeConfiguration() { case 'saveButtonDisabled': setSaveButtonDisabled(value); return state; - case 'aiConfiguration': + case 'aiConfiguration': { nextState.aiConfiguration = value; + const { throttlingConfiguration } = nextState.aiConfiguration; + if (throttlingConfiguration) { + throttlingConfiguration.isTokenBasedThrottlingEnabled = !!( + throttlingConfiguration.productionMaxPromptTokenCount || + throttlingConfiguration.productionMaxCompletionTokenCount || + throttlingConfiguration.productionMaxTotalTokenCount || + throttlingConfiguration.sandboxMaxPromptTokenCount || + throttlingConfiguration.sandboxMaxCompletionTokenCount || + throttlingConfiguration.sandboxMaxTotalTokenCount) + } return nextState; + } default: return state; } diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/components/AIBackendRateLimiting/BackendRateLimiting.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/components/AIBackendRateLimiting/BackendRateLimiting.jsx index 771ee8f04a5..b3e5fd4b0c4 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/components/AIBackendRateLimiting/BackendRateLimiting.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/components/AIBackendRateLimiting/BackendRateLimiting.jsx @@ -19,10 +19,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import Grid from '@mui/material/Grid'; -import { Switch, Typography } from '@mui/material'; - -import { isRestricted } from 'AppData/AuthManager'; -import { useAPI } from 'AppComponents/Apis/Details/components/ApiContext'; import BackendRateLimitingForm from './BackendRateLimitingForm'; // import { useIntl } from 'react-intl'; @@ -36,43 +32,9 @@ import BackendRateLimitingForm from './BackendRateLimitingForm'; */ export default function BackendRateLimiting(props) { const { api, configDispatcher } = props; - const [apiFromContext] = useAPI(); - // const intl = useIntl(); - - function handleOnChangeTokenBasedSwitch({ target: { checked } }) { - let throttlingConfiguration = {}; - if (api.aiConfiguration && api.aiConfiguration.throttlingConfiguration) { - throttlingConfiguration = api.aiConfiguration.throttlingConfiguration; - } - const dispatchValue = { - ...api.aiConfiguration, - throttlingConfiguration: { - ...throttlingConfiguration, - isTokenBasedThrottlingEnabled: checked - } - } - configDispatcher({ - action: 'aiConfiguration', - value: dispatchValue, - }) - } return ( <> - - - Token Based Throttling - - - + diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/components/AIBackendRateLimiting/CommonRateLimitingForm.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/components/AIBackendRateLimiting/CommonRateLimitingForm.jsx index 1d3114c2093..73e32f3a6fa 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/components/AIBackendRateLimiting/CommonRateLimitingForm.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/components/AIBackendRateLimiting/CommonRateLimitingForm.jsx @@ -45,8 +45,8 @@ export default function CommonRateLimitingForm(props) { function validateValue(value) { const validity = commonFormProps.validator ? commonFormProps.validator.validate(value, { abortEarly: false }).error - : APIValidation.isReqNumber.validate(value, { abortEarly: false }).error; - if (validity === null) { + : APIValidation.isNumber.validate(value, { abortEarly: false }).error; + if (validity === null || !value) { setIsValueValid(true); configDispatcher({ action: 'saveButtonDisabled', value: false }); } else { diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/components/AIBackendRateLimiting/RequestCountRateLimit.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/components/AIBackendRateLimiting/RequestCountRateLimit.jsx new file mode 100644 index 00000000000..080486fc457 --- /dev/null +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/components/AIBackendRateLimiting/RequestCountRateLimit.jsx @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import { Grid, InputAdornment, TextField } from '@mui/material'; +import { useIntl } from 'react-intl'; + +import { isRestricted } from 'AppData/AuthManager'; +import RequestCountRateLimitUnit from './RequestCountRateLimitUnit'; + + +export default function RequestCountRateLimit(props) { + + const { api, configDispatcher, isProduction } = props; + const intl = useIntl(); + let maxTpsValue; + if (api.maxTps) { + maxTpsValue = isProduction ? api.maxTps.production : api.maxTps.sandbox; + } else { + maxTpsValue = ''; + } + + return (<> + + { + const value = isProduction ? + { ...api.maxTps, production: event.target.value } : + { ...api.maxTps, sandbox: event.target.value }; + configDispatcher({ + action: 'maxTps', + value, + }); + }} + value={api.maxTps !== null ? maxTpsValue : ''} + disabled={isRestricted(['apim:api_create'], api)} + InputProps={{ + endAdornment: + + , + }} + /> + + ); +} + +RequestCountRateLimit.propTypes = { + api: PropTypes.shape({}).isRequired, + configDispatcher: PropTypes.func.isRequired, + isProduction: PropTypes.bool.isRequired, +}; \ No newline at end of file diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/components/AIBackendRateLimiting/RequestCountRateLimitUnit.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/components/AIBackendRateLimiting/RequestCountRateLimitUnit.jsx new file mode 100644 index 00000000000..cbc941945aa --- /dev/null +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/components/AIBackendRateLimiting/RequestCountRateLimitUnit.jsx @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import { MenuItem, TextField } from '@mui/material'; + +// time unit enum +const timeUnitEnum = { + SECOND: 'SECOND', + MINUTE: 'MINUTE', + HOUR: 'HOUR', +}; + +export default function RequestCountRateLimitUnit(props) { + const { api, configDispatcher, isProduction } = props; //eslint-disable-line + // const intl = useIntl(); + + let maxTpsUnitValue; + if (api.maxTps) { + maxTpsUnitValue = isProduction ? api.maxTps.productionTimeUnit : api.maxTps.sandboxTimeUnit; + } + + if (!maxTpsUnitValue) { + maxTpsUnitValue = timeUnitEnum.SECOND; + } + + return ( + <> + { + const value = isProduction ? + { ...api.maxTps, productionTimeUnit: event.target.value } : + { ...api.maxTps, sandboxTimeUnit: event.target.value }; + configDispatcher({ + action: 'maxTps', + value, + }); + }} + margin='none' + variant='standard' + sx={{ + '& .MuiInputBase-root': { + '&:before': { borderBottom: 'none' }, // Remove underline + '&:after': { borderBottom: 'none' }, // Remove underline + }, + }} + > + {Object.keys(timeUnitEnum).map((unit) => ( + + {unit} + + ))} + + + ); +} + +RequestCountRateLimitUnit.propTypes = { + api: PropTypes.shape({}).isRequired, + configDispatcher: PropTypes.func.isRequired, + isProduction: PropTypes.bool.isRequired, +}; \ No newline at end of file diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/EndpointOverview.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/EndpointOverview.jsx index ef8a306dd2a..dde20031c64 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/EndpointOverview.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/EndpointOverview.jsx @@ -288,6 +288,10 @@ function EndpointOverview(props) { supportedEndpointTypes = [ { key: 'http', value: 'HTTP/REST Endpoint' }, ]; + } else if (type === 'http' && api.aiConfiguration) { + supportedEndpointTypes = [ + { key: 'http', value: 'HTTP/REST Endpoint' }, + ] } else { supportedEndpointTypes = [ { key: 'http', value: 'HTTP/REST Endpoint' }, @@ -727,7 +731,22 @@ function EndpointOverview(props) { value={endpointType.key === 'MOCKED_OAS' ? 'INLINE' : endpointType.key} onChange={handleEndpointTypeSelect} > - {supportedEnpointTypes.map((endpoint) => { + {!api.aiConfiguration && supportedEnpointTypes.map((endpoint) => { + return ( + + )} + label={endpoint.value} + /> + ); + })} + {api.aiConfiguration && supportedEnpointTypes.map((endpoint) => { return ( { setSwagger(resp.obj); }).catch((err) => { - console.err(err); + console.error(err); }); } }, []); diff --git a/portals/publisher/src/main/webapp/source/src/app/data/APIValidation.js b/portals/publisher/src/main/webapp/source/src/app/data/APIValidation.js index 3e168820e73..7836867ed7a 100644 --- a/portals/publisher/src/main/webapp/source/src/app/data/APIValidation.js +++ b/portals/publisher/src/main/webapp/source/src/app/data/APIValidation.js @@ -198,7 +198,7 @@ const definition = { websubOperationTarget: Joi.string().regex(/^[^{}]*$/).required(), name: Joi.string().min(1).max(255), email: Joi.string().email({ tlds: false }).required(), - isReqNumber: Joi.number().required(), + isNumber: Joi.number(), }; export default definition;