diff --git a/common/constants.ts b/common/constants.ts index 223580a1..f286c1c2 100644 --- a/common/constants.ts +++ b/common/constants.ts @@ -202,6 +202,11 @@ export enum MODEL_TYPE { SPARSE_ENCODER = 'sparse_encoder', } +export enum MODEL_CATEGORY { + EMBEDDING = 'EMBEDDING', + LLM = 'LLM', +} + /** * Various constants pertaining to the drag-and-drop UI components */ @@ -262,6 +267,27 @@ export const ML_RESPONSE_PROCESSOR_EXAMPLE_DOCS_LINK = export const UPDATE_MODEL_DOCS_LINK = 'https://opensearch.org/docs/latest/ml-commons-plugin/api/model-apis/update-model/'; +// Large Language Models Documentation Links +export const BEDROCK_CLAUDE_3_SONNET_DOCS_LINK = + 'https://github.com/opensearch-project/dashboards-flow-framework/blob/main/documentation/models.md#claude-3-sonnet-hosted-on-amazon-bedrock'; + +export const OPENAI_GPT35_DOCS_LINK = + 'https://github.com/opensearch-project/dashboards-flow-framework/blob/main/documentation/models.md#openai-gpt-35'; + +export const DEEPSEEK_CHAT_DOCS_LINK = + 'https://github.com/opensearch-project/dashboards-flow-framework/blob/main/documentation/models.md#deepseek-chat'; + +// Embedding Models Documentation Links +export const COHERE_EMBEDDING_MODEL_DOCS_LINK = + 'https://github.com/opensearch-project/dashboards-flow-framework/blob/main/documentation/models.md#cohere-embed'; + +export const BEDROCK_TITAN_EMBEDDING_DOCS_LINK = + 'https://github.com/opensearch-project/dashboards-flow-framework/blob/main/documentation/models.md#amazon-bedrock-titan-text-embedding'; + +// ML Models setup Documentation Link +export const ML_MODELS_SETUP_DOCS_LINK = + 'https://github.com/opensearch-project/dashboards-flow-framework/blob/main/documentation/models.md'; + /** * Text chunking algorithm constants */ @@ -350,6 +376,24 @@ export const SEMANTIC_SEARCH_QUERY_NEURAL = { }, }, }; +export const SEMANTIC_SEARCH_TEMPLATE_QUERY = { + query: { + template: { + knn: { + [VECTOR_FIELD_PATTERN]: { + vector: VECTOR_PATTERN, + k: 2, + }, + }, + }, + }, + ext: { + ml_inference: { + text: QUERY_TEXT_PATTERN, + }, + }, +}; + export const MULTIMODAL_SEARCH_QUERY_NEURAL = { _source: { excludes: [VECTOR_FIELD_PATTERN], @@ -479,6 +523,14 @@ export const QUERY_PRESETS = [ name: WORKFLOW_TYPE.MULTIMODAL_SEARCH, query: customStringify(MULTIMODAL_SEARCH_QUERY_BOOL), }, + { + name: 'Semantic search (neural query)', + query: customStringify(SEMANTIC_SEARCH_QUERY_NEURAL), + }, + { + name: 'Semantic search (template query)', + query: customStringify(SEMANTIC_SEARCH_TEMPLATE_QUERY), + }, { name: `Hybrid search (match & k-NN queries)`, query: customStringify(HYBRID_SEARCH_QUERY_MATCH_KNN), diff --git a/documentation/models.md b/documentation/models.md index 4b34a327..bb867587 100644 --- a/documentation/models.md +++ b/documentation/models.md @@ -367,6 +367,112 @@ POST /_plugins/_ml/models/_register } ``` +### Cohere Embed + +Connector: + +``` +POST /_plugins/_ml/connectors/_create +{ + "name": "Cohere Embed Model", + "description": "The connector to Cohere's public embed API", + "version": "1", + "protocol": "http", + "credential": { + "cohere_key": "" + }, + "parameters": { + "model": "embed-english-v3.0", + "input_type":"search_document", + "truncate": "END" + }, + "actions": [ + { + "action_type": "predict", + "method": "POST", + "url": "https://api.cohere.ai/v1/embed", + "headers": { + "Authorization": "Bearer ${credential.cohere_key}", + "Request-Source": "unspecified:opensearch" + }, + "request_body": "{ \"texts\": [\"${parameters.text}\"], \"truncate\": \"${parameters.truncate}\", \"model\": \"${parameters.model}\", \"input_type\": \"${parameters.input_type}\" }" + } + ] +} +``` + +Model: + +``` +POST /_plugins/_ml/models/_register +{ + "name": "Cohere Embed Model", + "function_name": "remote", + "description": "Your Cohere Embedding Model", + "connector_id": "", + "interface": { + "input": { + "type": "object", + "properties": { + "parameters": { + "type": "object", + "properties": { + "text": { + "type": "string" + } + }, + "required": ["text"] + } + } + }, + "output": { + "type": "object", + "properties": { + "inference_results": { + "type": "array", + "items": { + "type": "object", + "properties": { + "output": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "dataAsMap": { + "type": "object", + "properties": { + + + "embeddings": { + "type": "array", + "items": { + "type": "array", + "items": {"type": "number"} + } + } + } + } + }, + "required": ["name", "dataAsMap"] + } + }, + "status_code": { + "type": "integer" + } + }, + "required": ["output", "status_code"] + } + } + }, + "required": ["inference_results"] + } + } +} +``` + ## Generative models ### Claude 3 Sonnet (hosted on Amazon Bedrock) @@ -491,6 +597,234 @@ POST /_plugins/_ml/models/_register } ``` +### DeepSeek Chat + +Connector: + +``` +POST /_plugins/_ml/connectors/_create +{ + "name": "DeepSeek Chat", + "description": "Test connector for DeepSeek Chat", + "version": "1", + "protocol": "http", + "parameters": { + "endpoint": "api.deepseek.com", + "model": "deepseek-chat" + }, + "credential": { + "deepSeek_key": "" + }, + "actions": [ + { + "action_type": "predict", + "method": "POST", + "url": "https://${parameters.endpoint}/v1/chat/completions", + "headers": { + "Content-Type": "application/json", + "Authorization": "Bearer ${credential.deepSeek_key}" + }, + "request_body": "{ \"model\": \"${parameters.model}\", \"messages\": [{\"role\": \"user\", \"content\": \"${parameters.prompt}\"}] }" + } + ] +} +``` + +Model: + +``` +POST /_plugins/_ml/models/_register +{ + "name": "DeepSeek Chat model", + "function_name": "remote", + "description": "DeepSeek Chat", + "connector_id": "", + "interface": { + "input": { + "properties": { + "parameters": { + "properties": { + "prompt": { + "type": "string" + } + } + } + } + }, + "output": { + "properties": { + "inference_results": { + "type": "array", + "items": { + "type": "object", + "properties": { + "output": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "dataAsMap": { + "type": "object", + "properties": { + "choices": { + "type": "array", + "items": { + "type": "object", + "properties": { + "index": { + "type": "integer" + }, + "message": { + "type": "object", + "properties": { + "role": { + "type": "string" + }, + "content": { + "type": "string" + } + } + }, + "finish_reason": { + "type": "string" + } + } + } + } + } + } + } + } + }, + "status_code": { + "type": "integer" + } + } + } + } + } + } + } +} +``` + +### OpenAI GPT-3.5 + +Connector: + +``` +POST /_plugins/_ml/connectors/_create +{ + "name": "OpenAI gpt-3.5 connector", + "description": "OpenAI gpt-3.5 connector", + "version": "gpt-3.5-turbo", + "protocol": "http", + "parameters": { + "endpoint": "api.openai.com", + "model": "gpt-3.5-turbo" + }, + "credential": { + "openAI_key": "" + }, + "actions": [ + { + "action_type": "predict", + "method": "POST", + "url": "https://${parameters.endpoint}/v1/chat/completions", + "headers": { + "Authorization": "Bearer ${credential.openAI_key}" + }, + "request_body": "{ \"model\": \"${parameters.model}\", \"messages\": [{\"role\": \"user\", \"content\": \"${parameters.prompt}\"}] }" + } + ] +} +``` + +Model: + +``` +POST /_plugins/_ml/models/_register +{ + "name": "openAI-gpt-3.5-turbo", + "function_name": "remote", + "description": "test model", + "connector_id": "", + "interface": { + "input": { + "properties": { + "parameters": { + "properties": { + "prompt": { + "type": "string", + "description": "This is a test description field" + } + } + } + } + }, + "output": { + "properties": { + "inference_results": { + "type": "array", + "items": { + "type": "object", + "properties": { + "output": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "dataAsMap": { + "type": "object", + "properties": { + "choices": { + "type": "array", + "items": { + "type": "object", + "properties": { + "index": { + "type": "integer" + }, + "message": { + "type": "object", + "properties": { + "role": { + "type": "string" + }, + "content": { + "type": "string" + } + } + }, + "finish_reason": { + "type": "string" + } + } + } + } + } + } + } + } + }, + "status_code": { + "type": "integer" + } + } + } + } + } + } + } +} +``` + ## Reranking ### Cohere ReRank diff --git a/public/pages/workflow_detail/workflow_inputs/input_fields/model_field.tsx b/public/pages/workflow_detail/workflow_inputs/input_fields/model_field.tsx index 35cf71be..fc1be031 100644 --- a/public/pages/workflow_detail/workflow_inputs/input_fields/model_field.tsx +++ b/public/pages/workflow_detail/workflow_inputs/input_fields/model_field.tsx @@ -24,12 +24,14 @@ import { MODEL_STATE, WorkflowFormValues, ModelFormValue, - ML_CHOOSE_MODEL_LINK, FETCH_ALL_QUERY_LARGE, UPDATE_MODEL_DOCS_LINK, + MODEL_CATEGORY, + ML_CHOOSE_MODEL_LINK, } from '../../../../../common'; import { AppState, searchModels, useAppDispatch } from '../../../../store'; import { getDataSourceId } from '../../../../utils'; +import { ModelInfoPopover } from './models_info_popover'; interface ModelFieldProps { fieldPath: string; // the full path in string-form to the field (e.g., 'ingest.enrich.processors.text_embedding_processor.inputField') @@ -41,6 +43,7 @@ interface ModelFieldProps { fullWidth?: boolean; showError?: boolean; showInvalid?: boolean; + modelCategory?: MODEL_CATEGORY; } type ModelItem = ModelFormValue & { @@ -119,7 +122,9 @@ export function ModelField(props: ModelFieldProps) { fullWidth={props.fullWidth} label={props.label || 'Model'} labelAppend={ - + props.modelCategory ? ( + + ) : Learn more @@ -137,33 +142,33 @@ export function ModelField(props: ModelFieldProps) { disabled={isEmpty(deployedModels)} options={deployedModels.map( (option) => - ({ - value: option.id, - inputDisplay: ( - <> + ({ + value: option.id, + inputDisplay: ( + <> + {option.name} + + ), + dropdownDisplay: ( + <> + {option.name} - - ), - dropdownDisplay: ( - <> - - {option.name} - - - {isEmpty(option.interface) - ? 'Not ready - no model interface' - : 'Deployed'} - - - ), - disabled: false, - } as EuiSuperSelectOption) + + + {isEmpty(option.interface) + ? 'Not ready - no model interface' + : 'Deployed'} + + + ), + disabled: false, + } as EuiSuperSelectOption) )} valueOfSelected={field.value?.id || ''} onChange={(option: string) => { diff --git a/public/pages/workflow_detail/workflow_inputs/input_fields/models_info_popover.tsx b/public/pages/workflow_detail/workflow_inputs/input_fields/models_info_popover.tsx new file mode 100644 index 00000000..fac5d130 --- /dev/null +++ b/public/pages/workflow_detail/workflow_inputs/input_fields/models_info_popover.tsx @@ -0,0 +1,101 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { useState } from 'react'; +import { + EuiLink, + EuiPopover, + EuiSmallButtonEmpty, +} from '@elastic/eui'; +import { + MODEL_CATEGORY, + ML_MODELS_SETUP_DOCS_LINK, + COHERE_EMBEDDING_MODEL_DOCS_LINK, + BEDROCK_TITAN_EMBEDDING_DOCS_LINK, + BEDROCK_CLAUDE_3_SONNET_DOCS_LINK, + OPENAI_GPT35_DOCS_LINK, + DEEPSEEK_CHAT_DOCS_LINK, +} from '../../../../../common'; + +interface ModelInfoPopoverProps { + modelCategory?: MODEL_CATEGORY; +} + +export function ModelInfoPopover({ modelCategory }: ModelInfoPopoverProps) { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + const getModelLinks = () => { + if (modelCategory === MODEL_CATEGORY.EMBEDDING) { + return ( + <> + + Cohere Embed + + {' '}or{' '} + + Amazon Bedrock Titan Embedding + + + ); + } else if (modelCategory === MODEL_CATEGORY.LLM) { + return ( + <> + + Amazon Bedrock Claude 3 Sonnet + + {', '} + + OpenAI GPT-3.5 + + {', or '} + + DeepSeek Chat model + + + ); + } + return null; + }; + + const getModelTypeText = () => { + if (modelCategory === MODEL_CATEGORY.EMBEDDING) { + return 'n embedding'; + } else if (modelCategory === MODEL_CATEGORY.LLM) { + return ' large language'; + } + return ''; + }; + + return ( + setIsPopoverOpen(false)} + button={ + setIsPopoverOpen(!isPopoverOpen)} + > + Learn more + + } + > +
+

+ To create this workflow, you must select a{getModelTypeText()} model. + {getModelLinks() && <> For example: {getModelLinks()}.} +

+

+ + Learn more + + {' '}about integrating ML models. +

+
+
+ ); + +} diff --git a/public/pages/workflows/new_workflow/quick_configure_modal.tsx b/public/pages/workflows/new_workflow/quick_configure_modal.tsx index 6f307327..1f158742 100644 --- a/public/pages/workflows/new_workflow/quick_configure_modal.tsx +++ b/public/pages/workflows/new_workflow/quick_configure_modal.tsx @@ -58,6 +58,7 @@ import { Model, MODEL_STATE, ML_REMOTE_MODEL_LINK, + MODEL_CATEGORY, } from '../../../../common'; import { APP_PATH, getInitialValue } from '../../../utils'; import { AppState, createWorkflow, useAppDispatch } from '../../../store'; @@ -304,6 +305,7 @@ export function QuickConfigureModal(props: QuickConfigureModalProps) { !isEmpty(deployedModels) && ( )}