mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-02-05 18:15:06 +08:00
Compare commits
9 Commits
fb0426419e
...
23062cb27a
| Author | SHA1 | Date | |
|---|---|---|---|
| 23062cb27a | |||
| 63c2f5b821 | |||
| 0a0bfc02a0 | |||
| f0c34d4454 | |||
| 7c719f8365 | |||
| 4fc9e42e74 | |||
| 35539092d0 | |||
| 581a54fbbb | |||
| 9ca86d801e |
@ -166,6 +166,17 @@ def create():
|
|||||||
if DocumentService.query(name=req["name"], kb_id=kb_id):
|
if DocumentService.query(name=req["name"], kb_id=kb_id):
|
||||||
return get_data_error_result(message="Duplicated document name in the same knowledgebase.")
|
return get_data_error_result(message="Duplicated document name in the same knowledgebase.")
|
||||||
|
|
||||||
|
kb_root_folder = FileService.get_kb_folder(kb.tenant_id)
|
||||||
|
if not kb_root_folder:
|
||||||
|
return get_data_error_result(message="Cannot find the root folder.")
|
||||||
|
kb_folder = FileService.new_a_file_from_kb(
|
||||||
|
kb.tenant_id,
|
||||||
|
kb.name,
|
||||||
|
kb_root_folder["id"],
|
||||||
|
)
|
||||||
|
if not kb_folder:
|
||||||
|
return get_data_error_result(message="Cannot find the kb folder for this file.")
|
||||||
|
|
||||||
doc = DocumentService.insert(
|
doc = DocumentService.insert(
|
||||||
{
|
{
|
||||||
"id": get_uuid(),
|
"id": get_uuid(),
|
||||||
@ -180,6 +191,9 @@ def create():
|
|||||||
"size": 0,
|
"size": 0,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
FileService.add_file_from_kb(doc.to_dict(), kb_folder["id"], kb.tenant_id)
|
||||||
|
|
||||||
return get_json_result(data=doc.to_json())
|
return get_json_result(data=doc.to_json())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return server_error_response(e)
|
return server_error_response(e)
|
||||||
|
|||||||
@ -81,7 +81,7 @@ def set_api_key():
|
|||||||
raise Exception(m)
|
raise Exception(m)
|
||||||
chat_passed = True
|
chat_passed = True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg += f"\nFail to access model({llm.llm_name}) using this api key." + str(
|
msg += f"\nFail to access model({llm.fid}/{llm.llm_name}) using this api key." + str(
|
||||||
e)
|
e)
|
||||||
elif not rerank_passed and llm.model_type == LLMType.RERANK:
|
elif not rerank_passed and llm.model_type == LLMType.RERANK:
|
||||||
assert factory in RerankModel, f"Re-rank model from {factory} is not supported yet."
|
assert factory in RerankModel, f"Re-rank model from {factory} is not supported yet."
|
||||||
@ -94,7 +94,7 @@ def set_api_key():
|
|||||||
rerank_passed = True
|
rerank_passed = True
|
||||||
logging.debug(f'passed model rerank {llm.llm_name}')
|
logging.debug(f'passed model rerank {llm.llm_name}')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg += f"\nFail to access model({llm.llm_name}) using this api key." + str(
|
msg += f"\nFail to access model({llm.fid}/{llm.llm_name}) using this api key." + str(
|
||||||
e)
|
e)
|
||||||
if any([embd_passed, chat_passed, rerank_passed]):
|
if any([embd_passed, chat_passed, rerank_passed]):
|
||||||
msg = ''
|
msg = ''
|
||||||
@ -229,7 +229,7 @@ def add_llm():
|
|||||||
if not tc and m.find("**ERROR**:") >= 0:
|
if not tc and m.find("**ERROR**:") >= 0:
|
||||||
raise Exception(m)
|
raise Exception(m)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg += f"\nFail to access model({mdl_nm})." + str(
|
msg += f"\nFail to access model({factory}/{mdl_nm})." + str(
|
||||||
e)
|
e)
|
||||||
elif llm["model_type"] == LLMType.RERANK:
|
elif llm["model_type"] == LLMType.RERANK:
|
||||||
assert factory in RerankModel, f"RE-rank model from {factory} is not supported yet."
|
assert factory in RerankModel, f"RE-rank model from {factory} is not supported yet."
|
||||||
@ -243,9 +243,9 @@ def add_llm():
|
|||||||
if len(arr) == 0:
|
if len(arr) == 0:
|
||||||
raise Exception("Not known.")
|
raise Exception("Not known.")
|
||||||
except KeyError:
|
except KeyError:
|
||||||
msg += f"{factory} dose not support this model({mdl_nm})"
|
msg += f"{factory} dose not support this model({factory}/{mdl_nm})"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg += f"\nFail to access model({mdl_nm})." + str(
|
msg += f"\nFail to access model({factory}/{mdl_nm})." + str(
|
||||||
e)
|
e)
|
||||||
elif llm["model_type"] == LLMType.IMAGE2TEXT.value:
|
elif llm["model_type"] == LLMType.IMAGE2TEXT.value:
|
||||||
assert factory in CvModel, f"Image to text model from {factory} is not supported yet."
|
assert factory in CvModel, f"Image to text model from {factory} is not supported yet."
|
||||||
@ -260,7 +260,7 @@ def add_llm():
|
|||||||
if not m and not tc:
|
if not m and not tc:
|
||||||
raise Exception(m)
|
raise Exception(m)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg += f"\nFail to access model({mdl_nm})." + str(e)
|
msg += f"\nFail to access model({factory}/{mdl_nm})." + str(e)
|
||||||
elif llm["model_type"] == LLMType.TTS:
|
elif llm["model_type"] == LLMType.TTS:
|
||||||
assert factory in TTSModel, f"TTS model from {factory} is not supported yet."
|
assert factory in TTSModel, f"TTS model from {factory} is not supported yet."
|
||||||
mdl = TTSModel[factory](
|
mdl = TTSModel[factory](
|
||||||
@ -270,7 +270,7 @@ def add_llm():
|
|||||||
for resp in mdl.tts("Hello~ Ragflower!"):
|
for resp in mdl.tts("Hello~ Ragflower!"):
|
||||||
pass
|
pass
|
||||||
except RuntimeError as e:
|
except RuntimeError as e:
|
||||||
msg += f"\nFail to access model({mdl_nm})." + str(e)
|
msg += f"\nFail to access model({factory}/{mdl_nm})." + str(e)
|
||||||
else:
|
else:
|
||||||
# TODO: check other type of models
|
# TODO: check other type of models
|
||||||
pass
|
pass
|
||||||
@ -358,8 +358,6 @@ def my_llms():
|
|||||||
return server_error_response(e)
|
return server_error_response(e)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@manager.route('/list', methods=['GET']) # noqa: F821
|
@manager.route('/list', methods=['GET']) # noqa: F821
|
||||||
@login_required
|
@login_required
|
||||||
def list_app():
|
def list_app():
|
||||||
|
|||||||
@ -62,6 +62,8 @@ MYSQL_DBNAME=rag_flow
|
|||||||
# The port used to expose the MySQL service to the host machine,
|
# The port used to expose the MySQL service to the host machine,
|
||||||
# allowing EXTERNAL access to the MySQL database running inside the Docker container.
|
# allowing EXTERNAL access to the MySQL database running inside the Docker container.
|
||||||
MYSQL_PORT=5455
|
MYSQL_PORT=5455
|
||||||
|
# The maximum size of communication packets sent to the MySQL server
|
||||||
|
MYSQL_MAX_PACKET=1073741824
|
||||||
|
|
||||||
# The hostname where the MinIO service is exposed
|
# The hostname where the MinIO service is exposed
|
||||||
MINIO_HOST=minio
|
MINIO_HOST=minio
|
||||||
|
|||||||
@ -9,6 +9,7 @@ mysql:
|
|||||||
port: 3306
|
port: 3306
|
||||||
max_connections: 900
|
max_connections: 900
|
||||||
stale_timeout: 300
|
stale_timeout: 300
|
||||||
|
max_allowed_packet: ${MYSQL_MAX_PACKET:-1073741824}
|
||||||
minio:
|
minio:
|
||||||
user: '${MINIO_USER:-rag_flow}'
|
user: '${MINIO_USER:-rag_flow}'
|
||||||
password: '${MINIO_PASSWORD:-infini_rag_flow}'
|
password: '${MINIO_PASSWORD:-infini_rag_flow}'
|
||||||
|
|||||||
@ -1216,11 +1216,11 @@ class LmStudioChat(Base):
|
|||||||
class OpenAI_APIChat(Base):
|
class OpenAI_APIChat(Base):
|
||||||
_FACTORY_NAME = ["VLLM", "OpenAI-API-Compatible"]
|
_FACTORY_NAME = ["VLLM", "OpenAI-API-Compatible"]
|
||||||
|
|
||||||
def __init__(self, key, model_name, base_url):
|
def __init__(self, key, model_name, base_url, **kwargs):
|
||||||
if not base_url:
|
if not base_url:
|
||||||
raise ValueError("url cannot be None")
|
raise ValueError("url cannot be None")
|
||||||
model_name = model_name.split("___")[0]
|
model_name = model_name.split("___")[0]
|
||||||
super().__init__(key, model_name, base_url)
|
super().__init__(key, model_name, base_url, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class PPIOChat(Base):
|
class PPIOChat(Base):
|
||||||
|
|||||||
@ -37,7 +37,12 @@ from rag.utils import num_tokens_from_string, truncate
|
|||||||
|
|
||||||
|
|
||||||
class Base(ABC):
|
class Base(ABC):
|
||||||
def __init__(self, key, model_name):
|
def __init__(self, key, model_name, **kwargs):
|
||||||
|
"""
|
||||||
|
Constructor for abstract base class.
|
||||||
|
Parameters are accepted for interface consistency but are not stored.
|
||||||
|
Subclasses should implement their own initialization as needed.
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def encode(self, texts: list):
|
def encode(self, texts: list):
|
||||||
@ -864,7 +869,7 @@ class VoyageEmbed(Base):
|
|||||||
class HuggingFaceEmbed(Base):
|
class HuggingFaceEmbed(Base):
|
||||||
_FACTORY_NAME = "HuggingFace"
|
_FACTORY_NAME = "HuggingFace"
|
||||||
|
|
||||||
def __init__(self, key, model_name, base_url=None):
|
def __init__(self, key, model_name, base_url=None, **kwargs):
|
||||||
if not model_name:
|
if not model_name:
|
||||||
raise ValueError("Model name cannot be None")
|
raise ValueError("Model name cannot be None")
|
||||||
self.key = key
|
self.key = key
|
||||||
@ -946,4 +951,4 @@ class Ai302Embed(Base):
|
|||||||
def __init__(self, key, model_name, base_url="https://api.302.ai/v1/embeddings"):
|
def __init__(self, key, model_name, base_url="https://api.302.ai/v1/embeddings"):
|
||||||
if not base_url:
|
if not base_url:
|
||||||
base_url = "https://api.302.ai/v1/embeddings"
|
base_url = "https://api.302.ai/v1/embeddings"
|
||||||
super().__init__(key, model_name, base_url)
|
super().__init__(key, model_name, base_url)
|
||||||
@ -33,7 +33,11 @@ from api.utils.log_utils import log_exception
|
|||||||
from rag.utils import num_tokens_from_string, truncate
|
from rag.utils import num_tokens_from_string, truncate
|
||||||
|
|
||||||
class Base(ABC):
|
class Base(ABC):
|
||||||
def __init__(self, key, model_name):
|
def __init__(self, key, model_name, **kwargs):
|
||||||
|
"""
|
||||||
|
Abstract base class constructor.
|
||||||
|
Parameters are not stored; initialization is left to subclasses.
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def similarity(self, query: str, texts: list):
|
def similarity(self, query: str, texts: list):
|
||||||
@ -315,7 +319,7 @@ class NvidiaRerank(Base):
|
|||||||
class LmStudioRerank(Base):
|
class LmStudioRerank(Base):
|
||||||
_FACTORY_NAME = "LM-Studio"
|
_FACTORY_NAME = "LM-Studio"
|
||||||
|
|
||||||
def __init__(self, key, model_name, base_url):
|
def __init__(self, key, model_name, base_url, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def similarity(self, query: str, texts: list):
|
def similarity(self, query: str, texts: list):
|
||||||
@ -396,7 +400,7 @@ class CoHereRerank(Base):
|
|||||||
class TogetherAIRerank(Base):
|
class TogetherAIRerank(Base):
|
||||||
_FACTORY_NAME = "TogetherAI"
|
_FACTORY_NAME = "TogetherAI"
|
||||||
|
|
||||||
def __init__(self, key, model_name, base_url):
|
def __init__(self, key, model_name, base_url, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def similarity(self, query: str, texts: list):
|
def similarity(self, query: str, texts: list):
|
||||||
|
|||||||
@ -28,7 +28,11 @@ from rag.utils import num_tokens_from_string
|
|||||||
|
|
||||||
|
|
||||||
class Base(ABC):
|
class Base(ABC):
|
||||||
def __init__(self, key, model_name):
|
def __init__(self, key, model_name, **kwargs):
|
||||||
|
"""
|
||||||
|
Abstract base class constructor.
|
||||||
|
Parameters are not stored; initialization is left to subclasses.
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def transcription(self, audio, **kwargs):
|
def transcription(self, audio, **kwargs):
|
||||||
|
|||||||
@ -63,7 +63,11 @@ class ServeTTSRequest(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class Base(ABC):
|
class Base(ABC):
|
||||||
def __init__(self, key, model_name, base_url):
|
def __init__(self, key, model_name, base_url, **kwargs):
|
||||||
|
"""
|
||||||
|
Abstract base class constructor.
|
||||||
|
Parameters are not stored; subclasses should handle their own initialization.
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def tts(self, audio):
|
def tts(self, audio):
|
||||||
|
|||||||
@ -611,6 +611,10 @@ def naive_merge_with_images(texts, images, chunk_token_num=128, delimiter="\n。
|
|||||||
if re.match(f"^{dels}$", sub_sec):
|
if re.match(f"^{dels}$", sub_sec):
|
||||||
continue
|
continue
|
||||||
add_chunk(sub_sec, image)
|
add_chunk(sub_sec, image)
|
||||||
|
|
||||||
|
for img in images:
|
||||||
|
if isinstance(img, Image.Image):
|
||||||
|
img.close()
|
||||||
|
|
||||||
return cks, result_images
|
return cks, result_images
|
||||||
|
|
||||||
|
|||||||
@ -231,7 +231,7 @@ async def get_storage_binary(bucket, name):
|
|||||||
return await trio.to_thread.run_sync(lambda: STORAGE_IMPL.get(bucket, name))
|
return await trio.to_thread.run_sync(lambda: STORAGE_IMPL.get(bucket, name))
|
||||||
|
|
||||||
|
|
||||||
@timeout(60*40, 1)
|
@timeout(60*80, 1)
|
||||||
async def build_chunks(task, progress_callback):
|
async def build_chunks(task, progress_callback):
|
||||||
if task["size"] > DOC_MAXIMUM_SIZE:
|
if task["size"] > DOC_MAXIMUM_SIZE:
|
||||||
set_progress(task["id"], prog=-1, msg="File size exceeds( <= %dMb )" %
|
set_progress(task["id"], prog=-1, msg="File size exceeds( <= %dMb )" %
|
||||||
|
|||||||
@ -23,7 +23,7 @@ SET GLOBAL max_allowed_packet={}
|
|||||||
def get_opendal_config():
|
def get_opendal_config():
|
||||||
try:
|
try:
|
||||||
opendal_config = get_base_config('opendal', {})
|
opendal_config = get_base_config('opendal', {})
|
||||||
if opendal_config.get("scheme") == 'mysql':
|
if opendal_config.get("scheme", "mysql") == 'mysql':
|
||||||
mysql_config = get_base_config('mysql', {})
|
mysql_config = get_base_config('mysql', {})
|
||||||
max_packet = mysql_config.get("max_allowed_packet", 134217728)
|
max_packet = mysql_config.get("max_allowed_packet", 134217728)
|
||||||
kwargs = {
|
kwargs = {
|
||||||
@ -33,7 +33,7 @@ def get_opendal_config():
|
|||||||
"user": mysql_config.get("user", "root"),
|
"user": mysql_config.get("user", "root"),
|
||||||
"password": mysql_config.get("password", ""),
|
"password": mysql_config.get("password", ""),
|
||||||
"database": mysql_config.get("name", "test_open_dal"),
|
"database": mysql_config.get("name", "test_open_dal"),
|
||||||
"table": opendal_config.get("config").get("oss_table", "opendal_storage"),
|
"table": opendal_config.get("config", {}).get("oss_table", "opendal_storage"),
|
||||||
"max_allowed_packet": str(max_packet)
|
"max_allowed_packet": str(max_packet)
|
||||||
}
|
}
|
||||||
kwargs["connection_string"] = f"mysql://{kwargs['user']}:{quote_plus(kwargs['password'])}@{kwargs['host']}:{kwargs['port']}/{kwargs['database']}?max_allowed_packet={max_packet}"
|
kwargs["connection_string"] = f"mysql://{kwargs['user']}:{quote_plus(kwargs['password'])}@{kwargs['host']}:{kwargs['port']}/{kwargs['database']}?max_allowed_packet={max_packet}"
|
||||||
|
|||||||
@ -30,6 +30,7 @@ class RAGFlowS3:
|
|||||||
self.s3_config = settings.S3
|
self.s3_config = settings.S3
|
||||||
self.access_key = self.s3_config.get('access_key', None)
|
self.access_key = self.s3_config.get('access_key', None)
|
||||||
self.secret_key = self.s3_config.get('secret_key', None)
|
self.secret_key = self.s3_config.get('secret_key', None)
|
||||||
|
self.session_token = self.s3_config.get('session_token', None)
|
||||||
self.region = self.s3_config.get('region', None)
|
self.region = self.s3_config.get('region', None)
|
||||||
self.endpoint_url = self.s3_config.get('endpoint_url', None)
|
self.endpoint_url = self.s3_config.get('endpoint_url', None)
|
||||||
self.signature_version = self.s3_config.get('signature_version', None)
|
self.signature_version = self.s3_config.get('signature_version', None)
|
||||||
@ -73,31 +74,32 @@ class RAGFlowS3:
|
|||||||
s3_params = {
|
s3_params = {
|
||||||
'aws_access_key_id': self.access_key,
|
'aws_access_key_id': self.access_key,
|
||||||
'aws_secret_access_key': self.secret_key,
|
'aws_secret_access_key': self.secret_key,
|
||||||
|
'aws_session_token': self.session_token,
|
||||||
}
|
}
|
||||||
if self.region in self.s3_config:
|
if self.region:
|
||||||
s3_params['region_name'] = self.region
|
s3_params['region_name'] = self.region
|
||||||
if 'endpoint_url' in self.s3_config:
|
if self.endpoint_url:
|
||||||
s3_params['endpoint_url'] = self.endpoint_url
|
s3_params['endpoint_url'] = self.endpoint_url
|
||||||
if 'signature_version' in self.s3_config:
|
if self.signature_version:
|
||||||
config_kwargs['signature_version'] = self.signature_version
|
s3_params['signature_version'] = self.signature_version
|
||||||
if 'addressing_style' in self.s3_config:
|
if self.addressing_style:
|
||||||
config_kwargs['addressing_style'] = self.addressing_style
|
s3_params['addressing_style'] = self.addressing_style
|
||||||
if config_kwargs:
|
if config_kwargs:
|
||||||
s3_params['config'] = Config(**config_kwargs)
|
s3_params['config'] = Config(**config_kwargs)
|
||||||
|
|
||||||
self.conn = boto3.client('s3', **s3_params)
|
self.conn = [boto3.client('s3', **s3_params)]
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.exception(f"Fail to connect at region {self.region} or endpoint {self.endpoint_url}")
|
logging.exception(f"Fail to connect at region {self.region} or endpoint {self.endpoint_url}")
|
||||||
|
|
||||||
def __close__(self):
|
def __close__(self):
|
||||||
del self.conn
|
del self.conn[0]
|
||||||
self.conn = None
|
self.conn = None
|
||||||
|
|
||||||
@use_default_bucket
|
@use_default_bucket
|
||||||
def bucket_exists(self, bucket):
|
def bucket_exists(self, bucket, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
logging.debug(f"head_bucket bucketname {bucket}")
|
logging.debug(f"head_bucket bucketname {bucket}")
|
||||||
self.conn.head_bucket(Bucket=bucket)
|
self.conn[0].head_bucket(Bucket=bucket)
|
||||||
exists = True
|
exists = True
|
||||||
except ClientError:
|
except ClientError:
|
||||||
logging.exception(f"head_bucket error {bucket}")
|
logging.exception(f"head_bucket error {bucket}")
|
||||||
@ -109,10 +111,10 @@ class RAGFlowS3:
|
|||||||
fnm = "txtxtxtxt1"
|
fnm = "txtxtxtxt1"
|
||||||
fnm, binary = f"{self.prefix_path}/{fnm}" if self.prefix_path else fnm, b"_t@@@1"
|
fnm, binary = f"{self.prefix_path}/{fnm}" if self.prefix_path else fnm, b"_t@@@1"
|
||||||
if not self.bucket_exists(bucket):
|
if not self.bucket_exists(bucket):
|
||||||
self.conn.create_bucket(Bucket=bucket)
|
self.conn[0].create_bucket(Bucket=bucket)
|
||||||
logging.debug(f"create bucket {bucket} ********")
|
logging.debug(f"create bucket {bucket} ********")
|
||||||
|
|
||||||
r = self.conn.upload_fileobj(BytesIO(binary), bucket, fnm)
|
r = self.conn[0].upload_fileobj(BytesIO(binary), bucket, fnm)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def get_properties(self, bucket, key):
|
def get_properties(self, bucket, key):
|
||||||
@ -123,14 +125,14 @@ class RAGFlowS3:
|
|||||||
|
|
||||||
@use_prefix_path
|
@use_prefix_path
|
||||||
@use_default_bucket
|
@use_default_bucket
|
||||||
def put(self, bucket, fnm, binary, **kwargs):
|
def put(self, bucket, fnm, binary, *args, **kwargs):
|
||||||
logging.debug(f"bucket name {bucket}; filename :{fnm}:")
|
logging.debug(f"bucket name {bucket}; filename :{fnm}:")
|
||||||
for _ in range(1):
|
for _ in range(1):
|
||||||
try:
|
try:
|
||||||
if not self.bucket_exists(bucket):
|
if not self.bucket_exists(bucket):
|
||||||
self.conn.create_bucket(Bucket=bucket)
|
self.conn[0].create_bucket(Bucket=bucket)
|
||||||
logging.info(f"create bucket {bucket} ********")
|
logging.info(f"create bucket {bucket} ********")
|
||||||
r = self.conn.upload_fileobj(BytesIO(binary), bucket, fnm)
|
r = self.conn[0].upload_fileobj(BytesIO(binary), bucket, fnm)
|
||||||
|
|
||||||
return r
|
return r
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -140,18 +142,18 @@ class RAGFlowS3:
|
|||||||
|
|
||||||
@use_prefix_path
|
@use_prefix_path
|
||||||
@use_default_bucket
|
@use_default_bucket
|
||||||
def rm(self, bucket, fnm, **kwargs):
|
def rm(self, bucket, fnm, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
self.conn.delete_object(Bucket=bucket, Key=fnm)
|
self.conn[0].delete_object(Bucket=bucket, Key=fnm)
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.exception(f"Fail rm {bucket}/{fnm}")
|
logging.exception(f"Fail rm {bucket}/{fnm}")
|
||||||
|
|
||||||
@use_prefix_path
|
@use_prefix_path
|
||||||
@use_default_bucket
|
@use_default_bucket
|
||||||
def get(self, bucket, fnm, **kwargs):
|
def get(self, bucket, fnm, *args, **kwargs):
|
||||||
for _ in range(1):
|
for _ in range(1):
|
||||||
try:
|
try:
|
||||||
r = self.conn.get_object(Bucket=bucket, Key=fnm)
|
r = self.conn[0].get_object(Bucket=bucket, Key=fnm)
|
||||||
object_data = r['Body'].read()
|
object_data = r['Body'].read()
|
||||||
return object_data
|
return object_data
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -162,9 +164,9 @@ class RAGFlowS3:
|
|||||||
|
|
||||||
@use_prefix_path
|
@use_prefix_path
|
||||||
@use_default_bucket
|
@use_default_bucket
|
||||||
def obj_exist(self, bucket, fnm, **kwargs):
|
def obj_exist(self, bucket, fnm, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
if self.conn.head_object(Bucket=bucket, Key=fnm):
|
if self.conn[0].head_object(Bucket=bucket, Key=fnm):
|
||||||
return True
|
return True
|
||||||
except ClientError as e:
|
except ClientError as e:
|
||||||
if e.response['Error']['Code'] == '404':
|
if e.response['Error']['Code'] == '404':
|
||||||
@ -174,10 +176,10 @@ class RAGFlowS3:
|
|||||||
|
|
||||||
@use_prefix_path
|
@use_prefix_path
|
||||||
@use_default_bucket
|
@use_default_bucket
|
||||||
def get_presigned_url(self, bucket, fnm, expires, **kwargs):
|
def get_presigned_url(self, bucket, fnm, expires, *args, **kwargs):
|
||||||
for _ in range(10):
|
for _ in range(10):
|
||||||
try:
|
try:
|
||||||
r = self.conn.generate_presigned_url('get_object',
|
r = self.conn[0].generate_presigned_url('get_object',
|
||||||
Params={'Bucket': bucket,
|
Params={'Bucket': bucket,
|
||||||
'Key': fnm},
|
'Key': fnm},
|
||||||
ExpiresIn=expires)
|
ExpiresIn=expires)
|
||||||
|
|||||||
@ -58,7 +58,7 @@ const EditTag = ({ value = [], onChange }: EditTagsProps) => {
|
|||||||
<HoverCardTrigger>
|
<HoverCardTrigger>
|
||||||
<div
|
<div
|
||||||
key={tag}
|
key={tag}
|
||||||
className="w-fit flex items-center justify-center gap-2 border-dashed border px-1 rounded-sm bg-background-card"
|
className="w-fit flex items-center justify-center gap-2 border-dashed border px-1 rounded-sm bg-bg-card"
|
||||||
>
|
>
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
<div className="max-w-80 overflow-hidden text-ellipsis">
|
<div className="max-w-80 overflow-hidden text-ellipsis">
|
||||||
@ -90,7 +90,7 @@ const EditTag = ({ value = [], onChange }: EditTagsProps) => {
|
|||||||
<Input
|
<Input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
type="text"
|
type="text"
|
||||||
className="h-8 bg-background-card"
|
className="h-8 bg-bg-card"
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
onBlur={handleInputConfirm}
|
onBlur={handleInputConfirm}
|
||||||
@ -103,7 +103,7 @@ const EditTag = ({ value = [], onChange }: EditTagsProps) => {
|
|||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
variant="dashed"
|
variant="dashed"
|
||||||
className="w-fit flex items-center justify-center gap-2 bg-background-card"
|
className="w-fit flex items-center justify-center gap-2 bg-bg-card"
|
||||||
onClick={showInput}
|
onClick={showInput}
|
||||||
style={tagPlusStyle}
|
style={tagPlusStyle}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -226,7 +226,7 @@ function MessageItem({
|
|||||||
? styles.messageTextDark
|
? styles.messageTextDark
|
||||||
: styles.messageText]: isAssistant,
|
: styles.messageText]: isAssistant,
|
||||||
[styles.messageUserText]: !isAssistant,
|
[styles.messageUserText]: !isAssistant,
|
||||||
'bg-background-card': !isAssistant,
|
'bg-bg-card': !isAssistant,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{item.data ? (
|
{item.data ? (
|
||||||
|
|||||||
@ -11,7 +11,7 @@ const badgeVariants = cva(
|
|||||||
default:
|
default:
|
||||||
'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
|
'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
|
||||||
secondary:
|
secondary:
|
||||||
'border-transparent bg-background-card text-text-sub-title-invert hover:bg-secondary/80 rounded-md',
|
'border-transparent bg-bg-card text-text-sub-title-invert hover:bg-secondary/80 rounded-md',
|
||||||
destructive:
|
destructive:
|
||||||
'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
|
'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
|
||||||
outline: 'text-foreground',
|
outline: 'text-foreground',
|
||||||
|
|||||||
@ -15,8 +15,7 @@ const buttonVariants = cva(
|
|||||||
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
||||||
outline:
|
outline:
|
||||||
'border border-text-sub-title-invert bg-transparent hover:bg-accent hover:text-accent-foreground',
|
'border border-text-sub-title-invert bg-transparent hover:bg-accent hover:text-accent-foreground',
|
||||||
secondary:
|
secondary: 'bg-bg-card text-secondary-foreground hover:bg-secondary/80',
|
||||||
'bg-background-card text-secondary-foreground hover:bg-secondary/80',
|
|
||||||
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
||||||
link: 'text-primary underline-offset-4 hover:underline',
|
link: 'text-primary underline-offset-4 hover:underline',
|
||||||
tertiary:
|
tertiary:
|
||||||
@ -52,7 +51,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|||||||
return (
|
return (
|
||||||
<Comp
|
<Comp
|
||||||
className={cn(
|
className={cn(
|
||||||
'bg-background-card',
|
'bg-bg-card',
|
||||||
buttonVariants({ variant, size, className }),
|
buttonVariants({ variant, size, className }),
|
||||||
)}
|
)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
|||||||
@ -8,10 +8,7 @@ const Card = React.forwardRef<
|
|||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn('rounded-lg bg-bg-card shadow-sm', className)}
|
||||||
'rounded-lg bg-background-card text-card-foreground shadow-sm',
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|||||||
102
web/src/components/ui/modal/modal-manage.tsx
Normal file
102
web/src/components/ui/modal/modal-manage.tsx
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { ReactNode, useEffect, useState } from 'react';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
import { Modal, ModalProps } from './modal';
|
||||||
|
|
||||||
|
type PortalModalProps = Omit<ModalProps, 'open' | 'onOpenChange'> & {
|
||||||
|
visible: boolean;
|
||||||
|
onVisibleChange: (visible: boolean) => void;
|
||||||
|
container?: HTMLElement;
|
||||||
|
children: ReactNode;
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
const PortalModal = ({
|
||||||
|
visible,
|
||||||
|
onVisibleChange,
|
||||||
|
container,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: PortalModalProps) => {
|
||||||
|
const [mounted, setMounted] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMounted(true);
|
||||||
|
return () => setMounted(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!mounted || !visible) return null;
|
||||||
|
console.log('PortalModal:', visible);
|
||||||
|
return createPortal(
|
||||||
|
<Modal open={visible} onOpenChange={onVisibleChange} {...restProps}>
|
||||||
|
{children}
|
||||||
|
</Modal>,
|
||||||
|
container || document.body,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createPortalModal = () => {
|
||||||
|
let container = document.createElement('div');
|
||||||
|
document.body.appendChild(container);
|
||||||
|
|
||||||
|
let currentProps: any = {};
|
||||||
|
let isVisible = false;
|
||||||
|
let root: ReturnType<typeof createRoot> | null = null;
|
||||||
|
|
||||||
|
root = createRoot(container);
|
||||||
|
const destroy = () => {
|
||||||
|
if (root && container) {
|
||||||
|
root.unmount();
|
||||||
|
if (container.parentNode) {
|
||||||
|
container.parentNode.removeChild(container);
|
||||||
|
}
|
||||||
|
root = null;
|
||||||
|
}
|
||||||
|
isVisible = false;
|
||||||
|
currentProps = {};
|
||||||
|
};
|
||||||
|
const render = () => {
|
||||||
|
const { onVisibleChange, ...props } = currentProps;
|
||||||
|
const modalParam = {
|
||||||
|
visible: isVisible,
|
||||||
|
|
||||||
|
onVisibleChange: (visible: boolean) => {
|
||||||
|
isVisible = visible;
|
||||||
|
if (onVisibleChange) {
|
||||||
|
onVisibleChange(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!visible) {
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
...props,
|
||||||
|
};
|
||||||
|
root?.render(isVisible ? <PortalModal {...modalParam} /> : null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const show = (props: PortalModalProps) => {
|
||||||
|
if (!container) {
|
||||||
|
container = document.createElement('div');
|
||||||
|
document.body.appendChild(container);
|
||||||
|
}
|
||||||
|
if (!root) {
|
||||||
|
root = createRoot(container);
|
||||||
|
}
|
||||||
|
currentProps = { ...currentProps, ...props };
|
||||||
|
isVisible = true;
|
||||||
|
render();
|
||||||
|
};
|
||||||
|
|
||||||
|
const hide = () => {
|
||||||
|
isVisible = false;
|
||||||
|
render();
|
||||||
|
};
|
||||||
|
|
||||||
|
const update = (props = {}) => {
|
||||||
|
currentProps = { ...currentProps, ...props };
|
||||||
|
render();
|
||||||
|
};
|
||||||
|
|
||||||
|
return { show, hide, update, destroy };
|
||||||
|
};
|
||||||
@ -1,15 +1,19 @@
|
|||||||
// src/components/ui/modal.tsx
|
// src/components/ui/modal.tsx
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
||||||
import { Loader, X } from 'lucide-react';
|
import { Loader, X } from 'lucide-react';
|
||||||
import { FC, ReactNode, useCallback, useEffect, useMemo } from 'react';
|
import { FC, ReactNode, useCallback, useEffect, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { createPortalModal } from './modal-manage';
|
||||||
|
|
||||||
interface ModalProps {
|
export interface ModalProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onOpenChange?: (open: boolean) => void;
|
onOpenChange?: (open: boolean) => void;
|
||||||
title?: ReactNode;
|
title?: ReactNode;
|
||||||
|
titleClassName?: string;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
footer?: ReactNode;
|
footer?: ReactNode;
|
||||||
|
footerClassName?: string;
|
||||||
showfooter?: boolean;
|
showfooter?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
size?: 'small' | 'default' | 'large';
|
size?: 'small' | 'default' | 'large';
|
||||||
@ -24,13 +28,19 @@ interface ModalProps {
|
|||||||
onOk?: () => void;
|
onOk?: () => void;
|
||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
}
|
}
|
||||||
|
export interface ModalType extends FC<ModalProps> {
|
||||||
|
show: typeof modalIns.show;
|
||||||
|
hide: typeof modalIns.hide;
|
||||||
|
}
|
||||||
|
|
||||||
export const Modal: FC<ModalProps> = ({
|
const Modal: ModalType = ({
|
||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
title,
|
title,
|
||||||
|
titleClassName,
|
||||||
children,
|
children,
|
||||||
footer,
|
footer,
|
||||||
|
footerClassName,
|
||||||
showfooter = true,
|
showfooter = true,
|
||||||
className = '',
|
className = '',
|
||||||
size = 'default',
|
size = 'default',
|
||||||
@ -74,6 +84,7 @@ export const Modal: FC<ModalProps> = ({
|
|||||||
}, [onOpenChange, onOk]);
|
}, [onOpenChange, onOk]);
|
||||||
const handleChange = (open: boolean) => {
|
const handleChange = (open: boolean) => {
|
||||||
onOpenChange?.(open);
|
onOpenChange?.(open);
|
||||||
|
console.log('open', open, onOpenChange);
|
||||||
if (open) {
|
if (open) {
|
||||||
handleOk();
|
handleOk();
|
||||||
}
|
}
|
||||||
@ -113,7 +124,12 @@ export const Modal: FC<ModalProps> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-end border-t border-border px-6 py-4">
|
<div
|
||||||
|
className={cn(
|
||||||
|
'flex items-center justify-end px-6 py-4',
|
||||||
|
footerClassName,
|
||||||
|
)}
|
||||||
|
>
|
||||||
{footerTemp}
|
{footerTemp}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -126,6 +142,7 @@ export const Modal: FC<ModalProps> = ({
|
|||||||
handleCancel,
|
handleCancel,
|
||||||
handleOk,
|
handleOk,
|
||||||
showfooter,
|
showfooter,
|
||||||
|
footerClassName,
|
||||||
]);
|
]);
|
||||||
return (
|
return (
|
||||||
<DialogPrimitive.Root open={open} onOpenChange={handleChange}>
|
<DialogPrimitive.Root open={open} onOpenChange={handleChange}>
|
||||||
@ -139,11 +156,23 @@ export const Modal: FC<ModalProps> = ({
|
|||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
{/* title */}
|
{/* title */}
|
||||||
{title && (
|
{(title || closable) && (
|
||||||
<div className="flex items-center justify-between border-b border-border px-6 py-4">
|
<div
|
||||||
<DialogPrimitive.Title className="text-lg font-medium text-foreground">
|
className={cn(
|
||||||
{title}
|
'flex items-center px-6 py-4',
|
||||||
</DialogPrimitive.Title>
|
{
|
||||||
|
'justify-end': closable && !title,
|
||||||
|
'justify-between': closable && title,
|
||||||
|
'justify-start': !closable,
|
||||||
|
},
|
||||||
|
titleClassName,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{title && (
|
||||||
|
<DialogPrimitive.Title className="text-lg font-medium text-foreground">
|
||||||
|
{title}
|
||||||
|
</DialogPrimitive.Title>
|
||||||
|
)}
|
||||||
{closable && (
|
{closable && (
|
||||||
<DialogPrimitive.Close asChild>
|
<DialogPrimitive.Close asChild>
|
||||||
<button
|
<button
|
||||||
@ -156,13 +185,9 @@ export const Modal: FC<ModalProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* title */}
|
|
||||||
{!title && (
|
|
||||||
<DialogPrimitive.Title className="text-lg font-medium text-foreground"></DialogPrimitive.Title>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* content */}
|
{/* content */}
|
||||||
<div className="p-6 overflow-y-auto max-h-[80vh] focus-visible:!outline-none">
|
<div className="py-2 px-6 overflow-y-auto max-h-[80vh] focus-visible:!outline-none">
|
||||||
{destroyOnClose && !open ? null : children}
|
{destroyOnClose && !open ? null : children}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -175,43 +200,13 @@ export const Modal: FC<ModalProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// example usage
|
let modalIns = createPortalModal();
|
||||||
/*
|
Modal.show = modalIns
|
||||||
import { Modal } from '@/components/ui/modal';
|
? modalIns.show
|
||||||
|
: () => {
|
||||||
|
modalIns = createPortalModal();
|
||||||
|
return modalIns.show;
|
||||||
|
};
|
||||||
|
Modal.hide = modalIns.hide;
|
||||||
|
|
||||||
function Demo() {
|
export { Modal };
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<button onClick={() => setOpen(true)}>open modal</button>
|
|
||||||
|
|
||||||
<Modal
|
|
||||||
open={open}
|
|
||||||
onOpenChange={setOpen}
|
|
||||||
title="title"
|
|
||||||
footer={
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<button onClick={() => setOpen(false)} className="px-4 py-2 border rounded-md">
|
|
||||||
cancel
|
|
||||||
</button>
|
|
||||||
<button onClick={() => setOpen(false)} className="px-4 py-2 bg-primary text-white rounded-md">
|
|
||||||
ok
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="py-4">弹窗内容区域</div>
|
|
||||||
</Modal>
|
|
||||||
<Modal
|
|
||||||
title={'modal-title'}
|
|
||||||
onOk={handleOk}
|
|
||||||
confirmLoading={loading}
|
|
||||||
destroyOnClose
|
|
||||||
>
|
|
||||||
<div className="py-4">弹窗内容区域</div>
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
@ -8,7 +8,7 @@ const Table = React.forwardRef<
|
|||||||
>(({ className, rootClassName, ...props }, ref) => (
|
>(({ className, rootClassName, ...props }, ref) => (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'relative w-full overflow-auto rounded-2xl bg-background-card',
|
'relative w-full overflow-auto rounded-2xl bg-bg-card',
|
||||||
rootClassName,
|
rootClassName,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -1,9 +1,15 @@
|
|||||||
import message from '@/components/ui/message';
|
import message from '@/components/ui/message';
|
||||||
import { ChatSearchParams } from '@/constants/chat';
|
import { ChatSearchParams } from '@/constants/chat';
|
||||||
import { IDialog } from '@/interfaces/database/chat';
|
import { IConversation, IDialog } from '@/interfaces/database/chat';
|
||||||
|
import { IAskRequestBody } from '@/interfaces/request/chat';
|
||||||
|
import { IClientConversation } from '@/pages/next-chats/chat/interface';
|
||||||
|
import { useGetSharedChatSearchParams } from '@/pages/next-chats/hooks/use-send-shared-message';
|
||||||
|
import { isConversationIdExist } from '@/pages/next-chats/utils';
|
||||||
import chatService from '@/services/next-chat-service ';
|
import chatService from '@/services/next-chat-service ';
|
||||||
|
import { buildMessageListWithUuid, getConversationId } from '@/utils/chat';
|
||||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { useDebounce } from 'ahooks';
|
import { useDebounce } from 'ahooks';
|
||||||
|
import { has } from 'lodash';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useParams, useSearchParams } from 'umi';
|
import { useParams, useSearchParams } from 'umi';
|
||||||
@ -17,6 +23,13 @@ export const enum ChatApiAction {
|
|||||||
RemoveDialog = 'removeDialog',
|
RemoveDialog = 'removeDialog',
|
||||||
SetDialog = 'setDialog',
|
SetDialog = 'setDialog',
|
||||||
FetchDialog = 'fetchDialog',
|
FetchDialog = 'fetchDialog',
|
||||||
|
FetchConversationList = 'fetchConversationList',
|
||||||
|
FetchConversation = 'fetchConversation',
|
||||||
|
UpdateConversation = 'updateConversation',
|
||||||
|
RemoveConversation = 'removeConversation',
|
||||||
|
DeleteMessage = 'deleteMessage',
|
||||||
|
FetchMindMap = 'fetchMindMap',
|
||||||
|
FetchRelatedQuestions = 'fetchRelatedQuestions',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useGetChatSearchParams = () => {
|
export const useGetChatSearchParams = () => {
|
||||||
@ -74,11 +87,17 @@ export const useFetchDialogList = () => {
|
|||||||
gcTime: 0,
|
gcTime: 0,
|
||||||
refetchOnWindowFocus: false,
|
refetchOnWindowFocus: false,
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const { data } = await chatService.listDialog({
|
const { data } = await chatService.listDialog(
|
||||||
keywords: debouncedSearchString,
|
{
|
||||||
page_size: pagination.pageSize,
|
params: {
|
||||||
page: pagination.current,
|
keywords: debouncedSearchString,
|
||||||
});
|
page_size: pagination.pageSize,
|
||||||
|
page: pagination.current,
|
||||||
|
},
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
return data?.data ?? { dialogs: [], total: 0 };
|
return data?.data ?? { dialogs: [], total: 0 };
|
||||||
},
|
},
|
||||||
@ -180,3 +199,227 @@ export const useFetchDialog = () => {
|
|||||||
|
|
||||||
return { data, loading, refetch };
|
return { data, loading, refetch };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//#region Conversation
|
||||||
|
|
||||||
|
export const useClickConversationCard = () => {
|
||||||
|
const [currentQueryParameters, setSearchParams] = useSearchParams();
|
||||||
|
const newQueryParameters: URLSearchParams = useMemo(
|
||||||
|
() => new URLSearchParams(currentQueryParameters.toString()),
|
||||||
|
[currentQueryParameters],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleClickConversation = useCallback(
|
||||||
|
(conversationId: string, isNew: string) => {
|
||||||
|
newQueryParameters.set(ChatSearchParams.ConversationId, conversationId);
|
||||||
|
newQueryParameters.set(ChatSearchParams.isNew, isNew);
|
||||||
|
setSearchParams(newQueryParameters);
|
||||||
|
},
|
||||||
|
[setSearchParams, newQueryParameters],
|
||||||
|
);
|
||||||
|
|
||||||
|
return { handleClickConversation };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFetchConversationList = () => {
|
||||||
|
const { id } = useParams();
|
||||||
|
const { handleClickConversation } = useClickConversationCard();
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isFetching: loading,
|
||||||
|
refetch,
|
||||||
|
} = useQuery<IConversation[]>({
|
||||||
|
queryKey: [ChatApiAction.FetchConversationList, id],
|
||||||
|
initialData: [],
|
||||||
|
gcTime: 0,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
enabled: !!id,
|
||||||
|
queryFn: async () => {
|
||||||
|
const { data } = await chatService.listConversation(
|
||||||
|
{ params: { dialog_id: id } },
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
if (data.code === 0) {
|
||||||
|
if (data.data.length > 0) {
|
||||||
|
handleClickConversation(data.data[0].id, '');
|
||||||
|
} else {
|
||||||
|
handleClickConversation('', '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data?.data;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, refetch };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFetchConversation = () => {
|
||||||
|
const { isNew, conversationId } = useGetChatSearchParams();
|
||||||
|
const { sharedId } = useGetSharedChatSearchParams();
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isFetching: loading,
|
||||||
|
refetch,
|
||||||
|
} = useQuery<IClientConversation>({
|
||||||
|
queryKey: [ChatApiAction.FetchConversation, conversationId],
|
||||||
|
initialData: {} as IClientConversation,
|
||||||
|
// enabled: isConversationIdExist(conversationId),
|
||||||
|
gcTime: 0,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
queryFn: async () => {
|
||||||
|
if (
|
||||||
|
isNew !== 'true' &&
|
||||||
|
isConversationIdExist(sharedId || conversationId)
|
||||||
|
) {
|
||||||
|
const { data } = await chatService.getConversation(
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
conversationId: conversationId || sharedId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
const conversation = data?.data ?? {};
|
||||||
|
|
||||||
|
const messageList = buildMessageListWithUuid(conversation?.message);
|
||||||
|
|
||||||
|
return { ...conversation, message: messageList };
|
||||||
|
}
|
||||||
|
return { message: [] };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, refetch };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUpdateConversation = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isPending: loading,
|
||||||
|
mutateAsync,
|
||||||
|
} = useMutation({
|
||||||
|
mutationKey: [ChatApiAction.UpdateConversation],
|
||||||
|
mutationFn: async (params: Record<string, any>) => {
|
||||||
|
const { data } = await chatService.setConversation({
|
||||||
|
...params,
|
||||||
|
conversation_id: params.conversation_id
|
||||||
|
? params.conversation_id
|
||||||
|
: getConversationId(),
|
||||||
|
});
|
||||||
|
if (data.code === 0) {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: [ChatApiAction.FetchConversationList],
|
||||||
|
});
|
||||||
|
message.success(t(`message.modified`));
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, updateConversation: mutateAsync };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useRemoveConversation = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const { dialogId } = useGetChatSearchParams();
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isPending: loading,
|
||||||
|
mutateAsync,
|
||||||
|
} = useMutation({
|
||||||
|
mutationKey: [ChatApiAction.RemoveConversation],
|
||||||
|
mutationFn: async (conversationIds: string[]) => {
|
||||||
|
const { data } = await chatService.removeConversation({
|
||||||
|
conversationIds,
|
||||||
|
dialogId,
|
||||||
|
});
|
||||||
|
if (data.code === 0) {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: [ChatApiAction.FetchConversationList],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return data.code;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, removeConversation: mutateAsync };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useDeleteMessage = () => {
|
||||||
|
const { conversationId } = useGetChatSearchParams();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isPending: loading,
|
||||||
|
mutateAsync,
|
||||||
|
} = useMutation({
|
||||||
|
mutationKey: [ChatApiAction.DeleteMessage],
|
||||||
|
mutationFn: async (messageId: string) => {
|
||||||
|
const { data } = await chatService.deleteMessage({
|
||||||
|
messageId,
|
||||||
|
conversationId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.code === 0) {
|
||||||
|
message.success(t(`message.deleted`));
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.code;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, deleteMessage: mutateAsync };
|
||||||
|
};
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region search page
|
||||||
|
|
||||||
|
export const useFetchMindMap = () => {
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isPending: loading,
|
||||||
|
mutateAsync,
|
||||||
|
} = useMutation({
|
||||||
|
mutationKey: [ChatApiAction.FetchMindMap],
|
||||||
|
gcTime: 0,
|
||||||
|
mutationFn: async (params: IAskRequestBody) => {
|
||||||
|
try {
|
||||||
|
const ret = await chatService.getMindMap(params);
|
||||||
|
return ret?.data?.data ?? {};
|
||||||
|
} catch (error: any) {
|
||||||
|
if (has(error, 'message')) {
|
||||||
|
message.error(error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, fetchMindMap: mutateAsync };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFetchRelatedQuestions = () => {
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isPending: loading,
|
||||||
|
mutateAsync,
|
||||||
|
} = useMutation({
|
||||||
|
mutationKey: [ChatApiAction.FetchRelatedQuestions],
|
||||||
|
gcTime: 0,
|
||||||
|
mutationFn: async (question: string): Promise<string[]> => {
|
||||||
|
const { data } = await chatService.getRelatedQuestions({ question });
|
||||||
|
|
||||||
|
return data?.data ?? [];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, fetchRelatedQuestions: mutateAsync };
|
||||||
|
};
|
||||||
|
//#endregion
|
||||||
|
|||||||
@ -1028,7 +1028,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
|
|||||||
'30d': '30 days',
|
'30d': '30 days',
|
||||||
},
|
},
|
||||||
publish: 'API',
|
publish: 'API',
|
||||||
exeSQL: 'ExeSQL',
|
exeSQL: 'Execute SQL',
|
||||||
exeSQLDescription:
|
exeSQLDescription:
|
||||||
'A component that performs SQL queries on a relational database, supporting querying from MySQL, PostgreSQL, or MariaDB.',
|
'A component that performs SQL queries on a relational database, supporting querying from MySQL, PostgreSQL, or MariaDB.',
|
||||||
dbType: 'Database Type',
|
dbType: 'Database Type',
|
||||||
@ -1376,5 +1376,8 @@ This delimiter is used to split the input text into several text pieces echo of
|
|||||||
addMCP: 'Add MCP',
|
addMCP: 'Add MCP',
|
||||||
editMCP: 'Edit MCP',
|
editMCP: 'Edit MCP',
|
||||||
},
|
},
|
||||||
|
search: {
|
||||||
|
createSearch: 'Create Search',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1310,4 +1310,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
|||||||
okText: '确认',
|
okText: '确认',
|
||||||
cancelText: '取消',
|
cancelText: '取消',
|
||||||
},
|
},
|
||||||
|
search: {
|
||||||
|
createSearch: '新建查询',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -83,12 +83,12 @@ function InnerAgentNode({
|
|||||||
></Handle>
|
></Handle>
|
||||||
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
|
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
|
||||||
<section className="flex flex-col gap-2">
|
<section className="flex flex-col gap-2">
|
||||||
<div className={'bg-background-card rounded-sm p-1'}>
|
<div className={'bg-bg-card rounded-sm p-1'}>
|
||||||
<LLMLabel value={get(data, 'form.llm_id')}></LLMLabel>
|
<LLMLabel value={get(data, 'form.llm_id')}></LLMLabel>
|
||||||
</div>
|
</div>
|
||||||
{(isGotoMethod ||
|
{(isGotoMethod ||
|
||||||
exceptionMethod === AgentExceptionMethod.Comment) && (
|
exceptionMethod === AgentExceptionMethod.Comment) && (
|
||||||
<div className="bg-background-card rounded-sm p-1 flex justify-between gap-2">
|
<div className="bg-bg-card rounded-sm p-1 flex justify-between gap-2">
|
||||||
<span className="text-text-sub-title">On Failure</span>
|
<span className="text-text-sub-title">On Failure</span>
|
||||||
<span className="truncate flex-1 text-right">
|
<span className="truncate flex-1 text-right">
|
||||||
{t(`flow.${exceptionMethod}`)}
|
{t(`flow.${exceptionMethod}`)}
|
||||||
|
|||||||
@ -31,13 +31,13 @@ export function InnerCategorizeNode({
|
|||||||
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
|
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
|
||||||
|
|
||||||
<section className="flex flex-col gap-2">
|
<section className="flex flex-col gap-2">
|
||||||
<div className={'bg-background-card rounded-sm px-1'}>
|
<div className={'bg-bg-card rounded-sm px-1'}>
|
||||||
<LLMLabel value={get(data, 'form.llm_id')}></LLMLabel>
|
<LLMLabel value={get(data, 'form.llm_id')}></LLMLabel>
|
||||||
</div>
|
</div>
|
||||||
{positions.map((position) => {
|
{positions.map((position) => {
|
||||||
return (
|
return (
|
||||||
<div key={position.uuid}>
|
<div key={position.uuid}>
|
||||||
<div className={'bg-background-card rounded-sm p-1 truncate'}>
|
<div className={'bg-bg-card rounded-sm p-1 truncate'}>
|
||||||
{position.name}
|
{position.name}
|
||||||
</div>
|
</div>
|
||||||
<CommonHandle
|
<CommonHandle
|
||||||
|
|||||||
@ -42,7 +42,7 @@ function OperatorItemList({ operators }: OperatorItemProps) {
|
|||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
key={x}
|
key={x}
|
||||||
className="hover:bg-background-card py-1 px-3 cursor-pointer rounded-sm flex gap-2 items-center justify-start"
|
className="hover:bg-bg-card py-1 px-3 cursor-pointer rounded-sm flex gap-2 items-center justify-start"
|
||||||
onClick={addCanvasNode(x, {
|
onClick={addCanvasNode(x, {
|
||||||
nodeId,
|
nodeId,
|
||||||
id,
|
id,
|
||||||
|
|||||||
@ -145,7 +145,7 @@ function EmbedDialog({
|
|||||||
{t(isAgent ? 'flow' : 'chat', { keyPrefix: 'header' })}
|
{t(isAgent ? 'flow' : 'chat', { keyPrefix: 'header' })}
|
||||||
<span className="ml-1 inline-block">ID</span>
|
<span className="ml-1 inline-block">ID</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-background-card rounded-lg flex justify-between p-2">
|
<div className="bg-bg-card rounded-lg flex justify-between p-2">
|
||||||
<span>{token} </span>
|
<span>{token} </span>
|
||||||
<CopyToClipboard text={token}></CopyToClipboard>
|
<CopyToClipboard text={token}></CopyToClipboard>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -36,7 +36,7 @@ export function ToolCard({
|
|||||||
<li
|
<li
|
||||||
{...props}
|
{...props}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex bg-background-card p-1 rounded-sm justify-between',
|
'flex bg-bg-card p-1 rounded-sm justify-between',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -133,7 +133,7 @@ function ConditionCards({
|
|||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<section className="p-2 bg-background-card flex justify-between items-center">
|
<section className="p-2 bg-bg-card flex justify-between items-center">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name={`${name}.${index}.cpn_id`}
|
name={`${name}.${index}.cpn_id`}
|
||||||
|
|||||||
@ -135,7 +135,7 @@ const ToolTimelineItem = ({
|
|||||||
<Accordion
|
<Accordion
|
||||||
type="single"
|
type="single"
|
||||||
collapsible
|
collapsible
|
||||||
className="bg-background-card px-3"
|
className="bg-bg-card px-3"
|
||||||
>
|
>
|
||||||
<AccordionItem value={idx.toString()}>
|
<AccordionItem value={idx.toString()}>
|
||||||
<AccordionTrigger
|
<AccordionTrigger
|
||||||
|
|||||||
@ -254,7 +254,7 @@ export const WorkFlowTimeline = ({
|
|||||||
<Accordion
|
<Accordion
|
||||||
type="single"
|
type="single"
|
||||||
collapsible
|
collapsible
|
||||||
className="bg-background-card px-3"
|
className="bg-bg-card px-3"
|
||||||
>
|
>
|
||||||
<AccordionItem value={idx.toString()}>
|
<AccordionItem value={idx.toString()}>
|
||||||
<AccordionTrigger
|
<AccordionTrigger
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import MessageItem from '@/components/next-message-item';
|
import MessageItem from '@/components/next-message-item';
|
||||||
import { Modal } from '@/components/ui/modal';
|
import { Modal } from '@/components/ui/modal/modal';
|
||||||
import { useFetchAgent } from '@/hooks/use-agent-request';
|
import { useFetchAgent } from '@/hooks/use-agent-request';
|
||||||
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
||||||
import { IAgentLogMessage } from '@/interfaces/database/agent';
|
import { IAgentLogMessage } from '@/interfaces/database/agent';
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import {
|
|||||||
HoverCardContent,
|
HoverCardContent,
|
||||||
HoverCardTrigger,
|
HoverCardTrigger,
|
||||||
} from '@/components/ui/hover-card';
|
} from '@/components/ui/hover-card';
|
||||||
import { Modal } from '@/components/ui/modal';
|
import { Modal } from '@/components/ui/modal/modal';
|
||||||
import Space from '@/components/ui/space';
|
import Space from '@/components/ui/space';
|
||||||
import { Switch } from '@/components/ui/switch';
|
import { Switch } from '@/components/ui/switch';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
|
|||||||
@ -61,7 +61,7 @@ export default ({
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="flex pr-[25px]">
|
<div className="flex pr-[25px]">
|
||||||
<div className="flex items-center gap-4 bg-background-card text-muted w-fit h-[35px] rounded-md px-4 py-2 text-base">
|
<div className="flex items-center gap-4 bg-bg-card text-muted-foreground w-fit h-[35px] rounded-md px-4 py-2">
|
||||||
{textSelectOptions.map((option) => (
|
{textSelectOptions.map((option) => (
|
||||||
<div
|
<div
|
||||||
key={option.value}
|
key={option.value}
|
||||||
@ -76,7 +76,7 @@ export default ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="ml-auto"></div>
|
<div className="ml-auto"></div>
|
||||||
<Input
|
<Input
|
||||||
className="bg-background-card text-muted-foreground"
|
className="bg-bg-card text-muted-foreground"
|
||||||
style={{ width: 200 }}
|
style={{ width: 200 }}
|
||||||
placeholder={t('search')}
|
placeholder={t('search')}
|
||||||
icon={<SearchOutlined />}
|
icon={<SearchOutlined />}
|
||||||
@ -86,7 +86,7 @@ export default ({
|
|||||||
<div className="w-[20px]"></div>
|
<div className="w-[20px]"></div>
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<Button className="bg-background-card text-muted-foreground hover:bg-card">
|
<Button className="bg-bg-card text-muted-foreground hover:bg-card">
|
||||||
<ListFilter />
|
<ListFilter />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
@ -95,10 +95,7 @@ export default ({
|
|||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
<div className="w-[20px]"></div>
|
<div className="w-[20px]"></div>
|
||||||
<Button
|
<Button onClick={() => createChunk()} className="bg-bg-card text-primary">
|
||||||
onClick={() => createChunk()}
|
|
||||||
className="bg-background-card text-primary"
|
|
||||||
>
|
|
||||||
<Plus size={44} />
|
<Plus size={44} />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -98,13 +98,13 @@ export default function DatasetSettings() {
|
|||||||
setCurrentTab(val);
|
setCurrentTab(val);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TabsList className="grid bg-background grid-cols-2 rounded-none bg-[#161618]">
|
<TabsList className="grid bg-transparent grid-cols-2 rounded-none text-foreground">
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="generalForm"
|
value="generalForm"
|
||||||
className="group bg-transparent p-0 !border-transparent"
|
className="group bg-transparent p-0 !border-transparent"
|
||||||
>
|
>
|
||||||
<div className="flex w-full h-full justify-center items-center bg-[#161618]">
|
<div className="flex w-full h-full justify-center items-center">
|
||||||
<span className="h-full group-data-[state=active]:border-b-2 border-white ">
|
<span className="h-full group-data-[state=active]:border-b-2 border-foreground ">
|
||||||
General
|
General
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -113,8 +113,8 @@ export default function DatasetSettings() {
|
|||||||
value="chunkMethodForm"
|
value="chunkMethodForm"
|
||||||
className="group bg-transparent p-0 !border-transparent"
|
className="group bg-transparent p-0 !border-transparent"
|
||||||
>
|
>
|
||||||
<div className="flex w-full h-full justify-center items-center bg-[#161618]">
|
<div className="flex w-full h-full justify-center items-center">
|
||||||
<span className="h-full group-data-[state=active]:border-b-2 border-white ">
|
<span className="h-full group-data-[state=active]:border-b-2 border-foreground ">
|
||||||
Chunk Method
|
Chunk Method
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -84,7 +84,7 @@ export function SideBar({ refreshCount }: PropType) {
|
|||||||
className={cn(
|
className={cn(
|
||||||
'w-full justify-start gap-2.5 px-3 relative h-10 text-text-sub-title-invert',
|
'w-full justify-start gap-2.5 px-3 relative h-10 text-text-sub-title-invert',
|
||||||
{
|
{
|
||||||
'bg-background-card': active,
|
'bg-bg-card': active,
|
||||||
'text-text-title': active,
|
'text-text-title': active,
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -31,7 +31,7 @@ export function ChatCard({ data, showChatRenameModal }: IProps) {
|
|||||||
</section>
|
</section>
|
||||||
<div className="flex justify-between items-end">
|
<div className="flex justify-between items-end">
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<h3 className="text-lg font-semibold mb-2 line-clamp-1">
|
<h3 className="text-lg font-semibold mb-2 line-clamp-1 truncate">
|
||||||
{data.name}
|
{data.name}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xs text-text-sub-title">{data.description}</p>
|
<p className="text-xs text-text-sub-title">{data.description}</p>
|
||||||
|
|||||||
@ -0,0 +1,23 @@
|
|||||||
|
import {
|
||||||
|
Sheet,
|
||||||
|
SheetContent,
|
||||||
|
SheetHeader,
|
||||||
|
SheetTitle,
|
||||||
|
SheetTrigger,
|
||||||
|
} from '@/components/ui/sheet';
|
||||||
|
import { PropsWithChildren } from 'react';
|
||||||
|
import { ChatSettings } from './chat-settings';
|
||||||
|
|
||||||
|
export function ChatSettingSheet({ children }: PropsWithChildren) {
|
||||||
|
return (
|
||||||
|
<Sheet>
|
||||||
|
<SheetTrigger asChild>{children}</SheetTrigger>
|
||||||
|
<SheetContent>
|
||||||
|
<SheetHeader>
|
||||||
|
<SheetTitle>Chat Settings</SheetTitle>
|
||||||
|
</SheetHeader>
|
||||||
|
<ChatSettings></ChatSettings>
|
||||||
|
</SheetContent>
|
||||||
|
</Sheet>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -7,8 +7,9 @@ import { ChatModelSettings } from './chat-model-settings';
|
|||||||
import { ChatPromptEngine } from './chat-prompt-engine';
|
import { ChatPromptEngine } from './chat-prompt-engine';
|
||||||
import { useChatSettingSchema } from './use-chat-setting-schema';
|
import { useChatSettingSchema } from './use-chat-setting-schema';
|
||||||
|
|
||||||
export function AppSettings() {
|
export function ChatSettings() {
|
||||||
const formSchema = useChatSettingSchema();
|
const formSchema = useChatSettingSchema();
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof formSchema>>({
|
const form = useForm<z.infer<typeof formSchema>>({
|
||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
@ -32,27 +33,19 @@ export function AppSettings() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="py-6 w-[500px] max-w-[25%] ">
|
<section className="py-6">
|
||||||
<div className="text-2xl font-bold mb-4 text-colors-text-neutral-strong px-6">
|
<FormProvider {...form}>
|
||||||
App settings
|
<form
|
||||||
</div>
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
<div className="overflow-auto max-h-[81vh] px-6 ">
|
className="space-y-6 overflow-auto max-h-[88vh] pr-4"
|
||||||
<FormProvider {...form}>
|
>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
<ChatBasicSetting></ChatBasicSetting>
|
||||||
<ChatBasicSetting></ChatBasicSetting>
|
<ChatPromptEngine></ChatPromptEngine>
|
||||||
<ChatPromptEngine></ChatPromptEngine>
|
<ChatModelSettings></ChatModelSettings>
|
||||||
<ChatModelSettings></ChatModelSettings>
|
</form>
|
||||||
</form>
|
</FormProvider>
|
||||||
</FormProvider>
|
|
||||||
</div>
|
<Button className="w-full my-4">Update</Button>
|
||||||
<div className="p-6 text-center">
|
|
||||||
<p className="text-colors-text-neutral-weak mb-1">
|
|
||||||
There are unsaved changes
|
|
||||||
</p>
|
|
||||||
<Button variant={'tertiary'} className="w-full">
|
|
||||||
Update
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1,9 +1,95 @@
|
|||||||
import { ChatInput } from '@/components/chat-input';
|
import { NextMessageInput } from '@/components/message-input/next';
|
||||||
|
import MessageItem from '@/components/message-item';
|
||||||
|
import { MessageType } from '@/constants/chat';
|
||||||
|
import {
|
||||||
|
useFetchConversation,
|
||||||
|
useFetchDialog,
|
||||||
|
useGetChatSearchParams,
|
||||||
|
} from '@/hooks/use-chat-request';
|
||||||
|
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
||||||
|
import { buildMessageUuidWithRole } from '@/utils/chat';
|
||||||
|
import {
|
||||||
|
useGetSendButtonDisabled,
|
||||||
|
useSendButtonDisabled,
|
||||||
|
} from '../hooks/use-button-disabled';
|
||||||
|
import { useCreateConversationBeforeUploadDocument } from '../hooks/use-create-conversation';
|
||||||
|
import { useSendMessage } from '../hooks/use-send-chat-message';
|
||||||
|
import { buildMessageItemReference } from '../utils';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
controller: AbortController;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ChatBox({ controller }: IProps) {
|
||||||
|
const {
|
||||||
|
value,
|
||||||
|
scrollRef,
|
||||||
|
messageContainerRef,
|
||||||
|
sendLoading,
|
||||||
|
derivedMessages,
|
||||||
|
handleInputChange,
|
||||||
|
handlePressEnter,
|
||||||
|
regenerateMessage,
|
||||||
|
removeMessageById,
|
||||||
|
stopOutputMessage,
|
||||||
|
} = useSendMessage(controller);
|
||||||
|
const { data: userInfo } = useFetchUserInfo();
|
||||||
|
const { data: currentDialog } = useFetchDialog();
|
||||||
|
const { createConversationBeforeUploadDocument } =
|
||||||
|
useCreateConversationBeforeUploadDocument();
|
||||||
|
const { conversationId } = useGetChatSearchParams();
|
||||||
|
const { data: conversation } = useFetchConversation();
|
||||||
|
const disabled = useGetSendButtonDisabled();
|
||||||
|
const sendDisabled = useSendButtonDisabled(value);
|
||||||
|
|
||||||
export function ChatBox() {
|
|
||||||
return (
|
return (
|
||||||
<section className="border-x flex-1">
|
<section className="border-x flex flex-col p-5 w-full">
|
||||||
<ChatInput></ChatInput>
|
<div ref={messageContainerRef} className="flex-1 overflow-auto">
|
||||||
|
<div className="w-full">
|
||||||
|
{derivedMessages?.map((message, i) => {
|
||||||
|
return (
|
||||||
|
<MessageItem
|
||||||
|
loading={
|
||||||
|
message.role === MessageType.Assistant &&
|
||||||
|
sendLoading &&
|
||||||
|
derivedMessages.length - 1 === i
|
||||||
|
}
|
||||||
|
key={buildMessageUuidWithRole(message)}
|
||||||
|
item={message}
|
||||||
|
nickname={userInfo.nickname}
|
||||||
|
avatar={userInfo.avatar}
|
||||||
|
avatarDialog={currentDialog.icon}
|
||||||
|
reference={buildMessageItemReference(
|
||||||
|
{
|
||||||
|
message: derivedMessages,
|
||||||
|
reference: conversation.reference,
|
||||||
|
},
|
||||||
|
message,
|
||||||
|
)}
|
||||||
|
// clickDocumentButton={clickDocumentButton}
|
||||||
|
index={i}
|
||||||
|
removeMessageById={removeMessageById}
|
||||||
|
regenerateMessage={regenerateMessage}
|
||||||
|
sendLoading={sendLoading}
|
||||||
|
></MessageItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div ref={scrollRef} />
|
||||||
|
</div>
|
||||||
|
<NextMessageInput
|
||||||
|
disabled={disabled}
|
||||||
|
sendDisabled={sendDisabled}
|
||||||
|
sendLoading={sendLoading}
|
||||||
|
value={value}
|
||||||
|
onInputChange={handleInputChange}
|
||||||
|
onPressEnter={handlePressEnter}
|
||||||
|
conversationId={conversationId}
|
||||||
|
createConversationBeforeUploadDocument={
|
||||||
|
createConversationBeforeUploadDocument
|
||||||
|
}
|
||||||
|
stopOutputMessage={stopOutputMessage}
|
||||||
|
/>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,32 +1,48 @@
|
|||||||
import { PageHeader } from '@/components/page-header';
|
import { PageHeader } from '@/components/page-header';
|
||||||
import { Button } from '@/components/ui/button';
|
import {
|
||||||
|
Breadcrumb,
|
||||||
|
BreadcrumbItem,
|
||||||
|
BreadcrumbLink,
|
||||||
|
BreadcrumbList,
|
||||||
|
BreadcrumbPage,
|
||||||
|
BreadcrumbSeparator,
|
||||||
|
} from '@/components/ui/breadcrumb';
|
||||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||||
import { useFetchDialog } from '@/hooks/use-chat-request';
|
import { useFetchDialog } from '@/hooks/use-chat-request';
|
||||||
import { EllipsisVertical } from 'lucide-react';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { AppSettings } from './app-settings';
|
import { useHandleClickConversationCard } from '../hooks/use-click-card';
|
||||||
import { ChatBox } from './chat-box';
|
import { ChatBox } from './chat-box';
|
||||||
import { Sessions } from './sessions';
|
import { Sessions } from './sessions';
|
||||||
|
|
||||||
export default function Chat() {
|
export default function Chat() {
|
||||||
const { navigateToChatList } = useNavigatePage();
|
const { navigateToChatList } = useNavigatePage();
|
||||||
useFetchDialog();
|
const { data } = useFetchDialog();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { handleConversationCardClick, controller } =
|
||||||
|
useHandleClickConversationCard();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="h-full flex flex-col">
|
<section className="h-full flex flex-col">
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<div className="flex items-center gap-2">
|
<Breadcrumb>
|
||||||
<Button variant={'icon'} size={'icon'}>
|
<BreadcrumbList>
|
||||||
<EllipsisVertical />
|
<BreadcrumbItem>
|
||||||
</Button>
|
<BreadcrumbLink onClick={navigateToChatList}>
|
||||||
<Button variant={'tertiary'} size={'sm'}>
|
{t('chat.chat')}
|
||||||
Publish
|
</BreadcrumbLink>
|
||||||
</Button>
|
</BreadcrumbItem>
|
||||||
</div>
|
<BreadcrumbSeparator />
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbPage>{data.name}</BreadcrumbPage>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
</BreadcrumbList>
|
||||||
|
</Breadcrumb>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<div className="flex flex-1">
|
<div className="flex flex-1 min-h-0">
|
||||||
<Sessions></Sessions>
|
<Sessions
|
||||||
<ChatBox></ChatBox>
|
handleConversationCardClick={handleConversationCardClick}
|
||||||
<AppSettings></AppSettings>
|
></Sessions>
|
||||||
|
<ChatBox controller={controller}></ChatBox>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
33
web/src/pages/next-chats/chat/interface.ts
Normal file
33
web/src/pages/next-chats/chat/interface.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { IConversation, IReference, Message } from '@/interfaces/database/chat';
|
||||||
|
import { FormInstance } from 'antd';
|
||||||
|
|
||||||
|
export interface ISegmentedContentProps {
|
||||||
|
show: boolean;
|
||||||
|
form: FormInstance;
|
||||||
|
setHasError: (hasError: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVariable {
|
||||||
|
temperature: number;
|
||||||
|
top_p: number;
|
||||||
|
frequency_penalty: number;
|
||||||
|
presence_penalty: number;
|
||||||
|
max_tokens: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VariableTableDataType {
|
||||||
|
key: string;
|
||||||
|
variable: string;
|
||||||
|
optional: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IPromptConfigParameters = Omit<VariableTableDataType, 'variable'>;
|
||||||
|
|
||||||
|
export interface IMessage extends Message {
|
||||||
|
id: string;
|
||||||
|
reference?: IReference; // the latest news has reference
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IClientConversation extends IConversation {
|
||||||
|
message: IMessage[];
|
||||||
|
}
|
||||||
@ -1,38 +1,60 @@
|
|||||||
|
import { MoreButton } from '@/components/more-button';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { EllipsisVertical, Plus } from 'lucide-react';
|
import { useGetChatSearchParams } from '@/hooks/use-chat-request';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { Plus } from 'lucide-react';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useHandleClickConversationCard } from '../hooks/use-click-card';
|
||||||
|
import { useSelectDerivedConversationList } from '../hooks/use-select-conversation-list';
|
||||||
|
import { ChatSettingSheet } from './app-settings/chat-settings-sheet';
|
||||||
|
|
||||||
function SessionCard() {
|
type SessionProps = Pick<
|
||||||
return (
|
ReturnType<typeof useHandleClickConversationCard>,
|
||||||
<Card className="bg-colors-background-inverse-weak border-colors-outline-neutral-standard">
|
'handleConversationCardClick'
|
||||||
<CardContent className="px-3 py-2 flex justify-between items-center">
|
>;
|
||||||
xxx
|
export function Sessions({ handleConversationCardClick }: SessionProps) {
|
||||||
<Button variant={'icon'} size={'icon'}>
|
const { list: conversationList, addTemporaryConversation } =
|
||||||
<EllipsisVertical />
|
useSelectDerivedConversationList();
|
||||||
</Button>
|
|
||||||
</CardContent>
|
const handleCardClick = useCallback(
|
||||||
</Card>
|
(conversationId: string, isNew: boolean) => () => {
|
||||||
|
handleConversationCardClick(conversationId, isNew);
|
||||||
|
},
|
||||||
|
[handleConversationCardClick],
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
export function Sessions() {
|
const { conversationId } = useGetChatSearchParams();
|
||||||
const sessionList = new Array(10).fill(1);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="p-6 w-[400px] max-w-[20%]">
|
<section className="p-6 w-[400px] max-w-[20%] flex flex-col">
|
||||||
<div className="flex justify-between items-center mb-4">
|
<div className="flex justify-between items-center mb-4">
|
||||||
<span className="text-colors-text-neutral-strong text-2xl font-bold">
|
<span className="text-xl font-bold">Conversations</span>
|
||||||
Sessions
|
<Button variant={'ghost'} onClick={addTemporaryConversation}>
|
||||||
</span>
|
|
||||||
<Button variant={'icon'} size={'icon'}>
|
|
||||||
<Plus></Plus>
|
<Plus></Plus>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4 flex-1 overflow-auto">
|
||||||
{sessionList.map((x) => (
|
{conversationList.map((x) => (
|
||||||
<SessionCard key={x.id}></SessionCard>
|
<Card
|
||||||
|
key={x.id}
|
||||||
|
onClick={handleCardClick(x.id, x.is_new)}
|
||||||
|
className={cn('cursor-pointer bg-transparent', {
|
||||||
|
'bg-bg-card': conversationId === x.id,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<CardContent className="px-3 py-2 flex justify-between items-center group">
|
||||||
|
{x.name}
|
||||||
|
<MoreButton></MoreButton>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="py-2">
|
||||||
|
<ChatSettingSheet>
|
||||||
|
<Button className="w-full">Chat Settings</Button>
|
||||||
|
</ChatSettingSheet>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
14
web/src/pages/next-chats/hooks/use-button-disabled.tsx
Normal file
14
web/src/pages/next-chats/hooks/use-button-disabled.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { useGetChatSearchParams } from '@/hooks/use-chat-request';
|
||||||
|
import { trim } from 'lodash';
|
||||||
|
import { useParams } from 'umi';
|
||||||
|
|
||||||
|
export const useGetSendButtonDisabled = () => {
|
||||||
|
const { conversationId } = useGetChatSearchParams();
|
||||||
|
const { id: dialogId } = useParams();
|
||||||
|
|
||||||
|
return dialogId === '' || conversationId === '';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSendButtonDisabled = (value: string) => {
|
||||||
|
return trim(value) === '';
|
||||||
|
};
|
||||||
20
web/src/pages/next-chats/hooks/use-click-card.ts
Normal file
20
web/src/pages/next-chats/hooks/use-click-card.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { useClickConversationCard } from '@/hooks/use-chat-request';
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
|
export function useHandleClickConversationCard() {
|
||||||
|
const [controller, setController] = useState(new AbortController());
|
||||||
|
const { handleClickConversation } = useClickConversationCard();
|
||||||
|
|
||||||
|
const handleConversationCardClick = useCallback(
|
||||||
|
(conversationId: string, isNew: boolean) => {
|
||||||
|
handleClickConversation(conversationId, isNew ? 'true' : '');
|
||||||
|
setController((pre) => {
|
||||||
|
pre.abort();
|
||||||
|
return new AbortController();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[handleClickConversation],
|
||||||
|
);
|
||||||
|
|
||||||
|
return { controller, handleConversationCardClick };
|
||||||
|
}
|
||||||
29
web/src/pages/next-chats/hooks/use-create-conversation.ts
Normal file
29
web/src/pages/next-chats/hooks/use-create-conversation.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { useGetChatSearchParams } from '@/hooks/use-chat-request';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import {
|
||||||
|
useSetChatRouteParams,
|
||||||
|
useSetConversation,
|
||||||
|
} from './use-send-chat-message';
|
||||||
|
|
||||||
|
export const useCreateConversationBeforeUploadDocument = () => {
|
||||||
|
const { setConversation } = useSetConversation();
|
||||||
|
const { dialogId } = useGetChatSearchParams();
|
||||||
|
const { getConversationIsNew } = useSetChatRouteParams();
|
||||||
|
|
||||||
|
const createConversationBeforeUploadDocument = useCallback(
|
||||||
|
async (message: string) => {
|
||||||
|
const isNew = getConversationIsNew();
|
||||||
|
if (isNew === 'true') {
|
||||||
|
const data = await setConversation(message, true);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[setConversation, getConversationIsNew],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
createConversationBeforeUploadDocument,
|
||||||
|
dialogId,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
import { ChatSearchParams, MessageType } from '@/constants/chat';
|
||||||
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
|
import {
|
||||||
|
useFetchConversationList,
|
||||||
|
useFetchDialogList,
|
||||||
|
} from '@/hooks/use-chat-request';
|
||||||
|
import { IConversation } from '@/interfaces/database/chat';
|
||||||
|
import { getConversationId } from '@/utils/chat';
|
||||||
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
import { useParams, useSearchParams } from 'umi';
|
||||||
|
|
||||||
|
export const useFindPrologueFromDialogList = () => {
|
||||||
|
const { id: dialogId } = useParams();
|
||||||
|
const { data } = useFetchDialogList();
|
||||||
|
|
||||||
|
const prologue = useMemo(() => {
|
||||||
|
return data.dialogs.find((x) => x.id === dialogId)?.prompt_config.prologue;
|
||||||
|
}, [dialogId, data]);
|
||||||
|
|
||||||
|
return prologue;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSetNewConversationRouteParams = () => {
|
||||||
|
const [currentQueryParameters, setSearchParams] = useSearchParams();
|
||||||
|
const newQueryParameters: URLSearchParams = useMemo(
|
||||||
|
() => new URLSearchParams(currentQueryParameters.toString()),
|
||||||
|
[currentQueryParameters],
|
||||||
|
);
|
||||||
|
|
||||||
|
const setNewConversationRouteParams = useCallback(
|
||||||
|
(conversationId: string, isNew: string) => {
|
||||||
|
newQueryParameters.set(ChatSearchParams.ConversationId, conversationId);
|
||||||
|
newQueryParameters.set(ChatSearchParams.isNew, isNew);
|
||||||
|
setSearchParams(newQueryParameters);
|
||||||
|
},
|
||||||
|
[newQueryParameters, setSearchParams],
|
||||||
|
);
|
||||||
|
|
||||||
|
return { setNewConversationRouteParams };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSelectDerivedConversationList = () => {
|
||||||
|
const { t } = useTranslate('chat');
|
||||||
|
|
||||||
|
const [list, setList] = useState<Array<IConversation>>([]);
|
||||||
|
const { data: conversationList, loading } = useFetchConversationList();
|
||||||
|
const { id: dialogId } = useParams();
|
||||||
|
const { setNewConversationRouteParams } = useSetNewConversationRouteParams();
|
||||||
|
const prologue = useFindPrologueFromDialogList();
|
||||||
|
|
||||||
|
const addTemporaryConversation = useCallback(() => {
|
||||||
|
const conversationId = getConversationId();
|
||||||
|
setList((pre) => {
|
||||||
|
if (dialogId) {
|
||||||
|
setNewConversationRouteParams(conversationId, 'true');
|
||||||
|
const nextList = [
|
||||||
|
{
|
||||||
|
id: conversationId,
|
||||||
|
name: t('newConversation'),
|
||||||
|
dialog_id: dialogId,
|
||||||
|
is_new: true,
|
||||||
|
message: [
|
||||||
|
{
|
||||||
|
content: prologue,
|
||||||
|
role: MessageType.Assistant,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as any,
|
||||||
|
...conversationList,
|
||||||
|
];
|
||||||
|
return nextList;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pre;
|
||||||
|
});
|
||||||
|
}, [conversationList, dialogId, prologue, t, setNewConversationRouteParams]);
|
||||||
|
|
||||||
|
// When you first enter the page, select the top conversation card
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setList([...conversationList]);
|
||||||
|
}, [conversationList]);
|
||||||
|
|
||||||
|
return { list, addTemporaryConversation, loading };
|
||||||
|
};
|
||||||
279
web/src/pages/next-chats/hooks/use-send-chat-message.ts
Normal file
279
web/src/pages/next-chats/hooks/use-send-chat-message.ts
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
import { ChatSearchParams, MessageType } from '@/constants/chat';
|
||||||
|
import {
|
||||||
|
useHandleMessageInputChange,
|
||||||
|
useRegenerateMessage,
|
||||||
|
useSelectDerivedMessages,
|
||||||
|
useSendMessageWithSse,
|
||||||
|
} from '@/hooks/logic-hooks';
|
||||||
|
import {
|
||||||
|
useFetchConversation,
|
||||||
|
useGetChatSearchParams,
|
||||||
|
useUpdateConversation,
|
||||||
|
} from '@/hooks/use-chat-request';
|
||||||
|
import { Message } from '@/interfaces/database/chat';
|
||||||
|
import api from '@/utils/api';
|
||||||
|
import { trim } from 'lodash';
|
||||||
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
|
import { useParams, useSearchParams } from 'umi';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
import { IMessage } from '../chat/interface';
|
||||||
|
import { useFindPrologueFromDialogList } from './use-select-conversation-list';
|
||||||
|
|
||||||
|
export const useSetChatRouteParams = () => {
|
||||||
|
const [currentQueryParameters, setSearchParams] = useSearchParams();
|
||||||
|
const newQueryParameters: URLSearchParams = useMemo(
|
||||||
|
() => new URLSearchParams(currentQueryParameters.toString()),
|
||||||
|
[currentQueryParameters],
|
||||||
|
);
|
||||||
|
|
||||||
|
const setConversationIsNew = useCallback(
|
||||||
|
(value: string) => {
|
||||||
|
newQueryParameters.set(ChatSearchParams.isNew, value);
|
||||||
|
setSearchParams(newQueryParameters);
|
||||||
|
},
|
||||||
|
[newQueryParameters, setSearchParams],
|
||||||
|
);
|
||||||
|
|
||||||
|
const getConversationIsNew = useCallback(() => {
|
||||||
|
return newQueryParameters.get(ChatSearchParams.isNew);
|
||||||
|
}, [newQueryParameters]);
|
||||||
|
|
||||||
|
return { setConversationIsNew, getConversationIsNew };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSelectNextMessages = () => {
|
||||||
|
const {
|
||||||
|
scrollRef,
|
||||||
|
messageContainerRef,
|
||||||
|
setDerivedMessages,
|
||||||
|
derivedMessages,
|
||||||
|
addNewestAnswer,
|
||||||
|
addNewestQuestion,
|
||||||
|
removeLatestMessage,
|
||||||
|
removeMessageById,
|
||||||
|
removeMessagesAfterCurrentMessage,
|
||||||
|
} = useSelectDerivedMessages();
|
||||||
|
const { data: conversation, loading } = useFetchConversation();
|
||||||
|
const { conversationId, isNew } = useGetChatSearchParams();
|
||||||
|
const { id: dialogId } = useParams();
|
||||||
|
const prologue = useFindPrologueFromDialogList();
|
||||||
|
|
||||||
|
const addPrologue = useCallback(() => {
|
||||||
|
if (dialogId !== '' && isNew === 'true') {
|
||||||
|
const nextMessage = {
|
||||||
|
role: MessageType.Assistant,
|
||||||
|
content: prologue,
|
||||||
|
id: uuid(),
|
||||||
|
} as IMessage;
|
||||||
|
|
||||||
|
setDerivedMessages([nextMessage]);
|
||||||
|
}
|
||||||
|
}, [dialogId, isNew, prologue, setDerivedMessages]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
addPrologue();
|
||||||
|
}, [addPrologue]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
conversationId &&
|
||||||
|
isNew !== 'true' &&
|
||||||
|
conversation.message?.length > 0
|
||||||
|
) {
|
||||||
|
setDerivedMessages(conversation.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!conversationId) {
|
||||||
|
setDerivedMessages([]);
|
||||||
|
}
|
||||||
|
}, [conversation.message, conversationId, setDerivedMessages, isNew]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
scrollRef,
|
||||||
|
messageContainerRef,
|
||||||
|
derivedMessages,
|
||||||
|
loading,
|
||||||
|
addNewestAnswer,
|
||||||
|
addNewestQuestion,
|
||||||
|
removeLatestMessage,
|
||||||
|
removeMessageById,
|
||||||
|
removeMessagesAfterCurrentMessage,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSetConversation = () => {
|
||||||
|
const { id: dialogId } = useParams();
|
||||||
|
const { updateConversation } = useUpdateConversation();
|
||||||
|
|
||||||
|
const setConversation = useCallback(
|
||||||
|
async (
|
||||||
|
message: string,
|
||||||
|
isNew: boolean = false,
|
||||||
|
conversationId?: string,
|
||||||
|
) => {
|
||||||
|
const data = await updateConversation({
|
||||||
|
dialog_id: dialogId,
|
||||||
|
name: message,
|
||||||
|
is_new: isNew,
|
||||||
|
conversation_id: conversationId,
|
||||||
|
message: [
|
||||||
|
{
|
||||||
|
role: MessageType.Assistant,
|
||||||
|
content: message,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
[updateConversation, dialogId],
|
||||||
|
);
|
||||||
|
|
||||||
|
return { setConversation };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSendMessage = (controller: AbortController) => {
|
||||||
|
const { setConversation } = useSetConversation();
|
||||||
|
const { conversationId, isNew } = useGetChatSearchParams();
|
||||||
|
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
||||||
|
|
||||||
|
const { send, answer, done } = useSendMessageWithSse(
|
||||||
|
api.completeConversation,
|
||||||
|
);
|
||||||
|
const {
|
||||||
|
scrollRef,
|
||||||
|
messageContainerRef,
|
||||||
|
derivedMessages,
|
||||||
|
loading,
|
||||||
|
addNewestAnswer,
|
||||||
|
addNewestQuestion,
|
||||||
|
removeLatestMessage,
|
||||||
|
removeMessageById,
|
||||||
|
removeMessagesAfterCurrentMessage,
|
||||||
|
} = useSelectNextMessages();
|
||||||
|
const { setConversationIsNew, getConversationIsNew } =
|
||||||
|
useSetChatRouteParams();
|
||||||
|
|
||||||
|
const stopOutputMessage = useCallback(() => {
|
||||||
|
controller.abort();
|
||||||
|
}, [controller]);
|
||||||
|
|
||||||
|
const sendMessage = useCallback(
|
||||||
|
async ({
|
||||||
|
message,
|
||||||
|
currentConversationId,
|
||||||
|
messages,
|
||||||
|
}: {
|
||||||
|
message: Message;
|
||||||
|
currentConversationId?: string;
|
||||||
|
messages?: Message[];
|
||||||
|
}) => {
|
||||||
|
const res = await send(
|
||||||
|
{
|
||||||
|
conversation_id: currentConversationId ?? conversationId,
|
||||||
|
messages: [...(messages ?? derivedMessages ?? []), message],
|
||||||
|
},
|
||||||
|
controller,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res && (res?.response.status !== 200 || res?.data?.code !== 0)) {
|
||||||
|
// cancel loading
|
||||||
|
setValue(message.content);
|
||||||
|
console.info('removeLatestMessage111');
|
||||||
|
removeLatestMessage();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
derivedMessages,
|
||||||
|
conversationId,
|
||||||
|
removeLatestMessage,
|
||||||
|
setValue,
|
||||||
|
send,
|
||||||
|
controller,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSendMessage = useCallback(
|
||||||
|
async (message: Message) => {
|
||||||
|
const isNew = getConversationIsNew();
|
||||||
|
if (isNew !== 'true') {
|
||||||
|
sendMessage({ message });
|
||||||
|
} else {
|
||||||
|
const data = await setConversation(
|
||||||
|
message.content,
|
||||||
|
true,
|
||||||
|
conversationId,
|
||||||
|
);
|
||||||
|
if (data.code === 0) {
|
||||||
|
setConversationIsNew('');
|
||||||
|
const id = data.data.id;
|
||||||
|
// currentConversationIdRef.current = id;
|
||||||
|
sendMessage({
|
||||||
|
message,
|
||||||
|
currentConversationId: id,
|
||||||
|
messages: data.data.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
setConversation,
|
||||||
|
sendMessage,
|
||||||
|
setConversationIsNew,
|
||||||
|
getConversationIsNew,
|
||||||
|
conversationId,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
const { regenerateMessage } = useRegenerateMessage({
|
||||||
|
removeMessagesAfterCurrentMessage,
|
||||||
|
sendMessage,
|
||||||
|
messages: derivedMessages,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// #1289
|
||||||
|
if (answer.answer && conversationId && isNew !== 'true') {
|
||||||
|
addNewestAnswer(answer);
|
||||||
|
}
|
||||||
|
}, [answer, addNewestAnswer, conversationId, isNew]);
|
||||||
|
|
||||||
|
const handlePressEnter = useCallback(
|
||||||
|
(documentIds: string[]) => {
|
||||||
|
if (trim(value) === '') return;
|
||||||
|
const id = uuid();
|
||||||
|
|
||||||
|
addNewestQuestion({
|
||||||
|
content: value,
|
||||||
|
doc_ids: documentIds,
|
||||||
|
id,
|
||||||
|
role: MessageType.User,
|
||||||
|
});
|
||||||
|
if (done) {
|
||||||
|
setValue('');
|
||||||
|
handleSendMessage({
|
||||||
|
id,
|
||||||
|
content: value.trim(),
|
||||||
|
role: MessageType.User,
|
||||||
|
doc_ids: documentIds,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[addNewestQuestion, handleSendMessage, done, setValue, value],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
handlePressEnter,
|
||||||
|
handleInputChange,
|
||||||
|
value,
|
||||||
|
setValue,
|
||||||
|
regenerateMessage,
|
||||||
|
sendLoading: !done,
|
||||||
|
loading,
|
||||||
|
scrollRef,
|
||||||
|
messageContainerRef,
|
||||||
|
derivedMessages,
|
||||||
|
removeMessageById,
|
||||||
|
stopOutputMessage,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -11,7 +11,8 @@ import { ChatCard } from './chat-card';
|
|||||||
import { useRenameChat } from './hooks/use-rename-chat';
|
import { useRenameChat } from './hooks/use-rename-chat';
|
||||||
|
|
||||||
export default function ChatList() {
|
export default function ChatList() {
|
||||||
const { data, setPagination, pagination } = useFetchDialogList();
|
const { data, setPagination, pagination, handleInputChange, searchString } =
|
||||||
|
useFetchDialogList();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
initialChatName,
|
initialChatName,
|
||||||
@ -36,7 +37,11 @@ export default function ChatList() {
|
|||||||
return (
|
return (
|
||||||
<section className="flex flex-col w-full flex-1">
|
<section className="flex flex-col w-full flex-1">
|
||||||
<div className="px-8 pt-8">
|
<div className="px-8 pt-8">
|
||||||
<ListFilterBar title="Chat apps">
|
<ListFilterBar
|
||||||
|
title="Chat apps"
|
||||||
|
onSearchChange={handleInputChange}
|
||||||
|
searchString={searchString}
|
||||||
|
>
|
||||||
<Button onClick={handleShowCreateModal}>
|
<Button onClick={handleShowCreateModal}>
|
||||||
<Plus className="size-2.5" />
|
<Plus className="size-2.5" />
|
||||||
{t('chat.createChat')}
|
{t('chat.createChat')}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Modal } from '@/components/ui/modal';
|
import { Modal } from '@/components/ui/modal/modal';
|
||||||
import { IModalProps } from '@/interfaces/common';
|
import { IModalProps } from '@/interfaces/common';
|
||||||
import DebugContent from '@/pages/agent/debug-content';
|
import DebugContent from '@/pages/agent/debug-content';
|
||||||
import { buildBeginInputListFromObject } from '@/pages/agent/form/begin-form/utils';
|
import { buildBeginInputListFromObject } from '@/pages/agent/form/begin-form/utils';
|
||||||
|
|||||||
@ -1,23 +1,73 @@
|
|||||||
import ListFilterBar from '@/components/list-filter-bar';
|
import ListFilterBar from '@/components/list-filter-bar';
|
||||||
|
import { Input } from '@/components/originui/input';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Modal } from '@/components/ui/modal/modal';
|
||||||
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
import { useFetchFlowList } from '@/hooks/flow-hooks';
|
import { useFetchFlowList } from '@/hooks/flow-hooks';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus, Search } from 'lucide-react';
|
||||||
|
import { useState } from 'react';
|
||||||
import { SearchCard } from './search-card';
|
import { SearchCard } from './search-card';
|
||||||
|
|
||||||
export default function SearchList() {
|
export default function SearchList() {
|
||||||
const { data } = useFetchFlowList();
|
const { data } = useFetchFlowList();
|
||||||
|
const { t } = useTranslate('search');
|
||||||
|
const [searchName, setSearchName] = useState('');
|
||||||
|
const handleSearchChange = (value: string) => {
|
||||||
|
console.log(value);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<div className="px-8 pt-8">
|
<div className="px-8 pt-8">
|
||||||
<ListFilterBar title="Search apps">
|
<ListFilterBar
|
||||||
<Button variant={'tertiary'} size={'sm'}>
|
icon={
|
||||||
|
<div className="rounded-sm bg-emerald-400 bg-gradient-to-t from-emerald-400 via-emerald-400 to-emerald-200 p-1 size-6 flex justify-center items-center">
|
||||||
|
<Search size={14} className="font-bold m-auto" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
title="Search apps"
|
||||||
|
showFilter={false}
|
||||||
|
onSearchChange={(e) => handleSearchChange(e.target.value)}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant={'default'}
|
||||||
|
onClick={() => {
|
||||||
|
Modal.show({
|
||||||
|
title: (
|
||||||
|
<div className="rounded-sm bg-emerald-400 bg-gradient-to-t from-emerald-400 via-emerald-400 to-emerald-200 p-1 size-6 flex justify-center items-center">
|
||||||
|
<Search size={14} className="font-bold m-auto" />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
titleClassName: 'border-none',
|
||||||
|
footerClassName: 'border-none',
|
||||||
|
visible: true,
|
||||||
|
children: (
|
||||||
|
<div>
|
||||||
|
<div>{t('createSearch')}</div>
|
||||||
|
<div>name:</div>
|
||||||
|
<Input
|
||||||
|
defaultValue={searchName}
|
||||||
|
onChange={(e) => {
|
||||||
|
console.log(e.target.value, e);
|
||||||
|
setSearchName(e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
onOk: () => {
|
||||||
|
console.log('ok', searchName);
|
||||||
|
},
|
||||||
|
onVisibleChange: (e) => {
|
||||||
|
Modal.hide();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Plus className="mr-2 h-4 w-4" />
|
<Plus className="mr-2 h-4 w-4" />
|
||||||
Create app
|
{t('createSearch')}
|
||||||
</Button>
|
</Button>
|
||||||
</ListFilterBar>
|
</ListFilterBar>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-8 max-h-[84vh] overflow-auto px-8">
|
<div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 max-h-[84vh] overflow-auto px-8">
|
||||||
{data.map((x) => {
|
{data.map((x) => {
|
||||||
return <SearchCard key={x.id} data={x}></SearchCard>;
|
return <SearchCard key={x.id} data={x}></SearchCard>;
|
||||||
})}
|
})}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
import { MoreButton } from '@/components/more-button';
|
||||||
|
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||||
@ -15,38 +16,40 @@ export function SearchCard({ data }: IProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="bg-colors-background-inverse-weak border-colors-outline-neutral-standard">
|
<Card className="bg-colors-background-inverse-weak border-colors-outline-neutral-standard">
|
||||||
<CardContent className="p-4">
|
<CardContent className="p-4 flex gap-2 items-start group">
|
||||||
<div className="flex justify-between mb-4">
|
<div className="flex justify-between mb-4">
|
||||||
{data.avatar ? (
|
<RAGFlowAvatar
|
||||||
<div
|
className="w-[70px] h-[70px]"
|
||||||
className="w-[70px] h-[70px] rounded-xl bg-cover"
|
avatar={data.avatar}
|
||||||
style={{ backgroundImage: `url(${data.avatar})` }}
|
name={data.title}
|
||||||
/>
|
/>
|
||||||
) : (
|
</div>
|
||||||
<Avatar className="w-[70px] h-[70px]">
|
<div className="flex flex-col gap-1">
|
||||||
<AvatarImage src="https://github.com/shadcn.png" />
|
<section className="flex justify-between">
|
||||||
<AvatarFallback>CN</AvatarFallback>
|
<div className="text-[20px] font-bold size-7 leading-5">
|
||||||
</Avatar>
|
{data.title}
|
||||||
)}
|
</div>
|
||||||
|
<MoreButton></MoreButton>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div>An app that does things An app that does things</div>
|
||||||
|
<section className="flex justify-between">
|
||||||
|
<div>
|
||||||
|
Search app
|
||||||
|
<p className="text-sm opacity-80">
|
||||||
|
{formatPureDate(data.update_time)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="space-x-2 invisible group-hover:visible">
|
||||||
|
<Button variant="icon" size="icon" onClick={navigateToSearch}>
|
||||||
|
<ChevronRight className="h-6 w-6" />
|
||||||
|
</Button>
|
||||||
|
<Button variant="icon" size="icon">
|
||||||
|
<Trash2 />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-xl font-bold mb-2">{data.title}</h3>
|
|
||||||
<p>An app that does things An app that does things</p>
|
|
||||||
<section className="flex justify-between pt-3">
|
|
||||||
<div>
|
|
||||||
Search app
|
|
||||||
<p className="text-sm opacity-80">
|
|
||||||
{formatPureDate(data.update_time)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="space-x-2">
|
|
||||||
<Button variant="icon" size="icon" onClick={navigateToSearch}>
|
|
||||||
<ChevronRight className="h-6 w-6" />
|
|
||||||
</Button>
|
|
||||||
<Button variant="icon" size="icon">
|
|
||||||
<Trash2 />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -58,6 +58,28 @@ module.exports = {
|
|||||||
'dot-green': 'var(--dot-green)',
|
'dot-green': 'var(--dot-green)',
|
||||||
'dot-red': 'var(--dot-red)',
|
'dot-red': 'var(--dot-red)',
|
||||||
|
|
||||||
|
/* design colors */
|
||||||
|
|
||||||
|
'bg-base': 'var(--bg-base)',
|
||||||
|
'bg-card': 'var(--bg-card)',
|
||||||
|
'text-primary': 'var(--text-primary)',
|
||||||
|
'text-disabled': 'var(--text-disabled)',
|
||||||
|
'text-input-tip': 'var(--text-input-tip)',
|
||||||
|
'border-default': 'var(--border-default)',
|
||||||
|
'border-accent': 'var(--border-accent)',
|
||||||
|
'border-button': 'var(--border-button)',
|
||||||
|
'accent-primary': 'var(--accent-primary)',
|
||||||
|
'bg-accent': 'var(--bg-accent)',
|
||||||
|
'state--success': 'var(--state--success)',
|
||||||
|
'state--warning': 'var(--state--warning)',
|
||||||
|
'state--error': 'var(--state--error)',
|
||||||
|
'team-group': 'var(--team-group)',
|
||||||
|
'team-member': 'var(--team-member)',
|
||||||
|
'team-department': 'var(--team-department)',
|
||||||
|
'bg-group': 'var(--bg-group)',
|
||||||
|
'bg-member': 'var(--bg-member)',
|
||||||
|
'bg-department': 'var(--bg-department)',
|
||||||
|
|
||||||
primary: {
|
primary: {
|
||||||
DEFAULT: 'hsl(var(--primary))',
|
DEFAULT: 'hsl(var(--primary))',
|
||||||
foreground: 'hsl(var(--primary-foreground))',
|
foreground: 'hsl(var(--primary-foreground))',
|
||||||
|
|||||||
@ -93,6 +93,38 @@
|
|||||||
--input-border: rgba(22, 22, 24, 0.2);
|
--input-border: rgba(22, 22, 24, 0.2);
|
||||||
--dot-green: rgba(59, 160, 92, 1);
|
--dot-green: rgba(59, 160, 92, 1);
|
||||||
--dot-red: rgba(216, 73, 75, 1);
|
--dot-red: rgba(216, 73, 75, 1);
|
||||||
|
|
||||||
|
/* design colors */
|
||||||
|
|
||||||
|
--bg-base: #f6f6f7;
|
||||||
|
/* card color , dividing line */
|
||||||
|
--bg-card: rgba(0, 0, 0, 0.05);
|
||||||
|
/* Button ,Body text, Input completed text */
|
||||||
|
--text-primary: #161618;
|
||||||
|
--text-secondary: #75787a;
|
||||||
|
--text-disabled: #b2b5b7;
|
||||||
|
/* input placeholder color */
|
||||||
|
--text-input-tip: #b2b5b7;
|
||||||
|
/* Input default color */
|
||||||
|
--border-default: rgba(0, 0, 0, 0.2);
|
||||||
|
/* Input accent color */
|
||||||
|
--border-accent: #000000;
|
||||||
|
--border-button: rgba(0, 0, 0, 0.1);
|
||||||
|
/* Regulators, parsing, switches, variables */
|
||||||
|
--accent-primary: #4ca4e7;
|
||||||
|
/* Output Variables Box */
|
||||||
|
--bg-accent: rgba(76, 164, 231, 0.05);
|
||||||
|
|
||||||
|
--state--success: #3ba05c;
|
||||||
|
--state--warning: #faad14;
|
||||||
|
--state-error: #d8494b;
|
||||||
|
|
||||||
|
--team-group: #5ab77e;
|
||||||
|
--team-member: #5c96c8;
|
||||||
|
--team-department: #8866d3;
|
||||||
|
--bg-group: rgba(90, 183, 126, 0.1);
|
||||||
|
--bg-member: rgba(92, 150, 200, 0.1);
|
||||||
|
--bg-department: rgba(136, 102, 211, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
@ -207,6 +239,18 @@
|
|||||||
|
|
||||||
--dot-green: rgba(59, 160, 92, 1);
|
--dot-green: rgba(59, 160, 92, 1);
|
||||||
--dot-red: rgba(216, 73, 75, 1);
|
--dot-red: rgba(216, 73, 75, 1);
|
||||||
|
|
||||||
|
/* design colors */
|
||||||
|
|
||||||
|
--bg-base: #161618;
|
||||||
|
--bg-card: rgba(255, 255, 255, 0.05);
|
||||||
|
--text-primary: #f6f6f7;
|
||||||
|
--text-secondary: #b2b5b7;
|
||||||
|
--text-disabled: #75787a;
|
||||||
|
--text-input-tip: #75787a;
|
||||||
|
--border-default: rgba(255, 255, 255, 0.2);
|
||||||
|
--border-accent: #ffffff;
|
||||||
|
--border-button: rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user