From 4cd79f5a18ecd43e203730352eb40e003d89e152 Mon Sep 17 00:00:00 2001 From: lakith-rambukkanage Date: Thu, 24 Oct 2024 12:04:14 +0530 Subject: [PATCH 1/5] Fix endpoint checkbox untick issue --- .../components/Apis/Details/Endpoints/EndpointOverview.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 065479bfe9b..a9a25761758 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 @@ -409,7 +409,7 @@ function EndpointOverview(props) { const endpointProp = 'production_endpoints'; if (endpointCategory[category]) { delete endpointConfigCopy[endpointProp]; - if (endpointConfigCopy?.endpoint_security?.production !== null) { + if (endpointConfigCopy?.endpoint_security?.production) { delete endpointConfigCopy.endpoint_security.production; } if (endpointConfigCopy.endpointType === 'failover') { @@ -427,7 +427,7 @@ function EndpointOverview(props) { const endpointProp = 'sandbox_endpoints'; if (endpointCategory[category]) { delete endpointConfigCopy[endpointProp]; - if (endpointConfigCopy?.endpoint_security?.sandbox !== null) { + if (endpointConfigCopy?.endpoint_security?.sandbox) { delete endpointConfigCopy.endpoint_security.sandbox; } if (endpointConfigCopy.endpointType === 'failover') { From d38e9d152c5dff6f4ceb9c4dff2579e52da50301 Mon Sep 17 00:00:00 2001 From: lakith-rambukkanage Date: Thu, 24 Oct 2024 12:22:10 +0530 Subject: [PATCH 2/5] Fix api key mandatory issue for unsecure AI APIs --- .../Details/Endpoints/EndpointOverview.jsx | 19 +--------------- .../Apis/Details/Endpoints/Endpoints.jsx | 22 ++++++++++++++++++- 2 files changed, 22 insertions(+), 19 deletions(-) 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 a9a25761758..7e3da732a4d 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 @@ -56,7 +56,6 @@ import ServiceEndpoint from './ServiceEndpoint'; import CustomBackend from './CustomBackend'; import { API_SECURITY_KEY_TYPE_PRODUCTION } from '../Configuration/components/APISecurity/components/apiSecurityConstants'; import { API_SECURITY_KEY_TYPE_SANDBOX } from '../Configuration/components/APISecurity/components/apiSecurityConstants'; -import API from 'AppData/api'; import AIEndpointAuth from './AIEndpointAuth'; const PREFIX = 'EndpointOverview'; @@ -194,6 +193,7 @@ function EndpointOverview(props) { setIsValidSequenceBackend, isCustomBackendSelected, setIsCustomBackendSelected, + apiKeyParamConfig, } = props; const { endpointConfig } = api; const [endpointType, setEndpointType] = useState(endpointTypes[0]); @@ -219,23 +219,6 @@ function EndpointOverview(props) { (api.serviceInfo) }); const [servicesList, setServicesList] = useState([]); - const [apiKeyParamConfig, setApiKeyParamConfig] = useState({ - authHeader: null, - authQueryParameter: null - }); - - useEffect(() => { - if (api.subtypeConfiguration?.subtype === 'AIAPI') { - API.getLLMProviderEndpointConfiguration(JSON.parse(api.subtypeConfiguration.configuration).llmProviderId) - .then((response) => { - if (response.body) { - const config = response.body; - setApiKeyParamConfig(config); - } - }); - } - }, []); - const handleToggleEndpointSecurity = () => { const tmpSecurityInfo = !endpointSecurityInfo ? { production: CONSTS.DEFAULT_ENDPOINT_SECURITY, diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/Endpoints.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/Endpoints.jsx index c41e369e073..ceda2db2300 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/Endpoints.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/Endpoints.jsx @@ -31,6 +31,8 @@ import { APIContext } from 'AppComponents/Apis/Details/components/ApiContext'; import cloneDeep from 'lodash.clonedeep'; import { isRestricted } from 'AppData/AuthManager'; import { Alert } from 'AppComponents/Shared'; + +import API from 'AppData/api'; import EndpointOverview from './EndpointOverview'; import { createEndpointConfig, getEndpointTemplateByType } from './endpointUtils'; import { API_SECURITY_KEY_TYPE_PRODUCTION, @@ -107,6 +109,22 @@ function Endpoints(props) { const [productionBackendList, setProductionBackendList] = useState([]); const [isValidSequenceBackend, setIsValidSequenceBackend] = useState(false); const [isCustomBackendSelected, setIsCustomBackendSelected] = useState(false); + const [apiKeyParamConfig, setApiKeyParamConfig] = useState({ + authHeader: null, + authQueryParameter: null + }); + + useEffect(() => { + if (api.subtypeConfiguration?.subtype === 'AIAPI') { + API.getLLMProviderEndpointConfiguration(JSON.parse(api.subtypeConfiguration.configuration).llmProviderId) + .then((response) => { + if (response.body) { + const config = response.body; + setApiKeyParamConfig(config); + } + }); + } + }, []); const apiReducer = (initState, configAction) => { const tmpEndpointConfig = cloneDeep(initState.endpointConfig); @@ -527,7 +545,8 @@ function Endpoints(props) { } } } else if ((!endpointConfig || !endpointConfig.endpoint_security) - && apiObject.subtypeConfiguration?.subtype === 'AIAPI' ) { + && apiObject.subtypeConfiguration?.subtype === 'AIAPI' + && (apiKeyParamConfig.authHeader || apiKeyParamConfig.authQueryParameter)) { return { isValid: false, message: intl.formatMessage({ @@ -734,6 +753,7 @@ function Endpoints(props) { setIsValidSequenceBackend={setIsValidSequenceBackend} isCustomBackendSelected={isCustomBackendSelected} setIsCustomBackendSelected={setIsCustomBackendSelected} + apiKeyParamConfig={apiKeyParamConfig} /> From def19a30c5a4d5b4d874027d19a51d24f88b0ee7 Mon Sep 17 00:00:00 2001 From: lakith-rambukkanage Date: Thu, 24 Oct 2024 13:29:44 +0530 Subject: [PATCH 3/5] Hide endpoint for AI API creation --- .../src/app/components/Apis/Create/AIAPI/APICreateAIAPI.jsx | 1 + 1 file changed, 1 insertion(+) 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 251e5c95e30..59e280d2a27 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 @@ -238,6 +238,7 @@ export default function ApiCreateAIAPI(props) { multiGateway={multiGateway} api={apiInputs} isAPIProduct={false} + hideEndpoint /> )} From c6b30979d471a67512742d90e21ee63b933f6c2b Mon Sep 17 00:00:00 2001 From: lakith-rambukkanage Date: Thu, 24 Oct 2024 14:20:23 +0530 Subject: [PATCH 4/5] Fix endpoint error on delete one of sand or prod --- .../src/app/components/Apis/Details/Endpoints/Endpoints.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/Endpoints.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/Endpoints.jsx index ceda2db2300..e4255e80022 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/Endpoints.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/Endpoints.jsx @@ -471,7 +471,7 @@ function Endpoints(props) { } } } else if (production.type === 'apikey') { - if (production.apiKeyValue === null) { + if (production.apiKeyValue === null && endpointConfig.production_endpoints) { return { isValid: false, message: intl.formatMessage({ @@ -525,7 +525,7 @@ function Endpoints(props) { } } } else if (sandbox.type === 'apikey') { - if (sandbox.apiKeyValue === null) { + if (sandbox.apiKeyValue === null && endpointConfig.sandbox_endpoints) { return { isValid: false, message: intl.formatMessage({ From 4e4f423a9f8a5c80ab3cae4f9e7bb4f9e9f223c1 Mon Sep 17 00:00:00 2001 From: lakith-rambukkanage Date: Thu, 24 Oct 2024 14:21:30 +0530 Subject: [PATCH 5/5] Add AI / LLM changes to AI APIs --- .../main/webapp/site/public/locales/en.json | 26 ++++++++++--------- .../Apis/Create/AIAPI/APICreateAIAPI.jsx | 8 +++--- .../Create/AIAPI/Steps/ProvideAIOpenAPI.jsx | 18 ++++++------- .../BackendRateLimiting.jsx | 2 +- .../BackendRateLimitingForm.jsx | 2 +- .../Apis/Details/Endpoints/Endpoints.jsx | 2 +- .../Details/components/APIDetailsTopMenu.jsx | 2 +- .../Apis/Listing/Landing/Menus/AIAPIMenu.jsx | 6 ++--- 8 files changed, 34 insertions(+), 32 deletions(-) diff --git a/portals/publisher/src/main/webapp/site/public/locales/en.json b/portals/publisher/src/main/webapp/site/public/locales/en.json index 60b00cab60a..bfd42eaa6b4 100644 --- a/portals/publisher/src/main/webapp/site/public/locales/en.json +++ b/portals/publisher/src/main/webapp/site/public/locales/en.json @@ -13,19 +13,20 @@ "Apis.Create.AIAPI.ApiCreateAIAPI.back": "Back", "Apis.Create.AIAPI.ApiCreateAIAPI.cancel": "Cancel", "Apis.Create.AIAPI.ApiCreateAIAPI.create": "Create", - "Apis.Create.AIAPI.ApiCreateAIAPI.heading": "Create an API using an AI Service provider API definition.", + "Apis.Create.AIAPI.ApiCreateAIAPI.heading": "Create an API using an AI/LLM Service provider API definition.", "Apis.Create.AIAPI.ApiCreateAIAPI.next": "Next", - "Apis.Create.AIAPI.ApiCreateAIAPI.sub.heading": "Create an API using an existing AI Service provider API definition.", - "Apis.Create.AIAPI.ApiCreateAIAPI.wizard.one": "Provide AI Service provider API", + "Apis.Create.AIAPI.ApiCreateAIAPI.sub.heading": "Create an API using an existing AI/LLM Service provider API definition.", + "Apis.Create.AIAPI.ApiCreateAIAPI.wizard.one": "Provide AI/LLM Service provider API", "Apis.Create.AIAPI.ApiCreateAIAPI.wizard.two": "Create API", "Apis.Create.AIAPI.Steps.ProvideAIOpenAPI.AI.model": "API version", - "Apis.Create.AIAPI.Steps.ProvideAIOpenAPI.AI.model.empty": "No AI Service Provider selected.", + "Apis.Create.AIAPI.Steps.ProvideAIOpenAPI.AI.model.empty": "No AI/LLM Service Provider selected.", "Apis.Create.AIAPI.Steps.ProvideAIOpenAPI.AI.model.helper": "Select API version for the API", "Apis.Create.AIAPI.Steps.ProvideAIOpenAPI.AI.model.placeholder": "Search API version", - "Apis.Create.AIAPI.Steps.ProvideAIOpenAPI.AI.provider": "AI Service Provider", - "Apis.Create.AIAPI.Steps.ProvideAIOpenAPI.AI.provider.empty": "Loading AI Service Providers...", - "Apis.Create.AIAPI.Steps.ProvideAIOpenAPI.AI.provider.helper.text": "Select AI Service Provider for the API", - "Apis.Create.AIAPI.Steps.ProvideAIOpenAPI.AI.provider.placeholder": "Search AI Service Provider", + "Apis.Create.AIAPI.Steps.ProvideAIOpenAPI.AI.provider": "AI/LLM Service Provider", + "Apis.Create.AIAPI.Steps.ProvideAIOpenAPI.AI.provider.empty": "No AI/LLM Service Provider defined.", + "Apis.Create.AIAPI.Steps.ProvideAIOpenAPI.AI.provider.helper.text": "Select AI/LLM Service Provider for the API", + "Apis.Create.AIAPI.Steps.ProvideAIOpenAPI.AI.provider.loading": "Loading AI/LLM Service Providers...", + "Apis.Create.AIAPI.Steps.ProvideAIOpenAPI.AI.provider.placeholder": "Search AI/LLM Service Provider", "Apis.Create.AIAPI.Steps.ProvideAIOpenAPI.LLM.Provider.fetch.error": "Something went wrong while fetching LLM Providers", "Apis.Create.AIAPI.Steps.ProvideAIOpenAPI.LLMProvider.API.Definition.fetch.error": "Something went wrong while fetching LLM Provider API Definition", "Apis.Create.AIAPI.Steps.ProvideAIOpenAPI.LLMProvider.API.Definition.validation.failure.error": "Error while validating the LLM Provider API Definition", @@ -726,6 +727,7 @@ "Apis.Details.Endpoints.Endpoints.delete.sequence.backend.error": "Error Deleting Sandbox Sequence Backend", "Apis.Details.Endpoints.Endpoints.endpoints.header": "Endpoints", "Apis.Details.Endpoints.Endpoints.missing.accessKey.secretKey.error": "Access Key, Secret Key and Region should not be empty", + "Apis.Details.Endpoints.Endpoints.missing.endpoint.ai.error": "Production & Sandbox Endpoint Security should be added", "Apis.Details.Endpoints.Endpoints.missing.endpoint.error": "Either one of Production or Sandbox Endpoints should be added.", "Apis.Details.Endpoints.Endpoints.missing.prod.endpoint.loadbalance": "Default Production Endpoint should not be empty", "Apis.Details.Endpoints.Endpoints.missing.prototype.url": "Prototype Endpoint URL should not be empty", @@ -1566,7 +1568,7 @@ "Apis.Details.TryOutConsole.token.helper": "Generate or provide an internal API Key", "Apis.Details.TryOutConsole.token.label": "Internal API Key", "Apis.Details.components.APIDetailsTopMenu.advertise.only.label": "Third Party", - "Apis.Details.components.APIDetailsTopMenu.ai.api.label": "AI API", + "Apis.Details.components.APIDetailsTopMenu.ai.api.label": "AI/LLM API", "Apis.Details.components.APIDetailsTopMenu.created.by": "Created by:", "Apis.Details.components.APIDetailsTopMenu.current.api": "Current API", "Apis.Details.components.APIDetailsTopMenu.error": "Something went wrong while downloading the API.", @@ -1625,7 +1627,7 @@ "Apis.Details.local.Scopes.heading.edit.heading": "Local Scopes", "Apis.Details.local.Scopes.heading.scope.heading": "Local Scopes", "Apis.Details.scopes.Edit.text.editor.edit": "Edit", - "Apis.Listing.AIAPI.ai.api": "AI API", + "Apis.Listing.AIAPI.ai.api": "AI/LLM API", "Apis.Listing.ApiTableView.context": "Context", "Apis.Listing.ApiTableView.items.per.page": "Items per page", "Apis.Listing.ApiTableView.name": "Name", @@ -1640,8 +1642,8 @@ "Apis.Listing.ApiThumb.owners.technical": "Technical", "Apis.Listing.ApiThumb.version": "Version", "Apis.Listing.Components.Create.API": "Create API", - "Apis.Listing.SampleAPI.SampleAPI.ai.api.create.title": "Create AI API", - "Apis.Listing.SampleAPI.SampleAPI.ai.api.import.content": "Create AI APIs by importing AI service provider APIs", + "Apis.Listing.SampleAPI.SampleAPI.ai.api.create.title": "Create AI/LLM API", + "Apis.Listing.SampleAPI.SampleAPI.ai.api.import.content": "Create AI/LLM APIs by importing service provider APIs", "Apis.Listing.SampleAPI.SampleAPI.create.new": "Let’s get started !", "Apis.Listing.SampleAPI.SampleAPI.create.new.description": "Choose your option to create an API", "Apis.Listing.SampleAPI.SampleAPI.graphql.api": "GraphQL", 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 59e280d2a27..146e3dcd851 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 @@ -71,7 +71,7 @@ function apiInputsReducer(currentState, inputAction) { } } /** - * Handle API creation from AI Service Provider API Definition. + * Handle API creation from AI/LLM Service Provider API Definition. * * @export * @param {*} props @@ -189,13 +189,13 @@ export default function ApiCreateAIAPI(props) { @@ -207,7 +207,7 @@ export default function ApiCreateAIAPI(props) { diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/AIAPI/Steps/ProvideAIOpenAPI.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/AIAPI/Steps/ProvideAIOpenAPI.jsx index 24f91ad9544..066813942c4 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/AIAPI/Steps/ProvideAIOpenAPI.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/AIAPI/Steps/ProvideAIOpenAPI.jsx @@ -139,7 +139,7 @@ export default function ProvideAIOpenAPI(props) { fullWidth id='AI-providers-autocomplete' options={getUniqueProviderList(llmProviders)} - noOptionsText='No AI Service Provider defined' + noOptionsText='No AI/LLM Service Provider defined' value={selectedProvider} onChange={(e, newValue) => { setSelectedProvider(newValue); @@ -156,25 +156,25 @@ export default function ProvideAIOpenAPI(props) { <> * ) : ( ) } placeholder={intl.formatMessage({ id: 'Apis.Create.AIAPI.Steps.ProvideAIOpenAPI.AI.provider.placeholder', - defaultMessage: 'Search AI Service Provider' + defaultMessage: 'Search AI/LLM Service Provider' })} helperText={( )} margin='dense' @@ -193,7 +193,7 @@ export default function ProvideAIOpenAPI(props) { fullWidth id='AI-model-autocomplete' options={llmProviders.list.filter((model) => model.name === selectedProvider)} - noOptionsText='No AI Service Provider selected' + noOptionsText='No AI/LLM Service Provider selected' getOptionLabel={(option) => option.apiVersion } @@ -239,7 +239,7 @@ export default function ProvideAIOpenAPI(props) { ) : ( ) } @@ -280,8 +280,8 @@ export default function ProvideAIOpenAPI(props) { 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 a39ad90d5c5..b5c7e909f15 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 @@ -25,7 +25,7 @@ import { isRestricted } from 'AppData/AuthManager'; import BackendRateLimitingForm from './BackendRateLimitingForm'; /** - * Backend Rate Limiting for AI APIs + * Backend Rate Limiting for AI/LLM APIs * * @export * @param {*} props diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/components/AIBackendRateLimiting/BackendRateLimitingForm.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/components/AIBackendRateLimiting/BackendRateLimitingForm.jsx index eed5dbf02e6..2b26f94f249 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/components/AIBackendRateLimiting/BackendRateLimitingForm.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/components/AIBackendRateLimiting/BackendRateLimitingForm.jsx @@ -29,7 +29,7 @@ import RequestCountRateLimit from './RequestCountRateLimit'; import RequestCountRateLimitUnit from './RequestCountRateLimitUnit'; /** - * Backend Rate Limiting for AI APIs + * Backend Rate Limiting for AI/LLM APIs * * @export * @param {*} props diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/Endpoints.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/Endpoints.jsx index e4255e80022..1cfaf0c4da0 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/Endpoints.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/Endpoints.jsx @@ -550,7 +550,7 @@ function Endpoints(props) { return { isValid: false, message: intl.formatMessage({ - id: 'Apis.Details.Endpoints.Endpoints.missing.endpoint.error', + id: 'Apis.Details.Endpoints.Endpoints.missing.endpoint.ai.error', defaultMessage: 'Production & Sandbox Endpoint Security should be added', }), }; diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/components/APIDetailsTopMenu.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/components/APIDetailsTopMenu.jsx index 56e63180ada..5835361ad8b 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/components/APIDetailsTopMenu.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/components/APIDetailsTopMenu.jsx @@ -311,7 +311,7 @@ const APIDetailsTopMenu = (props) => { > )} diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Listing/Landing/Menus/AIAPIMenu.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Listing/Landing/Menus/AIAPIMenu.jsx index 975a05cd006..ed2f6cbdf7f 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Listing/Landing/Menus/AIAPIMenu.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Listing/Landing/Menus/AIAPIMenu.jsx @@ -33,7 +33,7 @@ const AIAPIMenu = (props) => { title={( )} icon={icon} @@ -45,13 +45,13 @@ const AIAPIMenu = (props) => { helperText={( )} >