mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-01-04 03:25:30 +08:00
Compare commits
7 Commits
d874683ae4
...
fe32952825
| Author | SHA1 | Date | |
|---|---|---|---|
| fe32952825 | |||
| 5808aef28c | |||
| ca720bd811 | |||
| ba11312766 | |||
| c8bbf7452d | |||
| b08650bc4c | |||
| fb77f9917b |
@ -67,9 +67,17 @@ class CodeExecParam(ToolParamBase):
|
||||
"description": """
|
||||
This tool has a sandbox that can execute code written in 'Python'/'Javascript'. It recieves a piece of code and return a Json string.
|
||||
Here's a code example for Python(`main` function MUST be included):
|
||||
def main(arg1: str, arg2: str) -> dict:
|
||||
def main() -> dict:
|
||||
\"\"\"
|
||||
Generate Fibonacci numbers within 100.
|
||||
\"\"\"
|
||||
def fibonacci_recursive(n):
|
||||
if n <= 1:
|
||||
return n
|
||||
else:
|
||||
return fibonacci_recursive(n-1) + fibonacci_recursive(n-2)
|
||||
return {
|
||||
"result": arg1 + arg2,
|
||||
"result": fibonacci_recursive(100),
|
||||
}
|
||||
|
||||
Here's a code example for Javascript(`main` function MUST be included and exported):
|
||||
|
||||
@ -74,11 +74,11 @@ def rm():
|
||||
@login_required
|
||||
def save():
|
||||
req = request.json
|
||||
req["user_id"] = current_user.id
|
||||
if not isinstance(req["dsl"], str):
|
||||
req["dsl"] = json.dumps(req["dsl"], ensure_ascii=False)
|
||||
req["dsl"] = json.loads(req["dsl"])
|
||||
if "id" not in req:
|
||||
req["user_id"] = current_user.id
|
||||
if UserCanvasService.query(user_id=current_user.id, title=req["title"].strip()):
|
||||
return get_data_error_result(message=f"{req['title'].strip()} already exists.")
|
||||
req["id"] = get_uuid()
|
||||
|
||||
@ -34,6 +34,7 @@ from api.db.services.user_service import TenantService, UserTenantService
|
||||
from api.utils.api_utils import get_data_error_result, get_json_result, server_error_response, validate_request
|
||||
from graphrag.general.mind_map_extractor import MindMapExtractor
|
||||
from rag.app.tag import label_question
|
||||
from rag.prompts.prompt_template import load_prompt
|
||||
from rag.prompts.prompts import chunks_format
|
||||
|
||||
|
||||
@ -389,39 +390,7 @@ def related_questions():
|
||||
req = request.json
|
||||
question = req["question"]
|
||||
chat_mdl = LLMBundle(current_user.id, LLMType.CHAT)
|
||||
prompt = """
|
||||
Role: You are an AI language model assistant tasked with generating 5-10 related questions based on a user’s original query. These questions should help expand the search query scope and improve search relevance.
|
||||
|
||||
Instructions:
|
||||
Input: You are provided with a user’s question.
|
||||
Output: Generate 5-10 alternative questions that are related to the original user question. These alternatives should help retrieve a broader range of relevant documents from a vector database.
|
||||
Context: Focus on rephrasing the original question in different ways, making sure the alternative questions are diverse but still connected to the topic of the original query. Do not create overly obscure, irrelevant, or unrelated questions.
|
||||
Fallback: If you cannot generate any relevant alternatives, do not return any questions.
|
||||
Guidance:
|
||||
1. Each alternative should be unique but still relevant to the original query.
|
||||
2. Keep the phrasing clear, concise, and easy to understand.
|
||||
3. Avoid overly technical jargon or specialized terms unless directly relevant.
|
||||
4. Ensure that each question contributes towards improving search results by broadening the search angle, not narrowing it.
|
||||
|
||||
Example:
|
||||
Original Question: What are the benefits of electric vehicles?
|
||||
|
||||
Alternative Questions:
|
||||
1. How do electric vehicles impact the environment?
|
||||
2. What are the advantages of owning an electric car?
|
||||
3. What is the cost-effectiveness of electric vehicles?
|
||||
4. How do electric vehicles compare to traditional cars in terms of fuel efficiency?
|
||||
5. What are the environmental benefits of switching to electric cars?
|
||||
6. How do electric vehicles help reduce carbon emissions?
|
||||
7. Why are electric vehicles becoming more popular?
|
||||
8. What are the long-term savings of using electric vehicles?
|
||||
9. How do electric vehicles contribute to sustainability?
|
||||
10. What are the key benefits of electric vehicles for consumers?
|
||||
|
||||
Reason:
|
||||
Rephrasing the original query into multiple alternative questions helps the user explore different aspects of their search topic, improving the quality of search results.
|
||||
These questions guide the search engine to provide a more comprehensive set of relevant documents.
|
||||
"""
|
||||
prompt = load_prompt("related_question")
|
||||
ans = chat_mdl.chat(
|
||||
prompt,
|
||||
[
|
||||
|
||||
@ -18,7 +18,9 @@ import re
|
||||
import time
|
||||
import tiktoken
|
||||
from flask import Response, jsonify, request
|
||||
import trio
|
||||
from agent.canvas import Canvas
|
||||
from api import settings
|
||||
from api.db import LLMType, StatusEnum
|
||||
from api.db.db_models import APIToken
|
||||
from api.db.services.api_service import API4ConversationService
|
||||
@ -29,9 +31,15 @@ from api.db.services.conversation_service import completion as rag_completion
|
||||
from api.db.services.dialog_service import DialogService, ask, chat
|
||||
from api.db.services.knowledgebase_service import KnowledgebaseService
|
||||
from api.db.services.llm_service import LLMBundle
|
||||
from api.db.services.search_service import SearchService
|
||||
from api.db.services.user_service import UserTenantService
|
||||
from api.utils import get_uuid
|
||||
from api.utils.api_utils import check_duplicate_ids, get_data_openai, get_error_data_result, get_result, token_required, validate_request
|
||||
from api.utils.api_utils import check_duplicate_ids, get_data_openai, get_error_data_result, get_json_result, get_result, server_error_response, token_required, validate_request
|
||||
from graphrag.general.mind_map_extractor import MindMapExtractor
|
||||
from rag.app.tag import label_question
|
||||
from rag.prompts import chunks_format
|
||||
from rag.prompts.prompt_template import load_prompt
|
||||
from rag.prompts.prompts import cross_languages, keyword_extraction
|
||||
|
||||
|
||||
@manager.route("/chats/<chat_id>/sessions", methods=["POST"]) # noqa: F821
|
||||
@ -855,3 +863,215 @@ def begin_inputs(agent_id):
|
||||
"prologue": canvas.get_prologue()
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@manager.route("/searchbots/ask", methods=["POST"]) # noqa: F821
|
||||
@validate_request("question", "kb_ids")
|
||||
def ask_about_embedded():
|
||||
token = request.headers.get("Authorization").split()
|
||||
if len(token) != 2:
|
||||
return get_error_data_result(message='Authorization is not valid!"')
|
||||
token = token[1]
|
||||
objs = APIToken.query(beta=token)
|
||||
if not objs:
|
||||
return get_error_data_result(message='Authentication error: API key is invalid!"')
|
||||
|
||||
req = request.json
|
||||
uid = objs[0].tenant_id
|
||||
|
||||
def stream():
|
||||
nonlocal req, uid
|
||||
try:
|
||||
for ans in ask(req["question"], req["kb_ids"], uid):
|
||||
yield "data:" + json.dumps({"code": 0, "message": "", "data": ans}, ensure_ascii=False) + "\n\n"
|
||||
except Exception as e:
|
||||
yield "data:" + json.dumps({"code": 500, "message": str(e), "data": {"answer": "**ERROR**: " + str(e), "reference": []}}, ensure_ascii=False) + "\n\n"
|
||||
yield "data:" + json.dumps({"code": 0, "message": "", "data": True}, ensure_ascii=False) + "\n\n"
|
||||
|
||||
resp = Response(stream(), mimetype="text/event-stream")
|
||||
resp.headers.add_header("Cache-control", "no-cache")
|
||||
resp.headers.add_header("Connection", "keep-alive")
|
||||
resp.headers.add_header("X-Accel-Buffering", "no")
|
||||
resp.headers.add_header("Content-Type", "text/event-stream; charset=utf-8")
|
||||
return resp
|
||||
|
||||
|
||||
@manager.route("/searchbots/retrieval_test", methods=['POST']) # noqa: F821
|
||||
@validate_request("kb_id", "question")
|
||||
def retrieval_test_embedded():
|
||||
token = request.headers.get("Authorization").split()
|
||||
if len(token) != 2:
|
||||
return get_error_data_result(message='Authorization is not valid!"')
|
||||
token = token[1]
|
||||
objs = APIToken.query(beta=token)
|
||||
if not objs:
|
||||
return get_error_data_result(message='Authentication error: API key is invalid!"')
|
||||
|
||||
req = request.json
|
||||
page = int(req.get("page", 1))
|
||||
size = int(req.get("size", 30))
|
||||
question = req["question"]
|
||||
kb_ids = req["kb_id"]
|
||||
if isinstance(kb_ids, str):
|
||||
kb_ids = [kb_ids]
|
||||
doc_ids = req.get("doc_ids", [])
|
||||
similarity_threshold = float(req.get("similarity_threshold", 0.0))
|
||||
vector_similarity_weight = float(req.get("vector_similarity_weight", 0.3))
|
||||
use_kg = req.get("use_kg", False)
|
||||
top = int(req.get("top_k", 1024))
|
||||
langs = req.get("cross_languages", [])
|
||||
tenant_ids = []
|
||||
|
||||
tenant_id = objs[0].tenant_id
|
||||
if not tenant_id:
|
||||
return get_error_data_result(message="permission denined.")
|
||||
|
||||
try:
|
||||
tenants = UserTenantService.query(user_id=tenant_id)
|
||||
for kb_id in kb_ids:
|
||||
for tenant in tenants:
|
||||
if KnowledgebaseService.query(
|
||||
tenant_id=tenant.tenant_id, id=kb_id):
|
||||
tenant_ids.append(tenant.tenant_id)
|
||||
break
|
||||
else:
|
||||
return get_json_result(
|
||||
data=False, message='Only owner of knowledgebase authorized for this operation.',
|
||||
code=settings.RetCode.OPERATING_ERROR)
|
||||
|
||||
e, kb = KnowledgebaseService.get_by_id(kb_ids[0])
|
||||
if not e:
|
||||
return get_error_data_result(message="Knowledgebase not found!")
|
||||
|
||||
if langs:
|
||||
question = cross_languages(kb.tenant_id, None, question, langs)
|
||||
|
||||
embd_mdl = LLMBundle(kb.tenant_id, LLMType.EMBEDDING.value, llm_name=kb.embd_id)
|
||||
|
||||
rerank_mdl = None
|
||||
if req.get("rerank_id"):
|
||||
rerank_mdl = LLMBundle(kb.tenant_id, LLMType.RERANK.value, llm_name=req["rerank_id"])
|
||||
|
||||
if req.get("keyword", False):
|
||||
chat_mdl = LLMBundle(kb.tenant_id, LLMType.CHAT)
|
||||
question += keyword_extraction(chat_mdl, question)
|
||||
|
||||
labels = label_question(question, [kb])
|
||||
ranks = settings.retrievaler.retrieval(question, embd_mdl, tenant_ids, kb_ids, page, size,
|
||||
similarity_threshold, vector_similarity_weight, top,
|
||||
doc_ids, rerank_mdl=rerank_mdl, highlight=req.get("highlight"),
|
||||
rank_feature=labels
|
||||
)
|
||||
if use_kg:
|
||||
ck = settings.kg_retrievaler.retrieval(question,
|
||||
tenant_ids,
|
||||
kb_ids,
|
||||
embd_mdl,
|
||||
LLMBundle(kb.tenant_id, LLMType.CHAT))
|
||||
if ck["content_with_weight"]:
|
||||
ranks["chunks"].insert(0, ck)
|
||||
|
||||
for c in ranks["chunks"]:
|
||||
c.pop("vector", None)
|
||||
ranks["labels"] = labels
|
||||
|
||||
return get_json_result(data=ranks)
|
||||
except Exception as e:
|
||||
if str(e).find("not_found") > 0:
|
||||
return get_json_result(data=False, message='No chunk found! Check the chunk status please!',
|
||||
code=settings.RetCode.DATA_ERROR)
|
||||
return server_error_response(e)
|
||||
|
||||
|
||||
@manager.route("/searchbots/related_questions", methods=["POST"]) # noqa: F821
|
||||
@validate_request("question")
|
||||
def related_questions_embedded():
|
||||
token = request.headers.get("Authorization").split()
|
||||
if len(token) != 2:
|
||||
return get_error_data_result(message='Authorization is not valid!"')
|
||||
token = token[1]
|
||||
objs = APIToken.query(beta=token)
|
||||
if not objs:
|
||||
return get_error_data_result(message='Authentication error: API key is invalid!"')
|
||||
|
||||
req = request.json
|
||||
tenant_id = objs[0].tenant_id
|
||||
if not tenant_id:
|
||||
return get_error_data_result(message="permission denined.")
|
||||
question = req["question"]
|
||||
chat_mdl = LLMBundle(tenant_id, LLMType.CHAT)
|
||||
prompt = load_prompt("related_question")
|
||||
ans = chat_mdl.chat(
|
||||
prompt,
|
||||
[
|
||||
{
|
||||
"role": "user",
|
||||
"content": f"""
|
||||
Keywords: {question}
|
||||
Related search terms:
|
||||
""",
|
||||
}
|
||||
],
|
||||
{"temperature": 0.9},
|
||||
)
|
||||
return get_json_result(data=[re.sub(r"^[0-9]\. ", "", a) for a in ans.split("\n") if re.match(r"^[0-9]\. ", a)])
|
||||
|
||||
|
||||
@manager.route("/searchbots/detail", methods=["GET"]) # noqa: F821
|
||||
def detail_share_embedded():
|
||||
token = request.headers.get("Authorization").split()
|
||||
if len(token) != 2:
|
||||
return get_error_data_result(message='Authorization is not valid!"')
|
||||
token = token[1]
|
||||
objs = APIToken.query(beta=token)
|
||||
if not objs:
|
||||
return get_error_data_result(message='Authentication error: API key is invalid!"')
|
||||
|
||||
search_id = request.args["search_id"]
|
||||
tenant_id = objs[0].tenant_id
|
||||
if not tenant_id:
|
||||
return get_error_data_result(message="permission denined.")
|
||||
try:
|
||||
tenants = UserTenantService.query(user_id=tenant_id)
|
||||
for tenant in tenants:
|
||||
if SearchService.query(tenant_id=tenant.tenant_id, id=search_id):
|
||||
break
|
||||
else:
|
||||
return get_json_result(data=False, message="Has no permission for this operation.", code=settings.RetCode.OPERATING_ERROR)
|
||||
|
||||
search = SearchService.get_detail(search_id)
|
||||
if not search:
|
||||
return get_error_data_result(message="Can't find this Search App!")
|
||||
return get_json_result(data=search)
|
||||
except Exception as e:
|
||||
return server_error_response(e)
|
||||
|
||||
|
||||
@manager.route("/searchbots/mindmap", methods=["POST"]) # noqa: F821
|
||||
@validate_request("question", "kb_ids")
|
||||
def mindmap():
|
||||
token = request.headers.get("Authorization").split()
|
||||
if len(token) != 2:
|
||||
return get_error_data_result(message='Authorization is not valid!"')
|
||||
token = token[1]
|
||||
objs = APIToken.query(beta=token)
|
||||
if not objs:
|
||||
return get_error_data_result(message='Authentication error: API key is invalid!"')
|
||||
|
||||
tenant_id = objs[0].tenant_id
|
||||
req = request.json
|
||||
kb_ids = req["kb_ids"]
|
||||
e, kb = KnowledgebaseService.get_by_id(kb_ids[0])
|
||||
if not e:
|
||||
return get_error_data_result(message="Knowledgebase not found!")
|
||||
|
||||
embd_mdl = LLMBundle(kb.tenant_id, LLMType.EMBEDDING, llm_name=kb.embd_id)
|
||||
chat_mdl = LLMBundle(tenant_id, LLMType.CHAT)
|
||||
question = req["question"]
|
||||
ranks = settings.retrievaler.retrieval(question, embd_mdl, kb.tenant_id, kb_ids, 1, 12, 0.3, 0.3, aggs=False, rank_feature=label_question(question, [kb]))
|
||||
mindmap = MindMapExtractor(chat_mdl)
|
||||
mind_map = trio.run(mindmap, [c["content_with_weight"] for c in ranks["chunks"]])
|
||||
mind_map = mind_map.output
|
||||
if "error" in mind_map:
|
||||
return server_error_response(Exception(mind_map["error"]))
|
||||
return get_json_result(data=mind_map)
|
||||
|
||||
@ -1146,60 +1146,35 @@
|
||||
"llm_name": "gemini-2.5-flash",
|
||||
"tags": "LLM,CHAT,1024K,IMAGE2TEXT",
|
||||
"max_tokens": 1048576,
|
||||
"model_type": "image2text",
|
||||
"model_type": "chat",
|
||||
"is_tools": true
|
||||
},
|
||||
{
|
||||
"llm_name": "gemini-2.5-pro",
|
||||
"tags": "LLM,CHAT,IMAGE2TEXT,1024K",
|
||||
"max_tokens": 1048576,
|
||||
"model_type": "image2text",
|
||||
"model_type": "chat",
|
||||
"is_tools": true
|
||||
},
|
||||
{
|
||||
"llm_name": "gemini-2.5-flash-preview-05-20",
|
||||
"llm_name": "gemini-2.5-flash-lite",
|
||||
"tags": "LLM,CHAT,1024K,IMAGE2TEXT",
|
||||
"max_tokens": 1048576,
|
||||
"model_type": "image2text",
|
||||
"model_type": "chat",
|
||||
"is_tools": true
|
||||
},
|
||||
{
|
||||
"llm_name": "gemini-2.0-flash-001",
|
||||
"tags": "LLM,CHAT,1024K",
|
||||
"max_tokens": 1048576,
|
||||
"model_type": "image2text",
|
||||
"is_tools": true
|
||||
},
|
||||
{
|
||||
"llm_name": "gemini-2.0-flash-thinking-exp-01-21",
|
||||
"llm_name": "gemini-2.0-flash",
|
||||
"tags": "LLM,CHAT,1024K",
|
||||
"max_tokens": 1048576,
|
||||
"model_type": "chat",
|
||||
"is_tools": true
|
||||
},
|
||||
{
|
||||
"llm_name": "gemini-1.5-flash",
|
||||
"tags": "LLM,IMAGE2TEXT,1024K",
|
||||
"llm_name": "gemini-2.0-flash-lite",
|
||||
"tags": "LLM,CHAT,1024K",
|
||||
"max_tokens": 1048576,
|
||||
"model_type": "image2text"
|
||||
},
|
||||
{
|
||||
"llm_name": "gemini-2.5-pro-preview-05-06",
|
||||
"tags": "LLM,IMAGE2TEXT,1024K",
|
||||
"max_tokens": 1048576,
|
||||
"model_type": "image2text"
|
||||
},
|
||||
{
|
||||
"llm_name": "gemini-1.5-pro",
|
||||
"tags": "LLM,IMAGE2TEXT,2048K",
|
||||
"max_tokens": 2097152,
|
||||
"model_type": "image2text"
|
||||
},
|
||||
{
|
||||
"llm_name": "gemini-1.5-flash-8b",
|
||||
"tags": "LLM,IMAGE2TEXT,1024K",
|
||||
"max_tokens": 1048576,
|
||||
"model_type": "image2text",
|
||||
"model_type": "chat",
|
||||
"is_tools": true
|
||||
},
|
||||
{
|
||||
|
||||
@ -57,7 +57,7 @@ async def run_graphrag(
|
||||
):
|
||||
chunks.append(d["content_with_weight"])
|
||||
|
||||
with trio.fail_after(len(chunks)*60):
|
||||
with trio.fail_after(max(120, len(chunks)*120)):
|
||||
subgraph = await generate_subgraph(
|
||||
LightKGExt
|
||||
if "method" not in row["kb_parser_config"].get("graphrag", {}) or row["kb_parser_config"]["graphrag"]["method"] != "general"
|
||||
|
||||
@ -43,7 +43,7 @@ dependencies = [
|
||||
"groq==0.9.0",
|
||||
"hanziconv==0.3.2",
|
||||
"html-text==0.6.2",
|
||||
"httpx==0.27.2",
|
||||
"httpx[socks]==0.27.2",
|
||||
"huggingface-hub>=0.25.0,<0.26.0",
|
||||
"infinity-sdk==0.6.0-dev4",
|
||||
"infinity-emb>=0.0.66,<0.0.67",
|
||||
|
||||
@ -539,24 +539,24 @@ class GeminiCV(Base):
|
||||
return res.text, res.usage_metadata.total_token_count
|
||||
|
||||
def chat(self, system, history, gen_conf, images=[]):
|
||||
from transformers import GenerationConfig
|
||||
generation_config = dict(temperature=gen_conf.get("temperature", 0.3), top_p=gen_conf.get("top_p", 0.7))
|
||||
try:
|
||||
response = self.model.generate_content(
|
||||
self._form_history(system, history, images),
|
||||
generation_config=GenerationConfig(temperature=gen_conf.get("temperature", 0.3), top_p=gen_conf.get("top_p", 0.7)))
|
||||
generation_config=generation_config)
|
||||
ans = response.text
|
||||
return ans, response.usage_metadata.total_token_count
|
||||
except Exception as e:
|
||||
return "**ERROR**: " + str(e), 0
|
||||
|
||||
def chat_streamly(self, system, history, gen_conf, images=[]):
|
||||
from transformers import GenerationConfig
|
||||
ans = ""
|
||||
response = None
|
||||
try:
|
||||
generation_config = dict(temperature=gen_conf.get("temperature", 0.3), top_p=gen_conf.get("top_p", 0.7))
|
||||
response = self.model.generate_content(
|
||||
self._form_history(system, history, images),
|
||||
generation_config=GenerationConfig(temperature=gen_conf.get("temperature", 0.3), top_p=gen_conf.get("top_p", 0.7)),
|
||||
generation_config=generation_config,
|
||||
stream=True,
|
||||
)
|
||||
|
||||
@ -572,7 +572,7 @@ class GeminiCV(Base):
|
||||
yield response.usage_metadata.total_token_count
|
||||
else:
|
||||
yield 0
|
||||
|
||||
|
||||
|
||||
class NvidiaCV(Base):
|
||||
_FACTORY_NAME = "NVIDIA"
|
||||
|
||||
@ -100,7 +100,7 @@ class DefaultRerank(Base):
|
||||
old_dynamic_batch_size = self._dynamic_batch_size
|
||||
if max_batch_size is not None:
|
||||
self._dynamic_batch_size = max_batch_size
|
||||
res = np.array([], dtype=float)
|
||||
res = np.array(len(pairs), dtype=float)
|
||||
i = 0
|
||||
while i < len(pairs):
|
||||
cur_i = i
|
||||
@ -111,7 +111,7 @@ class DefaultRerank(Base):
|
||||
try:
|
||||
# call subclass implemented batch processing calculation
|
||||
batch_scores = self._compute_batch_scores(pairs[i : i + current_batch])
|
||||
res = np.append(res, batch_scores)
|
||||
res[i : i + current_batch] = batch_scores
|
||||
i += current_batch
|
||||
self._dynamic_batch_size = min(self._dynamic_batch_size * 2, 8)
|
||||
break
|
||||
@ -125,8 +125,8 @@ class DefaultRerank(Base):
|
||||
raise
|
||||
if retry_count >= max_retries:
|
||||
raise RuntimeError("max retry times, still cannot process batch, please check your GPU memory")
|
||||
self.torch_empty_cache()
|
||||
|
||||
|
||||
self.torch_empty_cache()
|
||||
self._dynamic_batch_size = old_dynamic_batch_size
|
||||
return np.array(res)
|
||||
|
||||
|
||||
55
rag/prompts/related_question.md
Normal file
55
rag/prompts/related_question.md
Normal file
@ -0,0 +1,55 @@
|
||||
# Role
|
||||
You are an AI language model assistant tasked with generating **5-10 related questions** based on a user’s original query.
|
||||
These questions should help **expand the search query scope** and **improve search relevance**.
|
||||
|
||||
---
|
||||
|
||||
## Instructions
|
||||
|
||||
**Input:**
|
||||
You are provided with a **user’s question**.
|
||||
|
||||
**Output:**
|
||||
Generate **5-10 alternative questions** that are **related** to the original user question.
|
||||
These alternatives should help retrieve a **broader range of relevant documents** from a vector database.
|
||||
|
||||
**Context:**
|
||||
Focus on **rephrasing** the original question in different ways, ensuring the alternative questions are **diverse but still connected** to the topic of the original query.
|
||||
Do **not** create overly obscure, irrelevant, or unrelated questions.
|
||||
|
||||
**Fallback:**
|
||||
If you cannot generate any relevant alternatives, do **not** return any questions.
|
||||
|
||||
---
|
||||
|
||||
## Guidance
|
||||
|
||||
1. Each alternative should be **unique** but still **relevant** to the original query.
|
||||
2. Keep the phrasing **clear, concise, and easy to understand**.
|
||||
3. Avoid overly technical jargon or specialized terms **unless directly relevant**.
|
||||
4. Ensure that each question **broadens** the search angle, **not narrows** it.
|
||||
|
||||
---
|
||||
|
||||
## Example
|
||||
|
||||
**Original Question:**
|
||||
> What are the benefits of electric vehicles?
|
||||
|
||||
**Alternative Questions:**
|
||||
1. How do electric vehicles impact the environment?
|
||||
2. What are the advantages of owning an electric car?
|
||||
3. What is the cost-effectiveness of electric vehicles?
|
||||
4. How do electric vehicles compare to traditional cars in terms of fuel efficiency?
|
||||
5. What are the environmental benefits of switching to electric cars?
|
||||
6. How do electric vehicles help reduce carbon emissions?
|
||||
7. Why are electric vehicles becoming more popular?
|
||||
8. What are the long-term savings of using electric vehicles?
|
||||
9. How do electric vehicles contribute to sustainability?
|
||||
10. What are the key benefits of electric vehicles for consumers?
|
||||
|
||||
---
|
||||
|
||||
## Reason
|
||||
Rephrasing the original query into multiple alternative questions helps the user explore **different aspects** of their search topic, improving the **quality of search results**.
|
||||
These questions guide the search engine to provide a **more comprehensive set** of relevant documents.
|
||||
@ -302,7 +302,7 @@ async def build_chunks(task, progress_callback):
|
||||
# If the image is in RGBA mode, convert it to RGB mode before saving it in JPEG format.
|
||||
if d["image"].mode in ("RGBA", "P"):
|
||||
converted_image = d["image"].convert("RGB")
|
||||
d["image"].close() # Close original image
|
||||
#d["image"].close() # Close original image
|
||||
d["image"] = converted_image
|
||||
try:
|
||||
d["image"].save(output_buffer, format='JPEG')
|
||||
|
||||
18
uv.lock
generated
18
uv.lock
generated
@ -2422,6 +2422,11 @@ wheels = [
|
||||
{ url = "https://mirrors.aliyun.com/pypi/packages/56/95/9377bcb415797e44274b51d46e3249eba641711cf3348050f76ee7b15ffc/httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
socks = [
|
||||
{ name = "socksio" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httpx-sse"
|
||||
version = "0.4.1"
|
||||
@ -5308,7 +5313,7 @@ dependencies = [
|
||||
{ name = "groq" },
|
||||
{ name = "hanziconv" },
|
||||
{ name = "html-text" },
|
||||
{ name = "httpx" },
|
||||
{ name = "httpx", extra = ["socks"] },
|
||||
{ name = "huggingface-hub" },
|
||||
{ name = "infinity-emb" },
|
||||
{ name = "infinity-sdk" },
|
||||
@ -5463,7 +5468,7 @@ requires-dist = [
|
||||
{ name = "groq", specifier = "==0.9.0" },
|
||||
{ name = "hanziconv", specifier = "==0.3.2" },
|
||||
{ name = "html-text", specifier = "==0.6.2" },
|
||||
{ name = "httpx", specifier = "==0.27.2" },
|
||||
{ name = "httpx", extras = ["socks"], specifier = "==0.27.2" },
|
||||
{ name = "huggingface-hub", specifier = ">=0.25.0,<0.26.0" },
|
||||
{ name = "infinity-emb", specifier = ">=0.0.66,<0.0.67" },
|
||||
{ name = "infinity-sdk", specifier = "==0.6.0.dev4" },
|
||||
@ -6216,6 +6221,15 @@ wheels = [
|
||||
{ url = "https://mirrors.aliyun.com/pypi/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socksio"
|
||||
version = "1.0.0"
|
||||
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
|
||||
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/f8/5c/48a7d9495be3d1c651198fd99dbb6ce190e2274d0f28b9051307bdec6b85/socksio-1.0.0.tar.gz", hash = "sha256:f88beb3da5b5c38b9890469de67d0cb0f9d494b78b106ca1845f96c10b91c4ac" }
|
||||
wheels = [
|
||||
{ url = "https://mirrors.aliyun.com/pypi/packages/37/c3/6eeb6034408dac0fa653d126c9204ade96b819c936e136c5e8a6897eee9c/socksio-1.0.0-py3-none-any.whl", hash = "sha256:95dc1f15f9b34e8d7b16f06d74b8ccf48f609af32ab33c608d08761c5dcbb1f3" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sortedcontainers"
|
||||
version = "2.4.0"
|
||||
|
||||
@ -28,20 +28,32 @@ interface LlmSettingFieldItemsProps {
|
||||
options?: any[];
|
||||
}
|
||||
|
||||
export const LlmSettingSchema = {
|
||||
export const LLMIdFormField = {
|
||||
llm_id: z.string(),
|
||||
temperature: z.coerce.number().optional(),
|
||||
top_p: z.number().optional(),
|
||||
presence_penalty: z.coerce.number().optional(),
|
||||
frequency_penalty: z.coerce.number().optional(),
|
||||
};
|
||||
|
||||
export const LlmSettingEnabledSchema = {
|
||||
temperatureEnabled: z.boolean().optional(),
|
||||
topPEnabled: z.boolean().optional(),
|
||||
presencePenaltyEnabled: z.boolean().optional(),
|
||||
frequencyPenaltyEnabled: z.boolean().optional(),
|
||||
maxTokensEnabled: z.boolean().optional(),
|
||||
};
|
||||
|
||||
export const LlmSettingFieldSchema = {
|
||||
temperature: z.coerce.number().optional(),
|
||||
top_p: z.number().optional(),
|
||||
presence_penalty: z.coerce.number().optional(),
|
||||
frequency_penalty: z.coerce.number().optional(),
|
||||
max_tokens: z.number().optional(),
|
||||
};
|
||||
|
||||
export const LlmSettingSchema = {
|
||||
...LLMIdFormField,
|
||||
...LlmSettingFieldSchema,
|
||||
...LlmSettingEnabledSchema,
|
||||
};
|
||||
|
||||
export function LlmSettingFieldItems({
|
||||
prefix,
|
||||
options,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { settledModelVariableMap } from '@/constants/knowledge';
|
||||
import { AgentFormContext } from '@/pages/agent/context';
|
||||
import useGraphStore from '@/pages/agent/store';
|
||||
import { setChatVariableEnabledFieldValuePage } from '@/utils/chat';
|
||||
import { useCallback, useContext } from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
|
||||
@ -11,6 +12,20 @@ export function useHandleFreedomChange(
|
||||
const node = useContext(AgentFormContext);
|
||||
const updateNodeForm = useGraphStore((state) => state.updateNodeForm);
|
||||
|
||||
const setLLMParameters = useCallback(
|
||||
(values: Record<string, any>, withPrefix: boolean) => {
|
||||
for (const key in values) {
|
||||
if (Object.prototype.hasOwnProperty.call(values, key)) {
|
||||
const realKey = getFieldWithPrefix(key);
|
||||
const element = values[key as keyof typeof values];
|
||||
|
||||
form.setValue(withPrefix ? realKey : key, element);
|
||||
}
|
||||
}
|
||||
},
|
||||
[form, getFieldWithPrefix],
|
||||
);
|
||||
|
||||
const handleChange = useCallback(
|
||||
(parameter: string) => {
|
||||
const currentValues = { ...form.getValues() };
|
||||
@ -25,16 +40,12 @@ export function useHandleFreedomChange(
|
||||
updateNodeForm(node?.id, nextValues);
|
||||
}
|
||||
|
||||
for (const key in values) {
|
||||
if (Object.prototype.hasOwnProperty.call(values, key)) {
|
||||
const realKey = getFieldWithPrefix(key);
|
||||
const element = values[key as keyof typeof values];
|
||||
const variableCheckBoxFieldMap = setChatVariableEnabledFieldValuePage();
|
||||
|
||||
form.setValue(realKey, element);
|
||||
}
|
||||
}
|
||||
setLLMParameters(values, true);
|
||||
setLLMParameters(variableCheckBoxFieldMap, false);
|
||||
},
|
||||
[form, getFieldWithPrefix, node?.id, updateNodeForm],
|
||||
[form, node?.id, setLLMParameters, updateNodeForm],
|
||||
);
|
||||
|
||||
return handleChange;
|
||||
|
||||
@ -49,7 +49,7 @@ export const RAGFlowAvatar = memo(
|
||||
const initials = getInitials(name);
|
||||
const { from, to } = name
|
||||
? getColorForName(name)
|
||||
: { from: 'hsl(0, 0%, 80%)', to: 'hsl(0, 0%, 30%)' };
|
||||
: { from: 'hsl(0, 0%, 30%)', to: 'hsl(0, 0%, 80%)' };
|
||||
|
||||
const fallbackRef = useRef<HTMLElement>(null);
|
||||
const [fontSize, setFontSize] = useState('0.875rem');
|
||||
|
||||
@ -1419,6 +1419,24 @@ This delimiter is used to split the input text into several text pieces echo of
|
||||
search: {
|
||||
createSearch: 'Create Search',
|
||||
searchGreeting: 'How can I help you today ?',
|
||||
profile: 'Hide Profile',
|
||||
locale: 'Locale',
|
||||
embedCode: 'Embed code',
|
||||
id: 'ID',
|
||||
copySuccess: 'Copy Success',
|
||||
welcomeBack: 'Welcome back',
|
||||
searchSettings: 'Search Settings',
|
||||
name: 'Name',
|
||||
avatar: 'Avatar',
|
||||
description: 'Description',
|
||||
datasets: 'Datasets',
|
||||
rerankModel: 'Rerank Model',
|
||||
AISummary: 'AI Summary',
|
||||
enableWebSearch: 'Enable Web Search',
|
||||
enableRelatedSearch: 'Enable Related Search',
|
||||
showQueryMindmap: 'Show Query Mindmap',
|
||||
embedApp: 'Embed App',
|
||||
relatedSearch: 'Related Search',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -1192,6 +1192,12 @@ export default {
|
||||
search: {
|
||||
createSearch: '新建查詢',
|
||||
searchGreeting: '今天我能為你做些什麽?',
|
||||
profile: '隱藏個人資料',
|
||||
locale: '語言',
|
||||
embedCode: '嵌入代碼',
|
||||
id: 'ID',
|
||||
copySuccess: '複製成功',
|
||||
welcomeBack: '歡迎回來',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -1323,6 +1323,24 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
||||
search: {
|
||||
createSearch: '新建查询',
|
||||
searchGreeting: '今天我能为你做些什么?',
|
||||
profile: '隐藏个人资料',
|
||||
locale: '语言',
|
||||
embedCode: '嵌入代码',
|
||||
id: 'ID',
|
||||
copySuccess: '复制成功',
|
||||
welcomeBack: '欢迎回来',
|
||||
searchSettings: '搜索设置',
|
||||
name: '姓名',
|
||||
avatar: '头像',
|
||||
description: '描述',
|
||||
datasets: '数据集',
|
||||
rerankModel: 'rerank 模型',
|
||||
AISummary: 'AI 总结',
|
||||
enableWebSearch: '启用网页搜索',
|
||||
enableRelatedSearch: '启用相关搜索',
|
||||
showQueryMindmap: '显示查询思维导图',
|
||||
embedApp: '嵌入网站',
|
||||
relatedSearch: '相关搜索',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -54,6 +54,8 @@ export function useAgentToolInitialValues() {
|
||||
return pick(initialValues, 'top_n');
|
||||
case Operator.WenCai:
|
||||
return pick(initialValues, 'top_n', 'query_type');
|
||||
case Operator.Code:
|
||||
return {};
|
||||
|
||||
default:
|
||||
return initialValues;
|
||||
|
||||
@ -7,6 +7,7 @@ import { useNavigate } from 'umi';
|
||||
import { Agents } from './agent-list';
|
||||
import { SeeAllAppCard } from './application-card';
|
||||
import { ChatList } from './chat-list';
|
||||
import { SearchList } from './search-list';
|
||||
|
||||
const IconMap = {
|
||||
[Routes.Chats]: 'chat',
|
||||
@ -56,6 +57,7 @@ export function Applications() {
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{val === Routes.Agents && <Agents></Agents>}
|
||||
{val === Routes.Chats && <ChatList></ChatList>}
|
||||
{val === Routes.Searches && <SearchList></SearchList>}
|
||||
{<SeeAllAppCard click={handleNavigate}></SeeAllAppCard>}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
15
web/src/pages/home/search-list.tsx
Normal file
15
web/src/pages/home/search-list.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import { useFetchSearchList } from '../next-searches/hooks';
|
||||
import { ApplicationCard } from './application-card';
|
||||
|
||||
export function SearchList() {
|
||||
const { data } = useFetchSearchList();
|
||||
|
||||
return data?.data.search_apps
|
||||
.slice(0, 10)
|
||||
.map((x) => (
|
||||
<ApplicationCard
|
||||
key={x.id}
|
||||
app={{ avatar: x.avatar, title: x.name, update_time: x.update_time }}
|
||||
></ApplicationCard>
|
||||
));
|
||||
}
|
||||
@ -3,6 +3,10 @@ import { Form } from '@/components/ui/form';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { useFetchDialog, useSetDialog } from '@/hooks/use-chat-request';
|
||||
import { transformBase64ToFile, transformFile2Base64 } from '@/utils/file-util';
|
||||
import {
|
||||
removeUselessFieldsFromValues,
|
||||
setLLMSettingEnabledValues,
|
||||
} from '@/utils/form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { X } from 'lucide-react';
|
||||
import { useEffect } from 'react';
|
||||
@ -26,6 +30,7 @@ export function ChatSettings({ switchSettingVisible }: ChatSettingsProps) {
|
||||
|
||||
const form = useForm<FormSchemaType>({
|
||||
resolver: zodResolver(formSchema),
|
||||
shouldUnregister: true,
|
||||
defaultValues: {
|
||||
name: '',
|
||||
language: 'English',
|
||||
@ -47,14 +52,18 @@ export function ChatSettings({ switchSettingVisible }: ChatSettingsProps) {
|
||||
});
|
||||
|
||||
async function onSubmit(values: FormSchemaType) {
|
||||
const icon = values.icon;
|
||||
const nextValues: Record<string, any> = removeUselessFieldsFromValues(
|
||||
values,
|
||||
'llm_setting.',
|
||||
);
|
||||
const icon = nextValues.icon;
|
||||
const avatar =
|
||||
Array.isArray(icon) && icon.length > 0
|
||||
? await transformFile2Base64(icon[0])
|
||||
: '';
|
||||
setDialog({
|
||||
...data,
|
||||
...values,
|
||||
...nextValues,
|
||||
icon: avatar,
|
||||
dialog_id: id,
|
||||
});
|
||||
@ -65,9 +74,14 @@ export function ChatSettings({ switchSettingVisible }: ChatSettingsProps) {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const llmSettingEnabledValues = setLLMSettingEnabledValues(
|
||||
data.llm_setting,
|
||||
);
|
||||
|
||||
const nextData = {
|
||||
...data,
|
||||
icon: data.icon ? [transformBase64ToFile(data.icon)] : [],
|
||||
...llmSettingEnabledValues,
|
||||
};
|
||||
form.reset(nextData as FormSchemaType);
|
||||
}, [data, form]);
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import { LlmSettingSchema } from '@/components/llm-setting-items/next';
|
||||
import {
|
||||
LlmSettingEnabledSchema,
|
||||
LlmSettingFieldSchema,
|
||||
} from '@/components/llm-setting-items/next';
|
||||
import { rerankFormSchema } from '@/components/rerank';
|
||||
import { vectorSimilarityWeightSchema } from '@/components/similarity-slider';
|
||||
import { topnSchema } from '@/components/top-n-item';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { omit } from 'lodash';
|
||||
import { z } from 'zod';
|
||||
|
||||
export function useChatSettingSchema() {
|
||||
@ -39,20 +41,23 @@ export function useChatSettingSchema() {
|
||||
}),
|
||||
prompt_config: promptConfigSchema,
|
||||
...rerankFormSchema,
|
||||
llm_setting: z.object(omit(LlmSettingSchema, 'llm_id')),
|
||||
llm_setting: z.object(LlmSettingFieldSchema),
|
||||
...LlmSettingEnabledSchema,
|
||||
llm_id: z.string().optional(),
|
||||
...vectorSimilarityWeightSchema,
|
||||
...topnSchema,
|
||||
meta_data_filter: z
|
||||
.object({
|
||||
method: z.string().optional(),
|
||||
manual: z.array(
|
||||
z.object({
|
||||
key: z.string(),
|
||||
op: z.string(),
|
||||
value: z.string(),
|
||||
}),
|
||||
),
|
||||
manual: z
|
||||
.array(
|
||||
z.object({
|
||||
key: z.string(),
|
||||
op: z.string(),
|
||||
value: z.string(),
|
||||
}),
|
||||
)
|
||||
.optional(),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
||||
@ -7,21 +7,28 @@ import {
|
||||
LanguageAbbreviationMap,
|
||||
} from '@/constants/common';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { message } from 'antd';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useFetchTokenListBeforeOtherStep } from '../agent/hooks/use-show-dialog';
|
||||
|
||||
type IEmbedAppModalProps = {
|
||||
open: any;
|
||||
url: string;
|
||||
token: string;
|
||||
from: string;
|
||||
beta: string;
|
||||
setOpen: (e: any) => void;
|
||||
tenantId: string;
|
||||
};
|
||||
|
||||
const EmbedAppModal = (props: IEmbedAppModalProps) => {
|
||||
const { t } = useTranslate('chat');
|
||||
const { open, setOpen, token = '', from, beta = '', url, tenantId } = props;
|
||||
const { t } = useTranslate('search');
|
||||
const { open, setOpen, token = '', from, url, tenantId } = props;
|
||||
const { beta, handleOperate } = useFetchTokenListBeforeOtherStep();
|
||||
useEffect(() => {
|
||||
if (open && !beta) {
|
||||
handleOperate();
|
||||
}
|
||||
}, [handleOperate, open, beta]);
|
||||
const [hideAvatar, setHideAvatar] = useState(false);
|
||||
const [locale, setLocale] = useState('');
|
||||
|
||||
@ -69,7 +76,7 @@ const EmbedAppModal = (props: IEmbedAppModalProps) => {
|
||||
{/* Hide Avatar Toggle */}
|
||||
<div className="mb-6">
|
||||
<label className="block text-sm font-medium mb-2">
|
||||
{t('avatarHidden')}
|
||||
{t('profile')}
|
||||
</label>
|
||||
<div className="flex items-center">
|
||||
<Switch
|
||||
@ -83,7 +90,9 @@ const EmbedAppModal = (props: IEmbedAppModalProps) => {
|
||||
|
||||
{/* Locale Select */}
|
||||
<div className="mb-6">
|
||||
<label className="block text-sm font-medium mb-2">Locale</label>
|
||||
<label className="block text-sm font-medium mb-2">
|
||||
{t('locale')}
|
||||
</label>
|
||||
<RAGFlowSelect
|
||||
placeholder="Select a locale"
|
||||
value={locale}
|
||||
@ -93,7 +102,9 @@ const EmbedAppModal = (props: IEmbedAppModalProps) => {
|
||||
</div>
|
||||
{/* Embed Code */}
|
||||
<div className="mb-6">
|
||||
<label className="block text-sm font-medium mb-2">Embed code</label>
|
||||
<label className="block text-sm font-medium mb-2">
|
||||
{t('embedCode')}
|
||||
</label>
|
||||
{/* <div className=" border rounded-lg"> */}
|
||||
{/* <pre className="text-sm whitespace-pre-wrap">{text}</pre> */}
|
||||
<HightLightMarkdown>{text}</HightLightMarkdown>
|
||||
@ -102,18 +113,21 @@ const EmbedAppModal = (props: IEmbedAppModalProps) => {
|
||||
|
||||
{/* ID Field */}
|
||||
<div className="mb-4">
|
||||
<label className="block text-sm font-medium mb-2">ID</label>
|
||||
<div className="flex items-center">
|
||||
<label className="block text-sm font-medium mb-2">{t('id')}</label>
|
||||
<div className="flex items-center border border-border rounded-lg bg-bg-base">
|
||||
<input
|
||||
type="text"
|
||||
value={token}
|
||||
readOnly
|
||||
className="flex-1 px-4 py-2 border border-gray-700 rounded-lg bg-bg-base focus:outline-none"
|
||||
className="flex-1 px-4 py-2 focus:outline-none bg-bg-base rounded-lg"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigator.clipboard.writeText(token)}
|
||||
className="ml-2 p-2 text-gray-400 hover:text-white transition-colors"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(token);
|
||||
message.success(t('copySuccess'));
|
||||
}}
|
||||
className="ml-2 p-2 hover:text-white transition-colors"
|
||||
title="Copy ID"
|
||||
>
|
||||
<svg
|
||||
|
||||
@ -486,3 +486,16 @@ export const useSearching = ({
|
||||
onChange,
|
||||
};
|
||||
};
|
||||
|
||||
export const useCheckSettings = (data: ISearchAppDetailProps) => {
|
||||
if (!data) {
|
||||
return {
|
||||
openSetting: false,
|
||||
};
|
||||
}
|
||||
const { search_config, name } = data;
|
||||
const { kb_ids } = search_config;
|
||||
return {
|
||||
openSetting: kb_ids && kb_ids.length && name ? false : true,
|
||||
};
|
||||
};
|
||||
|
||||
@ -13,12 +13,13 @@ import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { useFetchTenantInfo } from '@/hooks/user-setting-hooks';
|
||||
import { Send, Settings } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useFetchTokenListBeforeOtherStep } from '../agent/hooks/use-show-dialog';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
ISearchAppDetailProps,
|
||||
useFetchSearchDetail,
|
||||
} from '../next-searches/hooks';
|
||||
import EmbedAppModal from './embed-app-modal';
|
||||
import { useCheckSettings } from './hooks';
|
||||
import './index.less';
|
||||
import SearchHome from './search-home';
|
||||
import { SearchSetting } from './search-setting';
|
||||
@ -28,15 +29,20 @@ export default function SearchPage() {
|
||||
const { navigateToSearchList } = useNavigatePage();
|
||||
const [isSearching, setIsSearching] = useState(false);
|
||||
const { data: SearchData } = useFetchSearchDetail();
|
||||
const { beta, handleOperate } = useFetchTokenListBeforeOtherStep();
|
||||
|
||||
const [openSetting, setOpenSetting] = useState(false);
|
||||
const [openEmbed, setOpenEmbed] = useState(false);
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const { data: tenantInfo } = useFetchTenantInfo();
|
||||
const tenantId = tenantInfo.tenant_id;
|
||||
const { t } = useTranslation();
|
||||
const { openSetting: checkOpenSetting } = useCheckSettings(
|
||||
SearchData as ISearchAppDetailProps,
|
||||
);
|
||||
useEffect(() => {
|
||||
handleOperate();
|
||||
}, [handleOperate]);
|
||||
setOpenSetting(checkOpenSetting);
|
||||
}, [checkOpenSetting]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isSearching) {
|
||||
setOpenSetting(false);
|
||||
@ -60,7 +66,7 @@ export default function SearchPage() {
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
</PageHeader>
|
||||
<div className="flex gap-3 w-full">
|
||||
<div className="flex gap-3 w-full bg-bg-base">
|
||||
<div className="flex-1">
|
||||
{!isSearching && (
|
||||
<div className="animate-fade-in-down">
|
||||
@ -98,7 +104,6 @@ export default function SearchPage() {
|
||||
url="/next-search/share"
|
||||
token={SearchData?.id as string}
|
||||
from={SharedFrom.Search}
|
||||
beta={beta}
|
||||
tenantId={tenantId}
|
||||
/>
|
||||
}
|
||||
@ -119,7 +124,7 @@ export default function SearchPage() {
|
||||
onClick={() => setOpenEmbed(!openEmbed)}
|
||||
>
|
||||
<Send />
|
||||
<div>Embed App</div>
|
||||
<div>{t('search.embedApp')}</div>
|
||||
</Button>
|
||||
</div>
|
||||
{!isSearching && (
|
||||
@ -130,7 +135,9 @@ export default function SearchPage() {
|
||||
onClick={() => setOpenSetting(!openSetting)}
|
||||
>
|
||||
<Settings className="text-text-secondary" />
|
||||
<div className="text-text-secondary">Search Settings</div>
|
||||
<div className="text-text-secondary">
|
||||
{t('search.searchSettings')}
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -3,6 +3,7 @@ import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Search } from 'lucide-react';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import './index.less';
|
||||
import Spotlight from './spotlight';
|
||||
|
||||
@ -18,6 +19,7 @@ export default function SearchPage({
|
||||
setSearchText: Dispatch<SetStateAction<string>>;
|
||||
}) {
|
||||
const { data: userInfo } = useFetchUserInfo();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<section className="relative w-full flex transition-all justify-center items-center mt-32">
|
||||
<div className="relative z-10 px-8 pt-8 flex text-transparent flex-col justify-center items-center w-[780px]">
|
||||
@ -36,14 +38,14 @@ export default function SearchPage({
|
||||
<>
|
||||
<p className="mb-4 transition-opacity">👋 Hi there</p>
|
||||
<p className="mb-10 transition-opacity">
|
||||
Welcome back, {userInfo?.nickname}
|
||||
{t('search.welcomeBack')}, {userInfo?.nickname}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="relative w-full ">
|
||||
<Input
|
||||
placeholder="How can I help you today?"
|
||||
placeholder={t('search.searchGreeting')}
|
||||
className="w-full rounded-full py-6 px-4 pr-10 text-text-primary text-lg bg-bg-base delay-700"
|
||||
value={searchText}
|
||||
onKeyUp={(e) => {
|
||||
@ -57,7 +59,7 @@ export default function SearchPage({
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="absolute right-2 top-1/2 -translate-y-1/2 transform rounded-full bg-white p-2 text-gray-800 shadow w-12"
|
||||
className="absolute right-2 top-1/2 -translate-y-1/2 transform rounded-full bg-text-primary p-2 text-bg-base shadow w-12"
|
||||
onClick={() => {
|
||||
setIsSearching(!isSearching);
|
||||
}}
|
||||
|
||||
@ -28,10 +28,10 @@ import { IKnowledge } from '@/interfaces/database/knowledge';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { transformFile2Base64 } from '@/utils/file-util';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { t } from 'i18next';
|
||||
import { Pencil, Upload, X } from 'lucide-react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { z } from 'zod';
|
||||
import {
|
||||
LlmModelType,
|
||||
@ -113,6 +113,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
const [datasetList, setDatasetList] = useState<MultiSelectOptionType[]>([]);
|
||||
const [datasetSelectEmbdId, setDatasetSelectEmbdId] = useState('');
|
||||
const descriptionDefaultValue = 'You are an intelligent assistant.';
|
||||
const { t } = useTranslation();
|
||||
const resetForm = useCallback(() => {
|
||||
formMethods.reset({
|
||||
search_id: data?.id,
|
||||
@ -305,7 +306,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
style={{ maxHeight: 'calc(100dvh - 170px)' }}
|
||||
>
|
||||
<div className="flex justify-between items-center text-base mb-8">
|
||||
<div className="text-text-primary">Search Settings</div>
|
||||
<div className="text-text-primary">{t('search.searchSettings')}</div>
|
||||
<div onClick={() => setOpen(false)}>
|
||||
<X size={16} className="text-text-primary cursor-pointer" />
|
||||
</div>
|
||||
@ -334,10 +335,11 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<span className="text-destructive mr-1"> *</span>Name
|
||||
<span className="text-destructive mr-1"> *</span>
|
||||
{t('search.name')}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="Name" {...field} />
|
||||
<Input placeholder={t('search.name')} {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -350,7 +352,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
name="avatar"
|
||||
render={() => (
|
||||
<FormItem>
|
||||
<FormLabel>Avatar</FormLabel>
|
||||
<FormLabel>{t('search.avatar')}</FormLabel>
|
||||
<FormControl>
|
||||
<div className="relative group flex items-end gap-2">
|
||||
<div>
|
||||
@ -413,7 +415,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
name="description"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Description</FormLabel>
|
||||
<FormLabel>{t('search.description')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder="You are an intelligent assistant."
|
||||
@ -443,7 +445,8 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<span className="text-destructive mr-1"> *</span>Datasets
|
||||
<span className="text-destructive mr-1"> *</span>
|
||||
{t('search.datasets')}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<MultiSelect
|
||||
@ -501,26 +504,6 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
// <FormItem className="flex flex-col">
|
||||
// <FormLabel>
|
||||
// <span className="text-destructive mr-1"> *</span>Keyword
|
||||
// Similarity Weight
|
||||
// </FormLabel>
|
||||
// <FormControl>
|
||||
// {/* <div className="flex justify-between items-center">
|
||||
// <SingleFormSlider
|
||||
// max={100}
|
||||
// step={1}
|
||||
// value={field.value as number}
|
||||
// onChange={(values) => field.onChange(values)}
|
||||
// ></SingleFormSlider>
|
||||
// <Label className="w-10 h-6 bg-bg-card flex justify-center items-center rounded-lg ml-20">
|
||||
// {field.value}
|
||||
// </Label>
|
||||
// </div> */}
|
||||
// </FormControl>
|
||||
// <FormMessage />
|
||||
// </FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
@ -536,7 +519,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel>Rerank Model</FormLabel>
|
||||
<FormLabel>{t('search.rerankModel')}</FormLabel>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
@ -612,7 +595,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel>AI Summary</FormLabel>
|
||||
<FormLabel>{t('search.AISummary')}</FormLabel>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
@ -636,7 +619,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel>Enable Web Search</FormLabel>
|
||||
<FormLabel>{t('search.enableWebSearch')}</FormLabel>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
@ -652,7 +635,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel>Enable Related Search</FormLabel>
|
||||
<FormLabel>{t('search.enableRelatedSearch')}</FormLabel>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
@ -668,7 +651,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel>Show Query Mindmap</FormLabel>
|
||||
<FormLabel>{t('search.showQueryMindmap')}</FormLabel>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
@ -683,9 +666,9 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
{t('modal.cancelText')}
|
||||
</Button>
|
||||
<Button type="submit">Confirm</Button>
|
||||
<Button type="submit">{t('modal.okText')}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@ -9,13 +9,11 @@ import {
|
||||
} from '@/components/ui/popover';
|
||||
import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { Spin } from '@/components/ui/spin';
|
||||
import { IReference } from '@/interfaces/database/chat';
|
||||
import { cn } from '@/lib/utils';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { TFunction } from 'i18next';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { BrainCircuit, Search, Square, X } from 'lucide-react';
|
||||
import { BrainCircuit, Search, X } from 'lucide-react';
|
||||
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ISearchAppDetailProps } from '../next-searches/hooks';
|
||||
@ -35,7 +33,6 @@ export default function SearchingView({
|
||||
answer,
|
||||
sendingLoading,
|
||||
relatedQuestions,
|
||||
loading,
|
||||
isFirstRender,
|
||||
selectedDocumentIds,
|
||||
isSearchStrEmpty,
|
||||
@ -56,19 +53,17 @@ export default function SearchingView({
|
||||
handleSearch,
|
||||
pagination,
|
||||
onChange,
|
||||
t,
|
||||
}: ISearchReturnProps & {
|
||||
setIsSearching?: Dispatch<SetStateAction<boolean>>;
|
||||
searchData: ISearchAppDetailProps;
|
||||
t: TFunction<'translation', undefined>;
|
||||
}) {
|
||||
const { t: tt, i18n } = useTranslation();
|
||||
useEffect(() => {
|
||||
const changeLanguage = async () => {
|
||||
await i18n.changeLanguage('zh');
|
||||
};
|
||||
changeLanguage();
|
||||
}, [i18n]);
|
||||
const { t } = useTranslation();
|
||||
// useEffect(() => {
|
||||
// const changeLanguage = async () => {
|
||||
// await i18n.changeLanguage('zh');
|
||||
// };
|
||||
// changeLanguage();
|
||||
// }, [i18n]);
|
||||
const [searchtext, setSearchtext] = useState<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
@ -105,9 +100,9 @@ export default function SearchingView({
|
||||
<div className={cn('flex flex-col justify-start items-start w-full')}>
|
||||
<div className="relative w-full text-primary">
|
||||
<Input
|
||||
placeholder={tt('search.searchGreeting')}
|
||||
placeholder={t('search.searchGreeting')}
|
||||
className={cn(
|
||||
'w-full rounded-full py-6 pl-4 !pr-[8rem] text-primary text-lg bg-background',
|
||||
'w-full rounded-full py-6 pl-4 !pr-[8rem] text-primary text-lg bg-bg-base',
|
||||
)}
|
||||
value={searchtext}
|
||||
onChange={(e) => {
|
||||
@ -122,16 +117,17 @@ export default function SearchingView({
|
||||
/>
|
||||
<div className="absolute right-2 top-1/2 -translate-y-1/2 transform flex items-center gap-1">
|
||||
<X
|
||||
className="text-text-secondary"
|
||||
className="text-text-secondary cursor-pointer"
|
||||
size={14}
|
||||
onClick={() => {
|
||||
setSearchtext('');
|
||||
handleClickRelatedQuestion('');
|
||||
}}
|
||||
/>
|
||||
<span className="text-text-secondary">|</span>
|
||||
<span className="text-text-secondary ml-4">|</span>
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-full bg-white p-1 text-gray-800 shadow w-12 h-8 ml-4"
|
||||
className="rounded-full bg-text-primary p-1 text-bg-base shadow w-12 h-8 ml-4"
|
||||
onClick={() => {
|
||||
if (sendingLoading) {
|
||||
stopOutputMessage();
|
||||
@ -141,7 +137,8 @@ export default function SearchingView({
|
||||
}}
|
||||
>
|
||||
{sendingLoading ? (
|
||||
<Square size={22} className="m-auto" />
|
||||
// <Square size={22} className="m-auto" />
|
||||
<div className="w-2 h-2 bg-bg-base m-auto"></div>
|
||||
) : (
|
||||
<Search size={22} className="m-auto" />
|
||||
)}
|
||||
@ -157,10 +154,10 @@ export default function SearchingView({
|
||||
{searchData.search_config.summary && !isSearchStrEmpty && (
|
||||
<>
|
||||
<div className="flex justify-start items-start text-text-primary text-2xl">
|
||||
AI Summary
|
||||
{t('search.AISummary')}
|
||||
</div>
|
||||
{isEmpty(answer) && sendingLoading ? (
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-2 mt-2">
|
||||
<Skeleton className="h-4 w-full bg-bg-card" />
|
||||
<Skeleton className="h-4 w-full bg-bg-card" />
|
||||
<Skeleton className="h-4 w-2/3 bg-bg-card" />
|
||||
@ -194,76 +191,75 @@ export default function SearchingView({
|
||||
</>
|
||||
)}
|
||||
<div className="mt-3 ">
|
||||
<Spin spinning={loading}>
|
||||
{chunks?.length > 0 && (
|
||||
<>
|
||||
{chunks.map((chunk, index) => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
key={chunk.chunk_id}
|
||||
className="w-full flex flex-col"
|
||||
>
|
||||
<div className="w-full">
|
||||
<ImageWithPopover
|
||||
id={chunk.img_id}
|
||||
></ImageWithPopover>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: DOMPurify.sanitize(
|
||||
`${chunk.highlight}...`,
|
||||
),
|
||||
}}
|
||||
className="text-sm text-text-primary mb-1"
|
||||
></div>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="text-text-primary">
|
||||
<HightLightMarkdown>
|
||||
{chunk.content_with_weight}
|
||||
</HightLightMarkdown>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
<div
|
||||
className="flex gap-2 items-center text-xs text-text-secondary border p-1 rounded-lg w-fit"
|
||||
onClick={() =>
|
||||
clickDocumentButton(chunk.doc_id, chunk as any)
|
||||
}
|
||||
>
|
||||
<FileIcon name={chunk.docnm_kwd}></FileIcon>
|
||||
{chunk.docnm_kwd}
|
||||
</div>
|
||||
{chunks?.length > 0 && (
|
||||
<>
|
||||
{chunks.map((chunk, index) => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
key={chunk.chunk_id}
|
||||
className="w-full flex flex-col"
|
||||
>
|
||||
<div className="w-full">
|
||||
<ImageWithPopover
|
||||
id={chunk.img_id}
|
||||
></ImageWithPopover>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: DOMPurify.sanitize(
|
||||
`${chunk.highlight}...`,
|
||||
),
|
||||
}}
|
||||
className="text-sm text-text-primary mb-1"
|
||||
></div>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="text-text-primary">
|
||||
<HightLightMarkdown>
|
||||
{chunk.content_with_weight}
|
||||
</HightLightMarkdown>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
{index < chunks.length - 1 && (
|
||||
<div className="w-full border-b border-border-default/80 mt-6"></div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</Spin>
|
||||
{relatedQuestions?.length > 0 && (
|
||||
<div className="mt-14 w-full overflow-hidden opacity-100 max-h-96">
|
||||
<p className="text-text-primary mb-2 text-xl">
|
||||
Related Search
|
||||
</p>
|
||||
<div className="mt-2 flex flex-wrap justify-start gap-2">
|
||||
{relatedQuestions?.map((x, idx) => (
|
||||
<Button
|
||||
key={idx}
|
||||
variant="transparent"
|
||||
className="bg-bg-card text-text-secondary"
|
||||
onClick={handleClickRelatedQuestion(x)}
|
||||
>
|
||||
Related Search{x}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="flex gap-2 items-center text-xs text-text-secondary border p-1 rounded-lg w-fit"
|
||||
onClick={() =>
|
||||
clickDocumentButton(chunk.doc_id, chunk as any)
|
||||
}
|
||||
>
|
||||
<FileIcon name={chunk.docnm_kwd}></FileIcon>
|
||||
{chunk.docnm_kwd}
|
||||
</div>
|
||||
</div>
|
||||
{index < chunks.length - 1 && (
|
||||
<div className="w-full border-b border-border-default/80 mt-6"></div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
{relatedQuestions?.length > 0 &&
|
||||
searchData.search_config.related_search && (
|
||||
<div className="mt-14 w-full overflow-hidden opacity-100 max-h-96">
|
||||
<p className="text-text-primary mb-2 text-xl">
|
||||
{t('relatedSearch')}
|
||||
</p>
|
||||
<div className="mt-2 flex flex-wrap justify-start gap-2">
|
||||
{relatedQuestions?.map((x, idx) => (
|
||||
<Button
|
||||
key={idx}
|
||||
variant="transparent"
|
||||
className="bg-bg-card text-text-secondary"
|
||||
onClick={handleClickRelatedQuestion(x)}
|
||||
>
|
||||
{x}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ISearchAppDetailProps } from '../next-searches/hooks';
|
||||
import { useSearching } from './hooks';
|
||||
import './index.less';
|
||||
@ -21,13 +20,11 @@ export default function SearchingPage({
|
||||
setIsSearching,
|
||||
setSearchText,
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<SearchingView
|
||||
{...searchingParam}
|
||||
searchData={searchData}
|
||||
setIsSearching={setIsSearching}
|
||||
t={t}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
|
||||
import i18n from '@/locales/config';
|
||||
import { useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
ISearchAppDetailProps,
|
||||
useFetchSearchDetail,
|
||||
@ -9,7 +9,7 @@ import { useGetSharedSearchParams, useSearching } from '../hooks';
|
||||
import '../index.less';
|
||||
import SearchingView from '../search-view';
|
||||
export default function SearchingPage() {
|
||||
const { tenantId, locale } = useGetSharedSearchParams();
|
||||
const { tenantId, locale, visibleAvatar } = useGetSharedSearchParams();
|
||||
const {
|
||||
data: searchData = {
|
||||
search_config: { kb_ids: [] },
|
||||
@ -18,18 +18,25 @@ export default function SearchingPage() {
|
||||
const searchingParam = useSearching({
|
||||
data: searchData,
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
|
||||
// useEffect(() => {
|
||||
// if (locale) {
|
||||
// i18n.changeLanguage(locale);
|
||||
// }
|
||||
// }, [locale, i18n]);
|
||||
useEffect(() => {
|
||||
console.log('locale', locale, i18n.language);
|
||||
if (locale && i18n.language !== locale) {
|
||||
i18n.changeLanguage(locale);
|
||||
}
|
||||
}, [locale]);
|
||||
return <SearchingView {...searchingParam} searchData={searchData} t={t} />;
|
||||
return (
|
||||
<>
|
||||
{visibleAvatar && (
|
||||
<div className="flex justify-start items-center gap-1 mx-6 mt-6 text-text-primary">
|
||||
<RAGFlowAvatar
|
||||
avatar={searchData.avatar}
|
||||
name={searchData.name}
|
||||
></RAGFlowAvatar>
|
||||
<div>{searchData.name}</div>
|
||||
</div>
|
||||
)}
|
||||
<SearchingView {...searchingParam} searchData={searchData} />;
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -30,3 +30,22 @@ export const removeUselessFieldsFromValues = (values: any, prefix?: string) => {
|
||||
export function buildOptions(data: Record<string, any>) {
|
||||
return Object.values(data).map((val) => ({ label: val, value: val }));
|
||||
}
|
||||
|
||||
export function setLLMSettingEnabledValues(
|
||||
initialLlmSetting?: Record<string, any>,
|
||||
) {
|
||||
const values = Object.keys(variableEnabledFieldMap).reduce<
|
||||
Record<string, boolean>
|
||||
>((pre, field) => {
|
||||
pre[field] =
|
||||
initialLlmSetting === undefined
|
||||
? false
|
||||
: !!initialLlmSetting[
|
||||
variableEnabledFieldMap[
|
||||
field as keyof typeof variableEnabledFieldMap
|
||||
]
|
||||
];
|
||||
return pre;
|
||||
}, {});
|
||||
return values;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user