mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-01-04 03:25:30 +08:00
Compare commits
8 Commits
9db999ccae
...
1deb0a2d42
| Author | SHA1 | Date | |
|---|---|---|---|
| 1deb0a2d42 | |||
| dd055deee9 | |||
| a249803961 | |||
| 6ec3f18e22 | |||
| 7724acbadb | |||
| a36ba95c1c | |||
| 30ccc4a66c | |||
| dda5a0080a |
@ -20,94 +20,128 @@ BEGIN_SEARCH_RESULT = "<|begin_search_result|>"
|
||||
END_SEARCH_RESULT = "<|end_search_result|>"
|
||||
MAX_SEARCH_LIMIT = 6
|
||||
|
||||
REASON_PROMPT = (
|
||||
"You are a reasoning assistant with the ability to perform dataset searches to help "
|
||||
"you answer the user's question accurately. You have special tools:\n\n"
|
||||
f"- To perform a search: write {BEGIN_SEARCH_QUERY} your query here {END_SEARCH_QUERY}.\n"
|
||||
f"Then, the system will search and analyze relevant content, then provide you with helpful information in the format {BEGIN_SEARCH_RESULT} ...search results... {END_SEARCH_RESULT}.\n\n"
|
||||
f"You can repeat the search process multiple times if necessary. The maximum number of search attempts is limited to {MAX_SEARCH_LIMIT}.\n\n"
|
||||
"Once you have all the information you need, continue your reasoning.\n\n"
|
||||
"-- Example 1 --\n" ########################################
|
||||
"Question: \"Are both the directors of Jaws and Casino Royale from the same country?\"\n"
|
||||
"Assistant:\n"
|
||||
f" {BEGIN_SEARCH_QUERY}Who is the director of Jaws?{END_SEARCH_QUERY}\n\n"
|
||||
"User:\n"
|
||||
f" {BEGIN_SEARCH_RESULT}\nThe director of Jaws is Steven Spielberg...\n{END_SEARCH_RESULT}\n\n"
|
||||
"Continues reasoning with the new information.\n"
|
||||
"Assistant:\n"
|
||||
f" {BEGIN_SEARCH_QUERY}Where is Steven Spielberg from?{END_SEARCH_QUERY}\n\n"
|
||||
"User:\n"
|
||||
f" {BEGIN_SEARCH_RESULT}\nSteven Allan Spielberg is an American filmmaker...\n{END_SEARCH_RESULT}\n\n"
|
||||
"Continues reasoning with the new information...\n\n"
|
||||
"Assistant:\n"
|
||||
f" {BEGIN_SEARCH_QUERY}Who is the director of Casino Royale?{END_SEARCH_QUERY}\n\n"
|
||||
"User:\n"
|
||||
f" {BEGIN_SEARCH_RESULT}\nCasino Royale is a 2006 spy film directed by Martin Campbell...\n{END_SEARCH_RESULT}\n\n"
|
||||
"Continues reasoning with the new information...\n\n"
|
||||
"Assistant:\n"
|
||||
f" {BEGIN_SEARCH_QUERY}Where is Martin Campbell from?{END_SEARCH_QUERY}\n\n"
|
||||
"User:\n"
|
||||
f" {BEGIN_SEARCH_RESULT}\nMartin Campbell (born 24 October 1943) is a New Zealand film and television director...\n{END_SEARCH_RESULT}\n\n"
|
||||
"Continues reasoning with the new information...\n\n"
|
||||
"Assistant:\nIt's enough to answer the question\n"
|
||||
REASON_PROMPT = f"""You are an advanced reasoning agent. Your goal is to answer the user's question by breaking it down into a series of verifiable steps.
|
||||
|
||||
"-- Example 2 --\n" #########################################
|
||||
"Question: \"When was the founder of craigslist born?\"\n"
|
||||
"Assistant:\n"
|
||||
f" {BEGIN_SEARCH_QUERY}Who was the founder of craigslist?{END_SEARCH_QUERY}\n\n"
|
||||
"User:\n"
|
||||
f" {BEGIN_SEARCH_RESULT}\nCraigslist was founded by Craig Newmark...\n{END_SEARCH_RESULT}\n\n"
|
||||
"Continues reasoning with the new information.\n"
|
||||
"Assistant:\n"
|
||||
f" {BEGIN_SEARCH_QUERY} When was Craig Newmark born?{END_SEARCH_QUERY}\n\n"
|
||||
"User:\n"
|
||||
f" {BEGIN_SEARCH_RESULT}\nCraig Newmark was born on December 6, 1952...\n{END_SEARCH_RESULT}\n\n"
|
||||
"Continues reasoning with the new information...\n\n"
|
||||
"Assistant:\nIt's enough to answer the question\n"
|
||||
"**Remember**:\n"
|
||||
f"- You have a dataset to search, so you just provide a proper search query.\n"
|
||||
f"- Use {BEGIN_SEARCH_QUERY} to request a dataset search and end with {END_SEARCH_QUERY}.\n"
|
||||
"- The language of query MUST be as the same as 'Question' or 'search result'.\n"
|
||||
"- If no helpful information can be found, rewrite the search query to be less and precise keywords.\n"
|
||||
"- When done searching, continue your reasoning.\n\n"
|
||||
'Please answer the following question. You should think step by step to solve it.\n\n'
|
||||
)
|
||||
You have access to a powerful search tool to find information.
|
||||
|
||||
RELEVANT_EXTRACTION_PROMPT = """**Task Instruction:**
|
||||
**Your Task:**
|
||||
1. Analyze the user's question.
|
||||
2. If you need information, issue a search query to find a specific fact.
|
||||
3. Review the search results.
|
||||
4. Repeat the search process until you have all the facts needed to answer the question.
|
||||
5. Once you have gathered sufficient information, synthesize the facts and provide the final answer directly.
|
||||
|
||||
You are tasked with reading and analyzing web pages based on the following inputs: **Previous Reasoning Steps**, **Current Search Query**, and **Searched Web Pages**. Your objective is to extract relevant and helpful information for **Current Search Query** from the **Searched Web Pages** and seamlessly integrate this information into the **Previous Reasoning Steps** to continue reasoning for the original question.
|
||||
**Tool Usage:**
|
||||
- To search, you MUST write your query between the special tokens: {BEGIN_SEARCH_QUERY}your query{END_SEARCH_QUERY}.
|
||||
- The system will provide results between {BEGIN_SEARCH_RESULT}search results{END_SEARCH_RESULT}.
|
||||
- You have a maximum of {MAX_SEARCH_LIMIT} search attempts.
|
||||
|
||||
**Guidelines:**
|
||||
---
|
||||
**Example 1: Multi-hop Question**
|
||||
|
||||
1. **Analyze the Searched Web Pages:**
|
||||
- Carefully review the content of each searched web page.
|
||||
- Identify factual information that is relevant to the **Current Search Query** and can aid in the reasoning process for the original question.
|
||||
**Question:** "Are both the directors of Jaws and Casino Royale from the same country?"
|
||||
|
||||
2. **Extract Relevant Information:**
|
||||
- Select the information from the Searched Web Pages that directly contributes to advancing the **Previous Reasoning Steps**.
|
||||
- Ensure that the extracted information is accurate and relevant.
|
||||
**Your Thought Process & Actions:**
|
||||
First, I need to identify the director of Jaws.
|
||||
{BEGIN_SEARCH_QUERY}who is the director of Jaws?{END_SEARCH_QUERY}
|
||||
[System returns search results]
|
||||
{BEGIN_SEARCH_RESULT}
|
||||
Jaws is a 1975 American thriller film directed by Steven Spielberg.
|
||||
{END_SEARCH_RESULT}
|
||||
Okay, the director of Jaws is Steven Spielberg. Now I need to find out his nationality.
|
||||
{BEGIN_SEARCH_QUERY}where is Steven Spielberg from?{END_SEARCH_QUERY}
|
||||
[System returns search results]
|
||||
{BEGIN_SEARCH_RESULT}
|
||||
Steven Allan Spielberg is an American filmmaker. Born in Cincinnati, Ohio...
|
||||
{END_SEARCH_RESULT}
|
||||
So, Steven Spielberg is from the USA. Next, I need to find the director of Casino Royale.
|
||||
{BEGIN_SEARCH_QUERY}who is the director of Casino Royale 2006?{END_SEARCH_QUERY}
|
||||
[System returns search results]
|
||||
{BEGIN_SEARCH_RESULT}
|
||||
Casino Royale is a 2006 spy film directed by Martin Campbell.
|
||||
{END_SEARCH_RESULT}
|
||||
The director of Casino Royale is Martin Campbell. Now I need his nationality.
|
||||
{BEGIN_SEARCH_QUERY}where is Martin Campbell from?{END_SEARCH_QUERY}
|
||||
[System returns search results]
|
||||
{BEGIN_SEARCH_RESULT}
|
||||
Martin Campbell (born 24 October 1943) is a New Zealand film and television director.
|
||||
{END_SEARCH_RESULT}
|
||||
I have all the information. Steven Spielberg is from the USA, and Martin Campbell is from New Zealand. They are not from the same country.
|
||||
|
||||
3. **Output Format:**
|
||||
- **If the web pages provide helpful information for current search query:** Present the information beginning with `**Final Information**` as shown below.
|
||||
- The language of query **MUST BE** as the same as 'Search Query' or 'Web Pages'.\n"
|
||||
**Final Information**
|
||||
Final Answer: No, the directors of Jaws and Casino Royale are not from the same country. Steven Spielberg is from the USA, and Martin Campbell is from New Zealand.
|
||||
|
||||
[Helpful information]
|
||||
---
|
||||
**Example 2: Simple Fact Retrieval**
|
||||
|
||||
- **If the web pages do not provide any helpful information for current search query:** Output the following text.
|
||||
**Question:** "When was the founder of craigslist born?"
|
||||
|
||||
**Final Information**
|
||||
**Your Thought Process & Actions:**
|
||||
First, I need to know who founded craigslist.
|
||||
{BEGIN_SEARCH_QUERY}who founded craigslist?{END_SEARCH_QUERY}
|
||||
[System returns search results]
|
||||
{BEGIN_SEARCH_RESULT}
|
||||
Craigslist was founded in 1995 by Craig Newmark.
|
||||
{END_SEARCH_RESULT}
|
||||
The founder is Craig Newmark. Now I need his birth date.
|
||||
{BEGIN_SEARCH_QUERY}when was Craig Newmark born?{END_SEARCH_QUERY}
|
||||
[System returns search results]
|
||||
{BEGIN_SEARCH_RESULT}
|
||||
Craig Newmark was born on December 6, 1952.
|
||||
{END_SEARCH_RESULT}
|
||||
I have found the answer.
|
||||
|
||||
No helpful information found.
|
||||
Final Answer: The founder of craigslist, Craig Newmark, was born on December 6, 1952.
|
||||
|
||||
**Inputs:**
|
||||
- **Previous Reasoning Steps:**
|
||||
{prev_reasoning}
|
||||
---
|
||||
**Important Rules:**
|
||||
- **One Fact at a Time:** Decompose the problem and issue one search query at a time to find a single, specific piece of information.
|
||||
- **Be Precise:** Formulate clear and precise search queries. If a search fails, rephrase it.
|
||||
- **Synthesize at the End:** Do not provide the final answer until you have completed all necessary searches.
|
||||
- **Language Consistency:** Your search queries should be in the same language as the user's question.
|
||||
|
||||
- **Current Search Query:**
|
||||
{search_query}
|
||||
Now, begin your work. Please answer the following question by thinking step-by-step.
|
||||
"""
|
||||
|
||||
- **Searched Web Pages:**
|
||||
{document}
|
||||
RELEVANT_EXTRACTION_PROMPT = """You are a highly efficient information extraction module. Your sole purpose is to extract the single most relevant piece of information from the provided `Searched Web Pages` that directly answers the `Current Search Query`.
|
||||
|
||||
"""
|
||||
**Your Task:**
|
||||
1. Read the `Current Search Query` to understand what specific information is needed.
|
||||
2. Scan the `Searched Web Pages` to find the answer to that query.
|
||||
3. Extract only the essential, factual information that answers the query. Be concise.
|
||||
|
||||
**Context (For Your Information Only):**
|
||||
The `Previous Reasoning Steps` are provided to give you context on the overall goal, but your primary focus MUST be on answering the `Current Search Query`. Do not use information from the previous steps in your output.
|
||||
|
||||
**Output Format:**
|
||||
Your response must follow one of two formats precisely.
|
||||
|
||||
1. **If a direct and relevant answer is found:**
|
||||
- Start your response immediately with `Final Information`.
|
||||
- Provide only the extracted fact(s). Do not add any extra conversational text.
|
||||
|
||||
*Example:*
|
||||
`Current Search Query`: Where is Martin Campbell from?
|
||||
`Searched Web Pages`: [Long article snippet about Martin Campbell's career, which includes the sentence "Martin Campbell (born 24 October 1943) is a New Zealand film and television director..."]
|
||||
|
||||
*Your Output:*
|
||||
Final Information
|
||||
Martin Campbell is a New Zealand film and television director.
|
||||
|
||||
2. **If no relevant answer that directly addresses the query is found in the web pages:**
|
||||
- Start your response immediately with `Final Information`.
|
||||
- Write the exact phrase: `No helpful information found.`
|
||||
|
||||
---
|
||||
**BEGIN TASK**
|
||||
|
||||
**Inputs:**
|
||||
|
||||
- **Previous Reasoning Steps:**
|
||||
{prev_reasoning}
|
||||
|
||||
- **Current Search Query:**
|
||||
{search_query}
|
||||
|
||||
- **Searched Web Pages:**
|
||||
{document}
|
||||
"""
|
||||
@ -1,4 +1,4 @@
|
||||
#
|
||||
#
|
||||
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -75,11 +75,13 @@ def retrieval(tenant_id):
|
||||
for c in ranks["chunks"]:
|
||||
e, doc = DocumentService.get_by_id( c["doc_id"])
|
||||
c.pop("vector", None)
|
||||
meta = getattr(doc, 'meta_fields', {})
|
||||
meta["doc_id"] = c["doc_id"]
|
||||
records.append({
|
||||
"content": c["content_with_weight"],
|
||||
"score": c["similarity"],
|
||||
"title": c["docnm_kwd"],
|
||||
"metadata": getattr(doc, 'meta_fields', {})
|
||||
"metadata": meta
|
||||
})
|
||||
|
||||
return jsonify({"records": records})
|
||||
|
||||
@ -173,7 +173,8 @@ def completion(tenant_id, agent_id, session_id=None, **kwargs):
|
||||
conv.message.append({"role": "assistant", "content": txt, "created_at": time.time(), "id": message_id})
|
||||
conv.reference = canvas.get_reference()
|
||||
conv.errors = canvas.error
|
||||
API4ConversationService.append_message(conv.id, conv.to_dict())
|
||||
conv = conv.to_dict()
|
||||
API4ConversationService.append_message(conv["id"], conv)
|
||||
|
||||
|
||||
def completionOpenAI(tenant_id, agent_id, question, session_id=None, stream=True, **kwargs):
|
||||
|
||||
@ -225,6 +225,9 @@ class TenantLLMService(CommonService):
|
||||
if llm_id == llm["llm_name"]:
|
||||
return llm["model_type"].split(",")[-1]
|
||||
|
||||
for llm in LLMService.query(llm_name=llm_id):
|
||||
return llm.model_type
|
||||
|
||||
|
||||
class LLMBundle:
|
||||
def __init__(self, tenant_id, llm_type, llm_name=None, lang="Chinese", **kwargs):
|
||||
|
||||
@ -82,7 +82,7 @@ An integer specifying the number of previous dialogue rounds to input into the L
|
||||
This feature is used for multi-turn dialogue *only*.
|
||||
:::
|
||||
|
||||
### Max retrieves
|
||||
### Max retries
|
||||
|
||||
Defines the maximum number of attempts the agent will make to retry a failed task or operation before stopping or reporting failure.
|
||||
|
||||
@ -94,6 +94,10 @@ The waiting period in seconds that the agent observes before retrying a failed t
|
||||
|
||||
Defines the maximum number reflection rounds of the selected chat model. Defaults to 5 rounds.
|
||||
|
||||
:::tip NOTE
|
||||
You can set the value to 1 to shorten your agent's response time.
|
||||
:::
|
||||
|
||||
### Output
|
||||
|
||||
The global variable name for the output of the **Agent** component, which can be referenced by other components in the workflow.
|
||||
@ -34,7 +34,7 @@ From v0.20.0 onwards, Agents are no longer compatible with earlier versions, and
|
||||
|
||||
- Unified orchestration of both Agents and Workflows.
|
||||
- A comprehensive refactor of the Agent, greatly enhancing its capabilities and usability, with support for Multi-Agent configurations, planning and reflection, and visual functionalities.
|
||||
- Fully realized MCP functionality, allowing for MCP Server import, Agents functioning as MCP Clients, and RAGFlow itself operating as an MCP Server.
|
||||
- Fully implemented MCP functionality, allowing for MCP Server import, Agents functioning as MCP Clients, and RAGFlow itself operating as an MCP Server.
|
||||
- Access to runtime logs for Agents.
|
||||
- Chat histories with Agents available through the management panel.
|
||||
- Integration of a new, more robust version of Infinity, enabling the auto-tagging functionality with Infinity as the underlying document engine.
|
||||
@ -43,7 +43,7 @@ From v0.20.0 onwards, Agents are no longer compatible with earlier versions, and
|
||||
- RAGFlow’s codebase is now mirrored on Gitee.
|
||||
- Introduction of a new model provider, Gitee AI.
|
||||
|
||||
### New agent templates
|
||||
### New agent templates introduced
|
||||
|
||||
- Multi-Agent based Deep Research: Collaborative Agent teamwork led by a Lead Agent with multiple Subagents, distinct from traditional workflow orchestration.
|
||||
- An intelligent Q&A chatbot leveraging internal knowledge bases, designed for customer service and training scenarios.
|
||||
|
||||
@ -59,6 +59,10 @@ class Base(ABC):
|
||||
def _image_prompt(self, text, images):
|
||||
if not images:
|
||||
return text
|
||||
|
||||
if isinstance(images, str):
|
||||
images = [images]
|
||||
|
||||
pmpt = [{"type": "text", "text": text}]
|
||||
for img in images:
|
||||
pmpt.append({
|
||||
@ -518,6 +522,7 @@ class GeminiCV(Base):
|
||||
def chat_streamly(self, system, history, gen_conf, images=[]):
|
||||
from transformers import GenerationConfig
|
||||
ans = ""
|
||||
response = None
|
||||
try:
|
||||
response = self.model.generate_content(
|
||||
self._form_history(system, history, images),
|
||||
@ -533,8 +538,10 @@ class GeminiCV(Base):
|
||||
except Exception as e:
|
||||
yield ans + "\n**ERROR**: " + str(e)
|
||||
|
||||
yield response._chunks[-1].usage_metadata.total_token_count
|
||||
|
||||
if response and hasattr(response, "usage_metadata") and hasattr(response.usage_metadata, "total_token_count"):
|
||||
yield response.usage_metadata.total_token_count
|
||||
else:
|
||||
yield 0
|
||||
|
||||
class NvidiaCV(Base):
|
||||
_FACTORY_NAME = "NVIDIA"
|
||||
@ -795,4 +802,4 @@ class GoogleCV(AnthropicCV, GeminiCV):
|
||||
yield ans
|
||||
else:
|
||||
for ans in GeminiCV.chat_streamly(self, system, history, gen_conf, images):
|
||||
yield ans
|
||||
yield ans
|
||||
|
||||
@ -227,9 +227,20 @@ class RedisDB:
|
||||
"""https://redis.io/docs/latest/commands/xreadgroup/"""
|
||||
for _ in range(3):
|
||||
try:
|
||||
group_info = self.REDIS.xinfo_groups(queue_name)
|
||||
if not any(gi["name"] == group_name for gi in group_info):
|
||||
self.REDIS.xgroup_create(queue_name, group_name, id="0", mkstream=True)
|
||||
|
||||
try:
|
||||
group_info = self.REDIS.xinfo_groups(queue_name)
|
||||
if not any(gi["name"] == group_name for gi in group_info):
|
||||
self.REDIS.xgroup_create(queue_name, group_name, id="0", mkstream=True)
|
||||
except redis.exceptions.ResponseError as e:
|
||||
if "no such key" in str(e).lower():
|
||||
self.REDIS.xgroup_create(queue_name, group_name, id="0", mkstream=True)
|
||||
elif "busygroup" in str(e).lower():
|
||||
logging.warning("Group already exists, continue.")
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
|
||||
args = {
|
||||
"groupname": group_name,
|
||||
"consumername": consumer_name,
|
||||
@ -338,8 +349,8 @@ class RedisDB:
|
||||
logging.warning("RedisDB.delete " + str(key) + " got exception: " + str(e))
|
||||
self.__open__()
|
||||
return False
|
||||
|
||||
|
||||
|
||||
|
||||
REDIS_CONN = RedisDB()
|
||||
|
||||
|
||||
|
||||
@ -123,7 +123,7 @@ class RAGFlowS3:
|
||||
|
||||
@use_prefix_path
|
||||
@use_default_bucket
|
||||
def put(self, bucket, fnm, binary):
|
||||
def put(self, bucket, fnm, binary, **kwargs):
|
||||
logging.debug(f"bucket name {bucket}; filename :{fnm}:")
|
||||
for _ in range(1):
|
||||
try:
|
||||
@ -140,7 +140,7 @@ class RAGFlowS3:
|
||||
|
||||
@use_prefix_path
|
||||
@use_default_bucket
|
||||
def rm(self, bucket, fnm):
|
||||
def rm(self, bucket, fnm, **kwargs):
|
||||
try:
|
||||
self.conn.delete_object(Bucket=bucket, Key=fnm)
|
||||
except Exception:
|
||||
@ -148,7 +148,7 @@ class RAGFlowS3:
|
||||
|
||||
@use_prefix_path
|
||||
@use_default_bucket
|
||||
def get(self, bucket, fnm):
|
||||
def get(self, bucket, fnm, **kwargs):
|
||||
for _ in range(1):
|
||||
try:
|
||||
r = self.conn.get_object(Bucket=bucket, Key=fnm)
|
||||
@ -162,7 +162,7 @@ class RAGFlowS3:
|
||||
|
||||
@use_prefix_path
|
||||
@use_default_bucket
|
||||
def obj_exist(self, bucket, fnm):
|
||||
def obj_exist(self, bucket, fnm, **kwargs):
|
||||
try:
|
||||
if self.conn.head_object(Bucket=bucket, Key=fnm):
|
||||
return True
|
||||
@ -174,7 +174,7 @@ class RAGFlowS3:
|
||||
|
||||
@use_prefix_path
|
||||
@use_default_bucket
|
||||
def get_presigned_url(self, bucket, fnm, expires):
|
||||
def get_presigned_url(self, bucket, fnm, expires, **kwargs):
|
||||
for _ in range(10):
|
||||
try:
|
||||
r = self.conn.generate_presigned_url('get_object',
|
||||
|
||||
@ -353,7 +353,12 @@ export const useHandleMessageInputChange = () => {
|
||||
export const useSelectDerivedMessages = () => {
|
||||
const [derivedMessages, setDerivedMessages] = useState<IMessage[]>([]);
|
||||
|
||||
const ref = useScrollToBottom(derivedMessages);
|
||||
const messageContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { scrollRef, scrollToBottom } = useScrollToBottom(
|
||||
derivedMessages,
|
||||
messageContainerRef,
|
||||
);
|
||||
|
||||
const addNewestQuestion = useCallback(
|
||||
(message: Message, answer: string = '') => {
|
||||
@ -492,7 +497,8 @@ export const useSelectDerivedMessages = () => {
|
||||
}, [setDerivedMessages]);
|
||||
|
||||
return {
|
||||
ref,
|
||||
scrollRef,
|
||||
messageContainerRef,
|
||||
derivedMessages,
|
||||
setDerivedMessages,
|
||||
addNewestQuestion,
|
||||
@ -503,6 +509,7 @@ export const useSelectDerivedMessages = () => {
|
||||
addNewestOneAnswer,
|
||||
removeMessagesAfterCurrentMessage,
|
||||
removeAllMessages,
|
||||
scrollToBottom,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -39,6 +39,13 @@ export default {
|
||||
nextPage: 'Next',
|
||||
add: 'Add',
|
||||
promptPlaceholder: `Please input or use / to quickly insert variables.`,
|
||||
mcp: {
|
||||
namePlaceholder: 'My MCP Server',
|
||||
nameRequired:
|
||||
'It must be 1–64 characters long and can only contain letters, numbers, hyphens, and underscores.',
|
||||
urlPlaceholder: 'https://api.example.com/v1/mcp',
|
||||
tokenPlaceholder: 'e.g. eyJhbGciOiJIUzI1Ni...',
|
||||
},
|
||||
},
|
||||
login: {
|
||||
login: 'Sign in',
|
||||
|
||||
@ -22,7 +22,8 @@ import { useAwaitCompentData } from '../hooks/use-chat-logic';
|
||||
function AgentChatBox() {
|
||||
const {
|
||||
value,
|
||||
ref,
|
||||
scrollRef,
|
||||
messageContainerRef,
|
||||
sendLoading,
|
||||
derivedMessages,
|
||||
handleInputChange,
|
||||
@ -59,7 +60,7 @@ function AgentChatBox() {
|
||||
return (
|
||||
<>
|
||||
<section className="flex flex-1 flex-col px-5 h-[90vh]">
|
||||
<div className="flex-1 overflow-auto">
|
||||
<div className="flex-1 overflow-auto" ref={messageContainerRef}>
|
||||
<div>
|
||||
{/* <Spin spinning={sendLoading}> */}
|
||||
{derivedMessages?.map((message, i) => {
|
||||
@ -106,7 +107,7 @@ function AgentChatBox() {
|
||||
})}
|
||||
{/* </Spin> */}
|
||||
</div>
|
||||
<div ref={ref.scrollRef} />
|
||||
<div ref={scrollRef} />
|
||||
</div>
|
||||
<NextMessageInput
|
||||
value={value}
|
||||
|
||||
@ -198,12 +198,14 @@ export const useSendAgentMessage = (
|
||||
const prologue = useGetBeginNodePrologue();
|
||||
const {
|
||||
derivedMessages,
|
||||
ref,
|
||||
scrollRef,
|
||||
messageContainerRef,
|
||||
removeLatestMessage,
|
||||
removeMessageById,
|
||||
addNewestOneQuestion,
|
||||
addNewestOneAnswer,
|
||||
removeAllMessages,
|
||||
scrollToBottom,
|
||||
} = useSelectDerivedMessages();
|
||||
const { addEventList: addEventListFun } = useContext(AgentChatLogContext);
|
||||
const {
|
||||
@ -303,7 +305,18 @@ export const useSendAgentMessage = (
|
||||
});
|
||||
}
|
||||
addNewestOneQuestion({ ...msgBody, files: fileList });
|
||||
}, [value, done, addNewestOneQuestion, fileList, setValue, sendMessage]);
|
||||
setTimeout(() => {
|
||||
scrollToBottom();
|
||||
}, 100);
|
||||
}, [
|
||||
value,
|
||||
done,
|
||||
addNewestOneQuestion,
|
||||
fileList,
|
||||
setValue,
|
||||
sendMessage,
|
||||
scrollToBottom,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const { content, id } = findMessageFromList(answerList);
|
||||
@ -337,7 +350,8 @@ export const useSendAgentMessage = (
|
||||
value,
|
||||
sendLoading: !done,
|
||||
derivedMessages,
|
||||
ref,
|
||||
scrollRef,
|
||||
messageContainerRef,
|
||||
handlePressEnter,
|
||||
handleInputChange,
|
||||
removeMessageById,
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import MessageItem from '@/components/message-item';
|
||||
import { MessageType } from '@/constants/chat';
|
||||
import { Flex, Spin } from 'antd';
|
||||
import { useRef } from 'react';
|
||||
import {
|
||||
useCreateConversationBeforeUploadDocument,
|
||||
useGetFileIcon,
|
||||
@ -19,7 +18,6 @@ import {
|
||||
useFetchNextDialog,
|
||||
useGetChatSearchParams,
|
||||
} from '@/hooks/chat-hooks';
|
||||
import { useScrollToBottom } from '@/hooks/logic-hooks';
|
||||
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
||||
import { buildMessageUuidWithRole } from '@/utils/chat';
|
||||
import { memo } from 'react';
|
||||
@ -34,10 +32,10 @@ const ChatContainer = ({ controller }: IProps) => {
|
||||
const { data: conversation } = useFetchNextConversation();
|
||||
const { data: currentDialog } = useFetchNextDialog();
|
||||
|
||||
const messageContainerRef = useRef<HTMLDivElement>(null);
|
||||
const {
|
||||
value,
|
||||
ref,
|
||||
scrollRef,
|
||||
messageContainerRef,
|
||||
loading,
|
||||
sendLoading,
|
||||
derivedMessages,
|
||||
@ -47,10 +45,6 @@ const ChatContainer = ({ controller }: IProps) => {
|
||||
removeMessageById,
|
||||
stopOutputMessage,
|
||||
} = useSendNextMessage(controller);
|
||||
const { scrollRef, isAtBottom, scrollToBottom } = useScrollToBottom(
|
||||
derivedMessages,
|
||||
messageContainerRef,
|
||||
);
|
||||
|
||||
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
||||
useClickDrawer();
|
||||
@ -61,11 +55,6 @@ const ChatContainer = ({ controller }: IProps) => {
|
||||
const { createConversationBeforeUploadDocument } =
|
||||
useCreateConversationBeforeUploadDocument();
|
||||
|
||||
const handleSend = (msg) => {
|
||||
// your send logic
|
||||
setTimeout(scrollToBottom, 0);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex flex={1} className={styles.chatContainer} vertical>
|
||||
|
||||
@ -291,7 +291,8 @@ export const useSetConversation = () => {
|
||||
|
||||
export const useSelectNextMessages = () => {
|
||||
const {
|
||||
ref,
|
||||
scrollRef,
|
||||
messageContainerRef,
|
||||
setDerivedMessages,
|
||||
derivedMessages,
|
||||
addNewestAnswer,
|
||||
@ -335,7 +336,8 @@ export const useSelectNextMessages = () => {
|
||||
}, [conversation.message, conversationId, setDerivedMessages, isNew]);
|
||||
|
||||
return {
|
||||
ref,
|
||||
scrollRef,
|
||||
messageContainerRef,
|
||||
derivedMessages,
|
||||
loading,
|
||||
addNewestAnswer,
|
||||
@ -371,7 +373,8 @@ export const useSendNextMessage = (controller: AbortController) => {
|
||||
api.completeConversation,
|
||||
);
|
||||
const {
|
||||
ref,
|
||||
scrollRef,
|
||||
messageContainerRef,
|
||||
derivedMessages,
|
||||
loading,
|
||||
addNewestAnswer,
|
||||
@ -499,7 +502,8 @@ export const useSendNextMessage = (controller: AbortController) => {
|
||||
regenerateMessage,
|
||||
sendLoading: !done,
|
||||
loading,
|
||||
ref,
|
||||
scrollRef,
|
||||
messageContainerRef,
|
||||
derivedMessages,
|
||||
removeMessageById,
|
||||
stopOutputMessage,
|
||||
|
||||
@ -50,7 +50,8 @@ const ChatContainer = () => {
|
||||
handleInputChange,
|
||||
value,
|
||||
sendLoading,
|
||||
ref,
|
||||
scrollRef,
|
||||
messageContainerRef,
|
||||
derivedMessages,
|
||||
hasError,
|
||||
stopOutputMessage,
|
||||
@ -149,6 +150,7 @@ const ChatContainer = () => {
|
||||
className={cn(
|
||||
'flex flex-1 flex-col overflow-auto scrollbar-auto m-auto w-5/6',
|
||||
)}
|
||||
ref={messageContainerRef}
|
||||
>
|
||||
<div>
|
||||
{derivedMessages?.map((message, i) => {
|
||||
@ -203,7 +205,7 @@ const ChatContainer = () => {
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div ref={ref.scrollRef} />
|
||||
<div ref={scrollRef} />
|
||||
</div>
|
||||
<div className="flex w-full justify-center mb-8">
|
||||
<div className="w-5/6">
|
||||
|
||||
@ -37,20 +37,23 @@ export function useBuildFormSchema() {
|
||||
name: z
|
||||
.string()
|
||||
.min(1, {
|
||||
message: t('common.namePlaceholder'),
|
||||
message: t('common.mcp.namePlaceholder'),
|
||||
})
|
||||
.regex(/^[a-zA-Z0-9_-]{1,64}$/, {
|
||||
message: t('common.mcp.nameRequired'),
|
||||
})
|
||||
.trim(),
|
||||
url: z
|
||||
.string()
|
||||
.url()
|
||||
.min(1, {
|
||||
message: t('common.namePlaceholder'),
|
||||
message: t('common.mcp.urlPlaceholder'),
|
||||
})
|
||||
.trim(),
|
||||
server_type: z
|
||||
.string()
|
||||
.min(1, {
|
||||
message: t('common.namePlaceholder'),
|
||||
message: t('common.pleaseSelect'),
|
||||
})
|
||||
.trim(),
|
||||
authorization_token: z.string().optional(),
|
||||
@ -89,7 +92,7 @@ export function EditMcpForm({
|
||||
<FormLabel>{t('common.name')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder={t('common.namePlaceholder')}
|
||||
placeholder={t('common.mcp.namePlaceholder')}
|
||||
{...field}
|
||||
autoComplete="off"
|
||||
/>
|
||||
@ -106,7 +109,7 @@ export function EditMcpForm({
|
||||
<FormLabel>{t('mcp.url')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder={t('common.namePlaceholder')}
|
||||
placeholder={t('common.mcp.urlPlaceholder')}
|
||||
{...field}
|
||||
autoComplete="off"
|
||||
onChange={(e) => {
|
||||
@ -148,7 +151,7 @@ export function EditMcpForm({
|
||||
<FormLabel>Authorization Token</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder={t('common.namePlaceholder')}
|
||||
placeholder={t('common.mcp.tokenPlaceholder')}
|
||||
{...field}
|
||||
autoComplete="off"
|
||||
type="password"
|
||||
|
||||
Reference in New Issue
Block a user