mirror of
https://github.com/langgenius/dify.git
synced 2026-01-09 07:44:12 +00:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74de7cf33c | ||
|
|
f5e65b98a9 | ||
|
|
eb76d7a226 | ||
|
|
e635f3dc1d | ||
|
|
2a6b7d57cb | ||
|
|
8bb225bec6 | ||
|
|
39d3fc4742 | ||
|
|
5c98260cec | ||
|
|
f599f41336 | ||
|
|
1384a6d0fd | ||
|
|
28089c98c1 | ||
|
|
10d6d50b6c | ||
|
|
752f6fb15a | ||
|
|
8fcf459285 | ||
|
|
9c01bcb3e5 | ||
|
|
a2c068d949 | ||
|
|
4ad3f2cdc2 | ||
|
|
29918c498c | ||
|
|
269432a5e6 | ||
|
|
a33b774314 | ||
|
|
cc5ccaaca1 | ||
|
|
33ea689861 | ||
|
|
581836b716 | ||
|
|
0516b78d6f | ||
|
|
84d7cbf916 | ||
|
|
f514fd2182 | ||
|
|
86707928d4 | ||
|
|
3c3fb3cd3f | ||
|
|
337899a03d | ||
|
|
bae0c071cd | ||
|
|
19cb3c7871 | ||
|
|
2e4dec365d | ||
|
|
ca3e2e6cc0 | ||
|
|
283979fc46 | ||
|
|
a81c1ab6ae | ||
|
|
48d4d55ecc | ||
|
|
b7691f5658 | ||
|
|
1382f10433 | ||
|
|
d8db728c33 | ||
|
|
d2259f20cb | ||
|
|
9720d6b7a5 |
2
.github/workflows/build-push.yml
vendored
2
.github/workflows/build-push.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
||||
with:
|
||||
images: ${{ env[matrix.image_name_env] }}
|
||||
tags: |
|
||||
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' && startsWith(github.ref, 'refs/tags/') }}
|
||||
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
type=ref,event=branch
|
||||
type=sha,enable=true,priority=100,prefix=,suffix=,format=long
|
||||
type=raw,value=${{ github.ref_name }},enable=${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
|
||||
@@ -36,7 +36,7 @@ In terms of licensing, please take a minute to read our short [License and Contr
|
||||
| Feature Type | Priority |
|
||||
| ------------------------------------------------------------ | --------------- |
|
||||
| High-Priority Features as being labeled by a team member | High Priority |
|
||||
| Popular feature requests from our [community feedback board](https://feedback.dify.ai/) | Medium Priority |
|
||||
| Popular feature requests from our [community feedback board](https://github.com/langgenius/dify/discussions/categories/feedbacks) | Medium Priority |
|
||||
| Non-core features and minor enhancements | Low Priority |
|
||||
| Valuable but not immediate | Future-Feature |
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
| Feature Type | Priority |
|
||||
| ------------------------------------------------------------ | --------------- |
|
||||
| High-Priority Features as being labeled by a team member | High Priority |
|
||||
| Popular feature requests from our [community feedback board](https://feedback.dify.ai/) | Medium Priority |
|
||||
| Popular feature requests from our [community feedback board](https://github.com/langgenius/dify/discussions/categories/feedbacks) | Medium Priority |
|
||||
| Non-core features and minor enhancements | Low Priority |
|
||||
| Valuable but not immediate | Future-Feature |
|
||||
|
||||
|
||||
56
README.md
56
README.md
@@ -29,16 +29,35 @@
|
||||
|
||||
**Dify** is an open-source LLM app development platform. Dify's intuitive interface combines a RAG pipeline, AI workflow orchestration, agent capabilities, model management, observability features and more, letting you quickly go from prototype to production.
|
||||
|
||||

|
||||
https://github.com/langgenius/dify/assets/13230914/979e7a68-f067-4bbc-b38e-2deb2cc2bbb5
|
||||
|
||||
|
||||
## Using Dify Cloud
|
||||
|
||||
## Using our Cloud Services
|
||||
You can try out [Dify Cloud](https://dify.ai) now. It provides all the capabilities of the self-deployed version, and includes 200 free GPT-4 calls.
|
||||
|
||||
You can try out [Dify.AI Cloud](https://dify.ai) now. It provides all the capabilities of the self-deployed version, and includes 200 free requests to OpenAI GPT-3.5.
|
||||
## Dify for Enterprise / Organizations
|
||||
|
||||
[Schedule a meeting with us](#Direct-Meetings) or [send us an email](mailto:business@dify.ai?subject=[GitHub]Business%20License%20Inquiry) to discuss enterprise needs.
|
||||
|
||||
For startups and small businesses using AWS, check out [Dify Premium on AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-t22mebxzwjhu6) and deploy it to your own AWS VPC with one-click. It's an affordable AMI offering with the option to create apps with custom logo and branding.
|
||||
|
||||
## Features
|
||||
|
||||

|
||||
|
||||
**1. Workflow**: Create and test complex AI workflows on a visual canvas, with pre-built nodes taking advantage of the power of all the following features and beyond.
|
||||
|
||||
**2. Extensive LLM support**: Seamless integration with hundreds of proprietary / open-source LLMs and dozens of inference providers, including GPT, Mistral, Llama2, and OpenAI API-compatible models. A full list of supported model providers is kept [here](https://docs.dify.ai/getting-started/readme/model-providers).
|
||||
|
||||
**3. Prompt IDE**: Visual orchestration of applications and services based on any LLMs. Easily share with your team.
|
||||
|
||||
**4. RAG Engine**: Includes various RAG capabilities based on full-text indexing or vector database embeddings, allowing direct upload of PDFs, TXTs, and other text formats.
|
||||
|
||||
**5. AI Agent**: Based on Function Calling and ReAct, the Agent inference framework allows users to customize tools, what you see is what you get. Dify provides more than a dozen built-in tools for AI agents, such as Google Search, DELL·E, Stable Diffusion, WolframAlpha, etc.
|
||||
|
||||
**6. LLMOps**: Monitor and analyze application logs and performance, continuously improving Prompts, datasets, or models based on production data.
|
||||
|
||||
### Looking to purchase via AWS?
|
||||
Check out [Dify Premium on AWS](https://aws.amazon.com/marketplace/pp/prodview-t22mebxzwjhu6) and deploy it to your own AWS VPC with one-click.
|
||||
|
||||
## Dify vs. LangChain vs. Assistants API
|
||||
|
||||
@@ -52,28 +71,10 @@ Check out [Dify Premium on AWS](https://aws.amazon.com/marketplace/pp/prodview-t
|
||||
| **Local Deployment** | Supported | Not Supported | Not Applicable |
|
||||
|
||||
|
||||
|
||||
## Features
|
||||
|
||||

|
||||
|
||||
**1. LLM Support**: Integration with OpenAI's GPT family of models, or the open-source Llama2 family models. In fact, Dify supports mainstream commercial models and open-source models (locally deployed or based on MaaS).
|
||||
|
||||
**2. Prompt IDE**: Visual orchestration of applications and services based on LLMs with your team.
|
||||
|
||||
**3. RAG Engine**: Includes various RAG capabilities based on full-text indexing or vector database embeddings, allowing direct upload of PDFs, TXTs, and other text formats.
|
||||
|
||||
**4. AI Agent**: Based on Function Calling and ReAct, the Agent inference framework allows users to customize tools, what you see is what you get. Dify provides more than a dozen built-in tool calling capabilities, such as Google Search, DELL·E, Stable Diffusion, WolframAlpha, etc.
|
||||
|
||||
|
||||
**5. Continuous Operations**: Monitor and analyze application logs and performance, continuously improving Prompts, datasets, or models using production data.
|
||||
|
||||
## Before You Start
|
||||
|
||||
**Star us on GitHub, and be instantly notified for new releases!**
|
||||
|
||||

|
||||
|
||||
- [Website](https://dify.ai)
|
||||
- [Docs](https://docs.dify.ai)
|
||||
- [Deployment Docs](https://docs.dify.ai/getting-started/install-self-hosted)
|
||||
@@ -138,21 +139,18 @@ We are looking for contributors to help with translating Dify to languages other
|
||||
|
||||
## Community & Support
|
||||
|
||||
* [Github Discussion](https://github.com/langgenius/dify/discussions). Best for: sharing feedback and checking out our feature roadmap.
|
||||
* [Github Discussion](https://github.com/langgenius/dify/discussions). Best for: sharing feedback and asking questions.
|
||||
* [GitHub Issues](https://github.com/langgenius/dify/issues). Best for: bugs you encounter using Dify.AI, and feature proposals. See our [Contribution Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
|
||||
* [Email Support](mailto:hello@dify.ai?subject=[GitHub]Questions%20About%20Dify). Best for: questions you have about using Dify.AI.
|
||||
* [Discord](https://discord.gg/FngNHpbcY7). Best for: sharing your applications and hanging out with the community.
|
||||
* [Twitter](https://twitter.com/dify_ai). Best for: sharing your applications and hanging out with the community.
|
||||
* [Business Contact](mailto:business@dify.ai?subject=[GitHub]Business%20License%20Inquiry). Best for: business inquiries of licensing Dify.AI for commercial use.
|
||||
|
||||
### Direct Meetings
|
||||
|
||||
**Help us make Dify better. Reach out directly to us**.
|
||||
|
||||
| Point of Contact | Purpose |
|
||||
| :----------------------------------------------------------: | :----------------------------------------------------------: |
|
||||
| <a href='https://cal.com/guchenhe/15min' target='_blank'><img src='https://i.postimg.cc/fWBqSmjP/Git-Hub-README-Button-3x.png' border='0' alt='Git-Hub-README-Button-3x' height="60" width="214"/></a> | Product design feedback, user experience discussions, feature planning and roadmaps. |
|
||||
| <a href='https://cal.com/pinkbanana' target='_blank'><img src='https://i.postimg.cc/LsRTh87D/Git-Hub-README-Button-2x.png' border='0' alt='Git-Hub-README-Button-2x' height="60" width="225"/></a> | Technical support, issues, or feature requests |
|
||||
| <a href='https://cal.com/guchenhe/15min' target='_blank'><img src='https://i.postimg.cc/fWBqSmjP/Git-Hub-README-Button-3x.png' border='0' alt='Git-Hub-README-Button-3x' height="60" width="214"/></a> | Business enquiries & product feedback. |
|
||||
| <a href='https://cal.com/pinkbanana' target='_blank'><img src='https://i.postimg.cc/LsRTh87D/Git-Hub-README-Button-2x.png' border='0' alt='Git-Hub-README-Button-2x' height="60" width="225"/></a> | Contributions, issues & feature requests |
|
||||
|
||||
## Security Disclosure
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ docker compose up -d
|
||||
|
||||
Difyに貢献していただき、コードの提出、問題の報告、新しいアイデアの提供、またはDifyを基に作成した興味深く有用なAIアプリケーションの共有により、Difyをより良いものにするお手伝いを歓迎します。同時に、さまざまなイベント、会議、ソーシャルメディアでDifyを共有することも歓迎します。
|
||||
|
||||
- [Github Discussion](https://github.com/langgenius/dify/discussions). 👉:アプリを共有し、コミュニティとコミュニケーション。
|
||||
- [GitHub Issues](https://github.com/langgenius/dify/issues)。最適な使用法:Dify.AIの使用中に遭遇するバグやエラー、[貢献ガイド](CONTRIBUTING.md)を参照。
|
||||
- [Email サポート](mailto:hello@dify.ai?subject=[GitHub]Questions%20About%20Dify)。最適な使用法:Dify.AIの使用に関する質問。
|
||||
- [Discord](https://discord.gg/FngNHpbcY7)。最適な使用法:アプリケーションの共有とコミュニティとの交流。
|
||||
|
||||
@@ -17,16 +17,16 @@
|
||||
```bash
|
||||
sed -i "/^SECRET_KEY=/c\SECRET_KEY=$(openssl rand -base64 42)" .env
|
||||
```
|
||||
3.5 If you use Anaconda, create a new environment and activate it
|
||||
4. If you use Anaconda, create a new environment and activate it
|
||||
```bash
|
||||
conda create --name dify python=3.10
|
||||
conda activate dify
|
||||
```
|
||||
4. Install dependencies
|
||||
5. Install dependencies
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
5. Run migrate
|
||||
6. Run migrate
|
||||
|
||||
Before the first launch, migrate the database to the latest version.
|
||||
|
||||
@@ -47,9 +47,11 @@
|
||||
pip install -r requirements.txt --upgrade --force-reinstall
|
||||
```
|
||||
|
||||
6. Start backend:
|
||||
7. Start backend:
|
||||
```bash
|
||||
flask run --host 0.0.0.0 --port=5001 --debug
|
||||
```
|
||||
7. Setup your application by visiting http://localhost:5001/console/api/setup or other apis...
|
||||
8. If you need to debug local async processing, you can run `celery -A app.celery worker -P gevent -c 1 --loglevel INFO -Q dataset,generation,mail`, celery can do dataset importing and other async tasks.
|
||||
8. Setup your application by visiting http://localhost:5001/console/api/setup or other apis...
|
||||
9. If you need to debug local async processing, please start the worker service by running
|
||||
`celery -A app.celery worker -P gevent -c 1 --loglevel INFO -Q dataset,generation,mail`.
|
||||
The started celery app handles the async tasks, e.g. dataset importing and documents indexing.
|
||||
|
||||
@@ -67,6 +67,7 @@ DEFAULTS = {
|
||||
'CODE_EXECUTION_ENDPOINT': '',
|
||||
'CODE_EXECUTION_API_KEY': '',
|
||||
'TOOL_ICON_CACHE_MAX_AGE': 3600,
|
||||
'MILVUS_DATABASE': 'default',
|
||||
'KEYWORD_DATA_SOURCE_TYPE': 'database',
|
||||
}
|
||||
|
||||
@@ -98,7 +99,7 @@ class Config:
|
||||
# ------------------------
|
||||
# General Configurations.
|
||||
# ------------------------
|
||||
self.CURRENT_VERSION = "0.6.0"
|
||||
self.CURRENT_VERSION = "0.6.1"
|
||||
self.COMMIT_SHA = get_env('COMMIT_SHA')
|
||||
self.EDITION = "SELF_HOSTED"
|
||||
self.DEPLOY_ENV = get_env('DEPLOY_ENV')
|
||||
@@ -212,6 +213,7 @@ class Config:
|
||||
self.MILVUS_USER = get_env('MILVUS_USER')
|
||||
self.MILVUS_PASSWORD = get_env('MILVUS_PASSWORD')
|
||||
self.MILVUS_SECURE = get_env('MILVUS_SECURE')
|
||||
self.MILVUS_DATABASE = get_env('MILVUS_DATABASE')
|
||||
|
||||
# weaviate settings
|
||||
self.WEAVIATE_ENDPOINT = get_env('WEAVIATE_ENDPOINT')
|
||||
|
||||
@@ -6,6 +6,7 @@ from werkzeug.exceptions import NotFound
|
||||
from controllers.console import api
|
||||
from controllers.console.explore.error import NotChatAppError
|
||||
from controllers.console.explore.wraps import InstalledAppResource
|
||||
from core.app.entities.app_invoke_entities import InvokeFrom
|
||||
from fields.conversation_fields import conversation_infinite_scroll_pagination_fields, simple_conversation_fields
|
||||
from libs.helper import uuid_value
|
||||
from models.model import AppMode
|
||||
@@ -39,8 +40,8 @@ class ConversationListApi(InstalledAppResource):
|
||||
user=current_user,
|
||||
last_id=args['last_id'],
|
||||
limit=args['limit'],
|
||||
invoke_from=InvokeFrom.EXPLORE,
|
||||
pinned=pinned,
|
||||
exclude_debug_conversation=True
|
||||
)
|
||||
except LastConversationNotExistsError:
|
||||
raise NotFound("Last Conversation Not Exists.")
|
||||
|
||||
@@ -6,6 +6,7 @@ import services
|
||||
from controllers.service_api import api
|
||||
from controllers.service_api.app.error import NotChatAppError
|
||||
from controllers.service_api.wraps import FetchUserArg, WhereisUserArg, validate_app_token
|
||||
from core.app.entities.app_invoke_entities import InvokeFrom
|
||||
from fields.conversation_fields import conversation_infinite_scroll_pagination_fields, simple_conversation_fields
|
||||
from libs.helper import uuid_value
|
||||
from models.model import App, AppMode, EndUser
|
||||
@@ -27,7 +28,13 @@ class ConversationApi(Resource):
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
return ConversationService.pagination_by_last_id(app_model, end_user, args['last_id'], args['limit'])
|
||||
return ConversationService.pagination_by_last_id(
|
||||
app_model=app_model,
|
||||
user=end_user,
|
||||
last_id=args['last_id'],
|
||||
limit=args['limit'],
|
||||
invoke_from=InvokeFrom.SERVICE_API
|
||||
)
|
||||
except services.errors.conversation.LastConversationNotExistsError:
|
||||
raise NotFound("Last Conversation Not Exists.")
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from werkzeug.exceptions import NotFound
|
||||
from controllers.web import api
|
||||
from controllers.web.error import NotChatAppError
|
||||
from controllers.web.wraps import WebApiResource
|
||||
from core.app.entities.app_invoke_entities import InvokeFrom
|
||||
from fields.conversation_fields import conversation_infinite_scroll_pagination_fields, simple_conversation_fields
|
||||
from libs.helper import uuid_value
|
||||
from models.model import AppMode
|
||||
@@ -37,7 +38,8 @@ class ConversationListApi(WebApiResource):
|
||||
user=end_user,
|
||||
last_id=args['last_id'],
|
||||
limit=args['limit'],
|
||||
pinned=pinned
|
||||
invoke_from=InvokeFrom.WEB_APP,
|
||||
pinned=pinned,
|
||||
)
|
||||
except LastConversationNotExistsError:
|
||||
raise NotFound("Last Conversation Not Exists.")
|
||||
|
||||
@@ -687,4 +687,4 @@ class CotAgentRunner(BaseAgentRunner):
|
||||
try:
|
||||
return json.dumps(tools, ensure_ascii=False)
|
||||
except json.JSONDecodeError:
|
||||
return json.dumps(tools)
|
||||
return json.dumps(tools)
|
||||
@@ -207,19 +207,25 @@ class FunctionCallAgentRunner(BaseAgentRunner):
|
||||
)
|
||||
)
|
||||
|
||||
assistant_message = AssistantPromptMessage(
|
||||
content='',
|
||||
tool_calls=[]
|
||||
)
|
||||
if tool_calls:
|
||||
prompt_messages.append(AssistantPromptMessage(
|
||||
content='',
|
||||
name='',
|
||||
tool_calls=[AssistantPromptMessage.ToolCall(
|
||||
assistant_message.tool_calls=[
|
||||
AssistantPromptMessage.ToolCall(
|
||||
id=tool_call[0],
|
||||
type='function',
|
||||
function=AssistantPromptMessage.ToolCall.ToolCallFunction(
|
||||
name=tool_call[1],
|
||||
arguments=json.dumps(tool_call[2], ensure_ascii=False)
|
||||
)
|
||||
) for tool_call in tool_calls]
|
||||
))
|
||||
) for tool_call in tool_calls
|
||||
]
|
||||
else:
|
||||
assistant_message.content = response
|
||||
|
||||
prompt_messages.append(assistant_message)
|
||||
|
||||
# save thought
|
||||
self.save_agent_thought(
|
||||
@@ -239,12 +245,6 @@ class FunctionCallAgentRunner(BaseAgentRunner):
|
||||
|
||||
final_answer += response + '\n'
|
||||
|
||||
# update prompt messages
|
||||
if response.strip():
|
||||
prompt_messages.append(AssistantPromptMessage(
|
||||
content=response,
|
||||
))
|
||||
|
||||
# call tools
|
||||
tool_responses = []
|
||||
for tool_call_id, tool_call_name, tool_call_args in tool_calls:
|
||||
|
||||
@@ -122,7 +122,7 @@ class MessageEndStreamResponse(StreamResponse):
|
||||
"""
|
||||
event: StreamEvent = StreamEvent.MESSAGE_END
|
||||
id: str
|
||||
metadata: Optional[dict] = None
|
||||
metadata: dict = {}
|
||||
|
||||
|
||||
class MessageFileStreamResponse(StreamResponse):
|
||||
|
||||
@@ -1,12 +1,32 @@
|
||||
import os
|
||||
from typing import Any, Optional, Union
|
||||
from typing import Any, Optional, TextIO, Union
|
||||
|
||||
from langchain.callbacks.base import BaseCallbackHandler
|
||||
from langchain.input import print_text
|
||||
from pydantic import BaseModel
|
||||
|
||||
_TEXT_COLOR_MAPPING = {
|
||||
"blue": "36;1",
|
||||
"yellow": "33;1",
|
||||
"pink": "38;5;200",
|
||||
"green": "32;1",
|
||||
"red": "31;1",
|
||||
}
|
||||
|
||||
class DifyAgentCallbackHandler(BaseCallbackHandler, BaseModel):
|
||||
def get_colored_text(text: str, color: str) -> str:
|
||||
"""Get colored text."""
|
||||
color_str = _TEXT_COLOR_MAPPING[color]
|
||||
return f"\u001b[{color_str}m\033[1;3m{text}\u001b[0m"
|
||||
|
||||
|
||||
def print_text(
|
||||
text: str, color: Optional[str] = None, end: str = "", file: Optional[TextIO] = None
|
||||
) -> None:
|
||||
"""Print text with highlighting and no end characters."""
|
||||
text_to_print = get_colored_text(text, color) if color else text
|
||||
print(text_to_print, end=end, file=file)
|
||||
if file:
|
||||
file.flush() # ensure all printed content are written to file
|
||||
|
||||
class DifyAgentCallbackHandler(BaseModel):
|
||||
"""Callback Handler that prints to std out."""
|
||||
color: Optional[str] = ''
|
||||
current_loop = 1
|
||||
|
||||
@@ -41,7 +41,8 @@ class CacheEmbedding(Embeddings):
|
||||
embedding_queue_embeddings = []
|
||||
try:
|
||||
model_type_instance = cast(TextEmbeddingModel, self._model_instance.model_type_instance)
|
||||
model_schema = model_type_instance.get_model_schema(self._model_instance.model, self._model_instance.credentials)
|
||||
model_schema = model_type_instance.get_model_schema(self._model_instance.model,
|
||||
self._model_instance.credentials)
|
||||
max_chunks = model_schema.model_properties[ModelPropertyKey.MAX_CHUNKS] \
|
||||
if model_schema and ModelPropertyKey.MAX_CHUNKS in model_schema.model_properties else 1
|
||||
for i in range(0, len(embedding_queue_texts), max_chunks):
|
||||
@@ -61,17 +62,20 @@ class CacheEmbedding(Embeddings):
|
||||
except Exception as e:
|
||||
logging.exception('Failed transform embedding: ', e)
|
||||
cache_embeddings = []
|
||||
for i, embedding in zip(embedding_queue_indices, embedding_queue_embeddings):
|
||||
text_embeddings[i] = embedding
|
||||
hash = helper.generate_text_hash(texts[i])
|
||||
if hash not in cache_embeddings:
|
||||
embedding_cache = Embedding(model_name=self._model_instance.model,
|
||||
hash=hash,
|
||||
provider_name=self._model_instance.provider)
|
||||
embedding_cache.set_embedding(embedding)
|
||||
db.session.add(embedding_cache)
|
||||
cache_embeddings.append(hash)
|
||||
db.session.commit()
|
||||
try:
|
||||
for i, embedding in zip(embedding_queue_indices, embedding_queue_embeddings):
|
||||
text_embeddings[i] = embedding
|
||||
hash = helper.generate_text_hash(texts[i])
|
||||
if hash not in cache_embeddings:
|
||||
embedding_cache = Embedding(model_name=self._model_instance.model,
|
||||
hash=hash,
|
||||
provider_name=self._model_instance.provider)
|
||||
embedding_cache.set_embedding(embedding)
|
||||
db.session.add(embedding_cache)
|
||||
cache_embeddings.append(hash)
|
||||
db.session.commit()
|
||||
except IntegrityError:
|
||||
db.session.rollback()
|
||||
except Exception as ex:
|
||||
db.session.rollback()
|
||||
logger.error('Failed to embed documents: ', ex)
|
||||
|
||||
@@ -29,16 +29,16 @@ class NodeJsTemplateTransformer(TemplateTransformer):
|
||||
:param inputs: inputs
|
||||
:return:
|
||||
"""
|
||||
|
||||
|
||||
# transform inputs to json string
|
||||
inputs_str = json.dumps(inputs, indent=4)
|
||||
inputs_str = json.dumps(inputs, indent=4, ensure_ascii=False)
|
||||
|
||||
# replace code and inputs
|
||||
runner = NODEJS_RUNNER.replace('{{code}}', code)
|
||||
runner = runner.replace('{{inputs}}', inputs_str)
|
||||
|
||||
return runner, NODEJS_PRELOAD
|
||||
|
||||
|
||||
@classmethod
|
||||
def transform_response(cls, response: str) -> dict:
|
||||
"""
|
||||
|
||||
@@ -62,10 +62,10 @@ class Jinja2TemplateTransformer(TemplateTransformer):
|
||||
|
||||
# transform jinja2 template to python code
|
||||
runner = PYTHON_RUNNER.replace('{{code}}', code)
|
||||
runner = runner.replace('{{inputs}}', json.dumps(inputs, indent=4))
|
||||
runner = runner.replace('{{inputs}}', json.dumps(inputs, indent=4, ensure_ascii=False))
|
||||
|
||||
return runner, JINJA2_PRELOAD
|
||||
|
||||
|
||||
@classmethod
|
||||
def transform_response(cls, response: str) -> dict:
|
||||
"""
|
||||
@@ -81,4 +81,4 @@ class Jinja2TemplateTransformer(TemplateTransformer):
|
||||
|
||||
return {
|
||||
'result': result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class PythonTemplateTransformer(TemplateTransformer):
|
||||
"""
|
||||
|
||||
# transform inputs to json string
|
||||
inputs_str = json.dumps(inputs, indent=4)
|
||||
inputs_str = json.dumps(inputs, indent=4, ensure_ascii=False)
|
||||
|
||||
# replace code and inputs
|
||||
runner = PYTHON_RUNNER.replace('{{code}}', code)
|
||||
|
||||
@@ -19,6 +19,7 @@ from core.model_manager import ModelInstance, ModelManager
|
||||
from core.model_runtime.entities.model_entities import ModelType, PriceType
|
||||
from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel
|
||||
from core.model_runtime.model_providers.__base.text_embedding_model import TextEmbeddingModel
|
||||
from core.rag.datasource.keyword.keyword_factory import Keyword
|
||||
from core.rag.extractor.entity.extract_setting import ExtractSetting
|
||||
from core.rag.index_processor.index_processor_base import BaseIndexProcessor
|
||||
from core.rag.index_processor.index_processor_factory import IndexProcessorFactory
|
||||
@@ -657,18 +658,25 @@ class IndexingRunner:
|
||||
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))
|
||||
# create keyword index
|
||||
create_keyword_thread = threading.Thread(target=self._process_keyword_index,
|
||||
args=(current_app._get_current_object(),
|
||||
dataset.id, dataset_document.id, documents))
|
||||
create_keyword_thread.start()
|
||||
if dataset.indexing_technique == 'high_quality':
|
||||
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 future in futures:
|
||||
tokens += future.result()
|
||||
for future in futures:
|
||||
tokens += future.result()
|
||||
|
||||
create_keyword_thread.join()
|
||||
indexing_end_at = time.perf_counter()
|
||||
|
||||
# update document status to completed
|
||||
@@ -682,6 +690,27 @@ class IndexingRunner:
|
||||
}
|
||||
)
|
||||
|
||||
def _process_keyword_index(self, flask_app, dataset_id, document_id, documents):
|
||||
with flask_app.app_context():
|
||||
dataset = Dataset.query.filter_by(id=dataset_id).first()
|
||||
if not dataset:
|
||||
raise ValueError("no dataset found")
|
||||
keyword = Keyword(dataset)
|
||||
keyword.create(documents)
|
||||
if dataset.indexing_technique != 'high_quality':
|
||||
document_ids = [document.metadata['doc_id'] for document in documents]
|
||||
db.session.query(DocumentSegment).filter(
|
||||
DocumentSegment.document_id == document_id,
|
||||
DocumentSegment.index_node_id.in_(document_ids),
|
||||
DocumentSegment.status == "indexing"
|
||||
).update({
|
||||
DocumentSegment.status: "completed",
|
||||
DocumentSegment.enabled: True,
|
||||
DocumentSegment.completed_at: datetime.datetime.utcnow()
|
||||
})
|
||||
|
||||
db.session.commit()
|
||||
|
||||
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():
|
||||
@@ -700,7 +729,7 @@ class IndexingRunner:
|
||||
)
|
||||
|
||||
# load index
|
||||
index_processor.load(dataset, chunk_documents)
|
||||
index_processor.load(dataset, chunk_documents, with_keywords=False)
|
||||
|
||||
document_ids = [document.metadata['doc_id'] for document in chunk_documents]
|
||||
db.session.query(DocumentSegment).filter(
|
||||
|
||||
@@ -3,6 +3,7 @@ from core.file.message_file_parser import MessageFileParser
|
||||
from core.model_manager import ModelInstance
|
||||
from core.model_runtime.entities.message_entities import (
|
||||
AssistantPromptMessage,
|
||||
ImagePromptMessageContent,
|
||||
PromptMessage,
|
||||
PromptMessageRole,
|
||||
TextPromptMessageContent,
|
||||
@@ -124,7 +125,17 @@ class TokenBufferMemory:
|
||||
else:
|
||||
continue
|
||||
|
||||
message = f"{role}: {m.content}"
|
||||
string_messages.append(message)
|
||||
if isinstance(m.content, list):
|
||||
inner_msg = ""
|
||||
for content in m.content:
|
||||
if isinstance(content, TextPromptMessageContent):
|
||||
inner_msg += f"{content.data}\n"
|
||||
elif isinstance(content, ImagePromptMessageContent):
|
||||
inner_msg += "[image]\n"
|
||||
|
||||
string_messages.append(f"{role}: {inner_msg.strip()}")
|
||||
else:
|
||||
message = f"{role}: {m.content}"
|
||||
string_messages.append(message)
|
||||
|
||||
return "\n".join(string_messages)
|
||||
@@ -74,7 +74,7 @@ provider_credential_schema:
|
||||
label:
|
||||
en_US: Available Model Name
|
||||
zh_Hans: 可用模型名称
|
||||
type: secret-input
|
||||
type: text-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)
|
||||
|
||||
@@ -402,25 +402,25 @@ class BedrockLargeLanguageModel(LargeLanguageModel):
|
||||
:param credentials: model credentials
|
||||
:return:
|
||||
"""
|
||||
|
||||
if "anthropic.claude-3" in model:
|
||||
try:
|
||||
self._invoke_claude(model=model,
|
||||
credentials=credentials,
|
||||
prompt_messages=[{"role": "user", "content": "ping"}],
|
||||
model_parameters={},
|
||||
stop=None,
|
||||
stream=False)
|
||||
|
||||
except Exception as ex:
|
||||
raise CredentialsValidateFailedError(str(ex))
|
||||
|
||||
required_params = {}
|
||||
if "anthropic" in model:
|
||||
required_params = {
|
||||
"max_tokens": 32,
|
||||
}
|
||||
elif "ai21" in model:
|
||||
# ValidationException: Malformed input request: #/temperature: expected type: Number, found: Null#/maxTokens: expected type: Integer, found: Null#/topP: expected type: Number, found: Null, please reformat your input and try again.
|
||||
required_params = {
|
||||
"temperature": 0.7,
|
||||
"topP": 0.9,
|
||||
"maxTokens": 32,
|
||||
}
|
||||
|
||||
try:
|
||||
ping_message = UserPromptMessage(content="ping")
|
||||
self._generate(model=model,
|
||||
self._invoke(model=model,
|
||||
credentials=credentials,
|
||||
prompt_messages=[ping_message],
|
||||
model_parameters={},
|
||||
model_parameters=required_params,
|
||||
stream=False)
|
||||
|
||||
except ClientError as ex:
|
||||
|
||||
@@ -297,7 +297,6 @@ class CohereLargeLanguageModel(LargeLanguageModel):
|
||||
chat_history=chat_histories,
|
||||
model=real_model,
|
||||
stream=stream,
|
||||
return_preamble=True,
|
||||
**model_parameters,
|
||||
)
|
||||
|
||||
|
||||
@@ -1,8 +1,31 @@
|
||||
import json
|
||||
from collections.abc import Generator
|
||||
from typing import Optional, Union
|
||||
from typing import Optional, Union, cast
|
||||
|
||||
from core.model_runtime.entities.llm_entities import LLMResult
|
||||
from core.model_runtime.entities.message_entities import PromptMessage, PromptMessageTool
|
||||
import requests
|
||||
|
||||
from core.model_runtime.entities.common_entities import I18nObject
|
||||
from core.model_runtime.entities.llm_entities import LLMMode, LLMResult, LLMResultChunk, LLMResultChunkDelta
|
||||
from core.model_runtime.entities.message_entities import (
|
||||
AssistantPromptMessage,
|
||||
ImagePromptMessageContent,
|
||||
PromptMessage,
|
||||
PromptMessageContent,
|
||||
PromptMessageContentType,
|
||||
PromptMessageTool,
|
||||
SystemPromptMessage,
|
||||
ToolPromptMessage,
|
||||
UserPromptMessage,
|
||||
)
|
||||
from core.model_runtime.entities.model_entities import (
|
||||
AIModelEntity,
|
||||
FetchFrom,
|
||||
ModelFeature,
|
||||
ModelPropertyKey,
|
||||
ModelType,
|
||||
ParameterRule,
|
||||
ParameterType,
|
||||
)
|
||||
from core.model_runtime.model_providers.openai_api_compatible.llm.llm import OAIAPICompatLargeLanguageModel
|
||||
|
||||
|
||||
@@ -13,6 +36,7 @@ class MoonshotLargeLanguageModel(OAIAPICompatLargeLanguageModel):
|
||||
stream: bool = True, user: Optional[str] = None) \
|
||||
-> Union[LLMResult, Generator]:
|
||||
self._add_custom_parameters(credentials)
|
||||
self._add_function_call(model, credentials)
|
||||
user = user[:32] if user else None
|
||||
return super()._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user)
|
||||
|
||||
@@ -20,7 +44,293 @@ class MoonshotLargeLanguageModel(OAIAPICompatLargeLanguageModel):
|
||||
self._add_custom_parameters(credentials)
|
||||
super().validate_credentials(model, credentials)
|
||||
|
||||
@staticmethod
|
||||
def _add_custom_parameters(credentials: dict) -> None:
|
||||
def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None:
|
||||
return AIModelEntity(
|
||||
model=model,
|
||||
label=I18nObject(en_US=model, zh_Hans=model),
|
||||
model_type=ModelType.LLM,
|
||||
features=[ModelFeature.TOOL_CALL, ModelFeature.MULTI_TOOL_CALL, ModelFeature.STREAM_TOOL_CALL]
|
||||
if credentials.get('function_calling_type') == 'tool_call'
|
||||
else [],
|
||||
fetch_from=FetchFrom.CUSTOMIZABLE_MODEL,
|
||||
model_properties={
|
||||
ModelPropertyKey.CONTEXT_SIZE: int(credentials.get('context_size', 4096)),
|
||||
ModelPropertyKey.MODE: LLMMode.CHAT.value,
|
||||
},
|
||||
parameter_rules=[
|
||||
ParameterRule(
|
||||
name='temperature',
|
||||
use_template='temperature',
|
||||
label=I18nObject(en_US='Temperature', zh_Hans='温度'),
|
||||
type=ParameterType.FLOAT,
|
||||
),
|
||||
ParameterRule(
|
||||
name='max_tokens',
|
||||
use_template='max_tokens',
|
||||
default=512,
|
||||
min=1,
|
||||
max=int(credentials.get('max_tokens', 4096)),
|
||||
label=I18nObject(en_US='Max Tokens', zh_Hans='最大标记'),
|
||||
type=ParameterType.INT,
|
||||
),
|
||||
ParameterRule(
|
||||
name='top_p',
|
||||
use_template='top_p',
|
||||
label=I18nObject(en_US='Top P', zh_Hans='Top P'),
|
||||
type=ParameterType.FLOAT,
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
def _add_custom_parameters(self, credentials: dict) -> None:
|
||||
credentials['mode'] = 'chat'
|
||||
credentials['endpoint_url'] = 'https://api.moonshot.cn/v1'
|
||||
|
||||
def _add_function_call(self, model: str, credentials: dict) -> None:
|
||||
model_schema = self.get_model_schema(model, credentials)
|
||||
if model_schema and set([
|
||||
ModelFeature.TOOL_CALL, ModelFeature.MULTI_TOOL_CALL
|
||||
]).intersection(model_schema.features or []):
|
||||
credentials['function_calling_type'] = 'tool_call'
|
||||
|
||||
def _convert_prompt_message_to_dict(self, message: PromptMessage) -> dict:
|
||||
"""
|
||||
Convert PromptMessage to dict for OpenAI API format
|
||||
"""
|
||||
if isinstance(message, UserPromptMessage):
|
||||
message = cast(UserPromptMessage, message)
|
||||
if isinstance(message.content, str):
|
||||
message_dict = {"role": "user", "content": message.content}
|
||||
else:
|
||||
sub_messages = []
|
||||
for message_content in message.content:
|
||||
if message_content.type == PromptMessageContentType.TEXT:
|
||||
message_content = cast(PromptMessageContent, message_content)
|
||||
sub_message_dict = {
|
||||
"type": "text",
|
||||
"text": message_content.data
|
||||
}
|
||||
sub_messages.append(sub_message_dict)
|
||||
elif message_content.type == PromptMessageContentType.IMAGE:
|
||||
message_content = cast(ImagePromptMessageContent, message_content)
|
||||
sub_message_dict = {
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": message_content.data,
|
||||
"detail": message_content.detail.value
|
||||
}
|
||||
}
|
||||
sub_messages.append(sub_message_dict)
|
||||
message_dict = {"role": "user", "content": sub_messages}
|
||||
elif isinstance(message, AssistantPromptMessage):
|
||||
message = cast(AssistantPromptMessage, message)
|
||||
message_dict = {"role": "assistant", "content": message.content}
|
||||
if message.tool_calls:
|
||||
message_dict["tool_calls"] = []
|
||||
for function_call in message.tool_calls:
|
||||
message_dict["tool_calls"].append({
|
||||
"id": function_call.id,
|
||||
"type": function_call.type,
|
||||
"function": {
|
||||
"name": f"functions.{function_call.function.name}",
|
||||
"arguments": function_call.function.arguments
|
||||
}
|
||||
})
|
||||
elif isinstance(message, ToolPromptMessage):
|
||||
message = cast(ToolPromptMessage, message)
|
||||
message_dict = {"role": "tool", "content": message.content, "tool_call_id": message.tool_call_id}
|
||||
if not message.name.startswith("functions."):
|
||||
message.name = f"functions.{message.name}"
|
||||
elif isinstance(message, SystemPromptMessage):
|
||||
message = cast(SystemPromptMessage, message)
|
||||
message_dict = {"role": "system", "content": message.content}
|
||||
else:
|
||||
raise ValueError(f"Got unknown type {message}")
|
||||
|
||||
if message.name:
|
||||
message_dict["name"] = message.name
|
||||
|
||||
return message_dict
|
||||
|
||||
def _extract_response_tool_calls(self, response_tool_calls: list[dict]) -> list[AssistantPromptMessage.ToolCall]:
|
||||
"""
|
||||
Extract tool calls from response
|
||||
|
||||
:param response_tool_calls: response tool calls
|
||||
:return: list of tool calls
|
||||
"""
|
||||
tool_calls = []
|
||||
if response_tool_calls:
|
||||
for response_tool_call in response_tool_calls:
|
||||
function = AssistantPromptMessage.ToolCall.ToolCallFunction(
|
||||
name=response_tool_call["function"]["name"] if response_tool_call.get("function", {}).get("name") else "",
|
||||
arguments=response_tool_call["function"]["arguments"] if response_tool_call.get("function", {}).get("arguments") else ""
|
||||
)
|
||||
|
||||
tool_call = AssistantPromptMessage.ToolCall(
|
||||
id=response_tool_call["id"] if response_tool_call.get("id") else "",
|
||||
type=response_tool_call["type"] if response_tool_call.get("type") else "",
|
||||
function=function
|
||||
)
|
||||
tool_calls.append(tool_call)
|
||||
|
||||
return tool_calls
|
||||
|
||||
def _handle_generate_stream_response(self, model: str, credentials: dict, response: requests.Response,
|
||||
prompt_messages: list[PromptMessage]) -> Generator:
|
||||
"""
|
||||
Handle llm stream response
|
||||
|
||||
:param model: model name
|
||||
:param credentials: model credentials
|
||||
:param response: streamed response
|
||||
:param prompt_messages: prompt messages
|
||||
:return: llm response chunk generator
|
||||
"""
|
||||
full_assistant_content = ''
|
||||
chunk_index = 0
|
||||
|
||||
def create_final_llm_result_chunk(index: int, message: AssistantPromptMessage, finish_reason: str) \
|
||||
-> LLMResultChunk:
|
||||
# calculate num tokens
|
||||
prompt_tokens = self._num_tokens_from_string(model, prompt_messages[0].content)
|
||||
completion_tokens = self._num_tokens_from_string(model, full_assistant_content)
|
||||
|
||||
# transform usage
|
||||
usage = self._calc_response_usage(model, credentials, prompt_tokens, completion_tokens)
|
||||
|
||||
return LLMResultChunk(
|
||||
model=model,
|
||||
prompt_messages=prompt_messages,
|
||||
delta=LLMResultChunkDelta(
|
||||
index=index,
|
||||
message=message,
|
||||
finish_reason=finish_reason,
|
||||
usage=usage
|
||||
)
|
||||
)
|
||||
|
||||
tools_calls: list[AssistantPromptMessage.ToolCall] = []
|
||||
finish_reason = "Unknown"
|
||||
|
||||
def increase_tool_call(new_tool_calls: list[AssistantPromptMessage.ToolCall]):
|
||||
def get_tool_call(tool_name: str):
|
||||
if not tool_name:
|
||||
return tools_calls[-1]
|
||||
|
||||
tool_call = next((tool_call for tool_call in tools_calls if tool_call.function.name == tool_name), None)
|
||||
if tool_call is None:
|
||||
tool_call = AssistantPromptMessage.ToolCall(
|
||||
id='',
|
||||
type='',
|
||||
function=AssistantPromptMessage.ToolCall.ToolCallFunction(name=tool_name, arguments="")
|
||||
)
|
||||
tools_calls.append(tool_call)
|
||||
|
||||
return tool_call
|
||||
|
||||
for new_tool_call in new_tool_calls:
|
||||
# get tool call
|
||||
tool_call = get_tool_call(new_tool_call.function.name)
|
||||
# update tool call
|
||||
if new_tool_call.id:
|
||||
tool_call.id = new_tool_call.id
|
||||
if new_tool_call.type:
|
||||
tool_call.type = new_tool_call.type
|
||||
if new_tool_call.function.name:
|
||||
# remove the functions. prefix
|
||||
if new_tool_call.function.name.startswith('functions.'):
|
||||
parts = new_tool_call.function.name.split('functions.')
|
||||
if len(parts) > 1:
|
||||
new_tool_call.function.name = parts[1]
|
||||
tool_call.function.name = new_tool_call.function.name
|
||||
if new_tool_call.function.arguments:
|
||||
tool_call.function.arguments += new_tool_call.function.arguments
|
||||
|
||||
for chunk in response.iter_lines(decode_unicode=True, delimiter="\n\n"):
|
||||
if chunk:
|
||||
# ignore sse comments
|
||||
if chunk.startswith(':'):
|
||||
continue
|
||||
decoded_chunk = chunk.strip().lstrip('data: ').lstrip()
|
||||
chunk_json = None
|
||||
try:
|
||||
chunk_json = json.loads(decoded_chunk)
|
||||
# stream ended
|
||||
except json.JSONDecodeError as e:
|
||||
yield create_final_llm_result_chunk(
|
||||
index=chunk_index + 1,
|
||||
message=AssistantPromptMessage(content=""),
|
||||
finish_reason="Non-JSON encountered."
|
||||
)
|
||||
break
|
||||
if not chunk_json or len(chunk_json['choices']) == 0:
|
||||
continue
|
||||
|
||||
choice = chunk_json['choices'][0]
|
||||
finish_reason = chunk_json['choices'][0].get('finish_reason')
|
||||
chunk_index += 1
|
||||
|
||||
if 'delta' in choice:
|
||||
delta = choice['delta']
|
||||
delta_content = delta.get('content')
|
||||
|
||||
assistant_message_tool_calls = delta.get('tool_calls', None)
|
||||
# assistant_message_function_call = delta.delta.function_call
|
||||
|
||||
# extract tool calls from response
|
||||
if assistant_message_tool_calls:
|
||||
tool_calls = self._extract_response_tool_calls(assistant_message_tool_calls)
|
||||
increase_tool_call(tool_calls)
|
||||
|
||||
if delta_content is None or delta_content == '':
|
||||
continue
|
||||
|
||||
# transform assistant message to prompt message
|
||||
assistant_prompt_message = AssistantPromptMessage(
|
||||
content=delta_content,
|
||||
tool_calls=tool_calls if assistant_message_tool_calls else []
|
||||
)
|
||||
|
||||
full_assistant_content += delta_content
|
||||
elif 'text' in choice:
|
||||
choice_text = choice.get('text', '')
|
||||
if choice_text == '':
|
||||
continue
|
||||
|
||||
# transform assistant message to prompt message
|
||||
assistant_prompt_message = AssistantPromptMessage(content=choice_text)
|
||||
full_assistant_content += choice_text
|
||||
else:
|
||||
continue
|
||||
|
||||
# check payload indicator for completion
|
||||
yield LLMResultChunk(
|
||||
model=model,
|
||||
prompt_messages=prompt_messages,
|
||||
delta=LLMResultChunkDelta(
|
||||
index=chunk_index,
|
||||
message=assistant_prompt_message,
|
||||
)
|
||||
)
|
||||
|
||||
chunk_index += 1
|
||||
|
||||
if tools_calls:
|
||||
yield LLMResultChunk(
|
||||
model=model,
|
||||
prompt_messages=prompt_messages,
|
||||
delta=LLMResultChunkDelta(
|
||||
index=chunk_index,
|
||||
message=AssistantPromptMessage(
|
||||
tool_calls=tools_calls,
|
||||
content=""
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
yield create_final_llm_result_chunk(
|
||||
index=chunk_index,
|
||||
message=AssistantPromptMessage(content=""),
|
||||
finish_reason=finish_reason
|
||||
)
|
||||
@@ -20,6 +20,7 @@ supported_model_types:
|
||||
- llm
|
||||
configurate_methods:
|
||||
- predefined-model
|
||||
- customizable-model
|
||||
provider_credential_schema:
|
||||
credential_form_schemas:
|
||||
- variable: api_key
|
||||
@@ -30,3 +31,51 @@ provider_credential_schema:
|
||||
placeholder:
|
||||
zh_Hans: 在此输入您的 API Key
|
||||
en_US: Enter your API Key
|
||||
model_credential_schema:
|
||||
model:
|
||||
label:
|
||||
en_US: Model Name
|
||||
zh_Hans: 模型名称
|
||||
placeholder:
|
||||
en_US: Enter your model name
|
||||
zh_Hans: 输入模型名称
|
||||
credential_form_schemas:
|
||||
- variable: api_key
|
||||
label:
|
||||
en_US: API Key
|
||||
type: secret-input
|
||||
required: true
|
||||
placeholder:
|
||||
zh_Hans: 在此输入您的 API Key
|
||||
en_US: Enter your API Key
|
||||
- variable: context_size
|
||||
label:
|
||||
zh_Hans: 模型上下文长度
|
||||
en_US: Model context size
|
||||
required: true
|
||||
type: text-input
|
||||
default: '4096'
|
||||
placeholder:
|
||||
zh_Hans: 在此输入您的模型上下文长度
|
||||
en_US: Enter your Model context size
|
||||
- variable: max_tokens
|
||||
label:
|
||||
zh_Hans: 最大 token 上限
|
||||
en_US: Upper bound for max tokens
|
||||
default: '4096'
|
||||
type: text-input
|
||||
- variable: function_calling_type
|
||||
label:
|
||||
en_US: Function calling
|
||||
type: select
|
||||
required: false
|
||||
default: no_call
|
||||
options:
|
||||
- value: no_call
|
||||
label:
|
||||
en_US: Not supported
|
||||
zh_Hans: 不支持
|
||||
- value: tool_call
|
||||
label:
|
||||
en_US: Tool Call
|
||||
zh_Hans: Tool Call
|
||||
|
||||
@@ -378,6 +378,34 @@ class OAIAPICompatLargeLanguageModel(_CommonOAI_API_Compat, LargeLanguageModel):
|
||||
delimiter = credentials.get("stream_mode_delimiter", "\n\n")
|
||||
delimiter = codecs.decode(delimiter, "unicode_escape")
|
||||
|
||||
tools_calls: list[AssistantPromptMessage.ToolCall] = []
|
||||
|
||||
def increase_tool_call(new_tool_calls: list[AssistantPromptMessage.ToolCall]):
|
||||
def get_tool_call(tool_call_id: str):
|
||||
tool_call = next(
|
||||
(tool_call for tool_call in tools_calls if tool_call.id == tool_call_id), None
|
||||
)
|
||||
if tool_call is None:
|
||||
tool_call = AssistantPromptMessage.ToolCall(
|
||||
id='',
|
||||
type='function',
|
||||
function=AssistantPromptMessage.ToolCall.ToolCallFunction(
|
||||
name='',
|
||||
arguments=''
|
||||
)
|
||||
)
|
||||
tools_calls.append(tool_call)
|
||||
return tool_call
|
||||
|
||||
for new_tool_call in new_tool_calls:
|
||||
# get tool call
|
||||
tool_call = get_tool_call(new_tool_call.id)
|
||||
# update tool call
|
||||
tool_call.id = new_tool_call.id
|
||||
tool_call.type = new_tool_call.type
|
||||
tool_call.function.name = new_tool_call.function.name
|
||||
tool_call.function.arguments += new_tool_call.function.arguments
|
||||
|
||||
for chunk in response.iter_lines(decode_unicode=True, delimiter=delimiter):
|
||||
if chunk:
|
||||
# ignore sse comments
|
||||
@@ -405,8 +433,6 @@ class OAIAPICompatLargeLanguageModel(_CommonOAI_API_Compat, LargeLanguageModel):
|
||||
if 'delta' in choice:
|
||||
delta = choice['delta']
|
||||
delta_content = delta.get('content')
|
||||
if delta_content is None or delta_content == '':
|
||||
continue
|
||||
|
||||
assistant_message_tool_calls = delta.get('tool_calls', None)
|
||||
# assistant_message_function_call = delta.delta.function_call
|
||||
@@ -414,6 +440,11 @@ class OAIAPICompatLargeLanguageModel(_CommonOAI_API_Compat, LargeLanguageModel):
|
||||
# extract tool calls from response
|
||||
if assistant_message_tool_calls:
|
||||
tool_calls = self._extract_response_tool_calls(assistant_message_tool_calls)
|
||||
increase_tool_call(tool_calls)
|
||||
|
||||
if delta_content is None or delta_content == '':
|
||||
continue
|
||||
|
||||
# function_call = self._extract_response_function_call(assistant_message_function_call)
|
||||
# tool_calls = [function_call] if function_call else []
|
||||
|
||||
@@ -437,6 +468,18 @@ class OAIAPICompatLargeLanguageModel(_CommonOAI_API_Compat, LargeLanguageModel):
|
||||
|
||||
# check payload indicator for completion
|
||||
if finish_reason is not None:
|
||||
yield LLMResultChunk(
|
||||
model=model,
|
||||
prompt_messages=prompt_messages,
|
||||
delta=LLMResultChunkDelta(
|
||||
index=chunk_index,
|
||||
message=AssistantPromptMessage(
|
||||
tool_calls=tools_calls,
|
||||
),
|
||||
finish_reason=finish_reason
|
||||
)
|
||||
)
|
||||
|
||||
yield create_final_llm_result_chunk(
|
||||
index=chunk_index,
|
||||
message=assistant_prompt_message,
|
||||
@@ -735,4 +778,4 @@ class OAIAPICompatLargeLanguageModel(_CommonOAI_API_Compat, LargeLanguageModel):
|
||||
function=function
|
||||
)
|
||||
|
||||
return tool_call
|
||||
return tool_call
|
||||
@@ -1,6 +1,6 @@
|
||||
model: ernie-3.5-8k
|
||||
model: ernie-3.5-4k-0205
|
||||
label:
|
||||
en_US: Ernie-3.5-8K
|
||||
en_US: Ernie-3.5-4k-0205
|
||||
model_type: llm
|
||||
features:
|
||||
- agent-thought
|
||||
|
||||
@@ -24,56 +24,64 @@ class Jieba(BaseKeyword):
|
||||
self._config = KeywordTableConfig()
|
||||
|
||||
def create(self, texts: list[Document], **kwargs) -> BaseKeyword:
|
||||
keyword_table_handler = JiebaKeywordTableHandler()
|
||||
keyword_table = self._get_dataset_keyword_table()
|
||||
for text in texts:
|
||||
keywords = keyword_table_handler.extract_keywords(text.page_content, self._config.max_keywords_per_chunk)
|
||||
self._update_segment_keywords(self.dataset.id, text.metadata['doc_id'], list(keywords))
|
||||
keyword_table = self._add_text_to_keyword_table(keyword_table, text.metadata['doc_id'], list(keywords))
|
||||
lock_name = 'keyword_indexing_lock_{}'.format(self.dataset.id)
|
||||
with redis_client.lock(lock_name, timeout=600):
|
||||
keyword_table_handler = JiebaKeywordTableHandler()
|
||||
keyword_table = self._get_dataset_keyword_table()
|
||||
for text in texts:
|
||||
keywords = keyword_table_handler.extract_keywords(text.page_content, self._config.max_keywords_per_chunk)
|
||||
self._update_segment_keywords(self.dataset.id, text.metadata['doc_id'], list(keywords))
|
||||
keyword_table = self._add_text_to_keyword_table(keyword_table, text.metadata['doc_id'], list(keywords))
|
||||
|
||||
self._save_dataset_keyword_table(keyword_table)
|
||||
self._save_dataset_keyword_table(keyword_table)
|
||||
|
||||
return self
|
||||
return self
|
||||
|
||||
def add_texts(self, texts: list[Document], **kwargs):
|
||||
keyword_table_handler = JiebaKeywordTableHandler()
|
||||
lock_name = 'keyword_indexing_lock_{}'.format(self.dataset.id)
|
||||
with redis_client.lock(lock_name, timeout=600):
|
||||
keyword_table_handler = JiebaKeywordTableHandler()
|
||||
|
||||
keyword_table = self._get_dataset_keyword_table()
|
||||
keywords_list = kwargs.get('keywords_list', None)
|
||||
for i in range(len(texts)):
|
||||
text = texts[i]
|
||||
if keywords_list:
|
||||
keywords = keywords_list[i]
|
||||
else:
|
||||
keywords = keyword_table_handler.extract_keywords(text.page_content, self._config.max_keywords_per_chunk)
|
||||
self._update_segment_keywords(self.dataset.id, text.metadata['doc_id'], list(keywords))
|
||||
keyword_table = self._add_text_to_keyword_table(keyword_table, text.metadata['doc_id'], list(keywords))
|
||||
keyword_table = self._get_dataset_keyword_table()
|
||||
keywords_list = kwargs.get('keywords_list', None)
|
||||
for i in range(len(texts)):
|
||||
text = texts[i]
|
||||
if keywords_list:
|
||||
keywords = keywords_list[i]
|
||||
else:
|
||||
keywords = keyword_table_handler.extract_keywords(text.page_content, self._config.max_keywords_per_chunk)
|
||||
self._update_segment_keywords(self.dataset.id, text.metadata['doc_id'], list(keywords))
|
||||
keyword_table = self._add_text_to_keyword_table(keyword_table, text.metadata['doc_id'], list(keywords))
|
||||
|
||||
self._save_dataset_keyword_table(keyword_table)
|
||||
self._save_dataset_keyword_table(keyword_table)
|
||||
|
||||
def text_exists(self, id: str) -> bool:
|
||||
keyword_table = self._get_dataset_keyword_table()
|
||||
return id in set.union(*keyword_table.values())
|
||||
|
||||
def delete_by_ids(self, ids: list[str]) -> None:
|
||||
keyword_table = self._get_dataset_keyword_table()
|
||||
keyword_table = self._delete_ids_from_keyword_table(keyword_table, ids)
|
||||
lock_name = 'keyword_indexing_lock_{}'.format(self.dataset.id)
|
||||
with redis_client.lock(lock_name, timeout=600):
|
||||
keyword_table = self._get_dataset_keyword_table()
|
||||
keyword_table = self._delete_ids_from_keyword_table(keyword_table, ids)
|
||||
|
||||
self._save_dataset_keyword_table(keyword_table)
|
||||
self._save_dataset_keyword_table(keyword_table)
|
||||
|
||||
def delete_by_document_id(self, document_id: str):
|
||||
# get segment ids by document_id
|
||||
segments = db.session.query(DocumentSegment).filter(
|
||||
DocumentSegment.dataset_id == self.dataset.id,
|
||||
DocumentSegment.document_id == document_id
|
||||
).all()
|
||||
lock_name = 'keyword_indexing_lock_{}'.format(self.dataset.id)
|
||||
with redis_client.lock(lock_name, timeout=600):
|
||||
# get segment ids by document_id
|
||||
segments = db.session.query(DocumentSegment).filter(
|
||||
DocumentSegment.dataset_id == self.dataset.id,
|
||||
DocumentSegment.document_id == document_id
|
||||
).all()
|
||||
|
||||
ids = [segment.index_node_id for segment in segments]
|
||||
ids = [segment.index_node_id for segment in segments]
|
||||
|
||||
keyword_table = self._get_dataset_keyword_table()
|
||||
keyword_table = self._delete_ids_from_keyword_table(keyword_table, ids)
|
||||
keyword_table = self._get_dataset_keyword_table()
|
||||
keyword_table = self._delete_ids_from_keyword_table(keyword_table, ids)
|
||||
|
||||
self._save_dataset_keyword_table(keyword_table)
|
||||
self._save_dataset_keyword_table(keyword_table)
|
||||
|
||||
def search(
|
||||
self, query: str,
|
||||
@@ -106,13 +114,15 @@ class Jieba(BaseKeyword):
|
||||
return documents
|
||||
|
||||
def delete(self) -> None:
|
||||
dataset_keyword_table = self.dataset.dataset_keyword_table
|
||||
if dataset_keyword_table:
|
||||
db.session.delete(dataset_keyword_table)
|
||||
db.session.commit()
|
||||
if dataset_keyword_table.data_source_type != 'database':
|
||||
file_key = 'keyword_files/' + self.dataset.tenant_id + '/' + self.dataset.id + '.txt'
|
||||
storage.delete(file_key)
|
||||
lock_name = 'keyword_indexing_lock_{}'.format(self.dataset.id)
|
||||
with redis_client.lock(lock_name, timeout=600):
|
||||
dataset_keyword_table = self.dataset.dataset_keyword_table
|
||||
if dataset_keyword_table:
|
||||
db.session.delete(dataset_keyword_table)
|
||||
db.session.commit()
|
||||
if dataset_keyword_table.data_source_type != 'database':
|
||||
file_key = 'keyword_files/' + self.dataset.tenant_id + '/' + self.dataset.id + '.txt'
|
||||
storage.delete(file_key)
|
||||
|
||||
def _save_dataset_keyword_table(self, keyword_table):
|
||||
keyword_table_dict = {
|
||||
@@ -135,33 +145,31 @@ class Jieba(BaseKeyword):
|
||||
storage.save(file_key, json.dumps(keyword_table_dict, cls=SetEncoder).encode('utf-8'))
|
||||
|
||||
def _get_dataset_keyword_table(self) -> Optional[dict]:
|
||||
lock_name = 'keyword_indexing_lock_{}'.format(self.dataset.id)
|
||||
with redis_client.lock(lock_name, timeout=20):
|
||||
dataset_keyword_table = self.dataset.dataset_keyword_table
|
||||
if dataset_keyword_table:
|
||||
keyword_table_dict = dataset_keyword_table.keyword_table_dict
|
||||
if keyword_table_dict:
|
||||
return keyword_table_dict['__data__']['table']
|
||||
else:
|
||||
keyword_data_source_type = current_app.config['KEYWORD_DATA_SOURCE_TYPE']
|
||||
dataset_keyword_table = DatasetKeywordTable(
|
||||
dataset_id=self.dataset.id,
|
||||
keyword_table='',
|
||||
data_source_type=keyword_data_source_type,
|
||||
)
|
||||
if keyword_data_source_type == 'database':
|
||||
dataset_keyword_table.keyword_table = json.dumps({
|
||||
'__type__': 'keyword_table',
|
||||
'__data__': {
|
||||
"index_id": self.dataset.id,
|
||||
"summary": None,
|
||||
"table": {}
|
||||
}
|
||||
}, cls=SetEncoder)
|
||||
db.session.add(dataset_keyword_table)
|
||||
db.session.commit()
|
||||
dataset_keyword_table = self.dataset.dataset_keyword_table
|
||||
if dataset_keyword_table:
|
||||
keyword_table_dict = dataset_keyword_table.keyword_table_dict
|
||||
if keyword_table_dict:
|
||||
return keyword_table_dict['__data__']['table']
|
||||
else:
|
||||
keyword_data_source_type = current_app.config['KEYWORD_DATA_SOURCE_TYPE']
|
||||
dataset_keyword_table = DatasetKeywordTable(
|
||||
dataset_id=self.dataset.id,
|
||||
keyword_table='',
|
||||
data_source_type=keyword_data_source_type,
|
||||
)
|
||||
if keyword_data_source_type == 'database':
|
||||
dataset_keyword_table.keyword_table = json.dumps({
|
||||
'__type__': 'keyword_table',
|
||||
'__data__': {
|
||||
"index_id": self.dataset.id,
|
||||
"summary": None,
|
||||
"table": {}
|
||||
}
|
||||
}, cls=SetEncoder)
|
||||
db.session.add(dataset_keyword_table)
|
||||
db.session.commit()
|
||||
|
||||
return {}
|
||||
return {}
|
||||
|
||||
def _add_text_to_keyword_table(self, keyword_table: dict, id: str, keywords: list[str]) -> dict:
|
||||
for keyword in keywords:
|
||||
|
||||
@@ -20,16 +20,17 @@ class MilvusConfig(BaseModel):
|
||||
password: str
|
||||
secure: bool = False
|
||||
batch_size: int = 100
|
||||
database: str = "default"
|
||||
|
||||
@root_validator()
|
||||
def validate_config(cls, values: dict) -> dict:
|
||||
if not values['host']:
|
||||
if not values.get('host'):
|
||||
raise ValueError("config MILVUS_HOST is required")
|
||||
if not values['port']:
|
||||
if not values.get('port'):
|
||||
raise ValueError("config MILVUS_PORT is required")
|
||||
if not values['user']:
|
||||
if not values.get('user'):
|
||||
raise ValueError("config MILVUS_USER is required")
|
||||
if not values['password']:
|
||||
if not values.get('password'):
|
||||
raise ValueError("config MILVUS_PASSWORD is required")
|
||||
return values
|
||||
|
||||
@@ -39,7 +40,8 @@ class MilvusConfig(BaseModel):
|
||||
'port': self.port,
|
||||
'user': self.user,
|
||||
'password': self.password,
|
||||
'secure': self.secure
|
||||
'secure': self.secure,
|
||||
'db_name': self.database,
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +130,8 @@ class MilvusVector(BaseVector):
|
||||
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)
|
||||
connections.connect(alias=alias, uri=uri, user=self._client_config.user, password=self._client_config.password,
|
||||
db_name=self._client_config.database)
|
||||
|
||||
from pymilvus import utility
|
||||
if utility.has_collection(self._collection_name, using=alias):
|
||||
@@ -140,7 +143,8 @@ class MilvusVector(BaseVector):
|
||||
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)
|
||||
connections.connect(alias=alias, uri=uri, user=self._client_config.user, password=self._client_config.password,
|
||||
db_name=self._client_config.database)
|
||||
|
||||
from pymilvus import utility
|
||||
if not utility.has_collection(self._collection_name, using=alias):
|
||||
@@ -192,7 +196,7 @@ class MilvusVector(BaseVector):
|
||||
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)
|
||||
password=self._client_config.password, db_name=self._client_config.database)
|
||||
if not utility.has_collection(self._collection_name, using=alias):
|
||||
from pymilvus import CollectionSchema, DataType, FieldSchema
|
||||
from pymilvus.orm.types import infer_dtype_bydata
|
||||
|
||||
@@ -110,6 +110,7 @@ class Vector:
|
||||
user=config.get('MILVUS_USER'),
|
||||
password=config.get('MILVUS_PASSWORD'),
|
||||
secure=config.get('MILVUS_SECURE'),
|
||||
database=config.get('MILVUS_DATABASE'),
|
||||
)
|
||||
)
|
||||
else:
|
||||
|
||||
@@ -1,11 +1,92 @@
|
||||
from typing import Any
|
||||
import logging
|
||||
from typing import Any, Optional
|
||||
|
||||
from langchain.utilities import ArxivAPIWrapper
|
||||
import arxiv
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from core.tools.entities.tool_entities import ToolInvokeMessage
|
||||
from core.tools.tool.builtin_tool import BuiltinTool
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
class ArxivAPIWrapper(BaseModel):
|
||||
"""Wrapper around ArxivAPI.
|
||||
|
||||
To use, you should have the ``arxiv`` python package installed.
|
||||
https://lukasschwab.me/arxiv.py/index.html
|
||||
This wrapper will use the Arxiv API to conduct searches and
|
||||
fetch document summaries. By default, it will return the document summaries
|
||||
of the top-k results.
|
||||
It limits the Document content by doc_content_chars_max.
|
||||
Set doc_content_chars_max=None if you don't want to limit the content size.
|
||||
|
||||
Args:
|
||||
top_k_results: number of the top-scored document used for the arxiv tool
|
||||
ARXIV_MAX_QUERY_LENGTH: the cut limit on the query used for the arxiv tool.
|
||||
load_max_docs: a limit to the number of loaded documents
|
||||
load_all_available_meta:
|
||||
if True: the `metadata` of the loaded Documents contains all available
|
||||
meta info (see https://lukasschwab.me/arxiv.py/index.html#Result),
|
||||
if False: the `metadata` contains only the published date, title,
|
||||
authors and summary.
|
||||
doc_content_chars_max: an optional cut limit for the length of a document's
|
||||
content
|
||||
|
||||
Example:
|
||||
.. code-block:: python
|
||||
|
||||
arxiv = ArxivAPIWrapper(
|
||||
top_k_results = 3,
|
||||
ARXIV_MAX_QUERY_LENGTH = 300,
|
||||
load_max_docs = 3,
|
||||
load_all_available_meta = False,
|
||||
doc_content_chars_max = 40000
|
||||
)
|
||||
arxiv.run("tree of thought llm)
|
||||
"""
|
||||
|
||||
arxiv_search = arxiv.Search #: :meta private:
|
||||
arxiv_exceptions = (
|
||||
arxiv.ArxivError,
|
||||
arxiv.UnexpectedEmptyPageError,
|
||||
arxiv.HTTPError,
|
||||
) # :meta private:
|
||||
top_k_results: int = 3
|
||||
ARXIV_MAX_QUERY_LENGTH = 300
|
||||
load_max_docs: int = 100
|
||||
load_all_available_meta: bool = False
|
||||
doc_content_chars_max: Optional[int] = 4000
|
||||
|
||||
def run(self, query: str) -> str:
|
||||
"""
|
||||
Performs an arxiv search and A single string
|
||||
with the publish date, title, authors, and summary
|
||||
for each article separated by two newlines.
|
||||
|
||||
If an error occurs or no documents found, error text
|
||||
is returned instead. Wrapper for
|
||||
https://lukasschwab.me/arxiv.py/index.html#Search
|
||||
|
||||
Args:
|
||||
query: a plaintext search query
|
||||
""" # noqa: E501
|
||||
try:
|
||||
results = self.arxiv_search( # type: ignore
|
||||
query[: self.ARXIV_MAX_QUERY_LENGTH], max_results=self.top_k_results
|
||||
).results()
|
||||
except self.arxiv_exceptions as ex:
|
||||
return f"Arxiv exception: {ex}"
|
||||
docs = [
|
||||
f"Published: {result.updated.date()}\n"
|
||||
f"Title: {result.title}\n"
|
||||
f"Authors: {', '.join(a.name for a in result.authors)}\n"
|
||||
f"Summary: {result.summary}"
|
||||
for result in results
|
||||
]
|
||||
if docs:
|
||||
return "\n\n".join(docs)[: self.doc_content_chars_max]
|
||||
else:
|
||||
return "No good Arxiv Result was found"
|
||||
|
||||
|
||||
class ArxivSearchInput(BaseModel):
|
||||
query: str = Field(..., description="Search query.")
|
||||
|
||||
@@ -1,11 +1,95 @@
|
||||
from typing import Any
|
||||
import json
|
||||
from typing import Any, Optional
|
||||
|
||||
from langchain.tools import BraveSearch
|
||||
import requests
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from core.tools.entities.tool_entities import ToolInvokeMessage
|
||||
from core.tools.tool.builtin_tool import BuiltinTool
|
||||
|
||||
|
||||
class BraveSearchWrapper(BaseModel):
|
||||
"""Wrapper around the Brave search engine."""
|
||||
|
||||
api_key: str
|
||||
"""The API key to use for the Brave search engine."""
|
||||
search_kwargs: dict = Field(default_factory=dict)
|
||||
"""Additional keyword arguments to pass to the search request."""
|
||||
base_url = "https://api.search.brave.com/res/v1/web/search"
|
||||
"""The base URL for the Brave search engine."""
|
||||
|
||||
def run(self, query: str) -> str:
|
||||
"""Query the Brave search engine and return the results as a JSON string.
|
||||
|
||||
Args:
|
||||
query: The query to search for.
|
||||
|
||||
Returns: The results as a JSON string.
|
||||
|
||||
"""
|
||||
web_search_results = self._search_request(query=query)
|
||||
final_results = [
|
||||
{
|
||||
"title": item.get("title"),
|
||||
"link": item.get("url"),
|
||||
"snippet": item.get("description"),
|
||||
}
|
||||
for item in web_search_results
|
||||
]
|
||||
return json.dumps(final_results)
|
||||
|
||||
def _search_request(self, query: str) -> list[dict]:
|
||||
headers = {
|
||||
"X-Subscription-Token": self.api_key,
|
||||
"Accept": "application/json",
|
||||
}
|
||||
req = requests.PreparedRequest()
|
||||
params = {**self.search_kwargs, **{"q": query}}
|
||||
req.prepare_url(self.base_url, params)
|
||||
if req.url is None:
|
||||
raise ValueError("prepared url is None, this should not happen")
|
||||
|
||||
response = requests.get(req.url, headers=headers)
|
||||
if not response.ok:
|
||||
raise Exception(f"HTTP error {response.status_code}")
|
||||
|
||||
return response.json().get("web", {}).get("results", [])
|
||||
|
||||
class BraveSearch(BaseModel):
|
||||
"""Tool that queries the BraveSearch."""
|
||||
|
||||
name = "brave_search"
|
||||
description = (
|
||||
"a search engine. "
|
||||
"useful for when you need to answer questions about current events."
|
||||
" input should be a search query."
|
||||
)
|
||||
search_wrapper: BraveSearchWrapper
|
||||
|
||||
@classmethod
|
||||
def from_api_key(
|
||||
cls, api_key: str, search_kwargs: Optional[dict] = None, **kwargs: Any
|
||||
) -> "BraveSearch":
|
||||
"""Create a tool from an api key.
|
||||
|
||||
Args:
|
||||
api_key: The api key to use.
|
||||
search_kwargs: Any additional kwargs to pass to the search wrapper.
|
||||
**kwargs: Any additional kwargs to pass to the tool.
|
||||
|
||||
Returns:
|
||||
A tool.
|
||||
"""
|
||||
wrapper = BraveSearchWrapper(api_key=api_key, search_kwargs=search_kwargs or {})
|
||||
return cls(search_wrapper=wrapper, **kwargs)
|
||||
|
||||
def _run(
|
||||
self,
|
||||
query: str,
|
||||
) -> str:
|
||||
"""Use the tool."""
|
||||
return self.search_wrapper.run(query)
|
||||
|
||||
class BraveSearchTool(BuiltinTool):
|
||||
"""
|
||||
Tool for performing a search using Brave search engine.
|
||||
@@ -31,7 +115,7 @@ class BraveSearchTool(BuiltinTool):
|
||||
|
||||
tool = BraveSearch.from_api_key(api_key=api_key, search_kwargs={"count": count})
|
||||
|
||||
results = tool.run(query)
|
||||
results = tool._run(query)
|
||||
|
||||
if not results:
|
||||
return self.create_text_message(f"No results found for '{query}' in Tavily")
|
||||
|
||||
@@ -1,16 +1,147 @@
|
||||
from typing import Any
|
||||
from typing import Any, Optional
|
||||
|
||||
from langchain.tools import DuckDuckGoSearchRun
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from core.tools.entities.tool_entities import ToolInvokeMessage
|
||||
from core.tools.tool.builtin_tool import BuiltinTool
|
||||
|
||||
|
||||
class DuckDuckGoSearchAPIWrapper(BaseModel):
|
||||
"""Wrapper for DuckDuckGo Search API.
|
||||
|
||||
Free and does not require any setup.
|
||||
"""
|
||||
|
||||
region: Optional[str] = "wt-wt"
|
||||
safesearch: str = "moderate"
|
||||
time: Optional[str] = "y"
|
||||
max_results: int = 5
|
||||
|
||||
def get_snippets(self, query: str) -> list[str]:
|
||||
"""Run query through DuckDuckGo and return concatenated results."""
|
||||
from duckduckgo_search import DDGS
|
||||
|
||||
with DDGS() as ddgs:
|
||||
results = ddgs.text(
|
||||
query,
|
||||
region=self.region,
|
||||
safesearch=self.safesearch,
|
||||
timelimit=self.time,
|
||||
)
|
||||
if results is None:
|
||||
return ["No good DuckDuckGo Search Result was found"]
|
||||
snippets = []
|
||||
for i, res in enumerate(results, 1):
|
||||
if res is not None:
|
||||
snippets.append(res["body"])
|
||||
if len(snippets) == self.max_results:
|
||||
break
|
||||
return snippets
|
||||
|
||||
def run(self, query: str) -> str:
|
||||
snippets = self.get_snippets(query)
|
||||
return " ".join(snippets)
|
||||
|
||||
def results(
|
||||
self, query: str, num_results: int, backend: str = "api"
|
||||
) -> list[dict[str, str]]:
|
||||
"""Run query through DuckDuckGo and return metadata.
|
||||
|
||||
Args:
|
||||
query: The query to search for.
|
||||
num_results: The number of results to return.
|
||||
|
||||
Returns:
|
||||
A list of dictionaries with the following keys:
|
||||
snippet - The description of the result.
|
||||
title - The title of the result.
|
||||
link - The link to the result.
|
||||
"""
|
||||
from duckduckgo_search import DDGS
|
||||
|
||||
with DDGS() as ddgs:
|
||||
results = ddgs.text(
|
||||
query,
|
||||
region=self.region,
|
||||
safesearch=self.safesearch,
|
||||
timelimit=self.time,
|
||||
backend=backend,
|
||||
)
|
||||
if results is None:
|
||||
return [{"Result": "No good DuckDuckGo Search Result was found"}]
|
||||
|
||||
def to_metadata(result: dict) -> dict[str, str]:
|
||||
if backend == "news":
|
||||
return {
|
||||
"date": result["date"],
|
||||
"title": result["title"],
|
||||
"snippet": result["body"],
|
||||
"source": result["source"],
|
||||
"link": result["url"],
|
||||
}
|
||||
return {
|
||||
"snippet": result["body"],
|
||||
"title": result["title"],
|
||||
"link": result["href"],
|
||||
}
|
||||
|
||||
formatted_results = []
|
||||
for i, res in enumerate(results, 1):
|
||||
if res is not None:
|
||||
formatted_results.append(to_metadata(res))
|
||||
if len(formatted_results) == num_results:
|
||||
break
|
||||
return formatted_results
|
||||
|
||||
|
||||
class DuckDuckGoSearchRun(BaseModel):
|
||||
"""Tool that queries the DuckDuckGo search API."""
|
||||
|
||||
name = "duckduckgo_search"
|
||||
description = (
|
||||
"A wrapper around DuckDuckGo Search. "
|
||||
"Useful for when you need to answer questions about current events. "
|
||||
"Input should be a search query."
|
||||
)
|
||||
api_wrapper: DuckDuckGoSearchAPIWrapper = Field(
|
||||
default_factory=DuckDuckGoSearchAPIWrapper
|
||||
)
|
||||
|
||||
def _run(
|
||||
self,
|
||||
query: str,
|
||||
) -> str:
|
||||
"""Use the tool."""
|
||||
return self.api_wrapper.run(query)
|
||||
|
||||
|
||||
class DuckDuckGoSearchResults(BaseModel):
|
||||
"""Tool that queries the DuckDuckGo search API and gets back json."""
|
||||
|
||||
name = "DuckDuckGo Results JSON"
|
||||
description = (
|
||||
"A wrapper around Duck Duck Go Search. "
|
||||
"Useful for when you need to answer questions about current events. "
|
||||
"Input should be a search query. Output is a JSON array of the query results"
|
||||
)
|
||||
num_results: int = 4
|
||||
api_wrapper: DuckDuckGoSearchAPIWrapper = Field(
|
||||
default_factory=DuckDuckGoSearchAPIWrapper
|
||||
)
|
||||
backend: str = "api"
|
||||
|
||||
def _run(
|
||||
self,
|
||||
query: str,
|
||||
) -> str:
|
||||
"""Use the tool."""
|
||||
res = self.api_wrapper.results(query, self.num_results, backend=self.backend)
|
||||
res_strs = [", ".join([f"{k}: {v}" for k, v in d.items()]) for d in res]
|
||||
return ", ".join([f"[{rs}]" for rs in res_strs])
|
||||
|
||||
class DuckDuckGoInput(BaseModel):
|
||||
query: str = Field(..., description="Search query.")
|
||||
|
||||
|
||||
class DuckDuckGoSearchTool(BuiltinTool):
|
||||
"""
|
||||
Tool for performing a search using DuckDuckGo search engine.
|
||||
@@ -34,7 +165,7 @@ class DuckDuckGoSearchTool(BuiltinTool):
|
||||
|
||||
tool = DuckDuckGoSearchRun(args_schema=DuckDuckGoInput)
|
||||
|
||||
result = tool.run(query)
|
||||
result = tool._run(query)
|
||||
|
||||
return self.create_text_message(self.summary(user_id=user_id, content=result))
|
||||
|
||||
@@ -70,43 +70,44 @@ class SerpAPI:
|
||||
raise ValueError(f"Got error from SerpAPI: {res['error']}")
|
||||
|
||||
if typ == "text":
|
||||
toret = ""
|
||||
if "answer_box" in res.keys() and type(res["answer_box"]) == list:
|
||||
res["answer_box"] = res["answer_box"][0]
|
||||
res["answer_box"] = res["answer_box"][0] + "\n"
|
||||
if "answer_box" in res.keys() and "answer" in res["answer_box"].keys():
|
||||
toret = res["answer_box"]["answer"]
|
||||
elif "answer_box" in res.keys() and "snippet" in res["answer_box"].keys():
|
||||
toret = res["answer_box"]["snippet"]
|
||||
elif (
|
||||
toret += res["answer_box"]["answer"] + "\n"
|
||||
if "answer_box" in res.keys() and "snippet" in res["answer_box"].keys():
|
||||
toret += res["answer_box"]["snippet"] + "\n"
|
||||
if (
|
||||
"answer_box" in res.keys()
|
||||
and "snippet_highlighted_words" in res["answer_box"].keys()
|
||||
):
|
||||
toret = res["answer_box"]["snippet_highlighted_words"][0]
|
||||
elif (
|
||||
for item in res["answer_box"]["snippet_highlighted_words"]:
|
||||
toret += item + "\n"
|
||||
if (
|
||||
"sports_results" in res.keys()
|
||||
and "game_spotlight" in res["sports_results"].keys()
|
||||
):
|
||||
toret = res["sports_results"]["game_spotlight"]
|
||||
elif (
|
||||
toret += res["sports_results"]["game_spotlight"] + "\n"
|
||||
if (
|
||||
"shopping_results" in res.keys()
|
||||
and "title" in res["shopping_results"][0].keys()
|
||||
):
|
||||
toret = res["shopping_results"][:3]
|
||||
elif (
|
||||
toret += res["shopping_results"][:3] + "\n"
|
||||
if (
|
||||
"knowledge_graph" in res.keys()
|
||||
and "description" in res["knowledge_graph"].keys()
|
||||
):
|
||||
toret = res["knowledge_graph"]["description"]
|
||||
elif "snippet" in res["organic_results"][0].keys():
|
||||
toret = res["organic_results"][0]["snippet"]
|
||||
elif "link" in res["organic_results"][0].keys():
|
||||
toret = res["organic_results"][0]["link"]
|
||||
elif (
|
||||
toret = res["knowledge_graph"]["description"] + "\n"
|
||||
if "snippet" in res["organic_results"][0].keys():
|
||||
for item in res["organic_results"]:
|
||||
toret += "content: " + item["snippet"] + "\n" + "link: " + item["link"] + "\n"
|
||||
if (
|
||||
"images_results" in res.keys()
|
||||
and "thumbnail" in res["images_results"][0].keys()
|
||||
):
|
||||
thumbnails = [item["thumbnail"] for item in res["images_results"][:10]]
|
||||
toret = thumbnails
|
||||
else:
|
||||
if toret == "":
|
||||
toret = "No good search result found"
|
||||
elif typ == "link":
|
||||
if "knowledge_graph" in res.keys() and "title" in res["knowledge_graph"].keys() \
|
||||
|
||||
@@ -1,16 +1,187 @@
|
||||
import json
|
||||
import time
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from typing import Any
|
||||
|
||||
from langchain.tools import PubmedQueryRun
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from core.tools.entities.tool_entities import ToolInvokeMessage
|
||||
from core.tools.tool.builtin_tool import BuiltinTool
|
||||
|
||||
|
||||
class PubMedAPIWrapper(BaseModel):
|
||||
"""
|
||||
Wrapper around PubMed API.
|
||||
|
||||
This wrapper will use the PubMed API to conduct searches and fetch
|
||||
document summaries. By default, it will return the document summaries
|
||||
of the top-k results of an input search.
|
||||
|
||||
Parameters:
|
||||
top_k_results: number of the top-scored document used for the PubMed tool
|
||||
load_max_docs: a limit to the number of loaded documents
|
||||
load_all_available_meta:
|
||||
if True: the `metadata` of the loaded Documents gets all available meta info
|
||||
(see https://www.ncbi.nlm.nih.gov/books/NBK25499/#chapter4.ESearch)
|
||||
if False: the `metadata` gets only the most informative fields.
|
||||
"""
|
||||
|
||||
base_url_esearch = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?"
|
||||
base_url_efetch = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?"
|
||||
max_retry = 5
|
||||
sleep_time = 0.2
|
||||
|
||||
# Default values for the parameters
|
||||
top_k_results: int = 3
|
||||
load_max_docs: int = 25
|
||||
ARXIV_MAX_QUERY_LENGTH = 300
|
||||
doc_content_chars_max: int = 2000
|
||||
load_all_available_meta: bool = False
|
||||
email: str = "your_email@example.com"
|
||||
|
||||
def run(self, query: str) -> str:
|
||||
"""
|
||||
Run PubMed search and get the article meta information.
|
||||
See https://www.ncbi.nlm.nih.gov/books/NBK25499/#chapter4.ESearch
|
||||
It uses only the most informative fields of article meta information.
|
||||
"""
|
||||
|
||||
try:
|
||||
# Retrieve the top-k results for the query
|
||||
docs = [
|
||||
f"Published: {result['pub_date']}\nTitle: {result['title']}\n"
|
||||
f"Summary: {result['summary']}"
|
||||
for result in self.load(query[: self.ARXIV_MAX_QUERY_LENGTH])
|
||||
]
|
||||
|
||||
# Join the results and limit the character count
|
||||
return (
|
||||
"\n\n".join(docs)[:self.doc_content_chars_max]
|
||||
if docs
|
||||
else "No good PubMed Result was found"
|
||||
)
|
||||
except Exception as ex:
|
||||
return f"PubMed exception: {ex}"
|
||||
|
||||
def load(self, query: str) -> list[dict]:
|
||||
"""
|
||||
Search PubMed for documents matching the query.
|
||||
Return a list of dictionaries containing the document metadata.
|
||||
"""
|
||||
|
||||
url = (
|
||||
self.base_url_esearch
|
||||
+ "db=pubmed&term="
|
||||
+ str({urllib.parse.quote(query)})
|
||||
+ f"&retmode=json&retmax={self.top_k_results}&usehistory=y"
|
||||
)
|
||||
result = urllib.request.urlopen(url)
|
||||
text = result.read().decode("utf-8")
|
||||
json_text = json.loads(text)
|
||||
|
||||
articles = []
|
||||
webenv = json_text["esearchresult"]["webenv"]
|
||||
for uid in json_text["esearchresult"]["idlist"]:
|
||||
article = self.retrieve_article(uid, webenv)
|
||||
articles.append(article)
|
||||
|
||||
# Convert the list of articles to a JSON string
|
||||
return articles
|
||||
|
||||
def retrieve_article(self, uid: str, webenv: str) -> dict:
|
||||
url = (
|
||||
self.base_url_efetch
|
||||
+ "db=pubmed&retmode=xml&id="
|
||||
+ uid
|
||||
+ "&webenv="
|
||||
+ webenv
|
||||
)
|
||||
|
||||
retry = 0
|
||||
while True:
|
||||
try:
|
||||
result = urllib.request.urlopen(url)
|
||||
break
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code == 429 and retry < self.max_retry:
|
||||
# Too Many Requests error
|
||||
# wait for an exponentially increasing amount of time
|
||||
print(
|
||||
f"Too Many Requests, "
|
||||
f"waiting for {self.sleep_time:.2f} seconds..."
|
||||
)
|
||||
time.sleep(self.sleep_time)
|
||||
self.sleep_time *= 2
|
||||
retry += 1
|
||||
else:
|
||||
raise e
|
||||
|
||||
xml_text = result.read().decode("utf-8")
|
||||
|
||||
# Get title
|
||||
title = ""
|
||||
if "<ArticleTitle>" in xml_text and "</ArticleTitle>" in xml_text:
|
||||
start_tag = "<ArticleTitle>"
|
||||
end_tag = "</ArticleTitle>"
|
||||
title = xml_text[
|
||||
xml_text.index(start_tag) + len(start_tag) : xml_text.index(end_tag)
|
||||
]
|
||||
|
||||
# Get abstract
|
||||
abstract = ""
|
||||
if "<AbstractText>" in xml_text and "</AbstractText>" in xml_text:
|
||||
start_tag = "<AbstractText>"
|
||||
end_tag = "</AbstractText>"
|
||||
abstract = xml_text[
|
||||
xml_text.index(start_tag) + len(start_tag) : xml_text.index(end_tag)
|
||||
]
|
||||
|
||||
# Get publication date
|
||||
pub_date = ""
|
||||
if "<PubDate>" in xml_text and "</PubDate>" in xml_text:
|
||||
start_tag = "<PubDate>"
|
||||
end_tag = "</PubDate>"
|
||||
pub_date = xml_text[
|
||||
xml_text.index(start_tag) + len(start_tag) : xml_text.index(end_tag)
|
||||
]
|
||||
|
||||
# Return article as dictionary
|
||||
article = {
|
||||
"uid": uid,
|
||||
"title": title,
|
||||
"summary": abstract,
|
||||
"pub_date": pub_date,
|
||||
}
|
||||
return article
|
||||
|
||||
|
||||
class PubmedQueryRun(BaseModel):
|
||||
"""Tool that searches the PubMed API."""
|
||||
|
||||
name = "PubMed"
|
||||
description = (
|
||||
"A wrapper around PubMed.org "
|
||||
"Useful for when you need to answer questions about Physics, Mathematics, "
|
||||
"Computer Science, Quantitative Biology, Quantitative Finance, Statistics, "
|
||||
"Electrical Engineering, and Economics "
|
||||
"from scientific articles on PubMed.org. "
|
||||
"Input should be a search query."
|
||||
)
|
||||
api_wrapper: PubMedAPIWrapper = Field(default_factory=PubMedAPIWrapper)
|
||||
|
||||
def _run(
|
||||
self,
|
||||
query: str,
|
||||
) -> str:
|
||||
"""Use the Arxiv tool."""
|
||||
return self.api_wrapper.run(query)
|
||||
|
||||
|
||||
class PubMedInput(BaseModel):
|
||||
query: str = Field(..., description="Search query.")
|
||||
|
||||
|
||||
class PubMedSearchTool(BuiltinTool):
|
||||
"""
|
||||
Tool for performing a search using PubMed search engine.
|
||||
@@ -34,7 +205,7 @@ class PubMedSearchTool(BuiltinTool):
|
||||
|
||||
tool = PubmedQueryRun(args_schema=PubMedInput)
|
||||
|
||||
result = tool.run(query)
|
||||
result = tool._run(query)
|
||||
|
||||
return self.create_text_message(self.summary(user_id=user_id, content=result))
|
||||
|
||||
@@ -1,11 +1,81 @@
|
||||
from typing import Any, Union
|
||||
from typing import Any, Optional, Union
|
||||
|
||||
from langchain.utilities import TwilioAPIWrapper
|
||||
from pydantic import BaseModel, validator
|
||||
|
||||
from core.tools.entities.tool_entities import ToolInvokeMessage
|
||||
from core.tools.tool.builtin_tool import BuiltinTool
|
||||
|
||||
|
||||
class TwilioAPIWrapper(BaseModel):
|
||||
"""Messaging Client using Twilio.
|
||||
|
||||
To use, you should have the ``twilio`` python package installed,
|
||||
and the environment variables ``TWILIO_ACCOUNT_SID``, ``TWILIO_AUTH_TOKEN``, and
|
||||
``TWILIO_FROM_NUMBER``, or pass `account_sid`, `auth_token`, and `from_number` as
|
||||
named parameters to the constructor.
|
||||
|
||||
Example:
|
||||
.. code-block:: python
|
||||
|
||||
from langchain.utilities.twilio import TwilioAPIWrapper
|
||||
twilio = TwilioAPIWrapper(
|
||||
account_sid="ACxxx",
|
||||
auth_token="xxx",
|
||||
from_number="+10123456789"
|
||||
)
|
||||
twilio.run('test', '+12484345508')
|
||||
"""
|
||||
|
||||
client: Any #: :meta private:
|
||||
account_sid: Optional[str] = None
|
||||
"""Twilio account string identifier."""
|
||||
auth_token: Optional[str] = None
|
||||
"""Twilio auth token."""
|
||||
from_number: Optional[str] = None
|
||||
"""A Twilio phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164)
|
||||
format, an
|
||||
[alphanumeric sender ID](https://www.twilio.com/docs/sms/send-messages#use-an-alphanumeric-sender-id),
|
||||
or a [Channel Endpoint address](https://www.twilio.com/docs/sms/channels#channel-addresses)
|
||||
that is enabled for the type of message you want to send. Phone numbers or
|
||||
[short codes](https://www.twilio.com/docs/sms/api/short-code) purchased from
|
||||
Twilio also work here. You cannot, for example, spoof messages from a private
|
||||
cell phone number. If you are using `messaging_service_sid`, this parameter
|
||||
must be empty.
|
||||
""" # noqa: E501
|
||||
|
||||
@validator("client", pre=True, always=True)
|
||||
def set_validator(cls, values: dict) -> dict:
|
||||
"""Validate that api key and python package exists in environment."""
|
||||
try:
|
||||
from twilio.rest import Client
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Could not import twilio python package. "
|
||||
"Please install it with `pip install twilio`."
|
||||
)
|
||||
account_sid = values.get("account_sid")
|
||||
auth_token = values.get("auth_token")
|
||||
values["from_number"] = values.get("from_number")
|
||||
values["client"] = Client(account_sid, auth_token)
|
||||
|
||||
return values
|
||||
|
||||
def run(self, body: str, to: str) -> str:
|
||||
"""Run body through Twilio and respond with message sid.
|
||||
|
||||
Args:
|
||||
body: The text of the message you want to send. Can be up to 1,600
|
||||
characters in length.
|
||||
to: The destination phone number in
|
||||
[E.164](https://www.twilio.com/docs/glossary/what-e164) format for
|
||||
SMS/MMS or
|
||||
[Channel user address](https://www.twilio.com/docs/sms/channels#channel-addresses)
|
||||
for other 3rd-party channels.
|
||||
""" # noqa: E501
|
||||
message = self.client.messages.create(to, from_=self.from_number, body=body)
|
||||
return message.sid
|
||||
|
||||
|
||||
class SendMessageTool(BuiltinTool):
|
||||
"""
|
||||
A tool for sending messages using Twilio API.
|
||||
|
||||
@@ -1,16 +1,79 @@
|
||||
from typing import Any, Union
|
||||
from typing import Any, Optional, Union
|
||||
|
||||
from langchain import WikipediaAPIWrapper
|
||||
from langchain.tools import WikipediaQueryRun
|
||||
from pydantic import BaseModel, Field
|
||||
import wikipedia
|
||||
|
||||
from core.tools.entities.tool_entities import ToolInvokeMessage
|
||||
from core.tools.tool.builtin_tool import BuiltinTool
|
||||
|
||||
WIKIPEDIA_MAX_QUERY_LENGTH = 300
|
||||
|
||||
class WikipediaInput(BaseModel):
|
||||
query: str = Field(..., description="search query.")
|
||||
class WikipediaAPIWrapper:
|
||||
"""Wrapper around WikipediaAPI.
|
||||
|
||||
To use, you should have the ``wikipedia`` python package installed.
|
||||
This wrapper will use the Wikipedia API to conduct searches and
|
||||
fetch page summaries. By default, it will return the page summaries
|
||||
of the top-k results.
|
||||
It limits the Document content by doc_content_chars_max.
|
||||
"""
|
||||
|
||||
top_k_results: int = 3
|
||||
lang: str = "en"
|
||||
load_all_available_meta: bool = False
|
||||
doc_content_chars_max: int = 4000
|
||||
|
||||
def __init__(self, doc_content_chars_max: int = 4000):
|
||||
self.doc_content_chars_max = doc_content_chars_max
|
||||
|
||||
def run(self, query: str) -> str:
|
||||
wikipedia.set_lang(self.lang)
|
||||
wiki_client = wikipedia
|
||||
|
||||
"""Run Wikipedia search and get page summaries."""
|
||||
page_titles = wiki_client.search(query[:WIKIPEDIA_MAX_QUERY_LENGTH])
|
||||
summaries = []
|
||||
for page_title in page_titles[: self.top_k_results]:
|
||||
if wiki_page := self._fetch_page(page_title):
|
||||
if summary := self._formatted_page_summary(page_title, wiki_page):
|
||||
summaries.append(summary)
|
||||
if not summaries:
|
||||
return "No good Wikipedia Search Result was found"
|
||||
return "\n\n".join(summaries)[: self.doc_content_chars_max]
|
||||
|
||||
@staticmethod
|
||||
def _formatted_page_summary(page_title: str, wiki_page: Any) -> Optional[str]:
|
||||
return f"Page: {page_title}\nSummary: {wiki_page.summary}"
|
||||
|
||||
def _fetch_page(self, page: str) -> Optional[str]:
|
||||
try:
|
||||
return wikipedia.page(title=page, auto_suggest=False)
|
||||
except (
|
||||
wikipedia.exceptions.PageError,
|
||||
wikipedia.exceptions.DisambiguationError,
|
||||
):
|
||||
return None
|
||||
|
||||
class WikipediaQueryRun:
|
||||
"""Tool that searches the Wikipedia API."""
|
||||
|
||||
name = "Wikipedia"
|
||||
description = (
|
||||
"A wrapper around Wikipedia. "
|
||||
"Useful for when you need to answer general questions about "
|
||||
"people, places, companies, facts, historical events, or other subjects. "
|
||||
"Input should be a search query."
|
||||
)
|
||||
api_wrapper: WikipediaAPIWrapper
|
||||
|
||||
def __init__(self, api_wrapper: WikipediaAPIWrapper):
|
||||
self.api_wrapper = api_wrapper
|
||||
|
||||
def _run(
|
||||
self,
|
||||
query: str,
|
||||
) -> str:
|
||||
"""Use the Wikipedia tool."""
|
||||
return self.api_wrapper.run(query)
|
||||
class WikiPediaSearchTool(BuiltinTool):
|
||||
def _invoke(self,
|
||||
user_id: str,
|
||||
@@ -24,14 +87,10 @@ class WikiPediaSearchTool(BuiltinTool):
|
||||
return self.create_text_message('Please input query')
|
||||
|
||||
tool = WikipediaQueryRun(
|
||||
name="wikipedia",
|
||||
api_wrapper=WikipediaAPIWrapper(doc_content_chars_max=4000),
|
||||
args_schema=WikipediaInput
|
||||
)
|
||||
|
||||
result = tool.run(tool_input={
|
||||
'query': query
|
||||
})
|
||||
result = tool._run(query)
|
||||
|
||||
return self.create_text_message(self.summary(user_id=user_id,content=result))
|
||||
|
||||
@@ -234,6 +234,9 @@ class CodeNode(BaseNode):
|
||||
parameters_validated = {}
|
||||
for output_name, output_config in output_schema.items():
|
||||
dot = '.' if prefix else ''
|
||||
if output_name not in result:
|
||||
raise ValueError(f'Output {prefix}{dot}{output_name} is missing.')
|
||||
|
||||
if output_config.type == 'object':
|
||||
# check if output is object
|
||||
if not isinstance(result.get(output_name), dict):
|
||||
|
||||
@@ -242,7 +242,10 @@ class KnowledgeRetrievalNode(BaseNode):
|
||||
# get top k
|
||||
top_k = retrieval_model_config['top_k']
|
||||
# get retrieval method
|
||||
retrival_method = retrieval_model_config['search_method']
|
||||
if dataset.indexing_technique == "economy":
|
||||
retrival_method = 'keyword_search'
|
||||
else:
|
||||
retrival_method = retrieval_model_config['search_method']
|
||||
# get reranking model
|
||||
reranking_model=retrieval_model_config['reranking_model'] \
|
||||
if retrieval_model_config['reranking_enable'] else None
|
||||
|
||||
@@ -13,7 +13,7 @@ from core.prompt.entities.advanced_prompt_entities import ChatModelMessage, Comp
|
||||
from core.prompt.simple_prompt_transform import ModelMode
|
||||
from core.prompt.utils.prompt_message_util import PromptMessageUtil
|
||||
from core.workflow.entities.base_node_data_entities import BaseNodeData
|
||||
from core.workflow.entities.node_entities import NodeRunResult, NodeType
|
||||
from core.workflow.entities.node_entities import NodeRunMetadataKey, NodeRunResult, NodeType
|
||||
from core.workflow.entities.variable_pool import VariablePool
|
||||
from core.workflow.nodes.llm.llm_node import LLMNode
|
||||
from core.workflow.nodes.question_classifier.entities import QuestionClassifierNodeData
|
||||
@@ -65,7 +65,9 @@ class QuestionClassifierNode(LLMNode):
|
||||
categories = [_class.name for _class in node_data.classes]
|
||||
try:
|
||||
result_text_json = json.loads(result_text.strip('```JSON\n'))
|
||||
categories = result_text_json.get('categories', [])
|
||||
categories_result = result_text_json.get('categories', [])
|
||||
if categories_result:
|
||||
categories = categories_result
|
||||
except Exception:
|
||||
logging.error(f"Failed to parse result text: {result_text}")
|
||||
try:
|
||||
@@ -89,14 +91,24 @@ class QuestionClassifierNode(LLMNode):
|
||||
inputs=variables,
|
||||
process_data=process_data,
|
||||
outputs=outputs,
|
||||
edge_source_handle=classes_map.get(categories[0], None)
|
||||
edge_source_handle=classes_map.get(categories[0], None),
|
||||
metadata={
|
||||
NodeRunMetadataKey.TOTAL_TOKENS: usage.total_tokens,
|
||||
NodeRunMetadataKey.TOTAL_PRICE: usage.total_price,
|
||||
NodeRunMetadataKey.CURRENCY: usage.currency
|
||||
}
|
||||
)
|
||||
|
||||
except ValueError as e:
|
||||
return NodeRunResult(
|
||||
status=WorkflowNodeExecutionStatus.FAILED,
|
||||
inputs=variables,
|
||||
error=str(e)
|
||||
error=str(e),
|
||||
metadata={
|
||||
NodeRunMetadataKey.TOTAL_TOKENS: usage.total_tokens,
|
||||
NodeRunMetadataKey.TOTAL_PRICE: usage.total_price,
|
||||
NodeRunMetadataKey.CURRENCY: usage.currency
|
||||
}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional
|
||||
from typing import Optional, cast
|
||||
|
||||
from core.app.app_config.entities import FileExtraConfig
|
||||
from core.app.apps.base_app_queue_manager import GenerateTaskStoppedException
|
||||
from core.file.file_obj import FileVar
|
||||
from core.file.file_obj import FileTransferMethod, FileType, FileVar
|
||||
from core.workflow.callbacks.base_workflow_callback import BaseWorkflowCallback
|
||||
from core.workflow.entities.node_entities import NodeRunMetadataKey, NodeRunResult, NodeType
|
||||
from core.workflow.entities.variable_pool import VariablePool, VariableValue
|
||||
@@ -16,6 +17,7 @@ from core.workflow.nodes.end.end_node import EndNode
|
||||
from core.workflow.nodes.http_request.http_request_node import HttpRequestNode
|
||||
from core.workflow.nodes.if_else.if_else_node import IfElseNode
|
||||
from core.workflow.nodes.knowledge_retrieval.knowledge_retrieval_node import KnowledgeRetrievalNode
|
||||
from core.workflow.nodes.llm.entities import LLMNodeData
|
||||
from core.workflow.nodes.llm.llm_node import LLMNode
|
||||
from core.workflow.nodes.question_classifier.question_classifier_node import QuestionClassifierNode
|
||||
from core.workflow.nodes.start.start_node import StartNode
|
||||
@@ -219,7 +221,8 @@ class WorkflowEngineManager:
|
||||
raise ValueError('node id not found in workflow graph')
|
||||
|
||||
# Get node class
|
||||
node_cls = node_classes.get(NodeType.value_of(node_config.get('data', {}).get('type')))
|
||||
node_type = NodeType.value_of(node_config.get('data', {}).get('type'))
|
||||
node_cls = node_classes.get(node_type)
|
||||
|
||||
# init workflow run state
|
||||
node_instance = node_cls(
|
||||
@@ -252,11 +255,40 @@ class WorkflowEngineManager:
|
||||
variable_node_id = variable_selector[0]
|
||||
variable_key_list = variable_selector[1:]
|
||||
|
||||
# get value
|
||||
value = user_inputs.get(variable_key)
|
||||
|
||||
# temp fix for image type
|
||||
if node_type == NodeType.LLM:
|
||||
new_value = []
|
||||
if isinstance(value, list):
|
||||
node_data = node_instance.node_data
|
||||
node_data = cast(LLMNodeData, node_data)
|
||||
|
||||
detail = node_data.vision.configs.detail if node_data.vision.configs else None
|
||||
|
||||
for item in value:
|
||||
if isinstance(item, dict) and 'type' in item and item['type'] == 'image':
|
||||
transfer_method = FileTransferMethod.value_of(item.get('transfer_method'))
|
||||
file = FileVar(
|
||||
tenant_id=workflow.tenant_id,
|
||||
type=FileType.IMAGE,
|
||||
transfer_method=transfer_method,
|
||||
url=item.get('url') if transfer_method == FileTransferMethod.REMOTE_URL else None,
|
||||
related_id=item.get(
|
||||
'upload_file_id') if transfer_method == FileTransferMethod.LOCAL_FILE else None,
|
||||
extra_config=FileExtraConfig(image_config={'detail': detail} if detail else None),
|
||||
)
|
||||
new_value.append(file)
|
||||
|
||||
if new_value:
|
||||
value = new_value
|
||||
|
||||
# append variable and value to variable pool
|
||||
variable_pool.append_variable(
|
||||
node_id=variable_node_id,
|
||||
variable_key_list=variable_key_list,
|
||||
value=user_inputs.get(variable_key)
|
||||
value=value
|
||||
)
|
||||
# run node
|
||||
node_run_result = node_instance.run(
|
||||
|
||||
@@ -28,6 +28,7 @@ def init_app(app: Flask) -> Celery:
|
||||
|
||||
celery_app.conf.update(
|
||||
result_backend=app.config["CELERY_RESULT_BACKEND"],
|
||||
broker_connection_retry_on_startup=True,
|
||||
)
|
||||
|
||||
if app.config["BROKER_USE_SSL"]:
|
||||
|
||||
@@ -815,7 +815,7 @@ class Message(db.Model):
|
||||
@property
|
||||
def workflow_run(self):
|
||||
if self.workflow_run_id:
|
||||
from api.models.workflow import WorkflowRun
|
||||
from .workflow import WorkflowRun
|
||||
return db.session.query(WorkflowRun).filter(WorkflowRun.id == self.workflow_run_id).first()
|
||||
|
||||
return None
|
||||
|
||||
@@ -299,6 +299,10 @@ class WorkflowRun(db.Model):
|
||||
Message.workflow_run_id == self.id
|
||||
).first()
|
||||
|
||||
@property
|
||||
def workflow(self):
|
||||
return db.session.query(Workflow).filter(Workflow.id == self.workflow_id).first()
|
||||
|
||||
|
||||
class WorkflowNodeExecutionTriggeredFrom(Enum):
|
||||
"""
|
||||
|
||||
@@ -221,7 +221,8 @@ class AppService:
|
||||
"name": app.name,
|
||||
"mode": app.mode,
|
||||
"icon": app.icon,
|
||||
"icon_background": app.icon_background
|
||||
"icon_background": app.icon_background,
|
||||
"description": app.description
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
from typing import Optional, Union
|
||||
|
||||
from sqlalchemy import or_
|
||||
|
||||
from core.app.entities.app_invoke_entities import InvokeFrom
|
||||
from core.llm_generator.llm_generator import LLMGenerator
|
||||
from extensions.ext_database import db
|
||||
from libs.infinite_scroll_pagination import InfiniteScrollPagination
|
||||
@@ -13,8 +16,9 @@ class ConversationService:
|
||||
@classmethod
|
||||
def pagination_by_last_id(cls, app_model: App, user: Optional[Union[Account, EndUser]],
|
||||
last_id: Optional[str], limit: int,
|
||||
include_ids: Optional[list] = None, exclude_ids: Optional[list] = None,
|
||||
exclude_debug_conversation: bool = False) -> InfiniteScrollPagination:
|
||||
invoke_from: InvokeFrom,
|
||||
include_ids: Optional[list] = None,
|
||||
exclude_ids: Optional[list] = None) -> InfiniteScrollPagination:
|
||||
if not user:
|
||||
return InfiniteScrollPagination(data=[], limit=limit, has_more=False)
|
||||
|
||||
@@ -24,6 +28,7 @@ class ConversationService:
|
||||
Conversation.from_source == ('api' if isinstance(user, EndUser) else 'console'),
|
||||
Conversation.from_end_user_id == (user.id if isinstance(user, EndUser) else None),
|
||||
Conversation.from_account_id == (user.id if isinstance(user, Account) else None),
|
||||
or_(Conversation.invoke_from.is_(None), Conversation.invoke_from == invoke_from.value)
|
||||
)
|
||||
|
||||
if include_ids is not None:
|
||||
@@ -32,9 +37,6 @@ class ConversationService:
|
||||
if exclude_ids is not None:
|
||||
base_query = base_query.filter(~Conversation.id.in_(exclude_ids))
|
||||
|
||||
if exclude_debug_conversation:
|
||||
base_query = base_query.filter(Conversation.override_model_configs == None)
|
||||
|
||||
if last_id:
|
||||
last_conversation = base_query.filter(
|
||||
Conversation.id == last_id,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from typing import Optional, Union
|
||||
|
||||
from core.app.entities.app_invoke_entities import InvokeFrom
|
||||
from extensions.ext_database import db
|
||||
from libs.infinite_scroll_pagination import InfiniteScrollPagination
|
||||
from models.account import Account
|
||||
@@ -11,8 +12,8 @@ from services.conversation_service import ConversationService
|
||||
class WebConversationService:
|
||||
@classmethod
|
||||
def pagination_by_last_id(cls, app_model: App, user: Optional[Union[Account, EndUser]],
|
||||
last_id: Optional[str], limit: int, pinned: Optional[bool] = None,
|
||||
exclude_debug_conversation: bool = False) -> InfiniteScrollPagination:
|
||||
last_id: Optional[str], limit: int, invoke_from: InvokeFrom,
|
||||
pinned: Optional[bool] = None) -> InfiniteScrollPagination:
|
||||
include_ids = None
|
||||
exclude_ids = None
|
||||
if pinned is not None:
|
||||
@@ -32,9 +33,9 @@ class WebConversationService:
|
||||
user=user,
|
||||
last_id=last_id,
|
||||
limit=limit,
|
||||
invoke_from=invoke_from,
|
||||
include_ids=include_ids,
|
||||
exclude_ids=exclude_ids,
|
||||
exclude_debug_conversation=exclude_debug_conversation
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
24
api/tests/unittests/test_model.py
Normal file
24
api/tests/unittests/test_model.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import pytest
|
||||
from pydantic.error_wrappers import ValidationError
|
||||
|
||||
from core.rag.datasource.vdb.milvus.milvus_vector import MilvusConfig
|
||||
|
||||
|
||||
def test_default_value():
|
||||
valid_config = {
|
||||
'host': 'localhost',
|
||||
'port': 19530,
|
||||
'user': 'root',
|
||||
'password': 'Milvus'
|
||||
}
|
||||
|
||||
for key in valid_config:
|
||||
config = valid_config.copy()
|
||||
del config[key]
|
||||
with pytest.raises(ValidationError) as e:
|
||||
MilvusConfig(**config)
|
||||
assert e.value.errors()[1]['msg'] == f'config MILVUS_{key.upper()} is required'
|
||||
|
||||
config = MilvusConfig(**valid_config)
|
||||
assert config.secure is False
|
||||
assert config.database == 'default'
|
||||
@@ -2,7 +2,7 @@ version: '3'
|
||||
services:
|
||||
# API service
|
||||
api:
|
||||
image: langgenius/dify-api:0.6.0
|
||||
image: langgenius/dify-api:0.6.1
|
||||
restart: always
|
||||
environment:
|
||||
# Startup mode, 'api' starts the API server.
|
||||
@@ -150,7 +150,7 @@ services:
|
||||
# worker service
|
||||
# The Celery worker for processing the queue.
|
||||
worker:
|
||||
image: langgenius/dify-api:0.6.0
|
||||
image: langgenius/dify-api:0.6.1
|
||||
restart: always
|
||||
environment:
|
||||
# Startup mode, 'worker' starts the Celery worker for processing the queue.
|
||||
@@ -232,7 +232,7 @@ services:
|
||||
|
||||
# Frontend web application.
|
||||
web:
|
||||
image: langgenius/dify-web:0.6.0
|
||||
image: langgenius/dify-web:0.6.1
|
||||
restart: always
|
||||
environment:
|
||||
EDITION: SELF_HOSTED
|
||||
|
||||
@@ -333,7 +333,7 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
|
||||
)}/>
|
||||
<div className='px-4 pb-2'>
|
||||
<div className='flex items-center gap-1 text-gray-700 text-md leading-6 font-semibold'>
|
||||
{t('app.newApp.advanced')}
|
||||
{showSwitchTip === 'chat' ? t('app.newApp.advanced') : t('app.types.workflow')}
|
||||
<span className='px-1 rounded-[5px] bg-white border border-black/8 text-gray-500 text-[10px] leading-[18px] font-medium'>BETA</span>
|
||||
</div>
|
||||
<div className='text-orange-500 text-xs leading-[18px] font-medium'>{t('app.newApp.advancedFor').toLocaleUpperCase()}</div>
|
||||
|
||||
@@ -20,7 +20,8 @@ import ConfigContext from '@/context/debug-configuration'
|
||||
import ConfigPrompt from '@/app/components/app/configuration/config-prompt'
|
||||
import ConfigVar from '@/app/components/app/configuration/config-var'
|
||||
import { type CitationConfig, type ModelConfig, type ModerationConfig, type MoreLikeThisConfig, type PromptVariable, type SpeechToTextConfig, type SuggestedQuestionsAfterAnswerConfig, type TextToSpeechConfig } from '@/models/debug'
|
||||
import { AppType, ModelModeType } from '@/types/app'
|
||||
import type { AppType } from '@/types/app'
|
||||
import { ModelModeType } from '@/types/app'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import ConfigParamModal from '@/app/components/app/configuration/toolbox/annotation/config-param-modal'
|
||||
import AnnotationFullModal from '@/app/components/billing/annotation-full/modal'
|
||||
@@ -60,7 +61,7 @@ const Config: FC = () => {
|
||||
moderationConfig,
|
||||
setModerationConfig,
|
||||
} = useContext(ConfigContext)
|
||||
const isChatApp = mode === AppType.chat
|
||||
const isChatApp = ['advanced-chat', 'agent-chat', 'chat'].includes(mode)
|
||||
const { data: speech2textDefaultModel } = useDefaultModel(ModelTypeEnum.speech2text)
|
||||
const { data: text2speechDefaultModel } = useDefaultModel(ModelTypeEnum.tts)
|
||||
const { setShowModerationSettingModal } = useModalContext()
|
||||
|
||||
@@ -3,7 +3,7 @@ import { clone } from 'lodash-es'
|
||||
import produce from 'immer'
|
||||
import type { ChatPromptConfig, CompletionPromptConfig, ConversationHistoriesRole, PromptItem } from '@/models/debug'
|
||||
import { PromptMode } from '@/models/debug'
|
||||
import { AppType, ModelModeType } from '@/types/app'
|
||||
import { ModelModeType } from '@/types/app'
|
||||
import { DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config'
|
||||
import { PRE_PROMPT_PLACEHOLDER_TEXT, checkHasContextBlock, checkHasHistoryBlock, checkHasQueryBlock } from '@/app/components/base/prompt-editor/constants'
|
||||
import { fetchPromptTemplate } from '@/service/debug'
|
||||
@@ -152,7 +152,7 @@ const useAdvancedPromptConfig = ({
|
||||
else
|
||||
draft.prompt.text = completionPromptConfig.prompt?.text.replace(PRE_PROMPT_PLACEHOLDER_TEXT, toReplacePrePrompt)
|
||||
|
||||
if (appMode === AppType.chat && completionPromptConfig.conversation_histories_role.assistant_prefix && completionPromptConfig.conversation_histories_role.user_prefix)
|
||||
if (['advanced-chat', 'agent-chat', 'chat'].includes(appMode) && completionPromptConfig.conversation_histories_role.assistant_prefix && completionPromptConfig.conversation_histories_role.user_prefix)
|
||||
draft.conversation_histories_role = completionPromptConfig.conversation_histories_role
|
||||
})
|
||||
setCompletionPromptConfig(newPromptConfig)
|
||||
|
||||
@@ -388,7 +388,10 @@ const Configuration: FC = () => {
|
||||
const promptMode = modelConfig.prompt_type === PromptMode.advanced ? PromptMode.advanced : PromptMode.simple
|
||||
doSetPromptMode(promptMode)
|
||||
if (promptMode === PromptMode.advanced) {
|
||||
setChatPromptConfig(modelConfig.chat_prompt_config || clone(DEFAULT_CHAT_PROMPT_CONFIG) as any)
|
||||
if (modelConfig.chat_prompt_config && modelConfig.chat_prompt_config.prompt.length > 0)
|
||||
setChatPromptConfig(modelConfig.chat_prompt_config)
|
||||
else
|
||||
setChatPromptConfig(clone(DEFAULT_CHAT_PROMPT_CONFIG) as any)
|
||||
setCompletionPromptConfig(modelConfig.completion_prompt_config || clone(DEFAULT_COMPLETION_PROMPT_CONFIG) as any)
|
||||
setCanReturnToSimpleMode(false)
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ const CreateAppModal = ({ show, onSuccess, onClose }: CreateAppDialogProps) => {
|
||||
'hidden z-20 absolute right-[26px] top-[-158px] w-[376px] rounded-xl bg-white border-[0.5px] border-[rgba(0,0,0,0.05)] shadow-lg group-hover:block',
|
||||
)}
|
||||
>
|
||||
<div className={cn('w-full h-[256px] bg-center bg-no-repeat bg-contain rounded-xl', s.basicPic)}/>
|
||||
<div className={cn('w-full h-[256px] bg-center bg-no-repeat bg-contain rounded-xl', s.advancedPic)}/>
|
||||
<div className='px-4 pb-2'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className='flex items-center'>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
'use client'
|
||||
import { useRef, useState } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { t } from 'i18next'
|
||||
import { useParams, usePathname } from 'next/navigation'
|
||||
import s from './style.module.css'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { randomString } from '@/utils'
|
||||
import { textToAudio } from '@/service/share'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
|
||||
type AudioBtnProps = {
|
||||
value: string
|
||||
@@ -14,6 +15,8 @@ type AudioBtnProps = {
|
||||
isAudition?: boolean
|
||||
}
|
||||
|
||||
type AudioState = 'initial' | 'loading' | 'playing' | 'paused' | 'ended'
|
||||
|
||||
const AudioBtn = ({
|
||||
value,
|
||||
voice,
|
||||
@@ -21,9 +24,8 @@ const AudioBtn = ({
|
||||
isAudition,
|
||||
}: AudioBtnProps) => {
|
||||
const audioRef = useRef<HTMLAudioElement | null>(null)
|
||||
const [isPlaying, setIsPlaying] = useState(false)
|
||||
const [isPause, setPause] = useState(false)
|
||||
const [hasEnded, setHasEnded] = useState(false)
|
||||
const [audioState, setAudioState] = useState<AudioState>('initial')
|
||||
|
||||
const selector = useRef(`play-tooltip-${randomString(4)}`)
|
||||
const params = useParams()
|
||||
const pathname = usePathname()
|
||||
@@ -34,9 +36,11 @@ const AudioBtn = ({
|
||||
return ''
|
||||
}
|
||||
|
||||
const playAudio = async () => {
|
||||
const loadAudio = async () => {
|
||||
const formData = new FormData()
|
||||
if (value !== '') {
|
||||
setAudioState('loading')
|
||||
|
||||
formData.append('text', removeCodeBlocks(value))
|
||||
formData.append('voice', removeCodeBlocks(voice))
|
||||
|
||||
@@ -59,67 +63,80 @@ const AudioBtn = ({
|
||||
const blob_bytes = Buffer.from(audioResponse.data, 'latin1')
|
||||
const blob = new Blob([blob_bytes], { type: 'audio/wav' })
|
||||
const audioUrl = URL.createObjectURL(blob)
|
||||
const audio = new Audio(audioUrl)
|
||||
audioRef.current = audio
|
||||
audio.play().then(() => {}).catch(() => {
|
||||
setIsPlaying(false)
|
||||
URL.revokeObjectURL(audioUrl)
|
||||
})
|
||||
audio.onended = () => {
|
||||
setHasEnded(true)
|
||||
setIsPlaying(false)
|
||||
}
|
||||
audioRef.current!.src = audioUrl
|
||||
}
|
||||
catch (error) {
|
||||
setIsPlaying(false)
|
||||
setAudioState('initial')
|
||||
console.error('Error playing audio:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
const togglePlayPause = () => {
|
||||
|
||||
const handleToggle = () => {
|
||||
if (audioState === 'initial')
|
||||
loadAudio()
|
||||
if (audioRef.current) {
|
||||
if (isPlaying) {
|
||||
if (!hasEnded) {
|
||||
setPause(false)
|
||||
audioRef.current.play()
|
||||
}
|
||||
if (!isPause) {
|
||||
setPause(true)
|
||||
audioRef.current.pause()
|
||||
}
|
||||
if (audioState === 'playing') {
|
||||
audioRef.current.pause()
|
||||
setAudioState('paused')
|
||||
}
|
||||
else if (!isPlaying) {
|
||||
if (isPause) {
|
||||
setPause(false)
|
||||
audioRef.current.play()
|
||||
}
|
||||
else {
|
||||
setHasEnded(false)
|
||||
playAudio().then()
|
||||
}
|
||||
else if (audioState === 'paused' || audioState === 'ended') {
|
||||
audioRef.current.play()
|
||||
setAudioState('playing')
|
||||
}
|
||||
setIsPlaying(prevIsPlaying => !prevIsPlaying)
|
||||
}
|
||||
else {
|
||||
setIsPlaying(true)
|
||||
if (!isPlaying)
|
||||
playAudio().then()
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const currentAudio = audioRef.current
|
||||
const handleLoading = () => {
|
||||
setAudioState('loading')
|
||||
}
|
||||
const handlePlay = () => {
|
||||
currentAudio?.play()
|
||||
setAudioState('playing')
|
||||
}
|
||||
const handleEnded = () => {
|
||||
setAudioState('ended')
|
||||
}
|
||||
currentAudio?.addEventListener('progress', handleLoading)
|
||||
currentAudio?.addEventListener('canplaythrough', handlePlay)
|
||||
currentAudio?.addEventListener('ended', handleEnded)
|
||||
return () => {
|
||||
if (currentAudio) {
|
||||
currentAudio.removeEventListener('progress', handleLoading)
|
||||
currentAudio.removeEventListener('canplaythrough', handlePlay)
|
||||
currentAudio.removeEventListener('ended', handleEnded)
|
||||
URL.revokeObjectURL(currentAudio.src)
|
||||
currentAudio.src = ''
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
const tooltipContent = {
|
||||
initial: t('appApi.play'),
|
||||
ended: t('appApi.play'),
|
||||
paused: t('appApi.pause'),
|
||||
playing: t('appApi.playing'),
|
||||
loading: t('appApi.loading'),
|
||||
}[audioState]
|
||||
|
||||
return (
|
||||
<div className={`${(isPlaying && !hasEnded) ? 'mr-1' : className}`}>
|
||||
<div className={`${(audioState === 'loading' || audioState === 'playing') ? 'mr-1' : className}`}>
|
||||
<Tooltip
|
||||
selector={selector.current}
|
||||
content={(!isPause ? ((isPlaying && !hasEnded) ? t('appApi.playing') : t('appApi.play')) : t('appApi.pause')) as string}
|
||||
content={tooltipContent}
|
||||
className='z-10'
|
||||
>
|
||||
<div
|
||||
<button
|
||||
disabled={audioState === 'loading'}
|
||||
className={`box-border p-0.5 flex items-center justify-center cursor-pointer ${isAudition || '!p-0 rounded-md bg-white'}`}
|
||||
onClick={togglePlayPause}>
|
||||
<div className={`w-6 h-6 rounded-md ${!isAudition ? 'w-4 h-4 hover:bg-gray-50' : 'hover:bg-gray-50'} ${(isPlaying && !hasEnded) ? s.pauseIcon : s.playIcon}`}></div>
|
||||
</div>
|
||||
onClick={handleToggle}>
|
||||
{audioState === 'loading' && <div className='w-6 h-6 rounded-md flex items-center justify-center p-2'><Loading /></div>}
|
||||
{audioState !== 'loading' && <div className={`w-6 h-6 rounded-md ${!isAudition ? 'w-4 h-4 hover:bg-gray-50' : 'hover:bg-gray-50'} ${(audioState === 'playing') ? s.pauseIcon : s.playIcon}`}></div>}
|
||||
</button>
|
||||
</Tooltip>
|
||||
<audio ref={audioRef} src='' className='hidden' />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,4 +7,4 @@
|
||||
background-image: url(~@/app/components/develop/secret-key/assets/pause.svg);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'
|
||||
import { ContentEditable } from '@lexical/react/LexicalContentEditable'
|
||||
import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary'
|
||||
import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin'
|
||||
import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin'
|
||||
// import TreeView from './plugins/tree-view'
|
||||
import Placeholder from './plugins/placeholder'
|
||||
import ComponentPickerBlock from './plugins/component-picker-block'
|
||||
@@ -208,6 +209,7 @@ const PromptEditor: FC<PromptEditorProps> = ({
|
||||
<OnChangePlugin onChange={handleEditorChange} />
|
||||
<OnBlurBlock onBlur={onBlur} onFocus={onFocus} />
|
||||
<UpdateBlock instanceId={instanceId} />
|
||||
<HistoryPlugin />
|
||||
{/* <TreeView /> */}
|
||||
</div>
|
||||
</LexicalComposer>
|
||||
|
||||
@@ -40,7 +40,7 @@ const OnBlurBlock: FC<OnBlurBlockProps> = ({
|
||||
() => {
|
||||
ref.current = setTimeout(() => {
|
||||
editor.dispatchCommand(KEY_ESCAPE_COMMAND, new KeyboardEvent('keydown', { key: 'Escape' }))
|
||||
}, 100)
|
||||
}, 200)
|
||||
|
||||
if (onBlur)
|
||||
onBlur()
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
export type FormValue = Record<string, any>
|
||||
|
||||
export type TypeWithI18N<T = string> = {
|
||||
'en-US': T
|
||||
'zh-Hans': T
|
||||
en_US: T
|
||||
zh_Hans: T
|
||||
[key: string]: T
|
||||
}
|
||||
|
||||
@@ -67,16 +67,16 @@ export enum ModelStatusEnum {
|
||||
|
||||
export const MODEL_STATUS_TEXT: { [k: string]: TypeWithI18N } = {
|
||||
'no-configure': {
|
||||
'en-US': 'No Configure',
|
||||
'zh-Hans': '未配置凭据',
|
||||
en_US: 'No Configure',
|
||||
zh_Hans: '未配置凭据',
|
||||
},
|
||||
'quota-exceeded': {
|
||||
'en-US': 'Quota Exceeded',
|
||||
'zh-Hans': '额度不足',
|
||||
en_US: 'Quota Exceeded',
|
||||
zh_Hans: '额度不足',
|
||||
},
|
||||
'no-permission': {
|
||||
'en-US': 'No Permission',
|
||||
'zh-Hans': '无使用权限',
|
||||
en_US: 'No Permission',
|
||||
zh_Hans: '无使用权限',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -11,11 +11,11 @@ import type {
|
||||
DefaultModel,
|
||||
DefaultModelResponse,
|
||||
Model,
|
||||
|
||||
ModelTypeEnum,
|
||||
} from './declarations'
|
||||
import {
|
||||
ConfigurateMethodEnum,
|
||||
ModelStatusEnum,
|
||||
} from './declarations'
|
||||
import I18n from '@/context/i18n'
|
||||
import {
|
||||
@@ -132,6 +132,7 @@ export const useCurrentProviderAndModel = (modelList: Model[], defaultModel?: De
|
||||
|
||||
export const useTextGenerationCurrentProviderAndModelAndModelList = (defaultModel?: DefaultModel) => {
|
||||
const { textGenerationModelList } = useProviderContext()
|
||||
const activeTextGenerationModelList = textGenerationModelList.filter(model => model.status === ModelStatusEnum.active)
|
||||
const {
|
||||
currentProvider,
|
||||
currentModel,
|
||||
@@ -141,6 +142,7 @@ export const useTextGenerationCurrentProviderAndModelAndModelList = (defaultMode
|
||||
currentProvider,
|
||||
currentModel,
|
||||
textGenerationModelList,
|
||||
activeTextGenerationModelList,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
|
||||
const {
|
||||
currentProvider,
|
||||
currentModel,
|
||||
textGenerationModelList,
|
||||
activeTextGenerationModelList,
|
||||
} = useTextGenerationCurrentProviderAndModelAndModelList(
|
||||
{ provider, model: modelId },
|
||||
)
|
||||
@@ -114,7 +114,7 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
|
||||
}
|
||||
|
||||
const handleChangeModel = ({ provider, model }: DefaultModel) => {
|
||||
const targetProvider = textGenerationModelList.find(modelItem => modelItem.provider === provider)
|
||||
const targetProvider = activeTextGenerationModelList.find(modelItem => modelItem.provider === provider)
|
||||
const targetModelItem = targetProvider?.models.find(modelItem => modelItem.model === model)
|
||||
setModel({
|
||||
modelId: model,
|
||||
@@ -223,7 +223,7 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
|
||||
</div>
|
||||
<ModelSelector
|
||||
defaultModel={(provider || modelId) ? { provider, model: modelId } : undefined}
|
||||
modelList={textGenerationModelList}
|
||||
modelList={activeTextGenerationModelList}
|
||||
onSelect={handleChangeModel}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -301,6 +301,8 @@ export const useNodesInteractions = () => {
|
||||
target,
|
||||
targetHandle,
|
||||
}) => {
|
||||
if (source === target)
|
||||
return
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
|
||||
@@ -176,6 +176,8 @@ export const useWorkflowRun = () => {
|
||||
const {
|
||||
getNodes,
|
||||
setNodes,
|
||||
edges,
|
||||
setEdges,
|
||||
} = store.getState()
|
||||
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
|
||||
draft.task_id = task_id
|
||||
@@ -192,6 +194,15 @@ export const useWorkflowRun = () => {
|
||||
})
|
||||
})
|
||||
setNodes(newNodes)
|
||||
const newEdges = produce(edges, (draft) => {
|
||||
draft.forEach((edge) => {
|
||||
edge.data = {
|
||||
...edge.data,
|
||||
_runned: false,
|
||||
}
|
||||
})
|
||||
})
|
||||
setEdges(newEdges)
|
||||
|
||||
if (onWorkflowStarted)
|
||||
onWorkflowStarted(params)
|
||||
|
||||
@@ -160,8 +160,10 @@ export const useWorkflow = () => {
|
||||
|
||||
if (incomers.length) {
|
||||
incomers.forEach((node) => {
|
||||
callback(node)
|
||||
traverse(node, callback)
|
||||
if (!list.find(n => node.id === n.id)) {
|
||||
callback(node)
|
||||
traverse(node, callback)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -272,7 +274,10 @@ export const useWorkflow = () => {
|
||||
}, [isVarUsedInNodes])
|
||||
|
||||
const isValidConnection = useCallback(({ source, target }: Connection) => {
|
||||
const { getNodes } = store.getState()
|
||||
const {
|
||||
edges,
|
||||
getNodes,
|
||||
} = store.getState()
|
||||
const nodes = getNodes()
|
||||
const sourceNode: Node = nodes.find(node => node.id === source)!
|
||||
const targetNode: Node = nodes.find(node => node.id === target)!
|
||||
@@ -287,7 +292,21 @@ export const useWorkflow = () => {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
const hasCycle = (node: Node, visited = new Set()) => {
|
||||
if (visited.has(node.id))
|
||||
return false
|
||||
|
||||
visited.add(node.id)
|
||||
|
||||
for (const outgoer of getOutgoers(node, nodes, edges)) {
|
||||
if (outgoer.id === source)
|
||||
return true
|
||||
if (hasCycle(outgoer, visited))
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return !hasCycle(targetNode)
|
||||
}, [store, nodesExtraData])
|
||||
|
||||
const formatTimeFromNow = useCallback((time: number) => {
|
||||
|
||||
@@ -12,6 +12,7 @@ import Split from '@/app/components/workflow/nodes/_base/components/split'
|
||||
import { InputVarType, NodeRunningStatus } from '@/app/components/workflow/types'
|
||||
import ResultPanel from '@/app/components/workflow/run/result-panel'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
|
||||
const i18nPrefix = 'workflow.singleRun'
|
||||
|
||||
@@ -51,7 +52,18 @@ const BeforeRunForm: FC<BeforeRunFormProps> = ({
|
||||
|
||||
const isFinished = runningStatus === NodeRunningStatus.Succeeded || runningStatus === NodeRunningStatus.Failed
|
||||
const isRunning = runningStatus === NodeRunningStatus.Running
|
||||
const isFileLoaded = (() => {
|
||||
// system files
|
||||
const filesForm = forms.find(item => !!item.values['#files#'])
|
||||
if (!filesForm)
|
||||
return true
|
||||
|
||||
const files = filesForm.values['#files#'] as any
|
||||
if (files?.some((item: any) => item.transfer_method === TransferMethod.local_file && !item.upload_file_id))
|
||||
return false
|
||||
|
||||
return true
|
||||
})()
|
||||
const handleRun = useCallback(() => {
|
||||
let errMsg = ''
|
||||
forms.forEach((form) => {
|
||||
@@ -129,7 +141,7 @@ const BeforeRunForm: FC<BeforeRunFormProps> = ({
|
||||
<StopCircle className='w-4 h-4 text-gray-500' />
|
||||
</div>
|
||||
)}
|
||||
<Button disabled={isRunning} type='primary' className='w-0 grow !h-8 flex items-center space-x-2 text-[13px]' onClick={handleRun}>
|
||||
<Button disabled={!isFileLoaded || isRunning} type='primary' className='w-0 grow !h-8 flex items-center space-x-2 text-[13px]' onClick={handleRun}>
|
||||
{isRunning && <Loading02 className='animate-spin w-4 h-4 text-white' />}
|
||||
<div>{t(`${i18nPrefix}.${isRunning ? 'running' : 'startRun'}`)}</div>
|
||||
</Button>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type FC, useEffect, useState } from 'react'
|
||||
import { type FC, useEffect, useRef, useState } from 'react'
|
||||
import React from 'react'
|
||||
import type { KnowledgeRetrievalNodeType } from './types'
|
||||
import { Folder } from '@/app/components/base/icons/src/vender/solid/files'
|
||||
@@ -10,10 +10,17 @@ const Node: FC<NodeProps<KnowledgeRetrievalNodeType>> = ({
|
||||
data,
|
||||
}) => {
|
||||
const [selectedDatasets, setSelectedDatasets] = useState<DataSet[]>([])
|
||||
const updateTime = useRef(0)
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
updateTime.current = updateTime.current + 1
|
||||
const currUpdateTime = updateTime.current
|
||||
|
||||
if (data.dataset_ids?.length > 0) {
|
||||
const { data: dataSetsWithDetail } = await fetchDatasets({ url: '/datasets', params: { page: 1, ids: data.dataset_ids } })
|
||||
// avoid old data overwrite new data
|
||||
if (currUpdateTime < updateTime.current)
|
||||
return
|
||||
setSelectedDatasets(dataSetsWithDetail)
|
||||
}
|
||||
else {
|
||||
@@ -33,7 +40,7 @@ const Node: FC<NodeProps<KnowledgeRetrievalNodeType>> = ({
|
||||
<div className='mr-1 shrink-0 p-1 bg-[#F5F8FF] rounded-md border-[0.5px] border-[#E0EAFF]'>
|
||||
<Folder className='w-3 h-3 text-[#444CE7]' />
|
||||
</div>
|
||||
<div className='text-xs font-normal text-gray-700'>
|
||||
<div className='grow w-0 text-xs font-normal text-gray-700 truncate'>
|
||||
{name}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -23,7 +23,7 @@ const Item: FC<ItemProps> = ({ title, value, onSelect, isSelected }) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(isSelected ? 'bg-white border-[2px] border-primary-400 shadow-xs' : 'bg-gray-25 border border-gray-100', 'flex items-center h-8 px-3 rounded-xl text-[13px] font-normal text-gray-900 cursor-pointer')}
|
||||
className={cn(isSelected ? 'bg-white border-[2px] border-primary-400 shadow-xs' : 'bg-gray-25 border border-gray-100', 'flex items-center h-8 px-3 rounded-lg text-[13px] font-normal text-gray-900 cursor-pointer')}
|
||||
onClick={handleSelect}
|
||||
>
|
||||
{title}
|
||||
@@ -43,7 +43,7 @@ const ResolutionPicker: FC<Props> = ({
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className='flex items-center'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className='mr-2 text-xs font-medium text-gray-500 uppercase'>{t(`${i18nPrefix}.resolution.name`)}</div>
|
||||
<div className='flex items-center space-x-1'>
|
||||
<Item
|
||||
|
||||
@@ -21,6 +21,7 @@ import ResultPanel from '@/app/components/workflow/run/result-panel'
|
||||
import TooltipPlus from '@/app/components/base/tooltip-plus'
|
||||
import { HelpCircle } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
const i18nPrefix = 'workflow.nodes.llm'
|
||||
|
||||
const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
@@ -51,6 +52,7 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
filterVar,
|
||||
handlePromptChange,
|
||||
handleMemoryChange,
|
||||
handleVisionResolutionEnabledChange,
|
||||
handleVisionResolutionChange,
|
||||
isShowSingleRun,
|
||||
hideSingleRun,
|
||||
@@ -240,12 +242,19 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
title={t(`${i18nPrefix}.vision`)}
|
||||
tooltip={t('appDebug.vision.description')!}
|
||||
operations={
|
||||
<ResolutionPicker
|
||||
value={inputs.vision.configs?.detail || Resolution.high}
|
||||
onChange={handleVisionResolutionChange}
|
||||
/>
|
||||
<Switch size='md' defaultValue={inputs.vision.enabled} onChange={handleVisionResolutionEnabledChange} />
|
||||
}
|
||||
/>
|
||||
>
|
||||
{inputs.vision.enabled
|
||||
? (
|
||||
<ResolutionPicker
|
||||
value={inputs.vision.configs?.detail || Resolution.high}
|
||||
onChange={handleVisionResolutionChange}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
|
||||
</Field>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -103,6 +103,7 @@ const useConfig = (id: string, payload: LLMNodeType) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [defaultConfig, isChatModel])
|
||||
|
||||
const [modelChanged, setModelChanged] = useState(false)
|
||||
const {
|
||||
currentProvider,
|
||||
currentModel,
|
||||
@@ -118,6 +119,7 @@ const useConfig = (id: string, payload: LLMNodeType) => {
|
||||
appendDefaultPromptConfig(draft, defaultConfig, model.mode === 'chat')
|
||||
})
|
||||
setInputs(newInputs)
|
||||
setModelChanged(true)
|
||||
}, [setInputs, defaultConfig, appendDefaultPromptConfig])
|
||||
|
||||
useEffect(() => {
|
||||
@@ -146,7 +148,35 @@ const useConfig = (id: string, payload: LLMNodeType) => {
|
||||
},
|
||||
)
|
||||
const isShowVisionConfig = !!currModel?.features?.includes(ModelFeatureEnum.vision)
|
||||
|
||||
// change to vision model to set vision enabled, else disabled
|
||||
useEffect(() => {
|
||||
if (!modelChanged)
|
||||
return
|
||||
setModelChanged(false)
|
||||
if (!isShowVisionConfig) {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
draft.vision = {
|
||||
enabled: false,
|
||||
}
|
||||
})
|
||||
setInputs(newInputs)
|
||||
return
|
||||
}
|
||||
if (!inputs.vision?.enabled) {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
if (!draft.vision?.enabled) {
|
||||
draft.vision = {
|
||||
enabled: true,
|
||||
configs: {
|
||||
detail: Resolution.high,
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isShowVisionConfig, modelChanged])
|
||||
// variables
|
||||
const { handleVarListChange, handleAddVariable } = useVarList<LLMNodeType>({
|
||||
inputs,
|
||||
@@ -176,6 +206,28 @@ const useConfig = (id: string, payload: LLMNodeType) => {
|
||||
setInputs(newInputs)
|
||||
}, [inputs, setInputs])
|
||||
|
||||
const handleVisionResolutionEnabledChange = useCallback((enabled: boolean) => {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
if (!draft.vision) {
|
||||
draft.vision = {
|
||||
enabled,
|
||||
configs: {
|
||||
detail: Resolution.high,
|
||||
},
|
||||
}
|
||||
}
|
||||
else {
|
||||
draft.vision.enabled = enabled
|
||||
if (!draft.vision.configs) {
|
||||
draft.vision.configs = {
|
||||
detail: Resolution.high,
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}, [inputs, setInputs])
|
||||
|
||||
const handleVisionResolutionChange = useCallback((newResolution: Resolution) => {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
if (!draft.vision.configs) {
|
||||
@@ -296,6 +348,7 @@ const useConfig = (id: string, payload: LLMNodeType) => {
|
||||
filterVar,
|
||||
handlePromptChange,
|
||||
handleMemoryChange,
|
||||
handleVisionResolutionEnabledChange,
|
||||
handleVisionResolutionChange,
|
||||
isShowSingleRun,
|
||||
hideSingleRun,
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
} from '../store'
|
||||
import { useWorkflowRun } from '../hooks'
|
||||
import type { StartNodeType } from '../nodes/start/types'
|
||||
import { TransferMethod } from '../../base/text-generation/types'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { useFeatures } from '@/app/components/base/features/hooks'
|
||||
|
||||
@@ -75,6 +76,13 @@ const InputsPanel = ({ onRun }: Props) => {
|
||||
handleRun({ inputs, files })
|
||||
}
|
||||
|
||||
const canRun = (() => {
|
||||
if (files?.some(item => (item.transfer_method as any) === TransferMethod.local_file && !item.upload_file_id))
|
||||
return false
|
||||
|
||||
return true
|
||||
})()
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='px-4 pb-2'>
|
||||
@@ -97,7 +105,7 @@ const InputsPanel = ({ onRun }: Props) => {
|
||||
<div className='flex items-center justify-between px-4 py-2'>
|
||||
<Button
|
||||
type='primary'
|
||||
disabled={workflowRunningData?.result?.status === WorkflowRunningStatus.Running}
|
||||
disabled={!canRun || workflowRunningData?.result?.status === WorkflowRunningStatus.Running}
|
||||
className='py-0 w-full h-8 rounded-lg text-[13px] font-medium'
|
||||
onClick={doRun}
|
||||
>
|
||||
|
||||
@@ -24,6 +24,60 @@ import type { ToolNodeType } from './nodes/tool/types'
|
||||
import { CollectionType } from '@/app/components/tools/types'
|
||||
import { toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
|
||||
|
||||
const WHITE = 'WHITE'
|
||||
const GRAY = 'GRAY'
|
||||
const BLACK = 'BLACK'
|
||||
|
||||
const isCyclicUtil = (nodeId: string, color: Record<string, string>, adjaList: Record<string, string[]>, stack: string[]) => {
|
||||
color[nodeId] = GRAY
|
||||
stack.push(nodeId)
|
||||
|
||||
for (let i = 0; i < adjaList[nodeId].length; ++i) {
|
||||
const childId = adjaList[nodeId][i]
|
||||
|
||||
if (color[childId] === GRAY) {
|
||||
stack.push(childId)
|
||||
return true
|
||||
}
|
||||
if (color[childId] === WHITE && isCyclicUtil(childId, color, adjaList, stack))
|
||||
return true
|
||||
}
|
||||
color[nodeId] = BLACK
|
||||
if (stack.length > 0 && stack[stack.length - 1] === nodeId)
|
||||
stack.pop()
|
||||
return false
|
||||
}
|
||||
|
||||
const getCycleEdges = (nodes: Node[], edges: Edge[]) => {
|
||||
const adjaList: Record<string, string[]> = {}
|
||||
const color: Record<string, string> = {}
|
||||
const stack: string[] = []
|
||||
|
||||
for (const node of nodes) {
|
||||
color[node.id] = WHITE
|
||||
adjaList[node.id] = []
|
||||
}
|
||||
|
||||
for (const edge of edges)
|
||||
adjaList[edge.source].push(edge.target)
|
||||
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
if (color[nodes[i].id] === WHITE)
|
||||
isCyclicUtil(nodes[i].id, color, adjaList, stack)
|
||||
}
|
||||
|
||||
const cycleEdges = []
|
||||
if (stack.length > 0) {
|
||||
const cycleNodes = new Set(stack)
|
||||
for (const edge of edges) {
|
||||
if (cycleNodes.has(edge.source) && cycleNodes.has(edge.target))
|
||||
cycleEdges.push(edge)
|
||||
}
|
||||
}
|
||||
|
||||
return cycleEdges
|
||||
}
|
||||
|
||||
export const initialNodes = (nodes: Node[], edges: Edge[]) => {
|
||||
const firstNode = nodes[0]
|
||||
|
||||
@@ -35,6 +89,7 @@ export const initialNodes = (nodes: Node[], edges: Edge[]) => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return nodes.map((node) => {
|
||||
node.type = 'custom'
|
||||
|
||||
@@ -75,7 +130,11 @@ export const initialEdges = (edges: Edge[], nodes: Node[]) => {
|
||||
|
||||
return acc
|
||||
}, {} as Record<string, Node>)
|
||||
return edges.map((edge) => {
|
||||
|
||||
const cycleEdges = getCycleEdges(nodes, edges)
|
||||
return edges.filter((edge) => {
|
||||
return !cycleEdges.find(cycEdge => cycEdge.source === edge.source && cycEdge.target === edge.target)
|
||||
}).map((edge) => {
|
||||
edge.type = 'custom'
|
||||
|
||||
if (!edge.sourceHandle)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* eslint-disable import/no-mutable-exports */
|
||||
import { InputVarType } from '@/app/components/workflow/types'
|
||||
import { AgentStrategy } from '@/types/app'
|
||||
import { PromptRole } from '@/models/debug'
|
||||
|
||||
export let apiPrefix = ''
|
||||
export let publicApiPrefix = ''
|
||||
@@ -70,7 +71,12 @@ export const TONE_LIST = [
|
||||
]
|
||||
|
||||
export const DEFAULT_CHAT_PROMPT_CONFIG = {
|
||||
prompt: [],
|
||||
prompt: [
|
||||
{
|
||||
role: PromptRole.system,
|
||||
text: '',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const DEFAULT_COMPLETION_PROMPT_CONFIG = {
|
||||
|
||||
@@ -9,6 +9,7 @@ const translation = {
|
||||
play: 'Play',
|
||||
pause: 'Pause',
|
||||
playing: 'Playing',
|
||||
loading: 'Loading',
|
||||
merMaind: {
|
||||
rerender: 'Redo Rerender',
|
||||
},
|
||||
|
||||
@@ -266,18 +266,32 @@ const translation = {
|
||||
queryNoBeEmpty: 'La requête doit être définie dans le prompt',
|
||||
},
|
||||
variableConig: {
|
||||
modalTitle: 'Paramètres du champ',
|
||||
description: 'Paramètre pour la variable {{varName}}',
|
||||
fieldType: 'Type de champ',
|
||||
string: 'Texte Court',
|
||||
paragraph: 'Paragraphe',
|
||||
select: 'Sélectionner',
|
||||
notSet: 'Non défini, essayez de taper {{input}} dans l\'invite de préfixe',
|
||||
stringTitle: 'Options de la boîte de texte du formulaire',
|
||||
maxLength: 'Longueur maximale',
|
||||
options: 'Options',
|
||||
addOption: 'Ajouter une option',
|
||||
apiBasedVar: 'Variable basée sur l\'API',
|
||||
'addModalTitle': 'Add Input Field',
|
||||
'editModalTitle': 'Edit Input Field',
|
||||
'description': 'Setting for variable {{varName}}',
|
||||
'fieldType': 'Field type',
|
||||
'string': 'Short Text',
|
||||
'text-input': 'Short Text',
|
||||
'paragraph': 'Paragraph',
|
||||
'select': 'Select',
|
||||
'number': 'Number',
|
||||
'notSet': 'Not set, try typing {{input}} in the prefix prompt',
|
||||
'stringTitle': 'Form text box options',
|
||||
'maxLength': 'Max length',
|
||||
'options': 'Options',
|
||||
'addOption': 'Add option',
|
||||
'apiBasedVar': 'API-based Variable',
|
||||
'varName': 'Variable Name',
|
||||
'labelName': 'Label Name',
|
||||
'inputPlaceholder': 'Please input',
|
||||
'required': 'Required',
|
||||
'errorMsg': {
|
||||
varNameRequired: 'Variable name is required',
|
||||
labelNameRequired: 'Label name is required',
|
||||
varNameCanBeRepeat: 'Variable name can not be repeated',
|
||||
atLeastOneOption: 'At least one option is required',
|
||||
optionRepeat: 'Has repeat options',
|
||||
},
|
||||
},
|
||||
vision: {
|
||||
name: 'Vision',
|
||||
@@ -348,6 +362,7 @@ const translation = {
|
||||
result: 'Texte de sortie',
|
||||
datasetConfig: {
|
||||
settingTitle: 'Paramètres de récupération',
|
||||
knowledgeTip: 'Cliquez sur le bouton “+” pour ajouter des connaissances',
|
||||
retrieveOneWay: {
|
||||
title: 'Récupération N-vers-1',
|
||||
description: 'En fonction de l\'intention de l\'utilisateur et des descriptions de Connaissance, l\'Agent sélectionne de manière autonome la meilleure Connaissance pour interroger. Idéal pour les applications avec une Connaissance distincte et limitée.',
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
const translation = {
|
||||
title: 'Journaux',
|
||||
description: 'Les journaux enregistrent l\'état de fonctionnement de l\'application, y compris les entrées de l\'utilisateur et les réponses de l\'IA.',
|
||||
dateTimeFormat: 'JJ/MM/AAAA hh:mm A',
|
||||
description: 'Les journaux enregistrent l\'état d\'exécution de l\'application, y compris les entrées utilisateur et les réponses de l\'IA.',
|
||||
dateTimeFormat: 'MM/DD/YYYY hh:mm A',
|
||||
table: {
|
||||
header: {
|
||||
time: 'Temps',
|
||||
time: 'Heure',
|
||||
endUser: 'Utilisateur final',
|
||||
input: 'Entrée',
|
||||
output: 'Sortie',
|
||||
summary: 'Titre',
|
||||
messageCount: 'Nombre de Messages',
|
||||
userRate: 'Taux d\'utilisateur',
|
||||
adminRate: 'Taux Op.',
|
||||
messageCount: 'Nombre de messages',
|
||||
userRate: 'Taux utilisateur',
|
||||
adminRate: 'Taux op.',
|
||||
startTime: 'HEURE DE DÉBUT',
|
||||
status: 'STATUT',
|
||||
runtime: 'TEMPS D\'EXÉCUTION',
|
||||
tokens: 'JETONS',
|
||||
user: 'UTILISATEUR FINAL',
|
||||
version: 'VERSION',
|
||||
},
|
||||
pagination: {
|
||||
previous: 'Précédent',
|
||||
@@ -21,49 +27,57 @@ const translation = {
|
||||
noChat: 'Aucune conversation pour le moment',
|
||||
noOutput: 'Aucune sortie',
|
||||
element: {
|
||||
title: 'Quelqu\'un est là ?',
|
||||
content: 'Observez et annotez les interactions entre les utilisateurs finaux et les applications IA ici pour améliorer continuellement la précision de l\'IA. Vous pouvez essayer de <shareLink>partager</shareLink> ou de <testLink>tester</testLink> l\'application Web',
|
||||
title: 'Y a-t-il quelqu\'un ?',
|
||||
content: 'Observez et annotez ici les interactions entre les utilisateurs finaux et les applications d\'IA pour améliorer en continu la précision de l\'IA. Vous pouvez essayer de <shareLink>partager</shareLink> ou de <testLink>tester</testLink> l\'application Web vous-même, puis revenir sur cette page.',
|
||||
},
|
||||
},
|
||||
},
|
||||
detail: {
|
||||
time: 'Temps',
|
||||
time: 'Heure',
|
||||
conversationId: 'ID de conversation',
|
||||
promptTemplate: 'Modèle de Prompt',
|
||||
promptTemplateBeforeChat: 'Modèle de Prompt Avant le Chat · En Tant que Message Système',
|
||||
annotationTip: 'Améliorations Marquées par {{user}}',
|
||||
timeConsuming: 'Apologies, but you haven\'t provided any text to translate. Could you please provide the text so I can help you with the translation?',
|
||||
second: '"s"',
|
||||
promptTemplate: 'Modèle de consigne',
|
||||
promptTemplateBeforeChat: 'Modèle de consigne avant la conversation · En tant que message système',
|
||||
annotationTip: 'Améliorations marquées par {{user}}',
|
||||
timeConsuming: '',
|
||||
second: 's',
|
||||
tokenCost: 'Jeton dépensé',
|
||||
loading: 'chargement',
|
||||
operation: {
|
||||
like: 'comme',
|
||||
dislike: 'déteste',
|
||||
like: 'j\'aime',
|
||||
dislike: 'je n\'aime pas',
|
||||
addAnnotation: 'Ajouter une amélioration',
|
||||
editAnnotation: 'Amélioration de l\'édition',
|
||||
annotationPlaceholder: 'Entrez la réponse attendue que vous souhaitez que l\'IA donne, qui peut être utilisée pour l\'ajustement fin du modèle et l\'amélioration continue de la qualité de génération de texte à l\'avenir.',
|
||||
editAnnotation: 'Modifier une amélioration',
|
||||
annotationPlaceholder: 'Entrez la réponse attendue que vous souhaitez que l\'IA donne, cela peut être utilisé pour le réglage fin du modèle et l\'amélioration continue de la qualité de génération de texte à l\'avenir.',
|
||||
},
|
||||
variables: 'Variables',
|
||||
uploadImages: 'Images Téléchargées',
|
||||
uploadImages: 'Images téléchargées',
|
||||
},
|
||||
filter: {
|
||||
period: {
|
||||
today: 'Aujourd\'hui',
|
||||
last7days: 'Les 7 Derniers Jours',
|
||||
last4weeks: 'Les 4 dernières semaines',
|
||||
last3months: 'Les 3 derniers mois',
|
||||
last12months: 'Les 12 derniers mois',
|
||||
last7days: '7 derniers jours',
|
||||
last4weeks: '4 dernières semaines',
|
||||
last3months: '3 derniers mois',
|
||||
last12months: '12 derniers mois',
|
||||
monthToDate: 'Mois à ce jour',
|
||||
quarterToDate: 'Trimestre à ce jour',
|
||||
yearToDate: 'Année à ce jour',
|
||||
allTime: 'Tout le temps',
|
||||
},
|
||||
annotation: {
|
||||
all: 'Tout',
|
||||
all: 'Tous',
|
||||
annotated: 'Améliorations annotées ({{count}} éléments)',
|
||||
not_annotated: 'Non Annoté',
|
||||
not_annotated: 'Non annoté',
|
||||
},
|
||||
},
|
||||
workflowTitle: 'Journaux de workflow',
|
||||
workflowSubtitle: 'Le journal enregistre l\'opération d\'Automate.',
|
||||
runDetail: {
|
||||
title: 'Journal de conversation',
|
||||
workflowTitle: 'Détail du journal',
|
||||
},
|
||||
promptLog: 'Journal de consigne',
|
||||
viewLog: 'Voir le journal',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
const translation = {
|
||||
welcome: {
|
||||
firstStepTip: 'Pour commencer,',
|
||||
enterKeyTip: 'entrez votre clé API OpenAI ci-dessous',
|
||||
enterKeyTip: 'saisissez votre clé API OpenAI ci-dessous',
|
||||
getKeyTip: 'Obtenez votre clé API depuis le tableau de bord OpenAI',
|
||||
placeholder: 'Votre clé API OpenAI (par exemple, sk-xxxx)',
|
||||
placeholder: 'Votre clé API OpenAI (ex. sk-xxxx)',
|
||||
},
|
||||
apiKeyInfo: {
|
||||
cloud: {
|
||||
trial: {
|
||||
title: 'Vous utilisez le quota d\'essai de {{providerName}}.',
|
||||
description: 'Le quota d\'essai est fourni pour votre utilisation de test. Avant que les appels de quota d\'essai ne soient épuisés, veuillez configurer votre propre fournisseur de modèle ou acheter un quota supplémentaire.',
|
||||
description: 'Le quota d\'essai est fourni pour votre usage de test. Avant l\'épuisement des appels de quota d\'essai, veuillez configurer votre propre fournisseur de modèle ou acheter un quota supplémentaire.',
|
||||
},
|
||||
exhausted: {
|
||||
title: 'Votre quota d\'essai a été utilisé, veuillez configurer votre APIKey.',
|
||||
title: 'Votre quota d\'essai a été utilisé, veuillez configurer votre clé API.',
|
||||
description: 'Votre quota d\'essai a été épuisé. Veuillez configurer votre propre fournisseur de modèle ou acheter un quota supplémentaire.',
|
||||
},
|
||||
},
|
||||
@@ -22,78 +22,78 @@ const translation = {
|
||||
row2: 'configurez d\'abord votre fournisseur de modèle.',
|
||||
},
|
||||
},
|
||||
callTimes: 'Heures d\'appel',
|
||||
usedToken: 'Token utilisé',
|
||||
setAPIBtn: 'Allez configurer le fournisseur de modèle',
|
||||
callTimes: 'Appels',
|
||||
usedToken: 'Token utilisés',
|
||||
setAPIBtn: 'Aller à la configuration du fournisseur de modèle',
|
||||
tryCloud: 'Ou essayez la version cloud de Dify avec un devis gratuit',
|
||||
},
|
||||
overview: {
|
||||
title: 'Aperçu',
|
||||
appInfo: {
|
||||
explanation: 'WebApp IA prête à l\'emploi',
|
||||
explanation: 'WebApp AI prête à l\'emploi',
|
||||
accessibleAddress: 'URL publique',
|
||||
preview: 'Aperçu',
|
||||
regenerate: 'Régénérer',
|
||||
regenerate: 'Regénérer',
|
||||
preUseReminder: 'Veuillez activer WebApp avant de continuer.',
|
||||
settings: {
|
||||
entry: 'Paramètres',
|
||||
title: 'Paramètres de l\'application Web',
|
||||
webName: 'Nom de l\'application Web',
|
||||
webDesc: 'Description de l\'application web',
|
||||
webDescTip: 'Ce texte sera affiché du côté du client, fournissant des indications de base sur comment utiliser l\'application',
|
||||
webDescPlaceholder: 'Entrez la description de la WebApp',
|
||||
webDesc: 'Description de l\'application Web',
|
||||
webDescTip: 'Ce texte sera affiché côté client, fournissant des directives de base sur la façon d\'utiliser l\'application',
|
||||
webDescPlaceholder: 'Entrez la description de l\'application Web',
|
||||
language: 'Langue',
|
||||
more: {
|
||||
entry: 'Montrer plus de paramètres',
|
||||
copyright: 'Droit d\'auteur',
|
||||
entry: 'Afficher plus de paramètres',
|
||||
copyright: 'Droits d\'auteur',
|
||||
copyRightPlaceholder: 'Entrez le nom de l\'auteur ou de l\'organisation',
|
||||
privacyPolicy: 'Politique de Confidentialité',
|
||||
privacyPolicy: 'Politique de confidentialité',
|
||||
privacyPolicyPlaceholder: 'Entrez le lien de la politique de confidentialité',
|
||||
privacyPolicyTip: 'Aide les visiteurs à comprendre les données que l\'application collecte, voir la <privacyPolicyLink>Politique de Confidentialité</privacyPolicyLink> de Dify.',
|
||||
privacyPolicyTip: 'Aide les visiteurs à comprendre les données collectées par l\'application, voir la <privacyPolicyLink>Politique de confidentialité</privacyPolicyLink> de Dify.',
|
||||
},
|
||||
},
|
||||
embedded: {
|
||||
entry: 'Intégré',
|
||||
title: 'Intégrer sur le site web',
|
||||
explanation: 'Choisissez la manière d\'intégrer l\'application de chat à votre site web',
|
||||
iframe: 'Pour ajouter l\'application de chat n\'importe où sur votre site web, ajoutez cette iframe à votre code html.',
|
||||
scripts: 'Pour ajouter une application de chat en bas à droite de votre site web, ajoutez ce code à votre html.',
|
||||
chromePlugin: 'Installez l\'extension Chrome Dify Chatbot',
|
||||
title: 'Intégrer sur un site Web',
|
||||
explanation: 'Choisissez la manière d\'intégrer l\'application de chat à votre site Web',
|
||||
iframe: 'Pour ajouter l\'application de chat n\'importe où sur votre site Web, ajoutez cette iframe à votre code HTML.',
|
||||
scripts: 'Pour ajouter une application de chat en bas à droite de votre site Web, ajoutez ce code à votre HTML.',
|
||||
chromePlugin: 'Installer l\'extension Chrome Dify Chatbot',
|
||||
copied: 'Copié',
|
||||
copy: 'Copier',
|
||||
},
|
||||
qrcode: {
|
||||
title: 'QR code à partager',
|
||||
scan: 'Application de Partage de Scan',
|
||||
download: 'Télécharger le Code QR',
|
||||
scan: 'Scanner et partager l\'application',
|
||||
download: 'Télécharger le code QR',
|
||||
},
|
||||
customize: {
|
||||
way: 'manière',
|
||||
way: 'façon',
|
||||
entry: 'Personnaliser',
|
||||
title: 'Personnaliser l\'WebApp IA',
|
||||
explanation: 'Vous pouvez personnaliser l\'interface utilisateur de l\'application Web pour répondre à vos besoins en termes de scénario et de style.',
|
||||
title: 'Personnaliser l\'application Web AI',
|
||||
explanation: 'Vous pouvez personnaliser l\'interface utilisateur de l\'application Web pour répondre à vos besoins de scénario et de style.',
|
||||
way1: {
|
||||
name: 'Faites une fourchette du code client, modifiez-le et déployez-le sur Vercel (recommandé)',
|
||||
step1: 'Faites une fourchette du code client et modifiez-le',
|
||||
step1Tip: 'Cliquez ici pour bifurquer le code source dans votre compte GitHub et modifier le code',
|
||||
step1Operation: 'Dify-WebClient',
|
||||
name: 'Faire une copie du code client, le modifier et le déployer sur Vercel (recommandé)',
|
||||
step1: 'Faire une copie du code client et le modifier',
|
||||
step1Tip: 'Cliquez ici pour faire une copie du code source dans votre compte GitHub et le modifier',
|
||||
step1Operation: 'Client-Web-Dify',
|
||||
step2: 'Déployer sur Vercel',
|
||||
step2Tip: 'Cliquez ici pour importer le dépôt dans Vercel et déployer',
|
||||
step2Tip: 'Cliquez ici pour importer le dépôt dans Vercel et le déployer',
|
||||
step2Operation: 'Importer le dépôt',
|
||||
step3: 'Configurer les variables d\'environnement',
|
||||
step3Tip: 'Ajoutez les variables d\'environnement suivantes dans Vercel',
|
||||
},
|
||||
way2: {
|
||||
name: 'Écrivez du code côté client pour appeler l\'API et déployez-le sur un serveur',
|
||||
name: 'Écrire du code côté client pour appeler l\'API et le déployer sur un serveur',
|
||||
operation: 'Documentation',
|
||||
},
|
||||
},
|
||||
},
|
||||
apiInfo: {
|
||||
title: 'API du service Backend',
|
||||
title: 'API de service Backend',
|
||||
explanation: 'Facilement intégré dans votre application',
|
||||
accessibleAddress: 'Point de terminaison du service API',
|
||||
doc: 'Référence API',
|
||||
doc: 'Référence de l\'API',
|
||||
},
|
||||
status: {
|
||||
running: 'En service',
|
||||
@@ -103,35 +103,39 @@ const translation = {
|
||||
analysis: {
|
||||
title: 'Analyse',
|
||||
ms: 'ms',
|
||||
tokenPS: 'Jeton/s',
|
||||
tokenPS: 'Token/s',
|
||||
totalMessages: {
|
||||
title: 'Messages Totaux',
|
||||
explanation: 'Nombre quotidien d\'interactions IA ; ingénierie/debuggage de prompt exclu.',
|
||||
title: 'Total des messages',
|
||||
explanation: 'Nombre d\'interactions quotidiennes avec l\'IA ; l\'ingénierie/le débogage des prompts sont exclus.',
|
||||
},
|
||||
activeUsers: {
|
||||
title: 'Utilisateurs Actifs',
|
||||
explanation: 'Utilisateurs uniques participant à des Q&A avec l\'IA ; l\'ingénierie/débogage de prompt exclu.',
|
||||
title: 'Utilisateurs actifs',
|
||||
explanation: 'Utilisateurs uniques engagés dans des Q&R avec l\'IA ; l\'ingénierie/le débogage des prompts sont exclus.',
|
||||
},
|
||||
tokenUsage: {
|
||||
title: 'Utilisation de Token',
|
||||
explanation: 'Reflet de l\'utilisation quotidienne des jetons du modèle de langage pour l\'application, utile à des fins de contrôle des coûts.',
|
||||
title: 'Utilisation des tokens',
|
||||
explanation: 'Reflet de l\'utilisation quotidienne des tokens du modèle de langue pour l\'application, utile pour le contrôle des coûts.',
|
||||
consumed: 'Consommé',
|
||||
},
|
||||
avgSessionInteractions: {
|
||||
title: 'Interactions Moyennes par Session',
|
||||
explanation: 'Comptage continu de la communication utilisateur-IA ; pour les applications basées sur la conversation.',
|
||||
title: 'Interactions moyennes par session',
|
||||
explanation: 'Nombre de communications continu utilisateur-IA ; pour les applications basées sur la conversation.',
|
||||
},
|
||||
avgUserInteractions: {
|
||||
title: 'Interactions moyennes par utilisateur',
|
||||
explanation: 'Reflet de la fréquence d\'utilisation quotidienne des utilisateurs. Cette métrique reflète la fidélité des utilisateurs.',
|
||||
},
|
||||
userSatisfactionRate: {
|
||||
title: 'Taux de Satisfaction de l\'Utilisateur',
|
||||
explanation: 'Le nombre de "j\'aime" par 1 000 messages. Cela indique la proportion de réponses dont les utilisateurs sont très satisfaits.',
|
||||
title: 'Taux de satisfaction des utilisateurs',
|
||||
explanation: 'Le nombre de likes parmi 1 000 messages. Cela indique la proportion de réponses avec lesquelles les utilisateurs sont très satisfaits.',
|
||||
},
|
||||
avgResponseTime: {
|
||||
title: 'Temps de réponse moyen',
|
||||
explanation: 'Temps (ms) pour que l\'IA traite/réponde; pour les applications basées sur le texte.',
|
||||
explanation: 'Temps (ms) pour l\'IA pour traiter/répondre ; pour les applications basées sur du texte.',
|
||||
},
|
||||
tps: {
|
||||
title: 'Vitesse de Sortie des Tokens',
|
||||
explanation: 'Mesurez la performance du LLM. Comptez la vitesse de sortie des Tokens du LLM depuis le début de la demande jusqu\'à l\'achèvement de la sortie.',
|
||||
title: 'Vitesse de sortie de token',
|
||||
explanation: 'Mesurer les performances du LLM. Compter la vitesse de sortie des tokens du LLM depuis le début de la requête jusqu\'à l\'achèvement de la sortie.',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,38 +1,59 @@
|
||||
const translation = {
|
||||
createApp: 'Créer une nouvelle application',
|
||||
createApp: 'CRÉER UNE APPLICATION',
|
||||
types: {
|
||||
all: 'Tout',
|
||||
assistant: 'Assistant',
|
||||
completion: 'Complétion',
|
||||
chatbot: 'Chatbot',
|
||||
agent: 'Agent',
|
||||
workflow: 'Flux de travail',
|
||||
completion: 'Terminaison',
|
||||
},
|
||||
modes: {
|
||||
completion: 'Générateur de Texte',
|
||||
chat: 'Assistant de Base',
|
||||
},
|
||||
createFromConfigFile: 'Créer une application à partir du fichier de configuration',
|
||||
duplicate: 'Dupliquer',
|
||||
duplicateTitle: 'Dupliquer l\'application',
|
||||
export: 'Exporter DSL',
|
||||
exportFailed: 'Échec de l\'exportation du DSL.',
|
||||
importDSL: 'Importer le fichier DSL',
|
||||
createFromConfigFile: 'Créer à partir du fichier DSL',
|
||||
deleteAppConfirmTitle: 'Supprimer cette application ?',
|
||||
deleteAppConfirmContent:
|
||||
'La suppression de l\'application est irréversible. Les utilisateurs ne pourront plus accéder à votre application, et toutes les configurations de prompt et les journaux seront définitivement supprimés.',
|
||||
'La suppression de l\'application est irréversible. Les utilisateurs ne pourront plus accéder à votre application et toutes les configurations de prompt et les journaux seront définitivement supprimés.',
|
||||
appDeleted: 'Application supprimée',
|
||||
appDeleteFailed: 'Échec de la suppression de l\'application',
|
||||
join: 'Rejoignez la communauté',
|
||||
join: 'Rejoindre la communauté',
|
||||
communityIntro:
|
||||
'Discutez avec les membres de l\'équipe, les contributeurs et les développeurs sur différents canaux.',
|
||||
roadmap: 'Voir notre feuille de route',
|
||||
appNamePlaceholder: 'Veuillez entrer le nom de l\'application',
|
||||
newApp: {
|
||||
startToCreate: 'Commençons avec votre nouvelle application',
|
||||
startFromBlank: 'Créer à partir de zéro',
|
||||
startFromTemplate: 'Créer à partir d\'un modèle',
|
||||
captionAppType: 'Quel type d\'application souhaitez-vous créer ?',
|
||||
chatbotDescription: 'Construisez une application basée sur le chat. Cette application utilise un format question-réponse, permettant ainsi plusieurs tours de conversation continue.',
|
||||
completionDescription: 'Construisez une application qui génère du texte de haute qualité en fonction des invites, telles que la génération d\'articles, de résumés, de traductions, et plus encore.',
|
||||
completionWarning: 'Ce type d\'application ne sera plus pris en charge.',
|
||||
agentDescription: 'Construisez un agent intelligent capable de choisir automatiquement les outils pour accomplir les tâches',
|
||||
workflowDescription: 'Construisez une application qui génère du texte de haute qualité en fonction d\'un flux de travail avec un haut degré de personnalisation. Il convient aux utilisateurs expérimentés.',
|
||||
workflowWarning: 'Actuellement en version bêta',
|
||||
chatbotType: 'Méthode d\'orchestration du chatbot',
|
||||
basic: 'Basique',
|
||||
basicTip: 'Pour les débutants, peut passer à Chatflow plus tard',
|
||||
basicFor: 'POUR LES DÉBUTANTS',
|
||||
basicDescription: 'L\'orchestration de base permet d\'orchestrer une application Chatbot à l\'aide de paramètres simples, sans possibilité de modifier les invites intégrées. Il convient aux débutants.',
|
||||
advanced: 'Chatflow',
|
||||
advancedFor: 'Pour les utilisateurs avancés',
|
||||
advancedDescription: 'L\'orchestration de flux de travail orchestre les Chatbots sous forme de workflows, offrant un haut degré de personnalisation, y compris la possibilité de modifier les invites intégrées. Il convient aux utilisateurs expérimentés.',
|
||||
captionName: 'Icône et nom de l\'application',
|
||||
captionAppType: 'Quel type d\'application voulez-vous créer ?',
|
||||
appNamePlaceholder: 'Donnez un nom à votre application',
|
||||
captionDescription: 'Description',
|
||||
appDescriptionPlaceholder: 'Entrez la description de l\'application',
|
||||
useTemplate: 'Utiliser ce modèle',
|
||||
previewDemo: 'Aperçu de la démo',
|
||||
chatApp: 'Assistant',
|
||||
chatAppIntro:
|
||||
'Je veux construire une application basée sur le chat. Cette application utilise un format de questions-réponses, permettant plusieurs tours de conversation continue.',
|
||||
agentAssistant: 'Nouvel Assistant Agent',
|
||||
completeApp: 'Générateur de Texte',
|
||||
'Je veux construire une application basée sur le chat. Cette application utilise un format question-réponse, permettant plusieurs tours de conversation continue.',
|
||||
agentAssistant: 'Nouvel assistant agent',
|
||||
completeApp: 'Générateur de texte',
|
||||
completeAppIntro:
|
||||
'Je veux créer une application qui génère du texte de haute qualité basé sur des prompts, tels que la génération d\'articles, de résumés, de traductions, et plus encore.',
|
||||
showTemplates: 'Je veux choisir à partir d\'un modèle',
|
||||
'Je veux créer une application qui génère du texte de haute qualité en fonction des invites, telles que la génération d\'articles, de résumés, de traductions, et plus encore.',
|
||||
showTemplates: 'Je veux choisir parmi un modèle',
|
||||
hideTemplates: 'Revenir à la sélection de mode',
|
||||
Create: 'Créer',
|
||||
Cancel: 'Annuler',
|
||||
@@ -42,13 +63,28 @@ const translation = {
|
||||
appCreated: 'Application créée',
|
||||
appCreateFailed: 'Échec de la création de l\'application',
|
||||
},
|
||||
editApp: {
|
||||
startToEdit: 'Modifier l\'application',
|
||||
},
|
||||
editApp: 'Modifier les informations',
|
||||
editAppTitle: 'Modifier les informations de l\'application',
|
||||
editDone: 'Informations sur l\'application mises à jour',
|
||||
editFailed: 'Échec de la mise à jour des informations de l\'application',
|
||||
emoji: {
|
||||
ok: 'D\'accord',
|
||||
ok: 'OK',
|
||||
cancel: 'Annuler',
|
||||
},
|
||||
switch: 'Passer à l\'orchestration de flux de travail',
|
||||
switchTipStart: 'Une nouvelle copie de l\'application sera créée pour vous, et la nouvelle copie passera à l\'orchestration de flux de travail. La nouvelle copie ne permettra pas le ',
|
||||
switchTip: 'retour',
|
||||
switchTipEnd: ' à l\'orchestration de base.',
|
||||
switchLabel: 'La copie de l\'application à créer',
|
||||
removeOriginal: 'Supprimer l\'application d\'origine',
|
||||
switchStart: 'Commencer la commutation',
|
||||
typeSelector: {
|
||||
all: 'Tous Types',
|
||||
chatbot: 'Chatbot',
|
||||
agent: 'Agent',
|
||||
workflow: 'Flux de travail',
|
||||
completion: 'Terminaison',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
||||
@@ -400,6 +400,7 @@ const translation = {
|
||||
promptEng: 'Orchestrer',
|
||||
apiAccess: 'Accès API',
|
||||
logAndAnn: 'Journaux & Annonces.',
|
||||
logs: 'Journaux',
|
||||
},
|
||||
environment: {
|
||||
testing: 'TESTER',
|
||||
@@ -477,6 +478,10 @@ const translation = {
|
||||
title: 'Variables & Outils Externes',
|
||||
desc: 'Insérer des Variables & Outils Externes',
|
||||
},
|
||||
outputToolDisabledItem: {
|
||||
title: 'Variables',
|
||||
desc: 'Insérer Variables',
|
||||
},
|
||||
modal: {
|
||||
add: 'Nouvelle variable',
|
||||
addTool: 'Nouvel outil',
|
||||
|
||||
@@ -18,7 +18,7 @@ const translation = {
|
||||
apps: {
|
||||
title: 'Explorez les applications par Dify',
|
||||
description: 'Utilisez ces applications modèles instantanément ou personnalisez vos propres applications basées sur les modèles.',
|
||||
allCategories: 'Toutes les catégories',
|
||||
allCategories: 'Recommandé',
|
||||
},
|
||||
appCard: {
|
||||
addToWorkspace: 'Ajouter à l\'espace de travail',
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
const translation = {
|
||||
|
||||
input: 'ENTRÉE',
|
||||
result: 'RÉSULTAT',
|
||||
detail: 'DÉTAIL',
|
||||
tracing: 'TRACE',
|
||||
resultPanel: {
|
||||
status: 'STATUT',
|
||||
time: 'TEMPS ÉCOULÉ',
|
||||
tokens: 'TOTAL DES JETONS',
|
||||
},
|
||||
meta: {
|
||||
title: 'MÉTADONNÉES',
|
||||
status: 'Statut',
|
||||
version: 'Version',
|
||||
executor: 'Exécuteur',
|
||||
startTime: 'Heure de début',
|
||||
time: 'Temps écoulé',
|
||||
tokens: 'Total des jetons',
|
||||
steps: 'Étapes d\'exécution',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
||||
@@ -1,3 +1,333 @@
|
||||
const translation = {}
|
||||
const translation = {
|
||||
common: {
|
||||
editing: 'Édition',
|
||||
autoSaved: 'Enregistré automatiquement',
|
||||
unpublished: 'Non publié',
|
||||
published: 'Publié',
|
||||
publish: 'Publier',
|
||||
update: 'Mettre à jour',
|
||||
run: 'Exécuter',
|
||||
running: 'En cours',
|
||||
inRunMode: 'En mode exécution',
|
||||
inPreview: 'En prévisualisation',
|
||||
inPreviewMode: 'En mode prévisualisation',
|
||||
preview: 'Aperçu',
|
||||
viewRunHistory: 'Voir l\'historique d\'exécution',
|
||||
runHistory: 'Historique d\'exécution',
|
||||
goBackToEdit: 'Retourner à l\'éditeur',
|
||||
conversationLog: 'Journal de conversation',
|
||||
features: 'Fonctionnalités',
|
||||
debugAndPreview: 'Déboguer et prévisualiser',
|
||||
restart: 'Redémarrer',
|
||||
currentDraft: 'Brouillon actuel',
|
||||
currentDraftUnpublished: 'Brouillon actuel non publié',
|
||||
latestPublished: 'Dernière publication',
|
||||
publishedAt: 'Publié',
|
||||
restore: 'Restaurer',
|
||||
runApp: 'Exécuter l\'application',
|
||||
batchRunApp: 'Exécuter l\'application en lot',
|
||||
accessAPIReference: 'Accéder à la référence de l\'API',
|
||||
embedIntoSite: 'Intégrer dans le site',
|
||||
addTitle: 'Ajouter un titre...',
|
||||
addDescription: 'Ajouter une description...',
|
||||
noVar: 'Aucune variable',
|
||||
searchVar: 'Rechercher une variable',
|
||||
variableNamePlaceholder: 'Nom de la variable',
|
||||
setVarValuePlaceholder: 'Définir la variable',
|
||||
needConnecttip: 'Cette étape n\'est connectée à rien',
|
||||
maxTreeDepth: 'Limite maximale de {{depth}} nœuds par branche',
|
||||
needEndNode: 'Le bloc de fin doit être ajouté',
|
||||
needAnswerNode: 'Le bloc de réponse doit être ajouté',
|
||||
workflowProcess: 'Processus de workflow',
|
||||
notRunning: 'Pas encore en cours d\'exécution',
|
||||
previewPlaceholder: 'Saisissez du contenu dans la zone ci-dessous pour commencer le débogage du Chatbot',
|
||||
effectVarConfirm: {
|
||||
title: 'Supprimer la variable',
|
||||
content: 'La variable est utilisée dans d\'autres nœuds. Voulez-vous toujours la supprimer ?',
|
||||
},
|
||||
insertVarTip: 'Appuyez sur la touche \'/\' pour insérer rapidement',
|
||||
},
|
||||
errorMsg: {
|
||||
fieldRequired: '{{field}} est requis',
|
||||
authRequired: 'L\'autorisation est requise',
|
||||
invalidJson: '{{field}} est un JSON invalide',
|
||||
fields: {
|
||||
variable: 'Nom de la variable',
|
||||
variableValue: 'Valeur de la variable',
|
||||
code: 'Code',
|
||||
model: 'Modèle',
|
||||
rerankModel: 'Modèle de retrait',
|
||||
},
|
||||
invalidVariable: 'Variable invalide',
|
||||
},
|
||||
singleRun: {
|
||||
testRun: 'Exécution de test ',
|
||||
startRun: 'Démarrer l\'exécution',
|
||||
running: 'En cours',
|
||||
},
|
||||
tabs: {
|
||||
'searchBlock': 'Rechercher un bloc',
|
||||
'blocks': 'Blocs',
|
||||
'builtInTool': 'Outil intégré',
|
||||
'customTool': 'Outil personnalisé',
|
||||
'question-understand': 'Compréhension des questions',
|
||||
'logic': 'Logique',
|
||||
'transform': 'Transformer',
|
||||
'utilities': 'Utilitaires',
|
||||
'noResult': 'Aucune correspondance trouvée',
|
||||
},
|
||||
blocks: {
|
||||
'start': 'Démarrer',
|
||||
'end': 'Fin',
|
||||
'answer': 'Réponse',
|
||||
'llm': 'LLM',
|
||||
'knowledge-retrieval': 'Récupération de connaissances',
|
||||
'question-classifier': 'Classificateur de questions',
|
||||
'if-else': 'SI/SINON',
|
||||
'code': 'Code',
|
||||
'template-transform': 'Modèle',
|
||||
'http-request': 'Requête HTTP',
|
||||
'variable-assigner': 'Assignateur de variables',
|
||||
},
|
||||
blocksAbout: {
|
||||
'start': 'Définir les paramètres initiaux pour lancer un flux de travail',
|
||||
'end': 'Définir la fin et le type de résultat d\'un flux de travail',
|
||||
'answer': 'Définir le contenu de réponse d\'une conversation',
|
||||
'llm': 'Appeler de grands modèles de langage pour répondre aux questions ou traiter le langage naturel',
|
||||
'knowledge-retrieval': 'Vous permet de interroger le contenu textuel lié aux questions des utilisateurs à partir des connaissances',
|
||||
'question-classifier': 'Définir les conditions de classification des questions des utilisateurs, LLM peut définir comment la conversation progresse en fonction de la description de la classification',
|
||||
'if-else': 'Vous permet de diviser le flux de travail en deux branches en fonction de conditions SI/SINON',
|
||||
'code': 'Exécuter un morceau de code Python ou NodeJS pour implémenter une logique personnalisée',
|
||||
'template-transform': 'Convertir des données en chaîne à l\'aide de la syntaxe du modèle Jinja',
|
||||
'http-request': 'Permet d\'envoyer des requêtes serveur via le protocole HTTP',
|
||||
'variable-assigner': 'Attribuer des variables dans différentes branches à la même variable pour obtenir une configuration unifiée des post-nœuds',
|
||||
},
|
||||
operator: {
|
||||
zoomIn: 'Zoomer',
|
||||
zoomOut: 'Dézoomer',
|
||||
zoomTo50: 'Zoom à 50%',
|
||||
zoomTo100: 'Zoom à 100%',
|
||||
zoomToFit: 'Ajuster à la fenêtre',
|
||||
},
|
||||
panel: {
|
||||
userInputField: 'Champ de saisie utilisateur',
|
||||
changeBlock: 'Changer de bloc',
|
||||
helpLink: 'Lien d\'aide',
|
||||
about: 'À propos',
|
||||
createdBy: 'Créé par ',
|
||||
nextStep: 'Étape suivante',
|
||||
addNextStep: 'Ajouter le prochain bloc dans ce flux de travail',
|
||||
selectNextStep: 'Sélectionner le bloc suivant',
|
||||
runThisStep: 'Exécuter cette étape',
|
||||
checklist: 'Liste de contrôle',
|
||||
checklistTip: 'Assurez-vous que tous les problèmes sont résolus avant de publier',
|
||||
checklistResolved: 'Tous les problèmes sont résolus',
|
||||
organizeBlocks: 'Organiser les blocs',
|
||||
change: 'Changer',
|
||||
},
|
||||
nodes: {
|
||||
common: {
|
||||
outputVars: 'Variables de sortie',
|
||||
insertVarTip: 'Insérer une variable',
|
||||
memory: {
|
||||
memory: 'Mémoire',
|
||||
memoryTip: 'Paramètres de mémoire de chat',
|
||||
windowSize: 'Taille de la fenêtre',
|
||||
conversationRoleName: 'Nom du rôle de conversation',
|
||||
user: 'Préfixe utilisateur',
|
||||
assistant: 'Préfixe assistant',
|
||||
},
|
||||
memories: {
|
||||
title: 'Mémoires',
|
||||
tip: 'Mémoire de chat',
|
||||
builtIn: 'Intégré',
|
||||
},
|
||||
},
|
||||
start: {
|
||||
required: 'requis',
|
||||
inputField: 'Champ d\'entrée',
|
||||
builtInVar: 'Variables intégrées',
|
||||
outputVars: {
|
||||
query: 'Entrée utilisateur',
|
||||
memories: {
|
||||
des: 'Historique de conversation',
|
||||
type: 'type de message',
|
||||
content: 'contenu du message',
|
||||
},
|
||||
files: 'Liste de fichiers',
|
||||
},
|
||||
noVarTip: 'Définissez les entrées pouvant être utilisées dans le flux de travail',
|
||||
},
|
||||
end: {
|
||||
outputs: 'Sorties',
|
||||
output: {
|
||||
type: 'type de sortie',
|
||||
variable: 'variable de sortie',
|
||||
},
|
||||
type: {
|
||||
'none': 'Aucun',
|
||||
'plain-text': 'Texte brut',
|
||||
'structured': 'Structuré',
|
||||
},
|
||||
},
|
||||
answer: {
|
||||
answer: 'Réponse',
|
||||
outputVars: 'Variables de sortie',
|
||||
},
|
||||
llm: {
|
||||
model: 'modèle',
|
||||
variables: 'variables',
|
||||
context: 'contexte',
|
||||
contextTooltip: 'Vous pouvez importer des connaissances comme contexte',
|
||||
notSetContextInPromptTip: 'Pour activer la fonction de contexte, veuillez remplir la variable de contexte dans PROMPT.',
|
||||
prompt: 'invite',
|
||||
roleDescription: {
|
||||
system: 'Donnez des instructions générales pour la conversation',
|
||||
user: 'Fournir des instructions, des requêtes ou toute entrée basée sur du texte au modèle',
|
||||
assistant: 'Les réponses du modèle basées sur les messages de l\'utilisateur',
|
||||
},
|
||||
addMessage: 'Ajouter un message',
|
||||
vision: 'vision',
|
||||
files: 'Fichiers',
|
||||
resolution: {
|
||||
name: 'Résolution',
|
||||
high: 'Élevée',
|
||||
low: 'Faible',
|
||||
},
|
||||
outputVars: {
|
||||
output: 'Générer du contenu',
|
||||
usage: 'Informations sur l\'utilisation du modèle',
|
||||
},
|
||||
singleRun: {
|
||||
variable: 'Variable',
|
||||
},
|
||||
},
|
||||
knowledgeRetrieval: {
|
||||
queryVariable: 'Variable de requête',
|
||||
knowledge: 'Connaissances',
|
||||
outputVars: {
|
||||
output: 'Données segmentées de récupération',
|
||||
content: 'Contenu segmenté',
|
||||
title: 'Titre segmenté',
|
||||
icon: 'Icône segmentée',
|
||||
url: 'URL segmentée',
|
||||
metadata: 'Autres métadonnées',
|
||||
},
|
||||
},
|
||||
http: {
|
||||
inputVars: 'Variables d\'entrée',
|
||||
api: 'API',
|
||||
apiPlaceholder: 'Saisissez l\'URL, tapez ‘/’ pour insérer une variable',
|
||||
notStartWithHttp: 'L\'API doit commencer par http:// ou https://',
|
||||
key: 'Clé',
|
||||
value: 'Valeur',
|
||||
bulkEdit: 'Édition en masse',
|
||||
keyValueEdit: 'Édition clé-valeur',
|
||||
headers: 'En-têtes',
|
||||
params: 'Paramètres',
|
||||
body: 'Corps',
|
||||
outputVars: {
|
||||
body: 'Contenu de la réponse',
|
||||
statusCode: 'Code d\'état de la réponse',
|
||||
headers: 'Liste d\'en-têtes de réponse JSON',
|
||||
files: 'Liste de fichiers',
|
||||
},
|
||||
authorization: {
|
||||
'authorization': 'Autorisation',
|
||||
'authorizationType': 'Type d\'autorisation',
|
||||
'no-auth': 'Aucune',
|
||||
'api-key': 'Clé API',
|
||||
'auth-type': 'Type d\'authentification',
|
||||
'basic': 'De base',
|
||||
'bearer': 'Porteur',
|
||||
'custom': 'Personnalisé',
|
||||
'api-key-title': 'Clé API',
|
||||
'header': 'En-tête',
|
||||
},
|
||||
insertVarPlaceholder: 'tapez \'/\' pour insérer une variable',
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Variables d\'entrée',
|
||||
outputVars: 'Variables de sortie',
|
||||
},
|
||||
templateTransform: {
|
||||
inputVars: 'Variables d\'entrée',
|
||||
code: 'Code',
|
||||
codeSupportTip: 'Ne prend en charge que Jinja2',
|
||||
outputVars: {
|
||||
output: 'Contenu transformé',
|
||||
},
|
||||
},
|
||||
ifElse: {
|
||||
if: 'Si',
|
||||
else: 'Sinon',
|
||||
elseDescription: 'Utilisé pour définir la logique qui doit être exécutée lorsque la condition SI n\'est pas remplie.',
|
||||
and: 'et',
|
||||
or: 'ou',
|
||||
operator: 'Opérateur',
|
||||
notSetVariable: 'Veuillez d\'abord définir la variable',
|
||||
comparisonOperator: {
|
||||
'contains': 'contient',
|
||||
'not contains': 'ne contient pas',
|
||||
'start with': 'commence par',
|
||||
'end with': 'se termine par',
|
||||
'is': 'est',
|
||||
'is not': 'n\'est pas',
|
||||
'empty': 'est vide',
|
||||
'not empty': 'n\'est pas vide',
|
||||
'null': 'est nul',
|
||||
'not null': 'n\'est pas nul',
|
||||
},
|
||||
enterValue: 'Entrer une valeur',
|
||||
addCondition: 'Ajouter une condition',
|
||||
conditionNotSetup: 'Condition NON configurée',
|
||||
},
|
||||
variableAssigner: {
|
||||
title: 'Attribuer des variables',
|
||||
outputType: 'Type de sortie',
|
||||
outputVarType: 'Type de variable de sortie',
|
||||
varNotSet: 'Variable non définie',
|
||||
noVarTip: 'Ajoutez les variables à attribuer',
|
||||
type: {
|
||||
string: 'Chaîne',
|
||||
number: 'Nombre',
|
||||
object: 'Objet',
|
||||
array: 'Tableau',
|
||||
},
|
||||
outputVars: {
|
||||
output: 'Valeur de la variable attribuée',
|
||||
},
|
||||
},
|
||||
tool: {
|
||||
toAuthorize: 'Pour autoriser',
|
||||
inputVars: 'Variables d\'entrée',
|
||||
outputVars: {
|
||||
text: 'contenu généré par l\'outil',
|
||||
files: {
|
||||
title: 'fichiers générés par l\'outil',
|
||||
type: 'Type de support. Actuellement, seul le support de l\'image est pris en charge',
|
||||
transfer_method: 'Méthode de transfert. La valeur est remote_url ou local_file',
|
||||
url: 'URL de l\'image',
|
||||
upload_file_id: 'ID du fichier téléchargé',
|
||||
},
|
||||
},
|
||||
},
|
||||
questionClassifiers: {
|
||||
model: 'modèle',
|
||||
inputVars: 'Variables d\'entrée',
|
||||
class: 'Classe',
|
||||
classNamePlaceholder: 'Écrivez votre nom de classe',
|
||||
advancedSetting: 'Paramètre avancé',
|
||||
topicName: 'Nom du sujet',
|
||||
topicPlaceholder: 'Écrivez votre nom de sujet',
|
||||
addClass: 'Ajouter une classe',
|
||||
instruction: 'Instruction',
|
||||
instructionPlaceholder: 'Écrivez votre instruction',
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
stopBy: 'Arrêté par {{user}}',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
||||
@@ -263,20 +263,33 @@ const translation = {
|
||||
queryNoBeEmpty: 'プロンプトにクエリを設定する必要があります',
|
||||
},
|
||||
variableConig: {
|
||||
modalTitle: 'フィールド設定',
|
||||
description: '変数 {{varName}} の設定',
|
||||
fieldType: 'フィールドタイプ',
|
||||
string: '短いテキスト',
|
||||
paragraph: '段落',
|
||||
select: '選択',
|
||||
notSet: '未設定、接頭辞プロンプトに {{input}} を入力してみてください',
|
||||
stringTitle: 'フォームテキストボックスのオプション',
|
||||
maxLength: '最大長',
|
||||
options: 'オプション',
|
||||
addOption: 'オプションを追加',
|
||||
apiBasedVar: 'APIベースの変数',
|
||||
'addModalTitle': '入力フィールドを追加',
|
||||
'editModalTitle': '入力フィールドを編集',
|
||||
'description': '{{varName}} の変数設定',
|
||||
'fieldType': 'フィールドタイプ',
|
||||
'string': 'ショートテキスト',
|
||||
'text-input': 'ショートテキスト',
|
||||
'paragraph': '段落',
|
||||
'select': '選択',
|
||||
'number': '数値',
|
||||
'notSet': '設定されていません。プレフィックスのプロンプトで {{input}} を入力してみてください。',
|
||||
'stringTitle': 'フォームテキストボックスオプション',
|
||||
'maxLength': '最大長',
|
||||
'options': 'オプション',
|
||||
'addOption': 'オプションを追加',
|
||||
'apiBasedVar': 'APIベースの変数',
|
||||
'varName': '変数名',
|
||||
'labelName': 'ラベル名',
|
||||
'inputPlaceholder': '入力してください',
|
||||
'required': '必須',
|
||||
'errorMsg': {
|
||||
varNameRequired: '変数名は必須です',
|
||||
labelNameRequired: 'ラベル名は必須です',
|
||||
varNameCanBeRepeat: '変数名は繰り返すことができません',
|
||||
atLeastOneOption: '少なくとも1つのオプションが必要です',
|
||||
optionRepeat: '繰り返しオプションがあります',
|
||||
},
|
||||
},
|
||||
|
||||
vision: {
|
||||
name: 'ビジョン',
|
||||
description: 'ビジョンを有効にすると、モデルが画像を受け取り、それに関する質問に答えることができます。',
|
||||
@@ -346,6 +359,7 @@ const translation = {
|
||||
result: '出力テキスト',
|
||||
datasetConfig: {
|
||||
settingTitle: 'リトリーバル設定',
|
||||
knowledgeTip: 'ナレッジを追加するには「+」ボタンをクリックしてください',
|
||||
retrieveOneWay: {
|
||||
title: 'N-to-1 リトリーバル',
|
||||
description: 'ユーザーの意図とナレッジの説明に基づいて、エージェントが自律的に最適なナレッジを選択します。個々の、限られたナレッジを持つアプリケーションに最適です。',
|
||||
|
||||
@@ -10,19 +10,25 @@ const translation = {
|
||||
output: '出力',
|
||||
summary: 'タイトル',
|
||||
messageCount: 'メッセージ数',
|
||||
userRate: 'ユーザー評価',
|
||||
adminRate: 'オペレータ評価',
|
||||
userRate: 'ユーザーレート',
|
||||
adminRate: '操作レート',
|
||||
startTime: '開始時間',
|
||||
status: 'ステータス',
|
||||
runtime: 'ランタイム',
|
||||
tokens: 'トークン',
|
||||
user: 'エンドユーザー',
|
||||
version: 'バージョン',
|
||||
},
|
||||
pagination: {
|
||||
previous: '前へ',
|
||||
next: '次へ',
|
||||
},
|
||||
empty: {
|
||||
noChat: 'まだ会話がありません',
|
||||
noOutput: '出力なし',
|
||||
noChat: 'まだ会話はありません',
|
||||
noOutput: '出力がありません',
|
||||
element: {
|
||||
title: '誰かいますか?',
|
||||
content: 'ここではエンドユーザーとAIアプリケーションの相互作用を観察し、注釈を付けることでAIの精度を継続的に向上させることができます。自分自身でWebアプリを<shareLink>共有</shareLink>したり<testLink>テスト</testLink>したりして、このページに戻ってください。',
|
||||
content: 'ここでは、エンドユーザーとAIアプリケーション間の相互作用を観察し、注釈を付けることで、AIの精度を継続的に向上させます。Webアプリを<shareLink>共有</shareLink>または<testLink>テスト</testLink>してみて、このページに戻ってください。',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -30,18 +36,18 @@ const translation = {
|
||||
time: '時間',
|
||||
conversationId: '会話ID',
|
||||
promptTemplate: 'プロンプトテンプレート',
|
||||
promptTemplateBeforeChat: 'チャット前のプロンプトテンプレート · システムメッセージとして',
|
||||
annotationTip: '{{user}}による改善',
|
||||
promptTemplateBeforeChat: 'チャット前のプロンプトテンプレート・システムメッセージとして',
|
||||
annotationTip: '{{user}} によってマークされた改善',
|
||||
timeConsuming: '',
|
||||
second: '秒',
|
||||
tokenCost: 'トークン消費',
|
||||
loading: '読み込み中',
|
||||
operation: {
|
||||
like: 'いいね',
|
||||
dislike: 'いまいち',
|
||||
dislike: 'いいね解除',
|
||||
addAnnotation: '改善を追加',
|
||||
editAnnotation: '改善を編集',
|
||||
annotationPlaceholder: 'AIが返答することを期待する回答を入力してください。これはモデルの微調整やテキスト生成品質の継続的な改善に使用されます。',
|
||||
annotationPlaceholder: '将来のモデルの微調整やテキスト生成品質の継続的改善のためにAIが返信することを期待する答えを入力してください。',
|
||||
},
|
||||
variables: '変数',
|
||||
uploadImages: 'アップロードされた画像',
|
||||
@@ -53,17 +59,25 @@ const translation = {
|
||||
last4weeks: '過去4週間',
|
||||
last3months: '過去3ヶ月',
|
||||
last12months: '過去12ヶ月',
|
||||
monthToDate: '今月まで',
|
||||
quarterToDate: '四半期まで',
|
||||
yearToDate: '今年まで',
|
||||
monthToDate: '月初から今日まで',
|
||||
quarterToDate: '四半期初から今日まで',
|
||||
yearToDate: '年初から今日まで',
|
||||
allTime: 'すべての期間',
|
||||
},
|
||||
annotation: {
|
||||
all: 'すべて',
|
||||
annotated: '注釈付きの改善({{count}}件)',
|
||||
annotated: '注釈付きの改善 ({{count}} アイテム)',
|
||||
not_annotated: '注釈なし',
|
||||
},
|
||||
},
|
||||
workflowTitle: 'ワークフローログ',
|
||||
workflowSubtitle: 'このログは Automate の操作を記録しました。',
|
||||
runDetail: {
|
||||
title: '会話ログ',
|
||||
workflowTitle: 'ログの詳細',
|
||||
},
|
||||
promptLog: 'プロンプトログ',
|
||||
viewLog: 'ログを表示',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
||||
@@ -1,137 +1,141 @@
|
||||
const translation = {
|
||||
welcome: {
|
||||
firstStepTip: '始めるには、',
|
||||
firstStepTip: 'はじめるには、',
|
||||
enterKeyTip: '以下にOpenAI APIキーを入力してください',
|
||||
getKeyTip: 'OpenAIダッシュボードからAPIキーを取得してください',
|
||||
placeholder: 'OpenAI APIキー(例:sk-xxxx)',
|
||||
placeholder: 'あなたのOpenAI APIキー(例:sk-xxxx)',
|
||||
},
|
||||
apiKeyInfo: {
|
||||
cloud: {
|
||||
trial: {
|
||||
title: '{{providerName}}のトライアルクオータを使用しています。',
|
||||
description: 'トライアルクオータはテスト用に提供されています。トライアルクオータの使用回数が尽きる前に、独自のモデルプロバイダを設定するか、追加のクオータを購入してください。',
|
||||
title: '{{providerName}}トライアルクォータを使用しています。',
|
||||
description: 'トライアルクォータはテスト用に提供されます。トライアルクォータのコールが使い切られる前に、独自のモデルプロバイダを設定するか、追加のクォータを購入してください。',
|
||||
},
|
||||
exhausted: {
|
||||
title: 'トライアルクオータが使い果たされました。APIキーを設定してください。',
|
||||
description: 'トライアルクオータが使い果たされました。独自のモデルプロバイダを設定するか、追加のクオータを購入してください。',
|
||||
title: 'トライアルクォータが使い切れました。APIキーを設定してください。',
|
||||
description: 'トライアルクォータが使い切れました。独自のモデルプロバイダを設定するか、追加のクォータを購入してください。',
|
||||
},
|
||||
},
|
||||
selfHost: {
|
||||
title: {
|
||||
row1: '始めるには、',
|
||||
row1: 'はじめるには、',
|
||||
row2: 'まずモデルプロバイダを設定してください。',
|
||||
},
|
||||
},
|
||||
callTimes: '呼び出し回数',
|
||||
callTimes: 'コール回数',
|
||||
usedToken: '使用済みトークン',
|
||||
setAPIBtn: 'モデルプロバイダの設定へ',
|
||||
tryCloud: 'または無料クオートでDifyのクラウドバージョンを試してみてください',
|
||||
tryCloud: 'またはDifyのクラウドバージョンを無料見積もりでお試しください',
|
||||
},
|
||||
overview: {
|
||||
title: '概要',
|
||||
appInfo: {
|
||||
explanation: '使いやすいAI Webアプリ',
|
||||
explanation: '使いやすいAI WebApp',
|
||||
accessibleAddress: '公開URL',
|
||||
preview: 'プレビュー',
|
||||
regenerate: '再生成',
|
||||
preUseReminder: '続行する前にWebアプリを有効にしてください。',
|
||||
preUseReminder: '続行する前にWebAppを有効にしてください。',
|
||||
settings: {
|
||||
entry: '設定',
|
||||
title: 'Webアプリの設定',
|
||||
webName: 'Webアプリ名',
|
||||
webDesc: 'Webアプリの説明',
|
||||
webDescTip: 'このテキストはクライアント側に表示され、アプリの使用方法に関する基本的なガイダンスを提供します。',
|
||||
webDescPlaceholder: 'Webアプリの説明を入力してください',
|
||||
title: 'WebApp設定',
|
||||
webName: 'WebApp名',
|
||||
webDesc: 'WebAppの説明',
|
||||
webDescTip: 'このテキストはクライアント側に表示され、アプリケーションの使用方法の基本的なガイダンスを提供します。',
|
||||
webDescPlaceholder: 'WebAppの説明を入力してください',
|
||||
language: '言語',
|
||||
more: {
|
||||
entry: '詳細設定を表示',
|
||||
entry: 'その他の設定を表示',
|
||||
copyright: '著作権',
|
||||
copyRightPlaceholder: '著作者または組織の名前を入力してください',
|
||||
copyRightPlaceholder: '著作者または組織名を入力してください',
|
||||
privacyPolicy: 'プライバシーポリシー',
|
||||
privacyPolicyPlaceholder: 'プライバシーポリシーリンクを入力してください',
|
||||
privacyPolicyTip: '訪問者がアプリが収集するデータを理解するのに役立ちます。Difyの<privacyPolicyLink>プライバシーポリシー</privacyPolicyLink>を参照してください。',
|
||||
privacyPolicyTip: '訪問者がアプリケーションが収集するデータを理解し、Difyの<privacyPolicyLink>プライバシーポリシー</privacyPolicyLink>を参照できるようにします。',
|
||||
},
|
||||
},
|
||||
embedded: {
|
||||
entry: '埋め込み',
|
||||
title: 'ウェブサイトに埋め込む',
|
||||
explanation: 'チャットアプリをウェブサイトに埋め込む方法を選択してください。',
|
||||
explanation: 'チャットアプリをウェブサイトに埋め込む方法を選択します。',
|
||||
iframe: 'ウェブサイトの任意の場所にチャットアプリを追加するには、このiframeをHTMLコードに追加してください。',
|
||||
scripts: 'ウェブサイトの右下にチャットアプリを追加するには、このコードをHTMLに追加してください。',
|
||||
chromePlugin: 'Dify Chatbot Chrome拡張機能をインストール',
|
||||
copied: 'コピー済み',
|
||||
copied: 'コピーしました',
|
||||
copy: 'コピー',
|
||||
},
|
||||
qrcode: {
|
||||
title: '共有用QRコード',
|
||||
scan: 'アプリを共有するためにスキャン',
|
||||
scan: 'アプリケーションの共有をスキャン',
|
||||
download: 'QRコードをダウンロード',
|
||||
},
|
||||
customize: {
|
||||
way: '方法',
|
||||
entry: 'カスタマイズ',
|
||||
title: 'AI Webアプリのカスタマイズ',
|
||||
explanation: 'シナリオとスタイルのニーズに合わせてWebアプリのフロントエンドをカスタマイズできます。',
|
||||
title: 'AI WebAppのカスタマイズ',
|
||||
explanation: 'シナリオとスタイルのニーズに合わせてWeb Appのフロントエンドをカスタマイズできます。',
|
||||
way1: {
|
||||
name: 'クライアントコードをフォークして変更し、Vercelにデプロイする(推奨)',
|
||||
step1: 'クライアントコードをフォークして変更する',
|
||||
step1Tip: 'ここをクリックしてソースコードをGitHubアカウントにフォークし、コードを変更してください',
|
||||
name: 'クライアントコードをフォークして修正し、Vercelにデプロイします(推奨)',
|
||||
step1: 'クライアントコードをフォークして修正します',
|
||||
step1Tip: 'ここをクリックしてソースコードをGitHubアカウントにフォークし、コードを修正します',
|
||||
step1Operation: 'Dify-WebClient',
|
||||
step2: 'Vercelにデプロイする',
|
||||
step2Tip: 'ここをクリックしてリポジトリをVercelにインポートし、デプロイしてください',
|
||||
step2Operation: 'リポジトリのインポート',
|
||||
step3: '環境変数を設定する',
|
||||
step3Tip: 'Vercelに以下の環境変数を追加してください',
|
||||
step2: 'Vercelにデプロイします',
|
||||
step2Tip: 'ここをクリックしてリポジトリをVercelにインポートし、デプロイします',
|
||||
step2Operation: 'リポジトリをインポート',
|
||||
step3: '環境変数を設定します',
|
||||
step3Tip: 'Vercelに次の環境変数を追加します',
|
||||
},
|
||||
way2: {
|
||||
name: 'APIを呼び出すためのクライアントサイドコードを記述し、サーバーにデプロイする',
|
||||
name: 'クライアントサイドのコードを記述してAPIを呼び出し、サーバーにデプロイします',
|
||||
operation: 'ドキュメント',
|
||||
},
|
||||
},
|
||||
},
|
||||
apiInfo: {
|
||||
title: 'バックエンドサービスAPI',
|
||||
explanation: 'アプリケーションに簡単に統合できます',
|
||||
explanation: 'あなたのアプリケーションに簡単に統合できます',
|
||||
accessibleAddress: 'サービスAPIエンドポイント',
|
||||
doc: 'APIリファレンス',
|
||||
},
|
||||
status: {
|
||||
running: 'サービス中',
|
||||
disable: '無効化',
|
||||
running: '稼働中',
|
||||
disable: '無効',
|
||||
},
|
||||
},
|
||||
analysis: {
|
||||
title: '分析',
|
||||
ms: 'ミリ秒',
|
||||
ms: 'ms',
|
||||
tokenPS: 'トークン/秒',
|
||||
totalMessages: {
|
||||
title: '総メッセージ数',
|
||||
explanation: 'AIとのやり取りのうち、プロンプトのエンジニアリングやデバッグを除いた日次の相互作用数です。',
|
||||
title: 'トータルメッセージ数',
|
||||
explanation: '日次AIインタラクション数;工学的/デバッグ目的のプロンプトは除外されます。',
|
||||
},
|
||||
activeUsers: {
|
||||
title: 'アクティブユーザー',
|
||||
explanation: 'AIとのQ&Aに参加しているユニークなユーザー数です。プロンプトのエンジニアリングやデバッグを除きます。',
|
||||
title: 'アクティブユーザー数',
|
||||
explanation: 'AIとのQ&Aに参加しているユニークユーザー数;工学的/デバッグ目的のプロンプトは除外されます。',
|
||||
},
|
||||
tokenUsage: {
|
||||
title: 'トークン使用量',
|
||||
explanation: 'アプリケーションの言語モデルの日次トークン使用量を反映し、コスト管理の目的に役立ちます。',
|
||||
consumed: '使用済み',
|
||||
explanation: 'アプリケーションの言語モデルの日次トークン使用量を反映し、コスト管理に役立ちます。',
|
||||
consumed: '消費されたトークン',
|
||||
},
|
||||
avgSessionInteractions: {
|
||||
title: '平均セッション相互作用数',
|
||||
explanation: '会話ベースのアプリケーションの連続したユーザーとAIのコミュニケーション数です。',
|
||||
title: '平均セッションインタラクション数',
|
||||
explanation: 'ユーザーとAIの連続的なコミュニケーション数;対話型アプリケーション向け。',
|
||||
},
|
||||
avgUserInteractions: {
|
||||
title: '平均ユーザーインタラクション数',
|
||||
explanation: 'ユーザーの日次使用頻度を反映します。この指標はユーザーの定着度を反映しています。',
|
||||
},
|
||||
userSatisfactionRate: {
|
||||
title: 'ユーザー満足率',
|
||||
explanation: '1,000メッセージあたりの「いいね」の数です。これは、ユーザーが非常に満足している回答の割合を示します。',
|
||||
title: 'ユーザー満足度率',
|
||||
explanation: '1,000件のメッセージあたりの「いいね」の数。これは、ユーザーが非常に満足している回答の割合を示します。',
|
||||
},
|
||||
avgResponseTime: {
|
||||
title: '平均応答時間',
|
||||
explanation: 'AIの処理/応答にかかる時間(ミリ秒)です。テキストベースのアプリケーションに適しています。',
|
||||
explanation: 'AIが処理/応答する時間(ミリ秒);テキストベースのアプリケーション向け。',
|
||||
},
|
||||
tps: {
|
||||
title: 'トークン出力速度',
|
||||
explanation: 'LLMのパフォーマンスを測定します。リクエストの開始から出力の完了までのLLMのトークン出力速度をカウントします。',
|
||||
explanation: 'LLMのパフォーマンスを測定します。リクエストの開始から出力の完了までのLLMのトークン出力速度を数えます。',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,54 +1,90 @@
|
||||
const translation = {
|
||||
createApp: '新しいアプリを作成する',
|
||||
createApp: 'アプリを作成する',
|
||||
types: {
|
||||
all: 'すべて',
|
||||
assistant: 'アシスタント',
|
||||
completion: '補完',
|
||||
all: '全て',
|
||||
chatbot: 'チャットボット',
|
||||
agent: 'エージェント',
|
||||
workflow: 'ワークフロー',
|
||||
completion: '完了',
|
||||
},
|
||||
modes: {
|
||||
completion: 'テキスト生成',
|
||||
chat: '基本アシスタント',
|
||||
},
|
||||
createFromConfigFile: '設定ファイルからアプリを作成する',
|
||||
duplicate: '複製',
|
||||
duplicateTitle: 'アプリを複製する',
|
||||
export: 'DSL をエクスポート',
|
||||
exportFailed: 'DSL のエクスポートに失敗しました。',
|
||||
importDSL: 'DSL ファイルをインポート',
|
||||
createFromConfigFile: 'DSL ファイルから作成する',
|
||||
deleteAppConfirmTitle: 'このアプリを削除しますか?',
|
||||
deleteAppConfirmContent:
|
||||
'アプリの削除は元に戻せません。ユーザーはアプリにアクセスできなくなり、プロンプトの設定とログは永久に削除されます。',
|
||||
'アプリを削除すると、元に戻すことはできません。ユーザーはもはやあなたのアプリにアクセスできず、すべてのプロンプトの設定とログが永久に削除されます。',
|
||||
appDeleted: 'アプリが削除されました',
|
||||
appDeleteFailed: 'アプリの削除に失敗しました',
|
||||
join: 'コミュニティに参加する',
|
||||
communityIntro:
|
||||
'チームメンバーや貢献者、開発者とさまざまなチャンネルでディスカッションを行います。',
|
||||
'さまざまなチャンネルでチームメンバーや貢献者、開発者と議論します。',
|
||||
roadmap: 'ロードマップを見る',
|
||||
appNamePlaceholder: 'アプリの名前を入力してください',
|
||||
newApp: {
|
||||
startToCreate: '新しいアプリを作成しましょう',
|
||||
captionName: 'アプリアイコンと名前',
|
||||
startFromBlank: 'から作成',
|
||||
startFromTemplate: 'テンプレートから作成',
|
||||
captionAppType: 'どのタイプのアプリを作成しますか?',
|
||||
previewDemo: 'デモをプレビューする',
|
||||
chatbotDescription: 'チャット形式のアプリケーションを構築します。このアプリは質問と回答の形式を使用し、複数のラウンドの継続的な会話を可能にします。',
|
||||
completionDescription: 'プロンプトに基づいて高品質のテキストを生成するアプリケーションを構築します。記事、要約、翻訳などを生成します。',
|
||||
completionWarning: 'この種類のアプリはもうサポートされなくなります。',
|
||||
agentDescription: 'タスクを自動的に完了するためのツールを選択できるインテリジェント エージェントを構築します',
|
||||
workflowDescription: '高度なカスタマイズが可能なワークフローに基づいて高品質のテキストを生成するアプリケーションを構築します。経験豊富なユーザー向けです。',
|
||||
workflowWarning: '現在ベータ版です',
|
||||
chatbotType: 'チャットボットのオーケストレーション方法',
|
||||
basic: '基本',
|
||||
basicTip: '初心者向け。後で Chatflow に切り替えることができます',
|
||||
basicFor: '初心者向け',
|
||||
basicDescription: '基本オーケストレートは、組み込みのプロンプトを変更する機能がなく、簡単な設定を使用してチャットボット アプリをオーケストレートします。初心者向けです。',
|
||||
advanced: 'Chatflow',
|
||||
advancedFor: '上級ユーザー向け',
|
||||
advancedDescription: 'ワークフロー オーケストレートは、ワークフロー形式でチャットボットをオーケストレートし、組み込みのプロンプトを編集する機能を含む高度なカスタマイズを提供します。経験豊富なユーザー向けです。',
|
||||
captionName: 'アプリのアイコンと名前',
|
||||
appNamePlaceholder: 'アプリに名前を付ける',
|
||||
captionDescription: '説明',
|
||||
appDescriptionPlaceholder: 'アプリの説明を入力してください',
|
||||
useTemplate: 'このテンプレートを使用する',
|
||||
previewDemo: 'デモをプレビュー',
|
||||
chatApp: 'アシスタント',
|
||||
chatAppIntro:
|
||||
'チャットベースのアプリケーションを構築したいです。このアプリは質問と回答の形式を使用し、複数のラウンドの連続した会話が可能です。',
|
||||
agentAssistant: '新しいエージェントアシスタント',
|
||||
completeApp: 'テキスト生成',
|
||||
'チャット形式のアプリケーションを構築したい。このアプリは質問と回答の形式を使用し、複数のラウンドの継続的な会話を可能にします。',
|
||||
agentAssistant: '新しいエージェント アシスタント',
|
||||
completeApp: 'テキスト ジェネレーター',
|
||||
completeAppIntro:
|
||||
'プロンプトに基づいて高品質のテキストを生成するアプリケーションを作成したいです。記事、要約、翻訳などの生成が可能です。',
|
||||
showTemplates: 'テンプレートから選択したいです',
|
||||
'プロンプトに基づいて高品質のテキストを生成するアプリケーションを作成したい。記事、要約、翻訳などを生成します。',
|
||||
showTemplates: 'テンプレートから選択したい',
|
||||
hideTemplates: 'モード選択に戻る',
|
||||
Create: '作成',
|
||||
Create: '作成する',
|
||||
Cancel: 'キャンセル',
|
||||
nameNotEmpty: '名前は空にできません',
|
||||
nameNotEmpty: '名前を入力してください',
|
||||
appTemplateNotSelected: 'テンプレートを選択してください',
|
||||
appTypeRequired: 'アプリのタイプを選択してください',
|
||||
appTypeRequired: 'アプリの種類を選択してください',
|
||||
appCreated: 'アプリが作成されました',
|
||||
appCreateFailed: 'アプリの作成に失敗しました',
|
||||
},
|
||||
editApp: {
|
||||
startToEdit: 'アプリを編集する',
|
||||
},
|
||||
editApp: '情報を編集する',
|
||||
editAppTitle: 'アプリ情報を編集する',
|
||||
editDone: 'アプリ情報が更新されました',
|
||||
editFailed: 'アプリ情報の更新に失敗しました',
|
||||
emoji: {
|
||||
ok: 'OK',
|
||||
cancel: 'キャンセル',
|
||||
},
|
||||
switch: 'ワークフロー オーケストレートに切り替える',
|
||||
switchTipStart: '新しいアプリのコピーが作成され、新しいコピーがワークフロー オーケストレートに切り替わります。新しいコピーは ',
|
||||
switchTip: '切り替えを許可しません',
|
||||
switchTipEnd: ' 基本的なオーケストレートに戻ることはできません。',
|
||||
switchLabel: '作成されるアプリのコピー',
|
||||
removeOriginal: '元のアプリを削除する',
|
||||
switchStart: '切り替えを開始する',
|
||||
typeSelector: {
|
||||
all: 'すべてのタイプ',
|
||||
chatbot: 'チャットボット',
|
||||
agent: 'エージェント',
|
||||
workflow: 'ワークフロー',
|
||||
completion: '完了',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
||||
@@ -351,6 +351,7 @@ const translation = {
|
||||
promptEng: 'Orchestrate',
|
||||
apiAccess: 'APIアクセス',
|
||||
logAndAnn: 'ログ&アナウンス',
|
||||
logs: 'ログ',
|
||||
},
|
||||
environment: {
|
||||
testing: 'テスト',
|
||||
@@ -428,6 +429,10 @@ const translation = {
|
||||
title: '変数&外部ツール',
|
||||
desc: '変数&外部ツールを挿入',
|
||||
},
|
||||
outputToolDisabledItem: {
|
||||
title: '変数',
|
||||
desc: '変数を挿入',
|
||||
},
|
||||
modal: {
|
||||
add: '新しい変数',
|
||||
addTool: '新しいツール',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const translation = {
|
||||
title: '探索する',
|
||||
title: '探索',
|
||||
sidebar: {
|
||||
discovery: '探索',
|
||||
chat: 'チャット',
|
||||
@@ -18,7 +18,7 @@ const translation = {
|
||||
apps: {
|
||||
title: 'Difyによるアプリの探索',
|
||||
description: 'これらのテンプレートアプリを即座に使用するか、テンプレートに基づいて独自のアプリをカスタマイズしてください。',
|
||||
allCategories: 'すべてのカテゴリ',
|
||||
allCategories: 'おすすめ',
|
||||
},
|
||||
appCard: {
|
||||
addToWorkspace: 'ワークスペースに追加',
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
const translation = {
|
||||
|
||||
input: '入力',
|
||||
result: '結果',
|
||||
detail: '詳細',
|
||||
tracing: 'トレース',
|
||||
resultPanel: {
|
||||
status: 'ステータス',
|
||||
time: '経過時間',
|
||||
tokens: 'トークンの合計',
|
||||
},
|
||||
meta: {
|
||||
title: 'メタデータ',
|
||||
status: 'ステータス',
|
||||
version: 'バージョン',
|
||||
executor: '実行者',
|
||||
startTime: '開始時間',
|
||||
time: '経過時間',
|
||||
tokens: 'トークンの合計',
|
||||
steps: '実行ステップ',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
||||
@@ -1,5 +1,333 @@
|
||||
const translation = {
|
||||
|
||||
common: {
|
||||
editing: '編集中',
|
||||
autoSaved: '自動保存済み',
|
||||
unpublished: '未公開',
|
||||
published: '公開済み',
|
||||
publish: '公開する',
|
||||
update: '更新',
|
||||
run: '実行',
|
||||
running: '実行中',
|
||||
inRunMode: '実行モード中',
|
||||
inPreview: 'プレビュー中',
|
||||
inPreviewMode: 'プレビューモード中',
|
||||
preview: 'プレビュー',
|
||||
viewRunHistory: '実行履歴を表示',
|
||||
runHistory: '実行履歴',
|
||||
goBackToEdit: '編集に戻る',
|
||||
conversationLog: '会話ログ',
|
||||
features: '機能',
|
||||
debugAndPreview: 'デバッグとプレビュー',
|
||||
restart: '再起動',
|
||||
currentDraft: '現在の下書き',
|
||||
currentDraftUnpublished: '現在の下書き(未公開)',
|
||||
latestPublished: '最新の公開済み',
|
||||
publishedAt: '公開日時',
|
||||
restore: '復元',
|
||||
runApp: 'アプリを実行',
|
||||
batchRunApp: 'バッチでアプリを実行',
|
||||
accessAPIReference: 'APIリファレンスにアクセス',
|
||||
embedIntoSite: 'サイトに埋め込む',
|
||||
addTitle: 'タイトルを追加...',
|
||||
addDescription: '説明を追加...',
|
||||
noVar: '変数なし',
|
||||
searchVar: '変数を検索',
|
||||
variableNamePlaceholder: '変数名',
|
||||
setVarValuePlaceholder: '変数を設定',
|
||||
needConnecttip: 'このステップは何にも接続されていません',
|
||||
maxTreeDepth: 'ブランチごとの最大制限は{{depth}}ノードです',
|
||||
needEndNode: '終了ブロックを追加する必要があります',
|
||||
needAnswerNode: '回答ブロックを追加する必要があります',
|
||||
workflowProcess: 'ワークフロー処理',
|
||||
notRunning: 'まだ実行されていません',
|
||||
previewPlaceholder: 'チャットボットのデバッグを開始するには、以下のボックスにコンテンツを入力してください',
|
||||
effectVarConfirm: {
|
||||
title: '変数を削除',
|
||||
content: '他のノードで変数が使用されています。それでも削除しますか?',
|
||||
},
|
||||
insertVarTip: 'クイック挿入のために\'/\'キーを押します',
|
||||
},
|
||||
errorMsg: {
|
||||
fieldRequired: '{{field}}は必須です',
|
||||
authRequired: '認証が必要です',
|
||||
invalidJson: '{{field}}は無効です',
|
||||
fields: {
|
||||
variable: '変数名',
|
||||
variableValue: '変数値',
|
||||
code: 'コード',
|
||||
model: 'モデル',
|
||||
rerankModel: '再ランクモデル',
|
||||
},
|
||||
invalidVariable: '無効な変数',
|
||||
},
|
||||
singleRun: {
|
||||
testRun: 'テスト実行',
|
||||
startRun: '実行を開始',
|
||||
running: '実行中',
|
||||
},
|
||||
tabs: {
|
||||
'searchBlock': 'ブロックを検索',
|
||||
'blocks': 'ブロック',
|
||||
'builtInTool': '組み込みツール',
|
||||
'customTool': 'カスタムツール',
|
||||
'question-understand': '質問の理解',
|
||||
'logic': 'ロジック',
|
||||
'transform': '変換',
|
||||
'utilities': 'ユーティリティ',
|
||||
'noResult': '一致するものが見つかりませんでした',
|
||||
},
|
||||
blocks: {
|
||||
'start': '開始',
|
||||
'end': '終了',
|
||||
'answer': '回答',
|
||||
'llm': 'LLM',
|
||||
'knowledge-retrieval': '知識取得',
|
||||
'question-classifier': '質問分類器',
|
||||
'if-else': 'IF/ELSE',
|
||||
'code': 'コード',
|
||||
'template-transform': 'テンプレート',
|
||||
'http-request': 'HTTPリクエスト',
|
||||
'variable-assigner': '変数割り当て',
|
||||
},
|
||||
blocksAbout: {
|
||||
'start': 'ワークフローの開始に必要なパラメータを定義します',
|
||||
'end': 'ワークフローの終了と結果のタイプを定義します',
|
||||
'answer': 'チャット会話の応答内容を定義します',
|
||||
'llm': '大規模言語モデルを呼び出して質問に回答したり、自然言語を処理したりします',
|
||||
'knowledge-retrieval': 'ユーザーの質問に関連するテキストコンテンツを知識からクエリできるようにします',
|
||||
'question-classifier': 'ユーザーの質問の分類条件を定義し、LLMは分類記述に基づいて会話がどのように進行するかを定義できます',
|
||||
'if-else': 'IF/ELSE条件に基づいてワークフローを2つのブランチに分割できます',
|
||||
'code': 'カスタムロジックを実装するためにPythonまたはNodeJSコードを実行します',
|
||||
'template-transform': 'Jinjaテンプレート構文を使用してデータを文字列に変換します',
|
||||
'http-request': 'HTTPプロトコル経由でサーバーリクエストを送信できます',
|
||||
'variable-assigner': '異なるブランチで同じ変数に変数を割り当てて、後続のノードの一元化された構成を実現できます',
|
||||
},
|
||||
operator: {
|
||||
zoomIn: '拡大',
|
||||
zoomOut: '縮小',
|
||||
zoomTo50: '50%にズーム',
|
||||
zoomTo100: '100%にズーム',
|
||||
zoomToFit: 'フィットにズーム',
|
||||
},
|
||||
panel: {
|
||||
userInputField: 'ユーザー入力フィールド',
|
||||
changeBlock: 'ブロックを変更',
|
||||
helpLink: 'ヘルプリンク',
|
||||
about: '情報',
|
||||
createdBy: '作成者 ',
|
||||
nextStep: '次のステップ',
|
||||
addNextStep: 'このワークフローで次のブロックを追加',
|
||||
selectNextStep: '次のブロックを選択',
|
||||
runThisStep: 'このステップを実行',
|
||||
checklist: 'チェックリスト',
|
||||
checklistTip: '公開する前にすべての問題が解決されていることを確認してください',
|
||||
checklistResolved: 'すべての問題が解決されました',
|
||||
organizeBlocks: 'ブロックを整理',
|
||||
change: '変更',
|
||||
},
|
||||
nodes: {
|
||||
common: {
|
||||
outputVars: '出力変数',
|
||||
insertVarTip: '変数を挿入',
|
||||
memory: {
|
||||
memory: 'メモリ',
|
||||
memoryTip: 'チャットメモリ設定',
|
||||
windowSize: 'ウィンドウサイズ',
|
||||
conversationRoleName: '会話ロール名',
|
||||
user: 'ユーザー接頭辞',
|
||||
assistant: 'アシスタント接頭辞',
|
||||
},
|
||||
memories: {
|
||||
title: 'メモリ',
|
||||
tip: 'チャットメモリ',
|
||||
builtIn: '組み込み',
|
||||
},
|
||||
},
|
||||
start: {
|
||||
required: '必須',
|
||||
inputField: '入力フィールド',
|
||||
builtInVar: '組み込み変数',
|
||||
outputVars: {
|
||||
query: 'ユーザー入力',
|
||||
memories: {
|
||||
des: '会話履歴',
|
||||
type: 'メッセージタイプ',
|
||||
content: 'メッセージ内容',
|
||||
},
|
||||
files: 'ファイルリスト',
|
||||
},
|
||||
noVarTip: 'ワークフローで使用できる入力を設定します',
|
||||
},
|
||||
end: {
|
||||
outputs: '出力',
|
||||
output: {
|
||||
type: '出力タイプ',
|
||||
variable: '出力変数',
|
||||
},
|
||||
type: {
|
||||
'none': 'なし',
|
||||
'plain-text': 'プレーンテキスト',
|
||||
'structured': '構造化',
|
||||
},
|
||||
},
|
||||
answer: {
|
||||
answer: '回答',
|
||||
outputVars: '出力変数',
|
||||
},
|
||||
llm: {
|
||||
model: 'モデル',
|
||||
variables: '変数',
|
||||
context: 'コンテキスト',
|
||||
contextTooltip: 'コンテキストとして知識をインポートできます',
|
||||
notSetContextInPromptTip: 'コンテキスト機能を有効にするには、PROMPTにコンテキスト変数を記入してください。',
|
||||
prompt: 'プロンプト',
|
||||
roleDescription: {
|
||||
system: '会話の高レベルな命令を与えます',
|
||||
user: 'モデルへの指示、クエリ、またはテキストベースの入力を提供します',
|
||||
assistant: 'ユーザーメッセージに基づいてモデルの応答',
|
||||
},
|
||||
addMessage: 'メッセージを追加',
|
||||
vision: 'ビジョン',
|
||||
files: 'ファイル',
|
||||
resolution: {
|
||||
name: '解像度',
|
||||
high: '高い',
|
||||
low: '低い',
|
||||
},
|
||||
outputVars: {
|
||||
output: 'コンテンツを生成',
|
||||
usage: 'モデルの使用情報',
|
||||
},
|
||||
singleRun: {
|
||||
variable: '変数',
|
||||
},
|
||||
},
|
||||
knowledgeRetrieval: {
|
||||
queryVariable: 'クエリ変数',
|
||||
knowledge: '知識',
|
||||
outputVars: {
|
||||
output: '検索されたセグメント化されたデータ',
|
||||
content: 'セグメント化されたコンテンツ',
|
||||
title: 'セグメント化されたタイトル',
|
||||
icon: 'セグメント化されたアイコン',
|
||||
url: 'セグメント化されたURL',
|
||||
metadata: 'その他のメタデータ',
|
||||
},
|
||||
},
|
||||
http: {
|
||||
inputVars: '入力変数',
|
||||
api: 'API',
|
||||
apiPlaceholder: 'URLを入力、「/」を入力して変数を挿入',
|
||||
notStartWithHttp: 'APIはhttp://またはhttps://で始まる必要があります',
|
||||
key: 'キー',
|
||||
value: '値',
|
||||
bulkEdit: '一括編集',
|
||||
keyValueEdit: 'キー-値の編集',
|
||||
headers: 'ヘッダー',
|
||||
params: 'パラメータ',
|
||||
body: 'ボディ',
|
||||
outputVars: {
|
||||
body: 'レスポンスコンテンツ',
|
||||
statusCode: 'レスポンスステータスコード',
|
||||
headers: 'レスポンスヘッダーリストJSON',
|
||||
files: 'ファイルリスト',
|
||||
},
|
||||
authorization: {
|
||||
'authorization': '認証',
|
||||
'authorizationType': '認証タイプ',
|
||||
'no-auth': 'なし',
|
||||
'api-key': 'APIキー',
|
||||
'auth-type': '認証タイプ',
|
||||
'basic': '基本',
|
||||
'bearer': 'Bearer',
|
||||
'custom': 'カスタム',
|
||||
'api-key-title': 'APIキー',
|
||||
'header': 'ヘッダー',
|
||||
},
|
||||
insertVarPlaceholder: '変数を挿入するには\'/\'を入力してください',
|
||||
},
|
||||
code: {
|
||||
inputVars: '入力変数',
|
||||
outputVars: '出力変数',
|
||||
},
|
||||
templateTransform: {
|
||||
inputVars: '入力変数',
|
||||
code: 'コード',
|
||||
codeSupportTip: 'Jinja2のみをサポートしています',
|
||||
outputVars: {
|
||||
output: '変換されたコンテンツ',
|
||||
},
|
||||
},
|
||||
ifElse: {
|
||||
if: 'もし',
|
||||
else: 'それ以外',
|
||||
elseDescription: 'IF条件が満たされない場合に実行するロジックを定義します。',
|
||||
and: 'かつ',
|
||||
or: 'または',
|
||||
operator: '演算子',
|
||||
notSetVariable: 'まず変数を設定してください',
|
||||
comparisonOperator: {
|
||||
'contains': '含む',
|
||||
'not contains': '含まない',
|
||||
'start with': 'で始まる',
|
||||
'end with': 'で終わる',
|
||||
'is': 'である',
|
||||
'is not': 'でない',
|
||||
'empty': '空',
|
||||
'not empty': '空でない',
|
||||
'null': 'null',
|
||||
'not null': 'nullでない',
|
||||
},
|
||||
enterValue: '値を入力',
|
||||
addCondition: '条件を追加',
|
||||
conditionNotSetup: '条件が設定されていません',
|
||||
},
|
||||
variableAssigner: {
|
||||
title: '変数を割り当てる',
|
||||
outputType: '出力タイプ',
|
||||
outputVarType: '出力変数のタイプ',
|
||||
varNotSet: '変数が設定されていません',
|
||||
noVarTip: '割り当てる変数を追加してください',
|
||||
type: {
|
||||
string: '文字列',
|
||||
number: '数値',
|
||||
object: 'オブジェクト',
|
||||
array: '配列',
|
||||
},
|
||||
outputVars: {
|
||||
output: '割り当てられた変数の値',
|
||||
},
|
||||
},
|
||||
tool: {
|
||||
toAuthorize: '承認するには',
|
||||
inputVars: '入力変数',
|
||||
outputVars: {
|
||||
text: 'ツールが生成したコンテンツ',
|
||||
files: {
|
||||
title: 'ツールが生成したファイル',
|
||||
type: 'サポートタイプ。現在は画像のみサポートされています',
|
||||
transfer_method: '転送方法。値はremote_urlまたはlocal_fileです',
|
||||
url: '画像URL',
|
||||
upload_file_id: 'アップロードファイルID',
|
||||
},
|
||||
},
|
||||
},
|
||||
questionClassifiers: {
|
||||
model: 'モデル',
|
||||
inputVars: '入力変数',
|
||||
class: 'クラス',
|
||||
classNamePlaceholder: 'クラス名を入力してください',
|
||||
advancedSetting: '高度な設定',
|
||||
topicName: 'トピック名',
|
||||
topicPlaceholder: 'トピック名を入力してください',
|
||||
addClass: 'クラスを追加',
|
||||
instruction: '指示',
|
||||
instructionPlaceholder: '指示を入力してください',
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
stopBy: '{{user}}によって停止',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
||||
@@ -266,19 +266,32 @@ const translation = {
|
||||
queryNoBeEmpty: 'A consulta deve ser definida na solicitação',
|
||||
},
|
||||
variableConig: {
|
||||
'addModalTitle': 'Configurações do Campo',
|
||||
'addModalTitle': 'Adicionar Campo de Entrada',
|
||||
'editModalTitle': 'Editar Campo de Entrada',
|
||||
'description': 'Configuração para a variável {{varName}}',
|
||||
'fieldType': 'Tipo de Campo',
|
||||
'string': 'Texto Curto',
|
||||
'text-input': 'Texto Curto',
|
||||
'paragraph': 'Parágrafo',
|
||||
'select': 'Selecionar',
|
||||
'notSet': 'Não definido, tente digitar {{input}} na solicitação',
|
||||
'stringTitle': 'Opções da Caixa de Texto do Formulário',
|
||||
'number': 'Número',
|
||||
'notSet': 'Não definido, tente digitar {{input}} no prompt de prefixo',
|
||||
'stringTitle': 'Opções da caixa de texto do formulário',
|
||||
'maxLength': 'Comprimento Máximo',
|
||||
'options': 'Opções',
|
||||
'addOption': 'Adicionar opção',
|
||||
'apiBasedVar': 'Variável Baseada em API',
|
||||
'varName': 'Nome da Variável',
|
||||
'labelName': 'Nome do Rótulo',
|
||||
'inputPlaceholder': 'Por favor, insira',
|
||||
'required': 'Obrigatório',
|
||||
'errorMsg': {
|
||||
varNameRequired: 'O nome da variável é obrigatório',
|
||||
labelNameRequired: 'O nome do rótulo é obrigatório',
|
||||
varNameCanBeRepeat: 'O nome da variável não pode ser repetido',
|
||||
atLeastOneOption: 'Pelo menos uma opção é obrigatória',
|
||||
optionRepeat: 'Tem opções repetidas',
|
||||
},
|
||||
},
|
||||
vision: {
|
||||
name: 'Visão',
|
||||
@@ -349,6 +362,7 @@ const translation = {
|
||||
result: 'Texto de Saída',
|
||||
datasetConfig: {
|
||||
settingTitle: 'Configurações de Recuperação',
|
||||
knowledgeTip: 'Clique no botão “+” para adicionar conhecimento',
|
||||
retrieveOneWay: {
|
||||
title: 'Recuperação N-para-1',
|
||||
description: 'Com base na intenção do usuário e nas descrições do Conhecimento, o Agente seleciona autonomamente o melhor Conhecimento para consulta. Melhor para aplicativos com Conhecimento distinto e limitado.',
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
const translation = {
|
||||
title: 'Registros',
|
||||
description: 'Os registros registram o status de execução do aplicativo, incluindo as entradas do usuário e as respostas da IA.',
|
||||
description: 'Os registros registram o status de execução do aplicativo, incluindo entradas do usuário e respostas do AI.',
|
||||
dateTimeFormat: 'MM/DD/YYYY hh:mm A',
|
||||
table: {
|
||||
header: {
|
||||
time: 'Tempo',
|
||||
time: 'Hora',
|
||||
endUser: 'Usuário Final',
|
||||
input: 'Entrada',
|
||||
output: 'Saída',
|
||||
@@ -12,58 +12,72 @@ const translation = {
|
||||
messageCount: 'Contagem de Mensagens',
|
||||
userRate: 'Taxa de Usuário',
|
||||
adminRate: 'Taxa de Op.',
|
||||
startTime: 'HORA DE INÍCIO',
|
||||
status: 'STATUS',
|
||||
runtime: 'TEMPO DE EXECUÇÃO',
|
||||
tokens: 'TOKENS',
|
||||
user: 'USUÁRIO FINAL',
|
||||
version: 'VERSÃO',
|
||||
},
|
||||
pagination: {
|
||||
previous: 'Anterior',
|
||||
next: 'Próximo',
|
||||
},
|
||||
empty: {
|
||||
noChat: 'Nenhuma conversa ainda',
|
||||
noOutput: 'Nenhuma saída',
|
||||
noChat: 'Ainda não há conversas',
|
||||
noOutput: 'Sem saída',
|
||||
element: {
|
||||
title: 'Tem alguém aí?',
|
||||
content: 'Observe e anote as interações entre usuários finais e aplicativos de IA aqui para melhorar continuamente a precisão da IA. Você pode tentar <shareLink>compartilhar</shareLink> ou <testLink>testar</testLink> o aplicativo da Web você mesmo e depois voltar para esta página.',
|
||||
content: 'Observe e anote as interações entre os usuários finais e os aplicativos de IA aqui para melhorar continuamente a precisão da IA. Você pode tentar <shareLink>compartilhar</shareLink> ou <testLink>testar</testLink> o aplicativo da Web você mesmo, e depois voltar para esta página.',
|
||||
},
|
||||
},
|
||||
},
|
||||
detail: {
|
||||
time: 'Tempo',
|
||||
time: 'Hora',
|
||||
conversationId: 'ID da Conversa',
|
||||
promptTemplate: 'Modelo de Prompt',
|
||||
promptTemplateBeforeChat: 'Modelo de Prompt Antes da Conversa · Como Mensagem do Sistema',
|
||||
promptTemplateBeforeChat: 'Modelo de Prompt Antes do Chat · Como Mensagem do Sistema',
|
||||
annotationTip: 'Melhorias Marcadas por {{user}}',
|
||||
timeConsuming: '',
|
||||
second: 's',
|
||||
tokenCost: 'Tokens gastos',
|
||||
tokenCost: 'Token gasto',
|
||||
loading: 'carregando',
|
||||
operation: {
|
||||
like: 'curtir',
|
||||
dislike: 'não curtir',
|
||||
addAnnotation: 'Adicionar Melhoria',
|
||||
editAnnotation: 'Editar Melhoria',
|
||||
annotationPlaceholder: 'Digite a resposta esperada que você deseja que a IA responda, que pode ser usada para ajustar o modelo e melhorar continuamente a qualidade da geração de texto no futuro.',
|
||||
annotationPlaceholder: 'Digite a resposta esperada que você deseja que o AI responda, o que pode ser usado para ajustar o modelo e melhorar continuamente a qualidade da geração de texto no futuro.',
|
||||
},
|
||||
variables: 'Variáveis',
|
||||
uploadImages: 'Imagens Enviadas',
|
||||
uploadImages: 'Imagens Carregadas',
|
||||
},
|
||||
filter: {
|
||||
period: {
|
||||
today: 'Hoje',
|
||||
last7days: 'Últimos 7 Dias',
|
||||
last7days: 'Últimos 7 dias',
|
||||
last4weeks: 'Últimas 4 semanas',
|
||||
last3months: 'Últimos 3 meses',
|
||||
last12months: 'Últimos 12 meses',
|
||||
monthToDate: 'Mês até a data',
|
||||
quarterToDate: 'Trimestre até a data',
|
||||
yearToDate: 'Ano até a data',
|
||||
monthToDate: 'Mês até hoje',
|
||||
quarterToDate: 'Trimestre até hoje',
|
||||
yearToDate: 'Ano até hoje',
|
||||
allTime: 'Todo o tempo',
|
||||
},
|
||||
annotation: {
|
||||
all: 'Todos',
|
||||
all: 'Tudo',
|
||||
annotated: 'Melhorias Anotadas ({{count}} itens)',
|
||||
not_annotated: 'Não Anotadas',
|
||||
not_annotated: 'Não Anotado',
|
||||
},
|
||||
},
|
||||
workflowTitle: 'Registros de Fluxo de Trabalho',
|
||||
workflowSubtitle: 'O registro registrou a operação do Automate.',
|
||||
runDetail: {
|
||||
title: 'Registro de Conversa',
|
||||
workflowTitle: 'Detalhes do Registro',
|
||||
},
|
||||
promptLog: 'Registro de Prompt',
|
||||
viewLog: 'Ver Registro',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
const translation = {
|
||||
welcome: {
|
||||
firstStepTip: 'Para começar,',
|
||||
enterKeyTip: 'insira sua chave de API do OpenAI abaixo',
|
||||
getKeyTip: 'Obtenha sua chave de API no painel do OpenAI',
|
||||
placeholder: 'Sua chave de API do OpenAI (por exemplo, sk-xxxx)',
|
||||
enterKeyTip: 'insira sua chave de API OpenAI abaixo',
|
||||
getKeyTip: 'Obtenha sua chave de API no painel da OpenAI',
|
||||
placeholder: 'Sua chave de API OpenAI (ex. sk-xxxx)',
|
||||
},
|
||||
apiKeyInfo: {
|
||||
cloud: {
|
||||
trial: {
|
||||
title: 'Você está usando a cota de teste do {{providerName}}.',
|
||||
description: 'A cota de teste é fornecida para uso de teste. Antes que as chamadas da cota de teste se esgotem, configure seu próprio provedor de modelo ou compre uma cota adicional.',
|
||||
title: 'Você está usando a cota de teste da {{providerName}}.',
|
||||
description: 'A cota de teste é fornecida para seu uso de teste. Antes que as chamadas de cota de teste se esgotem, configure seu próprio provedor de modelo ou compre cota adicional.',
|
||||
},
|
||||
exhausted: {
|
||||
title: 'Sua cota de teste foi esgotada, configure sua chave de API.',
|
||||
description: 'Sua cota de teste foi esgotada. Configure seu próprio provedor de modelo ou compre uma cota adicional.',
|
||||
title: 'Sua cota de teste foi usada, configure sua chave de API.',
|
||||
description: 'Sua cota de teste foi esgotada. Configure seu próprio provedor de modelo ou compre cota adicional.',
|
||||
},
|
||||
},
|
||||
selfHost: {
|
||||
title: {
|
||||
row1: 'Para começar,',
|
||||
row2: 'configure seu próprio provedor de modelo primeiro.',
|
||||
row2: 'configure primeiro seu provedor de modelo.',
|
||||
},
|
||||
},
|
||||
callTimes: 'Número de chamadas',
|
||||
usedToken: 'Tokens usados',
|
||||
setAPIBtn: 'Ir para configurar provedor de modelo',
|
||||
setAPIBtn: 'Ir para configurar o provedor de modelo',
|
||||
tryCloud: 'Ou experimente a versão em nuvem do Dify com cota gratuita',
|
||||
},
|
||||
overview: {
|
||||
title: 'Visão geral',
|
||||
title: 'Visão Geral',
|
||||
appInfo: {
|
||||
explanation: 'Aplicativo Web de IA pronto para uso',
|
||||
accessibleAddress: 'URL pública',
|
||||
explanation: 'WebApp de IA Pronta para Uso',
|
||||
accessibleAddress: 'URL Pública',
|
||||
preview: 'Visualização',
|
||||
regenerate: 'Regenerar',
|
||||
preUseReminder: 'Ative o aplicativo da Web antes de continuar.',
|
||||
preUseReminder: 'Por favor, ative o WebApp antes de continuar.',
|
||||
settings: {
|
||||
entry: 'Configurações',
|
||||
title: 'Configurações do aplicativo da Web',
|
||||
webName: 'Nome do aplicativo da Web',
|
||||
webDesc: 'Descrição do aplicativo da Web',
|
||||
title: 'Configurações do WebApp',
|
||||
webName: 'Nome do WebApp',
|
||||
webDesc: 'Descrição do WebApp',
|
||||
webDescTip: 'Este texto será exibido no lado do cliente, fornecendo orientações básicas sobre como usar o aplicativo',
|
||||
webDescPlaceholder: 'Insira a descrição do aplicativo da Web',
|
||||
webDescPlaceholder: 'Insira a descrição do WebApp',
|
||||
language: 'Idioma',
|
||||
more: {
|
||||
entry: 'Mostrar mais configurações',
|
||||
@@ -49,55 +49,55 @@ const translation = {
|
||||
copyRightPlaceholder: 'Insira o nome do autor ou organização',
|
||||
privacyPolicy: 'Política de Privacidade',
|
||||
privacyPolicyPlaceholder: 'Insira o link da política de privacidade',
|
||||
privacyPolicyTip: 'Ajuda os visitantes a entender os dados que o aplicativo coleta, consulte a <privacyPolicyLink>Política de Privacidade</privacyPolicyLink> do Dify.',
|
||||
privacyPolicyTip: 'Ajuda os visitantes a entender os dados coletados pelo aplicativo, consulte a <privacyPolicyLink>Política de Privacidade</privacyPolicyLink> do Dify.',
|
||||
},
|
||||
},
|
||||
embedded: {
|
||||
entry: 'Embutido',
|
||||
title: 'Incorporar no site',
|
||||
explanation: 'Escolha a maneira de incorporar o aplicativo de bate-papo ao seu site',
|
||||
iframe: 'Para adicionar o aplicativo de bate-papo em qualquer lugar do seu site, adicione este iframe ao seu código HTML.',
|
||||
scripts: 'Para adicionar um aplicativo de bate-papo na parte inferior direita do seu site, adicione este código ao seu HTML.',
|
||||
chromePlugin: 'Instalar Extensão do Chatbot Dify para o Chrome',
|
||||
explanation: 'Escolha a maneira de incorporar o aplicativo de chat ao seu site',
|
||||
iframe: 'Para adicionar o aplicativo de chat em qualquer lugar do seu site, adicione este iframe ao seu código HTML.',
|
||||
scripts: 'Para adicionar um aplicativo de chat no canto inferior direito do seu site, adicione este código ao seu HTML.',
|
||||
chromePlugin: 'Instalar a Extensão do Chrome Dify Chatbot',
|
||||
copied: 'Copiado',
|
||||
copy: 'Copiar',
|
||||
},
|
||||
qrcode: {
|
||||
title: 'Código QR para compartilhar',
|
||||
scan: 'Digitalizar para compartilhar o aplicativo',
|
||||
scan: 'Digitalizar e compartilhar o aplicativo',
|
||||
download: 'Baixar código QR',
|
||||
},
|
||||
customize: {
|
||||
way: 'maneira',
|
||||
way: 'modo',
|
||||
entry: 'Personalizar',
|
||||
title: 'Personalizar aplicativo Web de IA',
|
||||
explanation: 'Você pode personalizar a interface do usuário do aplicativo Web para se adequar ao seu cenário e necessidades de estilo.',
|
||||
title: 'Personalizar WebApp de IA',
|
||||
explanation: 'Você pode personalizar a interface do usuário do Web App para atender às suas necessidades de cenário e estilo.',
|
||||
way1: {
|
||||
name: 'Fork do código do cliente, modifique-o e implante no Vercel (recomendado)',
|
||||
step1: 'Fork do código do cliente e modifique-o',
|
||||
step1Tip: 'Clique aqui para fazer um fork do código-fonte em sua conta do GitHub e modificar o código',
|
||||
step1Operation: 'Dify-WebClient',
|
||||
name: 'Faça um fork do código do cliente, modifique-o e implante-o no Vercel (recomendado)',
|
||||
step1: 'Faça um fork do código do cliente e modifique-o',
|
||||
step1Tip: 'Clique aqui para fazer um fork do código-fonte na sua conta GitHub e modificar o código',
|
||||
step1Operation: 'Cliente-Web-Dify',
|
||||
step2: 'Implantar no Vercel',
|
||||
step2Tip: 'Clique aqui para importar o repositório no Vercel e implantar',
|
||||
step2Operation: 'Importar repositório',
|
||||
step3: 'Configurar variáveis de ambiente',
|
||||
step3: 'Configurar as variáveis de ambiente',
|
||||
step3Tip: 'Adicione as seguintes variáveis de ambiente no Vercel',
|
||||
},
|
||||
way2: {
|
||||
name: 'Escrever código do lado do cliente para chamar a API e implantá-lo em um servidor',
|
||||
name: 'Escreva código do lado do cliente para chamar a API e implante-o em um servidor',
|
||||
operation: 'Documentação',
|
||||
},
|
||||
},
|
||||
},
|
||||
apiInfo: {
|
||||
title: 'API do serviço de backend',
|
||||
explanation: 'Integração fácil em seu aplicativo',
|
||||
accessibleAddress: 'Endpoint da API de serviço',
|
||||
title: 'API de Serviço de Back-end',
|
||||
explanation: 'Facilmente integrado em sua aplicação',
|
||||
accessibleAddress: 'Endpoint do Serviço API',
|
||||
doc: 'Referência da API',
|
||||
},
|
||||
status: {
|
||||
running: 'Em serviço',
|
||||
disable: 'Desativar',
|
||||
disable: 'Desabilitar',
|
||||
},
|
||||
},
|
||||
analysis: {
|
||||
@@ -105,32 +105,36 @@ const translation = {
|
||||
ms: 'ms',
|
||||
tokenPS: 'Token/s',
|
||||
totalMessages: {
|
||||
title: 'Total de mensagens',
|
||||
explanation: 'Contagem diária de interações de IA; engenharia de prompt/depuração excluída.',
|
||||
title: 'Total de Mensagens',
|
||||
explanation: 'Contagem diária de interações AI; engenharia/de depuração excluída.',
|
||||
},
|
||||
activeUsers: {
|
||||
title: 'Usuários ativos',
|
||||
explanation: 'Usuários únicos envolvidos em perguntas e respostas com IA; engenharia de prompt/depuração excluída.',
|
||||
title: 'Usuários Ativos',
|
||||
explanation: 'Usuários únicos engajando em Q&A com AI; engenharia/de depuração excluída.',
|
||||
},
|
||||
tokenUsage: {
|
||||
title: 'Uso de tokens',
|
||||
explanation: 'Reflete o uso diário de tokens do modelo de linguagem para o aplicativo, útil para fins de controle de custos.',
|
||||
consumed: 'Consumidos',
|
||||
title: 'Uso de Token',
|
||||
explanation: 'Reflete o uso diário do token do modelo de linguagem para o aplicativo, útil para fins de controle de custos.',
|
||||
consumed: 'Consumido',
|
||||
},
|
||||
avgSessionInteractions: {
|
||||
title: 'Média de interações por sessão',
|
||||
explanation: 'Contagem contínua de comunicação usuário-IA; para aplicativos baseados em conversas.',
|
||||
title: 'Média de Interações por Sessão',
|
||||
explanation: 'Contagem de comunicação contínua entre usuário e AI; para aplicativos baseados em conversação.',
|
||||
},
|
||||
avgUserInteractions: {
|
||||
title: 'Média de Interações por Usuário',
|
||||
explanation: 'Reflete a frequência de uso diário dos usuários. Essa métrica reflete a fidelidade do usuário.',
|
||||
},
|
||||
userSatisfactionRate: {
|
||||
title: 'Taxa de satisfação do usuário',
|
||||
title: 'Taxa de Satisfação do Usuário',
|
||||
explanation: 'O número de curtidas por 1.000 mensagens. Isso indica a proporção de respostas com as quais os usuários estão altamente satisfeitos.',
|
||||
},
|
||||
avgResponseTime: {
|
||||
title: 'Tempo médio de resposta',
|
||||
explanation: 'Tempo (ms) para o processamento/resposta da IA; para aplicativos baseados em texto.',
|
||||
title: 'Tempo Médio de Resposta',
|
||||
explanation: 'Tempo (ms) para o AI processar/responder; para aplicativos baseados em texto.',
|
||||
},
|
||||
tps: {
|
||||
title: 'Velocidade de saída de tokens',
|
||||
title: 'Velocidade de Saída do Token',
|
||||
explanation: 'Mede o desempenho do LLM. Conta a velocidade de saída de tokens do LLM desde o início da solicitação até a conclusão da saída.',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,62 +1,90 @@
|
||||
const translation = {
|
||||
createApp: 'Criar aplicativo',
|
||||
createApp: 'CRIAR APLICATIVO',
|
||||
types: {
|
||||
all: 'Tudo',
|
||||
all: 'Todos',
|
||||
chatbot: 'Chatbot',
|
||||
agent: 'Agente',
|
||||
workflow: 'Fluxo de trabalho',
|
||||
completion: 'Gerador de Texto',
|
||||
completion: 'Conclusão',
|
||||
},
|
||||
duplicate: 'Duplicar',
|
||||
duplicateTitle: 'Duplicate aplicativo',
|
||||
duplicateTitle: 'Duplicar Aplicativo',
|
||||
export: 'Exportar DSL',
|
||||
createFromConfigFile: 'Criar através do arquivo DSL',
|
||||
exportFailed: 'Falha ao exportar DSL.',
|
||||
importDSL: 'Importar arquivo DSL',
|
||||
createFromConfigFile: 'Criar a partir do arquivo DSL',
|
||||
deleteAppConfirmTitle: 'Excluir este aplicativo?',
|
||||
deleteAppConfirmContent:
|
||||
'A exclusão do aplicativo é irreversível. Os usuários não poderão mais acessar seu aplicativo e todas as configurações de prompt e logs serão excluídas permanentemente.',
|
||||
'A exclusão do aplicativo é irreversível. Os usuários não poderão mais acessar seu aplicativo e todas as configurações de prompt e logs serão permanentemente excluídas.',
|
||||
appDeleted: 'Aplicativo excluído',
|
||||
appDeleteFailed: 'Falha ao excluir o aplicativo',
|
||||
appDeleteFailed: 'Falha ao excluir aplicativo',
|
||||
join: 'Participe da comunidade',
|
||||
communityIntro:
|
||||
'Discuta com membros da equipe, colaboradores e desenvolvedores em diferentes canais.',
|
||||
roadmap: 'Veja nosso roteiro',
|
||||
newApp: {
|
||||
startFromBlank: 'Começar de um aplicativo em branco',
|
||||
startFromTemplate: 'Começar a partir de um modelo',
|
||||
captionAppType: 'Que tipo de aplicativo você deseja?',
|
||||
chatbotDescription: 'Construir um assistente baseado em chat usando um Modelo de Linguagem de Grande Escala',
|
||||
agentDescription: 'Construir um Agente inteligente que pode escolher autonomamente ferramentas para completar as tarefas',
|
||||
workflowDescription: 'Description text here',
|
||||
captionName: 'Dê um nome ao seu aplicativo',
|
||||
appNamePlaceholder: 'Dê um nome ao seu aplicativo',
|
||||
startFromBlank: 'Criar do zero',
|
||||
startFromTemplate: 'Criar do modelo',
|
||||
captionAppType: 'Que tipo de aplicativo você deseja criar?',
|
||||
chatbotDescription: 'Construa um aplicativo baseado em chat. Este aplicativo usa um formato de pergunta e resposta, permitindo várias rodadas de conversa contínua.',
|
||||
completionDescription: 'Construa um aplicativo que gera texto de alta qualidade com base em prompts, como geração de artigos, resumos, traduções e muito mais.',
|
||||
completionWarning: 'Este tipo de aplicativo não será mais suportado.',
|
||||
agentDescription: 'Construa um Agente inteligente que pode escolher ferramentas para completar as tarefas autonomamente',
|
||||
workflowDescription: 'Construa um aplicativo que gera texto de alta qualidade com base em fluxo de trabalho com alto grau de personalização. É adequado para usuários experientes.',
|
||||
workflowWarning: 'Atualmente em beta',
|
||||
chatbotType: 'Método de orquestração do Chatbot',
|
||||
basic: 'Básico',
|
||||
basicTip: 'Para iniciantes, pode mudar para o Chatflow mais tarde',
|
||||
basicFor: 'PARA INICIANTES',
|
||||
basicDescription: 'A Orquestração Básica permite orquestrar um aplicativo Chatbot usando configurações simples, sem a capacidade de modificar prompts integrados. É adequado para iniciantes.',
|
||||
advanced: 'Chatflow',
|
||||
advancedFor: 'Para usuários avançados',
|
||||
advancedDescription: 'A Orquestração de Fluxo de Trabalho orquestra Chatbots na forma de fluxos de trabalho, oferecendo um alto grau de personalização, incluindo a capacidade de editar prompts integrados. É adequado para usuários experientes.',
|
||||
captionName: 'Ícone e nome do aplicativo',
|
||||
appNamePlaceholder: 'Dê um nome para o seu aplicativo',
|
||||
captionDescription: 'Descrição',
|
||||
appDescriptionPlaceholder: 'Enter the description of the app',
|
||||
useTemplate: 'Insira a descrição do aplicativo',
|
||||
appDescriptionPlaceholder: 'Digite a descrição do aplicativo',
|
||||
useTemplate: 'Usar este modelo',
|
||||
previewDemo: 'Visualizar demonstração',
|
||||
chatApp: 'Usar este modelo',
|
||||
chatApp: 'Assistente',
|
||||
chatAppIntro:
|
||||
'Quero construir um aplicativo baseado em chat. Este aplicativo usa um formato de pergunta e resposta, permitindo várias rodadas de conversa contínua.',
|
||||
agentAssistant: 'Assistente de novo agente',
|
||||
'Eu quero construir um aplicativo baseado em chat. Este aplicativo usa um formato de pergunta e resposta, permitindo várias rodadas de conversa contínua.',
|
||||
agentAssistant: 'Novo Assistente de Agente',
|
||||
completeApp: 'Gerador de Texto',
|
||||
completeAppIntro:
|
||||
'Quero criar um aplicativo que gera texto de alta qualidade com base em prompts, como geração de artigos, resumos, traduções e muito mais.',
|
||||
showTemplates: 'Quero escolher um modelo',
|
||||
hideTemplates: 'Voltar para seleção de modo',
|
||||
'Eu quero criar um aplicativo que gera texto de alta qualidade com base em prompts, como geração de artigos, resumos, traduções e muito mais.',
|
||||
showTemplates: 'Quero escolher a partir de um modelo',
|
||||
hideTemplates: 'Voltar para a seleção de modo',
|
||||
Create: 'Criar',
|
||||
Cancel: 'Cancelar',
|
||||
nameNotEmpty: 'O nome não pode estar vazio',
|
||||
appTemplateNotSelected: 'Por favor, selecione um modelo',
|
||||
appTypeRequired: 'Por favor, selecione um tipo de aplicativo',
|
||||
appCreated: 'Aplicativo criado',
|
||||
appCreateFailed: 'Falha ao criar o aplicativo',
|
||||
},
|
||||
editApp: {
|
||||
startToEdit: 'Editar Aplicativo',
|
||||
appCreateFailed: 'Falha ao criar aplicativo',
|
||||
},
|
||||
editApp: 'Editar Informações',
|
||||
editAppTitle: 'Editar Informações do Aplicativo',
|
||||
editDone: 'Informações do aplicativo atualizadas',
|
||||
editFailed: 'Falha ao atualizar informações do aplicativo',
|
||||
emoji: {
|
||||
ok: 'OK',
|
||||
cancel: 'Cancelar',
|
||||
},
|
||||
switch: 'Mudar para Orquestração de Fluxo de Trabalho',
|
||||
switchTipStart: 'Será criada uma nova cópia do aplicativo para você e a nova cópia mudará para Orquestração de Fluxo de Trabalho. A nova cópia não permitirá a ',
|
||||
switchTip: 'volta',
|
||||
switchTipEnd: ' para Orquestração Básica.',
|
||||
switchLabel: 'A cópia do aplicativo a ser criada',
|
||||
removeOriginal: 'Excluir o aplicativo original',
|
||||
switchStart: 'Iniciar mudança',
|
||||
typeSelector: {
|
||||
all: 'Todos os Tipos',
|
||||
chatbot: 'Chatbot',
|
||||
agent: 'Agente',
|
||||
workflow: 'Fluxo de trabalho',
|
||||
completion: 'Conclusão',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
||||
@@ -478,6 +478,10 @@ const translation = {
|
||||
title: 'Variáveis e Ferramentas Externas',
|
||||
desc: 'Inserir Variáveis e Ferramentas Externas',
|
||||
},
|
||||
outputToolDisabledItem: {
|
||||
title: 'Variáveis',
|
||||
desc: 'Inserir variáveis',
|
||||
},
|
||||
modal: {
|
||||
add: 'Nova variável',
|
||||
addTool: 'Nova ferramenta',
|
||||
|
||||
@@ -18,7 +18,7 @@ const translation = {
|
||||
apps: {
|
||||
title: 'Explorar Aplicações por Dify',
|
||||
description: 'Use esses aplicativos modelo instantaneamente ou personalize seus próprios aplicativos com base nos modelos.',
|
||||
allCategories: 'Todas as Categorias',
|
||||
allCategories: 'Recomendado',
|
||||
},
|
||||
appCard: {
|
||||
addToWorkspace: 'Adicionar ao Espaço de Trabalho',
|
||||
|
||||
@@ -1,10 +1,22 @@
|
||||
const translation = {
|
||||
result: 'RESULT',
|
||||
tracing: 'TRACING',
|
||||
input: 'ENTRADA',
|
||||
result: 'RESULTADO',
|
||||
detail: 'DETALHE',
|
||||
tracing: 'RASTREIO',
|
||||
resultPanel: {
|
||||
status: 'STATUS',
|
||||
time: 'ELAPSED TIME',
|
||||
tokens: 'TOTAL TOKENS',
|
||||
time: 'TEMPO DECORRIDO',
|
||||
tokens: 'TOTAL DE TOKENS',
|
||||
},
|
||||
meta: {
|
||||
title: 'METADADOS',
|
||||
status: 'Status',
|
||||
version: 'Versão',
|
||||
executor: 'Executor',
|
||||
startTime: 'Hora de Início',
|
||||
time: 'Tempo Decorrido',
|
||||
tokens: 'Total de Tokens',
|
||||
steps: 'Passos de Execução',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -1,90 +1,332 @@
|
||||
const translation = {
|
||||
common: {
|
||||
editing: 'Editando',
|
||||
autoSaved: 'Auto-salvo',
|
||||
unpublished: 'Não publicado',
|
||||
published: 'Publicado',
|
||||
publish: 'Publicar',
|
||||
update: 'Atualizar',
|
||||
run: 'Executar',
|
||||
running: 'Executando',
|
||||
inRunMode: 'No modo de execução',
|
||||
inPreview: 'Na prévia',
|
||||
inPreviewMode: 'No modo de prévia',
|
||||
preview: 'Prévia',
|
||||
viewRunHistory: 'Ver histórico de execução',
|
||||
runHistory: 'Histórico de execução',
|
||||
goBackToEdit: 'Voltar para a edição',
|
||||
conversationLog: 'Registro de conversa',
|
||||
features: 'Recursos',
|
||||
debugAndPreview: 'Depurar e pré-visualizar',
|
||||
restart: 'Reiniciar',
|
||||
currentDraft: 'Rascunho atual',
|
||||
currentDraftUnpublished: 'Rascunho atual não publicado',
|
||||
latestPublished: 'Último publicado',
|
||||
publishedAt: 'Publicado em',
|
||||
restore: 'Restaurar',
|
||||
runApp: 'Executar aplicativo',
|
||||
batchRunApp: 'Executar aplicativo em lote',
|
||||
accessAPIReference: 'Acessar referência da API',
|
||||
embedIntoSite: 'Incorporar no site',
|
||||
addTitle: 'Adicionar título...',
|
||||
addDescription: 'Adicionar descrição...',
|
||||
noVar: 'Sem variável',
|
||||
searchVar: 'Buscar variável',
|
||||
variableNamePlaceholder: 'Nome da variável',
|
||||
setVarValuePlaceholder: 'Definir variável',
|
||||
needConnecttip: 'Esta etapa não está conectada a nada',
|
||||
maxTreeDepth: 'Limite máximo de {{depth}} nós por ramificação',
|
||||
needEndNode: 'O bloco de fim deve ser adicionado',
|
||||
needAnswerNode: 'O bloco de resposta deve ser adicionado',
|
||||
workflowProcess: 'Processo de fluxo de trabalho',
|
||||
notRunning: 'Ainda não em execução',
|
||||
previewPlaceholder: 'Digite o conteúdo na caixa abaixo para iniciar a depuração do Chatbot',
|
||||
effectVarConfirm: {
|
||||
title: 'Remover variável',
|
||||
content: 'A variável está sendo usada em outros nós. Deseja removê-la mesmo assim?',
|
||||
},
|
||||
insertVarTip: 'Pressione a tecla \'/\' para inserir rapidamente',
|
||||
},
|
||||
errorMsg: {
|
||||
fieldRequired: '{{field}} é obrigatório',
|
||||
authRequired: 'Autorização é necessária',
|
||||
invalidJson: '{{field}} é JSON inválido',
|
||||
fields: {
|
||||
variable: 'Nome da Variável',
|
||||
variableValue: 'Valor da Variável',
|
||||
code: 'Código',
|
||||
model: 'Modelo',
|
||||
rerankModel: 'Modelo de Re-ordenação',
|
||||
},
|
||||
invalidVariable: 'Variável inválida',
|
||||
},
|
||||
singleRun: {
|
||||
testRun: 'Execução de teste ',
|
||||
startRun: 'Iniciar execução',
|
||||
running: 'Executando',
|
||||
},
|
||||
tabs: {
|
||||
'searchBlock': 'Buscar bloco',
|
||||
'blocks': 'Blocos',
|
||||
'builtInTool': 'Ferramenta incorporada',
|
||||
'customTool': 'Ferramenta personalizada',
|
||||
'question-understand': 'Entendimento da pergunta',
|
||||
'logic': 'Lógica',
|
||||
'transform': 'Transformar',
|
||||
'utilities': 'Utilitários',
|
||||
'noResult': 'Nenhum resultado encontrado',
|
||||
},
|
||||
blocks: {
|
||||
'start': 'Início',
|
||||
'end': 'Fim',
|
||||
'answer': 'Resposta',
|
||||
'llm': 'LLM',
|
||||
'knowledge-retrieval': 'Recuperação de conhecimento',
|
||||
'question-classifier': 'Classificador de perguntas',
|
||||
'if-else': 'Se/Senão',
|
||||
'code': 'Código',
|
||||
'template-transform': 'Modelo',
|
||||
'http-request': 'Requisição HTTP',
|
||||
'variable-assigner': 'Atribuidor de variáveis',
|
||||
},
|
||||
blocksAbout: {
|
||||
'start': 'Defina os parâmetros iniciais para iniciar um fluxo de trabalho',
|
||||
'end': 'Defina o final e o tipo de resultado de um fluxo de trabalho',
|
||||
'answer': 'Defina o conteúdo da resposta de uma conversa no chat',
|
||||
'llm': 'Invocar grandes modelos de linguagem para responder perguntas ou processar linguagem natural',
|
||||
'knowledge-retrieval': 'Permite consultar conteúdo de texto relacionado a perguntas de usuário do conhecimento',
|
||||
'question-classifier': 'Define as condições de classificação de perguntas do usuário, LLM pode definir como a conversa progride com base na descrição da classificação',
|
||||
'if-else': 'Permite dividir o fluxo de trabalho em dois ramos com base em condições if/else',
|
||||
'code': 'Execute um trecho de código Python ou NodeJS para implementar lógica personalizada',
|
||||
'template-transform': 'Converta dados em string usando a sintaxe do modelo Jinja',
|
||||
'http-request': 'Permite enviar solicitações de servidor sobre o protocolo HTTP',
|
||||
'variable-assigner': 'Atribua variáveis em diferentes ramos à mesma variável para alcançar uma configuração unificada de pós-nós',
|
||||
},
|
||||
operator: {
|
||||
zoomIn: 'Aumentar zoom',
|
||||
zoomOut: 'Diminuir zoom',
|
||||
zoomTo50: 'Zoom para 50%',
|
||||
zoomTo100: 'Zoom para 100%',
|
||||
zoomToFit: 'Zoom para ajustar',
|
||||
},
|
||||
panel: {
|
||||
userInputField: 'Campo de entrada do usuário',
|
||||
changeBlock: 'Mudar bloco',
|
||||
helpLink: 'Link de ajuda',
|
||||
about: 'Sobre',
|
||||
createdBy: 'Criado por ',
|
||||
nextStep: 'Próximo passo',
|
||||
addNextStep: 'Adicionar o próximo bloco neste fluxo de trabalho',
|
||||
selectNextStep: 'Selecionar próximo bloco',
|
||||
runThisStep: 'Executar este passo',
|
||||
checklist: 'Lista de verificação',
|
||||
checklistTip: 'Certifique-se de resolver todos os problemas antes de publicar',
|
||||
checklistResolved: 'Todos os problemas estão resolvidos',
|
||||
organizeBlocks: 'Organizar blocos',
|
||||
change: 'Mudar',
|
||||
},
|
||||
nodes: {
|
||||
common: {
|
||||
outputVars: 'Output Variables',
|
||||
insertVarTip: 'Insert Variable',
|
||||
},
|
||||
start: {
|
||||
required: 'required',
|
||||
inputField: 'Input Field',
|
||||
builtInVar: 'Built-in Variables',
|
||||
outputVars: {
|
||||
query: 'User input',
|
||||
memories: {
|
||||
des: 'Conversation history',
|
||||
type: 'message type',
|
||||
content: 'message content',
|
||||
},
|
||||
files: 'File list',
|
||||
outputVars: 'Variáveis de saída',
|
||||
insertVarTip: 'Pressione a tecla \'/\' para inserir',
|
||||
memory: {
|
||||
memory: 'Memória',
|
||||
memoryTip: 'Configurações de memória do chat',
|
||||
windowSize: 'Tamanho da janela',
|
||||
conversationRoleName: 'Nome do papel na conversa',
|
||||
user: 'Prefixo do usuário',
|
||||
assistant: 'Prefixo do assistente',
|
||||
},
|
||||
memories: {
|
||||
title: 'Memórias',
|
||||
tip: 'Memória do chat',
|
||||
builtIn: 'Incorporada',
|
||||
},
|
||||
},
|
||||
start: {
|
||||
required: 'obrigatório',
|
||||
inputField: 'Campo de entrada',
|
||||
builtInVar: 'Variáveis incorporadas',
|
||||
outputVars: {
|
||||
query: 'Entrada do usuário',
|
||||
memories: {
|
||||
des: 'Histórico da conversa',
|
||||
type: 'Tipo de mensagem',
|
||||
content: 'Conteúdo da mensagem',
|
||||
},
|
||||
files: 'Lista de arquivos',
|
||||
},
|
||||
noVarTip: 'Defina as entradas que podem ser usadas no fluxo de trabalho',
|
||||
},
|
||||
end: {
|
||||
outputs: 'Outputs',
|
||||
outputs: 'Saídas',
|
||||
output: {
|
||||
type: 'Tipo de saída',
|
||||
variable: 'Variável de saída',
|
||||
},
|
||||
type: {
|
||||
'none': 'None',
|
||||
'plain-text': 'Plain Text',
|
||||
'structured': 'Structured',
|
||||
'none': 'Nenhum',
|
||||
'plain-text': 'Texto simples',
|
||||
'structured': 'Estruturado',
|
||||
},
|
||||
},
|
||||
answer: {
|
||||
answer: 'Answer',
|
||||
inputVars: 'Input Variables',
|
||||
answer: 'Resposta',
|
||||
outputVars: 'Variáveis de saída',
|
||||
},
|
||||
llm: {
|
||||
model: 'model',
|
||||
variables: 'variables',
|
||||
context: 'context',
|
||||
model: 'modelo',
|
||||
variables: 'variáveis',
|
||||
context: 'contexto',
|
||||
contextTooltip: 'Você pode importar conhecimento como contexto',
|
||||
notSetContextInPromptTip: 'Para habilitar o recurso de contexto, preencha a variável de contexto em PROMPT.',
|
||||
prompt: 'prompt',
|
||||
vision: 'vision',
|
||||
roleDescription: {
|
||||
system: 'Dar instruções de alto nível para a conversa',
|
||||
user: 'Fornecer instruções, consultas ou qualquer entrada baseada em texto para o modelo',
|
||||
assistant: 'Respostas do modelo com base nas mensagens do usuário',
|
||||
},
|
||||
addMessage: 'Adicionar mensagem',
|
||||
vision: 'visão',
|
||||
files: 'Arquivos',
|
||||
resolution: {
|
||||
name: 'Resolução',
|
||||
high: 'Alta',
|
||||
low: 'Baixa',
|
||||
},
|
||||
outputVars: {
|
||||
output: 'Generate content',
|
||||
usage: 'Model Usage Information',
|
||||
output: 'Gerar conteúdo',
|
||||
usage: 'Informações de uso do modelo',
|
||||
},
|
||||
singleRun: {
|
||||
variable: 'Variável',
|
||||
},
|
||||
},
|
||||
knowledgeRetrieval: {
|
||||
queryVariable: 'Variável de consulta',
|
||||
knowledge: 'Conhecimento',
|
||||
outputVars: {
|
||||
output: 'Dados segmentados de recuperação',
|
||||
content: 'Conteúdo segmentado',
|
||||
title: 'Título segmentado',
|
||||
icon: 'Ícone segmentado',
|
||||
url: 'URL segmentada',
|
||||
metadata: 'Outros metadados',
|
||||
},
|
||||
},
|
||||
http: {
|
||||
inputVars: 'Input Variables',
|
||||
inputVars: 'Variáveis de entrada',
|
||||
api: 'API',
|
||||
headers: 'Headers',
|
||||
params: 'Params',
|
||||
body: 'Body',
|
||||
apiPlaceholder: 'Insira o URL, digite \'/\' para inserir a variável',
|
||||
notStartWithHttp: 'A API deve começar com http:// ou https://',
|
||||
key: 'Chave',
|
||||
value: 'Valor',
|
||||
bulkEdit: 'Editar em massa',
|
||||
keyValueEdit: 'Edição de chave-valor',
|
||||
headers: 'Cabeçalhos',
|
||||
params: 'Parâmetros',
|
||||
body: 'Corpo',
|
||||
outputVars: {
|
||||
body: 'Response Content',
|
||||
statusCode: 'Response Status Code',
|
||||
headers: 'Response Header List JSON',
|
||||
body: 'Conteúdo da resposta',
|
||||
statusCode: 'Código de status da resposta',
|
||||
headers: 'Lista de cabeçalhos de resposta JSON',
|
||||
files: 'Lista de arquivos',
|
||||
},
|
||||
authorization: {
|
||||
'authorization': 'Autorização',
|
||||
'authorizationType': 'Tipo de autorização',
|
||||
'no-auth': 'Nenhum',
|
||||
'api-key': 'Chave da API',
|
||||
'auth-type': 'Tipo de autenticação',
|
||||
'basic': 'Básica',
|
||||
'bearer': 'Bearer',
|
||||
'custom': 'Personalizada',
|
||||
'api-key-title': 'Chave da API',
|
||||
'header': 'Cabeçalho',
|
||||
},
|
||||
insertVarPlaceholder: 'digite \'/\' para inserir variável',
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Input Variables',
|
||||
outputVars: 'Output Variables',
|
||||
inputVars: 'Variáveis de entrada',
|
||||
outputVars: 'Variáveis de saída',
|
||||
},
|
||||
templateTransform: {
|
||||
inputVars: 'Input Variables',
|
||||
code: 'Code',
|
||||
codeSupportTip: 'Only supports Jinja2',
|
||||
inputVars: 'Variáveis de entrada',
|
||||
code: 'Código',
|
||||
codeSupportTip: 'Suporta apenas Jinja2',
|
||||
outputVars: {
|
||||
output: 'Transformed content',
|
||||
output: 'Conteúdo transformado',
|
||||
},
|
||||
},
|
||||
ifElse: {
|
||||
conditions: 'Conditions',
|
||||
and: 'and',
|
||||
or: 'or',
|
||||
if: 'Se',
|
||||
else: 'Senão',
|
||||
elseDescription: 'Usado para definir a lógica que deve ser executada quando a condição if não é atendida.',
|
||||
and: 'e',
|
||||
or: 'ou',
|
||||
operator: 'Operador',
|
||||
notSetVariable: 'Por favor, defina a variável primeiro',
|
||||
comparisonOperator: {
|
||||
'contains': 'contains',
|
||||
'not contains': 'not contains',
|
||||
'start with': 'start with',
|
||||
'end with': 'end with',
|
||||
'is': 'is',
|
||||
'is not': 'is not',
|
||||
'empty': 'empty',
|
||||
'not empty': 'not empty',
|
||||
'null': 'is null',
|
||||
'not null': 'is not null',
|
||||
'contains': 'contém',
|
||||
'not contains': 'não contém',
|
||||
'start with': 'começa com',
|
||||
'end with': 'termina com',
|
||||
'is': 'é',
|
||||
'is not': 'não é',
|
||||
'empty': 'vazio',
|
||||
'not empty': 'não está vazio',
|
||||
'null': 'nulo',
|
||||
'not null': 'não é nulo',
|
||||
},
|
||||
enterValue: 'Digite o valor',
|
||||
addCondition: 'Adicionar condição',
|
||||
conditionNotSetup: 'Condição NÃO configurada',
|
||||
},
|
||||
variableAssigner: {
|
||||
title: 'Assign variables',
|
||||
title: 'Atribuir variáveis',
|
||||
outputType: 'Tipo de saída',
|
||||
outputVarType: 'Tipo de variável de saída',
|
||||
varNotSet: 'Variável não definida',
|
||||
noVarTip: 'Adicione as variáveis a serem atribuídas',
|
||||
type: {
|
||||
string: 'String',
|
||||
number: 'Número',
|
||||
object: 'Objeto',
|
||||
array: 'Array',
|
||||
},
|
||||
outputVars: {
|
||||
output: 'Valor da variável atribuída',
|
||||
},
|
||||
},
|
||||
tool: {
|
||||
toAuthorize: 'Para autorizar',
|
||||
inputVars: 'Variáveis de entrada',
|
||||
outputVars: {
|
||||
text: 'conteúdo gerado pela ferramenta',
|
||||
files: {
|
||||
title: 'arquivos gerados pela ferramenta',
|
||||
type: 'Tipo de suporte. Agora apenas suporte a imagem',
|
||||
transfer_method: 'Método de transferência. O valor é remote_url ou local_file',
|
||||
url: 'URL da imagem',
|
||||
upload_file_id: 'ID de upload do arquivo',
|
||||
},
|
||||
},
|
||||
},
|
||||
questionClassifiers: {
|
||||
model: 'modelo',
|
||||
inputVars: 'Variáveis de entrada',
|
||||
class: 'Classe',
|
||||
classNamePlaceholder: 'Escreva o nome da classe',
|
||||
advancedSetting: 'Configuração avançada',
|
||||
topicName: 'Nome do tópico',
|
||||
topicPlaceholder: 'Escreva o nome do tópico',
|
||||
addClass: 'Adicionar classe',
|
||||
instruction: 'Instrução',
|
||||
instructionPlaceholder: 'Escreva sua instrução',
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
stopBy: 'Parado por {{user}}',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -260,19 +260,32 @@ const translation = {
|
||||
queryNoBeEmpty: 'Запит має бути встановлений у підказці', // Query must be set in the prompt
|
||||
},
|
||||
variableConig: {
|
||||
'addModalTitle': 'Налаштування поля', // Field settings
|
||||
'description': 'Налаштування для змінної {{varName}}', // Setting for variable {{varName}}
|
||||
'fieldType': 'Тип поля', // Field type
|
||||
'string': 'Короткий текст', // Short Text
|
||||
'text-input': 'Короткий текст', // Short Text
|
||||
'paragraph': 'Абзац', // Paragraph
|
||||
'select': 'Вибрати', // Select
|
||||
'notSet': 'Не налаштовано, спробуйте ввести {{input}} у префіксну підказку', // Not set, try typing {{input}} in the prefix prompt
|
||||
'stringTitle': 'Опції текстового поля форми', // Form text box options
|
||||
'maxLength': 'Максимальна довжина', // Max length
|
||||
'options': 'Опції', // Options
|
||||
'addOption': 'Додати опцію', // Add option
|
||||
'apiBasedVar': 'Змінна на основі API', // API-based Variable
|
||||
'addModalTitle': 'Додати Поле Введення',
|
||||
'editModalTitle': 'Редагувати Поле Введення',
|
||||
'description': 'Налаштування для змінної {{varName}}',
|
||||
'fieldType': 'Тип поля',
|
||||
'string': 'Короткий текст',
|
||||
'text-input': 'Короткий текст',
|
||||
'paragraph': 'Параграф',
|
||||
'select': 'Вибрати',
|
||||
'number': 'Число',
|
||||
'notSet': 'Не встановлено, спробуйте ввести {{input}} у префіксній підказці',
|
||||
'stringTitle': 'Параметри поля введення форми',
|
||||
'maxLength': 'Максимальна довжина',
|
||||
'options': 'Опції',
|
||||
'addOption': 'Додати опцію',
|
||||
'apiBasedVar': 'Змінна на основі API',
|
||||
'varName': 'Назва змінної',
|
||||
'labelName': 'Назва мітки',
|
||||
'inputPlaceholder': 'Будь ласка, введіть',
|
||||
'required': 'Обов\'язково',
|
||||
'errorMsg': {
|
||||
varNameRequired: 'Потрібно вказати назву змінної',
|
||||
labelNameRequired: 'Потрібно вказати назву мітки',
|
||||
varNameCanBeRepeat: 'Назва змінної не може повторюватися',
|
||||
atLeastOneOption: 'Потрібно щонайменше одну опцію',
|
||||
optionRepeat: 'Є повторні опції',
|
||||
},
|
||||
},
|
||||
vision: {
|
||||
name: 'Зображення', // Vision
|
||||
@@ -340,6 +353,7 @@ const translation = {
|
||||
result: 'Вихідний текст', // Output Text
|
||||
datasetConfig: {
|
||||
settingTitle: 'Налаштування пошуку', // Retrieval settings
|
||||
knowledgeTip: 'Клацніть кнопку “+”, щоб додати знання',
|
||||
retrieveOneWay: {
|
||||
title: 'Односторонній пошук', // N-to-1 retrieval
|
||||
description: 'На основі намірів користувача та описів Знань Агент самостійно вибирає найкращі Знання для запитів. Найкраще підходить для застосунків з окремими, обмеженими Знаннями.',
|
||||
|
||||
@@ -1,69 +1,83 @@
|
||||
const translation = {
|
||||
title: 'Журнали', // Logs
|
||||
description: 'Журнали фіксують стан роботи застосунку, включаючи введення користувача та відповіді ШІ.', // The logs record...
|
||||
dateTimeFormat: 'MM/DD/YYYY hh:mm A', // (Keep date format as is)
|
||||
title: 'Журнали',
|
||||
description: 'Журнали фіксують робочий статус додатка, включаючи введення користувачів та відповіді штучного інтелекту.',
|
||||
dateTimeFormat: 'MM/DD/YYYY hh:mm A',
|
||||
table: {
|
||||
header: {
|
||||
time: 'Час', // Time
|
||||
endUser: 'Кінцевий користувач', // End User
|
||||
input: 'Введення', // Input
|
||||
output: 'Виведення', // Output
|
||||
summary: 'Заголовок', // Title
|
||||
messageCount: 'Кількість повідомлень', // Message Count
|
||||
userRate: 'Оцінка користувача', // User Rate
|
||||
adminRate: 'Оцінка адміністратора', // Op. Rate
|
||||
time: 'Час',
|
||||
endUser: 'Кінцевий Користувач',
|
||||
input: 'Введення',
|
||||
output: 'Виведення',
|
||||
summary: 'Заголовок',
|
||||
messageCount: 'Кількість Повідомлень',
|
||||
userRate: 'Рейтинг Користувача',
|
||||
adminRate: 'Рейтинг Оператора',
|
||||
startTime: 'ЧАС ПОЧАТКУ',
|
||||
status: 'СТАТУС',
|
||||
runtime: 'ЧАС ВИКОНАННЯ',
|
||||
tokens: 'ТОКЕНИ',
|
||||
user: 'КІНЦЕВИЙ КОРИСТУВАЧ',
|
||||
version: 'ВЕРСІЯ',
|
||||
},
|
||||
pagination: {
|
||||
previous: 'Назад', // Prev
|
||||
next: 'Далі', // Next
|
||||
previous: 'Попередня',
|
||||
next: 'Наступна',
|
||||
},
|
||||
empty: {
|
||||
noChat: 'Ще немає розмови', // No conversation yet
|
||||
noOutput: 'Відповіді немає', // No output
|
||||
noChat: 'Ще немає розмов',
|
||||
noOutput: 'Немає виводу',
|
||||
element: {
|
||||
title: 'Хто-небудь тут є?', // Is anyone there?
|
||||
content: 'Спостерігайте й коментуйте взаємодію між кінцевими користувачами та програмами штучного інтелекту, щоб постійно покращувати точність ШІ. Ви можете спробувати <shareLink>поділитися</shareLink> або <testLink>тестувати</testLink> веб-програму самостійно, а потім повернутися на цю сторінку.',
|
||||
title: 'Хтось тут?',
|
||||
content: 'Спостерігайте та анотуйте взаємодії між кінцевими користувачами та додатками штучного інтелекту тут, щоб постійно покращувати точність штучного інтелекту. Ви можете спробувати <shareLink>поділитися</shareLink> або <testLink>протестувати</testLink> веб-додаток самостійно, а потім повернутися на цю сторінку.',
|
||||
},
|
||||
},
|
||||
},
|
||||
detail: {
|
||||
time: 'Час', // Time
|
||||
conversationId: 'Ідентифікатор розмови', // Conversation ID
|
||||
promptTemplate: 'Шаблон підказки', // Prompt Template
|
||||
promptTemplateBeforeChat: 'Шаблон підказки перед чатом · як системне повідомлення', // Prompt Template Before Chat...
|
||||
annotationTip: 'Покращення, позначені {{user}}', // Improvements Marked by {{user}}
|
||||
time: 'Час',
|
||||
conversationId: 'ID Розмови',
|
||||
promptTemplate: 'Шаблон Запитання',
|
||||
promptTemplateBeforeChat: 'Шаблон Запитання Перед Чатом · Як Системне Повідомлення',
|
||||
annotationTip: 'Покращення Позначені Користувачем {{user}}',
|
||||
timeConsuming: '',
|
||||
second: 'с', // s (seconds)
|
||||
tokenCost: 'Витрачені токени', // Token spent
|
||||
loading: 'Завантаження', // loading
|
||||
second: 'с',
|
||||
tokenCost: 'Витрати Токенів',
|
||||
loading: 'завантаження',
|
||||
operation: {
|
||||
like: 'Вподобати', // like
|
||||
dislike: 'Не вподобати', // dislike
|
||||
addAnnotation: 'Додати покращення', // Add Improvement
|
||||
editAnnotation: 'Редагувати покращення', // Edit Improvement
|
||||
annotationPlaceholder: 'Введіть очікувану відповідь, яку ви хочете, щоб відповів ШІ. ',
|
||||
like: 'подобається',
|
||||
dislike: 'не подобається',
|
||||
addAnnotation: 'Додати Покращення',
|
||||
editAnnotation: 'Редагувати Покращення',
|
||||
annotationPlaceholder: 'Введіть очікувану відповідь, яку ви хочете, щоб штучний інтелект повертав, це може бути використано для налаштування моделі та постійного покращення якості генерації тексту в майбутньому.',
|
||||
},
|
||||
variables: 'Змінні', // Variables
|
||||
uploadImages: 'Завантажені зображення', // Uploaded Images
|
||||
variables: 'Змінні',
|
||||
uploadImages: 'Завантажені Зображення',
|
||||
},
|
||||
filter: {
|
||||
period: {
|
||||
today: 'Сьогодні', // Today
|
||||
last7days: 'Останні 7 днів', // Last 7 Days
|
||||
last4weeks: 'Останні 4 тижні', // Last 4 weeks
|
||||
last3months: 'Останні 3 місяці', // Last 3 months
|
||||
last12months: 'Останні 12 місяців', // Last 12 months
|
||||
monthToDate: 'Місяць до дати', // Month to date
|
||||
quarterToDate: 'Квартал до дати', // Quarter to date
|
||||
yearToDate: 'Рік до дати', // Year to date
|
||||
allTime: 'Увесь час', // All time
|
||||
today: 'Сьогодні',
|
||||
last7days: 'Останні 7 днів',
|
||||
last4weeks: 'Останні 4 тижні',
|
||||
last3months: 'Останні 3 місяці',
|
||||
last12months: 'Останні 12 місяців',
|
||||
monthToDate: 'Місяць до сьогодні',
|
||||
quarterToDate: 'Квартал до сьогодні',
|
||||
yearToDate: 'Рік до сьогодні',
|
||||
allTime: 'За весь час',
|
||||
},
|
||||
annotation: {
|
||||
all: 'Усі', // All
|
||||
annotated: 'Анотовані покращення ({{count}} елементів)', // Annotated Improvements...
|
||||
not_annotated: 'Не анотований', // Not Annotated
|
||||
all: 'Всі',
|
||||
annotated: 'Покращення з Анотацією ({{count}} елементів)',
|
||||
not_annotated: 'Без Анотації',
|
||||
},
|
||||
},
|
||||
workflowTitle: 'Журнали Робочого Процесу',
|
||||
workflowSubtitle: 'Журнал зареєстрував роботу Автоматизації.',
|
||||
runDetail: {
|
||||
title: 'Журнал Розмови',
|
||||
workflowTitle: 'Деталі Журналу',
|
||||
},
|
||||
promptLog: 'Журнал Запитань',
|
||||
viewLog: 'Переглянути Журнал',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
||||
@@ -1,137 +1,141 @@
|
||||
const translation = {
|
||||
welcome: {
|
||||
firstStepTip: 'Щоб розпочати,',
|
||||
firstStepTip: 'Для початку,',
|
||||
enterKeyTip: 'введіть свій ключ API OpenAI нижче',
|
||||
getKeyTip: 'Отримайте свій ключ API з панелі керування OpenAI',
|
||||
placeholder: 'Ваш ключ API OpenAI (наприклад, sk-xxxx)',
|
||||
getKeyTip: 'Отримайте свій ключ API з панелі OpenAI',
|
||||
placeholder: 'Ваш ключ API OpenAI (напр. sk-xxxx)',
|
||||
},
|
||||
apiKeyInfo: {
|
||||
cloud: {
|
||||
trial: {
|
||||
title: 'Ви використовуєте пробну квоту {{providerName}}.', // You are using...
|
||||
description: 'Пробна квота надається для використання під час тестування. Перш ніж вичерпається пробна квота, будь ласка, налаштуйте свого постачальника моделі або придбайте додаткову квоту.',
|
||||
title: 'Ви використовуєте квоту пробного періоду {{providerName}}.',
|
||||
description: 'Квота пробного періоду надається для вашого тестувального використання. Перш ніж будуть вичерпані виклики квоти пробного періоду, налаштуйте свого власного постачальника моделей або придбайте додаткову квоту.',
|
||||
},
|
||||
exhausted: {
|
||||
title: 'Ваша пробна квота використана, будь ласка, налаштуйте свій APIKey.',
|
||||
description: 'Ваша пробна квота вичерпана. Будь ласка, налаштуйте свого постачальника моделі або придбайте додаткову квоту.',
|
||||
title: 'Вашу квоту пробного періоду вичерпано, налаштуйте свій ключ API.',
|
||||
description: 'Вашу квоту пробного періоду вичерпано. Налаштуйте свого власного постачальника моделей або придбайте додаткову квоту.',
|
||||
},
|
||||
},
|
||||
selfHost: {
|
||||
title: {
|
||||
row1: 'Щоб розпочати,',
|
||||
row2: 'спочатку налаштуйте свого постачальника моделі.',
|
||||
row1: 'Для початку,',
|
||||
row2: 'спочатку налаштуйте постачальника моделей.',
|
||||
},
|
||||
},
|
||||
callTimes: 'Кількість викликів', // Call times
|
||||
usedToken: 'Використано токенів', // Used token
|
||||
setAPIBtn: 'Перейти до налаштування постачальника моделі', // Go to setup model provider
|
||||
tryCloud: 'Або спробуйте хмарну версію Dify з безкоштовною квотою', // Or try the cloud version ...
|
||||
callTimes: 'Кількість викликів',
|
||||
usedToken: 'Використані токени',
|
||||
setAPIBtn: 'Перейти до налаштування постачальника моделей',
|
||||
tryCloud: 'Або спробуйте хмарну версію Dify з безкоштовним цитатою',
|
||||
},
|
||||
overview: {
|
||||
title: 'Огляд', // Overview
|
||||
title: 'Огляд',
|
||||
appInfo: {
|
||||
explanation: 'Готовий до використання AI WebApp',
|
||||
accessibleAddress: 'Публічна URL-адреса', // Public URL
|
||||
preview: 'Попередній перегляд', // Preview
|
||||
regenerate: 'Регенерувати', // Regenerate
|
||||
preUseReminder: 'Будь ласка, увімкніть WebApp, перш ніж продовжувати.', // Please enable WebApp...
|
||||
explanation: 'Готовий до використання веб-додаток зі штучним інтелектом',
|
||||
accessibleAddress: 'Публічний URL',
|
||||
preview: 'Попередній перегляд',
|
||||
regenerate: 'Відновити',
|
||||
preUseReminder: 'Будь ласка, активуйте веб-додаток перед продовженням.',
|
||||
settings: {
|
||||
entry: 'Налаштування', // Settings
|
||||
title: 'Налаштування веб-програми', // WebApp Settings
|
||||
webName: 'Ім’я веб-програми', // WebApp Name
|
||||
webDesc: 'Опис веб-програми', // WebApp Description
|
||||
webDescTip: 'Цей текст відображатиметься на стороні клієнта, надаючи базові вказівки щодо використання програми',
|
||||
webDescPlaceholder: 'Введіть опис WebApp',
|
||||
language: 'Мова', // Language
|
||||
entry: 'Налаштування',
|
||||
title: 'Налаштування веб-додатку',
|
||||
webName: 'Назва веб-додатку',
|
||||
webDesc: 'Опис веб-додатку',
|
||||
webDescTip: 'Цей текст буде відображений на клієнтському боці, надаючи базові вказівки щодо використання додатка',
|
||||
webDescPlaceholder: 'Введіть опис веб-додатку',
|
||||
language: 'Мова',
|
||||
more: {
|
||||
entry: 'Показати більше налаштувань', // Show more settings
|
||||
copyright: 'Авторські права', // Copyright
|
||||
copyRightPlaceholder: 'Введіть ім’я автора або організації',
|
||||
privacyPolicy: 'Політика конфіденційності', // Privacy Policy
|
||||
entry: 'Показати додаткові налаштування',
|
||||
copyright: 'Авторське право',
|
||||
copyRightPlaceholder: 'Введіть ім\'я автора або організації',
|
||||
privacyPolicy: 'Політика конфіденційності',
|
||||
privacyPolicyPlaceholder: 'Введіть посилання на політику конфіденційності',
|
||||
privacyPolicyTip: 'Допомагає відвідувачам зрозуміти, які дані збирає програма. Дивіться <privacyPolicyLink>Політику конфіденційності</privacyPolicyLink> Dify.',
|
||||
privacyPolicyTip: 'Допомагає відвідувачам зрозуміти дані, зібрані додатком, див. <privacyPolicyLink>Політику конфіденційності</privacyPolicyLink> Dify.',
|
||||
},
|
||||
},
|
||||
embedded: {
|
||||
entry: 'Вбудований', // Embedded
|
||||
title: 'Вбудувати на веб-сайт', // Embed on website
|
||||
explanation: 'Виберіть спосіб вбудувати чат-програму на свій веб-сайт',
|
||||
iframe: 'Щоб додати чат-програму будь-де на своєму веб-сайті, додайте цей iframe у свій код html.',
|
||||
scripts: 'Щоб додати програму чату у правий нижній кут свого веб-сайту, додайте цей код до свого html.',
|
||||
chromePlugin: 'Встановити розширення Dify Chatbot для Chrome',
|
||||
copied: 'Скопійовано', // Copied
|
||||
copy: 'Копіювати', // Copy
|
||||
entry: 'Вбудоване',
|
||||
title: 'Вбудувати на веб-сайт',
|
||||
explanation: 'Виберіть спосіб вбудування чат-додатка на ваш веб-сайт',
|
||||
iframe: 'Для додавання чат-додатка в будь-яке місце на вашому веб-сайті, додайте цей iframe до вашого HTML-коду.',
|
||||
scripts: 'Для додавання чат-додатка в правий нижній кут вашого веб-сайту додайте цей код до вашого HTML.',
|
||||
chromePlugin: 'Встановити розширення Chrome Dify Chatbot',
|
||||
copied: 'Скопійовано',
|
||||
copy: 'Скопіювати',
|
||||
},
|
||||
qrcode: {
|
||||
title: 'QR-код для спільного доступу',
|
||||
scan: 'Відсканувати програму спільного доступу',
|
||||
title: 'QR-код для обміну',
|
||||
scan: 'Сканувати та обмінюватися додатком',
|
||||
download: 'Завантажити QR-код',
|
||||
},
|
||||
customize: {
|
||||
way: 'спосіб', // way
|
||||
entry: 'Налаштувати', // Customize
|
||||
title: 'Налаштувати веб-додаток AI',
|
||||
explanation: 'Ви можете налаштувати зовнішній вигляд WebApp відповідно до вашого сценарію та стилю.',
|
||||
way: 'спосіб',
|
||||
entry: 'Налаштувати',
|
||||
title: 'Налаштування веб-додатку зі штучним інтелектом',
|
||||
explanation: 'Ви можете налаштувати інтерфейс користувача веб-додатка, щоб він відповідав вашим потребам у сценаріях та стилі.',
|
||||
way1: {
|
||||
name: 'Розгалужити код клієнта, змінити його та розгорнути у Vercel (рекомендовано)',
|
||||
step1: 'Розгалужити код клієнта та модифікувати його',
|
||||
step1Tip: 'Натисніть тут, щоб розгалужити вихідний код у свій обліковий запис GitHub і змінити код',
|
||||
step1Operation: 'Dify-WebClient',
|
||||
step2: 'Розгорнути у Vercel',
|
||||
step2Tip: 'Натисніть тут, щоб імпортувати репозиторій до Vercel та виконати розгортання',
|
||||
step2Operation: 'Імпортувати репозиторій',
|
||||
step3: 'Налаштувати змінні середовища',
|
||||
step3Tip: 'Додайте такі змінні середовища у Vercel',
|
||||
name: 'Склонуйте клієнтський код, відредагуйте його та розгорніть на Vercel (рекомендовано)',
|
||||
step1: 'Склонуйте клієнтський код та відредагуйте його',
|
||||
step1Tip: 'Натисніть тут, щоб склонувати вихідний код у ваш обліковий запис GitHub та відредагувати код',
|
||||
step1Operation: 'Клієнт-Web-Dify',
|
||||
step2: 'Розгорніть на Vercel',
|
||||
step2Tip: 'Натисніть тут, щоб імпортувати репозиторій у Vercel та розгорнути',
|
||||
step2Operation: 'Імпорт репозиторію',
|
||||
step3: 'Налаштуйте змінні середовища',
|
||||
step3Tip: 'Додайте наступні змінні середовища у Vercel',
|
||||
},
|
||||
way2: {
|
||||
name: 'Напишіть код на клієнтській стороні, щоб викликати API та розгорнути його на сервері',
|
||||
name: 'Напишіть клієнтський код для виклику API та розгорніть його на сервері',
|
||||
operation: 'Документація',
|
||||
},
|
||||
},
|
||||
},
|
||||
apiInfo: {
|
||||
title: 'API бекенд-сервісу', // Backend service API
|
||||
explanation: 'Легко інтегрується в вашу програму',
|
||||
accessibleAddress: 'Кінцевий ресурс сервісу API', // Service API Endpoint
|
||||
doc: 'API довідка', // API Reference
|
||||
title: 'API сервісу Backend',
|
||||
explanation: 'Легко інтегрований у вашу програму',
|
||||
accessibleAddress: 'Кінцева точка API сервісу',
|
||||
doc: 'Довідка з API',
|
||||
},
|
||||
status: {
|
||||
running: 'Працює', // In service
|
||||
disable: 'Вимкнути', // Disable
|
||||
running: 'У роботі',
|
||||
disable: 'Вимкнути',
|
||||
},
|
||||
},
|
||||
analysis: {
|
||||
title: 'Аналіз', // Analysis
|
||||
ms: 'мс', // ms
|
||||
tokenPS: 'Токен/с', // Token/s
|
||||
title: 'Аналіз',
|
||||
ms: 'мс',
|
||||
tokenPS: 'Токени/с',
|
||||
totalMessages: {
|
||||
title: 'Загалом повідомлень', // Total Messages
|
||||
explanation: 'Щоденна кількість взаємодій зі ШІ; виключено проектування запитань або налагодження.',
|
||||
title: 'Загальна кількість повідомлень',
|
||||
explanation: 'Щоденна кількість взаємодій з штучним інтелектом; інженерія/налагодження запитів виключено.',
|
||||
},
|
||||
activeUsers: {
|
||||
title: 'Активні користувачі', // Active Users
|
||||
explanation: 'Унікальні користувачі, які беруть участь у запитах/відповідях зі ШІ; виключено інженерію запитань або налагодження.',
|
||||
title: 'Активні користувачі',
|
||||
explanation: 'Унікальні користувачі, які взаємодіють з AI; інженерія/налагодження запитів виключено.',
|
||||
},
|
||||
tokenUsage: {
|
||||
title: 'Використання токенів', // Token Usage
|
||||
explanation: 'Відображає щоденне використання токенів мовної моделі для програми, корисно для контролю витрат.',
|
||||
consumed: 'Спожито', // Consumed
|
||||
title: 'Використання токенів',
|
||||
explanation: 'Відображає щоденне використання токенів мовної моделі для додатка, корисно для контролю витрат.',
|
||||
consumed: 'Спожито',
|
||||
},
|
||||
avgSessionInteractions: {
|
||||
title: 'Середня кількість взаємодій під час сеансу', // Avg. Session Interactions
|
||||
explanation: 'Кількість безперервних комунікацій між користувачем і ШІ; для програм, що базуються на розмовах.',
|
||||
title: 'Середня кількість взаємодій за сесію',
|
||||
explanation: 'Кількість продовжуючихся спілкувань користувача з AI; для програм, що базуються на розмові.',
|
||||
},
|
||||
avgUserInteractions: {
|
||||
title: 'Середня кількість взаємодій на користувача',
|
||||
explanation: 'Відображає щоденну частоту використання користувачами. Ця метрика відображає лояльність користувачів.',
|
||||
},
|
||||
userSatisfactionRate: {
|
||||
title: 'Рівень задоволеності користувачів',
|
||||
explanation: 'Кількість лайків на 1000 повідомлень. Це означає частку відповідей, якими користувачі дуже задоволені.',
|
||||
title: 'Показник задоволення користувача',
|
||||
explanation: 'Кількість лайків на 1000 повідомлень. Це вказує на те, наскільки задоволені користувачі відповідями.',
|
||||
},
|
||||
avgResponseTime: {
|
||||
title: 'Середній час відповіді',
|
||||
explanation: 'Час (мс) для обробки/відповіді ШІ; для текстових програм.',
|
||||
explanation: 'Час (мс) для обробки/відповіді AI; для текстових програм.',
|
||||
},
|
||||
tps: {
|
||||
title: 'Швидкість виведення токенів',
|
||||
explanation: 'Виміряйте продуктивність LLM. Підрахуйте швидкість виведення токенів LLM від початку запиту до завершення виведення.',
|
||||
explanation: 'Вимірює продуктивність LLM. Підраховує швидкість виведення токенів LLM від початку запиту до завершення виведення.',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user