mirror of
https://github.com/langgenius/dify.git
synced 2024-11-16 11:42:29 +08:00
Feat: Support re-segmentation (#114)
Co-authored-by: John Wang <takatost@gmail.com> Co-authored-by: Jyong <718720800@qq.com> Co-authored-by: 金伟强 <iamjoel007@gmail.com>
This commit is contained in:
parent
f65a3ad1cc
commit
c67f626b66
|
@ -208,9 +208,10 @@ class DatasetDocumentListApi(Resource):
|
||||||
parser = reqparse.RequestParser()
|
parser = reqparse.RequestParser()
|
||||||
parser.add_argument('indexing_technique', type=str, choices=Dataset.INDEXING_TECHNIQUE_LIST, nullable=False,
|
parser.add_argument('indexing_technique', type=str, choices=Dataset.INDEXING_TECHNIQUE_LIST, nullable=False,
|
||||||
location='json')
|
location='json')
|
||||||
parser.add_argument('data_source', type=dict, required=True, nullable=True, location='json')
|
parser.add_argument('data_source', type=dict, required=False, location='json')
|
||||||
parser.add_argument('process_rule', type=dict, required=True, nullable=True, location='json')
|
parser.add_argument('process_rule', type=dict, required=False, location='json')
|
||||||
parser.add_argument('duplicate', type=bool, nullable=False, location='json')
|
parser.add_argument('duplicate', type=bool, nullable=False, location='json')
|
||||||
|
parser.add_argument('original_document_id', type=str, required=False, location='json')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if not dataset.indexing_technique and not args['indexing_technique']:
|
if not dataset.indexing_technique and not args['indexing_technique']:
|
||||||
|
@ -347,10 +348,12 @@ class DocumentIndexingStatusApi(DocumentResource):
|
||||||
|
|
||||||
completed_segments = DocumentSegment.query \
|
completed_segments = DocumentSegment.query \
|
||||||
.filter(DocumentSegment.completed_at.isnot(None),
|
.filter(DocumentSegment.completed_at.isnot(None),
|
||||||
DocumentSegment.document_id == str(document_id)) \
|
DocumentSegment.document_id == str(document_id),
|
||||||
|
DocumentSegment.status != 're_segment') \
|
||||||
.count()
|
.count()
|
||||||
total_segments = DocumentSegment.query \
|
total_segments = DocumentSegment.query \
|
||||||
.filter_by(document_id=str(document_id)) \
|
.filter(DocumentSegment.document_id == str(document_id),
|
||||||
|
DocumentSegment.status != 're_segment') \
|
||||||
.count()
|
.count()
|
||||||
|
|
||||||
document.completed_segments = completed_segments
|
document.completed_segments = completed_segments
|
||||||
|
|
|
@ -12,7 +12,7 @@ from events.dataset_event import dataset_was_deleted
|
||||||
from events.document_event import document_was_deleted
|
from events.document_event import document_was_deleted
|
||||||
from extensions.ext_database import db
|
from extensions.ext_database import db
|
||||||
from models.account import Account
|
from models.account import Account
|
||||||
from models.dataset import Dataset, Document, DatasetQuery, DatasetProcessRule, AppDatasetJoin
|
from models.dataset import Dataset, Document, DatasetQuery, DatasetProcessRule, AppDatasetJoin, DocumentSegment
|
||||||
from models.model import UploadFile
|
from models.model import UploadFile
|
||||||
from services.errors.account import NoPermissionError
|
from services.errors.account import NoPermissionError
|
||||||
from services.errors.dataset import DatasetNameDuplicateError
|
from services.errors.dataset import DatasetNameDuplicateError
|
||||||
|
@ -20,6 +20,7 @@ from services.errors.document import DocumentIndexingError
|
||||||
from services.errors.file import FileNotExistsError
|
from services.errors.file import FileNotExistsError
|
||||||
from tasks.deal_dataset_vector_index_task import deal_dataset_vector_index_task
|
from tasks.deal_dataset_vector_index_task import deal_dataset_vector_index_task
|
||||||
from tasks.document_indexing_task import document_indexing_task
|
from tasks.document_indexing_task import document_indexing_task
|
||||||
|
from tasks.document_indexing_update_task import document_indexing_update_task
|
||||||
|
|
||||||
|
|
||||||
class DatasetService:
|
class DatasetService:
|
||||||
|
@ -276,6 +277,14 @@ class DocumentService:
|
||||||
|
|
||||||
return document
|
return document
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_document_by_id(document_id: str) -> Optional[Document]:
|
||||||
|
document = db.session.query(Document).filter(
|
||||||
|
Document.id == document_id
|
||||||
|
).first()
|
||||||
|
|
||||||
|
return document
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_document_file_detail(file_id: str):
|
def get_document_file_detail(file_id: str):
|
||||||
file_detail = db.session.query(UploadFile). \
|
file_detail = db.session.query(UploadFile). \
|
||||||
|
@ -355,6 +364,9 @@ class DocumentService:
|
||||||
if dataset.indexing_technique == 'high_quality':
|
if dataset.indexing_technique == 'high_quality':
|
||||||
IndexBuilder.get_default_service_context(dataset.tenant_id)
|
IndexBuilder.get_default_service_context(dataset.tenant_id)
|
||||||
|
|
||||||
|
if 'original_document_id' in document_data and document_data["original_document_id"]:
|
||||||
|
document = DocumentService.update_document_with_dataset_id(dataset, document_data, account)
|
||||||
|
else:
|
||||||
# save process rule
|
# save process rule
|
||||||
if not dataset_process_rule:
|
if not dataset_process_rule:
|
||||||
process_rule = document_data["process_rule"]
|
process_rule = document_data["process_rule"]
|
||||||
|
@ -414,6 +426,76 @@ class DocumentService:
|
||||||
|
|
||||||
# trigger async task
|
# trigger async task
|
||||||
document_indexing_task.delay(document.dataset_id, document.id)
|
document_indexing_task.delay(document.dataset_id, document.id)
|
||||||
|
return document
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def update_document_with_dataset_id(dataset: Dataset, document_data: dict,
|
||||||
|
account: Account, dataset_process_rule: Optional[DatasetProcessRule] = None,
|
||||||
|
created_from: str = 'web'):
|
||||||
|
document = DocumentService.get_document(dataset.id, document_data["original_document_id"])
|
||||||
|
if document.display_status != 'available':
|
||||||
|
raise ValueError("Document is not available")
|
||||||
|
# save process rule
|
||||||
|
if 'process_rule' in document_data and document_data['process_rule']:
|
||||||
|
process_rule = document_data["process_rule"]
|
||||||
|
if process_rule["mode"] == "custom":
|
||||||
|
dataset_process_rule = DatasetProcessRule(
|
||||||
|
dataset_id=dataset.id,
|
||||||
|
mode=process_rule["mode"],
|
||||||
|
rules=json.dumps(process_rule["rules"]),
|
||||||
|
created_by=account.id
|
||||||
|
)
|
||||||
|
elif process_rule["mode"] == "automatic":
|
||||||
|
dataset_process_rule = DatasetProcessRule(
|
||||||
|
dataset_id=dataset.id,
|
||||||
|
mode=process_rule["mode"],
|
||||||
|
rules=json.dumps(DatasetProcessRule.AUTOMATIC_RULES),
|
||||||
|
created_by=account.id
|
||||||
|
)
|
||||||
|
db.session.add(dataset_process_rule)
|
||||||
|
db.session.commit()
|
||||||
|
document.dataset_process_rule_id = dataset_process_rule.id
|
||||||
|
# update document data source
|
||||||
|
if 'data_source' in document_data and document_data['data_source']:
|
||||||
|
file_name = ''
|
||||||
|
data_source_info = {}
|
||||||
|
if document_data["data_source"]["type"] == "upload_file":
|
||||||
|
file_id = document_data["data_source"]["info"]
|
||||||
|
file = db.session.query(UploadFile).filter(
|
||||||
|
UploadFile.tenant_id == dataset.tenant_id,
|
||||||
|
UploadFile.id == file_id
|
||||||
|
).first()
|
||||||
|
|
||||||
|
# raise error if file not found
|
||||||
|
if not file:
|
||||||
|
raise FileNotExistsError()
|
||||||
|
|
||||||
|
file_name = file.name
|
||||||
|
data_source_info = {
|
||||||
|
"upload_file_id": file_id,
|
||||||
|
}
|
||||||
|
document.data_source_type = document_data["data_source"]["type"]
|
||||||
|
document.data_source_info = json.dumps(data_source_info)
|
||||||
|
document.name = file_name
|
||||||
|
# update document to be waiting
|
||||||
|
document.indexing_status = 'waiting'
|
||||||
|
document.completed_at = None
|
||||||
|
document.processing_started_at = None
|
||||||
|
document.parsing_completed_at = None
|
||||||
|
document.cleaning_completed_at = None
|
||||||
|
document.splitting_completed_at = None
|
||||||
|
document.updated_at = datetime.datetime.utcnow()
|
||||||
|
document.created_from = created_from
|
||||||
|
db.session.add(document)
|
||||||
|
db.session.commit()
|
||||||
|
# update document segment
|
||||||
|
update_params = {
|
||||||
|
DocumentSegment.status: 're_segment'
|
||||||
|
}
|
||||||
|
DocumentSegment.query.filter_by(document_id=document.id).update(update_params)
|
||||||
|
db.session.commit()
|
||||||
|
# trigger async task
|
||||||
|
document_indexing_update_task.delay(document.dataset_id, document.id)
|
||||||
|
|
||||||
return document
|
return document
|
||||||
|
|
||||||
|
@ -443,6 +525,21 @@ class DocumentService:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def document_create_args_validate(cls, args: dict):
|
def document_create_args_validate(cls, args: dict):
|
||||||
|
if 'original_document_id' not in args or not args['original_document_id']:
|
||||||
|
DocumentService.data_source_args_validate(args)
|
||||||
|
DocumentService.process_rule_args_validate(args)
|
||||||
|
else:
|
||||||
|
if ('data_source' not in args and not args['data_source'])\
|
||||||
|
and ('process_rule' not in args and not args['process_rule']):
|
||||||
|
raise ValueError("Data source or Process rule is required")
|
||||||
|
else:
|
||||||
|
if 'data_source' in args and args['data_source']:
|
||||||
|
DocumentService.data_source_args_validate(args)
|
||||||
|
if 'process_rule' in args and args['process_rule']:
|
||||||
|
DocumentService.process_rule_args_validate(args)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def data_source_args_validate(cls, args: dict):
|
||||||
if 'data_source' not in args or not args['data_source']:
|
if 'data_source' not in args or not args['data_source']:
|
||||||
raise ValueError("Data source is required")
|
raise ValueError("Data source is required")
|
||||||
|
|
||||||
|
@ -459,6 +556,8 @@ class DocumentService:
|
||||||
if 'info' not in args['data_source'] or not args['data_source']['info']:
|
if 'info' not in args['data_source'] or not args['data_source']['info']:
|
||||||
raise ValueError("Data source info is required")
|
raise ValueError("Data source info is required")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def process_rule_args_validate(cls, args: dict):
|
||||||
if 'process_rule' not in args or not args['process_rule']:
|
if 'process_rule' not in args or not args['process_rule']:
|
||||||
raise ValueError("Process rule is required")
|
raise ValueError("Process rule is required")
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,6 @@ def clean_document_task(document_id: str, dataset_id: str):
|
||||||
index_node_ids = [segment.index_node_id for segment in segments]
|
index_node_ids = [segment.index_node_id for segment in segments]
|
||||||
|
|
||||||
# delete from vector index
|
# delete from vector index
|
||||||
if dataset.indexing_technique == "high_quality":
|
|
||||||
vector_index.del_nodes(index_node_ids)
|
vector_index.del_nodes(index_node_ids)
|
||||||
|
|
||||||
# delete from keyword index
|
# delete from keyword index
|
||||||
|
|
85
api/tasks/document_indexing_update_task.py
Normal file
85
api/tasks/document_indexing_update_task.py
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import datetime
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
|
||||||
|
import click
|
||||||
|
from celery import shared_task
|
||||||
|
from werkzeug.exceptions import NotFound
|
||||||
|
|
||||||
|
from core.index.keyword_table_index import KeywordTableIndex
|
||||||
|
from core.index.vector_index import VectorIndex
|
||||||
|
from core.indexing_runner import IndexingRunner, DocumentIsPausedException
|
||||||
|
from core.llm.error import ProviderTokenNotInitError
|
||||||
|
from extensions.ext_database import db
|
||||||
|
from models.dataset import Document, Dataset, DocumentSegment
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def document_indexing_update_task(dataset_id: str, document_id: str):
|
||||||
|
"""
|
||||||
|
Async update document
|
||||||
|
:param dataset_id:
|
||||||
|
:param document_id:
|
||||||
|
|
||||||
|
Usage: document_indexing_update_task.delay(dataset_id, document_id)
|
||||||
|
"""
|
||||||
|
logging.info(click.style('Start update document: {}'.format(document_id), fg='green'))
|
||||||
|
start_at = time.perf_counter()
|
||||||
|
|
||||||
|
document = db.session.query(Document).filter(
|
||||||
|
Document.id == document_id,
|
||||||
|
Document.dataset_id == dataset_id
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if not document:
|
||||||
|
raise NotFound('Document not found')
|
||||||
|
|
||||||
|
document.indexing_status = 'parsing'
|
||||||
|
document.processing_started_at = datetime.datetime.utcnow()
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# delete all document segment and index
|
||||||
|
try:
|
||||||
|
dataset = db.session.query(Dataset).filter(Dataset.id == dataset_id).first()
|
||||||
|
if not dataset:
|
||||||
|
raise Exception('Dataset not found')
|
||||||
|
|
||||||
|
vector_index = VectorIndex(dataset=dataset)
|
||||||
|
keyword_table_index = KeywordTableIndex(dataset=dataset)
|
||||||
|
|
||||||
|
segments = db.session.query(DocumentSegment).filter(DocumentSegment.document_id == document_id).all()
|
||||||
|
index_node_ids = [segment.index_node_id for segment in segments]
|
||||||
|
|
||||||
|
# delete from vector index
|
||||||
|
vector_index.del_nodes(index_node_ids)
|
||||||
|
|
||||||
|
# delete from keyword index
|
||||||
|
if index_node_ids:
|
||||||
|
keyword_table_index.del_nodes(index_node_ids)
|
||||||
|
|
||||||
|
for segment in segments:
|
||||||
|
db.session.delete(segment)
|
||||||
|
|
||||||
|
end_at = time.perf_counter()
|
||||||
|
logging.info(
|
||||||
|
click.style('Cleaned document when document update data source or process rule: {} latency: {}'.format(document_id, end_at - start_at), fg='green'))
|
||||||
|
except Exception:
|
||||||
|
logging.exception("Cleaned document when document update data source or process rule failed")
|
||||||
|
try:
|
||||||
|
indexing_runner = IndexingRunner()
|
||||||
|
indexing_runner.run(document)
|
||||||
|
end_at = time.perf_counter()
|
||||||
|
logging.info(click.style('update document: {} latency: {}'.format(document.id, end_at - start_at), fg='green'))
|
||||||
|
except DocumentIsPausedException:
|
||||||
|
logging.info(click.style('Document update paused, document id: {}'.format(document.id), fg='yellow'))
|
||||||
|
except ProviderTokenNotInitError as e:
|
||||||
|
document.indexing_status = 'error'
|
||||||
|
document.error = str(e.description)
|
||||||
|
document.stopped_at = datetime.datetime.utcnow()
|
||||||
|
db.session.commit()
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("consume update document failed")
|
||||||
|
document.indexing_status = 'error'
|
||||||
|
document.error = str(e)
|
||||||
|
document.stopped_at = datetime.datetime.utcnow()
|
||||||
|
db.session.commit()
|
|
@ -42,7 +42,6 @@ def remove_document_from_index_task(document_id: str):
|
||||||
keyword_table_index = KeywordTableIndex(dataset=dataset)
|
keyword_table_index = KeywordTableIndex(dataset=dataset)
|
||||||
|
|
||||||
# delete from vector index
|
# delete from vector index
|
||||||
if dataset.indexing_technique == "high_quality":
|
|
||||||
vector_index.del_doc(document.id)
|
vector_index.del_doc(document.id)
|
||||||
|
|
||||||
# delete from keyword index
|
# delete from keyword index
|
||||||
|
|
|
@ -21,7 +21,7 @@ export type AppCardProps = {
|
||||||
|
|
||||||
const AppCard = ({
|
const AppCard = ({
|
||||||
app,
|
app,
|
||||||
onDelete
|
onDelete,
|
||||||
}: AppCardProps) => {
|
}: AppCardProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { notify } = useContext(ToastContext)
|
const { notify } = useContext(ToastContext)
|
||||||
|
|
|
@ -3,13 +3,13 @@
|
||||||
import { useEffect, useRef } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
import useSWRInfinite from 'swr/infinite'
|
import useSWRInfinite from 'swr/infinite'
|
||||||
import { debounce } from 'lodash-es'
|
import { debounce } from 'lodash-es'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import AppCard from './AppCard'
|
import AppCard from './AppCard'
|
||||||
import NewAppCard from './NewAppCard'
|
import NewAppCard from './NewAppCard'
|
||||||
import { AppListResponse } from '@/models/app'
|
import type { AppListResponse } from '@/models/app'
|
||||||
import { fetchAppList } from '@/service/apps'
|
import { fetchAppList } from '@/service/apps'
|
||||||
import { useSelector } from '@/context/app-context'
|
import { useSelector } from '@/context/app-context'
|
||||||
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
|
|
||||||
const getKey = (pageIndex: number, previousPageData: AppListResponse) => {
|
const getKey = (pageIndex: number, previousPageData: AppListResponse) => {
|
||||||
if (!pageIndex || previousPageData.has_more)
|
if (!pageIndex || previousPageData.has_more)
|
||||||
|
@ -25,8 +25,8 @@ const Apps = () => {
|
||||||
const anchorRef = useRef<HTMLAnchorElement>(null)
|
const anchorRef = useRef<HTMLAnchorElement>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = `${t('app.title')} - Dify`;
|
document.title = `${t('app.title')} - Dify`
|
||||||
if(localStorage.getItem(NEED_REFRESH_APP_LIST_KEY) === '1') {
|
if (localStorage.getItem(NEED_REFRESH_APP_LIST_KEY) === '1') {
|
||||||
localStorage.removeItem(NEED_REFRESH_APP_LIST_KEY)
|
localStorage.removeItem(NEED_REFRESH_APP_LIST_KEY)
|
||||||
mutate()
|
mutate()
|
||||||
}
|
}
|
||||||
|
@ -41,10 +41,9 @@ const Apps = () => {
|
||||||
if (!loadingStateRef.current) {
|
if (!loadingStateRef.current) {
|
||||||
const { scrollTop, clientHeight } = pageContainerRef.current!
|
const { scrollTop, clientHeight } = pageContainerRef.current!
|
||||||
const anchorOffset = anchorRef.current!.offsetTop
|
const anchorOffset = anchorRef.current!.offsetTop
|
||||||
if (anchorOffset - scrollTop - clientHeight < 100) {
|
if (anchorOffset - scrollTop - clientHeight < 100)
|
||||||
setSize(size => size + 1)
|
setSize(size => size + 1)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}, 50)
|
}, 50)
|
||||||
|
|
||||||
pageContainerRef.current?.addEventListener('scroll', onScroll)
|
pageContainerRef.current?.addEventListener('scroll', onScroll)
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import React from 'react'
|
||||||
|
import Settings from '@/app/components/datasets/documents/detail/settings'
|
||||||
|
|
||||||
|
export type IProps = {
|
||||||
|
params: { datasetId: string; documentId: string }
|
||||||
|
}
|
||||||
|
|
||||||
|
const DocumentSettings = async ({
|
||||||
|
params: { datasetId, documentId },
|
||||||
|
}: IProps) => {
|
||||||
|
return (
|
||||||
|
<Settings datasetId={datasetId} documentId={documentId} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DocumentSettings
|
|
@ -164,7 +164,10 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
|
||||||
extraInfo={<ExtraInfo />}
|
extraInfo={<ExtraInfo />}
|
||||||
iconType='dataset'
|
iconType='dataset'
|
||||||
/>}
|
/>}
|
||||||
<DatasetDetailContext.Provider value={{ indexingTechnique: datasetRes?.indexing_technique }}>
|
<DatasetDetailContext.Provider value={{
|
||||||
|
indexingTechnique: datasetRes?.indexing_technique,
|
||||||
|
dataset: datasetRes,
|
||||||
|
}}>
|
||||||
<div className="bg-white grow">{children}</div>
|
<div className="bg-white grow">{children}</div>
|
||||||
</DatasetDetailContext.Provider>
|
</DatasetDetailContext.Provider>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,20 +1,17 @@
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useContext, useContextSelector } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import useSWR from 'swr'
|
|
||||||
import type { MouseEventHandler } from 'react'
|
import type { MouseEventHandler } from 'react'
|
||||||
import { useCallback, useState } from 'react'
|
import { useCallback, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import classNames from 'classnames'
|
||||||
import style from '../list.module.css'
|
import style from '../list.module.css'
|
||||||
import type { App } from '@/types/app'
|
|
||||||
import Confirm from '@/app/components/base/confirm'
|
import Confirm from '@/app/components/base/confirm'
|
||||||
import { ToastContext } from '@/app/components/base/toast'
|
import { ToastContext } from '@/app/components/base/toast'
|
||||||
import { deleteDataset, fetchDatasets } from '@/service/datasets'
|
import { deleteDataset } from '@/service/datasets'
|
||||||
import AppIcon from '@/app/components/base/app-icon'
|
import AppIcon from '@/app/components/base/app-icon'
|
||||||
import AppsContext from '@/context/app-context'
|
import type { DataSet } from '@/models/datasets'
|
||||||
import { DataSet } from '@/models/datasets'
|
|
||||||
import classNames from 'classnames'
|
|
||||||
|
|
||||||
export type DatasetCardProps = {
|
export type DatasetCardProps = {
|
||||||
dataset: DataSet
|
dataset: DataSet
|
||||||
|
@ -23,7 +20,7 @@ export type DatasetCardProps = {
|
||||||
|
|
||||||
const DatasetCard = ({
|
const DatasetCard = ({
|
||||||
dataset,
|
dataset,
|
||||||
onDelete
|
onDelete,
|
||||||
}: DatasetCardProps) => {
|
}: DatasetCardProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { notify } = useContext(ToastContext)
|
const { notify } = useContext(ToastContext)
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
import { useEffect, useRef } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
import useSWRInfinite from 'swr/infinite'
|
import useSWRInfinite from 'swr/infinite'
|
||||||
import { debounce } from 'lodash-es';
|
import { debounce } from 'lodash-es'
|
||||||
import { DataSetListResponse } from '@/models/datasets';
|
|
||||||
import NewDatasetCard from './NewDatasetCard'
|
import NewDatasetCard from './NewDatasetCard'
|
||||||
import DatasetCard from './DatasetCard';
|
import DatasetCard from './DatasetCard'
|
||||||
import { fetchDatasets } from '@/service/datasets';
|
import type { DataSetListResponse } from '@/models/datasets'
|
||||||
import { useSelector } from '@/context/app-context';
|
import { fetchDatasets } from '@/service/datasets'
|
||||||
|
import { useSelector } from '@/context/app-context'
|
||||||
|
|
||||||
const getKey = (pageIndex: number, previousPageData: DataSetListResponse) => {
|
const getKey = (pageIndex: number, previousPageData: DataSetListResponse) => {
|
||||||
if (!pageIndex || previousPageData.has_more)
|
if (!pageIndex || previousPageData.has_more)
|
||||||
|
@ -30,10 +30,9 @@ const Datasets = () => {
|
||||||
if (!loadingStateRef.current) {
|
if (!loadingStateRef.current) {
|
||||||
const { scrollTop, clientHeight } = pageContainerRef.current!
|
const { scrollTop, clientHeight } = pageContainerRef.current!
|
||||||
const anchorOffset = anchorRef.current!.offsetTop
|
const anchorOffset = anchorRef.current!.offsetTop
|
||||||
if (anchorOffset - scrollTop - clientHeight < 100) {
|
if (anchorOffset - scrollTop - clientHeight < 100)
|
||||||
setSize(size => size + 1)
|
setSize(size => size + 1)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}, 50)
|
}, 50)
|
||||||
|
|
||||||
pageContainerRef.current?.addEventListener('scroll', onScroll)
|
pageContainerRef.current?.addEventListener('scroll', onScroll)
|
||||||
|
@ -43,7 +42,7 @@ const Datasets = () => {
|
||||||
return (
|
return (
|
||||||
<nav className='grid content-start grid-cols-1 gap-4 px-12 pt-8 sm:grid-cols-2 lg:grid-cols-4 grow shrink-0'>
|
<nav className='grid content-start grid-cols-1 gap-4 px-12 pt-8 sm:grid-cols-2 lg:grid-cols-4 grow shrink-0'>
|
||||||
{data?.map(({ data: datasets }) => datasets.map(dataset => (
|
{data?.map(({ data: datasets }) => datasets.map(dataset => (
|
||||||
<DatasetCard key={dataset.id} dataset={dataset} onDelete={mutate} />)
|
<DatasetCard key={dataset.id} dataset={dataset} onDelete={mutate} />),
|
||||||
))}
|
))}
|
||||||
<NewDatasetCard ref={anchorRef} />
|
<NewDatasetCard ref={anchorRef} />
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -51,4 +50,3 @@ const Datasets = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Datasets
|
export default Datasets
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import AppList from "@/app/components/explore/app-list"
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import AppList from '@/app/components/explore/app-list'
|
||||||
|
|
||||||
const Apps = ({ }) => {
|
const Apps = () => {
|
||||||
return <AppList />
|
return <AppList />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import React, { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
|
import React from 'react'
|
||||||
import Main from '@/app/components/explore/installed-app'
|
import Main from '@/app/components/explore/installed-app'
|
||||||
|
|
||||||
export interface IInstalledAppProps {
|
export type IInstalledAppProps = {
|
||||||
params: {
|
params: {
|
||||||
appId: string
|
appId: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const InstalledApp: FC<IInstalledAppProps> = ({ params: {appId} }) => {
|
const InstalledApp: FC<IInstalledAppProps> = ({ params: { appId } }) => {
|
||||||
return (
|
return (
|
||||||
<Main id={appId} />
|
<Main id={appId} />
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,7 +5,6 @@ import type { IMainProps } from '@/app/components/share/chat'
|
||||||
import Main from '@/app/components/share/chat'
|
import Main from '@/app/components/share/chat'
|
||||||
|
|
||||||
const Chat: FC<IMainProps> = () => {
|
const Chat: FC<IMainProps> = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Main />
|
<Main />
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
'use client'
|
'use client'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useBoolean, useClickAway } from 'ahooks'
|
import { useBoolean, useClickAway } from 'ahooks'
|
||||||
|
import { ChevronDownIcon, Cog8ToothIcon, InformationCircleIcon } from '@heroicons/react/24/outline'
|
||||||
import ParamItem from './param-item'
|
import ParamItem from './param-item'
|
||||||
import Radio from '@/app/components/base/radio'
|
import Radio from '@/app/components/base/radio'
|
||||||
import Panel from '@/app/components/base/panel'
|
import Panel from '@/app/components/base/panel'
|
||||||
import type { CompletionParams } from '@/models/debug'
|
import type { CompletionParams } from '@/models/debug'
|
||||||
import { Cog8ToothIcon, InformationCircleIcon, ChevronDownIcon } from '@heroicons/react/24/outline'
|
|
||||||
import { AppType } from '@/types/app'
|
import { AppType } from '@/types/app'
|
||||||
import { TONE_LIST } from '@/config'
|
import { TONE_LIST } from '@/config'
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
|
@ -51,7 +51,7 @@ const ConifgModel: FC<IConifgModelProps> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const isChatApp = mode === AppType.chat
|
const isChatApp = mode === AppType.chat
|
||||||
const availableModels = options.filter((item) => item.type === mode)
|
const availableModels = options.filter(item => item.type === mode)
|
||||||
const [isShowConfig, { setFalse: hideConfig, toggle: toogleShowConfig }] = useBoolean(false)
|
const [isShowConfig, { setFalse: hideConfig, toggle: toogleShowConfig }] = useBoolean(false)
|
||||||
const configContentRef = React.useRef(null)
|
const configContentRef = React.useRef(null)
|
||||||
useClickAway(() => {
|
useClickAway(() => {
|
||||||
|
@ -116,14 +116,14 @@ const ConifgModel: FC<IConifgModelProps> = ({
|
||||||
onShowUseGPT4Confirm()
|
onShowUseGPT4Confirm()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if(id !== 'gpt-4' && completionParams.max_tokens > 4000) {
|
if (id !== 'gpt-4' && completionParams.max_tokens > 4000) {
|
||||||
Toast.notify({
|
Toast.notify({
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
message: t('common.model.params.setToCurrentModelMaxTokenTip')
|
message: t('common.model.params.setToCurrentModelMaxTokenTip'),
|
||||||
})
|
})
|
||||||
onCompletionParamsChange({
|
onCompletionParamsChange({
|
||||||
...completionParams,
|
...completionParams,
|
||||||
max_tokens: 4000
|
max_tokens: 4000,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
setModelId(id)
|
setModelId(id)
|
||||||
|
@ -153,7 +153,7 @@ const ConifgModel: FC<IConifgModelProps> = ({
|
||||||
setToneId(id)
|
setToneId(id)
|
||||||
onCompletionParamsChange({
|
onCompletionParamsChange({
|
||||||
...tone.config,
|
...tone.config,
|
||||||
max_tokens: completionParams.max_tokens
|
max_tokens: completionParams.max_tokens,
|
||||||
} as CompletionParams)
|
} as CompletionParams)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,7 @@ const ConifgModel: FC<IConifgModelProps> = ({
|
||||||
return (
|
return (
|
||||||
<div className='relative' ref={configContentRef}>
|
<div className='relative' ref={configContentRef}>
|
||||||
<div
|
<div
|
||||||
className={cn(`flex items-center border h-8 px-2.5 space-x-2 rounded-lg`, disabled ? diabledStyle : ableStyle)}
|
className={cn('flex items-center border h-8 px-2.5 space-x-2 rounded-lg', disabled ? diabledStyle : ableStyle)}
|
||||||
onClick={() => !disabled && toogleShowConfig()}
|
onClick={() => !disabled && toogleShowConfig()}
|
||||||
>
|
>
|
||||||
<ModelIcon />
|
<ModelIcon />
|
||||||
|
@ -206,14 +206,14 @@ const ConifgModel: FC<IConifgModelProps> = ({
|
||||||
<div className="flex items-center justify-between my-5 h-9">
|
<div className="flex items-center justify-between my-5 h-9">
|
||||||
<div>{t('appDebug.modelConfig.model')}</div>
|
<div>{t('appDebug.modelConfig.model')}</div>
|
||||||
{/* model selector */}
|
{/* model selector */}
|
||||||
<div className="relative" style={{zIndex: 30}}>
|
<div className="relative" style={{ zIndex: 30 }}>
|
||||||
<div ref={triggerRef} onClick={() => !selectModelDisabled && toogleOption()} className={cn(selectModelDisabled ? 'cursor-not-allowed' : 'cursor-pointer', "flex items-center h-9 px-3 space-x-2 rounded-lg bg-gray-50 ")}>
|
<div ref={triggerRef} onClick={() => !selectModelDisabled && toogleOption()} className={cn(selectModelDisabled ? 'cursor-not-allowed' : 'cursor-pointer', 'flex items-center h-9 px-3 space-x-2 rounded-lg bg-gray-50 ')}>
|
||||||
<ModelIcon />
|
<ModelIcon />
|
||||||
<div className="text-sm gray-900">{selectedModel?.name}</div>
|
<div className="text-sm gray-900">{selectedModel?.name}</div>
|
||||||
{!selectModelDisabled && <ChevronDownIcon className={cn(isShowOption && 'rotate-180', 'w-[14px] h-[14px] text-gray-500')} />}
|
{!selectModelDisabled && <ChevronDownIcon className={cn(isShowOption && 'rotate-180', 'w-[14px] h-[14px] text-gray-500')} />}
|
||||||
</div>
|
</div>
|
||||||
{isShowOption && (
|
{isShowOption && (
|
||||||
<div className={cn(isChatApp ? 'w-[159px]' : 'w-[179px]', "absolute right-0 bg-gray-50 rounded-lg shadow")}>
|
<div className={cn(isChatApp ? 'w-[159px]' : 'w-[179px]', 'absolute right-0 bg-gray-50 rounded-lg shadow')}>
|
||||||
{availableModels.map(item => (
|
{availableModels.map(item => (
|
||||||
<div key={item.id} onClick={handleSelectModel(item.id)} className="flex items-center h-9 px-3 rounded-lg cursor-pointer hover:bg-gray-100">
|
<div key={item.id} onClick={handleSelectModel(item.id)} className="flex items-center h-9 px-3 rounded-lg cursor-pointer hover:bg-gray-100">
|
||||||
<ModelIcon className='mr-2' />
|
<ModelIcon className='mr-2' />
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
'use client'
|
'use client'
|
||||||
import React, { FC, useEffect } from 'react'
|
import type { FC } from 'react'
|
||||||
|
import React, { useEffect } from 'react'
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Modal from '@/app/components/base/modal'
|
import Link from 'next/link'
|
||||||
import { DataSet } from '@/models/datasets'
|
|
||||||
import TypeIcon from '../type-icon'
|
import TypeIcon from '../type-icon'
|
||||||
|
import s from './style.module.css'
|
||||||
|
import Modal from '@/app/components/base/modal'
|
||||||
|
import type { DataSet } from '@/models/datasets'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import { fetchDatasets } from '@/service/datasets'
|
import { fetchDatasets } from '@/service/datasets'
|
||||||
import Loading from '@/app/components/base/loading'
|
import Loading from '@/app/components/base/loading'
|
||||||
import { formatNumber } from '@/utils/format'
|
import { formatNumber } from '@/utils/format'
|
||||||
import Link from 'next/link'
|
|
||||||
|
|
||||||
import s from './style.module.css'
|
export type ISelectDataSetProps = {
|
||||||
|
|
||||||
export interface ISelectDataSetProps {
|
|
||||||
isShow: boolean
|
isShow: boolean
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
selectedIds: string[]
|
selectedIds: string[]
|
||||||
|
@ -37,22 +37,21 @@ const SelectDataSet: FC<ISelectDataSetProps> = ({
|
||||||
const { data } = await fetchDatasets({ url: '/datasets', params: { page: 1 } })
|
const { data } = await fetchDatasets({ url: '/datasets', params: { page: 1 } })
|
||||||
setDataSets(data)
|
setDataSets(data)
|
||||||
setLoaded(true)
|
setLoaded(true)
|
||||||
setSelected(data.filter((item) => selectedIds.includes(item.id)))
|
setSelected(data.filter(item => selectedIds.includes(item.id)))
|
||||||
})()
|
})()
|
||||||
}, [])
|
}, [])
|
||||||
const toggleSelect = (dataSet: DataSet) => {
|
const toggleSelect = (dataSet: DataSet) => {
|
||||||
const isSelected = selected.some((item) => item.id === dataSet.id)
|
const isSelected = selected.some(item => item.id === dataSet.id)
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
setSelected(selected.filter((item) => item.id !== dataSet.id))
|
setSelected(selected.filter(item => item.id !== dataSet.id))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (canSelectMulti) {
|
if (canSelectMulti)
|
||||||
setSelected([...selected, dataSet])
|
setSelected([...selected, dataSet])
|
||||||
} else {
|
else
|
||||||
setSelected([dataSet])
|
setSelected([dataSet])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const handleSelect = () => {
|
const handleSelect = () => {
|
||||||
onSelect(selected)
|
onSelect(selected)
|
||||||
|
@ -74,7 +73,7 @@ const SelectDataSet: FC<ISelectDataSetProps> = ({
|
||||||
<div className='flex items-center justify-center mt-6 rounded-lg space-x-1 h-[128px] text-[13px] border'
|
<div className='flex items-center justify-center mt-6 rounded-lg space-x-1 h-[128px] text-[13px] border'
|
||||||
style={{
|
style={{
|
||||||
background: 'rgba(0, 0, 0, 0.02)',
|
background: 'rgba(0, 0, 0, 0.02)',
|
||||||
borderColor: 'rgba(0, 0, 0, 0.02'
|
borderColor: 'rgba(0, 0, 0, 0.02',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className='text-gray-500'>{t('appDebug.feature.dataSet.noDataSet')}</span>
|
<span className='text-gray-500'>{t('appDebug.feature.dataSet.noDataSet')}</span>
|
||||||
|
@ -85,7 +84,7 @@ const SelectDataSet: FC<ISelectDataSetProps> = ({
|
||||||
{datasets && datasets?.length > 0 && (
|
{datasets && datasets?.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<div className='mt-7 space-y-1 max-h-[286px] overflow-y-auto'>
|
<div className='mt-7 space-y-1 max-h-[286px] overflow-y-auto'>
|
||||||
{datasets.map((item) => (
|
{datasets.map(item => (
|
||||||
<div
|
<div
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className={cn(s.item, selected.some(i => i.id === item.id) && s.selected, 'flex justify-between items-center h-10 px-2 rounded-lg bg-white border border-gray-200 cursor-pointer')}
|
className={cn(s.item, selected.some(i => i.id === item.id) && s.selected, 'flex justify-between items-center h-10 px-2 rounded-lg bg-white border border-gray-200 cursor-pointer')}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
|
/* eslint-disable multiline-ternary */
|
||||||
'use client'
|
'use client'
|
||||||
import React, { FC, useEffect, useRef, useState } from 'react'
|
import type { FC } from 'react'
|
||||||
|
import React, { useEffect, useRef, useState } from 'react'
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
import ConfigContext from '@/context/debug-configuration'
|
|
||||||
import produce from 'immer'
|
import produce from 'immer'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useBoolean } from 'ahooks'
|
import { useBoolean } from 'ahooks'
|
||||||
|
import ConfigContext from '@/context/debug-configuration'
|
||||||
import Panel from '@/app/components/app/configuration/base/feature-panel'
|
import Panel from '@/app/components/app/configuration/base/feature-panel'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import OperationBtn from '@/app/components/app/configuration/base/operation-btn'
|
import OperationBtn from '@/app/components/app/configuration/base/operation-btn'
|
||||||
|
@ -14,7 +16,7 @@ import ConfirmAddVar from '@/app/components/app/configuration/config-prompt/conf
|
||||||
import { getNewVar } from '@/utils/var'
|
import { getNewVar } from '@/utils/var'
|
||||||
import { varHighlightHTML } from '@/app/components/app/configuration/base/var-highlight'
|
import { varHighlightHTML } from '@/app/components/app/configuration/base/var-highlight'
|
||||||
|
|
||||||
export interface IOpeningStatementProps {
|
export type IOpeningStatementProps = {
|
||||||
promptTemplate: string
|
promptTemplate: string
|
||||||
value: string
|
value: string
|
||||||
onChange: (value: string) => void
|
onChange: (value: string) => void
|
||||||
|
@ -25,7 +27,7 @@ const regex = /\{\{([^}]+)\}\}/g
|
||||||
|
|
||||||
const OpeningStatement: FC<IOpeningStatementProps> = ({
|
const OpeningStatement: FC<IOpeningStatementProps> = ({
|
||||||
value = '',
|
value = '',
|
||||||
onChange
|
onChange,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const {
|
const {
|
||||||
|
@ -61,8 +63,6 @@ const OpeningStatement: FC<IOpeningStatementProps> = ({
|
||||||
.replace(regex, varHighlightHTML({ name: '$1' })) // `<span class="${highLightClassName}">{{$1}}</span>`
|
.replace(regex, varHighlightHTML({ name: '$1' })) // `<span class="${highLightClassName}">{{$1}}</span>`
|
||||||
.replace(/\n/g, '<br />')
|
.replace(/\n/g, '<br />')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleEdit = () => {
|
const handleEdit = () => {
|
||||||
setFocus()
|
setFocus()
|
||||||
}
|
}
|
||||||
|
@ -76,15 +76,15 @@ const OpeningStatement: FC<IOpeningStatementProps> = ({
|
||||||
|
|
||||||
const handleConfirm = () => {
|
const handleConfirm = () => {
|
||||||
const keys = getInputKeys(tempValue)
|
const keys = getInputKeys(tempValue)
|
||||||
const promptKeys = promptVariables.map((item) => item.key)
|
const promptKeys = promptVariables.map(item => item.key)
|
||||||
let notIncludeKeys: string[] = []
|
let notIncludeKeys: string[] = []
|
||||||
|
|
||||||
if (promptKeys.length === 0) {
|
if (promptKeys.length === 0) {
|
||||||
if (keys.length > 0) {
|
if (keys.length > 0)
|
||||||
notIncludeKeys = keys
|
notIncludeKeys = keys
|
||||||
}
|
}
|
||||||
} else {
|
else {
|
||||||
notIncludeKeys = keys.filter((key) => !promptKeys.includes(key))
|
notIncludeKeys = keys.filter(key => !promptKeys.includes(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notIncludeKeys.length > 0) {
|
if (notIncludeKeys.length > 0) {
|
||||||
|
@ -104,7 +104,7 @@ const OpeningStatement: FC<IOpeningStatementProps> = ({
|
||||||
|
|
||||||
const autoAddVar = () => {
|
const autoAddVar = () => {
|
||||||
const newModelConfig = produce(modelConfig, (draft) => {
|
const newModelConfig = produce(modelConfig, (draft) => {
|
||||||
draft.configs.prompt_variables = [...draft.configs.prompt_variables, ...notIncludeKeys.map((key) => getNewVar(key))]
|
draft.configs.prompt_variables = [...draft.configs.prompt_variables, ...notIncludeKeys.map(key => getNewVar(key))]
|
||||||
})
|
})
|
||||||
onChange(tempValue)
|
onChange(tempValue)
|
||||||
setModelConfig(newModelConfig)
|
setModelConfig(newModelConfig)
|
||||||
|
@ -130,9 +130,11 @@ const OpeningStatement: FC<IOpeningStatementProps> = ({
|
||||||
isFocus={isFocus}
|
isFocus={isFocus}
|
||||||
>
|
>
|
||||||
<div className='text-gray-700 text-sm'>
|
<div className='text-gray-700 text-sm'>
|
||||||
{(hasValue || (!hasValue && isFocus)) ? (
|
{(hasValue || (!hasValue && isFocus))
|
||||||
|
? (
|
||||||
<>
|
<>
|
||||||
{isFocus ? (
|
{isFocus
|
||||||
|
? (
|
||||||
<textarea
|
<textarea
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
value={tempValue}
|
value={tempValue}
|
||||||
|
@ -142,14 +144,16 @@ const OpeningStatement: FC<IOpeningStatementProps> = ({
|
||||||
placeholder={t('appDebug.openingStatement.placeholder') as string}
|
placeholder={t('appDebug.openingStatement.placeholder') as string}
|
||||||
>
|
>
|
||||||
</textarea>
|
</textarea>
|
||||||
) : (
|
)
|
||||||
|
: (
|
||||||
<div dangerouslySetInnerHTML={{
|
<div dangerouslySetInnerHTML={{
|
||||||
__html: coloredContent
|
__html: coloredContent,
|
||||||
}}></div>
|
}}></div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Operation Bar */}
|
{/* Operation Bar */}
|
||||||
{isFocus && (
|
{isFocus
|
||||||
|
&& (
|
||||||
<div className='mt-2 flex items-center justify-between'>
|
<div className='mt-2 flex items-center justify-between'>
|
||||||
<div className='text-xs text-gray-500'>{t('appDebug.openingStatement.varTip')}</div>
|
<div className='text-xs text-gray-500'>{t('appDebug.openingStatement.varTip')}</div>
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,12 @@ import { useContext } from 'use-context-selector'
|
||||||
import {
|
import {
|
||||||
PlayIcon,
|
PlayIcon,
|
||||||
} from '@heroicons/react/24/solid'
|
} from '@heroicons/react/24/solid'
|
||||||
|
import VarIcon from '../base/icons/var-icon'
|
||||||
import ConfigContext from '@/context/debug-configuration'
|
import ConfigContext from '@/context/debug-configuration'
|
||||||
import type { PromptVariable } from '@/models/debug'
|
import type { PromptVariable } from '@/models/debug'
|
||||||
import { AppType } from '@/types/app'
|
import { AppType } from '@/types/app'
|
||||||
import Select from '@/app/components/base/select'
|
import Select from '@/app/components/base/select'
|
||||||
import { DEFAULT_VALUE_MAX_LEN } from '@/config'
|
import { DEFAULT_VALUE_MAX_LEN } from '@/config'
|
||||||
import VarIcon from '../base/icons/var-icon'
|
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
|
|
||||||
export type IPromptValuePanelProps = {
|
export type IPromptValuePanelProps = {
|
||||||
|
@ -71,7 +71,8 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({
|
||||||
</div>
|
</div>
|
||||||
<div className='mt-2 leading-normal'>
|
<div className='mt-2 leading-normal'>
|
||||||
{
|
{
|
||||||
(promptTemplate && promptTemplate?.trim()) ? (
|
(promptTemplate && promptTemplate?.trim())
|
||||||
|
? (
|
||||||
<div
|
<div
|
||||||
className="max-h-48 overflow-y-auto text-sm text-gray-700 break-all"
|
className="max-h-48 overflow-y-auto text-sm text-gray-700 break-all"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
|
@ -79,7 +80,8 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
)
|
||||||
|
: (
|
||||||
<div className='text-xs text-gray-500'>{t('appDebug.inputs.noPrompt')}</div>
|
<div className='text-xs text-gray-500'>{t('appDebug.inputs.noPrompt')}</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -105,12 +107,14 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{
|
{
|
||||||
promptVariables.length > 0 ? (
|
promptVariables.length > 0
|
||||||
|
? (
|
||||||
<div className="space-y-3 ">
|
<div className="space-y-3 ">
|
||||||
{promptVariables.map(({ key, name, type, options, max_length, required }) => (
|
{promptVariables.map(({ key, name, type, options, max_length, required }) => (
|
||||||
<div key={key} className="flex items-center justify-between">
|
<div key={key} className="flex items-center justify-between">
|
||||||
<div className="mr-1 shrink-0 w-[120px] text-sm text-gray-900">{name || key}</div>
|
<div className="mr-1 shrink-0 w-[120px] text-sm text-gray-900">{name || key}</div>
|
||||||
{type === 'select' ? (
|
{type === 'select'
|
||||||
|
? (
|
||||||
<Select
|
<Select
|
||||||
className='w-full'
|
className='w-full'
|
||||||
defaultValue={inputs[key] as string}
|
defaultValue={inputs[key] as string}
|
||||||
|
@ -119,7 +123,8 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({
|
||||||
allowSearch={false}
|
allowSearch={false}
|
||||||
bgClassName='bg-gray-50'
|
bgClassName='bg-gray-50'
|
||||||
/>
|
/>
|
||||||
) : (
|
)
|
||||||
|
: (
|
||||||
<input
|
<input
|
||||||
className="w-full px-3 text-sm leading-9 text-gray-900 border-0 rounded-lg grow h-9 bg-gray-50 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
|
className="w-full px-3 text-sm leading-9 text-gray-900 border-0 rounded-lg grow h-9 bg-gray-50 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
|
||||||
placeholder={`${name}${!required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
|
placeholder={`${name}${!required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
|
||||||
|
@ -133,7 +138,8 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
)
|
||||||
|
: (
|
||||||
<div className='text-xs text-gray-500'>{t('appDebug.inputs.noVar')}</div>
|
<div className='text-xs text-gray-500'>{t('appDebug.inputs.noVar')}</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
'use client'
|
'use client'
|
||||||
import React, { FC, useState } from 'react'
|
import type { FC } from 'react'
|
||||||
|
import React, { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
import { Markdown } from '@/app/components/base/markdown'
|
|
||||||
import Loading from '@/app/components/base/loading'
|
|
||||||
import copy from 'copy-to-clipboard'
|
import copy from 'copy-to-clipboard'
|
||||||
import Toast from '@/app/components/base/toast'
|
|
||||||
import { Feedbacktype } from '@/app/components/app/chat'
|
|
||||||
import { HandThumbDownIcon, HandThumbUpIcon } from '@heroicons/react/24/outline'
|
import { HandThumbDownIcon, HandThumbUpIcon } from '@heroicons/react/24/outline'
|
||||||
import { useBoolean } from 'ahooks'
|
import { useBoolean } from 'ahooks'
|
||||||
|
import { Markdown } from '@/app/components/base/markdown'
|
||||||
|
import Loading from '@/app/components/base/loading'
|
||||||
|
import Toast from '@/app/components/base/toast'
|
||||||
|
import type { Feedbacktype } from '@/app/components/app/chat'
|
||||||
import { fetchMoreLikeThis, updateFeedback } from '@/service/share'
|
import { fetchMoreLikeThis, updateFeedback } from '@/service/share'
|
||||||
|
|
||||||
const MAX_DEPTH = 3
|
const MAX_DEPTH = 3
|
||||||
export interface IGenerationItemProps {
|
export type IGenerationItemProps = {
|
||||||
className?: string
|
className?: string
|
||||||
content: string
|
content: string
|
||||||
messageId?: string | null
|
messageId?: string | null
|
||||||
|
@ -24,13 +25,13 @@ export interface IGenerationItemProps {
|
||||||
onFeedback?: (feedback: Feedbacktype) => void
|
onFeedback?: (feedback: Feedbacktype) => void
|
||||||
onSave?: (messageId: string) => void
|
onSave?: (messageId: string) => void
|
||||||
isMobile?: boolean
|
isMobile?: boolean
|
||||||
isInstalledApp: boolean,
|
isInstalledApp: boolean
|
||||||
installedAppId?: string,
|
installedAppId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SimpleBtn = ({ className, onClick, children }: {
|
export const SimpleBtn = ({ className, onClick, children }: {
|
||||||
className?: string
|
className?: string
|
||||||
onClick?: () => void,
|
onClick?: () => void
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}) => (
|
}) => (
|
||||||
<div
|
<div
|
||||||
|
@ -88,7 +89,7 @@ const GenerationItem: FC<IGenerationItemProps> = ({
|
||||||
const [childMessageId, setChildMessageId] = useState<string | null>(null)
|
const [childMessageId, setChildMessageId] = useState<string | null>(null)
|
||||||
const hasChild = !!childMessageId
|
const hasChild = !!childMessageId
|
||||||
const [childFeedback, setChildFeedback] = useState<Feedbacktype>({
|
const [childFeedback, setChildFeedback] = useState<Feedbacktype>({
|
||||||
rating: null
|
rating: null,
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleFeedback = async (childFeedback: Feedbacktype) => {
|
const handleFeedback = async (childFeedback: Feedbacktype) => {
|
||||||
|
@ -126,24 +127,30 @@ const GenerationItem: FC<IGenerationItemProps> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
const mainStyle = (() => {
|
const mainStyle = (() => {
|
||||||
const res: any = !isTop ? {
|
const res: any = !isTop
|
||||||
background: depth % 2 === 0 ? 'linear-gradient(90.07deg, #F9FAFB 0.05%, rgba(249, 250, 251, 0) 99.93%)' : '#fff'
|
? {
|
||||||
} : {}
|
background: depth % 2 === 0 ? 'linear-gradient(90.07deg, #F9FAFB 0.05%, rgba(249, 250, 251, 0) 99.93%)' : '#fff',
|
||||||
|
|
||||||
if (hasChild) {
|
|
||||||
res.boxShadow = '0px 1px 2px rgba(16, 24, 40, 0.05)'
|
|
||||||
}
|
}
|
||||||
|
: {}
|
||||||
|
|
||||||
|
if (hasChild)
|
||||||
|
res.boxShadow = '0px 1px 2px rgba(16, 24, 40, 0.05)'
|
||||||
|
|
||||||
return res
|
return res
|
||||||
})()
|
})()
|
||||||
return (
|
return (
|
||||||
<div className={cn(className, isTop ? 'rounded-xl border border-gray-200 bg-white' : 'rounded-br-xl !mt-0')}
|
<div className={cn(className, isTop ? 'rounded-xl border border-gray-200 bg-white' : 'rounded-br-xl !mt-0')}
|
||||||
style={isTop ? {
|
style={isTop
|
||||||
boxShadow: '0px 1px 2px rgba(16, 24, 40, 0.05)'
|
? {
|
||||||
} : {}}
|
boxShadow: '0px 1px 2px rgba(16, 24, 40, 0.05)',
|
||||||
|
}
|
||||||
|
: {}}
|
||||||
>
|
>
|
||||||
{isLoading ? (
|
{isLoading
|
||||||
|
? (
|
||||||
<div className='flex items-center h-10'><Loading type='area' /></div>
|
<div className='flex items-center h-10'><Loading type='area' /></div>
|
||||||
) : (
|
)
|
||||||
|
: (
|
||||||
<div
|
<div
|
||||||
className={cn(!isTop && 'rounded-br-xl border-l-2 border-primary-400', 'p-4')}
|
className={cn(!isTop && 'rounded-br-xl border-l-2 border-primary-400', 'p-4')}
|
||||||
style={mainStyle}
|
style={mainStyle}
|
||||||
|
@ -185,7 +192,7 @@ const GenerationItem: FC<IGenerationItemProps> = ({
|
||||||
<div
|
<div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onFeedback?.({
|
onFeedback?.({
|
||||||
rating: 'like'
|
rating: 'like',
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
className='flex w-6 h-6 items-center justify-center rounded-md cursor-pointer hover:bg-gray-100'>
|
className='flex w-6 h-6 items-center justify-center rounded-md cursor-pointer hover:bg-gray-100'>
|
||||||
|
@ -194,7 +201,7 @@ const GenerationItem: FC<IGenerationItemProps> = ({
|
||||||
<div
|
<div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onFeedback?.({
|
onFeedback?.({
|
||||||
rating: 'dislike'
|
rating: 'dislike',
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
className='flex w-6 h-6 items-center justify-center rounded-md cursor-pointer hover:bg-gray-100'>
|
className='flex w-6 h-6 items-center justify-center rounded-md cursor-pointer hover:bg-gray-100'>
|
||||||
|
@ -207,7 +214,7 @@ const GenerationItem: FC<IGenerationItemProps> = ({
|
||||||
<div
|
<div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onFeedback?.({
|
onFeedback?.({
|
||||||
rating: null
|
rating: null,
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
className='flex w-7 h-7 items-center justify-center rounded-md cursor-pointer !text-primary-600 border border-primary-200 bg-primary-100 hover:border-primary-300 hover:bg-primary-200'>
|
className='flex w-7 h-7 items-center justify-center rounded-md cursor-pointer !text-primary-600 border border-primary-200 bg-primary-100 hover:border-primary-300 hover:bg-primary-200'>
|
||||||
|
@ -218,7 +225,7 @@ const GenerationItem: FC<IGenerationItemProps> = ({
|
||||||
<div
|
<div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onFeedback?.({
|
onFeedback?.({
|
||||||
rating: null
|
rating: null,
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
className='flex w-7 h-7 items-center justify-center rounded-md cursor-pointer !text-red-600 border border-red-200 bg-red-100 hover:border-red-300 hover:bg-red-200'>
|
className='flex w-7 h-7 items-center justify-center rounded-md cursor-pointer !text-red-600 border border-red-200 bg-red-100 hover:border-red-300 hover:bg-red-200'>
|
||||||
|
@ -235,7 +242,6 @@ const GenerationItem: FC<IGenerationItemProps> = ({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
{((childMessageId || isQuerying) && depth < 3) && (
|
{((childMessageId || isQuerying) && depth < 3) && (
|
||||||
<div className='pl-4'>
|
<div className='pl-4'>
|
||||||
<GenerationItem {...childProps} />
|
<GenerationItem {...childProps} />
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import style from './style.module.css'
|
|
||||||
|
|
||||||
import data from '@emoji-mart/data'
|
import data from '@emoji-mart/data'
|
||||||
import { init } from 'emoji-mart'
|
import { init } from 'emoji-mart'
|
||||||
|
import style from './style.module.css'
|
||||||
|
|
||||||
init({ data })
|
init({ data })
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ const AppIcon: FC<AppIconProps> = ({
|
||||||
}}
|
}}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
{innerIcon ? innerIcon : icon && icon !== '' ? <em-emoji id={icon} /> : <em-emoji id='🤖' />}
|
{innerIcon || ((icon && icon !== '') ? <em-emoji id={icon} /> : <em-emoji id='🤖' />)}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
import type { ChangeEvent, FC } from 'react'
|
import type { ChangeEvent, FC } from 'react'
|
||||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { checkKeys } from '@/utils/var'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Button from '@/app/components/base/button'
|
|
||||||
import Toast from '../toast'
|
import Toast from '../toast'
|
||||||
import { varHighlightHTML } from '../../app/configuration/base/var-highlight'
|
import { varHighlightHTML } from '../../app/configuration/base/var-highlight'
|
||||||
|
import Button from '@/app/components/base/button'
|
||||||
|
import { checkKeys } from '@/utils/var'
|
||||||
|
|
||||||
// regex to match the {{}} and replace it with a span
|
// regex to match the {{}} and replace it with a span
|
||||||
const regex = /\{\{([^}]+)\}\}/g
|
const regex = /\{\{([^}]+)\}\}/g
|
||||||
|
@ -55,9 +55,9 @@ const BlockInput: FC<IBlockInputProps> = ({
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isEditing && contentEditableRef.current) {
|
if (isEditing && contentEditableRef.current) {
|
||||||
// TODO: Focus at the click positon
|
// TODO: Focus at the click positon
|
||||||
if (currentValue) {
|
if (currentValue)
|
||||||
contentEditableRef.current.setSelectionRange(currentValue.length, currentValue.length)
|
contentEditableRef.current.setSelectionRange(currentValue.length, currentValue.length)
|
||||||
}
|
|
||||||
contentEditableRef.current.focus()
|
contentEditableRef.current.focus()
|
||||||
}
|
}
|
||||||
}, [isEditing])
|
}, [isEditing])
|
||||||
|
@ -73,7 +73,6 @@ const BlockInput: FC<IBlockInputProps> = ({
|
||||||
.replace(regex, varHighlightHTML({ name: '$1' })) // `<span class="${highLightClassName}">{{$1}}</span>`
|
.replace(regex, varHighlightHTML({ name: '$1' })) // `<span class="${highLightClassName}">{{$1}}</span>`
|
||||||
.replace(/\n/g, '<br />')
|
.replace(/\n/g, '<br />')
|
||||||
|
|
||||||
|
|
||||||
// Not use useCallback. That will cause out callback get old data.
|
// Not use useCallback. That will cause out callback get old data.
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
if (onConfirm) {
|
if (onConfirm) {
|
||||||
|
@ -83,7 +82,7 @@ const BlockInput: FC<IBlockInputProps> = ({
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
Toast.notify({
|
Toast.notify({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey })
|
message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey }),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -125,9 +124,9 @@ const BlockInput: FC<IBlockInputProps> = ({
|
||||||
value={currentValue}
|
value={currentValue}
|
||||||
onBlur={() => {
|
onBlur={() => {
|
||||||
blur()
|
blur()
|
||||||
if (!isContentChanged) {
|
if (!isContentChanged)
|
||||||
setIsEditing(false)
|
setIsEditing(false)
|
||||||
}
|
|
||||||
// click confirm also make blur. Then outter value is change. So below code has problem.
|
// click confirm also make blur. Then outter value is change. So below code has problem.
|
||||||
// setTimeout(() => {
|
// setTimeout(() => {
|
||||||
// handleCancel()
|
// handleCancel()
|
||||||
|
@ -143,7 +142,8 @@ const BlockInput: FC<IBlockInputProps> = ({
|
||||||
{textAreaContent}
|
{textAreaContent}
|
||||||
{/* footer */}
|
{/* footer */}
|
||||||
<div className='flex item-center h-14 px-4'>
|
<div className='flex item-center h-14 px-4'>
|
||||||
{isContentChanged ? (
|
{isContentChanged
|
||||||
|
? (
|
||||||
<div className='flex items-center justify-between w-full'>
|
<div className='flex items-center justify-between w-full'>
|
||||||
<div className="h-[18px] leading-[18px] px-1 rounded-md bg-gray-100 text-xs text-gray-500">{currentValue.length}</div>
|
<div className="h-[18px] leading-[18px] px-1 rounded-md bg-gray-100 text-xs text-gray-500">{currentValue.length}</div>
|
||||||
<div className='flex space-x-2'>
|
<div className='flex space-x-2'>
|
||||||
|
@ -163,7 +163,8 @@ const BlockInput: FC<IBlockInputProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
)
|
||||||
|
: (
|
||||||
<p className="leading-5 text-xs text-gray-500">
|
<p className="leading-5 text-xs text-gray-500">
|
||||||
{t('appDebug.promptTip')}
|
{t('appDebug.promptTip')}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -1,26 +1,28 @@
|
||||||
|
/* eslint-disable multiline-ternary */
|
||||||
'use client'
|
'use client'
|
||||||
import React from 'react'
|
import type { ChangeEvent, FC } from 'react'
|
||||||
import { useState, FC, ChangeEvent } from 'react'
|
import React, { useState } from 'react'
|
||||||
import data from '@emoji-mart/data'
|
import data from '@emoji-mart/data'
|
||||||
import { init, SearchIndex } from 'emoji-mart'
|
import { SearchIndex, init } from 'emoji-mart'
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
|
import {
|
||||||
|
MagnifyingGlassIcon,
|
||||||
|
} from '@heroicons/react/24/outline'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import s from './style.module.css'
|
||||||
import Divider from '@/app/components/base/divider'
|
import Divider from '@/app/components/base/divider'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import s from './style.module.css'
|
|
||||||
import {
|
|
||||||
MagnifyingGlassIcon
|
|
||||||
} from '@heroicons/react/24/outline'
|
|
||||||
|
|
||||||
import Modal from '@/app/components/base/modal'
|
import Modal from '@/app/components/base/modal'
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
namespace JSX {
|
namespace JSX {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
||||||
interface IntrinsicElements {
|
interface IntrinsicElements {
|
||||||
'em-emoji': React.DetailedHTMLProps<
|
'em-emoji': React.DetailedHTMLProps<
|
||||||
React.HTMLAttributes<HTMLElement>,
|
React.HTMLAttributes<HTMLElement>,
|
||||||
HTMLElement
|
HTMLElement
|
||||||
>;
|
>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +59,7 @@ const backgroundColors = [
|
||||||
'#ECE9FE',
|
'#ECE9FE',
|
||||||
'#FFE4E8',
|
'#FFE4E8',
|
||||||
]
|
]
|
||||||
interface IEmojiPickerProps {
|
type IEmojiPickerProps = {
|
||||||
isModal?: boolean
|
isModal?: boolean
|
||||||
onSelect?: (emoji: string, background: string) => void
|
onSelect?: (emoji: string, background: string) => void
|
||||||
onClose?: () => void
|
onClose?: () => void
|
||||||
|
@ -66,7 +68,7 @@ interface IEmojiPickerProps {
|
||||||
const EmojiPicker: FC<IEmojiPickerProps> = ({
|
const EmojiPicker: FC<IEmojiPickerProps> = ({
|
||||||
isModal = true,
|
isModal = true,
|
||||||
onSelect,
|
onSelect,
|
||||||
onClose
|
onClose,
|
||||||
|
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
@ -97,8 +99,8 @@ const EmojiPicker: FC<IEmojiPickerProps> = ({
|
||||||
onChange={async (e: ChangeEvent<HTMLInputElement>) => {
|
onChange={async (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
if (e.target.value === '') {
|
if (e.target.value === '') {
|
||||||
setIsSearching(false)
|
setIsSearching(false)
|
||||||
return
|
}
|
||||||
} else {
|
else {
|
||||||
setIsSearching(true)
|
setIsSearching(true)
|
||||||
const emojis = await search(e.target.value)
|
const emojis = await search(e.target.value)
|
||||||
setSearchedEmojis(emojis)
|
setSearchedEmojis(emojis)
|
||||||
|
@ -111,7 +113,7 @@ const EmojiPicker: FC<IEmojiPickerProps> = ({
|
||||||
|
|
||||||
<div className="w-full max-h-[200px] overflow-x-hidden overflow-y-auto px-3">
|
<div className="w-full max-h-[200px] overflow-x-hidden overflow-y-auto px-3">
|
||||||
{isSearching && <>
|
{isSearching && <>
|
||||||
<div key={`category-search`} className='flex flex-col'>
|
<div key={'category-search'} className='flex flex-col'>
|
||||||
<p className='font-medium uppercase text-xs text-[#101828] mb-1'>Search</p>
|
<p className='font-medium uppercase text-xs text-[#101828] mb-1'>Search</p>
|
||||||
<div className='w-full h-full grid grid-cols-8 gap-1'>
|
<div className='w-full h-full grid grid-cols-8 gap-1'>
|
||||||
{searchedEmojis.map((emoji: string, index: number) => {
|
{searchedEmojis.map((emoji: string, index: number) => {
|
||||||
|
@ -131,7 +133,6 @@ const EmojiPicker: FC<IEmojiPickerProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</>}
|
</>}
|
||||||
|
|
||||||
|
|
||||||
{categories.map((category: any, index: number) => {
|
{categories.map((category: any, index: number) => {
|
||||||
return <div key={`category-${index}`} className='flex flex-col'>
|
return <div key={`category-${index}`} className='flex flex-col'>
|
||||||
<p className='font-medium uppercase text-xs text-[#101828] mb-1'>{category.id}</p>
|
<p className='font-medium uppercase text-xs text-[#101828] mb-1'>{category.id}</p>
|
||||||
|
@ -156,7 +157,7 @@ const EmojiPicker: FC<IEmojiPickerProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Color Select */}
|
{/* Color Select */}
|
||||||
<div className={cn('p-3 ', selectedEmoji == '' ? 'opacity-25' : '')}>
|
<div className={cn('p-3 ', selectedEmoji === '' ? 'opacity-25' : '')}>
|
||||||
<p className='font-medium uppercase text-xs text-[#101828] mb-2'>Choose Style</p>
|
<p className='font-medium uppercase text-xs text-[#101828] mb-2'>Choose Style</p>
|
||||||
<div className='w-full h-full grid grid-cols-8 gap-1'>
|
<div className='w-full h-full grid grid-cols-8 gap-1'>
|
||||||
{backgroundColors.map((color) => {
|
{backgroundColors.map((color) => {
|
||||||
|
@ -165,9 +166,9 @@ const EmojiPicker: FC<IEmojiPickerProps> = ({
|
||||||
className={
|
className={
|
||||||
cn(
|
cn(
|
||||||
'cursor-pointer',
|
'cursor-pointer',
|
||||||
`hover:ring-1 ring-offset-1`,
|
'hover:ring-1 ring-offset-1',
|
||||||
'inline-flex w-10 h-10 rounded-lg items-center justify-center',
|
'inline-flex w-10 h-10 rounded-lg items-center justify-center',
|
||||||
color === selectedBackground ? `ring-1 ring-gray-300` : '',
|
color === selectedBackground ? 'ring-1 ring-gray-300' : '',
|
||||||
)}
|
)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedBackground(color)
|
setSelectedBackground(color)
|
||||||
|
@ -191,7 +192,7 @@ const EmojiPicker: FC<IEmojiPickerProps> = ({
|
||||||
{t('app.emoji.cancel')}
|
{t('app.emoji.cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
disabled={selectedEmoji == ''}
|
disabled={selectedEmoji === ''}
|
||||||
type="primary"
|
type="primary"
|
||||||
className='w-full'
|
className='w-full'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
createDocument,
|
createDocument,
|
||||||
fetchFileIndexingEstimate as didFetchFileIndexingEstimate,
|
fetchFileIndexingEstimate as didFetchFileIndexingEstimate,
|
||||||
} from '@/service/datasets'
|
} from '@/service/datasets'
|
||||||
import type { CreateDocumentReq, createDocumentResponse } from '@/models/datasets'
|
import type { CreateDocumentReq, createDocumentResponse, FullDocumentDetail } from '@/models/datasets'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import PreviewItem from './preview-item'
|
import PreviewItem from './preview-item'
|
||||||
import Loading from '@/app/components/base/loading'
|
import Loading from '@/app/components/base/loading'
|
||||||
|
@ -22,14 +22,18 @@ import Toast from '@/app/components/base/toast'
|
||||||
import { formatNumber } from '@/utils/format'
|
import { formatNumber } from '@/utils/format'
|
||||||
|
|
||||||
type StepTwoProps = {
|
type StepTwoProps = {
|
||||||
|
isSetting?: boolean,
|
||||||
|
documentDetail?: FullDocumentDetail
|
||||||
hasSetAPIKEY: boolean,
|
hasSetAPIKEY: boolean,
|
||||||
onSetting: () => void,
|
onSetting: () => void,
|
||||||
datasetId?: string,
|
datasetId?: string,
|
||||||
indexingType?: string,
|
indexingType?: string,
|
||||||
file?: File,
|
file?: File,
|
||||||
onStepChange: (delta: number) => void,
|
onStepChange?: (delta: number) => void,
|
||||||
updateIndexingTypeCache: (type: string) => void,
|
updateIndexingTypeCache?: (type: string) => void,
|
||||||
updateResultCache: (res: createDocumentResponse) => void
|
updateResultCache?: (res: createDocumentResponse) => void
|
||||||
|
onSave?: () => void
|
||||||
|
onCancel?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SegmentType {
|
enum SegmentType {
|
||||||
|
@ -42,6 +46,8 @@ enum IndexingType {
|
||||||
}
|
}
|
||||||
|
|
||||||
const StepTwo = ({
|
const StepTwo = ({
|
||||||
|
isSetting,
|
||||||
|
documentDetail,
|
||||||
hasSetAPIKEY,
|
hasSetAPIKEY,
|
||||||
onSetting,
|
onSetting,
|
||||||
datasetId,
|
datasetId,
|
||||||
|
@ -50,6 +56,8 @@ const StepTwo = ({
|
||||||
onStepChange,
|
onStepChange,
|
||||||
updateIndexingTypeCache,
|
updateIndexingTypeCache,
|
||||||
updateResultCache,
|
updateResultCache,
|
||||||
|
onSave,
|
||||||
|
onCancel,
|
||||||
}: StepTwoProps) => {
|
}: StepTwoProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const scrollRef = useRef<HTMLDivElement>(null)
|
const scrollRef = useRef<HTMLDivElement>(null)
|
||||||
|
@ -171,7 +179,14 @@ const StepTwo = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
const getCreationParams = () => {
|
const getCreationParams = () => {
|
||||||
const params = {
|
let params
|
||||||
|
if (isSetting) {
|
||||||
|
params = {
|
||||||
|
original_document_id: documentDetail?.id,
|
||||||
|
process_rule: getProcessRule(),
|
||||||
|
} as CreateDocumentReq
|
||||||
|
} else {
|
||||||
|
params = {
|
||||||
data_source: {
|
data_source: {
|
||||||
type: 'upload_file',
|
type: 'upload_file',
|
||||||
info: file?.id,
|
info: file?.id,
|
||||||
|
@ -180,6 +195,7 @@ const StepTwo = ({
|
||||||
indexing_technique: getIndexing_technique(),
|
indexing_technique: getIndexing_technique(),
|
||||||
process_rule: getProcessRule(),
|
process_rule: getProcessRule(),
|
||||||
} as CreateDocumentReq
|
} as CreateDocumentReq
|
||||||
|
}
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,6 +212,25 @@ const StepTwo = ({
|
||||||
console.log(err)
|
console.log(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getRulesFromDetail = () => {
|
||||||
|
if (documentDetail) {
|
||||||
|
const rules = documentDetail.dataset_process_rule.rules
|
||||||
|
const separator = rules.segmentation.separator
|
||||||
|
const max = rules.segmentation.max_tokens
|
||||||
|
setSegmentIdentifier(separator === '\n' ? '\\n' : separator || '\\n')
|
||||||
|
setMax(max)
|
||||||
|
setRules(rules.pre_processing_rules)
|
||||||
|
setDefaultConfig(rules)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDefaultMode = () => {
|
||||||
|
if (documentDetail) {
|
||||||
|
setSegmentationType(documentDetail.dataset_process_rule.mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const createHandle = async () => {
|
const createHandle = async () => {
|
||||||
try {
|
try {
|
||||||
let res;
|
let res;
|
||||||
|
@ -204,19 +239,20 @@ const StepTwo = ({
|
||||||
res = await createFirstDocument({
|
res = await createFirstDocument({
|
||||||
body: params
|
body: params
|
||||||
})
|
})
|
||||||
updateIndexingTypeCache(indexType)
|
updateIndexingTypeCache && updateIndexingTypeCache(indexType)
|
||||||
updateResultCache(res)
|
updateResultCache && updateResultCache(res)
|
||||||
} else {
|
} else {
|
||||||
res = await createDocument({
|
res = await createDocument({
|
||||||
datasetId,
|
datasetId,
|
||||||
body: params
|
body: params
|
||||||
})
|
})
|
||||||
updateIndexingTypeCache(indexType)
|
updateIndexingTypeCache && updateIndexingTypeCache(indexType)
|
||||||
updateResultCache({
|
updateResultCache && updateResultCache({
|
||||||
document: res,
|
document: res,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
onStepChange(+1)
|
onStepChange && onStepChange(+1)
|
||||||
|
isSetting && onSave && onSave()
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
Toast.notify({
|
Toast.notify({
|
||||||
|
@ -228,7 +264,12 @@ const StepTwo = ({
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// fetch rules
|
// fetch rules
|
||||||
|
if (!isSetting) {
|
||||||
getRules()
|
getRules()
|
||||||
|
} else {
|
||||||
|
getRulesFromDetail()
|
||||||
|
getDefaultMode()
|
||||||
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -444,11 +485,18 @@ const StepTwo = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{!isSetting ? (
|
||||||
<div className='flex items-center mt-8 py-2'>
|
<div className='flex items-center mt-8 py-2'>
|
||||||
<Button onClick={() => onStepChange(-1)}>{t('datasetCreation.stepTwo.lastStep')}</Button>
|
<Button onClick={() => onStepChange && onStepChange(-1)}>{t('datasetCreation.stepTwo.lastStep')}</Button>
|
||||||
<div className={s.divider} />
|
<div className={s.divider} />
|
||||||
<Button type='primary' onClick={createHandle}>{t('datasetCreation.stepTwo.nextStep')}</Button>
|
<Button type='primary' onClick={createHandle}>{t('datasetCreation.stepTwo.nextStep')}</Button>
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className='flex items-center mt-8 py-2'>
|
||||||
|
<Button type='primary' onClick={createHandle}>{t('datasetCreation.stepTwo.save')}</Button>
|
||||||
|
<Button className='ml-2' onClick={onCancel}>{t('datasetCreation.stepTwo.cancel')}</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -19,7 +19,7 @@ import type { FullDocumentDetail, ProcessRuleResponse } from '@/models/datasets'
|
||||||
import type { CommonResponse } from '@/models/common'
|
import type { CommonResponse } from '@/models/common'
|
||||||
import { asyncRunSafe } from '@/utils'
|
import { asyncRunSafe } from '@/utils'
|
||||||
import { formatNumber } from '@/utils/format'
|
import { formatNumber } from '@/utils/format'
|
||||||
import { fetchIndexingEstimate, fetchIndexingStatus, fetchProcessRule, pauseDocIndexing, resumeDocIndexing } from '@/service/datasets'
|
import { fetchIndexingEstimate, fetchProcessRule, pauseDocIndexing, resumeDocIndexing } from '@/service/datasets'
|
||||||
import DatasetDetailContext from '@/context/dataset-detail'
|
import DatasetDetailContext from '@/context/dataset-detail'
|
||||||
import StopEmbeddingModal from '@/app/components/datasets/create/stop-embedding-modal'
|
import StopEmbeddingModal from '@/app/components/datasets/create/stop-embedding-modal'
|
||||||
|
|
||||||
|
@ -118,14 +118,45 @@ const EmbeddingDetail: FC<Props> = ({ detail, stopPosition = 'top', datasetId: d
|
||||||
const localDocumentId = docId ?? documentId
|
const localDocumentId = docId ?? documentId
|
||||||
const localIndexingTechnique = indexingType ?? indexingTechnique
|
const localIndexingTechnique = indexingType ?? indexingTechnique
|
||||||
|
|
||||||
const { data: indexingStatusDetail, error: indexingStatusErr, mutate: statusMutate } = useSWR({
|
// const { data: indexingStatusDetailFromApi, error: indexingStatusErr, mutate: statusMutate } = useSWR({
|
||||||
action: 'fetchIndexingStatus',
|
// action: 'fetchIndexingStatus',
|
||||||
datasetId: localDatasetId,
|
// datasetId: localDatasetId,
|
||||||
documentId: localDocumentId,
|
// documentId: localDocumentId,
|
||||||
}, apiParams => fetchIndexingStatus(omit(apiParams, 'action')), {
|
// }, apiParams => fetchIndexingStatus(omit(apiParams, 'action')), {
|
||||||
refreshInterval: 5000,
|
// refreshInterval: 2500,
|
||||||
revalidateOnFocus: false,
|
// revalidateOnFocus: false,
|
||||||
})
|
// })
|
||||||
|
|
||||||
|
const [indexingStatusDetail, setIndexingStatusDetail, getIndexingStatusDetail] = useGetState<any>(null)
|
||||||
|
const fetchIndexingStatus = async () => {
|
||||||
|
const status = await doFetchIndexingStatus({ datasetId: localDatasetId, documentId: localDocumentId })
|
||||||
|
setIndexingStatusDetail(status)
|
||||||
|
}
|
||||||
|
|
||||||
|
const [runId, setRunId, getRunId] = useGetState<any>(null)
|
||||||
|
const startQueryStatus = () => {
|
||||||
|
const runId = setInterval(() => {
|
||||||
|
const indexingStatusDetail = getIndexingStatusDetail()
|
||||||
|
if (indexingStatusDetail?.indexing_status === 'completed') {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||||
|
stopQueryStatus()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fetchIndexingStatus()
|
||||||
|
}, 2500)
|
||||||
|
setRunId(runId)
|
||||||
|
}
|
||||||
|
const stopQueryStatus = () => {
|
||||||
|
clearInterval(getRunId())
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchIndexingStatus()
|
||||||
|
startQueryStatus()
|
||||||
|
return () => {
|
||||||
|
stopQueryStatus()
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
const { data: indexingEstimateDetail, error: indexingEstimateErr } = useSWR({
|
const { data: indexingEstimateDetail, error: indexingEstimateErr } = useSWR({
|
||||||
action: 'fetchIndexingEstimate',
|
action: 'fetchIndexingEstimate',
|
||||||
|
@ -168,7 +199,7 @@ const EmbeddingDetail: FC<Props> = ({ detail, stopPosition = 'top', datasetId: d
|
||||||
const [e] = await asyncRunSafe<CommonResponse>(opApi({ datasetId: localDatasetId, documentId: localDocumentId }) as Promise<CommonResponse>)
|
const [e] = await asyncRunSafe<CommonResponse>(opApi({ datasetId: localDatasetId, documentId: localDocumentId }) as Promise<CommonResponse>)
|
||||||
if (!e) {
|
if (!e) {
|
||||||
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
||||||
statusMutate()
|
setIndexingStatusDetail(null)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
notify({ type: 'error', message: t('common.actionMsg.modificationFailed') })
|
notify({ type: 'error', message: t('common.actionMsg.modificationFailed') })
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
'use client'
|
||||||
|
import React, { useState, useCallback, useEffect } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useBoolean } from 'ahooks'
|
||||||
|
import { useContext } from 'use-context-selector'
|
||||||
|
import { useRouter } from 'next/navigation'
|
||||||
|
import DatasetDetailContext from '@/context/dataset-detail'
|
||||||
|
import type { FullDocumentDetail } from '@/models/datasets'
|
||||||
|
import { fetchTenantInfo } from '@/service/common'
|
||||||
|
import { fetchDocumentDetail, MetadataType } from '@/service/datasets'
|
||||||
|
|
||||||
|
import Loading from '@/app/components/base/loading'
|
||||||
|
import StepTwo from '@/app/components/datasets/create/step-two'
|
||||||
|
import AccountSetting from '@/app/components/header/account-setting'
|
||||||
|
import AppUnavailable from '@/app/components/base/app-unavailable'
|
||||||
|
|
||||||
|
type DocumentSettingsProps = {
|
||||||
|
datasetId: string;
|
||||||
|
documentId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DocumentSettings = ({ datasetId, documentId }: DocumentSettingsProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const router = useRouter()
|
||||||
|
const [hasSetAPIKEY, setHasSetAPIKEY] = useState(true)
|
||||||
|
const [isShowSetAPIKey, { setTrue: showSetAPIKey, setFalse: hideSetAPIkey }] = useBoolean()
|
||||||
|
const [hasError, setHasError] = useState(false)
|
||||||
|
const { indexingTechnique, dataset } = useContext(DatasetDetailContext)
|
||||||
|
|
||||||
|
const saveHandler = () => router.push(`/datasets/${datasetId}/documents/${documentId}`)
|
||||||
|
|
||||||
|
const cancelHandler = () => router.back()
|
||||||
|
|
||||||
|
const checkAPIKey = async () => {
|
||||||
|
const data = await fetchTenantInfo({ url: '/info' })
|
||||||
|
const hasSetKey = data.providers.some(({ is_valid }) => is_valid)
|
||||||
|
setHasSetAPIKEY(hasSetKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
checkAPIKey()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const [documentDetail, setDocumentDetail] = useState<FullDocumentDetail | null>(null)
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const detail = await fetchDocumentDetail({
|
||||||
|
datasetId,
|
||||||
|
documentId,
|
||||||
|
params: { metadata: 'without' as MetadataType }
|
||||||
|
})
|
||||||
|
setDocumentDetail(detail)
|
||||||
|
} catch (e) {
|
||||||
|
setHasError(true)
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
}, [datasetId, documentId])
|
||||||
|
|
||||||
|
if (hasError) {
|
||||||
|
return <AppUnavailable code={500} unknownReason={t('datasetCreation.error.unavailable') as string} />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex' style={{ height: 'calc(100vh - 56px)' }}>
|
||||||
|
<div className="grow bg-white">
|
||||||
|
{!documentDetail && <Loading type='app' />}
|
||||||
|
{dataset && documentDetail && (
|
||||||
|
<StepTwo
|
||||||
|
hasSetAPIKEY={hasSetAPIKEY}
|
||||||
|
onSetting={showSetAPIKey}
|
||||||
|
datasetId={datasetId}
|
||||||
|
indexingType={indexingTechnique || ''}
|
||||||
|
isSetting
|
||||||
|
documentDetail={documentDetail}
|
||||||
|
file={documentDetail.data_source_info.upload_file}
|
||||||
|
onSave={saveHandler}
|
||||||
|
onCancel={cancelHandler}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{isShowSetAPIKey && <AccountSetting activeTab="provider" onCancel={async () => {
|
||||||
|
await checkAPIKey()
|
||||||
|
hideSetAPIkey()
|
||||||
|
}} />}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DocumentSettings
|
|
@ -95,6 +95,7 @@ export const OperationAction: FC<{
|
||||||
const [showModal, setShowModal] = useState(false)
|
const [showModal, setShowModal] = useState(false)
|
||||||
const { notify } = useContext(ToastContext)
|
const { notify } = useContext(ToastContext)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
const isListScene = scene === 'list'
|
const isListScene = scene === 'list'
|
||||||
|
|
||||||
|
@ -166,15 +167,19 @@ export const OperationAction: FC<{
|
||||||
</div>
|
</div>
|
||||||
<Divider />
|
<Divider />
|
||||||
</>}
|
</>}
|
||||||
{/* <div className={s.actionItem}>
|
{!archived && (
|
||||||
|
<>
|
||||||
|
<div className={s.actionItem} onClick={() => router.push(`/datasets/${datasetId}/documents/${detail.id}/settings`)}>
|
||||||
<SettingsIcon />
|
<SettingsIcon />
|
||||||
<span className={s.actionName}>{t('datasetDocuments.list.action.settings')}</span>
|
<span className={s.actionName}>{t('datasetDocuments.list.action.settings')}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={s.actionItem} onClick={() => router.push(`/datasets/${datasetId}/documents/create`)}>
|
{/* <div className={s.actionItem} onClick={() => router.push(`/datasets/${datasetId}/documents/create`)}>
|
||||||
<FilePlusIcon />
|
<FilePlusIcon />
|
||||||
<span className={s.actionName}>{t('datasetDocuments.list.action.uploadFile')}</span>
|
<span className={s.actionName}>{t('datasetDocuments.list.action.uploadFile')}</span>
|
||||||
</div>
|
</div> */}
|
||||||
<Divider className='my-1' /> */}
|
<Divider className='my-1' />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{!archived && <div className={s.actionItem} onClick={() => onOperate('archive')}>
|
{!archived && <div className={s.actionItem} onClick={() => onOperate('archive')}>
|
||||||
<ArchiveIcon />
|
<ArchiveIcon />
|
||||||
<span className={s.actionName}>{t('datasetDocuments.list.action.archive')}</span>
|
<span className={s.actionName}>{t('datasetDocuments.list.action.archive')}</span>
|
||||||
|
|
|
@ -72,7 +72,7 @@
|
||||||
.txtIcon {
|
.txtIcon {
|
||||||
background-image: url(./assets/txt.svg);
|
background-image: url(./assets/txt.svg);
|
||||||
}
|
}
|
||||||
.mdIcon {
|
.markdownIcon {
|
||||||
background-image: url(./assets/md.svg);
|
background-image: url(./assets/md.svg);
|
||||||
}
|
}
|
||||||
.statusItemDetail {
|
.statusItemDetail {
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
'use client'
|
'use client'
|
||||||
import { Dispatch, SetStateAction, useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
import { BookOpenIcon } from '@heroicons/react/24/outline'
|
import { BookOpenIcon } from '@heroicons/react/24/outline'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { ToastContext } from '@/app/components/base/toast'
|
|
||||||
import PermissionsRadio from '../permissions-radio'
|
import PermissionsRadio from '../permissions-radio'
|
||||||
import IndexMethodRadio from '../index-method-radio'
|
import IndexMethodRadio from '../index-method-radio'
|
||||||
|
import { ToastContext } from '@/app/components/base/toast'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import { updateDatasetSetting, fetchDataDetail } from '@/service/datasets'
|
import { fetchDataDetail, updateDatasetSetting } from '@/service/datasets'
|
||||||
import { DataSet } from '@/models/datasets'
|
import type { DataSet } from '@/models/datasets'
|
||||||
|
|
||||||
const rowClass = `
|
const rowClass = `
|
||||||
flex justify-between py-4
|
flex justify-between py-4
|
||||||
|
@ -20,8 +20,7 @@ const labelClass = `
|
||||||
const inputClass = `
|
const inputClass = `
|
||||||
w-[480px] px-3 bg-gray-100 text-sm text-gray-800 rounded-lg outline-none appearance-none
|
w-[480px] px-3 bg-gray-100 text-sm text-gray-800 rounded-lg outline-none appearance-none
|
||||||
`
|
`
|
||||||
|
const useInitialValue = (depend: any, dispatch: any) => {
|
||||||
const useInitialValue = <T,>(depend: T, dispatch: Dispatch<SetStateAction<T>>) => {
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(depend)
|
dispatch(depend)
|
||||||
}, [depend])
|
}, [depend])
|
||||||
|
@ -32,7 +31,7 @@ type Props = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Form = ({
|
const Form = ({
|
||||||
datasetId
|
datasetId,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { notify } = useContext(ToastContext)
|
const { notify } = useContext(ToastContext)
|
||||||
|
@ -44,7 +43,8 @@ const Form = ({
|
||||||
const [indexMethod, setIndexMethod] = useState(currentDataset?.indexing_technique)
|
const [indexMethod, setIndexMethod] = useState(currentDataset?.indexing_technique)
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
if (loading) return
|
if (loading)
|
||||||
|
return
|
||||||
if (!name?.trim()) {
|
if (!name?.trim()) {
|
||||||
notify({ type: 'error', message: t('datasetSettings.form.nameError') })
|
notify({ type: 'error', message: t('datasetSettings.form.nameError') })
|
||||||
return
|
return
|
||||||
|
@ -57,14 +57,16 @@ const Form = ({
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
permission,
|
permission,
|
||||||
indexing_technique: indexMethod
|
indexing_technique: indexMethod,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
||||||
await mutateDatasets()
|
await mutateDatasets()
|
||||||
} catch (e) {
|
}
|
||||||
|
catch (e) {
|
||||||
notify({ type: 'error', message: t('common.actionMsg.modificationFailed') })
|
notify({ type: 'error', message: t('common.actionMsg.modificationFailed') })
|
||||||
} finally {
|
}
|
||||||
|
finally {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
'use client'
|
'use client'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import copy from 'copy-to-clipboard'
|
import copy from 'copy-to-clipboard'
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
|
||||||
import { t } from 'i18next'
|
import { t } from 'i18next'
|
||||||
import s from './style.module.css'
|
import s from './style.module.css'
|
||||||
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
|
|
||||||
type IInputCopyProps = {
|
type IInputCopyProps = {
|
||||||
value?: string
|
value?: string
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
'use client'
|
'use client'
|
||||||
import React, { FC, useEffect } from 'react'
|
import type { FC } from 'react'
|
||||||
|
import React, { useEffect } from 'react'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
|
import Toast from '../../base/toast'
|
||||||
|
import s from './style.module.css'
|
||||||
import ExploreContext from '@/context/explore-context'
|
import ExploreContext from '@/context/explore-context'
|
||||||
import { App } from '@/models/explore'
|
import type { App } from '@/models/explore'
|
||||||
import Category from '@/app/components/explore/category'
|
import Category from '@/app/components/explore/category'
|
||||||
import AppCard from '@/app/components/explore/app-card'
|
import AppCard from '@/app/components/explore/app-card'
|
||||||
import { fetchAppList, installApp, fetchAppDetail } from '@/service/explore'
|
import { fetchAppDetail, fetchAppList, installApp } from '@/service/explore'
|
||||||
import { createApp } from '@/service/apps'
|
import { createApp } from '@/service/apps'
|
||||||
import CreateAppModal from '@/app/components/explore/create-app-modal'
|
import CreateAppModal from '@/app/components/explore/create-app-modal'
|
||||||
import Loading from '@/app/components/base/loading'
|
import Loading from '@/app/components/base/loading'
|
||||||
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
||||||
|
|
||||||
import s from './style.module.css'
|
const Apps: FC = () => {
|
||||||
import Toast from '../../base/toast'
|
|
||||||
|
|
||||||
const Apps: FC = ({ }) => {
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { setControlUpdateInstalledApps, hasEditPermission } = useContext(ExploreContext)
|
const { setControlUpdateInstalledApps, hasEditPermission } = useContext(ExploreContext)
|
||||||
|
@ -25,13 +25,14 @@ const Apps: FC = ({ }) => {
|
||||||
const [isLoaded, setIsLoaded] = React.useState(false)
|
const [isLoaded, setIsLoaded] = React.useState(false)
|
||||||
|
|
||||||
const currList = (() => {
|
const currList = (() => {
|
||||||
if(currCategory === '') return allList
|
if (currCategory === '')
|
||||||
|
return allList
|
||||||
return allList.filter(item => item.category === currCategory)
|
return allList.filter(item => item.category === currCategory)
|
||||||
})()
|
})()
|
||||||
const [categories, setCategories] = React.useState([])
|
const [categories, setCategories] = React.useState([])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const {categories, recommended_apps}:any = await fetchAppList()
|
const { categories, recommended_apps }: any = await fetchAppList()
|
||||||
setCategories(categories)
|
setCategories(categories)
|
||||||
setAllList(recommended_apps)
|
setAllList(recommended_apps)
|
||||||
setIsLoaded(true)
|
setIsLoaded(true)
|
||||||
|
@ -49,7 +50,7 @@ const Apps: FC = ({ }) => {
|
||||||
|
|
||||||
const [currApp, setCurrApp] = React.useState<App | null>(null)
|
const [currApp, setCurrApp] = React.useState<App | null>(null)
|
||||||
const [isShowCreateModal, setIsShowCreateModal] = React.useState(false)
|
const [isShowCreateModal, setIsShowCreateModal] = React.useState(false)
|
||||||
const onCreate = async ({name, icon, icon_background}: any) => {
|
const onCreate = async ({ name, icon, icon_background }: any) => {
|
||||||
const { app_model_config: model_config } = await fetchAppDetail(currApp?.app.id as string)
|
const { app_model_config: model_config } = await fetchAppDetail(currApp?.app.id as string)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -67,12 +68,13 @@ const Apps: FC = ({ }) => {
|
||||||
})
|
})
|
||||||
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
|
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
|
||||||
router.push(`/app/${app.id}/overview`)
|
router.push(`/app/${app.id}/overview`)
|
||||||
} catch (e) {
|
}
|
||||||
|
catch (e) {
|
||||||
Toast.notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
|
Toast.notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!isLoaded) {
|
if (!isLoaded) {
|
||||||
return (
|
return (
|
||||||
<div className='flex h-full items-center'>
|
<div className='flex h-full items-center'>
|
||||||
<Loading type='area' />
|
<Loading type='area' />
|
||||||
|
@ -95,7 +97,7 @@ const Apps: FC = ({ }) => {
|
||||||
<div
|
<div
|
||||||
className='flex mt-6 flex-col overflow-auto bg-gray-100 shrink-0 grow'
|
className='flex mt-6 flex-col overflow-auto bg-gray-100 shrink-0 grow'
|
||||||
style={{
|
style={{
|
||||||
maxHeight: 'calc(100vh - 243px)'
|
maxHeight: 'calc(100vh - 243px)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<nav
|
<nav
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
'use client'
|
'use client'
|
||||||
import React, { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import exploreI18n from '@/i18n/lang/explore.en'
|
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
|
import exploreI18n from '@/i18n/lang/explore.en'
|
||||||
|
|
||||||
const categoryI18n = exploreI18n.category
|
const categoryI18n = exploreI18n.category
|
||||||
|
|
||||||
export interface ICategoryProps {
|
export type ICategoryProps = {
|
||||||
className?: string
|
className?: string
|
||||||
list: string[]
|
list: string[]
|
||||||
value: string
|
value: string
|
||||||
|
@ -17,17 +18,17 @@ const Category: FC<ICategoryProps> = ({
|
||||||
className,
|
className,
|
||||||
list,
|
list,
|
||||||
value,
|
value,
|
||||||
onChange
|
onChange,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const itemClassName = (isSelected: boolean) => cn(isSelected ? 'bg-white text-primary-600 border-gray-200 font-semibold' : 'border-transparent font-medium','flex items-center h-7 px-3 border cursor-pointer rounded-lg')
|
const itemClassName = (isSelected: boolean) => cn(isSelected ? 'bg-white text-primary-600 border-gray-200 font-semibold' : 'border-transparent font-medium', 'flex items-center h-7 px-3 border cursor-pointer rounded-lg')
|
||||||
const itemStyle = (isSelected: boolean) => isSelected ? {boxShadow: '0px 1px 2px rgba(16, 24, 40, 0.05)'} : {}
|
const itemStyle = (isSelected: boolean) => isSelected ? { boxShadow: '0px 1px 2px rgba(16, 24, 40, 0.05)' } : {}
|
||||||
return (
|
return (
|
||||||
<div className={cn(className, 'flex space-x-1 text-[13px]')}>
|
<div className={cn(className, 'flex space-x-1 text-[13px]')}>
|
||||||
<div
|
<div
|
||||||
className={itemClassName('' === value)}
|
className={itemClassName(value === '')}
|
||||||
style={itemStyle('' === value)}
|
style={itemStyle(value === '')}
|
||||||
onClick={() => onChange('')}
|
onClick={() => onChange('')}
|
||||||
>
|
>
|
||||||
{t('explore.apps.allCategories')}
|
{t('explore.apps.allCategories')}
|
||||||
|
|
|
@ -2,19 +2,18 @@
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import s from './style.module.css'
|
||||||
import Modal from '@/app/components/base/modal'
|
import Modal from '@/app/components/base/modal'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
import AppIcon from '@/app/components/base/app-icon'
|
import AppIcon from '@/app/components/base/app-icon'
|
||||||
import EmojiPicker from '@/app/components/base/emoji-picker'
|
import EmojiPicker from '@/app/components/base/emoji-picker'
|
||||||
|
|
||||||
import s from './style.module.css'
|
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
appName: string,
|
appName: string
|
||||||
show: boolean,
|
show: boolean
|
||||||
onConfirm: (info: any) => void,
|
onConfirm: (info: any) => void
|
||||||
onHide: () => void,
|
onHide: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const CreateAppModal = ({
|
const CreateAppModal = ({
|
||||||
|
@ -31,7 +30,7 @@ const CreateAppModal = ({
|
||||||
const [emoji, setEmoji] = useState({ icon: '🤖', icon_background: '#FFEAD5' })
|
const [emoji, setEmoji] = useState({ icon: '🤖', icon_background: '#FFEAD5' })
|
||||||
|
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
if(!name.trim()) {
|
if (!name.trim()) {
|
||||||
Toast.notify({ type: 'error', message: t('explore.appCustomize.nameRequired') })
|
Toast.notify({ type: 'error', message: t('explore.appCustomize.nameRequired') })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -50,7 +49,7 @@ const CreateAppModal = ({
|
||||||
className={cn(s.modal, '!max-w-[480px]', 'px-8')}
|
className={cn(s.modal, '!max-w-[480px]', 'px-8')}
|
||||||
>
|
>
|
||||||
<span className={s.close} onClick={onHide}/>
|
<span className={s.close} onClick={onHide}/>
|
||||||
<div className={s.title}>{t('explore.appCustomize.title', {name: appName})}</div>
|
<div className={s.title}>{t('explore.appCustomize.title', { name: appName })}</div>
|
||||||
<div className={s.content}>
|
<div className={s.content}>
|
||||||
<div className={s.subTitle}>{t('explore.appCustomize.subTitle')}</div>
|
<div className={s.subTitle}>{t('explore.appCustomize.subTitle')}</div>
|
||||||
<div className='flex items-center justify-between space-x-3'>
|
<div className='flex items-center justify-between space-x-3'>
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
'use client'
|
'use client'
|
||||||
import React, { FC, useEffect, useState } from 'react'
|
import type { FC } from 'react'
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import ExploreContext from '@/context/explore-context'
|
import ExploreContext from '@/context/explore-context'
|
||||||
import Sidebar from '@/app/components/explore/sidebar'
|
import Sidebar from '@/app/components/explore/sidebar'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
import { fetchMembers } from '@/service/common'
|
import { fetchMembers } from '@/service/common'
|
||||||
import { InstalledApp } from '@/models/explore'
|
import type { InstalledApp } from '@/models/explore'
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
|
|
||||||
export interface IExploreProps {
|
export type IExploreProps = {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
const Explore: FC<IExploreProps> = ({
|
const Explore: FC<IExploreProps> = ({
|
||||||
children
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [controlUpdateInstalledApps, setControlUpdateInstalledApps] = useState(0)
|
const [controlUpdateInstalledApps, setControlUpdateInstalledApps] = useState(0)
|
||||||
|
@ -23,8 +24,9 @@ const Explore: FC<IExploreProps> = ({
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = `${t('explore.title')} - Dify`;
|
document.title = `${t('explore.title')} - Dify`;
|
||||||
(async () => {
|
(async () => {
|
||||||
const { accounts } = await fetchMembers({ url: '/workspaces/current/members', params: {}})
|
const { accounts } = await fetchMembers({ url: '/workspaces/current/members', params: {} })
|
||||||
if(!accounts) return
|
if (!accounts)
|
||||||
|
return
|
||||||
const currUser = accounts.find(account => account.id === userProfile.id)
|
const currUser = accounts.find(account => account.id === userProfile.id)
|
||||||
setHasEditPermission(currUser?.role !== 'normal')
|
setHasEditPermission(currUser?.role !== 'normal')
|
||||||
})()
|
})()
|
||||||
|
@ -39,7 +41,7 @@ const Explore: FC<IExploreProps> = ({
|
||||||
setControlUpdateInstalledApps,
|
setControlUpdateInstalledApps,
|
||||||
hasEditPermission,
|
hasEditPermission,
|
||||||
installedApps,
|
installedApps,
|
||||||
setInstalledApps
|
setInstalledApps,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
'use client'
|
'use client'
|
||||||
import React, { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
|
import React from 'react'
|
||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
import ExploreContext from '@/context/explore-context'
|
import ExploreContext from '@/context/explore-context'
|
||||||
import ChatApp from '@/app/components/share/chat'
|
import ChatApp from '@/app/components/share/chat'
|
||||||
import TextGenerationApp from '@/app/components/share/text-generation'
|
import TextGenerationApp from '@/app/components/share/text-generation'
|
||||||
import Loading from '@/app/components/base/loading'
|
import Loading from '@/app/components/base/loading'
|
||||||
|
|
||||||
export interface IInstalledAppProps {
|
export type IInstalledAppProps = {
|
||||||
id: string
|
id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ const InstalledApp: FC<IInstalledAppProps> = ({
|
||||||
const { installedApps } = useContext(ExploreContext)
|
const { installedApps } = useContext(ExploreContext)
|
||||||
const installedApp = installedApps.find(item => item.id === id)
|
const installedApp = installedApps.find(item => item.id === id)
|
||||||
|
|
||||||
if(!installedApp) {
|
if (!installedApp) {
|
||||||
return (
|
return (
|
||||||
<div className='flex h-full items-center'>
|
<div className='flex h-full items-center'>
|
||||||
<Loading type='area' />
|
<Loading type='area' />
|
||||||
|
@ -26,9 +27,11 @@ const InstalledApp: FC<IInstalledAppProps> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='h-full p-2'>
|
<div className='h-full p-2'>
|
||||||
{installedApp?.app.mode === 'chat' ? (
|
{installedApp?.app.mode === 'chat'
|
||||||
|
? (
|
||||||
<ChatApp isInstalledApp installedAppInfo={installedApp}/>
|
<ChatApp isInstalledApp installedAppInfo={installedApp}/>
|
||||||
): (
|
)
|
||||||
|
: (
|
||||||
<TextGenerationApp isInstalledApp installedAppInfo={installedApp}/>
|
<TextGenerationApp isInstalledApp installedAppInfo={installedApp}/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
'use client'
|
'use client'
|
||||||
import React, { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
|
import React from 'react'
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Popover from '@/app/components/base/popover'
|
|
||||||
import { TrashIcon } from '@heroicons/react/24/outline'
|
import { TrashIcon } from '@heroicons/react/24/outline'
|
||||||
|
|
||||||
import s from './style.module.css'
|
import s from './style.module.css'
|
||||||
|
import Popover from '@/app/components/base/popover'
|
||||||
|
|
||||||
const PinIcon = (
|
const PinIcon = (
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
@ -13,7 +14,7 @@ const PinIcon = (
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
|
|
||||||
export interface IItemOperationProps {
|
export type IItemOperationProps = {
|
||||||
className?: string
|
className?: string
|
||||||
isPinned: boolean
|
isPinned: boolean
|
||||||
isShowDelete: boolean
|
isShowDelete: boolean
|
||||||
|
@ -26,7 +27,7 @@ const ItemOperation: FC<IItemOperationProps> = ({
|
||||||
isPinned,
|
isPinned,
|
||||||
isShowDelete,
|
isShowDelete,
|
||||||
togglePin,
|
togglePin,
|
||||||
onDelete
|
onDelete,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
@ -52,8 +53,8 @@ const ItemOperation: FC<IItemOperationProps> = ({
|
||||||
trigger='click'
|
trigger='click'
|
||||||
position='br'
|
position='br'
|
||||||
btnElement={<div />}
|
btnElement={<div />}
|
||||||
btnClassName={(open) => cn(className, s.btn, 'h-6 w-6 rounded-md border-none p-1', open && '!bg-gray-100 !shadow-none')}
|
btnClassName={open => cn(className, s.btn, 'h-6 w-6 rounded-md border-none p-1', open && '!bg-gray-100 !shadow-none')}
|
||||||
className={`!w-[120px] h-fit !z-20`}
|
className={'!w-[120px] h-fit !z-20'}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
'use client'
|
'use client'
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
|
import s from './style.module.css'
|
||||||
import ItemOperation from '@/app/components/explore/item-operation'
|
import ItemOperation from '@/app/components/explore/item-operation'
|
||||||
import AppIcon from '@/app/components/base/app-icon'
|
import AppIcon from '@/app/components/base/app-icon'
|
||||||
|
|
||||||
import s from './style.module.css'
|
export type IAppNavItemProps = {
|
||||||
|
|
||||||
export interface IAppNavItemProps {
|
|
||||||
name: string
|
name: string
|
||||||
id: string
|
id: string
|
||||||
icon: string
|
icon: string
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
import type { Provider, ProviderAzureToken } from '@/models/common'
|
|
||||||
import { ProviderName } from '@/models/common'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline'
|
import { ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline'
|
||||||
import { useState, useEffect } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import ProviderInput from '../provider-input'
|
import ProviderInput from '../provider-input'
|
||||||
import useValidateToken, { ValidatedStatus, ValidatedStatusState } from '../provider-input/useValidateToken'
|
import type { ValidatedStatusState } from '../provider-input/useValidateToken'
|
||||||
|
import useValidateToken, { ValidatedStatus } from '../provider-input/useValidateToken'
|
||||||
import {
|
import {
|
||||||
ValidatedErrorIcon,
|
ValidatedErrorIcon,
|
||||||
|
ValidatedErrorOnAzureOpenaiTip,
|
||||||
ValidatedSuccessIcon,
|
ValidatedSuccessIcon,
|
||||||
ValidatingTip,
|
ValidatingTip,
|
||||||
ValidatedErrorOnAzureOpenaiTip
|
|
||||||
} from '../provider-input/Validate'
|
} from '../provider-input/Validate'
|
||||||
|
import { ProviderName } from '@/models/common'
|
||||||
|
import type { Provider, ProviderAzureToken } from '@/models/common'
|
||||||
|
|
||||||
interface IAzureProviderProps {
|
type IAzureProviderProps = {
|
||||||
provider: Provider
|
provider: Provider
|
||||||
onValidatedStatus: (status?: ValidatedStatusState) => void
|
onValidatedStatus: (status?: ValidatedStatusState) => void
|
||||||
onTokenChange: (token: ProviderAzureToken) => void
|
onTokenChange: (token: ProviderAzureToken) => void
|
||||||
|
@ -21,53 +22,50 @@ interface IAzureProviderProps {
|
||||||
const AzureProvider = ({
|
const AzureProvider = ({
|
||||||
provider,
|
provider,
|
||||||
onTokenChange,
|
onTokenChange,
|
||||||
onValidatedStatus
|
onValidatedStatus,
|
||||||
}: IAzureProviderProps) => {
|
}: IAzureProviderProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [token, setToken] = useState<ProviderAzureToken>(provider.provider_name === ProviderName.AZURE_OPENAI ? {...provider.token}: {})
|
const [token, setToken] = useState<ProviderAzureToken>(provider.provider_name === ProviderName.AZURE_OPENAI ? { ...provider.token } : {})
|
||||||
const [ validating, validatedStatus, setValidatedStatus, validate ] = useValidateToken(provider.provider_name)
|
const [validating, validatedStatus, setValidatedStatus, validate] = useValidateToken(provider.provider_name)
|
||||||
const handleFocus = (type: keyof ProviderAzureToken) => {
|
const handleFocus = (type: keyof ProviderAzureToken) => {
|
||||||
if (token[type] === (provider?.token as ProviderAzureToken)[type]) {
|
if (token[type] === (provider?.token as ProviderAzureToken)[type]) {
|
||||||
token[type] = ''
|
token[type] = ''
|
||||||
setToken({...token})
|
setToken({ ...token })
|
||||||
onTokenChange({...token})
|
onTokenChange({ ...token })
|
||||||
setValidatedStatus({})
|
setValidatedStatus({})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const handleChange = (type: keyof ProviderAzureToken, v: string, validate: any) => {
|
const handleChange = (type: keyof ProviderAzureToken, v: string, validate: any) => {
|
||||||
token[type] = v
|
token[type] = v
|
||||||
setToken({...token})
|
setToken({ ...token })
|
||||||
onTokenChange({...token})
|
onTokenChange({ ...token })
|
||||||
validate({...token}, {
|
validate({ ...token }, {
|
||||||
beforeValidating: () => {
|
beforeValidating: () => {
|
||||||
if (!token.openai_api_base || !token.openai_api_key) {
|
if (!token.openai_api_base || !token.openai_api_key) {
|
||||||
setValidatedStatus({})
|
setValidatedStatus({})
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const getValidatedIcon = () => {
|
const getValidatedIcon = () => {
|
||||||
if (validatedStatus.status === ValidatedStatus.Error || validatedStatus.status === ValidatedStatus.Exceed) {
|
if (validatedStatus.status === ValidatedStatus.Error || validatedStatus.status === ValidatedStatus.Exceed)
|
||||||
return <ValidatedErrorIcon />
|
return <ValidatedErrorIcon />
|
||||||
}
|
|
||||||
if (validatedStatus.status === ValidatedStatus.Success) {
|
if (validatedStatus.status === ValidatedStatus.Success)
|
||||||
return <ValidatedSuccessIcon />
|
return <ValidatedSuccessIcon />
|
||||||
}
|
}
|
||||||
}
|
|
||||||
const getValidatedTip = () => {
|
const getValidatedTip = () => {
|
||||||
if (validating) {
|
if (validating)
|
||||||
return <ValidatingTip />
|
return <ValidatingTip />
|
||||||
}
|
|
||||||
if (validatedStatus.status === ValidatedStatus.Error) {
|
if (validatedStatus.status === ValidatedStatus.Error)
|
||||||
return <ValidatedErrorOnAzureOpenaiTip errorMessage={validatedStatus.message ?? ''} />
|
return <ValidatedErrorOnAzureOpenaiTip errorMessage={validatedStatus.message ?? ''} />
|
||||||
}
|
}
|
||||||
}
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof onValidatedStatus === 'function') {
|
if (typeof onValidatedStatus === 'function')
|
||||||
onValidatedStatus(validatedStatus)
|
onValidatedStatus(validatedStatus)
|
||||||
}
|
|
||||||
}, [validatedStatus])
|
}, [validatedStatus])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -77,7 +75,7 @@ const AzureProvider = ({
|
||||||
name={t('common.provider.azure.apiBase')}
|
name={t('common.provider.azure.apiBase')}
|
||||||
placeholder={t('common.provider.azure.apiBasePlaceholder')}
|
placeholder={t('common.provider.azure.apiBasePlaceholder')}
|
||||||
value={token.openai_api_base}
|
value={token.openai_api_base}
|
||||||
onChange={(v) => handleChange('openai_api_base', v, validate)}
|
onChange={v => handleChange('openai_api_base', v, validate)}
|
||||||
onFocus={() => handleFocus('openai_api_base')}
|
onFocus={() => handleFocus('openai_api_base')}
|
||||||
validatedIcon={getValidatedIcon()}
|
validatedIcon={getValidatedIcon()}
|
||||||
/>
|
/>
|
||||||
|
@ -86,7 +84,7 @@ const AzureProvider = ({
|
||||||
name={t('common.provider.azure.apiKey')}
|
name={t('common.provider.azure.apiKey')}
|
||||||
placeholder={t('common.provider.azure.apiKeyPlaceholder')}
|
placeholder={t('common.provider.azure.apiKeyPlaceholder')}
|
||||||
value={token.openai_api_key}
|
value={token.openai_api_key}
|
||||||
onChange={(v) => handleChange('openai_api_key', v, validate)}
|
onChange={v => handleChange('openai_api_key', v, validate)}
|
||||||
onFocus={() => handleFocus('openai_api_key')}
|
onFocus={() => handleFocus('openai_api_key')}
|
||||||
validatedIcon={getValidatedIcon()}
|
validatedIcon={getValidatedIcon()}
|
||||||
validatedTip={getValidatedTip()}
|
validatedTip={getValidatedTip()}
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import { fetchProviders } from '@/service/common'
|
|
||||||
import ProviderItem from './provider-item'
|
|
||||||
import OpenaiHostedProvider from './openai-hosted-provider'
|
|
||||||
import type { ProviderHosted } from '@/models/common'
|
|
||||||
import { LockClosedIcon } from '@heroicons/react/24/solid'
|
import { LockClosedIcon } from '@heroicons/react/24/solid'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
import ProviderItem from './provider-item'
|
||||||
|
import OpenaiHostedProvider from './openai-hosted-provider'
|
||||||
|
import type { ProviderHosted } from '@/models/common'
|
||||||
|
import { fetchProviders } from '@/service/common'
|
||||||
import { IS_CE_EDITION } from '@/config'
|
import { IS_CE_EDITION } from '@/config'
|
||||||
|
|
||||||
const providersMap: {[k: string]: any} = {
|
const providersMap: { [k: string]: any } = {
|
||||||
'openai-custom': {
|
'openai-custom': {
|
||||||
icon: 'openai',
|
icon: 'openai',
|
||||||
name: 'OpenAI',
|
name: 'OpenAI',
|
||||||
|
@ -17,7 +17,7 @@ const providersMap: {[k: string]: any} = {
|
||||||
'azure_openai-custom': {
|
'azure_openai-custom': {
|
||||||
icon: 'azure',
|
icon: 'azure',
|
||||||
name: 'Azure OpenAI Service',
|
name: 'Azure OpenAI Service',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// const providersList = [
|
// const providersList = [
|
||||||
|
@ -56,7 +56,7 @@ const ProviderPage = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [activeProviderId, setActiveProviderId] = useState('')
|
const [activeProviderId, setActiveProviderId] = useState('')
|
||||||
const { data, mutate } = useSWR({ url: '/workspaces/current/providers' }, fetchProviders)
|
const { data, mutate } = useSWR({ url: '/workspaces/current/providers' }, fetchProviders)
|
||||||
const providers = data?.filter(provider => providersMap[`${provider.provider_name}-${provider.provider_type}`])?.map(provider => {
|
const providers = data?.filter(provider => providersMap[`${provider.provider_name}-${provider.provider_type}`])?.map((provider) => {
|
||||||
const providerKey = `${provider.provider_name}-${provider.provider_type}`
|
const providerKey = `${provider.provider_name}-${provider.provider_type}`
|
||||||
return {
|
return {
|
||||||
provider,
|
provider,
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
import type { Provider } from '@/models/common'
|
import { useEffect, useState } from 'react'
|
||||||
import { useState, useEffect } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import ProviderInput from '../provider-input'
|
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline'
|
import { ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline'
|
||||||
import useValidateToken, { ValidatedStatus, ValidatedStatusState } from '../provider-input/useValidateToken'
|
import ProviderInput from '../provider-input'
|
||||||
|
import type { ValidatedStatusState } from '../provider-input/useValidateToken'
|
||||||
|
import useValidateToken, { ValidatedStatus } from '../provider-input/useValidateToken'
|
||||||
import {
|
import {
|
||||||
ValidatedErrorIcon,
|
ValidatedErrorIcon,
|
||||||
|
ValidatedErrorOnOpenaiTip,
|
||||||
ValidatedSuccessIcon,
|
ValidatedSuccessIcon,
|
||||||
ValidatingTip,
|
ValidatingTip,
|
||||||
ValidatedExceedOnOpenaiTip,
|
|
||||||
ValidatedErrorOnOpenaiTip
|
|
||||||
} from '../provider-input/Validate'
|
} from '../provider-input/Validate'
|
||||||
|
import type { Provider } from '@/models/common'
|
||||||
|
|
||||||
interface IOpenaiProviderProps {
|
type IOpenaiProviderProps = {
|
||||||
provider: Provider
|
provider: Provider
|
||||||
onValidatedStatus: (status?: ValidatedStatusState) => void
|
onValidatedStatus: (status?: ValidatedStatusState) => void
|
||||||
onTokenChange: (token: string) => void
|
onTokenChange: (token: string) => void
|
||||||
|
@ -22,11 +22,11 @@ interface IOpenaiProviderProps {
|
||||||
const OpenaiProvider = ({
|
const OpenaiProvider = ({
|
||||||
provider,
|
provider,
|
||||||
onValidatedStatus,
|
onValidatedStatus,
|
||||||
onTokenChange
|
onTokenChange,
|
||||||
}: IOpenaiProviderProps) => {
|
}: IOpenaiProviderProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [token, setToken] = useState(provider.token as string || '')
|
const [token, setToken] = useState(provider.token as string || '')
|
||||||
const [ validating, validatedStatus, setValidatedStatus, validate ] = useValidateToken(provider.provider_name)
|
const [validating, validatedStatus, setValidatedStatus, validate] = useValidateToken(provider.provider_name)
|
||||||
const handleFocus = () => {
|
const handleFocus = () => {
|
||||||
if (token === provider.token) {
|
if (token === provider.token) {
|
||||||
setToken('')
|
setToken('')
|
||||||
|
@ -44,31 +44,28 @@ const OpenaiProvider = ({
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof onValidatedStatus === 'function') {
|
if (typeof onValidatedStatus === 'function')
|
||||||
onValidatedStatus(validatedStatus)
|
onValidatedStatus(validatedStatus)
|
||||||
}
|
|
||||||
}, [validatedStatus])
|
}, [validatedStatus])
|
||||||
|
|
||||||
const getValidatedIcon = () => {
|
const getValidatedIcon = () => {
|
||||||
if (validatedStatus?.status === ValidatedStatus.Error || validatedStatus.status === ValidatedStatus.Exceed) {
|
if (validatedStatus?.status === ValidatedStatus.Error || validatedStatus.status === ValidatedStatus.Exceed)
|
||||||
return <ValidatedErrorIcon />
|
return <ValidatedErrorIcon />
|
||||||
}
|
|
||||||
if (validatedStatus.status === ValidatedStatus.Success) {
|
if (validatedStatus.status === ValidatedStatus.Success)
|
||||||
return <ValidatedSuccessIcon />
|
return <ValidatedSuccessIcon />
|
||||||
}
|
}
|
||||||
}
|
|
||||||
const getValidatedTip = () => {
|
const getValidatedTip = () => {
|
||||||
if (validating) {
|
if (validating)
|
||||||
return <ValidatingTip />
|
return <ValidatingTip />
|
||||||
}
|
|
||||||
if (validatedStatus?.status === ValidatedStatus.Error) {
|
if (validatedStatus?.status === ValidatedStatus.Error)
|
||||||
return <ValidatedErrorOnOpenaiTip errorMessage={validatedStatus.message ?? ''} />
|
return <ValidatedErrorOnOpenaiTip errorMessage={validatedStatus.message ?? ''} />
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='px-4 pt-3 pb-4'>
|
<div className='px-4 pt-3 pb-4'>
|
||||||
|
|
|
@ -15,7 +15,7 @@ export const ValidatedSuccessIcon = () => {
|
||||||
export const ValidatingTip = () => {
|
export const ValidatingTip = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
return (
|
return (
|
||||||
<div className={`mt-2 text-primary-600 text-xs font-normal`}>
|
<div className={'mt-2 text-primary-600 text-xs font-normal'}>
|
||||||
{t('common.provider.validating')}
|
{t('common.provider.validating')}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -26,7 +26,7 @@ export const ValidatedExceedOnOpenaiTip = () => {
|
||||||
const { locale } = useContext(I18n)
|
const { locale } = useContext(I18n)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`mt-2 text-[#D92D20] text-xs font-normal`}>
|
<div className={'mt-2 text-[#D92D20] text-xs font-normal'}>
|
||||||
{t('common.provider.apiKeyExceedBill')}
|
{t('common.provider.apiKeyExceedBill')}
|
||||||
<Link
|
<Link
|
||||||
className='underline'
|
className='underline'
|
||||||
|
@ -42,7 +42,7 @@ export const ValidatedErrorOnOpenaiTip = ({ errorMessage }: { errorMessage: stri
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`mt-2 text-[#D92D20] text-xs font-normal`}>
|
<div className={'mt-2 text-[#D92D20] text-xs font-normal'}>
|
||||||
{t('common.provider.validatedError')}{errorMessage}
|
{t('common.provider.validatedError')}{errorMessage}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -52,7 +52,7 @@ export const ValidatedErrorOnAzureOpenaiTip = ({ errorMessage }: { errorMessage:
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`mt-2 text-[#D92D20] text-xs font-normal`}>
|
<div className={'mt-2 text-[#D92D20] text-xs font-normal'}>
|
||||||
{t('common.provider.validatedError')}{errorMessage}
|
{t('common.provider.validatedError')}{errorMessage}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { ChangeEvent } from 'react'
|
import type { ChangeEvent } from 'react'
|
||||||
import { ReactElement } from 'react-markdown/lib/react-markdown'
|
import type { ReactElement } from 'react-markdown/lib/react-markdown'
|
||||||
|
|
||||||
interface IProviderInputProps {
|
type IProviderInputProps = {
|
||||||
value?: string
|
value?: string
|
||||||
name: string
|
name: string
|
||||||
placeholder: string
|
placeholder: string
|
||||||
|
@ -20,9 +20,8 @@ const ProviderInput = ({
|
||||||
onChange,
|
onChange,
|
||||||
onFocus,
|
onFocus,
|
||||||
validatedIcon,
|
validatedIcon,
|
||||||
validatedTip
|
validatedTip,
|
||||||
}: IProviderInputProps) => {
|
}: IProviderInputProps) => {
|
||||||
|
|
||||||
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
const inputValue = e.target.value
|
const inputValue = e.target.value
|
||||||
onChange(inputValue)
|
onChange(inputValue)
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import { useState, useCallback, SetStateAction, Dispatch } from 'react'
|
import type { Dispatch, SetStateAction } from 'react'
|
||||||
|
import { useCallback, useState } from 'react'
|
||||||
import debounce from 'lodash-es/debounce'
|
import debounce from 'lodash-es/debounce'
|
||||||
import { DebouncedFunc } from 'lodash-es'
|
import type { DebouncedFunc } from 'lodash-es'
|
||||||
import { validateProviderKey } from '@/service/common'
|
import { validateProviderKey } from '@/service/common'
|
||||||
|
|
||||||
export enum ValidatedStatus {
|
export enum ValidatedStatus {
|
||||||
Success = 'success',
|
Success = 'success',
|
||||||
Error = 'error',
|
Error = 'error',
|
||||||
Exceed = 'exceed'
|
Exceed = 'exceed',
|
||||||
}
|
}
|
||||||
export type ValidatedStatusState = {
|
export type ValidatedStatusState = {
|
||||||
status?: ValidatedStatus,
|
status?: ValidatedStatus
|
||||||
message?: string
|
message?: string
|
||||||
}
|
}
|
||||||
// export type ValidatedStatusState = ValidatedStatus | undefined | ValidatedError
|
// export type ValidatedStatusState = ValidatedStatus | undefined | ValidatedError
|
||||||
|
@ -19,7 +20,7 @@ type ValidateTokenReturn = [
|
||||||
boolean,
|
boolean,
|
||||||
ValidatedStatusState,
|
ValidatedStatusState,
|
||||||
SetValidatedStatus,
|
SetValidatedStatus,
|
||||||
ValidateFn
|
ValidateFn,
|
||||||
]
|
]
|
||||||
export type ValidateFnConfig = {
|
export type ValidateFnConfig = {
|
||||||
beforeValidating: (token: any) => boolean
|
beforeValidating: (token: any) => boolean
|
||||||
|
@ -29,9 +30,9 @@ const useValidateToken = (providerName: string): ValidateTokenReturn => {
|
||||||
const [validating, setValidating] = useState(false)
|
const [validating, setValidating] = useState(false)
|
||||||
const [validatedStatus, setValidatedStatus] = useState<ValidatedStatusState>({})
|
const [validatedStatus, setValidatedStatus] = useState<ValidatedStatusState>({})
|
||||||
const validate = useCallback(debounce(async (token: string, config: ValidateFnConfig) => {
|
const validate = useCallback(debounce(async (token: string, config: ValidateFnConfig) => {
|
||||||
if (!config.beforeValidating(token)) {
|
if (!config.beforeValidating(token))
|
||||||
return false
|
return false
|
||||||
}
|
|
||||||
setValidating(true)
|
setValidating(true)
|
||||||
try {
|
try {
|
||||||
const res = await validateProviderKey({ url: `/workspaces/current/providers/${providerName}/token-validate`, body: { token } })
|
const res = await validateProviderKey({ url: `/workspaces/current/providers/${providerName}/token-validate`, body: { token } })
|
||||||
|
@ -39,9 +40,11 @@ const useValidateToken = (providerName: string): ValidateTokenReturn => {
|
||||||
res.result === 'success'
|
res.result === 'success'
|
||||||
? { status: ValidatedStatus.Success }
|
? { status: ValidatedStatus.Success }
|
||||||
: { status: ValidatedStatus.Error, message: res.error })
|
: { status: ValidatedStatus.Error, message: res.error })
|
||||||
} catch (e: any) {
|
}
|
||||||
|
catch (e: any) {
|
||||||
setValidatedStatus({ status: ValidatedStatus.Error, message: e.message })
|
setValidatedStatus({ status: ValidatedStatus.Error, message: e.message })
|
||||||
} finally {
|
}
|
||||||
|
finally {
|
||||||
setValidating(false)
|
setValidating(false)
|
||||||
}
|
}
|
||||||
}, 500), [])
|
}, 500), [])
|
||||||
|
@ -50,7 +53,7 @@ const useValidateToken = (providerName: string): ValidateTokenReturn => {
|
||||||
validating,
|
validating,
|
||||||
validatedStatus,
|
validatedStatus,
|
||||||
setValidatedStatus,
|
setValidatedStatus,
|
||||||
validate
|
validate,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
import s from './index.module.css'
|
|
||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
import Indicator from '../../../indicator'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import type { Provider, ProviderAzureToken } from '@/models/common'
|
import Indicator from '../../../indicator'
|
||||||
import { ProviderName } from '@/models/common'
|
|
||||||
import OpenaiProvider from '../openai-provider'
|
import OpenaiProvider from '../openai-provider'
|
||||||
import AzureProvider from '../azure-provider'
|
import AzureProvider from '../azure-provider'
|
||||||
import { ValidatedStatus, ValidatedStatusState } from '../provider-input/useValidateToken'
|
import type { ValidatedStatusState } from '../provider-input/useValidateToken'
|
||||||
|
import { ValidatedStatus } from '../provider-input/useValidateToken'
|
||||||
|
import s from './index.module.css'
|
||||||
|
import type { Provider, ProviderAzureToken } from '@/models/common'
|
||||||
|
import { ProviderName } from '@/models/common'
|
||||||
import { updateProviderAIKey } from '@/service/common'
|
import { updateProviderAIKey } from '@/service/common'
|
||||||
import { ToastContext } from '@/app/components/base/toast'
|
import { ToastContext } from '@/app/components/base/toast'
|
||||||
|
|
||||||
interface IProviderItemProps {
|
type IProviderItemProps = {
|
||||||
icon: string
|
icon: string
|
||||||
name: string
|
name: string
|
||||||
provider: Provider
|
provider: Provider
|
||||||
|
@ -26,7 +27,7 @@ const ProviderItem = ({
|
||||||
name,
|
name,
|
||||||
provider,
|
provider,
|
||||||
onActive,
|
onActive,
|
||||||
onSave
|
onSave,
|
||||||
}: IProviderItemProps) => {
|
}: IProviderItemProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [validatedStatus, setValidatedStatus] = useState<ValidatedStatusState>()
|
const [validatedStatus, setValidatedStatus] = useState<ValidatedStatusState>()
|
||||||
|
@ -35,7 +36,7 @@ const ProviderItem = ({
|
||||||
const [token, setToken] = useState<ProviderAzureToken | string>(
|
const [token, setToken] = useState<ProviderAzureToken | string>(
|
||||||
provider.provider_name === 'azure_openai'
|
provider.provider_name === 'azure_openai'
|
||||||
? { openai_api_base: '', openai_api_key: '' }
|
? { openai_api_base: '', openai_api_key: '' }
|
||||||
: ''
|
: '',
|
||||||
)
|
)
|
||||||
const id = `${provider.provider_name}-${provider.provider_type}`
|
const id = `${provider.provider_name}-${provider.provider_type}`
|
||||||
const isOpen = id === activeId
|
const isOpen = id === activeId
|
||||||
|
@ -44,26 +45,30 @@ const ProviderItem = ({
|
||||||
|
|
||||||
const providerTokenHasSetted = () => {
|
const providerTokenHasSetted = () => {
|
||||||
if (provider.provider_name === ProviderName.AZURE_OPENAI) {
|
if (provider.provider_name === ProviderName.AZURE_OPENAI) {
|
||||||
return provider.token && provider.token.openai_api_base && provider.token.openai_api_key ? {
|
return (provider.token && provider.token.openai_api_base && provider.token.openai_api_key)
|
||||||
|
? {
|
||||||
openai_api_base: provider.token.openai_api_base,
|
openai_api_base: provider.token.openai_api_base,
|
||||||
openai_api_key: provider.token.openai_api_key
|
openai_api_key: provider.token.openai_api_key,
|
||||||
}: undefined
|
|
||||||
}
|
}
|
||||||
if (provider.provider_name === ProviderName.OPENAI) {
|
: undefined
|
||||||
|
}
|
||||||
|
if (provider.provider_name === ProviderName.OPENAI)
|
||||||
return provider.token
|
return provider.token
|
||||||
}
|
}
|
||||||
}
|
|
||||||
const handleUpdateToken = async () => {
|
const handleUpdateToken = async () => {
|
||||||
if (loading) return
|
if (loading)
|
||||||
|
return
|
||||||
if (validatedStatus?.status === ValidatedStatus.Success) {
|
if (validatedStatus?.status === ValidatedStatus.Success) {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
await updateProviderAIKey({ url: `/workspaces/current/providers/${provider.provider_name}/token`, body: { token } })
|
await updateProviderAIKey({ url: `/workspaces/current/providers/${provider.provider_name}/token`, body: { token } })
|
||||||
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
||||||
onActive('')
|
onActive('')
|
||||||
} catch (e) {
|
}
|
||||||
|
catch (e) {
|
||||||
notify({ type: 'error', message: t('common.provider.saveFailed') })
|
notify({ type: 'error', message: t('common.provider.saveFailed') })
|
||||||
} finally {
|
}
|
||||||
|
finally {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
onSave()
|
onSave()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useSelectedLayoutSegment, useRouter } from 'next/navigation'
|
import { useRouter, useSelectedLayoutSegment } from 'next/navigation'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { CircleStackIcon, PuzzlePieceIcon } from '@heroicons/react/24/outline'
|
import { CircleStackIcon, PuzzlePieceIcon } from '@heroicons/react/24/outline'
|
||||||
import { CommandLineIcon, Squares2X2Icon } from '@heroicons/react/24/solid'
|
import { CommandLineIcon, Squares2X2Icon } from '@heroicons/react/24/solid'
|
||||||
|
@ -15,9 +15,9 @@ import NewAppDialog from '@/app/(commonLayout)/apps/NewAppDialog'
|
||||||
import { WorkspaceProvider } from '@/context/workspace-context'
|
import { WorkspaceProvider } from '@/context/workspace-context'
|
||||||
import { useDatasetsContext } from '@/context/datasets-context'
|
import { useDatasetsContext } from '@/context/datasets-context'
|
||||||
|
|
||||||
const BuildAppsIcon = ({isSelected}: {isSelected: boolean}) => (
|
const BuildAppsIcon = ({ isSelected }: { isSelected: boolean }) => (
|
||||||
<svg className='mr-1' width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg className='mr-1' width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M13.6666 4.85221L7.99998 8.00036M7.99998 8.00036L2.33331 4.85221M7.99998 8.00036L8 14.3337M14 10.7061V5.29468C14 5.06625 14 4.95204 13.9663 4.85017C13.9366 4.76005 13.8879 4.67733 13.8236 4.60754C13.7509 4.52865 13.651 4.47318 13.4514 4.36224L8.51802 1.6215C8.32895 1.51646 8.23442 1.46395 8.1343 1.44336C8.0457 1.42513 7.95431 1.42513 7.8657 1.44336C7.76559 1.46395 7.67105 1.51646 7.48198 1.6215L2.54865 4.36225C2.34896 4.47318 2.24912 4.52865 2.17642 4.60754C2.11211 4.67733 2.06343 4.76005 2.03366 4.85017C2 4.95204 2 5.06625 2 5.29468V10.7061C2 10.9345 2 11.0487 2.03366 11.1506C2.06343 11.2407 2.11211 11.3234 2.17642 11.3932C2.24912 11.4721 2.34897 11.5276 2.54865 11.6385L7.48198 14.3793C7.67105 14.4843 7.76559 14.5368 7.8657 14.5574C7.95431 14.5756 8.0457 14.5756 8.1343 14.5574C8.23442 14.5368 8.32895 14.4843 8.51802 14.3793L13.4514 11.6385C13.651 11.5276 13.7509 11.4721 13.8236 11.3932C13.8879 11.3234 13.9366 11.2407 13.9663 11.1506C14 11.0487 14 10.9345 14 10.7061Z" stroke={isSelected ? '#155EEF': '#667085'} strokeWidth="1.25" strokeLinecap="round" strokeLinejoin="round"/>
|
<path d="M13.6666 4.85221L7.99998 8.00036M7.99998 8.00036L2.33331 4.85221M7.99998 8.00036L8 14.3337M14 10.7061V5.29468C14 5.06625 14 4.95204 13.9663 4.85017C13.9366 4.76005 13.8879 4.67733 13.8236 4.60754C13.7509 4.52865 13.651 4.47318 13.4514 4.36224L8.51802 1.6215C8.32895 1.51646 8.23442 1.46395 8.1343 1.44336C8.0457 1.42513 7.95431 1.42513 7.8657 1.44336C7.76559 1.46395 7.67105 1.51646 7.48198 1.6215L2.54865 4.36225C2.34896 4.47318 2.24912 4.52865 2.17642 4.60754C2.11211 4.67733 2.06343 4.76005 2.03366 4.85017C2 4.95204 2 5.06625 2 5.29468V10.7061C2 10.9345 2 11.0487 2.03366 11.1506C2.06343 11.2407 2.11211 11.3234 2.17642 11.3932C2.24912 11.4721 2.34897 11.5276 2.54865 11.6385L7.48198 14.3793C7.67105 14.4843 7.76559 14.5368 7.8657 14.5574C7.95431 14.5756 8.0457 14.5756 8.1343 14.5574C8.23442 14.5368 8.32895 14.4843 8.51802 14.3793L13.4514 11.6385C13.651 11.5276 13.7509 11.4721 13.8236 11.3932C13.8879 11.3234 13.9366 11.2407 13.9663 11.1506C14 11.0487 14 10.9345 14 10.7061Z" stroke={isSelected ? '#155EEF' : '#667085'} strokeWidth="1.25" strokeLinecap="round" strokeLinejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -51,22 +51,22 @@ const Header: FC<IHeaderProps> = ({ appItems, curApp, userProfile, onLogout, lan
|
||||||
<div className={classNames(
|
<div className={classNames(
|
||||||
'sticky top-0 left-0 right-0 z-20 flex bg-gray-100 grow-0 shrink-0 basis-auto h-14',
|
'sticky top-0 left-0 right-0 z-20 flex bg-gray-100 grow-0 shrink-0 basis-auto h-14',
|
||||||
s.header,
|
s.header,
|
||||||
isBordered ? 'border-b border-gray-200' : ''
|
isBordered ? 'border-b border-gray-200' : '',
|
||||||
)}>
|
)}>
|
||||||
<div className={classNames(
|
<div className={classNames(
|
||||||
s[`header-${langeniusVersionInfo.current_env}`],
|
s[`header-${langeniusVersionInfo.current_env}`],
|
||||||
'flex flex-1 items-center justify-between px-4'
|
'flex flex-1 items-center justify-between px-4',
|
||||||
)}>
|
)}>
|
||||||
<div className='flex items-center'>
|
<div className='flex items-center'>
|
||||||
<Link href="/apps" className='flex items-center mr-3'>
|
<Link href="/apps" className='flex items-center mr-3'>
|
||||||
<div className={s['logo']} />
|
<div className={s.logo} />
|
||||||
</Link>
|
</Link>
|
||||||
{/* Add it when has many stars */}
|
{/* Add it when has many stars */}
|
||||||
<div className='
|
<div className='
|
||||||
flex items-center h-[26px] px-2 bg-white
|
flex items-center h-[26px] px-2 bg-white
|
||||||
border border-solid border-[#E5E7EB] rounded-l-[6px] rounded-r-[6px]
|
border border-solid border-[#E5E7EB] rounded-l-[6px] rounded-r-[6px]
|
||||||
'>
|
'>
|
||||||
<div className={s['alpha']} />
|
<div className={s.alpha} />
|
||||||
<div className='ml-1 text-xs font-semibold text-gray-700'>{t('common.menus.status')}</div>
|
<div className='ml-1 text-xs font-semibold text-gray-700'>{t('common.menus.status')}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -74,7 +74,7 @@ const Header: FC<IHeaderProps> = ({ appItems, curApp, userProfile, onLogout, lan
|
||||||
<Link href="/explore/apps" className={classNames(
|
<Link href="/explore/apps" className={classNames(
|
||||||
navClassName, 'group',
|
navClassName, 'group',
|
||||||
isExplore && 'bg-white shadow-[0_2px_5px_-1px_rgba(0,0,0,0.05),0_2px_4px_-2px_rgba(0,0,0,0.05)]',
|
isExplore && 'bg-white shadow-[0_2px_5px_-1px_rgba(0,0,0,0.05),0_2px_4px_-2px_rgba(0,0,0,0.05)]',
|
||||||
isExplore ? 'text-primary-600' : 'text-gray-500 hover:bg-gray-200 hover:text-gray-700'
|
isExplore ? 'text-primary-600' : 'text-gray-500 hover:bg-gray-200 hover:text-gray-700',
|
||||||
)}>
|
)}>
|
||||||
<Squares2X2Icon className='mr-1 w-[18px] h-[18px]' />
|
<Squares2X2Icon className='mr-1 w-[18px] h-[18px]' />
|
||||||
{t('common.menus.explore')}
|
{t('common.menus.explore')}
|
||||||
|
@ -84,13 +84,13 @@ const Header: FC<IHeaderProps> = ({ appItems, curApp, userProfile, onLogout, lan
|
||||||
text={t('common.menus.apps')}
|
text={t('common.menus.apps')}
|
||||||
activeSegment={['apps', 'app']}
|
activeSegment={['apps', 'app']}
|
||||||
link='/apps'
|
link='/apps'
|
||||||
curNav={curApp && { id: curApp.id, name: curApp.name ,icon: curApp.icon, icon_background: curApp.icon_background}}
|
curNav={curApp && { id: curApp.id, name: curApp.name, icon: curApp.icon, icon_background: curApp.icon_background }}
|
||||||
navs={appItems.map(item => ({
|
navs={appItems.map(item => ({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
link: `/app/${item.id}/overview`,
|
link: `/app/${item.id}/overview`,
|
||||||
icon: item.icon,
|
icon: item.icon,
|
||||||
icon_background: item.icon_background
|
icon_background: item.icon_background,
|
||||||
}))}
|
}))}
|
||||||
createText={t('common.menus.newApp')}
|
createText={t('common.menus.newApp')}
|
||||||
onCreate={() => setShowNewAppDialog(true)}
|
onCreate={() => setShowNewAppDialog(true)}
|
||||||
|
@ -98,7 +98,7 @@ const Header: FC<IHeaderProps> = ({ appItems, curApp, userProfile, onLogout, lan
|
||||||
<Link href="/plugins-coming-soon" className={classNames(
|
<Link href="/plugins-coming-soon" className={classNames(
|
||||||
navClassName, 'group',
|
navClassName, 'group',
|
||||||
isPluginsComingSoon && 'bg-white shadow-[0_2px_5px_-1px_rgba(0,0,0,0.05),0_2px_4px_-2px_rgba(0,0,0,0.05)]',
|
isPluginsComingSoon && 'bg-white shadow-[0_2px_5px_-1px_rgba(0,0,0,0.05),0_2px_4px_-2px_rgba(0,0,0,0.05)]',
|
||||||
isPluginsComingSoon ? 'text-primary-600' : 'text-gray-500 hover:bg-gray-200 hover:text-gray-700'
|
isPluginsComingSoon ? 'text-primary-600' : 'text-gray-500 hover:bg-gray-200 hover:text-gray-700',
|
||||||
)}>
|
)}>
|
||||||
<PuzzlePieceIcon className='mr-1 w-[18px] h-[18px]' />
|
<PuzzlePieceIcon className='mr-1 w-[18px] h-[18px]' />
|
||||||
{t('common.menus.plugins')}
|
{t('common.menus.plugins')}
|
||||||
|
@ -114,7 +114,7 @@ const Header: FC<IHeaderProps> = ({ appItems, curApp, userProfile, onLogout, lan
|
||||||
name: dataset.name,
|
name: dataset.name,
|
||||||
link: `/datasets/${dataset.id}/documents`,
|
link: `/datasets/${dataset.id}/documents`,
|
||||||
icon: dataset.icon,
|
icon: dataset.icon,
|
||||||
icon_background: dataset.icon_background
|
icon_background: dataset.icon_background,
|
||||||
}))}
|
}))}
|
||||||
createText={t('common.menus.newDataset')}
|
createText={t('common.menus.newDataset')}
|
||||||
onCreate={() => router.push('/datasets/create')}
|
onCreate={() => router.push('/datasets/create')}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
'use client'
|
'use client'
|
||||||
import React, { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
|
import React from 'react'
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
import { appDefaultIconBackground } from '@/config/index'
|
import { appDefaultIconBackground } from '@/config/index'
|
||||||
import AppIcon from '@/app/components/base/app-icon'
|
import AppIcon from '@/app/components/base/app-icon'
|
||||||
|
|
||||||
export interface IAppInfoProps {
|
export type IAppInfoProps = {
|
||||||
className?: string
|
className?: string
|
||||||
icon: string
|
icon: string
|
||||||
icon_background?: string
|
icon_background?: string
|
||||||
|
@ -15,7 +16,7 @@ const AppInfo: FC<IAppInfoProps> = ({
|
||||||
className,
|
className,
|
||||||
icon,
|
icon,
|
||||||
icon_background,
|
icon_background,
|
||||||
name
|
name,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={cn(className, 'flex items-center space-x-3')}>
|
<div className={cn(className, 'flex items-center space-x-3')}>
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import React, { useEffect, useRef } from 'react'
|
import React, { useRef } from 'react'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import {
|
import {
|
||||||
ChatBubbleOvalLeftEllipsisIcon,
|
ChatBubbleOvalLeftEllipsisIcon,
|
||||||
PencilSquareIcon
|
PencilSquareIcon,
|
||||||
} from '@heroicons/react/24/outline'
|
} from '@heroicons/react/24/outline'
|
||||||
import { ChatBubbleOvalLeftEllipsisIcon as ChatBubbleOvalLeftEllipsisSolidIcon, } from '@heroicons/react/24/solid'
|
import { ChatBubbleOvalLeftEllipsisIcon as ChatBubbleOvalLeftEllipsisSolidIcon } from '@heroicons/react/24/solid'
|
||||||
|
import { useInfiniteScroll } from 'ahooks'
|
||||||
import Button from '../../../base/button'
|
import Button from '../../../base/button'
|
||||||
import AppInfo from '@/app/components/share/chat/sidebar/app-info'
|
import AppInfo from '@/app/components/share/chat/sidebar/app-info'
|
||||||
// import Card from './card'
|
// import Card from './card'
|
||||||
import type { ConversationItem, SiteInfo } from '@/models/share'
|
import type { ConversationItem, SiteInfo } from '@/models/share'
|
||||||
import { useInfiniteScroll } from 'ahooks'
|
|
||||||
import { fetchConversations } from '@/service/share'
|
import { fetchConversations } from '@/service/share'
|
||||||
|
|
||||||
function classNames(...classes: any[]) {
|
function classNames(...classes: any[]) {
|
||||||
|
@ -25,7 +25,7 @@ export type ISidebarProps = {
|
||||||
isInstalledApp: boolean
|
isInstalledApp: boolean
|
||||||
installedAppId?: string
|
installedAppId?: string
|
||||||
siteInfo: SiteInfo
|
siteInfo: SiteInfo
|
||||||
onMoreLoaded: (res: {data: ConversationItem[], has_more: boolean}) => void
|
onMoreLoaded: (res: { data: ConversationItem[]; has_more: boolean }) => void
|
||||||
isNoMore: boolean
|
isNoMore: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,19 +45,19 @@ const Sidebar: FC<ISidebarProps> = ({
|
||||||
|
|
||||||
useInfiniteScroll(
|
useInfiniteScroll(
|
||||||
async () => {
|
async () => {
|
||||||
if(!isNoMore) {
|
if (!isNoMore) {
|
||||||
const lastId = list[list.length - 1].id
|
const lastId = list[list.length - 1].id
|
||||||
const { data: conversations, has_more }: any = await fetchConversations(isInstalledApp, installedAppId, lastId)
|
const { data: conversations, has_more }: any = await fetchConversations(isInstalledApp, installedAppId, lastId)
|
||||||
onMoreLoaded({ data: conversations, has_more })
|
onMoreLoaded({ data: conversations, has_more })
|
||||||
}
|
}
|
||||||
return {list: []}
|
return { list: [] }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
target: listRef,
|
target: listRef,
|
||||||
isNoMore: () => {
|
isNoMore: () => {
|
||||||
return isNoMore
|
return isNoMore
|
||||||
},
|
},
|
||||||
reloadDeps: [isNoMore]
|
reloadDeps: [isNoMore],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ const Sidebar: FC<ISidebarProps> = ({
|
||||||
className={
|
className={
|
||||||
classNames(
|
classNames(
|
||||||
isInstalledApp ? 'tablet:h-[calc(100vh_-_74px)]' : 'tablet:h-[calc(100vh_-_3rem)]',
|
isInstalledApp ? 'tablet:h-[calc(100vh_-_74px)]' : 'tablet:h-[calc(100vh_-_3rem)]',
|
||||||
"shrink-0 flex flex-col bg-white pc:w-[244px] tablet:w-[192px] mobile:w-[240px] border-r border-gray-200 mobile:h-screen"
|
'shrink-0 flex flex-col bg-white pc:w-[244px] tablet:w-[192px] mobile:w-[240px] border-r border-gray-200 mobile:h-screen',
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
|
@ -38,7 +38,8 @@ const ConfigSence: FC<IConfigSenceProps> = ({
|
||||||
<div className='w-full mt-4' key={item.key}>
|
<div className='w-full mt-4' key={item.key}>
|
||||||
<label className='text-gray-900 text-sm font-medium'>{item.name}</label>
|
<label className='text-gray-900 text-sm font-medium'>{item.name}</label>
|
||||||
<div className='mt-2'>
|
<div className='mt-2'>
|
||||||
{item.type === 'select' ? (
|
{item.type === 'select'
|
||||||
|
? (
|
||||||
<Select
|
<Select
|
||||||
className='w-full'
|
className='w-full'
|
||||||
defaultValue={inputs[item.key]}
|
defaultValue={inputs[item.key]}
|
||||||
|
@ -47,7 +48,8 @@ const ConfigSence: FC<IConfigSenceProps> = ({
|
||||||
allowSearch={false}
|
allowSearch={false}
|
||||||
bgClassName='bg-gray-50'
|
bgClassName='bg-gray-50'
|
||||||
/>
|
/>
|
||||||
) : (
|
)
|
||||||
|
: (
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="block w-full p-2 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 sm:text-xs focus:ring-blue-500 focus:border-blue-500 "
|
className="block w-full p-2 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 sm:text-xs focus:ring-blue-500 focus:border-blue-500 "
|
||||||
|
|
|
@ -1,35 +1,38 @@
|
||||||
const isDevelopment = process.env.NODE_ENV === 'development';
|
/* eslint-disable import/no-mutable-exports */
|
||||||
|
const isDevelopment = process.env.NODE_ENV === 'development'
|
||||||
|
|
||||||
export let apiPrefix = '';
|
export let apiPrefix = ''
|
||||||
let publicApiPrefix = '';
|
export let publicApiPrefix = ''
|
||||||
|
|
||||||
// NEXT_PUBLIC_API_PREFIX=/console/api NEXT_PUBLIC_PUBLIC_API_PREFIX=/api npm run start
|
// NEXT_PUBLIC_API_PREFIX=/console/api NEXT_PUBLIC_PUBLIC_API_PREFIX=/api npm run start
|
||||||
if (process.env.NEXT_PUBLIC_API_PREFIX && process.env.NEXT_PUBLIC_PUBLIC_API_PREFIX) {
|
if (process.env.NEXT_PUBLIC_API_PREFIX && process.env.NEXT_PUBLIC_PUBLIC_API_PREFIX) {
|
||||||
apiPrefix = process.env.NEXT_PUBLIC_API_PREFIX;
|
apiPrefix = process.env.NEXT_PUBLIC_API_PREFIX
|
||||||
publicApiPrefix = process.env.NEXT_PUBLIC_PUBLIC_API_PREFIX;
|
publicApiPrefix = process.env.NEXT_PUBLIC_PUBLIC_API_PREFIX
|
||||||
} else if (
|
}
|
||||||
globalThis.document?.body?.getAttribute('data-api-prefix') &&
|
else if (
|
||||||
globalThis.document?.body?.getAttribute('data-pubic-api-prefix')
|
globalThis.document?.body?.getAttribute('data-api-prefix')
|
||||||
|
&& globalThis.document?.body?.getAttribute('data-pubic-api-prefix')
|
||||||
) {
|
) {
|
||||||
// Not bulild can not get env from process.env.NEXT_PUBLIC_ in browser https://nextjs.org/docs/basic-features/environment-variables#exposing-environment-variables-to-the-browser
|
// Not bulild can not get env from process.env.NEXT_PUBLIC_ in browser https://nextjs.org/docs/basic-features/environment-variables#exposing-environment-variables-to-the-browser
|
||||||
apiPrefix = globalThis.document.body.getAttribute('data-api-prefix') as string
|
apiPrefix = globalThis.document.body.getAttribute('data-api-prefix') as string
|
||||||
publicApiPrefix = globalThis.document.body.getAttribute('data-pubic-api-prefix') as string
|
publicApiPrefix = globalThis.document.body.getAttribute('data-pubic-api-prefix') as string
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
if (isDevelopment) {
|
if (isDevelopment) {
|
||||||
apiPrefix = 'https://cloud.dify.dev/console/api';
|
apiPrefix = 'https://cloud.dify.dev/console/api'
|
||||||
publicApiPrefix = 'https://dev.udify.app/api';
|
publicApiPrefix = 'https://dev.udify.app/api'
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// const domainParts = globalThis.location?.host?.split('.');
|
// const domainParts = globalThis.location?.host?.split('.');
|
||||||
// in production env, the host is dify.app . In other env, the host is [dev].dify.app
|
// in production env, the host is dify.app . In other env, the host is [dev].dify.app
|
||||||
// const env = domainParts.length === 2 ? 'ai' : domainParts?.[0];
|
// const env = domainParts.length === 2 ? 'ai' : domainParts?.[0];
|
||||||
apiPrefix = '/console/api';
|
apiPrefix = '/console/api'
|
||||||
publicApiPrefix = `/api`; // avoid browser private mode api cross origin
|
publicApiPrefix = '/api' // avoid browser private mode api cross origin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const API_PREFIX: string = apiPrefix
|
||||||
export const API_PREFIX: string = apiPrefix;
|
export const PUBLIC_API_PREFIX: string = publicApiPrefix
|
||||||
export const PUBLIC_API_PREFIX: string = publicApiPrefix;
|
|
||||||
|
|
||||||
const EDITION = process.env.NEXT_PUBLIC_EDITION || globalThis.document?.body?.getAttribute('data-public-edition')
|
const EDITION = process.env.NEXT_PUBLIC_EDITION || globalThis.document?.body?.getAttribute('data-public-edition')
|
||||||
export const IS_CE_EDITION = EDITION === 'SELF_HOSTED'
|
export const IS_CE_EDITION = EDITION === 'SELF_HOSTED'
|
||||||
|
@ -75,15 +78,15 @@ export const LOCALE_COOKIE_NAME = 'locale'
|
||||||
|
|
||||||
export const DEFAULT_VALUE_MAX_LEN = 48
|
export const DEFAULT_VALUE_MAX_LEN = 48
|
||||||
|
|
||||||
export const zhRegex = /^[\u4e00-\u9fa5]$/m
|
export const zhRegex = /^[\u4E00-\u9FA5]$/m
|
||||||
export const emojiRegex = /^[\uD800-\uDBFF][\uDC00-\uDFFF]$/m
|
export const emojiRegex = /^[\uD800-\uDBFF][\uDC00-\uDFFF]$/m
|
||||||
export const emailRegex = /^[\w\.-]+@([\w-]+\.)+[\w-]{2,}$/m
|
export const emailRegex = /^[\w\.-]+@([\w-]+\.)+[\w-]{2,}$/m
|
||||||
const MAX_ZN_VAR_NAME_LENGHT = 8
|
const MAX_ZN_VAR_NAME_LENGHT = 8
|
||||||
const MAX_EN_VAR_VALUE_LENGHT = 16
|
const MAX_EN_VAR_VALUE_LENGHT = 16
|
||||||
export const getMaxVarNameLength = (value: string) => {
|
export const getMaxVarNameLength = (value: string) => {
|
||||||
if (zhRegex.test(value)) {
|
if (zhRegex.test(value))
|
||||||
return MAX_ZN_VAR_NAME_LENGHT
|
return MAX_ZN_VAR_NAME_LENGHT
|
||||||
}
|
|
||||||
return MAX_EN_VAR_VALUE_LENGHT
|
return MAX_EN_VAR_VALUE_LENGHT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,12 +97,9 @@ export const VAR_ITEM_TEMPLATE = {
|
||||||
name: '',
|
name: '',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
max_length: DEFAULT_VALUE_MAX_LEN,
|
max_length: DEFAULT_VALUE_MAX_LEN,
|
||||||
required: true
|
required: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const appDefaultIconBackground = '#D5F5F6'
|
export const appDefaultIconBackground = '#D5F5F6'
|
||||||
|
|
||||||
export const NEED_REFRESH_APP_LIST_KEY = 'needRefreshAppList'
|
export const NEED_REFRESH_APP_LIST_KEY = 'needRefreshAppList'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { createContext } from 'use-context-selector'
|
import { createContext } from 'use-context-selector'
|
||||||
|
import type { DataSet } from '@/models/datasets'
|
||||||
|
|
||||||
const DatasetDetailContext = createContext<{ indexingTechnique?: string; }>({})
|
const DatasetDetailContext = createContext<{ indexingTechnique?: string; dataset?: DataSet }>({})
|
||||||
|
|
||||||
export default DatasetDetailContext
|
export default DatasetDetailContext
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { createContext } from 'use-context-selector'
|
import { createContext } from 'use-context-selector'
|
||||||
import { InstalledApp } from '@/models/explore'
|
import type { InstalledApp } from '@/models/explore'
|
||||||
|
|
||||||
type IExplore = {
|
type IExplore = {
|
||||||
controlUpdateInstalledApps: number
|
controlUpdateInstalledApps: number
|
||||||
|
|
|
@ -39,7 +39,7 @@ const translation = {
|
||||||
emoji: {
|
emoji: {
|
||||||
ok: 'OK',
|
ok: 'OK',
|
||||||
cancel: 'Cancel',
|
cancel: 'Cancel',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default translation
|
export default translation
|
||||||
|
|
|
@ -38,7 +38,7 @@ const translation = {
|
||||||
emoji: {
|
emoji: {
|
||||||
ok: '确认',
|
ok: '确认',
|
||||||
cancel: '取消',
|
cancel: '取消',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default translation
|
export default translation
|
||||||
|
|
|
@ -76,6 +76,8 @@ const translation = {
|
||||||
fileName: 'Preprocess document',
|
fileName: 'Preprocess document',
|
||||||
lastStep: 'Last step',
|
lastStep: 'Last step',
|
||||||
nextStep: 'Save & Process',
|
nextStep: 'Save & Process',
|
||||||
|
save: 'Save & Process',
|
||||||
|
cancel: 'Cancel',
|
||||||
sideTipTitle: 'Why segment and preprocess?',
|
sideTipTitle: 'Why segment and preprocess?',
|
||||||
sideTipP1: 'When processing text data, segmentation and cleaning are two important preprocessing steps.',
|
sideTipP1: 'When processing text data, segmentation and cleaning are two important preprocessing steps.',
|
||||||
sideTipP2: 'Segmentation splits long text into paragraphs so models can understand better. This improves the quality and relevance of model results.',
|
sideTipP2: 'Segmentation splits long text into paragraphs so models can understand better. This improves the quality and relevance of model results.',
|
||||||
|
|
|
@ -76,6 +76,8 @@ const translation = {
|
||||||
fileName: '预处理文档',
|
fileName: '预处理文档',
|
||||||
lastStep: '上一步',
|
lastStep: '上一步',
|
||||||
nextStep: '保存并处理',
|
nextStep: '保存并处理',
|
||||||
|
save: '保存并处理',
|
||||||
|
cancel: '取消',
|
||||||
sideTipTitle: '为什么要分段和预处理?',
|
sideTipTitle: '为什么要分段和预处理?',
|
||||||
sideTipP1: '在处理文本数据时,分段和清洗是两个重要的预处理步骤。',
|
sideTipP1: '在处理文本数据时,分段和清洗是两个重要的预处理步骤。',
|
||||||
sideTipP2: '分段的目的是将长文本拆分成较小的段落,以便模型更有效地处理和理解。这有助于提高模型生成的结果的质量和相关性。',
|
sideTipP2: '分段的目的是将长文本拆分成较小的段落,以便模型更有效地处理和理解。这有助于提高模型生成的结果的质量和相关性。',
|
||||||
|
|
|
@ -11,7 +11,7 @@ const translation = {
|
||||||
delete: {
|
delete: {
|
||||||
title: 'Delete app',
|
title: 'Delete app',
|
||||||
content: 'Are you sure you want to delete this app?',
|
content: 'Are you sure you want to delete this app?',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
apps: {
|
apps: {
|
||||||
title: 'Explore Apps by Dify',
|
title: 'Explore Apps by Dify',
|
||||||
|
@ -28,12 +28,12 @@ const translation = {
|
||||||
nameRequired: 'App name is required',
|
nameRequired: 'App name is required',
|
||||||
},
|
},
|
||||||
category: {
|
category: {
|
||||||
'Assistant': 'Assistant',
|
Assistant: 'Assistant',
|
||||||
'Writing': 'Writing',
|
Writing: 'Writing',
|
||||||
'Translate': 'Translate',
|
Translate: 'Translate',
|
||||||
'Programming': 'Programming',
|
Programming: 'Programming',
|
||||||
'HR': 'HR',
|
HR: 'HR',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default translation
|
export default translation
|
||||||
|
|
|
@ -11,7 +11,7 @@ const translation = {
|
||||||
delete: {
|
delete: {
|
||||||
title: '删除程序',
|
title: '删除程序',
|
||||||
content: '您确定要删除此程序吗?',
|
content: '您确定要删除此程序吗?',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
apps: {
|
apps: {
|
||||||
title: '探索 Dify 的应用',
|
title: '探索 Dify 的应用',
|
||||||
|
@ -28,12 +28,12 @@ const translation = {
|
||||||
nameRequired: '应用程序名称不能为空',
|
nameRequired: '应用程序名称不能为空',
|
||||||
},
|
},
|
||||||
category: {
|
category: {
|
||||||
'Assistant': '助手',
|
Assistant: '助手',
|
||||||
'Writing': '写作',
|
Writing: '写作',
|
||||||
'Translate': '翻译',
|
Translate: '翻译',
|
||||||
'Programming': '编程',
|
Programming: '编程',
|
||||||
'HR': '人力资源',
|
HR: '人力资源',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default translation
|
export default translation
|
||||||
|
|
|
@ -56,7 +56,7 @@ export type Member = Pick<UserProfileResponse, 'id' | 'name' | 'email' | 'last_l
|
||||||
|
|
||||||
export enum ProviderName {
|
export enum ProviderName {
|
||||||
OPENAI = 'openai',
|
OPENAI = 'openai',
|
||||||
AZURE_OPENAI = 'azure_openai'
|
AZURE_OPENAI = 'azure_openai',
|
||||||
}
|
}
|
||||||
export type ProviderAzureToken = {
|
export type ProviderAzureToken = {
|
||||||
openai_api_base?: string
|
openai_api_base?: string
|
||||||
|
@ -91,7 +91,7 @@ export type AccountIntegrate = {
|
||||||
link: string
|
link: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IWorkspace {
|
export type IWorkspace = {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
plan: string
|
plan: string
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
import { AppMode } from "./app";
|
import type { AppMode } from './app'
|
||||||
|
|
||||||
export type AppBasicInfo = {
|
export type AppBasicInfo = {
|
||||||
id: string;
|
id: string
|
||||||
name: string;
|
name: string
|
||||||
mode: AppMode;
|
mode: AppMode
|
||||||
icon: string;
|
icon: string
|
||||||
icon_background: string;
|
icon_background: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type App = {
|
export type App = {
|
||||||
app: AppBasicInfo;
|
app: AppBasicInfo
|
||||||
app_id: string;
|
app_id: string
|
||||||
description: string;
|
description: string
|
||||||
copyright: string;
|
copyright: string
|
||||||
privacy_policy: string;
|
privacy_policy: string
|
||||||
category: string;
|
category: string
|
||||||
position: number;
|
position: number
|
||||||
is_listed: boolean;
|
is_listed: boolean
|
||||||
install_count: number;
|
install_count: number
|
||||||
installed: boolean;
|
installed: boolean
|
||||||
editable: boolean;
|
editable: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type InstalledApp = {
|
export type InstalledApp = {
|
||||||
app: AppBasicInfo;
|
app: AppBasicInfo
|
||||||
id: string;
|
id: string
|
||||||
uninstallable: boolean
|
uninstallable: boolean
|
||||||
is_pinned: boolean
|
is_pinned: boolean
|
||||||
}
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
import { get, post, del, patch } from './base'
|
import { del, get, patch, post } from './base'
|
||||||
|
|
||||||
export const fetchAppList = () => {
|
export const fetchAppList = () => {
|
||||||
return get('/explore/apps')
|
return get('/explore/apps')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchAppDetail = (id: string) : Promise<any> => {
|
export const fetchAppDetail = (id: string): Promise<any> => {
|
||||||
return get(`/explore/apps/${id}`)
|
return get(`/explore/apps/${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,8 +15,8 @@ export const fetchInstalledAppList = () => {
|
||||||
export const installApp = (id: string) => {
|
export const installApp = (id: string) => {
|
||||||
return post('/installed-apps', {
|
return post('/installed-apps', {
|
||||||
body: {
|
body: {
|
||||||
app_id: id
|
app_id: id,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ export const uninstallApp = (id: string) => {
|
||||||
export const updatePinStatus = (id: string, isPinned: boolean) => {
|
export const updatePinStatus = (id: string, isPinned: boolean) => {
|
||||||
return patch(`/installed-apps/${id}`, {
|
return patch(`/installed-apps/${id}`, {
|
||||||
body: {
|
body: {
|
||||||
is_pinned: isPinned
|
is_pinned: isPinned,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import type { IOnCompleted, IOnData, IOnError } from './base'
|
import type { IOnCompleted, IOnData, IOnError } from './base'
|
||||||
import {
|
import {
|
||||||
get as consoleGet, post as consolePost, del as consoleDel,
|
del as consoleDel, get as consoleGet, post as consolePost,
|
||||||
getPublic as get, postPublic as post, ssePost, delPublic as del
|
delPublic as del, getPublic as get, postPublic as post, ssePost,
|
||||||
} from './base'
|
} from './base'
|
||||||
import type { Feedbacktype } from '@/app/components/app/chat'
|
import type { Feedbacktype } from '@/app/components/app/chat'
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ function getUrl(url: string, isInstalledApp: boolean, installedAppId: string) {
|
||||||
export const sendChatMessage = async (body: Record<string, any>, { onData, onCompleted, onError, getAbortController }: {
|
export const sendChatMessage = async (body: Record<string, any>, { onData, onCompleted, onError, getAbortController }: {
|
||||||
onData: IOnData
|
onData: IOnData
|
||||||
onCompleted: IOnCompleted
|
onCompleted: IOnCompleted
|
||||||
onError: IOnError,
|
onError: IOnError
|
||||||
getAbortController?: (abortController: AbortController) => void
|
getAbortController?: (abortController: AbortController) => void
|
||||||
}, isInstalledApp: boolean, installedAppId = '') => {
|
}, isInstalledApp: boolean, installedAppId = '') => {
|
||||||
return ssePost(getUrl('chat-messages', isInstalledApp, installedAppId), {
|
return ssePost(getUrl('chat-messages', isInstalledApp, installedAppId), {
|
||||||
|
@ -51,11 +51,11 @@ export const fetchAppInfo = async () => {
|
||||||
return get('/site')
|
return get('/site')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchConversations = async (isInstalledApp: boolean, installedAppId='', last_id?: string) => {
|
export const fetchConversations = async (isInstalledApp: boolean, installedAppId = '', last_id?: string) => {
|
||||||
return getAction('get', isInstalledApp)(getUrl('conversations', isInstalledApp, installedAppId), { params: {...{ limit: 20 }, ...(last_id ? { last_id } : {}) } })
|
return getAction('get', isInstalledApp)(getUrl('conversations', isInstalledApp, installedAppId), { params: { ...{ limit: 20 }, ...(last_id ? { last_id } : {}) } })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchChatList = async (conversationId: string, isInstalledApp: boolean, installedAppId='') => {
|
export const fetchChatList = async (conversationId: string, isInstalledApp: boolean, installedAppId = '') => {
|
||||||
return getAction('get', isInstalledApp)(getUrl('messages', isInstalledApp, installedAppId), { params: { conversation_id: conversationId, limit: 20, last_id: '' } })
|
return getAction('get', isInstalledApp)(getUrl('messages', isInstalledApp, installedAppId), { params: { conversation_id: conversationId, limit: 20, last_id: '' } })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ export const fetchMoreLikeThis = async (messageId: string, isInstalledApp: boole
|
||||||
return (getAction('get', isInstalledApp))(getUrl(`/messages/${messageId}/more-like-this`, isInstalledApp, installedAppId), {
|
return (getAction('get', isInstalledApp))(getUrl(`/messages/${messageId}/more-like-this`, isInstalledApp, installedAppId), {
|
||||||
params: {
|
params: {
|
||||||
response_mode: 'blocking',
|
response_mode: 'blocking',
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ export const saveMessage = (messageId: string, isInstalledApp: boolean, installe
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchSavedMessage = async (isInstalledApp: boolean, installedAppId = '') => {
|
export const fetchSavedMessage = async (isInstalledApp: boolean, installedAppId = '') => {
|
||||||
return (getAction('get', isInstalledApp))(getUrl(`/saved-messages`, isInstalledApp, installedAppId))
|
return (getAction('get', isInstalledApp))(getUrl('/saved-messages', isInstalledApp, installedAppId))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const removeMessage = (messageId: string, isInstalledApp: boolean, installedAppId = '') => {
|
export const removeMessage = (messageId: string, isInstalledApp: boolean, installedAppId = '') => {
|
||||||
|
|
|
@ -38,16 +38,16 @@ export type PromptVariable = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TextTypeFormItem = {
|
export type TextTypeFormItem = {
|
||||||
label: string,
|
label: string
|
||||||
variable: string,
|
variable: string
|
||||||
required: boolean
|
required: boolean
|
||||||
max_length: number
|
max_length: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SelectTypeFormItem = {
|
export type SelectTypeFormItem = {
|
||||||
label: string,
|
label: string
|
||||||
variable: string,
|
variable: string
|
||||||
required: boolean,
|
required: boolean
|
||||||
options: string[]
|
options: string[]
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -59,7 +59,6 @@ export type UserInputFormItem = {
|
||||||
'select': SelectTypeFormItem
|
'select': SelectTypeFormItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export type ToolItem = {
|
export type ToolItem = {
|
||||||
dataset: {
|
dataset: {
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
|
|
Loading…
Reference in New Issue
Block a user