mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[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.
This commit is contained in:
parent
4e65ae9b1e
commit
abf6a1d4f6
20 changed files with 301 additions and 1761 deletions
|
@ -19,6 +19,7 @@ import {
|
|||
TextEmbeddingParamsSchema,
|
||||
TextEmbeddingResponseSchema,
|
||||
} from './schema';
|
||||
import { ConfigProperties } from '../dynamic_config/types';
|
||||
|
||||
export type Config = TypeOf<typeof ConfigSchema>;
|
||||
export type Secrets = TypeOf<typeof SecretsSchema>;
|
||||
|
@ -36,3 +37,17 @@ export type TextEmbeddingParams = TypeOf<typeof TextEmbeddingParamsSchema>;
|
|||
export type TextEmbeddingResponse = TypeOf<typeof TextEmbeddingResponseSchema>;
|
||||
|
||||
export type StreamingResponse = TypeOf<typeof StreamingResponseSchema>;
|
||||
|
||||
export type FieldsConfiguration = Record<string, ConfigProperties>;
|
||||
|
||||
export interface InferenceTaskType {
|
||||
task_type: string;
|
||||
configuration: FieldsConfiguration;
|
||||
}
|
||||
|
||||
export interface InferenceProvider {
|
||||
provider: string;
|
||||
task_types: InferenceTaskType[];
|
||||
logo?: string;
|
||||
configuration: FieldsConfiguration;
|
||||
}
|
||||
|
|
|
@ -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<void>;
|
||||
onTaskTypeOptionsSelect: (taskType: string, provider?: string) => Promise<void>;
|
||||
onTaskTypeOptionsSelect: (taskType: string, provider?: string) => void;
|
||||
selectedTaskType?: string;
|
||||
taskTypeFormFields: ConfigEntryView[];
|
||||
taskTypeSchema: ConfigEntryView[];
|
||||
|
|
|
@ -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<string, any> = {
|
||||
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(
|
||||
|
|
|
@ -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<ActionConnectorFields
|
|||
}, [isSubmitting, config, validateFields]);
|
||||
|
||||
const onTaskTypeOptionsSelect = useCallback(
|
||||
async (taskType: string, provider?: string) => {
|
||||
(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<ActionConnectorFields
|
|||
taskTypeConfig: configDefaults,
|
||||
},
|
||||
});
|
||||
generateInferenceEndpointId(
|
||||
{ ...config, taskType, taskTypeConfig: configDefaults },
|
||||
setFieldValue
|
||||
);
|
||||
},
|
||||
[config, http, setFieldValue, updateFieldValues]
|
||||
[config, providers, setFieldValue, updateFieldValues]
|
||||
);
|
||||
|
||||
const onProviderChange = useCallback(
|
||||
async (provider?: string) => {
|
||||
(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<ActionConnectorFields
|
|||
);
|
||||
|
||||
useEffect(() => {
|
||||
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<ActionConnectorFields
|
|||
|
||||
setProviderSchema(newProviderSchema);
|
||||
|
||||
getTaskTypeSchema();
|
||||
getTaskTypeSchema(newProvider?.task_types ?? []);
|
||||
}
|
||||
}, [config?.provider, config?.taskType, http, isEdit, providers]);
|
||||
|
||||
|
@ -309,6 +312,22 @@ const InferenceAPIConnectorFields: React.FunctionComponent<ActionConnectorFields
|
|||
setFieldValue('config.provider', '');
|
||||
}, [onProviderChange, setFieldValue]);
|
||||
|
||||
const providerIcon = useMemo(
|
||||
() =>
|
||||
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) => (
|
||||
<EuiFormControlLayout
|
||||
|
@ -317,11 +336,7 @@ const InferenceAPIConnectorFields: React.FunctionComponent<ActionConnectorFields
|
|||
isDisabled={isEdit || readOnly}
|
||||
isInvalid={isInvalid}
|
||||
fullWidth
|
||||
icon={
|
||||
!config?.provider
|
||||
? { type: 'sparkles', side: 'left' }
|
||||
: SERVICE_PROVIDERS[config?.provider as ServiceProviderKeys].icon
|
||||
}
|
||||
icon={!config?.provider ? { type: 'sparkles', side: 'left' } : providerIcon}
|
||||
>
|
||||
<EuiFieldText
|
||||
onClick={handleProviderPopover}
|
||||
|
@ -329,9 +344,7 @@ const InferenceAPIConnectorFields: React.FunctionComponent<ActionConnectorFields
|
|||
isInvalid={isInvalid}
|
||||
disabled={isEdit || readOnly}
|
||||
onKeyDown={handleProviderKeyboardOpen}
|
||||
value={
|
||||
config?.provider ? SERVICE_PROVIDERS[config?.provider as ServiceProviderKeys].name : ''
|
||||
}
|
||||
value={config?.provider ? providerName : ''}
|
||||
fullWidth
|
||||
placeholder={i18n.SELECT_PROVIDER}
|
||||
icon={{ type: 'arrowDown', side: 'right' }}
|
||||
|
@ -345,8 +358,10 @@ const InferenceAPIConnectorFields: React.FunctionComponent<ActionConnectorFields
|
|||
readOnly,
|
||||
onClearProvider,
|
||||
config?.provider,
|
||||
providerIcon,
|
||||
handleProviderPopover,
|
||||
handleProviderKeyboardOpen,
|
||||
providerName,
|
||||
isProviderPopoverOpen,
|
||||
]
|
||||
);
|
||||
|
|
|
@ -1,50 +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 { httpServiceMock } from '@kbn/core/public/mocks';
|
||||
import { DisplayType, FieldType } from '../lib/dynamic_config/types';
|
||||
import { getTaskTypes } from './get_task_types';
|
||||
|
||||
const http = httpServiceMock.createStartContract();
|
||||
|
||||
beforeEach(() => 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);
|
||||
});
|
||||
});
|
|
@ -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<InferenceTaskType[]> => {
|
||||
const providersTaskTypes: Record<string, InferenceTaskType[]> = {
|
||||
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]);
|
||||
};
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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[],
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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<ServiceProviderProps> = ({ providerKe
|
|||
|
||||
return provider ? (
|
||||
<EuiIcon data-test-subj={`icon-service-provider-${providerKey}`} type={provider.icon} />
|
||||
) : (
|
||||
<span>{providerKey}</span>
|
||||
);
|
||||
) : null;
|
||||
};
|
||||
|
||||
export const ServiceProviderName: React.FC<ServiceProviderProps> = ({
|
||||
|
|
|
@ -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<SelectableProviderProps> = ({
|
|||
|
||||
const renderProviderOption = useCallback<NonNullable<EuiSelectableProps['renderOption']>>(
|
||||
(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) => (
|
||||
<EuiFlexItem>
|
||||
<EuiBadge color="hollow">{solution}</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
))) ?? (
|
||||
<EuiFlexItem>
|
||||
<EuiBadge color="hollow">{'Search' as ProviderSolution}</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
return (
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
|
@ -65,12 +79,7 @@ const SelectableProviderComponent: React.FC<SelectableProviderProps> = ({
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup gutterSize="xs" responsive={false}>
|
||||
{provider &&
|
||||
provider.solutions.map((solution) => (
|
||||
<EuiFlexItem>
|
||||
<EuiBadge color="hollow">{solution}</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
{supportedBySolutions}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -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<string, ConfigProperties>;
|
||||
|
||||
export interface Config {
|
||||
taskType: string;
|
||||
taskTypeConfig?: Record<string, unknown>;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 };
|
||||
|
||||
|
|
|
@ -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<void, void> {
|
|||
|
||||
getWellKnownEmailServiceRoute(router);
|
||||
validSlackApiChannelsRoute(router, actions.getActionsConfigurationUtilities(), this.logger);
|
||||
getInferenceServicesRoute(router);
|
||||
|
||||
registerConnectorTypes({
|
||||
actions,
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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<unknown, unknown, unknown>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse> {
|
||||
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,
|
||||
});
|
||||
}
|
||||
};
|
|
@ -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';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue