mirror of https://github.com/langgenius/dify.git
Merge branch 'feat/mcp-06-18' into deploy/dev
This commit is contained in:
commit
79200d89d8
|
|
@ -11,7 +11,7 @@
|
|||
"nodeGypDependencies": true,
|
||||
"version": "lts"
|
||||
},
|
||||
"ghcr.io/devcontainers-contrib/features/npm-package:1": {
|
||||
"ghcr.io/devcontainers-extra/features/npm-package:1": {
|
||||
"package": "typescript",
|
||||
"version": "latest"
|
||||
},
|
||||
|
|
|
|||
10
README.md
10
README.md
|
|
@ -63,7 +63,7 @@ Dify is an open-source platform for developing LLM applications. Its intuitive i
|
|||
> - CPU >= 2 Core
|
||||
> - RAM >= 4 GiB
|
||||
|
||||
</br>
|
||||
<br/>
|
||||
|
||||
The easiest way to start the Dify server is through [Docker Compose](docker/docker-compose.yaml). Before running Dify with the following commands, make sure that [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) are installed on your machine:
|
||||
|
||||
|
|
@ -109,15 +109,15 @@ All of Dify's offerings come with corresponding APIs, so you could effortlessly
|
|||
|
||||
## Using Dify
|
||||
|
||||
- **Cloud </br>**
|
||||
- **Cloud <br/>**
|
||||
We host a [Dify Cloud](https://dify.ai) service for anyone to try with zero setup. It provides all the capabilities of the self-deployed version, and includes 200 free GPT-4 calls in the sandbox plan.
|
||||
|
||||
- **Self-hosting Dify Community Edition</br>**
|
||||
- **Self-hosting Dify Community Edition<br/>**
|
||||
Quickly get Dify running in your environment with this [starter guide](#quick-start).
|
||||
Use our [documentation](https://docs.dify.ai) for further references and more in-depth instructions.
|
||||
|
||||
- **Dify for enterprise / organizations</br>**
|
||||
We provide additional enterprise-centric features. [Log your questions for us through this chatbot](https://udify.app/chat/22L1zSxg6yW1cWQg) or [send us an email](mailto:business@dify.ai?subject=%5BGitHub%5DBusiness%20License%20Inquiry) to discuss enterprise needs. </br>
|
||||
- **Dify for enterprise / organizations<br/>**
|
||||
We provide additional enterprise-centric features. [Log your questions for us through this chatbot](https://udify.app/chat/22L1zSxg6yW1cWQg) or [send us an email](mailto:business@dify.ai?subject=%5BGitHub%5DBusiness%20License%20Inquiry) to discuss enterprise needs. <br/>
|
||||
|
||||
> 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.
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@
|
|||
1. If you need to handle and debug the async tasks (e.g. dataset importing and documents indexing), please start the worker service.
|
||||
|
||||
```bash
|
||||
uv run celery -A app.celery worker -P gevent -c 2 --loglevel INFO -Q dataset,generation,mail,ops_trace,app_deletion,plugin,workflow_storage,conversation
|
||||
uv run celery -A app.celery worker -P gevent -c 2 --loglevel INFO -Q dataset,priority_pipeline,pipeline,mail,ops_trace,app_deletion,plugin,workflow_storage,conversation
|
||||
```
|
||||
|
||||
Additionally, if you want to debug the celery scheduled tasks, you can run the following command in another terminal to start the beat service:
|
||||
|
|
|
|||
|
|
@ -14,10 +14,25 @@ from services.file_service import FileService
|
|||
|
||||
@files_ns.route("/<uuid:file_id>/image-preview")
|
||||
class ImagePreviewApi(Resource):
|
||||
"""
|
||||
Deprecated
|
||||
"""
|
||||
"""Deprecated endpoint for retrieving image previews."""
|
||||
|
||||
@files_ns.doc("get_image_preview")
|
||||
@files_ns.doc(description="Retrieve a signed image preview for a file")
|
||||
@files_ns.doc(
|
||||
params={
|
||||
"file_id": "ID of the file to preview",
|
||||
"timestamp": "Unix timestamp used in the signature",
|
||||
"nonce": "Random string used in the signature",
|
||||
"sign": "HMAC signature verifying the request",
|
||||
}
|
||||
)
|
||||
@files_ns.doc(
|
||||
responses={
|
||||
200: "Image preview returned successfully",
|
||||
400: "Missing or invalid signature parameters",
|
||||
415: "Unsupported file type",
|
||||
}
|
||||
)
|
||||
def get(self, file_id):
|
||||
file_id = str(file_id)
|
||||
|
||||
|
|
@ -43,6 +58,25 @@ class ImagePreviewApi(Resource):
|
|||
|
||||
@files_ns.route("/<uuid:file_id>/file-preview")
|
||||
class FilePreviewApi(Resource):
|
||||
@files_ns.doc("get_file_preview")
|
||||
@files_ns.doc(description="Download a file preview or attachment using signed parameters")
|
||||
@files_ns.doc(
|
||||
params={
|
||||
"file_id": "ID of the file to preview",
|
||||
"timestamp": "Unix timestamp used in the signature",
|
||||
"nonce": "Random string used in the signature",
|
||||
"sign": "HMAC signature verifying the request",
|
||||
"as_attachment": "Whether to download the file as an attachment",
|
||||
}
|
||||
)
|
||||
@files_ns.doc(
|
||||
responses={
|
||||
200: "File stream returned successfully",
|
||||
400: "Missing or invalid signature parameters",
|
||||
404: "File not found",
|
||||
415: "Unsupported file type",
|
||||
}
|
||||
)
|
||||
def get(self, file_id):
|
||||
file_id = str(file_id)
|
||||
|
||||
|
|
@ -101,6 +135,20 @@ class FilePreviewApi(Resource):
|
|||
|
||||
@files_ns.route("/workspaces/<uuid:workspace_id>/webapp-logo")
|
||||
class WorkspaceWebappLogoApi(Resource):
|
||||
@files_ns.doc("get_workspace_webapp_logo")
|
||||
@files_ns.doc(description="Fetch the custom webapp logo for a workspace")
|
||||
@files_ns.doc(
|
||||
params={
|
||||
"workspace_id": "Workspace identifier",
|
||||
}
|
||||
)
|
||||
@files_ns.doc(
|
||||
responses={
|
||||
200: "Logo returned successfully",
|
||||
404: "Webapp logo not configured",
|
||||
415: "Unsupported file type",
|
||||
}
|
||||
)
|
||||
def get(self, workspace_id):
|
||||
workspace_id = str(workspace_id)
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,26 @@ from extensions.ext_database import db as global_db
|
|||
|
||||
@files_ns.route("/tools/<uuid:file_id>.<string:extension>")
|
||||
class ToolFileApi(Resource):
|
||||
@files_ns.doc("get_tool_file")
|
||||
@files_ns.doc(description="Download a tool file by ID using signed parameters")
|
||||
@files_ns.doc(
|
||||
params={
|
||||
"file_id": "Tool file identifier",
|
||||
"extension": "Expected file extension",
|
||||
"timestamp": "Unix timestamp used in the signature",
|
||||
"nonce": "Random string used in the signature",
|
||||
"sign": "HMAC signature verifying the request",
|
||||
"as_attachment": "Whether to download the file as an attachment",
|
||||
}
|
||||
)
|
||||
@files_ns.doc(
|
||||
responses={
|
||||
200: "Tool file stream returned successfully",
|
||||
403: "Forbidden - invalid signature",
|
||||
404: "File not found",
|
||||
415: "Unsupported file type",
|
||||
}
|
||||
)
|
||||
def get(self, file_id, extension):
|
||||
file_id = str(file_id)
|
||||
|
||||
|
|
|
|||
|
|
@ -189,7 +189,9 @@ class MCPProviderEntity(BaseModel):
|
|||
masked_creds = self.masked_credentials()
|
||||
if masked_creds:
|
||||
response["authentication"] = masked_creds
|
||||
response["is_dynamic_registration"] = self.credentials.get("is_dynamic_registration", True)
|
||||
response["is_dynamic_registration"] = self.credentials.get("client_information", {}).get(
|
||||
"is_dynamic_registration", True
|
||||
)
|
||||
|
||||
return response
|
||||
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ class OpenSearchVector(BaseVector):
|
|||
logger.exception("Error deleting document: %s", error)
|
||||
|
||||
def delete(self):
|
||||
self._client.indices.delete(index=self._collection_name.lower())
|
||||
self._client.indices.delete(index=self._collection_name.lower(), ignore_unavailable=True)
|
||||
|
||||
def text_exists(self, id: str) -> bool:
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -726,9 +726,7 @@ class ToolManager:
|
|||
if "mcp" in filters:
|
||||
with Session(db.engine) as session:
|
||||
mcp_service = MCPToolManageService(session=session)
|
||||
mcp_providers = mcp_service.list_providers(
|
||||
tenant_id=tenant_id, for_list=True, include_sensitive=False
|
||||
)
|
||||
mcp_providers = mcp_service.list_providers(tenant_id=tenant_id, for_list=True)
|
||||
for mcp_provider in mcp_providers:
|
||||
result_providers[f"mcp_provider.{mcp_provider.name}"] = mcp_provider
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ VARIABLE_TO_PARAMETER_TYPE_MAPPING = {
|
|||
VariableEntityType.PARAGRAPH: ToolParameter.ToolParameterType.STRING,
|
||||
VariableEntityType.SELECT: ToolParameter.ToolParameterType.SELECT,
|
||||
VariableEntityType.NUMBER: ToolParameter.ToolParameterType.NUMBER,
|
||||
VariableEntityType.CHECKBOX: ToolParameter.ToolParameterType.BOOLEAN,
|
||||
VariableEntityType.FILE: ToolParameter.ToolParameterType.FILE,
|
||||
VariableEntityType.FILE_LIST: ToolParameter.ToolParameterType.FILES,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ from core.workflow.graph_events import (
|
|||
NodeRunLoopStartedEvent,
|
||||
NodeRunLoopSucceededEvent,
|
||||
NodeRunPauseRequestedEvent,
|
||||
NodeRunRetrieverResourceEvent,
|
||||
NodeRunRetryEvent,
|
||||
NodeRunStartedEvent,
|
||||
NodeRunStreamChunkEvent,
|
||||
|
|
@ -112,6 +113,7 @@ class EventHandler:
|
|||
@_dispatch.register(NodeRunLoopSucceededEvent)
|
||||
@_dispatch.register(NodeRunLoopFailedEvent)
|
||||
@_dispatch.register(NodeRunAgentLogEvent)
|
||||
@_dispatch.register(NodeRunRetrieverResourceEvent)
|
||||
def _(self, event: GraphNodeEventBase) -> None:
|
||||
self._event_collector.collect(event)
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ if [[ "${MODE}" == "worker" ]]; then
|
|||
|
||||
exec celery -A celery_entrypoint.celery worker -P ${CELERY_WORKER_CLASS:-gevent} $CONCURRENCY_OPTION \
|
||||
--max-tasks-per-child ${MAX_TASKS_PER_CHILD:-50} --loglevel ${LOG_LEVEL:-INFO} \
|
||||
-Q ${CELERY_QUEUES:-dataset,pipeline,mail,ops_trace,app_deletion,plugin,workflow_storage,conversation} \
|
||||
-Q ${CELERY_QUEUES:-dataset,priority_pipeline,pipeline,mail,ops_trace,app_deletion,plugin,workflow_storage,conversation} \
|
||||
--prefetch-multiplier=1
|
||||
|
||||
elif [[ "${MODE}" == "beat" ]]; then
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ dev = [
|
|||
"pytest-cov~=4.1.0",
|
||||
"pytest-env~=1.1.3",
|
||||
"pytest-mock~=3.14.0",
|
||||
"testcontainers~=4.10.0",
|
||||
"testcontainers~=4.13.2",
|
||||
"types-aiofiles~=24.1.0",
|
||||
"types-beautifulsoup4~=4.12.0",
|
||||
"types-cachetools~=5.5.0",
|
||||
|
|
|
|||
|
|
@ -716,7 +716,7 @@ class MCPToolManageService:
|
|||
}
|
||||
secret_fields = []
|
||||
if client_secret is not None:
|
||||
credentials_data["encrypted_client_secret"] = encrypter.encrypt_token(tenant_id, client_secret)
|
||||
credentials_data["encrypted_client_secret"] = client_secret
|
||||
secret_fields = ["encrypted_client_secret"]
|
||||
client_info = self._encrypt_dict_fields(credentials_data, secret_fields, tenant_id)
|
||||
return json.dumps({"client_information": client_info})
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import logging
|
|||
from collections.abc import Mapping
|
||||
from typing import Any, Union
|
||||
|
||||
from pydantic import ValidationError
|
||||
from yarl import URL
|
||||
|
||||
from configs import dify_config
|
||||
|
|
@ -248,12 +249,13 @@ class ToolTransformService:
|
|||
provider_entity = db_provider.to_entity()
|
||||
|
||||
response = provider_entity.to_api_response(user_name=user_name, include_sensitive=include_sensitive)
|
||||
|
||||
try:
|
||||
mcp_tools = [MCPTool(**tool) for tool in json.loads(db_provider.tools)]
|
||||
except (ValidationError, json.JSONDecodeError):
|
||||
mcp_tools = []
|
||||
# Add additional fields specific to the transform
|
||||
response["id"] = db_provider.server_identifier if not for_list else db_provider.id
|
||||
response["tools"] = ToolTransformService.mcp_tool_to_user_tool(
|
||||
db_provider, [MCPTool(**tool) for tool in json.loads(db_provider.tools)], user_name=user_name
|
||||
)
|
||||
response["tools"] = ToolTransformService.mcp_tool_to_user_tool(db_provider, mcp_tools, user_name=user_name)
|
||||
response["server_identifier"] = db_provider.server_identifier
|
||||
|
||||
# Convert configuration dict to MCPConfiguration object
|
||||
|
|
|
|||
|
|
@ -182,6 +182,28 @@ class TestOpenSearchVector:
|
|||
assert len(ids) == 1
|
||||
assert ids[0] == "mock_id"
|
||||
|
||||
def test_delete_nonexistent_index(self):
|
||||
"""Test deleting a non-existent index."""
|
||||
# Create a vector instance with a non-existent collection name
|
||||
self.vector._client.indices.exists.return_value = False
|
||||
|
||||
# Should not raise an exception
|
||||
self.vector.delete()
|
||||
|
||||
# Verify that exists was called but delete was not
|
||||
self.vector._client.indices.exists.assert_called_once_with(index=self.collection_name.lower())
|
||||
self.vector._client.indices.delete.assert_not_called()
|
||||
|
||||
def test_delete_existing_index(self):
|
||||
"""Test deleting an existing index."""
|
||||
self.vector._client.indices.exists.return_value = True
|
||||
|
||||
self.vector.delete()
|
||||
|
||||
# Verify both exists and delete were called
|
||||
self.vector._client.indices.exists.assert_called_once_with(index=self.collection_name.lower())
|
||||
self.vector._client.indices.delete.assert_called_once_with(index=self.collection_name.lower())
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("setup_mock_redis")
|
||||
class TestOpenSearchVectorWithRedis:
|
||||
|
|
|
|||
|
|
@ -1603,7 +1603,7 @@ dev = [
|
|||
{ name = "ruff", specifier = "~=0.14.0" },
|
||||
{ name = "scipy-stubs", specifier = ">=1.15.3.0" },
|
||||
{ name = "sseclient-py", specifier = ">=1.8.0" },
|
||||
{ name = "testcontainers", specifier = "~=4.10.0" },
|
||||
{ name = "testcontainers", specifier = "~=4.13.2" },
|
||||
{ name = "ty", specifier = "~=0.0.1a19" },
|
||||
{ name = "types-aiofiles", specifier = "~=24.1.0" },
|
||||
{ name = "types-beautifulsoup4", specifier = "~=4.12.0" },
|
||||
|
|
@ -5969,7 +5969,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "testcontainers"
|
||||
version = "4.10.0"
|
||||
version = "4.13.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "docker" },
|
||||
|
|
@ -5978,9 +5978,9 @@ dependencies = [
|
|||
{ name = "urllib3" },
|
||||
{ name = "wrapt" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a1/49/9c618aff1c50121d183cdfbc3a4a5cf2727a2cde1893efe6ca55c7009196/testcontainers-4.10.0.tar.gz", hash = "sha256:03f85c3e505d8b4edeb192c72a961cebbcba0dd94344ae778b4a159cb6dcf8d3", size = 63327, upload-time = "2025-04-02T16:13:27.582Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/18/51/edac83edab339d8b4dce9a7b659163afb1ea7e011bfed1d5573d495a4485/testcontainers-4.13.2.tar.gz", hash = "sha256:2315f1e21b059427a9d11e8921f85fef322fbe0d50749bcca4eaa11271708ba4", size = 78692, upload-time = "2025-10-07T21:53:07.531Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/0a/824b0c1ecf224802125279c3effff2e25ed785ed046e67da6e53d928de4c/testcontainers-4.10.0-py3-none-any.whl", hash = "sha256:31ed1a81238c7e131a2a29df6db8f23717d892b592fa5a1977fd0dcd0c23fc23", size = 107414, upload-time = "2025-04-02T16:13:25.785Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/5e/73aa94770f1df0595364aed526f31d54440db5492911e2857318ed326e51/testcontainers-4.13.2-py3-none-any.whl", hash = "sha256:0209baf8f4274b568cde95bef2cadf7b1d33b375321f793790462e235cd684ee", size = 124771, upload-time = "2025-10-07T21:53:05.937Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -206,6 +206,10 @@ ENABLE_WEBSITE_JINAREADER=true
|
|||
ENABLE_WEBSITE_FIRECRAWL=true
|
||||
ENABLE_WEBSITE_WATERCRAWL=true
|
||||
|
||||
# Enable inline LaTeX rendering with single dollar signs ($...$) in the web frontend
|
||||
# Default is false for security reasons to prevent conflicts with regular text
|
||||
NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX=false
|
||||
|
||||
# ------------------------------
|
||||
# Database Configuration
|
||||
# The database uses PostgreSQL. Please use the public schema.
|
||||
|
|
@ -321,7 +325,7 @@ REDIS_CLUSTERS_PASSWORD=
|
|||
# Celery Configuration
|
||||
# ------------------------------
|
||||
|
||||
# Use standalone redis as the broker, and redis db 1 for celery broker. (redis_username is usually set by defualt as empty)
|
||||
# Use standalone redis as the broker, and redis db 1 for celery broker. (redis_username is usually set by default as empty)
|
||||
# Format as follows: `redis://<redis_username>:<redis_password>@<redis_host>:<redis_port>/<redis_database>`.
|
||||
# Example: redis://:difyai123456@redis:6379/1
|
||||
# If use Redis Sentinel, format as follows: `sentinel://<redis_username>:<redis_password>@<sentinel_host1>:<sentinel_port>/<redis_database>`
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ x-shared-env: &shared-api-worker-env
|
|||
ENABLE_WEBSITE_JINAREADER: ${ENABLE_WEBSITE_JINAREADER:-true}
|
||||
ENABLE_WEBSITE_FIRECRAWL: ${ENABLE_WEBSITE_FIRECRAWL:-true}
|
||||
ENABLE_WEBSITE_WATERCRAWL: ${ENABLE_WEBSITE_WATERCRAWL:-true}
|
||||
NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX: ${NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX:-false}
|
||||
DB_USERNAME: ${DB_USERNAME:-postgres}
|
||||
DB_PASSWORD: ${DB_PASSWORD:-difyai123456}
|
||||
DB_HOST: ${DB_HOST:-db}
|
||||
|
|
|
|||
|
|
@ -61,5 +61,9 @@ NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER=true
|
|||
NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL=true
|
||||
NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL=true
|
||||
|
||||
# Enable inline LaTeX rendering with single dollar signs ($...$)
|
||||
# Default is false for security reasons to prevent conflicts with regular text
|
||||
NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX=false
|
||||
|
||||
# The maximum number of tree node depth for workflow
|
||||
NEXT_PUBLIC_MAX_TREE_DEPTH=50
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ import React from 'react'
|
|||
import Main from '@/app/components/explore/installed-app'
|
||||
|
||||
export type IInstalledAppProps = {
|
||||
params: {
|
||||
params?: Promise<{
|
||||
appId: string
|
||||
}
|
||||
}>
|
||||
}
|
||||
|
||||
// Using Next.js page convention for async server components
|
||||
async function InstalledApp({ params }: IInstalledAppProps) {
|
||||
const appId = (await params).appId
|
||||
const { appId } = await (params ?? Promise.reject(new Error('Missing params')))
|
||||
return (
|
||||
<Main id={appId} />
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
export const OAUTH_AUTHORIZE_PENDING_KEY = 'oauth_authorize_pending'
|
||||
export const REDIRECT_URL_KEY = 'oauth_redirect_url'
|
||||
export const OAUTH_AUTHORIZE_PENDING_TTL = 60 * 3
|
||||
|
|
@ -19,11 +19,11 @@ import {
|
|||
} from '@remixicon/react'
|
||||
import dayjs from 'dayjs'
|
||||
import { useIsLogin } from '@/service/use-common'
|
||||
|
||||
export const OAUTH_AUTHORIZE_PENDING_KEY = 'oauth_authorize_pending'
|
||||
export const REDIRECT_URL_KEY = 'oauth_redirect_url'
|
||||
|
||||
const OAUTH_AUTHORIZE_PENDING_TTL = 60 * 3
|
||||
import {
|
||||
OAUTH_AUTHORIZE_PENDING_KEY,
|
||||
OAUTH_AUTHORIZE_PENDING_TTL,
|
||||
REDIRECT_URL_KEY,
|
||||
} from './constants'
|
||||
|
||||
function setItemWithExpiry(key: string, value: string, ttl: number) {
|
||||
const item = {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ import { appDefaultIconBackground } from '@/config'
|
|||
import type { PublishWorkflowParams } from '@/types/workflow'
|
||||
import { useAppWhiteListSubjects, useGetUserCanAccessApp } from '@/service/access-control'
|
||||
import { AccessMode } from '@/models/access-control'
|
||||
import { fetchAppDetail } from '@/service/apps'
|
||||
import { fetchAppDetailDirect } from '@/service/apps'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
|
||||
import { webSocketClient } from '@/app/components/workflow/collaboration/core/websocket-manager'
|
||||
|
|
@ -182,11 +182,16 @@ const AppPublisher = ({
|
|||
}
|
||||
}, [appDetail?.id])
|
||||
|
||||
const handleAccessControlUpdate = useCallback(() => {
|
||||
fetchAppDetail({ url: '/apps', id: appDetail!.id }).then((res) => {
|
||||
const handleAccessControlUpdate = useCallback(async () => {
|
||||
if (!appDetail)
|
||||
return
|
||||
try {
|
||||
const res = await fetchAppDetailDirect({ url: '/apps', id: appDetail.id })
|
||||
setAppDetail(res)
|
||||
}
|
||||
finally {
|
||||
setShowAppAccessControl(false)
|
||||
})
|
||||
}
|
||||
}, [appDetail, setAppDetail])
|
||||
|
||||
const [embeddingModalOpen, setEmbeddingModalOpen] = useState(false)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ import { TransferMethod } from '@/app/components/base/chat/types'
|
|||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useFeatures } from '@/app/components/base/features/hooks'
|
||||
import { noop } from 'lodash-es'
|
||||
import { cloneDeep, noop } from 'lodash-es'
|
||||
import { DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config'
|
||||
|
||||
type TextGenerationItemProps = {
|
||||
modelAndParameter: ModelAndParameter
|
||||
|
|
@ -50,8 +51,8 @@ const TextGenerationItem: FC<TextGenerationItemProps> = ({
|
|||
const config: TextGenerationConfig = {
|
||||
pre_prompt: !isAdvancedMode ? modelConfig.configs.prompt_template : '',
|
||||
prompt_type: promptMode,
|
||||
chat_prompt_config: isAdvancedMode ? chatPromptConfig : {},
|
||||
completion_prompt_config: isAdvancedMode ? completionPromptConfig : {},
|
||||
chat_prompt_config: isAdvancedMode ? chatPromptConfig : cloneDeep(DEFAULT_CHAT_PROMPT_CONFIG),
|
||||
completion_prompt_config: isAdvancedMode ? completionPromptConfig : cloneDeep(DEFAULT_COMPLETION_PROMPT_CONFIG),
|
||||
user_input_form: promptVariablesToUserInputsForm(modelConfig.configs.prompt_variables),
|
||||
dataset_query_variable: contextVar || '',
|
||||
// features
|
||||
|
|
@ -74,6 +75,7 @@ const TextGenerationItem: FC<TextGenerationItemProps> = ({
|
|||
datasets: [...postDatasets],
|
||||
} as any,
|
||||
},
|
||||
system_parameters: modelConfig.system_parameters,
|
||||
}
|
||||
const {
|
||||
completion,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
import Chat from '@/app/components/base/chat/chat'
|
||||
import { useChat } from '@/app/components/base/chat/chat/hooks'
|
||||
import { useDebugConfigurationContext } from '@/context/debug-configuration'
|
||||
import type { ChatConfig, ChatItem, ChatItemInTree, OnSend } from '@/app/components/base/chat/types'
|
||||
import type { ChatConfig, ChatItem, OnSend } from '@/app/components/base/chat/types'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import {
|
||||
fetchConversationMessages,
|
||||
|
|
@ -127,7 +127,7 @@ const DebugWithSingleModel = (
|
|||
)
|
||||
}, [appId, chatList, checkCanSend, completionParams, config, handleSend, inputs, modelConfig.mode, modelConfig.model_id, modelConfig.provider, textGenerationModelList])
|
||||
|
||||
const doRegenerate = useCallback((chatItem: ChatItemInTree, editedQuestion?: { message: string, files?: FileEntity[] }) => {
|
||||
const doRegenerate = useCallback((chatItem: ChatItem, editedQuestion?: { message: string, files?: FileEntity[] }) => {
|
||||
const question = editedQuestion ? chatItem : chatList.find(item => item.id === chatItem.parentMessageId)!
|
||||
const parentAnswer = chatList.find(item => item.id === question.parentMessageId)
|
||||
doSend(editedQuestion ? editedQuestion.message : question.content,
|
||||
|
|
|
|||
|
|
@ -12,12 +12,15 @@ import type {
|
|||
ChatConfig,
|
||||
ChatItem,
|
||||
} from '@/app/components/base/chat/types'
|
||||
import cloneDeep from 'lodash-es/cloneDeep'
|
||||
import {
|
||||
AgentStrategy,
|
||||
} from '@/types/app'
|
||||
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
|
||||
import { promptVariablesToUserInputsForm } from '@/utils/model-config'
|
||||
import { useDebugConfigurationContext } from '@/context/debug-configuration'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import { DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config'
|
||||
|
||||
export const useDebugWithSingleOrMultipleModel = (appId: string) => {
|
||||
const localeDebugWithSingleOrMultipleModelConfigs = localStorage.getItem('app-debug-with-single-or-multiple-models')
|
||||
|
|
@ -95,16 +98,14 @@ export const useConfigFromDebugContext = () => {
|
|||
const config: ChatConfig = {
|
||||
pre_prompt: !isAdvancedMode ? modelConfig.configs.prompt_template : '',
|
||||
prompt_type: promptMode,
|
||||
chat_prompt_config: isAdvancedMode ? chatPromptConfig : {},
|
||||
completion_prompt_config: isAdvancedMode ? completionPromptConfig : {},
|
||||
chat_prompt_config: isAdvancedMode ? chatPromptConfig : cloneDeep(DEFAULT_CHAT_PROMPT_CONFIG),
|
||||
completion_prompt_config: isAdvancedMode ? completionPromptConfig : cloneDeep(DEFAULT_COMPLETION_PROMPT_CONFIG),
|
||||
user_input_form: promptVariablesToUserInputsForm(modelConfig.configs.prompt_variables),
|
||||
dataset_query_variable: contextVar || '',
|
||||
opening_statement: introduction,
|
||||
more_like_this: {
|
||||
enabled: false,
|
||||
},
|
||||
more_like_this: modelConfig.more_like_this ?? { enabled: false },
|
||||
suggested_questions: openingSuggestedQuestions,
|
||||
suggested_questions_after_answer: suggestedQuestionsAfterAnswerConfig,
|
||||
suggested_questions_after_answer: suggestedQuestionsAfterAnswerConfig ?? { enabled: false },
|
||||
text_to_speech: textToSpeechConfig,
|
||||
speech_to_text: speechToTextConfig,
|
||||
retriever_resource: citationConfig,
|
||||
|
|
@ -121,8 +122,13 @@ export const useConfigFromDebugContext = () => {
|
|||
},
|
||||
file_upload: {
|
||||
image: visionConfig,
|
||||
allowed_file_upload_methods: visionConfig.transfer_methods ?? [],
|
||||
allowed_file_types: [SupportUploadFileTypes.image],
|
||||
max_length: visionConfig.number_limits ?? 0,
|
||||
number_limits: visionConfig.number_limits,
|
||||
},
|
||||
annotation_reply: annotationConfig,
|
||||
system_parameters: modelConfig.system_parameters,
|
||||
|
||||
supportAnnotation: true,
|
||||
appId,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import type { FC } from 'react'
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { produce, setAutoFreeze } from 'immer'
|
||||
import cloneDeep from 'lodash-es/cloneDeep'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import {
|
||||
RiAddLine,
|
||||
|
|
@ -36,7 +37,7 @@ import ActionButton, { ActionButtonState } from '@/app/components/base/action-bu
|
|||
import type { ModelConfig as BackendModelConfig, VisionFile, VisionSettings } from '@/types/app'
|
||||
import { formatBooleanInputs, promptVariablesToUserInputsForm } from '@/utils/model-config'
|
||||
import TextGeneration from '@/app/components/app/text-generate/item'
|
||||
import { IS_CE_EDITION } from '@/config'
|
||||
import { DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG, IS_CE_EDITION } from '@/config'
|
||||
import type { Inputs } from '@/models/debug'
|
||||
import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import { ModelFeatureEnum, ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
|
|
@ -91,6 +92,7 @@ const Debug: FC<IDebug> = ({
|
|||
completionParams,
|
||||
hasSetContextVar,
|
||||
datasetConfigs,
|
||||
externalDataToolsConfig,
|
||||
} = useContext(ConfigContext)
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
const { data: text2speechDefaultModel } = useDefaultModel(ModelTypeEnum.textEmbedding)
|
||||
|
|
@ -224,8 +226,8 @@ const Debug: FC<IDebug> = ({
|
|||
const postModelConfig: BackendModelConfig = {
|
||||
pre_prompt: !isAdvancedMode ? modelConfig.configs.prompt_template : '',
|
||||
prompt_type: promptMode,
|
||||
chat_prompt_config: {},
|
||||
completion_prompt_config: {},
|
||||
chat_prompt_config: isAdvancedMode ? chatPromptConfig : cloneDeep(DEFAULT_CHAT_PROMPT_CONFIG),
|
||||
completion_prompt_config: isAdvancedMode ? completionPromptConfig : cloneDeep(DEFAULT_COMPLETION_PROMPT_CONFIG),
|
||||
user_input_form: promptVariablesToUserInputsForm(modelConfig.configs.prompt_variables),
|
||||
dataset_query_variable: contextVar || '',
|
||||
dataset_configs: {
|
||||
|
|
@ -252,11 +254,8 @@ const Debug: FC<IDebug> = ({
|
|||
suggested_questions_after_answer: suggestedQuestionsAfterAnswerConfig,
|
||||
speech_to_text: speechToTextConfig,
|
||||
retriever_resource: citationConfig,
|
||||
}
|
||||
|
||||
if (isAdvancedMode) {
|
||||
postModelConfig.chat_prompt_config = chatPromptConfig
|
||||
postModelConfig.completion_prompt_config = completionPromptConfig
|
||||
system_parameters: modelConfig.system_parameters,
|
||||
external_data_tools: externalDataToolsConfig,
|
||||
}
|
||||
|
||||
const data: Record<string, any> = {
|
||||
|
|
|
|||
|
|
@ -36,14 +36,14 @@ import type {
|
|||
} from '@/models/debug'
|
||||
import type { ExternalDataTool } from '@/models/common'
|
||||
import type { DataSet } from '@/models/datasets'
|
||||
import type { ModelConfig as BackendModelConfig, VisionSettings } from '@/types/app'
|
||||
import type { ModelConfig as BackendModelConfig, UserInputFormItem, VisionSettings } from '@/types/app'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import Config from '@/app/components/app/configuration/config'
|
||||
import Debug from '@/app/components/app/configuration/debug'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import { ModelFeatureEnum, ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
import { fetchAppDetail, updateAppModelConfig } from '@/service/apps'
|
||||
import { fetchAppDetailDirect, updateAppModelConfig } from '@/service/apps'
|
||||
import { promptVariablesToUserInputsForm, userInputsFormToPromptVariables } from '@/utils/model-config'
|
||||
import { fetchDatasets } from '@/service/datasets'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
|
|
@ -186,6 +186,8 @@ const Configuration: FC = () => {
|
|||
prompt_template: '',
|
||||
prompt_variables: [] as PromptVariable[],
|
||||
},
|
||||
chat_prompt_config: clone(DEFAULT_CHAT_PROMPT_CONFIG),
|
||||
completion_prompt_config: clone(DEFAULT_COMPLETION_PROMPT_CONFIG),
|
||||
more_like_this: null,
|
||||
opening_statement: '',
|
||||
suggested_questions: [],
|
||||
|
|
@ -196,6 +198,14 @@ const Configuration: FC = () => {
|
|||
suggested_questions_after_answer: null,
|
||||
retriever_resource: null,
|
||||
annotation_reply: null,
|
||||
external_data_tools: [],
|
||||
system_parameters: {
|
||||
audio_file_size_limit: 0,
|
||||
file_size_limit: 0,
|
||||
image_file_size_limit: 0,
|
||||
video_file_size_limit: 0,
|
||||
workflow_file_upload_limit: 0,
|
||||
},
|
||||
dataSets: [],
|
||||
agentConfig: DEFAULT_AGENT_SETTING,
|
||||
})
|
||||
|
|
@ -543,169 +553,169 @@ const Configuration: FC = () => {
|
|||
})
|
||||
}
|
||||
setCollectionList(collectionList)
|
||||
fetchAppDetail({ url: '/apps', id: appId }).then(async (res: any) => {
|
||||
setMode(res.mode)
|
||||
const modelConfig = res.model_config
|
||||
const promptMode = modelConfig.prompt_type === PromptMode.advanced ? PromptMode.advanced : PromptMode.simple
|
||||
doSetPromptMode(promptMode)
|
||||
if (promptMode === PromptMode.advanced) {
|
||||
if (modelConfig.chat_prompt_config && modelConfig.chat_prompt_config.prompt.length > 0)
|
||||
setChatPromptConfig(modelConfig.chat_prompt_config)
|
||||
else
|
||||
setChatPromptConfig(clone(DEFAULT_CHAT_PROMPT_CONFIG))
|
||||
setCompletionPromptConfig(modelConfig.completion_prompt_config || clone(DEFAULT_COMPLETION_PROMPT_CONFIG) as any)
|
||||
setCanReturnToSimpleMode(false)
|
||||
}
|
||||
const res = await fetchAppDetailDirect({ url: '/apps', id: appId })
|
||||
setMode(res.mode)
|
||||
const modelConfig = res.model_config as BackendModelConfig
|
||||
const promptMode = modelConfig.prompt_type === PromptMode.advanced ? PromptMode.advanced : PromptMode.simple
|
||||
doSetPromptMode(promptMode)
|
||||
if (promptMode === PromptMode.advanced) {
|
||||
if (modelConfig.chat_prompt_config && modelConfig.chat_prompt_config.prompt.length > 0)
|
||||
setChatPromptConfig(modelConfig.chat_prompt_config)
|
||||
else
|
||||
setChatPromptConfig(clone(DEFAULT_CHAT_PROMPT_CONFIG))
|
||||
setCompletionPromptConfig(modelConfig.completion_prompt_config || clone(DEFAULT_COMPLETION_PROMPT_CONFIG) as any)
|
||||
setCanReturnToSimpleMode(false)
|
||||
}
|
||||
|
||||
const model = res.model_config.model
|
||||
const model = modelConfig.model
|
||||
|
||||
let datasets: any = null
|
||||
let datasets: any = null
|
||||
// old dataset struct
|
||||
if (modelConfig.agent_mode?.tools?.find(({ dataset }: any) => dataset?.enabled))
|
||||
datasets = modelConfig.agent_mode?.tools.filter(({ dataset }: any) => dataset?.enabled)
|
||||
if (modelConfig.agent_mode?.tools?.find(({ dataset }: any) => dataset?.enabled))
|
||||
datasets = modelConfig.agent_mode?.tools.filter(({ dataset }: any) => dataset?.enabled)
|
||||
// new dataset struct
|
||||
else if (modelConfig.dataset_configs.datasets?.datasets?.length > 0)
|
||||
datasets = modelConfig.dataset_configs?.datasets?.datasets
|
||||
else if (modelConfig.dataset_configs.datasets?.datasets?.length > 0)
|
||||
datasets = modelConfig.dataset_configs?.datasets?.datasets
|
||||
|
||||
if (dataSets && datasets?.length && datasets?.length > 0) {
|
||||
const { data: dataSetsWithDetail } = await fetchDatasets({ url: '/datasets', params: { page: 1, ids: datasets.map(({ dataset }: any) => dataset.id) } })
|
||||
datasets = dataSetsWithDetail
|
||||
setDataSets(datasets)
|
||||
}
|
||||
if (dataSets && datasets?.length && datasets?.length > 0) {
|
||||
const { data: dataSetsWithDetail } = await fetchDatasets({ url: '/datasets', params: { page: 1, ids: datasets.map(({ dataset }: any) => dataset.id) } })
|
||||
datasets = dataSetsWithDetail
|
||||
setDataSets(datasets)
|
||||
}
|
||||
|
||||
setIntroduction(modelConfig.opening_statement)
|
||||
setSuggestedQuestions(modelConfig.suggested_questions || [])
|
||||
if (modelConfig.more_like_this)
|
||||
setMoreLikeThisConfig(modelConfig.more_like_this)
|
||||
setIntroduction(modelConfig.opening_statement)
|
||||
setSuggestedQuestions(modelConfig.suggested_questions || [])
|
||||
if (modelConfig.more_like_this)
|
||||
setMoreLikeThisConfig(modelConfig.more_like_this)
|
||||
|
||||
if (modelConfig.suggested_questions_after_answer)
|
||||
setSuggestedQuestionsAfterAnswerConfig(modelConfig.suggested_questions_after_answer)
|
||||
if (modelConfig.suggested_questions_after_answer)
|
||||
setSuggestedQuestionsAfterAnswerConfig(modelConfig.suggested_questions_after_answer)
|
||||
|
||||
if (modelConfig.speech_to_text)
|
||||
setSpeechToTextConfig(modelConfig.speech_to_text)
|
||||
if (modelConfig.speech_to_text)
|
||||
setSpeechToTextConfig(modelConfig.speech_to_text)
|
||||
|
||||
if (modelConfig.text_to_speech)
|
||||
setTextToSpeechConfig(modelConfig.text_to_speech)
|
||||
if (modelConfig.text_to_speech)
|
||||
setTextToSpeechConfig(modelConfig.text_to_speech)
|
||||
|
||||
if (modelConfig.retriever_resource)
|
||||
setCitationConfig(modelConfig.retriever_resource)
|
||||
if (modelConfig.retriever_resource)
|
||||
setCitationConfig(modelConfig.retriever_resource)
|
||||
|
||||
if (modelConfig.annotation_reply) {
|
||||
let annotationConfig = modelConfig.annotation_reply
|
||||
if (modelConfig.annotation_reply.enabled) {
|
||||
annotationConfig = {
|
||||
...modelConfig.annotation_reply,
|
||||
embedding_model: {
|
||||
...modelConfig.annotation_reply.embedding_model,
|
||||
embedding_provider_name: correctModelProvider(modelConfig.annotation_reply.embedding_model.embedding_provider_name),
|
||||
},
|
||||
}
|
||||
if (modelConfig.annotation_reply) {
|
||||
let annotationConfig = modelConfig.annotation_reply
|
||||
if (modelConfig.annotation_reply.enabled) {
|
||||
annotationConfig = {
|
||||
...modelConfig.annotation_reply,
|
||||
embedding_model: {
|
||||
...modelConfig.annotation_reply.embedding_model,
|
||||
embedding_provider_name: correctModelProvider(modelConfig.annotation_reply.embedding_model.embedding_provider_name),
|
||||
},
|
||||
}
|
||||
setAnnotationConfig(annotationConfig, true)
|
||||
}
|
||||
setAnnotationConfig(annotationConfig, true)
|
||||
}
|
||||
|
||||
if (modelConfig.sensitive_word_avoidance)
|
||||
setModerationConfig(modelConfig.sensitive_word_avoidance)
|
||||
if (modelConfig.sensitive_word_avoidance)
|
||||
setModerationConfig(modelConfig.sensitive_word_avoidance)
|
||||
|
||||
if (modelConfig.external_data_tools)
|
||||
setExternalDataToolsConfig(modelConfig.external_data_tools)
|
||||
if (modelConfig.external_data_tools)
|
||||
setExternalDataToolsConfig(modelConfig.external_data_tools)
|
||||
|
||||
const config = {
|
||||
modelConfig: {
|
||||
provider: correctModelProvider(model.provider),
|
||||
model_id: model.name,
|
||||
mode: model.mode,
|
||||
configs: {
|
||||
prompt_template: modelConfig.pre_prompt || '',
|
||||
prompt_variables: userInputsFormToPromptVariables(
|
||||
[
|
||||
...modelConfig.user_input_form,
|
||||
...(
|
||||
modelConfig.external_data_tools?.length
|
||||
? modelConfig.external_data_tools.map((item: any) => {
|
||||
return {
|
||||
external_data_tool: {
|
||||
variable: item.variable as string,
|
||||
label: item.label as string,
|
||||
enabled: item.enabled,
|
||||
type: item.type as string,
|
||||
config: item.config,
|
||||
required: true,
|
||||
icon: item.icon,
|
||||
icon_background: item.icon_background,
|
||||
},
|
||||
}
|
||||
})
|
||||
: []
|
||||
),
|
||||
],
|
||||
modelConfig.dataset_query_variable,
|
||||
),
|
||||
},
|
||||
more_like_this: modelConfig.more_like_this,
|
||||
opening_statement: modelConfig.opening_statement,
|
||||
suggested_questions: modelConfig.suggested_questions,
|
||||
sensitive_word_avoidance: modelConfig.sensitive_word_avoidance,
|
||||
speech_to_text: modelConfig.speech_to_text,
|
||||
text_to_speech: modelConfig.text_to_speech,
|
||||
file_upload: modelConfig.file_upload,
|
||||
suggested_questions_after_answer: modelConfig.suggested_questions_after_answer,
|
||||
retriever_resource: modelConfig.retriever_resource,
|
||||
annotation_reply: modelConfig.annotation_reply,
|
||||
external_data_tools: modelConfig.external_data_tools,
|
||||
dataSets: datasets || [],
|
||||
agentConfig: res.mode === 'agent-chat' ? {
|
||||
max_iteration: DEFAULT_AGENT_SETTING.max_iteration,
|
||||
...modelConfig.agent_mode,
|
||||
const config: PublishConfig = {
|
||||
modelConfig: {
|
||||
provider: correctModelProvider(model.provider),
|
||||
model_id: model.name,
|
||||
mode: model.mode,
|
||||
configs: {
|
||||
prompt_template: modelConfig.pre_prompt || '',
|
||||
prompt_variables: userInputsFormToPromptVariables(
|
||||
([
|
||||
...modelConfig.user_input_form,
|
||||
...(
|
||||
modelConfig.external_data_tools?.length
|
||||
? modelConfig.external_data_tools.map((item: any) => {
|
||||
return {
|
||||
external_data_tool: {
|
||||
variable: item.variable as string,
|
||||
label: item.label as string,
|
||||
enabled: item.enabled,
|
||||
type: item.type as string,
|
||||
config: item.config,
|
||||
required: true,
|
||||
icon: item.icon,
|
||||
icon_background: item.icon_background,
|
||||
},
|
||||
}
|
||||
})
|
||||
: []
|
||||
),
|
||||
]) as unknown as UserInputFormItem[],
|
||||
modelConfig.dataset_query_variable,
|
||||
),
|
||||
},
|
||||
more_like_this: modelConfig.more_like_this ?? { enabled: false },
|
||||
opening_statement: modelConfig.opening_statement,
|
||||
suggested_questions: modelConfig.suggested_questions ?? [],
|
||||
sensitive_word_avoidance: modelConfig.sensitive_word_avoidance,
|
||||
speech_to_text: modelConfig.speech_to_text,
|
||||
text_to_speech: modelConfig.text_to_speech,
|
||||
file_upload: modelConfig.file_upload ?? null,
|
||||
suggested_questions_after_answer: modelConfig.suggested_questions_after_answer ?? { enabled: false },
|
||||
retriever_resource: modelConfig.retriever_resource,
|
||||
annotation_reply: modelConfig.annotation_reply ?? null,
|
||||
external_data_tools: modelConfig.external_data_tools ?? [],
|
||||
system_parameters: modelConfig.system_parameters,
|
||||
dataSets: datasets || [],
|
||||
agentConfig: res.mode === 'agent-chat' ? {
|
||||
max_iteration: DEFAULT_AGENT_SETTING.max_iteration,
|
||||
...modelConfig.agent_mode,
|
||||
// remove dataset
|
||||
enabled: true, // modelConfig.agent_mode?.enabled is not correct. old app: the value of app with dataset's is always true
|
||||
tools: modelConfig.agent_mode?.tools.filter((tool: any) => {
|
||||
return !tool.dataset
|
||||
}).map((tool: any) => {
|
||||
const toolInCollectionList = collectionList.find(c => tool.provider_id === c.id)
|
||||
return {
|
||||
...tool,
|
||||
isDeleted: res.deleted_tools?.some((deletedTool: any) => deletedTool.id === tool.id && deletedTool.tool_name === tool.tool_name),
|
||||
notAuthor: toolInCollectionList?.is_team_authorization === false,
|
||||
...(tool.provider_type === 'builtin' ? {
|
||||
provider_id: correctToolProvider(tool.provider_name, !!toolInCollectionList),
|
||||
provider_name: correctToolProvider(tool.provider_name, !!toolInCollectionList),
|
||||
} : {}),
|
||||
}
|
||||
}),
|
||||
} : DEFAULT_AGENT_SETTING,
|
||||
},
|
||||
completionParams: model.completion_params,
|
||||
}
|
||||
enabled: true, // modelConfig.agent_mode?.enabled is not correct. old app: the value of app with dataset's is always true
|
||||
tools: (modelConfig.agent_mode?.tools ?? []).filter((tool: any) => {
|
||||
return !tool.dataset
|
||||
}).map((tool: any) => {
|
||||
const toolInCollectionList = collectionList.find(c => tool.provider_id === c.id)
|
||||
return {
|
||||
...tool,
|
||||
isDeleted: res.deleted_tools?.some((deletedTool: any) => deletedTool.id === tool.id && deletedTool.tool_name === tool.tool_name) ?? false,
|
||||
notAuthor: toolInCollectionList?.is_team_authorization === false,
|
||||
...(tool.provider_type === 'builtin' ? {
|
||||
provider_id: correctToolProvider(tool.provider_name, !!toolInCollectionList),
|
||||
provider_name: correctToolProvider(tool.provider_name, !!toolInCollectionList),
|
||||
} : {}),
|
||||
}
|
||||
}),
|
||||
strategy: modelConfig.agent_mode?.strategy ?? AgentStrategy.react,
|
||||
} : DEFAULT_AGENT_SETTING,
|
||||
},
|
||||
completionParams: model.completion_params,
|
||||
}
|
||||
|
||||
if (modelConfig.file_upload)
|
||||
handleSetVisionConfig(modelConfig.file_upload.image, true)
|
||||
if (modelConfig.file_upload)
|
||||
handleSetVisionConfig(modelConfig.file_upload.image, true)
|
||||
|
||||
syncToPublishedConfig(config)
|
||||
setPublishedConfig(config)
|
||||
const retrievalConfig = getMultipleRetrievalConfig({
|
||||
...modelConfig.dataset_configs,
|
||||
reranking_model: modelConfig.dataset_configs.reranking_model && {
|
||||
provider: modelConfig.dataset_configs.reranking_model.reranking_provider_name,
|
||||
model: modelConfig.dataset_configs.reranking_model.reranking_model_name,
|
||||
},
|
||||
}, datasets, datasets, {
|
||||
provider: currentRerankProvider?.provider,
|
||||
model: currentRerankModel?.model,
|
||||
})
|
||||
setDatasetConfigs({
|
||||
retrieval_model: RETRIEVE_TYPE.multiWay,
|
||||
...modelConfig.dataset_configs,
|
||||
...retrievalConfig,
|
||||
...(retrievalConfig.reranking_model ? {
|
||||
reranking_model: {
|
||||
reranking_model_name: retrievalConfig.reranking_model.model,
|
||||
reranking_provider_name: correctModelProvider(retrievalConfig.reranking_model.provider),
|
||||
},
|
||||
} : {}),
|
||||
})
|
||||
setHasFetchedDetail(true)
|
||||
syncToPublishedConfig(config)
|
||||
setPublishedConfig(config)
|
||||
const retrievalConfig = getMultipleRetrievalConfig({
|
||||
...modelConfig.dataset_configs,
|
||||
reranking_model: modelConfig.dataset_configs.reranking_model && {
|
||||
provider: modelConfig.dataset_configs.reranking_model.reranking_provider_name,
|
||||
model: modelConfig.dataset_configs.reranking_model.reranking_model_name,
|
||||
},
|
||||
}, datasets, datasets, {
|
||||
provider: currentRerankProvider?.provider,
|
||||
model: currentRerankModel?.model,
|
||||
})
|
||||
setDatasetConfigs({
|
||||
...modelConfig.dataset_configs,
|
||||
...retrievalConfig,
|
||||
...(retrievalConfig.reranking_model ? {
|
||||
reranking_model: {
|
||||
reranking_model_name: retrievalConfig.reranking_model.model,
|
||||
reranking_provider_name: correctModelProvider(retrievalConfig.reranking_model.provider),
|
||||
},
|
||||
} : {}),
|
||||
} as DatasetConfigs)
|
||||
setHasFetchedDetail(true)
|
||||
})()
|
||||
}, [appId])
|
||||
|
||||
|
|
@ -780,8 +790,8 @@ const Configuration: FC = () => {
|
|||
// Simple Mode prompt
|
||||
pre_prompt: !isAdvancedMode ? promptTemplate : '',
|
||||
prompt_type: promptMode,
|
||||
chat_prompt_config: {},
|
||||
completion_prompt_config: {},
|
||||
chat_prompt_config: isAdvancedMode ? chatPromptConfig : clone(DEFAULT_CHAT_PROMPT_CONFIG),
|
||||
completion_prompt_config: isAdvancedMode ? completionPromptConfig : clone(DEFAULT_COMPLETION_PROMPT_CONFIG),
|
||||
user_input_form: promptVariablesToUserInputsForm(promptVariables),
|
||||
dataset_query_variable: contextVar || '',
|
||||
// features
|
||||
|
|
@ -798,6 +808,7 @@ const Configuration: FC = () => {
|
|||
...modelConfig.agentConfig,
|
||||
strategy: isFunctionCall ? AgentStrategy.functionCall : AgentStrategy.react,
|
||||
},
|
||||
external_data_tools: externalDataToolsConfig,
|
||||
model: {
|
||||
provider: modelAndParameter?.provider || modelConfig.provider,
|
||||
name: modelId,
|
||||
|
|
@ -810,11 +821,7 @@ const Configuration: FC = () => {
|
|||
datasets: [...postDatasets],
|
||||
} as any,
|
||||
},
|
||||
}
|
||||
|
||||
if (isAdvancedMode) {
|
||||
data.chat_prompt_config = chatPromptConfig
|
||||
data.completion_prompt_config = completionPromptConfig
|
||||
system_parameters: modelConfig.system_parameters,
|
||||
}
|
||||
|
||||
await updateAppModelConfig({ url: `/apps/${appId}/model-config`, body: data })
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import Chat from '../chat'
|
|||
import type {
|
||||
ChatConfig,
|
||||
ChatItem,
|
||||
ChatItemInTree,
|
||||
OnSend,
|
||||
} from '../types'
|
||||
import { useChat } from '../chat/hooks'
|
||||
|
|
@ -155,7 +154,7 @@ const ChatWrapper = () => {
|
|||
)
|
||||
}, [chatList, handleNewConversationCompleted, handleSend, currentConversationId, currentConversationInputs, newConversationInputs, isInstalledApp, appId])
|
||||
|
||||
const doRegenerate = useCallback((chatItem: ChatItemInTree, editedQuestion?: { message: string, files?: FileEntity[] }) => {
|
||||
const doRegenerate = useCallback((chatItem: ChatItem, editedQuestion?: { message: string, files?: FileEntity[] }) => {
|
||||
const question = editedQuestion ? chatItem : chatList.find(item => item.id === chatItem.parentMessageId)!
|
||||
const parentAnswer = chatList.find(item => item.id === question.parentMessageId)
|
||||
doSend(editedQuestion ? editedQuestion.message : question.content,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import Chat from '../chat'
|
|||
import type {
|
||||
ChatConfig,
|
||||
ChatItem,
|
||||
ChatItemInTree,
|
||||
OnSend,
|
||||
} from '../types'
|
||||
import { useChat } from '../chat/hooks'
|
||||
|
|
@ -150,7 +149,7 @@ const ChatWrapper = () => {
|
|||
)
|
||||
}, [currentConversationId, currentConversationInputs, newConversationInputs, chatList, handleSend, isInstalledApp, appId, handleNewConversationCompleted])
|
||||
|
||||
const doRegenerate = useCallback((chatItem: ChatItemInTree, editedQuestion?: { message: string, files?: FileEntity[] }) => {
|
||||
const doRegenerate = useCallback((chatItem: ChatItem, editedQuestion?: { message: string, files?: FileEntity[] }) => {
|
||||
const question = editedQuestion ? chatItem : chatList.find(item => item.id === chatItem.parentMessageId)!
|
||||
const parentAnswer = chatList.find(item => item.id === question.parentMessageId)
|
||||
doSend(editedQuestion ? editedQuestion.message : question.content,
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ export type OnSend = {
|
|||
(message: string, files: FileEntity[] | undefined, isRegenerate: boolean, lastAnswer?: ChatItem | null): void
|
||||
}
|
||||
|
||||
export type OnRegenerate = (chatItem: ChatItem) => void
|
||||
export type OnRegenerate = (chatItem: ChatItem, editedQuestion?: { message: string; files?: FileEntity[] }) => void
|
||||
|
||||
export type Callback = {
|
||||
onSuccess: () => void
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ const meta = {
|
|||
},
|
||||
args: {
|
||||
show: false,
|
||||
children: null,
|
||||
},
|
||||
} satisfies Meta<typeof ContentDialog>
|
||||
|
||||
|
|
@ -92,6 +93,9 @@ const DemoWrapper = (props: Props) => {
|
|||
}
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
children: null,
|
||||
},
|
||||
render: args => <DemoWrapper {...args} />,
|
||||
}
|
||||
|
||||
|
|
@ -99,6 +103,7 @@ export const NarrowPanel: Story = {
|
|||
render: args => <DemoWrapper {...args} />,
|
||||
args: {
|
||||
className: 'max-w-[420px]',
|
||||
children: null,
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { fireEvent, render, screen } from '@testing-library/react'
|
|||
import TimePicker from './index'
|
||||
import dayjs from '../utils/dayjs'
|
||||
import { isDayjsObject } from '../utils/dayjs'
|
||||
import type { TimePickerProps } from '../types'
|
||||
|
||||
jest.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
|
|
@ -30,9 +31,10 @@ jest.mock('./options', () => () => <div data-testid="time-options" />)
|
|||
jest.mock('./header', () => () => <div data-testid="time-header" />)
|
||||
|
||||
describe('TimePicker', () => {
|
||||
const baseProps = {
|
||||
const baseProps: Pick<TimePickerProps, 'onChange' | 'onClear' | 'value'> = {
|
||||
onChange: jest.fn(),
|
||||
onClear: jest.fn(),
|
||||
value: undefined,
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ export const toDayjs = (value: string | Dayjs | undefined, options: ToDayjsOptio
|
|||
|
||||
if (format) {
|
||||
const parsedWithFormat = tzName
|
||||
? dayjs.tz(trimmed, format, tzName, true)
|
||||
? dayjs(trimmed, format, true).tz(tzName, true)
|
||||
: dayjs(trimmed, format, true)
|
||||
if (parsedWithFormat.isValid())
|
||||
return parsedWithFormat
|
||||
|
|
@ -191,7 +191,7 @@ export const toDayjs = (value: string | Dayjs | undefined, options: ToDayjsOptio
|
|||
const candidateFormats = formats ?? COMMON_PARSE_FORMATS
|
||||
for (const fmt of candidateFormats) {
|
||||
const parsed = tzName
|
||||
? dayjs.tz(trimmed, fmt, tzName, true)
|
||||
? dayjs(trimmed, fmt, true).tz(tzName, true)
|
||||
: dayjs(trimmed, fmt, true)
|
||||
if (parsed.isValid())
|
||||
return parsed
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ const meta = {
|
|||
args: {
|
||||
title: 'Manage API Keys',
|
||||
show: false,
|
||||
children: null,
|
||||
},
|
||||
} satisfies Meta<typeof Dialog>
|
||||
|
||||
|
|
@ -102,6 +103,7 @@ export const Default: Story = {
|
|||
</button>
|
||||
</>
|
||||
),
|
||||
children: null,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -110,6 +112,7 @@ export const WithoutFooter: Story = {
|
|||
args: {
|
||||
footer: undefined,
|
||||
title: 'Read-only summary',
|
||||
children: null,
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
|
|
@ -140,6 +143,7 @@ export const CustomStyling: Story = {
|
|||
</div>
|
||||
</>
|
||||
),
|
||||
children: null,
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export type FormOption = {
|
|||
icon?: string
|
||||
}
|
||||
|
||||
export type AnyValidators = FieldValidators<any, any, any, any, any, any, any, any, any, any>
|
||||
export type AnyValidators = FieldValidators<any, any, any, any, any, any, any, any, any, any, any, any>
|
||||
|
||||
export type FormSchema = {
|
||||
type: FormTypeEnum
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useChatContext } from '../chat/chat/context'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
const hasEndThink = (children: any): boolean => {
|
||||
if (typeof children === 'string')
|
||||
|
|
@ -40,7 +41,7 @@ const useThinkTimer = (children: any) => {
|
|||
const [startTime] = useState(() => Date.now())
|
||||
const [elapsedTime, setElapsedTime] = useState(0)
|
||||
const [isComplete, setIsComplete] = useState(false)
|
||||
const timerRef = useRef<NodeJS.Timeout>()
|
||||
const timerRef = useRef<NodeJS.Timeout | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (isComplete) return
|
||||
|
|
@ -63,16 +64,26 @@ const useThinkTimer = (children: any) => {
|
|||
return { elapsedTime, isComplete }
|
||||
}
|
||||
|
||||
const ThinkBlock = ({ children, ...props }: React.ComponentProps<'details'>) => {
|
||||
type ThinkBlockProps = React.ComponentProps<'details'> & {
|
||||
'data-think'?: boolean
|
||||
}
|
||||
|
||||
const ThinkBlock = ({ children, ...props }: ThinkBlockProps) => {
|
||||
const { elapsedTime, isComplete } = useThinkTimer(children)
|
||||
const displayContent = removeEndThink(children)
|
||||
const { t } = useTranslation()
|
||||
const { 'data-think': isThink = false, className, open, ...rest } = props
|
||||
|
||||
if (!(props['data-think'] ?? false))
|
||||
if (!isThink)
|
||||
return (<details {...props}>{children}</details>)
|
||||
|
||||
return (
|
||||
<details {...(!isComplete && { open: true })} className="group">
|
||||
<details
|
||||
{...rest}
|
||||
data-think={isThink}
|
||||
className={cn('group', className)}
|
||||
open={isComplete ? open : true}
|
||||
>
|
||||
<summary className="flex cursor-pointer select-none list-none items-center whitespace-nowrap pl-2 font-bold text-text-secondary">
|
||||
<div className="flex shrink-0 items-center">
|
||||
<svg
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import RemarkBreaks from 'remark-breaks'
|
|||
import RehypeKatex from 'rehype-katex'
|
||||
import RemarkGfm from 'remark-gfm'
|
||||
import RehypeRaw from 'rehype-raw'
|
||||
import { ENABLE_SINGLE_DOLLAR_LATEX } from '@/config'
|
||||
import AudioBlock from '@/app/components/base/markdown-blocks/audio-block'
|
||||
import Img from '@/app/components/base/markdown-blocks/img'
|
||||
import Link from '@/app/components/base/markdown-blocks/link'
|
||||
|
|
@ -34,7 +35,7 @@ export const ReactMarkdownWrapper: FC<ReactMarkdownWrapperProps> = (props) => {
|
|||
<ReactMarkdown
|
||||
remarkPlugins={[
|
||||
RemarkGfm,
|
||||
[RemarkMath, { singleDollarTextMath: false }],
|
||||
[RemarkMath, { singleDollarTextMath: ENABLE_SINGLE_DOLLAR_LATEX }],
|
||||
RemarkBreaks,
|
||||
]}
|
||||
rehypePlugins={[
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ const meta = {
|
|||
hideCloseBtn: false,
|
||||
onClose: () => console.log('close'),
|
||||
onConfirm: () => console.log('confirm'),
|
||||
children: null,
|
||||
},
|
||||
} satisfies Meta<typeof ModalLikeWrap>
|
||||
|
||||
|
|
@ -68,6 +69,9 @@ export const Default: Story = {
|
|||
<BaseContent />
|
||||
</ModalLikeWrap>
|
||||
),
|
||||
args: {
|
||||
children: null,
|
||||
},
|
||||
}
|
||||
|
||||
export const WithBackLink: Story = {
|
||||
|
|
@ -90,6 +94,7 @@ export const WithBackLink: Story = {
|
|||
),
|
||||
args: {
|
||||
title: 'Select metadata type',
|
||||
children: null,
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
|
|
@ -114,6 +119,7 @@ export const CustomWidth: Story = {
|
|||
),
|
||||
args: {
|
||||
title: 'Advanced configuration',
|
||||
children: null,
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Popover, PopoverButton, PopoverPanel, Transition } from '@headlessui/react'
|
||||
import { Fragment, cloneElement, useRef } from 'react'
|
||||
import { Fragment, cloneElement, isValidElement, useRef } from 'react'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
export type HtmlContentProps = {
|
||||
|
|
@ -103,15 +103,17 @@ export default function CustomPopover({
|
|||
})
|
||||
}
|
||||
>
|
||||
{cloneElement(htmlContent as React.ReactElement, {
|
||||
open,
|
||||
onClose: close,
|
||||
...(manualClose
|
||||
? {
|
||||
onClick: close,
|
||||
}
|
||||
: {}),
|
||||
})}
|
||||
{isValidElement(htmlContent)
|
||||
? cloneElement(htmlContent as React.ReactElement<HtmlContentProps>, {
|
||||
open,
|
||||
onClose: close,
|
||||
...(manualClose
|
||||
? {
|
||||
onClick: close,
|
||||
}
|
||||
: {}),
|
||||
})
|
||||
: htmlContent}
|
||||
</div>
|
||||
)}
|
||||
</PopoverPanel>
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ export const PortalToFollowElemTrigger = (
|
|||
children,
|
||||
asChild = false,
|
||||
...props
|
||||
}: React.HTMLProps<HTMLElement> & { ref?: React.RefObject<HTMLElement>, asChild?: boolean },
|
||||
}: React.HTMLProps<HTMLElement> & { ref?: React.RefObject<HTMLElement | null>, asChild?: boolean },
|
||||
) => {
|
||||
const context = usePortalToFollowElemContext()
|
||||
const childrenRef = (children as any).props?.ref
|
||||
|
|
@ -133,12 +133,13 @@ export const PortalToFollowElemTrigger = (
|
|||
|
||||
// `asChild` allows the user to pass any element as the anchor
|
||||
if (asChild && React.isValidElement(children)) {
|
||||
const childProps = (children.props ?? {}) as Record<string, unknown>
|
||||
return React.cloneElement(
|
||||
children,
|
||||
context.getReferenceProps({
|
||||
ref,
|
||||
...props,
|
||||
...children.props,
|
||||
...childProps,
|
||||
'data-state': context.open ? 'open' : 'closed',
|
||||
} as React.HTMLProps<HTMLElement>),
|
||||
)
|
||||
|
|
@ -164,7 +165,7 @@ export const PortalToFollowElemContent = (
|
|||
style,
|
||||
...props
|
||||
}: React.HTMLProps<HTMLDivElement> & {
|
||||
ref?: React.RefObject<HTMLDivElement>;
|
||||
ref?: React.RefObject<HTMLDivElement | null>;
|
||||
},
|
||||
) => {
|
||||
const context = usePortalToFollowElemContext()
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import { DELETE_QUERY_BLOCK_COMMAND } from './plugins/query-block'
|
|||
import type { CustomTextNode } from './plugins/custom-text/node'
|
||||
import { registerLexicalTextEntity } from './utils'
|
||||
|
||||
export type UseSelectOrDeleteHandler = (nodeKey: string, command?: LexicalCommand<undefined>) => [RefObject<HTMLDivElement>, boolean]
|
||||
export type UseSelectOrDeleteHandler = (nodeKey: string, command?: LexicalCommand<undefined>) => [RefObject<HTMLDivElement | null>, boolean]
|
||||
export const useSelectOrDelete: UseSelectOrDeleteHandler = (nodeKey: string, command?: LexicalCommand<undefined>) => {
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
const [editor] = useLexicalComposerContext()
|
||||
|
|
@ -110,7 +110,7 @@ export const useSelectOrDelete: UseSelectOrDeleteHandler = (nodeKey: string, com
|
|||
return [ref, isSelected]
|
||||
}
|
||||
|
||||
export type UseTriggerHandler = () => [RefObject<HTMLDivElement>, boolean, Dispatch<SetStateAction<boolean>>]
|
||||
export type UseTriggerHandler = () => [RefObject<HTMLDivElement | null>, boolean, Dispatch<SetStateAction<boolean>>]
|
||||
export const useTrigger: UseTriggerHandler = () => {
|
||||
const triggerRef = useRef<HTMLDivElement>(null)
|
||||
const [open, setOpen] = useState(false)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { memo } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
|
|
@ -8,7 +9,7 @@ const Placeholder = ({
|
|||
className,
|
||||
}: {
|
||||
compact?: boolean
|
||||
value?: string | JSX.Element
|
||||
value?: ReactNode
|
||||
className?: string
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
|
|
|||
|
|
@ -14,13 +14,19 @@ export const convertToMp3 = (recorder: any) => {
|
|||
const { channels, sampleRate } = wav
|
||||
const mp3enc = new lamejs.Mp3Encoder(channels, sampleRate, 128)
|
||||
const result = recorder.getChannelData()
|
||||
const buffer = []
|
||||
const buffer: BlobPart[] = []
|
||||
|
||||
const leftData = result.left && new Int16Array(result.left.buffer, 0, result.left.byteLength / 2)
|
||||
const rightData = result.right && new Int16Array(result.right.buffer, 0, result.right.byteLength / 2)
|
||||
const remaining = leftData.length + (rightData ? rightData.length : 0)
|
||||
|
||||
const maxSamples = 1152
|
||||
const toArrayBuffer = (bytes: Int8Array) => {
|
||||
const arrayBuffer = new ArrayBuffer(bytes.length)
|
||||
new Uint8Array(arrayBuffer).set(bytes)
|
||||
return arrayBuffer
|
||||
}
|
||||
|
||||
for (let i = 0; i < remaining; i += maxSamples) {
|
||||
const left = leftData.subarray(i, i + maxSamples)
|
||||
let right = null
|
||||
|
|
@ -35,13 +41,13 @@ export const convertToMp3 = (recorder: any) => {
|
|||
}
|
||||
|
||||
if (mp3buf.length > 0)
|
||||
buffer.push(mp3buf)
|
||||
buffer.push(toArrayBuffer(mp3buf))
|
||||
}
|
||||
|
||||
const enc = mp3enc.flush()
|
||||
|
||||
if (enc.length > 0)
|
||||
buffer.push(enc)
|
||||
buffer.push(toArrayBuffer(enc))
|
||||
|
||||
return new Blob(buffer, { type: 'audio/mp3' })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ const Pricing: FC<PricingProps> = ({
|
|||
const [planRange, setPlanRange] = React.useState<PlanRange>(PlanRange.monthly)
|
||||
const [currentCategory, setCurrentCategory] = useState<Category>(CategoryEnum.CLOUD)
|
||||
const canPay = isCurrentWorkspaceManager
|
||||
|
||||
useKeyPress(['esc'], onCancel)
|
||||
|
||||
const pricingPageLanguage = useGetPricingPageLanguage()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import SelfHostedPlanItem from './self-hosted-plan-item'
|
|||
|
||||
type PlansProps = {
|
||||
plan: {
|
||||
type: BasicPlan
|
||||
type: Plan
|
||||
usage: UsagePlanInfo
|
||||
total: UsagePlanInfo
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ const Plans = ({
|
|||
planRange,
|
||||
canPay,
|
||||
}: PlansProps) => {
|
||||
const currentPlanType: BasicPlan = plan.type === Plan.enterprise ? Plan.team : plan.type
|
||||
return (
|
||||
<div className='flex w-full justify-center border-t border-divider-accent px-10'>
|
||||
<div className='flex max-w-[1680px] grow border-x border-divider-accent'>
|
||||
|
|
@ -28,21 +29,21 @@ const Plans = ({
|
|||
currentPlan === 'cloud' && (
|
||||
<>
|
||||
<CloudPlanItem
|
||||
currentPlan={plan.type}
|
||||
currentPlan={currentPlanType}
|
||||
plan={Plan.sandbox}
|
||||
planRange={planRange}
|
||||
canPay={canPay}
|
||||
/>
|
||||
<Divider type='vertical' className='mx-0 shrink-0 bg-divider-accent' />
|
||||
<CloudPlanItem
|
||||
currentPlan={plan.type}
|
||||
currentPlan={currentPlanType}
|
||||
plan={Plan.professional}
|
||||
planRange={planRange}
|
||||
canPay={canPay}
|
||||
/>
|
||||
<Divider type='vertical' className='mx-0 shrink-0 bg-divider-accent' />
|
||||
<CloudPlanItem
|
||||
currentPlan={plan.type}
|
||||
currentPlan={currentPlanType}
|
||||
plan={Plan.team}
|
||||
planRange={planRange}
|
||||
canPay={canPay}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,13 @@ import DocumentFileIcon from '../../common/document-file-icon'
|
|||
import cn from '@/utils/classnames'
|
||||
import { FieldInfo } from '@/app/components/datasets/documents/detail/metadata'
|
||||
import Button from '@/app/components/base/button'
|
||||
import type { FullDocumentDetail, IndexingStatusResponse, ProcessRuleResponse } from '@/models/datasets'
|
||||
import type {
|
||||
DataSourceInfo,
|
||||
FullDocumentDetail,
|
||||
IndexingStatusResponse,
|
||||
LegacyDataSourceInfo,
|
||||
ProcessRuleResponse,
|
||||
} from '@/models/datasets'
|
||||
import { fetchIndexingStatusBatch as doFetchIndexingStatus, fetchProcessRule } from '@/service/datasets'
|
||||
import { DataSourceType, ProcessMode } from '@/models/datasets'
|
||||
import NotionIcon from '@/app/components/base/notion-icon'
|
||||
|
|
@ -241,10 +247,16 @@ const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], index
|
|||
return doc?.data_source_type as DataSourceType
|
||||
}
|
||||
|
||||
const isLegacyDataSourceInfo = (info: DataSourceInfo): info is LegacyDataSourceInfo => {
|
||||
return info != null && typeof (info as LegacyDataSourceInfo).upload_file === 'object'
|
||||
}
|
||||
|
||||
const getIcon = (id: string) => {
|
||||
const doc = documents.find(document => document.id === id)
|
||||
|
||||
return doc?.data_source_info.notion_page_icon
|
||||
const info = doc?.data_source_info
|
||||
if (info && isLegacyDataSourceInfo(info))
|
||||
return info.notion_page_icon
|
||||
return undefined
|
||||
}
|
||||
const isSourceEmbedding = (detail: IndexingStatusResponse) =>
|
||||
['indexing', 'splitting', 'parsing', 'cleaning', 'waiting'].includes(detail.indexing_status || '')
|
||||
|
|
|
|||
|
|
@ -105,6 +105,8 @@ const FileUploader = ({
|
|||
return isValidType && isValidSize
|
||||
}, [fileUploadConfig, notify, t, ACCEPTS])
|
||||
|
||||
type UploadResult = Awaited<ReturnType<typeof upload>>
|
||||
|
||||
const fileUpload = useCallback(async (fileItem: FileItem): Promise<FileItem> => {
|
||||
const formData = new FormData()
|
||||
formData.append('file', fileItem.file)
|
||||
|
|
@ -180,7 +182,7 @@ const FileUploader = ({
|
|||
prepareFileList(newFiles)
|
||||
fileListRef.current = newFiles
|
||||
uploadMultipleFiles(preparedFiles)
|
||||
}, [prepareFileList, uploadMultipleFiles, notify, t, fileList])
|
||||
}, [prepareFileList, uploadMultipleFiles, notify, t, fileList, fileUploadConfig])
|
||||
|
||||
const handleDragEnter = (e: DragEvent) => {
|
||||
e.preventDefault()
|
||||
|
|
@ -277,7 +279,7 @@ const FileUploader = ({
|
|||
let files = [...(e.target.files ?? [])] as File[]
|
||||
files = files.slice(0, fileUploadConfig.batch_count_limit)
|
||||
initialUpload(files.filter(isValid))
|
||||
}, [isValid, initialUpload])
|
||||
}, [isValid, initialUpload, fileUploadConfig])
|
||||
|
||||
const { theme } = useTheme()
|
||||
const chartColor = useMemo(() => theme === Theme.dark ? '#5289ff' : '#296dff', [theme])
|
||||
|
|
@ -326,7 +328,7 @@ const FileUploader = ({
|
|||
<div>{t('datasetCreation.stepOne.uploader.tip', {
|
||||
size: fileUploadConfig.file_size_limit,
|
||||
supportTypes: supportTypesShowNames,
|
||||
batchCount: fileUploadConfig.batch_count_limit,
|
||||
batchCount: notSupportBatchUpload ? 1 : fileUploadConfig.batch_count_limit,
|
||||
totalCount: fileUploadConfig.file_upload_limit,
|
||||
})}</div>
|
||||
{dragging && <div ref={dragRef} className='absolute left-0 top-0 h-full w-full' />}
|
||||
|
|
|
|||
|
|
@ -121,6 +121,8 @@ const LocalFile = ({
|
|||
return isValidType && isValidSize
|
||||
}, [fileUploadConfig, notify, t, ACCEPTS])
|
||||
|
||||
type UploadResult = Awaited<ReturnType<typeof upload>>
|
||||
|
||||
const fileUpload = useCallback(async (fileItem: FileItem): Promise<FileItem> => {
|
||||
const formData = new FormData()
|
||||
formData.append('file', fileItem.file)
|
||||
|
|
@ -136,10 +138,14 @@ const LocalFile = ({
|
|||
data: formData,
|
||||
onprogress: onProgress,
|
||||
}, false, undefined, '?source=datasets')
|
||||
.then((res: File) => {
|
||||
const completeFile = {
|
||||
.then((res: UploadResult) => {
|
||||
const updatedFile = Object.assign({}, fileItem.file, {
|
||||
id: res.id,
|
||||
...(res as Partial<File>),
|
||||
}) as File
|
||||
const completeFile: FileItem = {
|
||||
fileID: fileItem.fileID,
|
||||
file: res,
|
||||
file: updatedFile,
|
||||
progress: -1,
|
||||
}
|
||||
const index = fileListRef.current.findIndex(item => item.fileID === fileItem.fileID)
|
||||
|
|
@ -287,7 +293,7 @@ const LocalFile = ({
|
|||
<RiUploadCloud2Line className='mr-2 size-5' />
|
||||
|
||||
<span>
|
||||
{t('datasetCreation.stepOne.uploader.button')}
|
||||
{notSupportBatchUpload ? t('datasetCreation.stepOne.uploader.buttonSingleFile') : t('datasetCreation.stepOne.uploader.button')}
|
||||
{allowedExtensions.length > 0 && (
|
||||
<label className='ml-1 cursor-pointer text-text-accent' onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.browse')}</label>
|
||||
)}
|
||||
|
|
@ -296,7 +302,7 @@ const LocalFile = ({
|
|||
<div>{t('datasetCreation.stepOne.uploader.tip', {
|
||||
size: fileUploadConfig.file_size_limit,
|
||||
supportTypes: supportTypesShowNames,
|
||||
batchCount: fileUploadConfig.batch_count_limit,
|
||||
batchCount: notSupportBatchUpload ? 1 : fileUploadConfig.batch_count_limit,
|
||||
})}</div>
|
||||
{dragging && <div ref={dragRef} className='absolute left-0 top-0 h-full w-full' />}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ const CSVUploader: FC<Props> = ({
|
|||
file_size_limit: 15,
|
||||
}, [fileUploadConfigResponse])
|
||||
|
||||
type UploadResult = Awaited<ReturnType<typeof upload>>
|
||||
|
||||
const fileUpload = useCallback(async (fileItem: FileItem): Promise<FileItem> => {
|
||||
fileItem.progress = 0
|
||||
|
||||
|
|
@ -58,10 +60,14 @@ const CSVUploader: FC<Props> = ({
|
|||
data: formData,
|
||||
onprogress: onProgress,
|
||||
}, false, undefined, '?source=datasets')
|
||||
.then((res: File) => {
|
||||
const completeFile = {
|
||||
.then((res: UploadResult) => {
|
||||
const updatedFile = Object.assign({}, fileItem.file, {
|
||||
id: res.id,
|
||||
...(res as Partial<File>),
|
||||
}) as File
|
||||
const completeFile: FileItem = {
|
||||
fileID: fileItem.fileID,
|
||||
file: res,
|
||||
file: updatedFile,
|
||||
progress: 100,
|
||||
}
|
||||
updateFile(completeFile)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import Divider from '@/app/components/base/divider'
|
|||
import Loading from '@/app/components/base/loading'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { ChunkingMode } from '@/models/datasets'
|
||||
import type { FileItem } from '@/models/datasets'
|
||||
import type { DataSourceInfo, FileItem, LegacyDataSourceInfo } from '@/models/datasets'
|
||||
import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
|
||||
import FloatRightContainer from '@/app/components/base/float-right-container'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
|
|
@ -109,6 +109,18 @@ const DocumentDetail: FC<DocumentDetailProps> = ({ datasetId, documentId }) => {
|
|||
|
||||
const embedding = ['queuing', 'indexing', 'paused'].includes((documentDetail?.display_status || '').toLowerCase())
|
||||
|
||||
const isLegacyDataSourceInfo = (info?: DataSourceInfo): info is LegacyDataSourceInfo => {
|
||||
return !!info && 'upload_file' in info
|
||||
}
|
||||
|
||||
const documentUploadFile = useMemo(() => {
|
||||
if (!documentDetail?.data_source_info)
|
||||
return undefined
|
||||
if (isLegacyDataSourceInfo(documentDetail.data_source_info))
|
||||
return documentDetail.data_source_info.upload_file
|
||||
return undefined
|
||||
}, [documentDetail?.data_source_info])
|
||||
|
||||
const invalidChunkList = useInvalid(useSegmentListKey)
|
||||
const invalidChildChunkList = useInvalid(useChildSegmentListKey)
|
||||
const invalidDocumentList = useInvalidDocumentList(datasetId)
|
||||
|
|
@ -153,7 +165,7 @@ const DocumentDetail: FC<DocumentDetailProps> = ({ datasetId, documentId }) => {
|
|||
</div>
|
||||
<DocumentTitle
|
||||
datasetId={datasetId}
|
||||
extension={documentDetail?.data_source_info?.upload_file?.extension}
|
||||
extension={documentUploadFile?.extension}
|
||||
name={documentDetail?.name}
|
||||
wrapperCls='mr-2'
|
||||
parent_mode={parentMode}
|
||||
|
|
|
|||
|
|
@ -131,9 +131,15 @@ type IMetadataProps = {
|
|||
onUpdate: () => void
|
||||
}
|
||||
|
||||
type MetadataState = {
|
||||
documentType?: DocType | ''
|
||||
metadata: Record<string, string>
|
||||
}
|
||||
|
||||
const Metadata: FC<IMetadataProps> = ({ docDetail, loading, onUpdate }) => {
|
||||
const { doc_metadata = {} } = docDetail || {}
|
||||
const doc_type = docDetail?.doc_type || ''
|
||||
const rawDocType = docDetail?.doc_type ?? ''
|
||||
const doc_type = rawDocType === 'others' ? '' : rawDocType
|
||||
|
||||
const { t } = useTranslation()
|
||||
const metadataMap = useMetadataMap()
|
||||
|
|
@ -143,18 +149,16 @@ const Metadata: FC<IMetadataProps> = ({ docDetail, loading, onUpdate }) => {
|
|||
const businessDocCategoryMap = useBusinessDocCategories()
|
||||
const [editStatus, setEditStatus] = useState(!doc_type) // if no documentType, in editing status by default
|
||||
// the initial values are according to the documentType
|
||||
const [metadataParams, setMetadataParams] = useState<{
|
||||
documentType?: DocType | ''
|
||||
metadata: { [key: string]: string }
|
||||
}>(
|
||||
const [metadataParams, setMetadataParams] = useState<MetadataState>(
|
||||
doc_type
|
||||
? {
|
||||
documentType: doc_type,
|
||||
metadata: doc_metadata || {},
|
||||
documentType: doc_type as DocType,
|
||||
metadata: (doc_metadata || {}) as Record<string, string>,
|
||||
}
|
||||
: { metadata: {} })
|
||||
: { metadata: {} },
|
||||
)
|
||||
const [showDocTypes, setShowDocTypes] = useState(!doc_type) // whether show doc types
|
||||
const [tempDocType, setTempDocType] = useState<DocType | undefined | ''>('') // for remember icon click
|
||||
const [tempDocType, setTempDocType] = useState<DocType | ''>('') // for remember icon click
|
||||
const [saveLoading, setSaveLoading] = useState(false)
|
||||
|
||||
const { notify } = useContext(ToastContext)
|
||||
|
|
@ -165,13 +169,13 @@ const Metadata: FC<IMetadataProps> = ({ docDetail, loading, onUpdate }) => {
|
|||
if (docDetail?.doc_type) {
|
||||
setEditStatus(false)
|
||||
setShowDocTypes(false)
|
||||
setTempDocType(docDetail?.doc_type)
|
||||
setTempDocType(doc_type as DocType | '')
|
||||
setMetadataParams({
|
||||
documentType: docDetail?.doc_type,
|
||||
metadata: docDetail?.doc_metadata || {},
|
||||
documentType: doc_type as DocType | '',
|
||||
metadata: (docDetail?.doc_metadata || {}) as Record<string, string>,
|
||||
})
|
||||
}
|
||||
}, [docDetail?.doc_type])
|
||||
}, [docDetail?.doc_type, docDetail?.doc_metadata, doc_type])
|
||||
|
||||
// confirm doc type
|
||||
const confirmDocType = () => {
|
||||
|
|
@ -179,7 +183,7 @@ const Metadata: FC<IMetadataProps> = ({ docDetail, loading, onUpdate }) => {
|
|||
return
|
||||
setMetadataParams({
|
||||
documentType: tempDocType,
|
||||
metadata: tempDocType === metadataParams.documentType ? metadataParams.metadata : {}, // change doc type, clear metadata
|
||||
metadata: tempDocType === metadataParams.documentType ? metadataParams.metadata : {} as Record<string, string>, // change doc type, clear metadata
|
||||
})
|
||||
setEditStatus(true)
|
||||
setShowDocTypes(false)
|
||||
|
|
@ -187,7 +191,7 @@ const Metadata: FC<IMetadataProps> = ({ docDetail, loading, onUpdate }) => {
|
|||
|
||||
// cancel doc type
|
||||
const cancelDocType = () => {
|
||||
setTempDocType(metadataParams.documentType)
|
||||
setTempDocType(metadataParams.documentType ?? '')
|
||||
setEditStatus(true)
|
||||
setShowDocTypes(false)
|
||||
}
|
||||
|
|
@ -209,7 +213,7 @@ const Metadata: FC<IMetadataProps> = ({ docDetail, loading, onUpdate }) => {
|
|||
<span className={s.title}>{t('datasetDocuments.metadata.docTypeChangeTitle')}</span>
|
||||
<span className={s.changeTip}>{t('datasetDocuments.metadata.docTypeSelectWarning')}</span>
|
||||
</>}
|
||||
<Radio.Group value={tempDocType ?? documentType} onChange={setTempDocType} className={s.radioGroup}>
|
||||
<Radio.Group value={tempDocType ?? documentType ?? ''} onChange={setTempDocType} className={s.radioGroup}>
|
||||
{CUSTOMIZABLE_DOC_TYPES.map((type, index) => {
|
||||
const currValue = tempDocType ?? documentType
|
||||
return <Radio key={index} value={type} className={`${s.radio} ${currValue === type ? 'shadow-none' : ''}`}>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,17 @@ import { useBoolean } from 'ahooks'
|
|||
import { useContext } from 'use-context-selector'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import DatasetDetailContext from '@/context/dataset-detail'
|
||||
import type { CrawlOptions, CustomFile, DataSourceType } from '@/models/datasets'
|
||||
import type {
|
||||
CrawlOptions,
|
||||
CustomFile,
|
||||
DataSourceInfo,
|
||||
DataSourceType,
|
||||
LegacyDataSourceInfo,
|
||||
LocalFileInfo,
|
||||
OnlineDocumentInfo,
|
||||
WebsiteCrawlInfo,
|
||||
} from '@/models/datasets'
|
||||
import type { DataSourceProvider } from '@/models/common'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import StepTwo from '@/app/components/datasets/create/step-two'
|
||||
import AccountSetting from '@/app/components/header/account-setting'
|
||||
|
|
@ -42,15 +52,78 @@ const DocumentSettings = ({ datasetId, documentId }: DocumentSettingsProps) => {
|
|||
params: { metadata: 'without' },
|
||||
})
|
||||
|
||||
const dataSourceInfo = documentDetail?.data_source_info
|
||||
|
||||
const isLegacyDataSourceInfo = (info: DataSourceInfo | undefined): info is LegacyDataSourceInfo => {
|
||||
return !!info && 'upload_file' in info
|
||||
}
|
||||
const isWebsiteCrawlInfo = (info: DataSourceInfo | undefined): info is WebsiteCrawlInfo => {
|
||||
return !!info && 'source_url' in info && 'title' in info
|
||||
}
|
||||
const isOnlineDocumentInfo = (info: DataSourceInfo | undefined): info is OnlineDocumentInfo => {
|
||||
return !!info && 'page' in info
|
||||
}
|
||||
const isLocalFileInfo = (info: DataSourceInfo | undefined): info is LocalFileInfo => {
|
||||
return !!info && 'related_id' in info && 'transfer_method' in info
|
||||
}
|
||||
const legacyInfo = isLegacyDataSourceInfo(dataSourceInfo) ? dataSourceInfo : undefined
|
||||
const websiteInfo = isWebsiteCrawlInfo(dataSourceInfo) ? dataSourceInfo : undefined
|
||||
const onlineDocumentInfo = isOnlineDocumentInfo(dataSourceInfo) ? dataSourceInfo : undefined
|
||||
const localFileInfo = isLocalFileInfo(dataSourceInfo) ? dataSourceInfo : undefined
|
||||
|
||||
const currentPage = useMemo(() => {
|
||||
return {
|
||||
workspace_id: documentDetail?.data_source_info.notion_workspace_id,
|
||||
page_id: documentDetail?.data_source_info.notion_page_id,
|
||||
page_name: documentDetail?.name,
|
||||
page_icon: documentDetail?.data_source_info.notion_page_icon,
|
||||
type: documentDetail?.data_source_type,
|
||||
if (legacyInfo) {
|
||||
return {
|
||||
workspace_id: legacyInfo.notion_workspace_id ?? '',
|
||||
page_id: legacyInfo.notion_page_id ?? '',
|
||||
page_name: documentDetail?.name,
|
||||
page_icon: legacyInfo.notion_page_icon,
|
||||
type: documentDetail?.data_source_type,
|
||||
}
|
||||
}
|
||||
}, [documentDetail])
|
||||
if (onlineDocumentInfo) {
|
||||
return {
|
||||
workspace_id: onlineDocumentInfo.workspace_id,
|
||||
page_id: onlineDocumentInfo.page.page_id,
|
||||
page_name: onlineDocumentInfo.page.page_name,
|
||||
page_icon: onlineDocumentInfo.page.page_icon,
|
||||
type: onlineDocumentInfo.page.type,
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}, [documentDetail?.data_source_type, documentDetail?.name, legacyInfo, onlineDocumentInfo])
|
||||
|
||||
const files = useMemo<CustomFile[]>(() => {
|
||||
if (legacyInfo?.upload_file)
|
||||
return [legacyInfo.upload_file as CustomFile]
|
||||
if (localFileInfo) {
|
||||
const { related_id, name, extension } = localFileInfo
|
||||
return [{
|
||||
id: related_id,
|
||||
name,
|
||||
extension,
|
||||
} as unknown as CustomFile]
|
||||
}
|
||||
return []
|
||||
}, [legacyInfo?.upload_file, localFileInfo])
|
||||
|
||||
const websitePages = useMemo(() => {
|
||||
if (!websiteInfo)
|
||||
return []
|
||||
return [{
|
||||
title: websiteInfo.title,
|
||||
source_url: websiteInfo.source_url,
|
||||
content: websiteInfo.content,
|
||||
description: websiteInfo.description,
|
||||
}]
|
||||
}, [websiteInfo])
|
||||
|
||||
const crawlOptions = (dataSourceInfo && typeof dataSourceInfo === 'object' && 'includes' in dataSourceInfo && 'excludes' in dataSourceInfo)
|
||||
? dataSourceInfo as unknown as CrawlOptions
|
||||
: undefined
|
||||
|
||||
const websiteCrawlProvider = (websiteInfo?.provider ?? legacyInfo?.provider) as DataSourceProvider | undefined
|
||||
const websiteCrawlJobId = websiteInfo?.job_id ?? legacyInfo?.job_id
|
||||
|
||||
if (error)
|
||||
return <AppUnavailable code={500} unknownReason={t('datasetCreation.error.unavailable') as string} />
|
||||
|
|
@ -65,22 +138,16 @@ const DocumentSettings = ({ datasetId, documentId }: DocumentSettingsProps) => {
|
|||
onSetting={showSetAPIKey}
|
||||
datasetId={datasetId}
|
||||
dataSourceType={documentDetail.data_source_type as DataSourceType}
|
||||
notionPages={[currentPage as unknown as NotionPage]}
|
||||
websitePages={[
|
||||
{
|
||||
title: documentDetail.name,
|
||||
source_url: documentDetail.data_source_info?.url,
|
||||
content: '',
|
||||
description: '',
|
||||
},
|
||||
]}
|
||||
websiteCrawlProvider={documentDetail.data_source_info?.provider}
|
||||
websiteCrawlJobId={documentDetail.data_source_info?.job_id}
|
||||
crawlOptions={documentDetail.data_source_info as unknown as CrawlOptions}
|
||||
notionPages={currentPage ? [currentPage as unknown as NotionPage] : []}
|
||||
notionCredentialId={legacyInfo?.credential_id || onlineDocumentInfo?.credential_id || ''}
|
||||
websitePages={websitePages}
|
||||
websiteCrawlProvider={websiteCrawlProvider}
|
||||
websiteCrawlJobId={websiteCrawlJobId || ''}
|
||||
crawlOptions={crawlOptions}
|
||||
indexingType={indexingTechnique}
|
||||
isSetting
|
||||
documentDetail={documentDetail}
|
||||
files={[documentDetail.data_source_info.upload_file as CustomFile]}
|
||||
files={files}
|
||||
onSave={saveHandler}
|
||||
onCancel={cancelHandler}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import type {
|
|||
Model,
|
||||
ModelItem,
|
||||
} from '../declarations'
|
||||
import type { ModelFeatureEnum } from '../declarations'
|
||||
import { useCurrentProviderAndModel } from '../hooks'
|
||||
import ModelTrigger from './model-trigger'
|
||||
import EmptyTrigger from './empty-trigger'
|
||||
|
|
@ -24,7 +25,7 @@ type ModelSelectorProps = {
|
|||
popupClassName?: string
|
||||
onSelect?: (model: DefaultModel) => void
|
||||
readonly?: boolean
|
||||
scopeFeatures?: string[]
|
||||
scopeFeatures?: ModelFeatureEnum[]
|
||||
deprecatedClassName?: string
|
||||
showDeprecatedWarnIcon?: boolean
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ type PopupProps = {
|
|||
defaultModel?: DefaultModel
|
||||
modelList: Model[]
|
||||
onSelect: (provider: string, model: ModelItem) => void
|
||||
scopeFeatures?: string[]
|
||||
scopeFeatures?: ModelFeatureEnum[]
|
||||
onHide: () => void
|
||||
}
|
||||
const Popup: FC<PopupProps> = ({
|
||||
|
|
|
|||
|
|
@ -5,15 +5,17 @@ import { isEmpty } from 'lodash-es'
|
|||
export const pluginManifestToCardPluginProps = (pluginManifest: PluginDeclaration): Plugin => {
|
||||
return {
|
||||
plugin_id: pluginManifest.plugin_unique_identifier,
|
||||
type: pluginManifest.category,
|
||||
type: pluginManifest.category as Plugin['type'],
|
||||
category: pluginManifest.category,
|
||||
name: pluginManifest.name,
|
||||
version: pluginManifest.version,
|
||||
latest_version: '',
|
||||
latest_package_identifier: '',
|
||||
org: pluginManifest.author,
|
||||
author: pluginManifest.author,
|
||||
label: pluginManifest.label,
|
||||
brief: pluginManifest.description,
|
||||
description: pluginManifest.description,
|
||||
icon: pluginManifest.icon,
|
||||
verified: pluginManifest.verified,
|
||||
introduction: '',
|
||||
|
|
@ -22,14 +24,17 @@ export const pluginManifestToCardPluginProps = (pluginManifest: PluginDeclaratio
|
|||
endpoint: {
|
||||
settings: [],
|
||||
},
|
||||
tags: [],
|
||||
tags: pluginManifest.tags.map(tag => ({ name: tag })),
|
||||
badges: [],
|
||||
verification: { authorized_category: 'langgenius' },
|
||||
from: 'package',
|
||||
}
|
||||
}
|
||||
|
||||
export const pluginManifestInMarketToPluginProps = (pluginManifest: PluginManifestInMarket): Plugin => {
|
||||
return {
|
||||
plugin_id: pluginManifest.plugin_unique_identifier,
|
||||
type: pluginManifest.category,
|
||||
type: pluginManifest.category as Plugin['type'],
|
||||
category: pluginManifest.category,
|
||||
name: pluginManifest.name,
|
||||
version: pluginManifest.latest_version,
|
||||
|
|
@ -38,6 +43,7 @@ export const pluginManifestInMarketToPluginProps = (pluginManifest: PluginManife
|
|||
org: pluginManifest.org,
|
||||
label: pluginManifest.label,
|
||||
brief: pluginManifest.brief,
|
||||
description: pluginManifest.brief,
|
||||
icon: pluginManifest.icon,
|
||||
verified: true,
|
||||
introduction: pluginManifest.introduction,
|
||||
|
|
@ -49,6 +55,7 @@ export const pluginManifestInMarketToPluginProps = (pluginManifest: PluginManife
|
|||
tags: [],
|
||||
badges: pluginManifest.badges,
|
||||
verification: isEmpty(pluginManifest.verification) ? { authorized_category: 'langgenius' } : pluginManifest.verification,
|
||||
from: pluginManifest.from,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ const EndpointModal: FC<Props> = ({
|
|||
|
||||
// Fix: Process boolean fields to ensure they are sent as proper boolean values
|
||||
const processedCredential = { ...tempCredential }
|
||||
formSchemas.forEach((field) => {
|
||||
formSchemas.forEach((field: any) => {
|
||||
if (field.type === 'boolean' && processedCredential[field.name] !== undefined) {
|
||||
const value = processedCredential[field.name]
|
||||
if (typeof value === 'string')
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { useTranslation } from 'react-i18next'
|
|||
import type {
|
||||
DefaultModel,
|
||||
FormValue,
|
||||
ModelFeatureEnum,
|
||||
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { ModelStatusEnum, ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
|
||||
|
|
@ -57,7 +58,7 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
|
|||
const { isAPIKeySet } = useProviderContext()
|
||||
const [open, setOpen] = useState(false)
|
||||
const scopeArray = scope.split('&')
|
||||
const scopeFeatures = useMemo(() => {
|
||||
const scopeFeatures = useMemo((): ModelFeatureEnum[] => {
|
||||
if (scopeArray.includes('all'))
|
||||
return []
|
||||
return scopeArray.filter(item => ![
|
||||
|
|
@ -67,7 +68,7 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
|
|||
ModelTypeEnum.moderation,
|
||||
ModelTypeEnum.speech2text,
|
||||
ModelTypeEnum.tts,
|
||||
].includes(item as ModelTypeEnum))
|
||||
].includes(item as ModelTypeEnum)).map(item => item as ModelFeatureEnum)
|
||||
}, [scopeArray])
|
||||
|
||||
const { data: textGenerationList } = useModelList(ModelTypeEnum.textGeneration)
|
||||
|
|
|
|||
|
|
@ -56,10 +56,10 @@ const SwrInitializer = ({
|
|||
}
|
||||
|
||||
const redirectUrl = resolvePostLoginRedirect(searchParams)
|
||||
if (redirectUrl)
|
||||
if (redirectUrl) {
|
||||
location.replace(redirectUrl)
|
||||
else
|
||||
router.replace(pathname)
|
||||
return
|
||||
}
|
||||
|
||||
setInit(true)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import I18n from '@/context/i18n'
|
|||
import { getLanguage } from '@/i18n-config/language'
|
||||
import { useStore as useLabelStore } from '@/app/components/tools/labels/store'
|
||||
import { fetchLabelList } from '@/service/tools'
|
||||
import { renderI18nObject } from '@/i18n-config'
|
||||
|
||||
type Props = {
|
||||
value: string
|
||||
|
|
@ -55,14 +56,24 @@ const Category = ({
|
|||
<Apps02 className='mr-2 h-4 w-4 shrink-0' />
|
||||
{t('tools.type.all')}
|
||||
</div>
|
||||
{labelList.map(label => (
|
||||
<div key={label.name} title={label.label[language]} className={cn('mb-0.5 flex cursor-pointer items-center overflow-hidden truncate rounded-lg p-1 pl-3 text-sm leading-5 text-gray-700 hover:bg-white', value === label.name && '!bg-white font-medium !text-primary-600')} onClick={() => onSelect(label.name)}>
|
||||
<div className='mr-2 h-4 w-4 shrink-0'>
|
||||
<Icon active={value === label.name} svgString={label.icon} />
|
||||
{labelList.map((label) => {
|
||||
const labelText = typeof label.label === 'string'
|
||||
? label.label
|
||||
: (label.label ? renderI18nObject(label.label, language) : '')
|
||||
return (
|
||||
<div
|
||||
key={label.name}
|
||||
title={labelText}
|
||||
className={cn('mb-0.5 flex cursor-pointer items-center overflow-hidden truncate rounded-lg p-1 pl-3 text-sm leading-5 text-gray-700 hover:bg-white', value === label.name && '!bg-white font-medium !text-primary-600')}
|
||||
onClick={() => onSelect(label.name)}
|
||||
>
|
||||
<div className='mr-2 h-4 w-4 shrink-0'>
|
||||
<Icon active={value === label.name} svgString={label.icon || ''} />
|
||||
</div>
|
||||
{labelText}
|
||||
</div>
|
||||
{label.label[language]}
|
||||
</div>
|
||||
))}
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
} from '@remixicon/react'
|
||||
import { useMount } from 'ahooks'
|
||||
import type { Collection, CustomCollectionBackend, Tool } from '../types'
|
||||
import type { CollectionType } from '../types'
|
||||
import Type from './type'
|
||||
import Category from './category'
|
||||
import Tools from './tools'
|
||||
|
|
@ -129,7 +130,7 @@ const AddToolModal: FC<Props> = ({
|
|||
const nexModelConfig = produce(modelConfig, (draft: ModelConfig) => {
|
||||
draft.agentConfig.tools.push({
|
||||
provider_id: collection.id || collection.name,
|
||||
provider_type: collection.type,
|
||||
provider_type: collection.type as CollectionType,
|
||||
provider_name: collection.name,
|
||||
tool_name: tool.name,
|
||||
tool_label: tool.label[locale] || tool.label[locale.replaceAll('-', '_')],
|
||||
|
|
|
|||
|
|
@ -23,6 +23,14 @@ import type { Tool } from '@/app/components/tools/types'
|
|||
import { CollectionType } from '@/app/components/tools/types'
|
||||
import type { AgentTool } from '@/types/app'
|
||||
import { MAX_TOOLS_NUM } from '@/config'
|
||||
import type { TypeWithI18N } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { renderI18nObject } from '@/i18n-config'
|
||||
|
||||
const resolveI18nText = (value: TypeWithI18N | string | undefined, language: string): string => {
|
||||
if (!value)
|
||||
return ''
|
||||
return typeof value === 'string' ? value : renderI18nObject(value, language)
|
||||
}
|
||||
|
||||
type ToolsProps = {
|
||||
showWorkflowEmpty: boolean
|
||||
|
|
@ -53,7 +61,7 @@ const Blocks = ({
|
|||
className='group mb-1 last-of-type:mb-0'
|
||||
>
|
||||
<div className='flex h-[22px] w-full items-center justify-between pl-3 pr-1 text-xs font-medium text-gray-500'>
|
||||
{toolWithProvider.label[language]}
|
||||
{resolveI18nText(toolWithProvider.label, language)}
|
||||
<a className='hidden cursor-pointer items-center group-hover:flex' href={`${basePath}/tools?category=${toolWithProvider.type}`} target='_blank'>{t('tools.addToolModal.manageInTools')}<ArrowUpRight className='ml-0.5 h-3 w-3' /></a>
|
||||
</div>
|
||||
{list.map((tool) => {
|
||||
|
|
@ -62,7 +70,7 @@ const Blocks = ({
|
|||
return ''
|
||||
return tool.labels.map((name) => {
|
||||
const label = labelList.find(item => item.name === name)
|
||||
return label?.label[language]
|
||||
return resolveI18nText(label?.label, language)
|
||||
}).filter(Boolean).join(', ')
|
||||
})()
|
||||
const added = !!addedTools?.find(v => v.provider_id === toolWithProvider.id && v.provider_type === toolWithProvider.type && v.tool_name === tool.name)
|
||||
|
|
@ -79,8 +87,8 @@ const Blocks = ({
|
|||
type={BlockEnum.Tool}
|
||||
toolIcon={toolWithProvider.icon}
|
||||
/>
|
||||
<div className='mb-1 text-sm leading-5 text-gray-900'>{tool.label[language]}</div>
|
||||
<div className='text-xs leading-[18px] text-gray-700'>{tool.description[language]}</div>
|
||||
<div className='mb-1 text-sm leading-5 text-gray-900'>{resolveI18nText(tool.label, language)}</div>
|
||||
<div className='text-xs leading-[18px] text-gray-700'>{resolveI18nText(tool.description, language)}</div>
|
||||
{tool.labels?.length > 0 && (
|
||||
<div className='mt-1 flex shrink-0 items-center'>
|
||||
<div className='relative flex w-full items-center gap-1 rounded-md py-1 text-gray-500' title={labelContent}>
|
||||
|
|
@ -98,7 +106,7 @@ const Blocks = ({
|
|||
type={BlockEnum.Tool}
|
||||
toolIcon={toolWithProvider.icon}
|
||||
/>
|
||||
<div className={cn('grow truncate text-sm text-gray-900', needAuth && 'opacity-30')}>{tool.label[language]}</div>
|
||||
<div className={cn('grow truncate text-sm text-gray-900', needAuth && 'opacity-30')}>{resolveI18nText(tool.label, language)}</div>
|
||||
{!needAuth && added && (
|
||||
<div className='flex items-center gap-1 rounded-[6px] border border-gray-100 bg-white px-2 py-[3px] text-xs font-medium leading-[18px] text-gray-300'>
|
||||
<Check className='h-3 w-3' />
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import ConversationVariableModal from './conversation-variable-modal'
|
|||
import { useChat } from './hooks'
|
||||
import type { ChatWrapperRefType } from './index'
|
||||
import Chat from '@/app/components/base/chat/chat'
|
||||
import type { ChatItem, ChatItemInTree, OnSend } from '@/app/components/base/chat/types'
|
||||
import type { ChatItem, OnSend } from '@/app/components/base/chat/types'
|
||||
import { useFeatures } from '@/app/components/base/features/hooks'
|
||||
import {
|
||||
fetchSuggestedQuestions,
|
||||
|
|
@ -116,7 +116,7 @@ const ChatWrapper = (
|
|||
)
|
||||
}, [handleSend, workflowStore, conversationId, chatList, appDetail])
|
||||
|
||||
const doRegenerate = useCallback((chatItem: ChatItemInTree, editedQuestion?: { message: string, files?: FileEntity[] }) => {
|
||||
const doRegenerate = useCallback((chatItem: ChatItem, editedQuestion?: { message: string, files?: FileEntity[] }) => {
|
||||
const question = editedQuestion ? chatItem : chatList.find(item => item.id === chatItem.parentMessageId)!
|
||||
const parentAnswer = chatList.find(item => item.id === question.parentMessageId)
|
||||
doSend(editedQuestion ? editedQuestion.message : question.content,
|
||||
|
|
|
|||
|
|
@ -4,18 +4,18 @@ import { cloneDeep } from 'lodash-es'
|
|||
import type {
|
||||
Edge,
|
||||
Node,
|
||||
} from '../types'
|
||||
} from '@/app/components/workflow/types'
|
||||
import {
|
||||
BlockEnum,
|
||||
} from '../types'
|
||||
} from '@/app/components/workflow/types'
|
||||
import {
|
||||
CUSTOM_NODE,
|
||||
NODE_LAYOUT_HORIZONTAL_PADDING,
|
||||
NODE_LAYOUT_VERTICAL_PADDING,
|
||||
} from '../constants'
|
||||
import { CUSTOM_ITERATION_START_NODE } from '../nodes/iteration-start/constants'
|
||||
import { CUSTOM_LOOP_START_NODE } from '../nodes/loop-start/constants'
|
||||
import type { CaseItem, IfElseNodeType } from '../nodes/if-else/types'
|
||||
} from '@/app/components/workflow/constants'
|
||||
import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants'
|
||||
import { CUSTOM_LOOP_START_NODE } from '@/app/components/workflow/nodes/loop-start/constants'
|
||||
import type { CaseItem, IfElseNodeType } from '@/app/components/workflow/nodes/if-else/types'
|
||||
|
||||
// Although the file name refers to Dagre, the implementation now relies on ELK's layered algorithm.
|
||||
// Keep the export signatures unchanged to minimise the blast radius while we migrate the layout stack.
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
export * from './node'
|
||||
export * from './edge'
|
||||
export * from './workflow-init'
|
||||
export * from './layout'
|
||||
export * from './elk-layout'
|
||||
export * from './common'
|
||||
export * from './tool'
|
||||
export * from './workflow'
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ const LocaleLayout = async ({
|
|||
[DatasetAttr.DATA_PUBLIC_ENABLE_WEBSITE_JINAREADER]: process.env.NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER,
|
||||
[DatasetAttr.DATA_PUBLIC_ENABLE_WEBSITE_FIRECRAWL]: process.env.NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL,
|
||||
[DatasetAttr.DATA_PUBLIC_ENABLE_WEBSITE_WATERCRAWL]: process.env.NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL,
|
||||
[DatasetAttr.DATA_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX]: process.env.NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX,
|
||||
[DatasetAttr.NEXT_PUBLIC_ZENDESK_WIDGET_KEY]: process.env.NEXT_PUBLIC_ZENDESK_WIDGET_KEY,
|
||||
[DatasetAttr.NEXT_PUBLIC_ZENDESK_FIELD_ID_ENVIRONMENT]: process.env.NEXT_PUBLIC_ZENDESK_FIELD_ID_ENVIRONMENT,
|
||||
[DatasetAttr.NEXT_PUBLIC_ZENDESK_FIELD_ID_VERSION]: process.env.NEXT_PUBLIC_ZENDESK_FIELD_ID_VERSION,
|
||||
|
|
|
|||
|
|
@ -135,8 +135,8 @@ const NormalForm = () => {
|
|||
{!systemFeatures.branding.enabled && <p className='body-md-regular mt-2 text-text-tertiary'>{t('login.joinTipStart')}{workspaceName}{t('login.joinTipEnd')}</p>}
|
||||
</div>
|
||||
: <div className="mx-auto w-full">
|
||||
<h2 className="title-4xl-semi-bold text-text-primary">{t('login.pageTitle')}</h2>
|
||||
{!systemFeatures.branding.enabled && <p className='body-md-regular mt-2 text-text-tertiary'>{t('login.welcome')}</p>}
|
||||
<h2 className="title-4xl-semi-bold text-text-primary">{systemFeatures.branding.enabled ? t('login.pageTitleForE') : t('login.pageTitle')}</h2>
|
||||
<p className='body-md-regular mt-2 text-text-tertiary'>{t('login.welcome')}</p>
|
||||
</div>}
|
||||
<div className="relative">
|
||||
<div className="mt-6 flex flex-col gap-3">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { OAUTH_AUTHORIZE_PENDING_KEY, REDIRECT_URL_KEY } from '@/app/account/oauth/authorize/page'
|
||||
import { OAUTH_AUTHORIZE_PENDING_KEY, REDIRECT_URL_KEY } from '@/app/account/oauth/authorize/constants'
|
||||
import dayjs from 'dayjs'
|
||||
import type { ReadonlyURLSearchParams } from 'next/navigation'
|
||||
|
||||
|
|
|
|||
|
|
@ -375,6 +375,11 @@ export const ENABLE_WEBSITE_WATERCRAWL = getBooleanConfig(
|
|||
DatasetAttr.DATA_PUBLIC_ENABLE_WEBSITE_WATERCRAWL,
|
||||
false,
|
||||
)
|
||||
export const ENABLE_SINGLE_DOLLAR_LATEX = getBooleanConfig(
|
||||
process.env.NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX,
|
||||
DatasetAttr.DATA_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX,
|
||||
false,
|
||||
)
|
||||
|
||||
export const VALUE_SELECTOR_DELIMITER = '@@@'
|
||||
|
||||
|
|
|
|||
|
|
@ -212,6 +212,8 @@ const DebugConfigurationContext = createContext<IDebugConfiguration>({
|
|||
prompt_template: '',
|
||||
prompt_variables: [],
|
||||
},
|
||||
chat_prompt_config: DEFAULT_CHAT_PROMPT_CONFIG,
|
||||
completion_prompt_config: DEFAULT_COMPLETION_PROMPT_CONFIG,
|
||||
more_like_this: null,
|
||||
opening_statement: '',
|
||||
suggested_questions: [],
|
||||
|
|
@ -222,6 +224,14 @@ const DebugConfigurationContext = createContext<IDebugConfiguration>({
|
|||
suggested_questions_after_answer: null,
|
||||
retriever_resource: null,
|
||||
annotation_reply: null,
|
||||
external_data_tools: [],
|
||||
system_parameters: {
|
||||
audio_file_size_limit: 0,
|
||||
file_size_limit: 0,
|
||||
image_file_size_limit: 0,
|
||||
video_file_size_limit: 0,
|
||||
workflow_file_upload_limit: 0,
|
||||
},
|
||||
dataSets: [],
|
||||
agentConfig: DEFAULT_AGENT_SETTING,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ export NEXT_PUBLIC_MAX_TOOLS_NUM=${MAX_TOOLS_NUM}
|
|||
export NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER=${ENABLE_WEBSITE_JINAREADER:-true}
|
||||
export NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL=${ENABLE_WEBSITE_FIRECRAWL:-true}
|
||||
export NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL=${ENABLE_WEBSITE_WATERCRAWL:-true}
|
||||
export NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX=${NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX:-false}
|
||||
export NEXT_PUBLIC_LOOP_NODE_MAX_COUNT=${LOOP_NODE_MAX_COUNT}
|
||||
export NEXT_PUBLIC_MAX_PARALLEL_LIMIT=${MAX_PARALLEL_LIMIT}
|
||||
export NEXT_PUBLIC_MAX_ITERATIONS_NUM=${MAX_ITERATIONS_NUM}
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ const translation = {
|
|||
noAccount: 'Haben Sie kein Konto?',
|
||||
verifyMail: 'Fahren Sie mit dem Bestätigungscode fort',
|
||||
},
|
||||
pageTitleForE: 'Hey, lass uns anfangen!',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
const translation = {
|
||||
pageTitle: 'Log in to Dify',
|
||||
pageTitleForE: 'Hey, let\'s get started!',
|
||||
welcome: '👋 Welcome! Please log in to get started.',
|
||||
email: 'Email address',
|
||||
emailPlaceholder: 'Your email',
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ const translation = {
|
|||
welcome: '👋 ¡Bienvenido! Por favor, completa los detalles para comenzar.',
|
||||
verifyMail: 'Continuar con el código de verificación',
|
||||
},
|
||||
pageTitleForE: '¡Hola, vamos a empezar!',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ const translation = {
|
|||
noAccount: 'حساب کاربری ندارید؟',
|
||||
verifyMail: 'ادامه با کد تأیید',
|
||||
},
|
||||
pageTitleForE: 'هی، بیا شروع کنیم!',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ const translation = {
|
|||
verifyMail: 'Continuez avec le code de vérification',
|
||||
createAccount: 'Créez votre compte',
|
||||
},
|
||||
pageTitleForE: 'Hé, commençons !',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ const translation = {
|
|||
welcome: '👋 स्वागत है! कृपया शुरू करने के लिए विवरण भरें।',
|
||||
haveAccount: 'क्या आपका पहले से एक खाता है?',
|
||||
},
|
||||
pageTitleForE: 'अरे, चलो शुरू करें!',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ const translation = {
|
|||
noAccount: 'Tidak punya akun?',
|
||||
welcome: '👋 Selamat datang! Silakan isi detail untuk memulai.',
|
||||
},
|
||||
pageTitleForE: 'Hei, ayo kita mulai!',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@ const translation = {
|
|||
signUp: 'Iscriviti',
|
||||
welcome: '👋 Benvenuto! Per favore compila i dettagli per iniziare.',
|
||||
},
|
||||
pageTitleForE: 'Ehi, cominciamo!',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
const translation = {
|
||||
pageTitle: 'Dify にログイン',
|
||||
pageTitleForE: 'はじめましょう!',
|
||||
welcome: '👋 ようこそ!まずはログインしてご利用ください。',
|
||||
email: 'メールアドレス',
|
||||
emailPlaceholder: 'メールアドレスを入力してください',
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ const translation = {
|
|||
noAccount: '계정이 없으신가요?',
|
||||
welcome: '👋 환영합니다! 시작하려면 세부 정보를 입력해 주세요.',
|
||||
},
|
||||
pageTitleForE: '이봐, 시작하자!',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ const translation = {
|
|||
haveAccount: 'Masz już konto?',
|
||||
welcome: '👋 Witaj! Proszę wypełnić szczegóły, aby rozpocząć.',
|
||||
},
|
||||
pageTitleForE: 'Hej, zaczynajmy!',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ const translation = {
|
|||
signUp: 'Inscreva-se',
|
||||
welcome: '👋 Bem-vindo! Por favor, preencha os detalhes para começar.',
|
||||
},
|
||||
pageTitleForE: 'Ei, vamos começar!',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ const translation = {
|
|||
createAccount: 'Creează-ți contul',
|
||||
welcome: '👋 Buna! Te rugăm să completezi detaliile pentru a începe.',
|
||||
},
|
||||
pageTitleForE: 'Hei, hai să începem!',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ const translation = {
|
|||
verifyMail: 'Продолжите с кодом проверки',
|
||||
welcome: '👋 Добро пожаловать! Пожалуйста, заполните данные, чтобы начать.',
|
||||
},
|
||||
pageTitleForE: 'Привет, давай начнем!',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ const translation = {
|
|||
noAccount: 'Nimate računa?',
|
||||
welcome: '👋 Dobrodošli! Prosimo, izpolnite podatke, da začnete.',
|
||||
},
|
||||
pageTitleForE: 'Hej, začnimo!',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ const translation = {
|
|||
verifyMail: 'โปรดดำเนินการต่อด้วยรหัสการตรวจสอบ',
|
||||
haveAccount: 'มีบัญชีอยู่แล้วใช่ไหม?',
|
||||
},
|
||||
pageTitleForE: 'เฮ้ เรามาเริ่มกันเถอะ!',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ const translation = {
|
|||
haveAccount: 'Zaten bir hesabınız var mı?',
|
||||
welcome: '👋 Hoş geldiniz! Başlamak için lütfen detayları doldurun.',
|
||||
},
|
||||
pageTitleForE: 'Hey, haydi başlayalım!',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ const translation = {
|
|||
noAccount: 'Не маєте облікового запису?',
|
||||
welcome: '👋 Ласкаво просимо! Будь ласка, заповніть деталі, щоб почати.',
|
||||
},
|
||||
pageTitleForE: 'Гей, давай почнемо!',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ const translation = {
|
|||
verifyMail: 'Tiếp tục với mã xác minh',
|
||||
welcome: '👋 Chào mừng! Vui lòng điền vào các chi tiết để bắt đầu.',
|
||||
},
|
||||
pageTitleForE: 'Này, hãy bắt đầu nào!',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
const translation = {
|
||||
pageTitle: '登录 Dify',
|
||||
pageTitleForE: '嗨,近来可好',
|
||||
welcome: '👋 欢迎!请登录以开始使用。',
|
||||
email: '邮箱',
|
||||
emailPlaceholder: '输入邮箱地址',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
const translation = {
|
||||
pageTitle: '嗨,近來可好',
|
||||
pageTitleForE: '嗨,近來可好',
|
||||
welcome: '👋 歡迎來到 Dify, 登入以繼續',
|
||||
email: '郵箱',
|
||||
emailPlaceholder: '輸入郵箱地址',
|
||||
|
|
|
|||
|
|
@ -344,6 +344,8 @@ export type WebsiteCrawlInfo = {
|
|||
description: string
|
||||
source_url: string
|
||||
title: string
|
||||
provider?: string
|
||||
job_id?: string
|
||||
}
|
||||
|
||||
export type OnlineDocumentInfo = {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import type {
|
|||
MetadataFilteringModeEnum,
|
||||
} from '@/app/components/workflow/nodes/knowledge-retrieval/types'
|
||||
import type { ModelConfig as NodeModelConfig } from '@/app/components/workflow/types'
|
||||
import type { ExternalDataTool } from '@/models/common'
|
||||
export type Inputs = Record<string, string | number | object | boolean>
|
||||
|
||||
export enum PromptMode {
|
||||
|
|
@ -136,6 +137,8 @@ export type ModelConfig = {
|
|||
chat_prompt_config?: ChatPromptConfig | null
|
||||
completion_prompt_config?: CompletionPromptConfig | null
|
||||
configs: PromptConfig
|
||||
chat_prompt_config?: ChatPromptConfig | null
|
||||
completion_prompt_config?: CompletionPromptConfig | null
|
||||
opening_statement: string | null
|
||||
more_like_this: MoreLikeThisConfig | null
|
||||
suggested_questions: string[] | null
|
||||
|
|
@ -146,6 +149,14 @@ export type ModelConfig = {
|
|||
retriever_resource: RetrieverResourceConfig | null
|
||||
sensitive_word_avoidance: ModerationConfig | null
|
||||
annotation_reply: AnnotationReplyConfig | null
|
||||
external_data_tools?: ExternalDataTool[] | null
|
||||
system_parameters: {
|
||||
audio_file_size_limit: number
|
||||
file_size_limit: number
|
||||
image_file_size_limit: number
|
||||
video_file_size_limit: number
|
||||
workflow_file_upload_limit: number
|
||||
}
|
||||
dataSets: any[]
|
||||
agentConfig: AgentConfig
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
"dev": "cross-env NODE_OPTIONS='--inspect' next dev --turbopack",
|
||||
"build": "next build",
|
||||
"build:docker": "next build && node scripts/optimize-standalone.js",
|
||||
"start": "cp -r .next/static .next/standalone/.next/static && cp -r public .next/standalone/public && cross-env PORT=$npm_config_port HOSTNAME=$npm_config_host node .next/standalone/server.js",
|
||||
"start": "node ./scripts/copy-and-start.mjs",
|
||||
"lint": "eslint --cache --cache-location node_modules/.cache/eslint/.eslint-cache",
|
||||
"lint:fix": "eslint --cache --cache-location node_modules/.cache/eslint/.eslint-cache --fix",
|
||||
"lint:quiet": "eslint --cache --cache-location node_modules/.cache/eslint/.eslint-cache --quiet",
|
||||
|
|
@ -148,7 +148,7 @@
|
|||
"@babel/core": "^7.28.4",
|
||||
"@chromatic-com/storybook": "^4.1.1",
|
||||
"@eslint-react/eslint-plugin": "^1.53.1",
|
||||
"@happy-dom/jest-environment": "^20.0.7",
|
||||
"@happy-dom/jest-environment": "^20.0.8",
|
||||
"@mdx-js/loader": "^3.1.1",
|
||||
"@mdx-js/react": "^3.1.1",
|
||||
"@next/bundle-analyzer": "15.5.4",
|
||||
|
|
|
|||
|
|
@ -368,7 +368,7 @@ importers:
|
|||
specifier: ^1.53.1
|
||||
version: 1.53.1(eslint@9.38.0(jiti@2.6.1))(ts-api-utils@2.1.0(typescript@5.9.3))(typescript@5.9.3)
|
||||
'@happy-dom/jest-environment':
|
||||
specifier: ^20.0.7
|
||||
specifier: ^20.0.8
|
||||
version: 20.0.8(@jest/environment@29.7.0)(@jest/fake-timers@29.7.0)(@jest/types@29.6.3)(jest-mock@29.7.0)(jest-util@29.7.0)
|
||||
'@mdx-js/loader':
|
||||
specifier: ^3.1.1
|
||||
|
|
@ -706,6 +706,10 @@ packages:
|
|||
resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/helper-validator-identifier@7.28.5':
|
||||
resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/helper-validator-option@7.27.1':
|
||||
resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
|
@ -723,6 +727,11 @@ packages:
|
|||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
|
||||
'@babel/parser@7.28.5':
|
||||
resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
|
||||
'@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1':
|
||||
resolution: {integrity: sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
|
@ -1256,6 +1265,10 @@ packages:
|
|||
resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/types@7.28.5':
|
||||
resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@bcoe/v8-coverage@0.2.3':
|
||||
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
|
||||
|
||||
|
|
@ -3409,8 +3422,8 @@ packages:
|
|||
'@types/node@18.15.0':
|
||||
resolution: {integrity: sha512-z6nr0TTEOBGkzLGmbypWOGnpSpSIBorEhC4L+4HeQ2iezKCi4f77kyslRwvHeNitymGQ+oFyIWGP96l/DPSV9w==}
|
||||
|
||||
'@types/node@20.19.20':
|
||||
resolution: {integrity: sha512-2Q7WS25j4pS1cS8yw3d6buNCVJukOTeQ39bAnwR6sOJbaxvyCGebzTMypDFN82CxBLnl+lSWVdCCWbRY6y9yZQ==}
|
||||
'@types/node@20.19.23':
|
||||
resolution: {integrity: sha512-yIdlVVVHXpmqRhtyovZAcSy0MiPcYWGkoO4CGe/+jpP0hmNuihm4XhHbADpK++MsiLHP5MVlv+bcgdF99kSiFQ==}
|
||||
|
||||
'@types/papaparse@5.3.16':
|
||||
resolution: {integrity: sha512-T3VuKMC2H0lgsjI9buTB3uuKj3EMD2eap1MOuEQuBQ44EnDx/IkGhU6EwiTf9zG3za4SKlmwKAImdDKdNnCsXg==}
|
||||
|
|
@ -6294,6 +6307,9 @@ packages:
|
|||
magic-string@0.30.19:
|
||||
resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==}
|
||||
|
||||
magic-string@0.30.21:
|
||||
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||
|
||||
magicast@0.3.5:
|
||||
resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==}
|
||||
|
||||
|
|
@ -8905,6 +8921,8 @@ snapshots:
|
|||
|
||||
'@babel/helper-validator-identifier@7.27.1': {}
|
||||
|
||||
'@babel/helper-validator-identifier@7.28.5': {}
|
||||
|
||||
'@babel/helper-validator-option@7.27.1': {}
|
||||
|
||||
'@babel/helper-wrap-function@7.27.1':
|
||||
|
|
@ -8924,6 +8942,10 @@ snapshots:
|
|||
dependencies:
|
||||
'@babel/types': 7.28.4
|
||||
|
||||
'@babel/parser@7.28.5':
|
||||
dependencies:
|
||||
'@babel/types': 7.28.5
|
||||
|
||||
'@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.28.4)':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.4
|
||||
|
|
@ -9594,6 +9616,11 @@ snapshots:
|
|||
'@babel/helper-string-parser': 7.27.1
|
||||
'@babel/helper-validator-identifier': 7.27.1
|
||||
|
||||
'@babel/types@7.28.5':
|
||||
dependencies:
|
||||
'@babel/helper-string-parser': 7.27.1
|
||||
'@babel/helper-validator-identifier': 7.28.5
|
||||
|
||||
'@bcoe/v8-coverage@0.2.3': {}
|
||||
|
||||
'@braintree/sanitize-url@7.1.1': {}
|
||||
|
|
@ -12011,7 +12038,7 @@ snapshots:
|
|||
|
||||
'@types/node@18.15.0': {}
|
||||
|
||||
'@types/node@20.19.20':
|
||||
'@types/node@20.19.23':
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
|
||||
|
|
@ -12264,7 +12291,7 @@ snapshots:
|
|||
|
||||
'@vue/compiler-core@3.5.17':
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.4
|
||||
'@babel/parser': 7.28.5
|
||||
'@vue/shared': 3.5.17
|
||||
entities: 4.5.0
|
||||
estree-walker: 2.0.2
|
||||
|
|
@ -12277,13 +12304,13 @@ snapshots:
|
|||
|
||||
'@vue/compiler-sfc@3.5.17':
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.4
|
||||
'@babel/parser': 7.28.5
|
||||
'@vue/compiler-core': 3.5.17
|
||||
'@vue/compiler-dom': 3.5.17
|
||||
'@vue/compiler-ssr': 3.5.17
|
||||
'@vue/shared': 3.5.17
|
||||
estree-walker: 2.0.2
|
||||
magic-string: 0.30.19
|
||||
magic-string: 0.30.21
|
||||
postcss: 8.5.6
|
||||
source-map-js: 1.2.1
|
||||
|
||||
|
|
@ -14467,7 +14494,7 @@ snapshots:
|
|||
|
||||
happy-dom@20.0.8:
|
||||
dependencies:
|
||||
'@types/node': 20.19.20
|
||||
'@types/node': 20.19.23
|
||||
'@types/whatwg-mimetype': 3.0.2
|
||||
whatwg-mimetype: 3.0.0
|
||||
|
||||
|
|
@ -15478,6 +15505,10 @@ snapshots:
|
|||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
magic-string@0.30.21:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
magicast@0.3.5:
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.4
|
||||
|
|
|
|||
|
|
@ -0,0 +1,115 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* This script copies static files to the target directory and starts the server.
|
||||
* It is intended to be used as a replacement for `next start`.
|
||||
*/
|
||||
|
||||
import { cp, mkdir, stat } from 'node:fs/promises'
|
||||
import { spawn } from 'node:child_process'
|
||||
import path from 'node:path'
|
||||
|
||||
// Configuration for directories to copy
|
||||
const DIRS_TO_COPY = [
|
||||
{
|
||||
src: path.join('.next', 'static'),
|
||||
dest: path.join('.next', 'standalone', '.next', 'static'),
|
||||
},
|
||||
{
|
||||
src: 'public',
|
||||
dest: path.join('.next', 'standalone', 'public'),
|
||||
},
|
||||
]
|
||||
|
||||
// Path to the server script
|
||||
const SERVER_SCRIPT_PATH = path.join('.next', 'standalone', 'server.js')
|
||||
|
||||
// Function to check if a path exists
|
||||
const pathExists = async (path) => {
|
||||
try {
|
||||
console.debug(`Checking if path exists: ${path}`)
|
||||
await stat(path)
|
||||
console.debug(`Path exists: ${path}`)
|
||||
return true
|
||||
}
|
||||
catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
console.warn(`Path does not exist: ${path}`)
|
||||
return false
|
||||
}
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Function to recursively copy directories
|
||||
const copyDir = async (src, dest) => {
|
||||
console.debug(`Copying directory from ${src} to ${dest}`)
|
||||
await cp(src, dest, { recursive: true })
|
||||
console.info(`Successfully copied ${src} to ${dest}`)
|
||||
}
|
||||
|
||||
// Process each directory copy operation
|
||||
const copyAllDirs = async () => {
|
||||
console.debug('Starting directory copy operations')
|
||||
for (const { src, dest } of DIRS_TO_COPY) {
|
||||
try {
|
||||
// Instead of pre-creating destination directory, we ensure parent directory exists
|
||||
const destParent = path.dirname(dest)
|
||||
console.debug(`Ensuring destination parent directory exists: ${destParent}`)
|
||||
await mkdir(destParent, { recursive: true })
|
||||
if (await pathExists(src)) {
|
||||
await copyDir(src, dest)
|
||||
}
|
||||
else {
|
||||
console.error(`Error: ${src} directory does not exist. This is a required build artifact.`)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.error(`Error processing ${src}:`, err.message)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
console.debug('Finished directory copy operations')
|
||||
}
|
||||
|
||||
// Run copy operations and start server
|
||||
const main = async () => {
|
||||
console.debug('Starting copy-and-start script')
|
||||
await copyAllDirs()
|
||||
|
||||
// Start server
|
||||
const port = process.env.npm_config_port || process.env.PORT || '3000'
|
||||
const host = process.env.npm_config_host || process.env.HOSTNAME || '0.0.0.0'
|
||||
|
||||
console.info(`Starting server on ${host}:${port}`)
|
||||
console.debug(`Server script path: ${SERVER_SCRIPT_PATH}`)
|
||||
console.debug(`Environment variables - PORT: ${port}, HOSTNAME: ${host}`)
|
||||
|
||||
const server = spawn(
|
||||
process.execPath,
|
||||
[SERVER_SCRIPT_PATH],
|
||||
{
|
||||
env: {
|
||||
...process.env,
|
||||
PORT: port,
|
||||
HOSTNAME: host,
|
||||
},
|
||||
stdio: 'inherit',
|
||||
},
|
||||
)
|
||||
|
||||
server.on('error', (err) => {
|
||||
console.error('Failed to start server:', err)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
server.on('exit', (code) => {
|
||||
console.debug(`Server exited with code: ${code}`)
|
||||
process.exit(code || 0)
|
||||
})
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error('Unexpected error:', err)
|
||||
process.exit(1)
|
||||
})
|
||||
|
|
@ -8,6 +8,7 @@ import type {
|
|||
} from '@/models/datasets'
|
||||
import type { UploadFileSetting } from '@/app/components/workflow/types'
|
||||
import type { AccessMode } from '@/models/access-control'
|
||||
import type { ExternalDataTool } from '@/models/common'
|
||||
|
||||
export enum Theme {
|
||||
light = 'light',
|
||||
|
|
@ -206,12 +207,12 @@ export type ModelConfig = {
|
|||
suggested_questions?: string[]
|
||||
pre_prompt: string
|
||||
prompt_type: PromptMode
|
||||
chat_prompt_config: ChatPromptConfig | {}
|
||||
completion_prompt_config: CompletionPromptConfig | {}
|
||||
chat_prompt_config?: ChatPromptConfig | null
|
||||
completion_prompt_config?: CompletionPromptConfig | null
|
||||
user_input_form: UserInputFormItem[]
|
||||
dataset_query_variable?: string
|
||||
more_like_this: {
|
||||
enabled?: boolean
|
||||
enabled: boolean
|
||||
}
|
||||
suggested_questions_after_answer: {
|
||||
enabled: boolean
|
||||
|
|
@ -237,13 +238,20 @@ export type ModelConfig = {
|
|||
strategy?: AgentStrategy
|
||||
tools: ToolItem[]
|
||||
}
|
||||
external_data_tools?: ExternalDataTool[]
|
||||
model: Model
|
||||
dataset_configs: DatasetConfigs
|
||||
file_upload?: {
|
||||
image: VisionSettings
|
||||
} & UploadFileSetting
|
||||
files?: VisionFile[]
|
||||
external_data_tools: any[]
|
||||
system_parameters: {
|
||||
audio_file_size_limit: number
|
||||
file_size_limit: number
|
||||
image_file_size_limit: number
|
||||
video_file_size_limit: number
|
||||
workflow_file_upload_limit: number
|
||||
}
|
||||
created_at?: number
|
||||
updated_at?: number
|
||||
}
|
||||
|
|
@ -361,6 +369,7 @@ export type App = {
|
|||
updated_at: number
|
||||
updated_by?: string
|
||||
}
|
||||
deleted_tools?: Array<{ id: string; tool_name: string }>
|
||||
/** access control */
|
||||
access_mode: AccessMode
|
||||
max_active_requests?: number | null
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@ export enum DatasetAttr {
|
|||
DATA_PUBLIC_ENABLE_WEBSITE_JINAREADER = 'data-public-enable-website-jinareader',
|
||||
DATA_PUBLIC_ENABLE_WEBSITE_FIRECRAWL = 'data-public-enable-website-firecrawl',
|
||||
DATA_PUBLIC_ENABLE_WEBSITE_WATERCRAWL = 'data-public-enable-website-watercrawl',
|
||||
DATA_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX = 'data-public-enable-single-dollar-latex',
|
||||
NEXT_PUBLIC_ZENDESK_WIDGET_KEY = 'next-public-zendesk-widget-key',
|
||||
NEXT_PUBLIC_ZENDESK_FIELD_ID_ENVIRONMENT = 'next-public-zendesk-field-id-environment',
|
||||
NEXT_PUBLIC_ZENDESK_FIELD_ID_VERSION = 'next-public-zendesk-field-id-version',
|
||||
|
|
|
|||
Loading…
Reference in New Issue