From 451af66be014d8bd617c64f061ad582cd3cfae7e Mon Sep 17 00:00:00 2001 From: zxhlyh Date: Wed, 29 Nov 2023 14:58:11 +0800 Subject: [PATCH] feat: add jina embedding (#1647) Co-authored-by: takatost --- .../model_providers/model_provider_factory.py | 3 + .../models/embedding/jina_embedding.py | 25 ++++ .../providers/jina_provider.py | 141 ++++++++++++++++++ .../model_providers/rules/_providers.json | 3 +- api/core/model_providers/rules/jina.json | 10 ++ .../langchain/embeddings/jina_embedding.py | 69 +++++++++ api/tests/integration_tests/.env.example | 5 +- .../models/embedding/test_jina_embedding.py | 42 ++++++ .../model_providers/test_jina_provider.py | 88 +++++++++++ web/app/components/app/chat/answer/index.tsx | 2 +- .../app/configuration/config-var/index.tsx | 2 +- .../icons/assets/public/llm/jina-text.svg | 12 ++ .../base/icons/assets/public/llm/jina.svg | 11 ++ .../base/icons/src/public/llm/Jina.json | 75 ++++++++++ .../base/icons/src/public/llm/Jina.tsx | 16 ++ .../base/icons/src/public/llm/JinaText.json | 82 ++++++++++ .../base/icons/src/public/llm/JinaText.tsx | 16 ++ .../base/icons/src/public/llm/index.ts | 2 + .../model-page/configs/index.ts | 2 + .../model-page/configs/jina.tsx | 57 +++++++ .../model-page/declarations.ts | 1 + .../account-setting/model-page/index.tsx | 2 + 22 files changed, 662 insertions(+), 4 deletions(-) create mode 100644 api/core/model_providers/models/embedding/jina_embedding.py create mode 100644 api/core/model_providers/providers/jina_provider.py create mode 100644 api/core/model_providers/rules/jina.json create mode 100644 api/core/third_party/langchain/embeddings/jina_embedding.py create mode 100644 api/tests/integration_tests/models/embedding/test_jina_embedding.py create mode 100644 api/tests/unit_tests/model_providers/test_jina_provider.py create mode 100644 web/app/components/base/icons/assets/public/llm/jina-text.svg create mode 100644 web/app/components/base/icons/assets/public/llm/jina.svg create mode 100644 web/app/components/base/icons/src/public/llm/Jina.json create mode 100644 web/app/components/base/icons/src/public/llm/Jina.tsx create mode 100644 web/app/components/base/icons/src/public/llm/JinaText.json create mode 100644 web/app/components/base/icons/src/public/llm/JinaText.tsx create mode 100644 web/app/components/header/account-setting/model-page/configs/jina.tsx diff --git a/api/core/model_providers/model_provider_factory.py b/api/core/model_providers/model_provider_factory.py index ea17f212c1..de6b8287f8 100644 --- a/api/core/model_providers/model_provider_factory.py +++ b/api/core/model_providers/model_provider_factory.py @@ -75,6 +75,9 @@ class ModelProviderFactory: elif provider_name == 'cohere': from core.model_providers.providers.cohere_provider import CohereProvider return CohereProvider + elif provider_name == 'jina': + from core.model_providers.providers.jina_provider import JinaProvider + return JinaProvider else: raise NotImplementedError diff --git a/api/core/model_providers/models/embedding/jina_embedding.py b/api/core/model_providers/models/embedding/jina_embedding.py new file mode 100644 index 0000000000..7c611af1c5 --- /dev/null +++ b/api/core/model_providers/models/embedding/jina_embedding.py @@ -0,0 +1,25 @@ +from core.model_providers.error import LLMBadRequestError +from core.model_providers.models.embedding.base import BaseEmbedding +from core.model_providers.providers.base import BaseModelProvider +from core.third_party.langchain.embeddings.jina_embedding import JinaEmbeddings + + +class JinaEmbedding(BaseEmbedding): + def __init__(self, model_provider: BaseModelProvider, name: str): + credentials = model_provider.get_model_credentials( + model_name=name, + model_type=self.type + ) + + client = JinaEmbeddings( + model=name, + **credentials + ) + + super().__init__(model_provider, client, name) + + def handle_exceptions(self, ex: Exception) -> Exception: + if isinstance(ex, ValueError): + return LLMBadRequestError(f"Jina: {str(ex)}") + else: + return ex diff --git a/api/core/model_providers/providers/jina_provider.py b/api/core/model_providers/providers/jina_provider.py new file mode 100644 index 0000000000..fd3402582b --- /dev/null +++ b/api/core/model_providers/providers/jina_provider.py @@ -0,0 +1,141 @@ +import json +from json import JSONDecodeError +from typing import Type + +from core.helper import encrypter +from core.model_providers.models.base import BaseProviderModel +from core.model_providers.models.embedding.jina_embedding import JinaEmbedding +from core.model_providers.models.entity.model_params import ModelType, ModelKwargsRules +from core.model_providers.providers.base import BaseModelProvider, CredentialsValidateFailedError +from core.third_party.langchain.embeddings.jina_embedding import JinaEmbeddings +from models.provider import ProviderType + + +class JinaProvider(BaseModelProvider): + + @property + def provider_name(self): + """ + Returns the name of a provider. + """ + return 'jina' + + def _get_fixed_model_list(self, model_type: ModelType) -> list[dict]: + if model_type == ModelType.EMBEDDINGS: + return [ + { + 'id': 'jina-embeddings-v2-base-en', + 'name': 'jina-embeddings-v2-base-en', + }, + { + 'id': 'jina-embeddings-v2-small-en', + 'name': 'jina-embeddings-v2-small-en', + } + ] + else: + return [] + + def get_model_class(self, model_type: ModelType) -> Type[BaseProviderModel]: + """ + Returns the model class. + + :param model_type: + :return: + """ + if model_type == ModelType.EMBEDDINGS: + model_class = JinaEmbedding + else: + raise NotImplementedError + + return model_class + + @classmethod + def is_provider_credentials_valid_or_raise(cls, credentials: dict): + """ + Validates the given credentials. + """ + if 'api_key' not in credentials: + raise CredentialsValidateFailedError('Jina API Key must be provided.') + + try: + credential_kwargs = { + 'api_key': credentials['api_key'], + } + + embedding = JinaEmbeddings( + model='jina-embeddings-v2-small-en', + **credential_kwargs + ) + + embedding.embed_query("ping") + except Exception as ex: + raise CredentialsValidateFailedError(str(ex)) + + @classmethod + def encrypt_provider_credentials(cls, tenant_id: str, credentials: dict) -> dict: + credentials['api_key'] = encrypter.encrypt_token(tenant_id, credentials['api_key']) + return credentials + + def get_provider_credentials(self, obfuscated: bool = False) -> dict: + if self.provider.provider_type == ProviderType.CUSTOM.value: + try: + credentials = json.loads(self.provider.encrypted_config) + except JSONDecodeError: + credentials = { + 'api_key': None, + } + + if credentials['api_key']: + credentials['api_key'] = encrypter.decrypt_token( + self.provider.tenant_id, + credentials['api_key'] + ) + + if obfuscated: + credentials['api_key'] = encrypter.obfuscated_token(credentials['api_key']) + + return credentials + + return {} + + @classmethod + def is_model_credentials_valid_or_raise(cls, model_name: str, model_type: ModelType, credentials: dict): + """ + check model credentials valid. + + :param model_name: + :param model_type: + :param credentials: + """ + return + + @classmethod + def encrypt_model_credentials(cls, tenant_id: str, model_name: str, model_type: ModelType, + credentials: dict) -> dict: + """ + encrypt model credentials for save. + + :param tenant_id: + :param model_name: + :param model_type: + :param credentials: + :return: + """ + return {} + + def get_model_credentials(self, model_name: str, model_type: ModelType, obfuscated: bool = False) -> dict: + """ + get credentials for llm use. + + :param model_name: + :param model_type: + :param obfuscated: + :return: + """ + return self.get_provider_credentials(obfuscated) + + def _get_text_generation_model_mode(self, model_name) -> str: + raise NotImplementedError + + def get_model_parameter_rules(self, model_name: str, model_type: ModelType) -> ModelKwargsRules: + raise NotImplementedError diff --git a/api/core/model_providers/rules/_providers.json b/api/core/model_providers/rules/_providers.json index 0e549828bb..8c500e9dda 100644 --- a/api/core/model_providers/rules/_providers.json +++ b/api/core/model_providers/rules/_providers.json @@ -14,5 +14,6 @@ "xinference", "openllm", "localai", - "cohere" + "cohere", + "jina" ] diff --git a/api/core/model_providers/rules/jina.json b/api/core/model_providers/rules/jina.json new file mode 100644 index 0000000000..e63bf3975a --- /dev/null +++ b/api/core/model_providers/rules/jina.json @@ -0,0 +1,10 @@ +{ + "support_provider_types": [ + "custom" + ], + "system_config": null, + "model_flexibility": "fixed", + "supported_model_types": [ + "embeddings" + ] +} \ No newline at end of file diff --git a/api/core/third_party/langchain/embeddings/jina_embedding.py b/api/core/third_party/langchain/embeddings/jina_embedding.py new file mode 100644 index 0000000000..56c579763f --- /dev/null +++ b/api/core/third_party/langchain/embeddings/jina_embedding.py @@ -0,0 +1,69 @@ +"""Wrapper around Jina embedding models.""" +from typing import Any, List + +import requests +from pydantic import BaseModel, Extra + +from langchain.embeddings.base import Embeddings + + +class JinaEmbeddings(BaseModel, Embeddings): + """Wrapper around Jina embedding models. + """ + + client: Any #: :meta private: + api_key: str + model: str + + class Config: + """Configuration for this pydantic object.""" + + extra = Extra.forbid + + def embed_documents(self, texts: List[str]) -> List[List[float]]: + """Call out to Jina's embedding endpoint. + + Args: + texts: The list of texts to embed. + + Returns: + List of embeddings, one for each text. + """ + embeddings = [] + for text in texts: + result = self.invoke_embedding(text=text) + embeddings.append(result) + + return [list(map(float, e)) for e in embeddings] + + def invoke_embedding(self, text): + params = { + "model": self.model, + "input": [ + text + ] + } + + headers = {"Content-Type": "application/json", "Authorization": f"Bearer {self.api_key}"} + response = requests.post( + 'https://api.jina.ai/v1/embeddings', + headers=headers, + json=params + ) + + if not response.ok: + raise ValueError(f"Jina HTTP {response.status_code} error: {response.text}") + + json_response = response.json() + return json_response["data"][0]["embedding"] + + def embed_query(self, text: str) -> List[float]: + """Call out to Jina's embedding endpoint. + + Args: + text: The text to embed. + + Returns: + Embeddings for the text. + """ + return self.embed_documents([text])[0] diff --git a/api/tests/integration_tests/.env.example b/api/tests/integration_tests/.env.example index 11091aa34c..2faf8fcd64 100644 --- a/api/tests/integration_tests/.env.example +++ b/api/tests/integration_tests/.env.example @@ -53,4 +53,7 @@ OPENLLM_SERVER_URL= LOCALAI_SERVER_URL= # Cohere Credentials -COHERE_API_KEY= \ No newline at end of file +COHERE_API_KEY= + +# Jina Credentials +JINA_API_KEY= \ No newline at end of file diff --git a/api/tests/integration_tests/models/embedding/test_jina_embedding.py b/api/tests/integration_tests/models/embedding/test_jina_embedding.py new file mode 100644 index 0000000000..db76b253c2 --- /dev/null +++ b/api/tests/integration_tests/models/embedding/test_jina_embedding.py @@ -0,0 +1,42 @@ +import json +import os +from unittest.mock import patch + +from core.model_providers.models.embedding.jina_embedding import JinaEmbedding +from core.model_providers.providers.jina_provider import JinaProvider +from models.provider import Provider, ProviderType + + +def get_mock_provider(valid_api_key): + return Provider( + id='provider_id', + tenant_id='tenant_id', + provider_name='jina', + provider_type=ProviderType.CUSTOM.value, + encrypted_config=json.dumps({ + 'api_key': valid_api_key + }), + is_valid=True, + ) + + +def get_mock_embedding_model(): + model_name = 'jina-embeddings-v2-small-en' + valid_api_key = os.environ['JINA_API_KEY'] + provider = JinaProvider(provider=get_mock_provider(valid_api_key)) + return JinaEmbedding( + model_provider=provider, + name=model_name + ) + + +def decrypt_side_effect(tenant_id, encrypted_api_key): + return encrypted_api_key + + +@patch('core.helper.encrypter.decrypt_token', side_effect=decrypt_side_effect) +def test_embedding(mock_decrypt): + embedding_model = get_mock_embedding_model() + rst = embedding_model.client.embed_query('test') + assert isinstance(rst, list) + assert len(rst) == 512 diff --git a/api/tests/unit_tests/model_providers/test_jina_provider.py b/api/tests/unit_tests/model_providers/test_jina_provider.py new file mode 100644 index 0000000000..539fa4929a --- /dev/null +++ b/api/tests/unit_tests/model_providers/test_jina_provider.py @@ -0,0 +1,88 @@ +import pytest +from unittest.mock import patch +import json + +from core.model_providers.providers.base import CredentialsValidateFailedError +from core.model_providers.providers.jina_provider import JinaProvider +from models.provider import ProviderType, Provider + + +PROVIDER_NAME = 'jina' +MODEL_PROVIDER_CLASS = JinaProvider +VALIDATE_CREDENTIAL = { + 'api_key': 'valid_key' +} + + +def encrypt_side_effect(tenant_id, encrypt_key): + return f'encrypted_{encrypt_key}' + + +def decrypt_side_effect(tenant_id, encrypted_key): + return encrypted_key.replace('encrypted_', '') + + +def test_is_provider_credentials_valid_or_raise_valid(mocker): + mocker.patch('core.third_party.langchain.embeddings.jina_embedding.JinaEmbeddings.embed_query', + return_value=[1, 2]) + + MODEL_PROVIDER_CLASS.is_provider_credentials_valid_or_raise(VALIDATE_CREDENTIAL) + + +def test_is_provider_credentials_valid_or_raise_invalid(): + # raise CredentialsValidateFailedError if api_key is not in credentials + with pytest.raises(CredentialsValidateFailedError): + MODEL_PROVIDER_CLASS.is_provider_credentials_valid_or_raise({}) + + credential = VALIDATE_CREDENTIAL.copy() + credential['api_key'] = 'invalid_key' + + # raise CredentialsValidateFailedError if api_key is invalid + with pytest.raises(CredentialsValidateFailedError): + MODEL_PROVIDER_CLASS.is_provider_credentials_valid_or_raise(credential) + + +@patch('core.helper.encrypter.encrypt_token', side_effect=encrypt_side_effect) +def test_encrypt_credentials(mock_encrypt): + api_key = 'valid_key' + result = MODEL_PROVIDER_CLASS.encrypt_provider_credentials('tenant_id', VALIDATE_CREDENTIAL.copy()) + mock_encrypt.assert_called_with('tenant_id', api_key) + assert result['api_key'] == f'encrypted_{api_key}' + + +@patch('core.helper.encrypter.decrypt_token', side_effect=decrypt_side_effect) +def test_get_credentials_custom(mock_decrypt): + encrypted_credential = VALIDATE_CREDENTIAL.copy() + encrypted_credential['api_key'] = 'encrypted_' + encrypted_credential['api_key'] + + provider = Provider( + id='provider_id', + tenant_id='tenant_id', + provider_name=PROVIDER_NAME, + provider_type=ProviderType.CUSTOM.value, + encrypted_config=json.dumps(encrypted_credential), + is_valid=True, + ) + model_provider = MODEL_PROVIDER_CLASS(provider=provider) + result = model_provider.get_provider_credentials() + assert result['api_key'] == 'valid_key' + + +@patch('core.helper.encrypter.decrypt_token', side_effect=decrypt_side_effect) +def test_get_credentials_obfuscated(mock_decrypt): + encrypted_credential = VALIDATE_CREDENTIAL.copy() + encrypted_credential['api_key'] = 'encrypted_' + encrypted_credential['api_key'] + + provider = Provider( + id='provider_id', + tenant_id='tenant_id', + provider_name=PROVIDER_NAME, + provider_type=ProviderType.CUSTOM.value, + encrypted_config=json.dumps(encrypted_credential), + is_valid=True, + ) + model_provider = MODEL_PROVIDER_CLASS(provider=provider) + result = model_provider.get_provider_credentials(obfuscated=True) + middle_token = result['api_key'][6:-2] + assert len(middle_token) == max(len(VALIDATE_CREDENTIAL['api_key']) - 8, 0) + assert all(char == '*' for char in middle_token) diff --git a/web/app/components/app/chat/answer/index.tsx b/web/app/components/app/chat/answer/index.tsx index 3387780f6a..a6b0a6bae5 100644 --- a/web/app/components/app/chat/answer/index.tsx +++ b/web/app/components/app/chat/answer/index.tsx @@ -280,7 +280,7 @@ const Answer: FC = ({ {!feedbackDisabled && renderFeedbackRating(feedback?.rating, !isHideFeedbackEdit, displayScene !== 'console')} - {more && } + {more && } diff --git a/web/app/components/app/configuration/config-var/index.tsx b/web/app/components/app/configuration/config-var/index.tsx index 8ac94d626e..bbeca55f9f 100644 --- a/web/app/components/app/configuration/config-var/index.tsx +++ b/web/app/components/app/configuration/config-var/index.tsx @@ -186,7 +186,7 @@ const ConfigVar: FC = ({ promptVariables, readonly, onPromptVar )} {hasVar && (
- +
diff --git a/web/app/components/base/icons/assets/public/llm/jina-text.svg b/web/app/components/base/icons/assets/public/llm/jina-text.svg new file mode 100644 index 0000000000..6a241fc9ae --- /dev/null +++ b/web/app/components/base/icons/assets/public/llm/jina-text.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/web/app/components/base/icons/assets/public/llm/jina.svg b/web/app/components/base/icons/assets/public/llm/jina.svg new file mode 100644 index 0000000000..a55dac26f3 --- /dev/null +++ b/web/app/components/base/icons/assets/public/llm/jina.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/web/app/components/base/icons/src/public/llm/Jina.json b/web/app/components/base/icons/src/public/llm/Jina.json new file mode 100644 index 0000000000..1336c0a56b --- /dev/null +++ b/web/app/components/base/icons/src/public/llm/Jina.json @@ -0,0 +1,75 @@ +{ + "icon": { + "type": "element", + "isRootNode": true, + "name": "svg", + "attributes": { + "width": "22", + "height": "22", + "viewBox": "0 0 22 22", + "fill": "none", + "xmlns": "http://www.w3.org/2000/svg" + }, + "children": [ + { + "type": "element", + "name": "g", + "attributes": { + "id": "J 1" + }, + "children": [ + { + "type": "element", + "name": "rect", + "attributes": { + "width": "22", + "height": "22", + "rx": "5", + "fill": "black" + }, + "children": [] + }, + { + "type": "element", + "name": "g", + "attributes": { + "id": "Company-Logo---J" + }, + "children": [ + { + "type": "element", + "name": "g", + "attributes": { + "id": "Company-logo_light" + }, + "children": [ + { + "type": "element", + "name": "path", + "attributes": { + "id": "椭圆形备份-3", + "d": "M6.43944 18.5769C8.45441 18.5769 10.0879 16.9435 10.0879 14.9286C10.0879 12.9137 8.45441 11.2803 6.43944 11.2803C4.42447 11.2803 2.79102 12.9137 2.79102 14.9286C2.79102 16.9435 4.42447 18.5769 6.43944 18.5769Z", + "fill": "white" + }, + "children": [] + }, + { + "type": "element", + "name": "path", + "attributes": { + "id": "形状结合", + "d": "M18.7912 4.29374L18.7435 11.2803C18.7435 15.2625 15.5481 18.5054 11.5658 18.5769L11.4941 11.3042L11.4943 4.31759C11.4943 3.84069 11.8758 3.45917 12.3527 3.45917H17.9327C18.4096 3.45917 18.7912 3.81684 18.7912 4.29374Z", + "fill": "white" + }, + "children": [] + } + ] + } + ] + } + ] + } + ] + }, + "name": "Jina" +} \ No newline at end of file diff --git a/web/app/components/base/icons/src/public/llm/Jina.tsx b/web/app/components/base/icons/src/public/llm/Jina.tsx new file mode 100644 index 0000000000..8036e08979 --- /dev/null +++ b/web/app/components/base/icons/src/public/llm/Jina.tsx @@ -0,0 +1,16 @@ +// GENERATE BY script +// DON NOT EDIT IT MANUALLY + +import * as React from 'react' +import data from './Jina.json' +import IconBase from '@/app/components/base/icons/IconBase' +import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase' + +const Icon = React.forwardRef, Omit>(( + props, + ref, +) => ) + +Icon.displayName = 'Jina' + +export default Icon diff --git a/web/app/components/base/icons/src/public/llm/JinaText.json b/web/app/components/base/icons/src/public/llm/JinaText.json new file mode 100644 index 0000000000..04831fa4aa --- /dev/null +++ b/web/app/components/base/icons/src/public/llm/JinaText.json @@ -0,0 +1,82 @@ +{ + "icon": { + "type": "element", + "isRootNode": true, + "name": "svg", + "attributes": { + "width": "58", + "height": "24", + "viewBox": "0 0 58 24", + "fill": "none", + "xmlns": "http://www.w3.org/2000/svg" + }, + "children": [ + { + "type": "element", + "name": "g", + "attributes": { + "clip-path": "url(#clip0_13814_61529)" + }, + "children": [ + { + "type": "element", + "name": "path", + "attributes": { + "d": "M4.47132 23.952C6.49932 23.952 8.14332 22.308 8.14332 20.28C8.14332 18.252 6.49932 16.608 4.47132 16.608C2.44332 16.608 0.799316 18.252 0.799316 20.28C0.799316 22.308 2.44332 23.952 4.47132 23.952Z", + "fill": "#EB6161" + }, + "children": [] + }, + { + "type": "element", + "name": "path", + "attributes": { + "fill-rule": "evenodd", + "clip-rule": "evenodd", + "d": "M16.0387 8.71204C16.5187 8.71204 16.9027 9.09604 16.9027 9.57604L16.8547 16.608C16.8547 20.616 13.6387 23.88 9.63074 23.952H9.51074V16.632H9.53474L9.55874 9.60004C9.55874 9.12004 9.94274 8.73604 10.4227 8.73604H16.0387V8.71204ZM27.3187 8.71204C27.7987 8.71204 28.1827 9.09604 28.1827 9.57604V19.416C28.1827 19.896 27.7987 20.28 27.3187 20.28H21.7027C21.2227 20.28 20.8387 19.896 20.8387 19.416V9.57604C20.8387 9.09604 21.2227 8.71204 21.7027 8.71204H27.3187ZM36.1507 8.68804H36.2707C39.8707 8.73604 42.7987 11.64 42.8947 15.24V19.392C42.8947 19.872 42.5107 20.256 42.0307 20.256H32.9587C32.4787 20.256 32.0947 19.872 32.0947 19.392V9.55204C32.0947 9.07204 32.4787 8.68804 32.9587 8.68804H36.1507ZM51.0067 20.16C47.9827 19.968 45.5587 17.448 45.5587 14.376C45.5587 11.184 48.1507 8.59204 51.3427 8.59204C54.4147 8.59204 56.9347 10.992 57.1267 14.04V19.296C57.1267 19.776 56.7427 20.16 56.2627 20.16H51.0067Z", + "fill": "#009191" + }, + "children": [] + }, + { + "type": "element", + "name": "path", + "attributes": { + "d": "M24.4987 7.344C26.5267 7.344 28.1707 5.7 28.1707 3.672C28.1707 1.644 26.5267 0 24.4987 0C22.4707 0 20.8267 1.644 20.8267 3.672C20.8267 5.7 22.4707 7.344 24.4987 7.344Z", + "fill": "#FBCB67" + }, + "children": [] + } + ] + }, + { + "type": "element", + "name": "defs", + "attributes": {}, + "children": [ + { + "type": "element", + "name": "clipPath", + "attributes": { + "id": "clip0_13814_61529" + }, + "children": [ + { + "type": "element", + "name": "rect", + "attributes": { + "width": "56.4", + "height": "24", + "fill": "white", + "transform": "translate(0.800781)" + }, + "children": [] + } + ] + } + ] + } + ] + }, + "name": "JinaText" +} \ No newline at end of file diff --git a/web/app/components/base/icons/src/public/llm/JinaText.tsx b/web/app/components/base/icons/src/public/llm/JinaText.tsx new file mode 100644 index 0000000000..802d5aacad --- /dev/null +++ b/web/app/components/base/icons/src/public/llm/JinaText.tsx @@ -0,0 +1,16 @@ +// GENERATE BY script +// DON NOT EDIT IT MANUALLY + +import * as React from 'react' +import data from './JinaText.json' +import IconBase from '@/app/components/base/icons/IconBase' +import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase' + +const Icon = React.forwardRef, Omit>(( + props, + ref, +) => ) + +Icon.displayName = 'JinaText' + +export default Icon diff --git a/web/app/components/base/icons/src/public/llm/index.ts b/web/app/components/base/icons/src/public/llm/index.ts index e35c3838d1..3545049795 100644 --- a/web/app/components/base/icons/src/public/llm/index.ts +++ b/web/app/components/base/icons/src/public/llm/index.ts @@ -18,6 +18,8 @@ export { default as Huggingface } from './Huggingface' export { default as IflytekSparkTextCn } from './IflytekSparkTextCn' export { default as IflytekSparkText } from './IflytekSparkText' export { default as IflytekSpark } from './IflytekSpark' +export { default as JinaText } from './JinaText' +export { default as Jina } from './Jina' export { default as LocalaiText } from './LocalaiText' export { default as Localai } from './Localai' export { default as Microsoft } from './Microsoft' diff --git a/web/app/components/header/account-setting/model-page/configs/index.ts b/web/app/components/header/account-setting/model-page/configs/index.ts index cee0ad8214..6f0c49daaa 100644 --- a/web/app/components/header/account-setting/model-page/configs/index.ts +++ b/web/app/components/header/account-setting/model-page/configs/index.ts @@ -14,6 +14,7 @@ import localai from './localai' import zhipuai from './zhipuai' import baichuan from './baichuan' import cohere from './cohere' +import jina from './jina' export default { openai, @@ -32,4 +33,5 @@ export default { zhipuai, baichuan, cohere, + jina, } diff --git a/web/app/components/header/account-setting/model-page/configs/jina.tsx b/web/app/components/header/account-setting/model-page/configs/jina.tsx new file mode 100644 index 0000000000..e4ad6b9761 --- /dev/null +++ b/web/app/components/header/account-setting/model-page/configs/jina.tsx @@ -0,0 +1,57 @@ +import { ProviderEnum } from '../declarations' +import type { ProviderConfig } from '../declarations' +import { Jina, JinaText } from '@/app/components/base/icons/src/public/llm' + +const config: ProviderConfig = { + selector: { + name: { + 'en': 'Jina', + 'zh-Hans': 'Jina', + }, + icon: , + }, + item: { + key: ProviderEnum.jina, + titleIcon: { + 'en': , + 'zh-Hans': , + }, + hit: { + 'en': 'Embedding Model Supported', + 'zh-Hans': '支持 Embedding 模型', + }, + }, + modal: { + key: ProviderEnum.jina, + title: { + 'en': 'Embedding Model', + 'zh-Hans': 'Embedding 模型', + }, + icon: , + link: { + href: 'https://jina.ai/embeddings/', + label: { + 'en': 'Get your API key from Jina', + 'zh-Hans': '从 Jina 获取 API Key', + }, + }, + validateKeys: ['api_key'], + fields: [ + { + type: 'text', + key: 'api_key', + required: true, + label: { + 'en': 'API Key', + 'zh-Hans': 'API Key', + }, + placeholder: { + 'en': 'Enter your API key here', + 'zh-Hans': '在此输入您的 API Key', + }, + }, + ], + }, +} + +export default config diff --git a/web/app/components/header/account-setting/model-page/declarations.ts b/web/app/components/header/account-setting/model-page/declarations.ts index ba44844eaa..79a4e255cd 100644 --- a/web/app/components/header/account-setting/model-page/declarations.ts +++ b/web/app/components/header/account-setting/model-page/declarations.ts @@ -46,6 +46,7 @@ export enum ProviderEnum { 'zhipuai' = 'zhipuai', 'baichuan' = 'baichuan', 'cohere' = 'cohere', + 'jina' = 'jina', } export type ProviderConfigItem = { diff --git a/web/app/components/header/account-setting/model-page/index.tsx b/web/app/components/header/account-setting/model-page/index.tsx index b915e38149..1e7c880246 100644 --- a/web/app/components/header/account-setting/model-page/index.tsx +++ b/web/app/components/header/account-setting/model-page/index.tsx @@ -71,6 +71,7 @@ const ModelPage = () => { config.minimax, config.tongyi, config.wenxin, + config.jina, config.chatglm, config.xinference, config.openllm, @@ -89,6 +90,7 @@ const ModelPage = () => { config.replicate, config.tongyi, config.wenxin, + config.jina, config.chatglm, config.xinference, config.openllm,
{t('appDebug.variableTable.key')}