Compare commits
40
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1387f9b23e | ||
|
|
6817eab5f1 | ||
|
|
218f591a5d | ||
|
|
17af0de7b6 | ||
|
|
9d962053a2 | ||
|
|
59909b5ca7 | ||
|
|
a6cd0f0e73 | ||
|
|
2c43393bf1 | ||
|
|
669c8c3cca | ||
|
|
b0b0cc045f | ||
|
|
20d16d7b31 | ||
|
|
714722bb2d | ||
|
|
830495a607 | ||
|
|
41a4593b6d | ||
|
|
08b727833e | ||
|
|
c8b82b9d08 | ||
|
|
5becb4c43a | ||
|
|
13694293e3 | ||
|
|
815beac356 | ||
|
|
5e60204832 | ||
|
|
d2624b13a0 | ||
|
|
61f5de9662 | ||
|
|
40dbf30784 | ||
|
|
afd77c4745 | ||
|
|
d70bd4aaa4 | ||
|
|
8e05261588 | ||
|
|
a676d4387c | ||
|
|
08a5afcf9f | ||
|
|
eeaa3c1643 | ||
|
|
7c8c233cf4 | ||
|
|
129a9850eb | ||
|
|
1f98a4fff3 | ||
|
|
58e4702b14 | ||
|
|
c60749678b | ||
|
|
d5214e4644 | ||
|
|
52804ca6d1 | ||
|
|
4fb9606361 | ||
|
|
c534d95972 | ||
|
|
46ccfda493 | ||
|
|
6dc62334d6 |
+1
-1
@@ -155,4 +155,4 @@ And that's it! Once your PR is merged, you will be featured as a contributor in
|
||||
|
||||
## Getting Help
|
||||
|
||||
If you ever get stuck or got a burning question while contributing, simply shoot your queries our way via the related GitHub issue, or hop onto our [Discord](https://discord.gg/AhzKf7dNgk) for a quick chat.
|
||||
If you ever get stuck or got a burning question while contributing, simply shoot your queries our way via the related GitHub issue, or hop onto our [Discord](https://discord.gg/8Tpq4AcN9c) for a quick chat.
|
||||
|
||||
+1
-1
@@ -152,4 +152,4 @@ Dify的后端使用Python编写,使用[Flask](https://flask.palletsprojects.co
|
||||
|
||||
## 获取帮助
|
||||
|
||||
如果你在贡献过程中遇到困难或者有任何问题,可以通过相关的 GitHub 问题提出你的疑问,或者加入我们的 [Discord](https://discord.gg/AhzKf7dNgk) 进行快速交流。
|
||||
如果你在贡献过程中遇到困难或者有任何问题,可以通过相关的 GitHub 问题提出你的疑问,或者加入我们的 [Discord](https://discord.gg/8Tpq4AcN9c) 进行快速交流。
|
||||
|
||||
@@ -100,10 +100,12 @@ docker compose up -d
|
||||
|
||||
After running, you can access the Dify dashboard in your browser at [http://localhost/install](http://localhost/install) and start the initialization installation process.
|
||||
|
||||
### Helm Chart
|
||||
#### Deploy with Helm Chart
|
||||
|
||||
Big thanks to @BorisPolonsky for providing us with a [Helm Chart](https://helm.sh/) version, which allows Dify to be deployed on Kubernetes.
|
||||
You can go to https://github.com/BorisPolonsky/dify-helm for deployment information.
|
||||
[Helm Chart](https://helm.sh/) version, which allows Dify to be deployed on Kubernetes.
|
||||
|
||||
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
|
||||
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
|
||||
|
||||
### Configuration
|
||||
|
||||
@@ -120,6 +122,7 @@ For those who'd like to contribute code, see our [Contribution Guide](https://gi
|
||||
|
||||
At the same time, please consider supporting Dify by sharing it on social media and at events and conferences.
|
||||
|
||||
|
||||
### Contributors
|
||||
|
||||
<a href="https://github.com/langgenius/dify/graphs/contributors">
|
||||
@@ -128,7 +131,7 @@ At the same time, please consider supporting Dify by sharing it on social media
|
||||
|
||||
### Translations
|
||||
|
||||
We are looking for contributors to help with translating Dify to languages other than Mandarin or English. If you are interested in helping, please see the [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n/README_EN.md) for more information, and leave us a comment in the `global-users` channel of our [Discord Community Server](https://discord.gg/AhzKf7dNgk).
|
||||
We are looking for contributors to help with translating Dify to languages other than Mandarin or English. If you are interested in helping, please see the [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n/README.md) for more information, and leave us a comment in the `global-users` channel of our [Discord Community Server](https://discord.gg/8Tpq4AcN9c).
|
||||
|
||||
## Community & Support
|
||||
|
||||
|
||||
+5
-3
@@ -94,10 +94,12 @@ docker compose up -d
|
||||
|
||||
运行后,可以在浏览器上访问 [http://localhost/install](http://localhost/install) 进入 Dify 控制台并开始初始化安装操作。
|
||||
|
||||
### Helm Chart
|
||||
#### 使用 Helm Chart 部署
|
||||
|
||||
非常感谢 @BorisPolonsky 为我们提供了一个 [Helm Chart](https://helm.sh/) 版本,可以在 Kubernetes 上部署 Dify。
|
||||
您可以前往 https://github.com/BorisPolonsky/dify-helm 来获取部署信息。
|
||||
使用 [Helm Chart](https://helm.sh/) 版本,可以在 Kubernetes 上部署 Dify。
|
||||
|
||||
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
|
||||
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
|
||||
|
||||
### 配置
|
||||
|
||||
|
||||
+11
-10
@@ -109,19 +109,20 @@ def reset_encrypt_key_pair():
|
||||
click.echo(click.style('Sorry, only support SELF_HOSTED mode.', fg='red'))
|
||||
return
|
||||
|
||||
tenant = db.session.query(Tenant).first()
|
||||
if not tenant:
|
||||
click.echo(click.style('Sorry, no workspace found. Please enter /install to initialize.', fg='red'))
|
||||
return
|
||||
tenants = db.session.query(Tenant).all()
|
||||
for tenant in tenants:
|
||||
if not tenant:
|
||||
click.echo(click.style('Sorry, no workspace found. Please enter /install to initialize.', fg='red'))
|
||||
return
|
||||
|
||||
tenant.encrypt_public_key = generate_key_pair(tenant.id)
|
||||
tenant.encrypt_public_key = generate_key_pair(tenant.id)
|
||||
|
||||
db.session.query(Provider).filter(Provider.provider_type == 'custom').delete()
|
||||
db.session.query(ProviderModel).delete()
|
||||
db.session.commit()
|
||||
db.session.query(Provider).filter(Provider.provider_type == 'custom', Provider.tenant_id == tenant.id).delete()
|
||||
db.session.query(ProviderModel).filter(ProviderModel.tenant_id == tenant.id).delete()
|
||||
db.session.commit()
|
||||
|
||||
click.echo(click.style('Congratulations! '
|
||||
'the asymmetric key pair of workspace {} has been reset.'.format(tenant.id), fg='green'))
|
||||
click.echo(click.style('Congratulations! '
|
||||
'the asymmetric key pair of workspace {} has been reset.'.format(tenant.id), fg='green'))
|
||||
|
||||
|
||||
@click.command('vdb-migrate', help='migrate vector db.')
|
||||
|
||||
+6
-2
@@ -22,6 +22,7 @@ DEFAULTS = {
|
||||
'SERVICE_API_URL': 'https://api.dify.ai',
|
||||
'APP_WEB_URL': 'https://udify.app',
|
||||
'FILES_URL': '',
|
||||
'S3_ADDRESS_STYLE': 'auto',
|
||||
'STORAGE_TYPE': 'local',
|
||||
'STORAGE_LOCAL_PATH': 'storage',
|
||||
'CHECK_UPDATE_URL': 'https://updates.dify.ai',
|
||||
@@ -59,7 +60,8 @@ DEFAULTS = {
|
||||
'CAN_REPLACE_LOGO': 'False',
|
||||
'ETL_TYPE': 'dify',
|
||||
'KEYWORD_STORE': 'jieba',
|
||||
'BATCH_UPLOAD_LIMIT': 20
|
||||
'BATCH_UPLOAD_LIMIT': 20,
|
||||
'TOOL_ICON_CACHE_MAX_AGE': 3600,
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +92,7 @@ class Config:
|
||||
# ------------------------
|
||||
# General Configurations.
|
||||
# ------------------------
|
||||
self.CURRENT_VERSION = "0.5.10"
|
||||
self.CURRENT_VERSION = "0.5.11"
|
||||
self.COMMIT_SHA = get_env('COMMIT_SHA')
|
||||
self.EDITION = "SELF_HOSTED"
|
||||
self.DEPLOY_ENV = get_env('DEPLOY_ENV')
|
||||
@@ -180,6 +182,7 @@ class Config:
|
||||
self.S3_ACCESS_KEY = get_env('S3_ACCESS_KEY')
|
||||
self.S3_SECRET_KEY = get_env('S3_SECRET_KEY')
|
||||
self.S3_REGION = get_env('S3_REGION')
|
||||
self.S3_ADDRESS_STYLE = get_env('S3_ADDRESS_STYLE')
|
||||
self.AZURE_BLOB_ACCOUNT_NAME = get_env('AZURE_BLOB_ACCOUNT_NAME')
|
||||
self.AZURE_BLOB_ACCOUNT_KEY = get_env('AZURE_BLOB_ACCOUNT_KEY')
|
||||
self.AZURE_BLOB_CONTAINER_NAME = get_env('AZURE_BLOB_CONTAINER_NAME')
|
||||
@@ -298,6 +301,7 @@ class Config:
|
||||
self.BATCH_UPLOAD_LIMIT = get_env('BATCH_UPLOAD_LIMIT')
|
||||
|
||||
self.API_COMPRESSION_ENABLED = get_bool_env('API_COMPRESSION_ENABLED')
|
||||
self.TOOL_ICON_CACHE_MAX_AGE = get_env('TOOL_ICON_CACHE_MAX_AGE')
|
||||
|
||||
|
||||
class CloudEditionConfig(Config):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import io
|
||||
|
||||
from flask import send_file
|
||||
from flask import current_app, send_file
|
||||
from flask_login import current_user
|
||||
from flask_restful import Resource, reqparse
|
||||
from werkzeug.exceptions import Forbidden
|
||||
@@ -80,7 +80,8 @@ class ToolBuiltinProviderIconApi(Resource):
|
||||
@setup_required
|
||||
def get(self, provider):
|
||||
icon_bytes, minetype = ToolManageService.get_builtin_tool_provider_icon(provider)
|
||||
return send_file(io.BytesIO(icon_bytes), mimetype=minetype)
|
||||
icon_cache_max_age = int(current_app.config.get('TOOL_ICON_CACHE_MAX_AGE'))
|
||||
return send_file(io.BytesIO(icon_bytes), mimetype=minetype, max_age=icon_cache_max_age)
|
||||
|
||||
class ToolModelProviderIconApi(Resource):
|
||||
@setup_required
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import enum
|
||||
import importlib.util
|
||||
import importlib
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
@@ -145,7 +145,7 @@ class BaseAssistantApplicationRunner(AppRunner):
|
||||
result += f"result link: {response.message}. please tell user to check it."
|
||||
elif response.type == ToolInvokeMessage.MessageType.IMAGE_LINK or \
|
||||
response.type == ToolInvokeMessage.MessageType.IMAGE:
|
||||
result += "image has been created and sent to user already, you should tell user to check it now."
|
||||
result += "image has been created and sent to user already, you do not need to create it, just tell the user to check it now."
|
||||
else:
|
||||
result += f"tool response: {response.message}."
|
||||
|
||||
@@ -566,7 +566,11 @@ class BaseAssistantApplicationRunner(AppRunner):
|
||||
tools = tools.split(';')
|
||||
tool_calls: list[AssistantPromptMessage.ToolCall] = []
|
||||
tool_call_response: list[ToolPromptMessage] = []
|
||||
tool_inputs = json.loads(agent_thought.tool_input)
|
||||
try:
|
||||
tool_inputs = json.loads(agent_thought.tool_input)
|
||||
except Exception as e:
|
||||
logging.warning("tool execution error: {}, tool_input: {}.".format(str(e), agent_thought.tool_input))
|
||||
tool_inputs = { agent_thought.tool: agent_thought.tool_input }
|
||||
for tool in tools:
|
||||
# generate a uuid for tool call
|
||||
tool_call_id = str(uuid.uuid4())
|
||||
@@ -599,4 +603,4 @@ class BaseAssistantApplicationRunner(AppRunner):
|
||||
|
||||
db.session.close()
|
||||
|
||||
return result
|
||||
return result
|
||||
|
||||
@@ -182,7 +182,7 @@ class AssistantCotApplicationRunner(BaseAssistantApplicationRunner):
|
||||
delta=LLMResultChunkDelta(
|
||||
index=0,
|
||||
message=AssistantPromptMessage(
|
||||
content=json.dumps(chunk)
|
||||
content=json.dumps(chunk, ensure_ascii=False) # if ensure_ascii=True, the text in webui maybe garbled text
|
||||
),
|
||||
usage=None
|
||||
)
|
||||
@@ -667,4 +667,4 @@ class AssistantCotApplicationRunner(BaseAssistantApplicationRunner):
|
||||
try:
|
||||
return json.dumps(tools, ensure_ascii=False)
|
||||
except json.JSONDecodeError:
|
||||
return json.dumps(tools)
|
||||
return json.dumps(tools)
|
||||
|
||||
+33
-16
@@ -1,3 +1,4 @@
|
||||
import concurrent.futures
|
||||
import datetime
|
||||
import json
|
||||
import logging
|
||||
@@ -650,17 +651,44 @@ class IndexingRunner:
|
||||
# chunk nodes by chunk size
|
||||
indexing_start_at = time.perf_counter()
|
||||
tokens = 0
|
||||
chunk_size = 100
|
||||
chunk_size = 10
|
||||
|
||||
embedding_model_type_instance = None
|
||||
if embedding_model_instance:
|
||||
embedding_model_type_instance = embedding_model_instance.model_type_instance
|
||||
embedding_model_type_instance = cast(TextEmbeddingModel, embedding_model_type_instance)
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
|
||||
futures = []
|
||||
for i in range(0, len(documents), chunk_size):
|
||||
chunk_documents = documents[i:i + chunk_size]
|
||||
futures.append(executor.submit(self._process_chunk, current_app._get_current_object(), index_processor,
|
||||
chunk_documents, dataset,
|
||||
dataset_document, embedding_model_instance,
|
||||
embedding_model_type_instance))
|
||||
|
||||
for i in range(0, len(documents), chunk_size):
|
||||
for future in futures:
|
||||
tokens += future.result()
|
||||
|
||||
indexing_end_at = time.perf_counter()
|
||||
|
||||
# update document status to completed
|
||||
self._update_document_index_status(
|
||||
document_id=dataset_document.id,
|
||||
after_indexing_status="completed",
|
||||
extra_update_params={
|
||||
DatasetDocument.tokens: tokens,
|
||||
DatasetDocument.completed_at: datetime.datetime.utcnow(),
|
||||
DatasetDocument.indexing_latency: indexing_end_at - indexing_start_at,
|
||||
}
|
||||
)
|
||||
|
||||
def _process_chunk(self, flask_app, index_processor, chunk_documents, dataset, dataset_document,
|
||||
embedding_model_instance, embedding_model_type_instance):
|
||||
with flask_app.app_context():
|
||||
# check document is paused
|
||||
self._check_document_paused_status(dataset_document.id)
|
||||
chunk_documents = documents[i:i + chunk_size]
|
||||
|
||||
tokens = 0
|
||||
if dataset.indexing_technique == 'high_quality' or embedding_model_type_instance:
|
||||
tokens += sum(
|
||||
embedding_model_type_instance.get_num_tokens(
|
||||
@@ -670,9 +698,9 @@ class IndexingRunner:
|
||||
)
|
||||
for document in chunk_documents
|
||||
)
|
||||
|
||||
# load index
|
||||
index_processor.load(dataset, chunk_documents)
|
||||
db.session.add(dataset)
|
||||
|
||||
document_ids = [document.metadata['doc_id'] for document in chunk_documents]
|
||||
db.session.query(DocumentSegment).filter(
|
||||
@@ -687,18 +715,7 @@ class IndexingRunner:
|
||||
|
||||
db.session.commit()
|
||||
|
||||
indexing_end_at = time.perf_counter()
|
||||
|
||||
# update document status to completed
|
||||
self._update_document_index_status(
|
||||
document_id=dataset_document.id,
|
||||
after_indexing_status="completed",
|
||||
extra_update_params={
|
||||
DatasetDocument.tokens: tokens,
|
||||
DatasetDocument.completed_at: datetime.datetime.utcnow(),
|
||||
DatasetDocument.indexing_latency: indexing_end_at - indexing_start_at,
|
||||
}
|
||||
)
|
||||
return tokens
|
||||
|
||||
def _check_document_paused_status(self, document_id: str):
|
||||
indexing_cache_key = 'document_{}_is_paused'.format(document_id)
|
||||
|
||||
@@ -147,7 +147,7 @@
|
||||
|
||||
- `input` (float) Input price, i.e., Prompt price
|
||||
- `output` (float) Output price, i.e., returned content price
|
||||
- `unit` (float) Pricing unit, e.g., per 100K price is `0.000001`
|
||||
- `unit` (float) Pricing unit, e.g., if the price is meausred in 1M tokens, the corresponding token amount for the unit price is `0.000001`.
|
||||
- `currency` (string) Currency unit
|
||||
|
||||
### ProviderCredentialSchema
|
||||
|
||||
@@ -149,7 +149,7 @@
|
||||
|
||||
- `input` (float) 输入单价,即 Prompt 单价
|
||||
- `output` (float) 输出单价,即返回内容单价
|
||||
- `unit` (float) 价格单位,如:每 100K 的单价为 `0.000001`
|
||||
- `unit` (float) 价格单位,如以 1M tokens 计价,则单价对应的单位 token 数为 `0.000001`
|
||||
- `currency` (string) 货币单位
|
||||
|
||||
### ProviderCredentialSchema
|
||||
|
||||
@@ -73,8 +73,8 @@ PARAMETER_RULE_TEMPLATE: dict[DefaultParameterName, dict] = {
|
||||
},
|
||||
'type': 'int',
|
||||
'help': {
|
||||
'en_US': 'The maximum number of tokens to generate. Requests can use up to 2048 tokens shared between prompt and completion.',
|
||||
'zh_Hans': '要生成的标记的最大数量。请求可以使用最多2048个标记,这些标记在提示和完成之间共享。',
|
||||
'en_US': 'Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter.',
|
||||
'zh_Hans': '指定生成结果长度的上限。如果生成结果截断,可以调大该参数。',
|
||||
},
|
||||
'required': False,
|
||||
'default': 64,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import importlib
|
||||
import os
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
@@ -7,6 +6,7 @@ import yaml
|
||||
from core.model_runtime.entities.model_entities import AIModelEntity, ModelType
|
||||
from core.model_runtime.entities.provider_entities import ProviderEntity
|
||||
from core.model_runtime.model_providers.__base.ai_model import AIModel
|
||||
from core.utils.module_import_helper import get_subclasses_from_module, import_module_from_source
|
||||
|
||||
|
||||
class ModelProvider(ABC):
|
||||
@@ -104,17 +104,10 @@ class ModelProvider(ABC):
|
||||
|
||||
# Dynamic loading {model_type_name}.py file and find the subclass of AIModel
|
||||
parent_module = '.'.join(self.__class__.__module__.split('.')[:-1])
|
||||
spec = importlib.util.spec_from_file_location(f"{parent_module}.{model_type_name}.{model_type_name}", model_type_py_path)
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(mod)
|
||||
|
||||
model_class = None
|
||||
for name, obj in vars(mod).items():
|
||||
if (isinstance(obj, type) and issubclass(obj, AIModel) and not obj.__abstractmethods__
|
||||
and obj != AIModel and obj.__module__ == mod.__name__):
|
||||
model_class = obj
|
||||
break
|
||||
|
||||
mod = import_module_from_source(
|
||||
f'{parent_module}.{model_type_name}.{model_type_name}', model_type_py_path)
|
||||
model_class = next(filter(lambda x: x.__module__ == mod.__name__ and not x.__abstractmethods__,
|
||||
get_subclasses_from_module(mod, AIModel)), None)
|
||||
if not model_class:
|
||||
raise Exception(f'Missing AIModel Class for model type {model_type} in {model_type_py_path}')
|
||||
|
||||
|
||||
@@ -17,9 +17,11 @@ class BedrockProvider(ModelProvider):
|
||||
"""
|
||||
try:
|
||||
model_instance = self.get_model_instance(ModelType.LLM)
|
||||
bedrock_validate_model_name = credentials.get('model_for_validation', 'amazon.titan-text-lite-v1')
|
||||
|
||||
# Use `amazon.titan-text-lite-v1` model by default for validating credentials
|
||||
model_for_validation = credentials.get('model_for_validation', 'amazon.titan-text-lite-v1')
|
||||
model_instance.validate_credentials(
|
||||
model=bedrock_validate_model_name,
|
||||
model=model_for_validation,
|
||||
credentials=credentials
|
||||
)
|
||||
except CredentialsValidateFailedError as ex:
|
||||
|
||||
@@ -74,7 +74,7 @@ provider_credential_schema:
|
||||
label:
|
||||
en_US: Available Model Name
|
||||
zh_Hans: 可用模型名称
|
||||
type: text-input
|
||||
type: secret-input
|
||||
placeholder:
|
||||
en_US: A model you have access to (e.g. amazon.titan-text-lite-v1) for validation.
|
||||
zh_Hans: 为了进行验证,请输入一个您可用的模型名称 (例如:amazon.titan-text-lite-v1)
|
||||
|
||||
+35
-18
@@ -1,33 +1,50 @@
|
||||
model: anthropic.claude-instant-v1
|
||||
label:
|
||||
en_US: Claude Instant V1
|
||||
en_US: Claude Instant 1
|
||||
model_type: llm
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 100000
|
||||
parameter_rules:
|
||||
- name: temperature
|
||||
use_template: temperature
|
||||
- name: topP
|
||||
use_template: top_p
|
||||
- name: topK
|
||||
label:
|
||||
zh_Hans: 取样数量
|
||||
en_US: Top K
|
||||
type: int
|
||||
help:
|
||||
zh_Hans: 仅从每个后续标记的前 K 个选项中采样。
|
||||
en_US: Only sample from the top K options for each subsequent token.
|
||||
required: false
|
||||
default: 250
|
||||
min: 0
|
||||
max: 500
|
||||
- name: max_tokens_to_sample
|
||||
- name: max_tokens
|
||||
use_template: max_tokens
|
||||
required: true
|
||||
type: int
|
||||
default: 4096
|
||||
min: 1
|
||||
max: 4096
|
||||
help:
|
||||
zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。
|
||||
en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter.
|
||||
- name: temperature
|
||||
use_template: temperature
|
||||
required: false
|
||||
type: float
|
||||
default: 1
|
||||
min: 0.0
|
||||
max: 1.0
|
||||
help:
|
||||
zh_Hans: 生成内容的随机性。
|
||||
en_US: The amount of randomness injected into the response.
|
||||
- name: top_p
|
||||
required: false
|
||||
type: float
|
||||
default: 0.999
|
||||
min: 0.000
|
||||
max: 1.000
|
||||
help:
|
||||
zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。
|
||||
en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both.
|
||||
- name: top_k
|
||||
required: false
|
||||
type: int
|
||||
default: 0
|
||||
min: 0
|
||||
# tip docs from aws has error, max value is 500
|
||||
max: 500
|
||||
help:
|
||||
zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。
|
||||
en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses.
|
||||
pricing:
|
||||
input: '0.0008'
|
||||
output: '0.0024'
|
||||
|
||||
@@ -1,33 +1,50 @@
|
||||
model: anthropic.claude-v1
|
||||
label:
|
||||
en_US: Claude V1
|
||||
en_US: Claude 1
|
||||
model_type: llm
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 100000
|
||||
parameter_rules:
|
||||
- name: temperature
|
||||
use_template: temperature
|
||||
- name: top_p
|
||||
use_template: top_p
|
||||
- name: top_k
|
||||
label:
|
||||
zh_Hans: 取样数量
|
||||
en_US: Top K
|
||||
type: int
|
||||
help:
|
||||
zh_Hans: 仅从每个后续标记的前 K 个选项中采样。
|
||||
en_US: Only sample from the top K options for each subsequent token.
|
||||
required: false
|
||||
default: 250
|
||||
min: 0
|
||||
max: 500
|
||||
- name: max_tokens_to_sample
|
||||
- name: max_tokens
|
||||
use_template: max_tokens
|
||||
required: true
|
||||
type: int
|
||||
default: 4096
|
||||
min: 1
|
||||
max: 4096
|
||||
help:
|
||||
zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。
|
||||
en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter.
|
||||
- name: temperature
|
||||
use_template: temperature
|
||||
required: false
|
||||
type: float
|
||||
default: 1
|
||||
min: 0.0
|
||||
max: 1.0
|
||||
help:
|
||||
zh_Hans: 生成内容的随机性。
|
||||
en_US: The amount of randomness injected into the response.
|
||||
- name: top_p
|
||||
required: false
|
||||
type: float
|
||||
default: 0.999
|
||||
min: 0.000
|
||||
max: 1.000
|
||||
help:
|
||||
zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。
|
||||
en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both.
|
||||
- name: top_k
|
||||
required: false
|
||||
type: int
|
||||
default: 0
|
||||
min: 0
|
||||
# tip docs from aws has error, max value is 500
|
||||
max: 500
|
||||
help:
|
||||
zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。
|
||||
en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses.
|
||||
pricing:
|
||||
input: '0.008'
|
||||
output: '0.024'
|
||||
|
||||
@@ -1,33 +1,50 @@
|
||||
model: anthropic.claude-v2:1
|
||||
label:
|
||||
en_US: Claude V2.1
|
||||
en_US: Claude 2.1
|
||||
model_type: llm
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 200000
|
||||
parameter_rules:
|
||||
- name: temperature
|
||||
use_template: temperature
|
||||
- name: top_p
|
||||
use_template: top_p
|
||||
- name: top_k
|
||||
label:
|
||||
zh_Hans: 取样数量
|
||||
en_US: Top K
|
||||
type: int
|
||||
help:
|
||||
zh_Hans: 仅从每个后续标记的前 K 个选项中采样。
|
||||
en_US: Only sample from the top K options for each subsequent token.
|
||||
required: false
|
||||
default: 250
|
||||
min: 0
|
||||
max: 500
|
||||
- name: max_tokens_to_sample
|
||||
- name: max_tokens
|
||||
use_template: max_tokens
|
||||
required: true
|
||||
type: int
|
||||
default: 4096
|
||||
min: 1
|
||||
max: 4096
|
||||
help:
|
||||
zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。
|
||||
en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter.
|
||||
- name: temperature
|
||||
use_template: temperature
|
||||
required: false
|
||||
type: float
|
||||
default: 1
|
||||
min: 0.0
|
||||
max: 1.0
|
||||
help:
|
||||
zh_Hans: 生成内容的随机性。
|
||||
en_US: The amount of randomness injected into the response.
|
||||
- name: top_p
|
||||
required: false
|
||||
type: float
|
||||
default: 0.999
|
||||
min: 0.000
|
||||
max: 1.000
|
||||
help:
|
||||
zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。
|
||||
en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both.
|
||||
- name: top_k
|
||||
required: false
|
||||
type: int
|
||||
default: 0
|
||||
min: 0
|
||||
# tip docs from aws has error, max value is 500
|
||||
max: 500
|
||||
help:
|
||||
zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。
|
||||
en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses.
|
||||
pricing:
|
||||
input: '0.008'
|
||||
output: '0.024'
|
||||
|
||||
@@ -1,33 +1,50 @@
|
||||
model: anthropic.claude-v2
|
||||
label:
|
||||
en_US: Claude V2
|
||||
en_US: Claude 2
|
||||
model_type: llm
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 100000
|
||||
parameter_rules:
|
||||
- name: temperature
|
||||
use_template: temperature
|
||||
- name: top_p
|
||||
use_template: top_p
|
||||
- name: top_k
|
||||
label:
|
||||
zh_Hans: 取样数量
|
||||
en_US: Top K
|
||||
type: int
|
||||
help:
|
||||
zh_Hans: 仅从每个后续标记的前 K 个选项中采样。
|
||||
en_US: Only sample from the top K options for each subsequent token.
|
||||
required: false
|
||||
default: 250
|
||||
min: 0
|
||||
max: 500
|
||||
- name: max_tokens_to_sample
|
||||
- name: max_tokens
|
||||
use_template: max_tokens
|
||||
required: true
|
||||
type: int
|
||||
default: 4096
|
||||
min: 1
|
||||
max: 4096
|
||||
help:
|
||||
zh_Hans: 停止前生成的最大令牌数。请注意,Anthropic Claude 模型可能会在达到 max_tokens 的值之前停止生成令牌。不同的 Anthropic Claude 模型对此参数具有不同的最大值。
|
||||
en_US: The maximum number of tokens to generate before stopping. Note that Anthropic Claude models might stop generating tokens before reaching the value of max_tokens. Different Anthropic Claude models have different maximum values for this parameter.
|
||||
- name: temperature
|
||||
use_template: temperature
|
||||
required: false
|
||||
type: float
|
||||
default: 1
|
||||
min: 0.0
|
||||
max: 1.0
|
||||
help:
|
||||
zh_Hans: 生成内容的随机性。
|
||||
en_US: The amount of randomness injected into the response.
|
||||
- name: top_p
|
||||
required: false
|
||||
type: float
|
||||
default: 0.999
|
||||
min: 0.000
|
||||
max: 1.000
|
||||
help:
|
||||
zh_Hans: 在核采样中,Anthropic Claude 按概率递减顺序计算每个后续标记的所有选项的累积分布,并在达到 top_p 指定的特定概率时将其切断。您应该更改温度或top_p,但不能同时更改两者。
|
||||
en_US: In nucleus sampling, Anthropic Claude computes the cumulative distribution over all the options for each subsequent token in decreasing probability order and cuts it off once it reaches a particular probability specified by top_p. You should alter either temperature or top_p, but not both.
|
||||
- name: top_k
|
||||
required: false
|
||||
type: int
|
||||
default: 0
|
||||
min: 0
|
||||
# tip docs from aws has error, max value is 500
|
||||
max: 500
|
||||
help:
|
||||
zh_Hans: 对于每个后续标记,仅从前 K 个选项中进行采样。使用 top_k 删除长尾低概率响应。
|
||||
en_US: Only sample from the top K options for each subsequent token. Use top_k to remove long tail low probability responses.
|
||||
pricing:
|
||||
input: '0.008'
|
||||
output: '0.024'
|
||||
|
||||
@@ -72,16 +72,16 @@ class BedrockLargeLanguageModel(LargeLanguageModel):
|
||||
:return: full response or stream response chunk generator result
|
||||
"""
|
||||
|
||||
# invoke claude 3 models via anthropic official SDK
|
||||
if "anthropic.claude-3" in model:
|
||||
return self._invoke_claude3(model, credentials, prompt_messages, model_parameters, stop, stream, user)
|
||||
# invoke model
|
||||
# invoke anthropic models via anthropic official SDK
|
||||
if "anthropic" in model:
|
||||
return self._generate_anthropic(model, credentials, prompt_messages, model_parameters, stop, stream, user)
|
||||
# invoke other models via boto3 client
|
||||
return self._generate(model, credentials, prompt_messages, model_parameters, stop, stream, user)
|
||||
|
||||
def _invoke_claude3(self, model: str, credentials: dict, prompt_messages: list[PromptMessage], model_parameters: dict,
|
||||
def _generate_anthropic(self, model: str, credentials: dict, prompt_messages: list[PromptMessage], model_parameters: dict,
|
||||
stop: Optional[list[str]] = None, stream: bool = True, user: Optional[str] = None) -> Union[LLMResult, Generator]:
|
||||
"""
|
||||
Invoke Claude3 large language model
|
||||
Invoke Anthropic large language model
|
||||
|
||||
:param model: model name
|
||||
:param credentials: model credentials
|
||||
@@ -114,7 +114,7 @@ class BedrockLargeLanguageModel(LargeLanguageModel):
|
||||
# ref: https://github.com/anthropics/anthropic-sdk-python/blob/e84645b07ca5267066700a104b4d8d6a8da1383d/src/anthropic/resources/messages.py#L465
|
||||
# extra_model_kwargs['metadata'] = message_create_params.Metadata(user_id=user)
|
||||
|
||||
system, prompt_message_dicts = self._convert_claude3_prompt_messages(prompt_messages)
|
||||
system, prompt_message_dicts = self._convert_claude_prompt_messages(prompt_messages)
|
||||
|
||||
if system:
|
||||
extra_model_kwargs['system'] = system
|
||||
@@ -128,11 +128,11 @@ class BedrockLargeLanguageModel(LargeLanguageModel):
|
||||
)
|
||||
|
||||
if stream:
|
||||
return self._handle_claude3_stream_response(model, credentials, response, prompt_messages)
|
||||
return self._handle_claude_stream_response(model, credentials, response, prompt_messages)
|
||||
|
||||
return self._handle_claude3_response(model, credentials, response, prompt_messages)
|
||||
return self._handle_claude_response(model, credentials, response, prompt_messages)
|
||||
|
||||
def _handle_claude3_response(self, model: str, credentials: dict, response: Message,
|
||||
def _handle_claude_response(self, model: str, credentials: dict, response: Message,
|
||||
prompt_messages: list[PromptMessage]) -> LLMResult:
|
||||
"""
|
||||
Handle llm chat response
|
||||
@@ -172,7 +172,7 @@ class BedrockLargeLanguageModel(LargeLanguageModel):
|
||||
|
||||
return response
|
||||
|
||||
def _handle_claude3_stream_response(self, model: str, credentials: dict, response: Stream[MessageStreamEvent],
|
||||
def _handle_claude_stream_response(self, model: str, credentials: dict, response: Stream[MessageStreamEvent],
|
||||
prompt_messages: list[PromptMessage], ) -> Generator:
|
||||
"""
|
||||
Handle llm chat stream response
|
||||
@@ -231,7 +231,7 @@ class BedrockLargeLanguageModel(LargeLanguageModel):
|
||||
except Exception as ex:
|
||||
raise InvokeError(str(ex))
|
||||
|
||||
def _calc_claude3_response_usage(self, model: str, credentials: dict, prompt_tokens: int, completion_tokens: int) -> LLMUsage:
|
||||
def _calc_claude_response_usage(self, model: str, credentials: dict, prompt_tokens: int, completion_tokens: int) -> LLMUsage:
|
||||
"""
|
||||
Calculate response usage
|
||||
|
||||
@@ -275,7 +275,7 @@ class BedrockLargeLanguageModel(LargeLanguageModel):
|
||||
|
||||
return usage
|
||||
|
||||
def _convert_claude3_prompt_messages(self, prompt_messages: list[PromptMessage]) -> tuple[str, list[dict]]:
|
||||
def _convert_claude_prompt_messages(self, prompt_messages: list[PromptMessage]) -> tuple[str, list[dict]]:
|
||||
"""
|
||||
Convert prompt messages to dict list and system
|
||||
"""
|
||||
@@ -295,11 +295,11 @@ class BedrockLargeLanguageModel(LargeLanguageModel):
|
||||
prompt_message_dicts = []
|
||||
for message in prompt_messages:
|
||||
if not isinstance(message, SystemPromptMessage):
|
||||
prompt_message_dicts.append(self._convert_claude3_prompt_message_to_dict(message))
|
||||
prompt_message_dicts.append(self._convert_claude_prompt_message_to_dict(message))
|
||||
|
||||
return system, prompt_message_dicts
|
||||
|
||||
def _convert_claude3_prompt_message_to_dict(self, message: PromptMessage) -> dict:
|
||||
def _convert_claude_prompt_message_to_dict(self, message: PromptMessage) -> dict:
|
||||
"""
|
||||
Convert PromptMessage to dict
|
||||
"""
|
||||
@@ -405,7 +405,7 @@ class BedrockLargeLanguageModel(LargeLanguageModel):
|
||||
|
||||
if "anthropic.claude-3" in model:
|
||||
try:
|
||||
self._invoke_claude3(model=model,
|
||||
self._invoke_claude(model=model,
|
||||
credentials=credentials,
|
||||
prompt_messages=[{"role": "user", "content": "ping"}],
|
||||
model_parameters={},
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
model: jina-colbert-v1-en
|
||||
model_type: rerank
|
||||
model_properties:
|
||||
context_size: 8192
|
||||
@@ -1,4 +1,3 @@
|
||||
import importlib
|
||||
import logging
|
||||
import os
|
||||
from typing import Optional
|
||||
@@ -10,6 +9,7 @@ from core.model_runtime.entities.provider_entities import ProviderConfig, Provid
|
||||
from core.model_runtime.model_providers.__base.model_provider import ModelProvider
|
||||
from core.model_runtime.schema_validators.model_credential_schema_validator import ModelCredentialSchemaValidator
|
||||
from core.model_runtime.schema_validators.provider_credential_schema_validator import ProviderCredentialSchemaValidator
|
||||
from core.utils.module_import_helper import load_single_subclass_from_source
|
||||
from core.utils.position_helper import get_position_map, sort_to_dict_by_position_map
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -229,15 +229,10 @@ class ModelProviderFactory:
|
||||
|
||||
# Dynamic loading {model_provider_name}.py file and find the subclass of ModelProvider
|
||||
py_path = os.path.join(model_provider_dir_path, model_provider_name + '.py')
|
||||
spec = importlib.util.spec_from_file_location(f'core.model_runtime.model_providers.{model_provider_name}.{model_provider_name}', py_path)
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(mod)
|
||||
|
||||
model_provider_class = None
|
||||
for name, obj in vars(mod).items():
|
||||
if isinstance(obj, type) and issubclass(obj, ModelProvider) and obj != ModelProvider:
|
||||
model_provider_class = obj
|
||||
break
|
||||
model_provider_class = load_single_subclass_from_source(
|
||||
module_name=f'core.model_runtime.model_providers.{model_provider_name}.{model_provider_name}',
|
||||
script_path=py_path,
|
||||
parent_type=ModelProvider)
|
||||
|
||||
if not model_provider_class:
|
||||
logger.warning(f"Missing Model Provider Class that extends ModelProvider in {py_path}, Skip.")
|
||||
|
||||
@@ -148,7 +148,8 @@ class SparkLLMClient:
|
||||
data = {
|
||||
"header": {
|
||||
"app_id": self.app_id,
|
||||
"uid": user_id
|
||||
# resolve this error message => $.header.uid' length must be less or equal than 32
|
||||
"uid": user_id[:32] if user_id else None
|
||||
},
|
||||
"parameter": {
|
||||
"chat": {
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
model: ernie-3.5-8k
|
||||
label:
|
||||
en_US: Ernie-3.5-8K
|
||||
model_type: llm
|
||||
features:
|
||||
- agent-thought
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 4096
|
||||
parameter_rules:
|
||||
- name: temperature
|
||||
use_template: temperature
|
||||
min: 0.1
|
||||
max: 1.0
|
||||
default: 0.8
|
||||
- name: top_p
|
||||
use_template: top_p
|
||||
- name: max_tokens
|
||||
use_template: max_tokens
|
||||
default: 1024
|
||||
min: 2
|
||||
max: 2048
|
||||
- name: presence_penalty
|
||||
use_template: presence_penalty
|
||||
- name: frequency_penalty
|
||||
use_template: frequency_penalty
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
- name: disable_search
|
||||
label:
|
||||
zh_Hans: 禁用搜索
|
||||
en_US: Disable Search
|
||||
type: boolean
|
||||
help:
|
||||
zh_Hans: 禁用模型自行进行外部搜索。
|
||||
en_US: Disable the model to perform external search.
|
||||
required: false
|
||||
@@ -0,0 +1,37 @@
|
||||
model: ernie-3.5-8k-0205
|
||||
label:
|
||||
en_US: Ernie-3.5-8K-0205
|
||||
model_type: llm
|
||||
features:
|
||||
- agent-thought
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 8192
|
||||
parameter_rules:
|
||||
- name: temperature
|
||||
use_template: temperature
|
||||
min: 0.1
|
||||
max: 1.0
|
||||
default: 0.8
|
||||
- name: top_p
|
||||
use_template: top_p
|
||||
- name: max_tokens
|
||||
use_template: max_tokens
|
||||
default: 1024
|
||||
min: 2
|
||||
max: 2048
|
||||
- name: presence_penalty
|
||||
use_template: presence_penalty
|
||||
- name: frequency_penalty
|
||||
use_template: frequency_penalty
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
- name: disable_search
|
||||
label:
|
||||
zh_Hans: 禁用搜索
|
||||
en_US: Disable Search
|
||||
type: boolean
|
||||
help:
|
||||
zh_Hans: 禁用模型自行进行外部搜索。
|
||||
en_US: Disable the model to perform external search.
|
||||
required: false
|
||||
@@ -0,0 +1,37 @@
|
||||
model: ernie-3.5-8k-1222
|
||||
label:
|
||||
en_US: Ernie-3.5-8K-1222
|
||||
model_type: llm
|
||||
features:
|
||||
- agent-thought
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 8192
|
||||
parameter_rules:
|
||||
- name: temperature
|
||||
use_template: temperature
|
||||
min: 0.1
|
||||
max: 1.0
|
||||
default: 0.8
|
||||
- name: top_p
|
||||
use_template: top_p
|
||||
- name: max_tokens
|
||||
use_template: max_tokens
|
||||
default: 1024
|
||||
min: 2
|
||||
max: 2048
|
||||
- name: presence_penalty
|
||||
use_template: presence_penalty
|
||||
- name: frequency_penalty
|
||||
use_template: frequency_penalty
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
- name: disable_search
|
||||
label:
|
||||
zh_Hans: 禁用搜索
|
||||
en_US: Disable Search
|
||||
type: boolean
|
||||
help:
|
||||
zh_Hans: 禁用模型自行进行外部搜索。
|
||||
en_US: Disable the model to perform external search.
|
||||
required: false
|
||||
@@ -0,0 +1,37 @@
|
||||
model: ernie-3.5-8k
|
||||
label:
|
||||
en_US: Ernie-3.5-8K
|
||||
model_type: llm
|
||||
features:
|
||||
- agent-thought
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 8192
|
||||
parameter_rules:
|
||||
- name: temperature
|
||||
use_template: temperature
|
||||
min: 0.1
|
||||
max: 1.0
|
||||
default: 0.8
|
||||
- name: top_p
|
||||
use_template: top_p
|
||||
- name: max_tokens
|
||||
use_template: max_tokens
|
||||
default: 1024
|
||||
min: 2
|
||||
max: 2048
|
||||
- name: presence_penalty
|
||||
use_template: presence_penalty
|
||||
- name: frequency_penalty
|
||||
use_template: frequency_penalty
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
- name: disable_search
|
||||
label:
|
||||
zh_Hans: 禁用搜索
|
||||
en_US: Disable Search
|
||||
type: boolean
|
||||
help:
|
||||
zh_Hans: 禁用模型自行进行外部搜索。
|
||||
en_US: Disable the model to perform external search.
|
||||
required: false
|
||||
@@ -0,0 +1,37 @@
|
||||
model: ernie-4.0-8k
|
||||
label:
|
||||
en_US: Ernie-4.0-8K
|
||||
model_type: llm
|
||||
features:
|
||||
- agent-thought
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 8192
|
||||
parameter_rules:
|
||||
- name: temperature
|
||||
use_template: temperature
|
||||
min: 0.1
|
||||
max: 1.0
|
||||
default: 0.8
|
||||
- name: top_p
|
||||
use_template: top_p
|
||||
- name: max_tokens
|
||||
use_template: max_tokens
|
||||
default: 1024
|
||||
min: 2
|
||||
max: 2048
|
||||
- name: presence_penalty
|
||||
use_template: presence_penalty
|
||||
- name: frequency_penalty
|
||||
use_template: frequency_penalty
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
- name: disable_search
|
||||
label:
|
||||
zh_Hans: 禁用搜索
|
||||
en_US: Disable Search
|
||||
type: boolean
|
||||
help:
|
||||
zh_Hans: 禁用模型自行进行外部搜索。
|
||||
en_US: Disable the model to perform external search.
|
||||
required: false
|
||||
@@ -36,3 +36,4 @@ parameter_rules:
|
||||
zh_Hans: 禁用模型自行进行外部搜索。
|
||||
en_US: Disable the model to perform external search.
|
||||
required: false
|
||||
deprecated: true
|
||||
|
||||
@@ -36,3 +36,4 @@ parameter_rules:
|
||||
zh_Hans: 禁用模型自行进行外部搜索。
|
||||
en_US: Disable the model to perform external search.
|
||||
required: false
|
||||
deprecated: true
|
||||
|
||||
@@ -27,3 +27,4 @@ parameter_rules:
|
||||
use_template: frequency_penalty
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
deprecated: true
|
||||
|
||||
@@ -36,3 +36,4 @@ parameter_rules:
|
||||
required: false
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
deprecated: true
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
model: ernie-lite-8k-0308
|
||||
label:
|
||||
en_US: ERNIE-Lite-8K-0308
|
||||
model_type: llm
|
||||
features:
|
||||
- agent-thought
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 8192
|
||||
parameter_rules:
|
||||
- name: temperature
|
||||
use_template: temperature
|
||||
min: 0.1
|
||||
max: 1.0
|
||||
default: 0.95
|
||||
- name: top_p
|
||||
use_template: top_p
|
||||
min: 0
|
||||
max: 1.0
|
||||
default: 0.7
|
||||
- name: max_tokens
|
||||
use_template: max_tokens
|
||||
default: 1024
|
||||
min: 2
|
||||
max: 1024
|
||||
- name: presence_penalty
|
||||
use_template: presence_penalty
|
||||
default: 1.0
|
||||
min: 1.0
|
||||
max: 2.0
|
||||
@@ -0,0 +1,30 @@
|
||||
model: ernie-lite-8k-0922
|
||||
label:
|
||||
en_US: ERNIE-Lite-8K-0922
|
||||
model_type: llm
|
||||
features:
|
||||
- agent-thought
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 8192
|
||||
parameter_rules:
|
||||
- name: temperature
|
||||
use_template: temperature
|
||||
min: 0.1
|
||||
max: 1.0
|
||||
default: 0.95
|
||||
- name: top_p
|
||||
use_template: top_p
|
||||
min: 0
|
||||
max: 1.0
|
||||
default: 0.7
|
||||
- name: max_tokens
|
||||
use_template: max_tokens
|
||||
default: 1024
|
||||
min: 2
|
||||
max: 1024
|
||||
- name: presence_penalty
|
||||
use_template: presence_penalty
|
||||
default: 1.0
|
||||
min: 1.0
|
||||
max: 2.0
|
||||
@@ -0,0 +1,30 @@
|
||||
model: ernie-speed-128k
|
||||
label:
|
||||
en_US: ERNIE-Speed-128K
|
||||
model_type: llm
|
||||
features:
|
||||
- agent-thought
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 128000
|
||||
parameter_rules:
|
||||
- name: temperature
|
||||
use_template: temperature
|
||||
min: 0.1
|
||||
max: 1.0
|
||||
default: 0.95
|
||||
- name: top_p
|
||||
use_template: top_p
|
||||
min: 0
|
||||
max: 1.0
|
||||
default: 0.7
|
||||
- name: max_tokens
|
||||
use_template: max_tokens
|
||||
default: 1024
|
||||
min: 2
|
||||
max: 1024
|
||||
- name: presence_penalty
|
||||
use_template: presence_penalty
|
||||
default: 1.0
|
||||
min: 1.0
|
||||
max: 2.0
|
||||
@@ -0,0 +1,30 @@
|
||||
model: ernie-speed-8k
|
||||
label:
|
||||
en_US: ERNIE-Speed-8K
|
||||
model_type: llm
|
||||
features:
|
||||
- agent-thought
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 8192
|
||||
parameter_rules:
|
||||
- name: temperature
|
||||
use_template: temperature
|
||||
min: 0.1
|
||||
max: 1.0
|
||||
default: 0.95
|
||||
- name: top_p
|
||||
use_template: top_p
|
||||
min: 0
|
||||
max: 1.0
|
||||
default: 0.7
|
||||
- name: max_tokens
|
||||
use_template: max_tokens
|
||||
default: 1024
|
||||
min: 2
|
||||
max: 1024
|
||||
- name: presence_penalty
|
||||
use_template: presence_penalty
|
||||
default: 1.0
|
||||
min: 1.0
|
||||
max: 2.0
|
||||
@@ -0,0 +1,25 @@
|
||||
model: ernie-speed-appbuilder
|
||||
label:
|
||||
en_US: ERNIE-Speed-AppBuilder
|
||||
model_type: llm
|
||||
features:
|
||||
- agent-thought
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 8192
|
||||
parameter_rules:
|
||||
- name: temperature
|
||||
use_template: temperature
|
||||
min: 0.1
|
||||
max: 1.0
|
||||
default: 0.95
|
||||
- name: top_p
|
||||
use_template: top_p
|
||||
min: 0
|
||||
max: 1.0
|
||||
default: 0.7
|
||||
- name: presence_penalty
|
||||
use_template: presence_penalty
|
||||
default: 1.0
|
||||
min: 1.0
|
||||
max: 2.0
|
||||
@@ -121,15 +121,29 @@ class ErnieMessage:
|
||||
|
||||
class ErnieBotModel:
|
||||
api_bases = {
|
||||
'ernie-bot': 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions',
|
||||
'ernie-bot': 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-3.5-4k-0205',
|
||||
'ernie-bot-4': 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro',
|
||||
'ernie-bot-8k': 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie_bot_8k',
|
||||
'ernie-bot-8k': 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions',
|
||||
'ernie-bot-turbo': 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant',
|
||||
'ernie-3.5-8k': 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions',
|
||||
'ernie-3.5-8k-0205': 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-3.5-8k-0205',
|
||||
'ernie-3.5-8k-1222': 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-3.5-8k-1222',
|
||||
'ernie-3.5-4k-0205': 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-3.5-4k-0205',
|
||||
'ernie-4.0-8k': 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro',
|
||||
'ernie-speed-8k': 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie_speed',
|
||||
'ernie-speed-128k': 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-speed-128k',
|
||||
'ernie-speed-appbuilder': 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ai_apaas',
|
||||
'ernie-lite-8k-0922': 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant',
|
||||
'ernie-lite-8k-0308': 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-lite-8k',
|
||||
}
|
||||
|
||||
function_calling_supports = [
|
||||
'ernie-bot',
|
||||
'ernie-bot-8k',
|
||||
'ernie-3.5-8k',
|
||||
'ernie-3.5-8k-0205',
|
||||
'ernie-3.5-8k-1222',
|
||||
'ernie-3.5-4k-0205'
|
||||
]
|
||||
|
||||
api_key: str = ''
|
||||
@@ -285,6 +299,12 @@ class ErnieBotModel:
|
||||
**parameters
|
||||
}
|
||||
|
||||
if 'max_tokens' in parameters and type(parameters['max_tokens']) == int:
|
||||
body['max_output_tokens'] = parameters['max_tokens']
|
||||
|
||||
if 'presence_penalty' in parameters and type(parameters['presence_penalty']) == float:
|
||||
body['penalty_score'] = parameters['presence_penalty']
|
||||
|
||||
if system_message:
|
||||
body['system'] = system_message
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ parameter_rules:
|
||||
zh_Hans: 控制生成结果的随机性。数值越小,随机性越弱;数值越大,随机性越强。一般而言,top_p 和 temperature 两个参数选择一个进行调整即可。
|
||||
en_US: Control the randomness of generated results. The smaller the value, the weaker the randomness; the larger the value, the stronger the randomness. Generally speaking, you can adjust one of the two parameters top_p and temperature.
|
||||
pricing:
|
||||
input: '0.0025'
|
||||
output: '0.0025'
|
||||
unit: '0.00001'
|
||||
input: '2.5'
|
||||
output: '2.5'
|
||||
unit: '0.000001'
|
||||
currency: RMB
|
||||
|
||||
@@ -37,7 +37,7 @@ parameter_rules:
|
||||
zh_Hans: 控制生成结果的随机性。数值越小,随机性越弱;数值越大,随机性越强。一般而言,top_p 和 temperature 两个参数选择一个进行调整即可。
|
||||
en_US: Control the randomness of generated results. The smaller the value, the weaker the randomness; the larger the value, the stronger the randomness. Generally speaking, you can adjust one of the two parameters top_p and temperature.
|
||||
pricing:
|
||||
input: '0.012'
|
||||
output: '0.012'
|
||||
unit: '0.00001'
|
||||
input: '12'
|
||||
output: '12'
|
||||
unit: '0.000001'
|
||||
currency: RMB
|
||||
|
||||
@@ -37,7 +37,7 @@ parameter_rules:
|
||||
zh_Hans: 控制生成结果的随机性。数值越小,随机性越弱;数值越大,随机性越强。一般而言,top_p 和 temperature 两个参数选择一个进行调整即可。
|
||||
en_US: Control the randomness of generated results. The smaller the value, the weaker the randomness; the larger the value, the stronger the randomness. Generally speaking, you can adjust one of the two parameters top_p and temperature.
|
||||
pricing:
|
||||
input: '0.01'
|
||||
output: '0.03'
|
||||
unit: '0.001'
|
||||
currency: USD
|
||||
input: '6'
|
||||
output: '6'
|
||||
unit: '0.000001'
|
||||
currency: RMB
|
||||
|
||||
@@ -5,6 +5,7 @@ from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk,
|
||||
from core.model_runtime.entities.message_entities import (
|
||||
AssistantPromptMessage,
|
||||
PromptMessage,
|
||||
PromptMessageContent,
|
||||
PromptMessageContentType,
|
||||
PromptMessageRole,
|
||||
PromptMessageTool,
|
||||
@@ -31,6 +32,7 @@ And you should always end the block with a "```" to indicate the end of the JSON
|
||||
|
||||
```JSON"""
|
||||
|
||||
|
||||
class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel):
|
||||
|
||||
def _invoke(self, model: str, credentials: dict,
|
||||
@@ -159,7 +161,7 @@ class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel):
|
||||
|
||||
if len(prompt_messages) == 0:
|
||||
raise ValueError('At least one message is required')
|
||||
|
||||
|
||||
if prompt_messages[0].role == PromptMessageRole.SYSTEM:
|
||||
if not prompt_messages[0].content:
|
||||
prompt_messages = prompt_messages[1:]
|
||||
@@ -185,7 +187,7 @@ class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel):
|
||||
continue
|
||||
|
||||
if new_prompt_messages and new_prompt_messages[-1].role == PromptMessageRole.USER and \
|
||||
copy_prompt_message.role == PromptMessageRole.USER:
|
||||
copy_prompt_message.role == PromptMessageRole.USER:
|
||||
new_prompt_messages[-1].content += "\n\n" + copy_prompt_message.content
|
||||
else:
|
||||
if copy_prompt_message.role == PromptMessageRole.USER:
|
||||
@@ -205,31 +207,7 @@ class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel):
|
||||
new_prompt_messages.append(copy_prompt_message)
|
||||
|
||||
if model == 'glm-4v':
|
||||
params = {
|
||||
'model': model,
|
||||
'messages': [{
|
||||
'role': prompt_message.role.value,
|
||||
'content':
|
||||
[
|
||||
{
|
||||
'type': 'text',
|
||||
'text': prompt_message.content
|
||||
}
|
||||
] if isinstance(prompt_message.content, str) else
|
||||
[
|
||||
{
|
||||
'type': 'image',
|
||||
'image_url': {
|
||||
'url': content.data
|
||||
}
|
||||
} if content.type == PromptMessageContentType.IMAGE else {
|
||||
'type': 'text',
|
||||
'text': content.data
|
||||
} for content in prompt_message.content
|
||||
],
|
||||
} for prompt_message in new_prompt_messages],
|
||||
**model_parameters
|
||||
}
|
||||
params = self._construct_glm_4v_parameter(model, new_prompt_messages, model_parameters)
|
||||
else:
|
||||
params = {
|
||||
'model': model,
|
||||
@@ -277,8 +255,8 @@ class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel):
|
||||
for prompt_message in new_prompt_messages:
|
||||
# merge system message to user message
|
||||
if prompt_message.role == PromptMessageRole.SYSTEM or \
|
||||
prompt_message.role == PromptMessageRole.TOOL or \
|
||||
prompt_message.role == PromptMessageRole.USER:
|
||||
prompt_message.role == PromptMessageRole.TOOL or \
|
||||
prompt_message.role == PromptMessageRole.USER:
|
||||
if len(params['messages']) > 0 and params['messages'][-1]['role'] == 'user':
|
||||
params['messages'][-1]['content'] += "\n\n" + prompt_message.content
|
||||
else:
|
||||
@@ -306,8 +284,44 @@ class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel):
|
||||
|
||||
response = client.chat.completions.create(**params, **extra_model_kwargs)
|
||||
return self._handle_generate_response(model, credentials_kwargs, tools, response, prompt_messages)
|
||||
|
||||
def _handle_generate_response(self, model: str,
|
||||
|
||||
def _construct_glm_4v_parameter(self, model: str, prompt_messages: list[PromptMessage],
|
||||
model_parameters: dict):
|
||||
messages = [
|
||||
{
|
||||
'role': message.role.value,
|
||||
'content': self._construct_glm_4v_messages(message.content)
|
||||
}
|
||||
for message in prompt_messages
|
||||
]
|
||||
|
||||
params = {
|
||||
'model': model,
|
||||
'messages': messages,
|
||||
**model_parameters
|
||||
}
|
||||
|
||||
return params
|
||||
|
||||
def _construct_glm_4v_messages(self, prompt_message: Union[str | list[PromptMessageContent]]) -> list[dict]:
|
||||
if isinstance(prompt_message, str):
|
||||
return [{'type': 'text', 'text': prompt_message}]
|
||||
|
||||
return [
|
||||
{'type': 'image_url', 'image_url': {'url': self._remove_image_header(item.data)}}
|
||||
if item.type == PromptMessageContentType.IMAGE else
|
||||
{'type': 'text', 'text': item.data}
|
||||
|
||||
for item in prompt_message
|
||||
]
|
||||
|
||||
def _remove_image_header(self, image: str) -> str:
|
||||
if image.startswith('data:image'):
|
||||
return image.split(',')[1]
|
||||
|
||||
return image
|
||||
|
||||
def _handle_generate_response(self, model: str,
|
||||
credentials: dict,
|
||||
tools: Optional[list[PromptMessageTool]],
|
||||
response: Completion,
|
||||
@@ -338,7 +352,7 @@ class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel):
|
||||
)
|
||||
|
||||
text += choice.message.content or ''
|
||||
|
||||
|
||||
prompt_usage = response.usage.prompt_tokens
|
||||
completion_usage = response.usage.completion_tokens
|
||||
|
||||
@@ -358,7 +372,7 @@ class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel):
|
||||
|
||||
return result
|
||||
|
||||
def _handle_generate_stream_response(self, model: str,
|
||||
def _handle_generate_stream_response(self, model: str,
|
||||
credentials: dict,
|
||||
tools: Optional[list[PromptMessageTool]],
|
||||
responses: Generator[ChatCompletionChunk, None, None],
|
||||
@@ -380,7 +394,7 @@ class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel):
|
||||
|
||||
if delta.finish_reason is None and (delta.delta.content is None or delta.delta.content == ''):
|
||||
continue
|
||||
|
||||
|
||||
assistant_tool_calls: list[AssistantPromptMessage.ToolCall] = []
|
||||
for tool_call in delta.delta.tool_calls or []:
|
||||
if tool_call.type == 'function':
|
||||
@@ -454,8 +468,8 @@ class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel):
|
||||
|
||||
return message_text
|
||||
|
||||
|
||||
def _convert_messages_to_prompt(self, messages: list[PromptMessage], tools: Optional[list[PromptMessageTool]] = None) -> str:
|
||||
def _convert_messages_to_prompt(self, messages: list[PromptMessage],
|
||||
tools: Optional[list[PromptMessageTool]] = None) -> str:
|
||||
"""
|
||||
:param messages: List of PromptMessage to combine.
|
||||
:return: Combined string with necessary human_prompt and ai_prompt tags.
|
||||
@@ -473,4 +487,4 @@ class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel):
|
||||
text += f"\n{tool.json()}"
|
||||
|
||||
# trim off the trailing ' ' that might come from the "Assistant: "
|
||||
return text.rstrip()
|
||||
return text.rstrip()
|
||||
|
||||
@@ -144,6 +144,16 @@ class MilvusVector(BaseVector):
|
||||
utility.drop_collection(self._collection_name, None, using=alias)
|
||||
|
||||
def text_exists(self, id: str) -> bool:
|
||||
alias = uuid4().hex
|
||||
if self._client_config.secure:
|
||||
uri = "https://" + str(self._client_config.host) + ":" + str(self._client_config.port)
|
||||
else:
|
||||
uri = "http://" + str(self._client_config.host) + ":" + str(self._client_config.port)
|
||||
connections.connect(alias=alias, uri=uri, user=self._client_config.user, password=self._client_config.password)
|
||||
|
||||
from pymilvus import utility
|
||||
if not utility.has_collection(self._collection_name, using=alias):
|
||||
return False
|
||||
|
||||
result = self._client.query(collection_name=self._collection_name,
|
||||
filter=f'metadata["doc_id"] == "{id}"',
|
||||
|
||||
@@ -275,6 +275,13 @@ class QdrantVector(BaseVector):
|
||||
)
|
||||
|
||||
def text_exists(self, id: str) -> bool:
|
||||
all_collection_name = []
|
||||
collections_response = self._client.get_collections()
|
||||
collection_list = collections_response.collections
|
||||
for collection in collection_list:
|
||||
all_collection_name.append(collection.name)
|
||||
if self._collection_name not in all_collection_name:
|
||||
return False
|
||||
response = self._client.retrieve(
|
||||
collection_name=self._collection_name,
|
||||
ids=[id]
|
||||
|
||||
@@ -128,8 +128,8 @@ class Vector:
|
||||
if kwargs.get('duplicate_check', False):
|
||||
documents = self._filter_duplicate_texts(documents)
|
||||
embeddings = self._embeddings.embed_documents([document.page_content for document in documents])
|
||||
self._vector_processor.add_texts(
|
||||
documents=documents,
|
||||
self._vector_processor.create(
|
||||
texts=documents,
|
||||
embeddings=embeddings,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
@@ -134,6 +134,11 @@ class WeaviateVector(BaseVector):
|
||||
|
||||
def text_exists(self, id: str) -> bool:
|
||||
collection_name = self._collection_name
|
||||
schema = self._default_schema(self._collection_name)
|
||||
|
||||
# check whether the index already exists
|
||||
if not self._client.schema.contains(schema):
|
||||
return False
|
||||
result = self._client.query.get(collection_name).with_additional(["id"]).with_where({
|
||||
"path": ["doc_id"],
|
||||
"operator": "Equal",
|
||||
|
||||
@@ -53,7 +53,7 @@ class UnstructuredWordExtractor(BaseExtractor):
|
||||
elements = partition_docx(filename=self._file_path)
|
||||
|
||||
from unstructured.chunking.title import chunk_by_title
|
||||
chunks = chunk_by_title(elements, max_characters=2000, combine_text_under_n_chars=0)
|
||||
chunks = chunk_by_title(elements, max_characters=2000, combine_text_under_n_chars=2000)
|
||||
documents = []
|
||||
for chunk in chunks:
|
||||
text = chunk.text.strip()
|
||||
|
||||
@@ -43,7 +43,7 @@ class UnstructuredEmailExtractor(BaseExtractor):
|
||||
pass
|
||||
|
||||
from unstructured.chunking.title import chunk_by_title
|
||||
chunks = chunk_by_title(elements, max_characters=2000, combine_text_under_n_chars=0)
|
||||
chunks = chunk_by_title(elements, max_characters=2000, combine_text_under_n_chars=2000)
|
||||
documents = []
|
||||
for chunk in chunks:
|
||||
text = chunk.text.strip()
|
||||
|
||||
@@ -38,7 +38,7 @@ class UnstructuredMarkdownExtractor(BaseExtractor):
|
||||
|
||||
elements = partition_md(filename=self._file_path, api_url=self._api_url)
|
||||
from unstructured.chunking.title import chunk_by_title
|
||||
chunks = chunk_by_title(elements, max_characters=2000, combine_text_under_n_chars=0)
|
||||
chunks = chunk_by_title(elements, max_characters=2000, combine_text_under_n_chars=2000)
|
||||
documents = []
|
||||
for chunk in chunks:
|
||||
text = chunk.text.strip()
|
||||
|
||||
@@ -28,7 +28,7 @@ class UnstructuredMsgExtractor(BaseExtractor):
|
||||
|
||||
elements = partition_msg(filename=self._file_path, api_url=self._api_url)
|
||||
from unstructured.chunking.title import chunk_by_title
|
||||
chunks = chunk_by_title(elements, max_characters=2000, combine_text_under_n_chars=0)
|
||||
chunks = chunk_by_title(elements, max_characters=2000, combine_text_under_n_chars=2000)
|
||||
documents = []
|
||||
for chunk in chunks:
|
||||
text = chunk.text.strip()
|
||||
|
||||
@@ -28,7 +28,7 @@ class UnstructuredTextExtractor(BaseExtractor):
|
||||
|
||||
elements = partition_text(filename=self._file_path, api_url=self._api_url)
|
||||
from unstructured.chunking.title import chunk_by_title
|
||||
chunks = chunk_by_title(elements, max_characters=2000, combine_text_under_n_chars=0)
|
||||
chunks = chunk_by_title(elements, max_characters=2000, combine_text_under_n_chars=2000)
|
||||
documents = []
|
||||
for chunk in chunks:
|
||||
text = chunk.text.strip()
|
||||
|
||||
@@ -28,7 +28,7 @@ class UnstructuredXmlExtractor(BaseExtractor):
|
||||
|
||||
elements = partition_xml(filename=self._file_path, xml_keep_tags=True, api_url=self._api_url)
|
||||
from unstructured.chunking.title import chunk_by_title
|
||||
chunks = chunk_by_title(elements, max_characters=2000, combine_text_under_n_chars=0)
|
||||
chunks = chunk_by_title(elements, max_characters=2000, combine_text_under_n_chars=2000)
|
||||
documents = []
|
||||
for chunk in chunks:
|
||||
text = chunk.text.strip()
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.6111 1.5837C17.2678 1.34703 18.75 2.63255 18.75 4.30606V5.68256C19.9395 6.31131 20.75 7.56102 20.75 9.00004V19C20.75 21.0711 19.0711 22.75 17 22.75H7C4.92893 22.75 3.25 21.0711 3.25 19V5.00004C3.25 4.99074 3.25017 4.98148 3.2505 4.97227C3.25017 4.95788 3.25 4.94344 3.25 4.92897C3.25 4.02272 3.91638 3.25437 4.81353 3.12621L15.6111 1.5837ZM4.75 6.75004V19C4.75 20.2427 5.75736 21.25 7 21.25H17C18.2426 21.25 19.25 20.2427 19.25 19V9.00004C19.25 7.7574 18.2426 6.75004 17 6.75004H4.75ZM5.07107 5.25004H17.25V4.30606C17.25 3.54537 16.5763 2.96104 15.8232 3.06862L5.02566 4.61113C4.86749 4.63373 4.75 4.76919 4.75 4.92897C4.75 5.10629 4.89375 5.25004 5.07107 5.25004ZM7.25 12C7.25 11.5858 7.58579 11.25 8 11.25H16C16.4142 11.25 16.75 11.5858 16.75 12C16.75 12.4143 16.4142 12.75 16 12.75H8C7.58579 12.75 7.25 12.4143 7.25 12ZM7.25 15.5C7.25 15.0858 7.58579 14.75 8 14.75H13.5C13.9142 14.75 14.25 15.0858 14.25 15.5C14.25 15.9143 13.9142 16.25 13.5 16.25H8C7.58579 16.25 7.25 15.9143 7.25 15.5Z" fill="#1C274D"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,21 @@
|
||||
from core.tools.errors import ToolProviderCredentialValidationError
|
||||
from core.tools.provider.builtin.devdocs.tools.searchDevDocs import SearchDevDocsTool
|
||||
from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
|
||||
|
||||
|
||||
class DevDocsProvider(BuiltinToolProviderController):
|
||||
def _validate_credentials(self, credentials: dict) -> None:
|
||||
try:
|
||||
SearchDevDocsTool().fork_tool_runtime(
|
||||
meta={
|
||||
"credentials": credentials,
|
||||
}
|
||||
).invoke(
|
||||
user_id='',
|
||||
tool_parameters={
|
||||
"doc": "python~3.12",
|
||||
"topic": "library/code",
|
||||
},
|
||||
)
|
||||
except Exception as e:
|
||||
raise ToolProviderCredentialValidationError(str(e))
|
||||
@@ -0,0 +1,10 @@
|
||||
identity:
|
||||
author: Richards Tu
|
||||
name: devdocs
|
||||
label:
|
||||
en_US: DevDocs
|
||||
zh_Hans: DevDocs
|
||||
description:
|
||||
en_US: Get official developer documentations on DevDocs.
|
||||
zh_Hans: 从DevDocs获取官方开发者文档。
|
||||
icon: icon.svg
|
||||
@@ -0,0 +1,42 @@
|
||||
from typing import Any, Union
|
||||
|
||||
import requests
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from core.tools.entities.tool_entities import ToolInvokeMessage
|
||||
from core.tools.tool.builtin_tool import BuiltinTool
|
||||
|
||||
|
||||
class SearchDevDocsInput(BaseModel):
|
||||
doc: str = Field(..., description="The name of the documentation.")
|
||||
topic: str = Field(..., description="The path of the section/topic.")
|
||||
|
||||
|
||||
class SearchDevDocsTool(BuiltinTool):
|
||||
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
|
||||
"""
|
||||
Invokes the DevDocs search tool with the given user ID and tool parameters.
|
||||
|
||||
Args:
|
||||
user_id (str): The ID of the user invoking the tool.
|
||||
tool_parameters (dict[str, Any]): The parameters for the tool, including 'doc' and 'topic'.
|
||||
|
||||
Returns:
|
||||
ToolInvokeMessage | list[ToolInvokeMessage]: The result of the tool invocation, which can be a single message or a list of messages.
|
||||
"""
|
||||
doc = tool_parameters.get('doc', '')
|
||||
topic = tool_parameters.get('topic', '')
|
||||
|
||||
if not doc:
|
||||
return self.create_text_message('Please provide the documentation name.')
|
||||
if not topic:
|
||||
return self.create_text_message('Please provide the topic path.')
|
||||
|
||||
url = f"https://documents.devdocs.io/{doc}/{topic}.html"
|
||||
response = requests.get(url)
|
||||
|
||||
if response.status_code == 200:
|
||||
content = response.text
|
||||
return self.create_text_message(self.summary(user_id=user_id, content=content))
|
||||
else:
|
||||
return self.create_text_message(f"Failed to retrieve the documentation. Status code: {response.status_code}")
|
||||
@@ -0,0 +1,34 @@
|
||||
identity:
|
||||
name: searchDevDocs
|
||||
author: Richards Tu
|
||||
label:
|
||||
en_US: Search Developer Docs
|
||||
zh_Hans: 搜索开发者文档
|
||||
description:
|
||||
human:
|
||||
en_US: A tools for searching for a specific topic and path in DevDocs based on the provided documentation name and topic. Don't for get to add some shots in the system prompt; for example, the documentation name should be like \"vuex~4\", \"css\", or \"python~3.12\", while the topic should be like \"guide/actions\" for Vuex 4, \"display-box\" for CSS, or \"library/code\" for Python 3.12.
|
||||
zh_Hans: 一个用于根据提供的文档名称和主题,在DevDocs中搜索特定主题和路径的工具。不要忘记在系统提示词中添加一些示例;例如,文档名称应该是\"vuex~4\"、\"css\"或\"python~3.12\",而主题应该是\"guide/actions\"用于Vuex 4,\"display-box\"用于CSS,或\"library/code\"用于Python 3.12。
|
||||
llm: A tools for searching for specific developer documentation in DevDocs based on the provided documentation name and topic.
|
||||
parameters:
|
||||
- name: doc
|
||||
type: string
|
||||
required: true
|
||||
label:
|
||||
en_US: Documentation name
|
||||
zh_Hans: 文档名称
|
||||
human_description:
|
||||
en_US: The name of the documentation.
|
||||
zh_Hans: 文档名称。
|
||||
llm_description: The name of the documentation, such as \"vuex~4\", \"css\", or \"python~3.12\". The exact value should be identified by the user.
|
||||
form: llm
|
||||
- name: topic
|
||||
type: string
|
||||
required: true
|
||||
label:
|
||||
en_US: Topic name
|
||||
zh_Hans: 主题名称
|
||||
human_description:
|
||||
en_US: The path of the section/topic.
|
||||
zh_Hans: 文档主题的路径。
|
||||
llm_description: The path of the section/topic, such as \"guide/actions\" for Vuex 4, \"display-box\" for CSS, or \"library/code\" for Python 3.12.
|
||||
form: llm
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120"><style>.st0{fill:#376db6}.st1{fill:#4ca2da}.st2{fill:#91d8f4}.st3{fill:#1e5397}</style><path class="st0" d="M22.4 57.5h74.8v15.4H22.4z"/><path class="st1" d="M22.4 37.6h74.8V53H22.4z"/><path class="st2" d="M85.5 17H34.4c-6.6 0-12 5.5-12 12.3v4h74.8v-4C97.2 22.5 92 17 85.5 17z"/><path class="st3" d="M22.4 77.3v4c0 6.8 5.4 12.3 12 12.3h32v16.3l15.8-16.3h3.5c6.6 0 12-5.5 12-12.3v-4H22.4z"/></svg>
|
||||
|
After Width: | Height: | Size: 458 B |
@@ -0,0 +1,25 @@
|
||||
from core.tools.errors import ToolProviderCredentialValidationError
|
||||
from core.tools.provider.builtin.stackexchange.tools.searchStackExQuestions import SearchStackExQuestionsTool
|
||||
from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
|
||||
|
||||
|
||||
class StackExchangeProvider(BuiltinToolProviderController):
|
||||
def _validate_credentials(self, credentials: dict) -> None:
|
||||
try:
|
||||
SearchStackExQuestionsTool().fork_tool_runtime(
|
||||
meta={
|
||||
"credentials": credentials,
|
||||
}
|
||||
).invoke(
|
||||
user_id='',
|
||||
tool_parameters={
|
||||
"intitle": "Test",
|
||||
"sort": "relevance",
|
||||
"order": "desc",
|
||||
"site": "stackoverflow",
|
||||
"accepted": True,
|
||||
"pagesize": 1
|
||||
},
|
||||
)
|
||||
except Exception as e:
|
||||
raise ToolProviderCredentialValidationError(str(e))
|
||||
@@ -0,0 +1,10 @@
|
||||
identity:
|
||||
author: Richards Tu
|
||||
name: stackexchange
|
||||
label:
|
||||
en_US: Stack Exchange
|
||||
zh_Hans: Stack Exchange
|
||||
description:
|
||||
en_US: Access questions and answers from the Stack Exchange and its sub-sites.
|
||||
zh_Hans: 从Stack Exchange和其子论坛获取问题和答案。
|
||||
icon: icon.svg
|
||||
@@ -0,0 +1,37 @@
|
||||
from typing import Any, Union
|
||||
|
||||
import requests
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from core.tools.entities.tool_entities import ToolInvokeMessage
|
||||
from core.tools.tool.builtin_tool import BuiltinTool
|
||||
|
||||
|
||||
class FetchAnsByStackExQuesIDInput(BaseModel):
|
||||
id: int = Field(..., description="The question ID")
|
||||
site: str = Field(..., description="The Stack Exchange site")
|
||||
order: str = Field(..., description="asc or desc")
|
||||
sort: str = Field(..., description="activity, votes, creation")
|
||||
pagesize: int = Field(..., description="Number of answers per page")
|
||||
page: int = Field(..., description="Page number")
|
||||
|
||||
|
||||
class FetchAnsByStackExQuesIDTool(BuiltinTool):
|
||||
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
|
||||
input = FetchAnsByStackExQuesIDInput(**tool_parameters)
|
||||
|
||||
params = {
|
||||
"site": input.site,
|
||||
"filter": "!nNPvSNdWme",
|
||||
"order": input.order,
|
||||
"sort": input.sort,
|
||||
"pagesize": input.pagesize,
|
||||
"page": input.page
|
||||
}
|
||||
|
||||
response = requests.get(f"https://api.stackexchange.com/2.3/questions/{input.id}/answers", params=params)
|
||||
|
||||
if response.status_code == 200:
|
||||
return self.create_text_message(self.summary(user_id=user_id, content=response.text))
|
||||
else:
|
||||
return self.create_text_message(f"API request failed with status code {response.status_code}")
|
||||
@@ -0,0 +1,189 @@
|
||||
identity:
|
||||
name: fetchAnsByStackExQuesID
|
||||
author: Richards Tu
|
||||
label:
|
||||
en_US: Fetch Stack Exchange Answers
|
||||
zh_Hans: 获取 Stack Exchange 答案
|
||||
description:
|
||||
human:
|
||||
en_US: A tool for retrieving answers for a specific Stack Exchange question ID. Specify the question ID, Stack Exchange site, sorting order, number of results per page, and page number. Must be used with the searchStackExQuesID tool.
|
||||
zh_Hans: 用于检索特定Stack Exchange问题ID的答案的工具。指定问题ID、Stack Exchange站点、排序顺序、每页结果数和页码。 必须与searchStackExQuesID工具一起使用。
|
||||
llm: A tool for retrieving answers for a specific Stack Exchange question ID based on the provided parameters.
|
||||
parameters:
|
||||
- name: id
|
||||
type: string
|
||||
required: true
|
||||
label:
|
||||
en_US: Question ID
|
||||
zh_Hans: 问题ID
|
||||
human_description:
|
||||
en_US: The ID of the Stack Exchange question to fetch answers for.
|
||||
zh_Hans: 要获取答案的Stack Exchange问题的ID。
|
||||
llm_description: The ID of the Stack Exchange question.
|
||||
form: llm
|
||||
- name: site
|
||||
type: string
|
||||
required: true
|
||||
label:
|
||||
en_US: Stack Exchange site
|
||||
zh_Hans: Stack Exchange站点
|
||||
human_description:
|
||||
en_US: The Stack Exchange site the question is from, e.g. stackoverflow, unix, etc.
|
||||
zh_Hans: 问题所在的Stack Exchange站点,例如stackoverflow、unix等。
|
||||
llm_description: The Stack Exchange site identifier.
|
||||
options:
|
||||
- value: stackoverflow
|
||||
label:
|
||||
en_US: stackoverflow
|
||||
- value: serverfault
|
||||
label:
|
||||
en_US: serverfault
|
||||
- value: superuser
|
||||
label:
|
||||
en_US: superuser
|
||||
- value: askubuntu
|
||||
label:
|
||||
en_US: askubuntu
|
||||
- value: unix
|
||||
label:
|
||||
en_US: unix
|
||||
- value: cs
|
||||
label:
|
||||
en_US: cs
|
||||
- value: softwareengineering
|
||||
label:
|
||||
en_US: softwareengineering
|
||||
- value: codegolf
|
||||
label:
|
||||
en_US: codegolf
|
||||
- value: codereview
|
||||
label:
|
||||
en_US: codereview
|
||||
- value: cstheory
|
||||
label:
|
||||
en_US: cstheory
|
||||
- value: security
|
||||
label:
|
||||
en_US: security
|
||||
- value: cryptography
|
||||
label:
|
||||
en_US: cryptography
|
||||
- value: reverseengineering
|
||||
label:
|
||||
en_US: reverseengineering
|
||||
- value: datascience
|
||||
label:
|
||||
en_US: datascience
|
||||
- value: devops
|
||||
label:
|
||||
en_US: devops
|
||||
- value: ux
|
||||
label:
|
||||
en_US: ux
|
||||
- value: dba
|
||||
label:
|
||||
en_US: dba
|
||||
- value: gis
|
||||
label:
|
||||
en_US: gis
|
||||
- value: webmasters
|
||||
label:
|
||||
en_US: webmasters
|
||||
- value: arduino
|
||||
label:
|
||||
en_US: arduino
|
||||
- value: raspberrypi
|
||||
label:
|
||||
en_US: raspberrypi
|
||||
- value: networkengineering
|
||||
label:
|
||||
en_US: networkengineering
|
||||
- value: iot
|
||||
label:
|
||||
en_US: iot
|
||||
- value: tor
|
||||
label:
|
||||
en_US: tor
|
||||
- value: sqa
|
||||
label:
|
||||
en_US: sqa
|
||||
- value: mathoverflow
|
||||
label:
|
||||
en_US: mathoverflow
|
||||
- value: math
|
||||
label:
|
||||
en_US: math
|
||||
- value: mathematica
|
||||
label:
|
||||
en_US: mathematica
|
||||
- value: dsp
|
||||
label:
|
||||
en_US: dsp
|
||||
- value: gamedev
|
||||
label:
|
||||
en_US: gamedev
|
||||
- value: robotics
|
||||
label:
|
||||
en_US: robotics
|
||||
- value: genai
|
||||
label:
|
||||
en_US: genai
|
||||
- value: computergraphics
|
||||
label:
|
||||
en_US: computergraphics
|
||||
form: form
|
||||
- name: filter
|
||||
type: string
|
||||
required: true
|
||||
label:
|
||||
en_US: Filter
|
||||
zh_Hans: 过滤器
|
||||
human_description:
|
||||
en_US: This is required in order to actually get the body of the answer.
|
||||
zh_Hans: 为了实际获取答案的正文,这是必需的。
|
||||
llm_description: Required in order to actually get the body of the answer. Must be \"!nNPvSNdWme\".
|
||||
form: llm
|
||||
- name: order
|
||||
type: string
|
||||
required: true
|
||||
label:
|
||||
en_US: Sort direction
|
||||
zh_Hans: 排序方向
|
||||
human_description:
|
||||
en_US: The direction to sort the answers - ascending or descending.
|
||||
zh_Hans: 答案的排序方向 - 升序或降序。
|
||||
llm_description: asc for ascending, desc for descending.
|
||||
form: llm
|
||||
- name: sort
|
||||
type: string
|
||||
required: true
|
||||
label:
|
||||
en_US: Sort order
|
||||
zh_Hans: 排序
|
||||
human_description:
|
||||
en_US: The sort order for the answers - activity, votes, or creation date.
|
||||
zh_Hans: 答案的排序顺序 - 活动、投票或创建日期。
|
||||
llm_description: activity, votes, or creation.
|
||||
form: llm
|
||||
- name: pagesize
|
||||
type: number
|
||||
required: true
|
||||
label:
|
||||
en_US: Results per page
|
||||
zh_Hans: 每页结果数
|
||||
human_description:
|
||||
en_US: The number of answers to return per page.
|
||||
zh_Hans: 每页返回的答案数。
|
||||
llm_description: The number of answers per page.
|
||||
form: llm
|
||||
- name: page
|
||||
type: number
|
||||
required: true
|
||||
label:
|
||||
en_US: Page number
|
||||
zh_Hans: 页码
|
||||
human_description:
|
||||
en_US: The page number of answers to retrieve.
|
||||
zh_Hans: 要检索的答案的页码。
|
||||
llm_description: The page number to retrieve.
|
||||
form: llm
|
||||
@@ -0,0 +1,43 @@
|
||||
from typing import Any, Union
|
||||
|
||||
import requests
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from core.tools.entities.tool_entities import ToolInvokeMessage
|
||||
from core.tools.tool.builtin_tool import BuiltinTool
|
||||
|
||||
|
||||
class SearchStackExQuestionsInput(BaseModel):
|
||||
intitle: str = Field(..., description="The search query.")
|
||||
sort: str = Field(..., description="The sort order - relevance, activity, votes, creation.")
|
||||
order: str = Field(..., description="asc or desc")
|
||||
site: str = Field(..., description="The Stack Exchange site.")
|
||||
tagged: str = Field(None, description="Semicolon-separated tags to include.")
|
||||
nottagged: str = Field(None, description="Semicolon-separated tags to exclude.")
|
||||
accepted: bool = Field(..., description="true for only accepted answers, false otherwise")
|
||||
pagesize: int = Field(..., description="Number of results per page")
|
||||
|
||||
|
||||
class SearchStackExQuestionsTool(BuiltinTool):
|
||||
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
|
||||
input = SearchStackExQuestionsInput(**tool_parameters)
|
||||
|
||||
params = {
|
||||
"intitle": input.intitle,
|
||||
"sort": input.sort,
|
||||
"order": input.order,
|
||||
"site": input.site,
|
||||
"accepted": input.accepted,
|
||||
"pagesize": input.pagesize
|
||||
}
|
||||
if input.tagged:
|
||||
params["tagged"] = input.tagged
|
||||
if input.nottagged:
|
||||
params["nottagged"] = input.nottagged
|
||||
|
||||
response = requests.get("https://api.stackexchange.com/2.3/search", params=params)
|
||||
|
||||
if response.status_code == 200:
|
||||
return self.create_text_message(self.summary(user_id=user_id, content=response.text))
|
||||
else:
|
||||
return self.create_text_message(f"API request failed with status code {response.status_code}")
|
||||
@@ -0,0 +1,200 @@
|
||||
identity:
|
||||
name: searchStackExQuestions
|
||||
author: Richards Tu
|
||||
label:
|
||||
en_US: Search Stack Exchange Questions
|
||||
zh_Hans: 搜索Stack Exchange问题
|
||||
description:
|
||||
human:
|
||||
en_US: A tool for searching questions on a Stack Exchange site. Specify the search query, sorting order, tags to include or exclude, whether to search only for questions with accepted answers, the Stack Exchange site, and number of results per page.
|
||||
zh_Hans: 在Stack Exchange站点上搜索问题的工具。指定搜索查询、排序顺序、要包含或排除的标签、是否仅搜索有已接受答案的问题、Stack Exchange站点以及每页结果数。
|
||||
llm: A tool for searching questions on a Stack Exchange site based on the provided parameters.
|
||||
parameters:
|
||||
- name: intitle
|
||||
type: string
|
||||
required: true
|
||||
label:
|
||||
en_US: Search query
|
||||
zh_Hans: 搜索查询
|
||||
human_description:
|
||||
en_US: The search query to use for finding questions.
|
||||
zh_Hans: 用于查找问题的搜索查询。
|
||||
llm_description: The search query to use.
|
||||
form: llm
|
||||
- name: sort
|
||||
type: string
|
||||
required: true
|
||||
label:
|
||||
en_US: Sort order
|
||||
zh_Hans: 排序
|
||||
human_description:
|
||||
en_US: The sort order for the search results - relevance, activity, votes, or creation date.
|
||||
zh_Hans: 搜索结果的排序顺序 - 相关性、活动、投票或创建日期。
|
||||
llm_description: The sort order - relevance, activity, votes, or creation.
|
||||
form: llm
|
||||
- name: order
|
||||
type: string
|
||||
required: true
|
||||
label:
|
||||
en_US: Sort direction
|
||||
zh_Hans: 排序方向
|
||||
human_description:
|
||||
en_US: The direction to sort - ascending or descending.
|
||||
zh_Hans: 排序方向 - 升序或降序。
|
||||
llm_description: asc for ascending, desc for descending.
|
||||
form: llm
|
||||
- name: site
|
||||
type: string
|
||||
required: true
|
||||
label:
|
||||
en_US: Stack Exchange site
|
||||
zh_Hans: Stack Exchange 站点
|
||||
human_description:
|
||||
en_US: The Stack Exchange site to search, e.g. stackoverflow, unix, etc.
|
||||
zh_Hans: 要搜索的Stack Exchange站点,例如stackoverflow、unix等。
|
||||
llm_description: The Stack Exchange site identifier.
|
||||
options:
|
||||
- value: stackoverflow
|
||||
label:
|
||||
en_US: stackoverflow
|
||||
- value: serverfault
|
||||
label:
|
||||
en_US: serverfault
|
||||
- value: superuser
|
||||
label:
|
||||
en_US: superuser
|
||||
- value: askubuntu
|
||||
label:
|
||||
en_US: askubuntu
|
||||
- value: unix
|
||||
label:
|
||||
en_US: unix
|
||||
- value: cs
|
||||
label:
|
||||
en_US: cs
|
||||
- value: softwareengineering
|
||||
label:
|
||||
en_US: softwareengineering
|
||||
- value: codegolf
|
||||
label:
|
||||
en_US: codegolf
|
||||
- value: codereview
|
||||
label:
|
||||
en_US: codereview
|
||||
- value: cstheory
|
||||
label:
|
||||
en_US: cstheory
|
||||
- value: security
|
||||
label:
|
||||
en_US: security
|
||||
- value: cryptography
|
||||
label:
|
||||
en_US: cryptography
|
||||
- value: reverseengineering
|
||||
label:
|
||||
en_US: reverseengineering
|
||||
- value: datascience
|
||||
label:
|
||||
en_US: datascience
|
||||
- value: devops
|
||||
label:
|
||||
en_US: devops
|
||||
- value: ux
|
||||
label:
|
||||
en_US: ux
|
||||
- value: dba
|
||||
label:
|
||||
en_US: dba
|
||||
- value: gis
|
||||
label:
|
||||
en_US: gis
|
||||
- value: webmasters
|
||||
label:
|
||||
en_US: webmasters
|
||||
- value: arduino
|
||||
label:
|
||||
en_US: arduino
|
||||
- value: raspberrypi
|
||||
label:
|
||||
en_US: raspberrypi
|
||||
- value: networkengineering
|
||||
label:
|
||||
en_US: networkengineering
|
||||
- value: iot
|
||||
label:
|
||||
en_US: iot
|
||||
- value: tor
|
||||
label:
|
||||
en_US: tor
|
||||
- value: sqa
|
||||
label:
|
||||
en_US: sqa
|
||||
- value: mathoverflow
|
||||
label:
|
||||
en_US: mathoverflow
|
||||
- value: math
|
||||
label:
|
||||
en_US: math
|
||||
- value: mathematica
|
||||
label:
|
||||
en_US: mathematica
|
||||
- value: dsp
|
||||
label:
|
||||
en_US: dsp
|
||||
- value: gamedev
|
||||
label:
|
||||
en_US: gamedev
|
||||
- value: robotics
|
||||
label:
|
||||
en_US: robotics
|
||||
- value: genai
|
||||
label:
|
||||
en_US: genai
|
||||
- value: computergraphics
|
||||
label:
|
||||
en_US: computergraphics
|
||||
form: form
|
||||
- name: tagged
|
||||
type: string
|
||||
required: false
|
||||
label:
|
||||
en_US: Include tags
|
||||
zh_Hans: 包含标签
|
||||
human_description:
|
||||
en_US: A semicolon-separated list of tags that questions must have.
|
||||
zh_Hans: 问题必须具有的标签的分号分隔列表。
|
||||
llm_description: Semicolon-separated tags to include. Leave blank if not needed.
|
||||
form: llm
|
||||
- name: nottagged
|
||||
type: string
|
||||
required: false
|
||||
label:
|
||||
en_US: Exclude tags
|
||||
zh_Hans: 排除标签
|
||||
human_description:
|
||||
en_US: A semicolon-separated list of tags to exclude from the search.
|
||||
zh_Hans: 从搜索中排除的标签的分号分隔列表。
|
||||
llm_description: Semicolon-separated tags to exclude. Leave blank if not needed.
|
||||
form: llm
|
||||
- name: accepted
|
||||
type: boolean
|
||||
required: true
|
||||
label:
|
||||
en_US: Has accepted answer
|
||||
zh_Hans: 有已接受的答案
|
||||
human_description:
|
||||
en_US: Whether to limit to only questions that have an accepted answer.
|
||||
zh_Hans: 是否限制为只有已接受答案的问题。
|
||||
llm_description: true to limit to only questions with accepted answers, false otherwise.
|
||||
form: llm
|
||||
- name: pagesize
|
||||
type: number
|
||||
required: true
|
||||
label:
|
||||
en_US: Results per page
|
||||
zh_Hans: 每页结果数
|
||||
human_description:
|
||||
en_US: The number of results to return per page.
|
||||
zh_Hans: 每页返回的结果数。
|
||||
llm_description: The number of results per page.
|
||||
form: llm
|
||||
@@ -1,4 +1,3 @@
|
||||
import importlib
|
||||
from abc import abstractmethod
|
||||
from os import listdir, path
|
||||
from typing import Any
|
||||
@@ -16,6 +15,7 @@ from core.tools.errors import (
|
||||
from core.tools.provider.tool_provider import ToolProviderController
|
||||
from core.tools.tool.builtin_tool import BuiltinTool
|
||||
from core.tools.tool.tool import Tool
|
||||
from core.utils.module_import_helper import load_single_subclass_from_source
|
||||
|
||||
|
||||
class BuiltinToolProviderController(ToolProviderController):
|
||||
@@ -63,16 +63,11 @@ class BuiltinToolProviderController(ToolProviderController):
|
||||
tool_name = tool_file.split(".")[0]
|
||||
tool = load(f.read(), FullLoader)
|
||||
# get tool class, import the module
|
||||
py_path = path.join(path.dirname(path.realpath(__file__)), 'builtin', provider, 'tools', f'{tool_name}.py')
|
||||
spec = importlib.util.spec_from_file_location(f'core.tools.provider.builtin.{provider}.tools.{tool_name}', py_path)
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(mod)
|
||||
|
||||
# get all the classes in the module
|
||||
classes = [x for _, x in vars(mod).items()
|
||||
if isinstance(x, type) and x not in [BuiltinTool, Tool] and issubclass(x, BuiltinTool)
|
||||
]
|
||||
assistant_tool_class = classes[0]
|
||||
assistant_tool_class = load_single_subclass_from_source(
|
||||
module_name=f'core.tools.provider.builtin.{provider}.tools.{tool_name}',
|
||||
script_path=path.join(path.dirname(path.realpath(__file__)),
|
||||
'builtin', provider, 'tools', f'{tool_name}.py'),
|
||||
parent_type=BuiltinTool)
|
||||
tools.append(assistant_tool_class(**tool))
|
||||
|
||||
self.tools = tools
|
||||
|
||||
@@ -148,7 +148,7 @@ class ApiTool(Tool):
|
||||
value = ''
|
||||
if parameter['name'] in parameters:
|
||||
value = parameters[parameter['name']]
|
||||
elif parameter['required']:
|
||||
elif parameter.get('required', False):
|
||||
raise ToolParameterValidationError(f"Missing required parameter {parameter['name']}")
|
||||
else:
|
||||
value = (parameter.get('schema', {}) or {}).get('default', '')
|
||||
@@ -158,7 +158,7 @@ class ApiTool(Tool):
|
||||
value = ''
|
||||
if parameter['name'] in parameters:
|
||||
value = parameters[parameter['name']]
|
||||
elif parameter['required']:
|
||||
elif parameter.get('required', False):
|
||||
raise ToolParameterValidationError(f"Missing required parameter {parameter['name']}")
|
||||
else:
|
||||
value = (parameter.get('schema', {}) or {}).get('default', '')
|
||||
@@ -168,7 +168,7 @@ class ApiTool(Tool):
|
||||
value = ''
|
||||
if parameter['name'] in parameters:
|
||||
value = parameters[parameter['name']]
|
||||
elif parameter['required']:
|
||||
elif parameter.get('required', False):
|
||||
raise ToolParameterValidationError(f"Missing required parameter {parameter['name']}")
|
||||
else:
|
||||
value = (parameter.get('schema', {}) or {}).get('default', '')
|
||||
|
||||
@@ -232,7 +232,7 @@ class Tool(BaseModel, ABC):
|
||||
result += f"result link: {response.message}. please tell user to check it."
|
||||
elif response.type == ToolInvokeMessage.MessageType.IMAGE_LINK or \
|
||||
response.type == ToolInvokeMessage.MessageType.IMAGE:
|
||||
result += "image has been created and sent to user already, you should tell user to check it now."
|
||||
result += "image has been created and sent to user already, you do not need to create it, just tell the user to check it now."
|
||||
elif response.type == ToolInvokeMessage.MessageType.BLOB:
|
||||
if len(response.message) > 114:
|
||||
result += str(response.message[:114]) + '...'
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import importlib
|
||||
import json
|
||||
import logging
|
||||
import mimetypes
|
||||
@@ -34,6 +33,7 @@ from core.tools.utils.configuration import (
|
||||
ToolParameterConfigurationManager,
|
||||
)
|
||||
from core.tools.utils.encoder import serialize_base_model_dict
|
||||
from core.utils.module_import_helper import load_single_subclass_from_source
|
||||
from extensions.ext_database import db
|
||||
from models.tools import ApiToolProvider, BuiltinToolProvider
|
||||
|
||||
@@ -72,21 +72,11 @@ class ToolManager:
|
||||
|
||||
if provider_entity is None:
|
||||
# fetch the provider from .provider.builtin
|
||||
py_path = path.join(path.dirname(path.realpath(__file__)), 'builtin', provider, f'{provider}.py')
|
||||
spec = importlib.util.spec_from_file_location(f'core.tools.provider.builtin.{provider}.{provider}', py_path)
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(mod)
|
||||
|
||||
# get all the classes in the module
|
||||
classes = [ x for _, x in vars(mod).items()
|
||||
if isinstance(x, type) and x != ToolProviderController and issubclass(x, ToolProviderController)
|
||||
]
|
||||
if len(classes) == 0:
|
||||
raise ToolProviderNotFoundError(f'provider {provider} not found')
|
||||
if len(classes) > 1:
|
||||
raise ToolProviderNotFoundError(f'multiple providers found for {provider}')
|
||||
|
||||
provider_entity = classes[0]()
|
||||
provider_class = load_single_subclass_from_source(
|
||||
module_name=f'core.tools.provider.builtin.{provider}.{provider}',
|
||||
script_path=path.join(path.dirname(path.realpath(__file__)), 'builtin', provider, f'{provider}.py'),
|
||||
parent_type=ToolProviderController)
|
||||
provider_entity = provider_class()
|
||||
|
||||
return provider_entity.invoke(tool_id, tool_name, tool_parameters, credentials, prompt_messages)
|
||||
|
||||
@@ -330,23 +320,12 @@ class ToolManager:
|
||||
if provider.startswith('__'):
|
||||
continue
|
||||
|
||||
py_path = path.join(path.dirname(path.realpath(__file__)), 'provider', 'builtin', provider, f'{provider}.py')
|
||||
spec = importlib.util.spec_from_file_location(f'core.tools.provider.builtin.{provider}.{provider}', py_path)
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(mod)
|
||||
|
||||
# load all classes
|
||||
classes = [
|
||||
obj for name, obj in vars(mod).items()
|
||||
if isinstance(obj, type) and obj != BuiltinToolProviderController and issubclass(obj, BuiltinToolProviderController)
|
||||
]
|
||||
if len(classes) == 0:
|
||||
raise ToolProviderNotFoundError(f'provider {provider} not found')
|
||||
if len(classes) > 1:
|
||||
raise ToolProviderNotFoundError(f'multiple providers found for {provider}')
|
||||
|
||||
# init provider
|
||||
provider_class = classes[0]
|
||||
provider_class = load_single_subclass_from_source(
|
||||
module_name=f'core.tools.provider.builtin.{provider}.{provider}',
|
||||
script_path=path.join(path.dirname(path.realpath(__file__)),
|
||||
'provider', 'builtin', provider, f'{provider}.py'),
|
||||
parent_type=BuiltinToolProviderController)
|
||||
builtin_providers.append(provider_class())
|
||||
|
||||
# cache the builtin providers
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import importlib.util
|
||||
import logging
|
||||
import sys
|
||||
from types import ModuleType
|
||||
from typing import AnyStr
|
||||
|
||||
|
||||
def import_module_from_source(
|
||||
module_name: str,
|
||||
py_file_path: AnyStr,
|
||||
use_lazy_loader: bool = False
|
||||
) -> ModuleType:
|
||||
"""
|
||||
Importing a module from the source file directly
|
||||
"""
|
||||
try:
|
||||
existed_spec = importlib.util.find_spec(module_name)
|
||||
if existed_spec:
|
||||
spec = existed_spec
|
||||
else:
|
||||
# Refer to: https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly
|
||||
spec = importlib.util.spec_from_file_location(module_name, py_file_path)
|
||||
if use_lazy_loader:
|
||||
# Refer to: https://docs.python.org/3/library/importlib.html#implementing-lazy-imports
|
||||
spec.loader = importlib.util.LazyLoader(spec.loader)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
if not existed_spec:
|
||||
sys.modules[module_name] = module
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
except Exception as e:
|
||||
logging.exception(f'Failed to load module {module_name} from {py_file_path}: {str(e)}')
|
||||
raise e
|
||||
|
||||
|
||||
def get_subclasses_from_module(mod: ModuleType, parent_type: type) -> list[type]:
|
||||
"""
|
||||
Get all the subclasses of the parent type from the module
|
||||
"""
|
||||
classes = [x for _, x in vars(mod).items()
|
||||
if isinstance(x, type) and x != parent_type and issubclass(x, parent_type)]
|
||||
return classes
|
||||
|
||||
|
||||
def load_single_subclass_from_source(
|
||||
module_name: str,
|
||||
script_path: AnyStr,
|
||||
parent_type: type,
|
||||
use_lazy_loader: bool = False,
|
||||
) -> type:
|
||||
"""
|
||||
Load a single subclass from the source
|
||||
"""
|
||||
module = import_module_from_source(module_name, script_path, use_lazy_loader)
|
||||
subclasses = get_subclasses_from_module(module, parent_type)
|
||||
match len(subclasses):
|
||||
case 1:
|
||||
return subclasses[0]
|
||||
case 0:
|
||||
raise Exception(f'Missing subclass of {parent_type.__name__} in {script_path}')
|
||||
case _:
|
||||
raise Exception(f'Multiple subclasses of {parent_type.__name__} in {script_path}')
|
||||
@@ -5,6 +5,12 @@ def init_app(app: Flask):
|
||||
if app.config.get('API_COMPRESSION_ENABLED', False):
|
||||
from flask_compress import Compress
|
||||
|
||||
app.config['COMPRESS_MIMETYPES'] = [
|
||||
'application/json',
|
||||
'image/svg+xml',
|
||||
'text/html',
|
||||
]
|
||||
|
||||
compress = Compress()
|
||||
compress.init_app(app)
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ from typing import Union
|
||||
|
||||
import boto3
|
||||
from azure.storage.blob import AccountSasPermissions, BlobServiceClient, ResourceTypes, generate_account_sas
|
||||
from botocore.client import Config
|
||||
from botocore.exceptions import ClientError
|
||||
from flask import Flask
|
||||
|
||||
@@ -27,7 +28,8 @@ class Storage:
|
||||
aws_secret_access_key=app.config.get('S3_SECRET_KEY'),
|
||||
aws_access_key_id=app.config.get('S3_ACCESS_KEY'),
|
||||
endpoint_url=app.config.get('S3_ENDPOINT'),
|
||||
region_name=app.config.get('S3_REGION')
|
||||
region_name=app.config.get('S3_REGION'),
|
||||
config=Config(s3={'addressing_style': app.config.get('S3_ADDRESS_STYLE')})
|
||||
)
|
||||
elif self.storage_type == 'azure-blob':
|
||||
self.bucket_name = app.config.get('AZURE_BLOB_CONTAINER_NAME')
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ def login_required(func):
|
||||
def decorated_view(*args, **kwargs):
|
||||
auth_header = request.headers.get('Authorization')
|
||||
admin_api_key_enable = os.getenv('ADMIN_API_KEY_ENABLE', default='False')
|
||||
if admin_api_key_enable:
|
||||
if admin_api_key_enable.lower() == 'true':
|
||||
if auth_header:
|
||||
if ' ' not in auth_header:
|
||||
raise Unauthorized('Invalid Authorization header format. Expected \'Bearer <api-key>\' format.')
|
||||
|
||||
@@ -28,8 +28,8 @@ scikit-learn==1.2.2
|
||||
sentry-sdk[flask]~=1.39.2
|
||||
sympy==1.12
|
||||
jieba==0.42.1
|
||||
celery==5.2.7
|
||||
redis~=4.5.4
|
||||
celery~=5.3.6
|
||||
redis[hiredis]~=5.0.3
|
||||
openpyxl==3.1.2
|
||||
chardet~=5.1.0
|
||||
python-docx~=1.1.0
|
||||
|
||||
@@ -435,11 +435,13 @@ class RegisterService:
|
||||
|
||||
if open_id is not None or provider is not None:
|
||||
AccountService.link_account_integrate(provider, open_id, account)
|
||||
if current_app.config['EDITION'] != 'SELF_HOSTED':
|
||||
tenant = TenantService.create_tenant(f"{account.name}'s Workspace")
|
||||
|
||||
tenant = TenantService.create_tenant(f"{account.name}'s Workspace")
|
||||
TenantService.create_tenant_member(tenant, account, role='owner')
|
||||
account.current_tenant = tenant
|
||||
|
||||
TenantService.create_tenant_member(tenant, account, role='owner')
|
||||
account.current_tenant = tenant
|
||||
tenant_was_created.send(tenant)
|
||||
|
||||
db.session.commit()
|
||||
except Exception as e:
|
||||
@@ -447,8 +449,6 @@ class RegisterService:
|
||||
logging.error(f'Register failed: {e}')
|
||||
raise AccountRegisterError(f'Registration failed: {e}') from e
|
||||
|
||||
tenant_was_created.send(tenant)
|
||||
|
||||
return account
|
||||
|
||||
@classmethod
|
||||
@@ -461,7 +461,6 @@ class RegisterService:
|
||||
name = email.split('@')[0]
|
||||
|
||||
account = cls.register(email=email, name=name, language=language, status=AccountStatus.PENDING)
|
||||
|
||||
# Create new tenant member for invited tenant
|
||||
TenantService.create_tenant_member(tenant, account, role)
|
||||
TenantService.switch_tenant(account, tenant.id)
|
||||
|
||||
@@ -9,7 +9,7 @@ from core.rag.datasource.vdb.vector_factory import Vector
|
||||
from extensions.ext_database import db
|
||||
from extensions.ext_redis import redis_client
|
||||
from models.dataset import Dataset
|
||||
from models.model import App, AppAnnotationSetting
|
||||
from models.model import App, AppAnnotationSetting, MessageAnnotation
|
||||
|
||||
|
||||
@shared_task(queue='dataset')
|
||||
@@ -25,7 +25,7 @@ def disable_annotation_reply_task(job_id: str, app_id: str, tenant_id: str):
|
||||
App.tenant_id == tenant_id,
|
||||
App.status == 'normal'
|
||||
).first()
|
||||
|
||||
annotations_count = db.session.query(MessageAnnotation).filter(MessageAnnotation.app_id == app_id).count()
|
||||
if not app:
|
||||
raise NotFound("App not found")
|
||||
|
||||
@@ -49,8 +49,9 @@ def disable_annotation_reply_task(job_id: str, app_id: str, tenant_id: str):
|
||||
)
|
||||
|
||||
try:
|
||||
vector = Vector(dataset, attributes=['doc_id', 'annotation_id', 'app_id'])
|
||||
vector.delete_by_metadata_field('app_id', app_id)
|
||||
if annotations_count > 0:
|
||||
vector = Vector(dataset, attributes=['doc_id', 'annotation_id', 'app_id'])
|
||||
vector.delete_by_metadata_field('app_id', app_id)
|
||||
except Exception:
|
||||
logging.exception("Delete annotation index failed when annotation deleted.")
|
||||
redis_client.setex(disable_app_annotation_job_key, 600, 'completed')
|
||||
|
||||
@@ -154,7 +154,7 @@ def test_invoke_stream_model():
|
||||
model = ErnieBotLargeLanguageModel()
|
||||
|
||||
response = model.invoke(
|
||||
model='ernie-bot',
|
||||
model='ernie-3.5-8k',
|
||||
credentials={
|
||||
'api_key': os.environ.get('WENXIN_API_KEY'),
|
||||
'secret_key': os.environ.get('WENXIN_SECRET_KEY')
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
from tests.integration_tests.utils.parent_class import ParentClass
|
||||
|
||||
|
||||
class ChildClass(ParentClass):
|
||||
def __init__(self, name: str):
|
||||
super().__init__(name)
|
||||
self.name = name
|
||||
@@ -0,0 +1,7 @@
|
||||
from tests.integration_tests.utils.parent_class import ParentClass
|
||||
|
||||
|
||||
class LazyLoadChildClass(ParentClass):
|
||||
def __init__(self, name: str):
|
||||
super().__init__(name)
|
||||
self.name = name
|
||||
@@ -0,0 +1,6 @@
|
||||
class ParentClass:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def get_name(self):
|
||||
return self.name
|
||||
@@ -0,0 +1,32 @@
|
||||
import os
|
||||
|
||||
from core.utils.module_import_helper import load_single_subclass_from_source, import_module_from_source
|
||||
from tests.integration_tests.utils.parent_class import ParentClass
|
||||
|
||||
|
||||
def test_loading_subclass_from_source():
|
||||
current_path = os.getcwd()
|
||||
module = load_single_subclass_from_source(
|
||||
module_name='ChildClass',
|
||||
script_path=os.path.join(current_path, 'child_class.py'),
|
||||
parent_type=ParentClass)
|
||||
assert module and module.__name__ == 'ChildClass'
|
||||
|
||||
|
||||
def test_load_import_module_from_source():
|
||||
current_path = os.getcwd()
|
||||
module = import_module_from_source(
|
||||
module_name='ChildClass',
|
||||
py_file_path=os.path.join(current_path, 'child_class.py'))
|
||||
assert module and module.__name__ == 'ChildClass'
|
||||
|
||||
|
||||
def test_lazy_loading_subclass_from_source():
|
||||
current_path = os.getcwd()
|
||||
clz = load_single_subclass_from_source(
|
||||
module_name='LazyLoadChildClass',
|
||||
script_path=os.path.join(current_path, 'lazy_load_class.py'),
|
||||
parent_type=ParentClass,
|
||||
use_lazy_loader=True)
|
||||
instance = clz('dify')
|
||||
assert instance.get_name() == 'dify'
|
||||
@@ -2,7 +2,7 @@ version: '3'
|
||||
services:
|
||||
# API service
|
||||
api:
|
||||
image: langgenius/dify-api:0.5.10
|
||||
image: langgenius/dify-api:0.5.11
|
||||
restart: always
|
||||
environment:
|
||||
# Startup mode, 'api' starts the API server.
|
||||
@@ -140,7 +140,7 @@ services:
|
||||
# worker service
|
||||
# The Celery worker for processing the queue.
|
||||
worker:
|
||||
image: langgenius/dify-api:0.5.10
|
||||
image: langgenius/dify-api:0.5.11
|
||||
restart: always
|
||||
environment:
|
||||
# Startup mode, 'worker' starts the Celery worker for processing the queue.
|
||||
@@ -222,7 +222,7 @@ services:
|
||||
|
||||
# Frontend web application.
|
||||
web:
|
||||
image: langgenius/dify-web:0.5.10
|
||||
image: langgenius/dify-web:0.5.11
|
||||
restart: always
|
||||
environment:
|
||||
EDITION: SELF_HOSTED
|
||||
@@ -326,8 +326,10 @@ services:
|
||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
|
||||
- ./nginx/proxy.conf:/etc/nginx/proxy.conf
|
||||
- ./nginx/conf.d:/etc/nginx/conf.d
|
||||
#- ./nginx/ssl:/etc/ssl
|
||||
depends_on:
|
||||
- api
|
||||
- web
|
||||
ports:
|
||||
- "80:80"
|
||||
#- "443:443"
|
||||
|
||||
@@ -26,4 +26,13 @@ server {
|
||||
proxy_pass http://web:3000;
|
||||
include proxy.conf;
|
||||
}
|
||||
}
|
||||
|
||||
# If you want to support HTTPS, please uncomment the code snippet below
|
||||
#listen 443 ssl;
|
||||
#ssl_certificate ./../ssl/your_cert_file.cer;
|
||||
#ssl_certificate_key ./../ssl/your_cert_key.key;
|
||||
#ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
|
||||
#ssl_prefer_server_ciphers on;
|
||||
#ssl_session_cache shared:SSL:10m;
|
||||
#ssl_session_timeout 10m;
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -167,7 +167,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
|
||||
style.actionIconWrapper,
|
||||
)
|
||||
}
|
||||
className={'!w-[128px] h-fit !z-20'}
|
||||
className={'!w-[128px] h-fit !z-0'}
|
||||
manualClose
|
||||
/>}
|
||||
</div>
|
||||
|
||||
@@ -46,7 +46,7 @@ const DatasetCard = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<Link href={`/datasets/${dataset.id}/documents`} className={cn(style.listItem)}>
|
||||
<Link href={`/datasets/${dataset.id}/documents`} className={cn(style.listItem)} data-disable-nprogress={true}>
|
||||
<div className={style.listItemTitle}>
|
||||
<AppIcon size='small' className={cn(!dataset.embedding_available && style.unavailable)} />
|
||||
<div className={cn(style.listItemHeading, !dataset.embedding_available && style.unavailable)}>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
'use client'
|
||||
|
||||
import { forwardRef, useState } from 'react'
|
||||
import { forwardRef } from 'react'
|
||||
import classNames from 'classnames'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Link from 'next/link'
|
||||
import style from '../list.module.css'
|
||||
|
||||
const CreateAppCard = forwardRef<HTMLAnchorElement>((_, ref) => {
|
||||
const { t } = useTranslation()
|
||||
const [showNewAppDialog, setShowNewAppDialog] = useState(false)
|
||||
|
||||
return (
|
||||
<a ref={ref} className={classNames(style.listItem, style.newItemCard)} href='/datasets/create'>
|
||||
<Link ref={ref} className={classNames(style.listItem, style.newItemCard)} href='/datasets/create'>
|
||||
<div className={style.listItemTitle}>
|
||||
<span className={style.newItemIcon}>
|
||||
<span className={classNames(style.newItemIconImage, style.newItemIconAdd)} />
|
||||
@@ -21,8 +21,10 @@ const CreateAppCard = forwardRef<HTMLAnchorElement>((_, ref) => {
|
||||
</div>
|
||||
<div className={style.listItemDescription}>{t('dataset.createDatasetIntro')}</div>
|
||||
{/* <div className='text-xs text-gray-500'>{t('app.createFromConfigFile')}</div> */}
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
})
|
||||
|
||||
CreateAppCard.displayName = 'CreateAppCard'
|
||||
|
||||
export default CreateAppCard
|
||||
|
||||
@@ -113,7 +113,7 @@ const BatchModal: FC<IBatchModalProps> = ({
|
||||
type="primary"
|
||||
onClick={handleSend}
|
||||
disabled={isAnnotationFull || !currentCSV}
|
||||
loading={importStatus === ProcessStatus.PROCESSING}
|
||||
loading={importStatus === ProcessStatus.PROCESSING || importStatus === ProcessStatus.WAITING}
|
||||
>
|
||||
{t('appAnnotation.batchModal.run')}
|
||||
</Button>
|
||||
|
||||
@@ -117,7 +117,7 @@ const EditAnnotationModal: FC<Props> = ({
|
||||
<MessageCheckRemove />
|
||||
<div>{t('appAnnotation.editModal.removeThisCache')}</div>
|
||||
</div>
|
||||
{createdAt && <div>{t('appAnnotation.editModal.createdAt')} {dayjs(createdAt * 1000).format('YYYY-MM-DD hh:mm')}</div>}
|
||||
{createdAt && <div>{t('appAnnotation.editModal.createdAt')} {dayjs(createdAt * 1000).format('YYYY-MM-DD HH:mm')}</div>}
|
||||
</div>
|
||||
)
|
||||
: undefined
|
||||
|
||||
@@ -54,7 +54,7 @@ const List: FC<Props> = ({
|
||||
className='whitespace-nowrap overflow-hidden text-ellipsis max-w-[250px]'
|
||||
title={item.answer}
|
||||
>{item.answer}</td>
|
||||
<td>{dayjs(item.created_at * 1000).format('YYYY-MM-DD hh:mm')}</td>
|
||||
<td>{dayjs(item.created_at * 1000).format('YYYY-MM-DD HH:mm')}</td>
|
||||
<td>{item.hit_count}</td>
|
||||
<td className='w-[96px]' onClick={e => e.stopPropagation()}>
|
||||
{/* Actions */}
|
||||
|
||||
@@ -142,7 +142,7 @@ const ViewAnnotationModal: FC<Props> = ({
|
||||
>{item.response}</td>
|
||||
<td>{item.source}</td>
|
||||
<td>{item.score ? item.score.toFixed(2) : '-'}</td>
|
||||
<td>{dayjs(item.created_at * 1000).format('YYYY-MM-DD hh:mm')}</td>
|
||||
<td>{dayjs(item.created_at * 1000).format('YYYY-MM-DD HH:mm')}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
@@ -214,7 +214,7 @@ const ViewAnnotationModal: FC<Props> = ({
|
||||
<MessageCheckRemove />
|
||||
<div>{t('appAnnotation.editModal.removeThisCache')}</div>
|
||||
</div>
|
||||
<div>{t('appAnnotation.editModal.createdAt')} {dayjs(createdAt * 1000).format('YYYY-MM-DD hh:mm')}</div>
|
||||
<div>{t('appAnnotation.editModal.createdAt')} {dayjs(createdAt * 1000).format('YYYY-MM-DD HH:mm')}</div>
|
||||
</div>
|
||||
)
|
||||
: undefined}
|
||||
|
||||
@@ -212,16 +212,18 @@ const Chat: FC<IChatProps> = ({
|
||||
if (i === index) {
|
||||
return {
|
||||
...item,
|
||||
content: answer,
|
||||
annotation: {
|
||||
...item.annotation,
|
||||
logAnnotation: undefined,
|
||||
logAnnotation: {
|
||||
...item.annotation?.logAnnotation,
|
||||
content: answer,
|
||||
},
|
||||
} as any,
|
||||
}
|
||||
}
|
||||
return item
|
||||
}))
|
||||
}, [])
|
||||
}, [chatList])
|
||||
const handleAnnotationAdded = useCallback((annotationId: string, authorName: string, query: string, answer: string, index: number) => {
|
||||
onChatListChange?.(chatList.map((item, i) => {
|
||||
if (i === index - 1) {
|
||||
@@ -251,7 +253,7 @@ const Chat: FC<IChatProps> = ({
|
||||
}
|
||||
return item
|
||||
}))
|
||||
}, [])
|
||||
}, [chatList])
|
||||
const handleAnnotationRemoved = useCallback((index: number) => {
|
||||
onChatListChange?.(chatList.map((item, i) => {
|
||||
if (i === index) {
|
||||
@@ -261,12 +263,13 @@ const Chat: FC<IChatProps> = ({
|
||||
annotation: {
|
||||
...(item.annotation || {}),
|
||||
id: '',
|
||||
logAnnotation: undefined, // remove log
|
||||
} as Annotation,
|
||||
}
|
||||
}
|
||||
return item
|
||||
}))
|
||||
}, [])
|
||||
}, [chatList])
|
||||
|
||||
return (
|
||||
<div className={cn('px-3.5', 'h-full')}>
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
useRef,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useThrottleEffect } from 'ahooks'
|
||||
import { debounce } from 'lodash-es'
|
||||
import type {
|
||||
ChatConfig,
|
||||
@@ -92,10 +91,19 @@ const Chat: FC<ChatProps> = ({
|
||||
chatFooterInnerRef.current.style.width = `${chatContainerInnerRef.current.clientWidth}px`
|
||||
}, [])
|
||||
|
||||
useThrottleEffect(() => {
|
||||
useEffect(() => {
|
||||
handleScrolltoBottom()
|
||||
handleWindowResize()
|
||||
}, [chatList], { wait: 500 })
|
||||
}, [handleScrolltoBottom, handleWindowResize])
|
||||
|
||||
useEffect(() => {
|
||||
if (chatContainerRef.current) {
|
||||
requestAnimationFrame(() => {
|
||||
handleScrolltoBottom()
|
||||
handleWindowResize()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('resize', debounce(handleWindowResize))
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
$getRoot,
|
||||
TextNode,
|
||||
} from 'lexical'
|
||||
import { CodeNode } from '@lexical/code'
|
||||
import { LexicalComposer } from '@lexical/react/LexicalComposer'
|
||||
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'
|
||||
import { ContentEditable } from '@lexical/react/LexicalContentEditable'
|
||||
@@ -118,6 +119,7 @@ const PromptEditor: FC<PromptEditorProps> = ({
|
||||
const initialConfig = {
|
||||
namespace: 'prompt-editor',
|
||||
nodes: [
|
||||
CodeNode,
|
||||
CustomTextNode,
|
||||
{
|
||||
replace: TextNode,
|
||||
|
||||
@@ -130,7 +130,6 @@ const FileUploader = ({
|
||||
}
|
||||
}
|
||||
|
||||
const fileListCopy = fileListRef.current
|
||||
return upload({
|
||||
xhr: new XMLHttpRequest(),
|
||||
data: formData,
|
||||
@@ -142,14 +141,14 @@ const FileUploader = ({
|
||||
file: res,
|
||||
progress: -1,
|
||||
}
|
||||
const index = fileListCopy.findIndex(item => item.fileID === fileItem.fileID)
|
||||
fileListCopy[index] = completeFile
|
||||
onFileUpdate(completeFile, 100, fileListCopy)
|
||||
const index = fileListRef.current.findIndex(item => item.fileID === fileItem.fileID)
|
||||
fileListRef.current[index] = completeFile
|
||||
onFileUpdate(completeFile, 100, fileListRef.current)
|
||||
return Promise.resolve({ ...completeFile })
|
||||
})
|
||||
.catch((e) => {
|
||||
notify({ type: 'error', message: e?.response?.code === 'forbidden' ? e?.response?.message : t('datasetCreation.stepOne.uploader.failed') })
|
||||
onFileUpdate(fileItem, -2, fileListCopy)
|
||||
onFileUpdate(fileItem, -2, fileListRef.current)
|
||||
return Promise.resolve({ ...fileItem })
|
||||
})
|
||||
.finally()
|
||||
|
||||
@@ -138,7 +138,7 @@ The text generation application offers non-session support and is ideal for tran
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="POST" label="/completion-messages" targetCode={`curl -X POST '${props.appDetail.api_base_url}/completion-messages' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "inputs": {"query": "Hello, world!"},\n "response_mode": "streaming"\n "user": "abc-123"\n}'\n`}>
|
||||
<CodeGroup title="Request" tag="POST" label="/completion-messages" targetCode={`curl -X POST '${props.appDetail.api_base_url}/completion-messages' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "inputs": {"query": "Hello, world!"},\n "response_mode": "streaming",\n "user": "abc-123"\n}'\n`}>
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X POST '${props.appDetail.api_base_url}/completion-messages' \
|
||||
|
||||
@@ -139,7 +139,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="POST" label="/completion-messages" targetCode={`curl -X POST '${props.appDetail.api_base_url}/completion-messages' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "inputs": {"query": "Hello, world!"},\n "response_mode": "streaming"\n "user": "abc-123"\n}'\n`}>
|
||||
<CodeGroup title="Request" tag="POST" label="/completion-messages" targetCode={`curl -X POST '${props.appDetail.api_base_url}/completion-messages' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "inputs": {"query": "Hello, world!"},\n "response_mode": "streaming",\n "user": "abc-123"\n}'\n`}>
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X POST '${props.appDetail.api_base_url}/completion-messages' \
|
||||
|
||||
@@ -11,7 +11,7 @@ const HeaderWrapper = ({
|
||||
children,
|
||||
}: HeaderWrapperProps) => {
|
||||
const pathname = usePathname()
|
||||
const isBordered = ['/apps', '/datasets'].includes(pathname)
|
||||
const isBordered = ['/apps', '/datasets', '/datasets/create'].includes(pathname)
|
||||
|
||||
return (
|
||||
<div className={classNames(
|
||||
|
||||
@@ -161,7 +161,7 @@ const Form: FC<FormProps> = ({
|
||||
flex justify-center items-center mr-2 w-4 h-4 border border-gray-300 rounded-full
|
||||
${value[variable] === option.value && 'border-[5px] border-primary-600'}
|
||||
`} />
|
||||
<div className='text-sm text-gray-900'>{option.label[language]}</div>
|
||||
<div className='text-sm text-gray-900'>{option.label[language] || option.label.en_US}</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
@@ -206,9 +206,9 @@ const Form: FC<FormProps> = ({
|
||||
return option.show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value)
|
||||
|
||||
return true
|
||||
}).map(option => ({ value: option.value, name: option.label[language] }))}
|
||||
}).map(option => ({ value: option.value, name: option.label[language] || option.label.en_US }))}
|
||||
onSelect={item => handleFormChange(variable, item.value as string)}
|
||||
placeholder={placeholder?.[language]}
|
||||
placeholder={placeholder?.[language] || placeholder?.en_US}
|
||||
/>
|
||||
{fieldMoreInfo?.(formSchema)}
|
||||
{validating && changeKey === variable && <ValidatingTip />}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user