Compare commits

...

8 Commits

Author SHA1 Message Date
1deb0a2d42 Fix:local variable 'response' referenced before assignment (#9230)
### What problem does this PR solve?

https://github.com/infiniflow/ragflow/issues/9227

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)

---------

Co-authored-by: Kevin Hu <kevinhu.sh@gmail.com>
2025-08-05 11:00:06 +08:00
dd055deee9 Docs: Updated tips for max rounds (#9235)
### What problem does this PR solve?

### Type of change

- [x] Documentation Update
2025-08-05 10:59:37 +08:00
a249803961 Refa: ensure Redis stream queue could be created properly (#9223)
### What problem does this PR solve?

Ensure Redis queue could be created properly.

### Type of change

- [x] Refactoring
2025-08-05 09:54:31 +08:00
6ec3f18e22 Fix: self-deployed LLM error, (#9217)
### What problem does this PR solve?

Close #9197
Close #9145

### Type of change

- [x] Refactoring
- [x] Bug fixing.
2025-08-05 09:49:47 +08:00
7724acbadb Perf Impr: Decouple reasoning and extraction for faster, more precise logic (#9191)
### What problem does this PR solve?

This commit refactors the core prompts to decouple the high-level
reasoning from the low-level information extraction. By making
REASON_PROMPT a dedicated strategist that only generates search queries
and re-tasking RELEVANT_EXTRACTION_PROMPT to be a specialized tool for
single-fact extraction, we eliminate redundant information
summarization. This clear separation of concerns makes the overall
reasoning process significantly faster and more precise, as each
component now has a single, well-defined responsibility.

### Type of change

- [x] Performance Improvement
2025-08-05 09:36:14 +08:00
a36ba95c1c Fix: Add prompt text to the form in the MCP module (#9222)
### What problem does this PR solve?

Fix: Add prompt text to the form in the MCP module #3221

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-08-05 09:26:59 +08:00
30ccc4a66c Fix: correct single base64 image handling in image prompt (#9220)
### What problem does this PR solve?

Correct single base64 image handling in image prompt.


![img_v3_02or_ec4757c2-a9d4-4774-9a76-f7c6be633ebg](https://github.com/user-attachments/assets/872a86bf-e2a8-48d1-9b71-2a0c7a35ba9e)

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-08-05 09:26:42 +08:00
dda5a0080a Fix: Fixed the issue where the agent's chat box could not automatically scroll to the bottom #3221 (#9219)
### What problem does this PR solve?

Fix: Fixed the issue where the agent's chat box could not automatically
scroll to the bottom #3221

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-08-05 09:26:15 +08:00
17 changed files with 217 additions and 128 deletions

View File

@ -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}
"""

View File

@ -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})

View File

@ -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):

View File

@ -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):

View File

@ -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.

View File

@ -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
- RAGFlows 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.

View File

@ -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

View File

@ -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()

View File

@ -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',

View File

@ -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,
};
};

View File

@ -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 164 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',

View File

@ -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}

View File

@ -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,

View File

@ -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>

View File

@ -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,

View File

@ -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">

View File

@ -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"