From abf6a1d4f6e83f108ed11a3e6279d73db514b352 Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Mon, 11 Nov 2024 17:50:29 -0800 Subject: [PATCH] [Inference Connector] Modified getProvider to use _inference/_services ES API instead of hardcoded values. (#199047) @ymao1 [introduced](https://github.com/elastic/elasticsearch/pull/114862) new ES API which allows to fetch available services providers list with the settings and task types: `GET _inference/_services` This PR is changing hardcoded providers list `x-pack/plugins/stack_connectors/public/connector_types/inference/providers/get_providers.ts` to use new ES API. --- .../lib => common}/dynamic_config/types.ts | 0 .../common/inference/types.ts | 15 + .../inference/additional_options_fields.tsx | 4 +- .../inference/connector.test.tsx | 75 +- .../connector_types/inference/connector.tsx | 59 +- .../inference/get_task_types.test.ts | 50 - .../inference/get_task_types.ts | 606 ---------- .../connector_types/inference/helpers.ts | 2 +- .../inference/hidden_fields.tsx | 2 +- .../inference/providers/get_providers.ts | 1022 +---------------- .../service_provider.tsx | 6 +- .../inference/providers/selectable/index.tsx | 23 +- .../public/connector_types/inference/types.ts | 3 - .../connector_configuration_field.tsx | 2 +- .../connector_configuration_form_items.tsx | 2 +- .../connector_configuration_utils.ts | 2 +- .../plugins/stack_connectors/server/plugin.ts | 7 +- .../routes/get_inference_services.test.ts | 133 +++ .../server/routes/get_inference_services.ts | 48 + .../stack_connectors/server/routes/index.ts | 1 + 20 files changed, 301 insertions(+), 1761 deletions(-) rename x-pack/plugins/stack_connectors/{public/connector_types/lib => common}/dynamic_config/types.ts (100%) delete mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/get_task_types.test.ts delete mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/get_task_types.ts create mode 100644 x-pack/plugins/stack_connectors/server/routes/get_inference_services.test.ts create mode 100644 x-pack/plugins/stack_connectors/server/routes/get_inference_services.ts diff --git a/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/types.ts b/x-pack/plugins/stack_connectors/common/dynamic_config/types.ts similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/types.ts rename to x-pack/plugins/stack_connectors/common/dynamic_config/types.ts diff --git a/x-pack/plugins/stack_connectors/common/inference/types.ts b/x-pack/plugins/stack_connectors/common/inference/types.ts index 9dbd447cb4578..b9561efe24292 100644 --- a/x-pack/plugins/stack_connectors/common/inference/types.ts +++ b/x-pack/plugins/stack_connectors/common/inference/types.ts @@ -19,6 +19,7 @@ import { TextEmbeddingParamsSchema, TextEmbeddingResponseSchema, } from './schema'; +import { ConfigProperties } from '../dynamic_config/types'; export type Config = TypeOf; export type Secrets = TypeOf; @@ -36,3 +37,17 @@ export type TextEmbeddingParams = TypeOf; export type TextEmbeddingResponse = TypeOf; export type StreamingResponse = TypeOf; + +export type FieldsConfiguration = Record; + +export interface InferenceTaskType { + task_type: string; + configuration: FieldsConfiguration; +} + +export interface InferenceProvider { + provider: string; + task_types: InferenceTaskType[]; + logo?: string; + configuration: FieldsConfiguration; +} diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/additional_options_fields.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/additional_options_fields.tsx index 8973f3124bc86..7a3b1abfd800b 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/additional_options_fields.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/additional_options_fields.tsx @@ -32,10 +32,10 @@ import { import { FormattedMessage } from '@kbn/i18n-react'; import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; +import { ConfigEntryView } from '../../../common/dynamic_config/types'; import { ConnectorConfigurationFormItems } from '../lib/dynamic_config/connector_configuration_form_items'; import * as i18n from './translations'; import { DEFAULT_TASK_TYPE } from './constants'; -import { ConfigEntryView } from '../lib/dynamic_config/types'; import { Config } from './types'; import { TaskTypeOption } from './helpers'; @@ -52,7 +52,7 @@ interface AdditionalOptionsConnectorFieldsProps { isEdit: boolean; optionalProviderFormFields: ConfigEntryView[]; onSetProviderConfigEntry: (key: string, value: unknown) => Promise; - onTaskTypeOptionsSelect: (taskType: string, provider?: string) => Promise; + onTaskTypeOptionsSelect: (taskType: string, provider?: string) => void; selectedTaskType?: string; taskTypeFormFields: ConfigEntryView[]; taskTypeSchema: ConfigEntryView[]; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.test.tsx index 44632e8b08331..d445504011b5f 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.test.tsx @@ -12,13 +12,10 @@ import { ConnectorFormTestProvider } from '../lib/test_utils'; import { render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { createStartServicesMock } from '@kbn/triggers-actions-ui-plugin/public/common/lib/kibana/kibana_react.mock'; -import { DisplayType, FieldType } from '../lib/dynamic_config/types'; import { useProviders } from './providers/get_providers'; -import { getTaskTypes } from './get_task_types'; -import { HttpSetup } from '@kbn/core-http-browser'; +import { DisplayType, FieldType } from '../../../common/dynamic_config/types'; jest.mock('./providers/get_providers'); -jest.mock('./get_task_types'); const mockUseKibanaReturnValue = createStartServicesMock(); jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana', () => ({ @@ -37,13 +34,32 @@ jest.mock('@faker-js/faker', () => ({ })); const mockProviders = useProviders as jest.Mock; -const mockTaskTypes = getTaskTypes as jest.Mock; const providersSchemas = [ { provider: 'openai', logo: '', // should be openai logo here, the hardcoded uses assets/images - taskTypes: ['completion', 'text_embedding'], + task_types: [ + { + task_type: 'completion', + configuration: { + user: { + display: DisplayType.TEXTBOX, + label: 'User', + order: 1, + required: false, + sensitive: false, + tooltip: 'Specifies the user issuing the request.', + type: FieldType.STRING, + validations: [], + value: '', + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + ], configuration: { api_key: { display: DisplayType.TEXTBOX, @@ -106,7 +122,16 @@ const providersSchemas = [ { provider: 'googleaistudio', logo: '', // should be googleaistudio logo here, the hardcoded uses assets/images - taskTypes: ['completion', 'text_embedding'], + task_types: [ + { + task_type: 'completion', + configuration: {}, + }, + { + task_type: 'text_embedding', + configuration: {}, + }, + ], configuration: { api_key: { display: DisplayType.TEXTBOX, @@ -139,39 +164,6 @@ const providersSchemas = [ }, }, ]; -const taskTypesSchemas: Record = { - googleaistudio: [ - { - task_type: 'completion', - configuration: {}, - }, - { - task_type: 'text_embedding', - configuration: {}, - }, - ], - openai: [ - { - task_type: 'completion', - configuration: { - user: { - display: DisplayType.TEXTBOX, - label: 'User', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specifies the user issuing the request.', - type: FieldType.STRING, - validations: [], - value: '', - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - ], -}; const openAiConnector = { actionTypeId: '.inference', @@ -222,9 +214,6 @@ describe('ConnectorFields renders', () => { isLoading: false, data: providersSchemas, }); - mockTaskTypes.mockImplementation( - (http: HttpSetup, provider: string) => taskTypesSchemas[provider] - ); }); test('openai provider fields are rendered', async () => { const { getAllByTestId } = render( diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.tsx index 35314dc06167d..5f854384dbb54 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { EuiFormRow, EuiSpacer, @@ -31,12 +31,12 @@ import { import { useKibana } from '@kbn/triggers-actions-ui-plugin/public'; import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; +import { ConfigEntryView } from '../../../common/dynamic_config/types'; +import { InferenceTaskType } from '../../../common/inference/types'; import { ServiceProviderKeys } from '../../../common/inference/constants'; import { ConnectorConfigurationFormItems } from '../lib/dynamic_config/connector_configuration_form_items'; -import { getTaskTypes } from './get_task_types'; import * as i18n from './translations'; import { DEFAULT_TASK_TYPE } from './constants'; -import { ConfigEntryView } from '../lib/dynamic_config/types'; import { SelectableProvider } from './providers/selectable'; import { Config, Secrets } from './types'; import { generateInferenceEndpointId, getTaskTypeOptions, TaskTypeOption } from './helpers'; @@ -116,13 +116,13 @@ const InferenceAPIConnectorFields: React.FunctionComponent { + (taskType: string, provider?: string) => { // Get task type settings - const currentTaskTypes = await getTaskTypes(http, provider ?? config?.provider); + const currentProvider = providers?.find((p) => p.provider === (provider ?? config?.provider)); + const currentTaskTypes = currentProvider?.task_types; const newTaskType = currentTaskTypes?.find((p) => p.task_type === taskType); setSelectedTaskType(taskType); - generateInferenceEndpointId(config, setFieldValue); // transform the schema const newTaskTypeSchema = Object.keys(newTaskType?.configuration ?? {}).map((k) => ({ @@ -150,19 +150,23 @@ const InferenceAPIConnectorFields: React.FunctionComponent { + (provider?: string) => { const newProvider = providers?.find((p) => p.provider === provider); // Update task types list available for the selected provider - const providerTaskTypes = newProvider?.taskTypes ?? []; + const providerTaskTypes = (newProvider?.task_types ?? []).map((t) => t.task_type); setTaskTypeOptions(getTaskTypeOptions(providerTaskTypes)); if (providerTaskTypes.length > 0) { - await onTaskTypeOptionsSelect(providerTaskTypes[0], provider); + onTaskTypeOptionsSelect(providerTaskTypes[0], provider); } // Update connector providerSchema @@ -203,9 +207,8 @@ const InferenceAPIConnectorFields: React.FunctionComponent { - const getTaskTypeSchema = async () => { - const currentTaskTypes = await getTaskTypes(http, config?.provider ?? ''); - const newTaskType = currentTaskTypes?.find((p) => p.task_type === config?.taskType); + const getTaskTypeSchema = (taskTypes: InferenceTaskType[]) => { + const newTaskType = taskTypes.find((p) => p.task_type === config?.taskType); // transform the schema const newTaskTypeSchema = Object.keys(newTaskType?.configuration ?? {}).map((k) => ({ @@ -228,7 +231,7 @@ const InferenceAPIConnectorFields: React.FunctionComponent + Object.keys(SERVICE_PROVIDERS).includes(config?.provider) + ? SERVICE_PROVIDERS[config?.provider as ServiceProviderKeys].icon + : undefined, + [config?.provider] + ); + + const providerName = useMemo( + () => + Object.keys(SERVICE_PROVIDERS).includes(config?.provider) + ? SERVICE_PROVIDERS[config?.provider as ServiceProviderKeys].name + : config?.provider, + [config?.provider] + ); + const providerSuperSelect = useCallback( (isInvalid: boolean) => ( jest.resetAllMocks()); - -describe.skip('getTaskTypes', () => { - test('should call get inference task types api', async () => { - const apiResponse = { - amazonbedrock: [ - { - task_type: 'completion', - configuration: { - max_new_tokens: { - display: DisplayType.NUMERIC, - label: 'Max new tokens', - order: 1, - required: false, - sensitive: false, - tooltip: 'Sets the maximum number for the output tokens to be generated.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - task_type: 'text_embedding', - configuration: {}, - }, - ], - }; - http.get.mockResolvedValueOnce(apiResponse); - - const result = await getTaskTypes(http, 'amazonbedrock'); - expect(result).toEqual(apiResponse); - }); -}); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/get_task_types.ts b/x-pack/plugins/stack_connectors/public/connector_types/inference/get_task_types.ts deleted file mode 100644 index a4fbbd6a6288b..0000000000000 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/get_task_types.ts +++ /dev/null @@ -1,606 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { HttpSetup } from '@kbn/core-http-browser'; -import { DisplayType, FieldType } from '../lib/dynamic_config/types'; -import { FieldsConfiguration } from './types'; - -export interface InferenceTaskType { - task_type: string; - configuration: FieldsConfiguration; -} - -// this http param is for the future migrating to real API -export const getTaskTypes = (http: HttpSetup, provider: string): Promise => { - const providersTaskTypes: Record = { - openai: [ - { - task_type: 'completion', - configuration: { - user: { - display: DisplayType.TEXTBOX, - label: 'User', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specifies the user issuing the request.', - type: FieldType.STRING, - validations: [], - value: '', - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - task_type: 'text_embedding', - configuration: { - user: { - display: DisplayType.TEXTBOX, - label: 'User', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specifies the user issuing the request.', - type: FieldType.STRING, - validations: [], - value: '', - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - ], - mistral: [ - { - task_type: 'text_embedding', - configuration: {}, - }, - ], - hugging_face: [ - { - task_type: 'text_embedding', - configuration: {}, - }, - ], - googlevertexai: [ - { - task_type: 'text_embedding', - configuration: { - auto_truncate: { - display: DisplayType.TOGGLE, - label: 'Auto truncate', - order: 1, - required: false, - sensitive: false, - tooltip: - 'Specifies if the API truncates inputs longer than the maximum token length automatically.', - type: FieldType.BOOLEAN, - validations: [], - value: false, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - task_type: 'rerank', - configuration: { - top_n: { - display: DisplayType.TOGGLE, - label: 'Top N', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specifies the number of the top n documents, which should be returned.', - type: FieldType.BOOLEAN, - validations: [], - value: false, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - ], - googleaistudio: [ - { - task_type: 'completion', - configuration: {}, - }, - { - task_type: 'text_embedding', - configuration: {}, - }, - ], - elasticsearch: [ - { - task_type: 'rerank', - configuration: { - return_documents: { - display: DisplayType.TOGGLE, - label: 'Return documents', - options: [], - order: 1, - required: false, - sensitive: false, - tooltip: 'Returns the document instead of only the index.', - type: FieldType.BOOLEAN, - validations: [], - value: true, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - task_type: 'sparse_embedding', - configuration: {}, - }, - { - task_type: 'text_embedding', - configuration: {}, - }, - ], - cohere: [ - { - task_type: 'completion', - configuration: {}, - }, - { - task_type: 'text_embedding', - configuration: { - input_type: { - display: DisplayType.DROPDOWN, - label: 'Input type', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specifies the type of input passed to the model.', - type: FieldType.STRING, - validations: [], - options: [ - { - label: 'classification', - value: 'classification', - }, - { - label: 'clusterning', - value: 'clusterning', - }, - { - label: 'ingest', - value: 'ingest', - }, - { - label: 'search', - value: 'search', - }, - ], - value: '', - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - truncate: { - display: DisplayType.DROPDOWN, - options: [ - { - label: 'NONE', - value: 'NONE', - }, - { - label: 'START', - value: 'START', - }, - { - label: 'END', - value: 'END', - }, - ], - label: 'Truncate', - order: 2, - required: false, - sensitive: false, - tooltip: 'Specifies how the API handles inputs longer than the maximum token length.', - type: FieldType.STRING, - validations: [], - value: '', - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - task_type: 'rerank', - configuration: { - return_documents: { - display: DisplayType.TOGGLE, - label: 'Return documents', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specify whether to return doc text within the results.', - type: FieldType.BOOLEAN, - validations: [], - value: false, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - top_n: { - display: DisplayType.NUMERIC, - label: 'Top N', - order: 1, - required: false, - sensitive: false, - tooltip: - 'The number of most relevant documents to return, defaults to the number of the documents.', - type: FieldType.INTEGER, - validations: [], - value: false, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - ], - azureopenai: [ - { - task_type: 'completion', - configuration: { - user: { - display: DisplayType.TEXTBOX, - label: 'User', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specifies the user issuing the request.', - type: FieldType.STRING, - validations: [], - value: '', - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - task_type: 'text_embedding', - configuration: { - user: { - display: DisplayType.TEXTBOX, - label: 'User', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specifies the user issuing the request.', - type: FieldType.STRING, - validations: [], - value: '', - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - ], - azureaistudio: [ - { - task_type: 'completion', - configuration: { - user: { - display: DisplayType.TEXTBOX, - label: 'User', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specifies the user issuing the request.', - type: FieldType.STRING, - validations: [], - value: '', - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - task_type: 'text_embedding', - configuration: { - do_sample: { - display: DisplayType.NUMERIC, - label: 'Do sample', - order: 1, - required: false, - sensitive: false, - tooltip: 'Instructs the inference process to perform sampling or not.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - max_new_tokens: { - display: DisplayType.NUMERIC, - label: 'Max new tokens', - order: 1, - required: false, - sensitive: false, - tooltip: 'Provides a hint for the maximum number of output tokens to be generated.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - temperature: { - display: DisplayType.NUMERIC, - label: 'Temperature', - order: 1, - required: false, - sensitive: false, - tooltip: 'A number in the range of 0.0 to 2.0 that specifies the sampling temperature.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - top_p: { - display: DisplayType.NUMERIC, - label: 'Top P', - order: 1, - required: false, - sensitive: false, - tooltip: - 'A number in the range of 0.0 to 2.0 that is an alternative value to temperature. Should not be used if temperature is specified.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - ], - amazonbedrock: [ - { - task_type: 'completion', - configuration: { - max_new_tokens: { - display: DisplayType.NUMERIC, - label: 'Max new tokens', - order: 1, - required: false, - sensitive: false, - tooltip: 'Sets the maximum number for the output tokens to be generated.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - temperature: { - display: DisplayType.NUMERIC, - label: 'Temperature', - order: 1, - required: false, - sensitive: false, - tooltip: - 'A number between 0.0 and 1.0 that controls the apparent creativity of the results.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - top_p: { - display: DisplayType.NUMERIC, - label: 'Top P', - order: 1, - required: false, - sensitive: false, - tooltip: - 'Alternative to temperature. A number in the range of 0.0 to 1.0, to eliminate low-probability tokens.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - top_k: { - display: DisplayType.NUMERIC, - label: 'Top K', - order: 1, - required: false, - sensitive: false, - tooltip: - 'Only available for anthropic, cohere, and mistral providers. Alternative to temperature.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - task_type: 'text_embedding', - configuration: {}, - }, - ], - anthropic: [ - { - task_type: 'completion', - configuration: { - max_tokens: { - display: DisplayType.NUMERIC, - label: 'Max tokens', - order: 1, - required: true, - sensitive: false, - tooltip: 'The maximum number of tokens to generate before stopping.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - temperature: { - display: DisplayType.TEXTBOX, - label: 'Temperature', - order: 2, - required: false, - sensitive: false, - tooltip: 'The amount of randomness injected into the response.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - top_p: { - display: DisplayType.NUMERIC, - label: 'Top P', - order: 4, - required: false, - sensitive: false, - tooltip: 'Specifies to use Anthropic’s nucleus sampling.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - top_k: { - display: DisplayType.NUMERIC, - label: 'Top K', - order: 3, - required: false, - sensitive: false, - tooltip: 'Specifies to only sample from the top K options for each subsequent token.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - ], - 'alibabacloud-ai-search': [ - { - task_type: 'text_embedding', - configuration: { - input_type: { - display: DisplayType.DROPDOWN, - label: 'Input type', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specifies the type of input passed to the model.', - type: FieldType.STRING, - validations: [], - options: [ - { - label: 'ingest', - value: 'ingest', - }, - { - label: 'search', - value: 'search', - }, - ], - value: '', - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - task_type: 'sparse_embedding', - configuration: { - input_type: { - display: DisplayType.DROPDOWN, - label: 'Input type', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specifies the type of input passed to the model.', - type: FieldType.STRING, - validations: [], - options: [ - { - label: 'ingest', - value: 'ingest', - }, - { - label: 'search', - value: 'search', - }, - ], - value: '', - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - return_token: { - display: DisplayType.TOGGLE, - label: 'Return token', - options: [], - order: 1, - required: false, - sensitive: false, - tooltip: - 'If `true`, the token name will be returned in the response. Defaults to `false` which means only the token ID will be returned in the response.', - type: FieldType.BOOLEAN, - validations: [], - value: true, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - task_type: 'completion', - configuration: {}, - }, - { - task_type: 'rerank', - configuration: {}, - }, - ], - watsonxai: [ - { - task_type: 'text_embedding', - configuration: {}, - }, - ], - }; - return Promise.resolve(providersTaskTypes[provider]); -}; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/helpers.ts b/x-pack/plugins/stack_connectors/public/connector_types/inference/helpers.ts index 0e1e4cdaa41ad..8638caa998eff 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/helpers.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/helpers.ts @@ -7,7 +7,7 @@ import { isEmpty } from 'lodash/fp'; import { ValidationFunc } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; -import { ConfigEntryView } from '../lib/dynamic_config/types'; +import { ConfigEntryView } from '../../../common/dynamic_config/types'; import { Config } from './types'; import * as i18n from './translations'; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/hidden_fields.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/hidden_fields.tsx index 9b28d35aaaf3a..f6df891b4b9c8 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/hidden_fields.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/hidden_fields.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { HiddenField } from '@kbn/es-ui-shared-plugin/static/forms/components'; import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { ConfigEntryView } from '../../../common/dynamic_config/types'; import { getNonEmptyValidator } from './helpers'; -import { ConfigEntryView } from '../lib/dynamic_config/types'; export const getProviderSecretsHiddenField = ( providerSchema: ConfigEntryView[], diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/get_providers.ts b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/get_providers.ts index 109266c1273fc..badc0cb61030d 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/get_providers.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/get_providers.ts @@ -9,1025 +9,11 @@ import type { HttpSetup } from '@kbn/core-http-browser'; import { i18n } from '@kbn/i18n'; import { useQuery } from '@tanstack/react-query'; import type { ToastsStart } from '@kbn/core-notifications-browser'; -import { DisplayType, FieldType } from '../../lib/dynamic_config/types'; -import { FieldsConfiguration } from '../types'; +import { INTERNAL_BASE_STACK_CONNECTORS_API_PATH } from '../../../../common'; +import { InferenceProvider } from '../../../../common/inference/types'; -export interface InferenceProvider { - provider: string; - taskTypes: string[]; - logo?: string; - configuration: FieldsConfiguration; -} - -export const getProviders = (http: HttpSetup): Promise => { - const providers = [ - { - provider: 'openai', - logo: '', // should be openai logo here, the hardcoded uses assets/images - taskTypes: ['completion', 'text_embedding'], - configuration: { - api_key: { - display: DisplayType.TEXTBOX, - label: 'API Key', - order: 3, - required: true, - sensitive: true, - tooltip: `The OpenAI API authentication key. For more details about generating OpenAI API keys, refer to the https://platform.openai.com/account/api-keys.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - model_id: { - display: DisplayType.TEXTBOX, - label: 'Model ID', - order: 2, - required: true, - sensitive: false, - tooltip: 'The name of the model to use for the inference task.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - organization_id: { - display: DisplayType.TEXTBOX, - label: 'Organization ID', - order: 4, - required: false, - sensitive: false, - tooltip: 'The unique identifier of your organization.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - url: { - display: DisplayType.TEXTBOX, - label: 'URL', - order: 1, - required: true, - sensitive: false, - tooltip: - 'The OpenAI API endpoint URL. For more information on the URL, refer to the https://platform.openai.com/docs/api-reference.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: 'https://api.openai.com/v1/chat/completions', - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 5, - required: false, - sensitive: false, - tooltip: - 'Default number of requests allowed per minute. For text_embedding is 3000. For completion is 500.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'googleaistudio', - logo: '', // should be googleaistudio logo here, the hardcoded uses assets/images - taskTypes: ['completion', 'text_embedding'], - configuration: { - api_key: { - display: DisplayType.TEXTBOX, - label: 'API Key', - order: 1, - required: true, - sensitive: true, - tooltip: `API Key for the provider you're connecting to`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - model_id: { - display: DisplayType.TEXTBOX, - label: 'Model ID', - order: 2, - required: true, - sensitive: false, - tooltip: `ID of the LLM you're using`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 5, - required: false, - sensitive: false, - tooltip: 'Minimize the number of rate limit errors.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'amazonbedrock', - logo: '', // should be amazonbedrock logo here, the hardcoded uses assets/images - taskTypes: ['completion', 'text_embedding'], - configuration: { - access_key: { - display: DisplayType.TEXTBOX, - label: 'Access Key', - order: 1, - required: true, - sensitive: true, - tooltip: `A valid AWS access key that has permissions to use Amazon Bedrock.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - secret_key: { - display: DisplayType.TEXTBOX, - label: 'Secret Key', - order: 2, - required: true, - sensitive: true, - tooltip: `A valid AWS secret key that is paired with the access_key.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - provider: { - display: DisplayType.DROPDOWN, - label: 'Provider', - order: 3, - required: true, - options: [ - { - label: 'amazontitan', - value: 'amazontitan', - }, - { - label: 'anthropic', - value: 'anthropic', - }, - { - label: 'ai21labs', - value: 'ai21labs', - }, - { - label: 'cohere', - value: 'cohere', - }, - { - label: 'meta', - value: 'meta', - }, - { - label: 'mistral', - value: 'mistral', - }, - ], - sensitive: false, - tooltip: 'The model provider for your deployment.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - model: { - display: DisplayType.TEXTBOX, - label: 'Model', - order: 4, - required: true, - sensitive: false, - tooltip: `The base model ID or an ARN to a custom model based on a foundational model.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - region: { - display: DisplayType.TEXTBOX, - label: 'Region', - order: 5, - required: true, - sensitive: false, - tooltip: `The region that your model or ARN is deployed in.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 6, - required: false, - sensitive: false, - tooltip: - 'By default, the amazonbedrock service sets the number of requests allowed per minute to 240.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'googlevertexai', - logo: '', // should be googlevertexai logo here, the hardcoded uses assets/images - taskTypes: ['text_embedding', 'rerank'], - configuration: { - service_account_json: { - display: DisplayType.TEXTBOX, - label: 'Credentials JSON', - order: 1, - required: true, - sensitive: true, - tooltip: `API Key for the provider you're connecting to`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - model_id: { - display: DisplayType.TEXTBOX, - label: 'Model ID', - order: 2, - required: true, - sensitive: false, - tooltip: `ID of the LLM you're using`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - location: { - display: DisplayType.TEXTBOX, - label: 'GCP Region', - order: 2, - required: true, - sensitive: false, - tooltip: `Please provide the GCP region where the Vertex AI API(s) is enabled. For more information, refer to the {geminiVertexAIDocs}.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - project_id: { - display: DisplayType.TEXTBOX, - label: 'GCP Project', - order: 3, - required: true, - sensitive: false, - tooltip: - 'The GCP Project ID which has Vertex AI API(s) enabled. For more information on the URL, refer to the {geminiVertexAIDocs}.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 5, - required: false, - sensitive: false, - tooltip: 'Minimize the number of rate limit errors.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'mistral', - logo: '', // should be misral logo here, the hardcoded uses assets/images - taskTypes: ['text_embedding'], - configuration: { - api_key: { - display: DisplayType.TEXTBOX, - label: 'API Key', - order: 1, - required: true, - sensitive: true, - tooltip: `API Key for the provider you're connecting to`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - model: { - display: DisplayType.TEXTBOX, - label: 'Model', - order: 2, - required: true, - sensitive: false, - tooltip: `Refer to the Mistral models documentation for the list of available text embedding models`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 4, - required: false, - sensitive: false, - tooltip: 'Minimize the number of rate limit errors.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: 240, - depends_on: [], - }, - max_input_tokens: { - display: DisplayType.NUMERIC, - label: 'Maximum input tokens', - order: 3, - required: false, - sensitive: false, - tooltip: 'Allows you to specify the maximum number of tokens per input.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'hugging_face', - logo: '', // should be hugging_face logo here, the hardcoded uses assets/images - taskTypes: ['text_embedding'], - configuration: { - api_key: { - display: DisplayType.TEXTBOX, - label: 'API Key', - order: 2, - required: true, - sensitive: true, - tooltip: `API Key for the provider you're connecting to`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - url: { - display: DisplayType.TEXTBOX, - label: 'URL', - order: 1, - required: true, - sensitive: false, - tooltip: 'The URL endpoint to use for the requests.', - type: FieldType.STRING, - validations: [], - value: 'https://api.openai.com/v1/embeddings', - ui_restrictions: [], - default_value: 'https://api.openai.com/v1/embeddings', - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 3, - required: false, - sensitive: false, - tooltip: 'Minimize the number of rate limit errors.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'elasticsearch', - logo: '', // elasticsearch logo here - taskTypes: ['sparse_embedding', 'text_embedding', 'rerank'], - configuration: { - model_id: { - display: DisplayType.DROPDOWN, - label: 'Model ID', - order: 1, - required: true, - sensitive: false, - tooltip: `The name of the model to use for the inference task.`, - type: FieldType.STRING, - validations: [], - options: [ - { - label: '.elser_model_1', - value: '.elser_model_1', - }, - { - label: '.elser_model_2', - value: '.elser_model_2', - }, - { - label: '.elser_model_2_linux-x86_64', - value: '.elser_model_2_linux-x86_64', - }, - { - label: '.multilingual-e5-small', - value: '.multilingual-e5-small', - }, - { - label: '.multilingual-e5-small_linux-x86_64', - value: '.multilingual-e5-small_linux-x86_64', - }, - ], - value: null, - ui_restrictions: [], - default_value: '.multilingual-e5-small', - depends_on: [], - }, - num_allocations: { - display: DisplayType.NUMERIC, - label: 'Number allocations', - order: 2, - required: true, - sensitive: false, - tooltip: - 'The total number of allocations this model is assigned across machine learning nodes.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: 1, - depends_on: [], - }, - num_threads: { - display: DisplayType.NUMERIC, - label: 'Number threads', - order: 3, - required: true, - sensitive: false, - tooltip: 'Sets the number of threads used by each model allocation during inference.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: 2, - depends_on: [], - }, - }, - }, - { - provider: 'cohere', - logo: '', // should be cohere logo here, the hardcoded uses assets/images - taskTypes: ['completion', 'text_embedding', 'rerank'], - configuration: { - api_key: { - display: DisplayType.TEXTBOX, - label: 'API Key', - order: 1, - required: true, - sensitive: true, - tooltip: `API Key for the provider you're connecting to`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 5, - required: false, - sensitive: false, - tooltip: 'Minimize the number of rate limit errors.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'azureopenai', - logo: '', // should be azureopenai logo here, the hardcoded uses assets/images - taskTypes: ['completion', 'text_embedding'], - configuration: { - api_key: { - display: DisplayType.TEXTBOX, - label: 'API Key', - order: 1, - required: false, - sensitive: true, - tooltip: `You must provide either an API key or an Entra ID.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - entra_id: { - display: DisplayType.TEXTBOX, - label: 'Entra ID', - order: 2, - required: false, - sensitive: true, - tooltip: `You must provide either an API key or an Entra ID.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - resource_name: { - display: DisplayType.TEXTBOX, - label: 'Resource Name', - order: 3, - required: true, - sensitive: false, - tooltip: `The name of your Azure OpenAI resource`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - api_version: { - display: DisplayType.TEXTBOX, - label: 'API version', - order: 4, - required: true, - sensitive: false, - tooltip: 'The Azure API version ID to use.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - deployment_id: { - display: DisplayType.TEXTBOX, - label: 'Deployment ID', - order: 5, - required: true, - sensitive: false, - tooltip: 'The deployment name of your deployed models.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 5, - required: false, - sensitive: false, - tooltip: - 'The azureopenai service sets a default number of requests allowed per minute depending on the task type.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'azureaistudio', - logo: '', // should be azureaistudio logo here, the hardcoded uses assets/images - taskTypes: ['completion', 'text_embedding'], - configuration: { - api_key: { - display: DisplayType.TEXTBOX, - label: 'API Key', - order: 1, - required: true, - sensitive: true, - tooltip: `API Key for the provider you're connecting to`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - target: { - display: DisplayType.TEXTBOX, - label: 'Target', - order: 2, - required: true, - sensitive: false, - tooltip: `The target URL of your Azure AI Studio model deployment.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - endpoint_type: { - display: DisplayType.DROPDOWN, - label: 'Endpoint type', - order: 3, - required: true, - sensitive: false, - tooltip: 'Specifies the type of endpoint that is used in your model deployment.', - type: FieldType.STRING, - options: [ - { - label: 'token', - value: 'token', - }, - { - label: 'realtime', - value: 'realtime', - }, - ], - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - provider: { - display: DisplayType.DROPDOWN, - label: 'Provider', - order: 3, - required: true, - options: [ - { - label: 'cohere', - value: 'cohere', - }, - { - label: 'meta', - value: 'meta', - }, - { - label: 'microsoft_phi', - value: 'microsoft_phi', - }, - { - label: 'mistral', - value: 'mistral', - }, - { - label: 'openai', - value: 'openai', - }, - { - label: 'databricks', - value: 'databricks', - }, - ], - sensitive: false, - tooltip: 'The model provider for your deployment.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 5, - required: false, - sensitive: false, - tooltip: 'Minimize the number of rate limit errors.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'anthropic', - logo: '', // should be anthropic logo here, the hardcoded uses assets/images - taskTypes: ['completion'], - configuration: { - api_key: { - display: DisplayType.TEXTBOX, - label: 'API Key', - order: 1, - required: true, - sensitive: true, - tooltip: `API Key for the provider you're connecting to`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - model_id: { - display: DisplayType.TEXTBOX, - label: 'Model ID', - order: 2, - required: true, - sensitive: false, - tooltip: `The name of the model to use for the inference task.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 5, - required: false, - sensitive: false, - tooltip: - 'By default, the anthropic service sets the number of requests allowed per minute to 50.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'watsonxai', - logo: '', // should be anthropic logo here, the hardcoded uses assets/images - taskTypes: ['text_embedding'], - configuration: { - api_version: { - display: DisplayType.TEXTBOX, - label: 'API version', - order: 1, - required: true, - sensitive: false, - tooltip: 'The IBM Watsonx API version ID to use.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - project_id: { - display: DisplayType.TEXTBOX, - label: 'Project ID', - order: 2, - required: true, - sensitive: false, - tooltip: '', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - model_id: { - display: DisplayType.TEXTBOX, - label: 'Model ID', - order: 3, - required: true, - sensitive: false, - tooltip: `The name of the model to use for the inference task.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - url: { - display: DisplayType.TEXTBOX, - label: 'URL', - order: 4, - required: true, - sensitive: false, - tooltip: '', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - max_input_tokens: { - display: DisplayType.NUMERIC, - label: 'Maximum input tokens', - order: 5, - required: false, - sensitive: false, - tooltip: 'Allows you to specify the maximum number of tokens per input.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'alibabacloud-ai-search', - logo: '', // should be anthropic logo here, the hardcoded uses assets/images - taskTypes: ['completion', 'sparse_embedding', 'text_embedding', 'rerank'], - configuration: { - api_key: { - display: DisplayType.TEXTBOX, - label: 'API Key', - order: 1, - required: true, - sensitive: true, - tooltip: 'A valid API key for the AlibabaCloud AI Search API.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - service_id: { - display: DisplayType.DROPDOWN, - label: 'Project ID', - order: 2, - required: true, - sensitive: false, - tooltip: 'The name of the model service to use for the {infer} task.', - type: FieldType.STRING, - options: [ - { - label: 'ops-text-embedding-001', - value: 'ops-text-embedding-001', - }, - { - label: 'ops-text-embedding-zh-001', - value: 'ops-text-embedding-zh-001', - }, - { - label: 'ops-text-embedding-en-001', - value: 'ops-text-embedding-en-001', - }, - { - label: 'ops-text-embedding-002', - value: 'ops-text-embedding-002', - }, - { - label: 'ops-text-sparse-embedding-001', - value: 'ops-text-sparse-embedding-001', - }, - { - label: 'ops-bge-reranker-larger', - value: 'ops-bge-reranker-larger', - }, - ], - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - host: { - display: DisplayType.TEXTBOX, - label: 'Host', - order: 3, - required: true, - sensitive: false, - tooltip: - 'The name of the host address used for the {infer} task. You can find the host address at https://opensearch.console.aliyun.com/cn-shanghai/rag/api-key[ the API keys section] of the documentation.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - http_schema: { - display: DisplayType.DROPDOWN, - label: 'HTTP Schema', - order: 4, - required: true, - sensitive: false, - tooltip: '', - type: FieldType.STRING, - options: [ - { - label: 'https', - value: 'https', - }, - { - label: 'http', - value: 'http', - }, - ], - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - workspace: { - display: DisplayType.TEXTBOX, - label: 'Workspace', - order: 5, - required: true, - sensitive: false, - tooltip: 'The name of the workspace used for the {infer} task.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 6, - required: false, - sensitive: false, - tooltip: 'Minimize the number of rate limit errors.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - ] as InferenceProvider[]; - return Promise.resolve( - providers.sort((a, b) => (a.provider > b.provider ? 1 : b.provider > a.provider ? -1 : 0)) - ); +export const getProviders = async (http: HttpSetup): Promise => { + return await http.get(`${INTERNAL_BASE_STACK_CONNECTORS_API_PATH}/_inference/_services`); }; export const useProviders = (http: HttpSetup, toasts: ToastsStart) => { diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/render_service_provider/service_provider.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/render_service_provider/service_provider.tsx index 5d2c99ffd92ce..5eb8518a5ea15 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/render_service_provider/service_provider.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/render_service_provider/service_provider.tsx @@ -26,7 +26,7 @@ interface ServiceProviderProps { searchValue?: string; } -type ProviderSolution = 'Observability' | 'Security' | 'Search'; +export type ProviderSolution = 'Observability' | 'Security' | 'Search'; interface ServiceProviderRecord { icon: string; @@ -107,9 +107,7 @@ export const ServiceProviderIcon: React.FC = ({ providerKe return provider ? ( - ) : ( - {providerKey} - ); + ) : null; }; export const ServiceProviderName: React.FC = ({ diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/selectable/index.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/selectable/index.tsx index d4527e9c7b9a4..fc31c9dd6c4f7 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/selectable/index.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/selectable/index.tsx @@ -11,6 +11,7 @@ import React, { memo, useCallback, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { ServiceProviderKeys } from '../../../../../common/inference/constants'; import { + ProviderSolution, SERVICE_PROVIDERS, ServiceProviderIcon, ServiceProviderName, @@ -47,7 +48,20 @@ const SelectableProviderComponent: React.FC = ({ const renderProviderOption = useCallback>( (option, searchValue) => { - const provider = SERVICE_PROVIDERS[option.label as ServiceProviderKeys]; + const provider = Object.keys(SERVICE_PROVIDERS).includes(option.label) + ? SERVICE_PROVIDERS[option.label as ServiceProviderKeys] + : undefined; + + const supportedBySolutions = (provider && + provider.solutions.map((solution) => ( + + {solution} + + ))) ?? ( + + {'Search' as ProviderSolution} + + ); return ( @@ -65,12 +79,7 @@ const SelectableProviderComponent: React.FC = ({ - {provider && - provider.solutions.map((solution) => ( - - {solution} - - ))} + {supportedBySolutions} diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/types.ts b/x-pack/plugins/stack_connectors/public/connector_types/inference/types.ts index 150292894b643..1bd55793bc463 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/types.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/types.ts @@ -14,7 +14,6 @@ import { SparseEmbeddingParams, TextEmbeddingParams, } from '../../../common/inference/types'; -import { ConfigProperties } from '../lib/dynamic_config/types'; export type InferenceActionParams = | { subAction: SUB_ACTION.COMPLETION; subActionParams: ChatCompleteParams } @@ -22,8 +21,6 @@ export type InferenceActionParams = | { subAction: SUB_ACTION.SPARSE_EMBEDDING; subActionParams: SparseEmbeddingParams } | { subAction: SUB_ACTION.TEXT_EMBEDDING; subActionParams: TextEmbeddingParams }; -export type FieldsConfiguration = Record; - export interface Config { taskType: string; taskTypeConfig?: Record; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_field.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_field.tsx index 79ae552a9528a..5560c831c4a61 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_field.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_field.tsx @@ -25,12 +25,12 @@ import { } from '@elastic/eui'; import { isEmpty } from 'lodash/fp'; +import { ConfigEntryView, DisplayType } from '../../../../common/dynamic_config/types'; import { ensureBooleanType, ensureCorrectTyping, ensureStringType, } from './connector_configuration_utils'; -import { ConfigEntryView, DisplayType } from './types'; interface ConnectorConfigurationFieldProps { configEntry: ConfigEntryView; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_form_items.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_form_items.tsx index 3190ac80275f1..a7063d81719a8 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_form_items.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_form_items.tsx @@ -18,7 +18,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { ConfigEntryView, DisplayType } from './types'; +import { ConfigEntryView, DisplayType } from '../../../../common/dynamic_config/types'; import { ConnectorConfigurationField } from './connector_configuration_field'; interface ConnectorConfigurationFormItemsProps { diff --git a/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_utils.ts b/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_utils.ts index 182327a180a63..cce5bc15fa56c 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_utils.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_utils.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ConfigProperties, FieldType } from './types'; +import { ConfigProperties, FieldType } from '../../../../common/dynamic_config/types'; export type ConnectorConfigEntry = ConfigProperties & { key: string }; diff --git a/x-pack/plugins/stack_connectors/server/plugin.ts b/x-pack/plugins/stack_connectors/server/plugin.ts index aee84d963043d..b20892938735b 100644 --- a/x-pack/plugins/stack_connectors/server/plugin.ts +++ b/x-pack/plugins/stack_connectors/server/plugin.ts @@ -8,7 +8,11 @@ import { PluginInitializerContext, Plugin, CoreSetup, Logger } from '@kbn/core/server'; import { PluginSetupContract as ActionsPluginSetupContract } from '@kbn/actions-plugin/server'; import { registerConnectorTypes } from './connector_types'; -import { validSlackApiChannelsRoute, getWellKnownEmailServiceRoute } from './routes'; +import { + validSlackApiChannelsRoute, + getWellKnownEmailServiceRoute, + getInferenceServicesRoute, +} from './routes'; import { ExperimentalFeatures, parseExperimentalConfigValue, @@ -39,6 +43,7 @@ export class StackConnectorsPlugin implements Plugin { getWellKnownEmailServiceRoute(router); validSlackApiChannelsRoute(router, actions.getActionsConfigurationUtilities(), this.logger); + getInferenceServicesRoute(router); registerConnectorTypes({ actions, diff --git a/x-pack/plugins/stack_connectors/server/routes/get_inference_services.test.ts b/x-pack/plugins/stack_connectors/server/routes/get_inference_services.test.ts new file mode 100644 index 0000000000000..50596028d80a8 --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/routes/get_inference_services.test.ts @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { httpServiceMock, httpServerMock } from '@kbn/core/server/mocks'; +import { coreMock } from '@kbn/core/server/mocks'; +import { getInferenceServicesRoute } from './get_inference_services'; +import { DisplayType, FieldType } from '../../common/dynamic_config/types'; + +describe('getInferenceServicesRoute', () => { + it('returns available service providers', async () => { + const router = httpServiceMock.createRouter(); + const core = coreMock.createRequestHandlerContext(); + + const mockResult = [ + { + provider: 'openai', + task_types: [ + { + task_type: 'completion', + configuration: { + user: { + display: DisplayType.TEXTBOX, + label: 'User', + order: 1, + required: false, + sensitive: false, + tooltip: 'Specifies the user issuing the request.', + type: FieldType.STRING, + validations: [], + value: '', + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + ], + configuration: { + api_key: { + display: DisplayType.TEXTBOX, + label: 'API Key', + order: 3, + required: true, + sensitive: true, + tooltip: `The OpenAI API authentication key. For more details about generating OpenAI API keys, refer to the https://platform.openai.com/account/api-keys.`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + model_id: { + display: DisplayType.TEXTBOX, + label: 'Model ID', + order: 2, + required: true, + sensitive: false, + tooltip: 'The name of the model to use for the inference task.', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + organization_id: { + display: DisplayType.TEXTBOX, + label: 'Organization ID', + order: 4, + required: false, + sensitive: false, + tooltip: 'The unique identifier of your organization.', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + url: { + display: DisplayType.TEXTBOX, + label: 'URL', + order: 1, + required: true, + sensitive: false, + tooltip: + 'The OpenAI API endpoint URL. For more information on the URL, refer to the https://platform.openai.com/docs/api-reference.', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: 'https://api.openai.com/v1/chat/completions', + depends_on: [], + }, + 'rate_limit.requests_per_minute': { + display: DisplayType.NUMERIC, + label: 'Rate limit', + order: 5, + required: false, + sensitive: false, + tooltip: + 'Default number of requests allowed per minute. For text_embedding is 3000. For completion is 500.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + ]; + core.elasticsearch.client.asInternalUser.transport.request.mockResolvedValue(mockResult); + + getInferenceServicesRoute(router); + + const [config, handler] = router.get.mock.calls[0]; + expect(config.path).toMatchInlineSnapshot(`"/internal/stack_connectors/_inference/_services"`); + + const mockResponse = httpServerMock.createResponseFactory(); + const mockRequest = httpServerMock.createKibanaRequest(); + await handler({ core }, mockRequest, mockResponse); + + expect(mockResponse.ok).toHaveBeenCalledWith({ + body: mockResult, + }); + }); +}); diff --git a/x-pack/plugins/stack_connectors/server/routes/get_inference_services.ts b/x-pack/plugins/stack_connectors/server/routes/get_inference_services.ts new file mode 100644 index 0000000000000..1396072834261 --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/routes/get_inference_services.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + IRouter, + RequestHandlerContext, + KibanaRequest, + IKibanaResponse, + KibanaResponseFactory, +} from '@kbn/core/server'; +import { InferenceProvider } from '../../common/inference/types'; +import { INTERNAL_BASE_STACK_CONNECTORS_API_PATH } from '../../common'; + +export const getInferenceServicesRoute = (router: IRouter) => { + router.get( + { + path: `${INTERNAL_BASE_STACK_CONNECTORS_API_PATH}/_inference/_services`, + options: { + access: 'internal', + }, + validate: false, + }, + handler + ); + + async function handler( + ctx: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise { + const esClient = (await ctx.core).elasticsearch.client.asInternalUser; + + const response = await esClient.transport.request<{ + endpoints: InferenceProvider[]; + }>({ + method: 'GET', + path: `/_inference/_services`, + }); + + return res.ok({ + body: response, + }); + } +}; diff --git a/x-pack/plugins/stack_connectors/server/routes/index.ts b/x-pack/plugins/stack_connectors/server/routes/index.ts index cd9857b2168ed..e64995e1a50ef 100644 --- a/x-pack/plugins/stack_connectors/server/routes/index.ts +++ b/x-pack/plugins/stack_connectors/server/routes/index.ts @@ -7,3 +7,4 @@ export { getWellKnownEmailServiceRoute } from './get_well_known_email_service'; export { validSlackApiChannelsRoute } from './valid_slack_api_channels'; +export { getInferenceServicesRoute } from './get_inference_services';